From 44483582470f06fe0ff1ed17f01b5d47f590e1ba Mon Sep 17 00:00:00 2001
From: canewsin <canews.in@gmail.com>
Date: Thu, 16 Dec 2021 19:35:01 +0530
Subject: [PATCH] 0.7.6-internal 2

Moved Plugins to Seperate Repo
---
 .gitmodules                                   |     3 +
 plugins                                       |     1 +
 .../AnnounceBitTorrentPlugin.py               |   148 -
 plugins/AnnounceBitTorrent/__init__.py        |     1 -
 plugins/AnnounceBitTorrent/plugin_info.json   |     5 -
 plugins/AnnounceLocal/AnnounceLocalPlugin.py  |   147 -
 plugins/AnnounceLocal/BroadcastServer.py      |   139 -
 plugins/AnnounceLocal/Test/TestAnnounce.py    |   113 -
 plugins/AnnounceLocal/Test/conftest.py        |     4 -
 plugins/AnnounceLocal/Test/pytest.ini         |     5 -
 plugins/AnnounceLocal/__init__.py             |     1 -
 plugins/AnnounceLocal/plugin_info.json        |     5 -
 plugins/AnnounceShare/AnnounceSharePlugin.py  |   190 -
 .../AnnounceShare/Test/TestAnnounceShare.py   |    24 -
 plugins/AnnounceShare/Test/conftest.py        |     3 -
 plugins/AnnounceShare/Test/pytest.ini         |     5 -
 plugins/AnnounceShare/__init__.py             |     1 -
 plugins/AnnounceShare/plugin_info.json        |     5 -
 plugins/AnnounceZero/AnnounceZeroPlugin.py    |   141 -
 plugins/AnnounceZero/__init__.py              |     1 -
 plugins/AnnounceZero/plugin_info.json         |     5 -
 plugins/Benchmark/BenchmarkDb.py              |   143 -
 plugins/Benchmark/BenchmarkPack.py            |   183 -
 plugins/Benchmark/BenchmarkPlugin.py          |   428 -
 plugins/Benchmark/__init__.py                 |     3 -
 plugins/Benchmark/media/benchmark.html        |   123 -
 plugins/Benchmark/plugin_info.json            |     5 -
 plugins/Bigfile/BigfilePiecefield.py          |   170 -
 plugins/Bigfile/BigfilePlugin.py              |   843 -
 plugins/Bigfile/Test/TestBigfile.py           |   574 -
 plugins/Bigfile/Test/conftest.py              |     1 -
 plugins/Bigfile/Test/pytest.ini               |     5 -
 plugins/Bigfile/__init__.py                   |     2 -
 plugins/Chart/ChartCollector.py               |   181 -
 plugins/Chart/ChartDb.py                      |   133 -
 plugins/Chart/ChartPlugin.py                  |    57 -
 plugins/Chart/__init__.py                     |     1 -
 plugins/Chart/plugin_info.json                |     5 -
 plugins/ContentFilter/ContentFilterPlugin.py  |   262 -
 plugins/ContentFilter/ContentFilterStorage.py |   164 -
 .../ContentFilter/Test/TestContentFilter.py   |    82 -
 plugins/ContentFilter/Test/conftest.py        |     1 -
 plugins/ContentFilter/Test/pytest.ini         |     5 -
 plugins/ContentFilter/__init__.py             |     1 -
 plugins/ContentFilter/languages/hu.json       |     6 -
 plugins/ContentFilter/languages/it.json       |     6 -
 plugins/ContentFilter/languages/jp.json       |     6 -
 plugins/ContentFilter/languages/pt-br.json    |     6 -
 plugins/ContentFilter/languages/zh-tw.json    |     6 -
 plugins/ContentFilter/languages/zh.json       |     6 -
 plugins/ContentFilter/media/blocklisted.html  |    89 -
 plugins/ContentFilter/media/js/ZeroFrame.js   |   119 -
 plugins/ContentFilter/plugin_info.json        |     5 -
 plugins/Cors/CorsPlugin.py                    |   139 -
 plugins/Cors/__init__.py                      |     1 -
 plugins/Cors/plugin_info.json                 |     5 -
 plugins/CryptMessage/CryptMessage.py          |    58 -
 plugins/CryptMessage/CryptMessagePlugin.py    |   225 -
 plugins/CryptMessage/Test/TestCrypt.py        |   136 -
 plugins/CryptMessage/Test/conftest.py         |     1 -
 plugins/CryptMessage/Test/pytest.ini          |     5 -
 plugins/CryptMessage/__init__.py              |     1 -
 plugins/CryptMessage/plugin_info.json         |     5 -
 plugins/FilePack/FilePackPlugin.py            |   193 -
 plugins/FilePack/__init__.py                  |     1 -
 plugins/FilePack/plugin_info.json             |     5 -
 plugins/MergerSite/MergerSitePlugin.py        |   399 -
 plugins/MergerSite/__init__.py                |     1 -
 plugins/MergerSite/languages/es.json          |     5 -
 plugins/MergerSite/languages/fr.json          |     5 -
 plugins/MergerSite/languages/hu.json          |     5 -
 plugins/MergerSite/languages/it.json          |     5 -
 plugins/MergerSite/languages/jp.json          |     5 -
 plugins/MergerSite/languages/pt-br.json       |     5 -
 plugins/MergerSite/languages/tr.json          |     5 -
 plugins/MergerSite/languages/zh-tw.json       |     5 -
 plugins/MergerSite/languages/zh.json          |     5 -
 plugins/Newsfeed/NewsfeedPlugin.py            |   187 -
 plugins/Newsfeed/__init__.py                  |     1 -
 plugins/OptionalManager/ContentDbPlugin.py    |   414 -
 .../OptionalManager/OptionalManagerPlugin.py  |   253 -
 .../Test/TestOptionalManager.py               |   158 -
 plugins/OptionalManager/Test/conftest.py      |     1 -
 plugins/OptionalManager/Test/pytest.ini       |     5 -
 plugins/OptionalManager/UiWebsocketPlugin.py  |   396 -
 plugins/OptionalManager/__init__.py           |     2 -
 plugins/OptionalManager/languages/es.json     |     7 -
 plugins/OptionalManager/languages/fr.json     |     7 -
 plugins/OptionalManager/languages/hu.json     |     7 -
 plugins/OptionalManager/languages/jp.json     |     7 -
 plugins/OptionalManager/languages/pt-br.json  |     7 -
 plugins/OptionalManager/languages/zh-tw.json  |     7 -
 plugins/OptionalManager/languages/zh.json     |     7 -
 plugins/PeerDb/PeerDbPlugin.py                |   100 -
 plugins/PeerDb/__init__.py                    |     2 -
 plugins/PeerDb/plugin_info.json               |     5 -
 plugins/Sidebar/ConsolePlugin.py              |   101 -
 plugins/Sidebar/SidebarPlugin.py              |   805 -
 plugins/Sidebar/ZipStream.py                  |    59 -
 plugins/Sidebar/__init__.py                   |     2 -
 plugins/Sidebar/languages/da.json             |    81 -
 plugins/Sidebar/languages/de.json             |    81 -
 plugins/Sidebar/languages/es.json             |    79 -
 plugins/Sidebar/languages/fr.json             |    82 -
 plugins/Sidebar/languages/hu.json             |    82 -
 plugins/Sidebar/languages/it.json             |    81 -
 plugins/Sidebar/languages/jp.json             |   104 -
 plugins/Sidebar/languages/pl.json             |    82 -
 plugins/Sidebar/languages/pt-br.json          |    97 -
 plugins/Sidebar/languages/ru.json             |    82 -
 plugins/Sidebar/languages/tr.json             |    82 -
 plugins/Sidebar/languages/zh-tw.json          |    83 -
 plugins/Sidebar/languages/zh.json             |   101 -
 plugins/Sidebar/media/Class.coffee            |    23 -
 plugins/Sidebar/media/Console.coffee          |   201 -
 plugins/Sidebar/media/Console.css             |    31 -
 plugins/Sidebar/media/Menu.coffee             |    49 -
 plugins/Sidebar/media/Menu.css                |    19 -
 plugins/Sidebar/media/Prototypes.coffee       |     9 -
 plugins/Sidebar/media/RateLimit.coffee        |    14 -
 plugins/Sidebar/media/Scrollable.js           |    91 -
 plugins/Sidebar/media/Scrollbable.css         |    44 -
 plugins/Sidebar/media/Sidebar.coffee          |   644 -
 plugins/Sidebar/media/Sidebar.css             |   169 -
 plugins/Sidebar/media/all.css                 |   281 -
 plugins/Sidebar/media/all.js                  |  1770 --
 plugins/Sidebar/media/morphdom.js             |   340 -
 plugins/Sidebar/media_globe/Detector.js       |    60 -
 plugins/Sidebar/media_globe/Tween.js          |    12 -
 plugins/Sidebar/media_globe/all.js            |  1345 --
 plugins/Sidebar/media_globe/globe.js          |   436 -
 plugins/Sidebar/media_globe/three.min.js      |   814 -
 plugins/Sidebar/media_globe/world.jpg         |   Bin 94795 -> 0 bytes
 plugins/Sidebar/plugin_info.json              |     5 -
 plugins/Stats/StatsPlugin.py                  |   634 -
 plugins/Stats/__init__.py                     |     1 -
 plugins/Stats/plugin_info.json                |     5 -
 plugins/TranslateSite/TranslateSitePlugin.py  |    80 -
 plugins/TranslateSite/__init__.py             |     1 -
 plugins/TranslateSite/plugin_info.json        |     5 -
 plugins/Trayicon/TrayiconPlugin.py            |   171 -
 plugins/Trayicon/__init__.py                  |     4 -
 plugins/Trayicon/languages/es.json            |    14 -
 plugins/Trayicon/languages/fr.json            |    14 -
 plugins/Trayicon/languages/hu.json            |    14 -
 plugins/Trayicon/languages/it.json            |    14 -
 plugins/Trayicon/languages/jp.json            |    14 -
 plugins/Trayicon/languages/pl.json            |    14 -
 plugins/Trayicon/languages/pt-br.json         |    14 -
 plugins/Trayicon/languages/tr.json            |    14 -
 plugins/Trayicon/languages/zh-tw.json         |    14 -
 plugins/Trayicon/languages/zh.json            |    14 -
 plugins/Trayicon/lib/__init__.py              |     0
 plugins/Trayicon/lib/notificationicon.py      |   730 -
 plugins/Trayicon/lib/winfolders.py            |    54 -
 plugins/Trayicon/plugin_info.json             |     5 -
 plugins/Trayicon/trayicon.ico                 |   Bin 1150 -> 0 bytes
 plugins/UiConfig/UiConfigPlugin.py            |    72 -
 plugins/UiConfig/__init__.py                  |     1 -
 plugins/UiConfig/languages/hu.json            |    33 -
 plugins/UiConfig/languages/jp.json            |    62 -
 plugins/UiConfig/languages/pl.json            |    62 -
 plugins/UiConfig/languages/pt-br.json         |    56 -
 plugins/UiConfig/languages/zh.json            |    62 -
 plugins/UiConfig/media/config.html            |    20 -
 plugins/UiConfig/media/css/Config.css         |    68 -
 plugins/UiConfig/media/css/all.css            |   124 -
 plugins/UiConfig/media/css/button.css         |    12 -
 plugins/UiConfig/media/css/fonts.css          |    30 -
 plugins/UiConfig/media/img/loading.gif        |   Bin 723 -> 0 bytes
 .../UiConfig/media/js/ConfigStorage.coffee    |   222 -
 plugins/UiConfig/media/js/ConfigView.coffee   |   124 -
 plugins/UiConfig/media/js/UiConfig.coffee     |   129 -
 plugins/UiConfig/media/js/all.js              |  2066 --
 plugins/UiConfig/media/js/lib/Class.coffee    |    23 -
 plugins/UiConfig/media/js/lib/Promise.coffee  |    74 -
 .../UiConfig/media/js/lib/Prototypes.coffee   |     8 -
 plugins/UiConfig/media/js/lib/maquette.js     |   770 -
 .../UiConfig/media/js/utils/Animation.coffee  |   138 -
 plugins/UiConfig/media/js/utils/Dollar.coffee |     3 -
 .../UiConfig/media/js/utils/ZeroFrame.coffee  |    85 -
 plugins/UiConfig/plugin_info.json             |     5 -
 plugins/UiFileManager/UiFileManagerPlugin.py  |    90 -
 plugins/UiFileManager/__init__.py             |     1 -
 plugins/UiFileManager/languages/hu.json       |    20 -
 plugins/UiFileManager/languages/jp.json       |    20 -
 .../UiFileManager/media/codemirror/LICENSE    |    21 -
 .../UiFileManager/media/codemirror/all.css    |   678 -
 plugins/UiFileManager/media/codemirror/all.js | 19964 ----------------
 .../media/codemirror/base/codemirror.css      |   349 -
 .../media/codemirror/base/codemirror.js       |  9778 --------
 .../codemirror/extension/dialog/dialog.css    |    32 -
 .../codemirror/extension/dialog/dialog.js     |   163 -
 .../extension/edit/closebrackets.js           |   191 -
 .../codemirror/extension/edit/closetag.js     |   184 -
 .../codemirror/extension/edit/continuelist.js |   101 -
 .../extension/edit/matchbrackets.js           |   158 -
 .../codemirror/extension/edit/matchtags.js    |    66 -
 .../extension/edit/trailingspace.js           |    27 -
 .../codemirror/extension/fold/brace-fold.js   |   105 -
 .../codemirror/extension/fold/comment-fold.js |    59 -
 .../codemirror/extension/fold/foldcode.js     |   157 -
 .../codemirror/extension/fold/foldgutter.css  |    20 -
 .../codemirror/extension/fold/foldgutter.js   |   163 -
 .../codemirror/extension/fold/indent-fold.js  |    48 -
 .../extension/fold/markdown-fold.js           |    49 -
 .../codemirror/extension/fold/xml-fold.js     |   184 -
 .../codemirror/extension/hint/anyword-hint.js |    41 -
 .../codemirror/extension/hint/html-hint.js    |   350 -
 .../codemirror/extension/hint/show-hint.css   |    36 -
 .../codemirror/extension/hint/show-hint.js    |   479 -
 .../codemirror/extension/hint/sql-hint.js     |   304 -
 .../codemirror/extension/hint/xml-hint.js     |   123 -
 .../codemirror/extension/lint/json-lint.js    |    40 -
 .../codemirror/extension/lint/jsonlint.js     |     1 -
 .../media/codemirror/extension/lint/lint.css  |    73 -
 .../media/codemirror/extension/lint/lint.js   |   255 -
 .../codemirror/extension/mdn-like-custom.css  |    44 -
 .../extension/scroll/annotatescrollbar.js     |   122 -
 .../extension/scroll/scrollpastend.js         |    48 -
 .../extension/scroll/simplescrollbars.css     |    66 -
 .../extension/scroll/simplescrollbars.js      |   152 -
 .../extension/search/jump-to-line.js          |    50 -
 .../extension/search/match-highlighter.js     |   167 -
 .../extension/search/matchesonscrollbar.css   |     8 -
 .../extension/search/matchesonscrollbar.js    |    97 -
 .../codemirror/extension/search/search.js     |   260 -
 .../extension/search/searchcursor.js          |   296 -
 .../extension/selection/active-line.js        |    72 -
 .../extension/selection/mark-selection.js     |   119 -
 .../extension/selection/selection-pointer.js  |    98 -
 .../media/codemirror/extension/simple.js      |   216 -
 .../media/codemirror/extension/sublime.js     |   714 -
 .../media/codemirror/mode/coffeescript.js     |   359 -
 .../media/codemirror/mode/css.js              |   860 -
 .../UiFileManager/media/codemirror/mode/go.js |   187 -
 .../media/codemirror/mode/htmlembedded.js     |    37 -
 .../media/codemirror/mode/htmlmixed.js        |   152 -
 .../media/codemirror/mode/javascript.js       |   934 -
 .../media/codemirror/mode/markdown.js         |   886 -
 .../media/codemirror/mode/python.js           |   399 -
 .../media/codemirror/mode/rust.js             |    72 -
 .../media/codemirror/mode/xml.js              |   413 -
 plugins/UiFileManager/media/css/Menu.css      |    33 -
 plugins/UiFileManager/media/css/Selectbar.css |    17 -
 .../UiFileManager/media/css/UiFileManager.css |   148 -
 plugins/UiFileManager/media/css/all.css       |   211 -
 plugins/UiFileManager/media/img/loading.gif   |   Bin 723 -> 0 bytes
 plugins/UiFileManager/media/js/Config.coffee  |    15 -
 .../UiFileManager/media/js/FileEditor.coffee  |   179 -
 .../media/js/FileItemList.coffee              |   194 -
 .../UiFileManager/media/js/FileList.coffee    |   268 -
 .../media/js/UiFileManager.coffee             |    79 -
 plugins/UiFileManager/media/js/all.js         |  3042 ---
 .../media/js/lib/Animation.coffee             |   138 -
 .../UiFileManager/media/js/lib/Class.coffee   |    23 -
 .../UiFileManager/media/js/lib/Dollar.coffee  |     3 -
 .../media/js/lib/ItemList.coffee              |    26 -
 .../UiFileManager/media/js/lib/Menu.coffee    |   110 -
 .../UiFileManager/media/js/lib/Promise.coffee |    74 -
 .../media/js/lib/Prototypes.coffee            |     9 -
 .../media/js/lib/RateLimitCb.coffee           |    62 -
 .../UiFileManager/media/js/lib/Text.coffee    |   147 -
 .../UiFileManager/media/js/lib/Time.coffee    |    59 -
 .../media/js/lib/ZeroFrame.coffee             |    85 -
 .../UiFileManager/media/js/lib/maquette.js    |   770 -
 plugins/UiFileManager/media/list.html         |    18 -
 .../UiPluginManager/UiPluginManagerPlugin.py  |   221 -
 plugins/UiPluginManager/__init__.py           |     1 -
 .../media/css/PluginManager.css               |    75 -
 plugins/UiPluginManager/media/css/all.css     |   129 -
 plugins/UiPluginManager/media/css/button.css  |    12 -
 plugins/UiPluginManager/media/css/fonts.css   |    30 -
 plugins/UiPluginManager/media/img/loading.gif |   Bin 723 -> 0 bytes
 .../media/js/PluginList.coffee                |   132 -
 .../media/js/UiPluginManager.coffee           |    71 -
 plugins/UiPluginManager/media/js/all.js       |  1606 --
 .../UiPluginManager/media/js/lib/Class.coffee |    23 -
 .../media/js/lib/Promise.coffee               |    74 -
 .../media/js/lib/Prototypes.coffee            |     8 -
 .../UiPluginManager/media/js/lib/maquette.js  |   770 -
 .../media/js/utils/Animation.coffee           |   138 -
 .../media/js/utils/Dollar.coffee              |     3 -
 .../media/js/utils/ZeroFrame.coffee           |    85 -
 .../UiPluginManager/media/plugin_manager.html |    19 -
 plugins/Zeroname/README.md                    |    55 -
 plugins/Zeroname/SiteManagerPlugin.py         |    69 -
 plugins/Zeroname/__init__.py                  |     1 -
 plugins/Zeroname/updater/zeroname_updater.py  |   249 -
 plugins/__init__.py                           |     0
 .../disabled-Bootstrapper/BootstrapperDb.py   |   156 -
 .../BootstrapperPlugin.py                     |   156 -
 .../Test/TestBootstrapper.py                  |   246 -
 .../disabled-Bootstrapper/Test/conftest.py    |     1 -
 plugins/disabled-Bootstrapper/Test/pytest.ini |     6 -
 plugins/disabled-Bootstrapper/__init__.py     |     1 -
 .../disabled-Bootstrapper/plugin_info.json    |     5 -
 .../disabled-Dnschain/SiteManagerPlugin.py    |   153 -
 plugins/disabled-Dnschain/UiRequestPlugin.py  |    34 -
 plugins/disabled-Dnschain/__init__.py         |     3 -
 .../DonationMessagePlugin.py                  |    22 -
 plugins/disabled-DonationMessage/__init__.py  |     1 -
 plugins/disabled-Multiuser/MultiuserPlugin.py |   275 -
 .../disabled-Multiuser/Test/TestMultiuser.py  |    14 -
 plugins/disabled-Multiuser/Test/conftest.py   |     1 -
 plugins/disabled-Multiuser/Test/pytest.ini    |     5 -
 plugins/disabled-Multiuser/UserPlugin.py      |    35 -
 plugins/disabled-Multiuser/__init__.py        |     1 -
 plugins/disabled-Multiuser/plugin_info.json   |     5 -
 plugins/disabled-StemPort/StemPortPlugin.py   |   135 -
 plugins/disabled-StemPort/__init__.py         |    10 -
 .../disabled-UiPassword/UiPasswordPlugin.py   |   183 -
 plugins/disabled-UiPassword/__init__.py       |     1 -
 plugins/disabled-UiPassword/login.html        |   116 -
 plugins/disabled-UiPassword/plugin_info.json  |     5 -
 .../SiteManagerPlugin.py                      |   180 -
 .../disabled-ZeronameLocal/UiRequestPlugin.py |    39 -
 plugins/disabled-ZeronameLocal/__init__.py    |     2 -
 src/Config.py                                 |     2 +-
 319 files changed, 5 insertions(+), 74308 deletions(-)
 create mode 100644 .gitmodules
 create mode 160000 plugins
 delete mode 100644 plugins/AnnounceBitTorrent/AnnounceBitTorrentPlugin.py
 delete mode 100644 plugins/AnnounceBitTorrent/__init__.py
 delete mode 100644 plugins/AnnounceBitTorrent/plugin_info.json
 delete mode 100644 plugins/AnnounceLocal/AnnounceLocalPlugin.py
 delete mode 100644 plugins/AnnounceLocal/BroadcastServer.py
 delete mode 100644 plugins/AnnounceLocal/Test/TestAnnounce.py
 delete mode 100644 plugins/AnnounceLocal/Test/conftest.py
 delete mode 100644 plugins/AnnounceLocal/Test/pytest.ini
 delete mode 100644 plugins/AnnounceLocal/__init__.py
 delete mode 100644 plugins/AnnounceLocal/plugin_info.json
 delete mode 100644 plugins/AnnounceShare/AnnounceSharePlugin.py
 delete mode 100644 plugins/AnnounceShare/Test/TestAnnounceShare.py
 delete mode 100644 plugins/AnnounceShare/Test/conftest.py
 delete mode 100644 plugins/AnnounceShare/Test/pytest.ini
 delete mode 100644 plugins/AnnounceShare/__init__.py
 delete mode 100644 plugins/AnnounceShare/plugin_info.json
 delete mode 100644 plugins/AnnounceZero/AnnounceZeroPlugin.py
 delete mode 100644 plugins/AnnounceZero/__init__.py
 delete mode 100644 plugins/AnnounceZero/plugin_info.json
 delete mode 100644 plugins/Benchmark/BenchmarkDb.py
 delete mode 100644 plugins/Benchmark/BenchmarkPack.py
 delete mode 100644 plugins/Benchmark/BenchmarkPlugin.py
 delete mode 100644 plugins/Benchmark/__init__.py
 delete mode 100644 plugins/Benchmark/media/benchmark.html
 delete mode 100644 plugins/Benchmark/plugin_info.json
 delete mode 100644 plugins/Bigfile/BigfilePiecefield.py
 delete mode 100644 plugins/Bigfile/BigfilePlugin.py
 delete mode 100644 plugins/Bigfile/Test/TestBigfile.py
 delete mode 100644 plugins/Bigfile/Test/conftest.py
 delete mode 100644 plugins/Bigfile/Test/pytest.ini
 delete mode 100644 plugins/Bigfile/__init__.py
 delete mode 100644 plugins/Chart/ChartCollector.py
 delete mode 100644 plugins/Chart/ChartDb.py
 delete mode 100644 plugins/Chart/ChartPlugin.py
 delete mode 100644 plugins/Chart/__init__.py
 delete mode 100644 plugins/Chart/plugin_info.json
 delete mode 100644 plugins/ContentFilter/ContentFilterPlugin.py
 delete mode 100644 plugins/ContentFilter/ContentFilterStorage.py
 delete mode 100644 plugins/ContentFilter/Test/TestContentFilter.py
 delete mode 100644 plugins/ContentFilter/Test/conftest.py
 delete mode 100644 plugins/ContentFilter/Test/pytest.ini
 delete mode 100644 plugins/ContentFilter/__init__.py
 delete mode 100644 plugins/ContentFilter/languages/hu.json
 delete mode 100644 plugins/ContentFilter/languages/it.json
 delete mode 100644 plugins/ContentFilter/languages/jp.json
 delete mode 100644 plugins/ContentFilter/languages/pt-br.json
 delete mode 100644 plugins/ContentFilter/languages/zh-tw.json
 delete mode 100644 plugins/ContentFilter/languages/zh.json
 delete mode 100644 plugins/ContentFilter/media/blocklisted.html
 delete mode 100644 plugins/ContentFilter/media/js/ZeroFrame.js
 delete mode 100644 plugins/ContentFilter/plugin_info.json
 delete mode 100644 plugins/Cors/CorsPlugin.py
 delete mode 100644 plugins/Cors/__init__.py
 delete mode 100644 plugins/Cors/plugin_info.json
 delete mode 100644 plugins/CryptMessage/CryptMessage.py
 delete mode 100644 plugins/CryptMessage/CryptMessagePlugin.py
 delete mode 100644 plugins/CryptMessage/Test/TestCrypt.py
 delete mode 100644 plugins/CryptMessage/Test/conftest.py
 delete mode 100644 plugins/CryptMessage/Test/pytest.ini
 delete mode 100644 plugins/CryptMessage/__init__.py
 delete mode 100644 plugins/CryptMessage/plugin_info.json
 delete mode 100644 plugins/FilePack/FilePackPlugin.py
 delete mode 100644 plugins/FilePack/__init__.py
 delete mode 100644 plugins/FilePack/plugin_info.json
 delete mode 100644 plugins/MergerSite/MergerSitePlugin.py
 delete mode 100644 plugins/MergerSite/__init__.py
 delete mode 100644 plugins/MergerSite/languages/es.json
 delete mode 100644 plugins/MergerSite/languages/fr.json
 delete mode 100644 plugins/MergerSite/languages/hu.json
 delete mode 100644 plugins/MergerSite/languages/it.json
 delete mode 100644 plugins/MergerSite/languages/jp.json
 delete mode 100644 plugins/MergerSite/languages/pt-br.json
 delete mode 100644 plugins/MergerSite/languages/tr.json
 delete mode 100644 plugins/MergerSite/languages/zh-tw.json
 delete mode 100644 plugins/MergerSite/languages/zh.json
 delete mode 100644 plugins/Newsfeed/NewsfeedPlugin.py
 delete mode 100644 plugins/Newsfeed/__init__.py
 delete mode 100644 plugins/OptionalManager/ContentDbPlugin.py
 delete mode 100644 plugins/OptionalManager/OptionalManagerPlugin.py
 delete mode 100644 plugins/OptionalManager/Test/TestOptionalManager.py
 delete mode 100644 plugins/OptionalManager/Test/conftest.py
 delete mode 100644 plugins/OptionalManager/Test/pytest.ini
 delete mode 100644 plugins/OptionalManager/UiWebsocketPlugin.py
 delete mode 100644 plugins/OptionalManager/__init__.py
 delete mode 100644 plugins/OptionalManager/languages/es.json
 delete mode 100644 plugins/OptionalManager/languages/fr.json
 delete mode 100644 plugins/OptionalManager/languages/hu.json
 delete mode 100644 plugins/OptionalManager/languages/jp.json
 delete mode 100644 plugins/OptionalManager/languages/pt-br.json
 delete mode 100644 plugins/OptionalManager/languages/zh-tw.json
 delete mode 100644 plugins/OptionalManager/languages/zh.json
 delete mode 100644 plugins/PeerDb/PeerDbPlugin.py
 delete mode 100644 plugins/PeerDb/__init__.py
 delete mode 100644 plugins/PeerDb/plugin_info.json
 delete mode 100644 plugins/Sidebar/ConsolePlugin.py
 delete mode 100644 plugins/Sidebar/SidebarPlugin.py
 delete mode 100644 plugins/Sidebar/ZipStream.py
 delete mode 100644 plugins/Sidebar/__init__.py
 delete mode 100644 plugins/Sidebar/languages/da.json
 delete mode 100644 plugins/Sidebar/languages/de.json
 delete mode 100644 plugins/Sidebar/languages/es.json
 delete mode 100644 plugins/Sidebar/languages/fr.json
 delete mode 100644 plugins/Sidebar/languages/hu.json
 delete mode 100644 plugins/Sidebar/languages/it.json
 delete mode 100644 plugins/Sidebar/languages/jp.json
 delete mode 100644 plugins/Sidebar/languages/pl.json
 delete mode 100644 plugins/Sidebar/languages/pt-br.json
 delete mode 100644 plugins/Sidebar/languages/ru.json
 delete mode 100644 plugins/Sidebar/languages/tr.json
 delete mode 100644 plugins/Sidebar/languages/zh-tw.json
 delete mode 100644 plugins/Sidebar/languages/zh.json
 delete mode 100644 plugins/Sidebar/media/Class.coffee
 delete mode 100644 plugins/Sidebar/media/Console.coffee
 delete mode 100644 plugins/Sidebar/media/Console.css
 delete mode 100644 plugins/Sidebar/media/Menu.coffee
 delete mode 100644 plugins/Sidebar/media/Menu.css
 delete mode 100644 plugins/Sidebar/media/Prototypes.coffee
 delete mode 100644 plugins/Sidebar/media/RateLimit.coffee
 delete mode 100644 plugins/Sidebar/media/Scrollable.js
 delete mode 100644 plugins/Sidebar/media/Scrollbable.css
 delete mode 100644 plugins/Sidebar/media/Sidebar.coffee
 delete mode 100644 plugins/Sidebar/media/Sidebar.css
 delete mode 100644 plugins/Sidebar/media/all.css
 delete mode 100644 plugins/Sidebar/media/all.js
 delete mode 100644 plugins/Sidebar/media/morphdom.js
 delete mode 100644 plugins/Sidebar/media_globe/Detector.js
 delete mode 100644 plugins/Sidebar/media_globe/Tween.js
 delete mode 100644 plugins/Sidebar/media_globe/all.js
 delete mode 100644 plugins/Sidebar/media_globe/globe.js
 delete mode 100644 plugins/Sidebar/media_globe/three.min.js
 delete mode 100644 plugins/Sidebar/media_globe/world.jpg
 delete mode 100644 plugins/Sidebar/plugin_info.json
 delete mode 100644 plugins/Stats/StatsPlugin.py
 delete mode 100644 plugins/Stats/__init__.py
 delete mode 100644 plugins/Stats/plugin_info.json
 delete mode 100644 plugins/TranslateSite/TranslateSitePlugin.py
 delete mode 100644 plugins/TranslateSite/__init__.py
 delete mode 100644 plugins/TranslateSite/plugin_info.json
 delete mode 100644 plugins/Trayicon/TrayiconPlugin.py
 delete mode 100644 plugins/Trayicon/__init__.py
 delete mode 100644 plugins/Trayicon/languages/es.json
 delete mode 100644 plugins/Trayicon/languages/fr.json
 delete mode 100644 plugins/Trayicon/languages/hu.json
 delete mode 100644 plugins/Trayicon/languages/it.json
 delete mode 100644 plugins/Trayicon/languages/jp.json
 delete mode 100644 plugins/Trayicon/languages/pl.json
 delete mode 100644 plugins/Trayicon/languages/pt-br.json
 delete mode 100644 plugins/Trayicon/languages/tr.json
 delete mode 100644 plugins/Trayicon/languages/zh-tw.json
 delete mode 100644 plugins/Trayicon/languages/zh.json
 delete mode 100644 plugins/Trayicon/lib/__init__.py
 delete mode 100644 plugins/Trayicon/lib/notificationicon.py
 delete mode 100644 plugins/Trayicon/lib/winfolders.py
 delete mode 100644 plugins/Trayicon/plugin_info.json
 delete mode 100644 plugins/Trayicon/trayicon.ico
 delete mode 100644 plugins/UiConfig/UiConfigPlugin.py
 delete mode 100644 plugins/UiConfig/__init__.py
 delete mode 100644 plugins/UiConfig/languages/hu.json
 delete mode 100644 plugins/UiConfig/languages/jp.json
 delete mode 100644 plugins/UiConfig/languages/pl.json
 delete mode 100644 plugins/UiConfig/languages/pt-br.json
 delete mode 100644 plugins/UiConfig/languages/zh.json
 delete mode 100644 plugins/UiConfig/media/config.html
 delete mode 100644 plugins/UiConfig/media/css/Config.css
 delete mode 100644 plugins/UiConfig/media/css/all.css
 delete mode 100644 plugins/UiConfig/media/css/button.css
 delete mode 100644 plugins/UiConfig/media/css/fonts.css
 delete mode 100644 plugins/UiConfig/media/img/loading.gif
 delete mode 100644 plugins/UiConfig/media/js/ConfigStorage.coffee
 delete mode 100644 plugins/UiConfig/media/js/ConfigView.coffee
 delete mode 100644 plugins/UiConfig/media/js/UiConfig.coffee
 delete mode 100644 plugins/UiConfig/media/js/all.js
 delete mode 100644 plugins/UiConfig/media/js/lib/Class.coffee
 delete mode 100644 plugins/UiConfig/media/js/lib/Promise.coffee
 delete mode 100644 plugins/UiConfig/media/js/lib/Prototypes.coffee
 delete mode 100644 plugins/UiConfig/media/js/lib/maquette.js
 delete mode 100644 plugins/UiConfig/media/js/utils/Animation.coffee
 delete mode 100644 plugins/UiConfig/media/js/utils/Dollar.coffee
 delete mode 100644 plugins/UiConfig/media/js/utils/ZeroFrame.coffee
 delete mode 100644 plugins/UiConfig/plugin_info.json
 delete mode 100644 plugins/UiFileManager/UiFileManagerPlugin.py
 delete mode 100644 plugins/UiFileManager/__init__.py
 delete mode 100644 plugins/UiFileManager/languages/hu.json
 delete mode 100644 plugins/UiFileManager/languages/jp.json
 delete mode 100644 plugins/UiFileManager/media/codemirror/LICENSE
 delete mode 100644 plugins/UiFileManager/media/codemirror/all.css
 delete mode 100644 plugins/UiFileManager/media/codemirror/all.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/base/codemirror.css
 delete mode 100644 plugins/UiFileManager/media/codemirror/base/codemirror.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/dialog/dialog.css
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/dialog/dialog.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/edit/closebrackets.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/edit/closetag.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/edit/continuelist.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/edit/matchbrackets.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/edit/matchtags.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/edit/trailingspace.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/fold/brace-fold.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/fold/comment-fold.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/fold/foldcode.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/fold/foldgutter.css
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/fold/foldgutter.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/fold/indent-fold.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/fold/markdown-fold.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/fold/xml-fold.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/hint/anyword-hint.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/hint/html-hint.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/hint/show-hint.css
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/hint/show-hint.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/hint/sql-hint.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/hint/xml-hint.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/lint/json-lint.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/lint/jsonlint.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/lint/lint.css
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/lint/lint.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/mdn-like-custom.css
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/scroll/annotatescrollbar.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/scroll/scrollpastend.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/scroll/simplescrollbars.css
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/scroll/simplescrollbars.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/search/jump-to-line.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/search/match-highlighter.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/search/matchesonscrollbar.css
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/search/matchesonscrollbar.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/search/search.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/search/searchcursor.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/selection/active-line.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/selection/mark-selection.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/selection/selection-pointer.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/simple.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/extension/sublime.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/mode/coffeescript.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/mode/css.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/mode/go.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/mode/htmlembedded.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/mode/htmlmixed.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/mode/javascript.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/mode/markdown.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/mode/python.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/mode/rust.js
 delete mode 100644 plugins/UiFileManager/media/codemirror/mode/xml.js
 delete mode 100644 plugins/UiFileManager/media/css/Menu.css
 delete mode 100644 plugins/UiFileManager/media/css/Selectbar.css
 delete mode 100644 plugins/UiFileManager/media/css/UiFileManager.css
 delete mode 100644 plugins/UiFileManager/media/css/all.css
 delete mode 100644 plugins/UiFileManager/media/img/loading.gif
 delete mode 100644 plugins/UiFileManager/media/js/Config.coffee
 delete mode 100644 plugins/UiFileManager/media/js/FileEditor.coffee
 delete mode 100644 plugins/UiFileManager/media/js/FileItemList.coffee
 delete mode 100644 plugins/UiFileManager/media/js/FileList.coffee
 delete mode 100644 plugins/UiFileManager/media/js/UiFileManager.coffee
 delete mode 100644 plugins/UiFileManager/media/js/all.js
 delete mode 100644 plugins/UiFileManager/media/js/lib/Animation.coffee
 delete mode 100644 plugins/UiFileManager/media/js/lib/Class.coffee
 delete mode 100644 plugins/UiFileManager/media/js/lib/Dollar.coffee
 delete mode 100644 plugins/UiFileManager/media/js/lib/ItemList.coffee
 delete mode 100644 plugins/UiFileManager/media/js/lib/Menu.coffee
 delete mode 100644 plugins/UiFileManager/media/js/lib/Promise.coffee
 delete mode 100644 plugins/UiFileManager/media/js/lib/Prototypes.coffee
 delete mode 100644 plugins/UiFileManager/media/js/lib/RateLimitCb.coffee
 delete mode 100644 plugins/UiFileManager/media/js/lib/Text.coffee
 delete mode 100644 plugins/UiFileManager/media/js/lib/Time.coffee
 delete mode 100644 plugins/UiFileManager/media/js/lib/ZeroFrame.coffee
 delete mode 100644 plugins/UiFileManager/media/js/lib/maquette.js
 delete mode 100644 plugins/UiFileManager/media/list.html
 delete mode 100644 plugins/UiPluginManager/UiPluginManagerPlugin.py
 delete mode 100644 plugins/UiPluginManager/__init__.py
 delete mode 100644 plugins/UiPluginManager/media/css/PluginManager.css
 delete mode 100644 plugins/UiPluginManager/media/css/all.css
 delete mode 100644 plugins/UiPluginManager/media/css/button.css
 delete mode 100644 plugins/UiPluginManager/media/css/fonts.css
 delete mode 100644 plugins/UiPluginManager/media/img/loading.gif
 delete mode 100644 plugins/UiPluginManager/media/js/PluginList.coffee
 delete mode 100644 plugins/UiPluginManager/media/js/UiPluginManager.coffee
 delete mode 100644 plugins/UiPluginManager/media/js/all.js
 delete mode 100644 plugins/UiPluginManager/media/js/lib/Class.coffee
 delete mode 100644 plugins/UiPluginManager/media/js/lib/Promise.coffee
 delete mode 100644 plugins/UiPluginManager/media/js/lib/Prototypes.coffee
 delete mode 100644 plugins/UiPluginManager/media/js/lib/maquette.js
 delete mode 100644 plugins/UiPluginManager/media/js/utils/Animation.coffee
 delete mode 100644 plugins/UiPluginManager/media/js/utils/Dollar.coffee
 delete mode 100644 plugins/UiPluginManager/media/js/utils/ZeroFrame.coffee
 delete mode 100644 plugins/UiPluginManager/media/plugin_manager.html
 delete mode 100644 plugins/Zeroname/README.md
 delete mode 100644 plugins/Zeroname/SiteManagerPlugin.py
 delete mode 100644 plugins/Zeroname/__init__.py
 delete mode 100644 plugins/Zeroname/updater/zeroname_updater.py
 delete mode 100644 plugins/__init__.py
 delete mode 100644 plugins/disabled-Bootstrapper/BootstrapperDb.py
 delete mode 100644 plugins/disabled-Bootstrapper/BootstrapperPlugin.py
 delete mode 100644 plugins/disabled-Bootstrapper/Test/TestBootstrapper.py
 delete mode 100644 plugins/disabled-Bootstrapper/Test/conftest.py
 delete mode 100644 plugins/disabled-Bootstrapper/Test/pytest.ini
 delete mode 100644 plugins/disabled-Bootstrapper/__init__.py
 delete mode 100644 plugins/disabled-Bootstrapper/plugin_info.json
 delete mode 100644 plugins/disabled-Dnschain/SiteManagerPlugin.py
 delete mode 100644 plugins/disabled-Dnschain/UiRequestPlugin.py
 delete mode 100644 plugins/disabled-Dnschain/__init__.py
 delete mode 100644 plugins/disabled-DonationMessage/DonationMessagePlugin.py
 delete mode 100644 plugins/disabled-DonationMessage/__init__.py
 delete mode 100644 plugins/disabled-Multiuser/MultiuserPlugin.py
 delete mode 100644 plugins/disabled-Multiuser/Test/TestMultiuser.py
 delete mode 100644 plugins/disabled-Multiuser/Test/conftest.py
 delete mode 100644 plugins/disabled-Multiuser/Test/pytest.ini
 delete mode 100644 plugins/disabled-Multiuser/UserPlugin.py
 delete mode 100644 plugins/disabled-Multiuser/__init__.py
 delete mode 100644 plugins/disabled-Multiuser/plugin_info.json
 delete mode 100644 plugins/disabled-StemPort/StemPortPlugin.py
 delete mode 100644 plugins/disabled-StemPort/__init__.py
 delete mode 100644 plugins/disabled-UiPassword/UiPasswordPlugin.py
 delete mode 100644 plugins/disabled-UiPassword/__init__.py
 delete mode 100644 plugins/disabled-UiPassword/login.html
 delete mode 100644 plugins/disabled-UiPassword/plugin_info.json
 delete mode 100644 plugins/disabled-ZeronameLocal/SiteManagerPlugin.py
 delete mode 100644 plugins/disabled-ZeronameLocal/UiRequestPlugin.py
 delete mode 100644 plugins/disabled-ZeronameLocal/__init__.py

diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 00000000..2c602a5a
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "plugins"]
+	path = plugins
+	url = https://github.com/ZeroNetX/ZeroNet-Plugins.git
diff --git a/plugins b/plugins
new file mode 160000
index 00000000..5c22d549
--- /dev/null
+++ b/plugins
@@ -0,0 +1 @@
+Subproject commit 5c22d54984363d425d9f7b6aabbf1a871e7e60a1
diff --git a/plugins/AnnounceBitTorrent/AnnounceBitTorrentPlugin.py b/plugins/AnnounceBitTorrent/AnnounceBitTorrentPlugin.py
deleted file mode 100644
index fab7bb1f..00000000
--- a/plugins/AnnounceBitTorrent/AnnounceBitTorrentPlugin.py
+++ /dev/null
@@ -1,148 +0,0 @@
-import time
-import urllib.request
-import struct
-import socket
-
-import lib.bencode_open as bencode_open
-from lib.subtl.subtl import UdpTrackerClient
-import socks
-import sockshandler
-import gevent
-
-from Plugin import PluginManager
-from Config import config
-from Debug import Debug
-from util import helper
-
-
-# We can only import plugin host clases after the plugins are loaded
-@PluginManager.afterLoad
-def importHostClasses():
-    global Peer, AnnounceError
-    from Peer import Peer
-    from Site.SiteAnnouncer import AnnounceError
-
-
-@PluginManager.registerTo("SiteAnnouncer")
-class SiteAnnouncerPlugin(object):
-    def getSupportedTrackers(self):
-        trackers = super(SiteAnnouncerPlugin, self).getSupportedTrackers()
-        if config.disable_udp or config.trackers_proxy != "disable":
-            trackers = [tracker for tracker in trackers if not tracker.startswith("udp://")]
-
-        return trackers
-
-    def getTrackerHandler(self, protocol):
-        if protocol == "udp":
-            handler = self.announceTrackerUdp
-        elif protocol == "http":
-            handler = self.announceTrackerHttp
-        elif protocol == "https":
-            handler = self.announceTrackerHttps
-        else:
-            handler = super(SiteAnnouncerPlugin, self).getTrackerHandler(protocol)
-        return handler
-
-    def announceTrackerUdp(self, tracker_address, mode="start", num_want=10):
-        s = time.time()
-        if config.disable_udp:
-            raise AnnounceError("Udp disabled by config")
-        if config.trackers_proxy != "disable":
-            raise AnnounceError("Udp trackers not available with proxies")
-
-        ip, port = tracker_address.split("/")[0].split(":")
-        tracker = UdpTrackerClient(ip, int(port))
-        if helper.getIpType(ip) in self.getOpenedServiceTypes():
-            tracker.peer_port = self.fileserver_port
-        else:
-            tracker.peer_port = 0
-        tracker.connect()
-        if not tracker.poll_once():
-            raise AnnounceError("Could not connect")
-        tracker.announce(info_hash=self.site.address_sha1, num_want=num_want, left=431102370)
-        back = tracker.poll_once()
-        if not back:
-            raise AnnounceError("No response after %.0fs" % (time.time() - s))
-        elif type(back) is dict and "response" in back:
-            peers = back["response"]["peers"]
-        else:
-            raise AnnounceError("Invalid response: %r" % back)
-
-        return peers
-
-    def httpRequest(self, url):
-        headers = {
-            'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11',
-            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
-            'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
-            'Accept-Encoding': 'none',
-            'Accept-Language': 'en-US,en;q=0.8',
-            'Connection': 'keep-alive'
-        }
-
-        req = urllib.request.Request(url, headers=headers)
-
-        if config.trackers_proxy == "tor":
-            tor_manager = self.site.connection_server.tor_manager
-            handler = sockshandler.SocksiPyHandler(socks.SOCKS5, tor_manager.proxy_ip, tor_manager.proxy_port)
-            opener = urllib.request.build_opener(handler)
-            return opener.open(req, timeout=50)
-        elif config.trackers_proxy == "disable":
-            return urllib.request.urlopen(req, timeout=25)
-        else:
-            proxy_ip, proxy_port = config.trackers_proxy.split(":")
-            handler = sockshandler.SocksiPyHandler(socks.SOCKS5, proxy_ip, int(proxy_port))
-            opener = urllib.request.build_opener(handler)
-            return opener.open(req, timeout=50)
-
-    def announceTrackerHttps(self, *args, **kwargs):
-        kwargs["protocol"] = "https"
-        return self.announceTrackerHttp(*args, **kwargs)
-
-    def announceTrackerHttp(self, tracker_address, mode="start", num_want=10, protocol="http"):
-        tracker_ip, tracker_port = tracker_address.rsplit(":", 1)
-        if helper.getIpType(tracker_ip) in self.getOpenedServiceTypes():
-            port = self.fileserver_port
-        else:
-            port = 1
-        params = {
-            'info_hash': self.site.address_sha1,
-            'peer_id': self.peer_id, 'port': port,
-            'uploaded': 0, 'downloaded': 0, 'left': 431102370, 'compact': 1, 'numwant': num_want,
-            'event': 'started'
-        }
-
-        url = protocol + "://" + tracker_address + "?" + urllib.parse.urlencode(params)
-
-        s = time.time()
-        response = None
-        # Load url
-        if config.tor == "always" or config.trackers_proxy != "disable":
-            timeout = 60
-        else:
-            timeout = 30
-
-        with gevent.Timeout(timeout, False):  # Make sure of timeout
-            req = self.httpRequest(url)
-            response = req.read()
-            req.close()
-            req = None
-
-        if not response:
-            raise AnnounceError("No response after %.0fs" % (time.time() - s))
-
-        # Decode peers
-        try:
-            peer_data = bencode_open.loads(response)[b"peers"]
-            response = None
-            peer_count = int(len(peer_data) / 6)
-            peers = []
-            for peer_offset in range(peer_count):
-                off = 6 * peer_offset
-                peer = peer_data[off:off + 6]
-                addr, port = struct.unpack('!LH', peer)
-                peers.append({"addr": socket.inet_ntoa(struct.pack('!L', addr)), "port": port})
-        except Exception as err:
-            raise AnnounceError("Invalid response: %r (%s)" % (response, Debug.formatException(err)))
-
-        return peers
diff --git a/plugins/AnnounceBitTorrent/__init__.py b/plugins/AnnounceBitTorrent/__init__.py
deleted file mode 100644
index c7422855..00000000
--- a/plugins/AnnounceBitTorrent/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import AnnounceBitTorrentPlugin
\ No newline at end of file
diff --git a/plugins/AnnounceBitTorrent/plugin_info.json b/plugins/AnnounceBitTorrent/plugin_info.json
deleted file mode 100644
index 824749ee..00000000
--- a/plugins/AnnounceBitTorrent/plugin_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"name": "AnnounceBitTorrent",
-	"description": "Discover new peers using BitTorrent trackers.",
-	"default": "enabled"
-}
\ No newline at end of file
diff --git a/plugins/AnnounceLocal/AnnounceLocalPlugin.py b/plugins/AnnounceLocal/AnnounceLocalPlugin.py
deleted file mode 100644
index b9225966..00000000
--- a/plugins/AnnounceLocal/AnnounceLocalPlugin.py
+++ /dev/null
@@ -1,147 +0,0 @@
-import time
-
-import gevent
-
-from Plugin import PluginManager
-from Config import config
-from . import BroadcastServer
-
-
-@PluginManager.registerTo("SiteAnnouncer")
-class SiteAnnouncerPlugin(object):
-    def announce(self, force=False, *args, **kwargs):
-        local_announcer = self.site.connection_server.local_announcer
-
-        thread = None
-        if local_announcer and (force or time.time() - local_announcer.last_discover > 5 * 60):
-            thread = gevent.spawn(local_announcer.discover, force=force)
-        back = super(SiteAnnouncerPlugin, self).announce(force=force, *args, **kwargs)
-
-        if thread:
-            thread.join()
-
-        return back
-
-
-class LocalAnnouncer(BroadcastServer.BroadcastServer):
-    def __init__(self, server, listen_port):
-        super(LocalAnnouncer, self).__init__("zeronet", listen_port=listen_port)
-        self.server = server
-
-        self.sender_info["peer_id"] = self.server.peer_id
-        self.sender_info["port"] = self.server.port
-        self.sender_info["broadcast_port"] = listen_port
-        self.sender_info["rev"] = config.rev
-
-        self.known_peers = {}
-        self.last_discover = 0
-
-    def discover(self, force=False):
-        self.log.debug("Sending discover request (force: %s)" % force)
-        self.last_discover = time.time()
-        if force:  # Probably new site added, clean cache
-            self.known_peers = {}
-
-        for peer_id, known_peer in list(self.known_peers.items()):
-            if time.time() - known_peer["found"] > 20 * 60:
-                del(self.known_peers[peer_id])
-                self.log.debug("Timeout, removing from known_peers: %s" % peer_id)
-        self.broadcast({"cmd": "discoverRequest", "params": {}}, port=self.listen_port)
-
-    def actionDiscoverRequest(self, sender, params):
-        back = {
-            "cmd": "discoverResponse",
-            "params": {
-                "sites_changed": self.server.site_manager.sites_changed
-            }
-        }
-
-        if sender["peer_id"] not in self.known_peers:
-            self.known_peers[sender["peer_id"]] = {"added": time.time(), "sites_changed": 0, "updated": 0, "found": time.time()}
-            self.log.debug("Got discover request from unknown peer %s (%s), time to refresh known peers" % (sender["ip"], sender["peer_id"]))
-            gevent.spawn_later(1.0, self.discover)  # Let the response arrive first to the requester
-
-        return back
-
-    def actionDiscoverResponse(self, sender, params):
-        if sender["peer_id"] in self.known_peers:
-            self.known_peers[sender["peer_id"]]["found"] = time.time()
-        if params["sites_changed"] != self.known_peers.get(sender["peer_id"], {}).get("sites_changed"):
-            # Peer's site list changed, request the list of new sites
-            return {"cmd": "siteListRequest"}
-        else:
-            # Peer's site list is the same
-            for site in self.server.sites.values():
-                peer = site.peers.get("%s:%s" % (sender["ip"], sender["port"]))
-                if peer:
-                    peer.found("local")
-
-    def actionSiteListRequest(self, sender, params):
-        back = []
-        sites = list(self.server.sites.values())
-
-        # Split adresses to group of 100 to avoid UDP size limit
-        site_groups = [sites[i:i + 100] for i in range(0, len(sites), 100)]
-        for site_group in site_groups:
-            res = {}
-            res["sites_changed"] = self.server.site_manager.sites_changed
-            res["sites"] = [site.address_hash for site in site_group]
-            back.append({"cmd": "siteListResponse", "params": res})
-        return back
-
-    def actionSiteListResponse(self, sender, params):
-        s = time.time()
-        peer_sites = set(params["sites"])
-        num_found = 0
-        added_sites = []
-        for site in self.server.sites.values():
-            if site.address_hash in peer_sites:
-                added = site.addPeer(sender["ip"], sender["port"], source="local")
-                num_found += 1
-                if added:
-                    site.worker_manager.onPeers()
-                    site.updateWebsocket(peers_added=1)
-                    added_sites.append(site)
-
-        # Save sites changed value to avoid unnecessary site list download
-        if sender["peer_id"] not in self.known_peers:
-            self.known_peers[sender["peer_id"]] = {"added": time.time()}
-
-        self.known_peers[sender["peer_id"]]["sites_changed"] = params["sites_changed"]
-        self.known_peers[sender["peer_id"]]["updated"] = time.time()
-        self.known_peers[sender["peer_id"]]["found"] = time.time()
-
-        self.log.debug(
-            "Tracker result: Discover from %s response parsed in %.3fs, found: %s added: %s of %s" %
-            (sender["ip"], time.time() - s, num_found, added_sites, len(peer_sites))
-        )
-
-
-@PluginManager.registerTo("FileServer")
-class FileServerPlugin(object):
-    def __init__(self, *args, **kwargs):
-        super(FileServerPlugin, self).__init__(*args, **kwargs)
-        if config.broadcast_port and config.tor != "always" and not config.disable_udp:
-            self.local_announcer = LocalAnnouncer(self, config.broadcast_port)
-        else:
-            self.local_announcer = None
-
-    def start(self, *args, **kwargs):
-        if self.local_announcer:
-            gevent.spawn(self.local_announcer.start)
-        return super(FileServerPlugin, self).start(*args, **kwargs)
-
-    def stop(self):
-        if self.local_announcer:
-            self.local_announcer.stop()
-        res = super(FileServerPlugin, self).stop()
-        return res
-
-
-@PluginManager.registerTo("ConfigPlugin")
-class ConfigPlugin(object):
-    def createArguments(self):
-        group = self.parser.add_argument_group("AnnounceLocal plugin")
-        group.add_argument('--broadcast_port', help='UDP broadcasting port for local peer discovery', default=1544, type=int, metavar='port')
-
-        return super(ConfigPlugin, self).createArguments()
diff --git a/plugins/AnnounceLocal/BroadcastServer.py b/plugins/AnnounceLocal/BroadcastServer.py
deleted file mode 100644
index 74678896..00000000
--- a/plugins/AnnounceLocal/BroadcastServer.py
+++ /dev/null
@@ -1,139 +0,0 @@
-import socket
-import logging
-import time
-from contextlib import closing
-
-from Debug import Debug
-from util import UpnpPunch
-from util import Msgpack
-
-
-class BroadcastServer(object):
-    def __init__(self, service_name, listen_port=1544, listen_ip=''):
-        self.log = logging.getLogger("BroadcastServer")
-        self.listen_port = listen_port
-        self.listen_ip = listen_ip
-
-        self.running = False
-        self.sock = None
-        self.sender_info = {"service": service_name}
-
-    def createBroadcastSocket(self):
-        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-        sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
-        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-        if hasattr(socket, 'SO_REUSEPORT'):
-            try:
-                sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
-            except Exception as err:
-                self.log.warning("Error setting SO_REUSEPORT: %s" % err)
-
-        binded = False
-        for retry in range(3):
-            try:
-                sock.bind((self.listen_ip, self.listen_port))
-                binded = True
-                break
-            except Exception as err:
-                self.log.error(
-                    "Socket bind to %s:%s error: %s, retry #%s" %
-                    (self.listen_ip, self.listen_port, Debug.formatException(err), retry)
-                )
-                time.sleep(retry)
-
-        if binded:
-            return sock
-        else:
-            return False
-
-    def start(self):  # Listens for discover requests
-        self.sock = self.createBroadcastSocket()
-        if not self.sock:
-            self.log.error("Unable to listen on port %s" % self.listen_port)
-            return
-
-        self.log.debug("Started on port %s" % self.listen_port)
-
-        self.running = True
-
-        while self.running:
-            try:
-                data, addr = self.sock.recvfrom(8192)
-            except Exception as err:
-                if self.running:
-                    self.log.error("Listener receive error: %s" % err)
-                continue
-
-            if not self.running:
-                break
-
-            try:
-                message = Msgpack.unpack(data)
-                response_addr, message = self.handleMessage(addr, message)
-                if message:
-                    self.send(response_addr, message)
-            except Exception as err:
-                self.log.error("Handlemessage error: %s" % Debug.formatException(err))
-        self.log.debug("Stopped listening on port %s" % self.listen_port)
-
-    def stop(self):
-        self.log.debug("Stopping, socket: %s" % self.sock)
-        self.running = False
-        if self.sock:
-            self.sock.close()
-
-    def send(self, addr, message):
-        if type(message) is not list:
-            message = [message]
-
-        for message_part in message:
-            message_part["sender"] = self.sender_info
-
-            self.log.debug("Send to %s: %s" % (addr, message_part["cmd"]))
-            with closing(socket.socket(socket.AF_INET, socket.SOCK_DGRAM)) as sock:
-                sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-                sock.sendto(Msgpack.pack(message_part), addr)
-
-    def getMyIps(self):
-        return UpnpPunch._get_local_ips()
-
-    def broadcast(self, message, port=None):
-        if not port:
-            port = self.listen_port
-
-        my_ips = self.getMyIps()
-        addr = ("255.255.255.255", port)
-
-        message["sender"] = self.sender_info
-        self.log.debug("Broadcast using ips %s on port %s: %s" % (my_ips, port, message["cmd"]))
-
-        for my_ip in my_ips:
-            try:
-                with closing(socket.socket(socket.AF_INET, socket.SOCK_DGRAM)) as sock:
-                    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-                    sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
-                    sock.bind((my_ip, 0))
-                    sock.sendto(Msgpack.pack(message), addr)
-            except Exception as err:
-                self.log.warning("Error sending broadcast using ip %s: %s" % (my_ip, err))
-
-    def handleMessage(self, addr, message):
-        self.log.debug("Got from %s: %s" % (addr, message["cmd"]))
-        cmd = message["cmd"]
-        params = message.get("params", {})
-        sender = message["sender"]
-        sender["ip"] = addr[0]
-
-        func_name = "action" + cmd[0].upper() + cmd[1:]
-        func = getattr(self, func_name, None)
-
-        if sender["service"] != "zeronet" or sender["peer_id"] == self.sender_info["peer_id"]:
-            # Skip messages not for us or sent by us
-            message = None
-        elif func:
-            message = func(sender, params)
-        else:
-            self.log.debug("Unknown cmd: %s" % cmd)
-            message = None
-
-        return (sender["ip"], sender["broadcast_port"]), message
diff --git a/plugins/AnnounceLocal/Test/TestAnnounce.py b/plugins/AnnounceLocal/Test/TestAnnounce.py
deleted file mode 100644
index 4def02ed..00000000
--- a/plugins/AnnounceLocal/Test/TestAnnounce.py
+++ /dev/null
@@ -1,113 +0,0 @@
-import time
-import copy
-
-import gevent
-import pytest
-import mock
-
-from AnnounceLocal import AnnounceLocalPlugin
-from File import FileServer
-from Test import Spy
-
-@pytest.fixture
-def announcer(file_server, site):
-    file_server.sites[site.address] = site
-    announcer = AnnounceLocalPlugin.LocalAnnouncer(file_server, listen_port=1100)
-    file_server.local_announcer = announcer
-    announcer.listen_port = 1100
-    announcer.sender_info["broadcast_port"] = 1100
-    announcer.getMyIps = mock.MagicMock(return_value=["127.0.0.1"])
-    announcer.discover = mock.MagicMock(return_value=False)  # Don't send discover requests automatically
-    gevent.spawn(announcer.start)
-    time.sleep(0.5)
-
-    assert file_server.local_announcer.running
-    return file_server.local_announcer
-
-@pytest.fixture
-def announcer_remote(request, site_temp):
-    file_server_remote = FileServer("127.0.0.1", 1545)
-    file_server_remote.sites[site_temp.address] = site_temp
-    announcer = AnnounceLocalPlugin.LocalAnnouncer(file_server_remote, listen_port=1101)
-    file_server_remote.local_announcer = announcer
-    announcer.listen_port = 1101
-    announcer.sender_info["broadcast_port"] = 1101
-    announcer.getMyIps = mock.MagicMock(return_value=["127.0.0.1"])
-    announcer.discover = mock.MagicMock(return_value=False)  # Don't send discover requests automatically
-    gevent.spawn(announcer.start)
-    time.sleep(0.5)
-
-    assert file_server_remote.local_announcer.running
-
-    def cleanup():
-        file_server_remote.stop()
-    request.addfinalizer(cleanup)
-
-
-    return file_server_remote.local_announcer
-
-@pytest.mark.usefixtures("resetSettings")
-@pytest.mark.usefixtures("resetTempSettings")
-class TestAnnounce:
-    def testSenderInfo(self, announcer):
-        sender_info = announcer.sender_info
-        assert sender_info["port"] > 0
-        assert len(sender_info["peer_id"]) == 20
-        assert sender_info["rev"] > 0
-
-    def testIgnoreSelfMessages(self, announcer):
-        # No response to messages that has same peer_id as server
-        assert not announcer.handleMessage(("0.0.0.0", 123), {"cmd": "discoverRequest", "sender": announcer.sender_info, "params": {}})[1]
-
-        # Response to messages with different peer id
-        sender_info = copy.copy(announcer.sender_info)
-        sender_info["peer_id"] += "-"
-        addr, res = announcer.handleMessage(("0.0.0.0", 123), {"cmd": "discoverRequest", "sender": sender_info, "params": {}})
-        assert res["params"]["sites_changed"] > 0
-
-    def testDiscoverRequest(self, announcer, announcer_remote):
-        assert len(announcer_remote.known_peers) == 0
-        with Spy.Spy(announcer_remote, "handleMessage") as responses:
-            announcer_remote.broadcast({"cmd": "discoverRequest", "params": {}}, port=announcer.listen_port)
-            time.sleep(0.1)
-
-        response_cmds = [response[1]["cmd"] for response in responses]
-        assert response_cmds == ["discoverResponse", "siteListResponse"]
-        assert len(responses[-1][1]["params"]["sites"]) == 1
-
-        # It should only request siteList if sites_changed value is different from last response
-        with Spy.Spy(announcer_remote, "handleMessage") as responses:
-            announcer_remote.broadcast({"cmd": "discoverRequest", "params": {}}, port=announcer.listen_port)
-            time.sleep(0.1)
-
-        response_cmds = [response[1]["cmd"] for response in responses]
-        assert response_cmds == ["discoverResponse"]
-
-    def testPeerDiscover(self, announcer, announcer_remote, site):
-        assert announcer.server.peer_id != announcer_remote.server.peer_id
-        assert len(list(announcer.server.sites.values())[0].peers) == 0
-        announcer.broadcast({"cmd": "discoverRequest"}, port=announcer_remote.listen_port)
-        time.sleep(0.1)
-        assert len(list(announcer.server.sites.values())[0].peers) == 1
-
-    def testRecentPeerList(self, announcer, announcer_remote, site):
-        assert len(site.peers_recent) == 0
-        assert len(site.peers) == 0
-        with Spy.Spy(announcer, "handleMessage") as responses:
-            announcer.broadcast({"cmd": "discoverRequest", "params": {}}, port=announcer_remote.listen_port)
-            time.sleep(0.1)
-        assert [response[1]["cmd"] for response in responses] == ["discoverResponse", "siteListResponse"]
-        assert len(site.peers_recent) == 1
-        assert len(site.peers) == 1
-
-        # It should update peer without siteListResponse
-        last_time_found = list(site.peers.values())[0].time_found
-        site.peers_recent.clear()
-        with Spy.Spy(announcer, "handleMessage") as responses:
-            announcer.broadcast({"cmd": "discoverRequest", "params": {}}, port=announcer_remote.listen_port)
-            time.sleep(0.1)
-        assert [response[1]["cmd"] for response in responses] == ["discoverResponse"]
-        assert len(site.peers_recent) == 1
-        assert list(site.peers.values())[0].time_found > last_time_found
-
-
diff --git a/plugins/AnnounceLocal/Test/conftest.py b/plugins/AnnounceLocal/Test/conftest.py
deleted file mode 100644
index a88c642c..00000000
--- a/plugins/AnnounceLocal/Test/conftest.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from src.Test.conftest import *
-
-from Config import config
-config.broadcast_port = 0
diff --git a/plugins/AnnounceLocal/Test/pytest.ini b/plugins/AnnounceLocal/Test/pytest.ini
deleted file mode 100644
index d09210d1..00000000
--- a/plugins/AnnounceLocal/Test/pytest.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[pytest]
-python_files = Test*.py
-addopts = -rsxX -v --durations=6
-markers =
-    webtest: mark a test as a webtest.
\ No newline at end of file
diff --git a/plugins/AnnounceLocal/__init__.py b/plugins/AnnounceLocal/__init__.py
deleted file mode 100644
index 5b80abd2..00000000
--- a/plugins/AnnounceLocal/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import AnnounceLocalPlugin
\ No newline at end of file
diff --git a/plugins/AnnounceLocal/plugin_info.json b/plugins/AnnounceLocal/plugin_info.json
deleted file mode 100644
index 2908cbf1..00000000
--- a/plugins/AnnounceLocal/plugin_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"name": "AnnounceLocal",
-	"description": "Discover LAN clients using UDP broadcasting.",
-	"default": "enabled"
-}
\ No newline at end of file
diff --git a/plugins/AnnounceShare/AnnounceSharePlugin.py b/plugins/AnnounceShare/AnnounceSharePlugin.py
deleted file mode 100644
index 057ce55a..00000000
--- a/plugins/AnnounceShare/AnnounceSharePlugin.py
+++ /dev/null
@@ -1,190 +0,0 @@
-import time
-import os
-import logging
-import json
-import atexit
-
-import gevent
-
-from Config import config
-from Plugin import PluginManager
-from util import helper
-
-
-class TrackerStorage(object):
-    def __init__(self):
-        self.log = logging.getLogger("TrackerStorage")
-        self.file_path = "%s/trackers.json" % config.data_dir
-        self.load()
-        self.time_discover = 0.0
-        atexit.register(self.save)
-
-    def getDefaultFile(self):
-        return {"shared": {}}
-
-    def onTrackerFound(self, tracker_address, type="shared", my=False):
-        if not tracker_address.startswith("zero://"):
-            return False
-
-        trackers = self.getTrackers()
-        added = False
-        if tracker_address not in trackers:
-            trackers[tracker_address] = {
-                "time_added": time.time(),
-                "time_success": 0,
-                "latency": 99.0,
-                "num_error": 0,
-                "my": False
-            }
-            self.log.debug("New tracker found: %s" % tracker_address)
-            added = True
-
-        trackers[tracker_address]["time_found"] = time.time()
-        trackers[tracker_address]["my"] = my
-        return added
-
-    def onTrackerSuccess(self, tracker_address, latency):
-        trackers = self.getTrackers()
-        if tracker_address not in trackers:
-            return False
-
-        trackers[tracker_address]["latency"] = latency
-        trackers[tracker_address]["time_success"] = time.time()
-        trackers[tracker_address]["num_error"] = 0
-
-    def onTrackerError(self, tracker_address):
-        trackers = self.getTrackers()
-        if tracker_address not in trackers:
-            return False
-
-        trackers[tracker_address]["time_error"] = time.time()
-        trackers[tracker_address]["num_error"] += 1
-
-        if len(self.getWorkingTrackers()) >= config.working_shared_trackers_limit:
-            error_limit = 5
-        else:
-            error_limit = 30
-        error_limit
-
-        if trackers[tracker_address]["num_error"] > error_limit and trackers[tracker_address]["time_success"] < time.time() - 60 * 60:
-            self.log.debug("Tracker %s looks down, removing." % tracker_address)
-            del trackers[tracker_address]
-
-    def getTrackers(self, type="shared"):
-        return self.file_content.setdefault(type, {})
-
-    def getWorkingTrackers(self, type="shared"):
-        trackers = {
-            key: tracker for key, tracker in self.getTrackers(type).items()
-            if tracker["time_success"] > time.time() - 60 * 60
-        }
-        return trackers
-
-    def getFileContent(self):
-        if not os.path.isfile(self.file_path):
-            open(self.file_path, "w").write("{}")
-            return self.getDefaultFile()
-        try:
-            return json.load(open(self.file_path))
-        except Exception as err:
-            self.log.error("Error loading trackers list: %s" % err)
-            return self.getDefaultFile()
-
-    def load(self):
-        self.file_content = self.getFileContent()
-
-        trackers = self.getTrackers()
-        self.log.debug("Loaded %s shared trackers" % len(trackers))
-        for address, tracker in list(trackers.items()):
-            tracker["num_error"] = 0
-            if not address.startswith("zero://"):
-                del trackers[address]
-
-    def save(self):
-        s = time.time()
-        helper.atomicWrite(self.file_path, json.dumps(self.file_content, indent=2, sort_keys=True).encode("utf8"))
-        self.log.debug("Saved in %.3fs" % (time.time() - s))
-
-    def discoverTrackers(self, peers):
-        if len(self.getWorkingTrackers()) > config.working_shared_trackers_limit:
-            return False
-        s = time.time()
-        num_success = 0
-        for peer in peers:
-            if peer.connection and peer.connection.handshake.get("rev", 0) < 3560:
-                continue  # Not supported
-
-            res = peer.request("getTrackers")
-            if not res or "error" in res:
-                continue
-
-            num_success += 1
-            for tracker_address in res["trackers"]:
-                if type(tracker_address) is bytes:  # Backward compatibilitys
-                    tracker_address = tracker_address.decode("utf8")
-                added = self.onTrackerFound(tracker_address)
-                if added:  # Only add one tracker from one source
-                    break
-
-        if not num_success and len(peers) < 20:
-            self.time_discover = 0.0
-
-        if num_success:
-            self.save()
-
-        self.log.debug("Trackers discovered from %s/%s peers in %.3fs" % (num_success, len(peers), time.time() - s))
-
-
-if "tracker_storage" not in locals():
-    tracker_storage = TrackerStorage()
-
-
-@PluginManager.registerTo("SiteAnnouncer")
-class SiteAnnouncerPlugin(object):
-    def getTrackers(self):
-        if tracker_storage.time_discover < time.time() - 5 * 60:
-            tracker_storage.time_discover = time.time()
-            gevent.spawn(tracker_storage.discoverTrackers, self.site.getConnectedPeers())
-        trackers = super(SiteAnnouncerPlugin, self).getTrackers()
-        shared_trackers = list(tracker_storage.getTrackers("shared").keys())
-        if shared_trackers:
-            return trackers + shared_trackers
-        else:
-            return trackers
-
-    def announceTracker(self, tracker, *args, **kwargs):
-        res = super(SiteAnnouncerPlugin, self).announceTracker(tracker, *args, **kwargs)
-        if res:
-            latency = res
-            tracker_storage.onTrackerSuccess(tracker, latency)
-        elif res is False:
-            tracker_storage.onTrackerError(tracker)
-
-        return res
-
-
-@PluginManager.registerTo("FileRequest")
-class FileRequestPlugin(object):
-    def actionGetTrackers(self, params):
-        shared_trackers = list(tracker_storage.getWorkingTrackers("shared").keys())
-        self.response({"trackers": shared_trackers})
-
-
-@PluginManager.registerTo("FileServer")
-class FileServerPlugin(object):
-    def portCheck(self, *args, **kwargs):
-        res = super(FileServerPlugin, self).portCheck(*args, **kwargs)
-        if res and not config.tor == "always" and "Bootstrapper" in PluginManager.plugin_manager.plugin_names:
-            for ip in self.ip_external_list:
-                my_tracker_address = "zero://%s:%s" % (ip, config.fileserver_port)
-                tracker_storage.onTrackerFound(my_tracker_address, my=True)
-        return res
-
-
-@PluginManager.registerTo("ConfigPlugin")
-class ConfigPlugin(object):
-    def createArguments(self):
-        group = self.parser.add_argument_group("AnnounceShare plugin")
-        group.add_argument('--working_shared_trackers_limit', help='Stop discovering new shared trackers after this number of shared trackers reached', default=5, type=int, metavar='limit')
-
-        return super(ConfigPlugin, self).createArguments()
diff --git a/plugins/AnnounceShare/Test/TestAnnounceShare.py b/plugins/AnnounceShare/Test/TestAnnounceShare.py
deleted file mode 100644
index 7178eac8..00000000
--- a/plugins/AnnounceShare/Test/TestAnnounceShare.py
+++ /dev/null
@@ -1,24 +0,0 @@
-import pytest
-
-from AnnounceShare import AnnounceSharePlugin
-from Peer import Peer
-from Config import config
-
-
-@pytest.mark.usefixtures("resetSettings")
-@pytest.mark.usefixtures("resetTempSettings")
-class TestAnnounceShare:
-    def testAnnounceList(self, file_server):
-        open("%s/trackers.json" % config.data_dir, "w").write("{}")
-        tracker_storage = AnnounceSharePlugin.tracker_storage
-        tracker_storage.load()
-        peer = Peer(file_server.ip, 1544, connection_server=file_server)
-        assert peer.request("getTrackers")["trackers"] == []
-
-        tracker_storage.onTrackerFound("zero://%s:15441" % file_server.ip)
-        assert peer.request("getTrackers")["trackers"] == []
-
-        # It needs to have at least one successfull announce to be shared to other peers
-        tracker_storage.onTrackerSuccess("zero://%s:15441" % file_server.ip, 1.0)
-        assert peer.request("getTrackers")["trackers"] == ["zero://%s:15441" % file_server.ip]
-
diff --git a/plugins/AnnounceShare/Test/conftest.py b/plugins/AnnounceShare/Test/conftest.py
deleted file mode 100644
index 5abd4dd6..00000000
--- a/plugins/AnnounceShare/Test/conftest.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from src.Test.conftest import *
-
-from Config import config
diff --git a/plugins/AnnounceShare/Test/pytest.ini b/plugins/AnnounceShare/Test/pytest.ini
deleted file mode 100644
index d09210d1..00000000
--- a/plugins/AnnounceShare/Test/pytest.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[pytest]
-python_files = Test*.py
-addopts = -rsxX -v --durations=6
-markers =
-    webtest: mark a test as a webtest.
\ No newline at end of file
diff --git a/plugins/AnnounceShare/__init__.py b/plugins/AnnounceShare/__init__.py
deleted file mode 100644
index dc1e40bd..00000000
--- a/plugins/AnnounceShare/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import AnnounceSharePlugin
diff --git a/plugins/AnnounceShare/plugin_info.json b/plugins/AnnounceShare/plugin_info.json
deleted file mode 100644
index 0ad07e71..00000000
--- a/plugins/AnnounceShare/plugin_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"name": "AnnounceShare",
-	"description": "Share possible trackers between clients.",
-	"default": "enabled"
-}
\ No newline at end of file
diff --git a/plugins/AnnounceZero/AnnounceZeroPlugin.py b/plugins/AnnounceZero/AnnounceZeroPlugin.py
deleted file mode 100644
index 42196831..00000000
--- a/plugins/AnnounceZero/AnnounceZeroPlugin.py
+++ /dev/null
@@ -1,141 +0,0 @@
-import time
-import itertools
-
-from Plugin import PluginManager
-from util import helper
-from Crypt import CryptEd25519
-from Crypt import CryptRsa
-
-allow_reload = False  # No source reload supported in this plugin
-time_full_announced = {}  # Tracker address: Last announced all site to tracker
-connection_pool = {}  # Tracker address: Peer object
-
-
-# We can only import plugin host clases after the plugins are loaded
-@PluginManager.afterLoad
-def importHostClasses():
-    global Peer, AnnounceError
-    from Peer import Peer
-    from Site.SiteAnnouncer import AnnounceError
-
-
-# Process result got back from tracker
-def processPeerRes(tracker_address, site, peers):
-    added = 0
-
-    # Onion
-    found_onion = 0
-    for packed_address in peers["onion"]:
-        found_onion += 1
-        peer_onion, peer_port = helper.unpackOnionAddress(packed_address)
-        if site.addPeer(peer_onion, peer_port, source="tracker"):
-            added += 1
-
-    # Ip4
-    found_ipv4 = 0
-    peers_normal = itertools.chain(peers.get("ip4", []), peers.get("ipv4", []), peers.get("ipv6", []))
-    for packed_address in peers_normal:
-        found_ipv4 += 1
-        peer_ip, peer_port = helper.unpackAddress(packed_address)
-        if site.addPeer(peer_ip, peer_port, source="tracker"):
-            added += 1
-
-    if added:
-        site.worker_manager.onPeers()
-        site.updateWebsocket(peers_added=added)
-    return added
-
-
-@PluginManager.registerTo("SiteAnnouncer")
-class SiteAnnouncerPlugin(object):
-    def getTrackerHandler(self, protocol):
-        if protocol == "zero":
-            return self.announceTrackerZero
-        else:
-            return super(SiteAnnouncerPlugin, self).getTrackerHandler(protocol)
-
-    def announceTrackerZero(self, tracker_address, mode="start", num_want=10):
-        global time_full_announced
-        s = time.time()
-
-        need_types = ["ip4"]   # ip4 for backward compatibility reasons
-        need_types += self.site.connection_server.supported_ip_types
-        if self.site.connection_server.tor_manager.enabled:
-            need_types.append("onion")
-
-        if mode == "start" or mode == "more":  # Single: Announce only this site
-            sites = [self.site]
-            full_announce = False
-        else:  # Multi: Announce all currently serving site
-            full_announce = True
-            if time.time() - time_full_announced.get(tracker_address, 0) < 60 * 15:  # No reannounce all sites within short time
-                return None
-            time_full_announced[tracker_address] = time.time()
-            from Site import SiteManager
-            sites = [site for site in SiteManager.site_manager.sites.values() if site.isServing()]
-
-        # Create request
-        add_types = self.getOpenedServiceTypes()
-        request = {
-            "hashes": [], "onions": [], "port": self.fileserver_port, "need_types": need_types, "need_num": 20, "add": add_types
-        }
-        for site in sites:
-            if "onion" in add_types:
-                onion = self.site.connection_server.tor_manager.getOnion(site.address)
-                request["onions"].append(onion)
-            request["hashes"].append(site.address_hash)
-
-        # Tracker can remove sites that we don't announce
-        if full_announce:
-            request["delete"] = True
-
-        # Sent request to tracker
-        tracker_peer = connection_pool.get(tracker_address)  # Re-use tracker connection if possible
-        if not tracker_peer:
-            tracker_ip, tracker_port = tracker_address.rsplit(":", 1)
-            tracker_peer = Peer(str(tracker_ip), int(tracker_port), connection_server=self.site.connection_server)
-            tracker_peer.is_tracker_connection = True
-            connection_pool[tracker_address] = tracker_peer
-
-        res = tracker_peer.request("announce", request)
-
-        if not res or "peers" not in res:
-            if full_announce:
-                time_full_announced[tracker_address] = 0
-            raise AnnounceError("Invalid response: %s" % res)
-
-        # Add peers from response to site
-        site_index = 0
-        peers_added = 0
-        for site_res in res["peers"]:
-            site = sites[site_index]
-            peers_added += processPeerRes(tracker_address, site, site_res)
-            site_index += 1
-
-        # Check if we need to sign prove the onion addresses
-        if "onion_sign_this" in res:
-            self.site.log.debug("Signing %s for %s to add %s onions" % (res["onion_sign_this"], tracker_address, len(sites)))
-            request["onion_signs"] = {}
-            request["onion_sign_this"] = res["onion_sign_this"]
-            request["need_num"] = 0
-            for site in sites:
-                onion = self.site.connection_server.tor_manager.getOnion(site.address)
-                publickey = self.site.connection_server.tor_manager.getPublickey(onion)
-                if publickey not in request["onion_signs"]:
-                    sign = CryptRsa.sign(res["onion_sign_this"].encode("utf8"), self.site.connection_server.tor_manager.getPrivatekey(onion))
-                    request["onion_signs"][publickey] = sign
-            res = tracker_peer.request("announce", request)
-            if not res or "onion_sign_this" in res:
-                if full_announce:
-                    time_full_announced[tracker_address] = 0
-                raise AnnounceError("Announce onion address to failed: %s" % res)
-
-        if full_announce:
-            tracker_peer.remove()  # Close connection, we don't need it in next 5 minute
-
-        self.site.log.debug(
-            "Tracker announce result: zero://%s (sites: %s, new peers: %s, add: %s, mode: %s) in %.3fs" %
-            (tracker_address, site_index, peers_added, add_types, mode, time.time() - s)
-        )
-
-        return True
diff --git a/plugins/AnnounceZero/__init__.py b/plugins/AnnounceZero/__init__.py
deleted file mode 100644
index 8aec5ddb..00000000
--- a/plugins/AnnounceZero/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import AnnounceZeroPlugin
\ No newline at end of file
diff --git a/plugins/AnnounceZero/plugin_info.json b/plugins/AnnounceZero/plugin_info.json
deleted file mode 100644
index 50e7cf7f..00000000
--- a/plugins/AnnounceZero/plugin_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"name": "AnnounceZero",
-	"description": "Announce using ZeroNet protocol.",
-	"default": "enabled"
-}
\ No newline at end of file
diff --git a/plugins/Benchmark/BenchmarkDb.py b/plugins/Benchmark/BenchmarkDb.py
deleted file mode 100644
index a767a3f4..00000000
--- a/plugins/Benchmark/BenchmarkDb.py
+++ /dev/null
@@ -1,143 +0,0 @@
-import os
-import json
-import contextlib
-import time
-
-from Plugin import PluginManager
-from Config import config
-
-
-@PluginManager.registerTo("Actions")
-class ActionsPlugin:
-    def getBenchmarkTests(self, online=False):
-        tests = super().getBenchmarkTests(online)
-        tests.extend([
-            {"func": self.testDbConnect, "num": 10, "time_standard": 0.27},
-            {"func": self.testDbInsert, "num": 10, "time_standard": 0.91},
-            {"func": self.testDbInsertMultiuser, "num": 1, "time_standard": 0.57},
-            {"func": self.testDbQueryIndexed, "num": 1000, "time_standard": 0.84},
-            {"func": self.testDbQueryNotIndexed, "num": 1000, "time_standard": 1.30}
-        ])
-        return tests
-
-
-    @contextlib.contextmanager
-    def getTestDb(self):
-        from Db import Db
-        path = "%s/benchmark.db" % config.data_dir
-        if os.path.isfile(path):
-            os.unlink(path)
-        schema = {
-            "db_name": "TestDb",
-            "db_file": path,
-            "maps": {
-                ".*": {
-                    "to_table": {
-                        "test": "test"
-                    }
-                }
-            },
-            "tables": {
-                "test": {
-                    "cols": [
-                        ["test_id", "INTEGER"],
-                        ["title", "TEXT"],
-                        ["json_id", "INTEGER REFERENCES json (json_id)"]
-                    ],
-                    "indexes": ["CREATE UNIQUE INDEX test_key ON test(test_id, json_id)"],
-                    "schema_changed": 1426195822
-                }
-            }
-        }
-
-        db = Db.Db(schema, path)
-
-        yield db
-
-        db.close()
-        if os.path.isfile(path):
-            os.unlink(path)
-
-    def testDbConnect(self, num_run=1):
-        import sqlite3
-        for i in range(num_run):
-            with self.getTestDb() as db:
-                db.checkTables()
-            yield "."
-        yield "(SQLite version: %s, API: %s)" % (sqlite3.sqlite_version, sqlite3.version)
-
-    def testDbInsert(self, num_run=1):
-        yield "x 1000 lines "
-        for u in range(num_run):
-            with self.getTestDb() as db:
-                db.checkTables()
-                data = {"test": []}
-                for i in range(1000):  # 1000 line of data
-                    data["test"].append({"test_id": i, "title": "Testdata for %s message %s" % (u, i)})
-                json.dump(data, open("%s/test_%s.json" % (config.data_dir, u), "w"))
-                db.updateJson("%s/test_%s.json" % (config.data_dir, u))
-                os.unlink("%s/test_%s.json" % (config.data_dir, u))
-                assert db.execute("SELECT COUNT(*) FROM test").fetchone()[0] == 1000
-            yield "."
-
-    def fillTestDb(self, db):
-        db.checkTables()
-        cur = db.getCursor()
-        cur.logging = False
-        for u in range(100, 200):  # 100 user
-            data = {"test": []}
-            for i in range(100):  # 1000 line of data
-                data["test"].append({"test_id": i, "title": "Testdata for %s message %s" % (u, i)})
-            json.dump(data, open("%s/test_%s.json" % (config.data_dir, u), "w"))
-            db.updateJson("%s/test_%s.json" % (config.data_dir, u), cur=cur)
-            os.unlink("%s/test_%s.json" % (config.data_dir, u))
-            if u % 10 == 0:
-                yield "."
-
-    def testDbInsertMultiuser(self, num_run=1):
-        yield "x 100 users x 100 lines "
-        for u in range(num_run):
-            with self.getTestDb() as db:
-                for progress in self.fillTestDb(db):
-                    yield progress
-                num_rows = db.execute("SELECT COUNT(*) FROM test").fetchone()[0]
-                assert num_rows == 10000, "%s != 10000" % num_rows
-
-    def testDbQueryIndexed(self, num_run=1):
-        s = time.time()
-        with self.getTestDb() as db:
-            for progress in self.fillTestDb(db):
-                pass
-            yield " (Db warmup done in %.3fs) " % (time.time() - s)
-            found_total = 0
-            for i in range(num_run):  # 1000x by test_id
-                found = 0
-                res = db.execute("SELECT * FROM test WHERE test_id = %s" % (i % 100))
-                for row in res:
-                    found_total += 1
-                    found += 1
-                del(res)
-                yield "."
-                assert found == 100, "%s != 100 (i: %s)" % (found, i)
-            yield "Found: %s" % found_total
-
-    def testDbQueryNotIndexed(self, num_run=1):
-        s = time.time()
-        with self.getTestDb() as db:
-            for progress in self.fillTestDb(db):
-                pass
-            yield " (Db warmup done in %.3fs) " % (time.time() - s)
-            found_total = 0
-            for i in range(num_run):  # 1000x by test_id
-                found = 0
-                res = db.execute("SELECT * FROM test WHERE json_id = %s" % i)
-                for row in res:
-                    found_total += 1
-                    found += 1
-                yield "."
-                del(res)
-                if i == 0 or i > 100:
-                    assert found == 0, "%s != 0 (i: %s)" % (found, i)
-                else:
-                    assert found == 100, "%s != 100 (i: %s)" % (found, i)
-            yield "Found: %s" % found_total
diff --git a/plugins/Benchmark/BenchmarkPack.py b/plugins/Benchmark/BenchmarkPack.py
deleted file mode 100644
index 6b92e43a..00000000
--- a/plugins/Benchmark/BenchmarkPack.py
+++ /dev/null
@@ -1,183 +0,0 @@
-import os
-import io
-from collections import OrderedDict
-
-from Plugin import PluginManager
-from Config import config
-from util import Msgpack
-
-
-@PluginManager.registerTo("Actions")
-class ActionsPlugin:
-    def createZipFile(self, path):
-        import zipfile
-        test_data = b"Test" * 1024
-        file_name = b"\xc3\x81rv\xc3\xadzt\xc5\xb1r\xc5\x91%s.txt".decode("utf8")
-        with zipfile.ZipFile(path, 'w') as archive:
-            for y in range(100):
-                zip_info = zipfile.ZipInfo(file_name % y, (1980, 1, 1, 0, 0, 0))
-                zip_info.compress_type = zipfile.ZIP_DEFLATED
-                zip_info.create_system = 3
-                zip_info.flag_bits = 0
-                zip_info.external_attr = 25165824
-                archive.writestr(zip_info, test_data)
-
-    def testPackZip(self, num_run=1):
-        """
-        Test zip file creating
-        """
-        yield "x 100 x 5KB "
-        from Crypt import CryptHash
-        zip_path = '%s/test.zip' % config.data_dir
-        for i in range(num_run):
-            self.createZipFile(zip_path)
-            yield "."
-
-        archive_size = os.path.getsize(zip_path) / 1024
-        yield "(Generated file size: %.2fkB)" % archive_size
-
-        hash = CryptHash.sha512sum(open(zip_path, "rb"))
-        valid = "cb32fb43783a1c06a2170a6bc5bb228a032b67ff7a1fd7a5efb9b467b400f553"
-        assert hash == valid, "Invalid hash: %s != %s<br>" % (hash, valid)
-        os.unlink(zip_path)
-
-    def testUnpackZip(self, num_run=1):
-        """
-        Test zip file reading
-        """
-        yield "x 100 x 5KB "
-        import zipfile
-        zip_path = '%s/test.zip' % config.data_dir
-        test_data = b"Test" * 1024
-        file_name = b"\xc3\x81rv\xc3\xadzt\xc5\xb1r\xc5\x91".decode("utf8")
-
-        self.createZipFile(zip_path)
-        for i in range(num_run):
-            with zipfile.ZipFile(zip_path) as archive:
-                for f in archive.filelist:
-                    assert f.filename.startswith(file_name), "Invalid filename: %s != %s" % (f.filename, file_name)
-                    data = archive.open(f.filename).read()
-                    assert archive.open(f.filename).read() == test_data, "Invalid data: %s..." % data[0:30]
-            yield "."
-
-        os.unlink(zip_path)
-
-    def createArchiveFile(self, path, archive_type="gz"):
-        import tarfile
-        import gzip
-
-        # Monkey patch _init_write_gz to use fixed date in order to keep the hash independent from datetime
-        def nodate_write_gzip_header(self):
-            self._write_mtime = 0
-            original_write_gzip_header(self)
-
-        test_data_io = io.BytesIO(b"Test" * 1024)
-        file_name = b"\xc3\x81rv\xc3\xadzt\xc5\xb1r\xc5\x91%s.txt".decode("utf8")
-
-        original_write_gzip_header = gzip.GzipFile._write_gzip_header
-        gzip.GzipFile._write_gzip_header = nodate_write_gzip_header
-        with tarfile.open(path, 'w:%s' % archive_type) as archive:
-            for y in range(100):
-                test_data_io.seek(0)
-                tar_info = tarfile.TarInfo(file_name % y)
-                tar_info.size = 4 * 1024
-                archive.addfile(tar_info, test_data_io)
-
-    def testPackArchive(self, num_run=1, archive_type="gz"):
-        """
-        Test creating tar archive files
-        """
-        yield "x 100 x 5KB "
-        from Crypt import CryptHash
-
-        hash_valid_db = {
-            "gz": "92caec5121a31709cbbc8c11b0939758e670b055bbbe84f9beb3e781dfde710f",
-            "bz2": "b613f41e6ee947c8b9b589d3e8fa66f3e28f63be23f4faf015e2f01b5c0b032d",
-            "xz": "ae43892581d770959c8d993daffab25fd74490b7cf9fafc7aaee746f69895bcb",
-        }
-        archive_path = '%s/test.tar.%s' % (config.data_dir, archive_type)
-        for i in range(num_run):
-            self.createArchiveFile(archive_path, archive_type=archive_type)
-            yield "."
-
-        archive_size = os.path.getsize(archive_path) / 1024
-        yield "(Generated file size: %.2fkB)" % archive_size
-
-        hash = CryptHash.sha512sum(open("%s/test.tar.%s" % (config.data_dir, archive_type), "rb"))
-        valid = hash_valid_db[archive_type]
-        assert hash == valid, "Invalid hash: %s != %s<br>" % (hash, valid)
-
-        if os.path.isfile(archive_path):
-            os.unlink(archive_path)
-
-    def testUnpackArchive(self, num_run=1, archive_type="gz"):
-        """
-        Test reading tar archive files
-        """
-        yield "x 100 x 5KB "
-        import tarfile
-
-        test_data = b"Test" * 1024
-        file_name = b"\xc3\x81rv\xc3\xadzt\xc5\xb1r\xc5\x91%s.txt".decode("utf8")
-        archive_path = '%s/test.tar.%s' % (config.data_dir, archive_type)
-        self.createArchiveFile(archive_path, archive_type=archive_type)
-        for i in range(num_run):
-            with tarfile.open(archive_path, 'r:%s' % archive_type) as archive:
-                for y in range(100):
-                    assert archive.extractfile(file_name % y).read() == test_data
-            yield "."
-        if os.path.isfile(archive_path):
-            os.unlink(archive_path)
-
-    def testPackMsgpack(self, num_run=1):
-        """
-        Test msgpack encoding
-        """
-        yield "x 100 x 5KB "
-        binary = b'fqv\xf0\x1a"e\x10,\xbe\x9cT\x9e(\xa5]u\x072C\x8c\x15\xa2\xa8\x93Sw)\x19\x02\xdd\t\xfb\xf67\x88\xd9\xee\x86\xa1\xe4\xb6,\xc6\x14\xbb\xd7$z\x1d\xb2\xda\x85\xf5\xa0\x97^\x01*\xaf\xd3\xb0!\xb7\x9d\xea\x89\xbbh8\xa1"\xa7]e(@\xa2\xa5g\xb7[\xae\x8eE\xc2\x9fL\xb6s\x19\x19\r\xc8\x04S\xd0N\xe4]?/\x01\xea\xf6\xec\xd1\xb3\xc2\x91\x86\xd7\xf4K\xdf\xc2lV\xf4\xe8\x80\xfc\x8ep\xbb\x82\xb3\x86\x98F\x1c\xecS\xc8\x15\xcf\xdc\xf1\xed\xfc\xd8\x18r\xf9\x80\x0f\xfa\x8cO\x97(\x0b]\xf1\xdd\r\xe7\xbf\xed\x06\xbd\x1b?\xc5\xa0\xd7a\x82\xf3\xa8\xe6@\xf3\ri\xa1\xb10\xf6\xd4W\xbc\x86\x1a\xbb\xfd\x94!bS\xdb\xaeM\x92\x00#\x0b\xf7\xad\xe9\xc2\x8e\x86\xbfi![%\xd31]\xc6\xfc2\xc9\xda\xc6v\x82P\xcc\xa9\xea\xb9\xff\xf6\xc8\x17iD\xcf\xf3\xeeI\x04\xe9\xa1\x19\xbb\x01\x92\xf5nn4K\xf8\xbb\xc6\x17e>\xa7 \xbbv'
-        data = OrderedDict(
-            sorted({"int": 1024 * 1024 * 1024, "float": 12345.67890, "text": "hello" * 1024, "binary": binary}.items())
-        )
-        data_packed_valid = b'\x84\xa6binary\xc5\x01\x00fqv\xf0\x1a"e\x10,\xbe\x9cT\x9e(\xa5]u\x072C\x8c\x15\xa2\xa8\x93Sw)\x19\x02\xdd\t\xfb\xf67\x88\xd9\xee\x86\xa1\xe4\xb6,\xc6\x14\xbb\xd7$z\x1d\xb2\xda\x85\xf5\xa0\x97^\x01*\xaf\xd3\xb0!\xb7\x9d\xea\x89\xbbh8\xa1"\xa7]e(@\xa2\xa5g\xb7[\xae\x8eE\xc2\x9fL\xb6s\x19\x19\r\xc8\x04S\xd0N\xe4]?/\x01\xea\xf6\xec\xd1\xb3\xc2\x91\x86\xd7\xf4K\xdf\xc2lV\xf4\xe8\x80\xfc\x8ep\xbb\x82\xb3\x86\x98F\x1c\xecS\xc8\x15\xcf\xdc\xf1\xed\xfc\xd8\x18r\xf9\x80\x0f\xfa\x8cO\x97(\x0b]\xf1\xdd\r\xe7\xbf\xed\x06\xbd\x1b?\xc5\xa0\xd7a\x82\xf3\xa8\xe6@\xf3\ri\xa1\xb10\xf6\xd4W\xbc\x86\x1a\xbb\xfd\x94!bS\xdb\xaeM\x92\x00#\x0b\xf7\xad\xe9\xc2\x8e\x86\xbfi![%\xd31]\xc6\xfc2\xc9\xda\xc6v\x82P\xcc\xa9\xea\xb9\xff\xf6\xc8\x17iD\xcf\xf3\xeeI\x04\xe9\xa1\x19\xbb\x01\x92\xf5nn4K\xf8\xbb\xc6\x17e>\xa7 \xbbv\xa5float\xcb@\xc8\x1c\xd6\xe61\xf8\xa1\xa3int\xce@\x00\x00\x00\xa4text\xda\x14\x00'
-        data_packed_valid += b'hello' * 1024
-        for y in range(num_run):
-            for i in range(100):
-                data_packed = Msgpack.pack(data)
-            yield "."
-        assert data_packed == data_packed_valid, "%s<br>!=<br>%s" % (repr(data_packed), repr(data_packed_valid))
-
-    def testUnpackMsgpack(self, num_run=1):
-        """
-        Test msgpack decoding
-        """
-        yield "x 5KB "
-        binary = b'fqv\xf0\x1a"e\x10,\xbe\x9cT\x9e(\xa5]u\x072C\x8c\x15\xa2\xa8\x93Sw)\x19\x02\xdd\t\xfb\xf67\x88\xd9\xee\x86\xa1\xe4\xb6,\xc6\x14\xbb\xd7$z\x1d\xb2\xda\x85\xf5\xa0\x97^\x01*\xaf\xd3\xb0!\xb7\x9d\xea\x89\xbbh8\xa1"\xa7]e(@\xa2\xa5g\xb7[\xae\x8eE\xc2\x9fL\xb6s\x19\x19\r\xc8\x04S\xd0N\xe4]?/\x01\xea\xf6\xec\xd1\xb3\xc2\x91\x86\xd7\xf4K\xdf\xc2lV\xf4\xe8\x80\xfc\x8ep\xbb\x82\xb3\x86\x98F\x1c\xecS\xc8\x15\xcf\xdc\xf1\xed\xfc\xd8\x18r\xf9\x80\x0f\xfa\x8cO\x97(\x0b]\xf1\xdd\r\xe7\xbf\xed\x06\xbd\x1b?\xc5\xa0\xd7a\x82\xf3\xa8\xe6@\xf3\ri\xa1\xb10\xf6\xd4W\xbc\x86\x1a\xbb\xfd\x94!bS\xdb\xaeM\x92\x00#\x0b\xf7\xad\xe9\xc2\x8e\x86\xbfi![%\xd31]\xc6\xfc2\xc9\xda\xc6v\x82P\xcc\xa9\xea\xb9\xff\xf6\xc8\x17iD\xcf\xf3\xeeI\x04\xe9\xa1\x19\xbb\x01\x92\xf5nn4K\xf8\xbb\xc6\x17e>\xa7 \xbbv'
-        data = OrderedDict(
-            sorted({"int": 1024 * 1024 * 1024, "float": 12345.67890, "text": "hello" * 1024, "binary": binary}.items())
-        )
-        data_packed = b'\x84\xa6binary\xc5\x01\x00fqv\xf0\x1a"e\x10,\xbe\x9cT\x9e(\xa5]u\x072C\x8c\x15\xa2\xa8\x93Sw)\x19\x02\xdd\t\xfb\xf67\x88\xd9\xee\x86\xa1\xe4\xb6,\xc6\x14\xbb\xd7$z\x1d\xb2\xda\x85\xf5\xa0\x97^\x01*\xaf\xd3\xb0!\xb7\x9d\xea\x89\xbbh8\xa1"\xa7]e(@\xa2\xa5g\xb7[\xae\x8eE\xc2\x9fL\xb6s\x19\x19\r\xc8\x04S\xd0N\xe4]?/\x01\xea\xf6\xec\xd1\xb3\xc2\x91\x86\xd7\xf4K\xdf\xc2lV\xf4\xe8\x80\xfc\x8ep\xbb\x82\xb3\x86\x98F\x1c\xecS\xc8\x15\xcf\xdc\xf1\xed\xfc\xd8\x18r\xf9\x80\x0f\xfa\x8cO\x97(\x0b]\xf1\xdd\r\xe7\xbf\xed\x06\xbd\x1b?\xc5\xa0\xd7a\x82\xf3\xa8\xe6@\xf3\ri\xa1\xb10\xf6\xd4W\xbc\x86\x1a\xbb\xfd\x94!bS\xdb\xaeM\x92\x00#\x0b\xf7\xad\xe9\xc2\x8e\x86\xbfi![%\xd31]\xc6\xfc2\xc9\xda\xc6v\x82P\xcc\xa9\xea\xb9\xff\xf6\xc8\x17iD\xcf\xf3\xeeI\x04\xe9\xa1\x19\xbb\x01\x92\xf5nn4K\xf8\xbb\xc6\x17e>\xa7 \xbbv\xa5float\xcb@\xc8\x1c\xd6\xe61\xf8\xa1\xa3int\xce@\x00\x00\x00\xa4text\xda\x14\x00'
-        data_packed += b'hello' * 1024
-        for y in range(num_run):
-            data_unpacked = Msgpack.unpack(data_packed, decode=False)
-            yield "."
-        assert data_unpacked == data, "%s<br>!=<br>%s" % (data_unpacked, data)
-
-    def testUnpackMsgpackStreaming(self, num_run=1, fallback=False):
-        """
-        Test streaming msgpack decoding
-        """
-        yield "x 1000 x 5KB "
-        binary = b'fqv\xf0\x1a"e\x10,\xbe\x9cT\x9e(\xa5]u\x072C\x8c\x15\xa2\xa8\x93Sw)\x19\x02\xdd\t\xfb\xf67\x88\xd9\xee\x86\xa1\xe4\xb6,\xc6\x14\xbb\xd7$z\x1d\xb2\xda\x85\xf5\xa0\x97^\x01*\xaf\xd3\xb0!\xb7\x9d\xea\x89\xbbh8\xa1"\xa7]e(@\xa2\xa5g\xb7[\xae\x8eE\xc2\x9fL\xb6s\x19\x19\r\xc8\x04S\xd0N\xe4]?/\x01\xea\xf6\xec\xd1\xb3\xc2\x91\x86\xd7\xf4K\xdf\xc2lV\xf4\xe8\x80\xfc\x8ep\xbb\x82\xb3\x86\x98F\x1c\xecS\xc8\x15\xcf\xdc\xf1\xed\xfc\xd8\x18r\xf9\x80\x0f\xfa\x8cO\x97(\x0b]\xf1\xdd\r\xe7\xbf\xed\x06\xbd\x1b?\xc5\xa0\xd7a\x82\xf3\xa8\xe6@\xf3\ri\xa1\xb10\xf6\xd4W\xbc\x86\x1a\xbb\xfd\x94!bS\xdb\xaeM\x92\x00#\x0b\xf7\xad\xe9\xc2\x8e\x86\xbfi![%\xd31]\xc6\xfc2\xc9\xda\xc6v\x82P\xcc\xa9\xea\xb9\xff\xf6\xc8\x17iD\xcf\xf3\xeeI\x04\xe9\xa1\x19\xbb\x01\x92\xf5nn4K\xf8\xbb\xc6\x17e>\xa7 \xbbv'
-        data = OrderedDict(
-            sorted({"int": 1024 * 1024 * 1024, "float": 12345.67890, "text": "hello" * 1024, "binary": binary}.items())
-        )
-        data_packed = b'\x84\xa6binary\xc5\x01\x00fqv\xf0\x1a"e\x10,\xbe\x9cT\x9e(\xa5]u\x072C\x8c\x15\xa2\xa8\x93Sw)\x19\x02\xdd\t\xfb\xf67\x88\xd9\xee\x86\xa1\xe4\xb6,\xc6\x14\xbb\xd7$z\x1d\xb2\xda\x85\xf5\xa0\x97^\x01*\xaf\xd3\xb0!\xb7\x9d\xea\x89\xbbh8\xa1"\xa7]e(@\xa2\xa5g\xb7[\xae\x8eE\xc2\x9fL\xb6s\x19\x19\r\xc8\x04S\xd0N\xe4]?/\x01\xea\xf6\xec\xd1\xb3\xc2\x91\x86\xd7\xf4K\xdf\xc2lV\xf4\xe8\x80\xfc\x8ep\xbb\x82\xb3\x86\x98F\x1c\xecS\xc8\x15\xcf\xdc\xf1\xed\xfc\xd8\x18r\xf9\x80\x0f\xfa\x8cO\x97(\x0b]\xf1\xdd\r\xe7\xbf\xed\x06\xbd\x1b?\xc5\xa0\xd7a\x82\xf3\xa8\xe6@\xf3\ri\xa1\xb10\xf6\xd4W\xbc\x86\x1a\xbb\xfd\x94!bS\xdb\xaeM\x92\x00#\x0b\xf7\xad\xe9\xc2\x8e\x86\xbfi![%\xd31]\xc6\xfc2\xc9\xda\xc6v\x82P\xcc\xa9\xea\xb9\xff\xf6\xc8\x17iD\xcf\xf3\xeeI\x04\xe9\xa1\x19\xbb\x01\x92\xf5nn4K\xf8\xbb\xc6\x17e>\xa7 \xbbv\xa5float\xcb@\xc8\x1c\xd6\xe61\xf8\xa1\xa3int\xce@\x00\x00\x00\xa4text\xda\x14\x00'
-        data_packed += b'hello' * 1024
-        for i in range(num_run):
-            unpacker = Msgpack.getUnpacker(decode=False, fallback=fallback)
-            for y in range(1000):
-                unpacker.feed(data_packed)
-                for data_unpacked in unpacker:
-                    pass
-            yield "."
-        assert data == data_unpacked, "%s != %s" % (data_unpacked, data)
diff --git a/plugins/Benchmark/BenchmarkPlugin.py b/plugins/Benchmark/BenchmarkPlugin.py
deleted file mode 100644
index fd6cacf3..00000000
--- a/plugins/Benchmark/BenchmarkPlugin.py
+++ /dev/null
@@ -1,428 +0,0 @@
-import os
-import time
-import io
-import math
-import hashlib
-import re
-import sys
-
-from Config import config
-from Crypt import CryptHash
-from Plugin import PluginManager
-from Debug import Debug
-from util import helper
-
-plugin_dir = os.path.dirname(__file__)
-
-benchmark_key = None
-
-
-@PluginManager.registerTo("UiRequest")
-class UiRequestPlugin(object):
-    @helper.encodeResponse
-    def actionBenchmark(self):
-        global benchmark_key
-        script_nonce = self.getScriptNonce()
-        if not benchmark_key:
-            benchmark_key = CryptHash.random(encoding="base64")
-        self.sendHeader(script_nonce=script_nonce)
-
-        if "Multiuser" in PluginManager.plugin_manager.plugin_names and not config.multiuser_local:
-            yield "This function is disabled on this proxy"
-            return
-
-        data = self.render(
-            plugin_dir + "/media/benchmark.html",
-            script_nonce=script_nonce,
-            benchmark_key=benchmark_key,
-            filter=re.sub("[^A-Za-z0-9]", "", self.get.get("filter", ""))
-        )
-        yield data
-
-    @helper.encodeResponse
-    def actionBenchmarkResult(self):
-        global benchmark_key
-        if self.get.get("benchmark_key", "") != benchmark_key:
-            return self.error403("Invalid benchmark key")
-
-        self.sendHeader(content_type="text/plain", noscript=True)
-
-        if "Multiuser" in PluginManager.plugin_manager.plugin_names and not config.multiuser_local:
-            yield "This function is disabled on this proxy"
-            return
-
-        yield " " * 1024  # Head (required for streaming)
-
-        import main
-        s = time.time()
-
-        for part in main.actions.testBenchmark(filter=self.get.get("filter", "")):
-            yield part
-
-        yield "\n - Total time: %.3fs" % (time.time() - s)
-
-
-@PluginManager.registerTo("Actions")
-class ActionsPlugin:
-    def getMultiplerTitle(self, multipler):
-        if multipler < 0.3:
-            multipler_title = "Sloooow"
-        elif multipler < 0.6:
-            multipler_title = "Ehh"
-        elif multipler < 0.8:
-            multipler_title = "Goodish"
-        elif multipler < 1.2:
-            multipler_title = "OK"
-        elif multipler < 1.7:
-            multipler_title = "Fine"
-        elif multipler < 2.5:
-            multipler_title = "Fast"
-        elif multipler < 3.5:
-            multipler_title = "WOW"
-        else:
-            multipler_title = "Insane!!"
-        return multipler_title
-
-    def formatResult(self, taken, standard):
-        if not standard:
-            return " Done in %.3fs" % taken
-
-        if taken > 0:
-            multipler = standard / taken
-        else:
-            multipler = 99
-        multipler_title = self.getMultiplerTitle(multipler)
-
-        return " Done in %.3fs = %s (%.2fx)" % (taken, multipler_title, multipler)
-
-    def getBenchmarkTests(self, online=False):
-        if hasattr(super(), "getBenchmarkTests"):
-            tests = super().getBenchmarkTests(online)
-        else:
-            tests = []
-
-        tests.extend([
-            {"func": self.testHdPrivatekey, "num": 50, "time_standard": 0.57},
-            {"func": self.testSign, "num": 20, "time_standard": 0.46},
-            {"func": self.testVerify, "kwargs": {"lib_verify": "sslcrypto_fallback"}, "num": 20, "time_standard": 0.38},
-            {"func": self.testVerify, "kwargs": {"lib_verify": "sslcrypto"}, "num": 200, "time_standard": 0.30},
-            {"func": self.testVerify, "kwargs": {"lib_verify": "libsecp256k1"}, "num": 200, "time_standard": 0.10},
-
-            {"func": self.testPackMsgpack, "num": 100, "time_standard": 0.35},
-            {"func": self.testUnpackMsgpackStreaming, "kwargs": {"fallback": False}, "num": 100, "time_standard": 0.35},
-            {"func": self.testUnpackMsgpackStreaming, "kwargs": {"fallback": True}, "num": 10, "time_standard": 0.5},
-
-            {"func": self.testPackZip, "num": 5, "time_standard": 0.065},
-            {"func": self.testPackArchive, "kwargs": {"archive_type": "gz"}, "num": 5, "time_standard": 0.08},
-            {"func": self.testPackArchive, "kwargs": {"archive_type": "bz2"}, "num": 5, "time_standard": 0.68},
-            {"func": self.testPackArchive, "kwargs": {"archive_type": "xz"}, "num": 5, "time_standard": 0.47},
-            {"func": self.testUnpackZip, "num": 20, "time_standard": 0.25},
-            {"func": self.testUnpackArchive, "kwargs": {"archive_type": "gz"}, "num": 20, "time_standard": 0.28},
-            {"func": self.testUnpackArchive, "kwargs": {"archive_type": "bz2"}, "num": 20, "time_standard": 0.83},
-            {"func": self.testUnpackArchive, "kwargs": {"archive_type": "xz"}, "num": 20, "time_standard": 0.38},
-
-            {"func": self.testCryptHash, "kwargs": {"hash_type": "sha256"}, "num": 10, "time_standard": 0.50},
-            {"func": self.testCryptHash, "kwargs": {"hash_type": "sha512"}, "num": 10, "time_standard": 0.33},
-            {"func": self.testCryptHashlib, "kwargs": {"hash_type": "sha3_256"}, "num": 10, "time_standard": 0.33},
-            {"func": self.testCryptHashlib, "kwargs": {"hash_type": "sha3_512"}, "num": 10, "time_standard": 0.65},
-
-            {"func": self.testRandom, "num": 100, "time_standard": 0.08},
-        ])
-
-        if online:
-            tests += [
-                {"func": self.testHttps, "num": 1, "time_standard": 2.1}
-            ]
-        return tests
-
-    def testBenchmark(self, num_multipler=1, online=False, num_run=None, filter=None):
-        """
-        Run benchmark on client functions
-        """
-        tests = self.getBenchmarkTests(online=online)
-
-        if filter:
-            tests = [test for test in tests[:] if filter.lower() in test["func"].__name__.lower()]
-
-        yield "\n"
-        res = {}
-        res_time_taken = {}
-        multiplers = []
-        for test in tests:
-            s = time.time()
-            if num_run:
-                num_run_test = num_run
-            else:
-                num_run_test = math.ceil(test["num"] * num_multipler)
-            func = test["func"]
-            func_name = func.__name__
-            kwargs = test.get("kwargs", {})
-            key = "%s %s" % (func_name, kwargs)
-            if kwargs:
-                yield "* Running %s (%s) x %s " % (func_name, kwargs, num_run_test)
-            else:
-                yield "* Running %s x %s " % (func_name, num_run_test)
-            i = 0
-            try:
-                for progress in func(num_run_test, **kwargs):
-                    i += 1
-                    if num_run_test > 10:
-                        should_print = i % (num_run_test / 10) == 0 or progress != "."
-                    else:
-                        should_print = True
-
-                    if should_print:
-                        if num_run_test == 1 and progress == ".":
-                            progress = "..."
-                        yield progress
-                time_taken = time.time() - s
-                if num_run:
-                    time_standard = 0
-                else:
-                    time_standard = test["time_standard"] * num_multipler
-                yield self.formatResult(time_taken, time_standard)
-                yield "\n"
-                res[key] = "ok"
-                res_time_taken[key] = time_taken
-                multiplers.append(time_standard / max(time_taken, 0.001))
-            except Exception as err:
-                res[key] = err
-                yield "Failed!\n! Error: %s\n\n" % Debug.formatException(err)
-
-        yield "\n== Result ==\n"
-
-        # Check verification speed
-        if "testVerify {'lib_verify': 'sslcrypto'}" in res_time_taken:
-            speed_order = ["sslcrypto_fallback", "sslcrypto", "libsecp256k1"]
-            time_taken = {}
-            for lib_verify in speed_order:
-                time_taken[lib_verify] = res_time_taken["testVerify {'lib_verify': '%s'}" % lib_verify]
-
-            time_taken["sslcrypto_fallback"] *= 10  # fallback benchmark only run 20 times instead of 200
-            speedup_sslcrypto = time_taken["sslcrypto_fallback"] / time_taken["sslcrypto"]
-            speedup_libsecp256k1 = time_taken["sslcrypto_fallback"] / time_taken["libsecp256k1"]
-
-            yield "\n* Verification speedup:\n"
-            yield " - OpenSSL: %.1fx (reference: 7.0x)\n" % speedup_sslcrypto
-            yield " - libsecp256k1: %.1fx (reference: 23.8x)\n" % speedup_libsecp256k1
-
-            if speedup_sslcrypto < 2:
-                res["Verification speed"] = "error: OpenSSL speedup low: %.1fx" % speedup_sslcrypto
-
-            if speedup_libsecp256k1 < speedup_sslcrypto:
-                res["Verification speed"] = "error: libsecp256k1 speedup low: %.1fx" % speedup_libsecp256k1
-
-        if not res:
-            yield "! No tests found"
-            if config.action == "test":
-                sys.exit(1)
-        else:
-            num_failed = len([res_key for res_key, res_val in res.items() if res_val != "ok"])
-            num_success = len([res_key for res_key, res_val in res.items() if res_val == "ok"])
-            yield "\n* Tests:\n"
-            yield " - Total: %s tests\n" % len(res)
-            yield " - Success: %s tests\n" % num_success
-            yield " - Failed: %s tests\n" % num_failed
-            if any(multiplers):
-                multipler_avg = sum(multiplers) / len(multiplers)
-                multipler_title = self.getMultiplerTitle(multipler_avg)
-                yield " - Average speed factor: %.2fx (%s)\n" % (multipler_avg, multipler_title)
-
-            # Display errors
-            for res_key, res_val in res.items():
-                if res_val != "ok":
-                    yield " ! %s %s\n" % (res_key, res_val)
-
-            if num_failed != 0 and config.action == "test":
-                sys.exit(1)
-
-    def testHttps(self, num_run=1):
-        """
-        Test https connection with valid and invalid certs
-        """
-        import urllib.request
-        import urllib.error
-
-        body = urllib.request.urlopen("https://google.com").read()
-        assert len(body) > 100
-        yield "."
-
-        badssl_urls = [
-            "https://expired.badssl.com/",
-            "https://wrong.host.badssl.com/",
-            "https://self-signed.badssl.com/",
-            "https://untrusted-root.badssl.com/"
-        ]
-        for badssl_url in badssl_urls:
-            try:
-                body = urllib.request.urlopen(badssl_url).read()
-                https_err = None
-            except urllib.error.URLError as err:
-                https_err = err
-            assert https_err
-            yield "."
-
-    def testCryptHash(self, num_run=1, hash_type="sha256"):
-        """
-        Test hashing functions
-        """
-        yield "(5MB) "
-
-        from Crypt import CryptHash
-
-        hash_types = {
-            "sha256": {"func": CryptHash.sha256sum, "hash_valid": "8cd629d9d6aff6590da8b80782a5046d2673d5917b99d5603c3dcb4005c45ffa"},
-            "sha512": {"func": CryptHash.sha512sum, "hash_valid": "9ca7e855d430964d5b55b114e95c6bbb114a6d478f6485df93044d87b108904d"}
-        }
-        hash_func = hash_types[hash_type]["func"]
-        hash_valid = hash_types[hash_type]["hash_valid"]
-
-        data = io.BytesIO(b"Hello" * 1024 * 1024)  # 5MB
-        for i in range(num_run):
-            data.seek(0)
-            hash = hash_func(data)
-            yield "."
-        assert hash == hash_valid, "%s != %s" % (hash, hash_valid)
-
-    def testCryptHashlib(self, num_run=1, hash_type="sha3_256"):
-        """
-        Test SHA3 hashing functions
-        """
-        yield "x 5MB "
-
-        hash_types = {
-            "sha3_256": {"func": hashlib.sha3_256, "hash_valid": "c8aeb3ef9fe5d6404871c0d2a4410a4d4e23268e06735648c9596f436c495f7e"},
-            "sha3_512": {"func": hashlib.sha3_512, "hash_valid": "b75dba9472d8af3cc945ce49073f3f8214d7ac12086c0453fb08944823dee1ae83b3ffbc87a53a57cc454521d6a26fe73ff0f3be38dddf3f7de5d7692ebc7f95"},
-        }
-
-        hash_func = hash_types[hash_type]["func"]
-        hash_valid = hash_types[hash_type]["hash_valid"]
-
-        data = io.BytesIO(b"Hello" * 1024 * 1024)  # 5MB
-        for i in range(num_run):
-            data.seek(0)
-            h = hash_func()
-            while 1:
-                buff = data.read(1024 * 64)
-                if not buff:
-                    break
-                h.update(buff)
-            hash = h.hexdigest()
-            yield "."
-        assert hash == hash_valid, "%s != %s" % (hash, hash_valid)
-
-    def testRandom(self, num_run=1):
-        """
-        Test generating random data
-        """
-        yield "x 1000 x 256 bytes "
-        for i in range(num_run):
-            data_last = None
-            for y in range(1000):
-                data = os.urandom(256)
-                assert data != data_last
-                assert len(data) == 256
-                data_last = data
-            yield "."
-
-    def testHdPrivatekey(self, num_run=2):
-        """
-        Test generating deterministic private keys from a master seed
-        """
-        from Crypt import CryptBitcoin
-        seed = "e180efa477c63b0f2757eac7b1cce781877177fe0966be62754ffd4c8592ce38"
-        privatekeys = []
-        for i in range(num_run):
-            privatekeys.append(CryptBitcoin.hdPrivatekey(seed, i * 10))
-            yield "."
-        valid = "5JSbeF5PevdrsYjunqpg7kAGbnCVYa1T4APSL3QRu8EoAmXRc7Y"
-        assert privatekeys[0] == valid, "%s != %s" % (privatekeys[0], valid)
-        if len(privatekeys) > 1:
-            assert privatekeys[0] != privatekeys[-1]
-
-    def testSign(self, num_run=1):
-        """
-        Test signing data using a private key
-        """
-        from Crypt import CryptBitcoin
-        data = "Hello" * 1024
-        privatekey = "5JsunC55XGVqFQj5kPGK4MWgTL26jKbnPhjnmchSNPo75XXCwtk"
-        for i in range(num_run):
-            yield "."
-            sign = CryptBitcoin.sign(data, privatekey)
-            valid = "G1GXaDauZ8vX/N9Jn+MRiGm9h+I94zUhDnNYFaqMGuOiBHB+kp4cRPZOL7l1yqK5BHa6J+W97bMjvTXtxzljp6w="
-            assert sign == valid, "%s != %s" % (sign, valid)
-
-    def testVerify(self, num_run=1, lib_verify="sslcrypto"):
-        """
-        Test verification of generated signatures
-        """
-        from Crypt import CryptBitcoin
-        CryptBitcoin.loadLib(lib_verify, silent=True)
-
-
-        data = "Hello" * 1024
-        privatekey = "5JsunC55XGVqFQj5kPGK4MWgTL26jKbnPhjnmchSNPo75XXCwtk"
-        address = CryptBitcoin.privatekeyToAddress(privatekey)
-        sign = "G1GXaDauZ8vX/N9Jn+MRiGm9h+I94zUhDnNYFaqMGuOiBHB+kp4cRPZOL7l1yqK5BHa6J+W97bMjvTXtxzljp6w="
-
-        for i in range(num_run):
-            ok = CryptBitcoin.verify(data, address, sign, lib_verify=lib_verify)
-            yield "."
-            assert ok, "does not verify from %s" % address
-
-        if lib_verify == "sslcrypto":
-            yield("(%s)" % CryptBitcoin.sslcrypto.ecc.get_backend())
-
-    def testPortCheckers(self):
-        """
-        Test all active open port checker
-        """
-        from Peer import PeerPortchecker
-        for ip_type, func_names in PeerPortchecker.PeerPortchecker.checker_functions.items():
-            yield "\n- %s:" % ip_type
-            for func_name in func_names:
-                yield "\n - Tracker %s: " % func_name
-                try:
-                    for res in self.testPortChecker(func_name):
-                        yield res
-                except Exception as err:
-                    yield Debug.formatException(err)
-
-    def testPortChecker(self, func_name):
-        """
-        Test single open port checker
-        """
-        from Peer import PeerPortchecker
-        peer_portchecker = PeerPortchecker.PeerPortchecker(None)
-        announce_func = getattr(peer_portchecker, func_name)
-        res = announce_func(3894)
-        yield res
-
-    def testAll(self):
-        """
-        Run all tests to check system compatibility with ZeroNet functions
-        """
-        for progress in self.testBenchmark(online=not config.offline, num_run=1):
-            yield progress
-
-
-@PluginManager.registerTo("ConfigPlugin")
-class ConfigPlugin(object):
-    def createArguments(self):
-        back = super(ConfigPlugin, self).createArguments()
-        if self.getCmdlineValue("test") == "benchmark":
-            self.test_parser.add_argument(
-                '--num_multipler', help='Benchmark run time multipler',
-                default=1.0, type=float, metavar='num'
-            )
-            self.test_parser.add_argument(
-                '--filter', help='Filter running benchmark',
-                default=None, metavar='test name'
-            )
-        elif self.getCmdlineValue("test") == "portChecker":
-            self.test_parser.add_argument(
-                '--func_name', help='Name of open port checker function',
-                default=None, metavar='func_name'
-            )
-        return back
diff --git a/plugins/Benchmark/__init__.py b/plugins/Benchmark/__init__.py
deleted file mode 100644
index 76a5ae9c..00000000
--- a/plugins/Benchmark/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from . import BenchmarkPlugin
-from . import BenchmarkDb
-from . import BenchmarkPack
diff --git a/plugins/Benchmark/media/benchmark.html b/plugins/Benchmark/media/benchmark.html
deleted file mode 100644
index 73571367..00000000
--- a/plugins/Benchmark/media/benchmark.html
+++ /dev/null
@@ -1,123 +0,0 @@
-<html>
-<script nonce="{script_nonce}">
-window.benchmark_key = "{benchmark_key}";
-
-function setState(elem, text) {
-    var formatted = text
-    var parts = text.match(/\* Running (.*?)(\n|$)/g)
-    if (parts) {
-        for (var i=0; i < parts.length; i++) {
-            part = parts[i];
-            var details = part.match(/\* Running (.*?) (\.+|$)(.*)/);
-            if (details) {
-                var title = details[1]
-                var progress = details[2]
-                var result = details[3]
-
-                result_parts = result.match(/(.*) Done in ([0-9\.]+)s = (.*?) \(([0-9\.]+)x\)/)
-                var percent = Math.min(100, progress.length * 10)
-                if (result_parts) percent = 100
-                var style = "background-image: linear-gradient(90deg, #FFF " + percent + "%, #FFF 0%, #d9d5de 0%);"
-                var part_formatted = "<div class='test' style='" + style + "'>"
-                part_formatted += "<span class='title'>" + title + "</span><span class='percent percent-" + percent + "'>" + percent + "%</span> "
-                if (result_parts) {
-                    var result_extra = result_parts[1]
-                    var taken = result_parts[2]
-                    var multipler_title = result_parts[3]
-                    var multipler = result_parts[4]
-                    part_formatted += "<div class='result result-" + multipler_title.replace(/[^A-Za-z]/g, "") + "'>"
-                    part_formatted += " <span class='taken'>" + taken + "s</span>"
-                    part_formatted += " <span class='multipler'>" + multipler + "x</span>"
-                    part_formatted += " <span class='multipler-title'>" + multipler_title + "</span>"
-                    part_formatted += "</div>"
-                } else {
-                    part_formatted += "<div class='result'>" + result + "</div>"
-                }
-                part_formatted += "</div>"
-                formatted = formatted.replace(part, part_formatted);
-            }
-        }
-    }
-    formatted = formatted.replace(/(\! Error:.*)/, "<div class='test error'>$1</div>");
-    formatted = formatted.replace(/(\== Result ==[^]*)/, "<div class='test summary'>$1</div>");
-    var is_bottom = document.body.scrollTop + document.body.clientHeight >= document.body.scrollHeight - 5;
-    elem.innerHTML = formatted.trim();
-    if (is_bottom)
-        document.body.scrollTop = document.body.scrollHeight;
-}
-
-function stream(url, elem) {
-    document.getElementById("h1").innerText = "Benchmark: Starting..."
-    var xhr = new XMLHttpRequest();
-    xhr.open('GET', url, true);
-    xhr.setRequestHeader('Accept', 'text/html');
-    xhr.send(null);
-    xhr.onreadystatechange = function(state) {
-        document.getElementById("h1").innerText = "Benchmark: Running..."
-		setState(elem, xhr.responseText);
-		if (xhr.readyState == 4) {
-            document.getElementById("h1").innerText = "Benchmark: Done."
-		}
-	}
-}
-</script>
-<body>
-<style>
-body {
-background-color: #3c3546;
-background-image: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23cfcfcf' fill-opacity='0.09'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");}
-h1 {
-    font-family: monospace; color: white; font-weight: normal; text-transform: uppercase;
-    max-width: 690px; margin: 30px auto; margin-bottom: 10px;
-}
-#out {
-    white-space: pre-line; background-color: #ffffff1a; padding: 20px; font-family: Consolas, monospace;
-    font-size: 11px; width: 90%; margin: auto; max-width: 650px; box-shadow: 0px 10px 30px -10px #5c5c5c6b;
-}
-.test { padding: 12px; box-shadow: 0px 5px 13px -5px #5c5c5c6b; margin-bottom: -2px; background-color: white; border: 1px solid #dbdbdb; }
-.test .percent { float: right; }
-.test .percent-100 { display: none; }
-.test .result { float: right; }
-.test .title { max-width: calc(100% - 150px); display: inline-block; }
-.test .multipler-title { display: inline-block; width: 50px; text-align: right; }
-.test:last-child { margin-bottom: 15px; border-color: #c1c1c1; }
-
-.test .result-Sloooow { color: red; }
-.test .result-Ehh { color: #ad1457; }
-.test .result-Goodish { color: #ef6c00; }
-.test .result-Ok { color: #00cf03; }
-.test .result-Fine { color: #00bcd4; }
-.test .result-Fast { color: #4b78ff; }
-.test .result-WOW { color: #9c27b0; }
-.test .result-Insane { color: #d603f4; }
-
-.test.summary { margin-top: 20px; text-transform: uppercase; border-left: 10px solid #00ff63; border-color: #00ff63; }
-.test.error { background-color: #ff2259; color: white; border-color: red; }
-
-#start { text-align: center }
-.button {
-    background-color: white; padding: 10px 20px; display: inline-block; border-radius: 5px;
-    text-decoration: none; color: #673AB7; text-transform: uppercase; margin-bottom: 11px; border-bottom: 2px solid #c1bff8;
-}
-.button:hover { border-bottom-color: #673AB7; }
-.button:active { transform: translateY(1px) }
-small { text-transform: uppercase; opacity: 0.7; color: white; letter-spacing: 1px; }
-</style>
-
-<h1 id="h1">Benchmark</h1>
-<div id="out">
- <div id="start">
-  <a href="#Start" class="button" id="start_button">Start benchmark</a>
-  <small>(It will take around 20 sec)</small>
- </div>
-</div>
-
-<script nonce="{script_nonce}">
-function start() {
-    stream("/BenchmarkResult?benchmark_key={benchmark_key}&filter={filter}", document.getElementById("out"));
-    return false;
-}
-document.getElementById("start_button").onclick = start
-</script>
-</body>
-</html>
\ No newline at end of file
diff --git a/plugins/Benchmark/plugin_info.json b/plugins/Benchmark/plugin_info.json
deleted file mode 100644
index f3f57417..00000000
--- a/plugins/Benchmark/plugin_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"name": "Benchmark",
-	"description": "Test and benchmark database and cryptographic functions related to ZeroNet.",
-	"default": "enabled"
-}
\ No newline at end of file
diff --git a/plugins/Bigfile/BigfilePiecefield.py b/plugins/Bigfile/BigfilePiecefield.py
deleted file mode 100644
index 9a6f370b..00000000
--- a/plugins/Bigfile/BigfilePiecefield.py
+++ /dev/null
@@ -1,170 +0,0 @@
-import array
-
-
-def packPiecefield(data):
-    if not isinstance(data, bytes) and not isinstance(data, bytearray):
-        raise Exception("Invalid data type: %s" % type(data))
-
-    res = []
-    if not data:
-        return array.array("H", b"")
-
-    if data[0] == b"\x00":
-        res.append(0)
-        find = b"\x01"
-    else:
-        find = b"\x00"
-    last_pos = 0
-    pos = 0
-    while 1:
-        pos = data.find(find, pos)
-        if find == b"\x00":
-            find = b"\x01"
-        else:
-            find = b"\x00"
-        if pos == -1:
-            res.append(len(data) - last_pos)
-            break
-        res.append(pos - last_pos)
-        last_pos = pos
-    return array.array("H", res)
-
-
-def unpackPiecefield(data):
-    if not data:
-        return b""
-
-    res = []
-    char = b"\x01"
-    for times in data:
-        if times > 10000:
-            return b""
-        res.append(char * times)
-        if char == b"\x01":
-            char = b"\x00"
-        else:
-            char = b"\x01"
-    return b"".join(res)
-
-
-def spliceBit(data, idx, bit):
-    if bit != b"\x00" and bit != b"\x01":
-        raise Exception("Invalid bit: %s" % bit)
-
-    if len(data) < idx:
-        data = data.ljust(idx + 1, b"\x00")
-    return data[:idx] + bit + data[idx+ 1:]
-
-class Piecefield(object):
-    def tostring(self):
-        return "".join(["1" if b else "0" for b in self.tobytes()])
-
-
-class BigfilePiecefield(Piecefield):
-    __slots__ = ["data"]
-
-    def __init__(self):
-        self.data = b""
-
-    def frombytes(self, s):
-        if not isinstance(s, bytes) and not isinstance(s, bytearray):
-            raise Exception("Invalid type: %s" % type(s))
-        self.data = s
-
-    def tobytes(self):
-        return self.data
-
-    def pack(self):
-        return packPiecefield(self.data).tobytes()
-
-    def unpack(self, s):
-        self.data = unpackPiecefield(array.array("H", s))
-
-    def __getitem__(self, key):
-        try:
-            return self.data[key]
-        except IndexError:
-            return False
-
-    def __setitem__(self, key, value):
-        self.data = spliceBit(self.data, key, value)
-
-class BigfilePiecefieldPacked(Piecefield):
-    __slots__ = ["data"]
-
-    def __init__(self):
-        self.data = b""
-
-    def frombytes(self, data):
-        if not isinstance(data, bytes) and not isinstance(data, bytearray):
-            raise Exception("Invalid type: %s" % type(data))
-        self.data = packPiecefield(data).tobytes()
-
-    def tobytes(self):
-        return unpackPiecefield(array.array("H", self.data))
-
-    def pack(self):
-        return array.array("H", self.data).tobytes()
-
-    def unpack(self, data):
-        self.data = data
-
-    def __getitem__(self, key):
-        try:
-            return self.tobytes()[key]
-        except IndexError:
-            return False
-
-    def __setitem__(self, key, value):
-        data = spliceBit(self.tobytes(), key, value)
-        self.frombytes(data)
-
-
-if __name__ == "__main__":
-    import os
-    import psutil
-    import time
-    testdata = b"\x01" * 100 + b"\x00" * 900 + b"\x01" * 4000 + b"\x00" * 4999 + b"\x01"
-    meminfo = psutil.Process(os.getpid()).memory_info
-
-    for storage in [BigfilePiecefieldPacked, BigfilePiecefield]:
-        print("-- Testing storage: %s --" % storage)
-        m = meminfo()[0]
-        s = time.time()
-        piecefields = {}
-        for i in range(10000):
-            piecefield = storage()
-            piecefield.frombytes(testdata[:i] + b"\x00" + testdata[i + 1:])
-            piecefields[i] = piecefield
-
-        print("Create x10000: +%sKB in %.3fs (len: %s)" % ((meminfo()[0] - m) / 1024, time.time() - s, len(piecefields[0].data)))
-
-        m = meminfo()[0]
-        s = time.time()
-        for piecefield in list(piecefields.values()):
-            val = piecefield[1000]
-
-        print("Query one x10000: +%sKB in %.3fs" % ((meminfo()[0] - m) / 1024, time.time() - s))
-
-        m = meminfo()[0]
-        s = time.time()
-        for piecefield in list(piecefields.values()):
-            piecefield[1000] = b"\x01"
-
-        print("Change one x10000: +%sKB in %.3fs" % ((meminfo()[0] - m) / 1024, time.time() - s))
-
-        m = meminfo()[0]
-        s = time.time()
-        for piecefield in list(piecefields.values()):
-            packed = piecefield.pack()
-
-        print("Pack x10000: +%sKB in %.3fs (len: %s)" % ((meminfo()[0] - m) / 1024, time.time() - s, len(packed)))
-
-        m = meminfo()[0]
-        s = time.time()
-        for piecefield in list(piecefields.values()):
-            piecefield.unpack(packed)
-
-        print("Unpack x10000: +%sKB in %.3fs (len: %s)" % ((meminfo()[0] - m) / 1024, time.time() - s, len(piecefields[0].data)))
-
-        piecefields = {}
diff --git a/plugins/Bigfile/BigfilePlugin.py b/plugins/Bigfile/BigfilePlugin.py
deleted file mode 100644
index 78a27b05..00000000
--- a/plugins/Bigfile/BigfilePlugin.py
+++ /dev/null
@@ -1,843 +0,0 @@
-import time
-import os
-import subprocess
-import shutil
-import collections
-import math
-import warnings
-import base64
-import binascii
-import json
-
-import gevent
-import gevent.lock
-
-from Plugin import PluginManager
-from Debug import Debug
-from Crypt import CryptHash
-with warnings.catch_warnings():
-    warnings.filterwarnings("ignore")  # Ignore missing sha3 warning
-    import merkletools
-
-from util import helper
-from util import Msgpack
-from util.Flag import flag
-import util
-from .BigfilePiecefield import BigfilePiecefield, BigfilePiecefieldPacked
-
-
-# We can only import plugin host clases after the plugins are loaded
-@PluginManager.afterLoad
-def importPluginnedClasses():
-    global VerifyError, config
-    from Content.ContentManager import VerifyError
-    from Config import config
-
-
-if "upload_nonces" not in locals():
-    upload_nonces = {}
-
-
-@PluginManager.registerTo("UiRequest")
-class UiRequestPlugin(object):
-    def isCorsAllowed(self, path):
-        if path == "/ZeroNet-Internal/BigfileUpload":
-            return True
-        else:
-            return super(UiRequestPlugin, self).isCorsAllowed(path)
-
-    @helper.encodeResponse
-    def actionBigfileUpload(self):
-        nonce = self.get.get("upload_nonce")
-        if nonce not in upload_nonces:
-            return self.error403("Upload nonce error.")
-
-        upload_info = upload_nonces[nonce]
-        del upload_nonces[nonce]
-
-        self.sendHeader(200, "text/html", noscript=True, extra_headers={
-            "Access-Control-Allow-Origin": "null",
-            "Access-Control-Allow-Credentials": "true"
-        })
-
-        self.readMultipartHeaders(self.env['wsgi.input'])  # Skip http headers
-        result = self.handleBigfileUpload(upload_info, self.env['wsgi.input'].read)
-        return json.dumps(result)
-
-    def actionBigfileUploadWebsocket(self):
-        ws = self.env.get("wsgi.websocket")
-
-        if not ws:
-            self.start_response("400 Bad Request", [])
-            return [b"Not a websocket request!"]
-
-        nonce = self.get.get("upload_nonce")
-        if nonce not in upload_nonces:
-            return self.error403("Upload nonce error.")
-
-        upload_info = upload_nonces[nonce]
-        del upload_nonces[nonce]
-
-        ws.send("poll")
-
-        buffer = b""
-        def read(size):
-            nonlocal buffer
-            while len(buffer) < size:
-                buffer += ws.receive()
-                ws.send("poll")
-            part, buffer = buffer[:size], buffer[size:]
-            return part
-
-        result = self.handleBigfileUpload(upload_info, read)
-        ws.send(json.dumps(result))
-
-    def handleBigfileUpload(self, upload_info, read):
-        site = upload_info["site"]
-        inner_path = upload_info["inner_path"]
-
-        with site.storage.open(inner_path, "wb", create_dirs=True) as out_file:
-            merkle_root, piece_size, piecemap_info = site.content_manager.hashBigfile(
-                read, upload_info["size"], upload_info["piece_size"], out_file
-            )
-
-        if len(piecemap_info["sha512_pieces"]) == 1:  # Small file, don't split
-            hash = binascii.hexlify(piecemap_info["sha512_pieces"][0])
-            hash_id = site.content_manager.hashfield.getHashId(hash)
-            site.content_manager.optionalDownloaded(inner_path, hash_id, upload_info["size"], own=True)
-
-        else:  # Big file
-            file_name = helper.getFilename(inner_path)
-            site.storage.open(upload_info["piecemap"], "wb").write(Msgpack.pack({file_name: piecemap_info}))
-
-            # Find piecemap and file relative path to content.json
-            file_info = site.content_manager.getFileInfo(inner_path, new_file=True)
-            content_inner_path_dir = helper.getDirname(file_info["content_inner_path"])
-            piecemap_relative_path = upload_info["piecemap"][len(content_inner_path_dir):]
-            file_relative_path = inner_path[len(content_inner_path_dir):]
-
-            # Add file to content.json
-            if site.storage.isFile(file_info["content_inner_path"]):
-                content = site.storage.loadJson(file_info["content_inner_path"])
-            else:
-                content = {}
-            if "files_optional" not in content:
-                content["files_optional"] = {}
-
-            content["files_optional"][file_relative_path] = {
-                "sha512": merkle_root,
-                "size": upload_info["size"],
-                "piecemap": piecemap_relative_path,
-                "piece_size": piece_size
-            }
-
-            merkle_root_hash_id = site.content_manager.hashfield.getHashId(merkle_root)
-            site.content_manager.optionalDownloaded(inner_path, merkle_root_hash_id, upload_info["size"], own=True)
-            site.storage.writeJson(file_info["content_inner_path"], content)
-
-            site.content_manager.contents.loadItem(file_info["content_inner_path"])  # reload cache
-
-        return {
-            "merkle_root": merkle_root,
-            "piece_num": len(piecemap_info["sha512_pieces"]),
-            "piece_size": piece_size,
-            "inner_path": inner_path
-        }
-
-    def readMultipartHeaders(self, wsgi_input):
-        found = False
-        for i in range(100):
-            line = wsgi_input.readline()
-            if line == b"\r\n":
-                found = True
-                break
-        if not found:
-            raise Exception("No multipart header found")
-        return i
-
-    def actionFile(self, file_path, *args, **kwargs):
-        if kwargs.get("file_size", 0) > 1024 * 1024 and kwargs.get("path_parts"):  # Only check files larger than 1MB
-            path_parts = kwargs["path_parts"]
-            site = self.server.site_manager.get(path_parts["address"])
-            big_file = site.storage.openBigfile(path_parts["inner_path"], prebuffer=2 * 1024 * 1024)
-            if big_file:
-                kwargs["file_obj"] = big_file
-                kwargs["file_size"] = big_file.size
-
-        return super(UiRequestPlugin, self).actionFile(file_path, *args, **kwargs)
-
-
-@PluginManager.registerTo("UiWebsocket")
-class UiWebsocketPlugin(object):
-    def actionBigfileUploadInit(self, to, inner_path, size, protocol="xhr"):
-        valid_signers = self.site.content_manager.getValidSigners(inner_path)
-        auth_address = self.user.getAuthAddress(self.site.address)
-        if not self.site.settings["own"] and auth_address not in valid_signers:
-            self.log.error("FileWrite forbidden %s not in valid_signers %s" % (auth_address, valid_signers))
-            return self.response(to, {"error": "Forbidden, you can only modify your own files"})
-
-        nonce = CryptHash.random()
-        piece_size = 1024 * 1024
-        inner_path = self.site.content_manager.sanitizePath(inner_path)
-        file_info = self.site.content_manager.getFileInfo(inner_path, new_file=True)
-
-        content_inner_path_dir = helper.getDirname(file_info["content_inner_path"])
-        file_relative_path = inner_path[len(content_inner_path_dir):]
-
-        upload_nonces[nonce] = {
-            "added": time.time(),
-            "site": self.site,
-            "inner_path": inner_path,
-            "websocket_client": self,
-            "size": size,
-            "piece_size": piece_size,
-            "piecemap": inner_path + ".piecemap.msgpack"
-        }
-
-        if protocol == "xhr":
-            return {
-                "url": "/ZeroNet-Internal/BigfileUpload?upload_nonce=" + nonce,
-                "piece_size": piece_size,
-                "inner_path": inner_path,
-                "file_relative_path": file_relative_path
-            }
-        elif protocol == "websocket":
-            server_url = self.request.getWsServerUrl()
-            if server_url:
-                proto, host = server_url.split("://")
-                origin = proto.replace("http", "ws") + "://" + host
-            else:
-                origin = "{origin}"
-            return {
-                "url": origin + "/ZeroNet-Internal/BigfileUploadWebsocket?upload_nonce=" + nonce,
-                "piece_size": piece_size,
-                "inner_path": inner_path,
-                "file_relative_path": file_relative_path
-            }
-        else:
-            return {"error": "Unknown protocol"}
-
-    @flag.no_multiuser
-    def actionSiteSetAutodownloadBigfileLimit(self, to, limit):
-        permissions = self.getPermissions(to)
-        if "ADMIN" not in permissions:
-            return self.response(to, "You don't have permission to run this command")
-
-        self.site.settings["autodownload_bigfile_size_limit"] = int(limit)
-        self.response(to, "ok")
-
-    def actionFileDelete(self, to, inner_path):
-        piecemap_inner_path = inner_path + ".piecemap.msgpack"
-        if self.hasFilePermission(inner_path) and self.site.storage.isFile(piecemap_inner_path):
-            # Also delete .piecemap.msgpack file if exists
-            self.log.debug("Deleting piecemap: %s" % piecemap_inner_path)
-            file_info = self.site.content_manager.getFileInfo(piecemap_inner_path)
-            if file_info:
-                content_json = self.site.storage.loadJson(file_info["content_inner_path"])
-                relative_path = file_info["relative_path"]
-                if relative_path in content_json.get("files_optional", {}):
-                    del content_json["files_optional"][relative_path]
-                    self.site.storage.writeJson(file_info["content_inner_path"], content_json)
-                    self.site.content_manager.loadContent(file_info["content_inner_path"], add_bad_files=False, force=True)
-                    try:
-                        self.site.storage.delete(piecemap_inner_path)
-                    except Exception as err:
-                        self.log.error("File %s delete error: %s" % (piecemap_inner_path, err))
-
-        return super(UiWebsocketPlugin, self).actionFileDelete(to, inner_path)
-
-
-@PluginManager.registerTo("ContentManager")
-class ContentManagerPlugin(object):
-    def getFileInfo(self, inner_path, *args, **kwargs):
-        if "|" not in inner_path:
-            return super(ContentManagerPlugin, self).getFileInfo(inner_path, *args, **kwargs)
-
-        inner_path, file_range = inner_path.split("|")
-        pos_from, pos_to = map(int, file_range.split("-"))
-        file_info = super(ContentManagerPlugin, self).getFileInfo(inner_path, *args, **kwargs)
-        return file_info
-
-    def readFile(self, read_func, size, buff_size=1024 * 64):
-        part_num = 0
-        recv_left = size
-
-        while 1:
-            part_num += 1
-            read_size = min(buff_size, recv_left)
-            part = read_func(read_size)
-
-            if not part:
-                break
-            yield part
-
-            if part_num % 100 == 0:  # Avoid blocking ZeroNet execution during upload
-                time.sleep(0.001)
-
-            recv_left -= read_size
-            if recv_left <= 0:
-                break
-
-    def hashBigfile(self, read_func, size, piece_size=1024 * 1024, file_out=None):
-        self.site.settings["has_bigfile"] = True
-
-        recv = 0
-        try:
-            piece_hash = CryptHash.sha512t()
-            piece_hashes = []
-            piece_recv = 0
-
-            mt = merkletools.MerkleTools()
-            mt.hash_function = CryptHash.sha512t
-
-            part = ""
-            for part in self.readFile(read_func, size):
-                if file_out:
-                    file_out.write(part)
-
-                recv += len(part)
-                piece_recv += len(part)
-                piece_hash.update(part)
-                if piece_recv >= piece_size:
-                    piece_digest = piece_hash.digest()
-                    piece_hashes.append(piece_digest)
-                    mt.leaves.append(piece_digest)
-                    piece_hash = CryptHash.sha512t()
-                    piece_recv = 0
-
-                    if len(piece_hashes) % 100 == 0 or recv == size:
-                        self.log.info("- [HASHING:%.0f%%] Pieces: %s, %.1fMB/%.1fMB" % (
-                            float(recv) / size * 100, len(piece_hashes), recv / 1024 / 1024, size / 1024 / 1024
-                        ))
-                        part = ""
-            if len(part) > 0:
-                piece_digest = piece_hash.digest()
-                piece_hashes.append(piece_digest)
-                mt.leaves.append(piece_digest)
-        except Exception as err:
-            raise err
-        finally:
-            if file_out:
-                file_out.close()
-
-        mt.make_tree()
-        merkle_root = mt.get_merkle_root()
-        if type(merkle_root) is bytes:  # Python <3.5
-            merkle_root = merkle_root.decode()
-        return merkle_root, piece_size, {
-            "sha512_pieces": piece_hashes
-        }
-
-    def hashFile(self, dir_inner_path, file_relative_path, optional=False):
-        inner_path = dir_inner_path + file_relative_path
-
-        file_size = self.site.storage.getSize(inner_path)
-        # Only care about optional files >1MB
-        if not optional or file_size < 1 * 1024 * 1024:
-            return super(ContentManagerPlugin, self).hashFile(dir_inner_path, file_relative_path, optional)
-
-        back = {}
-        content = self.contents.get(dir_inner_path + "content.json")
-
-        hash = None
-        piecemap_relative_path = None
-        piece_size = None
-
-        # Don't re-hash if it's already in content.json
-        if content and file_relative_path in content.get("files_optional", {}):
-            file_node = content["files_optional"][file_relative_path]
-            if file_node["size"] == file_size:
-                self.log.info("- [SAME SIZE] %s" % file_relative_path)
-                hash = file_node.get("sha512")
-                piecemap_relative_path = file_node.get("piecemap")
-                piece_size = file_node.get("piece_size")
-
-        if not hash or not piecemap_relative_path:  # Not in content.json yet
-            if file_size < 5 * 1024 * 1024:  # Don't create piecemap automatically for files smaller than 5MB
-                return super(ContentManagerPlugin, self).hashFile(dir_inner_path, file_relative_path, optional)
-
-            self.log.info("- [HASHING] %s" % file_relative_path)
-            merkle_root, piece_size, piecemap_info = self.hashBigfile(self.site.storage.open(inner_path, "rb").read, file_size)
-            if not hash:
-                hash = merkle_root
-
-            if not piecemap_relative_path:
-                file_name = helper.getFilename(file_relative_path)
-                piecemap_relative_path = file_relative_path + ".piecemap.msgpack"
-                piecemap_inner_path = inner_path + ".piecemap.msgpack"
-
-                self.site.storage.open(piecemap_inner_path, "wb").write(Msgpack.pack({file_name: piecemap_info}))
-
-                back.update(super(ContentManagerPlugin, self).hashFile(dir_inner_path, piecemap_relative_path, optional=True))
-
-        piece_num = int(math.ceil(float(file_size) / piece_size))
-
-        # Add the merkle root to hashfield
-        hash_id = self.site.content_manager.hashfield.getHashId(hash)
-        self.optionalDownloaded(inner_path, hash_id, file_size, own=True)
-        self.site.storage.piecefields[hash].frombytes(b"\x01" * piece_num)
-
-        back[file_relative_path] = {"sha512": hash, "size": file_size, "piecemap": piecemap_relative_path, "piece_size": piece_size}
-        return back
-
-    def getPiecemap(self, inner_path):
-        file_info = self.site.content_manager.getFileInfo(inner_path)
-        piecemap_inner_path = helper.getDirname(file_info["content_inner_path"]) + file_info["piecemap"]
-        self.site.needFile(piecemap_inner_path, priority=20)
-        piecemap = Msgpack.unpack(self.site.storage.open(piecemap_inner_path, "rb").read())[helper.getFilename(inner_path)]
-        piecemap["piece_size"] = file_info["piece_size"]
-        return piecemap
-
-    def verifyPiece(self, inner_path, pos, piece):
-        try:
-            piecemap = self.getPiecemap(inner_path)
-        except Exception as err:
-            raise VerifyError("Unable to download piecemap: %s" % Debug.formatException(err))
-
-        piece_i = int(pos / piecemap["piece_size"])
-        if CryptHash.sha512sum(piece, format="digest") != piecemap["sha512_pieces"][piece_i]:
-            raise VerifyError("Invalid hash")
-        return True
-
-    def verifyFile(self, inner_path, file, ignore_same=True):
-        if "|" not in inner_path:
-            return super(ContentManagerPlugin, self).verifyFile(inner_path, file, ignore_same)
-
-        inner_path, file_range = inner_path.split("|")
-        pos_from, pos_to = map(int, file_range.split("-"))
-
-        return self.verifyPiece(inner_path, pos_from, file)
-
-    def optionalDownloaded(self, inner_path, hash_id, size=None, own=False):
-        if "|" in inner_path:
-            inner_path, file_range = inner_path.split("|")
-            pos_from, pos_to = map(int, file_range.split("-"))
-            file_info = self.getFileInfo(inner_path)
-
-            # Mark piece downloaded
-            piece_i = int(pos_from / file_info["piece_size"])
-            self.site.storage.piecefields[file_info["sha512"]][piece_i] = b"\x01"
-
-            # Only add to site size on first request
-            if hash_id in self.hashfield:
-                size = 0
-        elif size > 1024 * 1024:
-            file_info = self.getFileInfo(inner_path)
-            if file_info and "sha512" in file_info:  # We already have the file, but not in piecefield
-                sha512 = file_info["sha512"]
-                if sha512 not in self.site.storage.piecefields:
-                    self.site.storage.checkBigfile(inner_path)
-
-        return super(ContentManagerPlugin, self).optionalDownloaded(inner_path, hash_id, size, own)
-
-    def optionalRemoved(self, inner_path, hash_id, size=None):
-        if size and size > 1024 * 1024:
-            file_info = self.getFileInfo(inner_path)
-            sha512 = file_info["sha512"]
-            if sha512 in self.site.storage.piecefields:
-                del self.site.storage.piecefields[sha512]
-
-            # Also remove other pieces of the file from download queue
-            for key in list(self.site.bad_files.keys()):
-                if key.startswith(inner_path + "|"):
-                    del self.site.bad_files[key]
-            self.site.worker_manager.removeSolvedFileTasks()
-        return super(ContentManagerPlugin, self).optionalRemoved(inner_path, hash_id, size)
-
-
-@PluginManager.registerTo("SiteStorage")
-class SiteStoragePlugin(object):
-    def __init__(self, *args, **kwargs):
-        super(SiteStoragePlugin, self).__init__(*args, **kwargs)
-        self.piecefields = collections.defaultdict(BigfilePiecefield)
-        if "piecefields" in self.site.settings.get("cache", {}):
-            for sha512, piecefield_packed in self.site.settings["cache"].get("piecefields").items():
-                if piecefield_packed:
-                    self.piecefields[sha512].unpack(base64.b64decode(piecefield_packed))
-            self.site.settings["cache"]["piecefields"] = {}
-
-    def createSparseFile(self, inner_path, size, sha512=None):
-        file_path = self.getPath(inner_path)
-
-        self.ensureDir(os.path.dirname(inner_path))
-
-        f = open(file_path, 'wb')
-        f.truncate(min(1024 * 1024 * 5, size))  # Only pre-allocate up to 5MB
-        f.close()
-        if os.name == "nt":
-            startupinfo = subprocess.STARTUPINFO()
-            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
-            subprocess.call(["fsutil", "sparse", "setflag", file_path], close_fds=True, startupinfo=startupinfo)
-
-        if sha512 and sha512 in self.piecefields:
-            self.log.debug("%s: File not exists, but has piecefield. Deleting piecefield." % inner_path)
-            del self.piecefields[sha512]
-
-    def write(self, inner_path, content):
-        if "|" not in inner_path:
-            return super(SiteStoragePlugin, self).write(inner_path, content)
-
-        # Write to specific position by passing |{pos} after the filename
-        inner_path, file_range = inner_path.split("|")
-        pos_from, pos_to = map(int, file_range.split("-"))
-        file_path = self.getPath(inner_path)
-
-        # Create dir if not exist
-        self.ensureDir(os.path.dirname(inner_path))
-
-        if not os.path.isfile(file_path):
-            file_info = self.site.content_manager.getFileInfo(inner_path)
-            self.createSparseFile(inner_path, file_info["size"])
-
-        # Write file
-        with open(file_path, "rb+") as file:
-            file.seek(pos_from)
-            if hasattr(content, 'read'):  # File-like object
-                shutil.copyfileobj(content, file)  # Write buff to disk
-            else:  # Simple string
-                file.write(content)
-        del content
-        self.onUpdated(inner_path)
-
-    def checkBigfile(self, inner_path):
-        file_info = self.site.content_manager.getFileInfo(inner_path)
-        if not file_info or (file_info and "piecemap" not in file_info):  # It's not a big file
-            return False
-
-        self.site.settings["has_bigfile"] = True
-        file_path = self.getPath(inner_path)
-        sha512 = file_info["sha512"]
-        piece_num = int(math.ceil(float(file_info["size"]) / file_info["piece_size"]))
-        if os.path.isfile(file_path):
-            if sha512 not in self.piecefields:
-                if open(file_path, "rb").read(128) == b"\0" * 128:
-                    piece_data = b"\x00"
-                else:
-                    piece_data = b"\x01"
-                self.log.debug("%s: File exists, but not in piecefield. Filling piecefiled with %s * %s." % (inner_path, piece_num, piece_data))
-                self.piecefields[sha512].frombytes(piece_data * piece_num)
-        else:
-            self.log.debug("Creating bigfile: %s" % inner_path)
-            self.createSparseFile(inner_path, file_info["size"], sha512)
-            self.piecefields[sha512].frombytes(b"\x00" * piece_num)
-            self.log.debug("Created bigfile: %s" % inner_path)
-        return True
-
-    def openBigfile(self, inner_path, prebuffer=0):
-        if not self.checkBigfile(inner_path):
-            return False
-        self.site.needFile(inner_path, blocking=False)  # Download piecemap
-        return BigFile(self.site, inner_path, prebuffer=prebuffer)
-
-
-class BigFile(object):
-    def __init__(self, site, inner_path, prebuffer=0):
-        self.site = site
-        self.inner_path = inner_path
-        file_path = site.storage.getPath(inner_path)
-        file_info = self.site.content_manager.getFileInfo(inner_path)
-        self.piece_size = file_info["piece_size"]
-        self.sha512 = file_info["sha512"]
-        self.size = file_info["size"]
-        self.prebuffer = prebuffer
-        self.read_bytes = 0
-
-        self.piecefield = self.site.storage.piecefields[self.sha512]
-        self.f = open(file_path, "rb+")
-        self.read_lock = gevent.lock.Semaphore()
-
-    def read(self, buff=64 * 1024):
-        with self.read_lock:
-            pos = self.f.tell()
-            read_until = min(self.size, pos + buff)
-            requests = []
-            # Request all required blocks
-            while 1:
-                piece_i = int(pos / self.piece_size)
-                if piece_i * self.piece_size >= read_until:
-                    break
-                pos_from = piece_i * self.piece_size
-                pos_to = pos_from + self.piece_size
-                if not self.piecefield[piece_i]:
-                    requests.append(self.site.needFile("%s|%s-%s" % (self.inner_path, pos_from, pos_to), blocking=False, update=True, priority=10))
-                pos += self.piece_size
-
-            if not all(requests):
-                return None
-
-            # Request prebuffer
-            if self.prebuffer:
-                prebuffer_until = min(self.size, read_until + self.prebuffer)
-                priority = 3
-                while 1:
-                    piece_i = int(pos / self.piece_size)
-                    if piece_i * self.piece_size >= prebuffer_until:
-                        break
-                    pos_from = piece_i * self.piece_size
-                    pos_to = pos_from + self.piece_size
-                    if not self.piecefield[piece_i]:
-                        self.site.needFile("%s|%s-%s" % (self.inner_path, pos_from, pos_to), blocking=False, update=True, priority=max(0, priority))
-                    priority -= 1
-                    pos += self.piece_size
-
-            gevent.joinall(requests)
-            self.read_bytes += buff
-
-            # Increase buffer for long reads
-            if self.read_bytes > 7 * 1024 * 1024 and self.prebuffer < 5 * 1024 * 1024:
-                self.site.log.debug("%s: Increasing bigfile buffer size to 5MB..." % self.inner_path)
-                self.prebuffer = 5 * 1024 * 1024
-
-            return self.f.read(buff)
-
-    def seek(self, pos, whence=0):
-        with self.read_lock:
-            if whence == 2:  # Relative from file end
-                pos = self.size + pos  # Use the real size instead of size on the disk
-                whence = 0
-            return self.f.seek(pos, whence)
-
-    def seekable(self):
-        return self.f.seekable()
-
-    def tell(self):
-        return self.f.tell()
-
-    def close(self):
-        self.f.close()
-
-    def __enter__(self):
-        return self
-
-    def __exit__(self, exc_type, exc_val, exc_tb):
-        self.close()
-
-
-@PluginManager.registerTo("WorkerManager")
-class WorkerManagerPlugin(object):
-    def addTask(self, inner_path, *args, **kwargs):
-        file_info = kwargs.get("file_info")
-        if file_info and "piecemap" in file_info:  # Bigfile
-            self.site.settings["has_bigfile"] = True
-
-            piecemap_inner_path = helper.getDirname(file_info["content_inner_path"]) + file_info["piecemap"]
-            piecemap_task = None
-            if not self.site.storage.isFile(piecemap_inner_path):
-                # Start download piecemap
-                piecemap_task = super(WorkerManagerPlugin, self).addTask(piecemap_inner_path, priority=30)
-                autodownload_bigfile_size_limit = self.site.settings.get("autodownload_bigfile_size_limit", config.autodownload_bigfile_size_limit)
-                if "|" not in inner_path and self.site.isDownloadable(inner_path) and file_info["size"] / 1024 / 1024 <= autodownload_bigfile_size_limit:
-                    gevent.spawn_later(0.1, self.site.needFile, inner_path + "|all")  # Download all pieces
-
-            if "|" in inner_path:
-                # Start download piece
-                task = super(WorkerManagerPlugin, self).addTask(inner_path, *args, **kwargs)
-
-                inner_path, file_range = inner_path.split("|")
-                pos_from, pos_to = map(int, file_range.split("-"))
-                task["piece_i"] = int(pos_from / file_info["piece_size"])
-                task["sha512"] = file_info["sha512"]
-            else:
-                if inner_path in self.site.bad_files:
-                    del self.site.bad_files[inner_path]
-                if piecemap_task:
-                    task = piecemap_task
-                else:
-                    fake_evt = gevent.event.AsyncResult()  # Don't download anything if no range specified
-                    fake_evt.set(True)
-                    task = {"evt": fake_evt}
-
-            if not self.site.storage.isFile(inner_path):
-                self.site.storage.createSparseFile(inner_path, file_info["size"], file_info["sha512"])
-                piece_num = int(math.ceil(float(file_info["size"]) / file_info["piece_size"]))
-                self.site.storage.piecefields[file_info["sha512"]].frombytes(b"\x00" * piece_num)
-        else:
-            task = super(WorkerManagerPlugin, self).addTask(inner_path, *args, **kwargs)
-        return task
-
-    def taskAddPeer(self, task, peer):
-        if "piece_i" in task:
-            if not peer.piecefields[task["sha512"]][task["piece_i"]]:
-                if task["sha512"] not in peer.piecefields:
-                    gevent.spawn(peer.updatePiecefields, force=True)
-                elif not task["peers"]:
-                    gevent.spawn(peer.updatePiecefields)
-
-                return False  # Deny to add peers to task if file not in piecefield
-        return super(WorkerManagerPlugin, self).taskAddPeer(task, peer)
-
-
-@PluginManager.registerTo("FileRequest")
-class FileRequestPlugin(object):
-    def isReadable(self, site, inner_path, file, pos):
-        # Peek into file
-        if file.read(10) == b"\0" * 10:
-            # Looks empty, but makes sures we don't have that piece
-            file_info = site.content_manager.getFileInfo(inner_path)
-            if "piece_size" in file_info:
-                piece_i = int(pos / file_info["piece_size"])
-                if not site.storage.piecefields[file_info["sha512"]][piece_i]:
-                    return False
-        # Seek back to position we want to read
-        file.seek(pos)
-        return super(FileRequestPlugin, self).isReadable(site, inner_path, file, pos)
-
-    def actionGetPiecefields(self, params):
-        site = self.sites.get(params["site"])
-        if not site or not site.isServing():  # Site unknown or not serving
-            self.response({"error": "Unknown site"})
-            return False
-
-        # Add peer to site if not added before
-        peer = site.addPeer(self.connection.ip, self.connection.port, return_peer=True)
-        if not peer.connection:  # Just added
-            peer.connect(self.connection)  # Assign current connection to peer
-
-        piecefields_packed = {sha512: piecefield.pack() for sha512, piecefield in site.storage.piecefields.items()}
-        self.response({"piecefields_packed": piecefields_packed})
-
-    def actionSetPiecefields(self, params):
-        site = self.sites.get(params["site"])
-        if not site or not site.isServing():  # Site unknown or not serving
-            self.response({"error": "Unknown site"})
-            self.connection.badAction(5)
-            return False
-
-        # Add or get peer
-        peer = site.addPeer(self.connection.ip, self.connection.port, return_peer=True, connection=self.connection)
-        if not peer.connection:
-            peer.connect(self.connection)
-
-        peer.piecefields = collections.defaultdict(BigfilePiecefieldPacked)
-        for sha512, piecefield_packed in params["piecefields_packed"].items():
-            peer.piecefields[sha512].unpack(piecefield_packed)
-        site.settings["has_bigfile"] = True
-
-        self.response({"ok": "Updated"})
-
-
-@PluginManager.registerTo("Peer")
-class PeerPlugin(object):
-    def __getattr__(self, key):
-        if key == "piecefields":
-            self.piecefields = collections.defaultdict(BigfilePiecefieldPacked)
-            return self.piecefields
-        elif key == "time_piecefields_updated":
-            self.time_piecefields_updated = None
-            return self.time_piecefields_updated
-        else:
-            return super(PeerPlugin, self).__getattr__(key)
-
-    @util.Noparallel(ignore_args=True)
-    def updatePiecefields(self, force=False):
-        if self.connection and self.connection.handshake.get("rev", 0) < 2190:
-            return False  # Not supported
-
-        # Don't update piecefield again in 1 min
-        if self.time_piecefields_updated and time.time() - self.time_piecefields_updated < 60 and not force:
-            return False
-
-        self.time_piecefields_updated = time.time()
-        res = self.request("getPiecefields", {"site": self.site.address})
-        if not res or "error" in res:
-            return False
-
-        self.piecefields = collections.defaultdict(BigfilePiecefieldPacked)
-        try:
-            for sha512, piecefield_packed in res["piecefields_packed"].items():
-                self.piecefields[sha512].unpack(piecefield_packed)
-        except Exception as err:
-            self.log("Invalid updatePiecefields response: %s" % Debug.formatException(err))
-
-        return self.piecefields
-
-    def sendMyHashfield(self, *args, **kwargs):
-        return super(PeerPlugin, self).sendMyHashfield(*args, **kwargs)
-
-    def updateHashfield(self, *args, **kwargs):
-        if self.site.settings.get("has_bigfile"):
-            thread = gevent.spawn(self.updatePiecefields, *args, **kwargs)
-            back = super(PeerPlugin, self).updateHashfield(*args, **kwargs)
-            thread.join()
-            return back
-        else:
-            return super(PeerPlugin, self).updateHashfield(*args, **kwargs)
-
-    def getFile(self, site, inner_path, *args, **kwargs):
-        if "|" in inner_path:
-            inner_path, file_range = inner_path.split("|")
-            pos_from, pos_to = map(int, file_range.split("-"))
-            kwargs["pos_from"] = pos_from
-            kwargs["pos_to"] = pos_to
-        return super(PeerPlugin, self).getFile(site, inner_path, *args, **kwargs)
-
-
-@PluginManager.registerTo("Site")
-class SitePlugin(object):
-    def isFileDownloadAllowed(self, inner_path, file_info):
-        if "piecemap" in file_info:
-            file_size_mb = file_info["size"] / 1024 / 1024
-            if config.bigfile_size_limit and file_size_mb > config.bigfile_size_limit:
-                self.log.debug(
-                    "Bigfile size %s too large: %sMB > %sMB, skipping..." %
-                    (inner_path, file_size_mb, config.bigfile_size_limit)
-                )
-                return False
-
-            file_info = file_info.copy()
-            file_info["size"] = file_info["piece_size"]
-        return super(SitePlugin, self).isFileDownloadAllowed(inner_path, file_info)
-
-    def getSettingsCache(self):
-        back = super(SitePlugin, self).getSettingsCache()
-        if self.storage.piecefields:
-            back["piecefields"] = {sha512: base64.b64encode(piecefield.pack()).decode("utf8") for sha512, piecefield in self.storage.piecefields.items()}
-        return back
-
-    def needFile(self, inner_path, *args, **kwargs):
-        if inner_path.endswith("|all"):
-            @util.Pooled(20)
-            def pooledNeedBigfile(inner_path, *args, **kwargs):
-                if inner_path not in self.bad_files:
-                    self.log.debug("Cancelled piece, skipping %s" % inner_path)
-                    return False
-                return self.needFile(inner_path, *args, **kwargs)
-
-            inner_path = inner_path.replace("|all", "")
-            file_info = self.needFileInfo(inner_path)
-
-            # Use default function to download non-optional file
-            if "piece_size" not in file_info:
-                return super(SitePlugin, self).needFile(inner_path, *args, **kwargs)
-
-            file_size = file_info["size"]
-            piece_size = file_info["piece_size"]
-
-            piece_num = int(math.ceil(float(file_size) / piece_size))
-
-            file_threads = []
-
-            piecefield = self.storage.piecefields.get(file_info["sha512"])
-
-            for piece_i in range(piece_num):
-                piece_from = piece_i * piece_size
-                piece_to = min(file_size, piece_from + piece_size)
-                if not piecefield or not piecefield[piece_i]:
-                    inner_path_piece = "%s|%s-%s" % (inner_path, piece_from, piece_to)
-                    self.bad_files[inner_path_piece] = self.bad_files.get(inner_path_piece, 1)
-                    res = pooledNeedBigfile(inner_path_piece, blocking=False)
-                    if res is not True and res is not False:
-                        file_threads.append(res)
-            gevent.joinall(file_threads)
-        else:
-            return super(SitePlugin, self).needFile(inner_path, *args, **kwargs)
-
-
-@PluginManager.registerTo("ConfigPlugin")
-class ConfigPlugin(object):
-    def createArguments(self):
-        group = self.parser.add_argument_group("Bigfile plugin")
-        group.add_argument('--autodownload_bigfile_size_limit', help='Also download bigfiles smaller than this limit if help distribute option is checked', default=10, metavar="MB", type=int)
-        group.add_argument('--bigfile_size_limit', help='Maximum size of downloaded big files', default=False, metavar="MB", type=int)
-
-        return super(ConfigPlugin, self).createArguments()
diff --git a/plugins/Bigfile/Test/TestBigfile.py b/plugins/Bigfile/Test/TestBigfile.py
deleted file mode 100644
index 402646a6..00000000
--- a/plugins/Bigfile/Test/TestBigfile.py
+++ /dev/null
@@ -1,574 +0,0 @@
-import time
-import io
-import binascii
-
-import pytest
-import mock
-
-from Connection import ConnectionServer
-from Content.ContentManager import VerifyError
-from File import FileServer
-from File import FileRequest
-from Worker import WorkerManager
-from Peer import Peer
-from Bigfile import BigfilePiecefield, BigfilePiecefieldPacked
-from Test import Spy
-from util import Msgpack
-
-
-@pytest.mark.usefixtures("resetSettings")
-@pytest.mark.usefixtures("resetTempSettings")
-class TestBigfile:
-    privatekey = "5KUh3PvNm5HUWoCfSUfcYvfQ2g3PrRNJWr6Q9eqdBGu23mtMntv"
-    piece_size = 1024 * 1024
-
-    def createBigfile(self, site, inner_path="data/optional.any.iso", pieces=10):
-        f = site.storage.open(inner_path, "w")
-        for i in range(pieces * 100):
-            f.write(("Test%s" % i).ljust(10, "-") * 1000)
-        f.close()
-        assert site.content_manager.sign("content.json", self.privatekey)
-        return inner_path
-
-    def testPiecemapCreate(self, site):
-        inner_path = self.createBigfile(site)
-        content = site.storage.loadJson("content.json")
-        assert "data/optional.any.iso" in content["files_optional"]
-        file_node = content["files_optional"][inner_path]
-        assert file_node["size"] == 10 * 1000 * 1000
-        assert file_node["sha512"] == "47a72cde3be80b4a829e7674f72b7c6878cf6a70b0c58c6aa6c17d7e9948daf6"
-        assert file_node["piecemap"] == inner_path + ".piecemap.msgpack"
-
-        piecemap = Msgpack.unpack(site.storage.open(file_node["piecemap"], "rb").read())["optional.any.iso"]
-        assert len(piecemap["sha512_pieces"]) == 10
-        assert piecemap["sha512_pieces"][0] != piecemap["sha512_pieces"][1]
-        assert binascii.hexlify(piecemap["sha512_pieces"][0]) == b"a73abad9992b3d0b672d0c2a292046695d31bebdcb1e150c8410bbe7c972eff3"
-
-    def testVerifyPiece(self, site):
-        inner_path = self.createBigfile(site)
-
-        # Verify all 10 piece
-        f = site.storage.open(inner_path, "rb")
-        for i in range(10):
-            piece = io.BytesIO(f.read(1024 * 1024))
-            piece.seek(0)
-            site.content_manager.verifyPiece(inner_path, i * 1024 * 1024, piece)
-        f.close()
-
-        # Try to verify piece 0 with piece 1 hash
-        with pytest.raises(VerifyError) as err:
-            i = 1
-            f = site.storage.open(inner_path, "rb")
-            piece = io.BytesIO(f.read(1024 * 1024))
-            f.close()
-            site.content_manager.verifyPiece(inner_path, i * 1024 * 1024, piece)
-        assert "Invalid hash" in str(err.value)
-
-    def testSparseFile(self, site):
-        inner_path = "sparsefile"
-
-        # Create a 100MB sparse file
-        site.storage.createSparseFile(inner_path, 100 * 1024 * 1024)
-
-        # Write to file beginning
-        s = time.time()
-        f = site.storage.write("%s|%s-%s" % (inner_path, 0, 1024 * 1024), b"hellostart" * 1024)
-        time_write_start = time.time() - s
-
-        # Write to file end
-        s = time.time()
-        f = site.storage.write("%s|%s-%s" % (inner_path, 99 * 1024 * 1024, 99 * 1024 * 1024 + 1024 * 1024), b"helloend" * 1024)
-        time_write_end = time.time() - s
-
-        # Verify writes
-        f = site.storage.open(inner_path)
-        assert f.read(10) == b"hellostart"
-        f.seek(99 * 1024 * 1024)
-        assert f.read(8) == b"helloend"
-        f.close()
-
-        site.storage.delete(inner_path)
-
-        # Writing to end shold not take much longer, than writing to start
-        assert time_write_end <= max(0.1, time_write_start * 1.1)
-
-    def testRangedFileRequest(self, file_server, site, site_temp):
-        inner_path = self.createBigfile(site)
-
-        file_server.sites[site.address] = site
-        client = FileServer(file_server.ip, 1545)
-        client.sites[site_temp.address] = site_temp
-        site_temp.connection_server = client
-        connection = client.getConnection(file_server.ip, 1544)
-
-        # Add file_server as peer to client
-        peer_file_server = site_temp.addPeer(file_server.ip, 1544)
-
-        buff = peer_file_server.getFile(site_temp.address, "%s|%s-%s" % (inner_path, 5 * 1024 * 1024, 6 * 1024 * 1024))
-
-        assert len(buff.getvalue()) == 1 * 1024 * 1024  # Correct block size
-        assert buff.getvalue().startswith(b"Test524")  # Correct data
-        buff.seek(0)
-        assert site.content_manager.verifyPiece(inner_path, 5 * 1024 * 1024, buff)  # Correct hash
-
-        connection.close()
-        client.stop()
-
-    def testRangedFileDownload(self, file_server, site, site_temp):
-        inner_path = self.createBigfile(site)
-
-        # Init source server
-        site.connection_server = file_server
-        file_server.sites[site.address] = site
-
-        # Make sure the file and the piecemap in the optional hashfield
-        file_info = site.content_manager.getFileInfo(inner_path)
-        assert site.content_manager.hashfield.hasHash(file_info["sha512"])
-
-        piecemap_hash = site.content_manager.getFileInfo(file_info["piecemap"])["sha512"]
-        assert site.content_manager.hashfield.hasHash(piecemap_hash)
-
-        # Init client server
-        client = ConnectionServer(file_server.ip, 1545)
-        site_temp.connection_server = client
-        peer_client = site_temp.addPeer(file_server.ip, 1544)
-
-        # Download site
-        site_temp.download(blind_includes=True, retry_bad_files=False).join(timeout=10)
-
-        bad_files = site_temp.storage.verifyFiles(quick_check=True)["bad_files"]
-        assert not bad_files
-
-        # client_piecefield = peer_client.piecefields[file_info["sha512"]].tostring()
-        # assert client_piecefield == "1" * 10
-
-        # Download 5. and 10. block
-
-        site_temp.needFile("%s|%s-%s" % (inner_path, 5 * 1024 * 1024, 6 * 1024 * 1024))
-        site_temp.needFile("%s|%s-%s" % (inner_path, 9 * 1024 * 1024, 10 * 1024 * 1024))
-
-        # Verify 0. block not downloaded
-        f = site_temp.storage.open(inner_path)
-        assert f.read(10) == b"\0" * 10
-        # Verify 5. and 10. block downloaded
-        f.seek(5 * 1024 * 1024)
-        assert f.read(7) == b"Test524"
-        f.seek(9 * 1024 * 1024)
-        assert f.read(7) == b"943---T"
-
-        # Verify hashfield
-        assert set(site_temp.content_manager.hashfield) == set([18343, 43727])  # 18343: data/optional.any.iso, 43727: data/optional.any.iso.hashmap.msgpack
-
-    def testOpenBigfile(self, file_server, site, site_temp):
-        inner_path = self.createBigfile(site)
-
-        # Init source server
-        site.connection_server = file_server
-        file_server.sites[site.address] = site
-
-        # Init client server
-        client = ConnectionServer(file_server.ip, 1545)
-        site_temp.connection_server = client
-        site_temp.addPeer(file_server.ip, 1544)
-
-        # Download site
-        site_temp.download(blind_includes=True, retry_bad_files=False).join(timeout=10)
-
-        # Open virtual file
-        assert not site_temp.storage.isFile(inner_path)
-
-        with site_temp.storage.openBigfile(inner_path) as f:
-            with Spy.Spy(FileRequest, "route") as requests:
-                f.seek(5 * 1024 * 1024)
-                assert f.read(7) == b"Test524"
-
-                f.seek(9 * 1024 * 1024)
-                assert f.read(7) == b"943---T"
-
-            assert len(requests) == 4  # 1x peicemap + 1x getpiecefield + 2x for pieces
-
-            assert set(site_temp.content_manager.hashfield) == set([18343, 43727])
-
-            assert site_temp.storage.piecefields[f.sha512].tostring() == "0000010001"
-            assert f.sha512 in site_temp.getSettingsCache()["piecefields"]
-
-            # Test requesting already downloaded
-            with Spy.Spy(FileRequest, "route") as requests:
-                f.seek(5 * 1024 * 1024)
-                assert f.read(7) == b"Test524"
-
-            assert len(requests) == 0
-
-            # Test requesting multi-block overflow reads
-            with Spy.Spy(FileRequest, "route") as requests:
-                f.seek(5 * 1024 * 1024)  # We already have this block
-                data = f.read(1024 * 1024 * 3)  # Our read overflow to 6. and 7. block
-                assert data.startswith(b"Test524")
-                assert data.endswith(b"Test838-")
-                assert b"\0" not in data  # No null bytes allowed
-
-            assert len(requests) == 2  # Two block download
-
-            # Test out of range request
-            f.seek(5 * 1024 * 1024)
-            data = f.read(1024 * 1024 * 30)
-            assert len(data) == 10 * 1000 * 1000 - (5 * 1024 * 1024)
-
-            f.seek(30 * 1024 * 1024)
-            data = f.read(1024 * 1024 * 30)
-            assert len(data) == 0
-
-    @pytest.mark.parametrize("piecefield_obj", [BigfilePiecefield, BigfilePiecefieldPacked])
-    def testPiecefield(self, piecefield_obj, site):
-        testdatas = [
-            b"\x01" * 100 + b"\x00" * 900 + b"\x01" * 4000 + b"\x00" * 4999 + b"\x01",
-            b"\x00\x01\x00\x01\x00\x01" * 10 + b"\x00\x01" * 90 + b"\x01\x00" * 400 + b"\x00" * 4999,
-            b"\x01" * 10000,
-            b"\x00" * 10000
-        ]
-        for testdata in testdatas:
-            piecefield = piecefield_obj()
-
-            piecefield.frombytes(testdata)
-            assert piecefield.tobytes() == testdata
-            assert piecefield[0] == testdata[0]
-            assert piecefield[100] == testdata[100]
-            assert piecefield[1000] == testdata[1000]
-            assert piecefield[len(testdata) - 1] == testdata[len(testdata) - 1]
-
-            packed = piecefield.pack()
-            piecefield_new = piecefield_obj()
-            piecefield_new.unpack(packed)
-            assert piecefield.tobytes() == piecefield_new.tobytes()
-            assert piecefield_new.tobytes() == testdata
-
-    def testFileGet(self, file_server, site, site_temp):
-        inner_path = self.createBigfile(site)
-
-        # Init source server
-        site.connection_server = file_server
-        file_server.sites[site.address] = site
-
-        # Init client server
-        site_temp.connection_server = FileServer(file_server.ip, 1545)
-        site_temp.connection_server.sites[site_temp.address] = site_temp
-        site_temp.addPeer(file_server.ip, 1544)
-
-        # Download site
-        site_temp.download(blind_includes=True, retry_bad_files=False).join(timeout=10)
-
-        # Download second block
-        with site_temp.storage.openBigfile(inner_path) as f:
-            f.seek(1024 * 1024)
-            assert f.read(1024)[0:1] != b"\0"
-
-        # Make sure first block not download
-        with site_temp.storage.open(inner_path) as f:
-            assert f.read(1024)[0:1] == b"\0"
-
-        peer2 = site.addPeer(file_server.ip, 1545, return_peer=True)
-
-        # Should drop error on first block request
-        assert not peer2.getFile(site.address, "%s|0-%s" % (inner_path, 1024 * 1024 * 1))
-
-        # Should not drop error for second block request
-        assert peer2.getFile(site.address, "%s|%s-%s" % (inner_path, 1024 * 1024 * 1, 1024 * 1024 * 2))
-
-    def benchmarkPeerMemory(self, site, file_server):
-        # Init source server
-        site.connection_server = file_server
-        file_server.sites[site.address] = site
-
-        import psutil, os
-        meminfo = psutil.Process(os.getpid()).memory_info
-
-        mem_s = meminfo()[0]
-        s = time.time()
-        for i in range(25000):
-            site.addPeer(file_server.ip, i)
-        print("%.3fs MEM: + %sKB" % (time.time() - s, (meminfo()[0] - mem_s) / 1024))  # 0.082s MEM: + 6800KB
-        print(list(site.peers.values())[0].piecefields)
-
-    def testUpdatePiecefield(self, file_server, site, site_temp):
-        inner_path = self.createBigfile(site)
-
-        server1 = file_server
-        server1.sites[site.address] = site
-        server2 = FileServer(file_server.ip, 1545)
-        server2.sites[site_temp.address] = site_temp
-        site_temp.connection_server = server2
-
-        # Add file_server as peer to client
-        server2_peer1 = site_temp.addPeer(file_server.ip, 1544)
-
-        # Testing piecefield sync
-        assert len(server2_peer1.piecefields) == 0
-        assert server2_peer1.updatePiecefields()  # Query piecefields from peer
-        assert len(server2_peer1.piecefields) > 0
-
-    def testWorkerManagerPiecefieldDeny(self, file_server, site, site_temp):
-        inner_path = self.createBigfile(site)
-
-        server1 = file_server
-        server1.sites[site.address] = site
-        server2 = FileServer(file_server.ip, 1545)
-        server2.sites[site_temp.address] = site_temp
-        site_temp.connection_server = server2
-
-        # Add file_server as peer to client
-        server2_peer1 = site_temp.addPeer(file_server.ip, 1544)  # Working
-
-        site_temp.downloadContent("content.json", download_files=False)
-        site_temp.needFile("data/optional.any.iso.piecemap.msgpack")
-
-        # Add fake peers with optional files downloaded
-        for i in range(5):
-            fake_peer = site_temp.addPeer("127.0.1.%s" % i, 1544)
-            fake_peer.hashfield = site.content_manager.hashfield
-            fake_peer.has_hashfield = True
-
-        with Spy.Spy(WorkerManager, "addWorker") as requests:
-            site_temp.needFile("%s|%s-%s" % (inner_path, 5 * 1024 * 1024, 6 * 1024 * 1024))
-            site_temp.needFile("%s|%s-%s" % (inner_path, 6 * 1024 * 1024, 7 * 1024 * 1024))
-
-        # It should only request parts from peer1 as the other peers does not have the requested parts in piecefields
-        assert len([request[1] for request in requests if request[1] != server2_peer1]) == 0
-
-    def testWorkerManagerPiecefieldDownload(self, file_server, site, site_temp):
-        inner_path = self.createBigfile(site)
-
-        server1 = file_server
-        server1.sites[site.address] = site
-        server2 = FileServer(file_server.ip, 1545)
-        server2.sites[site_temp.address] = site_temp
-        site_temp.connection_server = server2
-        sha512 = site.content_manager.getFileInfo(inner_path)["sha512"]
-
-        # Create 10 fake peer for each piece
-        for i in range(10):
-            peer = Peer(file_server.ip, 1544, site_temp, server2)
-            peer.piecefields[sha512][i] = b"\x01"
-            peer.updateHashfield = mock.MagicMock(return_value=False)
-            peer.updatePiecefields = mock.MagicMock(return_value=False)
-            peer.findHashIds = mock.MagicMock(return_value={"nope": []})
-            peer.hashfield = site.content_manager.hashfield
-            peer.has_hashfield = True
-            peer.key = "Peer:%s" % i
-            site_temp.peers["Peer:%s" % i] = peer
-
-        site_temp.downloadContent("content.json", download_files=False)
-        site_temp.needFile("data/optional.any.iso.piecemap.msgpack")
-
-        with Spy.Spy(Peer, "getFile") as requests:
-            for i in range(10):
-                site_temp.needFile("%s|%s-%s" % (inner_path, i * 1024 * 1024, (i + 1) * 1024 * 1024))
-
-        assert len(requests) == 10
-        for i in range(10):
-            assert requests[i][0] == site_temp.peers["Peer:%s" % i]  # Every part should be requested from piece owner peer
-
-    def testDownloadStats(self, file_server, site, site_temp):
-        inner_path = self.createBigfile(site)
-
-        # Init source server
-        site.connection_server = file_server
-        file_server.sites[site.address] = site
-
-        # Init client server
-        client = ConnectionServer(file_server.ip, 1545)
-        site_temp.connection_server = client
-        site_temp.addPeer(file_server.ip, 1544)
-
-        # Download site
-        site_temp.download(blind_includes=True, retry_bad_files=False).join(timeout=10)
-
-        # Open virtual file
-        assert not site_temp.storage.isFile(inner_path)
-
-        # Check size before downloads
-        assert site_temp.settings["size"] < 10 * 1024 * 1024
-        assert site_temp.settings["optional_downloaded"] == 0
-        size_piecemap = site_temp.content_manager.getFileInfo(inner_path + ".piecemap.msgpack")["size"]
-        size_bigfile = site_temp.content_manager.getFileInfo(inner_path)["size"]
-
-        with site_temp.storage.openBigfile(inner_path) as f:
-            assert b"\0" not in f.read(1024)
-            assert site_temp.settings["optional_downloaded"] == size_piecemap + size_bigfile
-
-        with site_temp.storage.openBigfile(inner_path) as f:
-            # Don't count twice
-            assert b"\0" not in f.read(1024)
-            assert site_temp.settings["optional_downloaded"] == size_piecemap + size_bigfile
-
-            # Add second block
-            assert b"\0" not in f.read(1024 * 1024)
-            assert site_temp.settings["optional_downloaded"] == size_piecemap + size_bigfile
-
-    def testPrebuffer(self, file_server, site, site_temp):
-        inner_path = self.createBigfile(site)
-
-        # Init source server
-        site.connection_server = file_server
-        file_server.sites[site.address] = site
-
-        # Init client server
-        client = ConnectionServer(file_server.ip, 1545)
-        site_temp.connection_server = client
-        site_temp.addPeer(file_server.ip, 1544)
-
-        # Download site
-        site_temp.download(blind_includes=True, retry_bad_files=False).join(timeout=10)
-
-        # Open virtual file
-        assert not site_temp.storage.isFile(inner_path)
-
-        with site_temp.storage.openBigfile(inner_path, prebuffer=1024 * 1024 * 2) as f:
-            with Spy.Spy(FileRequest, "route") as requests:
-                f.seek(5 * 1024 * 1024)
-                assert f.read(7) == b"Test524"
-            # assert len(requests) == 3  # 1x piecemap + 1x getpiecefield + 1x for pieces
-            assert len([task for task in site_temp.worker_manager.tasks if task["inner_path"].startswith(inner_path)]) == 2
-
-            time.sleep(0.5)  # Wait prebuffer download
-
-            sha512 = site.content_manager.getFileInfo(inner_path)["sha512"]
-            assert site_temp.storage.piecefields[sha512].tostring() == "0000011100"
-
-            # No prebuffer beyond end of the file
-            f.seek(9 * 1024 * 1024)
-            assert b"\0" not in f.read(7)
-
-            assert len([task for task in site_temp.worker_manager.tasks if task["inner_path"].startswith(inner_path)]) == 0
-
-    def testDownloadAllPieces(self, file_server, site, site_temp):
-        inner_path = self.createBigfile(site)
-
-        # Init source server
-        site.connection_server = file_server
-        file_server.sites[site.address] = site
-
-        # Init client server
-        client = ConnectionServer(file_server.ip, 1545)
-        site_temp.connection_server = client
-        site_temp.addPeer(file_server.ip, 1544)
-
-        # Download site
-        site_temp.download(blind_includes=True, retry_bad_files=False).join(timeout=10)
-
-        # Open virtual file
-        assert not site_temp.storage.isFile(inner_path)
-
-        with Spy.Spy(FileRequest, "route") as requests:
-            site_temp.needFile("%s|all" % inner_path)
-
-        assert len(requests) == 12  # piecemap.msgpack, getPiecefields, 10 x piece
-
-        # Don't re-download already got pieces
-        with Spy.Spy(FileRequest, "route") as requests:
-            site_temp.needFile("%s|all" % inner_path)
-
-        assert len(requests) == 0
-
-    def testFileSize(self, file_server, site, site_temp):
-        inner_path = self.createBigfile(site)
-
-        # Init source server
-        site.connection_server = file_server
-        file_server.sites[site.address] = site
-
-        # Init client server
-        client = ConnectionServer(file_server.ip, 1545)
-        site_temp.connection_server = client
-        site_temp.addPeer(file_server.ip, 1544)
-
-        # Download site
-        site_temp.download(blind_includes=True, retry_bad_files=False).join(timeout=10)
-
-        # Open virtual file
-        assert not site_temp.storage.isFile(inner_path)
-
-        # Download first block
-        site_temp.needFile("%s|%s-%s" % (inner_path, 0 * 1024 * 1024, 1 * 1024 * 1024))
-        assert site_temp.storage.getSize(inner_path) < 1000 * 1000 * 10  # Size on the disk should be smaller than the real size
-
-        site_temp.needFile("%s|%s-%s" % (inner_path, 9 * 1024 * 1024, 10 * 1024 * 1024))
-        assert site_temp.storage.getSize(inner_path) == site.storage.getSize(inner_path)
-
-    def testFileRename(self, file_server, site, site_temp):
-        inner_path = self.createBigfile(site)
-
-        # Init source server
-        site.connection_server = file_server
-        file_server.sites[site.address] = site
-
-        # Init client server
-        site_temp.connection_server = FileServer(file_server.ip, 1545)
-        site_temp.connection_server.sites[site_temp.address] = site_temp
-        site_temp.addPeer(file_server.ip, 1544)
-
-        # Download site
-        site_temp.download(blind_includes=True, retry_bad_files=False).join(timeout=10)
-
-        with Spy.Spy(FileRequest, "route") as requests:
-            site_temp.needFile("%s|%s-%s" % (inner_path, 0, 1 * self.piece_size))
-
-        assert len([req for req in requests if req[1] == "streamFile"]) == 2  # 1 piece + piecemap
-
-        # Rename the file
-        inner_path_new = inner_path.replace(".iso", "-new.iso")
-        site.storage.rename(inner_path, inner_path_new)
-        site.storage.delete("data/optional.any.iso.piecemap.msgpack")
-        assert site.content_manager.sign("content.json", self.privatekey, remove_missing_optional=True)
-
-        files_optional = site.content_manager.contents["content.json"]["files_optional"].keys()
-
-        assert "data/optional.any-new.iso.piecemap.msgpack" in files_optional
-        assert "data/optional.any.iso.piecemap.msgpack" not in files_optional
-        assert "data/optional.any.iso" not in files_optional
-
-        with Spy.Spy(FileRequest, "route") as requests:
-            site.publish()
-            time.sleep(0.1)
-            site_temp.download(blind_includes=True, retry_bad_files=False).join(timeout=10)  # Wait for download
-
-            assert len([req[1] for req in requests if req[1] == "streamFile"]) == 0
-
-            with site_temp.storage.openBigfile(inner_path_new, prebuffer=0) as f:
-                f.read(1024)
-
-                # First piece already downloaded
-                assert [req for req in requests if req[1] == "streamFile"] == []
-
-                # Second piece needs to be downloaded + changed piecemap
-                f.seek(self.piece_size)
-                f.read(1024)
-                assert [req[3]["inner_path"] for req in requests if req[1] == "streamFile"] == [inner_path_new + ".piecemap.msgpack", inner_path_new]
-
-    @pytest.mark.parametrize("size", [1024 * 3, 1024 * 1024 * 3, 1024 * 1024 * 30])
-    def testNullFileRead(self, file_server, site, site_temp, size):
-        inner_path = "data/optional.iso"
-
-        f = site.storage.open(inner_path, "w")
-        f.write("\0" * size)
-        f.close()
-        assert site.content_manager.sign("content.json", self.privatekey)
-
-        # Init source server
-        site.connection_server = file_server
-        file_server.sites[site.address] = site
-
-        # Init client server
-        site_temp.connection_server = FileServer(file_server.ip, 1545)
-        site_temp.connection_server.sites[site_temp.address] = site_temp
-        site_temp.addPeer(file_server.ip, 1544)
-
-        # Download site
-        site_temp.download(blind_includes=True, retry_bad_files=False).join(timeout=10)
-
-        if "piecemap" in site.content_manager.getFileInfo(inner_path):  # Bigfile
-            site_temp.needFile(inner_path + "|all")
-        else:
-            site_temp.needFile(inner_path)
-
-
-        assert site_temp.storage.getSize(inner_path) == size
diff --git a/plugins/Bigfile/Test/conftest.py b/plugins/Bigfile/Test/conftest.py
deleted file mode 100644
index 634e66e2..00000000
--- a/plugins/Bigfile/Test/conftest.py
+++ /dev/null
@@ -1 +0,0 @@
-from src.Test.conftest import *
diff --git a/plugins/Bigfile/Test/pytest.ini b/plugins/Bigfile/Test/pytest.ini
deleted file mode 100644
index d09210d1..00000000
--- a/plugins/Bigfile/Test/pytest.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[pytest]
-python_files = Test*.py
-addopts = -rsxX -v --durations=6
-markers =
-    webtest: mark a test as a webtest.
\ No newline at end of file
diff --git a/plugins/Bigfile/__init__.py b/plugins/Bigfile/__init__.py
deleted file mode 100644
index cf2dcb49..00000000
--- a/plugins/Bigfile/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-from . import BigfilePlugin
-from .BigfilePiecefield import BigfilePiecefield, BigfilePiecefieldPacked
\ No newline at end of file
diff --git a/plugins/Chart/ChartCollector.py b/plugins/Chart/ChartCollector.py
deleted file mode 100644
index 215c603c..00000000
--- a/plugins/Chart/ChartCollector.py
+++ /dev/null
@@ -1,181 +0,0 @@
-import time
-import sys
-import collections
-import itertools
-import logging
-
-import gevent
-from util import helper
-from Config import config
-
-
-class ChartCollector(object):
-    def __init__(self, db):
-        self.db = db
-        if config.action == "main":
-            gevent.spawn_later(60 * 3, self.collector)
-        self.log = logging.getLogger("ChartCollector")
-        self.last_values = collections.defaultdict(dict)
-
-    def setInitialLastValues(self, sites):
-        # Recover last value of site bytes/sent
-        for site in sites:
-            self.last_values["site:" + site.address]["site_bytes_recv"] = site.settings.get("bytes_recv", 0)
-            self.last_values["site:" + site.address]["site_bytes_sent"] = site.settings.get("bytes_sent", 0)
-
-    def getCollectors(self):
-        collectors = {}
-        import main
-        file_server = main.file_server
-        sites = file_server.sites
-        if not sites:
-            return collectors
-        content_db = list(sites.values())[0].content_manager.contents.db
-
-        # Connection stats
-        collectors["connection"] = lambda: len(file_server.connections)
-        collectors["connection_in"] = (
-            lambda: len([1 for connection in file_server.connections if connection.type == "in"])
-        )
-        collectors["connection_onion"] = (
-            lambda: len([1 for connection in file_server.connections if connection.ip.endswith(".onion")])
-        )
-        collectors["connection_ping_avg"] = (
-            lambda: round(1000 * helper.avg(
-                [connection.last_ping_delay for connection in file_server.connections if connection.last_ping_delay]
-            ))
-        )
-        collectors["connection_ping_min"] = (
-            lambda: round(1000 * min(
-                [connection.last_ping_delay for connection in file_server.connections if connection.last_ping_delay]
-            ))
-        )
-        collectors["connection_rev_avg"] = (
-            lambda: helper.avg(
-                [connection.handshake["rev"] for connection in file_server.connections if connection.handshake]
-            )
-        )
-
-        # Request stats
-        collectors["file_bytes_recv|change"] = lambda: file_server.bytes_recv
-        collectors["file_bytes_sent|change"] = lambda: file_server.bytes_sent
-        collectors["request_num_recv|change"] = lambda: file_server.num_recv
-        collectors["request_num_sent|change"] = lambda: file_server.num_sent
-
-        # Limit
-        collectors["optional_limit"] = lambda: content_db.getOptionalLimitBytes()
-        collectors["optional_used"] = lambda: content_db.getOptionalUsedBytes()
-        collectors["optional_downloaded"] = lambda: sum([site.settings.get("optional_downloaded", 0) for site in sites.values()])
-
-        # Peers
-        collectors["peer"] = lambda peers: len(peers)
-        collectors["peer_onion"] = lambda peers: len([True for peer in peers if ".onion" in peer])
-
-        # Size
-        collectors["size"] = lambda: sum([site.settings.get("size", 0) for site in sites.values()])
-        collectors["size_optional"] = lambda: sum([site.settings.get("size_optional", 0) for site in sites.values()])
-        collectors["content"] = lambda: sum([len(site.content_manager.contents) for site in sites.values()])
-
-        return collectors
-
-    def getSiteCollectors(self):
-        site_collectors = {}
-
-        # Size
-        site_collectors["site_size"] = lambda site: site.settings.get("size", 0)
-        site_collectors["site_size_optional"] = lambda site: site.settings.get("size_optional", 0)
-        site_collectors["site_optional_downloaded"] = lambda site: site.settings.get("optional_downloaded", 0)
-        site_collectors["site_content"] = lambda site: len(site.content_manager.contents)
-
-        # Data transfer
-        site_collectors["site_bytes_recv|change"] = lambda site: site.settings.get("bytes_recv", 0)
-        site_collectors["site_bytes_sent|change"] = lambda site: site.settings.get("bytes_sent", 0)
-
-        # Peers
-        site_collectors["site_peer"] = lambda site: len(site.peers)
-        site_collectors["site_peer_onion"] = lambda site: len(
-            [True for peer in site.peers.values() if peer.ip.endswith(".onion")]
-        )
-        site_collectors["site_peer_connected"] = lambda site: len([True for peer in site.peers.values() if peer.connection])
-
-        return site_collectors
-
-    def getUniquePeers(self):
-        import main
-        sites = main.file_server.sites
-        return set(itertools.chain.from_iterable(
-            [site.peers.keys() for site in sites.values()]
-        ))
-
-    def collectDatas(self, collectors, last_values, site=None):
-        if site is None:
-            peers = self.getUniquePeers()
-        datas = {}
-        for key, collector in collectors.items():
-            try:
-                if site:
-                    value = collector(site)
-                elif key.startswith("peer"):
-                    value = collector(peers)
-                else:
-                    value = collector()
-            except ValueError:
-                value = None
-            except Exception as err:
-                self.log.info("Collector %s error: %s" % (key, err))
-                value = None
-
-            if "|change" in key:  # Store changes relative to last value
-                key = key.replace("|change", "")
-                last_value = last_values.get(key, 0)
-                last_values[key] = value
-                value = value - last_value
-
-            if value is None:
-                datas[key] = None
-            else:
-                datas[key] = round(value, 3)
-        return datas
-
-    def collectGlobal(self, collectors, last_values):
-        now = int(time.time())
-        s = time.time()
-        datas = self.collectDatas(collectors, last_values["global"])
-        values = []
-        for key, value in datas.items():
-            values.append((self.db.getTypeId(key), value, now))
-        self.log.debug("Global collectors done in %.3fs" % (time.time() - s))
-
-        s = time.time()
-        cur = self.db.getCursor()
-        cur.executemany("INSERT INTO data (type_id, value, date_added) VALUES (?, ?, ?)", values)
-        self.log.debug("Global collectors inserted in %.3fs" % (time.time() - s))
-
-    def collectSites(self, sites, collectors, last_values):
-        now = int(time.time())
-        s = time.time()
-        values = []
-        for address, site in list(sites.items()):
-            site_datas = self.collectDatas(collectors, last_values["site:%s" % address], site)
-            for key, value in site_datas.items():
-                values.append((self.db.getTypeId(key), self.db.getSiteId(address), value, now))
-            time.sleep(0.001)
-        self.log.debug("Site collections done in %.3fs" % (time.time() - s))
-
-        s = time.time()
-        cur = self.db.getCursor()
-        cur.executemany("INSERT INTO data (type_id, site_id, value, date_added) VALUES (?, ?, ?, ?)", values)
-        self.log.debug("Site collectors inserted in %.3fs" % (time.time() - s))
-
-    def collector(self):
-        collectors = self.getCollectors()
-        site_collectors = self.getSiteCollectors()
-        import main
-        sites = main.file_server.sites
-        i = 0
-        while 1:
-            self.collectGlobal(collectors, self.last_values)
-            if i % 12 == 0:  # Only collect sites data every hour
-                self.collectSites(sites, site_collectors, self.last_values)
-            time.sleep(60 * 5)
-            i += 1
diff --git a/plugins/Chart/ChartDb.py b/plugins/Chart/ChartDb.py
deleted file mode 100644
index 66a22082..00000000
--- a/plugins/Chart/ChartDb.py
+++ /dev/null
@@ -1,133 +0,0 @@
-from Config import config
-from Db.Db import Db
-import time
-
-
-class ChartDb(Db):
-    def __init__(self):
-        self.version = 2
-        super(ChartDb, self).__init__(self.getSchema(), "%s/chart.db" % config.data_dir)
-        self.foreign_keys = True
-        self.checkTables()
-        self.sites = self.loadSites()
-        self.types = self.loadTypes()
-
-    def getSchema(self):
-        schema = {}
-        schema["db_name"] = "Chart"
-        schema["tables"] = {}
-        schema["tables"]["data"] = {
-            "cols": [
-                ["data_id", "INTEGER PRIMARY KEY ASC AUTOINCREMENT NOT NULL UNIQUE"],
-                ["type_id", "INTEGER NOT NULL"],
-                ["site_id", "INTEGER"],
-                ["value", "INTEGER"],
-                ["date_added", "DATETIME DEFAULT (CURRENT_TIMESTAMP)"]
-            ],
-            "indexes": [
-                "CREATE INDEX site_id ON data (site_id)",
-                "CREATE INDEX date_added ON data (date_added)"
-            ],
-            "schema_changed": 2
-        }
-        schema["tables"]["type"] = {
-            "cols": [
-                ["type_id", "INTEGER PRIMARY KEY NOT NULL UNIQUE"],
-                ["name", "TEXT"]
-            ],
-            "schema_changed": 1
-        }
-        schema["tables"]["site"] = {
-            "cols": [
-                ["site_id", "INTEGER PRIMARY KEY NOT NULL UNIQUE"],
-                ["address", "TEXT"]
-            ],
-            "schema_changed": 1
-        }
-        return schema
-
-    def getTypeId(self, name):
-        if name not in self.types:
-            res = self.execute("INSERT INTO type ?", {"name": name})
-            self.types[name] = res.lastrowid
-
-        return self.types[name]
-
-    def getSiteId(self, address):
-        if address not in self.sites:
-            res = self.execute("INSERT INTO site ?", {"address": address})
-            self.sites[address] = res.lastrowid
-
-        return self.sites[address]
-
-    def loadSites(self):
-        sites = {}
-        for row in self.execute("SELECT * FROM site"):
-            sites[row["address"]] = row["site_id"]
-        return sites
-
-    def loadTypes(self):
-        types = {}
-        for row in self.execute("SELECT * FROM type"):
-            types[row["name"]] = row["type_id"]
-        return types
-
-    def deleteSite(self, address):
-        if address in self.sites:
-            site_id = self.sites[address]
-            del self.sites[address]
-            self.execute("DELETE FROM site WHERE ?", {"site_id": site_id})
-            self.execute("DELETE FROM data WHERE ?", {"site_id": site_id})
-
-    def archive(self):
-        week_back = 1
-        while 1:
-            s = time.time()
-            date_added_from = time.time() - 60 * 60 * 24 * 7 * (week_back + 1)
-            date_added_to = date_added_from + 60 * 60 * 24 * 7
-            res = self.execute("""
-                SELECT
-                 MAX(date_added) AS date_added,
-                 SUM(value) AS value,
-                 GROUP_CONCAT(data_id) AS data_ids,
-                 type_id,
-                 site_id,
-                 COUNT(*) AS num
-                FROM data
-                WHERE
-                 site_id IS NULL AND
-                 date_added > :date_added_from AND
-                 date_added < :date_added_to
-                GROUP BY strftime('%Y-%m-%d %H', date_added, 'unixepoch', 'localtime'), type_id
-            """, {"date_added_from": date_added_from, "date_added_to": date_added_to})
-
-            num_archived = 0
-            cur = self.getCursor()
-            for row in res:
-                if row["num"] == 1:
-                    continue
-                cur.execute("INSERT INTO data ?", {
-                    "type_id": row["type_id"],
-                    "site_id": row["site_id"],
-                    "value": row["value"],
-                    "date_added": row["date_added"]
-                })
-                cur.execute("DELETE FROM data WHERE data_id IN (%s)" % row["data_ids"])
-                num_archived += row["num"]
-            self.log.debug("Archived %s data from %s weeks ago in %.3fs" % (num_archived, week_back, time.time() - s))
-            week_back += 1
-            time.sleep(0.1)
-            if num_archived == 0:
-                break
-        # Only keep 6 month of global stats
-        self.execute(
-            "DELETE FROM data WHERE site_id IS NULL AND date_added < :date_added_limit",
-            {"date_added_limit": time.time() - 60 * 60 * 24 * 30 * 6 }
-        )
-        # Only keep 1 month of site stats
-        self.execute(
-            "DELETE FROM data WHERE site_id IS NOT NULL AND date_added < :date_added_limit",
-            {"date_added_limit": time.time() - 60 * 60 * 24 * 30 }
-        )
-        if week_back > 1:
-            self.execute("VACUUM")
diff --git a/plugins/Chart/ChartPlugin.py b/plugins/Chart/ChartPlugin.py
deleted file mode 100644
index 80a4d976..00000000
--- a/plugins/Chart/ChartPlugin.py
+++ /dev/null
@@ -1,57 +0,0 @@
-import time
-import itertools
-
-import gevent
-
-from Config import config
-from util import helper
-from util.Flag import flag
-from Plugin import PluginManager
-from .ChartDb import ChartDb
-from .ChartCollector import ChartCollector
-
-if "db" not in locals().keys():  # Share on reloads
-    db = ChartDb()
-    gevent.spawn_later(10 * 60, db.archive)
-    helper.timer(60 * 60 * 6, db.archive)
-    collector = ChartCollector(db)
-
-@PluginManager.registerTo("SiteManager")
-class SiteManagerPlugin(object):
-    def load(self, *args, **kwargs):
-        back = super(SiteManagerPlugin, self).load(*args, **kwargs)
-        collector.setInitialLastValues(self.sites.values())
-        return back
-
-    def delete(self, address, *args, **kwargs):
-        db.deleteSite(address)
-        return super(SiteManagerPlugin, self).delete(address, *args, **kwargs)
-
-@PluginManager.registerTo("UiWebsocket")
-class UiWebsocketPlugin(object):
-    @flag.admin
-    def actionChartDbQuery(self, to, query, params=None):
-        if config.debug or config.verbose:
-            s = time.time()
-        rows = []
-        try:
-            if not query.strip().upper().startswith("SELECT"):
-                raise Exception("Only SELECT query supported")
-            res = db.execute(query, params)
-        except Exception as err:  # Response the error to client
-            self.log.error("ChartDbQuery error: %s" % err)
-            return {"error": str(err)}
-        # Convert result to dict
-        for row in res:
-            rows.append(dict(row))
-        if config.verbose and time.time() - s > 0.1:  # Log slow query
-            self.log.debug("Slow query: %s (%.3fs)" % (query, time.time() - s))
-        return rows
-
-    @flag.admin
-    def actionChartGetPeerLocations(self, to):
-        peers = {}
-        for site in self.server.sites.values():
-            peers.update(site.peers)
-        peer_locations = self.getPeerLocations(peers)
-        return peer_locations
diff --git a/plugins/Chart/__init__.py b/plugins/Chart/__init__.py
deleted file mode 100644
index 2c284609..00000000
--- a/plugins/Chart/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import ChartPlugin
\ No newline at end of file
diff --git a/plugins/Chart/plugin_info.json b/plugins/Chart/plugin_info.json
deleted file mode 100644
index 3bdaea8a..00000000
--- a/plugins/Chart/plugin_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"name": "Chart",
-	"description": "Collect and provide stats of client information.",
-	"default": "enabled"
-}
\ No newline at end of file
diff --git a/plugins/ContentFilter/ContentFilterPlugin.py b/plugins/ContentFilter/ContentFilterPlugin.py
deleted file mode 100644
index f2f84b49..00000000
--- a/plugins/ContentFilter/ContentFilterPlugin.py
+++ /dev/null
@@ -1,262 +0,0 @@
-import time
-import re
-import html
-import os
-
-from Plugin import PluginManager
-from Translate import Translate
-from Config import config
-from util.Flag import flag
-
-from .ContentFilterStorage import ContentFilterStorage
-
-
-plugin_dir = os.path.dirname(__file__)
-
-if "_" not in locals():
-    _ = Translate(plugin_dir + "/languages/")
-
-
-@PluginManager.registerTo("SiteManager")
-class SiteManagerPlugin(object):
-    def load(self, *args, **kwargs):
-        global filter_storage
-        super(SiteManagerPlugin, self).load(*args, **kwargs)
-        filter_storage = ContentFilterStorage(site_manager=self)
-
-    def add(self, address, *args, **kwargs):
-        should_ignore_block = kwargs.get("ignore_block") or kwargs.get("settings")
-        if should_ignore_block:
-            block_details = None
-        elif filter_storage.isSiteblocked(address):
-            block_details = filter_storage.getSiteblockDetails(address)
-        else:
-            address_hashed = filter_storage.getSiteAddressHashed(address)
-            if filter_storage.isSiteblocked(address_hashed):
-                block_details = filter_storage.getSiteblockDetails(address_hashed)
-            else:
-                block_details = None
-
-        if block_details:
-            raise Exception("Site blocked: %s" % html.escape(block_details.get("reason", "unknown reason")))
-        else:
-            return super(SiteManagerPlugin, self).add(address, *args, **kwargs)
-
-
-@PluginManager.registerTo("UiWebsocket")
-class UiWebsocketPlugin(object):
-    # Mute
-    def cbMuteAdd(self, to, auth_address, cert_user_id, reason):
-        filter_storage.file_content["mutes"][auth_address] = {
-            "cert_user_id": cert_user_id, "reason": reason, "source": self.site.address, "date_added": time.time()
-        }
-        filter_storage.save()
-        filter_storage.changeDbs(auth_address, "remove")
-        self.response(to, "ok")
-
-    @flag.no_multiuser
-    def actionMuteAdd(self, to, auth_address, cert_user_id, reason):
-        if "ADMIN" in self.getPermissions(to):
-            self.cbMuteAdd(to, auth_address, cert_user_id, reason)
-        else:
-            self.cmd(
-                "confirm",
-                [_["Hide all content from <b>%s</b>?"] % html.escape(cert_user_id), _["Mute"]],
-                lambda res: self.cbMuteAdd(to, auth_address, cert_user_id, reason)
-            )
-
-    @flag.no_multiuser
-    def cbMuteRemove(self, to, auth_address):
-        del filter_storage.file_content["mutes"][auth_address]
-        filter_storage.save()
-        filter_storage.changeDbs(auth_address, "load")
-        self.response(to, "ok")
-
-    @flag.no_multiuser
-    def actionMuteRemove(self, to, auth_address):
-        if "ADMIN" in self.getPermissions(to):
-            self.cbMuteRemove(to, auth_address)
-        else:
-            cert_user_id = html.escape(filter_storage.file_content["mutes"][auth_address]["cert_user_id"])
-            self.cmd(
-                "confirm",
-                [_["Unmute <b>%s</b>?"] % cert_user_id, _["Unmute"]],
-                lambda res: self.cbMuteRemove(to, auth_address)
-            )
-
-    @flag.admin
-    def actionMuteList(self, to):
-        self.response(to, filter_storage.file_content["mutes"])
-
-    # Siteblock
-    @flag.no_multiuser
-    @flag.admin
-    def actionSiteblockIgnoreAddSite(self, to, site_address):
-        if site_address in filter_storage.site_manager.sites:
-            return {"error": "Site already added"}
-        else:
-            if filter_storage.site_manager.need(site_address, ignore_block=True):
-                return "ok"
-            else:
-                return {"error": "Invalid address"}
-
-    @flag.no_multiuser
-    @flag.admin
-    def actionSiteblockAdd(self, to, site_address, reason=None):
-        filter_storage.file_content["siteblocks"][site_address] = {"date_added": time.time(), "reason": reason}
-        filter_storage.save()
-        self.response(to, "ok")
-
-    @flag.no_multiuser
-    @flag.admin
-    def actionSiteblockRemove(self, to, site_address):
-        del filter_storage.file_content["siteblocks"][site_address]
-        filter_storage.save()
-        self.response(to, "ok")
-
-    @flag.admin
-    def actionSiteblockList(self, to):
-        self.response(to, filter_storage.file_content["siteblocks"])
-
-    @flag.admin
-    def actionSiteblockGet(self, to, site_address):
-        if filter_storage.isSiteblocked(site_address):
-            res = filter_storage.getSiteblockDetails(site_address)
-        else:
-            site_address_hashed = filter_storage.getSiteAddressHashed(site_address)
-            if filter_storage.isSiteblocked(site_address_hashed):
-                res = filter_storage.getSiteblockDetails(site_address_hashed)
-            else:
-                res = {"error": "Site block not found"}
-        self.response(to, res)
-
-    # Include
-    @flag.no_multiuser
-    def actionFilterIncludeAdd(self, to, inner_path, description=None, address=None):
-        if address:
-            if "ADMIN" not in self.getPermissions(to):
-                return self.response(to, {"error": "Forbidden: Only ADMIN sites can manage different site include"})
-            site = self.server.sites[address]
-        else:
-            address = self.site.address
-            site = self.site
-
-        if "ADMIN" in self.getPermissions(to):
-            self.cbFilterIncludeAdd(to, True, address, inner_path, description)
-        else:
-            content = site.storage.loadJson(inner_path)
-            title = _["New shared global content filter: <b>%s</b> (%s sites, %s users)"] % (
-                html.escape(inner_path), len(content.get("siteblocks", {})), len(content.get("mutes", {}))
-            )
-
-            self.cmd(
-                "confirm",
-                [title, "Add"],
-                lambda res: self.cbFilterIncludeAdd(to, res, address, inner_path, description)
-            )
-
-    def cbFilterIncludeAdd(self, to, res, address, inner_path, description):
-        if not res:
-            self.response(to, res)
-            return False
-
-        filter_storage.includeAdd(address, inner_path, description)
-        self.response(to, "ok")
-
-    @flag.no_multiuser
-    def actionFilterIncludeRemove(self, to, inner_path, address=None):
-        if address:
-            if "ADMIN" not in self.getPermissions(to):
-                return self.response(to, {"error": "Forbidden: Only ADMIN sites can manage different site include"})
-        else:
-            address = self.site.address
-
-        key = "%s/%s" % (address, inner_path)
-        if key not in filter_storage.file_content["includes"]:
-            self.response(to, {"error": "Include not found"})
-        filter_storage.includeRemove(address, inner_path)
-        self.response(to, "ok")
-
-    def actionFilterIncludeList(self, to, all_sites=False, filters=False):
-        if all_sites and "ADMIN" not in self.getPermissions(to):
-            return self.response(to, {"error": "Forbidden: Only ADMIN sites can list all sites includes"})
-
-        back = []
-        includes = filter_storage.file_content.get("includes", {}).values()
-        for include in includes:
-            if not all_sites and include["address"] != self.site.address:
-                continue
-            if filters:
-                include = dict(include)  # Don't modify original file_content
-                include_site = filter_storage.site_manager.get(include["address"])
-                if not include_site:
-                    continue
-                content = include_site.storage.loadJson(include["inner_path"])
-                include["mutes"] = content.get("mutes", {})
-                include["siteblocks"] = content.get("siteblocks", {})
-            back.append(include)
-        self.response(to, back)
-
-
-@PluginManager.registerTo("SiteStorage")
-class SiteStoragePlugin(object):
-    def updateDbFile(self, inner_path, file=None, cur=None):
-        if file is not False:  # File deletion always allowed
-            # Find for bitcoin addresses in file path
-            matches = re.findall("/(1[A-Za-z0-9]{26,35})/", inner_path)
-            # Check if any of the adresses are in the mute list
-            for auth_address in matches:
-                if filter_storage.isMuted(auth_address):
-                    self.log.debug("Mute match: %s, ignoring %s" % (auth_address, inner_path))
-                    return False
-
-        return super(SiteStoragePlugin, self).updateDbFile(inner_path, file=file, cur=cur)
-
-    def onUpdated(self, inner_path, file=None):
-        file_path = "%s/%s" % (self.site.address, inner_path)
-        if file_path in filter_storage.file_content["includes"]:
-            self.log.debug("Filter file updated: %s" % inner_path)
-            filter_storage.includeUpdateAll()
-        return super(SiteStoragePlugin, self).onUpdated(inner_path, file=file)
-
-
-@PluginManager.registerTo("UiRequest")
-class UiRequestPlugin(object):
-    def actionWrapper(self, path, extra_headers=None):
-        match = re.match(r"/(?P<address>[A-Za-z0-9\._-]+)(?P<inner_path>/.*|$)", path)
-        if not match:
-            return False
-        address = match.group("address")
-
-        if self.server.site_manager.get(address):  # Site already exists
-            return super(UiRequestPlugin, self).actionWrapper(path, extra_headers)
-
-        if self.isDomain(address):
-            address = self.resolveDomain(address)
-
-        if address:
-            address_hashed = filter_storage.getSiteAddressHashed(address)
-        else:
-            address_hashed = None
-
-        if filter_storage.isSiteblocked(address) or filter_storage.isSiteblocked(address_hashed):
-            site = self.server.site_manager.get(config.homepage)
-            if not extra_headers:
-                extra_headers = {}
-
-            script_nonce = self.getScriptNonce()
-
-            self.sendHeader(extra_headers=extra_headers, script_nonce=script_nonce)
-            return iter([super(UiRequestPlugin, self).renderWrapper(
-                site, path, "uimedia/plugins/contentfilter/blocklisted.html?address=" + address,
-                "Blacklisted site", extra_headers, show_loadingscreen=False, script_nonce=script_nonce
-            )])
-        else:
-            return super(UiRequestPlugin, self).actionWrapper(path, extra_headers)
-
-    def actionUiMedia(self, path, *args, **kwargs):
-        if path.startswith("/uimedia/plugins/contentfilter/"):
-            file_path = path.replace("/uimedia/plugins/contentfilter/", plugin_dir + "/media/")
-            return self.actionFile(file_path)
-        else:
-            return super(UiRequestPlugin, self).actionUiMedia(path)
diff --git a/plugins/ContentFilter/ContentFilterStorage.py b/plugins/ContentFilter/ContentFilterStorage.py
deleted file mode 100644
index 289ec2a9..00000000
--- a/plugins/ContentFilter/ContentFilterStorage.py
+++ /dev/null
@@ -1,164 +0,0 @@
-import os
-import json
-import logging
-import collections
-import time
-import hashlib
-
-from Debug import Debug
-from Plugin import PluginManager
-from Config import config
-from util import helper
-
-
-class ContentFilterStorage(object):
-    def __init__(self, site_manager):
-        self.log = logging.getLogger("ContentFilterStorage")
-        self.file_path = "%s/filters.json" % config.data_dir
-        self.site_manager = site_manager
-        self.file_content = self.load()
-
-        # Set default values for filters.json
-        if not self.file_content:
-            self.file_content = {}
-
-        # Site blacklist renamed to site blocks
-        if "site_blacklist" in self.file_content:
-            self.file_content["siteblocks"] = self.file_content["site_blacklist"]
-            del self.file_content["site_blacklist"]
-
-        for key in ["mutes", "siteblocks", "includes"]:
-            if key not in self.file_content:
-                self.file_content[key] = {}
-
-        self.include_filters = collections.defaultdict(set)  # Merged list of mutes and blacklists from all include
-        self.includeUpdateAll(update_site_dbs=False)
-
-    def load(self):
-        # Rename previously used mutes.json -> filters.json
-        if os.path.isfile("%s/mutes.json" % config.data_dir):
-            self.log.info("Renaming mutes.json to filters.json...")
-            os.rename("%s/mutes.json" % config.data_dir, self.file_path)
-        if os.path.isfile(self.file_path):
-            try:
-                return json.load(open(self.file_path))
-            except Exception as err:
-                self.log.error("Error loading filters.json: %s" % err)
-                return None
-        else:
-            return None
-
-    def includeUpdateAll(self, update_site_dbs=True):
-        s = time.time()
-        new_include_filters = collections.defaultdict(set)
-
-        # Load all include files data into a merged set
-        for include_path in self.file_content["includes"]:
-            address, inner_path = include_path.split("/", 1)
-            try:
-                content = self.site_manager.get(address).storage.loadJson(inner_path)
-            except Exception as err:
-                self.log.warning(
-                    "Error loading include %s: %s" %
-                    (include_path, Debug.formatException(err))
-                )
-                continue
-
-            for key, val in content.items():
-                if type(val) is not dict:
-                    continue
-
-                new_include_filters[key].update(val.keys())
-
-        mutes_added = new_include_filters["mutes"].difference(self.include_filters["mutes"])
-        mutes_removed = self.include_filters["mutes"].difference(new_include_filters["mutes"])
-
-        self.include_filters = new_include_filters
-
-        if update_site_dbs:
-            for auth_address in mutes_added:
-                self.changeDbs(auth_address, "remove")
-
-            for auth_address in mutes_removed:
-                if not self.isMuted(auth_address):
-                    self.changeDbs(auth_address, "load")
-
-        num_mutes = len(self.include_filters["mutes"])
-        num_siteblocks = len(self.include_filters["siteblocks"])
-        self.log.debug(
-            "Loaded %s mutes, %s blocked sites from %s includes in %.3fs" %
-            (num_mutes, num_siteblocks, len(self.file_content["includes"]), time.time() - s)
-        )
-
-    def includeAdd(self, address, inner_path, description=None):
-        self.file_content["includes"]["%s/%s" % (address, inner_path)] = {
-            "date_added": time.time(),
-            "address": address,
-            "description": description,
-            "inner_path": inner_path
-        }
-        self.includeUpdateAll()
-        self.save()
-
-    def includeRemove(self, address, inner_path):
-        del self.file_content["includes"]["%s/%s" % (address, inner_path)]
-        self.includeUpdateAll()
-        self.save()
-
-    def save(self):
-        s = time.time()
-        helper.atomicWrite(self.file_path, json.dumps(self.file_content, indent=2, sort_keys=True).encode("utf8"))
-        self.log.debug("Saved in %.3fs" % (time.time() - s))
-
-    def isMuted(self, auth_address):
-        if auth_address in self.file_content["mutes"] or auth_address in self.include_filters["mutes"]:
-            return True
-        else:
-            return False
-
-    def getSiteAddressHashed(self, address):
-        return "0x" + hashlib.sha256(address.encode("ascii")).hexdigest()
-
-    def isSiteblocked(self, address):
-        if address in self.file_content["siteblocks"] or address in self.include_filters["siteblocks"]:
-            return True
-        return False
-
-    def getSiteblockDetails(self, address):
-        details = self.file_content["siteblocks"].get(address)
-        if not details:
-            address_sha256 = self.getSiteAddressHashed(address)
-            details = self.file_content["siteblocks"].get(address_sha256)
-
-        if not details:
-            includes = self.file_content.get("includes", {}).values()
-            for include in includes:
-                include_site = self.site_manager.get(include["address"])
-                if not include_site:
-                    continue
-                content = include_site.storage.loadJson(include["inner_path"])
-                details = content.get("siteblocks", {}).get(address)
-                if details:
-                    details["include"] = include
-                    break
-
-        return details
-
-    # Search and remove or readd files of an user
-    def changeDbs(self, auth_address, action):
-        self.log.debug("Mute action %s on user %s" % (action, auth_address))
-        res = list(self.site_manager.list().values())[0].content_manager.contents.db.execute(
-            "SELECT * FROM content LEFT JOIN site USING (site_id) WHERE inner_path LIKE :inner_path",
-            {"inner_path": "%%/%s/%%" % auth_address}
-        )
-        for row in res:
-            site = self.site_manager.sites.get(row["address"])
-            if not site:
-                continue
-            dir_inner_path = helper.getDirname(row["inner_path"])
-            for file_name in site.storage.walk(dir_inner_path):
-                if action == "remove":
-                    site.storage.onUpdated(dir_inner_path + file_name, False)
-                else:
-                    site.storage.onUpdated(dir_inner_path + file_name)
-                site.onFileDone(dir_inner_path + file_name)
diff --git a/plugins/ContentFilter/Test/TestContentFilter.py b/plugins/ContentFilter/Test/TestContentFilter.py
deleted file mode 100644
index e1b37b16..00000000
--- a/plugins/ContentFilter/Test/TestContentFilter.py
+++ /dev/null
@@ -1,82 +0,0 @@
-import pytest
-from ContentFilter import ContentFilterPlugin
-from Site import SiteManager
-
-
-@pytest.fixture
-def filter_storage():
-    ContentFilterPlugin.filter_storage = ContentFilterPlugin.ContentFilterStorage(SiteManager.site_manager)
-    return ContentFilterPlugin.filter_storage
-
-
-@pytest.mark.usefixtures("resetSettings")
-@pytest.mark.usefixtures("resetTempSettings")
-class TestContentFilter:
-    def createInclude(self, site):
-        site.storage.writeJson("filters.json", {
-            "mutes": {"1J6UrZMkarjVg5ax9W4qThir3BFUikbW6C": {}},
-            "siteblocks": {site.address: {}}
-        })
-
-    def testIncludeLoad(self, site, filter_storage):
-        self.createInclude(site)
-        filter_storage.file_content["includes"]["%s/%s" % (site.address, "filters.json")] = {
-            "date_added": 1528295893,
-        }
-
-        assert not filter_storage.include_filters["mutes"]
-        assert not filter_storage.isMuted("1J6UrZMkarjVg5ax9W4qThir3BFUikbW6C")
-        assert not filter_storage.isSiteblocked(site.address)
-        filter_storage.includeUpdateAll(update_site_dbs=False)
-        assert len(filter_storage.include_filters["mutes"]) == 1
-        assert filter_storage.isMuted("1J6UrZMkarjVg5ax9W4qThir3BFUikbW6C")
-        assert filter_storage.isSiteblocked(site.address)
-
-    def testIncludeAdd(self, site, filter_storage):
-        self.createInclude(site)
-        query_num_json = "SELECT COUNT(*) AS num FROM json WHERE directory = 'users/1J6UrZMkarjVg5ax9W4qThir3BFUikbW6C'"
-        assert not filter_storage.isSiteblocked(site.address)
-        assert not filter_storage.isMuted("1J6UrZMkarjVg5ax9W4qThir3BFUikbW6C")
-        assert site.storage.query(query_num_json).fetchone()["num"] == 2
-
-        # Add include
-        filter_storage.includeAdd(site.address, "filters.json")
-
-        assert filter_storage.isSiteblocked(site.address)
-        assert filter_storage.isMuted("1J6UrZMkarjVg5ax9W4qThir3BFUikbW6C")
-        assert site.storage.query(query_num_json).fetchone()["num"] == 0
-
-        # Remove include
-        filter_storage.includeRemove(site.address, "filters.json")
-
-        assert not filter_storage.isSiteblocked(site.address)
-        assert not filter_storage.isMuted("1J6UrZMkarjVg5ax9W4qThir3BFUikbW6C")
-        assert site.storage.query(query_num_json).fetchone()["num"] == 2
-
-    def testIncludeChange(self, site, filter_storage):
-        self.createInclude(site)
-        filter_storage.includeAdd(site.address, "filters.json")
-        assert filter_storage.isSiteblocked(site.address)
-        assert filter_storage.isMuted("1J6UrZMkarjVg5ax9W4qThir3BFUikbW6C")
-
-        # Add new blocked site
-        assert not filter_storage.isSiteblocked("1Hello")
-
-        filter_content = site.storage.loadJson("filters.json")
-        filter_content["siteblocks"]["1Hello"] = {}
-        site.storage.writeJson("filters.json", filter_content)
-
-        assert filter_storage.isSiteblocked("1Hello")
-
-        # Add new muted user
-        query_num_json = "SELECT COUNT(*) AS num FROM json WHERE directory = 'users/1C5sgvWaSgfaTpV5kjBCnCiKtENNMYo69q'"
-        assert not filter_storage.isMuted("1C5sgvWaSgfaTpV5kjBCnCiKtENNMYo69q")
-        assert site.storage.query(query_num_json).fetchone()["num"] == 2
-
-        filter_content["mutes"]["1C5sgvWaSgfaTpV5kjBCnCiKtENNMYo69q"] = {}
-        site.storage.writeJson("filters.json", filter_content)
-
-        assert filter_storage.isMuted("1C5sgvWaSgfaTpV5kjBCnCiKtENNMYo69q")
-        assert site.storage.query(query_num_json).fetchone()["num"] == 0
-
-
diff --git a/plugins/ContentFilter/Test/conftest.py b/plugins/ContentFilter/Test/conftest.py
deleted file mode 100644
index 634e66e2..00000000
--- a/plugins/ContentFilter/Test/conftest.py
+++ /dev/null
@@ -1 +0,0 @@
-from src.Test.conftest import *
diff --git a/plugins/ContentFilter/Test/pytest.ini b/plugins/ContentFilter/Test/pytest.ini
deleted file mode 100644
index d09210d1..00000000
--- a/plugins/ContentFilter/Test/pytest.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[pytest]
-python_files = Test*.py
-addopts = -rsxX -v --durations=6
-markers =
-    webtest: mark a test as a webtest.
\ No newline at end of file
diff --git a/plugins/ContentFilter/__init__.py b/plugins/ContentFilter/__init__.py
deleted file mode 100644
index 2cbca8ee..00000000
--- a/plugins/ContentFilter/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import ContentFilterPlugin
diff --git a/plugins/ContentFilter/languages/hu.json b/plugins/ContentFilter/languages/hu.json
deleted file mode 100644
index 9b57e697..00000000
--- a/plugins/ContentFilter/languages/hu.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-	"Hide all content from <b>%s</b>?": "<b>%s</b> tartalmaniak elrejtése?",
-	"Mute": "Elnémítás",
-	"Unmute <b>%s</b>?": "<b>%s</b> tartalmaniak megjelenítése?",
-	"Unmute": "Némítás visszavonása"
-}
diff --git a/plugins/ContentFilter/languages/it.json b/plugins/ContentFilter/languages/it.json
deleted file mode 100644
index 9a2c6761..00000000
--- a/plugins/ContentFilter/languages/it.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-	"Hide all content from <b>%s</b>?": "<b>%s</b> Vuoi nascondere i contenuti di questo utente ?",
-	"Mute": "Attiva Silenzia",
-	"Unmute <b>%s</b>?": "<b>%s</b> Vuoi mostrare i contenuti di questo utente ?",
-	"Unmute": "Disattiva Silenzia"
-}
diff --git a/plugins/ContentFilter/languages/jp.json b/plugins/ContentFilter/languages/jp.json
deleted file mode 100644
index ef586a1a..00000000
--- a/plugins/ContentFilter/languages/jp.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-	"Hide all content from <b>%s</b>?": "<b>%s</b> のコンテンツをすべて隠しますか?",
-	"Mute": "ミュート",
-	"Unmute <b>%s</b>?": "<b>%s</b> のミュートを解除しますか?",
-	"Unmute": "ミュート解除"
-}
diff --git a/plugins/ContentFilter/languages/pt-br.json b/plugins/ContentFilter/languages/pt-br.json
deleted file mode 100644
index 3c6bfbdc..00000000
--- a/plugins/ContentFilter/languages/pt-br.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-	"Hide all content from <b>%s</b>?": "<b>%s</b> Ocultar todo o conteúdo de ?",
-	"Mute": "Ativar o Silêncio",
-	"Unmute <b>%s</b>?": "<b>%s</b> Você quer mostrar o conteúdo deste usuário ?",
-	"Unmute": "Desligar o silêncio"
-}
diff --git a/plugins/ContentFilter/languages/zh-tw.json b/plugins/ContentFilter/languages/zh-tw.json
deleted file mode 100644
index 0995f3a0..00000000
--- a/plugins/ContentFilter/languages/zh-tw.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-	"Hide all content from <b>%s</b>?": "屏蔽 <b>%s</b> 的所有內容?",
-	"Mute": "屏蔽",
-	"Unmute <b>%s</b>?": "對 <b>%s</b> 解除屏蔽?",
-	"Unmute": "解除屏蔽"
-}
diff --git a/plugins/ContentFilter/languages/zh.json b/plugins/ContentFilter/languages/zh.json
deleted file mode 100644
index bf63f107..00000000
--- a/plugins/ContentFilter/languages/zh.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-	"Hide all content from <b>%s</b>?": "屏蔽 <b>%s</b> 的所有内容?",
-	"Mute": "屏蔽",
-	"Unmute <b>%s</b>?": "对 <b>%s</b> 解除屏蔽?",
-	"Unmute": "解除屏蔽"
-}
diff --git a/plugins/ContentFilter/media/blocklisted.html b/plugins/ContentFilter/media/blocklisted.html
deleted file mode 100644
index c9d201a9..00000000
--- a/plugins/ContentFilter/media/blocklisted.html
+++ /dev/null
@@ -1,89 +0,0 @@
-<html>
-<body>
-
-<style>
-.content { line-height: 24px; font-family: monospace; font-size: 14px; color: #636363; text-transform: uppercase; top: 38%; position: relative; text-align: center; perspective: 1000px }
-.content h1, .content h2 { font-weight: normal; letter-spacing: 1px; }
-.content h2 { font-size: 15px; }
-.content #details {
-    text-align: left; display: inline-block; width: 350px; background-color: white; padding: 17px 27px; border-radius: 0px;
-    box-shadow: 0px 2px 7px -1px #d8d8d8; text-transform: none; margin: 15px; transform: scale(0) rotateX(90deg); transition: all 0.6s cubic-bezier(0.785, 0.135, 0.15, 0.86);
-}
-.content #details #added { font-size: 12px; text-align: right; color: #a9a9a9; }
-
-#button { transition: all 1s cubic-bezier(0.075, 0.82, 0.165, 1); opacity: 0; transform: translateY(50px); transition-delay: 0.5s }
-.button {
-    padding: 8px 20px; background-color: #FFF85F; border-bottom: 2px solid #CDBD1E; border-radius: 2px;
-    text-decoration: none; transition: all 0.5s; background-position: left center; display: inline-block; margin-top: 10px; color: black;
-}
-.button:hover { background-color: #FFF400; border-bottom: 2px solid #4D4D4C; transition: none; }
-.button:active { position: relative; top: 1px; }
-.button:focus { outline: none; }
-
-.textbutton { color: #999; margin-top: 25px; display: inline-block; text-transform: none; font-family: Arial, Helvetica; text-decoration: none; padding: 5px 15px; }
-.textbutton-main { background-color: #FFF; color: #333; border-radius: 5px; }
-.textbutton:hover { text-decoration: underline; color: #333; transition: none !important; }
-.textbutton:active { background-color: #fafbfc; }
-</style>
-
-<div class="content">
- <h1>Site blocked</h1>
- <h2>This site is on your blocklist:</h2>
- <div id="details">
-  <div id="reason">Too much image</div>
-  <div id="added">on 2015-01-25 12:32:11</div>
- </div>
- <div id="buttons">
-  <a href="/" class="textbutton textbutton-main" id="back">Back to homepage</a>
-  <a href="#Visit+Site" class="textbutton" id="visit">Remove from blocklist and visit the site</a>
- </div>
-</div>
-
-<script type="text/javascript" src="js/ZeroFrame.js"></script>
-
-<script>
-function buf2hex(buffer) {
-    return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
-}
-
-async function sha256hex(s) {
-    var buff = new TextEncoder("utf-8").encode(s)
-    return "0x" + buf2hex(await crypto.subtle.digest("SHA-256", buff))
-}
-
-class Page extends ZeroFrame {
-    onOpenWebsocket () {
-    	this.cmd("wrapperSetTitle", "Visiting a blocked site - ZeroNet")
-        this.cmd("siteInfo", {}, (site_info) => {
-            this.site_info = site_info
-        })
-        var address = document.location.search.match(/address=(.*?)[&\?]/)[1]
-        this.updateSiteblockDetails(address)
-    }
-
-    async updateSiteblockDetails(address) {
-        var block = await this.cmdp("siteblockGet", address)
-        var reason = block["reason"]
-        if (!reason) reason = "Unknown reason"
-        var date = new Date(block["date_added"] * 1000)
-        document.getElementById("reason").innerText = reason
-        document.getElementById("added").innerText = "at " + date.toLocaleDateString() + " " + date.toLocaleTimeString()
-        if (block["include"]) {
-            document.getElementById("added").innerText += " from a shared blocklist"
-            document.getElementById("visit").innerText = "Ignore blocking and visit the site"
-        }
-        document.getElementById("details").style.transform = "scale(1) rotateX(0deg)"
-        document.getElementById("visit").style.transform = "translateY(0)"
-        document.getElementById("visit").style.opacity = "1"
-        document.getElementById("visit").onclick = () => {
-            if (block["include"])
-                this.cmd("siteblockIgnoreAddSite", address, () => { this.cmd("wrapperReload") })
-            else
-                this.cmd("siteblockRemove", address, () => { this.cmd("wrapperReload") })
-        }
-    }
-}
-page = new Page()
-</script>
-</body>
-</html>
diff --git a/plugins/ContentFilter/media/js/ZeroFrame.js b/plugins/ContentFilter/media/js/ZeroFrame.js
deleted file mode 100644
index d6facdbf..00000000
--- a/plugins/ContentFilter/media/js/ZeroFrame.js
+++ /dev/null
@@ -1,119 +0,0 @@
-// Version 1.0.0 - Initial release
-// Version 1.1.0 (2017-08-02) - Added cmdp function that returns promise instead of using callback
-// Version 1.2.0 (2017-08-02) - Added Ajax monkey patch to emulate XMLHttpRequest over ZeroFrame API
-
-const CMD_INNER_READY = 'innerReady'
-const CMD_RESPONSE = 'response'
-const CMD_WRAPPER_READY = 'wrapperReady'
-const CMD_PING = 'ping'
-const CMD_PONG = 'pong'
-const CMD_WRAPPER_OPENED_WEBSOCKET = 'wrapperOpenedWebsocket'
-const CMD_WRAPPER_CLOSE_WEBSOCKET = 'wrapperClosedWebsocket'
-
-class ZeroFrame {
-    constructor(url) {
-        this.url = url
-        this.waiting_cb = {}
-        this.wrapper_nonce = document.location.href.replace(/.*wrapper_nonce=([A-Za-z0-9]+).*/, "$1")
-        this.connect()
-        this.next_message_id = 1
-        this.init()
-    }
-
-    init() {
-        return this
-    }
-
-    connect() {
-        this.target = window.parent
-        window.addEventListener('message', e => this.onMessage(e), false)
-        this.cmd(CMD_INNER_READY)
-    }
-
-    onMessage(e) {
-        let message = e.data
-        let cmd = message.cmd
-        if (cmd === CMD_RESPONSE) {
-            if (this.waiting_cb[message.to] !== undefined) {
-                this.waiting_cb[message.to](message.result)
-            }
-            else {
-                this.log("Websocket callback not found:", message)
-            }
-        } else if (cmd === CMD_WRAPPER_READY) {
-            this.cmd(CMD_INNER_READY)
-        } else if (cmd === CMD_PING) {
-            this.response(message.id, CMD_PONG)
-        } else if (cmd === CMD_WRAPPER_OPENED_WEBSOCKET) {
-            this.onOpenWebsocket()
-        } else if (cmd === CMD_WRAPPER_CLOSE_WEBSOCKET) {
-            this.onCloseWebsocket()
-        } else {
-            this.onRequest(cmd, message)
-        }
-    }
-
-    onRequest(cmd, message) {
-        this.log("Unknown request", message)
-    }
-
-    response(to, result) {
-        this.send({
-            cmd: CMD_RESPONSE,
-            to: to,
-            result: result
-        })
-    }
-
-    cmd(cmd, params={}, cb=null) {
-        this.send({
-            cmd: cmd,
-            params: params
-        }, cb)
-    }
-
-    cmdp(cmd, params={}) {
-        return new Promise((resolve, reject) => {
-            this.cmd(cmd, params, (res) => {
-                if (res && res.error) {
-                    reject(res.error)
-                } else {
-                    resolve(res)
-                }
-            })
-        })
-    }
-
-    send(message, cb=null) {
-        message.wrapper_nonce = this.wrapper_nonce
-        message.id = this.next_message_id
-        this.next_message_id++
-        this.target.postMessage(message, '*')
-        if (cb) {
-            this.waiting_cb[message.id] = cb
-        }
-    }
-
-    log(...args) {
-        console.log.apply(console, ['[ZeroFrame]'].concat(args))
-    }
-
-    onOpenWebsocket() {
-        this.log('Websocket open')
-    }
-
-    onCloseWebsocket() {
-        this.log('Websocket close')
-    }
-
-    monkeyPatchAjax() {
-        var page = this
-        XMLHttpRequest.prototype.realOpen = XMLHttpRequest.prototype.open
-        this.cmd("wrapperGetAjaxKey", [], (res) => { this.ajax_key = res })
-        var newOpen = function (method, url, async) {
-            url += "?ajax_key=" + page.ajax_key
-            return this.realOpen(method, url, async)
-        }
-        XMLHttpRequest.prototype.open = newOpen
-    }
-}
diff --git a/plugins/ContentFilter/plugin_info.json b/plugins/ContentFilter/plugin_info.json
deleted file mode 100644
index f63bc984..00000000
--- a/plugins/ContentFilter/plugin_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"name": "ContentFilter",
-	"description": "Manage site and user block list.",
-	"default": "enabled"
-}
\ No newline at end of file
diff --git a/plugins/Cors/CorsPlugin.py b/plugins/Cors/CorsPlugin.py
deleted file mode 100644
index c9437538..00000000
--- a/plugins/Cors/CorsPlugin.py
+++ /dev/null
@@ -1,139 +0,0 @@
-import re
-import html
-import copy
-import os
-import gevent
-
-from Plugin import PluginManager
-from Translate import Translate
-
-
-plugin_dir = os.path.dirname(__file__)
-
-if "_" not in locals():
-    _ = Translate(plugin_dir + "/languages/")
-
-
-def getCorsPath(site, inner_path):
-    match = re.match("^cors-([A-Za-z0-9]{26,35})/(.*)", inner_path)
-    if not match:
-        raise Exception("Invalid cors path: %s" % inner_path)
-    cors_address = match.group(1)
-    cors_inner_path = match.group(2)
-
-    if not "Cors:%s" % cors_address in site.settings["permissions"]:
-        raise Exception("This site has no permission to access site %s" % cors_address)
-
-    return cors_address, cors_inner_path
-
-
-@PluginManager.registerTo("UiWebsocket")
-class UiWebsocketPlugin(object):
-    def hasSitePermission(self, address, cmd=None):
-        if super(UiWebsocketPlugin, self).hasSitePermission(address, cmd=cmd):
-            return True
-
-        allowed_commands = [
-            "fileGet", "fileList", "dirList", "fileRules", "optionalFileInfo",
-            "fileQuery", "dbQuery", "userGetSettings", "siteInfo"
-        ]
-        if not "Cors:%s" % address in self.site.settings["permissions"] or cmd not in allowed_commands:
-            return False
-        else:
-            return True
-
-    # Add cors support for file commands
-    def corsFuncWrapper(self, func_name, to, inner_path, *args, **kwargs):
-        if inner_path.startswith("cors-"):
-            cors_address, cors_inner_path = getCorsPath(self.site, inner_path)
-
-            req_self = copy.copy(self)
-            req_self.site = self.server.sites.get(cors_address)  # Change the site to the merged one
-            if not req_self.site:
-                return {"error": "No site found"}
-
-            func = getattr(super(UiWebsocketPlugin, req_self), func_name)
-            back = func(to, cors_inner_path, *args, **kwargs)
-            return back
-        else:
-            func = getattr(super(UiWebsocketPlugin, self), func_name)
-            return func(to, inner_path, *args, **kwargs)
-
-    def actionFileGet(self, to, inner_path, *args, **kwargs):
-        return self.corsFuncWrapper("actionFileGet", to, inner_path, *args, **kwargs)
-
-    def actionFileList(self, to, inner_path, *args, **kwargs):
-        return self.corsFuncWrapper("actionFileList", to, inner_path, *args, **kwargs)
-
-    def actionDirList(self, to, inner_path, *args, **kwargs):
-        return self.corsFuncWrapper("actionDirList", to, inner_path, *args, **kwargs)
-
-    def actionFileRules(self, to, inner_path, *args, **kwargs):
-        return self.corsFuncWrapper("actionFileRules", to, inner_path, *args, **kwargs)
-
-    def actionOptionalFileInfo(self, to, inner_path, *args, **kwargs):
-        return self.corsFuncWrapper("actionOptionalFileInfo", to, inner_path, *args, **kwargs)
-
-    def actionCorsPermission(self, to, address):
-        if isinstance(address, list):
-            addresses = address
-        else:
-            addresses = [address]
-
-        button_title = _["Grant"]
-        site_names = []
-        site_addresses = []
-        for address in addresses:
-            site = self.server.sites.get(address)
-            if site:
-                site_name = site.content_manager.contents.get("content.json", {}).get("title", address)
-            else:
-                site_name = address
-                # If at least one site is not downloaded yet, show "Grant & Add" instead
-                button_title = _["Grant & Add"]
-
-            if not (site and "Cors:" + address in self.permissions):
-                # No site or no permission
-                site_names.append(site_name)
-                site_addresses.append(address)
-
-        if len(site_names) == 0:
-            return "ignored"
-
-        self.cmd(
-            "confirm",
-            [_["This site requests <b>read</b> permission to: <b>%s</b>"] % ", ".join(map(html.escape, site_names)), button_title],
-            lambda res: self.cbCorsPermission(to, site_addresses)
-        )
-
-    def cbCorsPermission(self, to, addresses):
-        # Add permissions
-        for address in addresses:
-            permission = "Cors:" + address
-            if permission not in self.site.settings["permissions"]:
-                self.site.settings["permissions"].append(permission)
-
-        self.site.saveSettings()
-        self.site.updateWebsocket(permission_added=permission)
-
-        self.response(to, "ok")
-
-        for address in addresses:
-            site = self.server.sites.get(address)
-            if not site:
-                gevent.spawn(self.server.site_manager.need, address)
-
-
-@PluginManager.registerTo("UiRequest")
-class UiRequestPlugin(object):
-    # Allow to load cross origin files using /cors-address/file.jpg
-    def parsePath(self, path):
-        path_parts = super(UiRequestPlugin, self).parsePath(path)
-        if "cors-" not in path:  # Optimization
-            return path_parts
-        site = self.server.sites[path_parts["address"]]
-        try:
-            path_parts["address"], path_parts["inner_path"] = getCorsPath(site, path_parts["inner_path"])
-        except Exception:
-            return None
-        return path_parts
diff --git a/plugins/Cors/__init__.py b/plugins/Cors/__init__.py
deleted file mode 100644
index bcaa502b..00000000
--- a/plugins/Cors/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import CorsPlugin
\ No newline at end of file
diff --git a/plugins/Cors/plugin_info.json b/plugins/Cors/plugin_info.json
deleted file mode 100644
index f8af18fa..00000000
--- a/plugins/Cors/plugin_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"name": "Cors",
-	"description": "Cross site resource read.",
-	"default": "enabled"
-}
\ No newline at end of file
diff --git a/plugins/CryptMessage/CryptMessage.py b/plugins/CryptMessage/CryptMessage.py
deleted file mode 100644
index 8349809c..00000000
--- a/plugins/CryptMessage/CryptMessage.py
+++ /dev/null
@@ -1,58 +0,0 @@
-import hashlib
-import base64
-import struct
-from lib import sslcrypto
-from Crypt import Crypt
-
-
-curve = sslcrypto.ecc.get_curve("secp256k1")
-
-
-def eciesEncrypt(data, pubkey, ciphername="aes-256-cbc"):
-    ciphertext, key_e = curve.encrypt(
-        data,
-        base64.b64decode(pubkey),
-        algo=ciphername,
-        derivation="sha512",
-        return_aes_key=True
-    )
-    return key_e, ciphertext
-
-
-@Crypt.thread_pool_crypt.wrap
-def eciesDecryptMulti(encrypted_datas, privatekey):
-    texts = []  # Decoded texts
-    for encrypted_data in encrypted_datas:
-        try:
-            text = eciesDecrypt(encrypted_data, privatekey).decode("utf8")
-            texts.append(text)
-        except Exception:
-            texts.append(None)
-    return texts
-
-
-def eciesDecrypt(ciphertext, privatekey):
-    return curve.decrypt(base64.b64decode(ciphertext), curve.wif_to_private(privatekey.encode()), derivation="sha512")
-
-
-def decodePubkey(pubkey):
-    i = 0
-    curve = struct.unpack('!H', pubkey[i:i + 2])[0]
-    i += 2
-    tmplen = struct.unpack('!H', pubkey[i:i + 2])[0]
-    i += 2
-    pubkey_x = pubkey[i:i + tmplen]
-    i += tmplen
-    tmplen = struct.unpack('!H', pubkey[i:i + 2])[0]
-    i += 2
-    pubkey_y = pubkey[i:i + tmplen]
-    i += tmplen
-    return curve, pubkey_x, pubkey_y, i
-
-
-def split(encrypted):
-    iv = encrypted[0:16]
-    curve, pubkey_x, pubkey_y, i = decodePubkey(encrypted[16:])
-    ciphertext = encrypted[16 + i:-32]
-
-    return iv, ciphertext
diff --git a/plugins/CryptMessage/CryptMessagePlugin.py b/plugins/CryptMessage/CryptMessagePlugin.py
deleted file mode 100644
index 7c24f730..00000000
--- a/plugins/CryptMessage/CryptMessagePlugin.py
+++ /dev/null
@@ -1,225 +0,0 @@
-import base64
-import os
-
-import gevent
-
-from Plugin import PluginManager
-from Crypt import CryptBitcoin, CryptHash
-from Config import config
-import sslcrypto
-
-from . import CryptMessage
-
-curve = sslcrypto.ecc.get_curve("secp256k1")
-
-
-@PluginManager.registerTo("UiWebsocket")
-class UiWebsocketPlugin(object):
-    # - Actions -
-
-    # Returns user's public key unique to site
-    # Return: Public key
-    def actionUserPublickey(self, to, index=0):
-        self.response(to, self.user.getEncryptPublickey(self.site.address, index))
-
-    # Encrypt a text using the publickey or user's sites unique publickey
-    # Return: Encrypted text using base64 encoding
-    def actionEciesEncrypt(self, to, text, publickey=0, return_aes_key=False):
-        if type(publickey) is int:  # Encrypt using user's publickey
-            publickey = self.user.getEncryptPublickey(self.site.address, publickey)
-        aes_key, encrypted = CryptMessage.eciesEncrypt(text.encode("utf8"), publickey)
-        if return_aes_key:
-            self.response(to, [base64.b64encode(encrypted).decode("utf8"), base64.b64encode(aes_key).decode("utf8")])
-        else:
-            self.response(to, base64.b64encode(encrypted).decode("utf8"))
-
-    # Decrypt a text using privatekey or the user's site unique private key
-    # Return: Decrypted text or list of decrypted texts
-    def actionEciesDecrypt(self, to, param, privatekey=0):
-        if type(privatekey) is int:  # Decrypt using user's privatekey
-            privatekey = self.user.getEncryptPrivatekey(self.site.address, privatekey)
-
-        if type(param) == list:
-            encrypted_texts = param
-        else:
-            encrypted_texts = [param]
-
-        texts = CryptMessage.eciesDecryptMulti(encrypted_texts, privatekey)
-
-        if type(param) == list:
-            self.response(to, texts)
-        else:
-            self.response(to, texts[0])
-
-    # Encrypt a text using AES
-    # Return: Iv, AES key, Encrypted text
-    def actionAesEncrypt(self, to, text, key=None):
-        if key:
-            key = base64.b64decode(key)
-        else:
-            key = sslcrypto.aes.new_key()
-
-        if text:
-            encrypted, iv = sslcrypto.aes.encrypt(text.encode("utf8"), key)
-        else:
-            encrypted, iv = b"", b""
-
-        res = [base64.b64encode(item).decode("utf8") for item in [key, iv, encrypted]]
-        self.response(to, res)
-
-    # Decrypt a text using AES
-    # Return: Decrypted text
-    def actionAesDecrypt(self, to, *args):
-        if len(args) == 3:  # Single decrypt
-            encrypted_texts = [(args[0], args[1])]
-            keys = [args[2]]
-        else:  # Batch decrypt
-            encrypted_texts, keys = args
-
-        texts = []  # Decoded texts
-        for iv, encrypted_text in encrypted_texts:
-            encrypted_text = base64.b64decode(encrypted_text)
-            iv = base64.b64decode(iv)
-            text = None
-            for key in keys:
-                try:
-                    decrypted = sslcrypto.aes.decrypt(encrypted_text, iv, base64.b64decode(key))
-                    if decrypted and decrypted.decode("utf8"):  # Valid text decoded
-                        text = decrypted.decode("utf8")
-                except Exception as err:
-                    pass
-            texts.append(text)
-
-        if len(args) == 3:
-            self.response(to, texts[0])
-        else:
-            self.response(to, texts)
-
-    # Sign data using ECDSA
-    # Return: Signature
-    def actionEcdsaSign(self, to, data, privatekey=None):
-        if privatekey is None:  # Sign using user's privatekey
-            privatekey = self.user.getAuthPrivatekey(self.site.address)
-
-        self.response(to, CryptBitcoin.sign(data, privatekey))
-
-    # Verify data using ECDSA (address is either a address or array of addresses)
-    # Return: bool
-    def actionEcdsaVerify(self, to, data, address, signature):
-        self.response(to, CryptBitcoin.verify(data, address, signature))
-
-    # Gets the publickey of a given privatekey
-    def actionEccPrivToPub(self, to, privatekey):
-        self.response(to, curve.private_to_public(curve.wif_to_private(privatekey.encode())))
-
-    # Gets the address of a given publickey
-    def actionEccPubToAddr(self, to, publickey):
-        self.response(to, curve.public_to_address(bytes.fromhex(publickey)))
-
-
-@PluginManager.registerTo("User")
-class UserPlugin(object):
-    def getEncryptPrivatekey(self, address, param_index=0):
-        if param_index < 0 or param_index > 1000:
-            raise Exception("Param_index out of range")
-
-        site_data = self.getSiteData(address)
-
-        if site_data.get("cert"):  # Different privatekey for different cert provider
-            index = param_index + self.getAddressAuthIndex(site_data["cert"])
-        else:
-            index = param_index
-
-        if "encrypt_privatekey_%s" % index not in site_data:
-            address_index = self.getAddressAuthIndex(address)
-            crypt_index = address_index + 1000 + index
-            site_data["encrypt_privatekey_%s" % index] = CryptBitcoin.hdPrivatekey(self.master_seed, crypt_index)
-            self.log.debug("New encrypt privatekey generated for %s:%s" % (address, index))
-        return site_data["encrypt_privatekey_%s" % index]
-
-    def getEncryptPublickey(self, address, param_index=0):
-        if param_index < 0 or param_index > 1000:
-            raise Exception("Param_index out of range")
-
-        site_data = self.getSiteData(address)
-
-        if site_data.get("cert"):  # Different privatekey for different cert provider
-            index = param_index + self.getAddressAuthIndex(site_data["cert"])
-        else:
-            index = param_index
-
-        if "encrypt_publickey_%s" % index not in site_data:
-            privatekey = self.getEncryptPrivatekey(address, param_index).encode()
-            publickey = curve.private_to_public(curve.wif_to_private(privatekey) + b"\x01")
-            site_data["encrypt_publickey_%s" % index] = base64.b64encode(publickey).decode("utf8")
-        return site_data["encrypt_publickey_%s" % index]
-
-
-@PluginManager.registerTo("Actions")
-class ActionsPlugin:
-    publickey = "A3HatibU4S6eZfIQhVs2u7GLN5G9wXa9WwlkyYIfwYaj"
-    privatekey = "5JBiKFYBm94EUdbxtnuLi6cvNcPzcKymCUHBDf2B6aq19vvG3rL"
-    utf8_text = '\xc1rv\xedzt\xfbr\xf5t\xfck\xf6rf\xfar\xf3g\xe9p'
-
-    def getBenchmarkTests(self, online=False):
-        if hasattr(super(), "getBenchmarkTests"):
-            tests = super().getBenchmarkTests(online)
-        else:
-            tests = []
-
-        aes_key, encrypted = CryptMessage.eciesEncrypt(self.utf8_text.encode("utf8"), self.publickey)  # Warm-up
-        tests.extend([
-            {"func": self.testCryptEciesEncrypt, "kwargs": {}, "num": 100, "time_standard": 1.2},
-            {"func": self.testCryptEciesDecrypt, "kwargs": {}, "num": 500, "time_standard": 1.3},
-            {"func": self.testCryptEciesDecryptMulti, "kwargs": {}, "num": 5, "time_standard": 0.68},
-            {"func": self.testCryptAesEncrypt, "kwargs": {}, "num": 10000, "time_standard": 0.27},
-            {"func": self.testCryptAesDecrypt, "kwargs": {}, "num": 10000, "time_standard": 0.25}
-        ])
-        return tests
-
-    def testCryptEciesEncrypt(self, num_run=1):
-        for i in range(num_run):
-            aes_key, encrypted = CryptMessage.eciesEncrypt(self.utf8_text.encode("utf8"), self.publickey)
-            assert len(aes_key) == 32
-            yield "."
-
-    def testCryptEciesDecrypt(self, num_run=1):
-        aes_key, encrypted = CryptMessage.eciesEncrypt(self.utf8_text.encode("utf8"), self.publickey)
-        for i in range(num_run):
-            assert len(aes_key) == 32
-            decrypted = CryptMessage.eciesDecrypt(base64.b64encode(encrypted), self.privatekey)
-            assert decrypted == self.utf8_text.encode("utf8"), "%s != %s" % (decrypted, self.utf8_text.encode("utf8"))
-            yield "."
-
-    def testCryptEciesDecryptMulti(self, num_run=1):
-        yield "x 100 (%s threads) " % config.threads_crypt
-        aes_key, encrypted = CryptMessage.eciesEncrypt(self.utf8_text.encode("utf8"), self.publickey)
-
-        threads = []
-        for i in range(num_run):
-            assert len(aes_key) == 32
-            threads.append(gevent.spawn(
-                CryptMessage.eciesDecryptMulti, [base64.b64encode(encrypted)] * 100, self.privatekey
-            ))
-
-        for thread in threads:
-            res = thread.get()
-            assert res[0] == self.utf8_text, "%s != %s" % (res[0], self.utf8_text)
-            assert res[0] == res[-1], "%s != %s" % (res[0], res[-1])
-            yield "."
-        gevent.joinall(threads)
-
-    def testCryptAesEncrypt(self, num_run=1):
-        for i in range(num_run):
-            key = os.urandom(32)
-            encrypted = sslcrypto.aes.encrypt(self.utf8_text.encode("utf8"), key)
-            yield "."
-
-    def testCryptAesDecrypt(self, num_run=1):
-        key = os.urandom(32)
-        encrypted_text, iv = sslcrypto.aes.encrypt(self.utf8_text.encode("utf8"), key)
-
-        for i in range(num_run):
-            decrypted = sslcrypto.aes.decrypt(encrypted_text, iv, key).decode("utf8")
-            assert decrypted == self.utf8_text
-            yield "."
diff --git a/plugins/CryptMessage/Test/TestCrypt.py b/plugins/CryptMessage/Test/TestCrypt.py
deleted file mode 100644
index 25a077d8..00000000
--- a/plugins/CryptMessage/Test/TestCrypt.py
+++ /dev/null
@@ -1,136 +0,0 @@
-import pytest
-import base64
-from CryptMessage import CryptMessage
-
-
-@pytest.mark.usefixtures("resetSettings")
-class TestCrypt:
-    publickey = "A3HatibU4S6eZfIQhVs2u7GLN5G9wXa9WwlkyYIfwYaj"
-    privatekey = "5JBiKFYBm94EUdbxtnuLi6cvNcPzcKymCUHBDf2B6aq19vvG3rL"
-    utf8_text = '\xc1rv\xedzt\xfbr\xf5t\xfck\xf6rf\xfar\xf3g\xe9'
-    ecies_encrypted_text = "R5J1RFIDOzE5bnWopvccmALKACCk/CRcd/KSE9OgExJKASyMbZ57JVSUenL2TpABMmcT+wAgr2UrOqClxpOWvIUwvwwupXnMbRTzthhIJJrTRW3sCJVaYlGEMn9DAcvbflgEkQX/MVVdLV3tWKySs1Vk8sJC/y+4pGYCrZz7vwDNEEERaqU="
-
-    @pytest.mark.parametrize("text", [b"hello", '\xc1rv\xedzt\xfbr\xf5t\xfck\xf6rf\xfar\xf3g\xe9'.encode("utf8")])
-    @pytest.mark.parametrize("text_repeat", [1, 10, 128, 1024])
-    def testEncryptEcies(self, text, text_repeat):
-        text_repeated = text * text_repeat
-        aes_key, encrypted = CryptMessage.eciesEncrypt(text_repeated, self.publickey)
-        assert len(aes_key) == 32
-        # assert len(encrypted) == 134 + int(len(text) / 16) * 16  # Not always true
-
-        assert CryptMessage.eciesDecrypt(base64.b64encode(encrypted), self.privatekey) == text_repeated
-
-    def testDecryptEcies(self, user):
-        assert CryptMessage.eciesDecrypt(self.ecies_encrypted_text, self.privatekey) == b"hello"
-
-    def testPublickey(self, ui_websocket):
-        pub = ui_websocket.testAction("UserPublickey", 0)
-        assert len(pub) == 44  # Compressed, b64 encoded publickey
-
-        # Different pubkey for specificed index
-        assert ui_websocket.testAction("UserPublickey", 1) != ui_websocket.testAction("UserPublickey", 0)
-
-        # Same publickey for same index
-        assert ui_websocket.testAction("UserPublickey", 2) == ui_websocket.testAction("UserPublickey", 2)
-
-        # Different publickey for different cert
-        site_data = ui_websocket.user.getSiteData(ui_websocket.site.address)
-        site_data["cert"] = None
-        pub1 = ui_websocket.testAction("UserPublickey", 0)
-
-        site_data = ui_websocket.user.getSiteData(ui_websocket.site.address)
-        site_data["cert"] = "zeroid.bit"
-        pub2 = ui_websocket.testAction("UserPublickey", 0)
-        assert pub1 != pub2
-
-    def testEcies(self, ui_websocket):
-        pub = ui_websocket.testAction("UserPublickey")
-
-        encrypted = ui_websocket.testAction("EciesEncrypt", "hello", pub)
-        assert len(encrypted) == 180
-
-        # Don't allow decrypt using other privatekey index
-        decrypted = ui_websocket.testAction("EciesDecrypt", encrypted, 123)
-        assert decrypted != "hello"
-
-        # Decrypt using correct privatekey
-        decrypted = ui_websocket.testAction("EciesDecrypt", encrypted)
-        assert decrypted == "hello"
-
-        # Decrypt incorrect text
-        decrypted = ui_websocket.testAction("EciesDecrypt", "baad")
-        assert decrypted is None
-
-        # Decrypt batch
-        decrypted = ui_websocket.testAction("EciesDecrypt", [encrypted, "baad", encrypted])
-        assert decrypted == ["hello", None, "hello"]
-
-    def testEciesUtf8(self, ui_websocket):
-        # Utf8 test
-        ui_websocket.actionEciesEncrypt(0, self.utf8_text)
-        encrypted = ui_websocket.ws.getResult()
-
-        ui_websocket.actionEciesDecrypt(0, encrypted)
-        assert ui_websocket.ws.getResult() == self.utf8_text
-
-    def testEciesAes(self, ui_websocket):
-        ui_websocket.actionEciesEncrypt(0, "hello", return_aes_key=True)
-        ecies_encrypted, aes_key = ui_websocket.ws.getResult()
-
-        # Decrypt using Ecies
-        ui_websocket.actionEciesDecrypt(0, ecies_encrypted)
-        assert ui_websocket.ws.getResult() == "hello"
-
-        # Decrypt using AES
-        aes_iv, aes_encrypted = CryptMessage.split(base64.b64decode(ecies_encrypted))
-
-        ui_websocket.actionAesDecrypt(0, base64.b64encode(aes_iv), base64.b64encode(aes_encrypted), aes_key)
-        assert ui_websocket.ws.getResult() == "hello"
-
-    def testEciesAesLongpubkey(self, ui_websocket):
-        privatekey = "5HwVS1bTFnveNk9EeGaRenWS1QFzLFb5kuncNbiY3RiHZrVR6ok"
-
-        ecies_encrypted, aes_key = ["lWiXfEikIjw1ac3J/RaY/gLKACALRUfksc9rXYRFyKDSaxhwcSFBYCgAdIyYlY294g/6VgAf/68PYBVMD3xKH1n7Zbo+ge8b4i/XTKmCZRJvy0eutMKWckYCMVcxgIYNa/ZL1BY1kvvH7omgzg1wBraoLfdbNmVtQgdAZ9XS8PwRy6OB2Q==", "Rvlf7zsMuBFHZIGHcbT1rb4If+YTmsWDv6kGwcvSeMM="]
-
-        # Decrypt using Ecies
-        ui_websocket.actionEciesDecrypt(0, ecies_encrypted, privatekey)
-        assert ui_websocket.ws.getResult() == "hello"
-
-        # Decrypt using AES
-        aes_iv, aes_encrypted = CryptMessage.split(base64.b64decode(ecies_encrypted))
-
-        ui_websocket.actionAesDecrypt(0, base64.b64encode(aes_iv), base64.b64encode(aes_encrypted), aes_key)
-        assert ui_websocket.ws.getResult() == "hello"
-
-    def testAes(self, ui_websocket):
-        ui_websocket.actionAesEncrypt(0, "hello")
-        key, iv, encrypted = ui_websocket.ws.getResult()
-
-        assert len(key) == 44
-        assert len(iv) == 24
-        assert len(encrypted) == 24
-
-        # Single decrypt
-        ui_websocket.actionAesDecrypt(0, iv, encrypted, key)
-        assert ui_websocket.ws.getResult() == "hello"
-
-        # Batch decrypt
-        ui_websocket.actionAesEncrypt(0, "hello")
-        key2, iv2, encrypted2 = ui_websocket.ws.getResult()
-
-        assert [key, iv, encrypted] != [key2, iv2, encrypted2]
-
-        # 2 correct key
-        ui_websocket.actionAesDecrypt(0, [[iv, encrypted], [iv, encrypted], [iv, "baad"], [iv2, encrypted2]], [key])
-        assert ui_websocket.ws.getResult() == ["hello", "hello", None, None]
-
-        # 3 key
-        ui_websocket.actionAesDecrypt(0, [[iv, encrypted], [iv, encrypted], [iv, "baad"], [iv2, encrypted2]], [key, key2])
-        assert ui_websocket.ws.getResult() == ["hello", "hello", None, "hello"]
-
-    def testAesUtf8(self, ui_websocket):
-        ui_websocket.actionAesEncrypt(0, self.utf8_text)
-        key, iv, encrypted = ui_websocket.ws.getResult()
-
-        ui_websocket.actionAesDecrypt(0, iv, encrypted, key)
-        assert ui_websocket.ws.getResult() == self.utf8_text
diff --git a/plugins/CryptMessage/Test/conftest.py b/plugins/CryptMessage/Test/conftest.py
deleted file mode 100644
index 8c1df5b2..00000000
--- a/plugins/CryptMessage/Test/conftest.py
+++ /dev/null
@@ -1 +0,0 @@
-from src.Test.conftest import *
\ No newline at end of file
diff --git a/plugins/CryptMessage/Test/pytest.ini b/plugins/CryptMessage/Test/pytest.ini
deleted file mode 100644
index d09210d1..00000000
--- a/plugins/CryptMessage/Test/pytest.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[pytest]
-python_files = Test*.py
-addopts = -rsxX -v --durations=6
-markers =
-    webtest: mark a test as a webtest.
\ No newline at end of file
diff --git a/plugins/CryptMessage/__init__.py b/plugins/CryptMessage/__init__.py
deleted file mode 100644
index 6aeb4e52..00000000
--- a/plugins/CryptMessage/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import CryptMessagePlugin
\ No newline at end of file
diff --git a/plugins/CryptMessage/plugin_info.json b/plugins/CryptMessage/plugin_info.json
deleted file mode 100644
index 96dfdd89..00000000
--- a/plugins/CryptMessage/plugin_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"name": "CryptMessage",
-	"description": "Cryptographic functions of ECIES and AES data encryption/decryption.",
-	"default": "enabled"
-}
\ No newline at end of file
diff --git a/plugins/FilePack/FilePackPlugin.py b/plugins/FilePack/FilePackPlugin.py
deleted file mode 100644
index a095c6d4..00000000
--- a/plugins/FilePack/FilePackPlugin.py
+++ /dev/null
@@ -1,193 +0,0 @@
-import os
-import re
-
-import gevent
-
-from Plugin import PluginManager
-from Config import config
-from Debug import Debug
-
-# Keep archive open for faster reponse times for large sites
-archive_cache = {}
-
-
-def closeArchive(archive_path):
-    if archive_path in archive_cache:
-        del archive_cache[archive_path]
-
-
-def openArchive(archive_path, file_obj=None):
-    if archive_path not in archive_cache:
-        if archive_path.endswith("tar.gz"):
-            import tarfile
-            archive_cache[archive_path] = tarfile.open(archive_path, fileobj=file_obj, mode="r:gz")
-        else:
-            import zipfile
-            archive_cache[archive_path] = zipfile.ZipFile(file_obj or archive_path)
-        gevent.spawn_later(5, lambda: closeArchive(archive_path))  # Close after 5 sec
-
-    archive = archive_cache[archive_path]
-    return archive
-
-
-def openArchiveFile(archive_path, path_within, file_obj=None):
-    archive = openArchive(archive_path, file_obj=file_obj)
-    if archive_path.endswith(".zip"):
-        return archive.open(path_within)
-    else:
-        return archive.extractfile(path_within)
-
-
-@PluginManager.registerTo("UiRequest")
-class UiRequestPlugin(object):
-    def actionSiteMedia(self, path, **kwargs):
-        if ".zip/" in path or ".tar.gz/" in path:
-            file_obj = None
-            path_parts = self.parsePath(path)
-            file_path = "%s/%s/%s" % (config.data_dir, path_parts["address"], path_parts["inner_path"])
-            match = re.match("^(.*\.(?:tar.gz|zip))/(.*)", file_path)
-            archive_path, path_within = match.groups()
-            if archive_path not in archive_cache:
-                site = self.server.site_manager.get(path_parts["address"])
-                if not site:
-                    return self.actionSiteAddPrompt(path)
-                archive_inner_path = site.storage.getInnerPath(archive_path)
-                if not os.path.isfile(archive_path):
-                    # Wait until file downloads
-                    result = site.needFile(archive_inner_path, priority=10)
-                    # Send virutal file path download finished event to remove loading screen
-                    site.updateWebsocket(file_done=archive_inner_path)
-                    if not result:
-                        return self.error404(archive_inner_path)
-                file_obj = site.storage.openBigfile(archive_inner_path)
-                if file_obj == False:
-                    file_obj = None
-
-            header_allow_ajax = False
-            if self.get.get("ajax_key"):
-                requester_site = self.server.site_manager.get(path_parts["request_address"])
-                if self.get["ajax_key"] == requester_site.settings["ajax_key"]:
-                    header_allow_ajax = True
-                else:
-                    return self.error403("Invalid ajax_key")
-
-            try:
-                file = openArchiveFile(archive_path, path_within, file_obj=file_obj)
-                content_type = self.getContentType(file_path)
-                self.sendHeader(200, content_type=content_type, noscript=kwargs.get("header_noscript", False), allow_ajax=header_allow_ajax)
-                return self.streamFile(file)
-            except Exception as err:
-                self.log.debug("Error opening archive file: %s" % Debug.formatException(err))
-                return self.error404(path)
-
-        return super(UiRequestPlugin, self).actionSiteMedia(path, **kwargs)
-
-    def streamFile(self, file):
-        for i in range(100):  # Read max 6MB
-            try:
-                block = file.read(60 * 1024)
-                if block:
-                    yield block
-                else:
-                    raise StopIteration
-            except StopIteration:
-                file.close()
-                break
-
-
-@PluginManager.registerTo("SiteStorage")
-class SiteStoragePlugin(object):
-    def isFile(self, inner_path):
-        if ".zip/" in inner_path or ".tar.gz/" in inner_path:
-            match = re.match("^(.*\.(?:tar.gz|zip))/(.*)", inner_path)
-            archive_inner_path, path_within = match.groups()
-            return super(SiteStoragePlugin, self).isFile(archive_inner_path)
-        else:
-            return super(SiteStoragePlugin, self).isFile(inner_path)
-
-    def openArchive(self, inner_path):
-        archive_path = self.getPath(inner_path)
-        file_obj = None
-        if archive_path not in archive_cache:
-            if not os.path.isfile(archive_path):
-                result = self.site.needFile(inner_path, priority=10)
-                self.site.updateWebsocket(file_done=inner_path)
-                if not result:
-                    raise Exception("Unable to download file")
-            file_obj = self.site.storage.openBigfile(inner_path)
-            if file_obj == False:
-                file_obj = None
-
-        try:
-            archive = openArchive(archive_path, file_obj=file_obj)
-        except Exception as err:
-            raise Exception("Unable to download file: %s" % Debug.formatException(err))
-
-        return archive
-
-    def walk(self, inner_path, *args, **kwags):
-        if ".zip" in inner_path or ".tar.gz" in inner_path:
-            match = re.match("^(.*\.(?:tar.gz|zip))(.*)", inner_path)
-            archive_inner_path, path_within = match.groups()
-            archive = self.openArchive(archive_inner_path)
-            path_within = path_within.lstrip("/")
-
-            if archive_inner_path.endswith(".zip"):
-                namelist = [name for name in archive.namelist() if not name.endswith("/")]
-            else:
-                namelist = [item.name for item in archive.getmembers() if not item.isdir()]
-
-            namelist_relative = []
-            for name in namelist:
-                if not name.startswith(path_within):
-                    continue
-                name_relative = name.replace(path_within, "", 1).rstrip("/")
-                namelist_relative.append(name_relative)
-
-            return namelist_relative
-
-        else:
-            return super(SiteStoragePlugin, self).walk(inner_path, *args, **kwags)
-
-    def list(self, inner_path, *args, **kwags):
-        if ".zip" in inner_path or ".tar.gz" in inner_path:
-            match = re.match("^(.*\.(?:tar.gz|zip))(.*)", inner_path)
-            archive_inner_path, path_within = match.groups()
-            archive = self.openArchive(archive_inner_path)
-            path_within = path_within.lstrip("/")
-
-            if archive_inner_path.endswith(".zip"):
-                namelist = [name for name in archive.namelist()]
-            else:
-                namelist = [item.name for item in archive.getmembers()]
-
-            namelist_relative = []
-            for name in namelist:
-                if not name.startswith(path_within):
-                    continue
-                name_relative = name.replace(path_within, "", 1).rstrip("/")
-
-                if "/" in name_relative:  # File is in sub-directory
-                    continue
-
-                namelist_relative.append(name_relative)
-            return namelist_relative
-
-        else:
-            return super(SiteStoragePlugin, self).list(inner_path, *args, **kwags)
-
-    def read(self, inner_path, mode="rb", **kwargs):
-        if ".zip/" in inner_path or ".tar.gz/" in inner_path:
-            match = re.match("^(.*\.(?:tar.gz|zip))(.*)", inner_path)
-            archive_inner_path, path_within = match.groups()
-            archive = self.openArchive(archive_inner_path)
-            path_within = path_within.lstrip("/")
-
-            if archive_inner_path.endswith(".zip"):
-                return archive.open(path_within).read()
-            else:
-                return archive.extractfile(path_within).read()
-
-        else:
-            return super(SiteStoragePlugin, self).read(inner_path, mode, **kwargs)
-
diff --git a/plugins/FilePack/__init__.py b/plugins/FilePack/__init__.py
deleted file mode 100644
index 660a0920..00000000
--- a/plugins/FilePack/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import FilePackPlugin
\ No newline at end of file
diff --git a/plugins/FilePack/plugin_info.json b/plugins/FilePack/plugin_info.json
deleted file mode 100644
index 42112f95..00000000
--- a/plugins/FilePack/plugin_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"name": "FilePack",
-	"description": "Transparent web access for Zip and Tar.gz files.",
-	"default": "enabled"
-}
\ No newline at end of file
diff --git a/plugins/MergerSite/MergerSitePlugin.py b/plugins/MergerSite/MergerSitePlugin.py
deleted file mode 100644
index 2dccc6de..00000000
--- a/plugins/MergerSite/MergerSitePlugin.py
+++ /dev/null
@@ -1,399 +0,0 @@
-import re
-import time
-import copy
-import os
-
-from Plugin import PluginManager
-from Translate import Translate
-from util import RateLimit
-from util import helper
-from util.Flag import flag
-from Debug import Debug
-try:
-    import OptionalManager.UiWebsocketPlugin  # To make optioanlFileInfo merger sites compatible
-except Exception:
-    pass
-
-if "merger_db" not in locals().keys():  # To keep merger_sites between module reloads
-    merger_db = {}  # Sites that allowed to list other sites {address: [type1, type2...]}
-    merged_db = {}  # Sites that allowed to be merged to other sites {address: type, ...}
-    merged_to_merger = {}  # {address: [site1, site2, ...]} cache
-    site_manager = None  # Site manager for merger sites
-
-
-plugin_dir = os.path.dirname(__file__)
-
-if "_" not in locals():
-    _ = Translate(plugin_dir + "/languages/")
-
-
-# Check if the site has permission to this merger site
-def checkMergerPath(address, inner_path):
-    merged_match = re.match("^merged-(.*?)/([A-Za-z0-9]{26,35})/", inner_path)
-    if merged_match:
-        merger_type = merged_match.group(1)
-        # Check if merged site is allowed to include other sites
-        if merger_type in merger_db.get(address, []):
-            # Check if included site allows to include
-            merged_address = merged_match.group(2)
-            if merged_db.get(merged_address) == merger_type:
-                inner_path = re.sub("^merged-(.*?)/([A-Za-z0-9]{26,35})/", "", inner_path)
-                return merged_address, inner_path
-            else:
-                raise Exception(
-                    "Merger site (%s) does not have permission for merged site: %s (%s)" %
-                    (merger_type, merged_address, merged_db.get(merged_address))
-                )
-        else:
-            raise Exception("No merger (%s) permission to load: <br>%s (%s not in %s)" % (
-                address, inner_path, merger_type, merger_db.get(address, []))
-            )
-    else:
-        raise Exception("Invalid merger path: %s" % inner_path)
-
-
-@PluginManager.registerTo("UiWebsocket")
-class UiWebsocketPlugin(object):
-    # Download new site
-    def actionMergerSiteAdd(self, to, addresses):
-        if type(addresses) != list:
-            # Single site add
-            addresses = [addresses]
-        # Check if the site has merger permission
-        merger_types = merger_db.get(self.site.address)
-        if not merger_types:
-            return self.response(to, {"error": "Not a merger site"})
-
-        if RateLimit.isAllowed(self.site.address + "-MergerSiteAdd", 10) and len(addresses) == 1:
-            # Without confirmation if only one site address and not called in last 10 sec
-            self.cbMergerSiteAdd(to, addresses)
-        else:
-            self.cmd(
-                "confirm",
-                [_["Add <b>%s</b> new site?"] % len(addresses), "Add"],
-                lambda res: self.cbMergerSiteAdd(to, addresses)
-            )
-        self.response(to, "ok")
-
-    # Callback of adding new site confirmation
-    def cbMergerSiteAdd(self, to, addresses):
-        added = 0
-        for address in addresses:
-            try:
-                site_manager.need(address)
-                added += 1
-            except Exception as err:
-                self.cmd("notification", ["error", _["Adding <b>%s</b> failed: %s"] % (address, err)])
-        if added:
-            self.cmd("notification", ["done", _["Added <b>%s</b> new site"] % added, 5000])
-        RateLimit.called(self.site.address + "-MergerSiteAdd")
-        site_manager.updateMergerSites()
-
-    # Delete a merged site
-    @flag.no_multiuser
-    def actionMergerSiteDelete(self, to, address):
-        site = self.server.sites.get(address)
-        if not site:
-            return self.response(to, {"error": "No site found: %s" % address})
-
-        merger_types = merger_db.get(self.site.address)
-        if not merger_types:
-            return self.response(to, {"error": "Not a merger site"})
-        if merged_db.get(address) not in merger_types:
-            return self.response(to, {"error": "Merged type (%s) not in %s" % (merged_db.get(address), merger_types)})
-
-        self.cmd("notification", ["done", _["Site deleted: <b>%s</b>"] % address, 5000])
-        self.response(to, "ok")
-
-    # Lists merged sites
-    def actionMergerSiteList(self, to, query_site_info=False):
-        merger_types = merger_db.get(self.site.address)
-        ret = {}
-        if not merger_types:
-            return self.response(to, {"error": "Not a merger site"})
-        for address, merged_type in merged_db.items():
-            if merged_type not in merger_types:
-                continue  # Site not for us
-            if query_site_info:
-                site = self.server.sites.get(address)
-                ret[address] = self.formatSiteInfo(site, create_user=False)
-            else:
-                ret[address] = merged_type
-        self.response(to, ret)
-
-    def hasSitePermission(self, address, *args, **kwargs):
-        if super(UiWebsocketPlugin, self).hasSitePermission(address, *args, **kwargs):
-            return True
-        else:
-            if self.site.address in [merger_site.address for merger_site in merged_to_merger.get(address, [])]:
-                return True
-            else:
-                return False
-
-    # Add support merger sites for file commands
-    def mergerFuncWrapper(self, func_name, to, inner_path, *args, **kwargs):
-        if inner_path.startswith("merged-"):
-            merged_address, merged_inner_path = checkMergerPath(self.site.address, inner_path)
-
-            # Set the same cert for merged site
-            merger_cert = self.user.getSiteData(self.site.address).get("cert")
-            if merger_cert and self.user.getSiteData(merged_address).get("cert") != merger_cert:
-                self.user.setCert(merged_address, merger_cert)
-
-            req_self = copy.copy(self)
-            req_self.site = self.server.sites.get(merged_address)  # Change the site to the merged one
-
-            func = getattr(super(UiWebsocketPlugin, req_self), func_name)
-            return func(to, merged_inner_path, *args, **kwargs)
-        else:
-            func = getattr(super(UiWebsocketPlugin, self), func_name)
-            return func(to, inner_path, *args, **kwargs)
-
-    def actionFileList(self, to, inner_path, *args, **kwargs):
-        return self.mergerFuncWrapper("actionFileList", to, inner_path, *args, **kwargs)
-
-    def actionDirList(self, to, inner_path, *args, **kwargs):
-        return self.mergerFuncWrapper("actionDirList", to, inner_path, *args, **kwargs)
-
-    def actionFileGet(self, to, inner_path, *args, **kwargs):
-        return self.mergerFuncWrapper("actionFileGet", to, inner_path, *args, **kwargs)
-
-    def actionFileWrite(self, to, inner_path, *args, **kwargs):
-        return self.mergerFuncWrapper("actionFileWrite", to, inner_path, *args, **kwargs)
-
-    def actionFileDelete(self, to, inner_path, *args, **kwargs):
-        return self.mergerFuncWrapper("actionFileDelete", to, inner_path, *args, **kwargs)
-
-    def actionFileRules(self, to, inner_path, *args, **kwargs):
-        return self.mergerFuncWrapper("actionFileRules", to, inner_path, *args, **kwargs)
-
-    def actionFileNeed(self, to, inner_path, *args, **kwargs):
-        return self.mergerFuncWrapper("actionFileNeed", to, inner_path, *args, **kwargs)
-
-    def actionOptionalFileInfo(self, to, inner_path, *args, **kwargs):
-        return self.mergerFuncWrapper("actionOptionalFileInfo", to, inner_path, *args, **kwargs)
-
-    def actionOptionalFileDelete(self, to, inner_path, *args, **kwargs):
-        return self.mergerFuncWrapper("actionOptionalFileDelete", to, inner_path, *args, **kwargs)
-
-    def actionBigfileUploadInit(self, to, inner_path, *args, **kwargs):
-        back = self.mergerFuncWrapper("actionBigfileUploadInit", to, inner_path, *args, **kwargs)
-        if inner_path.startswith("merged-"):
-            merged_address, merged_inner_path = checkMergerPath(self.site.address, inner_path)
-            back["inner_path"] = "merged-%s/%s/%s" % (merged_db[merged_address], merged_address, back["inner_path"])
-        return back
-
-    # Add support merger sites for file commands with privatekey parameter
-    def mergerFuncWrapperWithPrivatekey(self, func_name, to, privatekey, inner_path, *args, **kwargs):
-        func = getattr(super(UiWebsocketPlugin, self), func_name)
-        if inner_path.startswith("merged-"):
-            merged_address, merged_inner_path = checkMergerPath(self.site.address, inner_path)
-            merged_site = self.server.sites.get(merged_address)
-
-            # Set the same cert for merged site
-            merger_cert = self.user.getSiteData(self.site.address).get("cert")
-            if merger_cert:
-                self.user.setCert(merged_address, merger_cert)
-
-            site_before = self.site  # Save to be able to change it back after we ran the command
-            self.site = merged_site  # Change the site to the merged one
-            try:
-                back = func(to, privatekey, merged_inner_path, *args, **kwargs)
-            finally:
-                self.site = site_before  # Change back to original site
-            return back
-        else:
-            return func(to, privatekey, inner_path, *args, **kwargs)
-
-    def actionSiteSign(self, to, privatekey=None, inner_path="content.json", *args, **kwargs):
-        return self.mergerFuncWrapperWithPrivatekey("actionSiteSign", to, privatekey, inner_path, *args, **kwargs)
-
-    def actionSitePublish(self, to, privatekey=None, inner_path="content.json", *args, **kwargs):
-        return self.mergerFuncWrapperWithPrivatekey("actionSitePublish", to, privatekey, inner_path, *args, **kwargs)
-
-    def actionPermissionAdd(self, to, permission):
-        super(UiWebsocketPlugin, self).actionPermissionAdd(to, permission)
-        if permission.startswith("Merger"):
-            self.site.storage.rebuildDb()
-
-    def actionPermissionDetails(self, to, permission):
-        if not permission.startswith("Merger"):
-            return super(UiWebsocketPlugin, self).actionPermissionDetails(to, permission)
-
-        merger_type = permission.replace("Merger:", "")
-        if not re.match("^[A-Za-z0-9-]+$", merger_type):
-            raise Exception("Invalid merger_type: %s" % merger_type)
-        merged_sites = []
-        for address, merged_type in merged_db.items():
-            if merged_type != merger_type:
-                continue
-            site = self.server.sites.get(address)
-            try:
-                merged_sites.append(site.content_manager.contents.get("content.json").get("title", address))
-            except Exception:
-                merged_sites.append(address)
-
-        details = _["Read and write permissions to sites with merged type of <b>%s</b> "] % merger_type
-        details += _["(%s sites)"] % len(merged_sites)
-        details += "<div style='white-space: normal; max-width: 400px'>%s</div>" % ", ".join(merged_sites)
-        self.response(to, details)
-
-
-@PluginManager.registerTo("UiRequest")
-class UiRequestPlugin(object):
-    # Allow to load merged site files using /merged-ZeroMe/address/file.jpg
-    def parsePath(self, path):
-        path_parts = super(UiRequestPlugin, self).parsePath(path)
-        if "merged-" not in path:  # Optimization
-            return path_parts
-        path_parts["address"], path_parts["inner_path"] = checkMergerPath(path_parts["address"], path_parts["inner_path"])
-        return path_parts
-
-
-@PluginManager.registerTo("SiteStorage")
-class SiteStoragePlugin(object):
-    # Also rebuild from merged sites
-    def getDbFiles(self):
-        merger_types = merger_db.get(self.site.address)
-
-        # First return the site's own db files
-        for item in super(SiteStoragePlugin, self).getDbFiles():
-            yield item
-
-        # Not a merger site, that's all
-        if not merger_types:
-            return
-
-        merged_sites = [
-            site_manager.sites[address]
-            for address, merged_type in merged_db.items()
-            if merged_type in merger_types
-        ]
-        found = 0
-        for merged_site in merged_sites:
-            self.log.debug("Loading merged site: %s" % merged_site)
-            merged_type = merged_db[merged_site.address]
-            for content_inner_path, content in merged_site.content_manager.contents.items():
-                # content.json file itself
-                if merged_site.storage.isFile(content_inner_path):  # Missing content.json file
-                    merged_inner_path = "merged-%s/%s/%s" % (merged_type, merged_site.address, content_inner_path)
-                    yield merged_inner_path, merged_site.storage.getPath(content_inner_path)
-                else:
-                    merged_site.log.error("[MISSING] %s" % content_inner_path)
-                # Data files in content.json
-                content_inner_path_dir = helper.getDirname(content_inner_path)  # Content.json dir relative to site
-                for file_relative_path in list(content.get("files", {}).keys()) + list(content.get("files_optional", {}).keys()):
-                    if not file_relative_path.endswith(".json"):
-                        continue  # We only interesed in json files
-                    file_inner_path = content_inner_path_dir + file_relative_path  # File Relative to site dir
-                    file_inner_path = file_inner_path.strip("/")  # Strip leading /
-                    if merged_site.storage.isFile(file_inner_path):
-                        merged_inner_path = "merged-%s/%s/%s" % (merged_type, merged_site.address, file_inner_path)
-                        yield merged_inner_path, merged_site.storage.getPath(file_inner_path)
-                    else:
-                        merged_site.log.error("[MISSING] %s" % file_inner_path)
-                    found += 1
-                    if found % 100 == 0:
-                        time.sleep(0.001)  # Context switch to avoid UI block
-
-    # Also notice merger sites on a merged site file change
-    def onUpdated(self, inner_path, file=None):
-        super(SiteStoragePlugin, self).onUpdated(inner_path, file)
-
-        merged_type = merged_db.get(self.site.address)
-
-        for merger_site in merged_to_merger.get(self.site.address, []):
-            if merger_site.address == self.site.address:  # Avoid infinite loop
-                continue
-            virtual_path = "merged-%s/%s/%s" % (merged_type, self.site.address, inner_path)
-            if inner_path.endswith(".json"):
-                if file is not None:
-                    merger_site.storage.onUpdated(virtual_path, file=file)
-                else:
-                    merger_site.storage.onUpdated(virtual_path, file=self.open(inner_path))
-            else:
-                merger_site.storage.onUpdated(virtual_path)
-
-
-@PluginManager.registerTo("Site")
-class SitePlugin(object):
-    def fileDone(self, inner_path):
-        super(SitePlugin, self).fileDone(inner_path)
-
-        for merger_site in merged_to_merger.get(self.address, []):
-            if merger_site.address == self.address:
-                continue
-            for ws in merger_site.websockets:
-                ws.event("siteChanged", self, {"event": ["file_done", inner_path]})
-
-    def fileFailed(self, inner_path):
-        super(SitePlugin, self).fileFailed(inner_path)
-
-        for merger_site in merged_to_merger.get(self.address, []):
-            if merger_site.address == self.address:
-                continue
-            for ws in merger_site.websockets:
-                ws.event("siteChanged", self, {"event": ["file_failed", inner_path]})
-
-
-@PluginManager.registerTo("SiteManager")
-class SiteManagerPlugin(object):
-    # Update merger site for site types
-    def updateMergerSites(self):
-        global merger_db, merged_db, merged_to_merger, site_manager
-        s = time.time()
-        merger_db_new = {}
-        merged_db_new = {}
-        merged_to_merger_new = {}
-        site_manager = self
-        if not self.sites:
-            return
-        for site in self.sites.values():
-            # Update merged sites
-            try:
-                merged_type = site.content_manager.contents.get("content.json", {}).get("merged_type")
-            except Exception as err:
-                self.log.error("Error loading site %s: %s" % (site.address, Debug.formatException(err)))
-                continue
-            if merged_type:
-                merged_db_new[site.address] = merged_type
-
-            # Update merger sites
-            for permission in site.settings["permissions"]:
-                if not permission.startswith("Merger:"):
-                    continue
-                if merged_type:
-                    self.log.error(
-                        "Removing permission %s from %s: Merger and merged at the same time." %
-                        (permission, site.address)
-                    )
-                    site.settings["permissions"].remove(permission)
-                    continue
-                merger_type = permission.replace("Merger:", "")
-                if site.address not in merger_db_new:
-                    merger_db_new[site.address] = []
-                merger_db_new[site.address].append(merger_type)
-                site_manager.sites[site.address] = site
-
-            # Update merged to merger
-            if merged_type:
-                for merger_site in self.sites.values():
-                    if "Merger:" + merged_type in merger_site.settings["permissions"]:
-                        if site.address not in merged_to_merger_new:
-                            merged_to_merger_new[site.address] = []
-                        merged_to_merger_new[site.address].append(merger_site)
-
-        # Update globals
-        merger_db = merger_db_new
-        merged_db = merged_db_new
-        merged_to_merger = merged_to_merger_new
-
-        self.log.debug("Updated merger sites in %.3fs" % (time.time() - s))
-
-    def load(self, *args, **kwags):
-        super(SiteManagerPlugin, self).load(*args, **kwags)
-        self.updateMergerSites()
-
-    def saveDelayed(self, *args, **kwags):
-        super(SiteManagerPlugin, self).saveDelayed(*args, **kwags)
-        self.updateMergerSites()
diff --git a/plugins/MergerSite/__init__.py b/plugins/MergerSite/__init__.py
deleted file mode 100644
index 2cf54611..00000000
--- a/plugins/MergerSite/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import MergerSitePlugin
\ No newline at end of file
diff --git a/plugins/MergerSite/languages/es.json b/plugins/MergerSite/languages/es.json
deleted file mode 100644
index d554c3a9..00000000
--- a/plugins/MergerSite/languages/es.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"Add <b>%s</b> new site?": "¿Agregar <b>%s</b> nuevo sitio?",
-	"Added <b>%s</b> new site": "Sitio <b>%s</b> agregado",
-	"Site deleted: <b>%s</b>": "Sitio removido: <b>%s</b>"
-}
diff --git a/plugins/MergerSite/languages/fr.json b/plugins/MergerSite/languages/fr.json
deleted file mode 100644
index 9d59fde9..00000000
--- a/plugins/MergerSite/languages/fr.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"Add <b>%s</b> new site?": "Ajouter le site <b>%s</b> ?",
-	"Added <b>%s</b> new site": "Site <b>%s</b> ajouté",
-	"Site deleted: <b>%s</b>": "Site <b>%s</b> supprimé"
-}
diff --git a/plugins/MergerSite/languages/hu.json b/plugins/MergerSite/languages/hu.json
deleted file mode 100644
index 8e377aaa..00000000
--- a/plugins/MergerSite/languages/hu.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"Add <b>%s</b> new site?": "Új oldal hozzáadása: <b>%s</b>?",
-	"Added <b>%s</b> new site": "Új oldal hozzáadva: <b>%s</b>",
-	"Site deleted: <b>%s</b>": "Oldal törölve: <b>%s</b>"
-}
diff --git a/plugins/MergerSite/languages/it.json b/plugins/MergerSite/languages/it.json
deleted file mode 100644
index d56c9817..00000000
--- a/plugins/MergerSite/languages/it.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"Add <b>%s</b> new site?": "Aggiungere <b>%s</b> nuovo sito ?",
-	"Added <b>%s</b> new site": "Sito <b>%s</b> aggiunto",
-	"Site deleted: <b>%s</b>": "Sito <b>%s</b> eliminato"
-}
diff --git a/plugins/MergerSite/languages/jp.json b/plugins/MergerSite/languages/jp.json
deleted file mode 100644
index 7216f268..00000000
--- a/plugins/MergerSite/languages/jp.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"Add <b>%s</b> new site?": "サイト: <b>%s</b> を追加しますか?",
-	"Added <b>%s</b> new site": "サイト: <b>%s</b> を追加しました",
-	"Site deleted: <b>%s</b>": "サイト: <b>%s</b> を削除しました"
-}
diff --git a/plugins/MergerSite/languages/pt-br.json b/plugins/MergerSite/languages/pt-br.json
deleted file mode 100644
index cdc298cb..00000000
--- a/plugins/MergerSite/languages/pt-br.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"Add <b>%s</b> new site?": "Adicionar <b>%s</b> novo site?",
-	"Added <b>%s</b> new site": "Site <b>%s</b> adicionado",
-	"Site deleted: <b>%s</b>": "Site removido: <b>%s</b>"
-}
diff --git a/plugins/MergerSite/languages/tr.json b/plugins/MergerSite/languages/tr.json
deleted file mode 100644
index 5afb3942..00000000
--- a/plugins/MergerSite/languages/tr.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"Add <b>%s</b> new site?": "<b>%s</b> sitesi eklensin mi?",
-	"Added <b>%s</b> new site": "<b>%s</b> sitesi eklendi",
-	"Site deleted: <b>%s</b>": "<b>%s</b> sitesi silindi"
-}
diff --git a/plugins/MergerSite/languages/zh-tw.json b/plugins/MergerSite/languages/zh-tw.json
deleted file mode 100644
index a0684e63..00000000
--- a/plugins/MergerSite/languages/zh-tw.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"Add <b>%s</b> new site?": "添加新網站: <b>%s</b>?",
-	"Added <b>%s</b> new site": "已添加到新網站:<b>%s</b>",
-	"Site deleted: <b>%s</b>": "網站已刪除:<b>%s</b>"
-}
diff --git a/plugins/MergerSite/languages/zh.json b/plugins/MergerSite/languages/zh.json
deleted file mode 100644
index 127044e6..00000000
--- a/plugins/MergerSite/languages/zh.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"Add <b>%s</b> new site?": "添加新站点: <b>%s</b>?",
-	"Added <b>%s</b> new site": "已添加到新站点:<b>%s</b>",
-	"Site deleted: <b>%s</b>": "站点已删除:<b>%s</b>"
-}
diff --git a/plugins/Newsfeed/NewsfeedPlugin.py b/plugins/Newsfeed/NewsfeedPlugin.py
deleted file mode 100644
index 3eb14d6c..00000000
--- a/plugins/Newsfeed/NewsfeedPlugin.py
+++ /dev/null
@@ -1,187 +0,0 @@
-import time
-import re
-
-from Plugin import PluginManager
-from Db.DbQuery import DbQuery
-from Debug import Debug
-from util import helper
-from util.Flag import flag
-
-
-@PluginManager.registerTo("UiWebsocket")
-class UiWebsocketPlugin(object):
-    def formatSiteInfo(self, site, create_user=True):
-        site_info = super(UiWebsocketPlugin, self).formatSiteInfo(site, create_user=create_user)
-        feed_following = self.user.sites.get(site.address, {}).get("follow", None)
-        if feed_following == None:
-            site_info["feed_follow_num"] = None
-        else:
-            site_info["feed_follow_num"] = len(feed_following)
-        return site_info
-
-    def actionFeedFollow(self, to, feeds):
-        self.user.setFeedFollow(self.site.address, feeds)
-        self.user.save()
-        self.response(to, "ok")
-
-    def actionFeedListFollow(self, to):
-        feeds = self.user.sites.get(self.site.address, {}).get("follow", {})
-        self.response(to, feeds)
-
-    @flag.admin
-    def actionFeedQuery(self, to, limit=10, day_limit=3):
-        from Site import SiteManager
-        rows = []
-        stats = []
-
-        total_s = time.time()
-        num_sites = 0
-
-        for address, site_data in list(self.user.sites.items()):
-            feeds = site_data.get("follow")
-            if not feeds:
-                continue
-            if type(feeds) is not dict:
-                self.log.debug("Invalid feed for site %s" % address)
-                continue
-            num_sites += 1
-            for name, query_set in feeds.items():
-                site = SiteManager.site_manager.get(address)
-                if not site or not site.storage.has_db:
-                    continue
-
-                s = time.time()
-                try:
-                    query_raw, params = query_set
-                    query_parts = re.split(r"UNION(?:\s+ALL|)", query_raw)
-                    for i, query_part in enumerate(query_parts):
-                        db_query = DbQuery(query_part)
-                        if day_limit:
-                            where = " WHERE %s > strftime('%%s', 'now', '-%s day')" % (db_query.fields.get("date_added", "date_added"), day_limit)
-                            if "WHERE" in query_part:
-                                query_part = re.sub("WHERE (.*?)(?=$| GROUP BY)", where+" AND (\\1)", query_part)
-                            else:
-                                query_part += where
-                        query_parts[i] = query_part
-                    query = " UNION ".join(query_parts)
-
-                    if ":params" in query:
-                        query_params = map(helper.sqlquote, params)
-                        query = query.replace(":params", ",".join(query_params))
-
-                    res = site.storage.query(query + " ORDER BY date_added DESC LIMIT %s" % limit)
-
-                except Exception as err:  # Log error
-                    self.log.error("%s feed query %s error: %s" % (address, name, Debug.formatException(err)))
-                    stats.append({"site": site.address, "feed_name": name, "error": str(err)})
-                    continue
-
-                for row in res:
-                    row = dict(row)
-                    if not isinstance(row["date_added"], (int, float, complex)):
-                        self.log.debug("Invalid date_added from site %s: %r" % (address, row["date_added"]))
-                        continue
-                    if row["date_added"] > 1000000000000:  # Formatted as millseconds
-                        row["date_added"] = row["date_added"] / 1000
-                    if "date_added" not in row or row["date_added"] > time.time() + 120:
-                        self.log.debug("Newsfeed item from the future from from site %s" % address)
-                        continue  # Feed item is in the future, skip it
-                    row["site"] = address
-                    row["feed_name"] = name
-                    rows.append(row)
-                stats.append({"site": site.address, "feed_name": name, "taken": round(time.time() - s, 3)})
-                time.sleep(0.001)
-        return self.response(to, {"rows": rows, "stats": stats, "num": len(rows), "sites": num_sites, "taken": round(time.time() - total_s, 3)})
-
-    def parseSearch(self, search):
-        parts = re.split("(site|type):", search)
-        if len(parts) > 1:  # Found filter
-            search_text = parts[0]
-            parts = [part.strip() for part in parts]
-            filters = dict(zip(parts[1::2], parts[2::2]))
-        else:
-            search_text = search
-            filters = {}
-        return [search_text, filters]
-
-    def actionFeedSearch(self, to, search, limit=30, day_limit=30):
-        if "ADMIN" not in self.site.settings["permissions"]:
-            return self.response(to, "FeedSearch not allowed")
-
-        from Site import SiteManager
-        rows = []
-        stats = []
-        num_sites = 0
-        total_s = time.time()
-
-        search_text, filters = self.parseSearch(search)
-
-        for address, site in SiteManager.site_manager.list().items():
-            if not site.storage.has_db:
-                continue
-
-            if "site" in filters:
-                if filters["site"].lower() not in [site.address, site.content_manager.contents["content.json"].get("title").lower()]:
-                    continue
-
-            if site.storage.db:  # Database loaded
-                feeds = site.storage.db.schema.get("feeds")
-            else:
-                try:
-                    feeds = site.storage.loadJson("dbschema.json").get("feeds")
-                except:
-                    continue
-
-            if not feeds:
-                continue
-
-            num_sites += 1
-
-            for name, query in feeds.items():
-                s = time.time()
-                try:
-                    db_query = DbQuery(query)
-
-                    params = []
-                    # Filters
-                    if search_text:
-                        db_query.wheres.append("(%s LIKE ? OR %s LIKE ?)" % (db_query.fields["body"], db_query.fields["title"]))
-                        search_like = "%" + search_text.replace(" ", "%") + "%"
-                        params.append(search_like)
-                        params.append(search_like)
-                    if filters.get("type") and filters["type"] not in query:
-                        continue
-
-                    if day_limit:
-                        db_query.wheres.append(
-                            "%s > strftime('%%s', 'now', '-%s day')" % (db_query.fields.get("date_added", "date_added"), day_limit)
-                        )
-
-                    # Order
-                    db_query.parts["ORDER BY"] = "date_added DESC"
-                    db_query.parts["LIMIT"] = str(limit)
-
-                    res = site.storage.query(str(db_query), params)
-                except Exception as err:
-                    self.log.error("%s feed query %s error: %s" % (address, name, Debug.formatException(err)))
-                    stats.append({"site": site.address, "feed_name": name, "error": str(err), "query": query})
-                    continue
-                for row in res:
-                    row = dict(row)
-                    if not row["date_added"] or row["date_added"] > time.time() + 120:
-                        continue  # Feed item is in the future, skip it
-                    row["site"] = address
-                    row["feed_name"] = name
-                    rows.append(row)
-                stats.append({"site": site.address, "feed_name": name, "taken": round(time.time() - s, 3)})
-        return self.response(to, {"rows": rows, "num": len(rows), "sites": num_sites, "taken": round(time.time() - total_s, 3), "stats": stats})
-
-
-@PluginManager.registerTo("User")
-class UserPlugin(object):
-    # Set queries that user follows
-    def setFeedFollow(self, address, feeds):
-        site_data = self.getSiteData(address)
-        site_data["follow"] = feeds
-        self.save()
-        return site_data
diff --git a/plugins/Newsfeed/__init__.py b/plugins/Newsfeed/__init__.py
deleted file mode 100644
index 6e624df6..00000000
--- a/plugins/Newsfeed/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import NewsfeedPlugin
\ No newline at end of file
diff --git a/plugins/OptionalManager/ContentDbPlugin.py b/plugins/OptionalManager/ContentDbPlugin.py
deleted file mode 100644
index f0f8a877..00000000
--- a/plugins/OptionalManager/ContentDbPlugin.py
+++ /dev/null
@@ -1,414 +0,0 @@
-import time
-import collections
-import itertools
-import re
-
-import gevent
-
-from util import helper
-from Plugin import PluginManager
-from Config import config
-from Debug import Debug
-
-if "content_db" not in locals().keys():  # To keep between module reloads
-    content_db = None
-
-
-@PluginManager.registerTo("ContentDb")
-class ContentDbPlugin(object):
-    def __init__(self, *args, **kwargs):
-        global content_db
-        content_db = self
-        self.filled = {}  # Site addresses that already filled from content.json
-        self.need_filling = False  # file_optional table just created, fill data from content.json files
-        self.time_peer_numbers_updated = 0
-        self.my_optional_files = {}  # Last 50 site_address/inner_path called by fileWrite (auto-pinning these files)
-        self.optional_files = collections.defaultdict(dict)
-        self.optional_files_loaded = False
-        self.timer_check_optional = helper.timer(60 * 5, self.checkOptionalLimit)
-        super(ContentDbPlugin, self).__init__(*args, **kwargs)
-
-    def getSchema(self):
-        schema = super(ContentDbPlugin, self).getSchema()
-
-        # Need file_optional table
-        schema["tables"]["file_optional"] = {
-            "cols": [
-                ["file_id", "INTEGER PRIMARY KEY UNIQUE NOT NULL"],
-                ["site_id", "INTEGER REFERENCES site (site_id) ON DELETE CASCADE"],
-                ["inner_path", "TEXT"],
-                ["hash_id", "INTEGER"],
-                ["size", "INTEGER"],
-                ["peer", "INTEGER DEFAULT 0"],
-                ["uploaded", "INTEGER DEFAULT 0"],
-                ["is_downloaded", "INTEGER DEFAULT 0"],
-                ["is_pinned", "INTEGER DEFAULT 0"],
-                ["time_added", "INTEGER DEFAULT 0"],
-                ["time_downloaded", "INTEGER DEFAULT 0"],
-                ["time_accessed", "INTEGER DEFAULT 0"]
-            ],
-            "indexes": [
-                "CREATE UNIQUE INDEX file_optional_key ON file_optional (site_id, inner_path)",
-                "CREATE INDEX is_downloaded ON file_optional (is_downloaded)"
-            ],
-            "schema_changed": 11
-        }
-
-        return schema
-
-    def initSite(self, site):
-        super(ContentDbPlugin, self).initSite(site)
-        if self.need_filling:
-            self.fillTableFileOptional(site)
-
-    def checkTables(self):
-        changed_tables = super(ContentDbPlugin, self).checkTables()
-        if "file_optional" in changed_tables:
-            self.need_filling = True
-        return changed_tables
-
-    # Load optional files ending
-    def loadFilesOptional(self):
-        s = time.time()
-        num = 0
-        total = 0
-        total_downloaded = 0
-        res = content_db.execute("SELECT site_id, inner_path, size, is_downloaded FROM file_optional")
-        site_sizes = collections.defaultdict(lambda: collections.defaultdict(int))
-        for row in res:
-            self.optional_files[row["site_id"]][row["inner_path"][-8:]] = 1
-            num += 1
-
-            # Update site size stats
-            site_sizes[row["site_id"]]["size_optional"] += row["size"]
-            if row["is_downloaded"]:
-                site_sizes[row["site_id"]]["optional_downloaded"] += row["size"]
-
-        # Site site size stats to sites.json settings
-        site_ids_reverse = {val: key for key, val in self.site_ids.items()}
-        for site_id, stats in site_sizes.items():
-            site_address = site_ids_reverse.get(site_id)
-            if not site_address or site_address not in self.sites:
-                self.log.error("Not found site_id: %s" % site_id)
-                continue
-            site = self.sites[site_address]
-            site.settings["size_optional"] = stats["size_optional"]
-            site.settings["optional_downloaded"] = stats["optional_downloaded"]
-            total += stats["size_optional"]
-            total_downloaded += stats["optional_downloaded"]
-
-        self.log.info(
-            "Loaded %s optional files: %.2fMB, downloaded: %.2fMB in %.3fs" %
-            (num, float(total) / 1024 / 1024, float(total_downloaded) / 1024 / 1024, time.time() - s)
-        )
-
-        if self.need_filling and self.getOptionalLimitBytes() >= 0 and self.getOptionalLimitBytes() < total_downloaded:
-            limit_bytes = self.getOptionalLimitBytes()
-            limit_new = round((float(total_downloaded) / 1024 / 1024 / 1024) * 1.1, 2)  # Current limit + 10%
-            self.log.info(
-                "First startup after update and limit is smaller than downloaded files size (%.2fGB), increasing it from %.2fGB to %.2fGB" %
-                (float(total_downloaded) / 1024 / 1024 / 1024, float(limit_bytes) / 1024 / 1024 / 1024, limit_new)
-            )
-            config.saveValue("optional_limit", limit_new)
-            config.optional_limit = str(limit_new)
-
-    # Predicts if the file is optional
-    def isOptionalFile(self, site_id, inner_path):
-        return self.optional_files[site_id].get(inner_path[-8:])
-
-    # Fill file_optional table with optional files found in sites
-    def fillTableFileOptional(self, site):
-        s = time.time()
-        site_id = self.site_ids.get(site.address)
-        if not site_id:
-            return False
-        cur = self.getCursor()
-        res = cur.execute("SELECT * FROM content WHERE size_files_optional > 0 AND site_id = %s" % site_id)
-        num = 0
-        for row in res.fetchall():
-            content = site.content_manager.contents[row["inner_path"]]
-            try:
-                num += self.setContentFilesOptional(site, row["inner_path"], content, cur=cur)
-            except Exception as err:
-                self.log.error("Error loading %s into file_optional: %s" % (row["inner_path"], err))
-        cur.close()
-
-        # Set my files to pinned
-        from User import UserManager
-        user = UserManager.user_manager.get()
-        if not user:
-            user = UserManager.user_manager.create()
-        auth_address = user.getAuthAddress(site.address)
-        res = self.execute(
-            "UPDATE file_optional SET is_pinned = 1 WHERE site_id = :site_id AND inner_path LIKE :inner_path",
-            {"site_id": site_id, "inner_path": "%%/%s/%%" % auth_address}
-        )
-
-        self.log.debug(
-            "Filled file_optional table for %s in %.3fs (loaded: %s, is_pinned: %s)" %
-            (site.address, time.time() - s, num, res.rowcount)
-        )
-        self.filled[site.address] = True
-
-    def setContentFilesOptional(self, site, content_inner_path, content, cur=None):
-        if not cur:
-            cur = self
-
-        num = 0
-        site_id = self.site_ids[site.address]
-        content_inner_dir = helper.getDirname(content_inner_path)
-        for relative_inner_path, file in content.get("files_optional", {}).items():
-            file_inner_path = content_inner_dir + relative_inner_path
-            hash_id = int(file["sha512"][0:4], 16)
-            if hash_id in site.content_manager.hashfield:
-                is_downloaded = 1
-            else:
-                is_downloaded = 0
-            if site.address + "/" + content_inner_dir in self.my_optional_files:
-                is_pinned = 1
-            else:
-                is_pinned = 0
-            cur.insertOrUpdate("file_optional", {
-                "hash_id": hash_id,
-                "size": int(file["size"])
-            }, {
-                "site_id": site_id,
-                "inner_path": file_inner_path
-            }, oninsert={
-                "time_added": int(time.time()),
-                "time_downloaded": int(time.time()) if is_downloaded else 0,
-                "is_downloaded": is_downloaded,
-                "peer": is_downloaded,
-                "is_pinned": is_pinned
-            })
-            self.optional_files[site_id][file_inner_path[-8:]] = 1
-            num += 1
-
-        return num
-
-    def setContent(self, site, inner_path, content, size=0):
-        super(ContentDbPlugin, self).setContent(site, inner_path, content, size=size)
-        old_content = site.content_manager.contents.get(inner_path, {})
-        if (not self.need_filling or self.filled.get(site.address)) and ("files_optional" in content or "files_optional" in old_content):
-            self.setContentFilesOptional(site, inner_path, content)
-            # Check deleted files
-            if old_content:
-                old_files = old_content.get("files_optional", {}).keys()
-                new_files = content.get("files_optional", {}).keys()
-                content_inner_dir = helper.getDirname(inner_path)
-                deleted = [content_inner_dir + key for key in old_files if key not in new_files]
-                if deleted:
-                    site_id = self.site_ids[site.address]
-                    self.execute("DELETE FROM file_optional WHERE ?", {"site_id": site_id, "inner_path": deleted})
-
-    def deleteContent(self, site, inner_path):
-        content = site.content_manager.contents.get(inner_path)
-        if content and "files_optional" in content:
-            site_id = self.site_ids[site.address]
-            content_inner_dir = helper.getDirname(inner_path)
-            optional_inner_paths = [
-                content_inner_dir + relative_inner_path
-                for relative_inner_path in content.get("files_optional", {}).keys()
-            ]
-            self.execute("DELETE FROM file_optional WHERE ?", {"site_id": site_id, "inner_path": optional_inner_paths})
-        super(ContentDbPlugin, self).deleteContent(site, inner_path)
-
-    def updatePeerNumbers(self):
-        s = time.time()
-        num_file = 0
-        num_updated = 0
-        num_site = 0
-        for site in list(self.sites.values()):
-            if not site.content_manager.has_optional_files:
-                continue
-            if not site.isServing():
-                continue
-            has_updated_hashfield = next((
-                peer
-                for peer in site.peers.values()
-                if peer.has_hashfield and peer.hashfield.time_changed > self.time_peer_numbers_updated
-            ), None)
-
-            if not has_updated_hashfield and site.content_manager.hashfield.time_changed < self.time_peer_numbers_updated:
-                continue
-
-            hashfield_peers = itertools.chain.from_iterable(
-                peer.hashfield.storage
-                for peer in site.peers.values()
-                if peer.has_hashfield
-            )
-            peer_nums = collections.Counter(
-                itertools.chain(
-                    hashfield_peers,
-                    site.content_manager.hashfield
-                )
-            )
-
-            site_id = self.site_ids[site.address]
-            if not site_id:
-                continue
-
-            res = self.execute("SELECT file_id, hash_id, peer FROM file_optional WHERE ?", {"site_id": site_id})
-            updates = {}
-            for row in res:
-                peer_num = peer_nums.get(row["hash_id"], 0)
-                if peer_num != row["peer"]:
-                    updates[row["file_id"]] = peer_num
-
-            for file_id, peer_num in updates.items():
-                self.execute("UPDATE file_optional SET peer = ? WHERE file_id = ?", (peer_num, file_id))
-
-            num_updated += len(updates)
-            num_file += len(peer_nums)
-            num_site += 1
-
-        self.time_peer_numbers_updated = time.time()
-        self.log.debug("%s/%s peer number for %s site updated in %.3fs" % (num_updated, num_file, num_site, time.time() - s))
-
-    def queryDeletableFiles(self):
-        # First return the files with atleast 10 seeder and not accessed in last week
-        query = """
-            SELECT * FROM file_optional
-            WHERE peer > 10 AND %s
-            ORDER BY time_accessed < %s DESC, uploaded / size
-        """ % (self.getOptionalUsedWhere(), int(time.time() - 60 * 60 * 7))
-        limit_start = 0
-        while 1:
-            num = 0
-            res = self.execute("%s LIMIT %s, 50" % (query, limit_start))
-            for row in res:
-                yield row
-                num += 1
-            if num < 50:
-                break
-            limit_start += 50
-
-        self.log.debug("queryDeletableFiles returning less-seeded files")
-
-        # Then return files less seeder but still not accessed in last week
-        query = """
-            SELECT * FROM file_optional
-            WHERE peer <= 10 AND %s
-            ORDER BY peer DESC, time_accessed < %s DESC, uploaded / size
-        """ % (self.getOptionalUsedWhere(), int(time.time() - 60 * 60 * 7))
-        limit_start = 0
-        while 1:
-            num = 0
-            res = self.execute("%s LIMIT %s, 50" % (query, limit_start))
-            for row in res:
-                yield row
-                num += 1
-            if num < 50:
-                break
-            limit_start += 50
-
-        self.log.debug("queryDeletableFiles returning everyting")
-
-        # At the end return all files
-        query = """
-            SELECT * FROM file_optional
-            WHERE peer <= 10 AND %s
-            ORDER BY peer DESC, time_accessed, uploaded / size
-        """ % self.getOptionalUsedWhere()
-        limit_start = 0
-        while 1:
-            num = 0
-            res = self.execute("%s LIMIT %s, 50" % (query, limit_start))
-            for row in res:
-                yield row
-                num += 1
-            if num < 50:
-                break
-            limit_start += 50
-
-    def getOptionalLimitBytes(self):
-        if config.optional_limit.endswith("%"):
-            limit_percent = float(re.sub("[^0-9.]", "", config.optional_limit))
-            limit_bytes = helper.getFreeSpace() * (limit_percent / 100)
-        else:
-            limit_bytes = float(re.sub("[^0-9.]", "", config.optional_limit)) * 1024 * 1024 * 1024
-        return limit_bytes
-
-    def getOptionalUsedWhere(self):
-        maxsize = config.optional_limit_exclude_minsize * 1024 * 1024
-        query = "is_downloaded = 1 AND is_pinned = 0 AND size < %s" % maxsize
-
-        # Don't delete optional files from owned sites
-        my_site_ids = []
-        for address, site in self.sites.items():
-            if site.settings["own"]:
-                my_site_ids.append(str(self.site_ids[address]))
-
-        if my_site_ids:
-            query += " AND site_id NOT IN (%s)" % ", ".join(my_site_ids)
-        return query
-
-    def getOptionalUsedBytes(self):
-        size = self.execute("SELECT SUM(size) FROM file_optional WHERE %s" % self.getOptionalUsedWhere()).fetchone()[0]
-        if not size:
-            size = 0
-        return size
-
-    def getOptionalNeedDelete(self, size):
-        if config.optional_limit.endswith("%"):
-            limit_percent = float(re.sub("[^0-9.]", "", config.optional_limit))
-            need_delete = size - ((helper.getFreeSpace() + size) * (limit_percent / 100))
-        else:
-            need_delete = size - self.getOptionalLimitBytes()
-        return need_delete
-
-    def checkOptionalLimit(self, limit=None):
-        if not limit:
-            limit = self.getOptionalLimitBytes()
-
-        if limit < 0:
-            self.log.debug("Invalid limit for optional files: %s" % limit)
-            return False
-
-        size = self.getOptionalUsedBytes()
-
-        need_delete = self.getOptionalNeedDelete(size)
-
-        self.log.debug(
-            "Optional size: %.1fMB/%.1fMB, Need delete: %.1fMB" %
-            (float(size) / 1024 / 1024, float(limit) / 1024 / 1024, float(need_delete) / 1024 / 1024)
-        )
-        if need_delete <= 0:
-            return False
-
-        self.updatePeerNumbers()
-
-        site_ids_reverse = {val: key for key, val in self.site_ids.items()}
-        deleted_file_ids = []
-        for row in self.queryDeletableFiles():
-            site_address = site_ids_reverse.get(row["site_id"])
-            site = self.sites.get(site_address)
-            if not site:
-                self.log.error("No site found for id: %s" % row["site_id"])
-                continue
-            site.log.debug("Deleting %s %.3f MB left" % (row["inner_path"], float(need_delete) / 1024 / 1024))
-            deleted_file_ids.append(row["file_id"])
-            try:
-                site.content_manager.optionalRemoved(row["inner_path"], row["hash_id"], row["size"])
-                site.storage.delete(row["inner_path"])
-                need_delete -= row["size"]
-            except Exception as err:
-                site.log.error("Error deleting %s: %s" % (row["inner_path"], err))
-
-            if need_delete <= 0:
-                break
-
-        cur = self.getCursor()
-        for file_id in deleted_file_ids:
-            cur.execute("UPDATE file_optional SET is_downloaded = 0, is_pinned = 0, peer = peer - 1 WHERE ?", {"file_id": file_id})
-        cur.close()
-
-
-@PluginManager.registerTo("SiteManager")
-class SiteManagerPlugin(object):
-    def load(self, *args, **kwargs):
-        back = super(SiteManagerPlugin, self).load(*args, **kwargs)
-        if self.sites and not content_db.optional_files_loaded and content_db.conn:
-            content_db.optional_files_loaded = True
-            content_db.loadFilesOptional()
-        return back
\ No newline at end of file
diff --git a/plugins/OptionalManager/OptionalManagerPlugin.py b/plugins/OptionalManager/OptionalManagerPlugin.py
deleted file mode 100644
index f01fab65..00000000
--- a/plugins/OptionalManager/OptionalManagerPlugin.py
+++ /dev/null
@@ -1,253 +0,0 @@
-import time
-import re
-import collections
-
-import gevent
-
-from util import helper
-from Plugin import PluginManager
-from . import ContentDbPlugin
-
-
-# We can only import plugin host clases after the plugins are loaded
-@PluginManager.afterLoad
-def importPluginnedClasses():
-    global config
-    from Config import config
-
-
-def processAccessLog():
-    global access_log
-    if access_log:
-        content_db = ContentDbPlugin.content_db
-        if not content_db.conn:
-            return False
-
-        s = time.time()
-        access_log_prev = access_log
-        access_log = collections.defaultdict(dict)
-        now = int(time.time())
-        num = 0
-        for site_id in access_log_prev:
-            content_db.execute(
-                "UPDATE file_optional SET time_accessed = %s WHERE ?" % now,
-                {"site_id": site_id, "inner_path": list(access_log_prev[site_id].keys())}
-            )
-            num += len(access_log_prev[site_id])
-
-        content_db.log.debug("Inserted %s web request stat in %.3fs" % (num, time.time() - s))
-
-
-def processRequestLog():
-    global request_log
-    if request_log:
-        content_db = ContentDbPlugin.content_db
-        if not content_db.conn:
-            return False
-
-        s = time.time()
-        request_log_prev = request_log
-        request_log = collections.defaultdict(lambda: collections.defaultdict(int))  # {site_id: {inner_path1: 1, inner_path2: 1...}}
-        num = 0
-        for site_id in request_log_prev:
-            for inner_path, uploaded in request_log_prev[site_id].items():
-                content_db.execute(
-                    "UPDATE file_optional SET uploaded = uploaded + %s WHERE ?" % uploaded,
-                    {"site_id": site_id, "inner_path": inner_path}
-                )
-                num += 1
-        content_db.log.debug("Inserted %s file request stat in %.3fs" % (num, time.time() - s))
-
-
-if "access_log" not in locals().keys():  # To keep between module reloads
-    access_log = collections.defaultdict(dict)  # {site_id: {inner_path1: 1, inner_path2: 1...}}
-    request_log = collections.defaultdict(lambda: collections.defaultdict(int))  # {site_id: {inner_path1: 1, inner_path2: 1...}}
-    helper.timer(61, processAccessLog)
-    helper.timer(60, processRequestLog)
-
-
-@PluginManager.registerTo("ContentManager")
-class ContentManagerPlugin(object):
-    def __init__(self, *args, **kwargs):
-        self.cache_is_pinned = {}
-        super(ContentManagerPlugin, self).__init__(*args, **kwargs)
-
-    def optionalDownloaded(self, inner_path, hash_id, size=None, own=False):
-        if "|" in inner_path:  # Big file piece
-            file_inner_path, file_range = inner_path.split("|")
-        else:
-            file_inner_path = inner_path
-
-        self.contents.db.executeDelayed(
-            "UPDATE file_optional SET time_downloaded = :now, is_downloaded = 1, peer = peer + 1 WHERE site_id = :site_id AND inner_path = :inner_path AND is_downloaded = 0",
-            {"now": int(time.time()), "site_id": self.contents.db.site_ids[self.site.address], "inner_path": file_inner_path}
-        )
-
-        return super(ContentManagerPlugin, self).optionalDownloaded(inner_path, hash_id, size, own)
-
-    def optionalRemoved(self, inner_path, hash_id, size=None):
-        res = self.contents.db.execute(
-            "UPDATE file_optional SET is_downloaded = 0, is_pinned = 0, peer = peer - 1 WHERE site_id = :site_id AND inner_path = :inner_path AND is_downloaded = 1",
-            {"site_id": self.contents.db.site_ids[self.site.address], "inner_path": inner_path}
-        )
-
-        if res.rowcount > 0:
-            back = super(ContentManagerPlugin, self).optionalRemoved(inner_path, hash_id, size)
-            # Re-add to hashfield if we have other file with the same hash_id
-            if self.isDownloaded(hash_id=hash_id, force_check_db=True):
-                self.hashfield.appendHashId(hash_id)
-        else:
-            back = False
-        self.cache_is_pinned = {}
-        return back
-
-    def optionalRenamed(self, inner_path_old, inner_path_new):
-        back = super(ContentManagerPlugin, self).optionalRenamed(inner_path_old, inner_path_new)
-        self.cache_is_pinned = {}
-        self.contents.db.execute(
-            "UPDATE file_optional SET inner_path = :inner_path_new WHERE site_id = :site_id AND inner_path = :inner_path_old",
-            {"site_id": self.contents.db.site_ids[self.site.address], "inner_path_old": inner_path_old, "inner_path_new": inner_path_new}
-        )
-        return back
-
-    def isDownloaded(self, inner_path=None, hash_id=None, force_check_db=False):
-        if hash_id and not force_check_db and hash_id not in self.hashfield:
-            return False
-
-        if inner_path:
-            res = self.contents.db.execute(
-                "SELECT is_downloaded FROM file_optional WHERE site_id = :site_id AND inner_path = :inner_path LIMIT 1",
-                {"site_id": self.contents.db.site_ids[self.site.address], "inner_path": inner_path}
-            )
-        else:
-            res = self.contents.db.execute(
-                "SELECT is_downloaded FROM file_optional WHERE site_id = :site_id AND hash_id = :hash_id AND is_downloaded = 1 LIMIT 1",
-                {"site_id": self.contents.db.site_ids[self.site.address], "hash_id": hash_id}
-            )
-        row = res.fetchone()
-        if row and row["is_downloaded"]:
-            return True
-        else:
-            return False
-
-    def isPinned(self, inner_path):
-        if inner_path in self.cache_is_pinned:
-            self.site.log.debug("Cached is pinned: %s" % inner_path)
-            return self.cache_is_pinned[inner_path]
-
-        res = self.contents.db.execute(
-            "SELECT is_pinned FROM file_optional WHERE site_id = :site_id AND inner_path = :inner_path LIMIT 1",
-            {"site_id": self.contents.db.site_ids[self.site.address], "inner_path": inner_path}
-        )
-        row = res.fetchone()
-
-        if row and row[0]:
-            is_pinned = True
-        else:
-            is_pinned = False
-
-        self.cache_is_pinned[inner_path] = is_pinned
-        self.site.log.debug("Cache set is pinned: %s %s" % (inner_path, is_pinned))
-
-        return is_pinned
-
-    def setPin(self, inner_path, is_pinned):
-        content_db = self.contents.db
-        site_id = content_db.site_ids[self.site.address]
-        content_db.execute("UPDATE file_optional SET is_pinned = %d WHERE ?" % is_pinned, {"site_id": site_id, "inner_path": inner_path})
-        self.cache_is_pinned = {}
-
-    def optionalDelete(self, inner_path):
-        if self.isPinned(inner_path):
-            self.site.log.debug("Skip deleting pinned optional file: %s" % inner_path)
-            return False
-        else:
-            return super(ContentManagerPlugin, self).optionalDelete(inner_path)
-
-
-@PluginManager.registerTo("WorkerManager")
-class WorkerManagerPlugin(object):
-    def doneTask(self, task):
-        super(WorkerManagerPlugin, self).doneTask(task)
-
-        if task["optional_hash_id"] and not self.tasks:  # Execute delayed queries immedietly after tasks finished
-            ContentDbPlugin.content_db.processDelayed()
-
-
-@PluginManager.registerTo("UiRequest")
-class UiRequestPlugin(object):
-    def parsePath(self, path):
-        global access_log
-        path_parts = super(UiRequestPlugin, self).parsePath(path)
-        if path_parts:
-            site_id = ContentDbPlugin.content_db.site_ids.get(path_parts["request_address"])
-            if site_id:
-                if ContentDbPlugin.content_db.isOptionalFile(site_id, path_parts["inner_path"]):
-                    access_log[site_id][path_parts["inner_path"]] = 1
-        return path_parts
-
-
-@PluginManager.registerTo("FileRequest")
-class FileRequestPlugin(object):
-    def actionGetFile(self, params):
-        stats = super(FileRequestPlugin, self).actionGetFile(params)
-        self.recordFileRequest(params["site"], params["inner_path"], stats)
-        return stats
-
-    def actionStreamFile(self, params):
-        stats = super(FileRequestPlugin, self).actionStreamFile(params)
-        self.recordFileRequest(params["site"], params["inner_path"], stats)
-        return stats
-
-    def recordFileRequest(self, site_address, inner_path, stats):
-        if not stats:
-            # Only track the last request of files
-            return False
-        site_id = ContentDbPlugin.content_db.site_ids[site_address]
-        if site_id and ContentDbPlugin.content_db.isOptionalFile(site_id, inner_path):
-            request_log[site_id][inner_path] += stats["bytes_sent"]
-
-
-@PluginManager.registerTo("Site")
-class SitePlugin(object):
-    def isDownloadable(self, inner_path):
-        is_downloadable = super(SitePlugin, self).isDownloadable(inner_path)
-        if is_downloadable:
-            return is_downloadable
-
-        for path in self.settings.get("optional_help", {}).keys():
-            if inner_path.startswith(path):
-                return True
-
-        return False
-
-    def fileForgot(self, inner_path):
-        if "|" in inner_path and self.content_manager.isPinned(re.sub(r"\|.*", "", inner_path)):
-            self.log.debug("File %s is pinned, no fileForgot" % inner_path)
-            return False
-        else:
-            return super(SitePlugin, self).fileForgot(inner_path)
-
-    def fileDone(self, inner_path):
-        if "|" in inner_path and self.bad_files.get(inner_path, 0) > 5:  # Idle optional file done
-            inner_path_file = re.sub(r"\|.*", "", inner_path)
-            num_changed = 0
-            for key, val in self.bad_files.items():
-                if key.startswith(inner_path_file) and val > 1:
-                    self.bad_files[key] = 1
-                    num_changed += 1
-            self.log.debug("Idle optional file piece done, changed retry number of %s pieces." % num_changed)
-            if num_changed:
-                gevent.spawn(self.retryBadFiles)
-
-        return super(SitePlugin, self).fileDone(inner_path)
-
-
-@PluginManager.registerTo("ConfigPlugin")
-class ConfigPlugin(object):
-    def createArguments(self):
-        group = self.parser.add_argument_group("OptionalManager plugin")
-        group.add_argument('--optional_limit', help='Limit total size of optional files', default="10%", metavar="GB or free space %")
-        group.add_argument('--optional_limit_exclude_minsize', help='Exclude files larger than this limit from optional size limit calculation', default=20, metavar="MB", type=int)
-
-        return super(ConfigPlugin, self).createArguments()
diff --git a/plugins/OptionalManager/Test/TestOptionalManager.py b/plugins/OptionalManager/Test/TestOptionalManager.py
deleted file mode 100644
index 4bd44695..00000000
--- a/plugins/OptionalManager/Test/TestOptionalManager.py
+++ /dev/null
@@ -1,158 +0,0 @@
-import copy
-
-import pytest
-
-
-@pytest.mark.usefixtures("resetSettings")
-class TestOptionalManager:
-    def testDbFill(self, site):
-        contents = site.content_manager.contents
-        assert len(site.content_manager.hashfield) > 0
-        assert contents.db.execute("SELECT COUNT(*) FROM file_optional WHERE is_downloaded = 1").fetchone()[0] == len(site.content_manager.hashfield)
-
-    def testSetContent(self, site):
-        contents = site.content_manager.contents
-
-        # Add new file
-        new_content = copy.deepcopy(contents["content.json"])
-        new_content["files_optional"]["testfile"] = {
-            "size": 1234,
-            "sha512": "aaaabbbbcccc"
-        }
-        num_optional_files_before = contents.db.execute("SELECT COUNT(*) FROM file_optional").fetchone()[0]
-        contents["content.json"] = new_content
-        assert contents.db.execute("SELECT COUNT(*) FROM file_optional").fetchone()[0] > num_optional_files_before
-
-        # Remove file
-        new_content = copy.deepcopy(contents["content.json"])
-        del new_content["files_optional"]["testfile"]
-        num_optional_files_before = contents.db.execute("SELECT COUNT(*) FROM file_optional").fetchone()[0]
-        contents["content.json"] = new_content
-        assert contents.db.execute("SELECT COUNT(*) FROM file_optional").fetchone()[0] < num_optional_files_before
-
-    def testDeleteContent(self, site):
-        contents = site.content_manager.contents
-        num_optional_files_before = contents.db.execute("SELECT COUNT(*) FROM file_optional").fetchone()[0]
-        del contents["content.json"]
-        assert contents.db.execute("SELECT COUNT(*) FROM file_optional").fetchone()[0] < num_optional_files_before
-
-    def testVerifyFiles(self, site):
-        contents = site.content_manager.contents
-
-        # Add new file
-        new_content = copy.deepcopy(contents["content.json"])
-        new_content["files_optional"]["testfile"] = {
-            "size": 1234,
-            "sha512": "aaaabbbbcccc"
-        }
-        contents["content.json"] = new_content
-        file_row = contents.db.execute("SELECT * FROM file_optional WHERE inner_path = 'testfile'").fetchone()
-        assert not file_row["is_downloaded"]
-
-        # Write file from outside of ZeroNet
-        site.storage.open("testfile", "wb").write(b"A" * 1234)  # For quick check hash does not matter only file size
-
-        hashfield_len_before = len(site.content_manager.hashfield)
-        site.storage.verifyFiles(quick_check=True)
-        assert len(site.content_manager.hashfield) == hashfield_len_before + 1
-
-        file_row = contents.db.execute("SELECT * FROM file_optional WHERE inner_path = 'testfile'").fetchone()
-        assert file_row["is_downloaded"]
-
-        # Delete file outside of ZeroNet
-        site.storage.delete("testfile")
-        site.storage.verifyFiles(quick_check=True)
-        file_row = contents.db.execute("SELECT * FROM file_optional WHERE inner_path = 'testfile'").fetchone()
-        assert not file_row["is_downloaded"]
-
-    def testVerifyFilesSameHashId(self, site):
-        contents = site.content_manager.contents
-
-        new_content = copy.deepcopy(contents["content.json"])
-
-        # Add two files with same hashid (first 4 character)
-        new_content["files_optional"]["testfile1"] = {
-            "size": 1234,
-            "sha512": "aaaabbbbcccc"
-        }
-        new_content["files_optional"]["testfile2"] = {
-            "size": 2345,
-            "sha512": "aaaabbbbdddd"
-        }
-        contents["content.json"] = new_content
-
-        assert site.content_manager.hashfield.getHashId("aaaabbbbcccc") == site.content_manager.hashfield.getHashId("aaaabbbbdddd")
-
-        # Write files from outside of ZeroNet (For quick check hash does not matter only file size)
-        site.storage.open("testfile1", "wb").write(b"A" * 1234)
-        site.storage.open("testfile2", "wb").write(b"B" * 2345)
-
-        site.storage.verifyFiles(quick_check=True)
-
-        # Make sure that both is downloaded
-        assert site.content_manager.isDownloaded("testfile1")
-        assert site.content_manager.isDownloaded("testfile2")
-        assert site.content_manager.hashfield.getHashId("aaaabbbbcccc") in site.content_manager.hashfield
-
-        # Delete one of the files
-        site.storage.delete("testfile1")
-        site.storage.verifyFiles(quick_check=True)
-        assert not site.content_manager.isDownloaded("testfile1")
-        assert site.content_manager.isDownloaded("testfile2")
-        assert site.content_manager.hashfield.getHashId("aaaabbbbdddd") in site.content_manager.hashfield
-
-    def testIsPinned(self, site):
-        assert not site.content_manager.isPinned("data/img/zerotalk-upvote.png")
-        site.content_manager.setPin("data/img/zerotalk-upvote.png", True)
-        assert site.content_manager.isPinned("data/img/zerotalk-upvote.png")
-
-        assert len(site.content_manager.cache_is_pinned) == 1
-        site.content_manager.cache_is_pinned = {}
-        assert site.content_manager.isPinned("data/img/zerotalk-upvote.png")
-
-    def testBigfilePieceReset(self, site):
-        site.bad_files = {
-            "data/fake_bigfile.mp4|0-1024": 10,
-            "data/fake_bigfile.mp4|1024-2048": 10,
-            "data/fake_bigfile.mp4|2048-3064": 10
-        }
-        site.onFileDone("data/fake_bigfile.mp4|0-1024")
-        assert site.bad_files["data/fake_bigfile.mp4|1024-2048"] == 1
-        assert site.bad_files["data/fake_bigfile.mp4|2048-3064"] == 1
-
-    def testOptionalDelete(self, site):
-        contents = site.content_manager.contents
-
-        site.content_manager.setPin("data/img/zerotalk-upvote.png", True)
-        site.content_manager.setPin("data/img/zeroid.png", False)
-        new_content = copy.deepcopy(contents["content.json"])
-        del new_content["files_optional"]["data/img/zerotalk-upvote.png"]
-        del new_content["files_optional"]["data/img/zeroid.png"]
-
-        assert site.storage.isFile("data/img/zerotalk-upvote.png")
-        assert site.storage.isFile("data/img/zeroid.png")
-
-        site.storage.writeJson("content.json", new_content)
-        site.content_manager.loadContent("content.json", force=True)
-
-        assert not site.storage.isFile("data/img/zeroid.png")
-        assert site.storage.isFile("data/img/zerotalk-upvote.png")
-
-    def testOptionalRename(self, site):
-        contents = site.content_manager.contents
-
-        site.content_manager.setPin("data/img/zerotalk-upvote.png", True)
-        new_content = copy.deepcopy(contents["content.json"])
-        new_content["files_optional"]["data/img/zerotalk-upvote-new.png"] = new_content["files_optional"]["data/img/zerotalk-upvote.png"]
-        del new_content["files_optional"]["data/img/zerotalk-upvote.png"]
-
-        assert site.storage.isFile("data/img/zerotalk-upvote.png")
-        assert site.content_manager.isPinned("data/img/zerotalk-upvote.png")
-
-        site.storage.writeJson("content.json", new_content)
-        site.content_manager.loadContent("content.json", force=True)
-
-        assert not site.storage.isFile("data/img/zerotalk-upvote.png")
-        assert not site.content_manager.isPinned("data/img/zerotalk-upvote.png")
-        assert site.content_manager.isPinned("data/img/zerotalk-upvote-new.png")
-        assert site.storage.isFile("data/img/zerotalk-upvote-new.png")
diff --git a/plugins/OptionalManager/Test/conftest.py b/plugins/OptionalManager/Test/conftest.py
deleted file mode 100644
index 8c1df5b2..00000000
--- a/plugins/OptionalManager/Test/conftest.py
+++ /dev/null
@@ -1 +0,0 @@
-from src.Test.conftest import *
\ No newline at end of file
diff --git a/plugins/OptionalManager/Test/pytest.ini b/plugins/OptionalManager/Test/pytest.ini
deleted file mode 100644
index d09210d1..00000000
--- a/plugins/OptionalManager/Test/pytest.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[pytest]
-python_files = Test*.py
-addopts = -rsxX -v --durations=6
-markers =
-    webtest: mark a test as a webtest.
\ No newline at end of file
diff --git a/plugins/OptionalManager/UiWebsocketPlugin.py b/plugins/OptionalManager/UiWebsocketPlugin.py
deleted file mode 100644
index 0acc53cf..00000000
--- a/plugins/OptionalManager/UiWebsocketPlugin.py
+++ /dev/null
@@ -1,396 +0,0 @@
-import re
-import time
-import html
-import os
-
-import gevent
-
-from Plugin import PluginManager
-from Config import config
-from util import helper
-from util.Flag import flag
-from Translate import Translate
-
-
-plugin_dir = os.path.dirname(__file__)
-
-if "_" not in locals():
-    _ = Translate(plugin_dir + "/languages/")
-
-bigfile_sha512_cache = {}
-
-
-@PluginManager.registerTo("UiWebsocket")
-class UiWebsocketPlugin(object):
-    def __init__(self, *args, **kwargs):
-        self.time_peer_numbers_updated = 0
-        super(UiWebsocketPlugin, self).__init__(*args, **kwargs)
-
-    def actionSiteSign(self, to, privatekey=None, inner_path="content.json", *args, **kwargs):
-        # Add file to content.db and set it as pinned
-        content_db = self.site.content_manager.contents.db
-        content_inner_dir = helper.getDirname(inner_path)
-        content_db.my_optional_files[self.site.address + "/" + content_inner_dir] = time.time()
-        if len(content_db.my_optional_files) > 50:  # Keep only last 50
-            oldest_key = min(
-                iter(content_db.my_optional_files.keys()),
-                key=(lambda key: content_db.my_optional_files[key])
-            )
-            del content_db.my_optional_files[oldest_key]
-
-        return super(UiWebsocketPlugin, self).actionSiteSign(to, privatekey, inner_path, *args, **kwargs)
-
-    def updatePeerNumbers(self):
-        self.site.updateHashfield()
-        content_db = self.site.content_manager.contents.db
-        content_db.updatePeerNumbers()
-        self.site.updateWebsocket(peernumber_updated=True)
-
-    def addBigfileInfo(self, row):
-        global bigfile_sha512_cache
-
-        content_db = self.site.content_manager.contents.db
-        site = content_db.sites[row["address"]]
-        if not site.settings.get("has_bigfile"):
-            return False
-
-        file_key = row["address"] + "/" + row["inner_path"]
-        sha512 = bigfile_sha512_cache.get(file_key)
-        file_info = None
-        if not sha512:
-            file_info = site.content_manager.getFileInfo(row["inner_path"])
-            if not file_info or not file_info.get("piece_size"):
-                return False
-            sha512 = file_info["sha512"]
-            bigfile_sha512_cache[file_key] = sha512
-
-        if sha512 in site.storage.piecefields:
-            piecefield = site.storage.piecefields[sha512].tobytes()
-        else:
-            piecefield = None
-
-        if piecefield:
-            row["pieces"] = len(piecefield)
-            row["pieces_downloaded"] = piecefield.count(b"\x01")
-            row["downloaded_percent"] = 100 * row["pieces_downloaded"] / row["pieces"]
-            if row["pieces_downloaded"]:
-                if row["pieces"] == row["pieces_downloaded"]:
-                    row["bytes_downloaded"] = row["size"]
-                else:
-                    if not file_info:
-                        file_info = site.content_manager.getFileInfo(row["inner_path"])
-                    row["bytes_downloaded"] = row["pieces_downloaded"] * file_info.get("piece_size", 0)
-            else:
-                row["bytes_downloaded"] = 0
-
-            row["is_downloading"] = bool(next((inner_path for inner_path in site.bad_files if inner_path.startswith(row["inner_path"])), False))
-
-        # Add leech / seed stats
-        row["peer_seed"] = 0
-        row["peer_leech"] = 0
-        for peer in site.peers.values():
-            if not peer.time_piecefields_updated or sha512 not in peer.piecefields:
-                continue
-            peer_piecefield = peer.piecefields[sha512].tobytes()
-            if not peer_piecefield:
-                continue
-            if peer_piecefield == b"\x01" * len(peer_piecefield):
-                row["peer_seed"] += 1
-            else:
-                row["peer_leech"] += 1
-
-        # Add myself
-        if piecefield:
-            if row["pieces_downloaded"] == row["pieces"]:
-                row["peer_seed"] += 1
-            else:
-                row["peer_leech"] += 1
-
-        return True
-
-    # Optional file functions
-
-    def actionOptionalFileList(self, to, address=None, orderby="time_downloaded DESC", limit=10, filter="downloaded", filter_inner_path=None):
-        if not address:
-            address = self.site.address
-
-        # Update peer numbers if necessary
-        content_db = self.site.content_manager.contents.db
-        if time.time() - content_db.time_peer_numbers_updated > 60 * 1 and time.time() - self.time_peer_numbers_updated > 60 * 5:
-            # Start in new thread to avoid blocking
-            self.time_peer_numbers_updated = time.time()
-            gevent.spawn(self.updatePeerNumbers)
-
-        if address == "all" and "ADMIN" not in self.permissions:
-            return self.response(to, {"error": "Forbidden"})
-
-        if not self.hasSitePermission(address):
-            return self.response(to, {"error": "Forbidden"})
-
-        if not all([re.match("^[a-z_*/+-]+( DESC| ASC|)$", part.strip()) for part in orderby.split(",")]):
-            return self.response(to, "Invalid order_by")
-
-        if type(limit) != int:
-            return self.response(to, "Invalid limit")
-
-        back = []
-        content_db = self.site.content_manager.contents.db
-
-        wheres = {}
-        wheres_raw = []
-        if "bigfile" in filter:
-            wheres["size >"] = 1024 * 1024 * 1
-        if "downloaded" in filter:
-            wheres_raw.append("(is_downloaded = 1 OR is_pinned = 1)")
-        if "pinned" in filter:
-            wheres["is_pinned"] = 1
-        if filter_inner_path:
-            wheres["inner_path__like"] = filter_inner_path
-
-        if address == "all":
-            join = "LEFT JOIN site USING (site_id)"
-        else:
-            wheres["site_id"] = content_db.site_ids[address]
-            join = ""
-
-        if wheres_raw:
-            query_wheres_raw = "AND" + " AND ".join(wheres_raw)
-        else:
-            query_wheres_raw = ""
-
-        query = "SELECT * FROM file_optional %s WHERE ? %s ORDER BY %s LIMIT %s" % (join, query_wheres_raw, orderby, limit)
-
-        for row in content_db.execute(query, wheres):
-            row = dict(row)
-            if address != "all":
-                row["address"] = address
-
-            if row["size"] > 1024 * 1024:
-                has_bigfile_info = self.addBigfileInfo(row)
-            else:
-                has_bigfile_info = False
-
-            if not has_bigfile_info and "bigfile" in filter:
-                continue
-
-            if not has_bigfile_info:
-                if row["is_downloaded"]:
-                    row["bytes_downloaded"] = row["size"]
-                    row["downloaded_percent"] = 100
-                else:
-                    row["bytes_downloaded"] = 0
-                    row["downloaded_percent"] = 0
-
-            back.append(row)
-        self.response(to, back)
-
-    def actionOptionalFileInfo(self, to, inner_path):
-        content_db = self.site.content_manager.contents.db
-        site_id = content_db.site_ids[self.site.address]
-
-        # Update peer numbers if necessary
-        if time.time() - content_db.time_peer_numbers_updated > 60 * 1 and time.time() - self.time_peer_numbers_updated > 60 * 5:
-            # Start in new thread to avoid blocking
-            self.time_peer_numbers_updated = time.time()
-            gevent.spawn(self.updatePeerNumbers)
-
-        query = "SELECT * FROM file_optional WHERE site_id = :site_id AND inner_path = :inner_path LIMIT 1"
-        res = content_db.execute(query, {"site_id": site_id, "inner_path": inner_path})
-        row = next(res, None)
-        if row:
-            row = dict(row)
-            if row["size"] > 1024 * 1024:
-                row["address"] = self.site.address
-                self.addBigfileInfo(row)
-            self.response(to, row)
-        else:
-            self.response(to, None)
-
-    def setPin(self, inner_path, is_pinned, address=None):
-        if not address:
-            address = self.site.address
-
-        if not self.hasSitePermission(address):
-            return {"error": "Forbidden"}
-
-        site = self.server.sites[address]
-        site.content_manager.setPin(inner_path, is_pinned)
-
-        return "ok"
-
-    @flag.no_multiuser
-    def actionOptionalFilePin(self, to, inner_path, address=None):
-        if type(inner_path) is not list:
-            inner_path = [inner_path]
-        back = self.setPin(inner_path, 1, address)
-        num_file = len(inner_path)
-        if back == "ok":
-            if num_file == 1:
-                self.cmd("notification", ["done", _["Pinned %s"] % html.escape(helper.getFilename(inner_path[0])), 5000])
-            else:
-                self.cmd("notification", ["done", _["Pinned %s files"] % num_file, 5000])
-        self.response(to, back)
-
-    @flag.no_multiuser
-    def actionOptionalFileUnpin(self, to, inner_path, address=None):
-        if type(inner_path) is not list:
-            inner_path = [inner_path]
-        back = self.setPin(inner_path, 0, address)
-        num_file = len(inner_path)
-        if back == "ok":
-            if num_file == 1:
-                self.cmd("notification", ["done", _["Removed pin from %s"] % html.escape(helper.getFilename(inner_path[0])), 5000])
-            else:
-                self.cmd("notification", ["done", _["Removed pin from %s files"] % num_file, 5000])
-        self.response(to, back)
-
-    @flag.no_multiuser
-    def actionOptionalFileDelete(self, to, inner_path, address=None):
-        if not address:
-            address = self.site.address
-
-        if not self.hasSitePermission(address):
-            return self.response(to, {"error": "Forbidden"})
-
-        site = self.server.sites[address]
-
-        content_db = site.content_manager.contents.db
-        site_id = content_db.site_ids[site.address]
-
-        res = content_db.execute("SELECT * FROM file_optional WHERE ? LIMIT 1", {"site_id": site_id, "inner_path": inner_path, "is_downloaded": 1})
-        row = next(res, None)
-
-        if not row:
-            return self.response(to, {"error": "Not found in content.db"})
-
-        removed = site.content_manager.optionalRemoved(inner_path, row["hash_id"], row["size"])
-        # if not removed:
-        #    return self.response(to, {"error": "Not found in hash_id: %s" % row["hash_id"]})
-
-        content_db.execute("UPDATE file_optional SET is_downloaded = 0, is_pinned = 0, peer = peer - 1 WHERE ?", {"site_id": site_id, "inner_path": inner_path})
-
-        try:
-            site.storage.delete(inner_path)
-        except Exception as err:
-            return self.response(to, {"error": "File delete error: %s" % err})
-        site.updateWebsocket(file_delete=inner_path)
-
-        if inner_path in site.content_manager.cache_is_pinned:
-            site.content_manager.cache_is_pinned = {}
-
-        self.response(to, "ok")
-
-    # Limit functions
-
-    @flag.admin
-    def actionOptionalLimitStats(self, to):
-        back = {}
-        back["limit"] = config.optional_limit
-        back["used"] = self.site.content_manager.contents.db.getOptionalUsedBytes()
-        back["free"] = helper.getFreeSpace()
-
-        self.response(to, back)
-
-    @flag.no_multiuser
-    @flag.admin
-    def actionOptionalLimitSet(self, to, limit):
-        config.optional_limit = re.sub(r"\.0+$", "", limit)  # Remove unnecessary digits from end
-        config.saveValue("optional_limit", limit)
-        self.response(to, "ok")
-
-    # Distribute help functions
-
-    def actionOptionalHelpList(self, to, address=None):
-        if not address:
-            address = self.site.address
-
-        if not self.hasSitePermission(address):
-            return self.response(to, {"error": "Forbidden"})
-
-        site = self.server.sites[address]
-
-        self.response(to, site.settings.get("optional_help", {}))
-
-    @flag.no_multiuser
-    def actionOptionalHelp(self, to, directory, title, address=None):
-        if not address:
-            address = self.site.address
-
-        if not self.hasSitePermission(address):
-            return self.response(to, {"error": "Forbidden"})
-
-        site = self.server.sites[address]
-        content_db = site.content_manager.contents.db
-        site_id = content_db.site_ids[address]
-
-        if "optional_help" not in site.settings:
-            site.settings["optional_help"] = {}
-
-        stats = content_db.execute(
-            "SELECT COUNT(*) AS num, SUM(size) AS size FROM file_optional WHERE site_id = :site_id AND inner_path LIKE :inner_path",
-            {"site_id": site_id, "inner_path": directory + "%"}
-        ).fetchone()
-        stats = dict(stats)
-
-        if not stats["size"]:
-            stats["size"] = 0
-        if not stats["num"]:
-            stats["num"] = 0
-
-        self.cmd("notification", [
-            "done",
-            _["You started to help distribute <b>%s</b>.<br><small>Directory: %s</small>"] %
-            (html.escape(title), html.escape(directory)),
-            10000
-        ])
-
-        site.settings["optional_help"][directory] = title
-
-        self.response(to, dict(stats))
-
-    @flag.no_multiuser
-    def actionOptionalHelpRemove(self, to, directory, address=None):
-        if not address:
-            address = self.site.address
-
-        if not self.hasSitePermission(address):
-            return self.response(to, {"error": "Forbidden"})
-
-        site = self.server.sites[address]
-
-        try:
-            del site.settings["optional_help"][directory]
-            self.response(to, "ok")
-        except Exception:
-            self.response(to, {"error": "Not found"})
-
-    def cbOptionalHelpAll(self, to, site, value):
-        site.settings["autodownloadoptional"] = value
-        self.response(to, value)
-
-    @flag.no_multiuser
-    def actionOptionalHelpAll(self, to, value, address=None):
-        if not address:
-            address = self.site.address
-
-        if not self.hasSitePermission(address):
-            return self.response(to, {"error": "Forbidden"})
-
-        site = self.server.sites[address]
-
-        if value:
-            if "ADMIN" in self.site.settings["permissions"]:
-                self.cbOptionalHelpAll(to, site, True)
-            else:
-                site_title = site.content_manager.contents["content.json"].get("title", address)
-                self.cmd(
-                    "confirm",
-                    [
-                        _["Help distribute all new optional files on site <b>%s</b>"] % html.escape(site_title),
-                        _["Yes, I want to help!"]
-                    ],
-                    lambda res: self.cbOptionalHelpAll(to, site, True)
-                )
-        else:
-            site.settings["autodownloadoptional"] = False
-            self.response(to, False)
diff --git a/plugins/OptionalManager/__init__.py b/plugins/OptionalManager/__init__.py
deleted file mode 100644
index 77b8c348..00000000
--- a/plugins/OptionalManager/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-from . import OptionalManagerPlugin
-from . import UiWebsocketPlugin
diff --git a/plugins/OptionalManager/languages/es.json b/plugins/OptionalManager/languages/es.json
deleted file mode 100644
index 32ae46ae..00000000
--- a/plugins/OptionalManager/languages/es.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-	"Pinned %s files": "Archivos %s fijados",
-	"Removed pin from %s files": "Archivos %s que no estan fijados",
-	"You started to help distribute <b>%s</b>.<br><small>Directory: %s</small>": "Tu empezaste a ayudar a distribuir <b>%s</b>.<br><small>Directorio: %s</small>",
-	"Help distribute all new optional files on site <b>%s</b>": "Ayude a distribuir todos los archivos opcionales en el sitio <b>%s</b>",
-	"Yes, I want to help!": "¡Si, yo quiero ayudar!"
-}
diff --git a/plugins/OptionalManager/languages/fr.json b/plugins/OptionalManager/languages/fr.json
deleted file mode 100644
index 47a563dc..00000000
--- a/plugins/OptionalManager/languages/fr.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-	"Pinned %s files": "Fichiers %s épinglés",
-	"Removed pin from %s files": "Fichiers %s ne sont plus épinglés",
-	"You started to help distribute <b>%s</b>.<br><small>Directory: %s</small>": "Vous avez commencé à aider à distribuer <b>%s</b>.<br><small>Dossier : %s</small>",
-	"Help distribute all new optional files on site <b>%s</b>": "Aider à distribuer tous les fichiers optionnels du site <b>%s</b>",
-	"Yes, I want to help!": "Oui, je veux aider !"
-}
diff --git a/plugins/OptionalManager/languages/hu.json b/plugins/OptionalManager/languages/hu.json
deleted file mode 100644
index 7a23b86c..00000000
--- a/plugins/OptionalManager/languages/hu.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-	"Pinned %s files": "%s fájl rögzítve",
-	"Removed pin from %s files": "%s fájl rögzítés eltávolítva",
-	"You started to help distribute <b>%s</b>.<br><small>Directory: %s</small>": "Új segítség a terjesztésben: <b>%s</b>.<br><small>Könyvtár: %s</small>",
-	"Help distribute all new optional files on site <b>%s</b>": "Segítség az összes új opcionális fájl terjesztésében az <b>%s</b> oldalon",
-	"Yes, I want to help!": "Igen, segíteni akarok!"
-}
diff --git a/plugins/OptionalManager/languages/jp.json b/plugins/OptionalManager/languages/jp.json
deleted file mode 100644
index af6dc79e..00000000
--- a/plugins/OptionalManager/languages/jp.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-	"Pinned %s files": "%s 件のファイルを固定",
-	"Removed pin from %s files": "%s 件のファイルの固定を解除",
-	"You started to help distribute <b>%s</b>.<br><small>Directory: %s</small>": "あなたはサイト: <b>%s</b> の配布の援助を開始しました。<br><small>ディレクトリ: %s</small>",
-	"Help distribute all new optional files on site <b>%s</b>": "サイト: <b>%s</b> のすべての新しいオプションファイルの配布を援助しますか?",
-	"Yes, I want to help!": "はい、やります!"
-}
diff --git a/plugins/OptionalManager/languages/pt-br.json b/plugins/OptionalManager/languages/pt-br.json
deleted file mode 100644
index 21d90cc0..00000000
--- a/plugins/OptionalManager/languages/pt-br.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-	"Pinned %s files": "Arquivos %s fixados",
-	"Removed pin from %s files": "Arquivos %s não estão fixados",
-	"You started to help distribute <b>%s</b>.<br><small>Directory: %s</small>": "Você começou a ajudar a distribuir <b>%s</b>.<br><small>Pasta: %s</small>",
-	"Help distribute all new optional files on site <b>%s</b>": "Ajude a distribuir todos os novos arquivos opcionais no site <b>%s</b>",
-	"Yes, I want to help!": "Sim, eu quero ajudar!"
-}
diff --git a/plugins/OptionalManager/languages/zh-tw.json b/plugins/OptionalManager/languages/zh-tw.json
deleted file mode 100644
index dfa9eaf3..00000000
--- a/plugins/OptionalManager/languages/zh-tw.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-	"Pinned %s files": "已固定 %s 個檔",
-	"Removed pin from %s files": "已解除固定 %s 個檔",
-	"You started to help distribute <b>%s</b>.<br><small>Directory: %s</small>": "你已經開始幫助分發 <b>%s</b> 。<br><small>目錄:%s</small>",
-	"Help distribute all new optional files on site <b>%s</b>": "你想要幫助分發 <b>%s</b> 網站的所有檔嗎?",
-	"Yes, I want to help!": "是,我想要幫助!"
-}
diff --git a/plugins/OptionalManager/languages/zh.json b/plugins/OptionalManager/languages/zh.json
deleted file mode 100644
index ae18118e..00000000
--- a/plugins/OptionalManager/languages/zh.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-	"Pinned %s files": "已固定 %s 个文件",
-	"Removed pin from %s files": "已解除固定 %s 个文件",
-	"You started to help distribute <b>%s</b>.<br><small>Directory: %s</small>": "您已经开始帮助分发 <b>%s</b> 。<br><small>目录:%s</small>",
-	"Help distribute all new optional files on site <b>%s</b>": "您想要帮助分发 <b>%s</b> 站点的所有文件吗?",
-	"Yes, I want to help!": "是,我想要帮助!"
-}
diff --git a/plugins/PeerDb/PeerDbPlugin.py b/plugins/PeerDb/PeerDbPlugin.py
deleted file mode 100644
index a66b81cf..00000000
--- a/plugins/PeerDb/PeerDbPlugin.py
+++ /dev/null
@@ -1,100 +0,0 @@
-import time
-import sqlite3
-import random
-import atexit
-
-import gevent
-from Plugin import PluginManager
-
-
-@PluginManager.registerTo("ContentDb")
-class ContentDbPlugin(object):
-    def __init__(self, *args, **kwargs):
-        atexit.register(self.saveAllPeers)
-        super(ContentDbPlugin, self).__init__(*args, **kwargs)
-
-    def getSchema(self):
-        schema = super(ContentDbPlugin, self).getSchema()
-
-        schema["tables"]["peer"] = {
-            "cols": [
-                ["site_id", "INTEGER REFERENCES site (site_id) ON DELETE CASCADE"],
-                ["address", "TEXT NOT NULL"],
-                ["port", "INTEGER NOT NULL"],
-                ["hashfield", "BLOB"],
-                ["reputation", "INTEGER NOT NULL"],
-                ["time_added", "INTEGER NOT NULL"],
-                ["time_found", "INTEGER NOT NULL"]
-            ],
-            "indexes": [
-                "CREATE UNIQUE INDEX peer_key ON peer (site_id, address, port)"
-            ],
-            "schema_changed": 2
-        }
-
-        return schema
-
-    def loadPeers(self, site):
-        s = time.time()
-        site_id = self.site_ids.get(site.address)
-        res = self.execute("SELECT * FROM peer WHERE site_id = :site_id", {"site_id": site_id})
-        num = 0
-        num_hashfield = 0
-        for row in res:
-            peer = site.addPeer(str(row["address"]), row["port"])
-            if not peer:  # Already exist
-                continue
-            if row["hashfield"]:
-                peer.hashfield.replaceFromBytes(row["hashfield"])
-                num_hashfield += 1
-            peer.time_added = row["time_added"]
-            peer.time_found = row["time_found"]
-            peer.reputation = row["reputation"]
-            if row["address"].endswith(".onion"):
-                peer.reputation = peer.reputation / 2 - 1 # Onion peers less likely working
-            num += 1
-        if num_hashfield:
-            site.content_manager.has_optional_files = True
-        site.log.debug("%s peers (%s with hashfield) loaded in %.3fs" % (num, num_hashfield, time.time() - s))
-
-    def iteratePeers(self, site):
-        site_id = self.site_ids.get(site.address)
-        for key, peer in list(site.peers.items()):
-            address, port = key.rsplit(":", 1)
-            if peer.has_hashfield:
-                hashfield = sqlite3.Binary(peer.hashfield.tobytes())
-            else:
-                hashfield = ""
-            yield (site_id, address, port, hashfield, peer.reputation, int(peer.time_added), int(peer.time_found))
-
-    def savePeers(self, site, spawn=False):
-        if spawn:
-            # Save peers every hour (+random some secs to not update very site at same time)
-            site.greenlet_manager.spawnLater(60 * 60 + random.randint(0, 60), self.savePeers, site, spawn=True)
-        if not site.peers:
-            site.log.debug("Peers not saved: No peers found")
-            return
-        s = time.time()
-        site_id = self.site_ids.get(site.address)
-        cur = self.getCursor()
-        try:
-            cur.execute("DELETE FROM peer WHERE site_id = :site_id", {"site_id": site_id})
-            cur.executemany(
-                "INSERT INTO peer (site_id, address, port, hashfield, reputation, time_added, time_found) VALUES (?, ?, ?, ?, ?, ?, ?)",
-                self.iteratePeers(site)
-            )
-        except Exception as err:
-            site.log.error("Save peer error: %s" % err)
-        site.log.debug("Peers saved in %.3fs" % (time.time() - s))
-
-    def initSite(self, site):
-        super(ContentDbPlugin, self).initSite(site)
-        site.greenlet_manager.spawnLater(0.5, self.loadPeers, site)
-        site.greenlet_manager.spawnLater(60*60, self.savePeers, site, spawn=True)
-
-    def saveAllPeers(self):
-        for site in list(self.sites.values()):
-            try:
-                self.savePeers(site)
-            except Exception as err:
-                site.log.error("Save peer error: %s" % err)
diff --git a/plugins/PeerDb/__init__.py b/plugins/PeerDb/__init__.py
deleted file mode 100644
index bc8c93b9..00000000
--- a/plugins/PeerDb/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-from . import PeerDbPlugin
-
diff --git a/plugins/PeerDb/plugin_info.json b/plugins/PeerDb/plugin_info.json
deleted file mode 100644
index b13915ff..00000000
--- a/plugins/PeerDb/plugin_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"name": "PeerDb",
-	"description": "Save/restore peer list on client restart.",
-	"default": "enabled"
-}
\ No newline at end of file
diff --git a/plugins/Sidebar/ConsolePlugin.py b/plugins/Sidebar/ConsolePlugin.py
deleted file mode 100644
index 15f6a1ba..00000000
--- a/plugins/Sidebar/ConsolePlugin.py
+++ /dev/null
@@ -1,101 +0,0 @@
-import re
-import logging
-
-from Plugin import PluginManager
-from Config import config
-from Debug import Debug
-from util import SafeRe
-from util.Flag import flag
-
-
-class WsLogStreamer(logging.StreamHandler):
-    def __init__(self, stream_id, ui_websocket, filter):
-        self.stream_id = stream_id
-        self.ui_websocket = ui_websocket
-
-        if filter:
-            if not SafeRe.isSafePattern(filter):
-                raise Exception("Not a safe prex pattern")
-            self.filter_re = re.compile(".*" + filter)
-        else:
-            self.filter_re = None
-        return super(WsLogStreamer, self).__init__()
-
-    def emit(self, record):
-        if self.ui_websocket.ws.closed:
-            self.stop()
-            return
-        line = self.format(record)
-        if self.filter_re and not self.filter_re.match(line):
-            return False
-
-        self.ui_websocket.cmd("logLineAdd", {"stream_id": self.stream_id, "lines": [line]})
-
-    def stop(self):
-        logging.getLogger('').removeHandler(self)
-
-
-@PluginManager.registerTo("UiWebsocket")
-class UiWebsocketPlugin(object):
-    def __init__(self, *args, **kwargs):
-        self.log_streamers = {}
-        return super(UiWebsocketPlugin, self).__init__(*args, **kwargs)
-
-    @flag.no_multiuser
-    @flag.admin
-    def actionConsoleLogRead(self, to, filter=None, read_size=32 * 1024, limit=500):
-        log_file_path = "%s/debug.log" % config.log_dir
-        log_file = open(log_file_path, encoding="utf-8")
-        log_file.seek(0, 2)
-        end_pos = log_file.tell()
-        log_file.seek(max(0, end_pos - read_size))
-        if log_file.tell() != 0:
-            log_file.readline()  # Partial line junk
-
-        pos_start = log_file.tell()
-        lines = []
-        if filter:
-            assert SafeRe.isSafePattern(filter)
-            filter_re = re.compile(".*" + filter)
-
-        last_match = False
-        for line in log_file:
-            if not line.startswith("[") and last_match:  # Multi-line log entry
-                lines.append(line.replace(" ", "&nbsp;"))
-                continue
-
-            if filter and not filter_re.match(line):
-                last_match = False
-                continue
-            last_match = True
-            lines.append(line)
-
-        num_found = len(lines)
-        lines = lines[-limit:]
-
-        return {"lines": lines, "pos_end": log_file.tell(), "pos_start": pos_start, "num_found": num_found}
-
-    def addLogStreamer(self, stream_id, filter=None):
-        logger = WsLogStreamer(stream_id, self, filter)
-        logger.setFormatter(logging.Formatter('[%(asctime)s] %(levelname)-8s %(name)s %(message)s'))
-        logger.setLevel(logging.getLevelName("DEBUG"))
-
-        logging.getLogger('').addHandler(logger)
-        return logger
-
-    @flag.no_multiuser
-    @flag.admin
-    def actionConsoleLogStream(self, to, filter=None):
-        stream_id = to
-        self.log_streamers[stream_id] = self.addLogStreamer(stream_id, filter)
-        self.response(to, {"stream_id": stream_id})
-
-    @flag.no_multiuser
-    @flag.admin
-    def actionConsoleLogStreamRemove(self, to, stream_id):
-        try:
-            self.log_streamers[stream_id].stop()
-            del self.log_streamers[stream_id]
-            return "ok"
-        except Exception as err:
-            return {"error": Debug.formatException(err)}
diff --git a/plugins/Sidebar/SidebarPlugin.py b/plugins/Sidebar/SidebarPlugin.py
deleted file mode 100644
index 4ecca75a..00000000
--- a/plugins/Sidebar/SidebarPlugin.py
+++ /dev/null
@@ -1,805 +0,0 @@
-import re
-import os
-import html
-import sys
-import math
-import time
-import json
-import io
-import urllib
-import urllib.parse
-
-import gevent
-
-import util
-from Config import config
-from Plugin import PluginManager
-from Debug import Debug
-from Translate import Translate
-from util import helper
-from util.Flag import flag
-from .ZipStream import ZipStream
-
-plugin_dir = os.path.dirname(__file__)
-media_dir = plugin_dir + "/media"
-
-loc_cache = {}
-if "_" not in locals():
-    _ = Translate(plugin_dir + "/languages/")
-
-
-@PluginManager.registerTo("UiRequest")
-class UiRequestPlugin(object):
-    # Inject our resources to end of original file streams
-    def actionUiMedia(self, path):
-        if path == "/uimedia/all.js" or path == "/uimedia/all.css":
-            # First yield the original file and header
-            body_generator = super(UiRequestPlugin, self).actionUiMedia(path)
-            for part in body_generator:
-                yield part
-
-            # Append our media file to the end
-            ext = re.match(".*(js|css)$", path).group(1)
-            plugin_media_file = "%s/all.%s" % (media_dir, ext)
-            if config.debug:
-                # If debugging merge *.css to all.css and *.js to all.js
-                from Debug import DebugMedia
-                DebugMedia.merge(plugin_media_file)
-            if ext == "js":
-                yield _.translateData(open(plugin_media_file).read()).encode("utf8")
-            else:
-                for part in self.actionFile(plugin_media_file, send_header=False):
-                    yield part
-        elif path.startswith("/uimedia/globe/"):  # Serve WebGL globe files
-            file_name = re.match(".*/(.*)", path).group(1)
-            plugin_media_file = "%s_globe/%s" % (media_dir, file_name)
-            if config.debug and path.endswith("all.js"):
-                # If debugging merge *.css to all.css and *.js to all.js
-                from Debug import DebugMedia
-                DebugMedia.merge(plugin_media_file)
-            for part in self.actionFile(plugin_media_file):
-                yield part
-        else:
-            for part in super(UiRequestPlugin, self).actionUiMedia(path):
-                yield part
-
-    def actionZip(self):
-        address = self.get["address"]
-        site = self.server.site_manager.get(address)
-        if not site:
-            return self.error404("Site not found")
-
-        title = site.content_manager.contents.get("content.json", {}).get("title", "")
-        filename = "%s-backup-%s.zip" % (title, time.strftime("%Y-%m-%d_%H_%M"))
-        filename_quoted = urllib.parse.quote(filename)
-        self.sendHeader(content_type="application/zip", extra_headers={'Content-Disposition': 'attachment; filename="%s"' % filename_quoted})
-
-        return self.streamZip(site.storage.getPath("."))
-
-    def streamZip(self, dir_path):
-        zs = ZipStream(dir_path)
-        while 1:
-            data = zs.read()
-            if not data:
-                break
-            yield data
-
-
-@PluginManager.registerTo("UiWebsocket")
-class UiWebsocketPlugin(object):
-    def sidebarRenderPeerStats(self, body, site):
-        connected = len([peer for peer in list(site.peers.values()) if peer.connection and peer.connection.connected])
-        connectable = len([peer_id for peer_id in list(site.peers.keys()) if not peer_id.endswith(":0")])
-        onion = len([peer_id for peer_id in list(site.peers.keys()) if ".onion" in peer_id])
-        local = len([peer for peer in list(site.peers.values()) if helper.isPrivateIp(peer.ip)])
-        peers_total = len(site.peers)
-
-        # Add myself
-        if site.isServing():
-            peers_total += 1
-            if any(site.connection_server.port_opened.values()):
-                connectable += 1
-            if site.connection_server.tor_manager.start_onions:
-                onion += 1
-
-        if peers_total:
-            percent_connected = float(connected) / peers_total
-            percent_connectable = float(connectable) / peers_total
-            percent_onion = float(onion) / peers_total
-        else:
-            percent_connectable = percent_connected = percent_onion = 0
-
-        if local:
-            local_html = _("<li class='color-yellow'><span>{_[Local]}:</span><b>{local}</b></li>")
-        else:
-            local_html = ""
-
-        peer_ips = [peer.key for peer in site.getConnectablePeers(20, allow_private=False)]
-        peer_ips.sort(key=lambda peer_ip: ".onion:" in peer_ip)
-        copy_link = "http://127.0.0.1:43110/%s/?zeronet_peers=%s" % (
-            site.content_manager.contents.get("content.json", {}).get("domain", site.address),
-            ",".join(peer_ips)
-        )
-
-        body.append(_("""
-            <li>
-             <label>
-              {_[Peers]}
-              <small class="label-right"><a href='{copy_link}' id='link-copypeers' class='link-right'>{_[Copy to clipboard]}</a></small>
-             </label>
-             <ul class='graph'>
-              <li style='width: 100%' class='total back-black' title="{_[Total peers]}"></li>
-              <li style='width: {percent_connectable:.0%}' class='connectable back-blue' title='{_[Connectable peers]}'></li>
-              <li style='width: {percent_onion:.0%}' class='connected back-purple' title='{_[Onion]}'></li>
-              <li style='width: {percent_connected:.0%}' class='connected back-green' title='{_[Connected peers]}'></li>
-             </ul>
-             <ul class='graph-legend'>
-              <li class='color-green'><span>{_[Connected]}:</span><b>{connected}</b></li>
-              <li class='color-blue'><span>{_[Connectable]}:</span><b>{connectable}</b></li>
-              <li class='color-purple'><span>{_[Onion]}:</span><b>{onion}</b></li>
-              {local_html}
-              <li class='color-black'><span>{_[Total]}:</span><b>{peers_total}</b></li>
-             </ul>
-            </li>
-        """.replace("{local_html}", local_html)))
-
-    def sidebarRenderTransferStats(self, body, site):
-        recv = float(site.settings.get("bytes_recv", 0)) / 1024 / 1024
-        sent = float(site.settings.get("bytes_sent", 0)) / 1024 / 1024
-        transfer_total = recv + sent
-        if transfer_total:
-            percent_recv = recv / transfer_total
-            percent_sent = sent / transfer_total
-        else:
-            percent_recv = 0.5
-            percent_sent = 0.5
-
-        body.append(_("""
-            <li>
-             <label>{_[Data transfer]}</label>
-             <ul class='graph graph-stacked'>
-              <li style='width: {percent_recv:.0%}' class='received back-yellow' title="{_[Received bytes]}"></li>
-              <li style='width: {percent_sent:.0%}' class='sent back-green' title="{_[Sent bytes]}"></li>
-             </ul>
-             <ul class='graph-legend'>
-              <li class='color-yellow'><span>{_[Received]}:</span><b>{recv:.2f}MB</b></li>
-              <li class='color-green'<span>{_[Sent]}:</span><b>{sent:.2f}MB</b></li>
-             </ul>
-            </li>
-        """))
-
-    def sidebarRenderFileStats(self, body, site):
-        body.append(_("""
-            <li>
-             <label>
-              {_[Files]}
-              <a href='/list/{site.address}' class='link-right link-outline' id="browse-files">{_[Browse files]}</a>
-              <small class="label-right">
-               <a href='/ZeroNet-Internal/Zip?address={site.address}' id='link-zip' class='link-right' download='site.zip'>{_[Save as .zip]}</a>
-              </small>
-             </label>
-             <ul class='graph graph-stacked'>
-        """))
-
-        extensions = (
-            ("html", "yellow"),
-            ("css", "orange"),
-            ("js", "purple"),
-            ("Image", "green"),
-            ("json", "darkblue"),
-            ("User data", "blue"),
-            ("Other", "white"),
-            ("Total", "black")
-        )
-        # Collect stats
-        size_filetypes = {}
-        size_total = 0
-        contents = site.content_manager.listContents()  # Without user files
-        for inner_path in contents:
-            content = site.content_manager.contents[inner_path]
-            if "files" not in content or content["files"] is None:
-                continue
-            for file_name, file_details in list(content["files"].items()):
-                size_total += file_details["size"]
-                ext = file_name.split(".")[-1]
-                size_filetypes[ext] = size_filetypes.get(ext, 0) + file_details["size"]
-
-        # Get user file sizes
-        size_user_content = site.content_manager.contents.execute(
-            "SELECT SUM(size) + SUM(size_files) AS size FROM content WHERE ?",
-            {"not__inner_path": contents}
-        ).fetchone()["size"]
-        if not size_user_content:
-            size_user_content = 0
-        size_filetypes["User data"] = size_user_content
-        size_total += size_user_content
-
-        # The missing difference is content.json sizes
-        if "json" in size_filetypes:
-            size_filetypes["json"] += max(0, site.settings["size"] - size_total)
-        size_total = size_other = site.settings["size"]
-
-        # Bar
-        for extension, color in extensions:
-            if extension == "Total":
-                continue
-            if extension == "Other":
-                size = max(0, size_other)
-            elif extension == "Image":
-                size = size_filetypes.get("jpg", 0) + size_filetypes.get("png", 0) + size_filetypes.get("gif", 0)
-                size_other -= size
-            else:
-                size = size_filetypes.get(extension, 0)
-                size_other -= size
-            if size_total == 0:
-                percent = 0
-            else:
-                percent = 100 * (float(size) / size_total)
-            percent = math.floor(percent * 100) / 100  # Floor to 2 digits
-            body.append(
-                """<li style='width: %.2f%%' class='%s back-%s' title="%s"></li>""" %
-                (percent, _[extension], color, _[extension])
-            )
-
-        # Legend
-        body.append("</ul><ul class='graph-legend'>")
-        for extension, color in extensions:
-            if extension == "Other":
-                size = max(0, size_other)
-            elif extension == "Image":
-                size = size_filetypes.get("jpg", 0) + size_filetypes.get("png", 0) + size_filetypes.get("gif", 0)
-            elif extension == "Total":
-                size = size_total
-            else:
-                size = size_filetypes.get(extension, 0)
-
-            if extension == "js":
-                title = "javascript"
-            else:
-                title = extension
-
-            if size > 1024 * 1024 * 10:  # Format as mB is more than 10mB
-                size_formatted = "%.0fMB" % (size / 1024 / 1024)
-            else:
-                size_formatted = "%.0fkB" % (size / 1024)
-
-            body.append("<li class='color-%s'><span>%s:</span><b>%s</b></li>" % (color, _[title], size_formatted))
-
-        body.append("</ul></li>")
-
-    def sidebarRenderSizeLimit(self, body, site):
-        free_space = helper.getFreeSpace() / 1024 / 1024
-        size = float(site.settings["size"]) / 1024 / 1024
-        size_limit = site.getSizeLimit()
-        percent_used = size / size_limit
-
-        body.append(_("""
-            <li>
-             <label>{_[Size limit]} <small>({_[limit used]}: {percent_used:.0%}, {_[free space]}: {free_space:,.0f}MB)</small></label>
-             <input type='text' class='text text-num' value="{size_limit}" id='input-sitelimit'/><span class='text-post'>MB</span>
-             <a href='#Set' class='button' id='button-sitelimit'>{_[Set]}</a>
-            </li>
-        """))
-
-    def sidebarRenderOptionalFileStats(self, body, site):
-        size_total = float(site.settings["size_optional"])
-        size_downloaded = float(site.settings["optional_downloaded"])
-
-        if not size_total:
-            return False
-
-        percent_downloaded = size_downloaded / size_total
-
-        size_formatted_total = size_total / 1024 / 1024
-        size_formatted_downloaded = size_downloaded / 1024 / 1024
-
-        body.append(_("""
-            <li>
-             <label>{_[Optional files]}</label>
-             <ul class='graph'>
-              <li style='width: 100%' class='total back-black' title="{_[Total size]}"></li>
-              <li style='width: {percent_downloaded:.0%}' class='connected back-green' title='{_[Downloaded files]}'></li>
-             </ul>
-             <ul class='graph-legend'>
-              <li class='color-green'><span>{_[Downloaded]}:</span><b>{size_formatted_downloaded:.2f}MB</b></li>
-              <li class='color-black'><span>{_[Total]}:</span><b>{size_formatted_total:.2f}MB</b></li>
-             </ul>
-            </li>
-        """))
-
-        return True
-
-    def sidebarRenderOptionalFileSettings(self, body, site):
-        if self.site.settings.get("autodownloadoptional"):
-            checked = "checked='checked'"
-        else:
-            checked = ""
-
-        body.append(_("""
-            <li>
-             <label>{_[Help distribute added optional files]}</label>
-             <input type="checkbox" class="checkbox" id="checkbox-autodownloadoptional" {checked}/><div class="checkbox-skin"></div>
-        """))
-
-        if hasattr(config, "autodownload_bigfile_size_limit"):
-            autodownload_bigfile_size_limit = int(site.settings.get("autodownload_bigfile_size_limit", config.autodownload_bigfile_size_limit))
-            body.append(_("""
-                <div class='settings-autodownloadoptional'>
-                 <label>{_[Auto download big file size limit]}</label>
-                 <input type='text' class='text text-num' value="{autodownload_bigfile_size_limit}" id='input-autodownload_bigfile_size_limit'/><span class='text-post'>MB</span>
-                 <a href='#Set' class='button' id='button-autodownload_bigfile_size_limit'>{_[Set]}</a>
-                 <a href='#Download+previous' class='button' id='button-autodownload_previous'>{_[Download previous files]}</a>
-                </div>
-            """))
-        body.append("</li>")
-
-    def sidebarRenderBadFiles(self, body, site):
-        body.append(_("""
-            <li>
-             <label>{_[Needs to be updated]}:</label>
-             <ul class='filelist'>
-        """))
-
-        i = 0
-        for bad_file, tries in site.bad_files.items():
-            i += 1
-            body.append(_("""<li class='color-red' title="{bad_file_path} ({tries})">{bad_filename}</li>""", {
-                "bad_file_path": bad_file,
-                "bad_filename": helper.getFilename(bad_file),
-                "tries": _.pluralize(tries, "{} try", "{} tries")
-            }))
-            if i > 30:
-                break
-
-        if len(site.bad_files) > 30:
-            num_bad_files = len(site.bad_files) - 30
-            body.append(_("""<li class='color-red'>{_[+ {num_bad_files} more]}</li>""", nested=True))
-
-        body.append("""
-             </ul>
-            </li>
-        """)
-
-    def sidebarRenderDbOptions(self, body, site):
-        if site.storage.db:
-            inner_path = site.storage.getInnerPath(site.storage.db.db_path)
-            size = float(site.storage.getSize(inner_path)) / 1024
-            feeds = len(site.storage.db.schema.get("feeds", {}))
-        else:
-            inner_path = _["No database found"]
-            size = 0.0
-            feeds = 0
-
-        body.append(_("""
-            <li>
-             <label>{_[Database]} <small>({size:.2f}kB, {_[search feeds]}: {_[{feeds} query]})</small></label>
-             <div class='flex'>
-              <input type='text' class='text disabled' value="{inner_path}" disabled='disabled'/>
-              <a href='#Reload' id="button-dbreload" class='button'>{_[Reload]}</a>
-              <a href='#Rebuild' id="button-dbrebuild" class='button'>{_[Rebuild]}</a>
-             </div>
-            </li>
-        """, nested=True))
-
-    def sidebarRenderIdentity(self, body, site):
-        auth_address = self.user.getAuthAddress(self.site.address, create=False)
-        rules = self.site.content_manager.getRules("data/users/%s/content.json" % auth_address)
-        if rules and rules.get("max_size"):
-            quota = rules["max_size"] / 1024
-            try:
-                content = site.content_manager.contents["data/users/%s/content.json" % auth_address]
-                used = len(json.dumps(content)) + sum([file["size"] for file in list(content["files"].values())])
-            except:
-                used = 0
-            used = used / 1024
-        else:
-            quota = used = 0
-
-        body.append(_("""
-            <li>
-             <label>{_[Identity address]} <small>({_[limit used]}: {used:.2f}kB / {quota:.2f}kB)</small></label>
-             <div class='flex'>
-              <span class='input text disabled'>{auth_address}</span>
-              <a href='#Change' class='button' id='button-identity'>{_[Change]}</a>
-             </div>
-            </li>
-        """))
-
-    def sidebarRenderControls(self, body, site):
-        auth_address = self.user.getAuthAddress(self.site.address, create=False)
-        if self.site.settings["serving"]:
-            class_pause = ""
-            class_resume = "hidden"
-        else:
-            class_pause = "hidden"
-            class_resume = ""
-
-        body.append(_("""
-            <li>
-             <label>{_[Site control]}</label>
-             <a href='#Update' class='button noupdate' id='button-update'>{_[Update]}</a>
-             <a href='#Pause' class='button {class_pause}' id='button-pause'>{_[Pause]}</a>
-             <a href='#Resume' class='button {class_resume}' id='button-resume'>{_[Resume]}</a>
-             <a href='#Delete' class='button noupdate' id='button-delete'>{_[Delete]}</a>
-            </li>
-        """))
-
-        donate_key = site.content_manager.contents.get("content.json", {}).get("donate", True)
-        site_address = self.site.address
-        body.append(_("""
-            <li>
-             <label>{_[Site address]}</label><br>
-             <div class='flex'>
-              <span class='input text disabled'>{site_address}</span>
-        """))
-        if donate_key == False or donate_key == "":
-            pass
-        elif (type(donate_key) == str or type(donate_key) == str) and len(donate_key) > 0:
-            body.append(_("""
-             </div>
-            </li>
-            <li>
-             <label>{_[Donate]}</label><br>
-             <div class='flex'>
-             {donate_key}
-            """))
-        else:
-            body.append(_("""
-              <a href='bitcoin:{site_address}' class='button' id='button-donate'>{_[Donate]}</a>
-            """))
-        body.append(_("""
-             </div>
-            </li>
-        """))
-
-    def sidebarRenderOwnedCheckbox(self, body, site):
-        if self.site.settings["own"]:
-            checked = "checked='checked'"
-        else:
-            checked = ""
-
-        body.append(_("""
-            <h2 class='owned-title'>{_[This is my site]}</h2>
-            <input type="checkbox" class="checkbox" id="checkbox-owned" {checked}/><div class="checkbox-skin"></div>
-        """))
-
-    def sidebarRenderOwnSettings(self, body, site):
-        title = site.content_manager.contents.get("content.json", {}).get("title", "")
-        description = site.content_manager.contents.get("content.json", {}).get("description", "")
-
-        body.append(_("""
-            <li>
-             <label for='settings-title'>{_[Site title]}</label>
-             <input type='text' class='text' value="{title}" id='settings-title'/>
-            </li>
-
-            <li>
-             <label for='settings-description'>{_[Site description]}</label>
-             <input type='text' class='text' value="{description}" id='settings-description'/>
-            </li>
-
-            <li>
-             <a href='#Save' class='button' id='button-settings'>{_[Save site settings]}</a>
-            </li>
-        """))
-
-    def sidebarRenderContents(self, body, site):
-        has_privatekey = bool(self.user.getSiteData(site.address, create=False).get("privatekey"))
-        if has_privatekey:
-            tag_privatekey = _("{_[Private key saved.]} <a href='#Forget+private+key' id='privatekey-forget' class='link-right'>{_[Forget]}</a>")
-        else:
-            tag_privatekey = _("<a href='#Add+private+key' id='privatekey-add' class='link-right'>{_[Add saved private key]}</a>")
-
-        body.append(_("""
-            <li>
-             <label>{_[Content publishing]} <small class='label-right'>{tag_privatekey}</small></label>
-        """.replace("{tag_privatekey}", tag_privatekey)))
-
-        # Choose content you want to sign
-        body.append(_("""
-             <div class='flex'>
-              <input type='text' class='text' value="content.json" id='input-contents'/>
-              <a href='#Sign-and-Publish' id='button-sign-publish' class='button'>{_[Sign and publish]}</a>
-              <a href='#Sign-or-Publish' id='menu-sign-publish'>\u22EE</a>
-             </div>
-        """))
-
-        contents = ["content.json"]
-        contents += list(site.content_manager.contents.get("content.json", {}).get("includes", {}).keys())
-        body.append(_("<div class='contents'>{_[Choose]}: "))
-        for content in contents:
-            body.append(_("<a href='{content}' class='contents-content'>{content}</a> "))
-        body.append("</div>")
-        body.append("</li>")
-
-    @flag.admin
-    def actionSidebarGetHtmlTag(self, to):
-        site = self.site
-
-        body = []
-
-        body.append("<div>")
-        body.append("<a href='#Close' class='close'>&times;</a>")
-        body.append("<h1>%s</h1>" % html.escape(site.content_manager.contents.get("content.json", {}).get("title", ""), True))
-
-        body.append("<div class='globe loading'></div>")
-
-        body.append("<ul class='fields'>")
-
-        self.sidebarRenderPeerStats(body, site)
-        self.sidebarRenderTransferStats(body, site)
-        self.sidebarRenderFileStats(body, site)
-        self.sidebarRenderSizeLimit(body, site)
-        has_optional = self.sidebarRenderOptionalFileStats(body, site)
-        if has_optional:
-            self.sidebarRenderOptionalFileSettings(body, site)
-        self.sidebarRenderDbOptions(body, site)
-        self.sidebarRenderIdentity(body, site)
-        self.sidebarRenderControls(body, site)
-        if site.bad_files:
-            self.sidebarRenderBadFiles(body, site)
-
-        self.sidebarRenderOwnedCheckbox(body, site)
-        body.append("<div class='settings-owned'>")
-        self.sidebarRenderOwnSettings(body, site)
-        self.sidebarRenderContents(body, site)
-        body.append("</div>")
-        body.append("</ul>")
-        body.append("</div>")
-
-        body.append("<div class='menu template'>")
-        body.append("<a href='#'' class='menu-item template'>Template</a>")
-        body.append("</div>")
-
-        self.response(to, "".join(body))
-
-    def downloadGeoLiteDb(self, db_path):
-        import gzip
-        import shutil
-        from util import helper
-
-        if config.offline:
-            return False
-
-        self.log.info("Downloading GeoLite2 City database...")
-        self.cmd("progress", ["geolite-info", _["Downloading GeoLite2 City database (one time only, ~20MB)..."], 0])
-        db_urls = [
-            "https://raw.githubusercontent.com/aemr3/GeoLite2-Database/master/GeoLite2-City.mmdb.gz",
-            "https://raw.githubusercontent.com/texnikru/GeoLite2-Database/master/GeoLite2-City.mmdb.gz"
-        ]
-        for db_url in db_urls:
-            downloadl_err = None
-            try:
-                # Download
-                response = helper.httpRequest(db_url)
-                data_size = response.getheader('content-length')
-                data_recv = 0
-                data = io.BytesIO()
-                while True:
-                    buff = response.read(1024 * 512)
-                    if not buff:
-                        break
-                    data.write(buff)
-                    data_recv += 1024 * 512
-                    if data_size:
-                        progress = int(float(data_recv) / int(data_size) * 100)
-                        self.cmd("progress", ["geolite-info", _["Downloading GeoLite2 City database (one time only, ~20MB)..."], progress])
-                self.log.info("GeoLite2 City database downloaded (%s bytes), unpacking..." % data.tell())
-                data.seek(0)
-
-                # Unpack
-                with gzip.GzipFile(fileobj=data) as gzip_file:
-                    shutil.copyfileobj(gzip_file, open(db_path, "wb"))
-
-                self.cmd("progress", ["geolite-info", _["GeoLite2 City database downloaded!"], 100])
-                time.sleep(2)  # Wait for notify animation
-                self.log.info("GeoLite2 City database is ready at: %s" % db_path)
-                return True
-            except Exception as err:
-                download_err = err
-                self.log.error("Error downloading %s: %s" % (db_url, err))
-                pass
-        self.cmd("progress", [
-            "geolite-info",
-            _["GeoLite2 City database download error: {}!<br>Please download manually and unpack to data dir:<br>{}"].format(download_err, db_urls[0]),
-            -100
-        ])
-
-    def getLoc(self, geodb, ip):
-        global loc_cache
-
-        if ip in loc_cache:
-            return loc_cache[ip]
-        else:
-            try:
-                loc_data = geodb.get(ip)
-            except:
-                loc_data = None
-
-            if not loc_data or "location" not in loc_data:
-                loc_cache[ip] = None
-                return None
-
-            loc = {
-                "lat": loc_data["location"]["latitude"],
-                "lon": loc_data["location"]["longitude"],
-            }
-            if "city" in loc_data:
-                loc["city"] = loc_data["city"]["names"]["en"]
-
-            if "country" in loc_data:
-                loc["country"] = loc_data["country"]["names"]["en"]
-
-            loc_cache[ip] = loc
-            return loc
-
-    @util.Noparallel()
-    def getGeoipDb(self):
-        db_name = 'GeoLite2-City.mmdb'
-
-        sys_db_paths = []
-        if sys.platform == "linux":
-            sys_db_paths += ['/usr/share/GeoIP/' + db_name]
-
-        data_dir_db_path = os.path.join(config.data_dir, db_name)
-
-        db_paths = sys_db_paths + [data_dir_db_path]
-
-        for path in db_paths:
-            if os.path.isfile(path) and os.path.getsize(path) > 0:
-                return path
-
-        self.log.info("GeoIP database not found at [%s]. Downloading to: %s",
-                " ".join(db_paths), data_dir_db_path)
-        if self.downloadGeoLiteDb(data_dir_db_path):
-            return data_dir_db_path
-        return None
-
-    def getPeerLocations(self, peers):
-        import maxminddb
-
-        db_path = self.getGeoipDb()
-        if not db_path:
-            self.log.debug("Not showing peer locations: no GeoIP database")
-            return False
-
-        geodb = maxminddb.open_database(db_path)
-
-        peers = list(peers.values())
-        # Place bars
-        peer_locations = []
-        placed = {}  # Already placed bars here
-        for peer in peers:
-            # Height of bar
-            if peer.connection and peer.connection.last_ping_delay:
-                ping = round(peer.connection.last_ping_delay * 1000)
-            else:
-                ping = None
-            loc = self.getLoc(geodb, peer.ip)
-
-            if not loc:
-                continue
-            # Create position array
-            lat, lon = loc["lat"], loc["lon"]
-            latlon = "%s,%s" % (lat, lon)
-            if latlon in placed and helper.getIpType(peer.ip) == "ipv4":  # Dont place more than 1 bar to same place, fake repos using ip address last two part
-                lat += float(128 - int(peer.ip.split(".")[-2])) / 50
-                lon += float(128 - int(peer.ip.split(".")[-1])) / 50
-                latlon = "%s,%s" % (lat, lon)
-            placed[latlon] = True
-            peer_location = {}
-            peer_location.update(loc)
-            peer_location["lat"] = lat
-            peer_location["lon"] = lon
-            peer_location["ping"] = ping
-
-            peer_locations.append(peer_location)
-
-        # Append myself
-        for ip in self.site.connection_server.ip_external_list:
-            my_loc = self.getLoc(geodb, ip)
-            if my_loc:
-                my_loc["ping"] = 0
-                peer_locations.append(my_loc)
-
-        return peer_locations
-
-    @flag.admin
-    @flag.async_run
-    def actionSidebarGetPeers(self, to):
-        try:
-            peer_locations = self.getPeerLocations(self.site.peers)
-            globe_data = []
-            ping_times = [
-                peer_location["ping"]
-                for peer_location in peer_locations
-                if peer_location["ping"]
-            ]
-            if ping_times:
-                ping_avg = sum(ping_times) / float(len(ping_times))
-            else:
-                ping_avg = 0
-
-            for peer_location in peer_locations:
-                if peer_location["ping"] == 0:  # Me
-                    height = -0.135
-                elif peer_location["ping"]:
-                    height = min(0.20, math.log(1 + peer_location["ping"] / ping_avg, 300))
-                else:
-                    height = -0.03
-
-                globe_data += [peer_location["lat"], peer_location["lon"], height]
-
-            self.response(to, globe_data)
-        except Exception as err:
-            self.log.debug("sidebarGetPeers error: %s" % Debug.formatException(err))
-            self.response(to, {"error": str(err)})
-
-    @flag.admin
-    @flag.no_multiuser
-    def actionSiteSetOwned(self, to, owned):
-        if self.site.address == config.updatesite:
-            return {"error": "You can't change the ownership of the updater site"}
-
-        self.site.settings["own"] = bool(owned)
-        self.site.updateWebsocket(owned=owned)
-        return "ok"
-
-    @flag.admin
-    @flag.no_multiuser
-    def actionSiteRecoverPrivatekey(self, to):
-        from Crypt import CryptBitcoin
-
-        site_data = self.user.sites[self.site.address]
-        if site_data.get("privatekey"):
-            return {"error": "This site already has saved privated key"}
-
-        address_index = self.site.content_manager.contents.get("content.json", {}).get("address_index")
-        if not address_index:
-            return {"error": "No address_index in content.json"}
-
-        privatekey = CryptBitcoin.hdPrivatekey(self.user.master_seed, address_index)
-        privatekey_address = CryptBitcoin.privatekeyToAddress(privatekey)
-
-        if privatekey_address == self.site.address:
-            site_data["privatekey"] = privatekey
-            self.user.save()
-            self.site.updateWebsocket(recover_privatekey=True)
-            return "ok"
-        else:
-            return {"error": "Unable to deliver private key for this site from current user's master_seed"}
-
-    @flag.admin
-    @flag.no_multiuser
-    def actionUserSetSitePrivatekey(self, to, privatekey):
-        site_data = self.user.sites[self.site.address]
-        site_data["privatekey"] = privatekey
-        self.site.updateWebsocket(set_privatekey=bool(privatekey))
-        self.user.save()
-
-        return "ok"
-
-    @flag.admin
-    @flag.no_multiuser
-    def actionSiteSetAutodownloadoptional(self, to, owned):
-        self.site.settings["autodownloadoptional"] = bool(owned)
-        self.site.worker_manager.removeSolvedFileTasks()
-
-    @flag.no_multiuser
-    @flag.admin
-    def actionDbReload(self, to):
-        self.site.storage.closeDb()
-        self.site.storage.getDb()
-
-        return self.response(to, "ok")
-
-    @flag.no_multiuser
-    @flag.admin
-    def actionDbRebuild(self, to):
-        try:
-            self.site.storage.rebuildDb()
-        except Exception as err:
-            return self.response(to, {"error": str(err)})
-
-
-        return self.response(to, "ok")
diff --git a/plugins/Sidebar/ZipStream.py b/plugins/Sidebar/ZipStream.py
deleted file mode 100644
index b6e05b21..00000000
--- a/plugins/Sidebar/ZipStream.py
+++ /dev/null
@@ -1,59 +0,0 @@
-import io
-import os
-import zipfile
-
-class ZipStream(object):
-    def __init__(self, dir_path):
-        self.dir_path = dir_path
-        self.pos = 0
-        self.buff_pos = 0
-        self.zf = zipfile.ZipFile(self, 'w', zipfile.ZIP_DEFLATED, allowZip64=True)
-        self.buff = io.BytesIO()
-        self.file_list = self.getFileList()
-
-    def getFileList(self):
-        for root, dirs, files in os.walk(self.dir_path):
-            for file in files:
-                file_path = root + "/" + file
-                relative_path = os.path.join(os.path.relpath(root, self.dir_path), file)
-                yield file_path, relative_path
-        self.zf.close()
-
-    def read(self, size=60 * 1024):
-        for file_path, relative_path in self.file_list:
-            self.zf.write(file_path, relative_path)
-            if self.buff.tell() >= size:
-                break
-        self.buff.seek(0)
-        back = self.buff.read()
-        self.buff.truncate(0)
-        self.buff.seek(0)
-        self.buff_pos += len(back)
-        return back
-
-    def write(self, data):
-        self.pos += len(data)
-        self.buff.write(data)
-
-    def tell(self):
-        return self.pos
-
-    def seek(self, pos, whence=0):
-        if pos >= self.buff_pos:
-            self.buff.seek(pos - self.buff_pos, whence)
-            self.pos = pos
-
-    def flush(self):
-        pass
-
-
-if __name__ == "__main__":
-    zs = ZipStream(".")
-    out = open("out.zip", "wb")
-    while 1:
-        data = zs.read()
-        print("Write %s" % len(data))
-        if not data:
-            break
-        out.write(data)
-    out.close()
diff --git a/plugins/Sidebar/__init__.py b/plugins/Sidebar/__init__.py
deleted file mode 100644
index be7f14e1..00000000
--- a/plugins/Sidebar/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-from . import SidebarPlugin
-from . import ConsolePlugin
\ No newline at end of file
diff --git a/plugins/Sidebar/languages/da.json b/plugins/Sidebar/languages/da.json
deleted file mode 100644
index a421292c..00000000
--- a/plugins/Sidebar/languages/da.json
+++ /dev/null
@@ -1,81 +0,0 @@
-{
-	"Peers": "Klienter",
-	"Connected": "Forbundet",
-	"Connectable": "Mulige",
-	"Connectable peers": "Mulige klienter",
-
-	"Data transfer": "Data overførsel",
-	"Received": "Modtaget",
-	"Received bytes": "Bytes modtaget",
-	"Sent": "Sendt",
-	"Sent bytes": "Bytes sendt",
-
-	"Files": "Filer",
-	"Total": "I alt",
-	"Image": "Image",
-	"Other": "Andet",
-	"User data": "Bruger data",
-
-	"Size limit": "Side max størrelse",
-	"limit used": "brugt",
-	"free space": "fri",
-	"Set": "Opdater",
-
-	"Optional files": "Valgfri filer",
-	"Downloaded": "Downloadet",
-	"Download and help distribute all files": "Download og hjælp med at dele filer",
-	"Total size": "Størrelse i alt",
-	"Downloaded files": "Filer downloadet",
-
-	"Database": "Database",
-	"search feeds": "søgninger",
-	"{feeds} query": "{feeds} søgninger",
-	"Reload": "Genindlæs",
-	"Rebuild": "Genopbyg",
-	"No database found": "Ingen database fundet",
-
-	"Identity address": "Autorisations ID",
-	"Change": "Skift",
-
-	"Update": "Opdater",
-	"Pause": "Pause",
-	"Resume": "Aktiv",
-	"Delete": "Slet",
-	"Are you sure?": "Er du sikker?",
-
-	"Site address": "Side addresse",
-	"Donate": "Doner penge",
-
-	"Missing files": "Manglende filer",
-	"{} try": "{} forsøg",
-	"{} tries": "{} forsøg",
-	"+ {num_bad_files} more": "+ {num_bad_files} mere",
-
-	"This is my site": "Dette er min side",
-	"Site title": "Side navn",
-	"Site description": "Side beskrivelse",
-	"Save site settings": "Gem side opsætning",
-
-	"Content publishing": "Indhold offentliggøres",
-	"Choose": "Vælg",
-	"Sign": "Signer",
-	"Publish": "Offentliggør",
-
-	"This function is disabled on this proxy": "Denne funktion er slået fra på denne ZeroNet proxyEz a funkció ki van kapcsolva ezen a proxy-n",
-	"GeoLite2 City database download error: {}!<br>Please download manually and unpack to data dir:<br>{}": "GeoLite2 City database kunne ikke downloades: {}!<br>Download venligst databasen manuelt og udpak i data folder:<br>{}",
-	"Downloading GeoLite2 City database (one time only, ~20MB)...": "GeoLite2 város adatbázis letöltése (csak egyszer kell, kb 20MB)...",
-	"GeoLite2 City database downloaded!": "GeoLite2 City database downloadet!",
-
-	"Are you sure?": "Er du sikker?",
-	"Site storage limit modified!": "Side max størrelse ændret!",
-	"Database schema reloaded!": "Database definition genindlæst!",
-	"Database rebuilding....": "Genopbygger database...",
-	"Database rebuilt!": "Database genopbygget!",
-	"Site updated!": "Side opdateret!",
-	"Delete this site": "Slet denne side",
-	"File write error: ": "Fejl ved skrivning af fil: ",
-	"Site settings saved!": "Side opsætning gemt!",
-	"Enter your private key:": "Indtast din private nøgle:",
-	" Signed!": " Signeret!",
-	"WebGL not supported": "WebGL er ikke supporteret"
-}
\ No newline at end of file
diff --git a/plugins/Sidebar/languages/de.json b/plugins/Sidebar/languages/de.json
deleted file mode 100644
index 2f5feacd..00000000
--- a/plugins/Sidebar/languages/de.json
+++ /dev/null
@@ -1,81 +0,0 @@
-{
-	"Peers": "Peers",
-	"Connected": "Verbunden",
-	"Connectable": "Verbindbar",
-	"Connectable peers": "Verbindbare Peers",
-
-	"Data transfer": "Datei Transfer",
-	"Received": "Empfangen",
-	"Received bytes": "Empfangene Bytes",
-	"Sent": "Gesendet",
-	"Sent bytes": "Gesendete Bytes",
-
-	"Files": "Dateien",
-	"Total": "Gesamt",
-	"Image": "Bilder",
-	"Other": "Sonstiges",
-	"User data": "Nutzer Daten",
-
-	"Size limit": "Speicher Limit",
-	"limit used": "Limit benutzt",
-	"free space": "freier Speicher",
-	"Set": "Setzten",
-
-	"Optional files": "Optionale Dateien",
-	"Downloaded": "Heruntergeladen",
-	"Download and help distribute all files": "Herunterladen und helfen alle Dateien zu verteilen",
-	"Total size": "Gesamte Größe",
-	"Downloaded files": "Heruntergeladene Dateien",
-
-	"Database": "Datenbank",
-	"search feeds": "Feeds durchsuchen",
-	"{feeds} query": "{feeds} Abfrage",
-	"Reload": "Neu laden",
-	"Rebuild": "Neu bauen",
-	"No database found": "Keine Datenbank gefunden",
-
-	"Identity address": "Identitäts Adresse",
-	"Change": "Ändern",
-
-	"Update": "Aktualisieren",
-	"Pause": "Pausieren",
-	"Resume": "Fortsetzen",
-	"Delete": "Löschen",
-	"Are you sure?": "Bist du sicher?",
-
-	"Site address": "Seiten Adresse",
-	"Donate": "Spenden",
-
-	"Missing files": "Fehlende Dateien",
-	"{} try": "{} versuch",
-	"{} tries": "{} versuche",
-	"+ {num_bad_files} more": "+ {num_bad_files} mehr",
-
-	"This is my site": "Das ist meine Seite",
-	"Site title": "Seiten Titel",
-	"Site description": "Seiten Beschreibung",
-	"Save site settings": "Einstellungen der Seite speichern",
-
-	"Content publishing": "Inhaltsveröffentlichung",
-	"Choose": "Wähle",
-	"Sign": "Signieren",
-	"Publish": "Veröffentlichen",
-
-	"This function is disabled on this proxy": "Diese Funktion ist auf dieser Proxy deaktiviert",
-	"GeoLite2 City database download error: {}!<br>Please download manually and unpack to data dir:<br>{}": "GeoLite2 City Datenbank Download Fehler: {}!<br>Bitte manuell herunterladen und die Datei in das Datei Verzeichnis extrahieren:<br>{}",
-	"Downloading GeoLite2 City database (one time only, ~20MB)...": "Herunterladen der GeoLite2 City Datenbank (einmalig, ~20MB)...",
-	"GeoLite2 City database downloaded!": "GeoLite2 City Datenbank heruntergeladen!",
-
-	"Are you sure?": "Bist du sicher?",
-	"Site storage limit modified!": "Speicher Limit der Seite modifiziert!",
-	"Database schema reloaded!": "Datebank Schema neu geladen!",
-	"Database rebuilding....": "Datenbank neu bauen...",
-	"Database rebuilt!": "Datenbank neu gebaut!",
-	"Site updated!": "Seite aktualisiert!",
-	"Delete this site": "Diese Seite löschen",
-	"File write error: ": "Datei schreib fehler:",
-	"Site settings saved!": "Seiten Einstellungen gespeichert!",
-	"Enter your private key:": "Gib deinen privaten Schlüssel ein:",
-	" Signed!": " Signiert!",
-	"WebGL not supported": "WebGL nicht unterstützt"
-}
diff --git a/plugins/Sidebar/languages/es.json b/plugins/Sidebar/languages/es.json
deleted file mode 100644
index b9e98c46..00000000
--- a/plugins/Sidebar/languages/es.json
+++ /dev/null
@@ -1,79 +0,0 @@
-{
-	"Peers": "Pares",
-	"Connected": "Conectados",
-	"Connectable": "Conectables",
-	"Connectable peers": "Pares conectables",
-
-	"Data transfer": "Transferencia de datos",
-	"Received": "Recibidos",
-	"Received bytes": "Bytes recibidos",
-	"Sent": "Enviados",
-	"Sent bytes": "Bytes envidados",
-
-	"Files": "Ficheros",
-	"Total": "Total",
-	"Image": "Imagen",
-	"Other": "Otro",
-	"User data": "Datos del usuario",
-
-	"Size limit": "Límite de tamaño",
-	"limit used": "Límite utilizado",
-	"free space": "Espacio libre",
-	"Set": "Establecer",
-
-	"Optional files": "Ficheros opcionales",
-	"Downloaded": "Descargado",
-	"Download and help distribute all files": "Descargar y ayudar a distribuir todos los ficheros",
-	"Total size": "Tamaño total",
-	"Downloaded files": "Ficheros descargados",
-
-	"Database": "Base de datos",
-	"search feeds": "Fuentes de búsqueda",
-	"{feeds} query": "{feeds} consulta",
-	"Reload": "Recargar",
-	"Rebuild": "Reconstruir",
-	"No database found": "No se ha encontrado la base de datos",
-
-	"Identity address": "Dirección de la identidad",
-	"Change": "Cambiar",
-
-	"Update": "Actualizar",
-	"Pause": "Pausar",
-	"Resume": "Reanudar",
-	"Delete": "Borrar",
-
-	"Site address": "Dirección del sitio",
-	"Donate": "Donar",
-
-	"Missing files": "Ficheros perdidos",
-	"{} try": "{} intento",
-	"{} tries": "{} intentos",
-	"+ {num_bad_files} more": "+ {num_bad_files} más",
-
-	"This is my site": "Este es mi sitio",
-	"Site title": "Título del sitio",
-	"Site description": "Descripción del sitio",
-	"Save site settings": "Guardar la configuración del sitio",
-
-	"Content publishing": "Publicación del contenido",
-	"Choose": "Elegir",
-	"Sign": "Firmar",
-	"Publish": "Publicar",
-	"This function is disabled on this proxy": "Esta función está deshabilitada en este proxy",
-	"GeoLite2 City database download error: {}!<br>Please download manually and unpack to data dir:<br>{}": "¡Error de la base de datos GeoLite2: {}!<br>Por favor, descárgalo manualmente y descomprime al directorio de datos:<br>{}",
-	"Downloading GeoLite2 City database (one time only, ~20MB)...": "Descargando la base de datos de GeoLite2 (una única vez, ~20MB)...",
-	"GeoLite2 City database downloaded!": "¡Base de datos de GeoLite2 descargada!",
-
-	"Are you sure?": "¿Estás seguro?",
-	"Site storage limit modified!": "¡Límite de almacenamiento del sitio modificado!",
-	"Database schema reloaded!": "¡Esquema de la base de datos recargado!",
-	"Database rebuilding....": "Reconstruyendo la base de datos...",
-	"Database rebuilt!": "¡Base de datos reconstruida!",
-	"Site updated!": "¡Sitio actualizado!",
-	"Delete this site": "Borrar este sitio",
-	"File write error: ": "Error de escritura de fichero:",
-	"Site settings saved!": "¡Configuración del sitio guardada!",
-	"Enter your private key:": "Introduce tu clave privada:",
-	" Signed!": " ¡firmado!",
-	"WebGL not supported": "WebGL no está soportado"
-}
diff --git a/plugins/Sidebar/languages/fr.json b/plugins/Sidebar/languages/fr.json
deleted file mode 100644
index 5c4b3ac7..00000000
--- a/plugins/Sidebar/languages/fr.json
+++ /dev/null
@@ -1,82 +0,0 @@
-{
-	"Peers": "Pairs",
-	"Connected": "Connectés",
-	"Connectable": "Accessibles",
-	"Connectable peers": "Pairs accessibles",
-
-	"Data transfer": "Données transférées",
-	"Received": "Reçues",
-	"Received bytes": "Bytes reçus",
-	"Sent": "Envoyées",
-	"Sent bytes": "Bytes envoyés",
-
-	"Files": "Fichiers",
-	"Total": "Total",
-	"Image": "Image",
-	"Other": "Autre",
-	"User data": "Utilisateurs",
-
-	"Size limit": "Taille maximale",
-	"limit used": "utlisé",
-	"free space": "libre",
-	"Set": "Modifier",
-
-	"Optional files": "Fichiers optionnels",
-	"Downloaded": "Téléchargé",
-	"Download and help distribute all files": "Télécharger et distribuer tous les fichiers",
-	"Total size": "Taille totale",
-	"Downloaded files": "Fichiers téléchargés",
-
-	"Database": "Base de données",
-	"search feeds": "recherche",
-	"{feeds} query": "{feeds} requête",
-	"Reload": "Recharger",
-	"Rebuild": "Reconstruire",
-	"No database found": "Aucune base de données trouvée",
-
-	"Identity address": "Adresse d'identité",
-	"Change": "Modifier",
-
-	"Site control": "Opérations",
-	"Update": "Mettre à jour",
-	"Pause": "Suspendre",
-	"Resume": "Reprendre",
-	"Delete": "Supprimer",
-	"Are you sure?": "Êtes-vous certain?",
-
-	"Site address": "Adresse du site",
-	"Donate": "Faire un don",
-
-	"Missing files": "Fichiers manquants",
-	"{} try": "{} essai",
-	"{} tries": "{} essais",
-	"+ {num_bad_files} more": "+ {num_bad_files} manquants",
-
-	"This is my site": "Ce site m'appartient",
-	"Site title": "Nom du site",
-	"Site description": "Description du site",
-	"Save site settings": "Enregistrer les paramètres",
-
-	"Content publishing": "Publication du contenu",
-	"Choose": "Sélectionner",
-	"Sign": "Signer",
-	"Publish": "Publier",
-
-	"This function is disabled on this proxy": "Cette fonction est désactivé sur ce proxy",
-	"GeoLite2 City database download error: {}!<br>Please download manually and unpack to data dir:<br>{}": "Erreur au téléchargement de la base de données GeoLite2: {}!<br>Téléchargez et décompressez dans le dossier data:<br>{}",
-	"Downloading GeoLite2 City database (one time only, ~20MB)...": "Téléchargement de la base de données GeoLite2 (une seule fois, ~20MB)...",
-	"GeoLite2 City database downloaded!": "Base de données GeoLite2 téléchargée!",
-
-	"Are you sure?": "Êtes-vous certain?",
-	"Site storage limit modified!": "Taille maximale modifiée!",
-	"Database schema reloaded!": "Base de données rechargée!",
-	"Database rebuilding....": "Reconstruction de la base de données...",
-	"Database rebuilt!": "Base de données reconstruite!",
-	"Site updated!": "Site mis à jour!",
-	"Delete this site": "Supprimer ce site",
-	"File write error: ": "Erreur à l'écriture du fichier: ",
-	"Site settings saved!": "Paramètres du site enregistrés!",
-	"Enter your private key:": "Entrez votre clé privée:",
-	" Signed!": " Signé!",
-	"WebGL not supported": "WebGL n'est pas supporté"
-}
diff --git a/plugins/Sidebar/languages/hu.json b/plugins/Sidebar/languages/hu.json
deleted file mode 100644
index 21216825..00000000
--- a/plugins/Sidebar/languages/hu.json
+++ /dev/null
@@ -1,82 +0,0 @@
-{
-	"Peers": "Csatlakozási pontok",
-	"Connected": "Csaltakozva",
-	"Connectable": "Csatlakozható",
-	"Connectable peers": "Csatlakozható peer-ek",
-
-	"Data transfer": "Adatátvitel",
-	"Received": "Fogadott",
-	"Received bytes": "Fogadott byte-ok",
-	"Sent": "Küldött",
-	"Sent bytes": "Küldött byte-ok",
-
-	"Files": "Fájlok",
-	"Total": "Összesen",
-	"Image": "Kép",
-	"Other": "Egyéb",
-	"User data": "Felh. adat",
-
-	"Size limit": "Méret korlát",
-	"limit used": "felhasznált",
-	"free space": "szabad hely",
-	"Set": "Beállít",
-
-	"Optional files": "Opcionális fájlok",
-	"Downloaded": "Letöltött",
-	"Download and help distribute all files": "Minden opcionális fájl letöltése",
-	"Total size": "Teljes méret",
-	"Downloaded files": "Letöltve",
-
-	"Database": "Adatbázis",
-	"search feeds": "Keresés források",
-	"{feeds} query": "{feeds} lekérdezés",
-	"Reload": "Újratöltés",
-	"Rebuild": "Újraépítés",
-	"No database found": "Adatbázis nem található",
-
-	"Identity address": "Azonosító cím",
-	"Change": "Módosít",
-
-	"Site control": "Oldal műveletek",
-	"Update": "Frissít",
-	"Pause": "Szünteltet",
-	"Resume": "Folytat",
-	"Delete": "Töröl",
-	"Are you sure?": "Biztos vagy benne?",
-
-	"Site address": "Oldal címe",
-	"Donate": "Támogatás",
-
-	"Missing files": "Hiányzó fájlok",
-	"{} try": "{} próbálkozás",
-	"{} tries": "{} próbálkozás",
-	"+ {num_bad_files} more": "+ még {num_bad_files} darab",
-
-	"This is my site": "Ez az én oldalam",
-	"Site title": "Oldal neve",
-	"Site description": "Oldal leírása",
-	"Save site settings": "Oldal beállítások mentése",
-
-	"Content publishing": "Tartalom publikálás",
-	"Choose": "Válassz",
-	"Sign": "Aláírás",
-	"Publish": "Publikálás",
-
-	"This function is disabled on this proxy": "Ez a funkció ki van kapcsolva ezen a proxy-n",
-	"GeoLite2 City database download error: {}!<br>Please download manually and unpack to data dir:<br>{}": "GeoLite2 város adatbázis letöltési hiba: {}!<br>A térképhez töltsd le és csomagold ki a data könyvtárba:<br>{}",
-	"Downloading GeoLite2 City database (one time only, ~20MB)...": "GeoLite2 város adatbázis letöltése (csak egyszer kell, kb 20MB)...",
-	"GeoLite2 City database downloaded!": "GeoLite2 város adatbázis letöltve!",
-
-	"Are you sure?": "Biztos vagy benne?",
-	"Site storage limit modified!": "Az oldalt méret korlát módosítva!",
-	"Database schema reloaded!": "Adatbázis séma újratöltve!",
-	"Database rebuilding....": "Adatbázis újraépítés...",
-	"Database rebuilt!": "Adatbázis újraépítve!",
-	"Site updated!": "Az oldal frissítve!",
-	"Delete this site": "Az oldal törlése",
-	"File write error: ": "Fájl írási hiba: ",
-	"Site settings saved!": "Az oldal beállításai elmentve!",
-	"Enter your private key:": "Add meg a privát kulcsod:",
-	" Signed!": " Aláírva!",
-	"WebGL not supported": "WebGL nem támogatott"
-}
diff --git a/plugins/Sidebar/languages/it.json b/plugins/Sidebar/languages/it.json
deleted file mode 100644
index 6aa0969a..00000000
--- a/plugins/Sidebar/languages/it.json
+++ /dev/null
@@ -1,81 +0,0 @@
-{
-	"Peers": "Peer",
-	"Connected": "Connessi",
-	"Connectable": "Collegabili",
-	"Connectable peers": "Peer collegabili",
-
-	"Data transfer": "Trasferimento dati",
-	"Received": "Ricevuti",
-	"Received bytes": "Byte ricevuti",
-	"Sent": "Inviati",
-	"Sent bytes": "Byte inviati",
-
-	"Files": "File",
-	"Total": "Totale",
-	"Image": "Imagine",
-	"Other": "Altro",
-	"User data": "Dati utente",
-
-	"Size limit": "Limite dimensione",
-	"limit used": "limite usato",
-	"free space": "spazio libero",
-	"Set": "Imposta",
-
-	"Optional files": "File facoltativi",
-	"Downloaded": "Scaricati",
-	"Download and help distribute all files": "Scarica e aiuta a distribuire tutti i file",
-	"Total size": "Dimensione totale",
-	"Downloaded files": "File scaricati",
-
-	"Database": "Database",
-	"search feeds": "ricerca di feed",
-	"{feeds} query": "{feeds} interrogazione",
-	"Reload": "Ricaricare",
-	"Rebuild": "Ricostruire",
-	"No database found": "Nessun database trovato",
-
-	"Identity address": "Indirizzo di identità",
-	"Change": "Cambia",
-
-	"Update": "Aggiorna",
-	"Pause": "Sospendi",
-	"Resume": "Riprendi",
-	"Delete": "Cancella",
-	"Are you sure?": "Sei sicuro?",
-
-	"Site address": "Indirizzo sito",
-	"Donate": "Dona",
-
-	"Missing files": "File mancanti",
-	"{} try": "{} tenta",
-	"{} tries": "{} prova",
-	"+ {num_bad_files} more": "+ {num_bad_files} altri",
-
-	"This is my site": "Questo è il mio sito",
-	"Site title": "Titolo sito",
-	"Site description": "Descrizione sito",
-	"Save site settings": "Salva impostazioni sito",
-
-	"Content publishing": "Pubblicazione contenuto",
-	"Choose": "Scegli",
-	"Sign": "Firma",
-	"Publish": "Pubblica",
-
-	"This function is disabled on this proxy": "Questa funzione è disabilitata su questo proxy",
-	"GeoLite2 City database download error: {}!<br>Please download manually and unpack to data dir:<br>{}": "Errore scaricamento database GeoLite2 City: {}!<br>Si prega di scaricarlo manualmente e spacchetarlo nella cartella dir:<br>{}",
-	"Downloading GeoLite2 City database (one time only, ~20MB)...": "Scaricamento database GeoLite2 City (solo una volta, ~20MB)...",
-	"GeoLite2 City database downloaded!": "Database GeoLite2 City scaricato!",
-
-	"Are you sure?": "Sei sicuro?",
-	"Site storage limit modified!": "Limite di archiviazione del sito modificato!",
-	"Database schema reloaded!": "Schema database ricaricato!",
-	"Database rebuilding....": "Ricostruzione database...",
-	"Database rebuilt!": "Database ricostruito!",
-	"Site updated!": "Sito aggiornato!",
-	"Delete this site": "Cancella questo sito",
-	"File write error: ": "Errore scrittura file:",
-	"Site settings saved!": "Impostazioni sito salvate!",
-	"Enter your private key:": "Inserisci la tua chiave privata:",
-	" Signed!": " Firmato!",
-	"WebGL not supported": "WebGL non supportato"
-}
diff --git a/plugins/Sidebar/languages/jp.json b/plugins/Sidebar/languages/jp.json
deleted file mode 100644
index 38bbd420..00000000
--- a/plugins/Sidebar/languages/jp.json
+++ /dev/null
@@ -1,104 +0,0 @@
-{
-	"Copy to clipboard": "クリップボードにコピー",
-	"Peers": "ピア",
-	"Connected": "接続済み",
-	"Connectable": "利用可能",
-	"Connectable peers": "ピアに接続可能",
-	"Onion": "Onion",
-	"Local": "ローカル",
-
-	"Data transfer": "データ転送",
-	"Received": "受信",
-	"Received bytes": "受信バイト数",
-	"Sent": "送信",
-	"Sent bytes": "送信バイト数",
-
-	"Files": "ファイル",
-	"Browse files": "ファイルを見る",
-	"Save as .zip": "ZIP形式で保存",
-	"Total": "合計",
-	"Image": "画像",
-	"Other": "その他",
-	"User data": "ユーザーデータ",
-
-	"Size limit": "サイズ制限",
-	"limit used": "使用上限",
-	"free space": "フリースペース",
-	"Set": "セット",
-
-	"Optional files": "オプション ファイル",
-	"Downloaded": "ダウンロード済み",
-	"Help distribute added optional files": "オプションファイルの配布を支援する",
-	"Auto download big file size limit": "大きなファイルの自動ダウンロードのサイズ制限",
-	"Download previous files": "以前のファイルのダウンロード",
-	"Optional files download started": "オプションファイルのダウンロードを開始",
-	"Optional files downloaded": "オプションファイルのダウンロードが完了しました",
-	"Download and help distribute all files": "ダウンロードしてすべてのファイルの配布を支援する",
-	"Total size": "合計サイズ",
-	"Downloaded files": "ダウンロードされたファイル",
-
-	"Database": "データベース",
-	"search feeds": "フィードを検索する",
-	"{feeds} query": "{feeds} お問い合わせ",
-	"Reload": "再読込",
-	"Rebuild": "再ビルド",
-	"No database found": "データベースが見つかりません",
-
-	"Identity address": "あなたの識別アドレス",
-	"Change": "編集",
-
-	"Site control": "サイト管理",
-	"Update": "更新",
-	"Pause": "一時停止",
-	"Resume": "再開",
-	"Delete": "削除",
-	"Are you sure?": "本当によろしいですか?",
-
-	"Site address": "サイトアドレス",
-	"Donate": "寄付する",
-
-	"Missing files": "ファイルがありません",
-	"{} try": "{} 試す",
-	"{} tries": "{} 試行",
-	"+ {num_bad_files} more": "+ {num_bad_files} more",
-
-	"This is my site": "これは私のサイトです",
-	"Site title": "サイトタイトル",
-	"Site description": "サイトの説明",
-	"Save site settings": "サイトの設定を保存する",
-	"Open site directory": "サイトのディレクトリを開く",
-
-	"Content publishing": "コンテンツを公開する",
-	"Add saved private key": "秘密鍵の追加と保存",
-	"Save": "保存",
-	"Private key saved.": "秘密鍵が保存されています",
-	"Private key saved for site signing": "サイトに署名するための秘密鍵を保存",
-	"Forgot": "わすれる",
-	"Saved private key removed": "保存された秘密鍵を削除しました",
-	"Choose": "選択",
-	"Sign": "署名",
-	"Publish": "公開する",
-	"Sign and publish": "署名して公開",
-
-	"This function is disabled on this proxy": "この機能はこのプロキシで無効になっています",
-	"GeoLite2 City database download error: {}!<br>Please download manually and unpack to data dir:<br>{}": "GeoLite2 Cityデータベースのダウンロードエラー: {}!<br>手動でダウンロードして、フォルダに解凍してください。:<br>{}",
-	"Downloading GeoLite2 City database (one time only, ~20MB)...": "GeoLite2 Cityデータベースの読み込み (これは一度だけ行われます, ~20MB)...",
-	"GeoLite2 City database downloaded!": "GeoLite2 Cityデータベースがダウンロードされました!",
-
-	"Are you sure?": "本当によろしいですか?",
-	"Site storage limit modified!": "サイトの保存容量の制限が変更されました!",
-	"Database schema reloaded!": "データベーススキーマがリロードされました!",
-	"Database rebuilding....": "データベースの再構築中....",
-	"Database rebuilt!": "データベースが再構築されました!",
-	"Site updated!": "サイトが更新されました!",
-	"Delete this site": "このサイトを削除する",
-	"Blacklist": "NG",
-	"Blacklist this site": "NGリストに入れる",
-	"Reason": "理由",
-	"Delete and Blacklist": "削除してNG",
-	"File write error: ": "ファイル書き込みエラー:",
-	"Site settings saved!": "サイト設定が保存されました!",
-	"Enter your private key:": "秘密鍵を入力してください:",
-	" Signed!": " 署名しました!",
-	"WebGL not supported": "WebGLはサポートされていません"
-}
diff --git a/plugins/Sidebar/languages/pl.json b/plugins/Sidebar/languages/pl.json
deleted file mode 100644
index 93268507..00000000
--- a/plugins/Sidebar/languages/pl.json
+++ /dev/null
@@ -1,82 +0,0 @@
-{
-	"Peers": "Użytkownicy równorzędni",
-	"Connected": "Połączony",
-	"Connectable": "Możliwy do podłączenia",
-	"Connectable peers": "Połączeni użytkownicy równorzędni",
-
-	"Data transfer": "Transfer danych",
-	"Received": "Odebrane",
-	"Received bytes": "Odebrany bajty",
-	"Sent": "Wysłane",
-	"Sent bytes": "Wysłane bajty",
-
-	"Files": "Pliki",
-	"Total": "Sumarycznie",
-	"Image": "Obraz",
-	"Other": "Inne",
-	"User data": "Dane użytkownika",
-
-	"Size limit": "Rozmiar limitu",
-	"limit used": "zużyty limit",
-	"free space": "wolna przestrzeń",
-	"Set": "Ustaw",
-
-	"Optional files": "Pliki opcjonalne",
-	"Downloaded": "Ściągnięte",
-	"Download and help distribute all files": "Ściągnij i pomóż rozpowszechniać wszystkie pliki",
-	"Total size": "Rozmiar sumaryczny",
-	"Downloaded files": "Ściągnięte pliki",
-
-	"Database": "Baza danych",
-	"search feeds": "przeszukaj zasoby",
-	"{feeds} query": "{feeds} pytanie",
-	"Reload": "Odśwież",
-	"Rebuild": "Odbuduj",
-	"No database found": "Nie odnaleziono bazy danych",
-
-	"Identity address": "Adres identyfikacyjny",
-	"Change": "Zmień",
-
-	"Site control": "Kontrola strony",
-	"Update": "Zaktualizuj",
-	"Pause": "Wstrzymaj",
-	"Resume": "Wznów",
-	"Delete": "Skasuj",
-	"Are you sure?": "Jesteś pewien?",
-
-	"Site address": "Adres strony",
-	"Donate": "Wspomóż",
-
-	"Missing files": "Brakujące pliki",
-	"{} try": "{} próba",
-	"{} tries": "{} próby",
-	"+ {num_bad_files} more": "+ {num_bad_files} więcej",
-
-	"This is my site": "To moja strona",
-	"Site title": "Tytuł strony",
-	"Site description": "Opis strony",
-	"Save site settings": "Zapisz ustawienia strony",
-
-	"Content publishing": "Publikowanie treści",
-	"Choose": "Wybierz",
-	"Sign": "Podpisz",
-	"Publish": "Opublikuj",
-
-	"This function is disabled on this proxy": "Ta funkcja jest zablokowana w tym proxy",
-	"GeoLite2 City database download error: {}!<br>Please download manually and unpack to data dir:<br>{}": "Błąd ściągania bazy danych GeoLite2 City: {}!<br>Proszę ściągnąć ją recznie i wypakować do katalogu danych:<br>{}",
-	"Downloading GeoLite2 City database (one time only, ~20MB)...": "Ściąganie bazy danych GeoLite2 City (tylko jednorazowo, ok. 20MB)...",
-	"GeoLite2 City database downloaded!": "Baza danych GeoLite2 City ściagnięta!",
-
-	"Are you sure?": "Jesteś pewien?",
-	"Site storage limit modified!": "Limit pamięci strony zmodyfikowany!",
-	"Database schema reloaded!": "Schemat bazy danych załadowany ponownie!",
-	"Database rebuilding....": "Przebudowywanie bazy danych...",
-	"Database rebuilt!": "Baza danych przebudowana!",
-	"Site updated!": "Strona zaktualizowana!",
-	"Delete this site": "Usuń tę stronę",
-	"File write error: ": "Błąd zapisu pliku: ",
-	"Site settings saved!": "Ustawienia strony zapisane!",
-	"Enter your private key:": "Wpisz swój prywatny klucz:",
-	" Signed!": " Podpisane!",
-	"WebGL not supported": "WebGL nie jest obsługiwany"
-}
diff --git a/plugins/Sidebar/languages/pt-br.json b/plugins/Sidebar/languages/pt-br.json
deleted file mode 100644
index d5659171..00000000
--- a/plugins/Sidebar/languages/pt-br.json
+++ /dev/null
@@ -1,97 +0,0 @@
-{
-	"Copy to clipboard": "Copiar para área de transferência (clipboard)",
-	"Peers": "Peers",
-	"Connected": "Ligados",
-	"Connectable": "Disponíveis",
-	"Onion": "Onion",
-	"Local": "Locais",
-	"Connectable peers": "Peers disponíveis",
-
-	"Data transfer": "Transferência de dados",
-	"Received": "Recebidos",
-	"Received bytes": "Bytes recebidos",
-	"Sent": "Enviados",
-	"Sent bytes": "Bytes enviados",
-
-	"Files": "Arquivos",
-	"Save as .zip": "Salvar como .zip",
-	"Total": "Total",
-	"Image": "Imagem",
-	"Other": "Outros",
-	"User data": "Dados do usuário",
-
-	"Size limit": "Limite de tamanho",
-	"limit used": "limite utilizado",
-	"free space": "espaço livre",
-	"Set": "Definir",
-
-	"Optional files": "Arquivos opcionais",
-	"Downloaded": "Baixados",
-	"Download and help distribute all files": "Baixar e ajudar a distribuir todos os arquivos",
-	"Total size": "Tamanho total",
-	"Downloaded files": "Arquivos baixados",
-
-	"Database": "Banco de dados",
-	"search feeds": "pesquisar feeds",
-	"{feeds} query": "consulta de {feeds}",
-	"Reload": "Recarregar",
-	"Rebuild": "Reconstruir",
-	"No database found": "Base de dados não encontrada",
-
-	"Identity address": "Endereço de identidade",
-	"Change": "Alterar",
-
-	"Site control": "Controle do site",
-	"Update": "Atualizar",
-	"Pause": "Suspender",
-	"Resume": "Continuar",
-	"Delete": "Remover",
-	"Are you sure?": "Tem certeza?",
-
-	"Site address": "Endereço do site",
-	"Donate": "Doar",
-
-	"Needs to be updated": "Necessitam ser atualizados",
-	"{} try": "{} tentativa",
-	"{} tries": "{} tentativas",
-	"+ {num_bad_files} more": "+ {num_bad_files} adicionais",
-
-	"This is my site": "Este é o meu site",
-	"Site title": "Título do site",
-	"Site description": "Descrição do site",
-	"Save site settings": "Salvar definições do site",
-	"Open site directory": "Abrir diretório do site",
-
-	"Content publishing": "Publicação do conteúdo",
-	"Choose": "Escolher",
-	"Sign": "Assinar",
-	"Publish": "Publicar",
-	"Sign and publish": "Assinar e publicar",
-	"add saved private key": "adicionar privatekey (chave privada) para salvar",
-	"Private key saved for site signing": "Privatekey foi salva para assinar o site",
-	"Private key saved.": "Privatekey salva.",
-	"forgot": "esquecer",
-	"Saved private key removed": "Privatekey salva foi removida",
-	"This function is disabled on this proxy": "Esta função encontra-se desativada neste proxy",
-	"GeoLite2 City database download error: {}!<br>Please download manually and unpack to data dir:<br>{}": "Erro ao baixar a base de dados GeoLite2 City: {}!<br>Por favor baixe manualmente e descompacte os dados para a seguinte pasta:<br>{}",
-	"Downloading GeoLite2 City database (one time only, ~20MB)...": "Baixando a base de dados GeoLite2 City (uma única vez, ~20MB)...",
-	"GeoLite2 City database downloaded!": "A base de dados GeoLite2 City foi baixada!",
-
-	"Are you sure?": "Tem certeza?",
-	"Site storage limit modified!": "O limite de armazenamento do site foi modificado!",
-	"Database schema reloaded!": "O esquema da base de dados foi atualizado!",
-	"Database rebuilding....": "Reconstruindo base de dados...",
-	"Database rebuilt!": "Base de dados reconstruída!",
-	"Site updated!": "Site atualizado!",
-	"Delete this site": "Remover este site",
-	"Blacklist": "Blacklist",
-	"Blacklist this site": "Blacklistar este site",
-	"Reason": "Motivo",
-	"Delete and Blacklist": "Deletar e blacklistar",
-	"File write error: ": "Erro de escrita de arquivo: ",
-	"Site settings saved!": "Definições do site salvas!",
-	"Enter your private key:": "Digite sua chave privada:",
-	" Signed!": " Assinado!",
-	"WebGL not supported": "WebGL não é suportado",
-	"Save as .zip": "Salvar como .zip"
-}
diff --git a/plugins/Sidebar/languages/ru.json b/plugins/Sidebar/languages/ru.json
deleted file mode 100644
index f2eeca04..00000000
--- a/plugins/Sidebar/languages/ru.json
+++ /dev/null
@@ -1,82 +0,0 @@
-{
-	"Peers": "Пиры",
-	"Connected": "Подключенные",
-	"Connectable": "Доступные",
-	"Connectable peers": "Пиры доступны для подключения",
-
-	"Data transfer": "Передача данных",
-	"Received": "Получено",
-	"Received bytes": "Получено байн",
-	"Sent": "Отправлено",
-	"Sent bytes": "Отправлено байт",
-
-	"Files": "Файлы",
-	"Total": "Всего",
-	"Image": "Изображений",
-	"Other": "Другое",
-	"User data": "Ваш контент",
-
-	"Size limit": "Ограничение по размеру",
-	"limit used": "Использовано",
-	"free space": "Доступно",
-	"Set": "Установить",
-
-	"Optional files": "Опциональные файлы",
-	"Downloaded": "Загружено",
-	"Download and help distribute all files": "Загрузить опциональные файлы для помощи сайту",
-	"Total size": "Объём",
-	"Downloaded files": "Загруженные файлы",
-
-	"Database": "База данных",
-	"search feeds": "поиск подписок",
-	"{feeds} query": "{feeds} запрос",
-	"Reload": "Перезагрузить",
-	"Rebuild": "Перестроить",
-	"No database found": "База данных не найдена",
-
-	"Identity address": "Уникальный адрес",
-	"Change": "Изменить",
-
-	"Site control": "Управление сайтом",
-	"Update": "Обновить",
-	"Pause": "Пауза",
-	"Resume": "Продолжить",
-	"Delete": "Удалить",
-	"Are you sure?": "Вы уверены?",
-
-	"Site address": "Адрес сайта",
-	"Donate": "Пожертвовать",
-
-	"Missing files": "Отсутствующие файлы",
-	"{} try": "{} попробовать",
-	"{} tries": "{} попыток",
-	"+ {num_bad_files} more": "+ {num_bad_files} ещё",
-
-	"This is my site": "Это мой сайт",
-	"Site title": "Название сайта",
-	"Site description": "Описание сайта",
-	"Save site settings": "Сохранить настройки сайта",
-
-	"Content publishing": "Публикация контента",
-	"Choose": "Выбрать",
-	"Sign": "Подписать",
-	"Publish": "Опубликовать",
-
-	"This function is disabled on this proxy": "Эта функция отключена на этом прокси",
-	"GeoLite2 City database download error: {}!<br>Please download manually and unpack to data dir:<br>{}": "Ошибка загрузки базы городов GeoLite2: {}!<br>Пожалуйста, загрузите её вручную и распакуйте в папку:<br>{}",
-	"Downloading GeoLite2 City database (one time only, ~20MB)...": "Загрузка базы городов GeoLite2 (это делается только 1 раз, ~20MB)...",
-	"GeoLite2 City database downloaded!": "База GeoLite2 успешно загружена!",
-
-	"Are you sure?": "Вы уверены?",
-	"Site storage limit modified!": "Лимит хранилища для сайта изменен!",
-	"Database schema reloaded!": "Схема базы данных перезагружена!",
-	"Database rebuilding....": "Перестройка базы данных...",
-	"Database rebuilt!": "База данных перестроена!",
-	"Site updated!": "Сайт обновлён!",
-	"Delete this site": "Удалить этот сайт",
-	"File write error: ": "Ошибка записи файла:",
-	"Site settings saved!": "Настройки сайта сохранены!",
-	"Enter your private key:": "Введите свой приватный ключ:",
-	" Signed!": " Подписано!",
-	"WebGL not supported": "WebGL не поддерживается"
-}
diff --git a/plugins/Sidebar/languages/tr.json b/plugins/Sidebar/languages/tr.json
deleted file mode 100644
index 88fcd6e0..00000000
--- a/plugins/Sidebar/languages/tr.json
+++ /dev/null
@@ -1,82 +0,0 @@
-{
-	"Peers": "Eşler",
-	"Connected": "Bağlı",
-	"Connectable": "Erişilebilir",
-	"Connectable peers": "Bağlanılabilir eşler",
-
-	"Data transfer": "Veri aktarımı",
-	"Received": "Alınan",
-	"Received bytes": "Bayt alındı",
-	"Sent": "Gönderilen",
-	"Sent bytes": "Bayt gönderildi",
-
-	"Files": "Dosyalar",
-	"Total": "Toplam",
-	"Image": "Resim",
-	"Other": "Diğer",
-	"User data": "Kullanıcı verisi",
-
-	"Size limit": "Boyut sınırı",
-	"limit used": "kullanılan",
-	"free space": "boş",
-	"Set": "Ayarla",
-
-	"Optional files": "İsteğe bağlı dosyalar",
-	"Downloaded": "İndirilen",
-	"Download and help distribute all files": "Tüm dosyaları indir ve yayılmalarına yardım et",
-	"Total size": "Toplam boyut",
-	"Downloaded files": "İndirilen dosyalar",
-
-	"Database": "Veritabanı",
-	"search feeds": "kaynak ara",
-	"{feeds} query": "{feeds} sorgu",
-	"Reload": "Yenile",
-	"Rebuild": "Yapılandır",
-	"No database found": "Veritabanı yok",
-
-	"Identity address": "Kimlik adresi",
-	"Change": "Değiştir",
-
-	"Site control": "Site kontrolü",
-	"Update": "Güncelle",
-	"Pause": "Duraklat",
-	"Resume": "Sürdür",
-	"Delete": "Sil",
-	"Are you sure?": "Emin misin?",
-
-	"Site address": "Site adresi",
-	"Donate": "Bağış yap",
-
-	"Missing files": "Eksik dosyalar",
-	"{} try": "{} deneme",
-	"{} tries": "{} deneme",
-	"+ {num_bad_files} more": "+ {num_bad_files} tane daha",
-
-	"This is my site": "Bu benim sitem",
-	"Site title": "Site başlığı",
-	"Site description": "Site açıklaması",
-	"Save site settings": "Site ayarlarını kaydet",
-
-	"Content publishing": "İçerik yayımlanıyor",
-	"Choose": "Seç",
-	"Sign": "İmzala",
-	"Publish": "Yayımla",
-
-	"This function is disabled on this proxy": "Bu özellik bu vekilde kullanılamaz",
-	"GeoLite2 City database download error: {}!<br>Please download manually and unpack to data dir:<br>{}": "GeoLite2 Şehir veritabanı indirme hatası: {}!<br>Lütfen kendiniz indirip aşağıdaki konuma açınınız:<br>{}",
-	"Downloading GeoLite2 City database (one time only, ~20MB)...": "GeoLite2 Şehir veritabanı indiriliyor (sadece bir kere, ~20MB)...",
-	"GeoLite2 City database downloaded!": "GeoLite2 Şehir veritabanı indirildi!",
-
-	"Are you sure?": "Emin misiniz?",
-	"Site storage limit modified!": "Site saklama sınırı değiştirildi!",
-	"Database schema reloaded!": "Veritabanı şeması yeniden yüklendi!",
-	"Database rebuilding....": "Veritabanı yeniden inşa ediliyor...",
-	"Database rebuilt!": "Veritabanı yeniden inşa edildi!",
-	"Site updated!": "Site güncellendi!",
-	"Delete this site": "Bu siteyi sil",
-	"File write error: ": "Dosya yazma hatası: ",
-	"Site settings saved!": "Site ayarları kaydedildi!",
-	"Enter your private key:": "Özel anahtarınızı giriniz:",
-	" Signed!": " İmzala!",
-	"WebGL not supported": "WebGL desteklenmiyor"
-}
diff --git a/plugins/Sidebar/languages/zh-tw.json b/plugins/Sidebar/languages/zh-tw.json
deleted file mode 100644
index 9d4ea1be..00000000
--- a/plugins/Sidebar/languages/zh-tw.json
+++ /dev/null
@@ -1,83 +0,0 @@
-{
-	"Peers": "節點數",
-	"Connected": "已連線",
-	"Connectable": "可連線",
-	"Connectable peers": "可連線節點",
-
-	"Data transfer": "數據傳輸",
-	"Received": "已接收",
-	"Received bytes": "已接收位元組",
-	"Sent": "已傳送",
-	"Sent bytes": "已傳送位元組",
-
-	"Files": "檔案",
-	"Total": "共計",
-	"Image": "圖片",
-	"Other": "其他",
-	"User data": "使用者數據",
-
-	"Size limit": "大小限制",
-	"limit used": "已使用",
-	"free space": "可用空間",
-	"Set": "偏好設定",
-
-	"Optional files": "可選檔案",
-	"Downloaded": "已下載",
-	"Download and help distribute all files": "下載並幫助分發所有檔案",
-	"Total size": "總大小",
-	"Downloaded files": "下載的檔案",
-
-	"Database": "資料庫",
-	"search feeds": "搜尋供稿",
-	"{feeds} query": "{feeds} 查詢 ",
-	"Reload": "重新整理",
-	"Rebuild": "重建",
-	"No database found": "未找到資料庫",
-
-	"Identity address": "身分位址",
-	"Change": "變更",
-
-	"Site control": "網站控制",
-	"Update": "更新",
-	"Pause": "暫停",
-	"Resume": "恢復",
-	"Delete": "刪除",
-	"Are you sure?": "你確定?",
-
-	"Site address": "網站位址",
-	"Donate": "捐贈",
-
-	"Missing files": "缺少的檔案",
-	"{} try": "{} 嘗試",
-	"{} tries": "{} 已嘗試",
-	"+ {num_bad_files} more": "+ {num_bad_files} 更多",
-
-	"This is my site": "這是我的網站",
-	"Site title": "網站標題",
-	"Site description": "網站描述",
-	"Save site settings": "存儲網站設定",
-	"Open site directory": "打開所在資料夾",
-
-	"Content publishing": "內容發布",
-	"Choose": "選擇",
-	"Sign": "簽署",
-	"Publish": "發布",
-	"Sign and publish": "簽名並發布",
-	"This function is disabled on this proxy": "此代理上禁用此功能",
-	"GeoLite2 City database download error: {}!<br>Please download manually and unpack to data dir:<br>{}": "GeoLite2 地理位置資料庫下載錯誤:{}!<br>請手動下載並解壓到數據目錄:<br>{}",
-	"Downloading GeoLite2 City database (one time only, ~20MB)...": "正在下載 GeoLite2 地理位置資料庫 (僅一次,約 20MB )...",
-	"GeoLite2 City database downloaded!": "GeoLite2 地理位置資料庫已下載!",
-
-	"Are you sure?": "你確定?",
-	"Site storage limit modified!": "網站存儲限制已變更!",
-	"Database schema reloaded!": "資料庫架構重新加載!",
-	"Database rebuilding....": "資料庫重建中...",
-	"Database rebuilt!": "資料庫已重建!",
-	"Site updated!": "網站已更新!",
-	"Delete this site": "刪除此網站",
-	"File write error: ": "檔案寫入錯誤:",
-	"Site settings saved!": "網站設置已保存!",
-	"Enter your private key:": "輸入您的私鑰:",
-	" Signed!": " 已簽署!",
-	"WebGL not supported": "不支援 WebGL"
-}
diff --git a/plugins/Sidebar/languages/zh.json b/plugins/Sidebar/languages/zh.json
deleted file mode 100644
index 639ac7f6..00000000
--- a/plugins/Sidebar/languages/zh.json
+++ /dev/null
@@ -1,101 +0,0 @@
-{
-	"Copy to clipboard": "复制到剪切板",
-	"Peers": "节点数",
-	"Connected": "已连接",
-	"Connectable": "可连接",
-	"Onion": "洋葱点",
-	"Local": "局域网",
-	"Connectable peers": "可连接节点",
-
-	"Data transfer": "数据传输",
-	"Received": "已接收",
-	"Received bytes": "已接收字节",
-	"Sent": "已发送",
-	"Sent bytes": "已发送字节",
-
-	"Files": "文件",
-	"Save as .zip": "打包成zip文件",
-	"Total": "总计",
-	"Image": "图像",
-	"Other": "其他",
-	"User data": "用户数据",
-
-	"Size limit": "大小限制",
-	"limit used": "限额",
-	"free space": "剩余空间",
-	"Set": "设置",
-
-	"Optional files": "可选文件",
-	"Downloaded": "已下载",
-	"Help distribute added optional files": "帮助分发新的可选文件",
-	"Auto download big file size limit": "自动下载大文件大小限制",
-	"Download previous files": "下载之前的文件",
-	"Optional files download started": "可选文件下载启动",
-	"Optional files downloaded": "可选文件下载完成",
-	"Total size": "总大小",
-	"Downloaded files": "已下载文件",
-
-	"Database": "数据库",
-	"search feeds": "搜索数据源",
-	"{feeds} query": "{feeds} 请求",
-	"Reload": "重载",
-	"Rebuild": "重建",
-	"No database found": "没有找到数据库",
-
-	"Identity address": "身份地址",
-	"Change": "更改",
-
-	"Site control": "站点控制",
-	"Update": "更新",
-	"Pause": "暂停",
-	"Resume": "恢复",
-	"Delete": "删除",
-	"Are you sure?": "您确定吗?",
-
-	"Site address": "站点地址",
-	"Donate": "捐赠",
-
-	"Needs to be updated": "需要更新",
-	"{} try": "{} 尝试",
-	"{} tries": "{} 已尝试",
-	"+ {num_bad_files} more": "+ {num_bad_files} 更多",
-
-	"This is my site": "这是我的站点",
-	"Site title": "站点标题",
-	"Site description": "站点描述",
-	"Save site settings": "保存站点设置",
-	"Open site directory": "打开所在文件夹",
-
-	"Content publishing": "内容发布",
-	"Add saved private key": "添加并保存私钥",
-	"Save": "保存",
-	"Private key saved.": "私钥已保存",
-	"Private key saved for site signing": "已保存用于站点签名的私钥",
-	"Forgot": "删除私钥",
-	"Saved private key removed": "保存的私钥已删除",
-	"Choose": "选择",
-	"Sign": "签名",
-	"Publish": "发布",
-	"Sign and publish": "签名并发布",
-	"This function is disabled on this proxy": "此功能在代理上被禁用",
-	"GeoLite2 City database download error: {}!<br>Please download manually and unpack to data dir:<br>{}": "GeoLite2 地理位置数据库下载错误:{}!<br>请手动下载并解压在数据目录:<br>{}",
-	"Downloading GeoLite2 City database (one time only, ~20MB)...": "正在下载 GeoLite2 地理位置数据库 (仅需一次,约 20MB )...",
-	"GeoLite2 City database downloaded!": "GeoLite2 地理位置数据库已下载!",
-
-	"Are you sure?": "您确定吗?",
-	"Site storage limit modified!": "站点存储限制已更改!",
-	"Database schema reloaded!": "数据库模式已重新加载!",
-	"Database rebuilding....": "数据库重建中...",
-	"Database rebuilt!": "数据库已重建!",
-	"Site updated!": "站点已更新!",
-	"Delete this site": "删除此站点",
-	"Blacklist": "黑名单",
-	"Blacklist this site": "拉黑此站点",
-	"Reason": "原因",
-	"Delete and Blacklist": "删除并拉黑",
-	"File write error: ": "文件写入错误:",
-	"Site settings saved!": "站点设置已保存!",
-	"Enter your private key:": "输入您的私钥:",
-	" Signed!": " 已签名!",
-	"WebGL not supported": "不支持 WebGL"
-}
diff --git a/plugins/Sidebar/media/Class.coffee b/plugins/Sidebar/media/Class.coffee
deleted file mode 100644
index d62ab25c..00000000
--- a/plugins/Sidebar/media/Class.coffee
+++ /dev/null
@@ -1,23 +0,0 @@
-class Class
-	trace: true
-
-	log: (args...) ->
-		return unless @trace
-		return if typeof console is 'undefined'
-		args.unshift("[#{@.constructor.name}]")
-		console.log(args...)
-		@
-		
-	logStart: (name, args...) ->
-		return unless @trace
-		@logtimers or= {}
-		@logtimers[name] = +(new Date)
-		@log "#{name}", args..., "(started)" if args.length > 0
-		@
-		
-	logEnd: (name, args...) ->
-		ms = +(new Date)-@logtimers[name]
-		@log "#{name}", args..., "(Done in #{ms}ms)"
-		@ 
-
-window.Class = Class
\ No newline at end of file
diff --git a/plugins/Sidebar/media/Console.coffee b/plugins/Sidebar/media/Console.coffee
deleted file mode 100644
index d5a83346..00000000
--- a/plugins/Sidebar/media/Console.coffee
+++ /dev/null
@@ -1,201 +0,0 @@
-class Console extends Class
-	constructor: (@sidebar) ->
-		@tag = null
-		@opened = false
-		@filter = null
-		@tab_types = [
-			{title: "All", filter: ""},
-			{title: "Info", filter: "INFO"},
-			{title: "Warning", filter: "WARNING"},
-			{title: "Error", filter: "ERROR"}
-		]
-		@read_size = 32 * 1024
-		@tab_active = ""
-		#@filter = @sidebar.wrapper.site_info.address_short
-		handleMessageWebsocket_original = @sidebar.wrapper.handleMessageWebsocket
-		@sidebar.wrapper.handleMessageWebsocket = (message) =>
-			if message.cmd == "logLineAdd" and message.params.stream_id == @stream_id
-				@addLines(message.params.lines)
-			else
-				handleMessageWebsocket_original(message)
-
-		$(window).on "hashchange", =>
-			if window.top.location.hash.startsWith("#ZeroNet:Console")
-				@open()
-
-		if window.top.location.hash.startsWith("#ZeroNet:Console")
-			setTimeout (=> @open()), 10
-
-	createHtmltag: ->
-		if not @container
-			@container = $("""
-			<div class="console-container">
-				<div class="console">
-					<div class="console-top">
-						<div class="console-tabs"></div>
-						<div class="console-text">Loading...</div>
-					</div>
-					<div class="console-middle">
-						<div class="mynode"></div>
-						<div class="peers">
-							<div class="peer"><div class="line"></div><a href="#" class="icon">\u25BD</div></div>
-						</div>
-					</div>
-				</div>
-			</div>
-			""")
-			@text = @container.find(".console-text")
-			@text_elem = @text[0]
-			@tabs = @container.find(".console-tabs")
-
-			@text.on "mousewheel", (e) =>  # Stop animation on manual scrolling
-				if e.originalEvent.deltaY < 0
-					@text.stop()
-				RateLimit 300, @checkTextIsBottom
-
-			@text.is_bottom = true
-
-			@container.appendTo(document.body)
-			@tag = @container.find(".console")
-			for tab_type in @tab_types
-				tab = $("<a></a>", {href: "#", "data-filter": tab_type.filter, "data-title": tab_type.title}).text(tab_type.title)
-				if tab_type.filter == @tab_active
-					tab.addClass("active")
-				tab.on("click", @handleTabClick)
-				if window.top.location.hash.endsWith(tab_type.title)
-					@log "Triggering click on", tab
-					tab.trigger("click")
-				@tabs.append(tab)
-
-			@container.on "mousedown touchend touchcancel", (e) =>
-				if e.target != e.currentTarget
-					return true
-				@log "closing"
-				if $(document.body).hasClass("body-console")
-					@close()
-					return true
-
-			@loadConsoleText()
-
-	checkTextIsBottom: =>
-		@text.is_bottom = Math.round(@text_elem.scrollTop + @text_elem.clientHeight) >= @text_elem.scrollHeight - 15
-
-	toColor: (text, saturation=60, lightness=70) ->
-		hash = 0
-		for i in [0..text.length-1]
-			hash += text.charCodeAt(i)*i
-			hash = hash % 1777
-		return "hsl(" + (hash % 360) + ",#{saturation}%,#{lightness}%)";
-
-	formatLine: (line) =>
-		match = line.match(/(\[.*?\])[ ]+(.*?)[ ]+(.*?)[ ]+(.*)/)
-		if not match
-			return line.replace(/\</g, "&lt;").replace(/\>/g, "&gt;")
-
-		[line, added, level, module, text] = line.match(/(\[.*?\])[ ]+(.*?)[ ]+(.*?)[ ]+(.*)/)
-		added = "<span style='color: #dfd0fa'>#{added}</span>"
-		level = "<span style='color: #{@toColor(level, 100)};'>#{level}</span>"
-		module = "<span style='color: #{@toColor(module, 60)}; font-weight: bold;'>#{module}</span>"
-
-		text = text.replace(/(Site:[A-Za-z0-9\.]+)/g, "<span style='color: #AAAAFF'>$1</span>")
-		text = text.replace(/\</g, "&lt;").replace(/\>/g, "&gt;")
-		#text = text.replace(/( [0-9\.]+(|s|ms))/g, "<span style='color: #FFF;'>$1</span>")
-		return "#{added} #{level} #{module} #{text}"
-
-
-	addLines: (lines, animate=true) =>
-		html_lines = []
-		@logStart "formatting"
-		for line in lines
-			html_lines.push @formatLine(line)
-		@logEnd "formatting"
-		@logStart "adding"
-		@text.append(html_lines.join("<br>") + "<br>")
-		@logEnd "adding"
-		if @text.is_bottom and animate
-			@text.stop().animate({scrollTop: @text_elem.scrollHeight - @text_elem.clientHeight + 1}, 600, 'easeInOutCubic')
-
-
-	loadConsoleText: =>
-		@sidebar.wrapper.ws.cmd "consoleLogRead", {filter: @filter, read_size: @read_size}, (res) =>
-			@text.html("")
-			pos_diff = res["pos_end"] - res["pos_start"]
-			size_read = Math.round(pos_diff/1024)
-			size_total = Math.round(res['pos_end']/1024)
-			@text.append("<br><br>")
-			@text.append("Displaying #{res.lines.length} of #{res.num_found} lines found in the last #{size_read}kB of the log file. (#{size_total}kB total)<br>")
-			@addLines res.lines, false
-			@text_elem.scrollTop = @text_elem.scrollHeight
-		if @stream_id
-			@sidebar.wrapper.ws.cmd "consoleLogStreamRemove", {stream_id: @stream_id}
-		@sidebar.wrapper.ws.cmd "consoleLogStream", {filter: @filter}, (res) =>
-			@stream_id = res.stream_id
-
-	close: =>
-		window.top.location.hash = ""
-		@sidebar.move_lock = "y"
-		@sidebar.startDrag()
-		@sidebar.stopDrag()
-
-	open: =>
-		@sidebar.startDrag()
-		@sidebar.moved("y")
-		@sidebar.fixbutton_targety = @sidebar.page_height - @sidebar.fixbutton_inity - 50
-		@sidebar.stopDrag()
-
-	onOpened: =>
-		@sidebar.onClosed()
-		@log "onOpened"
-
-	onClosed: =>
-		$(document.body).removeClass("body-console")
-		if @stream_id
-			@sidebar.wrapper.ws.cmd "consoleLogStreamRemove", {stream_id: @stream_id}
-
-	cleanup: =>
-		if @container
-			@container.remove()
-			@container = null
-
-	stopDragY: =>
-		# Animate sidebar and iframe
-		if @sidebar.fixbutton_targety == @sidebar.fixbutton_inity
-			# Closed
-			targety = 0
-			@opened = false
-		else
-			# Opened
-			targety = @sidebar.fixbutton_targety - @sidebar.fixbutton_inity
-			@onOpened()
-			@opened = true
-
-		# Revent sidebar transitions
-		if @tag
-			@tag.css("transition", "0.5s ease-out")
-			@tag.css("transform", "translateY(#{targety}px)").one transitionEnd, =>
-				@tag.css("transition", "")
-				if not @opened
-					@cleanup()
-		# Revert body transformations
-		@log "stopDragY", "opened:", @opened, targety
-		if not @opened
-			@onClosed()
-
-	changeFilter: (filter) =>
-		@filter = filter
-		if @filter == ""
-			@read_size = 32 * 1024
-		else
-			@read_size = 5 * 1024 * 1024
-		@loadConsoleText()
-
-	handleTabClick: (e) =>
-		elem = $(e.currentTarget)
-		@tab_active = elem.data("filter")
-		$("a", @tabs).removeClass("active")
-		elem.addClass("active")
-		@changeFilter(@tab_active)
-		window.top.location.hash = "#ZeroNet:Console:" + elem.data("title")
-		return false
-
-window.Console = Console
diff --git a/plugins/Sidebar/media/Console.css b/plugins/Sidebar/media/Console.css
deleted file mode 100644
index 127d15bf..00000000
--- a/plugins/Sidebar/media/Console.css
+++ /dev/null
@@ -1,31 +0,0 @@
-.console-container { width: 100%; z-index: 998; position: absolute; top: -100vh; padding-bottom: 100%; }
-.console-container .console { background-color: #212121; height: 100vh; transform: translateY(0px); padding-top: 80px; box-sizing: border-box; }
-
-.console-top { color: white; font-family: Consolas, monospace; font-size: 11px; line-height: 20px; height: 100%; box-sizing: border-box; letter-spacing: 0.5px;}
-.console-text { overflow-y: scroll; height: calc(100% - 10px); color: #DDD; padding: 5px; margin-top: -36px; overflow-wrap: break-word; }
-.console-tabs {
-    background-color: #41193fad; position: relative; margin-right: 17px; /*backdrop-filter: blur(2px);*/
-    box-shadow: -30px 0px 45px #7d2463; background: linear-gradient(-75deg, #591a48ed, #70305e66); border-bottom: 1px solid #792e6473;
-}
-.console-tabs a {
-    margin-right: 5px; padding: 5px 15px; text-decoration: none; color: #AAA;
-    font-size: 11px; font-family: "Consolas"; text-transform: uppercase; border: 1px solid #666;
-    border-bottom: 0px; display: inline-block; margin: 5px; margin-bottom: 0px; background-color: rgba(0,0,0,0.5);
-}
-.console-tabs a:hover { color: #FFF }
-.console-tabs a.active { background-color: #46223c; color: #FFF }
-.console-middle {height: 0px; top: 50%; position: absolute; width: 100%; left: 50%; display: none; }
-
-.console .mynode {
-	border: 0.5px solid #aaa; width: 50px; height: 50px; transform: rotateZ(45deg); margin-top: -25px; margin-left: -25px;
-	opacity: 1; display: inline-block; background-color: #EEE; z-index: 9; position: absolute; outline: 5px solid #EEE;
-}
-.console .peers { width: 0px; height: 0px; position: absolute; left: -20px; top: -20px; text-align: center; }
-.console .peer { left: 0px; top: 0px; position: absolute; }
-.console .peer .icon { width: 20px; height: 20px; padding: 10px; display: inline-block; text-decoration: none; left: 200px; position: absolute; color: #666; }
-.console .peer .icon:before { content: "\25BC"; position: absolute; margin-top: 3px; margin-left: -1px; opacity: 0; transition: all 0.3s }
-.console .peer .icon:hover:before { opacity: 1; transition: none }
-.console .peer .line {
-	width: 187px; border-top: 1px solid #CCC; position: absolute; top: 20px; left: 20px;
-	transform: rotateZ(334deg); transform-origin: bottom left;
-}
diff --git a/plugins/Sidebar/media/Menu.coffee b/plugins/Sidebar/media/Menu.coffee
deleted file mode 100644
index 3e19fd9f..00000000
--- a/plugins/Sidebar/media/Menu.coffee
+++ /dev/null
@@ -1,49 +0,0 @@
-class Menu
-	constructor: (@button) ->
-		@elem = $(".menu.template").clone().removeClass("template")
-		@elem.appendTo("body")
-		@items = []
-
-	show: ->
-		if window.visible_menu and window.visible_menu.button[0] == @button[0] # Same menu visible then hide it
-			window.visible_menu.hide()
-			@hide()
-		else
-			button_pos = @button.offset()
-			left = button_pos.left
-			@elem.css({"top": button_pos.top+@button.outerHeight(), "left": left})
-			@button.addClass("menu-active")
-			@elem.addClass("visible")
-			if @elem.position().left + @elem.width() + 20 > window.innerWidth
-				@elem.css("left", window.innerWidth - @elem.width() - 20)
-			if window.visible_menu then window.visible_menu.hide()
-			window.visible_menu = @
-
-
-	hide: ->
-		@elem.removeClass("visible")
-		@button.removeClass("menu-active")
-		window.visible_menu = null
-
-
-	addItem: (title, cb) ->
-		item = $(".menu-item.template", @elem).clone().removeClass("template")
-		item.html(title)
-		item.on "click", =>
-			if not cb(item)
-				@hide()
-			return false
-		item.appendTo(@elem)
-		@items.push item
-		return item
-
-
-	log: (args...) ->
-		console.log "[Menu]", args...
-
-window.Menu = Menu
-
-# Hide menu on outside click
-$("body").on "click", (e) ->
-	if window.visible_menu and e.target != window.visible_menu.button[0] and $(e.target).parent()[0] != window.visible_menu.elem[0]
-		window.visible_menu.hide()
diff --git a/plugins/Sidebar/media/Menu.css b/plugins/Sidebar/media/Menu.css
deleted file mode 100644
index e2afa16e..00000000
--- a/plugins/Sidebar/media/Menu.css
+++ /dev/null
@@ -1,19 +0,0 @@
-.menu {
-	background-color: white; padding: 10px 0px; position: absolute; top: 0px; left: 0px; max-height: 0px; overflow: hidden; transform: translate(0px, -30px); pointer-events: none;
-	box-shadow: 0px 2px 8px rgba(0,0,0,0.3); border-radius: 2px; opacity: 0; transition: opacity 0.2s ease-out, transform 1s ease-out, max-height 0.2s ease-in-out;
-}
-.menu.visible { opacity: 1; max-height: 350px; transform: translate(0px, 0px); transition: opacity 0.1s ease-out, transform 0.3s ease-out, max-height 0.3s ease-in-out; pointer-events: all }
-
-.menu-item { display: block; text-decoration: none; color: black; padding: 6px 24px; transition: all 0.2s; border-bottom: none; font-weight: normal; padding-left: 30px; }
-.menu-item-separator { margin-top: 5px; border-top: 1px solid #eee }
-
-.menu-item:hover { background-color: #F6F6F6; transition: none; color: inherit; border: none }
-.menu-item:active, .menu-item:focus { background-color: #AF3BFF; color: white; transition: none }
-.menu-item.selected:before {
-	content: "L"; display: inline-block; transform: rotateZ(45deg) scaleX(-1);
-	font-weight: bold; position: absolute; margin-left: -17px; font-size: 12px; margin-top: 2px;
-}
-
-@media only screen and (max-width: 800px) {
-.menu, .menu.visible { position: absolute; left: unset !important; right: 20px; }
-}
\ No newline at end of file
diff --git a/plugins/Sidebar/media/Prototypes.coffee b/plugins/Sidebar/media/Prototypes.coffee
deleted file mode 100644
index a9edd255..00000000
--- a/plugins/Sidebar/media/Prototypes.coffee
+++ /dev/null
@@ -1,9 +0,0 @@
-String::startsWith = (s) -> @[...s.length] is s
-String::endsWith = (s) -> s is '' or @[-s.length..] is s
-String::capitalize = ->  if @.length then @[0].toUpperCase() + @.slice(1) else ""
-String::repeat = (count) -> new Array( count + 1 ).join(@)
-
-window.isEmpty = (obj) ->
-	for key of obj
-		return false
-	return true
diff --git a/plugins/Sidebar/media/RateLimit.coffee b/plugins/Sidebar/media/RateLimit.coffee
deleted file mode 100644
index 17c67433..00000000
--- a/plugins/Sidebar/media/RateLimit.coffee
+++ /dev/null
@@ -1,14 +0,0 @@
-limits = {}
-call_after_interval = {}
-window.RateLimit = (interval, fn) ->
-	if not limits[fn]
-		call_after_interval[fn] = false
-		fn() # First call is not delayed
-		limits[fn] = setTimeout (->
-			if call_after_interval[fn]
-				fn()
-			delete limits[fn]
-			delete call_after_interval[fn]
-		), interval
-	else # Called within iterval, delay the call
-		call_after_interval[fn] = true
diff --git a/plugins/Sidebar/media/Scrollable.js b/plugins/Sidebar/media/Scrollable.js
deleted file mode 100644
index 689a5719..00000000
--- a/plugins/Sidebar/media/Scrollable.js
+++ /dev/null
@@ -1,91 +0,0 @@
-/* via http://jsfiddle.net/elGrecode/00dgurnn/ */
-
-window.initScrollable = function () {
-
-    var scrollContainer = document.querySelector('.scrollable'),
-        scrollContentWrapper = document.querySelector('.scrollable .content-wrapper'),
-        scrollContent = document.querySelector('.scrollable .content'),
-        contentPosition = 0,
-        scrollerBeingDragged = false,
-        scroller,
-        topPosition,
-        scrollerHeight;
-
-    function calculateScrollerHeight() {
-        // *Calculation of how tall scroller should be
-        var visibleRatio = scrollContainer.offsetHeight / scrollContentWrapper.scrollHeight;
-        if (visibleRatio == 1)
-            scroller.style.display = "none";
-        else
-            scroller.style.display = "block";
-        return visibleRatio * scrollContainer.offsetHeight;
-    }
-
-    function moveScroller(evt) {
-        // Move Scroll bar to top offset
-        var scrollPercentage = evt.target.scrollTop / scrollContentWrapper.scrollHeight;
-        topPosition = scrollPercentage * (scrollContainer.offsetHeight - 5); // 5px arbitrary offset so scroll bar doesn't move too far beyond content wrapper bounding box
-        scroller.style.top = topPosition + 'px';
-    }
-
-    function startDrag(evt) {
-        normalizedPosition = evt.pageY;
-        contentPosition = scrollContentWrapper.scrollTop;
-        scrollerBeingDragged = true;
-        window.addEventListener('mousemove', scrollBarScroll);
-        return false;
-    }
-
-    function stopDrag(evt) {
-        scrollerBeingDragged = false;
-        window.removeEventListener('mousemove', scrollBarScroll);
-    }
-
-    function scrollBarScroll(evt) {
-        if (scrollerBeingDragged === true) {
-            evt.preventDefault();
-            var mouseDifferential = evt.pageY - normalizedPosition;
-            var scrollEquivalent = mouseDifferential * (scrollContentWrapper.scrollHeight / scrollContainer.offsetHeight);
-            scrollContentWrapper.scrollTop = contentPosition + scrollEquivalent;
-        }
-    }
-
-    function updateHeight() {
-        scrollerHeight = calculateScrollerHeight() - 10;
-        scroller.style.height = scrollerHeight + 'px';
-    }
-
-    function createScroller() {
-        // *Creates scroller element and appends to '.scrollable' div
-        // create scroller element
-        scroller = document.createElement("div");
-        scroller.className = 'scroller';
-
-        // determine how big scroller should be based on content
-        scrollerHeight = calculateScrollerHeight() - 10;
-
-        if (scrollerHeight / scrollContainer.offsetHeight < 1) {
-            // *If there is a need to have scroll bar based on content size
-            scroller.style.height = scrollerHeight + 'px';
-
-            // append scroller to scrollContainer div
-            scrollContainer.appendChild(scroller);
-
-            // show scroll path divot
-            scrollContainer.className += ' showScroll';
-
-            // attach related draggable listeners
-            scroller.addEventListener('mousedown', startDrag);
-            window.addEventListener('mouseup', stopDrag);
-        }
-
-    }
-
-    createScroller();
-
-
-    // *** Listeners ***
-    scrollContentWrapper.addEventListener('scroll', moveScroller);
-
-    return updateHeight;
-};
\ No newline at end of file
diff --git a/plugins/Sidebar/media/Scrollbable.css b/plugins/Sidebar/media/Scrollbable.css
deleted file mode 100644
index 6e3e0b6a..00000000
--- a/plugins/Sidebar/media/Scrollbable.css
+++ /dev/null
@@ -1,44 +0,0 @@
-.scrollable {
-    overflow: hidden;
-}
-
-.scrollable.showScroll::after {
-    position: absolute;
-    content: '';
-    top: 5%;
-    right: 7px;
-    height: 90%;
-    width: 3px;
-    background: rgba(224, 224, 255, .3);
-}
-
-.scrollable .content-wrapper {
-    width: 100%;
-    height: 100%;
-    padding-right: 50%;
-    overflow-y: scroll;
-}
-.scroller {
-    margin-top: 5px;
-    z-index: 5;
-    cursor: pointer;
-    position: absolute;
-    width: 7px;
-    border-radius: 5px;
-    background: #3A3A3A;
-    top: 0px;
-    left: 395px;
-    -webkit-transition: top .08s;
-    -moz-transition: top .08s;
-    -ms-transition: top .08s;
-    -o-transition: top .08s;
-    transition: top .08s;
-}
-.scroller {
-    -webkit-touch-callout: none;
-    -webkit-user-select: none;
-    -khtml-user-select: none;
-    -moz-user-select: none;
-    -ms-user-select: none;
-    user-select: none;
-}
diff --git a/plugins/Sidebar/media/Sidebar.coffee b/plugins/Sidebar/media/Sidebar.coffee
deleted file mode 100644
index 57d36eac..00000000
--- a/plugins/Sidebar/media/Sidebar.coffee
+++ /dev/null
@@ -1,644 +0,0 @@
-class Sidebar extends Class
-	constructor: (@wrapper) ->
-		@tag = null
-		@container = null
-		@opened = false
-		@width = 410
-		@console = new Console(@)
-		@fixbutton = $(".fixbutton")
-		@fixbutton_addx = 0
-		@fixbutton_addy = 0
-		@fixbutton_initx = 0
-		@fixbutton_inity = 15
-		@fixbutton_targetx = 0
-		@move_lock = null
-		@page_width = $(window).width()
-		@page_height = $(window).height()
-		@frame = $("#inner-iframe")
-		@initFixbutton()
-		@dragStarted = 0
-		@globe = null
-		@preload_html = null
-
-		@original_set_site_info = @wrapper.setSiteInfo  # We going to override this, save the original
-
-		# Start in opened state for debugging
-		if window.top.location.hash == "#ZeroNet:OpenSidebar"
-			@startDrag()
-			@moved("x")
-			@fixbutton_targetx = @fixbutton_initx - @width
-			@stopDrag()
-
-
-	initFixbutton: ->
-
-		# Detect dragging
-		@fixbutton.on "mousedown touchstart", (e) =>
-			if e.button > 0  # Right or middle click
-				return
-			e.preventDefault()
-
-			# Disable previous listeners
-			@fixbutton.off "click touchend touchcancel"
-
-			# Make sure its not a click
-			@dragStarted = (+ new Date)
-
-			# Fullscreen drag bg to capture mouse events over iframe
-			$(".drag-bg").remove()
-			$("<div class='drag-bg'></div>").appendTo(document.body)
-
-			$("body").one "mousemove touchmove", (e) =>
-				mousex = e.pageX
-				mousey = e.pageY
-				if not mousex
-					mousex = e.originalEvent.touches[0].pageX
-					mousey = e.originalEvent.touches[0].pageY
-
-				@fixbutton_addx = @fixbutton.offset().left - mousex
-				@fixbutton_addy = @fixbutton.offset().top - mousey
-				@startDrag()
-		@fixbutton.parent().on "click touchend touchcancel", (e) =>
-			if (+ new Date) - @dragStarted < 100
-				window.top.location = @fixbutton.find(".fixbutton-bg").attr("href")
-			@stopDrag()
-		@resized()
-		$(window).on "resize", @resized
-
-	resized: =>
-		@page_width = $(window).width()
-		@page_height = $(window).height()
-		@fixbutton_initx = @page_width - 75  # Initial x position
-		if @opened
-			@fixbutton.css
-				left: @fixbutton_initx - @width
-		else
-			@fixbutton.css
-				left: @fixbutton_initx
-
-	# Start dragging the fixbutton
-	startDrag: ->
-		#@move_lock = "x"  # Temporary until internals not finished
-		@log "startDrag", @fixbutton_initx, @fixbutton_inity
-		@fixbutton_targetx = @fixbutton_initx  # Fallback x position
-		@fixbutton_targety = @fixbutton_inity  # Fallback y position
-
-		@fixbutton.addClass("dragging")
-
-		# IE position wrap fix
-		if navigator.userAgent.indexOf('MSIE') != -1 or navigator.appVersion.indexOf('Trident/') > 0
-			@fixbutton.css("pointer-events", "none")
-
-		# Don't go to homepage
-		@fixbutton.one "click", (e) =>
-			@stopDrag()
-			@fixbutton.removeClass("dragging")
-			moved_x = Math.abs(@fixbutton.offset().left - @fixbutton_initx)
-			moved_y = Math.abs(@fixbutton.offset().top - @fixbutton_inity)
-			if moved_x > 5 or moved_y > 10
-				# If moved more than some pixel the button then don't go to homepage
-				e.preventDefault()
-
-		# Animate drag
-		@fixbutton.parents().on "mousemove touchmove", @animDrag
-		@fixbutton.parents().on "mousemove touchmove" ,@waitMove
-
-		# Stop dragging listener
-		@fixbutton.parents().one "mouseup touchend touchcancel", (e) =>
-			e.preventDefault()
-			@stopDrag()
-
-
-	# Wait for moving the fixbutton
-	waitMove: (e) =>
-		document.body.style.perspective = "1000px"
-		document.body.style.height = "100%"
-		document.body.style.willChange = "perspective"
-		document.documentElement.style.height = "100%"
-		#$(document.body).css("backface-visibility", "hidden").css("perspective", "1000px").css("height", "900px")
-		# $("iframe").css("backface-visibility", "hidden")
-
-		moved_x = Math.abs(parseInt(@fixbutton[0].style.left) - @fixbutton_targetx)
-		moved_y = Math.abs(parseInt(@fixbutton[0].style.top) - @fixbutton_targety)
-		if moved_x > 5 and (+ new Date) - @dragStarted + moved_x > 50
-			@moved("x")
-			@fixbutton.stop().animate {"top": @fixbutton_inity}, 1000
-			@fixbutton.parents().off "mousemove touchmove" ,@waitMove
-
-		else if moved_y > 5 and (+ new Date) - @dragStarted + moved_y > 50
-			@moved("y")
-			@fixbutton.parents().off "mousemove touchmove" ,@waitMove
-
-	moved: (direction) ->
-		@log "Moved", direction
-		@move_lock = direction
-		if direction == "y"
-			$(document.body).addClass("body-console")
-			return @console.createHtmltag()
-		@createHtmltag()
-		$(document.body).addClass("body-sidebar")
-		@container.on "mousedown touchend touchcancel", (e) =>
-			if e.target != e.currentTarget
-				return true
-			@log "closing"
-			if $(document.body).hasClass("body-sidebar")
-				@close()
-				return true
-
-		$(window).off "resize"
-		$(window).on "resize", =>
-			$(document.body).css "height", $(window).height()
-			@scrollable()
-			@resized()
-
-		# Override setsiteinfo to catch changes
-		@wrapper.setSiteInfo = (site_info) =>
-			@setSiteInfo(site_info)
-			@original_set_site_info.apply(@wrapper, arguments)
-
-		# Preload world.jpg
-		img = new Image();
-		img.src = "/uimedia/globe/world.jpg";
-
-	setSiteInfo: (site_info) ->
-		RateLimit 1500, =>
-			@updateHtmlTag()
-		RateLimit 30000, =>
-			@displayGlobe()
-
-	# Create the sidebar html tag
-	createHtmltag: ->
-		@when_loaded = $.Deferred()
-		if not @container
-			@container = $("""
-			<div class="sidebar-container"><div class="sidebar scrollable"><div class="content-wrapper"><div class="content">
-			</div></div></div></div>
-			""")
-			@container.appendTo(document.body)
-			@tag = @container.find(".sidebar")
-			@updateHtmlTag()
-			@scrollable = window.initScrollable()
-
-
-	updateHtmlTag: ->
-		if @preload_html
-			@setHtmlTag(@preload_html)
-			@preload_html = null
-		else
-			@wrapper.ws.cmd "sidebarGetHtmlTag", {}, @setHtmlTag
-
-	setHtmlTag: (res) =>
-		if @tag.find(".content").children().length == 0 # First update
-			@log "Creating content"
-			@container.addClass("loaded")
-			morphdom(@tag.find(".content")[0], '<div class="content">'+res+'</div>')
-			# @scrollable()
-			@when_loaded.resolve()
-
-		else  # Not first update, patch the html to keep unchanged dom elements
-			morphdom @tag.find(".content")[0], '<div class="content">'+res+'</div>', {
-				onBeforeMorphEl: (from_el, to_el) ->  # Ignore globe loaded state
-					if from_el.className == "globe" or from_el.className.indexOf("noupdate") >= 0
-						return false
-					else
-						return true
-				}
-
-		# Save and forget privatekey for site signing
-		@tag.find("#privatekey-add").off("click, touchend").on "click touchend", (e) =>
-			@wrapper.displayPrompt "Enter your private key:", "password", "Save", "", (privatekey) =>
-				@wrapper.ws.cmd "userSetSitePrivatekey", [privatekey], (res) =>
-					@wrapper.notifications.add "privatekey", "done", "Private key saved for site signing", 5000
-			return false
-
-		@tag.find("#privatekey-forget").off("click, touchend").on "click touchend", (e) =>
-			@wrapper.displayConfirm "Remove saved private key for this site?", "Forget", (res) =>
-				if not res
-					return false
-				@wrapper.ws.cmd "userSetSitePrivatekey", [""], (res) =>
-					@wrapper.notifications.add "privatekey", "done", "Saved private key removed", 5000
-			return false
-
-		# Use requested address for browse files urls
-		@tag.find("#browse-files").attr("href", document.location.pathname.replace(/(\/.*?(\/|$)).*$/, "/list$1"))
-
-
-
-	animDrag: (e) =>
-		mousex = e.pageX
-		mousey = e.pageY
-		if not mousex and e.originalEvent.touches
-			mousex = e.originalEvent.touches[0].pageX
-			mousey = e.originalEvent.touches[0].pageY
-
-		overdrag = @fixbutton_initx - @width - mousex
-		if overdrag > 0  # Overdragged
-			overdrag_percent = 1 + overdrag/300
-			mousex = (mousex + (@fixbutton_initx-@width)*overdrag_percent)/(1+overdrag_percent)
-		targetx = @fixbutton_initx - mousex - @fixbutton_addx
-		targety = @fixbutton_inity - mousey - @fixbutton_addy
-
-		if @move_lock == "x"
-			targety = @fixbutton_inity
-		else if @move_lock == "y"
-			targetx = @fixbutton_initx
-
-		if not @move_lock or @move_lock == "x"
-			@fixbutton[0].style.left = (mousex + @fixbutton_addx) + "px"
-			if @tag
-				@tag[0].style.transform = "translateX(#{0 - targetx}px)"
-
-		if not @move_lock or @move_lock == "y"
-			@fixbutton[0].style.top = (mousey + @fixbutton_addy) + "px"
-			if @console.tag
-				@console.tag[0].style.transform = "translateY(#{0 - targety}px)"
-
-		#if @move_lock == "x"
-			# @fixbutton[0].style.left = "#{@fixbutton_targetx} px"
-			#@fixbutton[0].style.top = "#{@fixbutton_inity}px"
-		#if @move_lock == "y"
-		#	@fixbutton[0].style.top = "#{@fixbutton_targety} px"
-
-		# Check if opened
-		if (not @opened and targetx > @width/3) or (@opened and targetx > @width*0.9)
-			@fixbutton_targetx = @fixbutton_initx - @width  # Make it opened
-		else
-			@fixbutton_targetx = @fixbutton_initx
-
-		if (not @console.opened and 0 - targety > @page_height/10) or (@console.opened and 0 - targety > @page_height*0.8)
-			@fixbutton_targety = @page_height - @fixbutton_inity - 50
-		else
-			@fixbutton_targety = @fixbutton_inity
-
-
-	# Stop dragging the fixbutton
-	stopDrag: ->
-		@fixbutton.parents().off "mousemove touchmove"
-		@fixbutton.off "mousemove touchmove"
-		@fixbutton.css("pointer-events", "")
-		$(".drag-bg").remove()
-		if not @fixbutton.hasClass("dragging")
-			return
-		@fixbutton.removeClass("dragging")
-
-		# Move back to initial position
-		if @fixbutton_targetx != @fixbutton.offset().left or @fixbutton_targety != @fixbutton.offset().top
-			# Animate fixbutton
-			if @move_lock == "y"
-				top = @fixbutton_targety
-				left = @fixbutton_initx
-			if @move_lock == "x"
-				top = @fixbutton_inity
-				left = @fixbutton_targetx
-			@fixbutton.stop().animate {"left": left, "top": top}, 500, "easeOutBack", =>
-				# Switch back to auto align
-				if @fixbutton_targetx == @fixbutton_initx  # Closed
-					@fixbutton.css("left", "auto")
-				else  # Opened
-					@fixbutton.css("left", left)
-
-				$(".fixbutton-bg").trigger "mouseout"  # Switch fixbutton back to normal status
-
-			@stopDragX()
-			@console.stopDragY()
-		@move_lock = null
-
-	stopDragX: ->
-		# Animate sidebar and iframe
-		if @fixbutton_targetx == @fixbutton_initx or @move_lock == "y"
-			# Closed
-			targetx = 0
-			@opened = false
-		else
-			# Opened
-			targetx = @width
-			if @opened
-				@onOpened()
-			else
-				@when_loaded.done =>
-					@onOpened()
-			@opened = true
-
-		# Revent sidebar transitions
-		if @tag
-			@tag.css("transition", "0.4s ease-out")
-			@tag.css("transform", "translateX(-#{targetx}px)").one transitionEnd, =>
-				@tag.css("transition", "")
-				if not @opened
-					@container.remove()
-					@container = null
-					if @tag
-						@tag.remove()
-						@tag = null
-
-		# Revert body transformations
-		@log "stopdrag", "opened:", @opened
-		if not @opened
-			@onClosed()
-
-	sign: (inner_path, privatekey) ->
-		@wrapper.displayProgress("sign", "Signing: #{inner_path}...", 0)
-		@wrapper.ws.cmd "siteSign", {privatekey: privatekey, inner_path: inner_path, update_changed_files: true}, (res) =>
-			if res == "ok"
-				@wrapper.displayProgress("sign", "#{inner_path} signed!", 100)
-			else
-				@wrapper.displayProgress("sign", "Error signing #{inner_path}", -1)
-
-	publish: (inner_path, privatekey) ->
-		@wrapper.ws.cmd "sitePublish", {privatekey: privatekey, inner_path: inner_path, sign: true, update_changed_files: true}, (res) =>
-			if res == "ok"
-				@wrapper.notifications.add "sign", "done", "#{inner_path} Signed and published!", 5000
-
-	handleSiteDeleteClick: ->
-		if @wrapper.site_info.privatekey
-			question = "Are you sure?<br>This site has a saved private key"
-			options = ["Forget private key and delete site"]
-		else
-			question = "Are you sure?"
-			options = ["Delete this site", "Blacklist"]
-		@wrapper.displayConfirm question, options, (confirmed) =>
-			if confirmed == 1
-				@tag.find("#button-delete").addClass("loading")
-				@wrapper.ws.cmd "siteDelete", @wrapper.site_info.address, ->
-					document.location = $(".fixbutton-bg").attr("href")
-			else if confirmed == 2
-				@wrapper.displayPrompt "Blacklist this site", "text", "Delete and Blacklist", "Reason", (reason) =>
-					@tag.find("#button-delete").addClass("loading")
-					@wrapper.ws.cmd "siteblockAdd", [@wrapper.site_info.address, reason]
-					@wrapper.ws.cmd "siteDelete", @wrapper.site_info.address, ->
-						document.location = $(".fixbutton-bg").attr("href")
-
-	onOpened: ->
-		@log "Opened"
-		@scrollable()
-
-		# Re-calculate height when site admin opened or closed
-		@tag.find("#checkbox-owned, #checkbox-autodownloadoptional").off("click touchend").on "click touchend", =>
-			setTimeout (=>
-				@scrollable()
-			), 300
-
-		# Site limit button
-		@tag.find("#button-sitelimit").off("click touchend").on "click touchend", =>
-			@wrapper.ws.cmd "siteSetLimit", $("#input-sitelimit").val(), (res) =>
-				if res == "ok"
-					@wrapper.notifications.add "done-sitelimit", "done", "Site storage limit modified!", 5000
-				@updateHtmlTag()
-			return false
-
-		# Site autodownload limit button
-		@tag.find("#button-autodownload_bigfile_size_limit").off("click touchend").on "click touchend", =>
-			@wrapper.ws.cmd "siteSetAutodownloadBigfileLimit", $("#input-autodownload_bigfile_size_limit").val(), (res) =>
-				if res == "ok"
-					@wrapper.notifications.add "done-bigfilelimit", "done", "Site bigfile auto download limit modified!", 5000
-				@updateHtmlTag()
-			return false
-
-		# Site start download optional files
-		@tag.find("#button-autodownload_previous").off("click touchend").on "click touchend", =>
-			@wrapper.ws.cmd "siteUpdate", {"address": @wrapper.site_info.address, "check_files": true}, =>
-				@wrapper.notifications.add "done-download_optional", "done", "Optional files downloaded", 5000
-
-			@wrapper.notifications.add "start-download_optional", "info", "Optional files download started", 5000
-			return false
-
-		# Database reload
-		@tag.find("#button-dbreload").off("click touchend").on "click touchend", =>
-			@wrapper.ws.cmd "dbReload", [], =>
-				@wrapper.notifications.add "done-dbreload", "done", "Database schema reloaded!", 5000
-				@updateHtmlTag()
-			return false
-
-		# Database rebuild
-		@tag.find("#button-dbrebuild").off("click touchend").on "click touchend", =>
-			@wrapper.notifications.add "done-dbrebuild", "info", "Database rebuilding...."
-			@wrapper.ws.cmd "dbRebuild", [], =>
-				@wrapper.notifications.add "done-dbrebuild", "done", "Database rebuilt!", 5000
-				@updateHtmlTag()
-			return false
-
-		# Update site
-		@tag.find("#button-update").off("click touchend").on "click touchend", =>
-			@tag.find("#button-update").addClass("loading")
-			@wrapper.ws.cmd "siteUpdate", @wrapper.site_info.address, =>
-				@wrapper.notifications.add "done-updated", "done", "Site updated!", 5000
-				@tag.find("#button-update").removeClass("loading")
-			return false
-
-		# Pause site
-		@tag.find("#button-pause").off("click touchend").on "click touchend", =>
-			@tag.find("#button-pause").addClass("hidden")
-			@wrapper.ws.cmd "sitePause", @wrapper.site_info.address
-			return false
-
-		# Resume site
-		@tag.find("#button-resume").off("click touchend").on "click touchend", =>
-			@tag.find("#button-resume").addClass("hidden")
-			@wrapper.ws.cmd "siteResume", @wrapper.site_info.address
-			return false
-
-		# Delete site
-		@tag.find("#button-delete").off("click touchend").on "click touchend", =>
-			@handleSiteDeleteClick()
-			return false
-
-		# Owned checkbox
-		@tag.find("#checkbox-owned").off("click touchend").on "click touchend", =>
-			owned = @tag.find("#checkbox-owned").is(":checked")
-			@wrapper.ws.cmd "siteSetOwned", [owned], (res_set_owned) =>
-				@log "Owned", owned
-				if owned
-					@wrapper.ws.cmd "siteRecoverPrivatekey", [], (res_recover) =>
-						if res_recover == "ok"
-							@wrapper.notifications.add("recover", "done", "Private key recovered from master seed", 5000)
-						else
-							@log "Unable to recover private key: #{res_recover.error}"
-
-
-		# Owned auto download checkbox
-		@tag.find("#checkbox-autodownloadoptional").off("click touchend").on "click touchend", =>
-			@wrapper.ws.cmd "siteSetAutodownloadoptional", [@tag.find("#checkbox-autodownloadoptional").is(":checked")]
-
-		# Change identity button
-		@tag.find("#button-identity").off("click touchend").on "click touchend", =>
-			@wrapper.ws.cmd "certSelect"
-			return false
-
-		# Save settings
-		@tag.find("#button-settings").off("click touchend").on "click touchend", =>
-			@wrapper.ws.cmd "fileGet", "content.json", (res) =>
-				data = JSON.parse(res)
-				data["title"] = $("#settings-title").val()
-				data["description"] = $("#settings-description").val()
-				json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t')))
-				@wrapper.ws.cmd "fileWrite", ["content.json", btoa(json_raw), true], (res) =>
-					if res != "ok" # fileWrite failed
-						@wrapper.notifications.add "file-write", "error", "File write error: #{res}"
-					else
-						@wrapper.notifications.add "file-write", "done", "Site settings saved!", 5000
-						if @wrapper.site_info.privatekey
-							@wrapper.ws.cmd "siteSign", {privatekey: "stored", inner_path: "content.json", update_changed_files: true}
-						@updateHtmlTag()
-			return false
-
-
-		# Open site directory
-		@tag.find("#link-directory").off("click touchend").on "click touchend", =>
-			@wrapper.ws.cmd "serverShowdirectory", ["site", @wrapper.site_info.address]
-			return false
-
-		# Copy site with peers
-		@tag.find("#link-copypeers").off("click touchend").on "click touchend", (e) =>
-			copy_text = e.currentTarget.href
-			handler = (e) =>
-				e.clipboardData.setData('text/plain', copy_text)
-				e.preventDefault()
-				@wrapper.notifications.add "copy", "done", "Site address with peers copied to your clipboard", 5000
-				document.removeEventListener('copy', handler, true)
-
-			document.addEventListener('copy', handler, true)
-			document.execCommand('copy')
-			return false
-
-		# Sign and publish content.json
-		$(document).on "click touchend", =>
-			@tag?.find("#button-sign-publish-menu").removeClass("visible")
-			@tag?.find(".contents + .flex").removeClass("sign-publish-flex")
-
-		@tag.find(".contents-content").off("click touchend").on "click touchend", (e) =>
-			$("#input-contents").val(e.currentTarget.innerText);
-			return false;
-
-		menu = new Menu(@tag.find("#menu-sign-publish"))
-		menu.elem.css("margin-top", "-130px")  # Open upwards
-		menu.addItem "Sign", =>
-			inner_path = @tag.find("#input-contents").val()
-
-			@wrapper.ws.cmd "fileRules", {inner_path: inner_path}, (rules) =>
-				if @wrapper.site_info.auth_address in rules.signers
-					# ZeroID or other ID provider
-					@sign(inner_path)
-				else if @wrapper.site_info.privatekey
-					# Privatekey stored in users.json
-					@sign(inner_path, "stored")
-				else
-					# Ask the user for privatekey
-					@wrapper.displayPrompt "Enter your private key:", "password", "Sign", "", (privatekey) => # Prompt the private key
-						@sign(inner_path, privatekey)
-
-			@tag.find(".contents + .flex").removeClass "active"
-			menu.hide()
-
-		menu.addItem "Publish", =>
-			inner_path = @tag.find("#input-contents").val()
-			@wrapper.ws.cmd "sitePublish", {"inner_path": inner_path, "sign": false}
-
-			@tag.find(".contents + .flex").removeClass "active"
-			menu.hide()
-
-		@tag.find("#menu-sign-publish").off("click touchend").on "click touchend", =>
-			if window.visible_menu == menu
-				@tag.find(".contents + .flex").removeClass "active"
-				menu.hide()
-			else
-				@tag.find(".contents + .flex").addClass "active"
-				@tag.find(".content-wrapper").prop "scrollTop", 10000
-				menu.show()
-			return false
-
-		$("body").on "click", =>
-			if @tag
-				@tag.find(".contents + .flex").removeClass "active"
-
-		@tag.find("#button-sign-publish").off("click touchend").on "click touchend", =>
-			inner_path = @tag.find("#input-contents").val()
-
-			@wrapper.ws.cmd "fileRules", {inner_path: inner_path}, (rules) =>
-				if @wrapper.site_info.auth_address in rules.signers
-					# ZeroID or other ID provider
-					@publish(inner_path, null)
-				else if @wrapper.site_info.privatekey
-					# Privatekey stored in users.json
-					@publish(inner_path, "stored")
-				else
-					# Ask the user for privatekey
-					@wrapper.displayPrompt "Enter your private key:", "password", "Sign", "", (privatekey) => # Prompt the private key
-						@publish(inner_path, privatekey)
-			return false
-
-		# Close
-		@tag.find(".close").off("click touchend").on "click touchend", (e) =>
-			@close()
-			return false
-
-		@loadGlobe()
-
-	close: ->
-		@move_lock = "x"
-		@startDrag()
-		@stopDrag()
-
-
-	onClosed: ->
-		$(window).off "resize"
-		$(window).on "resize", @resized
-		$(document.body).css("transition", "0.6s ease-in-out").removeClass("body-sidebar").on transitionEnd, (e) =>
-			if e.target == document.body and not $(document.body).hasClass("body-sidebar") and not $(document.body).hasClass("body-console")
-				$(document.body).css("height", "auto").css("perspective", "").css("will-change", "").css("transition", "").off transitionEnd
-				@unloadGlobe()
-
-		# We dont need site info anymore
-		@wrapper.setSiteInfo = @original_set_site_info
-
-
-	loadGlobe: =>
-		if @tag.find(".globe").hasClass("loading")
-			setTimeout (=>
-				if typeof(DAT) == "undefined"  # Globe script not loaded, do it first
-					script_tag = $("<script>")
-					script_tag.attr("nonce", @wrapper.script_nonce)
-					script_tag.attr("src", "/uimedia/globe/all.js")
-					script_tag.on("load", @displayGlobe)
-					document.head.appendChild(script_tag[0])
-				else
-					@displayGlobe()
-			), 600
-
-
-	displayGlobe: =>
-		img = new Image();
-		img.src = "/uimedia/globe/world.jpg";
-		img.onload = =>
-			@wrapper.ws.cmd "sidebarGetPeers", [], (globe_data) =>
-				if @globe
-					@globe.scene.remove(@globe.points)
-					@globe.addData( globe_data, {format: 'magnitude', name: "hello", animated: false} )
-					@globe.createPoints()
-					@tag?.find(".globe").removeClass("loading")
-				else if typeof(DAT) != "undefined"
-					try
-						@globe = new DAT.Globe( @tag.find(".globe")[0], {"imgDir": "/uimedia/globe/"} )
-						@globe.addData( globe_data, {format: 'magnitude', name: "hello"} )
-						@globe.createPoints()
-						@globe.animate()
-					catch e
-						console.log "WebGL error", e
-						@tag?.find(".globe").addClass("error").text("WebGL not supported")
-					@tag?.find(".globe").removeClass("loading")
-
-
-
-	unloadGlobe: =>
-		if not @globe
-			return false
-		@globe.unload()
-		@globe = null
-
-
-wrapper = window.wrapper
-setTimeout ( ->
-	window.sidebar = new Sidebar(wrapper)
-), 500
-
-
-window.transitionEnd = 'transitionend webkitTransitionEnd oTransitionEnd otransitionend'
diff --git a/plugins/Sidebar/media/Sidebar.css b/plugins/Sidebar/media/Sidebar.css
deleted file mode 100644
index 04ae1ba7..00000000
--- a/plugins/Sidebar/media/Sidebar.css
+++ /dev/null
@@ -1,169 +0,0 @@
-.menu {
-	font-family: Roboto, 'Segoe UI', 'Helvetica Neue'; z-index: 999;
-}
-
-.drag-bg { width: 100%; height: 100%; position: fixed; }
-.fixbutton.dragging { cursor: -webkit-grabbing; }
-.fixbutton-bg:active { cursor: -webkit-grabbing; }
-
-
-.body-sidebar, .body-console { background-color: #666 !important;  }
-#inner-iframe { transition: 0.3s ease-in-out; transform-origin: left bottom; }
-.body-sidebar iframe { transform: rotateY(5deg); opacity: 0.8; pointer-events: none; outline: 1px solid transparent }
-.body-console iframe { transform: rotateX(5deg); opacity: 0.8; pointer-events: none; outline: 1px solid transparent }
-
-.sidebar .label-right { float: right; margin-right: 7px; margin-top: 1px; float: right; }
-.sidebar .link-right { color: white; text-decoration: none; border-bottom: 1px solid #666; text-transform: uppercase; }
-.sidebar .link-right:hover { border-color: #CCC; }
-.sidebar .link-right:active { background-color: #444 }
-.sidebar .link-outline { outline: 1px solid #eee6; padding: 2px 13px; border-bottom: none; font-size: 80%; }
-/* SIDEBAR */
-
-.sidebar-container { width: 100%; height: 100%; overflow: hidden; position: fixed; top: 0px; z-index: 2;}
-.sidebar { background-color: #212121; position: fixed; backface-visibility: hidden; right: -1200px; height: 100%; width: 1200px; } /*box-shadow: inset 0px 0px 10px #000*/
-.sidebar .content { margin: 30px; font-family: "Segoe UI Light", "Segoe UI", "Helvetica Neue"; color: white; width: 375px; height: 300px; font-weight: 200; transition: all 1s; opacity: 0 }
-.sidebar-container.loaded .content { opacity: 1; transform: none }
-.sidebar h1, .sidebar h2 { font-weight: lighter; }
-.sidebar .close { color: #999; float: right; text-decoration: none; margin-top: -5px; padding: 0px 5px; font-size: 33px; margin-right: 20px; display: none }
-.sidebar .button { margin: 0px; display: inline-block; transition: all 0.3s; box-sizing: border-box; max-width: 260px }
-.sidebar .button.hidden { padding: 0px; max-width: 0px; opacity: 0; pointer-events: none }
-.sidebar #button-delete { background-color: transparent; border: 1px solid #333; color: #AAA; margin-left: 10px }
-.sidebar #button-delete:hover { border: 1px solid #666; color: white }
-
-.sidebar .flex { display: flex }
-.sidebar .flex .input.text, .sidebar .flex input.text { width: 100%; }
-.sidebar .flex .button { margin-left: 4px; white-space: nowrap; }
-
-/* FIELDS */
-
-.sidebar .fields { padding: 0px; list-style-type: none; width: 355px; }
-.sidebar .fields > li, .sidebar .fields .settings-owned > li { margin-bottom: 30px }
-.sidebar .fields > li:after, .sidebar .fields .settings-owned > li:after { clear: both; content: ''; display: block }
-.sidebar .fields label {
-	font-family: Consolas, monospace; text-transform: uppercase; font-size: 13px; color: #ACACAC; display: inline-block; margin-bottom: 10px;
-	vertical-align: text-bottom; margin-right: 10px; width: 100%
-}
-.sidebar .fields label small { font-weight: normal; color: white; text-transform: none; }
-.sidebar .fields .text { background-color: black; border: 0px; padding: 10px; color: white; border-radius: 3px; width: 260px; font-family: Consolas, monospace; }
-.sidebar .fields .text.long { width: 330px; font-size: 72%; }
-.sidebar .fields .disabled { color: #AAA; background-color: #3B3B3B; }
-.sidebar .fields .text-num { width: 30px; text-align: right; padding-right: 30px; }
-.sidebar .fields .text-post { color: white; font-family: Consolas, monospace; display: inline-block; font-size: 13px; margin-left: -25px; width: 25px; }
-
-/* Select */
-.sidebar .fields select {
-	width: 225px; background-color: #3B3B3B; color: white; font-family: Consolas, monospace; appearance: none;
-	padding: 5px; padding-right: 25px; border: 0px; border-radius: 3px; height: 35px; vertical-align: 1px; box-shadow: 0px 1px 2px rgba(0,0,0,0.5);
-}
-.sidebar .fields .select-down { margin-left: -39px; width: 34px; display: inline-block; transform: rotateZ(90deg); height: 35px; vertical-align: -8px; pointer-events: none; font-weight: bold }
-
-/* Checkbox */
-.sidebar .fields .checkbox { width: 50px; height: 24px; position: relative; z-index: 999; opacity: 0; }
-.sidebar .fields .checkbox-skin { background-color: #CCC; width: 50px; height: 24px; border-radius: 15px; transition: all 0.3s ease-in-out; display: inline-block; margin-left: -59px; }
-.sidebar .fields .checkbox-skin:before {
-	content: ""; position: relative; width: 20px; background-color: white; height: 20px; display: block; border-radius: 100%; margin-top: 2px; margin-left: 2px;
-	transition: all 0.5s cubic-bezier(0.785, 0.135, 0.15, 0.86);
-}
-.sidebar .fields .checkbox:checked ~ .checkbox-skin:before { margin-left: 27px; }
-.sidebar .fields .checkbox:checked ~ .checkbox-skin { background-color: #2ECC71; }
-
-/* Fake input */
-.sidebar .input { font-size: 13px; width: 250px; display: inline-block; overflow: hidden; text-overflow: ellipsis; vertical-align: top }
-
-/* GRAPH */
-
-.graph { padding: 0px; list-style-type: none; width: 351px; background-color: black; height: 10px; border-radius: 8px; overflow: hidden; position: relative; font-size: 0 }
-.graph li { height: 100%; position: absolute; transition: all 0.3s; }
-.graph-stacked { white-space: nowrap; }
-.graph-stacked li { position: static; display: inline-block; height: 20px }
-
-.graph-legend { padding: 0px; list-style-type: none; margin-top: 13px; font-family: Consolas, "Andale Mono", monospace; font-size: 13px; text-transform: capitalize; }
-.sidebar .graph-legend li { margin: 0px; margin-top: 5px; margin-left: 0px; width: 160px; float: left; position: relative; }
-.sidebar .graph-legend li:nth-child(odd) { margin-right: 29px }
-.graph-legend span { position: absolute; }
-.graph-legend b { text-align: right; display: inline-block; width: 50px; float: right; font-weight: normal; }
-.graph-legend li:before { content: '\2022'; font-size: 23px; line-height: 0px; vertical-align: -3px; margin-right: 5px; }
-
-.filelist { font-size: 12px; font-family: monospace; margin: 0px; padding: 0px; list-style-type: none; line-height: 1.5em; }
-.filelist li:before { content: '\2022'; font-size: 11px; line-height: 0px; vertical-align: 0px; margin-right: 5px; color: #FFBE00; }
-.filelist li { overflow: hidden; text-overflow: ellipsis; }
-
-/* COLORS */
-
-.back-green { background-color: #2ECC71 }
-.color-green:before { color: #2ECC71 }
-.back-blue { background-color: #3BAFDA }
-.color-blue:before { color: #3BAFDA }
-.back-darkblue { background-color: #156fb7 }
-.color-darkblue:before { color: #156fb7 }
-.back-purple { background-color: #B10DC9 }
-.color-purple:before { color: #B10DC9 }
-.back-yellow { background-color: #FFDC00 }
-.color-yellow:before { color: #FFDC00 }
-.back-orange { background-color: #FF9800 }
-.color-orange:before { color: #FF9800 }
-.back-gray { background-color: #ECF0F1 }
-.color-gray:before { color: #ECF0F1 }
-.back-black { background-color: #34495E }
-.color-black:before { color: #34495E }
-.back-red { background-color: #5E4934 }
-.color-red:before { color: #5E4934 }
-.back-gray { background-color: #9e9e9e }
-.color-gray:before { color: #9e9e9e }
-.back-white { background-color: #EEE }
-.color-white:before { color: #EEE }
-.back-red { background-color: #E91E63 }
-.color-red:before { color: #E91E63 }
-
-
-/* Settings owned */
-
-.owned-title { float: left }
-#checkbox-owned { margin-bottom: 25px; margin-top: 26px; margin-left: 11px; }
-.settings-owned { clear: both }
-#checkbox-owned ~ .settings-owned { opacity: 0; max-height: 0px; transition: all 0.3s linear; overflow: hidden }
-#checkbox-owned:checked ~ .settings-owned { opacity: 1; max-height: 420px }
-
-/* Settings autodownload */
-
-.settings-autodownloadoptional { clear: both; box-sizing: border-box; padding-top: 0px; }
-#checkbox-autodownloadoptional ~ .settings-autodownloadoptional { opacity: 0; max-height: 0px; transition: all 0.3s ease-in-out; overflow: hidden; }
-#checkbox-autodownloadoptional:checked ~ .settings-autodownloadoptional { opacity: 1; max-height: 120px; padding-top: 30px; }
-
-/* Globe */
-.globe { width: 360px; height: 360px }
-.globe.loading { background: url(/uimedia/img/loading-circle.gif) center center no-repeat }
-.globe.error { text-align: center; padding-top: 156px; box-sizing: border-box; opacity: 0.2; }
-
-/* Sign publish */
-.contents { background-color: #3B3B3B; color: white; padding: 7px 10px; font-family: Consolas; font-size: 11px; display: inline-block; margin-bottom: 6px; margin-top: 10px }
-.contents a { color: white }
-.contents a:active { background-color: #6B6B6B }
-
-.contents + .flex.active {
-	padding-bottom: 100px;
-}
-#wrapper-sign-publish {
-	padding: 0;
-}
-#button-sign-publish, #menu-sign-publish {
-	display: inline-block;
-	margin: 5px 10px;
-
-	text-decoration: none;
-}
-#button-sign-publish {
-	margin-right: 5px;
-}
-#menu-sign-publish {
-	margin-left: 5px;
-	color: #AAA;
-    padding: 7px;
-    margin: 0px;
-}
-#menu-sign-publish:hover { color: white }
-
-/* Small screen */
-@media screen and (max-width: 600px) {
-	.sidebar .close { display: block }
-}
diff --git a/plugins/Sidebar/media/all.css b/plugins/Sidebar/media/all.css
deleted file mode 100644
index 9411d32a..00000000
--- a/plugins/Sidebar/media/all.css
+++ /dev/null
@@ -1,281 +0,0 @@
-
-/* ---- Console.css ---- */
-
-
-.console-container { width: 100%; z-index: 998; position: absolute; top: -100vh; padding-bottom: 100%; }
-.console-container .console { background-color: #212121; height: 100vh; -webkit-transform: translateY(0px); -moz-transform: translateY(0px); -o-transform: translateY(0px); -ms-transform: translateY(0px); transform: translateY(0px) ; padding-top: 80px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ; }
-
-.console-top { color: white; font-family: Consolas, monospace; font-size: 11px; line-height: 20px; height: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ; letter-spacing: 0.5px;}
-.console-text { overflow-y: scroll; height: calc(100% - 10px); color: #DDD; padding: 5px; margin-top: -36px; overflow-wrap: break-word; }
-.console-tabs {
-    background-color: #41193fad; position: relative; margin-right: 17px; /*backdrop-filter: blur(2px);*/
-    -webkit-box-shadow: -30px 0px 45px #7d2463; -moz-box-shadow: -30px 0px 45px #7d2463; -o-box-shadow: -30px 0px 45px #7d2463; -ms-box-shadow: -30px 0px 45px #7d2463; box-shadow: -30px 0px 45px #7d2463 ; background: -webkit-linear-gradient(-75deg, #591a48ed, #70305e66);background: -moz-linear-gradient(-75deg, #591a48ed, #70305e66);background: -o-linear-gradient(-75deg, #591a48ed, #70305e66);background: -ms-linear-gradient(-75deg, #591a48ed, #70305e66);background: linear-gradient(-75deg, #591a48ed, #70305e66); border-bottom: 1px solid #792e6473;
-}
-.console-tabs a {
-    margin-right: 5px; padding: 5px 15px; text-decoration: none; color: #AAA;
-    font-size: 11px; font-family: "Consolas"; text-transform: uppercase; border: 1px solid #666;
-    border-bottom: 0px; display: inline-block; margin: 5px; margin-bottom: 0px; background-color: rgba(0,0,0,0.5);
-}
-.console-tabs a:hover { color: #FFF }
-.console-tabs a.active { background-color: #46223c; color: #FFF }
-.console-middle {height: 0px; top: 50%; position: absolute; width: 100%; left: 50%; display: none; }
-
-.console .mynode {
-	border: 0.5px solid #aaa; width: 50px; height: 50px; -webkit-transform: rotateZ(45deg); -moz-transform: rotateZ(45deg); -o-transform: rotateZ(45deg); -ms-transform: rotateZ(45deg); transform: rotateZ(45deg) ; margin-top: -25px; margin-left: -25px;
-	opacity: 1; display: inline-block; background-color: #EEE; z-index: 9; position: absolute; outline: 5px solid #EEE;
-}
-.console .peers { width: 0px; height: 0px; position: absolute; left: -20px; top: -20px; text-align: center; }
-.console .peer { left: 0px; top: 0px; position: absolute; }
-.console .peer .icon { width: 20px; height: 20px; padding: 10px; display: inline-block; text-decoration: none; left: 200px; position: absolute; color: #666; }
-.console .peer .icon:before { content: "\25BC"; position: absolute; margin-top: 3px; margin-left: -1px; opacity: 0; -webkit-transition: all 0.3s ; -moz-transition: all 0.3s ; -o-transition: all 0.3s ; -ms-transition: all 0.3s ; transition: all 0.3s  }
-.console .peer .icon:hover:before { opacity: 1; -webkit-transition: none ; -moz-transition: none ; -o-transition: none ; -ms-transition: none ; transition: none  }
-.console .peer .line {
-	width: 187px; border-top: 1px solid #CCC; position: absolute; top: 20px; left: 20px;
-	-webkit-transform: rotateZ(334deg); -moz-transform: rotateZ(334deg); -o-transform: rotateZ(334deg); -ms-transform: rotateZ(334deg); transform: rotateZ(334deg) ; transform-origin: bottom left;
-}
-
-
-/* ---- Menu.css ---- */
-
-
-.menu {
-	background-color: white; padding: 10px 0px; position: absolute; top: 0px; left: 0px; max-height: 0px; overflow: hidden; -webkit-transform: translate(0px, -30px); -moz-transform: translate(0px, -30px); -o-transform: translate(0px, -30px); -ms-transform: translate(0px, -30px); transform: translate(0px, -30px) ; pointer-events: none;
-	-webkit-box-shadow: 0px 2px 8px rgba(0,0,0,0.3); -moz-box-shadow: 0px 2px 8px rgba(0,0,0,0.3); -o-box-shadow: 0px 2px 8px rgba(0,0,0,0.3); -ms-box-shadow: 0px 2px 8px rgba(0,0,0,0.3); box-shadow: 0px 2px 8px rgba(0,0,0,0.3) ; -webkit-border-radius: 2px; -moz-border-radius: 2px; -o-border-radius: 2px; -ms-border-radius: 2px; border-radius: 2px ; opacity: 0; -webkit-transition: opacity 0.2s ease-out, transform 1s ease-out, max-height 0.2s ease-in-out; -moz-transition: opacity 0.2s ease-out, transform 1s ease-out, max-height 0.2s ease-in-out; -o-transition: opacity 0.2s ease-out, transform 1s ease-out, max-height 0.2s ease-in-out; -ms-transition: opacity 0.2s ease-out, transform 1s ease-out, max-height 0.2s ease-in-out; transition: opacity 0.2s ease-out, transform 1s ease-out, max-height 0.2s ease-in-out ;
-}
-.menu.visible { opacity: 1; max-height: 350px; -webkit-transform: translate(0px, 0px); -moz-transform: translate(0px, 0px); -o-transform: translate(0px, 0px); -ms-transform: translate(0px, 0px); transform: translate(0px, 0px) ; -webkit-transition: opacity 0.1s ease-out, transform 0.3s ease-out, max-height 0.3s ease-in-out; -moz-transition: opacity 0.1s ease-out, transform 0.3s ease-out, max-height 0.3s ease-in-out; -o-transition: opacity 0.1s ease-out, transform 0.3s ease-out, max-height 0.3s ease-in-out; -ms-transition: opacity 0.1s ease-out, transform 0.3s ease-out, max-height 0.3s ease-in-out; transition: opacity 0.1s ease-out, transform 0.3s ease-out, max-height 0.3s ease-in-out ; pointer-events: all }
-
-.menu-item { display: block; text-decoration: none; color: black; padding: 6px 24px; -webkit-transition: all 0.2s; -moz-transition: all 0.2s; -o-transition: all 0.2s; -ms-transition: all 0.2s; transition: all 0.2s ; border-bottom: none; font-weight: normal; padding-left: 30px; }
-.menu-item-separator { margin-top: 5px; border-top: 1px solid #eee }
-
-.menu-item:hover { background-color: #F6F6F6; -webkit-transition: none; -moz-transition: none; -o-transition: none; -ms-transition: none; transition: none ; color: inherit; border: none }
-.menu-item:active, .menu-item:focus { background-color: #AF3BFF; color: white; -webkit-transition: none ; -moz-transition: none ; -o-transition: none ; -ms-transition: none ; transition: none  }
-.menu-item.selected:before {
-	content: "L"; display: inline-block; -webkit-transform: rotateZ(45deg) scaleX(-1); -moz-transform: rotateZ(45deg) scaleX(-1); -o-transform: rotateZ(45deg) scaleX(-1); -ms-transform: rotateZ(45deg) scaleX(-1); transform: rotateZ(45deg) scaleX(-1) ;
-	font-weight: bold; position: absolute; margin-left: -17px; font-size: 12px; margin-top: 2px;
-}
-
-@media only screen and (max-width: 800px) {
-.menu, .menu.visible { position: absolute; left: unset !important; right: 20px; }
-}
-
-/* ---- Scrollbable.css ---- */
-
-
-.scrollable {
-    overflow: hidden;
-}
-
-.scrollable.showScroll::after {
-    position: absolute;
-    content: '';
-    top: 5%;
-    right: 7px;
-    height: 90%;
-    width: 3px;
-    background: rgba(224, 224, 255, .3);
-}
-
-.scrollable .content-wrapper {
-    width: 100%;
-    height: 100%;
-    padding-right: 50%;
-    overflow-y: scroll;
-}
-.scroller {
-    margin-top: 5px;
-    z-index: 5;
-    cursor: pointer;
-    position: absolute;
-    width: 7px;
-    -webkit-border-radius: 5px; -moz-border-radius: 5px; -o-border-radius: 5px; -ms-border-radius: 5px; border-radius: 5px ;
-    background: #3A3A3A;
-    top: 0px;
-    left: 395px;
-    -webkit-transition: top .08s;
-    -moz-transition: top .08s;
-    -ms-transition: top .08s;
-    -o-transition: top .08s;
-    -webkit-transition: top .08s; -moz-transition: top .08s; -o-transition: top .08s; -ms-transition: top .08s; transition: top .08s ;
-}
-.scroller {
-    -webkit-touch-callout: none;
-    -webkit-user-select: none;
-    -khtml-user-select: none;
-    -moz-user-select: none;
-    -ms-user-select: none;
-    user-select: none;
-}
-
-
-/* ---- Sidebar.css ---- */
-
-
-.menu {
-	font-family: Roboto, 'Segoe UI', 'Helvetica Neue'; z-index: 999;
-}
-
-.drag-bg { width: 100%; height: 100%; position: fixed; }
-.fixbutton.dragging { cursor: -webkit-grabbing; }
-.fixbutton-bg:active { cursor: -webkit-grabbing; }
-
-
-.body-sidebar, .body-console { background-color: #666 !important;  }
-#inner-iframe { -webkit-transition: 0.3s ease-in-out; -moz-transition: 0.3s ease-in-out; -o-transition: 0.3s ease-in-out; -ms-transition: 0.3s ease-in-out; transition: 0.3s ease-in-out ; transform-origin: left bottom; }
-.body-sidebar iframe { -webkit-transform: rotateY(5deg); -moz-transform: rotateY(5deg); -o-transform: rotateY(5deg); -ms-transform: rotateY(5deg); transform: rotateY(5deg) ; opacity: 0.8; pointer-events: none; outline: 1px solid transparent }
-.body-console iframe { -webkit-transform: rotateX(5deg); -moz-transform: rotateX(5deg); -o-transform: rotateX(5deg); -ms-transform: rotateX(5deg); transform: rotateX(5deg) ; opacity: 0.8; pointer-events: none; outline: 1px solid transparent }
-
-.sidebar .label-right { float: right; margin-right: 7px; margin-top: 1px; float: right; }
-.sidebar .link-right { color: white; text-decoration: none; border-bottom: 1px solid #666; text-transform: uppercase; }
-.sidebar .link-right:hover { border-color: #CCC; }
-.sidebar .link-right:active { background-color: #444 }
-.sidebar .link-outline { outline: 1px solid #eee6; padding: 2px 13px; border-bottom: none; font-size: 80%; }
-/* SIDEBAR */
-
-.sidebar-container { width: 100%; height: 100%; overflow: hidden; position: fixed; top: 0px; z-index: 2;}
-.sidebar { background-color: #212121; position: fixed; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; -o-backface-visibility: hidden; -ms-backface-visibility: hidden; backface-visibility: hidden ; right: -1200px; height: 100%; width: 1200px; } /*box-shadow: inset 0px 0px 10px #000*/
-.sidebar .content { margin: 30px; font-family: "Segoe UI Light", "Segoe UI", "Helvetica Neue"; color: white; width: 375px; height: 300px; font-weight: 200; -webkit-transition: all 1s; -moz-transition: all 1s; -o-transition: all 1s; -ms-transition: all 1s; transition: all 1s ; opacity: 0 }
-.sidebar-container.loaded .content { opacity: 1; -webkit-transform: none ; -moz-transform: none ; -o-transform: none ; -ms-transform: none ; transform: none  }
-.sidebar h1, .sidebar h2 { font-weight: lighter; }
-.sidebar .close { color: #999; float: right; text-decoration: none; margin-top: -5px; padding: 0px 5px; font-size: 33px; margin-right: 20px; display: none }
-.sidebar .button { margin: 0px; display: inline-block; -webkit-transition: all 0.3s; -moz-transition: all 0.3s; -o-transition: all 0.3s; -ms-transition: all 0.3s; transition: all 0.3s ; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ; max-width: 260px }
-.sidebar .button.hidden { padding: 0px; max-width: 0px; opacity: 0; pointer-events: none }
-.sidebar #button-delete { background-color: transparent; border: 1px solid #333; color: #AAA; margin-left: 10px }
-.sidebar #button-delete:hover { border: 1px solid #666; color: white }
-
-.sidebar .flex { display: flex }
-.sidebar .flex .input.text, .sidebar .flex input.text { width: 100%; }
-.sidebar .flex .button { margin-left: 4px; white-space: nowrap; }
-
-/* FIELDS */
-
-.sidebar .fields { padding: 0px; list-style-type: none; width: 355px; }
-.sidebar .fields > li, .sidebar .fields .settings-owned > li { margin-bottom: 30px }
-.sidebar .fields > li:after, .sidebar .fields .settings-owned > li:after { clear: both; content: ''; display: block }
-.sidebar .fields label {
-	font-family: Consolas, monospace; text-transform: uppercase; font-size: 13px; color: #ACACAC; display: inline-block; margin-bottom: 10px;
-	vertical-align: text-bottom; margin-right: 10px; width: 100%
-}
-.sidebar .fields label small { font-weight: normal; color: white; text-transform: none; }
-.sidebar .fields .text { background-color: black; border: 0px; padding: 10px; color: white; -webkit-border-radius: 3px; -moz-border-radius: 3px; -o-border-radius: 3px; -ms-border-radius: 3px; border-radius: 3px ; width: 260px; font-family: Consolas, monospace; }
-.sidebar .fields .text.long { width: 330px; font-size: 72%; }
-.sidebar .fields .disabled { color: #AAA; background-color: #3B3B3B; }
-.sidebar .fields .text-num { width: 30px; text-align: right; padding-right: 30px; }
-.sidebar .fields .text-post { color: white; font-family: Consolas, monospace; display: inline-block; font-size: 13px; margin-left: -25px; width: 25px; }
-
-/* Select */
-.sidebar .fields select {
-	width: 225px; background-color: #3B3B3B; color: white; font-family: Consolas, monospace; -webkit-appearance: none; -moz-appearance: none; -o-appearance: none; -ms-appearance: none; appearance: none ;
-	padding: 5px; padding-right: 25px; border: 0px; -webkit-border-radius: 3px; -moz-border-radius: 3px; -o-border-radius: 3px; -ms-border-radius: 3px; border-radius: 3px ; height: 35px; vertical-align: 1px; -webkit-box-shadow: 0px 1px 2px rgba(0,0,0,0.5); -moz-box-shadow: 0px 1px 2px rgba(0,0,0,0.5); -o-box-shadow: 0px 1px 2px rgba(0,0,0,0.5); -ms-box-shadow: 0px 1px 2px rgba(0,0,0,0.5); box-shadow: 0px 1px 2px rgba(0,0,0,0.5) ;
-}
-.sidebar .fields .select-down { margin-left: -39px; width: 34px; display: inline-block; -webkit-transform: rotateZ(90deg); -moz-transform: rotateZ(90deg); -o-transform: rotateZ(90deg); -ms-transform: rotateZ(90deg); transform: rotateZ(90deg) ; height: 35px; vertical-align: -8px; pointer-events: none; font-weight: bold }
-
-/* Checkbox */
-.sidebar .fields .checkbox { width: 50px; height: 24px; position: relative; z-index: 999; opacity: 0; }
-.sidebar .fields .checkbox-skin { background-color: #CCC; width: 50px; height: 24px; -webkit-border-radius: 15px; -moz-border-radius: 15px; -o-border-radius: 15px; -ms-border-radius: 15px; border-radius: 15px ; -webkit-transition: all 0.3s ease-in-out; -moz-transition: all 0.3s ease-in-out; -o-transition: all 0.3s ease-in-out; -ms-transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out ; display: inline-block; margin-left: -59px; }
-.sidebar .fields .checkbox-skin:before {
-	content: ""; position: relative; width: 20px; background-color: white; height: 20px; display: block; -webkit-border-radius: 100%; -moz-border-radius: 100%; -o-border-radius: 100%; -ms-border-radius: 100%; border-radius: 100% ; margin-top: 2px; margin-left: 2px;
-	-webkit-transition: all 0.5s cubic-bezier(0.785, 0.135, 0.15, 0.86); -moz-transition: all 0.5s cubic-bezier(0.785, 0.135, 0.15, 0.86); -o-transition: all 0.5s cubic-bezier(0.785, 0.135, 0.15, 0.86); -ms-transition: all 0.5s cubic-bezier(0.785, 0.135, 0.15, 0.86); transition: all 0.5s cubic-bezier(0.785, 0.135, 0.15, 0.86) ;
-}
-.sidebar .fields .checkbox:checked ~ .checkbox-skin:before { margin-left: 27px; }
-.sidebar .fields .checkbox:checked ~ .checkbox-skin { background-color: #2ECC71; }
-
-/* Fake input */
-.sidebar .input { font-size: 13px; width: 250px; display: inline-block; overflow: hidden; text-overflow: ellipsis; vertical-align: top }
-
-/* GRAPH */
-
-.graph { padding: 0px; list-style-type: none; width: 351px; background-color: black; height: 10px; -webkit-border-radius: 8px; -moz-border-radius: 8px; -o-border-radius: 8px; -ms-border-radius: 8px; border-radius: 8px ; overflow: hidden; position: relative; font-size: 0 }
-.graph li { height: 100%; position: absolute; -webkit-transition: all 0.3s; -moz-transition: all 0.3s; -o-transition: all 0.3s; -ms-transition: all 0.3s; transition: all 0.3s ; }
-.graph-stacked { white-space: nowrap; }
-.graph-stacked li { position: static; display: inline-block; height: 20px }
-
-.graph-legend { padding: 0px; list-style-type: none; margin-top: 13px; font-family: Consolas, "Andale Mono", monospace; font-size: 13px; text-transform: capitalize; }
-.sidebar .graph-legend li { margin: 0px; margin-top: 5px; margin-left: 0px; width: 160px; float: left; position: relative; }
-.sidebar .graph-legend li:nth-child(odd) { margin-right: 29px }
-.graph-legend span { position: absolute; }
-.graph-legend b { text-align: right; display: inline-block; width: 50px; float: right; font-weight: normal; }
-.graph-legend li:before { content: '\2022'; font-size: 23px; line-height: 0px; vertical-align: -3px; margin-right: 5px; }
-
-.filelist { font-size: 12px; font-family: monospace; margin: 0px; padding: 0px; list-style-type: none; line-height: 1.5em; }
-.filelist li:before { content: '\2022'; font-size: 11px; line-height: 0px; vertical-align: 0px; margin-right: 5px; color: #FFBE00; }
-.filelist li { overflow: hidden; text-overflow: ellipsis; }
-
-/* COLORS */
-
-.back-green { background-color: #2ECC71 }
-.color-green:before { color: #2ECC71 }
-.back-blue { background-color: #3BAFDA }
-.color-blue:before { color: #3BAFDA }
-.back-darkblue { background-color: #156fb7 }
-.color-darkblue:before { color: #156fb7 }
-.back-purple { background-color: #B10DC9 }
-.color-purple:before { color: #B10DC9 }
-.back-yellow { background-color: #FFDC00 }
-.color-yellow:before { color: #FFDC00 }
-.back-orange { background-color: #FF9800 }
-.color-orange:before { color: #FF9800 }
-.back-gray { background-color: #ECF0F1 }
-.color-gray:before { color: #ECF0F1 }
-.back-black { background-color: #34495E }
-.color-black:before { color: #34495E }
-.back-red { background-color: #5E4934 }
-.color-red:before { color: #5E4934 }
-.back-gray { background-color: #9e9e9e }
-.color-gray:before { color: #9e9e9e }
-.back-white { background-color: #EEE }
-.color-white:before { color: #EEE }
-.back-red { background-color: #E91E63 }
-.color-red:before { color: #E91E63 }
-
-
-/* Settings owned */
-
-.owned-title { float: left }
-#checkbox-owned { margin-bottom: 25px; margin-top: 26px; margin-left: 11px; }
-.settings-owned { clear: both }
-#checkbox-owned ~ .settings-owned { opacity: 0; max-height: 0px; -webkit-transition: all 0.3s linear; -moz-transition: all 0.3s linear; -o-transition: all 0.3s linear; -ms-transition: all 0.3s linear; transition: all 0.3s linear ; overflow: hidden }
-#checkbox-owned:checked ~ .settings-owned { opacity: 1; max-height: 420px }
-
-/* Settings autodownload */
-
-.settings-autodownloadoptional { clear: both; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ; padding-top: 0px; }
-#checkbox-autodownloadoptional ~ .settings-autodownloadoptional { opacity: 0; max-height: 0px; -webkit-transition: all 0.3s ease-in-out; -moz-transition: all 0.3s ease-in-out; -o-transition: all 0.3s ease-in-out; -ms-transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out ; overflow: hidden; }
-#checkbox-autodownloadoptional:checked ~ .settings-autodownloadoptional { opacity: 1; max-height: 120px; padding-top: 30px; }
-
-/* Globe */
-.globe { width: 360px; height: 360px }
-.globe.loading { background: url(/uimedia/img/loading-circle.gif) center center no-repeat }
-.globe.error { text-align: center; padding-top: 156px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ; opacity: 0.2; }
-
-/* Sign publish */
-.contents { background-color: #3B3B3B; color: white; padding: 7px 10px; font-family: Consolas; font-size: 11px; display: inline-block; margin-bottom: 6px; margin-top: 10px }
-.contents a { color: white }
-.contents a:active { background-color: #6B6B6B }
-
-.contents + .flex.active {
-	padding-bottom: 100px;
-}
-#wrapper-sign-publish {
-	padding: 0;
-}
-#button-sign-publish, #menu-sign-publish {
-	display: inline-block;
-	margin: 5px 10px;
-
-	text-decoration: none;
-}
-#button-sign-publish {
-	margin-right: 5px;
-}
-#menu-sign-publish {
-	margin-left: 5px;
-	color: #AAA;
-    padding: 7px;
-    margin: 0px;
-}
-#menu-sign-publish:hover { color: white }
-
-/* Small screen */
-@media screen and (max-width: 600px) {
-	.sidebar .close { display: block }
-}
diff --git a/plugins/Sidebar/media/all.js b/plugins/Sidebar/media/all.js
deleted file mode 100644
index 5a03a37c..00000000
--- a/plugins/Sidebar/media/all.js
+++ /dev/null
@@ -1,1770 +0,0 @@
-
-/* ---- Class.coffee ---- */
-
-
-(function() {
-  var Class,
-    slice = [].slice;
-
-  Class = (function() {
-    function Class() {}
-
-    Class.prototype.trace = true;
-
-    Class.prototype.log = function() {
-      var args;
-      args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
-      if (!this.trace) {
-        return;
-      }
-      if (typeof console === 'undefined') {
-        return;
-      }
-      args.unshift("[" + this.constructor.name + "]");
-      console.log.apply(console, args);
-      return this;
-    };
-
-    Class.prototype.logStart = function() {
-      var args, name;
-      name = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
-      if (!this.trace) {
-        return;
-      }
-      this.logtimers || (this.logtimers = {});
-      this.logtimers[name] = +(new Date);
-      if (args.length > 0) {
-        this.log.apply(this, ["" + name].concat(slice.call(args), ["(started)"]));
-      }
-      return this;
-    };
-
-    Class.prototype.logEnd = function() {
-      var args, ms, name;
-      name = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
-      ms = +(new Date) - this.logtimers[name];
-      this.log.apply(this, ["" + name].concat(slice.call(args), ["(Done in " + ms + "ms)"]));
-      return this;
-    };
-
-    return Class;
-
-  })();
-
-  window.Class = Class;
-
-}).call(this);
-
-/* ---- Console.coffee ---- */
-
-
-(function() {
-  var Console,
-    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
-    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
-    hasProp = {}.hasOwnProperty;
-
-  Console = (function(superClass) {
-    extend(Console, superClass);
-
-    function Console(sidebar) {
-      var handleMessageWebsocket_original;
-      this.sidebar = sidebar;
-      this.handleTabClick = bind(this.handleTabClick, this);
-      this.changeFilter = bind(this.changeFilter, this);
-      this.stopDragY = bind(this.stopDragY, this);
-      this.cleanup = bind(this.cleanup, this);
-      this.onClosed = bind(this.onClosed, this);
-      this.onOpened = bind(this.onOpened, this);
-      this.open = bind(this.open, this);
-      this.close = bind(this.close, this);
-      this.loadConsoleText = bind(this.loadConsoleText, this);
-      this.addLines = bind(this.addLines, this);
-      this.formatLine = bind(this.formatLine, this);
-      this.checkTextIsBottom = bind(this.checkTextIsBottom, this);
-      this.tag = null;
-      this.opened = false;
-      this.filter = null;
-      this.tab_types = [
-        {
-          title: "All",
-          filter: ""
-        }, {
-          title: "Info",
-          filter: "INFO"
-        }, {
-          title: "Warning",
-          filter: "WARNING"
-        }, {
-          title: "Error",
-          filter: "ERROR"
-        }
-      ];
-      this.read_size = 32 * 1024;
-      this.tab_active = "";
-      handleMessageWebsocket_original = this.sidebar.wrapper.handleMessageWebsocket;
-      this.sidebar.wrapper.handleMessageWebsocket = (function(_this) {
-        return function(message) {
-          if (message.cmd === "logLineAdd" && message.params.stream_id === _this.stream_id) {
-            return _this.addLines(message.params.lines);
-          } else {
-            return handleMessageWebsocket_original(message);
-          }
-        };
-      })(this);
-      $(window).on("hashchange", (function(_this) {
-        return function() {
-          if (window.top.location.hash.startsWith("#ZeroNet:Console")) {
-            return _this.open();
-          }
-        };
-      })(this));
-      if (window.top.location.hash.startsWith("#ZeroNet:Console")) {
-        setTimeout(((function(_this) {
-          return function() {
-            return _this.open();
-          };
-        })(this)), 10);
-      }
-    }
-
-    Console.prototype.createHtmltag = function() {
-      var j, len, ref, tab, tab_type;
-      if (!this.container) {
-        this.container = $("<div class=\"console-container\">\n	<div class=\"console\">\n		<div class=\"console-top\">\n			<div class=\"console-tabs\"></div>\n			<div class=\"console-text\">Loading...</div>\n		</div>\n		<div class=\"console-middle\">\n			<div class=\"mynode\"></div>\n			<div class=\"peers\">\n				<div class=\"peer\"><div class=\"line\"></div><a href=\"#\" class=\"icon\">\u25BD</div></div>\n			</div>\n		</div>\n	</div>\n</div>");
-        this.text = this.container.find(".console-text");
-        this.text_elem = this.text[0];
-        this.tabs = this.container.find(".console-tabs");
-        this.text.on("mousewheel", (function(_this) {
-          return function(e) {
-            if (e.originalEvent.deltaY < 0) {
-              _this.text.stop();
-            }
-            return RateLimit(300, _this.checkTextIsBottom);
-          };
-        })(this));
-        this.text.is_bottom = true;
-        this.container.appendTo(document.body);
-        this.tag = this.container.find(".console");
-        ref = this.tab_types;
-        for (j = 0, len = ref.length; j < len; j++) {
-          tab_type = ref[j];
-          tab = $("<a></a>", {
-            href: "#",
-            "data-filter": tab_type.filter,
-            "data-title": tab_type.title
-          }).text(tab_type.title);
-          if (tab_type.filter === this.tab_active) {
-            tab.addClass("active");
-          }
-          tab.on("click", this.handleTabClick);
-          if (window.top.location.hash.endsWith(tab_type.title)) {
-            this.log("Triggering click on", tab);
-            tab.trigger("click");
-          }
-          this.tabs.append(tab);
-        }
-        this.container.on("mousedown touchend touchcancel", (function(_this) {
-          return function(e) {
-            if (e.target !== e.currentTarget) {
-              return true;
-            }
-            _this.log("closing");
-            if ($(document.body).hasClass("body-console")) {
-              _this.close();
-              return true;
-            }
-          };
-        })(this));
-        return this.loadConsoleText();
-      }
-    };
-
-    Console.prototype.checkTextIsBottom = function() {
-      return this.text.is_bottom = Math.round(this.text_elem.scrollTop + this.text_elem.clientHeight) >= this.text_elem.scrollHeight - 15;
-    };
-
-    Console.prototype.toColor = function(text, saturation, lightness) {
-      var hash, i, j, ref;
-      if (saturation == null) {
-        saturation = 60;
-      }
-      if (lightness == null) {
-        lightness = 70;
-      }
-      hash = 0;
-      for (i = j = 0, ref = text.length - 1; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) {
-        hash += text.charCodeAt(i) * i;
-        hash = hash % 1777;
-      }
-      return "hsl(" + (hash % 360) + ("," + saturation + "%," + lightness + "%)");
-    };
-
-    Console.prototype.formatLine = function(line) {
-      var added, level, match, module, ref, text;
-      match = line.match(/(\[.*?\])[ ]+(.*?)[ ]+(.*?)[ ]+(.*)/);
-      if (!match) {
-        return line.replace(/\</g, "&lt;").replace(/\>/g, "&gt;");
-      }
-      ref = line.match(/(\[.*?\])[ ]+(.*?)[ ]+(.*?)[ ]+(.*)/), line = ref[0], added = ref[1], level = ref[2], module = ref[3], text = ref[4];
-      added = "<span style='color: #dfd0fa'>" + added + "</span>";
-      level = "<span style='color: " + (this.toColor(level, 100)) + ";'>" + level + "</span>";
-      module = "<span style='color: " + (this.toColor(module, 60)) + "; font-weight: bold;'>" + module + "</span>";
-      text = text.replace(/(Site:[A-Za-z0-9\.]+)/g, "<span style='color: #AAAAFF'>$1</span>");
-      text = text.replace(/\</g, "&lt;").replace(/\>/g, "&gt;");
-      return added + " " + level + " " + module + " " + text;
-    };
-
-    Console.prototype.addLines = function(lines, animate) {
-      var html_lines, j, len, line;
-      if (animate == null) {
-        animate = true;
-      }
-      html_lines = [];
-      this.logStart("formatting");
-      for (j = 0, len = lines.length; j < len; j++) {
-        line = lines[j];
-        html_lines.push(this.formatLine(line));
-      }
-      this.logEnd("formatting");
-      this.logStart("adding");
-      this.text.append(html_lines.join("<br>") + "<br>");
-      this.logEnd("adding");
-      if (this.text.is_bottom && animate) {
-        return this.text.stop().animate({
-          scrollTop: this.text_elem.scrollHeight - this.text_elem.clientHeight + 1
-        }, 600, 'easeInOutCubic');
-      }
-    };
-
-    Console.prototype.loadConsoleText = function() {
-      this.sidebar.wrapper.ws.cmd("consoleLogRead", {
-        filter: this.filter,
-        read_size: this.read_size
-      }, (function(_this) {
-        return function(res) {
-          var pos_diff, size_read, size_total;
-          _this.text.html("");
-          pos_diff = res["pos_end"] - res["pos_start"];
-          size_read = Math.round(pos_diff / 1024);
-          size_total = Math.round(res['pos_end'] / 1024);
-          _this.text.append("<br><br>");
-          _this.text.append("Displaying " + res.lines.length + " of " + res.num_found + " lines found in the last " + size_read + "kB of the log file. (" + size_total + "kB total)<br>");
-          _this.addLines(res.lines, false);
-          return _this.text_elem.scrollTop = _this.text_elem.scrollHeight;
-        };
-      })(this));
-      if (this.stream_id) {
-        this.sidebar.wrapper.ws.cmd("consoleLogStreamRemove", {
-          stream_id: this.stream_id
-        });
-      }
-      return this.sidebar.wrapper.ws.cmd("consoleLogStream", {
-        filter: this.filter
-      }, (function(_this) {
-        return function(res) {
-          return _this.stream_id = res.stream_id;
-        };
-      })(this));
-    };
-
-    Console.prototype.close = function() {
-      window.top.location.hash = "";
-      this.sidebar.move_lock = "y";
-      this.sidebar.startDrag();
-      return this.sidebar.stopDrag();
-    };
-
-    Console.prototype.open = function() {
-      this.sidebar.startDrag();
-      this.sidebar.moved("y");
-      this.sidebar.fixbutton_targety = this.sidebar.page_height - this.sidebar.fixbutton_inity - 50;
-      return this.sidebar.stopDrag();
-    };
-
-    Console.prototype.onOpened = function() {
-      this.sidebar.onClosed();
-      return this.log("onOpened");
-    };
-
-    Console.prototype.onClosed = function() {
-      $(document.body).removeClass("body-console");
-      if (this.stream_id) {
-        return this.sidebar.wrapper.ws.cmd("consoleLogStreamRemove", {
-          stream_id: this.stream_id
-        });
-      }
-    };
-
-    Console.prototype.cleanup = function() {
-      if (this.container) {
-        this.container.remove();
-        return this.container = null;
-      }
-    };
-
-    Console.prototype.stopDragY = function() {
-      var targety;
-      if (this.sidebar.fixbutton_targety === this.sidebar.fixbutton_inity) {
-        targety = 0;
-        this.opened = false;
-      } else {
-        targety = this.sidebar.fixbutton_targety - this.sidebar.fixbutton_inity;
-        this.onOpened();
-        this.opened = true;
-      }
-      if (this.tag) {
-        this.tag.css("transition", "0.5s ease-out");
-        this.tag.css("transform", "translateY(" + targety + "px)").one(transitionEnd, (function(_this) {
-          return function() {
-            _this.tag.css("transition", "");
-            if (!_this.opened) {
-              return _this.cleanup();
-            }
-          };
-        })(this));
-      }
-      this.log("stopDragY", "opened:", this.opened, targety);
-      if (!this.opened) {
-        return this.onClosed();
-      }
-    };
-
-    Console.prototype.changeFilter = function(filter) {
-      this.filter = filter;
-      if (this.filter === "") {
-        this.read_size = 32 * 1024;
-      } else {
-        this.read_size = 5 * 1024 * 1024;
-      }
-      return this.loadConsoleText();
-    };
-
-    Console.prototype.handleTabClick = function(e) {
-      var elem;
-      elem = $(e.currentTarget);
-      this.tab_active = elem.data("filter");
-      $("a", this.tabs).removeClass("active");
-      elem.addClass("active");
-      this.changeFilter(this.tab_active);
-      window.top.location.hash = "#ZeroNet:Console:" + elem.data("title");
-      return false;
-    };
-
-    return Console;
-
-  })(Class);
-
-  window.Console = Console;
-
-}).call(this);
-
-/* ---- Menu.coffee ---- */
-
-
-(function() {
-  var Menu,
-    slice = [].slice;
-
-  Menu = (function() {
-    function Menu(button) {
-      this.button = button;
-      this.elem = $(".menu.template").clone().removeClass("template");
-      this.elem.appendTo("body");
-      this.items = [];
-    }
-
-    Menu.prototype.show = function() {
-      var button_pos, left;
-      if (window.visible_menu && window.visible_menu.button[0] === this.button[0]) {
-        window.visible_menu.hide();
-        return this.hide();
-      } else {
-        button_pos = this.button.offset();
-        left = button_pos.left;
-        this.elem.css({
-          "top": button_pos.top + this.button.outerHeight(),
-          "left": left
-        });
-        this.button.addClass("menu-active");
-        this.elem.addClass("visible");
-        if (this.elem.position().left + this.elem.width() + 20 > window.innerWidth) {
-          this.elem.css("left", window.innerWidth - this.elem.width() - 20);
-        }
-        if (window.visible_menu) {
-          window.visible_menu.hide();
-        }
-        return window.visible_menu = this;
-      }
-    };
-
-    Menu.prototype.hide = function() {
-      this.elem.removeClass("visible");
-      this.button.removeClass("menu-active");
-      return window.visible_menu = null;
-    };
-
-    Menu.prototype.addItem = function(title, cb) {
-      var item;
-      item = $(".menu-item.template", this.elem).clone().removeClass("template");
-      item.html(title);
-      item.on("click", (function(_this) {
-        return function() {
-          if (!cb(item)) {
-            _this.hide();
-          }
-          return false;
-        };
-      })(this));
-      item.appendTo(this.elem);
-      this.items.push(item);
-      return item;
-    };
-
-    Menu.prototype.log = function() {
-      var args;
-      args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
-      return console.log.apply(console, ["[Menu]"].concat(slice.call(args)));
-    };
-
-    return Menu;
-
-  })();
-
-  window.Menu = Menu;
-
-  $("body").on("click", function(e) {
-    if (window.visible_menu && e.target !== window.visible_menu.button[0] && $(e.target).parent()[0] !== window.visible_menu.elem[0]) {
-      return window.visible_menu.hide();
-    }
-  });
-
-}).call(this);
-
-/* ---- Prototypes.coffee ---- */
-
-
-(function() {
-  String.prototype.startsWith = function(s) {
-    return this.slice(0, s.length) === s;
-  };
-
-  String.prototype.endsWith = function(s) {
-    return s === '' || this.slice(-s.length) === s;
-  };
-
-  String.prototype.capitalize = function() {
-    if (this.length) {
-      return this[0].toUpperCase() + this.slice(1);
-    } else {
-      return "";
-    }
-  };
-
-  String.prototype.repeat = function(count) {
-    return new Array(count + 1).join(this);
-  };
-
-  window.isEmpty = function(obj) {
-    var key;
-    for (key in obj) {
-      return false;
-    }
-    return true;
-  };
-
-}).call(this);
-
-/* ---- RateLimit.coffee ---- */
-
-
-(function() {
-  var call_after_interval, limits;
-
-  limits = {};
-
-  call_after_interval = {};
-
-  window.RateLimit = function(interval, fn) {
-    if (!limits[fn]) {
-      call_after_interval[fn] = false;
-      fn();
-      return limits[fn] = setTimeout((function() {
-        if (call_after_interval[fn]) {
-          fn();
-        }
-        delete limits[fn];
-        return delete call_after_interval[fn];
-      }), interval);
-    } else {
-      return call_after_interval[fn] = true;
-    }
-  };
-
-}).call(this);
-
-/* ---- Scrollable.js ---- */
-
-
-/* via http://jsfiddle.net/elGrecode/00dgurnn/ */
-
-window.initScrollable = function () {
-
-    var scrollContainer = document.querySelector('.scrollable'),
-        scrollContentWrapper = document.querySelector('.scrollable .content-wrapper'),
-        scrollContent = document.querySelector('.scrollable .content'),
-        contentPosition = 0,
-        scrollerBeingDragged = false,
-        scroller,
-        topPosition,
-        scrollerHeight;
-
-    function calculateScrollerHeight() {
-        // *Calculation of how tall scroller should be
-        var visibleRatio = scrollContainer.offsetHeight / scrollContentWrapper.scrollHeight;
-        if (visibleRatio == 1)
-            scroller.style.display = "none";
-        else
-            scroller.style.display = "block";
-        return visibleRatio * scrollContainer.offsetHeight;
-    }
-
-    function moveScroller(evt) {
-        // Move Scroll bar to top offset
-        var scrollPercentage = evt.target.scrollTop / scrollContentWrapper.scrollHeight;
-        topPosition = scrollPercentage * (scrollContainer.offsetHeight - 5); // 5px arbitrary offset so scroll bar doesn't move too far beyond content wrapper bounding box
-        scroller.style.top = topPosition + 'px';
-    }
-
-    function startDrag(evt) {
-        normalizedPosition = evt.pageY;
-        contentPosition = scrollContentWrapper.scrollTop;
-        scrollerBeingDragged = true;
-        window.addEventListener('mousemove', scrollBarScroll);
-        return false;
-    }
-
-    function stopDrag(evt) {
-        scrollerBeingDragged = false;
-        window.removeEventListener('mousemove', scrollBarScroll);
-    }
-
-    function scrollBarScroll(evt) {
-        if (scrollerBeingDragged === true) {
-            evt.preventDefault();
-            var mouseDifferential = evt.pageY - normalizedPosition;
-            var scrollEquivalent = mouseDifferential * (scrollContentWrapper.scrollHeight / scrollContainer.offsetHeight);
-            scrollContentWrapper.scrollTop = contentPosition + scrollEquivalent;
-        }
-    }
-
-    function updateHeight() {
-        scrollerHeight = calculateScrollerHeight() - 10;
-        scroller.style.height = scrollerHeight + 'px';
-    }
-
-    function createScroller() {
-        // *Creates scroller element and appends to '.scrollable' div
-        // create scroller element
-        scroller = document.createElement("div");
-        scroller.className = 'scroller';
-
-        // determine how big scroller should be based on content
-        scrollerHeight = calculateScrollerHeight() - 10;
-
-        if (scrollerHeight / scrollContainer.offsetHeight < 1) {
-            // *If there is a need to have scroll bar based on content size
-            scroller.style.height = scrollerHeight + 'px';
-
-            // append scroller to scrollContainer div
-            scrollContainer.appendChild(scroller);
-
-            // show scroll path divot
-            scrollContainer.className += ' showScroll';
-
-            // attach related draggable listeners
-            scroller.addEventListener('mousedown', startDrag);
-            window.addEventListener('mouseup', stopDrag);
-        }
-
-    }
-
-    createScroller();
-
-
-    // *** Listeners ***
-    scrollContentWrapper.addEventListener('scroll', moveScroller);
-
-    return updateHeight;
-};
-
-/* ---- Sidebar.coffee ---- */
-
-
-(function() {
-  var Sidebar, wrapper,
-    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
-    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
-    hasProp = {}.hasOwnProperty,
-    indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
-
-  Sidebar = (function(superClass) {
-    extend(Sidebar, superClass);
-
-    function Sidebar(wrapper1) {
-      this.wrapper = wrapper1;
-      this.unloadGlobe = bind(this.unloadGlobe, this);
-      this.displayGlobe = bind(this.displayGlobe, this);
-      this.loadGlobe = bind(this.loadGlobe, this);
-      this.animDrag = bind(this.animDrag, this);
-      this.setHtmlTag = bind(this.setHtmlTag, this);
-      this.waitMove = bind(this.waitMove, this);
-      this.resized = bind(this.resized, this);
-      this.tag = null;
-      this.container = null;
-      this.opened = false;
-      this.width = 410;
-      this.console = new Console(this);
-      this.fixbutton = $(".fixbutton");
-      this.fixbutton_addx = 0;
-      this.fixbutton_addy = 0;
-      this.fixbutton_initx = 0;
-      this.fixbutton_inity = 15;
-      this.fixbutton_targetx = 0;
-      this.move_lock = null;
-      this.page_width = $(window).width();
-      this.page_height = $(window).height();
-      this.frame = $("#inner-iframe");
-      this.initFixbutton();
-      this.dragStarted = 0;
-      this.globe = null;
-      this.preload_html = null;
-      this.original_set_site_info = this.wrapper.setSiteInfo;
-      if (window.top.location.hash === "#ZeroNet:OpenSidebar") {
-        this.startDrag();
-        this.moved("x");
-        this.fixbutton_targetx = this.fixbutton_initx - this.width;
-        this.stopDrag();
-      }
-    }
-
-    Sidebar.prototype.initFixbutton = function() {
-      this.fixbutton.on("mousedown touchstart", (function(_this) {
-        return function(e) {
-          if (e.button > 0) {
-            return;
-          }
-          e.preventDefault();
-          _this.fixbutton.off("click touchend touchcancel");
-          _this.dragStarted = +(new Date);
-          $(".drag-bg").remove();
-          $("<div class='drag-bg'></div>").appendTo(document.body);
-          return $("body").one("mousemove touchmove", function(e) {
-            var mousex, mousey;
-            mousex = e.pageX;
-            mousey = e.pageY;
-            if (!mousex) {
-              mousex = e.originalEvent.touches[0].pageX;
-              mousey = e.originalEvent.touches[0].pageY;
-            }
-            _this.fixbutton_addx = _this.fixbutton.offset().left - mousex;
-            _this.fixbutton_addy = _this.fixbutton.offset().top - mousey;
-            return _this.startDrag();
-          });
-        };
-      })(this));
-      this.fixbutton.parent().on("click touchend touchcancel", (function(_this) {
-        return function(e) {
-          if ((+(new Date)) - _this.dragStarted < 100) {
-            window.top.location = _this.fixbutton.find(".fixbutton-bg").attr("href");
-          }
-          return _this.stopDrag();
-        };
-      })(this));
-      this.resized();
-      return $(window).on("resize", this.resized);
-    };
-
-    Sidebar.prototype.resized = function() {
-      this.page_width = $(window).width();
-      this.page_height = $(window).height();
-      this.fixbutton_initx = this.page_width - 75;
-      if (this.opened) {
-        return this.fixbutton.css({
-          left: this.fixbutton_initx - this.width
-        });
-      } else {
-        return this.fixbutton.css({
-          left: this.fixbutton_initx
-        });
-      }
-    };
-
-    Sidebar.prototype.startDrag = function() {
-      this.log("startDrag", this.fixbutton_initx, this.fixbutton_inity);
-      this.fixbutton_targetx = this.fixbutton_initx;
-      this.fixbutton_targety = this.fixbutton_inity;
-      this.fixbutton.addClass("dragging");
-      if (navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') > 0) {
-        this.fixbutton.css("pointer-events", "none");
-      }
-      this.fixbutton.one("click", (function(_this) {
-        return function(e) {
-          var moved_x, moved_y;
-          _this.stopDrag();
-          _this.fixbutton.removeClass("dragging");
-          moved_x = Math.abs(_this.fixbutton.offset().left - _this.fixbutton_initx);
-          moved_y = Math.abs(_this.fixbutton.offset().top - _this.fixbutton_inity);
-          if (moved_x > 5 || moved_y > 10) {
-            return e.preventDefault();
-          }
-        };
-      })(this));
-      this.fixbutton.parents().on("mousemove touchmove", this.animDrag);
-      this.fixbutton.parents().on("mousemove touchmove", this.waitMove);
-      return this.fixbutton.parents().one("mouseup touchend touchcancel", (function(_this) {
-        return function(e) {
-          e.preventDefault();
-          return _this.stopDrag();
-        };
-      })(this));
-    };
-
-    Sidebar.prototype.waitMove = function(e) {
-      var moved_x, moved_y;
-      document.body.style.perspective = "1000px";
-      document.body.style.height = "100%";
-      document.body.style.willChange = "perspective";
-      document.documentElement.style.height = "100%";
-      moved_x = Math.abs(parseInt(this.fixbutton[0].style.left) - this.fixbutton_targetx);
-      moved_y = Math.abs(parseInt(this.fixbutton[0].style.top) - this.fixbutton_targety);
-      if (moved_x > 5 && (+(new Date)) - this.dragStarted + moved_x > 50) {
-        this.moved("x");
-        this.fixbutton.stop().animate({
-          "top": this.fixbutton_inity
-        }, 1000);
-        return this.fixbutton.parents().off("mousemove touchmove", this.waitMove);
-      } else if (moved_y > 5 && (+(new Date)) - this.dragStarted + moved_y > 50) {
-        this.moved("y");
-        return this.fixbutton.parents().off("mousemove touchmove", this.waitMove);
-      }
-    };
-
-    Sidebar.prototype.moved = function(direction) {
-      var img;
-      this.log("Moved", direction);
-      this.move_lock = direction;
-      if (direction === "y") {
-        $(document.body).addClass("body-console");
-        return this.console.createHtmltag();
-      }
-      this.createHtmltag();
-      $(document.body).addClass("body-sidebar");
-      this.container.on("mousedown touchend touchcancel", (function(_this) {
-        return function(e) {
-          if (e.target !== e.currentTarget) {
-            return true;
-          }
-          _this.log("closing");
-          if ($(document.body).hasClass("body-sidebar")) {
-            _this.close();
-            return true;
-          }
-        };
-      })(this));
-      $(window).off("resize");
-      $(window).on("resize", (function(_this) {
-        return function() {
-          $(document.body).css("height", $(window).height());
-          _this.scrollable();
-          return _this.resized();
-        };
-      })(this));
-      this.wrapper.setSiteInfo = (function(_this) {
-        return function(site_info) {
-          _this.setSiteInfo(site_info);
-          return _this.original_set_site_info.apply(_this.wrapper, arguments);
-        };
-      })(this);
-      img = new Image();
-      return img.src = "/uimedia/globe/world.jpg";
-    };
-
-    Sidebar.prototype.setSiteInfo = function(site_info) {
-      RateLimit(1500, (function(_this) {
-        return function() {
-          return _this.updateHtmlTag();
-        };
-      })(this));
-      return RateLimit(30000, (function(_this) {
-        return function() {
-          return _this.displayGlobe();
-        };
-      })(this));
-    };
-
-    Sidebar.prototype.createHtmltag = function() {
-      this.when_loaded = $.Deferred();
-      if (!this.container) {
-        this.container = $("<div class=\"sidebar-container\"><div class=\"sidebar scrollable\"><div class=\"content-wrapper\"><div class=\"content\">\n</div></div></div></div>");
-        this.container.appendTo(document.body);
-        this.tag = this.container.find(".sidebar");
-        this.updateHtmlTag();
-        return this.scrollable = window.initScrollable();
-      }
-    };
-
-    Sidebar.prototype.updateHtmlTag = function() {
-      if (this.preload_html) {
-        this.setHtmlTag(this.preload_html);
-        return this.preload_html = null;
-      } else {
-        return this.wrapper.ws.cmd("sidebarGetHtmlTag", {}, this.setHtmlTag);
-      }
-    };
-
-    Sidebar.prototype.setHtmlTag = function(res) {
-      if (this.tag.find(".content").children().length === 0) {
-        this.log("Creating content");
-        this.container.addClass("loaded");
-        morphdom(this.tag.find(".content")[0], '<div class="content">' + res + '</div>');
-        this.when_loaded.resolve();
-      } else {
-        morphdom(this.tag.find(".content")[0], '<div class="content">' + res + '</div>', {
-          onBeforeMorphEl: function(from_el, to_el) {
-            if (from_el.className === "globe" || from_el.className.indexOf("noupdate") >= 0) {
-              return false;
-            } else {
-              return true;
-            }
-          }
-        });
-      }
-      this.tag.find("#privatekey-add").off("click, touchend").on("click touchend", (function(_this) {
-        return function(e) {
-          _this.wrapper.displayPrompt("Enter your private key:", "password", "Save", "", function(privatekey) {
-            return _this.wrapper.ws.cmd("userSetSitePrivatekey", [privatekey], function(res) {
-              return _this.wrapper.notifications.add("privatekey", "done", "Private key saved for site signing", 5000);
-            });
-          });
-          return false;
-        };
-      })(this));
-      this.tag.find("#privatekey-forget").off("click, touchend").on("click touchend", (function(_this) {
-        return function(e) {
-          _this.wrapper.displayConfirm("Remove saved private key for this site?", "Forget", function(res) {
-            if (!res) {
-              return false;
-            }
-            return _this.wrapper.ws.cmd("userSetSitePrivatekey", [""], function(res) {
-              return _this.wrapper.notifications.add("privatekey", "done", "Saved private key removed", 5000);
-            });
-          });
-          return false;
-        };
-      })(this));
-      return this.tag.find("#browse-files").attr("href", document.location.pathname.replace(/(\/.*?(\/|$)).*$/, "/list$1"));
-    };
-
-    Sidebar.prototype.animDrag = function(e) {
-      var mousex, mousey, overdrag, overdrag_percent, targetx, targety;
-      mousex = e.pageX;
-      mousey = e.pageY;
-      if (!mousex && e.originalEvent.touches) {
-        mousex = e.originalEvent.touches[0].pageX;
-        mousey = e.originalEvent.touches[0].pageY;
-      }
-      overdrag = this.fixbutton_initx - this.width - mousex;
-      if (overdrag > 0) {
-        overdrag_percent = 1 + overdrag / 300;
-        mousex = (mousex + (this.fixbutton_initx - this.width) * overdrag_percent) / (1 + overdrag_percent);
-      }
-      targetx = this.fixbutton_initx - mousex - this.fixbutton_addx;
-      targety = this.fixbutton_inity - mousey - this.fixbutton_addy;
-      if (this.move_lock === "x") {
-        targety = this.fixbutton_inity;
-      } else if (this.move_lock === "y") {
-        targetx = this.fixbutton_initx;
-      }
-      if (!this.move_lock || this.move_lock === "x") {
-        this.fixbutton[0].style.left = (mousex + this.fixbutton_addx) + "px";
-        if (this.tag) {
-          this.tag[0].style.transform = "translateX(" + (0 - targetx) + "px)";
-        }
-      }
-      if (!this.move_lock || this.move_lock === "y") {
-        this.fixbutton[0].style.top = (mousey + this.fixbutton_addy) + "px";
-        if (this.console.tag) {
-          this.console.tag[0].style.transform = "translateY(" + (0 - targety) + "px)";
-        }
-      }
-      if ((!this.opened && targetx > this.width / 3) || (this.opened && targetx > this.width * 0.9)) {
-        this.fixbutton_targetx = this.fixbutton_initx - this.width;
-      } else {
-        this.fixbutton_targetx = this.fixbutton_initx;
-      }
-      if ((!this.console.opened && 0 - targety > this.page_height / 10) || (this.console.opened && 0 - targety > this.page_height * 0.8)) {
-        return this.fixbutton_targety = this.page_height - this.fixbutton_inity - 50;
-      } else {
-        return this.fixbutton_targety = this.fixbutton_inity;
-      }
-    };
-
-    Sidebar.prototype.stopDrag = function() {
-      var left, top;
-      this.fixbutton.parents().off("mousemove touchmove");
-      this.fixbutton.off("mousemove touchmove");
-      this.fixbutton.css("pointer-events", "");
-      $(".drag-bg").remove();
-      if (!this.fixbutton.hasClass("dragging")) {
-        return;
-      }
-      this.fixbutton.removeClass("dragging");
-      if (this.fixbutton_targetx !== this.fixbutton.offset().left || this.fixbutton_targety !== this.fixbutton.offset().top) {
-        if (this.move_lock === "y") {
-          top = this.fixbutton_targety;
-          left = this.fixbutton_initx;
-        }
-        if (this.move_lock === "x") {
-          top = this.fixbutton_inity;
-          left = this.fixbutton_targetx;
-        }
-        this.fixbutton.stop().animate({
-          "left": left,
-          "top": top
-        }, 500, "easeOutBack", (function(_this) {
-          return function() {
-            if (_this.fixbutton_targetx === _this.fixbutton_initx) {
-              _this.fixbutton.css("left", "auto");
-            } else {
-              _this.fixbutton.css("left", left);
-            }
-            return $(".fixbutton-bg").trigger("mouseout");
-          };
-        })(this));
-        this.stopDragX();
-        this.console.stopDragY();
-      }
-      return this.move_lock = null;
-    };
-
-    Sidebar.prototype.stopDragX = function() {
-      var targetx;
-      if (this.fixbutton_targetx === this.fixbutton_initx || this.move_lock === "y") {
-        targetx = 0;
-        this.opened = false;
-      } else {
-        targetx = this.width;
-        if (this.opened) {
-          this.onOpened();
-        } else {
-          this.when_loaded.done((function(_this) {
-            return function() {
-              return _this.onOpened();
-            };
-          })(this));
-        }
-        this.opened = true;
-      }
-      if (this.tag) {
-        this.tag.css("transition", "0.4s ease-out");
-        this.tag.css("transform", "translateX(-" + targetx + "px)").one(transitionEnd, (function(_this) {
-          return function() {
-            _this.tag.css("transition", "");
-            if (!_this.opened) {
-              _this.container.remove();
-              _this.container = null;
-              if (_this.tag) {
-                _this.tag.remove();
-                return _this.tag = null;
-              }
-            }
-          };
-        })(this));
-      }
-      this.log("stopdrag", "opened:", this.opened);
-      if (!this.opened) {
-        return this.onClosed();
-      }
-    };
-
-    Sidebar.prototype.sign = function(inner_path, privatekey) {
-      this.wrapper.displayProgress("sign", "Signing: " + inner_path + "...", 0);
-      return this.wrapper.ws.cmd("siteSign", {
-        privatekey: privatekey,
-        inner_path: inner_path,
-        update_changed_files: true
-      }, (function(_this) {
-        return function(res) {
-          if (res === "ok") {
-            return _this.wrapper.displayProgress("sign", inner_path + " signed!", 100);
-          } else {
-            return _this.wrapper.displayProgress("sign", "Error signing " + inner_path, -1);
-          }
-        };
-      })(this));
-    };
-
-    Sidebar.prototype.publish = function(inner_path, privatekey) {
-      return this.wrapper.ws.cmd("sitePublish", {
-        privatekey: privatekey,
-        inner_path: inner_path,
-        sign: true,
-        update_changed_files: true
-      }, (function(_this) {
-        return function(res) {
-          if (res === "ok") {
-            return _this.wrapper.notifications.add("sign", "done", inner_path + " Signed and published!", 5000);
-          }
-        };
-      })(this));
-    };
-
-    Sidebar.prototype.handleSiteDeleteClick = function() {
-      var options, question;
-      if (this.wrapper.site_info.privatekey) {
-        question = "Are you sure?<br>This site has a saved private key";
-        options = ["Forget private key and delete site"];
-      } else {
-        question = "Are you sure?";
-        options = ["Delete this site", "Blacklist"];
-      }
-      return this.wrapper.displayConfirm(question, options, (function(_this) {
-        return function(confirmed) {
-          if (confirmed === 1) {
-            _this.tag.find("#button-delete").addClass("loading");
-            return _this.wrapper.ws.cmd("siteDelete", _this.wrapper.site_info.address, function() {
-              return document.location = $(".fixbutton-bg").attr("href");
-            });
-          } else if (confirmed === 2) {
-            return _this.wrapper.displayPrompt("Blacklist this site", "text", "Delete and Blacklist", "Reason", function(reason) {
-              _this.tag.find("#button-delete").addClass("loading");
-              _this.wrapper.ws.cmd("siteblockAdd", [_this.wrapper.site_info.address, reason]);
-              return _this.wrapper.ws.cmd("siteDelete", _this.wrapper.site_info.address, function() {
-                return document.location = $(".fixbutton-bg").attr("href");
-              });
-            });
-          }
-        };
-      })(this));
-    };
-
-    Sidebar.prototype.onOpened = function() {
-      var menu;
-      this.log("Opened");
-      this.scrollable();
-      this.tag.find("#checkbox-owned, #checkbox-autodownloadoptional").off("click touchend").on("click touchend", (function(_this) {
-        return function() {
-          return setTimeout((function() {
-            return _this.scrollable();
-          }), 300);
-        };
-      })(this));
-      this.tag.find("#button-sitelimit").off("click touchend").on("click touchend", (function(_this) {
-        return function() {
-          _this.wrapper.ws.cmd("siteSetLimit", $("#input-sitelimit").val(), function(res) {
-            if (res === "ok") {
-              _this.wrapper.notifications.add("done-sitelimit", "done", "Site storage limit modified!", 5000);
-            }
-            return _this.updateHtmlTag();
-          });
-          return false;
-        };
-      })(this));
-      this.tag.find("#button-autodownload_bigfile_size_limit").off("click touchend").on("click touchend", (function(_this) {
-        return function() {
-          _this.wrapper.ws.cmd("siteSetAutodownloadBigfileLimit", $("#input-autodownload_bigfile_size_limit").val(), function(res) {
-            if (res === "ok") {
-              _this.wrapper.notifications.add("done-bigfilelimit", "done", "Site bigfile auto download limit modified!", 5000);
-            }
-            return _this.updateHtmlTag();
-          });
-          return false;
-        };
-      })(this));
-      this.tag.find("#button-autodownload_previous").off("click touchend").on("click touchend", (function(_this) {
-        return function() {
-          _this.wrapper.ws.cmd("siteUpdate", {
-            "address": _this.wrapper.site_info.address,
-            "check_files": true
-          }, function() {
-            return _this.wrapper.notifications.add("done-download_optional", "done", "Optional files downloaded", 5000);
-          });
-          _this.wrapper.notifications.add("start-download_optional", "info", "Optional files download started", 5000);
-          return false;
-        };
-      })(this));
-      this.tag.find("#button-dbreload").off("click touchend").on("click touchend", (function(_this) {
-        return function() {
-          _this.wrapper.ws.cmd("dbReload", [], function() {
-            _this.wrapper.notifications.add("done-dbreload", "done", "Database schema reloaded!", 5000);
-            return _this.updateHtmlTag();
-          });
-          return false;
-        };
-      })(this));
-      this.tag.find("#button-dbrebuild").off("click touchend").on("click touchend", (function(_this) {
-        return function() {
-          _this.wrapper.notifications.add("done-dbrebuild", "info", "Database rebuilding....");
-          _this.wrapper.ws.cmd("dbRebuild", [], function() {
-            _this.wrapper.notifications.add("done-dbrebuild", "done", "Database rebuilt!", 5000);
-            return _this.updateHtmlTag();
-          });
-          return false;
-        };
-      })(this));
-      this.tag.find("#button-update").off("click touchend").on("click touchend", (function(_this) {
-        return function() {
-          _this.tag.find("#button-update").addClass("loading");
-          _this.wrapper.ws.cmd("siteUpdate", _this.wrapper.site_info.address, function() {
-            _this.wrapper.notifications.add("done-updated", "done", "Site updated!", 5000);
-            return _this.tag.find("#button-update").removeClass("loading");
-          });
-          return false;
-        };
-      })(this));
-      this.tag.find("#button-pause").off("click touchend").on("click touchend", (function(_this) {
-        return function() {
-          _this.tag.find("#button-pause").addClass("hidden");
-          _this.wrapper.ws.cmd("sitePause", _this.wrapper.site_info.address);
-          return false;
-        };
-      })(this));
-      this.tag.find("#button-resume").off("click touchend").on("click touchend", (function(_this) {
-        return function() {
-          _this.tag.find("#button-resume").addClass("hidden");
-          _this.wrapper.ws.cmd("siteResume", _this.wrapper.site_info.address);
-          return false;
-        };
-      })(this));
-      this.tag.find("#button-delete").off("click touchend").on("click touchend", (function(_this) {
-        return function() {
-          _this.handleSiteDeleteClick();
-          return false;
-        };
-      })(this));
-      this.tag.find("#checkbox-owned").off("click touchend").on("click touchend", (function(_this) {
-        return function() {
-          var owned;
-          owned = _this.tag.find("#checkbox-owned").is(":checked");
-          return _this.wrapper.ws.cmd("siteSetOwned", [owned], function(res_set_owned) {
-            _this.log("Owned", owned);
-            if (owned) {
-              return _this.wrapper.ws.cmd("siteRecoverPrivatekey", [], function(res_recover) {
-                if (res_recover === "ok") {
-                  return _this.wrapper.notifications.add("recover", "done", "Private key recovered from master seed", 5000);
-                } else {
-                  return _this.log("Unable to recover private key: " + res_recover.error);
-                }
-              });
-            }
-          });
-        };
-      })(this));
-      this.tag.find("#checkbox-autodownloadoptional").off("click touchend").on("click touchend", (function(_this) {
-        return function() {
-          return _this.wrapper.ws.cmd("siteSetAutodownloadoptional", [_this.tag.find("#checkbox-autodownloadoptional").is(":checked")]);
-        };
-      })(this));
-      this.tag.find("#button-identity").off("click touchend").on("click touchend", (function(_this) {
-        return function() {
-          _this.wrapper.ws.cmd("certSelect");
-          return false;
-        };
-      })(this));
-      this.tag.find("#button-settings").off("click touchend").on("click touchend", (function(_this) {
-        return function() {
-          _this.wrapper.ws.cmd("fileGet", "content.json", function(res) {
-            var data, json_raw;
-            data = JSON.parse(res);
-            data["title"] = $("#settings-title").val();
-            data["description"] = $("#settings-description").val();
-            json_raw = unescape(encodeURIComponent(JSON.stringify(data, void 0, '\t')));
-            return _this.wrapper.ws.cmd("fileWrite", ["content.json", btoa(json_raw), true], function(res) {
-              if (res !== "ok") {
-                return _this.wrapper.notifications.add("file-write", "error", "File write error: " + res);
-              } else {
-                _this.wrapper.notifications.add("file-write", "done", "Site settings saved!", 5000);
-                if (_this.wrapper.site_info.privatekey) {
-                  _this.wrapper.ws.cmd("siteSign", {
-                    privatekey: "stored",
-                    inner_path: "content.json",
-                    update_changed_files: true
-                  });
-                }
-                return _this.updateHtmlTag();
-              }
-            });
-          });
-          return false;
-        };
-      })(this));
-      this.tag.find("#link-directory").off("click touchend").on("click touchend", (function(_this) {
-        return function() {
-          _this.wrapper.ws.cmd("serverShowdirectory", ["site", _this.wrapper.site_info.address]);
-          return false;
-        };
-      })(this));
-      this.tag.find("#link-copypeers").off("click touchend").on("click touchend", (function(_this) {
-        return function(e) {
-          var copy_text, handler;
-          copy_text = e.currentTarget.href;
-          handler = function(e) {
-            e.clipboardData.setData('text/plain', copy_text);
-            e.preventDefault();
-            _this.wrapper.notifications.add("copy", "done", "Site address with peers copied to your clipboard", 5000);
-            return document.removeEventListener('copy', handler, true);
-          };
-          document.addEventListener('copy', handler, true);
-          document.execCommand('copy');
-          return false;
-        };
-      })(this));
-      $(document).on("click touchend", (function(_this) {
-        return function() {
-          var ref, ref1;
-          if ((ref = _this.tag) != null) {
-            ref.find("#button-sign-publish-menu").removeClass("visible");
-          }
-          return (ref1 = _this.tag) != null ? ref1.find(".contents + .flex").removeClass("sign-publish-flex") : void 0;
-        };
-      })(this));
-      this.tag.find(".contents-content").off("click touchend").on("click touchend", (function(_this) {
-        return function(e) {
-          $("#input-contents").val(e.currentTarget.innerText);
-          return false;
-        };
-      })(this));
-      menu = new Menu(this.tag.find("#menu-sign-publish"));
-      menu.elem.css("margin-top", "-130px");
-      menu.addItem("Sign", (function(_this) {
-        return function() {
-          var inner_path;
-          inner_path = _this.tag.find("#input-contents").val();
-          _this.wrapper.ws.cmd("fileRules", {
-            inner_path: inner_path
-          }, function(rules) {
-            var ref;
-            if (ref = _this.wrapper.site_info.auth_address, indexOf.call(rules.signers, ref) >= 0) {
-              return _this.sign(inner_path);
-            } else if (_this.wrapper.site_info.privatekey) {
-              return _this.sign(inner_path, "stored");
-            } else {
-              return _this.wrapper.displayPrompt("Enter your private key:", "password", "Sign", "", function(privatekey) {
-                return _this.sign(inner_path, privatekey);
-              });
-            }
-          });
-          _this.tag.find(".contents + .flex").removeClass("active");
-          return menu.hide();
-        };
-      })(this));
-      menu.addItem("Publish", (function(_this) {
-        return function() {
-          var inner_path;
-          inner_path = _this.tag.find("#input-contents").val();
-          _this.wrapper.ws.cmd("sitePublish", {
-            "inner_path": inner_path,
-            "sign": false
-          });
-          _this.tag.find(".contents + .flex").removeClass("active");
-          return menu.hide();
-        };
-      })(this));
-      this.tag.find("#menu-sign-publish").off("click touchend").on("click touchend", (function(_this) {
-        return function() {
-          if (window.visible_menu === menu) {
-            _this.tag.find(".contents + .flex").removeClass("active");
-            menu.hide();
-          } else {
-            _this.tag.find(".contents + .flex").addClass("active");
-            _this.tag.find(".content-wrapper").prop("scrollTop", 10000);
-            menu.show();
-          }
-          return false;
-        };
-      })(this));
-      $("body").on("click", (function(_this) {
-        return function() {
-          if (_this.tag) {
-            return _this.tag.find(".contents + .flex").removeClass("active");
-          }
-        };
-      })(this));
-      this.tag.find("#button-sign-publish").off("click touchend").on("click touchend", (function(_this) {
-        return function() {
-          var inner_path;
-          inner_path = _this.tag.find("#input-contents").val();
-          _this.wrapper.ws.cmd("fileRules", {
-            inner_path: inner_path
-          }, function(rules) {
-            var ref;
-            if (ref = _this.wrapper.site_info.auth_address, indexOf.call(rules.signers, ref) >= 0) {
-              return _this.publish(inner_path, null);
-            } else if (_this.wrapper.site_info.privatekey) {
-              return _this.publish(inner_path, "stored");
-            } else {
-              return _this.wrapper.displayPrompt("Enter your private key:", "password", "Sign", "", function(privatekey) {
-                return _this.publish(inner_path, privatekey);
-              });
-            }
-          });
-          return false;
-        };
-      })(this));
-      this.tag.find(".close").off("click touchend").on("click touchend", (function(_this) {
-        return function(e) {
-          _this.close();
-          return false;
-        };
-      })(this));
-      return this.loadGlobe();
-    };
-
-    Sidebar.prototype.close = function() {
-      this.move_lock = "x";
-      this.startDrag();
-      return this.stopDrag();
-    };
-
-    Sidebar.prototype.onClosed = function() {
-      $(window).off("resize");
-      $(window).on("resize", this.resized);
-      $(document.body).css("transition", "0.6s ease-in-out").removeClass("body-sidebar").on(transitionEnd, (function(_this) {
-        return function(e) {
-          if (e.target === document.body && !$(document.body).hasClass("body-sidebar") && !$(document.body).hasClass("body-console")) {
-            $(document.body).css("height", "auto").css("perspective", "").css("will-change", "").css("transition", "").off(transitionEnd);
-            return _this.unloadGlobe();
-          }
-        };
-      })(this));
-      return this.wrapper.setSiteInfo = this.original_set_site_info;
-    };
-
-    Sidebar.prototype.loadGlobe = function() {
-      if (this.tag.find(".globe").hasClass("loading")) {
-        return setTimeout(((function(_this) {
-          return function() {
-            var script_tag;
-            if (typeof DAT === "undefined") {
-              script_tag = $("<script>");
-              script_tag.attr("nonce", _this.wrapper.script_nonce);
-              script_tag.attr("src", "/uimedia/globe/all.js");
-              script_tag.on("load", _this.displayGlobe);
-              return document.head.appendChild(script_tag[0]);
-            } else {
-              return _this.displayGlobe();
-            }
-          };
-        })(this)), 600);
-      }
-    };
-
-    Sidebar.prototype.displayGlobe = function() {
-      var img;
-      img = new Image();
-      img.src = "/uimedia/globe/world.jpg";
-      return img.onload = (function(_this) {
-        return function() {
-          return _this.wrapper.ws.cmd("sidebarGetPeers", [], function(globe_data) {
-            var e, ref, ref1, ref2;
-            if (_this.globe) {
-              _this.globe.scene.remove(_this.globe.points);
-              _this.globe.addData(globe_data, {
-                format: 'magnitude',
-                name: "hello",
-                animated: false
-              });
-              _this.globe.createPoints();
-              return (ref = _this.tag) != null ? ref.find(".globe").removeClass("loading") : void 0;
-            } else if (typeof DAT !== "undefined") {
-              try {
-                _this.globe = new DAT.Globe(_this.tag.find(".globe")[0], {
-                  "imgDir": "/uimedia/globe/"
-                });
-                _this.globe.addData(globe_data, {
-                  format: 'magnitude',
-                  name: "hello"
-                });
-                _this.globe.createPoints();
-                _this.globe.animate();
-              } catch (error) {
-                e = error;
-                console.log("WebGL error", e);
-                if ((ref1 = _this.tag) != null) {
-                  ref1.find(".globe").addClass("error").text("WebGL not supported");
-                }
-              }
-              return (ref2 = _this.tag) != null ? ref2.find(".globe").removeClass("loading") : void 0;
-            }
-          });
-        };
-      })(this);
-    };
-
-    Sidebar.prototype.unloadGlobe = function() {
-      if (!this.globe) {
-        return false;
-      }
-      this.globe.unload();
-      return this.globe = null;
-    };
-
-    return Sidebar;
-
-  })(Class);
-
-  wrapper = window.wrapper;
-
-  setTimeout((function() {
-    return window.sidebar = new Sidebar(wrapper);
-  }), 500);
-
-  window.transitionEnd = 'transitionend webkitTransitionEnd oTransitionEnd otransitionend';
-
-}).call(this);
-
-
-/* ---- morphdom.js ---- */
-
-
-(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.morphdom = 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<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
-var specialElHandlers = {
-    /**
-     * Needed for IE. Apparently IE doesn't think
-     * that "selected" is an attribute when reading
-     * over the attributes using selectEl.attributes
-     */
-    OPTION: function(fromEl, toEl) {
-        if ((fromEl.selected = toEl.selected)) {
-            fromEl.setAttribute('selected', '');
-        } else {
-            fromEl.removeAttribute('selected', '');
-        }
-    },
-    /**
-     * The "value" attribute is special for the <input> element
-     * since it sets the initial value. Changing the "value"
-     * attribute without changing the "value" property will have
-     * no effect since it is only used to the set the initial value.
-     * Similar for the "checked" attribute.
-     */
-    /*INPUT: function(fromEl, toEl) {
-        fromEl.checked = toEl.checked;
-        fromEl.value = toEl.value;
-
-        if (!toEl.hasAttribute('checked')) {
-            fromEl.removeAttribute('checked');
-        }
-
-        if (!toEl.hasAttribute('value')) {
-            fromEl.removeAttribute('value');
-        }
-    }*/
-};
-
-function noop() {}
-
-/**
- * Loop over all of the attributes on the target node and make sure the
- * original DOM node has the same attributes. If an attribute
- * found on the original node is not on the new node then remove it from
- * the original node
- * @param  {HTMLElement} fromNode
- * @param  {HTMLElement} toNode
- */
-function morphAttrs(fromNode, toNode) {
-    var attrs = toNode.attributes;
-    var i;
-    var attr;
-    var attrName;
-    var attrValue;
-    var foundAttrs = {};
-
-    for (i=attrs.length-1; i>=0; i--) {
-        attr = attrs[i];
-        if (attr.specified !== false) {
-            attrName = attr.name;
-            attrValue = attr.value;
-            foundAttrs[attrName] = true;
-
-            if (fromNode.getAttribute(attrName) !== attrValue) {
-                fromNode.setAttribute(attrName, attrValue);
-            }
-        }
-    }
-
-    // Delete any extra attributes found on the original DOM element that weren't
-    // found on the target element.
-    attrs = fromNode.attributes;
-
-    for (i=attrs.length-1; i>=0; i--) {
-        attr = attrs[i];
-        if (attr.specified !== false) {
-            attrName = attr.name;
-            if (!foundAttrs.hasOwnProperty(attrName)) {
-                fromNode.removeAttribute(attrName);
-            }
-        }
-    }
-}
-
-/**
- * Copies the children of one DOM element to another DOM element
- */
-function moveChildren(from, to) {
-    var curChild = from.firstChild;
-    while(curChild) {
-        var nextChild = curChild.nextSibling;
-        to.appendChild(curChild);
-        curChild = nextChild;
-    }
-    return to;
-}
-
-function morphdom(fromNode, toNode, options) {
-    if (!options) {
-        options = {};
-    }
-
-    if (typeof toNode === 'string') {
-        var newBodyEl = document.createElement('body');
-        newBodyEl.innerHTML = toNode;
-        toNode = newBodyEl.childNodes[0];
-    }
-
-    var savedEls = {}; // Used to save off DOM elements with IDs
-    var unmatchedEls = {};
-    var onNodeDiscarded = options.onNodeDiscarded || noop;
-    var onBeforeMorphEl = options.onBeforeMorphEl || noop;
-    var onBeforeMorphElChildren = options.onBeforeMorphElChildren || noop;
-
-    function removeNodeHelper(node, nestedInSavedEl) {
-        var id = node.id;
-        // If the node has an ID then save it off since we will want
-        // to reuse it in case the target DOM tree has a DOM element
-        // with the same ID
-        if (id) {
-            savedEls[id] = node;
-        } else if (!nestedInSavedEl) {
-            // If we are not nested in a saved element then we know that this node has been
-            // completely discarded and will not exist in the final DOM.
-            onNodeDiscarded(node);
-        }
-
-        if (node.nodeType === 1) {
-            var curChild = node.firstChild;
-            while(curChild) {
-                removeNodeHelper(curChild, nestedInSavedEl || id);
-                curChild = curChild.nextSibling;
-            }
-        }
-    }
-
-    function walkDiscardedChildNodes(node) {
-        if (node.nodeType === 1) {
-            var curChild = node.firstChild;
-            while(curChild) {
-
-
-                if (!curChild.id) {
-                    // We only want to handle nodes that don't have an ID to avoid double
-                    // walking the same saved element.
-
-                    onNodeDiscarded(curChild);
-
-                    // Walk recursively
-                    walkDiscardedChildNodes(curChild);
-                }
-
-                curChild = curChild.nextSibling;
-            }
-        }
-    }
-
-    function removeNode(node, parentNode, alreadyVisited) {
-        parentNode.removeChild(node);
-
-        if (alreadyVisited) {
-            if (!node.id) {
-                onNodeDiscarded(node);
-                walkDiscardedChildNodes(node);
-            }
-        } else {
-            removeNodeHelper(node);
-        }
-    }
-
-    function morphEl(fromNode, toNode, alreadyVisited) {
-        if (toNode.id) {
-            // If an element with an ID is being morphed then it is will be in the final
-            // DOM so clear it out of the saved elements collection
-            delete savedEls[toNode.id];
-        }
-
-        if (onBeforeMorphEl(fromNode, toNode) === false) {
-            return;
-        }
-
-        morphAttrs(fromNode, toNode);
-
-        if (onBeforeMorphElChildren(fromNode, toNode) === false) {
-            return;
-        }
-
-        var curToNodeChild = toNode.firstChild;
-        var curFromNodeChild = fromNode.firstChild;
-        var curToNodeId;
-
-        var fromNextSibling;
-        var toNextSibling;
-        var savedEl;
-        var unmatchedEl;
-
-        outer: while(curToNodeChild) {
-            toNextSibling = curToNodeChild.nextSibling;
-            curToNodeId = curToNodeChild.id;
-
-            while(curFromNodeChild) {
-                var curFromNodeId = curFromNodeChild.id;
-                fromNextSibling = curFromNodeChild.nextSibling;
-
-                if (!alreadyVisited) {
-                    if (curFromNodeId && (unmatchedEl = unmatchedEls[curFromNodeId])) {
-                        unmatchedEl.parentNode.replaceChild(curFromNodeChild, unmatchedEl);
-                        morphEl(curFromNodeChild, unmatchedEl, alreadyVisited);
-                        curFromNodeChild = fromNextSibling;
-                        continue;
-                    }
-                }
-
-                var curFromNodeType = curFromNodeChild.nodeType;
-
-                if (curFromNodeType === curToNodeChild.nodeType) {
-                    var isCompatible = false;
-
-                    if (curFromNodeType === 1) { // Both nodes being compared are Element nodes
-                        if (curFromNodeChild.tagName === curToNodeChild.tagName) {
-                            // We have compatible DOM elements
-                            if (curFromNodeId || curToNodeId) {
-                                // If either DOM element has an ID then we handle
-                                // those differently since we want to match up
-                                // by ID
-                                if (curToNodeId === curFromNodeId) {
-                                    isCompatible = true;
-                                }
-                            } else {
-                                isCompatible = true;
-                            }
-                        }
-
-                        if (isCompatible) {
-                            // We found compatible DOM elements so add a
-                            // task to morph the compatible DOM elements
-                            morphEl(curFromNodeChild, curToNodeChild, alreadyVisited);
-                        }
-                    } else if (curFromNodeType === 3) { // Both nodes being compared are Text nodes
-                        isCompatible = true;
-                        curFromNodeChild.nodeValue = curToNodeChild.nodeValue;
-                    }
-
-                    if (isCompatible) {
-                        curToNodeChild = toNextSibling;
-                        curFromNodeChild = fromNextSibling;
-                        continue outer;
-                    }
-                }
-
-                // No compatible match so remove the old node from the DOM
-                removeNode(curFromNodeChild, fromNode, alreadyVisited);
-
-                curFromNodeChild = fromNextSibling;
-            }
-
-            if (curToNodeId) {
-                if ((savedEl = savedEls[curToNodeId])) {
-                    morphEl(savedEl, curToNodeChild, true);
-                    curToNodeChild = savedEl; // We want to append the saved element instead
-                } else {
-                    // The current DOM element in the target tree has an ID
-                    // but we did not find a match in any of the corresponding
-                    // siblings. We just put the target element in the old DOM tree
-                    // but if we later find an element in the old DOM tree that has
-                    // a matching ID then we will replace the target element
-                    // with the corresponding old element and morph the old element
-                    unmatchedEls[curToNodeId] = curToNodeChild;
-                }
-            }
-
-            // If we got this far then we did not find a candidate match for our "to node"
-            // and we exhausted all of the children "from" nodes. Therefore, we will just
-            // append the current "to node" to the end
-            fromNode.appendChild(curToNodeChild);
-
-            curToNodeChild = toNextSibling;
-            curFromNodeChild = fromNextSibling;
-        }
-
-        // We have processed all of the "to nodes". If curFromNodeChild is non-null then
-        // we still have some from nodes left over that need to be removed
-        while(curFromNodeChild) {
-            fromNextSibling = curFromNodeChild.nextSibling;
-            removeNode(curFromNodeChild, fromNode, alreadyVisited);
-            curFromNodeChild = fromNextSibling;
-        }
-
-        var specialElHandler = specialElHandlers[fromNode.tagName];
-        if (specialElHandler) {
-            specialElHandler(fromNode, toNode);
-        }
-    }
-
-    var morphedNode = fromNode;
-    var morphedNodeType = morphedNode.nodeType;
-    var toNodeType = toNode.nodeType;
-
-    // Handle the case where we are given two DOM nodes that are not
-    // compatible (e.g. <div> --> <span> or <div> --> TEXT)
-    if (morphedNodeType === 1) {
-        if (toNodeType === 1) {
-            if (morphedNode.tagName !== toNode.tagName) {
-                onNodeDiscarded(fromNode);
-                morphedNode = moveChildren(morphedNode, document.createElement(toNode.tagName));
-            }
-        } else {
-            // Going from an element node to a text node
-            return toNode;
-        }
-    } else if (morphedNodeType === 3) { // Text node
-        if (toNodeType === 3) {
-            morphedNode.nodeValue = toNode.nodeValue;
-            return morphedNode;
-        } else {
-            onNodeDiscarded(fromNode);
-            // Text node to something else
-            return toNode;
-        }
-    }
-
-    morphEl(morphedNode, toNode, false);
-
-    // Fire the "onNodeDiscarded" event for any saved elements
-    // that never found a new home in the morphed DOM
-    for (var savedElId in savedEls) {
-        if (savedEls.hasOwnProperty(savedElId)) {
-            var savedEl = savedEls[savedElId];
-            onNodeDiscarded(savedEl);
-            walkDiscardedChildNodes(savedEl);
-        }
-    }
-
-    if (morphedNode !== fromNode && fromNode.parentNode) {
-        fromNode.parentNode.replaceChild(morphedNode, fromNode);
-    }
-
-    return morphedNode;
-}
-
-module.exports = morphdom;
-},{}]},{},[1])(1)
-});
\ No newline at end of file
diff --git a/plugins/Sidebar/media/morphdom.js b/plugins/Sidebar/media/morphdom.js
deleted file mode 100644
index 6829eef3..00000000
--- a/plugins/Sidebar/media/morphdom.js
+++ /dev/null
@@ -1,340 +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.morphdom = 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<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
-var specialElHandlers = {
-    /**
-     * Needed for IE. Apparently IE doesn't think
-     * that "selected" is an attribute when reading
-     * over the attributes using selectEl.attributes
-     */
-    OPTION: function(fromEl, toEl) {
-        if ((fromEl.selected = toEl.selected)) {
-            fromEl.setAttribute('selected', '');
-        } else {
-            fromEl.removeAttribute('selected', '');
-        }
-    },
-    /**
-     * The "value" attribute is special for the <input> element
-     * since it sets the initial value. Changing the "value"
-     * attribute without changing the "value" property will have
-     * no effect since it is only used to the set the initial value.
-     * Similar for the "checked" attribute.
-     */
-    /*INPUT: function(fromEl, toEl) {
-        fromEl.checked = toEl.checked;
-        fromEl.value = toEl.value;
-
-        if (!toEl.hasAttribute('checked')) {
-            fromEl.removeAttribute('checked');
-        }
-
-        if (!toEl.hasAttribute('value')) {
-            fromEl.removeAttribute('value');
-        }
-    }*/
-};
-
-function noop() {}
-
-/**
- * Loop over all of the attributes on the target node and make sure the
- * original DOM node has the same attributes. If an attribute
- * found on the original node is not on the new node then remove it from
- * the original node
- * @param  {HTMLElement} fromNode
- * @param  {HTMLElement} toNode
- */
-function morphAttrs(fromNode, toNode) {
-    var attrs = toNode.attributes;
-    var i;
-    var attr;
-    var attrName;
-    var attrValue;
-    var foundAttrs = {};
-
-    for (i=attrs.length-1; i>=0; i--) {
-        attr = attrs[i];
-        if (attr.specified !== false) {
-            attrName = attr.name;
-            attrValue = attr.value;
-            foundAttrs[attrName] = true;
-
-            if (fromNode.getAttribute(attrName) !== attrValue) {
-                fromNode.setAttribute(attrName, attrValue);
-            }
-        }
-    }
-
-    // Delete any extra attributes found on the original DOM element that weren't
-    // found on the target element.
-    attrs = fromNode.attributes;
-
-    for (i=attrs.length-1; i>=0; i--) {
-        attr = attrs[i];
-        if (attr.specified !== false) {
-            attrName = attr.name;
-            if (!foundAttrs.hasOwnProperty(attrName)) {
-                fromNode.removeAttribute(attrName);
-            }
-        }
-    }
-}
-
-/**
- * Copies the children of one DOM element to another DOM element
- */
-function moveChildren(from, to) {
-    var curChild = from.firstChild;
-    while(curChild) {
-        var nextChild = curChild.nextSibling;
-        to.appendChild(curChild);
-        curChild = nextChild;
-    }
-    return to;
-}
-
-function morphdom(fromNode, toNode, options) {
-    if (!options) {
-        options = {};
-    }
-
-    if (typeof toNode === 'string') {
-        var newBodyEl = document.createElement('body');
-        newBodyEl.innerHTML = toNode;
-        toNode = newBodyEl.childNodes[0];
-    }
-
-    var savedEls = {}; // Used to save off DOM elements with IDs
-    var unmatchedEls = {};
-    var onNodeDiscarded = options.onNodeDiscarded || noop;
-    var onBeforeMorphEl = options.onBeforeMorphEl || noop;
-    var onBeforeMorphElChildren = options.onBeforeMorphElChildren || noop;
-
-    function removeNodeHelper(node, nestedInSavedEl) {
-        var id = node.id;
-        // If the node has an ID then save it off since we will want
-        // to reuse it in case the target DOM tree has a DOM element
-        // with the same ID
-        if (id) {
-            savedEls[id] = node;
-        } else if (!nestedInSavedEl) {
-            // If we are not nested in a saved element then we know that this node has been
-            // completely discarded and will not exist in the final DOM.
-            onNodeDiscarded(node);
-        }
-
-        if (node.nodeType === 1) {
-            var curChild = node.firstChild;
-            while(curChild) {
-                removeNodeHelper(curChild, nestedInSavedEl || id);
-                curChild = curChild.nextSibling;
-            }
-        }
-    }
-
-    function walkDiscardedChildNodes(node) {
-        if (node.nodeType === 1) {
-            var curChild = node.firstChild;
-            while(curChild) {
-
-
-                if (!curChild.id) {
-                    // We only want to handle nodes that don't have an ID to avoid double
-                    // walking the same saved element.
-
-                    onNodeDiscarded(curChild);
-
-                    // Walk recursively
-                    walkDiscardedChildNodes(curChild);
-                }
-
-                curChild = curChild.nextSibling;
-            }
-        }
-    }
-
-    function removeNode(node, parentNode, alreadyVisited) {
-        parentNode.removeChild(node);
-
-        if (alreadyVisited) {
-            if (!node.id) {
-                onNodeDiscarded(node);
-                walkDiscardedChildNodes(node);
-            }
-        } else {
-            removeNodeHelper(node);
-        }
-    }
-
-    function morphEl(fromNode, toNode, alreadyVisited) {
-        if (toNode.id) {
-            // If an element with an ID is being morphed then it is will be in the final
-            // DOM so clear it out of the saved elements collection
-            delete savedEls[toNode.id];
-        }
-
-        if (onBeforeMorphEl(fromNode, toNode) === false) {
-            return;
-        }
-
-        morphAttrs(fromNode, toNode);
-
-        if (onBeforeMorphElChildren(fromNode, toNode) === false) {
-            return;
-        }
-
-        var curToNodeChild = toNode.firstChild;
-        var curFromNodeChild = fromNode.firstChild;
-        var curToNodeId;
-
-        var fromNextSibling;
-        var toNextSibling;
-        var savedEl;
-        var unmatchedEl;
-
-        outer: while(curToNodeChild) {
-            toNextSibling = curToNodeChild.nextSibling;
-            curToNodeId = curToNodeChild.id;
-
-            while(curFromNodeChild) {
-                var curFromNodeId = curFromNodeChild.id;
-                fromNextSibling = curFromNodeChild.nextSibling;
-
-                if (!alreadyVisited) {
-                    if (curFromNodeId && (unmatchedEl = unmatchedEls[curFromNodeId])) {
-                        unmatchedEl.parentNode.replaceChild(curFromNodeChild, unmatchedEl);
-                        morphEl(curFromNodeChild, unmatchedEl, alreadyVisited);
-                        curFromNodeChild = fromNextSibling;
-                        continue;
-                    }
-                }
-
-                var curFromNodeType = curFromNodeChild.nodeType;
-
-                if (curFromNodeType === curToNodeChild.nodeType) {
-                    var isCompatible = false;
-
-                    if (curFromNodeType === 1) { // Both nodes being compared are Element nodes
-                        if (curFromNodeChild.tagName === curToNodeChild.tagName) {
-                            // We have compatible DOM elements
-                            if (curFromNodeId || curToNodeId) {
-                                // If either DOM element has an ID then we handle
-                                // those differently since we want to match up
-                                // by ID
-                                if (curToNodeId === curFromNodeId) {
-                                    isCompatible = true;
-                                }
-                            } else {
-                                isCompatible = true;
-                            }
-                        }
-
-                        if (isCompatible) {
-                            // We found compatible DOM elements so add a
-                            // task to morph the compatible DOM elements
-                            morphEl(curFromNodeChild, curToNodeChild, alreadyVisited);
-                        }
-                    } else if (curFromNodeType === 3) { // Both nodes being compared are Text nodes
-                        isCompatible = true;
-                        curFromNodeChild.nodeValue = curToNodeChild.nodeValue;
-                    }
-
-                    if (isCompatible) {
-                        curToNodeChild = toNextSibling;
-                        curFromNodeChild = fromNextSibling;
-                        continue outer;
-                    }
-                }
-
-                // No compatible match so remove the old node from the DOM
-                removeNode(curFromNodeChild, fromNode, alreadyVisited);
-
-                curFromNodeChild = fromNextSibling;
-            }
-
-            if (curToNodeId) {
-                if ((savedEl = savedEls[curToNodeId])) {
-                    morphEl(savedEl, curToNodeChild, true);
-                    curToNodeChild = savedEl; // We want to append the saved element instead
-                } else {
-                    // The current DOM element in the target tree has an ID
-                    // but we did not find a match in any of the corresponding
-                    // siblings. We just put the target element in the old DOM tree
-                    // but if we later find an element in the old DOM tree that has
-                    // a matching ID then we will replace the target element
-                    // with the corresponding old element and morph the old element
-                    unmatchedEls[curToNodeId] = curToNodeChild;
-                }
-            }
-
-            // If we got this far then we did not find a candidate match for our "to node"
-            // and we exhausted all of the children "from" nodes. Therefore, we will just
-            // append the current "to node" to the end
-            fromNode.appendChild(curToNodeChild);
-
-            curToNodeChild = toNextSibling;
-            curFromNodeChild = fromNextSibling;
-        }
-
-        // We have processed all of the "to nodes". If curFromNodeChild is non-null then
-        // we still have some from nodes left over that need to be removed
-        while(curFromNodeChild) {
-            fromNextSibling = curFromNodeChild.nextSibling;
-            removeNode(curFromNodeChild, fromNode, alreadyVisited);
-            curFromNodeChild = fromNextSibling;
-        }
-
-        var specialElHandler = specialElHandlers[fromNode.tagName];
-        if (specialElHandler) {
-            specialElHandler(fromNode, toNode);
-        }
-    }
-
-    var morphedNode = fromNode;
-    var morphedNodeType = morphedNode.nodeType;
-    var toNodeType = toNode.nodeType;
-
-    // Handle the case where we are given two DOM nodes that are not
-    // compatible (e.g. <div> --> <span> or <div> --> TEXT)
-    if (morphedNodeType === 1) {
-        if (toNodeType === 1) {
-            if (morphedNode.tagName !== toNode.tagName) {
-                onNodeDiscarded(fromNode);
-                morphedNode = moveChildren(morphedNode, document.createElement(toNode.tagName));
-            }
-        } else {
-            // Going from an element node to a text node
-            return toNode;
-        }
-    } else if (morphedNodeType === 3) { // Text node
-        if (toNodeType === 3) {
-            morphedNode.nodeValue = toNode.nodeValue;
-            return morphedNode;
-        } else {
-            onNodeDiscarded(fromNode);
-            // Text node to something else
-            return toNode;
-        }
-    }
-
-    morphEl(morphedNode, toNode, false);
-
-    // Fire the "onNodeDiscarded" event for any saved elements
-    // that never found a new home in the morphed DOM
-    for (var savedElId in savedEls) {
-        if (savedEls.hasOwnProperty(savedElId)) {
-            var savedEl = savedEls[savedElId];
-            onNodeDiscarded(savedEl);
-            walkDiscardedChildNodes(savedEl);
-        }
-    }
-
-    if (morphedNode !== fromNode && fromNode.parentNode) {
-        fromNode.parentNode.replaceChild(morphedNode, fromNode);
-    }
-
-    return morphedNode;
-}
-
-module.exports = morphdom;
-},{}]},{},[1])(1)
-});
\ No newline at end of file
diff --git a/plugins/Sidebar/media_globe/Detector.js b/plugins/Sidebar/media_globe/Detector.js
deleted file mode 100644
index 1c074b83..00000000
--- a/plugins/Sidebar/media_globe/Detector.js
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * @author alteredq / http://alteredqualia.com/
- * @author mr.doob / http://mrdoob.com/
- */
-
-Detector = {
-
-  canvas : !! window.CanvasRenderingContext2D,
-  webgl : ( function () { try { return !! window.WebGLRenderingContext && !! document.createElement( 'canvas' ).getContext( 'experimental-webgl' ); } catch( e ) { return false; } } )(),
-  workers : !! window.Worker,
-  fileapi : window.File && window.FileReader && window.FileList && window.Blob,
-
-  getWebGLErrorMessage : function () {
-
-    var domElement = document.createElement( 'div' );
-
-    domElement.style.fontFamily = 'monospace';
-    domElement.style.fontSize = '13px';
-    domElement.style.textAlign = 'center';
-    domElement.style.background = '#eee';
-    domElement.style.color = '#000';
-    domElement.style.padding = '1em';
-    domElement.style.width = '475px';
-    domElement.style.margin = '5em auto 0';
-
-    if ( ! this.webgl ) {
-
-      domElement.innerHTML = window.WebGLRenderingContext ? [
-        'Sorry, your graphics card doesn\'t support <a href="http://khronos.org/webgl/wiki/Getting_a_WebGL_Implementation">WebGL</a>'
-      ].join( '\n' ) : [
-        'Sorry, your browser doesn\'t support <a href="http://khronos.org/webgl/wiki/Getting_a_WebGL_Implementation">WebGL</a><br/>',
-        'Please try with',
-        '<a href="http://www.google.com/chrome">Chrome</a>, ',
-        '<a href="http://www.mozilla.com/en-US/firefox/new/">Firefox 4</a> or',
-        '<a href="http://nightly.webkit.org/">Webkit Nightly (Mac)</a>'
-      ].join( '\n' );
-
-    }
-
-    return domElement;
-
-  },
-
-  addGetWebGLMessage : function ( parameters ) {
-
-    var parent, id, domElement;
-
-    parameters = parameters || {};
-
-    parent = parameters.parent !== undefined ? parameters.parent : document.body;
-    id = parameters.id !== undefined ? parameters.id : 'oldie';
-
-    domElement = Detector.getWebGLErrorMessage();
-    domElement.id = id;
-
-    parent.appendChild( domElement );
-
-  }
-
-};
diff --git a/plugins/Sidebar/media_globe/Tween.js b/plugins/Sidebar/media_globe/Tween.js
deleted file mode 100644
index bdf141ad..00000000
--- a/plugins/Sidebar/media_globe/Tween.js
+++ /dev/null
@@ -1,12 +0,0 @@
-// Tween.js - http://github.com/sole/tween.js
-var TWEEN=TWEEN||function(){var a,e,c,d,f=[];return{start:function(g){c=setInterval(this.update,1E3/(g||60))},stop:function(){clearInterval(c)},add:function(g){f.push(g)},remove:function(g){a=f.indexOf(g);a!==-1&&f.splice(a,1)},update:function(){a=0;e=f.length;for(d=(new Date).getTime();a<e;)if(f[a].update(d))a++;else{f.splice(a,1);e--}}}}();
-TWEEN.Tween=function(a){var e={},c={},d={},f=1E3,g=0,j=null,n=TWEEN.Easing.Linear.EaseNone,k=null,l=null,m=null;this.to=function(b,h){if(h!==null)f=h;for(var i in b)if(a[i]!==null)d[i]=b[i];return this};this.start=function(){TWEEN.add(this);j=(new Date).getTime()+g;for(var b in d)if(a[b]!==null){e[b]=a[b];c[b]=d[b]-a[b]}return this};this.stop=function(){TWEEN.remove(this);return this};this.delay=function(b){g=b;return this};this.easing=function(b){n=b;return this};this.chain=function(b){k=b};this.onUpdate=
-function(b){l=b;return this};this.onComplete=function(b){m=b;return this};this.update=function(b){var h,i;if(b<j)return true;b=(b-j)/f;b=b>1?1:b;i=n(b);for(h in c)a[h]=e[h]+c[h]*i;l!==null&&l.call(a,i);if(b==1){m!==null&&m.call(a);k!==null&&k.start();return false}return true}};TWEEN.Easing={Linear:{},Quadratic:{},Cubic:{},Quartic:{},Quintic:{},Sinusoidal:{},Exponential:{},Circular:{},Elastic:{},Back:{},Bounce:{}};TWEEN.Easing.Linear.EaseNone=function(a){return a};
-TWEEN.Easing.Quadratic.EaseIn=function(a){return a*a};TWEEN.Easing.Quadratic.EaseOut=function(a){return-a*(a-2)};TWEEN.Easing.Quadratic.EaseInOut=function(a){if((a*=2)<1)return 0.5*a*a;return-0.5*(--a*(a-2)-1)};TWEEN.Easing.Cubic.EaseIn=function(a){return a*a*a};TWEEN.Easing.Cubic.EaseOut=function(a){return--a*a*a+1};TWEEN.Easing.Cubic.EaseInOut=function(a){if((a*=2)<1)return 0.5*a*a*a;return 0.5*((a-=2)*a*a+2)};TWEEN.Easing.Quartic.EaseIn=function(a){return a*a*a*a};
-TWEEN.Easing.Quartic.EaseOut=function(a){return-(--a*a*a*a-1)};TWEEN.Easing.Quartic.EaseInOut=function(a){if((a*=2)<1)return 0.5*a*a*a*a;return-0.5*((a-=2)*a*a*a-2)};TWEEN.Easing.Quintic.EaseIn=function(a){return a*a*a*a*a};TWEEN.Easing.Quintic.EaseOut=function(a){return(a-=1)*a*a*a*a+1};TWEEN.Easing.Quintic.EaseInOut=function(a){if((a*=2)<1)return 0.5*a*a*a*a*a;return 0.5*((a-=2)*a*a*a*a+2)};TWEEN.Easing.Sinusoidal.EaseIn=function(a){return-Math.cos(a*Math.PI/2)+1};
-TWEEN.Easing.Sinusoidal.EaseOut=function(a){return Math.sin(a*Math.PI/2)};TWEEN.Easing.Sinusoidal.EaseInOut=function(a){return-0.5*(Math.cos(Math.PI*a)-1)};TWEEN.Easing.Exponential.EaseIn=function(a){return a==0?0:Math.pow(2,10*(a-1))};TWEEN.Easing.Exponential.EaseOut=function(a){return a==1?1:-Math.pow(2,-10*a)+1};TWEEN.Easing.Exponential.EaseInOut=function(a){if(a==0)return 0;if(a==1)return 1;if((a*=2)<1)return 0.5*Math.pow(2,10*(a-1));return 0.5*(-Math.pow(2,-10*(a-1))+2)};
-TWEEN.Easing.Circular.EaseIn=function(a){return-(Math.sqrt(1-a*a)-1)};TWEEN.Easing.Circular.EaseOut=function(a){return Math.sqrt(1- --a*a)};TWEEN.Easing.Circular.EaseInOut=function(a){if((a/=0.5)<1)return-0.5*(Math.sqrt(1-a*a)-1);return 0.5*(Math.sqrt(1-(a-=2)*a)+1)};TWEEN.Easing.Elastic.EaseIn=function(a){var e,c=0.1,d=0.4;if(a==0)return 0;if(a==1)return 1;d||(d=0.3);if(!c||c<1){c=1;e=d/4}else e=d/(2*Math.PI)*Math.asin(1/c);return-(c*Math.pow(2,10*(a-=1))*Math.sin((a-e)*2*Math.PI/d))};
-TWEEN.Easing.Elastic.EaseOut=function(a){var e,c=0.1,d=0.4;if(a==0)return 0;if(a==1)return 1;d||(d=0.3);if(!c||c<1){c=1;e=d/4}else e=d/(2*Math.PI)*Math.asin(1/c);return c*Math.pow(2,-10*a)*Math.sin((a-e)*2*Math.PI/d)+1};
-TWEEN.Easing.Elastic.EaseInOut=function(a){var e,c=0.1,d=0.4;if(a==0)return 0;if(a==1)return 1;d||(d=0.3);if(!c||c<1){c=1;e=d/4}else e=d/(2*Math.PI)*Math.asin(1/c);if((a*=2)<1)return-0.5*c*Math.pow(2,10*(a-=1))*Math.sin((a-e)*2*Math.PI/d);return c*Math.pow(2,-10*(a-=1))*Math.sin((a-e)*2*Math.PI/d)*0.5+1};TWEEN.Easing.Back.EaseIn=function(a){return a*a*(2.70158*a-1.70158)};TWEEN.Easing.Back.EaseOut=function(a){return(a-=1)*a*(2.70158*a+1.70158)+1};
-TWEEN.Easing.Back.EaseInOut=function(a){if((a*=2)<1)return 0.5*a*a*(3.5949095*a-2.5949095);return 0.5*((a-=2)*a*(3.5949095*a+2.5949095)+2)};TWEEN.Easing.Bounce.EaseIn=function(a){return 1-TWEEN.Easing.Bounce.EaseOut(1-a)};TWEEN.Easing.Bounce.EaseOut=function(a){return(a/=1)<1/2.75?7.5625*a*a:a<2/2.75?7.5625*(a-=1.5/2.75)*a+0.75:a<2.5/2.75?7.5625*(a-=2.25/2.75)*a+0.9375:7.5625*(a-=2.625/2.75)*a+0.984375};
-TWEEN.Easing.Bounce.EaseInOut=function(a){if(a<0.5)return TWEEN.Easing.Bounce.EaseIn(a*2)*0.5;return TWEEN.Easing.Bounce.EaseOut(a*2-1)*0.5+0.5};
diff --git a/plugins/Sidebar/media_globe/all.js b/plugins/Sidebar/media_globe/all.js
deleted file mode 100644
index f7a9c4eb..00000000
--- a/plugins/Sidebar/media_globe/all.js
+++ /dev/null
@@ -1,1345 +0,0 @@
-
-
-/* ---- plugins/Sidebar/media_globe/Detector.js ---- */
-
-
-/**
- * @author alteredq / http://alteredqualia.com/
- * @author mr.doob / http://mrdoob.com/
- */
-
-Detector = {
-
-  canvas : !! window.CanvasRenderingContext2D,
-  webgl : ( function () { try { return !! window.WebGLRenderingContext && !! document.createElement( 'canvas' ).getContext( 'experimental-webgl' ); } catch( e ) { return false; } } )(),
-  workers : !! window.Worker,
-  fileapi : window.File && window.FileReader && window.FileList && window.Blob,
-
-  getWebGLErrorMessage : function () {
-
-    var domElement = document.createElement( 'div' );
-
-    domElement.style.fontFamily = 'monospace';
-    domElement.style.fontSize = '13px';
-    domElement.style.textAlign = 'center';
-    domElement.style.background = '#eee';
-    domElement.style.color = '#000';
-    domElement.style.padding = '1em';
-    domElement.style.width = '475px';
-    domElement.style.margin = '5em auto 0';
-
-    if ( ! this.webgl ) {
-
-      domElement.innerHTML = window.WebGLRenderingContext ? [
-        'Sorry, your graphics card doesn\'t support <a href="http://khronos.org/webgl/wiki/Getting_a_WebGL_Implementation">WebGL</a>'
-      ].join( '\n' ) : [
-        'Sorry, your browser doesn\'t support <a href="http://khronos.org/webgl/wiki/Getting_a_WebGL_Implementation">WebGL</a><br/>',
-        'Please try with',
-        '<a href="http://www.google.com/chrome">Chrome</a>, ',
-        '<a href="http://www.mozilla.com/en-US/firefox/new/">Firefox 4</a> or',
-        '<a href="http://nightly.webkit.org/">Webkit Nightly (Mac)</a>'
-      ].join( '\n' );
-
-    }
-
-    return domElement;
-
-  },
-
-  addGetWebGLMessage : function ( parameters ) {
-
-    var parent, id, domElement;
-
-    parameters = parameters || {};
-
-    parent = parameters.parent !== undefined ? parameters.parent : document.body;
-    id = parameters.id !== undefined ? parameters.id : 'oldie';
-
-    domElement = Detector.getWebGLErrorMessage();
-    domElement.id = id;
-
-    parent.appendChild( domElement );
-
-  }
-
-};
-
-
-
-/* ---- plugins/Sidebar/media_globe/Tween.js ---- */
-
-
-// Tween.js - http://github.com/sole/tween.js
-var TWEEN=TWEEN||function(){var a,e,c,d,f=[];return{start:function(g){c=setInterval(this.update,1E3/(g||60))},stop:function(){clearInterval(c)},add:function(g){f.push(g)},remove:function(g){a=f.indexOf(g);a!==-1&&f.splice(a,1)},update:function(){a=0;e=f.length;for(d=(new Date).getTime();a<e;)if(f[a].update(d))a++;else{f.splice(a,1);e--}}}}();
-TWEEN.Tween=function(a){var e={},c={},d={},f=1E3,g=0,j=null,n=TWEEN.Easing.Linear.EaseNone,k=null,l=null,m=null;this.to=function(b,h){if(h!==null)f=h;for(var i in b)if(a[i]!==null)d[i]=b[i];return this};this.start=function(){TWEEN.add(this);j=(new Date).getTime()+g;for(var b in d)if(a[b]!==null){e[b]=a[b];c[b]=d[b]-a[b]}return this};this.stop=function(){TWEEN.remove(this);return this};this.delay=function(b){g=b;return this};this.easing=function(b){n=b;return this};this.chain=function(b){k=b};this.onUpdate=
-function(b){l=b;return this};this.onComplete=function(b){m=b;return this};this.update=function(b){var h,i;if(b<j)return true;b=(b-j)/f;b=b>1?1:b;i=n(b);for(h in c)a[h]=e[h]+c[h]*i;l!==null&&l.call(a,i);if(b==1){m!==null&&m.call(a);k!==null&&k.start();return false}return true}};TWEEN.Easing={Linear:{},Quadratic:{},Cubic:{},Quartic:{},Quintic:{},Sinusoidal:{},Exponential:{},Circular:{},Elastic:{},Back:{},Bounce:{}};TWEEN.Easing.Linear.EaseNone=function(a){return a};
-TWEEN.Easing.Quadratic.EaseIn=function(a){return a*a};TWEEN.Easing.Quadratic.EaseOut=function(a){return-a*(a-2)};TWEEN.Easing.Quadratic.EaseInOut=function(a){if((a*=2)<1)return 0.5*a*a;return-0.5*(--a*(a-2)-1)};TWEEN.Easing.Cubic.EaseIn=function(a){return a*a*a};TWEEN.Easing.Cubic.EaseOut=function(a){return--a*a*a+1};TWEEN.Easing.Cubic.EaseInOut=function(a){if((a*=2)<1)return 0.5*a*a*a;return 0.5*((a-=2)*a*a+2)};TWEEN.Easing.Quartic.EaseIn=function(a){return a*a*a*a};
-TWEEN.Easing.Quartic.EaseOut=function(a){return-(--a*a*a*a-1)};TWEEN.Easing.Quartic.EaseInOut=function(a){if((a*=2)<1)return 0.5*a*a*a*a;return-0.5*((a-=2)*a*a*a-2)};TWEEN.Easing.Quintic.EaseIn=function(a){return a*a*a*a*a};TWEEN.Easing.Quintic.EaseOut=function(a){return(a-=1)*a*a*a*a+1};TWEEN.Easing.Quintic.EaseInOut=function(a){if((a*=2)<1)return 0.5*a*a*a*a*a;return 0.5*((a-=2)*a*a*a*a+2)};TWEEN.Easing.Sinusoidal.EaseIn=function(a){return-Math.cos(a*Math.PI/2)+1};
-TWEEN.Easing.Sinusoidal.EaseOut=function(a){return Math.sin(a*Math.PI/2)};TWEEN.Easing.Sinusoidal.EaseInOut=function(a){return-0.5*(Math.cos(Math.PI*a)-1)};TWEEN.Easing.Exponential.EaseIn=function(a){return a==0?0:Math.pow(2,10*(a-1))};TWEEN.Easing.Exponential.EaseOut=function(a){return a==1?1:-Math.pow(2,-10*a)+1};TWEEN.Easing.Exponential.EaseInOut=function(a){if(a==0)return 0;if(a==1)return 1;if((a*=2)<1)return 0.5*Math.pow(2,10*(a-1));return 0.5*(-Math.pow(2,-10*(a-1))+2)};
-TWEEN.Easing.Circular.EaseIn=function(a){return-(Math.sqrt(1-a*a)-1)};TWEEN.Easing.Circular.EaseOut=function(a){return Math.sqrt(1- --a*a)};TWEEN.Easing.Circular.EaseInOut=function(a){if((a/=0.5)<1)return-0.5*(Math.sqrt(1-a*a)-1);return 0.5*(Math.sqrt(1-(a-=2)*a)+1)};TWEEN.Easing.Elastic.EaseIn=function(a){var e,c=0.1,d=0.4;if(a==0)return 0;if(a==1)return 1;d||(d=0.3);if(!c||c<1){c=1;e=d/4}else e=d/(2*Math.PI)*Math.asin(1/c);return-(c*Math.pow(2,10*(a-=1))*Math.sin((a-e)*2*Math.PI/d))};
-TWEEN.Easing.Elastic.EaseOut=function(a){var e,c=0.1,d=0.4;if(a==0)return 0;if(a==1)return 1;d||(d=0.3);if(!c||c<1){c=1;e=d/4}else e=d/(2*Math.PI)*Math.asin(1/c);return c*Math.pow(2,-10*a)*Math.sin((a-e)*2*Math.PI/d)+1};
-TWEEN.Easing.Elastic.EaseInOut=function(a){var e,c=0.1,d=0.4;if(a==0)return 0;if(a==1)return 1;d||(d=0.3);if(!c||c<1){c=1;e=d/4}else e=d/(2*Math.PI)*Math.asin(1/c);if((a*=2)<1)return-0.5*c*Math.pow(2,10*(a-=1))*Math.sin((a-e)*2*Math.PI/d);return c*Math.pow(2,-10*(a-=1))*Math.sin((a-e)*2*Math.PI/d)*0.5+1};TWEEN.Easing.Back.EaseIn=function(a){return a*a*(2.70158*a-1.70158)};TWEEN.Easing.Back.EaseOut=function(a){return(a-=1)*a*(2.70158*a+1.70158)+1};
-TWEEN.Easing.Back.EaseInOut=function(a){if((a*=2)<1)return 0.5*a*a*(3.5949095*a-2.5949095);return 0.5*((a-=2)*a*(3.5949095*a+2.5949095)+2)};TWEEN.Easing.Bounce.EaseIn=function(a){return 1-TWEEN.Easing.Bounce.EaseOut(1-a)};TWEEN.Easing.Bounce.EaseOut=function(a){return(a/=1)<1/2.75?7.5625*a*a:a<2/2.75?7.5625*(a-=1.5/2.75)*a+0.75:a<2.5/2.75?7.5625*(a-=2.25/2.75)*a+0.9375:7.5625*(a-=2.625/2.75)*a+0.984375};
-TWEEN.Easing.Bounce.EaseInOut=function(a){if(a<0.5)return TWEEN.Easing.Bounce.EaseIn(a*2)*0.5;return TWEEN.Easing.Bounce.EaseOut(a*2-1)*0.5+0.5};
-
-
-
-/* ---- plugins/Sidebar/media_globe/globe.js ---- */
-
-
-/**
- * dat.globe Javascript WebGL Globe Toolkit
- * http://dataarts.github.com/dat.globe
- *
- * Copyright 2011 Data Arts Team, Google Creative Lab
- *
- * Licensed under the Apache License, Version 2.0 (the 'License');
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- */
-
-var DAT = DAT || {};
-
-DAT.Globe = function(container, opts) {
-  opts = opts || {};
-
-  var colorFn = opts.colorFn || function(x) {
-    var c = new THREE.Color();
-    c.setHSL( ( 0.5 - (x * 2) ), Math.max(0.8, 1.0 - (x * 3)), 0.5 );
-    return c;
-  };
-  var imgDir = opts.imgDir || '/globe/';
-
-  var Shaders = {
-    'earth' : {
-      uniforms: {
-        'texture': { type: 't', value: null }
-      },
-      vertexShader: [
-        'varying vec3 vNormal;',
-        'varying vec2 vUv;',
-        'void main() {',
-          'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
-          'vNormal = normalize( normalMatrix * normal );',
-          'vUv = uv;',
-        '}'
-      ].join('\n'),
-      fragmentShader: [
-        'uniform sampler2D texture;',
-        'varying vec3 vNormal;',
-        'varying vec2 vUv;',
-        'void main() {',
-          'vec3 diffuse = texture2D( texture, vUv ).xyz;',
-          'float intensity = 1.05 - dot( vNormal, vec3( 0.0, 0.0, 1.0 ) );',
-          'vec3 atmosphere = vec3( 1.0, 1.0, 1.0 ) * pow( intensity, 3.0 );',
-          'gl_FragColor = vec4( diffuse + atmosphere, 1.0 );',
-        '}'
-      ].join('\n')
-    },
-    'atmosphere' : {
-      uniforms: {},
-      vertexShader: [
-        'varying vec3 vNormal;',
-        'void main() {',
-          'vNormal = normalize( normalMatrix * normal );',
-          'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
-        '}'
-      ].join('\n'),
-      fragmentShader: [
-        'varying vec3 vNormal;',
-        'void main() {',
-          'float intensity = pow( 0.8 - dot( vNormal, vec3( 0, 0, 1.0 ) ), 12.0 );',
-          'gl_FragColor = vec4( 1.0, 1.0, 1.0, 1.0 ) * intensity;',
-        '}'
-      ].join('\n')
-    }
-  };
-
-  var camera, scene, renderer, w, h;
-  var mesh, atmosphere, point, running;
-
-  var overRenderer;
-  var running = true;
-
-  var curZoomSpeed = 0;
-  var zoomSpeed = 50;
-
-  var mouse = { x: 0, y: 0 }, mouseOnDown = { x: 0, y: 0 };
-  var rotation = { x: 0, y: 0 },
-      target = { x: Math.PI*3/2, y: Math.PI / 6.0 },
-      targetOnDown = { x: 0, y: 0 };
-
-  var distance = 100000, distanceTarget = 100000;
-  var padding = 10;
-  var PI_HALF = Math.PI / 2;
-
-  function init() {
-
-    container.style.color = '#fff';
-    container.style.font = '13px/20px Arial, sans-serif';
-
-    var shader, uniforms, material;
-    w = container.offsetWidth || window.innerWidth;
-    h = container.offsetHeight || window.innerHeight;
-
-    camera = new THREE.PerspectiveCamera(30, w / h, 1, 10000);
-    camera.position.z = distance;
-
-    scene = new THREE.Scene();
-
-    var geometry = new THREE.SphereGeometry(200, 40, 30);
-
-    shader = Shaders['earth'];
-    uniforms = THREE.UniformsUtils.clone(shader.uniforms);
-
-    uniforms['texture'].value = THREE.ImageUtils.loadTexture(imgDir+'world.jpg');
-
-    material = new THREE.ShaderMaterial({
-
-          uniforms: uniforms,
-          vertexShader: shader.vertexShader,
-          fragmentShader: shader.fragmentShader
-
-        });
-
-    mesh = new THREE.Mesh(geometry, material);
-    mesh.rotation.y = Math.PI;
-    scene.add(mesh);
-
-    shader = Shaders['atmosphere'];
-    uniforms = THREE.UniformsUtils.clone(shader.uniforms);
-
-    material = new THREE.ShaderMaterial({
-
-          uniforms: uniforms,
-          vertexShader: shader.vertexShader,
-          fragmentShader: shader.fragmentShader,
-          side: THREE.BackSide,
-          blending: THREE.AdditiveBlending,
-          transparent: true
-
-        });
-
-    mesh = new THREE.Mesh(geometry, material);
-    mesh.scale.set( 1.1, 1.1, 1.1 );
-    scene.add(mesh);
-
-    geometry = new THREE.BoxGeometry(2.75, 2.75, 1);
-    geometry.applyMatrix(new THREE.Matrix4().makeTranslation(0,0,-0.5));
-
-    point = new THREE.Mesh(geometry);
-
-    renderer = new THREE.WebGLRenderer({antialias: true});
-    renderer.setSize(w, h);
-    renderer.setClearColor( 0x212121, 1 );
-
-    renderer.domElement.style.position = 'relative';
-
-    container.appendChild(renderer.domElement);
-
-    container.addEventListener('mousedown', onMouseDown, false);
-
-    if ('onwheel' in document) {
-      container.addEventListener('wheel', onMouseWheel, false);
-    } else {
-      container.addEventListener('mousewheel', onMouseWheel, false);
-    }
-
-    document.addEventListener('keydown', onDocumentKeyDown, false);
-
-    window.addEventListener('resize', onWindowResize, false);
-
-    container.addEventListener('mouseover', function() {
-      overRenderer = true;
-    }, false);
-
-    container.addEventListener('mouseout', function() {
-      overRenderer = false;
-    }, false);
-  }
-
-  function addData(data, opts) {
-    var lat, lng, size, color, i, step, colorFnWrapper;
-
-    opts.animated = opts.animated || false;
-    this.is_animated = opts.animated;
-    opts.format = opts.format || 'magnitude'; // other option is 'legend'
-    if (opts.format === 'magnitude') {
-      step = 3;
-      colorFnWrapper = function(data, i) { return colorFn(data[i+2]); }
-    } else if (opts.format === 'legend') {
-      step = 4;
-      colorFnWrapper = function(data, i) { return colorFn(data[i+3]); }
-    } else if (opts.format === 'peer') {
-      colorFnWrapper = function(data, i) { return colorFn(data[i+2]); }
-    } else {
-      throw('error: format not supported: '+opts.format);
-    }
-
-    if (opts.animated) {
-      if (this._baseGeometry === undefined) {
-        this._baseGeometry = new THREE.Geometry();
-        for (i = 0; i < data.length; i += step) {
-          lat = data[i];
-          lng = data[i + 1];
-//        size = data[i + 2];
-          color = colorFnWrapper(data,i);
-          size = 0;
-          addPoint(lat, lng, size, color, this._baseGeometry);
-        }
-      }
-      if(this._morphTargetId === undefined) {
-        this._morphTargetId = 0;
-      } else {
-        this._morphTargetId += 1;
-      }
-      opts.name = opts.name || 'morphTarget'+this._morphTargetId;
-    }
-    var subgeo = new THREE.Geometry();
-    for (i = 0; i < data.length; i += step) {
-      lat = data[i];
-      lng = data[i + 1];
-      color = colorFnWrapper(data,i);
-      size = data[i + 2];
-      size = size*200;
-      addPoint(lat, lng, size, color, subgeo);
-    }
-    if (opts.animated) {
-      this._baseGeometry.morphTargets.push({'name': opts.name, vertices: subgeo.vertices});
-    } else {
-      this._baseGeometry = subgeo;
-    }
-
-  };
-
-  function createPoints() {
-    if (this._baseGeometry !== undefined) {
-      if (this.is_animated === false) {
-        this.points = new THREE.Mesh(this._baseGeometry, new THREE.MeshBasicMaterial({
-              color: 0xffffff,
-              vertexColors: THREE.FaceColors,
-              morphTargets: false
-            }));
-      } else {
-        if (this._baseGeometry.morphTargets.length < 8) {
-          console.log('t l',this._baseGeometry.morphTargets.length);
-          var padding = 8-this._baseGeometry.morphTargets.length;
-          console.log('padding', padding);
-          for(var i=0; i<=padding; i++) {
-            console.log('padding',i);
-            this._baseGeometry.morphTargets.push({'name': 'morphPadding'+i, vertices: this._baseGeometry.vertices});
-          }
-        }
-        this.points = new THREE.Mesh(this._baseGeometry, new THREE.MeshBasicMaterial({
-              color: 0xffffff,
-              vertexColors: THREE.FaceColors,
-              morphTargets: true
-            }));
-      }
-      scene.add(this.points);
-    }
-  }
-
-  function addPoint(lat, lng, size, color, subgeo) {
-
-    var phi = (90 - lat) * Math.PI / 180;
-    var theta = (180 - lng) * Math.PI / 180;
-
-    point.position.x = 200 * Math.sin(phi) * Math.cos(theta);
-    point.position.y = 200 * Math.cos(phi);
-    point.position.z = 200 * Math.sin(phi) * Math.sin(theta);
-
-    point.lookAt(mesh.position);
-
-    point.scale.z = Math.max( size, 0.1 ); // avoid non-invertible matrix
-    point.updateMatrix();
-
-    for (var i = 0; i < point.geometry.faces.length; i++) {
-
-      point.geometry.faces[i].color = color;
-
-    }
-    if(point.matrixAutoUpdate){
-      point.updateMatrix();
-    }
-    subgeo.merge(point.geometry, point.matrix);
-  }
-
-  function onMouseDown(event) {
-    event.preventDefault();
-
-    container.addEventListener('mousemove', onMouseMove, false);
-    container.addEventListener('mouseup', onMouseUp, false);
-    container.addEventListener('mouseout', onMouseOut, false);
-
-    mouseOnDown.x = - event.clientX;
-    mouseOnDown.y = event.clientY;
-
-    targetOnDown.x = target.x;
-    targetOnDown.y = target.y;
-
-    container.style.cursor = 'move';
-  }
-
-  function onMouseMove(event) {
-    mouse.x = - event.clientX;
-    mouse.y = event.clientY;
-
-    var zoomDamp = distance/1000;
-
-    target.x = targetOnDown.x + (mouse.x - mouseOnDown.x) * 0.005 * zoomDamp;
-    target.y = targetOnDown.y + (mouse.y - mouseOnDown.y) * 0.005 * zoomDamp;
-
-    target.y = target.y > PI_HALF ? PI_HALF : target.y;
-    target.y = target.y < - PI_HALF ? - PI_HALF : target.y;
-  }
-
-  function onMouseUp(event) {
-    container.removeEventListener('mousemove', onMouseMove, false);
-    container.removeEventListener('mouseup', onMouseUp, false);
-    container.removeEventListener('mouseout', onMouseOut, false);
-    container.style.cursor = 'auto';
-  }
-
-  function onMouseOut(event) {
-    container.removeEventListener('mousemove', onMouseMove, false);
-    container.removeEventListener('mouseup', onMouseUp, false);
-    container.removeEventListener('mouseout', onMouseOut, false);
-  }
-
-  function onMouseWheel(event) {
-    if (container.style.cursor != "move") return false;
-    event.preventDefault();
-    if (overRenderer) {
-      if (event.deltaY) {
-        zoom(-event.deltaY * (event.deltaMode == 0 ? 1 : 50));
-      } else {
-        zoom(event.wheelDeltaY * 0.3);
-      }
-    }
-    return false;
-  }
-
-  function onDocumentKeyDown(event) {
-    switch (event.keyCode) {
-      case 38:
-        zoom(100);
-        event.preventDefault();
-        break;
-      case 40:
-        zoom(-100);
-        event.preventDefault();
-        break;
-    }
-  }
-
-  function onWindowResize( event ) {
-    camera.aspect = container.offsetWidth / container.offsetHeight;
-    camera.updateProjectionMatrix();
-    renderer.setSize( container.offsetWidth, container.offsetHeight );
-  }
-
-  function zoom(delta) {
-    distanceTarget -= delta;
-    distanceTarget = distanceTarget > 855 ? 855 : distanceTarget;
-    distanceTarget = distanceTarget < 350 ? 350 : distanceTarget;
-  }
-
-  function animate() {
-    if (!running) return
-    requestAnimationFrame(animate);
-    render();
-  }
-
-  function render() {
-    zoom(curZoomSpeed);
-
-    rotation.x += (target.x - rotation.x) * 0.1;
-    rotation.y += (target.y - rotation.y) * 0.1;
-    distance += (distanceTarget - distance) * 0.3;
-
-    camera.position.x = distance * Math.sin(rotation.x) * Math.cos(rotation.y);
-    camera.position.y = distance * Math.sin(rotation.y);
-    camera.position.z = distance * Math.cos(rotation.x) * Math.cos(rotation.y);
-
-    camera.lookAt(mesh.position);
-
-    renderer.render(scene, camera);
-  }
-
-  function unload() {
-    running = false
-    container.removeEventListener('mousedown', onMouseDown, false);
-    if ('onwheel' in document) {
-      container.removeEventListener('wheel', onMouseWheel, false);
-    } else {
-      container.removeEventListener('mousewheel', onMouseWheel, false);
-    }
-    document.removeEventListener('keydown', onDocumentKeyDown, false);
-    window.removeEventListener('resize', onWindowResize, false);
-
-  }
-
-  init();
-  this.animate = animate;
-  this.unload = unload;
-
-
-  this.__defineGetter__('time', function() {
-    return this._time || 0;
-  });
-
-  this.__defineSetter__('time', function(t) {
-    var validMorphs = [];
-    var morphDict = this.points.morphTargetDictionary;
-    for(var k in morphDict) {
-      if(k.indexOf('morphPadding') < 0) {
-        validMorphs.push(morphDict[k]);
-      }
-    }
-    validMorphs.sort();
-    var l = validMorphs.length-1;
-    var scaledt = t*l+1;
-    var index = Math.floor(scaledt);
-    for (i=0;i<validMorphs.length;i++) {
-      this.points.morphTargetInfluences[validMorphs[i]] = 0;
-    }
-    var lastIndex = index - 1;
-    var leftover = scaledt - index;
-    if (lastIndex >= 0) {
-      this.points.morphTargetInfluences[lastIndex] = 1 - leftover;
-    }
-    this.points.morphTargetInfluences[index] = leftover;
-    this._time = t;
-  });
-
-  this.addData = addData;
-  this.createPoints = createPoints;
-  this.renderer = renderer;
-  this.scene = scene;
-
-  return this;
-
-};
-
-
-
-/* ---- plugins/Sidebar/media_globe/three.min.js ---- */
-
-
-// threejs.org/license
-'use strict';var THREE={REVISION:"69"};"object"===typeof module&&(module.exports=THREE);void 0===Math.sign&&(Math.sign=function(a){return 0>a?-1:0<a?1:0});THREE.MOUSE={LEFT:0,MIDDLE:1,RIGHT:2};THREE.CullFaceNone=0;THREE.CullFaceBack=1;THREE.CullFaceFront=2;THREE.CullFaceFrontBack=3;THREE.FrontFaceDirectionCW=0;THREE.FrontFaceDirectionCCW=1;THREE.BasicShadowMap=0;THREE.PCFShadowMap=1;THREE.PCFSoftShadowMap=2;THREE.FrontSide=0;THREE.BackSide=1;THREE.DoubleSide=2;THREE.NoShading=0;
-THREE.FlatShading=1;THREE.SmoothShading=2;THREE.NoColors=0;THREE.FaceColors=1;THREE.VertexColors=2;THREE.NoBlending=0;THREE.NormalBlending=1;THREE.AdditiveBlending=2;THREE.SubtractiveBlending=3;THREE.MultiplyBlending=4;THREE.CustomBlending=5;THREE.AddEquation=100;THREE.SubtractEquation=101;THREE.ReverseSubtractEquation=102;THREE.MinEquation=103;THREE.MaxEquation=104;THREE.ZeroFactor=200;THREE.OneFactor=201;THREE.SrcColorFactor=202;THREE.OneMinusSrcColorFactor=203;THREE.SrcAlphaFactor=204;
-THREE.OneMinusSrcAlphaFactor=205;THREE.DstAlphaFactor=206;THREE.OneMinusDstAlphaFactor=207;THREE.DstColorFactor=208;THREE.OneMinusDstColorFactor=209;THREE.SrcAlphaSaturateFactor=210;THREE.MultiplyOperation=0;THREE.MixOperation=1;THREE.AddOperation=2;THREE.UVMapping=function(){};THREE.CubeReflectionMapping=function(){};THREE.CubeRefractionMapping=function(){};THREE.SphericalReflectionMapping=function(){};THREE.SphericalRefractionMapping=function(){};THREE.RepeatWrapping=1E3;
-THREE.ClampToEdgeWrapping=1001;THREE.MirroredRepeatWrapping=1002;THREE.NearestFilter=1003;THREE.NearestMipMapNearestFilter=1004;THREE.NearestMipMapLinearFilter=1005;THREE.LinearFilter=1006;THREE.LinearMipMapNearestFilter=1007;THREE.LinearMipMapLinearFilter=1008;THREE.UnsignedByteType=1009;THREE.ByteType=1010;THREE.ShortType=1011;THREE.UnsignedShortType=1012;THREE.IntType=1013;THREE.UnsignedIntType=1014;THREE.FloatType=1015;THREE.UnsignedShort4444Type=1016;THREE.UnsignedShort5551Type=1017;
-THREE.UnsignedShort565Type=1018;THREE.AlphaFormat=1019;THREE.RGBFormat=1020;THREE.RGBAFormat=1021;THREE.LuminanceFormat=1022;THREE.LuminanceAlphaFormat=1023;THREE.RGB_S3TC_DXT1_Format=2001;THREE.RGBA_S3TC_DXT1_Format=2002;THREE.RGBA_S3TC_DXT3_Format=2003;THREE.RGBA_S3TC_DXT5_Format=2004;THREE.RGB_PVRTC_4BPPV1_Format=2100;THREE.RGB_PVRTC_2BPPV1_Format=2101;THREE.RGBA_PVRTC_4BPPV1_Format=2102;THREE.RGBA_PVRTC_2BPPV1_Format=2103;
-THREE.Color=function(a){return 3===arguments.length?this.setRGB(arguments[0],arguments[1],arguments[2]):this.set(a)};
-THREE.Color.prototype={constructor:THREE.Color,r:1,g:1,b:1,set:function(a){a instanceof THREE.Color?this.copy(a):"number"===typeof a?this.setHex(a):"string"===typeof a&&this.setStyle(a);return this},setHex:function(a){a=Math.floor(a);this.r=(a>>16&255)/255;this.g=(a>>8&255)/255;this.b=(a&255)/255;return this},setRGB:function(a,b,c){this.r=a;this.g=b;this.b=c;return this},setHSL:function(a,b,c){if(0===b)this.r=this.g=this.b=c;else{var d=function(a,b,c){0>c&&(c+=1);1<c&&(c-=1);return c<1/6?a+6*(b-a)*
-c:.5>c?b:c<2/3?a+6*(b-a)*(2/3-c):a};b=.5>=c?c*(1+b):c+b-c*b;c=2*c-b;this.r=d(c,b,a+1/3);this.g=d(c,b,a);this.b=d(c,b,a-1/3)}return this},setStyle:function(a){if(/^rgb\((\d+), ?(\d+), ?(\d+)\)$/i.test(a))return a=/^rgb\((\d+), ?(\d+), ?(\d+)\)$/i.exec(a),this.r=Math.min(255,parseInt(a[1],10))/255,this.g=Math.min(255,parseInt(a[2],10))/255,this.b=Math.min(255,parseInt(a[3],10))/255,this;if(/^rgb\((\d+)\%, ?(\d+)\%, ?(\d+)\%\)$/i.test(a))return a=/^rgb\((\d+)\%, ?(\d+)\%, ?(\d+)\%\)$/i.exec(a),this.r=
-Math.min(100,parseInt(a[1],10))/100,this.g=Math.min(100,parseInt(a[2],10))/100,this.b=Math.min(100,parseInt(a[3],10))/100,this;if(/^\#([0-9a-f]{6})$/i.test(a))return a=/^\#([0-9a-f]{6})$/i.exec(a),this.setHex(parseInt(a[1],16)),this;if(/^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.test(a))return a=/^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(a),this.setHex(parseInt(a[1]+a[1]+a[2]+a[2]+a[3]+a[3],16)),this;if(/^(\w+)$/i.test(a))return this.setHex(THREE.ColorKeywords[a]),this},copy:function(a){this.r=a.r;this.g=
-a.g;this.b=a.b;return this},copyGammaToLinear:function(a){this.r=a.r*a.r;this.g=a.g*a.g;this.b=a.b*a.b;return this},copyLinearToGamma:function(a){this.r=Math.sqrt(a.r);this.g=Math.sqrt(a.g);this.b=Math.sqrt(a.b);return this},convertGammaToLinear:function(){var a=this.r,b=this.g,c=this.b;this.r=a*a;this.g=b*b;this.b=c*c;return this},convertLinearToGamma:function(){this.r=Math.sqrt(this.r);this.g=Math.sqrt(this.g);this.b=Math.sqrt(this.b);return this},getHex:function(){return 255*this.r<<16^255*this.g<<
-8^255*this.b<<0},getHexString:function(){return("000000"+this.getHex().toString(16)).slice(-6)},getHSL:function(a){a=a||{h:0,s:0,l:0};var b=this.r,c=this.g,d=this.b,e=Math.max(b,c,d),f=Math.min(b,c,d),g,h=(f+e)/2;if(f===e)f=g=0;else{var k=e-f,f=.5>=h?k/(e+f):k/(2-e-f);switch(e){case b:g=(c-d)/k+(c<d?6:0);break;case c:g=(d-b)/k+2;break;case d:g=(b-c)/k+4}g/=6}a.h=g;a.s=f;a.l=h;return a},getStyle:function(){return"rgb("+(255*this.r|0)+","+(255*this.g|0)+","+(255*this.b|0)+")"},offsetHSL:function(a,
-b,c){var d=this.getHSL();d.h+=a;d.s+=b;d.l+=c;this.setHSL(d.h,d.s,d.l);return this},add:function(a){this.r+=a.r;this.g+=a.g;this.b+=a.b;return this},addColors:function(a,b){this.r=a.r+b.r;this.g=a.g+b.g;this.b=a.b+b.b;return this},addScalar:function(a){this.r+=a;this.g+=a;this.b+=a;return this},multiply:function(a){this.r*=a.r;this.g*=a.g;this.b*=a.b;return this},multiplyScalar:function(a){this.r*=a;this.g*=a;this.b*=a;return this},lerp:function(a,b){this.r+=(a.r-this.r)*b;this.g+=(a.g-this.g)*b;
-this.b+=(a.b-this.b)*b;return this},equals:function(a){return a.r===this.r&&a.g===this.g&&a.b===this.b},fromArray:function(a){this.r=a[0];this.g=a[1];this.b=a[2];return this},toArray:function(){return[this.r,this.g,this.b]},clone:function(){return(new THREE.Color).setRGB(this.r,this.g,this.b)}};
-THREE.ColorKeywords={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,
-darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,
-grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,
-lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,
-palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,
-tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};THREE.Quaternion=function(a,b,c,d){this._x=a||0;this._y=b||0;this._z=c||0;this._w=void 0!==d?d:1};
-THREE.Quaternion.prototype={constructor:THREE.Quaternion,_x:0,_y:0,_z:0,_w:0,get x(){return this._x},set x(a){this._x=a;this.onChangeCallback()},get y(){return this._y},set y(a){this._y=a;this.onChangeCallback()},get z(){return this._z},set z(a){this._z=a;this.onChangeCallback()},get w(){return this._w},set w(a){this._w=a;this.onChangeCallback()},set:function(a,b,c,d){this._x=a;this._y=b;this._z=c;this._w=d;this.onChangeCallback();return this},copy:function(a){this._x=a.x;this._y=a.y;this._z=a.z;
-this._w=a.w;this.onChangeCallback();return this},setFromEuler:function(a,b){if(!1===a instanceof THREE.Euler)throw Error("THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.");var c=Math.cos(a._x/2),d=Math.cos(a._y/2),e=Math.cos(a._z/2),f=Math.sin(a._x/2),g=Math.sin(a._y/2),h=Math.sin(a._z/2);"XYZ"===a.order?(this._x=f*d*e+c*g*h,this._y=c*g*e-f*d*h,this._z=c*d*h+f*g*e,this._w=c*d*e-f*g*h):"YXZ"===a.order?(this._x=f*d*e+c*g*h,this._y=c*g*e-f*d*h,this._z=
-c*d*h-f*g*e,this._w=c*d*e+f*g*h):"ZXY"===a.order?(this._x=f*d*e-c*g*h,this._y=c*g*e+f*d*h,this._z=c*d*h+f*g*e,this._w=c*d*e-f*g*h):"ZYX"===a.order?(this._x=f*d*e-c*g*h,this._y=c*g*e+f*d*h,this._z=c*d*h-f*g*e,this._w=c*d*e+f*g*h):"YZX"===a.order?(this._x=f*d*e+c*g*h,this._y=c*g*e+f*d*h,this._z=c*d*h-f*g*e,this._w=c*d*e-f*g*h):"XZY"===a.order&&(this._x=f*d*e-c*g*h,this._y=c*g*e-f*d*h,this._z=c*d*h+f*g*e,this._w=c*d*e+f*g*h);if(!1!==b)this.onChangeCallback();return this},setFromAxisAngle:function(a,
-b){var c=b/2,d=Math.sin(c);this._x=a.x*d;this._y=a.y*d;this._z=a.z*d;this._w=Math.cos(c);this.onChangeCallback();return this},setFromRotationMatrix:function(a){var b=a.elements,c=b[0];a=b[4];var d=b[8],e=b[1],f=b[5],g=b[9],h=b[2],k=b[6],b=b[10],n=c+f+b;0<n?(c=.5/Math.sqrt(n+1),this._w=.25/c,this._x=(k-g)*c,this._y=(d-h)*c,this._z=(e-a)*c):c>f&&c>b?(c=2*Math.sqrt(1+c-f-b),this._w=(k-g)/c,this._x=.25*c,this._y=(a+e)/c,this._z=(d+h)/c):f>b?(c=2*Math.sqrt(1+f-c-b),this._w=(d-h)/c,this._x=(a+e)/c,this._y=
-.25*c,this._z=(g+k)/c):(c=2*Math.sqrt(1+b-c-f),this._w=(e-a)/c,this._x=(d+h)/c,this._y=(g+k)/c,this._z=.25*c);this.onChangeCallback();return this},setFromUnitVectors:function(){var a,b;return function(c,d){void 0===a&&(a=new THREE.Vector3);b=c.dot(d)+1;1E-6>b?(b=0,Math.abs(c.x)>Math.abs(c.z)?a.set(-c.y,c.x,0):a.set(0,-c.z,c.y)):a.crossVectors(c,d);this._x=a.x;this._y=a.y;this._z=a.z;this._w=b;this.normalize();return this}}(),inverse:function(){this.conjugate().normalize();return this},conjugate:function(){this._x*=
--1;this._y*=-1;this._z*=-1;this.onChangeCallback();return this},dot:function(a){return this._x*a._x+this._y*a._y+this._z*a._z+this._w*a._w},lengthSq:function(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w},length:function(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)},normalize:function(){var a=this.length();0===a?(this._z=this._y=this._x=0,this._w=1):(a=1/a,this._x*=a,this._y*=a,this._z*=a,this._w*=a);this.onChangeCallback();return this},
-multiply:function(a,b){return void 0!==b?(console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."),this.multiplyQuaternions(a,b)):this.multiplyQuaternions(this,a)},multiplyQuaternions:function(a,b){var c=a._x,d=a._y,e=a._z,f=a._w,g=b._x,h=b._y,k=b._z,n=b._w;this._x=c*n+f*g+d*k-e*h;this._y=d*n+f*h+e*g-c*k;this._z=e*n+f*k+c*h-d*g;this._w=f*n-c*g-d*h-e*k;this.onChangeCallback();return this},multiplyVector3:function(a){console.warn("THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.");
-return a.applyQuaternion(this)},slerp:function(a,b){if(0===b)return this;if(1===b)return this.copy(a);var c=this._x,d=this._y,e=this._z,f=this._w,g=f*a._w+c*a._x+d*a._y+e*a._z;0>g?(this._w=-a._w,this._x=-a._x,this._y=-a._y,this._z=-a._z,g=-g):this.copy(a);if(1<=g)return this._w=f,this._x=c,this._y=d,this._z=e,this;var h=Math.acos(g),k=Math.sqrt(1-g*g);if(.001>Math.abs(k))return this._w=.5*(f+this._w),this._x=.5*(c+this._x),this._y=.5*(d+this._y),this._z=.5*(e+this._z),this;g=Math.sin((1-b)*h)/k;h=
-Math.sin(b*h)/k;this._w=f*g+this._w*h;this._x=c*g+this._x*h;this._y=d*g+this._y*h;this._z=e*g+this._z*h;this.onChangeCallback();return this},equals:function(a){return a._x===this._x&&a._y===this._y&&a._z===this._z&&a._w===this._w},fromArray:function(a,b){void 0===b&&(b=0);this._x=a[b];this._y=a[b+1];this._z=a[b+2];this._w=a[b+3];this.onChangeCallback();return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this._x;a[b+1]=this._y;a[b+2]=this._z;a[b+3]=this._w;return a},onChange:function(a){this.onChangeCallback=
-a;return this},onChangeCallback:function(){},clone:function(){return new THREE.Quaternion(this._x,this._y,this._z,this._w)}};THREE.Quaternion.slerp=function(a,b,c,d){return c.copy(a).slerp(b,d)};THREE.Vector2=function(a,b){this.x=a||0;this.y=b||0};
-THREE.Vector2.prototype={constructor:THREE.Vector2,set:function(a,b){this.x=a;this.y=b;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;default:throw Error("index is out of range: "+a);}},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y;default:throw Error("index is out of range: "+a);}},copy:function(a){this.x=a.x;this.y=a.y;return this},add:function(a,
-b){if(void 0!==b)return console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;return this},addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;return this},addScalar:function(a){this.x+=a;this.y+=a;return this},sub:function(a,b){if(void 0!==b)return console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(a,b);this.x-=a.x;this.y-=a.y;return this},
-subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;return this},multiply:function(a){this.x*=a.x;this.y*=a.y;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;return this},divide:function(a){this.x/=a.x;this.y/=a.y;return this},divideScalar:function(a){0!==a?(a=1/a,this.x*=a,this.y*=a):this.y=this.x=0;return this},min:function(a){this.x>a.x&&(this.x=a.x);this.y>a.y&&(this.y=a.y);return this},max:function(a){this.x<a.x&&(this.x=a.x);this.y<a.y&&(this.y=a.y);return this},clamp:function(a,
-b){this.x<a.x?this.x=a.x:this.x>b.x&&(this.x=b.x);this.y<a.y?this.y=a.y:this.y>b.y&&(this.y=b.y);return this},clampScalar:function(){var a,b;return function(c,d){void 0===a&&(a=new THREE.Vector2,b=new THREE.Vector2);a.set(c,c);b.set(d,d);return this.clamp(a,b)}}(),floor:function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);return this},ceil:function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);return this},round:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);return this},
-roundToZero:function(){this.x=0>this.x?Math.ceil(this.x):Math.floor(this.x);this.y=0>this.y?Math.ceil(this.y):Math.floor(this.y);return this},negate:function(){this.x=-this.x;this.y=-this.y;return this},dot:function(a){return this.x*a.x+this.y*a.y},lengthSq:function(){return this.x*this.x+this.y*this.y},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},normalize:function(){return this.divideScalar(this.length())},distanceTo:function(a){return Math.sqrt(this.distanceToSquared(a))},distanceToSquared:function(a){var b=
-this.x-a.x;a=this.y-a.y;return b*b+a*a},setLength:function(a){var b=this.length();0!==b&&a!==b&&this.multiplyScalar(a/b);return this},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;return this},equals:function(a){return a.x===this.x&&a.y===this.y},fromArray:function(a,b){void 0===b&&(b=0);this.x=a[b];this.y=a[b+1];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this.x;a[b+1]=this.y;return a},clone:function(){return new THREE.Vector2(this.x,this.y)}};
-THREE.Vector3=function(a,b,c){this.x=a||0;this.y=b||0;this.z=c||0};
-THREE.Vector3.prototype={constructor:THREE.Vector3,set:function(a,b,c){this.x=a;this.y=b;this.z=c;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setZ:function(a){this.z=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;case 2:this.z=b;break;default:throw Error("index is out of range: "+a);}},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw Error("index is out of range: "+
-a);}},copy:function(a){this.x=a.x;this.y=a.y;this.z=a.z;return this},add:function(a,b){if(void 0!==b)return console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;this.z+=a.z;return this},addScalar:function(a){this.x+=a;this.y+=a;this.z+=a;return this},addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;this.z=a.z+b.z;return this},sub:function(a,b){if(void 0!==b)return console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),
-this.subVectors(a,b);this.x-=a.x;this.y-=a.y;this.z-=a.z;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;this.z=a.z-b.z;return this},multiply:function(a,b){if(void 0!==b)return console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."),this.multiplyVectors(a,b);this.x*=a.x;this.y*=a.y;this.z*=a.z;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;this.z*=a;return this},multiplyVectors:function(a,b){this.x=a.x*b.x;this.y=
-a.y*b.y;this.z=a.z*b.z;return this},applyEuler:function(){var a;return function(b){!1===b instanceof THREE.Euler&&console.error("THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order.");void 0===a&&(a=new THREE.Quaternion);this.applyQuaternion(a.setFromEuler(b));return this}}(),applyAxisAngle:function(){var a;return function(b,c){void 0===a&&(a=new THREE.Quaternion);this.applyQuaternion(a.setFromAxisAngle(b,c));return this}}(),applyMatrix3:function(a){var b=this.x,
-c=this.y,d=this.z;a=a.elements;this.x=a[0]*b+a[3]*c+a[6]*d;this.y=a[1]*b+a[4]*c+a[7]*d;this.z=a[2]*b+a[5]*c+a[8]*d;return this},applyMatrix4:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d+a[12];this.y=a[1]*b+a[5]*c+a[9]*d+a[13];this.z=a[2]*b+a[6]*c+a[10]*d+a[14];return this},applyProjection:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;var e=1/(a[3]*b+a[7]*c+a[11]*d+a[15]);this.x=(a[0]*b+a[4]*c+a[8]*d+a[12])*e;this.y=(a[1]*b+a[5]*c+a[9]*d+a[13])*e;this.z=
-(a[2]*b+a[6]*c+a[10]*d+a[14])*e;return this},applyQuaternion:function(a){var b=this.x,c=this.y,d=this.z,e=a.x,f=a.y,g=a.z;a=a.w;var h=a*b+f*d-g*c,k=a*c+g*b-e*d,n=a*d+e*c-f*b,b=-e*b-f*c-g*d;this.x=h*a+b*-e+k*-g-n*-f;this.y=k*a+b*-f+n*-e-h*-g;this.z=n*a+b*-g+h*-f-k*-e;return this},project:function(){var a;return function(b){void 0===a&&(a=new THREE.Matrix4);a.multiplyMatrices(b.projectionMatrix,a.getInverse(b.matrixWorld));return this.applyProjection(a)}}(),unproject:function(){var a;return function(b){void 0===
-a&&(a=new THREE.Matrix4);a.multiplyMatrices(b.matrixWorld,a.getInverse(b.projectionMatrix));return this.applyProjection(a)}}(),transformDirection:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d;this.y=a[1]*b+a[5]*c+a[9]*d;this.z=a[2]*b+a[6]*c+a[10]*d;this.normalize();return this},divide:function(a){this.x/=a.x;this.y/=a.y;this.z/=a.z;return this},divideScalar:function(a){0!==a?(a=1/a,this.x*=a,this.y*=a,this.z*=a):this.z=this.y=this.x=0;return this},min:function(a){this.x>
-a.x&&(this.x=a.x);this.y>a.y&&(this.y=a.y);this.z>a.z&&(this.z=a.z);return this},max:function(a){this.x<a.x&&(this.x=a.x);this.y<a.y&&(this.y=a.y);this.z<a.z&&(this.z=a.z);return this},clamp:function(a,b){this.x<a.x?this.x=a.x:this.x>b.x&&(this.x=b.x);this.y<a.y?this.y=a.y:this.y>b.y&&(this.y=b.y);this.z<a.z?this.z=a.z:this.z>b.z&&(this.z=b.z);return this},clampScalar:function(){var a,b;return function(c,d){void 0===a&&(a=new THREE.Vector3,b=new THREE.Vector3);a.set(c,c,c);b.set(d,d,d);return this.clamp(a,
-b)}}(),floor:function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);this.z=Math.floor(this.z);return this},ceil:function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);this.z=Math.ceil(this.z);return this},round:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);this.z=Math.round(this.z);return this},roundToZero:function(){this.x=0>this.x?Math.ceil(this.x):Math.floor(this.x);this.y=0>this.y?Math.ceil(this.y):Math.floor(this.y);this.z=0>this.z?Math.ceil(this.z):Math.floor(this.z);
-return this},negate:function(){this.x=-this.x;this.y=-this.y;this.z=-this.z;return this},dot:function(a){return this.x*a.x+this.y*a.y+this.z*a.z},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)},normalize:function(){return this.divideScalar(this.length())},setLength:function(a){var b=this.length();0!==b&&a!==b&&this.multiplyScalar(a/
-b);return this},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;this.z+=(a.z-this.z)*b;return this},cross:function(a,b){if(void 0!==b)return console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."),this.crossVectors(a,b);var c=this.x,d=this.y,e=this.z;this.x=d*a.z-e*a.y;this.y=e*a.x-c*a.z;this.z=c*a.y-d*a.x;return this},crossVectors:function(a,b){var c=a.x,d=a.y,e=a.z,f=b.x,g=b.y,h=b.z;this.x=d*h-e*g;this.y=e*f-c*h;this.z=c*g-d*f;return this},
-projectOnVector:function(){var a,b;return function(c){void 0===a&&(a=new THREE.Vector3);a.copy(c).normalize();b=this.dot(a);return this.copy(a).multiplyScalar(b)}}(),projectOnPlane:function(){var a;return function(b){void 0===a&&(a=new THREE.Vector3);a.copy(this).projectOnVector(b);return this.sub(a)}}(),reflect:function(){var a;return function(b){void 0===a&&(a=new THREE.Vector3);return this.sub(a.copy(b).multiplyScalar(2*this.dot(b)))}}(),angleTo:function(a){a=this.dot(a)/(this.length()*a.length());
-return Math.acos(THREE.Math.clamp(a,-1,1))},distanceTo:function(a){return Math.sqrt(this.distanceToSquared(a))},distanceToSquared:function(a){var b=this.x-a.x,c=this.y-a.y;a=this.z-a.z;return b*b+c*c+a*a},setEulerFromRotationMatrix:function(a,b){console.error("THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.")},setEulerFromQuaternion:function(a,b){console.error("THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.")},
-getPositionFromMatrix:function(a){console.warn("THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().");return this.setFromMatrixPosition(a)},getScaleFromMatrix:function(a){console.warn("THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().");return this.setFromMatrixScale(a)},getColumnFromMatrix:function(a,b){console.warn("THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().");return this.setFromMatrixColumn(a,
-b)},setFromMatrixPosition:function(a){this.x=a.elements[12];this.y=a.elements[13];this.z=a.elements[14];return this},setFromMatrixScale:function(a){var b=this.set(a.elements[0],a.elements[1],a.elements[2]).length(),c=this.set(a.elements[4],a.elements[5],a.elements[6]).length();a=this.set(a.elements[8],a.elements[9],a.elements[10]).length();this.x=b;this.y=c;this.z=a;return this},setFromMatrixColumn:function(a,b){var c=4*a,d=b.elements;this.x=d[c];this.y=d[c+1];this.z=d[c+2];return this},equals:function(a){return a.x===
-this.x&&a.y===this.y&&a.z===this.z},fromArray:function(a,b){void 0===b&&(b=0);this.x=a[b];this.y=a[b+1];this.z=a[b+2];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this.x;a[b+1]=this.y;a[b+2]=this.z;return a},clone:function(){return new THREE.Vector3(this.x,this.y,this.z)}};THREE.Vector4=function(a,b,c,d){this.x=a||0;this.y=b||0;this.z=c||0;this.w=void 0!==d?d:1};
-THREE.Vector4.prototype={constructor:THREE.Vector4,set:function(a,b,c,d){this.x=a;this.y=b;this.z=c;this.w=d;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setZ:function(a){this.z=a;return this},setW:function(a){this.w=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;case 2:this.z=b;break;case 3:this.w=b;break;default:throw Error("index is out of range: "+a);}},getComponent:function(a){switch(a){case 0:return this.x;
-case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw Error("index is out of range: "+a);}},copy:function(a){this.x=a.x;this.y=a.y;this.z=a.z;this.w=void 0!==a.w?a.w:1;return this},add:function(a,b){if(void 0!==b)return console.warn("THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;this.z+=a.z;this.w+=a.w;return this},addScalar:function(a){this.x+=a;this.y+=a;this.z+=a;this.w+=a;return this},
-addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;this.z=a.z+b.z;this.w=a.w+b.w;return this},sub:function(a,b){if(void 0!==b)return console.warn("THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(a,b);this.x-=a.x;this.y-=a.y;this.z-=a.z;this.w-=a.w;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;this.z=a.z-b.z;this.w=a.w-b.w;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;this.z*=a;this.w*=a;return this},applyMatrix4:function(a){var b=
-this.x,c=this.y,d=this.z,e=this.w;a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d+a[12]*e;this.y=a[1]*b+a[5]*c+a[9]*d+a[13]*e;this.z=a[2]*b+a[6]*c+a[10]*d+a[14]*e;this.w=a[3]*b+a[7]*c+a[11]*d+a[15]*e;return this},divideScalar:function(a){0!==a?(a=1/a,this.x*=a,this.y*=a,this.z*=a,this.w*=a):(this.z=this.y=this.x=0,this.w=1);return this},setAxisAngleFromQuaternion:function(a){this.w=2*Math.acos(a.w);var b=Math.sqrt(1-a.w*a.w);1E-4>b?(this.x=1,this.z=this.y=0):(this.x=a.x/b,this.y=a.y/b,this.z=a.z/b);return this},
-setAxisAngleFromRotationMatrix:function(a){var b,c,d;a=a.elements;var e=a[0];d=a[4];var f=a[8],g=a[1],h=a[5],k=a[9];c=a[2];b=a[6];var n=a[10];if(.01>Math.abs(d-g)&&.01>Math.abs(f-c)&&.01>Math.abs(k-b)){if(.1>Math.abs(d+g)&&.1>Math.abs(f+c)&&.1>Math.abs(k+b)&&.1>Math.abs(e+h+n-3))return this.set(1,0,0,0),this;a=Math.PI;e=(e+1)/2;h=(h+1)/2;n=(n+1)/2;d=(d+g)/4;f=(f+c)/4;k=(k+b)/4;e>h&&e>n?.01>e?(b=0,d=c=.707106781):(b=Math.sqrt(e),c=d/b,d=f/b):h>n?.01>h?(b=.707106781,c=0,d=.707106781):(c=Math.sqrt(h),
-b=d/c,d=k/c):.01>n?(c=b=.707106781,d=0):(d=Math.sqrt(n),b=f/d,c=k/d);this.set(b,c,d,a);return this}a=Math.sqrt((b-k)*(b-k)+(f-c)*(f-c)+(g-d)*(g-d));.001>Math.abs(a)&&(a=1);this.x=(b-k)/a;this.y=(f-c)/a;this.z=(g-d)/a;this.w=Math.acos((e+h+n-1)/2);return this},min:function(a){this.x>a.x&&(this.x=a.x);this.y>a.y&&(this.y=a.y);this.z>a.z&&(this.z=a.z);this.w>a.w&&(this.w=a.w);return this},max:function(a){this.x<a.x&&(this.x=a.x);this.y<a.y&&(this.y=a.y);this.z<a.z&&(this.z=a.z);this.w<a.w&&(this.w=a.w);
-return this},clamp:function(a,b){this.x<a.x?this.x=a.x:this.x>b.x&&(this.x=b.x);this.y<a.y?this.y=a.y:this.y>b.y&&(this.y=b.y);this.z<a.z?this.z=a.z:this.z>b.z&&(this.z=b.z);this.w<a.w?this.w=a.w:this.w>b.w&&(this.w=b.w);return this},clampScalar:function(){var a,b;return function(c,d){void 0===a&&(a=new THREE.Vector4,b=new THREE.Vector4);a.set(c,c,c,c);b.set(d,d,d,d);return this.clamp(a,b)}}(),floor:function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);this.z=Math.floor(this.z);this.w=Math.floor(this.w);
-return this},ceil:function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);this.z=Math.ceil(this.z);this.w=Math.ceil(this.w);return this},round:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);this.z=Math.round(this.z);this.w=Math.round(this.w);return this},roundToZero:function(){this.x=0>this.x?Math.ceil(this.x):Math.floor(this.x);this.y=0>this.y?Math.ceil(this.y):Math.floor(this.y);this.z=0>this.z?Math.ceil(this.z):Math.floor(this.z);this.w=0>this.w?Math.ceil(this.w):Math.floor(this.w);
-return this},negate:function(){this.x=-this.x;this.y=-this.y;this.z=-this.z;this.w=-this.w;return this},dot:function(a){return this.x*a.x+this.y*a.y+this.z*a.z+this.w*a.w},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)+Math.abs(this.w)},normalize:function(){return this.divideScalar(this.length())},
-setLength:function(a){var b=this.length();0!==b&&a!==b&&this.multiplyScalar(a/b);return this},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;this.z+=(a.z-this.z)*b;this.w+=(a.w-this.w)*b;return this},equals:function(a){return a.x===this.x&&a.y===this.y&&a.z===this.z&&a.w===this.w},fromArray:function(a,b){void 0===b&&(b=0);this.x=a[b];this.y=a[b+1];this.z=a[b+2];this.w=a[b+3];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this.x;a[b+1]=this.y;a[b+2]=
-this.z;a[b+3]=this.w;return a},clone:function(){return new THREE.Vector4(this.x,this.y,this.z,this.w)}};THREE.Euler=function(a,b,c,d){this._x=a||0;this._y=b||0;this._z=c||0;this._order=d||THREE.Euler.DefaultOrder};THREE.Euler.RotationOrders="XYZ YZX ZXY XZY YXZ ZYX".split(" ");THREE.Euler.DefaultOrder="XYZ";
-THREE.Euler.prototype={constructor:THREE.Euler,_x:0,_y:0,_z:0,_order:THREE.Euler.DefaultOrder,get x(){return this._x},set x(a){this._x=a;this.onChangeCallback()},get y(){return this._y},set y(a){this._y=a;this.onChangeCallback()},get z(){return this._z},set z(a){this._z=a;this.onChangeCallback()},get order(){return this._order},set order(a){this._order=a;this.onChangeCallback()},set:function(a,b,c,d){this._x=a;this._y=b;this._z=c;this._order=d||this._order;this.onChangeCallback();return this},copy:function(a){this._x=
-a._x;this._y=a._y;this._z=a._z;this._order=a._order;this.onChangeCallback();return this},setFromRotationMatrix:function(a,b){var c=THREE.Math.clamp,d=a.elements,e=d[0],f=d[4],g=d[8],h=d[1],k=d[5],n=d[9],p=d[2],q=d[6],d=d[10];b=b||this._order;"XYZ"===b?(this._y=Math.asin(c(g,-1,1)),.99999>Math.abs(g)?(this._x=Math.atan2(-n,d),this._z=Math.atan2(-f,e)):(this._x=Math.atan2(q,k),this._z=0)):"YXZ"===b?(this._x=Math.asin(-c(n,-1,1)),.99999>Math.abs(n)?(this._y=Math.atan2(g,d),this._z=Math.atan2(h,k)):(this._y=
-Math.atan2(-p,e),this._z=0)):"ZXY"===b?(this._x=Math.asin(c(q,-1,1)),.99999>Math.abs(q)?(this._y=Math.atan2(-p,d),this._z=Math.atan2(-f,k)):(this._y=0,this._z=Math.atan2(h,e))):"ZYX"===b?(this._y=Math.asin(-c(p,-1,1)),.99999>Math.abs(p)?(this._x=Math.atan2(q,d),this._z=Math.atan2(h,e)):(this._x=0,this._z=Math.atan2(-f,k))):"YZX"===b?(this._z=Math.asin(c(h,-1,1)),.99999>Math.abs(h)?(this._x=Math.atan2(-n,k),this._y=Math.atan2(-p,e)):(this._x=0,this._y=Math.atan2(g,d))):"XZY"===b?(this._z=Math.asin(-c(f,
--1,1)),.99999>Math.abs(f)?(this._x=Math.atan2(q,k),this._y=Math.atan2(g,e)):(this._x=Math.atan2(-n,d),this._y=0)):console.warn("THREE.Euler: .setFromRotationMatrix() given unsupported order: "+b);this._order=b;this.onChangeCallback();return this},setFromQuaternion:function(a,b,c){var d=THREE.Math.clamp,e=a.x*a.x,f=a.y*a.y,g=a.z*a.z,h=a.w*a.w;b=b||this._order;"XYZ"===b?(this._x=Math.atan2(2*(a.x*a.w-a.y*a.z),h-e-f+g),this._y=Math.asin(d(2*(a.x*a.z+a.y*a.w),-1,1)),this._z=Math.atan2(2*(a.z*a.w-a.x*
-a.y),h+e-f-g)):"YXZ"===b?(this._x=Math.asin(d(2*(a.x*a.w-a.y*a.z),-1,1)),this._y=Math.atan2(2*(a.x*a.z+a.y*a.w),h-e-f+g),this._z=Math.atan2(2*(a.x*a.y+a.z*a.w),h-e+f-g)):"ZXY"===b?(this._x=Math.asin(d(2*(a.x*a.w+a.y*a.z),-1,1)),this._y=Math.atan2(2*(a.y*a.w-a.z*a.x),h-e-f+g),this._z=Math.atan2(2*(a.z*a.w-a.x*a.y),h-e+f-g)):"ZYX"===b?(this._x=Math.atan2(2*(a.x*a.w+a.z*a.y),h-e-f+g),this._y=Math.asin(d(2*(a.y*a.w-a.x*a.z),-1,1)),this._z=Math.atan2(2*(a.x*a.y+a.z*a.w),h+e-f-g)):"YZX"===b?(this._x=Math.atan2(2*
-(a.x*a.w-a.z*a.y),h-e+f-g),this._y=Math.atan2(2*(a.y*a.w-a.x*a.z),h+e-f-g),this._z=Math.asin(d(2*(a.x*a.y+a.z*a.w),-1,1))):"XZY"===b?(this._x=Math.atan2(2*(a.x*a.w+a.y*a.z),h-e+f-g),this._y=Math.atan2(2*(a.x*a.z+a.y*a.w),h+e-f-g),this._z=Math.asin(d(2*(a.z*a.w-a.x*a.y),-1,1))):console.warn("THREE.Euler: .setFromQuaternion() given unsupported order: "+b);this._order=b;if(!1!==c)this.onChangeCallback();return this},reorder:function(){var a=new THREE.Quaternion;return function(b){a.setFromEuler(this);
-this.setFromQuaternion(a,b)}}(),equals:function(a){return a._x===this._x&&a._y===this._y&&a._z===this._z&&a._order===this._order},fromArray:function(a){this._x=a[0];this._y=a[1];this._z=a[2];void 0!==a[3]&&(this._order=a[3]);this.onChangeCallback();return this},toArray:function(){return[this._x,this._y,this._z,this._order]},onChange:function(a){this.onChangeCallback=a;return this},onChangeCallback:function(){},clone:function(){return new THREE.Euler(this._x,this._y,this._z,this._order)}};
-THREE.Line3=function(a,b){this.start=void 0!==a?a:new THREE.Vector3;this.end=void 0!==b?b:new THREE.Vector3};
-THREE.Line3.prototype={constructor:THREE.Line3,set:function(a,b){this.start.copy(a);this.end.copy(b);return this},copy:function(a){this.start.copy(a.start);this.end.copy(a.end);return this},center:function(a){return(a||new THREE.Vector3).addVectors(this.start,this.end).multiplyScalar(.5)},delta:function(a){return(a||new THREE.Vector3).subVectors(this.end,this.start)},distanceSq:function(){return this.start.distanceToSquared(this.end)},distance:function(){return this.start.distanceTo(this.end)},at:function(a,
-b){var c=b||new THREE.Vector3;return this.delta(c).multiplyScalar(a).add(this.start)},closestPointToPointParameter:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c,d){a.subVectors(c,this.start);b.subVectors(this.end,this.start);var e=b.dot(b),e=b.dot(a)/e;d&&(e=THREE.Math.clamp(e,0,1));return e}}(),closestPointToPoint:function(a,b,c){a=this.closestPointToPointParameter(a,b);c=c||new THREE.Vector3;return this.delta(c).multiplyScalar(a).add(this.start)},applyMatrix4:function(a){this.start.applyMatrix4(a);
-this.end.applyMatrix4(a);return this},equals:function(a){return a.start.equals(this.start)&&a.end.equals(this.end)},clone:function(){return(new THREE.Line3).copy(this)}};THREE.Box2=function(a,b){this.min=void 0!==a?a:new THREE.Vector2(Infinity,Infinity);this.max=void 0!==b?b:new THREE.Vector2(-Infinity,-Infinity)};
-THREE.Box2.prototype={constructor:THREE.Box2,set:function(a,b){this.min.copy(a);this.max.copy(b);return this},setFromPoints:function(a){this.makeEmpty();for(var b=0,c=a.length;b<c;b++)this.expandByPoint(a[b]);return this},setFromCenterAndSize:function(){var a=new THREE.Vector2;return function(b,c){var d=a.copy(c).multiplyScalar(.5);this.min.copy(b).sub(d);this.max.copy(b).add(d);return this}}(),copy:function(a){this.min.copy(a.min);this.max.copy(a.max);return this},makeEmpty:function(){this.min.x=
-this.min.y=Infinity;this.max.x=this.max.y=-Infinity;return this},empty:function(){return this.max.x<this.min.x||this.max.y<this.min.y},center:function(a){return(a||new THREE.Vector2).addVectors(this.min,this.max).multiplyScalar(.5)},size:function(a){return(a||new THREE.Vector2).subVectors(this.max,this.min)},expandByPoint:function(a){this.min.min(a);this.max.max(a);return this},expandByVector:function(a){this.min.sub(a);this.max.add(a);return this},expandByScalar:function(a){this.min.addScalar(-a);
-this.max.addScalar(a);return this},containsPoint:function(a){return a.x<this.min.x||a.x>this.max.x||a.y<this.min.y||a.y>this.max.y?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y?!0:!1},getParameter:function(a,b){return(b||new THREE.Vector2).set((a.x-this.min.x)/(this.max.x-this.min.x),(a.y-this.min.y)/(this.max.y-this.min.y))},isIntersectionBox:function(a){return a.max.x<this.min.x||a.min.x>this.max.x||a.max.y<this.min.y||a.min.y>
-this.max.y?!1:!0},clampPoint:function(a,b){return(b||new THREE.Vector2).copy(a).clamp(this.min,this.max)},distanceToPoint:function(){var a=new THREE.Vector2;return function(b){return a.copy(b).clamp(this.min,this.max).sub(b).length()}}(),intersect:function(a){this.min.max(a.min);this.max.min(a.max);return this},union:function(a){this.min.min(a.min);this.max.max(a.max);return this},translate:function(a){this.min.add(a);this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&&
-a.max.equals(this.max)},clone:function(){return(new THREE.Box2).copy(this)}};THREE.Box3=function(a,b){this.min=void 0!==a?a:new THREE.Vector3(Infinity,Infinity,Infinity);this.max=void 0!==b?b:new THREE.Vector3(-Infinity,-Infinity,-Infinity)};
-THREE.Box3.prototype={constructor:THREE.Box3,set:function(a,b){this.min.copy(a);this.max.copy(b);return this},setFromPoints:function(a){this.makeEmpty();for(var b=0,c=a.length;b<c;b++)this.expandByPoint(a[b]);return this},setFromCenterAndSize:function(){var a=new THREE.Vector3;return function(b,c){var d=a.copy(c).multiplyScalar(.5);this.min.copy(b).sub(d);this.max.copy(b).add(d);return this}}(),setFromObject:function(){var a=new THREE.Vector3;return function(b){var c=this;b.updateMatrixWorld(!0);
-this.makeEmpty();b.traverse(function(b){var e=b.geometry;if(void 0!==e)if(e instanceof THREE.Geometry)for(var f=e.vertices,e=0,g=f.length;e<g;e++)a.copy(f[e]),a.applyMatrix4(b.matrixWorld),c.expandByPoint(a);else if(e instanceof THREE.BufferGeometry&&void 0!==e.attributes.position)for(f=e.attributes.position.array,e=0,g=f.length;e<g;e+=3)a.set(f[e],f[e+1],f[e+2]),a.applyMatrix4(b.matrixWorld),c.expandByPoint(a)});return this}}(),copy:function(a){this.min.copy(a.min);this.max.copy(a.max);return this},
-makeEmpty:function(){this.min.x=this.min.y=this.min.z=Infinity;this.max.x=this.max.y=this.max.z=-Infinity;return this},empty:function(){return this.max.x<this.min.x||this.max.y<this.min.y||this.max.z<this.min.z},center:function(a){return(a||new THREE.Vector3).addVectors(this.min,this.max).multiplyScalar(.5)},size:function(a){return(a||new THREE.Vector3).subVectors(this.max,this.min)},expandByPoint:function(a){this.min.min(a);this.max.max(a);return this},expandByVector:function(a){this.min.sub(a);
-this.max.add(a);return this},expandByScalar:function(a){this.min.addScalar(-a);this.max.addScalar(a);return this},containsPoint:function(a){return a.x<this.min.x||a.x>this.max.x||a.y<this.min.y||a.y>this.max.y||a.z<this.min.z||a.z>this.max.z?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y&&this.min.z<=a.min.z&&a.max.z<=this.max.z?!0:!1},getParameter:function(a,b){return(b||new THREE.Vector3).set((a.x-this.min.x)/(this.max.x-
-this.min.x),(a.y-this.min.y)/(this.max.y-this.min.y),(a.z-this.min.z)/(this.max.z-this.min.z))},isIntersectionBox:function(a){return a.max.x<this.min.x||a.min.x>this.max.x||a.max.y<this.min.y||a.min.y>this.max.y||a.max.z<this.min.z||a.min.z>this.max.z?!1:!0},clampPoint:function(a,b){return(b||new THREE.Vector3).copy(a).clamp(this.min,this.max)},distanceToPoint:function(){var a=new THREE.Vector3;return function(b){return a.copy(b).clamp(this.min,this.max).sub(b).length()}}(),getBoundingSphere:function(){var a=
-new THREE.Vector3;return function(b){b=b||new THREE.Sphere;b.center=this.center();b.radius=.5*this.size(a).length();return b}}(),intersect:function(a){this.min.max(a.min);this.max.min(a.max);return this},union:function(a){this.min.min(a.min);this.max.max(a.max);return this},applyMatrix4:function(){var a=[new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3];return function(b){a[0].set(this.min.x,this.min.y,
-this.min.z).applyMatrix4(b);a[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(b);a[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(b);a[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(b);a[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(b);a[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(b);a[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(b);a[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(b);this.makeEmpty();this.setFromPoints(a);return this}}(),translate:function(a){this.min.add(a);
-this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&&a.max.equals(this.max)},clone:function(){return(new THREE.Box3).copy(this)}};THREE.Matrix3=function(){this.elements=new Float32Array([1,0,0,0,1,0,0,0,1]);0<arguments.length&&console.error("THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.")};
-THREE.Matrix3.prototype={constructor:THREE.Matrix3,set:function(a,b,c,d,e,f,g,h,k){var n=this.elements;n[0]=a;n[3]=b;n[6]=c;n[1]=d;n[4]=e;n[7]=f;n[2]=g;n[5]=h;n[8]=k;return this},identity:function(){this.set(1,0,0,0,1,0,0,0,1);return this},copy:function(a){a=a.elements;this.set(a[0],a[3],a[6],a[1],a[4],a[7],a[2],a[5],a[8]);return this},multiplyVector3:function(a){console.warn("THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.");return a.applyMatrix3(this)},
-multiplyVector3Array:function(a){console.warn("THREE.Matrix3: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.");return this.applyToVector3Array(a)},applyToVector3Array:function(){var a=new THREE.Vector3;return function(b,c,d){void 0===c&&(c=0);void 0===d&&(d=b.length);for(var e=0;e<d;e+=3,c+=3)a.x=b[c],a.y=b[c+1],a.z=b[c+2],a.applyMatrix3(this),b[c]=a.x,b[c+1]=a.y,b[c+2]=a.z;return b}}(),multiplyScalar:function(a){var b=this.elements;b[0]*=a;b[3]*=a;b[6]*=
-a;b[1]*=a;b[4]*=a;b[7]*=a;b[2]*=a;b[5]*=a;b[8]*=a;return this},determinant:function(){var a=this.elements,b=a[0],c=a[1],d=a[2],e=a[3],f=a[4],g=a[5],h=a[6],k=a[7],a=a[8];return b*f*a-b*g*k-c*e*a+c*g*h+d*e*k-d*f*h},getInverse:function(a,b){var c=a.elements,d=this.elements;d[0]=c[10]*c[5]-c[6]*c[9];d[1]=-c[10]*c[1]+c[2]*c[9];d[2]=c[6]*c[1]-c[2]*c[5];d[3]=-c[10]*c[4]+c[6]*c[8];d[4]=c[10]*c[0]-c[2]*c[8];d[5]=-c[6]*c[0]+c[2]*c[4];d[6]=c[9]*c[4]-c[5]*c[8];d[7]=-c[9]*c[0]+c[1]*c[8];d[8]=c[5]*c[0]-c[1]*c[4];
-c=c[0]*d[0]+c[1]*d[3]+c[2]*d[6];if(0===c){if(b)throw Error("Matrix3.getInverse(): can't invert matrix, determinant is 0");console.warn("Matrix3.getInverse(): can't invert matrix, determinant is 0");this.identity();return this}this.multiplyScalar(1/c);return this},transpose:function(){var a,b=this.elements;a=b[1];b[1]=b[3];b[3]=a;a=b[2];b[2]=b[6];b[6]=a;a=b[5];b[5]=b[7];b[7]=a;return this},flattenToArrayOffset:function(a,b){var c=this.elements;a[b]=c[0];a[b+1]=c[1];a[b+2]=c[2];a[b+3]=c[3];a[b+4]=c[4];
-a[b+5]=c[5];a[b+6]=c[6];a[b+7]=c[7];a[b+8]=c[8];return a},getNormalMatrix:function(a){this.getInverse(a).transpose();return this},transposeIntoArray:function(a){var b=this.elements;a[0]=b[0];a[1]=b[3];a[2]=b[6];a[3]=b[1];a[4]=b[4];a[5]=b[7];a[6]=b[2];a[7]=b[5];a[8]=b[8];return this},fromArray:function(a){this.elements.set(a);return this},toArray:function(){var a=this.elements;return[a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]]},clone:function(){return(new THREE.Matrix3).fromArray(this.elements)}};
-THREE.Matrix4=function(){this.elements=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);0<arguments.length&&console.error("THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.")};
-THREE.Matrix4.prototype={constructor:THREE.Matrix4,set:function(a,b,c,d,e,f,g,h,k,n,p,q,m,r,t,s){var u=this.elements;u[0]=a;u[4]=b;u[8]=c;u[12]=d;u[1]=e;u[5]=f;u[9]=g;u[13]=h;u[2]=k;u[6]=n;u[10]=p;u[14]=q;u[3]=m;u[7]=r;u[11]=t;u[15]=s;return this},identity:function(){this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1);return this},copy:function(a){this.elements.set(a.elements);return this},extractPosition:function(a){console.warn("THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().");return this.copyPosition(a)},
-copyPosition:function(a){var b=this.elements;a=a.elements;b[12]=a[12];b[13]=a[13];b[14]=a[14];return this},extractRotation:function(){var a=new THREE.Vector3;return function(b){var c=this.elements;b=b.elements;var d=1/a.set(b[0],b[1],b[2]).length(),e=1/a.set(b[4],b[5],b[6]).length(),f=1/a.set(b[8],b[9],b[10]).length();c[0]=b[0]*d;c[1]=b[1]*d;c[2]=b[2]*d;c[4]=b[4]*e;c[5]=b[5]*e;c[6]=b[6]*e;c[8]=b[8]*f;c[9]=b[9]*f;c[10]=b[10]*f;return this}}(),makeRotationFromEuler:function(a){!1===a instanceof THREE.Euler&&
-console.error("THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.");var b=this.elements,c=a.x,d=a.y,e=a.z,f=Math.cos(c),c=Math.sin(c),g=Math.cos(d),d=Math.sin(d),h=Math.cos(e),e=Math.sin(e);if("XYZ"===a.order){a=f*h;var k=f*e,n=c*h,p=c*e;b[0]=g*h;b[4]=-g*e;b[8]=d;b[1]=k+n*d;b[5]=a-p*d;b[9]=-c*g;b[2]=p-a*d;b[6]=n+k*d;b[10]=f*g}else"YXZ"===a.order?(a=g*h,k=g*e,n=d*h,p=d*e,b[0]=a+p*c,b[4]=n*c-k,b[8]=f*d,b[1]=f*e,b[5]=f*h,b[9]=-c,b[2]=k*c-n,b[6]=p+a*c,
-b[10]=f*g):"ZXY"===a.order?(a=g*h,k=g*e,n=d*h,p=d*e,b[0]=a-p*c,b[4]=-f*e,b[8]=n+k*c,b[1]=k+n*c,b[5]=f*h,b[9]=p-a*c,b[2]=-f*d,b[6]=c,b[10]=f*g):"ZYX"===a.order?(a=f*h,k=f*e,n=c*h,p=c*e,b[0]=g*h,b[4]=n*d-k,b[8]=a*d+p,b[1]=g*e,b[5]=p*d+a,b[9]=k*d-n,b[2]=-d,b[6]=c*g,b[10]=f*g):"YZX"===a.order?(a=f*g,k=f*d,n=c*g,p=c*d,b[0]=g*h,b[4]=p-a*e,b[8]=n*e+k,b[1]=e,b[5]=f*h,b[9]=-c*h,b[2]=-d*h,b[6]=k*e+n,b[10]=a-p*e):"XZY"===a.order&&(a=f*g,k=f*d,n=c*g,p=c*d,b[0]=g*h,b[4]=-e,b[8]=d*h,b[1]=a*e+p,b[5]=f*h,b[9]=k*
-e-n,b[2]=n*e-k,b[6]=c*h,b[10]=p*e+a);b[3]=0;b[7]=0;b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return this},setRotationFromQuaternion:function(a){console.warn("THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().");return this.makeRotationFromQuaternion(a)},makeRotationFromQuaternion:function(a){var b=this.elements,c=a.x,d=a.y,e=a.z,f=a.w,g=c+c,h=d+d,k=e+e;a=c*g;var n=c*h,c=c*k,p=d*h,d=d*k,e=e*k,g=f*g,h=f*h,f=f*k;b[0]=1-(p+e);b[4]=n-f;b[8]=c+h;b[1]=n+f;b[5]=1-
-(a+e);b[9]=d-g;b[2]=c-h;b[6]=d+g;b[10]=1-(a+p);b[3]=0;b[7]=0;b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return this},lookAt:function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Vector3;return function(d,e,f){var g=this.elements;c.subVectors(d,e).normalize();0===c.length()&&(c.z=1);a.crossVectors(f,c).normalize();0===a.length()&&(c.x+=1E-4,a.crossVectors(f,c).normalize());b.crossVectors(c,a);g[0]=a.x;g[4]=b.x;g[8]=c.x;g[1]=a.y;g[5]=b.y;g[9]=c.y;g[2]=a.z;g[6]=b.z;g[10]=c.z;return this}}(),
-multiply:function(a,b){return void 0!==b?(console.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."),this.multiplyMatrices(a,b)):this.multiplyMatrices(this,a)},multiplyMatrices:function(a,b){var c=a.elements,d=b.elements,e=this.elements,f=c[0],g=c[4],h=c[8],k=c[12],n=c[1],p=c[5],q=c[9],m=c[13],r=c[2],t=c[6],s=c[10],u=c[14],v=c[3],y=c[7],G=c[11],c=c[15],w=d[0],K=d[4],x=d[8],D=d[12],E=d[1],A=d[5],B=d[9],F=d[13],R=d[2],H=d[6],C=d[10],T=d[14],Q=d[3],
-O=d[7],S=d[11],d=d[15];e[0]=f*w+g*E+h*R+k*Q;e[4]=f*K+g*A+h*H+k*O;e[8]=f*x+g*B+h*C+k*S;e[12]=f*D+g*F+h*T+k*d;e[1]=n*w+p*E+q*R+m*Q;e[5]=n*K+p*A+q*H+m*O;e[9]=n*x+p*B+q*C+m*S;e[13]=n*D+p*F+q*T+m*d;e[2]=r*w+t*E+s*R+u*Q;e[6]=r*K+t*A+s*H+u*O;e[10]=r*x+t*B+s*C+u*S;e[14]=r*D+t*F+s*T+u*d;e[3]=v*w+y*E+G*R+c*Q;e[7]=v*K+y*A+G*H+c*O;e[11]=v*x+y*B+G*C+c*S;e[15]=v*D+y*F+G*T+c*d;return this},multiplyToArray:function(a,b,c){var d=this.elements;this.multiplyMatrices(a,b);c[0]=d[0];c[1]=d[1];c[2]=d[2];c[3]=d[3];c[4]=
-d[4];c[5]=d[5];c[6]=d[6];c[7]=d[7];c[8]=d[8];c[9]=d[9];c[10]=d[10];c[11]=d[11];c[12]=d[12];c[13]=d[13];c[14]=d[14];c[15]=d[15];return this},multiplyScalar:function(a){var b=this.elements;b[0]*=a;b[4]*=a;b[8]*=a;b[12]*=a;b[1]*=a;b[5]*=a;b[9]*=a;b[13]*=a;b[2]*=a;b[6]*=a;b[10]*=a;b[14]*=a;b[3]*=a;b[7]*=a;b[11]*=a;b[15]*=a;return this},multiplyVector3:function(a){console.warn("THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.");
-return a.applyProjection(this)},multiplyVector4:function(a){console.warn("THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.");return a.applyMatrix4(this)},multiplyVector3Array:function(a){console.warn("THREE.Matrix4: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.");return this.applyToVector3Array(a)},applyToVector3Array:function(){var a=new THREE.Vector3;return function(b,c,d){void 0===c&&(c=0);void 0===d&&(d=
-b.length);for(var e=0;e<d;e+=3,c+=3)a.x=b[c],a.y=b[c+1],a.z=b[c+2],a.applyMatrix4(this),b[c]=a.x,b[c+1]=a.y,b[c+2]=a.z;return b}}(),rotateAxis:function(a){console.warn("THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.");a.transformDirection(this)},crossVector:function(a){console.warn("THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.");return a.applyMatrix4(this)},determinant:function(){var a=this.elements,b=
-a[0],c=a[4],d=a[8],e=a[12],f=a[1],g=a[5],h=a[9],k=a[13],n=a[2],p=a[6],q=a[10],m=a[14];return a[3]*(+e*h*p-d*k*p-e*g*q+c*k*q+d*g*m-c*h*m)+a[7]*(+b*h*m-b*k*q+e*f*q-d*f*m+d*k*n-e*h*n)+a[11]*(+b*k*p-b*g*m-e*f*p+c*f*m+e*g*n-c*k*n)+a[15]*(-d*g*n-b*h*p+b*g*q+d*f*p-c*f*q+c*h*n)},transpose:function(){var a=this.elements,b;b=a[1];a[1]=a[4];a[4]=b;b=a[2];a[2]=a[8];a[8]=b;b=a[6];a[6]=a[9];a[9]=b;b=a[3];a[3]=a[12];a[12]=b;b=a[7];a[7]=a[13];a[13]=b;b=a[11];a[11]=a[14];a[14]=b;return this},flattenToArrayOffset:function(a,
-b){var c=this.elements;a[b]=c[0];a[b+1]=c[1];a[b+2]=c[2];a[b+3]=c[3];a[b+4]=c[4];a[b+5]=c[5];a[b+6]=c[6];a[b+7]=c[7];a[b+8]=c[8];a[b+9]=c[9];a[b+10]=c[10];a[b+11]=c[11];a[b+12]=c[12];a[b+13]=c[13];a[b+14]=c[14];a[b+15]=c[15];return a},getPosition:function(){var a=new THREE.Vector3;return function(){console.warn("THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.");var b=this.elements;return a.set(b[12],b[13],b[14])}}(),setPosition:function(a){var b=
-this.elements;b[12]=a.x;b[13]=a.y;b[14]=a.z;return this},getInverse:function(a,b){var c=this.elements,d=a.elements,e=d[0],f=d[4],g=d[8],h=d[12],k=d[1],n=d[5],p=d[9],q=d[13],m=d[2],r=d[6],t=d[10],s=d[14],u=d[3],v=d[7],y=d[11],d=d[15];c[0]=p*s*v-q*t*v+q*r*y-n*s*y-p*r*d+n*t*d;c[4]=h*t*v-g*s*v-h*r*y+f*s*y+g*r*d-f*t*d;c[8]=g*q*v-h*p*v+h*n*y-f*q*y-g*n*d+f*p*d;c[12]=h*p*r-g*q*r-h*n*t+f*q*t+g*n*s-f*p*s;c[1]=q*t*u-p*s*u-q*m*y+k*s*y+p*m*d-k*t*d;c[5]=g*s*u-h*t*u+h*m*y-e*s*y-g*m*d+e*t*d;c[9]=h*p*u-g*q*u-h*k*
-y+e*q*y+g*k*d-e*p*d;c[13]=g*q*m-h*p*m+h*k*t-e*q*t-g*k*s+e*p*s;c[2]=n*s*u-q*r*u+q*m*v-k*s*v-n*m*d+k*r*d;c[6]=h*r*u-f*s*u-h*m*v+e*s*v+f*m*d-e*r*d;c[10]=f*q*u-h*n*u+h*k*v-e*q*v-f*k*d+e*n*d;c[14]=h*n*m-f*q*m-h*k*r+e*q*r+f*k*s-e*n*s;c[3]=p*r*u-n*t*u-p*m*v+k*t*v+n*m*y-k*r*y;c[7]=f*t*u-g*r*u+g*m*v-e*t*v-f*m*y+e*r*y;c[11]=g*n*u-f*p*u-g*k*v+e*p*v+f*k*y-e*n*y;c[15]=f*p*m-g*n*m+g*k*r-e*p*r-f*k*t+e*n*t;c=e*c[0]+k*c[4]+m*c[8]+u*c[12];if(0==c){if(b)throw Error("Matrix4.getInverse(): can't invert matrix, determinant is 0");
-console.warn("Matrix4.getInverse(): can't invert matrix, determinant is 0");this.identity();return this}this.multiplyScalar(1/c);return this},translate:function(a){console.warn("THREE.Matrix4: .translate() has been removed.")},rotateX:function(a){console.warn("THREE.Matrix4: .rotateX() has been removed.")},rotateY:function(a){console.warn("THREE.Matrix4: .rotateY() has been removed.")},rotateZ:function(a){console.warn("THREE.Matrix4: .rotateZ() has been removed.")},rotateByAxis:function(a,b){console.warn("THREE.Matrix4: .rotateByAxis() has been removed.")},
-scale:function(a){var b=this.elements,c=a.x,d=a.y;a=a.z;b[0]*=c;b[4]*=d;b[8]*=a;b[1]*=c;b[5]*=d;b[9]*=a;b[2]*=c;b[6]*=d;b[10]*=a;b[3]*=c;b[7]*=d;b[11]*=a;return this},getMaxScaleOnAxis:function(){var a=this.elements;return Math.sqrt(Math.max(a[0]*a[0]+a[1]*a[1]+a[2]*a[2],Math.max(a[4]*a[4]+a[5]*a[5]+a[6]*a[6],a[8]*a[8]+a[9]*a[9]+a[10]*a[10])))},makeTranslation:function(a,b,c){this.set(1,0,0,a,0,1,0,b,0,0,1,c,0,0,0,1);return this},makeRotationX:function(a){var b=Math.cos(a);a=Math.sin(a);this.set(1,
-0,0,0,0,b,-a,0,0,a,b,0,0,0,0,1);return this},makeRotationY:function(a){var b=Math.cos(a);a=Math.sin(a);this.set(b,0,a,0,0,1,0,0,-a,0,b,0,0,0,0,1);return this},makeRotationZ:function(a){var b=Math.cos(a);a=Math.sin(a);this.set(b,-a,0,0,a,b,0,0,0,0,1,0,0,0,0,1);return this},makeRotationAxis:function(a,b){var c=Math.cos(b),d=Math.sin(b),e=1-c,f=a.x,g=a.y,h=a.z,k=e*f,n=e*g;this.set(k*f+c,k*g-d*h,k*h+d*g,0,k*g+d*h,n*g+c,n*h-d*f,0,k*h-d*g,n*h+d*f,e*h*h+c,0,0,0,0,1);return this},makeScale:function(a,b,c){this.set(a,
-0,0,0,0,b,0,0,0,0,c,0,0,0,0,1);return this},compose:function(a,b,c){this.makeRotationFromQuaternion(b);this.scale(c);this.setPosition(a);return this},decompose:function(){var a=new THREE.Vector3,b=new THREE.Matrix4;return function(c,d,e){var f=this.elements,g=a.set(f[0],f[1],f[2]).length(),h=a.set(f[4],f[5],f[6]).length(),k=a.set(f[8],f[9],f[10]).length();0>this.determinant()&&(g=-g);c.x=f[12];c.y=f[13];c.z=f[14];b.elements.set(this.elements);c=1/g;var f=1/h,n=1/k;b.elements[0]*=c;b.elements[1]*=
-c;b.elements[2]*=c;b.elements[4]*=f;b.elements[5]*=f;b.elements[6]*=f;b.elements[8]*=n;b.elements[9]*=n;b.elements[10]*=n;d.setFromRotationMatrix(b);e.x=g;e.y=h;e.z=k;return this}}(),makeFrustum:function(a,b,c,d,e,f){var g=this.elements;g[0]=2*e/(b-a);g[4]=0;g[8]=(b+a)/(b-a);g[12]=0;g[1]=0;g[5]=2*e/(d-c);g[9]=(d+c)/(d-c);g[13]=0;g[2]=0;g[6]=0;g[10]=-(f+e)/(f-e);g[14]=-2*f*e/(f-e);g[3]=0;g[7]=0;g[11]=-1;g[15]=0;return this},makePerspective:function(a,b,c,d){a=c*Math.tan(THREE.Math.degToRad(.5*a));
-var e=-a;return this.makeFrustum(e*b,a*b,e,a,c,d)},makeOrthographic:function(a,b,c,d,e,f){var g=this.elements,h=b-a,k=c-d,n=f-e;g[0]=2/h;g[4]=0;g[8]=0;g[12]=-((b+a)/h);g[1]=0;g[5]=2/k;g[9]=0;g[13]=-((c+d)/k);g[2]=0;g[6]=0;g[10]=-2/n;g[14]=-((f+e)/n);g[3]=0;g[7]=0;g[11]=0;g[15]=1;return this},fromArray:function(a){this.elements.set(a);return this},toArray:function(){var a=this.elements;return[a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9],a[10],a[11],a[12],a[13],a[14],a[15]]},clone:function(){return(new THREE.Matrix4).fromArray(this.elements)}};
-THREE.Ray=function(a,b){this.origin=void 0!==a?a:new THREE.Vector3;this.direction=void 0!==b?b:new THREE.Vector3};
-THREE.Ray.prototype={constructor:THREE.Ray,set:function(a,b){this.origin.copy(a);this.direction.copy(b);return this},copy:function(a){this.origin.copy(a.origin);this.direction.copy(a.direction);return this},at:function(a,b){return(b||new THREE.Vector3).copy(this.direction).multiplyScalar(a).add(this.origin)},recast:function(){var a=new THREE.Vector3;return function(b){this.origin.copy(this.at(b,a));return this}}(),closestPointToPoint:function(a,b){var c=b||new THREE.Vector3;c.subVectors(a,this.origin);
-var d=c.dot(this.direction);return 0>d?c.copy(this.origin):c.copy(this.direction).multiplyScalar(d).add(this.origin)},distanceToPoint:function(){var a=new THREE.Vector3;return function(b){var c=a.subVectors(b,this.origin).dot(this.direction);if(0>c)return this.origin.distanceTo(b);a.copy(this.direction).multiplyScalar(c).add(this.origin);return a.distanceTo(b)}}(),distanceSqToSegment:function(a,b,c,d){var e=a.clone().add(b).multiplyScalar(.5),f=b.clone().sub(a).normalize(),g=.5*a.distanceTo(b),h=
-this.origin.clone().sub(e);a=-this.direction.dot(f);b=h.dot(this.direction);var k=-h.dot(f),n=h.lengthSq(),p=Math.abs(1-a*a),q,m;0<=p?(h=a*k-b,q=a*b-k,m=g*p,0<=h?q>=-m?q<=m?(g=1/p,h*=g,q*=g,a=h*(h+a*q+2*b)+q*(a*h+q+2*k)+n):(q=g,h=Math.max(0,-(a*q+b)),a=-h*h+q*(q+2*k)+n):(q=-g,h=Math.max(0,-(a*q+b)),a=-h*h+q*(q+2*k)+n):q<=-m?(h=Math.max(0,-(-a*g+b)),q=0<h?-g:Math.min(Math.max(-g,-k),g),a=-h*h+q*(q+2*k)+n):q<=m?(h=0,q=Math.min(Math.max(-g,-k),g),a=q*(q+2*k)+n):(h=Math.max(0,-(a*g+b)),q=0<h?g:Math.min(Math.max(-g,
--k),g),a=-h*h+q*(q+2*k)+n)):(q=0<a?-g:g,h=Math.max(0,-(a*q+b)),a=-h*h+q*(q+2*k)+n);c&&c.copy(this.direction.clone().multiplyScalar(h).add(this.origin));d&&d.copy(f.clone().multiplyScalar(q).add(e));return a},isIntersectionSphere:function(a){return this.distanceToPoint(a.center)<=a.radius},intersectSphere:function(){var a=new THREE.Vector3;return function(b,c){a.subVectors(b.center,this.origin);var d=a.dot(this.direction),e=a.dot(a)-d*d,f=b.radius*b.radius;if(e>f)return null;f=Math.sqrt(f-e);e=d-f;
-d+=f;return 0>e&&0>d?null:0>e?this.at(d,c):this.at(e,c)}}(),isIntersectionPlane:function(a){var b=a.distanceToPoint(this.origin);return 0===b||0>a.normal.dot(this.direction)*b?!0:!1},distanceToPlane:function(a){var b=a.normal.dot(this.direction);if(0==b)return 0==a.distanceToPoint(this.origin)?0:null;a=-(this.origin.dot(a.normal)+a.constant)/b;return 0<=a?a:null},intersectPlane:function(a,b){var c=this.distanceToPlane(a);return null===c?null:this.at(c,b)},isIntersectionBox:function(){var a=new THREE.Vector3;
-return function(b){return null!==this.intersectBox(b,a)}}(),intersectBox:function(a,b){var c,d,e,f,g;d=1/this.direction.x;f=1/this.direction.y;g=1/this.direction.z;var h=this.origin;0<=d?(c=(a.min.x-h.x)*d,d*=a.max.x-h.x):(c=(a.max.x-h.x)*d,d*=a.min.x-h.x);0<=f?(e=(a.min.y-h.y)*f,f*=a.max.y-h.y):(e=(a.max.y-h.y)*f,f*=a.min.y-h.y);if(c>f||e>d)return null;if(e>c||c!==c)c=e;if(f<d||d!==d)d=f;0<=g?(e=(a.min.z-h.z)*g,g*=a.max.z-h.z):(e=(a.max.z-h.z)*g,g*=a.min.z-h.z);if(c>g||e>d)return null;if(e>c||c!==
-c)c=e;if(g<d||d!==d)d=g;return 0>d?null:this.at(0<=c?c:d,b)},intersectTriangle:function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Vector3,d=new THREE.Vector3;return function(e,f,g,h,k){b.subVectors(f,e);c.subVectors(g,e);d.crossVectors(b,c);f=this.direction.dot(d);if(0<f){if(h)return null;h=1}else if(0>f)h=-1,f=-f;else return null;a.subVectors(this.origin,e);e=h*this.direction.dot(c.crossVectors(a,c));if(0>e)return null;g=h*this.direction.dot(b.cross(a));if(0>g||e+g>f)return null;
-e=-h*a.dot(d);return 0>e?null:this.at(e/f,k)}}(),applyMatrix4:function(a){this.direction.add(this.origin).applyMatrix4(a);this.origin.applyMatrix4(a);this.direction.sub(this.origin);this.direction.normalize();return this},equals:function(a){return a.origin.equals(this.origin)&&a.direction.equals(this.direction)},clone:function(){return(new THREE.Ray).copy(this)}};THREE.Sphere=function(a,b){this.center=void 0!==a?a:new THREE.Vector3;this.radius=void 0!==b?b:0};
-THREE.Sphere.prototype={constructor:THREE.Sphere,set:function(a,b){this.center.copy(a);this.radius=b;return this},setFromPoints:function(){var a=new THREE.Box3;return function(b,c){var d=this.center;void 0!==c?d.copy(c):a.setFromPoints(b).center(d);for(var e=0,f=0,g=b.length;f<g;f++)e=Math.max(e,d.distanceToSquared(b[f]));this.radius=Math.sqrt(e);return this}}(),copy:function(a){this.center.copy(a.center);this.radius=a.radius;return this},empty:function(){return 0>=this.radius},containsPoint:function(a){return a.distanceToSquared(this.center)<=
-this.radius*this.radius},distanceToPoint:function(a){return a.distanceTo(this.center)-this.radius},intersectsSphere:function(a){var b=this.radius+a.radius;return a.center.distanceToSquared(this.center)<=b*b},clampPoint:function(a,b){var c=this.center.distanceToSquared(a),d=b||new THREE.Vector3;d.copy(a);c>this.radius*this.radius&&(d.sub(this.center).normalize(),d.multiplyScalar(this.radius).add(this.center));return d},getBoundingBox:function(a){a=a||new THREE.Box3;a.set(this.center,this.center);a.expandByScalar(this.radius);
-return a},applyMatrix4:function(a){this.center.applyMatrix4(a);this.radius*=a.getMaxScaleOnAxis();return this},translate:function(a){this.center.add(a);return this},equals:function(a){return a.center.equals(this.center)&&a.radius===this.radius},clone:function(){return(new THREE.Sphere).copy(this)}};
-THREE.Frustum=function(a,b,c,d,e,f){this.planes=[void 0!==a?a:new THREE.Plane,void 0!==b?b:new THREE.Plane,void 0!==c?c:new THREE.Plane,void 0!==d?d:new THREE.Plane,void 0!==e?e:new THREE.Plane,void 0!==f?f:new THREE.Plane]};
-THREE.Frustum.prototype={constructor:THREE.Frustum,set:function(a,b,c,d,e,f){var g=this.planes;g[0].copy(a);g[1].copy(b);g[2].copy(c);g[3].copy(d);g[4].copy(e);g[5].copy(f);return this},copy:function(a){for(var b=this.planes,c=0;6>c;c++)b[c].copy(a.planes[c]);return this},setFromMatrix:function(a){var b=this.planes,c=a.elements;a=c[0];var d=c[1],e=c[2],f=c[3],g=c[4],h=c[5],k=c[6],n=c[7],p=c[8],q=c[9],m=c[10],r=c[11],t=c[12],s=c[13],u=c[14],c=c[15];b[0].setComponents(f-a,n-g,r-p,c-t).normalize();b[1].setComponents(f+
-a,n+g,r+p,c+t).normalize();b[2].setComponents(f+d,n+h,r+q,c+s).normalize();b[3].setComponents(f-d,n-h,r-q,c-s).normalize();b[4].setComponents(f-e,n-k,r-m,c-u).normalize();b[5].setComponents(f+e,n+k,r+m,c+u).normalize();return this},intersectsObject:function(){var a=new THREE.Sphere;return function(b){var c=b.geometry;null===c.boundingSphere&&c.computeBoundingSphere();a.copy(c.boundingSphere);a.applyMatrix4(b.matrixWorld);return this.intersectsSphere(a)}}(),intersectsSphere:function(a){var b=this.planes,
-c=a.center;a=-a.radius;for(var d=0;6>d;d++)if(b[d].distanceToPoint(c)<a)return!1;return!0},intersectsBox:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c){for(var d=this.planes,e=0;6>e;e++){var f=d[e];a.x=0<f.normal.x?c.min.x:c.max.x;b.x=0<f.normal.x?c.max.x:c.min.x;a.y=0<f.normal.y?c.min.y:c.max.y;b.y=0<f.normal.y?c.max.y:c.min.y;a.z=0<f.normal.z?c.min.z:c.max.z;b.z=0<f.normal.z?c.max.z:c.min.z;var g=f.distanceToPoint(a),f=f.distanceToPoint(b);if(0>g&&0>f)return!1}return!0}}(),
-containsPoint:function(a){for(var b=this.planes,c=0;6>c;c++)if(0>b[c].distanceToPoint(a))return!1;return!0},clone:function(){return(new THREE.Frustum).copy(this)}};THREE.Plane=function(a,b){this.normal=void 0!==a?a:new THREE.Vector3(1,0,0);this.constant=void 0!==b?b:0};
-THREE.Plane.prototype={constructor:THREE.Plane,set:function(a,b){this.normal.copy(a);this.constant=b;return this},setComponents:function(a,b,c,d){this.normal.set(a,b,c);this.constant=d;return this},setFromNormalAndCoplanarPoint:function(a,b){this.normal.copy(a);this.constant=-b.dot(this.normal);return this},setFromCoplanarPoints:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c,d,e){d=a.subVectors(e,d).cross(b.subVectors(c,d)).normalize();this.setFromNormalAndCoplanarPoint(d,
-c);return this}}(),copy:function(a){this.normal.copy(a.normal);this.constant=a.constant;return this},normalize:function(){var a=1/this.normal.length();this.normal.multiplyScalar(a);this.constant*=a;return this},negate:function(){this.constant*=-1;this.normal.negate();return this},distanceToPoint:function(a){return this.normal.dot(a)+this.constant},distanceToSphere:function(a){return this.distanceToPoint(a.center)-a.radius},projectPoint:function(a,b){return this.orthoPoint(a,b).sub(a).negate()},orthoPoint:function(a,
-b){var c=this.distanceToPoint(a);return(b||new THREE.Vector3).copy(this.normal).multiplyScalar(c)},isIntersectionLine:function(a){var b=this.distanceToPoint(a.start);a=this.distanceToPoint(a.end);return 0>b&&0<a||0>a&&0<b},intersectLine:function(){var a=new THREE.Vector3;return function(b,c){var d=c||new THREE.Vector3,e=b.delta(a),f=this.normal.dot(e);if(0==f){if(0==this.distanceToPoint(b.start))return d.copy(b.start)}else return f=-(b.start.dot(this.normal)+this.constant)/f,0>f||1<f?void 0:d.copy(e).multiplyScalar(f).add(b.start)}}(),
-coplanarPoint:function(a){return(a||new THREE.Vector3).copy(this.normal).multiplyScalar(-this.constant)},applyMatrix4:function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Matrix3;return function(d,e){var f=e||c.getNormalMatrix(d),f=a.copy(this.normal).applyMatrix3(f),g=this.coplanarPoint(b);g.applyMatrix4(d);this.setFromNormalAndCoplanarPoint(f,g);return this}}(),translate:function(a){this.constant-=a.dot(this.normal);return this},equals:function(a){return a.normal.equals(this.normal)&&
-a.constant==this.constant},clone:function(){return(new THREE.Plane).copy(this)}};
-THREE.Math={generateUUID:function(){var a="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""),b=Array(36),c=0,d;return function(){for(var e=0;36>e;e++)8==e||13==e||18==e||23==e?b[e]="-":14==e?b[e]="4":(2>=c&&(c=33554432+16777216*Math.random()|0),d=c&15,c>>=4,b[e]=a[19==e?d&3|8:d]);return b.join("")}}(),clamp:function(a,b,c){return a<b?b:a>c?c:a},clampBottom:function(a,b){return a<b?b:a},mapLinear:function(a,b,c,d,e){return d+(a-b)*(e-d)/(c-b)},smoothstep:function(a,b,c){if(a<=
-b)return 0;if(a>=c)return 1;a=(a-b)/(c-b);return a*a*(3-2*a)},smootherstep:function(a,b,c){if(a<=b)return 0;if(a>=c)return 1;a=(a-b)/(c-b);return a*a*a*(a*(6*a-15)+10)},random16:function(){return(65280*Math.random()+255*Math.random())/65535},randInt:function(a,b){return a+Math.floor(Math.random()*(b-a+1))},randFloat:function(a,b){return a+Math.random()*(b-a)},randFloatSpread:function(a){return a*(.5-Math.random())},degToRad:function(){var a=Math.PI/180;return function(b){return b*a}}(),radToDeg:function(){var a=
-180/Math.PI;return function(b){return b*a}}(),isPowerOfTwo:function(a){return 0===(a&a-1)&&0!==a}};
-THREE.Spline=function(a){function b(a,b,c,d,e,f,g){a=.5*(c-a);d=.5*(d-b);return(2*(b-c)+a+d)*g+(-3*(b-c)-2*a-d)*f+a*e+b}this.points=a;var c=[],d={x:0,y:0,z:0},e,f,g,h,k,n,p,q,m;this.initFromArray=function(a){this.points=[];for(var b=0;b<a.length;b++)this.points[b]={x:a[b][0],y:a[b][1],z:a[b][2]}};this.getPoint=function(a){e=(this.points.length-1)*a;f=Math.floor(e);g=e-f;c[0]=0===f?f:f-1;c[1]=f;c[2]=f>this.points.length-2?this.points.length-1:f+1;c[3]=f>this.points.length-3?this.points.length-1:f+
-2;n=this.points[c[0]];p=this.points[c[1]];q=this.points[c[2]];m=this.points[c[3]];h=g*g;k=g*h;d.x=b(n.x,p.x,q.x,m.x,g,h,k);d.y=b(n.y,p.y,q.y,m.y,g,h,k);d.z=b(n.z,p.z,q.z,m.z,g,h,k);return d};this.getControlPointsArray=function(){var a,b,c=this.points.length,d=[];for(a=0;a<c;a++)b=this.points[a],d[a]=[b.x,b.y,b.z];return d};this.getLength=function(a){var b,c,d,e=b=b=0,f=new THREE.Vector3,g=new THREE.Vector3,h=[],k=0;h[0]=0;a||(a=100);c=this.points.length*a;f.copy(this.points[0]);for(a=1;a<c;a++)b=
-a/c,d=this.getPoint(b),g.copy(d),k+=g.distanceTo(f),f.copy(d),b*=this.points.length-1,b=Math.floor(b),b!=e&&(h[b]=k,e=b);h[h.length]=k;return{chunks:h,total:k}};this.reparametrizeByArcLength=function(a){var b,c,d,e,f,g,h=[],k=new THREE.Vector3,m=this.getLength();h.push(k.copy(this.points[0]).clone());for(b=1;b<this.points.length;b++){c=m.chunks[b]-m.chunks[b-1];g=Math.ceil(a*c/m.total);e=(b-1)/(this.points.length-1);f=b/(this.points.length-1);for(c=1;c<g-1;c++)d=e+1/g*c*(f-e),d=this.getPoint(d),h.push(k.copy(d).clone());
-h.push(k.copy(this.points[b]).clone())}this.points=h}};THREE.Triangle=function(a,b,c){this.a=void 0!==a?a:new THREE.Vector3;this.b=void 0!==b?b:new THREE.Vector3;this.c=void 0!==c?c:new THREE.Vector3};THREE.Triangle.normal=function(){var a=new THREE.Vector3;return function(b,c,d,e){e=e||new THREE.Vector3;e.subVectors(d,c);a.subVectors(b,c);e.cross(a);b=e.lengthSq();return 0<b?e.multiplyScalar(1/Math.sqrt(b)):e.set(0,0,0)}}();
-THREE.Triangle.barycoordFromPoint=function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Vector3;return function(d,e,f,g,h){a.subVectors(g,e);b.subVectors(f,e);c.subVectors(d,e);d=a.dot(a);e=a.dot(b);f=a.dot(c);var k=b.dot(b);g=b.dot(c);var n=d*k-e*e;h=h||new THREE.Vector3;if(0==n)return h.set(-2,-1,-1);n=1/n;k=(k*f-e*g)*n;d=(d*g-e*f)*n;return h.set(1-k-d,d,k)}}();
-THREE.Triangle.containsPoint=function(){var a=new THREE.Vector3;return function(b,c,d,e){b=THREE.Triangle.barycoordFromPoint(b,c,d,e,a);return 0<=b.x&&0<=b.y&&1>=b.x+b.y}}();
-THREE.Triangle.prototype={constructor:THREE.Triangle,set:function(a,b,c){this.a.copy(a);this.b.copy(b);this.c.copy(c);return this},setFromPointsAndIndices:function(a,b,c,d){this.a.copy(a[b]);this.b.copy(a[c]);this.c.copy(a[d]);return this},copy:function(a){this.a.copy(a.a);this.b.copy(a.b);this.c.copy(a.c);return this},area:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(){a.subVectors(this.c,this.b);b.subVectors(this.a,this.b);return.5*a.cross(b).length()}}(),midpoint:function(a){return(a||
-new THREE.Vector3).addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)},normal:function(a){return THREE.Triangle.normal(this.a,this.b,this.c,a)},plane:function(a){return(a||new THREE.Plane).setFromCoplanarPoints(this.a,this.b,this.c)},barycoordFromPoint:function(a,b){return THREE.Triangle.barycoordFromPoint(a,this.a,this.b,this.c,b)},containsPoint:function(a){return THREE.Triangle.containsPoint(a,this.a,this.b,this.c)},equals:function(a){return a.a.equals(this.a)&&a.b.equals(this.b)&&a.c.equals(this.c)},
-clone:function(){return(new THREE.Triangle).copy(this)}};THREE.Clock=function(a){this.autoStart=void 0!==a?a:!0;this.elapsedTime=this.oldTime=this.startTime=0;this.running=!1};
-THREE.Clock.prototype={constructor:THREE.Clock,start:function(){this.oldTime=this.startTime=void 0!==self.performance&&void 0!==self.performance.now?self.performance.now():Date.now();this.running=!0},stop:function(){this.getElapsedTime();this.running=!1},getElapsedTime:function(){this.getDelta();return this.elapsedTime},getDelta:function(){var a=0;this.autoStart&&!this.running&&this.start();if(this.running){var b=void 0!==self.performance&&void 0!==self.performance.now?self.performance.now():Date.now(),
-a=.001*(b-this.oldTime);this.oldTime=b;this.elapsedTime+=a}return a}};THREE.EventDispatcher=function(){};
-THREE.EventDispatcher.prototype={constructor:THREE.EventDispatcher,apply:function(a){a.addEventListener=THREE.EventDispatcher.prototype.addEventListener;a.hasEventListener=THREE.EventDispatcher.prototype.hasEventListener;a.removeEventListener=THREE.EventDispatcher.prototype.removeEventListener;a.dispatchEvent=THREE.EventDispatcher.prototype.dispatchEvent},addEventListener:function(a,b){void 0===this._listeners&&(this._listeners={});var c=this._listeners;void 0===c[a]&&(c[a]=[]);-1===c[a].indexOf(b)&&
-c[a].push(b)},hasEventListener:function(a,b){if(void 0===this._listeners)return!1;var c=this._listeners;return void 0!==c[a]&&-1!==c[a].indexOf(b)?!0:!1},removeEventListener:function(a,b){if(void 0!==this._listeners){var c=this._listeners[a];if(void 0!==c){var d=c.indexOf(b);-1!==d&&c.splice(d,1)}}},dispatchEvent:function(a){if(void 0!==this._listeners){var b=this._listeners[a.type];if(void 0!==b){a.target=this;for(var c=[],d=b.length,e=0;e<d;e++)c[e]=b[e];for(e=0;e<d;e++)c[e].call(this,a)}}}};
-(function(a){a.Raycaster=function(b,c,f,g){this.ray=new a.Ray(b,c);this.near=f||0;this.far=g||Infinity;this.params={Sprite:{},Mesh:{},PointCloud:{threshold:1},LOD:{},Line:{}}};var b=function(a,b){return a.distance-b.distance},c=function(a,b,f,g){a.raycast(b,f);if(!0===g){a=a.children;g=0;for(var h=a.length;g<h;g++)c(a[g],b,f,!0)}};a.Raycaster.prototype={constructor:a.Raycaster,precision:1E-4,linePrecision:1,set:function(a,b){this.ray.set(a,b)},intersectObject:function(a,e){var f=[];c(a,this,f,e);
-f.sort(b);return f},intersectObjects:function(a,e){var f=[];if(!1===a instanceof Array)return console.log("THREE.Raycaster.intersectObjects: objects is not an Array."),f;for(var g=0,h=a.length;g<h;g++)c(a[g],this,f,e);f.sort(b);return f}}})(THREE);
-THREE.Object3D=function(){Object.defineProperty(this,"id",{value:THREE.Object3DIdCount++});this.uuid=THREE.Math.generateUUID();this.name="";this.type="Object3D";this.parent=void 0;this.children=[];this.up=THREE.Object3D.DefaultUp.clone();var a=new THREE.Vector3,b=new THREE.Euler,c=new THREE.Quaternion,d=new THREE.Vector3(1,1,1);b.onChange(function(){c.setFromEuler(b,!1)});c.onChange(function(){b.setFromQuaternion(c,void 0,!1)});Object.defineProperties(this,{position:{enumerable:!0,value:a},rotation:{enumerable:!0,
-value:b},quaternion:{enumerable:!0,value:c},scale:{enumerable:!0,value:d}});this.renderDepth=null;this.rotationAutoUpdate=!0;this.matrix=new THREE.Matrix4;this.matrixWorld=new THREE.Matrix4;this.matrixAutoUpdate=!0;this.matrixWorldNeedsUpdate=!1;this.visible=!0;this.receiveShadow=this.castShadow=!1;this.frustumCulled=!0;this.userData={}};THREE.Object3D.DefaultUp=new THREE.Vector3(0,1,0);
-THREE.Object3D.prototype={constructor:THREE.Object3D,get eulerOrder(){console.warn("THREE.Object3D: .eulerOrder has been moved to .rotation.order.");return this.rotation.order},set eulerOrder(a){console.warn("THREE.Object3D: .eulerOrder has been moved to .rotation.order.");this.rotation.order=a},get useQuaternion(){console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.")},set useQuaternion(a){console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.")},
-applyMatrix:function(a){this.matrix.multiplyMatrices(a,this.matrix);this.matrix.decompose(this.position,this.quaternion,this.scale)},setRotationFromAxisAngle:function(a,b){this.quaternion.setFromAxisAngle(a,b)},setRotationFromEuler:function(a){this.quaternion.setFromEuler(a,!0)},setRotationFromMatrix:function(a){this.quaternion.setFromRotationMatrix(a)},setRotationFromQuaternion:function(a){this.quaternion.copy(a)},rotateOnAxis:function(){var a=new THREE.Quaternion;return function(b,c){a.setFromAxisAngle(b,
-c);this.quaternion.multiply(a);return this}}(),rotateX:function(){var a=new THREE.Vector3(1,0,0);return function(b){return this.rotateOnAxis(a,b)}}(),rotateY:function(){var a=new THREE.Vector3(0,1,0);return function(b){return this.rotateOnAxis(a,b)}}(),rotateZ:function(){var a=new THREE.Vector3(0,0,1);return function(b){return this.rotateOnAxis(a,b)}}(),translateOnAxis:function(){var a=new THREE.Vector3;return function(b,c){a.copy(b).applyQuaternion(this.quaternion);this.position.add(a.multiplyScalar(c));
-return this}}(),translate:function(a,b){console.warn("THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.");return this.translateOnAxis(b,a)},translateX:function(){var a=new THREE.Vector3(1,0,0);return function(b){return this.translateOnAxis(a,b)}}(),translateY:function(){var a=new THREE.Vector3(0,1,0);return function(b){return this.translateOnAxis(a,b)}}(),translateZ:function(){var a=new THREE.Vector3(0,0,1);return function(b){return this.translateOnAxis(a,
-b)}}(),localToWorld:function(a){return a.applyMatrix4(this.matrixWorld)},worldToLocal:function(){var a=new THREE.Matrix4;return function(b){return b.applyMatrix4(a.getInverse(this.matrixWorld))}}(),lookAt:function(){var a=new THREE.Matrix4;return function(b){a.lookAt(b,this.position,this.up);this.quaternion.setFromRotationMatrix(a)}}(),add:function(a){if(1<arguments.length){for(var b=0;b<arguments.length;b++)this.add(arguments[b]);return this}if(a===this)return console.error("THREE.Object3D.add:",
-a,"can't be added as a child of itself."),this;a instanceof THREE.Object3D?(void 0!==a.parent&&a.parent.remove(a),a.parent=this,a.dispatchEvent({type:"added"}),this.children.push(a)):console.error("THREE.Object3D.add:",a,"is not an instance of THREE.Object3D.");return this},remove:function(a){if(1<arguments.length)for(var b=0;b<arguments.length;b++)this.remove(arguments[b]);b=this.children.indexOf(a);-1!==b&&(a.parent=void 0,a.dispatchEvent({type:"removed"}),this.children.splice(b,1))},getChildByName:function(a,
-b){console.warn("THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().");return this.getObjectByName(a,b)},getObjectById:function(a,b){if(this.id===a)return this;for(var c=0,d=this.children.length;c<d;c++){var e=this.children[c].getObjectById(a,b);if(void 0!==e)return e}},getObjectByName:function(a,b){if(this.name===a)return this;for(var c=0,d=this.children.length;c<d;c++){var e=this.children[c].getObjectByName(a,b);if(void 0!==e)return e}},getWorldPosition:function(a){a=a||new THREE.Vector3;
-this.updateMatrixWorld(!0);return a.setFromMatrixPosition(this.matrixWorld)},getWorldQuaternion:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c){c=c||new THREE.Quaternion;this.updateMatrixWorld(!0);this.matrixWorld.decompose(a,c,b);return c}}(),getWorldRotation:function(){var a=new THREE.Quaternion;return function(b){b=b||new THREE.Euler;this.getWorldQuaternion(a);return b.setFromQuaternion(a,this.rotation.order,!1)}}(),getWorldScale:function(){var a=new THREE.Vector3,b=new THREE.Quaternion;
-return function(c){c=c||new THREE.Vector3;this.updateMatrixWorld(!0);this.matrixWorld.decompose(a,b,c);return c}}(),getWorldDirection:function(){var a=new THREE.Quaternion;return function(b){b=b||new THREE.Vector3;this.getWorldQuaternion(a);return b.set(0,0,1).applyQuaternion(a)}}(),raycast:function(){},traverse:function(a){a(this);for(var b=0,c=this.children.length;b<c;b++)this.children[b].traverse(a)},traverseVisible:function(a){if(!1!==this.visible){a(this);for(var b=0,c=this.children.length;b<
-c;b++)this.children[b].traverseVisible(a)}},updateMatrix:function(){this.matrix.compose(this.position,this.quaternion,this.scale);this.matrixWorldNeedsUpdate=!0},updateMatrixWorld:function(a){!0===this.matrixAutoUpdate&&this.updateMatrix();if(!0===this.matrixWorldNeedsUpdate||!0===a)void 0===this.parent?this.matrixWorld.copy(this.matrix):this.matrixWorld.multiplyMatrices(this.parent.matrixWorld,this.matrix),this.matrixWorldNeedsUpdate=!1,a=!0;for(var b=0,c=this.children.length;b<c;b++)this.children[b].updateMatrixWorld(a)},
-toJSON:function(){var a={metadata:{version:4.3,type:"Object",generator:"ObjectExporter"}},b={},c=function(c){void 0===a.geometries&&(a.geometries=[]);if(void 0===b[c.uuid]){var d=c.toJSON();delete d.metadata;b[c.uuid]=d;a.geometries.push(d)}return c.uuid},d={},e=function(b){void 0===a.materials&&(a.materials=[]);if(void 0===d[b.uuid]){var c=b.toJSON();delete c.metadata;d[b.uuid]=c;a.materials.push(c)}return b.uuid},f=function(a){var b={};b.uuid=a.uuid;b.type=a.type;""!==a.name&&(b.name=a.name);"{}"!==
-JSON.stringify(a.userData)&&(b.userData=a.userData);!0!==a.visible&&(b.visible=a.visible);a instanceof THREE.PerspectiveCamera?(b.fov=a.fov,b.aspect=a.aspect,b.near=a.near,b.far=a.far):a instanceof THREE.OrthographicCamera?(b.left=a.left,b.right=a.right,b.top=a.top,b.bottom=a.bottom,b.near=a.near,b.far=a.far):a instanceof THREE.AmbientLight?b.color=a.color.getHex():a instanceof THREE.DirectionalLight?(b.color=a.color.getHex(),b.intensity=a.intensity):a instanceof THREE.PointLight?(b.color=a.color.getHex(),
-b.intensity=a.intensity,b.distance=a.distance):a instanceof THREE.SpotLight?(b.color=a.color.getHex(),b.intensity=a.intensity,b.distance=a.distance,b.angle=a.angle,b.exponent=a.exponent):a instanceof THREE.HemisphereLight?(b.color=a.color.getHex(),b.groundColor=a.groundColor.getHex()):a instanceof THREE.Mesh?(b.geometry=c(a.geometry),b.material=e(a.material)):a instanceof THREE.Line?(b.geometry=c(a.geometry),b.material=e(a.material)):a instanceof THREE.Sprite&&(b.material=e(a.material));b.matrix=
-a.matrix.toArray();if(0<a.children.length){b.children=[];for(var d=0;d<a.children.length;d++)b.children.push(f(a.children[d]))}return b};a.object=f(this);return a},clone:function(a,b){void 0===a&&(a=new THREE.Object3D);void 0===b&&(b=!0);a.name=this.name;a.up.copy(this.up);a.position.copy(this.position);a.quaternion.copy(this.quaternion);a.scale.copy(this.scale);a.renderDepth=this.renderDepth;a.rotationAutoUpdate=this.rotationAutoUpdate;a.matrix.copy(this.matrix);a.matrixWorld.copy(this.matrixWorld);
-a.matrixAutoUpdate=this.matrixAutoUpdate;a.matrixWorldNeedsUpdate=this.matrixWorldNeedsUpdate;a.visible=this.visible;a.castShadow=this.castShadow;a.receiveShadow=this.receiveShadow;a.frustumCulled=this.frustumCulled;a.userData=JSON.parse(JSON.stringify(this.userData));if(!0===b)for(var c=0;c<this.children.length;c++)a.add(this.children[c].clone());return a}};THREE.EventDispatcher.prototype.apply(THREE.Object3D.prototype);THREE.Object3DIdCount=0;
-THREE.Projector=function(){console.warn("THREE.Projector has been moved to /examples/renderers/Projector.js.");this.projectVector=function(a,b){console.warn("THREE.Projector: .projectVector() is now vector.project().");a.project(b)};this.unprojectVector=function(a,b){console.warn("THREE.Projector: .unprojectVector() is now vector.unproject().");a.unproject(b)};this.pickingRay=function(a,b){console.error("THREE.Projector: .pickingRay() has been removed.")}};
-THREE.Face3=function(a,b,c,d,e,f){this.a=a;this.b=b;this.c=c;this.normal=d instanceof THREE.Vector3?d:new THREE.Vector3;this.vertexNormals=d instanceof Array?d:[];this.color=e instanceof THREE.Color?e:new THREE.Color;this.vertexColors=e instanceof Array?e:[];this.vertexTangents=[];this.materialIndex=void 0!==f?f:0};
-THREE.Face3.prototype={constructor:THREE.Face3,clone:function(){var a=new THREE.Face3(this.a,this.b,this.c);a.normal.copy(this.normal);a.color.copy(this.color);a.materialIndex=this.materialIndex;for(var b=0,c=this.vertexNormals.length;b<c;b++)a.vertexNormals[b]=this.vertexNormals[b].clone();b=0;for(c=this.vertexColors.length;b<c;b++)a.vertexColors[b]=this.vertexColors[b].clone();b=0;for(c=this.vertexTangents.length;b<c;b++)a.vertexTangents[b]=this.vertexTangents[b].clone();return a}};
-THREE.Face4=function(a,b,c,d,e,f,g){console.warn("THREE.Face4 has been removed. A THREE.Face3 will be created instead.");return new THREE.Face3(a,b,c,e,f,g)};THREE.BufferAttribute=function(a,b){this.array=a;this.itemSize=b;this.needsUpdate=!1};
-THREE.BufferAttribute.prototype={constructor:THREE.BufferAttribute,get length(){return this.array.length},copyAt:function(a,b,c){a*=this.itemSize;c*=b.itemSize;for(var d=0,e=this.itemSize;d<e;d++)this.array[a+d]=b.array[c+d]},set:function(a){this.array.set(a);return this},setX:function(a,b){this.array[a*this.itemSize]=b;return this},setY:function(a,b){this.array[a*this.itemSize+1]=b;return this},setZ:function(a,b){this.array[a*this.itemSize+2]=b;return this},setXY:function(a,b,c){a*=this.itemSize;
-this.array[a]=b;this.array[a+1]=c;return this},setXYZ:function(a,b,c,d){a*=this.itemSize;this.array[a]=b;this.array[a+1]=c;this.array[a+2]=d;return this},setXYZW:function(a,b,c,d,e){a*=this.itemSize;this.array[a]=b;this.array[a+1]=c;this.array[a+2]=d;this.array[a+3]=e;return this},clone:function(){return new THREE.BufferAttribute(new this.array.constructor(this.array),this.itemSize)}};
-THREE.Int8Attribute=function(a,b){console.warn("THREE.Int8Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};THREE.Uint8Attribute=function(a,b){console.warn("THREE.Uint8Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};
-THREE.Uint8ClampedAttribute=function(a,b){console.warn("THREE.Uint8ClampedAttribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};THREE.Int16Attribute=function(a,b){console.warn("THREE.Int16Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};
-THREE.Uint16Attribute=function(a,b){console.warn("THREE.Uint16Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};THREE.Int32Attribute=function(a,b){console.warn("THREE.Int32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};
-THREE.Uint32Attribute=function(a,b){console.warn("THREE.Uint32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};THREE.Float32Attribute=function(a,b){console.warn("THREE.Float32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};
-THREE.Float64Attribute=function(a,b){console.warn("THREE.Float64Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};THREE.BufferGeometry=function(){Object.defineProperty(this,"id",{value:THREE.GeometryIdCount++});this.uuid=THREE.Math.generateUUID();this.name="";this.type="BufferGeometry";this.attributes={};this.attributesKeys=[];this.offsets=this.drawcalls=[];this.boundingSphere=this.boundingBox=null};
-THREE.BufferGeometry.prototype={constructor:THREE.BufferGeometry,addAttribute:function(a,b,c){!1===b instanceof THREE.BufferAttribute?(console.warn("THREE.BufferGeometry: .addAttribute() now expects ( name, attribute )."),this.attributes[a]={array:b,itemSize:c}):(this.attributes[a]=b,this.attributesKeys=Object.keys(this.attributes))},getAttribute:function(a){return this.attributes[a]},addDrawCall:function(a,b,c){this.drawcalls.push({start:a,count:b,index:void 0!==c?c:0})},applyMatrix:function(a){var b=
-this.attributes.position;void 0!==b&&(a.applyToVector3Array(b.array),b.needsUpdate=!0);b=this.attributes.normal;void 0!==b&&((new THREE.Matrix3).getNormalMatrix(a).applyToVector3Array(b.array),b.needsUpdate=!0)},center:function(){},fromGeometry:function(a,b){b=b||{vertexColors:THREE.NoColors};var c=a.vertices,d=a.faces,e=a.faceVertexUvs,f=b.vertexColors,g=0<e[0].length,h=3==d[0].vertexNormals.length,k=new Float32Array(9*d.length);this.addAttribute("position",new THREE.BufferAttribute(k,3));var n=
-new Float32Array(9*d.length);this.addAttribute("normal",new THREE.BufferAttribute(n,3));if(f!==THREE.NoColors){var p=new Float32Array(9*d.length);this.addAttribute("color",new THREE.BufferAttribute(p,3))}if(!0===g){var q=new Float32Array(6*d.length);this.addAttribute("uv",new THREE.BufferAttribute(q,2))}for(var m=0,r=0,t=0;m<d.length;m++,r+=6,t+=9){var s=d[m],u=c[s.a],v=c[s.b],y=c[s.c];k[t]=u.x;k[t+1]=u.y;k[t+2]=u.z;k[t+3]=v.x;k[t+4]=v.y;k[t+5]=v.z;k[t+6]=y.x;k[t+7]=y.y;k[t+8]=y.z;!0===h?(u=s.vertexNormals[0],
-v=s.vertexNormals[1],y=s.vertexNormals[2],n[t]=u.x,n[t+1]=u.y,n[t+2]=u.z,n[t+3]=v.x,n[t+4]=v.y,n[t+5]=v.z,n[t+6]=y.x,n[t+7]=y.y,n[t+8]=y.z):(u=s.normal,n[t]=u.x,n[t+1]=u.y,n[t+2]=u.z,n[t+3]=u.x,n[t+4]=u.y,n[t+5]=u.z,n[t+6]=u.x,n[t+7]=u.y,n[t+8]=u.z);f===THREE.FaceColors?(s=s.color,p[t]=s.r,p[t+1]=s.g,p[t+2]=s.b,p[t+3]=s.r,p[t+4]=s.g,p[t+5]=s.b,p[t+6]=s.r,p[t+7]=s.g,p[t+8]=s.b):f===THREE.VertexColors&&(u=s.vertexColors[0],v=s.vertexColors[1],s=s.vertexColors[2],p[t]=u.r,p[t+1]=u.g,p[t+2]=u.b,p[t+3]=
-v.r,p[t+4]=v.g,p[t+5]=v.b,p[t+6]=s.r,p[t+7]=s.g,p[t+8]=s.b);!0===g&&(s=e[0][m][0],u=e[0][m][1],v=e[0][m][2],q[r]=s.x,q[r+1]=s.y,q[r+2]=u.x,q[r+3]=u.y,q[r+4]=v.x,q[r+5]=v.y)}this.computeBoundingSphere();return this},computeBoundingBox:function(){var a=new THREE.Vector3;return function(){null===this.boundingBox&&(this.boundingBox=new THREE.Box3);var b=this.attributes.position.array;if(b){var c=this.boundingBox;c.makeEmpty();for(var d=0,e=b.length;d<e;d+=3)a.set(b[d],b[d+1],b[d+2]),c.expandByPoint(a)}if(void 0===
-b||0===b.length)this.boundingBox.min.set(0,0,0),this.boundingBox.max.set(0,0,0);(isNaN(this.boundingBox.min.x)||isNaN(this.boundingBox.min.y)||isNaN(this.boundingBox.min.z))&&console.error('THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.')}}(),computeBoundingSphere:function(){var a=new THREE.Box3,b=new THREE.Vector3;return function(){null===this.boundingSphere&&(this.boundingSphere=new THREE.Sphere);var c=this.attributes.position.array;
-if(c){a.makeEmpty();for(var d=this.boundingSphere.center,e=0,f=c.length;e<f;e+=3)b.set(c[e],c[e+1],c[e+2]),a.expandByPoint(b);a.center(d);for(var g=0,e=0,f=c.length;e<f;e+=3)b.set(c[e],c[e+1],c[e+2]),g=Math.max(g,d.distanceToSquared(b));this.boundingSphere.radius=Math.sqrt(g);isNaN(this.boundingSphere.radius)&&console.error('THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.')}}}(),computeFaceNormals:function(){},computeVertexNormals:function(){var a=
-this.attributes;if(a.position){var b=a.position.array;if(void 0===a.normal)this.addAttribute("normal",new THREE.BufferAttribute(new Float32Array(b.length),3));else for(var c=a.normal.array,d=0,e=c.length;d<e;d++)c[d]=0;var c=a.normal.array,f,g,h,k=new THREE.Vector3,n=new THREE.Vector3,p=new THREE.Vector3,q=new THREE.Vector3,m=new THREE.Vector3;if(a.index)for(var r=a.index.array,t=0<this.offsets.length?this.offsets:[{start:0,count:r.length,index:0}],s=0,u=t.length;s<u;++s){e=t[s].start;f=t[s].count;
-for(var v=t[s].index,d=e,e=e+f;d<e;d+=3)f=3*(v+r[d]),g=3*(v+r[d+1]),h=3*(v+r[d+2]),k.fromArray(b,f),n.fromArray(b,g),p.fromArray(b,h),q.subVectors(p,n),m.subVectors(k,n),q.cross(m),c[f]+=q.x,c[f+1]+=q.y,c[f+2]+=q.z,c[g]+=q.x,c[g+1]+=q.y,c[g+2]+=q.z,c[h]+=q.x,c[h+1]+=q.y,c[h+2]+=q.z}else for(d=0,e=b.length;d<e;d+=9)k.fromArray(b,d),n.fromArray(b,d+3),p.fromArray(b,d+6),q.subVectors(p,n),m.subVectors(k,n),q.cross(m),c[d]=q.x,c[d+1]=q.y,c[d+2]=q.z,c[d+3]=q.x,c[d+4]=q.y,c[d+5]=q.z,c[d+6]=q.x,c[d+7]=q.y,
-c[d+8]=q.z;this.normalizeNormals();a.normal.needsUpdate=!0}},computeTangents:function(){function a(a,b,c){q.fromArray(d,3*a);m.fromArray(d,3*b);r.fromArray(d,3*c);t.fromArray(f,2*a);s.fromArray(f,2*b);u.fromArray(f,2*c);v=m.x-q.x;y=r.x-q.x;G=m.y-q.y;w=r.y-q.y;K=m.z-q.z;x=r.z-q.z;D=s.x-t.x;E=u.x-t.x;A=s.y-t.y;B=u.y-t.y;F=1/(D*B-E*A);R.set((B*v-A*y)*F,(B*G-A*w)*F,(B*K-A*x)*F);H.set((D*y-E*v)*F,(D*w-E*G)*F,(D*x-E*K)*F);k[a].add(R);k[b].add(R);k[c].add(R);n[a].add(H);n[b].add(H);n[c].add(H)}function b(a){ya.fromArray(e,
-3*a);P.copy(ya);Fa=k[a];la.copy(Fa);la.sub(ya.multiplyScalar(ya.dot(Fa))).normalize();ma.crossVectors(P,Fa);za=ma.dot(n[a]);Ga=0>za?-1:1;h[4*a]=la.x;h[4*a+1]=la.y;h[4*a+2]=la.z;h[4*a+3]=Ga}if(void 0===this.attributes.index||void 0===this.attributes.position||void 0===this.attributes.normal||void 0===this.attributes.uv)console.warn("Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()");else{var c=this.attributes.index.array,d=this.attributes.position.array,
-e=this.attributes.normal.array,f=this.attributes.uv.array,g=d.length/3;void 0===this.attributes.tangent&&this.addAttribute("tangent",new THREE.BufferAttribute(new Float32Array(4*g),4));for(var h=this.attributes.tangent.array,k=[],n=[],p=0;p<g;p++)k[p]=new THREE.Vector3,n[p]=new THREE.Vector3;var q=new THREE.Vector3,m=new THREE.Vector3,r=new THREE.Vector3,t=new THREE.Vector2,s=new THREE.Vector2,u=new THREE.Vector2,v,y,G,w,K,x,D,E,A,B,F,R=new THREE.Vector3,H=new THREE.Vector3,C,T,Q,O,S;0===this.drawcalls.length&&
-this.addDrawCall(0,c.length,0);var X=this.drawcalls,p=0;for(T=X.length;p<T;++p){C=X[p].start;Q=X[p].count;var Y=X[p].index,g=C;for(C+=Q;g<C;g+=3)Q=Y+c[g],O=Y+c[g+1],S=Y+c[g+2],a(Q,O,S)}var la=new THREE.Vector3,ma=new THREE.Vector3,ya=new THREE.Vector3,P=new THREE.Vector3,Ga,Fa,za,p=0;for(T=X.length;p<T;++p)for(C=X[p].start,Q=X[p].count,Y=X[p].index,g=C,C+=Q;g<C;g+=3)Q=Y+c[g],O=Y+c[g+1],S=Y+c[g+2],b(Q),b(O),b(S)}},computeOffsets:function(a){var b=a;void 0===a&&(b=65535);Date.now();a=this.attributes.index.array;
-for(var c=this.attributes.position.array,d=a.length/3,e=new Uint16Array(a.length),f=0,g=0,h=[{start:0,count:0,index:0}],k=h[0],n=0,p=0,q=new Int32Array(6),m=new Int32Array(c.length),r=new Int32Array(c.length),t=0;t<c.length;t++)m[t]=-1,r[t]=-1;for(c=0;c<d;c++){for(var s=p=0;3>s;s++)t=a[3*c+s],-1==m[t]?(q[2*s]=t,q[2*s+1]=-1,p++):m[t]<k.index?(q[2*s]=t,q[2*s+1]=-1,n++):(q[2*s]=t,q[2*s+1]=m[t]);if(g+p>k.index+b)for(k={start:f,count:0,index:g},h.push(k),p=0;6>p;p+=2)s=q[p+1],-1<s&&s<k.index&&(q[p+1]=
--1);for(p=0;6>p;p+=2)t=q[p],s=q[p+1],-1===s&&(s=g++),m[t]=s,r[s]=t,e[f++]=s-k.index,k.count++}this.reorderBuffers(e,r,g);return this.offsets=h},merge:function(){console.log("BufferGeometry.merge(): TODO")},normalizeNormals:function(){for(var a=this.attributes.normal.array,b,c,d,e=0,f=a.length;e<f;e+=3)b=a[e],c=a[e+1],d=a[e+2],b=1/Math.sqrt(b*b+c*c+d*d),a[e]*=b,a[e+1]*=b,a[e+2]*=b},reorderBuffers:function(a,b,c){var d={},e;for(e in this.attributes)"index"!=e&&(d[e]=new this.attributes[e].array.constructor(this.attributes[e].itemSize*
-c));for(var f=0;f<c;f++){var g=b[f];for(e in this.attributes)if("index"!=e)for(var h=this.attributes[e].array,k=this.attributes[e].itemSize,n=d[e],p=0;p<k;p++)n[f*k+p]=h[g*k+p]}this.attributes.index.array=a;for(e in this.attributes)"index"!=e&&(this.attributes[e].array=d[e],this.attributes[e].numItems=this.attributes[e].itemSize*c)},toJSON:function(){var a={metadata:{version:4,type:"BufferGeometry",generator:"BufferGeometryExporter"},uuid:this.uuid,type:this.type,data:{attributes:{}}},b=this.attributes,
-c=this.offsets,d=this.boundingSphere,e;for(e in b){for(var f=b[e],g=[],h=f.array,k=0,n=h.length;k<n;k++)g[k]=h[k];a.data.attributes[e]={itemSize:f.itemSize,type:f.array.constructor.name,array:g}}0<c.length&&(a.data.offsets=JSON.parse(JSON.stringify(c)));null!==d&&(a.data.boundingSphere={center:d.center.toArray(),radius:d.radius});return a},clone:function(){var a=new THREE.BufferGeometry,b;for(b in this.attributes)a.addAttribute(b,this.attributes[b].clone());b=0;for(var c=this.offsets.length;b<c;b++){var d=
-this.offsets[b];a.offsets.push({start:d.start,index:d.index,count:d.count})}return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.BufferGeometry.prototype);
-THREE.Geometry=function(){Object.defineProperty(this,"id",{value:THREE.GeometryIdCount++});this.uuid=THREE.Math.generateUUID();this.name="";this.type="Geometry";this.vertices=[];this.colors=[];this.faces=[];this.faceVertexUvs=[[]];this.morphTargets=[];this.morphColors=[];this.morphNormals=[];this.skinWeights=[];this.skinIndices=[];this.lineDistances=[];this.boundingSphere=this.boundingBox=null;this.hasTangents=!1;this.dynamic=!0;this.groupsNeedUpdate=this.lineDistancesNeedUpdate=this.colorsNeedUpdate=
-this.tangentsNeedUpdate=this.normalsNeedUpdate=this.uvsNeedUpdate=this.elementsNeedUpdate=this.verticesNeedUpdate=!1};
-THREE.Geometry.prototype={constructor:THREE.Geometry,applyMatrix:function(a){for(var b=(new THREE.Matrix3).getNormalMatrix(a),c=0,d=this.vertices.length;c<d;c++)this.vertices[c].applyMatrix4(a);c=0;for(d=this.faces.length;c<d;c++){a=this.faces[c];a.normal.applyMatrix3(b).normalize();for(var e=0,f=a.vertexNormals.length;e<f;e++)a.vertexNormals[e].applyMatrix3(b).normalize()}this.boundingBox instanceof THREE.Box3&&this.computeBoundingBox();this.boundingSphere instanceof THREE.Sphere&&this.computeBoundingSphere()},
-fromBufferGeometry:function(a){for(var b=this,c=a.attributes,d=c.position.array,e=void 0!==c.index?c.index.array:void 0,f=void 0!==c.normal?c.normal.array:void 0,g=void 0!==c.color?c.color.array:void 0,h=void 0!==c.uv?c.uv.array:void 0,k=[],n=[],p=c=0;c<d.length;c+=3,p+=2)b.vertices.push(new THREE.Vector3(d[c],d[c+1],d[c+2])),void 0!==f&&k.push(new THREE.Vector3(f[c],f[c+1],f[c+2])),void 0!==g&&b.colors.push(new THREE.Color(g[c],g[c+1],g[c+2])),void 0!==h&&n.push(new THREE.Vector2(h[p],h[p+1]));h=
-function(a,c,d){var e=void 0!==f?[k[a].clone(),k[c].clone(),k[d].clone()]:[],h=void 0!==g?[b.colors[a].clone(),b.colors[c].clone(),b.colors[d].clone()]:[];b.faces.push(new THREE.Face3(a,c,d,e,h));b.faceVertexUvs[0].push([n[a],n[c],n[d]])};if(void 0!==e)for(c=0;c<e.length;c+=3)h(e[c],e[c+1],e[c+2]);else for(c=0;c<d.length/3;c+=3)h(c,c+1,c+2);this.computeFaceNormals();null!==a.boundingBox&&(this.boundingBox=a.boundingBox.clone());null!==a.boundingSphere&&(this.boundingSphere=a.boundingSphere.clone());
-return this},center:function(){this.computeBoundingBox();var a=new THREE.Vector3;a.addVectors(this.boundingBox.min,this.boundingBox.max);a.multiplyScalar(-.5);this.applyMatrix((new THREE.Matrix4).makeTranslation(a.x,a.y,a.z));this.computeBoundingBox();return a},computeFaceNormals:function(){for(var a=new THREE.Vector3,b=new THREE.Vector3,c=0,d=this.faces.length;c<d;c++){var e=this.faces[c],f=this.vertices[e.a],g=this.vertices[e.b];a.subVectors(this.vertices[e.c],g);b.subVectors(f,g);a.cross(b);a.normalize();
-e.normal.copy(a)}},computeVertexNormals:function(a){var b,c,d;d=Array(this.vertices.length);b=0;for(c=this.vertices.length;b<c;b++)d[b]=new THREE.Vector3;if(a){var e,f,g,h=new THREE.Vector3,k=new THREE.Vector3;new THREE.Vector3;new THREE.Vector3;new THREE.Vector3;a=0;for(b=this.faces.length;a<b;a++)c=this.faces[a],e=this.vertices[c.a],f=this.vertices[c.b],g=this.vertices[c.c],h.subVectors(g,f),k.subVectors(e,f),h.cross(k),d[c.a].add(h),d[c.b].add(h),d[c.c].add(h)}else for(a=0,b=this.faces.length;a<
-b;a++)c=this.faces[a],d[c.a].add(c.normal),d[c.b].add(c.normal),d[c.c].add(c.normal);b=0;for(c=this.vertices.length;b<c;b++)d[b].normalize();a=0;for(b=this.faces.length;a<b;a++)c=this.faces[a],c.vertexNormals[0]=d[c.a].clone(),c.vertexNormals[1]=d[c.b].clone(),c.vertexNormals[2]=d[c.c].clone()},computeMorphNormals:function(){var a,b,c,d,e;c=0;for(d=this.faces.length;c<d;c++)for(e=this.faces[c],e.__originalFaceNormal?e.__originalFaceNormal.copy(e.normal):e.__originalFaceNormal=e.normal.clone(),e.__originalVertexNormals||
-(e.__originalVertexNormals=[]),a=0,b=e.vertexNormals.length;a<b;a++)e.__originalVertexNormals[a]?e.__originalVertexNormals[a].copy(e.vertexNormals[a]):e.__originalVertexNormals[a]=e.vertexNormals[a].clone();var f=new THREE.Geometry;f.faces=this.faces;a=0;for(b=this.morphTargets.length;a<b;a++){if(!this.morphNormals[a]){this.morphNormals[a]={};this.morphNormals[a].faceNormals=[];this.morphNormals[a].vertexNormals=[];e=this.morphNormals[a].faceNormals;var g=this.morphNormals[a].vertexNormals,h,k;c=
-0;for(d=this.faces.length;c<d;c++)h=new THREE.Vector3,k={a:new THREE.Vector3,b:new THREE.Vector3,c:new THREE.Vector3},e.push(h),g.push(k)}g=this.morphNormals[a];f.vertices=this.morphTargets[a].vertices;f.computeFaceNormals();f.computeVertexNormals();c=0;for(d=this.faces.length;c<d;c++)e=this.faces[c],h=g.faceNormals[c],k=g.vertexNormals[c],h.copy(e.normal),k.a.copy(e.vertexNormals[0]),k.b.copy(e.vertexNormals[1]),k.c.copy(e.vertexNormals[2])}c=0;for(d=this.faces.length;c<d;c++)e=this.faces[c],e.normal=
-e.__originalFaceNormal,e.vertexNormals=e.__originalVertexNormals},computeTangents:function(){var a,b,c,d,e,f,g,h,k,n,p,q,m,r,t,s,u,v=[],y=[];c=new THREE.Vector3;var G=new THREE.Vector3,w=new THREE.Vector3,K=new THREE.Vector3,x=new THREE.Vector3;a=0;for(b=this.vertices.length;a<b;a++)v[a]=new THREE.Vector3,y[a]=new THREE.Vector3;a=0;for(b=this.faces.length;a<b;a++)e=this.faces[a],f=this.faceVertexUvs[0][a],d=e.a,u=e.b,e=e.c,g=this.vertices[d],h=this.vertices[u],k=this.vertices[e],n=f[0],p=f[1],q=f[2],
-f=h.x-g.x,m=k.x-g.x,r=h.y-g.y,t=k.y-g.y,h=h.z-g.z,g=k.z-g.z,k=p.x-n.x,s=q.x-n.x,p=p.y-n.y,n=q.y-n.y,q=1/(k*n-s*p),c.set((n*f-p*m)*q,(n*r-p*t)*q,(n*h-p*g)*q),G.set((k*m-s*f)*q,(k*t-s*r)*q,(k*g-s*h)*q),v[d].add(c),v[u].add(c),v[e].add(c),y[d].add(G),y[u].add(G),y[e].add(G);G=["a","b","c","d"];a=0;for(b=this.faces.length;a<b;a++)for(e=this.faces[a],c=0;c<Math.min(e.vertexNormals.length,3);c++)x.copy(e.vertexNormals[c]),d=e[G[c]],u=v[d],w.copy(u),w.sub(x.multiplyScalar(x.dot(u))).normalize(),K.crossVectors(e.vertexNormals[c],
-u),d=K.dot(y[d]),d=0>d?-1:1,e.vertexTangents[c]=new THREE.Vector4(w.x,w.y,w.z,d);this.hasTangents=!0},computeLineDistances:function(){for(var a=0,b=this.vertices,c=0,d=b.length;c<d;c++)0<c&&(a+=b[c].distanceTo(b[c-1])),this.lineDistances[c]=a},computeBoundingBox:function(){null===this.boundingBox&&(this.boundingBox=new THREE.Box3);this.boundingBox.setFromPoints(this.vertices)},computeBoundingSphere:function(){null===this.boundingSphere&&(this.boundingSphere=new THREE.Sphere);this.boundingSphere.setFromPoints(this.vertices)},
-merge:function(a,b,c){if(!1===a instanceof THREE.Geometry)console.error("THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.",a);else{var d,e=this.vertices.length,f=this.vertices,g=a.vertices,h=this.faces,k=a.faces,n=this.faceVertexUvs[0];a=a.faceVertexUvs[0];void 0===c&&(c=0);void 0!==b&&(d=(new THREE.Matrix3).getNormalMatrix(b));for(var p=0,q=g.length;p<q;p++){var m=g[p].clone();void 0!==b&&m.applyMatrix4(b);f.push(m)}p=0;for(q=k.length;p<q;p++){var g=k[p],r,t=g.vertexNormals,s=
-g.vertexColors,m=new THREE.Face3(g.a+e,g.b+e,g.c+e);m.normal.copy(g.normal);void 0!==d&&m.normal.applyMatrix3(d).normalize();b=0;for(f=t.length;b<f;b++)r=t[b].clone(),void 0!==d&&r.applyMatrix3(d).normalize(),m.vertexNormals.push(r);m.color.copy(g.color);b=0;for(f=s.length;b<f;b++)r=s[b],m.vertexColors.push(r.clone());m.materialIndex=g.materialIndex+c;h.push(m)}p=0;for(q=a.length;p<q;p++)if(c=a[p],d=[],void 0!==c){b=0;for(f=c.length;b<f;b++)d.push(new THREE.Vector2(c[b].x,c[b].y));n.push(d)}}},mergeVertices:function(){var a=
-{},b=[],c=[],d,e=Math.pow(10,4),f,g;f=0;for(g=this.vertices.length;f<g;f++)d=this.vertices[f],d=Math.round(d.x*e)+"_"+Math.round(d.y*e)+"_"+Math.round(d.z*e),void 0===a[d]?(a[d]=f,b.push(this.vertices[f]),c[f]=b.length-1):c[f]=c[a[d]];a=[];f=0;for(g=this.faces.length;f<g;f++)for(e=this.faces[f],e.a=c[e.a],e.b=c[e.b],e.c=c[e.c],e=[e.a,e.b,e.c],d=0;3>d;d++)if(e[d]==e[(d+1)%3]){a.push(f);break}for(f=a.length-1;0<=f;f--)for(e=a[f],this.faces.splice(e,1),c=0,g=this.faceVertexUvs.length;c<g;c++)this.faceVertexUvs[c].splice(e,
-1);f=this.vertices.length-b.length;this.vertices=b;return f},toJSON:function(){function a(a,b,c){return c?a|1<<b:a&~(1<<b)}function b(a){var b=a.x.toString()+a.y.toString()+a.z.toString();if(void 0!==n[b])return n[b];n[b]=k.length/3;k.push(a.x,a.y,a.z);return n[b]}function c(a){var b=a.r.toString()+a.g.toString()+a.b.toString();if(void 0!==q[b])return q[b];q[b]=p.length;p.push(a.getHex());return q[b]}function d(a){var b=a.x.toString()+a.y.toString();if(void 0!==r[b])return r[b];r[b]=m.length/2;m.push(a.x,
-a.y);return r[b]}var e={metadata:{version:4,type:"BufferGeometry",generator:"BufferGeometryExporter"},uuid:this.uuid,type:this.type};""!==this.name&&(e.name=this.name);if(void 0!==this.parameters){var f=this.parameters,g;for(g in f)void 0!==f[g]&&(e[g]=f[g]);return e}f=[];for(g=0;g<this.vertices.length;g++){var h=this.vertices[g];f.push(h.x,h.y,h.z)}var h=[],k=[],n={},p=[],q={},m=[],r={};for(g=0;g<this.faces.length;g++){var t=this.faces[g],s=void 0!==this.faceVertexUvs[0][g],u=0<t.normal.length(),
-v=0<t.vertexNormals.length,y=1!==t.color.r||1!==t.color.g||1!==t.color.b,G=0<t.vertexColors.length,w=0,w=a(w,0,0),w=a(w,1,!1),w=a(w,2,!1),w=a(w,3,s),w=a(w,4,u),w=a(w,5,v),w=a(w,6,y),w=a(w,7,G);h.push(w);h.push(t.a,t.b,t.c);s&&(s=this.faceVertexUvs[0][g],h.push(d(s[0]),d(s[1]),d(s[2])));u&&h.push(b(t.normal));v&&(u=t.vertexNormals,h.push(b(u[0]),b(u[1]),b(u[2])));y&&h.push(c(t.color));G&&(t=t.vertexColors,h.push(c(t[0]),c(t[1]),c(t[2])))}e.data={};e.data.vertices=f;e.data.normals=k;0<p.length&&(e.data.colors=
-p);0<m.length&&(e.data.uvs=[m]);e.data.faces=h;return e},clone:function(){for(var a=new THREE.Geometry,b=this.vertices,c=0,d=b.length;c<d;c++)a.vertices.push(b[c].clone());b=this.faces;c=0;for(d=b.length;c<d;c++)a.faces.push(b[c].clone());b=this.faceVertexUvs[0];c=0;for(d=b.length;c<d;c++){for(var e=b[c],f=[],g=0,h=e.length;g<h;g++)f.push(new THREE.Vector2(e[g].x,e[g].y));a.faceVertexUvs[0].push(f)}return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.Geometry.prototype);
-THREE.GeometryIdCount=0;THREE.Camera=function(){THREE.Object3D.call(this);this.type="Camera";this.matrixWorldInverse=new THREE.Matrix4;this.projectionMatrix=new THREE.Matrix4};THREE.Camera.prototype=Object.create(THREE.Object3D.prototype);THREE.Camera.prototype.getWorldDirection=function(){var a=new THREE.Quaternion;return function(b){b=b||new THREE.Vector3;this.getWorldQuaternion(a);return b.set(0,0,-1).applyQuaternion(a)}}();
-THREE.Camera.prototype.lookAt=function(){var a=new THREE.Matrix4;return function(b){a.lookAt(this.position,b,this.up);this.quaternion.setFromRotationMatrix(a)}}();THREE.Camera.prototype.clone=function(a){void 0===a&&(a=new THREE.Camera);THREE.Object3D.prototype.clone.call(this,a);a.matrixWorldInverse.copy(this.matrixWorldInverse);a.projectionMatrix.copy(this.projectionMatrix);return a};
-THREE.CubeCamera=function(a,b,c){THREE.Object3D.call(this);this.type="CubeCamera";var d=new THREE.PerspectiveCamera(90,1,a,b);d.up.set(0,-1,0);d.lookAt(new THREE.Vector3(1,0,0));this.add(d);var e=new THREE.PerspectiveCamera(90,1,a,b);e.up.set(0,-1,0);e.lookAt(new THREE.Vector3(-1,0,0));this.add(e);var f=new THREE.PerspectiveCamera(90,1,a,b);f.up.set(0,0,1);f.lookAt(new THREE.Vector3(0,1,0));this.add(f);var g=new THREE.PerspectiveCamera(90,1,a,b);g.up.set(0,0,-1);g.lookAt(new THREE.Vector3(0,-1,0));
-this.add(g);var h=new THREE.PerspectiveCamera(90,1,a,b);h.up.set(0,-1,0);h.lookAt(new THREE.Vector3(0,0,1));this.add(h);var k=new THREE.PerspectiveCamera(90,1,a,b);k.up.set(0,-1,0);k.lookAt(new THREE.Vector3(0,0,-1));this.add(k);this.renderTarget=new THREE.WebGLRenderTargetCube(c,c,{format:THREE.RGBFormat,magFilter:THREE.LinearFilter,minFilter:THREE.LinearFilter});this.updateCubeMap=function(a,b){var c=this.renderTarget,m=c.generateMipmaps;c.generateMipmaps=!1;c.activeCubeFace=0;a.render(b,d,c);c.activeCubeFace=
-1;a.render(b,e,c);c.activeCubeFace=2;a.render(b,f,c);c.activeCubeFace=3;a.render(b,g,c);c.activeCubeFace=4;a.render(b,h,c);c.generateMipmaps=m;c.activeCubeFace=5;a.render(b,k,c)}};THREE.CubeCamera.prototype=Object.create(THREE.Object3D.prototype);THREE.OrthographicCamera=function(a,b,c,d,e,f){THREE.Camera.call(this);this.type="OrthographicCamera";this.zoom=1;this.left=a;this.right=b;this.top=c;this.bottom=d;this.near=void 0!==e?e:.1;this.far=void 0!==f?f:2E3;this.updateProjectionMatrix()};
-THREE.OrthographicCamera.prototype=Object.create(THREE.Camera.prototype);THREE.OrthographicCamera.prototype.updateProjectionMatrix=function(){var a=(this.right-this.left)/(2*this.zoom),b=(this.top-this.bottom)/(2*this.zoom),c=(this.right+this.left)/2,d=(this.top+this.bottom)/2;this.projectionMatrix.makeOrthographic(c-a,c+a,d+b,d-b,this.near,this.far)};
-THREE.OrthographicCamera.prototype.clone=function(){var a=new THREE.OrthographicCamera;THREE.Camera.prototype.clone.call(this,a);a.zoom=this.zoom;a.left=this.left;a.right=this.right;a.top=this.top;a.bottom=this.bottom;a.near=this.near;a.far=this.far;a.projectionMatrix.copy(this.projectionMatrix);return a};
-THREE.PerspectiveCamera=function(a,b,c,d){THREE.Camera.call(this);this.type="PerspectiveCamera";this.zoom=1;this.fov=void 0!==a?a:50;this.aspect=void 0!==b?b:1;this.near=void 0!==c?c:.1;this.far=void 0!==d?d:2E3;this.updateProjectionMatrix()};THREE.PerspectiveCamera.prototype=Object.create(THREE.Camera.prototype);THREE.PerspectiveCamera.prototype.setLens=function(a,b){void 0===b&&(b=24);this.fov=2*THREE.Math.radToDeg(Math.atan(b/(2*a)));this.updateProjectionMatrix()};
-THREE.PerspectiveCamera.prototype.setViewOffset=function(a,b,c,d,e,f){this.fullWidth=a;this.fullHeight=b;this.x=c;this.y=d;this.width=e;this.height=f;this.updateProjectionMatrix()};
-THREE.PerspectiveCamera.prototype.updateProjectionMatrix=function(){var a=THREE.Math.radToDeg(2*Math.atan(Math.tan(.5*THREE.Math.degToRad(this.fov))/this.zoom));if(this.fullWidth){var b=this.fullWidth/this.fullHeight,a=Math.tan(THREE.Math.degToRad(.5*a))*this.near,c=-a,d=b*c,b=Math.abs(b*a-d),c=Math.abs(a-c);this.projectionMatrix.makeFrustum(d+this.x*b/this.fullWidth,d+(this.x+this.width)*b/this.fullWidth,a-(this.y+this.height)*c/this.fullHeight,a-this.y*c/this.fullHeight,this.near,this.far)}else this.projectionMatrix.makePerspective(a,
-this.aspect,this.near,this.far)};THREE.PerspectiveCamera.prototype.clone=function(){var a=new THREE.PerspectiveCamera;THREE.Camera.prototype.clone.call(this,a);a.zoom=this.zoom;a.fov=this.fov;a.aspect=this.aspect;a.near=this.near;a.far=this.far;a.projectionMatrix.copy(this.projectionMatrix);return a};THREE.Light=function(a){THREE.Object3D.call(this);this.type="Light";this.color=new THREE.Color(a)};THREE.Light.prototype=Object.create(THREE.Object3D.prototype);
-THREE.Light.prototype.clone=function(a){void 0===a&&(a=new THREE.Light);THREE.Object3D.prototype.clone.call(this,a);a.color.copy(this.color);return a};THREE.AmbientLight=function(a){THREE.Light.call(this,a);this.type="AmbientLight"};THREE.AmbientLight.prototype=Object.create(THREE.Light.prototype);THREE.AmbientLight.prototype.clone=function(){var a=new THREE.AmbientLight;THREE.Light.prototype.clone.call(this,a);return a};
-THREE.AreaLight=function(a,b){THREE.Light.call(this,a);this.type="AreaLight";this.normal=new THREE.Vector3(0,-1,0);this.right=new THREE.Vector3(1,0,0);this.intensity=void 0!==b?b:1;this.height=this.width=1;this.constantAttenuation=1.5;this.linearAttenuation=.5;this.quadraticAttenuation=.1};THREE.AreaLight.prototype=Object.create(THREE.Light.prototype);
-THREE.DirectionalLight=function(a,b){THREE.Light.call(this,a);this.type="DirectionalLight";this.position.set(0,1,0);this.target=new THREE.Object3D;this.intensity=void 0!==b?b:1;this.onlyShadow=this.castShadow=!1;this.shadowCameraNear=50;this.shadowCameraFar=5E3;this.shadowCameraLeft=-500;this.shadowCameraTop=this.shadowCameraRight=500;this.shadowCameraBottom=-500;this.shadowCameraVisible=!1;this.shadowBias=0;this.shadowDarkness=.5;this.shadowMapHeight=this.shadowMapWidth=512;this.shadowCascade=!1;
-this.shadowCascadeOffset=new THREE.Vector3(0,0,-1E3);this.shadowCascadeCount=2;this.shadowCascadeBias=[0,0,0];this.shadowCascadeWidth=[512,512,512];this.shadowCascadeHeight=[512,512,512];this.shadowCascadeNearZ=[-1,.99,.998];this.shadowCascadeFarZ=[.99,.998,1];this.shadowCascadeArray=[];this.shadowMatrix=this.shadowCamera=this.shadowMapSize=this.shadowMap=null};THREE.DirectionalLight.prototype=Object.create(THREE.Light.prototype);
-THREE.DirectionalLight.prototype.clone=function(){var a=new THREE.DirectionalLight;THREE.Light.prototype.clone.call(this,a);a.target=this.target.clone();a.intensity=this.intensity;a.castShadow=this.castShadow;a.onlyShadow=this.onlyShadow;a.shadowCameraNear=this.shadowCameraNear;a.shadowCameraFar=this.shadowCameraFar;a.shadowCameraLeft=this.shadowCameraLeft;a.shadowCameraRight=this.shadowCameraRight;a.shadowCameraTop=this.shadowCameraTop;a.shadowCameraBottom=this.shadowCameraBottom;a.shadowCameraVisible=
-this.shadowCameraVisible;a.shadowBias=this.shadowBias;a.shadowDarkness=this.shadowDarkness;a.shadowMapWidth=this.shadowMapWidth;a.shadowMapHeight=this.shadowMapHeight;a.shadowCascade=this.shadowCascade;a.shadowCascadeOffset.copy(this.shadowCascadeOffset);a.shadowCascadeCount=this.shadowCascadeCount;a.shadowCascadeBias=this.shadowCascadeBias.slice(0);a.shadowCascadeWidth=this.shadowCascadeWidth.slice(0);a.shadowCascadeHeight=this.shadowCascadeHeight.slice(0);a.shadowCascadeNearZ=this.shadowCascadeNearZ.slice(0);
-a.shadowCascadeFarZ=this.shadowCascadeFarZ.slice(0);return a};THREE.HemisphereLight=function(a,b,c){THREE.Light.call(this,a);this.type="HemisphereLight";this.position.set(0,100,0);this.groundColor=new THREE.Color(b);this.intensity=void 0!==c?c:1};THREE.HemisphereLight.prototype=Object.create(THREE.Light.prototype);
-THREE.HemisphereLight.prototype.clone=function(){var a=new THREE.HemisphereLight;THREE.Light.prototype.clone.call(this,a);a.groundColor.copy(this.groundColor);a.intensity=this.intensity;return a};THREE.PointLight=function(a,b,c){THREE.Light.call(this,a);this.type="PointLight";this.intensity=void 0!==b?b:1;this.distance=void 0!==c?c:0};THREE.PointLight.prototype=Object.create(THREE.Light.prototype);
-THREE.PointLight.prototype.clone=function(){var a=new THREE.PointLight;THREE.Light.prototype.clone.call(this,a);a.intensity=this.intensity;a.distance=this.distance;return a};
-THREE.SpotLight=function(a,b,c,d,e){THREE.Light.call(this,a);this.type="SpotLight";this.position.set(0,1,0);this.target=new THREE.Object3D;this.intensity=void 0!==b?b:1;this.distance=void 0!==c?c:0;this.angle=void 0!==d?d:Math.PI/3;this.exponent=void 0!==e?e:10;this.onlyShadow=this.castShadow=!1;this.shadowCameraNear=50;this.shadowCameraFar=5E3;this.shadowCameraFov=50;this.shadowCameraVisible=!1;this.shadowBias=0;this.shadowDarkness=.5;this.shadowMapHeight=this.shadowMapWidth=512;this.shadowMatrix=
-this.shadowCamera=this.shadowMapSize=this.shadowMap=null};THREE.SpotLight.prototype=Object.create(THREE.Light.prototype);
-THREE.SpotLight.prototype.clone=function(){var a=new THREE.SpotLight;THREE.Light.prototype.clone.call(this,a);a.target=this.target.clone();a.intensity=this.intensity;a.distance=this.distance;a.angle=this.angle;a.exponent=this.exponent;a.castShadow=this.castShadow;a.onlyShadow=this.onlyShadow;a.shadowCameraNear=this.shadowCameraNear;a.shadowCameraFar=this.shadowCameraFar;a.shadowCameraFov=this.shadowCameraFov;a.shadowCameraVisible=this.shadowCameraVisible;a.shadowBias=this.shadowBias;a.shadowDarkness=
-this.shadowDarkness;a.shadowMapWidth=this.shadowMapWidth;a.shadowMapHeight=this.shadowMapHeight;return a};THREE.Cache=function(){this.files={}};THREE.Cache.prototype={constructor:THREE.Cache,add:function(a,b){this.files[a]=b},get:function(a){return this.files[a]},remove:function(a){delete this.files[a]},clear:function(){this.files={}}};
-THREE.Loader=function(a){this.statusDomElement=(this.showStatus=a)?THREE.Loader.prototype.addStatusElement():null;this.imageLoader=new THREE.ImageLoader;this.onLoadStart=function(){};this.onLoadProgress=function(){};this.onLoadComplete=function(){}};
-THREE.Loader.prototype={constructor:THREE.Loader,crossOrigin:void 0,addStatusElement:function(){var a=document.createElement("div");a.style.position="absolute";a.style.right="0px";a.style.top="0px";a.style.fontSize="0.8em";a.style.textAlign="left";a.style.background="rgba(0,0,0,0.25)";a.style.color="#fff";a.style.width="120px";a.style.padding="0.5em 0.5em 0.5em 0.5em";a.style.zIndex=1E3;a.innerHTML="Loading ...";return a},updateProgress:function(a){var b="Loaded ",b=a.total?b+((100*a.loaded/a.total).toFixed(0)+
-"%"):b+((a.loaded/1024).toFixed(2)+" KB");this.statusDomElement.innerHTML=b},extractUrlBase:function(a){a=a.split("/");if(1===a.length)return"./";a.pop();return a.join("/")+"/"},initMaterials:function(a,b){for(var c=[],d=0;d<a.length;++d)c[d]=this.createMaterial(a[d],b);return c},needsTangents:function(a){for(var b=0,c=a.length;b<c;b++)if(a[b]instanceof THREE.ShaderMaterial)return!0;return!1},createMaterial:function(a,b){function c(a){a=Math.log(a)/Math.LN2;return Math.pow(2,Math.round(a))}function d(a,
-d,e,g,h,k,s){var u=b+e,v,y=THREE.Loader.Handlers.get(u);null!==y?v=y.load(u):(v=new THREE.Texture,y=f.imageLoader,y.crossOrigin=f.crossOrigin,y.load(u,function(a){if(!1===THREE.Math.isPowerOfTwo(a.width)||!1===THREE.Math.isPowerOfTwo(a.height)){var b=c(a.width),d=c(a.height),e=document.createElement("canvas");e.width=b;e.height=d;e.getContext("2d").drawImage(a,0,0,b,d);v.image=e}else v.image=a;v.needsUpdate=!0}));v.sourceFile=e;g&&(v.repeat.set(g[0],g[1]),1!==g[0]&&(v.wrapS=THREE.RepeatWrapping),
-1!==g[1]&&(v.wrapT=THREE.RepeatWrapping));h&&v.offset.set(h[0],h[1]);k&&(e={repeat:THREE.RepeatWrapping,mirror:THREE.MirroredRepeatWrapping},void 0!==e[k[0]]&&(v.wrapS=e[k[0]]),void 0!==e[k[1]]&&(v.wrapT=e[k[1]]));s&&(v.anisotropy=s);a[d]=v}function e(a){return(255*a[0]<<16)+(255*a[1]<<8)+255*a[2]}var f=this,g="MeshLambertMaterial",h={color:15658734,opacity:1,map:null,lightMap:null,normalMap:null,bumpMap:null,wireframe:!1};if(a.shading){var k=a.shading.toLowerCase();"phong"===k?g="MeshPhongMaterial":
-"basic"===k&&(g="MeshBasicMaterial")}void 0!==a.blending&&void 0!==THREE[a.blending]&&(h.blending=THREE[a.blending]);if(void 0!==a.transparent||1>a.opacity)h.transparent=a.transparent;void 0!==a.depthTest&&(h.depthTest=a.depthTest);void 0!==a.depthWrite&&(h.depthWrite=a.depthWrite);void 0!==a.visible&&(h.visible=a.visible);void 0!==a.flipSided&&(h.side=THREE.BackSide);void 0!==a.doubleSided&&(h.side=THREE.DoubleSide);void 0!==a.wireframe&&(h.wireframe=a.wireframe);void 0!==a.vertexColors&&("face"===
-a.vertexColors?h.vertexColors=THREE.FaceColors:a.vertexColors&&(h.vertexColors=THREE.VertexColors));a.colorDiffuse?h.color=e(a.colorDiffuse):a.DbgColor&&(h.color=a.DbgColor);a.colorSpecular&&(h.specular=e(a.colorSpecular));a.colorAmbient&&(h.ambient=e(a.colorAmbient));a.colorEmissive&&(h.emissive=e(a.colorEmissive));a.transparency&&(h.opacity=a.transparency);a.specularCoef&&(h.shininess=a.specularCoef);a.mapDiffuse&&b&&d(h,"map",a.mapDiffuse,a.mapDiffuseRepeat,a.mapDiffuseOffset,a.mapDiffuseWrap,
-a.mapDiffuseAnisotropy);a.mapLight&&b&&d(h,"lightMap",a.mapLight,a.mapLightRepeat,a.mapLightOffset,a.mapLightWrap,a.mapLightAnisotropy);a.mapBump&&b&&d(h,"bumpMap",a.mapBump,a.mapBumpRepeat,a.mapBumpOffset,a.mapBumpWrap,a.mapBumpAnisotropy);a.mapNormal&&b&&d(h,"normalMap",a.mapNormal,a.mapNormalRepeat,a.mapNormalOffset,a.mapNormalWrap,a.mapNormalAnisotropy);a.mapSpecular&&b&&d(h,"specularMap",a.mapSpecular,a.mapSpecularRepeat,a.mapSpecularOffset,a.mapSpecularWrap,a.mapSpecularAnisotropy);a.mapAlpha&&
-b&&d(h,"alphaMap",a.mapAlpha,a.mapAlphaRepeat,a.mapAlphaOffset,a.mapAlphaWrap,a.mapAlphaAnisotropy);a.mapBumpScale&&(h.bumpScale=a.mapBumpScale);a.mapNormal?(g=THREE.ShaderLib.normalmap,k=THREE.UniformsUtils.clone(g.uniforms),k.tNormal.value=h.normalMap,a.mapNormalFactor&&k.uNormalScale.value.set(a.mapNormalFactor,a.mapNormalFactor),h.map&&(k.tDiffuse.value=h.map,k.enableDiffuse.value=!0),h.specularMap&&(k.tSpecular.value=h.specularMap,k.enableSpecular.value=!0),h.lightMap&&(k.tAO.value=h.lightMap,
-k.enableAO.value=!0),k.diffuse.value.setHex(h.color),k.specular.value.setHex(h.specular),k.ambient.value.setHex(h.ambient),k.shininess.value=h.shininess,void 0!==h.opacity&&(k.opacity.value=h.opacity),g=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,vertexShader:g.vertexShader,uniforms:k,lights:!0,fog:!0}),h.transparent&&(g.transparent=!0)):g=new THREE[g](h);void 0!==a.DbgName&&(g.name=a.DbgName);return g}};
-THREE.Loader.Handlers={handlers:[],add:function(a,b){this.handlers.push(a,b)},get:function(a){for(var b=0,c=this.handlers.length;b<c;b+=2){var d=this.handlers[b+1];if(this.handlers[b].test(a))return d}return null}};THREE.XHRLoader=function(a){this.cache=new THREE.Cache;this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
-THREE.XHRLoader.prototype={constructor:THREE.XHRLoader,load:function(a,b,c,d){var e=this,f=e.cache.get(a);void 0!==f?b&&b(f):(f=new XMLHttpRequest,f.open("GET",a,!0),f.addEventListener("load",function(c){e.cache.add(a,this.response);b&&b(this.response);e.manager.itemEnd(a)},!1),void 0!==c&&f.addEventListener("progress",function(a){c(a)},!1),void 0!==d&&f.addEventListener("error",function(a){d(a)},!1),void 0!==this.crossOrigin&&(f.crossOrigin=this.crossOrigin),void 0!==this.responseType&&(f.responseType=
-this.responseType),f.send(null),e.manager.itemStart(a))},setResponseType:function(a){this.responseType=a},setCrossOrigin:function(a){this.crossOrigin=a}};THREE.ImageLoader=function(a){this.cache=new THREE.Cache;this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
-THREE.ImageLoader.prototype={constructor:THREE.ImageLoader,load:function(a,b,c,d){var e=this,f=e.cache.get(a);if(void 0!==f)b(f);else return f=document.createElement("img"),void 0!==b&&f.addEventListener("load",function(c){e.cache.add(a,this);b(this);e.manager.itemEnd(a)},!1),void 0!==c&&f.addEventListener("progress",function(a){c(a)},!1),void 0!==d&&f.addEventListener("error",function(a){d(a)},!1),void 0!==this.crossOrigin&&(f.crossOrigin=this.crossOrigin),f.src=a,e.manager.itemStart(a),f},setCrossOrigin:function(a){this.crossOrigin=
-a}};THREE.JSONLoader=function(a){THREE.Loader.call(this,a);this.withCredentials=!1};THREE.JSONLoader.prototype=Object.create(THREE.Loader.prototype);THREE.JSONLoader.prototype.load=function(a,b,c){c=c&&"string"===typeof c?c:this.extractUrlBase(a);this.onLoadStart();this.loadAjaxJSON(this,a,b,c)};
-THREE.JSONLoader.prototype.loadAjaxJSON=function(a,b,c,d,e){var f=new XMLHttpRequest,g=0;f.onreadystatechange=function(){if(f.readyState===f.DONE)if(200===f.status||0===f.status){if(f.responseText){var h=JSON.parse(f.responseText);if(void 0!==h.metadata&&"scene"===h.metadata.type){console.error('THREE.JSONLoader: "'+b+'" seems to be a Scene. Use THREE.SceneLoader instead.');return}h=a.parse(h,d);c(h.geometry,h.materials)}else console.error('THREE.JSONLoader: "'+b+'" seems to be unreachable or the file is empty.');
-a.onLoadComplete()}else console.error("THREE.JSONLoader: Couldn't load \""+b+'" ('+f.status+")");else f.readyState===f.LOADING?e&&(0===g&&(g=f.getResponseHeader("Content-Length")),e({total:g,loaded:f.responseText.length})):f.readyState===f.HEADERS_RECEIVED&&void 0!==e&&(g=f.getResponseHeader("Content-Length"))};f.open("GET",b,!0);f.withCredentials=this.withCredentials;f.send(null)};
-THREE.JSONLoader.prototype.parse=function(a,b){var c=new THREE.Geometry,d=void 0!==a.scale?1/a.scale:1;(function(b){var d,g,h,k,n,p,q,m,r,t,s,u,v,y=a.faces;p=a.vertices;var G=a.normals,w=a.colors,K=0;if(void 0!==a.uvs){for(d=0;d<a.uvs.length;d++)a.uvs[d].length&&K++;for(d=0;d<K;d++)c.faceVertexUvs[d]=[]}k=0;for(n=p.length;k<n;)d=new THREE.Vector3,d.x=p[k++]*b,d.y=p[k++]*b,d.z=p[k++]*b,c.vertices.push(d);k=0;for(n=y.length;k<n;)if(b=y[k++],r=b&1,h=b&2,d=b&8,q=b&16,t=b&32,p=b&64,b&=128,r){r=new THREE.Face3;
-r.a=y[k];r.b=y[k+1];r.c=y[k+3];s=new THREE.Face3;s.a=y[k+1];s.b=y[k+2];s.c=y[k+3];k+=4;h&&(h=y[k++],r.materialIndex=h,s.materialIndex=h);h=c.faces.length;if(d)for(d=0;d<K;d++)for(u=a.uvs[d],c.faceVertexUvs[d][h]=[],c.faceVertexUvs[d][h+1]=[],g=0;4>g;g++)m=y[k++],v=u[2*m],m=u[2*m+1],v=new THREE.Vector2(v,m),2!==g&&c.faceVertexUvs[d][h].push(v),0!==g&&c.faceVertexUvs[d][h+1].push(v);q&&(q=3*y[k++],r.normal.set(G[q++],G[q++],G[q]),s.normal.copy(r.normal));if(t)for(d=0;4>d;d++)q=3*y[k++],t=new THREE.Vector3(G[q++],
-G[q++],G[q]),2!==d&&r.vertexNormals.push(t),0!==d&&s.vertexNormals.push(t);p&&(p=y[k++],p=w[p],r.color.setHex(p),s.color.setHex(p));if(b)for(d=0;4>d;d++)p=y[k++],p=w[p],2!==d&&r.vertexColors.push(new THREE.Color(p)),0!==d&&s.vertexColors.push(new THREE.Color(p));c.faces.push(r);c.faces.push(s)}else{r=new THREE.Face3;r.a=y[k++];r.b=y[k++];r.c=y[k++];h&&(h=y[k++],r.materialIndex=h);h=c.faces.length;if(d)for(d=0;d<K;d++)for(u=a.uvs[d],c.faceVertexUvs[d][h]=[],g=0;3>g;g++)m=y[k++],v=u[2*m],m=u[2*m+1],
-v=new THREE.Vector2(v,m),c.faceVertexUvs[d][h].push(v);q&&(q=3*y[k++],r.normal.set(G[q++],G[q++],G[q]));if(t)for(d=0;3>d;d++)q=3*y[k++],t=new THREE.Vector3(G[q++],G[q++],G[q]),r.vertexNormals.push(t);p&&(p=y[k++],r.color.setHex(w[p]));if(b)for(d=0;3>d;d++)p=y[k++],r.vertexColors.push(new THREE.Color(w[p]));c.faces.push(r)}})(d);(function(){var b=void 0!==a.influencesPerVertex?a.influencesPerVertex:2;if(a.skinWeights)for(var d=0,g=a.skinWeights.length;d<g;d+=b)c.skinWeights.push(new THREE.Vector4(a.skinWeights[d],
-1<b?a.skinWeights[d+1]:0,2<b?a.skinWeights[d+2]:0,3<b?a.skinWeights[d+3]:0));if(a.skinIndices)for(d=0,g=a.skinIndices.length;d<g;d+=b)c.skinIndices.push(new THREE.Vector4(a.skinIndices[d],1<b?a.skinIndices[d+1]:0,2<b?a.skinIndices[d+2]:0,3<b?a.skinIndices[d+3]:0));c.bones=a.bones;c.bones&&0<c.bones.length&&(c.skinWeights.length!==c.skinIndices.length||c.skinIndices.length!==c.vertices.length)&&console.warn("When skinning, number of vertices ("+c.vertices.length+"), skinIndices ("+c.skinIndices.length+
-"), and skinWeights ("+c.skinWeights.length+") should match.");c.animation=a.animation;c.animations=a.animations})();(function(b){if(void 0!==a.morphTargets){var d,g,h,k,n,p;d=0;for(g=a.morphTargets.length;d<g;d++)for(c.morphTargets[d]={},c.morphTargets[d].name=a.morphTargets[d].name,c.morphTargets[d].vertices=[],n=c.morphTargets[d].vertices,p=a.morphTargets[d].vertices,h=0,k=p.length;h<k;h+=3){var q=new THREE.Vector3;q.x=p[h]*b;q.y=p[h+1]*b;q.z=p[h+2]*b;n.push(q)}}if(void 0!==a.morphColors)for(d=
-0,g=a.morphColors.length;d<g;d++)for(c.morphColors[d]={},c.morphColors[d].name=a.morphColors[d].name,c.morphColors[d].colors=[],k=c.morphColors[d].colors,n=a.morphColors[d].colors,b=0,h=n.length;b<h;b+=3)p=new THREE.Color(16755200),p.setRGB(n[b],n[b+1],n[b+2]),k.push(p)})(d);c.computeFaceNormals();c.computeBoundingSphere();if(void 0===a.materials||0===a.materials.length)return{geometry:c};d=this.initMaterials(a.materials,b);this.needsTangents(d)&&c.computeTangents();return{geometry:c,materials:d}};
-THREE.LoadingManager=function(a,b,c){var d=this,e=0,f=0;this.onLoad=a;this.onProgress=b;this.onError=c;this.itemStart=function(a){f++};this.itemEnd=function(a){e++;if(void 0!==d.onProgress)d.onProgress(a,e,f);if(e===f&&void 0!==d.onLoad)d.onLoad()}};THREE.DefaultLoadingManager=new THREE.LoadingManager;THREE.BufferGeometryLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
-THREE.BufferGeometryLoader.prototype={constructor:THREE.BufferGeometryLoader,load:function(a,b,c,d){var e=this,f=new THREE.XHRLoader;f.setCrossOrigin(this.crossOrigin);f.load(a,function(a){b(e.parse(JSON.parse(a)))},c,d)},setCrossOrigin:function(a){this.crossOrigin=a},parse:function(a){var b=new THREE.BufferGeometry,c=a.attributes,d;for(d in c){var e=c[d],f=new self[e.type](e.array);b.addAttribute(d,new THREE.BufferAttribute(f,e.itemSize))}c=a.offsets;void 0!==c&&(b.offsets=JSON.parse(JSON.stringify(c)));
-a=a.boundingSphere;void 0!==a&&(c=new THREE.Vector3,void 0!==a.center&&c.fromArray(a.center),b.boundingSphere=new THREE.Sphere(c,a.radius));return b}};THREE.MaterialLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
-THREE.MaterialLoader.prototype={constructor:THREE.MaterialLoader,load:function(a,b,c,d){var e=this,f=new THREE.XHRLoader;f.setCrossOrigin(this.crossOrigin);f.load(a,function(a){b(e.parse(JSON.parse(a)))},c,d)},setCrossOrigin:function(a){this.crossOrigin=a},parse:function(a){var b=new THREE[a.type];void 0!==a.color&&b.color.setHex(a.color);void 0!==a.ambient&&b.ambient.setHex(a.ambient);void 0!==a.emissive&&b.emissive.setHex(a.emissive);void 0!==a.specular&&b.specular.setHex(a.specular);void 0!==a.shininess&&
-(b.shininess=a.shininess);void 0!==a.uniforms&&(b.uniforms=a.uniforms);void 0!==a.vertexShader&&(b.vertexShader=a.vertexShader);void 0!==a.fragmentShader&&(b.fragmentShader=a.fragmentShader);void 0!==a.vertexColors&&(b.vertexColors=a.vertexColors);void 0!==a.shading&&(b.shading=a.shading);void 0!==a.blending&&(b.blending=a.blending);void 0!==a.side&&(b.side=a.side);void 0!==a.opacity&&(b.opacity=a.opacity);void 0!==a.transparent&&(b.transparent=a.transparent);void 0!==a.wireframe&&(b.wireframe=a.wireframe);
-if(void 0!==a.materials)for(var c=0,d=a.materials.length;c<d;c++)b.materials.push(this.parse(a.materials[c]));return b}};THREE.ObjectLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
-THREE.ObjectLoader.prototype={constructor:THREE.ObjectLoader,load:function(a,b,c,d){var e=this,f=new THREE.XHRLoader(e.manager);f.setCrossOrigin(this.crossOrigin);f.load(a,function(a){b(e.parse(JSON.parse(a)))},c,d)},setCrossOrigin:function(a){this.crossOrigin=a},parse:function(a){var b=this.parseGeometries(a.geometries),c=this.parseMaterials(a.materials);return this.parseObject(a.object,b,c)},parseGeometries:function(a){var b={};if(void 0!==a)for(var c=new THREE.JSONLoader,d=new THREE.BufferGeometryLoader,
-e=0,f=a.length;e<f;e++){var g,h=a[e];switch(h.type){case "PlaneGeometry":g=new THREE.PlaneGeometry(h.width,h.height,h.widthSegments,h.heightSegments);break;case "BoxGeometry":case "CubeGeometry":g=new THREE.BoxGeometry(h.width,h.height,h.depth,h.widthSegments,h.heightSegments,h.depthSegments);break;case "CircleGeometry":g=new THREE.CircleGeometry(h.radius,h.segments);break;case "CylinderGeometry":g=new THREE.CylinderGeometry(h.radiusTop,h.radiusBottom,h.height,h.radialSegments,h.heightSegments,h.openEnded);
-break;case "SphereGeometry":g=new THREE.SphereGeometry(h.radius,h.widthSegments,h.heightSegments,h.phiStart,h.phiLength,h.thetaStart,h.thetaLength);break;case "IcosahedronGeometry":g=new THREE.IcosahedronGeometry(h.radius,h.detail);break;case "TorusGeometry":g=new THREE.TorusGeometry(h.radius,h.tube,h.radialSegments,h.tubularSegments,h.arc);break;case "TorusKnotGeometry":g=new THREE.TorusKnotGeometry(h.radius,h.tube,h.radialSegments,h.tubularSegments,h.p,h.q,h.heightScale);break;case "BufferGeometry":g=
-d.parse(h.data);break;case "Geometry":g=c.parse(h.data).geometry}g.uuid=h.uuid;void 0!==h.name&&(g.name=h.name);b[h.uuid]=g}return b},parseMaterials:function(a){var b={};if(void 0!==a)for(var c=new THREE.MaterialLoader,d=0,e=a.length;d<e;d++){var f=a[d],g=c.parse(f);g.uuid=f.uuid;void 0!==f.name&&(g.name=f.name);b[f.uuid]=g}return b},parseObject:function(){var a=new THREE.Matrix4;return function(b,c,d){var e;switch(b.type){case "Scene":e=new THREE.Scene;break;case "PerspectiveCamera":e=new THREE.PerspectiveCamera(b.fov,
-b.aspect,b.near,b.far);break;case "OrthographicCamera":e=new THREE.OrthographicCamera(b.left,b.right,b.top,b.bottom,b.near,b.far);break;case "AmbientLight":e=new THREE.AmbientLight(b.color);break;case "DirectionalLight":e=new THREE.DirectionalLight(b.color,b.intensity);break;case "PointLight":e=new THREE.PointLight(b.color,b.intensity,b.distance);break;case "SpotLight":e=new THREE.SpotLight(b.color,b.intensity,b.distance,b.angle,b.exponent);break;case "HemisphereLight":e=new THREE.HemisphereLight(b.color,
-b.groundColor,b.intensity);break;case "Mesh":e=c[b.geometry];var f=d[b.material];void 0===e&&console.warn("THREE.ObjectLoader: Undefined geometry",b.geometry);void 0===f&&console.warn("THREE.ObjectLoader: Undefined material",b.material);e=new THREE.Mesh(e,f);break;case "Line":e=c[b.geometry];f=d[b.material];void 0===e&&console.warn("THREE.ObjectLoader: Undefined geometry",b.geometry);void 0===f&&console.warn("THREE.ObjectLoader: Undefined material",b.material);e=new THREE.Line(e,f);break;case "Sprite":f=
-d[b.material];void 0===f&&console.warn("THREE.ObjectLoader: Undefined material",b.material);e=new THREE.Sprite(f);break;case "Group":e=new THREE.Group;break;default:e=new THREE.Object3D}e.uuid=b.uuid;void 0!==b.name&&(e.name=b.name);void 0!==b.matrix?(a.fromArray(b.matrix),a.decompose(e.position,e.quaternion,e.scale)):(void 0!==b.position&&e.position.fromArray(b.position),void 0!==b.rotation&&e.rotation.fromArray(b.rotation),void 0!==b.scale&&e.scale.fromArray(b.scale));void 0!==b.visible&&(e.visible=
-b.visible);void 0!==b.userData&&(e.userData=b.userData);if(void 0!==b.children)for(var g in b.children)e.add(this.parseObject(b.children[g],c,d));return e}}()};THREE.TextureLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
-THREE.TextureLoader.prototype={constructor:THREE.TextureLoader,load:function(a,b,c,d){var e=new THREE.ImageLoader(this.manager);e.setCrossOrigin(this.crossOrigin);e.load(a,function(a){a=new THREE.Texture(a);a.needsUpdate=!0;void 0!==b&&b(a)},c,d)},setCrossOrigin:function(a){this.crossOrigin=a}};THREE.CompressedTextureLoader=function(){this._parser=null};
-THREE.CompressedTextureLoader.prototype={constructor:THREE.CompressedTextureLoader,load:function(a,b,c){var d=this,e=[],f=new THREE.CompressedTexture;f.image=e;var g=new THREE.XHRLoader;g.setResponseType("arraybuffer");if(a instanceof Array){var h=0;c=function(c){g.load(a[c],function(a){a=d._parser(a,!0);e[c]={width:a.width,height:a.height,format:a.format,mipmaps:a.mipmaps};h+=1;6===h&&(1==a.mipmapCount&&(f.minFilter=THREE.LinearFilter),f.format=a.format,f.needsUpdate=!0,b&&b(f))})};for(var k=0,n=
-a.length;k<n;++k)c(k)}else g.load(a,function(a){a=d._parser(a,!0);if(a.isCubemap)for(var c=a.mipmaps.length/a.mipmapCount,g=0;g<c;g++){e[g]={mipmaps:[]};for(var h=0;h<a.mipmapCount;h++)e[g].mipmaps.push(a.mipmaps[g*a.mipmapCount+h]),e[g].format=a.format,e[g].width=a.width,e[g].height=a.height}else f.image.width=a.width,f.image.height=a.height,f.mipmaps=a.mipmaps;1===a.mipmapCount&&(f.minFilter=THREE.LinearFilter);f.format=a.format;f.needsUpdate=!0;b&&b(f)});return f}};
-THREE.Material=function(){Object.defineProperty(this,"id",{value:THREE.MaterialIdCount++});this.uuid=THREE.Math.generateUUID();this.name="";this.type="Material";this.side=THREE.FrontSide;this.opacity=1;this.transparent=!1;this.blending=THREE.NormalBlending;this.blendSrc=THREE.SrcAlphaFactor;this.blendDst=THREE.OneMinusSrcAlphaFactor;this.blendEquation=THREE.AddEquation;this.depthWrite=this.depthTest=!0;this.polygonOffset=!1;this.overdraw=this.alphaTest=this.polygonOffsetUnits=this.polygonOffsetFactor=
-0;this.needsUpdate=this.visible=!0};
-THREE.Material.prototype={constructor:THREE.Material,setValues:function(a){if(void 0!==a)for(var b in a){var c=a[b];if(void 0===c)console.warn("THREE.Material: '"+b+"' parameter is undefined.");else if(b in this){var d=this[b];d instanceof THREE.Color?d.set(c):d instanceof THREE.Vector3&&c instanceof THREE.Vector3?d.copy(c):this[b]="overdraw"==b?Number(c):c}}},toJSON:function(){var a={metadata:{version:4.2,type:"material",generator:"MaterialExporter"},uuid:this.uuid,type:this.type};""!==this.name&&
-(a.name=this.name);this instanceof THREE.MeshBasicMaterial?(a.color=this.color.getHex(),this.vertexColors!==THREE.NoColors&&(a.vertexColors=this.vertexColors),this.blending!==THREE.NormalBlending&&(a.blending=this.blending),this.side!==THREE.FrontSide&&(a.side=this.side)):this instanceof THREE.MeshLambertMaterial?(a.color=this.color.getHex(),a.ambient=this.ambient.getHex(),a.emissive=this.emissive.getHex(),this.vertexColors!==THREE.NoColors&&(a.vertexColors=this.vertexColors),this.blending!==THREE.NormalBlending&&
-(a.blending=this.blending),this.side!==THREE.FrontSide&&(a.side=this.side)):this instanceof THREE.MeshPhongMaterial?(a.color=this.color.getHex(),a.ambient=this.ambient.getHex(),a.emissive=this.emissive.getHex(),a.specular=this.specular.getHex(),a.shininess=this.shininess,this.vertexColors!==THREE.NoColors&&(a.vertexColors=this.vertexColors),this.blending!==THREE.NormalBlending&&(a.blending=this.blending),this.side!==THREE.FrontSide&&(a.side=this.side)):this instanceof THREE.MeshNormalMaterial?(this.shading!==
-THREE.FlatShading&&(a.shading=this.shading),this.blending!==THREE.NormalBlending&&(a.blending=this.blending),this.side!==THREE.FrontSide&&(a.side=this.side)):this instanceof THREE.MeshDepthMaterial?(this.blending!==THREE.NormalBlending&&(a.blending=this.blending),this.side!==THREE.FrontSide&&(a.side=this.side)):this instanceof THREE.ShaderMaterial?(a.uniforms=this.uniforms,a.vertexShader=this.vertexShader,a.fragmentShader=this.fragmentShader):this instanceof THREE.SpriteMaterial&&(a.color=this.color.getHex());
-1>this.opacity&&(a.opacity=this.opacity);!1!==this.transparent&&(a.transparent=this.transparent);!1!==this.wireframe&&(a.wireframe=this.wireframe);return a},clone:function(a){void 0===a&&(a=new THREE.Material);a.name=this.name;a.side=this.side;a.opacity=this.opacity;a.transparent=this.transparent;a.blending=this.blending;a.blendSrc=this.blendSrc;a.blendDst=this.blendDst;a.blendEquation=this.blendEquation;a.depthTest=this.depthTest;a.depthWrite=this.depthWrite;a.polygonOffset=this.polygonOffset;a.polygonOffsetFactor=
-this.polygonOffsetFactor;a.polygonOffsetUnits=this.polygonOffsetUnits;a.alphaTest=this.alphaTest;a.overdraw=this.overdraw;a.visible=this.visible;return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.Material.prototype);THREE.MaterialIdCount=0;
-THREE.LineBasicMaterial=function(a){THREE.Material.call(this);this.type="LineBasicMaterial";this.color=new THREE.Color(16777215);this.linewidth=1;this.linejoin=this.linecap="round";this.vertexColors=THREE.NoColors;this.fog=!0;this.setValues(a)};THREE.LineBasicMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.LineBasicMaterial.prototype.clone=function(){var a=new THREE.LineBasicMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.linewidth=this.linewidth;a.linecap=this.linecap;a.linejoin=this.linejoin;a.vertexColors=this.vertexColors;a.fog=this.fog;return a};
-THREE.LineDashedMaterial=function(a){THREE.Material.call(this);this.type="LineDashedMaterial";this.color=new THREE.Color(16777215);this.scale=this.linewidth=1;this.dashSize=3;this.gapSize=1;this.vertexColors=!1;this.fog=!0;this.setValues(a)};THREE.LineDashedMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.LineDashedMaterial.prototype.clone=function(){var a=new THREE.LineDashedMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.linewidth=this.linewidth;a.scale=this.scale;a.dashSize=this.dashSize;a.gapSize=this.gapSize;a.vertexColors=this.vertexColors;a.fog=this.fog;return a};
-THREE.MeshBasicMaterial=function(a){THREE.Material.call(this);this.type="MeshBasicMaterial";this.color=new THREE.Color(16777215);this.envMap=this.alphaMap=this.specularMap=this.lightMap=this.map=null;this.combine=THREE.MultiplyOperation;this.reflectivity=1;this.refractionRatio=.98;this.fog=!0;this.shading=THREE.SmoothShading;this.wireframe=!1;this.wireframeLinewidth=1;this.wireframeLinejoin=this.wireframeLinecap="round";this.vertexColors=THREE.NoColors;this.morphTargets=this.skinning=!1;this.setValues(a)};
-THREE.MeshBasicMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.MeshBasicMaterial.prototype.clone=function(){var a=new THREE.MeshBasicMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.map=this.map;a.lightMap=this.lightMap;a.specularMap=this.specularMap;a.alphaMap=this.alphaMap;a.envMap=this.envMap;a.combine=this.combine;a.reflectivity=this.reflectivity;a.refractionRatio=this.refractionRatio;a.fog=this.fog;a.shading=this.shading;a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;a.wireframeLinecap=this.wireframeLinecap;
-a.wireframeLinejoin=this.wireframeLinejoin;a.vertexColors=this.vertexColors;a.skinning=this.skinning;a.morphTargets=this.morphTargets;return a};
-THREE.MeshLambertMaterial=function(a){THREE.Material.call(this);this.type="MeshLambertMaterial";this.color=new THREE.Color(16777215);this.ambient=new THREE.Color(16777215);this.emissive=new THREE.Color(0);this.wrapAround=!1;this.wrapRGB=new THREE.Vector3(1,1,1);this.envMap=this.alphaMap=this.specularMap=this.lightMap=this.map=null;this.combine=THREE.MultiplyOperation;this.reflectivity=1;this.refractionRatio=.98;this.fog=!0;this.shading=THREE.SmoothShading;this.wireframe=!1;this.wireframeLinewidth=
-1;this.wireframeLinejoin=this.wireframeLinecap="round";this.vertexColors=THREE.NoColors;this.morphNormals=this.morphTargets=this.skinning=!1;this.setValues(a)};THREE.MeshLambertMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.MeshLambertMaterial.prototype.clone=function(){var a=new THREE.MeshLambertMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.ambient.copy(this.ambient);a.emissive.copy(this.emissive);a.wrapAround=this.wrapAround;a.wrapRGB.copy(this.wrapRGB);a.map=this.map;a.lightMap=this.lightMap;a.specularMap=this.specularMap;a.alphaMap=this.alphaMap;a.envMap=this.envMap;a.combine=this.combine;a.reflectivity=this.reflectivity;a.refractionRatio=this.refractionRatio;a.fog=this.fog;
-a.shading=this.shading;a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;a.wireframeLinecap=this.wireframeLinecap;a.wireframeLinejoin=this.wireframeLinejoin;a.vertexColors=this.vertexColors;a.skinning=this.skinning;a.morphTargets=this.morphTargets;a.morphNormals=this.morphNormals;return a};
-THREE.MeshPhongMaterial=function(a){THREE.Material.call(this);this.type="MeshPhongMaterial";this.color=new THREE.Color(16777215);this.ambient=new THREE.Color(16777215);this.emissive=new THREE.Color(0);this.specular=new THREE.Color(1118481);this.shininess=30;this.wrapAround=this.metal=!1;this.wrapRGB=new THREE.Vector3(1,1,1);this.bumpMap=this.lightMap=this.map=null;this.bumpScale=1;this.normalMap=null;this.normalScale=new THREE.Vector2(1,1);this.envMap=this.alphaMap=this.specularMap=null;this.combine=
-THREE.MultiplyOperation;this.reflectivity=1;this.refractionRatio=.98;this.fog=!0;this.shading=THREE.SmoothShading;this.wireframe=!1;this.wireframeLinewidth=1;this.wireframeLinejoin=this.wireframeLinecap="round";this.vertexColors=THREE.NoColors;this.morphNormals=this.morphTargets=this.skinning=!1;this.setValues(a)};THREE.MeshPhongMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.MeshPhongMaterial.prototype.clone=function(){var a=new THREE.MeshPhongMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.ambient.copy(this.ambient);a.emissive.copy(this.emissive);a.specular.copy(this.specular);a.shininess=this.shininess;a.metal=this.metal;a.wrapAround=this.wrapAround;a.wrapRGB.copy(this.wrapRGB);a.map=this.map;a.lightMap=this.lightMap;a.bumpMap=this.bumpMap;a.bumpScale=this.bumpScale;a.normalMap=this.normalMap;a.normalScale.copy(this.normalScale);
-a.specularMap=this.specularMap;a.alphaMap=this.alphaMap;a.envMap=this.envMap;a.combine=this.combine;a.reflectivity=this.reflectivity;a.refractionRatio=this.refractionRatio;a.fog=this.fog;a.shading=this.shading;a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;a.wireframeLinecap=this.wireframeLinecap;a.wireframeLinejoin=this.wireframeLinejoin;a.vertexColors=this.vertexColors;a.skinning=this.skinning;a.morphTargets=this.morphTargets;a.morphNormals=this.morphNormals;return a};
-THREE.MeshDepthMaterial=function(a){THREE.Material.call(this);this.type="MeshDepthMaterial";this.wireframe=this.morphTargets=!1;this.wireframeLinewidth=1;this.setValues(a)};THREE.MeshDepthMaterial.prototype=Object.create(THREE.Material.prototype);THREE.MeshDepthMaterial.prototype.clone=function(){var a=new THREE.MeshDepthMaterial;THREE.Material.prototype.clone.call(this,a);a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;return a};
-THREE.MeshNormalMaterial=function(a){THREE.Material.call(this,a);this.type="MeshNormalMaterial";this.shading=THREE.FlatShading;this.wireframe=!1;this.wireframeLinewidth=1;this.morphTargets=!1;this.setValues(a)};THREE.MeshNormalMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.MeshNormalMaterial.prototype.clone=function(){var a=new THREE.MeshNormalMaterial;THREE.Material.prototype.clone.call(this,a);a.shading=this.shading;a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;return a};THREE.MeshFaceMaterial=function(a){this.uuid=THREE.Math.generateUUID();this.type="MeshFaceMaterial";this.materials=a instanceof Array?a:[]};
-THREE.MeshFaceMaterial.prototype={constructor:THREE.MeshFaceMaterial,toJSON:function(){for(var a={metadata:{version:4.2,type:"material",generator:"MaterialExporter"},uuid:this.uuid,type:this.type,materials:[]},b=0,c=this.materials.length;b<c;b++)a.materials.push(this.materials[b].toJSON());return a},clone:function(){for(var a=new THREE.MeshFaceMaterial,b=0;b<this.materials.length;b++)a.materials.push(this.materials[b].clone());return a}};
-THREE.PointCloudMaterial=function(a){THREE.Material.call(this);this.type="PointCloudMaterial";this.color=new THREE.Color(16777215);this.map=null;this.size=1;this.sizeAttenuation=!0;this.vertexColors=THREE.NoColors;this.fog=!0;this.setValues(a)};THREE.PointCloudMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.PointCloudMaterial.prototype.clone=function(){var a=new THREE.PointCloudMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.map=this.map;a.size=this.size;a.sizeAttenuation=this.sizeAttenuation;a.vertexColors=this.vertexColors;a.fog=this.fog;return a};THREE.ParticleBasicMaterial=function(a){console.warn("THREE.ParticleBasicMaterial has been renamed to THREE.PointCloudMaterial.");return new THREE.PointCloudMaterial(a)};
-THREE.ParticleSystemMaterial=function(a){console.warn("THREE.ParticleSystemMaterial has been renamed to THREE.PointCloudMaterial.");return new THREE.PointCloudMaterial(a)};
-THREE.ShaderMaterial=function(a){THREE.Material.call(this);this.type="ShaderMaterial";this.defines={};this.uniforms={};this.attributes=null;this.vertexShader="void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}";this.fragmentShader="void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}";this.shading=THREE.SmoothShading;this.linewidth=1;this.wireframe=!1;this.wireframeLinewidth=1;this.lights=this.fog=!1;this.vertexColors=THREE.NoColors;this.morphNormals=
-this.morphTargets=this.skinning=!1;this.defaultAttributeValues={color:[1,1,1],uv:[0,0],uv2:[0,0]};this.index0AttributeName=void 0;this.setValues(a)};THREE.ShaderMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.ShaderMaterial.prototype.clone=function(){var a=new THREE.ShaderMaterial;THREE.Material.prototype.clone.call(this,a);a.fragmentShader=this.fragmentShader;a.vertexShader=this.vertexShader;a.uniforms=THREE.UniformsUtils.clone(this.uniforms);a.attributes=this.attributes;a.defines=this.defines;a.shading=this.shading;a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;a.fog=this.fog;a.lights=this.lights;a.vertexColors=this.vertexColors;a.skinning=this.skinning;a.morphTargets=
-this.morphTargets;a.morphNormals=this.morphNormals;return a};THREE.RawShaderMaterial=function(a){THREE.ShaderMaterial.call(this,a);this.type="RawShaderMaterial"};THREE.RawShaderMaterial.prototype=Object.create(THREE.ShaderMaterial.prototype);THREE.RawShaderMaterial.prototype.clone=function(){var a=new THREE.RawShaderMaterial;THREE.ShaderMaterial.prototype.clone.call(this,a);return a};
-THREE.SpriteMaterial=function(a){THREE.Material.call(this);this.type="SpriteMaterial";this.color=new THREE.Color(16777215);this.map=null;this.rotation=0;this.fog=!1;this.setValues(a)};THREE.SpriteMaterial.prototype=Object.create(THREE.Material.prototype);THREE.SpriteMaterial.prototype.clone=function(){var a=new THREE.SpriteMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.map=this.map;a.rotation=this.rotation;a.fog=this.fog;return a};
-THREE.Texture=function(a,b,c,d,e,f,g,h,k){Object.defineProperty(this,"id",{value:THREE.TextureIdCount++});this.uuid=THREE.Math.generateUUID();this.name="";this.image=void 0!==a?a:THREE.Texture.DEFAULT_IMAGE;this.mipmaps=[];this.mapping=void 0!==b?b:THREE.Texture.DEFAULT_MAPPING;this.wrapS=void 0!==c?c:THREE.ClampToEdgeWrapping;this.wrapT=void 0!==d?d:THREE.ClampToEdgeWrapping;this.magFilter=void 0!==e?e:THREE.LinearFilter;this.minFilter=void 0!==f?f:THREE.LinearMipMapLinearFilter;this.anisotropy=
-void 0!==k?k:1;this.format=void 0!==g?g:THREE.RGBAFormat;this.type=void 0!==h?h:THREE.UnsignedByteType;this.offset=new THREE.Vector2(0,0);this.repeat=new THREE.Vector2(1,1);this.generateMipmaps=!0;this.premultiplyAlpha=!1;this.flipY=!0;this.unpackAlignment=4;this._needsUpdate=!1;this.onUpdate=null};THREE.Texture.DEFAULT_IMAGE=void 0;THREE.Texture.DEFAULT_MAPPING=new THREE.UVMapping;
-THREE.Texture.prototype={constructor:THREE.Texture,get needsUpdate(){return this._needsUpdate},set needsUpdate(a){!0===a&&this.update();this._needsUpdate=a},clone:function(a){void 0===a&&(a=new THREE.Texture);a.image=this.image;a.mipmaps=this.mipmaps.slice(0);a.mapping=this.mapping;a.wrapS=this.wrapS;a.wrapT=this.wrapT;a.magFilter=this.magFilter;a.minFilter=this.minFilter;a.anisotropy=this.anisotropy;a.format=this.format;a.type=this.type;a.offset.copy(this.offset);a.repeat.copy(this.repeat);a.generateMipmaps=
-this.generateMipmaps;a.premultiplyAlpha=this.premultiplyAlpha;a.flipY=this.flipY;a.unpackAlignment=this.unpackAlignment;return a},update:function(){this.dispatchEvent({type:"update"})},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.Texture.prototype);THREE.TextureIdCount=0;THREE.CubeTexture=function(a,b,c,d,e,f,g,h,k){THREE.Texture.call(this,a,b,c,d,e,f,g,h,k);this.images=a};THREE.CubeTexture.prototype=Object.create(THREE.Texture.prototype);
-THREE.CubeTexture.clone=function(a){void 0===a&&(a=new THREE.CubeTexture);THREE.Texture.prototype.clone.call(this,a);a.images=this.images;return a};THREE.CompressedTexture=function(a,b,c,d,e,f,g,h,k,n,p){THREE.Texture.call(this,null,f,g,h,k,n,d,e,p);this.image={width:b,height:c};this.mipmaps=a;this.generateMipmaps=this.flipY=!1};THREE.CompressedTexture.prototype=Object.create(THREE.Texture.prototype);
-THREE.CompressedTexture.prototype.clone=function(){var a=new THREE.CompressedTexture;THREE.Texture.prototype.clone.call(this,a);return a};THREE.DataTexture=function(a,b,c,d,e,f,g,h,k,n,p){THREE.Texture.call(this,null,f,g,h,k,n,d,e,p);this.image={data:a,width:b,height:c}};THREE.DataTexture.prototype=Object.create(THREE.Texture.prototype);THREE.DataTexture.prototype.clone=function(){var a=new THREE.DataTexture;THREE.Texture.prototype.clone.call(this,a);return a};
-THREE.VideoTexture=function(a,b,c,d,e,f,g,h,k){THREE.Texture.call(this,a,b,c,d,e,f,g,h,k);this.generateMipmaps=!1;var n=this,p=function(){requestAnimationFrame(p);a.readyState===a.HAVE_ENOUGH_DATA&&(n.needsUpdate=!0)};p()};THREE.VideoTexture.prototype=Object.create(THREE.Texture.prototype);THREE.Group=function(){THREE.Object3D.call(this);this.type="Group"};THREE.Group.prototype=Object.create(THREE.Object3D.prototype);
-THREE.PointCloud=function(a,b){THREE.Object3D.call(this);this.type="PointCloud";this.geometry=void 0!==a?a:new THREE.Geometry;this.material=void 0!==b?b:new THREE.PointCloudMaterial({color:16777215*Math.random()});this.sortParticles=!1};THREE.PointCloud.prototype=Object.create(THREE.Object3D.prototype);
-THREE.PointCloud.prototype.raycast=function(){var a=new THREE.Matrix4,b=new THREE.Ray;return function(c,d){var e=this,f=e.geometry,g=c.params.PointCloud.threshold;a.getInverse(this.matrixWorld);b.copy(c.ray).applyMatrix4(a);if(null===f.boundingBox||!1!==b.isIntersectionBox(f.boundingBox)){var h=g/((this.scale.x+this.scale.y+this.scale.z)/3),k=new THREE.Vector3,g=function(a,f){var g=b.distanceToPoint(a);if(g<h){var k=b.closestPointToPoint(a);k.applyMatrix4(e.matrixWorld);var m=c.ray.origin.distanceTo(k);
-d.push({distance:m,distanceToRay:g,point:k.clone(),index:f,face:null,object:e})}};if(f instanceof THREE.BufferGeometry){var n=f.attributes,p=n.position.array;if(void 0!==n.index){var n=n.index.array,q=f.offsets;0===q.length&&(q=[{start:0,count:n.length,index:0}]);for(var m=0,r=q.length;m<r;++m)for(var t=q[m].start,s=q[m].index,f=t,t=t+q[m].count;f<t;f++){var u=s+n[f];k.fromArray(p,3*u);g(k,u)}}else for(n=p.length/3,f=0;f<n;f++)k.set(p[3*f],p[3*f+1],p[3*f+2]),g(k,f)}else for(k=this.geometry.vertices,
-f=0;f<k.length;f++)g(k[f],f)}}}();THREE.PointCloud.prototype.clone=function(a){void 0===a&&(a=new THREE.PointCloud(this.geometry,this.material));a.sortParticles=this.sortParticles;THREE.Object3D.prototype.clone.call(this,a);return a};THREE.ParticleSystem=function(a,b){console.warn("THREE.ParticleSystem has been renamed to THREE.PointCloud.");return new THREE.PointCloud(a,b)};
-THREE.Line=function(a,b,c){THREE.Object3D.call(this);this.type="Line";this.geometry=void 0!==a?a:new THREE.Geometry;this.material=void 0!==b?b:new THREE.LineBasicMaterial({color:16777215*Math.random()});this.mode=void 0!==c?c:THREE.LineStrip};THREE.LineStrip=0;THREE.LinePieces=1;THREE.Line.prototype=Object.create(THREE.Object3D.prototype);
-THREE.Line.prototype.raycast=function(){var a=new THREE.Matrix4,b=new THREE.Ray,c=new THREE.Sphere;return function(d,e){var f=d.linePrecision,f=f*f,g=this.geometry;null===g.boundingSphere&&g.computeBoundingSphere();c.copy(g.boundingSphere);c.applyMatrix4(this.matrixWorld);if(!1!==d.ray.isIntersectionSphere(c)&&(a.getInverse(this.matrixWorld),b.copy(d.ray).applyMatrix4(a),g instanceof THREE.Geometry))for(var g=g.vertices,h=g.length,k=new THREE.Vector3,n=new THREE.Vector3,p=this.mode===THREE.LineStrip?
-1:2,q=0;q<h-1;q+=p)if(!(b.distanceSqToSegment(g[q],g[q+1],n,k)>f)){var m=b.origin.distanceTo(n);m<d.near||m>d.far||e.push({distance:m,point:k.clone().applyMatrix4(this.matrixWorld),face:null,faceIndex:null,object:this})}}}();THREE.Line.prototype.clone=function(a){void 0===a&&(a=new THREE.Line(this.geometry,this.material,this.mode));THREE.Object3D.prototype.clone.call(this,a);return a};
-THREE.Mesh=function(a,b){THREE.Object3D.call(this);this.type="Mesh";this.geometry=void 0!==a?a:new THREE.Geometry;this.material=void 0!==b?b:new THREE.MeshBasicMaterial({color:16777215*Math.random()});this.updateMorphTargets()};THREE.Mesh.prototype=Object.create(THREE.Object3D.prototype);
-THREE.Mesh.prototype.updateMorphTargets=function(){if(void 0!==this.geometry.morphTargets&&0<this.geometry.morphTargets.length){this.morphTargetBase=-1;this.morphTargetForcedOrder=[];this.morphTargetInfluences=[];this.morphTargetDictionary={};for(var a=0,b=this.geometry.morphTargets.length;a<b;a++)this.morphTargetInfluences.push(0),this.morphTargetDictionary[this.geometry.morphTargets[a].name]=a}};
-THREE.Mesh.prototype.getMorphTargetIndexByName=function(a){if(void 0!==this.morphTargetDictionary[a])return this.morphTargetDictionary[a];console.log("THREE.Mesh.getMorphTargetIndexByName: morph target "+a+" does not exist. Returning 0.");return 0};
-THREE.Mesh.prototype.raycast=function(){var a=new THREE.Matrix4,b=new THREE.Ray,c=new THREE.Sphere,d=new THREE.Vector3,e=new THREE.Vector3,f=new THREE.Vector3;return function(g,h){var k=this.geometry;null===k.boundingSphere&&k.computeBoundingSphere();c.copy(k.boundingSphere);c.applyMatrix4(this.matrixWorld);if(!1!==g.ray.isIntersectionSphere(c)&&(a.getInverse(this.matrixWorld),b.copy(g.ray).applyMatrix4(a),null===k.boundingBox||!1!==b.isIntersectionBox(k.boundingBox)))if(k instanceof THREE.BufferGeometry){var n=
-this.material;if(void 0!==n){var p=k.attributes,q,m,r=g.precision;if(void 0!==p.index){var t=p.index.array,s=p.position.array,u=k.offsets;0===u.length&&(u=[{start:0,count:t.length,index:0}]);for(var v=0,y=u.length;v<y;++v)for(var p=u[v].start,G=u[v].index,k=p,w=p+u[v].count;k<w;k+=3){p=G+t[k];q=G+t[k+1];m=G+t[k+2];d.fromArray(s,3*p);e.fromArray(s,3*q);f.fromArray(s,3*m);var K=n.side===THREE.BackSide?b.intersectTriangle(f,e,d,!0):b.intersectTriangle(d,e,f,n.side!==THREE.DoubleSide);if(null!==K){K.applyMatrix4(this.matrixWorld);
-var x=g.ray.origin.distanceTo(K);x<r||x<g.near||x>g.far||h.push({distance:x,point:K,face:new THREE.Face3(p,q,m,THREE.Triangle.normal(d,e,f)),faceIndex:null,object:this})}}}else for(s=p.position.array,t=k=0,w=s.length;k<w;k+=3,t+=9)p=k,q=k+1,m=k+2,d.fromArray(s,t),e.fromArray(s,t+3),f.fromArray(s,t+6),K=n.side===THREE.BackSide?b.intersectTriangle(f,e,d,!0):b.intersectTriangle(d,e,f,n.side!==THREE.DoubleSide),null!==K&&(K.applyMatrix4(this.matrixWorld),x=g.ray.origin.distanceTo(K),x<r||x<g.near||x>
-g.far||h.push({distance:x,point:K,face:new THREE.Face3(p,q,m,THREE.Triangle.normal(d,e,f)),faceIndex:null,object:this}))}}else if(k instanceof THREE.Geometry)for(t=this.material instanceof THREE.MeshFaceMaterial,s=!0===t?this.material.materials:null,r=g.precision,u=k.vertices,v=0,y=k.faces.length;v<y;v++)if(G=k.faces[v],n=!0===t?s[G.materialIndex]:this.material,void 0!==n){p=u[G.a];q=u[G.b];m=u[G.c];if(!0===n.morphTargets){K=k.morphTargets;x=this.morphTargetInfluences;d.set(0,0,0);e.set(0,0,0);f.set(0,
-0,0);for(var w=0,D=K.length;w<D;w++){var E=x[w];if(0!==E){var A=K[w].vertices;d.x+=(A[G.a].x-p.x)*E;d.y+=(A[G.a].y-p.y)*E;d.z+=(A[G.a].z-p.z)*E;e.x+=(A[G.b].x-q.x)*E;e.y+=(A[G.b].y-q.y)*E;e.z+=(A[G.b].z-q.z)*E;f.x+=(A[G.c].x-m.x)*E;f.y+=(A[G.c].y-m.y)*E;f.z+=(A[G.c].z-m.z)*E}}d.add(p);e.add(q);f.add(m);p=d;q=e;m=f}K=n.side===THREE.BackSide?b.intersectTriangle(m,q,p,!0):b.intersectTriangle(p,q,m,n.side!==THREE.DoubleSide);null!==K&&(K.applyMatrix4(this.matrixWorld),x=g.ray.origin.distanceTo(K),x<r||
-x<g.near||x>g.far||h.push({distance:x,point:K,face:G,faceIndex:v,object:this}))}}}();THREE.Mesh.prototype.clone=function(a,b){void 0===a&&(a=new THREE.Mesh(this.geometry,this.material));THREE.Object3D.prototype.clone.call(this,a,b);return a};THREE.Bone=function(a){THREE.Object3D.call(this);this.skin=a};THREE.Bone.prototype=Object.create(THREE.Object3D.prototype);
-THREE.Skeleton=function(a,b,c){this.useVertexTexture=void 0!==c?c:!0;this.identityMatrix=new THREE.Matrix4;a=a||[];this.bones=a.slice(0);this.useVertexTexture?(this.boneTextureHeight=this.boneTextureWidth=a=256<this.bones.length?64:64<this.bones.length?32:16<this.bones.length?16:8,this.boneMatrices=new Float32Array(this.boneTextureWidth*this.boneTextureHeight*4),this.boneTexture=new THREE.DataTexture(this.boneMatrices,this.boneTextureWidth,this.boneTextureHeight,THREE.RGBAFormat,THREE.FloatType),
-this.boneTexture.minFilter=THREE.NearestFilter,this.boneTexture.magFilter=THREE.NearestFilter,this.boneTexture.generateMipmaps=!1,this.boneTexture.flipY=!1):this.boneMatrices=new Float32Array(16*this.bones.length);if(void 0===b)this.calculateInverses();else if(this.bones.length===b.length)this.boneInverses=b.slice(0);else for(console.warn("THREE.Skeleton bonInverses is the wrong length."),this.boneInverses=[],b=0,a=this.bones.length;b<a;b++)this.boneInverses.push(new THREE.Matrix4)};
-THREE.Skeleton.prototype.calculateInverses=function(){this.boneInverses=[];for(var a=0,b=this.bones.length;a<b;a++){var c=new THREE.Matrix4;this.bones[a]&&c.getInverse(this.bones[a].matrixWorld);this.boneInverses.push(c)}};
-THREE.Skeleton.prototype.pose=function(){for(var a,b=0,c=this.bones.length;b<c;b++)(a=this.bones[b])&&a.matrixWorld.getInverse(this.boneInverses[b]);b=0;for(c=this.bones.length;b<c;b++)if(a=this.bones[b])a.parent?(a.matrix.getInverse(a.parent.matrixWorld),a.matrix.multiply(a.matrixWorld)):a.matrix.copy(a.matrixWorld),a.matrix.decompose(a.position,a.quaternion,a.scale)};
-THREE.Skeleton.prototype.update=function(){var a=new THREE.Matrix4;return function(){for(var b=0,c=this.bones.length;b<c;b++)a.multiplyMatrices(this.bones[b]?this.bones[b].matrixWorld:this.identityMatrix,this.boneInverses[b]),a.flattenToArrayOffset(this.boneMatrices,16*b);this.useVertexTexture&&(this.boneTexture.needsUpdate=!0)}}();
-THREE.SkinnedMesh=function(a,b,c){THREE.Mesh.call(this,a,b);this.type="SkinnedMesh";this.bindMode="attached";this.bindMatrix=new THREE.Matrix4;this.bindMatrixInverse=new THREE.Matrix4;a=[];if(this.geometry&&void 0!==this.geometry.bones){for(var d,e,f,g,h=0,k=this.geometry.bones.length;h<k;++h)d=this.geometry.bones[h],e=d.pos,f=d.rotq,g=d.scl,b=new THREE.Bone(this),a.push(b),b.name=d.name,b.position.set(e[0],e[1],e[2]),b.quaternion.set(f[0],f[1],f[2],f[3]),void 0!==g?b.scale.set(g[0],g[1],g[2]):b.scale.set(1,
-1,1);h=0;for(k=this.geometry.bones.length;h<k;++h)d=this.geometry.bones[h],-1!==d.parent?a[d.parent].add(a[h]):this.add(a[h])}this.normalizeSkinWeights();this.updateMatrixWorld(!0);this.bind(new THREE.Skeleton(a,void 0,c))};THREE.SkinnedMesh.prototype=Object.create(THREE.Mesh.prototype);THREE.SkinnedMesh.prototype.bind=function(a,b){this.skeleton=a;void 0===b&&(this.updateMatrixWorld(!0),b=this.matrixWorld);this.bindMatrix.copy(b);this.bindMatrixInverse.getInverse(b)};
-THREE.SkinnedMesh.prototype.pose=function(){this.skeleton.pose()};THREE.SkinnedMesh.prototype.normalizeSkinWeights=function(){if(this.geometry instanceof THREE.Geometry)for(var a=0;a<this.geometry.skinIndices.length;a++){var b=this.geometry.skinWeights[a],c=1/b.lengthManhattan();Infinity!==c?b.multiplyScalar(c):b.set(1)}};
-THREE.SkinnedMesh.prototype.updateMatrixWorld=function(a){THREE.Mesh.prototype.updateMatrixWorld.call(this,!0);"attached"===this.bindMode?this.bindMatrixInverse.getInverse(this.matrixWorld):"detached"===this.bindMode?this.bindMatrixInverse.getInverse(this.bindMatrix):console.warn("THREE.SkinnedMesh unreckognized bindMode: "+this.bindMode)};
-THREE.SkinnedMesh.prototype.clone=function(a){void 0===a&&(a=new THREE.SkinnedMesh(this.geometry,this.material,this.useVertexTexture));THREE.Mesh.prototype.clone.call(this,a);return a};THREE.MorphAnimMesh=function(a,b){THREE.Mesh.call(this,a,b);this.type="MorphAnimMesh";this.duration=1E3;this.mirroredLoop=!1;this.currentKeyframe=this.lastKeyframe=this.time=0;this.direction=1;this.directionBackwards=!1;this.setFrameRange(0,this.geometry.morphTargets.length-1)};THREE.MorphAnimMesh.prototype=Object.create(THREE.Mesh.prototype);
-THREE.MorphAnimMesh.prototype.setFrameRange=function(a,b){this.startKeyframe=a;this.endKeyframe=b;this.length=this.endKeyframe-this.startKeyframe+1};THREE.MorphAnimMesh.prototype.setDirectionForward=function(){this.direction=1;this.directionBackwards=!1};THREE.MorphAnimMesh.prototype.setDirectionBackward=function(){this.direction=-1;this.directionBackwards=!0};
-THREE.MorphAnimMesh.prototype.parseAnimations=function(){var a=this.geometry;a.animations||(a.animations={});for(var b,c=a.animations,d=/([a-z]+)_?(\d+)/,e=0,f=a.morphTargets.length;e<f;e++){var g=a.morphTargets[e].name.match(d);if(g&&1<g.length){g=g[1];c[g]||(c[g]={start:Infinity,end:-Infinity});var h=c[g];e<h.start&&(h.start=e);e>h.end&&(h.end=e);b||(b=g)}}a.firstAnimation=b};
-THREE.MorphAnimMesh.prototype.setAnimationLabel=function(a,b,c){this.geometry.animations||(this.geometry.animations={});this.geometry.animations[a]={start:b,end:c}};THREE.MorphAnimMesh.prototype.playAnimation=function(a,b){var c=this.geometry.animations[a];c?(this.setFrameRange(c.start,c.end),this.duration=(c.end-c.start)/b*1E3,this.time=0):console.warn("animation["+a+"] undefined")};
-THREE.MorphAnimMesh.prototype.updateAnimation=function(a){var b=this.duration/this.length;this.time+=this.direction*a;if(this.mirroredLoop){if(this.time>this.duration||0>this.time)this.direction*=-1,this.time>this.duration&&(this.time=this.duration,this.directionBackwards=!0),0>this.time&&(this.time=0,this.directionBackwards=!1)}else this.time%=this.duration,0>this.time&&(this.time+=this.duration);a=this.startKeyframe+THREE.Math.clamp(Math.floor(this.time/b),0,this.length-1);a!==this.currentKeyframe&&
-(this.morphTargetInfluences[this.lastKeyframe]=0,this.morphTargetInfluences[this.currentKeyframe]=1,this.morphTargetInfluences[a]=0,this.lastKeyframe=this.currentKeyframe,this.currentKeyframe=a);b=this.time%b/b;this.directionBackwards&&(b=1-b);this.morphTargetInfluences[this.currentKeyframe]=b;this.morphTargetInfluences[this.lastKeyframe]=1-b};
-THREE.MorphAnimMesh.prototype.interpolateTargets=function(a,b,c){for(var d=this.morphTargetInfluences,e=0,f=d.length;e<f;e++)d[e]=0;-1<a&&(d[a]=1-c);-1<b&&(d[b]=c)};
-THREE.MorphAnimMesh.prototype.clone=function(a){void 0===a&&(a=new THREE.MorphAnimMesh(this.geometry,this.material));a.duration=this.duration;a.mirroredLoop=this.mirroredLoop;a.time=this.time;a.lastKeyframe=this.lastKeyframe;a.currentKeyframe=this.currentKeyframe;a.direction=this.direction;a.directionBackwards=this.directionBackwards;THREE.Mesh.prototype.clone.call(this,a);return a};THREE.LOD=function(){THREE.Object3D.call(this);this.objects=[]};THREE.LOD.prototype=Object.create(THREE.Object3D.prototype);
-THREE.LOD.prototype.addLevel=function(a,b){void 0===b&&(b=0);b=Math.abs(b);for(var c=0;c<this.objects.length&&!(b<this.objects[c].distance);c++);this.objects.splice(c,0,{distance:b,object:a});this.add(a)};THREE.LOD.prototype.getObjectForDistance=function(a){for(var b=1,c=this.objects.length;b<c&&!(a<this.objects[b].distance);b++);return this.objects[b-1].object};
-THREE.LOD.prototype.raycast=function(){var a=new THREE.Vector3;return function(b,c){a.setFromMatrixPosition(this.matrixWorld);var d=b.ray.origin.distanceTo(a);this.getObjectForDistance(d).raycast(b,c)}}();
-THREE.LOD.prototype.update=function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c){if(1<this.objects.length){a.setFromMatrixPosition(c.matrixWorld);b.setFromMatrixPosition(this.matrixWorld);c=a.distanceTo(b);this.objects[0].object.visible=!0;for(var d=1,e=this.objects.length;d<e;d++)if(c>=this.objects[d].distance)this.objects[d-1].object.visible=!1,this.objects[d].object.visible=!0;else break;for(;d<e;d++)this.objects[d].object.visible=!1}}}();
-THREE.LOD.prototype.clone=function(a){void 0===a&&(a=new THREE.LOD);THREE.Object3D.prototype.clone.call(this,a);for(var b=0,c=this.objects.length;b<c;b++){var d=this.objects[b].object.clone();d.visible=0===b;a.addLevel(d,this.objects[b].distance)}return a};
-THREE.Sprite=function(){var a=new Uint16Array([0,1,2,0,2,3]),b=new Float32Array([-.5,-.5,0,.5,-.5,0,.5,.5,0,-.5,.5,0]),c=new Float32Array([0,0,1,0,1,1,0,1]),d=new THREE.BufferGeometry;d.addAttribute("index",new THREE.BufferAttribute(a,1));d.addAttribute("position",new THREE.BufferAttribute(b,3));d.addAttribute("uv",new THREE.BufferAttribute(c,2));return function(a){THREE.Object3D.call(this);this.type="Sprite";this.geometry=d;this.material=void 0!==a?a:new THREE.SpriteMaterial}}();
-THREE.Sprite.prototype=Object.create(THREE.Object3D.prototype);THREE.Sprite.prototype.raycast=function(){var a=new THREE.Vector3;return function(b,c){a.setFromMatrixPosition(this.matrixWorld);var d=b.ray.distanceToPoint(a);d>this.scale.x||c.push({distance:d,point:this.position,face:null,object:this})}}();THREE.Sprite.prototype.clone=function(a){void 0===a&&(a=new THREE.Sprite(this.material));THREE.Object3D.prototype.clone.call(this,a);return a};THREE.Particle=THREE.Sprite;
-THREE.LensFlare=function(a,b,c,d,e){THREE.Object3D.call(this);this.lensFlares=[];this.positionScreen=new THREE.Vector3;this.customUpdateCallback=void 0;void 0!==a&&this.add(a,b,c,d,e)};THREE.LensFlare.prototype=Object.create(THREE.Object3D.prototype);
-THREE.LensFlare.prototype.add=function(a,b,c,d,e,f){void 0===b&&(b=-1);void 0===c&&(c=0);void 0===f&&(f=1);void 0===e&&(e=new THREE.Color(16777215));void 0===d&&(d=THREE.NormalBlending);c=Math.min(c,Math.max(0,c));this.lensFlares.push({texture:a,size:b,distance:c,x:0,y:0,z:0,scale:1,rotation:1,opacity:f,color:e,blending:d})};
-THREE.LensFlare.prototype.updateLensFlares=function(){var a,b=this.lensFlares.length,c,d=2*-this.positionScreen.x,e=2*-this.positionScreen.y;for(a=0;a<b;a++)c=this.lensFlares[a],c.x=this.positionScreen.x+d*c.distance,c.y=this.positionScreen.y+e*c.distance,c.wantedRotation=c.x*Math.PI*.25,c.rotation+=.25*(c.wantedRotation-c.rotation)};THREE.Scene=function(){THREE.Object3D.call(this);this.type="Scene";this.overrideMaterial=this.fog=null;this.autoUpdate=!0};THREE.Scene.prototype=Object.create(THREE.Object3D.prototype);
-THREE.Scene.prototype.clone=function(a){void 0===a&&(a=new THREE.Scene);THREE.Object3D.prototype.clone.call(this,a);null!==this.fog&&(a.fog=this.fog.clone());null!==this.overrideMaterial&&(a.overrideMaterial=this.overrideMaterial.clone());a.autoUpdate=this.autoUpdate;a.matrixAutoUpdate=this.matrixAutoUpdate;return a};THREE.Fog=function(a,b,c){this.name="";this.color=new THREE.Color(a);this.near=void 0!==b?b:1;this.far=void 0!==c?c:1E3};
-THREE.Fog.prototype.clone=function(){return new THREE.Fog(this.color.getHex(),this.near,this.far)};THREE.FogExp2=function(a,b){this.name="";this.color=new THREE.Color(a);this.density=void 0!==b?b:2.5E-4};THREE.FogExp2.prototype.clone=function(){return new THREE.FogExp2(this.color.getHex(),this.density)};THREE.ShaderChunk={};THREE.ShaderChunk.alphatest_fragment="#ifdef ALPHATEST\n\n\tif ( gl_FragColor.a < ALPHATEST ) discard;\n\n#endif\n";THREE.ShaderChunk.lights_lambert_vertex="vLightFront = vec3( 0.0 );\n\n#ifdef DOUBLE_SIDED\n\n\tvLightBack = vec3( 0.0 );\n\n#endif\n\ntransformedNormal = normalize( transformedNormal );\n\n#if MAX_DIR_LIGHTS > 0\n\nfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n\tvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\n\tvec3 dirVector = normalize( lDirection.xyz );\n\n\tfloat dotProduct = dot( transformedNormal, dirVector );\n\tvec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n\t#ifdef DOUBLE_SIDED\n\n\t\tvec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n\t\t#ifdef WRAP_AROUND\n\n\t\t\tvec3 directionalLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n\t\t#endif\n\n\t#endif\n\n\t#ifdef WRAP_AROUND\n\n\t\tvec3 directionalLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n\t\tdirectionalLightWeighting = mix( directionalLightWeighting, directionalLightWeightingHalf, wrapRGB );\n\n\t\t#ifdef DOUBLE_SIDED\n\n\t\t\tdirectionalLightWeightingBack = mix( directionalLightWeightingBack, directionalLightWeightingHalfBack, wrapRGB );\n\n\t\t#endif\n\n\t#endif\n\n\tvLightFront += directionalLightColor[ i ] * directionalLightWeighting;\n\n\t#ifdef DOUBLE_SIDED\n\n\t\tvLightBack += directionalLightColor[ i ] * directionalLightWeightingBack;\n\n\t#endif\n\n}\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n\tfor( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n\t\tvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n\t\tvec3 lVector = lPosition.xyz - mvPosition.xyz;\n\n\t\tfloat lDistance = 1.0;\n\t\tif ( pointLightDistance[ i ] > 0.0 )\n\t\t\tlDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\n\n\t\tlVector = normalize( lVector );\n\t\tfloat dotProduct = dot( transformedNormal, lVector );\n\n\t\tvec3 pointLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n\t\t#ifdef DOUBLE_SIDED\n\n\t\t\tvec3 pointLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n\t\t\t#ifdef WRAP_AROUND\n\n\t\t\t\tvec3 pointLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n\t\t\t#endif\n\n\t\t#endif\n\n\t\t#ifdef WRAP_AROUND\n\n\t\t\tvec3 pointLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n\t\t\tpointLightWeighting = mix( pointLightWeighting, pointLightWeightingHalf, wrapRGB );\n\n\t\t\t#ifdef DOUBLE_SIDED\n\n\t\t\t\tpointLightWeightingBack = mix( pointLightWeightingBack, pointLightWeightingHalfBack, wrapRGB );\n\n\t\t\t#endif\n\n\t\t#endif\n\n\t\tvLightFront += pointLightColor[ i ] * pointLightWeighting * lDistance;\n\n\t\t#ifdef DOUBLE_SIDED\n\n\t\t\tvLightBack += pointLightColor[ i ] * pointLightWeightingBack * lDistance;\n\n\t\t#endif\n\n\t}\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n\tfor( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n\t\tvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n\t\tvec3 lVector = lPosition.xyz - mvPosition.xyz;\n\n\t\tfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - worldPosition.xyz ) );\n\n\t\tif ( spotEffect > spotLightAngleCos[ i ] ) {\n\n\t\t\tspotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\n\t\t\tfloat lDistance = 1.0;\n\t\t\tif ( spotLightDistance[ i ] > 0.0 )\n\t\t\t\tlDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\n\n\t\t\tlVector = normalize( lVector );\n\n\t\t\tfloat dotProduct = dot( transformedNormal, lVector );\n\t\t\tvec3 spotLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n\t\t\t#ifdef DOUBLE_SIDED\n\n\t\t\t\tvec3 spotLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n\t\t\t\t#ifdef WRAP_AROUND\n\n\t\t\t\t\tvec3 spotLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n\t\t\t\t#endif\n\n\t\t\t#endif\n\n\t\t\t#ifdef WRAP_AROUND\n\n\t\t\t\tvec3 spotLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n\t\t\t\tspotLightWeighting = mix( spotLightWeighting, spotLightWeightingHalf, wrapRGB );\n\n\t\t\t\t#ifdef DOUBLE_SIDED\n\n\t\t\t\t\tspotLightWeightingBack = mix( spotLightWeightingBack, spotLightWeightingHalfBack, wrapRGB );\n\n\t\t\t\t#endif\n\n\t\t\t#endif\n\n\t\t\tvLightFront += spotLightColor[ i ] * spotLightWeighting * lDistance * spotEffect;\n\n\t\t\t#ifdef DOUBLE_SIDED\n\n\t\t\t\tvLightBack += spotLightColor[ i ] * spotLightWeightingBack * lDistance * spotEffect;\n\n\t\t\t#endif\n\n\t\t}\n\n\t}\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n\tfor( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n\t\tvec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\n\t\tvec3 lVector = normalize( lDirection.xyz );\n\n\t\tfloat dotProduct = dot( transformedNormal, lVector );\n\n\t\tfloat hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\t\tfloat hemiDiffuseWeightBack = -0.5 * dotProduct + 0.5;\n\n\t\tvLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n\t\t#ifdef DOUBLE_SIDED\n\n\t\t\tvLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n\n\t\t#endif\n\n\t}\n\n#endif\n\nvLightFront = vLightFront * diffuse + ambient * ambientLightColor + emissive;\n\n#ifdef DOUBLE_SIDED\n\n\tvLightBack = vLightBack * diffuse + ambient * ambientLightColor + emissive;\n\n#endif";
-THREE.ShaderChunk.map_particle_pars_fragment="#ifdef USE_MAP\n\n\tuniform sampler2D map;\n\n#endif";THREE.ShaderChunk.default_vertex="vec4 mvPosition;\n\n#ifdef USE_SKINNING\n\n\tmvPosition = modelViewMatrix * skinned;\n\n#endif\n\n#if !defined( USE_SKINNING ) && defined( USE_MORPHTARGETS )\n\n\tmvPosition = modelViewMatrix * vec4( morphed, 1.0 );\n\n#endif\n\n#if !defined( USE_SKINNING ) && ! defined( USE_MORPHTARGETS )\n\n\tmvPosition = modelViewMatrix * vec4( position, 1.0 );\n\n#endif\n\ngl_Position = projectionMatrix * mvPosition;";
-THREE.ShaderChunk.map_pars_fragment="#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n\tvarying vec2 vUv;\n\n#endif\n\n#ifdef USE_MAP\n\n\tuniform sampler2D map;\n\n#endif";THREE.ShaderChunk.skinnormal_vertex="#ifdef USE_SKINNING\n\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix  = bindMatrixInverse * skinMatrix * bindMatrix;\n\n\t#ifdef USE_MORPHNORMALS\n\n\tvec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );\n\n\t#else\n\n\tvec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );\n\n\t#endif\n\n#endif\n";
-THREE.ShaderChunk.logdepthbuf_pars_vertex="#ifdef USE_LOGDEPTHBUF\n\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\n\t\tvarying float vFragDepth;\n\n\t#endif\n\n\tuniform float logDepthBufFC;\n\n#endif";THREE.ShaderChunk.lightmap_pars_vertex="#ifdef USE_LIGHTMAP\n\n\tvarying vec2 vUv2;\n\n#endif";THREE.ShaderChunk.lights_phong_fragment="vec3 normal = normalize( vNormal );\nvec3 viewPosition = normalize( vViewPosition );\n\n#ifdef DOUBLE_SIDED\n\n\tnormal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\n#endif\n\n#ifdef USE_NORMALMAP\n\n\tnormal = perturbNormal2Arb( -vViewPosition, normal );\n\n#elif defined( USE_BUMPMAP )\n\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n\tvec3 pointDiffuse = vec3( 0.0 );\n\tvec3 pointSpecular = vec3( 0.0 );\n\n\tfor ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n\t\tvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n\t\tvec3 lVector = lPosition.xyz + vViewPosition.xyz;\n\n\t\tfloat lDistance = 1.0;\n\t\tif ( pointLightDistance[ i ] > 0.0 )\n\t\t\tlDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\n\n\t\tlVector = normalize( lVector );\n\n\t\t\t\t// diffuse\n\n\t\tfloat dotProduct = dot( normal, lVector );\n\n\t\t#ifdef WRAP_AROUND\n\n\t\t\tfloat pointDiffuseWeightFull = max( dotProduct, 0.0 );\n\t\t\tfloat pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n\t\t\tvec3 pointDiffuseWeight = mix( vec3( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );\n\n\t\t#else\n\n\t\t\tfloat pointDiffuseWeight = max( dotProduct, 0.0 );\n\n\t\t#endif\n\n\t\tpointDiffuse += diffuse * pointLightColor[ i ] * pointDiffuseWeight * lDistance;\n\n\t\t\t\t// specular\n\n\t\tvec3 pointHalfVector = normalize( lVector + viewPosition );\n\t\tfloat pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );\n\t\tfloat pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );\n\n\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n\t\tvec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, pointHalfVector ), 0.0 ), 5.0 );\n\t\tpointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance * specularNormalization;\n\n\t}\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n\tvec3 spotDiffuse = vec3( 0.0 );\n\tvec3 spotSpecular = vec3( 0.0 );\n\n\tfor ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n\t\tvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n\t\tvec3 lVector = lPosition.xyz + vViewPosition.xyz;\n\n\t\tfloat lDistance = 1.0;\n\t\tif ( spotLightDistance[ i ] > 0.0 )\n\t\t\tlDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\n\n\t\tlVector = normalize( lVector );\n\n\t\tfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );\n\n\t\tif ( spotEffect > spotLightAngleCos[ i ] ) {\n\n\t\t\tspotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\n\t\t\t\t\t// diffuse\n\n\t\t\tfloat dotProduct = dot( normal, lVector );\n\n\t\t\t#ifdef WRAP_AROUND\n\n\t\t\t\tfloat spotDiffuseWeightFull = max( dotProduct, 0.0 );\n\t\t\t\tfloat spotDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n\t\t\t\tvec3 spotDiffuseWeight = mix( vec3( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );\n\n\t\t\t#else\n\n\t\t\t\tfloat spotDiffuseWeight = max( dotProduct, 0.0 );\n\n\t\t\t#endif\n\n\t\t\tspotDiffuse += diffuse * spotLightColor[ i ] * spotDiffuseWeight * lDistance * spotEffect;\n\n\t\t\t\t\t// specular\n\n\t\t\tvec3 spotHalfVector = normalize( lVector + viewPosition );\n\t\t\tfloat spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );\n\t\t\tfloat spotSpecularWeight = specularStrength * max( pow( spotDotNormalHalf, shininess ), 0.0 );\n\n\t\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n\t\t\tvec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, spotHalfVector ), 0.0 ), 5.0 );\n\t\t\tspotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * specularNormalization * spotEffect;\n\n\t\t}\n\n\t}\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n\tvec3 dirDiffuse = vec3( 0.0 );\n\tvec3 dirSpecular = vec3( 0.0 );\n\n\tfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n\t\tvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\n\t\tvec3 dirVector = normalize( lDirection.xyz );\n\n\t\t\t\t// diffuse\n\n\t\tfloat dotProduct = dot( normal, dirVector );\n\n\t\t#ifdef WRAP_AROUND\n\n\t\t\tfloat dirDiffuseWeightFull = max( dotProduct, 0.0 );\n\t\t\tfloat dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n\t\t\tvec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );\n\n\t\t#else\n\n\t\t\tfloat dirDiffuseWeight = max( dotProduct, 0.0 );\n\n\t\t#endif\n\n\t\tdirDiffuse += diffuse * directionalLightColor[ i ] * dirDiffuseWeight;\n\n\t\t// specular\n\n\t\tvec3 dirHalfVector = normalize( dirVector + viewPosition );\n\t\tfloat dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );\n\t\tfloat dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );\n\n\t\t/*\n\t\t// fresnel term from skin shader\n\t\tconst float F0 = 0.128;\n\n\t\tfloat base = 1.0 - dot( viewPosition, dirHalfVector );\n\t\tfloat exponential = pow( base, 5.0 );\n\n\t\tfloat fresnel = exponential + F0 * ( 1.0 - exponential );\n\t\t*/\n\n\t\t/*\n\t\t// fresnel term from fresnel shader\n\t\tconst float mFresnelBias = 0.08;\n\t\tconst float mFresnelScale = 0.3;\n\t\tconst float mFresnelPower = 5.0;\n\n\t\tfloat fresnel = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( -viewPosition ), normal ), mFresnelPower );\n\t\t*/\n\n\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n\t\t// \t\tdirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization * fresnel;\n\n\t\tvec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( dirVector, dirHalfVector ), 0.0 ), 5.0 );\n\t\tdirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;\n\n\n\t}\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n\tvec3 hemiDiffuse = vec3( 0.0 );\n\tvec3 hemiSpecular = vec3( 0.0 );\n\n\tfor( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n\t\tvec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\n\t\tvec3 lVector = normalize( lDirection.xyz );\n\n\t\t// diffuse\n\n\t\tfloat dotProduct = dot( normal, lVector );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n\t\tvec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n\t\themiDiffuse += diffuse * hemiColor;\n\n\t\t// specular (sky light)\n\n\t\tvec3 hemiHalfVectorSky = normalize( lVector + viewPosition );\n\t\tfloat hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;\n\t\tfloat hemiSpecularWeightSky = specularStrength * max( pow( max( hemiDotNormalHalfSky, 0.0 ), shininess ), 0.0 );\n\n\t\t// specular (ground light)\n\n\t\tvec3 lVectorGround = -lVector;\n\n\t\tvec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );\n\t\tfloat hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;\n\t\tfloat hemiSpecularWeightGround = specularStrength * max( pow( max( hemiDotNormalHalfGround, 0.0 ), shininess ), 0.0 );\n\n\t\tfloat dotProductGround = dot( normal, lVectorGround );\n\n\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n\t\tvec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, hemiHalfVectorSky ), 0.0 ), 5.0 );\n\t\tvec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 0.0 ), 5.0 );\n\t\themiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );\n\n\t}\n\n#endif\n\nvec3 totalDiffuse = vec3( 0.0 );\nvec3 totalSpecular = vec3( 0.0 );\n\n#if MAX_DIR_LIGHTS > 0\n\n\ttotalDiffuse += dirDiffuse;\n\ttotalSpecular += dirSpecular;\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n\ttotalDiffuse += hemiDiffuse;\n\ttotalSpecular += hemiSpecular;\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n\ttotalDiffuse += pointDiffuse;\n\ttotalSpecular += pointSpecular;\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n\ttotalDiffuse += spotDiffuse;\n\ttotalSpecular += spotSpecular;\n\n#endif\n\n#ifdef METAL\n\n\tgl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient + totalSpecular );\n\n#else\n\n\tgl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;\n\n#endif";
-THREE.ShaderChunk.fog_pars_fragment="#ifdef USE_FOG\n\n\tuniform vec3 fogColor;\n\n\t#ifdef FOG_EXP2\n\n\t\tuniform float fogDensity;\n\n\t#else\n\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n\n#endif";THREE.ShaderChunk.morphnormal_vertex="#ifdef USE_MORPHNORMALS\n\n\tvec3 morphedNormal = vec3( 0.0 );\n\n\tmorphedNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n\tmorphedNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n\tmorphedNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n\tmorphedNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n\n\tmorphedNormal += normal;\n\n#endif";
-THREE.ShaderChunk.envmap_pars_fragment="#ifdef USE_ENVMAP\n\n\tuniform float reflectivity;\n\tuniform samplerCube envMap;\n\tuniform float flipEnvMap;\n\tuniform int combine;\n\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n\t\tuniform bool useRefract;\n\t\tuniform float refractionRatio;\n\n\t#else\n\n\t\tvarying vec3 vReflect;\n\n\t#endif\n\n#endif";THREE.ShaderChunk.logdepthbuf_fragment="#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\n\tgl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n\n#endif";
-THREE.ShaderChunk.normalmap_pars_fragment="#ifdef USE_NORMALMAP\n\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n\n\t\t\t// Per-Pixel Tangent Space Normal Mapping\n\t\t\t// http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html\n\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\n\t\tvec3 q0 = dFdx( eye_pos.xyz );\n\t\tvec3 q1 = dFdy( eye_pos.xyz );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\n\t\tvec3 S = normalize( q0 * st1.t - q1 * st0.t );\n\t\tvec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n\t\tvec3 N = normalize( surf_norm );\n\n\t\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\tmapN.xy = normalScale * mapN.xy;\n\t\tmat3 tsn = mat3( S, T, N );\n\t\treturn normalize( tsn * mapN );\n\n\t}\n\n#endif\n";
-THREE.ShaderChunk.lights_phong_pars_vertex="#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n\tvarying vec3 vWorldPosition;\n\n#endif\n";THREE.ShaderChunk.lightmap_pars_fragment="#ifdef USE_LIGHTMAP\n\n\tvarying vec2 vUv2;\n\tuniform sampler2D lightMap;\n\n#endif";THREE.ShaderChunk.shadowmap_vertex="#ifdef USE_SHADOWMAP\n\n\tfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n\t\tvShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n\n\t}\n\n#endif";
-THREE.ShaderChunk.lights_phong_vertex="#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n\tvWorldPosition = worldPosition.xyz;\n\n#endif";THREE.ShaderChunk.map_fragment="#ifdef USE_MAP\n\n\tvec4 texelColor = texture2D( map, vUv );\n\n\t#ifdef GAMMA_INPUT\n\n\t\ttexelColor.xyz *= texelColor.xyz;\n\n\t#endif\n\n\tgl_FragColor = gl_FragColor * texelColor;\n\n#endif";THREE.ShaderChunk.lightmap_vertex="#ifdef USE_LIGHTMAP\n\n\tvUv2 = uv2;\n\n#endif";
-THREE.ShaderChunk.map_particle_fragment="#ifdef USE_MAP\n\n\tgl_FragColor = gl_FragColor * texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) );\n\n#endif";THREE.ShaderChunk.color_pars_fragment="#ifdef USE_COLOR\n\n\tvarying vec3 vColor;\n\n#endif\n";THREE.ShaderChunk.color_vertex="#ifdef USE_COLOR\n\n\t#ifdef GAMMA_INPUT\n\n\t\tvColor = color * color;\n\n\t#else\n\n\t\tvColor = color;\n\n\t#endif\n\n#endif";THREE.ShaderChunk.skinning_vertex="#ifdef USE_SKINNING\n\n\t#ifdef USE_MORPHTARGETS\n\n\tvec4 skinVertex = bindMatrix * vec4( morphed, 1.0 );\n\n\t#else\n\n\tvec4 skinVertex = bindMatrix * vec4( position, 1.0 );\n\n\t#endif\n\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\tskinned  = bindMatrixInverse * skinned;\n\n#endif\n";
-THREE.ShaderChunk.envmap_pars_vertex="#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n\tvarying vec3 vReflect;\n\n\tuniform float refractionRatio;\n\tuniform bool useRefract;\n\n#endif\n";THREE.ShaderChunk.linear_to_gamma_fragment="#ifdef GAMMA_OUTPUT\n\n\tgl_FragColor.xyz = sqrt( gl_FragColor.xyz );\n\n#endif";THREE.ShaderChunk.color_pars_vertex="#ifdef USE_COLOR\n\n\tvarying vec3 vColor;\n\n#endif";
-THREE.ShaderChunk.lights_lambert_pars_vertex="uniform vec3 ambient;\nuniform vec3 diffuse;\nuniform vec3 emissive;\n\nuniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n\tuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n\tuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n\tuniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n\tuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\tuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n\tuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n\tuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#ifdef WRAP_AROUND\n\n\tuniform vec3 wrapRGB;\n\n#endif\n";
-THREE.ShaderChunk.map_pars_vertex="#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n\tvarying vec2 vUv;\n\tuniform vec4 offsetRepeat;\n\n#endif\n";THREE.ShaderChunk.envmap_fragment="#ifdef USE_ENVMAP\n\n\tvec3 reflectVec;\n\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n\t\tvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\n\t\t// http://en.wikibooks.org/wiki/GLSL_Programming/Applying_Matrix_Transformations\n\t\t// Transforming Normal Vectors with the Inverse Transformation\n\n\t\tvec3 worldNormal = normalize( vec3( vec4( normal, 0.0 ) * viewMatrix ) );\n\n\t\tif ( useRefract ) {\n\n\t\t\treflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\n\t\t} else { \n\n\t\t\treflectVec = reflect( cameraToVertex, worldNormal );\n\n\t\t}\n\n\t#else\n\n\t\treflectVec = vReflect;\n\n\t#endif\n\n\t#ifdef DOUBLE_SIDED\n\n\t\tfloat flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\t\tvec4 cubeColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n\t#else\n\n\t\tvec4 cubeColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n\t#endif\n\n\t#ifdef GAMMA_INPUT\n\n\t\tcubeColor.xyz *= cubeColor.xyz;\n\n\t#endif\n\n\tif ( combine == 1 ) {\n\n\t\tgl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularStrength * reflectivity );\n\n\t} else if ( combine == 2 ) {\n\n\t\tgl_FragColor.xyz += cubeColor.xyz * specularStrength * reflectivity;\n\n\t} else {\n\n\t\tgl_FragColor.xyz = mix( gl_FragColor.xyz, gl_FragColor.xyz * cubeColor.xyz, specularStrength * reflectivity );\n\n\t}\n\n#endif";
-THREE.ShaderChunk.specularmap_pars_fragment="#ifdef USE_SPECULARMAP\n\n\tuniform sampler2D specularMap;\n\n#endif";THREE.ShaderChunk.logdepthbuf_vertex="#ifdef USE_LOGDEPTHBUF\n\n\tgl_Position.z = log2(max(1e-6, gl_Position.w + 1.0)) * logDepthBufFC;\n\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\n#else\n\n\t\tgl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\n\t#endif\n\n#endif";THREE.ShaderChunk.morphtarget_pars_vertex="#ifdef USE_MORPHTARGETS\n\n\t#ifndef USE_MORPHNORMALS\n\n\tuniform float morphTargetInfluences[ 8 ];\n\n\t#else\n\n\tuniform float morphTargetInfluences[ 4 ];\n\n\t#endif\n\n#endif";
-THREE.ShaderChunk.specularmap_fragment="float specularStrength;\n\n#ifdef USE_SPECULARMAP\n\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n\n#else\n\n\tspecularStrength = 1.0;\n\n#endif";THREE.ShaderChunk.fog_fragment="#ifdef USE_FOG\n\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\n\t\tfloat depth = gl_FragDepthEXT / gl_FragCoord.w;\n\n\t#else\n\n\t\tfloat depth = gl_FragCoord.z / gl_FragCoord.w;\n\n\t#endif\n\n\t#ifdef FOG_EXP2\n\n\t\tconst float LOG2 = 1.442695;\n\t\tfloat fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\n\t\tfogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n\n\t#else\n\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, depth );\n\n\t#endif\n\t\n\tgl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n\n#endif";
-THREE.ShaderChunk.bumpmap_pars_fragment="#ifdef USE_BUMPMAP\n\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\n\t\t\t// Derivative maps - bump mapping unparametrized surfaces by Morten Mikkelsen\n\t\t\t//\thttp://mmikkelsen3d.blogspot.sk/2011/07/derivative-maps.html\n\n\t\t\t// Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2)\n\n\tvec2 dHdxy_fwd() {\n\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\n\t\treturn vec2( dBx, dBy );\n\n\t}\n\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\n\t\tvec3 vSigmaX = dFdx( surf_pos );\n\t\tvec3 vSigmaY = dFdy( surf_pos );\n\t\tvec3 vN = surf_norm;\t\t// normalized\n\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\n\t\tfloat fDet = dot( vSigmaX, R1 );\n\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\n\t}\n\n#endif";
-THREE.ShaderChunk.defaultnormal_vertex="vec3 objectNormal;\n\n#ifdef USE_SKINNING\n\n\tobjectNormal = skinnedNormal.xyz;\n\n#endif\n\n#if !defined( USE_SKINNING ) && defined( USE_MORPHNORMALS )\n\n\tobjectNormal = morphedNormal;\n\n#endif\n\n#if !defined( USE_SKINNING ) && ! defined( USE_MORPHNORMALS )\n\n\tobjectNormal = normal;\n\n#endif\n\n#ifdef FLIP_SIDED\n\n\tobjectNormal = -objectNormal;\n\n#endif\n\nvec3 transformedNormal = normalMatrix * objectNormal;";
-THREE.ShaderChunk.lights_phong_pars_fragment="uniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n\tuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n\tuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n\tuniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n\tuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\n\tuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n\tuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n\tuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n\n\tuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n\tvarying vec3 vWorldPosition;\n\n#endif\n\n#ifdef WRAP_AROUND\n\n\tuniform vec3 wrapRGB;\n\n#endif\n\nvarying vec3 vViewPosition;\nvarying vec3 vNormal;";
-THREE.ShaderChunk.skinbase_vertex="#ifdef USE_SKINNING\n\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n\n#endif";THREE.ShaderChunk.map_vertex="#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n\tvUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\n#endif";
-THREE.ShaderChunk.lightmap_fragment="#ifdef USE_LIGHTMAP\n\n\tgl_FragColor = gl_FragColor * texture2D( lightMap, vUv2 );\n\n#endif";THREE.ShaderChunk.shadowmap_pars_vertex="#ifdef USE_SHADOWMAP\n\n\tvarying vec4 vShadowCoord[ MAX_SHADOWS ];\n\tuniform mat4 shadowMatrix[ MAX_SHADOWS ];\n\n#endif";THREE.ShaderChunk.color_fragment="#ifdef USE_COLOR\n\n\tgl_FragColor = gl_FragColor * vec4( vColor, 1.0 );\n\n#endif";THREE.ShaderChunk.morphtarget_vertex="#ifdef USE_MORPHTARGETS\n\n\tvec3 morphed = vec3( 0.0 );\n\tmorphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n\tmorphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n\tmorphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n\tmorphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\n\t#ifndef USE_MORPHNORMALS\n\n\tmorphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n\tmorphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n\tmorphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n\tmorphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\n\t#endif\n\n\tmorphed += position;\n\n#endif";
-THREE.ShaderChunk.envmap_vertex="#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n\tvec3 worldNormal = mat3( modelMatrix[ 0 ].xyz, modelMatrix[ 1 ].xyz, modelMatrix[ 2 ].xyz ) * objectNormal;\n\tworldNormal = normalize( worldNormal );\n\n\tvec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\n\tif ( useRefract ) {\n\n\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\n\t} else {\n\n\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\n\t}\n\n#endif";
-THREE.ShaderChunk.shadowmap_fragment="#ifdef USE_SHADOWMAP\n\n\t#ifdef SHADOWMAP_DEBUG\n\n\t\tvec3 frustumColors[3];\n\t\tfrustumColors[0] = vec3( 1.0, 0.5, 0.0 );\n\t\tfrustumColors[1] = vec3( 0.0, 1.0, 0.8 );\n\t\tfrustumColors[2] = vec3( 0.0, 0.5, 1.0 );\n\n\t#endif\n\n\t#ifdef SHADOWMAP_CASCADE\n\n\t\tint inFrustumCount = 0;\n\n\t#endif\n\n\tfloat fDepth;\n\tvec3 shadowColor = vec3( 1.0 );\n\n\tfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n\t\tvec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\n\n\t\t\t\t// if ( something && something ) breaks ATI OpenGL shader compiler\n\t\t\t\t// if ( all( something, something ) ) using this instead\n\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\n\t\t\t\t// don't shadow pixels outside of light frustum\n\t\t\t\t// use just first frustum (for cascades)\n\t\t\t\t// don't shadow pixels behind far plane of light frustum\n\n\t\t#ifdef SHADOWMAP_CASCADE\n\n\t\t\tinFrustumCount += int( inFrustum );\n\t\t\tbvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );\n\n\t\t#else\n\n\t\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\n\t\t#endif\n\n\t\tbool frustumTest = all( frustumTestVec );\n\n\t\tif ( frustumTest ) {\n\n\t\t\tshadowCoord.z += shadowBias[ i ];\n\n\t\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\n\t\t\t\t\t\t// Percentage-close filtering\n\t\t\t\t\t\t// (9 pixel kernel)\n\t\t\t\t\t\t// http://fabiensanglard.net/shadowmappingPCF/\n\n\t\t\t\tfloat shadow = 0.0;\n\n\t\t/*\n\t\t\t\t\t\t// nested loops breaks shader compiler / validator on some ATI cards when using OpenGL\n\t\t\t\t\t\t// must enroll loop manually\n\n\t\t\t\tfor ( float y = -1.25; y <= 1.25; y += 1.25 )\n\t\t\t\t\tfor ( float x = -1.25; x <= 1.25; x += 1.25 ) {\n\n\t\t\t\t\t\tvec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );\n\n\t\t\t\t\t\t\t\t// doesn't seem to produce any noticeable visual difference compared to simple texture2D lookup\n\t\t\t\t\t\t\t\t//vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) );\n\n\t\t\t\t\t\tfloat fDepth = unpackDepth( rgbaDepth );\n\n\t\t\t\t\t\tif ( fDepth < shadowCoord.z )\n\t\t\t\t\t\t\tshadow += 1.0;\n\n\t\t\t\t}\n\n\t\t\t\tshadow /= 9.0;\n\n\t\t*/\n\n\t\t\t\tconst float shadowDelta = 1.0 / 9.0;\n\n\t\t\t\tfloat xPixelOffset = 1.0 / shadowMapSize[ i ].x;\n\t\t\t\tfloat yPixelOffset = 1.0 / shadowMapSize[ i ].y;\n\n\t\t\t\tfloat dx0 = -1.25 * xPixelOffset;\n\t\t\t\tfloat dy0 = -1.25 * yPixelOffset;\n\t\t\t\tfloat dx1 = 1.25 * xPixelOffset;\n\t\t\t\tfloat dy1 = 1.25 * yPixelOffset;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tshadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n\n\t\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n\t\t\t\t\t\t// Percentage-close filtering\n\t\t\t\t\t\t// (9 pixel kernel)\n\t\t\t\t\t\t// http://fabiensanglard.net/shadowmappingPCF/\n\n\t\t\t\tfloat shadow = 0.0;\n\n\t\t\t\tfloat xPixelOffset = 1.0 / shadowMapSize[ i ].x;\n\t\t\t\tfloat yPixelOffset = 1.0 / shadowMapSize[ i ].y;\n\n\t\t\t\tfloat dx0 = -1.0 * xPixelOffset;\n\t\t\t\tfloat dy0 = -1.0 * yPixelOffset;\n\t\t\t\tfloat dx1 = 1.0 * xPixelOffset;\n\t\t\t\tfloat dy1 = 1.0 * yPixelOffset;\n\n\t\t\t\tmat3 shadowKernel;\n\t\t\t\tmat3 depthKernel;\n\n\t\t\t\tdepthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n\t\t\t\tdepthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n\t\t\t\tdepthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n\t\t\t\tdepthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n\t\t\t\tdepthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n\t\t\t\tdepthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n\t\t\t\tdepthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n\t\t\t\tdepthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n\t\t\t\tdepthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\n\t\t\t\tvec3 shadowZ = vec3( shadowCoord.z );\n\t\t\t\tshadowKernel[0] = vec3(lessThan(depthKernel[0], shadowZ ));\n\t\t\t\tshadowKernel[0] *= vec3(0.25);\n\n\t\t\t\tshadowKernel[1] = vec3(lessThan(depthKernel[1], shadowZ ));\n\t\t\t\tshadowKernel[1] *= vec3(0.25);\n\n\t\t\t\tshadowKernel[2] = vec3(lessThan(depthKernel[2], shadowZ ));\n\t\t\t\tshadowKernel[2] *= vec3(0.25);\n\n\t\t\t\tvec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );\n\n\t\t\t\tshadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );\n\t\t\t\tshadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );\n\n\t\t\t\tvec4 shadowValues;\n\t\t\t\tshadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );\n\t\t\t\tshadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );\n\t\t\t\tshadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );\n\t\t\t\tshadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );\n\n\t\t\t\tshadow = dot( shadowValues, vec4( 1.0 ) );\n\n\t\t\t\tshadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n\n\t\t\t#else\n\n\t\t\t\tvec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\n\t\t\t\tfloat fDepth = unpackDepth( rgbaDepth );\n\n\t\t\t\tif ( fDepth < shadowCoord.z )\n\n\t\t// spot with multiple shadows is darker\n\n\t\t\t\t\tshadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );\n\n\t\t// spot with multiple shadows has the same color as single shadow spot\n\n\t\t// \t\t\t\t\tshadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) );\n\n\t\t\t#endif\n\n\t\t}\n\n\n\t\t#ifdef SHADOWMAP_DEBUG\n\n\t\t\t#ifdef SHADOWMAP_CASCADE\n\n\t\t\t\tif ( inFrustum && inFrustumCount == 1 ) gl_FragColor.xyz *= frustumColors[ i ];\n\n\t\t\t#else\n\n\t\t\t\tif ( inFrustum ) gl_FragColor.xyz *= frustumColors[ i ];\n\n\t\t\t#endif\n\n\t\t#endif\n\n\t}\n\n\t#ifdef GAMMA_OUTPUT\n\n\t\tshadowColor *= shadowColor;\n\n\t#endif\n\n\tgl_FragColor.xyz = gl_FragColor.xyz * shadowColor;\n\n#endif\n";
-THREE.ShaderChunk.worldpos_vertex="#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\n\t#ifdef USE_SKINNING\n\n\t\tvec4 worldPosition = modelMatrix * skinned;\n\n\t#endif\n\n\t#if defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )\n\n\t\tvec4 worldPosition = modelMatrix * vec4( morphed, 1.0 );\n\n\t#endif\n\n\t#if ! defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )\n\n\t\tvec4 worldPosition = modelMatrix * vec4( position, 1.0 );\n\n\t#endif\n\n#endif";
-THREE.ShaderChunk.shadowmap_pars_fragment="#ifdef USE_SHADOWMAP\n\n\tuniform sampler2D shadowMap[ MAX_SHADOWS ];\n\tuniform vec2 shadowMapSize[ MAX_SHADOWS ];\n\n\tuniform float shadowDarkness[ MAX_SHADOWS ];\n\tuniform float shadowBias[ MAX_SHADOWS ];\n\n\tvarying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n\tfloat unpackDepth( const in vec4 rgba_depth ) {\n\n\t\tconst vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n\t\tfloat depth = dot( rgba_depth, bit_shift );\n\t\treturn depth;\n\n\t}\n\n#endif";
-THREE.ShaderChunk.skinning_pars_vertex="#ifdef USE_SKINNING\n\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\n\t#ifdef BONE_TEXTURE\n\n\t\tuniform sampler2D boneTexture;\n\t\tuniform int boneTextureWidth;\n\t\tuniform int boneTextureHeight;\n\n\t\tmat4 getBoneMatrix( const in float i ) {\n\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureWidth ) );\n\t\t\tfloat y = floor( j / float( boneTextureWidth ) );\n\n\t\t\tfloat dx = 1.0 / float( boneTextureWidth );\n\t\t\tfloat dy = 1.0 / float( boneTextureHeight );\n\n\t\t\ty = dy * ( y + 0.5 );\n\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\n\t\t\treturn bone;\n\n\t\t}\n\n\t#else\n\n\t\tuniform mat4 boneGlobalMatrices[ MAX_BONES ];\n\n\t\tmat4 getBoneMatrix( const in float i ) {\n\n\t\t\tmat4 bone = boneGlobalMatrices[ int(i) ];\n\t\t\treturn bone;\n\n\t\t}\n\n\t#endif\n\n#endif\n";
-THREE.ShaderChunk.logdepthbuf_pars_fragment="#ifdef USE_LOGDEPTHBUF\n\n\tuniform float logDepthBufFC;\n\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\n\t\t#extension GL_EXT_frag_depth : enable\n\t\tvarying float vFragDepth;\n\n\t#endif\n\n#endif";THREE.ShaderChunk.alphamap_fragment="#ifdef USE_ALPHAMAP\n\n\tgl_FragColor.a *= texture2D( alphaMap, vUv ).g;\n\n#endif\n";THREE.ShaderChunk.alphamap_pars_fragment="#ifdef USE_ALPHAMAP\n\n\tuniform sampler2D alphaMap;\n\n#endif\n";
-THREE.UniformsUtils={merge:function(a){for(var b={},c=0;c<a.length;c++){var d=this.clone(a[c]),e;for(e in d)b[e]=d[e]}return b},clone:function(a){var b={},c;for(c in a){b[c]={};for(var d in a[c]){var e=a[c][d];b[c][d]=e instanceof THREE.Color||e instanceof THREE.Vector2||e instanceof THREE.Vector3||e instanceof THREE.Vector4||e instanceof THREE.Matrix4||e instanceof THREE.Texture?e.clone():e instanceof Array?e.slice():e}}return b}};
-THREE.UniformsLib={common:{diffuse:{type:"c",value:new THREE.Color(15658734)},opacity:{type:"f",value:1},map:{type:"t",value:null},offsetRepeat:{type:"v4",value:new THREE.Vector4(0,0,1,1)},lightMap:{type:"t",value:null},specularMap:{type:"t",value:null},alphaMap:{type:"t",value:null},envMap:{type:"t",value:null},flipEnvMap:{type:"f",value:-1},useRefract:{type:"i",value:0},reflectivity:{type:"f",value:1},refractionRatio:{type:"f",value:.98},combine:{type:"i",value:0},morphTargetInfluences:{type:"f",
-value:0}},bump:{bumpMap:{type:"t",value:null},bumpScale:{type:"f",value:1}},normalmap:{normalMap:{type:"t",value:null},normalScale:{type:"v2",value:new THREE.Vector2(1,1)}},fog:{fogDensity:{type:"f",value:2.5E-4},fogNear:{type:"f",value:1},fogFar:{type:"f",value:2E3},fogColor:{type:"c",value:new THREE.Color(16777215)}},lights:{ambientLightColor:{type:"fv",value:[]},directionalLightDirection:{type:"fv",value:[]},directionalLightColor:{type:"fv",value:[]},hemisphereLightDirection:{type:"fv",value:[]},
-hemisphereLightSkyColor:{type:"fv",value:[]},hemisphereLightGroundColor:{type:"fv",value:[]},pointLightColor:{type:"fv",value:[]},pointLightPosition:{type:"fv",value:[]},pointLightDistance:{type:"fv1",value:[]},spotLightColor:{type:"fv",value:[]},spotLightPosition:{type:"fv",value:[]},spotLightDirection:{type:"fv",value:[]},spotLightDistance:{type:"fv1",value:[]},spotLightAngleCos:{type:"fv1",value:[]},spotLightExponent:{type:"fv1",value:[]}},particle:{psColor:{type:"c",value:new THREE.Color(15658734)},
-opacity:{type:"f",value:1},size:{type:"f",value:1},scale:{type:"f",value:1},map:{type:"t",value:null},fogDensity:{type:"f",value:2.5E-4},fogNear:{type:"f",value:1},fogFar:{type:"f",value:2E3},fogColor:{type:"c",value:new THREE.Color(16777215)}},shadowmap:{shadowMap:{type:"tv",value:[]},shadowMapSize:{type:"v2v",value:[]},shadowBias:{type:"fv1",value:[]},shadowDarkness:{type:"fv1",value:[]},shadowMatrix:{type:"m4v",value:[]}}};
-THREE.ShaderLib={basic:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.common,THREE.UniformsLib.fog,THREE.UniformsLib.shadowmap]),vertexShader:[THREE.ShaderChunk.map_pars_vertex,THREE.ShaderChunk.lightmap_pars_vertex,THREE.ShaderChunk.envmap_pars_vertex,THREE.ShaderChunk.color_pars_vertex,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.shadowmap_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.map_vertex,
-THREE.ShaderChunk.lightmap_vertex,THREE.ShaderChunk.color_vertex,THREE.ShaderChunk.skinbase_vertex,"\t#ifdef USE_ENVMAP",THREE.ShaderChunk.morphnormal_vertex,THREE.ShaderChunk.skinnormal_vertex,THREE.ShaderChunk.defaultnormal_vertex,"\t#endif",THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.logdepthbuf_vertex,THREE.ShaderChunk.worldpos_vertex,THREE.ShaderChunk.envmap_vertex,THREE.ShaderChunk.shadowmap_vertex,"}"].join("\n"),
-fragmentShader:["uniform vec3 diffuse;\nuniform float opacity;",THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.map_pars_fragment,THREE.ShaderChunk.alphamap_pars_fragment,THREE.ShaderChunk.lightmap_pars_fragment,THREE.ShaderChunk.envmap_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.shadowmap_pars_fragment,THREE.ShaderChunk.specularmap_pars_fragment,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\n\tgl_FragColor = vec4( diffuse, opacity );",THREE.ShaderChunk.logdepthbuf_fragment,
-THREE.ShaderChunk.map_fragment,THREE.ShaderChunk.alphamap_fragment,THREE.ShaderChunk.alphatest_fragment,THREE.ShaderChunk.specularmap_fragment,THREE.ShaderChunk.lightmap_fragment,THREE.ShaderChunk.color_fragment,THREE.ShaderChunk.envmap_fragment,THREE.ShaderChunk.shadowmap_fragment,THREE.ShaderChunk.linear_to_gamma_fragment,THREE.ShaderChunk.fog_fragment,"}"].join("\n")},lambert:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.common,THREE.UniformsLib.fog,THREE.UniformsLib.lights,THREE.UniformsLib.shadowmap,
-{ambient:{type:"c",value:new THREE.Color(16777215)},emissive:{type:"c",value:new THREE.Color(0)},wrapRGB:{type:"v3",value:new THREE.Vector3(1,1,1)}}]),vertexShader:["#define LAMBERT\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n#endif",THREE.ShaderChunk.map_pars_vertex,THREE.ShaderChunk.lightmap_pars_vertex,THREE.ShaderChunk.envmap_pars_vertex,THREE.ShaderChunk.lights_lambert_pars_vertex,THREE.ShaderChunk.color_pars_vertex,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,
-THREE.ShaderChunk.shadowmap_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.map_vertex,THREE.ShaderChunk.lightmap_vertex,THREE.ShaderChunk.color_vertex,THREE.ShaderChunk.morphnormal_vertex,THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.skinnormal_vertex,THREE.ShaderChunk.defaultnormal_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.logdepthbuf_vertex,THREE.ShaderChunk.worldpos_vertex,
-THREE.ShaderChunk.envmap_vertex,THREE.ShaderChunk.lights_lambert_vertex,THREE.ShaderChunk.shadowmap_vertex,"}"].join("\n"),fragmentShader:["uniform float opacity;\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n#endif",THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.map_pars_fragment,THREE.ShaderChunk.alphamap_pars_fragment,THREE.ShaderChunk.lightmap_pars_fragment,THREE.ShaderChunk.envmap_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.shadowmap_pars_fragment,
-THREE.ShaderChunk.specularmap_pars_fragment,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\n\tgl_FragColor = vec4( vec3( 1.0 ), opacity );",THREE.ShaderChunk.logdepthbuf_fragment,THREE.ShaderChunk.map_fragment,THREE.ShaderChunk.alphamap_fragment,THREE.ShaderChunk.alphatest_fragment,THREE.ShaderChunk.specularmap_fragment,"\t#ifdef DOUBLE_SIDED\n\t\tif ( gl_FrontFacing )\n\t\t\tgl_FragColor.xyz *= vLightFront;\n\t\telse\n\t\t\tgl_FragColor.xyz *= vLightBack;\n\t#else\n\t\tgl_FragColor.xyz *= vLightFront;\n\t#endif",
-THREE.ShaderChunk.lightmap_fragment,THREE.ShaderChunk.color_fragment,THREE.ShaderChunk.envmap_fragment,THREE.ShaderChunk.shadowmap_fragment,THREE.ShaderChunk.linear_to_gamma_fragment,THREE.ShaderChunk.fog_fragment,"}"].join("\n")},phong:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.common,THREE.UniformsLib.bump,THREE.UniformsLib.normalmap,THREE.UniformsLib.fog,THREE.UniformsLib.lights,THREE.UniformsLib.shadowmap,{ambient:{type:"c",value:new THREE.Color(16777215)},emissive:{type:"c",value:new THREE.Color(0)},
-specular:{type:"c",value:new THREE.Color(1118481)},shininess:{type:"f",value:30},wrapRGB:{type:"v3",value:new THREE.Vector3(1,1,1)}}]),vertexShader:["#define PHONG\nvarying vec3 vViewPosition;\nvarying vec3 vNormal;",THREE.ShaderChunk.map_pars_vertex,THREE.ShaderChunk.lightmap_pars_vertex,THREE.ShaderChunk.envmap_pars_vertex,THREE.ShaderChunk.lights_phong_pars_vertex,THREE.ShaderChunk.color_pars_vertex,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.shadowmap_pars_vertex,
-THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.map_vertex,THREE.ShaderChunk.lightmap_vertex,THREE.ShaderChunk.color_vertex,THREE.ShaderChunk.morphnormal_vertex,THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.skinnormal_vertex,THREE.ShaderChunk.defaultnormal_vertex,"\tvNormal = normalize( transformedNormal );",THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"\tvViewPosition = -mvPosition.xyz;",
-THREE.ShaderChunk.worldpos_vertex,THREE.ShaderChunk.envmap_vertex,THREE.ShaderChunk.lights_phong_vertex,THREE.ShaderChunk.shadowmap_vertex,"}"].join("\n"),fragmentShader:["#define PHONG\nuniform vec3 diffuse;\nuniform float opacity;\nuniform vec3 ambient;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;",THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.map_pars_fragment,THREE.ShaderChunk.alphamap_pars_fragment,THREE.ShaderChunk.lightmap_pars_fragment,THREE.ShaderChunk.envmap_pars_fragment,
-THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.lights_phong_pars_fragment,THREE.ShaderChunk.shadowmap_pars_fragment,THREE.ShaderChunk.bumpmap_pars_fragment,THREE.ShaderChunk.normalmap_pars_fragment,THREE.ShaderChunk.specularmap_pars_fragment,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\n\tgl_FragColor = vec4( vec3( 1.0 ), opacity );",THREE.ShaderChunk.logdepthbuf_fragment,THREE.ShaderChunk.map_fragment,THREE.ShaderChunk.alphamap_fragment,THREE.ShaderChunk.alphatest_fragment,THREE.ShaderChunk.specularmap_fragment,
-THREE.ShaderChunk.lights_phong_fragment,THREE.ShaderChunk.lightmap_fragment,THREE.ShaderChunk.color_fragment,THREE.ShaderChunk.envmap_fragment,THREE.ShaderChunk.shadowmap_fragment,THREE.ShaderChunk.linear_to_gamma_fragment,THREE.ShaderChunk.fog_fragment,"}"].join("\n")},particle_basic:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.particle,THREE.UniformsLib.shadowmap]),vertexShader:["uniform float size;\nuniform float scale;",THREE.ShaderChunk.color_pars_vertex,THREE.ShaderChunk.shadowmap_pars_vertex,
-THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.color_vertex,"\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\t#ifdef USE_SIZEATTENUATION\n\t\tgl_PointSize = size * ( scale / length( mvPosition.xyz ) );\n\t#else\n\t\tgl_PointSize = size;\n\t#endif\n\tgl_Position = projectionMatrix * mvPosition;",THREE.ShaderChunk.logdepthbuf_vertex,THREE.ShaderChunk.worldpos_vertex,THREE.ShaderChunk.shadowmap_vertex,"}"].join("\n"),fragmentShader:["uniform vec3 psColor;\nuniform float opacity;",
-THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.map_particle_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.shadowmap_pars_fragment,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\n\tgl_FragColor = vec4( psColor, opacity );",THREE.ShaderChunk.logdepthbuf_fragment,THREE.ShaderChunk.map_particle_fragment,THREE.ShaderChunk.alphatest_fragment,THREE.ShaderChunk.color_fragment,THREE.ShaderChunk.shadowmap_fragment,THREE.ShaderChunk.fog_fragment,"}"].join("\n")},dashed:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.common,
-THREE.UniformsLib.fog,{scale:{type:"f",value:1},dashSize:{type:"f",value:1},totalSize:{type:"f",value:2}}]),vertexShader:["uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;",THREE.ShaderChunk.color_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.color_vertex,"\tvLineDistance = scale * lineDistance;\n\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\tgl_Position = projectionMatrix * mvPosition;",THREE.ShaderChunk.logdepthbuf_vertex,
-"}"].join("\n"),fragmentShader:["uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;",THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tgl_FragColor = vec4( diffuse, opacity );",THREE.ShaderChunk.logdepthbuf_fragment,THREE.ShaderChunk.color_fragment,THREE.ShaderChunk.fog_fragment,
-"}"].join("\n")},depth:{uniforms:{mNear:{type:"f",value:1},mFar:{type:"f",value:2E3},opacity:{type:"f",value:1}},vertexShader:[THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform float mNear;\nuniform float mFar;\nuniform float opacity;",THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {",THREE.ShaderChunk.logdepthbuf_fragment,
-"\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tfloat depth = gl_FragDepthEXT / gl_FragCoord.w;\n\t#else\n\t\tfloat depth = gl_FragCoord.z / gl_FragCoord.w;\n\t#endif\n\tfloat color = 1.0 - smoothstep( mNear, mFar, depth );\n\tgl_FragColor = vec4( vec3( color ), opacity );\n}"].join("\n")},normal:{uniforms:{opacity:{type:"f",value:1}},vertexShader:["varying vec3 vNormal;",THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {\n\tvNormal = normalize( normalMatrix * normal );",
-THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform float opacity;\nvarying vec3 vNormal;",THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\n\tgl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );",THREE.ShaderChunk.logdepthbuf_fragment,"}"].join("\n")},normalmap:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.fog,THREE.UniformsLib.lights,THREE.UniformsLib.shadowmap,{enableAO:{type:"i",
-value:0},enableDiffuse:{type:"i",value:0},enableSpecular:{type:"i",value:0},enableReflection:{type:"i",value:0},enableDisplacement:{type:"i",value:0},tDisplacement:{type:"t",value:null},tDiffuse:{type:"t",value:null},tCube:{type:"t",value:null},tNormal:{type:"t",value:null},tSpecular:{type:"t",value:null},tAO:{type:"t",value:null},uNormalScale:{type:"v2",value:new THREE.Vector2(1,1)},uDisplacementBias:{type:"f",value:0},uDisplacementScale:{type:"f",value:1},diffuse:{type:"c",value:new THREE.Color(16777215)},
-specular:{type:"c",value:new THREE.Color(1118481)},ambient:{type:"c",value:new THREE.Color(16777215)},shininess:{type:"f",value:30},opacity:{type:"f",value:1},useRefract:{type:"i",value:0},refractionRatio:{type:"f",value:.98},reflectivity:{type:"f",value:.5},uOffset:{type:"v2",value:new THREE.Vector2(0,0)},uRepeat:{type:"v2",value:new THREE.Vector2(1,1)},wrapRGB:{type:"v3",value:new THREE.Vector3(1,1,1)}}]),fragmentShader:["uniform vec3 ambient;\nuniform vec3 diffuse;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\nuniform bool enableDiffuse;\nuniform bool enableSpecular;\nuniform bool enableAO;\nuniform bool enableReflection;\nuniform sampler2D tDiffuse;\nuniform sampler2D tNormal;\nuniform sampler2D tSpecular;\nuniform sampler2D tAO;\nuniform samplerCube tCube;\nuniform vec2 uNormalScale;\nuniform bool useRefract;\nuniform float refractionRatio;\nuniform float reflectivity;\nvarying vec3 vTangent;\nvarying vec3 vBinormal;\nvarying vec3 vNormal;\nvarying vec2 vUv;\nuniform vec3 ambientLightColor;\n#if MAX_DIR_LIGHTS > 0\n\tuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n\tuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n#endif\n#if MAX_HEMI_LIGHTS > 0\n\tuniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n#endif\n#if MAX_POINT_LIGHTS > 0\n\tuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\tuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n\tuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n#endif\n#if MAX_SPOT_LIGHTS > 0\n\tuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n#endif\n#ifdef WRAP_AROUND\n\tuniform vec3 wrapRGB;\n#endif\nvarying vec3 vWorldPosition;\nvarying vec3 vViewPosition;",
-THREE.ShaderChunk.shadowmap_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {",THREE.ShaderChunk.logdepthbuf_fragment,"\tgl_FragColor = vec4( vec3( 1.0 ), opacity );\n\tvec3 specularTex = vec3( 1.0 );\n\tvec3 normalTex = texture2D( tNormal, vUv ).xyz * 2.0 - 1.0;\n\tnormalTex.xy *= uNormalScale;\n\tnormalTex = normalize( normalTex );\n\tif( enableDiffuse ) {\n\t\t#ifdef GAMMA_INPUT\n\t\t\tvec4 texelColor = texture2D( tDiffuse, vUv );\n\t\t\ttexelColor.xyz *= texelColor.xyz;\n\t\t\tgl_FragColor = gl_FragColor * texelColor;\n\t\t#else\n\t\t\tgl_FragColor = gl_FragColor * texture2D( tDiffuse, vUv );\n\t\t#endif\n\t}\n\tif( enableAO ) {\n\t\t#ifdef GAMMA_INPUT\n\t\t\tvec4 aoColor = texture2D( tAO, vUv );\n\t\t\taoColor.xyz *= aoColor.xyz;\n\t\t\tgl_FragColor.xyz = gl_FragColor.xyz * aoColor.xyz;\n\t\t#else\n\t\t\tgl_FragColor.xyz = gl_FragColor.xyz * texture2D( tAO, vUv ).xyz;\n\t\t#endif\n\t}",
-THREE.ShaderChunk.alphatest_fragment,"\tif( enableSpecular )\n\t\tspecularTex = texture2D( tSpecular, vUv ).xyz;\n\tmat3 tsb = mat3( normalize( vTangent ), normalize( vBinormal ), normalize( vNormal ) );\n\tvec3 finalNormal = tsb * normalTex;\n\t#ifdef FLIP_SIDED\n\t\tfinalNormal = -finalNormal;\n\t#endif\n\tvec3 normal = normalize( finalNormal );\n\tvec3 viewPosition = normalize( vViewPosition );\n\t#if MAX_POINT_LIGHTS > 0\n\t\tvec3 pointDiffuse = vec3( 0.0 );\n\t\tvec3 pointSpecular = vec3( 0.0 );\n\t\tfor ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\t\t\tvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n\t\t\tvec3 pointVector = lPosition.xyz + vViewPosition.xyz;\n\t\t\tfloat pointDistance = 1.0;\n\t\t\tif ( pointLightDistance[ i ] > 0.0 )\n\t\t\t\tpointDistance = 1.0 - min( ( length( pointVector ) / pointLightDistance[ i ] ), 1.0 );\n\t\t\tpointVector = normalize( pointVector );\n\t\t\t#ifdef WRAP_AROUND\n\t\t\t\tfloat pointDiffuseWeightFull = max( dot( normal, pointVector ), 0.0 );\n\t\t\t\tfloat pointDiffuseWeightHalf = max( 0.5 * dot( normal, pointVector ) + 0.5, 0.0 );\n\t\t\t\tvec3 pointDiffuseWeight = mix( vec3( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );\n\t\t\t#else\n\t\t\t\tfloat pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );\n\t\t\t#endif\n\t\t\tpointDiffuse += pointDistance * pointLightColor[ i ] * diffuse * pointDiffuseWeight;\n\t\t\tvec3 pointHalfVector = normalize( pointVector + viewPosition );\n\t\t\tfloat pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );\n\t\t\tfloat pointSpecularWeight = specularTex.r * max( pow( pointDotNormalHalf, shininess ), 0.0 );\n\t\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\t\t\tvec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( pointVector, pointHalfVector ), 0.0 ), 5.0 );\n\t\t\tpointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * pointDistance * specularNormalization;\n\t\t}\n\t#endif\n\t#if MAX_SPOT_LIGHTS > 0\n\t\tvec3 spotDiffuse = vec3( 0.0 );\n\t\tvec3 spotSpecular = vec3( 0.0 );\n\t\tfor ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\t\t\tvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n\t\t\tvec3 spotVector = lPosition.xyz + vViewPosition.xyz;\n\t\t\tfloat spotDistance = 1.0;\n\t\t\tif ( spotLightDistance[ i ] > 0.0 )\n\t\t\t\tspotDistance = 1.0 - min( ( length( spotVector ) / spotLightDistance[ i ] ), 1.0 );\n\t\t\tspotVector = normalize( spotVector );\n\t\t\tfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );\n\t\t\tif ( spotEffect > spotLightAngleCos[ i ] ) {\n\t\t\t\tspotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\t\t\t\t#ifdef WRAP_AROUND\n\t\t\t\t\tfloat spotDiffuseWeightFull = max( dot( normal, spotVector ), 0.0 );\n\t\t\t\t\tfloat spotDiffuseWeightHalf = max( 0.5 * dot( normal, spotVector ) + 0.5, 0.0 );\n\t\t\t\t\tvec3 spotDiffuseWeight = mix( vec3( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );\n\t\t\t\t#else\n\t\t\t\t\tfloat spotDiffuseWeight = max( dot( normal, spotVector ), 0.0 );\n\t\t\t\t#endif\n\t\t\t\tspotDiffuse += spotDistance * spotLightColor[ i ] * diffuse * spotDiffuseWeight * spotEffect;\n\t\t\t\tvec3 spotHalfVector = normalize( spotVector + viewPosition );\n\t\t\t\tfloat spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );\n\t\t\t\tfloat spotSpecularWeight = specularTex.r * max( pow( spotDotNormalHalf, shininess ), 0.0 );\n\t\t\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\t\t\t\tvec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( spotVector, spotHalfVector ), 0.0 ), 5.0 );\n\t\t\t\tspotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * spotDistance * specularNormalization * spotEffect;\n\t\t\t}\n\t\t}\n\t#endif\n\t#if MAX_DIR_LIGHTS > 0\n\t\tvec3 dirDiffuse = vec3( 0.0 );\n\t\tvec3 dirSpecular = vec3( 0.0 );\n\t\tfor( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {\n\t\t\tvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\n\t\t\tvec3 dirVector = normalize( lDirection.xyz );\n\t\t\t#ifdef WRAP_AROUND\n\t\t\t\tfloat directionalLightWeightingFull = max( dot( normal, dirVector ), 0.0 );\n\t\t\t\tfloat directionalLightWeightingHalf = max( 0.5 * dot( normal, dirVector ) + 0.5, 0.0 );\n\t\t\t\tvec3 dirDiffuseWeight = mix( vec3( directionalLightWeightingFull ), vec3( directionalLightWeightingHalf ), wrapRGB );\n\t\t\t#else\n\t\t\t\tfloat dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );\n\t\t\t#endif\n\t\t\tdirDiffuse += directionalLightColor[ i ] * diffuse * dirDiffuseWeight;\n\t\t\tvec3 dirHalfVector = normalize( dirVector + viewPosition );\n\t\t\tfloat dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );\n\t\t\tfloat dirSpecularWeight = specularTex.r * max( pow( dirDotNormalHalf, shininess ), 0.0 );\n\t\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\t\t\tvec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( dirVector, dirHalfVector ), 0.0 ), 5.0 );\n\t\t\tdirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;\n\t\t}\n\t#endif\n\t#if MAX_HEMI_LIGHTS > 0\n\t\tvec3 hemiDiffuse = vec3( 0.0 );\n\t\tvec3 hemiSpecular = vec3( 0.0 );\n\t\tfor( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\t\t\tvec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\n\t\t\tvec3 lVector = normalize( lDirection.xyz );\n\t\t\tfloat dotProduct = dot( normal, lVector );\n\t\t\tfloat hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\t\t\tvec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\t\t\themiDiffuse += diffuse * hemiColor;\n\t\t\tvec3 hemiHalfVectorSky = normalize( lVector + viewPosition );\n\t\t\tfloat hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;\n\t\t\tfloat hemiSpecularWeightSky = specularTex.r * max( pow( max( hemiDotNormalHalfSky, 0.0 ), shininess ), 0.0 );\n\t\t\tvec3 lVectorGround = -lVector;\n\t\t\tvec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );\n\t\t\tfloat hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;\n\t\t\tfloat hemiSpecularWeightGround = specularTex.r * max( pow( max( hemiDotNormalHalfGround, 0.0 ), shininess ), 0.0 );\n\t\t\tfloat dotProductGround = dot( normal, lVectorGround );\n\t\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\t\t\tvec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, hemiHalfVectorSky ), 0.0 ), 5.0 );\n\t\t\tvec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 0.0 ), 5.0 );\n\t\t\themiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );\n\t\t}\n\t#endif\n\tvec3 totalDiffuse = vec3( 0.0 );\n\tvec3 totalSpecular = vec3( 0.0 );\n\t#if MAX_DIR_LIGHTS > 0\n\t\ttotalDiffuse += dirDiffuse;\n\t\ttotalSpecular += dirSpecular;\n\t#endif\n\t#if MAX_HEMI_LIGHTS > 0\n\t\ttotalDiffuse += hemiDiffuse;\n\t\ttotalSpecular += hemiSpecular;\n\t#endif\n\t#if MAX_POINT_LIGHTS > 0\n\t\ttotalDiffuse += pointDiffuse;\n\t\ttotalSpecular += pointSpecular;\n\t#endif\n\t#if MAX_SPOT_LIGHTS > 0\n\t\ttotalDiffuse += spotDiffuse;\n\t\ttotalSpecular += spotSpecular;\n\t#endif\n\t#ifdef METAL\n\t\tgl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * ambient + totalSpecular );\n\t#else\n\t\tgl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * ambient ) + totalSpecular;\n\t#endif\n\tif ( enableReflection ) {\n\t\tvec3 vReflect;\n\t\tvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\t\tif ( useRefract ) {\n\t\t\tvReflect = refract( cameraToVertex, normal, refractionRatio );\n\t\t} else {\n\t\t\tvReflect = reflect( cameraToVertex, normal );\n\t\t}\n\t\tvec4 cubeColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );\n\t\t#ifdef GAMMA_INPUT\n\t\t\tcubeColor.xyz *= cubeColor.xyz;\n\t\t#endif\n\t\tgl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularTex.r * reflectivity );\n\t}",
-THREE.ShaderChunk.shadowmap_fragment,THREE.ShaderChunk.linear_to_gamma_fragment,THREE.ShaderChunk.fog_fragment,"}"].join("\n"),vertexShader:["attribute vec4 tangent;\nuniform vec2 uOffset;\nuniform vec2 uRepeat;\nuniform bool enableDisplacement;\n#ifdef VERTEX_TEXTURES\n\tuniform sampler2D tDisplacement;\n\tuniform float uDisplacementScale;\n\tuniform float uDisplacementBias;\n#endif\nvarying vec3 vTangent;\nvarying vec3 vBinormal;\nvarying vec3 vNormal;\nvarying vec2 vUv;\nvarying vec3 vWorldPosition;\nvarying vec3 vViewPosition;",
-THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.shadowmap_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.skinnormal_vertex,"\t#ifdef USE_SKINNING\n\t\tvNormal = normalize( normalMatrix * skinnedNormal.xyz );\n\t\tvec4 skinnedTangent = skinMatrix * vec4( tangent.xyz, 0.0 );\n\t\tvTangent = normalize( normalMatrix * skinnedTangent.xyz );\n\t#else\n\t\tvNormal = normalize( normalMatrix * normal );\n\t\tvTangent = normalize( normalMatrix * tangent.xyz );\n\t#endif\n\tvBinormal = normalize( cross( vNormal, vTangent ) * tangent.w );\n\tvUv = uv * uRepeat + uOffset;\n\tvec3 displacedPosition;\n\t#ifdef VERTEX_TEXTURES\n\t\tif ( enableDisplacement ) {\n\t\t\tvec3 dv = texture2D( tDisplacement, uv ).xyz;\n\t\t\tfloat df = uDisplacementScale * dv.x + uDisplacementBias;\n\t\t\tdisplacedPosition = position + normalize( normal ) * df;\n\t\t} else {\n\t\t\t#ifdef USE_SKINNING\n\t\t\t\tvec4 skinVertex = bindMatrix * vec4( position, 1.0 );\n\t\t\t\tvec4 skinned = vec4( 0.0 );\n\t\t\t\tskinned += boneMatX * skinVertex * skinWeight.x;\n\t\t\t\tskinned += boneMatY * skinVertex * skinWeight.y;\n\t\t\t\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\t\t\t\tskinned += boneMatW * skinVertex * skinWeight.w;\n\t\t\t\tskinned  = bindMatrixInverse * skinned;\n\t\t\t\tdisplacedPosition = skinned.xyz;\n\t\t\t#else\n\t\t\t\tdisplacedPosition = position;\n\t\t\t#endif\n\t\t}\n\t#else\n\t\t#ifdef USE_SKINNING\n\t\t\tvec4 skinVertex = bindMatrix * vec4( position, 1.0 );\n\t\t\tvec4 skinned = vec4( 0.0 );\n\t\t\tskinned += boneMatX * skinVertex * skinWeight.x;\n\t\t\tskinned += boneMatY * skinVertex * skinWeight.y;\n\t\t\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\t\t\tskinned += boneMatW * skinVertex * skinWeight.w;\n\t\t\tskinned  = bindMatrixInverse * skinned;\n\t\t\tdisplacedPosition = skinned.xyz;\n\t\t#else\n\t\t\tdisplacedPosition = position;\n\t\t#endif\n\t#endif\n\tvec4 mvPosition = modelViewMatrix * vec4( displacedPosition, 1.0 );\n\tvec4 worldPosition = modelMatrix * vec4( displacedPosition, 1.0 );\n\tgl_Position = projectionMatrix * mvPosition;",
-THREE.ShaderChunk.logdepthbuf_vertex,"\tvWorldPosition = worldPosition.xyz;\n\tvViewPosition = -mvPosition.xyz;\n\t#ifdef USE_SHADOWMAP\n\t\tfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\t\t\tvShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n\t\t}\n\t#endif\n}"].join("\n")},cube:{uniforms:{tCube:{type:"t",value:null},tFlip:{type:"f",value:-1}},vertexShader:["varying vec3 vWorldPosition;",THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {\n\tvec4 worldPosition = modelMatrix * vec4( position, 1.0 );\n\tvWorldPosition = worldPosition.xyz;\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
-THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform samplerCube tCube;\nuniform float tFlip;\nvarying vec3 vWorldPosition;",THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\n\tgl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );",THREE.ShaderChunk.logdepthbuf_fragment,"}"].join("\n")},depthRGBA:{uniforms:{},vertexShader:[THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,
-"void main() {",THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:[THREE.ShaderChunk.logdepthbuf_pars_fragment,"vec4 pack_depth( const in float depth ) {\n\tconst vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );\n\tconst vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );\n\tvec4 res = mod( depth * bit_shift * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );\n\tres -= res.xxyz * bit_mask;\n\treturn res;\n}\nvoid main() {",
-THREE.ShaderChunk.logdepthbuf_fragment,"\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tgl_FragData[ 0 ] = pack_depth( gl_FragDepthEXT );\n\t#else\n\t\tgl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );\n\t#endif\n}"].join("\n")}};
-THREE.WebGLRenderer=function(a){function b(a){var b=a.geometry;a=a.material;var c=b.vertices.length;if(a.attributes){void 0===b.__webglCustomAttributesList&&(b.__webglCustomAttributesList=[]);for(var d in a.attributes){var e=a.attributes[d];if(!e.__webglInitialized||e.createUniqueBuffers){e.__webglInitialized=!0;var f=1;"v2"===e.type?f=2:"v3"===e.type?f=3:"v4"===e.type?f=4:"c"===e.type&&(f=3);e.size=f;e.array=new Float32Array(c*f);e.buffer=l.createBuffer();e.buffer.belongsToAttribute=d;e.needsUpdate=
-!0}b.__webglCustomAttributesList.push(e)}}}function c(a,b){var c=b.geometry,e=a.faces3,f=3*e.length,g=1*e.length,h=3*e.length,e=d(b,a);a.__vertexArray=new Float32Array(3*f);a.__normalArray=new Float32Array(3*f);a.__colorArray=new Float32Array(3*f);a.__uvArray=new Float32Array(2*f);1<c.faceVertexUvs.length&&(a.__uv2Array=new Float32Array(2*f));c.hasTangents&&(a.__tangentArray=new Float32Array(4*f));b.geometry.skinWeights.length&&b.geometry.skinIndices.length&&(a.__skinIndexArray=new Float32Array(4*
-f),a.__skinWeightArray=new Float32Array(4*f));c=null!==pa.get("OES_element_index_uint")&&21845<g?Uint32Array:Uint16Array;a.__typeArray=c;a.__faceArray=new c(3*g);a.__lineArray=new c(2*h);var k;if(a.numMorphTargets)for(a.__morphTargetsArrays=[],c=0,k=a.numMorphTargets;c<k;c++)a.__morphTargetsArrays.push(new Float32Array(3*f));if(a.numMorphNormals)for(a.__morphNormalsArrays=[],c=0,k=a.numMorphNormals;c<k;c++)a.__morphNormalsArrays.push(new Float32Array(3*f));a.__webglFaceCount=3*g;a.__webglLineCount=
-2*h;if(e.attributes){void 0===a.__webglCustomAttributesList&&(a.__webglCustomAttributesList=[]);for(var m in e.attributes){var g=e.attributes[m],h={},n;for(n in g)h[n]=g[n];if(!h.__webglInitialized||h.createUniqueBuffers)h.__webglInitialized=!0,c=1,"v2"===h.type?c=2:"v3"===h.type?c=3:"v4"===h.type?c=4:"c"===h.type&&(c=3),h.size=c,h.array=new Float32Array(f*c),h.buffer=l.createBuffer(),h.buffer.belongsToAttribute=m,g.needsUpdate=!0,h.__original=g;a.__webglCustomAttributesList.push(h)}}a.__inittedArrays=
-!0}function d(a,b){return a.material instanceof THREE.MeshFaceMaterial?a.material.materials[b.materialIndex]:a.material}function e(a,b,c,d){c=c.attributes;var e=b.attributes;b=b.attributesKeys;for(var f=0,k=b.length;f<k;f++){var m=b[f],n=e[m];if(0<=n){var p=c[m];void 0!==p?(m=p.itemSize,l.bindBuffer(l.ARRAY_BUFFER,p.buffer),g(n),l.vertexAttribPointer(n,m,l.FLOAT,!1,0,d*m*4)):void 0!==a.defaultAttributeValues&&(2===a.defaultAttributeValues[m].length?l.vertexAttrib2fv(n,a.defaultAttributeValues[m]):
-3===a.defaultAttributeValues[m].length&&l.vertexAttrib3fv(n,a.defaultAttributeValues[m]))}}h()}function f(){for(var a=0,b=wb.length;a<b;a++)wb[a]=0}function g(a){wb[a]=1;0===ib[a]&&(l.enableVertexAttribArray(a),ib[a]=1)}function h(){for(var a=0,b=ib.length;a<b;a++)ib[a]!==wb[a]&&(l.disableVertexAttribArray(a),ib[a]=0)}function k(a,b){return a.material.id!==b.material.id?b.material.id-a.material.id:a.z!==b.z?b.z-a.z:a.id-b.id}function n(a,b){return a.z!==b.z?a.z-b.z:a.id-b.id}function p(a,b){return b[0]-
-a[0]}function q(a,e){if(!1!==e.visible){if(!(e instanceof THREE.Scene||e instanceof THREE.Group)){void 0===e.__webglInit&&(e.__webglInit=!0,e._modelViewMatrix=new THREE.Matrix4,e._normalMatrix=new THREE.Matrix3,e.addEventListener("removed",Hc));var f=e.geometry;if(void 0!==f&&void 0===f.__webglInit&&(f.__webglInit=!0,f.addEventListener("dispose",Ic),!(f instanceof THREE.BufferGeometry)))if(e instanceof THREE.Mesh)s(a,e,f);else if(e instanceof THREE.Line){if(void 0===f.__webglVertexBuffer){f.__webglVertexBuffer=
-l.createBuffer();f.__webglColorBuffer=l.createBuffer();f.__webglLineDistanceBuffer=l.createBuffer();J.info.memory.geometries++;var g=f.vertices.length;f.__vertexArray=new Float32Array(3*g);f.__colorArray=new Float32Array(3*g);f.__lineDistanceArray=new Float32Array(1*g);f.__webglLineCount=g;b(e);f.verticesNeedUpdate=!0;f.colorsNeedUpdate=!0;f.lineDistancesNeedUpdate=!0}}else if(e instanceof THREE.PointCloud&&void 0===f.__webglVertexBuffer){f.__webglVertexBuffer=l.createBuffer();f.__webglColorBuffer=
-l.createBuffer();J.info.memory.geometries++;var h=f.vertices.length;f.__vertexArray=new Float32Array(3*h);f.__colorArray=new Float32Array(3*h);f.__sortArray=[];f.__webglParticleCount=h;b(e);f.verticesNeedUpdate=!0;f.colorsNeedUpdate=!0}if(void 0===e.__webglActive)if(e.__webglActive=!0,e instanceof THREE.Mesh)if(f instanceof THREE.BufferGeometry)u(ob,f,e);else{if(f instanceof THREE.Geometry)for(var k=xb[f.id],m=0,n=k.length;m<n;m++)u(ob,k[m],e)}else e instanceof THREE.Line||e instanceof THREE.PointCloud?
-u(ob,f,e):(e instanceof THREE.ImmediateRenderObject||e.immediateRenderCallback)&&jb.push({id:null,object:e,opaque:null,transparent:null,z:0});if(e instanceof THREE.Light)cb.push(e);else if(e instanceof THREE.Sprite)yb.push(e);else if(e instanceof THREE.LensFlare)Ra.push(e);else{var t=ob[e.id];if(t&&(!1===e.frustumCulled||!0===Ec.intersectsObject(e))){var r=e.geometry,w,G;if(r instanceof THREE.BufferGeometry)for(var x=r.attributes,D=r.attributesKeys,E=0,B=D.length;E<B;E++){var A=D[E],K=x[A];void 0===
-K.buffer&&(K.buffer=l.createBuffer(),K.needsUpdate=!0);if(!0===K.needsUpdate){var F="index"===A?l.ELEMENT_ARRAY_BUFFER:l.ARRAY_BUFFER;l.bindBuffer(F,K.buffer);l.bufferData(F,K.array,l.STATIC_DRAW);K.needsUpdate=!1}}else if(e instanceof THREE.Mesh){!0===r.groupsNeedUpdate&&s(a,e,r);for(var H=xb[r.id],O=0,Q=H.length;O<Q;O++){var R=H[O];G=d(e,R);!0===r.groupsNeedUpdate&&c(R,e);w=G.attributes&&v(G);if(r.verticesNeedUpdate||r.morphTargetsNeedUpdate||r.elementsNeedUpdate||r.uvsNeedUpdate||r.normalsNeedUpdate||
-r.colorsNeedUpdate||r.tangentsNeedUpdate||w){var C=R,P=e,S=l.DYNAMIC_DRAW,T=!r.dynamic,X=G;if(C.__inittedArrays){var bb=X&&void 0!==X.shading&&X.shading===THREE.SmoothShading,M=void 0,ea=void 0,Y=void 0,ca=void 0,ma=void 0,pa=void 0,sa=void 0,Fa=void 0,la=void 0,hb=void 0,za=void 0,aa=void 0,$=void 0,Z=void 0,ya=void 0,qa=void 0,L=void 0,Ga=void 0,na=void 0,nc=void 0,ia=void 0,oc=void 0,pc=void 0,qc=void 0,Ba=void 0,zb=void 0,Ab=void 0,Ha=void 0,Bb=void 0,Aa=void 0,va=void 0,Cb=void 0,Oa=void 0,Qb=
-void 0,Ma=void 0,ib=void 0,Ya=void 0,Za=void 0,uc=void 0,Rb=void 0,db=0,eb=0,qb=0,rb=0,Db=0,Sa=0,Ca=0,Pa=0,Ka=0,ja=0,ta=0,I=0,Ia=void 0,Qa=C.__vertexArray,sb=C.__uvArray,fb=C.__uv2Array,Ta=C.__normalArray,ra=C.__tangentArray,La=C.__colorArray,Ua=C.__skinIndexArray,Va=C.__skinWeightArray,Eb=C.__morphTargetsArrays,Jc=C.__morphNormalsArrays,Kb=C.__webglCustomAttributesList,z=void 0,Sb=C.__faceArray,Ja=C.__lineArray,wa=P.geometry,$a=wa.elementsNeedUpdate,Kc=wa.uvsNeedUpdate,ec=wa.normalsNeedUpdate,da=
-wa.tangentsNeedUpdate,wb=wa.colorsNeedUpdate,U=wa.morphTargetsNeedUpdate,fa=wa.vertices,N=C.faces3,xa=wa.faces,ua=wa.faceVertexUvs[0],Lc=wa.faceVertexUvs[1],Fc=wa.skinIndices,Tb=wa.skinWeights,kb=wa.morphTargets,Da=wa.morphNormals;if(wa.verticesNeedUpdate){M=0;for(ea=N.length;M<ea;M++)ca=xa[N[M]],aa=fa[ca.a],$=fa[ca.b],Z=fa[ca.c],Qa[eb]=aa.x,Qa[eb+1]=aa.y,Qa[eb+2]=aa.z,Qa[eb+3]=$.x,Qa[eb+4]=$.y,Qa[eb+5]=$.z,Qa[eb+6]=Z.x,Qa[eb+7]=Z.y,Qa[eb+8]=Z.z,eb+=9;l.bindBuffer(l.ARRAY_BUFFER,C.__webglVertexBuffer);
-l.bufferData(l.ARRAY_BUFFER,Qa,S)}if(U)for(Ma=0,ib=kb.length;Ma<ib;Ma++){M=ta=0;for(ea=N.length;M<ea;M++)uc=N[M],ca=xa[uc],aa=kb[Ma].vertices[ca.a],$=kb[Ma].vertices[ca.b],Z=kb[Ma].vertices[ca.c],Ya=Eb[Ma],Ya[ta]=aa.x,Ya[ta+1]=aa.y,Ya[ta+2]=aa.z,Ya[ta+3]=$.x,Ya[ta+4]=$.y,Ya[ta+5]=$.z,Ya[ta+6]=Z.x,Ya[ta+7]=Z.y,Ya[ta+8]=Z.z,X.morphNormals&&(bb?(Rb=Da[Ma].vertexNormals[uc],Ga=Rb.a,na=Rb.b,nc=Rb.c):nc=na=Ga=Da[Ma].faceNormals[uc],Za=Jc[Ma],Za[ta]=Ga.x,Za[ta+1]=Ga.y,Za[ta+2]=Ga.z,Za[ta+3]=na.x,Za[ta+4]=
-na.y,Za[ta+5]=na.z,Za[ta+6]=nc.x,Za[ta+7]=nc.y,Za[ta+8]=nc.z),ta+=9;l.bindBuffer(l.ARRAY_BUFFER,C.__webglMorphTargetsBuffers[Ma]);l.bufferData(l.ARRAY_BUFFER,Eb[Ma],S);X.morphNormals&&(l.bindBuffer(l.ARRAY_BUFFER,C.__webglMorphNormalsBuffers[Ma]),l.bufferData(l.ARRAY_BUFFER,Jc[Ma],S))}if(Tb.length){M=0;for(ea=N.length;M<ea;M++)ca=xa[N[M]],qc=Tb[ca.a],Ba=Tb[ca.b],zb=Tb[ca.c],Va[ja]=qc.x,Va[ja+1]=qc.y,Va[ja+2]=qc.z,Va[ja+3]=qc.w,Va[ja+4]=Ba.x,Va[ja+5]=Ba.y,Va[ja+6]=Ba.z,Va[ja+7]=Ba.w,Va[ja+8]=zb.x,
-Va[ja+9]=zb.y,Va[ja+10]=zb.z,Va[ja+11]=zb.w,Ab=Fc[ca.a],Ha=Fc[ca.b],Bb=Fc[ca.c],Ua[ja]=Ab.x,Ua[ja+1]=Ab.y,Ua[ja+2]=Ab.z,Ua[ja+3]=Ab.w,Ua[ja+4]=Ha.x,Ua[ja+5]=Ha.y,Ua[ja+6]=Ha.z,Ua[ja+7]=Ha.w,Ua[ja+8]=Bb.x,Ua[ja+9]=Bb.y,Ua[ja+10]=Bb.z,Ua[ja+11]=Bb.w,ja+=12;0<ja&&(l.bindBuffer(l.ARRAY_BUFFER,C.__webglSkinIndicesBuffer),l.bufferData(l.ARRAY_BUFFER,Ua,S),l.bindBuffer(l.ARRAY_BUFFER,C.__webglSkinWeightsBuffer),l.bufferData(l.ARRAY_BUFFER,Va,S))}if(wb){M=0;for(ea=N.length;M<ea;M++)ca=xa[N[M]],sa=ca.vertexColors,
-Fa=ca.color,3===sa.length&&X.vertexColors===THREE.VertexColors?(ia=sa[0],oc=sa[1],pc=sa[2]):pc=oc=ia=Fa,La[Ka]=ia.r,La[Ka+1]=ia.g,La[Ka+2]=ia.b,La[Ka+3]=oc.r,La[Ka+4]=oc.g,La[Ka+5]=oc.b,La[Ka+6]=pc.r,La[Ka+7]=pc.g,La[Ka+8]=pc.b,Ka+=9;0<Ka&&(l.bindBuffer(l.ARRAY_BUFFER,C.__webglColorBuffer),l.bufferData(l.ARRAY_BUFFER,La,S))}if(da&&wa.hasTangents){M=0;for(ea=N.length;M<ea;M++)ca=xa[N[M]],la=ca.vertexTangents,ya=la[0],qa=la[1],L=la[2],ra[Ca]=ya.x,ra[Ca+1]=ya.y,ra[Ca+2]=ya.z,ra[Ca+3]=ya.w,ra[Ca+4]=qa.x,
-ra[Ca+5]=qa.y,ra[Ca+6]=qa.z,ra[Ca+7]=qa.w,ra[Ca+8]=L.x,ra[Ca+9]=L.y,ra[Ca+10]=L.z,ra[Ca+11]=L.w,Ca+=12;l.bindBuffer(l.ARRAY_BUFFER,C.__webglTangentBuffer);l.bufferData(l.ARRAY_BUFFER,ra,S)}if(ec){M=0;for(ea=N.length;M<ea;M++)if(ca=xa[N[M]],ma=ca.vertexNormals,pa=ca.normal,3===ma.length&&bb)for(Aa=0;3>Aa;Aa++)Cb=ma[Aa],Ta[Sa]=Cb.x,Ta[Sa+1]=Cb.y,Ta[Sa+2]=Cb.z,Sa+=3;else for(Aa=0;3>Aa;Aa++)Ta[Sa]=pa.x,Ta[Sa+1]=pa.y,Ta[Sa+2]=pa.z,Sa+=3;l.bindBuffer(l.ARRAY_BUFFER,C.__webglNormalBuffer);l.bufferData(l.ARRAY_BUFFER,
-Ta,S)}if(Kc&&ua){M=0;for(ea=N.length;M<ea;M++)if(Y=N[M],hb=ua[Y],void 0!==hb)for(Aa=0;3>Aa;Aa++)Oa=hb[Aa],sb[qb]=Oa.x,sb[qb+1]=Oa.y,qb+=2;0<qb&&(l.bindBuffer(l.ARRAY_BUFFER,C.__webglUVBuffer),l.bufferData(l.ARRAY_BUFFER,sb,S))}if(Kc&&Lc){M=0;for(ea=N.length;M<ea;M++)if(Y=N[M],za=Lc[Y],void 0!==za)for(Aa=0;3>Aa;Aa++)Qb=za[Aa],fb[rb]=Qb.x,fb[rb+1]=Qb.y,rb+=2;0<rb&&(l.bindBuffer(l.ARRAY_BUFFER,C.__webglUV2Buffer),l.bufferData(l.ARRAY_BUFFER,fb,S))}if($a){M=0;for(ea=N.length;M<ea;M++)Sb[Db]=db,Sb[Db+
-1]=db+1,Sb[Db+2]=db+2,Db+=3,Ja[Pa]=db,Ja[Pa+1]=db+1,Ja[Pa+2]=db,Ja[Pa+3]=db+2,Ja[Pa+4]=db+1,Ja[Pa+5]=db+2,Pa+=6,db+=3;l.bindBuffer(l.ELEMENT_ARRAY_BUFFER,C.__webglFaceBuffer);l.bufferData(l.ELEMENT_ARRAY_BUFFER,Sb,S);l.bindBuffer(l.ELEMENT_ARRAY_BUFFER,C.__webglLineBuffer);l.bufferData(l.ELEMENT_ARRAY_BUFFER,Ja,S)}if(Kb)for(Aa=0,va=Kb.length;Aa<va;Aa++)if(z=Kb[Aa],z.__original.needsUpdate){I=0;if(1===z.size)if(void 0===z.boundTo||"vertices"===z.boundTo)for(M=0,ea=N.length;M<ea;M++)ca=xa[N[M]],z.array[I]=
-z.value[ca.a],z.array[I+1]=z.value[ca.b],z.array[I+2]=z.value[ca.c],I+=3;else{if("faces"===z.boundTo)for(M=0,ea=N.length;M<ea;M++)Ia=z.value[N[M]],z.array[I]=Ia,z.array[I+1]=Ia,z.array[I+2]=Ia,I+=3}else if(2===z.size)if(void 0===z.boundTo||"vertices"===z.boundTo)for(M=0,ea=N.length;M<ea;M++)ca=xa[N[M]],aa=z.value[ca.a],$=z.value[ca.b],Z=z.value[ca.c],z.array[I]=aa.x,z.array[I+1]=aa.y,z.array[I+2]=$.x,z.array[I+3]=$.y,z.array[I+4]=Z.x,z.array[I+5]=Z.y,I+=6;else{if("faces"===z.boundTo)for(M=0,ea=N.length;M<
-ea;M++)Z=$=aa=Ia=z.value[N[M]],z.array[I]=aa.x,z.array[I+1]=aa.y,z.array[I+2]=$.x,z.array[I+3]=$.y,z.array[I+4]=Z.x,z.array[I+5]=Z.y,I+=6}else if(3===z.size){var ka;ka="c"===z.type?["r","g","b"]:["x","y","z"];if(void 0===z.boundTo||"vertices"===z.boundTo)for(M=0,ea=N.length;M<ea;M++)ca=xa[N[M]],aa=z.value[ca.a],$=z.value[ca.b],Z=z.value[ca.c],z.array[I]=aa[ka[0]],z.array[I+1]=aa[ka[1]],z.array[I+2]=aa[ka[2]],z.array[I+3]=$[ka[0]],z.array[I+4]=$[ka[1]],z.array[I+5]=$[ka[2]],z.array[I+6]=Z[ka[0]],z.array[I+
-7]=Z[ka[1]],z.array[I+8]=Z[ka[2]],I+=9;else if("faces"===z.boundTo)for(M=0,ea=N.length;M<ea;M++)Z=$=aa=Ia=z.value[N[M]],z.array[I]=aa[ka[0]],z.array[I+1]=aa[ka[1]],z.array[I+2]=aa[ka[2]],z.array[I+3]=$[ka[0]],z.array[I+4]=$[ka[1]],z.array[I+5]=$[ka[2]],z.array[I+6]=Z[ka[0]],z.array[I+7]=Z[ka[1]],z.array[I+8]=Z[ka[2]],I+=9;else if("faceVertices"===z.boundTo)for(M=0,ea=N.length;M<ea;M++)Ia=z.value[N[M]],aa=Ia[0],$=Ia[1],Z=Ia[2],z.array[I]=aa[ka[0]],z.array[I+1]=aa[ka[1]],z.array[I+2]=aa[ka[2]],z.array[I+
-3]=$[ka[0]],z.array[I+4]=$[ka[1]],z.array[I+5]=$[ka[2]],z.array[I+6]=Z[ka[0]],z.array[I+7]=Z[ka[1]],z.array[I+8]=Z[ka[2]],I+=9}else if(4===z.size)if(void 0===z.boundTo||"vertices"===z.boundTo)for(M=0,ea=N.length;M<ea;M++)ca=xa[N[M]],aa=z.value[ca.a],$=z.value[ca.b],Z=z.value[ca.c],z.array[I]=aa.x,z.array[I+1]=aa.y,z.array[I+2]=aa.z,z.array[I+3]=aa.w,z.array[I+4]=$.x,z.array[I+5]=$.y,z.array[I+6]=$.z,z.array[I+7]=$.w,z.array[I+8]=Z.x,z.array[I+9]=Z.y,z.array[I+10]=Z.z,z.array[I+11]=Z.w,I+=12;else if("faces"===
-z.boundTo)for(M=0,ea=N.length;M<ea;M++)Z=$=aa=Ia=z.value[N[M]],z.array[I]=aa.x,z.array[I+1]=aa.y,z.array[I+2]=aa.z,z.array[I+3]=aa.w,z.array[I+4]=$.x,z.array[I+5]=$.y,z.array[I+6]=$.z,z.array[I+7]=$.w,z.array[I+8]=Z.x,z.array[I+9]=Z.y,z.array[I+10]=Z.z,z.array[I+11]=Z.w,I+=12;else if("faceVertices"===z.boundTo)for(M=0,ea=N.length;M<ea;M++)Ia=z.value[N[M]],aa=Ia[0],$=Ia[1],Z=Ia[2],z.array[I]=aa.x,z.array[I+1]=aa.y,z.array[I+2]=aa.z,z.array[I+3]=aa.w,z.array[I+4]=$.x,z.array[I+5]=$.y,z.array[I+6]=$.z,
-z.array[I+7]=$.w,z.array[I+8]=Z.x,z.array[I+9]=Z.y,z.array[I+10]=Z.z,z.array[I+11]=Z.w,I+=12;l.bindBuffer(l.ARRAY_BUFFER,z.buffer);l.bufferData(l.ARRAY_BUFFER,z.array,S)}T&&(delete C.__inittedArrays,delete C.__colorArray,delete C.__normalArray,delete C.__tangentArray,delete C.__uvArray,delete C.__uv2Array,delete C.__faceArray,delete C.__vertexArray,delete C.__lineArray,delete C.__skinIndexArray,delete C.__skinWeightArray)}}}r.verticesNeedUpdate=!1;r.morphTargetsNeedUpdate=!1;r.elementsNeedUpdate=
-!1;r.uvsNeedUpdate=!1;r.normalsNeedUpdate=!1;r.colorsNeedUpdate=!1;r.tangentsNeedUpdate=!1;G.attributes&&y(G)}else if(e instanceof THREE.Line){G=d(e,r);w=G.attributes&&v(G);if(r.verticesNeedUpdate||r.colorsNeedUpdate||r.lineDistancesNeedUpdate||w){var Zb=l.DYNAMIC_DRAW,ab,Fb,gb,$b,ga,vc,dc=r.vertices,fc=r.colors,Pb=r.lineDistances,kc=dc.length,lc=fc.length,mc=Pb.length,wc=r.__vertexArray,xc=r.__colorArray,jc=r.__lineDistanceArray,sc=r.colorsNeedUpdate,tc=r.lineDistancesNeedUpdate,gc=r.__webglCustomAttributesList,
-yc,Lb,Ea,hc,Wa,oa;if(r.verticesNeedUpdate){for(ab=0;ab<kc;ab++)$b=dc[ab],ga=3*ab,wc[ga]=$b.x,wc[ga+1]=$b.y,wc[ga+2]=$b.z;l.bindBuffer(l.ARRAY_BUFFER,r.__webglVertexBuffer);l.bufferData(l.ARRAY_BUFFER,wc,Zb)}if(sc){for(Fb=0;Fb<lc;Fb++)vc=fc[Fb],ga=3*Fb,xc[ga]=vc.r,xc[ga+1]=vc.g,xc[ga+2]=vc.b;l.bindBuffer(l.ARRAY_BUFFER,r.__webglColorBuffer);l.bufferData(l.ARRAY_BUFFER,xc,Zb)}if(tc){for(gb=0;gb<mc;gb++)jc[gb]=Pb[gb];l.bindBuffer(l.ARRAY_BUFFER,r.__webglLineDistanceBuffer);l.bufferData(l.ARRAY_BUFFER,
-jc,Zb)}if(gc)for(yc=0,Lb=gc.length;yc<Lb;yc++)if(oa=gc[yc],oa.needsUpdate&&(void 0===oa.boundTo||"vertices"===oa.boundTo)){ga=0;hc=oa.value.length;if(1===oa.size)for(Ea=0;Ea<hc;Ea++)oa.array[Ea]=oa.value[Ea];else if(2===oa.size)for(Ea=0;Ea<hc;Ea++)Wa=oa.value[Ea],oa.array[ga]=Wa.x,oa.array[ga+1]=Wa.y,ga+=2;else if(3===oa.size)if("c"===oa.type)for(Ea=0;Ea<hc;Ea++)Wa=oa.value[Ea],oa.array[ga]=Wa.r,oa.array[ga+1]=Wa.g,oa.array[ga+2]=Wa.b,ga+=3;else for(Ea=0;Ea<hc;Ea++)Wa=oa.value[Ea],oa.array[ga]=Wa.x,
-oa.array[ga+1]=Wa.y,oa.array[ga+2]=Wa.z,ga+=3;else if(4===oa.size)for(Ea=0;Ea<hc;Ea++)Wa=oa.value[Ea],oa.array[ga]=Wa.x,oa.array[ga+1]=Wa.y,oa.array[ga+2]=Wa.z,oa.array[ga+3]=Wa.w,ga+=4;l.bindBuffer(l.ARRAY_BUFFER,oa.buffer);l.bufferData(l.ARRAY_BUFFER,oa.array,Zb)}}r.verticesNeedUpdate=!1;r.colorsNeedUpdate=!1;r.lineDistancesNeedUpdate=!1;G.attributes&&y(G)}else if(e instanceof THREE.PointCloud){G=d(e,r);w=G.attributes&&v(G);if(r.verticesNeedUpdate||r.colorsNeedUpdate||e.sortParticles||w){var Mb=
-l.DYNAMIC_DRAW,Xa,tb,ub,W,vb,Ub,zc=r.vertices,pb=zc.length,Nb=r.colors,Ob=Nb.length,ac=r.__vertexArray,bc=r.__colorArray,Gb=r.__sortArray,Xb=r.verticesNeedUpdate,Yb=r.colorsNeedUpdate,Hb=r.__webglCustomAttributesList,lb,ic,ba,mb,ha,V;if(e.sortParticles){Gc.copy(Ac);Gc.multiply(e.matrixWorld);for(Xa=0;Xa<pb;Xa++)ub=zc[Xa],Na.copy(ub),Na.applyProjection(Gc),Gb[Xa]=[Na.z,Xa];Gb.sort(p);for(Xa=0;Xa<pb;Xa++)ub=zc[Gb[Xa][1]],W=3*Xa,ac[W]=ub.x,ac[W+1]=ub.y,ac[W+2]=ub.z;for(tb=0;tb<Ob;tb++)W=3*tb,Ub=Nb[Gb[tb][1]],
-bc[W]=Ub.r,bc[W+1]=Ub.g,bc[W+2]=Ub.b;if(Hb)for(lb=0,ic=Hb.length;lb<ic;lb++)if(V=Hb[lb],void 0===V.boundTo||"vertices"===V.boundTo)if(W=0,mb=V.value.length,1===V.size)for(ba=0;ba<mb;ba++)vb=Gb[ba][1],V.array[ba]=V.value[vb];else if(2===V.size)for(ba=0;ba<mb;ba++)vb=Gb[ba][1],ha=V.value[vb],V.array[W]=ha.x,V.array[W+1]=ha.y,W+=2;else if(3===V.size)if("c"===V.type)for(ba=0;ba<mb;ba++)vb=Gb[ba][1],ha=V.value[vb],V.array[W]=ha.r,V.array[W+1]=ha.g,V.array[W+2]=ha.b,W+=3;else for(ba=0;ba<mb;ba++)vb=Gb[ba][1],
-ha=V.value[vb],V.array[W]=ha.x,V.array[W+1]=ha.y,V.array[W+2]=ha.z,W+=3;else if(4===V.size)for(ba=0;ba<mb;ba++)vb=Gb[ba][1],ha=V.value[vb],V.array[W]=ha.x,V.array[W+1]=ha.y,V.array[W+2]=ha.z,V.array[W+3]=ha.w,W+=4}else{if(Xb)for(Xa=0;Xa<pb;Xa++)ub=zc[Xa],W=3*Xa,ac[W]=ub.x,ac[W+1]=ub.y,ac[W+2]=ub.z;if(Yb)for(tb=0;tb<Ob;tb++)Ub=Nb[tb],W=3*tb,bc[W]=Ub.r,bc[W+1]=Ub.g,bc[W+2]=Ub.b;if(Hb)for(lb=0,ic=Hb.length;lb<ic;lb++)if(V=Hb[lb],V.needsUpdate&&(void 0===V.boundTo||"vertices"===V.boundTo))if(mb=V.value.length,
-W=0,1===V.size)for(ba=0;ba<mb;ba++)V.array[ba]=V.value[ba];else if(2===V.size)for(ba=0;ba<mb;ba++)ha=V.value[ba],V.array[W]=ha.x,V.array[W+1]=ha.y,W+=2;else if(3===V.size)if("c"===V.type)for(ba=0;ba<mb;ba++)ha=V.value[ba],V.array[W]=ha.r,V.array[W+1]=ha.g,V.array[W+2]=ha.b,W+=3;else for(ba=0;ba<mb;ba++)ha=V.value[ba],V.array[W]=ha.x,V.array[W+1]=ha.y,V.array[W+2]=ha.z,W+=3;else if(4===V.size)for(ba=0;ba<mb;ba++)ha=V.value[ba],V.array[W]=ha.x,V.array[W+1]=ha.y,V.array[W+2]=ha.z,V.array[W+3]=ha.w,W+=
-4}if(Xb||e.sortParticles)l.bindBuffer(l.ARRAY_BUFFER,r.__webglVertexBuffer),l.bufferData(l.ARRAY_BUFFER,ac,Mb);if(Yb||e.sortParticles)l.bindBuffer(l.ARRAY_BUFFER,r.__webglColorBuffer),l.bufferData(l.ARRAY_BUFFER,bc,Mb);if(Hb)for(lb=0,ic=Hb.length;lb<ic;lb++)if(V=Hb[lb],V.needsUpdate||e.sortParticles)l.bindBuffer(l.ARRAY_BUFFER,V.buffer),l.bufferData(l.ARRAY_BUFFER,V.array,Mb)}r.verticesNeedUpdate=!1;r.colorsNeedUpdate=!1;G.attributes&&y(G)}for(var cc=0,nb=t.length;cc<nb;cc++){var Bc=t[cc],Vb=Bc,rc=
-Vb.object,Cc=Vb.buffer,Dc=rc.geometry,Wb=rc.material;Wb instanceof THREE.MeshFaceMaterial?(Wb=Wb.materials[Dc instanceof THREE.BufferGeometry?0:Cc.materialIndex],Vb.material=Wb,Wb.transparent?Ib.push(Vb):Jb.push(Vb)):Wb&&(Vb.material=Wb,Wb.transparent?Ib.push(Vb):Jb.push(Vb));Bc.render=!0;!0===J.sortObjects&&(null!==e.renderDepth?Bc.z=e.renderDepth:(Na.setFromMatrixPosition(e.matrixWorld),Na.applyProjection(Ac),Bc.z=Na.z))}}}}cc=0;for(nb=e.children.length;cc<nb;cc++)q(a,e.children[cc])}}function m(a,
-b,c,d,e,f){for(var g,h=a.length-1;-1!==h;h--){g=a[h];var k=g.object,l=g.buffer;x(k,b);if(f)g=f;else{g=g.material;if(!g)continue;e&&J.setBlending(g.blending,g.blendEquation,g.blendSrc,g.blendDst);J.setDepthTest(g.depthTest);J.setDepthWrite(g.depthWrite);B(g.polygonOffset,g.polygonOffsetFactor,g.polygonOffsetUnits)}J.setMaterialFaces(g);l instanceof THREE.BufferGeometry?J.renderBufferDirect(b,c,d,g,l,k):J.renderBuffer(b,c,d,g,l,k)}}function r(a,b,c,d,e,f,g){for(var h,k=0,l=a.length;k<l;k++){h=a[k];
-var m=h.object;if(m.visible){if(g)h=g;else{h=h[b];if(!h)continue;f&&J.setBlending(h.blending,h.blendEquation,h.blendSrc,h.blendDst);J.setDepthTest(h.depthTest);J.setDepthWrite(h.depthWrite);B(h.polygonOffset,h.polygonOffsetFactor,h.polygonOffsetUnits)}J.renderImmediateObject(c,d,e,h,m)}}}function t(a){var b=a.object.material;b.transparent?(a.transparent=b,a.opaque=null):(a.opaque=b,a.transparent=null)}function s(a,b,d){var e=b.material,f=!1;if(void 0===xb[d.id]||!0===d.groupsNeedUpdate){delete ob[b.id];
-a=xb;for(var g=d.id,e=e instanceof THREE.MeshFaceMaterial,h=pa.get("OES_element_index_uint")?4294967296:65535,k,f={},m=d.morphTargets.length,n=d.morphNormals.length,p,r={},q=[],t=0,s=d.faces.length;t<s;t++){k=d.faces[t];var v=e?k.materialIndex:0;v in f||(f[v]={hash:v,counter:0});k=f[v].hash+"_"+f[v].counter;k in r||(p={id:rc++,faces3:[],materialIndex:v,vertices:0,numMorphTargets:m,numMorphNormals:n},r[k]=p,q.push(p));r[k].vertices+3>h&&(f[v].counter+=1,k=f[v].hash+"_"+f[v].counter,k in r||(p={id:rc++,
-faces3:[],materialIndex:v,vertices:0,numMorphTargets:m,numMorphNormals:n},r[k]=p,q.push(p)));r[k].faces3.push(t);r[k].vertices+=3}a[g]=q;d.groupsNeedUpdate=!1}a=xb[d.id];g=0;for(e=a.length;g<e;g++){h=a[g];if(void 0===h.__webglVertexBuffer){f=h;f.__webglVertexBuffer=l.createBuffer();f.__webglNormalBuffer=l.createBuffer();f.__webglTangentBuffer=l.createBuffer();f.__webglColorBuffer=l.createBuffer();f.__webglUVBuffer=l.createBuffer();f.__webglUV2Buffer=l.createBuffer();f.__webglSkinIndicesBuffer=l.createBuffer();
-f.__webglSkinWeightsBuffer=l.createBuffer();f.__webglFaceBuffer=l.createBuffer();f.__webglLineBuffer=l.createBuffer();n=m=void 0;if(f.numMorphTargets)for(f.__webglMorphTargetsBuffers=[],m=0,n=f.numMorphTargets;m<n;m++)f.__webglMorphTargetsBuffers.push(l.createBuffer());if(f.numMorphNormals)for(f.__webglMorphNormalsBuffers=[],m=0,n=f.numMorphNormals;m<n;m++)f.__webglMorphNormalsBuffers.push(l.createBuffer());J.info.memory.geometries++;c(h,b);d.verticesNeedUpdate=!0;d.morphTargetsNeedUpdate=!0;d.elementsNeedUpdate=
-!0;d.uvsNeedUpdate=!0;d.normalsNeedUpdate=!0;d.tangentsNeedUpdate=!0;f=d.colorsNeedUpdate=!0}else f=!1;(f||void 0===b.__webglActive)&&u(ob,h,b)}b.__webglActive=!0}function u(a,b,c){var d=c.id;a[d]=a[d]||[];a[d].push({id:d,buffer:b,object:c,material:null,z:0})}function v(a){for(var b in a.attributes)if(a.attributes[b].needsUpdate)return!0;return!1}function y(a){for(var b in a.attributes)a.attributes[b].needsUpdate=!1}function G(a,b,c,d,e){var f,g,h,k;dc=0;if(d.needsUpdate){d.program&&Cc(d);d.addEventListener("dispose",
-Dc);var m;d instanceof THREE.MeshDepthMaterial?m="depth":d instanceof THREE.MeshNormalMaterial?m="normal":d instanceof THREE.MeshBasicMaterial?m="basic":d instanceof THREE.MeshLambertMaterial?m="lambert":d instanceof THREE.MeshPhongMaterial?m="phong":d instanceof THREE.LineBasicMaterial?m="basic":d instanceof THREE.LineDashedMaterial?m="dashed":d instanceof THREE.PointCloudMaterial&&(m="particle_basic");if(m){var n=THREE.ShaderLib[m];d.__webglShader={uniforms:THREE.UniformsUtils.clone(n.uniforms),
-vertexShader:n.vertexShader,fragmentShader:n.fragmentShader}}else d.__webglShader={uniforms:d.uniforms,vertexShader:d.vertexShader,fragmentShader:d.fragmentShader};for(var p=0,r=0,q=0,t=0,s=0,u=b.length;s<u;s++){var v=b[s];v.onlyShadow||!1===v.visible||(v instanceof THREE.DirectionalLight&&p++,v instanceof THREE.PointLight&&r++,v instanceof THREE.SpotLight&&q++,v instanceof THREE.HemisphereLight&&t++)}f=p;g=r;h=q;k=t;for(var y,G=0,x=0,B=b.length;x<B;x++){var A=b[x];A.castShadow&&(A instanceof THREE.SpotLight&&
-G++,A instanceof THREE.DirectionalLight&&!A.shadowCascade&&G++)}y=G;var C;if(jc&&e&&e.skeleton&&e.skeleton.useVertexTexture)C=1024;else{var H=l.getParameter(l.MAX_VERTEX_UNIFORM_VECTORS),S=Math.floor((H-20)/4);void 0!==e&&e instanceof THREE.SkinnedMesh&&(S=Math.min(e.skeleton.bones.length,S),S<e.skeleton.bones.length&&console.warn("WebGLRenderer: too many bones - "+e.skeleton.bones.length+", this GPU supports just "+S+" (try OpenGL instead of ANGLE)"));C=S}var P={precision:X,supportsVertexTextures:sc,
-map:!!d.map,envMap:!!d.envMap,lightMap:!!d.lightMap,bumpMap:!!d.bumpMap,normalMap:!!d.normalMap,specularMap:!!d.specularMap,alphaMap:!!d.alphaMap,vertexColors:d.vertexColors,fog:c,useFog:d.fog,fogExp:c instanceof THREE.FogExp2,sizeAttenuation:d.sizeAttenuation,logarithmicDepthBuffer:Fa,skinning:d.skinning,maxBones:C,useVertexTexture:jc&&e&&e.skeleton&&e.skeleton.useVertexTexture,morphTargets:d.morphTargets,morphNormals:d.morphNormals,maxMorphTargets:J.maxMorphTargets,maxMorphNormals:J.maxMorphNormals,
-maxDirLights:f,maxPointLights:g,maxSpotLights:h,maxHemiLights:k,maxShadows:y,shadowMapEnabled:J.shadowMapEnabled&&e.receiveShadow&&0<y,shadowMapType:J.shadowMapType,shadowMapDebug:J.shadowMapDebug,shadowMapCascade:J.shadowMapCascade,alphaTest:d.alphaTest,metal:d.metal,wrapAround:d.wrapAround,doubleSided:d.side===THREE.DoubleSide,flipSided:d.side===THREE.BackSide},T=[];m?T.push(m):(T.push(d.fragmentShader),T.push(d.vertexShader));if(void 0!==d.defines)for(var bb in d.defines)T.push(bb),T.push(d.defines[bb]);
-for(bb in P)T.push(bb),T.push(P[bb]);for(var M=T.join(),Y,jb=0,ca=hb.length;jb<ca;jb++){var cb=hb[jb];if(cb.code===M){Y=cb;Y.usedTimes++;break}}void 0===Y&&(Y=new THREE.WebGLProgram(J,M,d,P),hb.push(Y),J.info.memory.programs=hb.length);d.program=Y;var ob=Y.attributes;if(d.morphTargets){d.numSupportedMorphTargets=0;for(var ma,pa="morphTarget",la=0;la<J.maxMorphTargets;la++)ma=pa+la,0<=ob[ma]&&d.numSupportedMorphTargets++}if(d.morphNormals)for(d.numSupportedMorphNormals=0,pa="morphNormal",la=0;la<J.maxMorphNormals;la++)ma=
-pa+la,0<=ob[ma]&&d.numSupportedMorphNormals++;d.uniformsList=[];for(var Jb in d.__webglShader.uniforms){var za=d.program.uniforms[Jb];za&&d.uniformsList.push([d.__webglShader.uniforms[Jb],za])}d.needsUpdate=!1}d.morphTargets&&!e.__webglMorphTargetInfluences&&(e.__webglMorphTargetInfluences=new Float32Array(J.maxMorphTargets));var aa=!1,$=!1,Z=!1,yb=d.program,qa=yb.uniforms,L=d.__webglShader.uniforms;yb.id!==tc&&(l.useProgram(yb.program),tc=yb.id,Z=$=aa=!0);d.id!==Kb&&(-1===Kb&&(Z=!0),Kb=d.id,$=!0);
-if(aa||a!==ec)l.uniformMatrix4fv(qa.projectionMatrix,!1,a.projectionMatrix.elements),Fa&&l.uniform1f(qa.logDepthBufFC,2/(Math.log(a.far+1)/Math.LN2)),a!==ec&&(ec=a),(d instanceof THREE.ShaderMaterial||d instanceof THREE.MeshPhongMaterial||d.envMap)&&null!==qa.cameraPosition&&(Na.setFromMatrixPosition(a.matrixWorld),l.uniform3f(qa.cameraPosition,Na.x,Na.y,Na.z)),(d instanceof THREE.MeshPhongMaterial||d instanceof THREE.MeshLambertMaterial||d instanceof THREE.ShaderMaterial||d.skinning)&&null!==qa.viewMatrix&&
-l.uniformMatrix4fv(qa.viewMatrix,!1,a.matrixWorldInverse.elements);if(d.skinning)if(e.bindMatrix&&null!==qa.bindMatrix&&l.uniformMatrix4fv(qa.bindMatrix,!1,e.bindMatrix.elements),e.bindMatrixInverse&&null!==qa.bindMatrixInverse&&l.uniformMatrix4fv(qa.bindMatrixInverse,!1,e.bindMatrixInverse.elements),jc&&e.skeleton&&e.skeleton.useVertexTexture){if(null!==qa.boneTexture){var Ib=K();l.uniform1i(qa.boneTexture,Ib);J.setTexture(e.skeleton.boneTexture,Ib)}null!==qa.boneTextureWidth&&l.uniform1i(qa.boneTextureWidth,
-e.skeleton.boneTextureWidth);null!==qa.boneTextureHeight&&l.uniform1i(qa.boneTextureHeight,e.skeleton.boneTextureHeight)}else e.skeleton&&e.skeleton.boneMatrices&&null!==qa.boneGlobalMatrices&&l.uniformMatrix4fv(qa.boneGlobalMatrices,!1,e.skeleton.boneMatrices);if($){c&&d.fog&&(L.fogColor.value=c.color,c instanceof THREE.Fog?(L.fogNear.value=c.near,L.fogFar.value=c.far):c instanceof THREE.FogExp2&&(L.fogDensity.value=c.density));if(d instanceof THREE.MeshPhongMaterial||d instanceof THREE.MeshLambertMaterial||
-d.lights){if(fc){var Z=!0,na,Ra,ia,ya=0,Ga=0,Oa=0,Ba,zb,Ab,Ha,Bb,Aa,va=Mc,Cb=va.directional.colors,ib=va.directional.positions,Qb=va.point.colors,Ma=va.point.positions,xb=va.point.distances,Ya=va.spot.colors,Za=va.spot.positions,Mb=va.spot.distances,Rb=va.spot.directions,db=va.spot.anglesCos,eb=va.spot.exponents,qb=va.hemi.skyColors,rb=va.hemi.groundColors,Db=va.hemi.positions,Sa=0,Ca=0,Pa=0,Ka=0,ja=0,ta=0,I=0,Ia=0,Qa=0,sb=0,fb=0,Ta=0;na=0;for(Ra=b.length;na<Ra;na++)ia=b[na],ia.onlyShadow||(Ba=ia.color,
-Ha=ia.intensity,Aa=ia.distance,ia instanceof THREE.AmbientLight?ia.visible&&(J.gammaInput?(ya+=Ba.r*Ba.r,Ga+=Ba.g*Ba.g,Oa+=Ba.b*Ba.b):(ya+=Ba.r,Ga+=Ba.g,Oa+=Ba.b)):ia instanceof THREE.DirectionalLight?(ja+=1,ia.visible&&(sa.setFromMatrixPosition(ia.matrixWorld),Na.setFromMatrixPosition(ia.target.matrixWorld),sa.sub(Na),sa.normalize(),Qa=3*Sa,ib[Qa]=sa.x,ib[Qa+1]=sa.y,ib[Qa+2]=sa.z,J.gammaInput?D(Cb,Qa,Ba,Ha*Ha):E(Cb,Qa,Ba,Ha),Sa+=1)):ia instanceof THREE.PointLight?(ta+=1,ia.visible&&(sb=3*Ca,J.gammaInput?
-D(Qb,sb,Ba,Ha*Ha):E(Qb,sb,Ba,Ha),Na.setFromMatrixPosition(ia.matrixWorld),Ma[sb]=Na.x,Ma[sb+1]=Na.y,Ma[sb+2]=Na.z,xb[Ca]=Aa,Ca+=1)):ia instanceof THREE.SpotLight?(I+=1,ia.visible&&(fb=3*Pa,J.gammaInput?D(Ya,fb,Ba,Ha*Ha):E(Ya,fb,Ba,Ha),sa.setFromMatrixPosition(ia.matrixWorld),Za[fb]=sa.x,Za[fb+1]=sa.y,Za[fb+2]=sa.z,Mb[Pa]=Aa,Na.setFromMatrixPosition(ia.target.matrixWorld),sa.sub(Na),sa.normalize(),Rb[fb]=sa.x,Rb[fb+1]=sa.y,Rb[fb+2]=sa.z,db[Pa]=Math.cos(ia.angle),eb[Pa]=ia.exponent,Pa+=1)):ia instanceof
-THREE.HemisphereLight&&(Ia+=1,ia.visible&&(sa.setFromMatrixPosition(ia.matrixWorld),sa.normalize(),Ta=3*Ka,Db[Ta]=sa.x,Db[Ta+1]=sa.y,Db[Ta+2]=sa.z,zb=ia.color,Ab=ia.groundColor,J.gammaInput?(Bb=Ha*Ha,D(qb,Ta,zb,Bb),D(rb,Ta,Ab,Bb)):(E(qb,Ta,zb,Ha),E(rb,Ta,Ab,Ha)),Ka+=1)));na=3*Sa;for(Ra=Math.max(Cb.length,3*ja);na<Ra;na++)Cb[na]=0;na=3*Ca;for(Ra=Math.max(Qb.length,3*ta);na<Ra;na++)Qb[na]=0;na=3*Pa;for(Ra=Math.max(Ya.length,3*I);na<Ra;na++)Ya[na]=0;na=3*Ka;for(Ra=Math.max(qb.length,3*Ia);na<Ra;na++)qb[na]=
-0;na=3*Ka;for(Ra=Math.max(rb.length,3*Ia);na<Ra;na++)rb[na]=0;va.directional.length=Sa;va.point.length=Ca;va.spot.length=Pa;va.hemi.length=Ka;va.ambient[0]=ya;va.ambient[1]=Ga;va.ambient[2]=Oa;fc=!1}if(Z){var ra=Mc;L.ambientLightColor.value=ra.ambient;L.directionalLightColor.value=ra.directional.colors;L.directionalLightDirection.value=ra.directional.positions;L.pointLightColor.value=ra.point.colors;L.pointLightPosition.value=ra.point.positions;L.pointLightDistance.value=ra.point.distances;L.spotLightColor.value=
-ra.spot.colors;L.spotLightPosition.value=ra.spot.positions;L.spotLightDistance.value=ra.spot.distances;L.spotLightDirection.value=ra.spot.directions;L.spotLightAngleCos.value=ra.spot.anglesCos;L.spotLightExponent.value=ra.spot.exponents;L.hemisphereLightSkyColor.value=ra.hemi.skyColors;L.hemisphereLightGroundColor.value=ra.hemi.groundColors;L.hemisphereLightDirection.value=ra.hemi.positions;w(L,!0)}else w(L,!1)}if(d instanceof THREE.MeshBasicMaterial||d instanceof THREE.MeshLambertMaterial||d instanceof
-THREE.MeshPhongMaterial){L.opacity.value=d.opacity;J.gammaInput?L.diffuse.value.copyGammaToLinear(d.color):L.diffuse.value=d.color;L.map.value=d.map;L.lightMap.value=d.lightMap;L.specularMap.value=d.specularMap;L.alphaMap.value=d.alphaMap;d.bumpMap&&(L.bumpMap.value=d.bumpMap,L.bumpScale.value=d.bumpScale);d.normalMap&&(L.normalMap.value=d.normalMap,L.normalScale.value.copy(d.normalScale));var La;d.map?La=d.map:d.specularMap?La=d.specularMap:d.normalMap?La=d.normalMap:d.bumpMap?La=d.bumpMap:d.alphaMap&&
-(La=d.alphaMap);if(void 0!==La){var Ua=La.offset,Va=La.repeat;L.offsetRepeat.value.set(Ua.x,Ua.y,Va.x,Va.y)}L.envMap.value=d.envMap;L.flipEnvMap.value=d.envMap instanceof THREE.WebGLRenderTargetCube?1:-1;L.reflectivity.value=d.reflectivity;L.refractionRatio.value=d.refractionRatio;L.combine.value=d.combine;L.useRefract.value=d.envMap&&d.envMap.mapping instanceof THREE.CubeRefractionMapping}d instanceof THREE.LineBasicMaterial?(L.diffuse.value=d.color,L.opacity.value=d.opacity):d instanceof THREE.LineDashedMaterial?
-(L.diffuse.value=d.color,L.opacity.value=d.opacity,L.dashSize.value=d.dashSize,L.totalSize.value=d.dashSize+d.gapSize,L.scale.value=d.scale):d instanceof THREE.PointCloudMaterial?(L.psColor.value=d.color,L.opacity.value=d.opacity,L.size.value=d.size,L.scale.value=O.height/2,L.map.value=d.map):d instanceof THREE.MeshPhongMaterial?(L.shininess.value=d.shininess,J.gammaInput?(L.ambient.value.copyGammaToLinear(d.ambient),L.emissive.value.copyGammaToLinear(d.emissive),L.specular.value.copyGammaToLinear(d.specular)):
-(L.ambient.value=d.ambient,L.emissive.value=d.emissive,L.specular.value=d.specular),d.wrapAround&&L.wrapRGB.value.copy(d.wrapRGB)):d instanceof THREE.MeshLambertMaterial?(J.gammaInput?(L.ambient.value.copyGammaToLinear(d.ambient),L.emissive.value.copyGammaToLinear(d.emissive)):(L.ambient.value=d.ambient,L.emissive.value=d.emissive),d.wrapAround&&L.wrapRGB.value.copy(d.wrapRGB)):d instanceof THREE.MeshDepthMaterial?(L.mNear.value=a.near,L.mFar.value=a.far,L.opacity.value=d.opacity):d instanceof THREE.MeshNormalMaterial&&
-(L.opacity.value=d.opacity);if(e.receiveShadow&&!d._shadowPass&&L.shadowMatrix)for(var Eb=0,pb=0,Nb=b.length;pb<Nb;pb++){var z=b[pb];z.castShadow&&(z instanceof THREE.SpotLight||z instanceof THREE.DirectionalLight&&!z.shadowCascade)&&(L.shadowMap.value[Eb]=z.shadowMap,L.shadowMapSize.value[Eb]=z.shadowMapSize,L.shadowMatrix.value[Eb]=z.shadowMatrix,L.shadowDarkness.value[Eb]=z.shadowDarkness,L.shadowBias.value[Eb]=z.shadowBias,Eb++)}for(var Sb=d.uniformsList,Ja,wa,$a,nb=0,Pb=Sb.length;nb<Pb;nb++){var da=
-Sb[nb][0];if(!1!==da.needsUpdate){var wb=da.type,U=da.value,fa=Sb[nb][1];switch(wb){case "1i":l.uniform1i(fa,U);break;case "1f":l.uniform1f(fa,U);break;case "2f":l.uniform2f(fa,U[0],U[1]);break;case "3f":l.uniform3f(fa,U[0],U[1],U[2]);break;case "4f":l.uniform4f(fa,U[0],U[1],U[2],U[3]);break;case "1iv":l.uniform1iv(fa,U);break;case "3iv":l.uniform3iv(fa,U);break;case "1fv":l.uniform1fv(fa,U);break;case "2fv":l.uniform2fv(fa,U);break;case "3fv":l.uniform3fv(fa,U);break;case "4fv":l.uniform4fv(fa,U);
-break;case "Matrix3fv":l.uniformMatrix3fv(fa,!1,U);break;case "Matrix4fv":l.uniformMatrix4fv(fa,!1,U);break;case "i":l.uniform1i(fa,U);break;case "f":l.uniform1f(fa,U);break;case "v2":l.uniform2f(fa,U.x,U.y);break;case "v3":l.uniform3f(fa,U.x,U.y,U.z);break;case "v4":l.uniform4f(fa,U.x,U.y,U.z,U.w);break;case "c":l.uniform3f(fa,U.r,U.g,U.b);break;case "iv1":l.uniform1iv(fa,U);break;case "iv":l.uniform3iv(fa,U);break;case "fv1":l.uniform1fv(fa,U);break;case "fv":l.uniform3fv(fa,U);break;case "v2v":void 0===
-da._array&&(da._array=new Float32Array(2*U.length));for(var N=0,xa=U.length;N<xa;N++)$a=2*N,da._array[$a]=U[N].x,da._array[$a+1]=U[N].y;l.uniform2fv(fa,da._array);break;case "v3v":void 0===da._array&&(da._array=new Float32Array(3*U.length));N=0;for(xa=U.length;N<xa;N++)$a=3*N,da._array[$a]=U[N].x,da._array[$a+1]=U[N].y,da._array[$a+2]=U[N].z;l.uniform3fv(fa,da._array);break;case "v4v":void 0===da._array&&(da._array=new Float32Array(4*U.length));N=0;for(xa=U.length;N<xa;N++)$a=4*N,da._array[$a]=U[N].x,
-da._array[$a+1]=U[N].y,da._array[$a+2]=U[N].z,da._array[$a+3]=U[N].w;l.uniform4fv(fa,da._array);break;case "m3":l.uniformMatrix3fv(fa,!1,U.elements);break;case "m3v":void 0===da._array&&(da._array=new Float32Array(9*U.length));N=0;for(xa=U.length;N<xa;N++)U[N].flattenToArrayOffset(da._array,9*N);l.uniformMatrix3fv(fa,!1,da._array);break;case "m4":l.uniformMatrix4fv(fa,!1,U.elements);break;case "m4v":void 0===da._array&&(da._array=new Float32Array(16*U.length));N=0;for(xa=U.length;N<xa;N++)U[N].flattenToArrayOffset(da._array,
-16*N);l.uniformMatrix4fv(fa,!1,da._array);break;case "t":Ja=U;wa=K();l.uniform1i(fa,wa);if(!Ja)continue;if(Ja instanceof THREE.CubeTexture||Ja.image instanceof Array&&6===Ja.image.length){var ua=Ja,Lb=wa;if(6===ua.image.length)if(ua.needsUpdate){ua.image.__webglTextureCube||(ua.addEventListener("dispose",gc),ua.image.__webglTextureCube=l.createTexture(),J.info.memory.textures++);l.activeTexture(l.TEXTURE0+Lb);l.bindTexture(l.TEXTURE_CUBE_MAP,ua.image.__webglTextureCube);l.pixelStorei(l.UNPACK_FLIP_Y_WEBGL,
-ua.flipY);for(var Ob=ua instanceof THREE.CompressedTexture,Tb=ua.image[0]instanceof THREE.DataTexture,kb=[],Da=0;6>Da;Da++)kb[Da]=!J.autoScaleCubemaps||Ob||Tb?Tb?ua.image[Da].image:ua.image[Da]:R(ua.image[Da],$c);var ka=kb[0],Zb=THREE.Math.isPowerOfTwo(ka.width)&&THREE.Math.isPowerOfTwo(ka.height),ab=Q(ua.format),Fb=Q(ua.type);F(l.TEXTURE_CUBE_MAP,ua,Zb);for(Da=0;6>Da;Da++)if(Ob)for(var gb,$b=kb[Da].mipmaps,ga=0,Xb=$b.length;ga<Xb;ga++)gb=$b[ga],ua.format!==THREE.RGBAFormat&&ua.format!==THREE.RGBFormat?
--1<Nc().indexOf(ab)?l.compressedTexImage2D(l.TEXTURE_CUBE_MAP_POSITIVE_X+Da,ga,ab,gb.width,gb.height,0,gb.data):console.warn("Attempt to load unsupported compressed texture format"):l.texImage2D(l.TEXTURE_CUBE_MAP_POSITIVE_X+Da,ga,ab,gb.width,gb.height,0,ab,Fb,gb.data);else Tb?l.texImage2D(l.TEXTURE_CUBE_MAP_POSITIVE_X+Da,0,ab,kb[Da].width,kb[Da].height,0,ab,Fb,kb[Da].data):l.texImage2D(l.TEXTURE_CUBE_MAP_POSITIVE_X+Da,0,ab,ab,Fb,kb[Da]);ua.generateMipmaps&&Zb&&l.generateMipmap(l.TEXTURE_CUBE_MAP);
-ua.needsUpdate=!1;if(ua.onUpdate)ua.onUpdate()}else l.activeTexture(l.TEXTURE0+Lb),l.bindTexture(l.TEXTURE_CUBE_MAP,ua.image.__webglTextureCube)}else if(Ja instanceof THREE.WebGLRenderTargetCube){var Yb=Ja;l.activeTexture(l.TEXTURE0+wa);l.bindTexture(l.TEXTURE_CUBE_MAP,Yb.__webglTexture)}else J.setTexture(Ja,wa);break;case "tv":void 0===da._array&&(da._array=[]);N=0;for(xa=da.value.length;N<xa;N++)da._array[N]=K();l.uniform1iv(fa,da._array);N=0;for(xa=da.value.length;N<xa;N++)Ja=da.value[N],wa=da._array[N],
-Ja&&J.setTexture(Ja,wa);break;default:console.warn("THREE.WebGLRenderer: Unknown uniform type: "+wb)}}}}l.uniformMatrix4fv(qa.modelViewMatrix,!1,e._modelViewMatrix.elements);qa.normalMatrix&&l.uniformMatrix3fv(qa.normalMatrix,!1,e._normalMatrix.elements);null!==qa.modelMatrix&&l.uniformMatrix4fv(qa.modelMatrix,!1,e.matrixWorld.elements);return yb}function w(a,b){a.ambientLightColor.needsUpdate=b;a.directionalLightColor.needsUpdate=b;a.directionalLightDirection.needsUpdate=b;a.pointLightColor.needsUpdate=
-b;a.pointLightPosition.needsUpdate=b;a.pointLightDistance.needsUpdate=b;a.spotLightColor.needsUpdate=b;a.spotLightPosition.needsUpdate=b;a.spotLightDistance.needsUpdate=b;a.spotLightDirection.needsUpdate=b;a.spotLightAngleCos.needsUpdate=b;a.spotLightExponent.needsUpdate=b;a.hemisphereLightSkyColor.needsUpdate=b;a.hemisphereLightGroundColor.needsUpdate=b;a.hemisphereLightDirection.needsUpdate=b}function K(){var a=dc;a>=Oc&&console.warn("WebGLRenderer: trying to use "+a+" texture units while this GPU supports only "+
-Oc);dc+=1;return a}function x(a,b){a._modelViewMatrix.multiplyMatrices(b.matrixWorldInverse,a.matrixWorld);a._normalMatrix.getNormalMatrix(a._modelViewMatrix)}function D(a,b,c,d){a[b]=c.r*c.r*d;a[b+1]=c.g*c.g*d;a[b+2]=c.b*c.b*d}function E(a,b,c,d){a[b]=c.r*d;a[b+1]=c.g*d;a[b+2]=c.b*d}function A(a){a!==Pc&&(l.lineWidth(a),Pc=a)}function B(a,b,c){Qc!==a&&(a?l.enable(l.POLYGON_OFFSET_FILL):l.disable(l.POLYGON_OFFSET_FILL),Qc=a);!a||Rc===b&&Sc===c||(l.polygonOffset(b,c),Rc=b,Sc=c)}function F(a,b,c){c?
-(l.texParameteri(a,l.TEXTURE_WRAP_S,Q(b.wrapS)),l.texParameteri(a,l.TEXTURE_WRAP_T,Q(b.wrapT)),l.texParameteri(a,l.TEXTURE_MAG_FILTER,Q(b.magFilter)),l.texParameteri(a,l.TEXTURE_MIN_FILTER,Q(b.minFilter))):(l.texParameteri(a,l.TEXTURE_WRAP_S,l.CLAMP_TO_EDGE),l.texParameteri(a,l.TEXTURE_WRAP_T,l.CLAMP_TO_EDGE),l.texParameteri(a,l.TEXTURE_MAG_FILTER,T(b.magFilter)),l.texParameteri(a,l.TEXTURE_MIN_FILTER,T(b.minFilter)));(c=pa.get("EXT_texture_filter_anisotropic"))&&b.type!==THREE.FloatType&&(1<b.anisotropy||
-b.__oldAnisotropy)&&(l.texParameterf(a,c.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(b.anisotropy,J.getMaxAnisotropy())),b.__oldAnisotropy=b.anisotropy)}function R(a,b){if(a.width>b||a.height>b){var c=b/Math.max(a.width,a.height),d=document.createElement("canvas");d.width=Math.floor(a.width*c);d.height=Math.floor(a.height*c);d.getContext("2d").drawImage(a,0,0,a.width,a.height,0,0,d.width,d.height);console.log("THREE.WebGLRenderer:",a,"is too big ("+a.width+"x"+a.height+"). Resized to "+d.width+"x"+d.height+
-".");return d}return a}function H(a,b){l.bindRenderbuffer(l.RENDERBUFFER,a);b.depthBuffer&&!b.stencilBuffer?(l.renderbufferStorage(l.RENDERBUFFER,l.DEPTH_COMPONENT16,b.width,b.height),l.framebufferRenderbuffer(l.FRAMEBUFFER,l.DEPTH_ATTACHMENT,l.RENDERBUFFER,a)):b.depthBuffer&&b.stencilBuffer?(l.renderbufferStorage(l.RENDERBUFFER,l.DEPTH_STENCIL,b.width,b.height),l.framebufferRenderbuffer(l.FRAMEBUFFER,l.DEPTH_STENCIL_ATTACHMENT,l.RENDERBUFFER,a)):l.renderbufferStorage(l.RENDERBUFFER,l.RGBA4,b.width,
-b.height)}function C(a){a instanceof THREE.WebGLRenderTargetCube?(l.bindTexture(l.TEXTURE_CUBE_MAP,a.__webglTexture),l.generateMipmap(l.TEXTURE_CUBE_MAP),l.bindTexture(l.TEXTURE_CUBE_MAP,null)):(l.bindTexture(l.TEXTURE_2D,a.__webglTexture),l.generateMipmap(l.TEXTURE_2D),l.bindTexture(l.TEXTURE_2D,null))}function T(a){return a===THREE.NearestFilter||a===THREE.NearestMipMapNearestFilter||a===THREE.NearestMipMapLinearFilter?l.NEAREST:l.LINEAR}function Q(a){var b;if(a===THREE.RepeatWrapping)return l.REPEAT;
-if(a===THREE.ClampToEdgeWrapping)return l.CLAMP_TO_EDGE;if(a===THREE.MirroredRepeatWrapping)return l.MIRRORED_REPEAT;if(a===THREE.NearestFilter)return l.NEAREST;if(a===THREE.NearestMipMapNearestFilter)return l.NEAREST_MIPMAP_NEAREST;if(a===THREE.NearestMipMapLinearFilter)return l.NEAREST_MIPMAP_LINEAR;if(a===THREE.LinearFilter)return l.LINEAR;if(a===THREE.LinearMipMapNearestFilter)return l.LINEAR_MIPMAP_NEAREST;if(a===THREE.LinearMipMapLinearFilter)return l.LINEAR_MIPMAP_LINEAR;if(a===THREE.UnsignedByteType)return l.UNSIGNED_BYTE;
-if(a===THREE.UnsignedShort4444Type)return l.UNSIGNED_SHORT_4_4_4_4;if(a===THREE.UnsignedShort5551Type)return l.UNSIGNED_SHORT_5_5_5_1;if(a===THREE.UnsignedShort565Type)return l.UNSIGNED_SHORT_5_6_5;if(a===THREE.ByteType)return l.BYTE;if(a===THREE.ShortType)return l.SHORT;if(a===THREE.UnsignedShortType)return l.UNSIGNED_SHORT;if(a===THREE.IntType)return l.INT;if(a===THREE.UnsignedIntType)return l.UNSIGNED_INT;if(a===THREE.FloatType)return l.FLOAT;if(a===THREE.AlphaFormat)return l.ALPHA;if(a===THREE.RGBFormat)return l.RGB;
-if(a===THREE.RGBAFormat)return l.RGBA;if(a===THREE.LuminanceFormat)return l.LUMINANCE;if(a===THREE.LuminanceAlphaFormat)return l.LUMINANCE_ALPHA;if(a===THREE.AddEquation)return l.FUNC_ADD;if(a===THREE.SubtractEquation)return l.FUNC_SUBTRACT;if(a===THREE.ReverseSubtractEquation)return l.FUNC_REVERSE_SUBTRACT;if(a===THREE.ZeroFactor)return l.ZERO;if(a===THREE.OneFactor)return l.ONE;if(a===THREE.SrcColorFactor)return l.SRC_COLOR;if(a===THREE.OneMinusSrcColorFactor)return l.ONE_MINUS_SRC_COLOR;if(a===
-THREE.SrcAlphaFactor)return l.SRC_ALPHA;if(a===THREE.OneMinusSrcAlphaFactor)return l.ONE_MINUS_SRC_ALPHA;if(a===THREE.DstAlphaFactor)return l.DST_ALPHA;if(a===THREE.OneMinusDstAlphaFactor)return l.ONE_MINUS_DST_ALPHA;if(a===THREE.DstColorFactor)return l.DST_COLOR;if(a===THREE.OneMinusDstColorFactor)return l.ONE_MINUS_DST_COLOR;if(a===THREE.SrcAlphaSaturateFactor)return l.SRC_ALPHA_SATURATE;b=pa.get("WEBGL_compressed_texture_s3tc");if(null!==b){if(a===THREE.RGB_S3TC_DXT1_Format)return b.COMPRESSED_RGB_S3TC_DXT1_EXT;
-if(a===THREE.RGBA_S3TC_DXT1_Format)return b.COMPRESSED_RGBA_S3TC_DXT1_EXT;if(a===THREE.RGBA_S3TC_DXT3_Format)return b.COMPRESSED_RGBA_S3TC_DXT3_EXT;if(a===THREE.RGBA_S3TC_DXT5_Format)return b.COMPRESSED_RGBA_S3TC_DXT5_EXT}b=pa.get("WEBGL_compressed_texture_pvrtc");if(null!==b){if(a===THREE.RGB_PVRTC_4BPPV1_Format)return b.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;if(a===THREE.RGB_PVRTC_2BPPV1_Format)return b.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;if(a===THREE.RGBA_PVRTC_4BPPV1_Format)return b.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
-if(a===THREE.RGBA_PVRTC_2BPPV1_Format)return b.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG}b=pa.get("EXT_blend_minmax");if(null!==b){if(a===THREE.MinEquation)return b.MIN_EXT;if(a===THREE.MaxEquation)return b.MAX_EXT}return 0}console.log("THREE.WebGLRenderer",THREE.REVISION);a=a||{};var O=void 0!==a.canvas?a.canvas:document.createElement("canvas"),S=void 0!==a.context?a.context:null,X=void 0!==a.precision?a.precision:"highp",Y=void 0!==a.alpha?a.alpha:!1,la=void 0!==a.depth?a.depth:!0,ma=void 0!==a.stencil?
-a.stencil:!0,ya=void 0!==a.antialias?a.antialias:!1,P=void 0!==a.premultipliedAlpha?a.premultipliedAlpha:!0,Ga=void 0!==a.preserveDrawingBuffer?a.preserveDrawingBuffer:!1,Fa=void 0!==a.logarithmicDepthBuffer?a.logarithmicDepthBuffer:!1,za=new THREE.Color(0),bb=0,cb=[],ob={},jb=[],Jb=[],Ib=[],yb=[],Ra=[];this.domElement=O;this.context=null;this.devicePixelRatio=void 0!==a.devicePixelRatio?a.devicePixelRatio:void 0!==self.devicePixelRatio?self.devicePixelRatio:1;this.sortObjects=this.autoClearStencil=
-this.autoClearDepth=this.autoClearColor=this.autoClear=!0;this.shadowMapEnabled=this.gammaOutput=this.gammaInput=!1;this.shadowMapType=THREE.PCFShadowMap;this.shadowMapCullFace=THREE.CullFaceFront;this.shadowMapCascade=this.shadowMapDebug=!1;this.maxMorphTargets=8;this.maxMorphNormals=4;this.autoScaleCubemaps=!0;this.info={memory:{programs:0,geometries:0,textures:0},render:{calls:0,vertices:0,faces:0,points:0}};var J=this,hb=[],tc=null,Tc=null,Kb=-1,Oa=-1,ec=null,dc=0,Lb=-1,Mb=-1,pb=-1,Nb=-1,Ob=-1,
-Xb=-1,Yb=-1,nb=-1,Qc=null,Rc=null,Sc=null,Pc=null,Pb=0,kc=0,lc=O.width,mc=O.height,Uc=0,Vc=0,wb=new Uint8Array(16),ib=new Uint8Array(16),Ec=new THREE.Frustum,Ac=new THREE.Matrix4,Gc=new THREE.Matrix4,Na=new THREE.Vector3,sa=new THREE.Vector3,fc=!0,Mc={ambient:[0,0,0],directional:{length:0,colors:[],positions:[]},point:{length:0,colors:[],positions:[],distances:[]},spot:{length:0,colors:[],positions:[],distances:[],directions:[],anglesCos:[],exponents:[]},hemi:{length:0,skyColors:[],groundColors:[],
-positions:[]}},l;try{var Wc={alpha:Y,depth:la,stencil:ma,antialias:ya,premultipliedAlpha:P,preserveDrawingBuffer:Ga};l=S||O.getContext("webgl",Wc)||O.getContext("experimental-webgl",Wc);if(null===l){if(null!==O.getContext("webgl"))throw"Error creating WebGL context with your selected attributes.";throw"Error creating WebGL context.";}}catch(ad){console.error(ad)}void 0===l.getShaderPrecisionFormat&&(l.getShaderPrecisionFormat=function(){return{rangeMin:1,rangeMax:1,precision:1}});var pa=new THREE.WebGLExtensions(l);
-pa.get("OES_texture_float");pa.get("OES_texture_float_linear");pa.get("OES_standard_derivatives");Fa&&pa.get("EXT_frag_depth");l.clearColor(0,0,0,1);l.clearDepth(1);l.clearStencil(0);l.enable(l.DEPTH_TEST);l.depthFunc(l.LEQUAL);l.frontFace(l.CCW);l.cullFace(l.BACK);l.enable(l.CULL_FACE);l.enable(l.BLEND);l.blendEquation(l.FUNC_ADD);l.blendFunc(l.SRC_ALPHA,l.ONE_MINUS_SRC_ALPHA);l.viewport(Pb,kc,lc,mc);l.clearColor(za.r,za.g,za.b,bb);this.context=l;var Oc=l.getParameter(l.MAX_TEXTURE_IMAGE_UNITS),
-bd=l.getParameter(l.MAX_VERTEX_TEXTURE_IMAGE_UNITS),cd=l.getParameter(l.MAX_TEXTURE_SIZE),$c=l.getParameter(l.MAX_CUBE_MAP_TEXTURE_SIZE),sc=0<bd,jc=sc&&pa.get("OES_texture_float"),dd=l.getShaderPrecisionFormat(l.VERTEX_SHADER,l.HIGH_FLOAT),ed=l.getShaderPrecisionFormat(l.VERTEX_SHADER,l.MEDIUM_FLOAT);l.getShaderPrecisionFormat(l.VERTEX_SHADER,l.LOW_FLOAT);var fd=l.getShaderPrecisionFormat(l.FRAGMENT_SHADER,l.HIGH_FLOAT),gd=l.getShaderPrecisionFormat(l.FRAGMENT_SHADER,l.MEDIUM_FLOAT);l.getShaderPrecisionFormat(l.FRAGMENT_SHADER,
-l.LOW_FLOAT);var Nc=function(){var a;return function(){if(void 0!==a)return a;a=[];if(pa.get("WEBGL_compressed_texture_pvrtc")||pa.get("WEBGL_compressed_texture_s3tc"))for(var b=l.getParameter(l.COMPRESSED_TEXTURE_FORMATS),c=0;c<b.length;c++)a.push(b[c]);return a}}(),hd=0<dd.precision&&0<fd.precision,Xc=0<ed.precision&&0<gd.precision;"highp"!==X||hd||(Xc?(X="mediump",console.warn("THREE.WebGLRenderer: highp not supported, using mediump.")):(X="lowp",console.warn("THREE.WebGLRenderer: highp and mediump not supported, using lowp.")));
-"mediump"!==X||Xc||(X="lowp",console.warn("THREE.WebGLRenderer: mediump not supported, using lowp."));var id=new THREE.ShadowMapPlugin(this,cb,ob,jb),jd=new THREE.SpritePlugin(this,yb),kd=new THREE.LensFlarePlugin(this,Ra);this.getContext=function(){return l};this.supportsVertexTextures=function(){return sc};this.supportsFloatTextures=function(){return pa.get("OES_texture_float")};this.supportsStandardDerivatives=function(){return pa.get("OES_standard_derivatives")};this.supportsCompressedTextureS3TC=
-function(){return pa.get("WEBGL_compressed_texture_s3tc")};this.supportsCompressedTexturePVRTC=function(){return pa.get("WEBGL_compressed_texture_pvrtc")};this.supportsBlendMinMax=function(){return pa.get("EXT_blend_minmax")};this.getMaxAnisotropy=function(){var a;return function(){if(void 0!==a)return a;var b=pa.get("EXT_texture_filter_anisotropic");return a=null!==b?l.getParameter(b.MAX_TEXTURE_MAX_ANISOTROPY_EXT):0}}();this.getPrecision=function(){return X};this.setSize=function(a,b,c){O.width=
-a*this.devicePixelRatio;O.height=b*this.devicePixelRatio;!1!==c&&(O.style.width=a+"px",O.style.height=b+"px");this.setViewport(0,0,a,b)};this.setViewport=function(a,b,c,d){Pb=a*this.devicePixelRatio;kc=b*this.devicePixelRatio;lc=c*this.devicePixelRatio;mc=d*this.devicePixelRatio;l.viewport(Pb,kc,lc,mc)};this.setScissor=function(a,b,c,d){l.scissor(a*this.devicePixelRatio,b*this.devicePixelRatio,c*this.devicePixelRatio,d*this.devicePixelRatio)};this.enableScissorTest=function(a){a?l.enable(l.SCISSOR_TEST):
-l.disable(l.SCISSOR_TEST)};this.setClearColor=function(a,b){za.set(a);bb=void 0!==b?b:1;l.clearColor(za.r,za.g,za.b,bb)};this.setClearColorHex=function(a,b){console.warn("THREE.WebGLRenderer: .setClearColorHex() is being removed. Use .setClearColor() instead.");this.setClearColor(a,b)};this.getClearColor=function(){return za};this.getClearAlpha=function(){return bb};this.clear=function(a,b,c){var d=0;if(void 0===a||a)d|=l.COLOR_BUFFER_BIT;if(void 0===b||b)d|=l.DEPTH_BUFFER_BIT;if(void 0===c||c)d|=
-l.STENCIL_BUFFER_BIT;l.clear(d)};this.clearColor=function(){l.clear(l.COLOR_BUFFER_BIT)};this.clearDepth=function(){l.clear(l.DEPTH_BUFFER_BIT)};this.clearStencil=function(){l.clear(l.STENCIL_BUFFER_BIT)};this.clearTarget=function(a,b,c,d){this.setRenderTarget(a);this.clear(b,c,d)};this.resetGLState=function(){ec=tc=null;Kb=Oa=Mb=Lb=nb=Yb=pb=-1;fc=!0};var Hc=function(a){a.target.traverse(function(a){a.removeEventListener("remove",Hc);if(a instanceof THREE.Mesh||a instanceof THREE.PointCloud||a instanceof
-THREE.Line)delete ob[a.id];else if(a instanceof THREE.ImmediateRenderObject||a.immediateRenderCallback)for(var b=jb,c=b.length-1;0<=c;c--)b[c].object===a&&b.splice(c,1);delete a.__webglInit;delete a._modelViewMatrix;delete a._normalMatrix;delete a.__webglActive})},Ic=function(a){a=a.target;a.removeEventListener("dispose",Ic);delete a.__webglInit;if(a instanceof THREE.BufferGeometry){for(var b in a.attributes){var c=a.attributes[b];void 0!==c.buffer&&(l.deleteBuffer(c.buffer),delete c.buffer)}J.info.memory.geometries--}else if(b=
-xb[a.id],void 0!==b){for(var c=0,d=b.length;c<d;c++){var e=b[c];if(void 0!==e.numMorphTargets){for(var f=0,g=e.numMorphTargets;f<g;f++)l.deleteBuffer(e.__webglMorphTargetsBuffers[f]);delete e.__webglMorphTargetsBuffers}if(void 0!==e.numMorphNormals){f=0;for(g=e.numMorphNormals;f<g;f++)l.deleteBuffer(e.__webglMorphNormalsBuffers[f]);delete e.__webglMorphNormalsBuffers}Yc(e)}delete xb[a.id]}else Yc(a);Oa=-1},gc=function(a){a=a.target;a.removeEventListener("dispose",gc);a.image&&a.image.__webglTextureCube?
-(l.deleteTexture(a.image.__webglTextureCube),delete a.image.__webglTextureCube):void 0!==a.__webglInit&&(l.deleteTexture(a.__webglTexture),delete a.__webglTexture,delete a.__webglInit);J.info.memory.textures--},Zc=function(a){a=a.target;a.removeEventListener("dispose",Zc);if(a&&void 0!==a.__webglTexture){l.deleteTexture(a.__webglTexture);delete a.__webglTexture;if(a instanceof THREE.WebGLRenderTargetCube)for(var b=0;6>b;b++)l.deleteFramebuffer(a.__webglFramebuffer[b]),l.deleteRenderbuffer(a.__webglRenderbuffer[b]);
-else l.deleteFramebuffer(a.__webglFramebuffer),l.deleteRenderbuffer(a.__webglRenderbuffer);delete a.__webglFramebuffer;delete a.__webglRenderbuffer}J.info.memory.textures--},Dc=function(a){a=a.target;a.removeEventListener("dispose",Dc);Cc(a)},Yc=function(a){for(var b="__webglVertexBuffer __webglNormalBuffer __webglTangentBuffer __webglColorBuffer __webglUVBuffer __webglUV2Buffer __webglSkinIndicesBuffer __webglSkinWeightsBuffer __webglFaceBuffer __webglLineBuffer __webglLineDistanceBuffer".split(" "),
-c=0,d=b.length;c<d;c++){var e=b[c];void 0!==a[e]&&(l.deleteBuffer(a[e]),delete a[e])}if(void 0!==a.__webglCustomAttributesList){for(e in a.__webglCustomAttributesList)l.deleteBuffer(a.__webglCustomAttributesList[e].buffer);delete a.__webglCustomAttributesList}J.info.memory.geometries--},Cc=function(a){var b=a.program.program;if(void 0!==b){a.program=void 0;var c,d,e=!1;a=0;for(c=hb.length;a<c;a++)if(d=hb[a],d.program===b){d.usedTimes--;0===d.usedTimes&&(e=!0);break}if(!0===e){e=[];a=0;for(c=hb.length;a<
-c;a++)d=hb[a],d.program!==b&&e.push(d);hb=e;l.deleteProgram(b);J.info.memory.programs--}}};this.renderBufferImmediate=function(a,b,c){f();a.hasPositions&&!a.__webglVertexBuffer&&(a.__webglVertexBuffer=l.createBuffer());a.hasNormals&&!a.__webglNormalBuffer&&(a.__webglNormalBuffer=l.createBuffer());a.hasUvs&&!a.__webglUvBuffer&&(a.__webglUvBuffer=l.createBuffer());a.hasColors&&!a.__webglColorBuffer&&(a.__webglColorBuffer=l.createBuffer());a.hasPositions&&(l.bindBuffer(l.ARRAY_BUFFER,a.__webglVertexBuffer),
-l.bufferData(l.ARRAY_BUFFER,a.positionArray,l.DYNAMIC_DRAW),g(b.attributes.position),l.vertexAttribPointer(b.attributes.position,3,l.FLOAT,!1,0,0));if(a.hasNormals){l.bindBuffer(l.ARRAY_BUFFER,a.__webglNormalBuffer);if(c.shading===THREE.FlatShading){var d,e,k,m,n,p,r,q,t,s,v,u=3*a.count;for(v=0;v<u;v+=9)s=a.normalArray,d=s[v],e=s[v+1],k=s[v+2],m=s[v+3],p=s[v+4],q=s[v+5],n=s[v+6],r=s[v+7],t=s[v+8],d=(d+m+n)/3,e=(e+p+r)/3,k=(k+q+t)/3,s[v]=d,s[v+1]=e,s[v+2]=k,s[v+3]=d,s[v+4]=e,s[v+5]=k,s[v+6]=d,s[v+
-7]=e,s[v+8]=k}l.bufferData(l.ARRAY_BUFFER,a.normalArray,l.DYNAMIC_DRAW);g(b.attributes.normal);l.vertexAttribPointer(b.attributes.normal,3,l.FLOAT,!1,0,0)}a.hasUvs&&c.map&&(l.bindBuffer(l.ARRAY_BUFFER,a.__webglUvBuffer),l.bufferData(l.ARRAY_BUFFER,a.uvArray,l.DYNAMIC_DRAW),g(b.attributes.uv),l.vertexAttribPointer(b.attributes.uv,2,l.FLOAT,!1,0,0));a.hasColors&&c.vertexColors!==THREE.NoColors&&(l.bindBuffer(l.ARRAY_BUFFER,a.__webglColorBuffer),l.bufferData(l.ARRAY_BUFFER,a.colorArray,l.DYNAMIC_DRAW),
-g(b.attributes.color),l.vertexAttribPointer(b.attributes.color,3,l.FLOAT,!1,0,0));h();l.drawArrays(l.TRIANGLES,0,a.count);a.count=0};this.renderBufferDirect=function(a,b,c,d,g,h){if(!1!==d.visible)if(a=G(a,b,c,d,h),b=!1,c=16777215*g.id+2*a.id+(d.wireframe?1:0),c!==Oa&&(Oa=c,b=!0),b&&f(),h instanceof THREE.Mesh)if(h=!0===d.wireframe?l.LINES:l.TRIANGLES,c=g.attributes.index){var k,m;c.array instanceof Uint32Array&&pa.get("OES_element_index_uint")?(k=l.UNSIGNED_INT,m=4):(k=l.UNSIGNED_SHORT,m=2);var n=
-g.offsets;if(0===n.length)b&&(e(d,a,g,0),l.bindBuffer(l.ELEMENT_ARRAY_BUFFER,c.buffer)),l.drawElements(h,c.array.length,k,0),J.info.render.calls++,J.info.render.vertices+=c.array.length,J.info.render.faces+=c.array.length/3;else{b=!0;for(var p=0,r=n.length;p<r;p++){var q=n[p].index;b&&(e(d,a,g,q),l.bindBuffer(l.ELEMENT_ARRAY_BUFFER,c.buffer));l.drawElements(h,n[p].count,k,n[p].start*m);J.info.render.calls++;J.info.render.vertices+=n[p].count;J.info.render.faces+=n[p].count/3}}}else b&&e(d,a,g,0),
-d=g.attributes.position,l.drawArrays(h,0,d.array.length/3),J.info.render.calls++,J.info.render.vertices+=d.array.length/3,J.info.render.faces+=d.array.length/9;else if(h instanceof THREE.PointCloud)b&&e(d,a,g,0),d=g.attributes.position,l.drawArrays(l.POINTS,0,d.array.length/3),J.info.render.calls++,J.info.render.points+=d.array.length/3;else if(h instanceof THREE.Line)if(h=h.mode===THREE.LineStrip?l.LINE_STRIP:l.LINES,A(d.linewidth),c=g.attributes.index)if(c.array instanceof Uint32Array?(k=l.UNSIGNED_INT,
-m=4):(k=l.UNSIGNED_SHORT,m=2),n=g.offsets,0===n.length)b&&(e(d,a,g,0),l.bindBuffer(l.ELEMENT_ARRAY_BUFFER,c.buffer)),l.drawElements(h,c.array.length,k,0),J.info.render.calls++,J.info.render.vertices+=c.array.length;else for(1<n.length&&(b=!0),p=0,r=n.length;p<r;p++)q=n[p].index,b&&(e(d,a,g,q),l.bindBuffer(l.ELEMENT_ARRAY_BUFFER,c.buffer)),l.drawElements(h,n[p].count,k,n[p].start*m),J.info.render.calls++,J.info.render.vertices+=n[p].count;else b&&e(d,a,g,0),d=g.attributes.position,l.drawArrays(h,0,
-d.array.length/3),J.info.render.calls++,J.info.render.points+=d.array.length/3};this.renderBuffer=function(a,b,c,d,e,k){if(!1!==d.visible){c=G(a,b,c,d,k);b=c.attributes;a=!1;c=16777215*e.id+2*c.id+(d.wireframe?1:0);c!==Oa&&(Oa=c,a=!0);a&&f();if(!d.morphTargets&&0<=b.position)a&&(l.bindBuffer(l.ARRAY_BUFFER,e.__webglVertexBuffer),g(b.position),l.vertexAttribPointer(b.position,3,l.FLOAT,!1,0,0));else if(k.morphTargetBase){c=d.program.attributes;-1!==k.morphTargetBase&&0<=c.position?(l.bindBuffer(l.ARRAY_BUFFER,
-e.__webglMorphTargetsBuffers[k.morphTargetBase]),g(c.position),l.vertexAttribPointer(c.position,3,l.FLOAT,!1,0,0)):0<=c.position&&(l.bindBuffer(l.ARRAY_BUFFER,e.__webglVertexBuffer),g(c.position),l.vertexAttribPointer(c.position,3,l.FLOAT,!1,0,0));if(k.morphTargetForcedOrder.length)for(var m=0,n=k.morphTargetForcedOrder,r=k.morphTargetInfluences;m<d.numSupportedMorphTargets&&m<n.length;)0<=c["morphTarget"+m]&&(l.bindBuffer(l.ARRAY_BUFFER,e.__webglMorphTargetsBuffers[n[m]]),g(c["morphTarget"+m]),l.vertexAttribPointer(c["morphTarget"+
-m],3,l.FLOAT,!1,0,0)),0<=c["morphNormal"+m]&&d.morphNormals&&(l.bindBuffer(l.ARRAY_BUFFER,e.__webglMorphNormalsBuffers[n[m]]),g(c["morphNormal"+m]),l.vertexAttribPointer(c["morphNormal"+m],3,l.FLOAT,!1,0,0)),k.__webglMorphTargetInfluences[m]=r[n[m]],m++;else{var n=[],r=k.morphTargetInfluences,q,t=r.length;for(q=0;q<t;q++)m=r[q],0<m&&n.push([m,q]);n.length>d.numSupportedMorphTargets?(n.sort(p),n.length=d.numSupportedMorphTargets):n.length>d.numSupportedMorphNormals?n.sort(p):0===n.length&&n.push([0,
-0]);for(m=0;m<d.numSupportedMorphTargets;)n[m]?(q=n[m][1],0<=c["morphTarget"+m]&&(l.bindBuffer(l.ARRAY_BUFFER,e.__webglMorphTargetsBuffers[q]),g(c["morphTarget"+m]),l.vertexAttribPointer(c["morphTarget"+m],3,l.FLOAT,!1,0,0)),0<=c["morphNormal"+m]&&d.morphNormals&&(l.bindBuffer(l.ARRAY_BUFFER,e.__webglMorphNormalsBuffers[q]),g(c["morphNormal"+m]),l.vertexAttribPointer(c["morphNormal"+m],3,l.FLOAT,!1,0,0)),k.__webglMorphTargetInfluences[m]=r[q]):k.__webglMorphTargetInfluences[m]=0,m++}null!==d.program.uniforms.morphTargetInfluences&&
-l.uniform1fv(d.program.uniforms.morphTargetInfluences,k.__webglMorphTargetInfluences)}if(a){if(e.__webglCustomAttributesList)for(c=0,r=e.__webglCustomAttributesList.length;c<r;c++)n=e.__webglCustomAttributesList[c],0<=b[n.buffer.belongsToAttribute]&&(l.bindBuffer(l.ARRAY_BUFFER,n.buffer),g(b[n.buffer.belongsToAttribute]),l.vertexAttribPointer(b[n.buffer.belongsToAttribute],n.size,l.FLOAT,!1,0,0));0<=b.color&&(0<k.geometry.colors.length||0<k.geometry.faces.length?(l.bindBuffer(l.ARRAY_BUFFER,e.__webglColorBuffer),
-g(b.color),l.vertexAttribPointer(b.color,3,l.FLOAT,!1,0,0)):void 0!==d.defaultAttributeValues&&l.vertexAttrib3fv(b.color,d.defaultAttributeValues.color));0<=b.normal&&(l.bindBuffer(l.ARRAY_BUFFER,e.__webglNormalBuffer),g(b.normal),l.vertexAttribPointer(b.normal,3,l.FLOAT,!1,0,0));0<=b.tangent&&(l.bindBuffer(l.ARRAY_BUFFER,e.__webglTangentBuffer),g(b.tangent),l.vertexAttribPointer(b.tangent,4,l.FLOAT,!1,0,0));0<=b.uv&&(k.geometry.faceVertexUvs[0]?(l.bindBuffer(l.ARRAY_BUFFER,e.__webglUVBuffer),g(b.uv),
-l.vertexAttribPointer(b.uv,2,l.FLOAT,!1,0,0)):void 0!==d.defaultAttributeValues&&l.vertexAttrib2fv(b.uv,d.defaultAttributeValues.uv));0<=b.uv2&&(k.geometry.faceVertexUvs[1]?(l.bindBuffer(l.ARRAY_BUFFER,e.__webglUV2Buffer),g(b.uv2),l.vertexAttribPointer(b.uv2,2,l.FLOAT,!1,0,0)):void 0!==d.defaultAttributeValues&&l.vertexAttrib2fv(b.uv2,d.defaultAttributeValues.uv2));d.skinning&&0<=b.skinIndex&&0<=b.skinWeight&&(l.bindBuffer(l.ARRAY_BUFFER,e.__webglSkinIndicesBuffer),g(b.skinIndex),l.vertexAttribPointer(b.skinIndex,
-4,l.FLOAT,!1,0,0),l.bindBuffer(l.ARRAY_BUFFER,e.__webglSkinWeightsBuffer),g(b.skinWeight),l.vertexAttribPointer(b.skinWeight,4,l.FLOAT,!1,0,0));0<=b.lineDistance&&(l.bindBuffer(l.ARRAY_BUFFER,e.__webglLineDistanceBuffer),g(b.lineDistance),l.vertexAttribPointer(b.lineDistance,1,l.FLOAT,!1,0,0))}h();k instanceof THREE.Mesh?(k=e.__typeArray===Uint32Array?l.UNSIGNED_INT:l.UNSIGNED_SHORT,d.wireframe?(A(d.wireframeLinewidth),a&&l.bindBuffer(l.ELEMENT_ARRAY_BUFFER,e.__webglLineBuffer),l.drawElements(l.LINES,
-e.__webglLineCount,k,0)):(a&&l.bindBuffer(l.ELEMENT_ARRAY_BUFFER,e.__webglFaceBuffer),l.drawElements(l.TRIANGLES,e.__webglFaceCount,k,0)),J.info.render.calls++,J.info.render.vertices+=e.__webglFaceCount,J.info.render.faces+=e.__webglFaceCount/3):k instanceof THREE.Line?(k=k.mode===THREE.LineStrip?l.LINE_STRIP:l.LINES,A(d.linewidth),l.drawArrays(k,0,e.__webglLineCount),J.info.render.calls++):k instanceof THREE.PointCloud&&(l.drawArrays(l.POINTS,0,e.__webglParticleCount),J.info.render.calls++,J.info.render.points+=
-e.__webglParticleCount)}};this.render=function(a,b,c,d){if(!1===b instanceof THREE.Camera)console.error("THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.");else{var e=a.fog;Kb=Oa=-1;ec=null;fc=!0;!0===a.autoUpdate&&a.updateMatrixWorld();void 0===b.parent&&b.updateMatrixWorld();a.traverse(function(a){a instanceof THREE.SkinnedMesh&&a.skeleton.update()});b.matrixWorldInverse.getInverse(b.matrixWorld);Ac.multiplyMatrices(b.projectionMatrix,b.matrixWorldInverse);Ec.setFromMatrix(Ac);
-cb.length=0;Jb.length=0;Ib.length=0;yb.length=0;Ra.length=0;q(a,a);!0===J.sortObjects&&(Jb.sort(k),Ib.sort(n));id.render(a,b);J.info.render.calls=0;J.info.render.vertices=0;J.info.render.faces=0;J.info.render.points=0;this.setRenderTarget(c);(this.autoClear||d)&&this.clear(this.autoClearColor,this.autoClearDepth,this.autoClearStencil);d=0;for(var f=jb.length;d<f;d++){var g=jb[d],h=g.object;h.visible&&(x(h,b),t(g))}a.overrideMaterial?(d=a.overrideMaterial,this.setBlending(d.blending,d.blendEquation,
-d.blendSrc,d.blendDst),this.setDepthTest(d.depthTest),this.setDepthWrite(d.depthWrite),B(d.polygonOffset,d.polygonOffsetFactor,d.polygonOffsetUnits),m(Jb,b,cb,e,!0,d),m(Ib,b,cb,e,!0,d),r(jb,"",b,cb,e,!1,d)):(d=null,this.setBlending(THREE.NoBlending),m(Jb,b,cb,e,!1,d),r(jb,"opaque",b,cb,e,!1,d),m(Ib,b,cb,e,!0,d),r(jb,"transparent",b,cb,e,!0,d));jd.render(a,b);kd.render(a,b,Uc,Vc);c&&c.generateMipmaps&&c.minFilter!==THREE.NearestFilter&&c.minFilter!==THREE.LinearFilter&&C(c);this.setDepthTest(!0);this.setDepthWrite(!0)}};
-this.renderImmediateObject=function(a,b,c,d,e){var f=G(a,b,c,d,e);Oa=-1;J.setMaterialFaces(d);e.immediateRenderCallback?e.immediateRenderCallback(f,l,Ec):e.render(function(a){J.renderBufferImmediate(a,f,d)})};var xb={},rc=0;this.setFaceCulling=function(a,b){a===THREE.CullFaceNone?l.disable(l.CULL_FACE):(b===THREE.FrontFaceDirectionCW?l.frontFace(l.CW):l.frontFace(l.CCW),a===THREE.CullFaceBack?l.cullFace(l.BACK):a===THREE.CullFaceFront?l.cullFace(l.FRONT):l.cullFace(l.FRONT_AND_BACK),l.enable(l.CULL_FACE))};
-this.setMaterialFaces=function(a){var b=a.side===THREE.DoubleSide;a=a.side===THREE.BackSide;Lb!==b&&(b?l.disable(l.CULL_FACE):l.enable(l.CULL_FACE),Lb=b);Mb!==a&&(a?l.frontFace(l.CW):l.frontFace(l.CCW),Mb=a)};this.setDepthTest=function(a){Yb!==a&&(a?l.enable(l.DEPTH_TEST):l.disable(l.DEPTH_TEST),Yb=a)};this.setDepthWrite=function(a){nb!==a&&(l.depthMask(a),nb=a)};this.setBlending=function(a,b,c,d){a!==pb&&(a===THREE.NoBlending?l.disable(l.BLEND):a===THREE.AdditiveBlending?(l.enable(l.BLEND),l.blendEquation(l.FUNC_ADD),
-l.blendFunc(l.SRC_ALPHA,l.ONE)):a===THREE.SubtractiveBlending?(l.enable(l.BLEND),l.blendEquation(l.FUNC_ADD),l.blendFunc(l.ZERO,l.ONE_MINUS_SRC_COLOR)):a===THREE.MultiplyBlending?(l.enable(l.BLEND),l.blendEquation(l.FUNC_ADD),l.blendFunc(l.ZERO,l.SRC_COLOR)):a===THREE.CustomBlending?l.enable(l.BLEND):(l.enable(l.BLEND),l.blendEquationSeparate(l.FUNC_ADD,l.FUNC_ADD),l.blendFuncSeparate(l.SRC_ALPHA,l.ONE_MINUS_SRC_ALPHA,l.ONE,l.ONE_MINUS_SRC_ALPHA)),pb=a);if(a===THREE.CustomBlending){if(b!==Nb&&(l.blendEquation(Q(b)),
-Nb=b),c!==Ob||d!==Xb)l.blendFunc(Q(c),Q(d)),Ob=c,Xb=d}else Xb=Ob=Nb=null};this.uploadTexture=function(a){void 0===a.__webglInit&&(a.__webglInit=!0,a.addEventListener("dispose",gc),a.__webglTexture=l.createTexture(),J.info.memory.textures++);l.bindTexture(l.TEXTURE_2D,a.__webglTexture);l.pixelStorei(l.UNPACK_FLIP_Y_WEBGL,a.flipY);l.pixelStorei(l.UNPACK_PREMULTIPLY_ALPHA_WEBGL,a.premultiplyAlpha);l.pixelStorei(l.UNPACK_ALIGNMENT,a.unpackAlignment);a.image=R(a.image,cd);var b=a.image,c=THREE.Math.isPowerOfTwo(b.width)&&
-THREE.Math.isPowerOfTwo(b.height),d=Q(a.format),e=Q(a.type);F(l.TEXTURE_2D,a,c);var f=a.mipmaps;if(a instanceof THREE.DataTexture)if(0<f.length&&c){for(var g=0,h=f.length;g<h;g++)b=f[g],l.texImage2D(l.TEXTURE_2D,g,d,b.width,b.height,0,d,e,b.data);a.generateMipmaps=!1}else l.texImage2D(l.TEXTURE_2D,0,d,b.width,b.height,0,d,e,b.data);else if(a instanceof THREE.CompressedTexture)for(g=0,h=f.length;g<h;g++)b=f[g],a.format!==THREE.RGBAFormat&&a.format!==THREE.RGBFormat?-1<Nc().indexOf(d)?l.compressedTexImage2D(l.TEXTURE_2D,
-g,d,b.width,b.height,0,b.data):console.warn("Attempt to load unsupported compressed texture format"):l.texImage2D(l.TEXTURE_2D,g,d,b.width,b.height,0,d,e,b.data);else if(0<f.length&&c){g=0;for(h=f.length;g<h;g++)b=f[g],l.texImage2D(l.TEXTURE_2D,g,d,d,e,b);a.generateMipmaps=!1}else l.texImage2D(l.TEXTURE_2D,0,d,d,e,a.image);a.generateMipmaps&&c&&l.generateMipmap(l.TEXTURE_2D);a.needsUpdate=!1;if(a.onUpdate)a.onUpdate()};this.setTexture=function(a,b){l.activeTexture(l.TEXTURE0+b);a.needsUpdate?J.uploadTexture(a):
-l.bindTexture(l.TEXTURE_2D,a.__webglTexture)};this.setRenderTarget=function(a){var b=a instanceof THREE.WebGLRenderTargetCube;if(a&&void 0===a.__webglFramebuffer){void 0===a.depthBuffer&&(a.depthBuffer=!0);void 0===a.stencilBuffer&&(a.stencilBuffer=!0);a.addEventListener("dispose",Zc);a.__webglTexture=l.createTexture();J.info.memory.textures++;var c=THREE.Math.isPowerOfTwo(a.width)&&THREE.Math.isPowerOfTwo(a.height),d=Q(a.format),e=Q(a.type);if(b){a.__webglFramebuffer=[];a.__webglRenderbuffer=[];
-l.bindTexture(l.TEXTURE_CUBE_MAP,a.__webglTexture);F(l.TEXTURE_CUBE_MAP,a,c);for(var f=0;6>f;f++){a.__webglFramebuffer[f]=l.createFramebuffer();a.__webglRenderbuffer[f]=l.createRenderbuffer();l.texImage2D(l.TEXTURE_CUBE_MAP_POSITIVE_X+f,0,d,a.width,a.height,0,d,e,null);var g=a,h=l.TEXTURE_CUBE_MAP_POSITIVE_X+f;l.bindFramebuffer(l.FRAMEBUFFER,a.__webglFramebuffer[f]);l.framebufferTexture2D(l.FRAMEBUFFER,l.COLOR_ATTACHMENT0,h,g.__webglTexture,0);H(a.__webglRenderbuffer[f],a)}c&&l.generateMipmap(l.TEXTURE_CUBE_MAP)}else a.__webglFramebuffer=
-l.createFramebuffer(),a.__webglRenderbuffer=a.shareDepthFrom?a.shareDepthFrom.__webglRenderbuffer:l.createRenderbuffer(),l.bindTexture(l.TEXTURE_2D,a.__webglTexture),F(l.TEXTURE_2D,a,c),l.texImage2D(l.TEXTURE_2D,0,d,a.width,a.height,0,d,e,null),d=l.TEXTURE_2D,l.bindFramebuffer(l.FRAMEBUFFER,a.__webglFramebuffer),l.framebufferTexture2D(l.FRAMEBUFFER,l.COLOR_ATTACHMENT0,d,a.__webglTexture,0),a.shareDepthFrom?a.depthBuffer&&!a.stencilBuffer?l.framebufferRenderbuffer(l.FRAMEBUFFER,l.DEPTH_ATTACHMENT,
-l.RENDERBUFFER,a.__webglRenderbuffer):a.depthBuffer&&a.stencilBuffer&&l.framebufferRenderbuffer(l.FRAMEBUFFER,l.DEPTH_STENCIL_ATTACHMENT,l.RENDERBUFFER,a.__webglRenderbuffer):H(a.__webglRenderbuffer,a),c&&l.generateMipmap(l.TEXTURE_2D);b?l.bindTexture(l.TEXTURE_CUBE_MAP,null):l.bindTexture(l.TEXTURE_2D,null);l.bindRenderbuffer(l.RENDERBUFFER,null);l.bindFramebuffer(l.FRAMEBUFFER,null)}a?(b=b?a.__webglFramebuffer[a.activeCubeFace]:a.__webglFramebuffer,c=a.width,a=a.height,e=d=0):(b=null,c=lc,a=mc,
-d=Pb,e=kc);b!==Tc&&(l.bindFramebuffer(l.FRAMEBUFFER,b),l.viewport(d,e,c,a),Tc=b);Uc=c;Vc=a};this.initMaterial=function(){console.warn("THREE.WebGLRenderer: .initMaterial() has been removed.")};this.addPrePlugin=function(){console.warn("THREE.WebGLRenderer: .addPrePlugin() has been removed.")};this.addPostPlugin=function(){console.warn("THREE.WebGLRenderer: .addPostPlugin() has been removed.")};this.updateShadowMap=function(){console.warn("THREE.WebGLRenderer: .updateShadowMap() has been removed.")}};
-THREE.WebGLRenderTarget=function(a,b,c){this.width=a;this.height=b;c=c||{};this.wrapS=void 0!==c.wrapS?c.wrapS:THREE.ClampToEdgeWrapping;this.wrapT=void 0!==c.wrapT?c.wrapT:THREE.ClampToEdgeWrapping;this.magFilter=void 0!==c.magFilter?c.magFilter:THREE.LinearFilter;this.minFilter=void 0!==c.minFilter?c.minFilter:THREE.LinearMipMapLinearFilter;this.anisotropy=void 0!==c.anisotropy?c.anisotropy:1;this.offset=new THREE.Vector2(0,0);this.repeat=new THREE.Vector2(1,1);this.format=void 0!==c.format?c.format:
-THREE.RGBAFormat;this.type=void 0!==c.type?c.type:THREE.UnsignedByteType;this.depthBuffer=void 0!==c.depthBuffer?c.depthBuffer:!0;this.stencilBuffer=void 0!==c.stencilBuffer?c.stencilBuffer:!0;this.generateMipmaps=!0;this.shareDepthFrom=null};
-THREE.WebGLRenderTarget.prototype={constructor:THREE.WebGLRenderTarget,setSize:function(a,b){this.width=a;this.height=b},clone:function(){var a=new THREE.WebGLRenderTarget(this.width,this.height);a.wrapS=this.wrapS;a.wrapT=this.wrapT;a.magFilter=this.magFilter;a.minFilter=this.minFilter;a.anisotropy=this.anisotropy;a.offset.copy(this.offset);a.repeat.copy(this.repeat);a.format=this.format;a.type=this.type;a.depthBuffer=this.depthBuffer;a.stencilBuffer=this.stencilBuffer;a.generateMipmaps=this.generateMipmaps;
-a.shareDepthFrom=this.shareDepthFrom;return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.WebGLRenderTarget.prototype);THREE.WebGLRenderTargetCube=function(a,b,c){THREE.WebGLRenderTarget.call(this,a,b,c);this.activeCubeFace=0};THREE.WebGLRenderTargetCube.prototype=Object.create(THREE.WebGLRenderTarget.prototype);
-THREE.WebGLExtensions=function(a){var b={};this.get=function(c){if(void 0!==b[c])return b[c];var d;switch(c){case "OES_texture_float":d=a.getExtension("OES_texture_float");break;case "OES_texture_float_linear":d=a.getExtension("OES_texture_float_linear");break;case "OES_standard_derivatives":d=a.getExtension("OES_standard_derivatives");break;case "EXT_texture_filter_anisotropic":d=a.getExtension("EXT_texture_filter_anisotropic")||a.getExtension("MOZ_EXT_texture_filter_anisotropic")||a.getExtension("WEBKIT_EXT_texture_filter_anisotropic");
-break;case "WEBGL_compressed_texture_s3tc":d=a.getExtension("WEBGL_compressed_texture_s3tc")||a.getExtension("MOZ_WEBGL_compressed_texture_s3tc")||a.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc");break;case "WEBGL_compressed_texture_pvrtc":d=a.getExtension("WEBGL_compressed_texture_pvrtc")||a.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc");break;case "OES_element_index_uint":d=a.getExtension("OES_element_index_uint");break;case "EXT_blend_minmax":d=a.getExtension("EXT_blend_minmax");break;
-case "EXT_frag_depth":d=a.getExtension("EXT_frag_depth")}null===d&&console.log("THREE.WebGLRenderer: "+c+" extension not supported.");return b[c]=d}};
-THREE.WebGLProgram=function(){var a=0;return function(b,c,d,e){var f=b.context,g=d.defines,h=d.__webglShader.uniforms,k=d.attributes,n=d.__webglShader.vertexShader,p=d.__webglShader.fragmentShader,q=d.index0AttributeName;void 0===q&&!0===e.morphTargets&&(q="position");var m="SHADOWMAP_TYPE_BASIC";e.shadowMapType===THREE.PCFShadowMap?m="SHADOWMAP_TYPE_PCF":e.shadowMapType===THREE.PCFSoftShadowMap&&(m="SHADOWMAP_TYPE_PCF_SOFT");var r,t;r=[];for(var s in g)t=g[s],!1!==t&&(t="#define "+s+" "+t,r.push(t));
-r=r.join("\n");g=f.createProgram();d instanceof THREE.RawShaderMaterial?b=d="":(d=["precision "+e.precision+" float;","precision "+e.precision+" int;",r,e.supportsVertexTextures?"#define VERTEX_TEXTURES":"",b.gammaInput?"#define GAMMA_INPUT":"",b.gammaOutput?"#define GAMMA_OUTPUT":"","#define MAX_DIR_LIGHTS "+e.maxDirLights,"#define MAX_POINT_LIGHTS "+e.maxPointLights,"#define MAX_SPOT_LIGHTS "+e.maxSpotLights,"#define MAX_HEMI_LIGHTS "+e.maxHemiLights,"#define MAX_SHADOWS "+e.maxShadows,"#define MAX_BONES "+
-e.maxBones,e.map?"#define USE_MAP":"",e.envMap?"#define USE_ENVMAP":"",e.lightMap?"#define USE_LIGHTMAP":"",e.bumpMap?"#define USE_BUMPMAP":"",e.normalMap?"#define USE_NORMALMAP":"",e.specularMap?"#define USE_SPECULARMAP":"",e.alphaMap?"#define USE_ALPHAMAP":"",e.vertexColors?"#define USE_COLOR":"",e.skinning?"#define USE_SKINNING":"",e.useVertexTexture?"#define BONE_TEXTURE":"",e.morphTargets?"#define USE_MORPHTARGETS":"",e.morphNormals?"#define USE_MORPHNORMALS":"",e.wrapAround?"#define WRAP_AROUND":
-"",e.doubleSided?"#define DOUBLE_SIDED":"",e.flipSided?"#define FLIP_SIDED":"",e.shadowMapEnabled?"#define USE_SHADOWMAP":"",e.shadowMapEnabled?"#define "+m:"",e.shadowMapDebug?"#define SHADOWMAP_DEBUG":"",e.shadowMapCascade?"#define SHADOWMAP_CASCADE":"",e.sizeAttenuation?"#define USE_SIZEATTENUATION":"",e.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"","uniform mat4 modelMatrix;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\nuniform mat3 normalMatrix;\nuniform vec3 cameraPosition;\nattribute vec3 position;\nattribute vec3 normal;\nattribute vec2 uv;\nattribute vec2 uv2;\n#ifdef USE_COLOR\n\tattribute vec3 color;\n#endif\n#ifdef USE_MORPHTARGETS\n\tattribute vec3 morphTarget0;\n\tattribute vec3 morphTarget1;\n\tattribute vec3 morphTarget2;\n\tattribute vec3 morphTarget3;\n\t#ifdef USE_MORPHNORMALS\n\t\tattribute vec3 morphNormal0;\n\t\tattribute vec3 morphNormal1;\n\t\tattribute vec3 morphNormal2;\n\t\tattribute vec3 morphNormal3;\n\t#else\n\t\tattribute vec3 morphTarget4;\n\t\tattribute vec3 morphTarget5;\n\t\tattribute vec3 morphTarget6;\n\t\tattribute vec3 morphTarget7;\n\t#endif\n#endif\n#ifdef USE_SKINNING\n\tattribute vec4 skinIndex;\n\tattribute vec4 skinWeight;\n#endif\n"].join("\n"),
-b=["precision "+e.precision+" float;","precision "+e.precision+" int;",e.bumpMap||e.normalMap?"#extension GL_OES_standard_derivatives : enable":"",r,"#define MAX_DIR_LIGHTS "+e.maxDirLights,"#define MAX_POINT_LIGHTS "+e.maxPointLights,"#define MAX_SPOT_LIGHTS "+e.maxSpotLights,"#define MAX_HEMI_LIGHTS "+e.maxHemiLights,"#define MAX_SHADOWS "+e.maxShadows,e.alphaTest?"#define ALPHATEST "+e.alphaTest:"",b.gammaInput?"#define GAMMA_INPUT":"",b.gammaOutput?"#define GAMMA_OUTPUT":"",e.useFog&&e.fog?"#define USE_FOG":
-"",e.useFog&&e.fogExp?"#define FOG_EXP2":"",e.map?"#define USE_MAP":"",e.envMap?"#define USE_ENVMAP":"",e.lightMap?"#define USE_LIGHTMAP":"",e.bumpMap?"#define USE_BUMPMAP":"",e.normalMap?"#define USE_NORMALMAP":"",e.specularMap?"#define USE_SPECULARMAP":"",e.alphaMap?"#define USE_ALPHAMAP":"",e.vertexColors?"#define USE_COLOR":"",e.metal?"#define METAL":"",e.wrapAround?"#define WRAP_AROUND":"",e.doubleSided?"#define DOUBLE_SIDED":"",e.flipSided?"#define FLIP_SIDED":"",e.shadowMapEnabled?"#define USE_SHADOWMAP":
-"",e.shadowMapEnabled?"#define "+m:"",e.shadowMapDebug?"#define SHADOWMAP_DEBUG":"",e.shadowMapCascade?"#define SHADOWMAP_CASCADE":"",e.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"","uniform mat4 viewMatrix;\nuniform vec3 cameraPosition;\n"].join("\n"));n=new THREE.WebGLShader(f,f.VERTEX_SHADER,d+n);p=new THREE.WebGLShader(f,f.FRAGMENT_SHADER,b+p);f.attachShader(g,n);f.attachShader(g,p);void 0!==q&&f.bindAttribLocation(g,0,q);f.linkProgram(g);!1===f.getProgramParameter(g,f.LINK_STATUS)&&(console.error("THREE.WebGLProgram: Could not initialise shader."),
-console.error("gl.VALIDATE_STATUS",f.getProgramParameter(g,f.VALIDATE_STATUS)),console.error("gl.getError()",f.getError()));""!==f.getProgramInfoLog(g)&&console.warn("THREE.WebGLProgram: gl.getProgramInfoLog()",f.getProgramInfoLog(g));f.deleteShader(n);f.deleteShader(p);q="viewMatrix modelViewMatrix projectionMatrix normalMatrix modelMatrix cameraPosition morphTargetInfluences bindMatrix bindMatrixInverse".split(" ");e.useVertexTexture?(q.push("boneTexture"),q.push("boneTextureWidth"),q.push("boneTextureHeight")):
-q.push("boneGlobalMatrices");e.logarithmicDepthBuffer&&q.push("logDepthBufFC");for(var u in h)q.push(u);h=q;u={};q=0;for(b=h.length;q<b;q++)m=h[q],u[m]=f.getUniformLocation(g,m);this.uniforms=u;q="position normal uv uv2 tangent color skinIndex skinWeight lineDistance".split(" ");for(h=0;h<e.maxMorphTargets;h++)q.push("morphTarget"+h);for(h=0;h<e.maxMorphNormals;h++)q.push("morphNormal"+h);for(var v in k)q.push(v);e=q;k={};v=0;for(h=e.length;v<h;v++)u=e[v],k[u]=f.getAttribLocation(g,u);this.attributes=
-k;this.attributesKeys=Object.keys(this.attributes);this.id=a++;this.code=c;this.usedTimes=1;this.program=g;this.vertexShader=n;this.fragmentShader=p;return this}}();
-THREE.WebGLShader=function(){var a=function(a){a=a.split("\n");for(var c=0;c<a.length;c++)a[c]=c+1+": "+a[c];return a.join("\n")};return function(b,c,d){c=b.createShader(c);b.shaderSource(c,d);b.compileShader(c);!1===b.getShaderParameter(c,b.COMPILE_STATUS)&&console.error("THREE.WebGLShader: Shader couldn't compile.");""!==b.getShaderInfoLog(c)&&(console.warn("THREE.WebGLShader: gl.getShaderInfoLog()",b.getShaderInfoLog(c)),console.warn(a(d)));return c}}();
-THREE.LensFlarePlugin=function(a,b){var c,d,e,f,g,h,k,n,p,q,m=a.context,r,t,s,u,v,y;this.render=function(G,w,K,x){if(0!==b.length){G=new THREE.Vector3;var D=x/K,E=.5*K,A=.5*x,B=16/x,F=new THREE.Vector2(B*D,B),R=new THREE.Vector3(1,1,0),H=new THREE.Vector2(1,1);if(void 0===s){var B=new Float32Array([-1,-1,0,0,1,-1,1,0,1,1,1,1,-1,1,0,1]),C=new Uint16Array([0,1,2,0,2,3]);r=m.createBuffer();t=m.createBuffer();m.bindBuffer(m.ARRAY_BUFFER,r);m.bufferData(m.ARRAY_BUFFER,B,m.STATIC_DRAW);m.bindBuffer(m.ELEMENT_ARRAY_BUFFER,
-t);m.bufferData(m.ELEMENT_ARRAY_BUFFER,C,m.STATIC_DRAW);v=m.createTexture();y=m.createTexture();m.bindTexture(m.TEXTURE_2D,v);m.texImage2D(m.TEXTURE_2D,0,m.RGB,16,16,0,m.RGB,m.UNSIGNED_BYTE,null);m.texParameteri(m.TEXTURE_2D,m.TEXTURE_WRAP_S,m.CLAMP_TO_EDGE);m.texParameteri(m.TEXTURE_2D,m.TEXTURE_WRAP_T,m.CLAMP_TO_EDGE);m.texParameteri(m.TEXTURE_2D,m.TEXTURE_MAG_FILTER,m.NEAREST);m.texParameteri(m.TEXTURE_2D,m.TEXTURE_MIN_FILTER,m.NEAREST);m.bindTexture(m.TEXTURE_2D,y);m.texImage2D(m.TEXTURE_2D,0,
-m.RGBA,16,16,0,m.RGBA,m.UNSIGNED_BYTE,null);m.texParameteri(m.TEXTURE_2D,m.TEXTURE_WRAP_S,m.CLAMP_TO_EDGE);m.texParameteri(m.TEXTURE_2D,m.TEXTURE_WRAP_T,m.CLAMP_TO_EDGE);m.texParameteri(m.TEXTURE_2D,m.TEXTURE_MAG_FILTER,m.NEAREST);m.texParameteri(m.TEXTURE_2D,m.TEXTURE_MIN_FILTER,m.NEAREST);var B=(u=0<m.getParameter(m.MAX_VERTEX_TEXTURE_IMAGE_UNITS))?{vertexShader:"uniform lowp int renderType;\nuniform vec3 screenPosition;\nuniform vec2 scale;\nuniform float rotation;\nuniform sampler2D occlusionMap;\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUV;\nvarying float vVisibility;\nvoid main() {\nvUV = uv;\nvec2 pos = position;\nif( renderType == 2 ) {\nvec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );\nvVisibility =        visibility.r / 9.0;\nvVisibility *= 1.0 - visibility.g / 9.0;\nvVisibility *=       visibility.b / 9.0;\nvVisibility *= 1.0 - visibility.a / 9.0;\npos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;\npos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;\n}\ngl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );\n}",
-fragmentShader:"uniform lowp int renderType;\nuniform sampler2D map;\nuniform float opacity;\nuniform vec3 color;\nvarying vec2 vUV;\nvarying float vVisibility;\nvoid main() {\nif( renderType == 0 ) {\ngl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );\n} else if( renderType == 1 ) {\ngl_FragColor = texture2D( map, vUV );\n} else {\nvec4 texture = texture2D( map, vUV );\ntexture.a *= opacity * vVisibility;\ngl_FragColor = texture;\ngl_FragColor.rgb *= color;\n}\n}"}:{vertexShader:"uniform lowp int renderType;\nuniform vec3 screenPosition;\nuniform vec2 scale;\nuniform float rotation;\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUV;\nvoid main() {\nvUV = uv;\nvec2 pos = position;\nif( renderType == 2 ) {\npos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;\npos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;\n}\ngl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );\n}",
-fragmentShader:"precision mediump float;\nuniform lowp int renderType;\nuniform sampler2D map;\nuniform sampler2D occlusionMap;\nuniform float opacity;\nuniform vec3 color;\nvarying vec2 vUV;\nvoid main() {\nif( renderType == 0 ) {\ngl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );\n} else if( renderType == 1 ) {\ngl_FragColor = texture2D( map, vUV );\n} else {\nfloat visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;\nvisibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;\nvisibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;\nvisibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;\nvisibility = ( 1.0 - visibility / 4.0 );\nvec4 texture = texture2D( map, vUV );\ntexture.a *= opacity * visibility;\ngl_FragColor = texture;\ngl_FragColor.rgb *= color;\n}\n}"},
-C=m.createProgram(),T=m.createShader(m.FRAGMENT_SHADER),Q=m.createShader(m.VERTEX_SHADER),O="precision "+a.getPrecision()+" float;\n";m.shaderSource(T,O+B.fragmentShader);m.shaderSource(Q,O+B.vertexShader);m.compileShader(T);m.compileShader(Q);m.attachShader(C,T);m.attachShader(C,Q);m.linkProgram(C);s=C;p=m.getAttribLocation(s,"position");q=m.getAttribLocation(s,"uv");c=m.getUniformLocation(s,"renderType");d=m.getUniformLocation(s,"map");e=m.getUniformLocation(s,"occlusionMap");f=m.getUniformLocation(s,
-"opacity");g=m.getUniformLocation(s,"color");h=m.getUniformLocation(s,"scale");k=m.getUniformLocation(s,"rotation");n=m.getUniformLocation(s,"screenPosition")}m.useProgram(s);m.enableVertexAttribArray(p);m.enableVertexAttribArray(q);m.uniform1i(e,0);m.uniform1i(d,1);m.bindBuffer(m.ARRAY_BUFFER,r);m.vertexAttribPointer(p,2,m.FLOAT,!1,16,0);m.vertexAttribPointer(q,2,m.FLOAT,!1,16,8);m.bindBuffer(m.ELEMENT_ARRAY_BUFFER,t);m.disable(m.CULL_FACE);m.depthMask(!1);C=0;for(T=b.length;C<T;C++)if(B=16/x,F.set(B*
-D,B),Q=b[C],G.set(Q.matrixWorld.elements[12],Q.matrixWorld.elements[13],Q.matrixWorld.elements[14]),G.applyMatrix4(w.matrixWorldInverse),G.applyProjection(w.projectionMatrix),R.copy(G),H.x=R.x*E+E,H.y=R.y*A+A,u||0<H.x&&H.x<K&&0<H.y&&H.y<x){m.activeTexture(m.TEXTURE1);m.bindTexture(m.TEXTURE_2D,v);m.copyTexImage2D(m.TEXTURE_2D,0,m.RGB,H.x-8,H.y-8,16,16,0);m.uniform1i(c,0);m.uniform2f(h,F.x,F.y);m.uniform3f(n,R.x,R.y,R.z);m.disable(m.BLEND);m.enable(m.DEPTH_TEST);m.drawElements(m.TRIANGLES,6,m.UNSIGNED_SHORT,
-0);m.activeTexture(m.TEXTURE0);m.bindTexture(m.TEXTURE_2D,y);m.copyTexImage2D(m.TEXTURE_2D,0,m.RGBA,H.x-8,H.y-8,16,16,0);m.uniform1i(c,1);m.disable(m.DEPTH_TEST);m.activeTexture(m.TEXTURE1);m.bindTexture(m.TEXTURE_2D,v);m.drawElements(m.TRIANGLES,6,m.UNSIGNED_SHORT,0);Q.positionScreen.copy(R);Q.customUpdateCallback?Q.customUpdateCallback(Q):Q.updateLensFlares();m.uniform1i(c,2);m.enable(m.BLEND);for(var O=0,S=Q.lensFlares.length;O<S;O++){var X=Q.lensFlares[O];.001<X.opacity&&.001<X.scale&&(R.x=X.x,
-R.y=X.y,R.z=X.z,B=X.size*X.scale/x,F.x=B*D,F.y=B,m.uniform3f(n,R.x,R.y,R.z),m.uniform2f(h,F.x,F.y),m.uniform1f(k,X.rotation),m.uniform1f(f,X.opacity),m.uniform3f(g,X.color.r,X.color.g,X.color.b),a.setBlending(X.blending,X.blendEquation,X.blendSrc,X.blendDst),a.setTexture(X.texture,1),m.drawElements(m.TRIANGLES,6,m.UNSIGNED_SHORT,0))}}m.enable(m.CULL_FACE);m.enable(m.DEPTH_TEST);m.depthMask(!0);a.resetGLState()}}};
-THREE.ShadowMapPlugin=function(a,b,c,d){function e(a,b,d){if(b.visible){var f=c[b.id];if(f&&b.castShadow&&(!1===b.frustumCulled||!0===p.intersectsObject(b)))for(var g=0,h=f.length;g<h;g++){var k=f[g];b._modelViewMatrix.multiplyMatrices(d.matrixWorldInverse,b.matrixWorld);s.push(k)}g=0;for(h=b.children.length;g<h;g++)e(a,b.children[g],d)}}var f=a.context,g,h,k,n,p=new THREE.Frustum,q=new THREE.Matrix4,m=new THREE.Vector3,r=new THREE.Vector3,t=new THREE.Vector3,s=[],u=THREE.ShaderLib.depthRGBA,v=THREE.UniformsUtils.clone(u.uniforms);
-g=new THREE.ShaderMaterial({uniforms:v,vertexShader:u.vertexShader,fragmentShader:u.fragmentShader});h=new THREE.ShaderMaterial({uniforms:v,vertexShader:u.vertexShader,fragmentShader:u.fragmentShader,morphTargets:!0});k=new THREE.ShaderMaterial({uniforms:v,vertexShader:u.vertexShader,fragmentShader:u.fragmentShader,skinning:!0});n=new THREE.ShaderMaterial({uniforms:v,vertexShader:u.vertexShader,fragmentShader:u.fragmentShader,morphTargets:!0,skinning:!0});g._shadowPass=!0;h._shadowPass=!0;k._shadowPass=
-!0;n._shadowPass=!0;this.render=function(c,v){if(!1!==a.shadowMapEnabled){var u,K,x,D,E,A,B,F,R=[];D=0;f.clearColor(1,1,1,1);f.disable(f.BLEND);f.enable(f.CULL_FACE);f.frontFace(f.CCW);a.shadowMapCullFace===THREE.CullFaceFront?f.cullFace(f.FRONT):f.cullFace(f.BACK);a.setDepthTest(!0);u=0;for(K=b.length;u<K;u++)if(x=b[u],x.castShadow)if(x instanceof THREE.DirectionalLight&&x.shadowCascade)for(E=0;E<x.shadowCascadeCount;E++){var H;if(x.shadowCascadeArray[E])H=x.shadowCascadeArray[E];else{B=x;var C=
-E;H=new THREE.DirectionalLight;H.isVirtual=!0;H.onlyShadow=!0;H.castShadow=!0;H.shadowCameraNear=B.shadowCameraNear;H.shadowCameraFar=B.shadowCameraFar;H.shadowCameraLeft=B.shadowCameraLeft;H.shadowCameraRight=B.shadowCameraRight;H.shadowCameraBottom=B.shadowCameraBottom;H.shadowCameraTop=B.shadowCameraTop;H.shadowCameraVisible=B.shadowCameraVisible;H.shadowDarkness=B.shadowDarkness;H.shadowBias=B.shadowCascadeBias[C];H.shadowMapWidth=B.shadowCascadeWidth[C];H.shadowMapHeight=B.shadowCascadeHeight[C];
-H.pointsWorld=[];H.pointsFrustum=[];F=H.pointsWorld;A=H.pointsFrustum;for(var T=0;8>T;T++)F[T]=new THREE.Vector3,A[T]=new THREE.Vector3;F=B.shadowCascadeNearZ[C];B=B.shadowCascadeFarZ[C];A[0].set(-1,-1,F);A[1].set(1,-1,F);A[2].set(-1,1,F);A[3].set(1,1,F);A[4].set(-1,-1,B);A[5].set(1,-1,B);A[6].set(-1,1,B);A[7].set(1,1,B);H.originalCamera=v;A=new THREE.Gyroscope;A.position.copy(x.shadowCascadeOffset);A.add(H);A.add(H.target);v.add(A);x.shadowCascadeArray[E]=H;console.log("Created virtualLight",H)}C=
-x;F=E;B=C.shadowCascadeArray[F];B.position.copy(C.position);B.target.position.copy(C.target.position);B.lookAt(B.target);B.shadowCameraVisible=C.shadowCameraVisible;B.shadowDarkness=C.shadowDarkness;B.shadowBias=C.shadowCascadeBias[F];A=C.shadowCascadeNearZ[F];C=C.shadowCascadeFarZ[F];B=B.pointsFrustum;B[0].z=A;B[1].z=A;B[2].z=A;B[3].z=A;B[4].z=C;B[5].z=C;B[6].z=C;B[7].z=C;R[D]=H;D++}else R[D]=x,D++;u=0;for(K=R.length;u<K;u++){x=R[u];x.shadowMap||(E=THREE.LinearFilter,a.shadowMapType===THREE.PCFSoftShadowMap&&
-(E=THREE.NearestFilter),x.shadowMap=new THREE.WebGLRenderTarget(x.shadowMapWidth,x.shadowMapHeight,{minFilter:E,magFilter:E,format:THREE.RGBAFormat}),x.shadowMapSize=new THREE.Vector2(x.shadowMapWidth,x.shadowMapHeight),x.shadowMatrix=new THREE.Matrix4);if(!x.shadowCamera){if(x instanceof THREE.SpotLight)x.shadowCamera=new THREE.PerspectiveCamera(x.shadowCameraFov,x.shadowMapWidth/x.shadowMapHeight,x.shadowCameraNear,x.shadowCameraFar);else if(x instanceof THREE.DirectionalLight)x.shadowCamera=new THREE.OrthographicCamera(x.shadowCameraLeft,
-x.shadowCameraRight,x.shadowCameraTop,x.shadowCameraBottom,x.shadowCameraNear,x.shadowCameraFar);else{console.error("Unsupported light type for shadow");continue}c.add(x.shadowCamera);!0===c.autoUpdate&&c.updateMatrixWorld()}x.shadowCameraVisible&&!x.cameraHelper&&(x.cameraHelper=new THREE.CameraHelper(x.shadowCamera),c.add(x.cameraHelper));if(x.isVirtual&&H.originalCamera==v){E=v;D=x.shadowCamera;A=x.pointsFrustum;B=x.pointsWorld;m.set(Infinity,Infinity,Infinity);r.set(-Infinity,-Infinity,-Infinity);
-for(C=0;8>C;C++)F=B[C],F.copy(A[C]),F.unproject(E),F.applyMatrix4(D.matrixWorldInverse),F.x<m.x&&(m.x=F.x),F.x>r.x&&(r.x=F.x),F.y<m.y&&(m.y=F.y),F.y>r.y&&(r.y=F.y),F.z<m.z&&(m.z=F.z),F.z>r.z&&(r.z=F.z);D.left=m.x;D.right=r.x;D.top=r.y;D.bottom=m.y;D.updateProjectionMatrix()}D=x.shadowMap;A=x.shadowMatrix;E=x.shadowCamera;E.position.setFromMatrixPosition(x.matrixWorld);t.setFromMatrixPosition(x.target.matrixWorld);E.lookAt(t);E.updateMatrixWorld();E.matrixWorldInverse.getInverse(E.matrixWorld);x.cameraHelper&&
-(x.cameraHelper.visible=x.shadowCameraVisible);x.shadowCameraVisible&&x.cameraHelper.update();A.set(.5,0,0,.5,0,.5,0,.5,0,0,.5,.5,0,0,0,1);A.multiply(E.projectionMatrix);A.multiply(E.matrixWorldInverse);q.multiplyMatrices(E.projectionMatrix,E.matrixWorldInverse);p.setFromMatrix(q);a.setRenderTarget(D);a.clear();s.length=0;e(c,c,E);x=0;for(D=s.length;x<D;x++)B=s[x],A=B.object,B=B.buffer,C=A.material instanceof THREE.MeshFaceMaterial?A.material.materials[0]:A.material,F=void 0!==A.geometry.morphTargets&&
-0<A.geometry.morphTargets.length&&C.morphTargets,T=A instanceof THREE.SkinnedMesh&&C.skinning,F=A.customDepthMaterial?A.customDepthMaterial:T?F?n:k:F?h:g,a.setMaterialFaces(C),B instanceof THREE.BufferGeometry?a.renderBufferDirect(E,b,null,F,B,A):a.renderBuffer(E,b,null,F,B,A);x=0;for(D=d.length;x<D;x++)B=d[x],A=B.object,A.visible&&A.castShadow&&(A._modelViewMatrix.multiplyMatrices(E.matrixWorldInverse,A.matrixWorld),a.renderImmediateObject(E,b,null,g,A))}u=a.getClearColor();K=a.getClearAlpha();f.clearColor(u.r,
-u.g,u.b,K);f.enable(f.BLEND);a.shadowMapCullFace===THREE.CullFaceFront&&f.cullFace(f.BACK);a.resetGLState()}}};
-THREE.SpritePlugin=function(a,b){var c,d,e,f,g,h,k,n,p,q,m,r,t,s,u,v,y;function G(a,b){return a.z!==b.z?b.z-a.z:b.id-a.id}var w=a.context,K,x,D,E;this.render=function(A,B){if(0!==b.length){if(void 0===D){var F=new Float32Array([-.5,-.5,0,0,.5,-.5,1,0,.5,.5,1,1,-.5,.5,0,1]),R=new Uint16Array([0,1,2,0,2,3]);K=w.createBuffer();x=w.createBuffer();w.bindBuffer(w.ARRAY_BUFFER,K);w.bufferData(w.ARRAY_BUFFER,F,w.STATIC_DRAW);w.bindBuffer(w.ELEMENT_ARRAY_BUFFER,x);w.bufferData(w.ELEMENT_ARRAY_BUFFER,R,w.STATIC_DRAW);
-var F=w.createProgram(),R=w.createShader(w.VERTEX_SHADER),H=w.createShader(w.FRAGMENT_SHADER);w.shaderSource(R,["precision "+a.getPrecision()+" float;","uniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform float rotation;\nuniform vec2 scale;\nuniform vec2 uvOffset;\nuniform vec2 uvScale;\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUV;\nvoid main() {\nvUV = uvOffset + uv * uvScale;\nvec2 alignedPosition = position * scale;\nvec2 rotatedPosition;\nrotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\nrotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\nvec4 finalPosition;\nfinalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\nfinalPosition.xy += rotatedPosition;\nfinalPosition = projectionMatrix * finalPosition;\ngl_Position = finalPosition;\n}"].join("\n"));
-w.shaderSource(H,["precision "+a.getPrecision()+" float;","uniform vec3 color;\nuniform sampler2D map;\nuniform float opacity;\nuniform int fogType;\nuniform vec3 fogColor;\nuniform float fogDensity;\nuniform float fogNear;\nuniform float fogFar;\nuniform float alphaTest;\nvarying vec2 vUV;\nvoid main() {\nvec4 texture = texture2D( map, vUV );\nif ( texture.a < alphaTest ) discard;\ngl_FragColor = vec4( color * texture.xyz, texture.a * opacity );\nif ( fogType > 0 ) {\nfloat depth = gl_FragCoord.z / gl_FragCoord.w;\nfloat fogFactor = 0.0;\nif ( fogType == 1 ) {\nfogFactor = smoothstep( fogNear, fogFar, depth );\n} else {\nconst float LOG2 = 1.442695;\nfloat fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\nfogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n}\ngl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n}\n}"].join("\n"));
-w.compileShader(R);w.compileShader(H);w.attachShader(F,R);w.attachShader(F,H);w.linkProgram(F);D=F;v=w.getAttribLocation(D,"position");y=w.getAttribLocation(D,"uv");c=w.getUniformLocation(D,"uvOffset");d=w.getUniformLocation(D,"uvScale");e=w.getUniformLocation(D,"rotation");f=w.getUniformLocation(D,"scale");g=w.getUniformLocation(D,"color");h=w.getUniformLocation(D,"map");k=w.getUniformLocation(D,"opacity");n=w.getUniformLocation(D,"modelViewMatrix");p=w.getUniformLocation(D,"projectionMatrix");q=
-w.getUniformLocation(D,"fogType");m=w.getUniformLocation(D,"fogDensity");r=w.getUniformLocation(D,"fogNear");t=w.getUniformLocation(D,"fogFar");s=w.getUniformLocation(D,"fogColor");u=w.getUniformLocation(D,"alphaTest");F=document.createElement("canvas");F.width=8;F.height=8;R=F.getContext("2d");R.fillStyle="white";R.fillRect(0,0,8,8);E=new THREE.Texture(F);E.needsUpdate=!0}w.useProgram(D);w.enableVertexAttribArray(v);w.enableVertexAttribArray(y);w.disable(w.CULL_FACE);w.enable(w.BLEND);w.bindBuffer(w.ARRAY_BUFFER,
-K);w.vertexAttribPointer(v,2,w.FLOAT,!1,16,0);w.vertexAttribPointer(y,2,w.FLOAT,!1,16,8);w.bindBuffer(w.ELEMENT_ARRAY_BUFFER,x);w.uniformMatrix4fv(p,!1,B.projectionMatrix.elements);w.activeTexture(w.TEXTURE0);w.uniform1i(h,0);R=F=0;(H=A.fog)?(w.uniform3f(s,H.color.r,H.color.g,H.color.b),H instanceof THREE.Fog?(w.uniform1f(r,H.near),w.uniform1f(t,H.far),w.uniform1i(q,1),R=F=1):H instanceof THREE.FogExp2&&(w.uniform1f(m,H.density),w.uniform1i(q,2),R=F=2)):(w.uniform1i(q,0),R=F=0);for(var H=0,C=b.length;H<
-C;H++){var T=b[H];T._modelViewMatrix.multiplyMatrices(B.matrixWorldInverse,T.matrixWorld);T.z=null===T.renderDepth?-T._modelViewMatrix.elements[14]:T.renderDepth}b.sort(G);for(var Q=[],H=0,C=b.length;H<C;H++){var T=b[H],O=T.material;w.uniform1f(u,O.alphaTest);w.uniformMatrix4fv(n,!1,T._modelViewMatrix.elements);Q[0]=T.scale.x;Q[1]=T.scale.y;T=0;A.fog&&O.fog&&(T=R);F!==T&&(w.uniform1i(q,T),F=T);null!==O.map?(w.uniform2f(c,O.map.offset.x,O.map.offset.y),w.uniform2f(d,O.map.repeat.x,O.map.repeat.y)):
-(w.uniform2f(c,0,0),w.uniform2f(d,1,1));w.uniform1f(k,O.opacity);w.uniform3f(g,O.color.r,O.color.g,O.color.b);w.uniform1f(e,O.rotation);w.uniform2fv(f,Q);a.setBlending(O.blending,O.blendEquation,O.blendSrc,O.blendDst);a.setDepthTest(O.depthTest);a.setDepthWrite(O.depthWrite);O.map&&O.map.image&&O.map.image.width?a.setTexture(O.map,0):a.setTexture(E,0);w.drawElements(w.TRIANGLES,6,w.UNSIGNED_SHORT,0)}w.enable(w.CULL_FACE);a.resetGLState()}}};
-THREE.GeometryUtils={merge:function(a,b,c){console.warn("THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.");var d;b instanceof THREE.Mesh&&(b.matrixAutoUpdate&&b.updateMatrix(),d=b.matrix,b=b.geometry);a.merge(b,d,c)},center:function(a){console.warn("THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.");return a.center()}};
-THREE.ImageUtils={crossOrigin:void 0,loadTexture:function(a,b,c,d){var e=new THREE.ImageLoader;e.crossOrigin=this.crossOrigin;var f=new THREE.Texture(void 0,b);e.load(a,function(a){f.image=a;f.needsUpdate=!0;c&&c(f)},void 0,function(a){d&&d(a)});f.sourceFile=a;return f},loadTextureCube:function(a,b,c,d){var e=new THREE.ImageLoader;e.crossOrigin=this.crossOrigin;var f=new THREE.CubeTexture([],b);f.flipY=!1;var g=0;b=function(b){e.load(a[b],function(a){f.images[b]=a;g+=1;6===g&&(f.needsUpdate=!0,c&&
-c(f))})};d=0;for(var h=a.length;d<h;++d)b(d);return f},loadCompressedTexture:function(){console.error("THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.")},loadCompressedTextureCube:function(){console.error("THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.")},getNormalMap:function(a,b){var c=function(a){var b=Math.sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]);return[a[0]/b,a[1]/b,a[2]/b]};b|=1;var d=a.width,e=a.height,f=document.createElement("canvas");
-f.width=d;f.height=e;var g=f.getContext("2d");g.drawImage(a,0,0);for(var h=g.getImageData(0,0,d,e).data,k=g.createImageData(d,e),n=k.data,p=0;p<d;p++)for(var q=0;q<e;q++){var m=0>q-1?0:q-1,r=q+1>e-1?e-1:q+1,t=0>p-1?0:p-1,s=p+1>d-1?d-1:p+1,u=[],v=[0,0,h[4*(q*d+p)]/255*b];u.push([-1,0,h[4*(q*d+t)]/255*b]);u.push([-1,-1,h[4*(m*d+t)]/255*b]);u.push([0,-1,h[4*(m*d+p)]/255*b]);u.push([1,-1,h[4*(m*d+s)]/255*b]);u.push([1,0,h[4*(q*d+s)]/255*b]);u.push([1,1,h[4*(r*d+s)]/255*b]);u.push([0,1,h[4*(r*d+p)]/255*
-b]);u.push([-1,1,h[4*(r*d+t)]/255*b]);m=[];t=u.length;for(r=0;r<t;r++){var s=u[r],y=u[(r+1)%t],s=[s[0]-v[0],s[1]-v[1],s[2]-v[2]],y=[y[0]-v[0],y[1]-v[1],y[2]-v[2]];m.push(c([s[1]*y[2]-s[2]*y[1],s[2]*y[0]-s[0]*y[2],s[0]*y[1]-s[1]*y[0]]))}u=[0,0,0];for(r=0;r<m.length;r++)u[0]+=m[r][0],u[1]+=m[r][1],u[2]+=m[r][2];u[0]/=m.length;u[1]/=m.length;u[2]/=m.length;v=4*(q*d+p);n[v]=(u[0]+1)/2*255|0;n[v+1]=(u[1]+1)/2*255|0;n[v+2]=255*u[2]|0;n[v+3]=255}g.putImageData(k,0,0);return f},generateDataTexture:function(a,
-b,c){var d=a*b,e=new Uint8Array(3*d),f=Math.floor(255*c.r),g=Math.floor(255*c.g);c=Math.floor(255*c.b);for(var h=0;h<d;h++)e[3*h]=f,e[3*h+1]=g,e[3*h+2]=c;a=new THREE.DataTexture(e,a,b,THREE.RGBFormat);a.needsUpdate=!0;return a}};
-THREE.SceneUtils={createMultiMaterialObject:function(a,b){for(var c=new THREE.Object3D,d=0,e=b.length;d<e;d++)c.add(new THREE.Mesh(a,b[d]));return c},detach:function(a,b,c){a.applyMatrix(b.matrixWorld);b.remove(a);c.add(a)},attach:function(a,b,c){var d=new THREE.Matrix4;d.getInverse(c.matrixWorld);a.applyMatrix(d);b.remove(a);c.add(a)}};
-THREE.FontUtils={faces:{},face:"helvetiker",weight:"normal",style:"normal",size:150,divisions:10,getFace:function(){try{return this.faces[this.face][this.weight][this.style]}catch(a){throw"The font "+this.face+" with "+this.weight+" weight and "+this.style+" style is missing.";}},loadFace:function(a){var b=a.familyName.toLowerCase();this.faces[b]=this.faces[b]||{};this.faces[b][a.cssFontWeight]=this.faces[b][a.cssFontWeight]||{};this.faces[b][a.cssFontWeight][a.cssFontStyle]=a;return this.faces[b][a.cssFontWeight][a.cssFontStyle]=
-a},drawText:function(a){var b=this.getFace(),c=this.size/b.resolution,d=0,e=String(a).split(""),f=e.length,g=[];for(a=0;a<f;a++){var h=new THREE.Path,h=this.extractGlyphPoints(e[a],b,c,d,h),d=d+h.offset;g.push(h.path)}return{paths:g,offset:d/2}},extractGlyphPoints:function(a,b,c,d,e){var f=[],g,h,k,n,p,q,m,r,t,s,u,v=b.glyphs[a]||b.glyphs["?"];if(v){if(v.o)for(b=v._cachedOutline||(v._cachedOutline=v.o.split(" ")),n=b.length,a=0;a<n;)switch(k=b[a++],k){case "m":k=b[a++]*c+d;p=b[a++]*c;e.moveTo(k,p);
-break;case "l":k=b[a++]*c+d;p=b[a++]*c;e.lineTo(k,p);break;case "q":k=b[a++]*c+d;p=b[a++]*c;r=b[a++]*c+d;t=b[a++]*c;e.quadraticCurveTo(r,t,k,p);if(g=f[f.length-1])for(q=g.x,m=g.y,g=1,h=this.divisions;g<=h;g++){var y=g/h;THREE.Shape.Utils.b2(y,q,r,k);THREE.Shape.Utils.b2(y,m,t,p)}break;case "b":if(k=b[a++]*c+d,p=b[a++]*c,r=b[a++]*c+d,t=b[a++]*c,s=b[a++]*c+d,u=b[a++]*c,e.bezierCurveTo(r,t,s,u,k,p),g=f[f.length-1])for(q=g.x,m=g.y,g=1,h=this.divisions;g<=h;g++)y=g/h,THREE.Shape.Utils.b3(y,q,r,s,k),THREE.Shape.Utils.b3(y,
-m,t,u,p)}return{offset:v.ha*c,path:e}}}};
-THREE.FontUtils.generateShapes=function(a,b){b=b||{};var c=void 0!==b.curveSegments?b.curveSegments:4,d=void 0!==b.font?b.font:"helvetiker",e=void 0!==b.weight?b.weight:"normal",f=void 0!==b.style?b.style:"normal";THREE.FontUtils.size=void 0!==b.size?b.size:100;THREE.FontUtils.divisions=c;THREE.FontUtils.face=d;THREE.FontUtils.weight=e;THREE.FontUtils.style=f;c=THREE.FontUtils.drawText(a).paths;d=[];e=0;for(f=c.length;e<f;e++)Array.prototype.push.apply(d,c[e].toShapes());return d};
-(function(a){var b=function(a){for(var b=a.length,e=0,f=b-1,g=0;g<b;f=g++)e+=a[f].x*a[g].y-a[g].x*a[f].y;return.5*e};a.Triangulate=function(a,d){var e=a.length;if(3>e)return null;var f=[],g=[],h=[],k,n,p;if(0<b(a))for(n=0;n<e;n++)g[n]=n;else for(n=0;n<e;n++)g[n]=e-1-n;var q=2*e;for(n=e-1;2<e;){if(0>=q--){console.log("Warning, unable to triangulate polygon!");break}k=n;e<=k&&(k=0);n=k+1;e<=n&&(n=0);p=n+1;e<=p&&(p=0);var m;a:{var r=m=void 0,t=void 0,s=void 0,u=void 0,v=void 0,y=void 0,G=void 0,w=void 0,
-r=a[g[k]].x,t=a[g[k]].y,s=a[g[n]].x,u=a[g[n]].y,v=a[g[p]].x,y=a[g[p]].y;if(1E-10>(s-r)*(y-t)-(u-t)*(v-r))m=!1;else{var K=void 0,x=void 0,D=void 0,E=void 0,A=void 0,B=void 0,F=void 0,R=void 0,H=void 0,C=void 0,H=R=F=w=G=void 0,K=v-s,x=y-u,D=r-v,E=t-y,A=s-r,B=u-t;for(m=0;m<e;m++)if(G=a[g[m]].x,w=a[g[m]].y,!(G===r&&w===t||G===s&&w===u||G===v&&w===y)&&(F=G-r,R=w-t,H=G-s,C=w-u,G-=v,w-=y,H=K*C-x*H,F=A*R-B*F,R=D*w-E*G,-1E-10<=H&&-1E-10<=R&&-1E-10<=F)){m=!1;break a}m=!0}}if(m){f.push([a[g[k]],a[g[n]],a[g[p]]]);
-h.push([g[k],g[n],g[p]]);k=n;for(p=n+1;p<e;k++,p++)g[k]=g[p];e--;q=2*e}}return d?h:f};a.Triangulate.area=b;return a})(THREE.FontUtils);self._typeface_js={faces:THREE.FontUtils.faces,loadFace:THREE.FontUtils.loadFace};THREE.typeface_js=self._typeface_js;
-THREE.Audio=function(a){THREE.Object3D.call(this);this.type="Audio";this.context=a.context;this.source=this.context.createBufferSource();this.gain=this.context.createGain();this.gain.connect(this.context.destination);this.panner=this.context.createPanner();this.panner.connect(this.gain)};THREE.Audio.prototype=Object.create(THREE.Object3D.prototype);
-THREE.Audio.prototype.load=function(a){var b=this,c=new XMLHttpRequest;c.open("GET",a,!0);c.responseType="arraybuffer";c.onload=function(a){b.context.decodeAudioData(this.response,function(a){b.source.buffer=a;b.source.connect(b.panner);b.source.start(0)})};c.send();return this};THREE.Audio.prototype.setLoop=function(a){this.source.loop=a};THREE.Audio.prototype.setRefDistance=function(a){this.panner.refDistance=a};THREE.Audio.prototype.setRolloffFactor=function(a){this.panner.rolloffFactor=a};
-THREE.Audio.prototype.updateMatrixWorld=function(){var a=new THREE.Vector3;return function(b){THREE.Object3D.prototype.updateMatrixWorld.call(this,b);a.setFromMatrixPosition(this.matrixWorld);this.panner.setPosition(a.x,a.y,a.z)}}();THREE.AudioListener=function(){THREE.Object3D.call(this);this.type="AudioListener";this.context=new (window.AudioContext||window.webkitAudioContext)};THREE.AudioListener.prototype=Object.create(THREE.Object3D.prototype);
-THREE.AudioListener.prototype.updateMatrixWorld=function(){var a=new THREE.Vector3,b=new THREE.Quaternion,c=new THREE.Vector3,d=new THREE.Vector3,e=new THREE.Vector3,f=new THREE.Vector3;return function(g){THREE.Object3D.prototype.updateMatrixWorld.call(this,g);g=this.context.listener;this.matrixWorld.decompose(a,b,c);d.set(0,0,-1).applyQuaternion(b);e.subVectors(a,f);g.setPosition(a.x,a.y,a.z);g.setOrientation(d.x,d.y,d.z,this.up.x,this.up.y,this.up.z);g.setVelocity(e.x,e.y,e.z);f.copy(a)}}();
-THREE.Curve=function(){};THREE.Curve.prototype.getPoint=function(a){console.log("Warning, getPoint() not implemented!");return null};THREE.Curve.prototype.getPointAt=function(a){a=this.getUtoTmapping(a);return this.getPoint(a)};THREE.Curve.prototype.getPoints=function(a){a||(a=5);var b,c=[];for(b=0;b<=a;b++)c.push(this.getPoint(b/a));return c};THREE.Curve.prototype.getSpacedPoints=function(a){a||(a=5);var b,c=[];for(b=0;b<=a;b++)c.push(this.getPointAt(b/a));return c};
-THREE.Curve.prototype.getLength=function(){var a=this.getLengths();return a[a.length-1]};THREE.Curve.prototype.getLengths=function(a){a||(a=this.__arcLengthDivisions?this.__arcLengthDivisions:200);if(this.cacheArcLengths&&this.cacheArcLengths.length==a+1&&!this.needsUpdate)return this.cacheArcLengths;this.needsUpdate=!1;var b=[],c,d=this.getPoint(0),e,f=0;b.push(0);for(e=1;e<=a;e++)c=this.getPoint(e/a),f+=c.distanceTo(d),b.push(f),d=c;return this.cacheArcLengths=b};
-THREE.Curve.prototype.updateArcLengths=function(){this.needsUpdate=!0;this.getLengths()};THREE.Curve.prototype.getUtoTmapping=function(a,b){var c=this.getLengths(),d=0,e=c.length,f;f=b?b:a*c[e-1];for(var g=0,h=e-1,k;g<=h;)if(d=Math.floor(g+(h-g)/2),k=c[d]-f,0>k)g=d+1;else if(0<k)h=d-1;else{h=d;break}d=h;if(c[d]==f)return d/(e-1);g=c[d];return c=(d+(f-g)/(c[d+1]-g))/(e-1)};THREE.Curve.prototype.getTangent=function(a){var b=a-1E-4;a+=1E-4;0>b&&(b=0);1<a&&(a=1);b=this.getPoint(b);return this.getPoint(a).clone().sub(b).normalize()};
-THREE.Curve.prototype.getTangentAt=function(a){a=this.getUtoTmapping(a);return this.getTangent(a)};
-THREE.Curve.Utils={tangentQuadraticBezier:function(a,b,c,d){return 2*(1-a)*(c-b)+2*a*(d-c)},tangentCubicBezier:function(a,b,c,d,e){return-3*b*(1-a)*(1-a)+3*c*(1-a)*(1-a)-6*a*c*(1-a)+6*a*d*(1-a)-3*a*a*d+3*a*a*e},tangentSpline:function(a,b,c,d,e){return 6*a*a-6*a+(3*a*a-4*a+1)+(-6*a*a+6*a)+(3*a*a-2*a)},interpolate:function(a,b,c,d,e){a=.5*(c-a);d=.5*(d-b);var f=e*e;return(2*b-2*c+a+d)*e*f+(-3*b+3*c-2*a-d)*f+a*e+b}};
-THREE.Curve.create=function(a,b){a.prototype=Object.create(THREE.Curve.prototype);a.prototype.getPoint=b;return a};THREE.CurvePath=function(){this.curves=[];this.bends=[];this.autoClose=!1};THREE.CurvePath.prototype=Object.create(THREE.Curve.prototype);THREE.CurvePath.prototype.add=function(a){this.curves.push(a)};THREE.CurvePath.prototype.checkConnection=function(){};
-THREE.CurvePath.prototype.closePath=function(){var a=this.curves[0].getPoint(0),b=this.curves[this.curves.length-1].getPoint(1);a.equals(b)||this.curves.push(new THREE.LineCurve(b,a))};THREE.CurvePath.prototype.getPoint=function(a){var b=a*this.getLength(),c=this.getCurveLengths();for(a=0;a<c.length;){if(c[a]>=b)return b=c[a]-b,a=this.curves[a],b=1-b/a.getLength(),a.getPointAt(b);a++}return null};THREE.CurvePath.prototype.getLength=function(){var a=this.getCurveLengths();return a[a.length-1]};
-THREE.CurvePath.prototype.getCurveLengths=function(){if(this.cacheLengths&&this.cacheLengths.length==this.curves.length)return this.cacheLengths;var a=[],b=0,c,d=this.curves.length;for(c=0;c<d;c++)b+=this.curves[c].getLength(),a.push(b);return this.cacheLengths=a};
-THREE.CurvePath.prototype.getBoundingBox=function(){var a=this.getPoints(),b,c,d,e,f,g;b=c=Number.NEGATIVE_INFINITY;e=f=Number.POSITIVE_INFINITY;var h,k,n,p,q=a[0]instanceof THREE.Vector3;p=q?new THREE.Vector3:new THREE.Vector2;k=0;for(n=a.length;k<n;k++)h=a[k],h.x>b?b=h.x:h.x<e&&(e=h.x),h.y>c?c=h.y:h.y<f&&(f=h.y),q&&(h.z>d?d=h.z:h.z<g&&(g=h.z)),p.add(h);a={minX:e,minY:f,maxX:b,maxY:c};q&&(a.maxZ=d,a.minZ=g);return a};
-THREE.CurvePath.prototype.createPointsGeometry=function(a){a=this.getPoints(a,!0);return this.createGeometry(a)};THREE.CurvePath.prototype.createSpacedPointsGeometry=function(a){a=this.getSpacedPoints(a,!0);return this.createGeometry(a)};THREE.CurvePath.prototype.createGeometry=function(a){for(var b=new THREE.Geometry,c=0;c<a.length;c++)b.vertices.push(new THREE.Vector3(a[c].x,a[c].y,a[c].z||0));return b};THREE.CurvePath.prototype.addWrapPath=function(a){this.bends.push(a)};
-THREE.CurvePath.prototype.getTransformedPoints=function(a,b){var c=this.getPoints(a),d,e;b||(b=this.bends);d=0;for(e=b.length;d<e;d++)c=this.getWrapPoints(c,b[d]);return c};THREE.CurvePath.prototype.getTransformedSpacedPoints=function(a,b){var c=this.getSpacedPoints(a),d,e;b||(b=this.bends);d=0;for(e=b.length;d<e;d++)c=this.getWrapPoints(c,b[d]);return c};
-THREE.CurvePath.prototype.getWrapPoints=function(a,b){var c=this.getBoundingBox(),d,e,f,g,h,k;d=0;for(e=a.length;d<e;d++)f=a[d],g=f.x,h=f.y,k=g/c.maxX,k=b.getUtoTmapping(k,g),g=b.getPoint(k),k=b.getTangent(k),k.set(-k.y,k.x).multiplyScalar(h),f.x=g.x+k.x,f.y=g.y+k.y;return a};THREE.Gyroscope=function(){THREE.Object3D.call(this)};THREE.Gyroscope.prototype=Object.create(THREE.Object3D.prototype);
-THREE.Gyroscope.prototype.updateMatrixWorld=function(){var a=new THREE.Vector3,b=new THREE.Quaternion,c=new THREE.Vector3,d=new THREE.Vector3,e=new THREE.Quaternion,f=new THREE.Vector3;return function(g){this.matrixAutoUpdate&&this.updateMatrix();if(this.matrixWorldNeedsUpdate||g)this.parent?(this.matrixWorld.multiplyMatrices(this.parent.matrixWorld,this.matrix),this.matrixWorld.decompose(d,e,f),this.matrix.decompose(a,b,c),this.matrixWorld.compose(d,b,f)):this.matrixWorld.copy(this.matrix),this.matrixWorldNeedsUpdate=
-!1,g=!0;for(var h=0,k=this.children.length;h<k;h++)this.children[h].updateMatrixWorld(g)}}();THREE.Path=function(a){THREE.CurvePath.call(this);this.actions=[];a&&this.fromPoints(a)};THREE.Path.prototype=Object.create(THREE.CurvePath.prototype);THREE.PathActions={MOVE_TO:"moveTo",LINE_TO:"lineTo",QUADRATIC_CURVE_TO:"quadraticCurveTo",BEZIER_CURVE_TO:"bezierCurveTo",CSPLINE_THRU:"splineThru",ARC:"arc",ELLIPSE:"ellipse"};
-THREE.Path.prototype.fromPoints=function(a){this.moveTo(a[0].x,a[0].y);for(var b=1,c=a.length;b<c;b++)this.lineTo(a[b].x,a[b].y)};THREE.Path.prototype.moveTo=function(a,b){var c=Array.prototype.slice.call(arguments);this.actions.push({action:THREE.PathActions.MOVE_TO,args:c})};
-THREE.Path.prototype.lineTo=function(a,b){var c=Array.prototype.slice.call(arguments),d=this.actions[this.actions.length-1].args,d=new THREE.LineCurve(new THREE.Vector2(d[d.length-2],d[d.length-1]),new THREE.Vector2(a,b));this.curves.push(d);this.actions.push({action:THREE.PathActions.LINE_TO,args:c})};
-THREE.Path.prototype.quadraticCurveTo=function(a,b,c,d){var e=Array.prototype.slice.call(arguments),f=this.actions[this.actions.length-1].args,f=new THREE.QuadraticBezierCurve(new THREE.Vector2(f[f.length-2],f[f.length-1]),new THREE.Vector2(a,b),new THREE.Vector2(c,d));this.curves.push(f);this.actions.push({action:THREE.PathActions.QUADRATIC_CURVE_TO,args:e})};
-THREE.Path.prototype.bezierCurveTo=function(a,b,c,d,e,f){var g=Array.prototype.slice.call(arguments),h=this.actions[this.actions.length-1].args,h=new THREE.CubicBezierCurve(new THREE.Vector2(h[h.length-2],h[h.length-1]),new THREE.Vector2(a,b),new THREE.Vector2(c,d),new THREE.Vector2(e,f));this.curves.push(h);this.actions.push({action:THREE.PathActions.BEZIER_CURVE_TO,args:g})};
-THREE.Path.prototype.splineThru=function(a){var b=Array.prototype.slice.call(arguments),c=this.actions[this.actions.length-1].args,c=[new THREE.Vector2(c[c.length-2],c[c.length-1])];Array.prototype.push.apply(c,a);c=new THREE.SplineCurve(c);this.curves.push(c);this.actions.push({action:THREE.PathActions.CSPLINE_THRU,args:b})};THREE.Path.prototype.arc=function(a,b,c,d,e,f){var g=this.actions[this.actions.length-1].args;this.absarc(a+g[g.length-2],b+g[g.length-1],c,d,e,f)};
-THREE.Path.prototype.absarc=function(a,b,c,d,e,f){this.absellipse(a,b,c,c,d,e,f)};THREE.Path.prototype.ellipse=function(a,b,c,d,e,f,g){var h=this.actions[this.actions.length-1].args;this.absellipse(a+h[h.length-2],b+h[h.length-1],c,d,e,f,g)};THREE.Path.prototype.absellipse=function(a,b,c,d,e,f,g){var h=Array.prototype.slice.call(arguments),k=new THREE.EllipseCurve(a,b,c,d,e,f,g);this.curves.push(k);k=k.getPoint(1);h.push(k.x);h.push(k.y);this.actions.push({action:THREE.PathActions.ELLIPSE,args:h})};
-THREE.Path.prototype.getSpacedPoints=function(a,b){a||(a=40);for(var c=[],d=0;d<a;d++)c.push(this.getPoint(d/a));return c};
-THREE.Path.prototype.getPoints=function(a,b){if(this.useSpacedPoints)return console.log("tata"),this.getSpacedPoints(a,b);a=a||12;var c=[],d,e,f,g,h,k,n,p,q,m,r,t,s;d=0;for(e=this.actions.length;d<e;d++)switch(f=this.actions[d],g=f.action,f=f.args,g){case THREE.PathActions.MOVE_TO:c.push(new THREE.Vector2(f[0],f[1]));break;case THREE.PathActions.LINE_TO:c.push(new THREE.Vector2(f[0],f[1]));break;case THREE.PathActions.QUADRATIC_CURVE_TO:h=f[2];k=f[3];q=f[0];m=f[1];0<c.length?(g=c[c.length-1],r=g.x,
-t=g.y):(g=this.actions[d-1].args,r=g[g.length-2],t=g[g.length-1]);for(f=1;f<=a;f++)s=f/a,g=THREE.Shape.Utils.b2(s,r,q,h),s=THREE.Shape.Utils.b2(s,t,m,k),c.push(new THREE.Vector2(g,s));break;case THREE.PathActions.BEZIER_CURVE_TO:h=f[4];k=f[5];q=f[0];m=f[1];n=f[2];p=f[3];0<c.length?(g=c[c.length-1],r=g.x,t=g.y):(g=this.actions[d-1].args,r=g[g.length-2],t=g[g.length-1]);for(f=1;f<=a;f++)s=f/a,g=THREE.Shape.Utils.b3(s,r,q,n,h),s=THREE.Shape.Utils.b3(s,t,m,p,k),c.push(new THREE.Vector2(g,s));break;case THREE.PathActions.CSPLINE_THRU:g=
-this.actions[d-1].args;s=[new THREE.Vector2(g[g.length-2],g[g.length-1])];g=a*f[0].length;s=s.concat(f[0]);s=new THREE.SplineCurve(s);for(f=1;f<=g;f++)c.push(s.getPointAt(f/g));break;case THREE.PathActions.ARC:h=f[0];k=f[1];m=f[2];n=f[3];g=f[4];q=!!f[5];r=g-n;t=2*a;for(f=1;f<=t;f++)s=f/t,q||(s=1-s),s=n+s*r,g=h+m*Math.cos(s),s=k+m*Math.sin(s),c.push(new THREE.Vector2(g,s));break;case THREE.PathActions.ELLIPSE:for(h=f[0],k=f[1],m=f[2],p=f[3],n=f[4],g=f[5],q=!!f[6],r=g-n,t=2*a,f=1;f<=t;f++)s=f/t,q||
-(s=1-s),s=n+s*r,g=h+m*Math.cos(s),s=k+p*Math.sin(s),c.push(new THREE.Vector2(g,s))}d=c[c.length-1];1E-10>Math.abs(d.x-c[0].x)&&1E-10>Math.abs(d.y-c[0].y)&&c.splice(c.length-1,1);b&&c.push(c[0]);return c};
-THREE.Path.prototype.toShapes=function(a,b){function c(a){for(var b=[],c=0,d=a.length;c<d;c++){var e=a[c],f=new THREE.Shape;f.actions=e.actions;f.curves=e.curves;b.push(f)}return b}function d(a,b){for(var c=b.length,d=!1,e=c-1,f=0;f<c;e=f++){var g=b[e],h=b[f],k=h.x-g.x,m=h.y-g.y;if(1E-10<Math.abs(m)){if(0>m&&(g=b[f],k=-k,h=b[e],m=-m),!(a.y<g.y||a.y>h.y))if(a.y==g.y){if(a.x==g.x)return!0}else{e=m*(a.x-g.x)-k*(a.y-g.y);if(0==e)return!0;0>e||(d=!d)}}else if(a.y==g.y&&(h.x<=a.x&&a.x<=g.x||g.x<=a.x&&a.x<=
-h.x))return!0}return d}var e=function(a){var b,c,d,e,f=[],g=new THREE.Path;b=0;for(c=a.length;b<c;b++)d=a[b],e=d.args,d=d.action,d==THREE.PathActions.MOVE_TO&&0!=g.actions.length&&(f.push(g),g=new THREE.Path),g[d].apply(g,e);0!=g.actions.length&&f.push(g);return f}(this.actions);if(0==e.length)return[];if(!0===b)return c(e);var f,g,h,k=[];if(1==e.length)return g=e[0],h=new THREE.Shape,h.actions=g.actions,h.curves=g.curves,k.push(h),k;var n=!THREE.Shape.Utils.isClockWise(e[0].getPoints()),n=a?!n:n;
-h=[];var p=[],q=[],m=0,r;p[m]=void 0;q[m]=[];var t,s;t=0;for(s=e.length;t<s;t++)g=e[t],r=g.getPoints(),f=THREE.Shape.Utils.isClockWise(r),(f=a?!f:f)?(!n&&p[m]&&m++,p[m]={s:new THREE.Shape,p:r},p[m].s.actions=g.actions,p[m].s.curves=g.curves,n&&m++,q[m]=[]):q[m].push({h:g,p:r[0]});if(!p[0])return c(e);if(1<p.length){t=!1;s=[];g=0;for(e=p.length;g<e;g++)h[g]=[];g=0;for(e=p.length;g<e;g++)for(f=q[g],n=0;n<f.length;n++){m=f[n];r=!0;for(var u=0;u<p.length;u++)d(m.p,p[u].p)&&(g!=u&&s.push({froms:g,tos:u,
-hole:n}),r?(r=!1,h[u].push(m)):t=!0);r&&h[g].push(m)}0<s.length&&(t||(q=h))}t=0;for(s=p.length;t<s;t++)for(h=p[t].s,k.push(h),g=q[t],e=0,f=g.length;e<f;e++)h.holes.push(g[e].h);return k};THREE.Shape=function(){THREE.Path.apply(this,arguments);this.holes=[]};THREE.Shape.prototype=Object.create(THREE.Path.prototype);THREE.Shape.prototype.extrude=function(a){return new THREE.ExtrudeGeometry(this,a)};THREE.Shape.prototype.makeGeometry=function(a){return new THREE.ShapeGeometry(this,a)};
-THREE.Shape.prototype.getPointsHoles=function(a){var b,c=this.holes.length,d=[];for(b=0;b<c;b++)d[b]=this.holes[b].getTransformedPoints(a,this.bends);return d};THREE.Shape.prototype.getSpacedPointsHoles=function(a){var b,c=this.holes.length,d=[];for(b=0;b<c;b++)d[b]=this.holes[b].getTransformedSpacedPoints(a,this.bends);return d};THREE.Shape.prototype.extractAllPoints=function(a){return{shape:this.getTransformedPoints(a),holes:this.getPointsHoles(a)}};
-THREE.Shape.prototype.extractPoints=function(a){return this.useSpacedPoints?this.extractAllSpacedPoints(a):this.extractAllPoints(a)};THREE.Shape.prototype.extractAllSpacedPoints=function(a){return{shape:this.getTransformedSpacedPoints(a),holes:this.getSpacedPointsHoles(a)}};
-THREE.Shape.Utils={triangulateShape:function(a,b){function c(a,b,c){return a.x!=b.x?a.x<b.x?a.x<=c.x&&c.x<=b.x:b.x<=c.x&&c.x<=a.x:a.y<b.y?a.y<=c.y&&c.y<=b.y:b.y<=c.y&&c.y<=a.y}function d(a,b,d,e,f){var g=b.x-a.x,h=b.y-a.y,k=e.x-d.x,n=e.y-d.y,p=a.x-d.x,q=a.y-d.y,D=h*k-g*n,E=h*p-g*q;if(1E-10<Math.abs(D)){if(0<D){if(0>E||E>D)return[];k=n*p-k*q;if(0>k||k>D)return[]}else{if(0<E||E<D)return[];k=n*p-k*q;if(0<k||k<D)return[]}if(0==k)return!f||0!=E&&E!=D?[a]:[];if(k==D)return!f||0!=E&&E!=D?[b]:[];if(0==E)return[d];
-if(E==D)return[e];f=k/D;return[{x:a.x+f*g,y:a.y+f*h}]}if(0!=E||n*p!=k*q)return[];h=0==g&&0==h;k=0==k&&0==n;if(h&&k)return a.x!=d.x||a.y!=d.y?[]:[a];if(h)return c(d,e,a)?[a]:[];if(k)return c(a,b,d)?[d]:[];0!=g?(a.x<b.x?(g=a,k=a.x,h=b,a=b.x):(g=b,k=b.x,h=a,a=a.x),d.x<e.x?(b=d,D=d.x,n=e,d=e.x):(b=e,D=e.x,n=d,d=d.x)):(a.y<b.y?(g=a,k=a.y,h=b,a=b.y):(g=b,k=b.y,h=a,a=a.y),d.y<e.y?(b=d,D=d.y,n=e,d=e.y):(b=e,D=e.y,n=d,d=d.y));return k<=D?a<D?[]:a==D?f?[]:[b]:a<=d?[b,h]:[b,n]:k>d?[]:k==d?f?[]:[g]:a<=d?[g,h]:
-[g,n]}function e(a,b,c,d){var e=b.x-a.x,f=b.y-a.y;b=c.x-a.x;c=c.y-a.y;var g=d.x-a.x;d=d.y-a.y;a=e*c-f*b;e=e*d-f*g;return 1E-10<Math.abs(a)?(b=g*c-d*b,0<a?0<=e&&0<=b:0<=e||0<=b):0<e}var f,g,h,k,n,p={};h=a.concat();f=0;for(g=b.length;f<g;f++)Array.prototype.push.apply(h,b[f]);f=0;for(g=h.length;f<g;f++)n=h[f].x+":"+h[f].y,void 0!==p[n]&&console.log("Duplicate point",n),p[n]=f;f=function(a,b){function c(a,b){var d=h.length-1,f=a-1;0>f&&(f=d);var g=a+1;g>d&&(g=0);d=e(h[a],h[f],h[g],k[b]);if(!d)return!1;
-d=k.length-1;f=b-1;0>f&&(f=d);g=b+1;g>d&&(g=0);return(d=e(k[b],k[f],k[g],h[a]))?!0:!1}function f(a,b){var c,e;for(c=0;c<h.length;c++)if(e=c+1,e%=h.length,e=d(a,b,h[c],h[e],!0),0<e.length)return!0;return!1}function g(a,c){var e,f,h,k;for(e=0;e<n.length;e++)for(f=b[n[e]],h=0;h<f.length;h++)if(k=h+1,k%=f.length,k=d(a,c,f[h],f[k],!0),0<k.length)return!0;return!1}var h=a.concat(),k,n=[],p,q,x,D,E,A=[],B,F,R,H=0;for(p=b.length;H<p;H++)n.push(H);B=0;for(var C=2*n.length;0<n.length;){C--;if(0>C){console.log("Infinite Loop! Holes left:"+
-n.length+", Probably Hole outside Shape!");break}for(q=B;q<h.length;q++){x=h[q];p=-1;for(H=0;H<n.length;H++)if(D=n[H],E=x.x+":"+x.y+":"+D,void 0===A[E]){k=b[D];for(F=0;F<k.length;F++)if(D=k[F],c(q,F)&&!f(x,D)&&!g(x,D)){p=F;n.splice(H,1);B=h.slice(0,q+1);D=h.slice(q);F=k.slice(p);R=k.slice(0,p+1);h=B.concat(F).concat(R).concat(D);B=q;break}if(0<=p)break;A[E]=!0}if(0<=p)break}}return h}(a,b);var q=THREE.FontUtils.Triangulate(f,!1);f=0;for(g=q.length;f<g;f++)for(k=q[f],h=0;3>h;h++)n=k[h].x+":"+k[h].y,
-n=p[n],void 0!==n&&(k[h]=n);return q.concat()},isClockWise:function(a){return 0>THREE.FontUtils.Triangulate.area(a)},b2p0:function(a,b){var c=1-a;return c*c*b},b2p1:function(a,b){return 2*(1-a)*a*b},b2p2:function(a,b){return a*a*b},b2:function(a,b,c,d){return this.b2p0(a,b)+this.b2p1(a,c)+this.b2p2(a,d)},b3p0:function(a,b){var c=1-a;return c*c*c*b},b3p1:function(a,b){var c=1-a;return 3*c*c*a*b},b3p2:function(a,b){return 3*(1-a)*a*a*b},b3p3:function(a,b){return a*a*a*b},b3:function(a,b,c,d,e){return this.b3p0(a,
-b)+this.b3p1(a,c)+this.b3p2(a,d)+this.b3p3(a,e)}};THREE.LineCurve=function(a,b){this.v1=a;this.v2=b};THREE.LineCurve.prototype=Object.create(THREE.Curve.prototype);THREE.LineCurve.prototype.getPoint=function(a){var b=this.v2.clone().sub(this.v1);b.multiplyScalar(a).add(this.v1);return b};THREE.LineCurve.prototype.getPointAt=function(a){return this.getPoint(a)};THREE.LineCurve.prototype.getTangent=function(a){return this.v2.clone().sub(this.v1).normalize()};
-THREE.QuadraticBezierCurve=function(a,b,c){this.v0=a;this.v1=b;this.v2=c};THREE.QuadraticBezierCurve.prototype=Object.create(THREE.Curve.prototype);THREE.QuadraticBezierCurve.prototype.getPoint=function(a){var b=new THREE.Vector2;b.x=THREE.Shape.Utils.b2(a,this.v0.x,this.v1.x,this.v2.x);b.y=THREE.Shape.Utils.b2(a,this.v0.y,this.v1.y,this.v2.y);return b};
-THREE.QuadraticBezierCurve.prototype.getTangent=function(a){var b=new THREE.Vector2;b.x=THREE.Curve.Utils.tangentQuadraticBezier(a,this.v0.x,this.v1.x,this.v2.x);b.y=THREE.Curve.Utils.tangentQuadraticBezier(a,this.v0.y,this.v1.y,this.v2.y);return b.normalize()};THREE.CubicBezierCurve=function(a,b,c,d){this.v0=a;this.v1=b;this.v2=c;this.v3=d};THREE.CubicBezierCurve.prototype=Object.create(THREE.Curve.prototype);
-THREE.CubicBezierCurve.prototype.getPoint=function(a){var b;b=THREE.Shape.Utils.b3(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x);a=THREE.Shape.Utils.b3(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y);return new THREE.Vector2(b,a)};THREE.CubicBezierCurve.prototype.getTangent=function(a){var b;b=THREE.Curve.Utils.tangentCubicBezier(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x);a=THREE.Curve.Utils.tangentCubicBezier(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y);b=new THREE.Vector2(b,a);b.normalize();return b};
-THREE.SplineCurve=function(a){this.points=void 0==a?[]:a};THREE.SplineCurve.prototype=Object.create(THREE.Curve.prototype);THREE.SplineCurve.prototype.getPoint=function(a){var b=this.points;a*=b.length-1;var c=Math.floor(a);a-=c;var d=b[0==c?c:c-1],e=b[c],f=b[c>b.length-2?b.length-1:c+1],b=b[c>b.length-3?b.length-1:c+2],c=new THREE.Vector2;c.x=THREE.Curve.Utils.interpolate(d.x,e.x,f.x,b.x,a);c.y=THREE.Curve.Utils.interpolate(d.y,e.y,f.y,b.y,a);return c};
-THREE.EllipseCurve=function(a,b,c,d,e,f,g){this.aX=a;this.aY=b;this.xRadius=c;this.yRadius=d;this.aStartAngle=e;this.aEndAngle=f;this.aClockwise=g};THREE.EllipseCurve.prototype=Object.create(THREE.Curve.prototype);
-THREE.EllipseCurve.prototype.getPoint=function(a){var b=this.aEndAngle-this.aStartAngle;0>b&&(b+=2*Math.PI);b>2*Math.PI&&(b-=2*Math.PI);a=!0===this.aClockwise?this.aEndAngle+(1-a)*(2*Math.PI-b):this.aStartAngle+a*b;b=new THREE.Vector2;b.x=this.aX+this.xRadius*Math.cos(a);b.y=this.aY+this.yRadius*Math.sin(a);return b};THREE.ArcCurve=function(a,b,c,d,e,f){THREE.EllipseCurve.call(this,a,b,c,c,d,e,f)};THREE.ArcCurve.prototype=Object.create(THREE.EllipseCurve.prototype);
-THREE.LineCurve3=THREE.Curve.create(function(a,b){this.v1=a;this.v2=b},function(a){var b=new THREE.Vector3;b.subVectors(this.v2,this.v1);b.multiplyScalar(a);b.add(this.v1);return b});THREE.QuadraticBezierCurve3=THREE.Curve.create(function(a,b,c){this.v0=a;this.v1=b;this.v2=c},function(a){var b=new THREE.Vector3;b.x=THREE.Shape.Utils.b2(a,this.v0.x,this.v1.x,this.v2.x);b.y=THREE.Shape.Utils.b2(a,this.v0.y,this.v1.y,this.v2.y);b.z=THREE.Shape.Utils.b2(a,this.v0.z,this.v1.z,this.v2.z);return b});
-THREE.CubicBezierCurve3=THREE.Curve.create(function(a,b,c,d){this.v0=a;this.v1=b;this.v2=c;this.v3=d},function(a){var b=new THREE.Vector3;b.x=THREE.Shape.Utils.b3(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x);b.y=THREE.Shape.Utils.b3(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y);b.z=THREE.Shape.Utils.b3(a,this.v0.z,this.v1.z,this.v2.z,this.v3.z);return b});
-THREE.SplineCurve3=THREE.Curve.create(function(a){this.points=void 0==a?[]:a},function(a){var b=this.points;a*=b.length-1;var c=Math.floor(a);a-=c;var d=b[0==c?c:c-1],e=b[c],f=b[c>b.length-2?b.length-1:c+1],b=b[c>b.length-3?b.length-1:c+2],c=new THREE.Vector3;c.x=THREE.Curve.Utils.interpolate(d.x,e.x,f.x,b.x,a);c.y=THREE.Curve.Utils.interpolate(d.y,e.y,f.y,b.y,a);c.z=THREE.Curve.Utils.interpolate(d.z,e.z,f.z,b.z,a);return c});
-THREE.ClosedSplineCurve3=THREE.Curve.create(function(a){this.points=void 0==a?[]:a},function(a){var b=this.points;a*=b.length-0;var c=Math.floor(a);a-=c;var c=c+(0<c?0:(Math.floor(Math.abs(c)/b.length)+1)*b.length),d=b[(c-1)%b.length],e=b[c%b.length],f=b[(c+1)%b.length],b=b[(c+2)%b.length],c=new THREE.Vector3;c.x=THREE.Curve.Utils.interpolate(d.x,e.x,f.x,b.x,a);c.y=THREE.Curve.Utils.interpolate(d.y,e.y,f.y,b.y,a);c.z=THREE.Curve.Utils.interpolate(d.z,e.z,f.z,b.z,a);return c});
-THREE.AnimationHandler={LINEAR:0,CATMULLROM:1,CATMULLROM_FORWARD:2,add:function(){console.warn("THREE.AnimationHandler.add() has been deprecated.")},get:function(){console.warn("THREE.AnimationHandler.get() has been deprecated.")},remove:function(){console.warn("THREE.AnimationHandler.remove() has been deprecated.")},animations:[],init:function(a){if(!0!==a.initialized){for(var b=0;b<a.hierarchy.length;b++){for(var c=0;c<a.hierarchy[b].keys.length;c++)if(0>a.hierarchy[b].keys[c].time&&(a.hierarchy[b].keys[c].time=
-0),void 0!==a.hierarchy[b].keys[c].rot&&!(a.hierarchy[b].keys[c].rot instanceof THREE.Quaternion)){var d=a.hierarchy[b].keys[c].rot;a.hierarchy[b].keys[c].rot=(new THREE.Quaternion).fromArray(d)}if(a.hierarchy[b].keys.length&&void 0!==a.hierarchy[b].keys[0].morphTargets){d={};for(c=0;c<a.hierarchy[b].keys.length;c++)for(var e=0;e<a.hierarchy[b].keys[c].morphTargets.length;e++){var f=a.hierarchy[b].keys[c].morphTargets[e];d[f]=-1}a.hierarchy[b].usedMorphTargets=d;for(c=0;c<a.hierarchy[b].keys.length;c++){var g=
-{};for(f in d){for(e=0;e<a.hierarchy[b].keys[c].morphTargets.length;e++)if(a.hierarchy[b].keys[c].morphTargets[e]===f){g[f]=a.hierarchy[b].keys[c].morphTargetsInfluences[e];break}e===a.hierarchy[b].keys[c].morphTargets.length&&(g[f]=0)}a.hierarchy[b].keys[c].morphTargetsInfluences=g}}for(c=1;c<a.hierarchy[b].keys.length;c++)a.hierarchy[b].keys[c].time===a.hierarchy[b].keys[c-1].time&&(a.hierarchy[b].keys.splice(c,1),c--);for(c=0;c<a.hierarchy[b].keys.length;c++)a.hierarchy[b].keys[c].index=c}a.initialized=
-!0;return a}},parse:function(a){var b=function(a,c){c.push(a);for(var d=0;d<a.children.length;d++)b(a.children[d],c)},c=[];if(a instanceof THREE.SkinnedMesh)for(var d=0;d<a.skeleton.bones.length;d++)c.push(a.skeleton.bones[d]);else b(a,c);return c},play:function(a){-1===this.animations.indexOf(a)&&this.animations.push(a)},stop:function(a){a=this.animations.indexOf(a);-1!==a&&this.animations.splice(a,1)},update:function(a){for(var b=0;b<this.animations.length;b++)this.animations[b].resetBlendWeights();
-for(b=0;b<this.animations.length;b++)this.animations[b].update(a)}};THREE.Animation=function(a,b){this.root=a;this.data=THREE.AnimationHandler.init(b);this.hierarchy=THREE.AnimationHandler.parse(a);this.currentTime=0;this.timeScale=1;this.isPlaying=!1;this.loop=!0;this.weight=0;this.interpolationType=THREE.AnimationHandler.LINEAR};THREE.Animation.prototype.keyTypes=["pos","rot","scl"];
-THREE.Animation.prototype.play=function(a,b){this.currentTime=void 0!==a?a:0;this.weight=void 0!==b?b:1;this.isPlaying=!0;this.reset();THREE.AnimationHandler.play(this)};THREE.Animation.prototype.stop=function(){this.isPlaying=!1;THREE.AnimationHandler.stop(this)};
-THREE.Animation.prototype.reset=function(){for(var a=0,b=this.hierarchy.length;a<b;a++){var c=this.hierarchy[a];c.matrixAutoUpdate=!0;void 0===c.animationCache&&(c.animationCache={animations:{},blending:{positionWeight:0,quaternionWeight:0,scaleWeight:0}});void 0===c.animationCache.animations[this.data.name]&&(c.animationCache.animations[this.data.name]={},c.animationCache.animations[this.data.name].prevKey={pos:0,rot:0,scl:0},c.animationCache.animations[this.data.name].nextKey={pos:0,rot:0,scl:0},
-c.animationCache.animations[this.data.name].originalMatrix=c.matrix);for(var c=c.animationCache.animations[this.data.name],d=0;3>d;d++){for(var e=this.keyTypes[d],f=this.data.hierarchy[a].keys[0],g=this.getNextKeyWith(e,a,1);g.time<this.currentTime&&g.index>f.index;)f=g,g=this.getNextKeyWith(e,a,g.index+1);c.prevKey[e]=f;c.nextKey[e]=g}}};
-THREE.Animation.prototype.resetBlendWeights=function(){for(var a=0,b=this.hierarchy.length;a<b;a++){var c=this.hierarchy[a];void 0!==c.animationCache&&(c.animationCache.blending.positionWeight=0,c.animationCache.blending.quaternionWeight=0,c.animationCache.blending.scaleWeight=0)}};
-THREE.Animation.prototype.update=function(){var a=[],b=new THREE.Vector3,c=new THREE.Vector3,d=new THREE.Quaternion,e=function(a,b){var c=[],d=[],e,q,m,r,t,s;e=(a.length-1)*b;q=Math.floor(e);e-=q;c[0]=0===q?q:q-1;c[1]=q;c[2]=q>a.length-2?q:q+1;c[3]=q>a.length-3?q:q+2;q=a[c[0]];r=a[c[1]];t=a[c[2]];s=a[c[3]];c=e*e;m=e*c;d[0]=f(q[0],r[0],t[0],s[0],e,c,m);d[1]=f(q[1],r[1],t[1],s[1],e,c,m);d[2]=f(q[2],r[2],t[2],s[2],e,c,m);return d},f=function(a,b,c,d,e,f,m){a=.5*(c-a);d=.5*(d-b);return(2*(b-c)+a+d)*m+
-(-3*(b-c)-2*a-d)*f+a*e+b};return function(f){if(!1!==this.isPlaying&&(this.currentTime+=f*this.timeScale,0!==this.weight)){f=this.data.length;if(this.currentTime>f||0>this.currentTime)if(this.loop)this.currentTime%=f,0>this.currentTime&&(this.currentTime+=f),this.reset();else{this.stop();return}f=0;for(var h=this.hierarchy.length;f<h;f++)for(var k=this.hierarchy[f],n=k.animationCache.animations[this.data.name],p=k.animationCache.blending,q=0;3>q;q++){var m=this.keyTypes[q],r=n.prevKey[m],t=n.nextKey[m];
-if(0<this.timeScale&&t.time<=this.currentTime||0>this.timeScale&&r.time>=this.currentTime){r=this.data.hierarchy[f].keys[0];for(t=this.getNextKeyWith(m,f,1);t.time<this.currentTime&&t.index>r.index;)r=t,t=this.getNextKeyWith(m,f,t.index+1);n.prevKey[m]=r;n.nextKey[m]=t}k.matrixAutoUpdate=!0;k.matrixWorldNeedsUpdate=!0;var s=(this.currentTime-r.time)/(t.time-r.time),u=r[m],v=t[m];0>s&&(s=0);1<s&&(s=1);if("pos"===m)if(this.interpolationType===THREE.AnimationHandler.LINEAR)c.x=u[0]+(v[0]-u[0])*s,c.y=
-u[1]+(v[1]-u[1])*s,c.z=u[2]+(v[2]-u[2])*s,r=this.weight/(this.weight+p.positionWeight),k.position.lerp(c,r),p.positionWeight+=this.weight;else{if(this.interpolationType===THREE.AnimationHandler.CATMULLROM||this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD)a[0]=this.getPrevKeyWith("pos",f,r.index-1).pos,a[1]=u,a[2]=v,a[3]=this.getNextKeyWith("pos",f,t.index+1).pos,s=.33*s+.33,t=e(a,s),r=this.weight/(this.weight+p.positionWeight),p.positionWeight+=this.weight,m=k.position,m.x+=(t[0]-
-m.x)*r,m.y+=(t[1]-m.y)*r,m.z+=(t[2]-m.z)*r,this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD&&(s=e(a,1.01*s),b.set(s[0],s[1],s[2]),b.sub(m),b.y=0,b.normalize(),s=Math.atan2(b.x,b.z),k.rotation.set(0,s,0))}else"rot"===m?(THREE.Quaternion.slerp(u,v,d,s),0===p.quaternionWeight?(k.quaternion.copy(d),p.quaternionWeight=this.weight):(r=this.weight/(this.weight+p.quaternionWeight),THREE.Quaternion.slerp(k.quaternion,d,k.quaternion,r),p.quaternionWeight+=this.weight)):"scl"===m&&(c.x=u[0]+
-(v[0]-u[0])*s,c.y=u[1]+(v[1]-u[1])*s,c.z=u[2]+(v[2]-u[2])*s,r=this.weight/(this.weight+p.scaleWeight),k.scale.lerp(c,r),p.scaleWeight+=this.weight)}return!0}}}();THREE.Animation.prototype.getNextKeyWith=function(a,b,c){var d=this.data.hierarchy[b].keys;for(c=this.interpolationType===THREE.AnimationHandler.CATMULLROM||this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD?c<d.length-1?c:d.length-1:c%d.length;c<d.length;c++)if(void 0!==d[c][a])return d[c];return this.data.hierarchy[b].keys[0]};
-THREE.Animation.prototype.getPrevKeyWith=function(a,b,c){var d=this.data.hierarchy[b].keys;for(c=this.interpolationType===THREE.AnimationHandler.CATMULLROM||this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD?0<c?c:0:0<=c?c:c+d.length;0<=c;c--)if(void 0!==d[c][a])return d[c];return this.data.hierarchy[b].keys[d.length-1]};
-THREE.KeyFrameAnimation=function(a){this.root=a.node;this.data=THREE.AnimationHandler.init(a);this.hierarchy=THREE.AnimationHandler.parse(this.root);this.currentTime=0;this.timeScale=.001;this.isPlaying=!1;this.loop=this.isPaused=!0;a=0;for(var b=this.hierarchy.length;a<b;a++){var c=this.data.hierarchy[a].sids,d=this.hierarchy[a];if(this.data.hierarchy[a].keys.length&&c){for(var e=0;e<c.length;e++){var f=c[e],g=this.getNextKeyWith(f,a,0);g&&g.apply(f)}d.matrixAutoUpdate=!1;this.data.hierarchy[a].node.updateMatrix();
-d.matrixWorldNeedsUpdate=!0}}};
-THREE.KeyFrameAnimation.prototype.play=function(a){this.currentTime=void 0!==a?a:0;if(!1===this.isPlaying){this.isPlaying=!0;var b=this.hierarchy.length,c,d;for(a=0;a<b;a++)c=this.hierarchy[a],d=this.data.hierarchy[a],void 0===d.animationCache&&(d.animationCache={},d.animationCache.prevKey=null,d.animationCache.nextKey=null,d.animationCache.originalMatrix=c.matrix),c=this.data.hierarchy[a].keys,c.length&&(d.animationCache.prevKey=c[0],d.animationCache.nextKey=c[1],this.startTime=Math.min(c[0].time,
-this.startTime),this.endTime=Math.max(c[c.length-1].time,this.endTime));this.update(0)}this.isPaused=!1;THREE.AnimationHandler.play(this)};THREE.KeyFrameAnimation.prototype.stop=function(){this.isPaused=this.isPlaying=!1;THREE.AnimationHandler.stop(this);for(var a=0;a<this.data.hierarchy.length;a++){var b=this.hierarchy[a],c=this.data.hierarchy[a];if(void 0!==c.animationCache){var d=c.animationCache.originalMatrix;d.copy(b.matrix);b.matrix=d;delete c.animationCache}}};
-THREE.KeyFrameAnimation.prototype.update=function(a){if(!1!==this.isPlaying){this.currentTime+=a*this.timeScale;a=this.data.length;!0===this.loop&&this.currentTime>a&&(this.currentTime%=a);this.currentTime=Math.min(this.currentTime,a);a=0;for(var b=this.hierarchy.length;a<b;a++){var c=this.hierarchy[a],d=this.data.hierarchy[a],e=d.keys,d=d.animationCache;if(e.length){var f=d.prevKey,g=d.nextKey;if(g.time<=this.currentTime){for(;g.time<this.currentTime&&g.index>f.index;)f=g,g=e[f.index+1];d.prevKey=
-f;d.nextKey=g}g.time>=this.currentTime?f.interpolate(g,this.currentTime):f.interpolate(g,g.time);this.data.hierarchy[a].node.updateMatrix();c.matrixWorldNeedsUpdate=!0}}}};THREE.KeyFrameAnimation.prototype.getNextKeyWith=function(a,b,c){b=this.data.hierarchy[b].keys;for(c%=b.length;c<b.length;c++)if(b[c].hasTarget(a))return b[c];return b[0]};
-THREE.KeyFrameAnimation.prototype.getPrevKeyWith=function(a,b,c){b=this.data.hierarchy[b].keys;for(c=0<=c?c:c+b.length;0<=c;c--)if(b[c].hasTarget(a))return b[c];return b[b.length-1]};THREE.MorphAnimation=function(a){this.mesh=a;this.frames=a.morphTargetInfluences.length;this.currentTime=0;this.duration=1E3;this.loop=!0;this.isPlaying=!1};
-THREE.MorphAnimation.prototype={play:function(){this.isPlaying=!0},pause:function(){this.isPlaying=!1},update:function(){var a=0,b=0;return function(c){if(!1!==this.isPlaying){this.currentTime+=c;!0===this.loop&&this.currentTime>this.duration&&(this.currentTime%=this.duration);this.currentTime=Math.min(this.currentTime,this.duration);c=this.duration/this.frames;var d=Math.floor(this.currentTime/c);d!=b&&(this.mesh.morphTargetInfluences[a]=0,this.mesh.morphTargetInfluences[b]=1,this.mesh.morphTargetInfluences[d]=
-0,a=b,b=d);this.mesh.morphTargetInfluences[d]=this.currentTime%c/c;this.mesh.morphTargetInfluences[a]=1-this.mesh.morphTargetInfluences[d]}}}()};
-THREE.BoxGeometry=function(a,b,c,d,e,f){function g(a,b,c,d,e,f,g,s){var u,v=h.widthSegments,y=h.heightSegments,G=e/2,w=f/2,K=h.vertices.length;if("x"===a&&"y"===b||"y"===a&&"x"===b)u="z";else if("x"===a&&"z"===b||"z"===a&&"x"===b)u="y",y=h.depthSegments;else if("z"===a&&"y"===b||"y"===a&&"z"===b)u="x",v=h.depthSegments;var x=v+1,D=y+1,E=e/v,A=f/y,B=new THREE.Vector3;B[u]=0<g?1:-1;for(e=0;e<D;e++)for(f=0;f<x;f++){var F=new THREE.Vector3;F[a]=(f*E-G)*c;F[b]=(e*A-w)*d;F[u]=g;h.vertices.push(F)}for(e=
-0;e<y;e++)for(f=0;f<v;f++)w=f+x*e,a=f+x*(e+1),b=f+1+x*(e+1),c=f+1+x*e,d=new THREE.Vector2(f/v,1-e/y),g=new THREE.Vector2(f/v,1-(e+1)/y),u=new THREE.Vector2((f+1)/v,1-(e+1)/y),G=new THREE.Vector2((f+1)/v,1-e/y),w=new THREE.Face3(w+K,a+K,c+K),w.normal.copy(B),w.vertexNormals.push(B.clone(),B.clone(),B.clone()),w.materialIndex=s,h.faces.push(w),h.faceVertexUvs[0].push([d,g,G]),w=new THREE.Face3(a+K,b+K,c+K),w.normal.copy(B),w.vertexNormals.push(B.clone(),B.clone(),B.clone()),w.materialIndex=s,h.faces.push(w),
-h.faceVertexUvs[0].push([g.clone(),u,G.clone()])}THREE.Geometry.call(this);this.type="BoxGeometry";this.parameters={width:a,height:b,depth:c,widthSegments:d,heightSegments:e,depthSegments:f};this.widthSegments=d||1;this.heightSegments=e||1;this.depthSegments=f||1;var h=this;d=a/2;e=b/2;f=c/2;g("z","y",-1,-1,c,b,d,0);g("z","y",1,-1,c,b,-d,1);g("x","z",1,1,a,c,e,2);g("x","z",1,-1,a,c,-e,3);g("x","y",1,-1,a,b,f,4);g("x","y",-1,-1,a,b,-f,5);this.mergeVertices()};THREE.BoxGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.CircleGeometry=function(a,b,c,d){THREE.Geometry.call(this);this.type="CircleGeometry";this.parameters={radius:a,segments:b,thetaStart:c,thetaLength:d};a=a||50;b=void 0!==b?Math.max(3,b):8;c=void 0!==c?c:0;d=void 0!==d?d:2*Math.PI;var e,f=[];e=new THREE.Vector3;var g=new THREE.Vector2(.5,.5);this.vertices.push(e);f.push(g);for(e=0;e<=b;e++){var h=new THREE.Vector3,k=c+e/b*d;h.x=a*Math.cos(k);h.y=a*Math.sin(k);this.vertices.push(h);f.push(new THREE.Vector2((h.x/a+1)/2,(h.y/a+1)/2))}c=new THREE.Vector3(0,
-0,1);for(e=1;e<=b;e++)this.faces.push(new THREE.Face3(e,e+1,0,[c.clone(),c.clone(),c.clone()])),this.faceVertexUvs[0].push([f[e].clone(),f[e+1].clone(),g.clone()]);this.computeFaceNormals();this.boundingSphere=new THREE.Sphere(new THREE.Vector3,a)};THREE.CircleGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.CubeGeometry=function(a,b,c,d,e,f){console.warn("THREE.CubeGeometry has been renamed to THREE.BoxGeometry.");return new THREE.BoxGeometry(a,b,c,d,e,f)};
-THREE.CylinderGeometry=function(a,b,c,d,e,f){THREE.Geometry.call(this);this.type="CylinderGeometry";this.parameters={radiusTop:a,radiusBottom:b,height:c,radialSegments:d,heightSegments:e,openEnded:f};a=void 0!==a?a:20;b=void 0!==b?b:20;c=void 0!==c?c:100;d=d||8;e=e||1;f=void 0!==f?f:!1;var g=c/2,h,k,n=[],p=[];for(k=0;k<=e;k++){var q=[],m=[],r=k/e,t=r*(b-a)+a;for(h=0;h<=d;h++){var s=h/d,u=new THREE.Vector3;u.x=t*Math.sin(s*Math.PI*2);u.y=-r*c+g;u.z=t*Math.cos(s*Math.PI*2);this.vertices.push(u);q.push(this.vertices.length-
-1);m.push(new THREE.Vector2(s,1-r))}n.push(q);p.push(m)}c=(b-a)/c;for(h=0;h<d;h++)for(0!==a?(q=this.vertices[n[0][h]].clone(),m=this.vertices[n[0][h+1]].clone()):(q=this.vertices[n[1][h]].clone(),m=this.vertices[n[1][h+1]].clone()),q.setY(Math.sqrt(q.x*q.x+q.z*q.z)*c).normalize(),m.setY(Math.sqrt(m.x*m.x+m.z*m.z)*c).normalize(),k=0;k<e;k++){var r=n[k][h],t=n[k+1][h],s=n[k+1][h+1],u=n[k][h+1],v=q.clone(),y=q.clone(),G=m.clone(),w=m.clone(),K=p[k][h].clone(),x=p[k+1][h].clone(),D=p[k+1][h+1].clone(),
-E=p[k][h+1].clone();this.faces.push(new THREE.Face3(r,t,u,[v,y,w]));this.faceVertexUvs[0].push([K,x,E]);this.faces.push(new THREE.Face3(t,s,u,[y.clone(),G,w.clone()]));this.faceVertexUvs[0].push([x.clone(),D,E.clone()])}if(!1===f&&0<a)for(this.vertices.push(new THREE.Vector3(0,g,0)),h=0;h<d;h++)r=n[0][h],t=n[0][h+1],s=this.vertices.length-1,v=new THREE.Vector3(0,1,0),y=new THREE.Vector3(0,1,0),G=new THREE.Vector3(0,1,0),K=p[0][h].clone(),x=p[0][h+1].clone(),D=new THREE.Vector2(x.x,0),this.faces.push(new THREE.Face3(r,
-t,s,[v,y,G])),this.faceVertexUvs[0].push([K,x,D]);if(!1===f&&0<b)for(this.vertices.push(new THREE.Vector3(0,-g,0)),h=0;h<d;h++)r=n[k][h+1],t=n[k][h],s=this.vertices.length-1,v=new THREE.Vector3(0,-1,0),y=new THREE.Vector3(0,-1,0),G=new THREE.Vector3(0,-1,0),K=p[k][h+1].clone(),x=p[k][h].clone(),D=new THREE.Vector2(x.x,1),this.faces.push(new THREE.Face3(r,t,s,[v,y,G])),this.faceVertexUvs[0].push([K,x,D]);this.computeFaceNormals()};THREE.CylinderGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.ExtrudeGeometry=function(a,b){"undefined"!==typeof a&&(THREE.Geometry.call(this),this.type="ExtrudeGeometry",a=a instanceof Array?a:[a],this.addShapeList(a,b),this.computeFaceNormals())};THREE.ExtrudeGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.ExtrudeGeometry.prototype.addShapeList=function(a,b){for(var c=a.length,d=0;d<c;d++)this.addShape(a[d],b)};
-THREE.ExtrudeGeometry.prototype.addShape=function(a,b){function c(a,b,c){b||console.log("die");return b.clone().multiplyScalar(c).add(a)}function d(a,b,c){var d=1,d=a.x-b.x,e=a.y-b.y,f=c.x-a.x,g=c.y-a.y,h=d*d+e*e;if(1E-10<Math.abs(d*g-e*f)){var k=Math.sqrt(h),m=Math.sqrt(f*f+g*g),h=b.x-e/k;b=b.y+d/k;f=((c.x-g/m-h)*g-(c.y+f/m-b)*f)/(d*g-e*f);c=h+d*f-a.x;a=b+e*f-a.y;d=c*c+a*a;if(2>=d)return new THREE.Vector2(c,a);d=Math.sqrt(d/2)}else a=!1,1E-10<d?1E-10<f&&(a=!0):-1E-10>d?-1E-10>f&&(a=!0):Math.sign(e)==
-Math.sign(g)&&(a=!0),a?(c=-e,a=d,d=Math.sqrt(h)):(c=d,a=e,d=Math.sqrt(h/2));return new THREE.Vector2(c/d,a/d)}function e(a,b){var c,d;for(P=a.length;0<=--P;){c=P;d=P-1;0>d&&(d=a.length-1);for(var e=0,f=r+2*p,e=0;e<f;e++){var g=la*e,h=la*(e+1),k=b+c+g,g=b+d+g,m=b+d+h,h=b+c+h,k=k+R,g=g+R,m=m+R,h=h+R;F.faces.push(new THREE.Face3(k,g,h,null,null,y));F.faces.push(new THREE.Face3(g,m,h,null,null,y));k=G.generateSideWallUV(F,k,g,m,h);F.faceVertexUvs[0].push([k[0],k[1],k[3]]);F.faceVertexUvs[0].push([k[1],
-k[2],k[3]])}}}function f(a,b,c){F.vertices.push(new THREE.Vector3(a,b,c))}function g(a,b,c){a+=R;b+=R;c+=R;F.faces.push(new THREE.Face3(a,b,c,null,null,v));a=G.generateTopUV(F,a,b,c);F.faceVertexUvs[0].push(a)}var h=void 0!==b.amount?b.amount:100,k=void 0!==b.bevelThickness?b.bevelThickness:6,n=void 0!==b.bevelSize?b.bevelSize:k-2,p=void 0!==b.bevelSegments?b.bevelSegments:3,q=void 0!==b.bevelEnabled?b.bevelEnabled:!0,m=void 0!==b.curveSegments?b.curveSegments:12,r=void 0!==b.steps?b.steps:1,t=b.extrudePath,
-s,u=!1,v=b.material,y=b.extrudeMaterial,G=void 0!==b.UVGenerator?b.UVGenerator:THREE.ExtrudeGeometry.WorldUVGenerator,w,K,x,D;t&&(s=t.getSpacedPoints(r),u=!0,q=!1,w=void 0!==b.frames?b.frames:new THREE.TubeGeometry.FrenetFrames(t,r,!1),K=new THREE.Vector3,x=new THREE.Vector3,D=new THREE.Vector3);q||(n=k=p=0);var E,A,B,F=this,R=this.vertices.length,t=a.extractPoints(m),m=t.shape,H=t.holes;if(t=!THREE.Shape.Utils.isClockWise(m)){m=m.reverse();A=0;for(B=H.length;A<B;A++)E=H[A],THREE.Shape.Utils.isClockWise(E)&&
-(H[A]=E.reverse());t=!1}var C=THREE.Shape.Utils.triangulateShape(m,H),T=m;A=0;for(B=H.length;A<B;A++)E=H[A],m=m.concat(E);var Q,O,S,X,Y,la=m.length,ma,ya=C.length,t=[],P=0;S=T.length;Q=S-1;for(O=P+1;P<S;P++,Q++,O++)Q===S&&(Q=0),O===S&&(O=0),t[P]=d(T[P],T[Q],T[O]);var Ga=[],Fa,za=t.concat();A=0;for(B=H.length;A<B;A++){E=H[A];Fa=[];P=0;S=E.length;Q=S-1;for(O=P+1;P<S;P++,Q++,O++)Q===S&&(Q=0),O===S&&(O=0),Fa[P]=d(E[P],E[Q],E[O]);Ga.push(Fa);za=za.concat(Fa)}for(Q=0;Q<p;Q++){S=Q/p;X=k*(1-S);O=n*Math.sin(S*
-Math.PI/2);P=0;for(S=T.length;P<S;P++)Y=c(T[P],t[P],O),f(Y.x,Y.y,-X);A=0;for(B=H.length;A<B;A++)for(E=H[A],Fa=Ga[A],P=0,S=E.length;P<S;P++)Y=c(E[P],Fa[P],O),f(Y.x,Y.y,-X)}O=n;for(P=0;P<la;P++)Y=q?c(m[P],za[P],O):m[P],u?(x.copy(w.normals[0]).multiplyScalar(Y.x),K.copy(w.binormals[0]).multiplyScalar(Y.y),D.copy(s[0]).add(x).add(K),f(D.x,D.y,D.z)):f(Y.x,Y.y,0);for(S=1;S<=r;S++)for(P=0;P<la;P++)Y=q?c(m[P],za[P],O):m[P],u?(x.copy(w.normals[S]).multiplyScalar(Y.x),K.copy(w.binormals[S]).multiplyScalar(Y.y),
-D.copy(s[S]).add(x).add(K),f(D.x,D.y,D.z)):f(Y.x,Y.y,h/r*S);for(Q=p-1;0<=Q;Q--){S=Q/p;X=k*(1-S);O=n*Math.sin(S*Math.PI/2);P=0;for(S=T.length;P<S;P++)Y=c(T[P],t[P],O),f(Y.x,Y.y,h+X);A=0;for(B=H.length;A<B;A++)for(E=H[A],Fa=Ga[A],P=0,S=E.length;P<S;P++)Y=c(E[P],Fa[P],O),u?f(Y.x,Y.y+s[r-1].y,s[r-1].x+X):f(Y.x,Y.y,h+X)}(function(){if(q){var a;a=0*la;for(P=0;P<ya;P++)ma=C[P],g(ma[2]+a,ma[1]+a,ma[0]+a);a=r+2*p;a*=la;for(P=0;P<ya;P++)ma=C[P],g(ma[0]+a,ma[1]+a,ma[2]+a)}else{for(P=0;P<ya;P++)ma=C[P],g(ma[2],
-ma[1],ma[0]);for(P=0;P<ya;P++)ma=C[P],g(ma[0]+la*r,ma[1]+la*r,ma[2]+la*r)}})();(function(){var a=0;e(T,a);a+=T.length;A=0;for(B=H.length;A<B;A++)E=H[A],e(E,a),a+=E.length})()};
-THREE.ExtrudeGeometry.WorldUVGenerator={generateTopUV:function(a,b,c,d){a=a.vertices;b=a[b];c=a[c];d=a[d];return[new THREE.Vector2(b.x,b.y),new THREE.Vector2(c.x,c.y),new THREE.Vector2(d.x,d.y)]},generateSideWallUV:function(a,b,c,d,e){a=a.vertices;b=a[b];c=a[c];d=a[d];e=a[e];return.01>Math.abs(b.y-c.y)?[new THREE.Vector2(b.x,1-b.z),new THREE.Vector2(c.x,1-c.z),new THREE.Vector2(d.x,1-d.z),new THREE.Vector2(e.x,1-e.z)]:[new THREE.Vector2(b.y,1-b.z),new THREE.Vector2(c.y,1-c.z),new THREE.Vector2(d.y,
-1-d.z),new THREE.Vector2(e.y,1-e.z)]}};THREE.ShapeGeometry=function(a,b){THREE.Geometry.call(this);this.type="ShapeGeometry";!1===a instanceof Array&&(a=[a]);this.addShapeList(a,b);this.computeFaceNormals()};THREE.ShapeGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.ShapeGeometry.prototype.addShapeList=function(a,b){for(var c=0,d=a.length;c<d;c++)this.addShape(a[c],b);return this};
-THREE.ShapeGeometry.prototype.addShape=function(a,b){void 0===b&&(b={});var c=b.material,d=void 0===b.UVGenerator?THREE.ExtrudeGeometry.WorldUVGenerator:b.UVGenerator,e,f,g,h=this.vertices.length;e=a.extractPoints(void 0!==b.curveSegments?b.curveSegments:12);var k=e.shape,n=e.holes;if(!THREE.Shape.Utils.isClockWise(k))for(k=k.reverse(),e=0,f=n.length;e<f;e++)g=n[e],THREE.Shape.Utils.isClockWise(g)&&(n[e]=g.reverse());var p=THREE.Shape.Utils.triangulateShape(k,n);e=0;for(f=n.length;e<f;e++)g=n[e],
-k=k.concat(g);n=k.length;f=p.length;for(e=0;e<n;e++)g=k[e],this.vertices.push(new THREE.Vector3(g.x,g.y,0));for(e=0;e<f;e++)n=p[e],k=n[0]+h,g=n[1]+h,n=n[2]+h,this.faces.push(new THREE.Face3(k,g,n,null,null,c)),this.faceVertexUvs[0].push(d.generateTopUV(this,k,g,n))};
-THREE.LatheGeometry=function(a,b,c,d){THREE.Geometry.call(this);this.type="LatheGeometry";this.parameters={points:a,segments:b,phiStart:c,phiLength:d};b=b||12;c=c||0;d=d||2*Math.PI;for(var e=1/(a.length-1),f=1/b,g=0,h=b;g<=h;g++)for(var k=c+g*f*d,n=Math.cos(k),p=Math.sin(k),k=0,q=a.length;k<q;k++){var m=a[k],r=new THREE.Vector3;r.x=n*m.x-p*m.y;r.y=p*m.x+n*m.y;r.z=m.z;this.vertices.push(r)}c=a.length;g=0;for(h=b;g<h;g++)for(k=0,q=a.length-1;k<q;k++){b=p=k+c*g;d=p+c;var n=p+1+c,p=p+1,m=g*f,r=k*e,t=
-m+f,s=r+e;this.faces.push(new THREE.Face3(b,d,p));this.faceVertexUvs[0].push([new THREE.Vector2(m,r),new THREE.Vector2(t,r),new THREE.Vector2(m,s)]);this.faces.push(new THREE.Face3(d,n,p));this.faceVertexUvs[0].push([new THREE.Vector2(t,r),new THREE.Vector2(t,s),new THREE.Vector2(m,s)])}this.mergeVertices();this.computeFaceNormals();this.computeVertexNormals()};THREE.LatheGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.PlaneGeometry=function(a,b,c,d){console.info("THREE.PlaneGeometry: Consider using THREE.PlaneBufferGeometry for lower memory footprint.");THREE.Geometry.call(this);this.type="PlaneGeometry";this.parameters={width:a,height:b,widthSegments:c,heightSegments:d};this.fromBufferGeometry(new THREE.PlaneBufferGeometry(a,b,c,d))};THREE.PlaneGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.PlaneBufferGeometry=function(a,b,c,d){THREE.BufferGeometry.call(this);this.type="PlaneBufferGeometry";this.parameters={width:a,height:b,widthSegments:c,heightSegments:d};var e=a/2,f=b/2;c=c||1;d=d||1;var g=c+1,h=d+1,k=a/c,n=b/d;b=new Float32Array(g*h*3);a=new Float32Array(g*h*3);for(var p=new Float32Array(g*h*2),q=0,m=0,r=0;r<h;r++)for(var t=r*n-f,s=0;s<g;s++)b[q]=s*k-e,b[q+1]=-t,a[q+2]=1,p[m]=s/c,p[m+1]=1-r/d,q+=3,m+=2;q=0;e=new (65535<b.length/3?Uint32Array:Uint16Array)(c*d*6);for(r=0;r<d;r++)for(s=
-0;s<c;s++)f=s+g*(r+1),h=s+1+g*(r+1),k=s+1+g*r,e[q]=s+g*r,e[q+1]=f,e[q+2]=k,e[q+3]=f,e[q+4]=h,e[q+5]=k,q+=6;this.addAttribute("index",new THREE.BufferAttribute(e,1));this.addAttribute("position",new THREE.BufferAttribute(b,3));this.addAttribute("normal",new THREE.BufferAttribute(a,3));this.addAttribute("uv",new THREE.BufferAttribute(p,2))};THREE.PlaneBufferGeometry.prototype=Object.create(THREE.BufferGeometry.prototype);
-THREE.RingGeometry=function(a,b,c,d,e,f){THREE.Geometry.call(this);this.type="RingGeometry";this.parameters={innerRadius:a,outerRadius:b,thetaSegments:c,phiSegments:d,thetaStart:e,thetaLength:f};a=a||0;b=b||50;e=void 0!==e?e:0;f=void 0!==f?f:2*Math.PI;c=void 0!==c?Math.max(3,c):8;d=void 0!==d?Math.max(1,d):8;var g,h=[],k=a,n=(b-a)/d;for(a=0;a<d+1;a++){for(g=0;g<c+1;g++){var p=new THREE.Vector3,q=e+g/c*f;p.x=k*Math.cos(q);p.y=k*Math.sin(q);this.vertices.push(p);h.push(new THREE.Vector2((p.x/b+1)/2,
-(p.y/b+1)/2))}k+=n}b=new THREE.Vector3(0,0,1);for(a=0;a<d;a++)for(e=a*(c+1),g=0;g<c;g++)f=q=g+e,n=q+c+1,p=q+c+2,this.faces.push(new THREE.Face3(f,n,p,[b.clone(),b.clone(),b.clone()])),this.faceVertexUvs[0].push([h[f].clone(),h[n].clone(),h[p].clone()]),f=q,n=q+c+2,p=q+1,this.faces.push(new THREE.Face3(f,n,p,[b.clone(),b.clone(),b.clone()])),this.faceVertexUvs[0].push([h[f].clone(),h[n].clone(),h[p].clone()]);this.computeFaceNormals();this.boundingSphere=new THREE.Sphere(new THREE.Vector3,k)};
-THREE.RingGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.SphereGeometry=function(a,b,c,d,e,f,g){THREE.Geometry.call(this);this.type="SphereGeometry";this.parameters={radius:a,widthSegments:b,heightSegments:c,phiStart:d,phiLength:e,thetaStart:f,thetaLength:g};a=a||50;b=Math.max(3,Math.floor(b)||8);c=Math.max(2,Math.floor(c)||6);d=void 0!==d?d:0;e=void 0!==e?e:2*Math.PI;f=void 0!==f?f:0;g=void 0!==g?g:Math.PI;var h,k,n=[],p=[];for(k=0;k<=c;k++){var q=[],m=[];for(h=0;h<=b;h++){var r=h/b,t=k/c,s=new THREE.Vector3;s.x=-a*Math.cos(d+r*e)*Math.sin(f+t*g);
-s.y=a*Math.cos(f+t*g);s.z=a*Math.sin(d+r*e)*Math.sin(f+t*g);this.vertices.push(s);q.push(this.vertices.length-1);m.push(new THREE.Vector2(r,1-t))}n.push(q);p.push(m)}for(k=0;k<c;k++)for(h=0;h<b;h++){d=n[k][h+1];e=n[k][h];f=n[k+1][h];g=n[k+1][h+1];var q=this.vertices[d].clone().normalize(),m=this.vertices[e].clone().normalize(),r=this.vertices[f].clone().normalize(),t=this.vertices[g].clone().normalize(),s=p[k][h+1].clone(),u=p[k][h].clone(),v=p[k+1][h].clone(),y=p[k+1][h+1].clone();Math.abs(this.vertices[d].y)===
-a?(s.x=(s.x+u.x)/2,this.faces.push(new THREE.Face3(d,f,g,[q,r,t])),this.faceVertexUvs[0].push([s,v,y])):Math.abs(this.vertices[f].y)===a?(v.x=(v.x+y.x)/2,this.faces.push(new THREE.Face3(d,e,f,[q,m,r])),this.faceVertexUvs[0].push([s,u,v])):(this.faces.push(new THREE.Face3(d,e,g,[q,m,t])),this.faceVertexUvs[0].push([s,u,y]),this.faces.push(new THREE.Face3(e,f,g,[m.clone(),r,t.clone()])),this.faceVertexUvs[0].push([u.clone(),v,y.clone()]))}this.computeFaceNormals();this.boundingSphere=new THREE.Sphere(new THREE.Vector3,
-a)};THREE.SphereGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.TextGeometry=function(a,b){b=b||{};var c=THREE.FontUtils.generateShapes(a,b);b.amount=void 0!==b.height?b.height:50;void 0===b.bevelThickness&&(b.bevelThickness=10);void 0===b.bevelSize&&(b.bevelSize=8);void 0===b.bevelEnabled&&(b.bevelEnabled=!1);THREE.ExtrudeGeometry.call(this,c,b);this.type="TextGeometry"};THREE.TextGeometry.prototype=Object.create(THREE.ExtrudeGeometry.prototype);
-THREE.TorusGeometry=function(a,b,c,d,e){THREE.Geometry.call(this);this.type="TorusGeometry";this.parameters={radius:a,tube:b,radialSegments:c,tubularSegments:d,arc:e};a=a||100;b=b||40;c=c||8;d=d||6;e=e||2*Math.PI;for(var f=new THREE.Vector3,g=[],h=[],k=0;k<=c;k++)for(var n=0;n<=d;n++){var p=n/d*e,q=k/c*Math.PI*2;f.x=a*Math.cos(p);f.y=a*Math.sin(p);var m=new THREE.Vector3;m.x=(a+b*Math.cos(q))*Math.cos(p);m.y=(a+b*Math.cos(q))*Math.sin(p);m.z=b*Math.sin(q);this.vertices.push(m);g.push(new THREE.Vector2(n/
-d,k/c));h.push(m.clone().sub(f).normalize())}for(k=1;k<=c;k++)for(n=1;n<=d;n++)a=(d+1)*k+n-1,b=(d+1)*(k-1)+n-1,e=(d+1)*(k-1)+n,f=(d+1)*k+n,p=new THREE.Face3(a,b,f,[h[a].clone(),h[b].clone(),h[f].clone()]),this.faces.push(p),this.faceVertexUvs[0].push([g[a].clone(),g[b].clone(),g[f].clone()]),p=new THREE.Face3(b,e,f,[h[b].clone(),h[e].clone(),h[f].clone()]),this.faces.push(p),this.faceVertexUvs[0].push([g[b].clone(),g[e].clone(),g[f].clone()]);this.computeFaceNormals()};
-THREE.TorusGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.TorusKnotGeometry=function(a,b,c,d,e,f,g){function h(a,b,c,d,e){var f=Math.cos(a),g=Math.sin(a);a*=b/c;b=Math.cos(a);f*=d*(2+b)*.5;g=d*(2+b)*g*.5;d=e*d*Math.sin(a)*.5;return new THREE.Vector3(f,g,d)}THREE.Geometry.call(this);this.type="TorusKnotGeometry";this.parameters={radius:a,tube:b,radialSegments:c,tubularSegments:d,p:e,q:f,heightScale:g};a=a||100;b=b||40;c=c||64;d=d||8;e=e||2;f=f||3;g=g||1;for(var k=Array(c),n=new THREE.Vector3,p=new THREE.Vector3,q=new THREE.Vector3,m=0;m<c;++m){k[m]=
-Array(d);var r=m/c*2*e*Math.PI,t=h(r,f,e,a,g),r=h(r+.01,f,e,a,g);n.subVectors(r,t);p.addVectors(r,t);q.crossVectors(n,p);p.crossVectors(q,n);q.normalize();p.normalize();for(r=0;r<d;++r){var s=r/d*2*Math.PI,u=-b*Math.cos(s),s=b*Math.sin(s),v=new THREE.Vector3;v.x=t.x+u*p.x+s*q.x;v.y=t.y+u*p.y+s*q.y;v.z=t.z+u*p.z+s*q.z;k[m][r]=this.vertices.push(v)-1}}for(m=0;m<c;++m)for(r=0;r<d;++r)e=(m+1)%c,f=(r+1)%d,a=k[m][r],b=k[e][r],e=k[e][f],f=k[m][f],g=new THREE.Vector2(m/c,r/d),n=new THREE.Vector2((m+1)/c,
-r/d),p=new THREE.Vector2((m+1)/c,(r+1)/d),q=new THREE.Vector2(m/c,(r+1)/d),this.faces.push(new THREE.Face3(a,b,f)),this.faceVertexUvs[0].push([g,n,q]),this.faces.push(new THREE.Face3(b,e,f)),this.faceVertexUvs[0].push([n.clone(),p,q.clone()]);this.computeFaceNormals();this.computeVertexNormals()};THREE.TorusKnotGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.TubeGeometry=function(a,b,c,d,e){THREE.Geometry.call(this);this.type="TubeGeometry";this.parameters={path:a,segments:b,radius:c,radialSegments:d,closed:e};b=b||64;c=c||1;d=d||8;e=e||!1;var f=[],g,h,k=b+1,n,p,q,m,r=new THREE.Vector3,t,s,u;t=new THREE.TubeGeometry.FrenetFrames(a,b,e);s=t.normals;u=t.binormals;this.tangents=t.tangents;this.normals=s;this.binormals=u;for(t=0;t<k;t++)for(f[t]=[],n=t/(k-1),m=a.getPointAt(n),g=s[t],h=u[t],n=0;n<d;n++)p=n/d*2*Math.PI,q=-c*Math.cos(p),p=c*Math.sin(p),
-r.copy(m),r.x+=q*g.x+p*h.x,r.y+=q*g.y+p*h.y,r.z+=q*g.z+p*h.z,f[t][n]=this.vertices.push(new THREE.Vector3(r.x,r.y,r.z))-1;for(t=0;t<b;t++)for(n=0;n<d;n++)k=e?(t+1)%b:t+1,r=(n+1)%d,a=f[t][n],c=f[k][n],k=f[k][r],r=f[t][r],s=new THREE.Vector2(t/b,n/d),u=new THREE.Vector2((t+1)/b,n/d),g=new THREE.Vector2((t+1)/b,(n+1)/d),h=new THREE.Vector2(t/b,(n+1)/d),this.faces.push(new THREE.Face3(a,c,r)),this.faceVertexUvs[0].push([s,u,h]),this.faces.push(new THREE.Face3(c,k,r)),this.faceVertexUvs[0].push([u.clone(),
-g,h.clone()]);this.computeFaceNormals();this.computeVertexNormals()};THREE.TubeGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.TubeGeometry.FrenetFrames=function(a,b,c){new THREE.Vector3;var d=new THREE.Vector3;new THREE.Vector3;var e=[],f=[],g=[],h=new THREE.Vector3,k=new THREE.Matrix4;b+=1;var n,p,q;this.tangents=e;this.normals=f;this.binormals=g;for(n=0;n<b;n++)p=n/(b-1),e[n]=a.getTangentAt(p),e[n].normalize();f[0]=new THREE.Vector3;g[0]=new THREE.Vector3;a=Number.MAX_VALUE;n=Math.abs(e[0].x);p=Math.abs(e[0].y);q=Math.abs(e[0].z);n<=a&&(a=n,d.set(1,0,0));p<=a&&(a=p,d.set(0,1,0));q<=a&&d.set(0,0,1);h.crossVectors(e[0],
-d).normalize();f[0].crossVectors(e[0],h);g[0].crossVectors(e[0],f[0]);for(n=1;n<b;n++)f[n]=f[n-1].clone(),g[n]=g[n-1].clone(),h.crossVectors(e[n-1],e[n]),1E-4<h.length()&&(h.normalize(),d=Math.acos(THREE.Math.clamp(e[n-1].dot(e[n]),-1,1)),f[n].applyMatrix4(k.makeRotationAxis(h,d))),g[n].crossVectors(e[n],f[n]);if(c)for(d=Math.acos(THREE.Math.clamp(f[0].dot(f[b-1]),-1,1)),d/=b-1,0<e[0].dot(h.crossVectors(f[0],f[b-1]))&&(d=-d),n=1;n<b;n++)f[n].applyMatrix4(k.makeRotationAxis(e[n],d*n)),g[n].crossVectors(e[n],
-f[n])};
-THREE.PolyhedronGeometry=function(a,b,c,d){function e(a){var b=a.normalize().clone();b.index=k.vertices.push(b)-1;var c=Math.atan2(a.z,-a.x)/2/Math.PI+.5;a=Math.atan2(-a.y,Math.sqrt(a.x*a.x+a.z*a.z))/Math.PI+.5;b.uv=new THREE.Vector2(c,1-a);return b}function f(a,b,c){var d=new THREE.Face3(a.index,b.index,c.index,[a.clone(),b.clone(),c.clone()]);k.faces.push(d);u.copy(a).add(b).add(c).divideScalar(3);d=Math.atan2(u.z,-u.x);k.faceVertexUvs[0].push([h(a.uv,a,d),h(b.uv,b,d),h(c.uv,c,d)])}function g(a,b){var c=
-Math.pow(2,b);Math.pow(4,b);for(var d=e(k.vertices[a.a]),g=e(k.vertices[a.b]),h=e(k.vertices[a.c]),m=[],n=0;n<=c;n++){m[n]=[];for(var p=e(d.clone().lerp(h,n/c)),q=e(g.clone().lerp(h,n/c)),r=c-n,s=0;s<=r;s++)m[n][s]=0==s&&n==c?p:e(p.clone().lerp(q,s/r))}for(n=0;n<c;n++)for(s=0;s<2*(c-n)-1;s++)d=Math.floor(s/2),0==s%2?f(m[n][d+1],m[n+1][d],m[n][d]):f(m[n][d+1],m[n+1][d+1],m[n+1][d])}function h(a,b,c){0>c&&1===a.x&&(a=new THREE.Vector2(a.x-1,a.y));0===b.x&&0===b.z&&(a=new THREE.Vector2(c/2/Math.PI+.5,
-a.y));return a.clone()}THREE.Geometry.call(this);this.type="PolyhedronGeometry";this.parameters={vertices:a,indices:b,radius:c,detail:d};c=c||1;d=d||0;for(var k=this,n=0,p=a.length;n<p;n+=3)e(new THREE.Vector3(a[n],a[n+1],a[n+2]));a=this.vertices;for(var q=[],m=n=0,p=b.length;n<p;n+=3,m++){var r=a[b[n]],t=a[b[n+1]],s=a[b[n+2]];q[m]=new THREE.Face3(r.index,t.index,s.index,[r.clone(),t.clone(),s.clone()])}for(var u=new THREE.Vector3,n=0,p=q.length;n<p;n++)g(q[n],d);n=0;for(p=this.faceVertexUvs[0].length;n<
-p;n++)b=this.faceVertexUvs[0][n],d=b[0].x,a=b[1].x,q=b[2].x,m=Math.max(d,Math.max(a,q)),r=Math.min(d,Math.min(a,q)),.9<m&&.1>r&&(.2>d&&(b[0].x+=1),.2>a&&(b[1].x+=1),.2>q&&(b[2].x+=1));n=0;for(p=this.vertices.length;n<p;n++)this.vertices[n].multiplyScalar(c);this.mergeVertices();this.computeFaceNormals();this.boundingSphere=new THREE.Sphere(new THREE.Vector3,c)};THREE.PolyhedronGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.DodecahedronGeometry=function(a,b){this.parameters={radius:a,detail:b};var c=(1+Math.sqrt(5))/2,d=1/c;THREE.PolyhedronGeometry.call(this,[-1,-1,-1,-1,-1,1,-1,1,-1,-1,1,1,1,-1,-1,1,-1,1,1,1,-1,1,1,1,0,-d,-c,0,-d,c,0,d,-c,0,d,c,-d,-c,0,-d,c,0,d,-c,0,d,c,0,-c,0,-d,c,0,-d,-c,0,d,c,0,d],[3,11,7,3,7,15,3,15,13,7,19,17,7,17,6,7,6,15,17,4,8,17,8,10,17,10,6,8,0,16,8,16,2,8,2,10,0,12,1,0,1,18,0,18,16,6,10,2,6,2,13,6,13,15,2,16,18,2,18,3,2,3,13,18,1,9,18,9,11,18,11,3,4,14,12,4,12,0,4,0,8,11,9,5,11,5,19,
-11,19,7,19,5,14,19,14,4,19,4,17,1,12,14,1,14,5,1,5,9],a,b)};THREE.DodecahedronGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.IcosahedronGeometry=function(a,b){var c=(1+Math.sqrt(5))/2;THREE.PolyhedronGeometry.call(this,[-1,c,0,1,c,0,-1,-c,0,1,-c,0,0,-1,c,0,1,c,0,-1,-c,0,1,-c,c,0,-1,c,0,1,-c,0,-1,-c,0,1],[0,11,5,0,5,1,0,1,7,0,7,10,0,10,11,1,5,9,5,11,4,11,10,2,10,7,6,7,1,8,3,9,4,3,4,2,3,2,6,3,6,8,3,8,9,4,9,5,2,4,11,6,2,10,8,6,7,9,8,1],a,b);this.type="IcosahedronGeometry";this.parameters={radius:a,detail:b}};THREE.IcosahedronGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.OctahedronGeometry=function(a,b){this.parameters={radius:a,detail:b};THREE.PolyhedronGeometry.call(this,[1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1],[0,2,4,0,4,3,0,3,5,0,5,2,1,2,5,1,5,3,1,3,4,1,4,2],a,b);this.type="OctahedronGeometry";this.parameters={radius:a,detail:b}};THREE.OctahedronGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.TetrahedronGeometry=function(a,b){THREE.PolyhedronGeometry.call(this,[1,1,1,-1,-1,1,-1,1,-1,1,-1,-1],[2,1,0,0,3,2,1,3,0,2,3,1],a,b);this.type="TetrahedronGeometry";this.parameters={radius:a,detail:b}};THREE.TetrahedronGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.ParametricGeometry=function(a,b,c){THREE.Geometry.call(this);this.type="ParametricGeometry";this.parameters={func:a,slices:b,stacks:c};var d=this.vertices,e=this.faces,f=this.faceVertexUvs[0],g,h,k,n,p=b+1;for(g=0;g<=c;g++)for(n=g/c,h=0;h<=b;h++)k=h/b,k=a(k,n),d.push(k);var q,m,r,t;for(g=0;g<c;g++)for(h=0;h<b;h++)a=g*p+h,d=g*p+h+1,n=(g+1)*p+h+1,k=(g+1)*p+h,q=new THREE.Vector2(h/b,g/c),m=new THREE.Vector2((h+1)/b,g/c),r=new THREE.Vector2((h+1)/b,(g+1)/c),t=new THREE.Vector2(h/b,(g+1)/c),e.push(new THREE.Face3(a,
-d,k)),f.push([q,m,t]),e.push(new THREE.Face3(d,n,k)),f.push([m.clone(),r,t.clone()]);this.computeFaceNormals();this.computeVertexNormals()};THREE.ParametricGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.AxisHelper=function(a){a=a||1;var b=new Float32Array([0,0,0,a,0,0,0,0,0,0,a,0,0,0,0,0,0,a]),c=new Float32Array([1,0,0,1,.6,0,0,1,0,.6,1,0,0,0,1,0,.6,1]);a=new THREE.BufferGeometry;a.addAttribute("position",new THREE.BufferAttribute(b,3));a.addAttribute("color",new THREE.BufferAttribute(c,3));b=new THREE.LineBasicMaterial({vertexColors:THREE.VertexColors});THREE.Line.call(this,a,b,THREE.LinePieces)};THREE.AxisHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.ArrowHelper=function(){var a=new THREE.Geometry;a.vertices.push(new THREE.Vector3(0,0,0),new THREE.Vector3(0,1,0));var b=new THREE.CylinderGeometry(0,.5,1,5,1);b.applyMatrix((new THREE.Matrix4).makeTranslation(0,-.5,0));return function(c,d,e,f,g,h){THREE.Object3D.call(this);void 0===f&&(f=16776960);void 0===e&&(e=1);void 0===g&&(g=.2*e);void 0===h&&(h=.2*g);this.position.copy(d);this.line=new THREE.Line(a,new THREE.LineBasicMaterial({color:f}));this.line.matrixAutoUpdate=!1;this.add(this.line);
-this.cone=new THREE.Mesh(b,new THREE.MeshBasicMaterial({color:f}));this.cone.matrixAutoUpdate=!1;this.add(this.cone);this.setDirection(c);this.setLength(e,g,h)}}();THREE.ArrowHelper.prototype=Object.create(THREE.Object3D.prototype);THREE.ArrowHelper.prototype.setDirection=function(){var a=new THREE.Vector3,b;return function(c){.99999<c.y?this.quaternion.set(0,0,0,1):-.99999>c.y?this.quaternion.set(1,0,0,0):(a.set(c.z,0,-c.x).normalize(),b=Math.acos(c.y),this.quaternion.setFromAxisAngle(a,b))}}();
-THREE.ArrowHelper.prototype.setLength=function(a,b,c){void 0===b&&(b=.2*a);void 0===c&&(c=.2*b);this.line.scale.set(1,a,1);this.line.updateMatrix();this.cone.scale.set(c,b,c);this.cone.position.y=a;this.cone.updateMatrix()};THREE.ArrowHelper.prototype.setColor=function(a){this.line.material.color.set(a);this.cone.material.color.set(a)};
-THREE.BoxHelper=function(a){var b=new THREE.BufferGeometry;b.addAttribute("position",new THREE.BufferAttribute(new Float32Array(72),3));THREE.Line.call(this,b,new THREE.LineBasicMaterial({color:16776960}),THREE.LinePieces);void 0!==a&&this.update(a)};THREE.BoxHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.BoxHelper.prototype.update=function(a){var b=a.geometry;null===b.boundingBox&&b.computeBoundingBox();var c=b.boundingBox.min,b=b.boundingBox.max,d=this.geometry.attributes.position.array;d[0]=b.x;d[1]=b.y;d[2]=b.z;d[3]=c.x;d[4]=b.y;d[5]=b.z;d[6]=c.x;d[7]=b.y;d[8]=b.z;d[9]=c.x;d[10]=c.y;d[11]=b.z;d[12]=c.x;d[13]=c.y;d[14]=b.z;d[15]=b.x;d[16]=c.y;d[17]=b.z;d[18]=b.x;d[19]=c.y;d[20]=b.z;d[21]=b.x;d[22]=b.y;d[23]=b.z;d[24]=b.x;d[25]=b.y;d[26]=c.z;d[27]=c.x;d[28]=b.y;d[29]=c.z;d[30]=c.x;d[31]=b.y;
-d[32]=c.z;d[33]=c.x;d[34]=c.y;d[35]=c.z;d[36]=c.x;d[37]=c.y;d[38]=c.z;d[39]=b.x;d[40]=c.y;d[41]=c.z;d[42]=b.x;d[43]=c.y;d[44]=c.z;d[45]=b.x;d[46]=b.y;d[47]=c.z;d[48]=b.x;d[49]=b.y;d[50]=b.z;d[51]=b.x;d[52]=b.y;d[53]=c.z;d[54]=c.x;d[55]=b.y;d[56]=b.z;d[57]=c.x;d[58]=b.y;d[59]=c.z;d[60]=c.x;d[61]=c.y;d[62]=b.z;d[63]=c.x;d[64]=c.y;d[65]=c.z;d[66]=b.x;d[67]=c.y;d[68]=b.z;d[69]=b.x;d[70]=c.y;d[71]=c.z;this.geometry.attributes.position.needsUpdate=!0;this.geometry.computeBoundingSphere();this.matrix=a.matrixWorld;
-this.matrixAutoUpdate=!1};THREE.BoundingBoxHelper=function(a,b){var c=void 0!==b?b:8947848;this.object=a;this.box=new THREE.Box3;THREE.Mesh.call(this,new THREE.BoxGeometry(1,1,1),new THREE.MeshBasicMaterial({color:c,wireframe:!0}))};THREE.BoundingBoxHelper.prototype=Object.create(THREE.Mesh.prototype);THREE.BoundingBoxHelper.prototype.update=function(){this.box.setFromObject(this.object);this.box.size(this.scale);this.box.center(this.position)};
-THREE.CameraHelper=function(a){function b(a,b,d){c(a,d);c(b,d)}function c(a,b){d.vertices.push(new THREE.Vector3);d.colors.push(new THREE.Color(b));void 0===f[a]&&(f[a]=[]);f[a].push(d.vertices.length-1)}var d=new THREE.Geometry,e=new THREE.LineBasicMaterial({color:16777215,vertexColors:THREE.FaceColors}),f={};b("n1","n2",16755200);b("n2","n4",16755200);b("n4","n3",16755200);b("n3","n1",16755200);b("f1","f2",16755200);b("f2","f4",16755200);b("f4","f3",16755200);b("f3","f1",16755200);b("n1","f1",16755200);
-b("n2","f2",16755200);b("n3","f3",16755200);b("n4","f4",16755200);b("p","n1",16711680);b("p","n2",16711680);b("p","n3",16711680);b("p","n4",16711680);b("u1","u2",43775);b("u2","u3",43775);b("u3","u1",43775);b("c","t",16777215);b("p","c",3355443);b("cn1","cn2",3355443);b("cn3","cn4",3355443);b("cf1","cf2",3355443);b("cf3","cf4",3355443);THREE.Line.call(this,d,e,THREE.LinePieces);this.camera=a;this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1;this.pointMap=f;this.update()};
-THREE.CameraHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.CameraHelper.prototype.update=function(){var a,b,c=new THREE.Vector3,d=new THREE.Camera,e=function(e,g,h,k){c.set(g,h,k).unproject(d);e=b[e];if(void 0!==e)for(g=0,h=e.length;g<h;g++)a.vertices[e[g]].copy(c)};return function(){a=this.geometry;b=this.pointMap;d.projectionMatrix.copy(this.camera.projectionMatrix);e("c",0,0,-1);e("t",0,0,1);e("n1",-1,-1,-1);e("n2",1,-1,-1);e("n3",-1,1,-1);e("n4",1,1,-1);e("f1",-1,-1,1);e("f2",1,-1,1);e("f3",-1,1,1);e("f4",1,1,1);e("u1",.7,1.1,-1);e("u2",-.7,1.1,
--1);e("u3",0,2,-1);e("cf1",-1,0,1);e("cf2",1,0,1);e("cf3",0,-1,1);e("cf4",0,1,1);e("cn1",-1,0,-1);e("cn2",1,0,-1);e("cn3",0,-1,-1);e("cn4",0,1,-1);a.verticesNeedUpdate=!0}}();
-THREE.DirectionalLightHelper=function(a,b){THREE.Object3D.call(this);this.light=a;this.light.updateMatrixWorld();this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1;b=b||1;var c=new THREE.Geometry;c.vertices.push(new THREE.Vector3(-b,b,0),new THREE.Vector3(b,b,0),new THREE.Vector3(b,-b,0),new THREE.Vector3(-b,-b,0),new THREE.Vector3(-b,b,0));var d=new THREE.LineBasicMaterial({fog:!1});d.color.copy(this.light.color).multiplyScalar(this.light.intensity);this.lightPlane=new THREE.Line(c,d);this.add(this.lightPlane);
-c=new THREE.Geometry;c.vertices.push(new THREE.Vector3,new THREE.Vector3);d=new THREE.LineBasicMaterial({fog:!1});d.color.copy(this.light.color).multiplyScalar(this.light.intensity);this.targetLine=new THREE.Line(c,d);this.add(this.targetLine);this.update()};THREE.DirectionalLightHelper.prototype=Object.create(THREE.Object3D.prototype);
-THREE.DirectionalLightHelper.prototype.dispose=function(){this.lightPlane.geometry.dispose();this.lightPlane.material.dispose();this.targetLine.geometry.dispose();this.targetLine.material.dispose()};
-THREE.DirectionalLightHelper.prototype.update=function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Vector3;return function(){a.setFromMatrixPosition(this.light.matrixWorld);b.setFromMatrixPosition(this.light.target.matrixWorld);c.subVectors(b,a);this.lightPlane.lookAt(c);this.lightPlane.material.color.copy(this.light.color).multiplyScalar(this.light.intensity);this.targetLine.geometry.vertices[1].copy(c);this.targetLine.geometry.verticesNeedUpdate=!0;this.targetLine.material.color.copy(this.lightPlane.material.color)}}();
-THREE.EdgesHelper=function(a,b){var c=void 0!==b?b:16777215,d=[0,0],e={},f=function(a,b){return a-b},g=["a","b","c"],h=new THREE.BufferGeometry,k=a.geometry.clone();k.mergeVertices();k.computeFaceNormals();for(var n=k.vertices,k=k.faces,p=0,q=0,m=k.length;q<m;q++)for(var r=k[q],t=0;3>t;t++){d[0]=r[g[t]];d[1]=r[g[(t+1)%3]];d.sort(f);var s=d.toString();void 0===e[s]?(e[s]={vert1:d[0],vert2:d[1],face1:q,face2:void 0},p++):e[s].face2=q}d=new Float32Array(6*p);f=0;for(s in e)if(g=e[s],void 0===g.face2||
-.9999>k[g.face1].normal.dot(k[g.face2].normal))p=n[g.vert1],d[f++]=p.x,d[f++]=p.y,d[f++]=p.z,p=n[g.vert2],d[f++]=p.x,d[f++]=p.y,d[f++]=p.z;h.addAttribute("position",new THREE.BufferAttribute(d,3));THREE.Line.call(this,h,new THREE.LineBasicMaterial({color:c}),THREE.LinePieces);this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1};THREE.EdgesHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.FaceNormalsHelper=function(a,b,c,d){this.object=a;this.size=void 0!==b?b:1;a=void 0!==c?c:16776960;d=void 0!==d?d:1;b=new THREE.Geometry;c=0;for(var e=this.object.geometry.faces.length;c<e;c++)b.vertices.push(new THREE.Vector3,new THREE.Vector3);THREE.Line.call(this,b,new THREE.LineBasicMaterial({color:a,linewidth:d}),THREE.LinePieces);this.matrixAutoUpdate=!1;this.normalMatrix=new THREE.Matrix3;this.update()};THREE.FaceNormalsHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.FaceNormalsHelper.prototype.update=function(){var a=this.geometry.vertices,b=this.object,c=b.geometry.vertices,d=b.geometry.faces,e=b.matrixWorld;b.updateMatrixWorld(!0);this.normalMatrix.getNormalMatrix(e);for(var f=b=0,g=d.length;b<g;b++,f+=2){var h=d[b];a[f].copy(c[h.a]).add(c[h.b]).add(c[h.c]).divideScalar(3).applyMatrix4(e);a[f+1].copy(h.normal).applyMatrix3(this.normalMatrix).normalize().multiplyScalar(this.size).add(a[f])}this.geometry.verticesNeedUpdate=!0;return this};
-THREE.GridHelper=function(a,b){var c=new THREE.Geometry,d=new THREE.LineBasicMaterial({vertexColors:THREE.VertexColors});this.color1=new THREE.Color(4473924);this.color2=new THREE.Color(8947848);for(var e=-a;e<=a;e+=b){c.vertices.push(new THREE.Vector3(-a,0,e),new THREE.Vector3(a,0,e),new THREE.Vector3(e,0,-a),new THREE.Vector3(e,0,a));var f=0===e?this.color1:this.color2;c.colors.push(f,f,f,f)}THREE.Line.call(this,c,d,THREE.LinePieces)};THREE.GridHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.GridHelper.prototype.setColors=function(a,b){this.color1.set(a);this.color2.set(b);this.geometry.colorsNeedUpdate=!0};
-THREE.HemisphereLightHelper=function(a,b,c,d){THREE.Object3D.call(this);this.light=a;this.light.updateMatrixWorld();this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1;this.colors=[new THREE.Color,new THREE.Color];a=new THREE.SphereGeometry(b,4,2);a.applyMatrix((new THREE.Matrix4).makeRotationX(-Math.PI/2));for(b=0;8>b;b++)a.faces[b].color=this.colors[4>b?0:1];b=new THREE.MeshBasicMaterial({vertexColors:THREE.FaceColors,wireframe:!0});this.lightSphere=new THREE.Mesh(a,b);this.add(this.lightSphere);
-this.update()};THREE.HemisphereLightHelper.prototype=Object.create(THREE.Object3D.prototype);THREE.HemisphereLightHelper.prototype.dispose=function(){this.lightSphere.geometry.dispose();this.lightSphere.material.dispose()};
-THREE.HemisphereLightHelper.prototype.update=function(){var a=new THREE.Vector3;return function(){this.colors[0].copy(this.light.color).multiplyScalar(this.light.intensity);this.colors[1].copy(this.light.groundColor).multiplyScalar(this.light.intensity);this.lightSphere.lookAt(a.setFromMatrixPosition(this.light.matrixWorld).negate());this.lightSphere.geometry.colorsNeedUpdate=!0}}();
-THREE.PointLightHelper=function(a,b){this.light=a;this.light.updateMatrixWorld();var c=new THREE.SphereGeometry(b,4,2),d=new THREE.MeshBasicMaterial({wireframe:!0,fog:!1});d.color.copy(this.light.color).multiplyScalar(this.light.intensity);THREE.Mesh.call(this,c,d);this.matrix=this.light.matrixWorld;this.matrixAutoUpdate=!1};THREE.PointLightHelper.prototype=Object.create(THREE.Mesh.prototype);THREE.PointLightHelper.prototype.dispose=function(){this.geometry.dispose();this.material.dispose()};
-THREE.PointLightHelper.prototype.update=function(){this.material.color.copy(this.light.color).multiplyScalar(this.light.intensity)};
-THREE.SkeletonHelper=function(a){this.bones=this.getBoneList(a);for(var b=new THREE.Geometry,c=0;c<this.bones.length;c++)this.bones[c].parent instanceof THREE.Bone&&(b.vertices.push(new THREE.Vector3),b.vertices.push(new THREE.Vector3),b.colors.push(new THREE.Color(0,0,1)),b.colors.push(new THREE.Color(0,1,0)));c=new THREE.LineBasicMaterial({vertexColors:THREE.VertexColors,depthTest:!1,depthWrite:!1,transparent:!0});THREE.Line.call(this,b,c,THREE.LinePieces);this.root=a;this.matrix=a.matrixWorld;
-this.matrixAutoUpdate=!1;this.update()};THREE.SkeletonHelper.prototype=Object.create(THREE.Line.prototype);THREE.SkeletonHelper.prototype.getBoneList=function(a){var b=[];a instanceof THREE.Bone&&b.push(a);for(var c=0;c<a.children.length;c++)b.push.apply(b,this.getBoneList(a.children[c]));return b};
-THREE.SkeletonHelper.prototype.update=function(){for(var a=this.geometry,b=(new THREE.Matrix4).getInverse(this.root.matrixWorld),c=new THREE.Matrix4,d=0,e=0;e<this.bones.length;e++){var f=this.bones[e];f.parent instanceof THREE.Bone&&(c.multiplyMatrices(b,f.matrixWorld),a.vertices[d].setFromMatrixPosition(c),c.multiplyMatrices(b,f.parent.matrixWorld),a.vertices[d+1].setFromMatrixPosition(c),d+=2)}a.verticesNeedUpdate=!0;a.computeBoundingSphere()};
-THREE.SpotLightHelper=function(a){THREE.Object3D.call(this);this.light=a;this.light.updateMatrixWorld();this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1;a=new THREE.CylinderGeometry(0,1,1,8,1,!0);a.applyMatrix((new THREE.Matrix4).makeTranslation(0,-.5,0));a.applyMatrix((new THREE.Matrix4).makeRotationX(-Math.PI/2));var b=new THREE.MeshBasicMaterial({wireframe:!0,fog:!1});this.cone=new THREE.Mesh(a,b);this.add(this.cone);this.update()};THREE.SpotLightHelper.prototype=Object.create(THREE.Object3D.prototype);
-THREE.SpotLightHelper.prototype.dispose=function(){this.cone.geometry.dispose();this.cone.material.dispose()};THREE.SpotLightHelper.prototype.update=function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(){var c=this.light.distance?this.light.distance:1E4,d=c*Math.tan(this.light.angle);this.cone.scale.set(d,d,c);a.setFromMatrixPosition(this.light.matrixWorld);b.setFromMatrixPosition(this.light.target.matrixWorld);this.cone.lookAt(b.sub(a));this.cone.material.color.copy(this.light.color).multiplyScalar(this.light.intensity)}}();
-THREE.VertexNormalsHelper=function(a,b,c,d){this.object=a;this.size=void 0!==b?b:1;b=void 0!==c?c:16711680;d=void 0!==d?d:1;c=new THREE.Geometry;a=a.geometry.faces;for(var e=0,f=a.length;e<f;e++)for(var g=0,h=a[e].vertexNormals.length;g<h;g++)c.vertices.push(new THREE.Vector3,new THREE.Vector3);THREE.Line.call(this,c,new THREE.LineBasicMaterial({color:b,linewidth:d}),THREE.LinePieces);this.matrixAutoUpdate=!1;this.normalMatrix=new THREE.Matrix3;this.update()};THREE.VertexNormalsHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.VertexNormalsHelper.prototype.update=function(a){var b=new THREE.Vector3;return function(a){a=["a","b","c","d"];this.object.updateMatrixWorld(!0);this.normalMatrix.getNormalMatrix(this.object.matrixWorld);for(var d=this.geometry.vertices,e=this.object.geometry.vertices,f=this.object.geometry.faces,g=this.object.matrixWorld,h=0,k=0,n=f.length;k<n;k++)for(var p=f[k],q=0,m=p.vertexNormals.length;q<m;q++){var r=p.vertexNormals[q];d[h].copy(e[p[a[q]]]).applyMatrix4(g);b.copy(r).applyMatrix3(this.normalMatrix).normalize().multiplyScalar(this.size);
-b.add(d[h]);h+=1;d[h].copy(b);h+=1}this.geometry.verticesNeedUpdate=!0;return this}}();
-THREE.VertexTangentsHelper=function(a,b,c,d){this.object=a;this.size=void 0!==b?b:1;b=void 0!==c?c:255;d=void 0!==d?d:1;c=new THREE.Geometry;a=a.geometry.faces;for(var e=0,f=a.length;e<f;e++)for(var g=0,h=a[e].vertexTangents.length;g<h;g++)c.vertices.push(new THREE.Vector3),c.vertices.push(new THREE.Vector3);THREE.Line.call(this,c,new THREE.LineBasicMaterial({color:b,linewidth:d}),THREE.LinePieces);this.matrixAutoUpdate=!1;this.update()};THREE.VertexTangentsHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.VertexTangentsHelper.prototype.update=function(a){var b=new THREE.Vector3;return function(a){a=["a","b","c","d"];this.object.updateMatrixWorld(!0);for(var d=this.geometry.vertices,e=this.object.geometry.vertices,f=this.object.geometry.faces,g=this.object.matrixWorld,h=0,k=0,n=f.length;k<n;k++)for(var p=f[k],q=0,m=p.vertexTangents.length;q<m;q++){var r=p.vertexTangents[q];d[h].copy(e[p[a[q]]]).applyMatrix4(g);b.copy(r).transformDirection(g).multiplyScalar(this.size);b.add(d[h]);h+=1;d[h].copy(b);
-h+=1}this.geometry.verticesNeedUpdate=!0;return this}}();
-THREE.WireframeHelper=function(a,b){var c=void 0!==b?b:16777215,d=[0,0],e={},f=function(a,b){return a-b},g=["a","b","c"],h=new THREE.BufferGeometry;if(a.geometry instanceof THREE.Geometry){for(var k=a.geometry.vertices,n=a.geometry.faces,p=0,q=new Uint32Array(6*n.length),m=0,r=n.length;m<r;m++)for(var t=n[m],s=0;3>s;s++){d[0]=t[g[s]];d[1]=t[g[(s+1)%3]];d.sort(f);var u=d.toString();void 0===e[u]&&(q[2*p]=d[0],q[2*p+1]=d[1],e[u]=!0,p++)}d=new Float32Array(6*p);m=0;for(r=p;m<r;m++)for(s=0;2>s;s++)p=
-k[q[2*m+s]],g=6*m+3*s,d[g+0]=p.x,d[g+1]=p.y,d[g+2]=p.z;h.addAttribute("position",new THREE.BufferAttribute(d,3))}else if(a.geometry instanceof THREE.BufferGeometry){if(void 0!==a.geometry.attributes.index){k=a.geometry.attributes.position.array;r=a.geometry.attributes.index.array;n=a.geometry.drawcalls;p=0;0===n.length&&(n=[{count:r.length,index:0,start:0}]);for(var q=new Uint32Array(2*r.length),t=0,v=n.length;t<v;++t)for(var s=n[t].start,u=n[t].count,g=n[t].index,m=s,y=s+u;m<y;m+=3)for(s=0;3>s;s++)d[0]=
-g+r[m+s],d[1]=g+r[m+(s+1)%3],d.sort(f),u=d.toString(),void 0===e[u]&&(q[2*p]=d[0],q[2*p+1]=d[1],e[u]=!0,p++);d=new Float32Array(6*p);m=0;for(r=p;m<r;m++)for(s=0;2>s;s++)g=6*m+3*s,p=3*q[2*m+s],d[g+0]=k[p],d[g+1]=k[p+1],d[g+2]=k[p+2]}else for(k=a.geometry.attributes.position.array,p=k.length/3,q=p/3,d=new Float32Array(6*p),m=0,r=q;m<r;m++)for(s=0;3>s;s++)g=18*m+6*s,q=9*m+3*s,d[g+0]=k[q],d[g+1]=k[q+1],d[g+2]=k[q+2],p=9*m+(s+1)%3*3,d[g+3]=k[p],d[g+4]=k[p+1],d[g+5]=k[p+2];h.addAttribute("position",new THREE.BufferAttribute(d,
-3))}THREE.Line.call(this,h,new THREE.LineBasicMaterial({color:c}),THREE.LinePieces);this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1};THREE.WireframeHelper.prototype=Object.create(THREE.Line.prototype);THREE.ImmediateRenderObject=function(){THREE.Object3D.call(this);this.render=function(a){}};THREE.ImmediateRenderObject.prototype=Object.create(THREE.Object3D.prototype);
-THREE.MorphBlendMesh=function(a,b){THREE.Mesh.call(this,a,b);this.animationsMap={};this.animationsList=[];var c=this.geometry.morphTargets.length;this.createAnimation("__default",0,c-1,c/1);this.setAnimationWeight("__default",1)};THREE.MorphBlendMesh.prototype=Object.create(THREE.Mesh.prototype);
-THREE.MorphBlendMesh.prototype.createAnimation=function(a,b,c,d){b={startFrame:b,endFrame:c,length:c-b+1,fps:d,duration:(c-b)/d,lastFrame:0,currentFrame:0,active:!1,time:0,direction:1,weight:1,directionBackwards:!1,mirroredLoop:!1};this.animationsMap[a]=b;this.animationsList.push(b)};
-THREE.MorphBlendMesh.prototype.autoCreateAnimations=function(a){for(var b=/([a-z]+)_?(\d+)/,c,d={},e=this.geometry,f=0,g=e.morphTargets.length;f<g;f++){var h=e.morphTargets[f].name.match(b);if(h&&1<h.length){var k=h[1];d[k]||(d[k]={start:Infinity,end:-Infinity});h=d[k];f<h.start&&(h.start=f);f>h.end&&(h.end=f);c||(c=k)}}for(k in d)h=d[k],this.createAnimation(k,h.start,h.end,a);this.firstAnimation=c};
-THREE.MorphBlendMesh.prototype.setAnimationDirectionForward=function(a){if(a=this.animationsMap[a])a.direction=1,a.directionBackwards=!1};THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward=function(a){if(a=this.animationsMap[a])a.direction=-1,a.directionBackwards=!0};THREE.MorphBlendMesh.prototype.setAnimationFPS=function(a,b){var c=this.animationsMap[a];c&&(c.fps=b,c.duration=(c.end-c.start)/c.fps)};
-THREE.MorphBlendMesh.prototype.setAnimationDuration=function(a,b){var c=this.animationsMap[a];c&&(c.duration=b,c.fps=(c.end-c.start)/c.duration)};THREE.MorphBlendMesh.prototype.setAnimationWeight=function(a,b){var c=this.animationsMap[a];c&&(c.weight=b)};THREE.MorphBlendMesh.prototype.setAnimationTime=function(a,b){var c=this.animationsMap[a];c&&(c.time=b)};THREE.MorphBlendMesh.prototype.getAnimationTime=function(a){var b=0;if(a=this.animationsMap[a])b=a.time;return b};
-THREE.MorphBlendMesh.prototype.getAnimationDuration=function(a){var b=-1;if(a=this.animationsMap[a])b=a.duration;return b};THREE.MorphBlendMesh.prototype.playAnimation=function(a){var b=this.animationsMap[a];b?(b.time=0,b.active=!0):console.warn("animation["+a+"] undefined")};THREE.MorphBlendMesh.prototype.stopAnimation=function(a){if(a=this.animationsMap[a])a.active=!1};
-THREE.MorphBlendMesh.prototype.update=function(a){for(var b=0,c=this.animationsList.length;b<c;b++){var d=this.animationsList[b];if(d.active){var e=d.duration/d.length;d.time+=d.direction*a;if(d.mirroredLoop){if(d.time>d.duration||0>d.time)d.direction*=-1,d.time>d.duration&&(d.time=d.duration,d.directionBackwards=!0),0>d.time&&(d.time=0,d.directionBackwards=!1)}else d.time%=d.duration,0>d.time&&(d.time+=d.duration);var f=d.startFrame+THREE.Math.clamp(Math.floor(d.time/e),0,d.length-1),g=d.weight;
-f!==d.currentFrame&&(this.morphTargetInfluences[d.lastFrame]=0,this.morphTargetInfluences[d.currentFrame]=1*g,this.morphTargetInfluences[f]=0,d.lastFrame=d.currentFrame,d.currentFrame=f);e=d.time%e/e;d.directionBackwards&&(e=1-e);this.morphTargetInfluences[d.currentFrame]=e*g;this.morphTargetInfluences[d.lastFrame]=(1-e)*g}}};
diff --git a/plugins/Sidebar/media_globe/globe.js b/plugins/Sidebar/media_globe/globe.js
deleted file mode 100644
index a5523796..00000000
--- a/plugins/Sidebar/media_globe/globe.js
+++ /dev/null
@@ -1,436 +0,0 @@
-/**
- * dat.globe Javascript WebGL Globe Toolkit
- * http://dataarts.github.com/dat.globe
- *
- * Copyright 2011 Data Arts Team, Google Creative Lab
- *
- * Licensed under the Apache License, Version 2.0 (the 'License');
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- */
-
-var DAT = DAT || {};
-
-DAT.Globe = function(container, opts) {
-  opts = opts || {};
-
-  var colorFn = opts.colorFn || function(x) {
-    var c = new THREE.Color();
-    c.setHSL( ( 0.5 - (x * 2) ), Math.max(0.8, 1.0 - (x * 3)), 0.5 );
-    return c;
-  };
-  var imgDir = opts.imgDir || '/globe/';
-
-  var Shaders = {
-    'earth' : {
-      uniforms: {
-        'texture': { type: 't', value: null }
-      },
-      vertexShader: [
-        'varying vec3 vNormal;',
-        'varying vec2 vUv;',
-        'void main() {',
-          'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
-          'vNormal = normalize( normalMatrix * normal );',
-          'vUv = uv;',
-        '}'
-      ].join('\n'),
-      fragmentShader: [
-        'uniform sampler2D texture;',
-        'varying vec3 vNormal;',
-        'varying vec2 vUv;',
-        'void main() {',
-          'vec3 diffuse = texture2D( texture, vUv ).xyz;',
-          'float intensity = 1.05 - dot( vNormal, vec3( 0.0, 0.0, 1.0 ) );',
-          'vec3 atmosphere = vec3( 1.0, 1.0, 1.0 ) * pow( intensity, 3.0 );',
-          'gl_FragColor = vec4( diffuse + atmosphere, 1.0 );',
-        '}'
-      ].join('\n')
-    },
-    'atmosphere' : {
-      uniforms: {},
-      vertexShader: [
-        'varying vec3 vNormal;',
-        'void main() {',
-          'vNormal = normalize( normalMatrix * normal );',
-          'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
-        '}'
-      ].join('\n'),
-      fragmentShader: [
-        'varying vec3 vNormal;',
-        'void main() {',
-          'float intensity = pow( 0.8 - dot( vNormal, vec3( 0, 0, 1.0 ) ), 12.0 );',
-          'gl_FragColor = vec4( 1.0, 1.0, 1.0, 1.0 ) * intensity;',
-        '}'
-      ].join('\n')
-    }
-  };
-
-  var camera, scene, renderer, w, h;
-  var mesh, atmosphere, point, running;
-
-  var overRenderer;
-  var running = true;
-
-  var curZoomSpeed = 0;
-  var zoomSpeed = 50;
-
-  var mouse = { x: 0, y: 0 }, mouseOnDown = { x: 0, y: 0 };
-  var rotation = { x: 0, y: 0 },
-      target = { x: Math.PI*3/2, y: Math.PI / 6.0 },
-      targetOnDown = { x: 0, y: 0 };
-
-  var distance = 100000, distanceTarget = 100000;
-  var padding = 10;
-  var PI_HALF = Math.PI / 2;
-
-  function init() {
-
-    container.style.color = '#fff';
-    container.style.font = '13px/20px Arial, sans-serif';
-
-    var shader, uniforms, material;
-    w = container.offsetWidth || window.innerWidth;
-    h = container.offsetHeight || window.innerHeight;
-
-    camera = new THREE.PerspectiveCamera(30, w / h, 1, 10000);
-    camera.position.z = distance;
-
-    scene = new THREE.Scene();
-
-    var geometry = new THREE.SphereGeometry(200, 40, 30);
-
-    shader = Shaders['earth'];
-    uniforms = THREE.UniformsUtils.clone(shader.uniforms);
-
-    uniforms['texture'].value = THREE.ImageUtils.loadTexture(imgDir+'world.jpg');
-
-    material = new THREE.ShaderMaterial({
-
-          uniforms: uniforms,
-          vertexShader: shader.vertexShader,
-          fragmentShader: shader.fragmentShader
-
-        });
-
-    mesh = new THREE.Mesh(geometry, material);
-    mesh.rotation.y = Math.PI;
-    scene.add(mesh);
-
-    shader = Shaders['atmosphere'];
-    uniforms = THREE.UniformsUtils.clone(shader.uniforms);
-
-    material = new THREE.ShaderMaterial({
-
-          uniforms: uniforms,
-          vertexShader: shader.vertexShader,
-          fragmentShader: shader.fragmentShader,
-          side: THREE.BackSide,
-          blending: THREE.AdditiveBlending,
-          transparent: true
-
-        });
-
-    mesh = new THREE.Mesh(geometry, material);
-    mesh.scale.set( 1.1, 1.1, 1.1 );
-    scene.add(mesh);
-
-    geometry = new THREE.BoxGeometry(2.75, 2.75, 1);
-    geometry.applyMatrix(new THREE.Matrix4().makeTranslation(0,0,-0.5));
-
-    point = new THREE.Mesh(geometry);
-
-    renderer = new THREE.WebGLRenderer({antialias: true});
-    renderer.setSize(w, h);
-    renderer.setClearColor( 0x212121, 1 );
-
-    renderer.domElement.style.position = 'relative';
-
-    container.appendChild(renderer.domElement);
-
-    container.addEventListener('mousedown', onMouseDown, false);
-
-    if ('onwheel' in document) {
-      container.addEventListener('wheel', onMouseWheel, false);
-    } else {
-      container.addEventListener('mousewheel', onMouseWheel, false);
-    }
-
-    document.addEventListener('keydown', onDocumentKeyDown, false);
-
-    window.addEventListener('resize', onWindowResize, false);
-
-    container.addEventListener('mouseover', function() {
-      overRenderer = true;
-    }, false);
-
-    container.addEventListener('mouseout', function() {
-      overRenderer = false;
-    }, false);
-  }
-
-  function addData(data, opts) {
-    var lat, lng, size, color, i, step, colorFnWrapper;
-
-    opts.animated = opts.animated || false;
-    this.is_animated = opts.animated;
-    opts.format = opts.format || 'magnitude'; // other option is 'legend'
-    if (opts.format === 'magnitude') {
-      step = 3;
-      colorFnWrapper = function(data, i) { return colorFn(data[i+2]); }
-    } else if (opts.format === 'legend') {
-      step = 4;
-      colorFnWrapper = function(data, i) { return colorFn(data[i+3]); }
-    } else if (opts.format === 'peer') {
-      colorFnWrapper = function(data, i) { return colorFn(data[i+2]); }
-    } else {
-      throw('error: format not supported: '+opts.format);
-    }
-
-    if (opts.animated) {
-      if (this._baseGeometry === undefined) {
-        this._baseGeometry = new THREE.Geometry();
-        for (i = 0; i < data.length; i += step) {
-          lat = data[i];
-          lng = data[i + 1];
-//        size = data[i + 2];
-          color = colorFnWrapper(data,i);
-          size = 0;
-          addPoint(lat, lng, size, color, this._baseGeometry);
-        }
-      }
-      if(this._morphTargetId === undefined) {
-        this._morphTargetId = 0;
-      } else {
-        this._morphTargetId += 1;
-      }
-      opts.name = opts.name || 'morphTarget'+this._morphTargetId;
-    }
-    var subgeo = new THREE.Geometry();
-    for (i = 0; i < data.length; i += step) {
-      lat = data[i];
-      lng = data[i + 1];
-      color = colorFnWrapper(data,i);
-      size = data[i + 2];
-      size = size*200;
-      addPoint(lat, lng, size, color, subgeo);
-    }
-    if (opts.animated) {
-      this._baseGeometry.morphTargets.push({'name': opts.name, vertices: subgeo.vertices});
-    } else {
-      this._baseGeometry = subgeo;
-    }
-
-  };
-
-  function createPoints() {
-    if (this._baseGeometry !== undefined) {
-      if (this.is_animated === false) {
-        this.points = new THREE.Mesh(this._baseGeometry, new THREE.MeshBasicMaterial({
-              color: 0xffffff,
-              vertexColors: THREE.FaceColors,
-              morphTargets: false
-            }));
-      } else {
-        if (this._baseGeometry.morphTargets.length < 8) {
-          console.log('t l',this._baseGeometry.morphTargets.length);
-          var padding = 8-this._baseGeometry.morphTargets.length;
-          console.log('padding', padding);
-          for(var i=0; i<=padding; i++) {
-            console.log('padding',i);
-            this._baseGeometry.morphTargets.push({'name': 'morphPadding'+i, vertices: this._baseGeometry.vertices});
-          }
-        }
-        this.points = new THREE.Mesh(this._baseGeometry, new THREE.MeshBasicMaterial({
-              color: 0xffffff,
-              vertexColors: THREE.FaceColors,
-              morphTargets: true
-            }));
-      }
-      scene.add(this.points);
-    }
-  }
-
-  function addPoint(lat, lng, size, color, subgeo) {
-
-    var phi = (90 - lat) * Math.PI / 180;
-    var theta = (180 - lng) * Math.PI / 180;
-
-    point.position.x = 200 * Math.sin(phi) * Math.cos(theta);
-    point.position.y = 200 * Math.cos(phi);
-    point.position.z = 200 * Math.sin(phi) * Math.sin(theta);
-
-    point.lookAt(mesh.position);
-
-    point.scale.z = Math.max( size, 0.1 ); // avoid non-invertible matrix
-    point.updateMatrix();
-
-    for (var i = 0; i < point.geometry.faces.length; i++) {
-
-      point.geometry.faces[i].color = color;
-
-    }
-    if(point.matrixAutoUpdate){
-      point.updateMatrix();
-    }
-    subgeo.merge(point.geometry, point.matrix);
-  }
-
-  function onMouseDown(event) {
-    event.preventDefault();
-
-    container.addEventListener('mousemove', onMouseMove, false);
-    container.addEventListener('mouseup', onMouseUp, false);
-    container.addEventListener('mouseout', onMouseOut, false);
-
-    mouseOnDown.x = - event.clientX;
-    mouseOnDown.y = event.clientY;
-
-    targetOnDown.x = target.x;
-    targetOnDown.y = target.y;
-
-    container.style.cursor = 'move';
-  }
-
-  function onMouseMove(event) {
-    mouse.x = - event.clientX;
-    mouse.y = event.clientY;
-
-    var zoomDamp = distance/1000;
-
-    target.x = targetOnDown.x + (mouse.x - mouseOnDown.x) * 0.005 * zoomDamp;
-    target.y = targetOnDown.y + (mouse.y - mouseOnDown.y) * 0.005 * zoomDamp;
-
-    target.y = target.y > PI_HALF ? PI_HALF : target.y;
-    target.y = target.y < - PI_HALF ? - PI_HALF : target.y;
-  }
-
-  function onMouseUp(event) {
-    container.removeEventListener('mousemove', onMouseMove, false);
-    container.removeEventListener('mouseup', onMouseUp, false);
-    container.removeEventListener('mouseout', onMouseOut, false);
-    container.style.cursor = 'auto';
-  }
-
-  function onMouseOut(event) {
-    container.removeEventListener('mousemove', onMouseMove, false);
-    container.removeEventListener('mouseup', onMouseUp, false);
-    container.removeEventListener('mouseout', onMouseOut, false);
-  }
-
-  function onMouseWheel(event) {
-    if (container.style.cursor != "move") return false;
-    event.preventDefault();
-    if (overRenderer) {
-      if (event.deltaY) {
-        zoom(-event.deltaY * (event.deltaMode == 0 ? 1 : 50));
-      } else {
-        zoom(event.wheelDeltaY * 0.3);
-      }
-    }
-    return false;
-  }
-
-  function onDocumentKeyDown(event) {
-    switch (event.keyCode) {
-      case 38:
-        zoom(100);
-        event.preventDefault();
-        break;
-      case 40:
-        zoom(-100);
-        event.preventDefault();
-        break;
-    }
-  }
-
-  function onWindowResize( event ) {
-    camera.aspect = container.offsetWidth / container.offsetHeight;
-    camera.updateProjectionMatrix();
-    renderer.setSize( container.offsetWidth, container.offsetHeight );
-  }
-
-  function zoom(delta) {
-    distanceTarget -= delta;
-    distanceTarget = distanceTarget > 855 ? 855 : distanceTarget;
-    distanceTarget = distanceTarget < 350 ? 350 : distanceTarget;
-  }
-
-  function animate() {
-    if (!running) return
-    requestAnimationFrame(animate);
-    render();
-  }
-
-  function render() {
-    zoom(curZoomSpeed);
-
-    rotation.x += (target.x - rotation.x) * 0.1;
-    rotation.y += (target.y - rotation.y) * 0.1;
-    distance += (distanceTarget - distance) * 0.3;
-
-    camera.position.x = distance * Math.sin(rotation.x) * Math.cos(rotation.y);
-    camera.position.y = distance * Math.sin(rotation.y);
-    camera.position.z = distance * Math.cos(rotation.x) * Math.cos(rotation.y);
-
-    camera.lookAt(mesh.position);
-
-    renderer.render(scene, camera);
-  }
-
-  function unload() {
-    running = false
-    container.removeEventListener('mousedown', onMouseDown, false);
-    if ('onwheel' in document) {
-      container.removeEventListener('wheel', onMouseWheel, false);
-    } else {
-      container.removeEventListener('mousewheel', onMouseWheel, false);
-    }
-    document.removeEventListener('keydown', onDocumentKeyDown, false);
-    window.removeEventListener('resize', onWindowResize, false);
-
-  }
-
-  init();
-  this.animate = animate;
-  this.unload = unload;
-
-
-  this.__defineGetter__('time', function() {
-    return this._time || 0;
-  });
-
-  this.__defineSetter__('time', function(t) {
-    var validMorphs = [];
-    var morphDict = this.points.morphTargetDictionary;
-    for(var k in morphDict) {
-      if(k.indexOf('morphPadding') < 0) {
-        validMorphs.push(morphDict[k]);
-      }
-    }
-    validMorphs.sort();
-    var l = validMorphs.length-1;
-    var scaledt = t*l+1;
-    var index = Math.floor(scaledt);
-    for (i=0;i<validMorphs.length;i++) {
-      this.points.morphTargetInfluences[validMorphs[i]] = 0;
-    }
-    var lastIndex = index - 1;
-    var leftover = scaledt - index;
-    if (lastIndex >= 0) {
-      this.points.morphTargetInfluences[lastIndex] = 1 - leftover;
-    }
-    this.points.morphTargetInfluences[index] = leftover;
-    this._time = t;
-  });
-
-  this.addData = addData;
-  this.createPoints = createPoints;
-  this.renderer = renderer;
-  this.scene = scene;
-
-  return this;
-
-};
diff --git a/plugins/Sidebar/media_globe/three.min.js b/plugins/Sidebar/media_globe/three.min.js
deleted file mode 100644
index a88b4afa..00000000
--- a/plugins/Sidebar/media_globe/three.min.js
+++ /dev/null
@@ -1,814 +0,0 @@
-// threejs.org/license
-'use strict';var THREE={REVISION:"69"};"object"===typeof module&&(module.exports=THREE);void 0===Math.sign&&(Math.sign=function(a){return 0>a?-1:0<a?1:0});THREE.MOUSE={LEFT:0,MIDDLE:1,RIGHT:2};THREE.CullFaceNone=0;THREE.CullFaceBack=1;THREE.CullFaceFront=2;THREE.CullFaceFrontBack=3;THREE.FrontFaceDirectionCW=0;THREE.FrontFaceDirectionCCW=1;THREE.BasicShadowMap=0;THREE.PCFShadowMap=1;THREE.PCFSoftShadowMap=2;THREE.FrontSide=0;THREE.BackSide=1;THREE.DoubleSide=2;THREE.NoShading=0;
-THREE.FlatShading=1;THREE.SmoothShading=2;THREE.NoColors=0;THREE.FaceColors=1;THREE.VertexColors=2;THREE.NoBlending=0;THREE.NormalBlending=1;THREE.AdditiveBlending=2;THREE.SubtractiveBlending=3;THREE.MultiplyBlending=4;THREE.CustomBlending=5;THREE.AddEquation=100;THREE.SubtractEquation=101;THREE.ReverseSubtractEquation=102;THREE.MinEquation=103;THREE.MaxEquation=104;THREE.ZeroFactor=200;THREE.OneFactor=201;THREE.SrcColorFactor=202;THREE.OneMinusSrcColorFactor=203;THREE.SrcAlphaFactor=204;
-THREE.OneMinusSrcAlphaFactor=205;THREE.DstAlphaFactor=206;THREE.OneMinusDstAlphaFactor=207;THREE.DstColorFactor=208;THREE.OneMinusDstColorFactor=209;THREE.SrcAlphaSaturateFactor=210;THREE.MultiplyOperation=0;THREE.MixOperation=1;THREE.AddOperation=2;THREE.UVMapping=function(){};THREE.CubeReflectionMapping=function(){};THREE.CubeRefractionMapping=function(){};THREE.SphericalReflectionMapping=function(){};THREE.SphericalRefractionMapping=function(){};THREE.RepeatWrapping=1E3;
-THREE.ClampToEdgeWrapping=1001;THREE.MirroredRepeatWrapping=1002;THREE.NearestFilter=1003;THREE.NearestMipMapNearestFilter=1004;THREE.NearestMipMapLinearFilter=1005;THREE.LinearFilter=1006;THREE.LinearMipMapNearestFilter=1007;THREE.LinearMipMapLinearFilter=1008;THREE.UnsignedByteType=1009;THREE.ByteType=1010;THREE.ShortType=1011;THREE.UnsignedShortType=1012;THREE.IntType=1013;THREE.UnsignedIntType=1014;THREE.FloatType=1015;THREE.UnsignedShort4444Type=1016;THREE.UnsignedShort5551Type=1017;
-THREE.UnsignedShort565Type=1018;THREE.AlphaFormat=1019;THREE.RGBFormat=1020;THREE.RGBAFormat=1021;THREE.LuminanceFormat=1022;THREE.LuminanceAlphaFormat=1023;THREE.RGB_S3TC_DXT1_Format=2001;THREE.RGBA_S3TC_DXT1_Format=2002;THREE.RGBA_S3TC_DXT3_Format=2003;THREE.RGBA_S3TC_DXT5_Format=2004;THREE.RGB_PVRTC_4BPPV1_Format=2100;THREE.RGB_PVRTC_2BPPV1_Format=2101;THREE.RGBA_PVRTC_4BPPV1_Format=2102;THREE.RGBA_PVRTC_2BPPV1_Format=2103;
-THREE.Color=function(a){return 3===arguments.length?this.setRGB(arguments[0],arguments[1],arguments[2]):this.set(a)};
-THREE.Color.prototype={constructor:THREE.Color,r:1,g:1,b:1,set:function(a){a instanceof THREE.Color?this.copy(a):"number"===typeof a?this.setHex(a):"string"===typeof a&&this.setStyle(a);return this},setHex:function(a){a=Math.floor(a);this.r=(a>>16&255)/255;this.g=(a>>8&255)/255;this.b=(a&255)/255;return this},setRGB:function(a,b,c){this.r=a;this.g=b;this.b=c;return this},setHSL:function(a,b,c){if(0===b)this.r=this.g=this.b=c;else{var d=function(a,b,c){0>c&&(c+=1);1<c&&(c-=1);return c<1/6?a+6*(b-a)*
-c:.5>c?b:c<2/3?a+6*(b-a)*(2/3-c):a};b=.5>=c?c*(1+b):c+b-c*b;c=2*c-b;this.r=d(c,b,a+1/3);this.g=d(c,b,a);this.b=d(c,b,a-1/3)}return this},setStyle:function(a){if(/^rgb\((\d+), ?(\d+), ?(\d+)\)$/i.test(a))return a=/^rgb\((\d+), ?(\d+), ?(\d+)\)$/i.exec(a),this.r=Math.min(255,parseInt(a[1],10))/255,this.g=Math.min(255,parseInt(a[2],10))/255,this.b=Math.min(255,parseInt(a[3],10))/255,this;if(/^rgb\((\d+)\%, ?(\d+)\%, ?(\d+)\%\)$/i.test(a))return a=/^rgb\((\d+)\%, ?(\d+)\%, ?(\d+)\%\)$/i.exec(a),this.r=
-Math.min(100,parseInt(a[1],10))/100,this.g=Math.min(100,parseInt(a[2],10))/100,this.b=Math.min(100,parseInt(a[3],10))/100,this;if(/^\#([0-9a-f]{6})$/i.test(a))return a=/^\#([0-9a-f]{6})$/i.exec(a),this.setHex(parseInt(a[1],16)),this;if(/^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.test(a))return a=/^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(a),this.setHex(parseInt(a[1]+a[1]+a[2]+a[2]+a[3]+a[3],16)),this;if(/^(\w+)$/i.test(a))return this.setHex(THREE.ColorKeywords[a]),this},copy:function(a){this.r=a.r;this.g=
-a.g;this.b=a.b;return this},copyGammaToLinear:function(a){this.r=a.r*a.r;this.g=a.g*a.g;this.b=a.b*a.b;return this},copyLinearToGamma:function(a){this.r=Math.sqrt(a.r);this.g=Math.sqrt(a.g);this.b=Math.sqrt(a.b);return this},convertGammaToLinear:function(){var a=this.r,b=this.g,c=this.b;this.r=a*a;this.g=b*b;this.b=c*c;return this},convertLinearToGamma:function(){this.r=Math.sqrt(this.r);this.g=Math.sqrt(this.g);this.b=Math.sqrt(this.b);return this},getHex:function(){return 255*this.r<<16^255*this.g<<
-8^255*this.b<<0},getHexString:function(){return("000000"+this.getHex().toString(16)).slice(-6)},getHSL:function(a){a=a||{h:0,s:0,l:0};var b=this.r,c=this.g,d=this.b,e=Math.max(b,c,d),f=Math.min(b,c,d),g,h=(f+e)/2;if(f===e)f=g=0;else{var k=e-f,f=.5>=h?k/(e+f):k/(2-e-f);switch(e){case b:g=(c-d)/k+(c<d?6:0);break;case c:g=(d-b)/k+2;break;case d:g=(b-c)/k+4}g/=6}a.h=g;a.s=f;a.l=h;return a},getStyle:function(){return"rgb("+(255*this.r|0)+","+(255*this.g|0)+","+(255*this.b|0)+")"},offsetHSL:function(a,
-b,c){var d=this.getHSL();d.h+=a;d.s+=b;d.l+=c;this.setHSL(d.h,d.s,d.l);return this},add:function(a){this.r+=a.r;this.g+=a.g;this.b+=a.b;return this},addColors:function(a,b){this.r=a.r+b.r;this.g=a.g+b.g;this.b=a.b+b.b;return this},addScalar:function(a){this.r+=a;this.g+=a;this.b+=a;return this},multiply:function(a){this.r*=a.r;this.g*=a.g;this.b*=a.b;return this},multiplyScalar:function(a){this.r*=a;this.g*=a;this.b*=a;return this},lerp:function(a,b){this.r+=(a.r-this.r)*b;this.g+=(a.g-this.g)*b;
-this.b+=(a.b-this.b)*b;return this},equals:function(a){return a.r===this.r&&a.g===this.g&&a.b===this.b},fromArray:function(a){this.r=a[0];this.g=a[1];this.b=a[2];return this},toArray:function(){return[this.r,this.g,this.b]},clone:function(){return(new THREE.Color).setRGB(this.r,this.g,this.b)}};
-THREE.ColorKeywords={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,
-darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,
-grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,
-lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,
-palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,
-tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};THREE.Quaternion=function(a,b,c,d){this._x=a||0;this._y=b||0;this._z=c||0;this._w=void 0!==d?d:1};
-THREE.Quaternion.prototype={constructor:THREE.Quaternion,_x:0,_y:0,_z:0,_w:0,get x(){return this._x},set x(a){this._x=a;this.onChangeCallback()},get y(){return this._y},set y(a){this._y=a;this.onChangeCallback()},get z(){return this._z},set z(a){this._z=a;this.onChangeCallback()},get w(){return this._w},set w(a){this._w=a;this.onChangeCallback()},set:function(a,b,c,d){this._x=a;this._y=b;this._z=c;this._w=d;this.onChangeCallback();return this},copy:function(a){this._x=a.x;this._y=a.y;this._z=a.z;
-this._w=a.w;this.onChangeCallback();return this},setFromEuler:function(a,b){if(!1===a instanceof THREE.Euler)throw Error("THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.");var c=Math.cos(a._x/2),d=Math.cos(a._y/2),e=Math.cos(a._z/2),f=Math.sin(a._x/2),g=Math.sin(a._y/2),h=Math.sin(a._z/2);"XYZ"===a.order?(this._x=f*d*e+c*g*h,this._y=c*g*e-f*d*h,this._z=c*d*h+f*g*e,this._w=c*d*e-f*g*h):"YXZ"===a.order?(this._x=f*d*e+c*g*h,this._y=c*g*e-f*d*h,this._z=
-c*d*h-f*g*e,this._w=c*d*e+f*g*h):"ZXY"===a.order?(this._x=f*d*e-c*g*h,this._y=c*g*e+f*d*h,this._z=c*d*h+f*g*e,this._w=c*d*e-f*g*h):"ZYX"===a.order?(this._x=f*d*e-c*g*h,this._y=c*g*e+f*d*h,this._z=c*d*h-f*g*e,this._w=c*d*e+f*g*h):"YZX"===a.order?(this._x=f*d*e+c*g*h,this._y=c*g*e+f*d*h,this._z=c*d*h-f*g*e,this._w=c*d*e-f*g*h):"XZY"===a.order&&(this._x=f*d*e-c*g*h,this._y=c*g*e-f*d*h,this._z=c*d*h+f*g*e,this._w=c*d*e+f*g*h);if(!1!==b)this.onChangeCallback();return this},setFromAxisAngle:function(a,
-b){var c=b/2,d=Math.sin(c);this._x=a.x*d;this._y=a.y*d;this._z=a.z*d;this._w=Math.cos(c);this.onChangeCallback();return this},setFromRotationMatrix:function(a){var b=a.elements,c=b[0];a=b[4];var d=b[8],e=b[1],f=b[5],g=b[9],h=b[2],k=b[6],b=b[10],n=c+f+b;0<n?(c=.5/Math.sqrt(n+1),this._w=.25/c,this._x=(k-g)*c,this._y=(d-h)*c,this._z=(e-a)*c):c>f&&c>b?(c=2*Math.sqrt(1+c-f-b),this._w=(k-g)/c,this._x=.25*c,this._y=(a+e)/c,this._z=(d+h)/c):f>b?(c=2*Math.sqrt(1+f-c-b),this._w=(d-h)/c,this._x=(a+e)/c,this._y=
-.25*c,this._z=(g+k)/c):(c=2*Math.sqrt(1+b-c-f),this._w=(e-a)/c,this._x=(d+h)/c,this._y=(g+k)/c,this._z=.25*c);this.onChangeCallback();return this},setFromUnitVectors:function(){var a,b;return function(c,d){void 0===a&&(a=new THREE.Vector3);b=c.dot(d)+1;1E-6>b?(b=0,Math.abs(c.x)>Math.abs(c.z)?a.set(-c.y,c.x,0):a.set(0,-c.z,c.y)):a.crossVectors(c,d);this._x=a.x;this._y=a.y;this._z=a.z;this._w=b;this.normalize();return this}}(),inverse:function(){this.conjugate().normalize();return this},conjugate:function(){this._x*=
--1;this._y*=-1;this._z*=-1;this.onChangeCallback();return this},dot:function(a){return this._x*a._x+this._y*a._y+this._z*a._z+this._w*a._w},lengthSq:function(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w},length:function(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)},normalize:function(){var a=this.length();0===a?(this._z=this._y=this._x=0,this._w=1):(a=1/a,this._x*=a,this._y*=a,this._z*=a,this._w*=a);this.onChangeCallback();return this},
-multiply:function(a,b){return void 0!==b?(console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."),this.multiplyQuaternions(a,b)):this.multiplyQuaternions(this,a)},multiplyQuaternions:function(a,b){var c=a._x,d=a._y,e=a._z,f=a._w,g=b._x,h=b._y,k=b._z,n=b._w;this._x=c*n+f*g+d*k-e*h;this._y=d*n+f*h+e*g-c*k;this._z=e*n+f*k+c*h-d*g;this._w=f*n-c*g-d*h-e*k;this.onChangeCallback();return this},multiplyVector3:function(a){console.warn("THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.");
-return a.applyQuaternion(this)},slerp:function(a,b){if(0===b)return this;if(1===b)return this.copy(a);var c=this._x,d=this._y,e=this._z,f=this._w,g=f*a._w+c*a._x+d*a._y+e*a._z;0>g?(this._w=-a._w,this._x=-a._x,this._y=-a._y,this._z=-a._z,g=-g):this.copy(a);if(1<=g)return this._w=f,this._x=c,this._y=d,this._z=e,this;var h=Math.acos(g),k=Math.sqrt(1-g*g);if(.001>Math.abs(k))return this._w=.5*(f+this._w),this._x=.5*(c+this._x),this._y=.5*(d+this._y),this._z=.5*(e+this._z),this;g=Math.sin((1-b)*h)/k;h=
-Math.sin(b*h)/k;this._w=f*g+this._w*h;this._x=c*g+this._x*h;this._y=d*g+this._y*h;this._z=e*g+this._z*h;this.onChangeCallback();return this},equals:function(a){return a._x===this._x&&a._y===this._y&&a._z===this._z&&a._w===this._w},fromArray:function(a,b){void 0===b&&(b=0);this._x=a[b];this._y=a[b+1];this._z=a[b+2];this._w=a[b+3];this.onChangeCallback();return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this._x;a[b+1]=this._y;a[b+2]=this._z;a[b+3]=this._w;return a},onChange:function(a){this.onChangeCallback=
-a;return this},onChangeCallback:function(){},clone:function(){return new THREE.Quaternion(this._x,this._y,this._z,this._w)}};THREE.Quaternion.slerp=function(a,b,c,d){return c.copy(a).slerp(b,d)};THREE.Vector2=function(a,b){this.x=a||0;this.y=b||0};
-THREE.Vector2.prototype={constructor:THREE.Vector2,set:function(a,b){this.x=a;this.y=b;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;default:throw Error("index is out of range: "+a);}},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y;default:throw Error("index is out of range: "+a);}},copy:function(a){this.x=a.x;this.y=a.y;return this},add:function(a,
-b){if(void 0!==b)return console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;return this},addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;return this},addScalar:function(a){this.x+=a;this.y+=a;return this},sub:function(a,b){if(void 0!==b)return console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(a,b);this.x-=a.x;this.y-=a.y;return this},
-subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;return this},multiply:function(a){this.x*=a.x;this.y*=a.y;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;return this},divide:function(a){this.x/=a.x;this.y/=a.y;return this},divideScalar:function(a){0!==a?(a=1/a,this.x*=a,this.y*=a):this.y=this.x=0;return this},min:function(a){this.x>a.x&&(this.x=a.x);this.y>a.y&&(this.y=a.y);return this},max:function(a){this.x<a.x&&(this.x=a.x);this.y<a.y&&(this.y=a.y);return this},clamp:function(a,
-b){this.x<a.x?this.x=a.x:this.x>b.x&&(this.x=b.x);this.y<a.y?this.y=a.y:this.y>b.y&&(this.y=b.y);return this},clampScalar:function(){var a,b;return function(c,d){void 0===a&&(a=new THREE.Vector2,b=new THREE.Vector2);a.set(c,c);b.set(d,d);return this.clamp(a,b)}}(),floor:function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);return this},ceil:function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);return this},round:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);return this},
-roundToZero:function(){this.x=0>this.x?Math.ceil(this.x):Math.floor(this.x);this.y=0>this.y?Math.ceil(this.y):Math.floor(this.y);return this},negate:function(){this.x=-this.x;this.y=-this.y;return this},dot:function(a){return this.x*a.x+this.y*a.y},lengthSq:function(){return this.x*this.x+this.y*this.y},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},normalize:function(){return this.divideScalar(this.length())},distanceTo:function(a){return Math.sqrt(this.distanceToSquared(a))},distanceToSquared:function(a){var b=
-this.x-a.x;a=this.y-a.y;return b*b+a*a},setLength:function(a){var b=this.length();0!==b&&a!==b&&this.multiplyScalar(a/b);return this},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;return this},equals:function(a){return a.x===this.x&&a.y===this.y},fromArray:function(a,b){void 0===b&&(b=0);this.x=a[b];this.y=a[b+1];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this.x;a[b+1]=this.y;return a},clone:function(){return new THREE.Vector2(this.x,this.y)}};
-THREE.Vector3=function(a,b,c){this.x=a||0;this.y=b||0;this.z=c||0};
-THREE.Vector3.prototype={constructor:THREE.Vector3,set:function(a,b,c){this.x=a;this.y=b;this.z=c;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setZ:function(a){this.z=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;case 2:this.z=b;break;default:throw Error("index is out of range: "+a);}},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw Error("index is out of range: "+
-a);}},copy:function(a){this.x=a.x;this.y=a.y;this.z=a.z;return this},add:function(a,b){if(void 0!==b)return console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;this.z+=a.z;return this},addScalar:function(a){this.x+=a;this.y+=a;this.z+=a;return this},addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;this.z=a.z+b.z;return this},sub:function(a,b){if(void 0!==b)return console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),
-this.subVectors(a,b);this.x-=a.x;this.y-=a.y;this.z-=a.z;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;this.z=a.z-b.z;return this},multiply:function(a,b){if(void 0!==b)return console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."),this.multiplyVectors(a,b);this.x*=a.x;this.y*=a.y;this.z*=a.z;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;this.z*=a;return this},multiplyVectors:function(a,b){this.x=a.x*b.x;this.y=
-a.y*b.y;this.z=a.z*b.z;return this},applyEuler:function(){var a;return function(b){!1===b instanceof THREE.Euler&&console.error("THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order.");void 0===a&&(a=new THREE.Quaternion);this.applyQuaternion(a.setFromEuler(b));return this}}(),applyAxisAngle:function(){var a;return function(b,c){void 0===a&&(a=new THREE.Quaternion);this.applyQuaternion(a.setFromAxisAngle(b,c));return this}}(),applyMatrix3:function(a){var b=this.x,
-c=this.y,d=this.z;a=a.elements;this.x=a[0]*b+a[3]*c+a[6]*d;this.y=a[1]*b+a[4]*c+a[7]*d;this.z=a[2]*b+a[5]*c+a[8]*d;return this},applyMatrix4:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d+a[12];this.y=a[1]*b+a[5]*c+a[9]*d+a[13];this.z=a[2]*b+a[6]*c+a[10]*d+a[14];return this},applyProjection:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;var e=1/(a[3]*b+a[7]*c+a[11]*d+a[15]);this.x=(a[0]*b+a[4]*c+a[8]*d+a[12])*e;this.y=(a[1]*b+a[5]*c+a[9]*d+a[13])*e;this.z=
-(a[2]*b+a[6]*c+a[10]*d+a[14])*e;return this},applyQuaternion:function(a){var b=this.x,c=this.y,d=this.z,e=a.x,f=a.y,g=a.z;a=a.w;var h=a*b+f*d-g*c,k=a*c+g*b-e*d,n=a*d+e*c-f*b,b=-e*b-f*c-g*d;this.x=h*a+b*-e+k*-g-n*-f;this.y=k*a+b*-f+n*-e-h*-g;this.z=n*a+b*-g+h*-f-k*-e;return this},project:function(){var a;return function(b){void 0===a&&(a=new THREE.Matrix4);a.multiplyMatrices(b.projectionMatrix,a.getInverse(b.matrixWorld));return this.applyProjection(a)}}(),unproject:function(){var a;return function(b){void 0===
-a&&(a=new THREE.Matrix4);a.multiplyMatrices(b.matrixWorld,a.getInverse(b.projectionMatrix));return this.applyProjection(a)}}(),transformDirection:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d;this.y=a[1]*b+a[5]*c+a[9]*d;this.z=a[2]*b+a[6]*c+a[10]*d;this.normalize();return this},divide:function(a){this.x/=a.x;this.y/=a.y;this.z/=a.z;return this},divideScalar:function(a){0!==a?(a=1/a,this.x*=a,this.y*=a,this.z*=a):this.z=this.y=this.x=0;return this},min:function(a){this.x>
-a.x&&(this.x=a.x);this.y>a.y&&(this.y=a.y);this.z>a.z&&(this.z=a.z);return this},max:function(a){this.x<a.x&&(this.x=a.x);this.y<a.y&&(this.y=a.y);this.z<a.z&&(this.z=a.z);return this},clamp:function(a,b){this.x<a.x?this.x=a.x:this.x>b.x&&(this.x=b.x);this.y<a.y?this.y=a.y:this.y>b.y&&(this.y=b.y);this.z<a.z?this.z=a.z:this.z>b.z&&(this.z=b.z);return this},clampScalar:function(){var a,b;return function(c,d){void 0===a&&(a=new THREE.Vector3,b=new THREE.Vector3);a.set(c,c,c);b.set(d,d,d);return this.clamp(a,
-b)}}(),floor:function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);this.z=Math.floor(this.z);return this},ceil:function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);this.z=Math.ceil(this.z);return this},round:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);this.z=Math.round(this.z);return this},roundToZero:function(){this.x=0>this.x?Math.ceil(this.x):Math.floor(this.x);this.y=0>this.y?Math.ceil(this.y):Math.floor(this.y);this.z=0>this.z?Math.ceil(this.z):Math.floor(this.z);
-return this},negate:function(){this.x=-this.x;this.y=-this.y;this.z=-this.z;return this},dot:function(a){return this.x*a.x+this.y*a.y+this.z*a.z},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)},normalize:function(){return this.divideScalar(this.length())},setLength:function(a){var b=this.length();0!==b&&a!==b&&this.multiplyScalar(a/
-b);return this},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;this.z+=(a.z-this.z)*b;return this},cross:function(a,b){if(void 0!==b)return console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."),this.crossVectors(a,b);var c=this.x,d=this.y,e=this.z;this.x=d*a.z-e*a.y;this.y=e*a.x-c*a.z;this.z=c*a.y-d*a.x;return this},crossVectors:function(a,b){var c=a.x,d=a.y,e=a.z,f=b.x,g=b.y,h=b.z;this.x=d*h-e*g;this.y=e*f-c*h;this.z=c*g-d*f;return this},
-projectOnVector:function(){var a,b;return function(c){void 0===a&&(a=new THREE.Vector3);a.copy(c).normalize();b=this.dot(a);return this.copy(a).multiplyScalar(b)}}(),projectOnPlane:function(){var a;return function(b){void 0===a&&(a=new THREE.Vector3);a.copy(this).projectOnVector(b);return this.sub(a)}}(),reflect:function(){var a;return function(b){void 0===a&&(a=new THREE.Vector3);return this.sub(a.copy(b).multiplyScalar(2*this.dot(b)))}}(),angleTo:function(a){a=this.dot(a)/(this.length()*a.length());
-return Math.acos(THREE.Math.clamp(a,-1,1))},distanceTo:function(a){return Math.sqrt(this.distanceToSquared(a))},distanceToSquared:function(a){var b=this.x-a.x,c=this.y-a.y;a=this.z-a.z;return b*b+c*c+a*a},setEulerFromRotationMatrix:function(a,b){console.error("THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.")},setEulerFromQuaternion:function(a,b){console.error("THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.")},
-getPositionFromMatrix:function(a){console.warn("THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().");return this.setFromMatrixPosition(a)},getScaleFromMatrix:function(a){console.warn("THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().");return this.setFromMatrixScale(a)},getColumnFromMatrix:function(a,b){console.warn("THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().");return this.setFromMatrixColumn(a,
-b)},setFromMatrixPosition:function(a){this.x=a.elements[12];this.y=a.elements[13];this.z=a.elements[14];return this},setFromMatrixScale:function(a){var b=this.set(a.elements[0],a.elements[1],a.elements[2]).length(),c=this.set(a.elements[4],a.elements[5],a.elements[6]).length();a=this.set(a.elements[8],a.elements[9],a.elements[10]).length();this.x=b;this.y=c;this.z=a;return this},setFromMatrixColumn:function(a,b){var c=4*a,d=b.elements;this.x=d[c];this.y=d[c+1];this.z=d[c+2];return this},equals:function(a){return a.x===
-this.x&&a.y===this.y&&a.z===this.z},fromArray:function(a,b){void 0===b&&(b=0);this.x=a[b];this.y=a[b+1];this.z=a[b+2];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this.x;a[b+1]=this.y;a[b+2]=this.z;return a},clone:function(){return new THREE.Vector3(this.x,this.y,this.z)}};THREE.Vector4=function(a,b,c,d){this.x=a||0;this.y=b||0;this.z=c||0;this.w=void 0!==d?d:1};
-THREE.Vector4.prototype={constructor:THREE.Vector4,set:function(a,b,c,d){this.x=a;this.y=b;this.z=c;this.w=d;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setZ:function(a){this.z=a;return this},setW:function(a){this.w=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;case 2:this.z=b;break;case 3:this.w=b;break;default:throw Error("index is out of range: "+a);}},getComponent:function(a){switch(a){case 0:return this.x;
-case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw Error("index is out of range: "+a);}},copy:function(a){this.x=a.x;this.y=a.y;this.z=a.z;this.w=void 0!==a.w?a.w:1;return this},add:function(a,b){if(void 0!==b)return console.warn("THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;this.z+=a.z;this.w+=a.w;return this},addScalar:function(a){this.x+=a;this.y+=a;this.z+=a;this.w+=a;return this},
-addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;this.z=a.z+b.z;this.w=a.w+b.w;return this},sub:function(a,b){if(void 0!==b)return console.warn("THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(a,b);this.x-=a.x;this.y-=a.y;this.z-=a.z;this.w-=a.w;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;this.z=a.z-b.z;this.w=a.w-b.w;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;this.z*=a;this.w*=a;return this},applyMatrix4:function(a){var b=
-this.x,c=this.y,d=this.z,e=this.w;a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d+a[12]*e;this.y=a[1]*b+a[5]*c+a[9]*d+a[13]*e;this.z=a[2]*b+a[6]*c+a[10]*d+a[14]*e;this.w=a[3]*b+a[7]*c+a[11]*d+a[15]*e;return this},divideScalar:function(a){0!==a?(a=1/a,this.x*=a,this.y*=a,this.z*=a,this.w*=a):(this.z=this.y=this.x=0,this.w=1);return this},setAxisAngleFromQuaternion:function(a){this.w=2*Math.acos(a.w);var b=Math.sqrt(1-a.w*a.w);1E-4>b?(this.x=1,this.z=this.y=0):(this.x=a.x/b,this.y=a.y/b,this.z=a.z/b);return this},
-setAxisAngleFromRotationMatrix:function(a){var b,c,d;a=a.elements;var e=a[0];d=a[4];var f=a[8],g=a[1],h=a[5],k=a[9];c=a[2];b=a[6];var n=a[10];if(.01>Math.abs(d-g)&&.01>Math.abs(f-c)&&.01>Math.abs(k-b)){if(.1>Math.abs(d+g)&&.1>Math.abs(f+c)&&.1>Math.abs(k+b)&&.1>Math.abs(e+h+n-3))return this.set(1,0,0,0),this;a=Math.PI;e=(e+1)/2;h=(h+1)/2;n=(n+1)/2;d=(d+g)/4;f=(f+c)/4;k=(k+b)/4;e>h&&e>n?.01>e?(b=0,d=c=.707106781):(b=Math.sqrt(e),c=d/b,d=f/b):h>n?.01>h?(b=.707106781,c=0,d=.707106781):(c=Math.sqrt(h),
-b=d/c,d=k/c):.01>n?(c=b=.707106781,d=0):(d=Math.sqrt(n),b=f/d,c=k/d);this.set(b,c,d,a);return this}a=Math.sqrt((b-k)*(b-k)+(f-c)*(f-c)+(g-d)*(g-d));.001>Math.abs(a)&&(a=1);this.x=(b-k)/a;this.y=(f-c)/a;this.z=(g-d)/a;this.w=Math.acos((e+h+n-1)/2);return this},min:function(a){this.x>a.x&&(this.x=a.x);this.y>a.y&&(this.y=a.y);this.z>a.z&&(this.z=a.z);this.w>a.w&&(this.w=a.w);return this},max:function(a){this.x<a.x&&(this.x=a.x);this.y<a.y&&(this.y=a.y);this.z<a.z&&(this.z=a.z);this.w<a.w&&(this.w=a.w);
-return this},clamp:function(a,b){this.x<a.x?this.x=a.x:this.x>b.x&&(this.x=b.x);this.y<a.y?this.y=a.y:this.y>b.y&&(this.y=b.y);this.z<a.z?this.z=a.z:this.z>b.z&&(this.z=b.z);this.w<a.w?this.w=a.w:this.w>b.w&&(this.w=b.w);return this},clampScalar:function(){var a,b;return function(c,d){void 0===a&&(a=new THREE.Vector4,b=new THREE.Vector4);a.set(c,c,c,c);b.set(d,d,d,d);return this.clamp(a,b)}}(),floor:function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);this.z=Math.floor(this.z);this.w=Math.floor(this.w);
-return this},ceil:function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);this.z=Math.ceil(this.z);this.w=Math.ceil(this.w);return this},round:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);this.z=Math.round(this.z);this.w=Math.round(this.w);return this},roundToZero:function(){this.x=0>this.x?Math.ceil(this.x):Math.floor(this.x);this.y=0>this.y?Math.ceil(this.y):Math.floor(this.y);this.z=0>this.z?Math.ceil(this.z):Math.floor(this.z);this.w=0>this.w?Math.ceil(this.w):Math.floor(this.w);
-return this},negate:function(){this.x=-this.x;this.y=-this.y;this.z=-this.z;this.w=-this.w;return this},dot:function(a){return this.x*a.x+this.y*a.y+this.z*a.z+this.w*a.w},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)+Math.abs(this.w)},normalize:function(){return this.divideScalar(this.length())},
-setLength:function(a){var b=this.length();0!==b&&a!==b&&this.multiplyScalar(a/b);return this},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;this.z+=(a.z-this.z)*b;this.w+=(a.w-this.w)*b;return this},equals:function(a){return a.x===this.x&&a.y===this.y&&a.z===this.z&&a.w===this.w},fromArray:function(a,b){void 0===b&&(b=0);this.x=a[b];this.y=a[b+1];this.z=a[b+2];this.w=a[b+3];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this.x;a[b+1]=this.y;a[b+2]=
-this.z;a[b+3]=this.w;return a},clone:function(){return new THREE.Vector4(this.x,this.y,this.z,this.w)}};THREE.Euler=function(a,b,c,d){this._x=a||0;this._y=b||0;this._z=c||0;this._order=d||THREE.Euler.DefaultOrder};THREE.Euler.RotationOrders="XYZ YZX ZXY XZY YXZ ZYX".split(" ");THREE.Euler.DefaultOrder="XYZ";
-THREE.Euler.prototype={constructor:THREE.Euler,_x:0,_y:0,_z:0,_order:THREE.Euler.DefaultOrder,get x(){return this._x},set x(a){this._x=a;this.onChangeCallback()},get y(){return this._y},set y(a){this._y=a;this.onChangeCallback()},get z(){return this._z},set z(a){this._z=a;this.onChangeCallback()},get order(){return this._order},set order(a){this._order=a;this.onChangeCallback()},set:function(a,b,c,d){this._x=a;this._y=b;this._z=c;this._order=d||this._order;this.onChangeCallback();return this},copy:function(a){this._x=
-a._x;this._y=a._y;this._z=a._z;this._order=a._order;this.onChangeCallback();return this},setFromRotationMatrix:function(a,b){var c=THREE.Math.clamp,d=a.elements,e=d[0],f=d[4],g=d[8],h=d[1],k=d[5],n=d[9],p=d[2],q=d[6],d=d[10];b=b||this._order;"XYZ"===b?(this._y=Math.asin(c(g,-1,1)),.99999>Math.abs(g)?(this._x=Math.atan2(-n,d),this._z=Math.atan2(-f,e)):(this._x=Math.atan2(q,k),this._z=0)):"YXZ"===b?(this._x=Math.asin(-c(n,-1,1)),.99999>Math.abs(n)?(this._y=Math.atan2(g,d),this._z=Math.atan2(h,k)):(this._y=
-Math.atan2(-p,e),this._z=0)):"ZXY"===b?(this._x=Math.asin(c(q,-1,1)),.99999>Math.abs(q)?(this._y=Math.atan2(-p,d),this._z=Math.atan2(-f,k)):(this._y=0,this._z=Math.atan2(h,e))):"ZYX"===b?(this._y=Math.asin(-c(p,-1,1)),.99999>Math.abs(p)?(this._x=Math.atan2(q,d),this._z=Math.atan2(h,e)):(this._x=0,this._z=Math.atan2(-f,k))):"YZX"===b?(this._z=Math.asin(c(h,-1,1)),.99999>Math.abs(h)?(this._x=Math.atan2(-n,k),this._y=Math.atan2(-p,e)):(this._x=0,this._y=Math.atan2(g,d))):"XZY"===b?(this._z=Math.asin(-c(f,
--1,1)),.99999>Math.abs(f)?(this._x=Math.atan2(q,k),this._y=Math.atan2(g,e)):(this._x=Math.atan2(-n,d),this._y=0)):console.warn("THREE.Euler: .setFromRotationMatrix() given unsupported order: "+b);this._order=b;this.onChangeCallback();return this},setFromQuaternion:function(a,b,c){var d=THREE.Math.clamp,e=a.x*a.x,f=a.y*a.y,g=a.z*a.z,h=a.w*a.w;b=b||this._order;"XYZ"===b?(this._x=Math.atan2(2*(a.x*a.w-a.y*a.z),h-e-f+g),this._y=Math.asin(d(2*(a.x*a.z+a.y*a.w),-1,1)),this._z=Math.atan2(2*(a.z*a.w-a.x*
-a.y),h+e-f-g)):"YXZ"===b?(this._x=Math.asin(d(2*(a.x*a.w-a.y*a.z),-1,1)),this._y=Math.atan2(2*(a.x*a.z+a.y*a.w),h-e-f+g),this._z=Math.atan2(2*(a.x*a.y+a.z*a.w),h-e+f-g)):"ZXY"===b?(this._x=Math.asin(d(2*(a.x*a.w+a.y*a.z),-1,1)),this._y=Math.atan2(2*(a.y*a.w-a.z*a.x),h-e-f+g),this._z=Math.atan2(2*(a.z*a.w-a.x*a.y),h-e+f-g)):"ZYX"===b?(this._x=Math.atan2(2*(a.x*a.w+a.z*a.y),h-e-f+g),this._y=Math.asin(d(2*(a.y*a.w-a.x*a.z),-1,1)),this._z=Math.atan2(2*(a.x*a.y+a.z*a.w),h+e-f-g)):"YZX"===b?(this._x=Math.atan2(2*
-(a.x*a.w-a.z*a.y),h-e+f-g),this._y=Math.atan2(2*(a.y*a.w-a.x*a.z),h+e-f-g),this._z=Math.asin(d(2*(a.x*a.y+a.z*a.w),-1,1))):"XZY"===b?(this._x=Math.atan2(2*(a.x*a.w+a.y*a.z),h-e+f-g),this._y=Math.atan2(2*(a.x*a.z+a.y*a.w),h+e-f-g),this._z=Math.asin(d(2*(a.z*a.w-a.x*a.y),-1,1))):console.warn("THREE.Euler: .setFromQuaternion() given unsupported order: "+b);this._order=b;if(!1!==c)this.onChangeCallback();return this},reorder:function(){var a=new THREE.Quaternion;return function(b){a.setFromEuler(this);
-this.setFromQuaternion(a,b)}}(),equals:function(a){return a._x===this._x&&a._y===this._y&&a._z===this._z&&a._order===this._order},fromArray:function(a){this._x=a[0];this._y=a[1];this._z=a[2];void 0!==a[3]&&(this._order=a[3]);this.onChangeCallback();return this},toArray:function(){return[this._x,this._y,this._z,this._order]},onChange:function(a){this.onChangeCallback=a;return this},onChangeCallback:function(){},clone:function(){return new THREE.Euler(this._x,this._y,this._z,this._order)}};
-THREE.Line3=function(a,b){this.start=void 0!==a?a:new THREE.Vector3;this.end=void 0!==b?b:new THREE.Vector3};
-THREE.Line3.prototype={constructor:THREE.Line3,set:function(a,b){this.start.copy(a);this.end.copy(b);return this},copy:function(a){this.start.copy(a.start);this.end.copy(a.end);return this},center:function(a){return(a||new THREE.Vector3).addVectors(this.start,this.end).multiplyScalar(.5)},delta:function(a){return(a||new THREE.Vector3).subVectors(this.end,this.start)},distanceSq:function(){return this.start.distanceToSquared(this.end)},distance:function(){return this.start.distanceTo(this.end)},at:function(a,
-b){var c=b||new THREE.Vector3;return this.delta(c).multiplyScalar(a).add(this.start)},closestPointToPointParameter:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c,d){a.subVectors(c,this.start);b.subVectors(this.end,this.start);var e=b.dot(b),e=b.dot(a)/e;d&&(e=THREE.Math.clamp(e,0,1));return e}}(),closestPointToPoint:function(a,b,c){a=this.closestPointToPointParameter(a,b);c=c||new THREE.Vector3;return this.delta(c).multiplyScalar(a).add(this.start)},applyMatrix4:function(a){this.start.applyMatrix4(a);
-this.end.applyMatrix4(a);return this},equals:function(a){return a.start.equals(this.start)&&a.end.equals(this.end)},clone:function(){return(new THREE.Line3).copy(this)}};THREE.Box2=function(a,b){this.min=void 0!==a?a:new THREE.Vector2(Infinity,Infinity);this.max=void 0!==b?b:new THREE.Vector2(-Infinity,-Infinity)};
-THREE.Box2.prototype={constructor:THREE.Box2,set:function(a,b){this.min.copy(a);this.max.copy(b);return this},setFromPoints:function(a){this.makeEmpty();for(var b=0,c=a.length;b<c;b++)this.expandByPoint(a[b]);return this},setFromCenterAndSize:function(){var a=new THREE.Vector2;return function(b,c){var d=a.copy(c).multiplyScalar(.5);this.min.copy(b).sub(d);this.max.copy(b).add(d);return this}}(),copy:function(a){this.min.copy(a.min);this.max.copy(a.max);return this},makeEmpty:function(){this.min.x=
-this.min.y=Infinity;this.max.x=this.max.y=-Infinity;return this},empty:function(){return this.max.x<this.min.x||this.max.y<this.min.y},center:function(a){return(a||new THREE.Vector2).addVectors(this.min,this.max).multiplyScalar(.5)},size:function(a){return(a||new THREE.Vector2).subVectors(this.max,this.min)},expandByPoint:function(a){this.min.min(a);this.max.max(a);return this},expandByVector:function(a){this.min.sub(a);this.max.add(a);return this},expandByScalar:function(a){this.min.addScalar(-a);
-this.max.addScalar(a);return this},containsPoint:function(a){return a.x<this.min.x||a.x>this.max.x||a.y<this.min.y||a.y>this.max.y?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y?!0:!1},getParameter:function(a,b){return(b||new THREE.Vector2).set((a.x-this.min.x)/(this.max.x-this.min.x),(a.y-this.min.y)/(this.max.y-this.min.y))},isIntersectionBox:function(a){return a.max.x<this.min.x||a.min.x>this.max.x||a.max.y<this.min.y||a.min.y>
-this.max.y?!1:!0},clampPoint:function(a,b){return(b||new THREE.Vector2).copy(a).clamp(this.min,this.max)},distanceToPoint:function(){var a=new THREE.Vector2;return function(b){return a.copy(b).clamp(this.min,this.max).sub(b).length()}}(),intersect:function(a){this.min.max(a.min);this.max.min(a.max);return this},union:function(a){this.min.min(a.min);this.max.max(a.max);return this},translate:function(a){this.min.add(a);this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&&
-a.max.equals(this.max)},clone:function(){return(new THREE.Box2).copy(this)}};THREE.Box3=function(a,b){this.min=void 0!==a?a:new THREE.Vector3(Infinity,Infinity,Infinity);this.max=void 0!==b?b:new THREE.Vector3(-Infinity,-Infinity,-Infinity)};
-THREE.Box3.prototype={constructor:THREE.Box3,set:function(a,b){this.min.copy(a);this.max.copy(b);return this},setFromPoints:function(a){this.makeEmpty();for(var b=0,c=a.length;b<c;b++)this.expandByPoint(a[b]);return this},setFromCenterAndSize:function(){var a=new THREE.Vector3;return function(b,c){var d=a.copy(c).multiplyScalar(.5);this.min.copy(b).sub(d);this.max.copy(b).add(d);return this}}(),setFromObject:function(){var a=new THREE.Vector3;return function(b){var c=this;b.updateMatrixWorld(!0);
-this.makeEmpty();b.traverse(function(b){var e=b.geometry;if(void 0!==e)if(e instanceof THREE.Geometry)for(var f=e.vertices,e=0,g=f.length;e<g;e++)a.copy(f[e]),a.applyMatrix4(b.matrixWorld),c.expandByPoint(a);else if(e instanceof THREE.BufferGeometry&&void 0!==e.attributes.position)for(f=e.attributes.position.array,e=0,g=f.length;e<g;e+=3)a.set(f[e],f[e+1],f[e+2]),a.applyMatrix4(b.matrixWorld),c.expandByPoint(a)});return this}}(),copy:function(a){this.min.copy(a.min);this.max.copy(a.max);return this},
-makeEmpty:function(){this.min.x=this.min.y=this.min.z=Infinity;this.max.x=this.max.y=this.max.z=-Infinity;return this},empty:function(){return this.max.x<this.min.x||this.max.y<this.min.y||this.max.z<this.min.z},center:function(a){return(a||new THREE.Vector3).addVectors(this.min,this.max).multiplyScalar(.5)},size:function(a){return(a||new THREE.Vector3).subVectors(this.max,this.min)},expandByPoint:function(a){this.min.min(a);this.max.max(a);return this},expandByVector:function(a){this.min.sub(a);
-this.max.add(a);return this},expandByScalar:function(a){this.min.addScalar(-a);this.max.addScalar(a);return this},containsPoint:function(a){return a.x<this.min.x||a.x>this.max.x||a.y<this.min.y||a.y>this.max.y||a.z<this.min.z||a.z>this.max.z?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y&&this.min.z<=a.min.z&&a.max.z<=this.max.z?!0:!1},getParameter:function(a,b){return(b||new THREE.Vector3).set((a.x-this.min.x)/(this.max.x-
-this.min.x),(a.y-this.min.y)/(this.max.y-this.min.y),(a.z-this.min.z)/(this.max.z-this.min.z))},isIntersectionBox:function(a){return a.max.x<this.min.x||a.min.x>this.max.x||a.max.y<this.min.y||a.min.y>this.max.y||a.max.z<this.min.z||a.min.z>this.max.z?!1:!0},clampPoint:function(a,b){return(b||new THREE.Vector3).copy(a).clamp(this.min,this.max)},distanceToPoint:function(){var a=new THREE.Vector3;return function(b){return a.copy(b).clamp(this.min,this.max).sub(b).length()}}(),getBoundingSphere:function(){var a=
-new THREE.Vector3;return function(b){b=b||new THREE.Sphere;b.center=this.center();b.radius=.5*this.size(a).length();return b}}(),intersect:function(a){this.min.max(a.min);this.max.min(a.max);return this},union:function(a){this.min.min(a.min);this.max.max(a.max);return this},applyMatrix4:function(){var a=[new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3];return function(b){a[0].set(this.min.x,this.min.y,
-this.min.z).applyMatrix4(b);a[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(b);a[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(b);a[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(b);a[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(b);a[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(b);a[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(b);a[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(b);this.makeEmpty();this.setFromPoints(a);return this}}(),translate:function(a){this.min.add(a);
-this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&&a.max.equals(this.max)},clone:function(){return(new THREE.Box3).copy(this)}};THREE.Matrix3=function(){this.elements=new Float32Array([1,0,0,0,1,0,0,0,1]);0<arguments.length&&console.error("THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.")};
-THREE.Matrix3.prototype={constructor:THREE.Matrix3,set:function(a,b,c,d,e,f,g,h,k){var n=this.elements;n[0]=a;n[3]=b;n[6]=c;n[1]=d;n[4]=e;n[7]=f;n[2]=g;n[5]=h;n[8]=k;return this},identity:function(){this.set(1,0,0,0,1,0,0,0,1);return this},copy:function(a){a=a.elements;this.set(a[0],a[3],a[6],a[1],a[4],a[7],a[2],a[5],a[8]);return this},multiplyVector3:function(a){console.warn("THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.");return a.applyMatrix3(this)},
-multiplyVector3Array:function(a){console.warn("THREE.Matrix3: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.");return this.applyToVector3Array(a)},applyToVector3Array:function(){var a=new THREE.Vector3;return function(b,c,d){void 0===c&&(c=0);void 0===d&&(d=b.length);for(var e=0;e<d;e+=3,c+=3)a.x=b[c],a.y=b[c+1],a.z=b[c+2],a.applyMatrix3(this),b[c]=a.x,b[c+1]=a.y,b[c+2]=a.z;return b}}(),multiplyScalar:function(a){var b=this.elements;b[0]*=a;b[3]*=a;b[6]*=
-a;b[1]*=a;b[4]*=a;b[7]*=a;b[2]*=a;b[5]*=a;b[8]*=a;return this},determinant:function(){var a=this.elements,b=a[0],c=a[1],d=a[2],e=a[3],f=a[4],g=a[5],h=a[6],k=a[7],a=a[8];return b*f*a-b*g*k-c*e*a+c*g*h+d*e*k-d*f*h},getInverse:function(a,b){var c=a.elements,d=this.elements;d[0]=c[10]*c[5]-c[6]*c[9];d[1]=-c[10]*c[1]+c[2]*c[9];d[2]=c[6]*c[1]-c[2]*c[5];d[3]=-c[10]*c[4]+c[6]*c[8];d[4]=c[10]*c[0]-c[2]*c[8];d[5]=-c[6]*c[0]+c[2]*c[4];d[6]=c[9]*c[4]-c[5]*c[8];d[7]=-c[9]*c[0]+c[1]*c[8];d[8]=c[5]*c[0]-c[1]*c[4];
-c=c[0]*d[0]+c[1]*d[3]+c[2]*d[6];if(0===c){if(b)throw Error("Matrix3.getInverse(): can't invert matrix, determinant is 0");console.warn("Matrix3.getInverse(): can't invert matrix, determinant is 0");this.identity();return this}this.multiplyScalar(1/c);return this},transpose:function(){var a,b=this.elements;a=b[1];b[1]=b[3];b[3]=a;a=b[2];b[2]=b[6];b[6]=a;a=b[5];b[5]=b[7];b[7]=a;return this},flattenToArrayOffset:function(a,b){var c=this.elements;a[b]=c[0];a[b+1]=c[1];a[b+2]=c[2];a[b+3]=c[3];a[b+4]=c[4];
-a[b+5]=c[5];a[b+6]=c[6];a[b+7]=c[7];a[b+8]=c[8];return a},getNormalMatrix:function(a){this.getInverse(a).transpose();return this},transposeIntoArray:function(a){var b=this.elements;a[0]=b[0];a[1]=b[3];a[2]=b[6];a[3]=b[1];a[4]=b[4];a[5]=b[7];a[6]=b[2];a[7]=b[5];a[8]=b[8];return this},fromArray:function(a){this.elements.set(a);return this},toArray:function(){var a=this.elements;return[a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]]},clone:function(){return(new THREE.Matrix3).fromArray(this.elements)}};
-THREE.Matrix4=function(){this.elements=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);0<arguments.length&&console.error("THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.")};
-THREE.Matrix4.prototype={constructor:THREE.Matrix4,set:function(a,b,c,d,e,f,g,h,k,n,p,q,m,r,t,s){var u=this.elements;u[0]=a;u[4]=b;u[8]=c;u[12]=d;u[1]=e;u[5]=f;u[9]=g;u[13]=h;u[2]=k;u[6]=n;u[10]=p;u[14]=q;u[3]=m;u[7]=r;u[11]=t;u[15]=s;return this},identity:function(){this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1);return this},copy:function(a){this.elements.set(a.elements);return this},extractPosition:function(a){console.warn("THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().");return this.copyPosition(a)},
-copyPosition:function(a){var b=this.elements;a=a.elements;b[12]=a[12];b[13]=a[13];b[14]=a[14];return this},extractRotation:function(){var a=new THREE.Vector3;return function(b){var c=this.elements;b=b.elements;var d=1/a.set(b[0],b[1],b[2]).length(),e=1/a.set(b[4],b[5],b[6]).length(),f=1/a.set(b[8],b[9],b[10]).length();c[0]=b[0]*d;c[1]=b[1]*d;c[2]=b[2]*d;c[4]=b[4]*e;c[5]=b[5]*e;c[6]=b[6]*e;c[8]=b[8]*f;c[9]=b[9]*f;c[10]=b[10]*f;return this}}(),makeRotationFromEuler:function(a){!1===a instanceof THREE.Euler&&
-console.error("THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.");var b=this.elements,c=a.x,d=a.y,e=a.z,f=Math.cos(c),c=Math.sin(c),g=Math.cos(d),d=Math.sin(d),h=Math.cos(e),e=Math.sin(e);if("XYZ"===a.order){a=f*h;var k=f*e,n=c*h,p=c*e;b[0]=g*h;b[4]=-g*e;b[8]=d;b[1]=k+n*d;b[5]=a-p*d;b[9]=-c*g;b[2]=p-a*d;b[6]=n+k*d;b[10]=f*g}else"YXZ"===a.order?(a=g*h,k=g*e,n=d*h,p=d*e,b[0]=a+p*c,b[4]=n*c-k,b[8]=f*d,b[1]=f*e,b[5]=f*h,b[9]=-c,b[2]=k*c-n,b[6]=p+a*c,
-b[10]=f*g):"ZXY"===a.order?(a=g*h,k=g*e,n=d*h,p=d*e,b[0]=a-p*c,b[4]=-f*e,b[8]=n+k*c,b[1]=k+n*c,b[5]=f*h,b[9]=p-a*c,b[2]=-f*d,b[6]=c,b[10]=f*g):"ZYX"===a.order?(a=f*h,k=f*e,n=c*h,p=c*e,b[0]=g*h,b[4]=n*d-k,b[8]=a*d+p,b[1]=g*e,b[5]=p*d+a,b[9]=k*d-n,b[2]=-d,b[6]=c*g,b[10]=f*g):"YZX"===a.order?(a=f*g,k=f*d,n=c*g,p=c*d,b[0]=g*h,b[4]=p-a*e,b[8]=n*e+k,b[1]=e,b[5]=f*h,b[9]=-c*h,b[2]=-d*h,b[6]=k*e+n,b[10]=a-p*e):"XZY"===a.order&&(a=f*g,k=f*d,n=c*g,p=c*d,b[0]=g*h,b[4]=-e,b[8]=d*h,b[1]=a*e+p,b[5]=f*h,b[9]=k*
-e-n,b[2]=n*e-k,b[6]=c*h,b[10]=p*e+a);b[3]=0;b[7]=0;b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return this},setRotationFromQuaternion:function(a){console.warn("THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().");return this.makeRotationFromQuaternion(a)},makeRotationFromQuaternion:function(a){var b=this.elements,c=a.x,d=a.y,e=a.z,f=a.w,g=c+c,h=d+d,k=e+e;a=c*g;var n=c*h,c=c*k,p=d*h,d=d*k,e=e*k,g=f*g,h=f*h,f=f*k;b[0]=1-(p+e);b[4]=n-f;b[8]=c+h;b[1]=n+f;b[5]=1-
-(a+e);b[9]=d-g;b[2]=c-h;b[6]=d+g;b[10]=1-(a+p);b[3]=0;b[7]=0;b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return this},lookAt:function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Vector3;return function(d,e,f){var g=this.elements;c.subVectors(d,e).normalize();0===c.length()&&(c.z=1);a.crossVectors(f,c).normalize();0===a.length()&&(c.x+=1E-4,a.crossVectors(f,c).normalize());b.crossVectors(c,a);g[0]=a.x;g[4]=b.x;g[8]=c.x;g[1]=a.y;g[5]=b.y;g[9]=c.y;g[2]=a.z;g[6]=b.z;g[10]=c.z;return this}}(),
-multiply:function(a,b){return void 0!==b?(console.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."),this.multiplyMatrices(a,b)):this.multiplyMatrices(this,a)},multiplyMatrices:function(a,b){var c=a.elements,d=b.elements,e=this.elements,f=c[0],g=c[4],h=c[8],k=c[12],n=c[1],p=c[5],q=c[9],m=c[13],r=c[2],t=c[6],s=c[10],u=c[14],v=c[3],y=c[7],G=c[11],c=c[15],w=d[0],K=d[4],x=d[8],D=d[12],E=d[1],A=d[5],B=d[9],F=d[13],R=d[2],H=d[6],C=d[10],T=d[14],Q=d[3],
-O=d[7],S=d[11],d=d[15];e[0]=f*w+g*E+h*R+k*Q;e[4]=f*K+g*A+h*H+k*O;e[8]=f*x+g*B+h*C+k*S;e[12]=f*D+g*F+h*T+k*d;e[1]=n*w+p*E+q*R+m*Q;e[5]=n*K+p*A+q*H+m*O;e[9]=n*x+p*B+q*C+m*S;e[13]=n*D+p*F+q*T+m*d;e[2]=r*w+t*E+s*R+u*Q;e[6]=r*K+t*A+s*H+u*O;e[10]=r*x+t*B+s*C+u*S;e[14]=r*D+t*F+s*T+u*d;e[3]=v*w+y*E+G*R+c*Q;e[7]=v*K+y*A+G*H+c*O;e[11]=v*x+y*B+G*C+c*S;e[15]=v*D+y*F+G*T+c*d;return this},multiplyToArray:function(a,b,c){var d=this.elements;this.multiplyMatrices(a,b);c[0]=d[0];c[1]=d[1];c[2]=d[2];c[3]=d[3];c[4]=
-d[4];c[5]=d[5];c[6]=d[6];c[7]=d[7];c[8]=d[8];c[9]=d[9];c[10]=d[10];c[11]=d[11];c[12]=d[12];c[13]=d[13];c[14]=d[14];c[15]=d[15];return this},multiplyScalar:function(a){var b=this.elements;b[0]*=a;b[4]*=a;b[8]*=a;b[12]*=a;b[1]*=a;b[5]*=a;b[9]*=a;b[13]*=a;b[2]*=a;b[6]*=a;b[10]*=a;b[14]*=a;b[3]*=a;b[7]*=a;b[11]*=a;b[15]*=a;return this},multiplyVector3:function(a){console.warn("THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.");
-return a.applyProjection(this)},multiplyVector4:function(a){console.warn("THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.");return a.applyMatrix4(this)},multiplyVector3Array:function(a){console.warn("THREE.Matrix4: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.");return this.applyToVector3Array(a)},applyToVector3Array:function(){var a=new THREE.Vector3;return function(b,c,d){void 0===c&&(c=0);void 0===d&&(d=
-b.length);for(var e=0;e<d;e+=3,c+=3)a.x=b[c],a.y=b[c+1],a.z=b[c+2],a.applyMatrix4(this),b[c]=a.x,b[c+1]=a.y,b[c+2]=a.z;return b}}(),rotateAxis:function(a){console.warn("THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.");a.transformDirection(this)},crossVector:function(a){console.warn("THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.");return a.applyMatrix4(this)},determinant:function(){var a=this.elements,b=
-a[0],c=a[4],d=a[8],e=a[12],f=a[1],g=a[5],h=a[9],k=a[13],n=a[2],p=a[6],q=a[10],m=a[14];return a[3]*(+e*h*p-d*k*p-e*g*q+c*k*q+d*g*m-c*h*m)+a[7]*(+b*h*m-b*k*q+e*f*q-d*f*m+d*k*n-e*h*n)+a[11]*(+b*k*p-b*g*m-e*f*p+c*f*m+e*g*n-c*k*n)+a[15]*(-d*g*n-b*h*p+b*g*q+d*f*p-c*f*q+c*h*n)},transpose:function(){var a=this.elements,b;b=a[1];a[1]=a[4];a[4]=b;b=a[2];a[2]=a[8];a[8]=b;b=a[6];a[6]=a[9];a[9]=b;b=a[3];a[3]=a[12];a[12]=b;b=a[7];a[7]=a[13];a[13]=b;b=a[11];a[11]=a[14];a[14]=b;return this},flattenToArrayOffset:function(a,
-b){var c=this.elements;a[b]=c[0];a[b+1]=c[1];a[b+2]=c[2];a[b+3]=c[3];a[b+4]=c[4];a[b+5]=c[5];a[b+6]=c[6];a[b+7]=c[7];a[b+8]=c[8];a[b+9]=c[9];a[b+10]=c[10];a[b+11]=c[11];a[b+12]=c[12];a[b+13]=c[13];a[b+14]=c[14];a[b+15]=c[15];return a},getPosition:function(){var a=new THREE.Vector3;return function(){console.warn("THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.");var b=this.elements;return a.set(b[12],b[13],b[14])}}(),setPosition:function(a){var b=
-this.elements;b[12]=a.x;b[13]=a.y;b[14]=a.z;return this},getInverse:function(a,b){var c=this.elements,d=a.elements,e=d[0],f=d[4],g=d[8],h=d[12],k=d[1],n=d[5],p=d[9],q=d[13],m=d[2],r=d[6],t=d[10],s=d[14],u=d[3],v=d[7],y=d[11],d=d[15];c[0]=p*s*v-q*t*v+q*r*y-n*s*y-p*r*d+n*t*d;c[4]=h*t*v-g*s*v-h*r*y+f*s*y+g*r*d-f*t*d;c[8]=g*q*v-h*p*v+h*n*y-f*q*y-g*n*d+f*p*d;c[12]=h*p*r-g*q*r-h*n*t+f*q*t+g*n*s-f*p*s;c[1]=q*t*u-p*s*u-q*m*y+k*s*y+p*m*d-k*t*d;c[5]=g*s*u-h*t*u+h*m*y-e*s*y-g*m*d+e*t*d;c[9]=h*p*u-g*q*u-h*k*
-y+e*q*y+g*k*d-e*p*d;c[13]=g*q*m-h*p*m+h*k*t-e*q*t-g*k*s+e*p*s;c[2]=n*s*u-q*r*u+q*m*v-k*s*v-n*m*d+k*r*d;c[6]=h*r*u-f*s*u-h*m*v+e*s*v+f*m*d-e*r*d;c[10]=f*q*u-h*n*u+h*k*v-e*q*v-f*k*d+e*n*d;c[14]=h*n*m-f*q*m-h*k*r+e*q*r+f*k*s-e*n*s;c[3]=p*r*u-n*t*u-p*m*v+k*t*v+n*m*y-k*r*y;c[7]=f*t*u-g*r*u+g*m*v-e*t*v-f*m*y+e*r*y;c[11]=g*n*u-f*p*u-g*k*v+e*p*v+f*k*y-e*n*y;c[15]=f*p*m-g*n*m+g*k*r-e*p*r-f*k*t+e*n*t;c=e*c[0]+k*c[4]+m*c[8]+u*c[12];if(0==c){if(b)throw Error("Matrix4.getInverse(): can't invert matrix, determinant is 0");
-console.warn("Matrix4.getInverse(): can't invert matrix, determinant is 0");this.identity();return this}this.multiplyScalar(1/c);return this},translate:function(a){console.warn("THREE.Matrix4: .translate() has been removed.")},rotateX:function(a){console.warn("THREE.Matrix4: .rotateX() has been removed.")},rotateY:function(a){console.warn("THREE.Matrix4: .rotateY() has been removed.")},rotateZ:function(a){console.warn("THREE.Matrix4: .rotateZ() has been removed.")},rotateByAxis:function(a,b){console.warn("THREE.Matrix4: .rotateByAxis() has been removed.")},
-scale:function(a){var b=this.elements,c=a.x,d=a.y;a=a.z;b[0]*=c;b[4]*=d;b[8]*=a;b[1]*=c;b[5]*=d;b[9]*=a;b[2]*=c;b[6]*=d;b[10]*=a;b[3]*=c;b[7]*=d;b[11]*=a;return this},getMaxScaleOnAxis:function(){var a=this.elements;return Math.sqrt(Math.max(a[0]*a[0]+a[1]*a[1]+a[2]*a[2],Math.max(a[4]*a[4]+a[5]*a[5]+a[6]*a[6],a[8]*a[8]+a[9]*a[9]+a[10]*a[10])))},makeTranslation:function(a,b,c){this.set(1,0,0,a,0,1,0,b,0,0,1,c,0,0,0,1);return this},makeRotationX:function(a){var b=Math.cos(a);a=Math.sin(a);this.set(1,
-0,0,0,0,b,-a,0,0,a,b,0,0,0,0,1);return this},makeRotationY:function(a){var b=Math.cos(a);a=Math.sin(a);this.set(b,0,a,0,0,1,0,0,-a,0,b,0,0,0,0,1);return this},makeRotationZ:function(a){var b=Math.cos(a);a=Math.sin(a);this.set(b,-a,0,0,a,b,0,0,0,0,1,0,0,0,0,1);return this},makeRotationAxis:function(a,b){var c=Math.cos(b),d=Math.sin(b),e=1-c,f=a.x,g=a.y,h=a.z,k=e*f,n=e*g;this.set(k*f+c,k*g-d*h,k*h+d*g,0,k*g+d*h,n*g+c,n*h-d*f,0,k*h-d*g,n*h+d*f,e*h*h+c,0,0,0,0,1);return this},makeScale:function(a,b,c){this.set(a,
-0,0,0,0,b,0,0,0,0,c,0,0,0,0,1);return this},compose:function(a,b,c){this.makeRotationFromQuaternion(b);this.scale(c);this.setPosition(a);return this},decompose:function(){var a=new THREE.Vector3,b=new THREE.Matrix4;return function(c,d,e){var f=this.elements,g=a.set(f[0],f[1],f[2]).length(),h=a.set(f[4],f[5],f[6]).length(),k=a.set(f[8],f[9],f[10]).length();0>this.determinant()&&(g=-g);c.x=f[12];c.y=f[13];c.z=f[14];b.elements.set(this.elements);c=1/g;var f=1/h,n=1/k;b.elements[0]*=c;b.elements[1]*=
-c;b.elements[2]*=c;b.elements[4]*=f;b.elements[5]*=f;b.elements[6]*=f;b.elements[8]*=n;b.elements[9]*=n;b.elements[10]*=n;d.setFromRotationMatrix(b);e.x=g;e.y=h;e.z=k;return this}}(),makeFrustum:function(a,b,c,d,e,f){var g=this.elements;g[0]=2*e/(b-a);g[4]=0;g[8]=(b+a)/(b-a);g[12]=0;g[1]=0;g[5]=2*e/(d-c);g[9]=(d+c)/(d-c);g[13]=0;g[2]=0;g[6]=0;g[10]=-(f+e)/(f-e);g[14]=-2*f*e/(f-e);g[3]=0;g[7]=0;g[11]=-1;g[15]=0;return this},makePerspective:function(a,b,c,d){a=c*Math.tan(THREE.Math.degToRad(.5*a));
-var e=-a;return this.makeFrustum(e*b,a*b,e,a,c,d)},makeOrthographic:function(a,b,c,d,e,f){var g=this.elements,h=b-a,k=c-d,n=f-e;g[0]=2/h;g[4]=0;g[8]=0;g[12]=-((b+a)/h);g[1]=0;g[5]=2/k;g[9]=0;g[13]=-((c+d)/k);g[2]=0;g[6]=0;g[10]=-2/n;g[14]=-((f+e)/n);g[3]=0;g[7]=0;g[11]=0;g[15]=1;return this},fromArray:function(a){this.elements.set(a);return this},toArray:function(){var a=this.elements;return[a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9],a[10],a[11],a[12],a[13],a[14],a[15]]},clone:function(){return(new THREE.Matrix4).fromArray(this.elements)}};
-THREE.Ray=function(a,b){this.origin=void 0!==a?a:new THREE.Vector3;this.direction=void 0!==b?b:new THREE.Vector3};
-THREE.Ray.prototype={constructor:THREE.Ray,set:function(a,b){this.origin.copy(a);this.direction.copy(b);return this},copy:function(a){this.origin.copy(a.origin);this.direction.copy(a.direction);return this},at:function(a,b){return(b||new THREE.Vector3).copy(this.direction).multiplyScalar(a).add(this.origin)},recast:function(){var a=new THREE.Vector3;return function(b){this.origin.copy(this.at(b,a));return this}}(),closestPointToPoint:function(a,b){var c=b||new THREE.Vector3;c.subVectors(a,this.origin);
-var d=c.dot(this.direction);return 0>d?c.copy(this.origin):c.copy(this.direction).multiplyScalar(d).add(this.origin)},distanceToPoint:function(){var a=new THREE.Vector3;return function(b){var c=a.subVectors(b,this.origin).dot(this.direction);if(0>c)return this.origin.distanceTo(b);a.copy(this.direction).multiplyScalar(c).add(this.origin);return a.distanceTo(b)}}(),distanceSqToSegment:function(a,b,c,d){var e=a.clone().add(b).multiplyScalar(.5),f=b.clone().sub(a).normalize(),g=.5*a.distanceTo(b),h=
-this.origin.clone().sub(e);a=-this.direction.dot(f);b=h.dot(this.direction);var k=-h.dot(f),n=h.lengthSq(),p=Math.abs(1-a*a),q,m;0<=p?(h=a*k-b,q=a*b-k,m=g*p,0<=h?q>=-m?q<=m?(g=1/p,h*=g,q*=g,a=h*(h+a*q+2*b)+q*(a*h+q+2*k)+n):(q=g,h=Math.max(0,-(a*q+b)),a=-h*h+q*(q+2*k)+n):(q=-g,h=Math.max(0,-(a*q+b)),a=-h*h+q*(q+2*k)+n):q<=-m?(h=Math.max(0,-(-a*g+b)),q=0<h?-g:Math.min(Math.max(-g,-k),g),a=-h*h+q*(q+2*k)+n):q<=m?(h=0,q=Math.min(Math.max(-g,-k),g),a=q*(q+2*k)+n):(h=Math.max(0,-(a*g+b)),q=0<h?g:Math.min(Math.max(-g,
--k),g),a=-h*h+q*(q+2*k)+n)):(q=0<a?-g:g,h=Math.max(0,-(a*q+b)),a=-h*h+q*(q+2*k)+n);c&&c.copy(this.direction.clone().multiplyScalar(h).add(this.origin));d&&d.copy(f.clone().multiplyScalar(q).add(e));return a},isIntersectionSphere:function(a){return this.distanceToPoint(a.center)<=a.radius},intersectSphere:function(){var a=new THREE.Vector3;return function(b,c){a.subVectors(b.center,this.origin);var d=a.dot(this.direction),e=a.dot(a)-d*d,f=b.radius*b.radius;if(e>f)return null;f=Math.sqrt(f-e);e=d-f;
-d+=f;return 0>e&&0>d?null:0>e?this.at(d,c):this.at(e,c)}}(),isIntersectionPlane:function(a){var b=a.distanceToPoint(this.origin);return 0===b||0>a.normal.dot(this.direction)*b?!0:!1},distanceToPlane:function(a){var b=a.normal.dot(this.direction);if(0==b)return 0==a.distanceToPoint(this.origin)?0:null;a=-(this.origin.dot(a.normal)+a.constant)/b;return 0<=a?a:null},intersectPlane:function(a,b){var c=this.distanceToPlane(a);return null===c?null:this.at(c,b)},isIntersectionBox:function(){var a=new THREE.Vector3;
-return function(b){return null!==this.intersectBox(b,a)}}(),intersectBox:function(a,b){var c,d,e,f,g;d=1/this.direction.x;f=1/this.direction.y;g=1/this.direction.z;var h=this.origin;0<=d?(c=(a.min.x-h.x)*d,d*=a.max.x-h.x):(c=(a.max.x-h.x)*d,d*=a.min.x-h.x);0<=f?(e=(a.min.y-h.y)*f,f*=a.max.y-h.y):(e=(a.max.y-h.y)*f,f*=a.min.y-h.y);if(c>f||e>d)return null;if(e>c||c!==c)c=e;if(f<d||d!==d)d=f;0<=g?(e=(a.min.z-h.z)*g,g*=a.max.z-h.z):(e=(a.max.z-h.z)*g,g*=a.min.z-h.z);if(c>g||e>d)return null;if(e>c||c!==
-c)c=e;if(g<d||d!==d)d=g;return 0>d?null:this.at(0<=c?c:d,b)},intersectTriangle:function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Vector3,d=new THREE.Vector3;return function(e,f,g,h,k){b.subVectors(f,e);c.subVectors(g,e);d.crossVectors(b,c);f=this.direction.dot(d);if(0<f){if(h)return null;h=1}else if(0>f)h=-1,f=-f;else return null;a.subVectors(this.origin,e);e=h*this.direction.dot(c.crossVectors(a,c));if(0>e)return null;g=h*this.direction.dot(b.cross(a));if(0>g||e+g>f)return null;
-e=-h*a.dot(d);return 0>e?null:this.at(e/f,k)}}(),applyMatrix4:function(a){this.direction.add(this.origin).applyMatrix4(a);this.origin.applyMatrix4(a);this.direction.sub(this.origin);this.direction.normalize();return this},equals:function(a){return a.origin.equals(this.origin)&&a.direction.equals(this.direction)},clone:function(){return(new THREE.Ray).copy(this)}};THREE.Sphere=function(a,b){this.center=void 0!==a?a:new THREE.Vector3;this.radius=void 0!==b?b:0};
-THREE.Sphere.prototype={constructor:THREE.Sphere,set:function(a,b){this.center.copy(a);this.radius=b;return this},setFromPoints:function(){var a=new THREE.Box3;return function(b,c){var d=this.center;void 0!==c?d.copy(c):a.setFromPoints(b).center(d);for(var e=0,f=0,g=b.length;f<g;f++)e=Math.max(e,d.distanceToSquared(b[f]));this.radius=Math.sqrt(e);return this}}(),copy:function(a){this.center.copy(a.center);this.radius=a.radius;return this},empty:function(){return 0>=this.radius},containsPoint:function(a){return a.distanceToSquared(this.center)<=
-this.radius*this.radius},distanceToPoint:function(a){return a.distanceTo(this.center)-this.radius},intersectsSphere:function(a){var b=this.radius+a.radius;return a.center.distanceToSquared(this.center)<=b*b},clampPoint:function(a,b){var c=this.center.distanceToSquared(a),d=b||new THREE.Vector3;d.copy(a);c>this.radius*this.radius&&(d.sub(this.center).normalize(),d.multiplyScalar(this.radius).add(this.center));return d},getBoundingBox:function(a){a=a||new THREE.Box3;a.set(this.center,this.center);a.expandByScalar(this.radius);
-return a},applyMatrix4:function(a){this.center.applyMatrix4(a);this.radius*=a.getMaxScaleOnAxis();return this},translate:function(a){this.center.add(a);return this},equals:function(a){return a.center.equals(this.center)&&a.radius===this.radius},clone:function(){return(new THREE.Sphere).copy(this)}};
-THREE.Frustum=function(a,b,c,d,e,f){this.planes=[void 0!==a?a:new THREE.Plane,void 0!==b?b:new THREE.Plane,void 0!==c?c:new THREE.Plane,void 0!==d?d:new THREE.Plane,void 0!==e?e:new THREE.Plane,void 0!==f?f:new THREE.Plane]};
-THREE.Frustum.prototype={constructor:THREE.Frustum,set:function(a,b,c,d,e,f){var g=this.planes;g[0].copy(a);g[1].copy(b);g[2].copy(c);g[3].copy(d);g[4].copy(e);g[5].copy(f);return this},copy:function(a){for(var b=this.planes,c=0;6>c;c++)b[c].copy(a.planes[c]);return this},setFromMatrix:function(a){var b=this.planes,c=a.elements;a=c[0];var d=c[1],e=c[2],f=c[3],g=c[4],h=c[5],k=c[6],n=c[7],p=c[8],q=c[9],m=c[10],r=c[11],t=c[12],s=c[13],u=c[14],c=c[15];b[0].setComponents(f-a,n-g,r-p,c-t).normalize();b[1].setComponents(f+
-a,n+g,r+p,c+t).normalize();b[2].setComponents(f+d,n+h,r+q,c+s).normalize();b[3].setComponents(f-d,n-h,r-q,c-s).normalize();b[4].setComponents(f-e,n-k,r-m,c-u).normalize();b[5].setComponents(f+e,n+k,r+m,c+u).normalize();return this},intersectsObject:function(){var a=new THREE.Sphere;return function(b){var c=b.geometry;null===c.boundingSphere&&c.computeBoundingSphere();a.copy(c.boundingSphere);a.applyMatrix4(b.matrixWorld);return this.intersectsSphere(a)}}(),intersectsSphere:function(a){var b=this.planes,
-c=a.center;a=-a.radius;for(var d=0;6>d;d++)if(b[d].distanceToPoint(c)<a)return!1;return!0},intersectsBox:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c){for(var d=this.planes,e=0;6>e;e++){var f=d[e];a.x=0<f.normal.x?c.min.x:c.max.x;b.x=0<f.normal.x?c.max.x:c.min.x;a.y=0<f.normal.y?c.min.y:c.max.y;b.y=0<f.normal.y?c.max.y:c.min.y;a.z=0<f.normal.z?c.min.z:c.max.z;b.z=0<f.normal.z?c.max.z:c.min.z;var g=f.distanceToPoint(a),f=f.distanceToPoint(b);if(0>g&&0>f)return!1}return!0}}(),
-containsPoint:function(a){for(var b=this.planes,c=0;6>c;c++)if(0>b[c].distanceToPoint(a))return!1;return!0},clone:function(){return(new THREE.Frustum).copy(this)}};THREE.Plane=function(a,b){this.normal=void 0!==a?a:new THREE.Vector3(1,0,0);this.constant=void 0!==b?b:0};
-THREE.Plane.prototype={constructor:THREE.Plane,set:function(a,b){this.normal.copy(a);this.constant=b;return this},setComponents:function(a,b,c,d){this.normal.set(a,b,c);this.constant=d;return this},setFromNormalAndCoplanarPoint:function(a,b){this.normal.copy(a);this.constant=-b.dot(this.normal);return this},setFromCoplanarPoints:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c,d,e){d=a.subVectors(e,d).cross(b.subVectors(c,d)).normalize();this.setFromNormalAndCoplanarPoint(d,
-c);return this}}(),copy:function(a){this.normal.copy(a.normal);this.constant=a.constant;return this},normalize:function(){var a=1/this.normal.length();this.normal.multiplyScalar(a);this.constant*=a;return this},negate:function(){this.constant*=-1;this.normal.negate();return this},distanceToPoint:function(a){return this.normal.dot(a)+this.constant},distanceToSphere:function(a){return this.distanceToPoint(a.center)-a.radius},projectPoint:function(a,b){return this.orthoPoint(a,b).sub(a).negate()},orthoPoint:function(a,
-b){var c=this.distanceToPoint(a);return(b||new THREE.Vector3).copy(this.normal).multiplyScalar(c)},isIntersectionLine:function(a){var b=this.distanceToPoint(a.start);a=this.distanceToPoint(a.end);return 0>b&&0<a||0>a&&0<b},intersectLine:function(){var a=new THREE.Vector3;return function(b,c){var d=c||new THREE.Vector3,e=b.delta(a),f=this.normal.dot(e);if(0==f){if(0==this.distanceToPoint(b.start))return d.copy(b.start)}else return f=-(b.start.dot(this.normal)+this.constant)/f,0>f||1<f?void 0:d.copy(e).multiplyScalar(f).add(b.start)}}(),
-coplanarPoint:function(a){return(a||new THREE.Vector3).copy(this.normal).multiplyScalar(-this.constant)},applyMatrix4:function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Matrix3;return function(d,e){var f=e||c.getNormalMatrix(d),f=a.copy(this.normal).applyMatrix3(f),g=this.coplanarPoint(b);g.applyMatrix4(d);this.setFromNormalAndCoplanarPoint(f,g);return this}}(),translate:function(a){this.constant-=a.dot(this.normal);return this},equals:function(a){return a.normal.equals(this.normal)&&
-a.constant==this.constant},clone:function(){return(new THREE.Plane).copy(this)}};
-THREE.Math={generateUUID:function(){var a="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""),b=Array(36),c=0,d;return function(){for(var e=0;36>e;e++)8==e||13==e||18==e||23==e?b[e]="-":14==e?b[e]="4":(2>=c&&(c=33554432+16777216*Math.random()|0),d=c&15,c>>=4,b[e]=a[19==e?d&3|8:d]);return b.join("")}}(),clamp:function(a,b,c){return a<b?b:a>c?c:a},clampBottom:function(a,b){return a<b?b:a},mapLinear:function(a,b,c,d,e){return d+(a-b)*(e-d)/(c-b)},smoothstep:function(a,b,c){if(a<=
-b)return 0;if(a>=c)return 1;a=(a-b)/(c-b);return a*a*(3-2*a)},smootherstep:function(a,b,c){if(a<=b)return 0;if(a>=c)return 1;a=(a-b)/(c-b);return a*a*a*(a*(6*a-15)+10)},random16:function(){return(65280*Math.random()+255*Math.random())/65535},randInt:function(a,b){return a+Math.floor(Math.random()*(b-a+1))},randFloat:function(a,b){return a+Math.random()*(b-a)},randFloatSpread:function(a){return a*(.5-Math.random())},degToRad:function(){var a=Math.PI/180;return function(b){return b*a}}(),radToDeg:function(){var a=
-180/Math.PI;return function(b){return b*a}}(),isPowerOfTwo:function(a){return 0===(a&a-1)&&0!==a}};
-THREE.Spline=function(a){function b(a,b,c,d,e,f,g){a=.5*(c-a);d=.5*(d-b);return(2*(b-c)+a+d)*g+(-3*(b-c)-2*a-d)*f+a*e+b}this.points=a;var c=[],d={x:0,y:0,z:0},e,f,g,h,k,n,p,q,m;this.initFromArray=function(a){this.points=[];for(var b=0;b<a.length;b++)this.points[b]={x:a[b][0],y:a[b][1],z:a[b][2]}};this.getPoint=function(a){e=(this.points.length-1)*a;f=Math.floor(e);g=e-f;c[0]=0===f?f:f-1;c[1]=f;c[2]=f>this.points.length-2?this.points.length-1:f+1;c[3]=f>this.points.length-3?this.points.length-1:f+
-2;n=this.points[c[0]];p=this.points[c[1]];q=this.points[c[2]];m=this.points[c[3]];h=g*g;k=g*h;d.x=b(n.x,p.x,q.x,m.x,g,h,k);d.y=b(n.y,p.y,q.y,m.y,g,h,k);d.z=b(n.z,p.z,q.z,m.z,g,h,k);return d};this.getControlPointsArray=function(){var a,b,c=this.points.length,d=[];for(a=0;a<c;a++)b=this.points[a],d[a]=[b.x,b.y,b.z];return d};this.getLength=function(a){var b,c,d,e=b=b=0,f=new THREE.Vector3,g=new THREE.Vector3,h=[],k=0;h[0]=0;a||(a=100);c=this.points.length*a;f.copy(this.points[0]);for(a=1;a<c;a++)b=
-a/c,d=this.getPoint(b),g.copy(d),k+=g.distanceTo(f),f.copy(d),b*=this.points.length-1,b=Math.floor(b),b!=e&&(h[b]=k,e=b);h[h.length]=k;return{chunks:h,total:k}};this.reparametrizeByArcLength=function(a){var b,c,d,e,f,g,h=[],k=new THREE.Vector3,m=this.getLength();h.push(k.copy(this.points[0]).clone());for(b=1;b<this.points.length;b++){c=m.chunks[b]-m.chunks[b-1];g=Math.ceil(a*c/m.total);e=(b-1)/(this.points.length-1);f=b/(this.points.length-1);for(c=1;c<g-1;c++)d=e+1/g*c*(f-e),d=this.getPoint(d),h.push(k.copy(d).clone());
-h.push(k.copy(this.points[b]).clone())}this.points=h}};THREE.Triangle=function(a,b,c){this.a=void 0!==a?a:new THREE.Vector3;this.b=void 0!==b?b:new THREE.Vector3;this.c=void 0!==c?c:new THREE.Vector3};THREE.Triangle.normal=function(){var a=new THREE.Vector3;return function(b,c,d,e){e=e||new THREE.Vector3;e.subVectors(d,c);a.subVectors(b,c);e.cross(a);b=e.lengthSq();return 0<b?e.multiplyScalar(1/Math.sqrt(b)):e.set(0,0,0)}}();
-THREE.Triangle.barycoordFromPoint=function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Vector3;return function(d,e,f,g,h){a.subVectors(g,e);b.subVectors(f,e);c.subVectors(d,e);d=a.dot(a);e=a.dot(b);f=a.dot(c);var k=b.dot(b);g=b.dot(c);var n=d*k-e*e;h=h||new THREE.Vector3;if(0==n)return h.set(-2,-1,-1);n=1/n;k=(k*f-e*g)*n;d=(d*g-e*f)*n;return h.set(1-k-d,d,k)}}();
-THREE.Triangle.containsPoint=function(){var a=new THREE.Vector3;return function(b,c,d,e){b=THREE.Triangle.barycoordFromPoint(b,c,d,e,a);return 0<=b.x&&0<=b.y&&1>=b.x+b.y}}();
-THREE.Triangle.prototype={constructor:THREE.Triangle,set:function(a,b,c){this.a.copy(a);this.b.copy(b);this.c.copy(c);return this},setFromPointsAndIndices:function(a,b,c,d){this.a.copy(a[b]);this.b.copy(a[c]);this.c.copy(a[d]);return this},copy:function(a){this.a.copy(a.a);this.b.copy(a.b);this.c.copy(a.c);return this},area:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(){a.subVectors(this.c,this.b);b.subVectors(this.a,this.b);return.5*a.cross(b).length()}}(),midpoint:function(a){return(a||
-new THREE.Vector3).addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)},normal:function(a){return THREE.Triangle.normal(this.a,this.b,this.c,a)},plane:function(a){return(a||new THREE.Plane).setFromCoplanarPoints(this.a,this.b,this.c)},barycoordFromPoint:function(a,b){return THREE.Triangle.barycoordFromPoint(a,this.a,this.b,this.c,b)},containsPoint:function(a){return THREE.Triangle.containsPoint(a,this.a,this.b,this.c)},equals:function(a){return a.a.equals(this.a)&&a.b.equals(this.b)&&a.c.equals(this.c)},
-clone:function(){return(new THREE.Triangle).copy(this)}};THREE.Clock=function(a){this.autoStart=void 0!==a?a:!0;this.elapsedTime=this.oldTime=this.startTime=0;this.running=!1};
-THREE.Clock.prototype={constructor:THREE.Clock,start:function(){this.oldTime=this.startTime=void 0!==self.performance&&void 0!==self.performance.now?self.performance.now():Date.now();this.running=!0},stop:function(){this.getElapsedTime();this.running=!1},getElapsedTime:function(){this.getDelta();return this.elapsedTime},getDelta:function(){var a=0;this.autoStart&&!this.running&&this.start();if(this.running){var b=void 0!==self.performance&&void 0!==self.performance.now?self.performance.now():Date.now(),
-a=.001*(b-this.oldTime);this.oldTime=b;this.elapsedTime+=a}return a}};THREE.EventDispatcher=function(){};
-THREE.EventDispatcher.prototype={constructor:THREE.EventDispatcher,apply:function(a){a.addEventListener=THREE.EventDispatcher.prototype.addEventListener;a.hasEventListener=THREE.EventDispatcher.prototype.hasEventListener;a.removeEventListener=THREE.EventDispatcher.prototype.removeEventListener;a.dispatchEvent=THREE.EventDispatcher.prototype.dispatchEvent},addEventListener:function(a,b){void 0===this._listeners&&(this._listeners={});var c=this._listeners;void 0===c[a]&&(c[a]=[]);-1===c[a].indexOf(b)&&
-c[a].push(b)},hasEventListener:function(a,b){if(void 0===this._listeners)return!1;var c=this._listeners;return void 0!==c[a]&&-1!==c[a].indexOf(b)?!0:!1},removeEventListener:function(a,b){if(void 0!==this._listeners){var c=this._listeners[a];if(void 0!==c){var d=c.indexOf(b);-1!==d&&c.splice(d,1)}}},dispatchEvent:function(a){if(void 0!==this._listeners){var b=this._listeners[a.type];if(void 0!==b){a.target=this;for(var c=[],d=b.length,e=0;e<d;e++)c[e]=b[e];for(e=0;e<d;e++)c[e].call(this,a)}}}};
-(function(a){a.Raycaster=function(b,c,f,g){this.ray=new a.Ray(b,c);this.near=f||0;this.far=g||Infinity;this.params={Sprite:{},Mesh:{},PointCloud:{threshold:1},LOD:{},Line:{}}};var b=function(a,b){return a.distance-b.distance},c=function(a,b,f,g){a.raycast(b,f);if(!0===g){a=a.children;g=0;for(var h=a.length;g<h;g++)c(a[g],b,f,!0)}};a.Raycaster.prototype={constructor:a.Raycaster,precision:1E-4,linePrecision:1,set:function(a,b){this.ray.set(a,b)},intersectObject:function(a,e){var f=[];c(a,this,f,e);
-f.sort(b);return f},intersectObjects:function(a,e){var f=[];if(!1===a instanceof Array)return console.log("THREE.Raycaster.intersectObjects: objects is not an Array."),f;for(var g=0,h=a.length;g<h;g++)c(a[g],this,f,e);f.sort(b);return f}}})(THREE);
-THREE.Object3D=function(){Object.defineProperty(this,"id",{value:THREE.Object3DIdCount++});this.uuid=THREE.Math.generateUUID();this.name="";this.type="Object3D";this.parent=void 0;this.children=[];this.up=THREE.Object3D.DefaultUp.clone();var a=new THREE.Vector3,b=new THREE.Euler,c=new THREE.Quaternion,d=new THREE.Vector3(1,1,1);b.onChange(function(){c.setFromEuler(b,!1)});c.onChange(function(){b.setFromQuaternion(c,void 0,!1)});Object.defineProperties(this,{position:{enumerable:!0,value:a},rotation:{enumerable:!0,
-value:b},quaternion:{enumerable:!0,value:c},scale:{enumerable:!0,value:d}});this.renderDepth=null;this.rotationAutoUpdate=!0;this.matrix=new THREE.Matrix4;this.matrixWorld=new THREE.Matrix4;this.matrixAutoUpdate=!0;this.matrixWorldNeedsUpdate=!1;this.visible=!0;this.receiveShadow=this.castShadow=!1;this.frustumCulled=!0;this.userData={}};THREE.Object3D.DefaultUp=new THREE.Vector3(0,1,0);
-THREE.Object3D.prototype={constructor:THREE.Object3D,get eulerOrder(){console.warn("THREE.Object3D: .eulerOrder has been moved to .rotation.order.");return this.rotation.order},set eulerOrder(a){console.warn("THREE.Object3D: .eulerOrder has been moved to .rotation.order.");this.rotation.order=a},get useQuaternion(){console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.")},set useQuaternion(a){console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.")},
-applyMatrix:function(a){this.matrix.multiplyMatrices(a,this.matrix);this.matrix.decompose(this.position,this.quaternion,this.scale)},setRotationFromAxisAngle:function(a,b){this.quaternion.setFromAxisAngle(a,b)},setRotationFromEuler:function(a){this.quaternion.setFromEuler(a,!0)},setRotationFromMatrix:function(a){this.quaternion.setFromRotationMatrix(a)},setRotationFromQuaternion:function(a){this.quaternion.copy(a)},rotateOnAxis:function(){var a=new THREE.Quaternion;return function(b,c){a.setFromAxisAngle(b,
-c);this.quaternion.multiply(a);return this}}(),rotateX:function(){var a=new THREE.Vector3(1,0,0);return function(b){return this.rotateOnAxis(a,b)}}(),rotateY:function(){var a=new THREE.Vector3(0,1,0);return function(b){return this.rotateOnAxis(a,b)}}(),rotateZ:function(){var a=new THREE.Vector3(0,0,1);return function(b){return this.rotateOnAxis(a,b)}}(),translateOnAxis:function(){var a=new THREE.Vector3;return function(b,c){a.copy(b).applyQuaternion(this.quaternion);this.position.add(a.multiplyScalar(c));
-return this}}(),translate:function(a,b){console.warn("THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.");return this.translateOnAxis(b,a)},translateX:function(){var a=new THREE.Vector3(1,0,0);return function(b){return this.translateOnAxis(a,b)}}(),translateY:function(){var a=new THREE.Vector3(0,1,0);return function(b){return this.translateOnAxis(a,b)}}(),translateZ:function(){var a=new THREE.Vector3(0,0,1);return function(b){return this.translateOnAxis(a,
-b)}}(),localToWorld:function(a){return a.applyMatrix4(this.matrixWorld)},worldToLocal:function(){var a=new THREE.Matrix4;return function(b){return b.applyMatrix4(a.getInverse(this.matrixWorld))}}(),lookAt:function(){var a=new THREE.Matrix4;return function(b){a.lookAt(b,this.position,this.up);this.quaternion.setFromRotationMatrix(a)}}(),add:function(a){if(1<arguments.length){for(var b=0;b<arguments.length;b++)this.add(arguments[b]);return this}if(a===this)return console.error("THREE.Object3D.add:",
-a,"can't be added as a child of itself."),this;a instanceof THREE.Object3D?(void 0!==a.parent&&a.parent.remove(a),a.parent=this,a.dispatchEvent({type:"added"}),this.children.push(a)):console.error("THREE.Object3D.add:",a,"is not an instance of THREE.Object3D.");return this},remove:function(a){if(1<arguments.length)for(var b=0;b<arguments.length;b++)this.remove(arguments[b]);b=this.children.indexOf(a);-1!==b&&(a.parent=void 0,a.dispatchEvent({type:"removed"}),this.children.splice(b,1))},getChildByName:function(a,
-b){console.warn("THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().");return this.getObjectByName(a,b)},getObjectById:function(a,b){if(this.id===a)return this;for(var c=0,d=this.children.length;c<d;c++){var e=this.children[c].getObjectById(a,b);if(void 0!==e)return e}},getObjectByName:function(a,b){if(this.name===a)return this;for(var c=0,d=this.children.length;c<d;c++){var e=this.children[c].getObjectByName(a,b);if(void 0!==e)return e}},getWorldPosition:function(a){a=a||new THREE.Vector3;
-this.updateMatrixWorld(!0);return a.setFromMatrixPosition(this.matrixWorld)},getWorldQuaternion:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c){c=c||new THREE.Quaternion;this.updateMatrixWorld(!0);this.matrixWorld.decompose(a,c,b);return c}}(),getWorldRotation:function(){var a=new THREE.Quaternion;return function(b){b=b||new THREE.Euler;this.getWorldQuaternion(a);return b.setFromQuaternion(a,this.rotation.order,!1)}}(),getWorldScale:function(){var a=new THREE.Vector3,b=new THREE.Quaternion;
-return function(c){c=c||new THREE.Vector3;this.updateMatrixWorld(!0);this.matrixWorld.decompose(a,b,c);return c}}(),getWorldDirection:function(){var a=new THREE.Quaternion;return function(b){b=b||new THREE.Vector3;this.getWorldQuaternion(a);return b.set(0,0,1).applyQuaternion(a)}}(),raycast:function(){},traverse:function(a){a(this);for(var b=0,c=this.children.length;b<c;b++)this.children[b].traverse(a)},traverseVisible:function(a){if(!1!==this.visible){a(this);for(var b=0,c=this.children.length;b<
-c;b++)this.children[b].traverseVisible(a)}},updateMatrix:function(){this.matrix.compose(this.position,this.quaternion,this.scale);this.matrixWorldNeedsUpdate=!0},updateMatrixWorld:function(a){!0===this.matrixAutoUpdate&&this.updateMatrix();if(!0===this.matrixWorldNeedsUpdate||!0===a)void 0===this.parent?this.matrixWorld.copy(this.matrix):this.matrixWorld.multiplyMatrices(this.parent.matrixWorld,this.matrix),this.matrixWorldNeedsUpdate=!1,a=!0;for(var b=0,c=this.children.length;b<c;b++)this.children[b].updateMatrixWorld(a)},
-toJSON:function(){var a={metadata:{version:4.3,type:"Object",generator:"ObjectExporter"}},b={},c=function(c){void 0===a.geometries&&(a.geometries=[]);if(void 0===b[c.uuid]){var d=c.toJSON();delete d.metadata;b[c.uuid]=d;a.geometries.push(d)}return c.uuid},d={},e=function(b){void 0===a.materials&&(a.materials=[]);if(void 0===d[b.uuid]){var c=b.toJSON();delete c.metadata;d[b.uuid]=c;a.materials.push(c)}return b.uuid},f=function(a){var b={};b.uuid=a.uuid;b.type=a.type;""!==a.name&&(b.name=a.name);"{}"!==
-JSON.stringify(a.userData)&&(b.userData=a.userData);!0!==a.visible&&(b.visible=a.visible);a instanceof THREE.PerspectiveCamera?(b.fov=a.fov,b.aspect=a.aspect,b.near=a.near,b.far=a.far):a instanceof THREE.OrthographicCamera?(b.left=a.left,b.right=a.right,b.top=a.top,b.bottom=a.bottom,b.near=a.near,b.far=a.far):a instanceof THREE.AmbientLight?b.color=a.color.getHex():a instanceof THREE.DirectionalLight?(b.color=a.color.getHex(),b.intensity=a.intensity):a instanceof THREE.PointLight?(b.color=a.color.getHex(),
-b.intensity=a.intensity,b.distance=a.distance):a instanceof THREE.SpotLight?(b.color=a.color.getHex(),b.intensity=a.intensity,b.distance=a.distance,b.angle=a.angle,b.exponent=a.exponent):a instanceof THREE.HemisphereLight?(b.color=a.color.getHex(),b.groundColor=a.groundColor.getHex()):a instanceof THREE.Mesh?(b.geometry=c(a.geometry),b.material=e(a.material)):a instanceof THREE.Line?(b.geometry=c(a.geometry),b.material=e(a.material)):a instanceof THREE.Sprite&&(b.material=e(a.material));b.matrix=
-a.matrix.toArray();if(0<a.children.length){b.children=[];for(var d=0;d<a.children.length;d++)b.children.push(f(a.children[d]))}return b};a.object=f(this);return a},clone:function(a,b){void 0===a&&(a=new THREE.Object3D);void 0===b&&(b=!0);a.name=this.name;a.up.copy(this.up);a.position.copy(this.position);a.quaternion.copy(this.quaternion);a.scale.copy(this.scale);a.renderDepth=this.renderDepth;a.rotationAutoUpdate=this.rotationAutoUpdate;a.matrix.copy(this.matrix);a.matrixWorld.copy(this.matrixWorld);
-a.matrixAutoUpdate=this.matrixAutoUpdate;a.matrixWorldNeedsUpdate=this.matrixWorldNeedsUpdate;a.visible=this.visible;a.castShadow=this.castShadow;a.receiveShadow=this.receiveShadow;a.frustumCulled=this.frustumCulled;a.userData=JSON.parse(JSON.stringify(this.userData));if(!0===b)for(var c=0;c<this.children.length;c++)a.add(this.children[c].clone());return a}};THREE.EventDispatcher.prototype.apply(THREE.Object3D.prototype);THREE.Object3DIdCount=0;
-THREE.Projector=function(){console.warn("THREE.Projector has been moved to /examples/renderers/Projector.js.");this.projectVector=function(a,b){console.warn("THREE.Projector: .projectVector() is now vector.project().");a.project(b)};this.unprojectVector=function(a,b){console.warn("THREE.Projector: .unprojectVector() is now vector.unproject().");a.unproject(b)};this.pickingRay=function(a,b){console.error("THREE.Projector: .pickingRay() has been removed.")}};
-THREE.Face3=function(a,b,c,d,e,f){this.a=a;this.b=b;this.c=c;this.normal=d instanceof THREE.Vector3?d:new THREE.Vector3;this.vertexNormals=d instanceof Array?d:[];this.color=e instanceof THREE.Color?e:new THREE.Color;this.vertexColors=e instanceof Array?e:[];this.vertexTangents=[];this.materialIndex=void 0!==f?f:0};
-THREE.Face3.prototype={constructor:THREE.Face3,clone:function(){var a=new THREE.Face3(this.a,this.b,this.c);a.normal.copy(this.normal);a.color.copy(this.color);a.materialIndex=this.materialIndex;for(var b=0,c=this.vertexNormals.length;b<c;b++)a.vertexNormals[b]=this.vertexNormals[b].clone();b=0;for(c=this.vertexColors.length;b<c;b++)a.vertexColors[b]=this.vertexColors[b].clone();b=0;for(c=this.vertexTangents.length;b<c;b++)a.vertexTangents[b]=this.vertexTangents[b].clone();return a}};
-THREE.Face4=function(a,b,c,d,e,f,g){console.warn("THREE.Face4 has been removed. A THREE.Face3 will be created instead.");return new THREE.Face3(a,b,c,e,f,g)};THREE.BufferAttribute=function(a,b){this.array=a;this.itemSize=b;this.needsUpdate=!1};
-THREE.BufferAttribute.prototype={constructor:THREE.BufferAttribute,get length(){return this.array.length},copyAt:function(a,b,c){a*=this.itemSize;c*=b.itemSize;for(var d=0,e=this.itemSize;d<e;d++)this.array[a+d]=b.array[c+d]},set:function(a){this.array.set(a);return this},setX:function(a,b){this.array[a*this.itemSize]=b;return this},setY:function(a,b){this.array[a*this.itemSize+1]=b;return this},setZ:function(a,b){this.array[a*this.itemSize+2]=b;return this},setXY:function(a,b,c){a*=this.itemSize;
-this.array[a]=b;this.array[a+1]=c;return this},setXYZ:function(a,b,c,d){a*=this.itemSize;this.array[a]=b;this.array[a+1]=c;this.array[a+2]=d;return this},setXYZW:function(a,b,c,d,e){a*=this.itemSize;this.array[a]=b;this.array[a+1]=c;this.array[a+2]=d;this.array[a+3]=e;return this},clone:function(){return new THREE.BufferAttribute(new this.array.constructor(this.array),this.itemSize)}};
-THREE.Int8Attribute=function(a,b){console.warn("THREE.Int8Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};THREE.Uint8Attribute=function(a,b){console.warn("THREE.Uint8Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};
-THREE.Uint8ClampedAttribute=function(a,b){console.warn("THREE.Uint8ClampedAttribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};THREE.Int16Attribute=function(a,b){console.warn("THREE.Int16Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};
-THREE.Uint16Attribute=function(a,b){console.warn("THREE.Uint16Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};THREE.Int32Attribute=function(a,b){console.warn("THREE.Int32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};
-THREE.Uint32Attribute=function(a,b){console.warn("THREE.Uint32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};THREE.Float32Attribute=function(a,b){console.warn("THREE.Float32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};
-THREE.Float64Attribute=function(a,b){console.warn("THREE.Float64Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.");return new THREE.BufferAttribute(a,b)};THREE.BufferGeometry=function(){Object.defineProperty(this,"id",{value:THREE.GeometryIdCount++});this.uuid=THREE.Math.generateUUID();this.name="";this.type="BufferGeometry";this.attributes={};this.attributesKeys=[];this.offsets=this.drawcalls=[];this.boundingSphere=this.boundingBox=null};
-THREE.BufferGeometry.prototype={constructor:THREE.BufferGeometry,addAttribute:function(a,b,c){!1===b instanceof THREE.BufferAttribute?(console.warn("THREE.BufferGeometry: .addAttribute() now expects ( name, attribute )."),this.attributes[a]={array:b,itemSize:c}):(this.attributes[a]=b,this.attributesKeys=Object.keys(this.attributes))},getAttribute:function(a){return this.attributes[a]},addDrawCall:function(a,b,c){this.drawcalls.push({start:a,count:b,index:void 0!==c?c:0})},applyMatrix:function(a){var b=
-this.attributes.position;void 0!==b&&(a.applyToVector3Array(b.array),b.needsUpdate=!0);b=this.attributes.normal;void 0!==b&&((new THREE.Matrix3).getNormalMatrix(a).applyToVector3Array(b.array),b.needsUpdate=!0)},center:function(){},fromGeometry:function(a,b){b=b||{vertexColors:THREE.NoColors};var c=a.vertices,d=a.faces,e=a.faceVertexUvs,f=b.vertexColors,g=0<e[0].length,h=3==d[0].vertexNormals.length,k=new Float32Array(9*d.length);this.addAttribute("position",new THREE.BufferAttribute(k,3));var n=
-new Float32Array(9*d.length);this.addAttribute("normal",new THREE.BufferAttribute(n,3));if(f!==THREE.NoColors){var p=new Float32Array(9*d.length);this.addAttribute("color",new THREE.BufferAttribute(p,3))}if(!0===g){var q=new Float32Array(6*d.length);this.addAttribute("uv",new THREE.BufferAttribute(q,2))}for(var m=0,r=0,t=0;m<d.length;m++,r+=6,t+=9){var s=d[m],u=c[s.a],v=c[s.b],y=c[s.c];k[t]=u.x;k[t+1]=u.y;k[t+2]=u.z;k[t+3]=v.x;k[t+4]=v.y;k[t+5]=v.z;k[t+6]=y.x;k[t+7]=y.y;k[t+8]=y.z;!0===h?(u=s.vertexNormals[0],
-v=s.vertexNormals[1],y=s.vertexNormals[2],n[t]=u.x,n[t+1]=u.y,n[t+2]=u.z,n[t+3]=v.x,n[t+4]=v.y,n[t+5]=v.z,n[t+6]=y.x,n[t+7]=y.y,n[t+8]=y.z):(u=s.normal,n[t]=u.x,n[t+1]=u.y,n[t+2]=u.z,n[t+3]=u.x,n[t+4]=u.y,n[t+5]=u.z,n[t+6]=u.x,n[t+7]=u.y,n[t+8]=u.z);f===THREE.FaceColors?(s=s.color,p[t]=s.r,p[t+1]=s.g,p[t+2]=s.b,p[t+3]=s.r,p[t+4]=s.g,p[t+5]=s.b,p[t+6]=s.r,p[t+7]=s.g,p[t+8]=s.b):f===THREE.VertexColors&&(u=s.vertexColors[0],v=s.vertexColors[1],s=s.vertexColors[2],p[t]=u.r,p[t+1]=u.g,p[t+2]=u.b,p[t+3]=
-v.r,p[t+4]=v.g,p[t+5]=v.b,p[t+6]=s.r,p[t+7]=s.g,p[t+8]=s.b);!0===g&&(s=e[0][m][0],u=e[0][m][1],v=e[0][m][2],q[r]=s.x,q[r+1]=s.y,q[r+2]=u.x,q[r+3]=u.y,q[r+4]=v.x,q[r+5]=v.y)}this.computeBoundingSphere();return this},computeBoundingBox:function(){var a=new THREE.Vector3;return function(){null===this.boundingBox&&(this.boundingBox=new THREE.Box3);var b=this.attributes.position.array;if(b){var c=this.boundingBox;c.makeEmpty();for(var d=0,e=b.length;d<e;d+=3)a.set(b[d],b[d+1],b[d+2]),c.expandByPoint(a)}if(void 0===
-b||0===b.length)this.boundingBox.min.set(0,0,0),this.boundingBox.max.set(0,0,0);(isNaN(this.boundingBox.min.x)||isNaN(this.boundingBox.min.y)||isNaN(this.boundingBox.min.z))&&console.error('THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.')}}(),computeBoundingSphere:function(){var a=new THREE.Box3,b=new THREE.Vector3;return function(){null===this.boundingSphere&&(this.boundingSphere=new THREE.Sphere);var c=this.attributes.position.array;
-if(c){a.makeEmpty();for(var d=this.boundingSphere.center,e=0,f=c.length;e<f;e+=3)b.set(c[e],c[e+1],c[e+2]),a.expandByPoint(b);a.center(d);for(var g=0,e=0,f=c.length;e<f;e+=3)b.set(c[e],c[e+1],c[e+2]),g=Math.max(g,d.distanceToSquared(b));this.boundingSphere.radius=Math.sqrt(g);isNaN(this.boundingSphere.radius)&&console.error('THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.')}}}(),computeFaceNormals:function(){},computeVertexNormals:function(){var a=
-this.attributes;if(a.position){var b=a.position.array;if(void 0===a.normal)this.addAttribute("normal",new THREE.BufferAttribute(new Float32Array(b.length),3));else for(var c=a.normal.array,d=0,e=c.length;d<e;d++)c[d]=0;var c=a.normal.array,f,g,h,k=new THREE.Vector3,n=new THREE.Vector3,p=new THREE.Vector3,q=new THREE.Vector3,m=new THREE.Vector3;if(a.index)for(var r=a.index.array,t=0<this.offsets.length?this.offsets:[{start:0,count:r.length,index:0}],s=0,u=t.length;s<u;++s){e=t[s].start;f=t[s].count;
-for(var v=t[s].index,d=e,e=e+f;d<e;d+=3)f=3*(v+r[d]),g=3*(v+r[d+1]),h=3*(v+r[d+2]),k.fromArray(b,f),n.fromArray(b,g),p.fromArray(b,h),q.subVectors(p,n),m.subVectors(k,n),q.cross(m),c[f]+=q.x,c[f+1]+=q.y,c[f+2]+=q.z,c[g]+=q.x,c[g+1]+=q.y,c[g+2]+=q.z,c[h]+=q.x,c[h+1]+=q.y,c[h+2]+=q.z}else for(d=0,e=b.length;d<e;d+=9)k.fromArray(b,d),n.fromArray(b,d+3),p.fromArray(b,d+6),q.subVectors(p,n),m.subVectors(k,n),q.cross(m),c[d]=q.x,c[d+1]=q.y,c[d+2]=q.z,c[d+3]=q.x,c[d+4]=q.y,c[d+5]=q.z,c[d+6]=q.x,c[d+7]=q.y,
-c[d+8]=q.z;this.normalizeNormals();a.normal.needsUpdate=!0}},computeTangents:function(){function a(a,b,c){q.fromArray(d,3*a);m.fromArray(d,3*b);r.fromArray(d,3*c);t.fromArray(f,2*a);s.fromArray(f,2*b);u.fromArray(f,2*c);v=m.x-q.x;y=r.x-q.x;G=m.y-q.y;w=r.y-q.y;K=m.z-q.z;x=r.z-q.z;D=s.x-t.x;E=u.x-t.x;A=s.y-t.y;B=u.y-t.y;F=1/(D*B-E*A);R.set((B*v-A*y)*F,(B*G-A*w)*F,(B*K-A*x)*F);H.set((D*y-E*v)*F,(D*w-E*G)*F,(D*x-E*K)*F);k[a].add(R);k[b].add(R);k[c].add(R);n[a].add(H);n[b].add(H);n[c].add(H)}function b(a){ya.fromArray(e,
-3*a);P.copy(ya);Fa=k[a];la.copy(Fa);la.sub(ya.multiplyScalar(ya.dot(Fa))).normalize();ma.crossVectors(P,Fa);za=ma.dot(n[a]);Ga=0>za?-1:1;h[4*a]=la.x;h[4*a+1]=la.y;h[4*a+2]=la.z;h[4*a+3]=Ga}if(void 0===this.attributes.index||void 0===this.attributes.position||void 0===this.attributes.normal||void 0===this.attributes.uv)console.warn("Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()");else{var c=this.attributes.index.array,d=this.attributes.position.array,
-e=this.attributes.normal.array,f=this.attributes.uv.array,g=d.length/3;void 0===this.attributes.tangent&&this.addAttribute("tangent",new THREE.BufferAttribute(new Float32Array(4*g),4));for(var h=this.attributes.tangent.array,k=[],n=[],p=0;p<g;p++)k[p]=new THREE.Vector3,n[p]=new THREE.Vector3;var q=new THREE.Vector3,m=new THREE.Vector3,r=new THREE.Vector3,t=new THREE.Vector2,s=new THREE.Vector2,u=new THREE.Vector2,v,y,G,w,K,x,D,E,A,B,F,R=new THREE.Vector3,H=new THREE.Vector3,C,T,Q,O,S;0===this.drawcalls.length&&
-this.addDrawCall(0,c.length,0);var X=this.drawcalls,p=0;for(T=X.length;p<T;++p){C=X[p].start;Q=X[p].count;var Y=X[p].index,g=C;for(C+=Q;g<C;g+=3)Q=Y+c[g],O=Y+c[g+1],S=Y+c[g+2],a(Q,O,S)}var la=new THREE.Vector3,ma=new THREE.Vector3,ya=new THREE.Vector3,P=new THREE.Vector3,Ga,Fa,za,p=0;for(T=X.length;p<T;++p)for(C=X[p].start,Q=X[p].count,Y=X[p].index,g=C,C+=Q;g<C;g+=3)Q=Y+c[g],O=Y+c[g+1],S=Y+c[g+2],b(Q),b(O),b(S)}},computeOffsets:function(a){var b=a;void 0===a&&(b=65535);Date.now();a=this.attributes.index.array;
-for(var c=this.attributes.position.array,d=a.length/3,e=new Uint16Array(a.length),f=0,g=0,h=[{start:0,count:0,index:0}],k=h[0],n=0,p=0,q=new Int32Array(6),m=new Int32Array(c.length),r=new Int32Array(c.length),t=0;t<c.length;t++)m[t]=-1,r[t]=-1;for(c=0;c<d;c++){for(var s=p=0;3>s;s++)t=a[3*c+s],-1==m[t]?(q[2*s]=t,q[2*s+1]=-1,p++):m[t]<k.index?(q[2*s]=t,q[2*s+1]=-1,n++):(q[2*s]=t,q[2*s+1]=m[t]);if(g+p>k.index+b)for(k={start:f,count:0,index:g},h.push(k),p=0;6>p;p+=2)s=q[p+1],-1<s&&s<k.index&&(q[p+1]=
--1);for(p=0;6>p;p+=2)t=q[p],s=q[p+1],-1===s&&(s=g++),m[t]=s,r[s]=t,e[f++]=s-k.index,k.count++}this.reorderBuffers(e,r,g);return this.offsets=h},merge:function(){console.log("BufferGeometry.merge(): TODO")},normalizeNormals:function(){for(var a=this.attributes.normal.array,b,c,d,e=0,f=a.length;e<f;e+=3)b=a[e],c=a[e+1],d=a[e+2],b=1/Math.sqrt(b*b+c*c+d*d),a[e]*=b,a[e+1]*=b,a[e+2]*=b},reorderBuffers:function(a,b,c){var d={},e;for(e in this.attributes)"index"!=e&&(d[e]=new this.attributes[e].array.constructor(this.attributes[e].itemSize*
-c));for(var f=0;f<c;f++){var g=b[f];for(e in this.attributes)if("index"!=e)for(var h=this.attributes[e].array,k=this.attributes[e].itemSize,n=d[e],p=0;p<k;p++)n[f*k+p]=h[g*k+p]}this.attributes.index.array=a;for(e in this.attributes)"index"!=e&&(this.attributes[e].array=d[e],this.attributes[e].numItems=this.attributes[e].itemSize*c)},toJSON:function(){var a={metadata:{version:4,type:"BufferGeometry",generator:"BufferGeometryExporter"},uuid:this.uuid,type:this.type,data:{attributes:{}}},b=this.attributes,
-c=this.offsets,d=this.boundingSphere,e;for(e in b){for(var f=b[e],g=[],h=f.array,k=0,n=h.length;k<n;k++)g[k]=h[k];a.data.attributes[e]={itemSize:f.itemSize,type:f.array.constructor.name,array:g}}0<c.length&&(a.data.offsets=JSON.parse(JSON.stringify(c)));null!==d&&(a.data.boundingSphere={center:d.center.toArray(),radius:d.radius});return a},clone:function(){var a=new THREE.BufferGeometry,b;for(b in this.attributes)a.addAttribute(b,this.attributes[b].clone());b=0;for(var c=this.offsets.length;b<c;b++){var d=
-this.offsets[b];a.offsets.push({start:d.start,index:d.index,count:d.count})}return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.BufferGeometry.prototype);
-THREE.Geometry=function(){Object.defineProperty(this,"id",{value:THREE.GeometryIdCount++});this.uuid=THREE.Math.generateUUID();this.name="";this.type="Geometry";this.vertices=[];this.colors=[];this.faces=[];this.faceVertexUvs=[[]];this.morphTargets=[];this.morphColors=[];this.morphNormals=[];this.skinWeights=[];this.skinIndices=[];this.lineDistances=[];this.boundingSphere=this.boundingBox=null;this.hasTangents=!1;this.dynamic=!0;this.groupsNeedUpdate=this.lineDistancesNeedUpdate=this.colorsNeedUpdate=
-this.tangentsNeedUpdate=this.normalsNeedUpdate=this.uvsNeedUpdate=this.elementsNeedUpdate=this.verticesNeedUpdate=!1};
-THREE.Geometry.prototype={constructor:THREE.Geometry,applyMatrix:function(a){for(var b=(new THREE.Matrix3).getNormalMatrix(a),c=0,d=this.vertices.length;c<d;c++)this.vertices[c].applyMatrix4(a);c=0;for(d=this.faces.length;c<d;c++){a=this.faces[c];a.normal.applyMatrix3(b).normalize();for(var e=0,f=a.vertexNormals.length;e<f;e++)a.vertexNormals[e].applyMatrix3(b).normalize()}this.boundingBox instanceof THREE.Box3&&this.computeBoundingBox();this.boundingSphere instanceof THREE.Sphere&&this.computeBoundingSphere()},
-fromBufferGeometry:function(a){for(var b=this,c=a.attributes,d=c.position.array,e=void 0!==c.index?c.index.array:void 0,f=void 0!==c.normal?c.normal.array:void 0,g=void 0!==c.color?c.color.array:void 0,h=void 0!==c.uv?c.uv.array:void 0,k=[],n=[],p=c=0;c<d.length;c+=3,p+=2)b.vertices.push(new THREE.Vector3(d[c],d[c+1],d[c+2])),void 0!==f&&k.push(new THREE.Vector3(f[c],f[c+1],f[c+2])),void 0!==g&&b.colors.push(new THREE.Color(g[c],g[c+1],g[c+2])),void 0!==h&&n.push(new THREE.Vector2(h[p],h[p+1]));h=
-function(a,c,d){var e=void 0!==f?[k[a].clone(),k[c].clone(),k[d].clone()]:[],h=void 0!==g?[b.colors[a].clone(),b.colors[c].clone(),b.colors[d].clone()]:[];b.faces.push(new THREE.Face3(a,c,d,e,h));b.faceVertexUvs[0].push([n[a],n[c],n[d]])};if(void 0!==e)for(c=0;c<e.length;c+=3)h(e[c],e[c+1],e[c+2]);else for(c=0;c<d.length/3;c+=3)h(c,c+1,c+2);this.computeFaceNormals();null!==a.boundingBox&&(this.boundingBox=a.boundingBox.clone());null!==a.boundingSphere&&(this.boundingSphere=a.boundingSphere.clone());
-return this},center:function(){this.computeBoundingBox();var a=new THREE.Vector3;a.addVectors(this.boundingBox.min,this.boundingBox.max);a.multiplyScalar(-.5);this.applyMatrix((new THREE.Matrix4).makeTranslation(a.x,a.y,a.z));this.computeBoundingBox();return a},computeFaceNormals:function(){for(var a=new THREE.Vector3,b=new THREE.Vector3,c=0,d=this.faces.length;c<d;c++){var e=this.faces[c],f=this.vertices[e.a],g=this.vertices[e.b];a.subVectors(this.vertices[e.c],g);b.subVectors(f,g);a.cross(b);a.normalize();
-e.normal.copy(a)}},computeVertexNormals:function(a){var b,c,d;d=Array(this.vertices.length);b=0;for(c=this.vertices.length;b<c;b++)d[b]=new THREE.Vector3;if(a){var e,f,g,h=new THREE.Vector3,k=new THREE.Vector3;new THREE.Vector3;new THREE.Vector3;new THREE.Vector3;a=0;for(b=this.faces.length;a<b;a++)c=this.faces[a],e=this.vertices[c.a],f=this.vertices[c.b],g=this.vertices[c.c],h.subVectors(g,f),k.subVectors(e,f),h.cross(k),d[c.a].add(h),d[c.b].add(h),d[c.c].add(h)}else for(a=0,b=this.faces.length;a<
-b;a++)c=this.faces[a],d[c.a].add(c.normal),d[c.b].add(c.normal),d[c.c].add(c.normal);b=0;for(c=this.vertices.length;b<c;b++)d[b].normalize();a=0;for(b=this.faces.length;a<b;a++)c=this.faces[a],c.vertexNormals[0]=d[c.a].clone(),c.vertexNormals[1]=d[c.b].clone(),c.vertexNormals[2]=d[c.c].clone()},computeMorphNormals:function(){var a,b,c,d,e;c=0;for(d=this.faces.length;c<d;c++)for(e=this.faces[c],e.__originalFaceNormal?e.__originalFaceNormal.copy(e.normal):e.__originalFaceNormal=e.normal.clone(),e.__originalVertexNormals||
-(e.__originalVertexNormals=[]),a=0,b=e.vertexNormals.length;a<b;a++)e.__originalVertexNormals[a]?e.__originalVertexNormals[a].copy(e.vertexNormals[a]):e.__originalVertexNormals[a]=e.vertexNormals[a].clone();var f=new THREE.Geometry;f.faces=this.faces;a=0;for(b=this.morphTargets.length;a<b;a++){if(!this.morphNormals[a]){this.morphNormals[a]={};this.morphNormals[a].faceNormals=[];this.morphNormals[a].vertexNormals=[];e=this.morphNormals[a].faceNormals;var g=this.morphNormals[a].vertexNormals,h,k;c=
-0;for(d=this.faces.length;c<d;c++)h=new THREE.Vector3,k={a:new THREE.Vector3,b:new THREE.Vector3,c:new THREE.Vector3},e.push(h),g.push(k)}g=this.morphNormals[a];f.vertices=this.morphTargets[a].vertices;f.computeFaceNormals();f.computeVertexNormals();c=0;for(d=this.faces.length;c<d;c++)e=this.faces[c],h=g.faceNormals[c],k=g.vertexNormals[c],h.copy(e.normal),k.a.copy(e.vertexNormals[0]),k.b.copy(e.vertexNormals[1]),k.c.copy(e.vertexNormals[2])}c=0;for(d=this.faces.length;c<d;c++)e=this.faces[c],e.normal=
-e.__originalFaceNormal,e.vertexNormals=e.__originalVertexNormals},computeTangents:function(){var a,b,c,d,e,f,g,h,k,n,p,q,m,r,t,s,u,v=[],y=[];c=new THREE.Vector3;var G=new THREE.Vector3,w=new THREE.Vector3,K=new THREE.Vector3,x=new THREE.Vector3;a=0;for(b=this.vertices.length;a<b;a++)v[a]=new THREE.Vector3,y[a]=new THREE.Vector3;a=0;for(b=this.faces.length;a<b;a++)e=this.faces[a],f=this.faceVertexUvs[0][a],d=e.a,u=e.b,e=e.c,g=this.vertices[d],h=this.vertices[u],k=this.vertices[e],n=f[0],p=f[1],q=f[2],
-f=h.x-g.x,m=k.x-g.x,r=h.y-g.y,t=k.y-g.y,h=h.z-g.z,g=k.z-g.z,k=p.x-n.x,s=q.x-n.x,p=p.y-n.y,n=q.y-n.y,q=1/(k*n-s*p),c.set((n*f-p*m)*q,(n*r-p*t)*q,(n*h-p*g)*q),G.set((k*m-s*f)*q,(k*t-s*r)*q,(k*g-s*h)*q),v[d].add(c),v[u].add(c),v[e].add(c),y[d].add(G),y[u].add(G),y[e].add(G);G=["a","b","c","d"];a=0;for(b=this.faces.length;a<b;a++)for(e=this.faces[a],c=0;c<Math.min(e.vertexNormals.length,3);c++)x.copy(e.vertexNormals[c]),d=e[G[c]],u=v[d],w.copy(u),w.sub(x.multiplyScalar(x.dot(u))).normalize(),K.crossVectors(e.vertexNormals[c],
-u),d=K.dot(y[d]),d=0>d?-1:1,e.vertexTangents[c]=new THREE.Vector4(w.x,w.y,w.z,d);this.hasTangents=!0},computeLineDistances:function(){for(var a=0,b=this.vertices,c=0,d=b.length;c<d;c++)0<c&&(a+=b[c].distanceTo(b[c-1])),this.lineDistances[c]=a},computeBoundingBox:function(){null===this.boundingBox&&(this.boundingBox=new THREE.Box3);this.boundingBox.setFromPoints(this.vertices)},computeBoundingSphere:function(){null===this.boundingSphere&&(this.boundingSphere=new THREE.Sphere);this.boundingSphere.setFromPoints(this.vertices)},
-merge:function(a,b,c){if(!1===a instanceof THREE.Geometry)console.error("THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.",a);else{var d,e=this.vertices.length,f=this.vertices,g=a.vertices,h=this.faces,k=a.faces,n=this.faceVertexUvs[0];a=a.faceVertexUvs[0];void 0===c&&(c=0);void 0!==b&&(d=(new THREE.Matrix3).getNormalMatrix(b));for(var p=0,q=g.length;p<q;p++){var m=g[p].clone();void 0!==b&&m.applyMatrix4(b);f.push(m)}p=0;for(q=k.length;p<q;p++){var g=k[p],r,t=g.vertexNormals,s=
-g.vertexColors,m=new THREE.Face3(g.a+e,g.b+e,g.c+e);m.normal.copy(g.normal);void 0!==d&&m.normal.applyMatrix3(d).normalize();b=0;for(f=t.length;b<f;b++)r=t[b].clone(),void 0!==d&&r.applyMatrix3(d).normalize(),m.vertexNormals.push(r);m.color.copy(g.color);b=0;for(f=s.length;b<f;b++)r=s[b],m.vertexColors.push(r.clone());m.materialIndex=g.materialIndex+c;h.push(m)}p=0;for(q=a.length;p<q;p++)if(c=a[p],d=[],void 0!==c){b=0;for(f=c.length;b<f;b++)d.push(new THREE.Vector2(c[b].x,c[b].y));n.push(d)}}},mergeVertices:function(){var a=
-{},b=[],c=[],d,e=Math.pow(10,4),f,g;f=0;for(g=this.vertices.length;f<g;f++)d=this.vertices[f],d=Math.round(d.x*e)+"_"+Math.round(d.y*e)+"_"+Math.round(d.z*e),void 0===a[d]?(a[d]=f,b.push(this.vertices[f]),c[f]=b.length-1):c[f]=c[a[d]];a=[];f=0;for(g=this.faces.length;f<g;f++)for(e=this.faces[f],e.a=c[e.a],e.b=c[e.b],e.c=c[e.c],e=[e.a,e.b,e.c],d=0;3>d;d++)if(e[d]==e[(d+1)%3]){a.push(f);break}for(f=a.length-1;0<=f;f--)for(e=a[f],this.faces.splice(e,1),c=0,g=this.faceVertexUvs.length;c<g;c++)this.faceVertexUvs[c].splice(e,
-1);f=this.vertices.length-b.length;this.vertices=b;return f},toJSON:function(){function a(a,b,c){return c?a|1<<b:a&~(1<<b)}function b(a){var b=a.x.toString()+a.y.toString()+a.z.toString();if(void 0!==n[b])return n[b];n[b]=k.length/3;k.push(a.x,a.y,a.z);return n[b]}function c(a){var b=a.r.toString()+a.g.toString()+a.b.toString();if(void 0!==q[b])return q[b];q[b]=p.length;p.push(a.getHex());return q[b]}function d(a){var b=a.x.toString()+a.y.toString();if(void 0!==r[b])return r[b];r[b]=m.length/2;m.push(a.x,
-a.y);return r[b]}var e={metadata:{version:4,type:"BufferGeometry",generator:"BufferGeometryExporter"},uuid:this.uuid,type:this.type};""!==this.name&&(e.name=this.name);if(void 0!==this.parameters){var f=this.parameters,g;for(g in f)void 0!==f[g]&&(e[g]=f[g]);return e}f=[];for(g=0;g<this.vertices.length;g++){var h=this.vertices[g];f.push(h.x,h.y,h.z)}var h=[],k=[],n={},p=[],q={},m=[],r={};for(g=0;g<this.faces.length;g++){var t=this.faces[g],s=void 0!==this.faceVertexUvs[0][g],u=0<t.normal.length(),
-v=0<t.vertexNormals.length,y=1!==t.color.r||1!==t.color.g||1!==t.color.b,G=0<t.vertexColors.length,w=0,w=a(w,0,0),w=a(w,1,!1),w=a(w,2,!1),w=a(w,3,s),w=a(w,4,u),w=a(w,5,v),w=a(w,6,y),w=a(w,7,G);h.push(w);h.push(t.a,t.b,t.c);s&&(s=this.faceVertexUvs[0][g],h.push(d(s[0]),d(s[1]),d(s[2])));u&&h.push(b(t.normal));v&&(u=t.vertexNormals,h.push(b(u[0]),b(u[1]),b(u[2])));y&&h.push(c(t.color));G&&(t=t.vertexColors,h.push(c(t[0]),c(t[1]),c(t[2])))}e.data={};e.data.vertices=f;e.data.normals=k;0<p.length&&(e.data.colors=
-p);0<m.length&&(e.data.uvs=[m]);e.data.faces=h;return e},clone:function(){for(var a=new THREE.Geometry,b=this.vertices,c=0,d=b.length;c<d;c++)a.vertices.push(b[c].clone());b=this.faces;c=0;for(d=b.length;c<d;c++)a.faces.push(b[c].clone());b=this.faceVertexUvs[0];c=0;for(d=b.length;c<d;c++){for(var e=b[c],f=[],g=0,h=e.length;g<h;g++)f.push(new THREE.Vector2(e[g].x,e[g].y));a.faceVertexUvs[0].push(f)}return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.Geometry.prototype);
-THREE.GeometryIdCount=0;THREE.Camera=function(){THREE.Object3D.call(this);this.type="Camera";this.matrixWorldInverse=new THREE.Matrix4;this.projectionMatrix=new THREE.Matrix4};THREE.Camera.prototype=Object.create(THREE.Object3D.prototype);THREE.Camera.prototype.getWorldDirection=function(){var a=new THREE.Quaternion;return function(b){b=b||new THREE.Vector3;this.getWorldQuaternion(a);return b.set(0,0,-1).applyQuaternion(a)}}();
-THREE.Camera.prototype.lookAt=function(){var a=new THREE.Matrix4;return function(b){a.lookAt(this.position,b,this.up);this.quaternion.setFromRotationMatrix(a)}}();THREE.Camera.prototype.clone=function(a){void 0===a&&(a=new THREE.Camera);THREE.Object3D.prototype.clone.call(this,a);a.matrixWorldInverse.copy(this.matrixWorldInverse);a.projectionMatrix.copy(this.projectionMatrix);return a};
-THREE.CubeCamera=function(a,b,c){THREE.Object3D.call(this);this.type="CubeCamera";var d=new THREE.PerspectiveCamera(90,1,a,b);d.up.set(0,-1,0);d.lookAt(new THREE.Vector3(1,0,0));this.add(d);var e=new THREE.PerspectiveCamera(90,1,a,b);e.up.set(0,-1,0);e.lookAt(new THREE.Vector3(-1,0,0));this.add(e);var f=new THREE.PerspectiveCamera(90,1,a,b);f.up.set(0,0,1);f.lookAt(new THREE.Vector3(0,1,0));this.add(f);var g=new THREE.PerspectiveCamera(90,1,a,b);g.up.set(0,0,-1);g.lookAt(new THREE.Vector3(0,-1,0));
-this.add(g);var h=new THREE.PerspectiveCamera(90,1,a,b);h.up.set(0,-1,0);h.lookAt(new THREE.Vector3(0,0,1));this.add(h);var k=new THREE.PerspectiveCamera(90,1,a,b);k.up.set(0,-1,0);k.lookAt(new THREE.Vector3(0,0,-1));this.add(k);this.renderTarget=new THREE.WebGLRenderTargetCube(c,c,{format:THREE.RGBFormat,magFilter:THREE.LinearFilter,minFilter:THREE.LinearFilter});this.updateCubeMap=function(a,b){var c=this.renderTarget,m=c.generateMipmaps;c.generateMipmaps=!1;c.activeCubeFace=0;a.render(b,d,c);c.activeCubeFace=
-1;a.render(b,e,c);c.activeCubeFace=2;a.render(b,f,c);c.activeCubeFace=3;a.render(b,g,c);c.activeCubeFace=4;a.render(b,h,c);c.generateMipmaps=m;c.activeCubeFace=5;a.render(b,k,c)}};THREE.CubeCamera.prototype=Object.create(THREE.Object3D.prototype);THREE.OrthographicCamera=function(a,b,c,d,e,f){THREE.Camera.call(this);this.type="OrthographicCamera";this.zoom=1;this.left=a;this.right=b;this.top=c;this.bottom=d;this.near=void 0!==e?e:.1;this.far=void 0!==f?f:2E3;this.updateProjectionMatrix()};
-THREE.OrthographicCamera.prototype=Object.create(THREE.Camera.prototype);THREE.OrthographicCamera.prototype.updateProjectionMatrix=function(){var a=(this.right-this.left)/(2*this.zoom),b=(this.top-this.bottom)/(2*this.zoom),c=(this.right+this.left)/2,d=(this.top+this.bottom)/2;this.projectionMatrix.makeOrthographic(c-a,c+a,d+b,d-b,this.near,this.far)};
-THREE.OrthographicCamera.prototype.clone=function(){var a=new THREE.OrthographicCamera;THREE.Camera.prototype.clone.call(this,a);a.zoom=this.zoom;a.left=this.left;a.right=this.right;a.top=this.top;a.bottom=this.bottom;a.near=this.near;a.far=this.far;a.projectionMatrix.copy(this.projectionMatrix);return a};
-THREE.PerspectiveCamera=function(a,b,c,d){THREE.Camera.call(this);this.type="PerspectiveCamera";this.zoom=1;this.fov=void 0!==a?a:50;this.aspect=void 0!==b?b:1;this.near=void 0!==c?c:.1;this.far=void 0!==d?d:2E3;this.updateProjectionMatrix()};THREE.PerspectiveCamera.prototype=Object.create(THREE.Camera.prototype);THREE.PerspectiveCamera.prototype.setLens=function(a,b){void 0===b&&(b=24);this.fov=2*THREE.Math.radToDeg(Math.atan(b/(2*a)));this.updateProjectionMatrix()};
-THREE.PerspectiveCamera.prototype.setViewOffset=function(a,b,c,d,e,f){this.fullWidth=a;this.fullHeight=b;this.x=c;this.y=d;this.width=e;this.height=f;this.updateProjectionMatrix()};
-THREE.PerspectiveCamera.prototype.updateProjectionMatrix=function(){var a=THREE.Math.radToDeg(2*Math.atan(Math.tan(.5*THREE.Math.degToRad(this.fov))/this.zoom));if(this.fullWidth){var b=this.fullWidth/this.fullHeight,a=Math.tan(THREE.Math.degToRad(.5*a))*this.near,c=-a,d=b*c,b=Math.abs(b*a-d),c=Math.abs(a-c);this.projectionMatrix.makeFrustum(d+this.x*b/this.fullWidth,d+(this.x+this.width)*b/this.fullWidth,a-(this.y+this.height)*c/this.fullHeight,a-this.y*c/this.fullHeight,this.near,this.far)}else this.projectionMatrix.makePerspective(a,
-this.aspect,this.near,this.far)};THREE.PerspectiveCamera.prototype.clone=function(){var a=new THREE.PerspectiveCamera;THREE.Camera.prototype.clone.call(this,a);a.zoom=this.zoom;a.fov=this.fov;a.aspect=this.aspect;a.near=this.near;a.far=this.far;a.projectionMatrix.copy(this.projectionMatrix);return a};THREE.Light=function(a){THREE.Object3D.call(this);this.type="Light";this.color=new THREE.Color(a)};THREE.Light.prototype=Object.create(THREE.Object3D.prototype);
-THREE.Light.prototype.clone=function(a){void 0===a&&(a=new THREE.Light);THREE.Object3D.prototype.clone.call(this,a);a.color.copy(this.color);return a};THREE.AmbientLight=function(a){THREE.Light.call(this,a);this.type="AmbientLight"};THREE.AmbientLight.prototype=Object.create(THREE.Light.prototype);THREE.AmbientLight.prototype.clone=function(){var a=new THREE.AmbientLight;THREE.Light.prototype.clone.call(this,a);return a};
-THREE.AreaLight=function(a,b){THREE.Light.call(this,a);this.type="AreaLight";this.normal=new THREE.Vector3(0,-1,0);this.right=new THREE.Vector3(1,0,0);this.intensity=void 0!==b?b:1;this.height=this.width=1;this.constantAttenuation=1.5;this.linearAttenuation=.5;this.quadraticAttenuation=.1};THREE.AreaLight.prototype=Object.create(THREE.Light.prototype);
-THREE.DirectionalLight=function(a,b){THREE.Light.call(this,a);this.type="DirectionalLight";this.position.set(0,1,0);this.target=new THREE.Object3D;this.intensity=void 0!==b?b:1;this.onlyShadow=this.castShadow=!1;this.shadowCameraNear=50;this.shadowCameraFar=5E3;this.shadowCameraLeft=-500;this.shadowCameraTop=this.shadowCameraRight=500;this.shadowCameraBottom=-500;this.shadowCameraVisible=!1;this.shadowBias=0;this.shadowDarkness=.5;this.shadowMapHeight=this.shadowMapWidth=512;this.shadowCascade=!1;
-this.shadowCascadeOffset=new THREE.Vector3(0,0,-1E3);this.shadowCascadeCount=2;this.shadowCascadeBias=[0,0,0];this.shadowCascadeWidth=[512,512,512];this.shadowCascadeHeight=[512,512,512];this.shadowCascadeNearZ=[-1,.99,.998];this.shadowCascadeFarZ=[.99,.998,1];this.shadowCascadeArray=[];this.shadowMatrix=this.shadowCamera=this.shadowMapSize=this.shadowMap=null};THREE.DirectionalLight.prototype=Object.create(THREE.Light.prototype);
-THREE.DirectionalLight.prototype.clone=function(){var a=new THREE.DirectionalLight;THREE.Light.prototype.clone.call(this,a);a.target=this.target.clone();a.intensity=this.intensity;a.castShadow=this.castShadow;a.onlyShadow=this.onlyShadow;a.shadowCameraNear=this.shadowCameraNear;a.shadowCameraFar=this.shadowCameraFar;a.shadowCameraLeft=this.shadowCameraLeft;a.shadowCameraRight=this.shadowCameraRight;a.shadowCameraTop=this.shadowCameraTop;a.shadowCameraBottom=this.shadowCameraBottom;a.shadowCameraVisible=
-this.shadowCameraVisible;a.shadowBias=this.shadowBias;a.shadowDarkness=this.shadowDarkness;a.shadowMapWidth=this.shadowMapWidth;a.shadowMapHeight=this.shadowMapHeight;a.shadowCascade=this.shadowCascade;a.shadowCascadeOffset.copy(this.shadowCascadeOffset);a.shadowCascadeCount=this.shadowCascadeCount;a.shadowCascadeBias=this.shadowCascadeBias.slice(0);a.shadowCascadeWidth=this.shadowCascadeWidth.slice(0);a.shadowCascadeHeight=this.shadowCascadeHeight.slice(0);a.shadowCascadeNearZ=this.shadowCascadeNearZ.slice(0);
-a.shadowCascadeFarZ=this.shadowCascadeFarZ.slice(0);return a};THREE.HemisphereLight=function(a,b,c){THREE.Light.call(this,a);this.type="HemisphereLight";this.position.set(0,100,0);this.groundColor=new THREE.Color(b);this.intensity=void 0!==c?c:1};THREE.HemisphereLight.prototype=Object.create(THREE.Light.prototype);
-THREE.HemisphereLight.prototype.clone=function(){var a=new THREE.HemisphereLight;THREE.Light.prototype.clone.call(this,a);a.groundColor.copy(this.groundColor);a.intensity=this.intensity;return a};THREE.PointLight=function(a,b,c){THREE.Light.call(this,a);this.type="PointLight";this.intensity=void 0!==b?b:1;this.distance=void 0!==c?c:0};THREE.PointLight.prototype=Object.create(THREE.Light.prototype);
-THREE.PointLight.prototype.clone=function(){var a=new THREE.PointLight;THREE.Light.prototype.clone.call(this,a);a.intensity=this.intensity;a.distance=this.distance;return a};
-THREE.SpotLight=function(a,b,c,d,e){THREE.Light.call(this,a);this.type="SpotLight";this.position.set(0,1,0);this.target=new THREE.Object3D;this.intensity=void 0!==b?b:1;this.distance=void 0!==c?c:0;this.angle=void 0!==d?d:Math.PI/3;this.exponent=void 0!==e?e:10;this.onlyShadow=this.castShadow=!1;this.shadowCameraNear=50;this.shadowCameraFar=5E3;this.shadowCameraFov=50;this.shadowCameraVisible=!1;this.shadowBias=0;this.shadowDarkness=.5;this.shadowMapHeight=this.shadowMapWidth=512;this.shadowMatrix=
-this.shadowCamera=this.shadowMapSize=this.shadowMap=null};THREE.SpotLight.prototype=Object.create(THREE.Light.prototype);
-THREE.SpotLight.prototype.clone=function(){var a=new THREE.SpotLight;THREE.Light.prototype.clone.call(this,a);a.target=this.target.clone();a.intensity=this.intensity;a.distance=this.distance;a.angle=this.angle;a.exponent=this.exponent;a.castShadow=this.castShadow;a.onlyShadow=this.onlyShadow;a.shadowCameraNear=this.shadowCameraNear;a.shadowCameraFar=this.shadowCameraFar;a.shadowCameraFov=this.shadowCameraFov;a.shadowCameraVisible=this.shadowCameraVisible;a.shadowBias=this.shadowBias;a.shadowDarkness=
-this.shadowDarkness;a.shadowMapWidth=this.shadowMapWidth;a.shadowMapHeight=this.shadowMapHeight;return a};THREE.Cache=function(){this.files={}};THREE.Cache.prototype={constructor:THREE.Cache,add:function(a,b){this.files[a]=b},get:function(a){return this.files[a]},remove:function(a){delete this.files[a]},clear:function(){this.files={}}};
-THREE.Loader=function(a){this.statusDomElement=(this.showStatus=a)?THREE.Loader.prototype.addStatusElement():null;this.imageLoader=new THREE.ImageLoader;this.onLoadStart=function(){};this.onLoadProgress=function(){};this.onLoadComplete=function(){}};
-THREE.Loader.prototype={constructor:THREE.Loader,crossOrigin:void 0,addStatusElement:function(){var a=document.createElement("div");a.style.position="absolute";a.style.right="0px";a.style.top="0px";a.style.fontSize="0.8em";a.style.textAlign="left";a.style.background="rgba(0,0,0,0.25)";a.style.color="#fff";a.style.width="120px";a.style.padding="0.5em 0.5em 0.5em 0.5em";a.style.zIndex=1E3;a.innerHTML="Loading ...";return a},updateProgress:function(a){var b="Loaded ",b=a.total?b+((100*a.loaded/a.total).toFixed(0)+
-"%"):b+((a.loaded/1024).toFixed(2)+" KB");this.statusDomElement.innerHTML=b},extractUrlBase:function(a){a=a.split("/");if(1===a.length)return"./";a.pop();return a.join("/")+"/"},initMaterials:function(a,b){for(var c=[],d=0;d<a.length;++d)c[d]=this.createMaterial(a[d],b);return c},needsTangents:function(a){for(var b=0,c=a.length;b<c;b++)if(a[b]instanceof THREE.ShaderMaterial)return!0;return!1},createMaterial:function(a,b){function c(a){a=Math.log(a)/Math.LN2;return Math.pow(2,Math.round(a))}function d(a,
-d,e,g,h,k,s){var u=b+e,v,y=THREE.Loader.Handlers.get(u);null!==y?v=y.load(u):(v=new THREE.Texture,y=f.imageLoader,y.crossOrigin=f.crossOrigin,y.load(u,function(a){if(!1===THREE.Math.isPowerOfTwo(a.width)||!1===THREE.Math.isPowerOfTwo(a.height)){var b=c(a.width),d=c(a.height),e=document.createElement("canvas");e.width=b;e.height=d;e.getContext("2d").drawImage(a,0,0,b,d);v.image=e}else v.image=a;v.needsUpdate=!0}));v.sourceFile=e;g&&(v.repeat.set(g[0],g[1]),1!==g[0]&&(v.wrapS=THREE.RepeatWrapping),
-1!==g[1]&&(v.wrapT=THREE.RepeatWrapping));h&&v.offset.set(h[0],h[1]);k&&(e={repeat:THREE.RepeatWrapping,mirror:THREE.MirroredRepeatWrapping},void 0!==e[k[0]]&&(v.wrapS=e[k[0]]),void 0!==e[k[1]]&&(v.wrapT=e[k[1]]));s&&(v.anisotropy=s);a[d]=v}function e(a){return(255*a[0]<<16)+(255*a[1]<<8)+255*a[2]}var f=this,g="MeshLambertMaterial",h={color:15658734,opacity:1,map:null,lightMap:null,normalMap:null,bumpMap:null,wireframe:!1};if(a.shading){var k=a.shading.toLowerCase();"phong"===k?g="MeshPhongMaterial":
-"basic"===k&&(g="MeshBasicMaterial")}void 0!==a.blending&&void 0!==THREE[a.blending]&&(h.blending=THREE[a.blending]);if(void 0!==a.transparent||1>a.opacity)h.transparent=a.transparent;void 0!==a.depthTest&&(h.depthTest=a.depthTest);void 0!==a.depthWrite&&(h.depthWrite=a.depthWrite);void 0!==a.visible&&(h.visible=a.visible);void 0!==a.flipSided&&(h.side=THREE.BackSide);void 0!==a.doubleSided&&(h.side=THREE.DoubleSide);void 0!==a.wireframe&&(h.wireframe=a.wireframe);void 0!==a.vertexColors&&("face"===
-a.vertexColors?h.vertexColors=THREE.FaceColors:a.vertexColors&&(h.vertexColors=THREE.VertexColors));a.colorDiffuse?h.color=e(a.colorDiffuse):a.DbgColor&&(h.color=a.DbgColor);a.colorSpecular&&(h.specular=e(a.colorSpecular));a.colorAmbient&&(h.ambient=e(a.colorAmbient));a.colorEmissive&&(h.emissive=e(a.colorEmissive));a.transparency&&(h.opacity=a.transparency);a.specularCoef&&(h.shininess=a.specularCoef);a.mapDiffuse&&b&&d(h,"map",a.mapDiffuse,a.mapDiffuseRepeat,a.mapDiffuseOffset,a.mapDiffuseWrap,
-a.mapDiffuseAnisotropy);a.mapLight&&b&&d(h,"lightMap",a.mapLight,a.mapLightRepeat,a.mapLightOffset,a.mapLightWrap,a.mapLightAnisotropy);a.mapBump&&b&&d(h,"bumpMap",a.mapBump,a.mapBumpRepeat,a.mapBumpOffset,a.mapBumpWrap,a.mapBumpAnisotropy);a.mapNormal&&b&&d(h,"normalMap",a.mapNormal,a.mapNormalRepeat,a.mapNormalOffset,a.mapNormalWrap,a.mapNormalAnisotropy);a.mapSpecular&&b&&d(h,"specularMap",a.mapSpecular,a.mapSpecularRepeat,a.mapSpecularOffset,a.mapSpecularWrap,a.mapSpecularAnisotropy);a.mapAlpha&&
-b&&d(h,"alphaMap",a.mapAlpha,a.mapAlphaRepeat,a.mapAlphaOffset,a.mapAlphaWrap,a.mapAlphaAnisotropy);a.mapBumpScale&&(h.bumpScale=a.mapBumpScale);a.mapNormal?(g=THREE.ShaderLib.normalmap,k=THREE.UniformsUtils.clone(g.uniforms),k.tNormal.value=h.normalMap,a.mapNormalFactor&&k.uNormalScale.value.set(a.mapNormalFactor,a.mapNormalFactor),h.map&&(k.tDiffuse.value=h.map,k.enableDiffuse.value=!0),h.specularMap&&(k.tSpecular.value=h.specularMap,k.enableSpecular.value=!0),h.lightMap&&(k.tAO.value=h.lightMap,
-k.enableAO.value=!0),k.diffuse.value.setHex(h.color),k.specular.value.setHex(h.specular),k.ambient.value.setHex(h.ambient),k.shininess.value=h.shininess,void 0!==h.opacity&&(k.opacity.value=h.opacity),g=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,vertexShader:g.vertexShader,uniforms:k,lights:!0,fog:!0}),h.transparent&&(g.transparent=!0)):g=new THREE[g](h);void 0!==a.DbgName&&(g.name=a.DbgName);return g}};
-THREE.Loader.Handlers={handlers:[],add:function(a,b){this.handlers.push(a,b)},get:function(a){for(var b=0,c=this.handlers.length;b<c;b+=2){var d=this.handlers[b+1];if(this.handlers[b].test(a))return d}return null}};THREE.XHRLoader=function(a){this.cache=new THREE.Cache;this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
-THREE.XHRLoader.prototype={constructor:THREE.XHRLoader,load:function(a,b,c,d){var e=this,f=e.cache.get(a);void 0!==f?b&&b(f):(f=new XMLHttpRequest,f.open("GET",a,!0),f.addEventListener("load",function(c){e.cache.add(a,this.response);b&&b(this.response);e.manager.itemEnd(a)},!1),void 0!==c&&f.addEventListener("progress",function(a){c(a)},!1),void 0!==d&&f.addEventListener("error",function(a){d(a)},!1),void 0!==this.crossOrigin&&(f.crossOrigin=this.crossOrigin),void 0!==this.responseType&&(f.responseType=
-this.responseType),f.send(null),e.manager.itemStart(a))},setResponseType:function(a){this.responseType=a},setCrossOrigin:function(a){this.crossOrigin=a}};THREE.ImageLoader=function(a){this.cache=new THREE.Cache;this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
-THREE.ImageLoader.prototype={constructor:THREE.ImageLoader,load:function(a,b,c,d){var e=this,f=e.cache.get(a);if(void 0!==f)b(f);else return f=document.createElement("img"),void 0!==b&&f.addEventListener("load",function(c){e.cache.add(a,this);b(this);e.manager.itemEnd(a)},!1),void 0!==c&&f.addEventListener("progress",function(a){c(a)},!1),void 0!==d&&f.addEventListener("error",function(a){d(a)},!1),void 0!==this.crossOrigin&&(f.crossOrigin=this.crossOrigin),f.src=a,e.manager.itemStart(a),f},setCrossOrigin:function(a){this.crossOrigin=
-a}};THREE.JSONLoader=function(a){THREE.Loader.call(this,a);this.withCredentials=!1};THREE.JSONLoader.prototype=Object.create(THREE.Loader.prototype);THREE.JSONLoader.prototype.load=function(a,b,c){c=c&&"string"===typeof c?c:this.extractUrlBase(a);this.onLoadStart();this.loadAjaxJSON(this,a,b,c)};
-THREE.JSONLoader.prototype.loadAjaxJSON=function(a,b,c,d,e){var f=new XMLHttpRequest,g=0;f.onreadystatechange=function(){if(f.readyState===f.DONE)if(200===f.status||0===f.status){if(f.responseText){var h=JSON.parse(f.responseText);if(void 0!==h.metadata&&"scene"===h.metadata.type){console.error('THREE.JSONLoader: "'+b+'" seems to be a Scene. Use THREE.SceneLoader instead.');return}h=a.parse(h,d);c(h.geometry,h.materials)}else console.error('THREE.JSONLoader: "'+b+'" seems to be unreachable or the file is empty.');
-a.onLoadComplete()}else console.error("THREE.JSONLoader: Couldn't load \""+b+'" ('+f.status+")");else f.readyState===f.LOADING?e&&(0===g&&(g=f.getResponseHeader("Content-Length")),e({total:g,loaded:f.responseText.length})):f.readyState===f.HEADERS_RECEIVED&&void 0!==e&&(g=f.getResponseHeader("Content-Length"))};f.open("GET",b,!0);f.withCredentials=this.withCredentials;f.send(null)};
-THREE.JSONLoader.prototype.parse=function(a,b){var c=new THREE.Geometry,d=void 0!==a.scale?1/a.scale:1;(function(b){var d,g,h,k,n,p,q,m,r,t,s,u,v,y=a.faces;p=a.vertices;var G=a.normals,w=a.colors,K=0;if(void 0!==a.uvs){for(d=0;d<a.uvs.length;d++)a.uvs[d].length&&K++;for(d=0;d<K;d++)c.faceVertexUvs[d]=[]}k=0;for(n=p.length;k<n;)d=new THREE.Vector3,d.x=p[k++]*b,d.y=p[k++]*b,d.z=p[k++]*b,c.vertices.push(d);k=0;for(n=y.length;k<n;)if(b=y[k++],r=b&1,h=b&2,d=b&8,q=b&16,t=b&32,p=b&64,b&=128,r){r=new THREE.Face3;
-r.a=y[k];r.b=y[k+1];r.c=y[k+3];s=new THREE.Face3;s.a=y[k+1];s.b=y[k+2];s.c=y[k+3];k+=4;h&&(h=y[k++],r.materialIndex=h,s.materialIndex=h);h=c.faces.length;if(d)for(d=0;d<K;d++)for(u=a.uvs[d],c.faceVertexUvs[d][h]=[],c.faceVertexUvs[d][h+1]=[],g=0;4>g;g++)m=y[k++],v=u[2*m],m=u[2*m+1],v=new THREE.Vector2(v,m),2!==g&&c.faceVertexUvs[d][h].push(v),0!==g&&c.faceVertexUvs[d][h+1].push(v);q&&(q=3*y[k++],r.normal.set(G[q++],G[q++],G[q]),s.normal.copy(r.normal));if(t)for(d=0;4>d;d++)q=3*y[k++],t=new THREE.Vector3(G[q++],
-G[q++],G[q]),2!==d&&r.vertexNormals.push(t),0!==d&&s.vertexNormals.push(t);p&&(p=y[k++],p=w[p],r.color.setHex(p),s.color.setHex(p));if(b)for(d=0;4>d;d++)p=y[k++],p=w[p],2!==d&&r.vertexColors.push(new THREE.Color(p)),0!==d&&s.vertexColors.push(new THREE.Color(p));c.faces.push(r);c.faces.push(s)}else{r=new THREE.Face3;r.a=y[k++];r.b=y[k++];r.c=y[k++];h&&(h=y[k++],r.materialIndex=h);h=c.faces.length;if(d)for(d=0;d<K;d++)for(u=a.uvs[d],c.faceVertexUvs[d][h]=[],g=0;3>g;g++)m=y[k++],v=u[2*m],m=u[2*m+1],
-v=new THREE.Vector2(v,m),c.faceVertexUvs[d][h].push(v);q&&(q=3*y[k++],r.normal.set(G[q++],G[q++],G[q]));if(t)for(d=0;3>d;d++)q=3*y[k++],t=new THREE.Vector3(G[q++],G[q++],G[q]),r.vertexNormals.push(t);p&&(p=y[k++],r.color.setHex(w[p]));if(b)for(d=0;3>d;d++)p=y[k++],r.vertexColors.push(new THREE.Color(w[p]));c.faces.push(r)}})(d);(function(){var b=void 0!==a.influencesPerVertex?a.influencesPerVertex:2;if(a.skinWeights)for(var d=0,g=a.skinWeights.length;d<g;d+=b)c.skinWeights.push(new THREE.Vector4(a.skinWeights[d],
-1<b?a.skinWeights[d+1]:0,2<b?a.skinWeights[d+2]:0,3<b?a.skinWeights[d+3]:0));if(a.skinIndices)for(d=0,g=a.skinIndices.length;d<g;d+=b)c.skinIndices.push(new THREE.Vector4(a.skinIndices[d],1<b?a.skinIndices[d+1]:0,2<b?a.skinIndices[d+2]:0,3<b?a.skinIndices[d+3]:0));c.bones=a.bones;c.bones&&0<c.bones.length&&(c.skinWeights.length!==c.skinIndices.length||c.skinIndices.length!==c.vertices.length)&&console.warn("When skinning, number of vertices ("+c.vertices.length+"), skinIndices ("+c.skinIndices.length+
-"), and skinWeights ("+c.skinWeights.length+") should match.");c.animation=a.animation;c.animations=a.animations})();(function(b){if(void 0!==a.morphTargets){var d,g,h,k,n,p;d=0;for(g=a.morphTargets.length;d<g;d++)for(c.morphTargets[d]={},c.morphTargets[d].name=a.morphTargets[d].name,c.morphTargets[d].vertices=[],n=c.morphTargets[d].vertices,p=a.morphTargets[d].vertices,h=0,k=p.length;h<k;h+=3){var q=new THREE.Vector3;q.x=p[h]*b;q.y=p[h+1]*b;q.z=p[h+2]*b;n.push(q)}}if(void 0!==a.morphColors)for(d=
-0,g=a.morphColors.length;d<g;d++)for(c.morphColors[d]={},c.morphColors[d].name=a.morphColors[d].name,c.morphColors[d].colors=[],k=c.morphColors[d].colors,n=a.morphColors[d].colors,b=0,h=n.length;b<h;b+=3)p=new THREE.Color(16755200),p.setRGB(n[b],n[b+1],n[b+2]),k.push(p)})(d);c.computeFaceNormals();c.computeBoundingSphere();if(void 0===a.materials||0===a.materials.length)return{geometry:c};d=this.initMaterials(a.materials,b);this.needsTangents(d)&&c.computeTangents();return{geometry:c,materials:d}};
-THREE.LoadingManager=function(a,b,c){var d=this,e=0,f=0;this.onLoad=a;this.onProgress=b;this.onError=c;this.itemStart=function(a){f++};this.itemEnd=function(a){e++;if(void 0!==d.onProgress)d.onProgress(a,e,f);if(e===f&&void 0!==d.onLoad)d.onLoad()}};THREE.DefaultLoadingManager=new THREE.LoadingManager;THREE.BufferGeometryLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
-THREE.BufferGeometryLoader.prototype={constructor:THREE.BufferGeometryLoader,load:function(a,b,c,d){var e=this,f=new THREE.XHRLoader;f.setCrossOrigin(this.crossOrigin);f.load(a,function(a){b(e.parse(JSON.parse(a)))},c,d)},setCrossOrigin:function(a){this.crossOrigin=a},parse:function(a){var b=new THREE.BufferGeometry,c=a.attributes,d;for(d in c){var e=c[d],f=new self[e.type](e.array);b.addAttribute(d,new THREE.BufferAttribute(f,e.itemSize))}c=a.offsets;void 0!==c&&(b.offsets=JSON.parse(JSON.stringify(c)));
-a=a.boundingSphere;void 0!==a&&(c=new THREE.Vector3,void 0!==a.center&&c.fromArray(a.center),b.boundingSphere=new THREE.Sphere(c,a.radius));return b}};THREE.MaterialLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
-THREE.MaterialLoader.prototype={constructor:THREE.MaterialLoader,load:function(a,b,c,d){var e=this,f=new THREE.XHRLoader;f.setCrossOrigin(this.crossOrigin);f.load(a,function(a){b(e.parse(JSON.parse(a)))},c,d)},setCrossOrigin:function(a){this.crossOrigin=a},parse:function(a){var b=new THREE[a.type];void 0!==a.color&&b.color.setHex(a.color);void 0!==a.ambient&&b.ambient.setHex(a.ambient);void 0!==a.emissive&&b.emissive.setHex(a.emissive);void 0!==a.specular&&b.specular.setHex(a.specular);void 0!==a.shininess&&
-(b.shininess=a.shininess);void 0!==a.uniforms&&(b.uniforms=a.uniforms);void 0!==a.vertexShader&&(b.vertexShader=a.vertexShader);void 0!==a.fragmentShader&&(b.fragmentShader=a.fragmentShader);void 0!==a.vertexColors&&(b.vertexColors=a.vertexColors);void 0!==a.shading&&(b.shading=a.shading);void 0!==a.blending&&(b.blending=a.blending);void 0!==a.side&&(b.side=a.side);void 0!==a.opacity&&(b.opacity=a.opacity);void 0!==a.transparent&&(b.transparent=a.transparent);void 0!==a.wireframe&&(b.wireframe=a.wireframe);
-if(void 0!==a.materials)for(var c=0,d=a.materials.length;c<d;c++)b.materials.push(this.parse(a.materials[c]));return b}};THREE.ObjectLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
-THREE.ObjectLoader.prototype={constructor:THREE.ObjectLoader,load:function(a,b,c,d){var e=this,f=new THREE.XHRLoader(e.manager);f.setCrossOrigin(this.crossOrigin);f.load(a,function(a){b(e.parse(JSON.parse(a)))},c,d)},setCrossOrigin:function(a){this.crossOrigin=a},parse:function(a){var b=this.parseGeometries(a.geometries),c=this.parseMaterials(a.materials);return this.parseObject(a.object,b,c)},parseGeometries:function(a){var b={};if(void 0!==a)for(var c=new THREE.JSONLoader,d=new THREE.BufferGeometryLoader,
-e=0,f=a.length;e<f;e++){var g,h=a[e];switch(h.type){case "PlaneGeometry":g=new THREE.PlaneGeometry(h.width,h.height,h.widthSegments,h.heightSegments);break;case "BoxGeometry":case "CubeGeometry":g=new THREE.BoxGeometry(h.width,h.height,h.depth,h.widthSegments,h.heightSegments,h.depthSegments);break;case "CircleGeometry":g=new THREE.CircleGeometry(h.radius,h.segments);break;case "CylinderGeometry":g=new THREE.CylinderGeometry(h.radiusTop,h.radiusBottom,h.height,h.radialSegments,h.heightSegments,h.openEnded);
-break;case "SphereGeometry":g=new THREE.SphereGeometry(h.radius,h.widthSegments,h.heightSegments,h.phiStart,h.phiLength,h.thetaStart,h.thetaLength);break;case "IcosahedronGeometry":g=new THREE.IcosahedronGeometry(h.radius,h.detail);break;case "TorusGeometry":g=new THREE.TorusGeometry(h.radius,h.tube,h.radialSegments,h.tubularSegments,h.arc);break;case "TorusKnotGeometry":g=new THREE.TorusKnotGeometry(h.radius,h.tube,h.radialSegments,h.tubularSegments,h.p,h.q,h.heightScale);break;case "BufferGeometry":g=
-d.parse(h.data);break;case "Geometry":g=c.parse(h.data).geometry}g.uuid=h.uuid;void 0!==h.name&&(g.name=h.name);b[h.uuid]=g}return b},parseMaterials:function(a){var b={};if(void 0!==a)for(var c=new THREE.MaterialLoader,d=0,e=a.length;d<e;d++){var f=a[d],g=c.parse(f);g.uuid=f.uuid;void 0!==f.name&&(g.name=f.name);b[f.uuid]=g}return b},parseObject:function(){var a=new THREE.Matrix4;return function(b,c,d){var e;switch(b.type){case "Scene":e=new THREE.Scene;break;case "PerspectiveCamera":e=new THREE.PerspectiveCamera(b.fov,
-b.aspect,b.near,b.far);break;case "OrthographicCamera":e=new THREE.OrthographicCamera(b.left,b.right,b.top,b.bottom,b.near,b.far);break;case "AmbientLight":e=new THREE.AmbientLight(b.color);break;case "DirectionalLight":e=new THREE.DirectionalLight(b.color,b.intensity);break;case "PointLight":e=new THREE.PointLight(b.color,b.intensity,b.distance);break;case "SpotLight":e=new THREE.SpotLight(b.color,b.intensity,b.distance,b.angle,b.exponent);break;case "HemisphereLight":e=new THREE.HemisphereLight(b.color,
-b.groundColor,b.intensity);break;case "Mesh":e=c[b.geometry];var f=d[b.material];void 0===e&&console.warn("THREE.ObjectLoader: Undefined geometry",b.geometry);void 0===f&&console.warn("THREE.ObjectLoader: Undefined material",b.material);e=new THREE.Mesh(e,f);break;case "Line":e=c[b.geometry];f=d[b.material];void 0===e&&console.warn("THREE.ObjectLoader: Undefined geometry",b.geometry);void 0===f&&console.warn("THREE.ObjectLoader: Undefined material",b.material);e=new THREE.Line(e,f);break;case "Sprite":f=
-d[b.material];void 0===f&&console.warn("THREE.ObjectLoader: Undefined material",b.material);e=new THREE.Sprite(f);break;case "Group":e=new THREE.Group;break;default:e=new THREE.Object3D}e.uuid=b.uuid;void 0!==b.name&&(e.name=b.name);void 0!==b.matrix?(a.fromArray(b.matrix),a.decompose(e.position,e.quaternion,e.scale)):(void 0!==b.position&&e.position.fromArray(b.position),void 0!==b.rotation&&e.rotation.fromArray(b.rotation),void 0!==b.scale&&e.scale.fromArray(b.scale));void 0!==b.visible&&(e.visible=
-b.visible);void 0!==b.userData&&(e.userData=b.userData);if(void 0!==b.children)for(var g in b.children)e.add(this.parseObject(b.children[g],c,d));return e}}()};THREE.TextureLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
-THREE.TextureLoader.prototype={constructor:THREE.TextureLoader,load:function(a,b,c,d){var e=new THREE.ImageLoader(this.manager);e.setCrossOrigin(this.crossOrigin);e.load(a,function(a){a=new THREE.Texture(a);a.needsUpdate=!0;void 0!==b&&b(a)},c,d)},setCrossOrigin:function(a){this.crossOrigin=a}};THREE.CompressedTextureLoader=function(){this._parser=null};
-THREE.CompressedTextureLoader.prototype={constructor:THREE.CompressedTextureLoader,load:function(a,b,c){var d=this,e=[],f=new THREE.CompressedTexture;f.image=e;var g=new THREE.XHRLoader;g.setResponseType("arraybuffer");if(a instanceof Array){var h=0;c=function(c){g.load(a[c],function(a){a=d._parser(a,!0);e[c]={width:a.width,height:a.height,format:a.format,mipmaps:a.mipmaps};h+=1;6===h&&(1==a.mipmapCount&&(f.minFilter=THREE.LinearFilter),f.format=a.format,f.needsUpdate=!0,b&&b(f))})};for(var k=0,n=
-a.length;k<n;++k)c(k)}else g.load(a,function(a){a=d._parser(a,!0);if(a.isCubemap)for(var c=a.mipmaps.length/a.mipmapCount,g=0;g<c;g++){e[g]={mipmaps:[]};for(var h=0;h<a.mipmapCount;h++)e[g].mipmaps.push(a.mipmaps[g*a.mipmapCount+h]),e[g].format=a.format,e[g].width=a.width,e[g].height=a.height}else f.image.width=a.width,f.image.height=a.height,f.mipmaps=a.mipmaps;1===a.mipmapCount&&(f.minFilter=THREE.LinearFilter);f.format=a.format;f.needsUpdate=!0;b&&b(f)});return f}};
-THREE.Material=function(){Object.defineProperty(this,"id",{value:THREE.MaterialIdCount++});this.uuid=THREE.Math.generateUUID();this.name="";this.type="Material";this.side=THREE.FrontSide;this.opacity=1;this.transparent=!1;this.blending=THREE.NormalBlending;this.blendSrc=THREE.SrcAlphaFactor;this.blendDst=THREE.OneMinusSrcAlphaFactor;this.blendEquation=THREE.AddEquation;this.depthWrite=this.depthTest=!0;this.polygonOffset=!1;this.overdraw=this.alphaTest=this.polygonOffsetUnits=this.polygonOffsetFactor=
-0;this.needsUpdate=this.visible=!0};
-THREE.Material.prototype={constructor:THREE.Material,setValues:function(a){if(void 0!==a)for(var b in a){var c=a[b];if(void 0===c)console.warn("THREE.Material: '"+b+"' parameter is undefined.");else if(b in this){var d=this[b];d instanceof THREE.Color?d.set(c):d instanceof THREE.Vector3&&c instanceof THREE.Vector3?d.copy(c):this[b]="overdraw"==b?Number(c):c}}},toJSON:function(){var a={metadata:{version:4.2,type:"material",generator:"MaterialExporter"},uuid:this.uuid,type:this.type};""!==this.name&&
-(a.name=this.name);this instanceof THREE.MeshBasicMaterial?(a.color=this.color.getHex(),this.vertexColors!==THREE.NoColors&&(a.vertexColors=this.vertexColors),this.blending!==THREE.NormalBlending&&(a.blending=this.blending),this.side!==THREE.FrontSide&&(a.side=this.side)):this instanceof THREE.MeshLambertMaterial?(a.color=this.color.getHex(),a.ambient=this.ambient.getHex(),a.emissive=this.emissive.getHex(),this.vertexColors!==THREE.NoColors&&(a.vertexColors=this.vertexColors),this.blending!==THREE.NormalBlending&&
-(a.blending=this.blending),this.side!==THREE.FrontSide&&(a.side=this.side)):this instanceof THREE.MeshPhongMaterial?(a.color=this.color.getHex(),a.ambient=this.ambient.getHex(),a.emissive=this.emissive.getHex(),a.specular=this.specular.getHex(),a.shininess=this.shininess,this.vertexColors!==THREE.NoColors&&(a.vertexColors=this.vertexColors),this.blending!==THREE.NormalBlending&&(a.blending=this.blending),this.side!==THREE.FrontSide&&(a.side=this.side)):this instanceof THREE.MeshNormalMaterial?(this.shading!==
-THREE.FlatShading&&(a.shading=this.shading),this.blending!==THREE.NormalBlending&&(a.blending=this.blending),this.side!==THREE.FrontSide&&(a.side=this.side)):this instanceof THREE.MeshDepthMaterial?(this.blending!==THREE.NormalBlending&&(a.blending=this.blending),this.side!==THREE.FrontSide&&(a.side=this.side)):this instanceof THREE.ShaderMaterial?(a.uniforms=this.uniforms,a.vertexShader=this.vertexShader,a.fragmentShader=this.fragmentShader):this instanceof THREE.SpriteMaterial&&(a.color=this.color.getHex());
-1>this.opacity&&(a.opacity=this.opacity);!1!==this.transparent&&(a.transparent=this.transparent);!1!==this.wireframe&&(a.wireframe=this.wireframe);return a},clone:function(a){void 0===a&&(a=new THREE.Material);a.name=this.name;a.side=this.side;a.opacity=this.opacity;a.transparent=this.transparent;a.blending=this.blending;a.blendSrc=this.blendSrc;a.blendDst=this.blendDst;a.blendEquation=this.blendEquation;a.depthTest=this.depthTest;a.depthWrite=this.depthWrite;a.polygonOffset=this.polygonOffset;a.polygonOffsetFactor=
-this.polygonOffsetFactor;a.polygonOffsetUnits=this.polygonOffsetUnits;a.alphaTest=this.alphaTest;a.overdraw=this.overdraw;a.visible=this.visible;return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.Material.prototype);THREE.MaterialIdCount=0;
-THREE.LineBasicMaterial=function(a){THREE.Material.call(this);this.type="LineBasicMaterial";this.color=new THREE.Color(16777215);this.linewidth=1;this.linejoin=this.linecap="round";this.vertexColors=THREE.NoColors;this.fog=!0;this.setValues(a)};THREE.LineBasicMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.LineBasicMaterial.prototype.clone=function(){var a=new THREE.LineBasicMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.linewidth=this.linewidth;a.linecap=this.linecap;a.linejoin=this.linejoin;a.vertexColors=this.vertexColors;a.fog=this.fog;return a};
-THREE.LineDashedMaterial=function(a){THREE.Material.call(this);this.type="LineDashedMaterial";this.color=new THREE.Color(16777215);this.scale=this.linewidth=1;this.dashSize=3;this.gapSize=1;this.vertexColors=!1;this.fog=!0;this.setValues(a)};THREE.LineDashedMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.LineDashedMaterial.prototype.clone=function(){var a=new THREE.LineDashedMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.linewidth=this.linewidth;a.scale=this.scale;a.dashSize=this.dashSize;a.gapSize=this.gapSize;a.vertexColors=this.vertexColors;a.fog=this.fog;return a};
-THREE.MeshBasicMaterial=function(a){THREE.Material.call(this);this.type="MeshBasicMaterial";this.color=new THREE.Color(16777215);this.envMap=this.alphaMap=this.specularMap=this.lightMap=this.map=null;this.combine=THREE.MultiplyOperation;this.reflectivity=1;this.refractionRatio=.98;this.fog=!0;this.shading=THREE.SmoothShading;this.wireframe=!1;this.wireframeLinewidth=1;this.wireframeLinejoin=this.wireframeLinecap="round";this.vertexColors=THREE.NoColors;this.morphTargets=this.skinning=!1;this.setValues(a)};
-THREE.MeshBasicMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.MeshBasicMaterial.prototype.clone=function(){var a=new THREE.MeshBasicMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.map=this.map;a.lightMap=this.lightMap;a.specularMap=this.specularMap;a.alphaMap=this.alphaMap;a.envMap=this.envMap;a.combine=this.combine;a.reflectivity=this.reflectivity;a.refractionRatio=this.refractionRatio;a.fog=this.fog;a.shading=this.shading;a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;a.wireframeLinecap=this.wireframeLinecap;
-a.wireframeLinejoin=this.wireframeLinejoin;a.vertexColors=this.vertexColors;a.skinning=this.skinning;a.morphTargets=this.morphTargets;return a};
-THREE.MeshLambertMaterial=function(a){THREE.Material.call(this);this.type="MeshLambertMaterial";this.color=new THREE.Color(16777215);this.ambient=new THREE.Color(16777215);this.emissive=new THREE.Color(0);this.wrapAround=!1;this.wrapRGB=new THREE.Vector3(1,1,1);this.envMap=this.alphaMap=this.specularMap=this.lightMap=this.map=null;this.combine=THREE.MultiplyOperation;this.reflectivity=1;this.refractionRatio=.98;this.fog=!0;this.shading=THREE.SmoothShading;this.wireframe=!1;this.wireframeLinewidth=
-1;this.wireframeLinejoin=this.wireframeLinecap="round";this.vertexColors=THREE.NoColors;this.morphNormals=this.morphTargets=this.skinning=!1;this.setValues(a)};THREE.MeshLambertMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.MeshLambertMaterial.prototype.clone=function(){var a=new THREE.MeshLambertMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.ambient.copy(this.ambient);a.emissive.copy(this.emissive);a.wrapAround=this.wrapAround;a.wrapRGB.copy(this.wrapRGB);a.map=this.map;a.lightMap=this.lightMap;a.specularMap=this.specularMap;a.alphaMap=this.alphaMap;a.envMap=this.envMap;a.combine=this.combine;a.reflectivity=this.reflectivity;a.refractionRatio=this.refractionRatio;a.fog=this.fog;
-a.shading=this.shading;a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;a.wireframeLinecap=this.wireframeLinecap;a.wireframeLinejoin=this.wireframeLinejoin;a.vertexColors=this.vertexColors;a.skinning=this.skinning;a.morphTargets=this.morphTargets;a.morphNormals=this.morphNormals;return a};
-THREE.MeshPhongMaterial=function(a){THREE.Material.call(this);this.type="MeshPhongMaterial";this.color=new THREE.Color(16777215);this.ambient=new THREE.Color(16777215);this.emissive=new THREE.Color(0);this.specular=new THREE.Color(1118481);this.shininess=30;this.wrapAround=this.metal=!1;this.wrapRGB=new THREE.Vector3(1,1,1);this.bumpMap=this.lightMap=this.map=null;this.bumpScale=1;this.normalMap=null;this.normalScale=new THREE.Vector2(1,1);this.envMap=this.alphaMap=this.specularMap=null;this.combine=
-THREE.MultiplyOperation;this.reflectivity=1;this.refractionRatio=.98;this.fog=!0;this.shading=THREE.SmoothShading;this.wireframe=!1;this.wireframeLinewidth=1;this.wireframeLinejoin=this.wireframeLinecap="round";this.vertexColors=THREE.NoColors;this.morphNormals=this.morphTargets=this.skinning=!1;this.setValues(a)};THREE.MeshPhongMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.MeshPhongMaterial.prototype.clone=function(){var a=new THREE.MeshPhongMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.ambient.copy(this.ambient);a.emissive.copy(this.emissive);a.specular.copy(this.specular);a.shininess=this.shininess;a.metal=this.metal;a.wrapAround=this.wrapAround;a.wrapRGB.copy(this.wrapRGB);a.map=this.map;a.lightMap=this.lightMap;a.bumpMap=this.bumpMap;a.bumpScale=this.bumpScale;a.normalMap=this.normalMap;a.normalScale.copy(this.normalScale);
-a.specularMap=this.specularMap;a.alphaMap=this.alphaMap;a.envMap=this.envMap;a.combine=this.combine;a.reflectivity=this.reflectivity;a.refractionRatio=this.refractionRatio;a.fog=this.fog;a.shading=this.shading;a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;a.wireframeLinecap=this.wireframeLinecap;a.wireframeLinejoin=this.wireframeLinejoin;a.vertexColors=this.vertexColors;a.skinning=this.skinning;a.morphTargets=this.morphTargets;a.morphNormals=this.morphNormals;return a};
-THREE.MeshDepthMaterial=function(a){THREE.Material.call(this);this.type="MeshDepthMaterial";this.wireframe=this.morphTargets=!1;this.wireframeLinewidth=1;this.setValues(a)};THREE.MeshDepthMaterial.prototype=Object.create(THREE.Material.prototype);THREE.MeshDepthMaterial.prototype.clone=function(){var a=new THREE.MeshDepthMaterial;THREE.Material.prototype.clone.call(this,a);a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;return a};
-THREE.MeshNormalMaterial=function(a){THREE.Material.call(this,a);this.type="MeshNormalMaterial";this.shading=THREE.FlatShading;this.wireframe=!1;this.wireframeLinewidth=1;this.morphTargets=!1;this.setValues(a)};THREE.MeshNormalMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.MeshNormalMaterial.prototype.clone=function(){var a=new THREE.MeshNormalMaterial;THREE.Material.prototype.clone.call(this,a);a.shading=this.shading;a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;return a};THREE.MeshFaceMaterial=function(a){this.uuid=THREE.Math.generateUUID();this.type="MeshFaceMaterial";this.materials=a instanceof Array?a:[]};
-THREE.MeshFaceMaterial.prototype={constructor:THREE.MeshFaceMaterial,toJSON:function(){for(var a={metadata:{version:4.2,type:"material",generator:"MaterialExporter"},uuid:this.uuid,type:this.type,materials:[]},b=0,c=this.materials.length;b<c;b++)a.materials.push(this.materials[b].toJSON());return a},clone:function(){for(var a=new THREE.MeshFaceMaterial,b=0;b<this.materials.length;b++)a.materials.push(this.materials[b].clone());return a}};
-THREE.PointCloudMaterial=function(a){THREE.Material.call(this);this.type="PointCloudMaterial";this.color=new THREE.Color(16777215);this.map=null;this.size=1;this.sizeAttenuation=!0;this.vertexColors=THREE.NoColors;this.fog=!0;this.setValues(a)};THREE.PointCloudMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.PointCloudMaterial.prototype.clone=function(){var a=new THREE.PointCloudMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.map=this.map;a.size=this.size;a.sizeAttenuation=this.sizeAttenuation;a.vertexColors=this.vertexColors;a.fog=this.fog;return a};THREE.ParticleBasicMaterial=function(a){console.warn("THREE.ParticleBasicMaterial has been renamed to THREE.PointCloudMaterial.");return new THREE.PointCloudMaterial(a)};
-THREE.ParticleSystemMaterial=function(a){console.warn("THREE.ParticleSystemMaterial has been renamed to THREE.PointCloudMaterial.");return new THREE.PointCloudMaterial(a)};
-THREE.ShaderMaterial=function(a){THREE.Material.call(this);this.type="ShaderMaterial";this.defines={};this.uniforms={};this.attributes=null;this.vertexShader="void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}";this.fragmentShader="void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}";this.shading=THREE.SmoothShading;this.linewidth=1;this.wireframe=!1;this.wireframeLinewidth=1;this.lights=this.fog=!1;this.vertexColors=THREE.NoColors;this.morphNormals=
-this.morphTargets=this.skinning=!1;this.defaultAttributeValues={color:[1,1,1],uv:[0,0],uv2:[0,0]};this.index0AttributeName=void 0;this.setValues(a)};THREE.ShaderMaterial.prototype=Object.create(THREE.Material.prototype);
-THREE.ShaderMaterial.prototype.clone=function(){var a=new THREE.ShaderMaterial;THREE.Material.prototype.clone.call(this,a);a.fragmentShader=this.fragmentShader;a.vertexShader=this.vertexShader;a.uniforms=THREE.UniformsUtils.clone(this.uniforms);a.attributes=this.attributes;a.defines=this.defines;a.shading=this.shading;a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;a.fog=this.fog;a.lights=this.lights;a.vertexColors=this.vertexColors;a.skinning=this.skinning;a.morphTargets=
-this.morphTargets;a.morphNormals=this.morphNormals;return a};THREE.RawShaderMaterial=function(a){THREE.ShaderMaterial.call(this,a);this.type="RawShaderMaterial"};THREE.RawShaderMaterial.prototype=Object.create(THREE.ShaderMaterial.prototype);THREE.RawShaderMaterial.prototype.clone=function(){var a=new THREE.RawShaderMaterial;THREE.ShaderMaterial.prototype.clone.call(this,a);return a};
-THREE.SpriteMaterial=function(a){THREE.Material.call(this);this.type="SpriteMaterial";this.color=new THREE.Color(16777215);this.map=null;this.rotation=0;this.fog=!1;this.setValues(a)};THREE.SpriteMaterial.prototype=Object.create(THREE.Material.prototype);THREE.SpriteMaterial.prototype.clone=function(){var a=new THREE.SpriteMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.map=this.map;a.rotation=this.rotation;a.fog=this.fog;return a};
-THREE.Texture=function(a,b,c,d,e,f,g,h,k){Object.defineProperty(this,"id",{value:THREE.TextureIdCount++});this.uuid=THREE.Math.generateUUID();this.name="";this.image=void 0!==a?a:THREE.Texture.DEFAULT_IMAGE;this.mipmaps=[];this.mapping=void 0!==b?b:THREE.Texture.DEFAULT_MAPPING;this.wrapS=void 0!==c?c:THREE.ClampToEdgeWrapping;this.wrapT=void 0!==d?d:THREE.ClampToEdgeWrapping;this.magFilter=void 0!==e?e:THREE.LinearFilter;this.minFilter=void 0!==f?f:THREE.LinearMipMapLinearFilter;this.anisotropy=
-void 0!==k?k:1;this.format=void 0!==g?g:THREE.RGBAFormat;this.type=void 0!==h?h:THREE.UnsignedByteType;this.offset=new THREE.Vector2(0,0);this.repeat=new THREE.Vector2(1,1);this.generateMipmaps=!0;this.premultiplyAlpha=!1;this.flipY=!0;this.unpackAlignment=4;this._needsUpdate=!1;this.onUpdate=null};THREE.Texture.DEFAULT_IMAGE=void 0;THREE.Texture.DEFAULT_MAPPING=new THREE.UVMapping;
-THREE.Texture.prototype={constructor:THREE.Texture,get needsUpdate(){return this._needsUpdate},set needsUpdate(a){!0===a&&this.update();this._needsUpdate=a},clone:function(a){void 0===a&&(a=new THREE.Texture);a.image=this.image;a.mipmaps=this.mipmaps.slice(0);a.mapping=this.mapping;a.wrapS=this.wrapS;a.wrapT=this.wrapT;a.magFilter=this.magFilter;a.minFilter=this.minFilter;a.anisotropy=this.anisotropy;a.format=this.format;a.type=this.type;a.offset.copy(this.offset);a.repeat.copy(this.repeat);a.generateMipmaps=
-this.generateMipmaps;a.premultiplyAlpha=this.premultiplyAlpha;a.flipY=this.flipY;a.unpackAlignment=this.unpackAlignment;return a},update:function(){this.dispatchEvent({type:"update"})},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.Texture.prototype);THREE.TextureIdCount=0;THREE.CubeTexture=function(a,b,c,d,e,f,g,h,k){THREE.Texture.call(this,a,b,c,d,e,f,g,h,k);this.images=a};THREE.CubeTexture.prototype=Object.create(THREE.Texture.prototype);
-THREE.CubeTexture.clone=function(a){void 0===a&&(a=new THREE.CubeTexture);THREE.Texture.prototype.clone.call(this,a);a.images=this.images;return a};THREE.CompressedTexture=function(a,b,c,d,e,f,g,h,k,n,p){THREE.Texture.call(this,null,f,g,h,k,n,d,e,p);this.image={width:b,height:c};this.mipmaps=a;this.generateMipmaps=this.flipY=!1};THREE.CompressedTexture.prototype=Object.create(THREE.Texture.prototype);
-THREE.CompressedTexture.prototype.clone=function(){var a=new THREE.CompressedTexture;THREE.Texture.prototype.clone.call(this,a);return a};THREE.DataTexture=function(a,b,c,d,e,f,g,h,k,n,p){THREE.Texture.call(this,null,f,g,h,k,n,d,e,p);this.image={data:a,width:b,height:c}};THREE.DataTexture.prototype=Object.create(THREE.Texture.prototype);THREE.DataTexture.prototype.clone=function(){var a=new THREE.DataTexture;THREE.Texture.prototype.clone.call(this,a);return a};
-THREE.VideoTexture=function(a,b,c,d,e,f,g,h,k){THREE.Texture.call(this,a,b,c,d,e,f,g,h,k);this.generateMipmaps=!1;var n=this,p=function(){requestAnimationFrame(p);a.readyState===a.HAVE_ENOUGH_DATA&&(n.needsUpdate=!0)};p()};THREE.VideoTexture.prototype=Object.create(THREE.Texture.prototype);THREE.Group=function(){THREE.Object3D.call(this);this.type="Group"};THREE.Group.prototype=Object.create(THREE.Object3D.prototype);
-THREE.PointCloud=function(a,b){THREE.Object3D.call(this);this.type="PointCloud";this.geometry=void 0!==a?a:new THREE.Geometry;this.material=void 0!==b?b:new THREE.PointCloudMaterial({color:16777215*Math.random()});this.sortParticles=!1};THREE.PointCloud.prototype=Object.create(THREE.Object3D.prototype);
-THREE.PointCloud.prototype.raycast=function(){var a=new THREE.Matrix4,b=new THREE.Ray;return function(c,d){var e=this,f=e.geometry,g=c.params.PointCloud.threshold;a.getInverse(this.matrixWorld);b.copy(c.ray).applyMatrix4(a);if(null===f.boundingBox||!1!==b.isIntersectionBox(f.boundingBox)){var h=g/((this.scale.x+this.scale.y+this.scale.z)/3),k=new THREE.Vector3,g=function(a,f){var g=b.distanceToPoint(a);if(g<h){var k=b.closestPointToPoint(a);k.applyMatrix4(e.matrixWorld);var m=c.ray.origin.distanceTo(k);
-d.push({distance:m,distanceToRay:g,point:k.clone(),index:f,face:null,object:e})}};if(f instanceof THREE.BufferGeometry){var n=f.attributes,p=n.position.array;if(void 0!==n.index){var n=n.index.array,q=f.offsets;0===q.length&&(q=[{start:0,count:n.length,index:0}]);for(var m=0,r=q.length;m<r;++m)for(var t=q[m].start,s=q[m].index,f=t,t=t+q[m].count;f<t;f++){var u=s+n[f];k.fromArray(p,3*u);g(k,u)}}else for(n=p.length/3,f=0;f<n;f++)k.set(p[3*f],p[3*f+1],p[3*f+2]),g(k,f)}else for(k=this.geometry.vertices,
-f=0;f<k.length;f++)g(k[f],f)}}}();THREE.PointCloud.prototype.clone=function(a){void 0===a&&(a=new THREE.PointCloud(this.geometry,this.material));a.sortParticles=this.sortParticles;THREE.Object3D.prototype.clone.call(this,a);return a};THREE.ParticleSystem=function(a,b){console.warn("THREE.ParticleSystem has been renamed to THREE.PointCloud.");return new THREE.PointCloud(a,b)};
-THREE.Line=function(a,b,c){THREE.Object3D.call(this);this.type="Line";this.geometry=void 0!==a?a:new THREE.Geometry;this.material=void 0!==b?b:new THREE.LineBasicMaterial({color:16777215*Math.random()});this.mode=void 0!==c?c:THREE.LineStrip};THREE.LineStrip=0;THREE.LinePieces=1;THREE.Line.prototype=Object.create(THREE.Object3D.prototype);
-THREE.Line.prototype.raycast=function(){var a=new THREE.Matrix4,b=new THREE.Ray,c=new THREE.Sphere;return function(d,e){var f=d.linePrecision,f=f*f,g=this.geometry;null===g.boundingSphere&&g.computeBoundingSphere();c.copy(g.boundingSphere);c.applyMatrix4(this.matrixWorld);if(!1!==d.ray.isIntersectionSphere(c)&&(a.getInverse(this.matrixWorld),b.copy(d.ray).applyMatrix4(a),g instanceof THREE.Geometry))for(var g=g.vertices,h=g.length,k=new THREE.Vector3,n=new THREE.Vector3,p=this.mode===THREE.LineStrip?
-1:2,q=0;q<h-1;q+=p)if(!(b.distanceSqToSegment(g[q],g[q+1],n,k)>f)){var m=b.origin.distanceTo(n);m<d.near||m>d.far||e.push({distance:m,point:k.clone().applyMatrix4(this.matrixWorld),face:null,faceIndex:null,object:this})}}}();THREE.Line.prototype.clone=function(a){void 0===a&&(a=new THREE.Line(this.geometry,this.material,this.mode));THREE.Object3D.prototype.clone.call(this,a);return a};
-THREE.Mesh=function(a,b){THREE.Object3D.call(this);this.type="Mesh";this.geometry=void 0!==a?a:new THREE.Geometry;this.material=void 0!==b?b:new THREE.MeshBasicMaterial({color:16777215*Math.random()});this.updateMorphTargets()};THREE.Mesh.prototype=Object.create(THREE.Object3D.prototype);
-THREE.Mesh.prototype.updateMorphTargets=function(){if(void 0!==this.geometry.morphTargets&&0<this.geometry.morphTargets.length){this.morphTargetBase=-1;this.morphTargetForcedOrder=[];this.morphTargetInfluences=[];this.morphTargetDictionary={};for(var a=0,b=this.geometry.morphTargets.length;a<b;a++)this.morphTargetInfluences.push(0),this.morphTargetDictionary[this.geometry.morphTargets[a].name]=a}};
-THREE.Mesh.prototype.getMorphTargetIndexByName=function(a){if(void 0!==this.morphTargetDictionary[a])return this.morphTargetDictionary[a];console.log("THREE.Mesh.getMorphTargetIndexByName: morph target "+a+" does not exist. Returning 0.");return 0};
-THREE.Mesh.prototype.raycast=function(){var a=new THREE.Matrix4,b=new THREE.Ray,c=new THREE.Sphere,d=new THREE.Vector3,e=new THREE.Vector3,f=new THREE.Vector3;return function(g,h){var k=this.geometry;null===k.boundingSphere&&k.computeBoundingSphere();c.copy(k.boundingSphere);c.applyMatrix4(this.matrixWorld);if(!1!==g.ray.isIntersectionSphere(c)&&(a.getInverse(this.matrixWorld),b.copy(g.ray).applyMatrix4(a),null===k.boundingBox||!1!==b.isIntersectionBox(k.boundingBox)))if(k instanceof THREE.BufferGeometry){var n=
-this.material;if(void 0!==n){var p=k.attributes,q,m,r=g.precision;if(void 0!==p.index){var t=p.index.array,s=p.position.array,u=k.offsets;0===u.length&&(u=[{start:0,count:t.length,index:0}]);for(var v=0,y=u.length;v<y;++v)for(var p=u[v].start,G=u[v].index,k=p,w=p+u[v].count;k<w;k+=3){p=G+t[k];q=G+t[k+1];m=G+t[k+2];d.fromArray(s,3*p);e.fromArray(s,3*q);f.fromArray(s,3*m);var K=n.side===THREE.BackSide?b.intersectTriangle(f,e,d,!0):b.intersectTriangle(d,e,f,n.side!==THREE.DoubleSide);if(null!==K){K.applyMatrix4(this.matrixWorld);
-var x=g.ray.origin.distanceTo(K);x<r||x<g.near||x>g.far||h.push({distance:x,point:K,face:new THREE.Face3(p,q,m,THREE.Triangle.normal(d,e,f)),faceIndex:null,object:this})}}}else for(s=p.position.array,t=k=0,w=s.length;k<w;k+=3,t+=9)p=k,q=k+1,m=k+2,d.fromArray(s,t),e.fromArray(s,t+3),f.fromArray(s,t+6),K=n.side===THREE.BackSide?b.intersectTriangle(f,e,d,!0):b.intersectTriangle(d,e,f,n.side!==THREE.DoubleSide),null!==K&&(K.applyMatrix4(this.matrixWorld),x=g.ray.origin.distanceTo(K),x<r||x<g.near||x>
-g.far||h.push({distance:x,point:K,face:new THREE.Face3(p,q,m,THREE.Triangle.normal(d,e,f)),faceIndex:null,object:this}))}}else if(k instanceof THREE.Geometry)for(t=this.material instanceof THREE.MeshFaceMaterial,s=!0===t?this.material.materials:null,r=g.precision,u=k.vertices,v=0,y=k.faces.length;v<y;v++)if(G=k.faces[v],n=!0===t?s[G.materialIndex]:this.material,void 0!==n){p=u[G.a];q=u[G.b];m=u[G.c];if(!0===n.morphTargets){K=k.morphTargets;x=this.morphTargetInfluences;d.set(0,0,0);e.set(0,0,0);f.set(0,
-0,0);for(var w=0,D=K.length;w<D;w++){var E=x[w];if(0!==E){var A=K[w].vertices;d.x+=(A[G.a].x-p.x)*E;d.y+=(A[G.a].y-p.y)*E;d.z+=(A[G.a].z-p.z)*E;e.x+=(A[G.b].x-q.x)*E;e.y+=(A[G.b].y-q.y)*E;e.z+=(A[G.b].z-q.z)*E;f.x+=(A[G.c].x-m.x)*E;f.y+=(A[G.c].y-m.y)*E;f.z+=(A[G.c].z-m.z)*E}}d.add(p);e.add(q);f.add(m);p=d;q=e;m=f}K=n.side===THREE.BackSide?b.intersectTriangle(m,q,p,!0):b.intersectTriangle(p,q,m,n.side!==THREE.DoubleSide);null!==K&&(K.applyMatrix4(this.matrixWorld),x=g.ray.origin.distanceTo(K),x<r||
-x<g.near||x>g.far||h.push({distance:x,point:K,face:G,faceIndex:v,object:this}))}}}();THREE.Mesh.prototype.clone=function(a,b){void 0===a&&(a=new THREE.Mesh(this.geometry,this.material));THREE.Object3D.prototype.clone.call(this,a,b);return a};THREE.Bone=function(a){THREE.Object3D.call(this);this.skin=a};THREE.Bone.prototype=Object.create(THREE.Object3D.prototype);
-THREE.Skeleton=function(a,b,c){this.useVertexTexture=void 0!==c?c:!0;this.identityMatrix=new THREE.Matrix4;a=a||[];this.bones=a.slice(0);this.useVertexTexture?(this.boneTextureHeight=this.boneTextureWidth=a=256<this.bones.length?64:64<this.bones.length?32:16<this.bones.length?16:8,this.boneMatrices=new Float32Array(this.boneTextureWidth*this.boneTextureHeight*4),this.boneTexture=new THREE.DataTexture(this.boneMatrices,this.boneTextureWidth,this.boneTextureHeight,THREE.RGBAFormat,THREE.FloatType),
-this.boneTexture.minFilter=THREE.NearestFilter,this.boneTexture.magFilter=THREE.NearestFilter,this.boneTexture.generateMipmaps=!1,this.boneTexture.flipY=!1):this.boneMatrices=new Float32Array(16*this.bones.length);if(void 0===b)this.calculateInverses();else if(this.bones.length===b.length)this.boneInverses=b.slice(0);else for(console.warn("THREE.Skeleton bonInverses is the wrong length."),this.boneInverses=[],b=0,a=this.bones.length;b<a;b++)this.boneInverses.push(new THREE.Matrix4)};
-THREE.Skeleton.prototype.calculateInverses=function(){this.boneInverses=[];for(var a=0,b=this.bones.length;a<b;a++){var c=new THREE.Matrix4;this.bones[a]&&c.getInverse(this.bones[a].matrixWorld);this.boneInverses.push(c)}};
-THREE.Skeleton.prototype.pose=function(){for(var a,b=0,c=this.bones.length;b<c;b++)(a=this.bones[b])&&a.matrixWorld.getInverse(this.boneInverses[b]);b=0;for(c=this.bones.length;b<c;b++)if(a=this.bones[b])a.parent?(a.matrix.getInverse(a.parent.matrixWorld),a.matrix.multiply(a.matrixWorld)):a.matrix.copy(a.matrixWorld),a.matrix.decompose(a.position,a.quaternion,a.scale)};
-THREE.Skeleton.prototype.update=function(){var a=new THREE.Matrix4;return function(){for(var b=0,c=this.bones.length;b<c;b++)a.multiplyMatrices(this.bones[b]?this.bones[b].matrixWorld:this.identityMatrix,this.boneInverses[b]),a.flattenToArrayOffset(this.boneMatrices,16*b);this.useVertexTexture&&(this.boneTexture.needsUpdate=!0)}}();
-THREE.SkinnedMesh=function(a,b,c){THREE.Mesh.call(this,a,b);this.type="SkinnedMesh";this.bindMode="attached";this.bindMatrix=new THREE.Matrix4;this.bindMatrixInverse=new THREE.Matrix4;a=[];if(this.geometry&&void 0!==this.geometry.bones){for(var d,e,f,g,h=0,k=this.geometry.bones.length;h<k;++h)d=this.geometry.bones[h],e=d.pos,f=d.rotq,g=d.scl,b=new THREE.Bone(this),a.push(b),b.name=d.name,b.position.set(e[0],e[1],e[2]),b.quaternion.set(f[0],f[1],f[2],f[3]),void 0!==g?b.scale.set(g[0],g[1],g[2]):b.scale.set(1,
-1,1);h=0;for(k=this.geometry.bones.length;h<k;++h)d=this.geometry.bones[h],-1!==d.parent?a[d.parent].add(a[h]):this.add(a[h])}this.normalizeSkinWeights();this.updateMatrixWorld(!0);this.bind(new THREE.Skeleton(a,void 0,c))};THREE.SkinnedMesh.prototype=Object.create(THREE.Mesh.prototype);THREE.SkinnedMesh.prototype.bind=function(a,b){this.skeleton=a;void 0===b&&(this.updateMatrixWorld(!0),b=this.matrixWorld);this.bindMatrix.copy(b);this.bindMatrixInverse.getInverse(b)};
-THREE.SkinnedMesh.prototype.pose=function(){this.skeleton.pose()};THREE.SkinnedMesh.prototype.normalizeSkinWeights=function(){if(this.geometry instanceof THREE.Geometry)for(var a=0;a<this.geometry.skinIndices.length;a++){var b=this.geometry.skinWeights[a],c=1/b.lengthManhattan();Infinity!==c?b.multiplyScalar(c):b.set(1)}};
-THREE.SkinnedMesh.prototype.updateMatrixWorld=function(a){THREE.Mesh.prototype.updateMatrixWorld.call(this,!0);"attached"===this.bindMode?this.bindMatrixInverse.getInverse(this.matrixWorld):"detached"===this.bindMode?this.bindMatrixInverse.getInverse(this.bindMatrix):console.warn("THREE.SkinnedMesh unreckognized bindMode: "+this.bindMode)};
-THREE.SkinnedMesh.prototype.clone=function(a){void 0===a&&(a=new THREE.SkinnedMesh(this.geometry,this.material,this.useVertexTexture));THREE.Mesh.prototype.clone.call(this,a);return a};THREE.MorphAnimMesh=function(a,b){THREE.Mesh.call(this,a,b);this.type="MorphAnimMesh";this.duration=1E3;this.mirroredLoop=!1;this.currentKeyframe=this.lastKeyframe=this.time=0;this.direction=1;this.directionBackwards=!1;this.setFrameRange(0,this.geometry.morphTargets.length-1)};THREE.MorphAnimMesh.prototype=Object.create(THREE.Mesh.prototype);
-THREE.MorphAnimMesh.prototype.setFrameRange=function(a,b){this.startKeyframe=a;this.endKeyframe=b;this.length=this.endKeyframe-this.startKeyframe+1};THREE.MorphAnimMesh.prototype.setDirectionForward=function(){this.direction=1;this.directionBackwards=!1};THREE.MorphAnimMesh.prototype.setDirectionBackward=function(){this.direction=-1;this.directionBackwards=!0};
-THREE.MorphAnimMesh.prototype.parseAnimations=function(){var a=this.geometry;a.animations||(a.animations={});for(var b,c=a.animations,d=/([a-z]+)_?(\d+)/,e=0,f=a.morphTargets.length;e<f;e++){var g=a.morphTargets[e].name.match(d);if(g&&1<g.length){g=g[1];c[g]||(c[g]={start:Infinity,end:-Infinity});var h=c[g];e<h.start&&(h.start=e);e>h.end&&(h.end=e);b||(b=g)}}a.firstAnimation=b};
-THREE.MorphAnimMesh.prototype.setAnimationLabel=function(a,b,c){this.geometry.animations||(this.geometry.animations={});this.geometry.animations[a]={start:b,end:c}};THREE.MorphAnimMesh.prototype.playAnimation=function(a,b){var c=this.geometry.animations[a];c?(this.setFrameRange(c.start,c.end),this.duration=(c.end-c.start)/b*1E3,this.time=0):console.warn("animation["+a+"] undefined")};
-THREE.MorphAnimMesh.prototype.updateAnimation=function(a){var b=this.duration/this.length;this.time+=this.direction*a;if(this.mirroredLoop){if(this.time>this.duration||0>this.time)this.direction*=-1,this.time>this.duration&&(this.time=this.duration,this.directionBackwards=!0),0>this.time&&(this.time=0,this.directionBackwards=!1)}else this.time%=this.duration,0>this.time&&(this.time+=this.duration);a=this.startKeyframe+THREE.Math.clamp(Math.floor(this.time/b),0,this.length-1);a!==this.currentKeyframe&&
-(this.morphTargetInfluences[this.lastKeyframe]=0,this.morphTargetInfluences[this.currentKeyframe]=1,this.morphTargetInfluences[a]=0,this.lastKeyframe=this.currentKeyframe,this.currentKeyframe=a);b=this.time%b/b;this.directionBackwards&&(b=1-b);this.morphTargetInfluences[this.currentKeyframe]=b;this.morphTargetInfluences[this.lastKeyframe]=1-b};
-THREE.MorphAnimMesh.prototype.interpolateTargets=function(a,b,c){for(var d=this.morphTargetInfluences,e=0,f=d.length;e<f;e++)d[e]=0;-1<a&&(d[a]=1-c);-1<b&&(d[b]=c)};
-THREE.MorphAnimMesh.prototype.clone=function(a){void 0===a&&(a=new THREE.MorphAnimMesh(this.geometry,this.material));a.duration=this.duration;a.mirroredLoop=this.mirroredLoop;a.time=this.time;a.lastKeyframe=this.lastKeyframe;a.currentKeyframe=this.currentKeyframe;a.direction=this.direction;a.directionBackwards=this.directionBackwards;THREE.Mesh.prototype.clone.call(this,a);return a};THREE.LOD=function(){THREE.Object3D.call(this);this.objects=[]};THREE.LOD.prototype=Object.create(THREE.Object3D.prototype);
-THREE.LOD.prototype.addLevel=function(a,b){void 0===b&&(b=0);b=Math.abs(b);for(var c=0;c<this.objects.length&&!(b<this.objects[c].distance);c++);this.objects.splice(c,0,{distance:b,object:a});this.add(a)};THREE.LOD.prototype.getObjectForDistance=function(a){for(var b=1,c=this.objects.length;b<c&&!(a<this.objects[b].distance);b++);return this.objects[b-1].object};
-THREE.LOD.prototype.raycast=function(){var a=new THREE.Vector3;return function(b,c){a.setFromMatrixPosition(this.matrixWorld);var d=b.ray.origin.distanceTo(a);this.getObjectForDistance(d).raycast(b,c)}}();
-THREE.LOD.prototype.update=function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c){if(1<this.objects.length){a.setFromMatrixPosition(c.matrixWorld);b.setFromMatrixPosition(this.matrixWorld);c=a.distanceTo(b);this.objects[0].object.visible=!0;for(var d=1,e=this.objects.length;d<e;d++)if(c>=this.objects[d].distance)this.objects[d-1].object.visible=!1,this.objects[d].object.visible=!0;else break;for(;d<e;d++)this.objects[d].object.visible=!1}}}();
-THREE.LOD.prototype.clone=function(a){void 0===a&&(a=new THREE.LOD);THREE.Object3D.prototype.clone.call(this,a);for(var b=0,c=this.objects.length;b<c;b++){var d=this.objects[b].object.clone();d.visible=0===b;a.addLevel(d,this.objects[b].distance)}return a};
-THREE.Sprite=function(){var a=new Uint16Array([0,1,2,0,2,3]),b=new Float32Array([-.5,-.5,0,.5,-.5,0,.5,.5,0,-.5,.5,0]),c=new Float32Array([0,0,1,0,1,1,0,1]),d=new THREE.BufferGeometry;d.addAttribute("index",new THREE.BufferAttribute(a,1));d.addAttribute("position",new THREE.BufferAttribute(b,3));d.addAttribute("uv",new THREE.BufferAttribute(c,2));return function(a){THREE.Object3D.call(this);this.type="Sprite";this.geometry=d;this.material=void 0!==a?a:new THREE.SpriteMaterial}}();
-THREE.Sprite.prototype=Object.create(THREE.Object3D.prototype);THREE.Sprite.prototype.raycast=function(){var a=new THREE.Vector3;return function(b,c){a.setFromMatrixPosition(this.matrixWorld);var d=b.ray.distanceToPoint(a);d>this.scale.x||c.push({distance:d,point:this.position,face:null,object:this})}}();THREE.Sprite.prototype.clone=function(a){void 0===a&&(a=new THREE.Sprite(this.material));THREE.Object3D.prototype.clone.call(this,a);return a};THREE.Particle=THREE.Sprite;
-THREE.LensFlare=function(a,b,c,d,e){THREE.Object3D.call(this);this.lensFlares=[];this.positionScreen=new THREE.Vector3;this.customUpdateCallback=void 0;void 0!==a&&this.add(a,b,c,d,e)};THREE.LensFlare.prototype=Object.create(THREE.Object3D.prototype);
-THREE.LensFlare.prototype.add=function(a,b,c,d,e,f){void 0===b&&(b=-1);void 0===c&&(c=0);void 0===f&&(f=1);void 0===e&&(e=new THREE.Color(16777215));void 0===d&&(d=THREE.NormalBlending);c=Math.min(c,Math.max(0,c));this.lensFlares.push({texture:a,size:b,distance:c,x:0,y:0,z:0,scale:1,rotation:1,opacity:f,color:e,blending:d})};
-THREE.LensFlare.prototype.updateLensFlares=function(){var a,b=this.lensFlares.length,c,d=2*-this.positionScreen.x,e=2*-this.positionScreen.y;for(a=0;a<b;a++)c=this.lensFlares[a],c.x=this.positionScreen.x+d*c.distance,c.y=this.positionScreen.y+e*c.distance,c.wantedRotation=c.x*Math.PI*.25,c.rotation+=.25*(c.wantedRotation-c.rotation)};THREE.Scene=function(){THREE.Object3D.call(this);this.type="Scene";this.overrideMaterial=this.fog=null;this.autoUpdate=!0};THREE.Scene.prototype=Object.create(THREE.Object3D.prototype);
-THREE.Scene.prototype.clone=function(a){void 0===a&&(a=new THREE.Scene);THREE.Object3D.prototype.clone.call(this,a);null!==this.fog&&(a.fog=this.fog.clone());null!==this.overrideMaterial&&(a.overrideMaterial=this.overrideMaterial.clone());a.autoUpdate=this.autoUpdate;a.matrixAutoUpdate=this.matrixAutoUpdate;return a};THREE.Fog=function(a,b,c){this.name="";this.color=new THREE.Color(a);this.near=void 0!==b?b:1;this.far=void 0!==c?c:1E3};
-THREE.Fog.prototype.clone=function(){return new THREE.Fog(this.color.getHex(),this.near,this.far)};THREE.FogExp2=function(a,b){this.name="";this.color=new THREE.Color(a);this.density=void 0!==b?b:2.5E-4};THREE.FogExp2.prototype.clone=function(){return new THREE.FogExp2(this.color.getHex(),this.density)};THREE.ShaderChunk={};THREE.ShaderChunk.alphatest_fragment="#ifdef ALPHATEST\n\n\tif ( gl_FragColor.a < ALPHATEST ) discard;\n\n#endif\n";THREE.ShaderChunk.lights_lambert_vertex="vLightFront = vec3( 0.0 );\n\n#ifdef DOUBLE_SIDED\n\n\tvLightBack = vec3( 0.0 );\n\n#endif\n\ntransformedNormal = normalize( transformedNormal );\n\n#if MAX_DIR_LIGHTS > 0\n\nfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n\tvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\n\tvec3 dirVector = normalize( lDirection.xyz );\n\n\tfloat dotProduct = dot( transformedNormal, dirVector );\n\tvec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n\t#ifdef DOUBLE_SIDED\n\n\t\tvec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n\t\t#ifdef WRAP_AROUND\n\n\t\t\tvec3 directionalLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n\t\t#endif\n\n\t#endif\n\n\t#ifdef WRAP_AROUND\n\n\t\tvec3 directionalLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n\t\tdirectionalLightWeighting = mix( directionalLightWeighting, directionalLightWeightingHalf, wrapRGB );\n\n\t\t#ifdef DOUBLE_SIDED\n\n\t\t\tdirectionalLightWeightingBack = mix( directionalLightWeightingBack, directionalLightWeightingHalfBack, wrapRGB );\n\n\t\t#endif\n\n\t#endif\n\n\tvLightFront += directionalLightColor[ i ] * directionalLightWeighting;\n\n\t#ifdef DOUBLE_SIDED\n\n\t\tvLightBack += directionalLightColor[ i ] * directionalLightWeightingBack;\n\n\t#endif\n\n}\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n\tfor( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n\t\tvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n\t\tvec3 lVector = lPosition.xyz - mvPosition.xyz;\n\n\t\tfloat lDistance = 1.0;\n\t\tif ( pointLightDistance[ i ] > 0.0 )\n\t\t\tlDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\n\n\t\tlVector = normalize( lVector );\n\t\tfloat dotProduct = dot( transformedNormal, lVector );\n\n\t\tvec3 pointLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n\t\t#ifdef DOUBLE_SIDED\n\n\t\t\tvec3 pointLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n\t\t\t#ifdef WRAP_AROUND\n\n\t\t\t\tvec3 pointLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n\t\t\t#endif\n\n\t\t#endif\n\n\t\t#ifdef WRAP_AROUND\n\n\t\t\tvec3 pointLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n\t\t\tpointLightWeighting = mix( pointLightWeighting, pointLightWeightingHalf, wrapRGB );\n\n\t\t\t#ifdef DOUBLE_SIDED\n\n\t\t\t\tpointLightWeightingBack = mix( pointLightWeightingBack, pointLightWeightingHalfBack, wrapRGB );\n\n\t\t\t#endif\n\n\t\t#endif\n\n\t\tvLightFront += pointLightColor[ i ] * pointLightWeighting * lDistance;\n\n\t\t#ifdef DOUBLE_SIDED\n\n\t\t\tvLightBack += pointLightColor[ i ] * pointLightWeightingBack * lDistance;\n\n\t\t#endif\n\n\t}\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n\tfor( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n\t\tvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n\t\tvec3 lVector = lPosition.xyz - mvPosition.xyz;\n\n\t\tfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - worldPosition.xyz ) );\n\n\t\tif ( spotEffect > spotLightAngleCos[ i ] ) {\n\n\t\t\tspotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\n\t\t\tfloat lDistance = 1.0;\n\t\t\tif ( spotLightDistance[ i ] > 0.0 )\n\t\t\t\tlDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\n\n\t\t\tlVector = normalize( lVector );\n\n\t\t\tfloat dotProduct = dot( transformedNormal, lVector );\n\t\t\tvec3 spotLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n\t\t\t#ifdef DOUBLE_SIDED\n\n\t\t\t\tvec3 spotLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n\t\t\t\t#ifdef WRAP_AROUND\n\n\t\t\t\t\tvec3 spotLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n\t\t\t\t#endif\n\n\t\t\t#endif\n\n\t\t\t#ifdef WRAP_AROUND\n\n\t\t\t\tvec3 spotLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n\t\t\t\tspotLightWeighting = mix( spotLightWeighting, spotLightWeightingHalf, wrapRGB );\n\n\t\t\t\t#ifdef DOUBLE_SIDED\n\n\t\t\t\t\tspotLightWeightingBack = mix( spotLightWeightingBack, spotLightWeightingHalfBack, wrapRGB );\n\n\t\t\t\t#endif\n\n\t\t\t#endif\n\n\t\t\tvLightFront += spotLightColor[ i ] * spotLightWeighting * lDistance * spotEffect;\n\n\t\t\t#ifdef DOUBLE_SIDED\n\n\t\t\t\tvLightBack += spotLightColor[ i ] * spotLightWeightingBack * lDistance * spotEffect;\n\n\t\t\t#endif\n\n\t\t}\n\n\t}\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n\tfor( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n\t\tvec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\n\t\tvec3 lVector = normalize( lDirection.xyz );\n\n\t\tfloat dotProduct = dot( transformedNormal, lVector );\n\n\t\tfloat hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\t\tfloat hemiDiffuseWeightBack = -0.5 * dotProduct + 0.5;\n\n\t\tvLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n\t\t#ifdef DOUBLE_SIDED\n\n\t\t\tvLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n\n\t\t#endif\n\n\t}\n\n#endif\n\nvLightFront = vLightFront * diffuse + ambient * ambientLightColor + emissive;\n\n#ifdef DOUBLE_SIDED\n\n\tvLightBack = vLightBack * diffuse + ambient * ambientLightColor + emissive;\n\n#endif";
-THREE.ShaderChunk.map_particle_pars_fragment="#ifdef USE_MAP\n\n\tuniform sampler2D map;\n\n#endif";THREE.ShaderChunk.default_vertex="vec4 mvPosition;\n\n#ifdef USE_SKINNING\n\n\tmvPosition = modelViewMatrix * skinned;\n\n#endif\n\n#if !defined( USE_SKINNING ) && defined( USE_MORPHTARGETS )\n\n\tmvPosition = modelViewMatrix * vec4( morphed, 1.0 );\n\n#endif\n\n#if !defined( USE_SKINNING ) && ! defined( USE_MORPHTARGETS )\n\n\tmvPosition = modelViewMatrix * vec4( position, 1.0 );\n\n#endif\n\ngl_Position = projectionMatrix * mvPosition;";
-THREE.ShaderChunk.map_pars_fragment="#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n\tvarying vec2 vUv;\n\n#endif\n\n#ifdef USE_MAP\n\n\tuniform sampler2D map;\n\n#endif";THREE.ShaderChunk.skinnormal_vertex="#ifdef USE_SKINNING\n\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix  = bindMatrixInverse * skinMatrix * bindMatrix;\n\n\t#ifdef USE_MORPHNORMALS\n\n\tvec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );\n\n\t#else\n\n\tvec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );\n\n\t#endif\n\n#endif\n";
-THREE.ShaderChunk.logdepthbuf_pars_vertex="#ifdef USE_LOGDEPTHBUF\n\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\n\t\tvarying float vFragDepth;\n\n\t#endif\n\n\tuniform float logDepthBufFC;\n\n#endif";THREE.ShaderChunk.lightmap_pars_vertex="#ifdef USE_LIGHTMAP\n\n\tvarying vec2 vUv2;\n\n#endif";THREE.ShaderChunk.lights_phong_fragment="vec3 normal = normalize( vNormal );\nvec3 viewPosition = normalize( vViewPosition );\n\n#ifdef DOUBLE_SIDED\n\n\tnormal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\n#endif\n\n#ifdef USE_NORMALMAP\n\n\tnormal = perturbNormal2Arb( -vViewPosition, normal );\n\n#elif defined( USE_BUMPMAP )\n\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n\tvec3 pointDiffuse = vec3( 0.0 );\n\tvec3 pointSpecular = vec3( 0.0 );\n\n\tfor ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n\t\tvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n\t\tvec3 lVector = lPosition.xyz + vViewPosition.xyz;\n\n\t\tfloat lDistance = 1.0;\n\t\tif ( pointLightDistance[ i ] > 0.0 )\n\t\t\tlDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\n\n\t\tlVector = normalize( lVector );\n\n\t\t\t\t// diffuse\n\n\t\tfloat dotProduct = dot( normal, lVector );\n\n\t\t#ifdef WRAP_AROUND\n\n\t\t\tfloat pointDiffuseWeightFull = max( dotProduct, 0.0 );\n\t\t\tfloat pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n\t\t\tvec3 pointDiffuseWeight = mix( vec3( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );\n\n\t\t#else\n\n\t\t\tfloat pointDiffuseWeight = max( dotProduct, 0.0 );\n\n\t\t#endif\n\n\t\tpointDiffuse += diffuse * pointLightColor[ i ] * pointDiffuseWeight * lDistance;\n\n\t\t\t\t// specular\n\n\t\tvec3 pointHalfVector = normalize( lVector + viewPosition );\n\t\tfloat pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );\n\t\tfloat pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );\n\n\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n\t\tvec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, pointHalfVector ), 0.0 ), 5.0 );\n\t\tpointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance * specularNormalization;\n\n\t}\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n\tvec3 spotDiffuse = vec3( 0.0 );\n\tvec3 spotSpecular = vec3( 0.0 );\n\n\tfor ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n\t\tvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n\t\tvec3 lVector = lPosition.xyz + vViewPosition.xyz;\n\n\t\tfloat lDistance = 1.0;\n\t\tif ( spotLightDistance[ i ] > 0.0 )\n\t\t\tlDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\n\n\t\tlVector = normalize( lVector );\n\n\t\tfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );\n\n\t\tif ( spotEffect > spotLightAngleCos[ i ] ) {\n\n\t\t\tspotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\n\t\t\t\t\t// diffuse\n\n\t\t\tfloat dotProduct = dot( normal, lVector );\n\n\t\t\t#ifdef WRAP_AROUND\n\n\t\t\t\tfloat spotDiffuseWeightFull = max( dotProduct, 0.0 );\n\t\t\t\tfloat spotDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n\t\t\t\tvec3 spotDiffuseWeight = mix( vec3( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );\n\n\t\t\t#else\n\n\t\t\t\tfloat spotDiffuseWeight = max( dotProduct, 0.0 );\n\n\t\t\t#endif\n\n\t\t\tspotDiffuse += diffuse * spotLightColor[ i ] * spotDiffuseWeight * lDistance * spotEffect;\n\n\t\t\t\t\t// specular\n\n\t\t\tvec3 spotHalfVector = normalize( lVector + viewPosition );\n\t\t\tfloat spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );\n\t\t\tfloat spotSpecularWeight = specularStrength * max( pow( spotDotNormalHalf, shininess ), 0.0 );\n\n\t\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n\t\t\tvec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, spotHalfVector ), 0.0 ), 5.0 );\n\t\t\tspotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * specularNormalization * spotEffect;\n\n\t\t}\n\n\t}\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n\tvec3 dirDiffuse = vec3( 0.0 );\n\tvec3 dirSpecular = vec3( 0.0 );\n\n\tfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n\t\tvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\n\t\tvec3 dirVector = normalize( lDirection.xyz );\n\n\t\t\t\t// diffuse\n\n\t\tfloat dotProduct = dot( normal, dirVector );\n\n\t\t#ifdef WRAP_AROUND\n\n\t\t\tfloat dirDiffuseWeightFull = max( dotProduct, 0.0 );\n\t\t\tfloat dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n\t\t\tvec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );\n\n\t\t#else\n\n\t\t\tfloat dirDiffuseWeight = max( dotProduct, 0.0 );\n\n\t\t#endif\n\n\t\tdirDiffuse += diffuse * directionalLightColor[ i ] * dirDiffuseWeight;\n\n\t\t// specular\n\n\t\tvec3 dirHalfVector = normalize( dirVector + viewPosition );\n\t\tfloat dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );\n\t\tfloat dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );\n\n\t\t/*\n\t\t// fresnel term from skin shader\n\t\tconst float F0 = 0.128;\n\n\t\tfloat base = 1.0 - dot( viewPosition, dirHalfVector );\n\t\tfloat exponential = pow( base, 5.0 );\n\n\t\tfloat fresnel = exponential + F0 * ( 1.0 - exponential );\n\t\t*/\n\n\t\t/*\n\t\t// fresnel term from fresnel shader\n\t\tconst float mFresnelBias = 0.08;\n\t\tconst float mFresnelScale = 0.3;\n\t\tconst float mFresnelPower = 5.0;\n\n\t\tfloat fresnel = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( -viewPosition ), normal ), mFresnelPower );\n\t\t*/\n\n\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n\t\t// \t\tdirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization * fresnel;\n\n\t\tvec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( dirVector, dirHalfVector ), 0.0 ), 5.0 );\n\t\tdirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;\n\n\n\t}\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n\tvec3 hemiDiffuse = vec3( 0.0 );\n\tvec3 hemiSpecular = vec3( 0.0 );\n\n\tfor( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n\t\tvec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\n\t\tvec3 lVector = normalize( lDirection.xyz );\n\n\t\t// diffuse\n\n\t\tfloat dotProduct = dot( normal, lVector );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n\t\tvec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n\t\themiDiffuse += diffuse * hemiColor;\n\n\t\t// specular (sky light)\n\n\t\tvec3 hemiHalfVectorSky = normalize( lVector + viewPosition );\n\t\tfloat hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;\n\t\tfloat hemiSpecularWeightSky = specularStrength * max( pow( max( hemiDotNormalHalfSky, 0.0 ), shininess ), 0.0 );\n\n\t\t// specular (ground light)\n\n\t\tvec3 lVectorGround = -lVector;\n\n\t\tvec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );\n\t\tfloat hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;\n\t\tfloat hemiSpecularWeightGround = specularStrength * max( pow( max( hemiDotNormalHalfGround, 0.0 ), shininess ), 0.0 );\n\n\t\tfloat dotProductGround = dot( normal, lVectorGround );\n\n\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n\t\tvec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, hemiHalfVectorSky ), 0.0 ), 5.0 );\n\t\tvec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 0.0 ), 5.0 );\n\t\themiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );\n\n\t}\n\n#endif\n\nvec3 totalDiffuse = vec3( 0.0 );\nvec3 totalSpecular = vec3( 0.0 );\n\n#if MAX_DIR_LIGHTS > 0\n\n\ttotalDiffuse += dirDiffuse;\n\ttotalSpecular += dirSpecular;\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n\ttotalDiffuse += hemiDiffuse;\n\ttotalSpecular += hemiSpecular;\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n\ttotalDiffuse += pointDiffuse;\n\ttotalSpecular += pointSpecular;\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n\ttotalDiffuse += spotDiffuse;\n\ttotalSpecular += spotSpecular;\n\n#endif\n\n#ifdef METAL\n\n\tgl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient + totalSpecular );\n\n#else\n\n\tgl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;\n\n#endif";
-THREE.ShaderChunk.fog_pars_fragment="#ifdef USE_FOG\n\n\tuniform vec3 fogColor;\n\n\t#ifdef FOG_EXP2\n\n\t\tuniform float fogDensity;\n\n\t#else\n\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n\n#endif";THREE.ShaderChunk.morphnormal_vertex="#ifdef USE_MORPHNORMALS\n\n\tvec3 morphedNormal = vec3( 0.0 );\n\n\tmorphedNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n\tmorphedNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n\tmorphedNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n\tmorphedNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n\n\tmorphedNormal += normal;\n\n#endif";
-THREE.ShaderChunk.envmap_pars_fragment="#ifdef USE_ENVMAP\n\n\tuniform float reflectivity;\n\tuniform samplerCube envMap;\n\tuniform float flipEnvMap;\n\tuniform int combine;\n\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n\t\tuniform bool useRefract;\n\t\tuniform float refractionRatio;\n\n\t#else\n\n\t\tvarying vec3 vReflect;\n\n\t#endif\n\n#endif";THREE.ShaderChunk.logdepthbuf_fragment="#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\n\tgl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n\n#endif";
-THREE.ShaderChunk.normalmap_pars_fragment="#ifdef USE_NORMALMAP\n\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n\n\t\t\t// Per-Pixel Tangent Space Normal Mapping\n\t\t\t// http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html\n\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\n\t\tvec3 q0 = dFdx( eye_pos.xyz );\n\t\tvec3 q1 = dFdy( eye_pos.xyz );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\n\t\tvec3 S = normalize( q0 * st1.t - q1 * st0.t );\n\t\tvec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n\t\tvec3 N = normalize( surf_norm );\n\n\t\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\tmapN.xy = normalScale * mapN.xy;\n\t\tmat3 tsn = mat3( S, T, N );\n\t\treturn normalize( tsn * mapN );\n\n\t}\n\n#endif\n";
-THREE.ShaderChunk.lights_phong_pars_vertex="#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n\tvarying vec3 vWorldPosition;\n\n#endif\n";THREE.ShaderChunk.lightmap_pars_fragment="#ifdef USE_LIGHTMAP\n\n\tvarying vec2 vUv2;\n\tuniform sampler2D lightMap;\n\n#endif";THREE.ShaderChunk.shadowmap_vertex="#ifdef USE_SHADOWMAP\n\n\tfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n\t\tvShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n\n\t}\n\n#endif";
-THREE.ShaderChunk.lights_phong_vertex="#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n\tvWorldPosition = worldPosition.xyz;\n\n#endif";THREE.ShaderChunk.map_fragment="#ifdef USE_MAP\n\n\tvec4 texelColor = texture2D( map, vUv );\n\n\t#ifdef GAMMA_INPUT\n\n\t\ttexelColor.xyz *= texelColor.xyz;\n\n\t#endif\n\n\tgl_FragColor = gl_FragColor * texelColor;\n\n#endif";THREE.ShaderChunk.lightmap_vertex="#ifdef USE_LIGHTMAP\n\n\tvUv2 = uv2;\n\n#endif";
-THREE.ShaderChunk.map_particle_fragment="#ifdef USE_MAP\n\n\tgl_FragColor = gl_FragColor * texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) );\n\n#endif";THREE.ShaderChunk.color_pars_fragment="#ifdef USE_COLOR\n\n\tvarying vec3 vColor;\n\n#endif\n";THREE.ShaderChunk.color_vertex="#ifdef USE_COLOR\n\n\t#ifdef GAMMA_INPUT\n\n\t\tvColor = color * color;\n\n\t#else\n\n\t\tvColor = color;\n\n\t#endif\n\n#endif";THREE.ShaderChunk.skinning_vertex="#ifdef USE_SKINNING\n\n\t#ifdef USE_MORPHTARGETS\n\n\tvec4 skinVertex = bindMatrix * vec4( morphed, 1.0 );\n\n\t#else\n\n\tvec4 skinVertex = bindMatrix * vec4( position, 1.0 );\n\n\t#endif\n\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\tskinned  = bindMatrixInverse * skinned;\n\n#endif\n";
-THREE.ShaderChunk.envmap_pars_vertex="#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n\tvarying vec3 vReflect;\n\n\tuniform float refractionRatio;\n\tuniform bool useRefract;\n\n#endif\n";THREE.ShaderChunk.linear_to_gamma_fragment="#ifdef GAMMA_OUTPUT\n\n\tgl_FragColor.xyz = sqrt( gl_FragColor.xyz );\n\n#endif";THREE.ShaderChunk.color_pars_vertex="#ifdef USE_COLOR\n\n\tvarying vec3 vColor;\n\n#endif";
-THREE.ShaderChunk.lights_lambert_pars_vertex="uniform vec3 ambient;\nuniform vec3 diffuse;\nuniform vec3 emissive;\n\nuniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n\tuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n\tuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n\tuniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n\tuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\tuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n\tuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n\tuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#ifdef WRAP_AROUND\n\n\tuniform vec3 wrapRGB;\n\n#endif\n";
-THREE.ShaderChunk.map_pars_vertex="#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n\tvarying vec2 vUv;\n\tuniform vec4 offsetRepeat;\n\n#endif\n";THREE.ShaderChunk.envmap_fragment="#ifdef USE_ENVMAP\n\n\tvec3 reflectVec;\n\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n\t\tvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\n\t\t// http://en.wikibooks.org/wiki/GLSL_Programming/Applying_Matrix_Transformations\n\t\t// Transforming Normal Vectors with the Inverse Transformation\n\n\t\tvec3 worldNormal = normalize( vec3( vec4( normal, 0.0 ) * viewMatrix ) );\n\n\t\tif ( useRefract ) {\n\n\t\t\treflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\n\t\t} else { \n\n\t\t\treflectVec = reflect( cameraToVertex, worldNormal );\n\n\t\t}\n\n\t#else\n\n\t\treflectVec = vReflect;\n\n\t#endif\n\n\t#ifdef DOUBLE_SIDED\n\n\t\tfloat flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\t\tvec4 cubeColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n\t#else\n\n\t\tvec4 cubeColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n\t#endif\n\n\t#ifdef GAMMA_INPUT\n\n\t\tcubeColor.xyz *= cubeColor.xyz;\n\n\t#endif\n\n\tif ( combine == 1 ) {\n\n\t\tgl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularStrength * reflectivity );\n\n\t} else if ( combine == 2 ) {\n\n\t\tgl_FragColor.xyz += cubeColor.xyz * specularStrength * reflectivity;\n\n\t} else {\n\n\t\tgl_FragColor.xyz = mix( gl_FragColor.xyz, gl_FragColor.xyz * cubeColor.xyz, specularStrength * reflectivity );\n\n\t}\n\n#endif";
-THREE.ShaderChunk.specularmap_pars_fragment="#ifdef USE_SPECULARMAP\n\n\tuniform sampler2D specularMap;\n\n#endif";THREE.ShaderChunk.logdepthbuf_vertex="#ifdef USE_LOGDEPTHBUF\n\n\tgl_Position.z = log2(max(1e-6, gl_Position.w + 1.0)) * logDepthBufFC;\n\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\n#else\n\n\t\tgl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\n\t#endif\n\n#endif";THREE.ShaderChunk.morphtarget_pars_vertex="#ifdef USE_MORPHTARGETS\n\n\t#ifndef USE_MORPHNORMALS\n\n\tuniform float morphTargetInfluences[ 8 ];\n\n\t#else\n\n\tuniform float morphTargetInfluences[ 4 ];\n\n\t#endif\n\n#endif";
-THREE.ShaderChunk.specularmap_fragment="float specularStrength;\n\n#ifdef USE_SPECULARMAP\n\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n\n#else\n\n\tspecularStrength = 1.0;\n\n#endif";THREE.ShaderChunk.fog_fragment="#ifdef USE_FOG\n\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\n\t\tfloat depth = gl_FragDepthEXT / gl_FragCoord.w;\n\n\t#else\n\n\t\tfloat depth = gl_FragCoord.z / gl_FragCoord.w;\n\n\t#endif\n\n\t#ifdef FOG_EXP2\n\n\t\tconst float LOG2 = 1.442695;\n\t\tfloat fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\n\t\tfogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n\n\t#else\n\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, depth );\n\n\t#endif\n\t\n\tgl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n\n#endif";
-THREE.ShaderChunk.bumpmap_pars_fragment="#ifdef USE_BUMPMAP\n\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\n\t\t\t// Derivative maps - bump mapping unparametrized surfaces by Morten Mikkelsen\n\t\t\t//\thttp://mmikkelsen3d.blogspot.sk/2011/07/derivative-maps.html\n\n\t\t\t// Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2)\n\n\tvec2 dHdxy_fwd() {\n\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\n\t\treturn vec2( dBx, dBy );\n\n\t}\n\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\n\t\tvec3 vSigmaX = dFdx( surf_pos );\n\t\tvec3 vSigmaY = dFdy( surf_pos );\n\t\tvec3 vN = surf_norm;\t\t// normalized\n\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\n\t\tfloat fDet = dot( vSigmaX, R1 );\n\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\n\t}\n\n#endif";
-THREE.ShaderChunk.defaultnormal_vertex="vec3 objectNormal;\n\n#ifdef USE_SKINNING\n\n\tobjectNormal = skinnedNormal.xyz;\n\n#endif\n\n#if !defined( USE_SKINNING ) && defined( USE_MORPHNORMALS )\n\n\tobjectNormal = morphedNormal;\n\n#endif\n\n#if !defined( USE_SKINNING ) && ! defined( USE_MORPHNORMALS )\n\n\tobjectNormal = normal;\n\n#endif\n\n#ifdef FLIP_SIDED\n\n\tobjectNormal = -objectNormal;\n\n#endif\n\nvec3 transformedNormal = normalMatrix * objectNormal;";
-THREE.ShaderChunk.lights_phong_pars_fragment="uniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n\tuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n\tuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n\tuniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n\tuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\n\tuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n\tuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n\tuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n\n\tuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n\tvarying vec3 vWorldPosition;\n\n#endif\n\n#ifdef WRAP_AROUND\n\n\tuniform vec3 wrapRGB;\n\n#endif\n\nvarying vec3 vViewPosition;\nvarying vec3 vNormal;";
-THREE.ShaderChunk.skinbase_vertex="#ifdef USE_SKINNING\n\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n\n#endif";THREE.ShaderChunk.map_vertex="#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n\tvUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\n#endif";
-THREE.ShaderChunk.lightmap_fragment="#ifdef USE_LIGHTMAP\n\n\tgl_FragColor = gl_FragColor * texture2D( lightMap, vUv2 );\n\n#endif";THREE.ShaderChunk.shadowmap_pars_vertex="#ifdef USE_SHADOWMAP\n\n\tvarying vec4 vShadowCoord[ MAX_SHADOWS ];\n\tuniform mat4 shadowMatrix[ MAX_SHADOWS ];\n\n#endif";THREE.ShaderChunk.color_fragment="#ifdef USE_COLOR\n\n\tgl_FragColor = gl_FragColor * vec4( vColor, 1.0 );\n\n#endif";THREE.ShaderChunk.morphtarget_vertex="#ifdef USE_MORPHTARGETS\n\n\tvec3 morphed = vec3( 0.0 );\n\tmorphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n\tmorphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n\tmorphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n\tmorphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\n\t#ifndef USE_MORPHNORMALS\n\n\tmorphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n\tmorphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n\tmorphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n\tmorphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\n\t#endif\n\n\tmorphed += position;\n\n#endif";
-THREE.ShaderChunk.envmap_vertex="#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n\tvec3 worldNormal = mat3( modelMatrix[ 0 ].xyz, modelMatrix[ 1 ].xyz, modelMatrix[ 2 ].xyz ) * objectNormal;\n\tworldNormal = normalize( worldNormal );\n\n\tvec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\n\tif ( useRefract ) {\n\n\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\n\t} else {\n\n\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\n\t}\n\n#endif";
-THREE.ShaderChunk.shadowmap_fragment="#ifdef USE_SHADOWMAP\n\n\t#ifdef SHADOWMAP_DEBUG\n\n\t\tvec3 frustumColors[3];\n\t\tfrustumColors[0] = vec3( 1.0, 0.5, 0.0 );\n\t\tfrustumColors[1] = vec3( 0.0, 1.0, 0.8 );\n\t\tfrustumColors[2] = vec3( 0.0, 0.5, 1.0 );\n\n\t#endif\n\n\t#ifdef SHADOWMAP_CASCADE\n\n\t\tint inFrustumCount = 0;\n\n\t#endif\n\n\tfloat fDepth;\n\tvec3 shadowColor = vec3( 1.0 );\n\n\tfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n\t\tvec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\n\n\t\t\t\t// if ( something && something ) breaks ATI OpenGL shader compiler\n\t\t\t\t// if ( all( something, something ) ) using this instead\n\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\n\t\t\t\t// don't shadow pixels outside of light frustum\n\t\t\t\t// use just first frustum (for cascades)\n\t\t\t\t// don't shadow pixels behind far plane of light frustum\n\n\t\t#ifdef SHADOWMAP_CASCADE\n\n\t\t\tinFrustumCount += int( inFrustum );\n\t\t\tbvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );\n\n\t\t#else\n\n\t\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\n\t\t#endif\n\n\t\tbool frustumTest = all( frustumTestVec );\n\n\t\tif ( frustumTest ) {\n\n\t\t\tshadowCoord.z += shadowBias[ i ];\n\n\t\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\n\t\t\t\t\t\t// Percentage-close filtering\n\t\t\t\t\t\t// (9 pixel kernel)\n\t\t\t\t\t\t// http://fabiensanglard.net/shadowmappingPCF/\n\n\t\t\t\tfloat shadow = 0.0;\n\n\t\t/*\n\t\t\t\t\t\t// nested loops breaks shader compiler / validator on some ATI cards when using OpenGL\n\t\t\t\t\t\t// must enroll loop manually\n\n\t\t\t\tfor ( float y = -1.25; y <= 1.25; y += 1.25 )\n\t\t\t\t\tfor ( float x = -1.25; x <= 1.25; x += 1.25 ) {\n\n\t\t\t\t\t\tvec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );\n\n\t\t\t\t\t\t\t\t// doesn't seem to produce any noticeable visual difference compared to simple texture2D lookup\n\t\t\t\t\t\t\t\t//vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) );\n\n\t\t\t\t\t\tfloat fDepth = unpackDepth( rgbaDepth );\n\n\t\t\t\t\t\tif ( fDepth < shadowCoord.z )\n\t\t\t\t\t\t\tshadow += 1.0;\n\n\t\t\t\t}\n\n\t\t\t\tshadow /= 9.0;\n\n\t\t*/\n\n\t\t\t\tconst float shadowDelta = 1.0 / 9.0;\n\n\t\t\t\tfloat xPixelOffset = 1.0 / shadowMapSize[ i ].x;\n\t\t\t\tfloat yPixelOffset = 1.0 / shadowMapSize[ i ].y;\n\n\t\t\t\tfloat dx0 = -1.25 * xPixelOffset;\n\t\t\t\tfloat dy0 = -1.25 * yPixelOffset;\n\t\t\t\tfloat dx1 = 1.25 * xPixelOffset;\n\t\t\t\tfloat dy1 = 1.25 * yPixelOffset;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tshadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n\n\t\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n\t\t\t\t\t\t// Percentage-close filtering\n\t\t\t\t\t\t// (9 pixel kernel)\n\t\t\t\t\t\t// http://fabiensanglard.net/shadowmappingPCF/\n\n\t\t\t\tfloat shadow = 0.0;\n\n\t\t\t\tfloat xPixelOffset = 1.0 / shadowMapSize[ i ].x;\n\t\t\t\tfloat yPixelOffset = 1.0 / shadowMapSize[ i ].y;\n\n\t\t\t\tfloat dx0 = -1.0 * xPixelOffset;\n\t\t\t\tfloat dy0 = -1.0 * yPixelOffset;\n\t\t\t\tfloat dx1 = 1.0 * xPixelOffset;\n\t\t\t\tfloat dy1 = 1.0 * yPixelOffset;\n\n\t\t\t\tmat3 shadowKernel;\n\t\t\t\tmat3 depthKernel;\n\n\t\t\t\tdepthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n\t\t\t\tdepthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n\t\t\t\tdepthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n\t\t\t\tdepthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n\t\t\t\tdepthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n\t\t\t\tdepthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n\t\t\t\tdepthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n\t\t\t\tdepthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n\t\t\t\tdepthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\n\t\t\t\tvec3 shadowZ = vec3( shadowCoord.z );\n\t\t\t\tshadowKernel[0] = vec3(lessThan(depthKernel[0], shadowZ ));\n\t\t\t\tshadowKernel[0] *= vec3(0.25);\n\n\t\t\t\tshadowKernel[1] = vec3(lessThan(depthKernel[1], shadowZ ));\n\t\t\t\tshadowKernel[1] *= vec3(0.25);\n\n\t\t\t\tshadowKernel[2] = vec3(lessThan(depthKernel[2], shadowZ ));\n\t\t\t\tshadowKernel[2] *= vec3(0.25);\n\n\t\t\t\tvec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );\n\n\t\t\t\tshadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );\n\t\t\t\tshadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );\n\n\t\t\t\tvec4 shadowValues;\n\t\t\t\tshadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );\n\t\t\t\tshadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );\n\t\t\t\tshadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );\n\t\t\t\tshadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );\n\n\t\t\t\tshadow = dot( shadowValues, vec4( 1.0 ) );\n\n\t\t\t\tshadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n\n\t\t\t#else\n\n\t\t\t\tvec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\n\t\t\t\tfloat fDepth = unpackDepth( rgbaDepth );\n\n\t\t\t\tif ( fDepth < shadowCoord.z )\n\n\t\t// spot with multiple shadows is darker\n\n\t\t\t\t\tshadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );\n\n\t\t// spot with multiple shadows has the same color as single shadow spot\n\n\t\t// \t\t\t\t\tshadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) );\n\n\t\t\t#endif\n\n\t\t}\n\n\n\t\t#ifdef SHADOWMAP_DEBUG\n\n\t\t\t#ifdef SHADOWMAP_CASCADE\n\n\t\t\t\tif ( inFrustum && inFrustumCount == 1 ) gl_FragColor.xyz *= frustumColors[ i ];\n\n\t\t\t#else\n\n\t\t\t\tif ( inFrustum ) gl_FragColor.xyz *= frustumColors[ i ];\n\n\t\t\t#endif\n\n\t\t#endif\n\n\t}\n\n\t#ifdef GAMMA_OUTPUT\n\n\t\tshadowColor *= shadowColor;\n\n\t#endif\n\n\tgl_FragColor.xyz = gl_FragColor.xyz * shadowColor;\n\n#endif\n";
-THREE.ShaderChunk.worldpos_vertex="#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\n\t#ifdef USE_SKINNING\n\n\t\tvec4 worldPosition = modelMatrix * skinned;\n\n\t#endif\n\n\t#if defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )\n\n\t\tvec4 worldPosition = modelMatrix * vec4( morphed, 1.0 );\n\n\t#endif\n\n\t#if ! defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )\n\n\t\tvec4 worldPosition = modelMatrix * vec4( position, 1.0 );\n\n\t#endif\n\n#endif";
-THREE.ShaderChunk.shadowmap_pars_fragment="#ifdef USE_SHADOWMAP\n\n\tuniform sampler2D shadowMap[ MAX_SHADOWS ];\n\tuniform vec2 shadowMapSize[ MAX_SHADOWS ];\n\n\tuniform float shadowDarkness[ MAX_SHADOWS ];\n\tuniform float shadowBias[ MAX_SHADOWS ];\n\n\tvarying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n\tfloat unpackDepth( const in vec4 rgba_depth ) {\n\n\t\tconst vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n\t\tfloat depth = dot( rgba_depth, bit_shift );\n\t\treturn depth;\n\n\t}\n\n#endif";
-THREE.ShaderChunk.skinning_pars_vertex="#ifdef USE_SKINNING\n\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\n\t#ifdef BONE_TEXTURE\n\n\t\tuniform sampler2D boneTexture;\n\t\tuniform int boneTextureWidth;\n\t\tuniform int boneTextureHeight;\n\n\t\tmat4 getBoneMatrix( const in float i ) {\n\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureWidth ) );\n\t\t\tfloat y = floor( j / float( boneTextureWidth ) );\n\n\t\t\tfloat dx = 1.0 / float( boneTextureWidth );\n\t\t\tfloat dy = 1.0 / float( boneTextureHeight );\n\n\t\t\ty = dy * ( y + 0.5 );\n\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\n\t\t\treturn bone;\n\n\t\t}\n\n\t#else\n\n\t\tuniform mat4 boneGlobalMatrices[ MAX_BONES ];\n\n\t\tmat4 getBoneMatrix( const in float i ) {\n\n\t\t\tmat4 bone = boneGlobalMatrices[ int(i) ];\n\t\t\treturn bone;\n\n\t\t}\n\n\t#endif\n\n#endif\n";
-THREE.ShaderChunk.logdepthbuf_pars_fragment="#ifdef USE_LOGDEPTHBUF\n\n\tuniform float logDepthBufFC;\n\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\n\t\t#extension GL_EXT_frag_depth : enable\n\t\tvarying float vFragDepth;\n\n\t#endif\n\n#endif";THREE.ShaderChunk.alphamap_fragment="#ifdef USE_ALPHAMAP\n\n\tgl_FragColor.a *= texture2D( alphaMap, vUv ).g;\n\n#endif\n";THREE.ShaderChunk.alphamap_pars_fragment="#ifdef USE_ALPHAMAP\n\n\tuniform sampler2D alphaMap;\n\n#endif\n";
-THREE.UniformsUtils={merge:function(a){for(var b={},c=0;c<a.length;c++){var d=this.clone(a[c]),e;for(e in d)b[e]=d[e]}return b},clone:function(a){var b={},c;for(c in a){b[c]={};for(var d in a[c]){var e=a[c][d];b[c][d]=e instanceof THREE.Color||e instanceof THREE.Vector2||e instanceof THREE.Vector3||e instanceof THREE.Vector4||e instanceof THREE.Matrix4||e instanceof THREE.Texture?e.clone():e instanceof Array?e.slice():e}}return b}};
-THREE.UniformsLib={common:{diffuse:{type:"c",value:new THREE.Color(15658734)},opacity:{type:"f",value:1},map:{type:"t",value:null},offsetRepeat:{type:"v4",value:new THREE.Vector4(0,0,1,1)},lightMap:{type:"t",value:null},specularMap:{type:"t",value:null},alphaMap:{type:"t",value:null},envMap:{type:"t",value:null},flipEnvMap:{type:"f",value:-1},useRefract:{type:"i",value:0},reflectivity:{type:"f",value:1},refractionRatio:{type:"f",value:.98},combine:{type:"i",value:0},morphTargetInfluences:{type:"f",
-value:0}},bump:{bumpMap:{type:"t",value:null},bumpScale:{type:"f",value:1}},normalmap:{normalMap:{type:"t",value:null},normalScale:{type:"v2",value:new THREE.Vector2(1,1)}},fog:{fogDensity:{type:"f",value:2.5E-4},fogNear:{type:"f",value:1},fogFar:{type:"f",value:2E3},fogColor:{type:"c",value:new THREE.Color(16777215)}},lights:{ambientLightColor:{type:"fv",value:[]},directionalLightDirection:{type:"fv",value:[]},directionalLightColor:{type:"fv",value:[]},hemisphereLightDirection:{type:"fv",value:[]},
-hemisphereLightSkyColor:{type:"fv",value:[]},hemisphereLightGroundColor:{type:"fv",value:[]},pointLightColor:{type:"fv",value:[]},pointLightPosition:{type:"fv",value:[]},pointLightDistance:{type:"fv1",value:[]},spotLightColor:{type:"fv",value:[]},spotLightPosition:{type:"fv",value:[]},spotLightDirection:{type:"fv",value:[]},spotLightDistance:{type:"fv1",value:[]},spotLightAngleCos:{type:"fv1",value:[]},spotLightExponent:{type:"fv1",value:[]}},particle:{psColor:{type:"c",value:new THREE.Color(15658734)},
-opacity:{type:"f",value:1},size:{type:"f",value:1},scale:{type:"f",value:1},map:{type:"t",value:null},fogDensity:{type:"f",value:2.5E-4},fogNear:{type:"f",value:1},fogFar:{type:"f",value:2E3},fogColor:{type:"c",value:new THREE.Color(16777215)}},shadowmap:{shadowMap:{type:"tv",value:[]},shadowMapSize:{type:"v2v",value:[]},shadowBias:{type:"fv1",value:[]},shadowDarkness:{type:"fv1",value:[]},shadowMatrix:{type:"m4v",value:[]}}};
-THREE.ShaderLib={basic:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.common,THREE.UniformsLib.fog,THREE.UniformsLib.shadowmap]),vertexShader:[THREE.ShaderChunk.map_pars_vertex,THREE.ShaderChunk.lightmap_pars_vertex,THREE.ShaderChunk.envmap_pars_vertex,THREE.ShaderChunk.color_pars_vertex,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.shadowmap_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.map_vertex,
-THREE.ShaderChunk.lightmap_vertex,THREE.ShaderChunk.color_vertex,THREE.ShaderChunk.skinbase_vertex,"\t#ifdef USE_ENVMAP",THREE.ShaderChunk.morphnormal_vertex,THREE.ShaderChunk.skinnormal_vertex,THREE.ShaderChunk.defaultnormal_vertex,"\t#endif",THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.logdepthbuf_vertex,THREE.ShaderChunk.worldpos_vertex,THREE.ShaderChunk.envmap_vertex,THREE.ShaderChunk.shadowmap_vertex,"}"].join("\n"),
-fragmentShader:["uniform vec3 diffuse;\nuniform float opacity;",THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.map_pars_fragment,THREE.ShaderChunk.alphamap_pars_fragment,THREE.ShaderChunk.lightmap_pars_fragment,THREE.ShaderChunk.envmap_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.shadowmap_pars_fragment,THREE.ShaderChunk.specularmap_pars_fragment,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\n\tgl_FragColor = vec4( diffuse, opacity );",THREE.ShaderChunk.logdepthbuf_fragment,
-THREE.ShaderChunk.map_fragment,THREE.ShaderChunk.alphamap_fragment,THREE.ShaderChunk.alphatest_fragment,THREE.ShaderChunk.specularmap_fragment,THREE.ShaderChunk.lightmap_fragment,THREE.ShaderChunk.color_fragment,THREE.ShaderChunk.envmap_fragment,THREE.ShaderChunk.shadowmap_fragment,THREE.ShaderChunk.linear_to_gamma_fragment,THREE.ShaderChunk.fog_fragment,"}"].join("\n")},lambert:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.common,THREE.UniformsLib.fog,THREE.UniformsLib.lights,THREE.UniformsLib.shadowmap,
-{ambient:{type:"c",value:new THREE.Color(16777215)},emissive:{type:"c",value:new THREE.Color(0)},wrapRGB:{type:"v3",value:new THREE.Vector3(1,1,1)}}]),vertexShader:["#define LAMBERT\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n#endif",THREE.ShaderChunk.map_pars_vertex,THREE.ShaderChunk.lightmap_pars_vertex,THREE.ShaderChunk.envmap_pars_vertex,THREE.ShaderChunk.lights_lambert_pars_vertex,THREE.ShaderChunk.color_pars_vertex,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,
-THREE.ShaderChunk.shadowmap_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.map_vertex,THREE.ShaderChunk.lightmap_vertex,THREE.ShaderChunk.color_vertex,THREE.ShaderChunk.morphnormal_vertex,THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.skinnormal_vertex,THREE.ShaderChunk.defaultnormal_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.logdepthbuf_vertex,THREE.ShaderChunk.worldpos_vertex,
-THREE.ShaderChunk.envmap_vertex,THREE.ShaderChunk.lights_lambert_vertex,THREE.ShaderChunk.shadowmap_vertex,"}"].join("\n"),fragmentShader:["uniform float opacity;\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n#endif",THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.map_pars_fragment,THREE.ShaderChunk.alphamap_pars_fragment,THREE.ShaderChunk.lightmap_pars_fragment,THREE.ShaderChunk.envmap_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.shadowmap_pars_fragment,
-THREE.ShaderChunk.specularmap_pars_fragment,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\n\tgl_FragColor = vec4( vec3( 1.0 ), opacity );",THREE.ShaderChunk.logdepthbuf_fragment,THREE.ShaderChunk.map_fragment,THREE.ShaderChunk.alphamap_fragment,THREE.ShaderChunk.alphatest_fragment,THREE.ShaderChunk.specularmap_fragment,"\t#ifdef DOUBLE_SIDED\n\t\tif ( gl_FrontFacing )\n\t\t\tgl_FragColor.xyz *= vLightFront;\n\t\telse\n\t\t\tgl_FragColor.xyz *= vLightBack;\n\t#else\n\t\tgl_FragColor.xyz *= vLightFront;\n\t#endif",
-THREE.ShaderChunk.lightmap_fragment,THREE.ShaderChunk.color_fragment,THREE.ShaderChunk.envmap_fragment,THREE.ShaderChunk.shadowmap_fragment,THREE.ShaderChunk.linear_to_gamma_fragment,THREE.ShaderChunk.fog_fragment,"}"].join("\n")},phong:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.common,THREE.UniformsLib.bump,THREE.UniformsLib.normalmap,THREE.UniformsLib.fog,THREE.UniformsLib.lights,THREE.UniformsLib.shadowmap,{ambient:{type:"c",value:new THREE.Color(16777215)},emissive:{type:"c",value:new THREE.Color(0)},
-specular:{type:"c",value:new THREE.Color(1118481)},shininess:{type:"f",value:30},wrapRGB:{type:"v3",value:new THREE.Vector3(1,1,1)}}]),vertexShader:["#define PHONG\nvarying vec3 vViewPosition;\nvarying vec3 vNormal;",THREE.ShaderChunk.map_pars_vertex,THREE.ShaderChunk.lightmap_pars_vertex,THREE.ShaderChunk.envmap_pars_vertex,THREE.ShaderChunk.lights_phong_pars_vertex,THREE.ShaderChunk.color_pars_vertex,THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.shadowmap_pars_vertex,
-THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.map_vertex,THREE.ShaderChunk.lightmap_vertex,THREE.ShaderChunk.color_vertex,THREE.ShaderChunk.morphnormal_vertex,THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.skinnormal_vertex,THREE.ShaderChunk.defaultnormal_vertex,"\tvNormal = normalize( transformedNormal );",THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"\tvViewPosition = -mvPosition.xyz;",
-THREE.ShaderChunk.worldpos_vertex,THREE.ShaderChunk.envmap_vertex,THREE.ShaderChunk.lights_phong_vertex,THREE.ShaderChunk.shadowmap_vertex,"}"].join("\n"),fragmentShader:["#define PHONG\nuniform vec3 diffuse;\nuniform float opacity;\nuniform vec3 ambient;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;",THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.map_pars_fragment,THREE.ShaderChunk.alphamap_pars_fragment,THREE.ShaderChunk.lightmap_pars_fragment,THREE.ShaderChunk.envmap_pars_fragment,
-THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.lights_phong_pars_fragment,THREE.ShaderChunk.shadowmap_pars_fragment,THREE.ShaderChunk.bumpmap_pars_fragment,THREE.ShaderChunk.normalmap_pars_fragment,THREE.ShaderChunk.specularmap_pars_fragment,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\n\tgl_FragColor = vec4( vec3( 1.0 ), opacity );",THREE.ShaderChunk.logdepthbuf_fragment,THREE.ShaderChunk.map_fragment,THREE.ShaderChunk.alphamap_fragment,THREE.ShaderChunk.alphatest_fragment,THREE.ShaderChunk.specularmap_fragment,
-THREE.ShaderChunk.lights_phong_fragment,THREE.ShaderChunk.lightmap_fragment,THREE.ShaderChunk.color_fragment,THREE.ShaderChunk.envmap_fragment,THREE.ShaderChunk.shadowmap_fragment,THREE.ShaderChunk.linear_to_gamma_fragment,THREE.ShaderChunk.fog_fragment,"}"].join("\n")},particle_basic:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.particle,THREE.UniformsLib.shadowmap]),vertexShader:["uniform float size;\nuniform float scale;",THREE.ShaderChunk.color_pars_vertex,THREE.ShaderChunk.shadowmap_pars_vertex,
-THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.color_vertex,"\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\t#ifdef USE_SIZEATTENUATION\n\t\tgl_PointSize = size * ( scale / length( mvPosition.xyz ) );\n\t#else\n\t\tgl_PointSize = size;\n\t#endif\n\tgl_Position = projectionMatrix * mvPosition;",THREE.ShaderChunk.logdepthbuf_vertex,THREE.ShaderChunk.worldpos_vertex,THREE.ShaderChunk.shadowmap_vertex,"}"].join("\n"),fragmentShader:["uniform vec3 psColor;\nuniform float opacity;",
-THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.map_particle_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.shadowmap_pars_fragment,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\n\tgl_FragColor = vec4( psColor, opacity );",THREE.ShaderChunk.logdepthbuf_fragment,THREE.ShaderChunk.map_particle_fragment,THREE.ShaderChunk.alphatest_fragment,THREE.ShaderChunk.color_fragment,THREE.ShaderChunk.shadowmap_fragment,THREE.ShaderChunk.fog_fragment,"}"].join("\n")},dashed:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.common,
-THREE.UniformsLib.fog,{scale:{type:"f",value:1},dashSize:{type:"f",value:1},totalSize:{type:"f",value:2}}]),vertexShader:["uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;",THREE.ShaderChunk.color_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.color_vertex,"\tvLineDistance = scale * lineDistance;\n\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\tgl_Position = projectionMatrix * mvPosition;",THREE.ShaderChunk.logdepthbuf_vertex,
-"}"].join("\n"),fragmentShader:["uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;",THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tgl_FragColor = vec4( diffuse, opacity );",THREE.ShaderChunk.logdepthbuf_fragment,THREE.ShaderChunk.color_fragment,THREE.ShaderChunk.fog_fragment,
-"}"].join("\n")},depth:{uniforms:{mNear:{type:"f",value:1},mFar:{type:"f",value:2E3},opacity:{type:"f",value:1}},vertexShader:[THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform float mNear;\nuniform float mFar;\nuniform float opacity;",THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {",THREE.ShaderChunk.logdepthbuf_fragment,
-"\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tfloat depth = gl_FragDepthEXT / gl_FragCoord.w;\n\t#else\n\t\tfloat depth = gl_FragCoord.z / gl_FragCoord.w;\n\t#endif\n\tfloat color = 1.0 - smoothstep( mNear, mFar, depth );\n\tgl_FragColor = vec4( vec3( color ), opacity );\n}"].join("\n")},normal:{uniforms:{opacity:{type:"f",value:1}},vertexShader:["varying vec3 vNormal;",THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {\n\tvNormal = normalize( normalMatrix * normal );",
-THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform float opacity;\nvarying vec3 vNormal;",THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\n\tgl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );",THREE.ShaderChunk.logdepthbuf_fragment,"}"].join("\n")},normalmap:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.fog,THREE.UniformsLib.lights,THREE.UniformsLib.shadowmap,{enableAO:{type:"i",
-value:0},enableDiffuse:{type:"i",value:0},enableSpecular:{type:"i",value:0},enableReflection:{type:"i",value:0},enableDisplacement:{type:"i",value:0},tDisplacement:{type:"t",value:null},tDiffuse:{type:"t",value:null},tCube:{type:"t",value:null},tNormal:{type:"t",value:null},tSpecular:{type:"t",value:null},tAO:{type:"t",value:null},uNormalScale:{type:"v2",value:new THREE.Vector2(1,1)},uDisplacementBias:{type:"f",value:0},uDisplacementScale:{type:"f",value:1},diffuse:{type:"c",value:new THREE.Color(16777215)},
-specular:{type:"c",value:new THREE.Color(1118481)},ambient:{type:"c",value:new THREE.Color(16777215)},shininess:{type:"f",value:30},opacity:{type:"f",value:1},useRefract:{type:"i",value:0},refractionRatio:{type:"f",value:.98},reflectivity:{type:"f",value:.5},uOffset:{type:"v2",value:new THREE.Vector2(0,0)},uRepeat:{type:"v2",value:new THREE.Vector2(1,1)},wrapRGB:{type:"v3",value:new THREE.Vector3(1,1,1)}}]),fragmentShader:["uniform vec3 ambient;\nuniform vec3 diffuse;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\nuniform bool enableDiffuse;\nuniform bool enableSpecular;\nuniform bool enableAO;\nuniform bool enableReflection;\nuniform sampler2D tDiffuse;\nuniform sampler2D tNormal;\nuniform sampler2D tSpecular;\nuniform sampler2D tAO;\nuniform samplerCube tCube;\nuniform vec2 uNormalScale;\nuniform bool useRefract;\nuniform float refractionRatio;\nuniform float reflectivity;\nvarying vec3 vTangent;\nvarying vec3 vBinormal;\nvarying vec3 vNormal;\nvarying vec2 vUv;\nuniform vec3 ambientLightColor;\n#if MAX_DIR_LIGHTS > 0\n\tuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n\tuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n#endif\n#if MAX_HEMI_LIGHTS > 0\n\tuniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n#endif\n#if MAX_POINT_LIGHTS > 0\n\tuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\tuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n\tuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n#endif\n#if MAX_SPOT_LIGHTS > 0\n\tuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n#endif\n#ifdef WRAP_AROUND\n\tuniform vec3 wrapRGB;\n#endif\nvarying vec3 vWorldPosition;\nvarying vec3 vViewPosition;",
-THREE.ShaderChunk.shadowmap_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {",THREE.ShaderChunk.logdepthbuf_fragment,"\tgl_FragColor = vec4( vec3( 1.0 ), opacity );\n\tvec3 specularTex = vec3( 1.0 );\n\tvec3 normalTex = texture2D( tNormal, vUv ).xyz * 2.0 - 1.0;\n\tnormalTex.xy *= uNormalScale;\n\tnormalTex = normalize( normalTex );\n\tif( enableDiffuse ) {\n\t\t#ifdef GAMMA_INPUT\n\t\t\tvec4 texelColor = texture2D( tDiffuse, vUv );\n\t\t\ttexelColor.xyz *= texelColor.xyz;\n\t\t\tgl_FragColor = gl_FragColor * texelColor;\n\t\t#else\n\t\t\tgl_FragColor = gl_FragColor * texture2D( tDiffuse, vUv );\n\t\t#endif\n\t}\n\tif( enableAO ) {\n\t\t#ifdef GAMMA_INPUT\n\t\t\tvec4 aoColor = texture2D( tAO, vUv );\n\t\t\taoColor.xyz *= aoColor.xyz;\n\t\t\tgl_FragColor.xyz = gl_FragColor.xyz * aoColor.xyz;\n\t\t#else\n\t\t\tgl_FragColor.xyz = gl_FragColor.xyz * texture2D( tAO, vUv ).xyz;\n\t\t#endif\n\t}",
-THREE.ShaderChunk.alphatest_fragment,"\tif( enableSpecular )\n\t\tspecularTex = texture2D( tSpecular, vUv ).xyz;\n\tmat3 tsb = mat3( normalize( vTangent ), normalize( vBinormal ), normalize( vNormal ) );\n\tvec3 finalNormal = tsb * normalTex;\n\t#ifdef FLIP_SIDED\n\t\tfinalNormal = -finalNormal;\n\t#endif\n\tvec3 normal = normalize( finalNormal );\n\tvec3 viewPosition = normalize( vViewPosition );\n\t#if MAX_POINT_LIGHTS > 0\n\t\tvec3 pointDiffuse = vec3( 0.0 );\n\t\tvec3 pointSpecular = vec3( 0.0 );\n\t\tfor ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\t\t\tvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n\t\t\tvec3 pointVector = lPosition.xyz + vViewPosition.xyz;\n\t\t\tfloat pointDistance = 1.0;\n\t\t\tif ( pointLightDistance[ i ] > 0.0 )\n\t\t\t\tpointDistance = 1.0 - min( ( length( pointVector ) / pointLightDistance[ i ] ), 1.0 );\n\t\t\tpointVector = normalize( pointVector );\n\t\t\t#ifdef WRAP_AROUND\n\t\t\t\tfloat pointDiffuseWeightFull = max( dot( normal, pointVector ), 0.0 );\n\t\t\t\tfloat pointDiffuseWeightHalf = max( 0.5 * dot( normal, pointVector ) + 0.5, 0.0 );\n\t\t\t\tvec3 pointDiffuseWeight = mix( vec3( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );\n\t\t\t#else\n\t\t\t\tfloat pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );\n\t\t\t#endif\n\t\t\tpointDiffuse += pointDistance * pointLightColor[ i ] * diffuse * pointDiffuseWeight;\n\t\t\tvec3 pointHalfVector = normalize( pointVector + viewPosition );\n\t\t\tfloat pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );\n\t\t\tfloat pointSpecularWeight = specularTex.r * max( pow( pointDotNormalHalf, shininess ), 0.0 );\n\t\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\t\t\tvec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( pointVector, pointHalfVector ), 0.0 ), 5.0 );\n\t\t\tpointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * pointDistance * specularNormalization;\n\t\t}\n\t#endif\n\t#if MAX_SPOT_LIGHTS > 0\n\t\tvec3 spotDiffuse = vec3( 0.0 );\n\t\tvec3 spotSpecular = vec3( 0.0 );\n\t\tfor ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\t\t\tvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n\t\t\tvec3 spotVector = lPosition.xyz + vViewPosition.xyz;\n\t\t\tfloat spotDistance = 1.0;\n\t\t\tif ( spotLightDistance[ i ] > 0.0 )\n\t\t\t\tspotDistance = 1.0 - min( ( length( spotVector ) / spotLightDistance[ i ] ), 1.0 );\n\t\t\tspotVector = normalize( spotVector );\n\t\t\tfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );\n\t\t\tif ( spotEffect > spotLightAngleCos[ i ] ) {\n\t\t\t\tspotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\t\t\t\t#ifdef WRAP_AROUND\n\t\t\t\t\tfloat spotDiffuseWeightFull = max( dot( normal, spotVector ), 0.0 );\n\t\t\t\t\tfloat spotDiffuseWeightHalf = max( 0.5 * dot( normal, spotVector ) + 0.5, 0.0 );\n\t\t\t\t\tvec3 spotDiffuseWeight = mix( vec3( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );\n\t\t\t\t#else\n\t\t\t\t\tfloat spotDiffuseWeight = max( dot( normal, spotVector ), 0.0 );\n\t\t\t\t#endif\n\t\t\t\tspotDiffuse += spotDistance * spotLightColor[ i ] * diffuse * spotDiffuseWeight * spotEffect;\n\t\t\t\tvec3 spotHalfVector = normalize( spotVector + viewPosition );\n\t\t\t\tfloat spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );\n\t\t\t\tfloat spotSpecularWeight = specularTex.r * max( pow( spotDotNormalHalf, shininess ), 0.0 );\n\t\t\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\t\t\t\tvec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( spotVector, spotHalfVector ), 0.0 ), 5.0 );\n\t\t\t\tspotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * spotDistance * specularNormalization * spotEffect;\n\t\t\t}\n\t\t}\n\t#endif\n\t#if MAX_DIR_LIGHTS > 0\n\t\tvec3 dirDiffuse = vec3( 0.0 );\n\t\tvec3 dirSpecular = vec3( 0.0 );\n\t\tfor( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {\n\t\t\tvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\n\t\t\tvec3 dirVector = normalize( lDirection.xyz );\n\t\t\t#ifdef WRAP_AROUND\n\t\t\t\tfloat directionalLightWeightingFull = max( dot( normal, dirVector ), 0.0 );\n\t\t\t\tfloat directionalLightWeightingHalf = max( 0.5 * dot( normal, dirVector ) + 0.5, 0.0 );\n\t\t\t\tvec3 dirDiffuseWeight = mix( vec3( directionalLightWeightingFull ), vec3( directionalLightWeightingHalf ), wrapRGB );\n\t\t\t#else\n\t\t\t\tfloat dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );\n\t\t\t#endif\n\t\t\tdirDiffuse += directionalLightColor[ i ] * diffuse * dirDiffuseWeight;\n\t\t\tvec3 dirHalfVector = normalize( dirVector + viewPosition );\n\t\t\tfloat dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );\n\t\t\tfloat dirSpecularWeight = specularTex.r * max( pow( dirDotNormalHalf, shininess ), 0.0 );\n\t\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\t\t\tvec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( dirVector, dirHalfVector ), 0.0 ), 5.0 );\n\t\t\tdirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;\n\t\t}\n\t#endif\n\t#if MAX_HEMI_LIGHTS > 0\n\t\tvec3 hemiDiffuse = vec3( 0.0 );\n\t\tvec3 hemiSpecular = vec3( 0.0 );\n\t\tfor( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\t\t\tvec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\n\t\t\tvec3 lVector = normalize( lDirection.xyz );\n\t\t\tfloat dotProduct = dot( normal, lVector );\n\t\t\tfloat hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\t\t\tvec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\t\t\themiDiffuse += diffuse * hemiColor;\n\t\t\tvec3 hemiHalfVectorSky = normalize( lVector + viewPosition );\n\t\t\tfloat hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;\n\t\t\tfloat hemiSpecularWeightSky = specularTex.r * max( pow( max( hemiDotNormalHalfSky, 0.0 ), shininess ), 0.0 );\n\t\t\tvec3 lVectorGround = -lVector;\n\t\t\tvec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );\n\t\t\tfloat hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;\n\t\t\tfloat hemiSpecularWeightGround = specularTex.r * max( pow( max( hemiDotNormalHalfGround, 0.0 ), shininess ), 0.0 );\n\t\t\tfloat dotProductGround = dot( normal, lVectorGround );\n\t\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\t\t\tvec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, hemiHalfVectorSky ), 0.0 ), 5.0 );\n\t\t\tvec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 0.0 ), 5.0 );\n\t\t\themiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );\n\t\t}\n\t#endif\n\tvec3 totalDiffuse = vec3( 0.0 );\n\tvec3 totalSpecular = vec3( 0.0 );\n\t#if MAX_DIR_LIGHTS > 0\n\t\ttotalDiffuse += dirDiffuse;\n\t\ttotalSpecular += dirSpecular;\n\t#endif\n\t#if MAX_HEMI_LIGHTS > 0\n\t\ttotalDiffuse += hemiDiffuse;\n\t\ttotalSpecular += hemiSpecular;\n\t#endif\n\t#if MAX_POINT_LIGHTS > 0\n\t\ttotalDiffuse += pointDiffuse;\n\t\ttotalSpecular += pointSpecular;\n\t#endif\n\t#if MAX_SPOT_LIGHTS > 0\n\t\ttotalDiffuse += spotDiffuse;\n\t\ttotalSpecular += spotSpecular;\n\t#endif\n\t#ifdef METAL\n\t\tgl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * ambient + totalSpecular );\n\t#else\n\t\tgl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * ambient ) + totalSpecular;\n\t#endif\n\tif ( enableReflection ) {\n\t\tvec3 vReflect;\n\t\tvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\t\tif ( useRefract ) {\n\t\t\tvReflect = refract( cameraToVertex, normal, refractionRatio );\n\t\t} else {\n\t\t\tvReflect = reflect( cameraToVertex, normal );\n\t\t}\n\t\tvec4 cubeColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );\n\t\t#ifdef GAMMA_INPUT\n\t\t\tcubeColor.xyz *= cubeColor.xyz;\n\t\t#endif\n\t\tgl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularTex.r * reflectivity );\n\t}",
-THREE.ShaderChunk.shadowmap_fragment,THREE.ShaderChunk.linear_to_gamma_fragment,THREE.ShaderChunk.fog_fragment,"}"].join("\n"),vertexShader:["attribute vec4 tangent;\nuniform vec2 uOffset;\nuniform vec2 uRepeat;\nuniform bool enableDisplacement;\n#ifdef VERTEX_TEXTURES\n\tuniform sampler2D tDisplacement;\n\tuniform float uDisplacementScale;\n\tuniform float uDisplacementBias;\n#endif\nvarying vec3 vTangent;\nvarying vec3 vBinormal;\nvarying vec3 vNormal;\nvarying vec2 vUv;\nvarying vec3 vWorldPosition;\nvarying vec3 vViewPosition;",
-THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.shadowmap_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.skinnormal_vertex,"\t#ifdef USE_SKINNING\n\t\tvNormal = normalize( normalMatrix * skinnedNormal.xyz );\n\t\tvec4 skinnedTangent = skinMatrix * vec4( tangent.xyz, 0.0 );\n\t\tvTangent = normalize( normalMatrix * skinnedTangent.xyz );\n\t#else\n\t\tvNormal = normalize( normalMatrix * normal );\n\t\tvTangent = normalize( normalMatrix * tangent.xyz );\n\t#endif\n\tvBinormal = normalize( cross( vNormal, vTangent ) * tangent.w );\n\tvUv = uv * uRepeat + uOffset;\n\tvec3 displacedPosition;\n\t#ifdef VERTEX_TEXTURES\n\t\tif ( enableDisplacement ) {\n\t\t\tvec3 dv = texture2D( tDisplacement, uv ).xyz;\n\t\t\tfloat df = uDisplacementScale * dv.x + uDisplacementBias;\n\t\t\tdisplacedPosition = position + normalize( normal ) * df;\n\t\t} else {\n\t\t\t#ifdef USE_SKINNING\n\t\t\t\tvec4 skinVertex = bindMatrix * vec4( position, 1.0 );\n\t\t\t\tvec4 skinned = vec4( 0.0 );\n\t\t\t\tskinned += boneMatX * skinVertex * skinWeight.x;\n\t\t\t\tskinned += boneMatY * skinVertex * skinWeight.y;\n\t\t\t\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\t\t\t\tskinned += boneMatW * skinVertex * skinWeight.w;\n\t\t\t\tskinned  = bindMatrixInverse * skinned;\n\t\t\t\tdisplacedPosition = skinned.xyz;\n\t\t\t#else\n\t\t\t\tdisplacedPosition = position;\n\t\t\t#endif\n\t\t}\n\t#else\n\t\t#ifdef USE_SKINNING\n\t\t\tvec4 skinVertex = bindMatrix * vec4( position, 1.0 );\n\t\t\tvec4 skinned = vec4( 0.0 );\n\t\t\tskinned += boneMatX * skinVertex * skinWeight.x;\n\t\t\tskinned += boneMatY * skinVertex * skinWeight.y;\n\t\t\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\t\t\tskinned += boneMatW * skinVertex * skinWeight.w;\n\t\t\tskinned  = bindMatrixInverse * skinned;\n\t\t\tdisplacedPosition = skinned.xyz;\n\t\t#else\n\t\t\tdisplacedPosition = position;\n\t\t#endif\n\t#endif\n\tvec4 mvPosition = modelViewMatrix * vec4( displacedPosition, 1.0 );\n\tvec4 worldPosition = modelMatrix * vec4( displacedPosition, 1.0 );\n\tgl_Position = projectionMatrix * mvPosition;",
-THREE.ShaderChunk.logdepthbuf_vertex,"\tvWorldPosition = worldPosition.xyz;\n\tvViewPosition = -mvPosition.xyz;\n\t#ifdef USE_SHADOWMAP\n\t\tfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\t\t\tvShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n\t\t}\n\t#endif\n}"].join("\n")},cube:{uniforms:{tCube:{type:"t",value:null},tFlip:{type:"f",value:-1}},vertexShader:["varying vec3 vWorldPosition;",THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {\n\tvec4 worldPosition = modelMatrix * vec4( position, 1.0 );\n\tvWorldPosition = worldPosition.xyz;\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
-THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform samplerCube tCube;\nuniform float tFlip;\nvarying vec3 vWorldPosition;",THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\n\tgl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );",THREE.ShaderChunk.logdepthbuf_fragment,"}"].join("\n")},depthRGBA:{uniforms:{},vertexShader:[THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,
-"void main() {",THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:[THREE.ShaderChunk.logdepthbuf_pars_fragment,"vec4 pack_depth( const in float depth ) {\n\tconst vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );\n\tconst vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );\n\tvec4 res = mod( depth * bit_shift * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );\n\tres -= res.xxyz * bit_mask;\n\treturn res;\n}\nvoid main() {",
-THREE.ShaderChunk.logdepthbuf_fragment,"\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tgl_FragData[ 0 ] = pack_depth( gl_FragDepthEXT );\n\t#else\n\t\tgl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );\n\t#endif\n}"].join("\n")}};
-THREE.WebGLRenderer=function(a){function b(a){var b=a.geometry;a=a.material;var c=b.vertices.length;if(a.attributes){void 0===b.__webglCustomAttributesList&&(b.__webglCustomAttributesList=[]);for(var d in a.attributes){var e=a.attributes[d];if(!e.__webglInitialized||e.createUniqueBuffers){e.__webglInitialized=!0;var f=1;"v2"===e.type?f=2:"v3"===e.type?f=3:"v4"===e.type?f=4:"c"===e.type&&(f=3);e.size=f;e.array=new Float32Array(c*f);e.buffer=l.createBuffer();e.buffer.belongsToAttribute=d;e.needsUpdate=
-!0}b.__webglCustomAttributesList.push(e)}}}function c(a,b){var c=b.geometry,e=a.faces3,f=3*e.length,g=1*e.length,h=3*e.length,e=d(b,a);a.__vertexArray=new Float32Array(3*f);a.__normalArray=new Float32Array(3*f);a.__colorArray=new Float32Array(3*f);a.__uvArray=new Float32Array(2*f);1<c.faceVertexUvs.length&&(a.__uv2Array=new Float32Array(2*f));c.hasTangents&&(a.__tangentArray=new Float32Array(4*f));b.geometry.skinWeights.length&&b.geometry.skinIndices.length&&(a.__skinIndexArray=new Float32Array(4*
-f),a.__skinWeightArray=new Float32Array(4*f));c=null!==pa.get("OES_element_index_uint")&&21845<g?Uint32Array:Uint16Array;a.__typeArray=c;a.__faceArray=new c(3*g);a.__lineArray=new c(2*h);var k;if(a.numMorphTargets)for(a.__morphTargetsArrays=[],c=0,k=a.numMorphTargets;c<k;c++)a.__morphTargetsArrays.push(new Float32Array(3*f));if(a.numMorphNormals)for(a.__morphNormalsArrays=[],c=0,k=a.numMorphNormals;c<k;c++)a.__morphNormalsArrays.push(new Float32Array(3*f));a.__webglFaceCount=3*g;a.__webglLineCount=
-2*h;if(e.attributes){void 0===a.__webglCustomAttributesList&&(a.__webglCustomAttributesList=[]);for(var m in e.attributes){var g=e.attributes[m],h={},n;for(n in g)h[n]=g[n];if(!h.__webglInitialized||h.createUniqueBuffers)h.__webglInitialized=!0,c=1,"v2"===h.type?c=2:"v3"===h.type?c=3:"v4"===h.type?c=4:"c"===h.type&&(c=3),h.size=c,h.array=new Float32Array(f*c),h.buffer=l.createBuffer(),h.buffer.belongsToAttribute=m,g.needsUpdate=!0,h.__original=g;a.__webglCustomAttributesList.push(h)}}a.__inittedArrays=
-!0}function d(a,b){return a.material instanceof THREE.MeshFaceMaterial?a.material.materials[b.materialIndex]:a.material}function e(a,b,c,d){c=c.attributes;var e=b.attributes;b=b.attributesKeys;for(var f=0,k=b.length;f<k;f++){var m=b[f],n=e[m];if(0<=n){var p=c[m];void 0!==p?(m=p.itemSize,l.bindBuffer(l.ARRAY_BUFFER,p.buffer),g(n),l.vertexAttribPointer(n,m,l.FLOAT,!1,0,d*m*4)):void 0!==a.defaultAttributeValues&&(2===a.defaultAttributeValues[m].length?l.vertexAttrib2fv(n,a.defaultAttributeValues[m]):
-3===a.defaultAttributeValues[m].length&&l.vertexAttrib3fv(n,a.defaultAttributeValues[m]))}}h()}function f(){for(var a=0,b=wb.length;a<b;a++)wb[a]=0}function g(a){wb[a]=1;0===ib[a]&&(l.enableVertexAttribArray(a),ib[a]=1)}function h(){for(var a=0,b=ib.length;a<b;a++)ib[a]!==wb[a]&&(l.disableVertexAttribArray(a),ib[a]=0)}function k(a,b){return a.material.id!==b.material.id?b.material.id-a.material.id:a.z!==b.z?b.z-a.z:a.id-b.id}function n(a,b){return a.z!==b.z?a.z-b.z:a.id-b.id}function p(a,b){return b[0]-
-a[0]}function q(a,e){if(!1!==e.visible){if(!(e instanceof THREE.Scene||e instanceof THREE.Group)){void 0===e.__webglInit&&(e.__webglInit=!0,e._modelViewMatrix=new THREE.Matrix4,e._normalMatrix=new THREE.Matrix3,e.addEventListener("removed",Hc));var f=e.geometry;if(void 0!==f&&void 0===f.__webglInit&&(f.__webglInit=!0,f.addEventListener("dispose",Ic),!(f instanceof THREE.BufferGeometry)))if(e instanceof THREE.Mesh)s(a,e,f);else if(e instanceof THREE.Line){if(void 0===f.__webglVertexBuffer){f.__webglVertexBuffer=
-l.createBuffer();f.__webglColorBuffer=l.createBuffer();f.__webglLineDistanceBuffer=l.createBuffer();J.info.memory.geometries++;var g=f.vertices.length;f.__vertexArray=new Float32Array(3*g);f.__colorArray=new Float32Array(3*g);f.__lineDistanceArray=new Float32Array(1*g);f.__webglLineCount=g;b(e);f.verticesNeedUpdate=!0;f.colorsNeedUpdate=!0;f.lineDistancesNeedUpdate=!0}}else if(e instanceof THREE.PointCloud&&void 0===f.__webglVertexBuffer){f.__webglVertexBuffer=l.createBuffer();f.__webglColorBuffer=
-l.createBuffer();J.info.memory.geometries++;var h=f.vertices.length;f.__vertexArray=new Float32Array(3*h);f.__colorArray=new Float32Array(3*h);f.__sortArray=[];f.__webglParticleCount=h;b(e);f.verticesNeedUpdate=!0;f.colorsNeedUpdate=!0}if(void 0===e.__webglActive)if(e.__webglActive=!0,e instanceof THREE.Mesh)if(f instanceof THREE.BufferGeometry)u(ob,f,e);else{if(f instanceof THREE.Geometry)for(var k=xb[f.id],m=0,n=k.length;m<n;m++)u(ob,k[m],e)}else e instanceof THREE.Line||e instanceof THREE.PointCloud?
-u(ob,f,e):(e instanceof THREE.ImmediateRenderObject||e.immediateRenderCallback)&&jb.push({id:null,object:e,opaque:null,transparent:null,z:0});if(e instanceof THREE.Light)cb.push(e);else if(e instanceof THREE.Sprite)yb.push(e);else if(e instanceof THREE.LensFlare)Ra.push(e);else{var t=ob[e.id];if(t&&(!1===e.frustumCulled||!0===Ec.intersectsObject(e))){var r=e.geometry,w,G;if(r instanceof THREE.BufferGeometry)for(var x=r.attributes,D=r.attributesKeys,E=0,B=D.length;E<B;E++){var A=D[E],K=x[A];void 0===
-K.buffer&&(K.buffer=l.createBuffer(),K.needsUpdate=!0);if(!0===K.needsUpdate){var F="index"===A?l.ELEMENT_ARRAY_BUFFER:l.ARRAY_BUFFER;l.bindBuffer(F,K.buffer);l.bufferData(F,K.array,l.STATIC_DRAW);K.needsUpdate=!1}}else if(e instanceof THREE.Mesh){!0===r.groupsNeedUpdate&&s(a,e,r);for(var H=xb[r.id],O=0,Q=H.length;O<Q;O++){var R=H[O];G=d(e,R);!0===r.groupsNeedUpdate&&c(R,e);w=G.attributes&&v(G);if(r.verticesNeedUpdate||r.morphTargetsNeedUpdate||r.elementsNeedUpdate||r.uvsNeedUpdate||r.normalsNeedUpdate||
-r.colorsNeedUpdate||r.tangentsNeedUpdate||w){var C=R,P=e,S=l.DYNAMIC_DRAW,T=!r.dynamic,X=G;if(C.__inittedArrays){var bb=X&&void 0!==X.shading&&X.shading===THREE.SmoothShading,M=void 0,ea=void 0,Y=void 0,ca=void 0,ma=void 0,pa=void 0,sa=void 0,Fa=void 0,la=void 0,hb=void 0,za=void 0,aa=void 0,$=void 0,Z=void 0,ya=void 0,qa=void 0,L=void 0,Ga=void 0,na=void 0,nc=void 0,ia=void 0,oc=void 0,pc=void 0,qc=void 0,Ba=void 0,zb=void 0,Ab=void 0,Ha=void 0,Bb=void 0,Aa=void 0,va=void 0,Cb=void 0,Oa=void 0,Qb=
-void 0,Ma=void 0,ib=void 0,Ya=void 0,Za=void 0,uc=void 0,Rb=void 0,db=0,eb=0,qb=0,rb=0,Db=0,Sa=0,Ca=0,Pa=0,Ka=0,ja=0,ta=0,I=0,Ia=void 0,Qa=C.__vertexArray,sb=C.__uvArray,fb=C.__uv2Array,Ta=C.__normalArray,ra=C.__tangentArray,La=C.__colorArray,Ua=C.__skinIndexArray,Va=C.__skinWeightArray,Eb=C.__morphTargetsArrays,Jc=C.__morphNormalsArrays,Kb=C.__webglCustomAttributesList,z=void 0,Sb=C.__faceArray,Ja=C.__lineArray,wa=P.geometry,$a=wa.elementsNeedUpdate,Kc=wa.uvsNeedUpdate,ec=wa.normalsNeedUpdate,da=
-wa.tangentsNeedUpdate,wb=wa.colorsNeedUpdate,U=wa.morphTargetsNeedUpdate,fa=wa.vertices,N=C.faces3,xa=wa.faces,ua=wa.faceVertexUvs[0],Lc=wa.faceVertexUvs[1],Fc=wa.skinIndices,Tb=wa.skinWeights,kb=wa.morphTargets,Da=wa.morphNormals;if(wa.verticesNeedUpdate){M=0;for(ea=N.length;M<ea;M++)ca=xa[N[M]],aa=fa[ca.a],$=fa[ca.b],Z=fa[ca.c],Qa[eb]=aa.x,Qa[eb+1]=aa.y,Qa[eb+2]=aa.z,Qa[eb+3]=$.x,Qa[eb+4]=$.y,Qa[eb+5]=$.z,Qa[eb+6]=Z.x,Qa[eb+7]=Z.y,Qa[eb+8]=Z.z,eb+=9;l.bindBuffer(l.ARRAY_BUFFER,C.__webglVertexBuffer);
-l.bufferData(l.ARRAY_BUFFER,Qa,S)}if(U)for(Ma=0,ib=kb.length;Ma<ib;Ma++){M=ta=0;for(ea=N.length;M<ea;M++)uc=N[M],ca=xa[uc],aa=kb[Ma].vertices[ca.a],$=kb[Ma].vertices[ca.b],Z=kb[Ma].vertices[ca.c],Ya=Eb[Ma],Ya[ta]=aa.x,Ya[ta+1]=aa.y,Ya[ta+2]=aa.z,Ya[ta+3]=$.x,Ya[ta+4]=$.y,Ya[ta+5]=$.z,Ya[ta+6]=Z.x,Ya[ta+7]=Z.y,Ya[ta+8]=Z.z,X.morphNormals&&(bb?(Rb=Da[Ma].vertexNormals[uc],Ga=Rb.a,na=Rb.b,nc=Rb.c):nc=na=Ga=Da[Ma].faceNormals[uc],Za=Jc[Ma],Za[ta]=Ga.x,Za[ta+1]=Ga.y,Za[ta+2]=Ga.z,Za[ta+3]=na.x,Za[ta+4]=
-na.y,Za[ta+5]=na.z,Za[ta+6]=nc.x,Za[ta+7]=nc.y,Za[ta+8]=nc.z),ta+=9;l.bindBuffer(l.ARRAY_BUFFER,C.__webglMorphTargetsBuffers[Ma]);l.bufferData(l.ARRAY_BUFFER,Eb[Ma],S);X.morphNormals&&(l.bindBuffer(l.ARRAY_BUFFER,C.__webglMorphNormalsBuffers[Ma]),l.bufferData(l.ARRAY_BUFFER,Jc[Ma],S))}if(Tb.length){M=0;for(ea=N.length;M<ea;M++)ca=xa[N[M]],qc=Tb[ca.a],Ba=Tb[ca.b],zb=Tb[ca.c],Va[ja]=qc.x,Va[ja+1]=qc.y,Va[ja+2]=qc.z,Va[ja+3]=qc.w,Va[ja+4]=Ba.x,Va[ja+5]=Ba.y,Va[ja+6]=Ba.z,Va[ja+7]=Ba.w,Va[ja+8]=zb.x,
-Va[ja+9]=zb.y,Va[ja+10]=zb.z,Va[ja+11]=zb.w,Ab=Fc[ca.a],Ha=Fc[ca.b],Bb=Fc[ca.c],Ua[ja]=Ab.x,Ua[ja+1]=Ab.y,Ua[ja+2]=Ab.z,Ua[ja+3]=Ab.w,Ua[ja+4]=Ha.x,Ua[ja+5]=Ha.y,Ua[ja+6]=Ha.z,Ua[ja+7]=Ha.w,Ua[ja+8]=Bb.x,Ua[ja+9]=Bb.y,Ua[ja+10]=Bb.z,Ua[ja+11]=Bb.w,ja+=12;0<ja&&(l.bindBuffer(l.ARRAY_BUFFER,C.__webglSkinIndicesBuffer),l.bufferData(l.ARRAY_BUFFER,Ua,S),l.bindBuffer(l.ARRAY_BUFFER,C.__webglSkinWeightsBuffer),l.bufferData(l.ARRAY_BUFFER,Va,S))}if(wb){M=0;for(ea=N.length;M<ea;M++)ca=xa[N[M]],sa=ca.vertexColors,
-Fa=ca.color,3===sa.length&&X.vertexColors===THREE.VertexColors?(ia=sa[0],oc=sa[1],pc=sa[2]):pc=oc=ia=Fa,La[Ka]=ia.r,La[Ka+1]=ia.g,La[Ka+2]=ia.b,La[Ka+3]=oc.r,La[Ka+4]=oc.g,La[Ka+5]=oc.b,La[Ka+6]=pc.r,La[Ka+7]=pc.g,La[Ka+8]=pc.b,Ka+=9;0<Ka&&(l.bindBuffer(l.ARRAY_BUFFER,C.__webglColorBuffer),l.bufferData(l.ARRAY_BUFFER,La,S))}if(da&&wa.hasTangents){M=0;for(ea=N.length;M<ea;M++)ca=xa[N[M]],la=ca.vertexTangents,ya=la[0],qa=la[1],L=la[2],ra[Ca]=ya.x,ra[Ca+1]=ya.y,ra[Ca+2]=ya.z,ra[Ca+3]=ya.w,ra[Ca+4]=qa.x,
-ra[Ca+5]=qa.y,ra[Ca+6]=qa.z,ra[Ca+7]=qa.w,ra[Ca+8]=L.x,ra[Ca+9]=L.y,ra[Ca+10]=L.z,ra[Ca+11]=L.w,Ca+=12;l.bindBuffer(l.ARRAY_BUFFER,C.__webglTangentBuffer);l.bufferData(l.ARRAY_BUFFER,ra,S)}if(ec){M=0;for(ea=N.length;M<ea;M++)if(ca=xa[N[M]],ma=ca.vertexNormals,pa=ca.normal,3===ma.length&&bb)for(Aa=0;3>Aa;Aa++)Cb=ma[Aa],Ta[Sa]=Cb.x,Ta[Sa+1]=Cb.y,Ta[Sa+2]=Cb.z,Sa+=3;else for(Aa=0;3>Aa;Aa++)Ta[Sa]=pa.x,Ta[Sa+1]=pa.y,Ta[Sa+2]=pa.z,Sa+=3;l.bindBuffer(l.ARRAY_BUFFER,C.__webglNormalBuffer);l.bufferData(l.ARRAY_BUFFER,
-Ta,S)}if(Kc&&ua){M=0;for(ea=N.length;M<ea;M++)if(Y=N[M],hb=ua[Y],void 0!==hb)for(Aa=0;3>Aa;Aa++)Oa=hb[Aa],sb[qb]=Oa.x,sb[qb+1]=Oa.y,qb+=2;0<qb&&(l.bindBuffer(l.ARRAY_BUFFER,C.__webglUVBuffer),l.bufferData(l.ARRAY_BUFFER,sb,S))}if(Kc&&Lc){M=0;for(ea=N.length;M<ea;M++)if(Y=N[M],za=Lc[Y],void 0!==za)for(Aa=0;3>Aa;Aa++)Qb=za[Aa],fb[rb]=Qb.x,fb[rb+1]=Qb.y,rb+=2;0<rb&&(l.bindBuffer(l.ARRAY_BUFFER,C.__webglUV2Buffer),l.bufferData(l.ARRAY_BUFFER,fb,S))}if($a){M=0;for(ea=N.length;M<ea;M++)Sb[Db]=db,Sb[Db+
-1]=db+1,Sb[Db+2]=db+2,Db+=3,Ja[Pa]=db,Ja[Pa+1]=db+1,Ja[Pa+2]=db,Ja[Pa+3]=db+2,Ja[Pa+4]=db+1,Ja[Pa+5]=db+2,Pa+=6,db+=3;l.bindBuffer(l.ELEMENT_ARRAY_BUFFER,C.__webglFaceBuffer);l.bufferData(l.ELEMENT_ARRAY_BUFFER,Sb,S);l.bindBuffer(l.ELEMENT_ARRAY_BUFFER,C.__webglLineBuffer);l.bufferData(l.ELEMENT_ARRAY_BUFFER,Ja,S)}if(Kb)for(Aa=0,va=Kb.length;Aa<va;Aa++)if(z=Kb[Aa],z.__original.needsUpdate){I=0;if(1===z.size)if(void 0===z.boundTo||"vertices"===z.boundTo)for(M=0,ea=N.length;M<ea;M++)ca=xa[N[M]],z.array[I]=
-z.value[ca.a],z.array[I+1]=z.value[ca.b],z.array[I+2]=z.value[ca.c],I+=3;else{if("faces"===z.boundTo)for(M=0,ea=N.length;M<ea;M++)Ia=z.value[N[M]],z.array[I]=Ia,z.array[I+1]=Ia,z.array[I+2]=Ia,I+=3}else if(2===z.size)if(void 0===z.boundTo||"vertices"===z.boundTo)for(M=0,ea=N.length;M<ea;M++)ca=xa[N[M]],aa=z.value[ca.a],$=z.value[ca.b],Z=z.value[ca.c],z.array[I]=aa.x,z.array[I+1]=aa.y,z.array[I+2]=$.x,z.array[I+3]=$.y,z.array[I+4]=Z.x,z.array[I+5]=Z.y,I+=6;else{if("faces"===z.boundTo)for(M=0,ea=N.length;M<
-ea;M++)Z=$=aa=Ia=z.value[N[M]],z.array[I]=aa.x,z.array[I+1]=aa.y,z.array[I+2]=$.x,z.array[I+3]=$.y,z.array[I+4]=Z.x,z.array[I+5]=Z.y,I+=6}else if(3===z.size){var ka;ka="c"===z.type?["r","g","b"]:["x","y","z"];if(void 0===z.boundTo||"vertices"===z.boundTo)for(M=0,ea=N.length;M<ea;M++)ca=xa[N[M]],aa=z.value[ca.a],$=z.value[ca.b],Z=z.value[ca.c],z.array[I]=aa[ka[0]],z.array[I+1]=aa[ka[1]],z.array[I+2]=aa[ka[2]],z.array[I+3]=$[ka[0]],z.array[I+4]=$[ka[1]],z.array[I+5]=$[ka[2]],z.array[I+6]=Z[ka[0]],z.array[I+
-7]=Z[ka[1]],z.array[I+8]=Z[ka[2]],I+=9;else if("faces"===z.boundTo)for(M=0,ea=N.length;M<ea;M++)Z=$=aa=Ia=z.value[N[M]],z.array[I]=aa[ka[0]],z.array[I+1]=aa[ka[1]],z.array[I+2]=aa[ka[2]],z.array[I+3]=$[ka[0]],z.array[I+4]=$[ka[1]],z.array[I+5]=$[ka[2]],z.array[I+6]=Z[ka[0]],z.array[I+7]=Z[ka[1]],z.array[I+8]=Z[ka[2]],I+=9;else if("faceVertices"===z.boundTo)for(M=0,ea=N.length;M<ea;M++)Ia=z.value[N[M]],aa=Ia[0],$=Ia[1],Z=Ia[2],z.array[I]=aa[ka[0]],z.array[I+1]=aa[ka[1]],z.array[I+2]=aa[ka[2]],z.array[I+
-3]=$[ka[0]],z.array[I+4]=$[ka[1]],z.array[I+5]=$[ka[2]],z.array[I+6]=Z[ka[0]],z.array[I+7]=Z[ka[1]],z.array[I+8]=Z[ka[2]],I+=9}else if(4===z.size)if(void 0===z.boundTo||"vertices"===z.boundTo)for(M=0,ea=N.length;M<ea;M++)ca=xa[N[M]],aa=z.value[ca.a],$=z.value[ca.b],Z=z.value[ca.c],z.array[I]=aa.x,z.array[I+1]=aa.y,z.array[I+2]=aa.z,z.array[I+3]=aa.w,z.array[I+4]=$.x,z.array[I+5]=$.y,z.array[I+6]=$.z,z.array[I+7]=$.w,z.array[I+8]=Z.x,z.array[I+9]=Z.y,z.array[I+10]=Z.z,z.array[I+11]=Z.w,I+=12;else if("faces"===
-z.boundTo)for(M=0,ea=N.length;M<ea;M++)Z=$=aa=Ia=z.value[N[M]],z.array[I]=aa.x,z.array[I+1]=aa.y,z.array[I+2]=aa.z,z.array[I+3]=aa.w,z.array[I+4]=$.x,z.array[I+5]=$.y,z.array[I+6]=$.z,z.array[I+7]=$.w,z.array[I+8]=Z.x,z.array[I+9]=Z.y,z.array[I+10]=Z.z,z.array[I+11]=Z.w,I+=12;else if("faceVertices"===z.boundTo)for(M=0,ea=N.length;M<ea;M++)Ia=z.value[N[M]],aa=Ia[0],$=Ia[1],Z=Ia[2],z.array[I]=aa.x,z.array[I+1]=aa.y,z.array[I+2]=aa.z,z.array[I+3]=aa.w,z.array[I+4]=$.x,z.array[I+5]=$.y,z.array[I+6]=$.z,
-z.array[I+7]=$.w,z.array[I+8]=Z.x,z.array[I+9]=Z.y,z.array[I+10]=Z.z,z.array[I+11]=Z.w,I+=12;l.bindBuffer(l.ARRAY_BUFFER,z.buffer);l.bufferData(l.ARRAY_BUFFER,z.array,S)}T&&(delete C.__inittedArrays,delete C.__colorArray,delete C.__normalArray,delete C.__tangentArray,delete C.__uvArray,delete C.__uv2Array,delete C.__faceArray,delete C.__vertexArray,delete C.__lineArray,delete C.__skinIndexArray,delete C.__skinWeightArray)}}}r.verticesNeedUpdate=!1;r.morphTargetsNeedUpdate=!1;r.elementsNeedUpdate=
-!1;r.uvsNeedUpdate=!1;r.normalsNeedUpdate=!1;r.colorsNeedUpdate=!1;r.tangentsNeedUpdate=!1;G.attributes&&y(G)}else if(e instanceof THREE.Line){G=d(e,r);w=G.attributes&&v(G);if(r.verticesNeedUpdate||r.colorsNeedUpdate||r.lineDistancesNeedUpdate||w){var Zb=l.DYNAMIC_DRAW,ab,Fb,gb,$b,ga,vc,dc=r.vertices,fc=r.colors,Pb=r.lineDistances,kc=dc.length,lc=fc.length,mc=Pb.length,wc=r.__vertexArray,xc=r.__colorArray,jc=r.__lineDistanceArray,sc=r.colorsNeedUpdate,tc=r.lineDistancesNeedUpdate,gc=r.__webglCustomAttributesList,
-yc,Lb,Ea,hc,Wa,oa;if(r.verticesNeedUpdate){for(ab=0;ab<kc;ab++)$b=dc[ab],ga=3*ab,wc[ga]=$b.x,wc[ga+1]=$b.y,wc[ga+2]=$b.z;l.bindBuffer(l.ARRAY_BUFFER,r.__webglVertexBuffer);l.bufferData(l.ARRAY_BUFFER,wc,Zb)}if(sc){for(Fb=0;Fb<lc;Fb++)vc=fc[Fb],ga=3*Fb,xc[ga]=vc.r,xc[ga+1]=vc.g,xc[ga+2]=vc.b;l.bindBuffer(l.ARRAY_BUFFER,r.__webglColorBuffer);l.bufferData(l.ARRAY_BUFFER,xc,Zb)}if(tc){for(gb=0;gb<mc;gb++)jc[gb]=Pb[gb];l.bindBuffer(l.ARRAY_BUFFER,r.__webglLineDistanceBuffer);l.bufferData(l.ARRAY_BUFFER,
-jc,Zb)}if(gc)for(yc=0,Lb=gc.length;yc<Lb;yc++)if(oa=gc[yc],oa.needsUpdate&&(void 0===oa.boundTo||"vertices"===oa.boundTo)){ga=0;hc=oa.value.length;if(1===oa.size)for(Ea=0;Ea<hc;Ea++)oa.array[Ea]=oa.value[Ea];else if(2===oa.size)for(Ea=0;Ea<hc;Ea++)Wa=oa.value[Ea],oa.array[ga]=Wa.x,oa.array[ga+1]=Wa.y,ga+=2;else if(3===oa.size)if("c"===oa.type)for(Ea=0;Ea<hc;Ea++)Wa=oa.value[Ea],oa.array[ga]=Wa.r,oa.array[ga+1]=Wa.g,oa.array[ga+2]=Wa.b,ga+=3;else for(Ea=0;Ea<hc;Ea++)Wa=oa.value[Ea],oa.array[ga]=Wa.x,
-oa.array[ga+1]=Wa.y,oa.array[ga+2]=Wa.z,ga+=3;else if(4===oa.size)for(Ea=0;Ea<hc;Ea++)Wa=oa.value[Ea],oa.array[ga]=Wa.x,oa.array[ga+1]=Wa.y,oa.array[ga+2]=Wa.z,oa.array[ga+3]=Wa.w,ga+=4;l.bindBuffer(l.ARRAY_BUFFER,oa.buffer);l.bufferData(l.ARRAY_BUFFER,oa.array,Zb)}}r.verticesNeedUpdate=!1;r.colorsNeedUpdate=!1;r.lineDistancesNeedUpdate=!1;G.attributes&&y(G)}else if(e instanceof THREE.PointCloud){G=d(e,r);w=G.attributes&&v(G);if(r.verticesNeedUpdate||r.colorsNeedUpdate||e.sortParticles||w){var Mb=
-l.DYNAMIC_DRAW,Xa,tb,ub,W,vb,Ub,zc=r.vertices,pb=zc.length,Nb=r.colors,Ob=Nb.length,ac=r.__vertexArray,bc=r.__colorArray,Gb=r.__sortArray,Xb=r.verticesNeedUpdate,Yb=r.colorsNeedUpdate,Hb=r.__webglCustomAttributesList,lb,ic,ba,mb,ha,V;if(e.sortParticles){Gc.copy(Ac);Gc.multiply(e.matrixWorld);for(Xa=0;Xa<pb;Xa++)ub=zc[Xa],Na.copy(ub),Na.applyProjection(Gc),Gb[Xa]=[Na.z,Xa];Gb.sort(p);for(Xa=0;Xa<pb;Xa++)ub=zc[Gb[Xa][1]],W=3*Xa,ac[W]=ub.x,ac[W+1]=ub.y,ac[W+2]=ub.z;for(tb=0;tb<Ob;tb++)W=3*tb,Ub=Nb[Gb[tb][1]],
-bc[W]=Ub.r,bc[W+1]=Ub.g,bc[W+2]=Ub.b;if(Hb)for(lb=0,ic=Hb.length;lb<ic;lb++)if(V=Hb[lb],void 0===V.boundTo||"vertices"===V.boundTo)if(W=0,mb=V.value.length,1===V.size)for(ba=0;ba<mb;ba++)vb=Gb[ba][1],V.array[ba]=V.value[vb];else if(2===V.size)for(ba=0;ba<mb;ba++)vb=Gb[ba][1],ha=V.value[vb],V.array[W]=ha.x,V.array[W+1]=ha.y,W+=2;else if(3===V.size)if("c"===V.type)for(ba=0;ba<mb;ba++)vb=Gb[ba][1],ha=V.value[vb],V.array[W]=ha.r,V.array[W+1]=ha.g,V.array[W+2]=ha.b,W+=3;else for(ba=0;ba<mb;ba++)vb=Gb[ba][1],
-ha=V.value[vb],V.array[W]=ha.x,V.array[W+1]=ha.y,V.array[W+2]=ha.z,W+=3;else if(4===V.size)for(ba=0;ba<mb;ba++)vb=Gb[ba][1],ha=V.value[vb],V.array[W]=ha.x,V.array[W+1]=ha.y,V.array[W+2]=ha.z,V.array[W+3]=ha.w,W+=4}else{if(Xb)for(Xa=0;Xa<pb;Xa++)ub=zc[Xa],W=3*Xa,ac[W]=ub.x,ac[W+1]=ub.y,ac[W+2]=ub.z;if(Yb)for(tb=0;tb<Ob;tb++)Ub=Nb[tb],W=3*tb,bc[W]=Ub.r,bc[W+1]=Ub.g,bc[W+2]=Ub.b;if(Hb)for(lb=0,ic=Hb.length;lb<ic;lb++)if(V=Hb[lb],V.needsUpdate&&(void 0===V.boundTo||"vertices"===V.boundTo))if(mb=V.value.length,
-W=0,1===V.size)for(ba=0;ba<mb;ba++)V.array[ba]=V.value[ba];else if(2===V.size)for(ba=0;ba<mb;ba++)ha=V.value[ba],V.array[W]=ha.x,V.array[W+1]=ha.y,W+=2;else if(3===V.size)if("c"===V.type)for(ba=0;ba<mb;ba++)ha=V.value[ba],V.array[W]=ha.r,V.array[W+1]=ha.g,V.array[W+2]=ha.b,W+=3;else for(ba=0;ba<mb;ba++)ha=V.value[ba],V.array[W]=ha.x,V.array[W+1]=ha.y,V.array[W+2]=ha.z,W+=3;else if(4===V.size)for(ba=0;ba<mb;ba++)ha=V.value[ba],V.array[W]=ha.x,V.array[W+1]=ha.y,V.array[W+2]=ha.z,V.array[W+3]=ha.w,W+=
-4}if(Xb||e.sortParticles)l.bindBuffer(l.ARRAY_BUFFER,r.__webglVertexBuffer),l.bufferData(l.ARRAY_BUFFER,ac,Mb);if(Yb||e.sortParticles)l.bindBuffer(l.ARRAY_BUFFER,r.__webglColorBuffer),l.bufferData(l.ARRAY_BUFFER,bc,Mb);if(Hb)for(lb=0,ic=Hb.length;lb<ic;lb++)if(V=Hb[lb],V.needsUpdate||e.sortParticles)l.bindBuffer(l.ARRAY_BUFFER,V.buffer),l.bufferData(l.ARRAY_BUFFER,V.array,Mb)}r.verticesNeedUpdate=!1;r.colorsNeedUpdate=!1;G.attributes&&y(G)}for(var cc=0,nb=t.length;cc<nb;cc++){var Bc=t[cc],Vb=Bc,rc=
-Vb.object,Cc=Vb.buffer,Dc=rc.geometry,Wb=rc.material;Wb instanceof THREE.MeshFaceMaterial?(Wb=Wb.materials[Dc instanceof THREE.BufferGeometry?0:Cc.materialIndex],Vb.material=Wb,Wb.transparent?Ib.push(Vb):Jb.push(Vb)):Wb&&(Vb.material=Wb,Wb.transparent?Ib.push(Vb):Jb.push(Vb));Bc.render=!0;!0===J.sortObjects&&(null!==e.renderDepth?Bc.z=e.renderDepth:(Na.setFromMatrixPosition(e.matrixWorld),Na.applyProjection(Ac),Bc.z=Na.z))}}}}cc=0;for(nb=e.children.length;cc<nb;cc++)q(a,e.children[cc])}}function m(a,
-b,c,d,e,f){for(var g,h=a.length-1;-1!==h;h--){g=a[h];var k=g.object,l=g.buffer;x(k,b);if(f)g=f;else{g=g.material;if(!g)continue;e&&J.setBlending(g.blending,g.blendEquation,g.blendSrc,g.blendDst);J.setDepthTest(g.depthTest);J.setDepthWrite(g.depthWrite);B(g.polygonOffset,g.polygonOffsetFactor,g.polygonOffsetUnits)}J.setMaterialFaces(g);l instanceof THREE.BufferGeometry?J.renderBufferDirect(b,c,d,g,l,k):J.renderBuffer(b,c,d,g,l,k)}}function r(a,b,c,d,e,f,g){for(var h,k=0,l=a.length;k<l;k++){h=a[k];
-var m=h.object;if(m.visible){if(g)h=g;else{h=h[b];if(!h)continue;f&&J.setBlending(h.blending,h.blendEquation,h.blendSrc,h.blendDst);J.setDepthTest(h.depthTest);J.setDepthWrite(h.depthWrite);B(h.polygonOffset,h.polygonOffsetFactor,h.polygonOffsetUnits)}J.renderImmediateObject(c,d,e,h,m)}}}function t(a){var b=a.object.material;b.transparent?(a.transparent=b,a.opaque=null):(a.opaque=b,a.transparent=null)}function s(a,b,d){var e=b.material,f=!1;if(void 0===xb[d.id]||!0===d.groupsNeedUpdate){delete ob[b.id];
-a=xb;for(var g=d.id,e=e instanceof THREE.MeshFaceMaterial,h=pa.get("OES_element_index_uint")?4294967296:65535,k,f={},m=d.morphTargets.length,n=d.morphNormals.length,p,r={},q=[],t=0,s=d.faces.length;t<s;t++){k=d.faces[t];var v=e?k.materialIndex:0;v in f||(f[v]={hash:v,counter:0});k=f[v].hash+"_"+f[v].counter;k in r||(p={id:rc++,faces3:[],materialIndex:v,vertices:0,numMorphTargets:m,numMorphNormals:n},r[k]=p,q.push(p));r[k].vertices+3>h&&(f[v].counter+=1,k=f[v].hash+"_"+f[v].counter,k in r||(p={id:rc++,
-faces3:[],materialIndex:v,vertices:0,numMorphTargets:m,numMorphNormals:n},r[k]=p,q.push(p)));r[k].faces3.push(t);r[k].vertices+=3}a[g]=q;d.groupsNeedUpdate=!1}a=xb[d.id];g=0;for(e=a.length;g<e;g++){h=a[g];if(void 0===h.__webglVertexBuffer){f=h;f.__webglVertexBuffer=l.createBuffer();f.__webglNormalBuffer=l.createBuffer();f.__webglTangentBuffer=l.createBuffer();f.__webglColorBuffer=l.createBuffer();f.__webglUVBuffer=l.createBuffer();f.__webglUV2Buffer=l.createBuffer();f.__webglSkinIndicesBuffer=l.createBuffer();
-f.__webglSkinWeightsBuffer=l.createBuffer();f.__webglFaceBuffer=l.createBuffer();f.__webglLineBuffer=l.createBuffer();n=m=void 0;if(f.numMorphTargets)for(f.__webglMorphTargetsBuffers=[],m=0,n=f.numMorphTargets;m<n;m++)f.__webglMorphTargetsBuffers.push(l.createBuffer());if(f.numMorphNormals)for(f.__webglMorphNormalsBuffers=[],m=0,n=f.numMorphNormals;m<n;m++)f.__webglMorphNormalsBuffers.push(l.createBuffer());J.info.memory.geometries++;c(h,b);d.verticesNeedUpdate=!0;d.morphTargetsNeedUpdate=!0;d.elementsNeedUpdate=
-!0;d.uvsNeedUpdate=!0;d.normalsNeedUpdate=!0;d.tangentsNeedUpdate=!0;f=d.colorsNeedUpdate=!0}else f=!1;(f||void 0===b.__webglActive)&&u(ob,h,b)}b.__webglActive=!0}function u(a,b,c){var d=c.id;a[d]=a[d]||[];a[d].push({id:d,buffer:b,object:c,material:null,z:0})}function v(a){for(var b in a.attributes)if(a.attributes[b].needsUpdate)return!0;return!1}function y(a){for(var b in a.attributes)a.attributes[b].needsUpdate=!1}function G(a,b,c,d,e){var f,g,h,k;dc=0;if(d.needsUpdate){d.program&&Cc(d);d.addEventListener("dispose",
-Dc);var m;d instanceof THREE.MeshDepthMaterial?m="depth":d instanceof THREE.MeshNormalMaterial?m="normal":d instanceof THREE.MeshBasicMaterial?m="basic":d instanceof THREE.MeshLambertMaterial?m="lambert":d instanceof THREE.MeshPhongMaterial?m="phong":d instanceof THREE.LineBasicMaterial?m="basic":d instanceof THREE.LineDashedMaterial?m="dashed":d instanceof THREE.PointCloudMaterial&&(m="particle_basic");if(m){var n=THREE.ShaderLib[m];d.__webglShader={uniforms:THREE.UniformsUtils.clone(n.uniforms),
-vertexShader:n.vertexShader,fragmentShader:n.fragmentShader}}else d.__webglShader={uniforms:d.uniforms,vertexShader:d.vertexShader,fragmentShader:d.fragmentShader};for(var p=0,r=0,q=0,t=0,s=0,u=b.length;s<u;s++){var v=b[s];v.onlyShadow||!1===v.visible||(v instanceof THREE.DirectionalLight&&p++,v instanceof THREE.PointLight&&r++,v instanceof THREE.SpotLight&&q++,v instanceof THREE.HemisphereLight&&t++)}f=p;g=r;h=q;k=t;for(var y,G=0,x=0,B=b.length;x<B;x++){var A=b[x];A.castShadow&&(A instanceof THREE.SpotLight&&
-G++,A instanceof THREE.DirectionalLight&&!A.shadowCascade&&G++)}y=G;var C;if(jc&&e&&e.skeleton&&e.skeleton.useVertexTexture)C=1024;else{var H=l.getParameter(l.MAX_VERTEX_UNIFORM_VECTORS),S=Math.floor((H-20)/4);void 0!==e&&e instanceof THREE.SkinnedMesh&&(S=Math.min(e.skeleton.bones.length,S),S<e.skeleton.bones.length&&console.warn("WebGLRenderer: too many bones - "+e.skeleton.bones.length+", this GPU supports just "+S+" (try OpenGL instead of ANGLE)"));C=S}var P={precision:X,supportsVertexTextures:sc,
-map:!!d.map,envMap:!!d.envMap,lightMap:!!d.lightMap,bumpMap:!!d.bumpMap,normalMap:!!d.normalMap,specularMap:!!d.specularMap,alphaMap:!!d.alphaMap,vertexColors:d.vertexColors,fog:c,useFog:d.fog,fogExp:c instanceof THREE.FogExp2,sizeAttenuation:d.sizeAttenuation,logarithmicDepthBuffer:Fa,skinning:d.skinning,maxBones:C,useVertexTexture:jc&&e&&e.skeleton&&e.skeleton.useVertexTexture,morphTargets:d.morphTargets,morphNormals:d.morphNormals,maxMorphTargets:J.maxMorphTargets,maxMorphNormals:J.maxMorphNormals,
-maxDirLights:f,maxPointLights:g,maxSpotLights:h,maxHemiLights:k,maxShadows:y,shadowMapEnabled:J.shadowMapEnabled&&e.receiveShadow&&0<y,shadowMapType:J.shadowMapType,shadowMapDebug:J.shadowMapDebug,shadowMapCascade:J.shadowMapCascade,alphaTest:d.alphaTest,metal:d.metal,wrapAround:d.wrapAround,doubleSided:d.side===THREE.DoubleSide,flipSided:d.side===THREE.BackSide},T=[];m?T.push(m):(T.push(d.fragmentShader),T.push(d.vertexShader));if(void 0!==d.defines)for(var bb in d.defines)T.push(bb),T.push(d.defines[bb]);
-for(bb in P)T.push(bb),T.push(P[bb]);for(var M=T.join(),Y,jb=0,ca=hb.length;jb<ca;jb++){var cb=hb[jb];if(cb.code===M){Y=cb;Y.usedTimes++;break}}void 0===Y&&(Y=new THREE.WebGLProgram(J,M,d,P),hb.push(Y),J.info.memory.programs=hb.length);d.program=Y;var ob=Y.attributes;if(d.morphTargets){d.numSupportedMorphTargets=0;for(var ma,pa="morphTarget",la=0;la<J.maxMorphTargets;la++)ma=pa+la,0<=ob[ma]&&d.numSupportedMorphTargets++}if(d.morphNormals)for(d.numSupportedMorphNormals=0,pa="morphNormal",la=0;la<J.maxMorphNormals;la++)ma=
-pa+la,0<=ob[ma]&&d.numSupportedMorphNormals++;d.uniformsList=[];for(var Jb in d.__webglShader.uniforms){var za=d.program.uniforms[Jb];za&&d.uniformsList.push([d.__webglShader.uniforms[Jb],za])}d.needsUpdate=!1}d.morphTargets&&!e.__webglMorphTargetInfluences&&(e.__webglMorphTargetInfluences=new Float32Array(J.maxMorphTargets));var aa=!1,$=!1,Z=!1,yb=d.program,qa=yb.uniforms,L=d.__webglShader.uniforms;yb.id!==tc&&(l.useProgram(yb.program),tc=yb.id,Z=$=aa=!0);d.id!==Kb&&(-1===Kb&&(Z=!0),Kb=d.id,$=!0);
-if(aa||a!==ec)l.uniformMatrix4fv(qa.projectionMatrix,!1,a.projectionMatrix.elements),Fa&&l.uniform1f(qa.logDepthBufFC,2/(Math.log(a.far+1)/Math.LN2)),a!==ec&&(ec=a),(d instanceof THREE.ShaderMaterial||d instanceof THREE.MeshPhongMaterial||d.envMap)&&null!==qa.cameraPosition&&(Na.setFromMatrixPosition(a.matrixWorld),l.uniform3f(qa.cameraPosition,Na.x,Na.y,Na.z)),(d instanceof THREE.MeshPhongMaterial||d instanceof THREE.MeshLambertMaterial||d instanceof THREE.ShaderMaterial||d.skinning)&&null!==qa.viewMatrix&&
-l.uniformMatrix4fv(qa.viewMatrix,!1,a.matrixWorldInverse.elements);if(d.skinning)if(e.bindMatrix&&null!==qa.bindMatrix&&l.uniformMatrix4fv(qa.bindMatrix,!1,e.bindMatrix.elements),e.bindMatrixInverse&&null!==qa.bindMatrixInverse&&l.uniformMatrix4fv(qa.bindMatrixInverse,!1,e.bindMatrixInverse.elements),jc&&e.skeleton&&e.skeleton.useVertexTexture){if(null!==qa.boneTexture){var Ib=K();l.uniform1i(qa.boneTexture,Ib);J.setTexture(e.skeleton.boneTexture,Ib)}null!==qa.boneTextureWidth&&l.uniform1i(qa.boneTextureWidth,
-e.skeleton.boneTextureWidth);null!==qa.boneTextureHeight&&l.uniform1i(qa.boneTextureHeight,e.skeleton.boneTextureHeight)}else e.skeleton&&e.skeleton.boneMatrices&&null!==qa.boneGlobalMatrices&&l.uniformMatrix4fv(qa.boneGlobalMatrices,!1,e.skeleton.boneMatrices);if($){c&&d.fog&&(L.fogColor.value=c.color,c instanceof THREE.Fog?(L.fogNear.value=c.near,L.fogFar.value=c.far):c instanceof THREE.FogExp2&&(L.fogDensity.value=c.density));if(d instanceof THREE.MeshPhongMaterial||d instanceof THREE.MeshLambertMaterial||
-d.lights){if(fc){var Z=!0,na,Ra,ia,ya=0,Ga=0,Oa=0,Ba,zb,Ab,Ha,Bb,Aa,va=Mc,Cb=va.directional.colors,ib=va.directional.positions,Qb=va.point.colors,Ma=va.point.positions,xb=va.point.distances,Ya=va.spot.colors,Za=va.spot.positions,Mb=va.spot.distances,Rb=va.spot.directions,db=va.spot.anglesCos,eb=va.spot.exponents,qb=va.hemi.skyColors,rb=va.hemi.groundColors,Db=va.hemi.positions,Sa=0,Ca=0,Pa=0,Ka=0,ja=0,ta=0,I=0,Ia=0,Qa=0,sb=0,fb=0,Ta=0;na=0;for(Ra=b.length;na<Ra;na++)ia=b[na],ia.onlyShadow||(Ba=ia.color,
-Ha=ia.intensity,Aa=ia.distance,ia instanceof THREE.AmbientLight?ia.visible&&(J.gammaInput?(ya+=Ba.r*Ba.r,Ga+=Ba.g*Ba.g,Oa+=Ba.b*Ba.b):(ya+=Ba.r,Ga+=Ba.g,Oa+=Ba.b)):ia instanceof THREE.DirectionalLight?(ja+=1,ia.visible&&(sa.setFromMatrixPosition(ia.matrixWorld),Na.setFromMatrixPosition(ia.target.matrixWorld),sa.sub(Na),sa.normalize(),Qa=3*Sa,ib[Qa]=sa.x,ib[Qa+1]=sa.y,ib[Qa+2]=sa.z,J.gammaInput?D(Cb,Qa,Ba,Ha*Ha):E(Cb,Qa,Ba,Ha),Sa+=1)):ia instanceof THREE.PointLight?(ta+=1,ia.visible&&(sb=3*Ca,J.gammaInput?
-D(Qb,sb,Ba,Ha*Ha):E(Qb,sb,Ba,Ha),Na.setFromMatrixPosition(ia.matrixWorld),Ma[sb]=Na.x,Ma[sb+1]=Na.y,Ma[sb+2]=Na.z,xb[Ca]=Aa,Ca+=1)):ia instanceof THREE.SpotLight?(I+=1,ia.visible&&(fb=3*Pa,J.gammaInput?D(Ya,fb,Ba,Ha*Ha):E(Ya,fb,Ba,Ha),sa.setFromMatrixPosition(ia.matrixWorld),Za[fb]=sa.x,Za[fb+1]=sa.y,Za[fb+2]=sa.z,Mb[Pa]=Aa,Na.setFromMatrixPosition(ia.target.matrixWorld),sa.sub(Na),sa.normalize(),Rb[fb]=sa.x,Rb[fb+1]=sa.y,Rb[fb+2]=sa.z,db[Pa]=Math.cos(ia.angle),eb[Pa]=ia.exponent,Pa+=1)):ia instanceof
-THREE.HemisphereLight&&(Ia+=1,ia.visible&&(sa.setFromMatrixPosition(ia.matrixWorld),sa.normalize(),Ta=3*Ka,Db[Ta]=sa.x,Db[Ta+1]=sa.y,Db[Ta+2]=sa.z,zb=ia.color,Ab=ia.groundColor,J.gammaInput?(Bb=Ha*Ha,D(qb,Ta,zb,Bb),D(rb,Ta,Ab,Bb)):(E(qb,Ta,zb,Ha),E(rb,Ta,Ab,Ha)),Ka+=1)));na=3*Sa;for(Ra=Math.max(Cb.length,3*ja);na<Ra;na++)Cb[na]=0;na=3*Ca;for(Ra=Math.max(Qb.length,3*ta);na<Ra;na++)Qb[na]=0;na=3*Pa;for(Ra=Math.max(Ya.length,3*I);na<Ra;na++)Ya[na]=0;na=3*Ka;for(Ra=Math.max(qb.length,3*Ia);na<Ra;na++)qb[na]=
-0;na=3*Ka;for(Ra=Math.max(rb.length,3*Ia);na<Ra;na++)rb[na]=0;va.directional.length=Sa;va.point.length=Ca;va.spot.length=Pa;va.hemi.length=Ka;va.ambient[0]=ya;va.ambient[1]=Ga;va.ambient[2]=Oa;fc=!1}if(Z){var ra=Mc;L.ambientLightColor.value=ra.ambient;L.directionalLightColor.value=ra.directional.colors;L.directionalLightDirection.value=ra.directional.positions;L.pointLightColor.value=ra.point.colors;L.pointLightPosition.value=ra.point.positions;L.pointLightDistance.value=ra.point.distances;L.spotLightColor.value=
-ra.spot.colors;L.spotLightPosition.value=ra.spot.positions;L.spotLightDistance.value=ra.spot.distances;L.spotLightDirection.value=ra.spot.directions;L.spotLightAngleCos.value=ra.spot.anglesCos;L.spotLightExponent.value=ra.spot.exponents;L.hemisphereLightSkyColor.value=ra.hemi.skyColors;L.hemisphereLightGroundColor.value=ra.hemi.groundColors;L.hemisphereLightDirection.value=ra.hemi.positions;w(L,!0)}else w(L,!1)}if(d instanceof THREE.MeshBasicMaterial||d instanceof THREE.MeshLambertMaterial||d instanceof
-THREE.MeshPhongMaterial){L.opacity.value=d.opacity;J.gammaInput?L.diffuse.value.copyGammaToLinear(d.color):L.diffuse.value=d.color;L.map.value=d.map;L.lightMap.value=d.lightMap;L.specularMap.value=d.specularMap;L.alphaMap.value=d.alphaMap;d.bumpMap&&(L.bumpMap.value=d.bumpMap,L.bumpScale.value=d.bumpScale);d.normalMap&&(L.normalMap.value=d.normalMap,L.normalScale.value.copy(d.normalScale));var La;d.map?La=d.map:d.specularMap?La=d.specularMap:d.normalMap?La=d.normalMap:d.bumpMap?La=d.bumpMap:d.alphaMap&&
-(La=d.alphaMap);if(void 0!==La){var Ua=La.offset,Va=La.repeat;L.offsetRepeat.value.set(Ua.x,Ua.y,Va.x,Va.y)}L.envMap.value=d.envMap;L.flipEnvMap.value=d.envMap instanceof THREE.WebGLRenderTargetCube?1:-1;L.reflectivity.value=d.reflectivity;L.refractionRatio.value=d.refractionRatio;L.combine.value=d.combine;L.useRefract.value=d.envMap&&d.envMap.mapping instanceof THREE.CubeRefractionMapping}d instanceof THREE.LineBasicMaterial?(L.diffuse.value=d.color,L.opacity.value=d.opacity):d instanceof THREE.LineDashedMaterial?
-(L.diffuse.value=d.color,L.opacity.value=d.opacity,L.dashSize.value=d.dashSize,L.totalSize.value=d.dashSize+d.gapSize,L.scale.value=d.scale):d instanceof THREE.PointCloudMaterial?(L.psColor.value=d.color,L.opacity.value=d.opacity,L.size.value=d.size,L.scale.value=O.height/2,L.map.value=d.map):d instanceof THREE.MeshPhongMaterial?(L.shininess.value=d.shininess,J.gammaInput?(L.ambient.value.copyGammaToLinear(d.ambient),L.emissive.value.copyGammaToLinear(d.emissive),L.specular.value.copyGammaToLinear(d.specular)):
-(L.ambient.value=d.ambient,L.emissive.value=d.emissive,L.specular.value=d.specular),d.wrapAround&&L.wrapRGB.value.copy(d.wrapRGB)):d instanceof THREE.MeshLambertMaterial?(J.gammaInput?(L.ambient.value.copyGammaToLinear(d.ambient),L.emissive.value.copyGammaToLinear(d.emissive)):(L.ambient.value=d.ambient,L.emissive.value=d.emissive),d.wrapAround&&L.wrapRGB.value.copy(d.wrapRGB)):d instanceof THREE.MeshDepthMaterial?(L.mNear.value=a.near,L.mFar.value=a.far,L.opacity.value=d.opacity):d instanceof THREE.MeshNormalMaterial&&
-(L.opacity.value=d.opacity);if(e.receiveShadow&&!d._shadowPass&&L.shadowMatrix)for(var Eb=0,pb=0,Nb=b.length;pb<Nb;pb++){var z=b[pb];z.castShadow&&(z instanceof THREE.SpotLight||z instanceof THREE.DirectionalLight&&!z.shadowCascade)&&(L.shadowMap.value[Eb]=z.shadowMap,L.shadowMapSize.value[Eb]=z.shadowMapSize,L.shadowMatrix.value[Eb]=z.shadowMatrix,L.shadowDarkness.value[Eb]=z.shadowDarkness,L.shadowBias.value[Eb]=z.shadowBias,Eb++)}for(var Sb=d.uniformsList,Ja,wa,$a,nb=0,Pb=Sb.length;nb<Pb;nb++){var da=
-Sb[nb][0];if(!1!==da.needsUpdate){var wb=da.type,U=da.value,fa=Sb[nb][1];switch(wb){case "1i":l.uniform1i(fa,U);break;case "1f":l.uniform1f(fa,U);break;case "2f":l.uniform2f(fa,U[0],U[1]);break;case "3f":l.uniform3f(fa,U[0],U[1],U[2]);break;case "4f":l.uniform4f(fa,U[0],U[1],U[2],U[3]);break;case "1iv":l.uniform1iv(fa,U);break;case "3iv":l.uniform3iv(fa,U);break;case "1fv":l.uniform1fv(fa,U);break;case "2fv":l.uniform2fv(fa,U);break;case "3fv":l.uniform3fv(fa,U);break;case "4fv":l.uniform4fv(fa,U);
-break;case "Matrix3fv":l.uniformMatrix3fv(fa,!1,U);break;case "Matrix4fv":l.uniformMatrix4fv(fa,!1,U);break;case "i":l.uniform1i(fa,U);break;case "f":l.uniform1f(fa,U);break;case "v2":l.uniform2f(fa,U.x,U.y);break;case "v3":l.uniform3f(fa,U.x,U.y,U.z);break;case "v4":l.uniform4f(fa,U.x,U.y,U.z,U.w);break;case "c":l.uniform3f(fa,U.r,U.g,U.b);break;case "iv1":l.uniform1iv(fa,U);break;case "iv":l.uniform3iv(fa,U);break;case "fv1":l.uniform1fv(fa,U);break;case "fv":l.uniform3fv(fa,U);break;case "v2v":void 0===
-da._array&&(da._array=new Float32Array(2*U.length));for(var N=0,xa=U.length;N<xa;N++)$a=2*N,da._array[$a]=U[N].x,da._array[$a+1]=U[N].y;l.uniform2fv(fa,da._array);break;case "v3v":void 0===da._array&&(da._array=new Float32Array(3*U.length));N=0;for(xa=U.length;N<xa;N++)$a=3*N,da._array[$a]=U[N].x,da._array[$a+1]=U[N].y,da._array[$a+2]=U[N].z;l.uniform3fv(fa,da._array);break;case "v4v":void 0===da._array&&(da._array=new Float32Array(4*U.length));N=0;for(xa=U.length;N<xa;N++)$a=4*N,da._array[$a]=U[N].x,
-da._array[$a+1]=U[N].y,da._array[$a+2]=U[N].z,da._array[$a+3]=U[N].w;l.uniform4fv(fa,da._array);break;case "m3":l.uniformMatrix3fv(fa,!1,U.elements);break;case "m3v":void 0===da._array&&(da._array=new Float32Array(9*U.length));N=0;for(xa=U.length;N<xa;N++)U[N].flattenToArrayOffset(da._array,9*N);l.uniformMatrix3fv(fa,!1,da._array);break;case "m4":l.uniformMatrix4fv(fa,!1,U.elements);break;case "m4v":void 0===da._array&&(da._array=new Float32Array(16*U.length));N=0;for(xa=U.length;N<xa;N++)U[N].flattenToArrayOffset(da._array,
-16*N);l.uniformMatrix4fv(fa,!1,da._array);break;case "t":Ja=U;wa=K();l.uniform1i(fa,wa);if(!Ja)continue;if(Ja instanceof THREE.CubeTexture||Ja.image instanceof Array&&6===Ja.image.length){var ua=Ja,Lb=wa;if(6===ua.image.length)if(ua.needsUpdate){ua.image.__webglTextureCube||(ua.addEventListener("dispose",gc),ua.image.__webglTextureCube=l.createTexture(),J.info.memory.textures++);l.activeTexture(l.TEXTURE0+Lb);l.bindTexture(l.TEXTURE_CUBE_MAP,ua.image.__webglTextureCube);l.pixelStorei(l.UNPACK_FLIP_Y_WEBGL,
-ua.flipY);for(var Ob=ua instanceof THREE.CompressedTexture,Tb=ua.image[0]instanceof THREE.DataTexture,kb=[],Da=0;6>Da;Da++)kb[Da]=!J.autoScaleCubemaps||Ob||Tb?Tb?ua.image[Da].image:ua.image[Da]:R(ua.image[Da],$c);var ka=kb[0],Zb=THREE.Math.isPowerOfTwo(ka.width)&&THREE.Math.isPowerOfTwo(ka.height),ab=Q(ua.format),Fb=Q(ua.type);F(l.TEXTURE_CUBE_MAP,ua,Zb);for(Da=0;6>Da;Da++)if(Ob)for(var gb,$b=kb[Da].mipmaps,ga=0,Xb=$b.length;ga<Xb;ga++)gb=$b[ga],ua.format!==THREE.RGBAFormat&&ua.format!==THREE.RGBFormat?
--1<Nc().indexOf(ab)?l.compressedTexImage2D(l.TEXTURE_CUBE_MAP_POSITIVE_X+Da,ga,ab,gb.width,gb.height,0,gb.data):console.warn("Attempt to load unsupported compressed texture format"):l.texImage2D(l.TEXTURE_CUBE_MAP_POSITIVE_X+Da,ga,ab,gb.width,gb.height,0,ab,Fb,gb.data);else Tb?l.texImage2D(l.TEXTURE_CUBE_MAP_POSITIVE_X+Da,0,ab,kb[Da].width,kb[Da].height,0,ab,Fb,kb[Da].data):l.texImage2D(l.TEXTURE_CUBE_MAP_POSITIVE_X+Da,0,ab,ab,Fb,kb[Da]);ua.generateMipmaps&&Zb&&l.generateMipmap(l.TEXTURE_CUBE_MAP);
-ua.needsUpdate=!1;if(ua.onUpdate)ua.onUpdate()}else l.activeTexture(l.TEXTURE0+Lb),l.bindTexture(l.TEXTURE_CUBE_MAP,ua.image.__webglTextureCube)}else if(Ja instanceof THREE.WebGLRenderTargetCube){var Yb=Ja;l.activeTexture(l.TEXTURE0+wa);l.bindTexture(l.TEXTURE_CUBE_MAP,Yb.__webglTexture)}else J.setTexture(Ja,wa);break;case "tv":void 0===da._array&&(da._array=[]);N=0;for(xa=da.value.length;N<xa;N++)da._array[N]=K();l.uniform1iv(fa,da._array);N=0;for(xa=da.value.length;N<xa;N++)Ja=da.value[N],wa=da._array[N],
-Ja&&J.setTexture(Ja,wa);break;default:console.warn("THREE.WebGLRenderer: Unknown uniform type: "+wb)}}}}l.uniformMatrix4fv(qa.modelViewMatrix,!1,e._modelViewMatrix.elements);qa.normalMatrix&&l.uniformMatrix3fv(qa.normalMatrix,!1,e._normalMatrix.elements);null!==qa.modelMatrix&&l.uniformMatrix4fv(qa.modelMatrix,!1,e.matrixWorld.elements);return yb}function w(a,b){a.ambientLightColor.needsUpdate=b;a.directionalLightColor.needsUpdate=b;a.directionalLightDirection.needsUpdate=b;a.pointLightColor.needsUpdate=
-b;a.pointLightPosition.needsUpdate=b;a.pointLightDistance.needsUpdate=b;a.spotLightColor.needsUpdate=b;a.spotLightPosition.needsUpdate=b;a.spotLightDistance.needsUpdate=b;a.spotLightDirection.needsUpdate=b;a.spotLightAngleCos.needsUpdate=b;a.spotLightExponent.needsUpdate=b;a.hemisphereLightSkyColor.needsUpdate=b;a.hemisphereLightGroundColor.needsUpdate=b;a.hemisphereLightDirection.needsUpdate=b}function K(){var a=dc;a>=Oc&&console.warn("WebGLRenderer: trying to use "+a+" texture units while this GPU supports only "+
-Oc);dc+=1;return a}function x(a,b){a._modelViewMatrix.multiplyMatrices(b.matrixWorldInverse,a.matrixWorld);a._normalMatrix.getNormalMatrix(a._modelViewMatrix)}function D(a,b,c,d){a[b]=c.r*c.r*d;a[b+1]=c.g*c.g*d;a[b+2]=c.b*c.b*d}function E(a,b,c,d){a[b]=c.r*d;a[b+1]=c.g*d;a[b+2]=c.b*d}function A(a){a!==Pc&&(l.lineWidth(a),Pc=a)}function B(a,b,c){Qc!==a&&(a?l.enable(l.POLYGON_OFFSET_FILL):l.disable(l.POLYGON_OFFSET_FILL),Qc=a);!a||Rc===b&&Sc===c||(l.polygonOffset(b,c),Rc=b,Sc=c)}function F(a,b,c){c?
-(l.texParameteri(a,l.TEXTURE_WRAP_S,Q(b.wrapS)),l.texParameteri(a,l.TEXTURE_WRAP_T,Q(b.wrapT)),l.texParameteri(a,l.TEXTURE_MAG_FILTER,Q(b.magFilter)),l.texParameteri(a,l.TEXTURE_MIN_FILTER,Q(b.minFilter))):(l.texParameteri(a,l.TEXTURE_WRAP_S,l.CLAMP_TO_EDGE),l.texParameteri(a,l.TEXTURE_WRAP_T,l.CLAMP_TO_EDGE),l.texParameteri(a,l.TEXTURE_MAG_FILTER,T(b.magFilter)),l.texParameteri(a,l.TEXTURE_MIN_FILTER,T(b.minFilter)));(c=pa.get("EXT_texture_filter_anisotropic"))&&b.type!==THREE.FloatType&&(1<b.anisotropy||
-b.__oldAnisotropy)&&(l.texParameterf(a,c.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(b.anisotropy,J.getMaxAnisotropy())),b.__oldAnisotropy=b.anisotropy)}function R(a,b){if(a.width>b||a.height>b){var c=b/Math.max(a.width,a.height),d=document.createElement("canvas");d.width=Math.floor(a.width*c);d.height=Math.floor(a.height*c);d.getContext("2d").drawImage(a,0,0,a.width,a.height,0,0,d.width,d.height);console.log("THREE.WebGLRenderer:",a,"is too big ("+a.width+"x"+a.height+"). Resized to "+d.width+"x"+d.height+
-".");return d}return a}function H(a,b){l.bindRenderbuffer(l.RENDERBUFFER,a);b.depthBuffer&&!b.stencilBuffer?(l.renderbufferStorage(l.RENDERBUFFER,l.DEPTH_COMPONENT16,b.width,b.height),l.framebufferRenderbuffer(l.FRAMEBUFFER,l.DEPTH_ATTACHMENT,l.RENDERBUFFER,a)):b.depthBuffer&&b.stencilBuffer?(l.renderbufferStorage(l.RENDERBUFFER,l.DEPTH_STENCIL,b.width,b.height),l.framebufferRenderbuffer(l.FRAMEBUFFER,l.DEPTH_STENCIL_ATTACHMENT,l.RENDERBUFFER,a)):l.renderbufferStorage(l.RENDERBUFFER,l.RGBA4,b.width,
-b.height)}function C(a){a instanceof THREE.WebGLRenderTargetCube?(l.bindTexture(l.TEXTURE_CUBE_MAP,a.__webglTexture),l.generateMipmap(l.TEXTURE_CUBE_MAP),l.bindTexture(l.TEXTURE_CUBE_MAP,null)):(l.bindTexture(l.TEXTURE_2D,a.__webglTexture),l.generateMipmap(l.TEXTURE_2D),l.bindTexture(l.TEXTURE_2D,null))}function T(a){return a===THREE.NearestFilter||a===THREE.NearestMipMapNearestFilter||a===THREE.NearestMipMapLinearFilter?l.NEAREST:l.LINEAR}function Q(a){var b;if(a===THREE.RepeatWrapping)return l.REPEAT;
-if(a===THREE.ClampToEdgeWrapping)return l.CLAMP_TO_EDGE;if(a===THREE.MirroredRepeatWrapping)return l.MIRRORED_REPEAT;if(a===THREE.NearestFilter)return l.NEAREST;if(a===THREE.NearestMipMapNearestFilter)return l.NEAREST_MIPMAP_NEAREST;if(a===THREE.NearestMipMapLinearFilter)return l.NEAREST_MIPMAP_LINEAR;if(a===THREE.LinearFilter)return l.LINEAR;if(a===THREE.LinearMipMapNearestFilter)return l.LINEAR_MIPMAP_NEAREST;if(a===THREE.LinearMipMapLinearFilter)return l.LINEAR_MIPMAP_LINEAR;if(a===THREE.UnsignedByteType)return l.UNSIGNED_BYTE;
-if(a===THREE.UnsignedShort4444Type)return l.UNSIGNED_SHORT_4_4_4_4;if(a===THREE.UnsignedShort5551Type)return l.UNSIGNED_SHORT_5_5_5_1;if(a===THREE.UnsignedShort565Type)return l.UNSIGNED_SHORT_5_6_5;if(a===THREE.ByteType)return l.BYTE;if(a===THREE.ShortType)return l.SHORT;if(a===THREE.UnsignedShortType)return l.UNSIGNED_SHORT;if(a===THREE.IntType)return l.INT;if(a===THREE.UnsignedIntType)return l.UNSIGNED_INT;if(a===THREE.FloatType)return l.FLOAT;if(a===THREE.AlphaFormat)return l.ALPHA;if(a===THREE.RGBFormat)return l.RGB;
-if(a===THREE.RGBAFormat)return l.RGBA;if(a===THREE.LuminanceFormat)return l.LUMINANCE;if(a===THREE.LuminanceAlphaFormat)return l.LUMINANCE_ALPHA;if(a===THREE.AddEquation)return l.FUNC_ADD;if(a===THREE.SubtractEquation)return l.FUNC_SUBTRACT;if(a===THREE.ReverseSubtractEquation)return l.FUNC_REVERSE_SUBTRACT;if(a===THREE.ZeroFactor)return l.ZERO;if(a===THREE.OneFactor)return l.ONE;if(a===THREE.SrcColorFactor)return l.SRC_COLOR;if(a===THREE.OneMinusSrcColorFactor)return l.ONE_MINUS_SRC_COLOR;if(a===
-THREE.SrcAlphaFactor)return l.SRC_ALPHA;if(a===THREE.OneMinusSrcAlphaFactor)return l.ONE_MINUS_SRC_ALPHA;if(a===THREE.DstAlphaFactor)return l.DST_ALPHA;if(a===THREE.OneMinusDstAlphaFactor)return l.ONE_MINUS_DST_ALPHA;if(a===THREE.DstColorFactor)return l.DST_COLOR;if(a===THREE.OneMinusDstColorFactor)return l.ONE_MINUS_DST_COLOR;if(a===THREE.SrcAlphaSaturateFactor)return l.SRC_ALPHA_SATURATE;b=pa.get("WEBGL_compressed_texture_s3tc");if(null!==b){if(a===THREE.RGB_S3TC_DXT1_Format)return b.COMPRESSED_RGB_S3TC_DXT1_EXT;
-if(a===THREE.RGBA_S3TC_DXT1_Format)return b.COMPRESSED_RGBA_S3TC_DXT1_EXT;if(a===THREE.RGBA_S3TC_DXT3_Format)return b.COMPRESSED_RGBA_S3TC_DXT3_EXT;if(a===THREE.RGBA_S3TC_DXT5_Format)return b.COMPRESSED_RGBA_S3TC_DXT5_EXT}b=pa.get("WEBGL_compressed_texture_pvrtc");if(null!==b){if(a===THREE.RGB_PVRTC_4BPPV1_Format)return b.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;if(a===THREE.RGB_PVRTC_2BPPV1_Format)return b.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;if(a===THREE.RGBA_PVRTC_4BPPV1_Format)return b.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
-if(a===THREE.RGBA_PVRTC_2BPPV1_Format)return b.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG}b=pa.get("EXT_blend_minmax");if(null!==b){if(a===THREE.MinEquation)return b.MIN_EXT;if(a===THREE.MaxEquation)return b.MAX_EXT}return 0}console.log("THREE.WebGLRenderer",THREE.REVISION);a=a||{};var O=void 0!==a.canvas?a.canvas:document.createElement("canvas"),S=void 0!==a.context?a.context:null,X=void 0!==a.precision?a.precision:"highp",Y=void 0!==a.alpha?a.alpha:!1,la=void 0!==a.depth?a.depth:!0,ma=void 0!==a.stencil?
-a.stencil:!0,ya=void 0!==a.antialias?a.antialias:!1,P=void 0!==a.premultipliedAlpha?a.premultipliedAlpha:!0,Ga=void 0!==a.preserveDrawingBuffer?a.preserveDrawingBuffer:!1,Fa=void 0!==a.logarithmicDepthBuffer?a.logarithmicDepthBuffer:!1,za=new THREE.Color(0),bb=0,cb=[],ob={},jb=[],Jb=[],Ib=[],yb=[],Ra=[];this.domElement=O;this.context=null;this.devicePixelRatio=void 0!==a.devicePixelRatio?a.devicePixelRatio:void 0!==self.devicePixelRatio?self.devicePixelRatio:1;this.sortObjects=this.autoClearStencil=
-this.autoClearDepth=this.autoClearColor=this.autoClear=!0;this.shadowMapEnabled=this.gammaOutput=this.gammaInput=!1;this.shadowMapType=THREE.PCFShadowMap;this.shadowMapCullFace=THREE.CullFaceFront;this.shadowMapCascade=this.shadowMapDebug=!1;this.maxMorphTargets=8;this.maxMorphNormals=4;this.autoScaleCubemaps=!0;this.info={memory:{programs:0,geometries:0,textures:0},render:{calls:0,vertices:0,faces:0,points:0}};var J=this,hb=[],tc=null,Tc=null,Kb=-1,Oa=-1,ec=null,dc=0,Lb=-1,Mb=-1,pb=-1,Nb=-1,Ob=-1,
-Xb=-1,Yb=-1,nb=-1,Qc=null,Rc=null,Sc=null,Pc=null,Pb=0,kc=0,lc=O.width,mc=O.height,Uc=0,Vc=0,wb=new Uint8Array(16),ib=new Uint8Array(16),Ec=new THREE.Frustum,Ac=new THREE.Matrix4,Gc=new THREE.Matrix4,Na=new THREE.Vector3,sa=new THREE.Vector3,fc=!0,Mc={ambient:[0,0,0],directional:{length:0,colors:[],positions:[]},point:{length:0,colors:[],positions:[],distances:[]},spot:{length:0,colors:[],positions:[],distances:[],directions:[],anglesCos:[],exponents:[]},hemi:{length:0,skyColors:[],groundColors:[],
-positions:[]}},l;try{var Wc={alpha:Y,depth:la,stencil:ma,antialias:ya,premultipliedAlpha:P,preserveDrawingBuffer:Ga};l=S||O.getContext("webgl",Wc)||O.getContext("experimental-webgl",Wc);if(null===l){if(null!==O.getContext("webgl"))throw"Error creating WebGL context with your selected attributes.";throw"Error creating WebGL context.";}}catch(ad){console.error(ad)}void 0===l.getShaderPrecisionFormat&&(l.getShaderPrecisionFormat=function(){return{rangeMin:1,rangeMax:1,precision:1}});var pa=new THREE.WebGLExtensions(l);
-pa.get("OES_texture_float");pa.get("OES_texture_float_linear");pa.get("OES_standard_derivatives");Fa&&pa.get("EXT_frag_depth");l.clearColor(0,0,0,1);l.clearDepth(1);l.clearStencil(0);l.enable(l.DEPTH_TEST);l.depthFunc(l.LEQUAL);l.frontFace(l.CCW);l.cullFace(l.BACK);l.enable(l.CULL_FACE);l.enable(l.BLEND);l.blendEquation(l.FUNC_ADD);l.blendFunc(l.SRC_ALPHA,l.ONE_MINUS_SRC_ALPHA);l.viewport(Pb,kc,lc,mc);l.clearColor(za.r,za.g,za.b,bb);this.context=l;var Oc=l.getParameter(l.MAX_TEXTURE_IMAGE_UNITS),
-bd=l.getParameter(l.MAX_VERTEX_TEXTURE_IMAGE_UNITS),cd=l.getParameter(l.MAX_TEXTURE_SIZE),$c=l.getParameter(l.MAX_CUBE_MAP_TEXTURE_SIZE),sc=0<bd,jc=sc&&pa.get("OES_texture_float"),dd=l.getShaderPrecisionFormat(l.VERTEX_SHADER,l.HIGH_FLOAT),ed=l.getShaderPrecisionFormat(l.VERTEX_SHADER,l.MEDIUM_FLOAT);l.getShaderPrecisionFormat(l.VERTEX_SHADER,l.LOW_FLOAT);var fd=l.getShaderPrecisionFormat(l.FRAGMENT_SHADER,l.HIGH_FLOAT),gd=l.getShaderPrecisionFormat(l.FRAGMENT_SHADER,l.MEDIUM_FLOAT);l.getShaderPrecisionFormat(l.FRAGMENT_SHADER,
-l.LOW_FLOAT);var Nc=function(){var a;return function(){if(void 0!==a)return a;a=[];if(pa.get("WEBGL_compressed_texture_pvrtc")||pa.get("WEBGL_compressed_texture_s3tc"))for(var b=l.getParameter(l.COMPRESSED_TEXTURE_FORMATS),c=0;c<b.length;c++)a.push(b[c]);return a}}(),hd=0<dd.precision&&0<fd.precision,Xc=0<ed.precision&&0<gd.precision;"highp"!==X||hd||(Xc?(X="mediump",console.warn("THREE.WebGLRenderer: highp not supported, using mediump.")):(X="lowp",console.warn("THREE.WebGLRenderer: highp and mediump not supported, using lowp.")));
-"mediump"!==X||Xc||(X="lowp",console.warn("THREE.WebGLRenderer: mediump not supported, using lowp."));var id=new THREE.ShadowMapPlugin(this,cb,ob,jb),jd=new THREE.SpritePlugin(this,yb),kd=new THREE.LensFlarePlugin(this,Ra);this.getContext=function(){return l};this.supportsVertexTextures=function(){return sc};this.supportsFloatTextures=function(){return pa.get("OES_texture_float")};this.supportsStandardDerivatives=function(){return pa.get("OES_standard_derivatives")};this.supportsCompressedTextureS3TC=
-function(){return pa.get("WEBGL_compressed_texture_s3tc")};this.supportsCompressedTexturePVRTC=function(){return pa.get("WEBGL_compressed_texture_pvrtc")};this.supportsBlendMinMax=function(){return pa.get("EXT_blend_minmax")};this.getMaxAnisotropy=function(){var a;return function(){if(void 0!==a)return a;var b=pa.get("EXT_texture_filter_anisotropic");return a=null!==b?l.getParameter(b.MAX_TEXTURE_MAX_ANISOTROPY_EXT):0}}();this.getPrecision=function(){return X};this.setSize=function(a,b,c){O.width=
-a*this.devicePixelRatio;O.height=b*this.devicePixelRatio;!1!==c&&(O.style.width=a+"px",O.style.height=b+"px");this.setViewport(0,0,a,b)};this.setViewport=function(a,b,c,d){Pb=a*this.devicePixelRatio;kc=b*this.devicePixelRatio;lc=c*this.devicePixelRatio;mc=d*this.devicePixelRatio;l.viewport(Pb,kc,lc,mc)};this.setScissor=function(a,b,c,d){l.scissor(a*this.devicePixelRatio,b*this.devicePixelRatio,c*this.devicePixelRatio,d*this.devicePixelRatio)};this.enableScissorTest=function(a){a?l.enable(l.SCISSOR_TEST):
-l.disable(l.SCISSOR_TEST)};this.setClearColor=function(a,b){za.set(a);bb=void 0!==b?b:1;l.clearColor(za.r,za.g,za.b,bb)};this.setClearColorHex=function(a,b){console.warn("THREE.WebGLRenderer: .setClearColorHex() is being removed. Use .setClearColor() instead.");this.setClearColor(a,b)};this.getClearColor=function(){return za};this.getClearAlpha=function(){return bb};this.clear=function(a,b,c){var d=0;if(void 0===a||a)d|=l.COLOR_BUFFER_BIT;if(void 0===b||b)d|=l.DEPTH_BUFFER_BIT;if(void 0===c||c)d|=
-l.STENCIL_BUFFER_BIT;l.clear(d)};this.clearColor=function(){l.clear(l.COLOR_BUFFER_BIT)};this.clearDepth=function(){l.clear(l.DEPTH_BUFFER_BIT)};this.clearStencil=function(){l.clear(l.STENCIL_BUFFER_BIT)};this.clearTarget=function(a,b,c,d){this.setRenderTarget(a);this.clear(b,c,d)};this.resetGLState=function(){ec=tc=null;Kb=Oa=Mb=Lb=nb=Yb=pb=-1;fc=!0};var Hc=function(a){a.target.traverse(function(a){a.removeEventListener("remove",Hc);if(a instanceof THREE.Mesh||a instanceof THREE.PointCloud||a instanceof
-THREE.Line)delete ob[a.id];else if(a instanceof THREE.ImmediateRenderObject||a.immediateRenderCallback)for(var b=jb,c=b.length-1;0<=c;c--)b[c].object===a&&b.splice(c,1);delete a.__webglInit;delete a._modelViewMatrix;delete a._normalMatrix;delete a.__webglActive})},Ic=function(a){a=a.target;a.removeEventListener("dispose",Ic);delete a.__webglInit;if(a instanceof THREE.BufferGeometry){for(var b in a.attributes){var c=a.attributes[b];void 0!==c.buffer&&(l.deleteBuffer(c.buffer),delete c.buffer)}J.info.memory.geometries--}else if(b=
-xb[a.id],void 0!==b){for(var c=0,d=b.length;c<d;c++){var e=b[c];if(void 0!==e.numMorphTargets){for(var f=0,g=e.numMorphTargets;f<g;f++)l.deleteBuffer(e.__webglMorphTargetsBuffers[f]);delete e.__webglMorphTargetsBuffers}if(void 0!==e.numMorphNormals){f=0;for(g=e.numMorphNormals;f<g;f++)l.deleteBuffer(e.__webglMorphNormalsBuffers[f]);delete e.__webglMorphNormalsBuffers}Yc(e)}delete xb[a.id]}else Yc(a);Oa=-1},gc=function(a){a=a.target;a.removeEventListener("dispose",gc);a.image&&a.image.__webglTextureCube?
-(l.deleteTexture(a.image.__webglTextureCube),delete a.image.__webglTextureCube):void 0!==a.__webglInit&&(l.deleteTexture(a.__webglTexture),delete a.__webglTexture,delete a.__webglInit);J.info.memory.textures--},Zc=function(a){a=a.target;a.removeEventListener("dispose",Zc);if(a&&void 0!==a.__webglTexture){l.deleteTexture(a.__webglTexture);delete a.__webglTexture;if(a instanceof THREE.WebGLRenderTargetCube)for(var b=0;6>b;b++)l.deleteFramebuffer(a.__webglFramebuffer[b]),l.deleteRenderbuffer(a.__webglRenderbuffer[b]);
-else l.deleteFramebuffer(a.__webglFramebuffer),l.deleteRenderbuffer(a.__webglRenderbuffer);delete a.__webglFramebuffer;delete a.__webglRenderbuffer}J.info.memory.textures--},Dc=function(a){a=a.target;a.removeEventListener("dispose",Dc);Cc(a)},Yc=function(a){for(var b="__webglVertexBuffer __webglNormalBuffer __webglTangentBuffer __webglColorBuffer __webglUVBuffer __webglUV2Buffer __webglSkinIndicesBuffer __webglSkinWeightsBuffer __webglFaceBuffer __webglLineBuffer __webglLineDistanceBuffer".split(" "),
-c=0,d=b.length;c<d;c++){var e=b[c];void 0!==a[e]&&(l.deleteBuffer(a[e]),delete a[e])}if(void 0!==a.__webglCustomAttributesList){for(e in a.__webglCustomAttributesList)l.deleteBuffer(a.__webglCustomAttributesList[e].buffer);delete a.__webglCustomAttributesList}J.info.memory.geometries--},Cc=function(a){var b=a.program.program;if(void 0!==b){a.program=void 0;var c,d,e=!1;a=0;for(c=hb.length;a<c;a++)if(d=hb[a],d.program===b){d.usedTimes--;0===d.usedTimes&&(e=!0);break}if(!0===e){e=[];a=0;for(c=hb.length;a<
-c;a++)d=hb[a],d.program!==b&&e.push(d);hb=e;l.deleteProgram(b);J.info.memory.programs--}}};this.renderBufferImmediate=function(a,b,c){f();a.hasPositions&&!a.__webglVertexBuffer&&(a.__webglVertexBuffer=l.createBuffer());a.hasNormals&&!a.__webglNormalBuffer&&(a.__webglNormalBuffer=l.createBuffer());a.hasUvs&&!a.__webglUvBuffer&&(a.__webglUvBuffer=l.createBuffer());a.hasColors&&!a.__webglColorBuffer&&(a.__webglColorBuffer=l.createBuffer());a.hasPositions&&(l.bindBuffer(l.ARRAY_BUFFER,a.__webglVertexBuffer),
-l.bufferData(l.ARRAY_BUFFER,a.positionArray,l.DYNAMIC_DRAW),g(b.attributes.position),l.vertexAttribPointer(b.attributes.position,3,l.FLOAT,!1,0,0));if(a.hasNormals){l.bindBuffer(l.ARRAY_BUFFER,a.__webglNormalBuffer);if(c.shading===THREE.FlatShading){var d,e,k,m,n,p,r,q,t,s,v,u=3*a.count;for(v=0;v<u;v+=9)s=a.normalArray,d=s[v],e=s[v+1],k=s[v+2],m=s[v+3],p=s[v+4],q=s[v+5],n=s[v+6],r=s[v+7],t=s[v+8],d=(d+m+n)/3,e=(e+p+r)/3,k=(k+q+t)/3,s[v]=d,s[v+1]=e,s[v+2]=k,s[v+3]=d,s[v+4]=e,s[v+5]=k,s[v+6]=d,s[v+
-7]=e,s[v+8]=k}l.bufferData(l.ARRAY_BUFFER,a.normalArray,l.DYNAMIC_DRAW);g(b.attributes.normal);l.vertexAttribPointer(b.attributes.normal,3,l.FLOAT,!1,0,0)}a.hasUvs&&c.map&&(l.bindBuffer(l.ARRAY_BUFFER,a.__webglUvBuffer),l.bufferData(l.ARRAY_BUFFER,a.uvArray,l.DYNAMIC_DRAW),g(b.attributes.uv),l.vertexAttribPointer(b.attributes.uv,2,l.FLOAT,!1,0,0));a.hasColors&&c.vertexColors!==THREE.NoColors&&(l.bindBuffer(l.ARRAY_BUFFER,a.__webglColorBuffer),l.bufferData(l.ARRAY_BUFFER,a.colorArray,l.DYNAMIC_DRAW),
-g(b.attributes.color),l.vertexAttribPointer(b.attributes.color,3,l.FLOAT,!1,0,0));h();l.drawArrays(l.TRIANGLES,0,a.count);a.count=0};this.renderBufferDirect=function(a,b,c,d,g,h){if(!1!==d.visible)if(a=G(a,b,c,d,h),b=!1,c=16777215*g.id+2*a.id+(d.wireframe?1:0),c!==Oa&&(Oa=c,b=!0),b&&f(),h instanceof THREE.Mesh)if(h=!0===d.wireframe?l.LINES:l.TRIANGLES,c=g.attributes.index){var k,m;c.array instanceof Uint32Array&&pa.get("OES_element_index_uint")?(k=l.UNSIGNED_INT,m=4):(k=l.UNSIGNED_SHORT,m=2);var n=
-g.offsets;if(0===n.length)b&&(e(d,a,g,0),l.bindBuffer(l.ELEMENT_ARRAY_BUFFER,c.buffer)),l.drawElements(h,c.array.length,k,0),J.info.render.calls++,J.info.render.vertices+=c.array.length,J.info.render.faces+=c.array.length/3;else{b=!0;for(var p=0,r=n.length;p<r;p++){var q=n[p].index;b&&(e(d,a,g,q),l.bindBuffer(l.ELEMENT_ARRAY_BUFFER,c.buffer));l.drawElements(h,n[p].count,k,n[p].start*m);J.info.render.calls++;J.info.render.vertices+=n[p].count;J.info.render.faces+=n[p].count/3}}}else b&&e(d,a,g,0),
-d=g.attributes.position,l.drawArrays(h,0,d.array.length/3),J.info.render.calls++,J.info.render.vertices+=d.array.length/3,J.info.render.faces+=d.array.length/9;else if(h instanceof THREE.PointCloud)b&&e(d,a,g,0),d=g.attributes.position,l.drawArrays(l.POINTS,0,d.array.length/3),J.info.render.calls++,J.info.render.points+=d.array.length/3;else if(h instanceof THREE.Line)if(h=h.mode===THREE.LineStrip?l.LINE_STRIP:l.LINES,A(d.linewidth),c=g.attributes.index)if(c.array instanceof Uint32Array?(k=l.UNSIGNED_INT,
-m=4):(k=l.UNSIGNED_SHORT,m=2),n=g.offsets,0===n.length)b&&(e(d,a,g,0),l.bindBuffer(l.ELEMENT_ARRAY_BUFFER,c.buffer)),l.drawElements(h,c.array.length,k,0),J.info.render.calls++,J.info.render.vertices+=c.array.length;else for(1<n.length&&(b=!0),p=0,r=n.length;p<r;p++)q=n[p].index,b&&(e(d,a,g,q),l.bindBuffer(l.ELEMENT_ARRAY_BUFFER,c.buffer)),l.drawElements(h,n[p].count,k,n[p].start*m),J.info.render.calls++,J.info.render.vertices+=n[p].count;else b&&e(d,a,g,0),d=g.attributes.position,l.drawArrays(h,0,
-d.array.length/3),J.info.render.calls++,J.info.render.points+=d.array.length/3};this.renderBuffer=function(a,b,c,d,e,k){if(!1!==d.visible){c=G(a,b,c,d,k);b=c.attributes;a=!1;c=16777215*e.id+2*c.id+(d.wireframe?1:0);c!==Oa&&(Oa=c,a=!0);a&&f();if(!d.morphTargets&&0<=b.position)a&&(l.bindBuffer(l.ARRAY_BUFFER,e.__webglVertexBuffer),g(b.position),l.vertexAttribPointer(b.position,3,l.FLOAT,!1,0,0));else if(k.morphTargetBase){c=d.program.attributes;-1!==k.morphTargetBase&&0<=c.position?(l.bindBuffer(l.ARRAY_BUFFER,
-e.__webglMorphTargetsBuffers[k.morphTargetBase]),g(c.position),l.vertexAttribPointer(c.position,3,l.FLOAT,!1,0,0)):0<=c.position&&(l.bindBuffer(l.ARRAY_BUFFER,e.__webglVertexBuffer),g(c.position),l.vertexAttribPointer(c.position,3,l.FLOAT,!1,0,0));if(k.morphTargetForcedOrder.length)for(var m=0,n=k.morphTargetForcedOrder,r=k.morphTargetInfluences;m<d.numSupportedMorphTargets&&m<n.length;)0<=c["morphTarget"+m]&&(l.bindBuffer(l.ARRAY_BUFFER,e.__webglMorphTargetsBuffers[n[m]]),g(c["morphTarget"+m]),l.vertexAttribPointer(c["morphTarget"+
-m],3,l.FLOAT,!1,0,0)),0<=c["morphNormal"+m]&&d.morphNormals&&(l.bindBuffer(l.ARRAY_BUFFER,e.__webglMorphNormalsBuffers[n[m]]),g(c["morphNormal"+m]),l.vertexAttribPointer(c["morphNormal"+m],3,l.FLOAT,!1,0,0)),k.__webglMorphTargetInfluences[m]=r[n[m]],m++;else{var n=[],r=k.morphTargetInfluences,q,t=r.length;for(q=0;q<t;q++)m=r[q],0<m&&n.push([m,q]);n.length>d.numSupportedMorphTargets?(n.sort(p),n.length=d.numSupportedMorphTargets):n.length>d.numSupportedMorphNormals?n.sort(p):0===n.length&&n.push([0,
-0]);for(m=0;m<d.numSupportedMorphTargets;)n[m]?(q=n[m][1],0<=c["morphTarget"+m]&&(l.bindBuffer(l.ARRAY_BUFFER,e.__webglMorphTargetsBuffers[q]),g(c["morphTarget"+m]),l.vertexAttribPointer(c["morphTarget"+m],3,l.FLOAT,!1,0,0)),0<=c["morphNormal"+m]&&d.morphNormals&&(l.bindBuffer(l.ARRAY_BUFFER,e.__webglMorphNormalsBuffers[q]),g(c["morphNormal"+m]),l.vertexAttribPointer(c["morphNormal"+m],3,l.FLOAT,!1,0,0)),k.__webglMorphTargetInfluences[m]=r[q]):k.__webglMorphTargetInfluences[m]=0,m++}null!==d.program.uniforms.morphTargetInfluences&&
-l.uniform1fv(d.program.uniforms.morphTargetInfluences,k.__webglMorphTargetInfluences)}if(a){if(e.__webglCustomAttributesList)for(c=0,r=e.__webglCustomAttributesList.length;c<r;c++)n=e.__webglCustomAttributesList[c],0<=b[n.buffer.belongsToAttribute]&&(l.bindBuffer(l.ARRAY_BUFFER,n.buffer),g(b[n.buffer.belongsToAttribute]),l.vertexAttribPointer(b[n.buffer.belongsToAttribute],n.size,l.FLOAT,!1,0,0));0<=b.color&&(0<k.geometry.colors.length||0<k.geometry.faces.length?(l.bindBuffer(l.ARRAY_BUFFER,e.__webglColorBuffer),
-g(b.color),l.vertexAttribPointer(b.color,3,l.FLOAT,!1,0,0)):void 0!==d.defaultAttributeValues&&l.vertexAttrib3fv(b.color,d.defaultAttributeValues.color));0<=b.normal&&(l.bindBuffer(l.ARRAY_BUFFER,e.__webglNormalBuffer),g(b.normal),l.vertexAttribPointer(b.normal,3,l.FLOAT,!1,0,0));0<=b.tangent&&(l.bindBuffer(l.ARRAY_BUFFER,e.__webglTangentBuffer),g(b.tangent),l.vertexAttribPointer(b.tangent,4,l.FLOAT,!1,0,0));0<=b.uv&&(k.geometry.faceVertexUvs[0]?(l.bindBuffer(l.ARRAY_BUFFER,e.__webglUVBuffer),g(b.uv),
-l.vertexAttribPointer(b.uv,2,l.FLOAT,!1,0,0)):void 0!==d.defaultAttributeValues&&l.vertexAttrib2fv(b.uv,d.defaultAttributeValues.uv));0<=b.uv2&&(k.geometry.faceVertexUvs[1]?(l.bindBuffer(l.ARRAY_BUFFER,e.__webglUV2Buffer),g(b.uv2),l.vertexAttribPointer(b.uv2,2,l.FLOAT,!1,0,0)):void 0!==d.defaultAttributeValues&&l.vertexAttrib2fv(b.uv2,d.defaultAttributeValues.uv2));d.skinning&&0<=b.skinIndex&&0<=b.skinWeight&&(l.bindBuffer(l.ARRAY_BUFFER,e.__webglSkinIndicesBuffer),g(b.skinIndex),l.vertexAttribPointer(b.skinIndex,
-4,l.FLOAT,!1,0,0),l.bindBuffer(l.ARRAY_BUFFER,e.__webglSkinWeightsBuffer),g(b.skinWeight),l.vertexAttribPointer(b.skinWeight,4,l.FLOAT,!1,0,0));0<=b.lineDistance&&(l.bindBuffer(l.ARRAY_BUFFER,e.__webglLineDistanceBuffer),g(b.lineDistance),l.vertexAttribPointer(b.lineDistance,1,l.FLOAT,!1,0,0))}h();k instanceof THREE.Mesh?(k=e.__typeArray===Uint32Array?l.UNSIGNED_INT:l.UNSIGNED_SHORT,d.wireframe?(A(d.wireframeLinewidth),a&&l.bindBuffer(l.ELEMENT_ARRAY_BUFFER,e.__webglLineBuffer),l.drawElements(l.LINES,
-e.__webglLineCount,k,0)):(a&&l.bindBuffer(l.ELEMENT_ARRAY_BUFFER,e.__webglFaceBuffer),l.drawElements(l.TRIANGLES,e.__webglFaceCount,k,0)),J.info.render.calls++,J.info.render.vertices+=e.__webglFaceCount,J.info.render.faces+=e.__webglFaceCount/3):k instanceof THREE.Line?(k=k.mode===THREE.LineStrip?l.LINE_STRIP:l.LINES,A(d.linewidth),l.drawArrays(k,0,e.__webglLineCount),J.info.render.calls++):k instanceof THREE.PointCloud&&(l.drawArrays(l.POINTS,0,e.__webglParticleCount),J.info.render.calls++,J.info.render.points+=
-e.__webglParticleCount)}};this.render=function(a,b,c,d){if(!1===b instanceof THREE.Camera)console.error("THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.");else{var e=a.fog;Kb=Oa=-1;ec=null;fc=!0;!0===a.autoUpdate&&a.updateMatrixWorld();void 0===b.parent&&b.updateMatrixWorld();a.traverse(function(a){a instanceof THREE.SkinnedMesh&&a.skeleton.update()});b.matrixWorldInverse.getInverse(b.matrixWorld);Ac.multiplyMatrices(b.projectionMatrix,b.matrixWorldInverse);Ec.setFromMatrix(Ac);
-cb.length=0;Jb.length=0;Ib.length=0;yb.length=0;Ra.length=0;q(a,a);!0===J.sortObjects&&(Jb.sort(k),Ib.sort(n));id.render(a,b);J.info.render.calls=0;J.info.render.vertices=0;J.info.render.faces=0;J.info.render.points=0;this.setRenderTarget(c);(this.autoClear||d)&&this.clear(this.autoClearColor,this.autoClearDepth,this.autoClearStencil);d=0;for(var f=jb.length;d<f;d++){var g=jb[d],h=g.object;h.visible&&(x(h,b),t(g))}a.overrideMaterial?(d=a.overrideMaterial,this.setBlending(d.blending,d.blendEquation,
-d.blendSrc,d.blendDst),this.setDepthTest(d.depthTest),this.setDepthWrite(d.depthWrite),B(d.polygonOffset,d.polygonOffsetFactor,d.polygonOffsetUnits),m(Jb,b,cb,e,!0,d),m(Ib,b,cb,e,!0,d),r(jb,"",b,cb,e,!1,d)):(d=null,this.setBlending(THREE.NoBlending),m(Jb,b,cb,e,!1,d),r(jb,"opaque",b,cb,e,!1,d),m(Ib,b,cb,e,!0,d),r(jb,"transparent",b,cb,e,!0,d));jd.render(a,b);kd.render(a,b,Uc,Vc);c&&c.generateMipmaps&&c.minFilter!==THREE.NearestFilter&&c.minFilter!==THREE.LinearFilter&&C(c);this.setDepthTest(!0);this.setDepthWrite(!0)}};
-this.renderImmediateObject=function(a,b,c,d,e){var f=G(a,b,c,d,e);Oa=-1;J.setMaterialFaces(d);e.immediateRenderCallback?e.immediateRenderCallback(f,l,Ec):e.render(function(a){J.renderBufferImmediate(a,f,d)})};var xb={},rc=0;this.setFaceCulling=function(a,b){a===THREE.CullFaceNone?l.disable(l.CULL_FACE):(b===THREE.FrontFaceDirectionCW?l.frontFace(l.CW):l.frontFace(l.CCW),a===THREE.CullFaceBack?l.cullFace(l.BACK):a===THREE.CullFaceFront?l.cullFace(l.FRONT):l.cullFace(l.FRONT_AND_BACK),l.enable(l.CULL_FACE))};
-this.setMaterialFaces=function(a){var b=a.side===THREE.DoubleSide;a=a.side===THREE.BackSide;Lb!==b&&(b?l.disable(l.CULL_FACE):l.enable(l.CULL_FACE),Lb=b);Mb!==a&&(a?l.frontFace(l.CW):l.frontFace(l.CCW),Mb=a)};this.setDepthTest=function(a){Yb!==a&&(a?l.enable(l.DEPTH_TEST):l.disable(l.DEPTH_TEST),Yb=a)};this.setDepthWrite=function(a){nb!==a&&(l.depthMask(a),nb=a)};this.setBlending=function(a,b,c,d){a!==pb&&(a===THREE.NoBlending?l.disable(l.BLEND):a===THREE.AdditiveBlending?(l.enable(l.BLEND),l.blendEquation(l.FUNC_ADD),
-l.blendFunc(l.SRC_ALPHA,l.ONE)):a===THREE.SubtractiveBlending?(l.enable(l.BLEND),l.blendEquation(l.FUNC_ADD),l.blendFunc(l.ZERO,l.ONE_MINUS_SRC_COLOR)):a===THREE.MultiplyBlending?(l.enable(l.BLEND),l.blendEquation(l.FUNC_ADD),l.blendFunc(l.ZERO,l.SRC_COLOR)):a===THREE.CustomBlending?l.enable(l.BLEND):(l.enable(l.BLEND),l.blendEquationSeparate(l.FUNC_ADD,l.FUNC_ADD),l.blendFuncSeparate(l.SRC_ALPHA,l.ONE_MINUS_SRC_ALPHA,l.ONE,l.ONE_MINUS_SRC_ALPHA)),pb=a);if(a===THREE.CustomBlending){if(b!==Nb&&(l.blendEquation(Q(b)),
-Nb=b),c!==Ob||d!==Xb)l.blendFunc(Q(c),Q(d)),Ob=c,Xb=d}else Xb=Ob=Nb=null};this.uploadTexture=function(a){void 0===a.__webglInit&&(a.__webglInit=!0,a.addEventListener("dispose",gc),a.__webglTexture=l.createTexture(),J.info.memory.textures++);l.bindTexture(l.TEXTURE_2D,a.__webglTexture);l.pixelStorei(l.UNPACK_FLIP_Y_WEBGL,a.flipY);l.pixelStorei(l.UNPACK_PREMULTIPLY_ALPHA_WEBGL,a.premultiplyAlpha);l.pixelStorei(l.UNPACK_ALIGNMENT,a.unpackAlignment);a.image=R(a.image,cd);var b=a.image,c=THREE.Math.isPowerOfTwo(b.width)&&
-THREE.Math.isPowerOfTwo(b.height),d=Q(a.format),e=Q(a.type);F(l.TEXTURE_2D,a,c);var f=a.mipmaps;if(a instanceof THREE.DataTexture)if(0<f.length&&c){for(var g=0,h=f.length;g<h;g++)b=f[g],l.texImage2D(l.TEXTURE_2D,g,d,b.width,b.height,0,d,e,b.data);a.generateMipmaps=!1}else l.texImage2D(l.TEXTURE_2D,0,d,b.width,b.height,0,d,e,b.data);else if(a instanceof THREE.CompressedTexture)for(g=0,h=f.length;g<h;g++)b=f[g],a.format!==THREE.RGBAFormat&&a.format!==THREE.RGBFormat?-1<Nc().indexOf(d)?l.compressedTexImage2D(l.TEXTURE_2D,
-g,d,b.width,b.height,0,b.data):console.warn("Attempt to load unsupported compressed texture format"):l.texImage2D(l.TEXTURE_2D,g,d,b.width,b.height,0,d,e,b.data);else if(0<f.length&&c){g=0;for(h=f.length;g<h;g++)b=f[g],l.texImage2D(l.TEXTURE_2D,g,d,d,e,b);a.generateMipmaps=!1}else l.texImage2D(l.TEXTURE_2D,0,d,d,e,a.image);a.generateMipmaps&&c&&l.generateMipmap(l.TEXTURE_2D);a.needsUpdate=!1;if(a.onUpdate)a.onUpdate()};this.setTexture=function(a,b){l.activeTexture(l.TEXTURE0+b);a.needsUpdate?J.uploadTexture(a):
-l.bindTexture(l.TEXTURE_2D,a.__webglTexture)};this.setRenderTarget=function(a){var b=a instanceof THREE.WebGLRenderTargetCube;if(a&&void 0===a.__webglFramebuffer){void 0===a.depthBuffer&&(a.depthBuffer=!0);void 0===a.stencilBuffer&&(a.stencilBuffer=!0);a.addEventListener("dispose",Zc);a.__webglTexture=l.createTexture();J.info.memory.textures++;var c=THREE.Math.isPowerOfTwo(a.width)&&THREE.Math.isPowerOfTwo(a.height),d=Q(a.format),e=Q(a.type);if(b){a.__webglFramebuffer=[];a.__webglRenderbuffer=[];
-l.bindTexture(l.TEXTURE_CUBE_MAP,a.__webglTexture);F(l.TEXTURE_CUBE_MAP,a,c);for(var f=0;6>f;f++){a.__webglFramebuffer[f]=l.createFramebuffer();a.__webglRenderbuffer[f]=l.createRenderbuffer();l.texImage2D(l.TEXTURE_CUBE_MAP_POSITIVE_X+f,0,d,a.width,a.height,0,d,e,null);var g=a,h=l.TEXTURE_CUBE_MAP_POSITIVE_X+f;l.bindFramebuffer(l.FRAMEBUFFER,a.__webglFramebuffer[f]);l.framebufferTexture2D(l.FRAMEBUFFER,l.COLOR_ATTACHMENT0,h,g.__webglTexture,0);H(a.__webglRenderbuffer[f],a)}c&&l.generateMipmap(l.TEXTURE_CUBE_MAP)}else a.__webglFramebuffer=
-l.createFramebuffer(),a.__webglRenderbuffer=a.shareDepthFrom?a.shareDepthFrom.__webglRenderbuffer:l.createRenderbuffer(),l.bindTexture(l.TEXTURE_2D,a.__webglTexture),F(l.TEXTURE_2D,a,c),l.texImage2D(l.TEXTURE_2D,0,d,a.width,a.height,0,d,e,null),d=l.TEXTURE_2D,l.bindFramebuffer(l.FRAMEBUFFER,a.__webglFramebuffer),l.framebufferTexture2D(l.FRAMEBUFFER,l.COLOR_ATTACHMENT0,d,a.__webglTexture,0),a.shareDepthFrom?a.depthBuffer&&!a.stencilBuffer?l.framebufferRenderbuffer(l.FRAMEBUFFER,l.DEPTH_ATTACHMENT,
-l.RENDERBUFFER,a.__webglRenderbuffer):a.depthBuffer&&a.stencilBuffer&&l.framebufferRenderbuffer(l.FRAMEBUFFER,l.DEPTH_STENCIL_ATTACHMENT,l.RENDERBUFFER,a.__webglRenderbuffer):H(a.__webglRenderbuffer,a),c&&l.generateMipmap(l.TEXTURE_2D);b?l.bindTexture(l.TEXTURE_CUBE_MAP,null):l.bindTexture(l.TEXTURE_2D,null);l.bindRenderbuffer(l.RENDERBUFFER,null);l.bindFramebuffer(l.FRAMEBUFFER,null)}a?(b=b?a.__webglFramebuffer[a.activeCubeFace]:a.__webglFramebuffer,c=a.width,a=a.height,e=d=0):(b=null,c=lc,a=mc,
-d=Pb,e=kc);b!==Tc&&(l.bindFramebuffer(l.FRAMEBUFFER,b),l.viewport(d,e,c,a),Tc=b);Uc=c;Vc=a};this.initMaterial=function(){console.warn("THREE.WebGLRenderer: .initMaterial() has been removed.")};this.addPrePlugin=function(){console.warn("THREE.WebGLRenderer: .addPrePlugin() has been removed.")};this.addPostPlugin=function(){console.warn("THREE.WebGLRenderer: .addPostPlugin() has been removed.")};this.updateShadowMap=function(){console.warn("THREE.WebGLRenderer: .updateShadowMap() has been removed.")}};
-THREE.WebGLRenderTarget=function(a,b,c){this.width=a;this.height=b;c=c||{};this.wrapS=void 0!==c.wrapS?c.wrapS:THREE.ClampToEdgeWrapping;this.wrapT=void 0!==c.wrapT?c.wrapT:THREE.ClampToEdgeWrapping;this.magFilter=void 0!==c.magFilter?c.magFilter:THREE.LinearFilter;this.minFilter=void 0!==c.minFilter?c.minFilter:THREE.LinearMipMapLinearFilter;this.anisotropy=void 0!==c.anisotropy?c.anisotropy:1;this.offset=new THREE.Vector2(0,0);this.repeat=new THREE.Vector2(1,1);this.format=void 0!==c.format?c.format:
-THREE.RGBAFormat;this.type=void 0!==c.type?c.type:THREE.UnsignedByteType;this.depthBuffer=void 0!==c.depthBuffer?c.depthBuffer:!0;this.stencilBuffer=void 0!==c.stencilBuffer?c.stencilBuffer:!0;this.generateMipmaps=!0;this.shareDepthFrom=null};
-THREE.WebGLRenderTarget.prototype={constructor:THREE.WebGLRenderTarget,setSize:function(a,b){this.width=a;this.height=b},clone:function(){var a=new THREE.WebGLRenderTarget(this.width,this.height);a.wrapS=this.wrapS;a.wrapT=this.wrapT;a.magFilter=this.magFilter;a.minFilter=this.minFilter;a.anisotropy=this.anisotropy;a.offset.copy(this.offset);a.repeat.copy(this.repeat);a.format=this.format;a.type=this.type;a.depthBuffer=this.depthBuffer;a.stencilBuffer=this.stencilBuffer;a.generateMipmaps=this.generateMipmaps;
-a.shareDepthFrom=this.shareDepthFrom;return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.WebGLRenderTarget.prototype);THREE.WebGLRenderTargetCube=function(a,b,c){THREE.WebGLRenderTarget.call(this,a,b,c);this.activeCubeFace=0};THREE.WebGLRenderTargetCube.prototype=Object.create(THREE.WebGLRenderTarget.prototype);
-THREE.WebGLExtensions=function(a){var b={};this.get=function(c){if(void 0!==b[c])return b[c];var d;switch(c){case "OES_texture_float":d=a.getExtension("OES_texture_float");break;case "OES_texture_float_linear":d=a.getExtension("OES_texture_float_linear");break;case "OES_standard_derivatives":d=a.getExtension("OES_standard_derivatives");break;case "EXT_texture_filter_anisotropic":d=a.getExtension("EXT_texture_filter_anisotropic")||a.getExtension("MOZ_EXT_texture_filter_anisotropic")||a.getExtension("WEBKIT_EXT_texture_filter_anisotropic");
-break;case "WEBGL_compressed_texture_s3tc":d=a.getExtension("WEBGL_compressed_texture_s3tc")||a.getExtension("MOZ_WEBGL_compressed_texture_s3tc")||a.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc");break;case "WEBGL_compressed_texture_pvrtc":d=a.getExtension("WEBGL_compressed_texture_pvrtc")||a.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc");break;case "OES_element_index_uint":d=a.getExtension("OES_element_index_uint");break;case "EXT_blend_minmax":d=a.getExtension("EXT_blend_minmax");break;
-case "EXT_frag_depth":d=a.getExtension("EXT_frag_depth")}null===d&&console.log("THREE.WebGLRenderer: "+c+" extension not supported.");return b[c]=d}};
-THREE.WebGLProgram=function(){var a=0;return function(b,c,d,e){var f=b.context,g=d.defines,h=d.__webglShader.uniforms,k=d.attributes,n=d.__webglShader.vertexShader,p=d.__webglShader.fragmentShader,q=d.index0AttributeName;void 0===q&&!0===e.morphTargets&&(q="position");var m="SHADOWMAP_TYPE_BASIC";e.shadowMapType===THREE.PCFShadowMap?m="SHADOWMAP_TYPE_PCF":e.shadowMapType===THREE.PCFSoftShadowMap&&(m="SHADOWMAP_TYPE_PCF_SOFT");var r,t;r=[];for(var s in g)t=g[s],!1!==t&&(t="#define "+s+" "+t,r.push(t));
-r=r.join("\n");g=f.createProgram();d instanceof THREE.RawShaderMaterial?b=d="":(d=["precision "+e.precision+" float;","precision "+e.precision+" int;",r,e.supportsVertexTextures?"#define VERTEX_TEXTURES":"",b.gammaInput?"#define GAMMA_INPUT":"",b.gammaOutput?"#define GAMMA_OUTPUT":"","#define MAX_DIR_LIGHTS "+e.maxDirLights,"#define MAX_POINT_LIGHTS "+e.maxPointLights,"#define MAX_SPOT_LIGHTS "+e.maxSpotLights,"#define MAX_HEMI_LIGHTS "+e.maxHemiLights,"#define MAX_SHADOWS "+e.maxShadows,"#define MAX_BONES "+
-e.maxBones,e.map?"#define USE_MAP":"",e.envMap?"#define USE_ENVMAP":"",e.lightMap?"#define USE_LIGHTMAP":"",e.bumpMap?"#define USE_BUMPMAP":"",e.normalMap?"#define USE_NORMALMAP":"",e.specularMap?"#define USE_SPECULARMAP":"",e.alphaMap?"#define USE_ALPHAMAP":"",e.vertexColors?"#define USE_COLOR":"",e.skinning?"#define USE_SKINNING":"",e.useVertexTexture?"#define BONE_TEXTURE":"",e.morphTargets?"#define USE_MORPHTARGETS":"",e.morphNormals?"#define USE_MORPHNORMALS":"",e.wrapAround?"#define WRAP_AROUND":
-"",e.doubleSided?"#define DOUBLE_SIDED":"",e.flipSided?"#define FLIP_SIDED":"",e.shadowMapEnabled?"#define USE_SHADOWMAP":"",e.shadowMapEnabled?"#define "+m:"",e.shadowMapDebug?"#define SHADOWMAP_DEBUG":"",e.shadowMapCascade?"#define SHADOWMAP_CASCADE":"",e.sizeAttenuation?"#define USE_SIZEATTENUATION":"",e.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"","uniform mat4 modelMatrix;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\nuniform mat3 normalMatrix;\nuniform vec3 cameraPosition;\nattribute vec3 position;\nattribute vec3 normal;\nattribute vec2 uv;\nattribute vec2 uv2;\n#ifdef USE_COLOR\n\tattribute vec3 color;\n#endif\n#ifdef USE_MORPHTARGETS\n\tattribute vec3 morphTarget0;\n\tattribute vec3 morphTarget1;\n\tattribute vec3 morphTarget2;\n\tattribute vec3 morphTarget3;\n\t#ifdef USE_MORPHNORMALS\n\t\tattribute vec3 morphNormal0;\n\t\tattribute vec3 morphNormal1;\n\t\tattribute vec3 morphNormal2;\n\t\tattribute vec3 morphNormal3;\n\t#else\n\t\tattribute vec3 morphTarget4;\n\t\tattribute vec3 morphTarget5;\n\t\tattribute vec3 morphTarget6;\n\t\tattribute vec3 morphTarget7;\n\t#endif\n#endif\n#ifdef USE_SKINNING\n\tattribute vec4 skinIndex;\n\tattribute vec4 skinWeight;\n#endif\n"].join("\n"),
-b=["precision "+e.precision+" float;","precision "+e.precision+" int;",e.bumpMap||e.normalMap?"#extension GL_OES_standard_derivatives : enable":"",r,"#define MAX_DIR_LIGHTS "+e.maxDirLights,"#define MAX_POINT_LIGHTS "+e.maxPointLights,"#define MAX_SPOT_LIGHTS "+e.maxSpotLights,"#define MAX_HEMI_LIGHTS "+e.maxHemiLights,"#define MAX_SHADOWS "+e.maxShadows,e.alphaTest?"#define ALPHATEST "+e.alphaTest:"",b.gammaInput?"#define GAMMA_INPUT":"",b.gammaOutput?"#define GAMMA_OUTPUT":"",e.useFog&&e.fog?"#define USE_FOG":
-"",e.useFog&&e.fogExp?"#define FOG_EXP2":"",e.map?"#define USE_MAP":"",e.envMap?"#define USE_ENVMAP":"",e.lightMap?"#define USE_LIGHTMAP":"",e.bumpMap?"#define USE_BUMPMAP":"",e.normalMap?"#define USE_NORMALMAP":"",e.specularMap?"#define USE_SPECULARMAP":"",e.alphaMap?"#define USE_ALPHAMAP":"",e.vertexColors?"#define USE_COLOR":"",e.metal?"#define METAL":"",e.wrapAround?"#define WRAP_AROUND":"",e.doubleSided?"#define DOUBLE_SIDED":"",e.flipSided?"#define FLIP_SIDED":"",e.shadowMapEnabled?"#define USE_SHADOWMAP":
-"",e.shadowMapEnabled?"#define "+m:"",e.shadowMapDebug?"#define SHADOWMAP_DEBUG":"",e.shadowMapCascade?"#define SHADOWMAP_CASCADE":"",e.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"","uniform mat4 viewMatrix;\nuniform vec3 cameraPosition;\n"].join("\n"));n=new THREE.WebGLShader(f,f.VERTEX_SHADER,d+n);p=new THREE.WebGLShader(f,f.FRAGMENT_SHADER,b+p);f.attachShader(g,n);f.attachShader(g,p);void 0!==q&&f.bindAttribLocation(g,0,q);f.linkProgram(g);!1===f.getProgramParameter(g,f.LINK_STATUS)&&(console.error("THREE.WebGLProgram: Could not initialise shader."),
-console.error("gl.VALIDATE_STATUS",f.getProgramParameter(g,f.VALIDATE_STATUS)),console.error("gl.getError()",f.getError()));""!==f.getProgramInfoLog(g)&&console.warn("THREE.WebGLProgram: gl.getProgramInfoLog()",f.getProgramInfoLog(g));f.deleteShader(n);f.deleteShader(p);q="viewMatrix modelViewMatrix projectionMatrix normalMatrix modelMatrix cameraPosition morphTargetInfluences bindMatrix bindMatrixInverse".split(" ");e.useVertexTexture?(q.push("boneTexture"),q.push("boneTextureWidth"),q.push("boneTextureHeight")):
-q.push("boneGlobalMatrices");e.logarithmicDepthBuffer&&q.push("logDepthBufFC");for(var u in h)q.push(u);h=q;u={};q=0;for(b=h.length;q<b;q++)m=h[q],u[m]=f.getUniformLocation(g,m);this.uniforms=u;q="position normal uv uv2 tangent color skinIndex skinWeight lineDistance".split(" ");for(h=0;h<e.maxMorphTargets;h++)q.push("morphTarget"+h);for(h=0;h<e.maxMorphNormals;h++)q.push("morphNormal"+h);for(var v in k)q.push(v);e=q;k={};v=0;for(h=e.length;v<h;v++)u=e[v],k[u]=f.getAttribLocation(g,u);this.attributes=
-k;this.attributesKeys=Object.keys(this.attributes);this.id=a++;this.code=c;this.usedTimes=1;this.program=g;this.vertexShader=n;this.fragmentShader=p;return this}}();
-THREE.WebGLShader=function(){var a=function(a){a=a.split("\n");for(var c=0;c<a.length;c++)a[c]=c+1+": "+a[c];return a.join("\n")};return function(b,c,d){c=b.createShader(c);b.shaderSource(c,d);b.compileShader(c);!1===b.getShaderParameter(c,b.COMPILE_STATUS)&&console.error("THREE.WebGLShader: Shader couldn't compile.");""!==b.getShaderInfoLog(c)&&(console.warn("THREE.WebGLShader: gl.getShaderInfoLog()",b.getShaderInfoLog(c)),console.warn(a(d)));return c}}();
-THREE.LensFlarePlugin=function(a,b){var c,d,e,f,g,h,k,n,p,q,m=a.context,r,t,s,u,v,y;this.render=function(G,w,K,x){if(0!==b.length){G=new THREE.Vector3;var D=x/K,E=.5*K,A=.5*x,B=16/x,F=new THREE.Vector2(B*D,B),R=new THREE.Vector3(1,1,0),H=new THREE.Vector2(1,1);if(void 0===s){var B=new Float32Array([-1,-1,0,0,1,-1,1,0,1,1,1,1,-1,1,0,1]),C=new Uint16Array([0,1,2,0,2,3]);r=m.createBuffer();t=m.createBuffer();m.bindBuffer(m.ARRAY_BUFFER,r);m.bufferData(m.ARRAY_BUFFER,B,m.STATIC_DRAW);m.bindBuffer(m.ELEMENT_ARRAY_BUFFER,
-t);m.bufferData(m.ELEMENT_ARRAY_BUFFER,C,m.STATIC_DRAW);v=m.createTexture();y=m.createTexture();m.bindTexture(m.TEXTURE_2D,v);m.texImage2D(m.TEXTURE_2D,0,m.RGB,16,16,0,m.RGB,m.UNSIGNED_BYTE,null);m.texParameteri(m.TEXTURE_2D,m.TEXTURE_WRAP_S,m.CLAMP_TO_EDGE);m.texParameteri(m.TEXTURE_2D,m.TEXTURE_WRAP_T,m.CLAMP_TO_EDGE);m.texParameteri(m.TEXTURE_2D,m.TEXTURE_MAG_FILTER,m.NEAREST);m.texParameteri(m.TEXTURE_2D,m.TEXTURE_MIN_FILTER,m.NEAREST);m.bindTexture(m.TEXTURE_2D,y);m.texImage2D(m.TEXTURE_2D,0,
-m.RGBA,16,16,0,m.RGBA,m.UNSIGNED_BYTE,null);m.texParameteri(m.TEXTURE_2D,m.TEXTURE_WRAP_S,m.CLAMP_TO_EDGE);m.texParameteri(m.TEXTURE_2D,m.TEXTURE_WRAP_T,m.CLAMP_TO_EDGE);m.texParameteri(m.TEXTURE_2D,m.TEXTURE_MAG_FILTER,m.NEAREST);m.texParameteri(m.TEXTURE_2D,m.TEXTURE_MIN_FILTER,m.NEAREST);var B=(u=0<m.getParameter(m.MAX_VERTEX_TEXTURE_IMAGE_UNITS))?{vertexShader:"uniform lowp int renderType;\nuniform vec3 screenPosition;\nuniform vec2 scale;\nuniform float rotation;\nuniform sampler2D occlusionMap;\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUV;\nvarying float vVisibility;\nvoid main() {\nvUV = uv;\nvec2 pos = position;\nif( renderType == 2 ) {\nvec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );\nvVisibility =        visibility.r / 9.0;\nvVisibility *= 1.0 - visibility.g / 9.0;\nvVisibility *=       visibility.b / 9.0;\nvVisibility *= 1.0 - visibility.a / 9.0;\npos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;\npos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;\n}\ngl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );\n}",
-fragmentShader:"uniform lowp int renderType;\nuniform sampler2D map;\nuniform float opacity;\nuniform vec3 color;\nvarying vec2 vUV;\nvarying float vVisibility;\nvoid main() {\nif( renderType == 0 ) {\ngl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );\n} else if( renderType == 1 ) {\ngl_FragColor = texture2D( map, vUV );\n} else {\nvec4 texture = texture2D( map, vUV );\ntexture.a *= opacity * vVisibility;\ngl_FragColor = texture;\ngl_FragColor.rgb *= color;\n}\n}"}:{vertexShader:"uniform lowp int renderType;\nuniform vec3 screenPosition;\nuniform vec2 scale;\nuniform float rotation;\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUV;\nvoid main() {\nvUV = uv;\nvec2 pos = position;\nif( renderType == 2 ) {\npos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;\npos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;\n}\ngl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );\n}",
-fragmentShader:"precision mediump float;\nuniform lowp int renderType;\nuniform sampler2D map;\nuniform sampler2D occlusionMap;\nuniform float opacity;\nuniform vec3 color;\nvarying vec2 vUV;\nvoid main() {\nif( renderType == 0 ) {\ngl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );\n} else if( renderType == 1 ) {\ngl_FragColor = texture2D( map, vUV );\n} else {\nfloat visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;\nvisibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;\nvisibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;\nvisibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;\nvisibility = ( 1.0 - visibility / 4.0 );\nvec4 texture = texture2D( map, vUV );\ntexture.a *= opacity * visibility;\ngl_FragColor = texture;\ngl_FragColor.rgb *= color;\n}\n}"},
-C=m.createProgram(),T=m.createShader(m.FRAGMENT_SHADER),Q=m.createShader(m.VERTEX_SHADER),O="precision "+a.getPrecision()+" float;\n";m.shaderSource(T,O+B.fragmentShader);m.shaderSource(Q,O+B.vertexShader);m.compileShader(T);m.compileShader(Q);m.attachShader(C,T);m.attachShader(C,Q);m.linkProgram(C);s=C;p=m.getAttribLocation(s,"position");q=m.getAttribLocation(s,"uv");c=m.getUniformLocation(s,"renderType");d=m.getUniformLocation(s,"map");e=m.getUniformLocation(s,"occlusionMap");f=m.getUniformLocation(s,
-"opacity");g=m.getUniformLocation(s,"color");h=m.getUniformLocation(s,"scale");k=m.getUniformLocation(s,"rotation");n=m.getUniformLocation(s,"screenPosition")}m.useProgram(s);m.enableVertexAttribArray(p);m.enableVertexAttribArray(q);m.uniform1i(e,0);m.uniform1i(d,1);m.bindBuffer(m.ARRAY_BUFFER,r);m.vertexAttribPointer(p,2,m.FLOAT,!1,16,0);m.vertexAttribPointer(q,2,m.FLOAT,!1,16,8);m.bindBuffer(m.ELEMENT_ARRAY_BUFFER,t);m.disable(m.CULL_FACE);m.depthMask(!1);C=0;for(T=b.length;C<T;C++)if(B=16/x,F.set(B*
-D,B),Q=b[C],G.set(Q.matrixWorld.elements[12],Q.matrixWorld.elements[13],Q.matrixWorld.elements[14]),G.applyMatrix4(w.matrixWorldInverse),G.applyProjection(w.projectionMatrix),R.copy(G),H.x=R.x*E+E,H.y=R.y*A+A,u||0<H.x&&H.x<K&&0<H.y&&H.y<x){m.activeTexture(m.TEXTURE1);m.bindTexture(m.TEXTURE_2D,v);m.copyTexImage2D(m.TEXTURE_2D,0,m.RGB,H.x-8,H.y-8,16,16,0);m.uniform1i(c,0);m.uniform2f(h,F.x,F.y);m.uniform3f(n,R.x,R.y,R.z);m.disable(m.BLEND);m.enable(m.DEPTH_TEST);m.drawElements(m.TRIANGLES,6,m.UNSIGNED_SHORT,
-0);m.activeTexture(m.TEXTURE0);m.bindTexture(m.TEXTURE_2D,y);m.copyTexImage2D(m.TEXTURE_2D,0,m.RGBA,H.x-8,H.y-8,16,16,0);m.uniform1i(c,1);m.disable(m.DEPTH_TEST);m.activeTexture(m.TEXTURE1);m.bindTexture(m.TEXTURE_2D,v);m.drawElements(m.TRIANGLES,6,m.UNSIGNED_SHORT,0);Q.positionScreen.copy(R);Q.customUpdateCallback?Q.customUpdateCallback(Q):Q.updateLensFlares();m.uniform1i(c,2);m.enable(m.BLEND);for(var O=0,S=Q.lensFlares.length;O<S;O++){var X=Q.lensFlares[O];.001<X.opacity&&.001<X.scale&&(R.x=X.x,
-R.y=X.y,R.z=X.z,B=X.size*X.scale/x,F.x=B*D,F.y=B,m.uniform3f(n,R.x,R.y,R.z),m.uniform2f(h,F.x,F.y),m.uniform1f(k,X.rotation),m.uniform1f(f,X.opacity),m.uniform3f(g,X.color.r,X.color.g,X.color.b),a.setBlending(X.blending,X.blendEquation,X.blendSrc,X.blendDst),a.setTexture(X.texture,1),m.drawElements(m.TRIANGLES,6,m.UNSIGNED_SHORT,0))}}m.enable(m.CULL_FACE);m.enable(m.DEPTH_TEST);m.depthMask(!0);a.resetGLState()}}};
-THREE.ShadowMapPlugin=function(a,b,c,d){function e(a,b,d){if(b.visible){var f=c[b.id];if(f&&b.castShadow&&(!1===b.frustumCulled||!0===p.intersectsObject(b)))for(var g=0,h=f.length;g<h;g++){var k=f[g];b._modelViewMatrix.multiplyMatrices(d.matrixWorldInverse,b.matrixWorld);s.push(k)}g=0;for(h=b.children.length;g<h;g++)e(a,b.children[g],d)}}var f=a.context,g,h,k,n,p=new THREE.Frustum,q=new THREE.Matrix4,m=new THREE.Vector3,r=new THREE.Vector3,t=new THREE.Vector3,s=[],u=THREE.ShaderLib.depthRGBA,v=THREE.UniformsUtils.clone(u.uniforms);
-g=new THREE.ShaderMaterial({uniforms:v,vertexShader:u.vertexShader,fragmentShader:u.fragmentShader});h=new THREE.ShaderMaterial({uniforms:v,vertexShader:u.vertexShader,fragmentShader:u.fragmentShader,morphTargets:!0});k=new THREE.ShaderMaterial({uniforms:v,vertexShader:u.vertexShader,fragmentShader:u.fragmentShader,skinning:!0});n=new THREE.ShaderMaterial({uniforms:v,vertexShader:u.vertexShader,fragmentShader:u.fragmentShader,morphTargets:!0,skinning:!0});g._shadowPass=!0;h._shadowPass=!0;k._shadowPass=
-!0;n._shadowPass=!0;this.render=function(c,v){if(!1!==a.shadowMapEnabled){var u,K,x,D,E,A,B,F,R=[];D=0;f.clearColor(1,1,1,1);f.disable(f.BLEND);f.enable(f.CULL_FACE);f.frontFace(f.CCW);a.shadowMapCullFace===THREE.CullFaceFront?f.cullFace(f.FRONT):f.cullFace(f.BACK);a.setDepthTest(!0);u=0;for(K=b.length;u<K;u++)if(x=b[u],x.castShadow)if(x instanceof THREE.DirectionalLight&&x.shadowCascade)for(E=0;E<x.shadowCascadeCount;E++){var H;if(x.shadowCascadeArray[E])H=x.shadowCascadeArray[E];else{B=x;var C=
-E;H=new THREE.DirectionalLight;H.isVirtual=!0;H.onlyShadow=!0;H.castShadow=!0;H.shadowCameraNear=B.shadowCameraNear;H.shadowCameraFar=B.shadowCameraFar;H.shadowCameraLeft=B.shadowCameraLeft;H.shadowCameraRight=B.shadowCameraRight;H.shadowCameraBottom=B.shadowCameraBottom;H.shadowCameraTop=B.shadowCameraTop;H.shadowCameraVisible=B.shadowCameraVisible;H.shadowDarkness=B.shadowDarkness;H.shadowBias=B.shadowCascadeBias[C];H.shadowMapWidth=B.shadowCascadeWidth[C];H.shadowMapHeight=B.shadowCascadeHeight[C];
-H.pointsWorld=[];H.pointsFrustum=[];F=H.pointsWorld;A=H.pointsFrustum;for(var T=0;8>T;T++)F[T]=new THREE.Vector3,A[T]=new THREE.Vector3;F=B.shadowCascadeNearZ[C];B=B.shadowCascadeFarZ[C];A[0].set(-1,-1,F);A[1].set(1,-1,F);A[2].set(-1,1,F);A[3].set(1,1,F);A[4].set(-1,-1,B);A[5].set(1,-1,B);A[6].set(-1,1,B);A[7].set(1,1,B);H.originalCamera=v;A=new THREE.Gyroscope;A.position.copy(x.shadowCascadeOffset);A.add(H);A.add(H.target);v.add(A);x.shadowCascadeArray[E]=H;console.log("Created virtualLight",H)}C=
-x;F=E;B=C.shadowCascadeArray[F];B.position.copy(C.position);B.target.position.copy(C.target.position);B.lookAt(B.target);B.shadowCameraVisible=C.shadowCameraVisible;B.shadowDarkness=C.shadowDarkness;B.shadowBias=C.shadowCascadeBias[F];A=C.shadowCascadeNearZ[F];C=C.shadowCascadeFarZ[F];B=B.pointsFrustum;B[0].z=A;B[1].z=A;B[2].z=A;B[3].z=A;B[4].z=C;B[5].z=C;B[6].z=C;B[7].z=C;R[D]=H;D++}else R[D]=x,D++;u=0;for(K=R.length;u<K;u++){x=R[u];x.shadowMap||(E=THREE.LinearFilter,a.shadowMapType===THREE.PCFSoftShadowMap&&
-(E=THREE.NearestFilter),x.shadowMap=new THREE.WebGLRenderTarget(x.shadowMapWidth,x.shadowMapHeight,{minFilter:E,magFilter:E,format:THREE.RGBAFormat}),x.shadowMapSize=new THREE.Vector2(x.shadowMapWidth,x.shadowMapHeight),x.shadowMatrix=new THREE.Matrix4);if(!x.shadowCamera){if(x instanceof THREE.SpotLight)x.shadowCamera=new THREE.PerspectiveCamera(x.shadowCameraFov,x.shadowMapWidth/x.shadowMapHeight,x.shadowCameraNear,x.shadowCameraFar);else if(x instanceof THREE.DirectionalLight)x.shadowCamera=new THREE.OrthographicCamera(x.shadowCameraLeft,
-x.shadowCameraRight,x.shadowCameraTop,x.shadowCameraBottom,x.shadowCameraNear,x.shadowCameraFar);else{console.error("Unsupported light type for shadow");continue}c.add(x.shadowCamera);!0===c.autoUpdate&&c.updateMatrixWorld()}x.shadowCameraVisible&&!x.cameraHelper&&(x.cameraHelper=new THREE.CameraHelper(x.shadowCamera),c.add(x.cameraHelper));if(x.isVirtual&&H.originalCamera==v){E=v;D=x.shadowCamera;A=x.pointsFrustum;B=x.pointsWorld;m.set(Infinity,Infinity,Infinity);r.set(-Infinity,-Infinity,-Infinity);
-for(C=0;8>C;C++)F=B[C],F.copy(A[C]),F.unproject(E),F.applyMatrix4(D.matrixWorldInverse),F.x<m.x&&(m.x=F.x),F.x>r.x&&(r.x=F.x),F.y<m.y&&(m.y=F.y),F.y>r.y&&(r.y=F.y),F.z<m.z&&(m.z=F.z),F.z>r.z&&(r.z=F.z);D.left=m.x;D.right=r.x;D.top=r.y;D.bottom=m.y;D.updateProjectionMatrix()}D=x.shadowMap;A=x.shadowMatrix;E=x.shadowCamera;E.position.setFromMatrixPosition(x.matrixWorld);t.setFromMatrixPosition(x.target.matrixWorld);E.lookAt(t);E.updateMatrixWorld();E.matrixWorldInverse.getInverse(E.matrixWorld);x.cameraHelper&&
-(x.cameraHelper.visible=x.shadowCameraVisible);x.shadowCameraVisible&&x.cameraHelper.update();A.set(.5,0,0,.5,0,.5,0,.5,0,0,.5,.5,0,0,0,1);A.multiply(E.projectionMatrix);A.multiply(E.matrixWorldInverse);q.multiplyMatrices(E.projectionMatrix,E.matrixWorldInverse);p.setFromMatrix(q);a.setRenderTarget(D);a.clear();s.length=0;e(c,c,E);x=0;for(D=s.length;x<D;x++)B=s[x],A=B.object,B=B.buffer,C=A.material instanceof THREE.MeshFaceMaterial?A.material.materials[0]:A.material,F=void 0!==A.geometry.morphTargets&&
-0<A.geometry.morphTargets.length&&C.morphTargets,T=A instanceof THREE.SkinnedMesh&&C.skinning,F=A.customDepthMaterial?A.customDepthMaterial:T?F?n:k:F?h:g,a.setMaterialFaces(C),B instanceof THREE.BufferGeometry?a.renderBufferDirect(E,b,null,F,B,A):a.renderBuffer(E,b,null,F,B,A);x=0;for(D=d.length;x<D;x++)B=d[x],A=B.object,A.visible&&A.castShadow&&(A._modelViewMatrix.multiplyMatrices(E.matrixWorldInverse,A.matrixWorld),a.renderImmediateObject(E,b,null,g,A))}u=a.getClearColor();K=a.getClearAlpha();f.clearColor(u.r,
-u.g,u.b,K);f.enable(f.BLEND);a.shadowMapCullFace===THREE.CullFaceFront&&f.cullFace(f.BACK);a.resetGLState()}}};
-THREE.SpritePlugin=function(a,b){var c,d,e,f,g,h,k,n,p,q,m,r,t,s,u,v,y;function G(a,b){return a.z!==b.z?b.z-a.z:b.id-a.id}var w=a.context,K,x,D,E;this.render=function(A,B){if(0!==b.length){if(void 0===D){var F=new Float32Array([-.5,-.5,0,0,.5,-.5,1,0,.5,.5,1,1,-.5,.5,0,1]),R=new Uint16Array([0,1,2,0,2,3]);K=w.createBuffer();x=w.createBuffer();w.bindBuffer(w.ARRAY_BUFFER,K);w.bufferData(w.ARRAY_BUFFER,F,w.STATIC_DRAW);w.bindBuffer(w.ELEMENT_ARRAY_BUFFER,x);w.bufferData(w.ELEMENT_ARRAY_BUFFER,R,w.STATIC_DRAW);
-var F=w.createProgram(),R=w.createShader(w.VERTEX_SHADER),H=w.createShader(w.FRAGMENT_SHADER);w.shaderSource(R,["precision "+a.getPrecision()+" float;","uniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform float rotation;\nuniform vec2 scale;\nuniform vec2 uvOffset;\nuniform vec2 uvScale;\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUV;\nvoid main() {\nvUV = uvOffset + uv * uvScale;\nvec2 alignedPosition = position * scale;\nvec2 rotatedPosition;\nrotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\nrotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\nvec4 finalPosition;\nfinalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\nfinalPosition.xy += rotatedPosition;\nfinalPosition = projectionMatrix * finalPosition;\ngl_Position = finalPosition;\n}"].join("\n"));
-w.shaderSource(H,["precision "+a.getPrecision()+" float;","uniform vec3 color;\nuniform sampler2D map;\nuniform float opacity;\nuniform int fogType;\nuniform vec3 fogColor;\nuniform float fogDensity;\nuniform float fogNear;\nuniform float fogFar;\nuniform float alphaTest;\nvarying vec2 vUV;\nvoid main() {\nvec4 texture = texture2D( map, vUV );\nif ( texture.a < alphaTest ) discard;\ngl_FragColor = vec4( color * texture.xyz, texture.a * opacity );\nif ( fogType > 0 ) {\nfloat depth = gl_FragCoord.z / gl_FragCoord.w;\nfloat fogFactor = 0.0;\nif ( fogType == 1 ) {\nfogFactor = smoothstep( fogNear, fogFar, depth );\n} else {\nconst float LOG2 = 1.442695;\nfloat fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\nfogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n}\ngl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n}\n}"].join("\n"));
-w.compileShader(R);w.compileShader(H);w.attachShader(F,R);w.attachShader(F,H);w.linkProgram(F);D=F;v=w.getAttribLocation(D,"position");y=w.getAttribLocation(D,"uv");c=w.getUniformLocation(D,"uvOffset");d=w.getUniformLocation(D,"uvScale");e=w.getUniformLocation(D,"rotation");f=w.getUniformLocation(D,"scale");g=w.getUniformLocation(D,"color");h=w.getUniformLocation(D,"map");k=w.getUniformLocation(D,"opacity");n=w.getUniformLocation(D,"modelViewMatrix");p=w.getUniformLocation(D,"projectionMatrix");q=
-w.getUniformLocation(D,"fogType");m=w.getUniformLocation(D,"fogDensity");r=w.getUniformLocation(D,"fogNear");t=w.getUniformLocation(D,"fogFar");s=w.getUniformLocation(D,"fogColor");u=w.getUniformLocation(D,"alphaTest");F=document.createElement("canvas");F.width=8;F.height=8;R=F.getContext("2d");R.fillStyle="white";R.fillRect(0,0,8,8);E=new THREE.Texture(F);E.needsUpdate=!0}w.useProgram(D);w.enableVertexAttribArray(v);w.enableVertexAttribArray(y);w.disable(w.CULL_FACE);w.enable(w.BLEND);w.bindBuffer(w.ARRAY_BUFFER,
-K);w.vertexAttribPointer(v,2,w.FLOAT,!1,16,0);w.vertexAttribPointer(y,2,w.FLOAT,!1,16,8);w.bindBuffer(w.ELEMENT_ARRAY_BUFFER,x);w.uniformMatrix4fv(p,!1,B.projectionMatrix.elements);w.activeTexture(w.TEXTURE0);w.uniform1i(h,0);R=F=0;(H=A.fog)?(w.uniform3f(s,H.color.r,H.color.g,H.color.b),H instanceof THREE.Fog?(w.uniform1f(r,H.near),w.uniform1f(t,H.far),w.uniform1i(q,1),R=F=1):H instanceof THREE.FogExp2&&(w.uniform1f(m,H.density),w.uniform1i(q,2),R=F=2)):(w.uniform1i(q,0),R=F=0);for(var H=0,C=b.length;H<
-C;H++){var T=b[H];T._modelViewMatrix.multiplyMatrices(B.matrixWorldInverse,T.matrixWorld);T.z=null===T.renderDepth?-T._modelViewMatrix.elements[14]:T.renderDepth}b.sort(G);for(var Q=[],H=0,C=b.length;H<C;H++){var T=b[H],O=T.material;w.uniform1f(u,O.alphaTest);w.uniformMatrix4fv(n,!1,T._modelViewMatrix.elements);Q[0]=T.scale.x;Q[1]=T.scale.y;T=0;A.fog&&O.fog&&(T=R);F!==T&&(w.uniform1i(q,T),F=T);null!==O.map?(w.uniform2f(c,O.map.offset.x,O.map.offset.y),w.uniform2f(d,O.map.repeat.x,O.map.repeat.y)):
-(w.uniform2f(c,0,0),w.uniform2f(d,1,1));w.uniform1f(k,O.opacity);w.uniform3f(g,O.color.r,O.color.g,O.color.b);w.uniform1f(e,O.rotation);w.uniform2fv(f,Q);a.setBlending(O.blending,O.blendEquation,O.blendSrc,O.blendDst);a.setDepthTest(O.depthTest);a.setDepthWrite(O.depthWrite);O.map&&O.map.image&&O.map.image.width?a.setTexture(O.map,0):a.setTexture(E,0);w.drawElements(w.TRIANGLES,6,w.UNSIGNED_SHORT,0)}w.enable(w.CULL_FACE);a.resetGLState()}}};
-THREE.GeometryUtils={merge:function(a,b,c){console.warn("THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.");var d;b instanceof THREE.Mesh&&(b.matrixAutoUpdate&&b.updateMatrix(),d=b.matrix,b=b.geometry);a.merge(b,d,c)},center:function(a){console.warn("THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.");return a.center()}};
-THREE.ImageUtils={crossOrigin:void 0,loadTexture:function(a,b,c,d){var e=new THREE.ImageLoader;e.crossOrigin=this.crossOrigin;var f=new THREE.Texture(void 0,b);e.load(a,function(a){f.image=a;f.needsUpdate=!0;c&&c(f)},void 0,function(a){d&&d(a)});f.sourceFile=a;return f},loadTextureCube:function(a,b,c,d){var e=new THREE.ImageLoader;e.crossOrigin=this.crossOrigin;var f=new THREE.CubeTexture([],b);f.flipY=!1;var g=0;b=function(b){e.load(a[b],function(a){f.images[b]=a;g+=1;6===g&&(f.needsUpdate=!0,c&&
-c(f))})};d=0;for(var h=a.length;d<h;++d)b(d);return f},loadCompressedTexture:function(){console.error("THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.")},loadCompressedTextureCube:function(){console.error("THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.")},getNormalMap:function(a,b){var c=function(a){var b=Math.sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]);return[a[0]/b,a[1]/b,a[2]/b]};b|=1;var d=a.width,e=a.height,f=document.createElement("canvas");
-f.width=d;f.height=e;var g=f.getContext("2d");g.drawImage(a,0,0);for(var h=g.getImageData(0,0,d,e).data,k=g.createImageData(d,e),n=k.data,p=0;p<d;p++)for(var q=0;q<e;q++){var m=0>q-1?0:q-1,r=q+1>e-1?e-1:q+1,t=0>p-1?0:p-1,s=p+1>d-1?d-1:p+1,u=[],v=[0,0,h[4*(q*d+p)]/255*b];u.push([-1,0,h[4*(q*d+t)]/255*b]);u.push([-1,-1,h[4*(m*d+t)]/255*b]);u.push([0,-1,h[4*(m*d+p)]/255*b]);u.push([1,-1,h[4*(m*d+s)]/255*b]);u.push([1,0,h[4*(q*d+s)]/255*b]);u.push([1,1,h[4*(r*d+s)]/255*b]);u.push([0,1,h[4*(r*d+p)]/255*
-b]);u.push([-1,1,h[4*(r*d+t)]/255*b]);m=[];t=u.length;for(r=0;r<t;r++){var s=u[r],y=u[(r+1)%t],s=[s[0]-v[0],s[1]-v[1],s[2]-v[2]],y=[y[0]-v[0],y[1]-v[1],y[2]-v[2]];m.push(c([s[1]*y[2]-s[2]*y[1],s[2]*y[0]-s[0]*y[2],s[0]*y[1]-s[1]*y[0]]))}u=[0,0,0];for(r=0;r<m.length;r++)u[0]+=m[r][0],u[1]+=m[r][1],u[2]+=m[r][2];u[0]/=m.length;u[1]/=m.length;u[2]/=m.length;v=4*(q*d+p);n[v]=(u[0]+1)/2*255|0;n[v+1]=(u[1]+1)/2*255|0;n[v+2]=255*u[2]|0;n[v+3]=255}g.putImageData(k,0,0);return f},generateDataTexture:function(a,
-b,c){var d=a*b,e=new Uint8Array(3*d),f=Math.floor(255*c.r),g=Math.floor(255*c.g);c=Math.floor(255*c.b);for(var h=0;h<d;h++)e[3*h]=f,e[3*h+1]=g,e[3*h+2]=c;a=new THREE.DataTexture(e,a,b,THREE.RGBFormat);a.needsUpdate=!0;return a}};
-THREE.SceneUtils={createMultiMaterialObject:function(a,b){for(var c=new THREE.Object3D,d=0,e=b.length;d<e;d++)c.add(new THREE.Mesh(a,b[d]));return c},detach:function(a,b,c){a.applyMatrix(b.matrixWorld);b.remove(a);c.add(a)},attach:function(a,b,c){var d=new THREE.Matrix4;d.getInverse(c.matrixWorld);a.applyMatrix(d);b.remove(a);c.add(a)}};
-THREE.FontUtils={faces:{},face:"helvetiker",weight:"normal",style:"normal",size:150,divisions:10,getFace:function(){try{return this.faces[this.face][this.weight][this.style]}catch(a){throw"The font "+this.face+" with "+this.weight+" weight and "+this.style+" style is missing.";}},loadFace:function(a){var b=a.familyName.toLowerCase();this.faces[b]=this.faces[b]||{};this.faces[b][a.cssFontWeight]=this.faces[b][a.cssFontWeight]||{};this.faces[b][a.cssFontWeight][a.cssFontStyle]=a;return this.faces[b][a.cssFontWeight][a.cssFontStyle]=
-a},drawText:function(a){var b=this.getFace(),c=this.size/b.resolution,d=0,e=String(a).split(""),f=e.length,g=[];for(a=0;a<f;a++){var h=new THREE.Path,h=this.extractGlyphPoints(e[a],b,c,d,h),d=d+h.offset;g.push(h.path)}return{paths:g,offset:d/2}},extractGlyphPoints:function(a,b,c,d,e){var f=[],g,h,k,n,p,q,m,r,t,s,u,v=b.glyphs[a]||b.glyphs["?"];if(v){if(v.o)for(b=v._cachedOutline||(v._cachedOutline=v.o.split(" ")),n=b.length,a=0;a<n;)switch(k=b[a++],k){case "m":k=b[a++]*c+d;p=b[a++]*c;e.moveTo(k,p);
-break;case "l":k=b[a++]*c+d;p=b[a++]*c;e.lineTo(k,p);break;case "q":k=b[a++]*c+d;p=b[a++]*c;r=b[a++]*c+d;t=b[a++]*c;e.quadraticCurveTo(r,t,k,p);if(g=f[f.length-1])for(q=g.x,m=g.y,g=1,h=this.divisions;g<=h;g++){var y=g/h;THREE.Shape.Utils.b2(y,q,r,k);THREE.Shape.Utils.b2(y,m,t,p)}break;case "b":if(k=b[a++]*c+d,p=b[a++]*c,r=b[a++]*c+d,t=b[a++]*c,s=b[a++]*c+d,u=b[a++]*c,e.bezierCurveTo(r,t,s,u,k,p),g=f[f.length-1])for(q=g.x,m=g.y,g=1,h=this.divisions;g<=h;g++)y=g/h,THREE.Shape.Utils.b3(y,q,r,s,k),THREE.Shape.Utils.b3(y,
-m,t,u,p)}return{offset:v.ha*c,path:e}}}};
-THREE.FontUtils.generateShapes=function(a,b){b=b||{};var c=void 0!==b.curveSegments?b.curveSegments:4,d=void 0!==b.font?b.font:"helvetiker",e=void 0!==b.weight?b.weight:"normal",f=void 0!==b.style?b.style:"normal";THREE.FontUtils.size=void 0!==b.size?b.size:100;THREE.FontUtils.divisions=c;THREE.FontUtils.face=d;THREE.FontUtils.weight=e;THREE.FontUtils.style=f;c=THREE.FontUtils.drawText(a).paths;d=[];e=0;for(f=c.length;e<f;e++)Array.prototype.push.apply(d,c[e].toShapes());return d};
-(function(a){var b=function(a){for(var b=a.length,e=0,f=b-1,g=0;g<b;f=g++)e+=a[f].x*a[g].y-a[g].x*a[f].y;return.5*e};a.Triangulate=function(a,d){var e=a.length;if(3>e)return null;var f=[],g=[],h=[],k,n,p;if(0<b(a))for(n=0;n<e;n++)g[n]=n;else for(n=0;n<e;n++)g[n]=e-1-n;var q=2*e;for(n=e-1;2<e;){if(0>=q--){console.log("Warning, unable to triangulate polygon!");break}k=n;e<=k&&(k=0);n=k+1;e<=n&&(n=0);p=n+1;e<=p&&(p=0);var m;a:{var r=m=void 0,t=void 0,s=void 0,u=void 0,v=void 0,y=void 0,G=void 0,w=void 0,
-r=a[g[k]].x,t=a[g[k]].y,s=a[g[n]].x,u=a[g[n]].y,v=a[g[p]].x,y=a[g[p]].y;if(1E-10>(s-r)*(y-t)-(u-t)*(v-r))m=!1;else{var K=void 0,x=void 0,D=void 0,E=void 0,A=void 0,B=void 0,F=void 0,R=void 0,H=void 0,C=void 0,H=R=F=w=G=void 0,K=v-s,x=y-u,D=r-v,E=t-y,A=s-r,B=u-t;for(m=0;m<e;m++)if(G=a[g[m]].x,w=a[g[m]].y,!(G===r&&w===t||G===s&&w===u||G===v&&w===y)&&(F=G-r,R=w-t,H=G-s,C=w-u,G-=v,w-=y,H=K*C-x*H,F=A*R-B*F,R=D*w-E*G,-1E-10<=H&&-1E-10<=R&&-1E-10<=F)){m=!1;break a}m=!0}}if(m){f.push([a[g[k]],a[g[n]],a[g[p]]]);
-h.push([g[k],g[n],g[p]]);k=n;for(p=n+1;p<e;k++,p++)g[k]=g[p];e--;q=2*e}}return d?h:f};a.Triangulate.area=b;return a})(THREE.FontUtils);self._typeface_js={faces:THREE.FontUtils.faces,loadFace:THREE.FontUtils.loadFace};THREE.typeface_js=self._typeface_js;
-THREE.Audio=function(a){THREE.Object3D.call(this);this.type="Audio";this.context=a.context;this.source=this.context.createBufferSource();this.gain=this.context.createGain();this.gain.connect(this.context.destination);this.panner=this.context.createPanner();this.panner.connect(this.gain)};THREE.Audio.prototype=Object.create(THREE.Object3D.prototype);
-THREE.Audio.prototype.load=function(a){var b=this,c=new XMLHttpRequest;c.open("GET",a,!0);c.responseType="arraybuffer";c.onload=function(a){b.context.decodeAudioData(this.response,function(a){b.source.buffer=a;b.source.connect(b.panner);b.source.start(0)})};c.send();return this};THREE.Audio.prototype.setLoop=function(a){this.source.loop=a};THREE.Audio.prototype.setRefDistance=function(a){this.panner.refDistance=a};THREE.Audio.prototype.setRolloffFactor=function(a){this.panner.rolloffFactor=a};
-THREE.Audio.prototype.updateMatrixWorld=function(){var a=new THREE.Vector3;return function(b){THREE.Object3D.prototype.updateMatrixWorld.call(this,b);a.setFromMatrixPosition(this.matrixWorld);this.panner.setPosition(a.x,a.y,a.z)}}();THREE.AudioListener=function(){THREE.Object3D.call(this);this.type="AudioListener";this.context=new (window.AudioContext||window.webkitAudioContext)};THREE.AudioListener.prototype=Object.create(THREE.Object3D.prototype);
-THREE.AudioListener.prototype.updateMatrixWorld=function(){var a=new THREE.Vector3,b=new THREE.Quaternion,c=new THREE.Vector3,d=new THREE.Vector3,e=new THREE.Vector3,f=new THREE.Vector3;return function(g){THREE.Object3D.prototype.updateMatrixWorld.call(this,g);g=this.context.listener;this.matrixWorld.decompose(a,b,c);d.set(0,0,-1).applyQuaternion(b);e.subVectors(a,f);g.setPosition(a.x,a.y,a.z);g.setOrientation(d.x,d.y,d.z,this.up.x,this.up.y,this.up.z);g.setVelocity(e.x,e.y,e.z);f.copy(a)}}();
-THREE.Curve=function(){};THREE.Curve.prototype.getPoint=function(a){console.log("Warning, getPoint() not implemented!");return null};THREE.Curve.prototype.getPointAt=function(a){a=this.getUtoTmapping(a);return this.getPoint(a)};THREE.Curve.prototype.getPoints=function(a){a||(a=5);var b,c=[];for(b=0;b<=a;b++)c.push(this.getPoint(b/a));return c};THREE.Curve.prototype.getSpacedPoints=function(a){a||(a=5);var b,c=[];for(b=0;b<=a;b++)c.push(this.getPointAt(b/a));return c};
-THREE.Curve.prototype.getLength=function(){var a=this.getLengths();return a[a.length-1]};THREE.Curve.prototype.getLengths=function(a){a||(a=this.__arcLengthDivisions?this.__arcLengthDivisions:200);if(this.cacheArcLengths&&this.cacheArcLengths.length==a+1&&!this.needsUpdate)return this.cacheArcLengths;this.needsUpdate=!1;var b=[],c,d=this.getPoint(0),e,f=0;b.push(0);for(e=1;e<=a;e++)c=this.getPoint(e/a),f+=c.distanceTo(d),b.push(f),d=c;return this.cacheArcLengths=b};
-THREE.Curve.prototype.updateArcLengths=function(){this.needsUpdate=!0;this.getLengths()};THREE.Curve.prototype.getUtoTmapping=function(a,b){var c=this.getLengths(),d=0,e=c.length,f;f=b?b:a*c[e-1];for(var g=0,h=e-1,k;g<=h;)if(d=Math.floor(g+(h-g)/2),k=c[d]-f,0>k)g=d+1;else if(0<k)h=d-1;else{h=d;break}d=h;if(c[d]==f)return d/(e-1);g=c[d];return c=(d+(f-g)/(c[d+1]-g))/(e-1)};THREE.Curve.prototype.getTangent=function(a){var b=a-1E-4;a+=1E-4;0>b&&(b=0);1<a&&(a=1);b=this.getPoint(b);return this.getPoint(a).clone().sub(b).normalize()};
-THREE.Curve.prototype.getTangentAt=function(a){a=this.getUtoTmapping(a);return this.getTangent(a)};
-THREE.Curve.Utils={tangentQuadraticBezier:function(a,b,c,d){return 2*(1-a)*(c-b)+2*a*(d-c)},tangentCubicBezier:function(a,b,c,d,e){return-3*b*(1-a)*(1-a)+3*c*(1-a)*(1-a)-6*a*c*(1-a)+6*a*d*(1-a)-3*a*a*d+3*a*a*e},tangentSpline:function(a,b,c,d,e){return 6*a*a-6*a+(3*a*a-4*a+1)+(-6*a*a+6*a)+(3*a*a-2*a)},interpolate:function(a,b,c,d,e){a=.5*(c-a);d=.5*(d-b);var f=e*e;return(2*b-2*c+a+d)*e*f+(-3*b+3*c-2*a-d)*f+a*e+b}};
-THREE.Curve.create=function(a,b){a.prototype=Object.create(THREE.Curve.prototype);a.prototype.getPoint=b;return a};THREE.CurvePath=function(){this.curves=[];this.bends=[];this.autoClose=!1};THREE.CurvePath.prototype=Object.create(THREE.Curve.prototype);THREE.CurvePath.prototype.add=function(a){this.curves.push(a)};THREE.CurvePath.prototype.checkConnection=function(){};
-THREE.CurvePath.prototype.closePath=function(){var a=this.curves[0].getPoint(0),b=this.curves[this.curves.length-1].getPoint(1);a.equals(b)||this.curves.push(new THREE.LineCurve(b,a))};THREE.CurvePath.prototype.getPoint=function(a){var b=a*this.getLength(),c=this.getCurveLengths();for(a=0;a<c.length;){if(c[a]>=b)return b=c[a]-b,a=this.curves[a],b=1-b/a.getLength(),a.getPointAt(b);a++}return null};THREE.CurvePath.prototype.getLength=function(){var a=this.getCurveLengths();return a[a.length-1]};
-THREE.CurvePath.prototype.getCurveLengths=function(){if(this.cacheLengths&&this.cacheLengths.length==this.curves.length)return this.cacheLengths;var a=[],b=0,c,d=this.curves.length;for(c=0;c<d;c++)b+=this.curves[c].getLength(),a.push(b);return this.cacheLengths=a};
-THREE.CurvePath.prototype.getBoundingBox=function(){var a=this.getPoints(),b,c,d,e,f,g;b=c=Number.NEGATIVE_INFINITY;e=f=Number.POSITIVE_INFINITY;var h,k,n,p,q=a[0]instanceof THREE.Vector3;p=q?new THREE.Vector3:new THREE.Vector2;k=0;for(n=a.length;k<n;k++)h=a[k],h.x>b?b=h.x:h.x<e&&(e=h.x),h.y>c?c=h.y:h.y<f&&(f=h.y),q&&(h.z>d?d=h.z:h.z<g&&(g=h.z)),p.add(h);a={minX:e,minY:f,maxX:b,maxY:c};q&&(a.maxZ=d,a.minZ=g);return a};
-THREE.CurvePath.prototype.createPointsGeometry=function(a){a=this.getPoints(a,!0);return this.createGeometry(a)};THREE.CurvePath.prototype.createSpacedPointsGeometry=function(a){a=this.getSpacedPoints(a,!0);return this.createGeometry(a)};THREE.CurvePath.prototype.createGeometry=function(a){for(var b=new THREE.Geometry,c=0;c<a.length;c++)b.vertices.push(new THREE.Vector3(a[c].x,a[c].y,a[c].z||0));return b};THREE.CurvePath.prototype.addWrapPath=function(a){this.bends.push(a)};
-THREE.CurvePath.prototype.getTransformedPoints=function(a,b){var c=this.getPoints(a),d,e;b||(b=this.bends);d=0;for(e=b.length;d<e;d++)c=this.getWrapPoints(c,b[d]);return c};THREE.CurvePath.prototype.getTransformedSpacedPoints=function(a,b){var c=this.getSpacedPoints(a),d,e;b||(b=this.bends);d=0;for(e=b.length;d<e;d++)c=this.getWrapPoints(c,b[d]);return c};
-THREE.CurvePath.prototype.getWrapPoints=function(a,b){var c=this.getBoundingBox(),d,e,f,g,h,k;d=0;for(e=a.length;d<e;d++)f=a[d],g=f.x,h=f.y,k=g/c.maxX,k=b.getUtoTmapping(k,g),g=b.getPoint(k),k=b.getTangent(k),k.set(-k.y,k.x).multiplyScalar(h),f.x=g.x+k.x,f.y=g.y+k.y;return a};THREE.Gyroscope=function(){THREE.Object3D.call(this)};THREE.Gyroscope.prototype=Object.create(THREE.Object3D.prototype);
-THREE.Gyroscope.prototype.updateMatrixWorld=function(){var a=new THREE.Vector3,b=new THREE.Quaternion,c=new THREE.Vector3,d=new THREE.Vector3,e=new THREE.Quaternion,f=new THREE.Vector3;return function(g){this.matrixAutoUpdate&&this.updateMatrix();if(this.matrixWorldNeedsUpdate||g)this.parent?(this.matrixWorld.multiplyMatrices(this.parent.matrixWorld,this.matrix),this.matrixWorld.decompose(d,e,f),this.matrix.decompose(a,b,c),this.matrixWorld.compose(d,b,f)):this.matrixWorld.copy(this.matrix),this.matrixWorldNeedsUpdate=
-!1,g=!0;for(var h=0,k=this.children.length;h<k;h++)this.children[h].updateMatrixWorld(g)}}();THREE.Path=function(a){THREE.CurvePath.call(this);this.actions=[];a&&this.fromPoints(a)};THREE.Path.prototype=Object.create(THREE.CurvePath.prototype);THREE.PathActions={MOVE_TO:"moveTo",LINE_TO:"lineTo",QUADRATIC_CURVE_TO:"quadraticCurveTo",BEZIER_CURVE_TO:"bezierCurveTo",CSPLINE_THRU:"splineThru",ARC:"arc",ELLIPSE:"ellipse"};
-THREE.Path.prototype.fromPoints=function(a){this.moveTo(a[0].x,a[0].y);for(var b=1,c=a.length;b<c;b++)this.lineTo(a[b].x,a[b].y)};THREE.Path.prototype.moveTo=function(a,b){var c=Array.prototype.slice.call(arguments);this.actions.push({action:THREE.PathActions.MOVE_TO,args:c})};
-THREE.Path.prototype.lineTo=function(a,b){var c=Array.prototype.slice.call(arguments),d=this.actions[this.actions.length-1].args,d=new THREE.LineCurve(new THREE.Vector2(d[d.length-2],d[d.length-1]),new THREE.Vector2(a,b));this.curves.push(d);this.actions.push({action:THREE.PathActions.LINE_TO,args:c})};
-THREE.Path.prototype.quadraticCurveTo=function(a,b,c,d){var e=Array.prototype.slice.call(arguments),f=this.actions[this.actions.length-1].args,f=new THREE.QuadraticBezierCurve(new THREE.Vector2(f[f.length-2],f[f.length-1]),new THREE.Vector2(a,b),new THREE.Vector2(c,d));this.curves.push(f);this.actions.push({action:THREE.PathActions.QUADRATIC_CURVE_TO,args:e})};
-THREE.Path.prototype.bezierCurveTo=function(a,b,c,d,e,f){var g=Array.prototype.slice.call(arguments),h=this.actions[this.actions.length-1].args,h=new THREE.CubicBezierCurve(new THREE.Vector2(h[h.length-2],h[h.length-1]),new THREE.Vector2(a,b),new THREE.Vector2(c,d),new THREE.Vector2(e,f));this.curves.push(h);this.actions.push({action:THREE.PathActions.BEZIER_CURVE_TO,args:g})};
-THREE.Path.prototype.splineThru=function(a){var b=Array.prototype.slice.call(arguments),c=this.actions[this.actions.length-1].args,c=[new THREE.Vector2(c[c.length-2],c[c.length-1])];Array.prototype.push.apply(c,a);c=new THREE.SplineCurve(c);this.curves.push(c);this.actions.push({action:THREE.PathActions.CSPLINE_THRU,args:b})};THREE.Path.prototype.arc=function(a,b,c,d,e,f){var g=this.actions[this.actions.length-1].args;this.absarc(a+g[g.length-2],b+g[g.length-1],c,d,e,f)};
-THREE.Path.prototype.absarc=function(a,b,c,d,e,f){this.absellipse(a,b,c,c,d,e,f)};THREE.Path.prototype.ellipse=function(a,b,c,d,e,f,g){var h=this.actions[this.actions.length-1].args;this.absellipse(a+h[h.length-2],b+h[h.length-1],c,d,e,f,g)};THREE.Path.prototype.absellipse=function(a,b,c,d,e,f,g){var h=Array.prototype.slice.call(arguments),k=new THREE.EllipseCurve(a,b,c,d,e,f,g);this.curves.push(k);k=k.getPoint(1);h.push(k.x);h.push(k.y);this.actions.push({action:THREE.PathActions.ELLIPSE,args:h})};
-THREE.Path.prototype.getSpacedPoints=function(a,b){a||(a=40);for(var c=[],d=0;d<a;d++)c.push(this.getPoint(d/a));return c};
-THREE.Path.prototype.getPoints=function(a,b){if(this.useSpacedPoints)return console.log("tata"),this.getSpacedPoints(a,b);a=a||12;var c=[],d,e,f,g,h,k,n,p,q,m,r,t,s;d=0;for(e=this.actions.length;d<e;d++)switch(f=this.actions[d],g=f.action,f=f.args,g){case THREE.PathActions.MOVE_TO:c.push(new THREE.Vector2(f[0],f[1]));break;case THREE.PathActions.LINE_TO:c.push(new THREE.Vector2(f[0],f[1]));break;case THREE.PathActions.QUADRATIC_CURVE_TO:h=f[2];k=f[3];q=f[0];m=f[1];0<c.length?(g=c[c.length-1],r=g.x,
-t=g.y):(g=this.actions[d-1].args,r=g[g.length-2],t=g[g.length-1]);for(f=1;f<=a;f++)s=f/a,g=THREE.Shape.Utils.b2(s,r,q,h),s=THREE.Shape.Utils.b2(s,t,m,k),c.push(new THREE.Vector2(g,s));break;case THREE.PathActions.BEZIER_CURVE_TO:h=f[4];k=f[5];q=f[0];m=f[1];n=f[2];p=f[3];0<c.length?(g=c[c.length-1],r=g.x,t=g.y):(g=this.actions[d-1].args,r=g[g.length-2],t=g[g.length-1]);for(f=1;f<=a;f++)s=f/a,g=THREE.Shape.Utils.b3(s,r,q,n,h),s=THREE.Shape.Utils.b3(s,t,m,p,k),c.push(new THREE.Vector2(g,s));break;case THREE.PathActions.CSPLINE_THRU:g=
-this.actions[d-1].args;s=[new THREE.Vector2(g[g.length-2],g[g.length-1])];g=a*f[0].length;s=s.concat(f[0]);s=new THREE.SplineCurve(s);for(f=1;f<=g;f++)c.push(s.getPointAt(f/g));break;case THREE.PathActions.ARC:h=f[0];k=f[1];m=f[2];n=f[3];g=f[4];q=!!f[5];r=g-n;t=2*a;for(f=1;f<=t;f++)s=f/t,q||(s=1-s),s=n+s*r,g=h+m*Math.cos(s),s=k+m*Math.sin(s),c.push(new THREE.Vector2(g,s));break;case THREE.PathActions.ELLIPSE:for(h=f[0],k=f[1],m=f[2],p=f[3],n=f[4],g=f[5],q=!!f[6],r=g-n,t=2*a,f=1;f<=t;f++)s=f/t,q||
-(s=1-s),s=n+s*r,g=h+m*Math.cos(s),s=k+p*Math.sin(s),c.push(new THREE.Vector2(g,s))}d=c[c.length-1];1E-10>Math.abs(d.x-c[0].x)&&1E-10>Math.abs(d.y-c[0].y)&&c.splice(c.length-1,1);b&&c.push(c[0]);return c};
-THREE.Path.prototype.toShapes=function(a,b){function c(a){for(var b=[],c=0,d=a.length;c<d;c++){var e=a[c],f=new THREE.Shape;f.actions=e.actions;f.curves=e.curves;b.push(f)}return b}function d(a,b){for(var c=b.length,d=!1,e=c-1,f=0;f<c;e=f++){var g=b[e],h=b[f],k=h.x-g.x,m=h.y-g.y;if(1E-10<Math.abs(m)){if(0>m&&(g=b[f],k=-k,h=b[e],m=-m),!(a.y<g.y||a.y>h.y))if(a.y==g.y){if(a.x==g.x)return!0}else{e=m*(a.x-g.x)-k*(a.y-g.y);if(0==e)return!0;0>e||(d=!d)}}else if(a.y==g.y&&(h.x<=a.x&&a.x<=g.x||g.x<=a.x&&a.x<=
-h.x))return!0}return d}var e=function(a){var b,c,d,e,f=[],g=new THREE.Path;b=0;for(c=a.length;b<c;b++)d=a[b],e=d.args,d=d.action,d==THREE.PathActions.MOVE_TO&&0!=g.actions.length&&(f.push(g),g=new THREE.Path),g[d].apply(g,e);0!=g.actions.length&&f.push(g);return f}(this.actions);if(0==e.length)return[];if(!0===b)return c(e);var f,g,h,k=[];if(1==e.length)return g=e[0],h=new THREE.Shape,h.actions=g.actions,h.curves=g.curves,k.push(h),k;var n=!THREE.Shape.Utils.isClockWise(e[0].getPoints()),n=a?!n:n;
-h=[];var p=[],q=[],m=0,r;p[m]=void 0;q[m]=[];var t,s;t=0;for(s=e.length;t<s;t++)g=e[t],r=g.getPoints(),f=THREE.Shape.Utils.isClockWise(r),(f=a?!f:f)?(!n&&p[m]&&m++,p[m]={s:new THREE.Shape,p:r},p[m].s.actions=g.actions,p[m].s.curves=g.curves,n&&m++,q[m]=[]):q[m].push({h:g,p:r[0]});if(!p[0])return c(e);if(1<p.length){t=!1;s=[];g=0;for(e=p.length;g<e;g++)h[g]=[];g=0;for(e=p.length;g<e;g++)for(f=q[g],n=0;n<f.length;n++){m=f[n];r=!0;for(var u=0;u<p.length;u++)d(m.p,p[u].p)&&(g!=u&&s.push({froms:g,tos:u,
-hole:n}),r?(r=!1,h[u].push(m)):t=!0);r&&h[g].push(m)}0<s.length&&(t||(q=h))}t=0;for(s=p.length;t<s;t++)for(h=p[t].s,k.push(h),g=q[t],e=0,f=g.length;e<f;e++)h.holes.push(g[e].h);return k};THREE.Shape=function(){THREE.Path.apply(this,arguments);this.holes=[]};THREE.Shape.prototype=Object.create(THREE.Path.prototype);THREE.Shape.prototype.extrude=function(a){return new THREE.ExtrudeGeometry(this,a)};THREE.Shape.prototype.makeGeometry=function(a){return new THREE.ShapeGeometry(this,a)};
-THREE.Shape.prototype.getPointsHoles=function(a){var b,c=this.holes.length,d=[];for(b=0;b<c;b++)d[b]=this.holes[b].getTransformedPoints(a,this.bends);return d};THREE.Shape.prototype.getSpacedPointsHoles=function(a){var b,c=this.holes.length,d=[];for(b=0;b<c;b++)d[b]=this.holes[b].getTransformedSpacedPoints(a,this.bends);return d};THREE.Shape.prototype.extractAllPoints=function(a){return{shape:this.getTransformedPoints(a),holes:this.getPointsHoles(a)}};
-THREE.Shape.prototype.extractPoints=function(a){return this.useSpacedPoints?this.extractAllSpacedPoints(a):this.extractAllPoints(a)};THREE.Shape.prototype.extractAllSpacedPoints=function(a){return{shape:this.getTransformedSpacedPoints(a),holes:this.getSpacedPointsHoles(a)}};
-THREE.Shape.Utils={triangulateShape:function(a,b){function c(a,b,c){return a.x!=b.x?a.x<b.x?a.x<=c.x&&c.x<=b.x:b.x<=c.x&&c.x<=a.x:a.y<b.y?a.y<=c.y&&c.y<=b.y:b.y<=c.y&&c.y<=a.y}function d(a,b,d,e,f){var g=b.x-a.x,h=b.y-a.y,k=e.x-d.x,n=e.y-d.y,p=a.x-d.x,q=a.y-d.y,D=h*k-g*n,E=h*p-g*q;if(1E-10<Math.abs(D)){if(0<D){if(0>E||E>D)return[];k=n*p-k*q;if(0>k||k>D)return[]}else{if(0<E||E<D)return[];k=n*p-k*q;if(0<k||k<D)return[]}if(0==k)return!f||0!=E&&E!=D?[a]:[];if(k==D)return!f||0!=E&&E!=D?[b]:[];if(0==E)return[d];
-if(E==D)return[e];f=k/D;return[{x:a.x+f*g,y:a.y+f*h}]}if(0!=E||n*p!=k*q)return[];h=0==g&&0==h;k=0==k&&0==n;if(h&&k)return a.x!=d.x||a.y!=d.y?[]:[a];if(h)return c(d,e,a)?[a]:[];if(k)return c(a,b,d)?[d]:[];0!=g?(a.x<b.x?(g=a,k=a.x,h=b,a=b.x):(g=b,k=b.x,h=a,a=a.x),d.x<e.x?(b=d,D=d.x,n=e,d=e.x):(b=e,D=e.x,n=d,d=d.x)):(a.y<b.y?(g=a,k=a.y,h=b,a=b.y):(g=b,k=b.y,h=a,a=a.y),d.y<e.y?(b=d,D=d.y,n=e,d=e.y):(b=e,D=e.y,n=d,d=d.y));return k<=D?a<D?[]:a==D?f?[]:[b]:a<=d?[b,h]:[b,n]:k>d?[]:k==d?f?[]:[g]:a<=d?[g,h]:
-[g,n]}function e(a,b,c,d){var e=b.x-a.x,f=b.y-a.y;b=c.x-a.x;c=c.y-a.y;var g=d.x-a.x;d=d.y-a.y;a=e*c-f*b;e=e*d-f*g;return 1E-10<Math.abs(a)?(b=g*c-d*b,0<a?0<=e&&0<=b:0<=e||0<=b):0<e}var f,g,h,k,n,p={};h=a.concat();f=0;for(g=b.length;f<g;f++)Array.prototype.push.apply(h,b[f]);f=0;for(g=h.length;f<g;f++)n=h[f].x+":"+h[f].y,void 0!==p[n]&&console.log("Duplicate point",n),p[n]=f;f=function(a,b){function c(a,b){var d=h.length-1,f=a-1;0>f&&(f=d);var g=a+1;g>d&&(g=0);d=e(h[a],h[f],h[g],k[b]);if(!d)return!1;
-d=k.length-1;f=b-1;0>f&&(f=d);g=b+1;g>d&&(g=0);return(d=e(k[b],k[f],k[g],h[a]))?!0:!1}function f(a,b){var c,e;for(c=0;c<h.length;c++)if(e=c+1,e%=h.length,e=d(a,b,h[c],h[e],!0),0<e.length)return!0;return!1}function g(a,c){var e,f,h,k;for(e=0;e<n.length;e++)for(f=b[n[e]],h=0;h<f.length;h++)if(k=h+1,k%=f.length,k=d(a,c,f[h],f[k],!0),0<k.length)return!0;return!1}var h=a.concat(),k,n=[],p,q,x,D,E,A=[],B,F,R,H=0;for(p=b.length;H<p;H++)n.push(H);B=0;for(var C=2*n.length;0<n.length;){C--;if(0>C){console.log("Infinite Loop! Holes left:"+
-n.length+", Probably Hole outside Shape!");break}for(q=B;q<h.length;q++){x=h[q];p=-1;for(H=0;H<n.length;H++)if(D=n[H],E=x.x+":"+x.y+":"+D,void 0===A[E]){k=b[D];for(F=0;F<k.length;F++)if(D=k[F],c(q,F)&&!f(x,D)&&!g(x,D)){p=F;n.splice(H,1);B=h.slice(0,q+1);D=h.slice(q);F=k.slice(p);R=k.slice(0,p+1);h=B.concat(F).concat(R).concat(D);B=q;break}if(0<=p)break;A[E]=!0}if(0<=p)break}}return h}(a,b);var q=THREE.FontUtils.Triangulate(f,!1);f=0;for(g=q.length;f<g;f++)for(k=q[f],h=0;3>h;h++)n=k[h].x+":"+k[h].y,
-n=p[n],void 0!==n&&(k[h]=n);return q.concat()},isClockWise:function(a){return 0>THREE.FontUtils.Triangulate.area(a)},b2p0:function(a,b){var c=1-a;return c*c*b},b2p1:function(a,b){return 2*(1-a)*a*b},b2p2:function(a,b){return a*a*b},b2:function(a,b,c,d){return this.b2p0(a,b)+this.b2p1(a,c)+this.b2p2(a,d)},b3p0:function(a,b){var c=1-a;return c*c*c*b},b3p1:function(a,b){var c=1-a;return 3*c*c*a*b},b3p2:function(a,b){return 3*(1-a)*a*a*b},b3p3:function(a,b){return a*a*a*b},b3:function(a,b,c,d,e){return this.b3p0(a,
-b)+this.b3p1(a,c)+this.b3p2(a,d)+this.b3p3(a,e)}};THREE.LineCurve=function(a,b){this.v1=a;this.v2=b};THREE.LineCurve.prototype=Object.create(THREE.Curve.prototype);THREE.LineCurve.prototype.getPoint=function(a){var b=this.v2.clone().sub(this.v1);b.multiplyScalar(a).add(this.v1);return b};THREE.LineCurve.prototype.getPointAt=function(a){return this.getPoint(a)};THREE.LineCurve.prototype.getTangent=function(a){return this.v2.clone().sub(this.v1).normalize()};
-THREE.QuadraticBezierCurve=function(a,b,c){this.v0=a;this.v1=b;this.v2=c};THREE.QuadraticBezierCurve.prototype=Object.create(THREE.Curve.prototype);THREE.QuadraticBezierCurve.prototype.getPoint=function(a){var b=new THREE.Vector2;b.x=THREE.Shape.Utils.b2(a,this.v0.x,this.v1.x,this.v2.x);b.y=THREE.Shape.Utils.b2(a,this.v0.y,this.v1.y,this.v2.y);return b};
-THREE.QuadraticBezierCurve.prototype.getTangent=function(a){var b=new THREE.Vector2;b.x=THREE.Curve.Utils.tangentQuadraticBezier(a,this.v0.x,this.v1.x,this.v2.x);b.y=THREE.Curve.Utils.tangentQuadraticBezier(a,this.v0.y,this.v1.y,this.v2.y);return b.normalize()};THREE.CubicBezierCurve=function(a,b,c,d){this.v0=a;this.v1=b;this.v2=c;this.v3=d};THREE.CubicBezierCurve.prototype=Object.create(THREE.Curve.prototype);
-THREE.CubicBezierCurve.prototype.getPoint=function(a){var b;b=THREE.Shape.Utils.b3(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x);a=THREE.Shape.Utils.b3(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y);return new THREE.Vector2(b,a)};THREE.CubicBezierCurve.prototype.getTangent=function(a){var b;b=THREE.Curve.Utils.tangentCubicBezier(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x);a=THREE.Curve.Utils.tangentCubicBezier(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y);b=new THREE.Vector2(b,a);b.normalize();return b};
-THREE.SplineCurve=function(a){this.points=void 0==a?[]:a};THREE.SplineCurve.prototype=Object.create(THREE.Curve.prototype);THREE.SplineCurve.prototype.getPoint=function(a){var b=this.points;a*=b.length-1;var c=Math.floor(a);a-=c;var d=b[0==c?c:c-1],e=b[c],f=b[c>b.length-2?b.length-1:c+1],b=b[c>b.length-3?b.length-1:c+2],c=new THREE.Vector2;c.x=THREE.Curve.Utils.interpolate(d.x,e.x,f.x,b.x,a);c.y=THREE.Curve.Utils.interpolate(d.y,e.y,f.y,b.y,a);return c};
-THREE.EllipseCurve=function(a,b,c,d,e,f,g){this.aX=a;this.aY=b;this.xRadius=c;this.yRadius=d;this.aStartAngle=e;this.aEndAngle=f;this.aClockwise=g};THREE.EllipseCurve.prototype=Object.create(THREE.Curve.prototype);
-THREE.EllipseCurve.prototype.getPoint=function(a){var b=this.aEndAngle-this.aStartAngle;0>b&&(b+=2*Math.PI);b>2*Math.PI&&(b-=2*Math.PI);a=!0===this.aClockwise?this.aEndAngle+(1-a)*(2*Math.PI-b):this.aStartAngle+a*b;b=new THREE.Vector2;b.x=this.aX+this.xRadius*Math.cos(a);b.y=this.aY+this.yRadius*Math.sin(a);return b};THREE.ArcCurve=function(a,b,c,d,e,f){THREE.EllipseCurve.call(this,a,b,c,c,d,e,f)};THREE.ArcCurve.prototype=Object.create(THREE.EllipseCurve.prototype);
-THREE.LineCurve3=THREE.Curve.create(function(a,b){this.v1=a;this.v2=b},function(a){var b=new THREE.Vector3;b.subVectors(this.v2,this.v1);b.multiplyScalar(a);b.add(this.v1);return b});THREE.QuadraticBezierCurve3=THREE.Curve.create(function(a,b,c){this.v0=a;this.v1=b;this.v2=c},function(a){var b=new THREE.Vector3;b.x=THREE.Shape.Utils.b2(a,this.v0.x,this.v1.x,this.v2.x);b.y=THREE.Shape.Utils.b2(a,this.v0.y,this.v1.y,this.v2.y);b.z=THREE.Shape.Utils.b2(a,this.v0.z,this.v1.z,this.v2.z);return b});
-THREE.CubicBezierCurve3=THREE.Curve.create(function(a,b,c,d){this.v0=a;this.v1=b;this.v2=c;this.v3=d},function(a){var b=new THREE.Vector3;b.x=THREE.Shape.Utils.b3(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x);b.y=THREE.Shape.Utils.b3(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y);b.z=THREE.Shape.Utils.b3(a,this.v0.z,this.v1.z,this.v2.z,this.v3.z);return b});
-THREE.SplineCurve3=THREE.Curve.create(function(a){this.points=void 0==a?[]:a},function(a){var b=this.points;a*=b.length-1;var c=Math.floor(a);a-=c;var d=b[0==c?c:c-1],e=b[c],f=b[c>b.length-2?b.length-1:c+1],b=b[c>b.length-3?b.length-1:c+2],c=new THREE.Vector3;c.x=THREE.Curve.Utils.interpolate(d.x,e.x,f.x,b.x,a);c.y=THREE.Curve.Utils.interpolate(d.y,e.y,f.y,b.y,a);c.z=THREE.Curve.Utils.interpolate(d.z,e.z,f.z,b.z,a);return c});
-THREE.ClosedSplineCurve3=THREE.Curve.create(function(a){this.points=void 0==a?[]:a},function(a){var b=this.points;a*=b.length-0;var c=Math.floor(a);a-=c;var c=c+(0<c?0:(Math.floor(Math.abs(c)/b.length)+1)*b.length),d=b[(c-1)%b.length],e=b[c%b.length],f=b[(c+1)%b.length],b=b[(c+2)%b.length],c=new THREE.Vector3;c.x=THREE.Curve.Utils.interpolate(d.x,e.x,f.x,b.x,a);c.y=THREE.Curve.Utils.interpolate(d.y,e.y,f.y,b.y,a);c.z=THREE.Curve.Utils.interpolate(d.z,e.z,f.z,b.z,a);return c});
-THREE.AnimationHandler={LINEAR:0,CATMULLROM:1,CATMULLROM_FORWARD:2,add:function(){console.warn("THREE.AnimationHandler.add() has been deprecated.")},get:function(){console.warn("THREE.AnimationHandler.get() has been deprecated.")},remove:function(){console.warn("THREE.AnimationHandler.remove() has been deprecated.")},animations:[],init:function(a){if(!0!==a.initialized){for(var b=0;b<a.hierarchy.length;b++){for(var c=0;c<a.hierarchy[b].keys.length;c++)if(0>a.hierarchy[b].keys[c].time&&(a.hierarchy[b].keys[c].time=
-0),void 0!==a.hierarchy[b].keys[c].rot&&!(a.hierarchy[b].keys[c].rot instanceof THREE.Quaternion)){var d=a.hierarchy[b].keys[c].rot;a.hierarchy[b].keys[c].rot=(new THREE.Quaternion).fromArray(d)}if(a.hierarchy[b].keys.length&&void 0!==a.hierarchy[b].keys[0].morphTargets){d={};for(c=0;c<a.hierarchy[b].keys.length;c++)for(var e=0;e<a.hierarchy[b].keys[c].morphTargets.length;e++){var f=a.hierarchy[b].keys[c].morphTargets[e];d[f]=-1}a.hierarchy[b].usedMorphTargets=d;for(c=0;c<a.hierarchy[b].keys.length;c++){var g=
-{};for(f in d){for(e=0;e<a.hierarchy[b].keys[c].morphTargets.length;e++)if(a.hierarchy[b].keys[c].morphTargets[e]===f){g[f]=a.hierarchy[b].keys[c].morphTargetsInfluences[e];break}e===a.hierarchy[b].keys[c].morphTargets.length&&(g[f]=0)}a.hierarchy[b].keys[c].morphTargetsInfluences=g}}for(c=1;c<a.hierarchy[b].keys.length;c++)a.hierarchy[b].keys[c].time===a.hierarchy[b].keys[c-1].time&&(a.hierarchy[b].keys.splice(c,1),c--);for(c=0;c<a.hierarchy[b].keys.length;c++)a.hierarchy[b].keys[c].index=c}a.initialized=
-!0;return a}},parse:function(a){var b=function(a,c){c.push(a);for(var d=0;d<a.children.length;d++)b(a.children[d],c)},c=[];if(a instanceof THREE.SkinnedMesh)for(var d=0;d<a.skeleton.bones.length;d++)c.push(a.skeleton.bones[d]);else b(a,c);return c},play:function(a){-1===this.animations.indexOf(a)&&this.animations.push(a)},stop:function(a){a=this.animations.indexOf(a);-1!==a&&this.animations.splice(a,1)},update:function(a){for(var b=0;b<this.animations.length;b++)this.animations[b].resetBlendWeights();
-for(b=0;b<this.animations.length;b++)this.animations[b].update(a)}};THREE.Animation=function(a,b){this.root=a;this.data=THREE.AnimationHandler.init(b);this.hierarchy=THREE.AnimationHandler.parse(a);this.currentTime=0;this.timeScale=1;this.isPlaying=!1;this.loop=!0;this.weight=0;this.interpolationType=THREE.AnimationHandler.LINEAR};THREE.Animation.prototype.keyTypes=["pos","rot","scl"];
-THREE.Animation.prototype.play=function(a,b){this.currentTime=void 0!==a?a:0;this.weight=void 0!==b?b:1;this.isPlaying=!0;this.reset();THREE.AnimationHandler.play(this)};THREE.Animation.prototype.stop=function(){this.isPlaying=!1;THREE.AnimationHandler.stop(this)};
-THREE.Animation.prototype.reset=function(){for(var a=0,b=this.hierarchy.length;a<b;a++){var c=this.hierarchy[a];c.matrixAutoUpdate=!0;void 0===c.animationCache&&(c.animationCache={animations:{},blending:{positionWeight:0,quaternionWeight:0,scaleWeight:0}});void 0===c.animationCache.animations[this.data.name]&&(c.animationCache.animations[this.data.name]={},c.animationCache.animations[this.data.name].prevKey={pos:0,rot:0,scl:0},c.animationCache.animations[this.data.name].nextKey={pos:0,rot:0,scl:0},
-c.animationCache.animations[this.data.name].originalMatrix=c.matrix);for(var c=c.animationCache.animations[this.data.name],d=0;3>d;d++){for(var e=this.keyTypes[d],f=this.data.hierarchy[a].keys[0],g=this.getNextKeyWith(e,a,1);g.time<this.currentTime&&g.index>f.index;)f=g,g=this.getNextKeyWith(e,a,g.index+1);c.prevKey[e]=f;c.nextKey[e]=g}}};
-THREE.Animation.prototype.resetBlendWeights=function(){for(var a=0,b=this.hierarchy.length;a<b;a++){var c=this.hierarchy[a];void 0!==c.animationCache&&(c.animationCache.blending.positionWeight=0,c.animationCache.blending.quaternionWeight=0,c.animationCache.blending.scaleWeight=0)}};
-THREE.Animation.prototype.update=function(){var a=[],b=new THREE.Vector3,c=new THREE.Vector3,d=new THREE.Quaternion,e=function(a,b){var c=[],d=[],e,q,m,r,t,s;e=(a.length-1)*b;q=Math.floor(e);e-=q;c[0]=0===q?q:q-1;c[1]=q;c[2]=q>a.length-2?q:q+1;c[3]=q>a.length-3?q:q+2;q=a[c[0]];r=a[c[1]];t=a[c[2]];s=a[c[3]];c=e*e;m=e*c;d[0]=f(q[0],r[0],t[0],s[0],e,c,m);d[1]=f(q[1],r[1],t[1],s[1],e,c,m);d[2]=f(q[2],r[2],t[2],s[2],e,c,m);return d},f=function(a,b,c,d,e,f,m){a=.5*(c-a);d=.5*(d-b);return(2*(b-c)+a+d)*m+
-(-3*(b-c)-2*a-d)*f+a*e+b};return function(f){if(!1!==this.isPlaying&&(this.currentTime+=f*this.timeScale,0!==this.weight)){f=this.data.length;if(this.currentTime>f||0>this.currentTime)if(this.loop)this.currentTime%=f,0>this.currentTime&&(this.currentTime+=f),this.reset();else{this.stop();return}f=0;for(var h=this.hierarchy.length;f<h;f++)for(var k=this.hierarchy[f],n=k.animationCache.animations[this.data.name],p=k.animationCache.blending,q=0;3>q;q++){var m=this.keyTypes[q],r=n.prevKey[m],t=n.nextKey[m];
-if(0<this.timeScale&&t.time<=this.currentTime||0>this.timeScale&&r.time>=this.currentTime){r=this.data.hierarchy[f].keys[0];for(t=this.getNextKeyWith(m,f,1);t.time<this.currentTime&&t.index>r.index;)r=t,t=this.getNextKeyWith(m,f,t.index+1);n.prevKey[m]=r;n.nextKey[m]=t}k.matrixAutoUpdate=!0;k.matrixWorldNeedsUpdate=!0;var s=(this.currentTime-r.time)/(t.time-r.time),u=r[m],v=t[m];0>s&&(s=0);1<s&&(s=1);if("pos"===m)if(this.interpolationType===THREE.AnimationHandler.LINEAR)c.x=u[0]+(v[0]-u[0])*s,c.y=
-u[1]+(v[1]-u[1])*s,c.z=u[2]+(v[2]-u[2])*s,r=this.weight/(this.weight+p.positionWeight),k.position.lerp(c,r),p.positionWeight+=this.weight;else{if(this.interpolationType===THREE.AnimationHandler.CATMULLROM||this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD)a[0]=this.getPrevKeyWith("pos",f,r.index-1).pos,a[1]=u,a[2]=v,a[3]=this.getNextKeyWith("pos",f,t.index+1).pos,s=.33*s+.33,t=e(a,s),r=this.weight/(this.weight+p.positionWeight),p.positionWeight+=this.weight,m=k.position,m.x+=(t[0]-
-m.x)*r,m.y+=(t[1]-m.y)*r,m.z+=(t[2]-m.z)*r,this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD&&(s=e(a,1.01*s),b.set(s[0],s[1],s[2]),b.sub(m),b.y=0,b.normalize(),s=Math.atan2(b.x,b.z),k.rotation.set(0,s,0))}else"rot"===m?(THREE.Quaternion.slerp(u,v,d,s),0===p.quaternionWeight?(k.quaternion.copy(d),p.quaternionWeight=this.weight):(r=this.weight/(this.weight+p.quaternionWeight),THREE.Quaternion.slerp(k.quaternion,d,k.quaternion,r),p.quaternionWeight+=this.weight)):"scl"===m&&(c.x=u[0]+
-(v[0]-u[0])*s,c.y=u[1]+(v[1]-u[1])*s,c.z=u[2]+(v[2]-u[2])*s,r=this.weight/(this.weight+p.scaleWeight),k.scale.lerp(c,r),p.scaleWeight+=this.weight)}return!0}}}();THREE.Animation.prototype.getNextKeyWith=function(a,b,c){var d=this.data.hierarchy[b].keys;for(c=this.interpolationType===THREE.AnimationHandler.CATMULLROM||this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD?c<d.length-1?c:d.length-1:c%d.length;c<d.length;c++)if(void 0!==d[c][a])return d[c];return this.data.hierarchy[b].keys[0]};
-THREE.Animation.prototype.getPrevKeyWith=function(a,b,c){var d=this.data.hierarchy[b].keys;for(c=this.interpolationType===THREE.AnimationHandler.CATMULLROM||this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD?0<c?c:0:0<=c?c:c+d.length;0<=c;c--)if(void 0!==d[c][a])return d[c];return this.data.hierarchy[b].keys[d.length-1]};
-THREE.KeyFrameAnimation=function(a){this.root=a.node;this.data=THREE.AnimationHandler.init(a);this.hierarchy=THREE.AnimationHandler.parse(this.root);this.currentTime=0;this.timeScale=.001;this.isPlaying=!1;this.loop=this.isPaused=!0;a=0;for(var b=this.hierarchy.length;a<b;a++){var c=this.data.hierarchy[a].sids,d=this.hierarchy[a];if(this.data.hierarchy[a].keys.length&&c){for(var e=0;e<c.length;e++){var f=c[e],g=this.getNextKeyWith(f,a,0);g&&g.apply(f)}d.matrixAutoUpdate=!1;this.data.hierarchy[a].node.updateMatrix();
-d.matrixWorldNeedsUpdate=!0}}};
-THREE.KeyFrameAnimation.prototype.play=function(a){this.currentTime=void 0!==a?a:0;if(!1===this.isPlaying){this.isPlaying=!0;var b=this.hierarchy.length,c,d;for(a=0;a<b;a++)c=this.hierarchy[a],d=this.data.hierarchy[a],void 0===d.animationCache&&(d.animationCache={},d.animationCache.prevKey=null,d.animationCache.nextKey=null,d.animationCache.originalMatrix=c.matrix),c=this.data.hierarchy[a].keys,c.length&&(d.animationCache.prevKey=c[0],d.animationCache.nextKey=c[1],this.startTime=Math.min(c[0].time,
-this.startTime),this.endTime=Math.max(c[c.length-1].time,this.endTime));this.update(0)}this.isPaused=!1;THREE.AnimationHandler.play(this)};THREE.KeyFrameAnimation.prototype.stop=function(){this.isPaused=this.isPlaying=!1;THREE.AnimationHandler.stop(this);for(var a=0;a<this.data.hierarchy.length;a++){var b=this.hierarchy[a],c=this.data.hierarchy[a];if(void 0!==c.animationCache){var d=c.animationCache.originalMatrix;d.copy(b.matrix);b.matrix=d;delete c.animationCache}}};
-THREE.KeyFrameAnimation.prototype.update=function(a){if(!1!==this.isPlaying){this.currentTime+=a*this.timeScale;a=this.data.length;!0===this.loop&&this.currentTime>a&&(this.currentTime%=a);this.currentTime=Math.min(this.currentTime,a);a=0;for(var b=this.hierarchy.length;a<b;a++){var c=this.hierarchy[a],d=this.data.hierarchy[a],e=d.keys,d=d.animationCache;if(e.length){var f=d.prevKey,g=d.nextKey;if(g.time<=this.currentTime){for(;g.time<this.currentTime&&g.index>f.index;)f=g,g=e[f.index+1];d.prevKey=
-f;d.nextKey=g}g.time>=this.currentTime?f.interpolate(g,this.currentTime):f.interpolate(g,g.time);this.data.hierarchy[a].node.updateMatrix();c.matrixWorldNeedsUpdate=!0}}}};THREE.KeyFrameAnimation.prototype.getNextKeyWith=function(a,b,c){b=this.data.hierarchy[b].keys;for(c%=b.length;c<b.length;c++)if(b[c].hasTarget(a))return b[c];return b[0]};
-THREE.KeyFrameAnimation.prototype.getPrevKeyWith=function(a,b,c){b=this.data.hierarchy[b].keys;for(c=0<=c?c:c+b.length;0<=c;c--)if(b[c].hasTarget(a))return b[c];return b[b.length-1]};THREE.MorphAnimation=function(a){this.mesh=a;this.frames=a.morphTargetInfluences.length;this.currentTime=0;this.duration=1E3;this.loop=!0;this.isPlaying=!1};
-THREE.MorphAnimation.prototype={play:function(){this.isPlaying=!0},pause:function(){this.isPlaying=!1},update:function(){var a=0,b=0;return function(c){if(!1!==this.isPlaying){this.currentTime+=c;!0===this.loop&&this.currentTime>this.duration&&(this.currentTime%=this.duration);this.currentTime=Math.min(this.currentTime,this.duration);c=this.duration/this.frames;var d=Math.floor(this.currentTime/c);d!=b&&(this.mesh.morphTargetInfluences[a]=0,this.mesh.morphTargetInfluences[b]=1,this.mesh.morphTargetInfluences[d]=
-0,a=b,b=d);this.mesh.morphTargetInfluences[d]=this.currentTime%c/c;this.mesh.morphTargetInfluences[a]=1-this.mesh.morphTargetInfluences[d]}}}()};
-THREE.BoxGeometry=function(a,b,c,d,e,f){function g(a,b,c,d,e,f,g,s){var u,v=h.widthSegments,y=h.heightSegments,G=e/2,w=f/2,K=h.vertices.length;if("x"===a&&"y"===b||"y"===a&&"x"===b)u="z";else if("x"===a&&"z"===b||"z"===a&&"x"===b)u="y",y=h.depthSegments;else if("z"===a&&"y"===b||"y"===a&&"z"===b)u="x",v=h.depthSegments;var x=v+1,D=y+1,E=e/v,A=f/y,B=new THREE.Vector3;B[u]=0<g?1:-1;for(e=0;e<D;e++)for(f=0;f<x;f++){var F=new THREE.Vector3;F[a]=(f*E-G)*c;F[b]=(e*A-w)*d;F[u]=g;h.vertices.push(F)}for(e=
-0;e<y;e++)for(f=0;f<v;f++)w=f+x*e,a=f+x*(e+1),b=f+1+x*(e+1),c=f+1+x*e,d=new THREE.Vector2(f/v,1-e/y),g=new THREE.Vector2(f/v,1-(e+1)/y),u=new THREE.Vector2((f+1)/v,1-(e+1)/y),G=new THREE.Vector2((f+1)/v,1-e/y),w=new THREE.Face3(w+K,a+K,c+K),w.normal.copy(B),w.vertexNormals.push(B.clone(),B.clone(),B.clone()),w.materialIndex=s,h.faces.push(w),h.faceVertexUvs[0].push([d,g,G]),w=new THREE.Face3(a+K,b+K,c+K),w.normal.copy(B),w.vertexNormals.push(B.clone(),B.clone(),B.clone()),w.materialIndex=s,h.faces.push(w),
-h.faceVertexUvs[0].push([g.clone(),u,G.clone()])}THREE.Geometry.call(this);this.type="BoxGeometry";this.parameters={width:a,height:b,depth:c,widthSegments:d,heightSegments:e,depthSegments:f};this.widthSegments=d||1;this.heightSegments=e||1;this.depthSegments=f||1;var h=this;d=a/2;e=b/2;f=c/2;g("z","y",-1,-1,c,b,d,0);g("z","y",1,-1,c,b,-d,1);g("x","z",1,1,a,c,e,2);g("x","z",1,-1,a,c,-e,3);g("x","y",1,-1,a,b,f,4);g("x","y",-1,-1,a,b,-f,5);this.mergeVertices()};THREE.BoxGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.CircleGeometry=function(a,b,c,d){THREE.Geometry.call(this);this.type="CircleGeometry";this.parameters={radius:a,segments:b,thetaStart:c,thetaLength:d};a=a||50;b=void 0!==b?Math.max(3,b):8;c=void 0!==c?c:0;d=void 0!==d?d:2*Math.PI;var e,f=[];e=new THREE.Vector3;var g=new THREE.Vector2(.5,.5);this.vertices.push(e);f.push(g);for(e=0;e<=b;e++){var h=new THREE.Vector3,k=c+e/b*d;h.x=a*Math.cos(k);h.y=a*Math.sin(k);this.vertices.push(h);f.push(new THREE.Vector2((h.x/a+1)/2,(h.y/a+1)/2))}c=new THREE.Vector3(0,
-0,1);for(e=1;e<=b;e++)this.faces.push(new THREE.Face3(e,e+1,0,[c.clone(),c.clone(),c.clone()])),this.faceVertexUvs[0].push([f[e].clone(),f[e+1].clone(),g.clone()]);this.computeFaceNormals();this.boundingSphere=new THREE.Sphere(new THREE.Vector3,a)};THREE.CircleGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.CubeGeometry=function(a,b,c,d,e,f){console.warn("THREE.CubeGeometry has been renamed to THREE.BoxGeometry.");return new THREE.BoxGeometry(a,b,c,d,e,f)};
-THREE.CylinderGeometry=function(a,b,c,d,e,f){THREE.Geometry.call(this);this.type="CylinderGeometry";this.parameters={radiusTop:a,radiusBottom:b,height:c,radialSegments:d,heightSegments:e,openEnded:f};a=void 0!==a?a:20;b=void 0!==b?b:20;c=void 0!==c?c:100;d=d||8;e=e||1;f=void 0!==f?f:!1;var g=c/2,h,k,n=[],p=[];for(k=0;k<=e;k++){var q=[],m=[],r=k/e,t=r*(b-a)+a;for(h=0;h<=d;h++){var s=h/d,u=new THREE.Vector3;u.x=t*Math.sin(s*Math.PI*2);u.y=-r*c+g;u.z=t*Math.cos(s*Math.PI*2);this.vertices.push(u);q.push(this.vertices.length-
-1);m.push(new THREE.Vector2(s,1-r))}n.push(q);p.push(m)}c=(b-a)/c;for(h=0;h<d;h++)for(0!==a?(q=this.vertices[n[0][h]].clone(),m=this.vertices[n[0][h+1]].clone()):(q=this.vertices[n[1][h]].clone(),m=this.vertices[n[1][h+1]].clone()),q.setY(Math.sqrt(q.x*q.x+q.z*q.z)*c).normalize(),m.setY(Math.sqrt(m.x*m.x+m.z*m.z)*c).normalize(),k=0;k<e;k++){var r=n[k][h],t=n[k+1][h],s=n[k+1][h+1],u=n[k][h+1],v=q.clone(),y=q.clone(),G=m.clone(),w=m.clone(),K=p[k][h].clone(),x=p[k+1][h].clone(),D=p[k+1][h+1].clone(),
-E=p[k][h+1].clone();this.faces.push(new THREE.Face3(r,t,u,[v,y,w]));this.faceVertexUvs[0].push([K,x,E]);this.faces.push(new THREE.Face3(t,s,u,[y.clone(),G,w.clone()]));this.faceVertexUvs[0].push([x.clone(),D,E.clone()])}if(!1===f&&0<a)for(this.vertices.push(new THREE.Vector3(0,g,0)),h=0;h<d;h++)r=n[0][h],t=n[0][h+1],s=this.vertices.length-1,v=new THREE.Vector3(0,1,0),y=new THREE.Vector3(0,1,0),G=new THREE.Vector3(0,1,0),K=p[0][h].clone(),x=p[0][h+1].clone(),D=new THREE.Vector2(x.x,0),this.faces.push(new THREE.Face3(r,
-t,s,[v,y,G])),this.faceVertexUvs[0].push([K,x,D]);if(!1===f&&0<b)for(this.vertices.push(new THREE.Vector3(0,-g,0)),h=0;h<d;h++)r=n[k][h+1],t=n[k][h],s=this.vertices.length-1,v=new THREE.Vector3(0,-1,0),y=new THREE.Vector3(0,-1,0),G=new THREE.Vector3(0,-1,0),K=p[k][h+1].clone(),x=p[k][h].clone(),D=new THREE.Vector2(x.x,1),this.faces.push(new THREE.Face3(r,t,s,[v,y,G])),this.faceVertexUvs[0].push([K,x,D]);this.computeFaceNormals()};THREE.CylinderGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.ExtrudeGeometry=function(a,b){"undefined"!==typeof a&&(THREE.Geometry.call(this),this.type="ExtrudeGeometry",a=a instanceof Array?a:[a],this.addShapeList(a,b),this.computeFaceNormals())};THREE.ExtrudeGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.ExtrudeGeometry.prototype.addShapeList=function(a,b){for(var c=a.length,d=0;d<c;d++)this.addShape(a[d],b)};
-THREE.ExtrudeGeometry.prototype.addShape=function(a,b){function c(a,b,c){b||console.log("die");return b.clone().multiplyScalar(c).add(a)}function d(a,b,c){var d=1,d=a.x-b.x,e=a.y-b.y,f=c.x-a.x,g=c.y-a.y,h=d*d+e*e;if(1E-10<Math.abs(d*g-e*f)){var k=Math.sqrt(h),m=Math.sqrt(f*f+g*g),h=b.x-e/k;b=b.y+d/k;f=((c.x-g/m-h)*g-(c.y+f/m-b)*f)/(d*g-e*f);c=h+d*f-a.x;a=b+e*f-a.y;d=c*c+a*a;if(2>=d)return new THREE.Vector2(c,a);d=Math.sqrt(d/2)}else a=!1,1E-10<d?1E-10<f&&(a=!0):-1E-10>d?-1E-10>f&&(a=!0):Math.sign(e)==
-Math.sign(g)&&(a=!0),a?(c=-e,a=d,d=Math.sqrt(h)):(c=d,a=e,d=Math.sqrt(h/2));return new THREE.Vector2(c/d,a/d)}function e(a,b){var c,d;for(P=a.length;0<=--P;){c=P;d=P-1;0>d&&(d=a.length-1);for(var e=0,f=r+2*p,e=0;e<f;e++){var g=la*e,h=la*(e+1),k=b+c+g,g=b+d+g,m=b+d+h,h=b+c+h,k=k+R,g=g+R,m=m+R,h=h+R;F.faces.push(new THREE.Face3(k,g,h,null,null,y));F.faces.push(new THREE.Face3(g,m,h,null,null,y));k=G.generateSideWallUV(F,k,g,m,h);F.faceVertexUvs[0].push([k[0],k[1],k[3]]);F.faceVertexUvs[0].push([k[1],
-k[2],k[3]])}}}function f(a,b,c){F.vertices.push(new THREE.Vector3(a,b,c))}function g(a,b,c){a+=R;b+=R;c+=R;F.faces.push(new THREE.Face3(a,b,c,null,null,v));a=G.generateTopUV(F,a,b,c);F.faceVertexUvs[0].push(a)}var h=void 0!==b.amount?b.amount:100,k=void 0!==b.bevelThickness?b.bevelThickness:6,n=void 0!==b.bevelSize?b.bevelSize:k-2,p=void 0!==b.bevelSegments?b.bevelSegments:3,q=void 0!==b.bevelEnabled?b.bevelEnabled:!0,m=void 0!==b.curveSegments?b.curveSegments:12,r=void 0!==b.steps?b.steps:1,t=b.extrudePath,
-s,u=!1,v=b.material,y=b.extrudeMaterial,G=void 0!==b.UVGenerator?b.UVGenerator:THREE.ExtrudeGeometry.WorldUVGenerator,w,K,x,D;t&&(s=t.getSpacedPoints(r),u=!0,q=!1,w=void 0!==b.frames?b.frames:new THREE.TubeGeometry.FrenetFrames(t,r,!1),K=new THREE.Vector3,x=new THREE.Vector3,D=new THREE.Vector3);q||(n=k=p=0);var E,A,B,F=this,R=this.vertices.length,t=a.extractPoints(m),m=t.shape,H=t.holes;if(t=!THREE.Shape.Utils.isClockWise(m)){m=m.reverse();A=0;for(B=H.length;A<B;A++)E=H[A],THREE.Shape.Utils.isClockWise(E)&&
-(H[A]=E.reverse());t=!1}var C=THREE.Shape.Utils.triangulateShape(m,H),T=m;A=0;for(B=H.length;A<B;A++)E=H[A],m=m.concat(E);var Q,O,S,X,Y,la=m.length,ma,ya=C.length,t=[],P=0;S=T.length;Q=S-1;for(O=P+1;P<S;P++,Q++,O++)Q===S&&(Q=0),O===S&&(O=0),t[P]=d(T[P],T[Q],T[O]);var Ga=[],Fa,za=t.concat();A=0;for(B=H.length;A<B;A++){E=H[A];Fa=[];P=0;S=E.length;Q=S-1;for(O=P+1;P<S;P++,Q++,O++)Q===S&&(Q=0),O===S&&(O=0),Fa[P]=d(E[P],E[Q],E[O]);Ga.push(Fa);za=za.concat(Fa)}for(Q=0;Q<p;Q++){S=Q/p;X=k*(1-S);O=n*Math.sin(S*
-Math.PI/2);P=0;for(S=T.length;P<S;P++)Y=c(T[P],t[P],O),f(Y.x,Y.y,-X);A=0;for(B=H.length;A<B;A++)for(E=H[A],Fa=Ga[A],P=0,S=E.length;P<S;P++)Y=c(E[P],Fa[P],O),f(Y.x,Y.y,-X)}O=n;for(P=0;P<la;P++)Y=q?c(m[P],za[P],O):m[P],u?(x.copy(w.normals[0]).multiplyScalar(Y.x),K.copy(w.binormals[0]).multiplyScalar(Y.y),D.copy(s[0]).add(x).add(K),f(D.x,D.y,D.z)):f(Y.x,Y.y,0);for(S=1;S<=r;S++)for(P=0;P<la;P++)Y=q?c(m[P],za[P],O):m[P],u?(x.copy(w.normals[S]).multiplyScalar(Y.x),K.copy(w.binormals[S]).multiplyScalar(Y.y),
-D.copy(s[S]).add(x).add(K),f(D.x,D.y,D.z)):f(Y.x,Y.y,h/r*S);for(Q=p-1;0<=Q;Q--){S=Q/p;X=k*(1-S);O=n*Math.sin(S*Math.PI/2);P=0;for(S=T.length;P<S;P++)Y=c(T[P],t[P],O),f(Y.x,Y.y,h+X);A=0;for(B=H.length;A<B;A++)for(E=H[A],Fa=Ga[A],P=0,S=E.length;P<S;P++)Y=c(E[P],Fa[P],O),u?f(Y.x,Y.y+s[r-1].y,s[r-1].x+X):f(Y.x,Y.y,h+X)}(function(){if(q){var a;a=0*la;for(P=0;P<ya;P++)ma=C[P],g(ma[2]+a,ma[1]+a,ma[0]+a);a=r+2*p;a*=la;for(P=0;P<ya;P++)ma=C[P],g(ma[0]+a,ma[1]+a,ma[2]+a)}else{for(P=0;P<ya;P++)ma=C[P],g(ma[2],
-ma[1],ma[0]);for(P=0;P<ya;P++)ma=C[P],g(ma[0]+la*r,ma[1]+la*r,ma[2]+la*r)}})();(function(){var a=0;e(T,a);a+=T.length;A=0;for(B=H.length;A<B;A++)E=H[A],e(E,a),a+=E.length})()};
-THREE.ExtrudeGeometry.WorldUVGenerator={generateTopUV:function(a,b,c,d){a=a.vertices;b=a[b];c=a[c];d=a[d];return[new THREE.Vector2(b.x,b.y),new THREE.Vector2(c.x,c.y),new THREE.Vector2(d.x,d.y)]},generateSideWallUV:function(a,b,c,d,e){a=a.vertices;b=a[b];c=a[c];d=a[d];e=a[e];return.01>Math.abs(b.y-c.y)?[new THREE.Vector2(b.x,1-b.z),new THREE.Vector2(c.x,1-c.z),new THREE.Vector2(d.x,1-d.z),new THREE.Vector2(e.x,1-e.z)]:[new THREE.Vector2(b.y,1-b.z),new THREE.Vector2(c.y,1-c.z),new THREE.Vector2(d.y,
-1-d.z),new THREE.Vector2(e.y,1-e.z)]}};THREE.ShapeGeometry=function(a,b){THREE.Geometry.call(this);this.type="ShapeGeometry";!1===a instanceof Array&&(a=[a]);this.addShapeList(a,b);this.computeFaceNormals()};THREE.ShapeGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.ShapeGeometry.prototype.addShapeList=function(a,b){for(var c=0,d=a.length;c<d;c++)this.addShape(a[c],b);return this};
-THREE.ShapeGeometry.prototype.addShape=function(a,b){void 0===b&&(b={});var c=b.material,d=void 0===b.UVGenerator?THREE.ExtrudeGeometry.WorldUVGenerator:b.UVGenerator,e,f,g,h=this.vertices.length;e=a.extractPoints(void 0!==b.curveSegments?b.curveSegments:12);var k=e.shape,n=e.holes;if(!THREE.Shape.Utils.isClockWise(k))for(k=k.reverse(),e=0,f=n.length;e<f;e++)g=n[e],THREE.Shape.Utils.isClockWise(g)&&(n[e]=g.reverse());var p=THREE.Shape.Utils.triangulateShape(k,n);e=0;for(f=n.length;e<f;e++)g=n[e],
-k=k.concat(g);n=k.length;f=p.length;for(e=0;e<n;e++)g=k[e],this.vertices.push(new THREE.Vector3(g.x,g.y,0));for(e=0;e<f;e++)n=p[e],k=n[0]+h,g=n[1]+h,n=n[2]+h,this.faces.push(new THREE.Face3(k,g,n,null,null,c)),this.faceVertexUvs[0].push(d.generateTopUV(this,k,g,n))};
-THREE.LatheGeometry=function(a,b,c,d){THREE.Geometry.call(this);this.type="LatheGeometry";this.parameters={points:a,segments:b,phiStart:c,phiLength:d};b=b||12;c=c||0;d=d||2*Math.PI;for(var e=1/(a.length-1),f=1/b,g=0,h=b;g<=h;g++)for(var k=c+g*f*d,n=Math.cos(k),p=Math.sin(k),k=0,q=a.length;k<q;k++){var m=a[k],r=new THREE.Vector3;r.x=n*m.x-p*m.y;r.y=p*m.x+n*m.y;r.z=m.z;this.vertices.push(r)}c=a.length;g=0;for(h=b;g<h;g++)for(k=0,q=a.length-1;k<q;k++){b=p=k+c*g;d=p+c;var n=p+1+c,p=p+1,m=g*f,r=k*e,t=
-m+f,s=r+e;this.faces.push(new THREE.Face3(b,d,p));this.faceVertexUvs[0].push([new THREE.Vector2(m,r),new THREE.Vector2(t,r),new THREE.Vector2(m,s)]);this.faces.push(new THREE.Face3(d,n,p));this.faceVertexUvs[0].push([new THREE.Vector2(t,r),new THREE.Vector2(t,s),new THREE.Vector2(m,s)])}this.mergeVertices();this.computeFaceNormals();this.computeVertexNormals()};THREE.LatheGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.PlaneGeometry=function(a,b,c,d){console.info("THREE.PlaneGeometry: Consider using THREE.PlaneBufferGeometry for lower memory footprint.");THREE.Geometry.call(this);this.type="PlaneGeometry";this.parameters={width:a,height:b,widthSegments:c,heightSegments:d};this.fromBufferGeometry(new THREE.PlaneBufferGeometry(a,b,c,d))};THREE.PlaneGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.PlaneBufferGeometry=function(a,b,c,d){THREE.BufferGeometry.call(this);this.type="PlaneBufferGeometry";this.parameters={width:a,height:b,widthSegments:c,heightSegments:d};var e=a/2,f=b/2;c=c||1;d=d||1;var g=c+1,h=d+1,k=a/c,n=b/d;b=new Float32Array(g*h*3);a=new Float32Array(g*h*3);for(var p=new Float32Array(g*h*2),q=0,m=0,r=0;r<h;r++)for(var t=r*n-f,s=0;s<g;s++)b[q]=s*k-e,b[q+1]=-t,a[q+2]=1,p[m]=s/c,p[m+1]=1-r/d,q+=3,m+=2;q=0;e=new (65535<b.length/3?Uint32Array:Uint16Array)(c*d*6);for(r=0;r<d;r++)for(s=
-0;s<c;s++)f=s+g*(r+1),h=s+1+g*(r+1),k=s+1+g*r,e[q]=s+g*r,e[q+1]=f,e[q+2]=k,e[q+3]=f,e[q+4]=h,e[q+5]=k,q+=6;this.addAttribute("index",new THREE.BufferAttribute(e,1));this.addAttribute("position",new THREE.BufferAttribute(b,3));this.addAttribute("normal",new THREE.BufferAttribute(a,3));this.addAttribute("uv",new THREE.BufferAttribute(p,2))};THREE.PlaneBufferGeometry.prototype=Object.create(THREE.BufferGeometry.prototype);
-THREE.RingGeometry=function(a,b,c,d,e,f){THREE.Geometry.call(this);this.type="RingGeometry";this.parameters={innerRadius:a,outerRadius:b,thetaSegments:c,phiSegments:d,thetaStart:e,thetaLength:f};a=a||0;b=b||50;e=void 0!==e?e:0;f=void 0!==f?f:2*Math.PI;c=void 0!==c?Math.max(3,c):8;d=void 0!==d?Math.max(1,d):8;var g,h=[],k=a,n=(b-a)/d;for(a=0;a<d+1;a++){for(g=0;g<c+1;g++){var p=new THREE.Vector3,q=e+g/c*f;p.x=k*Math.cos(q);p.y=k*Math.sin(q);this.vertices.push(p);h.push(new THREE.Vector2((p.x/b+1)/2,
-(p.y/b+1)/2))}k+=n}b=new THREE.Vector3(0,0,1);for(a=0;a<d;a++)for(e=a*(c+1),g=0;g<c;g++)f=q=g+e,n=q+c+1,p=q+c+2,this.faces.push(new THREE.Face3(f,n,p,[b.clone(),b.clone(),b.clone()])),this.faceVertexUvs[0].push([h[f].clone(),h[n].clone(),h[p].clone()]),f=q,n=q+c+2,p=q+1,this.faces.push(new THREE.Face3(f,n,p,[b.clone(),b.clone(),b.clone()])),this.faceVertexUvs[0].push([h[f].clone(),h[n].clone(),h[p].clone()]);this.computeFaceNormals();this.boundingSphere=new THREE.Sphere(new THREE.Vector3,k)};
-THREE.RingGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.SphereGeometry=function(a,b,c,d,e,f,g){THREE.Geometry.call(this);this.type="SphereGeometry";this.parameters={radius:a,widthSegments:b,heightSegments:c,phiStart:d,phiLength:e,thetaStart:f,thetaLength:g};a=a||50;b=Math.max(3,Math.floor(b)||8);c=Math.max(2,Math.floor(c)||6);d=void 0!==d?d:0;e=void 0!==e?e:2*Math.PI;f=void 0!==f?f:0;g=void 0!==g?g:Math.PI;var h,k,n=[],p=[];for(k=0;k<=c;k++){var q=[],m=[];for(h=0;h<=b;h++){var r=h/b,t=k/c,s=new THREE.Vector3;s.x=-a*Math.cos(d+r*e)*Math.sin(f+t*g);
-s.y=a*Math.cos(f+t*g);s.z=a*Math.sin(d+r*e)*Math.sin(f+t*g);this.vertices.push(s);q.push(this.vertices.length-1);m.push(new THREE.Vector2(r,1-t))}n.push(q);p.push(m)}for(k=0;k<c;k++)for(h=0;h<b;h++){d=n[k][h+1];e=n[k][h];f=n[k+1][h];g=n[k+1][h+1];var q=this.vertices[d].clone().normalize(),m=this.vertices[e].clone().normalize(),r=this.vertices[f].clone().normalize(),t=this.vertices[g].clone().normalize(),s=p[k][h+1].clone(),u=p[k][h].clone(),v=p[k+1][h].clone(),y=p[k+1][h+1].clone();Math.abs(this.vertices[d].y)===
-a?(s.x=(s.x+u.x)/2,this.faces.push(new THREE.Face3(d,f,g,[q,r,t])),this.faceVertexUvs[0].push([s,v,y])):Math.abs(this.vertices[f].y)===a?(v.x=(v.x+y.x)/2,this.faces.push(new THREE.Face3(d,e,f,[q,m,r])),this.faceVertexUvs[0].push([s,u,v])):(this.faces.push(new THREE.Face3(d,e,g,[q,m,t])),this.faceVertexUvs[0].push([s,u,y]),this.faces.push(new THREE.Face3(e,f,g,[m.clone(),r,t.clone()])),this.faceVertexUvs[0].push([u.clone(),v,y.clone()]))}this.computeFaceNormals();this.boundingSphere=new THREE.Sphere(new THREE.Vector3,
-a)};THREE.SphereGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.TextGeometry=function(a,b){b=b||{};var c=THREE.FontUtils.generateShapes(a,b);b.amount=void 0!==b.height?b.height:50;void 0===b.bevelThickness&&(b.bevelThickness=10);void 0===b.bevelSize&&(b.bevelSize=8);void 0===b.bevelEnabled&&(b.bevelEnabled=!1);THREE.ExtrudeGeometry.call(this,c,b);this.type="TextGeometry"};THREE.TextGeometry.prototype=Object.create(THREE.ExtrudeGeometry.prototype);
-THREE.TorusGeometry=function(a,b,c,d,e){THREE.Geometry.call(this);this.type="TorusGeometry";this.parameters={radius:a,tube:b,radialSegments:c,tubularSegments:d,arc:e};a=a||100;b=b||40;c=c||8;d=d||6;e=e||2*Math.PI;for(var f=new THREE.Vector3,g=[],h=[],k=0;k<=c;k++)for(var n=0;n<=d;n++){var p=n/d*e,q=k/c*Math.PI*2;f.x=a*Math.cos(p);f.y=a*Math.sin(p);var m=new THREE.Vector3;m.x=(a+b*Math.cos(q))*Math.cos(p);m.y=(a+b*Math.cos(q))*Math.sin(p);m.z=b*Math.sin(q);this.vertices.push(m);g.push(new THREE.Vector2(n/
-d,k/c));h.push(m.clone().sub(f).normalize())}for(k=1;k<=c;k++)for(n=1;n<=d;n++)a=(d+1)*k+n-1,b=(d+1)*(k-1)+n-1,e=(d+1)*(k-1)+n,f=(d+1)*k+n,p=new THREE.Face3(a,b,f,[h[a].clone(),h[b].clone(),h[f].clone()]),this.faces.push(p),this.faceVertexUvs[0].push([g[a].clone(),g[b].clone(),g[f].clone()]),p=new THREE.Face3(b,e,f,[h[b].clone(),h[e].clone(),h[f].clone()]),this.faces.push(p),this.faceVertexUvs[0].push([g[b].clone(),g[e].clone(),g[f].clone()]);this.computeFaceNormals()};
-THREE.TorusGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.TorusKnotGeometry=function(a,b,c,d,e,f,g){function h(a,b,c,d,e){var f=Math.cos(a),g=Math.sin(a);a*=b/c;b=Math.cos(a);f*=d*(2+b)*.5;g=d*(2+b)*g*.5;d=e*d*Math.sin(a)*.5;return new THREE.Vector3(f,g,d)}THREE.Geometry.call(this);this.type="TorusKnotGeometry";this.parameters={radius:a,tube:b,radialSegments:c,tubularSegments:d,p:e,q:f,heightScale:g};a=a||100;b=b||40;c=c||64;d=d||8;e=e||2;f=f||3;g=g||1;for(var k=Array(c),n=new THREE.Vector3,p=new THREE.Vector3,q=new THREE.Vector3,m=0;m<c;++m){k[m]=
-Array(d);var r=m/c*2*e*Math.PI,t=h(r,f,e,a,g),r=h(r+.01,f,e,a,g);n.subVectors(r,t);p.addVectors(r,t);q.crossVectors(n,p);p.crossVectors(q,n);q.normalize();p.normalize();for(r=0;r<d;++r){var s=r/d*2*Math.PI,u=-b*Math.cos(s),s=b*Math.sin(s),v=new THREE.Vector3;v.x=t.x+u*p.x+s*q.x;v.y=t.y+u*p.y+s*q.y;v.z=t.z+u*p.z+s*q.z;k[m][r]=this.vertices.push(v)-1}}for(m=0;m<c;++m)for(r=0;r<d;++r)e=(m+1)%c,f=(r+1)%d,a=k[m][r],b=k[e][r],e=k[e][f],f=k[m][f],g=new THREE.Vector2(m/c,r/d),n=new THREE.Vector2((m+1)/c,
-r/d),p=new THREE.Vector2((m+1)/c,(r+1)/d),q=new THREE.Vector2(m/c,(r+1)/d),this.faces.push(new THREE.Face3(a,b,f)),this.faceVertexUvs[0].push([g,n,q]),this.faces.push(new THREE.Face3(b,e,f)),this.faceVertexUvs[0].push([n.clone(),p,q.clone()]);this.computeFaceNormals();this.computeVertexNormals()};THREE.TorusKnotGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.TubeGeometry=function(a,b,c,d,e){THREE.Geometry.call(this);this.type="TubeGeometry";this.parameters={path:a,segments:b,radius:c,radialSegments:d,closed:e};b=b||64;c=c||1;d=d||8;e=e||!1;var f=[],g,h,k=b+1,n,p,q,m,r=new THREE.Vector3,t,s,u;t=new THREE.TubeGeometry.FrenetFrames(a,b,e);s=t.normals;u=t.binormals;this.tangents=t.tangents;this.normals=s;this.binormals=u;for(t=0;t<k;t++)for(f[t]=[],n=t/(k-1),m=a.getPointAt(n),g=s[t],h=u[t],n=0;n<d;n++)p=n/d*2*Math.PI,q=-c*Math.cos(p),p=c*Math.sin(p),
-r.copy(m),r.x+=q*g.x+p*h.x,r.y+=q*g.y+p*h.y,r.z+=q*g.z+p*h.z,f[t][n]=this.vertices.push(new THREE.Vector3(r.x,r.y,r.z))-1;for(t=0;t<b;t++)for(n=0;n<d;n++)k=e?(t+1)%b:t+1,r=(n+1)%d,a=f[t][n],c=f[k][n],k=f[k][r],r=f[t][r],s=new THREE.Vector2(t/b,n/d),u=new THREE.Vector2((t+1)/b,n/d),g=new THREE.Vector2((t+1)/b,(n+1)/d),h=new THREE.Vector2(t/b,(n+1)/d),this.faces.push(new THREE.Face3(a,c,r)),this.faceVertexUvs[0].push([s,u,h]),this.faces.push(new THREE.Face3(c,k,r)),this.faceVertexUvs[0].push([u.clone(),
-g,h.clone()]);this.computeFaceNormals();this.computeVertexNormals()};THREE.TubeGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.TubeGeometry.FrenetFrames=function(a,b,c){new THREE.Vector3;var d=new THREE.Vector3;new THREE.Vector3;var e=[],f=[],g=[],h=new THREE.Vector3,k=new THREE.Matrix4;b+=1;var n,p,q;this.tangents=e;this.normals=f;this.binormals=g;for(n=0;n<b;n++)p=n/(b-1),e[n]=a.getTangentAt(p),e[n].normalize();f[0]=new THREE.Vector3;g[0]=new THREE.Vector3;a=Number.MAX_VALUE;n=Math.abs(e[0].x);p=Math.abs(e[0].y);q=Math.abs(e[0].z);n<=a&&(a=n,d.set(1,0,0));p<=a&&(a=p,d.set(0,1,0));q<=a&&d.set(0,0,1);h.crossVectors(e[0],
-d).normalize();f[0].crossVectors(e[0],h);g[0].crossVectors(e[0],f[0]);for(n=1;n<b;n++)f[n]=f[n-1].clone(),g[n]=g[n-1].clone(),h.crossVectors(e[n-1],e[n]),1E-4<h.length()&&(h.normalize(),d=Math.acos(THREE.Math.clamp(e[n-1].dot(e[n]),-1,1)),f[n].applyMatrix4(k.makeRotationAxis(h,d))),g[n].crossVectors(e[n],f[n]);if(c)for(d=Math.acos(THREE.Math.clamp(f[0].dot(f[b-1]),-1,1)),d/=b-1,0<e[0].dot(h.crossVectors(f[0],f[b-1]))&&(d=-d),n=1;n<b;n++)f[n].applyMatrix4(k.makeRotationAxis(e[n],d*n)),g[n].crossVectors(e[n],
-f[n])};
-THREE.PolyhedronGeometry=function(a,b,c,d){function e(a){var b=a.normalize().clone();b.index=k.vertices.push(b)-1;var c=Math.atan2(a.z,-a.x)/2/Math.PI+.5;a=Math.atan2(-a.y,Math.sqrt(a.x*a.x+a.z*a.z))/Math.PI+.5;b.uv=new THREE.Vector2(c,1-a);return b}function f(a,b,c){var d=new THREE.Face3(a.index,b.index,c.index,[a.clone(),b.clone(),c.clone()]);k.faces.push(d);u.copy(a).add(b).add(c).divideScalar(3);d=Math.atan2(u.z,-u.x);k.faceVertexUvs[0].push([h(a.uv,a,d),h(b.uv,b,d),h(c.uv,c,d)])}function g(a,b){var c=
-Math.pow(2,b);Math.pow(4,b);for(var d=e(k.vertices[a.a]),g=e(k.vertices[a.b]),h=e(k.vertices[a.c]),m=[],n=0;n<=c;n++){m[n]=[];for(var p=e(d.clone().lerp(h,n/c)),q=e(g.clone().lerp(h,n/c)),r=c-n,s=0;s<=r;s++)m[n][s]=0==s&&n==c?p:e(p.clone().lerp(q,s/r))}for(n=0;n<c;n++)for(s=0;s<2*(c-n)-1;s++)d=Math.floor(s/2),0==s%2?f(m[n][d+1],m[n+1][d],m[n][d]):f(m[n][d+1],m[n+1][d+1],m[n+1][d])}function h(a,b,c){0>c&&1===a.x&&(a=new THREE.Vector2(a.x-1,a.y));0===b.x&&0===b.z&&(a=new THREE.Vector2(c/2/Math.PI+.5,
-a.y));return a.clone()}THREE.Geometry.call(this);this.type="PolyhedronGeometry";this.parameters={vertices:a,indices:b,radius:c,detail:d};c=c||1;d=d||0;for(var k=this,n=0,p=a.length;n<p;n+=3)e(new THREE.Vector3(a[n],a[n+1],a[n+2]));a=this.vertices;for(var q=[],m=n=0,p=b.length;n<p;n+=3,m++){var r=a[b[n]],t=a[b[n+1]],s=a[b[n+2]];q[m]=new THREE.Face3(r.index,t.index,s.index,[r.clone(),t.clone(),s.clone()])}for(var u=new THREE.Vector3,n=0,p=q.length;n<p;n++)g(q[n],d);n=0;for(p=this.faceVertexUvs[0].length;n<
-p;n++)b=this.faceVertexUvs[0][n],d=b[0].x,a=b[1].x,q=b[2].x,m=Math.max(d,Math.max(a,q)),r=Math.min(d,Math.min(a,q)),.9<m&&.1>r&&(.2>d&&(b[0].x+=1),.2>a&&(b[1].x+=1),.2>q&&(b[2].x+=1));n=0;for(p=this.vertices.length;n<p;n++)this.vertices[n].multiplyScalar(c);this.mergeVertices();this.computeFaceNormals();this.boundingSphere=new THREE.Sphere(new THREE.Vector3,c)};THREE.PolyhedronGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.DodecahedronGeometry=function(a,b){this.parameters={radius:a,detail:b};var c=(1+Math.sqrt(5))/2,d=1/c;THREE.PolyhedronGeometry.call(this,[-1,-1,-1,-1,-1,1,-1,1,-1,-1,1,1,1,-1,-1,1,-1,1,1,1,-1,1,1,1,0,-d,-c,0,-d,c,0,d,-c,0,d,c,-d,-c,0,-d,c,0,d,-c,0,d,c,0,-c,0,-d,c,0,-d,-c,0,d,c,0,d],[3,11,7,3,7,15,3,15,13,7,19,17,7,17,6,7,6,15,17,4,8,17,8,10,17,10,6,8,0,16,8,16,2,8,2,10,0,12,1,0,1,18,0,18,16,6,10,2,6,2,13,6,13,15,2,16,18,2,18,3,2,3,13,18,1,9,18,9,11,18,11,3,4,14,12,4,12,0,4,0,8,11,9,5,11,5,19,
-11,19,7,19,5,14,19,14,4,19,4,17,1,12,14,1,14,5,1,5,9],a,b)};THREE.DodecahedronGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.IcosahedronGeometry=function(a,b){var c=(1+Math.sqrt(5))/2;THREE.PolyhedronGeometry.call(this,[-1,c,0,1,c,0,-1,-c,0,1,-c,0,0,-1,c,0,1,c,0,-1,-c,0,1,-c,c,0,-1,c,0,1,-c,0,-1,-c,0,1],[0,11,5,0,5,1,0,1,7,0,7,10,0,10,11,1,5,9,5,11,4,11,10,2,10,7,6,7,1,8,3,9,4,3,4,2,3,2,6,3,6,8,3,8,9,4,9,5,2,4,11,6,2,10,8,6,7,9,8,1],a,b);this.type="IcosahedronGeometry";this.parameters={radius:a,detail:b}};THREE.IcosahedronGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.OctahedronGeometry=function(a,b){this.parameters={radius:a,detail:b};THREE.PolyhedronGeometry.call(this,[1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1],[0,2,4,0,4,3,0,3,5,0,5,2,1,2,5,1,5,3,1,3,4,1,4,2],a,b);this.type="OctahedronGeometry";this.parameters={radius:a,detail:b}};THREE.OctahedronGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.TetrahedronGeometry=function(a,b){THREE.PolyhedronGeometry.call(this,[1,1,1,-1,-1,1,-1,1,-1,1,-1,-1],[2,1,0,0,3,2,1,3,0,2,3,1],a,b);this.type="TetrahedronGeometry";this.parameters={radius:a,detail:b}};THREE.TetrahedronGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.ParametricGeometry=function(a,b,c){THREE.Geometry.call(this);this.type="ParametricGeometry";this.parameters={func:a,slices:b,stacks:c};var d=this.vertices,e=this.faces,f=this.faceVertexUvs[0],g,h,k,n,p=b+1;for(g=0;g<=c;g++)for(n=g/c,h=0;h<=b;h++)k=h/b,k=a(k,n),d.push(k);var q,m,r,t;for(g=0;g<c;g++)for(h=0;h<b;h++)a=g*p+h,d=g*p+h+1,n=(g+1)*p+h+1,k=(g+1)*p+h,q=new THREE.Vector2(h/b,g/c),m=new THREE.Vector2((h+1)/b,g/c),r=new THREE.Vector2((h+1)/b,(g+1)/c),t=new THREE.Vector2(h/b,(g+1)/c),e.push(new THREE.Face3(a,
-d,k)),f.push([q,m,t]),e.push(new THREE.Face3(d,n,k)),f.push([m.clone(),r,t.clone()]);this.computeFaceNormals();this.computeVertexNormals()};THREE.ParametricGeometry.prototype=Object.create(THREE.Geometry.prototype);
-THREE.AxisHelper=function(a){a=a||1;var b=new Float32Array([0,0,0,a,0,0,0,0,0,0,a,0,0,0,0,0,0,a]),c=new Float32Array([1,0,0,1,.6,0,0,1,0,.6,1,0,0,0,1,0,.6,1]);a=new THREE.BufferGeometry;a.addAttribute("position",new THREE.BufferAttribute(b,3));a.addAttribute("color",new THREE.BufferAttribute(c,3));b=new THREE.LineBasicMaterial({vertexColors:THREE.VertexColors});THREE.Line.call(this,a,b,THREE.LinePieces)};THREE.AxisHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.ArrowHelper=function(){var a=new THREE.Geometry;a.vertices.push(new THREE.Vector3(0,0,0),new THREE.Vector3(0,1,0));var b=new THREE.CylinderGeometry(0,.5,1,5,1);b.applyMatrix((new THREE.Matrix4).makeTranslation(0,-.5,0));return function(c,d,e,f,g,h){THREE.Object3D.call(this);void 0===f&&(f=16776960);void 0===e&&(e=1);void 0===g&&(g=.2*e);void 0===h&&(h=.2*g);this.position.copy(d);this.line=new THREE.Line(a,new THREE.LineBasicMaterial({color:f}));this.line.matrixAutoUpdate=!1;this.add(this.line);
-this.cone=new THREE.Mesh(b,new THREE.MeshBasicMaterial({color:f}));this.cone.matrixAutoUpdate=!1;this.add(this.cone);this.setDirection(c);this.setLength(e,g,h)}}();THREE.ArrowHelper.prototype=Object.create(THREE.Object3D.prototype);THREE.ArrowHelper.prototype.setDirection=function(){var a=new THREE.Vector3,b;return function(c){.99999<c.y?this.quaternion.set(0,0,0,1):-.99999>c.y?this.quaternion.set(1,0,0,0):(a.set(c.z,0,-c.x).normalize(),b=Math.acos(c.y),this.quaternion.setFromAxisAngle(a,b))}}();
-THREE.ArrowHelper.prototype.setLength=function(a,b,c){void 0===b&&(b=.2*a);void 0===c&&(c=.2*b);this.line.scale.set(1,a,1);this.line.updateMatrix();this.cone.scale.set(c,b,c);this.cone.position.y=a;this.cone.updateMatrix()};THREE.ArrowHelper.prototype.setColor=function(a){this.line.material.color.set(a);this.cone.material.color.set(a)};
-THREE.BoxHelper=function(a){var b=new THREE.BufferGeometry;b.addAttribute("position",new THREE.BufferAttribute(new Float32Array(72),3));THREE.Line.call(this,b,new THREE.LineBasicMaterial({color:16776960}),THREE.LinePieces);void 0!==a&&this.update(a)};THREE.BoxHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.BoxHelper.prototype.update=function(a){var b=a.geometry;null===b.boundingBox&&b.computeBoundingBox();var c=b.boundingBox.min,b=b.boundingBox.max,d=this.geometry.attributes.position.array;d[0]=b.x;d[1]=b.y;d[2]=b.z;d[3]=c.x;d[4]=b.y;d[5]=b.z;d[6]=c.x;d[7]=b.y;d[8]=b.z;d[9]=c.x;d[10]=c.y;d[11]=b.z;d[12]=c.x;d[13]=c.y;d[14]=b.z;d[15]=b.x;d[16]=c.y;d[17]=b.z;d[18]=b.x;d[19]=c.y;d[20]=b.z;d[21]=b.x;d[22]=b.y;d[23]=b.z;d[24]=b.x;d[25]=b.y;d[26]=c.z;d[27]=c.x;d[28]=b.y;d[29]=c.z;d[30]=c.x;d[31]=b.y;
-d[32]=c.z;d[33]=c.x;d[34]=c.y;d[35]=c.z;d[36]=c.x;d[37]=c.y;d[38]=c.z;d[39]=b.x;d[40]=c.y;d[41]=c.z;d[42]=b.x;d[43]=c.y;d[44]=c.z;d[45]=b.x;d[46]=b.y;d[47]=c.z;d[48]=b.x;d[49]=b.y;d[50]=b.z;d[51]=b.x;d[52]=b.y;d[53]=c.z;d[54]=c.x;d[55]=b.y;d[56]=b.z;d[57]=c.x;d[58]=b.y;d[59]=c.z;d[60]=c.x;d[61]=c.y;d[62]=b.z;d[63]=c.x;d[64]=c.y;d[65]=c.z;d[66]=b.x;d[67]=c.y;d[68]=b.z;d[69]=b.x;d[70]=c.y;d[71]=c.z;this.geometry.attributes.position.needsUpdate=!0;this.geometry.computeBoundingSphere();this.matrix=a.matrixWorld;
-this.matrixAutoUpdate=!1};THREE.BoundingBoxHelper=function(a,b){var c=void 0!==b?b:8947848;this.object=a;this.box=new THREE.Box3;THREE.Mesh.call(this,new THREE.BoxGeometry(1,1,1),new THREE.MeshBasicMaterial({color:c,wireframe:!0}))};THREE.BoundingBoxHelper.prototype=Object.create(THREE.Mesh.prototype);THREE.BoundingBoxHelper.prototype.update=function(){this.box.setFromObject(this.object);this.box.size(this.scale);this.box.center(this.position)};
-THREE.CameraHelper=function(a){function b(a,b,d){c(a,d);c(b,d)}function c(a,b){d.vertices.push(new THREE.Vector3);d.colors.push(new THREE.Color(b));void 0===f[a]&&(f[a]=[]);f[a].push(d.vertices.length-1)}var d=new THREE.Geometry,e=new THREE.LineBasicMaterial({color:16777215,vertexColors:THREE.FaceColors}),f={};b("n1","n2",16755200);b("n2","n4",16755200);b("n4","n3",16755200);b("n3","n1",16755200);b("f1","f2",16755200);b("f2","f4",16755200);b("f4","f3",16755200);b("f3","f1",16755200);b("n1","f1",16755200);
-b("n2","f2",16755200);b("n3","f3",16755200);b("n4","f4",16755200);b("p","n1",16711680);b("p","n2",16711680);b("p","n3",16711680);b("p","n4",16711680);b("u1","u2",43775);b("u2","u3",43775);b("u3","u1",43775);b("c","t",16777215);b("p","c",3355443);b("cn1","cn2",3355443);b("cn3","cn4",3355443);b("cf1","cf2",3355443);b("cf3","cf4",3355443);THREE.Line.call(this,d,e,THREE.LinePieces);this.camera=a;this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1;this.pointMap=f;this.update()};
-THREE.CameraHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.CameraHelper.prototype.update=function(){var a,b,c=new THREE.Vector3,d=new THREE.Camera,e=function(e,g,h,k){c.set(g,h,k).unproject(d);e=b[e];if(void 0!==e)for(g=0,h=e.length;g<h;g++)a.vertices[e[g]].copy(c)};return function(){a=this.geometry;b=this.pointMap;d.projectionMatrix.copy(this.camera.projectionMatrix);e("c",0,0,-1);e("t",0,0,1);e("n1",-1,-1,-1);e("n2",1,-1,-1);e("n3",-1,1,-1);e("n4",1,1,-1);e("f1",-1,-1,1);e("f2",1,-1,1);e("f3",-1,1,1);e("f4",1,1,1);e("u1",.7,1.1,-1);e("u2",-.7,1.1,
--1);e("u3",0,2,-1);e("cf1",-1,0,1);e("cf2",1,0,1);e("cf3",0,-1,1);e("cf4",0,1,1);e("cn1",-1,0,-1);e("cn2",1,0,-1);e("cn3",0,-1,-1);e("cn4",0,1,-1);a.verticesNeedUpdate=!0}}();
-THREE.DirectionalLightHelper=function(a,b){THREE.Object3D.call(this);this.light=a;this.light.updateMatrixWorld();this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1;b=b||1;var c=new THREE.Geometry;c.vertices.push(new THREE.Vector3(-b,b,0),new THREE.Vector3(b,b,0),new THREE.Vector3(b,-b,0),new THREE.Vector3(-b,-b,0),new THREE.Vector3(-b,b,0));var d=new THREE.LineBasicMaterial({fog:!1});d.color.copy(this.light.color).multiplyScalar(this.light.intensity);this.lightPlane=new THREE.Line(c,d);this.add(this.lightPlane);
-c=new THREE.Geometry;c.vertices.push(new THREE.Vector3,new THREE.Vector3);d=new THREE.LineBasicMaterial({fog:!1});d.color.copy(this.light.color).multiplyScalar(this.light.intensity);this.targetLine=new THREE.Line(c,d);this.add(this.targetLine);this.update()};THREE.DirectionalLightHelper.prototype=Object.create(THREE.Object3D.prototype);
-THREE.DirectionalLightHelper.prototype.dispose=function(){this.lightPlane.geometry.dispose();this.lightPlane.material.dispose();this.targetLine.geometry.dispose();this.targetLine.material.dispose()};
-THREE.DirectionalLightHelper.prototype.update=function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Vector3;return function(){a.setFromMatrixPosition(this.light.matrixWorld);b.setFromMatrixPosition(this.light.target.matrixWorld);c.subVectors(b,a);this.lightPlane.lookAt(c);this.lightPlane.material.color.copy(this.light.color).multiplyScalar(this.light.intensity);this.targetLine.geometry.vertices[1].copy(c);this.targetLine.geometry.verticesNeedUpdate=!0;this.targetLine.material.color.copy(this.lightPlane.material.color)}}();
-THREE.EdgesHelper=function(a,b){var c=void 0!==b?b:16777215,d=[0,0],e={},f=function(a,b){return a-b},g=["a","b","c"],h=new THREE.BufferGeometry,k=a.geometry.clone();k.mergeVertices();k.computeFaceNormals();for(var n=k.vertices,k=k.faces,p=0,q=0,m=k.length;q<m;q++)for(var r=k[q],t=0;3>t;t++){d[0]=r[g[t]];d[1]=r[g[(t+1)%3]];d.sort(f);var s=d.toString();void 0===e[s]?(e[s]={vert1:d[0],vert2:d[1],face1:q,face2:void 0},p++):e[s].face2=q}d=new Float32Array(6*p);f=0;for(s in e)if(g=e[s],void 0===g.face2||
-.9999>k[g.face1].normal.dot(k[g.face2].normal))p=n[g.vert1],d[f++]=p.x,d[f++]=p.y,d[f++]=p.z,p=n[g.vert2],d[f++]=p.x,d[f++]=p.y,d[f++]=p.z;h.addAttribute("position",new THREE.BufferAttribute(d,3));THREE.Line.call(this,h,new THREE.LineBasicMaterial({color:c}),THREE.LinePieces);this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1};THREE.EdgesHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.FaceNormalsHelper=function(a,b,c,d){this.object=a;this.size=void 0!==b?b:1;a=void 0!==c?c:16776960;d=void 0!==d?d:1;b=new THREE.Geometry;c=0;for(var e=this.object.geometry.faces.length;c<e;c++)b.vertices.push(new THREE.Vector3,new THREE.Vector3);THREE.Line.call(this,b,new THREE.LineBasicMaterial({color:a,linewidth:d}),THREE.LinePieces);this.matrixAutoUpdate=!1;this.normalMatrix=new THREE.Matrix3;this.update()};THREE.FaceNormalsHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.FaceNormalsHelper.prototype.update=function(){var a=this.geometry.vertices,b=this.object,c=b.geometry.vertices,d=b.geometry.faces,e=b.matrixWorld;b.updateMatrixWorld(!0);this.normalMatrix.getNormalMatrix(e);for(var f=b=0,g=d.length;b<g;b++,f+=2){var h=d[b];a[f].copy(c[h.a]).add(c[h.b]).add(c[h.c]).divideScalar(3).applyMatrix4(e);a[f+1].copy(h.normal).applyMatrix3(this.normalMatrix).normalize().multiplyScalar(this.size).add(a[f])}this.geometry.verticesNeedUpdate=!0;return this};
-THREE.GridHelper=function(a,b){var c=new THREE.Geometry,d=new THREE.LineBasicMaterial({vertexColors:THREE.VertexColors});this.color1=new THREE.Color(4473924);this.color2=new THREE.Color(8947848);for(var e=-a;e<=a;e+=b){c.vertices.push(new THREE.Vector3(-a,0,e),new THREE.Vector3(a,0,e),new THREE.Vector3(e,0,-a),new THREE.Vector3(e,0,a));var f=0===e?this.color1:this.color2;c.colors.push(f,f,f,f)}THREE.Line.call(this,c,d,THREE.LinePieces)};THREE.GridHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.GridHelper.prototype.setColors=function(a,b){this.color1.set(a);this.color2.set(b);this.geometry.colorsNeedUpdate=!0};
-THREE.HemisphereLightHelper=function(a,b,c,d){THREE.Object3D.call(this);this.light=a;this.light.updateMatrixWorld();this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1;this.colors=[new THREE.Color,new THREE.Color];a=new THREE.SphereGeometry(b,4,2);a.applyMatrix((new THREE.Matrix4).makeRotationX(-Math.PI/2));for(b=0;8>b;b++)a.faces[b].color=this.colors[4>b?0:1];b=new THREE.MeshBasicMaterial({vertexColors:THREE.FaceColors,wireframe:!0});this.lightSphere=new THREE.Mesh(a,b);this.add(this.lightSphere);
-this.update()};THREE.HemisphereLightHelper.prototype=Object.create(THREE.Object3D.prototype);THREE.HemisphereLightHelper.prototype.dispose=function(){this.lightSphere.geometry.dispose();this.lightSphere.material.dispose()};
-THREE.HemisphereLightHelper.prototype.update=function(){var a=new THREE.Vector3;return function(){this.colors[0].copy(this.light.color).multiplyScalar(this.light.intensity);this.colors[1].copy(this.light.groundColor).multiplyScalar(this.light.intensity);this.lightSphere.lookAt(a.setFromMatrixPosition(this.light.matrixWorld).negate());this.lightSphere.geometry.colorsNeedUpdate=!0}}();
-THREE.PointLightHelper=function(a,b){this.light=a;this.light.updateMatrixWorld();var c=new THREE.SphereGeometry(b,4,2),d=new THREE.MeshBasicMaterial({wireframe:!0,fog:!1});d.color.copy(this.light.color).multiplyScalar(this.light.intensity);THREE.Mesh.call(this,c,d);this.matrix=this.light.matrixWorld;this.matrixAutoUpdate=!1};THREE.PointLightHelper.prototype=Object.create(THREE.Mesh.prototype);THREE.PointLightHelper.prototype.dispose=function(){this.geometry.dispose();this.material.dispose()};
-THREE.PointLightHelper.prototype.update=function(){this.material.color.copy(this.light.color).multiplyScalar(this.light.intensity)};
-THREE.SkeletonHelper=function(a){this.bones=this.getBoneList(a);for(var b=new THREE.Geometry,c=0;c<this.bones.length;c++)this.bones[c].parent instanceof THREE.Bone&&(b.vertices.push(new THREE.Vector3),b.vertices.push(new THREE.Vector3),b.colors.push(new THREE.Color(0,0,1)),b.colors.push(new THREE.Color(0,1,0)));c=new THREE.LineBasicMaterial({vertexColors:THREE.VertexColors,depthTest:!1,depthWrite:!1,transparent:!0});THREE.Line.call(this,b,c,THREE.LinePieces);this.root=a;this.matrix=a.matrixWorld;
-this.matrixAutoUpdate=!1;this.update()};THREE.SkeletonHelper.prototype=Object.create(THREE.Line.prototype);THREE.SkeletonHelper.prototype.getBoneList=function(a){var b=[];a instanceof THREE.Bone&&b.push(a);for(var c=0;c<a.children.length;c++)b.push.apply(b,this.getBoneList(a.children[c]));return b};
-THREE.SkeletonHelper.prototype.update=function(){for(var a=this.geometry,b=(new THREE.Matrix4).getInverse(this.root.matrixWorld),c=new THREE.Matrix4,d=0,e=0;e<this.bones.length;e++){var f=this.bones[e];f.parent instanceof THREE.Bone&&(c.multiplyMatrices(b,f.matrixWorld),a.vertices[d].setFromMatrixPosition(c),c.multiplyMatrices(b,f.parent.matrixWorld),a.vertices[d+1].setFromMatrixPosition(c),d+=2)}a.verticesNeedUpdate=!0;a.computeBoundingSphere()};
-THREE.SpotLightHelper=function(a){THREE.Object3D.call(this);this.light=a;this.light.updateMatrixWorld();this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1;a=new THREE.CylinderGeometry(0,1,1,8,1,!0);a.applyMatrix((new THREE.Matrix4).makeTranslation(0,-.5,0));a.applyMatrix((new THREE.Matrix4).makeRotationX(-Math.PI/2));var b=new THREE.MeshBasicMaterial({wireframe:!0,fog:!1});this.cone=new THREE.Mesh(a,b);this.add(this.cone);this.update()};THREE.SpotLightHelper.prototype=Object.create(THREE.Object3D.prototype);
-THREE.SpotLightHelper.prototype.dispose=function(){this.cone.geometry.dispose();this.cone.material.dispose()};THREE.SpotLightHelper.prototype.update=function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(){var c=this.light.distance?this.light.distance:1E4,d=c*Math.tan(this.light.angle);this.cone.scale.set(d,d,c);a.setFromMatrixPosition(this.light.matrixWorld);b.setFromMatrixPosition(this.light.target.matrixWorld);this.cone.lookAt(b.sub(a));this.cone.material.color.copy(this.light.color).multiplyScalar(this.light.intensity)}}();
-THREE.VertexNormalsHelper=function(a,b,c,d){this.object=a;this.size=void 0!==b?b:1;b=void 0!==c?c:16711680;d=void 0!==d?d:1;c=new THREE.Geometry;a=a.geometry.faces;for(var e=0,f=a.length;e<f;e++)for(var g=0,h=a[e].vertexNormals.length;g<h;g++)c.vertices.push(new THREE.Vector3,new THREE.Vector3);THREE.Line.call(this,c,new THREE.LineBasicMaterial({color:b,linewidth:d}),THREE.LinePieces);this.matrixAutoUpdate=!1;this.normalMatrix=new THREE.Matrix3;this.update()};THREE.VertexNormalsHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.VertexNormalsHelper.prototype.update=function(a){var b=new THREE.Vector3;return function(a){a=["a","b","c","d"];this.object.updateMatrixWorld(!0);this.normalMatrix.getNormalMatrix(this.object.matrixWorld);for(var d=this.geometry.vertices,e=this.object.geometry.vertices,f=this.object.geometry.faces,g=this.object.matrixWorld,h=0,k=0,n=f.length;k<n;k++)for(var p=f[k],q=0,m=p.vertexNormals.length;q<m;q++){var r=p.vertexNormals[q];d[h].copy(e[p[a[q]]]).applyMatrix4(g);b.copy(r).applyMatrix3(this.normalMatrix).normalize().multiplyScalar(this.size);
-b.add(d[h]);h+=1;d[h].copy(b);h+=1}this.geometry.verticesNeedUpdate=!0;return this}}();
-THREE.VertexTangentsHelper=function(a,b,c,d){this.object=a;this.size=void 0!==b?b:1;b=void 0!==c?c:255;d=void 0!==d?d:1;c=new THREE.Geometry;a=a.geometry.faces;for(var e=0,f=a.length;e<f;e++)for(var g=0,h=a[e].vertexTangents.length;g<h;g++)c.vertices.push(new THREE.Vector3),c.vertices.push(new THREE.Vector3);THREE.Line.call(this,c,new THREE.LineBasicMaterial({color:b,linewidth:d}),THREE.LinePieces);this.matrixAutoUpdate=!1;this.update()};THREE.VertexTangentsHelper.prototype=Object.create(THREE.Line.prototype);
-THREE.VertexTangentsHelper.prototype.update=function(a){var b=new THREE.Vector3;return function(a){a=["a","b","c","d"];this.object.updateMatrixWorld(!0);for(var d=this.geometry.vertices,e=this.object.geometry.vertices,f=this.object.geometry.faces,g=this.object.matrixWorld,h=0,k=0,n=f.length;k<n;k++)for(var p=f[k],q=0,m=p.vertexTangents.length;q<m;q++){var r=p.vertexTangents[q];d[h].copy(e[p[a[q]]]).applyMatrix4(g);b.copy(r).transformDirection(g).multiplyScalar(this.size);b.add(d[h]);h+=1;d[h].copy(b);
-h+=1}this.geometry.verticesNeedUpdate=!0;return this}}();
-THREE.WireframeHelper=function(a,b){var c=void 0!==b?b:16777215,d=[0,0],e={},f=function(a,b){return a-b},g=["a","b","c"],h=new THREE.BufferGeometry;if(a.geometry instanceof THREE.Geometry){for(var k=a.geometry.vertices,n=a.geometry.faces,p=0,q=new Uint32Array(6*n.length),m=0,r=n.length;m<r;m++)for(var t=n[m],s=0;3>s;s++){d[0]=t[g[s]];d[1]=t[g[(s+1)%3]];d.sort(f);var u=d.toString();void 0===e[u]&&(q[2*p]=d[0],q[2*p+1]=d[1],e[u]=!0,p++)}d=new Float32Array(6*p);m=0;for(r=p;m<r;m++)for(s=0;2>s;s++)p=
-k[q[2*m+s]],g=6*m+3*s,d[g+0]=p.x,d[g+1]=p.y,d[g+2]=p.z;h.addAttribute("position",new THREE.BufferAttribute(d,3))}else if(a.geometry instanceof THREE.BufferGeometry){if(void 0!==a.geometry.attributes.index){k=a.geometry.attributes.position.array;r=a.geometry.attributes.index.array;n=a.geometry.drawcalls;p=0;0===n.length&&(n=[{count:r.length,index:0,start:0}]);for(var q=new Uint32Array(2*r.length),t=0,v=n.length;t<v;++t)for(var s=n[t].start,u=n[t].count,g=n[t].index,m=s,y=s+u;m<y;m+=3)for(s=0;3>s;s++)d[0]=
-g+r[m+s],d[1]=g+r[m+(s+1)%3],d.sort(f),u=d.toString(),void 0===e[u]&&(q[2*p]=d[0],q[2*p+1]=d[1],e[u]=!0,p++);d=new Float32Array(6*p);m=0;for(r=p;m<r;m++)for(s=0;2>s;s++)g=6*m+3*s,p=3*q[2*m+s],d[g+0]=k[p],d[g+1]=k[p+1],d[g+2]=k[p+2]}else for(k=a.geometry.attributes.position.array,p=k.length/3,q=p/3,d=new Float32Array(6*p),m=0,r=q;m<r;m++)for(s=0;3>s;s++)g=18*m+6*s,q=9*m+3*s,d[g+0]=k[q],d[g+1]=k[q+1],d[g+2]=k[q+2],p=9*m+(s+1)%3*3,d[g+3]=k[p],d[g+4]=k[p+1],d[g+5]=k[p+2];h.addAttribute("position",new THREE.BufferAttribute(d,
-3))}THREE.Line.call(this,h,new THREE.LineBasicMaterial({color:c}),THREE.LinePieces);this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1};THREE.WireframeHelper.prototype=Object.create(THREE.Line.prototype);THREE.ImmediateRenderObject=function(){THREE.Object3D.call(this);this.render=function(a){}};THREE.ImmediateRenderObject.prototype=Object.create(THREE.Object3D.prototype);
-THREE.MorphBlendMesh=function(a,b){THREE.Mesh.call(this,a,b);this.animationsMap={};this.animationsList=[];var c=this.geometry.morphTargets.length;this.createAnimation("__default",0,c-1,c/1);this.setAnimationWeight("__default",1)};THREE.MorphBlendMesh.prototype=Object.create(THREE.Mesh.prototype);
-THREE.MorphBlendMesh.prototype.createAnimation=function(a,b,c,d){b={startFrame:b,endFrame:c,length:c-b+1,fps:d,duration:(c-b)/d,lastFrame:0,currentFrame:0,active:!1,time:0,direction:1,weight:1,directionBackwards:!1,mirroredLoop:!1};this.animationsMap[a]=b;this.animationsList.push(b)};
-THREE.MorphBlendMesh.prototype.autoCreateAnimations=function(a){for(var b=/([a-z]+)_?(\d+)/,c,d={},e=this.geometry,f=0,g=e.morphTargets.length;f<g;f++){var h=e.morphTargets[f].name.match(b);if(h&&1<h.length){var k=h[1];d[k]||(d[k]={start:Infinity,end:-Infinity});h=d[k];f<h.start&&(h.start=f);f>h.end&&(h.end=f);c||(c=k)}}for(k in d)h=d[k],this.createAnimation(k,h.start,h.end,a);this.firstAnimation=c};
-THREE.MorphBlendMesh.prototype.setAnimationDirectionForward=function(a){if(a=this.animationsMap[a])a.direction=1,a.directionBackwards=!1};THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward=function(a){if(a=this.animationsMap[a])a.direction=-1,a.directionBackwards=!0};THREE.MorphBlendMesh.prototype.setAnimationFPS=function(a,b){var c=this.animationsMap[a];c&&(c.fps=b,c.duration=(c.end-c.start)/c.fps)};
-THREE.MorphBlendMesh.prototype.setAnimationDuration=function(a,b){var c=this.animationsMap[a];c&&(c.duration=b,c.fps=(c.end-c.start)/c.duration)};THREE.MorphBlendMesh.prototype.setAnimationWeight=function(a,b){var c=this.animationsMap[a];c&&(c.weight=b)};THREE.MorphBlendMesh.prototype.setAnimationTime=function(a,b){var c=this.animationsMap[a];c&&(c.time=b)};THREE.MorphBlendMesh.prototype.getAnimationTime=function(a){var b=0;if(a=this.animationsMap[a])b=a.time;return b};
-THREE.MorphBlendMesh.prototype.getAnimationDuration=function(a){var b=-1;if(a=this.animationsMap[a])b=a.duration;return b};THREE.MorphBlendMesh.prototype.playAnimation=function(a){var b=this.animationsMap[a];b?(b.time=0,b.active=!0):console.warn("animation["+a+"] undefined")};THREE.MorphBlendMesh.prototype.stopAnimation=function(a){if(a=this.animationsMap[a])a.active=!1};
-THREE.MorphBlendMesh.prototype.update=function(a){for(var b=0,c=this.animationsList.length;b<c;b++){var d=this.animationsList[b];if(d.active){var e=d.duration/d.length;d.time+=d.direction*a;if(d.mirroredLoop){if(d.time>d.duration||0>d.time)d.direction*=-1,d.time>d.duration&&(d.time=d.duration,d.directionBackwards=!0),0>d.time&&(d.time=0,d.directionBackwards=!1)}else d.time%=d.duration,0>d.time&&(d.time+=d.duration);var f=d.startFrame+THREE.Math.clamp(Math.floor(d.time/e),0,d.length-1),g=d.weight;
-f!==d.currentFrame&&(this.morphTargetInfluences[d.lastFrame]=0,this.morphTargetInfluences[d.currentFrame]=1*g,this.morphTargetInfluences[f]=0,d.lastFrame=d.currentFrame,d.currentFrame=f);e=d.time%e/e;d.directionBackwards&&(e=1-e);this.morphTargetInfluences[d.currentFrame]=e*g;this.morphTargetInfluences[d.lastFrame]=(1-e)*g}}};
diff --git a/plugins/Sidebar/media_globe/world.jpg b/plugins/Sidebar/media_globe/world.jpg
deleted file mode 100644
index 222bd939d5bf2067910ef4f9da2ea9092dfd6abb..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 94795
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
Z`oH@t|Ln8>|5^UC@4$cE@5g>^`)^we#C!k%

diff --git a/plugins/Sidebar/plugin_info.json b/plugins/Sidebar/plugin_info.json
deleted file mode 100644
index 7d6f91fc..00000000
--- a/plugins/Sidebar/plugin_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"name": "Sidebar",
-	"description": "Access site management sidebar and console by dragging top-right 0 button to left or down.",
-	"default": "enabled"
-}
\ No newline at end of file
diff --git a/plugins/Stats/StatsPlugin.py b/plugins/Stats/StatsPlugin.py
deleted file mode 100644
index cdd09d8f..00000000
--- a/plugins/Stats/StatsPlugin.py
+++ /dev/null
@@ -1,634 +0,0 @@
-import time
-import html
-import os
-import json
-import sys
-import itertools
-
-from Plugin import PluginManager
-from Config import config
-from util import helper
-from Debug import Debug
-from Db import Db
-
-
-@PluginManager.registerTo("UiRequest")
-class UiRequestPlugin(object):
-
-    def formatTableRow(self, row, class_name=""):
-        back = []
-        for format, val in row:
-            if val is None:
-                formatted = "n/a"
-            elif format == "since":
-                if val:
-                    formatted = "%.0f" % (time.time() - val)
-                else:
-                    formatted = "n/a"
-            else:
-                formatted = format % val
-            back.append("<td>%s</td>" % formatted)
-        return "<tr class='%s'>%s</tr>" % (class_name, "".join(back))
-
-    def getObjSize(self, obj, hpy=None):
-        if hpy:
-            return float(hpy.iso(obj).domisize) / 1024
-        else:
-            return 0
-
-    def renderHead(self):
-        import main
-        from Crypt import CryptConnection
-
-        # Memory
-        yield "rev%s | " % config.rev
-        yield "%s | " % main.file_server.ip_external_list
-        yield "Port: %s | " % main.file_server.port
-        yield "Network: %s | " % main.file_server.supported_ip_types
-        yield "Opened: %s | " % main.file_server.port_opened
-        yield "Crypt: %s, TLSv1.3: %s | " % (CryptConnection.manager.crypt_supported, CryptConnection.ssl.HAS_TLSv1_3)
-        yield "In: %.2fMB, Out: %.2fMB  | " % (
-            float(main.file_server.bytes_recv) / 1024 / 1024,
-            float(main.file_server.bytes_sent) / 1024 / 1024
-        )
-        yield "Peerid: %s  | " % main.file_server.peer_id
-        yield "Time: %.2fs | " % main.file_server.getTimecorrection()
-        yield "Blocks: %s" % Debug.num_block
-
-        try:
-            import psutil
-            process = psutil.Process(os.getpid())
-            mem = process.get_memory_info()[0] / float(2 ** 20)
-            yield "Mem: %.2fMB | " % mem
-            yield "Threads: %s | " % len(process.threads())
-            yield "CPU: usr %.2fs sys %.2fs | " % process.cpu_times()
-            yield "Files: %s | " % len(process.open_files())
-            yield "Sockets: %s | " % len(process.connections())
-            yield "Calc size <a href='?size=1'>on</a> <a href='?size=0'>off</a>"
-        except Exception:
-            pass
-        yield "<br>"
-
-    def renderConnectionsTable(self):
-        import main
-
-        # Connections
-        yield "<b>Connections</b> (%s, current version: %s, total made: %s, in: %s, out: %s):<br>" % (
-            len(main.file_server.connections), main.file_server.last_connection_id_current_version, main.file_server.last_connection_id,
-            main.file_server.num_incoming, main.file_server.num_outgoing
-        )
-        yield "<table class='connections'><tr> <th>id</th> <th>type</th> <th>ip</th> <th>open</th> <th>crypt</th> <th>ping</th>"
-        yield "<th>buff</th> <th>bad</th> <th>idle</th> <th>open</th> <th>delay</th> <th>cpu</th> <th>out</th> <th>in</th> <th>last sent</th>"
-        yield "<th>wait</th> <th>version</th> <th>time</th> <th>sites</th> </tr>"
-        connections = sorted(main.file_server.connections, key=lambda connection: connection.handshake.get("rev", 0), reverse=True)
-        for connection in connections:
-            if "cipher" in dir(connection.sock):
-                cipher = connection.sock.cipher()[0]
-                tls_version = connection.sock.version()
-            else:
-                cipher = connection.crypt
-                tls_version = ""
-            if "time" in connection.handshake and connection.last_ping_delay:
-                time_correction = connection.handshake["time"] - connection.handshake_time - connection.last_ping_delay
-            else:
-                time_correction = 0.0
-            yield self.formatTableRow([
-                ("%3d", connection.id),
-                ("%s", connection.type),
-                ("%s:%s", (connection.ip, connection.port)),
-                ("%s", connection.handshake.get("port_opened")),
-                ("<span title='%s %s'>%s</span>", (cipher, tls_version, connection.crypt)),
-                ("%6.3f", connection.last_ping_delay),
-                ("%s", connection.incomplete_buff_recv),
-                ("%s", connection.bad_actions),
-                ("since", max(connection.last_send_time, connection.last_recv_time)),
-                ("since", connection.start_time),
-                ("%.3f", max(-1, connection.last_sent_time - connection.last_send_time)),
-                ("%.3f", connection.cpu_time),
-                ("%.0fk", connection.bytes_sent / 1024),
-                ("%.0fk", connection.bytes_recv / 1024),
-                ("<span title='Recv: %s'>%s</span>", (connection.last_cmd_recv, connection.last_cmd_sent)),
-                ("%s", list(connection.waiting_requests.keys())),
-                ("%s r%s", (connection.handshake.get("version"), connection.handshake.get("rev", "?"))),
-                ("%.2fs", time_correction),
-                ("%s", connection.sites)
-            ])
-        yield "</table>"
-
-    def renderTrackers(self):
-        # Trackers
-        yield "<br><br><b>Trackers:</b><br>"
-        yield "<table class='trackers'><tr> <th>address</th> <th>request</th> <th>successive errors</th> <th>last_request</th></tr>"
-        from Site import SiteAnnouncer  # importing at the top of the file breaks plugins
-        for tracker_address, tracker_stat in sorted(SiteAnnouncer.global_stats.items()):
-            yield self.formatTableRow([
-                ("%s", tracker_address),
-                ("%s", tracker_stat["num_request"]),
-                ("%s", tracker_stat["num_error"]),
-                ("%.0f min ago", min(999, (time.time() - tracker_stat["time_request"]) / 60))
-            ])
-        yield "</table>"
-
-        if "AnnounceShare" in PluginManager.plugin_manager.plugin_names:
-            yield "<br><br><b>Shared trackers:</b><br>"
-            yield "<table class='trackers'><tr> <th>address</th> <th>added</th> <th>found</th> <th>latency</th> <th>successive errors</th> <th>last_success</th></tr>"
-            from AnnounceShare import AnnounceSharePlugin
-            for tracker_address, tracker_stat in sorted(AnnounceSharePlugin.tracker_storage.getTrackers().items()):
-                yield self.formatTableRow([
-                    ("%s", tracker_address),
-                    ("%.0f min ago", min(999, (time.time() - tracker_stat["time_added"]) / 60)),
-                    ("%.0f min ago", min(999, (time.time() - tracker_stat.get("time_found", 0)) / 60)),
-                    ("%.3fs", tracker_stat["latency"]),
-                    ("%s", tracker_stat["num_error"]),
-                    ("%.0f min ago", min(999, (time.time() - tracker_stat["time_success"]) / 60)),
-                ])
-            yield "</table>"
-
-    def renderTor(self):
-        import main
-        yield "<br><br><b>Tor hidden services (status: %s):</b><br>" % main.file_server.tor_manager.status
-        for site_address, onion in list(main.file_server.tor_manager.site_onions.items()):
-            yield "- %-34s: %s<br>" % (site_address, onion)
-
-    def renderDbStats(self):
-        yield "<br><br><b>Db</b>:<br>"
-        for db in Db.opened_dbs:
-            tables = [row["name"] for row in db.execute("SELECT name FROM sqlite_master WHERE type = 'table'").fetchall()]
-            table_rows = {}
-            for table in tables:
-                table_rows[table] = db.execute("SELECT COUNT(*) AS c FROM %s" % table).fetchone()["c"]
-            db_size = os.path.getsize(db.db_path) / 1024.0 / 1024.0
-            yield "- %.3fs: %s %.3fMB, table rows: %s<br>" % (
-                time.time() - db.last_query_time, db.db_path, db_size, json.dumps(table_rows, sort_keys=True)
-            )
-
-    def renderSites(self):
-        yield "<br><br><b>Sites</b>:"
-        yield "<table>"
-        yield "<tr><th>address</th> <th>connected</th> <th title='connected/good/total'>peers</th> <th>content.json</th> <th>out</th> <th>in</th>  </tr>"
-        for site in list(self.server.sites.values()):
-            yield self.formatTableRow([
-                (
-                    """<a href='#' onclick='document.getElementById("peers_%s").style.display="initial"; return false'>%s</a>""",
-                    (site.address, site.address)
-                ),
-                ("%s", [peer.connection.id for peer in list(site.peers.values()) if peer.connection and peer.connection.connected]),
-                ("%s/%s/%s", (
-                    len([peer for peer in list(site.peers.values()) if peer.connection and peer.connection.connected]),
-                    len(site.getConnectablePeers(100)),
-                    len(site.peers)
-                )),
-                ("%s (loaded: %s)", (
-                    len(site.content_manager.contents),
-                    len([key for key, val in dict(site.content_manager.contents).items() if val])
-                )),
-                ("%.0fk", site.settings.get("bytes_sent", 0) / 1024),
-                ("%.0fk", site.settings.get("bytes_recv", 0) / 1024),
-            ], "serving-%s" % site.settings["serving"])
-            yield "<tr><td id='peers_%s' style='display: none; white-space: pre' colspan=6>" % site.address
-            for key, peer in list(site.peers.items()):
-                if peer.time_found:
-                    time_found = int(time.time() - peer.time_found) / 60
-                else:
-                    time_found = "--"
-                if peer.connection:
-                    connection_id = peer.connection.id
-                else:
-                    connection_id = None
-                if site.content_manager.has_optional_files:
-                    yield "Optional files: %4s " % len(peer.hashfield)
-                time_added = (time.time() - peer.time_added) / (60 * 60 * 24)
-                yield "(#%4s, rep: %2s, err: %s, found: %.1fs min, add: %.1f day) %30s -<br>" % (connection_id, peer.reputation, peer.connection_error, time_found, time_added, key)
-            yield "<br></td></tr>"
-        yield "</table>"
-
-    def renderBigfiles(self):
-        yield "<br><br><b>Big files</b>:<br>"
-        for site in list(self.server.sites.values()):
-            if not site.settings.get("has_bigfile"):
-                continue
-            bigfiles = {}
-            yield """<a href="#" onclick='document.getElementById("bigfiles_%s").style.display="initial"; return false'>%s</a><br>""" % (site.address, site.address)
-            for peer in list(site.peers.values()):
-                if not peer.time_piecefields_updated:
-                    continue
-                for sha512, piecefield in peer.piecefields.items():
-                    if sha512 not in bigfiles:
-                        bigfiles[sha512] = []
-                    bigfiles[sha512].append(peer)
-
-            yield "<div id='bigfiles_%s' style='display: none'>" % site.address
-            for sha512, peers in bigfiles.items():
-                yield "<br> - " + sha512 + " (hash id: %s)<br>" % site.content_manager.hashfield.getHashId(sha512)
-                yield "<table>"
-                for peer in peers:
-                    yield "<tr><td>" + peer.key + "</td><td>" + peer.piecefields[sha512].tostring() + "</td></tr>"
-                yield "</table>"
-            yield "</div>"
-
-    def renderRequests(self):
-        import main
-        yield "<div style='float: left'>"
-        yield "<br><br><b>Sent commands</b>:<br>"
-        yield "<table>"
-        for stat_key, stat in sorted(main.file_server.stat_sent.items(), key=lambda i: i[1]["bytes"], reverse=True):
-            yield "<tr><td>%s</td><td style='white-space: nowrap'>x %s =</td><td>%.0fkB</td></tr>" % (stat_key, stat["num"], stat["bytes"] / 1024)
-        yield "</table>"
-        yield "</div>"
-
-        yield "<div style='float: left; margin-left: 20%; max-width: 50%'>"
-        yield "<br><br><b>Received commands</b>:<br>"
-        yield "<table>"
-        for stat_key, stat in sorted(main.file_server.stat_recv.items(), key=lambda i: i[1]["bytes"], reverse=True):
-            yield "<tr><td>%s</td><td style='white-space: nowrap'>x %s =</td><td>%.0fkB</td></tr>" % (stat_key, stat["num"], stat["bytes"] / 1024)
-        yield "</table>"
-        yield "</div>"
-        yield "<div style='clear: both'></div>"
-
-    def renderMemory(self):
-        import gc
-        from Ui import UiRequest
-
-        hpy = None
-        if self.get.get("size") == "1":  # Calc obj size
-            try:
-                import guppy
-                hpy = guppy.hpy()
-            except Exception:
-                pass
-        self.sendHeader()
-
-        # Object types
-
-        obj_count = {}
-        for obj in gc.get_objects():
-            obj_type = str(type(obj))
-            if obj_type not in obj_count:
-                obj_count[obj_type] = [0, 0]
-            obj_count[obj_type][0] += 1  # Count
-            obj_count[obj_type][1] += float(sys.getsizeof(obj)) / 1024  # Size
-
-        yield "<br><br><b>Objects in memory (types: %s, total: %s, %.2fkb):</b><br>" % (
-            len(obj_count),
-            sum([stat[0] for stat in list(obj_count.values())]),
-            sum([stat[1] for stat in list(obj_count.values())])
-        )
-
-        for obj, stat in sorted(list(obj_count.items()), key=lambda x: x[1][0], reverse=True):  # Sorted by count
-            yield " - %.1fkb = %s x <a href=\"/Listobj?type=%s\">%s</a><br>" % (stat[1], stat[0], obj, html.escape(obj))
-
-        # Classes
-
-        class_count = {}
-        for obj in gc.get_objects():
-            obj_type = str(type(obj))
-            if obj_type != "<type 'instance'>":
-                continue
-            class_name = obj.__class__.__name__
-            if class_name not in class_count:
-                class_count[class_name] = [0, 0]
-            class_count[class_name][0] += 1  # Count
-            class_count[class_name][1] += float(sys.getsizeof(obj)) / 1024  # Size
-
-        yield "<br><br><b>Classes in memory (types: %s, total: %s, %.2fkb):</b><br>" % (
-            len(class_count),
-            sum([stat[0] for stat in list(class_count.values())]),
-            sum([stat[1] for stat in list(class_count.values())])
-        )
-
-        for obj, stat in sorted(list(class_count.items()), key=lambda x: x[1][0], reverse=True):  # Sorted by count
-            yield " - %.1fkb = %s x <a href=\"/Dumpobj?class=%s\">%s</a><br>" % (stat[1], stat[0], obj, html.escape(obj))
-
-        from greenlet import greenlet
-        objs = [obj for obj in gc.get_objects() if isinstance(obj, greenlet)]
-        yield "<br>Greenlets (%s):<br>" % len(objs)
-        for obj in objs:
-            yield " - %.1fkb: %s<br>" % (self.getObjSize(obj, hpy), html.escape(repr(obj)))
-
-        from Worker import Worker
-        objs = [obj for obj in gc.get_objects() if isinstance(obj, Worker)]
-        yield "<br>Workers (%s):<br>" % len(objs)
-        for obj in objs:
-            yield " - %.1fkb: %s<br>" % (self.getObjSize(obj, hpy), html.escape(repr(obj)))
-
-        from Connection import Connection
-        objs = [obj for obj in gc.get_objects() if isinstance(obj, Connection)]
-        yield "<br>Connections (%s):<br>" % len(objs)
-        for obj in objs:
-            yield " - %.1fkb: %s<br>" % (self.getObjSize(obj, hpy), html.escape(repr(obj)))
-
-        from socket import socket
-        objs = [obj for obj in gc.get_objects() if isinstance(obj, socket)]
-        yield "<br>Sockets (%s):<br>" % len(objs)
-        for obj in objs:
-            yield " - %.1fkb: %s<br>" % (self.getObjSize(obj, hpy), html.escape(repr(obj)))
-
-        from msgpack import Unpacker
-        objs = [obj for obj in gc.get_objects() if isinstance(obj, Unpacker)]
-        yield "<br>Msgpack unpacker (%s):<br>" % len(objs)
-        for obj in objs:
-            yield " - %.1fkb: %s<br>" % (self.getObjSize(obj, hpy), html.escape(repr(obj)))
-
-        from Site.Site import Site
-        objs = [obj for obj in gc.get_objects() if isinstance(obj, Site)]
-        yield "<br>Sites (%s):<br>" % len(objs)
-        for obj in objs:
-            yield " - %.1fkb: %s<br>" % (self.getObjSize(obj, hpy), html.escape(repr(obj)))
-
-        objs = [obj for obj in gc.get_objects() if isinstance(obj, self.server.log.__class__)]
-        yield "<br>Loggers (%s):<br>" % len(objs)
-        for obj in objs:
-            yield " - %.1fkb: %s<br>" % (self.getObjSize(obj, hpy), html.escape(repr(obj.name)))
-
-        objs = [obj for obj in gc.get_objects() if isinstance(obj, UiRequest)]
-        yield "<br>UiRequests (%s):<br>" % len(objs)
-        for obj in objs:
-            yield " - %.1fkb: %s<br>" % (self.getObjSize(obj, hpy), html.escape(repr(obj)))
-
-        from Peer import Peer
-        objs = [obj for obj in gc.get_objects() if isinstance(obj, Peer)]
-        yield "<br>Peers (%s):<br>" % len(objs)
-        for obj in objs:
-            yield " - %.1fkb: %s<br>" % (self.getObjSize(obj, hpy), html.escape(repr(obj)))
-
-        objs = [(key, val) for key, val in sys.modules.items() if val is not None]
-        objs.sort()
-        yield "<br>Modules (%s):<br>" % len(objs)
-        for module_name, module in objs:
-            yield " - %.3fkb: %s %s<br>" % (self.getObjSize(module, hpy), module_name, html.escape(repr(module)))
-
-    # /Stats entry point
-    @helper.encodeResponse
-    def actionStats(self):
-        import gc
-
-        self.sendHeader()
-
-        if "Multiuser" in PluginManager.plugin_manager.plugin_names and not config.multiuser_local:
-            if 'access_key' not in self.get.keys():
-                yield "This function is disabled on this proxy"
-                return
-            else: 
-                access_key = self.get["access_key"]
-                if access_key != config.access_key:
-                    yield "This function is disabled on this proxy"
-                    return
-
-        s = time.time()
-
-        # Style
-        yield """
-        <style>
-         * { font-family: monospace }
-         table td, table th { text-align: right; padding: 0px 10px }
-         .connections td { white-space: nowrap }
-         .serving-False { opacity: 0.3 }
-        </style>
-        """
-
-        renderers = [
-            self.renderHead(),
-            self.renderConnectionsTable(),
-            self.renderTrackers(),
-            self.renderTor(),
-            self.renderDbStats(),
-            self.renderSites(),
-            self.renderBigfiles(),
-            self.renderRequests()
-
-        ]
-
-        for part in itertools.chain(*renderers):
-            yield part
-
-        if config.debug:
-            for part in self.renderMemory():
-                yield part
-
-        gc.collect()  # Implicit grabage collection
-        yield "Done in %.1f" % (time.time() - s)
-
-    @helper.encodeResponse
-    def actionDumpobj(self):
-
-        import gc
-        import sys
-
-        self.sendHeader()
-
-        if "Multiuser" in PluginManager.plugin_manager.plugin_names and not config.multiuser_local:
-            yield "This function is disabled on this proxy"
-            return
-
-        # No more if not in debug mode
-        if not config.debug:
-            yield "Not in debug mode"
-            return
-
-        class_filter = self.get.get("class")
-
-        yield """
-        <style>
-         * { font-family: monospace; white-space: pre }
-         table * { text-align: right; padding: 0px 10px }
-        </style>
-        """
-
-        objs = gc.get_objects()
-        for obj in objs:
-            obj_type = str(type(obj))
-            if obj_type != "<type 'instance'>" or obj.__class__.__name__ != class_filter:
-                continue
-            yield "%.1fkb %s... " % (float(sys.getsizeof(obj)) / 1024, html.escape(str(obj)))
-            for attr in dir(obj):
-                yield "- %s: %s<br>" % (attr, html.escape(str(getattr(obj, attr))))
-            yield "<br>"
-
-        gc.collect()  # Implicit grabage collection
-
-    @helper.encodeResponse
-    def actionListobj(self):
-
-        import gc
-        import sys
-
-        self.sendHeader()
-
-        if "Multiuser" in PluginManager.plugin_manager.plugin_names and not config.multiuser_local:
-            yield "This function is disabled on this proxy"
-            return
-
-        # No more if not in debug mode
-        if not config.debug:
-            yield "Not in debug mode"
-            return
-
-        type_filter = self.get.get("type")
-
-        yield """
-        <style>
-         * { font-family: monospace; white-space: pre }
-         table * { text-align: right; padding: 0px 10px }
-        </style>
-        """
-
-        yield "Listing all %s objects in memory...<br>" % html.escape(type_filter)
-
-        ref_count = {}
-        objs = gc.get_objects()
-        for obj in objs:
-            obj_type = str(type(obj))
-            if obj_type != type_filter:
-                continue
-            refs = [
-                ref for ref in gc.get_referrers(obj)
-                if hasattr(ref, "__class__") and
-                ref.__class__.__name__ not in ["list", "dict", "function", "type", "frame", "WeakSet", "tuple"]
-            ]
-            if not refs:
-                continue
-            try:
-                yield "%.1fkb <span title=\"%s\">%s</span>... " % (
-                    float(sys.getsizeof(obj)) / 1024, html.escape(str(obj)), html.escape(str(obj)[0:100].ljust(100))
-                )
-            except Exception:
-                continue
-            for ref in refs:
-                yield " ["
-                if "object at" in str(ref) or len(str(ref)) > 100:
-                    yield str(ref.__class__.__name__)
-                else:
-                    yield str(ref.__class__.__name__) + ":" + html.escape(str(ref))
-                yield "] "
-                ref_type = ref.__class__.__name__
-                if ref_type not in ref_count:
-                    ref_count[ref_type] = [0, 0]
-                ref_count[ref_type][0] += 1  # Count
-                ref_count[ref_type][1] += float(sys.getsizeof(obj)) / 1024  # Size
-            yield "<br>"
-
-        yield "<br>Object referrer (total: %s, %.2fkb):<br>" % (len(ref_count), sum([stat[1] for stat in list(ref_count.values())]))
-
-        for obj, stat in sorted(list(ref_count.items()), key=lambda x: x[1][0], reverse=True)[0:30]:  # Sorted by count
-            yield " - %.1fkb = %s x %s<br>" % (stat[1], stat[0], html.escape(str(obj)))
-
-        gc.collect()  # Implicit grabage collection
-
-    @helper.encodeResponse
-    def actionGcCollect(self):
-        import gc
-        self.sendHeader()
-        yield str(gc.collect())
-
-    # /About entry point
-    @helper.encodeResponse
-    def actionEnv(self):
-        import main
-
-        self.sendHeader()
-
-        yield """
-        <style>
-         * { font-family: monospace; white-space: pre; }
-         h2 { font-size: 100%; margin-bottom: 0px; }
-         small { opacity: 0.5; }
-         table { border-collapse: collapse; }
-         td { padding-right: 10px; }
-        </style>
-        """
-
-        if "Multiuser" in PluginManager.plugin_manager.plugin_names and not config.multiuser_local:
-            yield "This function is disabled on this proxy"
-            return
-
-        yield from main.actions.testEnv(format="html")
-
-
-@PluginManager.registerTo("Actions")
-class ActionsPlugin:
-    def formatTable(self, *rows, format="text"):
-        if format == "html":
-            return self.formatTableHtml(*rows)
-        else:
-            return self.formatTableText(*rows)
-
-    def formatHead(self, title, format="text"):
-        if format == "html":
-            return "<h2>%s</h2>" % title
-        else:
-            return "\n* %s\n" % title
-
-    def formatTableHtml(self, *rows):
-        yield "<table>"
-        for row in rows:
-            yield "<tr>"
-            for col in row:
-                yield "<td>%s</td>" % html.escape(str(col))
-            yield "</tr>"
-        yield "</table>"
-
-    def formatTableText(self, *rows):
-        for row in rows:
-            yield " "
-            for col in row:
-                yield " " + str(col)
-            yield "\n"
-
-    def testEnv(self, format="text"):
-        import gevent
-        import msgpack
-        import pkg_resources
-        import importlib
-        import coincurve
-        import sqlite3
-        from Crypt import CryptBitcoin
-
-        yield "\n"
-
-        yield from self.formatTable(
-            ["ZeroNet version:", "%s rev%s" % (config.version, config.rev)],
-            ["Python:", "%s" % sys.version],
-            ["Platform:", "%s" % sys.platform],
-            ["Crypt verify lib:", "%s" % CryptBitcoin.lib_verify_best],
-            ["OpenSSL:", "%s" % CryptBitcoin.sslcrypto.ecc.get_backend()],
-            ["Libsecp256k1:", "%s" % type(coincurve._libsecp256k1.lib).__name__],
-            ["SQLite:", "%s, API: %s" % (sqlite3.sqlite_version, sqlite3.version)],
-            format=format
-        )
-
-
-        yield self.formatHead("Libraries:")
-        rows = []
-        for lib_name in ["gevent", "greenlet", "msgpack", "base58", "merkletools", "rsa", "socks", "pyasn1", "gevent_ws", "websocket", "maxminddb"]:
-            try:
-                module = importlib.import_module(lib_name)
-                if "__version__" in dir(module):
-                    version = module.__version__
-                elif "version" in dir(module):
-                    version = module.version
-                else:
-                    version = "unknown version"
-
-                if type(version) is tuple:
-                    version = ".".join(map(str, version))
-
-                rows.append(["- %s:" % lib_name, version, "at " + module.__file__])
-            except Exception as err:
-                rows.append(["! Error importing %s:", repr(err)])
-
-            """
-            try:
-                yield " - %s<br>" % html.escape(repr(pkg_resources.get_distribution(lib_name)))
-            except Exception as err:
-                yield " ! %s<br>" % html.escape(repr(err))
-            """
-
-        yield from self.formatTable(*rows, format=format)
-
-        yield self.formatHead("Library config:", format=format)
-
-        yield from self.formatTable(
-            ["- gevent:", gevent.config.loop.__module__],
-            ["- msgpack unpacker:", msgpack.Unpacker.__module__],
-            format=format
-        )
diff --git a/plugins/Stats/__init__.py b/plugins/Stats/__init__.py
deleted file mode 100644
index 791fb6e0..00000000
--- a/plugins/Stats/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import StatsPlugin
\ No newline at end of file
diff --git a/plugins/Stats/plugin_info.json b/plugins/Stats/plugin_info.json
deleted file mode 100644
index 1f401a4f..00000000
--- a/plugins/Stats/plugin_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"name": "Stats",
-	"description": "/Stats and /Benchmark pages.",
-	"default": "enabled"
-}
\ No newline at end of file
diff --git a/plugins/TranslateSite/TranslateSitePlugin.py b/plugins/TranslateSite/TranslateSitePlugin.py
deleted file mode 100644
index d82d0ebe..00000000
--- a/plugins/TranslateSite/TranslateSitePlugin.py
+++ /dev/null
@@ -1,80 +0,0 @@
-import time
-
-from Plugin import PluginManager
-from Translate import translate
-
-
-@PluginManager.registerTo("UiRequest")
-class UiRequestPlugin(object):
-    def actionSiteMedia(self, path, **kwargs):
-        file_name = path.split("/")[-1].lower()
-        if not file_name:  # Path ends with /
-            file_name = "index.html"
-        extension = file_name.split(".")[-1]
-
-        if extension == "html":  # Always replace translate variables in html files
-            should_translate = True
-        elif extension == "js" and translate.lang != "en":
-            should_translate = True
-        else:
-            should_translate = False
-
-        if should_translate:
-            path_parts = self.parsePath(path)
-            kwargs["header_length"] = False
-            file_generator = super(UiRequestPlugin, self).actionSiteMedia(path, **kwargs)
-            if "__next__" in dir(file_generator):  # File found and generator returned
-                site = self.server.sites.get(path_parts["address"])
-                if not site or not site.content_manager.contents.get("content.json"):
-                    return file_generator
-                return self.actionPatchFile(site, path_parts["inner_path"], file_generator)
-            else:
-                return file_generator
-
-        else:
-            return super(UiRequestPlugin, self).actionSiteMedia(path, **kwargs)
-
-    def actionUiMedia(self, path):
-        file_generator = super(UiRequestPlugin, self).actionUiMedia(path)
-        if translate.lang != "en" and path.endswith(".js"):
-            s = time.time()
-            data = b"".join(list(file_generator))
-            data = translate.translateData(data.decode("utf8"))
-            self.log.debug("Patched %s (%s bytes) in %.3fs" % (path, len(data), time.time() - s))
-            return iter([data.encode("utf8")])
-        else:
-            return file_generator
-
-    def actionPatchFile(self, site, inner_path, file_generator):
-        content_json = site.content_manager.contents.get("content.json")
-        lang_file = "languages/%s.json" % translate.lang
-        lang_file_exist = False
-        if site.settings.get("own"):  # My site, check if the file is exist (allow to add new lang without signing)
-            if site.storage.isFile(lang_file):
-                lang_file_exist = True
-        else:  # Not my site the reference in content.json is enough (will wait for download later)
-            if lang_file in content_json.get("files", {}):
-                lang_file_exist = True
-
-        if not lang_file_exist or inner_path not in content_json.get("translate", []):
-            for part in file_generator:
-                if inner_path.endswith(".html"):
-                    yield part.replace(b"lang={lang}", b"lang=" + translate.lang.encode("utf8"))  # lang get parameter to .js file to avoid cache
-                else:
-                    yield part
-        else:
-            s = time.time()
-            data = b"".join(list(file_generator)).decode("utf8")
-
-            # if site.content_manager.contents["content.json"]["files"].get(lang_file):
-            site.needFile(lang_file, priority=10)
-            try:
-                if inner_path.endswith("js"):
-                    data = translate.translateData(data, site.storage.loadJson(lang_file), "js")
-                else:
-                    data = translate.translateData(data, site.storage.loadJson(lang_file), "html")
-            except Exception as err:
-                site.log.error("Error loading translation file %s: %s" % (lang_file, err))
-
-            self.log.debug("Patched %s (%s bytes) in %.3fs" % (inner_path, len(data), time.time() - s))
-            yield data.encode("utf8")
diff --git a/plugins/TranslateSite/__init__.py b/plugins/TranslateSite/__init__.py
deleted file mode 100644
index 1ebbe31f..00000000
--- a/plugins/TranslateSite/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import TranslateSitePlugin
diff --git a/plugins/TranslateSite/plugin_info.json b/plugins/TranslateSite/plugin_info.json
deleted file mode 100644
index 1d520eda..00000000
--- a/plugins/TranslateSite/plugin_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"name": "TranslateSite",
-	"description": "Transparent support translation of site javascript and html files.",
-	"default": "enabled"
-}
\ No newline at end of file
diff --git a/plugins/Trayicon/TrayiconPlugin.py b/plugins/Trayicon/TrayiconPlugin.py
deleted file mode 100644
index 2dc3a5c6..00000000
--- a/plugins/Trayicon/TrayiconPlugin.py
+++ /dev/null
@@ -1,171 +0,0 @@
-import os
-import sys
-import atexit
-
-from Plugin import PluginManager
-from Config import config
-from Translate import Translate
-
-allow_reload = False  # No source reload supported in this plugin
-
-
-plugin_dir = os.path.dirname(__file__)
-
-if "_" not in locals():
-    _ = Translate(plugin_dir + "/languages/")
-
-
-@PluginManager.registerTo("Actions")
-class ActionsPlugin(object):
-
-    def main(self):
-        global notificationicon, winfolders
-        from .lib import notificationicon, winfolders
-        import gevent.threadpool
-        import main
-
-        self.main = main
-
-        icon = notificationicon.NotificationIcon(
-            os.path.join(os.path.dirname(os.path.abspath(__file__)), 'trayicon.ico'),
-            "ZeroNet %s" % config.version
-        )
-        self.icon = icon
-
-        self.console = False
-
-        @atexit.register
-        def hideIcon():
-            try:
-                icon.die()
-            except Exception as err:
-                print("Error removing trayicon: %s" % err)
-
-        ui_ip = config.ui_ip if config.ui_ip != "*" else "127.0.0.1"
-
-        if ":" in ui_ip:
-            ui_ip = "[" + ui_ip + "]"
-
-        icon.items = [
-            (self.titleIp, False),
-            (self.titleConnections, False),
-            (self.titleTransfer, False),
-            (self.titleConsole, self.toggleConsole),
-            (self.titleAutorun, self.toggleAutorun),
-            "--",
-            (_["ZeroNet Twitter"], lambda: self.opensite("https://twitter.com/HelloZeroNet")),
-            (_["ZeroNet Reddit"], lambda: self.opensite("http://www.reddit.com/r/zeronet/")),
-            (_["ZeroNet Github"], lambda: self.opensite("https://github.com/HelloZeroNet/ZeroNet")),
-            (_["Report bug/request feature"], lambda: self.opensite("https://github.com/HelloZeroNet/ZeroNet/issues")),
-            "--",
-            (_["!Open ZeroNet"], lambda: self.opensite("http://%s:%s/%s" % (ui_ip, config.ui_port, config.homepage))),
-            "--",
-            (_["Quit"], self.quit),
-        ]
-
-        if not notificationicon.hasConsole():
-            del icon.items[3]
-
-        icon.clicked = lambda: self.opensite("http://%s:%s/%s" % (ui_ip, config.ui_port, config.homepage))
-        self.quit_servers_event = gevent.threadpool.ThreadResult(
-            lambda res: gevent.spawn_later(0.1, self.quitServers), gevent.threadpool.get_hub(), lambda: True
-        )  # Fix gevent thread switch error
-        gevent.threadpool.start_new_thread(icon._run, ())  # Start in real thread (not gevent compatible)
-        super(ActionsPlugin, self).main()
-        icon._die = True
-
-    def quit(self):
-        self.icon.die()
-        self.quit_servers_event.set(True)
-
-    def quitServers(self):
-        self.main.ui_server.stop()
-        self.main.file_server.stop()
-
-    def opensite(self, url):
-        import webbrowser
-        webbrowser.open(url, new=0)
-
-    def titleIp(self):
-        title = "!IP: %s " % ", ".join(self.main.file_server.ip_external_list)
-        if any(self.main.file_server.port_opened):
-            title += _["(active)"]
-        else:
-            title += _["(passive)"]
-        return title
-
-    def titleConnections(self):
-        title = _["Connections: %s"] % len(self.main.file_server.connections)
-        return title
-
-    def titleTransfer(self):
-        title = _["Received: %.2f MB | Sent: %.2f MB"] % (
-            float(self.main.file_server.bytes_recv) / 1024 / 1024,
-            float(self.main.file_server.bytes_sent) / 1024 / 1024
-        )
-        return title
-
-    def titleConsole(self):
-        translate = _["Show console window"]
-        if self.console:
-            return "+" + translate
-        else:
-            return translate
-
-    def toggleConsole(self):
-        if self.console:
-            notificationicon.hideConsole()
-            self.console = False
-        else:
-            notificationicon.showConsole()
-            self.console = True
-
-    def getAutorunPath(self):
-        return "%s\\zeronet.cmd" % winfolders.get(winfolders.STARTUP)
-
-    def formatAutorun(self):
-        args = sys.argv[:]
-
-        if not getattr(sys, 'frozen', False):  # Not frozen
-            args.insert(0, sys.executable)
-            cwd = os.getcwd()
-        else:
-            cwd = os.path.dirname(sys.executable)
-
-        ignored_args = [
-            "--open_browser", "default_browser",
-            "--dist_type", "bundle_win64"
-        ]
-
-        if sys.platform == 'win32':
-            args = ['"%s"' % arg for arg in args if arg and arg not in ignored_args]
-        cmd = " ".join(args)
-
-        # Dont open browser on autorun
-        cmd = cmd.replace("start.py", "zeronet.py").strip()
-        cmd += ' --open_browser ""'
-
-        return "\r\n".join([
-            '@echo off',
-            'chcp 65001 > nul',
-            'set PYTHONIOENCODING=utf-8',
-            'cd /D \"%s\"' % cwd,
-            'start "" %s' % cmd
-        ])
-
-    def isAutorunEnabled(self):
-        path = self.getAutorunPath()
-        return os.path.isfile(path) and open(path, "rb").read().decode("utf8") == self.formatAutorun()
-
-    def titleAutorun(self):
-        translate = _["Start ZeroNet when Windows starts"]
-        if self.isAutorunEnabled():
-            return "+" + translate
-        else:
-            return translate
-
-    def toggleAutorun(self):
-        if self.isAutorunEnabled():
-            os.unlink(self.getAutorunPath())
-        else:
-            open(self.getAutorunPath(), "wb").write(self.formatAutorun().encode("utf8"))
diff --git a/plugins/Trayicon/__init__.py b/plugins/Trayicon/__init__.py
deleted file mode 100644
index 918f76dc..00000000
--- a/plugins/Trayicon/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import sys
-
-if sys.platform == 'win32':
-	from . import TrayiconPlugin
\ No newline at end of file
diff --git a/plugins/Trayicon/languages/es.json b/plugins/Trayicon/languages/es.json
deleted file mode 100644
index 6710c3c5..00000000
--- a/plugins/Trayicon/languages/es.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-	"ZeroNet Twitter": "ZeroNet Twitter",
-	"ZeroNet Reddit": "ZeroNet Reddit",
-	"ZeroNet Github": "ZeroNet Github",
-	"Report bug/request feature": "Reportar fallo/sugerir característica",
-	"!Open ZeroNet": "!Abrir ZeroNet",
-	"Quit": "Sair",
-	"(active)": "(activo)",
-	"(passive)": "(pasivo)",
-	"Connections: %s": "Conecciones: %s",
-	"Received: %.2f MB | Sent: %.2f MB": "Recibido: %.2f MB | Enviado: %.2f MB",
-	"Show console window": "Mostrar consola",
-	"Start ZeroNet when Windows starts": "Iniciar Zeronet cuando inicie Windows"
-}
diff --git a/plugins/Trayicon/languages/fr.json b/plugins/Trayicon/languages/fr.json
deleted file mode 100644
index a6fca769..00000000
--- a/plugins/Trayicon/languages/fr.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-	"ZeroNet Twitter": "ZeroNet Twitter",
-	"ZeroNet Reddit": "ZeroNet Reddit",
-	"ZeroNet Github": "ZeroNet Github",
-	"Report bug/request feature": "Rapport d'erreur/Demander une fonctionnalité",
-	"!Open ZeroNet": "!Ouvrir ZeroNet",
-	"Quit": "Quitter",
-	"(active)": "(actif)",
-	"(passive)": "(passif)",
-	"Connections: %s": "Connexions: %s",
-	"Received: %.2f MB | Sent: %.2f MB": "Reçu: %.2f MB | Envoyé: %.2f MB",
-	"Show console window": "Afficher la console",
-	"Start ZeroNet when Windows starts": "Lancer ZeroNet au démarrage de Windows"
-}
diff --git a/plugins/Trayicon/languages/hu.json b/plugins/Trayicon/languages/hu.json
deleted file mode 100644
index 56fef23a..00000000
--- a/plugins/Trayicon/languages/hu.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-	"ZeroNet Twitter": "ZeroNet Twitter",
-	"ZeroNet Reddit": "ZeroNet Reddit",
-	"ZeroNet Github": "ZeroNet Github",
-	"Report bug/request feature": "Hiba bejelentés/ötletek",
-	"!Open ZeroNet": "!ZeroNet megnyitása",
-	"Quit": "Kilépés",
-	"(active)": "(aktív)",
-	"(passive)": "(passive)",
-	"Connections: %s": "Kapcsolatok: %s",
-	"Received: %.2f MB | Sent: %.2f MB": "Fogadott: %.2f MB | Küldött: %.2f MB",
-	"Show console window": "Parancssor mutatása",
-	"Start ZeroNet when Windows starts": "ZeroNet indítása a Windows-al együtt"
-}
diff --git a/plugins/Trayicon/languages/it.json b/plugins/Trayicon/languages/it.json
deleted file mode 100644
index efbd6cfa..00000000
--- a/plugins/Trayicon/languages/it.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-	"ZeroNet Twitter": "ZeroNet Twitter",
-	"ZeroNet Reddit": "ZeroNet Reddit",
-	"ZeroNet Github": "ZeroNet Github",
-	"Report bug/request feature": "Segnala bug/richiesta di una funzione",
-	"!Open ZeroNet": "!Apri ZeroNet",
-	"Quit": "Chiudi",
-	"(active)": "(attivo)",
-	"(passive)": "(passivo)",
-	"Connections: %s": "Connessioni: %s",
-	"Received: %.2f MB | Sent: %.2f MB": "Ricevuto: %.2f MB | Inviato: %.2f MB",
-	"Show console window": "Mostra finestra console",
-	"Start ZeroNet when Windows starts": "Avvia ZeroNet all'avvio di Windows"
-}
diff --git a/plugins/Trayicon/languages/jp.json b/plugins/Trayicon/languages/jp.json
deleted file mode 100644
index aa28457b..00000000
--- a/plugins/Trayicon/languages/jp.json
+++ /dev/null
@@ -1,14 +0,0 @@
- {
-	"ZeroNet Twitter": "ZeroNet Twitter",
-	"ZeroNet Reddit": "ZeroNet Reddit",
-	"ZeroNet Github": "ZeroNet Github",
-	"Report bug/request feature": "バグ報告/要望",
-	"!Open ZeroNet": "!ZeroNetをブラウザで開く",
-	"Quit": "閉じる",
-	"(active)": "(アクティブ)",
-	"(passive)": "(パッシブ)",
-	"Connections: %s": "接続数: %s",
-	"Received: %.2f MB | Sent: %.2f MB": "受信: %.2f MB | 送信: %.2f MB",
-	"Show console window": "コンソールを表示",
-	"Start ZeroNet when Windows starts": "Windows起動時にZeroNetも起動する"
-}
diff --git a/plugins/Trayicon/languages/pl.json b/plugins/Trayicon/languages/pl.json
deleted file mode 100644
index 84c14796..00000000
--- a/plugins/Trayicon/languages/pl.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-	"ZeroNet Twitter": "ZeroNet Twitter",
-	"ZeroNet Reddit": "ZeroNet Reddit",
-	"ZeroNet Github": "ZeroNet Github",
-	"Report bug/request feature": "Zgłoś błąd / propozycję",
-	"!Open ZeroNet": "!Otwórz ZeroNet",
-	"Quit": "Zamknij",
-	"(active)": "(aktywny)",
-	"(passive)": "(pasywny)",
-	"Connections: %s": "Połączenia: %s",
-	"Received: %.2f MB | Sent: %.2f MB": "Odebrano: %.2f MB | Wysłano: %.2f MB",
-	"Show console window": "Pokaż okno konsoli",
-	"Start ZeroNet when Windows starts": "Uruchom ZeroNet podczas startu Windows"
-}
diff --git a/plugins/Trayicon/languages/pt-br.json b/plugins/Trayicon/languages/pt-br.json
deleted file mode 100644
index 5e86493a..00000000
--- a/plugins/Trayicon/languages/pt-br.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-	"ZeroNet Twitter": "ZeroNet Twitter",
-	"ZeroNet Reddit": "ZeroNet Reddit",
-	"ZeroNet Github": "ZeroNet Github",
-	"Report bug/request feature": "Reportar bug/sugerir recurso",
-	"!Open ZeroNet": "!Abrir ZeroNet",
-	"Quit": "Sair",
-	"(active)": "(ativo)",
-	"(passive)": "(passivo)",
-	"Connections: %s": "Conexões: %s",
-	"Received: %.2f MB | Sent: %.2f MB": "Recebido: %.2f MB | Enviado: %.2f MB",
-	"Show console window": "Mostrar console",
-	"Start ZeroNet when Windows starts": "Iniciar o ZeroNet quando o Windows for iniciado"
-}
diff --git a/plugins/Trayicon/languages/tr.json b/plugins/Trayicon/languages/tr.json
deleted file mode 100644
index 077b8ddd..00000000
--- a/plugins/Trayicon/languages/tr.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-	"ZeroNet Twitter": "ZeroNet Twitter",
-	"ZeroNet Reddit": "ZeroNet Reddit",
-	"ZeroNet Github": "ZeroNet Github",
-	"Report bug/request feature": "Hata bildir/geliştirme taleb et",
-	"!Open ZeroNet": "!ZeroNet'i Aç",
-	"Quit": "Kapat",
-	"(active)": "(aktif)",
-	"(passive)": "(pasif)",
-	"Connections: %s": "Bağlantı sayısı: %s",
-	"Received: %.2f MB | Sent: %.2f MB": "Gelen: %.2f MB | Gönderilen: %.2f MB",
-	"Show console window": "Konsolu aç",
-	"Start ZeroNet when Windows starts": "ZeroNet'i açılışta otomatik başlat"
-}
diff --git a/plugins/Trayicon/languages/zh-tw.json b/plugins/Trayicon/languages/zh-tw.json
deleted file mode 100644
index 2189033e..00000000
--- a/plugins/Trayicon/languages/zh-tw.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-	"ZeroNet Twitter": "ZeroNet Twitter",
-	"ZeroNet Reddit": "ZeroNet Reddit",
-	"ZeroNet Github": "ZeroNet Github",
-	"Report bug/request feature": "回饋问题/請求功能",
-	"!Open ZeroNet": "!開啟 ZeroNet",
-	"Quit": "退出",
-	"(active)": "(主動模式)",
-	"(passive)": "(被動模式)",
-	"Connections: %s": "連線數: %s",
-	"Received: %.2f MB | Sent: %.2f MB": "已收到: %.2f MB | 已傳送: %.2f MB",
-	"Show console window": "顯示控制臺窗體",
-	"Start ZeroNet when Windows starts": "在 Windows 啟動時執行 ZeroNet"
-}
diff --git a/plugins/Trayicon/languages/zh.json b/plugins/Trayicon/languages/zh.json
deleted file mode 100644
index 29b73305..00000000
--- a/plugins/Trayicon/languages/zh.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-	"ZeroNet Twitter": "ZeroNet Twitter",
-	"ZeroNet Reddit": "ZeroNet Reddit",
-	"ZeroNet Github": "ZeroNet Github",
-	"Report bug/request feature": "反馈问题/请求功能",
-	"!Open ZeroNet": "!打开 ZeroNet",
-	"Quit": "退出",
-	"(active)": "(主动模式)",
-	"(passive)": "(被动模式)",
-	"Connections: %s": "连接数: %s",
-	"Received: %.2f MB | Sent: %.2f MB": "已接收: %.2f MB | 已发送: %.2f MB",
-	"Show console window": "显示控制台窗口",
-	"Start ZeroNet when Windows starts": "在 Windows 启动时运行 ZeroNet"
-}
diff --git a/plugins/Trayicon/lib/__init__.py b/plugins/Trayicon/lib/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/plugins/Trayicon/lib/notificationicon.py b/plugins/Trayicon/lib/notificationicon.py
deleted file mode 100644
index 8b913f83..00000000
--- a/plugins/Trayicon/lib/notificationicon.py
+++ /dev/null
@@ -1,730 +0,0 @@
-# Pure ctypes windows taskbar notification icon
-# via https://gist.github.com/jasonbot/5759510
-# Modified for ZeroNet
-
-import ctypes
-import ctypes.wintypes
-import os
-import uuid
-import time
-import gevent
-import threading
-try:
-    from queue import Empty as queue_Empty  # Python 3
-except ImportError:
-    from Queue import Empty as queue_Empty  # Python 2
-
-__all__ = ['NotificationIcon']
-
-# Create popup menu
-
-CreatePopupMenu = ctypes.windll.user32.CreatePopupMenu
-CreatePopupMenu.restype = ctypes.wintypes.HMENU
-CreatePopupMenu.argtypes = []
-
-MF_BYCOMMAND    = 0x0
-MF_BYPOSITION   = 0x400
-
-MF_BITMAP       = 0x4
-MF_CHECKED      = 0x8
-MF_DISABLED     = 0x2
-MF_ENABLED      = 0x0
-MF_GRAYED       = 0x1
-MF_MENUBARBREAK = 0x20
-MF_MENUBREAK    = 0x40
-MF_OWNERDRAW    = 0x100
-MF_POPUP        = 0x10
-MF_SEPARATOR    = 0x800
-MF_STRING       = 0x0
-MF_UNCHECKED    = 0x0
-
-InsertMenu = ctypes.windll.user32.InsertMenuW
-InsertMenu.restype = ctypes.wintypes.BOOL
-InsertMenu.argtypes = [ctypes.wintypes.HMENU, ctypes.wintypes.UINT, ctypes.wintypes.UINT, ctypes.wintypes.UINT, ctypes.wintypes.LPCWSTR]
-
-AppendMenu = ctypes.windll.user32.AppendMenuW
-AppendMenu.restype = ctypes.wintypes.BOOL
-AppendMenu.argtypes = [ctypes.wintypes.HMENU, ctypes.wintypes.UINT, ctypes.wintypes.UINT, ctypes.wintypes.LPCWSTR]
-
-SetMenuDefaultItem = ctypes.windll.user32.SetMenuDefaultItem
-SetMenuDefaultItem.restype = ctypes.wintypes.BOOL
-SetMenuDefaultItem.argtypes = [ctypes.wintypes.HMENU, ctypes.wintypes.UINT, ctypes.wintypes.UINT]
-
-class POINT(ctypes.Structure):
-    _fields_ = [ ('x', ctypes.wintypes.LONG),
-                 ('y', ctypes.wintypes.LONG)]
-
-GetCursorPos = ctypes.windll.user32.GetCursorPos
-GetCursorPos.argtypes = [ctypes.POINTER(POINT)]
-
-SetForegroundWindow = ctypes.windll.user32.SetForegroundWindow
-SetForegroundWindow.argtypes = [ctypes.wintypes.HWND]
-
-TPM_LEFTALIGN       = 0x0
-TPM_CENTERALIGN     = 0x4
-TPM_RIGHTALIGN      = 0x8
-
-TPM_TOPALIGN        = 0x0
-TPM_VCENTERALIGN    = 0x10
-TPM_BOTTOMALIGN     = 0x20
-
-TPM_NONOTIFY        = 0x80
-TPM_RETURNCMD       = 0x100
-
-TPM_LEFTBUTTON      = 0x0
-TPM_RIGHTBUTTON     = 0x2
-
-TPM_HORNEGANIMATION = 0x800
-TPM_HORPOSANIMATION = 0x400
-TPM_NOANIMATION     = 0x4000
-TPM_VERNEGANIMATION = 0x2000
-TPM_VERPOSANIMATION = 0x1000
-
-TrackPopupMenu = ctypes.windll.user32.TrackPopupMenu
-TrackPopupMenu.restype = ctypes.wintypes.BOOL
-TrackPopupMenu.argtypes = [ctypes.wintypes.HMENU, ctypes.wintypes.UINT, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.wintypes.HWND, ctypes.c_void_p]
-
-PostMessage = ctypes.windll.user32.PostMessageW
-PostMessage.restype = ctypes.wintypes.BOOL
-PostMessage.argtypes = [ctypes.wintypes.HWND, ctypes.wintypes.UINT, ctypes.wintypes.WPARAM, ctypes.wintypes.LPARAM]
-
-DestroyMenu = ctypes.windll.user32.DestroyMenu
-DestroyMenu.restype = ctypes.wintypes.BOOL
-DestroyMenu.argtypes = [ctypes.wintypes.HMENU]
-
-# Create notification icon
-
-GUID = ctypes.c_ubyte * 16
-
-class TimeoutVersionUnion(ctypes.Union):
-    _fields_ = [('uTimeout', ctypes.wintypes.UINT),
-                ('uVersion', ctypes.wintypes.UINT),]
-
-NIS_HIDDEN     = 0x1
-NIS_SHAREDICON = 0x2
-
-class NOTIFYICONDATA(ctypes.Structure):
-    def __init__(self, *args, **kwargs):
-        super(NOTIFYICONDATA, self).__init__(*args, **kwargs)
-        self.cbSize = ctypes.sizeof(self)
-    _fields_ = [
-        ('cbSize', ctypes.wintypes.DWORD),
-        ('hWnd', ctypes.wintypes.HWND),
-        ('uID', ctypes.wintypes.UINT),
-        ('uFlags', ctypes.wintypes.UINT),
-        ('uCallbackMessage', ctypes.wintypes.UINT),
-        ('hIcon', ctypes.wintypes.HICON),
-        ('szTip', ctypes.wintypes.WCHAR * 64),
-        ('dwState', ctypes.wintypes.DWORD),
-        ('dwStateMask', ctypes.wintypes.DWORD),
-        ('szInfo', ctypes.wintypes.WCHAR * 256),
-        ('union', TimeoutVersionUnion),
-        ('szInfoTitle', ctypes.wintypes.WCHAR * 64),
-        ('dwInfoFlags', ctypes.wintypes.DWORD),
-        ('guidItem', GUID),
-        ('hBalloonIcon', ctypes.wintypes.HICON),
-    ]
-
-NIM_ADD = 0
-NIM_MODIFY = 1
-NIM_DELETE = 2
-NIM_SETFOCUS = 3
-NIM_SETVERSION = 4
-
-NIF_MESSAGE = 1
-NIF_ICON = 2
-NIF_TIP = 4
-NIF_STATE = 8
-NIF_INFO = 16
-NIF_GUID = 32
-NIF_REALTIME = 64
-NIF_SHOWTIP = 128
-
-NIIF_NONE = 0
-NIIF_INFO = 1
-NIIF_WARNING = 2
-NIIF_ERROR = 3
-NIIF_USER = 4
-
-NOTIFYICON_VERSION = 3
-NOTIFYICON_VERSION_4 = 4
-
-Shell_NotifyIcon = ctypes.windll.shell32.Shell_NotifyIconW
-Shell_NotifyIcon.restype = ctypes.wintypes.BOOL
-Shell_NotifyIcon.argtypes = [ctypes.wintypes.DWORD, ctypes.POINTER(NOTIFYICONDATA)]
-
-# Load icon/image
-
-IMAGE_BITMAP = 0
-IMAGE_ICON = 1
-IMAGE_CURSOR = 2
-
-LR_CREATEDIBSECTION = 0x00002000
-LR_DEFAULTCOLOR     = 0x00000000
-LR_DEFAULTSIZE      = 0x00000040
-LR_LOADFROMFILE     = 0x00000010
-LR_LOADMAP3DCOLORS  = 0x00001000
-LR_LOADTRANSPARENT  = 0x00000020
-LR_MONOCHROME       = 0x00000001
-LR_SHARED           = 0x00008000
-LR_VGACOLOR         = 0x00000080
-
-OIC_SAMPLE      = 32512
-OIC_HAND        = 32513
-OIC_QUES        = 32514
-OIC_BANG        = 32515
-OIC_NOTE        = 32516
-OIC_WINLOGO     = 32517
-OIC_WARNING     = OIC_BANG
-OIC_ERROR       = OIC_HAND
-OIC_INFORMATION = OIC_NOTE
-
-LoadImage = ctypes.windll.user32.LoadImageW
-LoadImage.restype = ctypes.wintypes.HANDLE
-LoadImage.argtypes = [ctypes.wintypes.HINSTANCE, ctypes.wintypes.LPCWSTR, ctypes.wintypes.UINT, ctypes.c_int, ctypes.c_int, ctypes.wintypes.UINT]
-
-# CreateWindow call
-
-WNDPROC = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.wintypes.HWND, ctypes.c_uint, ctypes.wintypes.WPARAM, ctypes.wintypes.LPARAM)
-DefWindowProc = ctypes.windll.user32.DefWindowProcW
-DefWindowProc.restype = ctypes.c_int
-DefWindowProc.argtypes = [ctypes.wintypes.HWND, ctypes.c_uint, ctypes.wintypes.WPARAM, ctypes.wintypes.LPARAM]
-
-WS_OVERLAPPED       = 0x00000000
-WS_POPUP            = 0x80000000
-WS_CHILD            = 0x40000000
-WS_MINIMIZE         = 0x20000000
-WS_VISIBLE          = 0x10000000
-WS_DISABLED         = 0x08000000
-WS_CLIPSIBLINGS     = 0x04000000
-WS_CLIPCHILDREN     = 0x02000000
-WS_MAXIMIZE         = 0x01000000
-WS_CAPTION          = 0x00C00000
-WS_BORDER           = 0x00800000
-WS_DLGFRAME         = 0x00400000
-WS_VSCROLL          = 0x00200000
-WS_HSCROLL          = 0x00100000
-WS_SYSMENU          = 0x00080000
-WS_THICKFRAME       = 0x00040000
-WS_GROUP            = 0x00020000
-WS_TABSTOP          = 0x00010000
-
-WS_MINIMIZEBOX      = 0x00020000
-WS_MAXIMIZEBOX      = 0x00010000
-
-WS_OVERLAPPEDWINDOW = (WS_OVERLAPPED     |
-                       WS_CAPTION        |
-                       WS_SYSMENU        |
-                       WS_THICKFRAME     |
-                       WS_MINIMIZEBOX    |
-                       WS_MAXIMIZEBOX)
-
-SM_XVIRTUALSCREEN      = 76
-SM_YVIRTUALSCREEN      = 77
-SM_CXVIRTUALSCREEN     = 78
-SM_CYVIRTUALSCREEN     = 79
-SM_CMONITORS           = 80
-SM_SAMEDISPLAYFORMAT   = 81
-
-WM_NULL                   = 0x0000
-WM_CREATE                 = 0x0001
-WM_DESTROY                = 0x0002
-WM_MOVE                   = 0x0003
-WM_SIZE                   = 0x0005
-WM_ACTIVATE               = 0x0006
-WM_SETFOCUS               = 0x0007
-WM_KILLFOCUS              = 0x0008
-WM_ENABLE                 = 0x000A
-WM_SETREDRAW              = 0x000B
-WM_SETTEXT                = 0x000C
-WM_GETTEXT                = 0x000D
-WM_GETTEXTLENGTH          = 0x000E
-WM_PAINT                  = 0x000F
-WM_CLOSE                  = 0x0010
-WM_QUERYENDSESSION        = 0x0011
-WM_QUIT                   = 0x0012
-WM_QUERYOPEN              = 0x0013
-WM_ERASEBKGND             = 0x0014
-WM_SYSCOLORCHANGE         = 0x0015
-WM_ENDSESSION             = 0x0016
-WM_SHOWWINDOW             = 0x0018
-WM_CTLCOLOR               = 0x0019
-WM_WININICHANGE           = 0x001A
-WM_SETTINGCHANGE          = 0x001A
-WM_DEVMODECHANGE          = 0x001B
-WM_ACTIVATEAPP            = 0x001C
-WM_FONTCHANGE             = 0x001D
-WM_TIMECHANGE             = 0x001E
-WM_CANCELMODE             = 0x001F
-WM_SETCURSOR              = 0x0020
-WM_MOUSEACTIVATE          = 0x0021
-WM_CHILDACTIVATE          = 0x0022
-WM_QUEUESYNC              = 0x0023
-WM_GETMINMAXINFO          = 0x0024
-WM_PAINTICON              = 0x0026
-WM_ICONERASEBKGND         = 0x0027
-WM_NEXTDLGCTL             = 0x0028
-WM_SPOOLERSTATUS          = 0x002A
-WM_DRAWITEM               = 0x002B
-WM_MEASUREITEM            = 0x002C
-WM_DELETEITEM             = 0x002D
-WM_VKEYTOITEM             = 0x002E
-WM_CHARTOITEM             = 0x002F
-WM_SETFONT                = 0x0030
-WM_GETFONT                = 0x0031
-WM_SETHOTKEY              = 0x0032
-WM_GETHOTKEY              = 0x0033
-WM_QUERYDRAGICON          = 0x0037
-WM_COMPAREITEM            = 0x0039
-WM_GETOBJECT              = 0x003D
-WM_COMPACTING             = 0x0041
-WM_COMMNOTIFY             = 0x0044
-WM_WINDOWPOSCHANGING      = 0x0046
-WM_WINDOWPOSCHANGED       = 0x0047
-WM_POWER                  = 0x0048
-WM_COPYDATA               = 0x004A
-WM_CANCELJOURNAL          = 0x004B
-WM_NOTIFY                 = 0x004E
-WM_INPUTLANGCHANGEREQUEST = 0x0050
-WM_INPUTLANGCHANGE        = 0x0051
-WM_TCARD                  = 0x0052
-WM_HELP                   = 0x0053
-WM_USERCHANGED            = 0x0054
-WM_NOTIFYFORMAT           = 0x0055
-WM_CONTEXTMENU            = 0x007B
-WM_STYLECHANGING          = 0x007C
-WM_STYLECHANGED           = 0x007D
-WM_DISPLAYCHANGE          = 0x007E
-WM_GETICON                = 0x007F
-WM_SETICON                = 0x0080
-WM_NCCREATE               = 0x0081
-WM_NCDESTROY              = 0x0082
-WM_NCCALCSIZE             = 0x0083
-WM_NCHITTEST              = 0x0084
-WM_NCPAINT                = 0x0085
-WM_NCACTIVATE             = 0x0086
-WM_GETDLGCODE             = 0x0087
-WM_SYNCPAINT              = 0x0088
-WM_NCMOUSEMOVE            = 0x00A0
-WM_NCLBUTTONDOWN          = 0x00A1
-WM_NCLBUTTONUP            = 0x00A2
-WM_NCLBUTTONDBLCLK        = 0x00A3
-WM_NCRBUTTONDOWN          = 0x00A4
-WM_NCRBUTTONUP            = 0x00A5
-WM_NCRBUTTONDBLCLK        = 0x00A6
-WM_NCMBUTTONDOWN          = 0x00A7
-WM_NCMBUTTONUP            = 0x00A8
-WM_NCMBUTTONDBLCLK        = 0x00A9
-WM_KEYDOWN                = 0x0100
-WM_KEYUP                  = 0x0101
-WM_CHAR                   = 0x0102
-WM_DEADCHAR               = 0x0103
-WM_SYSKEYDOWN             = 0x0104
-WM_SYSKEYUP               = 0x0105
-WM_SYSCHAR                = 0x0106
-WM_SYSDEADCHAR            = 0x0107
-WM_KEYLAST                = 0x0108
-WM_IME_STARTCOMPOSITION   = 0x010D
-WM_IME_ENDCOMPOSITION     = 0x010E
-WM_IME_COMPOSITION        = 0x010F
-WM_IME_KEYLAST            = 0x010F
-WM_INITDIALOG             = 0x0110
-WM_COMMAND                = 0x0111
-WM_SYSCOMMAND             = 0x0112
-WM_TIMER                  = 0x0113
-WM_HSCROLL                = 0x0114
-WM_VSCROLL                = 0x0115
-WM_INITMENU               = 0x0116
-WM_INITMENUPOPUP          = 0x0117
-WM_MENUSELECT             = 0x011F
-WM_MENUCHAR               = 0x0120
-WM_ENTERIDLE              = 0x0121
-WM_MENURBUTTONUP          = 0x0122
-WM_MENUDRAG               = 0x0123
-WM_MENUGETOBJECT          = 0x0124
-WM_UNINITMENUPOPUP        = 0x0125
-WM_MENUCOMMAND            = 0x0126
-WM_CTLCOLORMSGBOX         = 0x0132
-WM_CTLCOLOREDIT           = 0x0133
-WM_CTLCOLORLISTBOX        = 0x0134
-WM_CTLCOLORBTN            = 0x0135
-WM_CTLCOLORDLG            = 0x0136
-WM_CTLCOLORSCROLLBAR      = 0x0137
-WM_CTLCOLORSTATIC         = 0x0138
-WM_MOUSEMOVE              = 0x0200
-WM_LBUTTONDOWN            = 0x0201
-WM_LBUTTONUP              = 0x0202
-WM_LBUTTONDBLCLK          = 0x0203
-WM_RBUTTONDOWN            = 0x0204
-WM_RBUTTONUP              = 0x0205
-WM_RBUTTONDBLCLK          = 0x0206
-WM_MBUTTONDOWN            = 0x0207
-WM_MBUTTONUP              = 0x0208
-WM_MBUTTONDBLCLK          = 0x0209
-WM_MOUSEWHEEL             = 0x020A
-WM_PARENTNOTIFY           = 0x0210
-WM_ENTERMENULOOP          = 0x0211
-WM_EXITMENULOOP           = 0x0212
-WM_NEXTMENU               = 0x0213
-WM_SIZING                 = 0x0214
-WM_CAPTURECHANGED         = 0x0215
-WM_MOVING                 = 0x0216
-WM_DEVICECHANGE           = 0x0219
-WM_MDICREATE              = 0x0220
-WM_MDIDESTROY             = 0x0221
-WM_MDIACTIVATE            = 0x0222
-WM_MDIRESTORE             = 0x0223
-WM_MDINEXT                = 0x0224
-WM_MDIMAXIMIZE            = 0x0225
-WM_MDITILE                = 0x0226
-WM_MDICASCADE             = 0x0227
-WM_MDIICONARRANGE         = 0x0228
-WM_MDIGETACTIVE           = 0x0229
-WM_MDISETMENU             = 0x0230
-WM_ENTERSIZEMOVE          = 0x0231
-WM_EXITSIZEMOVE           = 0x0232
-WM_DROPFILES              = 0x0233
-WM_MDIREFRESHMENU         = 0x0234
-WM_IME_SETCONTEXT         = 0x0281
-WM_IME_NOTIFY             = 0x0282
-WM_IME_CONTROL            = 0x0283
-WM_IME_COMPOSITIONFULL    = 0x0284
-WM_IME_SELECT             = 0x0285
-WM_IME_CHAR               = 0x0286
-WM_IME_REQUEST            = 0x0288
-WM_IME_KEYDOWN            = 0x0290
-WM_IME_KEYUP              = 0x0291
-WM_MOUSEHOVER             = 0x02A1
-WM_MOUSELEAVE             = 0x02A3
-WM_CUT                    = 0x0300
-WM_COPY                   = 0x0301
-WM_PASTE                  = 0x0302
-WM_CLEAR                  = 0x0303
-WM_UNDO                   = 0x0304
-WM_RENDERFORMAT           = 0x0305
-WM_RENDERALLFORMATS       = 0x0306
-WM_DESTROYCLIPBOARD       = 0x0307
-WM_DRAWCLIPBOARD          = 0x0308
-WM_PAINTCLIPBOARD         = 0x0309
-WM_VSCROLLCLIPBOARD       = 0x030A
-WM_SIZECLIPBOARD          = 0x030B
-WM_ASKCBFORMATNAME        = 0x030C
-WM_CHANGECBCHAIN          = 0x030D
-WM_HSCROLLCLIPBOARD       = 0x030E
-WM_QUERYNEWPALETTE        = 0x030F
-WM_PALETTEISCHANGING      = 0x0310
-WM_PALETTECHANGED         = 0x0311
-WM_HOTKEY                 = 0x0312
-WM_PRINT                  = 0x0317
-WM_PRINTCLIENT            = 0x0318
-WM_HANDHELDFIRST          = 0x0358
-WM_HANDHELDLAST           = 0x035F
-WM_AFXFIRST               = 0x0360
-WM_AFXLAST                = 0x037F
-WM_PENWINFIRST            = 0x0380
-WM_PENWINLAST             = 0x038F
-WM_APP                    = 0x8000
-WM_USER                   = 0x0400
-WM_REFLECT                = WM_USER + 0x1c00
-
-class WNDCLASSEX(ctypes.Structure):
-    def __init__(self, *args, **kwargs):
-        super(WNDCLASSEX, self).__init__(*args, **kwargs)
-        self.cbSize = ctypes.sizeof(self)
-    _fields_ = [("cbSize", ctypes.c_uint),
-                ("style", ctypes.c_uint),
-                ("lpfnWndProc", WNDPROC),
-                ("cbClsExtra", ctypes.c_int),
-                ("cbWndExtra", ctypes.c_int),
-                ("hInstance", ctypes.wintypes.HANDLE),
-                ("hIcon", ctypes.wintypes.HANDLE),
-                ("hCursor", ctypes.wintypes.HANDLE),
-                ("hBrush", ctypes.wintypes.HANDLE),
-                ("lpszMenuName", ctypes.wintypes.LPCWSTR),
-                ("lpszClassName", ctypes.wintypes.LPCWSTR),
-                ("hIconSm", ctypes.wintypes.HANDLE)]
-
-ShowWindow = ctypes.windll.user32.ShowWindow
-ShowWindow.argtypes = [ctypes.wintypes.HWND, ctypes.c_int]
-
-def GenerateDummyWindow(callback, uid):
-    newclass = WNDCLASSEX()
-    newclass.lpfnWndProc = callback
-    newclass.lpszClassName = uid.replace("-", "")
-    ATOM = ctypes.windll.user32.RegisterClassExW(ctypes.byref(newclass))
-    hwnd = ctypes.windll.user32.CreateWindowExW(0, newclass.lpszClassName, None, WS_POPUP, 0, 0, 0, 0, 0, 0, 0, 0)
-    return hwnd
-
-# Message loop calls
-
-TIMERCALLBACK = ctypes.WINFUNCTYPE(None,
-                                   ctypes.wintypes.HWND,
-                                   ctypes.wintypes.UINT,
-                                   ctypes.POINTER(ctypes.wintypes.UINT),
-                                   ctypes.wintypes.DWORD)
-
-SetTimer = ctypes.windll.user32.SetTimer
-SetTimer.restype = ctypes.POINTER(ctypes.wintypes.UINT)
-SetTimer.argtypes = [ctypes.wintypes.HWND,
-                     ctypes.POINTER(ctypes.wintypes.UINT),
-                     ctypes.wintypes.UINT,
-                     TIMERCALLBACK]
-
-KillTimer = ctypes.windll.user32.KillTimer
-KillTimer.restype = ctypes.wintypes.BOOL
-KillTimer.argtypes = [ctypes.wintypes.HWND,
-                      ctypes.POINTER(ctypes.wintypes.UINT)]
-
-class MSG(ctypes.Structure):
-    _fields_ = [ ('HWND', ctypes.wintypes.HWND),
-                 ('message', ctypes.wintypes.UINT),
-                 ('wParam', ctypes.wintypes.WPARAM),
-                 ('lParam', ctypes.wintypes.LPARAM),
-                 ('time', ctypes.wintypes.DWORD),
-                 ('pt', POINT)]
-
-GetMessage = ctypes.windll.user32.GetMessageW
-GetMessage.restype = ctypes.wintypes.BOOL
-GetMessage.argtypes = [ctypes.POINTER(MSG), ctypes.wintypes.HWND, ctypes.wintypes.UINT, ctypes.wintypes.UINT]
-
-TranslateMessage = ctypes.windll.user32.TranslateMessage
-TranslateMessage.restype = ctypes.wintypes.ULONG
-TranslateMessage.argtypes = [ctypes.POINTER(MSG)]
-
-DispatchMessage = ctypes.windll.user32.DispatchMessageW
-DispatchMessage.restype = ctypes.wintypes.ULONG
-DispatchMessage.argtypes = [ctypes.POINTER(MSG)]
-
-def LoadIcon(iconfilename, small=False):
-        return LoadImage(0,
-                         str(iconfilename),
-                         IMAGE_ICON,
-                         16 if small else 0,
-                         16 if small else 0,
-                         LR_LOADFROMFILE)
-
-
-class NotificationIcon(object):
-    def __init__(self, iconfilename, tooltip=None):
-        assert os.path.isfile(str(iconfilename)), "{} doesn't exist".format(iconfilename)
-        self._iconfile = str(iconfilename)
-        self._hicon = LoadIcon(self._iconfile, True)
-        assert self._hicon, "Failed to load {}".format(iconfilename)
-        #self._pumpqueue = Queue.Queue()
-        self._die = False
-        self._timerid = None
-        self._uid = uuid.uuid4()
-        self._tooltip = str(tooltip) if tooltip else ''
-        #self._thread = threading.Thread(target=self._run)
-        #self._thread.start()
-        self._info_bubble = None
-        self.items = []
-
-
-    def _bubble(self, iconinfo):
-        if self._info_bubble:
-            info_bubble = self._info_bubble
-            self._info_bubble = None
-            message = str(self._info_bubble)
-            iconinfo.uFlags |= NIF_INFO
-            iconinfo.szInfo = message
-            iconinfo.szInfoTitle = message
-            iconinfo.dwInfoFlags = NIIF_INFO
-            iconinfo.union.uTimeout = 10000
-            Shell_NotifyIcon(NIM_MODIFY, ctypes.pointer(iconinfo))
-
-
-    def _run(self):
-        self.WM_TASKBARCREATED = ctypes.windll.user32.RegisterWindowMessageW('TaskbarCreated')
-
-        self._windowproc = WNDPROC(self._callback)
-        self._hwnd = GenerateDummyWindow(self._windowproc, str(self._uid))
-
-        iconinfo = NOTIFYICONDATA()
-        iconinfo.hWnd = self._hwnd
-        iconinfo.uID = 100
-        iconinfo.uFlags = NIF_ICON | NIF_SHOWTIP | NIF_MESSAGE | (NIF_TIP if self._tooltip else 0)
-        iconinfo.uCallbackMessage = WM_MENUCOMMAND
-        iconinfo.hIcon = self._hicon
-        iconinfo.szTip = self._tooltip
-
-        Shell_NotifyIcon(NIM_ADD, ctypes.pointer(iconinfo))
-
-        self.iconinfo = iconinfo
-
-        PostMessage(self._hwnd, WM_NULL, 0, 0)
-
-        message = MSG()
-        last_time = -1
-        ret = None
-        while not self._die:
-            try:
-                ret = GetMessage(ctypes.pointer(message), 0, 0, 0)
-                TranslateMessage(ctypes.pointer(message))
-                DispatchMessage(ctypes.pointer(message))
-            except Exception as err:
-                # print "NotificationIcon error", err, message
-                message = MSG()
-            time.sleep(0.125)
-        print("Icon thread stopped, removing icon (hicon: %s, hwnd: %s)..." % (self._hicon, self._hwnd))
-
-        Shell_NotifyIcon(NIM_DELETE, ctypes.cast(ctypes.pointer(iconinfo), ctypes.POINTER(NOTIFYICONDATA)))
-        ctypes.windll.user32.DestroyWindow(self._hwnd)
-        ctypes.windll.user32.DestroyIcon.argtypes = [ctypes.wintypes.HICON]
-        ctypes.windll.user32.DestroyIcon(self._hicon)
-
-
-    def _menu(self):
-        if not hasattr(self, 'items'):
-            return
-
-        menu = CreatePopupMenu()
-        func = None
-
-        try:
-            iidx = 1000
-            defaultitem = -1
-            item_map = {}
-            for fs in self.items:
-                iidx += 1
-                if isinstance(fs, str):
-                    if fs and not fs.strip('-_='):
-                        AppendMenu(menu, MF_SEPARATOR, iidx, fs)
-                    else:
-                        AppendMenu(menu, MF_STRING | MF_GRAYED, iidx, fs)
-                elif isinstance(fs, tuple):
-                    if callable(fs[0]):
-                        itemstring = fs[0]()
-                    else:
-                        itemstring = str(fs[0])
-                    flags = MF_STRING
-                    if itemstring.startswith("!"):
-                        itemstring = itemstring[1:]
-                        defaultitem = iidx
-                    if itemstring.startswith("+"):
-                        itemstring = itemstring[1:]
-                        flags = flags | MF_CHECKED
-                    itemcallable = fs[1]
-                    item_map[iidx] = itemcallable
-                    if itemcallable is False:
-                        flags = flags | MF_DISABLED
-                    elif not callable(itemcallable):
-                        flags = flags | MF_GRAYED
-                    AppendMenu(menu, flags, iidx, itemstring)
-
-            if defaultitem != -1:
-                SetMenuDefaultItem(menu, defaultitem, 0)
-
-            pos = POINT()
-            GetCursorPos(ctypes.pointer(pos))
-
-            PostMessage(self._hwnd, WM_NULL, 0, 0)
-
-            SetForegroundWindow(self._hwnd)
-
-            ti = TrackPopupMenu(menu, TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, pos.x, pos.y, 0, self._hwnd, None)
-
-            if ti in item_map:
-                func = item_map[ti]
-
-            PostMessage(self._hwnd, WM_NULL, 0, 0)
-        finally:
-            DestroyMenu(menu)
-        if func: func()
-
-
-    def clicked(self):
-        self._menu()
-
-
-
-    def _callback(self, hWnd, msg, wParam, lParam):
-        # Check if the main thread is still alive
-        if msg == WM_TIMER:
-            if not any(thread.getName() == 'MainThread' and thread.isAlive()
-                       for thread in threading.enumerate()):
-                self._die = True
-        elif msg == WM_MENUCOMMAND and lParam == WM_LBUTTONUP:
-            self.clicked()
-        elif msg == WM_MENUCOMMAND and lParam == WM_RBUTTONUP:
-            self._menu()
-        elif msg == self.WM_TASKBARCREATED: # Explorer restarted, add the icon again.
-            Shell_NotifyIcon(NIM_ADD, ctypes.pointer(self.iconinfo))
-        else:
-            return DefWindowProc(hWnd, msg, wParam, lParam)
-        return 1
-
-
-    def die(self):
-        self._die = True
-        PostMessage(self._hwnd, WM_NULL, 0, 0)
-        time.sleep(0.2)
-        try:
-            Shell_NotifyIcon(NIM_DELETE, self.iconinfo)
-        except Exception as err:
-            print("Icon remove error", err)
-        ctypes.windll.user32.DestroyWindow(self._hwnd)
-        ctypes.windll.user32.DestroyIcon(self._hicon)
-
-
-    def pump(self):
-        try:
-            while not self._pumpqueue.empty():
-                callable = self._pumpqueue.get(False)
-                callable()
-        except queue_Empty:
-            pass
-
-
-    def announce(self, text):
-        self._info_bubble = text
-
-
-def hideConsole():
-    ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 0)
-
-def showConsole():
-    ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 1)
-
-def hasConsole():
-    return ctypes.windll.kernel32.GetConsoleWindow() != 0
-
-if __name__ == "__main__":
-    import time
-
-    def greet():
-        ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 0)
-        print("Hello")
-
-    def quit():
-        ni._die = True
-
-    def announce():
-        ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 1)
-        ni.announce("Hello there")
-
-    def clicked():
-        ni.announce("Hello")
-
-    def dynamicTitle():
-        return "!The time is: %s" % time.time()
-
-    ni = NotificationIcon(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../trayicon.ico'), "ZeroNet 0.2.9")
-    ni.items = [
-        (dynamicTitle, False),
-        ('Hello', greet),
-        ('Title', False),
-        ('!Default', greet),
-        ('+Popup bubble', announce),
-        'Nothing',
-        '--',
-        ('Quit', quit)
-    ]
-    ni.clicked = clicked
-    import atexit
-
-    @atexit.register
-    def goodbye():
-        print("You are now leaving the Python sector.")
-
-    ni._run()
diff --git a/plugins/Trayicon/lib/winfolders.py b/plugins/Trayicon/lib/winfolders.py
deleted file mode 100644
index 75437c19..00000000
--- a/plugins/Trayicon/lib/winfolders.py
+++ /dev/null
@@ -1,54 +0,0 @@
-''' Get windows special folders without pythonwin
-    Example:
-            import specialfolders
-            start_programs = specialfolders.get(specialfolders.PROGRAMS)
-
-Code is public domain, do with it what you will.
-
-Luke Pinner - Environment.gov.au, 2010 February 10
-'''
-
-#Imports use _syntax to mask them from autocomplete IDE's
-import ctypes as _ctypes
-from ctypes import create_unicode_buffer as _cub
-from ctypes.wintypes import HWND as _HWND, HANDLE as _HANDLE,DWORD as _DWORD,LPCWSTR as _LPCWSTR,MAX_PATH as _MAX_PATH
-_SHGetFolderPath = _ctypes.windll.shell32.SHGetFolderPathW
-
-#public special folder constants
-DESKTOP=                             0
-PROGRAMS=                            2
-MYDOCUMENTS=                         5
-FAVORITES=                           6
-STARTUP=                             7
-RECENT=                              8
-SENDTO=                              9
-STARTMENU=                          11
-MYMUSIC=                            13
-MYVIDEOS=                           14
-NETHOOD=                            19
-FONTS=                              20
-TEMPLATES=                          21
-ALLUSERSSTARTMENU=                  22
-ALLUSERSPROGRAMS=                   23
-ALLUSERSSTARTUP=                    24
-ALLUSERSDESKTOP=                    25
-APPLICATIONDATA=                    26
-PRINTHOOD=                          27
-LOCALSETTINGSAPPLICATIONDATA=       28
-ALLUSERSFAVORITES=                  31
-LOCALSETTINGSTEMPORARYINTERNETFILES=32
-COOKIES=                            33
-LOCALSETTINGSHISTORY=               34
-ALLUSERSAPPLICATIONDATA=            35
-
-def get(intFolder):
-    _SHGetFolderPath.argtypes = [_HWND, _ctypes.c_int, _HANDLE, _DWORD, _LPCWSTR]
-    auPathBuffer = _cub(_MAX_PATH)
-    exit_code=_SHGetFolderPath(0, intFolder, 0, 0, auPathBuffer)
-    return auPathBuffer.value
-
-
-if __name__ == "__main__":
-	import os
-	print(get(STARTUP))
-	open(get(STARTUP)+"\\zeronet.cmd", "w").write("cd /D %s\r\nzeronet.py" % os.getcwd())
\ No newline at end of file
diff --git a/plugins/Trayicon/plugin_info.json b/plugins/Trayicon/plugin_info.json
deleted file mode 100644
index d74fa6c1..00000000
--- a/plugins/Trayicon/plugin_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"name": "Trayicon",
-	"description": "Icon for system tray. (Windows only)",
-	"default": "enabled"
-}
\ No newline at end of file
diff --git a/plugins/Trayicon/trayicon.ico b/plugins/Trayicon/trayicon.ico
deleted file mode 100644
index 086172254e3534b532ddc8a82c49c60bc6e036f2..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1150
zcmb7DziV1i6h3JS5!yl^kTt~+)5VgKq3i3~KOi6!iqN5UkU;HFDKwjOjEfCsaENq}
z7D|hwNU<g^RVRZwG`!%mT0vWXMAT=#k8<VZK_xx%`R=**`_4W0+<Qb8@LO6E{NIsh
zH$+xM<Su|I%>13lM+9qEv<2`OW)pA&%fMCbmc!wA7>Pt)G@H$QtycRM3Wc6S>;i7i
z3Bv5w0)c?HR4RR#UN*B>EXMtQ{}XVW0Q0k(E1S)}8;{55O4@F>OTAuCSN>=;`khLp
z_8?>~R%1L(@j9JOHJMDlb-Ue<yk4)TTrPi_OeV9q<O0?lF6ry_`Z2ZOYyh_bS1y-3
zQ1RhwfK?Ay?iq%0%pAcp2ZO<{N~Q8S7z{qenll!Qy`tWP!Pfx0ZQ;BpsNKg_t5sBc
zA>JR<Wj`K|zwY<@KOvt$sM*A`{D;F~D;y5*Boc{T<j_=cP{%V}*S|70wD<p|CqEjE
zzQp?KHVpUno_BzKN6ejfg0-!A`b;Jh!Mdv58paPAjmCGC3w#Z*)^C<SIeY`vYW0iH
z=X>n&c(zgdD9&D+qenP#abMJ{t9aCOyWLae^KR1VbeLQUY<3;KlW(+8C>-*vN{6qR
z&*u*<@AsNbYjxQp>+Bi4hwOj!k2MCz>{<5i-2R<TMa(xM=c^(=7DY}&BKi@=2CyM=
QwkTo%oZ}if<WfWJ1qU{xX#fBK

diff --git a/plugins/UiConfig/UiConfigPlugin.py b/plugins/UiConfig/UiConfigPlugin.py
deleted file mode 100644
index 81cfe992..00000000
--- a/plugins/UiConfig/UiConfigPlugin.py
+++ /dev/null
@@ -1,72 +0,0 @@
-import io
-import os
-
-from Plugin import PluginManager
-from Config import config
-from Translate import Translate
-from util.Flag import flag
-
-
-plugin_dir = os.path.dirname(__file__)
-
-if "_" not in locals():
-    _ = Translate(plugin_dir + "/languages/")
-
-
-@PluginManager.registerTo("UiRequest")
-class UiRequestPlugin(object):
-    def actionWrapper(self, path, extra_headers=None):
-        if path.strip("/") != "Config":
-            return super(UiRequestPlugin, self).actionWrapper(path, extra_headers)
-
-        if not extra_headers:
-            extra_headers = {}
-
-        script_nonce = self.getScriptNonce()
-
-        self.sendHeader(extra_headers=extra_headers, script_nonce=script_nonce)
-        site = self.server.site_manager.get(config.homepage)
-        return iter([super(UiRequestPlugin, self).renderWrapper(
-            site, path, "uimedia/plugins/uiconfig/config.html",
-            "Config", extra_headers, show_loadingscreen=False, script_nonce=script_nonce
-        )])
-
-    def actionUiMedia(self, path, *args, **kwargs):
-        if path.startswith("/uimedia/plugins/uiconfig/"):
-            file_path = path.replace("/uimedia/plugins/uiconfig/", plugin_dir + "/media/")
-            if config.debug and (file_path.endswith("all.js") or file_path.endswith("all.css")):
-                # If debugging merge *.css to all.css and *.js to all.js
-                from Debug import DebugMedia
-                DebugMedia.merge(file_path)
-
-            if file_path.endswith("js"):
-                data = _.translateData(open(file_path).read(), mode="js").encode("utf8")
-            elif file_path.endswith("html"):
-                data = _.translateData(open(file_path).read(), mode="html").encode("utf8")
-            else:
-                data = open(file_path, "rb").read()
-
-            return self.actionFile(file_path, file_obj=io.BytesIO(data), file_size=len(data))
-        else:
-            return super(UiRequestPlugin, self).actionUiMedia(path)
-
-
-@PluginManager.registerTo("UiWebsocket")
-class UiWebsocketPlugin(object):
-    @flag.admin
-    def actionConfigList(self, to):
-        back = {}
-        config_values = vars(config.arguments)
-        config_values.update(config.pending_changes)
-        for key, val in config_values.items():
-            if key not in config.keys_api_change_allowed:
-                continue
-            is_pending = key in config.pending_changes
-            if val is None and is_pending:
-                val = config.parser.get_default(key)
-            back[key] = {
-                "value": val,
-                "default": config.parser.get_default(key),
-                "pending": is_pending
-            }
-        return back
diff --git a/plugins/UiConfig/__init__.py b/plugins/UiConfig/__init__.py
deleted file mode 100644
index d4892e03..00000000
--- a/plugins/UiConfig/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import UiConfigPlugin
diff --git a/plugins/UiConfig/languages/hu.json b/plugins/UiConfig/languages/hu.json
deleted file mode 100644
index efaf2ea6..00000000
--- a/plugins/UiConfig/languages/hu.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
-	"ZeroNet config": "ZeroNet beállítások",
-	"Web Interface": "Web felület",
-	"Open web browser on ZeroNet startup": "Bögésző megnyitása a ZeroNet indulásakor",
-
-	"Network": "Hálózat",
-	"File server port": "FIle szerver port",
-	"Other peers will use this port to reach your served sites. (default: 15441)": "Más kliensek ezen a porton tudják elérni a kiszolgált oldalaidat (alapbeállítás: 15441)",
-
-	"Disable: Don't connect to peers on Tor network": "Kikapcsolás: Ne csatlakozzon a Tor hálózatra",
-	"Enable: Only use Tor for Tor network peers": "Bekapcsolás: Csak a Tor kliensekhez használja a Tor hálózatot",
-	"Always: Use Tor for every connections to hide your IP address (slower)": "Mindig: Minden kapcsolatot a Tor hálózaton keresztül hozza létre az IP cím elrejtéséhez (lassabb)",
-
-	"Disable": "Kikapcsolás",
-	"Enable": "Bekapcsolás",
-	"Always": "Mindig",
-
-	"Use Tor bridges": "Tor bridge-ek használata",
-	"Use obfuscated bridge relays to avoid network level Tor block (even slower)": "Tor elrejtő bridge-ek használata a hálózat szintű Tor tiltás megkerüléséhez (még lassabb)",
-
-	"Discover new peers using these adresses": "Új kapcsolat felfedezése ezen címek használatával",
-
-	"Trackers files": "Tracker file-ok",
-	"Load additional list of torrent trackers dynamically, from a file": "További trackerek felfedezése dinamikusan, ezen file használatával",
-	"Eg.: data/trackers.json": "Pl.: data/trackers.json",
-
-	"Proxy for tracker connections": "Proxy tracker kapcsolatohoz",
-
-	" configuration item value changed": " beállítás megváltoztatva",
-	"Save settings": "Beállítások mentése",
-	"Some changed settings requires restart": "A beállítások érvényesítéséhez a kliens újraindítása szükséges",
-	"Restart ZeroNet client": "ZeroNet kliens újraindítása"
-}
\ No newline at end of file
diff --git a/plugins/UiConfig/languages/jp.json b/plugins/UiConfig/languages/jp.json
deleted file mode 100644
index 08184b65..00000000
--- a/plugins/UiConfig/languages/jp.json
+++ /dev/null
@@ -1,62 +0,0 @@
-{
-	"ZeroNet config": "ZeroNetの設定",
-	"Web Interface": "WEBインターフェース",
-	"Open web browser on ZeroNet startup": "ZeroNet起動時に自動でブラウザーを開く",
-
-	"Network": "ネットワーク",
-	"Offline mode": "オフラインモード",
-	"Disable network communication.": "通信を無効化します",
-	"File server network": "ファイルサーバネットワーク",
-	"Accept incoming peers using IPv4 or IPv6 address. (default: dual)": "IPv4とIPv6からの受信を許可(既定: 両方)",
-	"Dual (IPv4 & IPv6)": "両方 (IPv4 & IPv6)",
-	"File server port": "ファイルサーバのポート",
-	"Other peers will use this port to reach your served sites. (default: randomize)": "他のピアはこのポートを使用してあなたが所持しているサイトにアクセスします (既定: ランダム)",
-	"File server external ip": "ファイルサーバの外部IP",
-	"Detect automatically": "自動検出",
-	"Your file server is accessible on these ips. (default: detect automatically)": "あなたのファイルサーバへはここで設定したIPでアクセスできます (既定: 自動検出)",
-
-	"Disable: Don't connect to peers on Tor network": "無効: Torネットワーク上のピアに接続しない",
-	"Enable: Only use Tor for Tor network peers": "有効: Torネットワーク上のピアに対してのみTorを使って接続する",
-	"Always: Use Tor for every connections to hide your IP address (slower)": "常時: 全ての接続にTorを使いIPを秘匿する(低速)",
-
-	"Disable": "無効",
-	"Enable": "有効",
-	"Always": "常時",
-
-	"Use Tor bridges": "Torブリッジを使用",
-	"Use obfuscated bridge relays to avoid network level Tor block (even slower)": "難読化されたブリッジリレーを使用してネットワークレベルのTorブロックを避ける(超低速)",
-
-	"Discover new peers using these adresses": "ここで設定したアドレスを用いてピアを発見します",
-
-	"Trackers files": "トラッカーファイル",
-	"Load additional list of torrent trackers dynamically, from a file": "ファイルからトレントラッカーの追加リストを動的に読み込みます",
-	"Eg.: data/trackers.json": "例: data/trackers.json",
-
-	"Proxy for tracker connections": "トラッカーへの接続に使うプロキシ",
-	"Custom": "カスタム",
-	"Custom socks proxy address for trackers": "トラッカーに接続するためのカスタムsocksプロキシのアドレス",
-
-	"Performance": "性能",
-	"Level of logging to file": "ログレベル",
-	"Everything": "全て",
-	"Only important messages": "重要なメッセージのみ",
-	"Only errors": "エラーのみ",
-	"Threads for async file system reads": "非同期ファイルシステムの読み込みに使うスレッド",
-	"Threads for async file system writes": "非同期ファイルシステムの書き込みに使うスレッド",
-	"Threads for cryptographic functions": "暗号機能に使うスレッド",
-	"Threads for database operations": "データベースの操作に使うスレッド",
-	"Sync read": "同期読み取り",
-	"Sync write": "同期書き込み",
-	"Sync execution": "同期実行",
-	"1 thread": "1スレッド",
-	"2 threads": "2スレッド",
-	"3 threads": "3スレッド",
-	"4 threads": "4スレッド",
-	"5 threads": "5スレッド",
-	"10 threads": "10スレッド",
-
-	" configuration item value changed": " の項目の値が変更されました",
-	"Save settings": "設定を保存",
-	"Some changed settings requires restart": "一部の変更の適用には再起動が必要です。",
-	"Restart ZeroNet client": "ZeroNetクライアントを再起動"
-}
diff --git a/plugins/UiConfig/languages/pl.json b/plugins/UiConfig/languages/pl.json
deleted file mode 100644
index daeba3d8..00000000
--- a/plugins/UiConfig/languages/pl.json
+++ /dev/null
@@ -1,62 +0,0 @@
-{
-	"ZeroNet config": "Konfiguracja ZeroNet",
-	"Web Interface": "Interfejs webowy",
-	"Open web browser on ZeroNet startup": "Otwórz przeglądarkę podczas uruchomienia ZeroNet",
-
-	"Network": "Sieć",
-	"Offline mode": "Tryb offline",
-	"Disable network communication.": "Wyłącz komunikacje sieciową",
-	"File server network": "Sieć serwera plików",
-	"Accept incoming peers using IPv4 or IPv6 address. (default: dual)": "Akceptuj połączenia przychodzące używając IPv4 i IPv6. (domyślnie: oba)",
-	"Dual (IPv4 & IPv6)": "Oba (IPv4 i IPv6)",
-	"File server port": "Port serwera plików",
-	"Other peers will use this port to reach your served sites. (default: 15441)": "Inni użytkownicy będą używać tego portu do połączenia się z Tobą. (domyślnie 15441)",
-	"File server external ip": "Zewnętrzny adres IP serwera plików",
-	"Detect automatically": "Wykryj automatycznie",
-	"Your file server is accessible on these ips. (default: detect automatically)": "Twój serwer plików będzie dostępny na tych adresach IP. (domyślnie: wykryj automatycznie)",
-
-	"Disable: Don't connect to peers on Tor network": "Wyłącz: Nie łącz się do użytkowników sieci Tor",
-	"Enable: Only use Tor for Tor network peers": "Włącz: Łącz się do użytkowników sieci Tor",
-	"Always: Use Tor for every connections to hide your IP address (slower)": "Zawsze Tor: Użyj Tor dla wszystkich połączeń w celu ukrycia Twojego adresu IP (wolne działanie)",
-
-	"Disable": "Wyłącz",
-	"Enable": "Włącz",
-	"Always": "Zawsze Tor",
-
-	"Use Tor bridges": "Użyj Tor bridges",
-	"Use obfuscated bridge relays to avoid network level Tor block (even slower)": "Użyj obfuskacji, aby uniknąć blokowania Tor na poziomie sieci (jeszcze wolniejsze działanie)",
-	"Trackers": "Trackery",
-	"Discover new peers using these adresses": "Wykryj użytkowników korzystając z tych adresów trackerów",
-
-	"Trackers files": "Pliki trackerów",
-	"Load additional list of torrent trackers dynamically, from a file": "Dynamicznie wczytaj dodatkową listę trackerów z pliku .json",
-	"Eg.: data/trackers.json": "Np.: data/trackers.json",
-
-	"Proxy for tracker connections": "Serwer proxy dla trackerów",
-	"Custom": "Własny",
-	"Custom socks proxy address for trackers": "Adres serwera proxy do łączenia z trackerami",
-	"Eg.: 127.0.0.1:1080": "Np.: 127.0.0.1:1080",
-	"Performance": "Wydajność",
-	"Level of logging to file": "Poziom logowania do pliku",
-	"Everything": "Wszystko",
-	"Only important messages": "Tylko ważne wiadomości",
-	"Only errors": "Tylko błędy",
-	"Threads for async file system reads": "Wątki asynchroniczne dla odczytu",
-	"Threads for async file system writes": "Wątki asynchroniczne dla zapisu",
-	"Threads for cryptographic functions": "Wątki dla funkcji kryptograficznych",
-	"Threads for database operations": "Wątki dla operacji na bazie danych",
-	"Sync read": "Synchroniczny odczyt",
-	"Sync write": "Synchroniczny zapis",
-	"Sync execution": "Synchroniczne wykonanie",
-	"1 thread": "1 wątek",
-	"2 threads": "2 wątki",
-	"3 threads": "3 wątki",
-	"4 threads": "4 wątki",
-	"5 threads": "5 wątków",
-	"10 threads": "10 wątków",
-
-	" configuration item value changed": " obiekt konfiguracji zmieniony",
-	"Save settings": "Zapisz ustawienia",
-	"Some changed settings requires restart": "Niektóre zmiany ustawień wymagają ponownego uruchomienia ZeroNet",
-	"Restart ZeroNet client": "Uruchom ponownie ZeroNet"
-}
diff --git a/plugins/UiConfig/languages/pt-br.json b/plugins/UiConfig/languages/pt-br.json
deleted file mode 100644
index 6235606e..00000000
--- a/plugins/UiConfig/languages/pt-br.json
+++ /dev/null
@@ -1,56 +0,0 @@
-{
-	"ZeroNet config": "Configurações da ZeroNet",
-	"Web Interface": "Interface Web",
-	"Open web browser on ZeroNet startup": "Abrir navegador da web quando a ZeroNet iniciar",
-
-	"Network": "Rede",
-	"File server port": "Porta de servidor de arquivos",
-	"Other peers will use this port to reach your served sites. (default: 15441)": "Outros peers usarão esta porta para alcançar seus sites servidos. (padrão: 15441)",
-
-	"Disable: Don't connect to peers on Tor network": "Desativar: Não conectar à peers na rede Tor",
-	"Enable: Only use Tor for Tor network peers": "Ativar: Somente usar Tor para os peers da rede Tor",
-	"Always: Use Tor for every connections to hide your IP address (slower)": "Sempre: Usar o Tor para todas as conexões, para esconder seu endereço IP (mais lento)",
-
-	"Disable": "Desativar",
-	"Enable": "Ativar",
-	"Always": "Sempre",
-
-	"Use Tor bridges": "Usar pontes do Tor",
-	"Use obfuscated bridge relays to avoid network level Tor block (even slower)": "Usar relays de ponte ofuscados para evitar o bloqueio de Tor de nível de rede (ainda mais lento)",
-
-	"Discover new peers using these adresses": "Descobrir novos peers usando estes endereços",
-
-	"Trackers files": "Arquivos de trackers",
-	"Load additional list of torrent trackers dynamically, from a file": "Carregar lista adicional de trackers de torrent dinamicamente, à partir de um arquivo",
-	"Eg.: data/trackers.json": "Exemplo: data/trackers.json",
-
-	"Proxy for tracker connections": "Proxy para conexões de tracker",
-
-	"configuration item value changed": " valor do item de configuração foi alterado",
-	"Save settings": "Salvar configurações",
-	"Some changed settings requires restart": "Algumas configurações alteradas requrem reinicialização",
-	"Restart ZeroNet client": "Reiniciar cliente da ZeroNet",
-
-	"Offline mode": "Modo offline",
-	"Disable network communication.": "Desativar a comunicação em rede.",
-	"File server network": "Rede do servidor de arquivos",
-	"Accept incoming peers using IPv4 or IPv6 address. (default: dual)": "Aceite pontos de entrada usando o endereço IPv4 ou IPv6. (padrão: dual)",
-	"File server external ip": "IP externo do servidor de arquivos",
-	"Performance": "Desempenho",
-	"Level of logging to file": "Nível de registro no arquivo",
-	"Everything": "Tudo",
-	"Only important messages": "Apenas mensagens importantes",
-	"Only errors": "Apenas erros",
-
-	"Threads for async file system reads": "Threads para leituras de sistema de arquivos assíncronas",
-	"Threads for async file system writes": "Threads para gravações do sistema de arquivos assíncrono",
-	"Threads for cryptographic functions": "Threads para funções criptográficas",
-	"Threads for database operations": "Threads para operações de banco de dados",
-	"Sync execution": "Execução de sincronização",
-	"Custom": "Personalizado",
-	"Custom socks proxy address for trackers": "Endereço de proxy de meias personalizadas para trackers",
-	"Your file server is accessible on these ips. (default: detect automatically)": "Seu servidor de arquivos está acessível nesses ips. (padrão: detectar automaticamente)",
-	"Detect automatically": "Detectar automaticamente",
-	" configuration item value changed": " valor do item de configuração alterado"
-
-}
diff --git a/plugins/UiConfig/languages/zh.json b/plugins/UiConfig/languages/zh.json
deleted file mode 100644
index 9240b249..00000000
--- a/plugins/UiConfig/languages/zh.json
+++ /dev/null
@@ -1,62 +0,0 @@
-{
-	"ZeroNet config": "ZeroNet配置",
-	"Web Interface": "网页界面",
-	"Open web browser on ZeroNet startup": "ZeroNet启动时,打开浏览器",
-
-	"Network": "网络",
-	"Offline mode": "离线模式",
-	"Disable network communication.": "关闭网络通信.",
-	"File server network": "文件服务器网络",
-	"Accept incoming peers using IPv4 or IPv6 address. (default: dual)": "使用IPv4或IPv6地址接受传入的节点请求. (默认:双重)",
-	"Dual (IPv4 & IPv6)": "双重 (IPv4与IPv6)",
-	"File server port": "文件服务器端口",
-	"Other peers will use this port to reach your served sites. (default: 15441)": "其他节点可通过该端口访问您服务的站点. (默认:15441)",
-	"File server external ip": "文件服务器外部IP",
-	"Detect automatically": "自动检测",
-	"Your file server is accessible on these ips. (default: detect automatically)": "其他节点可通过这些IP访问您的文件服务器. (默认:自动检测)",
-
-	"Disable: Don't connect to peers on Tor network": "关闭: 不连接洋葱网络中的节点",
-	"Enable: Only use Tor for Tor network peers": "开启: Tor仅用于连接洋葱网络中的节点",
-	"Always: Use Tor for every connections to hide your IP address (slower)": "总是: 将Tor用于每个网络连接,从而隐藏您的IP地址 (很慢)",
-
-	"Disable": "关闭",
-	"Enable": "开启",
-	"Always": "总是",
-
-	"Use Tor bridges": "使用Tor网桥",
-	"Use obfuscated bridge relays to avoid network level Tor block (even slower)": "使用混淆网桥中继,从而避免网络层Tor阻碍 (超级慢)",
-
-	"Discover new peers using these adresses": "使用这些地址发现新节点",
-
-	"Trackers files": "Trackers文件",
-	"Load additional list of torrent trackers dynamically, from a file": "从一个文件中,动态加载额外的种子Trackers列表",
-	"Eg.: data/trackers.json": "例如: data/trackers.json",
-
-	"Proxy for tracker connections": "Tracker连接代理",
-	"Custom": "自定义",
-	"Custom socks proxy address for trackers": "Tracker的自定义socks代理地址",
-
-	"Performance": "性能",
-	"Level of logging to file": "日志记录级别",
-	"Everything": "记录全部",
-	"Only important messages": "仅记录重要信息",
-	"Only errors": "仅记录错误",
-	"Threads for async file system reads": "文件系统异步读取线程",
-	"Threads for async file system writes": "文件系统异步写入线程",
-	"Threads for cryptographic functions": "密码函数线程",
-	"Threads for database operations": "数据库操作线程",
-	"Sync read": "同步读取",
-	"Sync write": "同步写入",
-	"Sync execution": "同步执行",
-	"1 thread": "1个线程",
-	"2 threads": "2个线程",
-	"3 threads": "3个线程",
-	"4 threads": "4个线程",
-	"5 threads": "5个线程",
-	"10 threads": "10个线程",
-
-	" configuration item value changed": " 个配置值已经改变",
-	"Save settings": "保存配置",
-	"Some changed settings requires restart": "一些配置已改变,需要重启才能生效",
-	"Restart ZeroNet client": "重启ZeroNet客户端"
-}
diff --git a/plugins/UiConfig/media/config.html b/plugins/UiConfig/media/config.html
deleted file mode 100644
index 752dccf0..00000000
--- a/plugins/UiConfig/media/config.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-
-<html>
-<head>
- <title>Settings - ZeroNet</title>
- <meta charset="utf-8" />
- <meta http-equiv="content-type" content="text/html; charset=utf-8" />
- <link rel="stylesheet" href="css/all.css?rev={rev}" />
-</head>
-
-
-<h1>ZeroNet config</h1>
-
-<div class="content" id="content"></div>
-<div class="bottom" id="bottom-save"></div>
-<div class="bottom" id="bottom-restart"></div>
-
-<script type="text/javascript" src="js/all.js?rev={rev}&lang={lang}"></script>
-</body>
-</html>
diff --git a/plugins/UiConfig/media/css/Config.css b/plugins/UiConfig/media/css/Config.css
deleted file mode 100644
index 2211758e..00000000
--- a/plugins/UiConfig/media/css/Config.css
+++ /dev/null
@@ -1,68 +0,0 @@
-body { background-color: #EDF2F5; font-family: Roboto, 'Segoe UI', Arial, 'Helvetica Neue'; margin: 0px; padding: 0px; backface-visibility: hidden; }
-h1, h2, h3, h4 { font-family: 'Roboto', Arial, sans-serif; font-weight: 200; font-size: 30px; margin: 0px; padding: 0px }
-h1 { background: linear-gradient(33deg,#af3bff,#0d99c9); color: white; padding: 16px 30px; }
-h2 { margin-top: 10px; }
-h3 { font-weight: normal }
-a { color: #9760F9 }
-a:hover { text-decoration: none }
-
-.link { background-color: transparent; outline: 5px solid transparent; transition: all 0.3s }
-.link:active { background-color: #EFEFEF; outline: 5px solid #EFEFEF; transition: none }
-
-.content { max-width: 800px; margin: auto; background-color: white; padding: 60px 20px; box-sizing: border-box; padding-bottom: 150px; }
-.section { margin: 0px 10%; }
-.config-items { font-size: 19px; margin-top: 25px; margin-bottom: 75px; }
-.config-item { transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1); position: relative; padding-bottom: 20px; padding-top: 10px; }
-.config-item.hidden { opacity: 0; height: 0px; padding: 0px; }
-.config-item .title { display: inline-block; line-height: 36px; }
-.config-item .title h3 { font-size: 20px; font-weight: lighter; margin-right: 100px; }
-.config-item .description { font-size: 14px; color: #666; line-height: 24px; }
-.config-item .value { display: inline-block;  white-space: nowrap; }
-.config-item .value-right { right: 0px; position: absolute; }
-.config-item .value-fullwidth { width: 100% }
-.config-item .marker {
-	font-weight: bold; text-decoration: none; font-size: 25px; position: absolute; padding: 2px 15px; line-height: 32px;
-	opacity: 0; pointer-events: none; transition: all 0.6s; transform: scale(2); color: #9760F9;
-}
-.config-item .marker.visible { opacity: 1; pointer-events: all; transform: scale(1); }
-.config-item .marker.changed { color: #2ecc71; }
-.config-item .marker.pending { color: #ffa200; }
-
-
-.input-text, .input-select { padding: 8px 18px; border: 1px solid #CCC; border-radius: 3px; font-size: 17px; box-sizing: border-box; }
-.input-text:focus, .input-select:focus { border: 1px solid #3396ff; outline: none; }
-.input-textarea {  overflow-x: auto; overflow-y: hidden; white-space: pre; line-height: 22px; }
-
-.input-select { width: initial; font-size: 14px; padding-right: 10px; padding-left: 10px; }
-
-.value-right .input-text { text-align: right; width: 100px; }
-.value-fullwidth .input-text { width: 100%; font-size: 14px; font-family: 'Segoe UI', Arial, 'Helvetica Neue'; }
-.value-fullwidth { margin-top: 10px; }
-
-/* Checkbox */
-.checkbox-skin { background-color: #CCC; width: 50px; height: 24px; border-radius: 15px; transition: all 0.3s ease-in-out; display: inline-block; }
-.checkbox-skin:before {
-	content: ""; position: relative; width: 20px; background-color: white; height: 20px; display: block; border-radius: 100%; margin-top: 2px; margin-left: 2px;
-	transition: all 0.5s cubic-bezier(0.785, 0.135, 0.15, 0.86);
-}
-.checkbox { font-size: 14px; font-weight: normal; display: inline-block; cursor: pointer; margin-top: 5px; }
-.checkbox .title { display: inline; line-height: 30px; vertical-align: 4px; margin-left: 11px }
-.checkbox.checked .checkbox-skin:before { margin-left: 27px; }
-.checkbox.checked .checkbox-skin { background-color: #2ECC71 }
-
-/* Bottom */
-
-.bottom {
-	width: 100%; text-align: center; background-color: #ffffffde; padding: 25px; bottom: -120px; -webkit-backdrop-filter: blur(5px); backdrop-filter: blur(5px);
-	transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1); position: fixed; backface-visibility: hidden; box-sizing: border-box;
-}
-.bottom-content { max-width: 750px; width: 100%; margin: 0px auto; }
-.bottom .button { float: right; }
-.bottom.visible { bottom: 0px; box-shadow: 0px 0px 35px #dcdcdc; }
-.bottom .title { padding: 10px 10px; color: #363636; float: left; text-transform: uppercase; letter-spacing: 1px; }
-.bottom .title:before { content: "•"; display: inline-block; color: #2ecc71; font-size: 31px; vertical-align: -7px; margin-right: 8px; line-height: 25px; }
-.bottom-restart .title:before { color: #ffa200; }
-
-.animate { transition: all 0.3s ease-out !important; }
-.animate-back { transition: all 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) !important; }
-.animate-inout { transition: all 0.6s cubic-bezier(0.77, 0, 0.175, 1) !important; }
diff --git a/plugins/UiConfig/media/css/all.css b/plugins/UiConfig/media/css/all.css
deleted file mode 100644
index 2b2991d0..00000000
--- a/plugins/UiConfig/media/css/all.css
+++ /dev/null
@@ -1,124 +0,0 @@
-
-/* ---- Config.css ---- */
-
-
-body { background-color: #EDF2F5; font-family: Roboto, 'Segoe UI', Arial, 'Helvetica Neue'; margin: 0px; padding: 0px; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; -o-backface-visibility: hidden; -ms-backface-visibility: hidden; backface-visibility: hidden ; }
-h1, h2, h3, h4 { font-family: 'Roboto', Arial, sans-serif; font-weight: 200; font-size: 30px; margin: 0px; padding: 0px }
-h1 { background: -webkit-linear-gradient(33deg,#af3bff,#0d99c9);background: -moz-linear-gradient(33deg,#af3bff,#0d99c9);background: -o-linear-gradient(33deg,#af3bff,#0d99c9);background: -ms-linear-gradient(33deg,#af3bff,#0d99c9);background: linear-gradient(33deg,#af3bff,#0d99c9); color: white; padding: 16px 30px; }
-h2 { margin-top: 10px; }
-h3 { font-weight: normal }
-a { color: #9760F9 }
-a:hover { text-decoration: none }
-
-.link { background-color: transparent; outline: 5px solid transparent; -webkit-transition: all 0.3s ; -moz-transition: all 0.3s ; -o-transition: all 0.3s ; -ms-transition: all 0.3s ; transition: all 0.3s  }
-.link:active { background-color: #EFEFEF; outline: 5px solid #EFEFEF; -webkit-transition: none ; -moz-transition: none ; -o-transition: none ; -ms-transition: none ; transition: none  }
-
-.content { max-width: 800px; margin: auto; background-color: white; padding: 60px 20px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ; padding-bottom: 150px; }
-.section { margin: 0px 10%; }
-.config-items { font-size: 19px; margin-top: 25px; margin-bottom: 75px; }
-.config-item { -webkit-transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1); -moz-transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1); -o-transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1); -ms-transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1); transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1) ; position: relative; padding-bottom: 20px; padding-top: 10px; }
-.config-item.hidden { opacity: 0; height: 0px; padding: 0px; }
-.config-item .title { display: inline-block; line-height: 36px; }
-.config-item .title h3 { font-size: 20px; font-weight: lighter; margin-right: 100px; }
-.config-item .description { font-size: 14px; color: #666; line-height: 24px; }
-.config-item .value { display: inline-block;  white-space: nowrap; }
-.config-item .value-right { right: 0px; position: absolute; }
-.config-item .value-fullwidth { width: 100% }
-.config-item .marker {
-	font-weight: bold; text-decoration: none; font-size: 25px; position: absolute; padding: 2px 15px; line-height: 32px;
-	opacity: 0; pointer-events: none; -webkit-transition: all 0.6s; -moz-transition: all 0.6s; -o-transition: all 0.6s; -ms-transition: all 0.6s; transition: all 0.6s ; -webkit-transform: scale(2); -moz-transform: scale(2); -o-transform: scale(2); -ms-transform: scale(2); transform: scale(2) ; color: #9760F9;
-}
-.config-item .marker.visible { opacity: 1; pointer-events: all; -webkit-transform: scale(1); -moz-transform: scale(1); -o-transform: scale(1); -ms-transform: scale(1); transform: scale(1) ; }
-.config-item .marker.changed { color: #2ecc71; }
-.config-item .marker.pending { color: #ffa200; }
-
-
-.input-text, .input-select { padding: 8px 18px; border: 1px solid #CCC; -webkit-border-radius: 3px; -moz-border-radius: 3px; -o-border-radius: 3px; -ms-border-radius: 3px; border-radius: 3px ; font-size: 17px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ; }
-.input-text:focus, .input-select:focus { border: 1px solid #3396ff; outline: none; }
-.input-textarea {  overflow-x: auto; overflow-y: hidden; white-space: pre; line-height: 22px; }
-
-.input-select { width: initial; font-size: 14px; padding-right: 10px; padding-left: 10px; }
-
-.value-right .input-text { text-align: right; width: 100px; }
-.value-fullwidth .input-text { width: 100%; font-size: 14px; font-family: 'Segoe UI', Arial, 'Helvetica Neue'; }
-.value-fullwidth { margin-top: 10px; }
-
-/* Checkbox */
-.checkbox-skin { background-color: #CCC; width: 50px; height: 24px; -webkit-border-radius: 15px; -moz-border-radius: 15px; -o-border-radius: 15px; -ms-border-radius: 15px; border-radius: 15px ; -webkit-transition: all 0.3s ease-in-out; -moz-transition: all 0.3s ease-in-out; -o-transition: all 0.3s ease-in-out; -ms-transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out ; display: inline-block; }
-.checkbox-skin:before {
-	content: ""; position: relative; width: 20px; background-color: white; height: 20px; display: block; -webkit-border-radius: 100%; -moz-border-radius: 100%; -o-border-radius: 100%; -ms-border-radius: 100%; border-radius: 100% ; margin-top: 2px; margin-left: 2px;
-	-webkit-transition: all 0.5s cubic-bezier(0.785, 0.135, 0.15, 0.86); -moz-transition: all 0.5s cubic-bezier(0.785, 0.135, 0.15, 0.86); -o-transition: all 0.5s cubic-bezier(0.785, 0.135, 0.15, 0.86); -ms-transition: all 0.5s cubic-bezier(0.785, 0.135, 0.15, 0.86); transition: all 0.5s cubic-bezier(0.785, 0.135, 0.15, 0.86) ;
-}
-.checkbox { font-size: 14px; font-weight: normal; display: inline-block; cursor: pointer; margin-top: 5px; }
-.checkbox .title { display: inline; line-height: 30px; vertical-align: 4px; margin-left: 11px }
-.checkbox.checked .checkbox-skin:before { margin-left: 27px; }
-.checkbox.checked .checkbox-skin { background-color: #2ECC71 }
-
-/* Bottom */
-
-.bottom {
-	width: 100%; text-align: center; background-color: #ffffffde; padding: 25px; bottom: -120px; -webkit-backdrop-filter: blur(5px); backdrop-filter: blur(5px);
-	-webkit-transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1); -moz-transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1); -o-transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1); -ms-transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1); transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1) ; position: fixed; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; -o-backface-visibility: hidden; -ms-backface-visibility: hidden; backface-visibility: hidden ; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ;
-}
-.bottom-content { max-width: 750px; width: 100%; margin: 0px auto; }
-.bottom .button { float: right; }
-.bottom.visible { bottom: 0px; -webkit-box-shadow: 0px 0px 35px #dcdcdc; -moz-box-shadow: 0px 0px 35px #dcdcdc; -o-box-shadow: 0px 0px 35px #dcdcdc; -ms-box-shadow: 0px 0px 35px #dcdcdc; box-shadow: 0px 0px 35px #dcdcdc ; }
-.bottom .title { padding: 10px 10px; color: #363636; float: left; text-transform: uppercase; letter-spacing: 1px; }
-.bottom .title:before { content: "•"; display: inline-block; color: #2ecc71; font-size: 31px; vertical-align: -7px; margin-right: 8px; line-height: 25px; }
-.bottom-restart .title:before { color: #ffa200; }
-
-.animate { -webkit-transition: all 0.3s ease-out !important; -moz-transition: all 0.3s ease-out !important; -o-transition: all 0.3s ease-out !important; -ms-transition: all 0.3s ease-out !important; transition: all 0.3s ease-out !important ; }
-.animate-back { -webkit-transition: all 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) !important; -moz-transition: all 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) !important; -o-transition: all 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) !important; -ms-transition: all 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) !important; transition: all 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) !important ; }
-.animate-inout { -webkit-transition: all 0.6s cubic-bezier(0.77, 0, 0.175, 1) !important; -moz-transition: all 0.6s cubic-bezier(0.77, 0, 0.175, 1) !important; -o-transition: all 0.6s cubic-bezier(0.77, 0, 0.175, 1) !important; -ms-transition: all 0.6s cubic-bezier(0.77, 0, 0.175, 1) !important; transition: all 0.6s cubic-bezier(0.77, 0, 0.175, 1) !important ; }
-
-
-/* ---- button.css ---- */
-
-
-/* Button */
-.button {
-	background-color: #FFDC00; color: black; padding: 10px 20px; display: inline-block; background-position: left center;
-	-webkit-border-radius: 2px; -moz-border-radius: 2px; -o-border-radius: 2px; -ms-border-radius: 2px; border-radius: 2px ; border-bottom: 2px solid #E8BE29; -webkit-transition: all 0.5s ease-out; -moz-transition: all 0.5s ease-out; -o-transition: all 0.5s ease-out; -ms-transition: all 0.5s ease-out; transition: all 0.5s ease-out ; text-decoration: none;
-}
-.button:hover { border-color: white; border-bottom: 2px solid #BD960C; -webkit-transition: none ; -moz-transition: none ; -o-transition: none ; -ms-transition: none ; transition: none  ; background-color: #FDEB07 }
-.button:active { position: relative; top: 1px }
-.button.loading {
-	color: rgba(0,0,0,0); background: #999 url(../img/loading.gif) no-repeat center center;
-	-webkit-transition: all 0.5s ease-out  ; -moz-transition: all 0.5s ease-out  ; -o-transition: all 0.5s ease-out  ; -ms-transition: all 0.5s ease-out  ; transition: all 0.5s ease-out   ; pointer-events: none; border-bottom: 2px solid #666
-}
-.button.disabled { color: #DDD; background-color: #999;  pointer-events: none; border-bottom: 2px solid #666 }
-
-
-/* ---- fonts.css ---- */
-
-
-/* Base64 encoder: http://www.motobit.com/util/base64-decoder-encoder.asp */
-/* Generated by Font Squirrel (http://www.fontsquirrel.com) on January 21, 2015 */
-
-
-@font-face {
-    font-family: 'Roboto';
-    font-style: normal;
-    font-weight: 400;
-    src:
-        local('Roboto'),
-        url(data:application/x-font-woff;charset=utf-8;base64,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) format('woff');
-}
-
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: bold;
-  src:
-  	local('Roboto Medium'),
-  	url(data:application/x-font-woff;charset=utf-8;base64,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) format('woff'),
-}
-
-@font-face {
-    font-family: 'Roboto';
-    font-style: normal;
-    font-weight: 200;
-    src:
-        local('Roboto Light'),
-        url(data:application/x-font-woff;charset=utf-8;base64,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) format('woff');
-}
\ No newline at end of file
diff --git a/plugins/UiConfig/media/css/button.css b/plugins/UiConfig/media/css/button.css
deleted file mode 100644
index f69021bf..00000000
--- a/plugins/UiConfig/media/css/button.css
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Button */
-.button {
-	background-color: #FFDC00; color: black; padding: 10px 20px; display: inline-block; background-position: left center;
-	border-radius: 2px; border-bottom: 2px solid #E8BE29; transition: all 0.5s ease-out; text-decoration: none;
-}
-.button:hover { border-color: white; border-bottom: 2px solid #BD960C; transition: none ; background-color: #FDEB07 }
-.button:active { position: relative; top: 1px }
-.button.loading {
-	color: rgba(0,0,0,0); background: #999 url(../img/loading.gif) no-repeat center center;
-	transition: all 0.5s ease-out  ; pointer-events: none; border-bottom: 2px solid #666
-}
-.button.disabled { color: #DDD; background-color: #999;  pointer-events: none; border-bottom: 2px solid #666 }
diff --git a/plugins/UiConfig/media/css/fonts.css b/plugins/UiConfig/media/css/fonts.css
deleted file mode 100644
index f5576c5a..00000000
--- a/plugins/UiConfig/media/css/fonts.css
+++ /dev/null
@@ -1,30 +0,0 @@
-/* Base64 encoder: http://www.motobit.com/util/base64-decoder-encoder.asp */
-/* Generated by Font Squirrel (http://www.fontsquirrel.com) on January 21, 2015 */
-
-
-@font-face {
-    font-family: 'Roboto';
-    font-style: normal;
-    font-weight: 400;
-    src:
-        local('Roboto'),
-        url(data:application/x-font-woff;charset=utf-8;base64,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) format('woff');
-}
-
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: bold;
-  src:
-  	local('Roboto Medium'),
-  	url(data:application/x-font-woff;charset=utf-8;base64,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) format('woff'),
-}
-
-@font-face {
-    font-family: 'Roboto';
-    font-style: normal;
-    font-weight: 200;
-    src:
-        local('Roboto Light'),
-        url(data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAEScABMAAAAAdFQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABqAAAABwAAAAcXzC5yUdERUYAAAHEAAAAHgAAACAAzgAER1BPUwAAAeQAAAVxAAANIkezYOlHU1VCAAAHWAAAACwAAAAwuP+4/k9TLzIAAAeEAAAAVgAAAGC3ouDrY21hcAAAB9wAAAG+AAACioYHy/VjdnQgAAAJnAAAADQAAAA0CnAOGGZwZ20AAAnQAAABsQAAAmVTtC+nZ2FzcAAAC4QAAAAIAAAACAAAABBnbHlmAAALjAAAMaIAAFTUMXgLR2hlYWQAAD0wAAAAMQAAADYBsFYkaGhlYQAAPWQAAAAfAAAAJA7cBhlobXR4AAA9hAAAAeEAAAKEbjk+b2xvY2EAAD9oAAABNgAAAUQwY0cibWF4cAAAQKAAAAAgAAAAIAG+AZluYW1lAABAwAAAAZAAAANoT6qDDHBvc3QAAEJQAAABjAAAAktoPRGfcHJlcAAAQ9wAAAC2AAABI0qzIoZ3ZWJmAABElAAAAAYAAAAGVU1R3QAAAAEAAAAAzD2izwAAAADE8BEuAAAAAM4DBct42mNgZGBg4ANiCQYQYGJgBMIFQMwC5jEAAAsqANMAAHjapZZ5bNRFFMff79dtd7u03UNsORWwKYhWGwFLsRBiGuSKkdIDsBg0kRCVGq6GcpSEFINKghzlMDFBVBITNRpDJEGCBlBBRSEQIQYJyLHd/pA78a99fn6zy3ZbykJxXr7zm3nz5s2b7xy/EUtE/FIiY8SuGDe5SvLeeHlhvfQRD3pRFbc9tWy9/ur8evG5JQOP2Hxt8ds7xLJrjO1AmYxUyiyZLQtlpayRmOWx/FbQGmSVWM9aVdZs6z1rk/WZFbU9dtgutIeCsVivND1dsWSG9JAMKZOeMkrCUi756MI6AN0g3Se1ellm6GlqOXpBxuoNmYXGlgn6D/qo9JOA5ksIFOoBKY79K6V4qtC/ZJy2yXNgPJgIKkEVqMbPNHpO14jUgXr6LcK+gbbFoBEsoX0pWE55Bd8W/G8BW9WNboZ+b/KPyWslDy5K9biU6TkZpY6U6ymiLdUv0Vyi9jvt1boT+x9lTmyXzNUhaHKIcqyEaDkLfw8YTQBNDpo2NHmsVjZtrl2u/kZLmDlHaT0BJ1HTZ45+gbdfTSznJVOK4WQkWAAWgiYQQB/EVzAxYhheIvASgZcIvETgJGK8NfDdgN1GsAlsBllYO1g7WDtYO1g7WDrMcAK+a2UA6xci+kp0i0EjWA4s2nMZO6DNrE4zDDbDYDMMNptIHSJ1iNQhUodI3R4DafGzG8JSKEUyRB6VJ+RJGSbDZQSrWsb+KJfR7OAJ8rxUM/Z0xq6Tl6Re3iTyjUS9WezsQ+7e9L7j24G//uznFl2th/WAOrqPNelG0hq5z6Srk6Ub4Kau0Mv6qe7W7ZQPsxIhPcgeX3sPns6DCDjYSX/9rj3/7ka8bbeNGQXHE/UzyZb3Naqtt/W+FAepZ1J3mVOWPoW7ipYzFE8hSiE3Erfcabyo/I+kF7TVzPBMiq6VU3Wr/FGy9F2y1MD5aLfeG7ukh3SKztOQHtOldxmvgTW/3uWKBeLrqifdSuxbPeNypiOTPb/StfqBbgBrYCOIKkifoH6ou3S//oxFky4jLzLWvTSoV/RrU96pR/UY36Mdx9VzerNDbA+b/M8UzXE97TKTYCcvdY079Fxl8v2duY3vJb3Y3lvbjK+QWdMjScujKb226ze6V0+AH9gHId3G3ghxPk5yZs+m2BVzo4j+otuYZ3wX5ibGa4uP3R5tYufcaU32pGm7er+ninU2ffVaVz47Mt+tHXstTVvae0Cv3PeYTjqG4n5v927ukWDyTnDucuZXdXEerpqzcsc10D9M3nKnmNPFnZ6n7nOlY/RxrdBhYDA7yovKyx/Mq5N0vr6l67EIaA4ne4k5369QP6Kvpd4r8RRjZ+hP4PPkPrp4i832qOJ/AP1E1+ke7uE9nPDWJJ+Jrx4Cu92zEZtr6m93h6H2O7CDtjENA6eSpZOdzwL/84C8m3g93kuyeVN44C/L1LyIT7J5D3gNqz0SVjloc7lZuAc7/RfC3NHu/+dBU8tP6vORAnN/90poeoM+5H3vIaYsM3omo/oYwfVdgLgpk6+vWxvGSuQWfkuMV4v5+Q1TAaIMIr2ZVYhyIWLzCipijKGIT4qRPvIU4uNFNJz8aaQvL6NSeBqJ+HkjlcHUKCRHnkEKeDGVw9dopJdUIBkyTsbD80TEIy/IFKKoRLJkKpIpVYhHahCvTEPyeGVNJ7oXkX68tuooz0SCvLrqiXCezCeSBbz//bIIyZAGxCOLpRGfS2QpHpYhPlmOZEkT4pcVSJ6sk/XM1325WdKC5JsXnCVbZCtlG75djiSFI9uwkwE37hv6Md6G2cx+NJYVzKs3MxtPlJOQ/sxtqjzEO7FaBpk5PMIMZtKznvgGm/hKiKsJPjcw3oj/AIgWgIQAAAB42mNgZGBg4GLQYdBjYHJx8wlh4MtJLMljkGBgAYoz/P8PJBAsIAAAnsoHa3jaY2BmvsGow8DKwMI6i9WYgYFRHkIzX2RIY2JgYABhCHjAwPQ/gEEhGshUAPHd8/PTgRTvAwa2tH9pDAwcSUzBCgyM8/0ZGRhYrFg3gNUxAQCExA4aAAB42mNgYGBmgGAZBkYgycDYAuQxgvksjBlAOozBgYGVQQzI4mWoY1jAsJhhKcNKhtUM6xi2MOxg2M1wkOEkw1mGywzXGG4x3GF4yPCS4S3DZ4ZvDL8Y/jAGMhYyHWO6xXRHgUtBREFKQU5BTUFfwUohXmGNotIDhv//QTYCzVUAmrsIaO4KoLlriTA3gLEAai6DgoCChIIM2FxLJHMZ/3/9//j/of8H/x/4v+//3v97/m//v+X/pv9r/y/7v/j/vP9z/s/8P+P/lP+9/7v+t/5v/t/wv/6/zn++v7v+Lv+77EHzg7oH1Q+qHhQ/yH6Q9MDu/qf7tQoLIOFDC8DIxgA3nJEJSDChKwBGEQsrGzsHJxc3Dy8fv4CgkLCIqJi4hKSUtIysnLyCopKyiqqauoamlraOrp6+gaGRsYmpmbmFpZW1ja2dvYOjk7OLq5u7h6eXt4+vn39AYFBwSGhYeERkVHRMbFx8QiLIlnyGopJSiIVlQFwOYlQwMFQyVDEwVDMwJKeABLLS52enQZ2ViumVjNyZSWDGxEnTpk+eAmbOmz0HRE2dASTyGBgKgFQhEBcDcUMTkGjMARIAqVuf0QAAAAAEOgWvAGYAqABiAGUAZwBoAGkAagBrAHUApABcAHgAZQBsAHIAeAB8AHAAegBaAEQFEXjaXVG7TltBEN0NDwOBxNggOdoUs5mQxnuhBQnE1Y1iZDuF5QhpN3KRi3EBH0CBRA3arxmgoaRImwYhF0h8Qj4hEjNriKI0Ozuzc86ZM0vKkap36WvPU+ckkMLdBs02/U5ItbMA96Tr642MtIMHWmxm9Mp1+/4LBpvRlDtqAOU9bykPGU07gVq0p/7R/AqG+/wf8zsYtDTT9NQ6CekhBOabcUuD7xnNussP+oLV4WIwMKSYpuIuP6ZS/rc052rLsLWR0byDMxH5yTRAU2ttBJr+1CHV83EUS5DLprE2mJiy/iQTwYXJdFVTtcz42sFdsrPoYIMqzYEH2MNWeQweDg8mFNK3JMosDRH2YqvECBGTHAo55dzJ/qRA+UgSxrxJSjvjhrUGxpHXwKA2T7P/PJtNbW8dwvhZHMF3vxlLOvjIhtoYEWI7YimACURCRlX5hhrPvSwG5FL7z0CUgOXxj3+dCLTu2EQ8l7V1DjFWCHp+29zyy4q7VrnOi0J3b6pqqNIpzftezr7HA54eC8NBY8Gbz/v+SoH6PCyuNGgOBEN6N3r/orXqiKu8Fz6yJ9O/sVoAAAAAAQAB//8AD3jarXwHfBRl+v/7TtuWLbMlm54smwIJJLBLCKGJCOqJgIp6NBEiiUgNiCb0IgiIFU9FkKCABKXNbAIqcoAUC3Y9I6ioh5yaE8RT9CeQHf7P885sCgS4/+/zE7OZzO7O+z79+5QZwpG+hHBjxNsIT0wkX6WkoEfEJCScDKmS+FWPCM/BIVF5PC3i6YhJSmzoEaF4PiwH5KyAHOjLZWiZdIU2Vrzt7Ka+wvsELkmqCKHtRYVdt4BE4FyeSoX6iMiRPKqYCxShTiEh1eSsV7iQaqF5RBWp7FaE4o6dwoVhHy+H5apHH6iorqZf85805OM15wrd6edSAhGJjfSCa1KSp0jhWk4gFiFPMYeoEleg0DpVcNXXii6SBCcFl2qieaoVztjYGdUOS3XslExxjbAHX+fyZYFqoTQgdCfnvz6snaPcl/AK611DiLAGaEgm6fRmEkkCGiK++MRwOBwxARkRsy0OjmsJTTLZ82o4OSU10x9WiaO+xutPSM70h2pFgb3Fu9LS8S1RrK+RLFY7vEWVjAIlqU5NdNUrifomza76iMlszavpbRIsQI9LjYezPjjri8ezPg+c9blUG5yNc9WrAZqndEna2etfp3OJL8+6s9e3p514oCS5argkkwfWZa8SvsIiNZZEMxzEu2qs8TYPXqrG7ouDD7jYq8xevfiKn/Gzz8C3Eti34JrJseukxK6Tip+pSYt9Mh3P871dHI9EumTkQkpqWnr+Bf8pvZNABJ7CgCcAP2Eef8K+IB/wBfigB3+K4K1rqGuwVk/bDRoziHaDl3/9z2ByXjs1YMwA7S14uY92G6y9SVfeQV8bRZ/X2M8o7bo7tDK6En/gPKggqTzfkY9Kj5AO5CkSyQMJKm1BDub6SJ6IPM3LteRFZBCm4g2rKZb6iJyCp2W3BbQ0v0Bx1KnpoKIko05WOXe9ku5SZWB7bkj1guDahhSvSzXDicSQmuWsV/3uerUAxCOngyrHFSteucYmprTJ9BcrZrcSLCZqiii7txPq8CdkwVngQlHYGx8OdSnsnJ2TTws7dykClUyjThrsnB1sI/m88f406vNKJl+wMJ9W8uWHHvvblsd3fPT225vLtu3l+PLnH//bs0ve+PCtj5TS7afoc5L63KqKSQ9f3WfnS2vfcxw65Pr+gLhi96r7py7r3e+V6g1vOXb/3fYxWNCk8z+JC8WDxI7aDdzpTh7S+aN2ctRHBOCImuCor+2amSfY89SucCjb2KHsqKdKjwKF1KkOYIHDpXp13UWFzYDDfDjMd6md4bAtaGlP+O11yO4am5ACRlCsds6HP1Iz89LgD6J27SS71ZT04mI1QYaj1LRiZArwIRyKT6VeKdgmu4gxqCfVGeKhfpp1mfcnrZ43d/Vzc+ZXjbprxNDRJcOG3VXLvXVDtJjOgTeqVsMbo0v0N0qE/gPmbt06d8CcLVvmDJk1a8iAIXPmDGmQhakdzz26euCcrVvnDIy9NXD4jJnDCHiz4ed/El4DvrUhHUlPUkEiKegVMpBx2VJ9xIqM684Di3oxFgVBeYK6eXeCw04utSsc2kGT7C7VB4fxcr16FfxGPmy3ChnZHWRkks8OTHInprZjTOqeLbt3EJM9MbVDZ11rOne5ijJ1ATaAdjgp7QUeDdTEbwrmOGgjV4rgUzkmB/WAHhXBRxiPhj+x1HnzwMiqx18adtsa+lynLpP+0u81bumM2w7d9/Hpyk1rR2y7VisRTVzBtEEPXXW12q3TPSPLJtN7K98YYxvz4l+rNq+dOWzB1TO09OuUMfM+/+th8ZGBt9ZFZlVffw09JpqEzJEruEN9Hr1pYYeSroPGLgAbnCb0IceY387WvbbhsqkiXeCvkVGN3nmauSxb6EOt7+3XThK05Ye1TtxEaSiRiYdQxc0YbAWr87AveQpdpCidSpzsc7mBDdnkYRq/SUp64vDhJ5KkLdoJrqeTjud6l9C/3B39Vdvu1bZHfx1/7RiuM17brXWivza/Nl+n2puu3cUtF7q4nKJwPIHLE1PQ/fiRow8nSS/TeO3EZkmrKOPc9EYv/QvnK7u2JLpXe8qpPRx9bwzbdyo3m78B4oiD3EMgpIKzoQVUcbL9cyB7EczExZy5kp1EIQjnv0NUQvPfQfd+ovP+TPTqDoW4FMdeQaEuhdvLqZwjP58qDnSmVBU58Dc20BQeY6jE/IrIh/ksv+gx2WiOJzWD3iiMNdO+Aa3mm9vq3rvtiHBr6Uw6VVs2t/Re7YuraCft4560PWH77U+WC52EHRBlbyEKKVBMYZXa6hUxBMJD70is4DQpwUPKo6OEsGutY3EcdFwIRSxWfM9igo9ZLXhoJZZY5AW3D6EdXL0clPvTyHT6utZvOjetnH6i5ZdrafSYvofBmkadZBfoTBbuATXG2kxjQDJoUwKSKxY3qszgfhXj4Iv+6pe1E/p1OnHdOBe3Biy3DV5HpVI9/lBFKAAW59XyXtREwB7G3nyd6Ddct9JS/G41vHQk6+G77WIIxl7feICXQAny3nr2o18CsUv10vXr8ftp5x/g/s0wkEwAMiHwgVX1z/lpmKZxoyZEX5gtdTjzKcNMi8G3BA2f3I1EbLiQLMW8MTqVFN3vOpv8LjAi1fCwqk0oRlZ4ZJc7HHInUhcXbMN59PAi695x8ekjR/44feTw/1SqGzZsU6qrt3KFtB9NpCHtA+0H7XXte+0j2omavv799Dd0/Lf/+c+3QMeu82e4DWItyKI7iQjo7zjcEeVcGXsLEO8wsQjACidslkeBC9SiGzNoMxMRMjcLRL6L/rtSNN865Gw/sRvyaDJgLBloToKjiAMptgHFaCRqPF8fiWdXi09CLUvWAZPMABPYpSrBcpIHPyDZQdU8Eh56HLByCrzrSZTdEd5mLQamqDbgj+IsVuLliEQ8xSzIZBvO00T9oI6FNOYefcHJ4h+f7Dr2zGJtMsf93FBJjy6c+OzDGzZPFjw7Gg7vqPyfFVo3sXQEl/rUOyOWrH91JdIx9vxP/GmgIxe0JtIW6RCBDrEtbkkEZkRSkCQvkORlCMObYMmrtce1TYGQakfR5unuACID51L8iDcS4DihADEFnEKUgRBDyXIp6fiuDMdyAaKTiJzOMEscEN4ewYcfYgegjrYsdsQB4FBJVnGxYpeVNgBJ3GpienFL5JEHxsMOGPU5jYxhyCPYJnMsV/7Gs6u27nhp2bI161eueLimnBP/3L3/h3nTliw+d3CP9jNdJC1TXnj62SfL1sxesvbFxdLLx+p23729fc5rc/Z9fQR1ux/IuT/YgpU4yRASscS0qJbYLJwdgDoAZ6lekQAYuwoUS50SF0LlVvhQxMxciFkCJloYPLagN5FRuWyoXLRY4WTFwVSMhmVAkqBnkJjkmPpxax44frwi+h2XKoVpeV++oSGrVHuclpfyvbiJzD9sBZszw77SyX4SSW2UW2qj3FwoN4+tvsaR6jLn1fptqS4Qmd9WzxC8s64myUkceSoHcRxFlOSMAXPmyx1O9OVOh+7Lr9p8ZjH6clFxuhTXXjBixbN351UP/tkVztpqvA6PJy8CrxkPZTwUlEBli4nizacRl8erw2aqmtHTpxYrSaABbtRsB8g3QsxJxRfIFERpyvEgpO5Fi7q4fV5wBtlbufHVy9a+8MITDz8ZGH0ztz+6rkvRwik7jx/9uvYXOl168rkDO9cdHDrMxadOjp4JdeH58+TwUe3PdwjzTyuAV+nMVnPIXSSSgNxKi/knG19f685MQIjoFoE5bZk+J6OrCinJLmSK6gPmtIPfgWTQUMHkTmAampkGGupzAgS0uYE4c7EiyIoJqZE7E9BEvykfAI2UCgYKbo0RQoqak7mCpn3cf3lxenH5wLWf9dg55cDx3w+8o52r3Pv08m0vV03fHuBS6OQG2qtNRklGWsP78weO1H498rn2I23f8PGv/3pxW92cu5guDAAdRV2II51JxIwaik5bJWie9gLFXIfpaixFg8CnOlAHiRk2zRfr0cNKeVOwyE08A/jXT5zNtVXacqn5C/GGsjLtx+gebemMGXQq91dqIoglxwA/7cBPPwlCjnw/ifiQo8nAUQuu2wE4mhPwWYCjObiFjoyjCcBRCR1AJhwkuNQ04KcbDnPxXBwwuBOcyM0ENGnhfckBJ2MxMlx1E3ACObLq5OF3B7caJxXrULKoGZJkNi+AzTfnsKfZ8ZiqRfcuPvn3Xf956N5FL2hnP/hEi1bse27FgbefXnGg3ZYli7aqCxdvpgvm72nXVrl/10cfv36/2rbdnnkHPv3kwGNr1z360JYtXMH8Vavmz6l+HnVqKPjNfxk6BejIGot5LAJkAQcS0qw8cCBBatIpbz0qFIQ/JRBSTV5dp5LRFdhZymV18LpmyVb9XAK6BzUL9Yz4dKIJi5BeAkaRU5RGWQKBuJkzcLNO7FByftenmnb6i4Grr4vvu2jwhgOFNZPe+m3W5uULtmVtX/XIK/zuozRXO6md1QZHtfq09DEZKV9/uHzEGOr9cuOxRSUrP/zytG47GCSCQldWD+nQhCYYIEAsYUbSADshlAAvyBCFpRFR8PCzculSwBX83xBbcARhTo7QDWKyhXQiEROgalXCC1ljAEkxh7D8IeH1CljR4AK0ZMOXcYCY0pbGMJOwAq+u28IMfgn/EVydgFf1UZPPT30D+O7RlRMmcGX099F0xhztlxQpRTs9B/fzFN3Af85vYvQl6UjLqlNnZdQZxKCNUPh5iu/TsJvvQzeMG0dXjRunrzkL1nxHX7OokBYV5lBYeRZXOWFCdAk/YMYs6k4GL+CcqT04mvH0ZjCi65nupJFJJJKMPE2xx9CDrSV6SNfRg5uhB4CiSnIIzaU2zUu6C3lKXCOkYElsXBLoCh8PhuKRVYsLHW18CjpaKe4C8OCgviB42Bh4MAWRqzfzdRtq3l00o1dyBc29Y8JdS+bcD1GHtlkmlLy4+9DmxR9PLRwx6oG7byt/Ztq8h5fed279ypVAzwytu/S5+DAJk2vIFhJxYrXCElaLxHolLaR0KlBzHfXK1QWqD35lFqg8Aq++zCRyIOfO0X2sBMlEP70ydNW+s1P11KGnS+m1FzzLGSVpL6lJSu7ZC+swtPGIhZYcsCCVtgWaA3Jvi4WXM3PzOxV2w+KF5FZNbZAJzlz4TId88NVXFwE7EhINdrhJIIPwEsYYI/3s4mauO8xLzJ70D3AkAMd++EQGofobPWiRh/n3GW76Ga2gi+lS2Vr3wcB75MLnyh5Y4vGf2Dhyaj+OD1lvKnr0RZtbU7Sntb9rI2QPnUhvHlLbK733B3dqC7VRXLHr1lG3P9KZFmQM7PigQr+mGzlJS9WGHNb2lQ0fNfqXgxoNFxZx0X0LR515iy6i27R22jxtkdahfbB/u470Nzp11au3T4UMlsvwJ/0M8oCsXvgG4oEJMqH2us0qfJgFhVrJTCi4JQlxQFwBy21UipHAigVMAPdBPsB7AkAo124KlzXr6Wjp07u5G7WvJVE5exN9WhvHUcg9WBzYA+ssZvmhH9Ycb3gHJ3hBFn8y0Av62XLMCwaYyJ3o/kMAJJje2pz1NaLNYwYDgPMpYHagyG0o/slCKlH9TpYioi+ECJuhY3JIxJojvayA7uUDhbGDPfSl76JzJy7aEP2HNo/Oe+HV6jXaRDqoasurivaBqOzZW74hI+HQwv2flK557IGNpcsWP7RMt+WFENs2g22mkrGGZXqAHk8yg+jxgKsYaIgDPBwn4Lk4CxppGiPNBSS4WPVTsYQYDDaF1HQslrhA+4TkYqRClRJRIeM8cMqUoFeNXODVBUj9UZ+4VOp1o4KF/RLEM7KQ5v72I3V5uPKEd17d88MPe1495C/nPNrP3/+m1XGjT9J4OvqPb6Tte7XDP5z6t3Zk1+vSl+fonehnUD7vg3wsxEM6GtKxxqTjwdDsjdUiFKsLUQHzIz7dfcug+FgzCAB3SU/amSBXq6mNjtDWa79DutXxMPVrP36ufSQq2nNa/evaj1pVKc3/Yfdxms94iesPhfVt5DpjdUtsdQF0Q9RVUeSZKuJGYmk4S9EtgFQUa0jPx40kXE/A9Z89/FMNx7i/R6/hg6JSFj1aFl1fShrXHcXo7q2ve/GaJj3itLamsaDtggX38C801HEHoj1wsbfujt6ur7Uc9OUD0JcMrKmlxfSlFSWpTUhMQ5DJ8uFAK/qCkNMUisQzVYuHNIvZga46aaA6yTKzhwRQHCW5WI2DNNFAmy3Uxyfr6iODMchMg5bTwj9+ohYfNzlp364Dp7T3n3g3S5tNz3XSogc17XVuCMjUQW/9aZe0fLt2/Gvtt+PaVzd3pLPKomevm0mHNfG0nsnyKsOjmHSPoojhWivPuGptkqSN9UcUm15lFljDpFGG2IAJQ64DTK3ge1RUNBwQleit3OazN3FV0RJ9PUi+6M2sBhFoJsPG2gVcDX/ExiseqUT/pH/3FsBmKnzXg3rnaMyNHI25kYVdCpTfHctcWQ5k05Vfz1UcwGsL5CiKu3l+AithZpmTXdj5Fq5843OLNlee3PV+xVS6TKpat32F4Dl38q2fxpXtNcd49jPzjzGeWZp4xtsZz3j0jM7G8ggXwooaUXm7nlFQPaNACsE5+y0U4nQQ2PYW13MxF93ALeIejT7/NrCvhKsSo8XRgMhtiQ421jbB2mIsAuBKBg+lGA8jPNN6XrTEKphMOL49lRwY9dntTfYkdYRryeQ241qmuHAjJbGKJkvsdUaa9AKkKhPGSMUs13BinB0jskmv92F1JcLbHCwKM9ooaoQnhwapySPvWc35JS6xqsIqRb8bHD0u2WA7msiBhjzAzebOakIDjS6Jzm7SzVNMN6+9SDebKyRoo2Dszo7ixt1xLGszG1tSeUtsQ0WootQk76nku0ugowchAJ5Lo8I/z94kHKfnUsG/zgLb//7Cupc5VveyXLHuJdj0uhf4/5ivzSAeNF83+Fssgvlm0Y6UUIF20d7VGs4T7cPK+o8+O3nqHx/9iK4/kY7U1mo/nNS+19bTETTpZ+1bmn7q1AmaoX17QsfvyJu/sfqFh/Rp7g3B/9dabEwHLS1DgS2E0cCJBV4jGqgem9wy8AYDibQp1v7+r3Pn/qUtoHNqt9du1xaISv3efT9G13H7X1n28Gv6Pmadby86gFcesOebSURGXvljvEpDXrVhG/DCBrwuNcngVRBLE17Muh2yjbWjZEiMABXIumalyaBOzVjo5Ux+UxbDaZdg5MTSs4O1P7s/cP0lubleOzP4RP8zqakXs5Qju4CfH4nbALsHSamhbS5d29QgsDQxmbE0EVmayShKAoqSQ0qSnvmlM/SuiCE1C9UgSTfzOFmRgapEomMd5uqV4EVYB6BBvN8Hfp41jZqJYBc9+e+zD85YXJGRNSMrbcsqbSy9++CO7a9oD4nb3j847ZXcNtsWLu07oU1C5oJrFz24KjqJ+3PN4sdXge1gLl8JculAyluv/2GTUU2BUJYi47mUhJYdxvbNOoytNBTN7bGmZ5ODLK/FJmKNw5fVvtUWYmY45AdCfaaWLUQhKKG7HcNN0jZv+Sxy9NQf1HP4nw89yE/6UN12cMc3P/2ufXf0i7VVdIX08voVsyue6dZj77rqT2ZP3yqK0vJdz02b9GTXHu9Vb/2AThp3SEJ/0QFk+BjDx2C1UvN6icKHWEor1aHuR0RWmRUBFEQk1naVsILXlBFiL6CDUKLZKrFScnaHeAPzR9Ws14b+skjPhlTJ8L2KtdFd8lgkdOHFWPUD3SWkLljsZaVwiDONAQfLGtWVX6m1xyq0o//+QTtGP+O/bMja+e6h1/H3zw1R3Q8i7v+Q4Z6AUakkHBs1QKzDAI1KLLGiT5j6w0WI9zMW0B2pkJ9uXxD95xTwcdeOHi3shFBKSTH4fewD+EitXuNRnGF2yQjFAACXjWekUEjVqUuNww4hyl7P4t7485erWVufuBTfXofe/9m5r+rkcaOUmO9Q5L2q2XdGVEzwxuyfb8FqIsSQGpfs9ORF4LVZQbGGM7tklv3t4Exmp0v2NXXlKaxthGziQ8fKvDiQmE6RRP9VFAmlOUETDRbPpJb2UhHtPIV2LpQKqGmG9tAU7bVsKUvbMRXIP/EN/VbwnjvxT/wFvv6OZ589t07nb3fgr8LiTLZh+eYwKwYbcUbPpjiMI4KVxREL1f8PWmh3elpLfoI+S1c9oaXQ049pt2m3c8e4D6LLuUnRUDSNWxCdA2sEYI2dsIYZEbupUYY8LGApUEx1DKFbEambWPQCivUDpBfWooirltG9dP+y6MkKUWn4nG/XMCZ6gkvWaYDEQBjPdCQ/FstjeJXn65sUxaRXqAE0G425cCENYBEk4LuTH9bwBv9xwzp+9gjh57K/noszcMI67W16UpoHdlXIKimA7LGSQvlYnajW5CV2IQ9RDphX7C8+FDMpgB5BOexbR2/45BPtbdOrZWe8ZXDdjucf4MVYP4q07EeBkIMd7+NG3ScqZz6FzxLYQ3+2h15EMRXoRl2A2J/twVQHy9VK+sKSS6VghRTs3RXbjClW8fFB+AcEHfj0U9pf2/6JdKLsz+uxvsQd4RoY/xp7YwbLYC8sfQYt4wfQvGE0d9qBNCntDfjC59F29Pi4cVqKzid6fhU/lWXQSc2wGR40IywM7oXyUxoeK2XfuUPYSfeLB4hA2hC9AcELxIWdRZFxFnLyOAG0Qt9IUdgTvINbeeg+cY+o/YHx927AxG8LAyFq5ZMTemarJIUjAVw9xwoZLhbizBDA+PYBD+JSLNIUMPPGgm2mS7Ghp2cTAECvG09hDTcipOaGQiFI0zGtVzsatn/tb/2Z7SfnC0rqXlFNij8jKAl7d+799XcLs/IEV01iQpInT0l11aSkJoO5w59N5h6Bc8zqExJTUmM1n8SURnvPtLNBFTUNgEnEE8hhzTI+AJbnx1zJLEdszni9xNM5s3usQVYAJt+5iFXAwL36IZAWNp85KITP3E35r0499eDsFydxk6Ztr/nC7pwdZ+3x9uyqbRXTx89/s/1/1u2nGU/XPjht4ZzhVJKkqcNG7Xg5eqJ4QmHRTe1uK9+4dMjk6SOPLWOYZzXEAUlKAE1JJ6MN7GVHhvsA+EjI8BQ8YH01iWJczWAMd+uJgOyqV9wuNQHnwPTujOpG2OPSywh2JDkF3Z2LN0CrzDoNst4zyTF5jPowIiDJtLqyy8Zp+7/66o2KzYV2ue2a+1dXPb969rNZUkK0cvhd2jta1Peb9s2dQ9fRjJGTfzzg+5Dys0Yz3RsNuvMO051RRNeYeNDX+ECsSBkRkBYnYAQnS3edNqRFRz8eoMXjUhNBL+JCaqqM5V0GfRKxACIEWHEuHg7NqcYEjbslDEDMg4Ew7Pf6vCbIvbjRv34Zuf9ebvy2uVurNygVO8ZxlbPXH/0PZ849QTveU7ZOEqUFq878PXfvn0umS5L4aEkpLWDymAx0fGrI404dr+vhGeUhxOQhMHkI5pbyMARhsoGux6SR4EYSnKBvVhmU0ZBGnMko6rBCImYROc0L9LKepU/+8sCUDUUV46xdXr5335eVq6umrcpr9/T0qjX0vI/ytGjUEG7BmR9X3z6CBn478OPYEbRh5H1a9ENGxwig4yOQRzzQMYxEvEiCXTJISMWqm8UrxKpuGc1LPIlG+oO7T7QirLZ7/Swtk1WXjLKw2FGhZEMWhE0rBXz61rH+2YZ4/AHdnEZQ2+63jkeFfVXlVV3DPV+f/67223yOm7Hh0UW1NFr0Iw01fFKW+sofvbrd0rs/bU8nimmP7H4X9KkPEFEjdSB+ciuJxDOrwPgjWQAk4WykHFaJCGoDWCyhQIlnExo+rJWEmk0URuJ9TP8QkSVixJLQJVjYvsN6W6ixAacjtT41654M9A06E8JtSsZSTtMq+cMlVesiVstdkmlWeVVJQ1v+MNMTrT9fB/xNJXlkmlEFDIBmmGFzOpPbmpkb9GIVtT1jcBrsL83FsE9mKMZuNl1WoHYAbqcR3XL9co0g25ONyToTcDwZ0htA/2pbe/OKIFOeIr3a0HqnJ6ZIRw/eu7HIUfrDBwOVPum9H7256oWijeX7j1Y+DyqVm/PM9Kq1hkqVjthy7h8f/5odKM0I7Fi75JahtM2v++vH3UH/GFmpNXygx6YqCEtfgI14yAAD41jDuq9yoq9yNvkqb6N9cyE0cZvhp7CCYvMw1ACmTQy8GfNO4HmD+kyHSa6q7FJbuemVymUzZr6YA27ontET/vFNtJRbrTw7f3xUYrq+BTaVCfthc76x/BWVBAOl0KIB5dQbUM7GBhQsiQ2oLRUVFUK3c2+K5Rs34jXPP6L1p3lwTSdQ2ZUwsaI0BQvAFZdCMc5hT99VoMp2PTMG2ODSpeoOGfVRXpdJrCKUje2Te+2urr6hYyqefzStkAoV2shS0TqzUnjy3MTq7VZTeqxHtQZ4jHNljlhdFOtCIs6X8XYiYvA11Ud4OyvNMFZfuj4ktlofWlM5hy5/mNMG0a/5pVr/h6SEhpH0gKglRF8VOWf0P7CHJr6mkEbo0XppbUuFlHDmR/jOCsgH5oJdZGGuyHCLKwXrQGgWqCJKXBjtRPGB4Wazi2Xp2pHlYkUPVuJng6hY+lRzcDJE1w8lVQZ1UVLQgBVZVuN86IsCLSoyfqY+/guUyNtcoVaMt3XeUjmrOrPT9gVbdlU+MmfZCjed/tjsuU+lCd1q7hxbOXPq/O//E13KTX/7xa1LTElStIKbfuCl+ROj5pjuHwH6Wuh+I3VoAJfXeo9BjE2+SPf9F+n+OFtndbryauWyeXPWBIVufx8z8fPj0Ync8p0rF02K2pnu48xmAuznorkq+v83V8X8OEllXWNS1KIsAhjm8BEqaecOf6Gdrdz9cvWevRs37ubiAqdwsupU4BftQ9rpl13ncZoq8Bo6TaOes1obJYiwN4ylQ4kBa6T6ZuyCWApJQCwAybrtcC5WJGyOaWRO5xpgGrt0AabxGJxrxDSJtCWmKXV22cRAzdRNXdqtmrZ63fqq6c9ka6PELzYOK4lhmttvin7IbRtadmK/7wMq3DtC9/Gj+A+M/d9pZOm4/yYfnwKZg63gAgwA4kaY29K/IxW2RixglplbbwULFGGJs3UsMLm6S9zYiqINkxgWKH+2fbtn7m3EAnfcvuZsNpc/6FbEAj+V/pVzD52infsw5q+554EOF+RcTd5R76vHxYGKyI2tBsizcNrHjf4jjsTuWQAO+3TLMuUwxbzHWVA10Z/ncA2d8kS60K02bky5SSiX5k6O+mC9SYA9VsN6Hci8S9SL6GXrRaT1epHPD7gKC0YOI+80p8vuWjFODuI0mJIlKwmx+hFx+BpH0HUXHBtBb71+xMr1RZ0Bz5vUygVPz16377WPN78yvoyb/My8Bx6Y8tIbe7+sfbN8PKXtpPvGTb35xqmZuQ/NmbVp2O3zAd4PXTjlxv4lWXlPzVtcPXLoDInxPPv8T9wUcRDgl9tIxIM8iItBF1GHLqbm0CXWYYpvHC6Nt7SELtgMRHBAZMWpAxhZnwdrhruyC+Xs16f//POA3qlFme602/OmzgX4Qn3aTyXRq8YNFaWhdsfjz3FvwP5Wgow+F7rpfgwtUy+3SmZjk1iE8l5QhFLsrDDJ/BirQ8msKoklFSqx2kqzqlRRI6rNXlm5eNaStRmV46ydlcpN++hb3L3RZW9unjGe5869qd55N8aN9uBX98N+mtWl6JXrUu1n0dyglE2zZ2mlo4RuDZ/NncvnnXsTvno1IeIBuJ6PfGPMHjmcEIfwojXUhH2GVktT3sbS1L6bfj7dSmnqtxPvtihNWUS9NNXzvVND9XmEOEiD94qKHSead+7bd/IelsuaXDVmkwVy2cbSFfzZLJeFc5jLbufMFptew4J8treVM8HfjmaVLCO51YtYBjc8wI3Yq1FcCF4961A7Kfz93d93ljocnKUdLPulQOp44m6hWzTrjTe4L6NZb77JfXnuTe74669HU4ArIeB/LfCrZd2K/nd1qxCdqz3xCA3SrEe1J+ich7X3tPe4HM6jXUt3Rk9Gj9D3tTCsEQTMfIjJxJiVh2tjh9UeVmVEyfEFyHwgTW4uaJAz0yID4F5Fg4tou2yJXveglpv74HxfD4cjrjBu4MhAMSjAT/P5p88lTlppEcdw4uS/Lme2iDc3bGG61aKehU6IN/139axh3MPRJbwzOoXbM4SfeffQhoVGPauvNoFbKfUkaeRGAuZc63eQRCGPzQhBbLMU1JrZCTajk8wwKHYvIM3NYJT6gZ8ebPpTGY3b4lZFux4OWABjdo23gsQK+ya9rt/3/imrXkmae9/wO+4YXjEv9ZVVU7j0sQ/OPL7pVNGgdoceOz5pbVbOuonHHjuYe1PRyZePzVjK9hrRfqV+ViNLIS1bpa569mOUy8ByI6Xar9LuM33Y9yxA450xGtMKaolOo79AjQcaHQW1ziYa+TrFqvep3QaNfhIbbIjHqKc43KrVzWjsRRmJOkkoXpbH+1g+L5kscytH3nXXyPvmJu14rryionzVK9qu3IOPHStfmxlcO+X44++0G1R0atPxGYvHLp1x7OWTRbo8HqPVQj3vIYnkJoLo3GKtR73iUb+SGLHGXWnM3IHmZCyuJyKIZJNQFuylk0S2W1XywG8eQrTdmCbEEKjHE7+edLHk0fdY1cy/Pjn0qvHFAyaUrJ0+5IkhvSd2HXQP/eKBHTfcWByeV+Kcv+u6QV0Kp4/R9zjjvI3/TswmQTJDr5UoaWE1XqyPBJj7D2QY5RK8OcEJpwWWUQniRRWTDL1vns6yGoyWRgklSa5HKWAJJT0D6MEyl15CqbHaEpP1yFjY2d3yfqymKko8uyUrm5vxwd8rq97l+cYyynhO+MdTlbvf58y5R2hOwldfyu+tblZIWbrP/d1xP80BGvH+wo7sXqJn9fuI1FRIlxJDEQnTeAdfX0toimTPU9xhVn/1hmpsKZIZKAyy+1Nk7DwzdMATnLfgUyzoOxUfYoM2QHCbAoULs5QfFC0ePh3fhgVML346Ppl9Wkfe7no1E6ck0KoTEXmrksMAvWGeybTxjjScKQbJmnBmPtyLFuZc867tH5HXd/F8+dLK2U/Y6D7talM4n6cNg63XXmviFpTRtu/Vf7hV+ttSZY12uEwZv693aanz+0ol1kNaDvYWjxUCR7M6fa1LdhA7G4BzIYIM1Xp97ARAAy+vQwM/wiGkzc7GHSN2NppgtwFhUijiYJmfwwV/eUMMKtsdsVq/r0WtH0jx6bUNcGX4r8MyWk03LtOK6b3acPqiNrxCv8GQThWVaAfu06hctq1M20mvhV86jl8revgs437XHiTWNVeJnWEWvS/WOOeJVeYErNizRjqWzOGvxn5YGBnrW7uVtt0ielbDf1jhHn/+J/EP8QDEHj8g1FV6/FedDmPa0QcHmQwx4gGrvGWCidSG8yyZkAiH4WxemN3wWIAW0oXtIs5F8vTRxwT9Zj2lrUvN18dqO8Jf6SGlowtxbq3EPqkW4e19bWX3DovTx2emhPXx7TzZvV2Kc6eTjrrR6C1kvQnf7NiYMW7NksBLjKdVtC3NoVXaaO0L7bBWchudSAVK6WRtuaZpDdqTNGnHM09uELjhk8ZNmjVz8vgJwznhxSef2cEdod2pot2kHdQOaANphPbQ6rW5dD71Ux/E3PnatorNn1c9JU2ZVD2/cuGLE6ZJT1d9xmQ2k6zle/ObiASZIU65YqA2fs2kOfdoJ6j3HkfsgEv10JnaTG0WnWkcXHB/EWlx9xCoNSkDmf1qyCxEuuNM50VSqwWQgPPNeNdlJyahToD0lbah2sTu7I3ExvstL5BXCCQUDikhFxNLu/YA/FPBVwfbhkJKagux4S2YRSHIA1BsGXh7oTsV9D8HhNcJpwKDxUpYrgUREnxT6Y43GFxGjpfoo+fRRBq7naTMkOYakOYRXZqTIAPj6CQmzai2HKTLPVn1l759e5gtZVbhxqG7tg8aP+Le568kzehA/pY5M/relZY4rn/Xtn18Lt/NuV1uvUF7ju65+frb9L7xNGEXPSK+CRJor1tiLblEj0flMfByen6fTMN+ftqHT/Jn4PtWSWvAa5VoA+hKuKoTpz5MDP7H1SvOWIBnd6uY6motumgsLpU37s5m96dIRL8P2CTrFVU9ySoKG/OWJcNmDh6bekfcoNFVT2qrenYv7mCe29syaPDwiUw/F4B+DojpZxE6Kh/Dk/BrAfVqJ+6hOdqRTxqP1tKFdJG2yKMtajzQ50vZHKspnc2xui47ySoX6Gltq5OsvAf4c9E4axEyrPlMKyU68/SZmaGwLq56xclF+UqTi+6LJhcpbqjZ+GL0XX0vxhCj5DOkiLw8BC8FsBeBmEkWiYgYaSQG7ywFiljHCj7YDjaLLKE31MFGAecdwqveUWlc7sxPxoAcr88tmTqzulIG6dnq5FKgtcpSm9g90YKN3RN9heElRuelJ5joZNzgFeeYuC90dgjGvpONe7+DpKyVnWNJLCOspkL8CoRikMogIwVcS7oewdIZwKoN6n8Fm0hEXJWRjiTKCbYrkxiLepemcjbGwysSyeezgMnpsyMgbxmQRffWpkf8rU2PJBhZe8Tp9hUXtz5BwqTRcozkLRTARcMkYodG/eON/YA/gMwukZRcvCMcZ4kPqx5gOD4dIqn59tCX+3QW+9ica22i/ldi09YRo8djrcwpXWLjMR632PtnyNaLtz4/hjtYv1v8GvQbrI/8j37Xl+IP6zO6mdb6iKux490uzRXreHdi2w/A9gMXd7wDLtxtREjKwY435nq+kBq6oOOdkC8oSXtF1Y8db1+zjrfPVRPv8+uPpEhMSvBgB8vfrEoA51jH2xefmKR3vP0J8YmNHe+A0fFOtgFscaVltu+AsEXxymp+AWt+411C3mSj+W33tNL8zr5s55uFkWbtb6m+ttX29x9MaZp64NP3tNYA52+OKRGv9ytBFtivzCQjrtSxzGqtY5ltdCy3Y8cyI/i/7VkyIi/XuDzHqLtk95K+0sw3PwuBVhPfbumb6X/lm5/VfbOwm13uXB/sT5HYcxoSxKMX+uYWVf/L+2bjeRVXKPwzb9B69Z+2ZX75cj0AbkPMJ+v7PdDok8c223EqeohAGO9tUjJCzQj4v/HKlyYu5jFap68L88iXJe+s7kbw/jespYKMPSQB51YvUU1NvEQ1NSnml2WvHwzyv6qoMslcWFa9k6nlRcVV/iddDryxT5x594MkFly4Ux+KIhEyUDuO6TRtPCW28RovT/A24cYEr4mKmuQ4C7yVoL+VUFCbrOd92GdKwCKXLOm3J1yRtJhcLqBuIvPlFxEn9GZSiMX9UUzHAiSHXN8qYmnbmlW0M6xiByKWNsFsfYRYzcy64uQ18xTBInilwUtH91/qFvG/l/1KzU9w2uEpVw7zNiqCvCQq6E7EsB/JcjFtLSz+8rShxbdC26XtozltrdvISy3puqyxfN6Sphhm6A+YwU9ScSb/YhST1hqKSTesZTugmITEFKQnTlaTki8HaAwqWuKa61vs/mKUMLL5jpntCFbxNMHKYjr2dC5h5RmXsPKAse9asPKkNGPbDtz25c2huRguMIlvW1JwsW2ktGA6Jc8Lx7l3xTqIRHns2Scie76YLOjBCJJH0UvMYLTWWKlfv3eosCgMiXCO6fnvSr4vr94gHPcd/dbNxiTA920SltKz4iesDnAjwYK3XgxWfAW1vJFGJsQy/CQ9wzfSd3wmDoZudxz4BwuPrPBByg6JZVO11dfsKUh6dN5017V9S0b3u65kYGF2VjiclV0otu83Gk6MGHFdTudw27aFXZDWMuEUdx5ipAd3BdhMEtmwBi/G+vO1Hj2t9TAx1Vr1cgJrbeHUGc9G59i8EClWeZeRM+q7aioAI2gqmzD46vWF+X1umnTLDSu7FPQW6e33Tbq+yDtk2qRru1y+jvK/f+9FbqvwHST7PPCddRv4en2ItmnqFb7yotCL21qG87FLuK3i3it+fonY1fj8cCFEZfZco8Zn1MSeakTY4Dt7Ro2o3x7Dvu0J877hk6+7SghtpV21t7fq+7zMdS7zrJvhV1VMhi923FGjvW9c53wHKlH+v76Onz3+bnjnijGfUut7+zS8LwP2wpmNZ+z1YRZw0RP2dNoU0cUqKDbjLiCDTEWS2egGu+k0RnK4kfB5zYg3WKCvab/8msYt7bHH+RlrGqRgeUUqVqzslqiWz/ZDJm1vxiiDXTgT0oX+Qd3/V2vqrDTWDFeO2di5cswhmrN9m/YpfAde0Z/jPS93s+cJYSWmn1EREczhMD4KQBUtoVCzpwvFxZ4uZJSJ8UkHism4w87beBegAQXwZ9dSKi8l55euZ//pOjGBrKUNrIYUIFQxxVyYTZ8XN8cEJ+jCYrXPCReVPOE6pXCd31teR+FCxqWarkPxOkapqrSVyhTb002Asd4TD4KHhXwyBwnOMB6dptjCqszjhGItoTlWO8Na2PpIxmcpshP4GEUeM8YaR44VeyHtC5TcOpWTsP4JMvImABdTc7F+lIodjvhQJJc9zSWXWLAThLVRlGOHZg9pseNDWuzGQ1p+nfzGNL197WAPabFjr3rn6bq951j6aXPVxEFamKe4XDVOlwPST/izWfoJ5zD9hICGqactzulq1o/OYNVWfbQyiOOV5ILxSvavecbVk9700ksvUedXxZN7W7pM6br5bS4YPYo/724qLu9s6XJf96+0U5yvbGNZ1mkadDnHuTw/vpUDf3rePCHLY50u2uZ3jx6HRvHPCNew+3X8pFKvjELOh0+w1MMR3/iAL3zWjtnpgfScRSapzng+W+t38qArAA2o9evRy+/C2bpaZ1P0ciG6tdoNPBVgD+iB7M0D/+Aohw/yJnkUnbfiBtpx5CZp65C/SM+HX5TE8f36ae3pP7T2XKI2lFZHf6BzqTaPPka1qUyPEPh1Zc/UIJ3kgIzH597+f+LPPhMAAHjaY2BkYGAAYqY1CuLx/DZfGeQ5GEDgHDPraRj9v/efIdsr9gQgl4OBCSQKAP2qCgwAAAB42mNgZGDgSPq7Fkgy/O/9f4rtFQNQBAUsBACcywcFAHjaNZJNSFRRGIafc853Z2rTohZu+lGiAknINv1trKZFP0ZWmxorNf8ycVqMkDpQlJQLIxCCEjWzRCmScBEExmyCpEXRrqBlizLJKGpr771Ni4f3fOec7573e7l+kcwKwP0s8ZYxf4Qr9of9luNytECXLZJ19eT9VQb9IKtDC+usn8NugBP+ENXuK1OhivX2mJvqmRM50S4OiBlxV9SKZnHKzTLsntNhZdrr445tohAmqEsfpdeWKbffFKMK+qMaijYiRlX3MBRNU/SVfLQ2jkdrtb+DYmpJZzOiiYL9kp6nEGXk4Z3eeklVdJYpW6I8Xcku+8Ie+0SFzXPOfeNh2MI2KeEktSGP8wc5Y7W0WZ5ReWqU5mwD9f4B+6xb6zxj7j1P3eflW+E79+N1ukyzaV9kkz71+Beq19Dlp9msejgssDW1ir3S7WKjOO0fkXGvmJWujHq5HWdvWc0/pNxfUxWKTKRauBgm6YszTnXQ6mvI615TGOdaktNIksebePYEzZrMG88g326eeyVfMcMxSU6qk3uxt0uMy8OTUKA1PIN0g/Ioqe/W//BB7P4Hi9IeabvO5Ok/0Q0mU9cZcJ36T2IayfpmcUHU6a0K5uI+30inaIm/adUcsx802E74C0holcIAAAB42mNgYNCBwjCGPsYCxj9MM5iNmMOYW5g3sXCx+LAUsPSxrGM5xirE6sC6hM2ErYFdjL2NfR+HA8cWjjucPJwqnG6ccZzHuPq4DnHrcE/ivsTDx+PCs4PnAy8fbxDvBN5tfGx8TnxT+G7w2/AvEZAT8BPoEtgkaCWYIzhH8JTgNyEeIRuhOKEKoRnCQcLbRKRE6kTuieqJrhH9IiYnFie2QGyXuJZ4kfgBCQWJFok9knaSfZLXJP9JTZM6Ic0ibSTdIb1E+peMDxDuk3WQXSJ7Ra5OboHcOvks+Qny5+Q/KegplCjMU/ilmKO4RUlA6Zqyk3KO8hEVE5UOlW+qKarn1NTUOtQ2qf1Td8EBg9QT1PPU29TnqR9Sf6bBoeGkUaOxTeODxgdNEU0rIPymFaeVBQDd1FqqAAAAAQAAAKEARAAFAAAAAAACAAEAAgAWAAABAAFRAAAAAHjadVLLSsNQED1Jq9IaRYuULoMLV22aVhGJIBVfWIoLLRbETfqyxT4kjYh7P8OvcVV/QvwUT26mNSlKuJMzcydnzswEQAZfSEBLpgAc8YRYg0EvxDrSqApOwEZdcBI5vAleQh7vgpcZnwpeQQXfglMwNFPwKra0vGADO1pF8Bruta7gddS1D8EbMPSs4E2k9W3BGeT0Gc8UWf1U8Cds/Q7nGGMEHybacPl2iVqMPeEVHvp4QE/dXjA2pjdAh16ZPZZorxlr8vg8tXn2LNdhZjTDjOQ4wmLj4N+cW9byMKEfaDRZ0eKxVe092sO5kt0YRyHCEefuk81UPfpkdtlzB0O+PTwyNkZ3oVMr5sVvgikNccIqnuL1aV2lM6wZaPcZD7QHelqMjOh3WNXEM3Fb5QRaemqqx5y6y7zQi3+TZ2RxHmWqsFWXPr90UOTzoh6LPL9cFvM96i5SeZRzwkgNl+zhDFe4oS0I5997/W9PDXI1ObvZn1RSHA3ptMpeBypq0wb7drivfdoy8XyDP0JQfA542m3Ou0+TcRTG8e+hpTcol9JSoCqKIiqI71taCqJCtS3ekIsWARVoUmxrgDaFd2hiTEx0AXVkZ1Q3Edlw0cHEwcEBBv1XlNLfAAnP8slzknNyKGM//56R5Kisg5SJCRNmyrFgxYYdBxVU4qSKamqoxUUdbjzU46WBRprwcYzjnKCZk5yihdOcoZWztHGO81ygnQ4u0sklNHT8dBEgSDcheujlMn1c4SrX6GeAMNe5QYQoMQa5yS1uc4e7DHGPYUYYZYz7PCDOOA+ZYJIpHvGYJ0wzwywJMfOK16zxjlXeSzkrvOUvH/jBHD/5RYrfpMmQY5kCz3nBS7GIVWxiZ4c/7IpDKqRSnFIl1VIjteKSOnGLR+rFyyc2+MIW3/jMJt/5KA1s81UapYk34rOk5gu5tG41FjOapkVKhjVlxDmcNhZTibyxMJ8wlp3ZQy1+qBkHW3Hfv3dQqSv9yi5lQBlUditDyh5lrzJcUld3dd3xNJMy8nPJxFK6NPLHSgZj5qiRzxZLdO+P/+/adfZ42j3OKRLCQBAF0Bkm+0JWE0Ex6LkCksTEUKikiuIGWCwYcHABOEQHReE5BYcJHWjG9fst/n/w/gj8zGpwlk3H+aXtKks1M4jbGvIVHod2ApZaNwyELEGoBRiyvItipL4wEcaUYMnyyUy+ZWQbn9ab4CDsF8FFODeCh3CvBB/hnQgBwq8IISL4V40RofyBQ0TTUkwj7OhEtUMmyHSjGSOTuWY2rI32PdNJPiQZL3TSQq4+STRSagAAAAFR3VVMAAA=) format('woff');
-}
\ No newline at end of file
diff --git a/plugins/UiConfig/media/img/loading.gif b/plugins/UiConfig/media/img/loading.gif
deleted file mode 100644
index 27d0aa8108b0800f9cddcf613f787347d9981e05..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 723
zcmZ?wbhEHb6ky<H_`<;O9}NB>0iXa9k4z>53@HBR_Hzvhc6JPKHPSO+W(0~W{*!Vp
zN=+<DO;IS%EXhzv%u1}t$xlqt%gjs5XHfjf!pRL(r2{en<VXfqT?K`{l+1Zc7H~Z}
z#k9^rpxNS#X~E^{d$)JY=VN~&*uLeF!wDX};&s=!T-Q!>!e)X>LZSp~!n_rkGVK%h
z9k_L9<(o^(d!N7A`+9eTzQ!EZMr*-N2_|eB&45;SC+a-zP~lXP;z?eTv`FKm^!Y8l
zuZ^S*OlLmOv^V<W32(v1vllP#5cZpSD3n`EWSZY00c=K@0*zY2;VKxy)ce>ZNyYQx
zE1Q1d^QD!~t!MErXFkzlm$bqCmuUZ)iN%&IQkAQ(b??%e8>EQMBqK<8T-y}!%q4L0
z4v$MoL7}cEx5PfOihDclHe=f1_`ny+jJ+qGonTF#=e6?cS1GK1Glv+XQW)E^VpGzx
z%$u!=(=#3~+Lk*jmQUf$-=^(}f)AMWru(Y&&oE(%*JUs>JH24vgCGuUPSS^%^#tgi
z6`S6zDw0tR+QR$5bp7w`G6mDQzjYm%RoE)?D^8cegv~i}{SvGJM6wyypd<nY06JwR
Aod5s;

diff --git a/plugins/UiConfig/media/js/ConfigStorage.coffee b/plugins/UiConfig/media/js/ConfigStorage.coffee
deleted file mode 100644
index 92327001..00000000
--- a/plugins/UiConfig/media/js/ConfigStorage.coffee
+++ /dev/null
@@ -1,222 +0,0 @@
-class ConfigStorage extends Class
-	constructor: (@config) ->
-		@items = []
-		@createSections()
-		@setValues(@config)
-
-	setValues: (values) ->
-		for section in @items
-			for item in section.items
-				if not values[item.key]
-					continue
-				item.value = @formatValue(values[item.key].value)
-				item.default = @formatValue(values[item.key].default)
-				item.pending = values[item.key].pending
-				values[item.key].item = item
-
-	formatValue: (value) ->
-		if not value
-			return false
-		else if typeof(value) == "object"
-			return value.join("\n")
-		else if typeof(value) == "number"
-			return value.toString()
-		else
-			return value
-
-	deformatValue: (value, type) ->
-		if type == "object" and typeof(value) == "string"
-			if not value.length
-				return value = null
-			else
-				return value.split("\n")
-		if type == "boolean" and not value
-			return false
-		else if type == "number"
-			if typeof(value) == "number"
-				return value.toString()
-			else if not value
-				return "0"
-			else
-				return value
-		else
-			return value
-
-	createSections: ->
-		# Web Interface
-		section = @createSection("Web Interface")
-
-		section.items.push
-			key: "open_browser"
-			title: "Open web browser on ZeroNet startup"
-			type: "checkbox"
-
-		# Network
-		section = @createSection("Network")
-		section.items.push
-			key: "offline"
-			title: "Offline mode"
-			type: "checkbox"
-			description: "Disable network communication."
-
-		section.items.push
-			key: "fileserver_ip_type"
-			title: "File server network"
-			type: "select"
-			options: [
-				{title: "IPv4", value: "ipv4"}
-				{title: "IPv6", value: "ipv6"}
-				{title: "Dual (IPv4 & IPv6)", value: "dual"}
-			]
-			description: "Accept incoming peers using IPv4 or IPv6 address. (default: dual)"
-
-		section.items.push
-			key: "fileserver_port"
-			title: "File server port"
-			type: "text"
-			valid_pattern: /[0-9]*/
-			description: "Other peers will use this port to reach your served sites. (default: randomize)"
-
-		section.items.push
-			key: "ip_external"
-			title: "File server external ip"
-			type: "textarea"
-			placeholder: "Detect automatically"
-			description: "Your file server is accessible on these ips. (default: detect automatically)"
-
-		section.items.push
-			title: "Tor"
-			key: "tor"
-			type: "select"
-			options: [
-				{title: "Disable", value: "disable"}
-				{title: "Enable", value: "enable"}
-				{title: "Always", value: "always"}
-			]
-			description: [
-				"Disable: Don't connect to peers on Tor network", h("br"),
-				"Enable: Only use Tor for Tor network peers", h("br"),
-				"Always: Use Tor for every connections to hide your IP address (slower)"
-			]
-
-		section.items.push
-			title: "Use Tor bridges"
-			key: "tor_use_bridges"
-			type: "checkbox"
-			description: "Use obfuscated bridge relays to avoid network level Tor block (even slower)"
-			isHidden: ->
-				return not Page.server_info.tor_has_meek_bridges
-
-		section.items.push
-			title: "Trackers"
-			key: "trackers"
-			type: "textarea"
-			description: "Discover new peers using these adresses"
-
-		section.items.push
-			title: "Trackers files"
-			key: "trackers_file"
-			type: "textarea"
-			description: "Load additional list of torrent trackers dynamically, from a file"
-			placeholder: "Eg.: {data_dir}/trackers.json"
-			value_pos: "fullwidth"
-
-		section.items.push
-			title: "Proxy for tracker connections"
-			key: "trackers_proxy"
-			type: "select"
-			options: [
-				{title: "Custom", value: ""}
-				{title: "Tor", value: "tor"}
-				{title: "Disable", value: "disable"}
-			]
-			isHidden: ->
-				Page.values["tor"] == "always"
-
-		section.items.push
-			title: "Custom socks proxy address for trackers"
-			key: "trackers_proxy"
-			type: "text"
-			placeholder: "Eg.: 127.0.0.1:1080"
-			value_pos: "fullwidth"
-			valid_pattern: /.+:[0-9]+/
-			isHidden: =>
-				Page.values["trackers_proxy"] in ["tor", "disable"]
-
-		# Performance
-		section = @createSection("Performance")
-
-		section.items.push
-			key: "log_level"
-			title: "Level of logging to file"
-			type: "select"
-			options: [
-				{title: "Everything", value: "DEBUG"}
-				{title: "Only important messages", value: "INFO"}
-				{title: "Only errors", value: "ERROR"}
-			]
-
-		section.items.push
-			key: "threads_fs_read"
-			title: "Threads for async file system reads"
-			type: "select"
-			options: [
-				{title: "Sync read", value: 0}
-				{title: "1 thread", value: 1}
-				{title: "2 threads", value: 2}
-				{title: "3 threads", value: 3}
-				{title: "4 threads", value: 4}
-				{title: "5 threads", value: 5}
-				{title: "10 threads", value: 10}
-			]
-
-		section.items.push
-			key: "threads_fs_write"
-			title: "Threads for async file system writes"
-			type: "select"
-			options: [
-				{title: "Sync write", value: 0}
-				{title: "1 thread", value: 1}
-				{title: "2 threads", value: 2}
-				{title: "3 threads", value: 3}
-				{title: "4 threads", value: 4}
-				{title: "5 threads", value: 5}
-				{title: "10 threads", value: 10}
-			]
-
-		section.items.push
-			key: "threads_crypt"
-			title: "Threads for cryptographic functions"
-			type: "select"
-			options: [
-				{title: "Sync execution", value: 0}
-				{title: "1 thread", value: 1}
-				{title: "2 threads", value: 2}
-				{title: "3 threads", value: 3}
-				{title: "4 threads", value: 4}
-				{title: "5 threads", value: 5}
-				{title: "10 threads", value: 10}
-			]
-
-		section.items.push
-			key: "threads_db"
-			title: "Threads for database operations"
-			type: "select"
-			options: [
-				{title: "Sync execution", value: 0}
-				{title: "1 thread", value: 1}
-				{title: "2 threads", value: 2}
-				{title: "3 threads", value: 3}
-				{title: "4 threads", value: 4}
-				{title: "5 threads", value: 5}
-				{title: "10 threads", value: 10}
-			]
-
-	createSection: (title) =>
-		section = {}
-		section.title = title
-		section.items = []
-		@items.push(section)
-		return section
-
-window.ConfigStorage = ConfigStorage
diff --git a/plugins/UiConfig/media/js/ConfigView.coffee b/plugins/UiConfig/media/js/ConfigView.coffee
deleted file mode 100644
index 64b86e5e..00000000
--- a/plugins/UiConfig/media/js/ConfigView.coffee
+++ /dev/null
@@ -1,124 +0,0 @@
-class ConfigView extends Class
-	constructor: () ->
-		@
-
-	render: ->
-		@config_storage.items.map @renderSection
-
-	renderSection: (section) =>
-		h("div.section", {key: section.title}, [
-			h("h2", section.title),
-			h("div.config-items", section.items.map @renderSectionItem)
-		])
-
-	handleResetClick: (e) =>
-		node = e.currentTarget
-		config_key = node.attributes.config_key.value
-		default_value = node.attributes.default_value?.value
-		Page.cmd "wrapperConfirm", ["Reset #{config_key} value?", "Reset to default"], (res) =>
-			if (res)
-				@values[config_key] = default_value
-			Page.projector.scheduleRender()
-
-	renderSectionItem: (item) =>
-		value_pos = item.value_pos
-
-		if item.type == "textarea"
-			value_pos ?= "fullwidth"
-		else
-			value_pos ?= "right"
-
-		value_changed = @config_storage.formatValue(@values[item.key]) != item.value
-		value_default = @config_storage.formatValue(@values[item.key]) == item.default
-
-		if item.key in ["open_browser", "fileserver_port"]  # Value default for some settings makes no sense
-			value_default = true
-
-		marker_title = "Changed from default value: #{item.default} -> #{@values[item.key]}"
-		if item.pending
-			marker_title += " (change pending until client restart)"
-
-		if item.isHidden?()
-			return null
-
-		h("div.config-item", {key: item.title, enterAnimation: Animation.slideDown, exitAnimation: Animation.slideUpInout}, [
-			h("div.title", [
-				h("h3", item.title),
-				h("div.description", item.description)
-			])
-			h("div.value.value-#{value_pos}",
-				if item.type == "select"
-					@renderValueSelect(item)
-				else if item.type == "checkbox"
-					@renderValueCheckbox(item)
-				else if item.type == "textarea"
-					@renderValueTextarea(item)
-				else
-					@renderValueText(item)
-				h("a.marker", {
-					href: "#Reset", title: marker_title,
-					onclick: @handleResetClick, config_key: item.key, default_value: item.default,
-					classes: {default: value_default, changed: value_changed, visible: not value_default or value_changed or item.pending, pending: item.pending}
-				}, "\u2022")
-			)
-		])
-
-	# Values
-	handleInputChange: (e) =>
-		node = e.target
-		config_key = node.attributes.config_key.value
-		@values[config_key] = node.value
-		Page.projector.scheduleRender()
-
-	handleCheckboxChange: (e) =>
-		node = e.currentTarget
-		config_key = node.attributes.config_key.value
-		value = not node.classList.contains("checked")
-		@values[config_key] = value
-		Page.projector.scheduleRender()
-
-	renderValueText: (item) =>
-		value = @values[item.key]
-		if not value
-			value = ""
-		h("input.input-#{item.type}", {type: item.type, config_key: item.key, value: value, placeholder: item.placeholder, oninput: @handleInputChange})
-
-	autosizeTextarea: (e) =>
-		if e.currentTarget
-			# @handleInputChange(e)
-			node = e.currentTarget
-		else
-			node = e
-		height_before = node.style.height
-		if height_before
-			node.style.height = "0px"
-		h = node.offsetHeight
-		scrollh = node.scrollHeight + 20
-		if scrollh > h
-			node.style.height = scrollh + "px"
-		else
-			node.style.height = height_before
-
-	renderValueTextarea: (item) =>
-		value = @values[item.key]
-		if not value
-			value = ""
-		h("textarea.input-#{item.type}.input-text",{
-			type: item.type, config_key: item.key, oninput: @handleInputChange, afterCreate: @autosizeTextarea,
-			updateAnimation: @autosizeTextarea, value: value, placeholder: item.placeholder
-		})
-
-	renderValueCheckbox: (item) =>
-		if @values[item.key] and @values[item.key] != "False"
-			checked = true
-		else
-			checked = false
-		h("div.checkbox", {onclick: @handleCheckboxChange, config_key: item.key, classes: {checked: checked}}, h("div.checkbox-skin"))
-
-	renderValueSelect: (item) =>
-		h("select.input-select", {config_key: item.key, oninput: @handleInputChange},
-			item.options.map (option) =>
-				h("option", {selected: option.value.toString() == @values[item.key], value: option.value}, option.title)
-		)
-
-window.ConfigView = ConfigView
\ No newline at end of file
diff --git a/plugins/UiConfig/media/js/UiConfig.coffee b/plugins/UiConfig/media/js/UiConfig.coffee
deleted file mode 100644
index bc1ac697..00000000
--- a/plugins/UiConfig/media/js/UiConfig.coffee
+++ /dev/null
@@ -1,129 +0,0 @@
-window.h = maquette.h
-
-class UiConfig extends ZeroFrame
-	init: ->
-		@save_visible = true
-		@config = null  # Setting currently set on the server
-		@values = null  # Entered values on the page
-		@config_view = new ConfigView()
-		window.onbeforeunload = =>
-			if @getValuesChanged().length > 0
-				return true
-			else
-				return null
-
-	onOpenWebsocket: =>
-		@cmd("wrapperSetTitle", "Config - ZeroNet")
-		@cmd "serverInfo", {}, (server_info) =>
-			@server_info = server_info
-		@restart_loading = false
-		@updateConfig()
-
-	updateConfig: (cb) =>
-		@cmd "configList", [], (res) =>
-			@config = res
-			@values = {}
-			@config_storage = new ConfigStorage(@config)
-			@config_view.values = @values
-			@config_view.config_storage = @config_storage
-			for key, item of res
-				value = item.value
-				@values[key] = @config_storage.formatValue(value)
-			@projector.scheduleRender()
-			cb?()
-
-	createProjector: =>
-		@projector = maquette.createProjector()
-		@projector.replace($("#content"), @render)
-		@projector.replace($("#bottom-save"), @renderBottomSave)
-		@projector.replace($("#bottom-restart"), @renderBottomRestart)
-
-	getValuesChanged: =>
-		values_changed = []
-		for key, value of @values
-			if @config_storage.formatValue(value) != @config_storage.formatValue(@config[key]?.value)
-				values_changed.push({key: key, value: value})
-		return values_changed
-
-	getValuesPending: =>
-		values_pending = []
-		for key, item of @config
-			if item.pending
-				values_pending.push(key)
-		return values_pending
-
-	saveValues: (cb) =>
-		changed_values = @getValuesChanged()
-		for item, i in changed_values
-			last = i == changed_values.length - 1
-			value = @config_storage.deformatValue(item.value, typeof(@config[item.key].default))
-			default_value = @config_storage.deformatValue(@config[item.key].default, typeof(@config[item.key].default))
-			value_same_as_default = JSON.stringify(default_value) == JSON.stringify(value)
-
-			if @config[item.key].item.valid_pattern and not @config[item.key].item.isHidden?()
-				match = value.match(@config[item.key].item.valid_pattern)
-				if not match or match[0] != value
-					message = "Invalid value of #{@config[item.key].item.title}: #{value} (does not matches #{@config[item.key].item.valid_pattern})"
-					Page.cmd("wrapperNotification", ["error", message])
-					cb(false)
-					break
-
-			if value_same_as_default
-				value = null
-
-			@saveValue(item.key, value, if last then cb else null)
-
-	saveValue: (key, value, cb) =>
-		if key == "open_browser"
-			if value
-				value = "default_browser"
-			else
-				value = "False"
-
-		Page.cmd "configSet", [key, value], (res) =>
-			if res != "ok"
-				Page.cmd "wrapperNotification", ["error", res.error]
-			cb?(true)
-
-	render: =>
-		if not @config
-			return h("div.content")
-
-		h("div.content", [
-			@config_view.render()
-		])
-
-	handleSaveClick: =>
-		@save_loading = true
-		@logStart "Save"
-		@saveValues (success) =>
-			@save_loading = false
-			@logEnd "Save"
-			if success
-				@updateConfig()
-			Page.projector.scheduleRender()
-		return false
-
-	renderBottomSave: =>
-		values_changed = @getValuesChanged()
-		h("div.bottom.bottom-save", {classes: {visible: values_changed.length}}, h("div.bottom-content", [
-			h("div.title", "#{values_changed.length} configuration item value changed"),
-			h("a.button.button-submit.button-save", {href: "#Save", classes: {loading: @save_loading}, onclick: @handleSaveClick}, "Save settings")
-		]))
-
-	handleRestartClick: =>
-		@restart_loading = true
-		Page.cmd("serverShutdown", {restart: true})
-		Page.projector.scheduleRender()
-		return false
-
-	renderBottomRestart: =>
-		values_pending = @getValuesPending()
-		values_changed = @getValuesChanged()
-		h("div.bottom.bottom-restart", {classes: {visible: values_pending.length and not values_changed.length}}, h("div.bottom-content", [
-			h("div.title", "Some changed settings requires restart"),
-			h("a.button.button-submit.button-restart", {href: "#Restart", classes: {loading: @restart_loading}, onclick: @handleRestartClick}, "Restart ZeroNet client")
-		]))
-
-window.Page = new UiConfig()
-window.Page.createProjector()
diff --git a/plugins/UiConfig/media/js/all.js b/plugins/UiConfig/media/js/all.js
deleted file mode 100644
index cb3b553f..00000000
--- a/plugins/UiConfig/media/js/all.js
+++ /dev/null
@@ -1,2066 +0,0 @@
-
-/* ---- lib/Class.coffee ---- */
-
-
-(function() {
-  var Class,
-    slice = [].slice;
-
-  Class = (function() {
-    function Class() {}
-
-    Class.prototype.trace = true;
-
-    Class.prototype.log = function() {
-      var args;
-      args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
-      if (!this.trace) {
-        return;
-      }
-      if (typeof console === 'undefined') {
-        return;
-      }
-      args.unshift("[" + this.constructor.name + "]");
-      console.log.apply(console, args);
-      return this;
-    };
-
-    Class.prototype.logStart = function() {
-      var args, name;
-      name = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
-      if (!this.trace) {
-        return;
-      }
-      this.logtimers || (this.logtimers = {});
-      this.logtimers[name] = +(new Date);
-      if (args.length > 0) {
-        this.log.apply(this, ["" + name].concat(slice.call(args), ["(started)"]));
-      }
-      return this;
-    };
-
-    Class.prototype.logEnd = function() {
-      var args, ms, name;
-      name = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
-      ms = +(new Date) - this.logtimers[name];
-      this.log.apply(this, ["" + name].concat(slice.call(args), ["(Done in " + ms + "ms)"]));
-      return this;
-    };
-
-    return Class;
-
-  })();
-
-  window.Class = Class;
-
-}).call(this);
-
-/* ---- lib/Promise.coffee ---- */
-
-
-(function() {
-  var Promise,
-    slice = [].slice;
-
-  Promise = (function() {
-    Promise.when = function() {
-      var args, fn, i, len, num_uncompleted, promise, task, task_id, tasks;
-      tasks = 1 <= arguments.length ? slice.call(arguments, 0) : [];
-      num_uncompleted = tasks.length;
-      args = new Array(num_uncompleted);
-      promise = new Promise();
-      fn = function(task_id) {
-        return task.then(function() {
-          args[task_id] = Array.prototype.slice.call(arguments);
-          num_uncompleted--;
-          if (num_uncompleted === 0) {
-            return promise.complete.apply(promise, args);
-          }
-        });
-      };
-      for (task_id = i = 0, len = tasks.length; i < len; task_id = ++i) {
-        task = tasks[task_id];
-        fn(task_id);
-      }
-      return promise;
-    };
-
-    function Promise() {
-      this.resolved = false;
-      this.end_promise = null;
-      this.result = null;
-      this.callbacks = [];
-    }
-
-    Promise.prototype.resolve = function() {
-      var back, callback, i, len, ref;
-      if (this.resolved) {
-        return false;
-      }
-      this.resolved = true;
-      this.data = arguments;
-      if (!arguments.length) {
-        this.data = [true];
-      }
-      this.result = this.data[0];
-      ref = this.callbacks;
-      for (i = 0, len = ref.length; i < len; i++) {
-        callback = ref[i];
-        back = callback.apply(callback, this.data);
-      }
-      if (this.end_promise) {
-        return this.end_promise.resolve(back);
-      }
-    };
-
-    Promise.prototype.fail = function() {
-      return this.resolve(false);
-    };
-
-    Promise.prototype.then = function(callback) {
-      if (this.resolved === true) {
-        callback.apply(callback, this.data);
-        return;
-      }
-      this.callbacks.push(callback);
-      return this.end_promise = new Promise();
-    };
-
-    return Promise;
-
-  })();
-
-  window.Promise = Promise;
-
-
-  /*
-  s = Date.now()
-  log = (text) ->
-  	console.log Date.now()-s, Array.prototype.slice.call(arguments).join(", ")
-  
-  log "Started"
-  
-  cmd = (query) ->
-  	p = new Promise()
-  	setTimeout ( ->
-  		p.resolve query+" Result"
-  	), 100
-  	return p
-  
-  back = cmd("SELECT * FROM message").then (res) ->
-  	log res
-  	return "Return from query"
-  .then (res) ->
-  	log "Back then", res
-  
-  log "Query started", back
-   */
-
-}).call(this);
-
-/* ---- lib/Prototypes.coffee ---- */
-
-
-(function() {
-  String.prototype.startsWith = function(s) {
-    return this.slice(0, s.length) === s;
-  };
-
-  String.prototype.endsWith = function(s) {
-    return s === '' || this.slice(-s.length) === s;
-  };
-
-  String.prototype.repeat = function(count) {
-    return new Array(count + 1).join(this);
-  };
-
-  window.isEmpty = function(obj) {
-    var key;
-    for (key in obj) {
-      return false;
-    }
-    return true;
-  };
-
-}).call(this);
-
-/* ---- lib/maquette.js ---- */
-
-
-(function (root, factory) {
-    if (typeof define === 'function' && define.amd) {
-        // AMD. Register as an anonymous module.
-        define(['exports'], factory);
-    } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
-        // CommonJS
-        factory(exports);
-    } else {
-        // Browser globals
-        factory(root.maquette = {});
-    }
-}(this, function (exports) {
-    'use strict';
-    ;
-    ;
-    ;
-    ;
-    var NAMESPACE_W3 = 'http://www.w3.org/';
-    var NAMESPACE_SVG = NAMESPACE_W3 + '2000/svg';
-    var NAMESPACE_XLINK = NAMESPACE_W3 + '1999/xlink';
-    // Utilities
-    var emptyArray = [];
-    var extend = function (base, overrides) {
-        var result = {};
-        Object.keys(base).forEach(function (key) {
-            result[key] = base[key];
-        });
-        if (overrides) {
-            Object.keys(overrides).forEach(function (key) {
-                result[key] = overrides[key];
-            });
-        }
-        return result;
-    };
-    // Hyperscript helper functions
-    var same = function (vnode1, vnode2) {
-        if (vnode1.vnodeSelector !== vnode2.vnodeSelector) {
-            return false;
-        }
-        if (vnode1.properties && vnode2.properties) {
-            if (vnode1.properties.key !== vnode2.properties.key) {
-                return false;
-            }
-            return vnode1.properties.bind === vnode2.properties.bind;
-        }
-        return !vnode1.properties && !vnode2.properties;
-    };
-    var toTextVNode = function (data) {
-        return {
-            vnodeSelector: '',
-            properties: undefined,
-            children: undefined,
-            text: data.toString(),
-            domNode: null
-        };
-    };
-    var appendChildren = function (parentSelector, insertions, main) {
-        for (var i = 0; i < insertions.length; i++) {
-            var item = insertions[i];
-            if (Array.isArray(item)) {
-                appendChildren(parentSelector, item, main);
-            } else {
-                if (item !== null && item !== undefined) {
-                    if (!item.hasOwnProperty('vnodeSelector')) {
-                        item = toTextVNode(item);
-                    }
-                    main.push(item);
-                }
-            }
-        }
-    };
-    // Render helper functions
-    var missingTransition = function () {
-        throw new Error('Provide a transitions object to the projectionOptions to do animations');
-    };
-    var DEFAULT_PROJECTION_OPTIONS = {
-        namespace: undefined,
-        eventHandlerInterceptor: undefined,
-        styleApplyer: function (domNode, styleName, value) {
-            // Provides a hook to add vendor prefixes for browsers that still need it.
-            domNode.style[styleName] = value;
-        },
-        transitions: {
-            enter: missingTransition,
-            exit: missingTransition
-        }
-    };
-    var applyDefaultProjectionOptions = function (projectorOptions) {
-        return extend(DEFAULT_PROJECTION_OPTIONS, projectorOptions);
-    };
-    var checkStyleValue = function (styleValue) {
-        if (typeof styleValue !== 'string') {
-            throw new Error('Style values must be strings');
-        }
-    };
-    var setProperties = function (domNode, properties, projectionOptions) {
-        if (!properties) {
-            return;
-        }
-        var eventHandlerInterceptor = projectionOptions.eventHandlerInterceptor;
-        var propNames = Object.keys(properties);
-        var propCount = propNames.length;
-        for (var i = 0; i < propCount; i++) {
-            var propName = propNames[i];
-            /* tslint:disable:no-var-keyword: edge case */
-            var propValue = properties[propName];
-            /* tslint:enable:no-var-keyword */
-            if (propName === 'className') {
-                throw new Error('Property "className" is not supported, use "class".');
-            } else if (propName === 'class') {
-                if (domNode.className) {
-                    // May happen if classes is specified before class
-                    domNode.className += ' ' + propValue;
-                } else {
-                    domNode.className = propValue;
-                }
-            } else if (propName === 'classes') {
-                // object with string keys and boolean values
-                var classNames = Object.keys(propValue);
-                var classNameCount = classNames.length;
-                for (var j = 0; j < classNameCount; j++) {
-                    var className = classNames[j];
-                    if (propValue[className]) {
-                        domNode.classList.add(className);
-                    }
-                }
-            } else if (propName === 'styles') {
-                // object with string keys and string (!) values
-                var styleNames = Object.keys(propValue);
-                var styleCount = styleNames.length;
-                for (var j = 0; j < styleCount; j++) {
-                    var styleName = styleNames[j];
-                    var styleValue = propValue[styleName];
-                    if (styleValue) {
-                        checkStyleValue(styleValue);
-                        projectionOptions.styleApplyer(domNode, styleName, styleValue);
-                    }
-                }
-            } else if (propName === 'key') {
-                continue;
-            } else if (propValue === null || propValue === undefined) {
-                continue;
-            } else {
-                var type = typeof propValue;
-                if (type === 'function') {
-                    if (propName.lastIndexOf('on', 0) === 0) {
-                        if (eventHandlerInterceptor) {
-                            propValue = eventHandlerInterceptor(propName, propValue, domNode, properties);    // intercept eventhandlers
-                        }
-                        if (propName === 'oninput') {
-                            (function () {
-                                // record the evt.target.value, because IE and Edge sometimes do a requestAnimationFrame between changing value and running oninput
-                                var oldPropValue = propValue;
-                                propValue = function (evt) {
-                                    evt.target['oninput-value'] = evt.target.value;
-                                    // may be HTMLTextAreaElement as well
-                                    oldPropValue.apply(this, [evt]);
-                                };
-                            }());
-                        }
-                        domNode[propName] = propValue;
-                    }
-                } else if (type === 'string' && propName !== 'value' && propName !== 'innerHTML') {
-                    if (projectionOptions.namespace === NAMESPACE_SVG && propName === 'href') {
-                        domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);
-                    } else {
-                        domNode.setAttribute(propName, propValue);
-                    }
-                } else {
-                    domNode[propName] = propValue;
-                }
-            }
-        }
-    };
-    var updateProperties = function (domNode, previousProperties, properties, projectionOptions) {
-        if (!properties) {
-            return;
-        }
-        var propertiesUpdated = false;
-        var propNames = Object.keys(properties);
-        var propCount = propNames.length;
-        for (var i = 0; i < propCount; i++) {
-            var propName = propNames[i];
-            // assuming that properties will be nullified instead of missing is by design
-            var propValue = properties[propName];
-            var previousValue = previousProperties[propName];
-            if (propName === 'class') {
-                if (previousValue !== propValue) {
-                    throw new Error('"class" property may not be updated. Use the "classes" property for conditional css classes.');
-                }
-            } else if (propName === 'classes') {
-                var classList = domNode.classList;
-                var classNames = Object.keys(propValue);
-                var classNameCount = classNames.length;
-                for (var j = 0; j < classNameCount; j++) {
-                    var className = classNames[j];
-                    var on = !!propValue[className];
-                    var previousOn = !!previousValue[className];
-                    if (on === previousOn) {
-                        continue;
-                    }
-                    propertiesUpdated = true;
-                    if (on) {
-                        classList.add(className);
-                    } else {
-                        classList.remove(className);
-                    }
-                }
-            } else if (propName === 'styles') {
-                var styleNames = Object.keys(propValue);
-                var styleCount = styleNames.length;
-                for (var j = 0; j < styleCount; j++) {
-                    var styleName = styleNames[j];
-                    var newStyleValue = propValue[styleName];
-                    var oldStyleValue = previousValue[styleName];
-                    if (newStyleValue === oldStyleValue) {
-                        continue;
-                    }
-                    propertiesUpdated = true;
-                    if (newStyleValue) {
-                        checkStyleValue(newStyleValue);
-                        projectionOptions.styleApplyer(domNode, styleName, newStyleValue);
-                    } else {
-                        projectionOptions.styleApplyer(domNode, styleName, '');
-                    }
-                }
-            } else {
-                if (!propValue && typeof previousValue === 'string') {
-                    propValue = '';
-                }
-                if (propName === 'value') {
-                    if (domNode[propName] !== propValue && domNode['oninput-value'] !== propValue) {
-                        domNode[propName] = propValue;
-                        // Reset the value, even if the virtual DOM did not change
-                        domNode['oninput-value'] = undefined;
-                    }
-                    // else do not update the domNode, otherwise the cursor position would be changed
-                    if (propValue !== previousValue) {
-                        propertiesUpdated = true;
-                    }
-                } else if (propValue !== previousValue) {
-                    var type = typeof propValue;
-                    if (type === 'function') {
-                        throw new Error('Functions may not be updated on subsequent renders (property: ' + propName + '). Hint: declare event handler functions outside the render() function.');
-                    }
-                    if (type === 'string' && propName !== 'innerHTML') {
-                        if (projectionOptions.namespace === NAMESPACE_SVG && propName === 'href') {
-                            domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);
-                        } else {
-                            domNode.setAttribute(propName, propValue);
-                        }
-                    } else {
-                        if (domNode[propName] !== propValue) {
-                            domNode[propName] = propValue;
-                        }
-                    }
-                    propertiesUpdated = true;
-                }
-            }
-        }
-        return propertiesUpdated;
-    };
-    var findIndexOfChild = function (children, sameAs, start) {
-        if (sameAs.vnodeSelector !== '') {
-            // Never scan for text-nodes
-            for (var i = start; i < children.length; i++) {
-                if (same(children[i], sameAs)) {
-                    return i;
-                }
-            }
-        }
-        return -1;
-    };
-    var nodeAdded = function (vNode, transitions) {
-        if (vNode.properties) {
-            var enterAnimation = vNode.properties.enterAnimation;
-            if (enterAnimation) {
-                if (typeof enterAnimation === 'function') {
-                    enterAnimation(vNode.domNode, vNode.properties);
-                } else {
-                    transitions.enter(vNode.domNode, vNode.properties, enterAnimation);
-                }
-            }
-        }
-    };
-    var nodeToRemove = function (vNode, transitions) {
-        var domNode = vNode.domNode;
-        if (vNode.properties) {
-            var exitAnimation = vNode.properties.exitAnimation;
-            if (exitAnimation) {
-                domNode.style.pointerEvents = 'none';
-                var removeDomNode = function () {
-                    if (domNode.parentNode) {
-                        domNode.parentNode.removeChild(domNode);
-                    }
-                };
-                if (typeof exitAnimation === 'function') {
-                    exitAnimation(domNode, removeDomNode, vNode.properties);
-                    return;
-                } else {
-                    transitions.exit(vNode.domNode, vNode.properties, exitAnimation, removeDomNode);
-                    return;
-                }
-            }
-        }
-        if (domNode.parentNode) {
-            domNode.parentNode.removeChild(domNode);
-        }
-    };
-    var checkDistinguishable = function (childNodes, indexToCheck, parentVNode, operation) {
-        var childNode = childNodes[indexToCheck];
-        if (childNode.vnodeSelector === '') {
-            return;    // Text nodes need not be distinguishable
-        }
-        var properties = childNode.properties;
-        var key = properties ? properties.key === undefined ? properties.bind : properties.key : undefined;
-        if (!key) {
-            for (var i = 0; i < childNodes.length; i++) {
-                if (i !== indexToCheck) {
-                    var node = childNodes[i];
-                    if (same(node, childNode)) {
-                        if (operation === 'added') {
-                            throw new Error(parentVNode.vnodeSelector + ' had a ' + childNode.vnodeSelector + ' child ' + 'added, but there is now more than one. You must add unique key properties to make them distinguishable.');
-                        } else {
-                            throw new Error(parentVNode.vnodeSelector + ' had a ' + childNode.vnodeSelector + ' child ' + 'removed, but there were more than one. You must add unique key properties to make them distinguishable.');
-                        }
-                    }
-                }
-            }
-        }
-    };
-    var createDom;
-    var updateDom;
-    var updateChildren = function (vnode, domNode, oldChildren, newChildren, projectionOptions) {
-        if (oldChildren === newChildren) {
-            return false;
-        }
-        oldChildren = oldChildren || emptyArray;
-        newChildren = newChildren || emptyArray;
-        var oldChildrenLength = oldChildren.length;
-        var newChildrenLength = newChildren.length;
-        var transitions = projectionOptions.transitions;
-        var oldIndex = 0;
-        var newIndex = 0;
-        var i;
-        var textUpdated = false;
-        while (newIndex < newChildrenLength) {
-            var oldChild = oldIndex < oldChildrenLength ? oldChildren[oldIndex] : undefined;
-            var newChild = newChildren[newIndex];
-            if (oldChild !== undefined && same(oldChild, newChild)) {
-                textUpdated = updateDom(oldChild, newChild, projectionOptions) || textUpdated;
-                oldIndex++;
-            } else {
-                var findOldIndex = findIndexOfChild(oldChildren, newChild, oldIndex + 1);
-                if (findOldIndex >= 0) {
-                    // Remove preceding missing children
-                    for (i = oldIndex; i < findOldIndex; i++) {
-                        nodeToRemove(oldChildren[i], transitions);
-                        checkDistinguishable(oldChildren, i, vnode, 'removed');
-                    }
-                    textUpdated = updateDom(oldChildren[findOldIndex], newChild, projectionOptions) || textUpdated;
-                    oldIndex = findOldIndex + 1;
-                } else {
-                    // New child
-                    createDom(newChild, domNode, oldIndex < oldChildrenLength ? oldChildren[oldIndex].domNode : undefined, projectionOptions);
-                    nodeAdded(newChild, transitions);
-                    checkDistinguishable(newChildren, newIndex, vnode, 'added');
-                }
-            }
-            newIndex++;
-        }
-        if (oldChildrenLength > oldIndex) {
-            // Remove child fragments
-            for (i = oldIndex; i < oldChildrenLength; i++) {
-                nodeToRemove(oldChildren[i], transitions);
-                checkDistinguishable(oldChildren, i, vnode, 'removed');
-            }
-        }
-        return textUpdated;
-    };
-    var addChildren = function (domNode, children, projectionOptions) {
-        if (!children) {
-            return;
-        }
-        for (var i = 0; i < children.length; i++) {
-            createDom(children[i], domNode, undefined, projectionOptions);
-        }
-    };
-    var initPropertiesAndChildren = function (domNode, vnode, projectionOptions) {
-        addChildren(domNode, vnode.children, projectionOptions);
-        // children before properties, needed for value property of <select>.
-        if (vnode.text) {
-            domNode.textContent = vnode.text;
-        }
-        setProperties(domNode, vnode.properties, projectionOptions);
-        if (vnode.properties && vnode.properties.afterCreate) {
-            vnode.properties.afterCreate(domNode, projectionOptions, vnode.vnodeSelector, vnode.properties, vnode.children);
-        }
-    };
-    createDom = function (vnode, parentNode, insertBefore, projectionOptions) {
-        var domNode, i, c, start = 0, type, found;
-        var vnodeSelector = vnode.vnodeSelector;
-        if (vnodeSelector === '') {
-            domNode = vnode.domNode = document.createTextNode(vnode.text);
-            if (insertBefore !== undefined) {
-                parentNode.insertBefore(domNode, insertBefore);
-            } else {
-                parentNode.appendChild(domNode);
-            }
-        } else {
-            for (i = 0; i <= vnodeSelector.length; ++i) {
-                c = vnodeSelector.charAt(i);
-                if (i === vnodeSelector.length || c === '.' || c === '#') {
-                    type = vnodeSelector.charAt(start - 1);
-                    found = vnodeSelector.slice(start, i);
-                    if (type === '.') {
-                        domNode.classList.add(found);
-                    } else if (type === '#') {
-                        domNode.id = found;
-                    } else {
-                        if (found === 'svg') {
-                            projectionOptions = extend(projectionOptions, { namespace: NAMESPACE_SVG });
-                        }
-                        if (projectionOptions.namespace !== undefined) {
-                            domNode = vnode.domNode = document.createElementNS(projectionOptions.namespace, found);
-                        } else {
-                            domNode = vnode.domNode = document.createElement(found);
-                        }
-                        if (insertBefore !== undefined) {
-                            parentNode.insertBefore(domNode, insertBefore);
-                        } else {
-                            parentNode.appendChild(domNode);
-                        }
-                    }
-                    start = i + 1;
-                }
-            }
-            initPropertiesAndChildren(domNode, vnode, projectionOptions);
-        }
-    };
-    updateDom = function (previous, vnode, projectionOptions) {
-        var domNode = previous.domNode;
-        var textUpdated = false;
-        if (previous === vnode) {
-            return false;    // By contract, VNode objects may not be modified anymore after passing them to maquette
-        }
-        var updated = false;
-        if (vnode.vnodeSelector === '') {
-            if (vnode.text !== previous.text) {
-                var newVNode = document.createTextNode(vnode.text);
-                domNode.parentNode.replaceChild(newVNode, domNode);
-                vnode.domNode = newVNode;
-                textUpdated = true;
-                return textUpdated;
-            }
-        } else {
-            if (vnode.vnodeSelector.lastIndexOf('svg', 0) === 0) {
-                projectionOptions = extend(projectionOptions, { namespace: NAMESPACE_SVG });
-            }
-            if (previous.text !== vnode.text) {
-                updated = true;
-                if (vnode.text === undefined) {
-                    domNode.removeChild(domNode.firstChild);    // the only textnode presumably
-                } else {
-                    domNode.textContent = vnode.text;
-                }
-            }
-            updated = updateChildren(vnode, domNode, previous.children, vnode.children, projectionOptions) || updated;
-            updated = updateProperties(domNode, previous.properties, vnode.properties, projectionOptions) || updated;
-            if (vnode.properties && vnode.properties.afterUpdate) {
-                vnode.properties.afterUpdate(domNode, projectionOptions, vnode.vnodeSelector, vnode.properties, vnode.children);
-            }
-        }
-        if (updated && vnode.properties && vnode.properties.updateAnimation) {
-            vnode.properties.updateAnimation(domNode, vnode.properties, previous.properties);
-        }
-        vnode.domNode = previous.domNode;
-        return textUpdated;
-    };
-    var createProjection = function (vnode, projectionOptions) {
-        return {
-            update: function (updatedVnode) {
-                if (vnode.vnodeSelector !== updatedVnode.vnodeSelector) {
-                    throw new Error('The selector for the root VNode may not be changed. (consider using dom.merge and add one extra level to the virtual DOM)');
-                }
-                updateDom(vnode, updatedVnode, projectionOptions);
-                vnode = updatedVnode;
-            },
-            domNode: vnode.domNode
-        };
-    };
-    ;
-    // The other two parameters are not added here, because the Typescript compiler creates surrogate code for desctructuring 'children'.
-    exports.h = function (selector) {
-        var properties = arguments[1];
-        if (typeof selector !== 'string') {
-            throw new Error();
-        }
-        var childIndex = 1;
-        if (properties && !properties.hasOwnProperty('vnodeSelector') && !Array.isArray(properties) && typeof properties === 'object') {
-            childIndex = 2;
-        } else {
-            // Optional properties argument was omitted
-            properties = undefined;
-        }
-        var text = undefined;
-        var children = undefined;
-        var argsLength = arguments.length;
-        // Recognize a common special case where there is only a single text node
-        if (argsLength === childIndex + 1) {
-            var onlyChild = arguments[childIndex];
-            if (typeof onlyChild === 'string') {
-                text = onlyChild;
-            } else if (onlyChild !== undefined && onlyChild.length === 1 && typeof onlyChild[0] === 'string') {
-                text = onlyChild[0];
-            }
-        }
-        if (text === undefined) {
-            children = [];
-            for (; childIndex < arguments.length; childIndex++) {
-                var child = arguments[childIndex];
-                if (child === null || child === undefined) {
-                    continue;
-                } else if (Array.isArray(child)) {
-                    appendChildren(selector, child, children);
-                } else if (child.hasOwnProperty('vnodeSelector')) {
-                    children.push(child);
-                } else {
-                    children.push(toTextVNode(child));
-                }
-            }
-        }
-        return {
-            vnodeSelector: selector,
-            properties: properties,
-            children: children,
-            text: text === '' ? undefined : text,
-            domNode: null
-        };
-    };
-    /**
- * Contains simple low-level utility functions to manipulate the real DOM.
- */
-    exports.dom = {
-        /**
-     * Creates a real DOM tree from `vnode`. The [[Projection]] object returned will contain the resulting DOM Node in
-     * its [[Projection.domNode|domNode]] property.
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
-     * objects may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the projection.
-     * @returns The [[Projection]] which also contains the DOM Node that was created.
-     */
-        create: function (vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            createDom(vnode, document.createElement('div'), undefined, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        },
-        /**
-     * Appends a new childnode to the DOM which is generated from a [[VNode]].
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param parentNode - The parent node for the new childNode.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
-     * objects may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the [[Projection]].
-     * @returns The [[Projection]] that was created.
-     */
-        append: function (parentNode, vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            createDom(vnode, parentNode, undefined, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        },
-        /**
-     * Inserts a new DOM node which is generated from a [[VNode]].
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param beforeNode - The node that the DOM Node is inserted before.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function.
-     * NOTE: [[VNode]] objects may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the projection, see [[createProjector]].
-     * @returns The [[Projection]] that was created.
-     */
-        insertBefore: function (beforeNode, vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            createDom(vnode, beforeNode.parentNode, beforeNode, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        },
-        /**
-     * Merges a new DOM node which is generated from a [[VNode]] with an existing DOM Node.
-     * This means that the virtual DOM and the real DOM will have one overlapping element.
-     * Therefore the selector for the root [[VNode]] will be ignored, but its properties and children will be applied to the Element provided.
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param domNode - The existing element to adopt as the root of the new virtual DOM. Existing attributes and childnodes are preserved.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]] objects
-     * may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the projection, see [[createProjector]].
-     * @returns The [[Projection]] that was created.
-     */
-        merge: function (element, vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            vnode.domNode = element;
-            initPropertiesAndChildren(element, vnode, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        }
-    };
-    /**
- * Creates a [[CalculationCache]] object, useful for caching [[VNode]] trees.
- * In practice, caching of [[VNode]] trees is not needed, because achieving 60 frames per second is almost never a problem.
- * For more information, see [[CalculationCache]].
- *
- * @param <Result> The type of the value that is cached.
- */
-    exports.createCache = function () {
-        var cachedInputs = undefined;
-        var cachedOutcome = undefined;
-        var result = {
-            invalidate: function () {
-                cachedOutcome = undefined;
-                cachedInputs = undefined;
-            },
-            result: function (inputs, calculation) {
-                if (cachedInputs) {
-                    for (var i = 0; i < inputs.length; i++) {
-                        if (cachedInputs[i] !== inputs[i]) {
-                            cachedOutcome = undefined;
-                        }
-                    }
-                }
-                if (!cachedOutcome) {
-                    cachedOutcome = calculation();
-                    cachedInputs = inputs;
-                }
-                return cachedOutcome;
-            }
-        };
-        return result;
-    };
-    /**
- * Creates a {@link Mapping} instance that keeps an array of result objects synchronized with an array of source objects.
- * See {@link http://maquettejs.org/docs/arrays.html|Working with arrays}.
- *
- * @param <Source>       The type of source items. A database-record for instance.
- * @param <Target>       The type of target items. A [[Component]] for instance.
- * @param getSourceKey   `function(source)` that must return a key to identify each source object. The result must either be a string or a number.
- * @param createResult   `function(source, index)` that must create a new result object from a given source. This function is identical
- *                       to the `callback` argument in `Array.map(callback)`.
- * @param updateResult   `function(source, target, index)` that updates a result to an updated source.
- */
-    exports.createMapping = function (getSourceKey, createResult, updateResult) {
-        var keys = [];
-        var results = [];
-        return {
-            results: results,
-            map: function (newSources) {
-                var newKeys = newSources.map(getSourceKey);
-                var oldTargets = results.slice();
-                var oldIndex = 0;
-                for (var i = 0; i < newSources.length; i++) {
-                    var source = newSources[i];
-                    var sourceKey = newKeys[i];
-                    if (sourceKey === keys[oldIndex]) {
-                        results[i] = oldTargets[oldIndex];
-                        updateResult(source, oldTargets[oldIndex], i);
-                        oldIndex++;
-                    } else {
-                        var found = false;
-                        for (var j = 1; j < keys.length; j++) {
-                            var searchIndex = (oldIndex + j) % keys.length;
-                            if (keys[searchIndex] === sourceKey) {
-                                results[i] = oldTargets[searchIndex];
-                                updateResult(newSources[i], oldTargets[searchIndex], i);
-                                oldIndex = searchIndex + 1;
-                                found = true;
-                                break;
-                            }
-                        }
-                        if (!found) {
-                            results[i] = createResult(source, i);
-                        }
-                    }
-                }
-                results.length = newSources.length;
-                keys = newKeys;
-            }
-        };
-    };
-    /**
- * Creates a [[Projector]] instance using the provided projectionOptions.
- *
- * For more information, see [[Projector]].
- *
- * @param projectionOptions   Options that influence how the DOM is rendered and updated.
- */
-    exports.createProjector = function (projectorOptions) {
-        var projector;
-        var projectionOptions = applyDefaultProjectionOptions(projectorOptions);
-        projectionOptions.eventHandlerInterceptor = function (propertyName, eventHandler, domNode, properties) {
-            return function () {
-                // intercept function calls (event handlers) to do a render afterwards.
-                projector.scheduleRender();
-                return eventHandler.apply(properties.bind || this, arguments);
-            };
-        };
-        var renderCompleted = true;
-        var scheduled;
-        var stopped = false;
-        var projections = [];
-        var renderFunctions = [];
-        // matches the projections array
-        var doRender = function () {
-            scheduled = undefined;
-            if (!renderCompleted) {
-                return;    // The last render threw an error, it should be logged in the browser console.
-            }
-            renderCompleted = false;
-            for (var i = 0; i < projections.length; i++) {
-                var updatedVnode = renderFunctions[i]();
-                projections[i].update(updatedVnode);
-            }
-            renderCompleted = true;
-        };
-        projector = {
-            scheduleRender: function () {
-                if (!scheduled && !stopped) {
-                    scheduled = requestAnimationFrame(doRender);
-                }
-            },
-            stop: function () {
-                if (scheduled) {
-                    cancelAnimationFrame(scheduled);
-                    scheduled = undefined;
-                }
-                stopped = true;
-            },
-            resume: function () {
-                stopped = false;
-                renderCompleted = true;
-                projector.scheduleRender();
-            },
-            append: function (parentNode, renderMaquetteFunction) {
-                projections.push(exports.dom.append(parentNode, renderMaquetteFunction(), projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            insertBefore: function (beforeNode, renderMaquetteFunction) {
-                projections.push(exports.dom.insertBefore(beforeNode, renderMaquetteFunction(), projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            merge: function (domNode, renderMaquetteFunction) {
-                projections.push(exports.dom.merge(domNode, renderMaquetteFunction(), projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            replace: function (domNode, renderMaquetteFunction) {
-                var vnode = renderMaquetteFunction();
-                createDom(vnode, domNode.parentNode, domNode, projectionOptions);
-                domNode.parentNode.removeChild(domNode);
-                projections.push(createProjection(vnode, projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            detach: function (renderMaquetteFunction) {
-                for (var i = 0; i < renderFunctions.length; i++) {
-                    if (renderFunctions[i] === renderMaquetteFunction) {
-                        renderFunctions.splice(i, 1);
-                        return projections.splice(i, 1)[0];
-                    }
-                }
-                throw new Error('renderMaquetteFunction was not found');
-            }
-        };
-        return projector;
-    };
-}));
-
-
-/* ---- utils/Animation.coffee ---- */
-
-
-(function() {
-  var Animation;
-
-  Animation = (function() {
-    function Animation() {}
-
-    Animation.prototype.slideDown = function(elem, props) {
-      var cstyle, h, margin_bottom, margin_top, padding_bottom, padding_top, transition;
-      if (elem.offsetTop > 2000) {
-        return;
-      }
-      h = elem.offsetHeight;
-      cstyle = window.getComputedStyle(elem);
-      margin_top = cstyle.marginTop;
-      margin_bottom = cstyle.marginBottom;
-      padding_top = cstyle.paddingTop;
-      padding_bottom = cstyle.paddingBottom;
-      transition = cstyle.transition;
-      elem.style.boxSizing = "border-box";
-      elem.style.overflow = "hidden";
-      elem.style.transform = "scale(0.6)";
-      elem.style.opacity = "0";
-      elem.style.height = "0px";
-      elem.style.marginTop = "0px";
-      elem.style.marginBottom = "0px";
-      elem.style.paddingTop = "0px";
-      elem.style.paddingBottom = "0px";
-      elem.style.transition = "none";
-      setTimeout((function() {
-        elem.className += " animate-inout";
-        elem.style.height = h + "px";
-        elem.style.transform = "scale(1)";
-        elem.style.opacity = "1";
-        elem.style.marginTop = margin_top;
-        elem.style.marginBottom = margin_bottom;
-        elem.style.paddingTop = padding_top;
-        return elem.style.paddingBottom = padding_bottom;
-      }), 1);
-      return elem.addEventListener("transitionend", function() {
-        elem.classList.remove("animate-inout");
-        elem.style.transition = elem.style.transform = elem.style.opacity = elem.style.height = null;
-        elem.style.boxSizing = elem.style.marginTop = elem.style.marginBottom = null;
-        elem.style.paddingTop = elem.style.paddingBottom = elem.style.overflow = null;
-        return elem.removeEventListener("transitionend", arguments.callee, false);
-      });
-    };
-
-    Animation.prototype.slideUp = function(elem, remove_func, props) {
-      if (elem.offsetTop > 1000) {
-        return remove_func();
-      }
-      elem.className += " animate-back";
-      elem.style.boxSizing = "border-box";
-      elem.style.height = elem.offsetHeight + "px";
-      elem.style.overflow = "hidden";
-      elem.style.transform = "scale(1)";
-      elem.style.opacity = "1";
-      elem.style.pointerEvents = "none";
-      setTimeout((function() {
-        elem.style.height = "0px";
-        elem.style.marginTop = "0px";
-        elem.style.marginBottom = "0px";
-        elem.style.paddingTop = "0px";
-        elem.style.paddingBottom = "0px";
-        elem.style.transform = "scale(0.8)";
-        elem.style.borderTopWidth = "0px";
-        elem.style.borderBottomWidth = "0px";
-        return elem.style.opacity = "0";
-      }), 1);
-      return elem.addEventListener("transitionend", function(e) {
-        if (e.propertyName === "opacity" || e.elapsedTime >= 0.6) {
-          elem.removeEventListener("transitionend", arguments.callee, false);
-          return remove_func();
-        }
-      });
-    };
-
-    Animation.prototype.slideUpInout = function(elem, remove_func, props) {
-      elem.className += " animate-inout";
-      elem.style.boxSizing = "border-box";
-      elem.style.height = elem.offsetHeight + "px";
-      elem.style.overflow = "hidden";
-      elem.style.transform = "scale(1)";
-      elem.style.opacity = "1";
-      elem.style.pointerEvents = "none";
-      setTimeout((function() {
-        elem.style.height = "0px";
-        elem.style.marginTop = "0px";
-        elem.style.marginBottom = "0px";
-        elem.style.paddingTop = "0px";
-        elem.style.paddingBottom = "0px";
-        elem.style.transform = "scale(0.8)";
-        elem.style.borderTopWidth = "0px";
-        elem.style.borderBottomWidth = "0px";
-        return elem.style.opacity = "0";
-      }), 1);
-      return elem.addEventListener("transitionend", function(e) {
-        if (e.propertyName === "opacity" || e.elapsedTime >= 0.6) {
-          elem.removeEventListener("transitionend", arguments.callee, false);
-          return remove_func();
-        }
-      });
-    };
-
-    Animation.prototype.showRight = function(elem, props) {
-      elem.className += " animate";
-      elem.style.opacity = 0;
-      elem.style.transform = "TranslateX(-20px) Scale(1.01)";
-      setTimeout((function() {
-        elem.style.opacity = 1;
-        return elem.style.transform = "TranslateX(0px) Scale(1)";
-      }), 1);
-      return elem.addEventListener("transitionend", function() {
-        elem.classList.remove("animate");
-        return elem.style.transform = elem.style.opacity = null;
-      });
-    };
-
-    Animation.prototype.show = function(elem, props) {
-      var delay, ref;
-      delay = ((ref = arguments[arguments.length - 2]) != null ? ref.delay : void 0) * 1000 || 1;
-      elem.style.opacity = 0;
-      setTimeout((function() {
-        return elem.className += " animate";
-      }), 1);
-      setTimeout((function() {
-        return elem.style.opacity = 1;
-      }), delay);
-      return elem.addEventListener("transitionend", function() {
-        elem.classList.remove("animate");
-        elem.style.opacity = null;
-        return elem.removeEventListener("transitionend", arguments.callee, false);
-      });
-    };
-
-    Animation.prototype.hide = function(elem, remove_func, props) {
-      var delay, ref;
-      delay = ((ref = arguments[arguments.length - 2]) != null ? ref.delay : void 0) * 1000 || 1;
-      elem.className += " animate";
-      setTimeout((function() {
-        return elem.style.opacity = 0;
-      }), delay);
-      return elem.addEventListener("transitionend", function(e) {
-        if (e.propertyName === "opacity") {
-          return remove_func();
-        }
-      });
-    };
-
-    Animation.prototype.addVisibleClass = function(elem, props) {
-      return setTimeout(function() {
-        return elem.classList.add("visible");
-      });
-    };
-
-    return Animation;
-
-  })();
-
-  window.Animation = new Animation();
-
-}).call(this);
-
-/* ---- utils/Dollar.coffee ---- */
-
-
-(function() {
-  window.$ = function(selector) {
-    if (selector.startsWith("#")) {
-      return document.getElementById(selector.replace("#", ""));
-    }
-  };
-
-}).call(this);
-
-/* ---- utils/ZeroFrame.coffee ---- */
-
-
-(function() {
-  var ZeroFrame,
-    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
-    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
-    hasProp = {}.hasOwnProperty;
-
-  ZeroFrame = (function(superClass) {
-    extend(ZeroFrame, superClass);
-
-    function ZeroFrame(url) {
-      this.onCloseWebsocket = bind(this.onCloseWebsocket, this);
-      this.onOpenWebsocket = bind(this.onOpenWebsocket, this);
-      this.onRequest = bind(this.onRequest, this);
-      this.onMessage = bind(this.onMessage, this);
-      this.url = url;
-      this.waiting_cb = {};
-      this.wrapper_nonce = document.location.href.replace(/.*wrapper_nonce=([A-Za-z0-9]+).*/, "$1");
-      this.connect();
-      this.next_message_id = 1;
-      this.history_state = {};
-      this.init();
-    }
-
-    ZeroFrame.prototype.init = function() {
-      return this;
-    };
-
-    ZeroFrame.prototype.connect = function() {
-      this.target = window.parent;
-      window.addEventListener("message", this.onMessage, false);
-      this.cmd("innerReady");
-      window.addEventListener("beforeunload", (function(_this) {
-        return function(e) {
-          _this.log("save scrollTop", window.pageYOffset);
-          _this.history_state["scrollTop"] = window.pageYOffset;
-          return _this.cmd("wrapperReplaceState", [_this.history_state, null]);
-        };
-      })(this));
-      return this.cmd("wrapperGetState", [], (function(_this) {
-        return function(state) {
-          if (state != null) {
-            _this.history_state = state;
-          }
-          _this.log("restore scrollTop", state, window.pageYOffset);
-          if (window.pageYOffset === 0 && state) {
-            return window.scroll(window.pageXOffset, state.scrollTop);
-          }
-        };
-      })(this));
-    };
-
-    ZeroFrame.prototype.onMessage = function(e) {
-      var cmd, message;
-      message = e.data;
-      cmd = message.cmd;
-      if (cmd === "response") {
-        if (this.waiting_cb[message.to] != null) {
-          return this.waiting_cb[message.to](message.result);
-        } else {
-          return this.log("Websocket callback not found:", message);
-        }
-      } else if (cmd === "wrapperReady") {
-        return this.cmd("innerReady");
-      } else if (cmd === "ping") {
-        return this.response(message.id, "pong");
-      } else if (cmd === "wrapperOpenedWebsocket") {
-        return this.onOpenWebsocket();
-      } else if (cmd === "wrapperClosedWebsocket") {
-        return this.onCloseWebsocket();
-      } else {
-        return this.onRequest(cmd, message.params);
-      }
-    };
-
-    ZeroFrame.prototype.onRequest = function(cmd, message) {
-      return this.log("Unknown request", message);
-    };
-
-    ZeroFrame.prototype.response = function(to, result) {
-      return this.send({
-        "cmd": "response",
-        "to": to,
-        "result": result
-      });
-    };
-
-    ZeroFrame.prototype.cmd = function(cmd, params, cb) {
-      if (params == null) {
-        params = {};
-      }
-      if (cb == null) {
-        cb = null;
-      }
-      return this.send({
-        "cmd": cmd,
-        "params": params
-      }, cb);
-    };
-
-    ZeroFrame.prototype.send = function(message, cb) {
-      if (cb == null) {
-        cb = null;
-      }
-      message.wrapper_nonce = this.wrapper_nonce;
-      message.id = this.next_message_id;
-      this.next_message_id += 1;
-      this.target.postMessage(message, "*");
-      if (cb) {
-        return this.waiting_cb[message.id] = cb;
-      }
-    };
-
-    ZeroFrame.prototype.onOpenWebsocket = function() {
-      return this.log("Websocket open");
-    };
-
-    ZeroFrame.prototype.onCloseWebsocket = function() {
-      return this.log("Websocket close");
-    };
-
-    return ZeroFrame;
-
-  })(Class);
-
-  window.ZeroFrame = ZeroFrame;
-
-}).call(this);
-
-/* ---- ConfigStorage.coffee ---- */
-
-
-(function() {
-  var ConfigStorage,
-    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
-    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
-    hasProp = {}.hasOwnProperty;
-
-  ConfigStorage = (function(superClass) {
-    extend(ConfigStorage, superClass);
-
-    function ConfigStorage(config) {
-      this.config = config;
-      this.createSection = bind(this.createSection, this);
-      this.items = [];
-      this.createSections();
-      this.setValues(this.config);
-    }
-
-    ConfigStorage.prototype.setValues = function(values) {
-      var i, item, len, ref, results, section;
-      ref = this.items;
-      results = [];
-      for (i = 0, len = ref.length; i < len; i++) {
-        section = ref[i];
-        results.push((function() {
-          var j, len1, ref1, results1;
-          ref1 = section.items;
-          results1 = [];
-          for (j = 0, len1 = ref1.length; j < len1; j++) {
-            item = ref1[j];
-            if (!values[item.key]) {
-              continue;
-            }
-            item.value = this.formatValue(values[item.key].value);
-            item["default"] = this.formatValue(values[item.key]["default"]);
-            item.pending = values[item.key].pending;
-            results1.push(values[item.key].item = item);
-          }
-          return results1;
-        }).call(this));
-      }
-      return results;
-    };
-
-    ConfigStorage.prototype.formatValue = function(value) {
-      if (!value) {
-        return false;
-      } else if (typeof value === "object") {
-        return value.join("\n");
-      } else if (typeof value === "number") {
-        return value.toString();
-      } else {
-        return value;
-      }
-    };
-
-    ConfigStorage.prototype.deformatValue = function(value, type) {
-      if (type === "object" && typeof value === "string") {
-        if (!value.length) {
-          return value = null;
-        } else {
-          return value.split("\n");
-        }
-      }
-      if (type === "boolean" && !value) {
-        return false;
-      } else if (type === "number") {
-        if (typeof value === "number") {
-          return value.toString();
-        } else if (!value) {
-          return "0";
-        } else {
-          return value;
-        }
-      } else {
-        return value;
-      }
-    };
-
-    ConfigStorage.prototype.createSections = function() {
-      var section;
-      section = this.createSection("Web Interface");
-      section.items.push({
-        key: "open_browser",
-        title: "Open web browser on ZeroNet startup",
-        type: "checkbox"
-      });
-      section = this.createSection("Network");
-      section.items.push({
-        key: "offline",
-        title: "Offline mode",
-        type: "checkbox",
-        description: "Disable network communication."
-      });
-      section.items.push({
-        key: "fileserver_ip_type",
-        title: "File server network",
-        type: "select",
-        options: [
-          {
-            title: "IPv4",
-            value: "ipv4"
-          }, {
-            title: "IPv6",
-            value: "ipv6"
-          }, {
-            title: "Dual (IPv4 & IPv6)",
-            value: "dual"
-          }
-        ],
-        description: "Accept incoming peers using IPv4 or IPv6 address. (default: dual)"
-      });
-      section.items.push({
-        key: "fileserver_port",
-        title: "File server port",
-        type: "text",
-        valid_pattern: /[0-9]*/,
-        description: "Other peers will use this port to reach your served sites. (default: randomize)"
-      });
-      section.items.push({
-        key: "ip_external",
-        title: "File server external ip",
-        type: "textarea",
-        placeholder: "Detect automatically",
-        description: "Your file server is accessible on these ips. (default: detect automatically)"
-      });
-      section.items.push({
-        title: "Tor",
-        key: "tor",
-        type: "select",
-        options: [
-          {
-            title: "Disable",
-            value: "disable"
-          }, {
-            title: "Enable",
-            value: "enable"
-          }, {
-            title: "Always",
-            value: "always"
-          }
-        ],
-        description: ["Disable: Don't connect to peers on Tor network", h("br"), "Enable: Only use Tor for Tor network peers", h("br"), "Always: Use Tor for every connections to hide your IP address (slower)"]
-      });
-      section.items.push({
-        title: "Use Tor bridges",
-        key: "tor_use_bridges",
-        type: "checkbox",
-        description: "Use obfuscated bridge relays to avoid network level Tor block (even slower)",
-        isHidden: function() {
-          return !Page.server_info.tor_has_meek_bridges;
-        }
-      });
-      section.items.push({
-        title: "Trackers",
-        key: "trackers",
-        type: "textarea",
-        description: "Discover new peers using these adresses"
-      });
-      section.items.push({
-        title: "Trackers files",
-        key: "trackers_file",
-        type: "textarea",
-        description: "Load additional list of torrent trackers dynamically, from a file",
-        placeholder: "Eg.: {data_dir}/trackers.json",
-        value_pos: "fullwidth"
-      });
-      section.items.push({
-        title: "Proxy for tracker connections",
-        key: "trackers_proxy",
-        type: "select",
-        options: [
-          {
-            title: "Custom",
-            value: ""
-          }, {
-            title: "Tor",
-            value: "tor"
-          }, {
-            title: "Disable",
-            value: "disable"
-          }
-        ],
-        isHidden: function() {
-          return Page.values["tor"] === "always";
-        }
-      });
-      section.items.push({
-        title: "Custom socks proxy address for trackers",
-        key: "trackers_proxy",
-        type: "text",
-        placeholder: "Eg.: 127.0.0.1:1080",
-        value_pos: "fullwidth",
-        valid_pattern: /.+:[0-9]+/,
-        isHidden: (function(_this) {
-          return function() {
-            var ref;
-            return (ref = Page.values["trackers_proxy"]) === "tor" || ref === "disable";
-          };
-        })(this)
-      });
-      section = this.createSection("Performance");
-      section.items.push({
-        key: "log_level",
-        title: "Level of logging to file",
-        type: "select",
-        options: [
-          {
-            title: "Everything",
-            value: "DEBUG"
-          }, {
-            title: "Only important messages",
-            value: "INFO"
-          }, {
-            title: "Only errors",
-            value: "ERROR"
-          }
-        ]
-      });
-      section.items.push({
-        key: "threads_fs_read",
-        title: "Threads for async file system reads",
-        type: "select",
-        options: [
-          {
-            title: "Sync read",
-            value: 0
-          }, {
-            title: "1 thread",
-            value: 1
-          }, {
-            title: "2 threads",
-            value: 2
-          }, {
-            title: "3 threads",
-            value: 3
-          }, {
-            title: "4 threads",
-            value: 4
-          }, {
-            title: "5 threads",
-            value: 5
-          }, {
-            title: "10 threads",
-            value: 10
-          }
-        ]
-      });
-      section.items.push({
-        key: "threads_fs_write",
-        title: "Threads for async file system writes",
-        type: "select",
-        options: [
-          {
-            title: "Sync write",
-            value: 0
-          }, {
-            title: "1 thread",
-            value: 1
-          }, {
-            title: "2 threads",
-            value: 2
-          }, {
-            title: "3 threads",
-            value: 3
-          }, {
-            title: "4 threads",
-            value: 4
-          }, {
-            title: "5 threads",
-            value: 5
-          }, {
-            title: "10 threads",
-            value: 10
-          }
-        ]
-      });
-      section.items.push({
-        key: "threads_crypt",
-        title: "Threads for cryptographic functions",
-        type: "select",
-        options: [
-          {
-            title: "Sync execution",
-            value: 0
-          }, {
-            title: "1 thread",
-            value: 1
-          }, {
-            title: "2 threads",
-            value: 2
-          }, {
-            title: "3 threads",
-            value: 3
-          }, {
-            title: "4 threads",
-            value: 4
-          }, {
-            title: "5 threads",
-            value: 5
-          }, {
-            title: "10 threads",
-            value: 10
-          }
-        ]
-      });
-      return section.items.push({
-        key: "threads_db",
-        title: "Threads for database operations",
-        type: "select",
-        options: [
-          {
-            title: "Sync execution",
-            value: 0
-          }, {
-            title: "1 thread",
-            value: 1
-          }, {
-            title: "2 threads",
-            value: 2
-          }, {
-            title: "3 threads",
-            value: 3
-          }, {
-            title: "4 threads",
-            value: 4
-          }, {
-            title: "5 threads",
-            value: 5
-          }, {
-            title: "10 threads",
-            value: 10
-          }
-        ]
-      });
-    };
-
-    ConfigStorage.prototype.createSection = function(title) {
-      var section;
-      section = {};
-      section.title = title;
-      section.items = [];
-      this.items.push(section);
-      return section;
-    };
-
-    return ConfigStorage;
-
-  })(Class);
-
-  window.ConfigStorage = ConfigStorage;
-
-}).call(this);
-
-
-/* ---- ConfigView.coffee ---- */
-
-
-(function() {
-  var ConfigView,
-    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
-    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
-    hasProp = {}.hasOwnProperty;
-
-  ConfigView = (function(superClass) {
-    extend(ConfigView, superClass);
-
-    function ConfigView() {
-      this.renderValueSelect = bind(this.renderValueSelect, this);
-      this.renderValueCheckbox = bind(this.renderValueCheckbox, this);
-      this.renderValueTextarea = bind(this.renderValueTextarea, this);
-      this.autosizeTextarea = bind(this.autosizeTextarea, this);
-      this.renderValueText = bind(this.renderValueText, this);
-      this.handleCheckboxChange = bind(this.handleCheckboxChange, this);
-      this.handleInputChange = bind(this.handleInputChange, this);
-      this.renderSectionItem = bind(this.renderSectionItem, this);
-      this.handleResetClick = bind(this.handleResetClick, this);
-      this.renderSection = bind(this.renderSection, this);
-      this;
-    }
-
-    ConfigView.prototype.render = function() {
-      return this.config_storage.items.map(this.renderSection);
-    };
-
-    ConfigView.prototype.renderSection = function(section) {
-      return h("div.section", {
-        key: section.title
-      }, [h("h2", section.title), h("div.config-items", section.items.map(this.renderSectionItem))]);
-    };
-
-    ConfigView.prototype.handleResetClick = function(e) {
-      var config_key, default_value, node, ref;
-      node = e.currentTarget;
-      config_key = node.attributes.config_key.value;
-      default_value = (ref = node.attributes.default_value) != null ? ref.value : void 0;
-      return Page.cmd("wrapperConfirm", ["Reset " + config_key + " value?", "Reset to default"], (function(_this) {
-        return function(res) {
-          if (res) {
-            _this.values[config_key] = default_value;
-          }
-          return Page.projector.scheduleRender();
-        };
-      })(this));
-    };
-
-    ConfigView.prototype.renderSectionItem = function(item) {
-      var marker_title, ref, value_changed, value_default, value_pos;
-      value_pos = item.value_pos;
-      if (item.type === "textarea") {
-        if (value_pos == null) {
-          value_pos = "fullwidth";
-        }
-      } else {
-        if (value_pos == null) {
-          value_pos = "right";
-        }
-      }
-      value_changed = this.config_storage.formatValue(this.values[item.key]) !== item.value;
-      value_default = this.config_storage.formatValue(this.values[item.key]) === item["default"];
-      if ((ref = item.key) === "open_browser" || ref === "fileserver_port") {
-        value_default = true;
-      }
-      marker_title = "Changed from default value: " + item["default"] + " -> " + this.values[item.key];
-      if (item.pending) {
-        marker_title += " (change pending until client restart)";
-      }
-      if (typeof item.isHidden === "function" ? item.isHidden() : void 0) {
-        return null;
-      }
-      return h("div.config-item", {
-        key: item.title,
-        enterAnimation: Animation.slideDown,
-        exitAnimation: Animation.slideUpInout
-      }, [
-        h("div.title", [h("h3", item.title), h("div.description", item.description)]), h("div.value.value-" + value_pos, item.type === "select" ? this.renderValueSelect(item) : item.type === "checkbox" ? this.renderValueCheckbox(item) : item.type === "textarea" ? this.renderValueTextarea(item) : this.renderValueText(item), h("a.marker", {
-          href: "#Reset",
-          title: marker_title,
-          onclick: this.handleResetClick,
-          config_key: item.key,
-          default_value: item["default"],
-          classes: {
-            "default": value_default,
-            changed: value_changed,
-            visible: !value_default || value_changed || item.pending,
-            pending: item.pending
-          }
-        }, "\u2022"))
-      ]);
-    };
-
-    ConfigView.prototype.handleInputChange = function(e) {
-      var config_key, node;
-      node = e.target;
-      config_key = node.attributes.config_key.value;
-      this.values[config_key] = node.value;
-      return Page.projector.scheduleRender();
-    };
-
-    ConfigView.prototype.handleCheckboxChange = function(e) {
-      var config_key, node, value;
-      node = e.currentTarget;
-      config_key = node.attributes.config_key.value;
-      value = !node.classList.contains("checked");
-      this.values[config_key] = value;
-      return Page.projector.scheduleRender();
-    };
-
-    ConfigView.prototype.renderValueText = function(item) {
-      var value;
-      value = this.values[item.key];
-      if (!value) {
-        value = "";
-      }
-      return h("input.input-" + item.type, {
-        type: item.type,
-        config_key: item.key,
-        value: value,
-        placeholder: item.placeholder,
-        oninput: this.handleInputChange
-      });
-    };
-
-    ConfigView.prototype.autosizeTextarea = function(e) {
-      var h, height_before, node, scrollh;
-      if (e.currentTarget) {
-        node = e.currentTarget;
-      } else {
-        node = e;
-      }
-      height_before = node.style.height;
-      if (height_before) {
-        node.style.height = "0px";
-      }
-      h = node.offsetHeight;
-      scrollh = node.scrollHeight + 20;
-      if (scrollh > h) {
-        return node.style.height = scrollh + "px";
-      } else {
-        return node.style.height = height_before;
-      }
-    };
-
-    ConfigView.prototype.renderValueTextarea = function(item) {
-      var value;
-      value = this.values[item.key];
-      if (!value) {
-        value = "";
-      }
-      return h("textarea.input-" + item.type + ".input-text", {
-        type: item.type,
-        config_key: item.key,
-        oninput: this.handleInputChange,
-        afterCreate: this.autosizeTextarea,
-        updateAnimation: this.autosizeTextarea,
-        value: value,
-        placeholder: item.placeholder
-      });
-    };
-
-    ConfigView.prototype.renderValueCheckbox = function(item) {
-      var checked;
-      if (this.values[item.key] && this.values[item.key] !== "False") {
-        checked = true;
-      } else {
-        checked = false;
-      }
-      return h("div.checkbox", {
-        onclick: this.handleCheckboxChange,
-        config_key: item.key,
-        classes: {
-          checked: checked
-        }
-      }, h("div.checkbox-skin"));
-    };
-
-    ConfigView.prototype.renderValueSelect = function(item) {
-      return h("select.input-select", {
-        config_key: item.key,
-        oninput: this.handleInputChange
-      }, item.options.map((function(_this) {
-        return function(option) {
-          return h("option", {
-            selected: option.value.toString() === _this.values[item.key],
-            value: option.value
-          }, option.title);
-        };
-      })(this)));
-    };
-
-    return ConfigView;
-
-  })(Class);
-
-  window.ConfigView = ConfigView;
-
-}).call(this);
-
-/* ---- UiConfig.coffee ---- */
-
-
-(function() {
-  var UiConfig,
-    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
-    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
-    hasProp = {}.hasOwnProperty;
-
-  window.h = maquette.h;
-
-  UiConfig = (function(superClass) {
-    extend(UiConfig, superClass);
-
-    function UiConfig() {
-      this.renderBottomRestart = bind(this.renderBottomRestart, this);
-      this.handleRestartClick = bind(this.handleRestartClick, this);
-      this.renderBottomSave = bind(this.renderBottomSave, this);
-      this.handleSaveClick = bind(this.handleSaveClick, this);
-      this.render = bind(this.render, this);
-      this.saveValue = bind(this.saveValue, this);
-      this.saveValues = bind(this.saveValues, this);
-      this.getValuesPending = bind(this.getValuesPending, this);
-      this.getValuesChanged = bind(this.getValuesChanged, this);
-      this.createProjector = bind(this.createProjector, this);
-      this.updateConfig = bind(this.updateConfig, this);
-      this.onOpenWebsocket = bind(this.onOpenWebsocket, this);
-      return UiConfig.__super__.constructor.apply(this, arguments);
-    }
-
-    UiConfig.prototype.init = function() {
-      this.save_visible = true;
-      this.config = null;
-      this.values = null;
-      this.config_view = new ConfigView();
-      return window.onbeforeunload = (function(_this) {
-        return function() {
-          if (_this.getValuesChanged().length > 0) {
-            return true;
-          } else {
-            return null;
-          }
-        };
-      })(this);
-    };
-
-    UiConfig.prototype.onOpenWebsocket = function() {
-      this.cmd("wrapperSetTitle", "Config - ZeroNet");
-      this.cmd("serverInfo", {}, (function(_this) {
-        return function(server_info) {
-          return _this.server_info = server_info;
-        };
-      })(this));
-      this.restart_loading = false;
-      return this.updateConfig();
-    };
-
-    UiConfig.prototype.updateConfig = function(cb) {
-      return this.cmd("configList", [], (function(_this) {
-        return function(res) {
-          var item, key, value;
-          _this.config = res;
-          _this.values = {};
-          _this.config_storage = new ConfigStorage(_this.config);
-          _this.config_view.values = _this.values;
-          _this.config_view.config_storage = _this.config_storage;
-          for (key in res) {
-            item = res[key];
-            value = item.value;
-            _this.values[key] = _this.config_storage.formatValue(value);
-          }
-          _this.projector.scheduleRender();
-          return typeof cb === "function" ? cb() : void 0;
-        };
-      })(this));
-    };
-
-    UiConfig.prototype.createProjector = function() {
-      this.projector = maquette.createProjector();
-      this.projector.replace($("#content"), this.render);
-      this.projector.replace($("#bottom-save"), this.renderBottomSave);
-      return this.projector.replace($("#bottom-restart"), this.renderBottomRestart);
-    };
-
-    UiConfig.prototype.getValuesChanged = function() {
-      var key, ref, ref1, value, values_changed;
-      values_changed = [];
-      ref = this.values;
-      for (key in ref) {
-        value = ref[key];
-        if (this.config_storage.formatValue(value) !== this.config_storage.formatValue((ref1 = this.config[key]) != null ? ref1.value : void 0)) {
-          values_changed.push({
-            key: key,
-            value: value
-          });
-        }
-      }
-      return values_changed;
-    };
-
-    UiConfig.prototype.getValuesPending = function() {
-      var item, key, ref, values_pending;
-      values_pending = [];
-      ref = this.config;
-      for (key in ref) {
-        item = ref[key];
-        if (item.pending) {
-          values_pending.push(key);
-        }
-      }
-      return values_pending;
-    };
-
-    UiConfig.prototype.saveValues = function(cb) {
-      var base, changed_values, default_value, i, item, j, last, len, match, message, results, value, value_same_as_default;
-      changed_values = this.getValuesChanged();
-      results = [];
-      for (i = j = 0, len = changed_values.length; j < len; i = ++j) {
-        item = changed_values[i];
-        last = i === changed_values.length - 1;
-        value = this.config_storage.deformatValue(item.value, typeof this.config[item.key]["default"]);
-        default_value = this.config_storage.deformatValue(this.config[item.key]["default"], typeof this.config[item.key]["default"]);
-        value_same_as_default = JSON.stringify(default_value) === JSON.stringify(value);
-        if (this.config[item.key].item.valid_pattern && !(typeof (base = this.config[item.key].item).isHidden === "function" ? base.isHidden() : void 0)) {
-          match = value.match(this.config[item.key].item.valid_pattern);
-          if (!match || match[0] !== value) {
-            message = "Invalid value of " + this.config[item.key].item.title + ": " + value + " (does not matches " + this.config[item.key].item.valid_pattern + ")";
-            Page.cmd("wrapperNotification", ["error", message]);
-            cb(false);
-            break;
-          }
-        }
-        if (value_same_as_default) {
-          value = null;
-        }
-        results.push(this.saveValue(item.key, value, last ? cb : null));
-      }
-      return results;
-    };
-
-    UiConfig.prototype.saveValue = function(key, value, cb) {
-      if (key === "open_browser") {
-        if (value) {
-          value = "default_browser";
-        } else {
-          value = "False";
-        }
-      }
-      return Page.cmd("configSet", [key, value], (function(_this) {
-        return function(res) {
-          if (res !== "ok") {
-            Page.cmd("wrapperNotification", ["error", res.error]);
-          }
-          return typeof cb === "function" ? cb(true) : void 0;
-        };
-      })(this));
-    };
-
-    UiConfig.prototype.render = function() {
-      if (!this.config) {
-        return h("div.content");
-      }
-      return h("div.content", [this.config_view.render()]);
-    };
-
-    UiConfig.prototype.handleSaveClick = function() {
-      this.save_loading = true;
-      this.logStart("Save");
-      this.saveValues((function(_this) {
-        return function(success) {
-          _this.save_loading = false;
-          _this.logEnd("Save");
-          if (success) {
-            _this.updateConfig();
-          }
-          return Page.projector.scheduleRender();
-        };
-      })(this));
-      return false;
-    };
-
-    UiConfig.prototype.renderBottomSave = function() {
-      var values_changed;
-      values_changed = this.getValuesChanged();
-      return h("div.bottom.bottom-save", {
-        classes: {
-          visible: values_changed.length
-        }
-      }, h("div.bottom-content", [
-        h("div.title", values_changed.length + " configuration item value changed"), h("a.button.button-submit.button-save", {
-          href: "#Save",
-          classes: {
-            loading: this.save_loading
-          },
-          onclick: this.handleSaveClick
-        }, "Save settings")
-      ]));
-    };
-
-    UiConfig.prototype.handleRestartClick = function() {
-      this.restart_loading = true;
-      Page.cmd("serverShutdown", {
-        restart: true
-      });
-      Page.projector.scheduleRender();
-      return false;
-    };
-
-    UiConfig.prototype.renderBottomRestart = function() {
-      var values_changed, values_pending;
-      values_pending = this.getValuesPending();
-      values_changed = this.getValuesChanged();
-      return h("div.bottom.bottom-restart", {
-        classes: {
-          visible: values_pending.length && !values_changed.length
-        }
-      }, h("div.bottom-content", [
-        h("div.title", "Some changed settings requires restart"), h("a.button.button-submit.button-restart", {
-          href: "#Restart",
-          classes: {
-            loading: this.restart_loading
-          },
-          onclick: this.handleRestartClick
-        }, "Restart ZeroNet client")
-      ]));
-    };
-
-    return UiConfig;
-
-  })(ZeroFrame);
-
-  window.Page = new UiConfig();
-
-  window.Page.createProjector();
-
-}).call(this);
diff --git a/plugins/UiConfig/media/js/lib/Class.coffee b/plugins/UiConfig/media/js/lib/Class.coffee
deleted file mode 100644
index d62ab25c..00000000
--- a/plugins/UiConfig/media/js/lib/Class.coffee
+++ /dev/null
@@ -1,23 +0,0 @@
-class Class
-	trace: true
-
-	log: (args...) ->
-		return unless @trace
-		return if typeof console is 'undefined'
-		args.unshift("[#{@.constructor.name}]")
-		console.log(args...)
-		@
-		
-	logStart: (name, args...) ->
-		return unless @trace
-		@logtimers or= {}
-		@logtimers[name] = +(new Date)
-		@log "#{name}", args..., "(started)" if args.length > 0
-		@
-		
-	logEnd: (name, args...) ->
-		ms = +(new Date)-@logtimers[name]
-		@log "#{name}", args..., "(Done in #{ms}ms)"
-		@ 
-
-window.Class = Class
\ No newline at end of file
diff --git a/plugins/UiConfig/media/js/lib/Promise.coffee b/plugins/UiConfig/media/js/lib/Promise.coffee
deleted file mode 100644
index 136e3ec7..00000000
--- a/plugins/UiConfig/media/js/lib/Promise.coffee
+++ /dev/null
@@ -1,74 +0,0 @@
-# From: http://dev.bizo.com/2011/12/promises-in-javascriptcoffeescript.html
-
-class Promise
-	@when: (tasks...) ->
-		num_uncompleted = tasks.length
-		args = new Array(num_uncompleted)
-		promise = new Promise()
-
-		for task, task_id in tasks
-			((task_id) ->
-				task.then(() ->
-					args[task_id] = Array.prototype.slice.call(arguments)
-					num_uncompleted--
-					promise.complete.apply(promise, args) if num_uncompleted == 0
-				)
-			)(task_id)
-
-		return promise
-
-	constructor: ->
-		@resolved = false
-		@end_promise = null
-		@result = null
-		@callbacks = []
-
-	resolve: ->
-		if @resolved
-			return false
-		@resolved = true
-		@data = arguments
-		if not arguments.length
-			@data = [true]
-		@result = @data[0]
-		for callback in @callbacks
-			back = callback.apply callback, @data
-		if @end_promise
-			@end_promise.resolve(back)
-
-	fail: ->
-		@resolve(false)
-
-	then: (callback) ->
-		if @resolved == true
-			callback.apply callback, @data
-			return
-
-		@callbacks.push callback
-
-		@end_promise = new Promise()
-
-window.Promise = Promise
-
-###
-s = Date.now()
-log = (text) ->
-	console.log Date.now()-s, Array.prototype.slice.call(arguments).join(", ")
-
-log "Started"
-
-cmd = (query) ->
-	p = new Promise()
-	setTimeout ( ->
-		p.resolve query+" Result"
-	), 100
-	return p
-
-back = cmd("SELECT * FROM message").then (res) ->
-	log res
-	return "Return from query"
-.then (res) ->
-	log "Back then", res
-
-log "Query started", back
-###
\ No newline at end of file
diff --git a/plugins/UiConfig/media/js/lib/Prototypes.coffee b/plugins/UiConfig/media/js/lib/Prototypes.coffee
deleted file mode 100644
index 034add50..00000000
--- a/plugins/UiConfig/media/js/lib/Prototypes.coffee
+++ /dev/null
@@ -1,8 +0,0 @@
-String::startsWith = (s) -> @[...s.length] is s
-String::endsWith = (s) -> s is '' or @[-s.length..] is s
-String::repeat = (count) -> new Array( count + 1 ).join(@)
-
-window.isEmpty = (obj) ->
-	for key of obj
-		return false
-	return true
diff --git a/plugins/UiConfig/media/js/lib/maquette.js b/plugins/UiConfig/media/js/lib/maquette.js
deleted file mode 100644
index 84d14471..00000000
--- a/plugins/UiConfig/media/js/lib/maquette.js
+++ /dev/null
@@ -1,770 +0,0 @@
-(function (root, factory) {
-    if (typeof define === 'function' && define.amd) {
-        // AMD. Register as an anonymous module.
-        define(['exports'], factory);
-    } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
-        // CommonJS
-        factory(exports);
-    } else {
-        // Browser globals
-        factory(root.maquette = {});
-    }
-}(this, function (exports) {
-    'use strict';
-    ;
-    ;
-    ;
-    ;
-    var NAMESPACE_W3 = 'http://www.w3.org/';
-    var NAMESPACE_SVG = NAMESPACE_W3 + '2000/svg';
-    var NAMESPACE_XLINK = NAMESPACE_W3 + '1999/xlink';
-    // Utilities
-    var emptyArray = [];
-    var extend = function (base, overrides) {
-        var result = {};
-        Object.keys(base).forEach(function (key) {
-            result[key] = base[key];
-        });
-        if (overrides) {
-            Object.keys(overrides).forEach(function (key) {
-                result[key] = overrides[key];
-            });
-        }
-        return result;
-    };
-    // Hyperscript helper functions
-    var same = function (vnode1, vnode2) {
-        if (vnode1.vnodeSelector !== vnode2.vnodeSelector) {
-            return false;
-        }
-        if (vnode1.properties && vnode2.properties) {
-            if (vnode1.properties.key !== vnode2.properties.key) {
-                return false;
-            }
-            return vnode1.properties.bind === vnode2.properties.bind;
-        }
-        return !vnode1.properties && !vnode2.properties;
-    };
-    var toTextVNode = function (data) {
-        return {
-            vnodeSelector: '',
-            properties: undefined,
-            children: undefined,
-            text: data.toString(),
-            domNode: null
-        };
-    };
-    var appendChildren = function (parentSelector, insertions, main) {
-        for (var i = 0; i < insertions.length; i++) {
-            var item = insertions[i];
-            if (Array.isArray(item)) {
-                appendChildren(parentSelector, item, main);
-            } else {
-                if (item !== null && item !== undefined) {
-                    if (!item.hasOwnProperty('vnodeSelector')) {
-                        item = toTextVNode(item);
-                    }
-                    main.push(item);
-                }
-            }
-        }
-    };
-    // Render helper functions
-    var missingTransition = function () {
-        throw new Error('Provide a transitions object to the projectionOptions to do animations');
-    };
-    var DEFAULT_PROJECTION_OPTIONS = {
-        namespace: undefined,
-        eventHandlerInterceptor: undefined,
-        styleApplyer: function (domNode, styleName, value) {
-            // Provides a hook to add vendor prefixes for browsers that still need it.
-            domNode.style[styleName] = value;
-        },
-        transitions: {
-            enter: missingTransition,
-            exit: missingTransition
-        }
-    };
-    var applyDefaultProjectionOptions = function (projectorOptions) {
-        return extend(DEFAULT_PROJECTION_OPTIONS, projectorOptions);
-    };
-    var checkStyleValue = function (styleValue) {
-        if (typeof styleValue !== 'string') {
-            throw new Error('Style values must be strings');
-        }
-    };
-    var setProperties = function (domNode, properties, projectionOptions) {
-        if (!properties) {
-            return;
-        }
-        var eventHandlerInterceptor = projectionOptions.eventHandlerInterceptor;
-        var propNames = Object.keys(properties);
-        var propCount = propNames.length;
-        for (var i = 0; i < propCount; i++) {
-            var propName = propNames[i];
-            /* tslint:disable:no-var-keyword: edge case */
-            var propValue = properties[propName];
-            /* tslint:enable:no-var-keyword */
-            if (propName === 'className') {
-                throw new Error('Property "className" is not supported, use "class".');
-            } else if (propName === 'class') {
-                if (domNode.className) {
-                    // May happen if classes is specified before class
-                    domNode.className += ' ' + propValue;
-                } else {
-                    domNode.className = propValue;
-                }
-            } else if (propName === 'classes') {
-                // object with string keys and boolean values
-                var classNames = Object.keys(propValue);
-                var classNameCount = classNames.length;
-                for (var j = 0; j < classNameCount; j++) {
-                    var className = classNames[j];
-                    if (propValue[className]) {
-                        domNode.classList.add(className);
-                    }
-                }
-            } else if (propName === 'styles') {
-                // object with string keys and string (!) values
-                var styleNames = Object.keys(propValue);
-                var styleCount = styleNames.length;
-                for (var j = 0; j < styleCount; j++) {
-                    var styleName = styleNames[j];
-                    var styleValue = propValue[styleName];
-                    if (styleValue) {
-                        checkStyleValue(styleValue);
-                        projectionOptions.styleApplyer(domNode, styleName, styleValue);
-                    }
-                }
-            } else if (propName === 'key') {
-                continue;
-            } else if (propValue === null || propValue === undefined) {
-                continue;
-            } else {
-                var type = typeof propValue;
-                if (type === 'function') {
-                    if (propName.lastIndexOf('on', 0) === 0) {
-                        if (eventHandlerInterceptor) {
-                            propValue = eventHandlerInterceptor(propName, propValue, domNode, properties);    // intercept eventhandlers
-                        }
-                        if (propName === 'oninput') {
-                            (function () {
-                                // record the evt.target.value, because IE and Edge sometimes do a requestAnimationFrame between changing value and running oninput
-                                var oldPropValue = propValue;
-                                propValue = function (evt) {
-                                    evt.target['oninput-value'] = evt.target.value;
-                                    // may be HTMLTextAreaElement as well
-                                    oldPropValue.apply(this, [evt]);
-                                };
-                            }());
-                        }
-                        domNode[propName] = propValue;
-                    }
-                } else if (type === 'string' && propName !== 'value' && propName !== 'innerHTML') {
-                    if (projectionOptions.namespace === NAMESPACE_SVG && propName === 'href') {
-                        domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);
-                    } else {
-                        domNode.setAttribute(propName, propValue);
-                    }
-                } else {
-                    domNode[propName] = propValue;
-                }
-            }
-        }
-    };
-    var updateProperties = function (domNode, previousProperties, properties, projectionOptions) {
-        if (!properties) {
-            return;
-        }
-        var propertiesUpdated = false;
-        var propNames = Object.keys(properties);
-        var propCount = propNames.length;
-        for (var i = 0; i < propCount; i++) {
-            var propName = propNames[i];
-            // assuming that properties will be nullified instead of missing is by design
-            var propValue = properties[propName];
-            var previousValue = previousProperties[propName];
-            if (propName === 'class') {
-                if (previousValue !== propValue) {
-                    throw new Error('"class" property may not be updated. Use the "classes" property for conditional css classes.');
-                }
-            } else if (propName === 'classes') {
-                var classList = domNode.classList;
-                var classNames = Object.keys(propValue);
-                var classNameCount = classNames.length;
-                for (var j = 0; j < classNameCount; j++) {
-                    var className = classNames[j];
-                    var on = !!propValue[className];
-                    var previousOn = !!previousValue[className];
-                    if (on === previousOn) {
-                        continue;
-                    }
-                    propertiesUpdated = true;
-                    if (on) {
-                        classList.add(className);
-                    } else {
-                        classList.remove(className);
-                    }
-                }
-            } else if (propName === 'styles') {
-                var styleNames = Object.keys(propValue);
-                var styleCount = styleNames.length;
-                for (var j = 0; j < styleCount; j++) {
-                    var styleName = styleNames[j];
-                    var newStyleValue = propValue[styleName];
-                    var oldStyleValue = previousValue[styleName];
-                    if (newStyleValue === oldStyleValue) {
-                        continue;
-                    }
-                    propertiesUpdated = true;
-                    if (newStyleValue) {
-                        checkStyleValue(newStyleValue);
-                        projectionOptions.styleApplyer(domNode, styleName, newStyleValue);
-                    } else {
-                        projectionOptions.styleApplyer(domNode, styleName, '');
-                    }
-                }
-            } else {
-                if (!propValue && typeof previousValue === 'string') {
-                    propValue = '';
-                }
-                if (propName === 'value') {
-                    if (domNode[propName] !== propValue && domNode['oninput-value'] !== propValue) {
-                        domNode[propName] = propValue;
-                        // Reset the value, even if the virtual DOM did not change
-                        domNode['oninput-value'] = undefined;
-                    }
-                    // else do not update the domNode, otherwise the cursor position would be changed
-                    if (propValue !== previousValue) {
-                        propertiesUpdated = true;
-                    }
-                } else if (propValue !== previousValue) {
-                    var type = typeof propValue;
-                    if (type === 'function') {
-                        throw new Error('Functions may not be updated on subsequent renders (property: ' + propName + '). Hint: declare event handler functions outside the render() function.');
-                    }
-                    if (type === 'string' && propName !== 'innerHTML') {
-                        if (projectionOptions.namespace === NAMESPACE_SVG && propName === 'href') {
-                            domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);
-                        } else {
-                            domNode.setAttribute(propName, propValue);
-                        }
-                    } else {
-                        if (domNode[propName] !== propValue) {
-                            domNode[propName] = propValue;
-                        }
-                    }
-                    propertiesUpdated = true;
-                }
-            }
-        }
-        return propertiesUpdated;
-    };
-    var findIndexOfChild = function (children, sameAs, start) {
-        if (sameAs.vnodeSelector !== '') {
-            // Never scan for text-nodes
-            for (var i = start; i < children.length; i++) {
-                if (same(children[i], sameAs)) {
-                    return i;
-                }
-            }
-        }
-        return -1;
-    };
-    var nodeAdded = function (vNode, transitions) {
-        if (vNode.properties) {
-            var enterAnimation = vNode.properties.enterAnimation;
-            if (enterAnimation) {
-                if (typeof enterAnimation === 'function') {
-                    enterAnimation(vNode.domNode, vNode.properties);
-                } else {
-                    transitions.enter(vNode.domNode, vNode.properties, enterAnimation);
-                }
-            }
-        }
-    };
-    var nodeToRemove = function (vNode, transitions) {
-        var domNode = vNode.domNode;
-        if (vNode.properties) {
-            var exitAnimation = vNode.properties.exitAnimation;
-            if (exitAnimation) {
-                domNode.style.pointerEvents = 'none';
-                var removeDomNode = function () {
-                    if (domNode.parentNode) {
-                        domNode.parentNode.removeChild(domNode);
-                    }
-                };
-                if (typeof exitAnimation === 'function') {
-                    exitAnimation(domNode, removeDomNode, vNode.properties);
-                    return;
-                } else {
-                    transitions.exit(vNode.domNode, vNode.properties, exitAnimation, removeDomNode);
-                    return;
-                }
-            }
-        }
-        if (domNode.parentNode) {
-            domNode.parentNode.removeChild(domNode);
-        }
-    };
-    var checkDistinguishable = function (childNodes, indexToCheck, parentVNode, operation) {
-        var childNode = childNodes[indexToCheck];
-        if (childNode.vnodeSelector === '') {
-            return;    // Text nodes need not be distinguishable
-        }
-        var properties = childNode.properties;
-        var key = properties ? properties.key === undefined ? properties.bind : properties.key : undefined;
-        if (!key) {
-            for (var i = 0; i < childNodes.length; i++) {
-                if (i !== indexToCheck) {
-                    var node = childNodes[i];
-                    if (same(node, childNode)) {
-                        if (operation === 'added') {
-                            throw new Error(parentVNode.vnodeSelector + ' had a ' + childNode.vnodeSelector + ' child ' + 'added, but there is now more than one. You must add unique key properties to make them distinguishable.');
-                        } else {
-                            throw new Error(parentVNode.vnodeSelector + ' had a ' + childNode.vnodeSelector + ' child ' + 'removed, but there were more than one. You must add unique key properties to make them distinguishable.');
-                        }
-                    }
-                }
-            }
-        }
-    };
-    var createDom;
-    var updateDom;
-    var updateChildren = function (vnode, domNode, oldChildren, newChildren, projectionOptions) {
-        if (oldChildren === newChildren) {
-            return false;
-        }
-        oldChildren = oldChildren || emptyArray;
-        newChildren = newChildren || emptyArray;
-        var oldChildrenLength = oldChildren.length;
-        var newChildrenLength = newChildren.length;
-        var transitions = projectionOptions.transitions;
-        var oldIndex = 0;
-        var newIndex = 0;
-        var i;
-        var textUpdated = false;
-        while (newIndex < newChildrenLength) {
-            var oldChild = oldIndex < oldChildrenLength ? oldChildren[oldIndex] : undefined;
-            var newChild = newChildren[newIndex];
-            if (oldChild !== undefined && same(oldChild, newChild)) {
-                textUpdated = updateDom(oldChild, newChild, projectionOptions) || textUpdated;
-                oldIndex++;
-            } else {
-                var findOldIndex = findIndexOfChild(oldChildren, newChild, oldIndex + 1);
-                if (findOldIndex >= 0) {
-                    // Remove preceding missing children
-                    for (i = oldIndex; i < findOldIndex; i++) {
-                        nodeToRemove(oldChildren[i], transitions);
-                        checkDistinguishable(oldChildren, i, vnode, 'removed');
-                    }
-                    textUpdated = updateDom(oldChildren[findOldIndex], newChild, projectionOptions) || textUpdated;
-                    oldIndex = findOldIndex + 1;
-                } else {
-                    // New child
-                    createDom(newChild, domNode, oldIndex < oldChildrenLength ? oldChildren[oldIndex].domNode : undefined, projectionOptions);
-                    nodeAdded(newChild, transitions);
-                    checkDistinguishable(newChildren, newIndex, vnode, 'added');
-                }
-            }
-            newIndex++;
-        }
-        if (oldChildrenLength > oldIndex) {
-            // Remove child fragments
-            for (i = oldIndex; i < oldChildrenLength; i++) {
-                nodeToRemove(oldChildren[i], transitions);
-                checkDistinguishable(oldChildren, i, vnode, 'removed');
-            }
-        }
-        return textUpdated;
-    };
-    var addChildren = function (domNode, children, projectionOptions) {
-        if (!children) {
-            return;
-        }
-        for (var i = 0; i < children.length; i++) {
-            createDom(children[i], domNode, undefined, projectionOptions);
-        }
-    };
-    var initPropertiesAndChildren = function (domNode, vnode, projectionOptions) {
-        addChildren(domNode, vnode.children, projectionOptions);
-        // children before properties, needed for value property of <select>.
-        if (vnode.text) {
-            domNode.textContent = vnode.text;
-        }
-        setProperties(domNode, vnode.properties, projectionOptions);
-        if (vnode.properties && vnode.properties.afterCreate) {
-            vnode.properties.afterCreate(domNode, projectionOptions, vnode.vnodeSelector, vnode.properties, vnode.children);
-        }
-    };
-    createDom = function (vnode, parentNode, insertBefore, projectionOptions) {
-        var domNode, i, c, start = 0, type, found;
-        var vnodeSelector = vnode.vnodeSelector;
-        if (vnodeSelector === '') {
-            domNode = vnode.domNode = document.createTextNode(vnode.text);
-            if (insertBefore !== undefined) {
-                parentNode.insertBefore(domNode, insertBefore);
-            } else {
-                parentNode.appendChild(domNode);
-            }
-        } else {
-            for (i = 0; i <= vnodeSelector.length; ++i) {
-                c = vnodeSelector.charAt(i);
-                if (i === vnodeSelector.length || c === '.' || c === '#') {
-                    type = vnodeSelector.charAt(start - 1);
-                    found = vnodeSelector.slice(start, i);
-                    if (type === '.') {
-                        domNode.classList.add(found);
-                    } else if (type === '#') {
-                        domNode.id = found;
-                    } else {
-                        if (found === 'svg') {
-                            projectionOptions = extend(projectionOptions, { namespace: NAMESPACE_SVG });
-                        }
-                        if (projectionOptions.namespace !== undefined) {
-                            domNode = vnode.domNode = document.createElementNS(projectionOptions.namespace, found);
-                        } else {
-                            domNode = vnode.domNode = document.createElement(found);
-                        }
-                        if (insertBefore !== undefined) {
-                            parentNode.insertBefore(domNode, insertBefore);
-                        } else {
-                            parentNode.appendChild(domNode);
-                        }
-                    }
-                    start = i + 1;
-                }
-            }
-            initPropertiesAndChildren(domNode, vnode, projectionOptions);
-        }
-    };
-    updateDom = function (previous, vnode, projectionOptions) {
-        var domNode = previous.domNode;
-        var textUpdated = false;
-        if (previous === vnode) {
-            return false;    // By contract, VNode objects may not be modified anymore after passing them to maquette
-        }
-        var updated = false;
-        if (vnode.vnodeSelector === '') {
-            if (vnode.text !== previous.text) {
-                var newVNode = document.createTextNode(vnode.text);
-                domNode.parentNode.replaceChild(newVNode, domNode);
-                vnode.domNode = newVNode;
-                textUpdated = true;
-                return textUpdated;
-            }
-        } else {
-            if (vnode.vnodeSelector.lastIndexOf('svg', 0) === 0) {
-                projectionOptions = extend(projectionOptions, { namespace: NAMESPACE_SVG });
-            }
-            if (previous.text !== vnode.text) {
-                updated = true;
-                if (vnode.text === undefined) {
-                    domNode.removeChild(domNode.firstChild);    // the only textnode presumably
-                } else {
-                    domNode.textContent = vnode.text;
-                }
-            }
-            updated = updateChildren(vnode, domNode, previous.children, vnode.children, projectionOptions) || updated;
-            updated = updateProperties(domNode, previous.properties, vnode.properties, projectionOptions) || updated;
-            if (vnode.properties && vnode.properties.afterUpdate) {
-                vnode.properties.afterUpdate(domNode, projectionOptions, vnode.vnodeSelector, vnode.properties, vnode.children);
-            }
-        }
-        if (updated && vnode.properties && vnode.properties.updateAnimation) {
-            vnode.properties.updateAnimation(domNode, vnode.properties, previous.properties);
-        }
-        vnode.domNode = previous.domNode;
-        return textUpdated;
-    };
-    var createProjection = function (vnode, projectionOptions) {
-        return {
-            update: function (updatedVnode) {
-                if (vnode.vnodeSelector !== updatedVnode.vnodeSelector) {
-                    throw new Error('The selector for the root VNode may not be changed. (consider using dom.merge and add one extra level to the virtual DOM)');
-                }
-                updateDom(vnode, updatedVnode, projectionOptions);
-                vnode = updatedVnode;
-            },
-            domNode: vnode.domNode
-        };
-    };
-    ;
-    // The other two parameters are not added here, because the Typescript compiler creates surrogate code for desctructuring 'children'.
-    exports.h = function (selector) {
-        var properties = arguments[1];
-        if (typeof selector !== 'string') {
-            throw new Error();
-        }
-        var childIndex = 1;
-        if (properties && !properties.hasOwnProperty('vnodeSelector') && !Array.isArray(properties) && typeof properties === 'object') {
-            childIndex = 2;
-        } else {
-            // Optional properties argument was omitted
-            properties = undefined;
-        }
-        var text = undefined;
-        var children = undefined;
-        var argsLength = arguments.length;
-        // Recognize a common special case where there is only a single text node
-        if (argsLength === childIndex + 1) {
-            var onlyChild = arguments[childIndex];
-            if (typeof onlyChild === 'string') {
-                text = onlyChild;
-            } else if (onlyChild !== undefined && onlyChild.length === 1 && typeof onlyChild[0] === 'string') {
-                text = onlyChild[0];
-            }
-        }
-        if (text === undefined) {
-            children = [];
-            for (; childIndex < arguments.length; childIndex++) {
-                var child = arguments[childIndex];
-                if (child === null || child === undefined) {
-                    continue;
-                } else if (Array.isArray(child)) {
-                    appendChildren(selector, child, children);
-                } else if (child.hasOwnProperty('vnodeSelector')) {
-                    children.push(child);
-                } else {
-                    children.push(toTextVNode(child));
-                }
-            }
-        }
-        return {
-            vnodeSelector: selector,
-            properties: properties,
-            children: children,
-            text: text === '' ? undefined : text,
-            domNode: null
-        };
-    };
-    /**
- * Contains simple low-level utility functions to manipulate the real DOM.
- */
-    exports.dom = {
-        /**
-     * Creates a real DOM tree from `vnode`. The [[Projection]] object returned will contain the resulting DOM Node in
-     * its [[Projection.domNode|domNode]] property.
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
-     * objects may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the projection.
-     * @returns The [[Projection]] which also contains the DOM Node that was created.
-     */
-        create: function (vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            createDom(vnode, document.createElement('div'), undefined, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        },
-        /**
-     * Appends a new childnode to the DOM which is generated from a [[VNode]].
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param parentNode - The parent node for the new childNode.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
-     * objects may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the [[Projection]].
-     * @returns The [[Projection]] that was created.
-     */
-        append: function (parentNode, vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            createDom(vnode, parentNode, undefined, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        },
-        /**
-     * Inserts a new DOM node which is generated from a [[VNode]].
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param beforeNode - The node that the DOM Node is inserted before.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function.
-     * NOTE: [[VNode]] objects may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the projection, see [[createProjector]].
-     * @returns The [[Projection]] that was created.
-     */
-        insertBefore: function (beforeNode, vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            createDom(vnode, beforeNode.parentNode, beforeNode, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        },
-        /**
-     * Merges a new DOM node which is generated from a [[VNode]] with an existing DOM Node.
-     * This means that the virtual DOM and the real DOM will have one overlapping element.
-     * Therefore the selector for the root [[VNode]] will be ignored, but its properties and children will be applied to the Element provided.
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param domNode - The existing element to adopt as the root of the new virtual DOM. Existing attributes and childnodes are preserved.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]] objects
-     * may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the projection, see [[createProjector]].
-     * @returns The [[Projection]] that was created.
-     */
-        merge: function (element, vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            vnode.domNode = element;
-            initPropertiesAndChildren(element, vnode, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        }
-    };
-    /**
- * Creates a [[CalculationCache]] object, useful for caching [[VNode]] trees.
- * In practice, caching of [[VNode]] trees is not needed, because achieving 60 frames per second is almost never a problem.
- * For more information, see [[CalculationCache]].
- *
- * @param <Result> The type of the value that is cached.
- */
-    exports.createCache = function () {
-        var cachedInputs = undefined;
-        var cachedOutcome = undefined;
-        var result = {
-            invalidate: function () {
-                cachedOutcome = undefined;
-                cachedInputs = undefined;
-            },
-            result: function (inputs, calculation) {
-                if (cachedInputs) {
-                    for (var i = 0; i < inputs.length; i++) {
-                        if (cachedInputs[i] !== inputs[i]) {
-                            cachedOutcome = undefined;
-                        }
-                    }
-                }
-                if (!cachedOutcome) {
-                    cachedOutcome = calculation();
-                    cachedInputs = inputs;
-                }
-                return cachedOutcome;
-            }
-        };
-        return result;
-    };
-    /**
- * Creates a {@link Mapping} instance that keeps an array of result objects synchronized with an array of source objects.
- * See {@link http://maquettejs.org/docs/arrays.html|Working with arrays}.
- *
- * @param <Source>       The type of source items. A database-record for instance.
- * @param <Target>       The type of target items. A [[Component]] for instance.
- * @param getSourceKey   `function(source)` that must return a key to identify each source object. The result must either be a string or a number.
- * @param createResult   `function(source, index)` that must create a new result object from a given source. This function is identical
- *                       to the `callback` argument in `Array.map(callback)`.
- * @param updateResult   `function(source, target, index)` that updates a result to an updated source.
- */
-    exports.createMapping = function (getSourceKey, createResult, updateResult) {
-        var keys = [];
-        var results = [];
-        return {
-            results: results,
-            map: function (newSources) {
-                var newKeys = newSources.map(getSourceKey);
-                var oldTargets = results.slice();
-                var oldIndex = 0;
-                for (var i = 0; i < newSources.length; i++) {
-                    var source = newSources[i];
-                    var sourceKey = newKeys[i];
-                    if (sourceKey === keys[oldIndex]) {
-                        results[i] = oldTargets[oldIndex];
-                        updateResult(source, oldTargets[oldIndex], i);
-                        oldIndex++;
-                    } else {
-                        var found = false;
-                        for (var j = 1; j < keys.length; j++) {
-                            var searchIndex = (oldIndex + j) % keys.length;
-                            if (keys[searchIndex] === sourceKey) {
-                                results[i] = oldTargets[searchIndex];
-                                updateResult(newSources[i], oldTargets[searchIndex], i);
-                                oldIndex = searchIndex + 1;
-                                found = true;
-                                break;
-                            }
-                        }
-                        if (!found) {
-                            results[i] = createResult(source, i);
-                        }
-                    }
-                }
-                results.length = newSources.length;
-                keys = newKeys;
-            }
-        };
-    };
-    /**
- * Creates a [[Projector]] instance using the provided projectionOptions.
- *
- * For more information, see [[Projector]].
- *
- * @param projectionOptions   Options that influence how the DOM is rendered and updated.
- */
-    exports.createProjector = function (projectorOptions) {
-        var projector;
-        var projectionOptions = applyDefaultProjectionOptions(projectorOptions);
-        projectionOptions.eventHandlerInterceptor = function (propertyName, eventHandler, domNode, properties) {
-            return function () {
-                // intercept function calls (event handlers) to do a render afterwards.
-                projector.scheduleRender();
-                return eventHandler.apply(properties.bind || this, arguments);
-            };
-        };
-        var renderCompleted = true;
-        var scheduled;
-        var stopped = false;
-        var projections = [];
-        var renderFunctions = [];
-        // matches the projections array
-        var doRender = function () {
-            scheduled = undefined;
-            if (!renderCompleted) {
-                return;    // The last render threw an error, it should be logged in the browser console.
-            }
-            renderCompleted = false;
-            for (var i = 0; i < projections.length; i++) {
-                var updatedVnode = renderFunctions[i]();
-                projections[i].update(updatedVnode);
-            }
-            renderCompleted = true;
-        };
-        projector = {
-            scheduleRender: function () {
-                if (!scheduled && !stopped) {
-                    scheduled = requestAnimationFrame(doRender);
-                }
-            },
-            stop: function () {
-                if (scheduled) {
-                    cancelAnimationFrame(scheduled);
-                    scheduled = undefined;
-                }
-                stopped = true;
-            },
-            resume: function () {
-                stopped = false;
-                renderCompleted = true;
-                projector.scheduleRender();
-            },
-            append: function (parentNode, renderMaquetteFunction) {
-                projections.push(exports.dom.append(parentNode, renderMaquetteFunction(), projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            insertBefore: function (beforeNode, renderMaquetteFunction) {
-                projections.push(exports.dom.insertBefore(beforeNode, renderMaquetteFunction(), projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            merge: function (domNode, renderMaquetteFunction) {
-                projections.push(exports.dom.merge(domNode, renderMaquetteFunction(), projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            replace: function (domNode, renderMaquetteFunction) {
-                var vnode = renderMaquetteFunction();
-                createDom(vnode, domNode.parentNode, domNode, projectionOptions);
-                domNode.parentNode.removeChild(domNode);
-                projections.push(createProjection(vnode, projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            detach: function (renderMaquetteFunction) {
-                for (var i = 0; i < renderFunctions.length; i++) {
-                    if (renderFunctions[i] === renderMaquetteFunction) {
-                        renderFunctions.splice(i, 1);
-                        return projections.splice(i, 1)[0];
-                    }
-                }
-                throw new Error('renderMaquetteFunction was not found');
-            }
-        };
-        return projector;
-    };
-}));
diff --git a/plugins/UiConfig/media/js/utils/Animation.coffee b/plugins/UiConfig/media/js/utils/Animation.coffee
deleted file mode 100644
index 271b88c1..00000000
--- a/plugins/UiConfig/media/js/utils/Animation.coffee
+++ /dev/null
@@ -1,138 +0,0 @@
-class Animation
-	slideDown: (elem, props) ->
-		if elem.offsetTop > 2000
-			return
-
-		h = elem.offsetHeight
-		cstyle = window.getComputedStyle(elem)
-		margin_top = cstyle.marginTop
-		margin_bottom = cstyle.marginBottom
-		padding_top = cstyle.paddingTop
-		padding_bottom = cstyle.paddingBottom
-		transition = cstyle.transition
-
-		elem.style.boxSizing = "border-box"
-		elem.style.overflow = "hidden"
-		elem.style.transform = "scale(0.6)"
-		elem.style.opacity = "0"
-		elem.style.height = "0px"
-		elem.style.marginTop = "0px"
-		elem.style.marginBottom = "0px"
-		elem.style.paddingTop = "0px"
-		elem.style.paddingBottom = "0px"
-		elem.style.transition = "none"
-
-		setTimeout (->
-			elem.className += " animate-inout"
-			elem.style.height = h+"px"
-			elem.style.transform = "scale(1)"
-			elem.style.opacity = "1"
-			elem.style.marginTop = margin_top
-			elem.style.marginBottom = margin_bottom
-			elem.style.paddingTop = padding_top
-			elem.style.paddingBottom = padding_bottom
-		), 1
-
-		elem.addEventListener "transitionend", ->
-			elem.classList.remove("animate-inout")
-			elem.style.transition = elem.style.transform = elem.style.opacity = elem.style.height = null
-			elem.style.boxSizing = elem.style.marginTop = elem.style.marginBottom = null
-			elem.style.paddingTop = elem.style.paddingBottom = elem.style.overflow = null
-			elem.removeEventListener "transitionend", arguments.callee, false
-
-
-	slideUp: (elem, remove_func, props) ->
-		if elem.offsetTop > 1000
-			return remove_func()
-
-		elem.className += " animate-back"
-		elem.style.boxSizing = "border-box"
-		elem.style.height = elem.offsetHeight+"px"
-		elem.style.overflow = "hidden"
-		elem.style.transform = "scale(1)"
-		elem.style.opacity = "1"
-		elem.style.pointerEvents = "none"
-		setTimeout (->
-			elem.style.height = "0px"
-			elem.style.marginTop = "0px"
-			elem.style.marginBottom = "0px"
-			elem.style.paddingTop = "0px"
-			elem.style.paddingBottom = "0px"
-			elem.style.transform = "scale(0.8)"
-			elem.style.borderTopWidth = "0px"
-			elem.style.borderBottomWidth = "0px"
-			elem.style.opacity = "0"
-		), 1
-		elem.addEventListener "transitionend", (e) ->
-			if e.propertyName == "opacity" or e.elapsedTime >= 0.6
-				elem.removeEventListener "transitionend", arguments.callee, false
-				remove_func()
-
-
-	slideUpInout: (elem, remove_func, props) ->
-		elem.className += " animate-inout"
-		elem.style.boxSizing = "border-box"
-		elem.style.height = elem.offsetHeight+"px"
-		elem.style.overflow = "hidden"
-		elem.style.transform = "scale(1)"
-		elem.style.opacity = "1"
-		elem.style.pointerEvents = "none"
-		setTimeout (->
-			elem.style.height = "0px"
-			elem.style.marginTop = "0px"
-			elem.style.marginBottom = "0px"
-			elem.style.paddingTop = "0px"
-			elem.style.paddingBottom = "0px"
-			elem.style.transform = "scale(0.8)"
-			elem.style.borderTopWidth = "0px"
-			elem.style.borderBottomWidth = "0px"
-			elem.style.opacity = "0"
-		), 1
-		elem.addEventListener "transitionend", (e) ->
-			if e.propertyName == "opacity" or e.elapsedTime >= 0.6
-				elem.removeEventListener "transitionend", arguments.callee, false
-				remove_func()
-
-
-	showRight: (elem, props) ->
-		elem.className += " animate"
-		elem.style.opacity = 0
-		elem.style.transform = "TranslateX(-20px) Scale(1.01)"
-		setTimeout (->
-			elem.style.opacity = 1
-			elem.style.transform = "TranslateX(0px) Scale(1)"
-		), 1
-		elem.addEventListener "transitionend", ->
-			elem.classList.remove("animate")
-			elem.style.transform = elem.style.opacity = null
-
-
-	show: (elem, props) ->
-		delay = arguments[arguments.length-2]?.delay*1000 or 1
-		elem.style.opacity = 0
-		setTimeout (->
-			elem.className += " animate"
-		), 1
-		setTimeout (->
-			elem.style.opacity = 1
-		), delay
-		elem.addEventListener "transitionend", ->
-			elem.classList.remove("animate")
-			elem.style.opacity = null
-			elem.removeEventListener "transitionend", arguments.callee, false
-
-	hide: (elem, remove_func, props) ->
-		delay = arguments[arguments.length-2]?.delay*1000 or 1
-		elem.className += " animate"
-		setTimeout (->
-			elem.style.opacity = 0
-		), delay
-		elem.addEventListener "transitionend", (e) ->
-			if e.propertyName == "opacity"
-				remove_func()
-
-	addVisibleClass: (elem, props) ->
-		setTimeout ->
-			elem.classList.add("visible")
-
-window.Animation = new Animation()
\ No newline at end of file
diff --git a/plugins/UiConfig/media/js/utils/Dollar.coffee b/plugins/UiConfig/media/js/utils/Dollar.coffee
deleted file mode 100644
index 7f19f551..00000000
--- a/plugins/UiConfig/media/js/utils/Dollar.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-window.$ = (selector) ->
-	if selector.startsWith("#")
-		return document.getElementById(selector.replace("#", ""))
diff --git a/plugins/UiConfig/media/js/utils/ZeroFrame.coffee b/plugins/UiConfig/media/js/utils/ZeroFrame.coffee
deleted file mode 100644
index 11512d16..00000000
--- a/plugins/UiConfig/media/js/utils/ZeroFrame.coffee
+++ /dev/null
@@ -1,85 +0,0 @@
-class ZeroFrame extends Class
-	constructor: (url) ->
-		@url = url
-		@waiting_cb = {}
-		@wrapper_nonce = document.location.href.replace(/.*wrapper_nonce=([A-Za-z0-9]+).*/, "$1")
-		@connect()
-		@next_message_id = 1
-		@history_state = {}
-		@init()
-
-
-	init: ->
-		@
-
-
-	connect: ->
-		@target = window.parent
-		window.addEventListener("message", @onMessage, false)
-		@cmd("innerReady")
-
-		# Save scrollTop
-		window.addEventListener "beforeunload", (e) =>
-			@log "save scrollTop", window.pageYOffset
-			@history_state["scrollTop"] = window.pageYOffset
-			@cmd "wrapperReplaceState", [@history_state, null]
-
-		# Restore scrollTop
-		@cmd "wrapperGetState", [], (state) =>
-			@history_state = state if state?
-			@log "restore scrollTop", state, window.pageYOffset
-			if window.pageYOffset == 0 and state
-				window.scroll(window.pageXOffset, state.scrollTop)
-
-
-	onMessage: (e) =>
-		message = e.data
-		cmd = message.cmd
-		if cmd == "response"
-			if @waiting_cb[message.to]?
-				@waiting_cb[message.to](message.result)
-			else
-				@log "Websocket callback not found:", message
-		else if cmd == "wrapperReady" # Wrapper inited later
-			@cmd("innerReady")
-		else if cmd == "ping"
-			@response message.id, "pong"
-		else if cmd == "wrapperOpenedWebsocket"
-			@onOpenWebsocket()
-		else if cmd == "wrapperClosedWebsocket"
-			@onCloseWebsocket()
-		else
-			@onRequest cmd, message.params
-
-
-	onRequest: (cmd, message) =>
-		@log "Unknown request", message
-
-
-	response: (to, result) ->
-		@send {"cmd": "response", "to": to, "result": result}
-
-
-	cmd: (cmd, params={}, cb=null) ->
-		@send {"cmd": cmd, "params": params}, cb
-
-
-	send: (message, cb=null) ->
-		message.wrapper_nonce = @wrapper_nonce
-		message.id = @next_message_id
-		@next_message_id += 1
-		@target.postMessage(message, "*")
-		if cb
-			@waiting_cb[message.id] = cb
-
-
-	onOpenWebsocket: =>
-		@log "Websocket open"
-
-
-	onCloseWebsocket: =>
-		@log "Websocket close"
-
-
-
-window.ZeroFrame = ZeroFrame
diff --git a/plugins/UiConfig/plugin_info.json b/plugins/UiConfig/plugin_info.json
deleted file mode 100644
index 01e9dd31..00000000
--- a/plugins/UiConfig/plugin_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"name": "UiConfig",
-	"description": "Change client settings using the web interface.",
-	"default": "enabled"
-}
\ No newline at end of file
diff --git a/plugins/UiFileManager/UiFileManagerPlugin.py b/plugins/UiFileManager/UiFileManagerPlugin.py
deleted file mode 100644
index 040e1d22..00000000
--- a/plugins/UiFileManager/UiFileManagerPlugin.py
+++ /dev/null
@@ -1,90 +0,0 @@
-import io
-import os
-import re
-import urllib
-
-from Plugin import PluginManager
-from Config import config
-from Translate import Translate
-
-plugin_dir = os.path.dirname(__file__)
-
-if "_" not in locals():
-    _ = Translate(plugin_dir + "/languages/")
-
-
-@PluginManager.registerTo("UiRequest")
-class UiFileManagerPlugin(object):
-    def actionWrapper(self, path, extra_headers=None):
-        match = re.match("/list/(.*?)(/.*|)$", path)
-        if not match:
-            return super().actionWrapper(path, extra_headers)
-
-        if not extra_headers:
-            extra_headers = {}
-
-        request_address, inner_path = match.groups()
-
-        script_nonce = self.getScriptNonce()
-
-        self.sendHeader(extra_headers=extra_headers, script_nonce=script_nonce)
-
-        site = self.server.site_manager.need(request_address)
-
-        if not site:
-            return super().actionWrapper(path, extra_headers)
-
-        request_params = urllib.parse.urlencode(
-            {"address": site.address, "site": request_address, "inner_path": inner_path.strip("/")}
-        )
-
-        is_content_loaded = "content.json" in site.content_manager.contents
-
-        return iter([super().renderWrapper(
-            site, path, "uimedia/plugins/uifilemanager/list.html?%s" % request_params,
-            "List", extra_headers, show_loadingscreen=not is_content_loaded, script_nonce=script_nonce
-        )])
-
-    def actionUiMedia(self, path, *args, **kwargs):
-        if path.startswith("/uimedia/plugins/uifilemanager/"):
-            file_path = path.replace("/uimedia/plugins/uifilemanager/", plugin_dir + "/media/")
-            if config.debug and (file_path.endswith("all.js") or file_path.endswith("all.css")):
-                # If debugging merge *.css to all.css and *.js to all.js
-                from Debug import DebugMedia
-                DebugMedia.merge(file_path)
-
-            if file_path.endswith("js"):
-                data = _.translateData(open(file_path).read(), mode="js").encode("utf8")
-            elif file_path.endswith("html"):
-                if self.get.get("address"):
-                    site = self.server.site_manager.need(self.get.get("address"))
-                    if "content.json" not in site.content_manager.contents:
-                        site.needFile("content.json")
-                data = _.translateData(open(file_path).read(), mode="html").encode("utf8")
-            else:
-                data = open(file_path, "rb").read()
-
-            return self.actionFile(file_path, file_obj=io.BytesIO(data), file_size=len(data))
-        else:
-            return super().actionUiMedia(path)
-
-    def error404(self, path=""):
-        if not path.endswith("index.html") and not path.endswith("/"):
-            return super().error404(path)
-
-        path_parts = self.parsePath(path)
-        if not path_parts:
-            return super().error404(path)
-
-        site = self.server.site_manager.get(path_parts["request_address"])
-
-        if not site or not site.content_manager.contents.get("content.json"):
-            return super().error404(path)
-
-        if path_parts["inner_path"] in site.content_manager.contents.get("content.json").get("files", {}):
-            return super().error404(path)
-
-        self.sendHeader(200)
-        path_redirect = "/list" + re.sub("^/media/", "/", path)
-        self.log.debug("Index.html not found: %s, redirecting to: %s" % (path, path_redirect))
-        return self.formatRedirect(path_redirect)
diff --git a/plugins/UiFileManager/__init__.py b/plugins/UiFileManager/__init__.py
deleted file mode 100644
index b6c2bd1a..00000000
--- a/plugins/UiFileManager/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import UiFileManagerPlugin
diff --git a/plugins/UiFileManager/languages/hu.json b/plugins/UiFileManager/languages/hu.json
deleted file mode 100644
index 5a915f9f..00000000
--- a/plugins/UiFileManager/languages/hu.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-    "New file name:": "Új fájl neve:",
-    "Delete": "Törlés",
-    "Cancel": "Mégse",
-    "Selected:": "Köjelölt:",
-    "Delete and remove optional:": "Törlés és opcionális fájl eltávolítása",
-    " files": " fájl",
-    " (modified)": " (módostott)",
-    " (new)": " (új)",
-    " (optional)": " (opcionális)",
-    " (ignored from content.json)": " (content.json-ból kihagyott)",
-    "Total: ": "Összesen: ",
-    " dir, ": " könyvtár, ",
-    " file in ": " fájl, ",
-    "+ New": "+ Új",
-    "Edit": "Módosít",
-    "View": "Megnyit",
-    "Save": "Mentés",
-    "Save: done!": "Mentés: Kész!"
-}
\ No newline at end of file
diff --git a/plugins/UiFileManager/languages/jp.json b/plugins/UiFileManager/languages/jp.json
deleted file mode 100644
index 6d874a61..00000000
--- a/plugins/UiFileManager/languages/jp.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-	"New file name:": "新しいファイルの名前:",
-	"Delete": "削除",
-	"Cancel": "キャンセル",
-	"Selected:": "選択済み: ",
-	"Delete and remove optional:": "オプションを削除",
-	" files": " ファイル",
-	" (modified)": " (編集済み)",
-	" (new)": " (新しい)",
-	" (optional)": " (オプション)",
-	" (ignored from content.json)": " (content.jsonから無視されます)",
-	"Total: ": "合計: ",
-	" dir, ": " のディレクトリ, ",
-	" file in ": " のファイル, ",
-	"+ New": "+ 新規作成",
-	"Edit": "編集",
-	"View": "閲覧",
-	"Save": "保存",
-	"Save: done!": "保存完了!"
-}
diff --git a/plugins/UiFileManager/media/codemirror/LICENSE b/plugins/UiFileManager/media/codemirror/LICENSE
deleted file mode 100644
index ff7db4b9..00000000
--- a/plugins/UiFileManager/media/codemirror/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (C) 2017 by Marijn Haverbeke <marijnh@gmail.com> and others
-
-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/plugins/UiFileManager/media/codemirror/all.css b/plugins/UiFileManager/media/codemirror/all.css
deleted file mode 100644
index 86904409..00000000
--- a/plugins/UiFileManager/media/codemirror/all.css
+++ /dev/null
@@ -1,678 +0,0 @@
-
-/* ---- base/codemirror.css ---- */
-
-
-/* BASICS */
-
-.CodeMirror {
-  /* Set height, width, borders, and global font properties here */
-  font-family: monospace;
-  height: 300px;
-  color: black;
-  direction: ltr;
-}
-
-/* PADDING */
-
-.CodeMirror-lines {
-  padding: 4px 0; /* Vertical padding around content */
-}
-.CodeMirror pre.CodeMirror-line,
-.CodeMirror pre.CodeMirror-line-like {
-  padding: 0 4px; /* Horizontal padding of content */
-}
-
-.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
-  background-color: white; /* The little square between H and V scrollbars */
-}
-
-/* GUTTER */
-
-.CodeMirror-gutters {
-  border-right: 1px solid #ddd;
-  background-color: #f7f7f7;
-  white-space: nowrap;
-}
-.CodeMirror-linenumbers {}
-.CodeMirror-linenumber {
-  padding: 0 3px 0 5px;
-  min-width: 20px;
-  text-align: right;
-  color: #999;
-  white-space: nowrap;
-}
-
-.CodeMirror-guttermarker { color: black; }
-.CodeMirror-guttermarker-subtle { color: #999; }
-
-/* CURSOR */
-
-.CodeMirror-cursor {
-  border-left: 1px solid black;
-  border-right: none;
-  width: 0;
-}
-/* Shown when moving in bi-directional text */
-.CodeMirror div.CodeMirror-secondarycursor {
-  border-left: 1px solid silver;
-}
-.cm-fat-cursor .CodeMirror-cursor {
-  width: auto;
-  border: 0 !important;
-  background: #7e7;
-}
-.cm-fat-cursor div.CodeMirror-cursors {
-  z-index: 1;
-}
-.cm-fat-cursor-mark {
-  background-color: rgba(20, 255, 20, 0.5);
-  -webkit-animation: blink 1.06s steps(1) infinite;
-  -moz-animation: blink 1.06s steps(1) infinite;
-  -webkit-animation: blink 1.06s steps(1) infinite; -moz-animation: blink 1.06s steps(1) infinite; -o-animation: blink 1.06s steps(1) infinite; -ms-animation: blink 1.06s steps(1) infinite; animation: blink 1.06s steps(1) infinite ;
-}
-.cm-animate-fat-cursor {
-  width: auto;
-  border: 0;
-  -webkit-animation: blink 1.06s steps(1) infinite;
-  -moz-animation: blink 1.06s steps(1) infinite;
-  -webkit-animation: blink 1.06s steps(1) infinite; -moz-animation: blink 1.06s steps(1) infinite; -o-animation: blink 1.06s steps(1) infinite; -ms-animation: blink 1.06s steps(1) infinite; animation: blink 1.06s steps(1) infinite ;
-  background-color: #7e7;
-}
-@-moz-keyframes blink {
-  0% {}
-  50% { background-color: transparent; }
-  100% {}
-}
-@-webkit-keyframes blink {
-  0% {}
-  50% { background-color: transparent; }
-  100% {}
-}
-@keyframes blink {
-  0% {}
-  50% { background-color: transparent; }
-  100% {}
-}
-@-webkit-keyframes blink {
-  0% {}
-  50% { background-color: transparent; }
-  100% {}
-}
-@-moz-keyframes blink {
-  0% {}
-  50% { background-color: transparent; }
-  100% {}
-}
-
-
-/* Can style cursor different in overwrite (non-insert) mode */
-.CodeMirror-overwrite .CodeMirror-cursor {}
-
-.cm-tab { display: inline-block; text-decoration: inherit; }
-
-.CodeMirror-rulers {
-  position: absolute;
-  left: 0; right: 0; top: -50px; bottom: 0;
-  overflow: hidden;
-}
-.CodeMirror-ruler {
-  border-left: 1px solid #ccc;
-  top: 0; bottom: 0;
-  position: absolute;
-}
-
-/* DEFAULT THEME */
-
-.cm-s-default .cm-header {color: blue;}
-.cm-s-default .cm-quote {color: #090;}
-.cm-negative {color: #d44;}
-.cm-positive {color: #292;}
-.cm-header, .cm-strong {font-weight: bold;}
-.cm-em {font-style: italic;}
-.cm-link {text-decoration: underline;}
-.cm-strikethrough {text-decoration: line-through;}
-
-.cm-s-default .cm-keyword {color: #708;}
-.cm-s-default .cm-atom {color: #219;}
-.cm-s-default .cm-number {color: #164;}
-.cm-s-default .cm-def {color: #00f;}
-.cm-s-default .cm-variable,
-.cm-s-default .cm-punctuation,
-.cm-s-default .cm-property,
-.cm-s-default .cm-operator {}
-.cm-s-default .cm-variable-2 {color: #05a;}
-.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
-.cm-s-default .cm-comment {color: #a50;}
-.cm-s-default .cm-string {color: #a11;}
-.cm-s-default .cm-string-2 {color: #f50;}
-.cm-s-default .cm-meta {color: #555;}
-.cm-s-default .cm-qualifier {color: #555;}
-.cm-s-default .cm-builtin {color: #30a;}
-.cm-s-default .cm-bracket {color: #997;}
-.cm-s-default .cm-tag {color: #170;}
-.cm-s-default .cm-attribute {color: #00c;}
-.cm-s-default .cm-hr {color: #999;}
-.cm-s-default .cm-link {color: #00c;}
-
-.cm-s-default .cm-error {color: #f00;}
-.cm-invalidchar {color: #f00;}
-
-.CodeMirror-composing { border-bottom: 2px solid; }
-
-/* Default styles for common addons */
-
-div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}
-div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
-.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
-.CodeMirror-activeline-background {background: #e8f2ff;}
-
-/* STOP */
-
-/* The rest of this file contains styles related to the mechanics of
-   the editor. You probably shouldn't touch them. */
-
-.CodeMirror {
-  position: relative;
-  overflow: hidden;
-  background: white;
-}
-
-.CodeMirror-scroll {
-  overflow: scroll !important; /* Things will break if this is overridden */
-  /* 50px is the magic margin used to hide the element's real scrollbars */
-  /* See overflow: hidden in .CodeMirror */
-  margin-bottom: -50px; margin-right: -50px;
-  padding-bottom: 50px;
-  height: 100%;
-  outline: none; /* Prevent dragging from highlighting the element */
-  position: relative;
-}
-.CodeMirror-sizer {
-  position: relative;
-  border-right: 50px solid transparent;
-}
-
-/* The fake, visible scrollbars. Used to force redraw during scrolling
-   before actual scrolling happens, thus preventing shaking and
-   flickering artifacts. */
-.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
-  position: absolute;
-  z-index: 6;
-  display: none;
-}
-.CodeMirror-vscrollbar {
-  right: 0; top: 0;
-  overflow-x: hidden;
-  overflow-y: scroll;
-}
-.CodeMirror-hscrollbar {
-  bottom: 0; left: 0;
-  overflow-y: hidden;
-  overflow-x: scroll;
-}
-.CodeMirror-scrollbar-filler {
-  right: 0; bottom: 0;
-}
-.CodeMirror-gutter-filler {
-  left: 0; bottom: 0;
-}
-
-.CodeMirror-gutters {
-  position: absolute; left: 0; top: 0;
-  min-height: 100%;
-  z-index: 3;
-}
-.CodeMirror-gutter {
-  white-space: normal;
-  height: 100%;
-  display: inline-block;
-  vertical-align: top;
-  margin-bottom: -50px;
-}
-.CodeMirror-gutter-wrapper {
-  position: absolute;
-  z-index: 4;
-  background: none !important;
-  border: none !important;
-}
-.CodeMirror-gutter-background {
-  position: absolute;
-  top: 0; bottom: 0;
-  z-index: 4;
-}
-.CodeMirror-gutter-elt {
-  position: absolute;
-  cursor: default;
-  z-index: 4;
-}
-.CodeMirror-gutter-wrapper ::selection { background-color: transparent }
-.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
-
-.CodeMirror-lines {
-  cursor: text;
-  min-height: 1px; /* prevents collapsing before first draw */
-}
-.CodeMirror pre.CodeMirror-line,
-.CodeMirror pre.CodeMirror-line-like {
-  /* Reset some styles that the rest of the page might have set */
-  -moz-border-radius: 0; -webkit-border-radius: 0; -webkit-border-radius: 0; -moz-border-radius: 0; -o-border-radius: 0; -ms-border-radius: 0; border-radius: 0 ;
-  border-width: 0;
-  background: transparent;
-  font-family: inherit;
-  font-size: inherit;
-  margin: 0;
-  white-space: pre;
-  word-wrap: normal;
-  line-height: inherit;
-  color: inherit;
-  z-index: 2;
-  position: relative;
-  overflow: visible;
-  -webkit-tap-highlight-color: transparent;
-  -webkit-font-variant-ligatures: contextual;
-  font-variant-ligatures: contextual;
-}
-.CodeMirror-wrap pre.CodeMirror-line,
-.CodeMirror-wrap pre.CodeMirror-line-like {
-  word-wrap: break-word;
-  white-space: pre-wrap;
-  word-break: normal;
-}
-
-.CodeMirror-linebackground {
-  position: absolute;
-  left: 0; right: 0; top: 0; bottom: 0;
-  z-index: 0;
-}
-
-.CodeMirror-linewidget {
-  position: relative;
-  z-index: 2;
-  padding: 0.1px; /* Force widget margins to stay inside of the container */
-}
-
-.CodeMirror-widget {}
-
-.CodeMirror-rtl pre { direction: rtl; }
-
-.CodeMirror-code {
-  outline: none;
-}
-
-/* Force content-box sizing for the elements where we expect it */
-.CodeMirror-scroll,
-.CodeMirror-sizer,
-.CodeMirror-gutter,
-.CodeMirror-gutters,
-.CodeMirror-linenumber {
-  -moz-box-sizing: content-box;
-  -webkit-box-sizing: content-box; -moz-box-sizing: content-box; -o-box-sizing: content-box; -ms-box-sizing: content-box; box-sizing: content-box ;
-}
-
-.CodeMirror-measure {
-  position: absolute;
-  width: 100%;
-  height: 0;
-  overflow: hidden;
-  visibility: hidden;
-}
-
-.CodeMirror-cursor {
-  position: absolute;
-  pointer-events: none;
-}
-.CodeMirror-measure pre { position: static; }
-
-div.CodeMirror-cursors {
-  visibility: hidden;
-  position: relative;
-  z-index: 3;
-}
-div.CodeMirror-dragcursors {
-  visibility: visible;
-}
-
-.CodeMirror-focused div.CodeMirror-cursors {
-  visibility: visible;
-}
-
-.CodeMirror-selected { background: #d9d9d9; }
-.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
-.CodeMirror-crosshair { cursor: crosshair; }
-.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
-.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
-
-.cm-searching {
-  background-color: #ffa;
-  background-color: rgba(255, 255, 0, .4);
-}
-
-/* Used to force a border model for a node */
-.cm-force-border { padding-right: .1px; }
-
-@media print {
-  /* Hide the cursor when printing */
-  .CodeMirror div.CodeMirror-cursors {
-    visibility: hidden;
-  }
-}
-
-/* See issue #2901 */
-.cm-tab-wrap-hack:after { content: ''; }
-
-/* Help users use markselection to safely style text background */
-span.CodeMirror-selectedtext { background: none; }
-
-
-/* ---- extension/mdn-like-custom.css ---- */
-
-
-/*
-  MDN-LIKE Theme - Mozilla
-  Ported to CodeMirror by Peter Kroon <plakroon@gmail.com>
-  Report bugs/issues here: https://github.com/codemirror/CodeMirror/issues
-  GitHub: @peterkroon
-
-  The mdn-like theme is inspired on the displayed code examples at: https://developer.mozilla.org/en-US/docs/Web/CSS/animation
-
-*/
-.cm-s-mdn-like.CodeMirror { color: #666; background-color: #fff; }
-.cm-s-mdn-like div.CodeMirror-selected { background: #fefdb5; }
-.cm-s-mdn-like .CodeMirror-line::selection, .cm-s-mdn-like .CodeMirror-line > span::selection, .cm-s-mdn-like .CodeMirror-line > span > span::selection { background: #fefdb5; }
-.cm-s-mdn-like .CodeMirror-line::-moz-selection, .cm-s-mdn-like .CodeMirror-line > span::-moz-selection, .cm-s-mdn-like .CodeMirror-line > span > span::-moz-selection { background: #fefdb5; }
-
-.cm-s-mdn-like .CodeMirror-gutters { background: #f8f8f8; color: #333; }
-.cm-s-mdn-like .CodeMirror-linenumber { color: #aaa; padding-left: 8px; }
-.cm-s-mdn-like .CodeMirror-cursor { border-left: 2px solid #222; }
-
-.cm-s-mdn-like .cm-keyword { color: #6262FF; }
-.cm-s-mdn-like .cm-atom { color: #F90; }
-.cm-s-mdn-like .cm-number { color:  #ca7841; }
-.cm-s-mdn-like .cm-def { color: #8DA6CE; }
-.cm-s-mdn-like span.cm-variable-2, .cm-s-mdn-like span.cm-tag { color: #690; }
-.cm-s-mdn-like span.cm-variable-3, .cm-s-mdn-like span.cm-def, .cm-s-mdn-like span.cm-type { color: #07a; }
-
-.cm-s-mdn-like .cm-variable { color: #07a; }
-.cm-s-mdn-like .cm-property { color: #905; }
-.cm-s-mdn-like .cm-qualifier { color: #690; }
-
-.cm-s-mdn-like .cm-operator { color: #cda869; }
-.cm-s-mdn-like .cm-comment { color:#777; font-weight:normal; }
-.cm-s-mdn-like .cm-string { color:#07a; }
-.cm-s-mdn-like .cm-string-2 { color:#bd6b18; } /*?*/
-.cm-s-mdn-like .cm-meta { color: #000; } /*?*/
-.cm-s-mdn-like .cm-builtin { color: #9B7536; } /*?*/
-.cm-s-mdn-like .cm-tag { color: #997643; }
-.cm-s-mdn-like .cm-attribute { color: #d6bb6d; } /*?*/
-.cm-s-mdn-like .cm-header { color: #FF6400; }
-.cm-s-mdn-like .cm-hr { color: #AEAEAE; }
-.cm-s-mdn-like .cm-link { color:#ad9361; font-style:italic; text-decoration:none; }
-.cm-s-mdn-like .cm-error { border-bottom: 1px solid red; }
-
-div.cm-s-mdn-like .CodeMirror-activeline-background { background: #efefff; }
-div.cm-s-mdn-like span.CodeMirror-matchingbracket { outline:1px solid grey; color: inherit; }
-
-
-/* ---- extension/dialog/dialog.css ---- */
-
-
-.CodeMirror-dialog {
-  position: absolute;
-  left: 0; right: 0;
-  background: inherit;
-  z-index: 15;
-  padding: .1em .8em;
-  overflow: hidden;
-  color: inherit;
-}
-
-.CodeMirror-dialog-top {
-  border-bottom: 1px solid #eee;
-  top: 0;
-}
-
-.CodeMirror-dialog-bottom {
-  border-top: 1px solid #eee;
-  bottom: 0;
-}
-
-.CodeMirror-dialog input {
-  border: none;
-  outline: none;
-  background: transparent;
-  width: 20em;
-  color: inherit;
-  font-family: monospace;
-}
-
-.CodeMirror-dialog button {
-  font-size: 70%;
-}
-
-
-/* ---- extension/fold/foldgutter.css ---- */
-
-
-.CodeMirror-foldmarker {
-  color: blue;
-  text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;
-  font-family: arial;
-  line-height: .3;
-  cursor: pointer;
-}
-.CodeMirror-foldgutter {
-  width: .7em;
-}
-.CodeMirror-foldgutter-open,
-.CodeMirror-foldgutter-folded {
-  cursor: pointer;
-}
-.CodeMirror-foldgutter-open:after {
-  content: "\25BE";
-}
-.CodeMirror-foldgutter-folded:after {
-  content: "\25B8";
-}
-
-
-/* ---- extension/hint/show-hint.css ---- */
-
-
-.CodeMirror-hints {
-  position: absolute;
-  z-index: 10;
-  overflow: hidden;
-  list-style: none;
-
-  margin: 0;
-  padding: 2px;
-
-  -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
-  -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
-  -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); -o-box-shadow: 2px 3px 5px rgba(0,0,0,.2); -ms-box-shadow: 2px 3px 5px rgba(0,0,0,.2); box-shadow: 2px 3px 5px rgba(0,0,0,.2) ;
-  -webkit-border-radius: 3px; -moz-border-radius: 3px; -o-border-radius: 3px; -ms-border-radius: 3px; border-radius: 3px ;
-  border: 1px solid silver;
-
-  background: white;
-  font-size: 90%;
-  font-family: monospace;
-
-  max-height: 20em;
-  overflow-y: auto;
-}
-
-.CodeMirror-hint {
-  margin: 0;
-  padding: 0 4px;
-  -webkit-border-radius: 2px; -moz-border-radius: 2px; -o-border-radius: 2px; -ms-border-radius: 2px; border-radius: 2px ;
-  white-space: pre;
-  color: black;
-  cursor: pointer;
-}
-
-li.CodeMirror-hint-active {
-  background: #08f;
-  color: white;
-}
-
-
-/* ---- extension/lint/lint.css ---- */
-
-
-/* The lint marker gutter */
-.CodeMirror-lint-markers {
-  width: 16px;
-}
-
-.CodeMirror-lint-tooltip {
-  background-color: #ffd;
-  border: 1px solid black;
-  -webkit-border-radius: 4px 4px 4px 4px; -moz-border-radius: 4px 4px 4px 4px; -o-border-radius: 4px 4px 4px 4px; -ms-border-radius: 4px 4px 4px 4px; border-radius: 4px 4px 4px 4px ;
-  color: black;
-  font-family: monospace;
-  font-size: 10pt;
-  overflow: hidden;
-  padding: 2px 5px;
-  position: fixed;
-  white-space: pre;
-  white-space: pre-wrap;
-  z-index: 100;
-  max-width: 600px;
-  opacity: 0;
-  -webkit-transition: opacity .4s; -moz-transition: opacity .4s; -o-transition: opacity .4s; -ms-transition: opacity .4s; transition: opacity .4s ;
-  -moz-transition: opacity .4s;
-  -webkit-transition: opacity .4s;
-  -o-transition: opacity .4s;
-  -ms-transition: opacity .4s;
-}
-
-.CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning {
-  background-position: left bottom;
-  background-repeat: repeat-x;
-}
-
-.CodeMirror-lint-mark-error {
-  background-image:
-  url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==")
-  ;
-}
-
-.CodeMirror-lint-mark-warning {
-  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
-}
-
-.CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning {
-  background-position: center center;
-  background-repeat: no-repeat;
-  cursor: pointer;
-  display: inline-block;
-  height: 16px;
-  width: 16px;
-  vertical-align: middle;
-  position: relative;
-}
-
-.CodeMirror-lint-message-error, .CodeMirror-lint-message-warning {
-  padding-left: 18px;
-  background-position: top left;
-  background-repeat: no-repeat;
-}
-
-.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error {
-  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=");
-}
-
-.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning {
-  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=");
-}
-
-.CodeMirror-lint-marker-multiple {
-  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC");
-  background-repeat: no-repeat;
-  background-position: right bottom;
-  width: 100%; height: 100%;
-}
-
-
-/* ---- extension/scroll/simplescrollbars.css ---- */
-
-
-.CodeMirror-simplescroll-horizontal div, .CodeMirror-simplescroll-vertical div {
-  position: absolute;
-  background: #ccc;
-  -moz-box-sizing: border-box;
-  -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ;
-  border: 1px solid #bbb;
-  -webkit-border-radius: 2px; -moz-border-radius: 2px; -o-border-radius: 2px; -ms-border-radius: 2px; border-radius: 2px ;
-}
-
-.CodeMirror-simplescroll-horizontal, .CodeMirror-simplescroll-vertical {
-  position: absolute;
-  z-index: 6;
-  background: #eee;
-}
-
-.CodeMirror-simplescroll-horizontal {
-  bottom: 0; left: 0;
-  height: 8px;
-}
-.CodeMirror-simplescroll-horizontal div {
-  bottom: 0;
-  height: 100%;
-}
-
-.CodeMirror-simplescroll-vertical {
-  right: 0; top: 0;
-  width: 8px;
-}
-.CodeMirror-simplescroll-vertical div {
-  right: 0;
-  width: 100%;
-}
-
-
-.CodeMirror-overlayscroll .CodeMirror-scrollbar-filler, .CodeMirror-overlayscroll .CodeMirror-gutter-filler {
-  display: none;
-}
-
-.CodeMirror-overlayscroll-horizontal div, .CodeMirror-overlayscroll-vertical div {
-  position: absolute;
-  background: #bcd;
-  -webkit-border-radius: 3px; -moz-border-radius: 3px; -o-border-radius: 3px; -ms-border-radius: 3px; border-radius: 3px ;
-}
-
-.CodeMirror-overlayscroll-horizontal, .CodeMirror-overlayscroll-vertical {
-  position: absolute;
-  z-index: 6;
-}
-
-.CodeMirror-overlayscroll-horizontal {
-  bottom: 0; left: 0;
-  height: 6px;
-}
-.CodeMirror-overlayscroll-horizontal div {
-  bottom: 0;
-  height: 100%;
-}
-
-.CodeMirror-overlayscroll-vertical {
-  right: 0; top: 0;
-  width: 6px;
-}
-.CodeMirror-overlayscroll-vertical div {
-  right: 0;
-  width: 100%;
-}
-
-
-/* ---- extension/search/matchesonscrollbar.css ---- */
-
-
-.CodeMirror-search-match {
-  background: gold;
-  border-top: 1px solid orange;
-  border-bottom: 1px solid orange;
-  -moz-box-sizing: border-box;
-  -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ;
-  opacity: .5;
-}
diff --git a/plugins/UiFileManager/media/codemirror/all.js b/plugins/UiFileManager/media/codemirror/all.js
deleted file mode 100644
index ef2a423a..00000000
--- a/plugins/UiFileManager/media/codemirror/all.js
+++ /dev/null
@@ -1,19964 +0,0 @@
-
-/* ---- base/codemirror.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-// This is CodeMirror (https://codemirror.net), a code editor
-// implemented in JavaScript on top of the browser's DOM.
-//
-// You can find some technical background for some of the code below
-// at http://marijnhaverbeke.nl/blog/#cm-internals .
-
-(function (global, factory) {
-  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
-  typeof define === 'function' && define.amd ? define(factory) :
-  (global = global || self, global.CodeMirror = factory());
-}(this, (function () { 'use strict';
-
-  // Kludges for bugs and behavior differences that can't be feature
-  // detected are enabled based on userAgent etc sniffing.
-  var userAgent = navigator.userAgent;
-  var platform = navigator.platform;
-
-  var gecko = /gecko\/\d/i.test(userAgent);
-  var ie_upto10 = /MSIE \d/.test(userAgent);
-  var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
-  var edge = /Edge\/(\d+)/.exec(userAgent);
-  var ie = ie_upto10 || ie_11up || edge;
-  var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);
-  var webkit = !edge && /WebKit\//.test(userAgent);
-  var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
-  var chrome = !edge && /Chrome\//.test(userAgent);
-  var presto = /Opera\//.test(userAgent);
-  var safari = /Apple Computer/.test(navigator.vendor);
-  var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
-  var phantom = /PhantomJS/.test(userAgent);
-
-  var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
-  var android = /Android/.test(userAgent);
-  // This is woefully incomplete. Suggestions for alternative methods welcome.
-  var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
-  var mac = ios || /Mac/.test(platform);
-  var chromeOS = /\bCrOS\b/.test(userAgent);
-  var windows = /win/i.test(platform);
-
-  var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
-  if (presto_version) { presto_version = Number(presto_version[1]); }
-  if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
-  // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
-  var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
-  var captureRightClick = gecko || (ie && ie_version >= 9);
-
-  function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
-
-  var rmClass = function(node, cls) {
-    var current = node.className;
-    var match = classTest(cls).exec(current);
-    if (match) {
-      var after = current.slice(match.index + match[0].length);
-      node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
-    }
-  };
-
-  function removeChildren(e) {
-    for (var count = e.childNodes.length; count > 0; --count)
-      { e.removeChild(e.firstChild); }
-    return e
-  }
-
-  function removeChildrenAndAdd(parent, e) {
-    return removeChildren(parent).appendChild(e)
-  }
-
-  function elt(tag, content, className, style) {
-    var e = document.createElement(tag);
-    if (className) { e.className = className; }
-    if (style) { e.style.cssText = style; }
-    if (typeof content == "string") { e.appendChild(document.createTextNode(content)); }
-    else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }
-    return e
-  }
-  // wrapper for elt, which removes the elt from the accessibility tree
-  function eltP(tag, content, className, style) {
-    var e = elt(tag, content, className, style);
-    e.setAttribute("role", "presentation");
-    return e
-  }
-
-  var range;
-  if (document.createRange) { range = function(node, start, end, endNode) {
-    var r = document.createRange();
-    r.setEnd(endNode || node, end);
-    r.setStart(node, start);
-    return r
-  }; }
-  else { range = function(node, start, end) {
-    var r = document.body.createTextRange();
-    try { r.moveToElementText(node.parentNode); }
-    catch(e) { return r }
-    r.collapse(true);
-    r.moveEnd("character", end);
-    r.moveStart("character", start);
-    return r
-  }; }
-
-  function contains(parent, child) {
-    if (child.nodeType == 3) // Android browser always returns false when child is a textnode
-      { child = child.parentNode; }
-    if (parent.contains)
-      { return parent.contains(child) }
-    do {
-      if (child.nodeType == 11) { child = child.host; }
-      if (child == parent) { return true }
-    } while (child = child.parentNode)
-  }
-
-  function activeElt() {
-    // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
-    // IE < 10 will throw when accessed while the page is loading or in an iframe.
-    // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
-    var activeElement;
-    try {
-      activeElement = document.activeElement;
-    } catch(e) {
-      activeElement = document.body || null;
-    }
-    while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
-      { activeElement = activeElement.shadowRoot.activeElement; }
-    return activeElement
-  }
-
-  function addClass(node, cls) {
-    var current = node.className;
-    if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; }
-  }
-  function joinClasses(a, b) {
-    var as = a.split(" ");
-    for (var i = 0; i < as.length; i++)
-      { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } }
-    return b
-  }
-
-  var selectInput = function(node) { node.select(); };
-  if (ios) // Mobile Safari apparently has a bug where select() is broken.
-    { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }
-  else if (ie) // Suppress mysterious IE10 errors
-    { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }
-
-  function bind(f) {
-    var args = Array.prototype.slice.call(arguments, 1);
-    return function(){return f.apply(null, args)}
-  }
-
-  function copyObj(obj, target, overwrite) {
-    if (!target) { target = {}; }
-    for (var prop in obj)
-      { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
-        { target[prop] = obj[prop]; } }
-    return target
-  }
-
-  // Counts the column offset in a string, taking tabs into account.
-  // Used mostly to find indentation.
-  function countColumn(string, end, tabSize, startIndex, startValue) {
-    if (end == null) {
-      end = string.search(/[^\s\u00a0]/);
-      if (end == -1) { end = string.length; }
-    }
-    for (var i = startIndex || 0, n = startValue || 0;;) {
-      var nextTab = string.indexOf("\t", i);
-      if (nextTab < 0 || nextTab >= end)
-        { return n + (end - i) }
-      n += nextTab - i;
-      n += tabSize - (n % tabSize);
-      i = nextTab + 1;
-    }
-  }
-
-  var Delayed = function() {
-    this.id = null;
-    this.f = null;
-    this.time = 0;
-    this.handler = bind(this.onTimeout, this);
-  };
-  Delayed.prototype.onTimeout = function (self) {
-    self.id = 0;
-    if (self.time <= +new Date) {
-      self.f();
-    } else {
-      setTimeout(self.handler, self.time - +new Date);
-    }
-  };
-  Delayed.prototype.set = function (ms, f) {
-    this.f = f;
-    var time = +new Date + ms;
-    if (!this.id || time < this.time) {
-      clearTimeout(this.id);
-      this.id = setTimeout(this.handler, ms);
-      this.time = time;
-    }
-  };
-
-  function indexOf(array, elt) {
-    for (var i = 0; i < array.length; ++i)
-      { if (array[i] == elt) { return i } }
-    return -1
-  }
-
-  // Number of pixels added to scroller and sizer to hide scrollbar
-  var scrollerGap = 50;
-
-  // Returned or thrown by various protocols to signal 'I'm not
-  // handling this'.
-  var Pass = {toString: function(){return "CodeMirror.Pass"}};
-
-  // Reused option objects for setSelection & friends
-  var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
-
-  // The inverse of countColumn -- find the offset that corresponds to
-  // a particular column.
-  function findColumn(string, goal, tabSize) {
-    for (var pos = 0, col = 0;;) {
-      var nextTab = string.indexOf("\t", pos);
-      if (nextTab == -1) { nextTab = string.length; }
-      var skipped = nextTab - pos;
-      if (nextTab == string.length || col + skipped >= goal)
-        { return pos + Math.min(skipped, goal - col) }
-      col += nextTab - pos;
-      col += tabSize - (col % tabSize);
-      pos = nextTab + 1;
-      if (col >= goal) { return pos }
-    }
-  }
-
-  var spaceStrs = [""];
-  function spaceStr(n) {
-    while (spaceStrs.length <= n)
-      { spaceStrs.push(lst(spaceStrs) + " "); }
-    return spaceStrs[n]
-  }
-
-  function lst(arr) { return arr[arr.length-1] }
-
-  function map(array, f) {
-    var out = [];
-    for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }
-    return out
-  }
-
-  function insertSorted(array, value, score) {
-    var pos = 0, priority = score(value);
-    while (pos < array.length && score(array[pos]) <= priority) { pos++; }
-    array.splice(pos, 0, value);
-  }
-
-  function nothing() {}
-
-  function createObj(base, props) {
-    var inst;
-    if (Object.create) {
-      inst = Object.create(base);
-    } else {
-      nothing.prototype = base;
-      inst = new nothing();
-    }
-    if (props) { copyObj(props, inst); }
-    return inst
-  }
-
-  var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
-  function isWordCharBasic(ch) {
-    return /\w/.test(ch) || ch > "\x80" &&
-      (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
-  }
-  function isWordChar(ch, helper) {
-    if (!helper) { return isWordCharBasic(ch) }
-    if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
-    return helper.test(ch)
-  }
-
-  function isEmpty(obj) {
-    for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
-    return true
-  }
-
-  // Extending unicode characters. A series of a non-extending char +
-  // any number of extending chars is treated as a single unit as far
-  // as editing and measuring is concerned. This is not fully correct,
-  // since some scripts/fonts/browsers also treat other configurations
-  // of code points as a group.
-  var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;
-  function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
-
-  // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
-  function skipExtendingChars(str, pos, dir) {
-    while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }
-    return pos
-  }
-
-  // Returns the value from the range [`from`; `to`] that satisfies
-  // `pred` and is closest to `from`. Assumes that at least `to`
-  // satisfies `pred`. Supports `from` being greater than `to`.
-  function findFirst(pred, from, to) {
-    // At any point we are certain `to` satisfies `pred`, don't know
-    // whether `from` does.
-    var dir = from > to ? -1 : 1;
-    for (;;) {
-      if (from == to) { return from }
-      var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);
-      if (mid == from) { return pred(mid) ? from : to }
-      if (pred(mid)) { to = mid; }
-      else { from = mid + dir; }
-    }
-  }
-
-  // BIDI HELPERS
-
-  function iterateBidiSections(order, from, to, f) {
-    if (!order) { return f(from, to, "ltr", 0) }
-    var found = false;
-    for (var i = 0; i < order.length; ++i) {
-      var part = order[i];
-      if (part.from < to && part.to > from || from == to && part.to == from) {
-        f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i);
-        found = true;
-      }
-    }
-    if (!found) { f(from, to, "ltr"); }
-  }
-
-  var bidiOther = null;
-  function getBidiPartAt(order, ch, sticky) {
-    var found;
-    bidiOther = null;
-    for (var i = 0; i < order.length; ++i) {
-      var cur = order[i];
-      if (cur.from < ch && cur.to > ch) { return i }
-      if (cur.to == ch) {
-        if (cur.from != cur.to && sticky == "before") { found = i; }
-        else { bidiOther = i; }
-      }
-      if (cur.from == ch) {
-        if (cur.from != cur.to && sticky != "before") { found = i; }
-        else { bidiOther = i; }
-      }
-    }
-    return found != null ? found : bidiOther
-  }
-
-  // Bidirectional ordering algorithm
-  // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
-  // that this (partially) implements.
-
-  // One-char codes used for character types:
-  // L (L):   Left-to-Right
-  // R (R):   Right-to-Left
-  // r (AL):  Right-to-Left Arabic
-  // 1 (EN):  European Number
-  // + (ES):  European Number Separator
-  // % (ET):  European Number Terminator
-  // n (AN):  Arabic Number
-  // , (CS):  Common Number Separator
-  // m (NSM): Non-Spacing Mark
-  // b (BN):  Boundary Neutral
-  // s (B):   Paragraph Separator
-  // t (S):   Segment Separator
-  // w (WS):  Whitespace
-  // N (ON):  Other Neutrals
-
-  // Returns null if characters are ordered as they appear
-  // (left-to-right), or an array of sections ({from, to, level}
-  // objects) in the order in which they occur visually.
-  var bidiOrdering = (function() {
-    // Character types for codepoints 0 to 0xff
-    var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
-    // Character types for codepoints 0x600 to 0x6f9
-    var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";
-    function charType(code) {
-      if (code <= 0xf7) { return lowTypes.charAt(code) }
-      else if (0x590 <= code && code <= 0x5f4) { return "R" }
-      else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
-      else if (0x6ee <= code && code <= 0x8ac) { return "r" }
-      else if (0x2000 <= code && code <= 0x200b) { return "w" }
-      else if (code == 0x200c) { return "b" }
-      else { return "L" }
-    }
-
-    var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
-    var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
-
-    function BidiSpan(level, from, to) {
-      this.level = level;
-      this.from = from; this.to = to;
-    }
-
-    return function(str, direction) {
-      var outerType = direction == "ltr" ? "L" : "R";
-
-      if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
-      var len = str.length, types = [];
-      for (var i = 0; i < len; ++i)
-        { types.push(charType(str.charCodeAt(i))); }
-
-      // W1. Examine each non-spacing mark (NSM) in the level run, and
-      // change the type of the NSM to the type of the previous
-      // character. If the NSM is at the start of the level run, it will
-      // get the type of sor.
-      for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
-        var type = types[i$1];
-        if (type == "m") { types[i$1] = prev; }
-        else { prev = type; }
-      }
-
-      // W2. Search backwards from each instance of a European number
-      // until the first strong type (R, L, AL, or sor) is found. If an
-      // AL is found, change the type of the European number to Arabic
-      // number.
-      // W3. Change all ALs to R.
-      for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
-        var type$1 = types[i$2];
-        if (type$1 == "1" && cur == "r") { types[i$2] = "n"; }
-        else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } }
-      }
-
-      // W4. A single European separator between two European numbers
-      // changes to a European number. A single common separator between
-      // two numbers of the same type changes to that type.
-      for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
-        var type$2 = types[i$3];
-        if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; }
-        else if (type$2 == "," && prev$1 == types[i$3+1] &&
-                 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; }
-        prev$1 = type$2;
-      }
-
-      // W5. A sequence of European terminators adjacent to European
-      // numbers changes to all European numbers.
-      // W6. Otherwise, separators and terminators change to Other
-      // Neutral.
-      for (var i$4 = 0; i$4 < len; ++i$4) {
-        var type$3 = types[i$4];
-        if (type$3 == ",") { types[i$4] = "N"; }
-        else if (type$3 == "%") {
-          var end = (void 0);
-          for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
-          var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
-          for (var j = i$4; j < end; ++j) { types[j] = replace; }
-          i$4 = end - 1;
-        }
-      }
-
-      // W7. Search backwards from each instance of a European number
-      // until the first strong type (R, L, or sor) is found. If an L is
-      // found, then change the type of the European number to L.
-      for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
-        var type$4 = types[i$5];
-        if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; }
-        else if (isStrong.test(type$4)) { cur$1 = type$4; }
-      }
-
-      // N1. A sequence of neutrals takes the direction of the
-      // surrounding strong text if the text on both sides has the same
-      // direction. European and Arabic numbers act as if they were R in
-      // terms of their influence on neutrals. Start-of-level-run (sor)
-      // and end-of-level-run (eor) are used at level run boundaries.
-      // N2. Any remaining neutrals take the embedding direction.
-      for (var i$6 = 0; i$6 < len; ++i$6) {
-        if (isNeutral.test(types[i$6])) {
-          var end$1 = (void 0);
-          for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
-          var before = (i$6 ? types[i$6-1] : outerType) == "L";
-          var after = (end$1 < len ? types[end$1] : outerType) == "L";
-          var replace$1 = before == after ? (before ? "L" : "R") : outerType;
-          for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }
-          i$6 = end$1 - 1;
-        }
-      }
-
-      // Here we depart from the documented algorithm, in order to avoid
-      // building up an actual levels array. Since there are only three
-      // levels (0, 1, 2) in an implementation that doesn't take
-      // explicit embedding into account, we can build up the order on
-      // the fly, without following the level-based algorithm.
-      var order = [], m;
-      for (var i$7 = 0; i$7 < len;) {
-        if (countsAsLeft.test(types[i$7])) {
-          var start = i$7;
-          for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
-          order.push(new BidiSpan(0, start, i$7));
-        } else {
-          var pos = i$7, at = order.length, isRTL = direction == "rtl" ? 1 : 0;
-          for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
-          for (var j$2 = pos; j$2 < i$7;) {
-            if (countsAsNum.test(types[j$2])) {
-              if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); at += isRTL; }
-              var nstart = j$2;
-              for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
-              order.splice(at, 0, new BidiSpan(2, nstart, j$2));
-              at += isRTL;
-              pos = j$2;
-            } else { ++j$2; }
-          }
-          if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }
-        }
-      }
-      if (direction == "ltr") {
-        if (order[0].level == 1 && (m = str.match(/^\s+/))) {
-          order[0].from = m[0].length;
-          order.unshift(new BidiSpan(0, 0, m[0].length));
-        }
-        if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
-          lst(order).to -= m[0].length;
-          order.push(new BidiSpan(0, len - m[0].length, len));
-        }
-      }
-
-      return direction == "rtl" ? order.reverse() : order
-    }
-  })();
-
-  // Get the bidi ordering for the given line (and cache it). Returns
-  // false for lines that are fully left-to-right, and an array of
-  // BidiSpan objects otherwise.
-  function getOrder(line, direction) {
-    var order = line.order;
-    if (order == null) { order = line.order = bidiOrdering(line.text, direction); }
-    return order
-  }
-
-  // EVENT HANDLING
-
-  // Lightweight event framework. on/off also work on DOM nodes,
-  // registering native DOM handlers.
-
-  var noHandlers = [];
-
-  var on = function(emitter, type, f) {
-    if (emitter.addEventListener) {
-      emitter.addEventListener(type, f, false);
-    } else if (emitter.attachEvent) {
-      emitter.attachEvent("on" + type, f);
-    } else {
-      var map = emitter._handlers || (emitter._handlers = {});
-      map[type] = (map[type] || noHandlers).concat(f);
-    }
-  };
-
-  function getHandlers(emitter, type) {
-    return emitter._handlers && emitter._handlers[type] || noHandlers
-  }
-
-  function off(emitter, type, f) {
-    if (emitter.removeEventListener) {
-      emitter.removeEventListener(type, f, false);
-    } else if (emitter.detachEvent) {
-      emitter.detachEvent("on" + type, f);
-    } else {
-      var map = emitter._handlers, arr = map && map[type];
-      if (arr) {
-        var index = indexOf(arr, f);
-        if (index > -1)
-          { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }
-      }
-    }
-  }
-
-  function signal(emitter, type /*, values...*/) {
-    var handlers = getHandlers(emitter, type);
-    if (!handlers.length) { return }
-    var args = Array.prototype.slice.call(arguments, 2);
-    for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }
-  }
-
-  // The DOM events that CodeMirror handles can be overridden by
-  // registering a (non-DOM) handler on the editor for the event name,
-  // and preventDefault-ing the event in that handler.
-  function signalDOMEvent(cm, e, override) {
-    if (typeof e == "string")
-      { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }
-    signal(cm, override || e.type, cm, e);
-    return e_defaultPrevented(e) || e.codemirrorIgnore
-  }
-
-  function signalCursorActivity(cm) {
-    var arr = cm._handlers && cm._handlers.cursorActivity;
-    if (!arr) { return }
-    var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
-    for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
-      { set.push(arr[i]); } }
-  }
-
-  function hasHandler(emitter, type) {
-    return getHandlers(emitter, type).length > 0
-  }
-
-  // Add on and off methods to a constructor's prototype, to make
-  // registering events on such objects more convenient.
-  function eventMixin(ctor) {
-    ctor.prototype.on = function(type, f) {on(this, type, f);};
-    ctor.prototype.off = function(type, f) {off(this, type, f);};
-  }
-
-  // Due to the fact that we still support jurassic IE versions, some
-  // compatibility wrappers are needed.
-
-  function e_preventDefault(e) {
-    if (e.preventDefault) { e.preventDefault(); }
-    else { e.returnValue = false; }
-  }
-  function e_stopPropagation(e) {
-    if (e.stopPropagation) { e.stopPropagation(); }
-    else { e.cancelBubble = true; }
-  }
-  function e_defaultPrevented(e) {
-    return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
-  }
-  function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
-
-  function e_target(e) {return e.target || e.srcElement}
-  function e_button(e) {
-    var b = e.which;
-    if (b == null) {
-      if (e.button & 1) { b = 1; }
-      else if (e.button & 2) { b = 3; }
-      else if (e.button & 4) { b = 2; }
-    }
-    if (mac && e.ctrlKey && b == 1) { b = 3; }
-    return b
-  }
-
-  // Detect drag-and-drop
-  var dragAndDrop = function() {
-    // There is *some* kind of drag-and-drop support in IE6-8, but I
-    // couldn't get it to work yet.
-    if (ie && ie_version < 9) { return false }
-    var div = elt('div');
-    return "draggable" in div || "dragDrop" in div
-  }();
-
-  var zwspSupported;
-  function zeroWidthElement(measure) {
-    if (zwspSupported == null) {
-      var test = elt("span", "\u200b");
-      removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
-      if (measure.firstChild.offsetHeight != 0)
-        { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }
-    }
-    var node = zwspSupported ? elt("span", "\u200b") :
-      elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
-    node.setAttribute("cm-text", "");
-    return node
-  }
-
-  // Feature-detect IE's crummy client rect reporting for bidi text
-  var badBidiRects;
-  function hasBadBidiRects(measure) {
-    if (badBidiRects != null) { return badBidiRects }
-    var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
-    var r0 = range(txt, 0, 1).getBoundingClientRect();
-    var r1 = range(txt, 1, 2).getBoundingClientRect();
-    removeChildren(measure);
-    if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
-    return badBidiRects = (r1.right - r0.right < 3)
-  }
-
-  // See if "".split is the broken IE version, if so, provide an
-  // alternative way to split lines.
-  var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
-    var pos = 0, result = [], l = string.length;
-    while (pos <= l) {
-      var nl = string.indexOf("\n", pos);
-      if (nl == -1) { nl = string.length; }
-      var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
-      var rt = line.indexOf("\r");
-      if (rt != -1) {
-        result.push(line.slice(0, rt));
-        pos += rt + 1;
-      } else {
-        result.push(line);
-        pos = nl + 1;
-      }
-    }
-    return result
-  } : function (string) { return string.split(/\r\n?|\n/); };
-
-  var hasSelection = window.getSelection ? function (te) {
-    try { return te.selectionStart != te.selectionEnd }
-    catch(e) { return false }
-  } : function (te) {
-    var range;
-    try {range = te.ownerDocument.selection.createRange();}
-    catch(e) {}
-    if (!range || range.parentElement() != te) { return false }
-    return range.compareEndPoints("StartToEnd", range) != 0
-  };
-
-  var hasCopyEvent = (function () {
-    var e = elt("div");
-    if ("oncopy" in e) { return true }
-    e.setAttribute("oncopy", "return;");
-    return typeof e.oncopy == "function"
-  })();
-
-  var badZoomedRects = null;
-  function hasBadZoomedRects(measure) {
-    if (badZoomedRects != null) { return badZoomedRects }
-    var node = removeChildrenAndAdd(measure, elt("span", "x"));
-    var normal = node.getBoundingClientRect();
-    var fromRange = range(node, 0, 1).getBoundingClientRect();
-    return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
-  }
-
-  // Known modes, by name and by MIME
-  var modes = {}, mimeModes = {};
-
-  // Extra arguments are stored as the mode's dependencies, which is
-  // used by (legacy) mechanisms like loadmode.js to automatically
-  // load a mode. (Preferred mechanism is the require/define calls.)
-  function defineMode(name, mode) {
-    if (arguments.length > 2)
-      { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
-    modes[name] = mode;
-  }
-
-  function defineMIME(mime, spec) {
-    mimeModes[mime] = spec;
-  }
-
-  // Given a MIME type, a {name, ...options} config object, or a name
-  // string, return a mode config object.
-  function resolveMode(spec) {
-    if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
-      spec = mimeModes[spec];
-    } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
-      var found = mimeModes[spec.name];
-      if (typeof found == "string") { found = {name: found}; }
-      spec = createObj(found, spec);
-      spec.name = found.name;
-    } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
-      return resolveMode("application/xml")
-    } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
-      return resolveMode("application/json")
-    }
-    if (typeof spec == "string") { return {name: spec} }
-    else { return spec || {name: "null"} }
-  }
-
-  // Given a mode spec (anything that resolveMode accepts), find and
-  // initialize an actual mode object.
-  function getMode(options, spec) {
-    spec = resolveMode(spec);
-    var mfactory = modes[spec.name];
-    if (!mfactory) { return getMode(options, "text/plain") }
-    var modeObj = mfactory(options, spec);
-    if (modeExtensions.hasOwnProperty(spec.name)) {
-      var exts = modeExtensions[spec.name];
-      for (var prop in exts) {
-        if (!exts.hasOwnProperty(prop)) { continue }
-        if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
-        modeObj[prop] = exts[prop];
-      }
-    }
-    modeObj.name = spec.name;
-    if (spec.helperType) { modeObj.helperType = spec.helperType; }
-    if (spec.modeProps) { for (var prop$1 in spec.modeProps)
-      { modeObj[prop$1] = spec.modeProps[prop$1]; } }
-
-    return modeObj
-  }
-
-  // This can be used to attach properties to mode objects from
-  // outside the actual mode definition.
-  var modeExtensions = {};
-  function extendMode(mode, properties) {
-    var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
-    copyObj(properties, exts);
-  }
-
-  function copyState(mode, state) {
-    if (state === true) { return state }
-    if (mode.copyState) { return mode.copyState(state) }
-    var nstate = {};
-    for (var n in state) {
-      var val = state[n];
-      if (val instanceof Array) { val = val.concat([]); }
-      nstate[n] = val;
-    }
-    return nstate
-  }
-
-  // Given a mode and a state (for that mode), find the inner mode and
-  // state at the position that the state refers to.
-  function innerMode(mode, state) {
-    var info;
-    while (mode.innerMode) {
-      info = mode.innerMode(state);
-      if (!info || info.mode == mode) { break }
-      state = info.state;
-      mode = info.mode;
-    }
-    return info || {mode: mode, state: state}
-  }
-
-  function startState(mode, a1, a2) {
-    return mode.startState ? mode.startState(a1, a2) : true
-  }
-
-  // STRING STREAM
-
-  // Fed to the mode parsers, provides helper functions to make
-  // parsers more succinct.
-
-  var StringStream = function(string, tabSize, lineOracle) {
-    this.pos = this.start = 0;
-    this.string = string;
-    this.tabSize = tabSize || 8;
-    this.lastColumnPos = this.lastColumnValue = 0;
-    this.lineStart = 0;
-    this.lineOracle = lineOracle;
-  };
-
-  StringStream.prototype.eol = function () {return this.pos >= this.string.length};
-  StringStream.prototype.sol = function () {return this.pos == this.lineStart};
-  StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
-  StringStream.prototype.next = function () {
-    if (this.pos < this.string.length)
-      { return this.string.charAt(this.pos++) }
-  };
-  StringStream.prototype.eat = function (match) {
-    var ch = this.string.charAt(this.pos);
-    var ok;
-    if (typeof match == "string") { ok = ch == match; }
-    else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
-    if (ok) {++this.pos; return ch}
-  };
-  StringStream.prototype.eatWhile = function (match) {
-    var start = this.pos;
-    while (this.eat(match)){}
-    return this.pos > start
-  };
-  StringStream.prototype.eatSpace = function () {
-    var start = this.pos;
-    while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; }
-    return this.pos > start
-  };
-  StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
-  StringStream.prototype.skipTo = function (ch) {
-    var found = this.string.indexOf(ch, this.pos);
-    if (found > -1) {this.pos = found; return true}
-  };
-  StringStream.prototype.backUp = function (n) {this.pos -= n;};
-  StringStream.prototype.column = function () {
-    if (this.lastColumnPos < this.start) {
-      this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
-      this.lastColumnPos = this.start;
-    }
-    return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
-  };
-  StringStream.prototype.indentation = function () {
-    return countColumn(this.string, null, this.tabSize) -
-      (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
-  };
-  StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
-    if (typeof pattern == "string") {
-      var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
-      var substr = this.string.substr(this.pos, pattern.length);
-      if (cased(substr) == cased(pattern)) {
-        if (consume !== false) { this.pos += pattern.length; }
-        return true
-      }
-    } else {
-      var match = this.string.slice(this.pos).match(pattern);
-      if (match && match.index > 0) { return null }
-      if (match && consume !== false) { this.pos += match[0].length; }
-      return match
-    }
-  };
-  StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
-  StringStream.prototype.hideFirstChars = function (n, inner) {
-    this.lineStart += n;
-    try { return inner() }
-    finally { this.lineStart -= n; }
-  };
-  StringStream.prototype.lookAhead = function (n) {
-    var oracle = this.lineOracle;
-    return oracle && oracle.lookAhead(n)
-  };
-  StringStream.prototype.baseToken = function () {
-    var oracle = this.lineOracle;
-    return oracle && oracle.baseToken(this.pos)
-  };
-
-  // Find the line object corresponding to the given line number.
-  function getLine(doc, n) {
-    n -= doc.first;
-    if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
-    var chunk = doc;
-    while (!chunk.lines) {
-      for (var i = 0;; ++i) {
-        var child = chunk.children[i], sz = child.chunkSize();
-        if (n < sz) { chunk = child; break }
-        n -= sz;
-      }
-    }
-    return chunk.lines[n]
-  }
-
-  // Get the part of a document between two positions, as an array of
-  // strings.
-  function getBetween(doc, start, end) {
-    var out = [], n = start.line;
-    doc.iter(start.line, end.line + 1, function (line) {
-      var text = line.text;
-      if (n == end.line) { text = text.slice(0, end.ch); }
-      if (n == start.line) { text = text.slice(start.ch); }
-      out.push(text);
-      ++n;
-    });
-    return out
-  }
-  // Get the lines between from and to, as array of strings.
-  function getLines(doc, from, to) {
-    var out = [];
-    doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value
-    return out
-  }
-
-  // Update the height of a line, propagating the height change
-  // upwards to parent nodes.
-  function updateLineHeight(line, height) {
-    var diff = height - line.height;
-    if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }
-  }
-
-  // Given a line object, find its line number by walking up through
-  // its parent links.
-  function lineNo(line) {
-    if (line.parent == null) { return null }
-    var cur = line.parent, no = indexOf(cur.lines, line);
-    for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
-      for (var i = 0;; ++i) {
-        if (chunk.children[i] == cur) { break }
-        no += chunk.children[i].chunkSize();
-      }
-    }
-    return no + cur.first
-  }
-
-  // Find the line at the given vertical position, using the height
-  // information in the document tree.
-  function lineAtHeight(chunk, h) {
-    var n = chunk.first;
-    outer: do {
-      for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
-        var child = chunk.children[i$1], ch = child.height;
-        if (h < ch) { chunk = child; continue outer }
-        h -= ch;
-        n += child.chunkSize();
-      }
-      return n
-    } while (!chunk.lines)
-    var i = 0;
-    for (; i < chunk.lines.length; ++i) {
-      var line = chunk.lines[i], lh = line.height;
-      if (h < lh) { break }
-      h -= lh;
-    }
-    return n + i
-  }
-
-  function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
-
-  function lineNumberFor(options, i) {
-    return String(options.lineNumberFormatter(i + options.firstLineNumber))
-  }
-
-  // A Pos instance represents a position within the text.
-  function Pos(line, ch, sticky) {
-    if ( sticky === void 0 ) sticky = null;
-
-    if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
-    this.line = line;
-    this.ch = ch;
-    this.sticky = sticky;
-  }
-
-  // Compare two positions, return 0 if they are the same, a negative
-  // number when a is less, and a positive number otherwise.
-  function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
-
-  function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
-
-  function copyPos(x) {return Pos(x.line, x.ch)}
-  function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
-  function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
-
-  // Most of the external API clips given positions to make sure they
-  // actually exist within the document.
-  function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
-  function clipPos(doc, pos) {
-    if (pos.line < doc.first) { return Pos(doc.first, 0) }
-    var last = doc.first + doc.size - 1;
-    if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
-    return clipToLen(pos, getLine(doc, pos.line).text.length)
-  }
-  function clipToLen(pos, linelen) {
-    var ch = pos.ch;
-    if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
-    else if (ch < 0) { return Pos(pos.line, 0) }
-    else { return pos }
-  }
-  function clipPosArray(doc, array) {
-    var out = [];
-    for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }
-    return out
-  }
-
-  var SavedContext = function(state, lookAhead) {
-    this.state = state;
-    this.lookAhead = lookAhead;
-  };
-
-  var Context = function(doc, state, line, lookAhead) {
-    this.state = state;
-    this.doc = doc;
-    this.line = line;
-    this.maxLookAhead = lookAhead || 0;
-    this.baseTokens = null;
-    this.baseTokenPos = 1;
-  };
-
-  Context.prototype.lookAhead = function (n) {
-    var line = this.doc.getLine(this.line + n);
-    if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }
-    return line
-  };
-
-  Context.prototype.baseToken = function (n) {
-    if (!this.baseTokens) { return null }
-    while (this.baseTokens[this.baseTokenPos] <= n)
-      { this.baseTokenPos += 2; }
-    var type = this.baseTokens[this.baseTokenPos + 1];
-    return {type: type && type.replace(/( |^)overlay .*/, ""),
-            size: this.baseTokens[this.baseTokenPos] - n}
-  };
-
-  Context.prototype.nextLine = function () {
-    this.line++;
-    if (this.maxLookAhead > 0) { this.maxLookAhead--; }
-  };
-
-  Context.fromSaved = function (doc, saved, line) {
-    if (saved instanceof SavedContext)
-      { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
-    else
-      { return new Context(doc, copyState(doc.mode, saved), line) }
-  };
-
-  Context.prototype.save = function (copy) {
-    var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;
-    return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
-  };
-
-
-  // Compute a style array (an array starting with a mode generation
-  // -- for invalidation -- followed by pairs of end positions and
-  // style strings), which is used to highlight the tokens on the
-  // line.
-  function highlightLine(cm, line, context, forceToEnd) {
-    // A styles array always starts with a number identifying the
-    // mode/overlays that it is based on (for easy invalidation).
-    var st = [cm.state.modeGen], lineClasses = {};
-    // Compute the base array of styles
-    runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
-            lineClasses, forceToEnd);
-    var state = context.state;
-
-    // Run overlays, adjust style array.
-    var loop = function ( o ) {
-      context.baseTokens = st;
-      var overlay = cm.state.overlays[o], i = 1, at = 0;
-      context.state = true;
-      runMode(cm, line.text, overlay.mode, context, function (end, style) {
-        var start = i;
-        // Ensure there's a token end at the current position, and that i points at it
-        while (at < end) {
-          var i_end = st[i];
-          if (i_end > end)
-            { st.splice(i, 1, end, st[i+1], i_end); }
-          i += 2;
-          at = Math.min(end, i_end);
-        }
-        if (!style) { return }
-        if (overlay.opaque) {
-          st.splice(start, i - start, end, "overlay " + style);
-          i = start + 2;
-        } else {
-          for (; start < i; start += 2) {
-            var cur = st[start+1];
-            st[start+1] = (cur ? cur + " " : "") + "overlay " + style;
-          }
-        }
-      }, lineClasses);
-      context.state = state;
-      context.baseTokens = null;
-      context.baseTokenPos = 1;
-    };
-
-    for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
-
-    return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
-  }
-
-  function getLineStyles(cm, line, updateFrontier) {
-    if (!line.styles || line.styles[0] != cm.state.modeGen) {
-      var context = getContextBefore(cm, lineNo(line));
-      var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);
-      var result = highlightLine(cm, line, context);
-      if (resetState) { context.state = resetState; }
-      line.stateAfter = context.save(!resetState);
-      line.styles = result.styles;
-      if (result.classes) { line.styleClasses = result.classes; }
-      else if (line.styleClasses) { line.styleClasses = null; }
-      if (updateFrontier === cm.doc.highlightFrontier)
-        { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }
-    }
-    return line.styles
-  }
-
-  function getContextBefore(cm, n, precise) {
-    var doc = cm.doc, display = cm.display;
-    if (!doc.mode.startState) { return new Context(doc, true, n) }
-    var start = findStartLine(cm, n, precise);
-    var saved = start > doc.first && getLine(doc, start - 1).stateAfter;
-    var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);
-
-    doc.iter(start, n, function (line) {
-      processLine(cm, line.text, context);
-      var pos = context.line;
-      line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;
-      context.nextLine();
-    });
-    if (precise) { doc.modeFrontier = context.line; }
-    return context
-  }
-
-  // Lightweight form of highlight -- proceed over this line and
-  // update state, but don't save a style array. Used for lines that
-  // aren't currently visible.
-  function processLine(cm, text, context, startAt) {
-    var mode = cm.doc.mode;
-    var stream = new StringStream(text, cm.options.tabSize, context);
-    stream.start = stream.pos = startAt || 0;
-    if (text == "") { callBlankLine(mode, context.state); }
-    while (!stream.eol()) {
-      readToken(mode, stream, context.state);
-      stream.start = stream.pos;
-    }
-  }
-
-  function callBlankLine(mode, state) {
-    if (mode.blankLine) { return mode.blankLine(state) }
-    if (!mode.innerMode) { return }
-    var inner = innerMode(mode, state);
-    if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
-  }
-
-  function readToken(mode, stream, state, inner) {
-    for (var i = 0; i < 10; i++) {
-      if (inner) { inner[0] = innerMode(mode, state).mode; }
-      var style = mode.token(stream, state);
-      if (stream.pos > stream.start) { return style }
-    }
-    throw new Error("Mode " + mode.name + " failed to advance stream.")
-  }
-
-  var Token = function(stream, type, state) {
-    this.start = stream.start; this.end = stream.pos;
-    this.string = stream.current();
-    this.type = type || null;
-    this.state = state;
-  };
-
-  // Utility for getTokenAt and getLineTokens
-  function takeToken(cm, pos, precise, asArray) {
-    var doc = cm.doc, mode = doc.mode, style;
-    pos = clipPos(doc, pos);
-    var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);
-    var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;
-    if (asArray) { tokens = []; }
-    while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
-      stream.start = stream.pos;
-      style = readToken(mode, stream, context.state);
-      if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }
-    }
-    return asArray ? tokens : new Token(stream, style, context.state)
-  }
-
-  function extractLineClasses(type, output) {
-    if (type) { for (;;) {
-      var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
-      if (!lineClass) { break }
-      type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
-      var prop = lineClass[1] ? "bgClass" : "textClass";
-      if (output[prop] == null)
-        { output[prop] = lineClass[2]; }
-      else if (!(new RegExp("(?:^|\\s)" + lineClass[2] + "(?:$|\\s)")).test(output[prop]))
-        { output[prop] += " " + lineClass[2]; }
-    } }
-    return type
-  }
-
-  // Run the given mode's parser over a line, calling f for each token.
-  function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
-    var flattenSpans = mode.flattenSpans;
-    if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }
-    var curStart = 0, curStyle = null;
-    var stream = new StringStream(text, cm.options.tabSize, context), style;
-    var inner = cm.options.addModeClass && [null];
-    if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }
-    while (!stream.eol()) {
-      if (stream.pos > cm.options.maxHighlightLength) {
-        flattenSpans = false;
-        if (forceToEnd) { processLine(cm, text, context, stream.pos); }
-        stream.pos = text.length;
-        style = null;
-      } else {
-        style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);
-      }
-      if (inner) {
-        var mName = inner[0].name;
-        if (mName) { style = "m-" + (style ? mName + " " + style : mName); }
-      }
-      if (!flattenSpans || curStyle != style) {
-        while (curStart < stream.start) {
-          curStart = Math.min(stream.start, curStart + 5000);
-          f(curStart, curStyle);
-        }
-        curStyle = style;
-      }
-      stream.start = stream.pos;
-    }
-    while (curStart < stream.pos) {
-      // Webkit seems to refuse to render text nodes longer than 57444
-      // characters, and returns inaccurate measurements in nodes
-      // starting around 5000 chars.
-      var pos = Math.min(stream.pos, curStart + 5000);
-      f(pos, curStyle);
-      curStart = pos;
-    }
-  }
-
-  // Finds the line to start with when starting a parse. Tries to
-  // find a line with a stateAfter, so that it can start with a
-  // valid state. If that fails, it returns the line with the
-  // smallest indentation, which tends to need the least context to
-  // parse correctly.
-  function findStartLine(cm, n, precise) {
-    var minindent, minline, doc = cm.doc;
-    var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
-    for (var search = n; search > lim; --search) {
-      if (search <= doc.first) { return doc.first }
-      var line = getLine(doc, search - 1), after = line.stateAfter;
-      if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
-        { return search }
-      var indented = countColumn(line.text, null, cm.options.tabSize);
-      if (minline == null || minindent > indented) {
-        minline = search - 1;
-        minindent = indented;
-      }
-    }
-    return minline
-  }
-
-  function retreatFrontier(doc, n) {
-    doc.modeFrontier = Math.min(doc.modeFrontier, n);
-    if (doc.highlightFrontier < n - 10) { return }
-    var start = doc.first;
-    for (var line = n - 1; line > start; line--) {
-      var saved = getLine(doc, line).stateAfter;
-      // change is on 3
-      // state on line 1 looked ahead 2 -- so saw 3
-      // test 1 + 2 < 3 should cover this
-      if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
-        start = line + 1;
-        break
-      }
-    }
-    doc.highlightFrontier = Math.min(doc.highlightFrontier, start);
-  }
-
-  // Optimize some code when these features are not used.
-  var sawReadOnlySpans = false, sawCollapsedSpans = false;
-
-  function seeReadOnlySpans() {
-    sawReadOnlySpans = true;
-  }
-
-  function seeCollapsedSpans() {
-    sawCollapsedSpans = true;
-  }
-
-  // TEXTMARKER SPANS
-
-  function MarkedSpan(marker, from, to) {
-    this.marker = marker;
-    this.from = from; this.to = to;
-  }
-
-  // Search an array of spans for a span matching the given marker.
-  function getMarkedSpanFor(spans, marker) {
-    if (spans) { for (var i = 0; i < spans.length; ++i) {
-      var span = spans[i];
-      if (span.marker == marker) { return span }
-    } }
-  }
-  // Remove a span from an array, returning undefined if no spans are
-  // left (we don't store arrays for lines without spans).
-  function removeMarkedSpan(spans, span) {
-    var r;
-    for (var i = 0; i < spans.length; ++i)
-      { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }
-    return r
-  }
-  // Add a span to a line.
-  function addMarkedSpan(line, span) {
-    line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
-    span.marker.attachLine(line);
-  }
-
-  // Used for the algorithm that adjusts markers for a change in the
-  // document. These functions cut an array of spans at a given
-  // character position, returning an array of remaining chunks (or
-  // undefined if nothing remains).
-  function markedSpansBefore(old, startCh, isInsert) {
-    var nw;
-    if (old) { for (var i = 0; i < old.length; ++i) {
-      var span = old[i], marker = span.marker;
-      var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
-      if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
-        var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)
-        ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
-      }
-    } }
-    return nw
-  }
-  function markedSpansAfter(old, endCh, isInsert) {
-    var nw;
-    if (old) { for (var i = 0; i < old.length; ++i) {
-      var span = old[i], marker = span.marker;
-      var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
-      if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
-        var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
-        ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
-                                              span.to == null ? null : span.to - endCh));
-      }
-    } }
-    return nw
-  }
-
-  // Given a change object, compute the new set of marker spans that
-  // cover the line in which the change took place. Removes spans
-  // entirely within the change, reconnects spans belonging to the
-  // same marker that appear on both sides of the change, and cuts off
-  // spans partially within the change. Returns an array of span
-  // arrays with one element for each line in (after) the change.
-  function stretchSpansOverChange(doc, change) {
-    if (change.full) { return null }
-    var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
-    var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
-    if (!oldFirst && !oldLast) { return null }
-
-    var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
-    // Get the spans that 'stick out' on both sides
-    var first = markedSpansBefore(oldFirst, startCh, isInsert);
-    var last = markedSpansAfter(oldLast, endCh, isInsert);
-
-    // Next, merge those two ends
-    var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
-    if (first) {
-      // Fix up .to properties of first
-      for (var i = 0; i < first.length; ++i) {
-        var span = first[i];
-        if (span.to == null) {
-          var found = getMarkedSpanFor(last, span.marker);
-          if (!found) { span.to = startCh; }
-          else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }
-        }
-      }
-    }
-    if (last) {
-      // Fix up .from in last (or move them into first in case of sameLine)
-      for (var i$1 = 0; i$1 < last.length; ++i$1) {
-        var span$1 = last[i$1];
-        if (span$1.to != null) { span$1.to += offset; }
-        if (span$1.from == null) {
-          var found$1 = getMarkedSpanFor(first, span$1.marker);
-          if (!found$1) {
-            span$1.from = offset;
-            if (sameLine) { (first || (first = [])).push(span$1); }
-          }
-        } else {
-          span$1.from += offset;
-          if (sameLine) { (first || (first = [])).push(span$1); }
-        }
-      }
-    }
-    // Make sure we didn't create any zero-length spans
-    if (first) { first = clearEmptySpans(first); }
-    if (last && last != first) { last = clearEmptySpans(last); }
-
-    var newMarkers = [first];
-    if (!sameLine) {
-      // Fill gap with whole-line-spans
-      var gap = change.text.length - 2, gapMarkers;
-      if (gap > 0 && first)
-        { for (var i$2 = 0; i$2 < first.length; ++i$2)
-          { if (first[i$2].to == null)
-            { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }
-      for (var i$3 = 0; i$3 < gap; ++i$3)
-        { newMarkers.push(gapMarkers); }
-      newMarkers.push(last);
-    }
-    return newMarkers
-  }
-
-  // Remove spans that are empty and don't have a clearWhenEmpty
-  // option of false.
-  function clearEmptySpans(spans) {
-    for (var i = 0; i < spans.length; ++i) {
-      var span = spans[i];
-      if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
-        { spans.splice(i--, 1); }
-    }
-    if (!spans.length) { return null }
-    return spans
-  }
-
-  // Used to 'clip' out readOnly ranges when making a change.
-  function removeReadOnlyRanges(doc, from, to) {
-    var markers = null;
-    doc.iter(from.line, to.line + 1, function (line) {
-      if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
-        var mark = line.markedSpans[i].marker;
-        if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
-          { (markers || (markers = [])).push(mark); }
-      } }
-    });
-    if (!markers) { return null }
-    var parts = [{from: from, to: to}];
-    for (var i = 0; i < markers.length; ++i) {
-      var mk = markers[i], m = mk.find(0);
-      for (var j = 0; j < parts.length; ++j) {
-        var p = parts[j];
-        if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
-        var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
-        if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
-          { newParts.push({from: p.from, to: m.from}); }
-        if (dto > 0 || !mk.inclusiveRight && !dto)
-          { newParts.push({from: m.to, to: p.to}); }
-        parts.splice.apply(parts, newParts);
-        j += newParts.length - 3;
-      }
-    }
-    return parts
-  }
-
-  // Connect or disconnect spans from a line.
-  function detachMarkedSpans(line) {
-    var spans = line.markedSpans;
-    if (!spans) { return }
-    for (var i = 0; i < spans.length; ++i)
-      { spans[i].marker.detachLine(line); }
-    line.markedSpans = null;
-  }
-  function attachMarkedSpans(line, spans) {
-    if (!spans) { return }
-    for (var i = 0; i < spans.length; ++i)
-      { spans[i].marker.attachLine(line); }
-    line.markedSpans = spans;
-  }
-
-  // Helpers used when computing which overlapping collapsed span
-  // counts as the larger one.
-  function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
-  function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
-
-  // Returns a number indicating which of two overlapping collapsed
-  // spans is larger (and thus includes the other). Falls back to
-  // comparing ids when the spans cover exactly the same range.
-  function compareCollapsedMarkers(a, b) {
-    var lenDiff = a.lines.length - b.lines.length;
-    if (lenDiff != 0) { return lenDiff }
-    var aPos = a.find(), bPos = b.find();
-    var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
-    if (fromCmp) { return -fromCmp }
-    var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
-    if (toCmp) { return toCmp }
-    return b.id - a.id
-  }
-
-  // Find out whether a line ends or starts in a collapsed span. If
-  // so, return the marker for that span.
-  function collapsedSpanAtSide(line, start) {
-    var sps = sawCollapsedSpans && line.markedSpans, found;
-    if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
-      sp = sps[i];
-      if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
-          (!found || compareCollapsedMarkers(found, sp.marker) < 0))
-        { found = sp.marker; }
-    } }
-    return found
-  }
-  function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
-  function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
-
-  function collapsedSpanAround(line, ch) {
-    var sps = sawCollapsedSpans && line.markedSpans, found;
-    if (sps) { for (var i = 0; i < sps.length; ++i) {
-      var sp = sps[i];
-      if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&
-          (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; }
-    } }
-    return found
-  }
-
-  // Test whether there exists a collapsed span that partially
-  // overlaps (covers the start or end, but not both) of a new span.
-  // Such overlap is not allowed.
-  function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
-    var line = getLine(doc, lineNo);
-    var sps = sawCollapsedSpans && line.markedSpans;
-    if (sps) { for (var i = 0; i < sps.length; ++i) {
-      var sp = sps[i];
-      if (!sp.marker.collapsed) { continue }
-      var found = sp.marker.find(0);
-      var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
-      var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
-      if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
-      if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
-          fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
-        { return true }
-    } }
-  }
-
-  // A visual line is a line as drawn on the screen. Folding, for
-  // example, can cause multiple logical lines to appear on the same
-  // visual line. This finds the start of the visual line that the
-  // given line is part of (usually that is the line itself).
-  function visualLine(line) {
-    var merged;
-    while (merged = collapsedSpanAtStart(line))
-      { line = merged.find(-1, true).line; }
-    return line
-  }
-
-  function visualLineEnd(line) {
-    var merged;
-    while (merged = collapsedSpanAtEnd(line))
-      { line = merged.find(1, true).line; }
-    return line
-  }
-
-  // Returns an array of logical lines that continue the visual line
-  // started by the argument, or undefined if there are no such lines.
-  function visualLineContinued(line) {
-    var merged, lines;
-    while (merged = collapsedSpanAtEnd(line)) {
-      line = merged.find(1, true).line
-      ;(lines || (lines = [])).push(line);
-    }
-    return lines
-  }
-
-  // Get the line number of the start of the visual line that the
-  // given line number is part of.
-  function visualLineNo(doc, lineN) {
-    var line = getLine(doc, lineN), vis = visualLine(line);
-    if (line == vis) { return lineN }
-    return lineNo(vis)
-  }
-
-  // Get the line number of the start of the next visual line after
-  // the given line.
-  function visualLineEndNo(doc, lineN) {
-    if (lineN > doc.lastLine()) { return lineN }
-    var line = getLine(doc, lineN), merged;
-    if (!lineIsHidden(doc, line)) { return lineN }
-    while (merged = collapsedSpanAtEnd(line))
-      { line = merged.find(1, true).line; }
-    return lineNo(line) + 1
-  }
-
-  // Compute whether a line is hidden. Lines count as hidden when they
-  // are part of a visual line that starts with another line, or when
-  // they are entirely covered by collapsed, non-widget span.
-  function lineIsHidden(doc, line) {
-    var sps = sawCollapsedSpans && line.markedSpans;
-    if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
-      sp = sps[i];
-      if (!sp.marker.collapsed) { continue }
-      if (sp.from == null) { return true }
-      if (sp.marker.widgetNode) { continue }
-      if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
-        { return true }
-    } }
-  }
-  function lineIsHiddenInner(doc, line, span) {
-    if (span.to == null) {
-      var end = span.marker.find(1, true);
-      return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
-    }
-    if (span.marker.inclusiveRight && span.to == line.text.length)
-      { return true }
-    for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
-      sp = line.markedSpans[i];
-      if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
-          (sp.to == null || sp.to != span.from) &&
-          (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
-          lineIsHiddenInner(doc, line, sp)) { return true }
-    }
-  }
-
-  // Find the height above the given line.
-  function heightAtLine(lineObj) {
-    lineObj = visualLine(lineObj);
-
-    var h = 0, chunk = lineObj.parent;
-    for (var i = 0; i < chunk.lines.length; ++i) {
-      var line = chunk.lines[i];
-      if (line == lineObj) { break }
-      else { h += line.height; }
-    }
-    for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
-      for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
-        var cur = p.children[i$1];
-        if (cur == chunk) { break }
-        else { h += cur.height; }
-      }
-    }
-    return h
-  }
-
-  // Compute the character length of a line, taking into account
-  // collapsed ranges (see markText) that might hide parts, and join
-  // other lines onto it.
-  function lineLength(line) {
-    if (line.height == 0) { return 0 }
-    var len = line.text.length, merged, cur = line;
-    while (merged = collapsedSpanAtStart(cur)) {
-      var found = merged.find(0, true);
-      cur = found.from.line;
-      len += found.from.ch - found.to.ch;
-    }
-    cur = line;
-    while (merged = collapsedSpanAtEnd(cur)) {
-      var found$1 = merged.find(0, true);
-      len -= cur.text.length - found$1.from.ch;
-      cur = found$1.to.line;
-      len += cur.text.length - found$1.to.ch;
-    }
-    return len
-  }
-
-  // Find the longest line in the document.
-  function findMaxLine(cm) {
-    var d = cm.display, doc = cm.doc;
-    d.maxLine = getLine(doc, doc.first);
-    d.maxLineLength = lineLength(d.maxLine);
-    d.maxLineChanged = true;
-    doc.iter(function (line) {
-      var len = lineLength(line);
-      if (len > d.maxLineLength) {
-        d.maxLineLength = len;
-        d.maxLine = line;
-      }
-    });
-  }
-
-  // LINE DATA STRUCTURE
-
-  // Line objects. These hold state related to a line, including
-  // highlighting info (the styles array).
-  var Line = function(text, markedSpans, estimateHeight) {
-    this.text = text;
-    attachMarkedSpans(this, markedSpans);
-    this.height = estimateHeight ? estimateHeight(this) : 1;
-  };
-
-  Line.prototype.lineNo = function () { return lineNo(this) };
-  eventMixin(Line);
-
-  // Change the content (text, markers) of a line. Automatically
-  // invalidates cached information and tries to re-estimate the
-  // line's height.
-  function updateLine(line, text, markedSpans, estimateHeight) {
-    line.text = text;
-    if (line.stateAfter) { line.stateAfter = null; }
-    if (line.styles) { line.styles = null; }
-    if (line.order != null) { line.order = null; }
-    detachMarkedSpans(line);
-    attachMarkedSpans(line, markedSpans);
-    var estHeight = estimateHeight ? estimateHeight(line) : 1;
-    if (estHeight != line.height) { updateLineHeight(line, estHeight); }
-  }
-
-  // Detach a line from the document tree and its markers.
-  function cleanUpLine(line) {
-    line.parent = null;
-    detachMarkedSpans(line);
-  }
-
-  // Convert a style as returned by a mode (either null, or a string
-  // containing one or more styles) to a CSS style. This is cached,
-  // and also looks for line-wide styles.
-  var styleToClassCache = {}, styleToClassCacheWithMode = {};
-  function interpretTokenStyle(style, options) {
-    if (!style || /^\s*$/.test(style)) { return null }
-    var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
-    return cache[style] ||
-      (cache[style] = style.replace(/\S+/g, "cm-$&"))
-  }
-
-  // Render the DOM representation of the text of a line. Also builds
-  // up a 'line map', which points at the DOM nodes that represent
-  // specific stretches of text, and is used by the measuring code.
-  // The returned object contains the DOM node, this map, and
-  // information about line-wide styles that were set by the mode.
-  function buildLineContent(cm, lineView) {
-    // The padding-right forces the element to have a 'border', which
-    // is needed on Webkit to be able to get line-level bounding
-    // rectangles for it (in measureChar).
-    var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
-    var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
-                   col: 0, pos: 0, cm: cm,
-                   trailingSpace: false,
-                   splitSpaces: cm.getOption("lineWrapping")};
-    lineView.measure = {};
-
-    // Iterate over the logical lines that make up this visual line.
-    for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
-      var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);
-      builder.pos = 0;
-      builder.addToken = buildToken;
-      // Optionally wire in some hacks into the token-rendering
-      // algorithm, to deal with browser quirks.
-      if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
-        { builder.addToken = buildTokenBadBidi(builder.addToken, order); }
-      builder.map = [];
-      var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
-      insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
-      if (line.styleClasses) {
-        if (line.styleClasses.bgClass)
-          { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); }
-        if (line.styleClasses.textClass)
-          { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); }
-      }
-
-      // Ensure at least a single node is present, for measuring.
-      if (builder.map.length == 0)
-        { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }
-
-      // Store the map and a cache object for the current logical line
-      if (i == 0) {
-        lineView.measure.map = builder.map;
-        lineView.measure.cache = {};
-      } else {
-  (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
-        ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});
-      }
-    }
-
-    // See issue #2901
-    if (webkit) {
-      var last = builder.content.lastChild;
-      if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
-        { builder.content.className = "cm-tab-wrap-hack"; }
-    }
-
-    signal(cm, "renderLine", cm, lineView.line, builder.pre);
-    if (builder.pre.className)
-      { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); }
-
-    return builder
-  }
-
-  function defaultSpecialCharPlaceholder(ch) {
-    var token = elt("span", "\u2022", "cm-invalidchar");
-    token.title = "\\u" + ch.charCodeAt(0).toString(16);
-    token.setAttribute("aria-label", token.title);
-    return token
-  }
-
-  // Build up the DOM representation for a single token, and add it to
-  // the line map. Takes care to render special characters separately.
-  function buildToken(builder, text, style, startStyle, endStyle, css, attributes) {
-    if (!text) { return }
-    var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;
-    var special = builder.cm.state.specialChars, mustWrap = false;
-    var content;
-    if (!special.test(text)) {
-      builder.col += text.length;
-      content = document.createTextNode(displayText);
-      builder.map.push(builder.pos, builder.pos + text.length, content);
-      if (ie && ie_version < 9) { mustWrap = true; }
-      builder.pos += text.length;
-    } else {
-      content = document.createDocumentFragment();
-      var pos = 0;
-      while (true) {
-        special.lastIndex = pos;
-        var m = special.exec(text);
-        var skipped = m ? m.index - pos : text.length - pos;
-        if (skipped) {
-          var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
-          if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); }
-          else { content.appendChild(txt); }
-          builder.map.push(builder.pos, builder.pos + skipped, txt);
-          builder.col += skipped;
-          builder.pos += skipped;
-        }
-        if (!m) { break }
-        pos += skipped + 1;
-        var txt$1 = (void 0);
-        if (m[0] == "\t") {
-          var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
-          txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
-          txt$1.setAttribute("role", "presentation");
-          txt$1.setAttribute("cm-text", "\t");
-          builder.col += tabWidth;
-        } else if (m[0] == "\r" || m[0] == "\n") {
-          txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
-          txt$1.setAttribute("cm-text", m[0]);
-          builder.col += 1;
-        } else {
-          txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);
-          txt$1.setAttribute("cm-text", m[0]);
-          if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); }
-          else { content.appendChild(txt$1); }
-          builder.col += 1;
-        }
-        builder.map.push(builder.pos, builder.pos + 1, txt$1);
-        builder.pos++;
-      }
-    }
-    builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;
-    if (style || startStyle || endStyle || mustWrap || css) {
-      var fullStyle = style || "";
-      if (startStyle) { fullStyle += startStyle; }
-      if (endStyle) { fullStyle += endStyle; }
-      var token = elt("span", [content], fullStyle, css);
-      if (attributes) {
-        for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class")
-          { token.setAttribute(attr, attributes[attr]); } }
-      }
-      return builder.content.appendChild(token)
-    }
-    builder.content.appendChild(content);
-  }
-
-  // Change some spaces to NBSP to prevent the browser from collapsing
-  // trailing spaces at the end of a line when rendering text (issue #1362).
-  function splitSpaces(text, trailingBefore) {
-    if (text.length > 1 && !/  /.test(text)) { return text }
-    var spaceBefore = trailingBefore, result = "";
-    for (var i = 0; i < text.length; i++) {
-      var ch = text.charAt(i);
-      if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
-        { ch = "\u00a0"; }
-      result += ch;
-      spaceBefore = ch == " ";
-    }
-    return result
-  }
-
-  // Work around nonsense dimensions being reported for stretches of
-  // right-to-left text.
-  function buildTokenBadBidi(inner, order) {
-    return function (builder, text, style, startStyle, endStyle, css, attributes) {
-      style = style ? style + " cm-force-border" : "cm-force-border";
-      var start = builder.pos, end = start + text.length;
-      for (;;) {
-        // Find the part that overlaps with the start of this text
-        var part = (void 0);
-        for (var i = 0; i < order.length; i++) {
-          part = order[i];
-          if (part.to > start && part.from <= start) { break }
-        }
-        if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) }
-        inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes);
-        startStyle = null;
-        text = text.slice(part.to - start);
-        start = part.to;
-      }
-    }
-  }
-
-  function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
-    var widget = !ignoreWidget && marker.widgetNode;
-    if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }
-    if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
-      if (!widget)
-        { widget = builder.content.appendChild(document.createElement("span")); }
-      widget.setAttribute("cm-marker", marker.id);
-    }
-    if (widget) {
-      builder.cm.display.input.setUneditable(widget);
-      builder.content.appendChild(widget);
-    }
-    builder.pos += size;
-    builder.trailingSpace = false;
-  }
-
-  // Outputs a number of spans to make up a line, taking highlighting
-  // and marked text into account.
-  function insertLineContent(line, builder, styles) {
-    var spans = line.markedSpans, allText = line.text, at = 0;
-    if (!spans) {
-      for (var i$1 = 1; i$1 < styles.length; i$1+=2)
-        { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }
-      return
-    }
-
-    var len = allText.length, pos = 0, i = 1, text = "", style, css;
-    var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes;
-    for (;;) {
-      if (nextChange == pos) { // Update current marker set
-        spanStyle = spanEndStyle = spanStartStyle = css = "";
-        attributes = null;
-        collapsed = null; nextChange = Infinity;
-        var foundBookmarks = [], endStyles = (void 0);
-        for (var j = 0; j < spans.length; ++j) {
-          var sp = spans[j], m = sp.marker;
-          if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
-            foundBookmarks.push(m);
-          } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
-            if (sp.to != null && sp.to != pos && nextChange > sp.to) {
-              nextChange = sp.to;
-              spanEndStyle = "";
-            }
-            if (m.className) { spanStyle += " " + m.className; }
-            if (m.css) { css = (css ? css + ";" : "") + m.css; }
-            if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; }
-            if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }
-            // support for the old title property
-            // https://github.com/codemirror/CodeMirror/pull/5673
-            if (m.title) { (attributes || (attributes = {})).title = m.title; }
-            if (m.attributes) {
-              for (var attr in m.attributes)
-                { (attributes || (attributes = {}))[attr] = m.attributes[attr]; }
-            }
-            if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
-              { collapsed = sp; }
-          } else if (sp.from > pos && nextChange > sp.from) {
-            nextChange = sp.from;
-          }
-        }
-        if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
-          { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } }
-
-        if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
-          { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }
-        if (collapsed && (collapsed.from || 0) == pos) {
-          buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
-                             collapsed.marker, collapsed.from == null);
-          if (collapsed.to == null) { return }
-          if (collapsed.to == pos) { collapsed = false; }
-        }
-      }
-      if (pos >= len) { break }
-
-      var upto = Math.min(len, nextChange);
-      while (true) {
-        if (text) {
-          var end = pos + text.length;
-          if (!collapsed) {
-            var tokenText = end > upto ? text.slice(0, upto - pos) : text;
-            builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
-                             spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes);
-          }
-          if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
-          pos = end;
-          spanStartStyle = "";
-        }
-        text = allText.slice(at, at = styles[i++]);
-        style = interpretTokenStyle(styles[i++], builder.cm.options);
-      }
-    }
-  }
-
-
-  // These objects are used to represent the visible (currently drawn)
-  // part of the document. A LineView may correspond to multiple
-  // logical lines, if those are connected by collapsed ranges.
-  function LineView(doc, line, lineN) {
-    // The starting line
-    this.line = line;
-    // Continuing lines, if any
-    this.rest = visualLineContinued(line);
-    // Number of logical lines in this visual line
-    this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
-    this.node = this.text = null;
-    this.hidden = lineIsHidden(doc, line);
-  }
-
-  // Create a range of LineView objects for the given lines.
-  function buildViewArray(cm, from, to) {
-    var array = [], nextPos;
-    for (var pos = from; pos < to; pos = nextPos) {
-      var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
-      nextPos = pos + view.size;
-      array.push(view);
-    }
-    return array
-  }
-
-  var operationGroup = null;
-
-  function pushOperation(op) {
-    if (operationGroup) {
-      operationGroup.ops.push(op);
-    } else {
-      op.ownsGroup = operationGroup = {
-        ops: [op],
-        delayedCallbacks: []
-      };
-    }
-  }
-
-  function fireCallbacksForOps(group) {
-    // Calls delayed callbacks and cursorActivity handlers until no
-    // new ones appear
-    var callbacks = group.delayedCallbacks, i = 0;
-    do {
-      for (; i < callbacks.length; i++)
-        { callbacks[i].call(null); }
-      for (var j = 0; j < group.ops.length; j++) {
-        var op = group.ops[j];
-        if (op.cursorActivityHandlers)
-          { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
-            { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }
-      }
-    } while (i < callbacks.length)
-  }
-
-  function finishOperation(op, endCb) {
-    var group = op.ownsGroup;
-    if (!group) { return }
-
-    try { fireCallbacksForOps(group); }
-    finally {
-      operationGroup = null;
-      endCb(group);
-    }
-  }
-
-  var orphanDelayedCallbacks = null;
-
-  // Often, we want to signal events at a point where we are in the
-  // middle of some work, but don't want the handler to start calling
-  // other methods on the editor, which might be in an inconsistent
-  // state or simply not expect any other events to happen.
-  // signalLater looks whether there are any handlers, and schedules
-  // them to be executed when the last operation ends, or, if no
-  // operation is active, when a timeout fires.
-  function signalLater(emitter, type /*, values...*/) {
-    var arr = getHandlers(emitter, type);
-    if (!arr.length) { return }
-    var args = Array.prototype.slice.call(arguments, 2), list;
-    if (operationGroup) {
-      list = operationGroup.delayedCallbacks;
-    } else if (orphanDelayedCallbacks) {
-      list = orphanDelayedCallbacks;
-    } else {
-      list = orphanDelayedCallbacks = [];
-      setTimeout(fireOrphanDelayed, 0);
-    }
-    var loop = function ( i ) {
-      list.push(function () { return arr[i].apply(null, args); });
-    };
-
-    for (var i = 0; i < arr.length; ++i)
-      loop( i );
-  }
-
-  function fireOrphanDelayed() {
-    var delayed = orphanDelayedCallbacks;
-    orphanDelayedCallbacks = null;
-    for (var i = 0; i < delayed.length; ++i) { delayed[i](); }
-  }
-
-  // When an aspect of a line changes, a string is added to
-  // lineView.changes. This updates the relevant part of the line's
-  // DOM structure.
-  function updateLineForChanges(cm, lineView, lineN, dims) {
-    for (var j = 0; j < lineView.changes.length; j++) {
-      var type = lineView.changes[j];
-      if (type == "text") { updateLineText(cm, lineView); }
-      else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); }
-      else if (type == "class") { updateLineClasses(cm, lineView); }
-      else if (type == "widget") { updateLineWidgets(cm, lineView, dims); }
-    }
-    lineView.changes = null;
-  }
-
-  // Lines with gutter elements, widgets or a background class need to
-  // be wrapped, and have the extra elements added to the wrapper div
-  function ensureLineWrapped(lineView) {
-    if (lineView.node == lineView.text) {
-      lineView.node = elt("div", null, null, "position: relative");
-      if (lineView.text.parentNode)
-        { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }
-      lineView.node.appendChild(lineView.text);
-      if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }
-    }
-    return lineView.node
-  }
-
-  function updateLineBackground(cm, lineView) {
-    var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
-    if (cls) { cls += " CodeMirror-linebackground"; }
-    if (lineView.background) {
-      if (cls) { lineView.background.className = cls; }
-      else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
-    } else if (cls) {
-      var wrap = ensureLineWrapped(lineView);
-      lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
-      cm.display.input.setUneditable(lineView.background);
-    }
-  }
-
-  // Wrapper around buildLineContent which will reuse the structure
-  // in display.externalMeasured when possible.
-  function getLineContent(cm, lineView) {
-    var ext = cm.display.externalMeasured;
-    if (ext && ext.line == lineView.line) {
-      cm.display.externalMeasured = null;
-      lineView.measure = ext.measure;
-      return ext.built
-    }
-    return buildLineContent(cm, lineView)
-  }
-
-  // Redraw the line's text. Interacts with the background and text
-  // classes because the mode may output tokens that influence these
-  // classes.
-  function updateLineText(cm, lineView) {
-    var cls = lineView.text.className;
-    var built = getLineContent(cm, lineView);
-    if (lineView.text == lineView.node) { lineView.node = built.pre; }
-    lineView.text.parentNode.replaceChild(built.pre, lineView.text);
-    lineView.text = built.pre;
-    if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
-      lineView.bgClass = built.bgClass;
-      lineView.textClass = built.textClass;
-      updateLineClasses(cm, lineView);
-    } else if (cls) {
-      lineView.text.className = cls;
-    }
-  }
-
-  function updateLineClasses(cm, lineView) {
-    updateLineBackground(cm, lineView);
-    if (lineView.line.wrapClass)
-      { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }
-    else if (lineView.node != lineView.text)
-      { lineView.node.className = ""; }
-    var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
-    lineView.text.className = textClass || "";
-  }
-
-  function updateLineGutter(cm, lineView, lineN, dims) {
-    if (lineView.gutter) {
-      lineView.node.removeChild(lineView.gutter);
-      lineView.gutter = null;
-    }
-    if (lineView.gutterBackground) {
-      lineView.node.removeChild(lineView.gutterBackground);
-      lineView.gutterBackground = null;
-    }
-    if (lineView.line.gutterClass) {
-      var wrap = ensureLineWrapped(lineView);
-      lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
-                                      ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"));
-      cm.display.input.setUneditable(lineView.gutterBackground);
-      wrap.insertBefore(lineView.gutterBackground, lineView.text);
-    }
-    var markers = lineView.line.gutterMarkers;
-    if (cm.options.lineNumbers || markers) {
-      var wrap$1 = ensureLineWrapped(lineView);
-      var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
-      cm.display.input.setUneditable(gutterWrap);
-      wrap$1.insertBefore(gutterWrap, lineView.text);
-      if (lineView.line.gutterClass)
-        { gutterWrap.className += " " + lineView.line.gutterClass; }
-      if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
-        { lineView.lineNumber = gutterWrap.appendChild(
-          elt("div", lineNumberFor(cm.options, lineN),
-              "CodeMirror-linenumber CodeMirror-gutter-elt",
-              ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); }
-      if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) {
-        var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id];
-        if (found)
-          { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
-                                     ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); }
-      } }
-    }
-  }
-
-  function updateLineWidgets(cm, lineView, dims) {
-    if (lineView.alignable) { lineView.alignable = null; }
-    var isWidget = classTest("CodeMirror-linewidget");
-    for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
-      next = node.nextSibling;
-      if (isWidget.test(node.className)) { lineView.node.removeChild(node); }
-    }
-    insertLineWidgets(cm, lineView, dims);
-  }
-
-  // Build a line's DOM representation from scratch
-  function buildLineElement(cm, lineView, lineN, dims) {
-    var built = getLineContent(cm, lineView);
-    lineView.text = lineView.node = built.pre;
-    if (built.bgClass) { lineView.bgClass = built.bgClass; }
-    if (built.textClass) { lineView.textClass = built.textClass; }
-
-    updateLineClasses(cm, lineView);
-    updateLineGutter(cm, lineView, lineN, dims);
-    insertLineWidgets(cm, lineView, dims);
-    return lineView.node
-  }
-
-  // A lineView may contain multiple logical lines (when merged by
-  // collapsed spans). The widgets for all of them need to be drawn.
-  function insertLineWidgets(cm, lineView, dims) {
-    insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
-    if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
-      { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }
-  }
-
-  function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
-    if (!line.widgets) { return }
-    var wrap = ensureLineWrapped(lineView);
-    for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
-      var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : ""));
-      if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); }
-      positionLineWidget(widget, node, lineView, dims);
-      cm.display.input.setUneditable(node);
-      if (allowAbove && widget.above)
-        { wrap.insertBefore(node, lineView.gutter || lineView.text); }
-      else
-        { wrap.appendChild(node); }
-      signalLater(widget, "redraw");
-    }
-  }
-
-  function positionLineWidget(widget, node, lineView, dims) {
-    if (widget.noHScroll) {
-  (lineView.alignable || (lineView.alignable = [])).push(node);
-      var width = dims.wrapperWidth;
-      node.style.left = dims.fixedPos + "px";
-      if (!widget.coverGutter) {
-        width -= dims.gutterTotalWidth;
-        node.style.paddingLeft = dims.gutterTotalWidth + "px";
-      }
-      node.style.width = width + "px";
-    }
-    if (widget.coverGutter) {
-      node.style.zIndex = 5;
-      node.style.position = "relative";
-      if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; }
-    }
-  }
-
-  function widgetHeight(widget) {
-    if (widget.height != null) { return widget.height }
-    var cm = widget.doc.cm;
-    if (!cm) { return 0 }
-    if (!contains(document.body, widget.node)) {
-      var parentStyle = "position: relative;";
-      if (widget.coverGutter)
-        { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; }
-      if (widget.noHScroll)
-        { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; }
-      removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
-    }
-    return widget.height = widget.node.parentNode.offsetHeight
-  }
-
-  // Return true when the given mouse event happened in a widget
-  function eventInWidget(display, e) {
-    for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
-      if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
-          (n.parentNode == display.sizer && n != display.mover))
-        { return true }
-    }
-  }
-
-  // POSITION MEASUREMENT
-
-  function paddingTop(display) {return display.lineSpace.offsetTop}
-  function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
-  function paddingH(display) {
-    if (display.cachedPaddingH) { return display.cachedPaddingH }
-    var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like"));
-    var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
-    var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
-    if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }
-    return data
-  }
-
-  function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
-  function displayWidth(cm) {
-    return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
-  }
-  function displayHeight(cm) {
-    return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
-  }
-
-  // Ensure the lineView.wrapping.heights array is populated. This is
-  // an array of bottom offsets for the lines that make up a drawn
-  // line. When lineWrapping is on, there might be more than one
-  // height.
-  function ensureLineHeights(cm, lineView, rect) {
-    var wrapping = cm.options.lineWrapping;
-    var curWidth = wrapping && displayWidth(cm);
-    if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
-      var heights = lineView.measure.heights = [];
-      if (wrapping) {
-        lineView.measure.width = curWidth;
-        var rects = lineView.text.firstChild.getClientRects();
-        for (var i = 0; i < rects.length - 1; i++) {
-          var cur = rects[i], next = rects[i + 1];
-          if (Math.abs(cur.bottom - next.bottom) > 2)
-            { heights.push((cur.bottom + next.top) / 2 - rect.top); }
-        }
-      }
-      heights.push(rect.bottom - rect.top);
-    }
-  }
-
-  // Find a line map (mapping character offsets to text nodes) and a
-  // measurement cache for the given line number. (A line view might
-  // contain multiple lines when collapsed ranges are present.)
-  function mapFromLineView(lineView, line, lineN) {
-    if (lineView.line == line)
-      { return {map: lineView.measure.map, cache: lineView.measure.cache} }
-    for (var i = 0; i < lineView.rest.length; i++)
-      { if (lineView.rest[i] == line)
-        { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
-    for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
-      { if (lineNo(lineView.rest[i$1]) > lineN)
-        { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
-  }
-
-  // Render a line into the hidden node display.externalMeasured. Used
-  // when measurement is needed for a line that's not in the viewport.
-  function updateExternalMeasurement(cm, line) {
-    line = visualLine(line);
-    var lineN = lineNo(line);
-    var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
-    view.lineN = lineN;
-    var built = view.built = buildLineContent(cm, view);
-    view.text = built.pre;
-    removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
-    return view
-  }
-
-  // Get a {top, bottom, left, right} box (in line-local coordinates)
-  // for a given character.
-  function measureChar(cm, line, ch, bias) {
-    return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
-  }
-
-  // Find a line view that corresponds to the given line number.
-  function findViewForLine(cm, lineN) {
-    if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
-      { return cm.display.view[findViewIndex(cm, lineN)] }
-    var ext = cm.display.externalMeasured;
-    if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
-      { return ext }
-  }
-
-  // Measurement can be split in two steps, the set-up work that
-  // applies to the whole line, and the measurement of the actual
-  // character. Functions like coordsChar, that need to do a lot of
-  // measurements in a row, can thus ensure that the set-up work is
-  // only done once.
-  function prepareMeasureForLine(cm, line) {
-    var lineN = lineNo(line);
-    var view = findViewForLine(cm, lineN);
-    if (view && !view.text) {
-      view = null;
-    } else if (view && view.changes) {
-      updateLineForChanges(cm, view, lineN, getDimensions(cm));
-      cm.curOp.forceUpdate = true;
-    }
-    if (!view)
-      { view = updateExternalMeasurement(cm, line); }
-
-    var info = mapFromLineView(view, line, lineN);
-    return {
-      line: line, view: view, rect: null,
-      map: info.map, cache: info.cache, before: info.before,
-      hasHeights: false
-    }
-  }
-
-  // Given a prepared measurement object, measures the position of an
-  // actual character (or fetches it from the cache).
-  function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
-    if (prepared.before) { ch = -1; }
-    var key = ch + (bias || ""), found;
-    if (prepared.cache.hasOwnProperty(key)) {
-      found = prepared.cache[key];
-    } else {
-      if (!prepared.rect)
-        { prepared.rect = prepared.view.text.getBoundingClientRect(); }
-      if (!prepared.hasHeights) {
-        ensureLineHeights(cm, prepared.view, prepared.rect);
-        prepared.hasHeights = true;
-      }
-      found = measureCharInner(cm, prepared, ch, bias);
-      if (!found.bogus) { prepared.cache[key] = found; }
-    }
-    return {left: found.left, right: found.right,
-            top: varHeight ? found.rtop : found.top,
-            bottom: varHeight ? found.rbottom : found.bottom}
-  }
-
-  var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
-
-  function nodeAndOffsetInLineMap(map, ch, bias) {
-    var node, start, end, collapse, mStart, mEnd;
-    // First, search the line map for the text node corresponding to,
-    // or closest to, the target character.
-    for (var i = 0; i < map.length; i += 3) {
-      mStart = map[i];
-      mEnd = map[i + 1];
-      if (ch < mStart) {
-        start = 0; end = 1;
-        collapse = "left";
-      } else if (ch < mEnd) {
-        start = ch - mStart;
-        end = start + 1;
-      } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
-        end = mEnd - mStart;
-        start = end - 1;
-        if (ch >= mEnd) { collapse = "right"; }
-      }
-      if (start != null) {
-        node = map[i + 2];
-        if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
-          { collapse = bias; }
-        if (bias == "left" && start == 0)
-          { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
-            node = map[(i -= 3) + 2];
-            collapse = "left";
-          } }
-        if (bias == "right" && start == mEnd - mStart)
-          { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
-            node = map[(i += 3) + 2];
-            collapse = "right";
-          } }
-        break
-      }
-    }
-    return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
-  }
-
-  function getUsefulRect(rects, bias) {
-    var rect = nullRect;
-    if (bias == "left") { for (var i = 0; i < rects.length; i++) {
-      if ((rect = rects[i]).left != rect.right) { break }
-    } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
-      if ((rect = rects[i$1]).left != rect.right) { break }
-    } }
-    return rect
-  }
-
-  function measureCharInner(cm, prepared, ch, bias) {
-    var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
-    var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
-
-    var rect;
-    if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
-      for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
-        while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }
-        while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }
-        if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
-          { rect = node.parentNode.getBoundingClientRect(); }
-        else
-          { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }
-        if (rect.left || rect.right || start == 0) { break }
-        end = start;
-        start = start - 1;
-        collapse = "right";
-      }
-      if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }
-    } else { // If it is a widget, simply get the box for the whole widget.
-      if (start > 0) { collapse = bias = "right"; }
-      var rects;
-      if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
-        { rect = rects[bias == "right" ? rects.length - 1 : 0]; }
-      else
-        { rect = node.getBoundingClientRect(); }
-    }
-    if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
-      var rSpan = node.parentNode.getClientRects()[0];
-      if (rSpan)
-        { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }
-      else
-        { rect = nullRect; }
-    }
-
-    var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
-    var mid = (rtop + rbot) / 2;
-    var heights = prepared.view.measure.heights;
-    var i = 0;
-    for (; i < heights.length - 1; i++)
-      { if (mid < heights[i]) { break } }
-    var top = i ? heights[i - 1] : 0, bot = heights[i];
-    var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
-                  right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
-                  top: top, bottom: bot};
-    if (!rect.left && !rect.right) { result.bogus = true; }
-    if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
-
-    return result
-  }
-
-  // Work around problem with bounding client rects on ranges being
-  // returned incorrectly when zoomed on IE10 and below.
-  function maybeUpdateRectForZooming(measure, rect) {
-    if (!window.screen || screen.logicalXDPI == null ||
-        screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
-      { return rect }
-    var scaleX = screen.logicalXDPI / screen.deviceXDPI;
-    var scaleY = screen.logicalYDPI / screen.deviceYDPI;
-    return {left: rect.left * scaleX, right: rect.right * scaleX,
-            top: rect.top * scaleY, bottom: rect.bottom * scaleY}
-  }
-
-  function clearLineMeasurementCacheFor(lineView) {
-    if (lineView.measure) {
-      lineView.measure.cache = {};
-      lineView.measure.heights = null;
-      if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
-        { lineView.measure.caches[i] = {}; } }
-    }
-  }
-
-  function clearLineMeasurementCache(cm) {
-    cm.display.externalMeasure = null;
-    removeChildren(cm.display.lineMeasure);
-    for (var i = 0; i < cm.display.view.length; i++)
-      { clearLineMeasurementCacheFor(cm.display.view[i]); }
-  }
-
-  function clearCaches(cm) {
-    clearLineMeasurementCache(cm);
-    cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
-    if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }
-    cm.display.lineNumChars = null;
-  }
-
-  function pageScrollX() {
-    // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
-    // which causes page_Offset and bounding client rects to use
-    // different reference viewports and invalidate our calculations.
-    if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
-    return window.pageXOffset || (document.documentElement || document.body).scrollLeft
-  }
-  function pageScrollY() {
-    if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
-    return window.pageYOffset || (document.documentElement || document.body).scrollTop
-  }
-
-  function widgetTopHeight(lineObj) {
-    var height = 0;
-    if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
-      { height += widgetHeight(lineObj.widgets[i]); } } }
-    return height
-  }
-
-  // Converts a {top, bottom, left, right} box from line-local
-  // coordinates into another coordinate system. Context may be one of
-  // "line", "div" (display.lineDiv), "local"./null (editor), "window",
-  // or "page".
-  function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
-    if (!includeWidgets) {
-      var height = widgetTopHeight(lineObj);
-      rect.top += height; rect.bottom += height;
-    }
-    if (context == "line") { return rect }
-    if (!context) { context = "local"; }
-    var yOff = heightAtLine(lineObj);
-    if (context == "local") { yOff += paddingTop(cm.display); }
-    else { yOff -= cm.display.viewOffset; }
-    if (context == "page" || context == "window") {
-      var lOff = cm.display.lineSpace.getBoundingClientRect();
-      yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
-      var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
-      rect.left += xOff; rect.right += xOff;
-    }
-    rect.top += yOff; rect.bottom += yOff;
-    return rect
-  }
-
-  // Coverts a box from "div" coords to another coordinate system.
-  // Context may be "window", "page", "div", or "local"./null.
-  function fromCoordSystem(cm, coords, context) {
-    if (context == "div") { return coords }
-    var left = coords.left, top = coords.top;
-    // First move into "page" coordinate system
-    if (context == "page") {
-      left -= pageScrollX();
-      top -= pageScrollY();
-    } else if (context == "local" || !context) {
-      var localBox = cm.display.sizer.getBoundingClientRect();
-      left += localBox.left;
-      top += localBox.top;
-    }
-
-    var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
-    return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
-  }
-
-  function charCoords(cm, pos, context, lineObj, bias) {
-    if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }
-    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
-  }
-
-  // Returns a box for a given cursor position, which may have an
-  // 'other' property containing the position of the secondary cursor
-  // on a bidi boundary.
-  // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
-  // and after `char - 1` in writing order of `char - 1`
-  // A cursor Pos(line, char, "after") is on the same visual line as `char`
-  // and before `char` in writing order of `char`
-  // Examples (upper-case letters are RTL, lower-case are LTR):
-  //     Pos(0, 1, ...)
-  //     before   after
-  // ab     a|b     a|b
-  // aB     a|B     aB|
-  // Ab     |Ab     A|b
-  // AB     B|A     B|A
-  // Every position after the last character on a line is considered to stick
-  // to the last character on the line.
-  function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
-    lineObj = lineObj || getLine(cm.doc, pos.line);
-    if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
-    function get(ch, right) {
-      var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
-      if (right) { m.left = m.right; } else { m.right = m.left; }
-      return intoCoordSystem(cm, lineObj, m, context)
-    }
-    var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;
-    if (ch >= lineObj.text.length) {
-      ch = lineObj.text.length;
-      sticky = "before";
-    } else if (ch <= 0) {
-      ch = 0;
-      sticky = "after";
-    }
-    if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
-
-    function getBidi(ch, partPos, invert) {
-      var part = order[partPos], right = part.level == 1;
-      return get(invert ? ch - 1 : ch, right != invert)
-    }
-    var partPos = getBidiPartAt(order, ch, sticky);
-    var other = bidiOther;
-    var val = getBidi(ch, partPos, sticky == "before");
-    if (other != null) { val.other = getBidi(ch, other, sticky != "before"); }
-    return val
-  }
-
-  // Used to cheaply estimate the coordinates for a position. Used for
-  // intermediate scroll updates.
-  function estimateCoords(cm, pos) {
-    var left = 0;
-    pos = clipPos(cm.doc, pos);
-    if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }
-    var lineObj = getLine(cm.doc, pos.line);
-    var top = heightAtLine(lineObj) + paddingTop(cm.display);
-    return {left: left, right: left, top: top, bottom: top + lineObj.height}
-  }
-
-  // Positions returned by coordsChar contain some extra information.
-  // xRel is the relative x position of the input coordinates compared
-  // to the found position (so xRel > 0 means the coordinates are to
-  // the right of the character position, for example). When outside
-  // is true, that means the coordinates lie outside the line's
-  // vertical range.
-  function PosWithInfo(line, ch, sticky, outside, xRel) {
-    var pos = Pos(line, ch, sticky);
-    pos.xRel = xRel;
-    if (outside) { pos.outside = outside; }
-    return pos
-  }
-
-  // Compute the character position closest to the given coordinates.
-  // Input must be lineSpace-local ("div" coordinate system).
-  function coordsChar(cm, x, y) {
-    var doc = cm.doc;
-    y += cm.display.viewOffset;
-    if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) }
-    var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
-    if (lineN > last)
-      { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) }
-    if (x < 0) { x = 0; }
-
-    var lineObj = getLine(doc, lineN);
-    for (;;) {
-      var found = coordsCharInner(cm, lineObj, lineN, x, y);
-      var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0));
-      if (!collapsed) { return found }
-      var rangeEnd = collapsed.find(1);
-      if (rangeEnd.line == lineN) { return rangeEnd }
-      lineObj = getLine(doc, lineN = rangeEnd.line);
-    }
-  }
-
-  function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
-    y -= widgetTopHeight(lineObj);
-    var end = lineObj.text.length;
-    var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);
-    end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);
-    return {begin: begin, end: end}
-  }
-
-  function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
-    if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
-    var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top;
-    return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
-  }
-
-  // Returns true if the given side of a box is after the given
-  // coordinates, in top-to-bottom, left-to-right order.
-  function boxIsAfter(box, x, y, left) {
-    return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
-  }
-
-  function coordsCharInner(cm, lineObj, lineNo, x, y) {
-    // Move y into line-local coordinate space
-    y -= heightAtLine(lineObj);
-    var preparedMeasure = prepareMeasureForLine(cm, lineObj);
-    // When directly calling `measureCharPrepared`, we have to adjust
-    // for the widgets at this line.
-    var widgetHeight = widgetTopHeight(lineObj);
-    var begin = 0, end = lineObj.text.length, ltr = true;
-
-    var order = getOrder(lineObj, cm.doc.direction);
-    // If the line isn't plain left-to-right text, first figure out
-    // which bidi section the coordinates fall into.
-    if (order) {
-      var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
-                   (cm, lineObj, lineNo, preparedMeasure, order, x, y);
-      ltr = part.level != 1;
-      // The awkward -1 offsets are needed because findFirst (called
-      // on these below) will treat its first bound as inclusive,
-      // second as exclusive, but we want to actually address the
-      // characters in the part's range
-      begin = ltr ? part.from : part.to - 1;
-      end = ltr ? part.to : part.from - 1;
-    }
-
-    // A binary search to find the first character whose bounding box
-    // starts after the coordinates. If we run across any whose box wrap
-    // the coordinates, store that.
-    var chAround = null, boxAround = null;
-    var ch = findFirst(function (ch) {
-      var box = measureCharPrepared(cm, preparedMeasure, ch);
-      box.top += widgetHeight; box.bottom += widgetHeight;
-      if (!boxIsAfter(box, x, y, false)) { return false }
-      if (box.top <= y && box.left <= x) {
-        chAround = ch;
-        boxAround = box;
-      }
-      return true
-    }, begin, end);
-
-    var baseX, sticky, outside = false;
-    // If a box around the coordinates was found, use that
-    if (boxAround) {
-      // Distinguish coordinates nearer to the left or right side of the box
-      var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;
-      ch = chAround + (atStart ? 0 : 1);
-      sticky = atStart ? "after" : "before";
-      baseX = atLeft ? boxAround.left : boxAround.right;
-    } else {
-      // (Adjust for extended bound, if necessary.)
-      if (!ltr && (ch == end || ch == begin)) { ch++; }
-      // To determine which side to associate with, get the box to the
-      // left of the character and compare it's vertical position to the
-      // coordinates
-      sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
-        (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ?
-        "after" : "before";
-      // Now get accurate coordinates for this place, in order to get a
-      // base X position
-      var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure);
-      baseX = coords.left;
-      outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0;
-    }
-
-    ch = skipExtendingChars(lineObj.text, ch, 1);
-    return PosWithInfo(lineNo, ch, sticky, outside, x - baseX)
-  }
-
-  function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) {
-    // Bidi parts are sorted left-to-right, and in a non-line-wrapping
-    // situation, we can take this ordering to correspond to the visual
-    // ordering. This finds the first part whose end is after the given
-    // coordinates.
-    var index = findFirst(function (i) {
-      var part = order[i], ltr = part.level != 1;
-      return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"),
-                                     "line", lineObj, preparedMeasure), x, y, true)
-    }, 0, order.length - 1);
-    var part = order[index];
-    // If this isn't the first part, the part's start is also after
-    // the coordinates, and the coordinates aren't on the same line as
-    // that start, move one part back.
-    if (index > 0) {
-      var ltr = part.level != 1;
-      var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"),
-                               "line", lineObj, preparedMeasure);
-      if (boxIsAfter(start, x, y, true) && start.top > y)
-        { part = order[index - 1]; }
-    }
-    return part
-  }
-
-  function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
-    // In a wrapped line, rtl text on wrapping boundaries can do things
-    // that don't correspond to the ordering in our `order` array at
-    // all, so a binary search doesn't work, and we want to return a
-    // part that only spans one line so that the binary search in
-    // coordsCharInner is safe. As such, we first find the extent of the
-    // wrapped line, and then do a flat search in which we discard any
-    // spans that aren't on the line.
-    var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
-    var begin = ref.begin;
-    var end = ref.end;
-    if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; }
-    var part = null, closestDist = null;
-    for (var i = 0; i < order.length; i++) {
-      var p = order[i];
-      if (p.from >= end || p.to <= begin) { continue }
-      var ltr = p.level != 1;
-      var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;
-      // Weigh against spans ending before this, so that they are only
-      // picked if nothing ends after
-      var dist = endX < x ? x - endX + 1e9 : endX - x;
-      if (!part || closestDist > dist) {
-        part = p;
-        closestDist = dist;
-      }
-    }
-    if (!part) { part = order[order.length - 1]; }
-    // Clip the part to the wrapped line.
-    if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }
-    if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }
-    return part
-  }
-
-  var measureText;
-  // Compute the default text height.
-  function textHeight(display) {
-    if (display.cachedTextHeight != null) { return display.cachedTextHeight }
-    if (measureText == null) {
-      measureText = elt("pre", null, "CodeMirror-line-like");
-      // Measure a bunch of lines, for browsers that compute
-      // fractional heights.
-      for (var i = 0; i < 49; ++i) {
-        measureText.appendChild(document.createTextNode("x"));
-        measureText.appendChild(elt("br"));
-      }
-      measureText.appendChild(document.createTextNode("x"));
-    }
-    removeChildrenAndAdd(display.measure, measureText);
-    var height = measureText.offsetHeight / 50;
-    if (height > 3) { display.cachedTextHeight = height; }
-    removeChildren(display.measure);
-    return height || 1
-  }
-
-  // Compute the default character width.
-  function charWidth(display) {
-    if (display.cachedCharWidth != null) { return display.cachedCharWidth }
-    var anchor = elt("span", "xxxxxxxxxx");
-    var pre = elt("pre", [anchor], "CodeMirror-line-like");
-    removeChildrenAndAdd(display.measure, pre);
-    var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
-    if (width > 2) { display.cachedCharWidth = width; }
-    return width || 10
-  }
-
-  // Do a bulk-read of the DOM positions and sizes needed to draw the
-  // view, so that we don't interleave reading and writing to the DOM.
-  function getDimensions(cm) {
-    var d = cm.display, left = {}, width = {};
-    var gutterLeft = d.gutters.clientLeft;
-    for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
-      var id = cm.display.gutterSpecs[i].className;
-      left[id] = n.offsetLeft + n.clientLeft + gutterLeft;
-      width[id] = n.clientWidth;
-    }
-    return {fixedPos: compensateForHScroll(d),
-            gutterTotalWidth: d.gutters.offsetWidth,
-            gutterLeft: left,
-            gutterWidth: width,
-            wrapperWidth: d.wrapper.clientWidth}
-  }
-
-  // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
-  // but using getBoundingClientRect to get a sub-pixel-accurate
-  // result.
-  function compensateForHScroll(display) {
-    return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
-  }
-
-  // Returns a function that estimates the height of a line, to use as
-  // first approximation until the line becomes visible (and is thus
-  // properly measurable).
-  function estimateHeight(cm) {
-    var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
-    var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
-    return function (line) {
-      if (lineIsHidden(cm.doc, line)) { return 0 }
-
-      var widgetsHeight = 0;
-      if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
-        if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }
-      } }
-
-      if (wrapping)
-        { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
-      else
-        { return widgetsHeight + th }
-    }
-  }
-
-  function estimateLineHeights(cm) {
-    var doc = cm.doc, est = estimateHeight(cm);
-    doc.iter(function (line) {
-      var estHeight = est(line);
-      if (estHeight != line.height) { updateLineHeight(line, estHeight); }
-    });
-  }
-
-  // Given a mouse event, find the corresponding position. If liberal
-  // is false, it checks whether a gutter or scrollbar was clicked,
-  // and returns null if it was. forRect is used by rectangular
-  // selections, and tries to estimate a character position even for
-  // coordinates beyond the right of the text.
-  function posFromMouse(cm, e, liberal, forRect) {
-    var display = cm.display;
-    if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
-
-    var x, y, space = display.lineSpace.getBoundingClientRect();
-    // Fails unpredictably on IE[67] when mouse is dragged around quickly.
-    try { x = e.clientX - space.left; y = e.clientY - space.top; }
-    catch (e$1) { return null }
-    var coords = coordsChar(cm, x, y), line;
-    if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
-      var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
-      coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
-    }
-    return coords
-  }
-
-  // Find the view element corresponding to a given line. Return null
-  // when the line isn't visible.
-  function findViewIndex(cm, n) {
-    if (n >= cm.display.viewTo) { return null }
-    n -= cm.display.viewFrom;
-    if (n < 0) { return null }
-    var view = cm.display.view;
-    for (var i = 0; i < view.length; i++) {
-      n -= view[i].size;
-      if (n < 0) { return i }
-    }
-  }
-
-  // Updates the display.view data structure for a given change to the
-  // document. From and to are in pre-change coordinates. Lendiff is
-  // the amount of lines added or subtracted by the change. This is
-  // used for changes that span multiple lines, or change the way
-  // lines are divided into visual lines. regLineChange (below)
-  // registers single-line changes.
-  function regChange(cm, from, to, lendiff) {
-    if (from == null) { from = cm.doc.first; }
-    if (to == null) { to = cm.doc.first + cm.doc.size; }
-    if (!lendiff) { lendiff = 0; }
-
-    var display = cm.display;
-    if (lendiff && to < display.viewTo &&
-        (display.updateLineNumbers == null || display.updateLineNumbers > from))
-      { display.updateLineNumbers = from; }
-
-    cm.curOp.viewChanged = true;
-
-    if (from >= display.viewTo) { // Change after
-      if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
-        { resetView(cm); }
-    } else if (to <= display.viewFrom) { // Change before
-      if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
-        resetView(cm);
-      } else {
-        display.viewFrom += lendiff;
-        display.viewTo += lendiff;
-      }
-    } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
-      resetView(cm);
-    } else if (from <= display.viewFrom) { // Top overlap
-      var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
-      if (cut) {
-        display.view = display.view.slice(cut.index);
-        display.viewFrom = cut.lineN;
-        display.viewTo += lendiff;
-      } else {
-        resetView(cm);
-      }
-    } else if (to >= display.viewTo) { // Bottom overlap
-      var cut$1 = viewCuttingPoint(cm, from, from, -1);
-      if (cut$1) {
-        display.view = display.view.slice(0, cut$1.index);
-        display.viewTo = cut$1.lineN;
-      } else {
-        resetView(cm);
-      }
-    } else { // Gap in the middle
-      var cutTop = viewCuttingPoint(cm, from, from, -1);
-      var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
-      if (cutTop && cutBot) {
-        display.view = display.view.slice(0, cutTop.index)
-          .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
-          .concat(display.view.slice(cutBot.index));
-        display.viewTo += lendiff;
-      } else {
-        resetView(cm);
-      }
-    }
-
-    var ext = display.externalMeasured;
-    if (ext) {
-      if (to < ext.lineN)
-        { ext.lineN += lendiff; }
-      else if (from < ext.lineN + ext.size)
-        { display.externalMeasured = null; }
-    }
-  }
-
-  // Register a change to a single line. Type must be one of "text",
-  // "gutter", "class", "widget"
-  function regLineChange(cm, line, type) {
-    cm.curOp.viewChanged = true;
-    var display = cm.display, ext = cm.display.externalMeasured;
-    if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
-      { display.externalMeasured = null; }
-
-    if (line < display.viewFrom || line >= display.viewTo) { return }
-    var lineView = display.view[findViewIndex(cm, line)];
-    if (lineView.node == null) { return }
-    var arr = lineView.changes || (lineView.changes = []);
-    if (indexOf(arr, type) == -1) { arr.push(type); }
-  }
-
-  // Clear the view.
-  function resetView(cm) {
-    cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
-    cm.display.view = [];
-    cm.display.viewOffset = 0;
-  }
-
-  function viewCuttingPoint(cm, oldN, newN, dir) {
-    var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
-    if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
-      { return {index: index, lineN: newN} }
-    var n = cm.display.viewFrom;
-    for (var i = 0; i < index; i++)
-      { n += view[i].size; }
-    if (n != oldN) {
-      if (dir > 0) {
-        if (index == view.length - 1) { return null }
-        diff = (n + view[index].size) - oldN;
-        index++;
-      } else {
-        diff = n - oldN;
-      }
-      oldN += diff; newN += diff;
-    }
-    while (visualLineNo(cm.doc, newN) != newN) {
-      if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
-      newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
-      index += dir;
-    }
-    return {index: index, lineN: newN}
-  }
-
-  // Force the view to cover a given range, adding empty view element
-  // or clipping off existing ones as needed.
-  function adjustView(cm, from, to) {
-    var display = cm.display, view = display.view;
-    if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
-      display.view = buildViewArray(cm, from, to);
-      display.viewFrom = from;
-    } else {
-      if (display.viewFrom > from)
-        { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }
-      else if (display.viewFrom < from)
-        { display.view = display.view.slice(findViewIndex(cm, from)); }
-      display.viewFrom = from;
-      if (display.viewTo < to)
-        { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }
-      else if (display.viewTo > to)
-        { display.view = display.view.slice(0, findViewIndex(cm, to)); }
-    }
-    display.viewTo = to;
-  }
-
-  // Count the number of lines in the view whose DOM representation is
-  // out of date (or nonexistent).
-  function countDirtyView(cm) {
-    var view = cm.display.view, dirty = 0;
-    for (var i = 0; i < view.length; i++) {
-      var lineView = view[i];
-      if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }
-    }
-    return dirty
-  }
-
-  function updateSelection(cm) {
-    cm.display.input.showSelection(cm.display.input.prepareSelection());
-  }
-
-  function prepareSelection(cm, primary) {
-    if ( primary === void 0 ) primary = true;
-
-    var doc = cm.doc, result = {};
-    var curFragment = result.cursors = document.createDocumentFragment();
-    var selFragment = result.selection = document.createDocumentFragment();
-
-    for (var i = 0; i < doc.sel.ranges.length; i++) {
-      if (!primary && i == doc.sel.primIndex) { continue }
-      var range = doc.sel.ranges[i];
-      if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue }
-      var collapsed = range.empty();
-      if (collapsed || cm.options.showCursorWhenSelecting)
-        { drawSelectionCursor(cm, range.head, curFragment); }
-      if (!collapsed)
-        { drawSelectionRange(cm, range, selFragment); }
-    }
-    return result
-  }
-
-  // Draws a cursor for the given range
-  function drawSelectionCursor(cm, head, output) {
-    var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
-
-    var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
-    cursor.style.left = pos.left + "px";
-    cursor.style.top = pos.top + "px";
-    cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
-
-    if (pos.other) {
-      // Secondary cursor, shown when on a 'jump' in bi-directional text
-      var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
-      otherCursor.style.display = "";
-      otherCursor.style.left = pos.other.left + "px";
-      otherCursor.style.top = pos.other.top + "px";
-      otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
-    }
-  }
-
-  function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
-
-  // Draws the given range as a highlighted selection
-  function drawSelectionRange(cm, range, output) {
-    var display = cm.display, doc = cm.doc;
-    var fragment = document.createDocumentFragment();
-    var padding = paddingH(cm.display), leftSide = padding.left;
-    var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
-    var docLTR = doc.direction == "ltr";
-
-    function add(left, top, width, bottom) {
-      if (top < 0) { top = 0; }
-      top = Math.round(top);
-      bottom = Math.round(bottom);
-      fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n                             top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n                             height: " + (bottom - top) + "px")));
-    }
-
-    function drawForLine(line, fromArg, toArg) {
-      var lineObj = getLine(doc, line);
-      var lineLen = lineObj.text.length;
-      var start, end;
-      function coords(ch, bias) {
-        return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
-      }
-
-      function wrapX(pos, dir, side) {
-        var extent = wrappedLineExtentChar(cm, lineObj, null, pos);
-        var prop = (dir == "ltr") == (side == "after") ? "left" : "right";
-        var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);
-        return coords(ch, prop)[prop]
-      }
-
-      var order = getOrder(lineObj, doc.direction);
-      iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
-        var ltr = dir == "ltr";
-        var fromPos = coords(from, ltr ? "left" : "right");
-        var toPos = coords(to - 1, ltr ? "right" : "left");
-
-        var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;
-        var first = i == 0, last = !order || i == order.length - 1;
-        if (toPos.top - fromPos.top <= 3) { // Single line
-          var openLeft = (docLTR ? openStart : openEnd) && first;
-          var openRight = (docLTR ? openEnd : openStart) && last;
-          var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;
-          var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;
-          add(left, fromPos.top, right - left, fromPos.bottom);
-        } else { // Multiple lines
-          var topLeft, topRight, botLeft, botRight;
-          if (ltr) {
-            topLeft = docLTR && openStart && first ? leftSide : fromPos.left;
-            topRight = docLTR ? rightSide : wrapX(from, dir, "before");
-            botLeft = docLTR ? leftSide : wrapX(to, dir, "after");
-            botRight = docLTR && openEnd && last ? rightSide : toPos.right;
-          } else {
-            topLeft = !docLTR ? leftSide : wrapX(from, dir, "before");
-            topRight = !docLTR && openStart && first ? rightSide : fromPos.right;
-            botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;
-            botRight = !docLTR ? rightSide : wrapX(to, dir, "after");
-          }
-          add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);
-          if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }
-          add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);
-        }
-
-        if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }
-        if (cmpCoords(toPos, start) < 0) { start = toPos; }
-        if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }
-        if (cmpCoords(toPos, end) < 0) { end = toPos; }
-      });
-      return {start: start, end: end}
-    }
-
-    var sFrom = range.from(), sTo = range.to();
-    if (sFrom.line == sTo.line) {
-      drawForLine(sFrom.line, sFrom.ch, sTo.ch);
-    } else {
-      var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
-      var singleVLine = visualLine(fromLine) == visualLine(toLine);
-      var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
-      var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
-      if (singleVLine) {
-        if (leftEnd.top < rightStart.top - 2) {
-          add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
-          add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
-        } else {
-          add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
-        }
-      }
-      if (leftEnd.bottom < rightStart.top)
-        { add(leftSide, leftEnd.bottom, null, rightStart.top); }
-    }
-
-    output.appendChild(fragment);
-  }
-
-  // Cursor-blinking
-  function restartBlink(cm) {
-    if (!cm.state.focused) { return }
-    var display = cm.display;
-    clearInterval(display.blinker);
-    var on = true;
-    display.cursorDiv.style.visibility = "";
-    if (cm.options.cursorBlinkRate > 0)
-      { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
-        cm.options.cursorBlinkRate); }
-    else if (cm.options.cursorBlinkRate < 0)
-      { display.cursorDiv.style.visibility = "hidden"; }
-  }
-
-  function ensureFocus(cm) {
-    if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
-  }
-
-  function delayBlurEvent(cm) {
-    cm.state.delayingBlurEvent = true;
-    setTimeout(function () { if (cm.state.delayingBlurEvent) {
-      cm.state.delayingBlurEvent = false;
-      onBlur(cm);
-    } }, 100);
-  }
-
-  function onFocus(cm, e) {
-    if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; }
-
-    if (cm.options.readOnly == "nocursor") { return }
-    if (!cm.state.focused) {
-      signal(cm, "focus", cm, e);
-      cm.state.focused = true;
-      addClass(cm.display.wrapper, "CodeMirror-focused");
-      // This test prevents this from firing when a context
-      // menu is closed (since the input reset would kill the
-      // select-all detection hack)
-      if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
-        cm.display.input.reset();
-        if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730
-      }
-      cm.display.input.receivedFocus();
-    }
-    restartBlink(cm);
-  }
-  function onBlur(cm, e) {
-    if (cm.state.delayingBlurEvent) { return }
-
-    if (cm.state.focused) {
-      signal(cm, "blur", cm, e);
-      cm.state.focused = false;
-      rmClass(cm.display.wrapper, "CodeMirror-focused");
-    }
-    clearInterval(cm.display.blinker);
-    setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);
-  }
-
-  // Read the actual heights of the rendered lines, and update their
-  // stored heights to match.
-  function updateHeightsInViewport(cm) {
-    var display = cm.display;
-    var prevBottom = display.lineDiv.offsetTop;
-    for (var i = 0; i < display.view.length; i++) {
-      var cur = display.view[i], wrapping = cm.options.lineWrapping;
-      var height = (void 0), width = 0;
-      if (cur.hidden) { continue }
-      if (ie && ie_version < 8) {
-        var bot = cur.node.offsetTop + cur.node.offsetHeight;
-        height = bot - prevBottom;
-        prevBottom = bot;
-      } else {
-        var box = cur.node.getBoundingClientRect();
-        height = box.bottom - box.top;
-        // Check that lines don't extend past the right of the current
-        // editor width
-        if (!wrapping && cur.text.firstChild)
-          { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; }
-      }
-      var diff = cur.line.height - height;
-      if (diff > .005 || diff < -.005) {
-        updateLineHeight(cur.line, height);
-        updateWidgetHeight(cur.line);
-        if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
-          { updateWidgetHeight(cur.rest[j]); } }
-      }
-      if (width > cm.display.sizerWidth) {
-        var chWidth = Math.ceil(width / charWidth(cm.display));
-        if (chWidth > cm.display.maxLineLength) {
-          cm.display.maxLineLength = chWidth;
-          cm.display.maxLine = cur.line;
-          cm.display.maxLineChanged = true;
-        }
-      }
-    }
-  }
-
-  // Read and store the height of line widgets associated with the
-  // given line.
-  function updateWidgetHeight(line) {
-    if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
-      var w = line.widgets[i], parent = w.node.parentNode;
-      if (parent) { w.height = parent.offsetHeight; }
-    } }
-  }
-
-  // Compute the lines that are visible in a given viewport (defaults
-  // the the current scroll position). viewport may contain top,
-  // height, and ensure (see op.scrollToPos) properties.
-  function visibleLines(display, doc, viewport) {
-    var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
-    top = Math.floor(top - paddingTop(display));
-    var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
-
-    var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
-    // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
-    // forces those lines into the viewport (if possible).
-    if (viewport && viewport.ensure) {
-      var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
-      if (ensureFrom < from) {
-        from = ensureFrom;
-        to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
-      } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
-        from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
-        to = ensureTo;
-      }
-    }
-    return {from: from, to: Math.max(to, from + 1)}
-  }
-
-  // SCROLLING THINGS INTO VIEW
-
-  // If an editor sits on the top or bottom of the window, partially
-  // scrolled out of view, this ensures that the cursor is visible.
-  function maybeScrollWindow(cm, rect) {
-    if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
-
-    var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
-    if (rect.top + box.top < 0) { doScroll = true; }
-    else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; }
-    if (doScroll != null && !phantom) {
-      var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n                         top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n                         height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n                         left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;"));
-      cm.display.lineSpace.appendChild(scrollNode);
-      scrollNode.scrollIntoView(doScroll);
-      cm.display.lineSpace.removeChild(scrollNode);
-    }
-  }
-
-  // Scroll a given position into view (immediately), verifying that
-  // it actually became visible (as line heights are accurately
-  // measured, the position of something may 'drift' during drawing).
-  function scrollPosIntoView(cm, pos, end, margin) {
-    if (margin == null) { margin = 0; }
-    var rect;
-    if (!cm.options.lineWrapping && pos == end) {
-      // Set pos and end to the cursor positions around the character pos sticks to
-      // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
-      // If pos == Pos(_, 0, "before"), pos and end are unchanged
-      pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos;
-      end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos;
-    }
-    for (var limit = 0; limit < 5; limit++) {
-      var changed = false;
-      var coords = cursorCoords(cm, pos);
-      var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
-      rect = {left: Math.min(coords.left, endCoords.left),
-              top: Math.min(coords.top, endCoords.top) - margin,
-              right: Math.max(coords.left, endCoords.left),
-              bottom: Math.max(coords.bottom, endCoords.bottom) + margin};
-      var scrollPos = calculateScrollPos(cm, rect);
-      var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
-      if (scrollPos.scrollTop != null) {
-        updateScrollTop(cm, scrollPos.scrollTop);
-        if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }
-      }
-      if (scrollPos.scrollLeft != null) {
-        setScrollLeft(cm, scrollPos.scrollLeft);
-        if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }
-      }
-      if (!changed) { break }
-    }
-    return rect
-  }
-
-  // Scroll a given set of coordinates into view (immediately).
-  function scrollIntoView(cm, rect) {
-    var scrollPos = calculateScrollPos(cm, rect);
-    if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }
-    if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }
-  }
-
-  // Calculate a new scroll position needed to scroll the given
-  // rectangle into view. Returns an object with scrollTop and
-  // scrollLeft properties. When these are undefined, the
-  // vertical/horizontal position does not need to be adjusted.
-  function calculateScrollPos(cm, rect) {
-    var display = cm.display, snapMargin = textHeight(cm.display);
-    if (rect.top < 0) { rect.top = 0; }
-    var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
-    var screen = displayHeight(cm), result = {};
-    if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }
-    var docBottom = cm.doc.height + paddingVert(display);
-    var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;
-    if (rect.top < screentop) {
-      result.scrollTop = atTop ? 0 : rect.top;
-    } else if (rect.bottom > screentop + screen) {
-      var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);
-      if (newTop != screentop) { result.scrollTop = newTop; }
-    }
-
-    var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
-    var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
-    var tooWide = rect.right - rect.left > screenw;
-    if (tooWide) { rect.right = rect.left + screenw; }
-    if (rect.left < 10)
-      { result.scrollLeft = 0; }
-    else if (rect.left < screenleft)
-      { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); }
-    else if (rect.right > screenw + screenleft - 3)
-      { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }
-    return result
-  }
-
-  // Store a relative adjustment to the scroll position in the current
-  // operation (to be applied when the operation finishes).
-  function addToScrollTop(cm, top) {
-    if (top == null) { return }
-    resolveScrollToPos(cm);
-    cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
-  }
-
-  // Make sure that at the end of the operation the current cursor is
-  // shown.
-  function ensureCursorVisible(cm) {
-    resolveScrollToPos(cm);
-    var cur = cm.getCursor();
-    cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};
-  }
-
-  function scrollToCoords(cm, x, y) {
-    if (x != null || y != null) { resolveScrollToPos(cm); }
-    if (x != null) { cm.curOp.scrollLeft = x; }
-    if (y != null) { cm.curOp.scrollTop = y; }
-  }
-
-  function scrollToRange(cm, range) {
-    resolveScrollToPos(cm);
-    cm.curOp.scrollToPos = range;
-  }
-
-  // When an operation has its scrollToPos property set, and another
-  // scroll action is applied before the end of the operation, this
-  // 'simulates' scrolling that position into view in a cheap way, so
-  // that the effect of intermediate scroll commands is not ignored.
-  function resolveScrollToPos(cm) {
-    var range = cm.curOp.scrollToPos;
-    if (range) {
-      cm.curOp.scrollToPos = null;
-      var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);
-      scrollToCoordsRange(cm, from, to, range.margin);
-    }
-  }
-
-  function scrollToCoordsRange(cm, from, to, margin) {
-    var sPos = calculateScrollPos(cm, {
-      left: Math.min(from.left, to.left),
-      top: Math.min(from.top, to.top) - margin,
-      right: Math.max(from.right, to.right),
-      bottom: Math.max(from.bottom, to.bottom) + margin
-    });
-    scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);
-  }
-
-  // Sync the scrollable area and scrollbars, ensure the viewport
-  // covers the visible area.
-  function updateScrollTop(cm, val) {
-    if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
-    if (!gecko) { updateDisplaySimple(cm, {top: val}); }
-    setScrollTop(cm, val, true);
-    if (gecko) { updateDisplaySimple(cm); }
-    startWorker(cm, 100);
-  }
-
-  function setScrollTop(cm, val, forceScroll) {
-    val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val));
-    if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
-    cm.doc.scrollTop = val;
-    cm.display.scrollbars.setScrollTop(val);
-    if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }
-  }
-
-  // Sync scroller and scrollbar, ensure the gutter elements are
-  // aligned.
-  function setScrollLeft(cm, val, isScroller, forceScroll) {
-    val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth));
-    if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
-    cm.doc.scrollLeft = val;
-    alignHorizontally(cm);
-    if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }
-    cm.display.scrollbars.setScrollLeft(val);
-  }
-
-  // SCROLLBARS
-
-  // Prepare DOM reads needed to update the scrollbars. Done in one
-  // shot to minimize update/measure roundtrips.
-  function measureForScrollbars(cm) {
-    var d = cm.display, gutterW = d.gutters.offsetWidth;
-    var docH = Math.round(cm.doc.height + paddingVert(cm.display));
-    return {
-      clientHeight: d.scroller.clientHeight,
-      viewHeight: d.wrapper.clientHeight,
-      scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
-      viewWidth: d.wrapper.clientWidth,
-      barLeft: cm.options.fixedGutter ? gutterW : 0,
-      docHeight: docH,
-      scrollHeight: docH + scrollGap(cm) + d.barHeight,
-      nativeBarWidth: d.nativeBarWidth,
-      gutterWidth: gutterW
-    }
-  }
-
-  var NativeScrollbars = function(place, scroll, cm) {
-    this.cm = cm;
-    var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
-    var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
-    vert.tabIndex = horiz.tabIndex = -1;
-    place(vert); place(horiz);
-
-    on(vert, "scroll", function () {
-      if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); }
-    });
-    on(horiz, "scroll", function () {
-      if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); }
-    });
-
-    this.checkedZeroWidth = false;
-    // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
-    if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; }
-  };
-
-  NativeScrollbars.prototype.update = function (measure) {
-    var needsH = measure.scrollWidth > measure.clientWidth + 1;
-    var needsV = measure.scrollHeight > measure.clientHeight + 1;
-    var sWidth = measure.nativeBarWidth;
-
-    if (needsV) {
-      this.vert.style.display = "block";
-      this.vert.style.bottom = needsH ? sWidth + "px" : "0";
-      var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
-      // A bug in IE8 can cause this value to be negative, so guard it.
-      this.vert.firstChild.style.height =
-        Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
-    } else {
-      this.vert.style.display = "";
-      this.vert.firstChild.style.height = "0";
-    }
-
-    if (needsH) {
-      this.horiz.style.display = "block";
-      this.horiz.style.right = needsV ? sWidth + "px" : "0";
-      this.horiz.style.left = measure.barLeft + "px";
-      var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
-      this.horiz.firstChild.style.width =
-        Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
-    } else {
-      this.horiz.style.display = "";
-      this.horiz.firstChild.style.width = "0";
-    }
-
-    if (!this.checkedZeroWidth && measure.clientHeight > 0) {
-      if (sWidth == 0) { this.zeroWidthHack(); }
-      this.checkedZeroWidth = true;
-    }
-
-    return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
-  };
-
-  NativeScrollbars.prototype.setScrollLeft = function (pos) {
-    if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }
-    if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); }
-  };
-
-  NativeScrollbars.prototype.setScrollTop = function (pos) {
-    if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }
-    if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); }
-  };
-
-  NativeScrollbars.prototype.zeroWidthHack = function () {
-    var w = mac && !mac_geMountainLion ? "12px" : "18px";
-    this.horiz.style.height = this.vert.style.width = w;
-    this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
-    this.disableHoriz = new Delayed;
-    this.disableVert = new Delayed;
-  };
-
-  NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
-    bar.style.pointerEvents = "auto";
-    function maybeDisable() {
-      // To find out whether the scrollbar is still visible, we
-      // check whether the element under the pixel in the bottom
-      // right corner of the scrollbar box is the scrollbar box
-      // itself (when the bar is still visible) or its filler child
-      // (when the bar is hidden). If it is still visible, we keep
-      // it enabled, if it's hidden, we disable pointer events.
-      var box = bar.getBoundingClientRect();
-      var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
-          : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);
-      if (elt != bar) { bar.style.pointerEvents = "none"; }
-      else { delay.set(1000, maybeDisable); }
-    }
-    delay.set(1000, maybeDisable);
-  };
-
-  NativeScrollbars.prototype.clear = function () {
-    var parent = this.horiz.parentNode;
-    parent.removeChild(this.horiz);
-    parent.removeChild(this.vert);
-  };
-
-  var NullScrollbars = function () {};
-
-  NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
-  NullScrollbars.prototype.setScrollLeft = function () {};
-  NullScrollbars.prototype.setScrollTop = function () {};
-  NullScrollbars.prototype.clear = function () {};
-
-  function updateScrollbars(cm, measure) {
-    if (!measure) { measure = measureForScrollbars(cm); }
-    var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
-    updateScrollbarsInner(cm, measure);
-    for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
-      if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
-        { updateHeightsInViewport(cm); }
-      updateScrollbarsInner(cm, measureForScrollbars(cm));
-      startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
-    }
-  }
-
-  // Re-synchronize the fake scrollbars with the actual size of the
-  // content.
-  function updateScrollbarsInner(cm, measure) {
-    var d = cm.display;
-    var sizes = d.scrollbars.update(measure);
-
-    d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
-    d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
-    d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent";
-
-    if (sizes.right && sizes.bottom) {
-      d.scrollbarFiller.style.display = "block";
-      d.scrollbarFiller.style.height = sizes.bottom + "px";
-      d.scrollbarFiller.style.width = sizes.right + "px";
-    } else { d.scrollbarFiller.style.display = ""; }
-    if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
-      d.gutterFiller.style.display = "block";
-      d.gutterFiller.style.height = sizes.bottom + "px";
-      d.gutterFiller.style.width = measure.gutterWidth + "px";
-    } else { d.gutterFiller.style.display = ""; }
-  }
-
-  var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
-
-  function initScrollbars(cm) {
-    if (cm.display.scrollbars) {
-      cm.display.scrollbars.clear();
-      if (cm.display.scrollbars.addClass)
-        { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
-    }
-
-    cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
-      cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
-      // Prevent clicks in the scrollbars from killing focus
-      on(node, "mousedown", function () {
-        if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }
-      });
-      node.setAttribute("cm-not-content", "true");
-    }, function (pos, axis) {
-      if (axis == "horizontal") { setScrollLeft(cm, pos); }
-      else { updateScrollTop(cm, pos); }
-    }, cm);
-    if (cm.display.scrollbars.addClass)
-      { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
-  }
-
-  // Operations are used to wrap a series of changes to the editor
-  // state in such a way that each change won't have to update the
-  // cursor and display (which would be awkward, slow, and
-  // error-prone). Instead, display updates are batched and then all
-  // combined and executed at once.
-
-  var nextOpId = 0;
-  // Start a new operation.
-  function startOperation(cm) {
-    cm.curOp = {
-      cm: cm,
-      viewChanged: false,      // Flag that indicates that lines might need to be redrawn
-      startHeight: cm.doc.height, // Used to detect need to update scrollbar
-      forceUpdate: false,      // Used to force a redraw
-      updateInput: 0,       // Whether to reset the input textarea
-      typing: false,           // Whether this reset should be careful to leave existing text (for compositing)
-      changeObjs: null,        // Accumulated changes, for firing change events
-      cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
-      cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
-      selectionChanged: false, // Whether the selection needs to be redrawn
-      updateMaxLine: false,    // Set when the widest line needs to be determined anew
-      scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
-      scrollToPos: null,       // Used to scroll to a specific position
-      focus: false,
-      id: ++nextOpId           // Unique ID
-    };
-    pushOperation(cm.curOp);
-  }
-
-  // Finish an operation, updating the display and signalling delayed events
-  function endOperation(cm) {
-    var op = cm.curOp;
-    if (op) { finishOperation(op, function (group) {
-      for (var i = 0; i < group.ops.length; i++)
-        { group.ops[i].cm.curOp = null; }
-      endOperations(group);
-    }); }
-  }
-
-  // The DOM updates done when an operation finishes are batched so
-  // that the minimum number of relayouts are required.
-  function endOperations(group) {
-    var ops = group.ops;
-    for (var i = 0; i < ops.length; i++) // Read DOM
-      { endOperation_R1(ops[i]); }
-    for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
-      { endOperation_W1(ops[i$1]); }
-    for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
-      { endOperation_R2(ops[i$2]); }
-    for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
-      { endOperation_W2(ops[i$3]); }
-    for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
-      { endOperation_finish(ops[i$4]); }
-  }
-
-  function endOperation_R1(op) {
-    var cm = op.cm, display = cm.display;
-    maybeClipScrollbars(cm);
-    if (op.updateMaxLine) { findMaxLine(cm); }
-
-    op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
-      op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
-                         op.scrollToPos.to.line >= display.viewTo) ||
-      display.maxLineChanged && cm.options.lineWrapping;
-    op.update = op.mustUpdate &&
-      new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
-  }
-
-  function endOperation_W1(op) {
-    op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
-  }
-
-  function endOperation_R2(op) {
-    var cm = op.cm, display = cm.display;
-    if (op.updatedDisplay) { updateHeightsInViewport(cm); }
-
-    op.barMeasure = measureForScrollbars(cm);
-
-    // If the max line changed since it was last measured, measure it,
-    // and ensure the document's width matches it.
-    // updateDisplay_W2 will use these properties to do the actual resizing
-    if (display.maxLineChanged && !cm.options.lineWrapping) {
-      op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
-      cm.display.sizerWidth = op.adjustWidthTo;
-      op.barMeasure.scrollWidth =
-        Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
-      op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
-    }
-
-    if (op.updatedDisplay || op.selectionChanged)
-      { op.preparedSelection = display.input.prepareSelection(); }
-  }
-
-  function endOperation_W2(op) {
-    var cm = op.cm;
-
-    if (op.adjustWidthTo != null) {
-      cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
-      if (op.maxScrollLeft < cm.doc.scrollLeft)
-        { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }
-      cm.display.maxLineChanged = false;
-    }
-
-    var takeFocus = op.focus && op.focus == activeElt();
-    if (op.preparedSelection)
-      { cm.display.input.showSelection(op.preparedSelection, takeFocus); }
-    if (op.updatedDisplay || op.startHeight != cm.doc.height)
-      { updateScrollbars(cm, op.barMeasure); }
-    if (op.updatedDisplay)
-      { setDocumentHeight(cm, op.barMeasure); }
-
-    if (op.selectionChanged) { restartBlink(cm); }
-
-    if (cm.state.focused && op.updateInput)
-      { cm.display.input.reset(op.typing); }
-    if (takeFocus) { ensureFocus(op.cm); }
-  }
-
-  function endOperation_finish(op) {
-    var cm = op.cm, display = cm.display, doc = cm.doc;
-
-    if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }
-
-    // Abort mouse wheel delta measurement, when scrolling explicitly
-    if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
-      { display.wheelStartX = display.wheelStartY = null; }
-
-    // Propagate the scroll position to the actual DOM scroller
-    if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }
-
-    if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }
-    // If we need to scroll a specific position into view, do so.
-    if (op.scrollToPos) {
-      var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
-                                   clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
-      maybeScrollWindow(cm, rect);
-    }
-
-    // Fire events for markers that are hidden/unidden by editing or
-    // undoing
-    var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
-    if (hidden) { for (var i = 0; i < hidden.length; ++i)
-      { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } }
-    if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
-      { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } }
-
-    if (display.wrapper.offsetHeight)
-      { doc.scrollTop = cm.display.scroller.scrollTop; }
-
-    // Fire change events, and delayed event handlers
-    if (op.changeObjs)
-      { signal(cm, "changes", cm, op.changeObjs); }
-    if (op.update)
-      { op.update.finish(); }
-  }
-
-  // Run the given function in an operation
-  function runInOp(cm, f) {
-    if (cm.curOp) { return f() }
-    startOperation(cm);
-    try { return f() }
-    finally { endOperation(cm); }
-  }
-  // Wraps a function in an operation. Returns the wrapped function.
-  function operation(cm, f) {
-    return function() {
-      if (cm.curOp) { return f.apply(cm, arguments) }
-      startOperation(cm);
-      try { return f.apply(cm, arguments) }
-      finally { endOperation(cm); }
-    }
-  }
-  // Used to add methods to editor and doc instances, wrapping them in
-  // operations.
-  function methodOp(f) {
-    return function() {
-      if (this.curOp) { return f.apply(this, arguments) }
-      startOperation(this);
-      try { return f.apply(this, arguments) }
-      finally { endOperation(this); }
-    }
-  }
-  function docMethodOp(f) {
-    return function() {
-      var cm = this.cm;
-      if (!cm || cm.curOp) { return f.apply(this, arguments) }
-      startOperation(cm);
-      try { return f.apply(this, arguments) }
-      finally { endOperation(cm); }
-    }
-  }
-
-  // HIGHLIGHT WORKER
-
-  function startWorker(cm, time) {
-    if (cm.doc.highlightFrontier < cm.display.viewTo)
-      { cm.state.highlight.set(time, bind(highlightWorker, cm)); }
-  }
-
-  function highlightWorker(cm) {
-    var doc = cm.doc;
-    if (doc.highlightFrontier >= cm.display.viewTo) { return }
-    var end = +new Date + cm.options.workTime;
-    var context = getContextBefore(cm, doc.highlightFrontier);
-    var changedLines = [];
-
-    doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
-      if (context.line >= cm.display.viewFrom) { // Visible
-        var oldStyles = line.styles;
-        var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;
-        var highlighted = highlightLine(cm, line, context, true);
-        if (resetState) { context.state = resetState; }
-        line.styles = highlighted.styles;
-        var oldCls = line.styleClasses, newCls = highlighted.classes;
-        if (newCls) { line.styleClasses = newCls; }
-        else if (oldCls) { line.styleClasses = null; }
-        var ischange = !oldStyles || oldStyles.length != line.styles.length ||
-          oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
-        for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }
-        if (ischange) { changedLines.push(context.line); }
-        line.stateAfter = context.save();
-        context.nextLine();
-      } else {
-        if (line.text.length <= cm.options.maxHighlightLength)
-          { processLine(cm, line.text, context); }
-        line.stateAfter = context.line % 5 == 0 ? context.save() : null;
-        context.nextLine();
-      }
-      if (+new Date > end) {
-        startWorker(cm, cm.options.workDelay);
-        return true
-      }
-    });
-    doc.highlightFrontier = context.line;
-    doc.modeFrontier = Math.max(doc.modeFrontier, context.line);
-    if (changedLines.length) { runInOp(cm, function () {
-      for (var i = 0; i < changedLines.length; i++)
-        { regLineChange(cm, changedLines[i], "text"); }
-    }); }
-  }
-
-  // DISPLAY DRAWING
-
-  var DisplayUpdate = function(cm, viewport, force) {
-    var display = cm.display;
-
-    this.viewport = viewport;
-    // Store some values that we'll need later (but don't want to force a relayout for)
-    this.visible = visibleLines(display, cm.doc, viewport);
-    this.editorIsHidden = !display.wrapper.offsetWidth;
-    this.wrapperHeight = display.wrapper.clientHeight;
-    this.wrapperWidth = display.wrapper.clientWidth;
-    this.oldDisplayWidth = displayWidth(cm);
-    this.force = force;
-    this.dims = getDimensions(cm);
-    this.events = [];
-  };
-
-  DisplayUpdate.prototype.signal = function (emitter, type) {
-    if (hasHandler(emitter, type))
-      { this.events.push(arguments); }
-  };
-  DisplayUpdate.prototype.finish = function () {
-    for (var i = 0; i < this.events.length; i++)
-      { signal.apply(null, this.events[i]); }
-  };
-
-  function maybeClipScrollbars(cm) {
-    var display = cm.display;
-    if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
-      display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
-      display.heightForcer.style.height = scrollGap(cm) + "px";
-      display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
-      display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
-      display.scrollbarsClipped = true;
-    }
-  }
-
-  function selectionSnapshot(cm) {
-    if (cm.hasFocus()) { return null }
-    var active = activeElt();
-    if (!active || !contains(cm.display.lineDiv, active)) { return null }
-    var result = {activeElt: active};
-    if (window.getSelection) {
-      var sel = window.getSelection();
-      if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
-        result.anchorNode = sel.anchorNode;
-        result.anchorOffset = sel.anchorOffset;
-        result.focusNode = sel.focusNode;
-        result.focusOffset = sel.focusOffset;
-      }
-    }
-    return result
-  }
-
-  function restoreSelection(snapshot) {
-    if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
-    snapshot.activeElt.focus();
-    if (!/^(INPUT|TEXTAREA)$/.test(snapshot.activeElt.nodeName) &&
-        snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
-      var sel = window.getSelection(), range = document.createRange();
-      range.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
-      range.collapse(false);
-      sel.removeAllRanges();
-      sel.addRange(range);
-      sel.extend(snapshot.focusNode, snapshot.focusOffset);
-    }
-  }
-
-  // Does the actual updating of the line display. Bails out
-  // (returning false) when there is nothing to be done and forced is
-  // false.
-  function updateDisplayIfNeeded(cm, update) {
-    var display = cm.display, doc = cm.doc;
-
-    if (update.editorIsHidden) {
-      resetView(cm);
-      return false
-    }
-
-    // Bail out if the visible area is already rendered and nothing changed.
-    if (!update.force &&
-        update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
-        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
-        display.renderedView == display.view && countDirtyView(cm) == 0)
-      { return false }
-
-    if (maybeUpdateLineNumberWidth(cm)) {
-      resetView(cm);
-      update.dims = getDimensions(cm);
-    }
-
-    // Compute a suitable new viewport (from & to)
-    var end = doc.first + doc.size;
-    var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
-    var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
-    if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }
-    if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }
-    if (sawCollapsedSpans) {
-      from = visualLineNo(cm.doc, from);
-      to = visualLineEndNo(cm.doc, to);
-    }
-
-    var different = from != display.viewFrom || to != display.viewTo ||
-      display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
-    adjustView(cm, from, to);
-
-    display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
-    // Position the mover div to align with the current scroll position
-    cm.display.mover.style.top = display.viewOffset + "px";
-
-    var toUpdate = countDirtyView(cm);
-    if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
-        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
-      { return false }
-
-    // For big changes, we hide the enclosing element during the
-    // update, since that speeds up the operations on most browsers.
-    var selSnapshot = selectionSnapshot(cm);
-    if (toUpdate > 4) { display.lineDiv.style.display = "none"; }
-    patchDisplay(cm, display.updateLineNumbers, update.dims);
-    if (toUpdate > 4) { display.lineDiv.style.display = ""; }
-    display.renderedView = display.view;
-    // There might have been a widget with a focused element that got
-    // hidden or updated, if so re-focus it.
-    restoreSelection(selSnapshot);
-
-    // Prevent selection and cursors from interfering with the scroll
-    // width and height.
-    removeChildren(display.cursorDiv);
-    removeChildren(display.selectionDiv);
-    display.gutters.style.height = display.sizer.style.minHeight = 0;
-
-    if (different) {
-      display.lastWrapHeight = update.wrapperHeight;
-      display.lastWrapWidth = update.wrapperWidth;
-      startWorker(cm, 400);
-    }
-
-    display.updateLineNumbers = null;
-
-    return true
-  }
-
-  function postUpdateDisplay(cm, update) {
-    var viewport = update.viewport;
-
-    for (var first = true;; first = false) {
-      if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
-        // Clip forced viewport to actual scrollable area.
-        if (viewport && viewport.top != null)
-          { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }
-        // Updated line heights might result in the drawn area not
-        // actually covering the viewport. Keep looping until it does.
-        update.visible = visibleLines(cm.display, cm.doc, viewport);
-        if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
-          { break }
-      } else if (first) {
-        update.visible = visibleLines(cm.display, cm.doc, viewport);
-      }
-      if (!updateDisplayIfNeeded(cm, update)) { break }
-      updateHeightsInViewport(cm);
-      var barMeasure = measureForScrollbars(cm);
-      updateSelection(cm);
-      updateScrollbars(cm, barMeasure);
-      setDocumentHeight(cm, barMeasure);
-      update.force = false;
-    }
-
-    update.signal(cm, "update", cm);
-    if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
-      update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
-      cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
-    }
-  }
-
-  function updateDisplaySimple(cm, viewport) {
-    var update = new DisplayUpdate(cm, viewport);
-    if (updateDisplayIfNeeded(cm, update)) {
-      updateHeightsInViewport(cm);
-      postUpdateDisplay(cm, update);
-      var barMeasure = measureForScrollbars(cm);
-      updateSelection(cm);
-      updateScrollbars(cm, barMeasure);
-      setDocumentHeight(cm, barMeasure);
-      update.finish();
-    }
-  }
-
-  // Sync the actual display DOM structure with display.view, removing
-  // nodes for lines that are no longer in view, and creating the ones
-  // that are not there yet, and updating the ones that are out of
-  // date.
-  function patchDisplay(cm, updateNumbersFrom, dims) {
-    var display = cm.display, lineNumbers = cm.options.lineNumbers;
-    var container = display.lineDiv, cur = container.firstChild;
-
-    function rm(node) {
-      var next = node.nextSibling;
-      // Works around a throw-scroll bug in OS X Webkit
-      if (webkit && mac && cm.display.currentWheelTarget == node)
-        { node.style.display = "none"; }
-      else
-        { node.parentNode.removeChild(node); }
-      return next
-    }
-
-    var view = display.view, lineN = display.viewFrom;
-    // Loop over the elements in the view, syncing cur (the DOM nodes
-    // in display.lineDiv) with the view as we go.
-    for (var i = 0; i < view.length; i++) {
-      var lineView = view[i];
-      if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
-        var node = buildLineElement(cm, lineView, lineN, dims);
-        container.insertBefore(node, cur);
-      } else { // Already drawn
-        while (cur != lineView.node) { cur = rm(cur); }
-        var updateNumber = lineNumbers && updateNumbersFrom != null &&
-          updateNumbersFrom <= lineN && lineView.lineNumber;
-        if (lineView.changes) {
-          if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; }
-          updateLineForChanges(cm, lineView, lineN, dims);
-        }
-        if (updateNumber) {
-          removeChildren(lineView.lineNumber);
-          lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
-        }
-        cur = lineView.node.nextSibling;
-      }
-      lineN += lineView.size;
-    }
-    while (cur) { cur = rm(cur); }
-  }
-
-  function updateGutterSpace(display) {
-    var width = display.gutters.offsetWidth;
-    display.sizer.style.marginLeft = width + "px";
-  }
-
-  function setDocumentHeight(cm, measure) {
-    cm.display.sizer.style.minHeight = measure.docHeight + "px";
-    cm.display.heightForcer.style.top = measure.docHeight + "px";
-    cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
-  }
-
-  // Re-align line numbers and gutter marks to compensate for
-  // horizontal scrolling.
-  function alignHorizontally(cm) {
-    var display = cm.display, view = display.view;
-    if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
-    var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
-    var gutterW = display.gutters.offsetWidth, left = comp + "px";
-    for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
-      if (cm.options.fixedGutter) {
-        if (view[i].gutter)
-          { view[i].gutter.style.left = left; }
-        if (view[i].gutterBackground)
-          { view[i].gutterBackground.style.left = left; }
-      }
-      var align = view[i].alignable;
-      if (align) { for (var j = 0; j < align.length; j++)
-        { align[j].style.left = left; } }
-    } }
-    if (cm.options.fixedGutter)
-      { display.gutters.style.left = (comp + gutterW) + "px"; }
-  }
-
-  // Used to ensure that the line number gutter is still the right
-  // size for the current document size. Returns true when an update
-  // is needed.
-  function maybeUpdateLineNumberWidth(cm) {
-    if (!cm.options.lineNumbers) { return false }
-    var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
-    if (last.length != display.lineNumChars) {
-      var test = display.measure.appendChild(elt("div", [elt("div", last)],
-                                                 "CodeMirror-linenumber CodeMirror-gutter-elt"));
-      var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
-      display.lineGutter.style.width = "";
-      display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
-      display.lineNumWidth = display.lineNumInnerWidth + padding;
-      display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
-      display.lineGutter.style.width = display.lineNumWidth + "px";
-      updateGutterSpace(cm.display);
-      return true
-    }
-    return false
-  }
-
-  function getGutters(gutters, lineNumbers) {
-    var result = [], sawLineNumbers = false;
-    for (var i = 0; i < gutters.length; i++) {
-      var name = gutters[i], style = null;
-      if (typeof name != "string") { style = name.style; name = name.className; }
-      if (name == "CodeMirror-linenumbers") {
-        if (!lineNumbers) { continue }
-        else { sawLineNumbers = true; }
-      }
-      result.push({className: name, style: style});
-    }
-    if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); }
-    return result
-  }
-
-  // Rebuild the gutter elements, ensure the margin to the left of the
-  // code matches their width.
-  function renderGutters(display) {
-    var gutters = display.gutters, specs = display.gutterSpecs;
-    removeChildren(gutters);
-    display.lineGutter = null;
-    for (var i = 0; i < specs.length; ++i) {
-      var ref = specs[i];
-      var className = ref.className;
-      var style = ref.style;
-      var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className));
-      if (style) { gElt.style.cssText = style; }
-      if (className == "CodeMirror-linenumbers") {
-        display.lineGutter = gElt;
-        gElt.style.width = (display.lineNumWidth || 1) + "px";
-      }
-    }
-    gutters.style.display = specs.length ? "" : "none";
-    updateGutterSpace(display);
-  }
-
-  function updateGutters(cm) {
-    renderGutters(cm.display);
-    regChange(cm);
-    alignHorizontally(cm);
-  }
-
-  // The display handles the DOM integration, both for input reading
-  // and content drawing. It holds references to DOM nodes and
-  // display-related state.
-
-  function Display(place, doc, input, options) {
-    var d = this;
-    this.input = input;
-
-    // Covers bottom-right square when both scrollbars are present.
-    d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
-    d.scrollbarFiller.setAttribute("cm-not-content", "true");
-    // Covers bottom of gutter when coverGutterNextToScrollbar is on
-    // and h scrollbar is present.
-    d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
-    d.gutterFiller.setAttribute("cm-not-content", "true");
-    // Will contain the actual code, positioned to cover the viewport.
-    d.lineDiv = eltP("div", null, "CodeMirror-code");
-    // Elements are added to these to represent selection and cursors.
-    d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
-    d.cursorDiv = elt("div", null, "CodeMirror-cursors");
-    // A visibility: hidden element used to find the size of things.
-    d.measure = elt("div", null, "CodeMirror-measure");
-    // When lines outside of the viewport are measured, they are drawn in this.
-    d.lineMeasure = elt("div", null, "CodeMirror-measure");
-    // Wraps everything that needs to exist inside the vertically-padded coordinate system
-    d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
-                      null, "position: relative; outline: none");
-    var lines = eltP("div", [d.lineSpace], "CodeMirror-lines");
-    // Moved around its parent to cover visible view.
-    d.mover = elt("div", [lines], null, "position: relative");
-    // Set to the height of the document, allowing scrolling.
-    d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
-    d.sizerWidth = null;
-    // Behavior of elts with overflow: auto and padding is
-    // inconsistent across browsers. This is used to ensure the
-    // scrollable area is big enough.
-    d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
-    // Will contain the gutters, if any.
-    d.gutters = elt("div", null, "CodeMirror-gutters");
-    d.lineGutter = null;
-    // Actual scrollable element.
-    d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
-    d.scroller.setAttribute("tabIndex", "-1");
-    // The element in which the editor lives.
-    d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
-
-    // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
-    if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
-    if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }
-
-    if (place) {
-      if (place.appendChild) { place.appendChild(d.wrapper); }
-      else { place(d.wrapper); }
-    }
-
-    // Current rendered range (may be bigger than the view window).
-    d.viewFrom = d.viewTo = doc.first;
-    d.reportedViewFrom = d.reportedViewTo = doc.first;
-    // Information about the rendered lines.
-    d.view = [];
-    d.renderedView = null;
-    // Holds info about a single rendered line when it was rendered
-    // for measurement, while not in view.
-    d.externalMeasured = null;
-    // Empty space (in pixels) above the view
-    d.viewOffset = 0;
-    d.lastWrapHeight = d.lastWrapWidth = 0;
-    d.updateLineNumbers = null;
-
-    d.nativeBarWidth = d.barHeight = d.barWidth = 0;
-    d.scrollbarsClipped = false;
-
-    // Used to only resize the line number gutter when necessary (when
-    // the amount of lines crosses a boundary that makes its width change)
-    d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
-    // Set to true when a non-horizontal-scrolling line widget is
-    // added. As an optimization, line widget aligning is skipped when
-    // this is false.
-    d.alignWidgets = false;
-
-    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
-
-    // Tracks the maximum line length so that the horizontal scrollbar
-    // can be kept static when scrolling.
-    d.maxLine = null;
-    d.maxLineLength = 0;
-    d.maxLineChanged = false;
-
-    // Used for measuring wheel scrolling granularity
-    d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
-
-    // True when shift is held down.
-    d.shift = false;
-
-    // Used to track whether anything happened since the context menu
-    // was opened.
-    d.selForContextMenu = null;
-
-    d.activeTouch = null;
-
-    d.gutterSpecs = getGutters(options.gutters, options.lineNumbers);
-    renderGutters(d);
-
-    input.init(d);
-  }
-
-  // Since the delta values reported on mouse wheel events are
-  // unstandardized between browsers and even browser versions, and
-  // generally horribly unpredictable, this code starts by measuring
-  // the scroll effect that the first few mouse wheel events have,
-  // and, from that, detects the way it can convert deltas to pixel
-  // offsets afterwards.
-  //
-  // The reason we want to know the amount a wheel event will scroll
-  // is that it gives us a chance to update the display before the
-  // actual scrolling happens, reducing flickering.
-
-  var wheelSamples = 0, wheelPixelsPerUnit = null;
-  // Fill in a browser-detected starting value on browsers where we
-  // know one. These don't have to be accurate -- the result of them
-  // being wrong would just be a slight flicker on the first wheel
-  // scroll (if it is large enough).
-  if (ie) { wheelPixelsPerUnit = -.53; }
-  else if (gecko) { wheelPixelsPerUnit = 15; }
-  else if (chrome) { wheelPixelsPerUnit = -.7; }
-  else if (safari) { wheelPixelsPerUnit = -1/3; }
-
-  function wheelEventDelta(e) {
-    var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
-    if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }
-    if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }
-    else if (dy == null) { dy = e.wheelDelta; }
-    return {x: dx, y: dy}
-  }
-  function wheelEventPixels(e) {
-    var delta = wheelEventDelta(e);
-    delta.x *= wheelPixelsPerUnit;
-    delta.y *= wheelPixelsPerUnit;
-    return delta
-  }
-
-  function onScrollWheel(cm, e) {
-    var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
-
-    var display = cm.display, scroll = display.scroller;
-    // Quit if there's nothing to scroll here
-    var canScrollX = scroll.scrollWidth > scroll.clientWidth;
-    var canScrollY = scroll.scrollHeight > scroll.clientHeight;
-    if (!(dx && canScrollX || dy && canScrollY)) { return }
-
-    // Webkit browsers on OS X abort momentum scrolls when the target
-    // of the scroll event is removed from the scrollable element.
-    // This hack (see related code in patchDisplay) makes sure the
-    // element is kept around.
-    if (dy && mac && webkit) {
-      outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
-        for (var i = 0; i < view.length; i++) {
-          if (view[i].node == cur) {
-            cm.display.currentWheelTarget = cur;
-            break outer
-          }
-        }
-      }
-    }
-
-    // On some browsers, horizontal scrolling will cause redraws to
-    // happen before the gutter has been realigned, causing it to
-    // wriggle around in a most unseemly way. When we have an
-    // estimated pixels/delta value, we just handle horizontal
-    // scrolling entirely here. It'll be slightly off from native, but
-    // better than glitching out.
-    if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
-      if (dy && canScrollY)
-        { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); }
-      setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit));
-      // Only prevent default scrolling if vertical scrolling is
-      // actually possible. Otherwise, it causes vertical scroll
-      // jitter on OSX trackpads when deltaX is small and deltaY
-      // is large (issue #3579)
-      if (!dy || (dy && canScrollY))
-        { e_preventDefault(e); }
-      display.wheelStartX = null; // Abort measurement, if in progress
-      return
-    }
-
-    // 'Project' the visible viewport to cover the area that is being
-    // scrolled into view (if we know enough to estimate it).
-    if (dy && wheelPixelsPerUnit != null) {
-      var pixels = dy * wheelPixelsPerUnit;
-      var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
-      if (pixels < 0) { top = Math.max(0, top + pixels - 50); }
-      else { bot = Math.min(cm.doc.height, bot + pixels + 50); }
-      updateDisplaySimple(cm, {top: top, bottom: bot});
-    }
-
-    if (wheelSamples < 20) {
-      if (display.wheelStartX == null) {
-        display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
-        display.wheelDX = dx; display.wheelDY = dy;
-        setTimeout(function () {
-          if (display.wheelStartX == null) { return }
-          var movedX = scroll.scrollLeft - display.wheelStartX;
-          var movedY = scroll.scrollTop - display.wheelStartY;
-          var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
-            (movedX && display.wheelDX && movedX / display.wheelDX);
-          display.wheelStartX = display.wheelStartY = null;
-          if (!sample) { return }
-          wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
-          ++wheelSamples;
-        }, 200);
-      } else {
-        display.wheelDX += dx; display.wheelDY += dy;
-      }
-    }
-  }
-
-  // Selection objects are immutable. A new one is created every time
-  // the selection changes. A selection is one or more non-overlapping
-  // (and non-touching) ranges, sorted, and an integer that indicates
-  // which one is the primary selection (the one that's scrolled into
-  // view, that getCursor returns, etc).
-  var Selection = function(ranges, primIndex) {
-    this.ranges = ranges;
-    this.primIndex = primIndex;
-  };
-
-  Selection.prototype.primary = function () { return this.ranges[this.primIndex] };
-
-  Selection.prototype.equals = function (other) {
-    if (other == this) { return true }
-    if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
-    for (var i = 0; i < this.ranges.length; i++) {
-      var here = this.ranges[i], there = other.ranges[i];
-      if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
-    }
-    return true
-  };
-
-  Selection.prototype.deepCopy = function () {
-    var out = [];
-    for (var i = 0; i < this.ranges.length; i++)
-      { out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); }
-    return new Selection(out, this.primIndex)
-  };
-
-  Selection.prototype.somethingSelected = function () {
-    for (var i = 0; i < this.ranges.length; i++)
-      { if (!this.ranges[i].empty()) { return true } }
-    return false
-  };
-
-  Selection.prototype.contains = function (pos, end) {
-    if (!end) { end = pos; }
-    for (var i = 0; i < this.ranges.length; i++) {
-      var range = this.ranges[i];
-      if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
-        { return i }
-    }
-    return -1
-  };
-
-  var Range = function(anchor, head) {
-    this.anchor = anchor; this.head = head;
-  };
-
-  Range.prototype.from = function () { return minPos(this.anchor, this.head) };
-  Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
-  Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };
-
-  // Take an unsorted, potentially overlapping set of ranges, and
-  // build a selection out of it. 'Consumes' ranges array (modifying
-  // it).
-  function normalizeSelection(cm, ranges, primIndex) {
-    var mayTouch = cm && cm.options.selectionsMayTouch;
-    var prim = ranges[primIndex];
-    ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });
-    primIndex = indexOf(ranges, prim);
-    for (var i = 1; i < ranges.length; i++) {
-      var cur = ranges[i], prev = ranges[i - 1];
-      var diff = cmp(prev.to(), cur.from());
-      if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {
-        var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
-        var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
-        if (i <= primIndex) { --primIndex; }
-        ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
-      }
-    }
-    return new Selection(ranges, primIndex)
-  }
-
-  function simpleSelection(anchor, head) {
-    return new Selection([new Range(anchor, head || anchor)], 0)
-  }
-
-  // Compute the position of the end of a change (its 'to' property
-  // refers to the pre-change end).
-  function changeEnd(change) {
-    if (!change.text) { return change.to }
-    return Pos(change.from.line + change.text.length - 1,
-               lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
-  }
-
-  // Adjust a position to refer to the post-change position of the
-  // same text, or the end of the change if the change covers it.
-  function adjustForChange(pos, change) {
-    if (cmp(pos, change.from) < 0) { return pos }
-    if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
-
-    var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
-    if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }
-    return Pos(line, ch)
-  }
-
-  function computeSelAfterChange(doc, change) {
-    var out = [];
-    for (var i = 0; i < doc.sel.ranges.length; i++) {
-      var range = doc.sel.ranges[i];
-      out.push(new Range(adjustForChange(range.anchor, change),
-                         adjustForChange(range.head, change)));
-    }
-    return normalizeSelection(doc.cm, out, doc.sel.primIndex)
-  }
-
-  function offsetPos(pos, old, nw) {
-    if (pos.line == old.line)
-      { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
-    else
-      { return Pos(nw.line + (pos.line - old.line), pos.ch) }
-  }
-
-  // Used by replaceSelections to allow moving the selection to the
-  // start or around the replaced test. Hint may be "start" or "around".
-  function computeReplacedSel(doc, changes, hint) {
-    var out = [];
-    var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
-    for (var i = 0; i < changes.length; i++) {
-      var change = changes[i];
-      var from = offsetPos(change.from, oldPrev, newPrev);
-      var to = offsetPos(changeEnd(change), oldPrev, newPrev);
-      oldPrev = change.to;
-      newPrev = to;
-      if (hint == "around") {
-        var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
-        out[i] = new Range(inv ? to : from, inv ? from : to);
-      } else {
-        out[i] = new Range(from, from);
-      }
-    }
-    return new Selection(out, doc.sel.primIndex)
-  }
-
-  // Used to get the editor into a consistent state again when options change.
-
-  function loadMode(cm) {
-    cm.doc.mode = getMode(cm.options, cm.doc.modeOption);
-    resetModeState(cm);
-  }
-
-  function resetModeState(cm) {
-    cm.doc.iter(function (line) {
-      if (line.stateAfter) { line.stateAfter = null; }
-      if (line.styles) { line.styles = null; }
-    });
-    cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;
-    startWorker(cm, 100);
-    cm.state.modeGen++;
-    if (cm.curOp) { regChange(cm); }
-  }
-
-  // DOCUMENT DATA STRUCTURE
-
-  // By default, updates that start and end at the beginning of a line
-  // are treated specially, in order to make the association of line
-  // widgets and marker elements with the text behave more intuitive.
-  function isWholeLineUpdate(doc, change) {
-    return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
-      (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
-  }
-
-  // Perform a change on the document data structure.
-  function updateDoc(doc, change, markedSpans, estimateHeight) {
-    function spansFor(n) {return markedSpans ? markedSpans[n] : null}
-    function update(line, text, spans) {
-      updateLine(line, text, spans, estimateHeight);
-      signalLater(line, "change", line, change);
-    }
-    function linesFor(start, end) {
-      var result = [];
-      for (var i = start; i < end; ++i)
-        { result.push(new Line(text[i], spansFor(i), estimateHeight)); }
-      return result
-    }
-
-    var from = change.from, to = change.to, text = change.text;
-    var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
-    var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
-
-    // Adjust the line structure
-    if (change.full) {
-      doc.insert(0, linesFor(0, text.length));
-      doc.remove(text.length, doc.size - text.length);
-    } else if (isWholeLineUpdate(doc, change)) {
-      // This is a whole-line replace. Treated specially to make
-      // sure line objects move the way they are supposed to.
-      var added = linesFor(0, text.length - 1);
-      update(lastLine, lastLine.text, lastSpans);
-      if (nlines) { doc.remove(from.line, nlines); }
-      if (added.length) { doc.insert(from.line, added); }
-    } else if (firstLine == lastLine) {
-      if (text.length == 1) {
-        update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
-      } else {
-        var added$1 = linesFor(1, text.length - 1);
-        added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
-        update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
-        doc.insert(from.line + 1, added$1);
-      }
-    } else if (text.length == 1) {
-      update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
-      doc.remove(from.line + 1, nlines);
-    } else {
-      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
-      update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
-      var added$2 = linesFor(1, text.length - 1);
-      if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }
-      doc.insert(from.line + 1, added$2);
-    }
-
-    signalLater(doc, "change", doc, change);
-  }
-
-  // Call f for all linked documents.
-  function linkedDocs(doc, f, sharedHistOnly) {
-    function propagate(doc, skip, sharedHist) {
-      if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
-        var rel = doc.linked[i];
-        if (rel.doc == skip) { continue }
-        var shared = sharedHist && rel.sharedHist;
-        if (sharedHistOnly && !shared) { continue }
-        f(rel.doc, shared);
-        propagate(rel.doc, doc, shared);
-      } }
-    }
-    propagate(doc, null, true);
-  }
-
-  // Attach a document to an editor.
-  function attachDoc(cm, doc) {
-    if (doc.cm) { throw new Error("This document is already in use.") }
-    cm.doc = doc;
-    doc.cm = cm;
-    estimateLineHeights(cm);
-    loadMode(cm);
-    setDirectionClass(cm);
-    if (!cm.options.lineWrapping) { findMaxLine(cm); }
-    cm.options.mode = doc.modeOption;
-    regChange(cm);
-  }
-
-  function setDirectionClass(cm) {
-  (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl");
-  }
-
-  function directionChanged(cm) {
-    runInOp(cm, function () {
-      setDirectionClass(cm);
-      regChange(cm);
-    });
-  }
-
-  function History(startGen) {
-    // Arrays of change events and selections. Doing something adds an
-    // event to done and clears undo. Undoing moves events from done
-    // to undone, redoing moves them in the other direction.
-    this.done = []; this.undone = [];
-    this.undoDepth = Infinity;
-    // Used to track when changes can be merged into a single undo
-    // event
-    this.lastModTime = this.lastSelTime = 0;
-    this.lastOp = this.lastSelOp = null;
-    this.lastOrigin = this.lastSelOrigin = null;
-    // Used by the isClean() method
-    this.generation = this.maxGeneration = startGen || 1;
-  }
-
-  // Create a history change event from an updateDoc-style change
-  // object.
-  function historyChangeFromChange(doc, change) {
-    var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
-    attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
-    linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);
-    return histChange
-  }
-
-  // Pop all selection events off the end of a history array. Stop at
-  // a change event.
-  function clearSelectionEvents(array) {
-    while (array.length) {
-      var last = lst(array);
-      if (last.ranges) { array.pop(); }
-      else { break }
-    }
-  }
-
-  // Find the top change event in the history. Pop off selection
-  // events that are in the way.
-  function lastChangeEvent(hist, force) {
-    if (force) {
-      clearSelectionEvents(hist.done);
-      return lst(hist.done)
-    } else if (hist.done.length && !lst(hist.done).ranges) {
-      return lst(hist.done)
-    } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
-      hist.done.pop();
-      return lst(hist.done)
-    }
-  }
-
-  // Register a change in the history. Merges changes that are within
-  // a single operation, or are close together with an origin that
-  // allows merging (starting with "+") into a single event.
-  function addChangeToHistory(doc, change, selAfter, opId) {
-    var hist = doc.history;
-    hist.undone.length = 0;
-    var time = +new Date, cur;
-    var last;
-
-    if ((hist.lastOp == opId ||
-         hist.lastOrigin == change.origin && change.origin &&
-         ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||
-          change.origin.charAt(0) == "*")) &&
-        (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
-      // Merge this change into the last event
-      last = lst(cur.changes);
-      if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
-        // Optimized case for simple insertion -- don't want to add
-        // new changesets for every character typed
-        last.to = changeEnd(change);
-      } else {
-        // Add new sub-event
-        cur.changes.push(historyChangeFromChange(doc, change));
-      }
-    } else {
-      // Can not be merged, start a new event.
-      var before = lst(hist.done);
-      if (!before || !before.ranges)
-        { pushSelectionToHistory(doc.sel, hist.done); }
-      cur = {changes: [historyChangeFromChange(doc, change)],
-             generation: hist.generation};
-      hist.done.push(cur);
-      while (hist.done.length > hist.undoDepth) {
-        hist.done.shift();
-        if (!hist.done[0].ranges) { hist.done.shift(); }
-      }
-    }
-    hist.done.push(selAfter);
-    hist.generation = ++hist.maxGeneration;
-    hist.lastModTime = hist.lastSelTime = time;
-    hist.lastOp = hist.lastSelOp = opId;
-    hist.lastOrigin = hist.lastSelOrigin = change.origin;
-
-    if (!last) { signal(doc, "historyAdded"); }
-  }
-
-  function selectionEventCanBeMerged(doc, origin, prev, sel) {
-    var ch = origin.charAt(0);
-    return ch == "*" ||
-      ch == "+" &&
-      prev.ranges.length == sel.ranges.length &&
-      prev.somethingSelected() == sel.somethingSelected() &&
-      new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
-  }
-
-  // Called whenever the selection changes, sets the new selection as
-  // the pending selection in the history, and pushes the old pending
-  // selection into the 'done' array when it was significantly
-  // different (in number of selected ranges, emptiness, or time).
-  function addSelectionToHistory(doc, sel, opId, options) {
-    var hist = doc.history, origin = options && options.origin;
-
-    // A new event is started when the previous origin does not match
-    // the current, or the origins don't allow matching. Origins
-    // starting with * are always merged, those starting with + are
-    // merged when similar and close together in time.
-    if (opId == hist.lastSelOp ||
-        (origin && hist.lastSelOrigin == origin &&
-         (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
-          selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
-      { hist.done[hist.done.length - 1] = sel; }
-    else
-      { pushSelectionToHistory(sel, hist.done); }
-
-    hist.lastSelTime = +new Date;
-    hist.lastSelOrigin = origin;
-    hist.lastSelOp = opId;
-    if (options && options.clearRedo !== false)
-      { clearSelectionEvents(hist.undone); }
-  }
-
-  function pushSelectionToHistory(sel, dest) {
-    var top = lst(dest);
-    if (!(top && top.ranges && top.equals(sel)))
-      { dest.push(sel); }
-  }
-
-  // Used to store marked span information in the history.
-  function attachLocalSpans(doc, change, from, to) {
-    var existing = change["spans_" + doc.id], n = 0;
-    doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
-      if (line.markedSpans)
-        { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; }
-      ++n;
-    });
-  }
-
-  // When un/re-doing restores text containing marked spans, those
-  // that have been explicitly cleared should not be restored.
-  function removeClearedSpans(spans) {
-    if (!spans) { return null }
-    var out;
-    for (var i = 0; i < spans.length; ++i) {
-      if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }
-      else if (out) { out.push(spans[i]); }
-    }
-    return !out ? spans : out.length ? out : null
-  }
-
-  // Retrieve and filter the old marked spans stored in a change event.
-  function getOldSpans(doc, change) {
-    var found = change["spans_" + doc.id];
-    if (!found) { return null }
-    var nw = [];
-    for (var i = 0; i < change.text.length; ++i)
-      { nw.push(removeClearedSpans(found[i])); }
-    return nw
-  }
-
-  // Used for un/re-doing changes from the history. Combines the
-  // result of computing the existing spans with the set of spans that
-  // existed in the history (so that deleting around a span and then
-  // undoing brings back the span).
-  function mergeOldSpans(doc, change) {
-    var old = getOldSpans(doc, change);
-    var stretched = stretchSpansOverChange(doc, change);
-    if (!old) { return stretched }
-    if (!stretched) { return old }
-
-    for (var i = 0; i < old.length; ++i) {
-      var oldCur = old[i], stretchCur = stretched[i];
-      if (oldCur && stretchCur) {
-        spans: for (var j = 0; j < stretchCur.length; ++j) {
-          var span = stretchCur[j];
-          for (var k = 0; k < oldCur.length; ++k)
-            { if (oldCur[k].marker == span.marker) { continue spans } }
-          oldCur.push(span);
-        }
-      } else if (stretchCur) {
-        old[i] = stretchCur;
-      }
-    }
-    return old
-  }
-
-  // Used both to provide a JSON-safe object in .getHistory, and, when
-  // detaching a document, to split the history in two
-  function copyHistoryArray(events, newGroup, instantiateSel) {
-    var copy = [];
-    for (var i = 0; i < events.length; ++i) {
-      var event = events[i];
-      if (event.ranges) {
-        copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
-        continue
-      }
-      var changes = event.changes, newChanges = [];
-      copy.push({changes: newChanges});
-      for (var j = 0; j < changes.length; ++j) {
-        var change = changes[j], m = (void 0);
-        newChanges.push({from: change.from, to: change.to, text: change.text});
-        if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
-          if (indexOf(newGroup, Number(m[1])) > -1) {
-            lst(newChanges)[prop] = change[prop];
-            delete change[prop];
-          }
-        } } }
-      }
-    }
-    return copy
-  }
-
-  // The 'scroll' parameter given to many of these indicated whether
-  // the new cursor position should be scrolled into view after
-  // modifying the selection.
-
-  // If shift is held or the extend flag is set, extends a range to
-  // include a given position (and optionally a second position).
-  // Otherwise, simply returns the range between the given positions.
-  // Used for cursor motion and such.
-  function extendRange(range, head, other, extend) {
-    if (extend) {
-      var anchor = range.anchor;
-      if (other) {
-        var posBefore = cmp(head, anchor) < 0;
-        if (posBefore != (cmp(other, anchor) < 0)) {
-          anchor = head;
-          head = other;
-        } else if (posBefore != (cmp(head, other) < 0)) {
-          head = other;
-        }
-      }
-      return new Range(anchor, head)
-    } else {
-      return new Range(other || head, head)
-    }
-  }
-
-  // Extend the primary selection range, discard the rest.
-  function extendSelection(doc, head, other, options, extend) {
-    if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }
-    setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);
-  }
-
-  // Extend all selections (pos is an array of selections with length
-  // equal the number of selections)
-  function extendSelections(doc, heads, options) {
-    var out = [];
-    var extend = doc.cm && (doc.cm.display.shift || doc.extend);
-    for (var i = 0; i < doc.sel.ranges.length; i++)
-      { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }
-    var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex);
-    setSelection(doc, newSel, options);
-  }
-
-  // Updates a single range in the selection.
-  function replaceOneSelection(doc, i, range, options) {
-    var ranges = doc.sel.ranges.slice(0);
-    ranges[i] = range;
-    setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options);
-  }
-
-  // Reset the selection to a single range.
-  function setSimpleSelection(doc, anchor, head, options) {
-    setSelection(doc, simpleSelection(anchor, head), options);
-  }
-
-  // Give beforeSelectionChange handlers a change to influence a
-  // selection update.
-  function filterSelectionChange(doc, sel, options) {
-    var obj = {
-      ranges: sel.ranges,
-      update: function(ranges) {
-        this.ranges = [];
-        for (var i = 0; i < ranges.length; i++)
-          { this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
-                                     clipPos(doc, ranges[i].head)); }
-      },
-      origin: options && options.origin
-    };
-    signal(doc, "beforeSelectionChange", doc, obj);
-    if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); }
-    if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) }
-    else { return sel }
-  }
-
-  function setSelectionReplaceHistory(doc, sel, options) {
-    var done = doc.history.done, last = lst(done);
-    if (last && last.ranges) {
-      done[done.length - 1] = sel;
-      setSelectionNoUndo(doc, sel, options);
-    } else {
-      setSelection(doc, sel, options);
-    }
-  }
-
-  // Set a new selection.
-  function setSelection(doc, sel, options) {
-    setSelectionNoUndo(doc, sel, options);
-    addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
-  }
-
-  function setSelectionNoUndo(doc, sel, options) {
-    if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
-      { sel = filterSelectionChange(doc, sel, options); }
-
-    var bias = options && options.bias ||
-      (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
-    setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
-
-    if (!(options && options.scroll === false) && doc.cm)
-      { ensureCursorVisible(doc.cm); }
-  }
-
-  function setSelectionInner(doc, sel) {
-    if (sel.equals(doc.sel)) { return }
-
-    doc.sel = sel;
-
-    if (doc.cm) {
-      doc.cm.curOp.updateInput = 1;
-      doc.cm.curOp.selectionChanged = true;
-      signalCursorActivity(doc.cm);
-    }
-    signalLater(doc, "cursorActivity", doc);
-  }
-
-  // Verify that the selection does not partially select any atomic
-  // marked ranges.
-  function reCheckSelection(doc) {
-    setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));
-  }
-
-  // Return a selection that does not partially select any atomic
-  // ranges.
-  function skipAtomicInSelection(doc, sel, bias, mayClear) {
-    var out;
-    for (var i = 0; i < sel.ranges.length; i++) {
-      var range = sel.ranges[i];
-      var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
-      var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
-      var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
-      if (out || newAnchor != range.anchor || newHead != range.head) {
-        if (!out) { out = sel.ranges.slice(0, i); }
-        out[i] = new Range(newAnchor, newHead);
-      }
-    }
-    return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel
-  }
-
-  function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
-    var line = getLine(doc, pos.line);
-    if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
-      var sp = line.markedSpans[i], m = sp.marker;
-
-      // Determine if we should prevent the cursor being placed to the left/right of an atomic marker
-      // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it
-      // is with selectLeft/Right
-      var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft;
-      var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight;
-
-      if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
-          (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
-        if (mayClear) {
-          signal(m, "beforeCursorEnter");
-          if (m.explicitlyCleared) {
-            if (!line.markedSpans) { break }
-            else {--i; continue}
-          }
-        }
-        if (!m.atomic) { continue }
-
-        if (oldPos) {
-          var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);
-          if (dir < 0 ? preventCursorRight : preventCursorLeft)
-            { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }
-          if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
-            { return skipAtomicInner(doc, near, pos, dir, mayClear) }
-        }
-
-        var far = m.find(dir < 0 ? -1 : 1);
-        if (dir < 0 ? preventCursorLeft : preventCursorRight)
-          { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }
-        return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
-      }
-    } }
-    return pos
-  }
-
-  // Ensure a given position is not inside an atomic range.
-  function skipAtomic(doc, pos, oldPos, bias, mayClear) {
-    var dir = bias || 1;
-    var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
-        (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
-        skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
-        (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
-    if (!found) {
-      doc.cantEdit = true;
-      return Pos(doc.first, 0)
-    }
-    return found
-  }
-
-  function movePos(doc, pos, dir, line) {
-    if (dir < 0 && pos.ch == 0) {
-      if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
-      else { return null }
-    } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
-      if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
-      else { return null }
-    } else {
-      return new Pos(pos.line, pos.ch + dir)
-    }
-  }
-
-  function selectAll(cm) {
-    cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);
-  }
-
-  // UPDATING
-
-  // Allow "beforeChange" event handlers to influence a change
-  function filterChange(doc, change, update) {
-    var obj = {
-      canceled: false,
-      from: change.from,
-      to: change.to,
-      text: change.text,
-      origin: change.origin,
-      cancel: function () { return obj.canceled = true; }
-    };
-    if (update) { obj.update = function (from, to, text, origin) {
-      if (from) { obj.from = clipPos(doc, from); }
-      if (to) { obj.to = clipPos(doc, to); }
-      if (text) { obj.text = text; }
-      if (origin !== undefined) { obj.origin = origin; }
-    }; }
-    signal(doc, "beforeChange", doc, obj);
-    if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); }
-
-    if (obj.canceled) {
-      if (doc.cm) { doc.cm.curOp.updateInput = 2; }
-      return null
-    }
-    return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
-  }
-
-  // Apply a change to a document, and add it to the document's
-  // history, and propagating it to all linked documents.
-  function makeChange(doc, change, ignoreReadOnly) {
-    if (doc.cm) {
-      if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
-      if (doc.cm.state.suppressEdits) { return }
-    }
-
-    if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
-      change = filterChange(doc, change, true);
-      if (!change) { return }
-    }
-
-    // Possibly split or suppress the update based on the presence
-    // of read-only spans in its range.
-    var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
-    if (split) {
-      for (var i = split.length - 1; i >= 0; --i)
-        { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); }
-    } else {
-      makeChangeInner(doc, change);
-    }
-  }
-
-  function makeChangeInner(doc, change) {
-    if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
-    var selAfter = computeSelAfterChange(doc, change);
-    addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
-
-    makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
-    var rebased = [];
-
-    linkedDocs(doc, function (doc, sharedHist) {
-      if (!sharedHist && indexOf(rebased, doc.history) == -1) {
-        rebaseHist(doc.history, change);
-        rebased.push(doc.history);
-      }
-      makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
-    });
-  }
-
-  // Revert a change stored in a document's history.
-  function makeChangeFromHistory(doc, type, allowSelectionOnly) {
-    var suppress = doc.cm && doc.cm.state.suppressEdits;
-    if (suppress && !allowSelectionOnly) { return }
-
-    var hist = doc.history, event, selAfter = doc.sel;
-    var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
-
-    // Verify that there is a useable event (so that ctrl-z won't
-    // needlessly clear selection events)
-    var i = 0;
-    for (; i < source.length; i++) {
-      event = source[i];
-      if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
-        { break }
-    }
-    if (i == source.length) { return }
-    hist.lastOrigin = hist.lastSelOrigin = null;
-
-    for (;;) {
-      event = source.pop();
-      if (event.ranges) {
-        pushSelectionToHistory(event, dest);
-        if (allowSelectionOnly && !event.equals(doc.sel)) {
-          setSelection(doc, event, {clearRedo: false});
-          return
-        }
-        selAfter = event;
-      } else if (suppress) {
-        source.push(event);
-        return
-      } else { break }
-    }
-
-    // Build up a reverse change object to add to the opposite history
-    // stack (redo when undoing, and vice versa).
-    var antiChanges = [];
-    pushSelectionToHistory(selAfter, dest);
-    dest.push({changes: antiChanges, generation: hist.generation});
-    hist.generation = event.generation || ++hist.maxGeneration;
-
-    var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
-
-    var loop = function ( i ) {
-      var change = event.changes[i];
-      change.origin = type;
-      if (filter && !filterChange(doc, change, false)) {
-        source.length = 0;
-        return {}
-      }
-
-      antiChanges.push(historyChangeFromChange(doc, change));
-
-      var after = i ? computeSelAfterChange(doc, change) : lst(source);
-      makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
-      if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }
-      var rebased = [];
-
-      // Propagate to the linked documents
-      linkedDocs(doc, function (doc, sharedHist) {
-        if (!sharedHist && indexOf(rebased, doc.history) == -1) {
-          rebaseHist(doc.history, change);
-          rebased.push(doc.history);
-        }
-        makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
-      });
-    };
-
-    for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
-      var returned = loop( i$1 );
-
-      if ( returned ) return returned.v;
-    }
-  }
-
-  // Sub-views need their line numbers shifted when text is added
-  // above or below them in the parent document.
-  function shiftDoc(doc, distance) {
-    if (distance == 0) { return }
-    doc.first += distance;
-    doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
-      Pos(range.anchor.line + distance, range.anchor.ch),
-      Pos(range.head.line + distance, range.head.ch)
-    ); }), doc.sel.primIndex);
-    if (doc.cm) {
-      regChange(doc.cm, doc.first, doc.first - distance, distance);
-      for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
-        { regLineChange(doc.cm, l, "gutter"); }
-    }
-  }
-
-  // More lower-level change function, handling only a single document
-  // (not linked ones).
-  function makeChangeSingleDoc(doc, change, selAfter, spans) {
-    if (doc.cm && !doc.cm.curOp)
-      { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
-
-    if (change.to.line < doc.first) {
-      shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
-      return
-    }
-    if (change.from.line > doc.lastLine()) { return }
-
-    // Clip the change to the size of this doc
-    if (change.from.line < doc.first) {
-      var shift = change.text.length - 1 - (doc.first - change.from.line);
-      shiftDoc(doc, shift);
-      change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
-                text: [lst(change.text)], origin: change.origin};
-    }
-    var last = doc.lastLine();
-    if (change.to.line > last) {
-      change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
-                text: [change.text[0]], origin: change.origin};
-    }
-
-    change.removed = getBetween(doc, change.from, change.to);
-
-    if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }
-    if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }
-    else { updateDoc(doc, change, spans); }
-    setSelectionNoUndo(doc, selAfter, sel_dontScroll);
-
-    if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0)))
-      { doc.cantEdit = false; }
-  }
-
-  // Handle the interaction of a change to a document with the editor
-  // that this document is part of.
-  function makeChangeSingleDocInEditor(cm, change, spans) {
-    var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
-
-    var recomputeMaxLength = false, checkWidthStart = from.line;
-    if (!cm.options.lineWrapping) {
-      checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
-      doc.iter(checkWidthStart, to.line + 1, function (line) {
-        if (line == display.maxLine) {
-          recomputeMaxLength = true;
-          return true
-        }
-      });
-    }
-
-    if (doc.sel.contains(change.from, change.to) > -1)
-      { signalCursorActivity(cm); }
-
-    updateDoc(doc, change, spans, estimateHeight(cm));
-
-    if (!cm.options.lineWrapping) {
-      doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
-        var len = lineLength(line);
-        if (len > display.maxLineLength) {
-          display.maxLine = line;
-          display.maxLineLength = len;
-          display.maxLineChanged = true;
-          recomputeMaxLength = false;
-        }
-      });
-      if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }
-    }
-
-    retreatFrontier(doc, from.line);
-    startWorker(cm, 400);
-
-    var lendiff = change.text.length - (to.line - from.line) - 1;
-    // Remember that these lines changed, for updating the display
-    if (change.full)
-      { regChange(cm); }
-    else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
-      { regLineChange(cm, from.line, "text"); }
-    else
-      { regChange(cm, from.line, to.line + 1, lendiff); }
-
-    var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
-    if (changeHandler || changesHandler) {
-      var obj = {
-        from: from, to: to,
-        text: change.text,
-        removed: change.removed,
-        origin: change.origin
-      };
-      if (changeHandler) { signalLater(cm, "change", cm, obj); }
-      if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }
-    }
-    cm.display.selForContextMenu = null;
-  }
-
-  function replaceRange(doc, code, from, to, origin) {
-    var assign;
-
-    if (!to) { to = from; }
-    if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); }
-    if (typeof code == "string") { code = doc.splitLines(code); }
-    makeChange(doc, {from: from, to: to, text: code, origin: origin});
-  }
-
-  // Rebasing/resetting history to deal with externally-sourced changes
-
-  function rebaseHistSelSingle(pos, from, to, diff) {
-    if (to < pos.line) {
-      pos.line += diff;
-    } else if (from < pos.line) {
-      pos.line = from;
-      pos.ch = 0;
-    }
-  }
-
-  // Tries to rebase an array of history events given a change in the
-  // document. If the change touches the same lines as the event, the
-  // event, and everything 'behind' it, is discarded. If the change is
-  // before the event, the event's positions are updated. Uses a
-  // copy-on-write scheme for the positions, to avoid having to
-  // reallocate them all on every rebase, but also avoid problems with
-  // shared position objects being unsafely updated.
-  function rebaseHistArray(array, from, to, diff) {
-    for (var i = 0; i < array.length; ++i) {
-      var sub = array[i], ok = true;
-      if (sub.ranges) {
-        if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
-        for (var j = 0; j < sub.ranges.length; j++) {
-          rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
-          rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
-        }
-        continue
-      }
-      for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
-        var cur = sub.changes[j$1];
-        if (to < cur.from.line) {
-          cur.from = Pos(cur.from.line + diff, cur.from.ch);
-          cur.to = Pos(cur.to.line + diff, cur.to.ch);
-        } else if (from <= cur.to.line) {
-          ok = false;
-          break
-        }
-      }
-      if (!ok) {
-        array.splice(0, i + 1);
-        i = 0;
-      }
-    }
-  }
-
-  function rebaseHist(hist, change) {
-    var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
-    rebaseHistArray(hist.done, from, to, diff);
-    rebaseHistArray(hist.undone, from, to, diff);
-  }
-
-  // Utility for applying a change to a line by handle or number,
-  // returning the number and optionally registering the line as
-  // changed.
-  function changeLine(doc, handle, changeType, op) {
-    var no = handle, line = handle;
-    if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); }
-    else { no = lineNo(handle); }
-    if (no == null) { return null }
-    if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }
-    return line
-  }
-
-  // The document is represented as a BTree consisting of leaves, with
-  // chunk of lines in them, and branches, with up to ten leaves or
-  // other branch nodes below them. The top node is always a branch
-  // node, and is the document object itself (meaning it has
-  // additional methods and properties).
-  //
-  // All nodes have parent links. The tree is used both to go from
-  // line numbers to line objects, and to go from objects to numbers.
-  // It also indexes by height, and is used to convert between height
-  // and line object, and to find the total height of the document.
-  //
-  // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
-
-  function LeafChunk(lines) {
-    this.lines = lines;
-    this.parent = null;
-    var height = 0;
-    for (var i = 0; i < lines.length; ++i) {
-      lines[i].parent = this;
-      height += lines[i].height;
-    }
-    this.height = height;
-  }
-
-  LeafChunk.prototype = {
-    chunkSize: function() { return this.lines.length },
-
-    // Remove the n lines at offset 'at'.
-    removeInner: function(at, n) {
-      for (var i = at, e = at + n; i < e; ++i) {
-        var line = this.lines[i];
-        this.height -= line.height;
-        cleanUpLine(line);
-        signalLater(line, "delete");
-      }
-      this.lines.splice(at, n);
-    },
-
-    // Helper used to collapse a small branch into a single leaf.
-    collapse: function(lines) {
-      lines.push.apply(lines, this.lines);
-    },
-
-    // Insert the given array of lines at offset 'at', count them as
-    // having the given height.
-    insertInner: function(at, lines, height) {
-      this.height += height;
-      this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
-      for (var i = 0; i < lines.length; ++i) { lines[i].parent = this; }
-    },
-
-    // Used to iterate over a part of the tree.
-    iterN: function(at, n, op) {
-      for (var e = at + n; at < e; ++at)
-        { if (op(this.lines[at])) { return true } }
-    }
-  };
-
-  function BranchChunk(children) {
-    this.children = children;
-    var size = 0, height = 0;
-    for (var i = 0; i < children.length; ++i) {
-      var ch = children[i];
-      size += ch.chunkSize(); height += ch.height;
-      ch.parent = this;
-    }
-    this.size = size;
-    this.height = height;
-    this.parent = null;
-  }
-
-  BranchChunk.prototype = {
-    chunkSize: function() { return this.size },
-
-    removeInner: function(at, n) {
-      this.size -= n;
-      for (var i = 0; i < this.children.length; ++i) {
-        var child = this.children[i], sz = child.chunkSize();
-        if (at < sz) {
-          var rm = Math.min(n, sz - at), oldHeight = child.height;
-          child.removeInner(at, rm);
-          this.height -= oldHeight - child.height;
-          if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
-          if ((n -= rm) == 0) { break }
-          at = 0;
-        } else { at -= sz; }
-      }
-      // If the result is smaller than 25 lines, ensure that it is a
-      // single leaf node.
-      if (this.size - n < 25 &&
-          (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
-        var lines = [];
-        this.collapse(lines);
-        this.children = [new LeafChunk(lines)];
-        this.children[0].parent = this;
-      }
-    },
-
-    collapse: function(lines) {
-      for (var i = 0; i < this.children.length; ++i) { this.children[i].collapse(lines); }
-    },
-
-    insertInner: function(at, lines, height) {
-      this.size += lines.length;
-      this.height += height;
-      for (var i = 0; i < this.children.length; ++i) {
-        var child = this.children[i], sz = child.chunkSize();
-        if (at <= sz) {
-          child.insertInner(at, lines, height);
-          if (child.lines && child.lines.length > 50) {
-            // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
-            // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
-            var remaining = child.lines.length % 25 + 25;
-            for (var pos = remaining; pos < child.lines.length;) {
-              var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
-              child.height -= leaf.height;
-              this.children.splice(++i, 0, leaf);
-              leaf.parent = this;
-            }
-            child.lines = child.lines.slice(0, remaining);
-            this.maybeSpill();
-          }
-          break
-        }
-        at -= sz;
-      }
-    },
-
-    // When a node has grown, check whether it should be split.
-    maybeSpill: function() {
-      if (this.children.length <= 10) { return }
-      var me = this;
-      do {
-        var spilled = me.children.splice(me.children.length - 5, 5);
-        var sibling = new BranchChunk(spilled);
-        if (!me.parent) { // Become the parent node
-          var copy = new BranchChunk(me.children);
-          copy.parent = me;
-          me.children = [copy, sibling];
-          me = copy;
-       } else {
-          me.size -= sibling.size;
-          me.height -= sibling.height;
-          var myIndex = indexOf(me.parent.children, me);
-          me.parent.children.splice(myIndex + 1, 0, sibling);
-        }
-        sibling.parent = me.parent;
-      } while (me.children.length > 10)
-      me.parent.maybeSpill();
-    },
-
-    iterN: function(at, n, op) {
-      for (var i = 0; i < this.children.length; ++i) {
-        var child = this.children[i], sz = child.chunkSize();
-        if (at < sz) {
-          var used = Math.min(n, sz - at);
-          if (child.iterN(at, used, op)) { return true }
-          if ((n -= used) == 0) { break }
-          at = 0;
-        } else { at -= sz; }
-      }
-    }
-  };
-
-  // Line widgets are block elements displayed above or below a line.
-
-  var LineWidget = function(doc, node, options) {
-    if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
-      { this[opt] = options[opt]; } } }
-    this.doc = doc;
-    this.node = node;
-  };
-
-  LineWidget.prototype.clear = function () {
-    var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
-    if (no == null || !ws) { return }
-    for (var i = 0; i < ws.length; ++i) { if (ws[i] == this) { ws.splice(i--, 1); } }
-    if (!ws.length) { line.widgets = null; }
-    var height = widgetHeight(this);
-    updateLineHeight(line, Math.max(0, line.height - height));
-    if (cm) {
-      runInOp(cm, function () {
-        adjustScrollWhenAboveVisible(cm, line, -height);
-        regLineChange(cm, no, "widget");
-      });
-      signalLater(cm, "lineWidgetCleared", cm, this, no);
-    }
-  };
-
-  LineWidget.prototype.changed = function () {
-      var this$1 = this;
-
-    var oldH = this.height, cm = this.doc.cm, line = this.line;
-    this.height = null;
-    var diff = widgetHeight(this) - oldH;
-    if (!diff) { return }
-    if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); }
-    if (cm) {
-      runInOp(cm, function () {
-        cm.curOp.forceUpdate = true;
-        adjustScrollWhenAboveVisible(cm, line, diff);
-        signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line));
-      });
-    }
-  };
-  eventMixin(LineWidget);
-
-  function adjustScrollWhenAboveVisible(cm, line, diff) {
-    if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
-      { addToScrollTop(cm, diff); }
-  }
-
-  function addLineWidget(doc, handle, node, options) {
-    var widget = new LineWidget(doc, node, options);
-    var cm = doc.cm;
-    if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }
-    changeLine(doc, handle, "widget", function (line) {
-      var widgets = line.widgets || (line.widgets = []);
-      if (widget.insertAt == null) { widgets.push(widget); }
-      else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); }
-      widget.line = line;
-      if (cm && !lineIsHidden(doc, line)) {
-        var aboveVisible = heightAtLine(line) < doc.scrollTop;
-        updateLineHeight(line, line.height + widgetHeight(widget));
-        if (aboveVisible) { addToScrollTop(cm, widget.height); }
-        cm.curOp.forceUpdate = true;
-      }
-      return true
-    });
-    if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); }
-    return widget
-  }
-
-  // TEXTMARKERS
-
-  // Created with markText and setBookmark methods. A TextMarker is a
-  // handle that can be used to clear or find a marked position in the
-  // document. Line objects hold arrays (markedSpans) containing
-  // {from, to, marker} object pointing to such marker objects, and
-  // indicating that such a marker is present on that line. Multiple
-  // lines may point to the same marker when it spans across lines.
-  // The spans will have null for their from/to properties when the
-  // marker continues beyond the start/end of the line. Markers have
-  // links back to the lines they currently touch.
-
-  // Collapsed markers have unique ids, in order to be able to order
-  // them, which is needed for uniquely determining an outer marker
-  // when they overlap (they may nest, but not partially overlap).
-  var nextMarkerId = 0;
-
-  var TextMarker = function(doc, type) {
-    this.lines = [];
-    this.type = type;
-    this.doc = doc;
-    this.id = ++nextMarkerId;
-  };
-
-  // Clear the marker.
-  TextMarker.prototype.clear = function () {
-    if (this.explicitlyCleared) { return }
-    var cm = this.doc.cm, withOp = cm && !cm.curOp;
-    if (withOp) { startOperation(cm); }
-    if (hasHandler(this, "clear")) {
-      var found = this.find();
-      if (found) { signalLater(this, "clear", found.from, found.to); }
-    }
-    var min = null, max = null;
-    for (var i = 0; i < this.lines.length; ++i) {
-      var line = this.lines[i];
-      var span = getMarkedSpanFor(line.markedSpans, this);
-      if (cm && !this.collapsed) { regLineChange(cm, lineNo(line), "text"); }
-      else if (cm) {
-        if (span.to != null) { max = lineNo(line); }
-        if (span.from != null) { min = lineNo(line); }
-      }
-      line.markedSpans = removeMarkedSpan(line.markedSpans, span);
-      if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm)
-        { updateLineHeight(line, textHeight(cm.display)); }
-    }
-    if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
-      var visual = visualLine(this.lines[i$1]), len = lineLength(visual);
-      if (len > cm.display.maxLineLength) {
-        cm.display.maxLine = visual;
-        cm.display.maxLineLength = len;
-        cm.display.maxLineChanged = true;
-      }
-    } }
-
-    if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }
-    this.lines.length = 0;
-    this.explicitlyCleared = true;
-    if (this.atomic && this.doc.cantEdit) {
-      this.doc.cantEdit = false;
-      if (cm) { reCheckSelection(cm.doc); }
-    }
-    if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); }
-    if (withOp) { endOperation(cm); }
-    if (this.parent) { this.parent.clear(); }
-  };
-
-  // Find the position of the marker in the document. Returns a {from,
-  // to} object by default. Side can be passed to get a specific side
-  // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
-  // Pos objects returned contain a line object, rather than a line
-  // number (used to prevent looking up the same line twice).
-  TextMarker.prototype.find = function (side, lineObj) {
-    if (side == null && this.type == "bookmark") { side = 1; }
-    var from, to;
-    for (var i = 0; i < this.lines.length; ++i) {
-      var line = this.lines[i];
-      var span = getMarkedSpanFor(line.markedSpans, this);
-      if (span.from != null) {
-        from = Pos(lineObj ? line : lineNo(line), span.from);
-        if (side == -1) { return from }
-      }
-      if (span.to != null) {
-        to = Pos(lineObj ? line : lineNo(line), span.to);
-        if (side == 1) { return to }
-      }
-    }
-    return from && {from: from, to: to}
-  };
-
-  // Signals that the marker's widget changed, and surrounding layout
-  // should be recomputed.
-  TextMarker.prototype.changed = function () {
-      var this$1 = this;
-
-    var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
-    if (!pos || !cm) { return }
-    runInOp(cm, function () {
-      var line = pos.line, lineN = lineNo(pos.line);
-      var view = findViewForLine(cm, lineN);
-      if (view) {
-        clearLineMeasurementCacheFor(view);
-        cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
-      }
-      cm.curOp.updateMaxLine = true;
-      if (!lineIsHidden(widget.doc, line) && widget.height != null) {
-        var oldHeight = widget.height;
-        widget.height = null;
-        var dHeight = widgetHeight(widget) - oldHeight;
-        if (dHeight)
-          { updateLineHeight(line, line.height + dHeight); }
-      }
-      signalLater(cm, "markerChanged", cm, this$1);
-    });
-  };
-
-  TextMarker.prototype.attachLine = function (line) {
-    if (!this.lines.length && this.doc.cm) {
-      var op = this.doc.cm.curOp;
-      if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
-        { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }
-    }
-    this.lines.push(line);
-  };
-
-  TextMarker.prototype.detachLine = function (line) {
-    this.lines.splice(indexOf(this.lines, line), 1);
-    if (!this.lines.length && this.doc.cm) {
-      var op = this.doc.cm.curOp
-      ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
-    }
-  };
-  eventMixin(TextMarker);
-
-  // Create a marker, wire it up to the right lines, and
-  function markText(doc, from, to, options, type) {
-    // Shared markers (across linked documents) are handled separately
-    // (markTextShared will call out to this again, once per
-    // document).
-    if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
-    // Ensure we are in an operation.
-    if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
-
-    var marker = new TextMarker(doc, type), diff = cmp(from, to);
-    if (options) { copyObj(options, marker, false); }
-    // Don't connect empty markers unless clearWhenEmpty is false
-    if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
-      { return marker }
-    if (marker.replacedWith) {
-      // Showing up as a widget implies collapsed (widget replaces text)
-      marker.collapsed = true;
-      marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget");
-      if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); }
-      if (options.insertLeft) { marker.widgetNode.insertLeft = true; }
-    }
-    if (marker.collapsed) {
-      if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
-          from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
-        { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
-      seeCollapsedSpans();
-    }
-
-    if (marker.addToHistory)
-      { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); }
-
-    var curLine = from.line, cm = doc.cm, updateMaxLine;
-    doc.iter(curLine, to.line + 1, function (line) {
-      if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
-        { updateMaxLine = true; }
-      if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }
-      addMarkedSpan(line, new MarkedSpan(marker,
-                                         curLine == from.line ? from.ch : null,
-                                         curLine == to.line ? to.ch : null));
-      ++curLine;
-    });
-    // lineIsHidden depends on the presence of the spans, so needs a second pass
-    if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
-      if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }
-    }); }
-
-    if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); }
-
-    if (marker.readOnly) {
-      seeReadOnlySpans();
-      if (doc.history.done.length || doc.history.undone.length)
-        { doc.clearHistory(); }
-    }
-    if (marker.collapsed) {
-      marker.id = ++nextMarkerId;
-      marker.atomic = true;
-    }
-    if (cm) {
-      // Sync editor state
-      if (updateMaxLine) { cm.curOp.updateMaxLine = true; }
-      if (marker.collapsed)
-        { regChange(cm, from.line, to.line + 1); }
-      else if (marker.className || marker.startStyle || marker.endStyle || marker.css ||
-               marker.attributes || marker.title)
-        { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } }
-      if (marker.atomic) { reCheckSelection(cm.doc); }
-      signalLater(cm, "markerAdded", cm, marker);
-    }
-    return marker
-  }
-
-  // SHARED TEXTMARKERS
-
-  // A shared marker spans multiple linked documents. It is
-  // implemented as a meta-marker-object controlling multiple normal
-  // markers.
-  var SharedTextMarker = function(markers, primary) {
-    this.markers = markers;
-    this.primary = primary;
-    for (var i = 0; i < markers.length; ++i)
-      { markers[i].parent = this; }
-  };
-
-  SharedTextMarker.prototype.clear = function () {
-    if (this.explicitlyCleared) { return }
-    this.explicitlyCleared = true;
-    for (var i = 0; i < this.markers.length; ++i)
-      { this.markers[i].clear(); }
-    signalLater(this, "clear");
-  };
-
-  SharedTextMarker.prototype.find = function (side, lineObj) {
-    return this.primary.find(side, lineObj)
-  };
-  eventMixin(SharedTextMarker);
-
-  function markTextShared(doc, from, to, options, type) {
-    options = copyObj(options);
-    options.shared = false;
-    var markers = [markText(doc, from, to, options, type)], primary = markers[0];
-    var widget = options.widgetNode;
-    linkedDocs(doc, function (doc) {
-      if (widget) { options.widgetNode = widget.cloneNode(true); }
-      markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
-      for (var i = 0; i < doc.linked.length; ++i)
-        { if (doc.linked[i].isParent) { return } }
-      primary = lst(markers);
-    });
-    return new SharedTextMarker(markers, primary)
-  }
-
-  function findSharedMarkers(doc) {
-    return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
-  }
-
-  function copySharedMarkers(doc, markers) {
-    for (var i = 0; i < markers.length; i++) {
-      var marker = markers[i], pos = marker.find();
-      var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
-      if (cmp(mFrom, mTo)) {
-        var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
-        marker.markers.push(subMark);
-        subMark.parent = marker;
-      }
-    }
-  }
-
-  function detachSharedMarkers(markers) {
-    var loop = function ( i ) {
-      var marker = markers[i], linked = [marker.primary.doc];
-      linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });
-      for (var j = 0; j < marker.markers.length; j++) {
-        var subMarker = marker.markers[j];
-        if (indexOf(linked, subMarker.doc) == -1) {
-          subMarker.parent = null;
-          marker.markers.splice(j--, 1);
-        }
-      }
-    };
-
-    for (var i = 0; i < markers.length; i++) loop( i );
-  }
-
-  var nextDocId = 0;
-  var Doc = function(text, mode, firstLine, lineSep, direction) {
-    if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
-    if (firstLine == null) { firstLine = 0; }
-
-    BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
-    this.first = firstLine;
-    this.scrollTop = this.scrollLeft = 0;
-    this.cantEdit = false;
-    this.cleanGeneration = 1;
-    this.modeFrontier = this.highlightFrontier = firstLine;
-    var start = Pos(firstLine, 0);
-    this.sel = simpleSelection(start);
-    this.history = new History(null);
-    this.id = ++nextDocId;
-    this.modeOption = mode;
-    this.lineSep = lineSep;
-    this.direction = (direction == "rtl") ? "rtl" : "ltr";
-    this.extend = false;
-
-    if (typeof text == "string") { text = this.splitLines(text); }
-    updateDoc(this, {from: start, to: start, text: text});
-    setSelection(this, simpleSelection(start), sel_dontScroll);
-  };
-
-  Doc.prototype = createObj(BranchChunk.prototype, {
-    constructor: Doc,
-    // Iterate over the document. Supports two forms -- with only one
-    // argument, it calls that for each line in the document. With
-    // three, it iterates over the range given by the first two (with
-    // the second being non-inclusive).
-    iter: function(from, to, op) {
-      if (op) { this.iterN(from - this.first, to - from, op); }
-      else { this.iterN(this.first, this.first + this.size, from); }
-    },
-
-    // Non-public interface for adding and removing lines.
-    insert: function(at, lines) {
-      var height = 0;
-      for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }
-      this.insertInner(at - this.first, lines, height);
-    },
-    remove: function(at, n) { this.removeInner(at - this.first, n); },
-
-    // From here, the methods are part of the public interface. Most
-    // are also available from CodeMirror (editor) instances.
-
-    getValue: function(lineSep) {
-      var lines = getLines(this, this.first, this.first + this.size);
-      if (lineSep === false) { return lines }
-      return lines.join(lineSep || this.lineSeparator())
-    },
-    setValue: docMethodOp(function(code) {
-      var top = Pos(this.first, 0), last = this.first + this.size - 1;
-      makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
-                        text: this.splitLines(code), origin: "setValue", full: true}, true);
-      if (this.cm) { scrollToCoords(this.cm, 0, 0); }
-      setSelection(this, simpleSelection(top), sel_dontScroll);
-    }),
-    replaceRange: function(code, from, to, origin) {
-      from = clipPos(this, from);
-      to = to ? clipPos(this, to) : from;
-      replaceRange(this, code, from, to, origin);
-    },
-    getRange: function(from, to, lineSep) {
-      var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
-      if (lineSep === false) { return lines }
-      return lines.join(lineSep || this.lineSeparator())
-    },
-
-    getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
-
-    getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
-    getLineNumber: function(line) {return lineNo(line)},
-
-    getLineHandleVisualStart: function(line) {
-      if (typeof line == "number") { line = getLine(this, line); }
-      return visualLine(line)
-    },
-
-    lineCount: function() {return this.size},
-    firstLine: function() {return this.first},
-    lastLine: function() {return this.first + this.size - 1},
-
-    clipPos: function(pos) {return clipPos(this, pos)},
-
-    getCursor: function(start) {
-      var range = this.sel.primary(), pos;
-      if (start == null || start == "head") { pos = range.head; }
-      else if (start == "anchor") { pos = range.anchor; }
-      else if (start == "end" || start == "to" || start === false) { pos = range.to(); }
-      else { pos = range.from(); }
-      return pos
-    },
-    listSelections: function() { return this.sel.ranges },
-    somethingSelected: function() {return this.sel.somethingSelected()},
-
-    setCursor: docMethodOp(function(line, ch, options) {
-      setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
-    }),
-    setSelection: docMethodOp(function(anchor, head, options) {
-      setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
-    }),
-    extendSelection: docMethodOp(function(head, other, options) {
-      extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
-    }),
-    extendSelections: docMethodOp(function(heads, options) {
-      extendSelections(this, clipPosArray(this, heads), options);
-    }),
-    extendSelectionsBy: docMethodOp(function(f, options) {
-      var heads = map(this.sel.ranges, f);
-      extendSelections(this, clipPosArray(this, heads), options);
-    }),
-    setSelections: docMethodOp(function(ranges, primary, options) {
-      if (!ranges.length) { return }
-      var out = [];
-      for (var i = 0; i < ranges.length; i++)
-        { out[i] = new Range(clipPos(this, ranges[i].anchor),
-                           clipPos(this, ranges[i].head)); }
-      if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }
-      setSelection(this, normalizeSelection(this.cm, out, primary), options);
-    }),
-    addSelection: docMethodOp(function(anchor, head, options) {
-      var ranges = this.sel.ranges.slice(0);
-      ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
-      setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options);
-    }),
-
-    getSelection: function(lineSep) {
-      var ranges = this.sel.ranges, lines;
-      for (var i = 0; i < ranges.length; i++) {
-        var sel = getBetween(this, ranges[i].from(), ranges[i].to());
-        lines = lines ? lines.concat(sel) : sel;
-      }
-      if (lineSep === false) { return lines }
-      else { return lines.join(lineSep || this.lineSeparator()) }
-    },
-    getSelections: function(lineSep) {
-      var parts = [], ranges = this.sel.ranges;
-      for (var i = 0; i < ranges.length; i++) {
-        var sel = getBetween(this, ranges[i].from(), ranges[i].to());
-        if (lineSep !== false) { sel = sel.join(lineSep || this.lineSeparator()); }
-        parts[i] = sel;
-      }
-      return parts
-    },
-    replaceSelection: function(code, collapse, origin) {
-      var dup = [];
-      for (var i = 0; i < this.sel.ranges.length; i++)
-        { dup[i] = code; }
-      this.replaceSelections(dup, collapse, origin || "+input");
-    },
-    replaceSelections: docMethodOp(function(code, collapse, origin) {
-      var changes = [], sel = this.sel;
-      for (var i = 0; i < sel.ranges.length; i++) {
-        var range = sel.ranges[i];
-        changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin};
-      }
-      var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
-      for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
-        { makeChange(this, changes[i$1]); }
-      if (newSel) { setSelectionReplaceHistory(this, newSel); }
-      else if (this.cm) { ensureCursorVisible(this.cm); }
-    }),
-    undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
-    redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
-    undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
-    redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
-
-    setExtending: function(val) {this.extend = val;},
-    getExtending: function() {return this.extend},
-
-    historySize: function() {
-      var hist = this.history, done = 0, undone = 0;
-      for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }
-      for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }
-      return {undo: done, redo: undone}
-    },
-    clearHistory: function() {
-      var this$1 = this;
-
-      this.history = new History(this.history.maxGeneration);
-      linkedDocs(this, function (doc) { return doc.history = this$1.history; }, true);
-    },
-
-    markClean: function() {
-      this.cleanGeneration = this.changeGeneration(true);
-    },
-    changeGeneration: function(forceSplit) {
-      if (forceSplit)
-        { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }
-      return this.history.generation
-    },
-    isClean: function (gen) {
-      return this.history.generation == (gen || this.cleanGeneration)
-    },
-
-    getHistory: function() {
-      return {done: copyHistoryArray(this.history.done),
-              undone: copyHistoryArray(this.history.undone)}
-    },
-    setHistory: function(histData) {
-      var hist = this.history = new History(this.history.maxGeneration);
-      hist.done = copyHistoryArray(histData.done.slice(0), null, true);
-      hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
-    },
-
-    setGutterMarker: docMethodOp(function(line, gutterID, value) {
-      return changeLine(this, line, "gutter", function (line) {
-        var markers = line.gutterMarkers || (line.gutterMarkers = {});
-        markers[gutterID] = value;
-        if (!value && isEmpty(markers)) { line.gutterMarkers = null; }
-        return true
-      })
-    }),
-
-    clearGutter: docMethodOp(function(gutterID) {
-      var this$1 = this;
-
-      this.iter(function (line) {
-        if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
-          changeLine(this$1, line, "gutter", function () {
-            line.gutterMarkers[gutterID] = null;
-            if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }
-            return true
-          });
-        }
-      });
-    }),
-
-    lineInfo: function(line) {
-      var n;
-      if (typeof line == "number") {
-        if (!isLine(this, line)) { return null }
-        n = line;
-        line = getLine(this, line);
-        if (!line) { return null }
-      } else {
-        n = lineNo(line);
-        if (n == null) { return null }
-      }
-      return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
-              textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
-              widgets: line.widgets}
-    },
-
-    addLineClass: docMethodOp(function(handle, where, cls) {
-      return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
-        var prop = where == "text" ? "textClass"
-                 : where == "background" ? "bgClass"
-                 : where == "gutter" ? "gutterClass" : "wrapClass";
-        if (!line[prop]) { line[prop] = cls; }
-        else if (classTest(cls).test(line[prop])) { return false }
-        else { line[prop] += " " + cls; }
-        return true
-      })
-    }),
-    removeLineClass: docMethodOp(function(handle, where, cls) {
-      return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
-        var prop = where == "text" ? "textClass"
-                 : where == "background" ? "bgClass"
-                 : where == "gutter" ? "gutterClass" : "wrapClass";
-        var cur = line[prop];
-        if (!cur) { return false }
-        else if (cls == null) { line[prop] = null; }
-        else {
-          var found = cur.match(classTest(cls));
-          if (!found) { return false }
-          var end = found.index + found[0].length;
-          line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
-        }
-        return true
-      })
-    }),
-
-    addLineWidget: docMethodOp(function(handle, node, options) {
-      return addLineWidget(this, handle, node, options)
-    }),
-    removeLineWidget: function(widget) { widget.clear(); },
-
-    markText: function(from, to, options) {
-      return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
-    },
-    setBookmark: function(pos, options) {
-      var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
-                      insertLeft: options && options.insertLeft,
-                      clearWhenEmpty: false, shared: options && options.shared,
-                      handleMouseEvents: options && options.handleMouseEvents};
-      pos = clipPos(this, pos);
-      return markText(this, pos, pos, realOpts, "bookmark")
-    },
-    findMarksAt: function(pos) {
-      pos = clipPos(this, pos);
-      var markers = [], spans = getLine(this, pos.line).markedSpans;
-      if (spans) { for (var i = 0; i < spans.length; ++i) {
-        var span = spans[i];
-        if ((span.from == null || span.from <= pos.ch) &&
-            (span.to == null || span.to >= pos.ch))
-          { markers.push(span.marker.parent || span.marker); }
-      } }
-      return markers
-    },
-    findMarks: function(from, to, filter) {
-      from = clipPos(this, from); to = clipPos(this, to);
-      var found = [], lineNo = from.line;
-      this.iter(from.line, to.line + 1, function (line) {
-        var spans = line.markedSpans;
-        if (spans) { for (var i = 0; i < spans.length; i++) {
-          var span = spans[i];
-          if (!(span.to != null && lineNo == from.line && from.ch >= span.to ||
-                span.from == null && lineNo != from.line ||
-                span.from != null && lineNo == to.line && span.from >= to.ch) &&
-              (!filter || filter(span.marker)))
-            { found.push(span.marker.parent || span.marker); }
-        } }
-        ++lineNo;
-      });
-      return found
-    },
-    getAllMarks: function() {
-      var markers = [];
-      this.iter(function (line) {
-        var sps = line.markedSpans;
-        if (sps) { for (var i = 0; i < sps.length; ++i)
-          { if (sps[i].from != null) { markers.push(sps[i].marker); } } }
-      });
-      return markers
-    },
-
-    posFromIndex: function(off) {
-      var ch, lineNo = this.first, sepSize = this.lineSeparator().length;
-      this.iter(function (line) {
-        var sz = line.text.length + sepSize;
-        if (sz > off) { ch = off; return true }
-        off -= sz;
-        ++lineNo;
-      });
-      return clipPos(this, Pos(lineNo, ch))
-    },
-    indexFromPos: function (coords) {
-      coords = clipPos(this, coords);
-      var index = coords.ch;
-      if (coords.line < this.first || coords.ch < 0) { return 0 }
-      var sepSize = this.lineSeparator().length;
-      this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
-        index += line.text.length + sepSize;
-      });
-      return index
-    },
-
-    copy: function(copyHistory) {
-      var doc = new Doc(getLines(this, this.first, this.first + this.size),
-                        this.modeOption, this.first, this.lineSep, this.direction);
-      doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
-      doc.sel = this.sel;
-      doc.extend = false;
-      if (copyHistory) {
-        doc.history.undoDepth = this.history.undoDepth;
-        doc.setHistory(this.getHistory());
-      }
-      return doc
-    },
-
-    linkedDoc: function(options) {
-      if (!options) { options = {}; }
-      var from = this.first, to = this.first + this.size;
-      if (options.from != null && options.from > from) { from = options.from; }
-      if (options.to != null && options.to < to) { to = options.to; }
-      var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);
-      if (options.sharedHist) { copy.history = this.history
-      ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
-      copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
-      copySharedMarkers(copy, findSharedMarkers(this));
-      return copy
-    },
-    unlinkDoc: function(other) {
-      if (other instanceof CodeMirror) { other = other.doc; }
-      if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
-        var link = this.linked[i];
-        if (link.doc != other) { continue }
-        this.linked.splice(i, 1);
-        other.unlinkDoc(this);
-        detachSharedMarkers(findSharedMarkers(this));
-        break
-      } }
-      // If the histories were shared, split them again
-      if (other.history == this.history) {
-        var splitIds = [other.id];
-        linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);
-        other.history = new History(null);
-        other.history.done = copyHistoryArray(this.history.done, splitIds);
-        other.history.undone = copyHistoryArray(this.history.undone, splitIds);
-      }
-    },
-    iterLinkedDocs: function(f) {linkedDocs(this, f);},
-
-    getMode: function() {return this.mode},
-    getEditor: function() {return this.cm},
-
-    splitLines: function(str) {
-      if (this.lineSep) { return str.split(this.lineSep) }
-      return splitLinesAuto(str)
-    },
-    lineSeparator: function() { return this.lineSep || "\n" },
-
-    setDirection: docMethodOp(function (dir) {
-      if (dir != "rtl") { dir = "ltr"; }
-      if (dir == this.direction) { return }
-      this.direction = dir;
-      this.iter(function (line) { return line.order = null; });
-      if (this.cm) { directionChanged(this.cm); }
-    })
-  });
-
-  // Public alias.
-  Doc.prototype.eachLine = Doc.prototype.iter;
-
-  // Kludge to work around strange IE behavior where it'll sometimes
-  // re-fire a series of drag-related events right after the drop (#1551)
-  var lastDrop = 0;
-
-  function onDrop(e) {
-    var cm = this;
-    clearDragCursor(cm);
-    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
-      { return }
-    e_preventDefault(e);
-    if (ie) { lastDrop = +new Date; }
-    var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
-    if (!pos || cm.isReadOnly()) { return }
-    // Might be a file drop, in which case we simply extract the text
-    // and insert it.
-    if (files && files.length && window.FileReader && window.File) {
-      var n = files.length, text = Array(n), read = 0;
-      var markAsReadAndPasteIfAllFilesAreRead = function () {
-        if (++read == n) {
-          operation(cm, function () {
-            pos = clipPos(cm.doc, pos);
-            var change = {from: pos, to: pos,
-                          text: cm.doc.splitLines(
-                              text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())),
-                          origin: "paste"};
-            makeChange(cm.doc, change);
-            setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change))));
-          })();
-        }
-      };
-      var readTextFromFile = function (file, i) {
-        if (cm.options.allowDropFileTypes &&
-            indexOf(cm.options.allowDropFileTypes, file.type) == -1) {
-          markAsReadAndPasteIfAllFilesAreRead();
-          return
-        }
-        var reader = new FileReader;
-        reader.onerror = function () { return markAsReadAndPasteIfAllFilesAreRead(); };
-        reader.onload = function () {
-          var content = reader.result;
-          if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) {
-            markAsReadAndPasteIfAllFilesAreRead();
-            return
-          }
-          text[i] = content;
-          markAsReadAndPasteIfAllFilesAreRead();
-        };
-        reader.readAsText(file);
-      };
-      for (var i = 0; i < files.length; i++) { readTextFromFile(files[i], i); }
-    } else { // Normal drop
-      // Don't do a replace if the drop happened inside of the selected text.
-      if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
-        cm.state.draggingText(e);
-        // Ensure the editor is re-focused
-        setTimeout(function () { return cm.display.input.focus(); }, 20);
-        return
-      }
-      try {
-        var text$1 = e.dataTransfer.getData("Text");
-        if (text$1) {
-          var selected;
-          if (cm.state.draggingText && !cm.state.draggingText.copy)
-            { selected = cm.listSelections(); }
-          setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
-          if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
-            { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } }
-          cm.replaceSelection(text$1, "around", "paste");
-          cm.display.input.focus();
-        }
-      }
-      catch(e$1){}
-    }
-  }
-
-  function onDragStart(cm, e) {
-    if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
-    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
-
-    e.dataTransfer.setData("Text", cm.getSelection());
-    e.dataTransfer.effectAllowed = "copyMove";
-
-    // Use dummy image instead of default browsers image.
-    // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
-    if (e.dataTransfer.setDragImage && !safari) {
-      var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
-      img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
-      if (presto) {
-        img.width = img.height = 1;
-        cm.display.wrapper.appendChild(img);
-        // Force a relayout, or Opera won't use our image for some obscure reason
-        img._top = img.offsetTop;
-      }
-      e.dataTransfer.setDragImage(img, 0, 0);
-      if (presto) { img.parentNode.removeChild(img); }
-    }
-  }
-
-  function onDragOver(cm, e) {
-    var pos = posFromMouse(cm, e);
-    if (!pos) { return }
-    var frag = document.createDocumentFragment();
-    drawSelectionCursor(cm, pos, frag);
-    if (!cm.display.dragCursor) {
-      cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
-      cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
-    }
-    removeChildrenAndAdd(cm.display.dragCursor, frag);
-  }
-
-  function clearDragCursor(cm) {
-    if (cm.display.dragCursor) {
-      cm.display.lineSpace.removeChild(cm.display.dragCursor);
-      cm.display.dragCursor = null;
-    }
-  }
-
-  // These must be handled carefully, because naively registering a
-  // handler for each editor will cause the editors to never be
-  // garbage collected.
-
-  function forEachCodeMirror(f) {
-    if (!document.getElementsByClassName) { return }
-    var byClass = document.getElementsByClassName("CodeMirror"), editors = [];
-    for (var i = 0; i < byClass.length; i++) {
-      var cm = byClass[i].CodeMirror;
-      if (cm) { editors.push(cm); }
-    }
-    if (editors.length) { editors[0].operation(function () {
-      for (var i = 0; i < editors.length; i++) { f(editors[i]); }
-    }); }
-  }
-
-  var globalsRegistered = false;
-  function ensureGlobalHandlers() {
-    if (globalsRegistered) { return }
-    registerGlobalHandlers();
-    globalsRegistered = true;
-  }
-  function registerGlobalHandlers() {
-    // When the window resizes, we need to refresh active editors.
-    var resizeTimer;
-    on(window, "resize", function () {
-      if (resizeTimer == null) { resizeTimer = setTimeout(function () {
-        resizeTimer = null;
-        forEachCodeMirror(onResize);
-      }, 100); }
-    });
-    // When the window loses focus, we want to show the editor as blurred
-    on(window, "blur", function () { return forEachCodeMirror(onBlur); });
-  }
-  // Called when the window resizes
-  function onResize(cm) {
-    var d = cm.display;
-    // Might be a text scaling operation, clear size caches.
-    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
-    d.scrollbarsClipped = false;
-    cm.setSize();
-  }
-
-  var keyNames = {
-    3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
-    19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
-    36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
-    46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
-    106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock",
-    173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
-    221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
-    63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
-  };
-
-  // Number keys
-  for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }
-  // Alphabetic keys
-  for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }
-  // Function keys
-  for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; }
-
-  var keyMap = {};
-
-  keyMap.basic = {
-    "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
-    "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
-    "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
-    "Tab": "defaultTab", "Shift-Tab": "indentAuto",
-    "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
-    "Esc": "singleSelection"
-  };
-  // Note that the save and find-related commands aren't defined by
-  // default. User code or addons can define them. Unknown commands
-  // are simply ignored.
-  keyMap.pcDefault = {
-    "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
-    "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
-    "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
-    "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
-    "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
-    "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
-    "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
-    "fallthrough": "basic"
-  };
-  // Very basic readline/emacs-style bindings, which are standard on Mac.
-  keyMap.emacsy = {
-    "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
-    "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
-    "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
-    "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
-    "Ctrl-O": "openLine"
-  };
-  keyMap.macDefault = {
-    "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
-    "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
-    "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
-    "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
-    "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
-    "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
-    "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
-    "fallthrough": ["basic", "emacsy"]
-  };
-  keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
-
-  // KEYMAP DISPATCH
-
-  function normalizeKeyName(name) {
-    var parts = name.split(/-(?!$)/);
-    name = parts[parts.length - 1];
-    var alt, ctrl, shift, cmd;
-    for (var i = 0; i < parts.length - 1; i++) {
-      var mod = parts[i];
-      if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }
-      else if (/^a(lt)?$/i.test(mod)) { alt = true; }
-      else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }
-      else if (/^s(hift)?$/i.test(mod)) { shift = true; }
-      else { throw new Error("Unrecognized modifier name: " + mod) }
-    }
-    if (alt) { name = "Alt-" + name; }
-    if (ctrl) { name = "Ctrl-" + name; }
-    if (cmd) { name = "Cmd-" + name; }
-    if (shift) { name = "Shift-" + name; }
-    return name
-  }
-
-  // This is a kludge to keep keymaps mostly working as raw objects
-  // (backwards compatibility) while at the same time support features
-  // like normalization and multi-stroke key bindings. It compiles a
-  // new normalized keymap, and then updates the old object to reflect
-  // this.
-  function normalizeKeyMap(keymap) {
-    var copy = {};
-    for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
-      var value = keymap[keyname];
-      if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
-      if (value == "...") { delete keymap[keyname]; continue }
-
-      var keys = map(keyname.split(" "), normalizeKeyName);
-      for (var i = 0; i < keys.length; i++) {
-        var val = (void 0), name = (void 0);
-        if (i == keys.length - 1) {
-          name = keys.join(" ");
-          val = value;
-        } else {
-          name = keys.slice(0, i + 1).join(" ");
-          val = "...";
-        }
-        var prev = copy[name];
-        if (!prev) { copy[name] = val; }
-        else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
-      }
-      delete keymap[keyname];
-    } }
-    for (var prop in copy) { keymap[prop] = copy[prop]; }
-    return keymap
-  }
-
-  function lookupKey(key, map, handle, context) {
-    map = getKeyMap(map);
-    var found = map.call ? map.call(key, context) : map[key];
-    if (found === false) { return "nothing" }
-    if (found === "...") { return "multi" }
-    if (found != null && handle(found)) { return "handled" }
-
-    if (map.fallthrough) {
-      if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
-        { return lookupKey(key, map.fallthrough, handle, context) }
-      for (var i = 0; i < map.fallthrough.length; i++) {
-        var result = lookupKey(key, map.fallthrough[i], handle, context);
-        if (result) { return result }
-      }
-    }
-  }
-
-  // Modifier key presses don't count as 'real' key presses for the
-  // purpose of keymap fallthrough.
-  function isModifierKey(value) {
-    var name = typeof value == "string" ? value : keyNames[value.keyCode];
-    return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
-  }
-
-  function addModifierNames(name, event, noShift) {
-    var base = name;
-    if (event.altKey && base != "Alt") { name = "Alt-" + name; }
-    if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; }
-    if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; }
-    if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; }
-    return name
-  }
-
-  // Look up the name of a key as indicated by an event object.
-  function keyName(event, noShift) {
-    if (presto && event.keyCode == 34 && event["char"]) { return false }
-    var name = keyNames[event.keyCode];
-    if (name == null || event.altGraphKey) { return false }
-    // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
-    // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
-    if (event.keyCode == 3 && event.code) { name = event.code; }
-    return addModifierNames(name, event, noShift)
-  }
-
-  function getKeyMap(val) {
-    return typeof val == "string" ? keyMap[val] : val
-  }
-
-  // Helper for deleting text near the selection(s), used to implement
-  // backspace, delete, and similar functionality.
-  function deleteNearSelection(cm, compute) {
-    var ranges = cm.doc.sel.ranges, kill = [];
-    // Build up a set of ranges to kill first, merging overlapping
-    // ranges.
-    for (var i = 0; i < ranges.length; i++) {
-      var toKill = compute(ranges[i]);
-      while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
-        var replaced = kill.pop();
-        if (cmp(replaced.from, toKill.from) < 0) {
-          toKill.from = replaced.from;
-          break
-        }
-      }
-      kill.push(toKill);
-    }
-    // Next, remove those actual ranges.
-    runInOp(cm, function () {
-      for (var i = kill.length - 1; i >= 0; i--)
-        { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); }
-      ensureCursorVisible(cm);
-    });
-  }
-
-  function moveCharLogically(line, ch, dir) {
-    var target = skipExtendingChars(line.text, ch + dir, dir);
-    return target < 0 || target > line.text.length ? null : target
-  }
-
-  function moveLogically(line, start, dir) {
-    var ch = moveCharLogically(line, start.ch, dir);
-    return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
-  }
-
-  function endOfLine(visually, cm, lineObj, lineNo, dir) {
-    if (visually) {
-      if (cm.doc.direction == "rtl") { dir = -dir; }
-      var order = getOrder(lineObj, cm.doc.direction);
-      if (order) {
-        var part = dir < 0 ? lst(order) : order[0];
-        var moveInStorageOrder = (dir < 0) == (part.level == 1);
-        var sticky = moveInStorageOrder ? "after" : "before";
-        var ch;
-        // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
-        // it could be that the last bidi part is not on the last visual line,
-        // since visual lines contain content order-consecutive chunks.
-        // Thus, in rtl, we are looking for the first (content-order) character
-        // in the rtl chunk that is on the last line (that is, the same line
-        // as the last (content-order) character).
-        if (part.level > 0 || cm.doc.direction == "rtl") {
-          var prep = prepareMeasureForLine(cm, lineObj);
-          ch = dir < 0 ? lineObj.text.length - 1 : 0;
-          var targetTop = measureCharPrepared(cm, prep, ch).top;
-          ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);
-          if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); }
-        } else { ch = dir < 0 ? part.to : part.from; }
-        return new Pos(lineNo, ch, sticky)
-      }
-    }
-    return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
-  }
-
-  function moveVisually(cm, line, start, dir) {
-    var bidi = getOrder(line, cm.doc.direction);
-    if (!bidi) { return moveLogically(line, start, dir) }
-    if (start.ch >= line.text.length) {
-      start.ch = line.text.length;
-      start.sticky = "before";
-    } else if (start.ch <= 0) {
-      start.ch = 0;
-      start.sticky = "after";
-    }
-    var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];
-    if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
-      // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
-      // nothing interesting happens.
-      return moveLogically(line, start, dir)
-    }
-
-    var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };
-    var prep;
-    var getWrappedLineExtent = function (ch) {
-      if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
-      prep = prep || prepareMeasureForLine(cm, line);
-      return wrappedLineExtentChar(cm, line, prep, ch)
-    };
-    var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch);
-
-    if (cm.doc.direction == "rtl" || part.level == 1) {
-      var moveInStorageOrder = (part.level == 1) == (dir < 0);
-      var ch = mv(start, moveInStorageOrder ? 1 : -1);
-      if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
-        // Case 2: We move within an rtl part or in an rtl editor on the same visual line
-        var sticky = moveInStorageOrder ? "before" : "after";
-        return new Pos(start.line, ch, sticky)
-      }
-    }
-
-    // Case 3: Could not move within this bidi part in this visual line, so leave
-    // the current bidi part
-
-    var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
-      var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
-        ? new Pos(start.line, mv(ch, 1), "before")
-        : new Pos(start.line, ch, "after"); };
-
-      for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
-        var part = bidi[partPos];
-        var moveInStorageOrder = (dir > 0) == (part.level != 1);
-        var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);
-        if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
-        ch = moveInStorageOrder ? part.from : mv(part.to, -1);
-        if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
-      }
-    };
-
-    // Case 3a: Look for other bidi parts on the same visual line
-    var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);
-    if (res) { return res }
-
-    // Case 3b: Look for other bidi parts on the next visual line
-    var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);
-    if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
-      res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));
-      if (res) { return res }
-    }
-
-    // Case 4: Nowhere to move
-    return null
-  }
-
-  // Commands are parameter-less actions that can be performed on an
-  // editor, mostly used for keybindings.
-  var commands = {
-    selectAll: selectAll,
-    singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
-    killLine: function (cm) { return deleteNearSelection(cm, function (range) {
-      if (range.empty()) {
-        var len = getLine(cm.doc, range.head.line).text.length;
-        if (range.head.ch == len && range.head.line < cm.lastLine())
-          { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
-        else
-          { return {from: range.head, to: Pos(range.head.line, len)} }
-      } else {
-        return {from: range.from(), to: range.to()}
-      }
-    }); },
-    deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
-      from: Pos(range.from().line, 0),
-      to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
-    }); }); },
-    delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
-      from: Pos(range.from().line, 0), to: range.from()
-    }); }); },
-    delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
-      var top = cm.charCoords(range.head, "div").top + 5;
-      var leftPos = cm.coordsChar({left: 0, top: top}, "div");
-      return {from: leftPos, to: range.from()}
-    }); },
-    delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
-      var top = cm.charCoords(range.head, "div").top + 5;
-      var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
-      return {from: range.from(), to: rightPos }
-    }); },
-    undo: function (cm) { return cm.undo(); },
-    redo: function (cm) { return cm.redo(); },
-    undoSelection: function (cm) { return cm.undoSelection(); },
-    redoSelection: function (cm) { return cm.redoSelection(); },
-    goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
-    goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
-    goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
-      {origin: "+move", bias: 1}
-    ); },
-    goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
-      {origin: "+move", bias: 1}
-    ); },
-    goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
-      {origin: "+move", bias: -1}
-    ); },
-    goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
-      var top = cm.cursorCoords(range.head, "div").top + 5;
-      return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
-    }, sel_move); },
-    goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
-      var top = cm.cursorCoords(range.head, "div").top + 5;
-      return cm.coordsChar({left: 0, top: top}, "div")
-    }, sel_move); },
-    goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
-      var top = cm.cursorCoords(range.head, "div").top + 5;
-      var pos = cm.coordsChar({left: 0, top: top}, "div");
-      if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
-      return pos
-    }, sel_move); },
-    goLineUp: function (cm) { return cm.moveV(-1, "line"); },
-    goLineDown: function (cm) { return cm.moveV(1, "line"); },
-    goPageUp: function (cm) { return cm.moveV(-1, "page"); },
-    goPageDown: function (cm) { return cm.moveV(1, "page"); },
-    goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
-    goCharRight: function (cm) { return cm.moveH(1, "char"); },
-    goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
-    goColumnRight: function (cm) { return cm.moveH(1, "column"); },
-    goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
-    goGroupRight: function (cm) { return cm.moveH(1, "group"); },
-    goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
-    goWordRight: function (cm) { return cm.moveH(1, "word"); },
-    delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
-    delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
-    delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
-    delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
-    delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
-    delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
-    indentAuto: function (cm) { return cm.indentSelection("smart"); },
-    indentMore: function (cm) { return cm.indentSelection("add"); },
-    indentLess: function (cm) { return cm.indentSelection("subtract"); },
-    insertTab: function (cm) { return cm.replaceSelection("\t"); },
-    insertSoftTab: function (cm) {
-      var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
-      for (var i = 0; i < ranges.length; i++) {
-        var pos = ranges[i].from();
-        var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
-        spaces.push(spaceStr(tabSize - col % tabSize));
-      }
-      cm.replaceSelections(spaces);
-    },
-    defaultTab: function (cm) {
-      if (cm.somethingSelected()) { cm.indentSelection("add"); }
-      else { cm.execCommand("insertTab"); }
-    },
-    // Swap the two chars left and right of each selection's head.
-    // Move cursor behind the two swapped characters afterwards.
-    //
-    // Doesn't consider line feeds a character.
-    // Doesn't scan more than one line above to find a character.
-    // Doesn't do anything on an empty line.
-    // Doesn't do anything with non-empty selections.
-    transposeChars: function (cm) { return runInOp(cm, function () {
-      var ranges = cm.listSelections(), newSel = [];
-      for (var i = 0; i < ranges.length; i++) {
-        if (!ranges[i].empty()) { continue }
-        var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
-        if (line) {
-          if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }
-          if (cur.ch > 0) {
-            cur = new Pos(cur.line, cur.ch + 1);
-            cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
-                            Pos(cur.line, cur.ch - 2), cur, "+transpose");
-          } else if (cur.line > cm.doc.first) {
-            var prev = getLine(cm.doc, cur.line - 1).text;
-            if (prev) {
-              cur = new Pos(cur.line, 1);
-              cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
-                              prev.charAt(prev.length - 1),
-                              Pos(cur.line - 1, prev.length - 1), cur, "+transpose");
-            }
-          }
-        }
-        newSel.push(new Range(cur, cur));
-      }
-      cm.setSelections(newSel);
-    }); },
-    newlineAndIndent: function (cm) { return runInOp(cm, function () {
-      var sels = cm.listSelections();
-      for (var i = sels.length - 1; i >= 0; i--)
-        { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); }
-      sels = cm.listSelections();
-      for (var i$1 = 0; i$1 < sels.length; i$1++)
-        { cm.indentLine(sels[i$1].from().line, null, true); }
-      ensureCursorVisible(cm);
-    }); },
-    openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
-    toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
-  };
-
-
-  function lineStart(cm, lineN) {
-    var line = getLine(cm.doc, lineN);
-    var visual = visualLine(line);
-    if (visual != line) { lineN = lineNo(visual); }
-    return endOfLine(true, cm, visual, lineN, 1)
-  }
-  function lineEnd(cm, lineN) {
-    var line = getLine(cm.doc, lineN);
-    var visual = visualLineEnd(line);
-    if (visual != line) { lineN = lineNo(visual); }
-    return endOfLine(true, cm, line, lineN, -1)
-  }
-  function lineStartSmart(cm, pos) {
-    var start = lineStart(cm, pos.line);
-    var line = getLine(cm.doc, start.line);
-    var order = getOrder(line, cm.doc.direction);
-    if (!order || order[0].level == 0) {
-      var firstNonWS = Math.max(start.ch, line.text.search(/\S/));
-      var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
-      return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
-    }
-    return start
-  }
-
-  // Run a handler that was bound to a key.
-  function doHandleBinding(cm, bound, dropShift) {
-    if (typeof bound == "string") {
-      bound = commands[bound];
-      if (!bound) { return false }
-    }
-    // Ensure previous input has been read, so that the handler sees a
-    // consistent view of the document
-    cm.display.input.ensurePolled();
-    var prevShift = cm.display.shift, done = false;
-    try {
-      if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
-      if (dropShift) { cm.display.shift = false; }
-      done = bound(cm) != Pass;
-    } finally {
-      cm.display.shift = prevShift;
-      cm.state.suppressEdits = false;
-    }
-    return done
-  }
-
-  function lookupKeyForEditor(cm, name, handle) {
-    for (var i = 0; i < cm.state.keyMaps.length; i++) {
-      var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
-      if (result) { return result }
-    }
-    return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
-      || lookupKey(name, cm.options.keyMap, handle, cm)
-  }
-
-  // Note that, despite the name, this function is also used to check
-  // for bound mouse clicks.
-
-  var stopSeq = new Delayed;
-
-  function dispatchKey(cm, name, e, handle) {
-    var seq = cm.state.keySeq;
-    if (seq) {
-      if (isModifierKey(name)) { return "handled" }
-      if (/\'$/.test(name))
-        { cm.state.keySeq = null; }
-      else
-        { stopSeq.set(50, function () {
-          if (cm.state.keySeq == seq) {
-            cm.state.keySeq = null;
-            cm.display.input.reset();
-          }
-        }); }
-      if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true }
-    }
-    return dispatchKeyInner(cm, name, e, handle)
-  }
-
-  function dispatchKeyInner(cm, name, e, handle) {
-    var result = lookupKeyForEditor(cm, name, handle);
-
-    if (result == "multi")
-      { cm.state.keySeq = name; }
-    if (result == "handled")
-      { signalLater(cm, "keyHandled", cm, name, e); }
-
-    if (result == "handled" || result == "multi") {
-      e_preventDefault(e);
-      restartBlink(cm);
-    }
-
-    return !!result
-  }
-
-  // Handle a key from the keydown event.
-  function handleKeyBinding(cm, e) {
-    var name = keyName(e, true);
-    if (!name) { return false }
-
-    if (e.shiftKey && !cm.state.keySeq) {
-      // First try to resolve full name (including 'Shift-'). Failing
-      // that, see if there is a cursor-motion command (starting with
-      // 'go') bound to the keyname without 'Shift-'.
-      return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
-          || dispatchKey(cm, name, e, function (b) {
-               if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
-                 { return doHandleBinding(cm, b) }
-             })
-    } else {
-      return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
-    }
-  }
-
-  // Handle a key from the keypress event
-  function handleCharBinding(cm, e, ch) {
-    return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
-  }
-
-  var lastStoppedKey = null;
-  function onKeyDown(e) {
-    var cm = this;
-    if (e.target && e.target != cm.display.input.getField()) { return }
-    cm.curOp.focus = activeElt();
-    if (signalDOMEvent(cm, e)) { return }
-    // IE does strange things with escape.
-    if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }
-    var code = e.keyCode;
-    cm.display.shift = code == 16 || e.shiftKey;
-    var handled = handleKeyBinding(cm, e);
-    if (presto) {
-      lastStoppedKey = handled ? code : null;
-      // Opera has no cut event... we try to at least catch the key combo
-      if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
-        { cm.replaceSelection("", null, "cut"); }
-    }
-    if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand)
-      { document.execCommand("cut"); }
-
-    // Turn mouse into crosshair when Alt is held on Mac.
-    if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
-      { showCrossHair(cm); }
-  }
-
-  function showCrossHair(cm) {
-    var lineDiv = cm.display.lineDiv;
-    addClass(lineDiv, "CodeMirror-crosshair");
-
-    function up(e) {
-      if (e.keyCode == 18 || !e.altKey) {
-        rmClass(lineDiv, "CodeMirror-crosshair");
-        off(document, "keyup", up);
-        off(document, "mouseover", up);
-      }
-    }
-    on(document, "keyup", up);
-    on(document, "mouseover", up);
-  }
-
-  function onKeyUp(e) {
-    if (e.keyCode == 16) { this.doc.sel.shift = false; }
-    signalDOMEvent(this, e);
-  }
-
-  function onKeyPress(e) {
-    var cm = this;
-    if (e.target && e.target != cm.display.input.getField()) { return }
-    if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
-    var keyCode = e.keyCode, charCode = e.charCode;
-    if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
-    if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
-    var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
-    // Some browsers fire keypress events for backspace
-    if (ch == "\x08") { return }
-    if (handleCharBinding(cm, e, ch)) { return }
-    cm.display.input.onKeyPress(e);
-  }
-
-  var DOUBLECLICK_DELAY = 400;
-
-  var PastClick = function(time, pos, button) {
-    this.time = time;
-    this.pos = pos;
-    this.button = button;
-  };
-
-  PastClick.prototype.compare = function (time, pos, button) {
-    return this.time + DOUBLECLICK_DELAY > time &&
-      cmp(pos, this.pos) == 0 && button == this.button
-  };
-
-  var lastClick, lastDoubleClick;
-  function clickRepeat(pos, button) {
-    var now = +new Date;
-    if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
-      lastClick = lastDoubleClick = null;
-      return "triple"
-    } else if (lastClick && lastClick.compare(now, pos, button)) {
-      lastDoubleClick = new PastClick(now, pos, button);
-      lastClick = null;
-      return "double"
-    } else {
-      lastClick = new PastClick(now, pos, button);
-      lastDoubleClick = null;
-      return "single"
-    }
-  }
-
-  // A mouse down can be a single click, double click, triple click,
-  // start of selection drag, start of text drag, new cursor
-  // (ctrl-click), rectangle drag (alt-drag), or xwin
-  // middle-click-paste. Or it might be a click on something we should
-  // not interfere with, such as a scrollbar or widget.
-  function onMouseDown(e) {
-    var cm = this, display = cm.display;
-    if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
-    display.input.ensurePolled();
-    display.shift = e.shiftKey;
-
-    if (eventInWidget(display, e)) {
-      if (!webkit) {
-        // Briefly turn off draggability, to allow widgets to do
-        // normal dragging things.
-        display.scroller.draggable = false;
-        setTimeout(function () { return display.scroller.draggable = true; }, 100);
-      }
-      return
-    }
-    if (clickInGutter(cm, e)) { return }
-    var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single";
-    window.focus();
-
-    // #3261: make sure, that we're not starting a second selection
-    if (button == 1 && cm.state.selectingText)
-      { cm.state.selectingText(e); }
-
-    if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }
-
-    if (button == 1) {
-      if (pos) { leftButtonDown(cm, pos, repeat, e); }
-      else if (e_target(e) == display.scroller) { e_preventDefault(e); }
-    } else if (button == 2) {
-      if (pos) { extendSelection(cm.doc, pos); }
-      setTimeout(function () { return display.input.focus(); }, 20);
-    } else if (button == 3) {
-      if (captureRightClick) { cm.display.input.onContextMenu(e); }
-      else { delayBlurEvent(cm); }
-    }
-  }
-
-  function handleMappedButton(cm, button, pos, repeat, event) {
-    var name = "Click";
-    if (repeat == "double") { name = "Double" + name; }
-    else if (repeat == "triple") { name = "Triple" + name; }
-    name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name;
-
-    return dispatchKey(cm,  addModifierNames(name, event), event, function (bound) {
-      if (typeof bound == "string") { bound = commands[bound]; }
-      if (!bound) { return false }
-      var done = false;
-      try {
-        if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
-        done = bound(cm, pos) != Pass;
-      } finally {
-        cm.state.suppressEdits = false;
-      }
-      return done
-    })
-  }
-
-  function configureMouse(cm, repeat, event) {
-    var option = cm.getOption("configureMouse");
-    var value = option ? option(cm, repeat, event) : {};
-    if (value.unit == null) {
-      var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;
-      value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line";
-    }
-    if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }
-    if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }
-    if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }
-    return value
-  }
-
-  function leftButtonDown(cm, pos, repeat, event) {
-    if (ie) { setTimeout(bind(ensureFocus, cm), 0); }
-    else { cm.curOp.focus = activeElt(); }
-
-    var behavior = configureMouse(cm, repeat, event);
-
-    var sel = cm.doc.sel, contained;
-    if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
-        repeat == "single" && (contained = sel.contains(pos)) > -1 &&
-        (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
-        (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
-      { leftButtonStartDrag(cm, event, pos, behavior); }
-    else
-      { leftButtonSelect(cm, event, pos, behavior); }
-  }
-
-  // Start a text drag. When it ends, see if any dragging actually
-  // happen, and treat as a click if it didn't.
-  function leftButtonStartDrag(cm, event, pos, behavior) {
-    var display = cm.display, moved = false;
-    var dragEnd = operation(cm, function (e) {
-      if (webkit) { display.scroller.draggable = false; }
-      cm.state.draggingText = false;
-      off(display.wrapper.ownerDocument, "mouseup", dragEnd);
-      off(display.wrapper.ownerDocument, "mousemove", mouseMove);
-      off(display.scroller, "dragstart", dragStart);
-      off(display.scroller, "drop", dragEnd);
-      if (!moved) {
-        e_preventDefault(e);
-        if (!behavior.addNew)
-          { extendSelection(cm.doc, pos, null, null, behavior.extend); }
-        // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
-        if ((webkit && !safari) || ie && ie_version == 9)
-          { setTimeout(function () {display.wrapper.ownerDocument.body.focus({preventScroll: true}); display.input.focus();}, 20); }
-        else
-          { display.input.focus(); }
-      }
-    });
-    var mouseMove = function(e2) {
-      moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;
-    };
-    var dragStart = function () { return moved = true; };
-    // Let the drag handler handle this.
-    if (webkit) { display.scroller.draggable = true; }
-    cm.state.draggingText = dragEnd;
-    dragEnd.copy = !behavior.moveOnDrag;
-    // IE's approach to draggable
-    if (display.scroller.dragDrop) { display.scroller.dragDrop(); }
-    on(display.wrapper.ownerDocument, "mouseup", dragEnd);
-    on(display.wrapper.ownerDocument, "mousemove", mouseMove);
-    on(display.scroller, "dragstart", dragStart);
-    on(display.scroller, "drop", dragEnd);
-
-    delayBlurEvent(cm);
-    setTimeout(function () { return display.input.focus(); }, 20);
-  }
-
-  function rangeForUnit(cm, pos, unit) {
-    if (unit == "char") { return new Range(pos, pos) }
-    if (unit == "word") { return cm.findWordAt(pos) }
-    if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
-    var result = unit(cm, pos);
-    return new Range(result.from, result.to)
-  }
-
-  // Normal selection, as opposed to text dragging.
-  function leftButtonSelect(cm, event, start, behavior) {
-    var display = cm.display, doc = cm.doc;
-    e_preventDefault(event);
-
-    var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
-    if (behavior.addNew && !behavior.extend) {
-      ourIndex = doc.sel.contains(start);
-      if (ourIndex > -1)
-        { ourRange = ranges[ourIndex]; }
-      else
-        { ourRange = new Range(start, start); }
-    } else {
-      ourRange = doc.sel.primary();
-      ourIndex = doc.sel.primIndex;
-    }
-
-    if (behavior.unit == "rectangle") {
-      if (!behavior.addNew) { ourRange = new Range(start, start); }
-      start = posFromMouse(cm, event, true, true);
-      ourIndex = -1;
-    } else {
-      var range = rangeForUnit(cm, start, behavior.unit);
-      if (behavior.extend)
-        { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend); }
-      else
-        { ourRange = range; }
-    }
-
-    if (!behavior.addNew) {
-      ourIndex = 0;
-      setSelection(doc, new Selection([ourRange], 0), sel_mouse);
-      startSel = doc.sel;
-    } else if (ourIndex == -1) {
-      ourIndex = ranges.length;
-      setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex),
-                   {scroll: false, origin: "*mouse"});
-    } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
-      setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
-                   {scroll: false, origin: "*mouse"});
-      startSel = doc.sel;
-    } else {
-      replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
-    }
-
-    var lastPos = start;
-    function extendTo(pos) {
-      if (cmp(lastPos, pos) == 0) { return }
-      lastPos = pos;
-
-      if (behavior.unit == "rectangle") {
-        var ranges = [], tabSize = cm.options.tabSize;
-        var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
-        var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
-        var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
-        for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
-             line <= end; line++) {
-          var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
-          if (left == right)
-            { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }
-          else if (text.length > leftPos)
-            { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }
-        }
-        if (!ranges.length) { ranges.push(new Range(start, start)); }
-        setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
-                     {origin: "*mouse", scroll: false});
-        cm.scrollIntoView(pos);
-      } else {
-        var oldRange = ourRange;
-        var range = rangeForUnit(cm, pos, behavior.unit);
-        var anchor = oldRange.anchor, head;
-        if (cmp(range.anchor, anchor) > 0) {
-          head = range.head;
-          anchor = minPos(oldRange.from(), range.anchor);
-        } else {
-          head = range.anchor;
-          anchor = maxPos(oldRange.to(), range.head);
-        }
-        var ranges$1 = startSel.ranges.slice(0);
-        ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head));
-        setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse);
-      }
-    }
-
-    var editorSize = display.wrapper.getBoundingClientRect();
-    // Used to ensure timeout re-tries don't fire when another extend
-    // happened in the meantime (clearTimeout isn't reliable -- at
-    // least on Chrome, the timeouts still happen even when cleared,
-    // if the clear happens after their scheduled firing time).
-    var counter = 0;
-
-    function extend(e) {
-      var curCount = ++counter;
-      var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle");
-      if (!cur) { return }
-      if (cmp(cur, lastPos) != 0) {
-        cm.curOp.focus = activeElt();
-        extendTo(cur);
-        var visible = visibleLines(display, doc);
-        if (cur.line >= visible.to || cur.line < visible.from)
-          { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }
-      } else {
-        var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
-        if (outside) { setTimeout(operation(cm, function () {
-          if (counter != curCount) { return }
-          display.scroller.scrollTop += outside;
-          extend(e);
-        }), 50); }
-      }
-    }
-
-    function done(e) {
-      cm.state.selectingText = false;
-      counter = Infinity;
-      // If e is null or undefined we interpret this as someone trying
-      // to explicitly cancel the selection rather than the user
-      // letting go of the mouse button.
-      if (e) {
-        e_preventDefault(e);
-        display.input.focus();
-      }
-      off(display.wrapper.ownerDocument, "mousemove", move);
-      off(display.wrapper.ownerDocument, "mouseup", up);
-      doc.history.lastSelOrigin = null;
-    }
-
-    var move = operation(cm, function (e) {
-      if (e.buttons === 0 || !e_button(e)) { done(e); }
-      else { extend(e); }
-    });
-    var up = operation(cm, done);
-    cm.state.selectingText = up;
-    on(display.wrapper.ownerDocument, "mousemove", move);
-    on(display.wrapper.ownerDocument, "mouseup", up);
-  }
-
-  // Used when mouse-selecting to adjust the anchor to the proper side
-  // of a bidi jump depending on the visual position of the head.
-  function bidiSimplify(cm, range) {
-    var anchor = range.anchor;
-    var head = range.head;
-    var anchorLine = getLine(cm.doc, anchor.line);
-    if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range }
-    var order = getOrder(anchorLine);
-    if (!order) { return range }
-    var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index];
-    if (part.from != anchor.ch && part.to != anchor.ch) { return range }
-    var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1);
-    if (boundary == 0 || boundary == order.length) { return range }
-
-    // Compute the relative visual position of the head compared to the
-    // anchor (<0 is to the left, >0 to the right)
-    var leftSide;
-    if (head.line != anchor.line) {
-      leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0;
-    } else {
-      var headIndex = getBidiPartAt(order, head.ch, head.sticky);
-      var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1);
-      if (headIndex == boundary - 1 || headIndex == boundary)
-        { leftSide = dir < 0; }
-      else
-        { leftSide = dir > 0; }
-    }
-
-    var usePart = order[boundary + (leftSide ? -1 : 0)];
-    var from = leftSide == (usePart.level == 1);
-    var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before";
-    return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head)
-  }
-
-
-  // Determines whether an event happened in the gutter, and fires the
-  // handlers for the corresponding event.
-  function gutterEvent(cm, e, type, prevent) {
-    var mX, mY;
-    if (e.touches) {
-      mX = e.touches[0].clientX;
-      mY = e.touches[0].clientY;
-    } else {
-      try { mX = e.clientX; mY = e.clientY; }
-      catch(e$1) { return false }
-    }
-    if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
-    if (prevent) { e_preventDefault(e); }
-
-    var display = cm.display;
-    var lineBox = display.lineDiv.getBoundingClientRect();
-
-    if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
-    mY -= lineBox.top - display.viewOffset;
-
-    for (var i = 0; i < cm.display.gutterSpecs.length; ++i) {
-      var g = display.gutters.childNodes[i];
-      if (g && g.getBoundingClientRect().right >= mX) {
-        var line = lineAtHeight(cm.doc, mY);
-        var gutter = cm.display.gutterSpecs[i];
-        signal(cm, type, cm, line, gutter.className, e);
-        return e_defaultPrevented(e)
-      }
-    }
-  }
-
-  function clickInGutter(cm, e) {
-    return gutterEvent(cm, e, "gutterClick", true)
-  }
-
-  // CONTEXT MENU HANDLING
-
-  // To make the context menu work, we need to briefly unhide the
-  // textarea (making it as unobtrusive as possible) to let the
-  // right-click take effect on it.
-  function onContextMenu(cm, e) {
-    if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
-    if (signalDOMEvent(cm, e, "contextmenu")) { return }
-    if (!captureRightClick) { cm.display.input.onContextMenu(e); }
-  }
-
-  function contextMenuInGutter(cm, e) {
-    if (!hasHandler(cm, "gutterContextMenu")) { return false }
-    return gutterEvent(cm, e, "gutterContextMenu", false)
-  }
-
-  function themeChanged(cm) {
-    cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
-      cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
-    clearCaches(cm);
-  }
-
-  var Init = {toString: function(){return "CodeMirror.Init"}};
-
-  var defaults = {};
-  var optionHandlers = {};
-
-  function defineOptions(CodeMirror) {
-    var optionHandlers = CodeMirror.optionHandlers;
-
-    function option(name, deflt, handle, notOnInit) {
-      CodeMirror.defaults[name] = deflt;
-      if (handle) { optionHandlers[name] =
-        notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }
-    }
-
-    CodeMirror.defineOption = option;
-
-    // Passed to option handlers when there is no old value.
-    CodeMirror.Init = Init;
-
-    // These two are, on init, called from the constructor because they
-    // have to be initialized before the editor can start at all.
-    option("value", "", function (cm, val) { return cm.setValue(val); }, true);
-    option("mode", null, function (cm, val) {
-      cm.doc.modeOption = val;
-      loadMode(cm);
-    }, true);
-
-    option("indentUnit", 2, loadMode, true);
-    option("indentWithTabs", false);
-    option("smartIndent", true);
-    option("tabSize", 4, function (cm) {
-      resetModeState(cm);
-      clearCaches(cm);
-      regChange(cm);
-    }, true);
-
-    option("lineSeparator", null, function (cm, val) {
-      cm.doc.lineSep = val;
-      if (!val) { return }
-      var newBreaks = [], lineNo = cm.doc.first;
-      cm.doc.iter(function (line) {
-        for (var pos = 0;;) {
-          var found = line.text.indexOf(val, pos);
-          if (found == -1) { break }
-          pos = found + val.length;
-          newBreaks.push(Pos(lineNo, found));
-        }
-        lineNo++;
-      });
-      for (var i = newBreaks.length - 1; i >= 0; i--)
-        { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }
-    });
-    option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200c\u200e\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, function (cm, val, old) {
-      cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
-      if (old != Init) { cm.refresh(); }
-    });
-    option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);
-    option("electricChars", true);
-    option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
-      throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
-    }, true);
-    option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);
-    option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true);
-    option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true);
-    option("rtlMoveVisually", !windows);
-    option("wholeLineUpdateBefore", true);
-
-    option("theme", "default", function (cm) {
-      themeChanged(cm);
-      updateGutters(cm);
-    }, true);
-    option("keyMap", "default", function (cm, val, old) {
-      var next = getKeyMap(val);
-      var prev = old != Init && getKeyMap(old);
-      if (prev && prev.detach) { prev.detach(cm, next); }
-      if (next.attach) { next.attach(cm, prev || null); }
-    });
-    option("extraKeys", null);
-    option("configureMouse", null);
-
-    option("lineWrapping", false, wrappingChanged, true);
-    option("gutters", [], function (cm, val) {
-      cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers);
-      updateGutters(cm);
-    }, true);
-    option("fixedGutter", true, function (cm, val) {
-      cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
-      cm.refresh();
-    }, true);
-    option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true);
-    option("scrollbarStyle", "native", function (cm) {
-      initScrollbars(cm);
-      updateScrollbars(cm);
-      cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
-      cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
-    }, true);
-    option("lineNumbers", false, function (cm, val) {
-      cm.display.gutterSpecs = getGutters(cm.options.gutters, val);
-      updateGutters(cm);
-    }, true);
-    option("firstLineNumber", 1, updateGutters, true);
-    option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true);
-    option("showCursorWhenSelecting", false, updateSelection, true);
-
-    option("resetSelectionOnContextMenu", true);
-    option("lineWiseCopyCut", true);
-    option("pasteLinesPerSelection", true);
-    option("selectionsMayTouch", false);
-
-    option("readOnly", false, function (cm, val) {
-      if (val == "nocursor") {
-        onBlur(cm);
-        cm.display.input.blur();
-      }
-      cm.display.input.readOnlyChanged(val);
-    });
-
-    option("screenReaderLabel", null, function (cm, val) {
-      val = (val === '') ? null : val;
-      cm.display.input.screenReaderLabelChanged(val);
-    });
-
-    option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);
-    option("dragDrop", true, dragDropChanged);
-    option("allowDropFileTypes", null);
-
-    option("cursorBlinkRate", 530);
-    option("cursorScrollMargin", 0);
-    option("cursorHeight", 1, updateSelection, true);
-    option("singleCursorHeightPerLine", true, updateSelection, true);
-    option("workTime", 100);
-    option("workDelay", 100);
-    option("flattenSpans", true, resetModeState, true);
-    option("addModeClass", false, resetModeState, true);
-    option("pollInterval", 100);
-    option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });
-    option("historyEventDelay", 1250);
-    option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true);
-    option("maxHighlightLength", 10000, resetModeState, true);
-    option("moveInputWithCursor", true, function (cm, val) {
-      if (!val) { cm.display.input.resetPosition(); }
-    });
-
-    option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; });
-    option("autofocus", null);
-    option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true);
-    option("phrases", null);
-  }
-
-  function dragDropChanged(cm, value, old) {
-    var wasOn = old && old != Init;
-    if (!value != !wasOn) {
-      var funcs = cm.display.dragFunctions;
-      var toggle = value ? on : off;
-      toggle(cm.display.scroller, "dragstart", funcs.start);
-      toggle(cm.display.scroller, "dragenter", funcs.enter);
-      toggle(cm.display.scroller, "dragover", funcs.over);
-      toggle(cm.display.scroller, "dragleave", funcs.leave);
-      toggle(cm.display.scroller, "drop", funcs.drop);
-    }
-  }
-
-  function wrappingChanged(cm) {
-    if (cm.options.lineWrapping) {
-      addClass(cm.display.wrapper, "CodeMirror-wrap");
-      cm.display.sizer.style.minWidth = "";
-      cm.display.sizerWidth = null;
-    } else {
-      rmClass(cm.display.wrapper, "CodeMirror-wrap");
-      findMaxLine(cm);
-    }
-    estimateLineHeights(cm);
-    regChange(cm);
-    clearCaches(cm);
-    setTimeout(function () { return updateScrollbars(cm); }, 100);
-  }
-
-  // A CodeMirror instance represents an editor. This is the object
-  // that user code is usually dealing with.
-
-  function CodeMirror(place, options) {
-    var this$1 = this;
-
-    if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }
-
-    this.options = options = options ? copyObj(options) : {};
-    // Determine effective options based on given values and defaults.
-    copyObj(defaults, options, false);
-
-    var doc = options.value;
-    if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }
-    else if (options.mode) { doc.modeOption = options.mode; }
-    this.doc = doc;
-
-    var input = new CodeMirror.inputStyles[options.inputStyle](this);
-    var display = this.display = new Display(place, doc, input, options);
-    display.wrapper.CodeMirror = this;
-    themeChanged(this);
-    if (options.lineWrapping)
-      { this.display.wrapper.className += " CodeMirror-wrap"; }
-    initScrollbars(this);
-
-    this.state = {
-      keyMaps: [],  // stores maps added by addKeyMap
-      overlays: [], // highlighting overlays, as added by addOverlay
-      modeGen: 0,   // bumped when mode/overlay changes, used to invalidate highlighting info
-      overwrite: false,
-      delayingBlurEvent: false,
-      focused: false,
-      suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
-      pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll
-      selectingText: false,
-      draggingText: false,
-      highlight: new Delayed(), // stores highlight worker timeout
-      keySeq: null,  // Unfinished key sequence
-      specialChars: null
-    };
-
-    if (options.autofocus && !mobile) { display.input.focus(); }
-
-    // Override magic textarea content restore that IE sometimes does
-    // on our hidden textarea on reload
-    if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }
-
-    registerEventHandlers(this);
-    ensureGlobalHandlers();
-
-    startOperation(this);
-    this.curOp.forceUpdate = true;
-    attachDoc(this, doc);
-
-    if ((options.autofocus && !mobile) || this.hasFocus())
-      { setTimeout(bind(onFocus, this), 20); }
-    else
-      { onBlur(this); }
-
-    for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
-      { optionHandlers[opt](this, options[opt], Init); } }
-    maybeUpdateLineNumberWidth(this);
-    if (options.finishInit) { options.finishInit(this); }
-    for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this); }
-    endOperation(this);
-    // Suppress optimizelegibility in Webkit, since it breaks text
-    // measuring on line wrapping boundaries.
-    if (webkit && options.lineWrapping &&
-        getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
-      { display.lineDiv.style.textRendering = "auto"; }
-  }
-
-  // The default configuration options.
-  CodeMirror.defaults = defaults;
-  // Functions to run when options are changed.
-  CodeMirror.optionHandlers = optionHandlers;
-
-  // Attach the necessary event handlers when initializing the editor
-  function registerEventHandlers(cm) {
-    var d = cm.display;
-    on(d.scroller, "mousedown", operation(cm, onMouseDown));
-    // Older IE's will not fire a second mousedown for a double click
-    if (ie && ie_version < 11)
-      { on(d.scroller, "dblclick", operation(cm, function (e) {
-        if (signalDOMEvent(cm, e)) { return }
-        var pos = posFromMouse(cm, e);
-        if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
-        e_preventDefault(e);
-        var word = cm.findWordAt(pos);
-        extendSelection(cm.doc, word.anchor, word.head);
-      })); }
-    else
-      { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }
-    // Some browsers fire contextmenu *after* opening the menu, at
-    // which point we can't mess with it anymore. Context menu is
-    // handled in onMouseDown for these browsers.
-    on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); });
-    on(d.input.getField(), "contextmenu", function (e) {
-      if (!d.scroller.contains(e.target)) { onContextMenu(cm, e); }
-    });
-
-    // Used to suppress mouse event handling when a touch happens
-    var touchFinished, prevTouch = {end: 0};
-    function finishTouch() {
-      if (d.activeTouch) {
-        touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);
-        prevTouch = d.activeTouch;
-        prevTouch.end = +new Date;
-      }
-    }
-    function isMouseLikeTouchEvent(e) {
-      if (e.touches.length != 1) { return false }
-      var touch = e.touches[0];
-      return touch.radiusX <= 1 && touch.radiusY <= 1
-    }
-    function farAway(touch, other) {
-      if (other.left == null) { return true }
-      var dx = other.left - touch.left, dy = other.top - touch.top;
-      return dx * dx + dy * dy > 20 * 20
-    }
-    on(d.scroller, "touchstart", function (e) {
-      if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
-        d.input.ensurePolled();
-        clearTimeout(touchFinished);
-        var now = +new Date;
-        d.activeTouch = {start: now, moved: false,
-                         prev: now - prevTouch.end <= 300 ? prevTouch : null};
-        if (e.touches.length == 1) {
-          d.activeTouch.left = e.touches[0].pageX;
-          d.activeTouch.top = e.touches[0].pageY;
-        }
-      }
-    });
-    on(d.scroller, "touchmove", function () {
-      if (d.activeTouch) { d.activeTouch.moved = true; }
-    });
-    on(d.scroller, "touchend", function (e) {
-      var touch = d.activeTouch;
-      if (touch && !eventInWidget(d, e) && touch.left != null &&
-          !touch.moved && new Date - touch.start < 300) {
-        var pos = cm.coordsChar(d.activeTouch, "page"), range;
-        if (!touch.prev || farAway(touch, touch.prev)) // Single tap
-          { range = new Range(pos, pos); }
-        else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
-          { range = cm.findWordAt(pos); }
-        else // Triple tap
-          { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }
-        cm.setSelection(range.anchor, range.head);
-        cm.focus();
-        e_preventDefault(e);
-      }
-      finishTouch();
-    });
-    on(d.scroller, "touchcancel", finishTouch);
-
-    // Sync scrolling between fake scrollbars and real scrollable
-    // area, ensure viewport is updated when scrolling.
-    on(d.scroller, "scroll", function () {
-      if (d.scroller.clientHeight) {
-        updateScrollTop(cm, d.scroller.scrollTop);
-        setScrollLeft(cm, d.scroller.scrollLeft, true);
-        signal(cm, "scroll", cm);
-      }
-    });
-
-    // Listen to wheel events in order to try and update the viewport on time.
-    on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); });
-    on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); });
-
-    // Prevent wrapper from ever scrolling
-    on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
-
-    d.dragFunctions = {
-      enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},
-      over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
-      start: function (e) { return onDragStart(cm, e); },
-      drop: operation(cm, onDrop),
-      leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
-    };
-
-    var inp = d.input.getField();
-    on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); });
-    on(inp, "keydown", operation(cm, onKeyDown));
-    on(inp, "keypress", operation(cm, onKeyPress));
-    on(inp, "focus", function (e) { return onFocus(cm, e); });
-    on(inp, "blur", function (e) { return onBlur(cm, e); });
-  }
-
-  var initHooks = [];
-  CodeMirror.defineInitHook = function (f) { return initHooks.push(f); };
-
-  // Indent the given line. The how parameter can be "smart",
-  // "add"/null, "subtract", or "prev". When aggressive is false
-  // (typically set to true for forced single-line indents), empty
-  // lines are not indented, and places where the mode returns Pass
-  // are left alone.
-  function indentLine(cm, n, how, aggressive) {
-    var doc = cm.doc, state;
-    if (how == null) { how = "add"; }
-    if (how == "smart") {
-      // Fall back to "prev" when the mode doesn't have an indentation
-      // method.
-      if (!doc.mode.indent) { how = "prev"; }
-      else { state = getContextBefore(cm, n).state; }
-    }
-
-    var tabSize = cm.options.tabSize;
-    var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
-    if (line.stateAfter) { line.stateAfter = null; }
-    var curSpaceString = line.text.match(/^\s*/)[0], indentation;
-    if (!aggressive && !/\S/.test(line.text)) {
-      indentation = 0;
-      how = "not";
-    } else if (how == "smart") {
-      indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
-      if (indentation == Pass || indentation > 150) {
-        if (!aggressive) { return }
-        how = "prev";
-      }
-    }
-    if (how == "prev") {
-      if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }
-      else { indentation = 0; }
-    } else if (how == "add") {
-      indentation = curSpace + cm.options.indentUnit;
-    } else if (how == "subtract") {
-      indentation = curSpace - cm.options.indentUnit;
-    } else if (typeof how == "number") {
-      indentation = curSpace + how;
-    }
-    indentation = Math.max(0, indentation);
-
-    var indentString = "", pos = 0;
-    if (cm.options.indentWithTabs)
-      { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} }
-    if (pos < indentation) { indentString += spaceStr(indentation - pos); }
-
-    if (indentString != curSpaceString) {
-      replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
-      line.stateAfter = null;
-      return true
-    } else {
-      // Ensure that, if the cursor was in the whitespace at the start
-      // of the line, it is moved to the end of that space.
-      for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
-        var range = doc.sel.ranges[i$1];
-        if (range.head.line == n && range.head.ch < curSpaceString.length) {
-          var pos$1 = Pos(n, curSpaceString.length);
-          replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));
-          break
-        }
-      }
-    }
-  }
-
-  // This will be set to a {lineWise: bool, text: [string]} object, so
-  // that, when pasting, we know what kind of selections the copied
-  // text was made out of.
-  var lastCopied = null;
-
-  function setLastCopied(newLastCopied) {
-    lastCopied = newLastCopied;
-  }
-
-  function applyTextInput(cm, inserted, deleted, sel, origin) {
-    var doc = cm.doc;
-    cm.display.shift = false;
-    if (!sel) { sel = doc.sel; }
-
-    var recent = +new Date - 200;
-    var paste = origin == "paste" || cm.state.pasteIncoming > recent;
-    var textLines = splitLinesAuto(inserted), multiPaste = null;
-    // When pasting N lines into N selections, insert one line per selection
-    if (paste && sel.ranges.length > 1) {
-      if (lastCopied && lastCopied.text.join("\n") == inserted) {
-        if (sel.ranges.length % lastCopied.text.length == 0) {
-          multiPaste = [];
-          for (var i = 0; i < lastCopied.text.length; i++)
-            { multiPaste.push(doc.splitLines(lastCopied.text[i])); }
-        }
-      } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
-        multiPaste = map(textLines, function (l) { return [l]; });
-      }
-    }
-
-    var updateInput = cm.curOp.updateInput;
-    // Normal behavior is to insert the new text into every selection
-    for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
-      var range = sel.ranges[i$1];
-      var from = range.from(), to = range.to();
-      if (range.empty()) {
-        if (deleted && deleted > 0) // Handle deletion
-          { from = Pos(from.line, from.ch - deleted); }
-        else if (cm.state.overwrite && !paste) // Handle overwrite
-          { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }
-        else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == textLines.join("\n"))
-          { from = to = Pos(from.line, 0); }
-      }
-      var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
-                         origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")};
-      makeChange(cm.doc, changeEvent);
-      signalLater(cm, "inputRead", cm, changeEvent);
-    }
-    if (inserted && !paste)
-      { triggerElectric(cm, inserted); }
-
-    ensureCursorVisible(cm);
-    if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; }
-    cm.curOp.typing = true;
-    cm.state.pasteIncoming = cm.state.cutIncoming = -1;
-  }
-
-  function handlePaste(e, cm) {
-    var pasted = e.clipboardData && e.clipboardData.getData("Text");
-    if (pasted) {
-      e.preventDefault();
-      if (!cm.isReadOnly() && !cm.options.disableInput)
-        { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); }
-      return true
-    }
-  }
-
-  function triggerElectric(cm, inserted) {
-    // When an 'electric' character is inserted, immediately trigger a reindent
-    if (!cm.options.electricChars || !cm.options.smartIndent) { return }
-    var sel = cm.doc.sel;
-
-    for (var i = sel.ranges.length - 1; i >= 0; i--) {
-      var range = sel.ranges[i];
-      if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue }
-      var mode = cm.getModeAt(range.head);
-      var indented = false;
-      if (mode.electricChars) {
-        for (var j = 0; j < mode.electricChars.length; j++)
-          { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
-            indented = indentLine(cm, range.head.line, "smart");
-            break
-          } }
-      } else if (mode.electricInput) {
-        if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))
-          { indented = indentLine(cm, range.head.line, "smart"); }
-      }
-      if (indented) { signalLater(cm, "electricInput", cm, range.head.line); }
-    }
-  }
-
-  function copyableRanges(cm) {
-    var text = [], ranges = [];
-    for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
-      var line = cm.doc.sel.ranges[i].head.line;
-      var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
-      ranges.push(lineRange);
-      text.push(cm.getRange(lineRange.anchor, lineRange.head));
-    }
-    return {text: text, ranges: ranges}
-  }
-
-  function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) {
-    field.setAttribute("autocorrect", autocorrect ? "" : "off");
-    field.setAttribute("autocapitalize", autocapitalize ? "" : "off");
-    field.setAttribute("spellcheck", !!spellcheck);
-  }
-
-  function hiddenTextarea() {
-    var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none");
-    var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
-    // The textarea is kept positioned near the cursor to prevent the
-    // fact that it'll be scrolled into view on input from scrolling
-    // our fake cursor out of view. On webkit, when wrap=off, paste is
-    // very slow. So make the area wide instead.
-    if (webkit) { te.style.width = "1000px"; }
-    else { te.setAttribute("wrap", "off"); }
-    // If border: 0; -- iOS fails to open keyboard (issue #1287)
-    if (ios) { te.style.border = "1px solid black"; }
-    disableBrowserMagic(te);
-    return div
-  }
-
-  // The publicly visible API. Note that methodOp(f) means
-  // 'wrap f in an operation, performed on its `this` parameter'.
-
-  // This is not the complete set of editor methods. Most of the
-  // methods defined on the Doc type are also injected into
-  // CodeMirror.prototype, for backwards compatibility and
-  // convenience.
-
-  function addEditorMethods(CodeMirror) {
-    var optionHandlers = CodeMirror.optionHandlers;
-
-    var helpers = CodeMirror.helpers = {};
-
-    CodeMirror.prototype = {
-      constructor: CodeMirror,
-      focus: function(){window.focus(); this.display.input.focus();},
-
-      setOption: function(option, value) {
-        var options = this.options, old = options[option];
-        if (options[option] == value && option != "mode") { return }
-        options[option] = value;
-        if (optionHandlers.hasOwnProperty(option))
-          { operation(this, optionHandlers[option])(this, value, old); }
-        signal(this, "optionChange", this, option);
-      },
-
-      getOption: function(option) {return this.options[option]},
-      getDoc: function() {return this.doc},
-
-      addKeyMap: function(map, bottom) {
-        this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map));
-      },
-      removeKeyMap: function(map) {
-        var maps = this.state.keyMaps;
-        for (var i = 0; i < maps.length; ++i)
-          { if (maps[i] == map || maps[i].name == map) {
-            maps.splice(i, 1);
-            return true
-          } }
-      },
-
-      addOverlay: methodOp(function(spec, options) {
-        var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
-        if (mode.startState) { throw new Error("Overlays may not be stateful.") }
-        insertSorted(this.state.overlays,
-                     {mode: mode, modeSpec: spec, opaque: options && options.opaque,
-                      priority: (options && options.priority) || 0},
-                     function (overlay) { return overlay.priority; });
-        this.state.modeGen++;
-        regChange(this);
-      }),
-      removeOverlay: methodOp(function(spec) {
-        var overlays = this.state.overlays;
-        for (var i = 0; i < overlays.length; ++i) {
-          var cur = overlays[i].modeSpec;
-          if (cur == spec || typeof spec == "string" && cur.name == spec) {
-            overlays.splice(i, 1);
-            this.state.modeGen++;
-            regChange(this);
-            return
-          }
-        }
-      }),
-
-      indentLine: methodOp(function(n, dir, aggressive) {
-        if (typeof dir != "string" && typeof dir != "number") {
-          if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; }
-          else { dir = dir ? "add" : "subtract"; }
-        }
-        if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }
-      }),
-      indentSelection: methodOp(function(how) {
-        var ranges = this.doc.sel.ranges, end = -1;
-        for (var i = 0; i < ranges.length; i++) {
-          var range = ranges[i];
-          if (!range.empty()) {
-            var from = range.from(), to = range.to();
-            var start = Math.max(end, from.line);
-            end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
-            for (var j = start; j < end; ++j)
-              { indentLine(this, j, how); }
-            var newRanges = this.doc.sel.ranges;
-            if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
-              { replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }
-          } else if (range.head.line > end) {
-            indentLine(this, range.head.line, how, true);
-            end = range.head.line;
-            if (i == this.doc.sel.primIndex) { ensureCursorVisible(this); }
-          }
-        }
-      }),
-
-      // Fetch the parser token for a given character. Useful for hacks
-      // that want to inspect the mode state (say, for completion).
-      getTokenAt: function(pos, precise) {
-        return takeToken(this, pos, precise)
-      },
-
-      getLineTokens: function(line, precise) {
-        return takeToken(this, Pos(line), precise, true)
-      },
-
-      getTokenTypeAt: function(pos) {
-        pos = clipPos(this.doc, pos);
-        var styles = getLineStyles(this, getLine(this.doc, pos.line));
-        var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
-        var type;
-        if (ch == 0) { type = styles[2]; }
-        else { for (;;) {
-          var mid = (before + after) >> 1;
-          if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }
-          else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }
-          else { type = styles[mid * 2 + 2]; break }
-        } }
-        var cut = type ? type.indexOf("overlay ") : -1;
-        return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
-      },
-
-      getModeAt: function(pos) {
-        var mode = this.doc.mode;
-        if (!mode.innerMode) { return mode }
-        return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
-      },
-
-      getHelper: function(pos, type) {
-        return this.getHelpers(pos, type)[0]
-      },
-
-      getHelpers: function(pos, type) {
-        var found = [];
-        if (!helpers.hasOwnProperty(type)) { return found }
-        var help = helpers[type], mode = this.getModeAt(pos);
-        if (typeof mode[type] == "string") {
-          if (help[mode[type]]) { found.push(help[mode[type]]); }
-        } else if (mode[type]) {
-          for (var i = 0; i < mode[type].length; i++) {
-            var val = help[mode[type][i]];
-            if (val) { found.push(val); }
-          }
-        } else if (mode.helperType && help[mode.helperType]) {
-          found.push(help[mode.helperType]);
-        } else if (help[mode.name]) {
-          found.push(help[mode.name]);
-        }
-        for (var i$1 = 0; i$1 < help._global.length; i$1++) {
-          var cur = help._global[i$1];
-          if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)
-            { found.push(cur.val); }
-        }
-        return found
-      },
-
-      getStateAfter: function(line, precise) {
-        var doc = this.doc;
-        line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
-        return getContextBefore(this, line + 1, precise).state
-      },
-
-      cursorCoords: function(start, mode) {
-        var pos, range = this.doc.sel.primary();
-        if (start == null) { pos = range.head; }
-        else if (typeof start == "object") { pos = clipPos(this.doc, start); }
-        else { pos = start ? range.from() : range.to(); }
-        return cursorCoords(this, pos, mode || "page")
-      },
-
-      charCoords: function(pos, mode) {
-        return charCoords(this, clipPos(this.doc, pos), mode || "page")
-      },
-
-      coordsChar: function(coords, mode) {
-        coords = fromCoordSystem(this, coords, mode || "page");
-        return coordsChar(this, coords.left, coords.top)
-      },
-
-      lineAtHeight: function(height, mode) {
-        height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
-        return lineAtHeight(this.doc, height + this.display.viewOffset)
-      },
-      heightAtLine: function(line, mode, includeWidgets) {
-        var end = false, lineObj;
-        if (typeof line == "number") {
-          var last = this.doc.first + this.doc.size - 1;
-          if (line < this.doc.first) { line = this.doc.first; }
-          else if (line > last) { line = last; end = true; }
-          lineObj = getLine(this.doc, line);
-        } else {
-          lineObj = line;
-        }
-        return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
-          (end ? this.doc.height - heightAtLine(lineObj) : 0)
-      },
-
-      defaultTextHeight: function() { return textHeight(this.display) },
-      defaultCharWidth: function() { return charWidth(this.display) },
-
-      getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
-
-      addWidget: function(pos, node, scroll, vert, horiz) {
-        var display = this.display;
-        pos = cursorCoords(this, clipPos(this.doc, pos));
-        var top = pos.bottom, left = pos.left;
-        node.style.position = "absolute";
-        node.setAttribute("cm-ignore-events", "true");
-        this.display.input.setUneditable(node);
-        display.sizer.appendChild(node);
-        if (vert == "over") {
-          top = pos.top;
-        } else if (vert == "above" || vert == "near") {
-          var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
-          hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
-          // Default to positioning above (if specified and possible); otherwise default to positioning below
-          if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
-            { top = pos.top - node.offsetHeight; }
-          else if (pos.bottom + node.offsetHeight <= vspace)
-            { top = pos.bottom; }
-          if (left + node.offsetWidth > hspace)
-            { left = hspace - node.offsetWidth; }
-        }
-        node.style.top = top + "px";
-        node.style.left = node.style.right = "";
-        if (horiz == "right") {
-          left = display.sizer.clientWidth - node.offsetWidth;
-          node.style.right = "0px";
-        } else {
-          if (horiz == "left") { left = 0; }
-          else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }
-          node.style.left = left + "px";
-        }
-        if (scroll)
-          { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }
-      },
-
-      triggerOnKeyDown: methodOp(onKeyDown),
-      triggerOnKeyPress: methodOp(onKeyPress),
-      triggerOnKeyUp: onKeyUp,
-      triggerOnMouseDown: methodOp(onMouseDown),
-
-      execCommand: function(cmd) {
-        if (commands.hasOwnProperty(cmd))
-          { return commands[cmd].call(null, this) }
-      },
-
-      triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
-
-      findPosH: function(from, amount, unit, visually) {
-        var dir = 1;
-        if (amount < 0) { dir = -1; amount = -amount; }
-        var cur = clipPos(this.doc, from);
-        for (var i = 0; i < amount; ++i) {
-          cur = findPosH(this.doc, cur, dir, unit, visually);
-          if (cur.hitSide) { break }
-        }
-        return cur
-      },
-
-      moveH: methodOp(function(dir, unit) {
-        var this$1 = this;
-
-        this.extendSelectionsBy(function (range) {
-          if (this$1.display.shift || this$1.doc.extend || range.empty())
-            { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) }
-          else
-            { return dir < 0 ? range.from() : range.to() }
-        }, sel_move);
-      }),
-
-      deleteH: methodOp(function(dir, unit) {
-        var sel = this.doc.sel, doc = this.doc;
-        if (sel.somethingSelected())
-          { doc.replaceSelection("", null, "+delete"); }
-        else
-          { deleteNearSelection(this, function (range) {
-            var other = findPosH(doc, range.head, dir, unit, false);
-            return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other}
-          }); }
-      }),
-
-      findPosV: function(from, amount, unit, goalColumn) {
-        var dir = 1, x = goalColumn;
-        if (amount < 0) { dir = -1; amount = -amount; }
-        var cur = clipPos(this.doc, from);
-        for (var i = 0; i < amount; ++i) {
-          var coords = cursorCoords(this, cur, "div");
-          if (x == null) { x = coords.left; }
-          else { coords.left = x; }
-          cur = findPosV(this, coords, dir, unit);
-          if (cur.hitSide) { break }
-        }
-        return cur
-      },
-
-      moveV: methodOp(function(dir, unit) {
-        var this$1 = this;
-
-        var doc = this.doc, goals = [];
-        var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();
-        doc.extendSelectionsBy(function (range) {
-          if (collapse)
-            { return dir < 0 ? range.from() : range.to() }
-          var headPos = cursorCoords(this$1, range.head, "div");
-          if (range.goalColumn != null) { headPos.left = range.goalColumn; }
-          goals.push(headPos.left);
-          var pos = findPosV(this$1, headPos, dir, unit);
-          if (unit == "page" && range == doc.sel.primary())
-            { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); }
-          return pos
-        }, sel_move);
-        if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
-          { doc.sel.ranges[i].goalColumn = goals[i]; } }
-      }),
-
-      // Find the word at the given position (as returned by coordsChar).
-      findWordAt: function(pos) {
-        var doc = this.doc, line = getLine(doc, pos.line).text;
-        var start = pos.ch, end = pos.ch;
-        if (line) {
-          var helper = this.getHelper(pos, "wordChars");
-          if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; }
-          var startChar = line.charAt(start);
-          var check = isWordChar(startChar, helper)
-            ? function (ch) { return isWordChar(ch, helper); }
-            : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
-            : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); };
-          while (start > 0 && check(line.charAt(start - 1))) { --start; }
-          while (end < line.length && check(line.charAt(end))) { ++end; }
-        }
-        return new Range(Pos(pos.line, start), Pos(pos.line, end))
-      },
-
-      toggleOverwrite: function(value) {
-        if (value != null && value == this.state.overwrite) { return }
-        if (this.state.overwrite = !this.state.overwrite)
-          { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
-        else
-          { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
-
-        signal(this, "overwriteToggle", this, this.state.overwrite);
-      },
-      hasFocus: function() { return this.display.input.getField() == activeElt() },
-      isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
-
-      scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),
-      getScrollInfo: function() {
-        var scroller = this.display.scroller;
-        return {left: scroller.scrollLeft, top: scroller.scrollTop,
-                height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
-                width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
-                clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
-      },
-
-      scrollIntoView: methodOp(function(range, margin) {
-        if (range == null) {
-          range = {from: this.doc.sel.primary().head, to: null};
-          if (margin == null) { margin = this.options.cursorScrollMargin; }
-        } else if (typeof range == "number") {
-          range = {from: Pos(range, 0), to: null};
-        } else if (range.from == null) {
-          range = {from: range, to: null};
-        }
-        if (!range.to) { range.to = range.from; }
-        range.margin = margin || 0;
-
-        if (range.from.line != null) {
-          scrollToRange(this, range);
-        } else {
-          scrollToCoordsRange(this, range.from, range.to, range.margin);
-        }
-      }),
-
-      setSize: methodOp(function(width, height) {
-        var this$1 = this;
-
-        var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; };
-        if (width != null) { this.display.wrapper.style.width = interpret(width); }
-        if (height != null) { this.display.wrapper.style.height = interpret(height); }
-        if (this.options.lineWrapping) { clearLineMeasurementCache(this); }
-        var lineNo = this.display.viewFrom;
-        this.doc.iter(lineNo, this.display.viewTo, function (line) {
-          if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
-            { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } }
-          ++lineNo;
-        });
-        this.curOp.forceUpdate = true;
-        signal(this, "refresh", this);
-      }),
-
-      operation: function(f){return runInOp(this, f)},
-      startOperation: function(){return startOperation(this)},
-      endOperation: function(){return endOperation(this)},
-
-      refresh: methodOp(function() {
-        var oldHeight = this.display.cachedTextHeight;
-        regChange(this);
-        this.curOp.forceUpdate = true;
-        clearCaches(this);
-        scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);
-        updateGutterSpace(this.display);
-        if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5 || this.options.lineWrapping)
-          { estimateLineHeights(this); }
-        signal(this, "refresh", this);
-      }),
-
-      swapDoc: methodOp(function(doc) {
-        var old = this.doc;
-        old.cm = null;
-        // Cancel the current text selection if any (#5821)
-        if (this.state.selectingText) { this.state.selectingText(); }
-        attachDoc(this, doc);
-        clearCaches(this);
-        this.display.input.reset();
-        scrollToCoords(this, doc.scrollLeft, doc.scrollTop);
-        this.curOp.forceScroll = true;
-        signalLater(this, "swapDoc", this, old);
-        return old
-      }),
-
-      phrase: function(phraseText) {
-        var phrases = this.options.phrases;
-        return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText
-      },
-
-      getInputField: function(){return this.display.input.getField()},
-      getWrapperElement: function(){return this.display.wrapper},
-      getScrollerElement: function(){return this.display.scroller},
-      getGutterElement: function(){return this.display.gutters}
-    };
-    eventMixin(CodeMirror);
-
-    CodeMirror.registerHelper = function(type, name, value) {
-      if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }
-      helpers[type][name] = value;
-    };
-    CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
-      CodeMirror.registerHelper(type, name, value);
-      helpers[type]._global.push({pred: predicate, val: value});
-    };
-  }
-
-  // Used for horizontal relative motion. Dir is -1 or 1 (left or
-  // right), unit can be "char", "column" (like char, but doesn't
-  // cross line boundaries), "word" (across next word), or "group" (to
-  // the start of next group of word or non-word-non-whitespace
-  // chars). The visually param controls whether, in right-to-left
-  // text, direction 1 means to move towards the next index in the
-  // string, or towards the character to the right of the current
-  // position. The resulting position will have a hitSide=true
-  // property if it reached the end of the document.
-  function findPosH(doc, pos, dir, unit, visually) {
-    var oldPos = pos;
-    var origDir = dir;
-    var lineObj = getLine(doc, pos.line);
-    var lineDir = visually && doc.direction == "rtl" ? -dir : dir;
-    function findNextLine() {
-      var l = pos.line + lineDir;
-      if (l < doc.first || l >= doc.first + doc.size) { return false }
-      pos = new Pos(l, pos.ch, pos.sticky);
-      return lineObj = getLine(doc, l)
-    }
-    function moveOnce(boundToLine) {
-      var next;
-      if (visually) {
-        next = moveVisually(doc.cm, lineObj, pos, dir);
-      } else {
-        next = moveLogically(lineObj, pos, dir);
-      }
-      if (next == null) {
-        if (!boundToLine && findNextLine())
-          { pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir); }
-        else
-          { return false }
-      } else {
-        pos = next;
-      }
-      return true
-    }
-
-    if (unit == "char") {
-      moveOnce();
-    } else if (unit == "column") {
-      moveOnce(true);
-    } else if (unit == "word" || unit == "group") {
-      var sawType = null, group = unit == "group";
-      var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
-      for (var first = true;; first = false) {
-        if (dir < 0 && !moveOnce(!first)) { break }
-        var cur = lineObj.text.charAt(pos.ch) || "\n";
-        var type = isWordChar(cur, helper) ? "w"
-          : group && cur == "\n" ? "n"
-          : !group || /\s/.test(cur) ? null
-          : "p";
-        if (group && !first && !type) { type = "s"; }
-        if (sawType && sawType != type) {
-          if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";}
-          break
-        }
-
-        if (type) { sawType = type; }
-        if (dir > 0 && !moveOnce(!first)) { break }
-      }
-    }
-    var result = skipAtomic(doc, pos, oldPos, origDir, true);
-    if (equalCursorPos(oldPos, result)) { result.hitSide = true; }
-    return result
-  }
-
-  // For relative vertical movement. Dir may be -1 or 1. Unit can be
-  // "page" or "line". The resulting position will have a hitSide=true
-  // property if it reached the end of the document.
-  function findPosV(cm, pos, dir, unit) {
-    var doc = cm.doc, x = pos.left, y;
-    if (unit == "page") {
-      var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
-      var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);
-      y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;
-
-    } else if (unit == "line") {
-      y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
-    }
-    var target;
-    for (;;) {
-      target = coordsChar(cm, x, y);
-      if (!target.outside) { break }
-      if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
-      y += dir * 5;
-    }
-    return target
-  }
-
-  // CONTENTEDITABLE INPUT STYLE
-
-  var ContentEditableInput = function(cm) {
-    this.cm = cm;
-    this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
-    this.polling = new Delayed();
-    this.composing = null;
-    this.gracePeriod = false;
-    this.readDOMTimeout = null;
-  };
-
-  ContentEditableInput.prototype.init = function (display) {
-      var this$1 = this;
-
-    var input = this, cm = input.cm;
-    var div = input.div = display.lineDiv;
-    disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize);
-
-    function belongsToInput(e) {
-      for (var t = e.target; t; t = t.parentNode) {
-        if (t == div) { return true }
-        if (/\bCodeMirror-(?:line)?widget\b/.test(t.className)) { break }
-      }
-      return false
-    }
-
-    on(div, "paste", function (e) {
-      if (!belongsToInput(e) || signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
-      // IE doesn't fire input events, so we schedule a read for the pasted content in this way
-      if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }
-    });
-
-    on(div, "compositionstart", function (e) {
-      this$1.composing = {data: e.data, done: false};
-    });
-    on(div, "compositionupdate", function (e) {
-      if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }
-    });
-    on(div, "compositionend", function (e) {
-      if (this$1.composing) {
-        if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }
-        this$1.composing.done = true;
-      }
-    });
-
-    on(div, "touchstart", function () { return input.forceCompositionEnd(); });
-
-    on(div, "input", function () {
-      if (!this$1.composing) { this$1.readFromDOMSoon(); }
-    });
-
-    function onCopyCut(e) {
-      if (!belongsToInput(e) || signalDOMEvent(cm, e)) { return }
-      if (cm.somethingSelected()) {
-        setLastCopied({lineWise: false, text: cm.getSelections()});
-        if (e.type == "cut") { cm.replaceSelection("", null, "cut"); }
-      } else if (!cm.options.lineWiseCopyCut) {
-        return
-      } else {
-        var ranges = copyableRanges(cm);
-        setLastCopied({lineWise: true, text: ranges.text});
-        if (e.type == "cut") {
-          cm.operation(function () {
-            cm.setSelections(ranges.ranges, 0, sel_dontScroll);
-            cm.replaceSelection("", null, "cut");
-          });
-        }
-      }
-      if (e.clipboardData) {
-        e.clipboardData.clearData();
-        var content = lastCopied.text.join("\n");
-        // iOS exposes the clipboard API, but seems to discard content inserted into it
-        e.clipboardData.setData("Text", content);
-        if (e.clipboardData.getData("Text") == content) {
-          e.preventDefault();
-          return
-        }
-      }
-      // Old-fashioned briefly-focus-a-textarea hack
-      var kludge = hiddenTextarea(), te = kludge.firstChild;
-      cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
-      te.value = lastCopied.text.join("\n");
-      var hadFocus = document.activeElement;
-      selectInput(te);
-      setTimeout(function () {
-        cm.display.lineSpace.removeChild(kludge);
-        hadFocus.focus();
-        if (hadFocus == div) { input.showPrimarySelection(); }
-      }, 50);
-    }
-    on(div, "copy", onCopyCut);
-    on(div, "cut", onCopyCut);
-  };
-
-  ContentEditableInput.prototype.screenReaderLabelChanged = function (label) {
-    // Label for screenreaders, accessibility
-    if(label) {
-      this.div.setAttribute('aria-label', label);
-    } else {
-      this.div.removeAttribute('aria-label');
-    }
-  };
-
-  ContentEditableInput.prototype.prepareSelection = function () {
-    var result = prepareSelection(this.cm, false);
-    result.focus = document.activeElement == this.div;
-    return result
-  };
-
-  ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
-    if (!info || !this.cm.display.view.length) { return }
-    if (info.focus || takeFocus) { this.showPrimarySelection(); }
-    this.showMultipleSelections(info);
-  };
-
-  ContentEditableInput.prototype.getSelection = function () {
-    return this.cm.display.wrapper.ownerDocument.getSelection()
-  };
-
-  ContentEditableInput.prototype.showPrimarySelection = function () {
-    var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();
-    var from = prim.from(), to = prim.to();
-
-    if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
-      sel.removeAllRanges();
-      return
-    }
-
-    var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
-    var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);
-    if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
-        cmp(minPos(curAnchor, curFocus), from) == 0 &&
-        cmp(maxPos(curAnchor, curFocus), to) == 0)
-      { return }
-
-    var view = cm.display.view;
-    var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
-        {node: view[0].measure.map[2], offset: 0};
-    var end = to.line < cm.display.viewTo && posToDOM(cm, to);
-    if (!end) {
-      var measure = view[view.length - 1].measure;
-      var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
-      end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]};
-    }
-
-    if (!start || !end) {
-      sel.removeAllRanges();
-      return
-    }
-
-    var old = sel.rangeCount && sel.getRangeAt(0), rng;
-    try { rng = range(start.node, start.offset, end.offset, end.node); }
-    catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
-    if (rng) {
-      if (!gecko && cm.state.focused) {
-        sel.collapse(start.node, start.offset);
-        if (!rng.collapsed) {
-          sel.removeAllRanges();
-          sel.addRange(rng);
-        }
-      } else {
-        sel.removeAllRanges();
-        sel.addRange(rng);
-      }
-      if (old && sel.anchorNode == null) { sel.addRange(old); }
-      else if (gecko) { this.startGracePeriod(); }
-    }
-    this.rememberSelection();
-  };
-
-  ContentEditableInput.prototype.startGracePeriod = function () {
-      var this$1 = this;
-
-    clearTimeout(this.gracePeriod);
-    this.gracePeriod = setTimeout(function () {
-      this$1.gracePeriod = false;
-      if (this$1.selectionChanged())
-        { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }
-    }, 20);
-  };
-
-  ContentEditableInput.prototype.showMultipleSelections = function (info) {
-    removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
-    removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
-  };
-
-  ContentEditableInput.prototype.rememberSelection = function () {
-    var sel = this.getSelection();
-    this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
-    this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
-  };
-
-  ContentEditableInput.prototype.selectionInEditor = function () {
-    var sel = this.getSelection();
-    if (!sel.rangeCount) { return false }
-    var node = sel.getRangeAt(0).commonAncestorContainer;
-    return contains(this.div, node)
-  };
-
-  ContentEditableInput.prototype.focus = function () {
-    if (this.cm.options.readOnly != "nocursor") {
-      if (!this.selectionInEditor() || document.activeElement != this.div)
-        { this.showSelection(this.prepareSelection(), true); }
-      this.div.focus();
-    }
-  };
-  ContentEditableInput.prototype.blur = function () { this.div.blur(); };
-  ContentEditableInput.prototype.getField = function () { return this.div };
-
-  ContentEditableInput.prototype.supportsTouch = function () { return true };
-
-  ContentEditableInput.prototype.receivedFocus = function () {
-    var input = this;
-    if (this.selectionInEditor())
-      { this.pollSelection(); }
-    else
-      { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }
-
-    function poll() {
-      if (input.cm.state.focused) {
-        input.pollSelection();
-        input.polling.set(input.cm.options.pollInterval, poll);
-      }
-    }
-    this.polling.set(this.cm.options.pollInterval, poll);
-  };
-
-  ContentEditableInput.prototype.selectionChanged = function () {
-    var sel = this.getSelection();
-    return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
-      sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
-  };
-
-  ContentEditableInput.prototype.pollSelection = function () {
-    if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
-    var sel = this.getSelection(), cm = this.cm;
-    // On Android Chrome (version 56, at least), backspacing into an
-    // uneditable block element will put the cursor in that element,
-    // and then, because it's not editable, hide the virtual keyboard.
-    // Because Android doesn't allow us to actually detect backspace
-    // presses in a sane way, this code checks for when that happens
-    // and simulates a backspace press in this case.
-    if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) {
-      this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs});
-      this.blur();
-      this.focus();
-      return
-    }
-    if (this.composing) { return }
-    this.rememberSelection();
-    var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
-    var head = domToPos(cm, sel.focusNode, sel.focusOffset);
-    if (anchor && head) { runInOp(cm, function () {
-      setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
-      if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }
-    }); }
-  };
-
-  ContentEditableInput.prototype.pollContent = function () {
-    if (this.readDOMTimeout != null) {
-      clearTimeout(this.readDOMTimeout);
-      this.readDOMTimeout = null;
-    }
-
-    var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
-    var from = sel.from(), to = sel.to();
-    if (from.ch == 0 && from.line > cm.firstLine())
-      { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }
-    if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
-      { to = Pos(to.line + 1, 0); }
-    if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
-
-    var fromIndex, fromLine, fromNode;
-    if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
-      fromLine = lineNo(display.view[0].line);
-      fromNode = display.view[0].node;
-    } else {
-      fromLine = lineNo(display.view[fromIndex].line);
-      fromNode = display.view[fromIndex - 1].node.nextSibling;
-    }
-    var toIndex = findViewIndex(cm, to.line);
-    var toLine, toNode;
-    if (toIndex == display.view.length - 1) {
-      toLine = display.viewTo - 1;
-      toNode = display.lineDiv.lastChild;
-    } else {
-      toLine = lineNo(display.view[toIndex + 1].line) - 1;
-      toNode = display.view[toIndex + 1].node.previousSibling;
-    }
-
-    if (!fromNode) { return false }
-    var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
-    var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
-    while (newText.length > 1 && oldText.length > 1) {
-      if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
-      else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
-      else { break }
-    }
-
-    var cutFront = 0, cutEnd = 0;
-    var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
-    while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
-      { ++cutFront; }
-    var newBot = lst(newText), oldBot = lst(oldText);
-    var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
-                             oldBot.length - (oldText.length == 1 ? cutFront : 0));
-    while (cutEnd < maxCutEnd &&
-           newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
-      { ++cutEnd; }
-    // Try to move start of change to start of selection if ambiguous
-    if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
-      while (cutFront && cutFront > from.ch &&
-             newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
-        cutFront--;
-        cutEnd++;
-      }
-    }
-
-    newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "");
-    newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "");
-
-    var chFrom = Pos(fromLine, cutFront);
-    var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
-    if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
-      replaceRange(cm.doc, newText, chFrom, chTo, "+input");
-      return true
-    }
-  };
-
-  ContentEditableInput.prototype.ensurePolled = function () {
-    this.forceCompositionEnd();
-  };
-  ContentEditableInput.prototype.reset = function () {
-    this.forceCompositionEnd();
-  };
-  ContentEditableInput.prototype.forceCompositionEnd = function () {
-    if (!this.composing) { return }
-    clearTimeout(this.readDOMTimeout);
-    this.composing = null;
-    this.updateFromDOM();
-    this.div.blur();
-    this.div.focus();
-  };
-  ContentEditableInput.prototype.readFromDOMSoon = function () {
-      var this$1 = this;
-
-    if (this.readDOMTimeout != null) { return }
-    this.readDOMTimeout = setTimeout(function () {
-      this$1.readDOMTimeout = null;
-      if (this$1.composing) {
-        if (this$1.composing.done) { this$1.composing = null; }
-        else { return }
-      }
-      this$1.updateFromDOM();
-    }, 80);
-  };
-
-  ContentEditableInput.prototype.updateFromDOM = function () {
-      var this$1 = this;
-
-    if (this.cm.isReadOnly() || !this.pollContent())
-      { runInOp(this.cm, function () { return regChange(this$1.cm); }); }
-  };
-
-  ContentEditableInput.prototype.setUneditable = function (node) {
-    node.contentEditable = "false";
-  };
-
-  ContentEditableInput.prototype.onKeyPress = function (e) {
-    if (e.charCode == 0 || this.composing) { return }
-    e.preventDefault();
-    if (!this.cm.isReadOnly())
-      { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }
-  };
-
-  ContentEditableInput.prototype.readOnlyChanged = function (val) {
-    this.div.contentEditable = String(val != "nocursor");
-  };
-
-  ContentEditableInput.prototype.onContextMenu = function () {};
-  ContentEditableInput.prototype.resetPosition = function () {};
-
-  ContentEditableInput.prototype.needsContentAttribute = true;
-
-  function posToDOM(cm, pos) {
-    var view = findViewForLine(cm, pos.line);
-    if (!view || view.hidden) { return null }
-    var line = getLine(cm.doc, pos.line);
-    var info = mapFromLineView(view, line, pos.line);
-
-    var order = getOrder(line, cm.doc.direction), side = "left";
-    if (order) {
-      var partPos = getBidiPartAt(order, pos.ch);
-      side = partPos % 2 ? "right" : "left";
-    }
-    var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
-    result.offset = result.collapse == "right" ? result.end : result.start;
-    return result
-  }
-
-  function isInGutter(node) {
-    for (var scan = node; scan; scan = scan.parentNode)
-      { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
-    return false
-  }
-
-  function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
-
-  function domTextBetween(cm, from, to, fromLine, toLine) {
-    var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false;
-    function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
-    function close() {
-      if (closing) {
-        text += lineSep;
-        if (extraLinebreak) { text += lineSep; }
-        closing = extraLinebreak = false;
-      }
-    }
-    function addText(str) {
-      if (str) {
-        close();
-        text += str;
-      }
-    }
-    function walk(node) {
-      if (node.nodeType == 1) {
-        var cmText = node.getAttribute("cm-text");
-        if (cmText) {
-          addText(cmText);
-          return
-        }
-        var markerID = node.getAttribute("cm-marker"), range;
-        if (markerID) {
-          var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
-          if (found.length && (range = found[0].find(0)))
-            { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)); }
-          return
-        }
-        if (node.getAttribute("contenteditable") == "false") { return }
-        var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName);
-        if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return }
-
-        if (isBlock) { close(); }
-        for (var i = 0; i < node.childNodes.length; i++)
-          { walk(node.childNodes[i]); }
-
-        if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; }
-        if (isBlock) { closing = true; }
-      } else if (node.nodeType == 3) {
-        addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " "));
-      }
-    }
-    for (;;) {
-      walk(from);
-      if (from == to) { break }
-      from = from.nextSibling;
-      extraLinebreak = false;
-    }
-    return text
-  }
-
-  function domToPos(cm, node, offset) {
-    var lineNode;
-    if (node == cm.display.lineDiv) {
-      lineNode = cm.display.lineDiv.childNodes[offset];
-      if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
-      node = null; offset = 0;
-    } else {
-      for (lineNode = node;; lineNode = lineNode.parentNode) {
-        if (!lineNode || lineNode == cm.display.lineDiv) { return null }
-        if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
-      }
-    }
-    for (var i = 0; i < cm.display.view.length; i++) {
-      var lineView = cm.display.view[i];
-      if (lineView.node == lineNode)
-        { return locateNodeInLineView(lineView, node, offset) }
-    }
-  }
-
-  function locateNodeInLineView(lineView, node, offset) {
-    var wrapper = lineView.text.firstChild, bad = false;
-    if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
-    if (node == wrapper) {
-      bad = true;
-      node = wrapper.childNodes[offset];
-      offset = 0;
-      if (!node) {
-        var line = lineView.rest ? lst(lineView.rest) : lineView.line;
-        return badPos(Pos(lineNo(line), line.text.length), bad)
-      }
-    }
-
-    var textNode = node.nodeType == 3 ? node : null, topNode = node;
-    if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
-      textNode = node.firstChild;
-      if (offset) { offset = textNode.nodeValue.length; }
-    }
-    while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }
-    var measure = lineView.measure, maps = measure.maps;
-
-    function find(textNode, topNode, offset) {
-      for (var i = -1; i < (maps ? maps.length : 0); i++) {
-        var map = i < 0 ? measure.map : maps[i];
-        for (var j = 0; j < map.length; j += 3) {
-          var curNode = map[j + 2];
-          if (curNode == textNode || curNode == topNode) {
-            var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
-            var ch = map[j] + offset;
-            if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)]; }
-            return Pos(line, ch)
-          }
-        }
-      }
-    }
-    var found = find(textNode, topNode, offset);
-    if (found) { return badPos(found, bad) }
-
-    // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
-    for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
-      found = find(after, after.firstChild, 0);
-      if (found)
-        { return badPos(Pos(found.line, found.ch - dist), bad) }
-      else
-        { dist += after.textContent.length; }
-    }
-    for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
-      found = find(before, before.firstChild, -1);
-      if (found)
-        { return badPos(Pos(found.line, found.ch + dist$1), bad) }
-      else
-        { dist$1 += before.textContent.length; }
-    }
-  }
-
-  // TEXTAREA INPUT STYLE
-
-  var TextareaInput = function(cm) {
-    this.cm = cm;
-    // See input.poll and input.reset
-    this.prevInput = "";
-
-    // Flag that indicates whether we expect input to appear real soon
-    // now (after some event like 'keypress' or 'input') and are
-    // polling intensively.
-    this.pollingFast = false;
-    // Self-resetting timeout for the poller
-    this.polling = new Delayed();
-    // Used to work around IE issue with selection being forgotten when focus moves away from textarea
-    this.hasSelection = false;
-    this.composing = null;
-  };
-
-  TextareaInput.prototype.init = function (display) {
-      var this$1 = this;
-
-    var input = this, cm = this.cm;
-    this.createField(display);
-    var te = this.textarea;
-
-    display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild);
-
-    // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
-    if (ios) { te.style.width = "0px"; }
-
-    on(te, "input", function () {
-      if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }
-      input.poll();
-    });
-
-    on(te, "paste", function (e) {
-      if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
-
-      cm.state.pasteIncoming = +new Date;
-      input.fastPoll();
-    });
-
-    function prepareCopyCut(e) {
-      if (signalDOMEvent(cm, e)) { return }
-      if (cm.somethingSelected()) {
-        setLastCopied({lineWise: false, text: cm.getSelections()});
-      } else if (!cm.options.lineWiseCopyCut) {
-        return
-      } else {
-        var ranges = copyableRanges(cm);
-        setLastCopied({lineWise: true, text: ranges.text});
-        if (e.type == "cut") {
-          cm.setSelections(ranges.ranges, null, sel_dontScroll);
-        } else {
-          input.prevInput = "";
-          te.value = ranges.text.join("\n");
-          selectInput(te);
-        }
-      }
-      if (e.type == "cut") { cm.state.cutIncoming = +new Date; }
-    }
-    on(te, "cut", prepareCopyCut);
-    on(te, "copy", prepareCopyCut);
-
-    on(display.scroller, "paste", function (e) {
-      if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
-      if (!te.dispatchEvent) {
-        cm.state.pasteIncoming = +new Date;
-        input.focus();
-        return
-      }
-
-      // Pass the `paste` event to the textarea so it's handled by its event listener.
-      var event = new Event("paste");
-      event.clipboardData = e.clipboardData;
-      te.dispatchEvent(event);
-    });
-
-    // Prevent normal selection in the editor (we handle our own)
-    on(display.lineSpace, "selectstart", function (e) {
-      if (!eventInWidget(display, e)) { e_preventDefault(e); }
-    });
-
-    on(te, "compositionstart", function () {
-      var start = cm.getCursor("from");
-      if (input.composing) { input.composing.range.clear(); }
-      input.composing = {
-        start: start,
-        range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
-      };
-    });
-    on(te, "compositionend", function () {
-      if (input.composing) {
-        input.poll();
-        input.composing.range.clear();
-        input.composing = null;
-      }
-    });
-  };
-
-  TextareaInput.prototype.createField = function (_display) {
-    // Wraps and hides input textarea
-    this.wrapper = hiddenTextarea();
-    // The semihidden textarea that is focused when the editor is
-    // focused, and receives input.
-    this.textarea = this.wrapper.firstChild;
-  };
-
-  TextareaInput.prototype.screenReaderLabelChanged = function (label) {
-    // Label for screenreaders, accessibility
-    if(label) {
-      this.textarea.setAttribute('aria-label', label);
-    } else {
-      this.textarea.removeAttribute('aria-label');
-    }
-  };
-
-  TextareaInput.prototype.prepareSelection = function () {
-    // Redraw the selection and/or cursor
-    var cm = this.cm, display = cm.display, doc = cm.doc;
-    var result = prepareSelection(cm);
-
-    // Move the hidden textarea near the cursor to prevent scrolling artifacts
-    if (cm.options.moveInputWithCursor) {
-      var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
-      var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
-      result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
-                                          headPos.top + lineOff.top - wrapOff.top));
-      result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
-                                           headPos.left + lineOff.left - wrapOff.left));
-    }
-
-    return result
-  };
-
-  TextareaInput.prototype.showSelection = function (drawn) {
-    var cm = this.cm, display = cm.display;
-    removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
-    removeChildrenAndAdd(display.selectionDiv, drawn.selection);
-    if (drawn.teTop != null) {
-      this.wrapper.style.top = drawn.teTop + "px";
-      this.wrapper.style.left = drawn.teLeft + "px";
-    }
-  };
-
-  // Reset the input to correspond to the selection (or to be empty,
-  // when not typing and nothing is selected)
-  TextareaInput.prototype.reset = function (typing) {
-    if (this.contextMenuPending || this.composing) { return }
-    var cm = this.cm;
-    if (cm.somethingSelected()) {
-      this.prevInput = "";
-      var content = cm.getSelection();
-      this.textarea.value = content;
-      if (cm.state.focused) { selectInput(this.textarea); }
-      if (ie && ie_version >= 9) { this.hasSelection = content; }
-    } else if (!typing) {
-      this.prevInput = this.textarea.value = "";
-      if (ie && ie_version >= 9) { this.hasSelection = null; }
-    }
-  };
-
-  TextareaInput.prototype.getField = function () { return this.textarea };
-
-  TextareaInput.prototype.supportsTouch = function () { return false };
-
-  TextareaInput.prototype.focus = function () {
-    if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
-      try { this.textarea.focus(); }
-      catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
-    }
-  };
-
-  TextareaInput.prototype.blur = function () { this.textarea.blur(); };
-
-  TextareaInput.prototype.resetPosition = function () {
-    this.wrapper.style.top = this.wrapper.style.left = 0;
-  };
-
-  TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };
-
-  // Poll for input changes, using the normal rate of polling. This
-  // runs as long as the editor is focused.
-  TextareaInput.prototype.slowPoll = function () {
-      var this$1 = this;
-
-    if (this.pollingFast) { return }
-    this.polling.set(this.cm.options.pollInterval, function () {
-      this$1.poll();
-      if (this$1.cm.state.focused) { this$1.slowPoll(); }
-    });
-  };
-
-  // When an event has just come in that is likely to add or change
-  // something in the input textarea, we poll faster, to ensure that
-  // the change appears on the screen quickly.
-  TextareaInput.prototype.fastPoll = function () {
-    var missed = false, input = this;
-    input.pollingFast = true;
-    function p() {
-      var changed = input.poll();
-      if (!changed && !missed) {missed = true; input.polling.set(60, p);}
-      else {input.pollingFast = false; input.slowPoll();}
-    }
-    input.polling.set(20, p);
-  };
-
-  // Read input from the textarea, and update the document to match.
-  // When something is selected, it is present in the textarea, and
-  // selected (unless it is huge, in which case a placeholder is
-  // used). When nothing is selected, the cursor sits after previously
-  // seen text (can be empty), which is stored in prevInput (we must
-  // not reset the textarea when typing, because that breaks IME).
-  TextareaInput.prototype.poll = function () {
-      var this$1 = this;
-
-    var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
-    // Since this is called a *lot*, try to bail out as cheaply as
-    // possible when it is clear that nothing happened. hasSelection
-    // will be the case when there is a lot of text in the textarea,
-    // in which case reading its value would be expensive.
-    if (this.contextMenuPending || !cm.state.focused ||
-        (hasSelection(input) && !prevInput && !this.composing) ||
-        cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
-      { return false }
-
-    var text = input.value;
-    // If nothing changed, bail.
-    if (text == prevInput && !cm.somethingSelected()) { return false }
-    // Work around nonsensical selection resetting in IE9/10, and
-    // inexplicable appearance of private area unicode characters on
-    // some key combos in Mac (#2689).
-    if (ie && ie_version >= 9 && this.hasSelection === text ||
-        mac && /[\uf700-\uf7ff]/.test(text)) {
-      cm.display.input.reset();
-      return false
-    }
-
-    if (cm.doc.sel == cm.display.selForContextMenu) {
-      var first = text.charCodeAt(0);
-      if (first == 0x200b && !prevInput) { prevInput = "\u200b"; }
-      if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
-    }
-    // Find the part of the input that is actually new
-    var same = 0, l = Math.min(prevInput.length, text.length);
-    while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }
-
-    runInOp(cm, function () {
-      applyTextInput(cm, text.slice(same), prevInput.length - same,
-                     null, this$1.composing ? "*compose" : null);
-
-      // Don't leave long text in the textarea, since it makes further polling slow
-      if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; }
-      else { this$1.prevInput = text; }
-
-      if (this$1.composing) {
-        this$1.composing.range.clear();
-        this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
-                                           {className: "CodeMirror-composing"});
-      }
-    });
-    return true
-  };
-
-  TextareaInput.prototype.ensurePolled = function () {
-    if (this.pollingFast && this.poll()) { this.pollingFast = false; }
-  };
-
-  TextareaInput.prototype.onKeyPress = function () {
-    if (ie && ie_version >= 9) { this.hasSelection = null; }
-    this.fastPoll();
-  };
-
-  TextareaInput.prototype.onContextMenu = function (e) {
-    var input = this, cm = input.cm, display = cm.display, te = input.textarea;
-    if (input.contextMenuPending) { input.contextMenuPending(); }
-    var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
-    if (!pos || presto) { return } // Opera is difficult.
-
-    // Reset the current text selection only if the click is done outside of the selection
-    // and 'resetSelectionOnContextMenu' option is true.
-    var reset = cm.options.resetSelectionOnContextMenu;
-    if (reset && cm.doc.sel.contains(pos) == -1)
-      { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }
-
-    var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;
-    var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect();
-    input.wrapper.style.cssText = "position: static";
-    te.style.cssText = "position: absolute; width: 30px; height: 30px;\n      top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n      z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n      outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
-    var oldScrollY;
-    if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712)
-    display.input.focus();
-    if (webkit) { window.scrollTo(null, oldScrollY); }
-    display.input.reset();
-    // Adds "Select all" to context menu in FF
-    if (!cm.somethingSelected()) { te.value = input.prevInput = " "; }
-    input.contextMenuPending = rehide;
-    display.selForContextMenu = cm.doc.sel;
-    clearTimeout(display.detectingSelectAll);
-
-    // Select-all will be greyed out if there's nothing to select, so
-    // this adds a zero-width space so that we can later check whether
-    // it got selected.
-    function prepareSelectAllHack() {
-      if (te.selectionStart != null) {
-        var selected = cm.somethingSelected();
-        var extval = "\u200b" + (selected ? te.value : "");
-        te.value = "\u21da"; // Used to catch context-menu undo
-        te.value = extval;
-        input.prevInput = selected ? "" : "\u200b";
-        te.selectionStart = 1; te.selectionEnd = extval.length;
-        // Re-set this, in case some other handler touched the
-        // selection in the meantime.
-        display.selForContextMenu = cm.doc.sel;
-      }
-    }
-    function rehide() {
-      if (input.contextMenuPending != rehide) { return }
-      input.contextMenuPending = false;
-      input.wrapper.style.cssText = oldWrapperCSS;
-      te.style.cssText = oldCSS;
-      if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }
-
-      // Try to detect the user choosing select-all
-      if (te.selectionStart != null) {
-        if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }
-        var i = 0, poll = function () {
-          if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
-              te.selectionEnd > 0 && input.prevInput == "\u200b") {
-            operation(cm, selectAll)(cm);
-          } else if (i++ < 10) {
-            display.detectingSelectAll = setTimeout(poll, 500);
-          } else {
-            display.selForContextMenu = null;
-            display.input.reset();
-          }
-        };
-        display.detectingSelectAll = setTimeout(poll, 200);
-      }
-    }
-
-    if (ie && ie_version >= 9) { prepareSelectAllHack(); }
-    if (captureRightClick) {
-      e_stop(e);
-      var mouseup = function () {
-        off(window, "mouseup", mouseup);
-        setTimeout(rehide, 20);
-      };
-      on(window, "mouseup", mouseup);
-    } else {
-      setTimeout(rehide, 50);
-    }
-  };
-
-  TextareaInput.prototype.readOnlyChanged = function (val) {
-    if (!val) { this.reset(); }
-    this.textarea.disabled = val == "nocursor";
-  };
-
-  TextareaInput.prototype.setUneditable = function () {};
-
-  TextareaInput.prototype.needsContentAttribute = false;
-
-  function fromTextArea(textarea, options) {
-    options = options ? copyObj(options) : {};
-    options.value = textarea.value;
-    if (!options.tabindex && textarea.tabIndex)
-      { options.tabindex = textarea.tabIndex; }
-    if (!options.placeholder && textarea.placeholder)
-      { options.placeholder = textarea.placeholder; }
-    // Set autofocus to true if this textarea is focused, or if it has
-    // autofocus and no other element is focused.
-    if (options.autofocus == null) {
-      var hasFocus = activeElt();
-      options.autofocus = hasFocus == textarea ||
-        textarea.getAttribute("autofocus") != null && hasFocus == document.body;
-    }
-
-    function save() {textarea.value = cm.getValue();}
-
-    var realSubmit;
-    if (textarea.form) {
-      on(textarea.form, "submit", save);
-      // Deplorable hack to make the submit method do the right thing.
-      if (!options.leaveSubmitMethodAlone) {
-        var form = textarea.form;
-        realSubmit = form.submit;
-        try {
-          var wrappedSubmit = form.submit = function () {
-            save();
-            form.submit = realSubmit;
-            form.submit();
-            form.submit = wrappedSubmit;
-          };
-        } catch(e) {}
-      }
-    }
-
-    options.finishInit = function (cm) {
-      cm.save = save;
-      cm.getTextArea = function () { return textarea; };
-      cm.toTextArea = function () {
-        cm.toTextArea = isNaN; // Prevent this from being ran twice
-        save();
-        textarea.parentNode.removeChild(cm.getWrapperElement());
-        textarea.style.display = "";
-        if (textarea.form) {
-          off(textarea.form, "submit", save);
-          if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function")
-            { textarea.form.submit = realSubmit; }
-        }
-      };
-    };
-
-    textarea.style.display = "none";
-    var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
-      options);
-    return cm
-  }
-
-  function addLegacyProps(CodeMirror) {
-    CodeMirror.off = off;
-    CodeMirror.on = on;
-    CodeMirror.wheelEventPixels = wheelEventPixels;
-    CodeMirror.Doc = Doc;
-    CodeMirror.splitLines = splitLinesAuto;
-    CodeMirror.countColumn = countColumn;
-    CodeMirror.findColumn = findColumn;
-    CodeMirror.isWordChar = isWordCharBasic;
-    CodeMirror.Pass = Pass;
-    CodeMirror.signal = signal;
-    CodeMirror.Line = Line;
-    CodeMirror.changeEnd = changeEnd;
-    CodeMirror.scrollbarModel = scrollbarModel;
-    CodeMirror.Pos = Pos;
-    CodeMirror.cmpPos = cmp;
-    CodeMirror.modes = modes;
-    CodeMirror.mimeModes = mimeModes;
-    CodeMirror.resolveMode = resolveMode;
-    CodeMirror.getMode = getMode;
-    CodeMirror.modeExtensions = modeExtensions;
-    CodeMirror.extendMode = extendMode;
-    CodeMirror.copyState = copyState;
-    CodeMirror.startState = startState;
-    CodeMirror.innerMode = innerMode;
-    CodeMirror.commands = commands;
-    CodeMirror.keyMap = keyMap;
-    CodeMirror.keyName = keyName;
-    CodeMirror.isModifierKey = isModifierKey;
-    CodeMirror.lookupKey = lookupKey;
-    CodeMirror.normalizeKeyMap = normalizeKeyMap;
-    CodeMirror.StringStream = StringStream;
-    CodeMirror.SharedTextMarker = SharedTextMarker;
-    CodeMirror.TextMarker = TextMarker;
-    CodeMirror.LineWidget = LineWidget;
-    CodeMirror.e_preventDefault = e_preventDefault;
-    CodeMirror.e_stopPropagation = e_stopPropagation;
-    CodeMirror.e_stop = e_stop;
-    CodeMirror.addClass = addClass;
-    CodeMirror.contains = contains;
-    CodeMirror.rmClass = rmClass;
-    CodeMirror.keyNames = keyNames;
-  }
-
-  // EDITOR CONSTRUCTOR
-
-  defineOptions(CodeMirror);
-
-  addEditorMethods(CodeMirror);
-
-  // Set up methods on CodeMirror's prototype to redirect to the editor's document.
-  var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
-  for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
-    { CodeMirror.prototype[prop] = (function(method) {
-      return function() {return method.apply(this.doc, arguments)}
-    })(Doc.prototype[prop]); } }
-
-  eventMixin(Doc);
-  CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
-
-  // Extra arguments are stored as the mode's dependencies, which is
-  // used by (legacy) mechanisms like loadmode.js to automatically
-  // load a mode. (Preferred mechanism is the require/define calls.)
-  CodeMirror.defineMode = function(name/*, mode, …*/) {
-    if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; }
-    defineMode.apply(this, arguments);
-  };
-
-  CodeMirror.defineMIME = defineMIME;
-
-  // Minimal default mode.
-  CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
-  CodeMirror.defineMIME("text/plain", "null");
-
-  // EXTENSIONS
-
-  CodeMirror.defineExtension = function (name, func) {
-    CodeMirror.prototype[name] = func;
-  };
-  CodeMirror.defineDocExtension = function (name, func) {
-    Doc.prototype[name] = func;
-  };
-
-  CodeMirror.fromTextArea = fromTextArea;
-
-  addLegacyProps(CodeMirror);
-
-  CodeMirror.version = "5.56.0";
-
-  return CodeMirror;
-
-})));
-
-
-/* ---- extension/simple.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  CodeMirror.defineSimpleMode = function(name, states) {
-    CodeMirror.defineMode(name, function(config) {
-      return CodeMirror.simpleMode(config, states);
-    });
-  };
-
-  CodeMirror.simpleMode = function(config, states) {
-    ensureState(states, "start");
-    var states_ = {}, meta = states.meta || {}, hasIndentation = false;
-    for (var state in states) if (state != meta && states.hasOwnProperty(state)) {
-      var list = states_[state] = [], orig = states[state];
-      for (var i = 0; i < orig.length; i++) {
-        var data = orig[i];
-        list.push(new Rule(data, states));
-        if (data.indent || data.dedent) hasIndentation = true;
-      }
-    }
-    var mode = {
-      startState: function() {
-        return {state: "start", pending: null,
-                local: null, localState: null,
-                indent: hasIndentation ? [] : null};
-      },
-      copyState: function(state) {
-        var s = {state: state.state, pending: state.pending,
-                 local: state.local, localState: null,
-                 indent: state.indent && state.indent.slice(0)};
-        if (state.localState)
-          s.localState = CodeMirror.copyState(state.local.mode, state.localState);
-        if (state.stack)
-          s.stack = state.stack.slice(0);
-        for (var pers = state.persistentStates; pers; pers = pers.next)
-          s.persistentStates = {mode: pers.mode,
-                                spec: pers.spec,
-                                state: pers.state == state.localState ? s.localState : CodeMirror.copyState(pers.mode, pers.state),
-                                next: s.persistentStates};
-        return s;
-      },
-      token: tokenFunction(states_, config),
-      innerMode: function(state) { return state.local && {mode: state.local.mode, state: state.localState}; },
-      indent: indentFunction(states_, meta)
-    };
-    if (meta) for (var prop in meta) if (meta.hasOwnProperty(prop))
-      mode[prop] = meta[prop];
-    return mode;
-  };
-
-  function ensureState(states, name) {
-    if (!states.hasOwnProperty(name))
-      throw new Error("Undefined state " + name + " in simple mode");
-  }
-
-  function toRegex(val, caret) {
-    if (!val) return /(?:)/;
-    var flags = "";
-    if (val instanceof RegExp) {
-      if (val.ignoreCase) flags = "i";
-      val = val.source;
-    } else {
-      val = String(val);
-    }
-    return new RegExp((caret === false ? "" : "^") + "(?:" + val + ")", flags);
-  }
-
-  function asToken(val) {
-    if (!val) return null;
-    if (val.apply) return val
-    if (typeof val == "string") return val.replace(/\./g, " ");
-    var result = [];
-    for (var i = 0; i < val.length; i++)
-      result.push(val[i] && val[i].replace(/\./g, " "));
-    return result;
-  }
-
-  function Rule(data, states) {
-    if (data.next || data.push) ensureState(states, data.next || data.push);
-    this.regex = toRegex(data.regex);
-    this.token = asToken(data.token);
-    this.data = data;
-  }
-
-  function tokenFunction(states, config) {
-    return function(stream, state) {
-      if (state.pending) {
-        var pend = state.pending.shift();
-        if (state.pending.length == 0) state.pending = null;
-        stream.pos += pend.text.length;
-        return pend.token;
-      }
-
-      if (state.local) {
-        if (state.local.end && stream.match(state.local.end)) {
-          var tok = state.local.endToken || null;
-          state.local = state.localState = null;
-          return tok;
-        } else {
-          var tok = state.local.mode.token(stream, state.localState), m;
-          if (state.local.endScan && (m = state.local.endScan.exec(stream.current())))
-            stream.pos = stream.start + m.index;
-          return tok;
-        }
-      }
-
-      var curState = states[state.state];
-      for (var i = 0; i < curState.length; i++) {
-        var rule = curState[i];
-        var matches = (!rule.data.sol || stream.sol()) && stream.match(rule.regex);
-        if (matches) {
-          if (rule.data.next) {
-            state.state = rule.data.next;
-          } else if (rule.data.push) {
-            (state.stack || (state.stack = [])).push(state.state);
-            state.state = rule.data.push;
-          } else if (rule.data.pop && state.stack && state.stack.length) {
-            state.state = state.stack.pop();
-          }
-
-          if (rule.data.mode)
-            enterLocalMode(config, state, rule.data.mode, rule.token);
-          if (rule.data.indent)
-            state.indent.push(stream.indentation() + config.indentUnit);
-          if (rule.data.dedent)
-            state.indent.pop();
-          var token = rule.token
-          if (token && token.apply) token = token(matches)
-          if (matches.length > 2 && rule.token && typeof rule.token != "string") {
-            state.pending = [];
-            for (var j = 2; j < matches.length; j++)
-              if (matches[j])
-                state.pending.push({text: matches[j], token: rule.token[j - 1]});
-            stream.backUp(matches[0].length - (matches[1] ? matches[1].length : 0));
-            return token[0];
-          } else if (token && token.join) {
-            return token[0];
-          } else {
-            return token;
-          }
-        }
-      }
-      stream.next();
-      return null;
-    };
-  }
-
-  function cmp(a, b) {
-    if (a === b) return true;
-    if (!a || typeof a != "object" || !b || typeof b != "object") return false;
-    var props = 0;
-    for (var prop in a) if (a.hasOwnProperty(prop)) {
-      if (!b.hasOwnProperty(prop) || !cmp(a[prop], b[prop])) return false;
-      props++;
-    }
-    for (var prop in b) if (b.hasOwnProperty(prop)) props--;
-    return props == 0;
-  }
-
-  function enterLocalMode(config, state, spec, token) {
-    var pers;
-    if (spec.persistent) for (var p = state.persistentStates; p && !pers; p = p.next)
-      if (spec.spec ? cmp(spec.spec, p.spec) : spec.mode == p.mode) pers = p;
-    var mode = pers ? pers.mode : spec.mode || CodeMirror.getMode(config, spec.spec);
-    var lState = pers ? pers.state : CodeMirror.startState(mode);
-    if (spec.persistent && !pers)
-      state.persistentStates = {mode: mode, spec: spec.spec, state: lState, next: state.persistentStates};
-
-    state.localState = lState;
-    state.local = {mode: mode,
-                   end: spec.end && toRegex(spec.end),
-                   endScan: spec.end && spec.forceEnd !== false && toRegex(spec.end, false),
-                   endToken: token && token.join ? token[token.length - 1] : token};
-  }
-
-  function indexOf(val, arr) {
-    for (var i = 0; i < arr.length; i++) if (arr[i] === val) return true;
-  }
-
-  function indentFunction(states, meta) {
-    return function(state, textAfter, line) {
-      if (state.local && state.local.mode.indent)
-        return state.local.mode.indent(state.localState, textAfter, line);
-      if (state.indent == null || state.local || meta.dontIndentStates && indexOf(state.state, meta.dontIndentStates) > -1)
-        return CodeMirror.Pass;
-
-      var pos = state.indent.length - 1, rules = states[state.state];
-      scan: for (;;) {
-        for (var i = 0; i < rules.length; i++) {
-          var rule = rules[i];
-          if (rule.data.dedent && rule.data.dedentIfLineStart !== false) {
-            var m = rule.regex.exec(textAfter);
-            if (m && m[0]) {
-              pos--;
-              if (rule.next || rule.push) rules = states[rule.next || rule.push];
-              textAfter = textAfter.slice(m[0].length);
-              continue scan;
-            }
-          }
-        }
-        break;
-      }
-      return pos < 0 ? 0 : state.indent[pos];
-    };
-  }
-});
-
-
-/* ---- extension/sublime.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-// A rough approximation of Sublime Text's keybindings
-// Depends on addon/search/searchcursor.js and optionally addon/dialog/dialogs.js
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../lib/codemirror"), require("../addon/search/searchcursor"), require("../addon/edit/matchbrackets"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../lib/codemirror", "../addon/search/searchcursor", "../addon/edit/matchbrackets"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  var cmds = CodeMirror.commands;
-  var Pos = CodeMirror.Pos;
-
-  // This is not exactly Sublime's algorithm. I couldn't make heads or tails of that.
-  function findPosSubword(doc, start, dir) {
-    if (dir < 0 && start.ch == 0) return doc.clipPos(Pos(start.line - 1));
-    var line = doc.getLine(start.line);
-    if (dir > 0 && start.ch >= line.length) return doc.clipPos(Pos(start.line + 1, 0));
-    var state = "start", type, startPos = start.ch;
-    for (var pos = startPos, e = dir < 0 ? 0 : line.length, i = 0; pos != e; pos += dir, i++) {
-      var next = line.charAt(dir < 0 ? pos - 1 : pos);
-      var cat = next != "_" && CodeMirror.isWordChar(next) ? "w" : "o";
-      if (cat == "w" && next.toUpperCase() == next) cat = "W";
-      if (state == "start") {
-        if (cat != "o") { state = "in"; type = cat; }
-        else startPos = pos + dir
-      } else if (state == "in") {
-        if (type != cat) {
-          if (type == "w" && cat == "W" && dir < 0) pos--;
-          if (type == "W" && cat == "w" && dir > 0) { // From uppercase to lowercase
-            if (pos == startPos + 1) { type = "w"; continue; }
-            else pos--;
-          }
-          break;
-        }
-      }
-    }
-    return Pos(start.line, pos);
-  }
-
-  function moveSubword(cm, dir) {
-    cm.extendSelectionsBy(function(range) {
-      if (cm.display.shift || cm.doc.extend || range.empty())
-        return findPosSubword(cm.doc, range.head, dir);
-      else
-        return dir < 0 ? range.from() : range.to();
-    });
-  }
-
-  cmds.goSubwordLeft = function(cm) { moveSubword(cm, -1); };
-  cmds.goSubwordRight = function(cm) { moveSubword(cm, 1); };
-
-  cmds.scrollLineUp = function(cm) {
-    var info = cm.getScrollInfo();
-    if (!cm.somethingSelected()) {
-      var visibleBottomLine = cm.lineAtHeight(info.top + info.clientHeight, "local");
-      if (cm.getCursor().line >= visibleBottomLine)
-        cm.execCommand("goLineUp");
-    }
-    cm.scrollTo(null, info.top - cm.defaultTextHeight());
-  };
-  cmds.scrollLineDown = function(cm) {
-    var info = cm.getScrollInfo();
-    if (!cm.somethingSelected()) {
-      var visibleTopLine = cm.lineAtHeight(info.top, "local")+1;
-      if (cm.getCursor().line <= visibleTopLine)
-        cm.execCommand("goLineDown");
-    }
-    cm.scrollTo(null, info.top + cm.defaultTextHeight());
-  };
-
-  cmds.splitSelectionByLine = function(cm) {
-    var ranges = cm.listSelections(), lineRanges = [];
-    for (var i = 0; i < ranges.length; i++) {
-      var from = ranges[i].from(), to = ranges[i].to();
-      for (var line = from.line; line <= to.line; ++line)
-        if (!(to.line > from.line && line == to.line && to.ch == 0))
-          lineRanges.push({anchor: line == from.line ? from : Pos(line, 0),
-                           head: line == to.line ? to : Pos(line)});
-    }
-    cm.setSelections(lineRanges, 0);
-  };
-
-  cmds.singleSelectionTop = function(cm) {
-    var range = cm.listSelections()[0];
-    cm.setSelection(range.anchor, range.head, {scroll: false});
-  };
-
-  cmds.selectLine = function(cm) {
-    var ranges = cm.listSelections(), extended = [];
-    for (var i = 0; i < ranges.length; i++) {
-      var range = ranges[i];
-      extended.push({anchor: Pos(range.from().line, 0),
-                     head: Pos(range.to().line + 1, 0)});
-    }
-    cm.setSelections(extended);
-  };
-
-  function insertLine(cm, above) {
-    if (cm.isReadOnly()) return CodeMirror.Pass
-    cm.operation(function() {
-      var len = cm.listSelections().length, newSelection = [], last = -1;
-      for (var i = 0; i < len; i++) {
-        var head = cm.listSelections()[i].head;
-        if (head.line <= last) continue;
-        var at = Pos(head.line + (above ? 0 : 1), 0);
-        cm.replaceRange("\n", at, null, "+insertLine");
-        cm.indentLine(at.line, null, true);
-        newSelection.push({head: at, anchor: at});
-        last = head.line + 1;
-      }
-      cm.setSelections(newSelection);
-    });
-    cm.execCommand("indentAuto");
-  }
-
-  cmds.insertLineAfter = function(cm) { return insertLine(cm, false); };
-
-  cmds.insertLineBefore = function(cm) { return insertLine(cm, true); };
-
-  function wordAt(cm, pos) {
-    var start = pos.ch, end = start, line = cm.getLine(pos.line);
-    while (start && CodeMirror.isWordChar(line.charAt(start - 1))) --start;
-    while (end < line.length && CodeMirror.isWordChar(line.charAt(end))) ++end;
-    return {from: Pos(pos.line, start), to: Pos(pos.line, end), word: line.slice(start, end)};
-  }
-
-  cmds.selectNextOccurrence = function(cm) {
-    var from = cm.getCursor("from"), to = cm.getCursor("to");
-    var fullWord = cm.state.sublimeFindFullWord == cm.doc.sel;
-    if (CodeMirror.cmpPos(from, to) == 0) {
-      var word = wordAt(cm, from);
-      if (!word.word) return;
-      cm.setSelection(word.from, word.to);
-      fullWord = true;
-    } else {
-      var text = cm.getRange(from, to);
-      var query = fullWord ? new RegExp("\\b" + text + "\\b") : text;
-      var cur = cm.getSearchCursor(query, to);
-      var found = cur.findNext();
-      if (!found) {
-        cur = cm.getSearchCursor(query, Pos(cm.firstLine(), 0));
-        found = cur.findNext();
-      }
-      if (!found || isSelectedRange(cm.listSelections(), cur.from(), cur.to())) return
-      cm.addSelection(cur.from(), cur.to());
-    }
-    if (fullWord)
-      cm.state.sublimeFindFullWord = cm.doc.sel;
-  };
-
-  cmds.skipAndSelectNextOccurrence = function(cm) {
-    var prevAnchor = cm.getCursor("anchor"), prevHead = cm.getCursor("head");
-    cmds.selectNextOccurrence(cm);
-    if (CodeMirror.cmpPos(prevAnchor, prevHead) != 0) {
-      cm.doc.setSelections(cm.doc.listSelections()
-          .filter(function (sel) {
-            return sel.anchor != prevAnchor || sel.head != prevHead;
-          }));
-    }
-  }
-
-  function addCursorToSelection(cm, dir) {
-    var ranges = cm.listSelections(), newRanges = [];
-    for (var i = 0; i < ranges.length; i++) {
-      var range = ranges[i];
-      var newAnchor = cm.findPosV(
-          range.anchor, dir, "line", range.anchor.goalColumn);
-      var newHead = cm.findPosV(
-          range.head, dir, "line", range.head.goalColumn);
-      newAnchor.goalColumn = range.anchor.goalColumn != null ?
-          range.anchor.goalColumn : cm.cursorCoords(range.anchor, "div").left;
-      newHead.goalColumn = range.head.goalColumn != null ?
-          range.head.goalColumn : cm.cursorCoords(range.head, "div").left;
-      var newRange = {anchor: newAnchor, head: newHead};
-      newRanges.push(range);
-      newRanges.push(newRange);
-    }
-    cm.setSelections(newRanges);
-  }
-  cmds.addCursorToPrevLine = function(cm) { addCursorToSelection(cm, -1); };
-  cmds.addCursorToNextLine = function(cm) { addCursorToSelection(cm, 1); };
-
-  function isSelectedRange(ranges, from, to) {
-    for (var i = 0; i < ranges.length; i++)
-      if (CodeMirror.cmpPos(ranges[i].from(), from) == 0 &&
-          CodeMirror.cmpPos(ranges[i].to(), to) == 0) return true
-    return false
-  }
-
-  var mirror = "(){}[]";
-  function selectBetweenBrackets(cm) {
-    var ranges = cm.listSelections(), newRanges = []
-    for (var i = 0; i < ranges.length; i++) {
-      var range = ranges[i], pos = range.head, opening = cm.scanForBracket(pos, -1);
-      if (!opening) return false;
-      for (;;) {
-        var closing = cm.scanForBracket(pos, 1);
-        if (!closing) return false;
-        if (closing.ch == mirror.charAt(mirror.indexOf(opening.ch) + 1)) {
-          var startPos = Pos(opening.pos.line, opening.pos.ch + 1);
-          if (CodeMirror.cmpPos(startPos, range.from()) == 0 &&
-              CodeMirror.cmpPos(closing.pos, range.to()) == 0) {
-            opening = cm.scanForBracket(opening.pos, -1);
-            if (!opening) return false;
-          } else {
-            newRanges.push({anchor: startPos, head: closing.pos});
-            break;
-          }
-        }
-        pos = Pos(closing.pos.line, closing.pos.ch + 1);
-      }
-    }
-    cm.setSelections(newRanges);
-    return true;
-  }
-
-  cmds.selectScope = function(cm) {
-    selectBetweenBrackets(cm) || cm.execCommand("selectAll");
-  };
-  cmds.selectBetweenBrackets = function(cm) {
-    if (!selectBetweenBrackets(cm)) return CodeMirror.Pass;
-  };
-
-  function puncType(type) {
-    return !type ? null : /\bpunctuation\b/.test(type) ? type : undefined
-  }
-
-  cmds.goToBracket = function(cm) {
-    cm.extendSelectionsBy(function(range) {
-      var next = cm.scanForBracket(range.head, 1, puncType(cm.getTokenTypeAt(range.head)));
-      if (next && CodeMirror.cmpPos(next.pos, range.head) != 0) return next.pos;
-      var prev = cm.scanForBracket(range.head, -1, puncType(cm.getTokenTypeAt(Pos(range.head.line, range.head.ch + 1))));
-      return prev && Pos(prev.pos.line, prev.pos.ch + 1) || range.head;
-    });
-  };
-
-  cmds.swapLineUp = function(cm) {
-    if (cm.isReadOnly()) return CodeMirror.Pass
-    var ranges = cm.listSelections(), linesToMove = [], at = cm.firstLine() - 1, newSels = [];
-    for (var i = 0; i < ranges.length; i++) {
-      var range = ranges[i], from = range.from().line - 1, to = range.to().line;
-      newSels.push({anchor: Pos(range.anchor.line - 1, range.anchor.ch),
-                    head: Pos(range.head.line - 1, range.head.ch)});
-      if (range.to().ch == 0 && !range.empty()) --to;
-      if (from > at) linesToMove.push(from, to);
-      else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to;
-      at = to;
-    }
-    cm.operation(function() {
-      for (var i = 0; i < linesToMove.length; i += 2) {
-        var from = linesToMove[i], to = linesToMove[i + 1];
-        var line = cm.getLine(from);
-        cm.replaceRange("", Pos(from, 0), Pos(from + 1, 0), "+swapLine");
-        if (to > cm.lastLine())
-          cm.replaceRange("\n" + line, Pos(cm.lastLine()), null, "+swapLine");
-        else
-          cm.replaceRange(line + "\n", Pos(to, 0), null, "+swapLine");
-      }
-      cm.setSelections(newSels);
-      cm.scrollIntoView();
-    });
-  };
-
-  cmds.swapLineDown = function(cm) {
-    if (cm.isReadOnly()) return CodeMirror.Pass
-    var ranges = cm.listSelections(), linesToMove = [], at = cm.lastLine() + 1;
-    for (var i = ranges.length - 1; i >= 0; i--) {
-      var range = ranges[i], from = range.to().line + 1, to = range.from().line;
-      if (range.to().ch == 0 && !range.empty()) from--;
-      if (from < at) linesToMove.push(from, to);
-      else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to;
-      at = to;
-    }
-    cm.operation(function() {
-      for (var i = linesToMove.length - 2; i >= 0; i -= 2) {
-        var from = linesToMove[i], to = linesToMove[i + 1];
-        var line = cm.getLine(from);
-        if (from == cm.lastLine())
-          cm.replaceRange("", Pos(from - 1), Pos(from), "+swapLine");
-        else
-          cm.replaceRange("", Pos(from, 0), Pos(from + 1, 0), "+swapLine");
-        cm.replaceRange(line + "\n", Pos(to, 0), null, "+swapLine");
-      }
-      cm.scrollIntoView();
-    });
-  };
-
-  cmds.toggleCommentIndented = function(cm) {
-    cm.toggleComment({ indent: true });
-  }
-
-  cmds.joinLines = function(cm) {
-    var ranges = cm.listSelections(), joined = [];
-    for (var i = 0; i < ranges.length; i++) {
-      var range = ranges[i], from = range.from();
-      var start = from.line, end = range.to().line;
-      while (i < ranges.length - 1 && ranges[i + 1].from().line == end)
-        end = ranges[++i].to().line;
-      joined.push({start: start, end: end, anchor: !range.empty() && from});
-    }
-    cm.operation(function() {
-      var offset = 0, ranges = [];
-      for (var i = 0; i < joined.length; i++) {
-        var obj = joined[i];
-        var anchor = obj.anchor && Pos(obj.anchor.line - offset, obj.anchor.ch), head;
-        for (var line = obj.start; line <= obj.end; line++) {
-          var actual = line - offset;
-          if (line == obj.end) head = Pos(actual, cm.getLine(actual).length + 1);
-          if (actual < cm.lastLine()) {
-            cm.replaceRange(" ", Pos(actual), Pos(actual + 1, /^\s*/.exec(cm.getLine(actual + 1))[0].length));
-            ++offset;
-          }
-        }
-        ranges.push({anchor: anchor || head, head: head});
-      }
-      cm.setSelections(ranges, 0);
-    });
-  };
-
-  cmds.duplicateLine = function(cm) {
-    cm.operation(function() {
-      var rangeCount = cm.listSelections().length;
-      for (var i = 0; i < rangeCount; i++) {
-        var range = cm.listSelections()[i];
-        if (range.empty())
-          cm.replaceRange(cm.getLine(range.head.line) + "\n", Pos(range.head.line, 0));
-        else
-          cm.replaceRange(cm.getRange(range.from(), range.to()), range.from());
-      }
-      cm.scrollIntoView();
-    });
-  };
-
-
-  function sortLines(cm, caseSensitive) {
-    if (cm.isReadOnly()) return CodeMirror.Pass
-    var ranges = cm.listSelections(), toSort = [], selected;
-    for (var i = 0; i < ranges.length; i++) {
-      var range = ranges[i];
-      if (range.empty()) continue;
-      var from = range.from().line, to = range.to().line;
-      while (i < ranges.length - 1 && ranges[i + 1].from().line == to)
-        to = ranges[++i].to().line;
-      if (!ranges[i].to().ch) to--;
-      toSort.push(from, to);
-    }
-    if (toSort.length) selected = true;
-    else toSort.push(cm.firstLine(), cm.lastLine());
-
-    cm.operation(function() {
-      var ranges = [];
-      for (var i = 0; i < toSort.length; i += 2) {
-        var from = toSort[i], to = toSort[i + 1];
-        var start = Pos(from, 0), end = Pos(to);
-        var lines = cm.getRange(start, end, false);
-        if (caseSensitive)
-          lines.sort();
-        else
-          lines.sort(function(a, b) {
-            var au = a.toUpperCase(), bu = b.toUpperCase();
-            if (au != bu) { a = au; b = bu; }
-            return a < b ? -1 : a == b ? 0 : 1;
-          });
-        cm.replaceRange(lines, start, end);
-        if (selected) ranges.push({anchor: start, head: Pos(to + 1, 0)});
-      }
-      if (selected) cm.setSelections(ranges, 0);
-    });
-  }
-
-  cmds.sortLines = function(cm) { sortLines(cm, true); };
-  cmds.sortLinesInsensitive = function(cm) { sortLines(cm, false); };
-
-  cmds.nextBookmark = function(cm) {
-    var marks = cm.state.sublimeBookmarks;
-    if (marks) while (marks.length) {
-      var current = marks.shift();
-      var found = current.find();
-      if (found) {
-        marks.push(current);
-        return cm.setSelection(found.from, found.to);
-      }
-    }
-  };
-
-  cmds.prevBookmark = function(cm) {
-    var marks = cm.state.sublimeBookmarks;
-    if (marks) while (marks.length) {
-      marks.unshift(marks.pop());
-      var found = marks[marks.length - 1].find();
-      if (!found)
-        marks.pop();
-      else
-        return cm.setSelection(found.from, found.to);
-    }
-  };
-
-  cmds.toggleBookmark = function(cm) {
-    var ranges = cm.listSelections();
-    var marks = cm.state.sublimeBookmarks || (cm.state.sublimeBookmarks = []);
-    for (var i = 0; i < ranges.length; i++) {
-      var from = ranges[i].from(), to = ranges[i].to();
-      var found = ranges[i].empty() ? cm.findMarksAt(from) : cm.findMarks(from, to);
-      for (var j = 0; j < found.length; j++) {
-        if (found[j].sublimeBookmark) {
-          found[j].clear();
-          for (var k = 0; k < marks.length; k++)
-            if (marks[k] == found[j])
-              marks.splice(k--, 1);
-          break;
-        }
-      }
-      if (j == found.length)
-        marks.push(cm.markText(from, to, {sublimeBookmark: true, clearWhenEmpty: false}));
-    }
-  };
-
-  cmds.clearBookmarks = function(cm) {
-    var marks = cm.state.sublimeBookmarks;
-    if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear();
-    marks.length = 0;
-  };
-
-  cmds.selectBookmarks = function(cm) {
-    var marks = cm.state.sublimeBookmarks, ranges = [];
-    if (marks) for (var i = 0; i < marks.length; i++) {
-      var found = marks[i].find();
-      if (!found)
-        marks.splice(i--, 0);
-      else
-        ranges.push({anchor: found.from, head: found.to});
-    }
-    if (ranges.length)
-      cm.setSelections(ranges, 0);
-  };
-
-  function modifyWordOrSelection(cm, mod) {
-    cm.operation(function() {
-      var ranges = cm.listSelections(), indices = [], replacements = [];
-      for (var i = 0; i < ranges.length; i++) {
-        var range = ranges[i];
-        if (range.empty()) { indices.push(i); replacements.push(""); }
-        else replacements.push(mod(cm.getRange(range.from(), range.to())));
-      }
-      cm.replaceSelections(replacements, "around", "case");
-      for (var i = indices.length - 1, at; i >= 0; i--) {
-        var range = ranges[indices[i]];
-        if (at && CodeMirror.cmpPos(range.head, at) > 0) continue;
-        var word = wordAt(cm, range.head);
-        at = word.from;
-        cm.replaceRange(mod(word.word), word.from, word.to);
-      }
-    });
-  }
-
-  cmds.smartBackspace = function(cm) {
-    if (cm.somethingSelected()) return CodeMirror.Pass;
-
-    cm.operation(function() {
-      var cursors = cm.listSelections();
-      var indentUnit = cm.getOption("indentUnit");
-
-      for (var i = cursors.length - 1; i >= 0; i--) {
-        var cursor = cursors[i].head;
-        var toStartOfLine = cm.getRange({line: cursor.line, ch: 0}, cursor);
-        var column = CodeMirror.countColumn(toStartOfLine, null, cm.getOption("tabSize"));
-
-        // Delete by one character by default
-        var deletePos = cm.findPosH(cursor, -1, "char", false);
-
-        if (toStartOfLine && !/\S/.test(toStartOfLine) && column % indentUnit == 0) {
-          var prevIndent = new Pos(cursor.line,
-            CodeMirror.findColumn(toStartOfLine, column - indentUnit, indentUnit));
-
-          // Smart delete only if we found a valid prevIndent location
-          if (prevIndent.ch != cursor.ch) deletePos = prevIndent;
-        }
-
-        cm.replaceRange("", deletePos, cursor, "+delete");
-      }
-    });
-  };
-
-  cmds.delLineRight = function(cm) {
-    cm.operation(function() {
-      var ranges = cm.listSelections();
-      for (var i = ranges.length - 1; i >= 0; i--)
-        cm.replaceRange("", ranges[i].anchor, Pos(ranges[i].to().line), "+delete");
-      cm.scrollIntoView();
-    });
-  };
-
-  cmds.upcaseAtCursor = function(cm) {
-    modifyWordOrSelection(cm, function(str) { return str.toUpperCase(); });
-  };
-  cmds.downcaseAtCursor = function(cm) {
-    modifyWordOrSelection(cm, function(str) { return str.toLowerCase(); });
-  };
-
-  cmds.setSublimeMark = function(cm) {
-    if (cm.state.sublimeMark) cm.state.sublimeMark.clear();
-    cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
-  };
-  cmds.selectToSublimeMark = function(cm) {
-    var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
-    if (found) cm.setSelection(cm.getCursor(), found);
-  };
-  cmds.deleteToSublimeMark = function(cm) {
-    var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
-    if (found) {
-      var from = cm.getCursor(), to = found;
-      if (CodeMirror.cmpPos(from, to) > 0) { var tmp = to; to = from; from = tmp; }
-      cm.state.sublimeKilled = cm.getRange(from, to);
-      cm.replaceRange("", from, to);
-    }
-  };
-  cmds.swapWithSublimeMark = function(cm) {
-    var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
-    if (found) {
-      cm.state.sublimeMark.clear();
-      cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
-      cm.setCursor(found);
-    }
-  };
-  cmds.sublimeYank = function(cm) {
-    if (cm.state.sublimeKilled != null)
-      cm.replaceSelection(cm.state.sublimeKilled, null, "paste");
-  };
-
-  cmds.showInCenter = function(cm) {
-    var pos = cm.cursorCoords(null, "local");
-    cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2);
-  };
-
-  function getTarget(cm) {
-    var from = cm.getCursor("from"), to = cm.getCursor("to");
-    if (CodeMirror.cmpPos(from, to) == 0) {
-      var word = wordAt(cm, from);
-      if (!word.word) return;
-      from = word.from;
-      to = word.to;
-    }
-    return {from: from, to: to, query: cm.getRange(from, to), word: word};
-  }
-
-  function findAndGoTo(cm, forward) {
-    var target = getTarget(cm);
-    if (!target) return;
-    var query = target.query;
-    var cur = cm.getSearchCursor(query, forward ? target.to : target.from);
-
-    if (forward ? cur.findNext() : cur.findPrevious()) {
-      cm.setSelection(cur.from(), cur.to());
-    } else {
-      cur = cm.getSearchCursor(query, forward ? Pos(cm.firstLine(), 0)
-                                              : cm.clipPos(Pos(cm.lastLine())));
-      if (forward ? cur.findNext() : cur.findPrevious())
-        cm.setSelection(cur.from(), cur.to());
-      else if (target.word)
-        cm.setSelection(target.from, target.to);
-    }
-  };
-  cmds.findUnder = function(cm) { findAndGoTo(cm, true); };
-  cmds.findUnderPrevious = function(cm) { findAndGoTo(cm,false); };
-  cmds.findAllUnder = function(cm) {
-    var target = getTarget(cm);
-    if (!target) return;
-    var cur = cm.getSearchCursor(target.query);
-    var matches = [];
-    var primaryIndex = -1;
-    while (cur.findNext()) {
-      matches.push({anchor: cur.from(), head: cur.to()});
-      if (cur.from().line <= target.from.line && cur.from().ch <= target.from.ch)
-        primaryIndex++;
-    }
-    cm.setSelections(matches, primaryIndex);
-  };
-
-
-  var keyMap = CodeMirror.keyMap;
-  keyMap.macSublime = {
-    "Cmd-Left": "goLineStartSmart",
-    "Shift-Tab": "indentLess",
-    "Shift-Ctrl-K": "deleteLine",
-    "Alt-Q": "wrapLines",
-    "Ctrl-Left": "goSubwordLeft",
-    "Ctrl-Right": "goSubwordRight",
-    "Ctrl-Alt-Up": "scrollLineUp",
-    "Ctrl-Alt-Down": "scrollLineDown",
-    "Cmd-L": "selectLine",
-    "Shift-Cmd-L": "splitSelectionByLine",
-    "Esc": "singleSelectionTop",
-    "Cmd-Enter": "insertLineAfter",
-    "Shift-Cmd-Enter": "insertLineBefore",
-    "Cmd-D": "selectNextOccurrence",
-    "Shift-Cmd-Space": "selectScope",
-    "Shift-Cmd-M": "selectBetweenBrackets",
-    "Cmd-M": "goToBracket",
-    "Cmd-Ctrl-Up": "swapLineUp",
-    "Cmd-Ctrl-Down": "swapLineDown",
-    "Cmd-/": "toggleCommentIndented",
-    "Cmd-J": "joinLines",
-    "Shift-Cmd-D": "duplicateLine",
-    "F5": "sortLines",
-    "Cmd-F5": "sortLinesInsensitive",
-    "F2": "nextBookmark",
-    "Shift-F2": "prevBookmark",
-    "Cmd-F2": "toggleBookmark",
-    "Shift-Cmd-F2": "clearBookmarks",
-    "Alt-F2": "selectBookmarks",
-    "Backspace": "smartBackspace",
-    "Cmd-K Cmd-D": "skipAndSelectNextOccurrence",
-    "Cmd-K Cmd-K": "delLineRight",
-    "Cmd-K Cmd-U": "upcaseAtCursor",
-    "Cmd-K Cmd-L": "downcaseAtCursor",
-    "Cmd-K Cmd-Space": "setSublimeMark",
-    "Cmd-K Cmd-A": "selectToSublimeMark",
-    "Cmd-K Cmd-W": "deleteToSublimeMark",
-    "Cmd-K Cmd-X": "swapWithSublimeMark",
-    "Cmd-K Cmd-Y": "sublimeYank",
-    "Cmd-K Cmd-C": "showInCenter",
-    "Cmd-K Cmd-G": "clearBookmarks",
-    "Cmd-K Cmd-Backspace": "delLineLeft",
-    "Cmd-K Cmd-1": "foldAll",
-    "Cmd-K Cmd-0": "unfoldAll",
-    "Cmd-K Cmd-J": "unfoldAll",
-    "Ctrl-Shift-Up": "addCursorToPrevLine",
-    "Ctrl-Shift-Down": "addCursorToNextLine",
-    "Cmd-F3": "findUnder",
-    "Shift-Cmd-F3": "findUnderPrevious",
-    "Alt-F3": "findAllUnder",
-    "Shift-Cmd-[": "fold",
-    "Shift-Cmd-]": "unfold",
-    "Cmd-I": "findIncremental",
-    "Shift-Cmd-I": "findIncrementalReverse",
-    "Cmd-H": "replace",
-    "F3": "findNext",
-    "Shift-F3": "findPrev",
-    "fallthrough": "macDefault"
-  };
-  CodeMirror.normalizeKeyMap(keyMap.macSublime);
-
-  keyMap.pcSublime = {
-    "Shift-Tab": "indentLess",
-    "Shift-Ctrl-K": "deleteLine",
-    "Alt-Q": "wrapLines",
-    "Ctrl-T": "transposeChars",
-    "Alt-Left": "goSubwordLeft",
-    "Alt-Right": "goSubwordRight",
-    "Ctrl-Up": "scrollLineUp",
-    "Ctrl-Down": "scrollLineDown",
-    "Ctrl-L": "selectLine",
-    "Shift-Ctrl-L": "splitSelectionByLine",
-    "Esc": "singleSelectionTop",
-    "Ctrl-Enter": "insertLineAfter",
-    "Shift-Ctrl-Enter": "insertLineBefore",
-    "Ctrl-D": "selectNextOccurrence",
-    "Shift-Ctrl-Space": "selectScope",
-    "Shift-Ctrl-M": "selectBetweenBrackets",
-    "Ctrl-M": "goToBracket",
-    "Shift-Ctrl-Up": "swapLineUp",
-    "Shift-Ctrl-Down": "swapLineDown",
-    "Ctrl-/": "toggleCommentIndented",
-    "Ctrl-J": "joinLines",
-    "Shift-Ctrl-D": "duplicateLine",
-    "F9": "sortLines",
-    "Ctrl-F9": "sortLinesInsensitive",
-    "F2": "nextBookmark",
-    "Shift-F2": "prevBookmark",
-    "Ctrl-F2": "toggleBookmark",
-    "Shift-Ctrl-F2": "clearBookmarks",
-    "Alt-F2": "selectBookmarks",
-    "Backspace": "smartBackspace",
-    "Ctrl-K Ctrl-D": "skipAndSelectNextOccurrence",
-    "Ctrl-K Ctrl-K": "delLineRight",
-    "Ctrl-K Ctrl-U": "upcaseAtCursor",
-    "Ctrl-K Ctrl-L": "downcaseAtCursor",
-    "Ctrl-K Ctrl-Space": "setSublimeMark",
-    "Ctrl-K Ctrl-A": "selectToSublimeMark",
-    "Ctrl-K Ctrl-W": "deleteToSublimeMark",
-    "Ctrl-K Ctrl-X": "swapWithSublimeMark",
-    "Ctrl-K Ctrl-Y": "sublimeYank",
-    "Ctrl-K Ctrl-C": "showInCenter",
-    "Ctrl-K Ctrl-G": "clearBookmarks",
-    "Ctrl-K Ctrl-Backspace": "delLineLeft",
-    "Ctrl-K Ctrl-1": "foldAll",
-    "Ctrl-K Ctrl-0": "unfoldAll",
-    "Ctrl-K Ctrl-J": "unfoldAll",
-    "Ctrl-Alt-Up": "addCursorToPrevLine",
-    "Ctrl-Alt-Down": "addCursorToNextLine",
-    "Ctrl-F3": "findUnder",
-    "Shift-Ctrl-F3": "findUnderPrevious",
-    "Alt-F3": "findAllUnder",
-    "Shift-Ctrl-[": "fold",
-    "Shift-Ctrl-]": "unfold",
-    "Ctrl-I": "findIncremental",
-    "Shift-Ctrl-I": "findIncrementalReverse",
-    "Ctrl-H": "replace",
-    "F3": "findNext",
-    "Shift-F3": "findPrev",
-    "fallthrough": "pcDefault"
-  };
-  CodeMirror.normalizeKeyMap(keyMap.pcSublime);
-
-  var mac = keyMap.default == keyMap.macDefault;
-  keyMap.sublime = mac ? keyMap.macSublime : keyMap.pcSublime;
-});
-
-
-/* ---- extension/dialog/dialog.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-// Open simple dialogs on top of an editor. Relies on dialog.css.
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  function dialogDiv(cm, template, bottom) {
-    var wrap = cm.getWrapperElement();
-    var dialog;
-    dialog = wrap.appendChild(document.createElement("div"));
-    if (bottom)
-      dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom";
-    else
-      dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";
-
-    if (typeof template == "string") {
-      dialog.innerHTML = template;
-    } else { // Assuming it's a detached DOM element.
-      dialog.appendChild(template);
-    }
-    CodeMirror.addClass(wrap, 'dialog-opened');
-    return dialog;
-  }
-
-  function closeNotification(cm, newVal) {
-    if (cm.state.currentNotificationClose)
-      cm.state.currentNotificationClose();
-    cm.state.currentNotificationClose = newVal;
-  }
-
-  CodeMirror.defineExtension("openDialog", function(template, callback, options) {
-    if (!options) options = {};
-
-    closeNotification(this, null);
-
-    var dialog = dialogDiv(this, template, options.bottom);
-    var closed = false, me = this;
-    function close(newVal) {
-      if (typeof newVal == 'string') {
-        inp.value = newVal;
-      } else {
-        if (closed) return;
-        closed = true;
-        CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
-        dialog.parentNode.removeChild(dialog);
-        me.focus();
-
-        if (options.onClose) options.onClose(dialog);
-      }
-    }
-
-    var inp = dialog.getElementsByTagName("input")[0], button;
-    if (inp) {
-      inp.focus();
-
-      if (options.value) {
-        inp.value = options.value;
-        if (options.selectValueOnOpen !== false) {
-          inp.select();
-        }
-      }
-
-      if (options.onInput)
-        CodeMirror.on(inp, "input", function(e) { options.onInput(e, inp.value, close);});
-      if (options.onKeyUp)
-        CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);});
-
-      CodeMirror.on(inp, "keydown", function(e) {
-        if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }
-        if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) {
-          inp.blur();
-          CodeMirror.e_stop(e);
-          close();
-        }
-        if (e.keyCode == 13) callback(inp.value, e);
-      });
-
-      if (options.closeOnBlur !== false) CodeMirror.on(dialog, "focusout", function (evt) {
-        if (evt.relatedTarget !== null) close();
-      });
-    } else if (button = dialog.getElementsByTagName("button")[0]) {
-      CodeMirror.on(button, "click", function() {
-        close();
-        me.focus();
-      });
-
-      if (options.closeOnBlur !== false) CodeMirror.on(button, "blur", close);
-
-      button.focus();
-    }
-    return close;
-  });
-
-  CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) {
-    closeNotification(this, null);
-    var dialog = dialogDiv(this, template, options && options.bottom);
-    var buttons = dialog.getElementsByTagName("button");
-    var closed = false, me = this, blurring = 1;
-    function close() {
-      if (closed) return;
-      closed = true;
-      CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
-      dialog.parentNode.removeChild(dialog);
-      me.focus();
-    }
-    buttons[0].focus();
-    for (var i = 0; i < buttons.length; ++i) {
-      var b = buttons[i];
-      (function(callback) {
-        CodeMirror.on(b, "click", function(e) {
-          CodeMirror.e_preventDefault(e);
-          close();
-          if (callback) callback(me);
-        });
-      })(callbacks[i]);
-      CodeMirror.on(b, "blur", function() {
-        --blurring;
-        setTimeout(function() { if (blurring <= 0) close(); }, 200);
-      });
-      CodeMirror.on(b, "focus", function() { ++blurring; });
-    }
-  });
-
-  /*
-   * openNotification
-   * Opens a notification, that can be closed with an optional timer
-   * (default 5000ms timer) and always closes on click.
-   *
-   * If a notification is opened while another is opened, it will close the
-   * currently opened one and open the new one immediately.
-   */
-  CodeMirror.defineExtension("openNotification", function(template, options) {
-    closeNotification(this, close);
-    var dialog = dialogDiv(this, template, options && options.bottom);
-    var closed = false, doneTimer;
-    var duration = options && typeof options.duration !== "undefined" ? options.duration : 5000;
-
-    function close() {
-      if (closed) return;
-      closed = true;
-      clearTimeout(doneTimer);
-      CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
-      dialog.parentNode.removeChild(dialog);
-    }
-
-    CodeMirror.on(dialog, 'click', function(e) {
-      CodeMirror.e_preventDefault(e);
-      close();
-    });
-
-    if (duration)
-      doneTimer = setTimeout(close, duration);
-
-    return close;
-  });
-});
-
-
-/* ---- extension/edit/closebrackets.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  var defaults = {
-    pairs: "()[]{}''\"\"",
-    closeBefore: ")]}'\":;>",
-    triples: "",
-    explode: "[]{}"
-  };
-
-  var Pos = CodeMirror.Pos;
-
-  CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
-    if (old && old != CodeMirror.Init) {
-      cm.removeKeyMap(keyMap);
-      cm.state.closeBrackets = null;
-    }
-    if (val) {
-      ensureBound(getOption(val, "pairs"))
-      cm.state.closeBrackets = val;
-      cm.addKeyMap(keyMap);
-    }
-  });
-
-  function getOption(conf, name) {
-    if (name == "pairs" && typeof conf == "string") return conf;
-    if (typeof conf == "object" && conf[name] != null) return conf[name];
-    return defaults[name];
-  }
-
-  var keyMap = {Backspace: handleBackspace, Enter: handleEnter};
-  function ensureBound(chars) {
-    for (var i = 0; i < chars.length; i++) {
-      var ch = chars.charAt(i), key = "'" + ch + "'"
-      if (!keyMap[key]) keyMap[key] = handler(ch)
-    }
-  }
-  ensureBound(defaults.pairs + "`")
-
-  function handler(ch) {
-    return function(cm) { return handleChar(cm, ch); };
-  }
-
-  function getConfig(cm) {
-    var deflt = cm.state.closeBrackets;
-    if (!deflt || deflt.override) return deflt;
-    var mode = cm.getModeAt(cm.getCursor());
-    return mode.closeBrackets || deflt;
-  }
-
-  function handleBackspace(cm) {
-    var conf = getConfig(cm);
-    if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
-
-    var pairs = getOption(conf, "pairs");
-    var ranges = cm.listSelections();
-    for (var i = 0; i < ranges.length; i++) {
-      if (!ranges[i].empty()) return CodeMirror.Pass;
-      var around = charsAround(cm, ranges[i].head);
-      if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
-    }
-    for (var i = ranges.length - 1; i >= 0; i--) {
-      var cur = ranges[i].head;
-      cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1), "+delete");
-    }
-  }
-
-  function handleEnter(cm) {
-    var conf = getConfig(cm);
-    var explode = conf && getOption(conf, "explode");
-    if (!explode || cm.getOption("disableInput")) return CodeMirror.Pass;
-
-    var ranges = cm.listSelections();
-    for (var i = 0; i < ranges.length; i++) {
-      if (!ranges[i].empty()) return CodeMirror.Pass;
-      var around = charsAround(cm, ranges[i].head);
-      if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass;
-    }
-    cm.operation(function() {
-      var linesep = cm.lineSeparator() || "\n";
-      cm.replaceSelection(linesep + linesep, null);
-      cm.execCommand("goCharLeft");
-      ranges = cm.listSelections();
-      for (var i = 0; i < ranges.length; i++) {
-        var line = ranges[i].head.line;
-        cm.indentLine(line, null, true);
-        cm.indentLine(line + 1, null, true);
-      }
-    });
-  }
-
-  function contractSelection(sel) {
-    var inverted = CodeMirror.cmpPos(sel.anchor, sel.head) > 0;
-    return {anchor: new Pos(sel.anchor.line, sel.anchor.ch + (inverted ? -1 : 1)),
-            head: new Pos(sel.head.line, sel.head.ch + (inverted ? 1 : -1))};
-  }
-
-  function handleChar(cm, ch) {
-    var conf = getConfig(cm);
-    if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
-
-    var pairs = getOption(conf, "pairs");
-    var pos = pairs.indexOf(ch);
-    if (pos == -1) return CodeMirror.Pass;
-
-    var closeBefore = getOption(conf,"closeBefore");
-
-    var triples = getOption(conf, "triples");
-
-    var identical = pairs.charAt(pos + 1) == ch;
-    var ranges = cm.listSelections();
-    var opening = pos % 2 == 0;
-
-    var type;
-    for (var i = 0; i < ranges.length; i++) {
-      var range = ranges[i], cur = range.head, curType;
-      var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1));
-      if (opening && !range.empty()) {
-        curType = "surround";
-      } else if ((identical || !opening) && next == ch) {
-        if (identical && stringStartsAfter(cm, cur))
-          curType = "both";
-        else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch)
-          curType = "skipThree";
-        else
-          curType = "skip";
-      } else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
-                 cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch) {
-        if (cur.ch > 2 && /\bstring/.test(cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2)))) return CodeMirror.Pass;
-        curType = "addFour";
-      } else if (identical) {
-        var prev = cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur)
-        if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = "both";
-        else return CodeMirror.Pass;
-      } else if (opening && (next.length === 0 || /\s/.test(next) || closeBefore.indexOf(next) > -1)) {
-        curType = "both";
-      } else {
-        return CodeMirror.Pass;
-      }
-      if (!type) type = curType;
-      else if (type != curType) return CodeMirror.Pass;
-    }
-
-    var left = pos % 2 ? pairs.charAt(pos - 1) : ch;
-    var right = pos % 2 ? ch : pairs.charAt(pos + 1);
-    cm.operation(function() {
-      if (type == "skip") {
-        cm.execCommand("goCharRight");
-      } else if (type == "skipThree") {
-        for (var i = 0; i < 3; i++)
-          cm.execCommand("goCharRight");
-      } else if (type == "surround") {
-        var sels = cm.getSelections();
-        for (var i = 0; i < sels.length; i++)
-          sels[i] = left + sels[i] + right;
-        cm.replaceSelections(sels, "around");
-        sels = cm.listSelections().slice();
-        for (var i = 0; i < sels.length; i++)
-          sels[i] = contractSelection(sels[i]);
-        cm.setSelections(sels);
-      } else if (type == "both") {
-        cm.replaceSelection(left + right, null);
-        cm.triggerElectric(left + right);
-        cm.execCommand("goCharLeft");
-      } else if (type == "addFour") {
-        cm.replaceSelection(left + left + left + left, "before");
-        cm.execCommand("goCharRight");
-      }
-    });
-  }
-
-  function charsAround(cm, pos) {
-    var str = cm.getRange(Pos(pos.line, pos.ch - 1),
-                          Pos(pos.line, pos.ch + 1));
-    return str.length == 2 ? str : null;
-  }
-
-  function stringStartsAfter(cm, pos) {
-    var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1))
-    return /\bstring/.test(token.type) && token.start == pos.ch &&
-      (pos.ch == 0 || !/\bstring/.test(cm.getTokenTypeAt(pos)))
-  }
-});
-
-
-/* ---- extension/edit/closetag.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-/**
- * Tag-closer extension for CodeMirror.
- *
- * This extension adds an "autoCloseTags" option that can be set to
- * either true to get the default behavior, or an object to further
- * configure its behavior.
- *
- * These are supported options:
- *
- * `whenClosing` (default true)
- *   Whether to autoclose when the '/' of a closing tag is typed.
- * `whenOpening` (default true)
- *   Whether to autoclose the tag when the final '>' of an opening
- *   tag is typed.
- * `dontCloseTags` (default is empty tags for HTML, none for XML)
- *   An array of tag names that should not be autoclosed.
- * `indentTags` (default is block tags for HTML, none for XML)
- *   An array of tag names that should, when opened, cause a
- *   blank line to be added inside the tag, and the blank line and
- *   closing line to be indented.
- * `emptyTags` (default is none)
- *   An array of XML tag names that should be autoclosed with '/>'.
- *
- * See demos/closetag.html for a usage example.
- */
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "../fold/xml-fold"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) {
-    if (old != CodeMirror.Init && old)
-      cm.removeKeyMap("autoCloseTags");
-    if (!val) return;
-    var map = {name: "autoCloseTags"};
-    if (typeof val != "object" || val.whenClosing !== false)
-      map["'/'"] = function(cm) { return autoCloseSlash(cm); };
-    if (typeof val != "object" || val.whenOpening !== false)
-      map["'>'"] = function(cm) { return autoCloseGT(cm); };
-    cm.addKeyMap(map);
-  });
-
-  var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
-                       "source", "track", "wbr"];
-  var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4",
-                    "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"];
-
-  function autoCloseGT(cm) {
-    if (cm.getOption("disableInput")) return CodeMirror.Pass;
-    var ranges = cm.listSelections(), replacements = [];
-    var opt = cm.getOption("autoCloseTags");
-    for (var i = 0; i < ranges.length; i++) {
-      if (!ranges[i].empty()) return CodeMirror.Pass;
-      var pos = ranges[i].head, tok = cm.getTokenAt(pos);
-      var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
-      var tagInfo = inner.mode.xmlCurrentTag && inner.mode.xmlCurrentTag(state)
-      var tagName = tagInfo && tagInfo.name
-      if (!tagName) return CodeMirror.Pass
-
-      var html = inner.mode.configuration == "html";
-      var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
-      var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
-
-      if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
-      var lowerTagName = tagName.toLowerCase();
-      // Don't process the '>' at the end of an end-tag or self-closing tag
-      if (!tagName ||
-          tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) ||
-          tok.type == "tag" && tagInfo.close ||
-          tok.string.indexOf("/") == (pos.ch - tok.start - 1) || // match something like <someTagName />
-          dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 ||
-          closingTagExists(cm, inner.mode.xmlCurrentContext && inner.mode.xmlCurrentContext(state) || [], tagName, pos, true))
-        return CodeMirror.Pass;
-
-      var emptyTags = typeof opt == "object" && opt.emptyTags;
-      if (emptyTags && indexOf(emptyTags, tagName) > -1) {
-        replacements[i] = { text: "/>", newPos: CodeMirror.Pos(pos.line, pos.ch + 2) };
-        continue;
-      }
-
-      var indent = indentTags && indexOf(indentTags, lowerTagName) > -1;
-      replacements[i] = {indent: indent,
-                         text: ">" + (indent ? "\n\n" : "") + "</" + tagName + ">",
-                         newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1)};
-    }
-
-    var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnAutoClose);
-    for (var i = ranges.length - 1; i >= 0; i--) {
-      var info = replacements[i];
-      cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, "+insert");
-      var sel = cm.listSelections().slice(0);
-      sel[i] = {head: info.newPos, anchor: info.newPos};
-      cm.setSelections(sel);
-      if (!dontIndentOnAutoClose && info.indent) {
-        cm.indentLine(info.newPos.line, null, true);
-        cm.indentLine(info.newPos.line + 1, null, true);
-      }
-    }
-  }
-
-  function autoCloseCurrent(cm, typingSlash) {
-    var ranges = cm.listSelections(), replacements = [];
-    var head = typingSlash ? "/" : "</";
-    var opt = cm.getOption("autoCloseTags");
-    var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnSlash);
-    for (var i = 0; i < ranges.length; i++) {
-      if (!ranges[i].empty()) return CodeMirror.Pass;
-      var pos = ranges[i].head, tok = cm.getTokenAt(pos);
-      var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
-      if (typingSlash && (tok.type == "string" || tok.string.charAt(0) != "<" ||
-                          tok.start != pos.ch - 1))
-        return CodeMirror.Pass;
-      // Kludge to get around the fact that we are not in XML mode
-      // when completing in JS/CSS snippet in htmlmixed mode. Does not
-      // work for other XML embedded languages (there is no general
-      // way to go from a mixed mode to its current XML state).
-      var replacement, mixed = inner.mode.name != "xml" && cm.getMode().name == "htmlmixed"
-      if (mixed && inner.mode.name == "javascript") {
-        replacement = head + "script";
-      } else if (mixed && inner.mode.name == "css") {
-        replacement = head + "style";
-      } else {
-        var context = inner.mode.xmlCurrentContext && inner.mode.xmlCurrentContext(state)
-        if (!context || (context.length && closingTagExists(cm, context, context[context.length - 1], pos)))
-          return CodeMirror.Pass;
-        replacement = head + context[context.length - 1]
-      }
-      if (cm.getLine(pos.line).charAt(tok.end) != ">") replacement += ">";
-      replacements[i] = replacement;
-    }
-    cm.replaceSelections(replacements);
-    ranges = cm.listSelections();
-    if (!dontIndentOnAutoClose) {
-        for (var i = 0; i < ranges.length; i++)
-            if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
-                cm.indentLine(ranges[i].head.line);
-    }
-  }
-
-  function autoCloseSlash(cm) {
-    if (cm.getOption("disableInput")) return CodeMirror.Pass;
-    return autoCloseCurrent(cm, true);
-  }
-
-  CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); };
-
-  function indexOf(collection, elt) {
-    if (collection.indexOf) return collection.indexOf(elt);
-    for (var i = 0, e = collection.length; i < e; ++i)
-      if (collection[i] == elt) return i;
-    return -1;
-  }
-
-  // If xml-fold is loaded, we use its functionality to try and verify
-  // whether a given tag is actually unclosed.
-  function closingTagExists(cm, context, tagName, pos, newTag) {
-    if (!CodeMirror.scanForClosingTag) return false;
-    var end = Math.min(cm.lastLine() + 1, pos.line + 500);
-    var nextClose = CodeMirror.scanForClosingTag(cm, pos, null, end);
-    if (!nextClose || nextClose.tag != tagName) return false;
-    // If the immediate wrapping context contains onCx instances of
-    // the same tag, a closing tag only exists if there are at least
-    // that many closing tags of that type following.
-    var onCx = newTag ? 1 : 0
-    for (var i = context.length - 1; i >= 0; i--) {
-      if (context[i] == tagName) ++onCx
-      else break
-    }
-    pos = nextClose.to;
-    for (var i = 1; i < onCx; i++) {
-      var next = CodeMirror.scanForClosingTag(cm, pos, null, end);
-      if (!next || next.tag != tagName) return false;
-      pos = next.to;
-    }
-    return true;
-  }
-});
-
-
-/* ---- extension/edit/continuelist.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  var listRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/,
-      emptyListRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/,
-      unorderedListRE = /[*+-]\s/;
-
-  CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
-    if (cm.getOption("disableInput")) return CodeMirror.Pass;
-    var ranges = cm.listSelections(), replacements = [];
-    for (var i = 0; i < ranges.length; i++) {
-      var pos = ranges[i].head;
-
-      // If we're not in Markdown mode, fall back to normal newlineAndIndent
-      var eolState = cm.getStateAfter(pos.line);
-      var inner = CodeMirror.innerMode(cm.getMode(), eolState);
-      if (inner.mode.name !== "markdown") {
-        cm.execCommand("newlineAndIndent");
-        return;
-      } else {
-        eolState = inner.state;
-      }
-
-      var inList = eolState.list !== false;
-      var inQuote = eolState.quote !== 0;
-
-      var line = cm.getLine(pos.line), match = listRE.exec(line);
-      var cursorBeforeBullet = /^\s*$/.test(line.slice(0, pos.ch));
-      if (!ranges[i].empty() || (!inList && !inQuote) || !match || cursorBeforeBullet) {
-        cm.execCommand("newlineAndIndent");
-        return;
-      }
-      if (emptyListRE.test(line)) {
-        var endOfQuote = inQuote && />\s*$/.test(line)
-        var endOfList = !/>\s*$/.test(line)
-        if (endOfQuote || endOfList) cm.replaceRange("", {
-          line: pos.line, ch: 0
-        }, {
-          line: pos.line, ch: pos.ch + 1
-        });
-        replacements[i] = "\n";
-      } else {
-        var indent = match[1], after = match[5];
-        var numbered = !(unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0);
-        var bullet = numbered ? (parseInt(match[3], 10) + 1) + match[4] : match[2].replace("x", " ");
-        replacements[i] = "\n" + indent + bullet + after;
-
-        if (numbered) incrementRemainingMarkdownListNumbers(cm, pos);
-      }
-    }
-
-    cm.replaceSelections(replacements);
-  };
-
-  // Auto-updating Markdown list numbers when a new item is added to the
-  // middle of a list
-  function incrementRemainingMarkdownListNumbers(cm, pos) {
-    var startLine = pos.line, lookAhead = 0, skipCount = 0;
-    var startItem = listRE.exec(cm.getLine(startLine)), startIndent = startItem[1];
-
-    do {
-      lookAhead += 1;
-      var nextLineNumber = startLine + lookAhead;
-      var nextLine = cm.getLine(nextLineNumber), nextItem = listRE.exec(nextLine);
-
-      if (nextItem) {
-        var nextIndent = nextItem[1];
-        var newNumber = (parseInt(startItem[3], 10) + lookAhead - skipCount);
-        var nextNumber = (parseInt(nextItem[3], 10)), itemNumber = nextNumber;
-
-        if (startIndent === nextIndent && !isNaN(nextNumber)) {
-          if (newNumber === nextNumber) itemNumber = nextNumber + 1;
-          if (newNumber > nextNumber) itemNumber = newNumber + 1;
-          cm.replaceRange(
-            nextLine.replace(listRE, nextIndent + itemNumber + nextItem[4] + nextItem[5]),
-          {
-            line: nextLineNumber, ch: 0
-          }, {
-            line: nextLineNumber, ch: nextLine.length
-          });
-        } else {
-          if (startIndent.length > nextIndent.length) return;
-          // This doesn't run if the next line immediatley indents, as it is
-          // not clear of the users intention (new indented item or same level)
-          if ((startIndent.length < nextIndent.length) && (lookAhead === 1)) return;
-          skipCount += 1;
-        }
-      }
-    } while (nextItem);
-  }
-});
-
-
-/* ---- extension/edit/matchbrackets.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
-    (document.documentMode == null || document.documentMode < 8);
-
-  var Pos = CodeMirror.Pos;
-
-  var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<"};
-
-  function bracketRegex(config) {
-    return config && config.bracketRegex || /[(){}[\]]/
-  }
-
-  function findMatchingBracket(cm, where, config) {
-    var line = cm.getLineHandle(where.line), pos = where.ch - 1;
-    var afterCursor = config && config.afterCursor
-    if (afterCursor == null)
-      afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className)
-    var re = bracketRegex(config)
-
-    // A cursor is defined as between two characters, but in in vim command mode
-    // (i.e. not insert mode), the cursor is visually represented as a
-    // highlighted box on top of the 2nd character. Otherwise, we allow matches
-    // from before or after the cursor.
-    var match = (!afterCursor && pos >= 0 && re.test(line.text.charAt(pos)) && matching[line.text.charAt(pos)]) ||
-        re.test(line.text.charAt(pos + 1)) && matching[line.text.charAt(++pos)];
-    if (!match) return null;
-    var dir = match.charAt(1) == ">" ? 1 : -1;
-    if (config && config.strict && (dir > 0) != (pos == where.ch)) return null;
-    var style = cm.getTokenTypeAt(Pos(where.line, pos + 1));
-
-    var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config);
-    if (found == null) return null;
-    return {from: Pos(where.line, pos), to: found && found.pos,
-            match: found && found.ch == match.charAt(0), forward: dir > 0};
-  }
-
-  // bracketRegex is used to specify which type of bracket to scan
-  // should be a regexp, e.g. /[[\]]/
-  //
-  // Note: If "where" is on an open bracket, then this bracket is ignored.
-  //
-  // Returns false when no bracket was found, null when it reached
-  // maxScanLines and gave up
-  function scanForBracket(cm, where, dir, style, config) {
-    var maxScanLen = (config && config.maxScanLineLength) || 10000;
-    var maxScanLines = (config && config.maxScanLines) || 1000;
-
-    var stack = [];
-    var re = bracketRegex(config)
-    var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1)
-                          : Math.max(cm.firstLine() - 1, where.line - maxScanLines);
-    for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) {
-      var line = cm.getLine(lineNo);
-      if (!line) continue;
-      var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1;
-      if (line.length > maxScanLen) continue;
-      if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0);
-      for (; pos != end; pos += dir) {
-        var ch = line.charAt(pos);
-        if (re.test(ch) && (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) {
-          var match = matching[ch];
-          if (match && (match.charAt(1) == ">") == (dir > 0)) stack.push(ch);
-          else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch};
-          else stack.pop();
-        }
-      }
-    }
-    return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null;
-  }
-
-  function matchBrackets(cm, autoclear, config) {
-    // Disable brace matching in long lines, since it'll cause hugely slow updates
-    var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000;
-    var marks = [], ranges = cm.listSelections();
-    for (var i = 0; i < ranges.length; i++) {
-      var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config);
-      if (match && cm.getLine(match.from.line).length <= maxHighlightLen) {
-        var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
-        marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style}));
-        if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen)
-          marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style}));
-      }
-    }
-
-    if (marks.length) {
-      // Kludge to work around the IE bug from issue #1193, where text
-      // input stops going to the textare whever this fires.
-      if (ie_lt8 && cm.state.focused) cm.focus();
-
-      var clear = function() {
-        cm.operation(function() {
-          for (var i = 0; i < marks.length; i++) marks[i].clear();
-        });
-      };
-      if (autoclear) setTimeout(clear, 800);
-      else return clear;
-    }
-  }
-
-  function doMatchBrackets(cm) {
-    cm.operation(function() {
-      if (cm.state.matchBrackets.currentlyHighlighted) {
-        cm.state.matchBrackets.currentlyHighlighted();
-        cm.state.matchBrackets.currentlyHighlighted = null;
-      }
-      cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
-    });
-  }
-
-  CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
-    function clear(cm) {
-      if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) {
-        cm.state.matchBrackets.currentlyHighlighted();
-        cm.state.matchBrackets.currentlyHighlighted = null;
-      }
-    }
-
-    if (old && old != CodeMirror.Init) {
-      cm.off("cursorActivity", doMatchBrackets);
-      cm.off("focus", doMatchBrackets)
-      cm.off("blur", clear)
-      clear(cm);
-    }
-    if (val) {
-      cm.state.matchBrackets = typeof val == "object" ? val : {};
-      cm.on("cursorActivity", doMatchBrackets);
-      cm.on("focus", doMatchBrackets)
-      cm.on("blur", clear)
-    }
-  });
-
-  CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
-  CodeMirror.defineExtension("findMatchingBracket", function(pos, config, oldConfig){
-    // Backwards-compatibility kludge
-    if (oldConfig || typeof config == "boolean") {
-      if (!oldConfig) {
-        config = config ? {strict: true} : null
-      } else {
-        oldConfig.strict = config
-        config = oldConfig
-      }
-    }
-    return findMatchingBracket(this, pos, config)
-  });
-  CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){
-    return scanForBracket(this, pos, dir, style, config);
-  });
-});
-
-
-/* ---- extension/edit/matchtags.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "../fold/xml-fold"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  CodeMirror.defineOption("matchTags", false, function(cm, val, old) {
-    if (old && old != CodeMirror.Init) {
-      cm.off("cursorActivity", doMatchTags);
-      cm.off("viewportChange", maybeUpdateMatch);
-      clear(cm);
-    }
-    if (val) {
-      cm.state.matchBothTags = typeof val == "object" && val.bothTags;
-      cm.on("cursorActivity", doMatchTags);
-      cm.on("viewportChange", maybeUpdateMatch);
-      doMatchTags(cm);
-    }
-  });
-
-  function clear(cm) {
-    if (cm.state.tagHit) cm.state.tagHit.clear();
-    if (cm.state.tagOther) cm.state.tagOther.clear();
-    cm.state.tagHit = cm.state.tagOther = null;
-  }
-
-  function doMatchTags(cm) {
-    cm.state.failedTagMatch = false;
-    cm.operation(function() {
-      clear(cm);
-      if (cm.somethingSelected()) return;
-      var cur = cm.getCursor(), range = cm.getViewport();
-      range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to);
-      var match = CodeMirror.findMatchingTag(cm, cur, range);
-      if (!match) return;
-      if (cm.state.matchBothTags) {
-        var hit = match.at == "open" ? match.open : match.close;
-        if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"});
-      }
-      var other = match.at == "close" ? match.open : match.close;
-      if (other)
-        cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"});
-      else
-        cm.state.failedTagMatch = true;
-    });
-  }
-
-  function maybeUpdateMatch(cm) {
-    if (cm.state.failedTagMatch) doMatchTags(cm);
-  }
-
-  CodeMirror.commands.toMatchingTag = function(cm) {
-    var found = CodeMirror.findMatchingTag(cm, cm.getCursor());
-    if (found) {
-      var other = found.at == "close" ? found.open : found.close;
-      if (other) cm.extendSelection(other.to, other.from);
-    }
-  };
-});
-
-
-/* ---- extension/edit/trailingspace.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) {
-    if (prev == CodeMirror.Init) prev = false;
-    if (prev && !val)
-      cm.removeOverlay("trailingspace");
-    else if (!prev && val)
-      cm.addOverlay({
-        token: function(stream) {
-          for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {}
-          if (i > stream.pos) { stream.pos = i; return null; }
-          stream.pos = l;
-          return "trailingspace";
-        },
-        name: "trailingspace"
-      });
-  });
-});
-
-
-/* ---- extension/fold/brace-fold.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-CodeMirror.registerHelper("fold", "brace", function(cm, start) {
-  var line = start.line, lineText = cm.getLine(line);
-  var tokenType;
-
-  function findOpening(openCh) {
-    for (var at = start.ch, pass = 0;;) {
-      var found = at <= 0 ? -1 : lineText.lastIndexOf(openCh, at - 1);
-      if (found == -1) {
-        if (pass == 1) break;
-        pass = 1;
-        at = lineText.length;
-        continue;
-      }
-      if (pass == 1 && found < start.ch) break;
-      tokenType = cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1));
-      if (!/^(comment|string)/.test(tokenType)) return found + 1;
-      at = found - 1;
-    }
-  }
-
-  var startToken = "{", endToken = "}", startCh = findOpening("{");
-  if (startCh == null) {
-    startToken = "[", endToken = "]";
-    startCh = findOpening("[");
-  }
-
-  if (startCh == null) return;
-  var count = 1, lastLine = cm.lastLine(), end, endCh;
-  outer: for (var i = line; i <= lastLine; ++i) {
-    var text = cm.getLine(i), pos = i == line ? startCh : 0;
-    for (;;) {
-      var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);
-      if (nextOpen < 0) nextOpen = text.length;
-      if (nextClose < 0) nextClose = text.length;
-      pos = Math.min(nextOpen, nextClose);
-      if (pos == text.length) break;
-      if (cm.getTokenTypeAt(CodeMirror.Pos(i, pos + 1)) == tokenType) {
-        if (pos == nextOpen) ++count;
-        else if (!--count) { end = i; endCh = pos; break outer; }
-      }
-      ++pos;
-    }
-  }
-  if (end == null || line == end) return;
-  return {from: CodeMirror.Pos(line, startCh),
-          to: CodeMirror.Pos(end, endCh)};
-});
-
-CodeMirror.registerHelper("fold", "import", function(cm, start) {
-  function hasImport(line) {
-    if (line < cm.firstLine() || line > cm.lastLine()) return null;
-    var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
-    if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
-    if (start.type != "keyword" || start.string != "import") return null;
-    // Now find closing semicolon, return its position
-    for (var i = line, e = Math.min(cm.lastLine(), line + 10); i <= e; ++i) {
-      var text = cm.getLine(i), semi = text.indexOf(";");
-      if (semi != -1) return {startCh: start.end, end: CodeMirror.Pos(i, semi)};
-    }
-  }
-
-  var startLine = start.line, has = hasImport(startLine), prev;
-  if (!has || hasImport(startLine - 1) || ((prev = hasImport(startLine - 2)) && prev.end.line == startLine - 1))
-    return null;
-  for (var end = has.end;;) {
-    var next = hasImport(end.line + 1);
-    if (next == null) break;
-    end = next.end;
-  }
-  return {from: cm.clipPos(CodeMirror.Pos(startLine, has.startCh + 1)), to: end};
-});
-
-CodeMirror.registerHelper("fold", "include", function(cm, start) {
-  function hasInclude(line) {
-    if (line < cm.firstLine() || line > cm.lastLine()) return null;
-    var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
-    if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
-    if (start.type == "meta" && start.string.slice(0, 8) == "#include") return start.start + 8;
-  }
-
-  var startLine = start.line, has = hasInclude(startLine);
-  if (has == null || hasInclude(startLine - 1) != null) return null;
-  for (var end = startLine;;) {
-    var next = hasInclude(end + 1);
-    if (next == null) break;
-    ++end;
-  }
-  return {from: CodeMirror.Pos(startLine, has + 1),
-          to: cm.clipPos(CodeMirror.Pos(end))};
-});
-
-});
-
-
-/* ---- extension/fold/comment-fold.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-CodeMirror.registerGlobalHelper("fold", "comment", function(mode) {
-  return mode.blockCommentStart && mode.blockCommentEnd;
-}, function(cm, start) {
-  var mode = cm.getModeAt(start), startToken = mode.blockCommentStart, endToken = mode.blockCommentEnd;
-  if (!startToken || !endToken) return;
-  var line = start.line, lineText = cm.getLine(line);
-
-  var startCh;
-  for (var at = start.ch, pass = 0;;) {
-    var found = at <= 0 ? -1 : lineText.lastIndexOf(startToken, at - 1);
-    if (found == -1) {
-      if (pass == 1) return;
-      pass = 1;
-      at = lineText.length;
-      continue;
-    }
-    if (pass == 1 && found < start.ch) return;
-    if (/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1))) &&
-        (found == 0 || lineText.slice(found - endToken.length, found) == endToken ||
-         !/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found))))) {
-      startCh = found + startToken.length;
-      break;
-    }
-    at = found - 1;
-  }
-
-  var depth = 1, lastLine = cm.lastLine(), end, endCh;
-  outer: for (var i = line; i <= lastLine; ++i) {
-    var text = cm.getLine(i), pos = i == line ? startCh : 0;
-    for (;;) {
-      var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);
-      if (nextOpen < 0) nextOpen = text.length;
-      if (nextClose < 0) nextClose = text.length;
-      pos = Math.min(nextOpen, nextClose);
-      if (pos == text.length) break;
-      if (pos == nextOpen) ++depth;
-      else if (!--depth) { end = i; endCh = pos; break outer; }
-      ++pos;
-    }
-  }
-  if (end == null || line == end && endCh == startCh) return;
-  return {from: CodeMirror.Pos(line, startCh),
-          to: CodeMirror.Pos(end, endCh)};
-});
-
-});
-
-
-/* ---- extension/fold/foldcode.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  function doFold(cm, pos, options, force) {
-    if (options && options.call) {
-      var finder = options;
-      options = null;
-    } else {
-      var finder = getOption(cm, options, "rangeFinder");
-    }
-    if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0);
-    var minSize = getOption(cm, options, "minFoldSize");
-
-    function getRange(allowFolded) {
-      var range = finder(cm, pos);
-      if (!range || range.to.line - range.from.line < minSize) return null;
-      var marks = cm.findMarksAt(range.from);
-      for (var i = 0; i < marks.length; ++i) {
-        if (marks[i].__isFold && force !== "fold") {
-          if (!allowFolded) return null;
-          range.cleared = true;
-          marks[i].clear();
-        }
-      }
-      return range;
-    }
-
-    var range = getRange(true);
-    if (getOption(cm, options, "scanUp")) while (!range && pos.line > cm.firstLine()) {
-      pos = CodeMirror.Pos(pos.line - 1, 0);
-      range = getRange(false);
-    }
-    if (!range || range.cleared || force === "unfold") return;
-
-    var myWidget = makeWidget(cm, options, range);
-    CodeMirror.on(myWidget, "mousedown", function(e) {
-      myRange.clear();
-      CodeMirror.e_preventDefault(e);
-    });
-    var myRange = cm.markText(range.from, range.to, {
-      replacedWith: myWidget,
-      clearOnEnter: getOption(cm, options, "clearOnEnter"),
-      __isFold: true
-    });
-    myRange.on("clear", function(from, to) {
-      CodeMirror.signal(cm, "unfold", cm, from, to);
-    });
-    CodeMirror.signal(cm, "fold", cm, range.from, range.to);
-  }
-
-  function makeWidget(cm, options, range) {
-    var widget = getOption(cm, options, "widget");
-
-    if (typeof widget == "function") {
-      widget = widget(range.from, range.to);
-    }
-
-    if (typeof widget == "string") {
-      var text = document.createTextNode(widget);
-      widget = document.createElement("span");
-      widget.appendChild(text);
-      widget.className = "CodeMirror-foldmarker";
-    } else if (widget) {
-      widget = widget.cloneNode(true)
-    }
-    return widget;
-  }
-
-  // Clumsy backwards-compatible interface
-  CodeMirror.newFoldFunction = function(rangeFinder, widget) {
-    return function(cm, pos) { doFold(cm, pos, {rangeFinder: rangeFinder, widget: widget}); };
-  };
-
-  // New-style interface
-  CodeMirror.defineExtension("foldCode", function(pos, options, force) {
-    doFold(this, pos, options, force);
-  });
-
-  CodeMirror.defineExtension("isFolded", function(pos) {
-    var marks = this.findMarksAt(pos);
-    for (var i = 0; i < marks.length; ++i)
-      if (marks[i].__isFold) return true;
-  });
-
-  CodeMirror.commands.toggleFold = function(cm) {
-    cm.foldCode(cm.getCursor());
-  };
-  CodeMirror.commands.fold = function(cm) {
-    cm.foldCode(cm.getCursor(), null, "fold");
-  };
-  CodeMirror.commands.unfold = function(cm) {
-    cm.foldCode(cm.getCursor(), null, "unfold");
-  };
-  CodeMirror.commands.foldAll = function(cm) {
-    cm.operation(function() {
-      for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)
-        cm.foldCode(CodeMirror.Pos(i, 0), null, "fold");
-    });
-  };
-  CodeMirror.commands.unfoldAll = function(cm) {
-    cm.operation(function() {
-      for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)
-        cm.foldCode(CodeMirror.Pos(i, 0), null, "unfold");
-    });
-  };
-
-  CodeMirror.registerHelper("fold", "combine", function() {
-    var funcs = Array.prototype.slice.call(arguments, 0);
-    return function(cm, start) {
-      for (var i = 0; i < funcs.length; ++i) {
-        var found = funcs[i](cm, start);
-        if (found) return found;
-      }
-    };
-  });
-
-  CodeMirror.registerHelper("fold", "auto", function(cm, start) {
-    var helpers = cm.getHelpers(start, "fold");
-    for (var i = 0; i < helpers.length; i++) {
-      var cur = helpers[i](cm, start);
-      if (cur) return cur;
-    }
-  });
-
-  var defaultOptions = {
-    rangeFinder: CodeMirror.fold.auto,
-    widget: "\u2194",
-    minFoldSize: 0,
-    scanUp: false,
-    clearOnEnter: true
-  };
-
-  CodeMirror.defineOption("foldOptions", null);
-
-  function getOption(cm, options, name) {
-    if (options && options[name] !== undefined)
-      return options[name];
-    var editorOptions = cm.options.foldOptions;
-    if (editorOptions && editorOptions[name] !== undefined)
-      return editorOptions[name];
-    return defaultOptions[name];
-  }
-
-  CodeMirror.defineExtension("foldOption", function(options, name) {
-    return getOption(this, options, name);
-  });
-});
-
-
-/* ---- extension/fold/foldgutter.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("./foldcode"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "./foldcode"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  CodeMirror.defineOption("foldGutter", false, function(cm, val, old) {
-    if (old && old != CodeMirror.Init) {
-      cm.clearGutter(cm.state.foldGutter.options.gutter);
-      cm.state.foldGutter = null;
-      cm.off("gutterClick", onGutterClick);
-      cm.off("changes", onChange);
-      cm.off("viewportChange", onViewportChange);
-      cm.off("fold", onFold);
-      cm.off("unfold", onFold);
-      cm.off("swapDoc", onChange);
-    }
-    if (val) {
-      cm.state.foldGutter = new State(parseOptions(val));
-      updateInViewport(cm);
-      cm.on("gutterClick", onGutterClick);
-      cm.on("changes", onChange);
-      cm.on("viewportChange", onViewportChange);
-      cm.on("fold", onFold);
-      cm.on("unfold", onFold);
-      cm.on("swapDoc", onChange);
-    }
-  });
-
-  var Pos = CodeMirror.Pos;
-
-  function State(options) {
-    this.options = options;
-    this.from = this.to = 0;
-  }
-
-  function parseOptions(opts) {
-    if (opts === true) opts = {};
-    if (opts.gutter == null) opts.gutter = "CodeMirror-foldgutter";
-    if (opts.indicatorOpen == null) opts.indicatorOpen = "CodeMirror-foldgutter-open";
-    if (opts.indicatorFolded == null) opts.indicatorFolded = "CodeMirror-foldgutter-folded";
-    return opts;
-  }
-
-  function isFolded(cm, line) {
-    var marks = cm.findMarks(Pos(line, 0), Pos(line + 1, 0));
-    for (var i = 0; i < marks.length; ++i) {
-      if (marks[i].__isFold) {
-        var fromPos = marks[i].find(-1);
-        if (fromPos && fromPos.line === line)
-          return marks[i];
-      }
-    }
-  }
-
-  function marker(spec) {
-    if (typeof spec == "string") {
-      var elt = document.createElement("div");
-      elt.className = spec + " CodeMirror-guttermarker-subtle";
-      return elt;
-    } else {
-      return spec.cloneNode(true);
-    }
-  }
-
-  function updateFoldInfo(cm, from, to) {
-    var opts = cm.state.foldGutter.options, cur = from - 1;
-    var minSize = cm.foldOption(opts, "minFoldSize");
-    var func = cm.foldOption(opts, "rangeFinder");
-    // we can reuse the built-in indicator element if its className matches the new state
-    var clsFolded = typeof opts.indicatorFolded == "string" && classTest(opts.indicatorFolded);
-    var clsOpen = typeof opts.indicatorOpen == "string" && classTest(opts.indicatorOpen);
-    cm.eachLine(from, to, function(line) {
-      ++cur;
-      var mark = null;
-      var old = line.gutterMarkers;
-      if (old) old = old[opts.gutter];
-      if (isFolded(cm, cur)) {
-        if (clsFolded && old && clsFolded.test(old.className)) return;
-        mark = marker(opts.indicatorFolded);
-      } else {
-        var pos = Pos(cur, 0);
-        var range = func && func(cm, pos);
-        if (range && range.to.line - range.from.line >= minSize) {
-          if (clsOpen && old && clsOpen.test(old.className)) return;
-          mark = marker(opts.indicatorOpen);
-        }
-      }
-      if (!mark && !old) return;
-      cm.setGutterMarker(line, opts.gutter, mark);
-    });
-  }
-
-  // copied from CodeMirror/src/util/dom.js
-  function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
-
-  function updateInViewport(cm) {
-    var vp = cm.getViewport(), state = cm.state.foldGutter;
-    if (!state) return;
-    cm.operation(function() {
-      updateFoldInfo(cm, vp.from, vp.to);
-    });
-    state.from = vp.from; state.to = vp.to;
-  }
-
-  function onGutterClick(cm, line, gutter) {
-    var state = cm.state.foldGutter;
-    if (!state) return;
-    var opts = state.options;
-    if (gutter != opts.gutter) return;
-    var folded = isFolded(cm, line);
-    if (folded) folded.clear();
-    else cm.foldCode(Pos(line, 0), opts);
-  }
-
-  function onChange(cm) {
-    var state = cm.state.foldGutter;
-    if (!state) return;
-    var opts = state.options;
-    state.from = state.to = 0;
-    clearTimeout(state.changeUpdate);
-    state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, opts.foldOnChangeTimeSpan || 600);
-  }
-
-  function onViewportChange(cm) {
-    var state = cm.state.foldGutter;
-    if (!state) return;
-    var opts = state.options;
-    clearTimeout(state.changeUpdate);
-    state.changeUpdate = setTimeout(function() {
-      var vp = cm.getViewport();
-      if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {
-        updateInViewport(cm);
-      } else {
-        cm.operation(function() {
-          if (vp.from < state.from) {
-            updateFoldInfo(cm, vp.from, state.from);
-            state.from = vp.from;
-          }
-          if (vp.to > state.to) {
-            updateFoldInfo(cm, state.to, vp.to);
-            state.to = vp.to;
-          }
-        });
-      }
-    }, opts.updateViewportTimeSpan || 400);
-  }
-
-  function onFold(cm, from) {
-    var state = cm.state.foldGutter;
-    if (!state) return;
-    var line = from.line;
-    if (line >= state.from && line < state.to)
-      updateFoldInfo(cm, line, line + 1);
-  }
-});
-
-
-/* ---- extension/fold/indent-fold.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-function lineIndent(cm, lineNo) {
-  var text = cm.getLine(lineNo)
-  var spaceTo = text.search(/\S/)
-  if (spaceTo == -1 || /\bcomment\b/.test(cm.getTokenTypeAt(CodeMirror.Pos(lineNo, spaceTo + 1))))
-    return -1
-  return CodeMirror.countColumn(text, null, cm.getOption("tabSize"))
-}
-
-CodeMirror.registerHelper("fold", "indent", function(cm, start) {
-  var myIndent = lineIndent(cm, start.line)
-  if (myIndent < 0) return
-  var lastLineInFold = null
-
-  // Go through lines until we find a line that definitely doesn't belong in
-  // the block we're folding, or to the end.
-  for (var i = start.line + 1, end = cm.lastLine(); i <= end; ++i) {
-    var indent = lineIndent(cm, i)
-    if (indent == -1) {
-    } else if (indent > myIndent) {
-      // Lines with a greater indent are considered part of the block.
-      lastLineInFold = i;
-    } else {
-      // If this line has non-space, non-comment content, and is
-      // indented less or equal to the start line, it is the start of
-      // another block.
-      break;
-    }
-  }
-  if (lastLineInFold) return {
-    from: CodeMirror.Pos(start.line, cm.getLine(start.line).length),
-    to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length)
-  };
-});
-
-});
-
-
-/* ---- extension/fold/markdown-fold.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-CodeMirror.registerHelper("fold", "markdown", function(cm, start) {
-  var maxDepth = 100;
-
-  function isHeader(lineNo) {
-    var tokentype = cm.getTokenTypeAt(CodeMirror.Pos(lineNo, 0));
-    return tokentype && /\bheader\b/.test(tokentype);
-  }
-
-  function headerLevel(lineNo, line, nextLine) {
-    var match = line && line.match(/^#+/);
-    if (match && isHeader(lineNo)) return match[0].length;
-    match = nextLine && nextLine.match(/^[=\-]+\s*$/);
-    if (match && isHeader(lineNo + 1)) return nextLine[0] == "=" ? 1 : 2;
-    return maxDepth;
-  }
-
-  var firstLine = cm.getLine(start.line), nextLine = cm.getLine(start.line + 1);
-  var level = headerLevel(start.line, firstLine, nextLine);
-  if (level === maxDepth) return undefined;
-
-  var lastLineNo = cm.lastLine();
-  var end = start.line, nextNextLine = cm.getLine(end + 2);
-  while (end < lastLineNo) {
-    if (headerLevel(end + 1, nextLine, nextNextLine) <= level) break;
-    ++end;
-    nextLine = nextNextLine;
-    nextNextLine = cm.getLine(end + 2);
-  }
-
-  return {
-    from: CodeMirror.Pos(start.line, firstLine.length),
-    to: CodeMirror.Pos(end, cm.getLine(end).length)
-  };
-});
-
-});
-
-
-/* ---- extension/fold/xml-fold.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  var Pos = CodeMirror.Pos;
-  function cmp(a, b) { return a.line - b.line || a.ch - b.ch; }
-
-  var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
-  var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
-  var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g");
-
-  function Iter(cm, line, ch, range) {
-    this.line = line; this.ch = ch;
-    this.cm = cm; this.text = cm.getLine(line);
-    this.min = range ? Math.max(range.from, cm.firstLine()) : cm.firstLine();
-    this.max = range ? Math.min(range.to - 1, cm.lastLine()) : cm.lastLine();
-  }
-
-  function tagAt(iter, ch) {
-    var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch));
-    return type && /\btag\b/.test(type);
-  }
-
-  function nextLine(iter) {
-    if (iter.line >= iter.max) return;
-    iter.ch = 0;
-    iter.text = iter.cm.getLine(++iter.line);
-    return true;
-  }
-  function prevLine(iter) {
-    if (iter.line <= iter.min) return;
-    iter.text = iter.cm.getLine(--iter.line);
-    iter.ch = iter.text.length;
-    return true;
-  }
-
-  function toTagEnd(iter) {
-    for (;;) {
-      var gt = iter.text.indexOf(">", iter.ch);
-      if (gt == -1) { if (nextLine(iter)) continue; else return; }
-      if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; }
-      var lastSlash = iter.text.lastIndexOf("/", gt);
-      var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
-      iter.ch = gt + 1;
-      return selfClose ? "selfClose" : "regular";
-    }
-  }
-  function toTagStart(iter) {
-    for (;;) {
-      var lt = iter.ch ? iter.text.lastIndexOf("<", iter.ch - 1) : -1;
-      if (lt == -1) { if (prevLine(iter)) continue; else return; }
-      if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; }
-      xmlTagStart.lastIndex = lt;
-      iter.ch = lt;
-      var match = xmlTagStart.exec(iter.text);
-      if (match && match.index == lt) return match;
-    }
-  }
-
-  function toNextTag(iter) {
-    for (;;) {
-      xmlTagStart.lastIndex = iter.ch;
-      var found = xmlTagStart.exec(iter.text);
-      if (!found) { if (nextLine(iter)) continue; else return; }
-      if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; }
-      iter.ch = found.index + found[0].length;
-      return found;
-    }
-  }
-  function toPrevTag(iter) {
-    for (;;) {
-      var gt = iter.ch ? iter.text.lastIndexOf(">", iter.ch - 1) : -1;
-      if (gt == -1) { if (prevLine(iter)) continue; else return; }
-      if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; }
-      var lastSlash = iter.text.lastIndexOf("/", gt);
-      var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
-      iter.ch = gt + 1;
-      return selfClose ? "selfClose" : "regular";
-    }
-  }
-
-  function findMatchingClose(iter, tag) {
-    var stack = [];
-    for (;;) {
-      var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0);
-      if (!next || !(end = toTagEnd(iter))) return;
-      if (end == "selfClose") continue;
-      if (next[1]) { // closing tag
-        for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {
-          stack.length = i;
-          break;
-        }
-        if (i < 0 && (!tag || tag == next[2])) return {
-          tag: next[2],
-          from: Pos(startLine, startCh),
-          to: Pos(iter.line, iter.ch)
-        };
-      } else { // opening tag
-        stack.push(next[2]);
-      }
-    }
-  }
-  function findMatchingOpen(iter, tag) {
-    var stack = [];
-    for (;;) {
-      var prev = toPrevTag(iter);
-      if (!prev) return;
-      if (prev == "selfClose") { toTagStart(iter); continue; }
-      var endLine = iter.line, endCh = iter.ch;
-      var start = toTagStart(iter);
-      if (!start) return;
-      if (start[1]) { // closing tag
-        stack.push(start[2]);
-      } else { // opening tag
-        for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) {
-          stack.length = i;
-          break;
-        }
-        if (i < 0 && (!tag || tag == start[2])) return {
-          tag: start[2],
-          from: Pos(iter.line, iter.ch),
-          to: Pos(endLine, endCh)
-        };
-      }
-    }
-  }
-
-  CodeMirror.registerHelper("fold", "xml", function(cm, start) {
-    var iter = new Iter(cm, start.line, 0);
-    for (;;) {
-      var openTag = toNextTag(iter)
-      if (!openTag || iter.line != start.line) return
-      var end = toTagEnd(iter)
-      if (!end) return
-      if (!openTag[1] && end != "selfClose") {
-        var startPos = Pos(iter.line, iter.ch);
-        var endPos = findMatchingClose(iter, openTag[2]);
-        return endPos && cmp(endPos.from, startPos) > 0 ? {from: startPos, to: endPos.from} : null
-      }
-    }
-  });
-  CodeMirror.findMatchingTag = function(cm, pos, range) {
-    var iter = new Iter(cm, pos.line, pos.ch, range);
-    if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return;
-    var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch);
-    var start = end && toTagStart(iter);
-    if (!end || !start || cmp(iter, pos) > 0) return;
-    var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]};
-    if (end == "selfClose") return {open: here, close: null, at: "open"};
-
-    if (start[1]) { // closing tag
-      return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"};
-    } else { // opening tag
-      iter = new Iter(cm, to.line, to.ch, range);
-      return {open: here, close: findMatchingClose(iter, start[2]), at: "open"};
-    }
-  };
-
-  CodeMirror.findEnclosingTag = function(cm, pos, range, tag) {
-    var iter = new Iter(cm, pos.line, pos.ch, range);
-    for (;;) {
-      var open = findMatchingOpen(iter, tag);
-      if (!open) break;
-      var forward = new Iter(cm, pos.line, pos.ch, range);
-      var close = findMatchingClose(forward, open.tag);
-      if (close) return {open: open, close: close};
-    }
-  };
-
-  // Used by addon/edit/closetag.js
-  CodeMirror.scanForClosingTag = function(cm, pos, name, end) {
-    var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null);
-    return findMatchingClose(iter, name);
-  };
-});
-
-
-/* ---- extension/hint/anyword-hint.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  var WORD = /[\w$]+/, RANGE = 500;
-
-  CodeMirror.registerHelper("hint", "anyword", function(editor, options) {
-    var word = options && options.word || WORD;
-    var range = options && options.range || RANGE;
-    var cur = editor.getCursor(), curLine = editor.getLine(cur.line);
-    var end = cur.ch, start = end;
-    while (start && word.test(curLine.charAt(start - 1))) --start;
-    var curWord = start != end && curLine.slice(start, end);
-
-    var list = options && options.list || [], seen = {};
-    var re = new RegExp(word.source, "g");
-    for (var dir = -1; dir <= 1; dir += 2) {
-      var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir;
-      for (; line != endLine; line += dir) {
-        var text = editor.getLine(line), m;
-        while (m = re.exec(text)) {
-          if (line == cur.line && m[0] === curWord) continue;
-          if ((!curWord || m[0].lastIndexOf(curWord, 0) == 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) {
-            seen[m[0]] = true;
-            list.push(m[0]);
-          }
-        }
-      }
-    }
-    return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)};
-  });
-});
-
-
-/* ---- extension/hint/html-hint.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("./xml-hint"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "./xml-hint"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  var langs = "ab aa af ak sq am ar an hy as av ae ay az bm ba eu be bn bh bi bs br bg my ca ch ce ny zh cv kw co cr hr cs da dv nl dz en eo et ee fo fj fi fr ff gl ka de el gn gu ht ha he hz hi ho hu ia id ie ga ig ik io is it iu ja jv kl kn kr ks kk km ki rw ky kv kg ko ku kj la lb lg li ln lo lt lu lv gv mk mg ms ml mt mi mr mh mn na nv nb nd ne ng nn no ii nr oc oj cu om or os pa pi fa pl ps pt qu rm rn ro ru sa sc sd se sm sg sr gd sn si sk sl so st es su sw ss sv ta te tg th ti bo tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa cy wo fy xh yi yo za zu".split(" ");
-  var targets = ["_blank", "_self", "_top", "_parent"];
-  var charsets = ["ascii", "utf-8", "utf-16", "latin1", "latin1"];
-  var methods = ["get", "post", "put", "delete"];
-  var encs = ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"];
-  var media = ["all", "screen", "print", "embossed", "braille", "handheld", "print", "projection", "screen", "tty", "tv", "speech",
-               "3d-glasses", "resolution [>][<][=] [X]", "device-aspect-ratio: X/Y", "orientation:portrait",
-               "orientation:landscape", "device-height: [X]", "device-width: [X]"];
-  var s = { attrs: {} }; // Simple tag, reused for a whole lot of tags
-
-  var data = {
-    a: {
-      attrs: {
-        href: null, ping: null, type: null,
-        media: media,
-        target: targets,
-        hreflang: langs
-      }
-    },
-    abbr: s,
-    acronym: s,
-    address: s,
-    applet: s,
-    area: {
-      attrs: {
-        alt: null, coords: null, href: null, target: null, ping: null,
-        media: media, hreflang: langs, type: null,
-        shape: ["default", "rect", "circle", "poly"]
-      }
-    },
-    article: s,
-    aside: s,
-    audio: {
-      attrs: {
-        src: null, mediagroup: null,
-        crossorigin: ["anonymous", "use-credentials"],
-        preload: ["none", "metadata", "auto"],
-        autoplay: ["", "autoplay"],
-        loop: ["", "loop"],
-        controls: ["", "controls"]
-      }
-    },
-    b: s,
-    base: { attrs: { href: null, target: targets } },
-    basefont: s,
-    bdi: s,
-    bdo: s,
-    big: s,
-    blockquote: { attrs: { cite: null } },
-    body: s,
-    br: s,
-    button: {
-      attrs: {
-        form: null, formaction: null, name: null, value: null,
-        autofocus: ["", "autofocus"],
-        disabled: ["", "autofocus"],
-        formenctype: encs,
-        formmethod: methods,
-        formnovalidate: ["", "novalidate"],
-        formtarget: targets,
-        type: ["submit", "reset", "button"]
-      }
-    },
-    canvas: { attrs: { width: null, height: null } },
-    caption: s,
-    center: s,
-    cite: s,
-    code: s,
-    col: { attrs: { span: null } },
-    colgroup: { attrs: { span: null } },
-    command: {
-      attrs: {
-        type: ["command", "checkbox", "radio"],
-        label: null, icon: null, radiogroup: null, command: null, title: null,
-        disabled: ["", "disabled"],
-        checked: ["", "checked"]
-      }
-    },
-    data: { attrs: { value: null } },
-    datagrid: { attrs: { disabled: ["", "disabled"], multiple: ["", "multiple"] } },
-    datalist: { attrs: { data: null } },
-    dd: s,
-    del: { attrs: { cite: null, datetime: null } },
-    details: { attrs: { open: ["", "open"] } },
-    dfn: s,
-    dir: s,
-    div: s,
-    dl: s,
-    dt: s,
-    em: s,
-    embed: { attrs: { src: null, type: null, width: null, height: null } },
-    eventsource: { attrs: { src: null } },
-    fieldset: { attrs: { disabled: ["", "disabled"], form: null, name: null } },
-    figcaption: s,
-    figure: s,
-    font: s,
-    footer: s,
-    form: {
-      attrs: {
-        action: null, name: null,
-        "accept-charset": charsets,
-        autocomplete: ["on", "off"],
-        enctype: encs,
-        method: methods,
-        novalidate: ["", "novalidate"],
-        target: targets
-      }
-    },
-    frame: s,
-    frameset: s,
-    h1: s, h2: s, h3: s, h4: s, h5: s, h6: s,
-    head: {
-      attrs: {},
-      children: ["title", "base", "link", "style", "meta", "script", "noscript", "command"]
-    },
-    header: s,
-    hgroup: s,
-    hr: s,
-    html: {
-      attrs: { manifest: null },
-      children: ["head", "body"]
-    },
-    i: s,
-    iframe: {
-      attrs: {
-        src: null, srcdoc: null, name: null, width: null, height: null,
-        sandbox: ["allow-top-navigation", "allow-same-origin", "allow-forms", "allow-scripts"],
-        seamless: ["", "seamless"]
-      }
-    },
-    img: {
-      attrs: {
-        alt: null, src: null, ismap: null, usemap: null, width: null, height: null,
-        crossorigin: ["anonymous", "use-credentials"]
-      }
-    },
-    input: {
-      attrs: {
-        alt: null, dirname: null, form: null, formaction: null,
-        height: null, list: null, max: null, maxlength: null, min: null,
-        name: null, pattern: null, placeholder: null, size: null, src: null,
-        step: null, value: null, width: null,
-        accept: ["audio/*", "video/*", "image/*"],
-        autocomplete: ["on", "off"],
-        autofocus: ["", "autofocus"],
-        checked: ["", "checked"],
-        disabled: ["", "disabled"],
-        formenctype: encs,
-        formmethod: methods,
-        formnovalidate: ["", "novalidate"],
-        formtarget: targets,
-        multiple: ["", "multiple"],
-        readonly: ["", "readonly"],
-        required: ["", "required"],
-        type: ["hidden", "text", "search", "tel", "url", "email", "password", "datetime", "date", "month",
-               "week", "time", "datetime-local", "number", "range", "color", "checkbox", "radio",
-               "file", "submit", "image", "reset", "button"]
-      }
-    },
-    ins: { attrs: { cite: null, datetime: null } },
-    kbd: s,
-    keygen: {
-      attrs: {
-        challenge: null, form: null, name: null,
-        autofocus: ["", "autofocus"],
-        disabled: ["", "disabled"],
-        keytype: ["RSA"]
-      }
-    },
-    label: { attrs: { "for": null, form: null } },
-    legend: s,
-    li: { attrs: { value: null } },
-    link: {
-      attrs: {
-        href: null, type: null,
-        hreflang: langs,
-        media: media,
-        sizes: ["all", "16x16", "16x16 32x32", "16x16 32x32 64x64"]
-      }
-    },
-    map: { attrs: { name: null } },
-    mark: s,
-    menu: { attrs: { label: null, type: ["list", "context", "toolbar"] } },
-    meta: {
-      attrs: {
-        content: null,
-        charset: charsets,
-        name: ["viewport", "application-name", "author", "description", "generator", "keywords"],
-        "http-equiv": ["content-language", "content-type", "default-style", "refresh"]
-      }
-    },
-    meter: { attrs: { value: null, min: null, low: null, high: null, max: null, optimum: null } },
-    nav: s,
-    noframes: s,
-    noscript: s,
-    object: {
-      attrs: {
-        data: null, type: null, name: null, usemap: null, form: null, width: null, height: null,
-        typemustmatch: ["", "typemustmatch"]
-      }
-    },
-    ol: { attrs: { reversed: ["", "reversed"], start: null, type: ["1", "a", "A", "i", "I"] } },
-    optgroup: { attrs: { disabled: ["", "disabled"], label: null } },
-    option: { attrs: { disabled: ["", "disabled"], label: null, selected: ["", "selected"], value: null } },
-    output: { attrs: { "for": null, form: null, name: null } },
-    p: s,
-    param: { attrs: { name: null, value: null } },
-    pre: s,
-    progress: { attrs: { value: null, max: null } },
-    q: { attrs: { cite: null } },
-    rp: s,
-    rt: s,
-    ruby: s,
-    s: s,
-    samp: s,
-    script: {
-      attrs: {
-        type: ["text/javascript"],
-        src: null,
-        async: ["", "async"],
-        defer: ["", "defer"],
-        charset: charsets
-      }
-    },
-    section: s,
-    select: {
-      attrs: {
-        form: null, name: null, size: null,
-        autofocus: ["", "autofocus"],
-        disabled: ["", "disabled"],
-        multiple: ["", "multiple"]
-      }
-    },
-    small: s,
-    source: { attrs: { src: null, type: null, media: null } },
-    span: s,
-    strike: s,
-    strong: s,
-    style: {
-      attrs: {
-        type: ["text/css"],
-        media: media,
-        scoped: null
-      }
-    },
-    sub: s,
-    summary: s,
-    sup: s,
-    table: s,
-    tbody: s,
-    td: { attrs: { colspan: null, rowspan: null, headers: null } },
-    textarea: {
-      attrs: {
-        dirname: null, form: null, maxlength: null, name: null, placeholder: null,
-        rows: null, cols: null,
-        autofocus: ["", "autofocus"],
-        disabled: ["", "disabled"],
-        readonly: ["", "readonly"],
-        required: ["", "required"],
-        wrap: ["soft", "hard"]
-      }
-    },
-    tfoot: s,
-    th: { attrs: { colspan: null, rowspan: null, headers: null, scope: ["row", "col", "rowgroup", "colgroup"] } },
-    thead: s,
-    time: { attrs: { datetime: null } },
-    title: s,
-    tr: s,
-    track: {
-      attrs: {
-        src: null, label: null, "default": null,
-        kind: ["subtitles", "captions", "descriptions", "chapters", "metadata"],
-        srclang: langs
-      }
-    },
-    tt: s,
-    u: s,
-    ul: s,
-    "var": s,
-    video: {
-      attrs: {
-        src: null, poster: null, width: null, height: null,
-        crossorigin: ["anonymous", "use-credentials"],
-        preload: ["auto", "metadata", "none"],
-        autoplay: ["", "autoplay"],
-        mediagroup: ["movie"],
-        muted: ["", "muted"],
-        controls: ["", "controls"]
-      }
-    },
-    wbr: s
-  };
-
-  var globalAttrs = {
-    accesskey: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
-    "class": null,
-    contenteditable: ["true", "false"],
-    contextmenu: null,
-    dir: ["ltr", "rtl", "auto"],
-    draggable: ["true", "false", "auto"],
-    dropzone: ["copy", "move", "link", "string:", "file:"],
-    hidden: ["hidden"],
-    id: null,
-    inert: ["inert"],
-    itemid: null,
-    itemprop: null,
-    itemref: null,
-    itemscope: ["itemscope"],
-    itemtype: null,
-    lang: ["en", "es"],
-    spellcheck: ["true", "false"],
-    autocorrect: ["true", "false"],
-    autocapitalize: ["true", "false"],
-    style: null,
-    tabindex: ["1", "2", "3", "4", "5", "6", "7", "8", "9"],
-    title: null,
-    translate: ["yes", "no"],
-    onclick: null,
-    rel: ["stylesheet", "alternate", "author", "bookmark", "help", "license", "next", "nofollow", "noreferrer", "prefetch", "prev", "search", "tag"]
-  };
-  function populate(obj) {
-    for (var attr in globalAttrs) if (globalAttrs.hasOwnProperty(attr))
-      obj.attrs[attr] = globalAttrs[attr];
-  }
-
-  populate(s);
-  for (var tag in data) if (data.hasOwnProperty(tag) && data[tag] != s)
-    populate(data[tag]);
-
-  CodeMirror.htmlSchema = data;
-  function htmlHint(cm, options) {
-    var local = {schemaInfo: data};
-    if (options) for (var opt in options) local[opt] = options[opt];
-    return CodeMirror.hint.xml(cm, local);
-  }
-  CodeMirror.registerHelper("hint", "html", htmlHint);
-});
-
-
-/* ---- extension/hint/show-hint.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  var HINT_ELEMENT_CLASS        = "CodeMirror-hint";
-  var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";
-
-  // This is the old interface, kept around for now to stay
-  // backwards-compatible.
-  CodeMirror.showHint = function(cm, getHints, options) {
-    if (!getHints) return cm.showHint(options);
-    if (options && options.async) getHints.async = true;
-    var newOpts = {hint: getHints};
-    if (options) for (var prop in options) newOpts[prop] = options[prop];
-    return cm.showHint(newOpts);
-  };
-
-  CodeMirror.defineExtension("showHint", function(options) {
-    options = parseOptions(this, this.getCursor("start"), options);
-    var selections = this.listSelections()
-    if (selections.length > 1) return;
-    // By default, don't allow completion when something is selected.
-    // A hint function can have a `supportsSelection` property to
-    // indicate that it can handle selections.
-    if (this.somethingSelected()) {
-      if (!options.hint.supportsSelection) return;
-      // Don't try with cross-line selections
-      for (var i = 0; i < selections.length; i++)
-        if (selections[i].head.line != selections[i].anchor.line) return;
-    }
-
-    if (this.state.completionActive) this.state.completionActive.close();
-    var completion = this.state.completionActive = new Completion(this, options);
-    if (!completion.options.hint) return;
-
-    CodeMirror.signal(this, "startCompletion", this);
-    completion.update(true);
-  });
-
-  CodeMirror.defineExtension("closeHint", function() {
-    if (this.state.completionActive) this.state.completionActive.close()
-  })
-
-  function Completion(cm, options) {
-    this.cm = cm;
-    this.options = options;
-    this.widget = null;
-    this.debounce = 0;
-    this.tick = 0;
-    this.startPos = this.cm.getCursor("start");
-    this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length;
-
-    var self = this;
-    cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); });
-  }
-
-  var requestAnimationFrame = window.requestAnimationFrame || function(fn) {
-    return setTimeout(fn, 1000/60);
-  };
-  var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;
-
-  Completion.prototype = {
-    close: function() {
-      if (!this.active()) return;
-      this.cm.state.completionActive = null;
-      this.tick = null;
-      this.cm.off("cursorActivity", this.activityFunc);
-
-      if (this.widget && this.data) CodeMirror.signal(this.data, "close");
-      if (this.widget) this.widget.close();
-      CodeMirror.signal(this.cm, "endCompletion", this.cm);
-    },
-
-    active: function() {
-      return this.cm.state.completionActive == this;
-    },
-
-    pick: function(data, i) {
-      var completion = data.list[i], self = this;
-      this.cm.operation(function() {
-        if (completion.hint)
-          completion.hint(self.cm, data, completion);
-        else
-          self.cm.replaceRange(getText(completion), completion.from || data.from,
-                               completion.to || data.to, "complete");
-        CodeMirror.signal(data, "pick", completion);
-        self.cm.scrollIntoView();
-      })
-      this.close();
-    },
-
-    cursorActivity: function() {
-      if (this.debounce) {
-        cancelAnimationFrame(this.debounce);
-        this.debounce = 0;
-      }
-
-      var identStart = this.startPos;
-      if(this.data) {
-        identStart = this.data.from;
-      }
-
-      var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line);
-      if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||
-          pos.ch < identStart.ch || this.cm.somethingSelected() ||
-          (!pos.ch || this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {
-        this.close();
-      } else {
-        var self = this;
-        this.debounce = requestAnimationFrame(function() {self.update();});
-        if (this.widget) this.widget.disable();
-      }
-    },
-
-    update: function(first) {
-      if (this.tick == null) return
-      var self = this, myTick = ++this.tick
-      fetchHints(this.options.hint, this.cm, this.options, function(data) {
-        if (self.tick == myTick) self.finishUpdate(data, first)
-      })
-    },
-
-    finishUpdate: function(data, first) {
-      if (this.data) CodeMirror.signal(this.data, "update");
-
-      var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);
-      if (this.widget) this.widget.close();
-
-      this.data = data;
-
-      if (data && data.list.length) {
-        if (picked && data.list.length == 1) {
-          this.pick(data, 0);
-        } else {
-          this.widget = new Widget(this, data);
-          CodeMirror.signal(data, "shown");
-        }
-      }
-    }
-  };
-
-  function parseOptions(cm, pos, options) {
-    var editor = cm.options.hintOptions;
-    var out = {};
-    for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
-    if (editor) for (var prop in editor)
-      if (editor[prop] !== undefined) out[prop] = editor[prop];
-    if (options) for (var prop in options)
-      if (options[prop] !== undefined) out[prop] = options[prop];
-    if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos)
-    return out;
-  }
-
-  function getText(completion) {
-    if (typeof completion == "string") return completion;
-    else return completion.text;
-  }
-
-  function buildKeyMap(completion, handle) {
-    var baseMap = {
-      Up: function() {handle.moveFocus(-1);},
-      Down: function() {handle.moveFocus(1);},
-      PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);},
-      PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);},
-      Home: function() {handle.setFocus(0);},
-      End: function() {handle.setFocus(handle.length - 1);},
-      Enter: handle.pick,
-      Tab: handle.pick,
-      Esc: handle.close
-    };
-
-    var mac = /Mac/.test(navigator.platform);
-
-    if (mac) {
-      baseMap["Ctrl-P"] = function() {handle.moveFocus(-1);};
-      baseMap["Ctrl-N"] = function() {handle.moveFocus(1);};
-    }
-
-    var custom = completion.options.customKeys;
-    var ourMap = custom ? {} : baseMap;
-    function addBinding(key, val) {
-      var bound;
-      if (typeof val != "string")
-        bound = function(cm) { return val(cm, handle); };
-      // This mechanism is deprecated
-      else if (baseMap.hasOwnProperty(val))
-        bound = baseMap[val];
-      else
-        bound = val;
-      ourMap[key] = bound;
-    }
-    if (custom)
-      for (var key in custom) if (custom.hasOwnProperty(key))
-        addBinding(key, custom[key]);
-    var extra = completion.options.extraKeys;
-    if (extra)
-      for (var key in extra) if (extra.hasOwnProperty(key))
-        addBinding(key, extra[key]);
-    return ourMap;
-  }
-
-  function getHintElement(hintsElement, el) {
-    while (el && el != hintsElement) {
-      if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el;
-      el = el.parentNode;
-    }
-  }
-
-  function Widget(completion, data) {
-    this.completion = completion;
-    this.data = data;
-    this.picked = false;
-    var widget = this, cm = completion.cm;
-    var ownerDocument = cm.getInputField().ownerDocument;
-    var parentWindow = ownerDocument.defaultView || ownerDocument.parentWindow;
-
-    var hints = this.hints = ownerDocument.createElement("ul");
-    var theme = completion.cm.options.theme;
-    hints.className = "CodeMirror-hints " + theme;
-    this.selectedHint = data.selectedHint || 0;
-
-    var completions = data.list;
-    for (var i = 0; i < completions.length; ++i) {
-      var elt = hints.appendChild(ownerDocument.createElement("li")), cur = completions[i];
-      var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS);
-      if (cur.className != null) className = cur.className + " " + className;
-      elt.className = className;
-      if (cur.render) cur.render(elt, data, cur);
-      else elt.appendChild(ownerDocument.createTextNode(cur.displayText || getText(cur)));
-      elt.hintId = i;
-    }
-
-    var container = completion.options.container || ownerDocument.body;
-    var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);
-    var left = pos.left, top = pos.bottom, below = true;
-    var offsetLeft = 0, offsetTop = 0;
-    if (container !== ownerDocument.body) {
-      // We offset the cursor position because left and top are relative to the offsetParent's top left corner.
-      var isContainerPositioned = ['absolute', 'relative', 'fixed'].indexOf(parentWindow.getComputedStyle(container).position) !== -1;
-      var offsetParent = isContainerPositioned ? container : container.offsetParent;
-      var offsetParentPosition = offsetParent.getBoundingClientRect();
-      var bodyPosition = ownerDocument.body.getBoundingClientRect();
-      offsetLeft = (offsetParentPosition.left - bodyPosition.left - offsetParent.scrollLeft);
-      offsetTop = (offsetParentPosition.top - bodyPosition.top - offsetParent.scrollTop);
-    }
-    hints.style.left = (left - offsetLeft) + "px";
-    hints.style.top = (top - offsetTop) + "px";
-
-    // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
-    var winW = parentWindow.innerWidth || Math.max(ownerDocument.body.offsetWidth, ownerDocument.documentElement.offsetWidth);
-    var winH = parentWindow.innerHeight || Math.max(ownerDocument.body.offsetHeight, ownerDocument.documentElement.offsetHeight);
-    container.appendChild(hints);
-    var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
-    var scrolls = hints.scrollHeight > hints.clientHeight + 1
-    var startScroll = cm.getScrollInfo();
-
-    if (overlapY > 0) {
-      var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
-      if (curTop - height > 0) { // Fits above cursor
-        hints.style.top = (top = pos.top - height - offsetTop) + "px";
-        below = false;
-      } else if (height > winH) {
-        hints.style.height = (winH - 5) + "px";
-        hints.style.top = (top = pos.bottom - box.top - offsetTop) + "px";
-        var cursor = cm.getCursor();
-        if (data.from.ch != cursor.ch) {
-          pos = cm.cursorCoords(cursor);
-          hints.style.left = (left = pos.left - offsetLeft) + "px";
-          box = hints.getBoundingClientRect();
-        }
-      }
-    }
-    var overlapX = box.right - winW;
-    if (overlapX > 0) {
-      if (box.right - box.left > winW) {
-        hints.style.width = (winW - 5) + "px";
-        overlapX -= (box.right - box.left) - winW;
-      }
-      hints.style.left = (left = pos.left - overlapX - offsetLeft) + "px";
-    }
-    if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling)
-      node.style.paddingRight = cm.display.nativeBarWidth + "px"
-
-    cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
-      moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
-      setFocus: function(n) { widget.changeActive(n); },
-      menuSize: function() { return widget.screenAmount(); },
-      length: completions.length,
-      close: function() { completion.close(); },
-      pick: function() { widget.pick(); },
-      data: data
-    }));
-
-    if (completion.options.closeOnUnfocus) {
-      var closingOnBlur;
-      cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
-      cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
-    }
-
-    cm.on("scroll", this.onScroll = function() {
-      var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
-      var newTop = top + startScroll.top - curScroll.top;
-      var point = newTop - (parentWindow.pageYOffset || (ownerDocument.documentElement || ownerDocument.body).scrollTop);
-      if (!below) point += hints.offsetHeight;
-      if (point <= editor.top || point >= editor.bottom) return completion.close();
-      hints.style.top = newTop + "px";
-      hints.style.left = (left + startScroll.left - curScroll.left) + "px";
-    });
-
-    CodeMirror.on(hints, "dblclick", function(e) {
-      var t = getHintElement(hints, e.target || e.srcElement);
-      if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
-    });
-
-    CodeMirror.on(hints, "click", function(e) {
-      var t = getHintElement(hints, e.target || e.srcElement);
-      if (t && t.hintId != null) {
-        widget.changeActive(t.hintId);
-        if (completion.options.completeOnSingleClick) widget.pick();
-      }
-    });
-
-    CodeMirror.on(hints, "mousedown", function() {
-      setTimeout(function(){cm.focus();}, 20);
-    });
-    this.scrollToActive()
-
-    CodeMirror.signal(data, "select", completions[this.selectedHint], hints.childNodes[this.selectedHint]);
-    return true;
-  }
-
-  Widget.prototype = {
-    close: function() {
-      if (this.completion.widget != this) return;
-      this.completion.widget = null;
-      this.hints.parentNode.removeChild(this.hints);
-      this.completion.cm.removeKeyMap(this.keyMap);
-
-      var cm = this.completion.cm;
-      if (this.completion.options.closeOnUnfocus) {
-        cm.off("blur", this.onBlur);
-        cm.off("focus", this.onFocus);
-      }
-      cm.off("scroll", this.onScroll);
-    },
-
-    disable: function() {
-      this.completion.cm.removeKeyMap(this.keyMap);
-      var widget = this;
-      this.keyMap = {Enter: function() { widget.picked = true; }};
-      this.completion.cm.addKeyMap(this.keyMap);
-    },
-
-    pick: function() {
-      this.completion.pick(this.data, this.selectedHint);
-    },
-
-    changeActive: function(i, avoidWrap) {
-      if (i >= this.data.list.length)
-        i = avoidWrap ? this.data.list.length - 1 : 0;
-      else if (i < 0)
-        i = avoidWrap ? 0  : this.data.list.length - 1;
-      if (this.selectedHint == i) return;
-      var node = this.hints.childNodes[this.selectedHint];
-      if (node) node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, "");
-      node = this.hints.childNodes[this.selectedHint = i];
-      node.className += " " + ACTIVE_HINT_ELEMENT_CLASS;
-      this.scrollToActive()
-      CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node);
-    },
-
-    scrollToActive: function() {
-      var margin = this.completion.options.scrollMargin || 0;
-      var node1 = this.hints.childNodes[Math.max(0, this.selectedHint - margin)];
-      var node2 = this.hints.childNodes[Math.min(this.data.list.length - 1, this.selectedHint + margin)];
-      var firstNode = this.hints.firstChild;
-      if (node1.offsetTop < this.hints.scrollTop)
-        this.hints.scrollTop = node1.offsetTop - firstNode.offsetTop;
-      else if (node2.offsetTop + node2.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
-        this.hints.scrollTop = node2.offsetTop + node2.offsetHeight - this.hints.clientHeight + firstNode.offsetTop;
-    },
-
-    screenAmount: function() {
-      return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1;
-    }
-  };
-
-  function applicableHelpers(cm, helpers) {
-    if (!cm.somethingSelected()) return helpers
-    var result = []
-    for (var i = 0; i < helpers.length; i++)
-      if (helpers[i].supportsSelection) result.push(helpers[i])
-    return result
-  }
-
-  function fetchHints(hint, cm, options, callback) {
-    if (hint.async) {
-      hint(cm, callback, options)
-    } else {
-      var result = hint(cm, options)
-      if (result && result.then) result.then(callback)
-      else callback(result)
-    }
-  }
-
-  function resolveAutoHints(cm, pos) {
-    var helpers = cm.getHelpers(pos, "hint"), words
-    if (helpers.length) {
-      var resolved = function(cm, callback, options) {
-        var app = applicableHelpers(cm, helpers);
-        function run(i) {
-          if (i == app.length) return callback(null)
-          fetchHints(app[i], cm, options, function(result) {
-            if (result && result.list.length > 0) callback(result)
-            else run(i + 1)
-          })
-        }
-        run(0)
-      }
-      resolved.async = true
-      resolved.supportsSelection = true
-      return resolved
-    } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) {
-      return function(cm) { return CodeMirror.hint.fromList(cm, {words: words}) }
-    } else if (CodeMirror.hint.anyword) {
-      return function(cm, options) { return CodeMirror.hint.anyword(cm, options) }
-    } else {
-      return function() {}
-    }
-  }
-
-  CodeMirror.registerHelper("hint", "auto", {
-    resolve: resolveAutoHints
-  });
-
-  CodeMirror.registerHelper("hint", "fromList", function(cm, options) {
-    var cur = cm.getCursor(), token = cm.getTokenAt(cur)
-    var term, from = CodeMirror.Pos(cur.line, token.start), to = cur
-    if (token.start < cur.ch && /\w/.test(token.string.charAt(cur.ch - token.start - 1))) {
-      term = token.string.substr(0, cur.ch - token.start)
-    } else {
-      term = ""
-      from = cur
-    }
-    var found = [];
-    for (var i = 0; i < options.words.length; i++) {
-      var word = options.words[i];
-      if (word.slice(0, term.length) == term)
-        found.push(word);
-    }
-
-    if (found.length) return {list: found, from: from, to: to};
-  });
-
-  CodeMirror.commands.autocomplete = CodeMirror.showHint;
-
-  var defaultOptions = {
-    hint: CodeMirror.hint.auto,
-    completeSingle: true,
-    alignWithWord: true,
-    closeCharacters: /[\s()\[\]{};:>,]/,
-    closeOnUnfocus: true,
-    completeOnSingleClick: true,
-    container: null,
-    customKeys: null,
-    extraKeys: null
-  };
-
-  CodeMirror.defineOption("hintOptions", null);
-});
-
-
-/* ---- extension/hint/sql-hint.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("../../mode/sql/sql"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "../../mode/sql/sql"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  var tables;
-  var defaultTable;
-  var keywords;
-  var identifierQuote;
-  var CONS = {
-    QUERY_DIV: ";",
-    ALIAS_KEYWORD: "AS"
-  };
-  var Pos = CodeMirror.Pos, cmpPos = CodeMirror.cmpPos;
-
-  function isArray(val) { return Object.prototype.toString.call(val) == "[object Array]" }
-
-  function getKeywords(editor) {
-    var mode = editor.doc.modeOption;
-    if (mode === "sql") mode = "text/x-sql";
-    return CodeMirror.resolveMode(mode).keywords;
-  }
-
-  function getIdentifierQuote(editor) {
-    var mode = editor.doc.modeOption;
-    if (mode === "sql") mode = "text/x-sql";
-    return CodeMirror.resolveMode(mode).identifierQuote || "`";
-  }
-
-  function getText(item) {
-    return typeof item == "string" ? item : item.text;
-  }
-
-  function wrapTable(name, value) {
-    if (isArray(value)) value = {columns: value}
-    if (!value.text) value.text = name
-    return value
-  }
-
-  function parseTables(input) {
-    var result = {}
-    if (isArray(input)) {
-      for (var i = input.length - 1; i >= 0; i--) {
-        var item = input[i]
-        result[getText(item).toUpperCase()] = wrapTable(getText(item), item)
-      }
-    } else if (input) {
-      for (var name in input)
-        result[name.toUpperCase()] = wrapTable(name, input[name])
-    }
-    return result
-  }
-
-  function getTable(name) {
-    return tables[name.toUpperCase()]
-  }
-
-  function shallowClone(object) {
-    var result = {};
-    for (var key in object) if (object.hasOwnProperty(key))
-      result[key] = object[key];
-    return result;
-  }
-
-  function match(string, word) {
-    var len = string.length;
-    var sub = getText(word).substr(0, len);
-    return string.toUpperCase() === sub.toUpperCase();
-  }
-
-  function addMatches(result, search, wordlist, formatter) {
-    if (isArray(wordlist)) {
-      for (var i = 0; i < wordlist.length; i++)
-        if (match(search, wordlist[i])) result.push(formatter(wordlist[i]))
-    } else {
-      for (var word in wordlist) if (wordlist.hasOwnProperty(word)) {
-        var val = wordlist[word]
-        if (!val || val === true)
-          val = word
-        else
-          val = val.displayText ? {text: val.text, displayText: val.displayText} : val.text
-        if (match(search, val)) result.push(formatter(val))
-      }
-    }
-  }
-
-  function cleanName(name) {
-    // Get rid name from identifierQuote and preceding dot(.)
-    if (name.charAt(0) == ".") {
-      name = name.substr(1);
-    }
-    // replace doublicated identifierQuotes with single identifierQuotes
-    // and remove single identifierQuotes
-    var nameParts = name.split(identifierQuote+identifierQuote);
-    for (var i = 0; i < nameParts.length; i++)
-      nameParts[i] = nameParts[i].replace(new RegExp(identifierQuote,"g"), "");
-    return nameParts.join(identifierQuote);
-  }
-
-  function insertIdentifierQuotes(name) {
-    var nameParts = getText(name).split(".");
-    for (var i = 0; i < nameParts.length; i++)
-      nameParts[i] = identifierQuote +
-        // doublicate identifierQuotes
-        nameParts[i].replace(new RegExp(identifierQuote,"g"), identifierQuote+identifierQuote) +
-        identifierQuote;
-    var escaped = nameParts.join(".");
-    if (typeof name == "string") return escaped;
-    name = shallowClone(name);
-    name.text = escaped;
-    return name;
-  }
-
-  function nameCompletion(cur, token, result, editor) {
-    // Try to complete table, column names and return start position of completion
-    var useIdentifierQuotes = false;
-    var nameParts = [];
-    var start = token.start;
-    var cont = true;
-    while (cont) {
-      cont = (token.string.charAt(0) == ".");
-      useIdentifierQuotes = useIdentifierQuotes || (token.string.charAt(0) == identifierQuote);
-
-      start = token.start;
-      nameParts.unshift(cleanName(token.string));
-
-      token = editor.getTokenAt(Pos(cur.line, token.start));
-      if (token.string == ".") {
-        cont = true;
-        token = editor.getTokenAt(Pos(cur.line, token.start));
-      }
-    }
-
-    // Try to complete table names
-    var string = nameParts.join(".");
-    addMatches(result, string, tables, function(w) {
-      return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
-    });
-
-    // Try to complete columns from defaultTable
-    addMatches(result, string, defaultTable, function(w) {
-      return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
-    });
-
-    // Try to complete columns
-    string = nameParts.pop();
-    var table = nameParts.join(".");
-
-    var alias = false;
-    var aliasTable = table;
-    // Check if table is available. If not, find table by Alias
-    if (!getTable(table)) {
-      var oldTable = table;
-      table = findTableByAlias(table, editor);
-      if (table !== oldTable) alias = true;
-    }
-
-    var columns = getTable(table);
-    if (columns && columns.columns)
-      columns = columns.columns;
-
-    if (columns) {
-      addMatches(result, string, columns, function(w) {
-        var tableInsert = table;
-        if (alias == true) tableInsert = aliasTable;
-        if (typeof w == "string") {
-          w = tableInsert + "." + w;
-        } else {
-          w = shallowClone(w);
-          w.text = tableInsert + "." + w.text;
-        }
-        return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
-      });
-    }
-
-    return start;
-  }
-
-  function eachWord(lineText, f) {
-    var words = lineText.split(/\s+/)
-    for (var i = 0; i < words.length; i++)
-      if (words[i]) f(words[i].replace(/[,;]/g, ''))
-  }
-
-  function findTableByAlias(alias, editor) {
-    var doc = editor.doc;
-    var fullQuery = doc.getValue();
-    var aliasUpperCase = alias.toUpperCase();
-    var previousWord = "";
-    var table = "";
-    var separator = [];
-    var validRange = {
-      start: Pos(0, 0),
-      end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length)
-    };
-
-    //add separator
-    var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV);
-    while(indexOfSeparator != -1) {
-      separator.push(doc.posFromIndex(indexOfSeparator));
-      indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1);
-    }
-    separator.unshift(Pos(0, 0));
-    separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length));
-
-    //find valid range
-    var prevItem = null;
-    var current = editor.getCursor()
-    for (var i = 0; i < separator.length; i++) {
-      if ((prevItem == null || cmpPos(current, prevItem) > 0) && cmpPos(current, separator[i]) <= 0) {
-        validRange = {start: prevItem, end: separator[i]};
-        break;
-      }
-      prevItem = separator[i];
-    }
-
-    if (validRange.start) {
-      var query = doc.getRange(validRange.start, validRange.end, false);
-
-      for (var i = 0; i < query.length; i++) {
-        var lineText = query[i];
-        eachWord(lineText, function(word) {
-          var wordUpperCase = word.toUpperCase();
-          if (wordUpperCase === aliasUpperCase && getTable(previousWord))
-            table = previousWord;
-          if (wordUpperCase !== CONS.ALIAS_KEYWORD)
-            previousWord = word;
-        });
-        if (table) break;
-      }
-    }
-    return table;
-  }
-
-  CodeMirror.registerHelper("hint", "sql", function(editor, options) {
-    tables = parseTables(options && options.tables)
-    var defaultTableName = options && options.defaultTable;
-    var disableKeywords = options && options.disableKeywords;
-    defaultTable = defaultTableName && getTable(defaultTableName);
-    keywords = getKeywords(editor);
-    identifierQuote = getIdentifierQuote(editor);
-
-    if (defaultTableName && !defaultTable)
-      defaultTable = findTableByAlias(defaultTableName, editor);
-
-    defaultTable = defaultTable || [];
-
-    if (defaultTable.columns)
-      defaultTable = defaultTable.columns;
-
-    var cur = editor.getCursor();
-    var result = [];
-    var token = editor.getTokenAt(cur), start, end, search;
-    if (token.end > cur.ch) {
-      token.end = cur.ch;
-      token.string = token.string.slice(0, cur.ch - token.start);
-    }
-
-    if (token.string.match(/^[.`"'\w@][\w$#]*$/g)) {
-      search = token.string;
-      start = token.start;
-      end = token.end;
-    } else {
-      start = end = cur.ch;
-      search = "";
-    }
-    if (search.charAt(0) == "." || search.charAt(0) == identifierQuote) {
-      start = nameCompletion(cur, token, result, editor);
-    } else {
-      var objectOrClass = function(w, className) {
-        if (typeof w === "object") {
-          w.className = className;
-        } else {
-          w = { text: w, className: className };
-        }
-        return w;
-      };
-    addMatches(result, search, defaultTable, function(w) {
-        return objectOrClass(w, "CodeMirror-hint-table CodeMirror-hint-default-table");
-    });
-    addMatches(
-        result,
-        search,
-        tables, function(w) {
-          return objectOrClass(w, "CodeMirror-hint-table");
-        }
-    );
-    if (!disableKeywords)
-      addMatches(result, search, keywords, function(w) {
-          return objectOrClass(w.toUpperCase(), "CodeMirror-hint-keyword");
-      });
-  }
-
-    return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)};
-  });
-});
-
-
-/* ---- extension/hint/xml-hint.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  var Pos = CodeMirror.Pos;
-
-  function matches(hint, typed, matchInMiddle) {
-    if (matchInMiddle) return hint.indexOf(typed) >= 0;
-    else return hint.lastIndexOf(typed, 0) == 0;
-  }
-
-  function getHints(cm, options) {
-    var tags = options && options.schemaInfo;
-    var quote = (options && options.quoteChar) || '"';
-    var matchInMiddle = options && options.matchInMiddle;
-    if (!tags) return;
-    var cur = cm.getCursor(), token = cm.getTokenAt(cur);
-    if (token.end > cur.ch) {
-      token.end = cur.ch;
-      token.string = token.string.slice(0, cur.ch - token.start);
-    }
-    var inner = CodeMirror.innerMode(cm.getMode(), token.state);
-    if (!inner.mode.xmlCurrentTag) return
-    var result = [], replaceToken = false, prefix;
-    var tag = /\btag\b/.test(token.type) && !/>$/.test(token.string);
-    var tagName = tag && /^\w/.test(token.string), tagStart;
-
-    if (tagName) {
-      var before = cm.getLine(cur.line).slice(Math.max(0, token.start - 2), token.start);
-      var tagType = /<\/$/.test(before) ? "close" : /<$/.test(before) ? "open" : null;
-      if (tagType) tagStart = token.start - (tagType == "close" ? 2 : 1);
-    } else if (tag && token.string == "<") {
-      tagType = "open";
-    } else if (tag && token.string == "</") {
-      tagType = "close";
-    }
-
-    var tagInfo = inner.mode.xmlCurrentTag(inner.state)
-    if (!tag && !tagInfo || tagType) {
-      if (tagName)
-        prefix = token.string;
-      replaceToken = tagType;
-      var context = inner.mode.xmlCurrentContext ? inner.mode.xmlCurrentContext(inner.state) : []
-      var inner = context.length && context[context.length - 1]
-      var curTag = inner && tags[inner]
-      var childList = inner ? curTag && curTag.children : tags["!top"];
-      if (childList && tagType != "close") {
-        for (var i = 0; i < childList.length; ++i) if (!prefix || matches(childList[i], prefix, matchInMiddle))
-          result.push("<" + childList[i]);
-      } else if (tagType != "close") {
-        for (var name in tags)
-          if (tags.hasOwnProperty(name) && name != "!top" && name != "!attrs" && (!prefix || matches(name, prefix, matchInMiddle)))
-            result.push("<" + name);
-      }
-      if (inner && (!prefix || tagType == "close" && matches(inner, prefix, matchInMiddle)))
-        result.push("</" + inner + ">");
-    } else {
-      // Attribute completion
-      var curTag = tagInfo && tags[tagInfo.name], attrs = curTag && curTag.attrs;
-      var globalAttrs = tags["!attrs"];
-      if (!attrs && !globalAttrs) return;
-      if (!attrs) {
-        attrs = globalAttrs;
-      } else if (globalAttrs) { // Combine tag-local and global attributes
-        var set = {};
-        for (var nm in globalAttrs) if (globalAttrs.hasOwnProperty(nm)) set[nm] = globalAttrs[nm];
-        for (var nm in attrs) if (attrs.hasOwnProperty(nm)) set[nm] = attrs[nm];
-        attrs = set;
-      }
-      if (token.type == "string" || token.string == "=") { // A value
-        var before = cm.getRange(Pos(cur.line, Math.max(0, cur.ch - 60)),
-                                 Pos(cur.line, token.type == "string" ? token.start : token.end));
-        var atName = before.match(/([^\s\u00a0=<>\"\']+)=$/), atValues;
-        if (!atName || !attrs.hasOwnProperty(atName[1]) || !(atValues = attrs[atName[1]])) return;
-        if (typeof atValues == 'function') atValues = atValues.call(this, cm); // Functions can be used to supply values for autocomplete widget
-        if (token.type == "string") {
-          prefix = token.string;
-          var n = 0;
-          if (/['"]/.test(token.string.charAt(0))) {
-            quote = token.string.charAt(0);
-            prefix = token.string.slice(1);
-            n++;
-          }
-          var len = token.string.length;
-          if (/['"]/.test(token.string.charAt(len - 1))) {
-            quote = token.string.charAt(len - 1);
-            prefix = token.string.substr(n, len - 2);
-          }
-          if (n) { // an opening quote
-            var line = cm.getLine(cur.line);
-            if (line.length > token.end && line.charAt(token.end) == quote) token.end++; // include a closing quote
-          }
-          replaceToken = true;
-        }
-        for (var i = 0; i < atValues.length; ++i) if (!prefix || matches(atValues[i], prefix, matchInMiddle))
-          result.push(quote + atValues[i] + quote);
-      } else { // An attribute name
-        if (token.type == "attribute") {
-          prefix = token.string;
-          replaceToken = true;
-        }
-        for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || matches(attr, prefix, matchInMiddle)))
-          result.push(attr);
-      }
-    }
-    return {
-      list: result,
-      from: replaceToken ? Pos(cur.line, tagStart == null ? token.start : tagStart) : cur,
-      to: replaceToken ? Pos(cur.line, token.end) : cur
-    };
-  }
-
-  CodeMirror.registerHelper("hint", "xml", getHints);
-});
-
-
-/* ---- extension/lint/json-lint.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-// Depends on jsonlint.js from https://github.com/zaach/jsonlint
-
-// declare global: jsonlint
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-CodeMirror.registerHelper("lint", "json", function(text) {
-  var found = [];
-  if (!window.jsonlint) {
-    if (window.console) {
-      window.console.error("Error: window.jsonlint not defined, CodeMirror JSON linting cannot run.");
-    }
-    return found;
-  }
-  // for jsonlint's web dist jsonlint is exported as an object with a single property parser, of which parseError
-  // is a subproperty
-  var jsonlint = window.jsonlint.parser || window.jsonlint
-  jsonlint.parseError = function(str, hash) {
-    var loc = hash.loc;
-    found.push({from: CodeMirror.Pos(loc.first_line - 1, loc.first_column),
-                to: CodeMirror.Pos(loc.last_line - 1, loc.last_column),
-                message: str});
-  };
-  try { jsonlint.parse(text); }
-  catch(e) {}
-  return found;
-});
-
-});
-
-
-/* ---- extension/lint/jsonlint.js ---- */
-
-
-var jsonlint=function(){var a=!0,b=!1,c={},d=function(){var a={trace:function(){},yy:{},symbols_:{error:2,JSONString:3,STRING:4,JSONNumber:5,NUMBER:6,JSONNullLiteral:7,NULL:8,JSONBooleanLiteral:9,TRUE:10,FALSE:11,JSONText:12,JSONValue:13,EOF:14,JSONObject:15,JSONArray:16,"{":17,"}":18,JSONMemberList:19,JSONMember:20,":":21,",":22,"[":23,"]":24,JSONElementList:25,$accept:0,$end:1},terminals_:{2:"error",4:"STRING",6:"NUMBER",8:"NULL",10:"TRUE",11:"FALSE",14:"EOF",17:"{",18:"}",21:":",22:",",23:"[",24:"]"},productions_:[0,[3,1],[5,1],[7,1],[9,1],[9,1],[12,2],[13,1],[13,1],[13,1],[13,1],[13,1],[13,1],[15,2],[15,3],[20,3],[19,1],[19,3],[16,2],[16,3],[25,1],[25,3]],performAction:function(b,c,d,e,f,g,h){var i=g.length-1;switch(f){case 1:this.$=b.replace(/\\(\\|")/g,"$1").replace(/\\n/g,"\n").replace(/\\r/g,"\r").replace(/\\t/g,"	").replace(/\\v/g,"").replace(/\\f/g,"\f").replace(/\\b/g,"\b");break;case 2:this.$=Number(b);break;case 3:this.$=null;break;case 4:this.$=!0;break;case 5:this.$=!1;break;case 6:return this.$=g[i-1];case 13:this.$={};break;case 14:this.$=g[i-1];break;case 15:this.$=[g[i-2],g[i]];break;case 16:this.$={},this.$[g[i][0]]=g[i][1];break;case 17:this.$=g[i-2],g[i-2][g[i][0]]=g[i][1];break;case 18:this.$=[];break;case 19:this.$=g[i-1];break;case 20:this.$=[g[i]];break;case 21:this.$=g[i-2],g[i-2].push(g[i])}},table:[{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],12:1,13:2,15:7,16:8,17:[1,14],23:[1,15]},{1:[3]},{14:[1,16]},{14:[2,7],18:[2,7],22:[2,7],24:[2,7]},{14:[2,8],18:[2,8],22:[2,8],24:[2,8]},{14:[2,9],18:[2,9],22:[2,9],24:[2,9]},{14:[2,10],18:[2,10],22:[2,10],24:[2,10]},{14:[2,11],18:[2,11],22:[2,11],24:[2,11]},{14:[2,12],18:[2,12],22:[2,12],24:[2,12]},{14:[2,3],18:[2,3],22:[2,3],24:[2,3]},{14:[2,4],18:[2,4],22:[2,4],24:[2,4]},{14:[2,5],18:[2,5],22:[2,5],24:[2,5]},{14:[2,1],18:[2,1],21:[2,1],22:[2,1],24:[2,1]},{14:[2,2],18:[2,2],22:[2,2],24:[2,2]},{3:20,4:[1,12],18:[1,17],19:18,20:19},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:23,15:7,16:8,17:[1,14],23:[1,15],24:[1,21],25:22},{1:[2,6]},{14:[2,13],18:[2,13],22:[2,13],24:[2,13]},{18:[1,24],22:[1,25]},{18:[2,16],22:[2,16]},{21:[1,26]},{14:[2,18],18:[2,18],22:[2,18],24:[2,18]},{22:[1,28],24:[1,27]},{22:[2,20],24:[2,20]},{14:[2,14],18:[2,14],22:[2,14],24:[2,14]},{3:20,4:[1,12],20:29},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:30,15:7,16:8,17:[1,14],23:[1,15]},{14:[2,19],18:[2,19],22:[2,19],24:[2,19]},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:31,15:7,16:8,17:[1,14],23:[1,15]},{18:[2,17],22:[2,17]},{18:[2,15],22:[2,15]},{22:[2,21],24:[2,21]}],defaultActions:{16:[2,6]},parseError:function(b,c){throw new Error(b)},parse:function(b){function o(a){d.length=d.length-2*a,e.length=e.length-a,f.length=f.length-a}function p(){var a;return a=c.lexer.lex()||1,typeof a!="number"&&(a=c.symbols_[a]||a),a}var c=this,d=[0],e=[null],f=[],g=this.table,h="",i=0,j=0,k=0,l=2,m=1;this.lexer.setInput(b),this.lexer.yy=this.yy,this.yy.lexer=this.lexer,typeof this.lexer.yylloc=="undefined"&&(this.lexer.yylloc={});var n=this.lexer.yylloc;f.push(n),typeof this.yy.parseError=="function"&&(this.parseError=this.yy.parseError);var q,r,s,t,u,v,w={},x,y,z,A;for(;;){s=d[d.length-1],this.defaultActions[s]?t=this.defaultActions[s]:(q==null&&(q=p()),t=g[s]&&g[s][q]);if(typeof t=="undefined"||!t.length||!t[0]){if(!k){A=[];for(x in g[s])this.terminals_[x]&&x>2&&A.push("'"+this.terminals_[x]+"'");var B="";this.lexer.showPosition?B="Parse error on line "+(i+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+A.join(", ")+", got '"+this.terminals_[q]+"'":B="Parse error on line "+(i+1)+": Unexpected "+(q==1?"end of input":"'"+(this.terminals_[q]||q)+"'"),this.parseError(B,{text:this.lexer.match,token:this.terminals_[q]||q,line:this.lexer.yylineno,loc:n,expected:A})}if(k==3){if(q==m)throw new Error(B||"Parsing halted.");j=this.lexer.yyleng,h=this.lexer.yytext,i=this.lexer.yylineno,n=this.lexer.yylloc,q=p()}for(;;){if(l.toString()in g[s])break;if(s==0)throw new Error(B||"Parsing halted.");o(1),s=d[d.length-1]}r=q,q=l,s=d[d.length-1],t=g[s]&&g[s][l],k=3}if(t[0]instanceof Array&&t.length>1)throw new Error("Parse Error: multiple actions possible at state: "+s+", token: "+q);switch(t[0]){case 1:d.push(q),e.push(this.lexer.yytext),f.push(this.lexer.yylloc),d.push(t[1]),q=null,r?(q=r,r=null):(j=this.lexer.yyleng,h=this.lexer.yytext,i=this.lexer.yylineno,n=this.lexer.yylloc,k>0&&k--);break;case 2:y=this.productions_[t[1]][1],w.$=e[e.length-y],w._$={first_line:f[f.length-(y||1)].first_line,last_line:f[f.length-1].last_line,first_column:f[f.length-(y||1)].first_column,last_column:f[f.length-1].last_column},v=this.performAction.call(w,h,j,i,this.yy,t[1],e,f);if(typeof v!="undefined")return v;y&&(d=d.slice(0,-1*y*2),e=e.slice(0,-1*y),f=f.slice(0,-1*y)),d.push(this.productions_[t[1]][0]),e.push(w.$),f.push(w._$),z=g[d[d.length-2]][d[d.length-1]],d.push(z);break;case 3:return!0}}return!0}},b=function(){var a={EOF:1,parseError:function(b,c){if(!this.yy.parseError)throw new Error(b);this.yy.parseError(b,c)},setInput:function(a){return this._input=a,this._more=this._less=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this},input:function(){var a=this._input[0];this.yytext+=a,this.yyleng++,this.match+=a,this.matched+=a;var b=a.match(/\n/);return b&&this.yylineno++,this._input=this._input.slice(1),a},unput:function(a){return this._input=a+this._input,this},more:function(){return this._more=!0,this},less:function(a){this._input=this.match.slice(a)+this._input},pastInput:function(){var a=this.matched.substr(0,this.matched.length-this.match.length);return(a.length>20?"...":"")+a.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var a=this.match;return a.length<20&&(a+=this._input.substr(0,20-a.length)),(a.substr(0,20)+(a.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var a=this.pastInput(),b=(new Array(a.length+1)).join("-");return a+this.upcomingInput()+"\n"+b+"^"},next:function(){if(this.done)return this.EOF;this._input||(this.done=!0);var a,b,c,d,e,f;this._more||(this.yytext="",this.match="");var g=this._currentRules();for(var h=0;h<g.length;h++){c=this._input.match(this.rules[g[h]]);if(c&&(!b||c[0].length>b[0].length)){b=c,d=h;if(!this.options.flex)break}}if(b){f=b[0].match(/\n.*/g),f&&(this.yylineno+=f.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:f?f[f.length-1].length-1:this.yylloc.last_column+b[0].length},this.yytext+=b[0],this.match+=b[0],this.yyleng=this.yytext.length,this._more=!1,this._input=this._input.slice(b[0].length),this.matched+=b[0],a=this.performAction.call(this,this.yy,this,g[d],this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1);if(a)return a;return}if(this._input==="")return this.EOF;this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var b=this.next();return typeof b!="undefined"?b:this.lex()},begin:function(b){this.conditionStack.push(b)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules},topState:function(){return this.conditionStack[this.conditionStack.length-2]},pushState:function(b){this.begin(b)}};return a.options={},a.performAction=function(b,c,d,e){var f=e;switch(d){case 0:break;case 1:return 6;case 2:return c.yytext=c.yytext.substr(1,c.yyleng-2),4;case 3:return 17;case 4:return 18;case 5:return 23;case 6:return 24;case 7:return 22;case 8:return 21;case 9:return 10;case 10:return 11;case 11:return 8;case 12:return 14;case 13:return"INVALID"}},a.rules=[/^(?:\s+)/,/^(?:(-?([0-9]|[1-9][0-9]+))(\.[0-9]+)?([eE][-+]?[0-9]+)?\b)/,/^(?:"(?:\\[\\"bfnrt/]|\\u[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*")/,/^(?:\{)/,/^(?:\})/,/^(?:\[)/,/^(?:\])/,/^(?:,)/,/^(?::)/,/^(?:true\b)/,/^(?:false\b)/,/^(?:null\b)/,/^(?:$)/,/^(?:.)/],a.conditions={INITIAL:{rules:[0,1,2,3,4,5,6,7,8,9,10,11,12,13],inclusive:!0}},a}();return a.lexer=b,a}();return typeof a!="undefined"&&typeof c!="undefined"&&(c.parser=d,c.parse=function(){return d.parse.apply(d,arguments)},c.main=function(d){if(!d[1])throw new Error("Usage: "+d[0]+" FILE");if(typeof process!="undefined")var e=a("fs").readFileSync(a("path").join(process.cwd(),d[1]),"utf8");else var f=a("file").path(a("file").cwd()),e=f.join(d[1]).read({charset:"utf-8"});return c.parser.parse(e)},typeof b!="undefined"&&a.main===b&&c.main(typeof process!="undefined"?process.argv.slice(1):a("system").args)),c}();
-
-/* ---- extension/lint/lint.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-  var GUTTER_ID = "CodeMirror-lint-markers";
-
-  function showTooltip(cm, e, content) {
-    var tt = document.createElement("div");
-    tt.className = "CodeMirror-lint-tooltip cm-s-" + cm.options.theme;
-    tt.appendChild(content.cloneNode(true));
-    if (cm.state.lint.options.selfContain)
-      cm.getWrapperElement().appendChild(tt);
-    else
-      document.body.appendChild(tt);
-
-    function position(e) {
-      if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position);
-      tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px";
-      tt.style.left = (e.clientX + 5) + "px";
-    }
-    CodeMirror.on(document, "mousemove", position);
-    position(e);
-    if (tt.style.opacity != null) tt.style.opacity = 1;
-    return tt;
-  }
-  function rm(elt) {
-    if (elt.parentNode) elt.parentNode.removeChild(elt);
-  }
-  function hideTooltip(tt) {
-    if (!tt.parentNode) return;
-    if (tt.style.opacity == null) rm(tt);
-    tt.style.opacity = 0;
-    setTimeout(function() { rm(tt); }, 600);
-  }
-
-  function showTooltipFor(cm, e, content, node) {
-    var tooltip = showTooltip(cm, e, content);
-    function hide() {
-      CodeMirror.off(node, "mouseout", hide);
-      if (tooltip) { hideTooltip(tooltip); tooltip = null; }
-    }
-    var poll = setInterval(function() {
-      if (tooltip) for (var n = node;; n = n.parentNode) {
-        if (n && n.nodeType == 11) n = n.host;
-        if (n == document.body) return;
-        if (!n) { hide(); break; }
-      }
-      if (!tooltip) return clearInterval(poll);
-    }, 400);
-    CodeMirror.on(node, "mouseout", hide);
-  }
-
-  function LintState(cm, options, hasGutter) {
-    this.marked = [];
-    this.options = options;
-    this.timeout = null;
-    this.hasGutter = hasGutter;
-    this.onMouseOver = function(e) { onMouseOver(cm, e); };
-    this.waitingFor = 0
-  }
-
-  function parseOptions(_cm, options) {
-    if (options instanceof Function) return {getAnnotations: options};
-    if (!options || options === true) options = {};
-    return options;
-  }
-
-  function clearMarks(cm) {
-    var state = cm.state.lint;
-    if (state.hasGutter) cm.clearGutter(GUTTER_ID);
-    for (var i = 0; i < state.marked.length; ++i)
-      state.marked[i].clear();
-    state.marked.length = 0;
-  }
-
-  function makeMarker(cm, labels, severity, multiple, tooltips) {
-    var marker = document.createElement("div"), inner = marker;
-    marker.className = "CodeMirror-lint-marker-" + severity;
-    if (multiple) {
-      inner = marker.appendChild(document.createElement("div"));
-      inner.className = "CodeMirror-lint-marker-multiple";
-    }
-
-    if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) {
-      showTooltipFor(cm, e, labels, inner);
-    });
-
-    return marker;
-  }
-
-  function getMaxSeverity(a, b) {
-    if (a == "error") return a;
-    else return b;
-  }
-
-  function groupByLine(annotations) {
-    var lines = [];
-    for (var i = 0; i < annotations.length; ++i) {
-      var ann = annotations[i], line = ann.from.line;
-      (lines[line] || (lines[line] = [])).push(ann);
-    }
-    return lines;
-  }
-
-  function annotationTooltip(ann) {
-    var severity = ann.severity;
-    if (!severity) severity = "error";
-    var tip = document.createElement("div");
-    tip.className = "CodeMirror-lint-message-" + severity;
-    if (typeof ann.messageHTML != 'undefined') {
-      tip.innerHTML = ann.messageHTML;
-    } else {
-      tip.appendChild(document.createTextNode(ann.message));
-    }
-    return tip;
-  }
-
-  function lintAsync(cm, getAnnotations, passOptions) {
-    var state = cm.state.lint
-    var id = ++state.waitingFor
-    function abort() {
-      id = -1
-      cm.off("change", abort)
-    }
-    cm.on("change", abort)
-    getAnnotations(cm.getValue(), function(annotations, arg2) {
-      cm.off("change", abort)
-      if (state.waitingFor != id) return
-      if (arg2 && annotations instanceof CodeMirror) annotations = arg2
-      cm.operation(function() {updateLinting(cm, annotations)})
-    }, passOptions, cm);
-  }
-
-  function startLinting(cm) {
-    var state = cm.state.lint, options = state.options;
-    /*
-     * Passing rules in `options` property prevents JSHint (and other linters) from complaining
-     * about unrecognized rules like `onUpdateLinting`, `delay`, `lintOnChange`, etc.
-     */
-    var passOptions = options.options || options;
-    var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), "lint");
-    if (!getAnnotations) return;
-    if (options.async || getAnnotations.async) {
-      lintAsync(cm, getAnnotations, passOptions)
-    } else {
-      var annotations = getAnnotations(cm.getValue(), passOptions, cm);
-      if (!annotations) return;
-      if (annotations.then) annotations.then(function(issues) {
-        cm.operation(function() {updateLinting(cm, issues)})
-      });
-      else cm.operation(function() {updateLinting(cm, annotations)})
-    }
-  }
-
-  function updateLinting(cm, annotationsNotSorted) {
-    clearMarks(cm);
-    var state = cm.state.lint, options = state.options;
-
-    var annotations = groupByLine(annotationsNotSorted);
-
-    for (var line = 0; line < annotations.length; ++line) {
-      var anns = annotations[line];
-      if (!anns) continue;
-
-      var maxSeverity = null;
-      var tipLabel = state.hasGutter && document.createDocumentFragment();
-
-      for (var i = 0; i < anns.length; ++i) {
-        var ann = anns[i];
-        var severity = ann.severity;
-        if (!severity) severity = "error";
-        maxSeverity = getMaxSeverity(maxSeverity, severity);
-
-        if (options.formatAnnotation) ann = options.formatAnnotation(ann);
-        if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann));
-
-        if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, {
-          className: "CodeMirror-lint-mark-" + severity,
-          __annotation: ann
-        }));
-      }
-
-      if (state.hasGutter)
-        cm.setGutterMarker(line, GUTTER_ID, makeMarker(cm, tipLabel, maxSeverity, anns.length > 1,
-                                                       state.options.tooltips));
-    }
-    if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm);
-  }
-
-  function onChange(cm) {
-    var state = cm.state.lint;
-    if (!state) return;
-    clearTimeout(state.timeout);
-    state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500);
-  }
-
-  function popupTooltips(cm, annotations, e) {
-    var target = e.target || e.srcElement;
-    var tooltip = document.createDocumentFragment();
-    for (var i = 0; i < annotations.length; i++) {
-      var ann = annotations[i];
-      tooltip.appendChild(annotationTooltip(ann));
-    }
-    showTooltipFor(cm, e, tooltip, target);
-  }
-
-  function onMouseOver(cm, e) {
-    var target = e.target || e.srcElement;
-    if (!/\bCodeMirror-lint-mark-/.test(target.className)) return;
-    var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2;
-    var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client"));
-
-    var annotations = [];
-    for (var i = 0; i < spans.length; ++i) {
-      var ann = spans[i].__annotation;
-      if (ann) annotations.push(ann);
-    }
-    if (annotations.length) popupTooltips(cm, annotations, e);
-  }
-
-  CodeMirror.defineOption("lint", false, function(cm, val, old) {
-    if (old && old != CodeMirror.Init) {
-      clearMarks(cm);
-      if (cm.state.lint.options.lintOnChange !== false)
-        cm.off("change", onChange);
-      CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver);
-      clearTimeout(cm.state.lint.timeout);
-      delete cm.state.lint;
-    }
-
-    if (val) {
-      var gutters = cm.getOption("gutters"), hasLintGutter = false;
-      for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true;
-      var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter);
-      if (state.options.lintOnChange !== false)
-        cm.on("change", onChange);
-      if (state.options.tooltips != false && state.options.tooltips != "gutter")
-        CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver);
-
-      startLinting(cm);
-    }
-  });
-
-  CodeMirror.defineExtension("performLint", function() {
-    if (this.state.lint) startLinting(this);
-  });
-});
-
-
-/* ---- extension/scroll/annotatescrollbar.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  CodeMirror.defineExtension("annotateScrollbar", function(options) {
-    if (typeof options == "string") options = {className: options};
-    return new Annotation(this, options);
-  });
-
-  CodeMirror.defineOption("scrollButtonHeight", 0);
-
-  function Annotation(cm, options) {
-    this.cm = cm;
-    this.options = options;
-    this.buttonHeight = options.scrollButtonHeight || cm.getOption("scrollButtonHeight");
-    this.annotations = [];
-    this.doRedraw = this.doUpdate = null;
-    this.div = cm.getWrapperElement().appendChild(document.createElement("div"));
-    this.div.style.cssText = "position: absolute; right: 0; top: 0; z-index: 7; pointer-events: none";
-    this.computeScale();
-
-    function scheduleRedraw(delay) {
-      clearTimeout(self.doRedraw);
-      self.doRedraw = setTimeout(function() { self.redraw(); }, delay);
-    }
-
-    var self = this;
-    cm.on("refresh", this.resizeHandler = function() {
-      clearTimeout(self.doUpdate);
-      self.doUpdate = setTimeout(function() {
-        if (self.computeScale()) scheduleRedraw(20);
-      }, 100);
-    });
-    cm.on("markerAdded", this.resizeHandler);
-    cm.on("markerCleared", this.resizeHandler);
-    if (options.listenForChanges !== false)
-      cm.on("changes", this.changeHandler = function() {
-        scheduleRedraw(250);
-      });
-  }
-
-  Annotation.prototype.computeScale = function() {
-    var cm = this.cm;
-    var hScale = (cm.getWrapperElement().clientHeight - cm.display.barHeight - this.buttonHeight * 2) /
-      cm.getScrollerElement().scrollHeight
-    if (hScale != this.hScale) {
-      this.hScale = hScale;
-      return true;
-    }
-  };
-
-  Annotation.prototype.update = function(annotations) {
-    this.annotations = annotations;
-    this.redraw();
-  };
-
-  Annotation.prototype.redraw = function(compute) {
-    if (compute !== false) this.computeScale();
-    var cm = this.cm, hScale = this.hScale;
-
-    var frag = document.createDocumentFragment(), anns = this.annotations;
-
-    var wrapping = cm.getOption("lineWrapping");
-    var singleLineH = wrapping && cm.defaultTextHeight() * 1.5;
-    var curLine = null, curLineObj = null;
-    function getY(pos, top) {
-      if (curLine != pos.line) {
-        curLine = pos.line;
-        curLineObj = cm.getLineHandle(curLine);
-      }
-      if ((curLineObj.widgets && curLineObj.widgets.length) ||
-          (wrapping && curLineObj.height > singleLineH))
-        return cm.charCoords(pos, "local")[top ? "top" : "bottom"];
-      var topY = cm.heightAtLine(curLineObj, "local");
-      return topY + (top ? 0 : curLineObj.height);
-    }
-
-    var lastLine = cm.lastLine()
-    if (cm.display.barWidth) for (var i = 0, nextTop; i < anns.length; i++) {
-      var ann = anns[i];
-      if (ann.to.line > lastLine) continue;
-      var top = nextTop || getY(ann.from, true) * hScale;
-      var bottom = getY(ann.to, false) * hScale;
-      while (i < anns.length - 1) {
-        if (anns[i + 1].to.line > lastLine) break;
-        nextTop = getY(anns[i + 1].from, true) * hScale;
-        if (nextTop > bottom + .9) break;
-        ann = anns[++i];
-        bottom = getY(ann.to, false) * hScale;
-      }
-      if (bottom == top) continue;
-      var height = Math.max(bottom - top, 3);
-
-      var elt = frag.appendChild(document.createElement("div"));
-      elt.style.cssText = "position: absolute; right: 0px; width: " + Math.max(cm.display.barWidth - 1, 2) + "px; top: "
-        + (top + this.buttonHeight) + "px; height: " + height + "px";
-      elt.className = this.options.className;
-      if (ann.id) {
-        elt.setAttribute("annotation-id", ann.id);
-      }
-    }
-    this.div.textContent = "";
-    this.div.appendChild(frag);
-  };
-
-  Annotation.prototype.clear = function() {
-    this.cm.off("refresh", this.resizeHandler);
-    this.cm.off("markerAdded", this.resizeHandler);
-    this.cm.off("markerCleared", this.resizeHandler);
-    if (this.changeHandler) this.cm.off("changes", this.changeHandler);
-    this.div.parentNode.removeChild(this.div);
-  };
-});
-
-
-/* ---- extension/scroll/scrollpastend.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  CodeMirror.defineOption("scrollPastEnd", false, function(cm, val, old) {
-    if (old && old != CodeMirror.Init) {
-      cm.off("change", onChange);
-      cm.off("refresh", updateBottomMargin);
-      cm.display.lineSpace.parentNode.style.paddingBottom = "";
-      cm.state.scrollPastEndPadding = null;
-    }
-    if (val) {
-      cm.on("change", onChange);
-      cm.on("refresh", updateBottomMargin);
-      updateBottomMargin(cm);
-    }
-  });
-
-  function onChange(cm, change) {
-    if (CodeMirror.changeEnd(change).line == cm.lastLine())
-      updateBottomMargin(cm);
-  }
-
-  function updateBottomMargin(cm) {
-    var padding = "";
-    if (cm.lineCount() > 1) {
-      var totalH = cm.display.scroller.clientHeight - 30,
-          lastLineH = cm.getLineHandle(cm.lastLine()).height;
-      padding = (totalH - lastLineH) + "px";
-    }
-    if (cm.state.scrollPastEndPadding != padding) {
-      cm.state.scrollPastEndPadding = padding;
-      cm.display.lineSpace.parentNode.style.paddingBottom = padding;
-      cm.off("refresh", updateBottomMargin);
-      cm.setSize();
-      cm.on("refresh", updateBottomMargin);
-    }
-  }
-});
-
-
-/* ---- extension/scroll/simplescrollbars.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  function Bar(cls, orientation, scroll) {
-    this.orientation = orientation;
-    this.scroll = scroll;
-    this.screen = this.total = this.size = 1;
-    this.pos = 0;
-
-    this.node = document.createElement("div");
-    this.node.className = cls + "-" + orientation;
-    this.inner = this.node.appendChild(document.createElement("div"));
-
-    var self = this;
-    CodeMirror.on(this.inner, "mousedown", function(e) {
-      if (e.which != 1) return;
-      CodeMirror.e_preventDefault(e);
-      var axis = self.orientation == "horizontal" ? "pageX" : "pageY";
-      var start = e[axis], startpos = self.pos;
-      function done() {
-        CodeMirror.off(document, "mousemove", move);
-        CodeMirror.off(document, "mouseup", done);
-      }
-      function move(e) {
-        if (e.which != 1) return done();
-        self.moveTo(startpos + (e[axis] - start) * (self.total / self.size));
-      }
-      CodeMirror.on(document, "mousemove", move);
-      CodeMirror.on(document, "mouseup", done);
-    });
-
-    CodeMirror.on(this.node, "click", function(e) {
-      CodeMirror.e_preventDefault(e);
-      var innerBox = self.inner.getBoundingClientRect(), where;
-      if (self.orientation == "horizontal")
-        where = e.clientX < innerBox.left ? -1 : e.clientX > innerBox.right ? 1 : 0;
-      else
-        where = e.clientY < innerBox.top ? -1 : e.clientY > innerBox.bottom ? 1 : 0;
-      self.moveTo(self.pos + where * self.screen);
-    });
-
-    function onWheel(e) {
-      var moved = CodeMirror.wheelEventPixels(e)[self.orientation == "horizontal" ? "x" : "y"];
-      var oldPos = self.pos;
-      self.moveTo(self.pos + moved);
-      if (self.pos != oldPos) CodeMirror.e_preventDefault(e);
-    }
-    CodeMirror.on(this.node, "mousewheel", onWheel);
-    CodeMirror.on(this.node, "DOMMouseScroll", onWheel);
-  }
-
-  Bar.prototype.setPos = function(pos, force) {
-    if (pos < 0) pos = 0;
-    if (pos > this.total - this.screen) pos = this.total - this.screen;
-    if (!force && pos == this.pos) return false;
-    this.pos = pos;
-    this.inner.style[this.orientation == "horizontal" ? "left" : "top"] =
-      (pos * (this.size / this.total)) + "px";
-    return true
-  };
-
-  Bar.prototype.moveTo = function(pos) {
-    if (this.setPos(pos)) this.scroll(pos, this.orientation);
-  }
-
-  var minButtonSize = 10;
-
-  Bar.prototype.update = function(scrollSize, clientSize, barSize) {
-    var sizeChanged = this.screen != clientSize || this.total != scrollSize || this.size != barSize
-    if (sizeChanged) {
-      this.screen = clientSize;
-      this.total = scrollSize;
-      this.size = barSize;
-    }
-
-    var buttonSize = this.screen * (this.size / this.total);
-    if (buttonSize < minButtonSize) {
-      this.size -= minButtonSize - buttonSize;
-      buttonSize = minButtonSize;
-    }
-    this.inner.style[this.orientation == "horizontal" ? "width" : "height"] =
-      buttonSize + "px";
-    this.setPos(this.pos, sizeChanged);
-  };
-
-  function SimpleScrollbars(cls, place, scroll) {
-    this.addClass = cls;
-    this.horiz = new Bar(cls, "horizontal", scroll);
-    place(this.horiz.node);
-    this.vert = new Bar(cls, "vertical", scroll);
-    place(this.vert.node);
-    this.width = null;
-  }
-
-  SimpleScrollbars.prototype.update = function(measure) {
-    if (this.width == null) {
-      var style = window.getComputedStyle ? window.getComputedStyle(this.horiz.node) : this.horiz.node.currentStyle;
-      if (style) this.width = parseInt(style.height);
-    }
-    var width = this.width || 0;
-
-    var needsH = measure.scrollWidth > measure.clientWidth + 1;
-    var needsV = measure.scrollHeight > measure.clientHeight + 1;
-    this.vert.node.style.display = needsV ? "block" : "none";
-    this.horiz.node.style.display = needsH ? "block" : "none";
-
-    if (needsV) {
-      this.vert.update(measure.scrollHeight, measure.clientHeight,
-                       measure.viewHeight - (needsH ? width : 0));
-      this.vert.node.style.bottom = needsH ? width + "px" : "0";
-    }
-    if (needsH) {
-      this.horiz.update(measure.scrollWidth, measure.clientWidth,
-                        measure.viewWidth - (needsV ? width : 0) - measure.barLeft);
-      this.horiz.node.style.right = needsV ? width + "px" : "0";
-      this.horiz.node.style.left = measure.barLeft + "px";
-    }
-
-    return {right: needsV ? width : 0, bottom: needsH ? width : 0};
-  };
-
-  SimpleScrollbars.prototype.setScrollTop = function(pos) {
-    this.vert.setPos(pos);
-  };
-
-  SimpleScrollbars.prototype.setScrollLeft = function(pos) {
-    this.horiz.setPos(pos);
-  };
-
-  SimpleScrollbars.prototype.clear = function() {
-    var parent = this.horiz.node.parentNode;
-    parent.removeChild(this.horiz.node);
-    parent.removeChild(this.vert.node);
-  };
-
-  CodeMirror.scrollbarModel.simple = function(place, scroll) {
-    return new SimpleScrollbars("CodeMirror-simplescroll", place, scroll);
-  };
-  CodeMirror.scrollbarModel.overlay = function(place, scroll) {
-    return new SimpleScrollbars("CodeMirror-overlayscroll", place, scroll);
-  };
-});
-
-
-/* ---- extension/search/jump-to-line.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-// Defines jumpToLine command. Uses dialog.js if present.
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("../dialog/dialog"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "../dialog/dialog"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  function dialog(cm, text, shortText, deflt, f) {
-    if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true});
-    else f(prompt(shortText, deflt));
-  }
-
-  function getJumpDialog(cm) {
-    return cm.phrase("Jump to line:") + ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">' + cm.phrase("(Use line:column or scroll% syntax)") + '</span>';
-  }
-
-  function interpretLine(cm, string) {
-    var num = Number(string)
-    if (/^[-+]/.test(string)) return cm.getCursor().line + num
-    else return num - 1
-  }
-
-  CodeMirror.commands.jumpToLine = function(cm) {
-    var cur = cm.getCursor();
-    dialog(cm, getJumpDialog(cm), cm.phrase("Jump to line:"), (cur.line + 1) + ":" + cur.ch, function(posStr) {
-      if (!posStr) return;
-
-      var match;
-      if (match = /^\s*([\+\-]?\d+)\s*\:\s*(\d+)\s*$/.exec(posStr)) {
-        cm.setCursor(interpretLine(cm, match[1]), Number(match[2]))
-      } else if (match = /^\s*([\+\-]?\d+(\.\d+)?)\%\s*/.exec(posStr)) {
-        var line = Math.round(cm.lineCount() * Number(match[1]) / 100);
-        if (/^[-+]/.test(match[1])) line = cur.line + line + 1;
-        cm.setCursor(line - 1, cur.ch);
-      } else if (match = /^\s*\:?\s*([\+\-]?\d+)\s*/.exec(posStr)) {
-        cm.setCursor(interpretLine(cm, match[1]), cur.ch);
-      }
-    });
-  };
-
-  CodeMirror.keyMap["default"]["Alt-G"] = "jumpToLine";
-});
-
-
-/* ---- extension/search/match-highlighter.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-// Highlighting text that matches the selection
-//
-// Defines an option highlightSelectionMatches, which, when enabled,
-// will style strings that match the selection throughout the
-// document.
-//
-// The option can be set to true to simply enable it, or to a
-// {minChars, style, wordsOnly, showToken, delay} object to explicitly
-// configure it. minChars is the minimum amount of characters that should be
-// selected for the behavior to occur, and style is the token style to
-// apply to the matches. This will be prefixed by "cm-" to create an
-// actual CSS class name. If wordsOnly is enabled, the matches will be
-// highlighted only if the selected text is a word. showToken, when enabled,
-// will cause the current token to be highlighted when nothing is selected.
-// delay is used to specify how much time to wait, in milliseconds, before
-// highlighting the matches. If annotateScrollbar is enabled, the occurences
-// will be highlighted on the scrollbar via the matchesonscrollbar addon.
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("./matchesonscrollbar"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "./matchesonscrollbar"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  var defaults = {
-    style: "matchhighlight",
-    minChars: 2,
-    delay: 100,
-    wordsOnly: false,
-    annotateScrollbar: false,
-    showToken: false,
-    trim: true
-  }
-
-  function State(options) {
-    this.options = {}
-    for (var name in defaults)
-      this.options[name] = (options && options.hasOwnProperty(name) ? options : defaults)[name]
-    this.overlay = this.timeout = null;
-    this.matchesonscroll = null;
-    this.active = false;
-  }
-
-  CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) {
-    if (old && old != CodeMirror.Init) {
-      removeOverlay(cm);
-      clearTimeout(cm.state.matchHighlighter.timeout);
-      cm.state.matchHighlighter = null;
-      cm.off("cursorActivity", cursorActivity);
-      cm.off("focus", onFocus)
-    }
-    if (val) {
-      var state = cm.state.matchHighlighter = new State(val);
-      if (cm.hasFocus()) {
-        state.active = true
-        highlightMatches(cm)
-      } else {
-        cm.on("focus", onFocus)
-      }
-      cm.on("cursorActivity", cursorActivity);
-    }
-  });
-
-  function cursorActivity(cm) {
-    var state = cm.state.matchHighlighter;
-    if (state.active || cm.hasFocus()) scheduleHighlight(cm, state)
-  }
-
-  function onFocus(cm) {
-    var state = cm.state.matchHighlighter
-    if (!state.active) {
-      state.active = true
-      scheduleHighlight(cm, state)
-    }
-  }
-
-  function scheduleHighlight(cm, state) {
-    clearTimeout(state.timeout);
-    state.timeout = setTimeout(function() {highlightMatches(cm);}, state.options.delay);
-  }
-
-  function addOverlay(cm, query, hasBoundary, style) {
-    var state = cm.state.matchHighlighter;
-    cm.addOverlay(state.overlay = makeOverlay(query, hasBoundary, style));
-    if (state.options.annotateScrollbar && cm.showMatchesOnScrollbar) {
-      var searchFor = hasBoundary ? new RegExp((/\w/.test(query.charAt(0)) ? "\\b" : "") +
-                                               query.replace(/[\\\[.+*?(){|^$]/g, "\\$&") +
-                                               (/\w/.test(query.charAt(query.length - 1)) ? "\\b" : "")) : query;
-      state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, false,
-        {className: "CodeMirror-selection-highlight-scrollbar"});
-    }
-  }
-
-  function removeOverlay(cm) {
-    var state = cm.state.matchHighlighter;
-    if (state.overlay) {
-      cm.removeOverlay(state.overlay);
-      state.overlay = null;
-      if (state.matchesonscroll) {
-        state.matchesonscroll.clear();
-        state.matchesonscroll = null;
-      }
-    }
-  }
-
-  function highlightMatches(cm) {
-    cm.operation(function() {
-      var state = cm.state.matchHighlighter;
-      removeOverlay(cm);
-      if (!cm.somethingSelected() && state.options.showToken) {
-        var re = state.options.showToken === true ? /[\w$]/ : state.options.showToken;
-        var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start;
-        while (start && re.test(line.charAt(start - 1))) --start;
-        while (end < line.length && re.test(line.charAt(end))) ++end;
-        if (start < end)
-          addOverlay(cm, line.slice(start, end), re, state.options.style);
-        return;
-      }
-      var from = cm.getCursor("from"), to = cm.getCursor("to");
-      if (from.line != to.line) return;
-      if (state.options.wordsOnly && !isWord(cm, from, to)) return;
-      var selection = cm.getRange(from, to)
-      if (state.options.trim) selection = selection.replace(/^\s+|\s+$/g, "")
-      if (selection.length >= state.options.minChars)
-        addOverlay(cm, selection, false, state.options.style);
-    });
-  }
-
-  function isWord(cm, from, to) {
-    var str = cm.getRange(from, to);
-    if (str.match(/^\w+$/) !== null) {
-        if (from.ch > 0) {
-            var pos = {line: from.line, ch: from.ch - 1};
-            var chr = cm.getRange(pos, from);
-            if (chr.match(/\W/) === null) return false;
-        }
-        if (to.ch < cm.getLine(from.line).length) {
-            var pos = {line: to.line, ch: to.ch + 1};
-            var chr = cm.getRange(to, pos);
-            if (chr.match(/\W/) === null) return false;
-        }
-        return true;
-    } else return false;
-  }
-
-  function boundariesAround(stream, re) {
-    return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) &&
-      (stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos)));
-  }
-
-  function makeOverlay(query, hasBoundary, style) {
-    return {token: function(stream) {
-      if (stream.match(query) &&
-          (!hasBoundary || boundariesAround(stream, hasBoundary)))
-        return style;
-      stream.next();
-      stream.skipTo(query.charAt(0)) || stream.skipToEnd();
-    }};
-  }
-});
-
-
-/* ---- extension/search/matchesonscrollbar.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("./searchcursor"), require("../scroll/annotatescrollbar"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "./searchcursor", "../scroll/annotatescrollbar"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  CodeMirror.defineExtension("showMatchesOnScrollbar", function(query, caseFold, options) {
-    if (typeof options == "string") options = {className: options};
-    if (!options) options = {};
-    return new SearchAnnotation(this, query, caseFold, options);
-  });
-
-  function SearchAnnotation(cm, query, caseFold, options) {
-    this.cm = cm;
-    this.options = options;
-    var annotateOptions = {listenForChanges: false};
-    for (var prop in options) annotateOptions[prop] = options[prop];
-    if (!annotateOptions.className) annotateOptions.className = "CodeMirror-search-match";
-    this.annotation = cm.annotateScrollbar(annotateOptions);
-    this.query = query;
-    this.caseFold = caseFold;
-    this.gap = {from: cm.firstLine(), to: cm.lastLine() + 1};
-    this.matches = [];
-    this.update = null;
-
-    this.findMatches();
-    this.annotation.update(this.matches);
-
-    var self = this;
-    cm.on("change", this.changeHandler = function(_cm, change) { self.onChange(change); });
-  }
-
-  var MAX_MATCHES = 1000;
-
-  SearchAnnotation.prototype.findMatches = function() {
-    if (!this.gap) return;
-    for (var i = 0; i < this.matches.length; i++) {
-      var match = this.matches[i];
-      if (match.from.line >= this.gap.to) break;
-      if (match.to.line >= this.gap.from) this.matches.splice(i--, 1);
-    }
-    var cursor = this.cm.getSearchCursor(this.query, CodeMirror.Pos(this.gap.from, 0), {caseFold: this.caseFold, multiline: this.options.multiline});
-    var maxMatches = this.options && this.options.maxMatches || MAX_MATCHES;
-    while (cursor.findNext()) {
-      var match = {from: cursor.from(), to: cursor.to()};
-      if (match.from.line >= this.gap.to) break;
-      this.matches.splice(i++, 0, match);
-      if (this.matches.length > maxMatches) break;
-    }
-    this.gap = null;
-  };
-
-  function offsetLine(line, changeStart, sizeChange) {
-    if (line <= changeStart) return line;
-    return Math.max(changeStart, line + sizeChange);
-  }
-
-  SearchAnnotation.prototype.onChange = function(change) {
-    var startLine = change.from.line;
-    var endLine = CodeMirror.changeEnd(change).line;
-    var sizeChange = endLine - change.to.line;
-    if (this.gap) {
-      this.gap.from = Math.min(offsetLine(this.gap.from, startLine, sizeChange), change.from.line);
-      this.gap.to = Math.max(offsetLine(this.gap.to, startLine, sizeChange), change.from.line);
-    } else {
-      this.gap = {from: change.from.line, to: endLine + 1};
-    }
-
-    if (sizeChange) for (var i = 0; i < this.matches.length; i++) {
-      var match = this.matches[i];
-      var newFrom = offsetLine(match.from.line, startLine, sizeChange);
-      if (newFrom != match.from.line) match.from = CodeMirror.Pos(newFrom, match.from.ch);
-      var newTo = offsetLine(match.to.line, startLine, sizeChange);
-      if (newTo != match.to.line) match.to = CodeMirror.Pos(newTo, match.to.ch);
-    }
-    clearTimeout(this.update);
-    var self = this;
-    this.update = setTimeout(function() { self.updateAfterChange(); }, 250);
-  };
-
-  SearchAnnotation.prototype.updateAfterChange = function() {
-    this.findMatches();
-    this.annotation.update(this.matches);
-  };
-
-  SearchAnnotation.prototype.clear = function() {
-    this.cm.off("change", this.changeHandler);
-    this.annotation.clear();
-  };
-});
-
-
-/* ---- extension/search/search.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-// Define search commands. Depends on dialog.js or another
-// implementation of the openDialog method.
-
-// Replace works a little oddly -- it will do the replace on the next
-// Ctrl-G (or whatever is bound to findNext) press. You prevent a
-// replace by making sure the match is no longer selected when hitting
-// Ctrl-G.
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("./searchcursor"), require("../dialog/dialog"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "./searchcursor", "../dialog/dialog"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  function searchOverlay(query, caseInsensitive) {
-    if (typeof query == "string")
-      query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), caseInsensitive ? "gi" : "g");
-    else if (!query.global)
-      query = new RegExp(query.source, query.ignoreCase ? "gi" : "g");
-
-    return {token: function(stream) {
-      query.lastIndex = stream.pos;
-      var match = query.exec(stream.string);
-      if (match && match.index == stream.pos) {
-        stream.pos += match[0].length || 1;
-        return "searching";
-      } else if (match) {
-        stream.pos = match.index;
-      } else {
-        stream.skipToEnd();
-      }
-    }};
-  }
-
-  function SearchState() {
-    this.posFrom = this.posTo = this.lastQuery = this.query = null;
-    this.overlay = null;
-  }
-
-  function getSearchState(cm) {
-    return cm.state.search || (cm.state.search = new SearchState());
-  }
-
-  function queryCaseInsensitive(query) {
-    return typeof query == "string" && query == query.toLowerCase();
-  }
-
-  function getSearchCursor(cm, query, pos) {
-    // Heuristic: if the query string is all lowercase, do a case insensitive search.
-    return cm.getSearchCursor(query, pos, {caseFold: queryCaseInsensitive(query), multiline: true});
-  }
-
-  function persistentDialog(cm, text, deflt, onEnter, onKeyDown) {
-    cm.openDialog(text, onEnter, {
-      value: deflt,
-      selectValueOnOpen: true,
-      closeOnEnter: false,
-      onClose: function() { clearSearch(cm); },
-      onKeyDown: onKeyDown
-    });
-  }
-
-  function dialog(cm, text, shortText, deflt, f) {
-    if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true});
-    else f(prompt(shortText, deflt));
-  }
-
-  function confirmDialog(cm, text, shortText, fs) {
-    if (cm.openConfirm) cm.openConfirm(text, fs);
-    else if (confirm(shortText)) fs[0]();
-  }
-
-  function parseString(string) {
-    return string.replace(/\\([nrt\\])/g, function(match, ch) {
-      if (ch == "n") return "\n"
-      if (ch == "r") return "\r"
-      if (ch == "t") return "\t"
-      if (ch == "\\") return "\\"
-      return match
-    })
-  }
-
-  function parseQuery(query) {
-    var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
-    if (isRE) {
-      try { query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i"); }
-      catch(e) {} // Not a regular expression after all, do a string search
-    } else {
-      query = parseString(query)
-    }
-    if (typeof query == "string" ? query == "" : query.test(""))
-      query = /x^/;
-    return query;
-  }
-
-  function startSearch(cm, state, query) {
-    state.queryText = query;
-    state.query = parseQuery(query);
-    cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
-    state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
-    cm.addOverlay(state.overlay);
-    if (cm.showMatchesOnScrollbar) {
-      if (state.annotate) { state.annotate.clear(); state.annotate = null; }
-      state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));
-    }
-  }
-
-  function doSearch(cm, rev, persistent, immediate) {
-    var state = getSearchState(cm);
-    if (state.query) return findNext(cm, rev);
-    var q = cm.getSelection() || state.lastQuery;
-    if (q instanceof RegExp && q.source == "x^") q = null
-    if (persistent && cm.openDialog) {
-      var hiding = null
-      var searchNext = function(query, event) {
-        CodeMirror.e_stop(event);
-        if (!query) return;
-        if (query != state.queryText) {
-          startSearch(cm, state, query);
-          state.posFrom = state.posTo = cm.getCursor();
-        }
-        if (hiding) hiding.style.opacity = 1
-        findNext(cm, event.shiftKey, function(_, to) {
-          var dialog
-          if (to.line < 3 && document.querySelector &&
-              (dialog = cm.display.wrapper.querySelector(".CodeMirror-dialog")) &&
-              dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, "window").top)
-            (hiding = dialog).style.opacity = .4
-        })
-      };
-      persistentDialog(cm, getQueryDialog(cm), q, searchNext, function(event, query) {
-        var keyName = CodeMirror.keyName(event)
-        var extra = cm.getOption('extraKeys'), cmd = (extra && extra[keyName]) || CodeMirror.keyMap[cm.getOption("keyMap")][keyName]
-        if (cmd == "findNext" || cmd == "findPrev" ||
-          cmd == "findPersistentNext" || cmd == "findPersistentPrev") {
-          CodeMirror.e_stop(event);
-          startSearch(cm, getSearchState(cm), query);
-          cm.execCommand(cmd);
-        } else if (cmd == "find" || cmd == "findPersistent") {
-          CodeMirror.e_stop(event);
-          searchNext(query, event);
-        }
-      });
-      if (immediate && q) {
-        startSearch(cm, state, q);
-        findNext(cm, rev);
-      }
-    } else {
-      dialog(cm, getQueryDialog(cm), "Search for:", q, function(query) {
-        if (query && !state.query) cm.operation(function() {
-          startSearch(cm, state, query);
-          state.posFrom = state.posTo = cm.getCursor();
-          findNext(cm, rev);
-        });
-      });
-    }
-  }
-
-  function findNext(cm, rev, callback) {cm.operation(function() {
-    var state = getSearchState(cm);
-    var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
-    if (!cursor.find(rev)) {
-      cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
-      if (!cursor.find(rev)) return;
-    }
-    cm.setSelection(cursor.from(), cursor.to());
-    cm.scrollIntoView({from: cursor.from(), to: cursor.to()}, 20);
-    state.posFrom = cursor.from(); state.posTo = cursor.to();
-    if (callback) callback(cursor.from(), cursor.to())
-  });}
-
-  function clearSearch(cm) {cm.operation(function() {
-    var state = getSearchState(cm);
-    state.lastQuery = state.query;
-    if (!state.query) return;
-    state.query = state.queryText = null;
-    cm.removeOverlay(state.overlay);
-    if (state.annotate) { state.annotate.clear(); state.annotate = null; }
-  });}
-
-
-  function getQueryDialog(cm)  {
-    return '<span class="CodeMirror-search-label">' + cm.phrase("Search:") + '</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">' + cm.phrase("(Use /re/ syntax for regexp search)") + '</span>';
-  }
-  function getReplaceQueryDialog(cm) {
-    return ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">' + cm.phrase("(Use /re/ syntax for regexp search)") + '</span>';
-  }
-  function getReplacementQueryDialog(cm) {
-    return '<span class="CodeMirror-search-label">' + cm.phrase("With:") + '</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/>';
-  }
-  function getDoReplaceConfirm(cm) {
-    return '<span class="CodeMirror-search-label">' + cm.phrase("Replace?") + '</span> <button>' + cm.phrase("Yes") + '</button> <button>' + cm.phrase("No") + '</button> <button>' + cm.phrase("All") + '</button> <button>' + cm.phrase("Stop") + '</button> ';
-  }
-
-  function replaceAll(cm, query, text) {
-    cm.operation(function() {
-      for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
-        if (typeof query != "string") {
-          var match = cm.getRange(cursor.from(), cursor.to()).match(query);
-          cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
-        } else cursor.replace(text);
-      }
-    });
-  }
-
-  function replace(cm, all) {
-    if (cm.getOption("readOnly")) return;
-    var query = cm.getSelection() || getSearchState(cm).lastQuery;
-    var dialogText = '<span class="CodeMirror-search-label">' + (all ? cm.phrase("Replace all:") : cm.phrase("Replace:")) + '</span>';
-    dialog(cm, dialogText + getReplaceQueryDialog(cm), dialogText, query, function(query) {
-      if (!query) return;
-      query = parseQuery(query);
-      dialog(cm, getReplacementQueryDialog(cm), cm.phrase("Replace with:"), "", function(text) {
-        text = parseString(text)
-        if (all) {
-          replaceAll(cm, query, text)
-        } else {
-          clearSearch(cm);
-          var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
-          var advance = function() {
-            var start = cursor.from(), match;
-            if (!(match = cursor.findNext())) {
-              cursor = getSearchCursor(cm, query);
-              if (!(match = cursor.findNext()) ||
-                  (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
-            }
-            cm.setSelection(cursor.from(), cursor.to());
-            cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
-            confirmDialog(cm, getDoReplaceConfirm(cm), cm.phrase("Replace?"),
-                          [function() {doReplace(match);}, advance,
-                           function() {replaceAll(cm, query, text)}]);
-          };
-          var doReplace = function(match) {
-            cursor.replace(typeof query == "string" ? text :
-                           text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
-            advance();
-          };
-          advance();
-        }
-      });
-    });
-  }
-
-  CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
-  CodeMirror.commands.findPersistent = function(cm) {clearSearch(cm); doSearch(cm, false, true);};
-  CodeMirror.commands.findPersistentNext = function(cm) {doSearch(cm, false, true, true);};
-  CodeMirror.commands.findPersistentPrev = function(cm) {doSearch(cm, true, true, true);};
-  CodeMirror.commands.findNext = doSearch;
-  CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
-  CodeMirror.commands.clearSearch = clearSearch;
-  CodeMirror.commands.replace = replace;
-  CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
-});
-
-
-/* ---- extension/search/searchcursor.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"))
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod)
-  else // Plain browser env
-    mod(CodeMirror)
-})(function(CodeMirror) {
-  "use strict"
-  var Pos = CodeMirror.Pos
-
-  function regexpFlags(regexp) {
-    var flags = regexp.flags
-    return flags != null ? flags : (regexp.ignoreCase ? "i" : "")
-      + (regexp.global ? "g" : "")
-      + (regexp.multiline ? "m" : "")
-  }
-
-  function ensureFlags(regexp, flags) {
-    var current = regexpFlags(regexp), target = current
-    for (var i = 0; i < flags.length; i++) if (target.indexOf(flags.charAt(i)) == -1)
-      target += flags.charAt(i)
-    return current == target ? regexp : new RegExp(regexp.source, target)
-  }
-
-  function maybeMultiline(regexp) {
-    return /\\s|\\n|\n|\\W|\\D|\[\^/.test(regexp.source)
-  }
-
-  function searchRegexpForward(doc, regexp, start) {
-    regexp = ensureFlags(regexp, "g")
-    for (var line = start.line, ch = start.ch, last = doc.lastLine(); line <= last; line++, ch = 0) {
-      regexp.lastIndex = ch
-      var string = doc.getLine(line), match = regexp.exec(string)
-      if (match)
-        return {from: Pos(line, match.index),
-                to: Pos(line, match.index + match[0].length),
-                match: match}
-    }
-  }
-
-  function searchRegexpForwardMultiline(doc, regexp, start) {
-    if (!maybeMultiline(regexp)) return searchRegexpForward(doc, regexp, start)
-
-    regexp = ensureFlags(regexp, "gm")
-    var string, chunk = 1
-    for (var line = start.line, last = doc.lastLine(); line <= last;) {
-      // This grows the search buffer in exponentially-sized chunks
-      // between matches, so that nearby matches are fast and don't
-      // require concatenating the whole document (in case we're
-      // searching for something that has tons of matches), but at the
-      // same time, the amount of retries is limited.
-      for (var i = 0; i < chunk; i++) {
-        if (line > last) break
-        var curLine = doc.getLine(line++)
-        string = string == null ? curLine : string + "\n" + curLine
-      }
-      chunk = chunk * 2
-      regexp.lastIndex = start.ch
-      var match = regexp.exec(string)
-      if (match) {
-        var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n")
-        var startLine = start.line + before.length - 1, startCh = before[before.length - 1].length
-        return {from: Pos(startLine, startCh),
-                to: Pos(startLine + inside.length - 1,
-                        inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),
-                match: match}
-      }
-    }
-  }
-
-  function lastMatchIn(string, regexp, endMargin) {
-    var match, from = 0
-    while (from <= string.length) {
-      regexp.lastIndex = from
-      var newMatch = regexp.exec(string)
-      if (!newMatch) break
-      var end = newMatch.index + newMatch[0].length
-      if (end > string.length - endMargin) break
-      if (!match || end > match.index + match[0].length)
-        match = newMatch
-      from = newMatch.index + 1
-    }
-    return match
-  }
-
-  function searchRegexpBackward(doc, regexp, start) {
-    regexp = ensureFlags(regexp, "g")
-    for (var line = start.line, ch = start.ch, first = doc.firstLine(); line >= first; line--, ch = -1) {
-      var string = doc.getLine(line)
-      var match = lastMatchIn(string, regexp, ch < 0 ? 0 : string.length - ch)
-      if (match)
-        return {from: Pos(line, match.index),
-                to: Pos(line, match.index + match[0].length),
-                match: match}
-    }
-  }
-
-  function searchRegexpBackwardMultiline(doc, regexp, start) {
-    if (!maybeMultiline(regexp)) return searchRegexpBackward(doc, regexp, start)
-    regexp = ensureFlags(regexp, "gm")
-    var string, chunkSize = 1, endMargin = doc.getLine(start.line).length - start.ch
-    for (var line = start.line, first = doc.firstLine(); line >= first;) {
-      for (var i = 0; i < chunkSize && line >= first; i++) {
-        var curLine = doc.getLine(line--)
-        string = string == null ? curLine : curLine + "\n" + string
-      }
-      chunkSize *= 2
-
-      var match = lastMatchIn(string, regexp, endMargin)
-      if (match) {
-        var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n")
-        var startLine = line + before.length, startCh = before[before.length - 1].length
-        return {from: Pos(startLine, startCh),
-                to: Pos(startLine + inside.length - 1,
-                        inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),
-                match: match}
-      }
-    }
-  }
-
-  var doFold, noFold
-  if (String.prototype.normalize) {
-    doFold = function(str) { return str.normalize("NFD").toLowerCase() }
-    noFold = function(str) { return str.normalize("NFD") }
-  } else {
-    doFold = function(str) { return str.toLowerCase() }
-    noFold = function(str) { return str }
-  }
-
-  // Maps a position in a case-folded line back to a position in the original line
-  // (compensating for codepoints increasing in number during folding)
-  function adjustPos(orig, folded, pos, foldFunc) {
-    if (orig.length == folded.length) return pos
-    for (var min = 0, max = pos + Math.max(0, orig.length - folded.length);;) {
-      if (min == max) return min
-      var mid = (min + max) >> 1
-      var len = foldFunc(orig.slice(0, mid)).length
-      if (len == pos) return mid
-      else if (len > pos) max = mid
-      else min = mid + 1
-    }
-  }
-
-  function searchStringForward(doc, query, start, caseFold) {
-    // Empty string would match anything and never progress, so we
-    // define it to match nothing instead.
-    if (!query.length) return null
-    var fold = caseFold ? doFold : noFold
-    var lines = fold(query).split(/\r|\n\r?/)
-
-    search: for (var line = start.line, ch = start.ch, last = doc.lastLine() + 1 - lines.length; line <= last; line++, ch = 0) {
-      var orig = doc.getLine(line).slice(ch), string = fold(orig)
-      if (lines.length == 1) {
-        var found = string.indexOf(lines[0])
-        if (found == -1) continue search
-        var start = adjustPos(orig, string, found, fold) + ch
-        return {from: Pos(line, adjustPos(orig, string, found, fold) + ch),
-                to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold) + ch)}
-      } else {
-        var cutFrom = string.length - lines[0].length
-        if (string.slice(cutFrom) != lines[0]) continue search
-        for (var i = 1; i < lines.length - 1; i++)
-          if (fold(doc.getLine(line + i)) != lines[i]) continue search
-        var end = doc.getLine(line + lines.length - 1), endString = fold(end), lastLine = lines[lines.length - 1]
-        if (endString.slice(0, lastLine.length) != lastLine) continue search
-        return {from: Pos(line, adjustPos(orig, string, cutFrom, fold) + ch),
-                to: Pos(line + lines.length - 1, adjustPos(end, endString, lastLine.length, fold))}
-      }
-    }
-  }
-
-  function searchStringBackward(doc, query, start, caseFold) {
-    if (!query.length) return null
-    var fold = caseFold ? doFold : noFold
-    var lines = fold(query).split(/\r|\n\r?/)
-
-    search: for (var line = start.line, ch = start.ch, first = doc.firstLine() - 1 + lines.length; line >= first; line--, ch = -1) {
-      var orig = doc.getLine(line)
-      if (ch > -1) orig = orig.slice(0, ch)
-      var string = fold(orig)
-      if (lines.length == 1) {
-        var found = string.lastIndexOf(lines[0])
-        if (found == -1) continue search
-        return {from: Pos(line, adjustPos(orig, string, found, fold)),
-                to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold))}
-      } else {
-        var lastLine = lines[lines.length - 1]
-        if (string.slice(0, lastLine.length) != lastLine) continue search
-        for (var i = 1, start = line - lines.length + 1; i < lines.length - 1; i++)
-          if (fold(doc.getLine(start + i)) != lines[i]) continue search
-        var top = doc.getLine(line + 1 - lines.length), topString = fold(top)
-        if (topString.slice(topString.length - lines[0].length) != lines[0]) continue search
-        return {from: Pos(line + 1 - lines.length, adjustPos(top, topString, top.length - lines[0].length, fold)),
-                to: Pos(line, adjustPos(orig, string, lastLine.length, fold))}
-      }
-    }
-  }
-
-  function SearchCursor(doc, query, pos, options) {
-    this.atOccurrence = false
-    this.doc = doc
-    pos = pos ? doc.clipPos(pos) : Pos(0, 0)
-    this.pos = {from: pos, to: pos}
-
-    var caseFold
-    if (typeof options == "object") {
-      caseFold = options.caseFold
-    } else { // Backwards compat for when caseFold was the 4th argument
-      caseFold = options
-      options = null
-    }
-
-    if (typeof query == "string") {
-      if (caseFold == null) caseFold = false
-      this.matches = function(reverse, pos) {
-        return (reverse ? searchStringBackward : searchStringForward)(doc, query, pos, caseFold)
-      }
-    } else {
-      query = ensureFlags(query, "gm")
-      if (!options || options.multiline !== false)
-        this.matches = function(reverse, pos) {
-          return (reverse ? searchRegexpBackwardMultiline : searchRegexpForwardMultiline)(doc, query, pos)
-        }
-      else
-        this.matches = function(reverse, pos) {
-          return (reverse ? searchRegexpBackward : searchRegexpForward)(doc, query, pos)
-        }
-    }
-  }
-
-  SearchCursor.prototype = {
-    findNext: function() {return this.find(false)},
-    findPrevious: function() {return this.find(true)},
-
-    find: function(reverse) {
-      var result = this.matches(reverse, this.doc.clipPos(reverse ? this.pos.from : this.pos.to))
-
-      // Implements weird auto-growing behavior on null-matches for
-      // backwards-compatibility with the vim code (unfortunately)
-      while (result && CodeMirror.cmpPos(result.from, result.to) == 0) {
-        if (reverse) {
-          if (result.from.ch) result.from = Pos(result.from.line, result.from.ch - 1)
-          else if (result.from.line == this.doc.firstLine()) result = null
-          else result = this.matches(reverse, this.doc.clipPos(Pos(result.from.line - 1)))
-        } else {
-          if (result.to.ch < this.doc.getLine(result.to.line).length) result.to = Pos(result.to.line, result.to.ch + 1)
-          else if (result.to.line == this.doc.lastLine()) result = null
-          else result = this.matches(reverse, Pos(result.to.line + 1, 0))
-        }
-      }
-
-      if (result) {
-        this.pos = result
-        this.atOccurrence = true
-        return this.pos.match || true
-      } else {
-        var end = Pos(reverse ? this.doc.firstLine() : this.doc.lastLine() + 1, 0)
-        this.pos = {from: end, to: end}
-        return this.atOccurrence = false
-      }
-    },
-
-    from: function() {if (this.atOccurrence) return this.pos.from},
-    to: function() {if (this.atOccurrence) return this.pos.to},
-
-    replace: function(newText, origin) {
-      if (!this.atOccurrence) return
-      var lines = CodeMirror.splitLines(newText)
-      this.doc.replaceRange(lines, this.pos.from, this.pos.to, origin)
-      this.pos.to = Pos(this.pos.from.line + lines.length - 1,
-                        lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0))
-    }
-  }
-
-  CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
-    return new SearchCursor(this.doc, query, pos, caseFold)
-  })
-  CodeMirror.defineDocExtension("getSearchCursor", function(query, pos, caseFold) {
-    return new SearchCursor(this, query, pos, caseFold)
-  })
-
-  CodeMirror.defineExtension("selectMatches", function(query, caseFold) {
-    var ranges = []
-    var cur = this.getSearchCursor(query, this.getCursor("from"), caseFold)
-    while (cur.findNext()) {
-      if (CodeMirror.cmpPos(cur.to(), this.getCursor("to")) > 0) break
-      ranges.push({anchor: cur.from(), head: cur.to()})
-    }
-    if (ranges.length)
-      this.setSelections(ranges, 0)
-  })
-});
-
-
-/* ---- extension/selection/active-line.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-  var WRAP_CLASS = "CodeMirror-activeline";
-  var BACK_CLASS = "CodeMirror-activeline-background";
-  var GUTT_CLASS = "CodeMirror-activeline-gutter";
-
-  CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) {
-    var prev = old == CodeMirror.Init ? false : old;
-    if (val == prev) return
-    if (prev) {
-      cm.off("beforeSelectionChange", selectionChange);
-      clearActiveLines(cm);
-      delete cm.state.activeLines;
-    }
-    if (val) {
-      cm.state.activeLines = [];
-      updateActiveLines(cm, cm.listSelections());
-      cm.on("beforeSelectionChange", selectionChange);
-    }
-  });
-
-  function clearActiveLines(cm) {
-    for (var i = 0; i < cm.state.activeLines.length; i++) {
-      cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS);
-      cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS);
-      cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS);
-    }
-  }
-
-  function sameArray(a, b) {
-    if (a.length != b.length) return false;
-    for (var i = 0; i < a.length; i++)
-      if (a[i] != b[i]) return false;
-    return true;
-  }
-
-  function updateActiveLines(cm, ranges) {
-    var active = [];
-    for (var i = 0; i < ranges.length; i++) {
-      var range = ranges[i];
-      var option = cm.getOption("styleActiveLine");
-      if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty())
-        continue
-      var line = cm.getLineHandleVisualStart(range.head.line);
-      if (active[active.length - 1] != line) active.push(line);
-    }
-    if (sameArray(cm.state.activeLines, active)) return;
-    cm.operation(function() {
-      clearActiveLines(cm);
-      for (var i = 0; i < active.length; i++) {
-        cm.addLineClass(active[i], "wrap", WRAP_CLASS);
-        cm.addLineClass(active[i], "background", BACK_CLASS);
-        cm.addLineClass(active[i], "gutter", GUTT_CLASS);
-      }
-      cm.state.activeLines = active;
-    });
-  }
-
-  function selectionChange(cm, sel) {
-    updateActiveLines(cm, sel.ranges);
-  }
-});
-
-
-/* ---- extension/selection/mark-selection.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-// Because sometimes you need to mark the selected *text*.
-//
-// Adds an option 'styleSelectedText' which, when enabled, gives
-// selected text the CSS class given as option value, or
-// "CodeMirror-selectedtext" when the value is not a string.
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) {
-    var prev = old && old != CodeMirror.Init;
-    if (val && !prev) {
-      cm.state.markedSelection = [];
-      cm.state.markedSelectionStyle = typeof val == "string" ? val : "CodeMirror-selectedtext";
-      reset(cm);
-      cm.on("cursorActivity", onCursorActivity);
-      cm.on("change", onChange);
-    } else if (!val && prev) {
-      cm.off("cursorActivity", onCursorActivity);
-      cm.off("change", onChange);
-      clear(cm);
-      cm.state.markedSelection = cm.state.markedSelectionStyle = null;
-    }
-  });
-
-  function onCursorActivity(cm) {
-    if (cm.state.markedSelection)
-      cm.operation(function() { update(cm); });
-  }
-
-  function onChange(cm) {
-    if (cm.state.markedSelection && cm.state.markedSelection.length)
-      cm.operation(function() { clear(cm); });
-  }
-
-  var CHUNK_SIZE = 8;
-  var Pos = CodeMirror.Pos;
-  var cmp = CodeMirror.cmpPos;
-
-  function coverRange(cm, from, to, addAt) {
-    if (cmp(from, to) == 0) return;
-    var array = cm.state.markedSelection;
-    var cls = cm.state.markedSelectionStyle;
-    for (var line = from.line;;) {
-      var start = line == from.line ? from : Pos(line, 0);
-      var endLine = line + CHUNK_SIZE, atEnd = endLine >= to.line;
-      var end = atEnd ? to : Pos(endLine, 0);
-      var mark = cm.markText(start, end, {className: cls});
-      if (addAt == null) array.push(mark);
-      else array.splice(addAt++, 0, mark);
-      if (atEnd) break;
-      line = endLine;
-    }
-  }
-
-  function clear(cm) {
-    var array = cm.state.markedSelection;
-    for (var i = 0; i < array.length; ++i) array[i].clear();
-    array.length = 0;
-  }
-
-  function reset(cm) {
-    clear(cm);
-    var ranges = cm.listSelections();
-    for (var i = 0; i < ranges.length; i++)
-      coverRange(cm, ranges[i].from(), ranges[i].to());
-  }
-
-  function update(cm) {
-    if (!cm.somethingSelected()) return clear(cm);
-    if (cm.listSelections().length > 1) return reset(cm);
-
-    var from = cm.getCursor("start"), to = cm.getCursor("end");
-
-    var array = cm.state.markedSelection;
-    if (!array.length) return coverRange(cm, from, to);
-
-    var coverStart = array[0].find(), coverEnd = array[array.length - 1].find();
-    if (!coverStart || !coverEnd || to.line - from.line <= CHUNK_SIZE ||
-        cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0)
-      return reset(cm);
-
-    while (cmp(from, coverStart.from) > 0) {
-      array.shift().clear();
-      coverStart = array[0].find();
-    }
-    if (cmp(from, coverStart.from) < 0) {
-      if (coverStart.to.line - from.line < CHUNK_SIZE) {
-        array.shift().clear();
-        coverRange(cm, from, coverStart.to, 0);
-      } else {
-        coverRange(cm, from, coverStart.from, 0);
-      }
-    }
-
-    while (cmp(to, coverEnd.to) < 0) {
-      array.pop().clear();
-      coverEnd = array[array.length - 1].find();
-    }
-    if (cmp(to, coverEnd.to) > 0) {
-      if (to.line - coverEnd.from.line < CHUNK_SIZE) {
-        array.pop().clear();
-        coverRange(cm, coverEnd.from, to);
-      } else {
-        coverRange(cm, coverEnd.to, to);
-      }
-    }
-  }
-});
-
-
-/* ---- extension/selection/selection-pointer.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  CodeMirror.defineOption("selectionPointer", false, function(cm, val) {
-    var data = cm.state.selectionPointer;
-    if (data) {
-      CodeMirror.off(cm.getWrapperElement(), "mousemove", data.mousemove);
-      CodeMirror.off(cm.getWrapperElement(), "mouseout", data.mouseout);
-      CodeMirror.off(window, "scroll", data.windowScroll);
-      cm.off("cursorActivity", reset);
-      cm.off("scroll", reset);
-      cm.state.selectionPointer = null;
-      cm.display.lineDiv.style.cursor = "";
-    }
-    if (val) {
-      data = cm.state.selectionPointer = {
-        value: typeof val == "string" ? val : "default",
-        mousemove: function(event) { mousemove(cm, event); },
-        mouseout: function(event) { mouseout(cm, event); },
-        windowScroll: function() { reset(cm); },
-        rects: null,
-        mouseX: null, mouseY: null,
-        willUpdate: false
-      };
-      CodeMirror.on(cm.getWrapperElement(), "mousemove", data.mousemove);
-      CodeMirror.on(cm.getWrapperElement(), "mouseout", data.mouseout);
-      CodeMirror.on(window, "scroll", data.windowScroll);
-      cm.on("cursorActivity", reset);
-      cm.on("scroll", reset);
-    }
-  });
-
-  function mousemove(cm, event) {
-    var data = cm.state.selectionPointer;
-    if (event.buttons == null ? event.which : event.buttons) {
-      data.mouseX = data.mouseY = null;
-    } else {
-      data.mouseX = event.clientX;
-      data.mouseY = event.clientY;
-    }
-    scheduleUpdate(cm);
-  }
-
-  function mouseout(cm, event) {
-    if (!cm.getWrapperElement().contains(event.relatedTarget)) {
-      var data = cm.state.selectionPointer;
-      data.mouseX = data.mouseY = null;
-      scheduleUpdate(cm);
-    }
-  }
-
-  function reset(cm) {
-    cm.state.selectionPointer.rects = null;
-    scheduleUpdate(cm);
-  }
-
-  function scheduleUpdate(cm) {
-    if (!cm.state.selectionPointer.willUpdate) {
-      cm.state.selectionPointer.willUpdate = true;
-      setTimeout(function() {
-        update(cm);
-        cm.state.selectionPointer.willUpdate = false;
-      }, 50);
-    }
-  }
-
-  function update(cm) {
-    var data = cm.state.selectionPointer;
-    if (!data) return;
-    if (data.rects == null && data.mouseX != null) {
-      data.rects = [];
-      if (cm.somethingSelected()) {
-        for (var sel = cm.display.selectionDiv.firstChild; sel; sel = sel.nextSibling)
-          data.rects.push(sel.getBoundingClientRect());
-      }
-    }
-    var inside = false;
-    if (data.mouseX != null) for (var i = 0; i < data.rects.length; i++) {
-      var rect = data.rects[i];
-      if (rect.left <= data.mouseX && rect.right >= data.mouseX &&
-          rect.top <= data.mouseY && rect.bottom >= data.mouseY)
-        inside = true;
-    }
-    var cursor = inside ? data.value : "";
-    if (cm.display.lineDiv.style.cursor != cursor)
-      cm.display.lineDiv.style.cursor = cursor;
-  }
-});
-
-
-/* ---- mode/coffeescript.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-/**
- * Link to the project's GitHub page:
- * https://github.com/pickhardt/coffeescript-codemirror-mode
- */
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-CodeMirror.defineMode("coffeescript", function(conf, parserConf) {
-  var ERRORCLASS = "error";
-
-  function wordRegexp(words) {
-    return new RegExp("^((" + words.join(")|(") + "))\\b");
-  }
-
-  var operators = /^(?:->|=>|\+[+=]?|-[\-=]?|\*[\*=]?|\/[\/=]?|[=!]=|<[><]?=?|>>?=?|%=?|&=?|\|=?|\^=?|\~|!|\?|(or|and|\|\||&&|\?)=)/;
-  var delimiters = /^(?:[()\[\]{},:`=;]|\.\.?\.?)/;
-  var identifiers = /^[_A-Za-z$][_A-Za-z$0-9]*/;
-  var atProp = /^@[_A-Za-z$][_A-Za-z$0-9]*/;
-
-  var wordOperators = wordRegexp(["and", "or", "not",
-                                  "is", "isnt", "in",
-                                  "instanceof", "typeof"]);
-  var indentKeywords = ["for", "while", "loop", "if", "unless", "else",
-                        "switch", "try", "catch", "finally", "class"];
-  var commonKeywords = ["break", "by", "continue", "debugger", "delete",
-                        "do", "in", "of", "new", "return", "then",
-                        "this", "@", "throw", "when", "until", "extends"];
-
-  var keywords = wordRegexp(indentKeywords.concat(commonKeywords));
-
-  indentKeywords = wordRegexp(indentKeywords);
-
-
-  var stringPrefixes = /^('{3}|\"{3}|['\"])/;
-  var regexPrefixes = /^(\/{3}|\/)/;
-  var commonConstants = ["Infinity", "NaN", "undefined", "null", "true", "false", "on", "off", "yes", "no"];
-  var constants = wordRegexp(commonConstants);
-
-  // Tokenizers
-  function tokenBase(stream, state) {
-    // Handle scope changes
-    if (stream.sol()) {
-      if (state.scope.align === null) state.scope.align = false;
-      var scopeOffset = state.scope.offset;
-      if (stream.eatSpace()) {
-        var lineOffset = stream.indentation();
-        if (lineOffset > scopeOffset && state.scope.type == "coffee") {
-          return "indent";
-        } else if (lineOffset < scopeOffset) {
-          return "dedent";
-        }
-        return null;
-      } else {
-        if (scopeOffset > 0) {
-          dedent(stream, state);
-        }
-      }
-    }
-    if (stream.eatSpace()) {
-      return null;
-    }
-
-    var ch = stream.peek();
-
-    // Handle docco title comment (single line)
-    if (stream.match("####")) {
-      stream.skipToEnd();
-      return "comment";
-    }
-
-    // Handle multi line comments
-    if (stream.match("###")) {
-      state.tokenize = longComment;
-      return state.tokenize(stream, state);
-    }
-
-    // Single line comment
-    if (ch === "#") {
-      stream.skipToEnd();
-      return "comment";
-    }
-
-    // Handle number literals
-    if (stream.match(/^-?[0-9\.]/, false)) {
-      var floatLiteral = false;
-      // Floats
-      if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) {
-        floatLiteral = true;
-      }
-      if (stream.match(/^-?\d+\.\d*/)) {
-        floatLiteral = true;
-      }
-      if (stream.match(/^-?\.\d+/)) {
-        floatLiteral = true;
-      }
-
-      if (floatLiteral) {
-        // prevent from getting extra . on 1..
-        if (stream.peek() == "."){
-          stream.backUp(1);
-        }
-        return "number";
-      }
-      // Integers
-      var intLiteral = false;
-      // Hex
-      if (stream.match(/^-?0x[0-9a-f]+/i)) {
-        intLiteral = true;
-      }
-      // Decimal
-      if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) {
-        intLiteral = true;
-      }
-      // Zero by itself with no other piece of number.
-      if (stream.match(/^-?0(?![\dx])/i)) {
-        intLiteral = true;
-      }
-      if (intLiteral) {
-        return "number";
-      }
-    }
-
-    // Handle strings
-    if (stream.match(stringPrefixes)) {
-      state.tokenize = tokenFactory(stream.current(), false, "string");
-      return state.tokenize(stream, state);
-    }
-    // Handle regex literals
-    if (stream.match(regexPrefixes)) {
-      if (stream.current() != "/" || stream.match(/^.*\//, false)) { // prevent highlight of division
-        state.tokenize = tokenFactory(stream.current(), true, "string-2");
-        return state.tokenize(stream, state);
-      } else {
-        stream.backUp(1);
-      }
-    }
-
-
-
-    // Handle operators and delimiters
-    if (stream.match(operators) || stream.match(wordOperators)) {
-      return "operator";
-    }
-    if (stream.match(delimiters)) {
-      return "punctuation";
-    }
-
-    if (stream.match(constants)) {
-      return "atom";
-    }
-
-    if (stream.match(atProp) || state.prop && stream.match(identifiers)) {
-      return "property";
-    }
-
-    if (stream.match(keywords)) {
-      return "keyword";
-    }
-
-    if (stream.match(identifiers)) {
-      return "variable";
-    }
-
-    // Handle non-detected items
-    stream.next();
-    return ERRORCLASS;
-  }
-
-  function tokenFactory(delimiter, singleline, outclass) {
-    return function(stream, state) {
-      while (!stream.eol()) {
-        stream.eatWhile(/[^'"\/\\]/);
-        if (stream.eat("\\")) {
-          stream.next();
-          if (singleline && stream.eol()) {
-            return outclass;
-          }
-        } else if (stream.match(delimiter)) {
-          state.tokenize = tokenBase;
-          return outclass;
-        } else {
-          stream.eat(/['"\/]/);
-        }
-      }
-      if (singleline) {
-        if (parserConf.singleLineStringErrors) {
-          outclass = ERRORCLASS;
-        } else {
-          state.tokenize = tokenBase;
-        }
-      }
-      return outclass;
-    };
-  }
-
-  function longComment(stream, state) {
-    while (!stream.eol()) {
-      stream.eatWhile(/[^#]/);
-      if (stream.match("###")) {
-        state.tokenize = tokenBase;
-        break;
-      }
-      stream.eatWhile("#");
-    }
-    return "comment";
-  }
-
-  function indent(stream, state, type) {
-    type = type || "coffee";
-    var offset = 0, align = false, alignOffset = null;
-    for (var scope = state.scope; scope; scope = scope.prev) {
-      if (scope.type === "coffee" || scope.type == "}") {
-        offset = scope.offset + conf.indentUnit;
-        break;
-      }
-    }
-    if (type !== "coffee") {
-      align = null;
-      alignOffset = stream.column() + stream.current().length;
-    } else if (state.scope.align) {
-      state.scope.align = false;
-    }
-    state.scope = {
-      offset: offset,
-      type: type,
-      prev: state.scope,
-      align: align,
-      alignOffset: alignOffset
-    };
-  }
-
-  function dedent(stream, state) {
-    if (!state.scope.prev) return;
-    if (state.scope.type === "coffee") {
-      var _indent = stream.indentation();
-      var matched = false;
-      for (var scope = state.scope; scope; scope = scope.prev) {
-        if (_indent === scope.offset) {
-          matched = true;
-          break;
-        }
-      }
-      if (!matched) {
-        return true;
-      }
-      while (state.scope.prev && state.scope.offset !== _indent) {
-        state.scope = state.scope.prev;
-      }
-      return false;
-    } else {
-      state.scope = state.scope.prev;
-      return false;
-    }
-  }
-
-  function tokenLexer(stream, state) {
-    var style = state.tokenize(stream, state);
-    var current = stream.current();
-
-    // Handle scope changes.
-    if (current === "return") {
-      state.dedent = true;
-    }
-    if (((current === "->" || current === "=>") && stream.eol())
-        || style === "indent") {
-      indent(stream, state);
-    }
-    var delimiter_index = "[({".indexOf(current);
-    if (delimiter_index !== -1) {
-      indent(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
-    }
-    if (indentKeywords.exec(current)){
-      indent(stream, state);
-    }
-    if (current == "then"){
-      dedent(stream, state);
-    }
-
-
-    if (style === "dedent") {
-      if (dedent(stream, state)) {
-        return ERRORCLASS;
-      }
-    }
-    delimiter_index = "])}".indexOf(current);
-    if (delimiter_index !== -1) {
-      while (state.scope.type == "coffee" && state.scope.prev)
-        state.scope = state.scope.prev;
-      if (state.scope.type == current)
-        state.scope = state.scope.prev;
-    }
-    if (state.dedent && stream.eol()) {
-      if (state.scope.type == "coffee" && state.scope.prev)
-        state.scope = state.scope.prev;
-      state.dedent = false;
-    }
-
-    return style;
-  }
-
-  var external = {
-    startState: function(basecolumn) {
-      return {
-        tokenize: tokenBase,
-        scope: {offset:basecolumn || 0, type:"coffee", prev: null, align: false},
-        prop: false,
-        dedent: 0
-      };
-    },
-
-    token: function(stream, state) {
-      var fillAlign = state.scope.align === null && state.scope;
-      if (fillAlign && stream.sol()) fillAlign.align = false;
-
-      var style = tokenLexer(stream, state);
-      if (style && style != "comment") {
-        if (fillAlign) fillAlign.align = true;
-        state.prop = style == "punctuation" && stream.current() == "."
-      }
-
-      return style;
-    },
-
-    indent: function(state, text) {
-      if (state.tokenize != tokenBase) return 0;
-      var scope = state.scope;
-      var closer = text && "])}".indexOf(text.charAt(0)) > -1;
-      if (closer) while (scope.type == "coffee" && scope.prev) scope = scope.prev;
-      var closes = closer && scope.type === text.charAt(0);
-      if (scope.align)
-        return scope.alignOffset - (closes ? 1 : 0);
-      else
-        return (closes ? scope.prev : scope).offset;
-    },
-
-    lineComment: "#",
-    fold: "indent"
-  };
-  return external;
-});
-
-// IANA registered media type
-// https://www.iana.org/assignments/media-types/
-CodeMirror.defineMIME("application/vnd.coffeescript", "coffeescript");
-
-CodeMirror.defineMIME("text/x-coffeescript", "coffeescript");
-CodeMirror.defineMIME("text/coffeescript", "coffeescript");
-
-});
-
-
-/* ---- mode/css.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-CodeMirror.defineMode("css", function(config, parserConfig) {
-  var inline = parserConfig.inline
-  if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
-
-  var indentUnit = config.indentUnit,
-      tokenHooks = parserConfig.tokenHooks,
-      documentTypes = parserConfig.documentTypes || {},
-      mediaTypes = parserConfig.mediaTypes || {},
-      mediaFeatures = parserConfig.mediaFeatures || {},
-      mediaValueKeywords = parserConfig.mediaValueKeywords || {},
-      propertyKeywords = parserConfig.propertyKeywords || {},
-      nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
-      fontProperties = parserConfig.fontProperties || {},
-      counterDescriptors = parserConfig.counterDescriptors || {},
-      colorKeywords = parserConfig.colorKeywords || {},
-      valueKeywords = parserConfig.valueKeywords || {},
-      allowNested = parserConfig.allowNested,
-      lineComment = parserConfig.lineComment,
-      supportsAtComponent = parserConfig.supportsAtComponent === true;
-
-  var type, override;
-  function ret(style, tp) { type = tp; return style; }
-
-  // Tokenizers
-
-  function tokenBase(stream, state) {
-    var ch = stream.next();
-    if (tokenHooks[ch]) {
-      var result = tokenHooks[ch](stream, state);
-      if (result !== false) return result;
-    }
-    if (ch == "@") {
-      stream.eatWhile(/[\w\\\-]/);
-      return ret("def", stream.current());
-    } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
-      return ret(null, "compare");
-    } else if (ch == "\"" || ch == "'") {
-      state.tokenize = tokenString(ch);
-      return state.tokenize(stream, state);
-    } else if (ch == "#") {
-      stream.eatWhile(/[\w\\\-]/);
-      return ret("atom", "hash");
-    } else if (ch == "!") {
-      stream.match(/^\s*\w*/);
-      return ret("keyword", "important");
-    } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
-      stream.eatWhile(/[\w.%]/);
-      return ret("number", "unit");
-    } else if (ch === "-") {
-      if (/[\d.]/.test(stream.peek())) {
-        stream.eatWhile(/[\w.%]/);
-        return ret("number", "unit");
-      } else if (stream.match(/^-[\w\\\-]*/)) {
-        stream.eatWhile(/[\w\\\-]/);
-        if (stream.match(/^\s*:/, false))
-          return ret("variable-2", "variable-definition");
-        return ret("variable-2", "variable");
-      } else if (stream.match(/^\w+-/)) {
-        return ret("meta", "meta");
-      }
-    } else if (/[,+>*\/]/.test(ch)) {
-      return ret(null, "select-op");
-    } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
-      return ret("qualifier", "qualifier");
-    } else if (/[:;{}\[\]\(\)]/.test(ch)) {
-      return ret(null, ch);
-    } else if (stream.match(/[\w-.]+(?=\()/)) {
-      if (/^(url(-prefix)?|domain|regexp)$/.test(stream.current().toLowerCase())) {
-        state.tokenize = tokenParenthesized;
-      }
-      return ret("variable callee", "variable");
-    } else if (/[\w\\\-]/.test(ch)) {
-      stream.eatWhile(/[\w\\\-]/);
-      return ret("property", "word");
-    } else {
-      return ret(null, null);
-    }
-  }
-
-  function tokenString(quote) {
-    return function(stream, state) {
-      var escaped = false, ch;
-      while ((ch = stream.next()) != null) {
-        if (ch == quote && !escaped) {
-          if (quote == ")") stream.backUp(1);
-          break;
-        }
-        escaped = !escaped && ch == "\\";
-      }
-      if (ch == quote || !escaped && quote != ")") state.tokenize = null;
-      return ret("string", "string");
-    };
-  }
-
-  function tokenParenthesized(stream, state) {
-    stream.next(); // Must be '('
-    if (!stream.match(/\s*[\"\')]/, false))
-      state.tokenize = tokenString(")");
-    else
-      state.tokenize = null;
-    return ret(null, "(");
-  }
-
-  // Context management
-
-  function Context(type, indent, prev) {
-    this.type = type;
-    this.indent = indent;
-    this.prev = prev;
-  }
-
-  function pushContext(state, stream, type, indent) {
-    state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);
-    return type;
-  }
-
-  function popContext(state) {
-    if (state.context.prev)
-      state.context = state.context.prev;
-    return state.context.type;
-  }
-
-  function pass(type, stream, state) {
-    return states[state.context.type](type, stream, state);
-  }
-  function popAndPass(type, stream, state, n) {
-    for (var i = n || 1; i > 0; i--)
-      state.context = state.context.prev;
-    return pass(type, stream, state);
-  }
-
-  // Parser
-
-  function wordAsValue(stream) {
-    var word = stream.current().toLowerCase();
-    if (valueKeywords.hasOwnProperty(word))
-      override = "atom";
-    else if (colorKeywords.hasOwnProperty(word))
-      override = "keyword";
-    else
-      override = "variable";
-  }
-
-  var states = {};
-
-  states.top = function(type, stream, state) {
-    if (type == "{") {
-      return pushContext(state, stream, "block");
-    } else if (type == "}" && state.context.prev) {
-      return popContext(state);
-    } else if (supportsAtComponent && /@component/i.test(type)) {
-      return pushContext(state, stream, "atComponentBlock");
-    } else if (/^@(-moz-)?document$/i.test(type)) {
-      return pushContext(state, stream, "documentTypes");
-    } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {
-      return pushContext(state, stream, "atBlock");
-    } else if (/^@(font-face|counter-style)/i.test(type)) {
-      state.stateArg = type;
-      return "restricted_atBlock_before";
-    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {
-      return "keyframes";
-    } else if (type && type.charAt(0) == "@") {
-      return pushContext(state, stream, "at");
-    } else if (type == "hash") {
-      override = "builtin";
-    } else if (type == "word") {
-      override = "tag";
-    } else if (type == "variable-definition") {
-      return "maybeprop";
-    } else if (type == "interpolation") {
-      return pushContext(state, stream, "interpolation");
-    } else if (type == ":") {
-      return "pseudo";
-    } else if (allowNested && type == "(") {
-      return pushContext(state, stream, "parens");
-    }
-    return state.context.type;
-  };
-
-  states.block = function(type, stream, state) {
-    if (type == "word") {
-      var word = stream.current().toLowerCase();
-      if (propertyKeywords.hasOwnProperty(word)) {
-        override = "property";
-        return "maybeprop";
-      } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
-        override = "string-2";
-        return "maybeprop";
-      } else if (allowNested) {
-        override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
-        return "block";
-      } else {
-        override += " error";
-        return "maybeprop";
-      }
-    } else if (type == "meta") {
-      return "block";
-    } else if (!allowNested && (type == "hash" || type == "qualifier")) {
-      override = "error";
-      return "block";
-    } else {
-      return states.top(type, stream, state);
-    }
-  };
-
-  states.maybeprop = function(type, stream, state) {
-    if (type == ":") return pushContext(state, stream, "prop");
-    return pass(type, stream, state);
-  };
-
-  states.prop = function(type, stream, state) {
-    if (type == ";") return popContext(state);
-    if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
-    if (type == "}" || type == "{") return popAndPass(type, stream, state);
-    if (type == "(") return pushContext(state, stream, "parens");
-
-    if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) {
-      override += " error";
-    } else if (type == "word") {
-      wordAsValue(stream);
-    } else if (type == "interpolation") {
-      return pushContext(state, stream, "interpolation");
-    }
-    return "prop";
-  };
-
-  states.propBlock = function(type, _stream, state) {
-    if (type == "}") return popContext(state);
-    if (type == "word") { override = "property"; return "maybeprop"; }
-    return state.context.type;
-  };
-
-  states.parens = function(type, stream, state) {
-    if (type == "{" || type == "}") return popAndPass(type, stream, state);
-    if (type == ")") return popContext(state);
-    if (type == "(") return pushContext(state, stream, "parens");
-    if (type == "interpolation") return pushContext(state, stream, "interpolation");
-    if (type == "word") wordAsValue(stream);
-    return "parens";
-  };
-
-  states.pseudo = function(type, stream, state) {
-    if (type == "meta") return "pseudo";
-
-    if (type == "word") {
-      override = "variable-3";
-      return state.context.type;
-    }
-    return pass(type, stream, state);
-  };
-
-  states.documentTypes = function(type, stream, state) {
-    if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
-      override = "tag";
-      return state.context.type;
-    } else {
-      return states.atBlock(type, stream, state);
-    }
-  };
-
-  states.atBlock = function(type, stream, state) {
-    if (type == "(") return pushContext(state, stream, "atBlock_parens");
-    if (type == "}" || type == ";") return popAndPass(type, stream, state);
-    if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
-
-    if (type == "interpolation") return pushContext(state, stream, "interpolation");
-
-    if (type == "word") {
-      var word = stream.current().toLowerCase();
-      if (word == "only" || word == "not" || word == "and" || word == "or")
-        override = "keyword";
-      else if (mediaTypes.hasOwnProperty(word))
-        override = "attribute";
-      else if (mediaFeatures.hasOwnProperty(word))
-        override = "property";
-      else if (mediaValueKeywords.hasOwnProperty(word))
-        override = "keyword";
-      else if (propertyKeywords.hasOwnProperty(word))
-        override = "property";
-      else if (nonStandardPropertyKeywords.hasOwnProperty(word))
-        override = "string-2";
-      else if (valueKeywords.hasOwnProperty(word))
-        override = "atom";
-      else if (colorKeywords.hasOwnProperty(word))
-        override = "keyword";
-      else
-        override = "error";
-    }
-    return state.context.type;
-  };
-
-  states.atComponentBlock = function(type, stream, state) {
-    if (type == "}")
-      return popAndPass(type, stream, state);
-    if (type == "{")
-      return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false);
-    if (type == "word")
-      override = "error";
-    return state.context.type;
-  };
-
-  states.atBlock_parens = function(type, stream, state) {
-    if (type == ")") return popContext(state);
-    if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
-    return states.atBlock(type, stream, state);
-  };
-
-  states.restricted_atBlock_before = function(type, stream, state) {
-    if (type == "{")
-      return pushContext(state, stream, "restricted_atBlock");
-    if (type == "word" && state.stateArg == "@counter-style") {
-      override = "variable";
-      return "restricted_atBlock_before";
-    }
-    return pass(type, stream, state);
-  };
-
-  states.restricted_atBlock = function(type, stream, state) {
-    if (type == "}") {
-      state.stateArg = null;
-      return popContext(state);
-    }
-    if (type == "word") {
-      if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
-          (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
-        override = "error";
-      else
-        override = "property";
-      return "maybeprop";
-    }
-    return "restricted_atBlock";
-  };
-
-  states.keyframes = function(type, stream, state) {
-    if (type == "word") { override = "variable"; return "keyframes"; }
-    if (type == "{") return pushContext(state, stream, "top");
-    return pass(type, stream, state);
-  };
-
-  states.at = function(type, stream, state) {
-    if (type == ";") return popContext(state);
-    if (type == "{" || type == "}") return popAndPass(type, stream, state);
-    if (type == "word") override = "tag";
-    else if (type == "hash") override = "builtin";
-    return "at";
-  };
-
-  states.interpolation = function(type, stream, state) {
-    if (type == "}") return popContext(state);
-    if (type == "{" || type == ";") return popAndPass(type, stream, state);
-    if (type == "word") override = "variable";
-    else if (type != "variable" && type != "(" && type != ")") override = "error";
-    return "interpolation";
-  };
-
-  return {
-    startState: function(base) {
-      return {tokenize: null,
-              state: inline ? "block" : "top",
-              stateArg: null,
-              context: new Context(inline ? "block" : "top", base || 0, null)};
-    },
-
-    token: function(stream, state) {
-      if (!state.tokenize && stream.eatSpace()) return null;
-      var style = (state.tokenize || tokenBase)(stream, state);
-      if (style && typeof style == "object") {
-        type = style[1];
-        style = style[0];
-      }
-      override = style;
-      if (type != "comment")
-        state.state = states[state.state](type, stream, state);
-      return override;
-    },
-
-    indent: function(state, textAfter) {
-      var cx = state.context, ch = textAfter && textAfter.charAt(0);
-      var indent = cx.indent;
-      if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
-      if (cx.prev) {
-        if (ch == "}" && (cx.type == "block" || cx.type == "top" ||
-                          cx.type == "interpolation" || cx.type == "restricted_atBlock")) {
-          // Resume indentation from parent context.
-          cx = cx.prev;
-          indent = cx.indent;
-        } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
-            ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
-          // Dedent relative to current context.
-          indent = Math.max(0, cx.indent - indentUnit);
-        }
-      }
-      return indent;
-    },
-
-    electricChars: "}",
-    blockCommentStart: "/*",
-    blockCommentEnd: "*/",
-    blockCommentContinue: " * ",
-    lineComment: lineComment,
-    fold: "brace"
-  };
-});
-
-  function keySet(array) {
-    var keys = {};
-    for (var i = 0; i < array.length; ++i) {
-      keys[array[i].toLowerCase()] = true;
-    }
-    return keys;
-  }
-
-  var documentTypes_ = [
-    "domain", "regexp", "url", "url-prefix"
-  ], documentTypes = keySet(documentTypes_);
-
-  var mediaTypes_ = [
-    "all", "aural", "braille", "handheld", "print", "projection", "screen",
-    "tty", "tv", "embossed"
-  ], mediaTypes = keySet(mediaTypes_);
-
-  var mediaFeatures_ = [
-    "width", "min-width", "max-width", "height", "min-height", "max-height",
-    "device-width", "min-device-width", "max-device-width", "device-height",
-    "min-device-height", "max-device-height", "aspect-ratio",
-    "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
-    "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
-    "max-color", "color-index", "min-color-index", "max-color-index",
-    "monochrome", "min-monochrome", "max-monochrome", "resolution",
-    "min-resolution", "max-resolution", "scan", "grid", "orientation",
-    "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio",
-    "pointer", "any-pointer", "hover", "any-hover"
-  ], mediaFeatures = keySet(mediaFeatures_);
-
-  var mediaValueKeywords_ = [
-    "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover",
-    "interlace", "progressive"
-  ], mediaValueKeywords = keySet(mediaValueKeywords_);
-
-  var propertyKeywords_ = [
-    "align-content", "align-items", "align-self", "alignment-adjust",
-    "alignment-baseline", "anchor-point", "animation", "animation-delay",
-    "animation-direction", "animation-duration", "animation-fill-mode",
-    "animation-iteration-count", "animation-name", "animation-play-state",
-    "animation-timing-function", "appearance", "azimuth", "backdrop-filter",
-    "backface-visibility", "background", "background-attachment",
-    "background-blend-mode", "background-clip", "background-color",
-    "background-image", "background-origin", "background-position",
-    "background-position-x", "background-position-y", "background-repeat",
-    "background-size", "baseline-shift", "binding", "bleed", "block-size",
-    "bookmark-label", "bookmark-level", "bookmark-state", "bookmark-target",
-    "border", "border-bottom", "border-bottom-color", "border-bottom-left-radius",
-    "border-bottom-right-radius", "border-bottom-style", "border-bottom-width",
-    "border-collapse", "border-color", "border-image", "border-image-outset",
-    "border-image-repeat", "border-image-slice", "border-image-source",
-    "border-image-width", "border-left", "border-left-color", "border-left-style",
-    "border-left-width", "border-radius", "border-right", "border-right-color",
-    "border-right-style", "border-right-width", "border-spacing", "border-style",
-    "border-top", "border-top-color", "border-top-left-radius",
-    "border-top-right-radius", "border-top-style", "border-top-width",
-    "border-width", "bottom", "box-decoration-break", "box-shadow", "box-sizing",
-    "break-after", "break-before", "break-inside", "caption-side", "caret-color",
-    "clear", "clip", "color", "color-profile", "column-count", "column-fill",
-    "column-gap", "column-rule", "column-rule-color", "column-rule-style",
-    "column-rule-width", "column-span", "column-width", "columns", "contain",
-    "content", "counter-increment", "counter-reset", "crop", "cue", "cue-after",
-    "cue-before", "cursor", "direction", "display", "dominant-baseline",
-    "drop-initial-after-adjust", "drop-initial-after-align",
-    "drop-initial-before-adjust", "drop-initial-before-align", "drop-initial-size",
-    "drop-initial-value", "elevation", "empty-cells", "fit", "fit-position",
-    "flex", "flex-basis", "flex-direction", "flex-flow", "flex-grow",
-    "flex-shrink", "flex-wrap", "float", "float-offset", "flow-from", "flow-into",
-    "font", "font-family", "font-feature-settings", "font-kerning",
-    "font-language-override", "font-optical-sizing", "font-size",
-    "font-size-adjust", "font-stretch", "font-style", "font-synthesis",
-    "font-variant", "font-variant-alternates", "font-variant-caps",
-    "font-variant-east-asian", "font-variant-ligatures", "font-variant-numeric",
-    "font-variant-position", "font-variation-settings", "font-weight", "gap",
-    "grid", "grid-area", "grid-auto-columns", "grid-auto-flow", "grid-auto-rows",
-    "grid-column", "grid-column-end", "grid-column-gap", "grid-column-start",
-    "grid-gap", "grid-row", "grid-row-end", "grid-row-gap", "grid-row-start",
-    "grid-template", "grid-template-areas", "grid-template-columns",
-    "grid-template-rows", "hanging-punctuation", "height", "hyphens", "icon",
-    "image-orientation", "image-rendering", "image-resolution", "inline-box-align",
-    "inset", "inset-block", "inset-block-end", "inset-block-start", "inset-inline",
-    "inset-inline-end", "inset-inline-start", "isolation", "justify-content",
-    "justify-items", "justify-self", "left", "letter-spacing", "line-break",
-    "line-height", "line-height-step", "line-stacking", "line-stacking-ruby",
-    "line-stacking-shift", "line-stacking-strategy", "list-style",
-    "list-style-image", "list-style-position", "list-style-type", "margin",
-    "margin-bottom", "margin-left", "margin-right", "margin-top", "marks",
-    "marquee-direction", "marquee-loop", "marquee-play-count", "marquee-speed",
-    "marquee-style", "max-block-size", "max-height", "max-inline-size",
-    "max-width", "min-block-size", "min-height", "min-inline-size", "min-width",
-    "mix-blend-mode", "move-to", "nav-down", "nav-index", "nav-left", "nav-right",
-    "nav-up", "object-fit", "object-position", "offset", "offset-anchor",
-    "offset-distance", "offset-path", "offset-position", "offset-rotate",
-    "opacity", "order", "orphans", "outline", "outline-color", "outline-offset",
-    "outline-style", "outline-width", "overflow", "overflow-style",
-    "overflow-wrap", "overflow-x", "overflow-y", "padding", "padding-bottom",
-    "padding-left", "padding-right", "padding-top", "page", "page-break-after",
-    "page-break-before", "page-break-inside", "page-policy", "pause",
-    "pause-after", "pause-before", "perspective", "perspective-origin", "pitch",
-    "pitch-range", "place-content", "place-items", "place-self", "play-during",
-    "position", "presentation-level", "punctuation-trim", "quotes",
-    "region-break-after", "region-break-before", "region-break-inside",
-    "region-fragment", "rendering-intent", "resize", "rest", "rest-after",
-    "rest-before", "richness", "right", "rotate", "rotation", "rotation-point",
-    "row-gap", "ruby-align", "ruby-overhang", "ruby-position", "ruby-span",
-    "scale", "scroll-behavior", "scroll-margin", "scroll-margin-block",
-    "scroll-margin-block-end", "scroll-margin-block-start", "scroll-margin-bottom",
-    "scroll-margin-inline", "scroll-margin-inline-end",
-    "scroll-margin-inline-start", "scroll-margin-left", "scroll-margin-right",
-    "scroll-margin-top", "scroll-padding", "scroll-padding-block",
-    "scroll-padding-block-end", "scroll-padding-block-start",
-    "scroll-padding-bottom", "scroll-padding-inline", "scroll-padding-inline-end",
-    "scroll-padding-inline-start", "scroll-padding-left", "scroll-padding-right",
-    "scroll-padding-top", "scroll-snap-align", "scroll-snap-type",
-    "shape-image-threshold", "shape-inside", "shape-margin", "shape-outside",
-    "size", "speak", "speak-as", "speak-header", "speak-numeral",
-    "speak-punctuation", "speech-rate", "stress", "string-set", "tab-size",
-    "table-layout", "target", "target-name", "target-new", "target-position",
-    "text-align", "text-align-last", "text-combine-upright", "text-decoration",
-    "text-decoration-color", "text-decoration-line", "text-decoration-skip",
-    "text-decoration-skip-ink", "text-decoration-style", "text-emphasis",
-    "text-emphasis-color", "text-emphasis-position", "text-emphasis-style",
-    "text-height", "text-indent", "text-justify", "text-orientation",
-    "text-outline", "text-overflow", "text-rendering", "text-shadow",
-    "text-size-adjust", "text-space-collapse", "text-transform",
-    "text-underline-position", "text-wrap", "top", "transform", "transform-origin",
-    "transform-style", "transition", "transition-delay", "transition-duration",
-    "transition-property", "transition-timing-function", "translate",
-    "unicode-bidi", "user-select", "vertical-align", "visibility", "voice-balance",
-    "voice-duration", "voice-family", "voice-pitch", "voice-range", "voice-rate",
-    "voice-stress", "voice-volume", "volume", "white-space", "widows", "width",
-    "will-change", "word-break", "word-spacing", "word-wrap", "writing-mode", "z-index",
-    // SVG-specific
-    "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
-    "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
-    "color-interpolation", "color-interpolation-filters",
-    "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
-    "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
-    "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
-    "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
-    "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
-    "glyph-orientation-vertical", "text-anchor", "writing-mode"
-  ], propertyKeywords = keySet(propertyKeywords_);
-
-  var nonStandardPropertyKeywords_ = [
-    "border-block", "border-block-color", "border-block-end",
-    "border-block-end-color", "border-block-end-style", "border-block-end-width",
-    "border-block-start", "border-block-start-color", "border-block-start-style",
-    "border-block-start-width", "border-block-style", "border-block-width",
-    "border-inline", "border-inline-color", "border-inline-end",
-    "border-inline-end-color", "border-inline-end-style",
-    "border-inline-end-width", "border-inline-start", "border-inline-start-color",
-    "border-inline-start-style", "border-inline-start-width",
-    "border-inline-style", "border-inline-width", "margin-block",
-    "margin-block-end", "margin-block-start", "margin-inline", "margin-inline-end",
-    "margin-inline-start", "padding-block", "padding-block-end",
-    "padding-block-start", "padding-inline", "padding-inline-end",
-    "padding-inline-start", "scroll-snap-stop", "scrollbar-3d-light-color",
-    "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
-    "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
-    "scrollbar-track-color", "searchfield-cancel-button", "searchfield-decoration",
-    "searchfield-results-button", "searchfield-results-decoration", "shape-inside", "zoom"
-  ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
-
-  var fontProperties_ = [
-    "font-display", "font-family", "src", "unicode-range", "font-variant",
-     "font-feature-settings", "font-stretch", "font-weight", "font-style"
-  ], fontProperties = keySet(fontProperties_);
-
-  var counterDescriptors_ = [
-    "additive-symbols", "fallback", "negative", "pad", "prefix", "range",
-    "speak-as", "suffix", "symbols", "system"
-  ], counterDescriptors = keySet(counterDescriptors_);
-
-  var colorKeywords_ = [
-    "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
-    "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
-    "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
-    "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
-    "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
-    "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
-    "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
-    "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
-    "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
-    "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
-    "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
-    "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
-    "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
-    "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
-    "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
-    "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
-    "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
-    "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
-    "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
-    "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
-    "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
-    "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
-    "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
-    "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
-    "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
-    "whitesmoke", "yellow", "yellowgreen"
-  ], colorKeywords = keySet(colorKeywords_);
-
-  var valueKeywords_ = [
-    "above", "absolute", "activeborder", "additive", "activecaption", "afar",
-    "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
-    "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
-    "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page",
-    "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
-    "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
-    "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
-    "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian",
-    "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
-    "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
-    "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
-    "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
-    "compact", "condensed", "contain", "content", "contents",
-    "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
-    "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
-    "decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
-    "destination-in", "destination-out", "destination-over", "devanagari", "difference",
-    "disc", "discard", "disclosure-closed", "disclosure-open", "document",
-    "dot-dash", "dot-dot-dash",
-    "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
-    "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
-    "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
-    "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
-    "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
-    "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
-    "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
-    "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
-    "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
-    "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
-    "forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove",
-    "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
-    "help", "hidden", "hide", "higher", "highlight", "highlighttext",
-    "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
-    "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
-    "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
-    "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert",
-    "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
-    "katakana", "katakana-iroha", "keep-all", "khmer",
-    "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
-    "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
-    "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
-    "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
-    "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
-    "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d",
-    "media-controls-background", "media-current-time-display",
-    "media-fullscreen-button", "media-mute-button", "media-play-button",
-    "media-return-to-realtime-button", "media-rewind-button",
-    "media-seek-back-button", "media-seek-forward-button", "media-slider",
-    "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
-    "media-volume-slider-container", "media-volume-sliderthumb", "medium",
-    "menu", "menulist", "menulist-button", "menulist-text",
-    "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
-    "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
-    "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
-    "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
-    "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote",
-    "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
-    "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
-    "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
-    "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d",
-    "progress", "push-button", "radial-gradient", "radio", "read-only",
-    "read-write", "read-write-plaintext-only", "rectangle", "region",
-    "relative", "repeat", "repeating-linear-gradient",
-    "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
-    "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
-    "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
-    "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
-    "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield",
-    "searchfield-cancel-button", "searchfield-decoration",
-    "searchfield-results-button", "searchfield-results-decoration", "self-start", "self-end",
-    "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
-    "simp-chinese-formal", "simp-chinese-informal", "single",
-    "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
-    "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
-    "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
-    "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "space-evenly", "spell-out", "square",
-    "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
-    "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table",
-    "table-caption", "table-cell", "table-column", "table-column-group",
-    "table-footer-group", "table-header-group", "table-row", "table-row-group",
-    "tamil",
-    "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
-    "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
-    "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
-    "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
-    "trad-chinese-formal", "trad-chinese-informal", "transform",
-    "translate", "translate3d", "translateX", "translateY", "translateZ",
-    "transparent", "ultra-condensed", "ultra-expanded", "underline", "unset", "up",
-    "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
-    "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
-    "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
-    "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
-    "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor",
-    "xx-large", "xx-small"
-  ], valueKeywords = keySet(valueKeywords_);
-
-  var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)
-    .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)
-    .concat(valueKeywords_);
-  CodeMirror.registerHelper("hintWords", "css", allWords);
-
-  function tokenCComment(stream, state) {
-    var maybeEnd = false, ch;
-    while ((ch = stream.next()) != null) {
-      if (maybeEnd && ch == "/") {
-        state.tokenize = null;
-        break;
-      }
-      maybeEnd = (ch == "*");
-    }
-    return ["comment", "comment"];
-  }
-
-  CodeMirror.defineMIME("text/css", {
-    documentTypes: documentTypes,
-    mediaTypes: mediaTypes,
-    mediaFeatures: mediaFeatures,
-    mediaValueKeywords: mediaValueKeywords,
-    propertyKeywords: propertyKeywords,
-    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
-    fontProperties: fontProperties,
-    counterDescriptors: counterDescriptors,
-    colorKeywords: colorKeywords,
-    valueKeywords: valueKeywords,
-    tokenHooks: {
-      "/": function(stream, state) {
-        if (!stream.eat("*")) return false;
-        state.tokenize = tokenCComment;
-        return tokenCComment(stream, state);
-      }
-    },
-    name: "css"
-  });
-
-  CodeMirror.defineMIME("text/x-scss", {
-    mediaTypes: mediaTypes,
-    mediaFeatures: mediaFeatures,
-    mediaValueKeywords: mediaValueKeywords,
-    propertyKeywords: propertyKeywords,
-    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
-    colorKeywords: colorKeywords,
-    valueKeywords: valueKeywords,
-    fontProperties: fontProperties,
-    allowNested: true,
-    lineComment: "//",
-    tokenHooks: {
-      "/": function(stream, state) {
-        if (stream.eat("/")) {
-          stream.skipToEnd();
-          return ["comment", "comment"];
-        } else if (stream.eat("*")) {
-          state.tokenize = tokenCComment;
-          return tokenCComment(stream, state);
-        } else {
-          return ["operator", "operator"];
-        }
-      },
-      ":": function(stream) {
-        if (stream.match(/\s*\{/, false))
-          return [null, null]
-        return false;
-      },
-      "$": function(stream) {
-        stream.match(/^[\w-]+/);
-        if (stream.match(/^\s*:/, false))
-          return ["variable-2", "variable-definition"];
-        return ["variable-2", "variable"];
-      },
-      "#": function(stream) {
-        if (!stream.eat("{")) return false;
-        return [null, "interpolation"];
-      }
-    },
-    name: "css",
-    helperType: "scss"
-  });
-
-  CodeMirror.defineMIME("text/x-less", {
-    mediaTypes: mediaTypes,
-    mediaFeatures: mediaFeatures,
-    mediaValueKeywords: mediaValueKeywords,
-    propertyKeywords: propertyKeywords,
-    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
-    colorKeywords: colorKeywords,
-    valueKeywords: valueKeywords,
-    fontProperties: fontProperties,
-    allowNested: true,
-    lineComment: "//",
-    tokenHooks: {
-      "/": function(stream, state) {
-        if (stream.eat("/")) {
-          stream.skipToEnd();
-          return ["comment", "comment"];
-        } else if (stream.eat("*")) {
-          state.tokenize = tokenCComment;
-          return tokenCComment(stream, state);
-        } else {
-          return ["operator", "operator"];
-        }
-      },
-      "@": function(stream) {
-        if (stream.eat("{")) return [null, "interpolation"];
-        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false;
-        stream.eatWhile(/[\w\\\-]/);
-        if (stream.match(/^\s*:/, false))
-          return ["variable-2", "variable-definition"];
-        return ["variable-2", "variable"];
-      },
-      "&": function() {
-        return ["atom", "atom"];
-      }
-    },
-    name: "css",
-    helperType: "less"
-  });
-
-  CodeMirror.defineMIME("text/x-gss", {
-    documentTypes: documentTypes,
-    mediaTypes: mediaTypes,
-    mediaFeatures: mediaFeatures,
-    propertyKeywords: propertyKeywords,
-    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
-    fontProperties: fontProperties,
-    counterDescriptors: counterDescriptors,
-    colorKeywords: colorKeywords,
-    valueKeywords: valueKeywords,
-    supportsAtComponent: true,
-    tokenHooks: {
-      "/": function(stream, state) {
-        if (!stream.eat("*")) return false;
-        state.tokenize = tokenCComment;
-        return tokenCComment(stream, state);
-      }
-    },
-    name: "css",
-    helperType: "gss"
-  });
-
-});
-
-
-/* ---- mode/go.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-CodeMirror.defineMode("go", function(config) {
-  var indentUnit = config.indentUnit;
-
-  var keywords = {
-    "break":true, "case":true, "chan":true, "const":true, "continue":true,
-    "default":true, "defer":true, "else":true, "fallthrough":true, "for":true,
-    "func":true, "go":true, "goto":true, "if":true, "import":true,
-    "interface":true, "map":true, "package":true, "range":true, "return":true,
-    "select":true, "struct":true, "switch":true, "type":true, "var":true,
-    "bool":true, "byte":true, "complex64":true, "complex128":true,
-    "float32":true, "float64":true, "int8":true, "int16":true, "int32":true,
-    "int64":true, "string":true, "uint8":true, "uint16":true, "uint32":true,
-    "uint64":true, "int":true, "uint":true, "uintptr":true, "error": true,
-    "rune":true
-  };
-
-  var atoms = {
-    "true":true, "false":true, "iota":true, "nil":true, "append":true,
-    "cap":true, "close":true, "complex":true, "copy":true, "delete":true, "imag":true,
-    "len":true, "make":true, "new":true, "panic":true, "print":true,
-    "println":true, "real":true, "recover":true
-  };
-
-  var isOperatorChar = /[+\-*&^%:=<>!|\/]/;
-
-  var curPunc;
-
-  function tokenBase(stream, state) {
-    var ch = stream.next();
-    if (ch == '"' || ch == "'" || ch == "`") {
-      state.tokenize = tokenString(ch);
-      return state.tokenize(stream, state);
-    }
-    if (/[\d\.]/.test(ch)) {
-      if (ch == ".") {
-        stream.match(/^[0-9]+([eE][\-+]?[0-9]+)?/);
-      } else if (ch == "0") {
-        stream.match(/^[xX][0-9a-fA-F]+/) || stream.match(/^0[0-7]+/);
-      } else {
-        stream.match(/^[0-9]*\.?[0-9]*([eE][\-+]?[0-9]+)?/);
-      }
-      return "number";
-    }
-    if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
-      curPunc = ch;
-      return null;
-    }
-    if (ch == "/") {
-      if (stream.eat("*")) {
-        state.tokenize = tokenComment;
-        return tokenComment(stream, state);
-      }
-      if (stream.eat("/")) {
-        stream.skipToEnd();
-        return "comment";
-      }
-    }
-    if (isOperatorChar.test(ch)) {
-      stream.eatWhile(isOperatorChar);
-      return "operator";
-    }
-    stream.eatWhile(/[\w\$_\xa1-\uffff]/);
-    var cur = stream.current();
-    if (keywords.propertyIsEnumerable(cur)) {
-      if (cur == "case" || cur == "default") curPunc = "case";
-      return "keyword";
-    }
-    if (atoms.propertyIsEnumerable(cur)) return "atom";
-    return "variable";
-  }
-
-  function tokenString(quote) {
-    return function(stream, state) {
-      var escaped = false, next, end = false;
-      while ((next = stream.next()) != null) {
-        if (next == quote && !escaped) {end = true; break;}
-        escaped = !escaped && quote != "`" && next == "\\";
-      }
-      if (end || !(escaped || quote == "`"))
-        state.tokenize = tokenBase;
-      return "string";
-    };
-  }
-
-  function tokenComment(stream, state) {
-    var maybeEnd = false, ch;
-    while (ch = stream.next()) {
-      if (ch == "/" && maybeEnd) {
-        state.tokenize = tokenBase;
-        break;
-      }
-      maybeEnd = (ch == "*");
-    }
-    return "comment";
-  }
-
-  function Context(indented, column, type, align, prev) {
-    this.indented = indented;
-    this.column = column;
-    this.type = type;
-    this.align = align;
-    this.prev = prev;
-  }
-  function pushContext(state, col, type) {
-    return state.context = new Context(state.indented, col, type, null, state.context);
-  }
-  function popContext(state) {
-    if (!state.context.prev) return;
-    var t = state.context.type;
-    if (t == ")" || t == "]" || t == "}")
-      state.indented = state.context.indented;
-    return state.context = state.context.prev;
-  }
-
-  // Interface
-
-  return {
-    startState: function(basecolumn) {
-      return {
-        tokenize: null,
-        context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
-        indented: 0,
-        startOfLine: true
-      };
-    },
-
-    token: function(stream, state) {
-      var ctx = state.context;
-      if (stream.sol()) {
-        if (ctx.align == null) ctx.align = false;
-        state.indented = stream.indentation();
-        state.startOfLine = true;
-        if (ctx.type == "case") ctx.type = "}";
-      }
-      if (stream.eatSpace()) return null;
-      curPunc = null;
-      var style = (state.tokenize || tokenBase)(stream, state);
-      if (style == "comment") return style;
-      if (ctx.align == null) ctx.align = true;
-
-      if (curPunc == "{") pushContext(state, stream.column(), "}");
-      else if (curPunc == "[") pushContext(state, stream.column(), "]");
-      else if (curPunc == "(") pushContext(state, stream.column(), ")");
-      else if (curPunc == "case") ctx.type = "case";
-      else if (curPunc == "}" && ctx.type == "}") popContext(state);
-      else if (curPunc == ctx.type) popContext(state);
-      state.startOfLine = false;
-      return style;
-    },
-
-    indent: function(state, textAfter) {
-      if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
-      var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
-      if (ctx.type == "case" && /^(?:case|default)\b/.test(textAfter)) {
-        state.context.type = "}";
-        return ctx.indented;
-      }
-      var closing = firstChar == ctx.type;
-      if (ctx.align) return ctx.column + (closing ? 0 : 1);
-      else return ctx.indented + (closing ? 0 : indentUnit);
-    },
-
-    electricChars: "{}):",
-    closeBrackets: "()[]{}''\"\"``",
-    fold: "brace",
-    blockCommentStart: "/*",
-    blockCommentEnd: "*/",
-    lineComment: "//"
-  };
-});
-
-CodeMirror.defineMIME("text/x-go", "go");
-
-});
-
-
-/* ---- mode/htmlembedded.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"),
-        require("../../addon/mode/multiplex"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "../htmlmixed/htmlmixed",
-            "../../addon/mode/multiplex"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  CodeMirror.defineMode("htmlembedded", function(config, parserConfig) {
-    var closeComment = parserConfig.closeComment || "--%>"
-    return CodeMirror.multiplexingMode(CodeMirror.getMode(config, "htmlmixed"), {
-      open: parserConfig.openComment || "<%--",
-      close: closeComment,
-      delimStyle: "comment",
-      mode: {token: function(stream) {
-        stream.skipTo(closeComment) || stream.skipToEnd()
-        return "comment"
-      }}
-    }, {
-      open: parserConfig.open || parserConfig.scriptStartRegex || "<%",
-      close: parserConfig.close || parserConfig.scriptEndRegex || "%>",
-      mode: CodeMirror.getMode(config, parserConfig.scriptingModeSpec)
-    });
-  }, "htmlmixed");
-
-  CodeMirror.defineMIME("application/x-ejs", {name: "htmlembedded", scriptingModeSpec:"javascript"});
-  CodeMirror.defineMIME("application/x-aspx", {name: "htmlembedded", scriptingModeSpec:"text/x-csharp"});
-  CodeMirror.defineMIME("application/x-jsp", {name: "htmlembedded", scriptingModeSpec:"text/x-java"});
-  CodeMirror.defineMIME("application/x-erb", {name: "htmlembedded", scriptingModeSpec:"ruby"});
-});
-
-
-/* ---- mode/htmlmixed.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  var defaultTags = {
-    script: [
-      ["lang", /(javascript|babel)/i, "javascript"],
-      ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, "javascript"],
-      ["type", /./, "text/plain"],
-      [null, null, "javascript"]
-    ],
-    style:  [
-      ["lang", /^css$/i, "css"],
-      ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
-      ["type", /./, "text/plain"],
-      [null, null, "css"]
-    ]
-  };
-
-  function maybeBackup(stream, pat, style) {
-    var cur = stream.current(), close = cur.search(pat);
-    if (close > -1) {
-      stream.backUp(cur.length - close);
-    } else if (cur.match(/<\/?$/)) {
-      stream.backUp(cur.length);
-      if (!stream.match(pat, false)) stream.match(cur);
-    }
-    return style;
-  }
-
-  var attrRegexpCache = {};
-  function getAttrRegexp(attr) {
-    var regexp = attrRegexpCache[attr];
-    if (regexp) return regexp;
-    return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
-  }
-
-  function getAttrValue(text, attr) {
-    var match = text.match(getAttrRegexp(attr))
-    return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
-  }
-
-  function getTagRegexp(tagName, anchored) {
-    return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i");
-  }
-
-  function addTags(from, to) {
-    for (var tag in from) {
-      var dest = to[tag] || (to[tag] = []);
-      var source = from[tag];
-      for (var i = source.length - 1; i >= 0; i--)
-        dest.unshift(source[i])
-    }
-  }
-
-  function findMatchingMode(tagInfo, tagText) {
-    for (var i = 0; i < tagInfo.length; i++) {
-      var spec = tagInfo[i];
-      if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
-    }
-  }
-
-  CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
-    var htmlMode = CodeMirror.getMode(config, {
-      name: "xml",
-      htmlMode: true,
-      multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
-      multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag
-    });
-
-    var tags = {};
-    var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
-    addTags(defaultTags, tags);
-    if (configTags) addTags(configTags, tags);
-    if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
-      tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
-
-    function html(stream, state) {
-      var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
-      if (tag && !/[<>\s\/]/.test(stream.current()) &&
-          (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
-          tags.hasOwnProperty(tagName)) {
-        state.inTag = tagName + " "
-      } else if (state.inTag && tag && />$/.test(stream.current())) {
-        var inTag = /^([\S]+) (.*)/.exec(state.inTag)
-        state.inTag = null
-        var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
-        var mode = CodeMirror.getMode(config, modeSpec)
-        var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
-        state.token = function (stream, state) {
-          if (stream.match(endTagA, false)) {
-            state.token = html;
-            state.localState = state.localMode = null;
-            return null;
-          }
-          return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
-        };
-        state.localMode = mode;
-        state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "", ""));
-      } else if (state.inTag) {
-        state.inTag += stream.current()
-        if (stream.eol()) state.inTag += " "
-      }
-      return style;
-    };
-
-    return {
-      startState: function () {
-        var state = CodeMirror.startState(htmlMode);
-        return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
-      },
-
-      copyState: function (state) {
-        var local;
-        if (state.localState) {
-          local = CodeMirror.copyState(state.localMode, state.localState);
-        }
-        return {token: state.token, inTag: state.inTag,
-                localMode: state.localMode, localState: local,
-                htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
-      },
-
-      token: function (stream, state) {
-        return state.token(stream, state);
-      },
-
-      indent: function (state, textAfter, line) {
-        if (!state.localMode || /^\s*<\//.test(textAfter))
-          return htmlMode.indent(state.htmlState, textAfter, line);
-        else if (state.localMode.indent)
-          return state.localMode.indent(state.localState, textAfter, line);
-        else
-          return CodeMirror.Pass;
-      },
-
-      innerMode: function (state) {
-        return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
-      }
-    };
-  }, "xml", "javascript", "css");
-
-  CodeMirror.defineMIME("text/html", "htmlmixed");
-});
-
-
-/* ---- mode/javascript.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-CodeMirror.defineMode("javascript", function(config, parserConfig) {
-  var indentUnit = config.indentUnit;
-  var statementIndent = parserConfig.statementIndent;
-  var jsonldMode = parserConfig.jsonld;
-  var jsonMode = parserConfig.json || jsonldMode;
-  var isTS = parserConfig.typescript;
-  var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
-
-  // Tokenizer
-
-  var keywords = function(){
-    function kw(type) {return {type: type, style: "keyword"};}
-    var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
-    var operator = kw("operator"), atom = {type: "atom", style: "atom"};
-
-    return {
-      "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
-      "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
-      "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
-      "function": kw("function"), "catch": kw("catch"),
-      "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
-      "in": operator, "typeof": operator, "instanceof": operator,
-      "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
-      "this": kw("this"), "class": kw("class"), "super": kw("atom"),
-      "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
-      "await": C
-    };
-  }();
-
-  var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
-  var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
-
-  function readRegexp(stream) {
-    var escaped = false, next, inSet = false;
-    while ((next = stream.next()) != null) {
-      if (!escaped) {
-        if (next == "/" && !inSet) return;
-        if (next == "[") inSet = true;
-        else if (inSet && next == "]") inSet = false;
-      }
-      escaped = !escaped && next == "\\";
-    }
-  }
-
-  // Used as scratch variables to communicate multiple values without
-  // consing up tons of objects.
-  var type, content;
-  function ret(tp, style, cont) {
-    type = tp; content = cont;
-    return style;
-  }
-  function tokenBase(stream, state) {
-    var ch = stream.next();
-    if (ch == '"' || ch == "'") {
-      state.tokenize = tokenString(ch);
-      return state.tokenize(stream, state);
-    } else if (ch == "." && stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)) {
-      return ret("number", "number");
-    } else if (ch == "." && stream.match("..")) {
-      return ret("spread", "meta");
-    } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
-      return ret(ch);
-    } else if (ch == "=" && stream.eat(">")) {
-      return ret("=>", "operator");
-    } else if (ch == "0" && stream.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/)) {
-      return ret("number", "number");
-    } else if (/\d/.test(ch)) {
-      stream.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/);
-      return ret("number", "number");
-    } else if (ch == "/") {
-      if (stream.eat("*")) {
-        state.tokenize = tokenComment;
-        return tokenComment(stream, state);
-      } else if (stream.eat("/")) {
-        stream.skipToEnd();
-        return ret("comment", "comment");
-      } else if (expressionAllowed(stream, state, 1)) {
-        readRegexp(stream);
-        stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/);
-        return ret("regexp", "string-2");
-      } else {
-        stream.eat("=");
-        return ret("operator", "operator", stream.current());
-      }
-    } else if (ch == "`") {
-      state.tokenize = tokenQuasi;
-      return tokenQuasi(stream, state);
-    } else if (ch == "#" && stream.peek() == "!") {
-      stream.skipToEnd();
-      return ret("meta", "meta");
-    } else if (ch == "#" && stream.eatWhile(wordRE)) {
-      return ret("variable", "property")
-    } else if (ch == "<" && stream.match("!--") ||
-               (ch == "-" && stream.match("->") && !/\S/.test(stream.string.slice(0, stream.start)))) {
-      stream.skipToEnd()
-      return ret("comment", "comment")
-    } else if (isOperatorChar.test(ch)) {
-      if (ch != ">" || !state.lexical || state.lexical.type != ">") {
-        if (stream.eat("=")) {
-          if (ch == "!" || ch == "=") stream.eat("=")
-        } else if (/[<>*+\-]/.test(ch)) {
-          stream.eat(ch)
-          if (ch == ">") stream.eat(ch)
-        }
-      }
-      if (ch == "?" && stream.eat(".")) return ret(".")
-      return ret("operator", "operator", stream.current());
-    } else if (wordRE.test(ch)) {
-      stream.eatWhile(wordRE);
-      var word = stream.current()
-      if (state.lastType != ".") {
-        if (keywords.propertyIsEnumerable(word)) {
-          var kw = keywords[word]
-          return ret(kw.type, kw.style, word)
-        }
-        if (word == "async" && stream.match(/^(\s|\/\*.*?\*\/)*[\[\(\w]/, false))
-          return ret("async", "keyword", word)
-      }
-      return ret("variable", "variable", word)
-    }
-  }
-
-  function tokenString(quote) {
-    return function(stream, state) {
-      var escaped = false, next;
-      if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
-        state.tokenize = tokenBase;
-        return ret("jsonld-keyword", "meta");
-      }
-      while ((next = stream.next()) != null) {
-        if (next == quote && !escaped) break;
-        escaped = !escaped && next == "\\";
-      }
-      if (!escaped) state.tokenize = tokenBase;
-      return ret("string", "string");
-    };
-  }
-
-  function tokenComment(stream, state) {
-    var maybeEnd = false, ch;
-    while (ch = stream.next()) {
-      if (ch == "/" && maybeEnd) {
-        state.tokenize = tokenBase;
-        break;
-      }
-      maybeEnd = (ch == "*");
-    }
-    return ret("comment", "comment");
-  }
-
-  function tokenQuasi(stream, state) {
-    var escaped = false, next;
-    while ((next = stream.next()) != null) {
-      if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
-        state.tokenize = tokenBase;
-        break;
-      }
-      escaped = !escaped && next == "\\";
-    }
-    return ret("quasi", "string-2", stream.current());
-  }
-
-  var brackets = "([{}])";
-  // This is a crude lookahead trick to try and notice that we're
-  // parsing the argument patterns for a fat-arrow function before we
-  // actually hit the arrow token. It only works if the arrow is on
-  // the same line as the arguments and there's no strange noise
-  // (comments) in between. Fallback is to only notice when we hit the
-  // arrow, and not declare the arguments as locals for the arrow
-  // body.
-  function findFatArrow(stream, state) {
-    if (state.fatArrowAt) state.fatArrowAt = null;
-    var arrow = stream.string.indexOf("=>", stream.start);
-    if (arrow < 0) return;
-
-    if (isTS) { // Try to skip TypeScript return type declarations after the arguments
-      var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
-      if (m) arrow = m.index
-    }
-
-    var depth = 0, sawSomething = false;
-    for (var pos = arrow - 1; pos >= 0; --pos) {
-      var ch = stream.string.charAt(pos);
-      var bracket = brackets.indexOf(ch);
-      if (bracket >= 0 && bracket < 3) {
-        if (!depth) { ++pos; break; }
-        if (--depth == 0) { if (ch == "(") sawSomething = true; break; }
-      } else if (bracket >= 3 && bracket < 6) {
-        ++depth;
-      } else if (wordRE.test(ch)) {
-        sawSomething = true;
-      } else if (/["'\/`]/.test(ch)) {
-        for (;; --pos) {
-          if (pos == 0) return
-          var next = stream.string.charAt(pos - 1)
-          if (next == ch && stream.string.charAt(pos - 2) != "\\") { pos--; break }
-        }
-      } else if (sawSomething && !depth) {
-        ++pos;
-        break;
-      }
-    }
-    if (sawSomething && !depth) state.fatArrowAt = pos;
-  }
-
-  // Parser
-
-  var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
-
-  function JSLexical(indented, column, type, align, prev, info) {
-    this.indented = indented;
-    this.column = column;
-    this.type = type;
-    this.prev = prev;
-    this.info = info;
-    if (align != null) this.align = align;
-  }
-
-  function inScope(state, varname) {
-    for (var v = state.localVars; v; v = v.next)
-      if (v.name == varname) return true;
-    for (var cx = state.context; cx; cx = cx.prev) {
-      for (var v = cx.vars; v; v = v.next)
-        if (v.name == varname) return true;
-    }
-  }
-
-  function parseJS(state, style, type, content, stream) {
-    var cc = state.cc;
-    // Communicate our context to the combinators.
-    // (Less wasteful than consing up a hundred closures on every call.)
-    cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
-
-    if (!state.lexical.hasOwnProperty("align"))
-      state.lexical.align = true;
-
-    while(true) {
-      var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
-      if (combinator(type, content)) {
-        while(cc.length && cc[cc.length - 1].lex)
-          cc.pop()();
-        if (cx.marked) return cx.marked;
-        if (type == "variable" && inScope(state, content)) return "variable-2";
-        return style;
-      }
-    }
-  }
-
-  // Combinator utils
-
-  var cx = {state: null, column: null, marked: null, cc: null};
-  function pass() {
-    for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
-  }
-  function cont() {
-    pass.apply(null, arguments);
-    return true;
-  }
-  function inList(name, list) {
-    for (var v = list; v; v = v.next) if (v.name == name) return true
-    return false;
-  }
-  function register(varname) {
-    var state = cx.state;
-    cx.marked = "def";
-    if (state.context) {
-      if (state.lexical.info == "var" && state.context && state.context.block) {
-        // FIXME function decls are also not block scoped
-        var newContext = registerVarScoped(varname, state.context)
-        if (newContext != null) {
-          state.context = newContext
-          return
-        }
-      } else if (!inList(varname, state.localVars)) {
-        state.localVars = new Var(varname, state.localVars)
-        return
-      }
-    }
-    // Fall through means this is global
-    if (parserConfig.globalVars && !inList(varname, state.globalVars))
-      state.globalVars = new Var(varname, state.globalVars)
-  }
-  function registerVarScoped(varname, context) {
-    if (!context) {
-      return null
-    } else if (context.block) {
-      var inner = registerVarScoped(varname, context.prev)
-      if (!inner) return null
-      if (inner == context.prev) return context
-      return new Context(inner, context.vars, true)
-    } else if (inList(varname, context.vars)) {
-      return context
-    } else {
-      return new Context(context.prev, new Var(varname, context.vars), false)
-    }
-  }
-
-  function isModifier(name) {
-    return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
-  }
-
-  // Combinators
-
-  function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block }
-  function Var(name, next) { this.name = name; this.next = next }
-
-  var defaultVars = new Var("this", new Var("arguments", null))
-  function pushcontext() {
-    cx.state.context = new Context(cx.state.context, cx.state.localVars, false)
-    cx.state.localVars = defaultVars
-  }
-  function pushblockcontext() {
-    cx.state.context = new Context(cx.state.context, cx.state.localVars, true)
-    cx.state.localVars = null
-  }
-  function popcontext() {
-    cx.state.localVars = cx.state.context.vars
-    cx.state.context = cx.state.context.prev
-  }
-  popcontext.lex = true
-  function pushlex(type, info) {
-    var result = function() {
-      var state = cx.state, indent = state.indented;
-      if (state.lexical.type == "stat") indent = state.lexical.indented;
-      else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
-        indent = outer.indented;
-      state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
-    };
-    result.lex = true;
-    return result;
-  }
-  function poplex() {
-    var state = cx.state;
-    if (state.lexical.prev) {
-      if (state.lexical.type == ")")
-        state.indented = state.lexical.indented;
-      state.lexical = state.lexical.prev;
-    }
-  }
-  poplex.lex = true;
-
-  function expect(wanted) {
-    function exp(type) {
-      if (type == wanted) return cont();
-      else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass();
-      else return cont(exp);
-    };
-    return exp;
-  }
-
-  function statement(type, value) {
-    if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex);
-    if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
-    if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
-    if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex);
-    if (type == "debugger") return cont(expect(";"));
-    if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext);
-    if (type == ";") return cont();
-    if (type == "if") {
-      if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
-        cx.state.cc.pop()();
-      return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
-    }
-    if (type == "function") return cont(functiondef);
-    if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
-    if (type == "class" || (isTS && value == "interface")) {
-      cx.marked = "keyword"
-      return cont(pushlex("form", type == "class" ? type : value), className, poplex)
-    }
-    if (type == "variable") {
-      if (isTS && value == "declare") {
-        cx.marked = "keyword"
-        return cont(statement)
-      } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) {
-        cx.marked = "keyword"
-        if (value == "enum") return cont(enumdef);
-        else if (value == "type") return cont(typename, expect("operator"), typeexpr, expect(";"));
-        else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
-      } else if (isTS && value == "namespace") {
-        cx.marked = "keyword"
-        return cont(pushlex("form"), expression, statement, poplex)
-      } else if (isTS && value == "abstract") {
-        cx.marked = "keyword"
-        return cont(statement)
-      } else {
-        return cont(pushlex("stat"), maybelabel);
-      }
-    }
-    if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext,
-                                      block, poplex, poplex, popcontext);
-    if (type == "case") return cont(expression, expect(":"));
-    if (type == "default") return cont(expect(":"));
-    if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext);
-    if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
-    if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
-    if (type == "async") return cont(statement)
-    if (value == "@") return cont(expression, statement)
-    return pass(pushlex("stat"), expression, expect(";"), poplex);
-  }
-  function maybeCatchBinding(type) {
-    if (type == "(") return cont(funarg, expect(")"))
-  }
-  function expression(type, value) {
-    return expressionInner(type, value, false);
-  }
-  function expressionNoComma(type, value) {
-    return expressionInner(type, value, true);
-  }
-  function parenExpr(type) {
-    if (type != "(") return pass()
-    return cont(pushlex(")"), maybeexpression, expect(")"), poplex)
-  }
-  function expressionInner(type, value, noComma) {
-    if (cx.state.fatArrowAt == cx.stream.start) {
-      var body = noComma ? arrowBodyNoComma : arrowBody;
-      if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
-      else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
-    }
-
-    var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
-    if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
-    if (type == "function") return cont(functiondef, maybeop);
-    if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
-    if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
-    if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
-    if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
-    if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
-    if (type == "{") return contCommasep(objprop, "}", null, maybeop);
-    if (type == "quasi") return pass(quasi, maybeop);
-    if (type == "new") return cont(maybeTarget(noComma));
-    if (type == "import") return cont(expression);
-    return cont();
-  }
-  function maybeexpression(type) {
-    if (type.match(/[;\}\)\],]/)) return pass();
-    return pass(expression);
-  }
-
-  function maybeoperatorComma(type, value) {
-    if (type == ",") return cont(maybeexpression);
-    return maybeoperatorNoComma(type, value, false);
-  }
-  function maybeoperatorNoComma(type, value, noComma) {
-    var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
-    var expr = noComma == false ? expression : expressionNoComma;
-    if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
-    if (type == "operator") {
-      if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
-      if (isTS && value == "<" && cx.stream.match(/^([^<>]|<[^<>]*>)*>\s*\(/, false))
-        return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me);
-      if (value == "?") return cont(expression, expect(":"), expr);
-      return cont(expr);
-    }
-    if (type == "quasi") { return pass(quasi, me); }
-    if (type == ";") return;
-    if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
-    if (type == ".") return cont(property, me);
-    if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
-    if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
-    if (type == "regexp") {
-      cx.state.lastType = cx.marked = "operator"
-      cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
-      return cont(expr)
-    }
-  }
-  function quasi(type, value) {
-    if (type != "quasi") return pass();
-    if (value.slice(value.length - 2) != "${") return cont(quasi);
-    return cont(expression, continueQuasi);
-  }
-  function continueQuasi(type) {
-    if (type == "}") {
-      cx.marked = "string-2";
-      cx.state.tokenize = tokenQuasi;
-      return cont(quasi);
-    }
-  }
-  function arrowBody(type) {
-    findFatArrow(cx.stream, cx.state);
-    return pass(type == "{" ? statement : expression);
-  }
-  function arrowBodyNoComma(type) {
-    findFatArrow(cx.stream, cx.state);
-    return pass(type == "{" ? statement : expressionNoComma);
-  }
-  function maybeTarget(noComma) {
-    return function(type) {
-      if (type == ".") return cont(noComma ? targetNoComma : target);
-      else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)
-      else return pass(noComma ? expressionNoComma : expression);
-    };
-  }
-  function target(_, value) {
-    if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
-  }
-  function targetNoComma(_, value) {
-    if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
-  }
-  function maybelabel(type) {
-    if (type == ":") return cont(poplex, statement);
-    return pass(maybeoperatorComma, expect(";"), poplex);
-  }
-  function property(type) {
-    if (type == "variable") {cx.marked = "property"; return cont();}
-  }
-  function objprop(type, value) {
-    if (type == "async") {
-      cx.marked = "property";
-      return cont(objprop);
-    } else if (type == "variable" || cx.style == "keyword") {
-      cx.marked = "property";
-      if (value == "get" || value == "set") return cont(getterSetter);
-      var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params
-      if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
-        cx.state.fatArrowAt = cx.stream.pos + m[0].length
-      return cont(afterprop);
-    } else if (type == "number" || type == "string") {
-      cx.marked = jsonldMode ? "property" : (cx.style + " property");
-      return cont(afterprop);
-    } else if (type == "jsonld-keyword") {
-      return cont(afterprop);
-    } else if (isTS && isModifier(value)) {
-      cx.marked = "keyword"
-      return cont(objprop)
-    } else if (type == "[") {
-      return cont(expression, maybetype, expect("]"), afterprop);
-    } else if (type == "spread") {
-      return cont(expressionNoComma, afterprop);
-    } else if (value == "*") {
-      cx.marked = "keyword";
-      return cont(objprop);
-    } else if (type == ":") {
-      return pass(afterprop)
-    }
-  }
-  function getterSetter(type) {
-    if (type != "variable") return pass(afterprop);
-    cx.marked = "property";
-    return cont(functiondef);
-  }
-  function afterprop(type) {
-    if (type == ":") return cont(expressionNoComma);
-    if (type == "(") return pass(functiondef);
-  }
-  function commasep(what, end, sep) {
-    function proceed(type, value) {
-      if (sep ? sep.indexOf(type) > -1 : type == ",") {
-        var lex = cx.state.lexical;
-        if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
-        return cont(function(type, value) {
-          if (type == end || value == end) return pass()
-          return pass(what)
-        }, proceed);
-      }
-      if (type == end || value == end) return cont();
-      if (sep && sep.indexOf(";") > -1) return pass(what)
-      return cont(expect(end));
-    }
-    return function(type, value) {
-      if (type == end || value == end) return cont();
-      return pass(what, proceed);
-    };
-  }
-  function contCommasep(what, end, info) {
-    for (var i = 3; i < arguments.length; i++)
-      cx.cc.push(arguments[i]);
-    return cont(pushlex(end, info), commasep(what, end), poplex);
-  }
-  function block(type) {
-    if (type == "}") return cont();
-    return pass(statement, block);
-  }
-  function maybetype(type, value) {
-    if (isTS) {
-      if (type == ":") return cont(typeexpr);
-      if (value == "?") return cont(maybetype);
-    }
-  }
-  function maybetypeOrIn(type, value) {
-    if (isTS && (type == ":" || value == "in")) return cont(typeexpr)
-  }
-  function mayberettype(type) {
-    if (isTS && type == ":") {
-      if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr)
-      else return cont(typeexpr)
-    }
-  }
-  function isKW(_, value) {
-    if (value == "is") {
-      cx.marked = "keyword"
-      return cont()
-    }
-  }
-  function typeexpr(type, value) {
-    if (value == "keyof" || value == "typeof" || value == "infer") {
-      cx.marked = "keyword"
-      return cont(value == "typeof" ? expressionNoComma : typeexpr)
-    }
-    if (type == "variable" || value == "void") {
-      cx.marked = "type"
-      return cont(afterType)
-    }
-    if (value == "|" || value == "&") return cont(typeexpr)
-    if (type == "string" || type == "number" || type == "atom") return cont(afterType);
-    if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType)
-    if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType)
-    if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType, afterType)
-    if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr)
-  }
-  function maybeReturnType(type) {
-    if (type == "=>") return cont(typeexpr)
-  }
-  function typeprop(type, value) {
-    if (type == "variable" || cx.style == "keyword") {
-      cx.marked = "property"
-      return cont(typeprop)
-    } else if (value == "?" || type == "number" || type == "string") {
-      return cont(typeprop)
-    } else if (type == ":") {
-      return cont(typeexpr)
-    } else if (type == "[") {
-      return cont(expect("variable"), maybetypeOrIn, expect("]"), typeprop)
-    } else if (type == "(") {
-      return pass(functiondecl, typeprop)
-    }
-  }
-  function typearg(type, value) {
-    if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg)
-    if (type == ":") return cont(typeexpr)
-    if (type == "spread") return cont(typearg)
-    return pass(typeexpr)
-  }
-  function afterType(type, value) {
-    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
-    if (value == "|" || type == "." || value == "&") return cont(typeexpr)
-    if (type == "[") return cont(typeexpr, expect("]"), afterType)
-    if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
-    if (value == "?") return cont(typeexpr, expect(":"), typeexpr)
-  }
-  function maybeTypeArgs(_, value) {
-    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
-  }
-  function typeparam() {
-    return pass(typeexpr, maybeTypeDefault)
-  }
-  function maybeTypeDefault(_, value) {
-    if (value == "=") return cont(typeexpr)
-  }
-  function vardef(_, value) {
-    if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)}
-    return pass(pattern, maybetype, maybeAssign, vardefCont);
-  }
-  function pattern(type, value) {
-    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
-    if (type == "variable") { register(value); return cont(); }
-    if (type == "spread") return cont(pattern);
-    if (type == "[") return contCommasep(eltpattern, "]");
-    if (type == "{") return contCommasep(proppattern, "}");
-  }
-  function proppattern(type, value) {
-    if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
-      register(value);
-      return cont(maybeAssign);
-    }
-    if (type == "variable") cx.marked = "property";
-    if (type == "spread") return cont(pattern);
-    if (type == "}") return pass();
-    if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern);
-    return cont(expect(":"), pattern, maybeAssign);
-  }
-  function eltpattern() {
-    return pass(pattern, maybeAssign)
-  }
-  function maybeAssign(_type, value) {
-    if (value == "=") return cont(expressionNoComma);
-  }
-  function vardefCont(type) {
-    if (type == ",") return cont(vardef);
-  }
-  function maybeelse(type, value) {
-    if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
-  }
-  function forspec(type, value) {
-    if (value == "await") return cont(forspec);
-    if (type == "(") return cont(pushlex(")"), forspec1, poplex);
-  }
-  function forspec1(type) {
-    if (type == "var") return cont(vardef, forspec2);
-    if (type == "variable") return cont(forspec2);
-    return pass(forspec2)
-  }
-  function forspec2(type, value) {
-    if (type == ")") return cont()
-    if (type == ";") return cont(forspec2)
-    if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression, forspec2) }
-    return pass(expression, forspec2)
-  }
-  function functiondef(type, value) {
-    if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
-    if (type == "variable") {register(value); return cont(functiondef);}
-    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
-    if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
-  }
-  function functiondecl(type, value) {
-    if (value == "*") {cx.marked = "keyword"; return cont(functiondecl);}
-    if (type == "variable") {register(value); return cont(functiondecl);}
-    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext);
-    if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl)
-  }
-  function typename(type, value) {
-    if (type == "keyword" || type == "variable") {
-      cx.marked = "type"
-      return cont(typename)
-    } else if (value == "<") {
-      return cont(pushlex(">"), commasep(typeparam, ">"), poplex)
-    }
-  }
-  function funarg(type, value) {
-    if (value == "@") cont(expression, funarg)
-    if (type == "spread") return cont(funarg);
-    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
-    if (isTS && type == "this") return cont(maybetype, maybeAssign)
-    return pass(pattern, maybetype, maybeAssign);
-  }
-  function classExpression(type, value) {
-    // Class expressions may have an optional name.
-    if (type == "variable") return className(type, value);
-    return classNameAfter(type, value);
-  }
-  function className(type, value) {
-    if (type == "variable") {register(value); return cont(classNameAfter);}
-  }
-  function classNameAfter(type, value) {
-    if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
-    if (value == "extends" || value == "implements" || (isTS && type == ",")) {
-      if (value == "implements") cx.marked = "keyword";
-      return cont(isTS ? typeexpr : expression, classNameAfter);
-    }
-    if (type == "{") return cont(pushlex("}"), classBody, poplex);
-  }
-  function classBody(type, value) {
-    if (type == "async" ||
-        (type == "variable" &&
-         (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
-         cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
-      cx.marked = "keyword";
-      return cont(classBody);
-    }
-    if (type == "variable" || cx.style == "keyword") {
-      cx.marked = "property";
-      return cont(classfield, classBody);
-    }
-    if (type == "number" || type == "string") return cont(classfield, classBody);
-    if (type == "[")
-      return cont(expression, maybetype, expect("]"), classfield, classBody)
-    if (value == "*") {
-      cx.marked = "keyword";
-      return cont(classBody);
-    }
-    if (isTS && type == "(") return pass(functiondecl, classBody)
-    if (type == ";" || type == ",") return cont(classBody);
-    if (type == "}") return cont();
-    if (value == "@") return cont(expression, classBody)
-  }
-  function classfield(type, value) {
-    if (value == "?") return cont(classfield)
-    if (type == ":") return cont(typeexpr, maybeAssign)
-    if (value == "=") return cont(expressionNoComma)
-    var context = cx.state.lexical.prev, isInterface = context && context.info == "interface"
-    return pass(isInterface ? functiondecl : functiondef)
-  }
-  function afterExport(type, value) {
-    if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
-    if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
-    if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
-    return pass(statement);
-  }
-  function exportField(type, value) {
-    if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
-    if (type == "variable") return pass(expressionNoComma, exportField);
-  }
-  function afterImport(type) {
-    if (type == "string") return cont();
-    if (type == "(") return pass(expression);
-    return pass(importSpec, maybeMoreImports, maybeFrom);
-  }
-  function importSpec(type, value) {
-    if (type == "{") return contCommasep(importSpec, "}");
-    if (type == "variable") register(value);
-    if (value == "*") cx.marked = "keyword";
-    return cont(maybeAs);
-  }
-  function maybeMoreImports(type) {
-    if (type == ",") return cont(importSpec, maybeMoreImports)
-  }
-  function maybeAs(_type, value) {
-    if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
-  }
-  function maybeFrom(_type, value) {
-    if (value == "from") { cx.marked = "keyword"; return cont(expression); }
-  }
-  function arrayLiteral(type) {
-    if (type == "]") return cont();
-    return pass(commasep(expressionNoComma, "]"));
-  }
-  function enumdef() {
-    return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex)
-  }
-  function enummember() {
-    return pass(pattern, maybeAssign);
-  }
-
-  function isContinuedStatement(state, textAfter) {
-    return state.lastType == "operator" || state.lastType == "," ||
-      isOperatorChar.test(textAfter.charAt(0)) ||
-      /[,.]/.test(textAfter.charAt(0));
-  }
-
-  function expressionAllowed(stream, state, backUp) {
-    return state.tokenize == tokenBase &&
-      /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
-      (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
-  }
-
-  // Interface
-
-  return {
-    startState: function(basecolumn) {
-      var state = {
-        tokenize: tokenBase,
-        lastType: "sof",
-        cc: [],
-        lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
-        localVars: parserConfig.localVars,
-        context: parserConfig.localVars && new Context(null, null, false),
-        indented: basecolumn || 0
-      };
-      if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
-        state.globalVars = parserConfig.globalVars;
-      return state;
-    },
-
-    token: function(stream, state) {
-      if (stream.sol()) {
-        if (!state.lexical.hasOwnProperty("align"))
-          state.lexical.align = false;
-        state.indented = stream.indentation();
-        findFatArrow(stream, state);
-      }
-      if (state.tokenize != tokenComment && stream.eatSpace()) return null;
-      var style = state.tokenize(stream, state);
-      if (type == "comment") return style;
-      state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
-      return parseJS(state, style, type, content, stream);
-    },
-
-    indent: function(state, textAfter) {
-      if (state.tokenize == tokenComment) return CodeMirror.Pass;
-      if (state.tokenize != tokenBase) return 0;
-      var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
-      // Kludge to prevent 'maybelse' from blocking lexical scope pops
-      if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
-        var c = state.cc[i];
-        if (c == poplex) lexical = lexical.prev;
-        else if (c != maybeelse) break;
-      }
-      while ((lexical.type == "stat" || lexical.type == "form") &&
-             (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
-                                   (top == maybeoperatorComma || top == maybeoperatorNoComma) &&
-                                   !/^[,\.=+\-*:?[\(]/.test(textAfter))))
-        lexical = lexical.prev;
-      if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
-        lexical = lexical.prev;
-      var type = lexical.type, closing = firstChar == type;
-
-      if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0);
-      else if (type == "form" && firstChar == "{") return lexical.indented;
-      else if (type == "form") return lexical.indented + indentUnit;
-      else if (type == "stat")
-        return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
-      else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
-        return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
-      else if (lexical.align) return lexical.column + (closing ? 0 : 1);
-      else return lexical.indented + (closing ? 0 : indentUnit);
-    },
-
-    electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
-    blockCommentStart: jsonMode ? null : "/*",
-    blockCommentEnd: jsonMode ? null : "*/",
-    blockCommentContinue: jsonMode ? null : " * ",
-    lineComment: jsonMode ? null : "//",
-    fold: "brace",
-    closeBrackets: "()[]{}''\"\"``",
-
-    helperType: jsonMode ? "json" : "javascript",
-    jsonldMode: jsonldMode,
-    jsonMode: jsonMode,
-
-    expressionAllowed: expressionAllowed,
-
-    skipExpression: function(state) {
-      var top = state.cc[state.cc.length - 1]
-      if (top == expression || top == expressionNoComma) state.cc.pop()
-    }
-  };
-});
-
-CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
-
-CodeMirror.defineMIME("text/javascript", "javascript");
-CodeMirror.defineMIME("text/ecmascript", "javascript");
-CodeMirror.defineMIME("application/javascript", "javascript");
-CodeMirror.defineMIME("application/x-javascript", "javascript");
-CodeMirror.defineMIME("application/ecmascript", "javascript");
-CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
-CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
-CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
-CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
-CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
-
-});
-
-
-/* ---- mode/markdown.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("../xml/xml"), require("../meta"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "../xml/xml", "../meta"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
-
-  var htmlMode = CodeMirror.getMode(cmCfg, "text/html");
-  var htmlModeMissing = htmlMode.name == "null"
-
-  function getMode(name) {
-    if (CodeMirror.findModeByName) {
-      var found = CodeMirror.findModeByName(name);
-      if (found) name = found.mime || found.mimes[0];
-    }
-    var mode = CodeMirror.getMode(cmCfg, name);
-    return mode.name == "null" ? null : mode;
-  }
-
-  // Should characters that affect highlighting be highlighted separate?
-  // Does not include characters that will be output (such as `1.` and `-` for lists)
-  if (modeCfg.highlightFormatting === undefined)
-    modeCfg.highlightFormatting = false;
-
-  // Maximum number of nested blockquotes. Set to 0 for infinite nesting.
-  // Excess `>` will emit `error` token.
-  if (modeCfg.maxBlockquoteDepth === undefined)
-    modeCfg.maxBlockquoteDepth = 0;
-
-  // Turn on task lists? ("- [ ] " and "- [x] ")
-  if (modeCfg.taskLists === undefined) modeCfg.taskLists = false;
-
-  // Turn on strikethrough syntax
-  if (modeCfg.strikethrough === undefined)
-    modeCfg.strikethrough = false;
-
-  if (modeCfg.emoji === undefined)
-    modeCfg.emoji = false;
-
-  if (modeCfg.fencedCodeBlockHighlighting === undefined)
-    modeCfg.fencedCodeBlockHighlighting = true;
-
-  if (modeCfg.fencedCodeBlockDefaultMode === undefined)
-    modeCfg.fencedCodeBlockDefaultMode = 'text/plain';
-
-  if (modeCfg.xml === undefined)
-    modeCfg.xml = true;
-
-  // Allow token types to be overridden by user-provided token types.
-  if (modeCfg.tokenTypeOverrides === undefined)
-    modeCfg.tokenTypeOverrides = {};
-
-  var tokenTypes = {
-    header: "header",
-    code: "comment",
-    quote: "quote",
-    list1: "variable-2",
-    list2: "variable-3",
-    list3: "keyword",
-    hr: "hr",
-    image: "image",
-    imageAltText: "image-alt-text",
-    imageMarker: "image-marker",
-    formatting: "formatting",
-    linkInline: "link",
-    linkEmail: "link",
-    linkText: "link",
-    linkHref: "string",
-    em: "em",
-    strong: "strong",
-    strikethrough: "strikethrough",
-    emoji: "builtin"
-  };
-
-  for (var tokenType in tokenTypes) {
-    if (tokenTypes.hasOwnProperty(tokenType) && modeCfg.tokenTypeOverrides[tokenType]) {
-      tokenTypes[tokenType] = modeCfg.tokenTypeOverrides[tokenType];
-    }
-  }
-
-  var hrRE = /^([*\-_])(?:\s*\1){2,}\s*$/
-  ,   listRE = /^(?:[*\-+]|^[0-9]+([.)]))\s+/
-  ,   taskListRE = /^\[(x| )\](?=\s)/i // Must follow listRE
-  ,   atxHeaderRE = modeCfg.allowAtxHeaderWithoutSpace ? /^(#+)/ : /^(#+)(?: |$)/
-  ,   setextHeaderRE = /^ {0,3}(?:\={1,}|-{2,})\s*$/
-  ,   textRE = /^[^#!\[\]*_\\<>` "'(~:]+/
-  ,   fencedCodeRE = /^(~~~+|```+)[ \t]*([\w\/+#-]*)[^\n`]*$/
-  ,   linkDefRE = /^\s*\[[^\]]+?\]:.*$/ // naive link-definition
-  ,   punctuation = /[!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E42\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC9\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDF3C-\uDF3E]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]/
-  ,   expandedTab = "    " // CommonMark specifies tab as 4 spaces
-
-  function switchInline(stream, state, f) {
-    state.f = state.inline = f;
-    return f(stream, state);
-  }
-
-  function switchBlock(stream, state, f) {
-    state.f = state.block = f;
-    return f(stream, state);
-  }
-
-  function lineIsEmpty(line) {
-    return !line || !/\S/.test(line.string)
-  }
-
-  // Blocks
-
-  function blankLine(state) {
-    // Reset linkTitle state
-    state.linkTitle = false;
-    state.linkHref = false;
-    state.linkText = false;
-    // Reset EM state
-    state.em = false;
-    // Reset STRONG state
-    state.strong = false;
-    // Reset strikethrough state
-    state.strikethrough = false;
-    // Reset state.quote
-    state.quote = 0;
-    // Reset state.indentedCode
-    state.indentedCode = false;
-    if (state.f == htmlBlock) {
-      var exit = htmlModeMissing
-      if (!exit) {
-        var inner = CodeMirror.innerMode(htmlMode, state.htmlState)
-        exit = inner.mode.name == "xml" && inner.state.tagStart === null &&
-          (!inner.state.context && inner.state.tokenize.isInText)
-      }
-      if (exit) {
-        state.f = inlineNormal;
-        state.block = blockNormal;
-        state.htmlState = null;
-      }
-    }
-    // Reset state.trailingSpace
-    state.trailingSpace = 0;
-    state.trailingSpaceNewLine = false;
-    // Mark this line as blank
-    state.prevLine = state.thisLine
-    state.thisLine = {stream: null}
-    return null;
-  }
-
-  function blockNormal(stream, state) {
-    var firstTokenOnLine = stream.column() === state.indentation;
-    var prevLineLineIsEmpty = lineIsEmpty(state.prevLine.stream);
-    var prevLineIsIndentedCode = state.indentedCode;
-    var prevLineIsHr = state.prevLine.hr;
-    var prevLineIsList = state.list !== false;
-    var maxNonCodeIndentation = (state.listStack[state.listStack.length - 1] || 0) + 3;
-
-    state.indentedCode = false;
-
-    var lineIndentation = state.indentation;
-    // compute once per line (on first token)
-    if (state.indentationDiff === null) {
-      state.indentationDiff = state.indentation;
-      if (prevLineIsList) {
-        state.list = null;
-        // While this list item's marker's indentation is less than the deepest
-        //  list item's content's indentation,pop the deepest list item
-        //  indentation off the stack, and update block indentation state
-        while (lineIndentation < state.listStack[state.listStack.length - 1]) {
-          state.listStack.pop();
-          if (state.listStack.length) {
-            state.indentation = state.listStack[state.listStack.length - 1];
-          // less than the first list's indent -> the line is no longer a list
-          } else {
-            state.list = false;
-          }
-        }
-        if (state.list !== false) {
-          state.indentationDiff = lineIndentation - state.listStack[state.listStack.length - 1]
-        }
-      }
-    }
-
-    // not comprehensive (currently only for setext detection purposes)
-    var allowsInlineContinuation = (
-        !prevLineLineIsEmpty && !prevLineIsHr && !state.prevLine.header &&
-        (!prevLineIsList || !prevLineIsIndentedCode) &&
-        !state.prevLine.fencedCodeEnd
-    );
-
-    var isHr = (state.list === false || prevLineIsHr || prevLineLineIsEmpty) &&
-      state.indentation <= maxNonCodeIndentation && stream.match(hrRE);
-
-    var match = null;
-    if (state.indentationDiff >= 4 && (prevLineIsIndentedCode || state.prevLine.fencedCodeEnd ||
-         state.prevLine.header || prevLineLineIsEmpty)) {
-      stream.skipToEnd();
-      state.indentedCode = true;
-      return tokenTypes.code;
-    } else if (stream.eatSpace()) {
-      return null;
-    } else if (firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(atxHeaderRE)) && match[1].length <= 6) {
-      state.quote = 0;
-      state.header = match[1].length;
-      state.thisLine.header = true;
-      if (modeCfg.highlightFormatting) state.formatting = "header";
-      state.f = state.inline;
-      return getType(state);
-    } else if (state.indentation <= maxNonCodeIndentation && stream.eat('>')) {
-      state.quote = firstTokenOnLine ? 1 : state.quote + 1;
-      if (modeCfg.highlightFormatting) state.formatting = "quote";
-      stream.eatSpace();
-      return getType(state);
-    } else if (!isHr && !state.setext && firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(listRE))) {
-      var listType = match[1] ? "ol" : "ul";
-
-      state.indentation = lineIndentation + stream.current().length;
-      state.list = true;
-      state.quote = 0;
-
-      // Add this list item's content's indentation to the stack
-      state.listStack.push(state.indentation);
-      // Reset inline styles which shouldn't propagate aross list items
-      state.em = false;
-      state.strong = false;
-      state.code = false;
-      state.strikethrough = false;
-
-      if (modeCfg.taskLists && stream.match(taskListRE, false)) {
-        state.taskList = true;
-      }
-      state.f = state.inline;
-      if (modeCfg.highlightFormatting) state.formatting = ["list", "list-" + listType];
-      return getType(state);
-    } else if (firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(fencedCodeRE, true))) {
-      state.quote = 0;
-      state.fencedEndRE = new RegExp(match[1] + "+ *$");
-      // try switching mode
-      state.localMode = modeCfg.fencedCodeBlockHighlighting && getMode(match[2] || modeCfg.fencedCodeBlockDefaultMode );
-      if (state.localMode) state.localState = CodeMirror.startState(state.localMode);
-      state.f = state.block = local;
-      if (modeCfg.highlightFormatting) state.formatting = "code-block";
-      state.code = -1
-      return getType(state);
-    // SETEXT has lowest block-scope precedence after HR, so check it after
-    //  the others (code, blockquote, list...)
-    } else if (
-      // if setext set, indicates line after ---/===
-      state.setext || (
-        // line before ---/===
-        (!allowsInlineContinuation || !prevLineIsList) && !state.quote && state.list === false &&
-        !state.code && !isHr && !linkDefRE.test(stream.string) &&
-        (match = stream.lookAhead(1)) && (match = match.match(setextHeaderRE))
-      )
-    ) {
-      if ( !state.setext ) {
-        state.header = match[0].charAt(0) == '=' ? 1 : 2;
-        state.setext = state.header;
-      } else {
-        state.header = state.setext;
-        // has no effect on type so we can reset it now
-        state.setext = 0;
-        stream.skipToEnd();
-        if (modeCfg.highlightFormatting) state.formatting = "header";
-      }
-      state.thisLine.header = true;
-      state.f = state.inline;
-      return getType(state);
-    } else if (isHr) {
-      stream.skipToEnd();
-      state.hr = true;
-      state.thisLine.hr = true;
-      return tokenTypes.hr;
-    } else if (stream.peek() === '[') {
-      return switchInline(stream, state, footnoteLink);
-    }
-
-    return switchInline(stream, state, state.inline);
-  }
-
-  function htmlBlock(stream, state) {
-    var style = htmlMode.token(stream, state.htmlState);
-    if (!htmlModeMissing) {
-      var inner = CodeMirror.innerMode(htmlMode, state.htmlState)
-      if ((inner.mode.name == "xml" && inner.state.tagStart === null &&
-           (!inner.state.context && inner.state.tokenize.isInText)) ||
-          (state.md_inside && stream.current().indexOf(">") > -1)) {
-        state.f = inlineNormal;
-        state.block = blockNormal;
-        state.htmlState = null;
-      }
-    }
-    return style;
-  }
-
-  function local(stream, state) {
-    var currListInd = state.listStack[state.listStack.length - 1] || 0;
-    var hasExitedList = state.indentation < currListInd;
-    var maxFencedEndInd = currListInd + 3;
-    if (state.fencedEndRE && state.indentation <= maxFencedEndInd && (hasExitedList || stream.match(state.fencedEndRE))) {
-      if (modeCfg.highlightFormatting) state.formatting = "code-block";
-      var returnType;
-      if (!hasExitedList) returnType = getType(state)
-      state.localMode = state.localState = null;
-      state.block = blockNormal;
-      state.f = inlineNormal;
-      state.fencedEndRE = null;
-      state.code = 0
-      state.thisLine.fencedCodeEnd = true;
-      if (hasExitedList) return switchBlock(stream, state, state.block);
-      return returnType;
-    } else if (state.localMode) {
-      return state.localMode.token(stream, state.localState);
-    } else {
-      stream.skipToEnd();
-      return tokenTypes.code;
-    }
-  }
-
-  // Inline
-  function getType(state) {
-    var styles = [];
-
-    if (state.formatting) {
-      styles.push(tokenTypes.formatting);
-
-      if (typeof state.formatting === "string") state.formatting = [state.formatting];
-
-      for (var i = 0; i < state.formatting.length; i++) {
-        styles.push(tokenTypes.formatting + "-" + state.formatting[i]);
-
-        if (state.formatting[i] === "header") {
-          styles.push(tokenTypes.formatting + "-" + state.formatting[i] + "-" + state.header);
-        }
-
-        // Add `formatting-quote` and `formatting-quote-#` for blockquotes
-        // Add `error` instead if the maximum blockquote nesting depth is passed
-        if (state.formatting[i] === "quote") {
-          if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
-            styles.push(tokenTypes.formatting + "-" + state.formatting[i] + "-" + state.quote);
-          } else {
-            styles.push("error");
-          }
-        }
-      }
-    }
-
-    if (state.taskOpen) {
-      styles.push("meta");
-      return styles.length ? styles.join(' ') : null;
-    }
-    if (state.taskClosed) {
-      styles.push("property");
-      return styles.length ? styles.join(' ') : null;
-    }
-
-    if (state.linkHref) {
-      styles.push(tokenTypes.linkHref, "url");
-    } else { // Only apply inline styles to non-url text
-      if (state.strong) { styles.push(tokenTypes.strong); }
-      if (state.em) { styles.push(tokenTypes.em); }
-      if (state.strikethrough) { styles.push(tokenTypes.strikethrough); }
-      if (state.emoji) { styles.push(tokenTypes.emoji); }
-      if (state.linkText) { styles.push(tokenTypes.linkText); }
-      if (state.code) { styles.push(tokenTypes.code); }
-      if (state.image) { styles.push(tokenTypes.image); }
-      if (state.imageAltText) { styles.push(tokenTypes.imageAltText, "link"); }
-      if (state.imageMarker) { styles.push(tokenTypes.imageMarker); }
-    }
-
-    if (state.header) { styles.push(tokenTypes.header, tokenTypes.header + "-" + state.header); }
-
-    if (state.quote) {
-      styles.push(tokenTypes.quote);
-
-      // Add `quote-#` where the maximum for `#` is modeCfg.maxBlockquoteDepth
-      if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
-        styles.push(tokenTypes.quote + "-" + state.quote);
-      } else {
-        styles.push(tokenTypes.quote + "-" + modeCfg.maxBlockquoteDepth);
-      }
-    }
-
-    if (state.list !== false) {
-      var listMod = (state.listStack.length - 1) % 3;
-      if (!listMod) {
-        styles.push(tokenTypes.list1);
-      } else if (listMod === 1) {
-        styles.push(tokenTypes.list2);
-      } else {
-        styles.push(tokenTypes.list3);
-      }
-    }
-
-    if (state.trailingSpaceNewLine) {
-      styles.push("trailing-space-new-line");
-    } else if (state.trailingSpace) {
-      styles.push("trailing-space-" + (state.trailingSpace % 2 ? "a" : "b"));
-    }
-
-    return styles.length ? styles.join(' ') : null;
-  }
-
-  function handleText(stream, state) {
-    if (stream.match(textRE, true)) {
-      return getType(state);
-    }
-    return undefined;
-  }
-
-  function inlineNormal(stream, state) {
-    var style = state.text(stream, state);
-    if (typeof style !== 'undefined')
-      return style;
-
-    if (state.list) { // List marker (*, +, -, 1., etc)
-      state.list = null;
-      return getType(state);
-    }
-
-    if (state.taskList) {
-      var taskOpen = stream.match(taskListRE, true)[1] === " ";
-      if (taskOpen) state.taskOpen = true;
-      else state.taskClosed = true;
-      if (modeCfg.highlightFormatting) state.formatting = "task";
-      state.taskList = false;
-      return getType(state);
-    }
-
-    state.taskOpen = false;
-    state.taskClosed = false;
-
-    if (state.header && stream.match(/^#+$/, true)) {
-      if (modeCfg.highlightFormatting) state.formatting = "header";
-      return getType(state);
-    }
-
-    var ch = stream.next();
-
-    // Matches link titles present on next line
-    if (state.linkTitle) {
-      state.linkTitle = false;
-      var matchCh = ch;
-      if (ch === '(') {
-        matchCh = ')';
-      }
-      matchCh = (matchCh+'').replace(/([.?*+^\[\]\\(){}|-])/g, "\\$1");
-      var regex = '^\\s*(?:[^' + matchCh + '\\\\]+|\\\\\\\\|\\\\.)' + matchCh;
-      if (stream.match(new RegExp(regex), true)) {
-        return tokenTypes.linkHref;
-      }
-    }
-
-    // If this block is changed, it may need to be updated in GFM mode
-    if (ch === '`') {
-      var previousFormatting = state.formatting;
-      if (modeCfg.highlightFormatting) state.formatting = "code";
-      stream.eatWhile('`');
-      var count = stream.current().length
-      if (state.code == 0 && (!state.quote || count == 1)) {
-        state.code = count
-        return getType(state)
-      } else if (count == state.code) { // Must be exact
-        var t = getType(state)
-        state.code = 0
-        return t
-      } else {
-        state.formatting = previousFormatting
-        return getType(state)
-      }
-    } else if (state.code) {
-      return getType(state);
-    }
-
-    if (ch === '\\') {
-      stream.next();
-      if (modeCfg.highlightFormatting) {
-        var type = getType(state);
-        var formattingEscape = tokenTypes.formatting + "-escape";
-        return type ? type + " " + formattingEscape : formattingEscape;
-      }
-    }
-
-    if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) {
-      state.imageMarker = true;
-      state.image = true;
-      if (modeCfg.highlightFormatting) state.formatting = "image";
-      return getType(state);
-    }
-
-    if (ch === '[' && state.imageMarker && stream.match(/[^\]]*\](\(.*?\)| ?\[.*?\])/, false)) {
-      state.imageMarker = false;
-      state.imageAltText = true
-      if (modeCfg.highlightFormatting) state.formatting = "image";
-      return getType(state);
-    }
-
-    if (ch === ']' && state.imageAltText) {
-      if (modeCfg.highlightFormatting) state.formatting = "image";
-      var type = getType(state);
-      state.imageAltText = false;
-      state.image = false;
-      state.inline = state.f = linkHref;
-      return type;
-    }
-
-    if (ch === '[' && !state.image) {
-      if (state.linkText && stream.match(/^.*?\]/)) return getType(state)
-      state.linkText = true;
-      if (modeCfg.highlightFormatting) state.formatting = "link";
-      return getType(state);
-    }
-
-    if (ch === ']' && state.linkText) {
-      if (modeCfg.highlightFormatting) state.formatting = "link";
-      var type = getType(state);
-      state.linkText = false;
-      state.inline = state.f = stream.match(/\(.*?\)| ?\[.*?\]/, false) ? linkHref : inlineNormal
-      return type;
-    }
-
-    if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, false)) {
-      state.f = state.inline = linkInline;
-      if (modeCfg.highlightFormatting) state.formatting = "link";
-      var type = getType(state);
-      if (type){
-        type += " ";
-      } else {
-        type = "";
-      }
-      return type + tokenTypes.linkInline;
-    }
-
-    if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, false)) {
-      state.f = state.inline = linkInline;
-      if (modeCfg.highlightFormatting) state.formatting = "link";
-      var type = getType(state);
-      if (type){
-        type += " ";
-      } else {
-        type = "";
-      }
-      return type + tokenTypes.linkEmail;
-    }
-
-    if (modeCfg.xml && ch === '<' && stream.match(/^(!--|\?|!\[CDATA\[|[a-z][a-z0-9-]*(?:\s+[a-z_:.\-]+(?:\s*=\s*[^>]+)?)*\s*(?:>|$))/i, false)) {
-      var end = stream.string.indexOf(">", stream.pos);
-      if (end != -1) {
-        var atts = stream.string.substring(stream.start, end);
-        if (/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(atts)) state.md_inside = true;
-      }
-      stream.backUp(1);
-      state.htmlState = CodeMirror.startState(htmlMode);
-      return switchBlock(stream, state, htmlBlock);
-    }
-
-    if (modeCfg.xml && ch === '<' && stream.match(/^\/\w*?>/)) {
-      state.md_inside = false;
-      return "tag";
-    } else if (ch === "*" || ch === "_") {
-      var len = 1, before = stream.pos == 1 ? " " : stream.string.charAt(stream.pos - 2)
-      while (len < 3 && stream.eat(ch)) len++
-      var after = stream.peek() || " "
-      // See http://spec.commonmark.org/0.27/#emphasis-and-strong-emphasis
-      var leftFlanking = !/\s/.test(after) && (!punctuation.test(after) || /\s/.test(before) || punctuation.test(before))
-      var rightFlanking = !/\s/.test(before) && (!punctuation.test(before) || /\s/.test(after) || punctuation.test(after))
-      var setEm = null, setStrong = null
-      if (len % 2) { // Em
-        if (!state.em && leftFlanking && (ch === "*" || !rightFlanking || punctuation.test(before)))
-          setEm = true
-        else if (state.em == ch && rightFlanking && (ch === "*" || !leftFlanking || punctuation.test(after)))
-          setEm = false
-      }
-      if (len > 1) { // Strong
-        if (!state.strong && leftFlanking && (ch === "*" || !rightFlanking || punctuation.test(before)))
-          setStrong = true
-        else if (state.strong == ch && rightFlanking && (ch === "*" || !leftFlanking || punctuation.test(after)))
-          setStrong = false
-      }
-      if (setStrong != null || setEm != null) {
-        if (modeCfg.highlightFormatting) state.formatting = setEm == null ? "strong" : setStrong == null ? "em" : "strong em"
-        if (setEm === true) state.em = ch
-        if (setStrong === true) state.strong = ch
-        var t = getType(state)
-        if (setEm === false) state.em = false
-        if (setStrong === false) state.strong = false
-        return t
-      }
-    } else if (ch === ' ') {
-      if (stream.eat('*') || stream.eat('_')) { // Probably surrounded by spaces
-        if (stream.peek() === ' ') { // Surrounded by spaces, ignore
-          return getType(state);
-        } else { // Not surrounded by spaces, back up pointer
-          stream.backUp(1);
-        }
-      }
-    }
-
-    if (modeCfg.strikethrough) {
-      if (ch === '~' && stream.eatWhile(ch)) {
-        if (state.strikethrough) {// Remove strikethrough
-          if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
-          var t = getType(state);
-          state.strikethrough = false;
-          return t;
-        } else if (stream.match(/^[^\s]/, false)) {// Add strikethrough
-          state.strikethrough = true;
-          if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
-          return getType(state);
-        }
-      } else if (ch === ' ') {
-        if (stream.match(/^~~/, true)) { // Probably surrounded by space
-          if (stream.peek() === ' ') { // Surrounded by spaces, ignore
-            return getType(state);
-          } else { // Not surrounded by spaces, back up pointer
-            stream.backUp(2);
-          }
-        }
-      }
-    }
-
-    if (modeCfg.emoji && ch === ":" && stream.match(/^(?:[a-z_\d+][a-z_\d+-]*|\-[a-z_\d+][a-z_\d+-]*):/)) {
-      state.emoji = true;
-      if (modeCfg.highlightFormatting) state.formatting = "emoji";
-      var retType = getType(state);
-      state.emoji = false;
-      return retType;
-    }
-
-    if (ch === ' ') {
-      if (stream.match(/^ +$/, false)) {
-        state.trailingSpace++;
-      } else if (state.trailingSpace) {
-        state.trailingSpaceNewLine = true;
-      }
-    }
-
-    return getType(state);
-  }
-
-  function linkInline(stream, state) {
-    var ch = stream.next();
-
-    if (ch === ">") {
-      state.f = state.inline = inlineNormal;
-      if (modeCfg.highlightFormatting) state.formatting = "link";
-      var type = getType(state);
-      if (type){
-        type += " ";
-      } else {
-        type = "";
-      }
-      return type + tokenTypes.linkInline;
-    }
-
-    stream.match(/^[^>]+/, true);
-
-    return tokenTypes.linkInline;
-  }
-
-  function linkHref(stream, state) {
-    // Check if space, and return NULL if so (to avoid marking the space)
-    if(stream.eatSpace()){
-      return null;
-    }
-    var ch = stream.next();
-    if (ch === '(' || ch === '[') {
-      state.f = state.inline = getLinkHrefInside(ch === "(" ? ")" : "]");
-      if (modeCfg.highlightFormatting) state.formatting = "link-string";
-      state.linkHref = true;
-      return getType(state);
-    }
-    return 'error';
-  }
-
-  var linkRE = {
-    ")": /^(?:[^\\\(\)]|\\.|\((?:[^\\\(\)]|\\.)*\))*?(?=\))/,
-    "]": /^(?:[^\\\[\]]|\\.|\[(?:[^\\\[\]]|\\.)*\])*?(?=\])/
-  }
-
-  function getLinkHrefInside(endChar) {
-    return function(stream, state) {
-      var ch = stream.next();
-
-      if (ch === endChar) {
-        state.f = state.inline = inlineNormal;
-        if (modeCfg.highlightFormatting) state.formatting = "link-string";
-        var returnState = getType(state);
-        state.linkHref = false;
-        return returnState;
-      }
-
-      stream.match(linkRE[endChar])
-      state.linkHref = true;
-      return getType(state);
-    };
-  }
-
-  function footnoteLink(stream, state) {
-    if (stream.match(/^([^\]\\]|\\.)*\]:/, false)) {
-      state.f = footnoteLinkInside;
-      stream.next(); // Consume [
-      if (modeCfg.highlightFormatting) state.formatting = "link";
-      state.linkText = true;
-      return getType(state);
-    }
-    return switchInline(stream, state, inlineNormal);
-  }
-
-  function footnoteLinkInside(stream, state) {
-    if (stream.match(/^\]:/, true)) {
-      state.f = state.inline = footnoteUrl;
-      if (modeCfg.highlightFormatting) state.formatting = "link";
-      var returnType = getType(state);
-      state.linkText = false;
-      return returnType;
-    }
-
-    stream.match(/^([^\]\\]|\\.)+/, true);
-
-    return tokenTypes.linkText;
-  }
-
-  function footnoteUrl(stream, state) {
-    // Check if space, and return NULL if so (to avoid marking the space)
-    if(stream.eatSpace()){
-      return null;
-    }
-    // Match URL
-    stream.match(/^[^\s]+/, true);
-    // Check for link title
-    if (stream.peek() === undefined) { // End of line, set flag to check next line
-      state.linkTitle = true;
-    } else { // More content on line, check if link title
-      stream.match(/^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/, true);
-    }
-    state.f = state.inline = inlineNormal;
-    return tokenTypes.linkHref + " url";
-  }
-
-  var mode = {
-    startState: function() {
-      return {
-        f: blockNormal,
-
-        prevLine: {stream: null},
-        thisLine: {stream: null},
-
-        block: blockNormal,
-        htmlState: null,
-        indentation: 0,
-
-        inline: inlineNormal,
-        text: handleText,
-
-        formatting: false,
-        linkText: false,
-        linkHref: false,
-        linkTitle: false,
-        code: 0,
-        em: false,
-        strong: false,
-        header: 0,
-        setext: 0,
-        hr: false,
-        taskList: false,
-        list: false,
-        listStack: [],
-        quote: 0,
-        trailingSpace: 0,
-        trailingSpaceNewLine: false,
-        strikethrough: false,
-        emoji: false,
-        fencedEndRE: null
-      };
-    },
-
-    copyState: function(s) {
-      return {
-        f: s.f,
-
-        prevLine: s.prevLine,
-        thisLine: s.thisLine,
-
-        block: s.block,
-        htmlState: s.htmlState && CodeMirror.copyState(htmlMode, s.htmlState),
-        indentation: s.indentation,
-
-        localMode: s.localMode,
-        localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null,
-
-        inline: s.inline,
-        text: s.text,
-        formatting: false,
-        linkText: s.linkText,
-        linkTitle: s.linkTitle,
-        linkHref: s.linkHref,
-        code: s.code,
-        em: s.em,
-        strong: s.strong,
-        strikethrough: s.strikethrough,
-        emoji: s.emoji,
-        header: s.header,
-        setext: s.setext,
-        hr: s.hr,
-        taskList: s.taskList,
-        list: s.list,
-        listStack: s.listStack.slice(0),
-        quote: s.quote,
-        indentedCode: s.indentedCode,
-        trailingSpace: s.trailingSpace,
-        trailingSpaceNewLine: s.trailingSpaceNewLine,
-        md_inside: s.md_inside,
-        fencedEndRE: s.fencedEndRE
-      };
-    },
-
-    token: function(stream, state) {
-
-      // Reset state.formatting
-      state.formatting = false;
-
-      if (stream != state.thisLine.stream) {
-        state.header = 0;
-        state.hr = false;
-
-        if (stream.match(/^\s*$/, true)) {
-          blankLine(state);
-          return null;
-        }
-
-        state.prevLine = state.thisLine
-        state.thisLine = {stream: stream}
-
-        // Reset state.taskList
-        state.taskList = false;
-
-        // Reset state.trailingSpace
-        state.trailingSpace = 0;
-        state.trailingSpaceNewLine = false;
-
-        if (!state.localState) {
-          state.f = state.block;
-          if (state.f != htmlBlock) {
-            var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, expandedTab).length;
-            state.indentation = indentation;
-            state.indentationDiff = null;
-            if (indentation > 0) return null;
-          }
-        }
-      }
-      return state.f(stream, state);
-    },
-
-    innerMode: function(state) {
-      if (state.block == htmlBlock) return {state: state.htmlState, mode: htmlMode};
-      if (state.localState) return {state: state.localState, mode: state.localMode};
-      return {state: state, mode: mode};
-    },
-
-    indent: function(state, textAfter, line) {
-      if (state.block == htmlBlock && htmlMode.indent) return htmlMode.indent(state.htmlState, textAfter, line)
-      if (state.localState && state.localMode.indent) return state.localMode.indent(state.localState, textAfter, line)
-      return CodeMirror.Pass
-    },
-
-    blankLine: blankLine,
-
-    getType: getType,
-
-    blockCommentStart: "<!--",
-    blockCommentEnd: "-->",
-    closeBrackets: "()[]{}''\"\"``",
-    fold: "markdown"
-  };
-  return mode;
-}, "xml");
-
-CodeMirror.defineMIME("text/markdown", "markdown");
-
-CodeMirror.defineMIME("text/x-markdown", "markdown");
-
-});
-
-
-/* ---- mode/python.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  function wordRegexp(words) {
-    return new RegExp("^((" + words.join(")|(") + "))\\b");
-  }
-
-  var wordOperators = wordRegexp(["and", "or", "not", "is"]);
-  var commonKeywords = ["as", "assert", "break", "class", "continue",
-                        "def", "del", "elif", "else", "except", "finally",
-                        "for", "from", "global", "if", "import",
-                        "lambda", "pass", "raise", "return",
-                        "try", "while", "with", "yield", "in"];
-  var commonBuiltins = ["abs", "all", "any", "bin", "bool", "bytearray", "callable", "chr",
-                        "classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod",
-                        "enumerate", "eval", "filter", "float", "format", "frozenset",
-                        "getattr", "globals", "hasattr", "hash", "help", "hex", "id",
-                        "input", "int", "isinstance", "issubclass", "iter", "len",
-                        "list", "locals", "map", "max", "memoryview", "min", "next",
-                        "object", "oct", "open", "ord", "pow", "property", "range",
-                        "repr", "reversed", "round", "set", "setattr", "slice",
-                        "sorted", "staticmethod", "str", "sum", "super", "tuple",
-                        "type", "vars", "zip", "__import__", "NotImplemented",
-                        "Ellipsis", "__debug__"];
-  CodeMirror.registerHelper("hintWords", "python", commonKeywords.concat(commonBuiltins));
-
-  function top(state) {
-    return state.scopes[state.scopes.length - 1];
-  }
-
-  CodeMirror.defineMode("python", function(conf, parserConf) {
-    var ERRORCLASS = "error";
-
-    var delimiters = parserConf.delimiters || parserConf.singleDelimiters || /^[\(\)\[\]\{\}@,:`=;\.\\]/;
-    //               (Backwards-compatibility with old, cumbersome config system)
-    var operators = [parserConf.singleOperators, parserConf.doubleOperators, parserConf.doubleDelimiters, parserConf.tripleDelimiters,
-                     parserConf.operators || /^([-+*/%\/&|^]=?|[<>=]+|\/\/=?|\*\*=?|!=|[~!@]|\.\.\.)/]
-    for (var i = 0; i < operators.length; i++) if (!operators[i]) operators.splice(i--, 1)
-
-    var hangingIndent = parserConf.hangingIndent || conf.indentUnit;
-
-    var myKeywords = commonKeywords, myBuiltins = commonBuiltins;
-    if (parserConf.extra_keywords != undefined)
-      myKeywords = myKeywords.concat(parserConf.extra_keywords);
-
-    if (parserConf.extra_builtins != undefined)
-      myBuiltins = myBuiltins.concat(parserConf.extra_builtins);
-
-    var py3 = !(parserConf.version && Number(parserConf.version) < 3)
-    if (py3) {
-      // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator
-      var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*/;
-      myKeywords = myKeywords.concat(["nonlocal", "False", "True", "None", "async", "await"]);
-      myBuiltins = myBuiltins.concat(["ascii", "bytes", "exec", "print"]);
-      var stringPrefixes = new RegExp("^(([rbuf]|(br)|(fr))?('{3}|\"{3}|['\"]))", "i");
-    } else {
-      var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/;
-      myKeywords = myKeywords.concat(["exec", "print"]);
-      myBuiltins = myBuiltins.concat(["apply", "basestring", "buffer", "cmp", "coerce", "execfile",
-                                      "file", "intern", "long", "raw_input", "reduce", "reload",
-                                      "unichr", "unicode", "xrange", "False", "True", "None"]);
-      var stringPrefixes = new RegExp("^(([rubf]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
-    }
-    var keywords = wordRegexp(myKeywords);
-    var builtins = wordRegexp(myBuiltins);
-
-    // tokenizers
-    function tokenBase(stream, state) {
-      var sol = stream.sol() && state.lastToken != "\\"
-      if (sol) state.indent = stream.indentation()
-      // Handle scope changes
-      if (sol && top(state).type == "py") {
-        var scopeOffset = top(state).offset;
-        if (stream.eatSpace()) {
-          var lineOffset = stream.indentation();
-          if (lineOffset > scopeOffset)
-            pushPyScope(state);
-          else if (lineOffset < scopeOffset && dedent(stream, state) && stream.peek() != "#")
-            state.errorToken = true;
-          return null;
-        } else {
-          var style = tokenBaseInner(stream, state);
-          if (scopeOffset > 0 && dedent(stream, state))
-            style += " " + ERRORCLASS;
-          return style;
-        }
-      }
-      return tokenBaseInner(stream, state);
-    }
-
-    function tokenBaseInner(stream, state, inFormat) {
-      if (stream.eatSpace()) return null;
-
-      // Handle Comments
-      if (!inFormat && stream.match(/^#.*/)) return "comment";
-
-      // Handle Number Literals
-      if (stream.match(/^[0-9\.]/, false)) {
-        var floatLiteral = false;
-        // Floats
-        if (stream.match(/^[\d_]*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
-        if (stream.match(/^[\d_]+\.\d*/)) { floatLiteral = true; }
-        if (stream.match(/^\.\d+/)) { floatLiteral = true; }
-        if (floatLiteral) {
-          // Float literals may be "imaginary"
-          stream.eat(/J/i);
-          return "number";
-        }
-        // Integers
-        var intLiteral = false;
-        // Hex
-        if (stream.match(/^0x[0-9a-f_]+/i)) intLiteral = true;
-        // Binary
-        if (stream.match(/^0b[01_]+/i)) intLiteral = true;
-        // Octal
-        if (stream.match(/^0o[0-7_]+/i)) intLiteral = true;
-        // Decimal
-        if (stream.match(/^[1-9][\d_]*(e[\+\-]?[\d_]+)?/)) {
-          // Decimal literals may be "imaginary"
-          stream.eat(/J/i);
-          // TODO - Can you have imaginary longs?
-          intLiteral = true;
-        }
-        // Zero by itself with no other piece of number.
-        if (stream.match(/^0(?![\dx])/i)) intLiteral = true;
-        if (intLiteral) {
-          // Integer literals may be "long"
-          stream.eat(/L/i);
-          return "number";
-        }
-      }
-
-      // Handle Strings
-      if (stream.match(stringPrefixes)) {
-        var isFmtString = stream.current().toLowerCase().indexOf('f') !== -1;
-        if (!isFmtString) {
-          state.tokenize = tokenStringFactory(stream.current(), state.tokenize);
-          return state.tokenize(stream, state);
-        } else {
-          state.tokenize = formatStringFactory(stream.current(), state.tokenize);
-          return state.tokenize(stream, state);
-        }
-      }
-
-      for (var i = 0; i < operators.length; i++)
-        if (stream.match(operators[i])) return "operator"
-
-      if (stream.match(delimiters)) return "punctuation";
-
-      if (state.lastToken == "." && stream.match(identifiers))
-        return "property";
-
-      if (stream.match(keywords) || stream.match(wordOperators))
-        return "keyword";
-
-      if (stream.match(builtins))
-        return "builtin";
-
-      if (stream.match(/^(self|cls)\b/))
-        return "variable-2";
-
-      if (stream.match(identifiers)) {
-        if (state.lastToken == "def" || state.lastToken == "class")
-          return "def";
-        return "variable";
-      }
-
-      // Handle non-detected items
-      stream.next();
-      return inFormat ? null :ERRORCLASS;
-    }
-
-    function formatStringFactory(delimiter, tokenOuter) {
-      while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
-        delimiter = delimiter.substr(1);
-
-      var singleline = delimiter.length == 1;
-      var OUTCLASS = "string";
-
-      function tokenNestedExpr(depth) {
-        return function(stream, state) {
-          var inner = tokenBaseInner(stream, state, true)
-          if (inner == "punctuation") {
-            if (stream.current() == "{") {
-              state.tokenize = tokenNestedExpr(depth + 1)
-            } else if (stream.current() == "}") {
-              if (depth > 1) state.tokenize = tokenNestedExpr(depth - 1)
-              else state.tokenize = tokenString
-            }
-          }
-          return inner
-        }
-      }
-
-      function tokenString(stream, state) {
-        while (!stream.eol()) {
-          stream.eatWhile(/[^'"\{\}\\]/);
-          if (stream.eat("\\")) {
-            stream.next();
-            if (singleline && stream.eol())
-              return OUTCLASS;
-          } else if (stream.match(delimiter)) {
-            state.tokenize = tokenOuter;
-            return OUTCLASS;
-          } else if (stream.match('{{')) {
-            // ignore {{ in f-str
-            return OUTCLASS;
-          } else if (stream.match('{', false)) {
-            // switch to nested mode
-            state.tokenize = tokenNestedExpr(0)
-            if (stream.current()) return OUTCLASS;
-            else return state.tokenize(stream, state)
-          } else if (stream.match('}}')) {
-            return OUTCLASS;
-          } else if (stream.match('}')) {
-            // single } in f-string is an error
-            return ERRORCLASS;
-          } else {
-            stream.eat(/['"]/);
-          }
-        }
-        if (singleline) {
-          if (parserConf.singleLineStringErrors)
-            return ERRORCLASS;
-          else
-            state.tokenize = tokenOuter;
-        }
-        return OUTCLASS;
-      }
-      tokenString.isString = true;
-      return tokenString;
-    }
-
-    function tokenStringFactory(delimiter, tokenOuter) {
-      while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
-        delimiter = delimiter.substr(1);
-
-      var singleline = delimiter.length == 1;
-      var OUTCLASS = "string";
-
-      function tokenString(stream, state) {
-        while (!stream.eol()) {
-          stream.eatWhile(/[^'"\\]/);
-          if (stream.eat("\\")) {
-            stream.next();
-            if (singleline && stream.eol())
-              return OUTCLASS;
-          } else if (stream.match(delimiter)) {
-            state.tokenize = tokenOuter;
-            return OUTCLASS;
-          } else {
-            stream.eat(/['"]/);
-          }
-        }
-        if (singleline) {
-          if (parserConf.singleLineStringErrors)
-            return ERRORCLASS;
-          else
-            state.tokenize = tokenOuter;
-        }
-        return OUTCLASS;
-      }
-      tokenString.isString = true;
-      return tokenString;
-    }
-
-    function pushPyScope(state) {
-      while (top(state).type != "py") state.scopes.pop()
-      state.scopes.push({offset: top(state).offset + conf.indentUnit,
-                         type: "py",
-                         align: null})
-    }
-
-    function pushBracketScope(stream, state, type) {
-      var align = stream.match(/^([\s\[\{\(]|#.*)*$/, false) ? null : stream.column() + 1
-      state.scopes.push({offset: state.indent + hangingIndent,
-                         type: type,
-                         align: align})
-    }
-
-    function dedent(stream, state) {
-      var indented = stream.indentation();
-      while (state.scopes.length > 1 && top(state).offset > indented) {
-        if (top(state).type != "py") return true;
-        state.scopes.pop();
-      }
-      return top(state).offset != indented;
-    }
-
-    function tokenLexer(stream, state) {
-      if (stream.sol()) state.beginningOfLine = true;
-
-      var style = state.tokenize(stream, state);
-      var current = stream.current();
-
-      // Handle decorators
-      if (state.beginningOfLine && current == "@")
-        return stream.match(identifiers, false) ? "meta" : py3 ? "operator" : ERRORCLASS;
-
-      if (/\S/.test(current)) state.beginningOfLine = false;
-
-      if ((style == "variable" || style == "builtin")
-          && state.lastToken == "meta")
-        style = "meta";
-
-      // Handle scope changes.
-      if (current == "pass" || current == "return")
-        state.dedent += 1;
-
-      if (current == "lambda") state.lambda = true;
-      if (current == ":" && !state.lambda && top(state).type == "py")
-        pushPyScope(state);
-
-      if (current.length == 1 && !/string|comment/.test(style)) {
-        var delimiter_index = "[({".indexOf(current);
-        if (delimiter_index != -1)
-          pushBracketScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
-
-        delimiter_index = "])}".indexOf(current);
-        if (delimiter_index != -1) {
-          if (top(state).type == current) state.indent = state.scopes.pop().offset - hangingIndent
-          else return ERRORCLASS;
-        }
-      }
-      if (state.dedent > 0 && stream.eol() && top(state).type == "py") {
-        if (state.scopes.length > 1) state.scopes.pop();
-        state.dedent -= 1;
-      }
-
-      return style;
-    }
-
-    var external = {
-      startState: function(basecolumn) {
-        return {
-          tokenize: tokenBase,
-          scopes: [{offset: basecolumn || 0, type: "py", align: null}],
-          indent: basecolumn || 0,
-          lastToken: null,
-          lambda: false,
-          dedent: 0
-        };
-      },
-
-      token: function(stream, state) {
-        var addErr = state.errorToken;
-        if (addErr) state.errorToken = false;
-        var style = tokenLexer(stream, state);
-
-        if (style && style != "comment")
-          state.lastToken = (style == "keyword" || style == "punctuation") ? stream.current() : style;
-        if (style == "punctuation") style = null;
-
-        if (stream.eol() && state.lambda)
-          state.lambda = false;
-        return addErr ? style + " " + ERRORCLASS : style;
-      },
-
-      indent: function(state, textAfter) {
-        if (state.tokenize != tokenBase)
-          return state.tokenize.isString ? CodeMirror.Pass : 0;
-
-        var scope = top(state), closing = scope.type == textAfter.charAt(0)
-        if (scope.align != null)
-          return scope.align - (closing ? 1 : 0)
-        else
-          return scope.offset - (closing ? hangingIndent : 0)
-      },
-
-      electricInput: /^\s*[\}\]\)]$/,
-      closeBrackets: {triples: "'\""},
-      lineComment: "#",
-      fold: "indent"
-    };
-    return external;
-  });
-
-  CodeMirror.defineMIME("text/x-python", "python");
-
-  var words = function(str) { return str.split(" "); };
-
-  CodeMirror.defineMIME("text/x-cython", {
-    name: "python",
-    extra_keywords: words("by cdef cimport cpdef ctypedef enum except "+
-                          "extern gil include nogil property public "+
-                          "readonly struct union DEF IF ELIF ELSE")
-  });
-
-});
-
-
-/* ---- mode/rust.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("../../addon/mode/simple"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "../../addon/mode/simple"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-CodeMirror.defineSimpleMode("rust",{
-  start: [
-    // string and byte string
-    {regex: /b?"/, token: "string", next: "string"},
-    // raw string and raw byte string
-    {regex: /b?r"/, token: "string", next: "string_raw"},
-    {regex: /b?r#+"/, token: "string", next: "string_raw_hash"},
-    // character
-    {regex: /'(?:[^'\\]|\\(?:[nrt0'"]|x[\da-fA-F]{2}|u\{[\da-fA-F]{6}\}))'/, token: "string-2"},
-    // byte
-    {regex: /b'(?:[^']|\\(?:['\\nrt0]|x[\da-fA-F]{2}))'/, token: "string-2"},
-
-    {regex: /(?:(?:[0-9][0-9_]*)(?:(?:[Ee][+-]?[0-9_]+)|\.[0-9_]+(?:[Ee][+-]?[0-9_]+)?)(?:f32|f64)?)|(?:0(?:b[01_]+|(?:o[0-7_]+)|(?:x[0-9a-fA-F_]+))|(?:[0-9][0-9_]*))(?:u8|u16|u32|u64|i8|i16|i32|i64|isize|usize)?/,
-     token: "number"},
-    {regex: /(let(?:\s+mut)?|fn|enum|mod|struct|type|union)(\s+)([a-zA-Z_][a-zA-Z0-9_]*)/, token: ["keyword", null, "def"]},
-    {regex: /(?:abstract|alignof|as|async|await|box|break|continue|const|crate|do|dyn|else|enum|extern|fn|for|final|if|impl|in|loop|macro|match|mod|move|offsetof|override|priv|proc|pub|pure|ref|return|self|sizeof|static|struct|super|trait|type|typeof|union|unsafe|unsized|use|virtual|where|while|yield)\b/, token: "keyword"},
-    {regex: /\b(?:Self|isize|usize|char|bool|u8|u16|u32|u64|f16|f32|f64|i8|i16|i32|i64|str|Option)\b/, token: "atom"},
-    {regex: /\b(?:true|false|Some|None|Ok|Err)\b/, token: "builtin"},
-    {regex: /\b(fn)(\s+)([a-zA-Z_][a-zA-Z0-9_]*)/,
-     token: ["keyword", null ,"def"]},
-    {regex: /#!?\[.*\]/, token: "meta"},
-    {regex: /\/\/.*/, token: "comment"},
-    {regex: /\/\*/, token: "comment", next: "comment"},
-    {regex: /[-+\/*=<>!]+/, token: "operator"},
-    {regex: /[a-zA-Z_]\w*!/,token: "variable-3"},
-    {regex: /[a-zA-Z_]\w*/, token: "variable"},
-    {regex: /[\{\[\(]/, indent: true},
-    {regex: /[\}\]\)]/, dedent: true}
-  ],
-  string: [
-    {regex: /"/, token: "string", next: "start"},
-    {regex: /(?:[^\\"]|\\(?:.|$))*/, token: "string"}
-  ],
-  string_raw: [
-    {regex: /"/, token: "string", next: "start"},
-    {regex: /[^"]*/, token: "string"}
-  ],
-  string_raw_hash: [
-    {regex: /"#+/, token: "string", next: "start"},
-    {regex: /(?:[^"]|"(?!#))*/, token: "string"}
-  ],
-  comment: [
-    {regex: /.*?\*\//, token: "comment", next: "start"},
-    {regex: /.*/, token: "comment"}
-  ],
-  meta: {
-    dontIndentStates: ["comment"],
-    electricInput: /^\s*\}$/,
-    blockCommentStart: "/*",
-    blockCommentEnd: "*/",
-    lineComment: "//",
-    fold: "brace"
-  }
-});
-
-
-CodeMirror.defineMIME("text/x-rustsrc", "rust");
-CodeMirror.defineMIME("text/rust", "rust");
-});
-
-
-/* ---- mode/xml.js ---- */
-
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-var htmlConfig = {
-  autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
-                    'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
-                    'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
-                    'track': true, 'wbr': true, 'menuitem': true},
-  implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
-                     'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
-                     'th': true, 'tr': true},
-  contextGrabbers: {
-    'dd': {'dd': true, 'dt': true},
-    'dt': {'dd': true, 'dt': true},
-    'li': {'li': true},
-    'option': {'option': true, 'optgroup': true},
-    'optgroup': {'optgroup': true},
-    'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
-          'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
-          'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
-          'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
-          'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
-    'rp': {'rp': true, 'rt': true},
-    'rt': {'rp': true, 'rt': true},
-    'tbody': {'tbody': true, 'tfoot': true},
-    'td': {'td': true, 'th': true},
-    'tfoot': {'tbody': true},
-    'th': {'td': true, 'th': true},
-    'thead': {'tbody': true, 'tfoot': true},
-    'tr': {'tr': true}
-  },
-  doNotIndent: {"pre": true},
-  allowUnquoted: true,
-  allowMissing: true,
-  caseFold: true
-}
-
-var xmlConfig = {
-  autoSelfClosers: {},
-  implicitlyClosed: {},
-  contextGrabbers: {},
-  doNotIndent: {},
-  allowUnquoted: false,
-  allowMissing: false,
-  allowMissingTagName: false,
-  caseFold: false
-}
-
-CodeMirror.defineMode("xml", function(editorConf, config_) {
-  var indentUnit = editorConf.indentUnit
-  var config = {}
-  var defaults = config_.htmlMode ? htmlConfig : xmlConfig
-  for (var prop in defaults) config[prop] = defaults[prop]
-  for (var prop in config_) config[prop] = config_[prop]
-
-  // Return variables for tokenizers
-  var type, setStyle;
-
-  function inText(stream, state) {
-    function chain(parser) {
-      state.tokenize = parser;
-      return parser(stream, state);
-    }
-
-    var ch = stream.next();
-    if (ch == "<") {
-      if (stream.eat("!")) {
-        if (stream.eat("[")) {
-          if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
-          else return null;
-        } else if (stream.match("--")) {
-          return chain(inBlock("comment", "-->"));
-        } else if (stream.match("DOCTYPE", true, true)) {
-          stream.eatWhile(/[\w\._\-]/);
-          return chain(doctype(1));
-        } else {
-          return null;
-        }
-      } else if (stream.eat("?")) {
-        stream.eatWhile(/[\w\._\-]/);
-        state.tokenize = inBlock("meta", "?>");
-        return "meta";
-      } else {
-        type = stream.eat("/") ? "closeTag" : "openTag";
-        state.tokenize = inTag;
-        return "tag bracket";
-      }
-    } else if (ch == "&") {
-      var ok;
-      if (stream.eat("#")) {
-        if (stream.eat("x")) {
-          ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
-        } else {
-          ok = stream.eatWhile(/[\d]/) && stream.eat(";");
-        }
-      } else {
-        ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
-      }
-      return ok ? "atom" : "error";
-    } else {
-      stream.eatWhile(/[^&<]/);
-      return null;
-    }
-  }
-  inText.isInText = true;
-
-  function inTag(stream, state) {
-    var ch = stream.next();
-    if (ch == ">" || (ch == "/" && stream.eat(">"))) {
-      state.tokenize = inText;
-      type = ch == ">" ? "endTag" : "selfcloseTag";
-      return "tag bracket";
-    } else if (ch == "=") {
-      type = "equals";
-      return null;
-    } else if (ch == "<") {
-      state.tokenize = inText;
-      state.state = baseState;
-      state.tagName = state.tagStart = null;
-      var next = state.tokenize(stream, state);
-      return next ? next + " tag error" : "tag error";
-    } else if (/[\'\"]/.test(ch)) {
-      state.tokenize = inAttribute(ch);
-      state.stringStartCol = stream.column();
-      return state.tokenize(stream, state);
-    } else {
-      stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
-      return "word";
-    }
-  }
-
-  function inAttribute(quote) {
-    var closure = function(stream, state) {
-      while (!stream.eol()) {
-        if (stream.next() == quote) {
-          state.tokenize = inTag;
-          break;
-        }
-      }
-      return "string";
-    };
-    closure.isInAttribute = true;
-    return closure;
-  }
-
-  function inBlock(style, terminator) {
-    return function(stream, state) {
-      while (!stream.eol()) {
-        if (stream.match(terminator)) {
-          state.tokenize = inText;
-          break;
-        }
-        stream.next();
-      }
-      return style;
-    }
-  }
-
-  function doctype(depth) {
-    return function(stream, state) {
-      var ch;
-      while ((ch = stream.next()) != null) {
-        if (ch == "<") {
-          state.tokenize = doctype(depth + 1);
-          return state.tokenize(stream, state);
-        } else if (ch == ">") {
-          if (depth == 1) {
-            state.tokenize = inText;
-            break;
-          } else {
-            state.tokenize = doctype(depth - 1);
-            return state.tokenize(stream, state);
-          }
-        }
-      }
-      return "meta";
-    };
-  }
-
-  function Context(state, tagName, startOfLine) {
-    this.prev = state.context;
-    this.tagName = tagName;
-    this.indent = state.indented;
-    this.startOfLine = startOfLine;
-    if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
-      this.noIndent = true;
-  }
-  function popContext(state) {
-    if (state.context) state.context = state.context.prev;
-  }
-  function maybePopContext(state, nextTagName) {
-    var parentTagName;
-    while (true) {
-      if (!state.context) {
-        return;
-      }
-      parentTagName = state.context.tagName;
-      if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
-          !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
-        return;
-      }
-      popContext(state);
-    }
-  }
-
-  function baseState(type, stream, state) {
-    if (type == "openTag") {
-      state.tagStart = stream.column();
-      return tagNameState;
-    } else if (type == "closeTag") {
-      return closeTagNameState;
-    } else {
-      return baseState;
-    }
-  }
-  function tagNameState(type, stream, state) {
-    if (type == "word") {
-      state.tagName = stream.current();
-      setStyle = "tag";
-      return attrState;
-    } else if (config.allowMissingTagName && type == "endTag") {
-      setStyle = "tag bracket";
-      return attrState(type, stream, state);
-    } else {
-      setStyle = "error";
-      return tagNameState;
-    }
-  }
-  function closeTagNameState(type, stream, state) {
-    if (type == "word") {
-      var tagName = stream.current();
-      if (state.context && state.context.tagName != tagName &&
-          config.implicitlyClosed.hasOwnProperty(state.context.tagName))
-        popContext(state);
-      if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
-        setStyle = "tag";
-        return closeState;
-      } else {
-        setStyle = "tag error";
-        return closeStateErr;
-      }
-    } else if (config.allowMissingTagName && type == "endTag") {
-      setStyle = "tag bracket";
-      return closeState(type, stream, state);
-    } else {
-      setStyle = "error";
-      return closeStateErr;
-    }
-  }
-
-  function closeState(type, _stream, state) {
-    if (type != "endTag") {
-      setStyle = "error";
-      return closeState;
-    }
-    popContext(state);
-    return baseState;
-  }
-  function closeStateErr(type, stream, state) {
-    setStyle = "error";
-    return closeState(type, stream, state);
-  }
-
-  function attrState(type, _stream, state) {
-    if (type == "word") {
-      setStyle = "attribute";
-      return attrEqState;
-    } else if (type == "endTag" || type == "selfcloseTag") {
-      var tagName = state.tagName, tagStart = state.tagStart;
-      state.tagName = state.tagStart = null;
-      if (type == "selfcloseTag" ||
-          config.autoSelfClosers.hasOwnProperty(tagName)) {
-        maybePopContext(state, tagName);
-      } else {
-        maybePopContext(state, tagName);
-        state.context = new Context(state, tagName, tagStart == state.indented);
-      }
-      return baseState;
-    }
-    setStyle = "error";
-    return attrState;
-  }
-  function attrEqState(type, stream, state) {
-    if (type == "equals") return attrValueState;
-    if (!config.allowMissing) setStyle = "error";
-    return attrState(type, stream, state);
-  }
-  function attrValueState(type, stream, state) {
-    if (type == "string") return attrContinuedState;
-    if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
-    setStyle = "error";
-    return attrState(type, stream, state);
-  }
-  function attrContinuedState(type, stream, state) {
-    if (type == "string") return attrContinuedState;
-    return attrState(type, stream, state);
-  }
-
-  return {
-    startState: function(baseIndent) {
-      var state = {tokenize: inText,
-                   state: baseState,
-                   indented: baseIndent || 0,
-                   tagName: null, tagStart: null,
-                   context: null}
-      if (baseIndent != null) state.baseIndent = baseIndent
-      return state
-    },
-
-    token: function(stream, state) {
-      if (!state.tagName && stream.sol())
-        state.indented = stream.indentation();
-
-      if (stream.eatSpace()) return null;
-      type = null;
-      var style = state.tokenize(stream, state);
-      if ((style || type) && style != "comment") {
-        setStyle = null;
-        state.state = state.state(type || style, stream, state);
-        if (setStyle)
-          style = setStyle == "error" ? style + " error" : setStyle;
-      }
-      return style;
-    },
-
-    indent: function(state, textAfter, fullLine) {
-      var context = state.context;
-      // Indent multi-line strings (e.g. css).
-      if (state.tokenize.isInAttribute) {
-        if (state.tagStart == state.indented)
-          return state.stringStartCol + 1;
-        else
-          return state.indented + indentUnit;
-      }
-      if (context && context.noIndent) return CodeMirror.Pass;
-      if (state.tokenize != inTag && state.tokenize != inText)
-        return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
-      // Indent the starts of attribute names.
-      if (state.tagName) {
-        if (config.multilineTagIndentPastTag !== false)
-          return state.tagStart + state.tagName.length + 2;
-        else
-          return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
-      }
-      if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
-      var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
-      if (tagAfter && tagAfter[1]) { // Closing tag spotted
-        while (context) {
-          if (context.tagName == tagAfter[2]) {
-            context = context.prev;
-            break;
-          } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
-            context = context.prev;
-          } else {
-            break;
-          }
-        }
-      } else if (tagAfter) { // Opening tag spotted
-        while (context) {
-          var grabbers = config.contextGrabbers[context.tagName];
-          if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
-            context = context.prev;
-          else
-            break;
-        }
-      }
-      while (context && context.prev && !context.startOfLine)
-        context = context.prev;
-      if (context) return context.indent + indentUnit;
-      else return state.baseIndent || 0;
-    },
-
-    electricInput: /<\/[\s\w:]+>$/,
-    blockCommentStart: "<!--",
-    blockCommentEnd: "-->",
-
-    configuration: config.htmlMode ? "html" : "xml",
-    helperType: config.htmlMode ? "html" : "xml",
-
-    skipAttribute: function(state) {
-      if (state.state == attrValueState)
-        state.state = attrState
-    },
-
-    xmlCurrentTag: function(state) {
-      return state.tagName ? {name: state.tagName, close: state.type == "closeTag"} : null
-    },
-
-    xmlCurrentContext: function(state) {
-      var context = []
-      for (var cx = state.context; cx; cx = cx.prev)
-        if (cx.tagName) context.push(cx.tagName)
-      return context.reverse()
-    }
-  };
-});
-
-CodeMirror.defineMIME("text/xml", "xml");
-CodeMirror.defineMIME("application/xml", "xml");
-if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
-  CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
-
-});
diff --git a/plugins/UiFileManager/media/codemirror/base/codemirror.css b/plugins/UiFileManager/media/codemirror/base/codemirror.css
deleted file mode 100644
index 56896500..00000000
--- a/plugins/UiFileManager/media/codemirror/base/codemirror.css
+++ /dev/null
@@ -1,349 +0,0 @@
-/* BASICS */
-
-.CodeMirror {
-  /* Set height, width, borders, and global font properties here */
-  font-family: monospace;
-  height: 300px;
-  color: black;
-  direction: ltr;
-}
-
-/* PADDING */
-
-.CodeMirror-lines {
-  padding: 4px 0; /* Vertical padding around content */
-}
-.CodeMirror pre.CodeMirror-line,
-.CodeMirror pre.CodeMirror-line-like {
-  padding: 0 4px; /* Horizontal padding of content */
-}
-
-.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
-  background-color: white; /* The little square between H and V scrollbars */
-}
-
-/* GUTTER */
-
-.CodeMirror-gutters {
-  border-right: 1px solid #ddd;
-  background-color: #f7f7f7;
-  white-space: nowrap;
-}
-.CodeMirror-linenumbers {}
-.CodeMirror-linenumber {
-  padding: 0 3px 0 5px;
-  min-width: 20px;
-  text-align: right;
-  color: #999;
-  white-space: nowrap;
-}
-
-.CodeMirror-guttermarker { color: black; }
-.CodeMirror-guttermarker-subtle { color: #999; }
-
-/* CURSOR */
-
-.CodeMirror-cursor {
-  border-left: 1px solid black;
-  border-right: none;
-  width: 0;
-}
-/* Shown when moving in bi-directional text */
-.CodeMirror div.CodeMirror-secondarycursor {
-  border-left: 1px solid silver;
-}
-.cm-fat-cursor .CodeMirror-cursor {
-  width: auto;
-  border: 0 !important;
-  background: #7e7;
-}
-.cm-fat-cursor div.CodeMirror-cursors {
-  z-index: 1;
-}
-.cm-fat-cursor-mark {
-  background-color: rgba(20, 255, 20, 0.5);
-  -webkit-animation: blink 1.06s steps(1) infinite;
-  -moz-animation: blink 1.06s steps(1) infinite;
-  animation: blink 1.06s steps(1) infinite;
-}
-.cm-animate-fat-cursor {
-  width: auto;
-  border: 0;
-  -webkit-animation: blink 1.06s steps(1) infinite;
-  -moz-animation: blink 1.06s steps(1) infinite;
-  animation: blink 1.06s steps(1) infinite;
-  background-color: #7e7;
-}
-@-moz-keyframes blink {
-  0% {}
-  50% { background-color: transparent; }
-  100% {}
-}
-@-webkit-keyframes blink {
-  0% {}
-  50% { background-color: transparent; }
-  100% {}
-}
-@keyframes blink {
-  0% {}
-  50% { background-color: transparent; }
-  100% {}
-}
-
-/* Can style cursor different in overwrite (non-insert) mode */
-.CodeMirror-overwrite .CodeMirror-cursor {}
-
-.cm-tab { display: inline-block; text-decoration: inherit; }
-
-.CodeMirror-rulers {
-  position: absolute;
-  left: 0; right: 0; top: -50px; bottom: 0;
-  overflow: hidden;
-}
-.CodeMirror-ruler {
-  border-left: 1px solid #ccc;
-  top: 0; bottom: 0;
-  position: absolute;
-}
-
-/* DEFAULT THEME */
-
-.cm-s-default .cm-header {color: blue;}
-.cm-s-default .cm-quote {color: #090;}
-.cm-negative {color: #d44;}
-.cm-positive {color: #292;}
-.cm-header, .cm-strong {font-weight: bold;}
-.cm-em {font-style: italic;}
-.cm-link {text-decoration: underline;}
-.cm-strikethrough {text-decoration: line-through;}
-
-.cm-s-default .cm-keyword {color: #708;}
-.cm-s-default .cm-atom {color: #219;}
-.cm-s-default .cm-number {color: #164;}
-.cm-s-default .cm-def {color: #00f;}
-.cm-s-default .cm-variable,
-.cm-s-default .cm-punctuation,
-.cm-s-default .cm-property,
-.cm-s-default .cm-operator {}
-.cm-s-default .cm-variable-2 {color: #05a;}
-.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
-.cm-s-default .cm-comment {color: #a50;}
-.cm-s-default .cm-string {color: #a11;}
-.cm-s-default .cm-string-2 {color: #f50;}
-.cm-s-default .cm-meta {color: #555;}
-.cm-s-default .cm-qualifier {color: #555;}
-.cm-s-default .cm-builtin {color: #30a;}
-.cm-s-default .cm-bracket {color: #997;}
-.cm-s-default .cm-tag {color: #170;}
-.cm-s-default .cm-attribute {color: #00c;}
-.cm-s-default .cm-hr {color: #999;}
-.cm-s-default .cm-link {color: #00c;}
-
-.cm-s-default .cm-error {color: #f00;}
-.cm-invalidchar {color: #f00;}
-
-.CodeMirror-composing { border-bottom: 2px solid; }
-
-/* Default styles for common addons */
-
-div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}
-div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
-.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
-.CodeMirror-activeline-background {background: #e8f2ff;}
-
-/* STOP */
-
-/* The rest of this file contains styles related to the mechanics of
-   the editor. You probably shouldn't touch them. */
-
-.CodeMirror {
-  position: relative;
-  overflow: hidden;
-  background: white;
-}
-
-.CodeMirror-scroll {
-  overflow: scroll !important; /* Things will break if this is overridden */
-  /* 50px is the magic margin used to hide the element's real scrollbars */
-  /* See overflow: hidden in .CodeMirror */
-  margin-bottom: -50px; margin-right: -50px;
-  padding-bottom: 50px;
-  height: 100%;
-  outline: none; /* Prevent dragging from highlighting the element */
-  position: relative;
-}
-.CodeMirror-sizer {
-  position: relative;
-  border-right: 50px solid transparent;
-}
-
-/* The fake, visible scrollbars. Used to force redraw during scrolling
-   before actual scrolling happens, thus preventing shaking and
-   flickering artifacts. */
-.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
-  position: absolute;
-  z-index: 6;
-  display: none;
-}
-.CodeMirror-vscrollbar {
-  right: 0; top: 0;
-  overflow-x: hidden;
-  overflow-y: scroll;
-}
-.CodeMirror-hscrollbar {
-  bottom: 0; left: 0;
-  overflow-y: hidden;
-  overflow-x: scroll;
-}
-.CodeMirror-scrollbar-filler {
-  right: 0; bottom: 0;
-}
-.CodeMirror-gutter-filler {
-  left: 0; bottom: 0;
-}
-
-.CodeMirror-gutters {
-  position: absolute; left: 0; top: 0;
-  min-height: 100%;
-  z-index: 3;
-}
-.CodeMirror-gutter {
-  white-space: normal;
-  height: 100%;
-  display: inline-block;
-  vertical-align: top;
-  margin-bottom: -50px;
-}
-.CodeMirror-gutter-wrapper {
-  position: absolute;
-  z-index: 4;
-  background: none !important;
-  border: none !important;
-}
-.CodeMirror-gutter-background {
-  position: absolute;
-  top: 0; bottom: 0;
-  z-index: 4;
-}
-.CodeMirror-gutter-elt {
-  position: absolute;
-  cursor: default;
-  z-index: 4;
-}
-.CodeMirror-gutter-wrapper ::selection { background-color: transparent }
-.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
-
-.CodeMirror-lines {
-  cursor: text;
-  min-height: 1px; /* prevents collapsing before first draw */
-}
-.CodeMirror pre.CodeMirror-line,
-.CodeMirror pre.CodeMirror-line-like {
-  /* Reset some styles that the rest of the page might have set */
-  -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
-  border-width: 0;
-  background: transparent;
-  font-family: inherit;
-  font-size: inherit;
-  margin: 0;
-  white-space: pre;
-  word-wrap: normal;
-  line-height: inherit;
-  color: inherit;
-  z-index: 2;
-  position: relative;
-  overflow: visible;
-  -webkit-tap-highlight-color: transparent;
-  -webkit-font-variant-ligatures: contextual;
-  font-variant-ligatures: contextual;
-}
-.CodeMirror-wrap pre.CodeMirror-line,
-.CodeMirror-wrap pre.CodeMirror-line-like {
-  word-wrap: break-word;
-  white-space: pre-wrap;
-  word-break: normal;
-}
-
-.CodeMirror-linebackground {
-  position: absolute;
-  left: 0; right: 0; top: 0; bottom: 0;
-  z-index: 0;
-}
-
-.CodeMirror-linewidget {
-  position: relative;
-  z-index: 2;
-  padding: 0.1px; /* Force widget margins to stay inside of the container */
-}
-
-.CodeMirror-widget {}
-
-.CodeMirror-rtl pre { direction: rtl; }
-
-.CodeMirror-code {
-  outline: none;
-}
-
-/* Force content-box sizing for the elements where we expect it */
-.CodeMirror-scroll,
-.CodeMirror-sizer,
-.CodeMirror-gutter,
-.CodeMirror-gutters,
-.CodeMirror-linenumber {
-  -moz-box-sizing: content-box;
-  box-sizing: content-box;
-}
-
-.CodeMirror-measure {
-  position: absolute;
-  width: 100%;
-  height: 0;
-  overflow: hidden;
-  visibility: hidden;
-}
-
-.CodeMirror-cursor {
-  position: absolute;
-  pointer-events: none;
-}
-.CodeMirror-measure pre { position: static; }
-
-div.CodeMirror-cursors {
-  visibility: hidden;
-  position: relative;
-  z-index: 3;
-}
-div.CodeMirror-dragcursors {
-  visibility: visible;
-}
-
-.CodeMirror-focused div.CodeMirror-cursors {
-  visibility: visible;
-}
-
-.CodeMirror-selected { background: #d9d9d9; }
-.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
-.CodeMirror-crosshair { cursor: crosshair; }
-.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
-.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
-
-.cm-searching {
-  background-color: #ffa;
-  background-color: rgba(255, 255, 0, .4);
-}
-
-/* Used to force a border model for a node */
-.cm-force-border { padding-right: .1px; }
-
-@media print {
-  /* Hide the cursor when printing */
-  .CodeMirror div.CodeMirror-cursors {
-    visibility: hidden;
-  }
-}
-
-/* See issue #2901 */
-.cm-tab-wrap-hack:after { content: ''; }
-
-/* Help users use markselection to safely style text background */
-span.CodeMirror-selectedtext { background: none; }
diff --git a/plugins/UiFileManager/media/codemirror/base/codemirror.js b/plugins/UiFileManager/media/codemirror/base/codemirror.js
deleted file mode 100644
index 06f0f868..00000000
--- a/plugins/UiFileManager/media/codemirror/base/codemirror.js
+++ /dev/null
@@ -1,9778 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-// This is CodeMirror (https://codemirror.net), a code editor
-// implemented in JavaScript on top of the browser's DOM.
-//
-// You can find some technical background for some of the code below
-// at http://marijnhaverbeke.nl/blog/#cm-internals .
-
-(function (global, factory) {
-  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
-  typeof define === 'function' && define.amd ? define(factory) :
-  (global = global || self, global.CodeMirror = factory());
-}(this, (function () { 'use strict';
-
-  // Kludges for bugs and behavior differences that can't be feature
-  // detected are enabled based on userAgent etc sniffing.
-  var userAgent = navigator.userAgent;
-  var platform = navigator.platform;
-
-  var gecko = /gecko\/\d/i.test(userAgent);
-  var ie_upto10 = /MSIE \d/.test(userAgent);
-  var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
-  var edge = /Edge\/(\d+)/.exec(userAgent);
-  var ie = ie_upto10 || ie_11up || edge;
-  var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);
-  var webkit = !edge && /WebKit\//.test(userAgent);
-  var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
-  var chrome = !edge && /Chrome\//.test(userAgent);
-  var presto = /Opera\//.test(userAgent);
-  var safari = /Apple Computer/.test(navigator.vendor);
-  var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
-  var phantom = /PhantomJS/.test(userAgent);
-
-  var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
-  var android = /Android/.test(userAgent);
-  // This is woefully incomplete. Suggestions for alternative methods welcome.
-  var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
-  var mac = ios || /Mac/.test(platform);
-  var chromeOS = /\bCrOS\b/.test(userAgent);
-  var windows = /win/i.test(platform);
-
-  var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
-  if (presto_version) { presto_version = Number(presto_version[1]); }
-  if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
-  // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
-  var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
-  var captureRightClick = gecko || (ie && ie_version >= 9);
-
-  function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
-
-  var rmClass = function(node, cls) {
-    var current = node.className;
-    var match = classTest(cls).exec(current);
-    if (match) {
-      var after = current.slice(match.index + match[0].length);
-      node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
-    }
-  };
-
-  function removeChildren(e) {
-    for (var count = e.childNodes.length; count > 0; --count)
-      { e.removeChild(e.firstChild); }
-    return e
-  }
-
-  function removeChildrenAndAdd(parent, e) {
-    return removeChildren(parent).appendChild(e)
-  }
-
-  function elt(tag, content, className, style) {
-    var e = document.createElement(tag);
-    if (className) { e.className = className; }
-    if (style) { e.style.cssText = style; }
-    if (typeof content == "string") { e.appendChild(document.createTextNode(content)); }
-    else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }
-    return e
-  }
-  // wrapper for elt, which removes the elt from the accessibility tree
-  function eltP(tag, content, className, style) {
-    var e = elt(tag, content, className, style);
-    e.setAttribute("role", "presentation");
-    return e
-  }
-
-  var range;
-  if (document.createRange) { range = function(node, start, end, endNode) {
-    var r = document.createRange();
-    r.setEnd(endNode || node, end);
-    r.setStart(node, start);
-    return r
-  }; }
-  else { range = function(node, start, end) {
-    var r = document.body.createTextRange();
-    try { r.moveToElementText(node.parentNode); }
-    catch(e) { return r }
-    r.collapse(true);
-    r.moveEnd("character", end);
-    r.moveStart("character", start);
-    return r
-  }; }
-
-  function contains(parent, child) {
-    if (child.nodeType == 3) // Android browser always returns false when child is a textnode
-      { child = child.parentNode; }
-    if (parent.contains)
-      { return parent.contains(child) }
-    do {
-      if (child.nodeType == 11) { child = child.host; }
-      if (child == parent) { return true }
-    } while (child = child.parentNode)
-  }
-
-  function activeElt() {
-    // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
-    // IE < 10 will throw when accessed while the page is loading or in an iframe.
-    // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
-    var activeElement;
-    try {
-      activeElement = document.activeElement;
-    } catch(e) {
-      activeElement = document.body || null;
-    }
-    while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
-      { activeElement = activeElement.shadowRoot.activeElement; }
-    return activeElement
-  }
-
-  function addClass(node, cls) {
-    var current = node.className;
-    if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; }
-  }
-  function joinClasses(a, b) {
-    var as = a.split(" ");
-    for (var i = 0; i < as.length; i++)
-      { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } }
-    return b
-  }
-
-  var selectInput = function(node) { node.select(); };
-  if (ios) // Mobile Safari apparently has a bug where select() is broken.
-    { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }
-  else if (ie) // Suppress mysterious IE10 errors
-    { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }
-
-  function bind(f) {
-    var args = Array.prototype.slice.call(arguments, 1);
-    return function(){return f.apply(null, args)}
-  }
-
-  function copyObj(obj, target, overwrite) {
-    if (!target) { target = {}; }
-    for (var prop in obj)
-      { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
-        { target[prop] = obj[prop]; } }
-    return target
-  }
-
-  // Counts the column offset in a string, taking tabs into account.
-  // Used mostly to find indentation.
-  function countColumn(string, end, tabSize, startIndex, startValue) {
-    if (end == null) {
-      end = string.search(/[^\s\u00a0]/);
-      if (end == -1) { end = string.length; }
-    }
-    for (var i = startIndex || 0, n = startValue || 0;;) {
-      var nextTab = string.indexOf("\t", i);
-      if (nextTab < 0 || nextTab >= end)
-        { return n + (end - i) }
-      n += nextTab - i;
-      n += tabSize - (n % tabSize);
-      i = nextTab + 1;
-    }
-  }
-
-  var Delayed = function() {
-    this.id = null;
-    this.f = null;
-    this.time = 0;
-    this.handler = bind(this.onTimeout, this);
-  };
-  Delayed.prototype.onTimeout = function (self) {
-    self.id = 0;
-    if (self.time <= +new Date) {
-      self.f();
-    } else {
-      setTimeout(self.handler, self.time - +new Date);
-    }
-  };
-  Delayed.prototype.set = function (ms, f) {
-    this.f = f;
-    var time = +new Date + ms;
-    if (!this.id || time < this.time) {
-      clearTimeout(this.id);
-      this.id = setTimeout(this.handler, ms);
-      this.time = time;
-    }
-  };
-
-  function indexOf(array, elt) {
-    for (var i = 0; i < array.length; ++i)
-      { if (array[i] == elt) { return i } }
-    return -1
-  }
-
-  // Number of pixels added to scroller and sizer to hide scrollbar
-  var scrollerGap = 50;
-
-  // Returned or thrown by various protocols to signal 'I'm not
-  // handling this'.
-  var Pass = {toString: function(){return "CodeMirror.Pass"}};
-
-  // Reused option objects for setSelection & friends
-  var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
-
-  // The inverse of countColumn -- find the offset that corresponds to
-  // a particular column.
-  function findColumn(string, goal, tabSize) {
-    for (var pos = 0, col = 0;;) {
-      var nextTab = string.indexOf("\t", pos);
-      if (nextTab == -1) { nextTab = string.length; }
-      var skipped = nextTab - pos;
-      if (nextTab == string.length || col + skipped >= goal)
-        { return pos + Math.min(skipped, goal - col) }
-      col += nextTab - pos;
-      col += tabSize - (col % tabSize);
-      pos = nextTab + 1;
-      if (col >= goal) { return pos }
-    }
-  }
-
-  var spaceStrs = [""];
-  function spaceStr(n) {
-    while (spaceStrs.length <= n)
-      { spaceStrs.push(lst(spaceStrs) + " "); }
-    return spaceStrs[n]
-  }
-
-  function lst(arr) { return arr[arr.length-1] }
-
-  function map(array, f) {
-    var out = [];
-    for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }
-    return out
-  }
-
-  function insertSorted(array, value, score) {
-    var pos = 0, priority = score(value);
-    while (pos < array.length && score(array[pos]) <= priority) { pos++; }
-    array.splice(pos, 0, value);
-  }
-
-  function nothing() {}
-
-  function createObj(base, props) {
-    var inst;
-    if (Object.create) {
-      inst = Object.create(base);
-    } else {
-      nothing.prototype = base;
-      inst = new nothing();
-    }
-    if (props) { copyObj(props, inst); }
-    return inst
-  }
-
-  var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
-  function isWordCharBasic(ch) {
-    return /\w/.test(ch) || ch > "\x80" &&
-      (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
-  }
-  function isWordChar(ch, helper) {
-    if (!helper) { return isWordCharBasic(ch) }
-    if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
-    return helper.test(ch)
-  }
-
-  function isEmpty(obj) {
-    for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
-    return true
-  }
-
-  // Extending unicode characters. A series of a non-extending char +
-  // any number of extending chars is treated as a single unit as far
-  // as editing and measuring is concerned. This is not fully correct,
-  // since some scripts/fonts/browsers also treat other configurations
-  // of code points as a group.
-  var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;
-  function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
-
-  // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
-  function skipExtendingChars(str, pos, dir) {
-    while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }
-    return pos
-  }
-
-  // Returns the value from the range [`from`; `to`] that satisfies
-  // `pred` and is closest to `from`. Assumes that at least `to`
-  // satisfies `pred`. Supports `from` being greater than `to`.
-  function findFirst(pred, from, to) {
-    // At any point we are certain `to` satisfies `pred`, don't know
-    // whether `from` does.
-    var dir = from > to ? -1 : 1;
-    for (;;) {
-      if (from == to) { return from }
-      var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);
-      if (mid == from) { return pred(mid) ? from : to }
-      if (pred(mid)) { to = mid; }
-      else { from = mid + dir; }
-    }
-  }
-
-  // BIDI HELPERS
-
-  function iterateBidiSections(order, from, to, f) {
-    if (!order) { return f(from, to, "ltr", 0) }
-    var found = false;
-    for (var i = 0; i < order.length; ++i) {
-      var part = order[i];
-      if (part.from < to && part.to > from || from == to && part.to == from) {
-        f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i);
-        found = true;
-      }
-    }
-    if (!found) { f(from, to, "ltr"); }
-  }
-
-  var bidiOther = null;
-  function getBidiPartAt(order, ch, sticky) {
-    var found;
-    bidiOther = null;
-    for (var i = 0; i < order.length; ++i) {
-      var cur = order[i];
-      if (cur.from < ch && cur.to > ch) { return i }
-      if (cur.to == ch) {
-        if (cur.from != cur.to && sticky == "before") { found = i; }
-        else { bidiOther = i; }
-      }
-      if (cur.from == ch) {
-        if (cur.from != cur.to && sticky != "before") { found = i; }
-        else { bidiOther = i; }
-      }
-    }
-    return found != null ? found : bidiOther
-  }
-
-  // Bidirectional ordering algorithm
-  // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
-  // that this (partially) implements.
-
-  // One-char codes used for character types:
-  // L (L):   Left-to-Right
-  // R (R):   Right-to-Left
-  // r (AL):  Right-to-Left Arabic
-  // 1 (EN):  European Number
-  // + (ES):  European Number Separator
-  // % (ET):  European Number Terminator
-  // n (AN):  Arabic Number
-  // , (CS):  Common Number Separator
-  // m (NSM): Non-Spacing Mark
-  // b (BN):  Boundary Neutral
-  // s (B):   Paragraph Separator
-  // t (S):   Segment Separator
-  // w (WS):  Whitespace
-  // N (ON):  Other Neutrals
-
-  // Returns null if characters are ordered as they appear
-  // (left-to-right), or an array of sections ({from, to, level}
-  // objects) in the order in which they occur visually.
-  var bidiOrdering = (function() {
-    // Character types for codepoints 0 to 0xff
-    var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
-    // Character types for codepoints 0x600 to 0x6f9
-    var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";
-    function charType(code) {
-      if (code <= 0xf7) { return lowTypes.charAt(code) }
-      else if (0x590 <= code && code <= 0x5f4) { return "R" }
-      else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
-      else if (0x6ee <= code && code <= 0x8ac) { return "r" }
-      else if (0x2000 <= code && code <= 0x200b) { return "w" }
-      else if (code == 0x200c) { return "b" }
-      else { return "L" }
-    }
-
-    var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
-    var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
-
-    function BidiSpan(level, from, to) {
-      this.level = level;
-      this.from = from; this.to = to;
-    }
-
-    return function(str, direction) {
-      var outerType = direction == "ltr" ? "L" : "R";
-
-      if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
-      var len = str.length, types = [];
-      for (var i = 0; i < len; ++i)
-        { types.push(charType(str.charCodeAt(i))); }
-
-      // W1. Examine each non-spacing mark (NSM) in the level run, and
-      // change the type of the NSM to the type of the previous
-      // character. If the NSM is at the start of the level run, it will
-      // get the type of sor.
-      for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
-        var type = types[i$1];
-        if (type == "m") { types[i$1] = prev; }
-        else { prev = type; }
-      }
-
-      // W2. Search backwards from each instance of a European number
-      // until the first strong type (R, L, AL, or sor) is found. If an
-      // AL is found, change the type of the European number to Arabic
-      // number.
-      // W3. Change all ALs to R.
-      for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
-        var type$1 = types[i$2];
-        if (type$1 == "1" && cur == "r") { types[i$2] = "n"; }
-        else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } }
-      }
-
-      // W4. A single European separator between two European numbers
-      // changes to a European number. A single common separator between
-      // two numbers of the same type changes to that type.
-      for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
-        var type$2 = types[i$3];
-        if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; }
-        else if (type$2 == "," && prev$1 == types[i$3+1] &&
-                 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; }
-        prev$1 = type$2;
-      }
-
-      // W5. A sequence of European terminators adjacent to European
-      // numbers changes to all European numbers.
-      // W6. Otherwise, separators and terminators change to Other
-      // Neutral.
-      for (var i$4 = 0; i$4 < len; ++i$4) {
-        var type$3 = types[i$4];
-        if (type$3 == ",") { types[i$4] = "N"; }
-        else if (type$3 == "%") {
-          var end = (void 0);
-          for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
-          var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
-          for (var j = i$4; j < end; ++j) { types[j] = replace; }
-          i$4 = end - 1;
-        }
-      }
-
-      // W7. Search backwards from each instance of a European number
-      // until the first strong type (R, L, or sor) is found. If an L is
-      // found, then change the type of the European number to L.
-      for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
-        var type$4 = types[i$5];
-        if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; }
-        else if (isStrong.test(type$4)) { cur$1 = type$4; }
-      }
-
-      // N1. A sequence of neutrals takes the direction of the
-      // surrounding strong text if the text on both sides has the same
-      // direction. European and Arabic numbers act as if they were R in
-      // terms of their influence on neutrals. Start-of-level-run (sor)
-      // and end-of-level-run (eor) are used at level run boundaries.
-      // N2. Any remaining neutrals take the embedding direction.
-      for (var i$6 = 0; i$6 < len; ++i$6) {
-        if (isNeutral.test(types[i$6])) {
-          var end$1 = (void 0);
-          for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
-          var before = (i$6 ? types[i$6-1] : outerType) == "L";
-          var after = (end$1 < len ? types[end$1] : outerType) == "L";
-          var replace$1 = before == after ? (before ? "L" : "R") : outerType;
-          for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }
-          i$6 = end$1 - 1;
-        }
-      }
-
-      // Here we depart from the documented algorithm, in order to avoid
-      // building up an actual levels array. Since there are only three
-      // levels (0, 1, 2) in an implementation that doesn't take
-      // explicit embedding into account, we can build up the order on
-      // the fly, without following the level-based algorithm.
-      var order = [], m;
-      for (var i$7 = 0; i$7 < len;) {
-        if (countsAsLeft.test(types[i$7])) {
-          var start = i$7;
-          for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
-          order.push(new BidiSpan(0, start, i$7));
-        } else {
-          var pos = i$7, at = order.length, isRTL = direction == "rtl" ? 1 : 0;
-          for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
-          for (var j$2 = pos; j$2 < i$7;) {
-            if (countsAsNum.test(types[j$2])) {
-              if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); at += isRTL; }
-              var nstart = j$2;
-              for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
-              order.splice(at, 0, new BidiSpan(2, nstart, j$2));
-              at += isRTL;
-              pos = j$2;
-            } else { ++j$2; }
-          }
-          if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }
-        }
-      }
-      if (direction == "ltr") {
-        if (order[0].level == 1 && (m = str.match(/^\s+/))) {
-          order[0].from = m[0].length;
-          order.unshift(new BidiSpan(0, 0, m[0].length));
-        }
-        if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
-          lst(order).to -= m[0].length;
-          order.push(new BidiSpan(0, len - m[0].length, len));
-        }
-      }
-
-      return direction == "rtl" ? order.reverse() : order
-    }
-  })();
-
-  // Get the bidi ordering for the given line (and cache it). Returns
-  // false for lines that are fully left-to-right, and an array of
-  // BidiSpan objects otherwise.
-  function getOrder(line, direction) {
-    var order = line.order;
-    if (order == null) { order = line.order = bidiOrdering(line.text, direction); }
-    return order
-  }
-
-  // EVENT HANDLING
-
-  // Lightweight event framework. on/off also work on DOM nodes,
-  // registering native DOM handlers.
-
-  var noHandlers = [];
-
-  var on = function(emitter, type, f) {
-    if (emitter.addEventListener) {
-      emitter.addEventListener(type, f, false);
-    } else if (emitter.attachEvent) {
-      emitter.attachEvent("on" + type, f);
-    } else {
-      var map = emitter._handlers || (emitter._handlers = {});
-      map[type] = (map[type] || noHandlers).concat(f);
-    }
-  };
-
-  function getHandlers(emitter, type) {
-    return emitter._handlers && emitter._handlers[type] || noHandlers
-  }
-
-  function off(emitter, type, f) {
-    if (emitter.removeEventListener) {
-      emitter.removeEventListener(type, f, false);
-    } else if (emitter.detachEvent) {
-      emitter.detachEvent("on" + type, f);
-    } else {
-      var map = emitter._handlers, arr = map && map[type];
-      if (arr) {
-        var index = indexOf(arr, f);
-        if (index > -1)
-          { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }
-      }
-    }
-  }
-
-  function signal(emitter, type /*, values...*/) {
-    var handlers = getHandlers(emitter, type);
-    if (!handlers.length) { return }
-    var args = Array.prototype.slice.call(arguments, 2);
-    for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }
-  }
-
-  // The DOM events that CodeMirror handles can be overridden by
-  // registering a (non-DOM) handler on the editor for the event name,
-  // and preventDefault-ing the event in that handler.
-  function signalDOMEvent(cm, e, override) {
-    if (typeof e == "string")
-      { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }
-    signal(cm, override || e.type, cm, e);
-    return e_defaultPrevented(e) || e.codemirrorIgnore
-  }
-
-  function signalCursorActivity(cm) {
-    var arr = cm._handlers && cm._handlers.cursorActivity;
-    if (!arr) { return }
-    var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
-    for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
-      { set.push(arr[i]); } }
-  }
-
-  function hasHandler(emitter, type) {
-    return getHandlers(emitter, type).length > 0
-  }
-
-  // Add on and off methods to a constructor's prototype, to make
-  // registering events on such objects more convenient.
-  function eventMixin(ctor) {
-    ctor.prototype.on = function(type, f) {on(this, type, f);};
-    ctor.prototype.off = function(type, f) {off(this, type, f);};
-  }
-
-  // Due to the fact that we still support jurassic IE versions, some
-  // compatibility wrappers are needed.
-
-  function e_preventDefault(e) {
-    if (e.preventDefault) { e.preventDefault(); }
-    else { e.returnValue = false; }
-  }
-  function e_stopPropagation(e) {
-    if (e.stopPropagation) { e.stopPropagation(); }
-    else { e.cancelBubble = true; }
-  }
-  function e_defaultPrevented(e) {
-    return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
-  }
-  function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
-
-  function e_target(e) {return e.target || e.srcElement}
-  function e_button(e) {
-    var b = e.which;
-    if (b == null) {
-      if (e.button & 1) { b = 1; }
-      else if (e.button & 2) { b = 3; }
-      else if (e.button & 4) { b = 2; }
-    }
-    if (mac && e.ctrlKey && b == 1) { b = 3; }
-    return b
-  }
-
-  // Detect drag-and-drop
-  var dragAndDrop = function() {
-    // There is *some* kind of drag-and-drop support in IE6-8, but I
-    // couldn't get it to work yet.
-    if (ie && ie_version < 9) { return false }
-    var div = elt('div');
-    return "draggable" in div || "dragDrop" in div
-  }();
-
-  var zwspSupported;
-  function zeroWidthElement(measure) {
-    if (zwspSupported == null) {
-      var test = elt("span", "\u200b");
-      removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
-      if (measure.firstChild.offsetHeight != 0)
-        { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }
-    }
-    var node = zwspSupported ? elt("span", "\u200b") :
-      elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
-    node.setAttribute("cm-text", "");
-    return node
-  }
-
-  // Feature-detect IE's crummy client rect reporting for bidi text
-  var badBidiRects;
-  function hasBadBidiRects(measure) {
-    if (badBidiRects != null) { return badBidiRects }
-    var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
-    var r0 = range(txt, 0, 1).getBoundingClientRect();
-    var r1 = range(txt, 1, 2).getBoundingClientRect();
-    removeChildren(measure);
-    if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
-    return badBidiRects = (r1.right - r0.right < 3)
-  }
-
-  // See if "".split is the broken IE version, if so, provide an
-  // alternative way to split lines.
-  var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
-    var pos = 0, result = [], l = string.length;
-    while (pos <= l) {
-      var nl = string.indexOf("\n", pos);
-      if (nl == -1) { nl = string.length; }
-      var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
-      var rt = line.indexOf("\r");
-      if (rt != -1) {
-        result.push(line.slice(0, rt));
-        pos += rt + 1;
-      } else {
-        result.push(line);
-        pos = nl + 1;
-      }
-    }
-    return result
-  } : function (string) { return string.split(/\r\n?|\n/); };
-
-  var hasSelection = window.getSelection ? function (te) {
-    try { return te.selectionStart != te.selectionEnd }
-    catch(e) { return false }
-  } : function (te) {
-    var range;
-    try {range = te.ownerDocument.selection.createRange();}
-    catch(e) {}
-    if (!range || range.parentElement() != te) { return false }
-    return range.compareEndPoints("StartToEnd", range) != 0
-  };
-
-  var hasCopyEvent = (function () {
-    var e = elt("div");
-    if ("oncopy" in e) { return true }
-    e.setAttribute("oncopy", "return;");
-    return typeof e.oncopy == "function"
-  })();
-
-  var badZoomedRects = null;
-  function hasBadZoomedRects(measure) {
-    if (badZoomedRects != null) { return badZoomedRects }
-    var node = removeChildrenAndAdd(measure, elt("span", "x"));
-    var normal = node.getBoundingClientRect();
-    var fromRange = range(node, 0, 1).getBoundingClientRect();
-    return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
-  }
-
-  // Known modes, by name and by MIME
-  var modes = {}, mimeModes = {};
-
-  // Extra arguments are stored as the mode's dependencies, which is
-  // used by (legacy) mechanisms like loadmode.js to automatically
-  // load a mode. (Preferred mechanism is the require/define calls.)
-  function defineMode(name, mode) {
-    if (arguments.length > 2)
-      { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
-    modes[name] = mode;
-  }
-
-  function defineMIME(mime, spec) {
-    mimeModes[mime] = spec;
-  }
-
-  // Given a MIME type, a {name, ...options} config object, or a name
-  // string, return a mode config object.
-  function resolveMode(spec) {
-    if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
-      spec = mimeModes[spec];
-    } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
-      var found = mimeModes[spec.name];
-      if (typeof found == "string") { found = {name: found}; }
-      spec = createObj(found, spec);
-      spec.name = found.name;
-    } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
-      return resolveMode("application/xml")
-    } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
-      return resolveMode("application/json")
-    }
-    if (typeof spec == "string") { return {name: spec} }
-    else { return spec || {name: "null"} }
-  }
-
-  // Given a mode spec (anything that resolveMode accepts), find and
-  // initialize an actual mode object.
-  function getMode(options, spec) {
-    spec = resolveMode(spec);
-    var mfactory = modes[spec.name];
-    if (!mfactory) { return getMode(options, "text/plain") }
-    var modeObj = mfactory(options, spec);
-    if (modeExtensions.hasOwnProperty(spec.name)) {
-      var exts = modeExtensions[spec.name];
-      for (var prop in exts) {
-        if (!exts.hasOwnProperty(prop)) { continue }
-        if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
-        modeObj[prop] = exts[prop];
-      }
-    }
-    modeObj.name = spec.name;
-    if (spec.helperType) { modeObj.helperType = spec.helperType; }
-    if (spec.modeProps) { for (var prop$1 in spec.modeProps)
-      { modeObj[prop$1] = spec.modeProps[prop$1]; } }
-
-    return modeObj
-  }
-
-  // This can be used to attach properties to mode objects from
-  // outside the actual mode definition.
-  var modeExtensions = {};
-  function extendMode(mode, properties) {
-    var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
-    copyObj(properties, exts);
-  }
-
-  function copyState(mode, state) {
-    if (state === true) { return state }
-    if (mode.copyState) { return mode.copyState(state) }
-    var nstate = {};
-    for (var n in state) {
-      var val = state[n];
-      if (val instanceof Array) { val = val.concat([]); }
-      nstate[n] = val;
-    }
-    return nstate
-  }
-
-  // Given a mode and a state (for that mode), find the inner mode and
-  // state at the position that the state refers to.
-  function innerMode(mode, state) {
-    var info;
-    while (mode.innerMode) {
-      info = mode.innerMode(state);
-      if (!info || info.mode == mode) { break }
-      state = info.state;
-      mode = info.mode;
-    }
-    return info || {mode: mode, state: state}
-  }
-
-  function startState(mode, a1, a2) {
-    return mode.startState ? mode.startState(a1, a2) : true
-  }
-
-  // STRING STREAM
-
-  // Fed to the mode parsers, provides helper functions to make
-  // parsers more succinct.
-
-  var StringStream = function(string, tabSize, lineOracle) {
-    this.pos = this.start = 0;
-    this.string = string;
-    this.tabSize = tabSize || 8;
-    this.lastColumnPos = this.lastColumnValue = 0;
-    this.lineStart = 0;
-    this.lineOracle = lineOracle;
-  };
-
-  StringStream.prototype.eol = function () {return this.pos >= this.string.length};
-  StringStream.prototype.sol = function () {return this.pos == this.lineStart};
-  StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
-  StringStream.prototype.next = function () {
-    if (this.pos < this.string.length)
-      { return this.string.charAt(this.pos++) }
-  };
-  StringStream.prototype.eat = function (match) {
-    var ch = this.string.charAt(this.pos);
-    var ok;
-    if (typeof match == "string") { ok = ch == match; }
-    else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
-    if (ok) {++this.pos; return ch}
-  };
-  StringStream.prototype.eatWhile = function (match) {
-    var start = this.pos;
-    while (this.eat(match)){}
-    return this.pos > start
-  };
-  StringStream.prototype.eatSpace = function () {
-    var start = this.pos;
-    while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; }
-    return this.pos > start
-  };
-  StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
-  StringStream.prototype.skipTo = function (ch) {
-    var found = this.string.indexOf(ch, this.pos);
-    if (found > -1) {this.pos = found; return true}
-  };
-  StringStream.prototype.backUp = function (n) {this.pos -= n;};
-  StringStream.prototype.column = function () {
-    if (this.lastColumnPos < this.start) {
-      this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
-      this.lastColumnPos = this.start;
-    }
-    return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
-  };
-  StringStream.prototype.indentation = function () {
-    return countColumn(this.string, null, this.tabSize) -
-      (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
-  };
-  StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
-    if (typeof pattern == "string") {
-      var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
-      var substr = this.string.substr(this.pos, pattern.length);
-      if (cased(substr) == cased(pattern)) {
-        if (consume !== false) { this.pos += pattern.length; }
-        return true
-      }
-    } else {
-      var match = this.string.slice(this.pos).match(pattern);
-      if (match && match.index > 0) { return null }
-      if (match && consume !== false) { this.pos += match[0].length; }
-      return match
-    }
-  };
-  StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
-  StringStream.prototype.hideFirstChars = function (n, inner) {
-    this.lineStart += n;
-    try { return inner() }
-    finally { this.lineStart -= n; }
-  };
-  StringStream.prototype.lookAhead = function (n) {
-    var oracle = this.lineOracle;
-    return oracle && oracle.lookAhead(n)
-  };
-  StringStream.prototype.baseToken = function () {
-    var oracle = this.lineOracle;
-    return oracle && oracle.baseToken(this.pos)
-  };
-
-  // Find the line object corresponding to the given line number.
-  function getLine(doc, n) {
-    n -= doc.first;
-    if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
-    var chunk = doc;
-    while (!chunk.lines) {
-      for (var i = 0;; ++i) {
-        var child = chunk.children[i], sz = child.chunkSize();
-        if (n < sz) { chunk = child; break }
-        n -= sz;
-      }
-    }
-    return chunk.lines[n]
-  }
-
-  // Get the part of a document between two positions, as an array of
-  // strings.
-  function getBetween(doc, start, end) {
-    var out = [], n = start.line;
-    doc.iter(start.line, end.line + 1, function (line) {
-      var text = line.text;
-      if (n == end.line) { text = text.slice(0, end.ch); }
-      if (n == start.line) { text = text.slice(start.ch); }
-      out.push(text);
-      ++n;
-    });
-    return out
-  }
-  // Get the lines between from and to, as array of strings.
-  function getLines(doc, from, to) {
-    var out = [];
-    doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value
-    return out
-  }
-
-  // Update the height of a line, propagating the height change
-  // upwards to parent nodes.
-  function updateLineHeight(line, height) {
-    var diff = height - line.height;
-    if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }
-  }
-
-  // Given a line object, find its line number by walking up through
-  // its parent links.
-  function lineNo(line) {
-    if (line.parent == null) { return null }
-    var cur = line.parent, no = indexOf(cur.lines, line);
-    for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
-      for (var i = 0;; ++i) {
-        if (chunk.children[i] == cur) { break }
-        no += chunk.children[i].chunkSize();
-      }
-    }
-    return no + cur.first
-  }
-
-  // Find the line at the given vertical position, using the height
-  // information in the document tree.
-  function lineAtHeight(chunk, h) {
-    var n = chunk.first;
-    outer: do {
-      for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
-        var child = chunk.children[i$1], ch = child.height;
-        if (h < ch) { chunk = child; continue outer }
-        h -= ch;
-        n += child.chunkSize();
-      }
-      return n
-    } while (!chunk.lines)
-    var i = 0;
-    for (; i < chunk.lines.length; ++i) {
-      var line = chunk.lines[i], lh = line.height;
-      if (h < lh) { break }
-      h -= lh;
-    }
-    return n + i
-  }
-
-  function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
-
-  function lineNumberFor(options, i) {
-    return String(options.lineNumberFormatter(i + options.firstLineNumber))
-  }
-
-  // A Pos instance represents a position within the text.
-  function Pos(line, ch, sticky) {
-    if ( sticky === void 0 ) sticky = null;
-
-    if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
-    this.line = line;
-    this.ch = ch;
-    this.sticky = sticky;
-  }
-
-  // Compare two positions, return 0 if they are the same, a negative
-  // number when a is less, and a positive number otherwise.
-  function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
-
-  function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
-
-  function copyPos(x) {return Pos(x.line, x.ch)}
-  function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
-  function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
-
-  // Most of the external API clips given positions to make sure they
-  // actually exist within the document.
-  function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
-  function clipPos(doc, pos) {
-    if (pos.line < doc.first) { return Pos(doc.first, 0) }
-    var last = doc.first + doc.size - 1;
-    if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
-    return clipToLen(pos, getLine(doc, pos.line).text.length)
-  }
-  function clipToLen(pos, linelen) {
-    var ch = pos.ch;
-    if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
-    else if (ch < 0) { return Pos(pos.line, 0) }
-    else { return pos }
-  }
-  function clipPosArray(doc, array) {
-    var out = [];
-    for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }
-    return out
-  }
-
-  var SavedContext = function(state, lookAhead) {
-    this.state = state;
-    this.lookAhead = lookAhead;
-  };
-
-  var Context = function(doc, state, line, lookAhead) {
-    this.state = state;
-    this.doc = doc;
-    this.line = line;
-    this.maxLookAhead = lookAhead || 0;
-    this.baseTokens = null;
-    this.baseTokenPos = 1;
-  };
-
-  Context.prototype.lookAhead = function (n) {
-    var line = this.doc.getLine(this.line + n);
-    if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }
-    return line
-  };
-
-  Context.prototype.baseToken = function (n) {
-    if (!this.baseTokens) { return null }
-    while (this.baseTokens[this.baseTokenPos] <= n)
-      { this.baseTokenPos += 2; }
-    var type = this.baseTokens[this.baseTokenPos + 1];
-    return {type: type && type.replace(/( |^)overlay .*/, ""),
-            size: this.baseTokens[this.baseTokenPos] - n}
-  };
-
-  Context.prototype.nextLine = function () {
-    this.line++;
-    if (this.maxLookAhead > 0) { this.maxLookAhead--; }
-  };
-
-  Context.fromSaved = function (doc, saved, line) {
-    if (saved instanceof SavedContext)
-      { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
-    else
-      { return new Context(doc, copyState(doc.mode, saved), line) }
-  };
-
-  Context.prototype.save = function (copy) {
-    var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;
-    return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
-  };
-
-
-  // Compute a style array (an array starting with a mode generation
-  // -- for invalidation -- followed by pairs of end positions and
-  // style strings), which is used to highlight the tokens on the
-  // line.
-  function highlightLine(cm, line, context, forceToEnd) {
-    // A styles array always starts with a number identifying the
-    // mode/overlays that it is based on (for easy invalidation).
-    var st = [cm.state.modeGen], lineClasses = {};
-    // Compute the base array of styles
-    runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
-            lineClasses, forceToEnd);
-    var state = context.state;
-
-    // Run overlays, adjust style array.
-    var loop = function ( o ) {
-      context.baseTokens = st;
-      var overlay = cm.state.overlays[o], i = 1, at = 0;
-      context.state = true;
-      runMode(cm, line.text, overlay.mode, context, function (end, style) {
-        var start = i;
-        // Ensure there's a token end at the current position, and that i points at it
-        while (at < end) {
-          var i_end = st[i];
-          if (i_end > end)
-            { st.splice(i, 1, end, st[i+1], i_end); }
-          i += 2;
-          at = Math.min(end, i_end);
-        }
-        if (!style) { return }
-        if (overlay.opaque) {
-          st.splice(start, i - start, end, "overlay " + style);
-          i = start + 2;
-        } else {
-          for (; start < i; start += 2) {
-            var cur = st[start+1];
-            st[start+1] = (cur ? cur + " " : "") + "overlay " + style;
-          }
-        }
-      }, lineClasses);
-      context.state = state;
-      context.baseTokens = null;
-      context.baseTokenPos = 1;
-    };
-
-    for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
-
-    return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
-  }
-
-  function getLineStyles(cm, line, updateFrontier) {
-    if (!line.styles || line.styles[0] != cm.state.modeGen) {
-      var context = getContextBefore(cm, lineNo(line));
-      var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);
-      var result = highlightLine(cm, line, context);
-      if (resetState) { context.state = resetState; }
-      line.stateAfter = context.save(!resetState);
-      line.styles = result.styles;
-      if (result.classes) { line.styleClasses = result.classes; }
-      else if (line.styleClasses) { line.styleClasses = null; }
-      if (updateFrontier === cm.doc.highlightFrontier)
-        { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }
-    }
-    return line.styles
-  }
-
-  function getContextBefore(cm, n, precise) {
-    var doc = cm.doc, display = cm.display;
-    if (!doc.mode.startState) { return new Context(doc, true, n) }
-    var start = findStartLine(cm, n, precise);
-    var saved = start > doc.first && getLine(doc, start - 1).stateAfter;
-    var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);
-
-    doc.iter(start, n, function (line) {
-      processLine(cm, line.text, context);
-      var pos = context.line;
-      line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;
-      context.nextLine();
-    });
-    if (precise) { doc.modeFrontier = context.line; }
-    return context
-  }
-
-  // Lightweight form of highlight -- proceed over this line and
-  // update state, but don't save a style array. Used for lines that
-  // aren't currently visible.
-  function processLine(cm, text, context, startAt) {
-    var mode = cm.doc.mode;
-    var stream = new StringStream(text, cm.options.tabSize, context);
-    stream.start = stream.pos = startAt || 0;
-    if (text == "") { callBlankLine(mode, context.state); }
-    while (!stream.eol()) {
-      readToken(mode, stream, context.state);
-      stream.start = stream.pos;
-    }
-  }
-
-  function callBlankLine(mode, state) {
-    if (mode.blankLine) { return mode.blankLine(state) }
-    if (!mode.innerMode) { return }
-    var inner = innerMode(mode, state);
-    if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
-  }
-
-  function readToken(mode, stream, state, inner) {
-    for (var i = 0; i < 10; i++) {
-      if (inner) { inner[0] = innerMode(mode, state).mode; }
-      var style = mode.token(stream, state);
-      if (stream.pos > stream.start) { return style }
-    }
-    throw new Error("Mode " + mode.name + " failed to advance stream.")
-  }
-
-  var Token = function(stream, type, state) {
-    this.start = stream.start; this.end = stream.pos;
-    this.string = stream.current();
-    this.type = type || null;
-    this.state = state;
-  };
-
-  // Utility for getTokenAt and getLineTokens
-  function takeToken(cm, pos, precise, asArray) {
-    var doc = cm.doc, mode = doc.mode, style;
-    pos = clipPos(doc, pos);
-    var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);
-    var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;
-    if (asArray) { tokens = []; }
-    while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
-      stream.start = stream.pos;
-      style = readToken(mode, stream, context.state);
-      if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }
-    }
-    return asArray ? tokens : new Token(stream, style, context.state)
-  }
-
-  function extractLineClasses(type, output) {
-    if (type) { for (;;) {
-      var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
-      if (!lineClass) { break }
-      type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
-      var prop = lineClass[1] ? "bgClass" : "textClass";
-      if (output[prop] == null)
-        { output[prop] = lineClass[2]; }
-      else if (!(new RegExp("(?:^|\\s)" + lineClass[2] + "(?:$|\\s)")).test(output[prop]))
-        { output[prop] += " " + lineClass[2]; }
-    } }
-    return type
-  }
-
-  // Run the given mode's parser over a line, calling f for each token.
-  function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
-    var flattenSpans = mode.flattenSpans;
-    if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }
-    var curStart = 0, curStyle = null;
-    var stream = new StringStream(text, cm.options.tabSize, context), style;
-    var inner = cm.options.addModeClass && [null];
-    if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }
-    while (!stream.eol()) {
-      if (stream.pos > cm.options.maxHighlightLength) {
-        flattenSpans = false;
-        if (forceToEnd) { processLine(cm, text, context, stream.pos); }
-        stream.pos = text.length;
-        style = null;
-      } else {
-        style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);
-      }
-      if (inner) {
-        var mName = inner[0].name;
-        if (mName) { style = "m-" + (style ? mName + " " + style : mName); }
-      }
-      if (!flattenSpans || curStyle != style) {
-        while (curStart < stream.start) {
-          curStart = Math.min(stream.start, curStart + 5000);
-          f(curStart, curStyle);
-        }
-        curStyle = style;
-      }
-      stream.start = stream.pos;
-    }
-    while (curStart < stream.pos) {
-      // Webkit seems to refuse to render text nodes longer than 57444
-      // characters, and returns inaccurate measurements in nodes
-      // starting around 5000 chars.
-      var pos = Math.min(stream.pos, curStart + 5000);
-      f(pos, curStyle);
-      curStart = pos;
-    }
-  }
-
-  // Finds the line to start with when starting a parse. Tries to
-  // find a line with a stateAfter, so that it can start with a
-  // valid state. If that fails, it returns the line with the
-  // smallest indentation, which tends to need the least context to
-  // parse correctly.
-  function findStartLine(cm, n, precise) {
-    var minindent, minline, doc = cm.doc;
-    var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
-    for (var search = n; search > lim; --search) {
-      if (search <= doc.first) { return doc.first }
-      var line = getLine(doc, search - 1), after = line.stateAfter;
-      if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
-        { return search }
-      var indented = countColumn(line.text, null, cm.options.tabSize);
-      if (minline == null || minindent > indented) {
-        minline = search - 1;
-        minindent = indented;
-      }
-    }
-    return minline
-  }
-
-  function retreatFrontier(doc, n) {
-    doc.modeFrontier = Math.min(doc.modeFrontier, n);
-    if (doc.highlightFrontier < n - 10) { return }
-    var start = doc.first;
-    for (var line = n - 1; line > start; line--) {
-      var saved = getLine(doc, line).stateAfter;
-      // change is on 3
-      // state on line 1 looked ahead 2 -- so saw 3
-      // test 1 + 2 < 3 should cover this
-      if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
-        start = line + 1;
-        break
-      }
-    }
-    doc.highlightFrontier = Math.min(doc.highlightFrontier, start);
-  }
-
-  // Optimize some code when these features are not used.
-  var sawReadOnlySpans = false, sawCollapsedSpans = false;
-
-  function seeReadOnlySpans() {
-    sawReadOnlySpans = true;
-  }
-
-  function seeCollapsedSpans() {
-    sawCollapsedSpans = true;
-  }
-
-  // TEXTMARKER SPANS
-
-  function MarkedSpan(marker, from, to) {
-    this.marker = marker;
-    this.from = from; this.to = to;
-  }
-
-  // Search an array of spans for a span matching the given marker.
-  function getMarkedSpanFor(spans, marker) {
-    if (spans) { for (var i = 0; i < spans.length; ++i) {
-      var span = spans[i];
-      if (span.marker == marker) { return span }
-    } }
-  }
-  // Remove a span from an array, returning undefined if no spans are
-  // left (we don't store arrays for lines without spans).
-  function removeMarkedSpan(spans, span) {
-    var r;
-    for (var i = 0; i < spans.length; ++i)
-      { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }
-    return r
-  }
-  // Add a span to a line.
-  function addMarkedSpan(line, span) {
-    line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
-    span.marker.attachLine(line);
-  }
-
-  // Used for the algorithm that adjusts markers for a change in the
-  // document. These functions cut an array of spans at a given
-  // character position, returning an array of remaining chunks (or
-  // undefined if nothing remains).
-  function markedSpansBefore(old, startCh, isInsert) {
-    var nw;
-    if (old) { for (var i = 0; i < old.length; ++i) {
-      var span = old[i], marker = span.marker;
-      var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
-      if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
-        var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)
-        ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
-      }
-    } }
-    return nw
-  }
-  function markedSpansAfter(old, endCh, isInsert) {
-    var nw;
-    if (old) { for (var i = 0; i < old.length; ++i) {
-      var span = old[i], marker = span.marker;
-      var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
-      if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
-        var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
-        ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
-                                              span.to == null ? null : span.to - endCh));
-      }
-    } }
-    return nw
-  }
-
-  // Given a change object, compute the new set of marker spans that
-  // cover the line in which the change took place. Removes spans
-  // entirely within the change, reconnects spans belonging to the
-  // same marker that appear on both sides of the change, and cuts off
-  // spans partially within the change. Returns an array of span
-  // arrays with one element for each line in (after) the change.
-  function stretchSpansOverChange(doc, change) {
-    if (change.full) { return null }
-    var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
-    var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
-    if (!oldFirst && !oldLast) { return null }
-
-    var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
-    // Get the spans that 'stick out' on both sides
-    var first = markedSpansBefore(oldFirst, startCh, isInsert);
-    var last = markedSpansAfter(oldLast, endCh, isInsert);
-
-    // Next, merge those two ends
-    var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
-    if (first) {
-      // Fix up .to properties of first
-      for (var i = 0; i < first.length; ++i) {
-        var span = first[i];
-        if (span.to == null) {
-          var found = getMarkedSpanFor(last, span.marker);
-          if (!found) { span.to = startCh; }
-          else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }
-        }
-      }
-    }
-    if (last) {
-      // Fix up .from in last (or move them into first in case of sameLine)
-      for (var i$1 = 0; i$1 < last.length; ++i$1) {
-        var span$1 = last[i$1];
-        if (span$1.to != null) { span$1.to += offset; }
-        if (span$1.from == null) {
-          var found$1 = getMarkedSpanFor(first, span$1.marker);
-          if (!found$1) {
-            span$1.from = offset;
-            if (sameLine) { (first || (first = [])).push(span$1); }
-          }
-        } else {
-          span$1.from += offset;
-          if (sameLine) { (first || (first = [])).push(span$1); }
-        }
-      }
-    }
-    // Make sure we didn't create any zero-length spans
-    if (first) { first = clearEmptySpans(first); }
-    if (last && last != first) { last = clearEmptySpans(last); }
-
-    var newMarkers = [first];
-    if (!sameLine) {
-      // Fill gap with whole-line-spans
-      var gap = change.text.length - 2, gapMarkers;
-      if (gap > 0 && first)
-        { for (var i$2 = 0; i$2 < first.length; ++i$2)
-          { if (first[i$2].to == null)
-            { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }
-      for (var i$3 = 0; i$3 < gap; ++i$3)
-        { newMarkers.push(gapMarkers); }
-      newMarkers.push(last);
-    }
-    return newMarkers
-  }
-
-  // Remove spans that are empty and don't have a clearWhenEmpty
-  // option of false.
-  function clearEmptySpans(spans) {
-    for (var i = 0; i < spans.length; ++i) {
-      var span = spans[i];
-      if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
-        { spans.splice(i--, 1); }
-    }
-    if (!spans.length) { return null }
-    return spans
-  }
-
-  // Used to 'clip' out readOnly ranges when making a change.
-  function removeReadOnlyRanges(doc, from, to) {
-    var markers = null;
-    doc.iter(from.line, to.line + 1, function (line) {
-      if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
-        var mark = line.markedSpans[i].marker;
-        if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
-          { (markers || (markers = [])).push(mark); }
-      } }
-    });
-    if (!markers) { return null }
-    var parts = [{from: from, to: to}];
-    for (var i = 0; i < markers.length; ++i) {
-      var mk = markers[i], m = mk.find(0);
-      for (var j = 0; j < parts.length; ++j) {
-        var p = parts[j];
-        if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
-        var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
-        if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
-          { newParts.push({from: p.from, to: m.from}); }
-        if (dto > 0 || !mk.inclusiveRight && !dto)
-          { newParts.push({from: m.to, to: p.to}); }
-        parts.splice.apply(parts, newParts);
-        j += newParts.length - 3;
-      }
-    }
-    return parts
-  }
-
-  // Connect or disconnect spans from a line.
-  function detachMarkedSpans(line) {
-    var spans = line.markedSpans;
-    if (!spans) { return }
-    for (var i = 0; i < spans.length; ++i)
-      { spans[i].marker.detachLine(line); }
-    line.markedSpans = null;
-  }
-  function attachMarkedSpans(line, spans) {
-    if (!spans) { return }
-    for (var i = 0; i < spans.length; ++i)
-      { spans[i].marker.attachLine(line); }
-    line.markedSpans = spans;
-  }
-
-  // Helpers used when computing which overlapping collapsed span
-  // counts as the larger one.
-  function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
-  function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
-
-  // Returns a number indicating which of two overlapping collapsed
-  // spans is larger (and thus includes the other). Falls back to
-  // comparing ids when the spans cover exactly the same range.
-  function compareCollapsedMarkers(a, b) {
-    var lenDiff = a.lines.length - b.lines.length;
-    if (lenDiff != 0) { return lenDiff }
-    var aPos = a.find(), bPos = b.find();
-    var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
-    if (fromCmp) { return -fromCmp }
-    var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
-    if (toCmp) { return toCmp }
-    return b.id - a.id
-  }
-
-  // Find out whether a line ends or starts in a collapsed span. If
-  // so, return the marker for that span.
-  function collapsedSpanAtSide(line, start) {
-    var sps = sawCollapsedSpans && line.markedSpans, found;
-    if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
-      sp = sps[i];
-      if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
-          (!found || compareCollapsedMarkers(found, sp.marker) < 0))
-        { found = sp.marker; }
-    } }
-    return found
-  }
-  function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
-  function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
-
-  function collapsedSpanAround(line, ch) {
-    var sps = sawCollapsedSpans && line.markedSpans, found;
-    if (sps) { for (var i = 0; i < sps.length; ++i) {
-      var sp = sps[i];
-      if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&
-          (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; }
-    } }
-    return found
-  }
-
-  // Test whether there exists a collapsed span that partially
-  // overlaps (covers the start or end, but not both) of a new span.
-  // Such overlap is not allowed.
-  function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
-    var line = getLine(doc, lineNo);
-    var sps = sawCollapsedSpans && line.markedSpans;
-    if (sps) { for (var i = 0; i < sps.length; ++i) {
-      var sp = sps[i];
-      if (!sp.marker.collapsed) { continue }
-      var found = sp.marker.find(0);
-      var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
-      var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
-      if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
-      if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
-          fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
-        { return true }
-    } }
-  }
-
-  // A visual line is a line as drawn on the screen. Folding, for
-  // example, can cause multiple logical lines to appear on the same
-  // visual line. This finds the start of the visual line that the
-  // given line is part of (usually that is the line itself).
-  function visualLine(line) {
-    var merged;
-    while (merged = collapsedSpanAtStart(line))
-      { line = merged.find(-1, true).line; }
-    return line
-  }
-
-  function visualLineEnd(line) {
-    var merged;
-    while (merged = collapsedSpanAtEnd(line))
-      { line = merged.find(1, true).line; }
-    return line
-  }
-
-  // Returns an array of logical lines that continue the visual line
-  // started by the argument, or undefined if there are no such lines.
-  function visualLineContinued(line) {
-    var merged, lines;
-    while (merged = collapsedSpanAtEnd(line)) {
-      line = merged.find(1, true).line
-      ;(lines || (lines = [])).push(line);
-    }
-    return lines
-  }
-
-  // Get the line number of the start of the visual line that the
-  // given line number is part of.
-  function visualLineNo(doc, lineN) {
-    var line = getLine(doc, lineN), vis = visualLine(line);
-    if (line == vis) { return lineN }
-    return lineNo(vis)
-  }
-
-  // Get the line number of the start of the next visual line after
-  // the given line.
-  function visualLineEndNo(doc, lineN) {
-    if (lineN > doc.lastLine()) { return lineN }
-    var line = getLine(doc, lineN), merged;
-    if (!lineIsHidden(doc, line)) { return lineN }
-    while (merged = collapsedSpanAtEnd(line))
-      { line = merged.find(1, true).line; }
-    return lineNo(line) + 1
-  }
-
-  // Compute whether a line is hidden. Lines count as hidden when they
-  // are part of a visual line that starts with another line, or when
-  // they are entirely covered by collapsed, non-widget span.
-  function lineIsHidden(doc, line) {
-    var sps = sawCollapsedSpans && line.markedSpans;
-    if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
-      sp = sps[i];
-      if (!sp.marker.collapsed) { continue }
-      if (sp.from == null) { return true }
-      if (sp.marker.widgetNode) { continue }
-      if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
-        { return true }
-    } }
-  }
-  function lineIsHiddenInner(doc, line, span) {
-    if (span.to == null) {
-      var end = span.marker.find(1, true);
-      return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
-    }
-    if (span.marker.inclusiveRight && span.to == line.text.length)
-      { return true }
-    for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
-      sp = line.markedSpans[i];
-      if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
-          (sp.to == null || sp.to != span.from) &&
-          (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
-          lineIsHiddenInner(doc, line, sp)) { return true }
-    }
-  }
-
-  // Find the height above the given line.
-  function heightAtLine(lineObj) {
-    lineObj = visualLine(lineObj);
-
-    var h = 0, chunk = lineObj.parent;
-    for (var i = 0; i < chunk.lines.length; ++i) {
-      var line = chunk.lines[i];
-      if (line == lineObj) { break }
-      else { h += line.height; }
-    }
-    for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
-      for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
-        var cur = p.children[i$1];
-        if (cur == chunk) { break }
-        else { h += cur.height; }
-      }
-    }
-    return h
-  }
-
-  // Compute the character length of a line, taking into account
-  // collapsed ranges (see markText) that might hide parts, and join
-  // other lines onto it.
-  function lineLength(line) {
-    if (line.height == 0) { return 0 }
-    var len = line.text.length, merged, cur = line;
-    while (merged = collapsedSpanAtStart(cur)) {
-      var found = merged.find(0, true);
-      cur = found.from.line;
-      len += found.from.ch - found.to.ch;
-    }
-    cur = line;
-    while (merged = collapsedSpanAtEnd(cur)) {
-      var found$1 = merged.find(0, true);
-      len -= cur.text.length - found$1.from.ch;
-      cur = found$1.to.line;
-      len += cur.text.length - found$1.to.ch;
-    }
-    return len
-  }
-
-  // Find the longest line in the document.
-  function findMaxLine(cm) {
-    var d = cm.display, doc = cm.doc;
-    d.maxLine = getLine(doc, doc.first);
-    d.maxLineLength = lineLength(d.maxLine);
-    d.maxLineChanged = true;
-    doc.iter(function (line) {
-      var len = lineLength(line);
-      if (len > d.maxLineLength) {
-        d.maxLineLength = len;
-        d.maxLine = line;
-      }
-    });
-  }
-
-  // LINE DATA STRUCTURE
-
-  // Line objects. These hold state related to a line, including
-  // highlighting info (the styles array).
-  var Line = function(text, markedSpans, estimateHeight) {
-    this.text = text;
-    attachMarkedSpans(this, markedSpans);
-    this.height = estimateHeight ? estimateHeight(this) : 1;
-  };
-
-  Line.prototype.lineNo = function () { return lineNo(this) };
-  eventMixin(Line);
-
-  // Change the content (text, markers) of a line. Automatically
-  // invalidates cached information and tries to re-estimate the
-  // line's height.
-  function updateLine(line, text, markedSpans, estimateHeight) {
-    line.text = text;
-    if (line.stateAfter) { line.stateAfter = null; }
-    if (line.styles) { line.styles = null; }
-    if (line.order != null) { line.order = null; }
-    detachMarkedSpans(line);
-    attachMarkedSpans(line, markedSpans);
-    var estHeight = estimateHeight ? estimateHeight(line) : 1;
-    if (estHeight != line.height) { updateLineHeight(line, estHeight); }
-  }
-
-  // Detach a line from the document tree and its markers.
-  function cleanUpLine(line) {
-    line.parent = null;
-    detachMarkedSpans(line);
-  }
-
-  // Convert a style as returned by a mode (either null, or a string
-  // containing one or more styles) to a CSS style. This is cached,
-  // and also looks for line-wide styles.
-  var styleToClassCache = {}, styleToClassCacheWithMode = {};
-  function interpretTokenStyle(style, options) {
-    if (!style || /^\s*$/.test(style)) { return null }
-    var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
-    return cache[style] ||
-      (cache[style] = style.replace(/\S+/g, "cm-$&"))
-  }
-
-  // Render the DOM representation of the text of a line. Also builds
-  // up a 'line map', which points at the DOM nodes that represent
-  // specific stretches of text, and is used by the measuring code.
-  // The returned object contains the DOM node, this map, and
-  // information about line-wide styles that were set by the mode.
-  function buildLineContent(cm, lineView) {
-    // The padding-right forces the element to have a 'border', which
-    // is needed on Webkit to be able to get line-level bounding
-    // rectangles for it (in measureChar).
-    var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
-    var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
-                   col: 0, pos: 0, cm: cm,
-                   trailingSpace: false,
-                   splitSpaces: cm.getOption("lineWrapping")};
-    lineView.measure = {};
-
-    // Iterate over the logical lines that make up this visual line.
-    for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
-      var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);
-      builder.pos = 0;
-      builder.addToken = buildToken;
-      // Optionally wire in some hacks into the token-rendering
-      // algorithm, to deal with browser quirks.
-      if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
-        { builder.addToken = buildTokenBadBidi(builder.addToken, order); }
-      builder.map = [];
-      var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
-      insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
-      if (line.styleClasses) {
-        if (line.styleClasses.bgClass)
-          { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); }
-        if (line.styleClasses.textClass)
-          { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); }
-      }
-
-      // Ensure at least a single node is present, for measuring.
-      if (builder.map.length == 0)
-        { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }
-
-      // Store the map and a cache object for the current logical line
-      if (i == 0) {
-        lineView.measure.map = builder.map;
-        lineView.measure.cache = {};
-      } else {
-  (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
-        ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});
-      }
-    }
-
-    // See issue #2901
-    if (webkit) {
-      var last = builder.content.lastChild;
-      if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
-        { builder.content.className = "cm-tab-wrap-hack"; }
-    }
-
-    signal(cm, "renderLine", cm, lineView.line, builder.pre);
-    if (builder.pre.className)
-      { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); }
-
-    return builder
-  }
-
-  function defaultSpecialCharPlaceholder(ch) {
-    var token = elt("span", "\u2022", "cm-invalidchar");
-    token.title = "\\u" + ch.charCodeAt(0).toString(16);
-    token.setAttribute("aria-label", token.title);
-    return token
-  }
-
-  // Build up the DOM representation for a single token, and add it to
-  // the line map. Takes care to render special characters separately.
-  function buildToken(builder, text, style, startStyle, endStyle, css, attributes) {
-    if (!text) { return }
-    var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;
-    var special = builder.cm.state.specialChars, mustWrap = false;
-    var content;
-    if (!special.test(text)) {
-      builder.col += text.length;
-      content = document.createTextNode(displayText);
-      builder.map.push(builder.pos, builder.pos + text.length, content);
-      if (ie && ie_version < 9) { mustWrap = true; }
-      builder.pos += text.length;
-    } else {
-      content = document.createDocumentFragment();
-      var pos = 0;
-      while (true) {
-        special.lastIndex = pos;
-        var m = special.exec(text);
-        var skipped = m ? m.index - pos : text.length - pos;
-        if (skipped) {
-          var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
-          if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); }
-          else { content.appendChild(txt); }
-          builder.map.push(builder.pos, builder.pos + skipped, txt);
-          builder.col += skipped;
-          builder.pos += skipped;
-        }
-        if (!m) { break }
-        pos += skipped + 1;
-        var txt$1 = (void 0);
-        if (m[0] == "\t") {
-          var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
-          txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
-          txt$1.setAttribute("role", "presentation");
-          txt$1.setAttribute("cm-text", "\t");
-          builder.col += tabWidth;
-        } else if (m[0] == "\r" || m[0] == "\n") {
-          txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
-          txt$1.setAttribute("cm-text", m[0]);
-          builder.col += 1;
-        } else {
-          txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);
-          txt$1.setAttribute("cm-text", m[0]);
-          if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); }
-          else { content.appendChild(txt$1); }
-          builder.col += 1;
-        }
-        builder.map.push(builder.pos, builder.pos + 1, txt$1);
-        builder.pos++;
-      }
-    }
-    builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;
-    if (style || startStyle || endStyle || mustWrap || css) {
-      var fullStyle = style || "";
-      if (startStyle) { fullStyle += startStyle; }
-      if (endStyle) { fullStyle += endStyle; }
-      var token = elt("span", [content], fullStyle, css);
-      if (attributes) {
-        for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class")
-          { token.setAttribute(attr, attributes[attr]); } }
-      }
-      return builder.content.appendChild(token)
-    }
-    builder.content.appendChild(content);
-  }
-
-  // Change some spaces to NBSP to prevent the browser from collapsing
-  // trailing spaces at the end of a line when rendering text (issue #1362).
-  function splitSpaces(text, trailingBefore) {
-    if (text.length > 1 && !/  /.test(text)) { return text }
-    var spaceBefore = trailingBefore, result = "";
-    for (var i = 0; i < text.length; i++) {
-      var ch = text.charAt(i);
-      if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
-        { ch = "\u00a0"; }
-      result += ch;
-      spaceBefore = ch == " ";
-    }
-    return result
-  }
-
-  // Work around nonsense dimensions being reported for stretches of
-  // right-to-left text.
-  function buildTokenBadBidi(inner, order) {
-    return function (builder, text, style, startStyle, endStyle, css, attributes) {
-      style = style ? style + " cm-force-border" : "cm-force-border";
-      var start = builder.pos, end = start + text.length;
-      for (;;) {
-        // Find the part that overlaps with the start of this text
-        var part = (void 0);
-        for (var i = 0; i < order.length; i++) {
-          part = order[i];
-          if (part.to > start && part.from <= start) { break }
-        }
-        if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) }
-        inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes);
-        startStyle = null;
-        text = text.slice(part.to - start);
-        start = part.to;
-      }
-    }
-  }
-
-  function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
-    var widget = !ignoreWidget && marker.widgetNode;
-    if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }
-    if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
-      if (!widget)
-        { widget = builder.content.appendChild(document.createElement("span")); }
-      widget.setAttribute("cm-marker", marker.id);
-    }
-    if (widget) {
-      builder.cm.display.input.setUneditable(widget);
-      builder.content.appendChild(widget);
-    }
-    builder.pos += size;
-    builder.trailingSpace = false;
-  }
-
-  // Outputs a number of spans to make up a line, taking highlighting
-  // and marked text into account.
-  function insertLineContent(line, builder, styles) {
-    var spans = line.markedSpans, allText = line.text, at = 0;
-    if (!spans) {
-      for (var i$1 = 1; i$1 < styles.length; i$1+=2)
-        { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }
-      return
-    }
-
-    var len = allText.length, pos = 0, i = 1, text = "", style, css;
-    var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes;
-    for (;;) {
-      if (nextChange == pos) { // Update current marker set
-        spanStyle = spanEndStyle = spanStartStyle = css = "";
-        attributes = null;
-        collapsed = null; nextChange = Infinity;
-        var foundBookmarks = [], endStyles = (void 0);
-        for (var j = 0; j < spans.length; ++j) {
-          var sp = spans[j], m = sp.marker;
-          if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
-            foundBookmarks.push(m);
-          } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
-            if (sp.to != null && sp.to != pos && nextChange > sp.to) {
-              nextChange = sp.to;
-              spanEndStyle = "";
-            }
-            if (m.className) { spanStyle += " " + m.className; }
-            if (m.css) { css = (css ? css + ";" : "") + m.css; }
-            if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; }
-            if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }
-            // support for the old title property
-            // https://github.com/codemirror/CodeMirror/pull/5673
-            if (m.title) { (attributes || (attributes = {})).title = m.title; }
-            if (m.attributes) {
-              for (var attr in m.attributes)
-                { (attributes || (attributes = {}))[attr] = m.attributes[attr]; }
-            }
-            if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
-              { collapsed = sp; }
-          } else if (sp.from > pos && nextChange > sp.from) {
-            nextChange = sp.from;
-          }
-        }
-        if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
-          { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } }
-
-        if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
-          { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }
-        if (collapsed && (collapsed.from || 0) == pos) {
-          buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
-                             collapsed.marker, collapsed.from == null);
-          if (collapsed.to == null) { return }
-          if (collapsed.to == pos) { collapsed = false; }
-        }
-      }
-      if (pos >= len) { break }
-
-      var upto = Math.min(len, nextChange);
-      while (true) {
-        if (text) {
-          var end = pos + text.length;
-          if (!collapsed) {
-            var tokenText = end > upto ? text.slice(0, upto - pos) : text;
-            builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
-                             spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes);
-          }
-          if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
-          pos = end;
-          spanStartStyle = "";
-        }
-        text = allText.slice(at, at = styles[i++]);
-        style = interpretTokenStyle(styles[i++], builder.cm.options);
-      }
-    }
-  }
-
-
-  // These objects are used to represent the visible (currently drawn)
-  // part of the document. A LineView may correspond to multiple
-  // logical lines, if those are connected by collapsed ranges.
-  function LineView(doc, line, lineN) {
-    // The starting line
-    this.line = line;
-    // Continuing lines, if any
-    this.rest = visualLineContinued(line);
-    // Number of logical lines in this visual line
-    this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
-    this.node = this.text = null;
-    this.hidden = lineIsHidden(doc, line);
-  }
-
-  // Create a range of LineView objects for the given lines.
-  function buildViewArray(cm, from, to) {
-    var array = [], nextPos;
-    for (var pos = from; pos < to; pos = nextPos) {
-      var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
-      nextPos = pos + view.size;
-      array.push(view);
-    }
-    return array
-  }
-
-  var operationGroup = null;
-
-  function pushOperation(op) {
-    if (operationGroup) {
-      operationGroup.ops.push(op);
-    } else {
-      op.ownsGroup = operationGroup = {
-        ops: [op],
-        delayedCallbacks: []
-      };
-    }
-  }
-
-  function fireCallbacksForOps(group) {
-    // Calls delayed callbacks and cursorActivity handlers until no
-    // new ones appear
-    var callbacks = group.delayedCallbacks, i = 0;
-    do {
-      for (; i < callbacks.length; i++)
-        { callbacks[i].call(null); }
-      for (var j = 0; j < group.ops.length; j++) {
-        var op = group.ops[j];
-        if (op.cursorActivityHandlers)
-          { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
-            { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }
-      }
-    } while (i < callbacks.length)
-  }
-
-  function finishOperation(op, endCb) {
-    var group = op.ownsGroup;
-    if (!group) { return }
-
-    try { fireCallbacksForOps(group); }
-    finally {
-      operationGroup = null;
-      endCb(group);
-    }
-  }
-
-  var orphanDelayedCallbacks = null;
-
-  // Often, we want to signal events at a point where we are in the
-  // middle of some work, but don't want the handler to start calling
-  // other methods on the editor, which might be in an inconsistent
-  // state or simply not expect any other events to happen.
-  // signalLater looks whether there are any handlers, and schedules
-  // them to be executed when the last operation ends, or, if no
-  // operation is active, when a timeout fires.
-  function signalLater(emitter, type /*, values...*/) {
-    var arr = getHandlers(emitter, type);
-    if (!arr.length) { return }
-    var args = Array.prototype.slice.call(arguments, 2), list;
-    if (operationGroup) {
-      list = operationGroup.delayedCallbacks;
-    } else if (orphanDelayedCallbacks) {
-      list = orphanDelayedCallbacks;
-    } else {
-      list = orphanDelayedCallbacks = [];
-      setTimeout(fireOrphanDelayed, 0);
-    }
-    var loop = function ( i ) {
-      list.push(function () { return arr[i].apply(null, args); });
-    };
-
-    for (var i = 0; i < arr.length; ++i)
-      loop( i );
-  }
-
-  function fireOrphanDelayed() {
-    var delayed = orphanDelayedCallbacks;
-    orphanDelayedCallbacks = null;
-    for (var i = 0; i < delayed.length; ++i) { delayed[i](); }
-  }
-
-  // When an aspect of a line changes, a string is added to
-  // lineView.changes. This updates the relevant part of the line's
-  // DOM structure.
-  function updateLineForChanges(cm, lineView, lineN, dims) {
-    for (var j = 0; j < lineView.changes.length; j++) {
-      var type = lineView.changes[j];
-      if (type == "text") { updateLineText(cm, lineView); }
-      else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); }
-      else if (type == "class") { updateLineClasses(cm, lineView); }
-      else if (type == "widget") { updateLineWidgets(cm, lineView, dims); }
-    }
-    lineView.changes = null;
-  }
-
-  // Lines with gutter elements, widgets or a background class need to
-  // be wrapped, and have the extra elements added to the wrapper div
-  function ensureLineWrapped(lineView) {
-    if (lineView.node == lineView.text) {
-      lineView.node = elt("div", null, null, "position: relative");
-      if (lineView.text.parentNode)
-        { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }
-      lineView.node.appendChild(lineView.text);
-      if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }
-    }
-    return lineView.node
-  }
-
-  function updateLineBackground(cm, lineView) {
-    var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
-    if (cls) { cls += " CodeMirror-linebackground"; }
-    if (lineView.background) {
-      if (cls) { lineView.background.className = cls; }
-      else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
-    } else if (cls) {
-      var wrap = ensureLineWrapped(lineView);
-      lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
-      cm.display.input.setUneditable(lineView.background);
-    }
-  }
-
-  // Wrapper around buildLineContent which will reuse the structure
-  // in display.externalMeasured when possible.
-  function getLineContent(cm, lineView) {
-    var ext = cm.display.externalMeasured;
-    if (ext && ext.line == lineView.line) {
-      cm.display.externalMeasured = null;
-      lineView.measure = ext.measure;
-      return ext.built
-    }
-    return buildLineContent(cm, lineView)
-  }
-
-  // Redraw the line's text. Interacts with the background and text
-  // classes because the mode may output tokens that influence these
-  // classes.
-  function updateLineText(cm, lineView) {
-    var cls = lineView.text.className;
-    var built = getLineContent(cm, lineView);
-    if (lineView.text == lineView.node) { lineView.node = built.pre; }
-    lineView.text.parentNode.replaceChild(built.pre, lineView.text);
-    lineView.text = built.pre;
-    if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
-      lineView.bgClass = built.bgClass;
-      lineView.textClass = built.textClass;
-      updateLineClasses(cm, lineView);
-    } else if (cls) {
-      lineView.text.className = cls;
-    }
-  }
-
-  function updateLineClasses(cm, lineView) {
-    updateLineBackground(cm, lineView);
-    if (lineView.line.wrapClass)
-      { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }
-    else if (lineView.node != lineView.text)
-      { lineView.node.className = ""; }
-    var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
-    lineView.text.className = textClass || "";
-  }
-
-  function updateLineGutter(cm, lineView, lineN, dims) {
-    if (lineView.gutter) {
-      lineView.node.removeChild(lineView.gutter);
-      lineView.gutter = null;
-    }
-    if (lineView.gutterBackground) {
-      lineView.node.removeChild(lineView.gutterBackground);
-      lineView.gutterBackground = null;
-    }
-    if (lineView.line.gutterClass) {
-      var wrap = ensureLineWrapped(lineView);
-      lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
-                                      ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"));
-      cm.display.input.setUneditable(lineView.gutterBackground);
-      wrap.insertBefore(lineView.gutterBackground, lineView.text);
-    }
-    var markers = lineView.line.gutterMarkers;
-    if (cm.options.lineNumbers || markers) {
-      var wrap$1 = ensureLineWrapped(lineView);
-      var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
-      cm.display.input.setUneditable(gutterWrap);
-      wrap$1.insertBefore(gutterWrap, lineView.text);
-      if (lineView.line.gutterClass)
-        { gutterWrap.className += " " + lineView.line.gutterClass; }
-      if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
-        { lineView.lineNumber = gutterWrap.appendChild(
-          elt("div", lineNumberFor(cm.options, lineN),
-              "CodeMirror-linenumber CodeMirror-gutter-elt",
-              ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); }
-      if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) {
-        var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id];
-        if (found)
-          { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
-                                     ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); }
-      } }
-    }
-  }
-
-  function updateLineWidgets(cm, lineView, dims) {
-    if (lineView.alignable) { lineView.alignable = null; }
-    var isWidget = classTest("CodeMirror-linewidget");
-    for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
-      next = node.nextSibling;
-      if (isWidget.test(node.className)) { lineView.node.removeChild(node); }
-    }
-    insertLineWidgets(cm, lineView, dims);
-  }
-
-  // Build a line's DOM representation from scratch
-  function buildLineElement(cm, lineView, lineN, dims) {
-    var built = getLineContent(cm, lineView);
-    lineView.text = lineView.node = built.pre;
-    if (built.bgClass) { lineView.bgClass = built.bgClass; }
-    if (built.textClass) { lineView.textClass = built.textClass; }
-
-    updateLineClasses(cm, lineView);
-    updateLineGutter(cm, lineView, lineN, dims);
-    insertLineWidgets(cm, lineView, dims);
-    return lineView.node
-  }
-
-  // A lineView may contain multiple logical lines (when merged by
-  // collapsed spans). The widgets for all of them need to be drawn.
-  function insertLineWidgets(cm, lineView, dims) {
-    insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
-    if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
-      { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }
-  }
-
-  function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
-    if (!line.widgets) { return }
-    var wrap = ensureLineWrapped(lineView);
-    for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
-      var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : ""));
-      if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); }
-      positionLineWidget(widget, node, lineView, dims);
-      cm.display.input.setUneditable(node);
-      if (allowAbove && widget.above)
-        { wrap.insertBefore(node, lineView.gutter || lineView.text); }
-      else
-        { wrap.appendChild(node); }
-      signalLater(widget, "redraw");
-    }
-  }
-
-  function positionLineWidget(widget, node, lineView, dims) {
-    if (widget.noHScroll) {
-  (lineView.alignable || (lineView.alignable = [])).push(node);
-      var width = dims.wrapperWidth;
-      node.style.left = dims.fixedPos + "px";
-      if (!widget.coverGutter) {
-        width -= dims.gutterTotalWidth;
-        node.style.paddingLeft = dims.gutterTotalWidth + "px";
-      }
-      node.style.width = width + "px";
-    }
-    if (widget.coverGutter) {
-      node.style.zIndex = 5;
-      node.style.position = "relative";
-      if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; }
-    }
-  }
-
-  function widgetHeight(widget) {
-    if (widget.height != null) { return widget.height }
-    var cm = widget.doc.cm;
-    if (!cm) { return 0 }
-    if (!contains(document.body, widget.node)) {
-      var parentStyle = "position: relative;";
-      if (widget.coverGutter)
-        { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; }
-      if (widget.noHScroll)
-        { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; }
-      removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
-    }
-    return widget.height = widget.node.parentNode.offsetHeight
-  }
-
-  // Return true when the given mouse event happened in a widget
-  function eventInWidget(display, e) {
-    for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
-      if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
-          (n.parentNode == display.sizer && n != display.mover))
-        { return true }
-    }
-  }
-
-  // POSITION MEASUREMENT
-
-  function paddingTop(display) {return display.lineSpace.offsetTop}
-  function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
-  function paddingH(display) {
-    if (display.cachedPaddingH) { return display.cachedPaddingH }
-    var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like"));
-    var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
-    var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
-    if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }
-    return data
-  }
-
-  function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
-  function displayWidth(cm) {
-    return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
-  }
-  function displayHeight(cm) {
-    return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
-  }
-
-  // Ensure the lineView.wrapping.heights array is populated. This is
-  // an array of bottom offsets for the lines that make up a drawn
-  // line. When lineWrapping is on, there might be more than one
-  // height.
-  function ensureLineHeights(cm, lineView, rect) {
-    var wrapping = cm.options.lineWrapping;
-    var curWidth = wrapping && displayWidth(cm);
-    if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
-      var heights = lineView.measure.heights = [];
-      if (wrapping) {
-        lineView.measure.width = curWidth;
-        var rects = lineView.text.firstChild.getClientRects();
-        for (var i = 0; i < rects.length - 1; i++) {
-          var cur = rects[i], next = rects[i + 1];
-          if (Math.abs(cur.bottom - next.bottom) > 2)
-            { heights.push((cur.bottom + next.top) / 2 - rect.top); }
-        }
-      }
-      heights.push(rect.bottom - rect.top);
-    }
-  }
-
-  // Find a line map (mapping character offsets to text nodes) and a
-  // measurement cache for the given line number. (A line view might
-  // contain multiple lines when collapsed ranges are present.)
-  function mapFromLineView(lineView, line, lineN) {
-    if (lineView.line == line)
-      { return {map: lineView.measure.map, cache: lineView.measure.cache} }
-    for (var i = 0; i < lineView.rest.length; i++)
-      { if (lineView.rest[i] == line)
-        { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
-    for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
-      { if (lineNo(lineView.rest[i$1]) > lineN)
-        { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
-  }
-
-  // Render a line into the hidden node display.externalMeasured. Used
-  // when measurement is needed for a line that's not in the viewport.
-  function updateExternalMeasurement(cm, line) {
-    line = visualLine(line);
-    var lineN = lineNo(line);
-    var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
-    view.lineN = lineN;
-    var built = view.built = buildLineContent(cm, view);
-    view.text = built.pre;
-    removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
-    return view
-  }
-
-  // Get a {top, bottom, left, right} box (in line-local coordinates)
-  // for a given character.
-  function measureChar(cm, line, ch, bias) {
-    return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
-  }
-
-  // Find a line view that corresponds to the given line number.
-  function findViewForLine(cm, lineN) {
-    if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
-      { return cm.display.view[findViewIndex(cm, lineN)] }
-    var ext = cm.display.externalMeasured;
-    if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
-      { return ext }
-  }
-
-  // Measurement can be split in two steps, the set-up work that
-  // applies to the whole line, and the measurement of the actual
-  // character. Functions like coordsChar, that need to do a lot of
-  // measurements in a row, can thus ensure that the set-up work is
-  // only done once.
-  function prepareMeasureForLine(cm, line) {
-    var lineN = lineNo(line);
-    var view = findViewForLine(cm, lineN);
-    if (view && !view.text) {
-      view = null;
-    } else if (view && view.changes) {
-      updateLineForChanges(cm, view, lineN, getDimensions(cm));
-      cm.curOp.forceUpdate = true;
-    }
-    if (!view)
-      { view = updateExternalMeasurement(cm, line); }
-
-    var info = mapFromLineView(view, line, lineN);
-    return {
-      line: line, view: view, rect: null,
-      map: info.map, cache: info.cache, before: info.before,
-      hasHeights: false
-    }
-  }
-
-  // Given a prepared measurement object, measures the position of an
-  // actual character (or fetches it from the cache).
-  function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
-    if (prepared.before) { ch = -1; }
-    var key = ch + (bias || ""), found;
-    if (prepared.cache.hasOwnProperty(key)) {
-      found = prepared.cache[key];
-    } else {
-      if (!prepared.rect)
-        { prepared.rect = prepared.view.text.getBoundingClientRect(); }
-      if (!prepared.hasHeights) {
-        ensureLineHeights(cm, prepared.view, prepared.rect);
-        prepared.hasHeights = true;
-      }
-      found = measureCharInner(cm, prepared, ch, bias);
-      if (!found.bogus) { prepared.cache[key] = found; }
-    }
-    return {left: found.left, right: found.right,
-            top: varHeight ? found.rtop : found.top,
-            bottom: varHeight ? found.rbottom : found.bottom}
-  }
-
-  var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
-
-  function nodeAndOffsetInLineMap(map, ch, bias) {
-    var node, start, end, collapse, mStart, mEnd;
-    // First, search the line map for the text node corresponding to,
-    // or closest to, the target character.
-    for (var i = 0; i < map.length; i += 3) {
-      mStart = map[i];
-      mEnd = map[i + 1];
-      if (ch < mStart) {
-        start = 0; end = 1;
-        collapse = "left";
-      } else if (ch < mEnd) {
-        start = ch - mStart;
-        end = start + 1;
-      } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
-        end = mEnd - mStart;
-        start = end - 1;
-        if (ch >= mEnd) { collapse = "right"; }
-      }
-      if (start != null) {
-        node = map[i + 2];
-        if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
-          { collapse = bias; }
-        if (bias == "left" && start == 0)
-          { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
-            node = map[(i -= 3) + 2];
-            collapse = "left";
-          } }
-        if (bias == "right" && start == mEnd - mStart)
-          { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
-            node = map[(i += 3) + 2];
-            collapse = "right";
-          } }
-        break
-      }
-    }
-    return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
-  }
-
-  function getUsefulRect(rects, bias) {
-    var rect = nullRect;
-    if (bias == "left") { for (var i = 0; i < rects.length; i++) {
-      if ((rect = rects[i]).left != rect.right) { break }
-    } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
-      if ((rect = rects[i$1]).left != rect.right) { break }
-    } }
-    return rect
-  }
-
-  function measureCharInner(cm, prepared, ch, bias) {
-    var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
-    var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
-
-    var rect;
-    if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
-      for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
-        while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }
-        while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }
-        if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
-          { rect = node.parentNode.getBoundingClientRect(); }
-        else
-          { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }
-        if (rect.left || rect.right || start == 0) { break }
-        end = start;
-        start = start - 1;
-        collapse = "right";
-      }
-      if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }
-    } else { // If it is a widget, simply get the box for the whole widget.
-      if (start > 0) { collapse = bias = "right"; }
-      var rects;
-      if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
-        { rect = rects[bias == "right" ? rects.length - 1 : 0]; }
-      else
-        { rect = node.getBoundingClientRect(); }
-    }
-    if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
-      var rSpan = node.parentNode.getClientRects()[0];
-      if (rSpan)
-        { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }
-      else
-        { rect = nullRect; }
-    }
-
-    var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
-    var mid = (rtop + rbot) / 2;
-    var heights = prepared.view.measure.heights;
-    var i = 0;
-    for (; i < heights.length - 1; i++)
-      { if (mid < heights[i]) { break } }
-    var top = i ? heights[i - 1] : 0, bot = heights[i];
-    var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
-                  right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
-                  top: top, bottom: bot};
-    if (!rect.left && !rect.right) { result.bogus = true; }
-    if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
-
-    return result
-  }
-
-  // Work around problem with bounding client rects on ranges being
-  // returned incorrectly when zoomed on IE10 and below.
-  function maybeUpdateRectForZooming(measure, rect) {
-    if (!window.screen || screen.logicalXDPI == null ||
-        screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
-      { return rect }
-    var scaleX = screen.logicalXDPI / screen.deviceXDPI;
-    var scaleY = screen.logicalYDPI / screen.deviceYDPI;
-    return {left: rect.left * scaleX, right: rect.right * scaleX,
-            top: rect.top * scaleY, bottom: rect.bottom * scaleY}
-  }
-
-  function clearLineMeasurementCacheFor(lineView) {
-    if (lineView.measure) {
-      lineView.measure.cache = {};
-      lineView.measure.heights = null;
-      if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
-        { lineView.measure.caches[i] = {}; } }
-    }
-  }
-
-  function clearLineMeasurementCache(cm) {
-    cm.display.externalMeasure = null;
-    removeChildren(cm.display.lineMeasure);
-    for (var i = 0; i < cm.display.view.length; i++)
-      { clearLineMeasurementCacheFor(cm.display.view[i]); }
-  }
-
-  function clearCaches(cm) {
-    clearLineMeasurementCache(cm);
-    cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
-    if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }
-    cm.display.lineNumChars = null;
-  }
-
-  function pageScrollX() {
-    // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
-    // which causes page_Offset and bounding client rects to use
-    // different reference viewports and invalidate our calculations.
-    if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
-    return window.pageXOffset || (document.documentElement || document.body).scrollLeft
-  }
-  function pageScrollY() {
-    if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
-    return window.pageYOffset || (document.documentElement || document.body).scrollTop
-  }
-
-  function widgetTopHeight(lineObj) {
-    var height = 0;
-    if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
-      { height += widgetHeight(lineObj.widgets[i]); } } }
-    return height
-  }
-
-  // Converts a {top, bottom, left, right} box from line-local
-  // coordinates into another coordinate system. Context may be one of
-  // "line", "div" (display.lineDiv), "local"./null (editor), "window",
-  // or "page".
-  function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
-    if (!includeWidgets) {
-      var height = widgetTopHeight(lineObj);
-      rect.top += height; rect.bottom += height;
-    }
-    if (context == "line") { return rect }
-    if (!context) { context = "local"; }
-    var yOff = heightAtLine(lineObj);
-    if (context == "local") { yOff += paddingTop(cm.display); }
-    else { yOff -= cm.display.viewOffset; }
-    if (context == "page" || context == "window") {
-      var lOff = cm.display.lineSpace.getBoundingClientRect();
-      yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
-      var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
-      rect.left += xOff; rect.right += xOff;
-    }
-    rect.top += yOff; rect.bottom += yOff;
-    return rect
-  }
-
-  // Coverts a box from "div" coords to another coordinate system.
-  // Context may be "window", "page", "div", or "local"./null.
-  function fromCoordSystem(cm, coords, context) {
-    if (context == "div") { return coords }
-    var left = coords.left, top = coords.top;
-    // First move into "page" coordinate system
-    if (context == "page") {
-      left -= pageScrollX();
-      top -= pageScrollY();
-    } else if (context == "local" || !context) {
-      var localBox = cm.display.sizer.getBoundingClientRect();
-      left += localBox.left;
-      top += localBox.top;
-    }
-
-    var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
-    return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
-  }
-
-  function charCoords(cm, pos, context, lineObj, bias) {
-    if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }
-    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
-  }
-
-  // Returns a box for a given cursor position, which may have an
-  // 'other' property containing the position of the secondary cursor
-  // on a bidi boundary.
-  // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
-  // and after `char - 1` in writing order of `char - 1`
-  // A cursor Pos(line, char, "after") is on the same visual line as `char`
-  // and before `char` in writing order of `char`
-  // Examples (upper-case letters are RTL, lower-case are LTR):
-  //     Pos(0, 1, ...)
-  //     before   after
-  // ab     a|b     a|b
-  // aB     a|B     aB|
-  // Ab     |Ab     A|b
-  // AB     B|A     B|A
-  // Every position after the last character on a line is considered to stick
-  // to the last character on the line.
-  function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
-    lineObj = lineObj || getLine(cm.doc, pos.line);
-    if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
-    function get(ch, right) {
-      var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
-      if (right) { m.left = m.right; } else { m.right = m.left; }
-      return intoCoordSystem(cm, lineObj, m, context)
-    }
-    var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;
-    if (ch >= lineObj.text.length) {
-      ch = lineObj.text.length;
-      sticky = "before";
-    } else if (ch <= 0) {
-      ch = 0;
-      sticky = "after";
-    }
-    if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
-
-    function getBidi(ch, partPos, invert) {
-      var part = order[partPos], right = part.level == 1;
-      return get(invert ? ch - 1 : ch, right != invert)
-    }
-    var partPos = getBidiPartAt(order, ch, sticky);
-    var other = bidiOther;
-    var val = getBidi(ch, partPos, sticky == "before");
-    if (other != null) { val.other = getBidi(ch, other, sticky != "before"); }
-    return val
-  }
-
-  // Used to cheaply estimate the coordinates for a position. Used for
-  // intermediate scroll updates.
-  function estimateCoords(cm, pos) {
-    var left = 0;
-    pos = clipPos(cm.doc, pos);
-    if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }
-    var lineObj = getLine(cm.doc, pos.line);
-    var top = heightAtLine(lineObj) + paddingTop(cm.display);
-    return {left: left, right: left, top: top, bottom: top + lineObj.height}
-  }
-
-  // Positions returned by coordsChar contain some extra information.
-  // xRel is the relative x position of the input coordinates compared
-  // to the found position (so xRel > 0 means the coordinates are to
-  // the right of the character position, for example). When outside
-  // is true, that means the coordinates lie outside the line's
-  // vertical range.
-  function PosWithInfo(line, ch, sticky, outside, xRel) {
-    var pos = Pos(line, ch, sticky);
-    pos.xRel = xRel;
-    if (outside) { pos.outside = outside; }
-    return pos
-  }
-
-  // Compute the character position closest to the given coordinates.
-  // Input must be lineSpace-local ("div" coordinate system).
-  function coordsChar(cm, x, y) {
-    var doc = cm.doc;
-    y += cm.display.viewOffset;
-    if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) }
-    var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
-    if (lineN > last)
-      { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) }
-    if (x < 0) { x = 0; }
-
-    var lineObj = getLine(doc, lineN);
-    for (;;) {
-      var found = coordsCharInner(cm, lineObj, lineN, x, y);
-      var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0));
-      if (!collapsed) { return found }
-      var rangeEnd = collapsed.find(1);
-      if (rangeEnd.line == lineN) { return rangeEnd }
-      lineObj = getLine(doc, lineN = rangeEnd.line);
-    }
-  }
-
-  function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
-    y -= widgetTopHeight(lineObj);
-    var end = lineObj.text.length;
-    var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);
-    end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);
-    return {begin: begin, end: end}
-  }
-
-  function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
-    if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
-    var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top;
-    return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
-  }
-
-  // Returns true if the given side of a box is after the given
-  // coordinates, in top-to-bottom, left-to-right order.
-  function boxIsAfter(box, x, y, left) {
-    return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
-  }
-
-  function coordsCharInner(cm, lineObj, lineNo, x, y) {
-    // Move y into line-local coordinate space
-    y -= heightAtLine(lineObj);
-    var preparedMeasure = prepareMeasureForLine(cm, lineObj);
-    // When directly calling `measureCharPrepared`, we have to adjust
-    // for the widgets at this line.
-    var widgetHeight = widgetTopHeight(lineObj);
-    var begin = 0, end = lineObj.text.length, ltr = true;
-
-    var order = getOrder(lineObj, cm.doc.direction);
-    // If the line isn't plain left-to-right text, first figure out
-    // which bidi section the coordinates fall into.
-    if (order) {
-      var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
-                   (cm, lineObj, lineNo, preparedMeasure, order, x, y);
-      ltr = part.level != 1;
-      // The awkward -1 offsets are needed because findFirst (called
-      // on these below) will treat its first bound as inclusive,
-      // second as exclusive, but we want to actually address the
-      // characters in the part's range
-      begin = ltr ? part.from : part.to - 1;
-      end = ltr ? part.to : part.from - 1;
-    }
-
-    // A binary search to find the first character whose bounding box
-    // starts after the coordinates. If we run across any whose box wrap
-    // the coordinates, store that.
-    var chAround = null, boxAround = null;
-    var ch = findFirst(function (ch) {
-      var box = measureCharPrepared(cm, preparedMeasure, ch);
-      box.top += widgetHeight; box.bottom += widgetHeight;
-      if (!boxIsAfter(box, x, y, false)) { return false }
-      if (box.top <= y && box.left <= x) {
-        chAround = ch;
-        boxAround = box;
-      }
-      return true
-    }, begin, end);
-
-    var baseX, sticky, outside = false;
-    // If a box around the coordinates was found, use that
-    if (boxAround) {
-      // Distinguish coordinates nearer to the left or right side of the box
-      var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;
-      ch = chAround + (atStart ? 0 : 1);
-      sticky = atStart ? "after" : "before";
-      baseX = atLeft ? boxAround.left : boxAround.right;
-    } else {
-      // (Adjust for extended bound, if necessary.)
-      if (!ltr && (ch == end || ch == begin)) { ch++; }
-      // To determine which side to associate with, get the box to the
-      // left of the character and compare it's vertical position to the
-      // coordinates
-      sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
-        (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ?
-        "after" : "before";
-      // Now get accurate coordinates for this place, in order to get a
-      // base X position
-      var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure);
-      baseX = coords.left;
-      outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0;
-    }
-
-    ch = skipExtendingChars(lineObj.text, ch, 1);
-    return PosWithInfo(lineNo, ch, sticky, outside, x - baseX)
-  }
-
-  function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) {
-    // Bidi parts are sorted left-to-right, and in a non-line-wrapping
-    // situation, we can take this ordering to correspond to the visual
-    // ordering. This finds the first part whose end is after the given
-    // coordinates.
-    var index = findFirst(function (i) {
-      var part = order[i], ltr = part.level != 1;
-      return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"),
-                                     "line", lineObj, preparedMeasure), x, y, true)
-    }, 0, order.length - 1);
-    var part = order[index];
-    // If this isn't the first part, the part's start is also after
-    // the coordinates, and the coordinates aren't on the same line as
-    // that start, move one part back.
-    if (index > 0) {
-      var ltr = part.level != 1;
-      var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"),
-                               "line", lineObj, preparedMeasure);
-      if (boxIsAfter(start, x, y, true) && start.top > y)
-        { part = order[index - 1]; }
-    }
-    return part
-  }
-
-  function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
-    // In a wrapped line, rtl text on wrapping boundaries can do things
-    // that don't correspond to the ordering in our `order` array at
-    // all, so a binary search doesn't work, and we want to return a
-    // part that only spans one line so that the binary search in
-    // coordsCharInner is safe. As such, we first find the extent of the
-    // wrapped line, and then do a flat search in which we discard any
-    // spans that aren't on the line.
-    var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
-    var begin = ref.begin;
-    var end = ref.end;
-    if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; }
-    var part = null, closestDist = null;
-    for (var i = 0; i < order.length; i++) {
-      var p = order[i];
-      if (p.from >= end || p.to <= begin) { continue }
-      var ltr = p.level != 1;
-      var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;
-      // Weigh against spans ending before this, so that they are only
-      // picked if nothing ends after
-      var dist = endX < x ? x - endX + 1e9 : endX - x;
-      if (!part || closestDist > dist) {
-        part = p;
-        closestDist = dist;
-      }
-    }
-    if (!part) { part = order[order.length - 1]; }
-    // Clip the part to the wrapped line.
-    if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }
-    if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }
-    return part
-  }
-
-  var measureText;
-  // Compute the default text height.
-  function textHeight(display) {
-    if (display.cachedTextHeight != null) { return display.cachedTextHeight }
-    if (measureText == null) {
-      measureText = elt("pre", null, "CodeMirror-line-like");
-      // Measure a bunch of lines, for browsers that compute
-      // fractional heights.
-      for (var i = 0; i < 49; ++i) {
-        measureText.appendChild(document.createTextNode("x"));
-        measureText.appendChild(elt("br"));
-      }
-      measureText.appendChild(document.createTextNode("x"));
-    }
-    removeChildrenAndAdd(display.measure, measureText);
-    var height = measureText.offsetHeight / 50;
-    if (height > 3) { display.cachedTextHeight = height; }
-    removeChildren(display.measure);
-    return height || 1
-  }
-
-  // Compute the default character width.
-  function charWidth(display) {
-    if (display.cachedCharWidth != null) { return display.cachedCharWidth }
-    var anchor = elt("span", "xxxxxxxxxx");
-    var pre = elt("pre", [anchor], "CodeMirror-line-like");
-    removeChildrenAndAdd(display.measure, pre);
-    var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
-    if (width > 2) { display.cachedCharWidth = width; }
-    return width || 10
-  }
-
-  // Do a bulk-read of the DOM positions and sizes needed to draw the
-  // view, so that we don't interleave reading and writing to the DOM.
-  function getDimensions(cm) {
-    var d = cm.display, left = {}, width = {};
-    var gutterLeft = d.gutters.clientLeft;
-    for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
-      var id = cm.display.gutterSpecs[i].className;
-      left[id] = n.offsetLeft + n.clientLeft + gutterLeft;
-      width[id] = n.clientWidth;
-    }
-    return {fixedPos: compensateForHScroll(d),
-            gutterTotalWidth: d.gutters.offsetWidth,
-            gutterLeft: left,
-            gutterWidth: width,
-            wrapperWidth: d.wrapper.clientWidth}
-  }
-
-  // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
-  // but using getBoundingClientRect to get a sub-pixel-accurate
-  // result.
-  function compensateForHScroll(display) {
-    return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
-  }
-
-  // Returns a function that estimates the height of a line, to use as
-  // first approximation until the line becomes visible (and is thus
-  // properly measurable).
-  function estimateHeight(cm) {
-    var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
-    var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
-    return function (line) {
-      if (lineIsHidden(cm.doc, line)) { return 0 }
-
-      var widgetsHeight = 0;
-      if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
-        if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }
-      } }
-
-      if (wrapping)
-        { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
-      else
-        { return widgetsHeight + th }
-    }
-  }
-
-  function estimateLineHeights(cm) {
-    var doc = cm.doc, est = estimateHeight(cm);
-    doc.iter(function (line) {
-      var estHeight = est(line);
-      if (estHeight != line.height) { updateLineHeight(line, estHeight); }
-    });
-  }
-
-  // Given a mouse event, find the corresponding position. If liberal
-  // is false, it checks whether a gutter or scrollbar was clicked,
-  // and returns null if it was. forRect is used by rectangular
-  // selections, and tries to estimate a character position even for
-  // coordinates beyond the right of the text.
-  function posFromMouse(cm, e, liberal, forRect) {
-    var display = cm.display;
-    if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
-
-    var x, y, space = display.lineSpace.getBoundingClientRect();
-    // Fails unpredictably on IE[67] when mouse is dragged around quickly.
-    try { x = e.clientX - space.left; y = e.clientY - space.top; }
-    catch (e$1) { return null }
-    var coords = coordsChar(cm, x, y), line;
-    if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
-      var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
-      coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
-    }
-    return coords
-  }
-
-  // Find the view element corresponding to a given line. Return null
-  // when the line isn't visible.
-  function findViewIndex(cm, n) {
-    if (n >= cm.display.viewTo) { return null }
-    n -= cm.display.viewFrom;
-    if (n < 0) { return null }
-    var view = cm.display.view;
-    for (var i = 0; i < view.length; i++) {
-      n -= view[i].size;
-      if (n < 0) { return i }
-    }
-  }
-
-  // Updates the display.view data structure for a given change to the
-  // document. From and to are in pre-change coordinates. Lendiff is
-  // the amount of lines added or subtracted by the change. This is
-  // used for changes that span multiple lines, or change the way
-  // lines are divided into visual lines. regLineChange (below)
-  // registers single-line changes.
-  function regChange(cm, from, to, lendiff) {
-    if (from == null) { from = cm.doc.first; }
-    if (to == null) { to = cm.doc.first + cm.doc.size; }
-    if (!lendiff) { lendiff = 0; }
-
-    var display = cm.display;
-    if (lendiff && to < display.viewTo &&
-        (display.updateLineNumbers == null || display.updateLineNumbers > from))
-      { display.updateLineNumbers = from; }
-
-    cm.curOp.viewChanged = true;
-
-    if (from >= display.viewTo) { // Change after
-      if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
-        { resetView(cm); }
-    } else if (to <= display.viewFrom) { // Change before
-      if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
-        resetView(cm);
-      } else {
-        display.viewFrom += lendiff;
-        display.viewTo += lendiff;
-      }
-    } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
-      resetView(cm);
-    } else if (from <= display.viewFrom) { // Top overlap
-      var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
-      if (cut) {
-        display.view = display.view.slice(cut.index);
-        display.viewFrom = cut.lineN;
-        display.viewTo += lendiff;
-      } else {
-        resetView(cm);
-      }
-    } else if (to >= display.viewTo) { // Bottom overlap
-      var cut$1 = viewCuttingPoint(cm, from, from, -1);
-      if (cut$1) {
-        display.view = display.view.slice(0, cut$1.index);
-        display.viewTo = cut$1.lineN;
-      } else {
-        resetView(cm);
-      }
-    } else { // Gap in the middle
-      var cutTop = viewCuttingPoint(cm, from, from, -1);
-      var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
-      if (cutTop && cutBot) {
-        display.view = display.view.slice(0, cutTop.index)
-          .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
-          .concat(display.view.slice(cutBot.index));
-        display.viewTo += lendiff;
-      } else {
-        resetView(cm);
-      }
-    }
-
-    var ext = display.externalMeasured;
-    if (ext) {
-      if (to < ext.lineN)
-        { ext.lineN += lendiff; }
-      else if (from < ext.lineN + ext.size)
-        { display.externalMeasured = null; }
-    }
-  }
-
-  // Register a change to a single line. Type must be one of "text",
-  // "gutter", "class", "widget"
-  function regLineChange(cm, line, type) {
-    cm.curOp.viewChanged = true;
-    var display = cm.display, ext = cm.display.externalMeasured;
-    if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
-      { display.externalMeasured = null; }
-
-    if (line < display.viewFrom || line >= display.viewTo) { return }
-    var lineView = display.view[findViewIndex(cm, line)];
-    if (lineView.node == null) { return }
-    var arr = lineView.changes || (lineView.changes = []);
-    if (indexOf(arr, type) == -1) { arr.push(type); }
-  }
-
-  // Clear the view.
-  function resetView(cm) {
-    cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
-    cm.display.view = [];
-    cm.display.viewOffset = 0;
-  }
-
-  function viewCuttingPoint(cm, oldN, newN, dir) {
-    var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
-    if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
-      { return {index: index, lineN: newN} }
-    var n = cm.display.viewFrom;
-    for (var i = 0; i < index; i++)
-      { n += view[i].size; }
-    if (n != oldN) {
-      if (dir > 0) {
-        if (index == view.length - 1) { return null }
-        diff = (n + view[index].size) - oldN;
-        index++;
-      } else {
-        diff = n - oldN;
-      }
-      oldN += diff; newN += diff;
-    }
-    while (visualLineNo(cm.doc, newN) != newN) {
-      if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
-      newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
-      index += dir;
-    }
-    return {index: index, lineN: newN}
-  }
-
-  // Force the view to cover a given range, adding empty view element
-  // or clipping off existing ones as needed.
-  function adjustView(cm, from, to) {
-    var display = cm.display, view = display.view;
-    if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
-      display.view = buildViewArray(cm, from, to);
-      display.viewFrom = from;
-    } else {
-      if (display.viewFrom > from)
-        { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }
-      else if (display.viewFrom < from)
-        { display.view = display.view.slice(findViewIndex(cm, from)); }
-      display.viewFrom = from;
-      if (display.viewTo < to)
-        { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }
-      else if (display.viewTo > to)
-        { display.view = display.view.slice(0, findViewIndex(cm, to)); }
-    }
-    display.viewTo = to;
-  }
-
-  // Count the number of lines in the view whose DOM representation is
-  // out of date (or nonexistent).
-  function countDirtyView(cm) {
-    var view = cm.display.view, dirty = 0;
-    for (var i = 0; i < view.length; i++) {
-      var lineView = view[i];
-      if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }
-    }
-    return dirty
-  }
-
-  function updateSelection(cm) {
-    cm.display.input.showSelection(cm.display.input.prepareSelection());
-  }
-
-  function prepareSelection(cm, primary) {
-    if ( primary === void 0 ) primary = true;
-
-    var doc = cm.doc, result = {};
-    var curFragment = result.cursors = document.createDocumentFragment();
-    var selFragment = result.selection = document.createDocumentFragment();
-
-    for (var i = 0; i < doc.sel.ranges.length; i++) {
-      if (!primary && i == doc.sel.primIndex) { continue }
-      var range = doc.sel.ranges[i];
-      if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue }
-      var collapsed = range.empty();
-      if (collapsed || cm.options.showCursorWhenSelecting)
-        { drawSelectionCursor(cm, range.head, curFragment); }
-      if (!collapsed)
-        { drawSelectionRange(cm, range, selFragment); }
-    }
-    return result
-  }
-
-  // Draws a cursor for the given range
-  function drawSelectionCursor(cm, head, output) {
-    var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
-
-    var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
-    cursor.style.left = pos.left + "px";
-    cursor.style.top = pos.top + "px";
-    cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
-
-    if (pos.other) {
-      // Secondary cursor, shown when on a 'jump' in bi-directional text
-      var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
-      otherCursor.style.display = "";
-      otherCursor.style.left = pos.other.left + "px";
-      otherCursor.style.top = pos.other.top + "px";
-      otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
-    }
-  }
-
-  function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
-
-  // Draws the given range as a highlighted selection
-  function drawSelectionRange(cm, range, output) {
-    var display = cm.display, doc = cm.doc;
-    var fragment = document.createDocumentFragment();
-    var padding = paddingH(cm.display), leftSide = padding.left;
-    var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
-    var docLTR = doc.direction == "ltr";
-
-    function add(left, top, width, bottom) {
-      if (top < 0) { top = 0; }
-      top = Math.round(top);
-      bottom = Math.round(bottom);
-      fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n                             top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n                             height: " + (bottom - top) + "px")));
-    }
-
-    function drawForLine(line, fromArg, toArg) {
-      var lineObj = getLine(doc, line);
-      var lineLen = lineObj.text.length;
-      var start, end;
-      function coords(ch, bias) {
-        return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
-      }
-
-      function wrapX(pos, dir, side) {
-        var extent = wrappedLineExtentChar(cm, lineObj, null, pos);
-        var prop = (dir == "ltr") == (side == "after") ? "left" : "right";
-        var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);
-        return coords(ch, prop)[prop]
-      }
-
-      var order = getOrder(lineObj, doc.direction);
-      iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
-        var ltr = dir == "ltr";
-        var fromPos = coords(from, ltr ? "left" : "right");
-        var toPos = coords(to - 1, ltr ? "right" : "left");
-
-        var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;
-        var first = i == 0, last = !order || i == order.length - 1;
-        if (toPos.top - fromPos.top <= 3) { // Single line
-          var openLeft = (docLTR ? openStart : openEnd) && first;
-          var openRight = (docLTR ? openEnd : openStart) && last;
-          var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;
-          var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;
-          add(left, fromPos.top, right - left, fromPos.bottom);
-        } else { // Multiple lines
-          var topLeft, topRight, botLeft, botRight;
-          if (ltr) {
-            topLeft = docLTR && openStart && first ? leftSide : fromPos.left;
-            topRight = docLTR ? rightSide : wrapX(from, dir, "before");
-            botLeft = docLTR ? leftSide : wrapX(to, dir, "after");
-            botRight = docLTR && openEnd && last ? rightSide : toPos.right;
-          } else {
-            topLeft = !docLTR ? leftSide : wrapX(from, dir, "before");
-            topRight = !docLTR && openStart && first ? rightSide : fromPos.right;
-            botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;
-            botRight = !docLTR ? rightSide : wrapX(to, dir, "after");
-          }
-          add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);
-          if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }
-          add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);
-        }
-
-        if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }
-        if (cmpCoords(toPos, start) < 0) { start = toPos; }
-        if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }
-        if (cmpCoords(toPos, end) < 0) { end = toPos; }
-      });
-      return {start: start, end: end}
-    }
-
-    var sFrom = range.from(), sTo = range.to();
-    if (sFrom.line == sTo.line) {
-      drawForLine(sFrom.line, sFrom.ch, sTo.ch);
-    } else {
-      var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
-      var singleVLine = visualLine(fromLine) == visualLine(toLine);
-      var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
-      var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
-      if (singleVLine) {
-        if (leftEnd.top < rightStart.top - 2) {
-          add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
-          add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
-        } else {
-          add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
-        }
-      }
-      if (leftEnd.bottom < rightStart.top)
-        { add(leftSide, leftEnd.bottom, null, rightStart.top); }
-    }
-
-    output.appendChild(fragment);
-  }
-
-  // Cursor-blinking
-  function restartBlink(cm) {
-    if (!cm.state.focused) { return }
-    var display = cm.display;
-    clearInterval(display.blinker);
-    var on = true;
-    display.cursorDiv.style.visibility = "";
-    if (cm.options.cursorBlinkRate > 0)
-      { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
-        cm.options.cursorBlinkRate); }
-    else if (cm.options.cursorBlinkRate < 0)
-      { display.cursorDiv.style.visibility = "hidden"; }
-  }
-
-  function ensureFocus(cm) {
-    if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
-  }
-
-  function delayBlurEvent(cm) {
-    cm.state.delayingBlurEvent = true;
-    setTimeout(function () { if (cm.state.delayingBlurEvent) {
-      cm.state.delayingBlurEvent = false;
-      onBlur(cm);
-    } }, 100);
-  }
-
-  function onFocus(cm, e) {
-    if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; }
-
-    if (cm.options.readOnly == "nocursor") { return }
-    if (!cm.state.focused) {
-      signal(cm, "focus", cm, e);
-      cm.state.focused = true;
-      addClass(cm.display.wrapper, "CodeMirror-focused");
-      // This test prevents this from firing when a context
-      // menu is closed (since the input reset would kill the
-      // select-all detection hack)
-      if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
-        cm.display.input.reset();
-        if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730
-      }
-      cm.display.input.receivedFocus();
-    }
-    restartBlink(cm);
-  }
-  function onBlur(cm, e) {
-    if (cm.state.delayingBlurEvent) { return }
-
-    if (cm.state.focused) {
-      signal(cm, "blur", cm, e);
-      cm.state.focused = false;
-      rmClass(cm.display.wrapper, "CodeMirror-focused");
-    }
-    clearInterval(cm.display.blinker);
-    setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);
-  }
-
-  // Read the actual heights of the rendered lines, and update their
-  // stored heights to match.
-  function updateHeightsInViewport(cm) {
-    var display = cm.display;
-    var prevBottom = display.lineDiv.offsetTop;
-    for (var i = 0; i < display.view.length; i++) {
-      var cur = display.view[i], wrapping = cm.options.lineWrapping;
-      var height = (void 0), width = 0;
-      if (cur.hidden) { continue }
-      if (ie && ie_version < 8) {
-        var bot = cur.node.offsetTop + cur.node.offsetHeight;
-        height = bot - prevBottom;
-        prevBottom = bot;
-      } else {
-        var box = cur.node.getBoundingClientRect();
-        height = box.bottom - box.top;
-        // Check that lines don't extend past the right of the current
-        // editor width
-        if (!wrapping && cur.text.firstChild)
-          { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; }
-      }
-      var diff = cur.line.height - height;
-      if (diff > .005 || diff < -.005) {
-        updateLineHeight(cur.line, height);
-        updateWidgetHeight(cur.line);
-        if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
-          { updateWidgetHeight(cur.rest[j]); } }
-      }
-      if (width > cm.display.sizerWidth) {
-        var chWidth = Math.ceil(width / charWidth(cm.display));
-        if (chWidth > cm.display.maxLineLength) {
-          cm.display.maxLineLength = chWidth;
-          cm.display.maxLine = cur.line;
-          cm.display.maxLineChanged = true;
-        }
-      }
-    }
-  }
-
-  // Read and store the height of line widgets associated with the
-  // given line.
-  function updateWidgetHeight(line) {
-    if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
-      var w = line.widgets[i], parent = w.node.parentNode;
-      if (parent) { w.height = parent.offsetHeight; }
-    } }
-  }
-
-  // Compute the lines that are visible in a given viewport (defaults
-  // the the current scroll position). viewport may contain top,
-  // height, and ensure (see op.scrollToPos) properties.
-  function visibleLines(display, doc, viewport) {
-    var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
-    top = Math.floor(top - paddingTop(display));
-    var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
-
-    var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
-    // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
-    // forces those lines into the viewport (if possible).
-    if (viewport && viewport.ensure) {
-      var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
-      if (ensureFrom < from) {
-        from = ensureFrom;
-        to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
-      } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
-        from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
-        to = ensureTo;
-      }
-    }
-    return {from: from, to: Math.max(to, from + 1)}
-  }
-
-  // SCROLLING THINGS INTO VIEW
-
-  // If an editor sits on the top or bottom of the window, partially
-  // scrolled out of view, this ensures that the cursor is visible.
-  function maybeScrollWindow(cm, rect) {
-    if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
-
-    var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
-    if (rect.top + box.top < 0) { doScroll = true; }
-    else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; }
-    if (doScroll != null && !phantom) {
-      var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n                         top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n                         height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n                         left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;"));
-      cm.display.lineSpace.appendChild(scrollNode);
-      scrollNode.scrollIntoView(doScroll);
-      cm.display.lineSpace.removeChild(scrollNode);
-    }
-  }
-
-  // Scroll a given position into view (immediately), verifying that
-  // it actually became visible (as line heights are accurately
-  // measured, the position of something may 'drift' during drawing).
-  function scrollPosIntoView(cm, pos, end, margin) {
-    if (margin == null) { margin = 0; }
-    var rect;
-    if (!cm.options.lineWrapping && pos == end) {
-      // Set pos and end to the cursor positions around the character pos sticks to
-      // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
-      // If pos == Pos(_, 0, "before"), pos and end are unchanged
-      pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos;
-      end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos;
-    }
-    for (var limit = 0; limit < 5; limit++) {
-      var changed = false;
-      var coords = cursorCoords(cm, pos);
-      var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
-      rect = {left: Math.min(coords.left, endCoords.left),
-              top: Math.min(coords.top, endCoords.top) - margin,
-              right: Math.max(coords.left, endCoords.left),
-              bottom: Math.max(coords.bottom, endCoords.bottom) + margin};
-      var scrollPos = calculateScrollPos(cm, rect);
-      var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
-      if (scrollPos.scrollTop != null) {
-        updateScrollTop(cm, scrollPos.scrollTop);
-        if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }
-      }
-      if (scrollPos.scrollLeft != null) {
-        setScrollLeft(cm, scrollPos.scrollLeft);
-        if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }
-      }
-      if (!changed) { break }
-    }
-    return rect
-  }
-
-  // Scroll a given set of coordinates into view (immediately).
-  function scrollIntoView(cm, rect) {
-    var scrollPos = calculateScrollPos(cm, rect);
-    if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }
-    if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }
-  }
-
-  // Calculate a new scroll position needed to scroll the given
-  // rectangle into view. Returns an object with scrollTop and
-  // scrollLeft properties. When these are undefined, the
-  // vertical/horizontal position does not need to be adjusted.
-  function calculateScrollPos(cm, rect) {
-    var display = cm.display, snapMargin = textHeight(cm.display);
-    if (rect.top < 0) { rect.top = 0; }
-    var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
-    var screen = displayHeight(cm), result = {};
-    if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }
-    var docBottom = cm.doc.height + paddingVert(display);
-    var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;
-    if (rect.top < screentop) {
-      result.scrollTop = atTop ? 0 : rect.top;
-    } else if (rect.bottom > screentop + screen) {
-      var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);
-      if (newTop != screentop) { result.scrollTop = newTop; }
-    }
-
-    var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
-    var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
-    var tooWide = rect.right - rect.left > screenw;
-    if (tooWide) { rect.right = rect.left + screenw; }
-    if (rect.left < 10)
-      { result.scrollLeft = 0; }
-    else if (rect.left < screenleft)
-      { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); }
-    else if (rect.right > screenw + screenleft - 3)
-      { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }
-    return result
-  }
-
-  // Store a relative adjustment to the scroll position in the current
-  // operation (to be applied when the operation finishes).
-  function addToScrollTop(cm, top) {
-    if (top == null) { return }
-    resolveScrollToPos(cm);
-    cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
-  }
-
-  // Make sure that at the end of the operation the current cursor is
-  // shown.
-  function ensureCursorVisible(cm) {
-    resolveScrollToPos(cm);
-    var cur = cm.getCursor();
-    cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};
-  }
-
-  function scrollToCoords(cm, x, y) {
-    if (x != null || y != null) { resolveScrollToPos(cm); }
-    if (x != null) { cm.curOp.scrollLeft = x; }
-    if (y != null) { cm.curOp.scrollTop = y; }
-  }
-
-  function scrollToRange(cm, range) {
-    resolveScrollToPos(cm);
-    cm.curOp.scrollToPos = range;
-  }
-
-  // When an operation has its scrollToPos property set, and another
-  // scroll action is applied before the end of the operation, this
-  // 'simulates' scrolling that position into view in a cheap way, so
-  // that the effect of intermediate scroll commands is not ignored.
-  function resolveScrollToPos(cm) {
-    var range = cm.curOp.scrollToPos;
-    if (range) {
-      cm.curOp.scrollToPos = null;
-      var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);
-      scrollToCoordsRange(cm, from, to, range.margin);
-    }
-  }
-
-  function scrollToCoordsRange(cm, from, to, margin) {
-    var sPos = calculateScrollPos(cm, {
-      left: Math.min(from.left, to.left),
-      top: Math.min(from.top, to.top) - margin,
-      right: Math.max(from.right, to.right),
-      bottom: Math.max(from.bottom, to.bottom) + margin
-    });
-    scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);
-  }
-
-  // Sync the scrollable area and scrollbars, ensure the viewport
-  // covers the visible area.
-  function updateScrollTop(cm, val) {
-    if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
-    if (!gecko) { updateDisplaySimple(cm, {top: val}); }
-    setScrollTop(cm, val, true);
-    if (gecko) { updateDisplaySimple(cm); }
-    startWorker(cm, 100);
-  }
-
-  function setScrollTop(cm, val, forceScroll) {
-    val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val));
-    if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
-    cm.doc.scrollTop = val;
-    cm.display.scrollbars.setScrollTop(val);
-    if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }
-  }
-
-  // Sync scroller and scrollbar, ensure the gutter elements are
-  // aligned.
-  function setScrollLeft(cm, val, isScroller, forceScroll) {
-    val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth));
-    if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
-    cm.doc.scrollLeft = val;
-    alignHorizontally(cm);
-    if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }
-    cm.display.scrollbars.setScrollLeft(val);
-  }
-
-  // SCROLLBARS
-
-  // Prepare DOM reads needed to update the scrollbars. Done in one
-  // shot to minimize update/measure roundtrips.
-  function measureForScrollbars(cm) {
-    var d = cm.display, gutterW = d.gutters.offsetWidth;
-    var docH = Math.round(cm.doc.height + paddingVert(cm.display));
-    return {
-      clientHeight: d.scroller.clientHeight,
-      viewHeight: d.wrapper.clientHeight,
-      scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
-      viewWidth: d.wrapper.clientWidth,
-      barLeft: cm.options.fixedGutter ? gutterW : 0,
-      docHeight: docH,
-      scrollHeight: docH + scrollGap(cm) + d.barHeight,
-      nativeBarWidth: d.nativeBarWidth,
-      gutterWidth: gutterW
-    }
-  }
-
-  var NativeScrollbars = function(place, scroll, cm) {
-    this.cm = cm;
-    var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
-    var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
-    vert.tabIndex = horiz.tabIndex = -1;
-    place(vert); place(horiz);
-
-    on(vert, "scroll", function () {
-      if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); }
-    });
-    on(horiz, "scroll", function () {
-      if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); }
-    });
-
-    this.checkedZeroWidth = false;
-    // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
-    if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; }
-  };
-
-  NativeScrollbars.prototype.update = function (measure) {
-    var needsH = measure.scrollWidth > measure.clientWidth + 1;
-    var needsV = measure.scrollHeight > measure.clientHeight + 1;
-    var sWidth = measure.nativeBarWidth;
-
-    if (needsV) {
-      this.vert.style.display = "block";
-      this.vert.style.bottom = needsH ? sWidth + "px" : "0";
-      var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
-      // A bug in IE8 can cause this value to be negative, so guard it.
-      this.vert.firstChild.style.height =
-        Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
-    } else {
-      this.vert.style.display = "";
-      this.vert.firstChild.style.height = "0";
-    }
-
-    if (needsH) {
-      this.horiz.style.display = "block";
-      this.horiz.style.right = needsV ? sWidth + "px" : "0";
-      this.horiz.style.left = measure.barLeft + "px";
-      var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
-      this.horiz.firstChild.style.width =
-        Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
-    } else {
-      this.horiz.style.display = "";
-      this.horiz.firstChild.style.width = "0";
-    }
-
-    if (!this.checkedZeroWidth && measure.clientHeight > 0) {
-      if (sWidth == 0) { this.zeroWidthHack(); }
-      this.checkedZeroWidth = true;
-    }
-
-    return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
-  };
-
-  NativeScrollbars.prototype.setScrollLeft = function (pos) {
-    if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }
-    if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); }
-  };
-
-  NativeScrollbars.prototype.setScrollTop = function (pos) {
-    if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }
-    if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); }
-  };
-
-  NativeScrollbars.prototype.zeroWidthHack = function () {
-    var w = mac && !mac_geMountainLion ? "12px" : "18px";
-    this.horiz.style.height = this.vert.style.width = w;
-    this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
-    this.disableHoriz = new Delayed;
-    this.disableVert = new Delayed;
-  };
-
-  NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
-    bar.style.pointerEvents = "auto";
-    function maybeDisable() {
-      // To find out whether the scrollbar is still visible, we
-      // check whether the element under the pixel in the bottom
-      // right corner of the scrollbar box is the scrollbar box
-      // itself (when the bar is still visible) or its filler child
-      // (when the bar is hidden). If it is still visible, we keep
-      // it enabled, if it's hidden, we disable pointer events.
-      var box = bar.getBoundingClientRect();
-      var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
-          : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);
-      if (elt != bar) { bar.style.pointerEvents = "none"; }
-      else { delay.set(1000, maybeDisable); }
-    }
-    delay.set(1000, maybeDisable);
-  };
-
-  NativeScrollbars.prototype.clear = function () {
-    var parent = this.horiz.parentNode;
-    parent.removeChild(this.horiz);
-    parent.removeChild(this.vert);
-  };
-
-  var NullScrollbars = function () {};
-
-  NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
-  NullScrollbars.prototype.setScrollLeft = function () {};
-  NullScrollbars.prototype.setScrollTop = function () {};
-  NullScrollbars.prototype.clear = function () {};
-
-  function updateScrollbars(cm, measure) {
-    if (!measure) { measure = measureForScrollbars(cm); }
-    var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
-    updateScrollbarsInner(cm, measure);
-    for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
-      if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
-        { updateHeightsInViewport(cm); }
-      updateScrollbarsInner(cm, measureForScrollbars(cm));
-      startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
-    }
-  }
-
-  // Re-synchronize the fake scrollbars with the actual size of the
-  // content.
-  function updateScrollbarsInner(cm, measure) {
-    var d = cm.display;
-    var sizes = d.scrollbars.update(measure);
-
-    d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
-    d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
-    d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent";
-
-    if (sizes.right && sizes.bottom) {
-      d.scrollbarFiller.style.display = "block";
-      d.scrollbarFiller.style.height = sizes.bottom + "px";
-      d.scrollbarFiller.style.width = sizes.right + "px";
-    } else { d.scrollbarFiller.style.display = ""; }
-    if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
-      d.gutterFiller.style.display = "block";
-      d.gutterFiller.style.height = sizes.bottom + "px";
-      d.gutterFiller.style.width = measure.gutterWidth + "px";
-    } else { d.gutterFiller.style.display = ""; }
-  }
-
-  var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
-
-  function initScrollbars(cm) {
-    if (cm.display.scrollbars) {
-      cm.display.scrollbars.clear();
-      if (cm.display.scrollbars.addClass)
-        { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
-    }
-
-    cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
-      cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
-      // Prevent clicks in the scrollbars from killing focus
-      on(node, "mousedown", function () {
-        if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }
-      });
-      node.setAttribute("cm-not-content", "true");
-    }, function (pos, axis) {
-      if (axis == "horizontal") { setScrollLeft(cm, pos); }
-      else { updateScrollTop(cm, pos); }
-    }, cm);
-    if (cm.display.scrollbars.addClass)
-      { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
-  }
-
-  // Operations are used to wrap a series of changes to the editor
-  // state in such a way that each change won't have to update the
-  // cursor and display (which would be awkward, slow, and
-  // error-prone). Instead, display updates are batched and then all
-  // combined and executed at once.
-
-  var nextOpId = 0;
-  // Start a new operation.
-  function startOperation(cm) {
-    cm.curOp = {
-      cm: cm,
-      viewChanged: false,      // Flag that indicates that lines might need to be redrawn
-      startHeight: cm.doc.height, // Used to detect need to update scrollbar
-      forceUpdate: false,      // Used to force a redraw
-      updateInput: 0,       // Whether to reset the input textarea
-      typing: false,           // Whether this reset should be careful to leave existing text (for compositing)
-      changeObjs: null,        // Accumulated changes, for firing change events
-      cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
-      cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
-      selectionChanged: false, // Whether the selection needs to be redrawn
-      updateMaxLine: false,    // Set when the widest line needs to be determined anew
-      scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
-      scrollToPos: null,       // Used to scroll to a specific position
-      focus: false,
-      id: ++nextOpId           // Unique ID
-    };
-    pushOperation(cm.curOp);
-  }
-
-  // Finish an operation, updating the display and signalling delayed events
-  function endOperation(cm) {
-    var op = cm.curOp;
-    if (op) { finishOperation(op, function (group) {
-      for (var i = 0; i < group.ops.length; i++)
-        { group.ops[i].cm.curOp = null; }
-      endOperations(group);
-    }); }
-  }
-
-  // The DOM updates done when an operation finishes are batched so
-  // that the minimum number of relayouts are required.
-  function endOperations(group) {
-    var ops = group.ops;
-    for (var i = 0; i < ops.length; i++) // Read DOM
-      { endOperation_R1(ops[i]); }
-    for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
-      { endOperation_W1(ops[i$1]); }
-    for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
-      { endOperation_R2(ops[i$2]); }
-    for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
-      { endOperation_W2(ops[i$3]); }
-    for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
-      { endOperation_finish(ops[i$4]); }
-  }
-
-  function endOperation_R1(op) {
-    var cm = op.cm, display = cm.display;
-    maybeClipScrollbars(cm);
-    if (op.updateMaxLine) { findMaxLine(cm); }
-
-    op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
-      op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
-                         op.scrollToPos.to.line >= display.viewTo) ||
-      display.maxLineChanged && cm.options.lineWrapping;
-    op.update = op.mustUpdate &&
-      new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
-  }
-
-  function endOperation_W1(op) {
-    op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
-  }
-
-  function endOperation_R2(op) {
-    var cm = op.cm, display = cm.display;
-    if (op.updatedDisplay) { updateHeightsInViewport(cm); }
-
-    op.barMeasure = measureForScrollbars(cm);
-
-    // If the max line changed since it was last measured, measure it,
-    // and ensure the document's width matches it.
-    // updateDisplay_W2 will use these properties to do the actual resizing
-    if (display.maxLineChanged && !cm.options.lineWrapping) {
-      op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
-      cm.display.sizerWidth = op.adjustWidthTo;
-      op.barMeasure.scrollWidth =
-        Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
-      op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
-    }
-
-    if (op.updatedDisplay || op.selectionChanged)
-      { op.preparedSelection = display.input.prepareSelection(); }
-  }
-
-  function endOperation_W2(op) {
-    var cm = op.cm;
-
-    if (op.adjustWidthTo != null) {
-      cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
-      if (op.maxScrollLeft < cm.doc.scrollLeft)
-        { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }
-      cm.display.maxLineChanged = false;
-    }
-
-    var takeFocus = op.focus && op.focus == activeElt();
-    if (op.preparedSelection)
-      { cm.display.input.showSelection(op.preparedSelection, takeFocus); }
-    if (op.updatedDisplay || op.startHeight != cm.doc.height)
-      { updateScrollbars(cm, op.barMeasure); }
-    if (op.updatedDisplay)
-      { setDocumentHeight(cm, op.barMeasure); }
-
-    if (op.selectionChanged) { restartBlink(cm); }
-
-    if (cm.state.focused && op.updateInput)
-      { cm.display.input.reset(op.typing); }
-    if (takeFocus) { ensureFocus(op.cm); }
-  }
-
-  function endOperation_finish(op) {
-    var cm = op.cm, display = cm.display, doc = cm.doc;
-
-    if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }
-
-    // Abort mouse wheel delta measurement, when scrolling explicitly
-    if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
-      { display.wheelStartX = display.wheelStartY = null; }
-
-    // Propagate the scroll position to the actual DOM scroller
-    if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }
-
-    if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }
-    // If we need to scroll a specific position into view, do so.
-    if (op.scrollToPos) {
-      var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
-                                   clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
-      maybeScrollWindow(cm, rect);
-    }
-
-    // Fire events for markers that are hidden/unidden by editing or
-    // undoing
-    var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
-    if (hidden) { for (var i = 0; i < hidden.length; ++i)
-      { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } }
-    if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
-      { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } }
-
-    if (display.wrapper.offsetHeight)
-      { doc.scrollTop = cm.display.scroller.scrollTop; }
-
-    // Fire change events, and delayed event handlers
-    if (op.changeObjs)
-      { signal(cm, "changes", cm, op.changeObjs); }
-    if (op.update)
-      { op.update.finish(); }
-  }
-
-  // Run the given function in an operation
-  function runInOp(cm, f) {
-    if (cm.curOp) { return f() }
-    startOperation(cm);
-    try { return f() }
-    finally { endOperation(cm); }
-  }
-  // Wraps a function in an operation. Returns the wrapped function.
-  function operation(cm, f) {
-    return function() {
-      if (cm.curOp) { return f.apply(cm, arguments) }
-      startOperation(cm);
-      try { return f.apply(cm, arguments) }
-      finally { endOperation(cm); }
-    }
-  }
-  // Used to add methods to editor and doc instances, wrapping them in
-  // operations.
-  function methodOp(f) {
-    return function() {
-      if (this.curOp) { return f.apply(this, arguments) }
-      startOperation(this);
-      try { return f.apply(this, arguments) }
-      finally { endOperation(this); }
-    }
-  }
-  function docMethodOp(f) {
-    return function() {
-      var cm = this.cm;
-      if (!cm || cm.curOp) { return f.apply(this, arguments) }
-      startOperation(cm);
-      try { return f.apply(this, arguments) }
-      finally { endOperation(cm); }
-    }
-  }
-
-  // HIGHLIGHT WORKER
-
-  function startWorker(cm, time) {
-    if (cm.doc.highlightFrontier < cm.display.viewTo)
-      { cm.state.highlight.set(time, bind(highlightWorker, cm)); }
-  }
-
-  function highlightWorker(cm) {
-    var doc = cm.doc;
-    if (doc.highlightFrontier >= cm.display.viewTo) { return }
-    var end = +new Date + cm.options.workTime;
-    var context = getContextBefore(cm, doc.highlightFrontier);
-    var changedLines = [];
-
-    doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
-      if (context.line >= cm.display.viewFrom) { // Visible
-        var oldStyles = line.styles;
-        var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;
-        var highlighted = highlightLine(cm, line, context, true);
-        if (resetState) { context.state = resetState; }
-        line.styles = highlighted.styles;
-        var oldCls = line.styleClasses, newCls = highlighted.classes;
-        if (newCls) { line.styleClasses = newCls; }
-        else if (oldCls) { line.styleClasses = null; }
-        var ischange = !oldStyles || oldStyles.length != line.styles.length ||
-          oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
-        for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }
-        if (ischange) { changedLines.push(context.line); }
-        line.stateAfter = context.save();
-        context.nextLine();
-      } else {
-        if (line.text.length <= cm.options.maxHighlightLength)
-          { processLine(cm, line.text, context); }
-        line.stateAfter = context.line % 5 == 0 ? context.save() : null;
-        context.nextLine();
-      }
-      if (+new Date > end) {
-        startWorker(cm, cm.options.workDelay);
-        return true
-      }
-    });
-    doc.highlightFrontier = context.line;
-    doc.modeFrontier = Math.max(doc.modeFrontier, context.line);
-    if (changedLines.length) { runInOp(cm, function () {
-      for (var i = 0; i < changedLines.length; i++)
-        { regLineChange(cm, changedLines[i], "text"); }
-    }); }
-  }
-
-  // DISPLAY DRAWING
-
-  var DisplayUpdate = function(cm, viewport, force) {
-    var display = cm.display;
-
-    this.viewport = viewport;
-    // Store some values that we'll need later (but don't want to force a relayout for)
-    this.visible = visibleLines(display, cm.doc, viewport);
-    this.editorIsHidden = !display.wrapper.offsetWidth;
-    this.wrapperHeight = display.wrapper.clientHeight;
-    this.wrapperWidth = display.wrapper.clientWidth;
-    this.oldDisplayWidth = displayWidth(cm);
-    this.force = force;
-    this.dims = getDimensions(cm);
-    this.events = [];
-  };
-
-  DisplayUpdate.prototype.signal = function (emitter, type) {
-    if (hasHandler(emitter, type))
-      { this.events.push(arguments); }
-  };
-  DisplayUpdate.prototype.finish = function () {
-    for (var i = 0; i < this.events.length; i++)
-      { signal.apply(null, this.events[i]); }
-  };
-
-  function maybeClipScrollbars(cm) {
-    var display = cm.display;
-    if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
-      display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
-      display.heightForcer.style.height = scrollGap(cm) + "px";
-      display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
-      display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
-      display.scrollbarsClipped = true;
-    }
-  }
-
-  function selectionSnapshot(cm) {
-    if (cm.hasFocus()) { return null }
-    var active = activeElt();
-    if (!active || !contains(cm.display.lineDiv, active)) { return null }
-    var result = {activeElt: active};
-    if (window.getSelection) {
-      var sel = window.getSelection();
-      if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
-        result.anchorNode = sel.anchorNode;
-        result.anchorOffset = sel.anchorOffset;
-        result.focusNode = sel.focusNode;
-        result.focusOffset = sel.focusOffset;
-      }
-    }
-    return result
-  }
-
-  function restoreSelection(snapshot) {
-    if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
-    snapshot.activeElt.focus();
-    if (!/^(INPUT|TEXTAREA)$/.test(snapshot.activeElt.nodeName) &&
-        snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
-      var sel = window.getSelection(), range = document.createRange();
-      range.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
-      range.collapse(false);
-      sel.removeAllRanges();
-      sel.addRange(range);
-      sel.extend(snapshot.focusNode, snapshot.focusOffset);
-    }
-  }
-
-  // Does the actual updating of the line display. Bails out
-  // (returning false) when there is nothing to be done and forced is
-  // false.
-  function updateDisplayIfNeeded(cm, update) {
-    var display = cm.display, doc = cm.doc;
-
-    if (update.editorIsHidden) {
-      resetView(cm);
-      return false
-    }
-
-    // Bail out if the visible area is already rendered and nothing changed.
-    if (!update.force &&
-        update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
-        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
-        display.renderedView == display.view && countDirtyView(cm) == 0)
-      { return false }
-
-    if (maybeUpdateLineNumberWidth(cm)) {
-      resetView(cm);
-      update.dims = getDimensions(cm);
-    }
-
-    // Compute a suitable new viewport (from & to)
-    var end = doc.first + doc.size;
-    var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
-    var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
-    if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }
-    if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }
-    if (sawCollapsedSpans) {
-      from = visualLineNo(cm.doc, from);
-      to = visualLineEndNo(cm.doc, to);
-    }
-
-    var different = from != display.viewFrom || to != display.viewTo ||
-      display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
-    adjustView(cm, from, to);
-
-    display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
-    // Position the mover div to align with the current scroll position
-    cm.display.mover.style.top = display.viewOffset + "px";
-
-    var toUpdate = countDirtyView(cm);
-    if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
-        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
-      { return false }
-
-    // For big changes, we hide the enclosing element during the
-    // update, since that speeds up the operations on most browsers.
-    var selSnapshot = selectionSnapshot(cm);
-    if (toUpdate > 4) { display.lineDiv.style.display = "none"; }
-    patchDisplay(cm, display.updateLineNumbers, update.dims);
-    if (toUpdate > 4) { display.lineDiv.style.display = ""; }
-    display.renderedView = display.view;
-    // There might have been a widget with a focused element that got
-    // hidden or updated, if so re-focus it.
-    restoreSelection(selSnapshot);
-
-    // Prevent selection and cursors from interfering with the scroll
-    // width and height.
-    removeChildren(display.cursorDiv);
-    removeChildren(display.selectionDiv);
-    display.gutters.style.height = display.sizer.style.minHeight = 0;
-
-    if (different) {
-      display.lastWrapHeight = update.wrapperHeight;
-      display.lastWrapWidth = update.wrapperWidth;
-      startWorker(cm, 400);
-    }
-
-    display.updateLineNumbers = null;
-
-    return true
-  }
-
-  function postUpdateDisplay(cm, update) {
-    var viewport = update.viewport;
-
-    for (var first = true;; first = false) {
-      if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
-        // Clip forced viewport to actual scrollable area.
-        if (viewport && viewport.top != null)
-          { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }
-        // Updated line heights might result in the drawn area not
-        // actually covering the viewport. Keep looping until it does.
-        update.visible = visibleLines(cm.display, cm.doc, viewport);
-        if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
-          { break }
-      } else if (first) {
-        update.visible = visibleLines(cm.display, cm.doc, viewport);
-      }
-      if (!updateDisplayIfNeeded(cm, update)) { break }
-      updateHeightsInViewport(cm);
-      var barMeasure = measureForScrollbars(cm);
-      updateSelection(cm);
-      updateScrollbars(cm, barMeasure);
-      setDocumentHeight(cm, barMeasure);
-      update.force = false;
-    }
-
-    update.signal(cm, "update", cm);
-    if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
-      update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
-      cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
-    }
-  }
-
-  function updateDisplaySimple(cm, viewport) {
-    var update = new DisplayUpdate(cm, viewport);
-    if (updateDisplayIfNeeded(cm, update)) {
-      updateHeightsInViewport(cm);
-      postUpdateDisplay(cm, update);
-      var barMeasure = measureForScrollbars(cm);
-      updateSelection(cm);
-      updateScrollbars(cm, barMeasure);
-      setDocumentHeight(cm, barMeasure);
-      update.finish();
-    }
-  }
-
-  // Sync the actual display DOM structure with display.view, removing
-  // nodes for lines that are no longer in view, and creating the ones
-  // that are not there yet, and updating the ones that are out of
-  // date.
-  function patchDisplay(cm, updateNumbersFrom, dims) {
-    var display = cm.display, lineNumbers = cm.options.lineNumbers;
-    var container = display.lineDiv, cur = container.firstChild;
-
-    function rm(node) {
-      var next = node.nextSibling;
-      // Works around a throw-scroll bug in OS X Webkit
-      if (webkit && mac && cm.display.currentWheelTarget == node)
-        { node.style.display = "none"; }
-      else
-        { node.parentNode.removeChild(node); }
-      return next
-    }
-
-    var view = display.view, lineN = display.viewFrom;
-    // Loop over the elements in the view, syncing cur (the DOM nodes
-    // in display.lineDiv) with the view as we go.
-    for (var i = 0; i < view.length; i++) {
-      var lineView = view[i];
-      if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
-        var node = buildLineElement(cm, lineView, lineN, dims);
-        container.insertBefore(node, cur);
-      } else { // Already drawn
-        while (cur != lineView.node) { cur = rm(cur); }
-        var updateNumber = lineNumbers && updateNumbersFrom != null &&
-          updateNumbersFrom <= lineN && lineView.lineNumber;
-        if (lineView.changes) {
-          if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; }
-          updateLineForChanges(cm, lineView, lineN, dims);
-        }
-        if (updateNumber) {
-          removeChildren(lineView.lineNumber);
-          lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
-        }
-        cur = lineView.node.nextSibling;
-      }
-      lineN += lineView.size;
-    }
-    while (cur) { cur = rm(cur); }
-  }
-
-  function updateGutterSpace(display) {
-    var width = display.gutters.offsetWidth;
-    display.sizer.style.marginLeft = width + "px";
-  }
-
-  function setDocumentHeight(cm, measure) {
-    cm.display.sizer.style.minHeight = measure.docHeight + "px";
-    cm.display.heightForcer.style.top = measure.docHeight + "px";
-    cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
-  }
-
-  // Re-align line numbers and gutter marks to compensate for
-  // horizontal scrolling.
-  function alignHorizontally(cm) {
-    var display = cm.display, view = display.view;
-    if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
-    var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
-    var gutterW = display.gutters.offsetWidth, left = comp + "px";
-    for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
-      if (cm.options.fixedGutter) {
-        if (view[i].gutter)
-          { view[i].gutter.style.left = left; }
-        if (view[i].gutterBackground)
-          { view[i].gutterBackground.style.left = left; }
-      }
-      var align = view[i].alignable;
-      if (align) { for (var j = 0; j < align.length; j++)
-        { align[j].style.left = left; } }
-    } }
-    if (cm.options.fixedGutter)
-      { display.gutters.style.left = (comp + gutterW) + "px"; }
-  }
-
-  // Used to ensure that the line number gutter is still the right
-  // size for the current document size. Returns true when an update
-  // is needed.
-  function maybeUpdateLineNumberWidth(cm) {
-    if (!cm.options.lineNumbers) { return false }
-    var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
-    if (last.length != display.lineNumChars) {
-      var test = display.measure.appendChild(elt("div", [elt("div", last)],
-                                                 "CodeMirror-linenumber CodeMirror-gutter-elt"));
-      var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
-      display.lineGutter.style.width = "";
-      display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
-      display.lineNumWidth = display.lineNumInnerWidth + padding;
-      display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
-      display.lineGutter.style.width = display.lineNumWidth + "px";
-      updateGutterSpace(cm.display);
-      return true
-    }
-    return false
-  }
-
-  function getGutters(gutters, lineNumbers) {
-    var result = [], sawLineNumbers = false;
-    for (var i = 0; i < gutters.length; i++) {
-      var name = gutters[i], style = null;
-      if (typeof name != "string") { style = name.style; name = name.className; }
-      if (name == "CodeMirror-linenumbers") {
-        if (!lineNumbers) { continue }
-        else { sawLineNumbers = true; }
-      }
-      result.push({className: name, style: style});
-    }
-    if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); }
-    return result
-  }
-
-  // Rebuild the gutter elements, ensure the margin to the left of the
-  // code matches their width.
-  function renderGutters(display) {
-    var gutters = display.gutters, specs = display.gutterSpecs;
-    removeChildren(gutters);
-    display.lineGutter = null;
-    for (var i = 0; i < specs.length; ++i) {
-      var ref = specs[i];
-      var className = ref.className;
-      var style = ref.style;
-      var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className));
-      if (style) { gElt.style.cssText = style; }
-      if (className == "CodeMirror-linenumbers") {
-        display.lineGutter = gElt;
-        gElt.style.width = (display.lineNumWidth || 1) + "px";
-      }
-    }
-    gutters.style.display = specs.length ? "" : "none";
-    updateGutterSpace(display);
-  }
-
-  function updateGutters(cm) {
-    renderGutters(cm.display);
-    regChange(cm);
-    alignHorizontally(cm);
-  }
-
-  // The display handles the DOM integration, both for input reading
-  // and content drawing. It holds references to DOM nodes and
-  // display-related state.
-
-  function Display(place, doc, input, options) {
-    var d = this;
-    this.input = input;
-
-    // Covers bottom-right square when both scrollbars are present.
-    d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
-    d.scrollbarFiller.setAttribute("cm-not-content", "true");
-    // Covers bottom of gutter when coverGutterNextToScrollbar is on
-    // and h scrollbar is present.
-    d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
-    d.gutterFiller.setAttribute("cm-not-content", "true");
-    // Will contain the actual code, positioned to cover the viewport.
-    d.lineDiv = eltP("div", null, "CodeMirror-code");
-    // Elements are added to these to represent selection and cursors.
-    d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
-    d.cursorDiv = elt("div", null, "CodeMirror-cursors");
-    // A visibility: hidden element used to find the size of things.
-    d.measure = elt("div", null, "CodeMirror-measure");
-    // When lines outside of the viewport are measured, they are drawn in this.
-    d.lineMeasure = elt("div", null, "CodeMirror-measure");
-    // Wraps everything that needs to exist inside the vertically-padded coordinate system
-    d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
-                      null, "position: relative; outline: none");
-    var lines = eltP("div", [d.lineSpace], "CodeMirror-lines");
-    // Moved around its parent to cover visible view.
-    d.mover = elt("div", [lines], null, "position: relative");
-    // Set to the height of the document, allowing scrolling.
-    d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
-    d.sizerWidth = null;
-    // Behavior of elts with overflow: auto and padding is
-    // inconsistent across browsers. This is used to ensure the
-    // scrollable area is big enough.
-    d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
-    // Will contain the gutters, if any.
-    d.gutters = elt("div", null, "CodeMirror-gutters");
-    d.lineGutter = null;
-    // Actual scrollable element.
-    d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
-    d.scroller.setAttribute("tabIndex", "-1");
-    // The element in which the editor lives.
-    d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
-
-    // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
-    if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
-    if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }
-
-    if (place) {
-      if (place.appendChild) { place.appendChild(d.wrapper); }
-      else { place(d.wrapper); }
-    }
-
-    // Current rendered range (may be bigger than the view window).
-    d.viewFrom = d.viewTo = doc.first;
-    d.reportedViewFrom = d.reportedViewTo = doc.first;
-    // Information about the rendered lines.
-    d.view = [];
-    d.renderedView = null;
-    // Holds info about a single rendered line when it was rendered
-    // for measurement, while not in view.
-    d.externalMeasured = null;
-    // Empty space (in pixels) above the view
-    d.viewOffset = 0;
-    d.lastWrapHeight = d.lastWrapWidth = 0;
-    d.updateLineNumbers = null;
-
-    d.nativeBarWidth = d.barHeight = d.barWidth = 0;
-    d.scrollbarsClipped = false;
-
-    // Used to only resize the line number gutter when necessary (when
-    // the amount of lines crosses a boundary that makes its width change)
-    d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
-    // Set to true when a non-horizontal-scrolling line widget is
-    // added. As an optimization, line widget aligning is skipped when
-    // this is false.
-    d.alignWidgets = false;
-
-    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
-
-    // Tracks the maximum line length so that the horizontal scrollbar
-    // can be kept static when scrolling.
-    d.maxLine = null;
-    d.maxLineLength = 0;
-    d.maxLineChanged = false;
-
-    // Used for measuring wheel scrolling granularity
-    d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
-
-    // True when shift is held down.
-    d.shift = false;
-
-    // Used to track whether anything happened since the context menu
-    // was opened.
-    d.selForContextMenu = null;
-
-    d.activeTouch = null;
-
-    d.gutterSpecs = getGutters(options.gutters, options.lineNumbers);
-    renderGutters(d);
-
-    input.init(d);
-  }
-
-  // Since the delta values reported on mouse wheel events are
-  // unstandardized between browsers and even browser versions, and
-  // generally horribly unpredictable, this code starts by measuring
-  // the scroll effect that the first few mouse wheel events have,
-  // and, from that, detects the way it can convert deltas to pixel
-  // offsets afterwards.
-  //
-  // The reason we want to know the amount a wheel event will scroll
-  // is that it gives us a chance to update the display before the
-  // actual scrolling happens, reducing flickering.
-
-  var wheelSamples = 0, wheelPixelsPerUnit = null;
-  // Fill in a browser-detected starting value on browsers where we
-  // know one. These don't have to be accurate -- the result of them
-  // being wrong would just be a slight flicker on the first wheel
-  // scroll (if it is large enough).
-  if (ie) { wheelPixelsPerUnit = -.53; }
-  else if (gecko) { wheelPixelsPerUnit = 15; }
-  else if (chrome) { wheelPixelsPerUnit = -.7; }
-  else if (safari) { wheelPixelsPerUnit = -1/3; }
-
-  function wheelEventDelta(e) {
-    var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
-    if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }
-    if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }
-    else if (dy == null) { dy = e.wheelDelta; }
-    return {x: dx, y: dy}
-  }
-  function wheelEventPixels(e) {
-    var delta = wheelEventDelta(e);
-    delta.x *= wheelPixelsPerUnit;
-    delta.y *= wheelPixelsPerUnit;
-    return delta
-  }
-
-  function onScrollWheel(cm, e) {
-    var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
-
-    var display = cm.display, scroll = display.scroller;
-    // Quit if there's nothing to scroll here
-    var canScrollX = scroll.scrollWidth > scroll.clientWidth;
-    var canScrollY = scroll.scrollHeight > scroll.clientHeight;
-    if (!(dx && canScrollX || dy && canScrollY)) { return }
-
-    // Webkit browsers on OS X abort momentum scrolls when the target
-    // of the scroll event is removed from the scrollable element.
-    // This hack (see related code in patchDisplay) makes sure the
-    // element is kept around.
-    if (dy && mac && webkit) {
-      outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
-        for (var i = 0; i < view.length; i++) {
-          if (view[i].node == cur) {
-            cm.display.currentWheelTarget = cur;
-            break outer
-          }
-        }
-      }
-    }
-
-    // On some browsers, horizontal scrolling will cause redraws to
-    // happen before the gutter has been realigned, causing it to
-    // wriggle around in a most unseemly way. When we have an
-    // estimated pixels/delta value, we just handle horizontal
-    // scrolling entirely here. It'll be slightly off from native, but
-    // better than glitching out.
-    if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
-      if (dy && canScrollY)
-        { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); }
-      setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit));
-      // Only prevent default scrolling if vertical scrolling is
-      // actually possible. Otherwise, it causes vertical scroll
-      // jitter on OSX trackpads when deltaX is small and deltaY
-      // is large (issue #3579)
-      if (!dy || (dy && canScrollY))
-        { e_preventDefault(e); }
-      display.wheelStartX = null; // Abort measurement, if in progress
-      return
-    }
-
-    // 'Project' the visible viewport to cover the area that is being
-    // scrolled into view (if we know enough to estimate it).
-    if (dy && wheelPixelsPerUnit != null) {
-      var pixels = dy * wheelPixelsPerUnit;
-      var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
-      if (pixels < 0) { top = Math.max(0, top + pixels - 50); }
-      else { bot = Math.min(cm.doc.height, bot + pixels + 50); }
-      updateDisplaySimple(cm, {top: top, bottom: bot});
-    }
-
-    if (wheelSamples < 20) {
-      if (display.wheelStartX == null) {
-        display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
-        display.wheelDX = dx; display.wheelDY = dy;
-        setTimeout(function () {
-          if (display.wheelStartX == null) { return }
-          var movedX = scroll.scrollLeft - display.wheelStartX;
-          var movedY = scroll.scrollTop - display.wheelStartY;
-          var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
-            (movedX && display.wheelDX && movedX / display.wheelDX);
-          display.wheelStartX = display.wheelStartY = null;
-          if (!sample) { return }
-          wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
-          ++wheelSamples;
-        }, 200);
-      } else {
-        display.wheelDX += dx; display.wheelDY += dy;
-      }
-    }
-  }
-
-  // Selection objects are immutable. A new one is created every time
-  // the selection changes. A selection is one or more non-overlapping
-  // (and non-touching) ranges, sorted, and an integer that indicates
-  // which one is the primary selection (the one that's scrolled into
-  // view, that getCursor returns, etc).
-  var Selection = function(ranges, primIndex) {
-    this.ranges = ranges;
-    this.primIndex = primIndex;
-  };
-
-  Selection.prototype.primary = function () { return this.ranges[this.primIndex] };
-
-  Selection.prototype.equals = function (other) {
-    if (other == this) { return true }
-    if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
-    for (var i = 0; i < this.ranges.length; i++) {
-      var here = this.ranges[i], there = other.ranges[i];
-      if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
-    }
-    return true
-  };
-
-  Selection.prototype.deepCopy = function () {
-    var out = [];
-    for (var i = 0; i < this.ranges.length; i++)
-      { out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); }
-    return new Selection(out, this.primIndex)
-  };
-
-  Selection.prototype.somethingSelected = function () {
-    for (var i = 0; i < this.ranges.length; i++)
-      { if (!this.ranges[i].empty()) { return true } }
-    return false
-  };
-
-  Selection.prototype.contains = function (pos, end) {
-    if (!end) { end = pos; }
-    for (var i = 0; i < this.ranges.length; i++) {
-      var range = this.ranges[i];
-      if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
-        { return i }
-    }
-    return -1
-  };
-
-  var Range = function(anchor, head) {
-    this.anchor = anchor; this.head = head;
-  };
-
-  Range.prototype.from = function () { return minPos(this.anchor, this.head) };
-  Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
-  Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };
-
-  // Take an unsorted, potentially overlapping set of ranges, and
-  // build a selection out of it. 'Consumes' ranges array (modifying
-  // it).
-  function normalizeSelection(cm, ranges, primIndex) {
-    var mayTouch = cm && cm.options.selectionsMayTouch;
-    var prim = ranges[primIndex];
-    ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });
-    primIndex = indexOf(ranges, prim);
-    for (var i = 1; i < ranges.length; i++) {
-      var cur = ranges[i], prev = ranges[i - 1];
-      var diff = cmp(prev.to(), cur.from());
-      if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {
-        var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
-        var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
-        if (i <= primIndex) { --primIndex; }
-        ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
-      }
-    }
-    return new Selection(ranges, primIndex)
-  }
-
-  function simpleSelection(anchor, head) {
-    return new Selection([new Range(anchor, head || anchor)], 0)
-  }
-
-  // Compute the position of the end of a change (its 'to' property
-  // refers to the pre-change end).
-  function changeEnd(change) {
-    if (!change.text) { return change.to }
-    return Pos(change.from.line + change.text.length - 1,
-               lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
-  }
-
-  // Adjust a position to refer to the post-change position of the
-  // same text, or the end of the change if the change covers it.
-  function adjustForChange(pos, change) {
-    if (cmp(pos, change.from) < 0) { return pos }
-    if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
-
-    var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
-    if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }
-    return Pos(line, ch)
-  }
-
-  function computeSelAfterChange(doc, change) {
-    var out = [];
-    for (var i = 0; i < doc.sel.ranges.length; i++) {
-      var range = doc.sel.ranges[i];
-      out.push(new Range(adjustForChange(range.anchor, change),
-                         adjustForChange(range.head, change)));
-    }
-    return normalizeSelection(doc.cm, out, doc.sel.primIndex)
-  }
-
-  function offsetPos(pos, old, nw) {
-    if (pos.line == old.line)
-      { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
-    else
-      { return Pos(nw.line + (pos.line - old.line), pos.ch) }
-  }
-
-  // Used by replaceSelections to allow moving the selection to the
-  // start or around the replaced test. Hint may be "start" or "around".
-  function computeReplacedSel(doc, changes, hint) {
-    var out = [];
-    var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
-    for (var i = 0; i < changes.length; i++) {
-      var change = changes[i];
-      var from = offsetPos(change.from, oldPrev, newPrev);
-      var to = offsetPos(changeEnd(change), oldPrev, newPrev);
-      oldPrev = change.to;
-      newPrev = to;
-      if (hint == "around") {
-        var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
-        out[i] = new Range(inv ? to : from, inv ? from : to);
-      } else {
-        out[i] = new Range(from, from);
-      }
-    }
-    return new Selection(out, doc.sel.primIndex)
-  }
-
-  // Used to get the editor into a consistent state again when options change.
-
-  function loadMode(cm) {
-    cm.doc.mode = getMode(cm.options, cm.doc.modeOption);
-    resetModeState(cm);
-  }
-
-  function resetModeState(cm) {
-    cm.doc.iter(function (line) {
-      if (line.stateAfter) { line.stateAfter = null; }
-      if (line.styles) { line.styles = null; }
-    });
-    cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;
-    startWorker(cm, 100);
-    cm.state.modeGen++;
-    if (cm.curOp) { regChange(cm); }
-  }
-
-  // DOCUMENT DATA STRUCTURE
-
-  // By default, updates that start and end at the beginning of a line
-  // are treated specially, in order to make the association of line
-  // widgets and marker elements with the text behave more intuitive.
-  function isWholeLineUpdate(doc, change) {
-    return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
-      (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
-  }
-
-  // Perform a change on the document data structure.
-  function updateDoc(doc, change, markedSpans, estimateHeight) {
-    function spansFor(n) {return markedSpans ? markedSpans[n] : null}
-    function update(line, text, spans) {
-      updateLine(line, text, spans, estimateHeight);
-      signalLater(line, "change", line, change);
-    }
-    function linesFor(start, end) {
-      var result = [];
-      for (var i = start; i < end; ++i)
-        { result.push(new Line(text[i], spansFor(i), estimateHeight)); }
-      return result
-    }
-
-    var from = change.from, to = change.to, text = change.text;
-    var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
-    var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
-
-    // Adjust the line structure
-    if (change.full) {
-      doc.insert(0, linesFor(0, text.length));
-      doc.remove(text.length, doc.size - text.length);
-    } else if (isWholeLineUpdate(doc, change)) {
-      // This is a whole-line replace. Treated specially to make
-      // sure line objects move the way they are supposed to.
-      var added = linesFor(0, text.length - 1);
-      update(lastLine, lastLine.text, lastSpans);
-      if (nlines) { doc.remove(from.line, nlines); }
-      if (added.length) { doc.insert(from.line, added); }
-    } else if (firstLine == lastLine) {
-      if (text.length == 1) {
-        update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
-      } else {
-        var added$1 = linesFor(1, text.length - 1);
-        added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
-        update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
-        doc.insert(from.line + 1, added$1);
-      }
-    } else if (text.length == 1) {
-      update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
-      doc.remove(from.line + 1, nlines);
-    } else {
-      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
-      update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
-      var added$2 = linesFor(1, text.length - 1);
-      if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }
-      doc.insert(from.line + 1, added$2);
-    }
-
-    signalLater(doc, "change", doc, change);
-  }
-
-  // Call f for all linked documents.
-  function linkedDocs(doc, f, sharedHistOnly) {
-    function propagate(doc, skip, sharedHist) {
-      if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
-        var rel = doc.linked[i];
-        if (rel.doc == skip) { continue }
-        var shared = sharedHist && rel.sharedHist;
-        if (sharedHistOnly && !shared) { continue }
-        f(rel.doc, shared);
-        propagate(rel.doc, doc, shared);
-      } }
-    }
-    propagate(doc, null, true);
-  }
-
-  // Attach a document to an editor.
-  function attachDoc(cm, doc) {
-    if (doc.cm) { throw new Error("This document is already in use.") }
-    cm.doc = doc;
-    doc.cm = cm;
-    estimateLineHeights(cm);
-    loadMode(cm);
-    setDirectionClass(cm);
-    if (!cm.options.lineWrapping) { findMaxLine(cm); }
-    cm.options.mode = doc.modeOption;
-    regChange(cm);
-  }
-
-  function setDirectionClass(cm) {
-  (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl");
-  }
-
-  function directionChanged(cm) {
-    runInOp(cm, function () {
-      setDirectionClass(cm);
-      regChange(cm);
-    });
-  }
-
-  function History(startGen) {
-    // Arrays of change events and selections. Doing something adds an
-    // event to done and clears undo. Undoing moves events from done
-    // to undone, redoing moves them in the other direction.
-    this.done = []; this.undone = [];
-    this.undoDepth = Infinity;
-    // Used to track when changes can be merged into a single undo
-    // event
-    this.lastModTime = this.lastSelTime = 0;
-    this.lastOp = this.lastSelOp = null;
-    this.lastOrigin = this.lastSelOrigin = null;
-    // Used by the isClean() method
-    this.generation = this.maxGeneration = startGen || 1;
-  }
-
-  // Create a history change event from an updateDoc-style change
-  // object.
-  function historyChangeFromChange(doc, change) {
-    var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
-    attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
-    linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);
-    return histChange
-  }
-
-  // Pop all selection events off the end of a history array. Stop at
-  // a change event.
-  function clearSelectionEvents(array) {
-    while (array.length) {
-      var last = lst(array);
-      if (last.ranges) { array.pop(); }
-      else { break }
-    }
-  }
-
-  // Find the top change event in the history. Pop off selection
-  // events that are in the way.
-  function lastChangeEvent(hist, force) {
-    if (force) {
-      clearSelectionEvents(hist.done);
-      return lst(hist.done)
-    } else if (hist.done.length && !lst(hist.done).ranges) {
-      return lst(hist.done)
-    } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
-      hist.done.pop();
-      return lst(hist.done)
-    }
-  }
-
-  // Register a change in the history. Merges changes that are within
-  // a single operation, or are close together with an origin that
-  // allows merging (starting with "+") into a single event.
-  function addChangeToHistory(doc, change, selAfter, opId) {
-    var hist = doc.history;
-    hist.undone.length = 0;
-    var time = +new Date, cur;
-    var last;
-
-    if ((hist.lastOp == opId ||
-         hist.lastOrigin == change.origin && change.origin &&
-         ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||
-          change.origin.charAt(0) == "*")) &&
-        (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
-      // Merge this change into the last event
-      last = lst(cur.changes);
-      if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
-        // Optimized case for simple insertion -- don't want to add
-        // new changesets for every character typed
-        last.to = changeEnd(change);
-      } else {
-        // Add new sub-event
-        cur.changes.push(historyChangeFromChange(doc, change));
-      }
-    } else {
-      // Can not be merged, start a new event.
-      var before = lst(hist.done);
-      if (!before || !before.ranges)
-        { pushSelectionToHistory(doc.sel, hist.done); }
-      cur = {changes: [historyChangeFromChange(doc, change)],
-             generation: hist.generation};
-      hist.done.push(cur);
-      while (hist.done.length > hist.undoDepth) {
-        hist.done.shift();
-        if (!hist.done[0].ranges) { hist.done.shift(); }
-      }
-    }
-    hist.done.push(selAfter);
-    hist.generation = ++hist.maxGeneration;
-    hist.lastModTime = hist.lastSelTime = time;
-    hist.lastOp = hist.lastSelOp = opId;
-    hist.lastOrigin = hist.lastSelOrigin = change.origin;
-
-    if (!last) { signal(doc, "historyAdded"); }
-  }
-
-  function selectionEventCanBeMerged(doc, origin, prev, sel) {
-    var ch = origin.charAt(0);
-    return ch == "*" ||
-      ch == "+" &&
-      prev.ranges.length == sel.ranges.length &&
-      prev.somethingSelected() == sel.somethingSelected() &&
-      new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
-  }
-
-  // Called whenever the selection changes, sets the new selection as
-  // the pending selection in the history, and pushes the old pending
-  // selection into the 'done' array when it was significantly
-  // different (in number of selected ranges, emptiness, or time).
-  function addSelectionToHistory(doc, sel, opId, options) {
-    var hist = doc.history, origin = options && options.origin;
-
-    // A new event is started when the previous origin does not match
-    // the current, or the origins don't allow matching. Origins
-    // starting with * are always merged, those starting with + are
-    // merged when similar and close together in time.
-    if (opId == hist.lastSelOp ||
-        (origin && hist.lastSelOrigin == origin &&
-         (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
-          selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
-      { hist.done[hist.done.length - 1] = sel; }
-    else
-      { pushSelectionToHistory(sel, hist.done); }
-
-    hist.lastSelTime = +new Date;
-    hist.lastSelOrigin = origin;
-    hist.lastSelOp = opId;
-    if (options && options.clearRedo !== false)
-      { clearSelectionEvents(hist.undone); }
-  }
-
-  function pushSelectionToHistory(sel, dest) {
-    var top = lst(dest);
-    if (!(top && top.ranges && top.equals(sel)))
-      { dest.push(sel); }
-  }
-
-  // Used to store marked span information in the history.
-  function attachLocalSpans(doc, change, from, to) {
-    var existing = change["spans_" + doc.id], n = 0;
-    doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
-      if (line.markedSpans)
-        { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; }
-      ++n;
-    });
-  }
-
-  // When un/re-doing restores text containing marked spans, those
-  // that have been explicitly cleared should not be restored.
-  function removeClearedSpans(spans) {
-    if (!spans) { return null }
-    var out;
-    for (var i = 0; i < spans.length; ++i) {
-      if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }
-      else if (out) { out.push(spans[i]); }
-    }
-    return !out ? spans : out.length ? out : null
-  }
-
-  // Retrieve and filter the old marked spans stored in a change event.
-  function getOldSpans(doc, change) {
-    var found = change["spans_" + doc.id];
-    if (!found) { return null }
-    var nw = [];
-    for (var i = 0; i < change.text.length; ++i)
-      { nw.push(removeClearedSpans(found[i])); }
-    return nw
-  }
-
-  // Used for un/re-doing changes from the history. Combines the
-  // result of computing the existing spans with the set of spans that
-  // existed in the history (so that deleting around a span and then
-  // undoing brings back the span).
-  function mergeOldSpans(doc, change) {
-    var old = getOldSpans(doc, change);
-    var stretched = stretchSpansOverChange(doc, change);
-    if (!old) { return stretched }
-    if (!stretched) { return old }
-
-    for (var i = 0; i < old.length; ++i) {
-      var oldCur = old[i], stretchCur = stretched[i];
-      if (oldCur && stretchCur) {
-        spans: for (var j = 0; j < stretchCur.length; ++j) {
-          var span = stretchCur[j];
-          for (var k = 0; k < oldCur.length; ++k)
-            { if (oldCur[k].marker == span.marker) { continue spans } }
-          oldCur.push(span);
-        }
-      } else if (stretchCur) {
-        old[i] = stretchCur;
-      }
-    }
-    return old
-  }
-
-  // Used both to provide a JSON-safe object in .getHistory, and, when
-  // detaching a document, to split the history in two
-  function copyHistoryArray(events, newGroup, instantiateSel) {
-    var copy = [];
-    for (var i = 0; i < events.length; ++i) {
-      var event = events[i];
-      if (event.ranges) {
-        copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
-        continue
-      }
-      var changes = event.changes, newChanges = [];
-      copy.push({changes: newChanges});
-      for (var j = 0; j < changes.length; ++j) {
-        var change = changes[j], m = (void 0);
-        newChanges.push({from: change.from, to: change.to, text: change.text});
-        if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
-          if (indexOf(newGroup, Number(m[1])) > -1) {
-            lst(newChanges)[prop] = change[prop];
-            delete change[prop];
-          }
-        } } }
-      }
-    }
-    return copy
-  }
-
-  // The 'scroll' parameter given to many of these indicated whether
-  // the new cursor position should be scrolled into view after
-  // modifying the selection.
-
-  // If shift is held or the extend flag is set, extends a range to
-  // include a given position (and optionally a second position).
-  // Otherwise, simply returns the range between the given positions.
-  // Used for cursor motion and such.
-  function extendRange(range, head, other, extend) {
-    if (extend) {
-      var anchor = range.anchor;
-      if (other) {
-        var posBefore = cmp(head, anchor) < 0;
-        if (posBefore != (cmp(other, anchor) < 0)) {
-          anchor = head;
-          head = other;
-        } else if (posBefore != (cmp(head, other) < 0)) {
-          head = other;
-        }
-      }
-      return new Range(anchor, head)
-    } else {
-      return new Range(other || head, head)
-    }
-  }
-
-  // Extend the primary selection range, discard the rest.
-  function extendSelection(doc, head, other, options, extend) {
-    if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }
-    setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);
-  }
-
-  // Extend all selections (pos is an array of selections with length
-  // equal the number of selections)
-  function extendSelections(doc, heads, options) {
-    var out = [];
-    var extend = doc.cm && (doc.cm.display.shift || doc.extend);
-    for (var i = 0; i < doc.sel.ranges.length; i++)
-      { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }
-    var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex);
-    setSelection(doc, newSel, options);
-  }
-
-  // Updates a single range in the selection.
-  function replaceOneSelection(doc, i, range, options) {
-    var ranges = doc.sel.ranges.slice(0);
-    ranges[i] = range;
-    setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options);
-  }
-
-  // Reset the selection to a single range.
-  function setSimpleSelection(doc, anchor, head, options) {
-    setSelection(doc, simpleSelection(anchor, head), options);
-  }
-
-  // Give beforeSelectionChange handlers a change to influence a
-  // selection update.
-  function filterSelectionChange(doc, sel, options) {
-    var obj = {
-      ranges: sel.ranges,
-      update: function(ranges) {
-        this.ranges = [];
-        for (var i = 0; i < ranges.length; i++)
-          { this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
-                                     clipPos(doc, ranges[i].head)); }
-      },
-      origin: options && options.origin
-    };
-    signal(doc, "beforeSelectionChange", doc, obj);
-    if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); }
-    if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) }
-    else { return sel }
-  }
-
-  function setSelectionReplaceHistory(doc, sel, options) {
-    var done = doc.history.done, last = lst(done);
-    if (last && last.ranges) {
-      done[done.length - 1] = sel;
-      setSelectionNoUndo(doc, sel, options);
-    } else {
-      setSelection(doc, sel, options);
-    }
-  }
-
-  // Set a new selection.
-  function setSelection(doc, sel, options) {
-    setSelectionNoUndo(doc, sel, options);
-    addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
-  }
-
-  function setSelectionNoUndo(doc, sel, options) {
-    if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
-      { sel = filterSelectionChange(doc, sel, options); }
-
-    var bias = options && options.bias ||
-      (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
-    setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
-
-    if (!(options && options.scroll === false) && doc.cm)
-      { ensureCursorVisible(doc.cm); }
-  }
-
-  function setSelectionInner(doc, sel) {
-    if (sel.equals(doc.sel)) { return }
-
-    doc.sel = sel;
-
-    if (doc.cm) {
-      doc.cm.curOp.updateInput = 1;
-      doc.cm.curOp.selectionChanged = true;
-      signalCursorActivity(doc.cm);
-    }
-    signalLater(doc, "cursorActivity", doc);
-  }
-
-  // Verify that the selection does not partially select any atomic
-  // marked ranges.
-  function reCheckSelection(doc) {
-    setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));
-  }
-
-  // Return a selection that does not partially select any atomic
-  // ranges.
-  function skipAtomicInSelection(doc, sel, bias, mayClear) {
-    var out;
-    for (var i = 0; i < sel.ranges.length; i++) {
-      var range = sel.ranges[i];
-      var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
-      var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
-      var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
-      if (out || newAnchor != range.anchor || newHead != range.head) {
-        if (!out) { out = sel.ranges.slice(0, i); }
-        out[i] = new Range(newAnchor, newHead);
-      }
-    }
-    return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel
-  }
-
-  function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
-    var line = getLine(doc, pos.line);
-    if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
-      var sp = line.markedSpans[i], m = sp.marker;
-
-      // Determine if we should prevent the cursor being placed to the left/right of an atomic marker
-      // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it
-      // is with selectLeft/Right
-      var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft;
-      var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight;
-
-      if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
-          (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
-        if (mayClear) {
-          signal(m, "beforeCursorEnter");
-          if (m.explicitlyCleared) {
-            if (!line.markedSpans) { break }
-            else {--i; continue}
-          }
-        }
-        if (!m.atomic) { continue }
-
-        if (oldPos) {
-          var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);
-          if (dir < 0 ? preventCursorRight : preventCursorLeft)
-            { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }
-          if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
-            { return skipAtomicInner(doc, near, pos, dir, mayClear) }
-        }
-
-        var far = m.find(dir < 0 ? -1 : 1);
-        if (dir < 0 ? preventCursorLeft : preventCursorRight)
-          { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }
-        return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
-      }
-    } }
-    return pos
-  }
-
-  // Ensure a given position is not inside an atomic range.
-  function skipAtomic(doc, pos, oldPos, bias, mayClear) {
-    var dir = bias || 1;
-    var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
-        (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
-        skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
-        (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
-    if (!found) {
-      doc.cantEdit = true;
-      return Pos(doc.first, 0)
-    }
-    return found
-  }
-
-  function movePos(doc, pos, dir, line) {
-    if (dir < 0 && pos.ch == 0) {
-      if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
-      else { return null }
-    } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
-      if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
-      else { return null }
-    } else {
-      return new Pos(pos.line, pos.ch + dir)
-    }
-  }
-
-  function selectAll(cm) {
-    cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);
-  }
-
-  // UPDATING
-
-  // Allow "beforeChange" event handlers to influence a change
-  function filterChange(doc, change, update) {
-    var obj = {
-      canceled: false,
-      from: change.from,
-      to: change.to,
-      text: change.text,
-      origin: change.origin,
-      cancel: function () { return obj.canceled = true; }
-    };
-    if (update) { obj.update = function (from, to, text, origin) {
-      if (from) { obj.from = clipPos(doc, from); }
-      if (to) { obj.to = clipPos(doc, to); }
-      if (text) { obj.text = text; }
-      if (origin !== undefined) { obj.origin = origin; }
-    }; }
-    signal(doc, "beforeChange", doc, obj);
-    if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); }
-
-    if (obj.canceled) {
-      if (doc.cm) { doc.cm.curOp.updateInput = 2; }
-      return null
-    }
-    return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
-  }
-
-  // Apply a change to a document, and add it to the document's
-  // history, and propagating it to all linked documents.
-  function makeChange(doc, change, ignoreReadOnly) {
-    if (doc.cm) {
-      if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
-      if (doc.cm.state.suppressEdits) { return }
-    }
-
-    if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
-      change = filterChange(doc, change, true);
-      if (!change) { return }
-    }
-
-    // Possibly split or suppress the update based on the presence
-    // of read-only spans in its range.
-    var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
-    if (split) {
-      for (var i = split.length - 1; i >= 0; --i)
-        { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); }
-    } else {
-      makeChangeInner(doc, change);
-    }
-  }
-
-  function makeChangeInner(doc, change) {
-    if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
-    var selAfter = computeSelAfterChange(doc, change);
-    addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
-
-    makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
-    var rebased = [];
-
-    linkedDocs(doc, function (doc, sharedHist) {
-      if (!sharedHist && indexOf(rebased, doc.history) == -1) {
-        rebaseHist(doc.history, change);
-        rebased.push(doc.history);
-      }
-      makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
-    });
-  }
-
-  // Revert a change stored in a document's history.
-  function makeChangeFromHistory(doc, type, allowSelectionOnly) {
-    var suppress = doc.cm && doc.cm.state.suppressEdits;
-    if (suppress && !allowSelectionOnly) { return }
-
-    var hist = doc.history, event, selAfter = doc.sel;
-    var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
-
-    // Verify that there is a useable event (so that ctrl-z won't
-    // needlessly clear selection events)
-    var i = 0;
-    for (; i < source.length; i++) {
-      event = source[i];
-      if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
-        { break }
-    }
-    if (i == source.length) { return }
-    hist.lastOrigin = hist.lastSelOrigin = null;
-
-    for (;;) {
-      event = source.pop();
-      if (event.ranges) {
-        pushSelectionToHistory(event, dest);
-        if (allowSelectionOnly && !event.equals(doc.sel)) {
-          setSelection(doc, event, {clearRedo: false});
-          return
-        }
-        selAfter = event;
-      } else if (suppress) {
-        source.push(event);
-        return
-      } else { break }
-    }
-
-    // Build up a reverse change object to add to the opposite history
-    // stack (redo when undoing, and vice versa).
-    var antiChanges = [];
-    pushSelectionToHistory(selAfter, dest);
-    dest.push({changes: antiChanges, generation: hist.generation});
-    hist.generation = event.generation || ++hist.maxGeneration;
-
-    var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
-
-    var loop = function ( i ) {
-      var change = event.changes[i];
-      change.origin = type;
-      if (filter && !filterChange(doc, change, false)) {
-        source.length = 0;
-        return {}
-      }
-
-      antiChanges.push(historyChangeFromChange(doc, change));
-
-      var after = i ? computeSelAfterChange(doc, change) : lst(source);
-      makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
-      if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }
-      var rebased = [];
-
-      // Propagate to the linked documents
-      linkedDocs(doc, function (doc, sharedHist) {
-        if (!sharedHist && indexOf(rebased, doc.history) == -1) {
-          rebaseHist(doc.history, change);
-          rebased.push(doc.history);
-        }
-        makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
-      });
-    };
-
-    for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
-      var returned = loop( i$1 );
-
-      if ( returned ) return returned.v;
-    }
-  }
-
-  // Sub-views need their line numbers shifted when text is added
-  // above or below them in the parent document.
-  function shiftDoc(doc, distance) {
-    if (distance == 0) { return }
-    doc.first += distance;
-    doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
-      Pos(range.anchor.line + distance, range.anchor.ch),
-      Pos(range.head.line + distance, range.head.ch)
-    ); }), doc.sel.primIndex);
-    if (doc.cm) {
-      regChange(doc.cm, doc.first, doc.first - distance, distance);
-      for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
-        { regLineChange(doc.cm, l, "gutter"); }
-    }
-  }
-
-  // More lower-level change function, handling only a single document
-  // (not linked ones).
-  function makeChangeSingleDoc(doc, change, selAfter, spans) {
-    if (doc.cm && !doc.cm.curOp)
-      { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
-
-    if (change.to.line < doc.first) {
-      shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
-      return
-    }
-    if (change.from.line > doc.lastLine()) { return }
-
-    // Clip the change to the size of this doc
-    if (change.from.line < doc.first) {
-      var shift = change.text.length - 1 - (doc.first - change.from.line);
-      shiftDoc(doc, shift);
-      change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
-                text: [lst(change.text)], origin: change.origin};
-    }
-    var last = doc.lastLine();
-    if (change.to.line > last) {
-      change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
-                text: [change.text[0]], origin: change.origin};
-    }
-
-    change.removed = getBetween(doc, change.from, change.to);
-
-    if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }
-    if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }
-    else { updateDoc(doc, change, spans); }
-    setSelectionNoUndo(doc, selAfter, sel_dontScroll);
-
-    if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0)))
-      { doc.cantEdit = false; }
-  }
-
-  // Handle the interaction of a change to a document with the editor
-  // that this document is part of.
-  function makeChangeSingleDocInEditor(cm, change, spans) {
-    var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
-
-    var recomputeMaxLength = false, checkWidthStart = from.line;
-    if (!cm.options.lineWrapping) {
-      checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
-      doc.iter(checkWidthStart, to.line + 1, function (line) {
-        if (line == display.maxLine) {
-          recomputeMaxLength = true;
-          return true
-        }
-      });
-    }
-
-    if (doc.sel.contains(change.from, change.to) > -1)
-      { signalCursorActivity(cm); }
-
-    updateDoc(doc, change, spans, estimateHeight(cm));
-
-    if (!cm.options.lineWrapping) {
-      doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
-        var len = lineLength(line);
-        if (len > display.maxLineLength) {
-          display.maxLine = line;
-          display.maxLineLength = len;
-          display.maxLineChanged = true;
-          recomputeMaxLength = false;
-        }
-      });
-      if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }
-    }
-
-    retreatFrontier(doc, from.line);
-    startWorker(cm, 400);
-
-    var lendiff = change.text.length - (to.line - from.line) - 1;
-    // Remember that these lines changed, for updating the display
-    if (change.full)
-      { regChange(cm); }
-    else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
-      { regLineChange(cm, from.line, "text"); }
-    else
-      { regChange(cm, from.line, to.line + 1, lendiff); }
-
-    var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
-    if (changeHandler || changesHandler) {
-      var obj = {
-        from: from, to: to,
-        text: change.text,
-        removed: change.removed,
-        origin: change.origin
-      };
-      if (changeHandler) { signalLater(cm, "change", cm, obj); }
-      if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }
-    }
-    cm.display.selForContextMenu = null;
-  }
-
-  function replaceRange(doc, code, from, to, origin) {
-    var assign;
-
-    if (!to) { to = from; }
-    if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); }
-    if (typeof code == "string") { code = doc.splitLines(code); }
-    makeChange(doc, {from: from, to: to, text: code, origin: origin});
-  }
-
-  // Rebasing/resetting history to deal with externally-sourced changes
-
-  function rebaseHistSelSingle(pos, from, to, diff) {
-    if (to < pos.line) {
-      pos.line += diff;
-    } else if (from < pos.line) {
-      pos.line = from;
-      pos.ch = 0;
-    }
-  }
-
-  // Tries to rebase an array of history events given a change in the
-  // document. If the change touches the same lines as the event, the
-  // event, and everything 'behind' it, is discarded. If the change is
-  // before the event, the event's positions are updated. Uses a
-  // copy-on-write scheme for the positions, to avoid having to
-  // reallocate them all on every rebase, but also avoid problems with
-  // shared position objects being unsafely updated.
-  function rebaseHistArray(array, from, to, diff) {
-    for (var i = 0; i < array.length; ++i) {
-      var sub = array[i], ok = true;
-      if (sub.ranges) {
-        if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
-        for (var j = 0; j < sub.ranges.length; j++) {
-          rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
-          rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
-        }
-        continue
-      }
-      for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
-        var cur = sub.changes[j$1];
-        if (to < cur.from.line) {
-          cur.from = Pos(cur.from.line + diff, cur.from.ch);
-          cur.to = Pos(cur.to.line + diff, cur.to.ch);
-        } else if (from <= cur.to.line) {
-          ok = false;
-          break
-        }
-      }
-      if (!ok) {
-        array.splice(0, i + 1);
-        i = 0;
-      }
-    }
-  }
-
-  function rebaseHist(hist, change) {
-    var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
-    rebaseHistArray(hist.done, from, to, diff);
-    rebaseHistArray(hist.undone, from, to, diff);
-  }
-
-  // Utility for applying a change to a line by handle or number,
-  // returning the number and optionally registering the line as
-  // changed.
-  function changeLine(doc, handle, changeType, op) {
-    var no = handle, line = handle;
-    if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); }
-    else { no = lineNo(handle); }
-    if (no == null) { return null }
-    if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }
-    return line
-  }
-
-  // The document is represented as a BTree consisting of leaves, with
-  // chunk of lines in them, and branches, with up to ten leaves or
-  // other branch nodes below them. The top node is always a branch
-  // node, and is the document object itself (meaning it has
-  // additional methods and properties).
-  //
-  // All nodes have parent links. The tree is used both to go from
-  // line numbers to line objects, and to go from objects to numbers.
-  // It also indexes by height, and is used to convert between height
-  // and line object, and to find the total height of the document.
-  //
-  // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
-
-  function LeafChunk(lines) {
-    this.lines = lines;
-    this.parent = null;
-    var height = 0;
-    for (var i = 0; i < lines.length; ++i) {
-      lines[i].parent = this;
-      height += lines[i].height;
-    }
-    this.height = height;
-  }
-
-  LeafChunk.prototype = {
-    chunkSize: function() { return this.lines.length },
-
-    // Remove the n lines at offset 'at'.
-    removeInner: function(at, n) {
-      for (var i = at, e = at + n; i < e; ++i) {
-        var line = this.lines[i];
-        this.height -= line.height;
-        cleanUpLine(line);
-        signalLater(line, "delete");
-      }
-      this.lines.splice(at, n);
-    },
-
-    // Helper used to collapse a small branch into a single leaf.
-    collapse: function(lines) {
-      lines.push.apply(lines, this.lines);
-    },
-
-    // Insert the given array of lines at offset 'at', count them as
-    // having the given height.
-    insertInner: function(at, lines, height) {
-      this.height += height;
-      this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
-      for (var i = 0; i < lines.length; ++i) { lines[i].parent = this; }
-    },
-
-    // Used to iterate over a part of the tree.
-    iterN: function(at, n, op) {
-      for (var e = at + n; at < e; ++at)
-        { if (op(this.lines[at])) { return true } }
-    }
-  };
-
-  function BranchChunk(children) {
-    this.children = children;
-    var size = 0, height = 0;
-    for (var i = 0; i < children.length; ++i) {
-      var ch = children[i];
-      size += ch.chunkSize(); height += ch.height;
-      ch.parent = this;
-    }
-    this.size = size;
-    this.height = height;
-    this.parent = null;
-  }
-
-  BranchChunk.prototype = {
-    chunkSize: function() { return this.size },
-
-    removeInner: function(at, n) {
-      this.size -= n;
-      for (var i = 0; i < this.children.length; ++i) {
-        var child = this.children[i], sz = child.chunkSize();
-        if (at < sz) {
-          var rm = Math.min(n, sz - at), oldHeight = child.height;
-          child.removeInner(at, rm);
-          this.height -= oldHeight - child.height;
-          if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
-          if ((n -= rm) == 0) { break }
-          at = 0;
-        } else { at -= sz; }
-      }
-      // If the result is smaller than 25 lines, ensure that it is a
-      // single leaf node.
-      if (this.size - n < 25 &&
-          (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
-        var lines = [];
-        this.collapse(lines);
-        this.children = [new LeafChunk(lines)];
-        this.children[0].parent = this;
-      }
-    },
-
-    collapse: function(lines) {
-      for (var i = 0; i < this.children.length; ++i) { this.children[i].collapse(lines); }
-    },
-
-    insertInner: function(at, lines, height) {
-      this.size += lines.length;
-      this.height += height;
-      for (var i = 0; i < this.children.length; ++i) {
-        var child = this.children[i], sz = child.chunkSize();
-        if (at <= sz) {
-          child.insertInner(at, lines, height);
-          if (child.lines && child.lines.length > 50) {
-            // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
-            // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
-            var remaining = child.lines.length % 25 + 25;
-            for (var pos = remaining; pos < child.lines.length;) {
-              var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
-              child.height -= leaf.height;
-              this.children.splice(++i, 0, leaf);
-              leaf.parent = this;
-            }
-            child.lines = child.lines.slice(0, remaining);
-            this.maybeSpill();
-          }
-          break
-        }
-        at -= sz;
-      }
-    },
-
-    // When a node has grown, check whether it should be split.
-    maybeSpill: function() {
-      if (this.children.length <= 10) { return }
-      var me = this;
-      do {
-        var spilled = me.children.splice(me.children.length - 5, 5);
-        var sibling = new BranchChunk(spilled);
-        if (!me.parent) { // Become the parent node
-          var copy = new BranchChunk(me.children);
-          copy.parent = me;
-          me.children = [copy, sibling];
-          me = copy;
-       } else {
-          me.size -= sibling.size;
-          me.height -= sibling.height;
-          var myIndex = indexOf(me.parent.children, me);
-          me.parent.children.splice(myIndex + 1, 0, sibling);
-        }
-        sibling.parent = me.parent;
-      } while (me.children.length > 10)
-      me.parent.maybeSpill();
-    },
-
-    iterN: function(at, n, op) {
-      for (var i = 0; i < this.children.length; ++i) {
-        var child = this.children[i], sz = child.chunkSize();
-        if (at < sz) {
-          var used = Math.min(n, sz - at);
-          if (child.iterN(at, used, op)) { return true }
-          if ((n -= used) == 0) { break }
-          at = 0;
-        } else { at -= sz; }
-      }
-    }
-  };
-
-  // Line widgets are block elements displayed above or below a line.
-
-  var LineWidget = function(doc, node, options) {
-    if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
-      { this[opt] = options[opt]; } } }
-    this.doc = doc;
-    this.node = node;
-  };
-
-  LineWidget.prototype.clear = function () {
-    var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
-    if (no == null || !ws) { return }
-    for (var i = 0; i < ws.length; ++i) { if (ws[i] == this) { ws.splice(i--, 1); } }
-    if (!ws.length) { line.widgets = null; }
-    var height = widgetHeight(this);
-    updateLineHeight(line, Math.max(0, line.height - height));
-    if (cm) {
-      runInOp(cm, function () {
-        adjustScrollWhenAboveVisible(cm, line, -height);
-        regLineChange(cm, no, "widget");
-      });
-      signalLater(cm, "lineWidgetCleared", cm, this, no);
-    }
-  };
-
-  LineWidget.prototype.changed = function () {
-      var this$1 = this;
-
-    var oldH = this.height, cm = this.doc.cm, line = this.line;
-    this.height = null;
-    var diff = widgetHeight(this) - oldH;
-    if (!diff) { return }
-    if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); }
-    if (cm) {
-      runInOp(cm, function () {
-        cm.curOp.forceUpdate = true;
-        adjustScrollWhenAboveVisible(cm, line, diff);
-        signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line));
-      });
-    }
-  };
-  eventMixin(LineWidget);
-
-  function adjustScrollWhenAboveVisible(cm, line, diff) {
-    if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
-      { addToScrollTop(cm, diff); }
-  }
-
-  function addLineWidget(doc, handle, node, options) {
-    var widget = new LineWidget(doc, node, options);
-    var cm = doc.cm;
-    if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }
-    changeLine(doc, handle, "widget", function (line) {
-      var widgets = line.widgets || (line.widgets = []);
-      if (widget.insertAt == null) { widgets.push(widget); }
-      else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); }
-      widget.line = line;
-      if (cm && !lineIsHidden(doc, line)) {
-        var aboveVisible = heightAtLine(line) < doc.scrollTop;
-        updateLineHeight(line, line.height + widgetHeight(widget));
-        if (aboveVisible) { addToScrollTop(cm, widget.height); }
-        cm.curOp.forceUpdate = true;
-      }
-      return true
-    });
-    if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); }
-    return widget
-  }
-
-  // TEXTMARKERS
-
-  // Created with markText and setBookmark methods. A TextMarker is a
-  // handle that can be used to clear or find a marked position in the
-  // document. Line objects hold arrays (markedSpans) containing
-  // {from, to, marker} object pointing to such marker objects, and
-  // indicating that such a marker is present on that line. Multiple
-  // lines may point to the same marker when it spans across lines.
-  // The spans will have null for their from/to properties when the
-  // marker continues beyond the start/end of the line. Markers have
-  // links back to the lines they currently touch.
-
-  // Collapsed markers have unique ids, in order to be able to order
-  // them, which is needed for uniquely determining an outer marker
-  // when they overlap (they may nest, but not partially overlap).
-  var nextMarkerId = 0;
-
-  var TextMarker = function(doc, type) {
-    this.lines = [];
-    this.type = type;
-    this.doc = doc;
-    this.id = ++nextMarkerId;
-  };
-
-  // Clear the marker.
-  TextMarker.prototype.clear = function () {
-    if (this.explicitlyCleared) { return }
-    var cm = this.doc.cm, withOp = cm && !cm.curOp;
-    if (withOp) { startOperation(cm); }
-    if (hasHandler(this, "clear")) {
-      var found = this.find();
-      if (found) { signalLater(this, "clear", found.from, found.to); }
-    }
-    var min = null, max = null;
-    for (var i = 0; i < this.lines.length; ++i) {
-      var line = this.lines[i];
-      var span = getMarkedSpanFor(line.markedSpans, this);
-      if (cm && !this.collapsed) { regLineChange(cm, lineNo(line), "text"); }
-      else if (cm) {
-        if (span.to != null) { max = lineNo(line); }
-        if (span.from != null) { min = lineNo(line); }
-      }
-      line.markedSpans = removeMarkedSpan(line.markedSpans, span);
-      if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm)
-        { updateLineHeight(line, textHeight(cm.display)); }
-    }
-    if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
-      var visual = visualLine(this.lines[i$1]), len = lineLength(visual);
-      if (len > cm.display.maxLineLength) {
-        cm.display.maxLine = visual;
-        cm.display.maxLineLength = len;
-        cm.display.maxLineChanged = true;
-      }
-    } }
-
-    if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }
-    this.lines.length = 0;
-    this.explicitlyCleared = true;
-    if (this.atomic && this.doc.cantEdit) {
-      this.doc.cantEdit = false;
-      if (cm) { reCheckSelection(cm.doc); }
-    }
-    if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); }
-    if (withOp) { endOperation(cm); }
-    if (this.parent) { this.parent.clear(); }
-  };
-
-  // Find the position of the marker in the document. Returns a {from,
-  // to} object by default. Side can be passed to get a specific side
-  // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
-  // Pos objects returned contain a line object, rather than a line
-  // number (used to prevent looking up the same line twice).
-  TextMarker.prototype.find = function (side, lineObj) {
-    if (side == null && this.type == "bookmark") { side = 1; }
-    var from, to;
-    for (var i = 0; i < this.lines.length; ++i) {
-      var line = this.lines[i];
-      var span = getMarkedSpanFor(line.markedSpans, this);
-      if (span.from != null) {
-        from = Pos(lineObj ? line : lineNo(line), span.from);
-        if (side == -1) { return from }
-      }
-      if (span.to != null) {
-        to = Pos(lineObj ? line : lineNo(line), span.to);
-        if (side == 1) { return to }
-      }
-    }
-    return from && {from: from, to: to}
-  };
-
-  // Signals that the marker's widget changed, and surrounding layout
-  // should be recomputed.
-  TextMarker.prototype.changed = function () {
-      var this$1 = this;
-
-    var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
-    if (!pos || !cm) { return }
-    runInOp(cm, function () {
-      var line = pos.line, lineN = lineNo(pos.line);
-      var view = findViewForLine(cm, lineN);
-      if (view) {
-        clearLineMeasurementCacheFor(view);
-        cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
-      }
-      cm.curOp.updateMaxLine = true;
-      if (!lineIsHidden(widget.doc, line) && widget.height != null) {
-        var oldHeight = widget.height;
-        widget.height = null;
-        var dHeight = widgetHeight(widget) - oldHeight;
-        if (dHeight)
-          { updateLineHeight(line, line.height + dHeight); }
-      }
-      signalLater(cm, "markerChanged", cm, this$1);
-    });
-  };
-
-  TextMarker.prototype.attachLine = function (line) {
-    if (!this.lines.length && this.doc.cm) {
-      var op = this.doc.cm.curOp;
-      if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
-        { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }
-    }
-    this.lines.push(line);
-  };
-
-  TextMarker.prototype.detachLine = function (line) {
-    this.lines.splice(indexOf(this.lines, line), 1);
-    if (!this.lines.length && this.doc.cm) {
-      var op = this.doc.cm.curOp
-      ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
-    }
-  };
-  eventMixin(TextMarker);
-
-  // Create a marker, wire it up to the right lines, and
-  function markText(doc, from, to, options, type) {
-    // Shared markers (across linked documents) are handled separately
-    // (markTextShared will call out to this again, once per
-    // document).
-    if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
-    // Ensure we are in an operation.
-    if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
-
-    var marker = new TextMarker(doc, type), diff = cmp(from, to);
-    if (options) { copyObj(options, marker, false); }
-    // Don't connect empty markers unless clearWhenEmpty is false
-    if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
-      { return marker }
-    if (marker.replacedWith) {
-      // Showing up as a widget implies collapsed (widget replaces text)
-      marker.collapsed = true;
-      marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget");
-      if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); }
-      if (options.insertLeft) { marker.widgetNode.insertLeft = true; }
-    }
-    if (marker.collapsed) {
-      if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
-          from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
-        { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
-      seeCollapsedSpans();
-    }
-
-    if (marker.addToHistory)
-      { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); }
-
-    var curLine = from.line, cm = doc.cm, updateMaxLine;
-    doc.iter(curLine, to.line + 1, function (line) {
-      if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
-        { updateMaxLine = true; }
-      if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }
-      addMarkedSpan(line, new MarkedSpan(marker,
-                                         curLine == from.line ? from.ch : null,
-                                         curLine == to.line ? to.ch : null));
-      ++curLine;
-    });
-    // lineIsHidden depends on the presence of the spans, so needs a second pass
-    if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
-      if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }
-    }); }
-
-    if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); }
-
-    if (marker.readOnly) {
-      seeReadOnlySpans();
-      if (doc.history.done.length || doc.history.undone.length)
-        { doc.clearHistory(); }
-    }
-    if (marker.collapsed) {
-      marker.id = ++nextMarkerId;
-      marker.atomic = true;
-    }
-    if (cm) {
-      // Sync editor state
-      if (updateMaxLine) { cm.curOp.updateMaxLine = true; }
-      if (marker.collapsed)
-        { regChange(cm, from.line, to.line + 1); }
-      else if (marker.className || marker.startStyle || marker.endStyle || marker.css ||
-               marker.attributes || marker.title)
-        { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } }
-      if (marker.atomic) { reCheckSelection(cm.doc); }
-      signalLater(cm, "markerAdded", cm, marker);
-    }
-    return marker
-  }
-
-  // SHARED TEXTMARKERS
-
-  // A shared marker spans multiple linked documents. It is
-  // implemented as a meta-marker-object controlling multiple normal
-  // markers.
-  var SharedTextMarker = function(markers, primary) {
-    this.markers = markers;
-    this.primary = primary;
-    for (var i = 0; i < markers.length; ++i)
-      { markers[i].parent = this; }
-  };
-
-  SharedTextMarker.prototype.clear = function () {
-    if (this.explicitlyCleared) { return }
-    this.explicitlyCleared = true;
-    for (var i = 0; i < this.markers.length; ++i)
-      { this.markers[i].clear(); }
-    signalLater(this, "clear");
-  };
-
-  SharedTextMarker.prototype.find = function (side, lineObj) {
-    return this.primary.find(side, lineObj)
-  };
-  eventMixin(SharedTextMarker);
-
-  function markTextShared(doc, from, to, options, type) {
-    options = copyObj(options);
-    options.shared = false;
-    var markers = [markText(doc, from, to, options, type)], primary = markers[0];
-    var widget = options.widgetNode;
-    linkedDocs(doc, function (doc) {
-      if (widget) { options.widgetNode = widget.cloneNode(true); }
-      markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
-      for (var i = 0; i < doc.linked.length; ++i)
-        { if (doc.linked[i].isParent) { return } }
-      primary = lst(markers);
-    });
-    return new SharedTextMarker(markers, primary)
-  }
-
-  function findSharedMarkers(doc) {
-    return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
-  }
-
-  function copySharedMarkers(doc, markers) {
-    for (var i = 0; i < markers.length; i++) {
-      var marker = markers[i], pos = marker.find();
-      var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
-      if (cmp(mFrom, mTo)) {
-        var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
-        marker.markers.push(subMark);
-        subMark.parent = marker;
-      }
-    }
-  }
-
-  function detachSharedMarkers(markers) {
-    var loop = function ( i ) {
-      var marker = markers[i], linked = [marker.primary.doc];
-      linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });
-      for (var j = 0; j < marker.markers.length; j++) {
-        var subMarker = marker.markers[j];
-        if (indexOf(linked, subMarker.doc) == -1) {
-          subMarker.parent = null;
-          marker.markers.splice(j--, 1);
-        }
-      }
-    };
-
-    for (var i = 0; i < markers.length; i++) loop( i );
-  }
-
-  var nextDocId = 0;
-  var Doc = function(text, mode, firstLine, lineSep, direction) {
-    if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
-    if (firstLine == null) { firstLine = 0; }
-
-    BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
-    this.first = firstLine;
-    this.scrollTop = this.scrollLeft = 0;
-    this.cantEdit = false;
-    this.cleanGeneration = 1;
-    this.modeFrontier = this.highlightFrontier = firstLine;
-    var start = Pos(firstLine, 0);
-    this.sel = simpleSelection(start);
-    this.history = new History(null);
-    this.id = ++nextDocId;
-    this.modeOption = mode;
-    this.lineSep = lineSep;
-    this.direction = (direction == "rtl") ? "rtl" : "ltr";
-    this.extend = false;
-
-    if (typeof text == "string") { text = this.splitLines(text); }
-    updateDoc(this, {from: start, to: start, text: text});
-    setSelection(this, simpleSelection(start), sel_dontScroll);
-  };
-
-  Doc.prototype = createObj(BranchChunk.prototype, {
-    constructor: Doc,
-    // Iterate over the document. Supports two forms -- with only one
-    // argument, it calls that for each line in the document. With
-    // three, it iterates over the range given by the first two (with
-    // the second being non-inclusive).
-    iter: function(from, to, op) {
-      if (op) { this.iterN(from - this.first, to - from, op); }
-      else { this.iterN(this.first, this.first + this.size, from); }
-    },
-
-    // Non-public interface for adding and removing lines.
-    insert: function(at, lines) {
-      var height = 0;
-      for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }
-      this.insertInner(at - this.first, lines, height);
-    },
-    remove: function(at, n) { this.removeInner(at - this.first, n); },
-
-    // From here, the methods are part of the public interface. Most
-    // are also available from CodeMirror (editor) instances.
-
-    getValue: function(lineSep) {
-      var lines = getLines(this, this.first, this.first + this.size);
-      if (lineSep === false) { return lines }
-      return lines.join(lineSep || this.lineSeparator())
-    },
-    setValue: docMethodOp(function(code) {
-      var top = Pos(this.first, 0), last = this.first + this.size - 1;
-      makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
-                        text: this.splitLines(code), origin: "setValue", full: true}, true);
-      if (this.cm) { scrollToCoords(this.cm, 0, 0); }
-      setSelection(this, simpleSelection(top), sel_dontScroll);
-    }),
-    replaceRange: function(code, from, to, origin) {
-      from = clipPos(this, from);
-      to = to ? clipPos(this, to) : from;
-      replaceRange(this, code, from, to, origin);
-    },
-    getRange: function(from, to, lineSep) {
-      var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
-      if (lineSep === false) { return lines }
-      return lines.join(lineSep || this.lineSeparator())
-    },
-
-    getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
-
-    getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
-    getLineNumber: function(line) {return lineNo(line)},
-
-    getLineHandleVisualStart: function(line) {
-      if (typeof line == "number") { line = getLine(this, line); }
-      return visualLine(line)
-    },
-
-    lineCount: function() {return this.size},
-    firstLine: function() {return this.first},
-    lastLine: function() {return this.first + this.size - 1},
-
-    clipPos: function(pos) {return clipPos(this, pos)},
-
-    getCursor: function(start) {
-      var range = this.sel.primary(), pos;
-      if (start == null || start == "head") { pos = range.head; }
-      else if (start == "anchor") { pos = range.anchor; }
-      else if (start == "end" || start == "to" || start === false) { pos = range.to(); }
-      else { pos = range.from(); }
-      return pos
-    },
-    listSelections: function() { return this.sel.ranges },
-    somethingSelected: function() {return this.sel.somethingSelected()},
-
-    setCursor: docMethodOp(function(line, ch, options) {
-      setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
-    }),
-    setSelection: docMethodOp(function(anchor, head, options) {
-      setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
-    }),
-    extendSelection: docMethodOp(function(head, other, options) {
-      extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
-    }),
-    extendSelections: docMethodOp(function(heads, options) {
-      extendSelections(this, clipPosArray(this, heads), options);
-    }),
-    extendSelectionsBy: docMethodOp(function(f, options) {
-      var heads = map(this.sel.ranges, f);
-      extendSelections(this, clipPosArray(this, heads), options);
-    }),
-    setSelections: docMethodOp(function(ranges, primary, options) {
-      if (!ranges.length) { return }
-      var out = [];
-      for (var i = 0; i < ranges.length; i++)
-        { out[i] = new Range(clipPos(this, ranges[i].anchor),
-                           clipPos(this, ranges[i].head)); }
-      if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }
-      setSelection(this, normalizeSelection(this.cm, out, primary), options);
-    }),
-    addSelection: docMethodOp(function(anchor, head, options) {
-      var ranges = this.sel.ranges.slice(0);
-      ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
-      setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options);
-    }),
-
-    getSelection: function(lineSep) {
-      var ranges = this.sel.ranges, lines;
-      for (var i = 0; i < ranges.length; i++) {
-        var sel = getBetween(this, ranges[i].from(), ranges[i].to());
-        lines = lines ? lines.concat(sel) : sel;
-      }
-      if (lineSep === false) { return lines }
-      else { return lines.join(lineSep || this.lineSeparator()) }
-    },
-    getSelections: function(lineSep) {
-      var parts = [], ranges = this.sel.ranges;
-      for (var i = 0; i < ranges.length; i++) {
-        var sel = getBetween(this, ranges[i].from(), ranges[i].to());
-        if (lineSep !== false) { sel = sel.join(lineSep || this.lineSeparator()); }
-        parts[i] = sel;
-      }
-      return parts
-    },
-    replaceSelection: function(code, collapse, origin) {
-      var dup = [];
-      for (var i = 0; i < this.sel.ranges.length; i++)
-        { dup[i] = code; }
-      this.replaceSelections(dup, collapse, origin || "+input");
-    },
-    replaceSelections: docMethodOp(function(code, collapse, origin) {
-      var changes = [], sel = this.sel;
-      for (var i = 0; i < sel.ranges.length; i++) {
-        var range = sel.ranges[i];
-        changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin};
-      }
-      var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
-      for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
-        { makeChange(this, changes[i$1]); }
-      if (newSel) { setSelectionReplaceHistory(this, newSel); }
-      else if (this.cm) { ensureCursorVisible(this.cm); }
-    }),
-    undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
-    redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
-    undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
-    redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
-
-    setExtending: function(val) {this.extend = val;},
-    getExtending: function() {return this.extend},
-
-    historySize: function() {
-      var hist = this.history, done = 0, undone = 0;
-      for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }
-      for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }
-      return {undo: done, redo: undone}
-    },
-    clearHistory: function() {
-      var this$1 = this;
-
-      this.history = new History(this.history.maxGeneration);
-      linkedDocs(this, function (doc) { return doc.history = this$1.history; }, true);
-    },
-
-    markClean: function() {
-      this.cleanGeneration = this.changeGeneration(true);
-    },
-    changeGeneration: function(forceSplit) {
-      if (forceSplit)
-        { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }
-      return this.history.generation
-    },
-    isClean: function (gen) {
-      return this.history.generation == (gen || this.cleanGeneration)
-    },
-
-    getHistory: function() {
-      return {done: copyHistoryArray(this.history.done),
-              undone: copyHistoryArray(this.history.undone)}
-    },
-    setHistory: function(histData) {
-      var hist = this.history = new History(this.history.maxGeneration);
-      hist.done = copyHistoryArray(histData.done.slice(0), null, true);
-      hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
-    },
-
-    setGutterMarker: docMethodOp(function(line, gutterID, value) {
-      return changeLine(this, line, "gutter", function (line) {
-        var markers = line.gutterMarkers || (line.gutterMarkers = {});
-        markers[gutterID] = value;
-        if (!value && isEmpty(markers)) { line.gutterMarkers = null; }
-        return true
-      })
-    }),
-
-    clearGutter: docMethodOp(function(gutterID) {
-      var this$1 = this;
-
-      this.iter(function (line) {
-        if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
-          changeLine(this$1, line, "gutter", function () {
-            line.gutterMarkers[gutterID] = null;
-            if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }
-            return true
-          });
-        }
-      });
-    }),
-
-    lineInfo: function(line) {
-      var n;
-      if (typeof line == "number") {
-        if (!isLine(this, line)) { return null }
-        n = line;
-        line = getLine(this, line);
-        if (!line) { return null }
-      } else {
-        n = lineNo(line);
-        if (n == null) { return null }
-      }
-      return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
-              textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
-              widgets: line.widgets}
-    },
-
-    addLineClass: docMethodOp(function(handle, where, cls) {
-      return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
-        var prop = where == "text" ? "textClass"
-                 : where == "background" ? "bgClass"
-                 : where == "gutter" ? "gutterClass" : "wrapClass";
-        if (!line[prop]) { line[prop] = cls; }
-        else if (classTest(cls).test(line[prop])) { return false }
-        else { line[prop] += " " + cls; }
-        return true
-      })
-    }),
-    removeLineClass: docMethodOp(function(handle, where, cls) {
-      return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
-        var prop = where == "text" ? "textClass"
-                 : where == "background" ? "bgClass"
-                 : where == "gutter" ? "gutterClass" : "wrapClass";
-        var cur = line[prop];
-        if (!cur) { return false }
-        else if (cls == null) { line[prop] = null; }
-        else {
-          var found = cur.match(classTest(cls));
-          if (!found) { return false }
-          var end = found.index + found[0].length;
-          line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
-        }
-        return true
-      })
-    }),
-
-    addLineWidget: docMethodOp(function(handle, node, options) {
-      return addLineWidget(this, handle, node, options)
-    }),
-    removeLineWidget: function(widget) { widget.clear(); },
-
-    markText: function(from, to, options) {
-      return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
-    },
-    setBookmark: function(pos, options) {
-      var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
-                      insertLeft: options && options.insertLeft,
-                      clearWhenEmpty: false, shared: options && options.shared,
-                      handleMouseEvents: options && options.handleMouseEvents};
-      pos = clipPos(this, pos);
-      return markText(this, pos, pos, realOpts, "bookmark")
-    },
-    findMarksAt: function(pos) {
-      pos = clipPos(this, pos);
-      var markers = [], spans = getLine(this, pos.line).markedSpans;
-      if (spans) { for (var i = 0; i < spans.length; ++i) {
-        var span = spans[i];
-        if ((span.from == null || span.from <= pos.ch) &&
-            (span.to == null || span.to >= pos.ch))
-          { markers.push(span.marker.parent || span.marker); }
-      } }
-      return markers
-    },
-    findMarks: function(from, to, filter) {
-      from = clipPos(this, from); to = clipPos(this, to);
-      var found = [], lineNo = from.line;
-      this.iter(from.line, to.line + 1, function (line) {
-        var spans = line.markedSpans;
-        if (spans) { for (var i = 0; i < spans.length; i++) {
-          var span = spans[i];
-          if (!(span.to != null && lineNo == from.line && from.ch >= span.to ||
-                span.from == null && lineNo != from.line ||
-                span.from != null && lineNo == to.line && span.from >= to.ch) &&
-              (!filter || filter(span.marker)))
-            { found.push(span.marker.parent || span.marker); }
-        } }
-        ++lineNo;
-      });
-      return found
-    },
-    getAllMarks: function() {
-      var markers = [];
-      this.iter(function (line) {
-        var sps = line.markedSpans;
-        if (sps) { for (var i = 0; i < sps.length; ++i)
-          { if (sps[i].from != null) { markers.push(sps[i].marker); } } }
-      });
-      return markers
-    },
-
-    posFromIndex: function(off) {
-      var ch, lineNo = this.first, sepSize = this.lineSeparator().length;
-      this.iter(function (line) {
-        var sz = line.text.length + sepSize;
-        if (sz > off) { ch = off; return true }
-        off -= sz;
-        ++lineNo;
-      });
-      return clipPos(this, Pos(lineNo, ch))
-    },
-    indexFromPos: function (coords) {
-      coords = clipPos(this, coords);
-      var index = coords.ch;
-      if (coords.line < this.first || coords.ch < 0) { return 0 }
-      var sepSize = this.lineSeparator().length;
-      this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
-        index += line.text.length + sepSize;
-      });
-      return index
-    },
-
-    copy: function(copyHistory) {
-      var doc = new Doc(getLines(this, this.first, this.first + this.size),
-                        this.modeOption, this.first, this.lineSep, this.direction);
-      doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
-      doc.sel = this.sel;
-      doc.extend = false;
-      if (copyHistory) {
-        doc.history.undoDepth = this.history.undoDepth;
-        doc.setHistory(this.getHistory());
-      }
-      return doc
-    },
-
-    linkedDoc: function(options) {
-      if (!options) { options = {}; }
-      var from = this.first, to = this.first + this.size;
-      if (options.from != null && options.from > from) { from = options.from; }
-      if (options.to != null && options.to < to) { to = options.to; }
-      var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);
-      if (options.sharedHist) { copy.history = this.history
-      ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
-      copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
-      copySharedMarkers(copy, findSharedMarkers(this));
-      return copy
-    },
-    unlinkDoc: function(other) {
-      if (other instanceof CodeMirror) { other = other.doc; }
-      if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
-        var link = this.linked[i];
-        if (link.doc != other) { continue }
-        this.linked.splice(i, 1);
-        other.unlinkDoc(this);
-        detachSharedMarkers(findSharedMarkers(this));
-        break
-      } }
-      // If the histories were shared, split them again
-      if (other.history == this.history) {
-        var splitIds = [other.id];
-        linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);
-        other.history = new History(null);
-        other.history.done = copyHistoryArray(this.history.done, splitIds);
-        other.history.undone = copyHistoryArray(this.history.undone, splitIds);
-      }
-    },
-    iterLinkedDocs: function(f) {linkedDocs(this, f);},
-
-    getMode: function() {return this.mode},
-    getEditor: function() {return this.cm},
-
-    splitLines: function(str) {
-      if (this.lineSep) { return str.split(this.lineSep) }
-      return splitLinesAuto(str)
-    },
-    lineSeparator: function() { return this.lineSep || "\n" },
-
-    setDirection: docMethodOp(function (dir) {
-      if (dir != "rtl") { dir = "ltr"; }
-      if (dir == this.direction) { return }
-      this.direction = dir;
-      this.iter(function (line) { return line.order = null; });
-      if (this.cm) { directionChanged(this.cm); }
-    })
-  });
-
-  // Public alias.
-  Doc.prototype.eachLine = Doc.prototype.iter;
-
-  // Kludge to work around strange IE behavior where it'll sometimes
-  // re-fire a series of drag-related events right after the drop (#1551)
-  var lastDrop = 0;
-
-  function onDrop(e) {
-    var cm = this;
-    clearDragCursor(cm);
-    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
-      { return }
-    e_preventDefault(e);
-    if (ie) { lastDrop = +new Date; }
-    var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
-    if (!pos || cm.isReadOnly()) { return }
-    // Might be a file drop, in which case we simply extract the text
-    // and insert it.
-    if (files && files.length && window.FileReader && window.File) {
-      var n = files.length, text = Array(n), read = 0;
-      var markAsReadAndPasteIfAllFilesAreRead = function () {
-        if (++read == n) {
-          operation(cm, function () {
-            pos = clipPos(cm.doc, pos);
-            var change = {from: pos, to: pos,
-                          text: cm.doc.splitLines(
-                              text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())),
-                          origin: "paste"};
-            makeChange(cm.doc, change);
-            setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change))));
-          })();
-        }
-      };
-      var readTextFromFile = function (file, i) {
-        if (cm.options.allowDropFileTypes &&
-            indexOf(cm.options.allowDropFileTypes, file.type) == -1) {
-          markAsReadAndPasteIfAllFilesAreRead();
-          return
-        }
-        var reader = new FileReader;
-        reader.onerror = function () { return markAsReadAndPasteIfAllFilesAreRead(); };
-        reader.onload = function () {
-          var content = reader.result;
-          if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) {
-            markAsReadAndPasteIfAllFilesAreRead();
-            return
-          }
-          text[i] = content;
-          markAsReadAndPasteIfAllFilesAreRead();
-        };
-        reader.readAsText(file);
-      };
-      for (var i = 0; i < files.length; i++) { readTextFromFile(files[i], i); }
-    } else { // Normal drop
-      // Don't do a replace if the drop happened inside of the selected text.
-      if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
-        cm.state.draggingText(e);
-        // Ensure the editor is re-focused
-        setTimeout(function () { return cm.display.input.focus(); }, 20);
-        return
-      }
-      try {
-        var text$1 = e.dataTransfer.getData("Text");
-        if (text$1) {
-          var selected;
-          if (cm.state.draggingText && !cm.state.draggingText.copy)
-            { selected = cm.listSelections(); }
-          setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
-          if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
-            { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } }
-          cm.replaceSelection(text$1, "around", "paste");
-          cm.display.input.focus();
-        }
-      }
-      catch(e$1){}
-    }
-  }
-
-  function onDragStart(cm, e) {
-    if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
-    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
-
-    e.dataTransfer.setData("Text", cm.getSelection());
-    e.dataTransfer.effectAllowed = "copyMove";
-
-    // Use dummy image instead of default browsers image.
-    // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
-    if (e.dataTransfer.setDragImage && !safari) {
-      var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
-      img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
-      if (presto) {
-        img.width = img.height = 1;
-        cm.display.wrapper.appendChild(img);
-        // Force a relayout, or Opera won't use our image for some obscure reason
-        img._top = img.offsetTop;
-      }
-      e.dataTransfer.setDragImage(img, 0, 0);
-      if (presto) { img.parentNode.removeChild(img); }
-    }
-  }
-
-  function onDragOver(cm, e) {
-    var pos = posFromMouse(cm, e);
-    if (!pos) { return }
-    var frag = document.createDocumentFragment();
-    drawSelectionCursor(cm, pos, frag);
-    if (!cm.display.dragCursor) {
-      cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
-      cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
-    }
-    removeChildrenAndAdd(cm.display.dragCursor, frag);
-  }
-
-  function clearDragCursor(cm) {
-    if (cm.display.dragCursor) {
-      cm.display.lineSpace.removeChild(cm.display.dragCursor);
-      cm.display.dragCursor = null;
-    }
-  }
-
-  // These must be handled carefully, because naively registering a
-  // handler for each editor will cause the editors to never be
-  // garbage collected.
-
-  function forEachCodeMirror(f) {
-    if (!document.getElementsByClassName) { return }
-    var byClass = document.getElementsByClassName("CodeMirror"), editors = [];
-    for (var i = 0; i < byClass.length; i++) {
-      var cm = byClass[i].CodeMirror;
-      if (cm) { editors.push(cm); }
-    }
-    if (editors.length) { editors[0].operation(function () {
-      for (var i = 0; i < editors.length; i++) { f(editors[i]); }
-    }); }
-  }
-
-  var globalsRegistered = false;
-  function ensureGlobalHandlers() {
-    if (globalsRegistered) { return }
-    registerGlobalHandlers();
-    globalsRegistered = true;
-  }
-  function registerGlobalHandlers() {
-    // When the window resizes, we need to refresh active editors.
-    var resizeTimer;
-    on(window, "resize", function () {
-      if (resizeTimer == null) { resizeTimer = setTimeout(function () {
-        resizeTimer = null;
-        forEachCodeMirror(onResize);
-      }, 100); }
-    });
-    // When the window loses focus, we want to show the editor as blurred
-    on(window, "blur", function () { return forEachCodeMirror(onBlur); });
-  }
-  // Called when the window resizes
-  function onResize(cm) {
-    var d = cm.display;
-    // Might be a text scaling operation, clear size caches.
-    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
-    d.scrollbarsClipped = false;
-    cm.setSize();
-  }
-
-  var keyNames = {
-    3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
-    19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
-    36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
-    46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
-    106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock",
-    173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
-    221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
-    63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
-  };
-
-  // Number keys
-  for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }
-  // Alphabetic keys
-  for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }
-  // Function keys
-  for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; }
-
-  var keyMap = {};
-
-  keyMap.basic = {
-    "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
-    "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
-    "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
-    "Tab": "defaultTab", "Shift-Tab": "indentAuto",
-    "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
-    "Esc": "singleSelection"
-  };
-  // Note that the save and find-related commands aren't defined by
-  // default. User code or addons can define them. Unknown commands
-  // are simply ignored.
-  keyMap.pcDefault = {
-    "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
-    "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
-    "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
-    "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
-    "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
-    "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
-    "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
-    "fallthrough": "basic"
-  };
-  // Very basic readline/emacs-style bindings, which are standard on Mac.
-  keyMap.emacsy = {
-    "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
-    "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
-    "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
-    "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
-    "Ctrl-O": "openLine"
-  };
-  keyMap.macDefault = {
-    "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
-    "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
-    "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
-    "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
-    "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
-    "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
-    "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
-    "fallthrough": ["basic", "emacsy"]
-  };
-  keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
-
-  // KEYMAP DISPATCH
-
-  function normalizeKeyName(name) {
-    var parts = name.split(/-(?!$)/);
-    name = parts[parts.length - 1];
-    var alt, ctrl, shift, cmd;
-    for (var i = 0; i < parts.length - 1; i++) {
-      var mod = parts[i];
-      if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }
-      else if (/^a(lt)?$/i.test(mod)) { alt = true; }
-      else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }
-      else if (/^s(hift)?$/i.test(mod)) { shift = true; }
-      else { throw new Error("Unrecognized modifier name: " + mod) }
-    }
-    if (alt) { name = "Alt-" + name; }
-    if (ctrl) { name = "Ctrl-" + name; }
-    if (cmd) { name = "Cmd-" + name; }
-    if (shift) { name = "Shift-" + name; }
-    return name
-  }
-
-  // This is a kludge to keep keymaps mostly working as raw objects
-  // (backwards compatibility) while at the same time support features
-  // like normalization and multi-stroke key bindings. It compiles a
-  // new normalized keymap, and then updates the old object to reflect
-  // this.
-  function normalizeKeyMap(keymap) {
-    var copy = {};
-    for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
-      var value = keymap[keyname];
-      if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
-      if (value == "...") { delete keymap[keyname]; continue }
-
-      var keys = map(keyname.split(" "), normalizeKeyName);
-      for (var i = 0; i < keys.length; i++) {
-        var val = (void 0), name = (void 0);
-        if (i == keys.length - 1) {
-          name = keys.join(" ");
-          val = value;
-        } else {
-          name = keys.slice(0, i + 1).join(" ");
-          val = "...";
-        }
-        var prev = copy[name];
-        if (!prev) { copy[name] = val; }
-        else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
-      }
-      delete keymap[keyname];
-    } }
-    for (var prop in copy) { keymap[prop] = copy[prop]; }
-    return keymap
-  }
-
-  function lookupKey(key, map, handle, context) {
-    map = getKeyMap(map);
-    var found = map.call ? map.call(key, context) : map[key];
-    if (found === false) { return "nothing" }
-    if (found === "...") { return "multi" }
-    if (found != null && handle(found)) { return "handled" }
-
-    if (map.fallthrough) {
-      if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
-        { return lookupKey(key, map.fallthrough, handle, context) }
-      for (var i = 0; i < map.fallthrough.length; i++) {
-        var result = lookupKey(key, map.fallthrough[i], handle, context);
-        if (result) { return result }
-      }
-    }
-  }
-
-  // Modifier key presses don't count as 'real' key presses for the
-  // purpose of keymap fallthrough.
-  function isModifierKey(value) {
-    var name = typeof value == "string" ? value : keyNames[value.keyCode];
-    return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
-  }
-
-  function addModifierNames(name, event, noShift) {
-    var base = name;
-    if (event.altKey && base != "Alt") { name = "Alt-" + name; }
-    if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; }
-    if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; }
-    if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; }
-    return name
-  }
-
-  // Look up the name of a key as indicated by an event object.
-  function keyName(event, noShift) {
-    if (presto && event.keyCode == 34 && event["char"]) { return false }
-    var name = keyNames[event.keyCode];
-    if (name == null || event.altGraphKey) { return false }
-    // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
-    // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
-    if (event.keyCode == 3 && event.code) { name = event.code; }
-    return addModifierNames(name, event, noShift)
-  }
-
-  function getKeyMap(val) {
-    return typeof val == "string" ? keyMap[val] : val
-  }
-
-  // Helper for deleting text near the selection(s), used to implement
-  // backspace, delete, and similar functionality.
-  function deleteNearSelection(cm, compute) {
-    var ranges = cm.doc.sel.ranges, kill = [];
-    // Build up a set of ranges to kill first, merging overlapping
-    // ranges.
-    for (var i = 0; i < ranges.length; i++) {
-      var toKill = compute(ranges[i]);
-      while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
-        var replaced = kill.pop();
-        if (cmp(replaced.from, toKill.from) < 0) {
-          toKill.from = replaced.from;
-          break
-        }
-      }
-      kill.push(toKill);
-    }
-    // Next, remove those actual ranges.
-    runInOp(cm, function () {
-      for (var i = kill.length - 1; i >= 0; i--)
-        { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); }
-      ensureCursorVisible(cm);
-    });
-  }
-
-  function moveCharLogically(line, ch, dir) {
-    var target = skipExtendingChars(line.text, ch + dir, dir);
-    return target < 0 || target > line.text.length ? null : target
-  }
-
-  function moveLogically(line, start, dir) {
-    var ch = moveCharLogically(line, start.ch, dir);
-    return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
-  }
-
-  function endOfLine(visually, cm, lineObj, lineNo, dir) {
-    if (visually) {
-      if (cm.doc.direction == "rtl") { dir = -dir; }
-      var order = getOrder(lineObj, cm.doc.direction);
-      if (order) {
-        var part = dir < 0 ? lst(order) : order[0];
-        var moveInStorageOrder = (dir < 0) == (part.level == 1);
-        var sticky = moveInStorageOrder ? "after" : "before";
-        var ch;
-        // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
-        // it could be that the last bidi part is not on the last visual line,
-        // since visual lines contain content order-consecutive chunks.
-        // Thus, in rtl, we are looking for the first (content-order) character
-        // in the rtl chunk that is on the last line (that is, the same line
-        // as the last (content-order) character).
-        if (part.level > 0 || cm.doc.direction == "rtl") {
-          var prep = prepareMeasureForLine(cm, lineObj);
-          ch = dir < 0 ? lineObj.text.length - 1 : 0;
-          var targetTop = measureCharPrepared(cm, prep, ch).top;
-          ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);
-          if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); }
-        } else { ch = dir < 0 ? part.to : part.from; }
-        return new Pos(lineNo, ch, sticky)
-      }
-    }
-    return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
-  }
-
-  function moveVisually(cm, line, start, dir) {
-    var bidi = getOrder(line, cm.doc.direction);
-    if (!bidi) { return moveLogically(line, start, dir) }
-    if (start.ch >= line.text.length) {
-      start.ch = line.text.length;
-      start.sticky = "before";
-    } else if (start.ch <= 0) {
-      start.ch = 0;
-      start.sticky = "after";
-    }
-    var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];
-    if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
-      // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
-      // nothing interesting happens.
-      return moveLogically(line, start, dir)
-    }
-
-    var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };
-    var prep;
-    var getWrappedLineExtent = function (ch) {
-      if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
-      prep = prep || prepareMeasureForLine(cm, line);
-      return wrappedLineExtentChar(cm, line, prep, ch)
-    };
-    var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch);
-
-    if (cm.doc.direction == "rtl" || part.level == 1) {
-      var moveInStorageOrder = (part.level == 1) == (dir < 0);
-      var ch = mv(start, moveInStorageOrder ? 1 : -1);
-      if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
-        // Case 2: We move within an rtl part or in an rtl editor on the same visual line
-        var sticky = moveInStorageOrder ? "before" : "after";
-        return new Pos(start.line, ch, sticky)
-      }
-    }
-
-    // Case 3: Could not move within this bidi part in this visual line, so leave
-    // the current bidi part
-
-    var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
-      var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
-        ? new Pos(start.line, mv(ch, 1), "before")
-        : new Pos(start.line, ch, "after"); };
-
-      for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
-        var part = bidi[partPos];
-        var moveInStorageOrder = (dir > 0) == (part.level != 1);
-        var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);
-        if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
-        ch = moveInStorageOrder ? part.from : mv(part.to, -1);
-        if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
-      }
-    };
-
-    // Case 3a: Look for other bidi parts on the same visual line
-    var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);
-    if (res) { return res }
-
-    // Case 3b: Look for other bidi parts on the next visual line
-    var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);
-    if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
-      res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));
-      if (res) { return res }
-    }
-
-    // Case 4: Nowhere to move
-    return null
-  }
-
-  // Commands are parameter-less actions that can be performed on an
-  // editor, mostly used for keybindings.
-  var commands = {
-    selectAll: selectAll,
-    singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
-    killLine: function (cm) { return deleteNearSelection(cm, function (range) {
-      if (range.empty()) {
-        var len = getLine(cm.doc, range.head.line).text.length;
-        if (range.head.ch == len && range.head.line < cm.lastLine())
-          { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
-        else
-          { return {from: range.head, to: Pos(range.head.line, len)} }
-      } else {
-        return {from: range.from(), to: range.to()}
-      }
-    }); },
-    deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
-      from: Pos(range.from().line, 0),
-      to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
-    }); }); },
-    delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
-      from: Pos(range.from().line, 0), to: range.from()
-    }); }); },
-    delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
-      var top = cm.charCoords(range.head, "div").top + 5;
-      var leftPos = cm.coordsChar({left: 0, top: top}, "div");
-      return {from: leftPos, to: range.from()}
-    }); },
-    delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
-      var top = cm.charCoords(range.head, "div").top + 5;
-      var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
-      return {from: range.from(), to: rightPos }
-    }); },
-    undo: function (cm) { return cm.undo(); },
-    redo: function (cm) { return cm.redo(); },
-    undoSelection: function (cm) { return cm.undoSelection(); },
-    redoSelection: function (cm) { return cm.redoSelection(); },
-    goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
-    goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
-    goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
-      {origin: "+move", bias: 1}
-    ); },
-    goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
-      {origin: "+move", bias: 1}
-    ); },
-    goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
-      {origin: "+move", bias: -1}
-    ); },
-    goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
-      var top = cm.cursorCoords(range.head, "div").top + 5;
-      return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
-    }, sel_move); },
-    goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
-      var top = cm.cursorCoords(range.head, "div").top + 5;
-      return cm.coordsChar({left: 0, top: top}, "div")
-    }, sel_move); },
-    goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
-      var top = cm.cursorCoords(range.head, "div").top + 5;
-      var pos = cm.coordsChar({left: 0, top: top}, "div");
-      if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
-      return pos
-    }, sel_move); },
-    goLineUp: function (cm) { return cm.moveV(-1, "line"); },
-    goLineDown: function (cm) { return cm.moveV(1, "line"); },
-    goPageUp: function (cm) { return cm.moveV(-1, "page"); },
-    goPageDown: function (cm) { return cm.moveV(1, "page"); },
-    goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
-    goCharRight: function (cm) { return cm.moveH(1, "char"); },
-    goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
-    goColumnRight: function (cm) { return cm.moveH(1, "column"); },
-    goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
-    goGroupRight: function (cm) { return cm.moveH(1, "group"); },
-    goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
-    goWordRight: function (cm) { return cm.moveH(1, "word"); },
-    delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
-    delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
-    delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
-    delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
-    delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
-    delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
-    indentAuto: function (cm) { return cm.indentSelection("smart"); },
-    indentMore: function (cm) { return cm.indentSelection("add"); },
-    indentLess: function (cm) { return cm.indentSelection("subtract"); },
-    insertTab: function (cm) { return cm.replaceSelection("\t"); },
-    insertSoftTab: function (cm) {
-      var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
-      for (var i = 0; i < ranges.length; i++) {
-        var pos = ranges[i].from();
-        var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
-        spaces.push(spaceStr(tabSize - col % tabSize));
-      }
-      cm.replaceSelections(spaces);
-    },
-    defaultTab: function (cm) {
-      if (cm.somethingSelected()) { cm.indentSelection("add"); }
-      else { cm.execCommand("insertTab"); }
-    },
-    // Swap the two chars left and right of each selection's head.
-    // Move cursor behind the two swapped characters afterwards.
-    //
-    // Doesn't consider line feeds a character.
-    // Doesn't scan more than one line above to find a character.
-    // Doesn't do anything on an empty line.
-    // Doesn't do anything with non-empty selections.
-    transposeChars: function (cm) { return runInOp(cm, function () {
-      var ranges = cm.listSelections(), newSel = [];
-      for (var i = 0; i < ranges.length; i++) {
-        if (!ranges[i].empty()) { continue }
-        var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
-        if (line) {
-          if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }
-          if (cur.ch > 0) {
-            cur = new Pos(cur.line, cur.ch + 1);
-            cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
-                            Pos(cur.line, cur.ch - 2), cur, "+transpose");
-          } else if (cur.line > cm.doc.first) {
-            var prev = getLine(cm.doc, cur.line - 1).text;
-            if (prev) {
-              cur = new Pos(cur.line, 1);
-              cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
-                              prev.charAt(prev.length - 1),
-                              Pos(cur.line - 1, prev.length - 1), cur, "+transpose");
-            }
-          }
-        }
-        newSel.push(new Range(cur, cur));
-      }
-      cm.setSelections(newSel);
-    }); },
-    newlineAndIndent: function (cm) { return runInOp(cm, function () {
-      var sels = cm.listSelections();
-      for (var i = sels.length - 1; i >= 0; i--)
-        { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); }
-      sels = cm.listSelections();
-      for (var i$1 = 0; i$1 < sels.length; i$1++)
-        { cm.indentLine(sels[i$1].from().line, null, true); }
-      ensureCursorVisible(cm);
-    }); },
-    openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
-    toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
-  };
-
-
-  function lineStart(cm, lineN) {
-    var line = getLine(cm.doc, lineN);
-    var visual = visualLine(line);
-    if (visual != line) { lineN = lineNo(visual); }
-    return endOfLine(true, cm, visual, lineN, 1)
-  }
-  function lineEnd(cm, lineN) {
-    var line = getLine(cm.doc, lineN);
-    var visual = visualLineEnd(line);
-    if (visual != line) { lineN = lineNo(visual); }
-    return endOfLine(true, cm, line, lineN, -1)
-  }
-  function lineStartSmart(cm, pos) {
-    var start = lineStart(cm, pos.line);
-    var line = getLine(cm.doc, start.line);
-    var order = getOrder(line, cm.doc.direction);
-    if (!order || order[0].level == 0) {
-      var firstNonWS = Math.max(start.ch, line.text.search(/\S/));
-      var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
-      return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
-    }
-    return start
-  }
-
-  // Run a handler that was bound to a key.
-  function doHandleBinding(cm, bound, dropShift) {
-    if (typeof bound == "string") {
-      bound = commands[bound];
-      if (!bound) { return false }
-    }
-    // Ensure previous input has been read, so that the handler sees a
-    // consistent view of the document
-    cm.display.input.ensurePolled();
-    var prevShift = cm.display.shift, done = false;
-    try {
-      if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
-      if (dropShift) { cm.display.shift = false; }
-      done = bound(cm) != Pass;
-    } finally {
-      cm.display.shift = prevShift;
-      cm.state.suppressEdits = false;
-    }
-    return done
-  }
-
-  function lookupKeyForEditor(cm, name, handle) {
-    for (var i = 0; i < cm.state.keyMaps.length; i++) {
-      var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
-      if (result) { return result }
-    }
-    return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
-      || lookupKey(name, cm.options.keyMap, handle, cm)
-  }
-
-  // Note that, despite the name, this function is also used to check
-  // for bound mouse clicks.
-
-  var stopSeq = new Delayed;
-
-  function dispatchKey(cm, name, e, handle) {
-    var seq = cm.state.keySeq;
-    if (seq) {
-      if (isModifierKey(name)) { return "handled" }
-      if (/\'$/.test(name))
-        { cm.state.keySeq = null; }
-      else
-        { stopSeq.set(50, function () {
-          if (cm.state.keySeq == seq) {
-            cm.state.keySeq = null;
-            cm.display.input.reset();
-          }
-        }); }
-      if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true }
-    }
-    return dispatchKeyInner(cm, name, e, handle)
-  }
-
-  function dispatchKeyInner(cm, name, e, handle) {
-    var result = lookupKeyForEditor(cm, name, handle);
-
-    if (result == "multi")
-      { cm.state.keySeq = name; }
-    if (result == "handled")
-      { signalLater(cm, "keyHandled", cm, name, e); }
-
-    if (result == "handled" || result == "multi") {
-      e_preventDefault(e);
-      restartBlink(cm);
-    }
-
-    return !!result
-  }
-
-  // Handle a key from the keydown event.
-  function handleKeyBinding(cm, e) {
-    var name = keyName(e, true);
-    if (!name) { return false }
-
-    if (e.shiftKey && !cm.state.keySeq) {
-      // First try to resolve full name (including 'Shift-'). Failing
-      // that, see if there is a cursor-motion command (starting with
-      // 'go') bound to the keyname without 'Shift-'.
-      return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
-          || dispatchKey(cm, name, e, function (b) {
-               if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
-                 { return doHandleBinding(cm, b) }
-             })
-    } else {
-      return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
-    }
-  }
-
-  // Handle a key from the keypress event
-  function handleCharBinding(cm, e, ch) {
-    return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
-  }
-
-  var lastStoppedKey = null;
-  function onKeyDown(e) {
-    var cm = this;
-    if (e.target && e.target != cm.display.input.getField()) { return }
-    cm.curOp.focus = activeElt();
-    if (signalDOMEvent(cm, e)) { return }
-    // IE does strange things with escape.
-    if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }
-    var code = e.keyCode;
-    cm.display.shift = code == 16 || e.shiftKey;
-    var handled = handleKeyBinding(cm, e);
-    if (presto) {
-      lastStoppedKey = handled ? code : null;
-      // Opera has no cut event... we try to at least catch the key combo
-      if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
-        { cm.replaceSelection("", null, "cut"); }
-    }
-    if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand)
-      { document.execCommand("cut"); }
-
-    // Turn mouse into crosshair when Alt is held on Mac.
-    if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
-      { showCrossHair(cm); }
-  }
-
-  function showCrossHair(cm) {
-    var lineDiv = cm.display.lineDiv;
-    addClass(lineDiv, "CodeMirror-crosshair");
-
-    function up(e) {
-      if (e.keyCode == 18 || !e.altKey) {
-        rmClass(lineDiv, "CodeMirror-crosshair");
-        off(document, "keyup", up);
-        off(document, "mouseover", up);
-      }
-    }
-    on(document, "keyup", up);
-    on(document, "mouseover", up);
-  }
-
-  function onKeyUp(e) {
-    if (e.keyCode == 16) { this.doc.sel.shift = false; }
-    signalDOMEvent(this, e);
-  }
-
-  function onKeyPress(e) {
-    var cm = this;
-    if (e.target && e.target != cm.display.input.getField()) { return }
-    if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
-    var keyCode = e.keyCode, charCode = e.charCode;
-    if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
-    if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
-    var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
-    // Some browsers fire keypress events for backspace
-    if (ch == "\x08") { return }
-    if (handleCharBinding(cm, e, ch)) { return }
-    cm.display.input.onKeyPress(e);
-  }
-
-  var DOUBLECLICK_DELAY = 400;
-
-  var PastClick = function(time, pos, button) {
-    this.time = time;
-    this.pos = pos;
-    this.button = button;
-  };
-
-  PastClick.prototype.compare = function (time, pos, button) {
-    return this.time + DOUBLECLICK_DELAY > time &&
-      cmp(pos, this.pos) == 0 && button == this.button
-  };
-
-  var lastClick, lastDoubleClick;
-  function clickRepeat(pos, button) {
-    var now = +new Date;
-    if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
-      lastClick = lastDoubleClick = null;
-      return "triple"
-    } else if (lastClick && lastClick.compare(now, pos, button)) {
-      lastDoubleClick = new PastClick(now, pos, button);
-      lastClick = null;
-      return "double"
-    } else {
-      lastClick = new PastClick(now, pos, button);
-      lastDoubleClick = null;
-      return "single"
-    }
-  }
-
-  // A mouse down can be a single click, double click, triple click,
-  // start of selection drag, start of text drag, new cursor
-  // (ctrl-click), rectangle drag (alt-drag), or xwin
-  // middle-click-paste. Or it might be a click on something we should
-  // not interfere with, such as a scrollbar or widget.
-  function onMouseDown(e) {
-    var cm = this, display = cm.display;
-    if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
-    display.input.ensurePolled();
-    display.shift = e.shiftKey;
-
-    if (eventInWidget(display, e)) {
-      if (!webkit) {
-        // Briefly turn off draggability, to allow widgets to do
-        // normal dragging things.
-        display.scroller.draggable = false;
-        setTimeout(function () { return display.scroller.draggable = true; }, 100);
-      }
-      return
-    }
-    if (clickInGutter(cm, e)) { return }
-    var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single";
-    window.focus();
-
-    // #3261: make sure, that we're not starting a second selection
-    if (button == 1 && cm.state.selectingText)
-      { cm.state.selectingText(e); }
-
-    if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }
-
-    if (button == 1) {
-      if (pos) { leftButtonDown(cm, pos, repeat, e); }
-      else if (e_target(e) == display.scroller) { e_preventDefault(e); }
-    } else if (button == 2) {
-      if (pos) { extendSelection(cm.doc, pos); }
-      setTimeout(function () { return display.input.focus(); }, 20);
-    } else if (button == 3) {
-      if (captureRightClick) { cm.display.input.onContextMenu(e); }
-      else { delayBlurEvent(cm); }
-    }
-  }
-
-  function handleMappedButton(cm, button, pos, repeat, event) {
-    var name = "Click";
-    if (repeat == "double") { name = "Double" + name; }
-    else if (repeat == "triple") { name = "Triple" + name; }
-    name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name;
-
-    return dispatchKey(cm,  addModifierNames(name, event), event, function (bound) {
-      if (typeof bound == "string") { bound = commands[bound]; }
-      if (!bound) { return false }
-      var done = false;
-      try {
-        if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
-        done = bound(cm, pos) != Pass;
-      } finally {
-        cm.state.suppressEdits = false;
-      }
-      return done
-    })
-  }
-
-  function configureMouse(cm, repeat, event) {
-    var option = cm.getOption("configureMouse");
-    var value = option ? option(cm, repeat, event) : {};
-    if (value.unit == null) {
-      var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;
-      value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line";
-    }
-    if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }
-    if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }
-    if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }
-    return value
-  }
-
-  function leftButtonDown(cm, pos, repeat, event) {
-    if (ie) { setTimeout(bind(ensureFocus, cm), 0); }
-    else { cm.curOp.focus = activeElt(); }
-
-    var behavior = configureMouse(cm, repeat, event);
-
-    var sel = cm.doc.sel, contained;
-    if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
-        repeat == "single" && (contained = sel.contains(pos)) > -1 &&
-        (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
-        (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
-      { leftButtonStartDrag(cm, event, pos, behavior); }
-    else
-      { leftButtonSelect(cm, event, pos, behavior); }
-  }
-
-  // Start a text drag. When it ends, see if any dragging actually
-  // happen, and treat as a click if it didn't.
-  function leftButtonStartDrag(cm, event, pos, behavior) {
-    var display = cm.display, moved = false;
-    var dragEnd = operation(cm, function (e) {
-      if (webkit) { display.scroller.draggable = false; }
-      cm.state.draggingText = false;
-      off(display.wrapper.ownerDocument, "mouseup", dragEnd);
-      off(display.wrapper.ownerDocument, "mousemove", mouseMove);
-      off(display.scroller, "dragstart", dragStart);
-      off(display.scroller, "drop", dragEnd);
-      if (!moved) {
-        e_preventDefault(e);
-        if (!behavior.addNew)
-          { extendSelection(cm.doc, pos, null, null, behavior.extend); }
-        // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
-        if ((webkit && !safari) || ie && ie_version == 9)
-          { setTimeout(function () {display.wrapper.ownerDocument.body.focus({preventScroll: true}); display.input.focus();}, 20); }
-        else
-          { display.input.focus(); }
-      }
-    });
-    var mouseMove = function(e2) {
-      moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;
-    };
-    var dragStart = function () { return moved = true; };
-    // Let the drag handler handle this.
-    if (webkit) { display.scroller.draggable = true; }
-    cm.state.draggingText = dragEnd;
-    dragEnd.copy = !behavior.moveOnDrag;
-    // IE's approach to draggable
-    if (display.scroller.dragDrop) { display.scroller.dragDrop(); }
-    on(display.wrapper.ownerDocument, "mouseup", dragEnd);
-    on(display.wrapper.ownerDocument, "mousemove", mouseMove);
-    on(display.scroller, "dragstart", dragStart);
-    on(display.scroller, "drop", dragEnd);
-
-    delayBlurEvent(cm);
-    setTimeout(function () { return display.input.focus(); }, 20);
-  }
-
-  function rangeForUnit(cm, pos, unit) {
-    if (unit == "char") { return new Range(pos, pos) }
-    if (unit == "word") { return cm.findWordAt(pos) }
-    if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
-    var result = unit(cm, pos);
-    return new Range(result.from, result.to)
-  }
-
-  // Normal selection, as opposed to text dragging.
-  function leftButtonSelect(cm, event, start, behavior) {
-    var display = cm.display, doc = cm.doc;
-    e_preventDefault(event);
-
-    var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
-    if (behavior.addNew && !behavior.extend) {
-      ourIndex = doc.sel.contains(start);
-      if (ourIndex > -1)
-        { ourRange = ranges[ourIndex]; }
-      else
-        { ourRange = new Range(start, start); }
-    } else {
-      ourRange = doc.sel.primary();
-      ourIndex = doc.sel.primIndex;
-    }
-
-    if (behavior.unit == "rectangle") {
-      if (!behavior.addNew) { ourRange = new Range(start, start); }
-      start = posFromMouse(cm, event, true, true);
-      ourIndex = -1;
-    } else {
-      var range = rangeForUnit(cm, start, behavior.unit);
-      if (behavior.extend)
-        { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend); }
-      else
-        { ourRange = range; }
-    }
-
-    if (!behavior.addNew) {
-      ourIndex = 0;
-      setSelection(doc, new Selection([ourRange], 0), sel_mouse);
-      startSel = doc.sel;
-    } else if (ourIndex == -1) {
-      ourIndex = ranges.length;
-      setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex),
-                   {scroll: false, origin: "*mouse"});
-    } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
-      setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
-                   {scroll: false, origin: "*mouse"});
-      startSel = doc.sel;
-    } else {
-      replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
-    }
-
-    var lastPos = start;
-    function extendTo(pos) {
-      if (cmp(lastPos, pos) == 0) { return }
-      lastPos = pos;
-
-      if (behavior.unit == "rectangle") {
-        var ranges = [], tabSize = cm.options.tabSize;
-        var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
-        var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
-        var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
-        for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
-             line <= end; line++) {
-          var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
-          if (left == right)
-            { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }
-          else if (text.length > leftPos)
-            { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }
-        }
-        if (!ranges.length) { ranges.push(new Range(start, start)); }
-        setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
-                     {origin: "*mouse", scroll: false});
-        cm.scrollIntoView(pos);
-      } else {
-        var oldRange = ourRange;
-        var range = rangeForUnit(cm, pos, behavior.unit);
-        var anchor = oldRange.anchor, head;
-        if (cmp(range.anchor, anchor) > 0) {
-          head = range.head;
-          anchor = minPos(oldRange.from(), range.anchor);
-        } else {
-          head = range.anchor;
-          anchor = maxPos(oldRange.to(), range.head);
-        }
-        var ranges$1 = startSel.ranges.slice(0);
-        ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head));
-        setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse);
-      }
-    }
-
-    var editorSize = display.wrapper.getBoundingClientRect();
-    // Used to ensure timeout re-tries don't fire when another extend
-    // happened in the meantime (clearTimeout isn't reliable -- at
-    // least on Chrome, the timeouts still happen even when cleared,
-    // if the clear happens after their scheduled firing time).
-    var counter = 0;
-
-    function extend(e) {
-      var curCount = ++counter;
-      var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle");
-      if (!cur) { return }
-      if (cmp(cur, lastPos) != 0) {
-        cm.curOp.focus = activeElt();
-        extendTo(cur);
-        var visible = visibleLines(display, doc);
-        if (cur.line >= visible.to || cur.line < visible.from)
-          { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }
-      } else {
-        var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
-        if (outside) { setTimeout(operation(cm, function () {
-          if (counter != curCount) { return }
-          display.scroller.scrollTop += outside;
-          extend(e);
-        }), 50); }
-      }
-    }
-
-    function done(e) {
-      cm.state.selectingText = false;
-      counter = Infinity;
-      // If e is null or undefined we interpret this as someone trying
-      // to explicitly cancel the selection rather than the user
-      // letting go of the mouse button.
-      if (e) {
-        e_preventDefault(e);
-        display.input.focus();
-      }
-      off(display.wrapper.ownerDocument, "mousemove", move);
-      off(display.wrapper.ownerDocument, "mouseup", up);
-      doc.history.lastSelOrigin = null;
-    }
-
-    var move = operation(cm, function (e) {
-      if (e.buttons === 0 || !e_button(e)) { done(e); }
-      else { extend(e); }
-    });
-    var up = operation(cm, done);
-    cm.state.selectingText = up;
-    on(display.wrapper.ownerDocument, "mousemove", move);
-    on(display.wrapper.ownerDocument, "mouseup", up);
-  }
-
-  // Used when mouse-selecting to adjust the anchor to the proper side
-  // of a bidi jump depending on the visual position of the head.
-  function bidiSimplify(cm, range) {
-    var anchor = range.anchor;
-    var head = range.head;
-    var anchorLine = getLine(cm.doc, anchor.line);
-    if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range }
-    var order = getOrder(anchorLine);
-    if (!order) { return range }
-    var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index];
-    if (part.from != anchor.ch && part.to != anchor.ch) { return range }
-    var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1);
-    if (boundary == 0 || boundary == order.length) { return range }
-
-    // Compute the relative visual position of the head compared to the
-    // anchor (<0 is to the left, >0 to the right)
-    var leftSide;
-    if (head.line != anchor.line) {
-      leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0;
-    } else {
-      var headIndex = getBidiPartAt(order, head.ch, head.sticky);
-      var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1);
-      if (headIndex == boundary - 1 || headIndex == boundary)
-        { leftSide = dir < 0; }
-      else
-        { leftSide = dir > 0; }
-    }
-
-    var usePart = order[boundary + (leftSide ? -1 : 0)];
-    var from = leftSide == (usePart.level == 1);
-    var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before";
-    return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head)
-  }
-
-
-  // Determines whether an event happened in the gutter, and fires the
-  // handlers for the corresponding event.
-  function gutterEvent(cm, e, type, prevent) {
-    var mX, mY;
-    if (e.touches) {
-      mX = e.touches[0].clientX;
-      mY = e.touches[0].clientY;
-    } else {
-      try { mX = e.clientX; mY = e.clientY; }
-      catch(e$1) { return false }
-    }
-    if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
-    if (prevent) { e_preventDefault(e); }
-
-    var display = cm.display;
-    var lineBox = display.lineDiv.getBoundingClientRect();
-
-    if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
-    mY -= lineBox.top - display.viewOffset;
-
-    for (var i = 0; i < cm.display.gutterSpecs.length; ++i) {
-      var g = display.gutters.childNodes[i];
-      if (g && g.getBoundingClientRect().right >= mX) {
-        var line = lineAtHeight(cm.doc, mY);
-        var gutter = cm.display.gutterSpecs[i];
-        signal(cm, type, cm, line, gutter.className, e);
-        return e_defaultPrevented(e)
-      }
-    }
-  }
-
-  function clickInGutter(cm, e) {
-    return gutterEvent(cm, e, "gutterClick", true)
-  }
-
-  // CONTEXT MENU HANDLING
-
-  // To make the context menu work, we need to briefly unhide the
-  // textarea (making it as unobtrusive as possible) to let the
-  // right-click take effect on it.
-  function onContextMenu(cm, e) {
-    if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
-    if (signalDOMEvent(cm, e, "contextmenu")) { return }
-    if (!captureRightClick) { cm.display.input.onContextMenu(e); }
-  }
-
-  function contextMenuInGutter(cm, e) {
-    if (!hasHandler(cm, "gutterContextMenu")) { return false }
-    return gutterEvent(cm, e, "gutterContextMenu", false)
-  }
-
-  function themeChanged(cm) {
-    cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
-      cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
-    clearCaches(cm);
-  }
-
-  var Init = {toString: function(){return "CodeMirror.Init"}};
-
-  var defaults = {};
-  var optionHandlers = {};
-
-  function defineOptions(CodeMirror) {
-    var optionHandlers = CodeMirror.optionHandlers;
-
-    function option(name, deflt, handle, notOnInit) {
-      CodeMirror.defaults[name] = deflt;
-      if (handle) { optionHandlers[name] =
-        notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }
-    }
-
-    CodeMirror.defineOption = option;
-
-    // Passed to option handlers when there is no old value.
-    CodeMirror.Init = Init;
-
-    // These two are, on init, called from the constructor because they
-    // have to be initialized before the editor can start at all.
-    option("value", "", function (cm, val) { return cm.setValue(val); }, true);
-    option("mode", null, function (cm, val) {
-      cm.doc.modeOption = val;
-      loadMode(cm);
-    }, true);
-
-    option("indentUnit", 2, loadMode, true);
-    option("indentWithTabs", false);
-    option("smartIndent", true);
-    option("tabSize", 4, function (cm) {
-      resetModeState(cm);
-      clearCaches(cm);
-      regChange(cm);
-    }, true);
-
-    option("lineSeparator", null, function (cm, val) {
-      cm.doc.lineSep = val;
-      if (!val) { return }
-      var newBreaks = [], lineNo = cm.doc.first;
-      cm.doc.iter(function (line) {
-        for (var pos = 0;;) {
-          var found = line.text.indexOf(val, pos);
-          if (found == -1) { break }
-          pos = found + val.length;
-          newBreaks.push(Pos(lineNo, found));
-        }
-        lineNo++;
-      });
-      for (var i = newBreaks.length - 1; i >= 0; i--)
-        { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }
-    });
-    option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200c\u200e\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, function (cm, val, old) {
-      cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
-      if (old != Init) { cm.refresh(); }
-    });
-    option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);
-    option("electricChars", true);
-    option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
-      throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
-    }, true);
-    option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);
-    option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true);
-    option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true);
-    option("rtlMoveVisually", !windows);
-    option("wholeLineUpdateBefore", true);
-
-    option("theme", "default", function (cm) {
-      themeChanged(cm);
-      updateGutters(cm);
-    }, true);
-    option("keyMap", "default", function (cm, val, old) {
-      var next = getKeyMap(val);
-      var prev = old != Init && getKeyMap(old);
-      if (prev && prev.detach) { prev.detach(cm, next); }
-      if (next.attach) { next.attach(cm, prev || null); }
-    });
-    option("extraKeys", null);
-    option("configureMouse", null);
-
-    option("lineWrapping", false, wrappingChanged, true);
-    option("gutters", [], function (cm, val) {
-      cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers);
-      updateGutters(cm);
-    }, true);
-    option("fixedGutter", true, function (cm, val) {
-      cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
-      cm.refresh();
-    }, true);
-    option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true);
-    option("scrollbarStyle", "native", function (cm) {
-      initScrollbars(cm);
-      updateScrollbars(cm);
-      cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
-      cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
-    }, true);
-    option("lineNumbers", false, function (cm, val) {
-      cm.display.gutterSpecs = getGutters(cm.options.gutters, val);
-      updateGutters(cm);
-    }, true);
-    option("firstLineNumber", 1, updateGutters, true);
-    option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true);
-    option("showCursorWhenSelecting", false, updateSelection, true);
-
-    option("resetSelectionOnContextMenu", true);
-    option("lineWiseCopyCut", true);
-    option("pasteLinesPerSelection", true);
-    option("selectionsMayTouch", false);
-
-    option("readOnly", false, function (cm, val) {
-      if (val == "nocursor") {
-        onBlur(cm);
-        cm.display.input.blur();
-      }
-      cm.display.input.readOnlyChanged(val);
-    });
-
-    option("screenReaderLabel", null, function (cm, val) {
-      val = (val === '') ? null : val;
-      cm.display.input.screenReaderLabelChanged(val);
-    });
-
-    option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);
-    option("dragDrop", true, dragDropChanged);
-    option("allowDropFileTypes", null);
-
-    option("cursorBlinkRate", 530);
-    option("cursorScrollMargin", 0);
-    option("cursorHeight", 1, updateSelection, true);
-    option("singleCursorHeightPerLine", true, updateSelection, true);
-    option("workTime", 100);
-    option("workDelay", 100);
-    option("flattenSpans", true, resetModeState, true);
-    option("addModeClass", false, resetModeState, true);
-    option("pollInterval", 100);
-    option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });
-    option("historyEventDelay", 1250);
-    option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true);
-    option("maxHighlightLength", 10000, resetModeState, true);
-    option("moveInputWithCursor", true, function (cm, val) {
-      if (!val) { cm.display.input.resetPosition(); }
-    });
-
-    option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; });
-    option("autofocus", null);
-    option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true);
-    option("phrases", null);
-  }
-
-  function dragDropChanged(cm, value, old) {
-    var wasOn = old && old != Init;
-    if (!value != !wasOn) {
-      var funcs = cm.display.dragFunctions;
-      var toggle = value ? on : off;
-      toggle(cm.display.scroller, "dragstart", funcs.start);
-      toggle(cm.display.scroller, "dragenter", funcs.enter);
-      toggle(cm.display.scroller, "dragover", funcs.over);
-      toggle(cm.display.scroller, "dragleave", funcs.leave);
-      toggle(cm.display.scroller, "drop", funcs.drop);
-    }
-  }
-
-  function wrappingChanged(cm) {
-    if (cm.options.lineWrapping) {
-      addClass(cm.display.wrapper, "CodeMirror-wrap");
-      cm.display.sizer.style.minWidth = "";
-      cm.display.sizerWidth = null;
-    } else {
-      rmClass(cm.display.wrapper, "CodeMirror-wrap");
-      findMaxLine(cm);
-    }
-    estimateLineHeights(cm);
-    regChange(cm);
-    clearCaches(cm);
-    setTimeout(function () { return updateScrollbars(cm); }, 100);
-  }
-
-  // A CodeMirror instance represents an editor. This is the object
-  // that user code is usually dealing with.
-
-  function CodeMirror(place, options) {
-    var this$1 = this;
-
-    if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }
-
-    this.options = options = options ? copyObj(options) : {};
-    // Determine effective options based on given values and defaults.
-    copyObj(defaults, options, false);
-
-    var doc = options.value;
-    if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }
-    else if (options.mode) { doc.modeOption = options.mode; }
-    this.doc = doc;
-
-    var input = new CodeMirror.inputStyles[options.inputStyle](this);
-    var display = this.display = new Display(place, doc, input, options);
-    display.wrapper.CodeMirror = this;
-    themeChanged(this);
-    if (options.lineWrapping)
-      { this.display.wrapper.className += " CodeMirror-wrap"; }
-    initScrollbars(this);
-
-    this.state = {
-      keyMaps: [],  // stores maps added by addKeyMap
-      overlays: [], // highlighting overlays, as added by addOverlay
-      modeGen: 0,   // bumped when mode/overlay changes, used to invalidate highlighting info
-      overwrite: false,
-      delayingBlurEvent: false,
-      focused: false,
-      suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
-      pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll
-      selectingText: false,
-      draggingText: false,
-      highlight: new Delayed(), // stores highlight worker timeout
-      keySeq: null,  // Unfinished key sequence
-      specialChars: null
-    };
-
-    if (options.autofocus && !mobile) { display.input.focus(); }
-
-    // Override magic textarea content restore that IE sometimes does
-    // on our hidden textarea on reload
-    if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }
-
-    registerEventHandlers(this);
-    ensureGlobalHandlers();
-
-    startOperation(this);
-    this.curOp.forceUpdate = true;
-    attachDoc(this, doc);
-
-    if ((options.autofocus && !mobile) || this.hasFocus())
-      { setTimeout(bind(onFocus, this), 20); }
-    else
-      { onBlur(this); }
-
-    for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
-      { optionHandlers[opt](this, options[opt], Init); } }
-    maybeUpdateLineNumberWidth(this);
-    if (options.finishInit) { options.finishInit(this); }
-    for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this); }
-    endOperation(this);
-    // Suppress optimizelegibility in Webkit, since it breaks text
-    // measuring on line wrapping boundaries.
-    if (webkit && options.lineWrapping &&
-        getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
-      { display.lineDiv.style.textRendering = "auto"; }
-  }
-
-  // The default configuration options.
-  CodeMirror.defaults = defaults;
-  // Functions to run when options are changed.
-  CodeMirror.optionHandlers = optionHandlers;
-
-  // Attach the necessary event handlers when initializing the editor
-  function registerEventHandlers(cm) {
-    var d = cm.display;
-    on(d.scroller, "mousedown", operation(cm, onMouseDown));
-    // Older IE's will not fire a second mousedown for a double click
-    if (ie && ie_version < 11)
-      { on(d.scroller, "dblclick", operation(cm, function (e) {
-        if (signalDOMEvent(cm, e)) { return }
-        var pos = posFromMouse(cm, e);
-        if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
-        e_preventDefault(e);
-        var word = cm.findWordAt(pos);
-        extendSelection(cm.doc, word.anchor, word.head);
-      })); }
-    else
-      { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }
-    // Some browsers fire contextmenu *after* opening the menu, at
-    // which point we can't mess with it anymore. Context menu is
-    // handled in onMouseDown for these browsers.
-    on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); });
-    on(d.input.getField(), "contextmenu", function (e) {
-      if (!d.scroller.contains(e.target)) { onContextMenu(cm, e); }
-    });
-
-    // Used to suppress mouse event handling when a touch happens
-    var touchFinished, prevTouch = {end: 0};
-    function finishTouch() {
-      if (d.activeTouch) {
-        touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);
-        prevTouch = d.activeTouch;
-        prevTouch.end = +new Date;
-      }
-    }
-    function isMouseLikeTouchEvent(e) {
-      if (e.touches.length != 1) { return false }
-      var touch = e.touches[0];
-      return touch.radiusX <= 1 && touch.radiusY <= 1
-    }
-    function farAway(touch, other) {
-      if (other.left == null) { return true }
-      var dx = other.left - touch.left, dy = other.top - touch.top;
-      return dx * dx + dy * dy > 20 * 20
-    }
-    on(d.scroller, "touchstart", function (e) {
-      if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
-        d.input.ensurePolled();
-        clearTimeout(touchFinished);
-        var now = +new Date;
-        d.activeTouch = {start: now, moved: false,
-                         prev: now - prevTouch.end <= 300 ? prevTouch : null};
-        if (e.touches.length == 1) {
-          d.activeTouch.left = e.touches[0].pageX;
-          d.activeTouch.top = e.touches[0].pageY;
-        }
-      }
-    });
-    on(d.scroller, "touchmove", function () {
-      if (d.activeTouch) { d.activeTouch.moved = true; }
-    });
-    on(d.scroller, "touchend", function (e) {
-      var touch = d.activeTouch;
-      if (touch && !eventInWidget(d, e) && touch.left != null &&
-          !touch.moved && new Date - touch.start < 300) {
-        var pos = cm.coordsChar(d.activeTouch, "page"), range;
-        if (!touch.prev || farAway(touch, touch.prev)) // Single tap
-          { range = new Range(pos, pos); }
-        else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
-          { range = cm.findWordAt(pos); }
-        else // Triple tap
-          { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }
-        cm.setSelection(range.anchor, range.head);
-        cm.focus();
-        e_preventDefault(e);
-      }
-      finishTouch();
-    });
-    on(d.scroller, "touchcancel", finishTouch);
-
-    // Sync scrolling between fake scrollbars and real scrollable
-    // area, ensure viewport is updated when scrolling.
-    on(d.scroller, "scroll", function () {
-      if (d.scroller.clientHeight) {
-        updateScrollTop(cm, d.scroller.scrollTop);
-        setScrollLeft(cm, d.scroller.scrollLeft, true);
-        signal(cm, "scroll", cm);
-      }
-    });
-
-    // Listen to wheel events in order to try and update the viewport on time.
-    on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); });
-    on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); });
-
-    // Prevent wrapper from ever scrolling
-    on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
-
-    d.dragFunctions = {
-      enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},
-      over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
-      start: function (e) { return onDragStart(cm, e); },
-      drop: operation(cm, onDrop),
-      leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
-    };
-
-    var inp = d.input.getField();
-    on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); });
-    on(inp, "keydown", operation(cm, onKeyDown));
-    on(inp, "keypress", operation(cm, onKeyPress));
-    on(inp, "focus", function (e) { return onFocus(cm, e); });
-    on(inp, "blur", function (e) { return onBlur(cm, e); });
-  }
-
-  var initHooks = [];
-  CodeMirror.defineInitHook = function (f) { return initHooks.push(f); };
-
-  // Indent the given line. The how parameter can be "smart",
-  // "add"/null, "subtract", or "prev". When aggressive is false
-  // (typically set to true for forced single-line indents), empty
-  // lines are not indented, and places where the mode returns Pass
-  // are left alone.
-  function indentLine(cm, n, how, aggressive) {
-    var doc = cm.doc, state;
-    if (how == null) { how = "add"; }
-    if (how == "smart") {
-      // Fall back to "prev" when the mode doesn't have an indentation
-      // method.
-      if (!doc.mode.indent) { how = "prev"; }
-      else { state = getContextBefore(cm, n).state; }
-    }
-
-    var tabSize = cm.options.tabSize;
-    var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
-    if (line.stateAfter) { line.stateAfter = null; }
-    var curSpaceString = line.text.match(/^\s*/)[0], indentation;
-    if (!aggressive && !/\S/.test(line.text)) {
-      indentation = 0;
-      how = "not";
-    } else if (how == "smart") {
-      indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
-      if (indentation == Pass || indentation > 150) {
-        if (!aggressive) { return }
-        how = "prev";
-      }
-    }
-    if (how == "prev") {
-      if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }
-      else { indentation = 0; }
-    } else if (how == "add") {
-      indentation = curSpace + cm.options.indentUnit;
-    } else if (how == "subtract") {
-      indentation = curSpace - cm.options.indentUnit;
-    } else if (typeof how == "number") {
-      indentation = curSpace + how;
-    }
-    indentation = Math.max(0, indentation);
-
-    var indentString = "", pos = 0;
-    if (cm.options.indentWithTabs)
-      { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} }
-    if (pos < indentation) { indentString += spaceStr(indentation - pos); }
-
-    if (indentString != curSpaceString) {
-      replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
-      line.stateAfter = null;
-      return true
-    } else {
-      // Ensure that, if the cursor was in the whitespace at the start
-      // of the line, it is moved to the end of that space.
-      for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
-        var range = doc.sel.ranges[i$1];
-        if (range.head.line == n && range.head.ch < curSpaceString.length) {
-          var pos$1 = Pos(n, curSpaceString.length);
-          replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));
-          break
-        }
-      }
-    }
-  }
-
-  // This will be set to a {lineWise: bool, text: [string]} object, so
-  // that, when pasting, we know what kind of selections the copied
-  // text was made out of.
-  var lastCopied = null;
-
-  function setLastCopied(newLastCopied) {
-    lastCopied = newLastCopied;
-  }
-
-  function applyTextInput(cm, inserted, deleted, sel, origin) {
-    var doc = cm.doc;
-    cm.display.shift = false;
-    if (!sel) { sel = doc.sel; }
-
-    var recent = +new Date - 200;
-    var paste = origin == "paste" || cm.state.pasteIncoming > recent;
-    var textLines = splitLinesAuto(inserted), multiPaste = null;
-    // When pasting N lines into N selections, insert one line per selection
-    if (paste && sel.ranges.length > 1) {
-      if (lastCopied && lastCopied.text.join("\n") == inserted) {
-        if (sel.ranges.length % lastCopied.text.length == 0) {
-          multiPaste = [];
-          for (var i = 0; i < lastCopied.text.length; i++)
-            { multiPaste.push(doc.splitLines(lastCopied.text[i])); }
-        }
-      } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
-        multiPaste = map(textLines, function (l) { return [l]; });
-      }
-    }
-
-    var updateInput = cm.curOp.updateInput;
-    // Normal behavior is to insert the new text into every selection
-    for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
-      var range = sel.ranges[i$1];
-      var from = range.from(), to = range.to();
-      if (range.empty()) {
-        if (deleted && deleted > 0) // Handle deletion
-          { from = Pos(from.line, from.ch - deleted); }
-        else if (cm.state.overwrite && !paste) // Handle overwrite
-          { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }
-        else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == textLines.join("\n"))
-          { from = to = Pos(from.line, 0); }
-      }
-      var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
-                         origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")};
-      makeChange(cm.doc, changeEvent);
-      signalLater(cm, "inputRead", cm, changeEvent);
-    }
-    if (inserted && !paste)
-      { triggerElectric(cm, inserted); }
-
-    ensureCursorVisible(cm);
-    if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; }
-    cm.curOp.typing = true;
-    cm.state.pasteIncoming = cm.state.cutIncoming = -1;
-  }
-
-  function handlePaste(e, cm) {
-    var pasted = e.clipboardData && e.clipboardData.getData("Text");
-    if (pasted) {
-      e.preventDefault();
-      if (!cm.isReadOnly() && !cm.options.disableInput)
-        { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); }
-      return true
-    }
-  }
-
-  function triggerElectric(cm, inserted) {
-    // When an 'electric' character is inserted, immediately trigger a reindent
-    if (!cm.options.electricChars || !cm.options.smartIndent) { return }
-    var sel = cm.doc.sel;
-
-    for (var i = sel.ranges.length - 1; i >= 0; i--) {
-      var range = sel.ranges[i];
-      if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue }
-      var mode = cm.getModeAt(range.head);
-      var indented = false;
-      if (mode.electricChars) {
-        for (var j = 0; j < mode.electricChars.length; j++)
-          { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
-            indented = indentLine(cm, range.head.line, "smart");
-            break
-          } }
-      } else if (mode.electricInput) {
-        if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))
-          { indented = indentLine(cm, range.head.line, "smart"); }
-      }
-      if (indented) { signalLater(cm, "electricInput", cm, range.head.line); }
-    }
-  }
-
-  function copyableRanges(cm) {
-    var text = [], ranges = [];
-    for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
-      var line = cm.doc.sel.ranges[i].head.line;
-      var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
-      ranges.push(lineRange);
-      text.push(cm.getRange(lineRange.anchor, lineRange.head));
-    }
-    return {text: text, ranges: ranges}
-  }
-
-  function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) {
-    field.setAttribute("autocorrect", autocorrect ? "" : "off");
-    field.setAttribute("autocapitalize", autocapitalize ? "" : "off");
-    field.setAttribute("spellcheck", !!spellcheck);
-  }
-
-  function hiddenTextarea() {
-    var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none");
-    var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
-    // The textarea is kept positioned near the cursor to prevent the
-    // fact that it'll be scrolled into view on input from scrolling
-    // our fake cursor out of view. On webkit, when wrap=off, paste is
-    // very slow. So make the area wide instead.
-    if (webkit) { te.style.width = "1000px"; }
-    else { te.setAttribute("wrap", "off"); }
-    // If border: 0; -- iOS fails to open keyboard (issue #1287)
-    if (ios) { te.style.border = "1px solid black"; }
-    disableBrowserMagic(te);
-    return div
-  }
-
-  // The publicly visible API. Note that methodOp(f) means
-  // 'wrap f in an operation, performed on its `this` parameter'.
-
-  // This is not the complete set of editor methods. Most of the
-  // methods defined on the Doc type are also injected into
-  // CodeMirror.prototype, for backwards compatibility and
-  // convenience.
-
-  function addEditorMethods(CodeMirror) {
-    var optionHandlers = CodeMirror.optionHandlers;
-
-    var helpers = CodeMirror.helpers = {};
-
-    CodeMirror.prototype = {
-      constructor: CodeMirror,
-      focus: function(){window.focus(); this.display.input.focus();},
-
-      setOption: function(option, value) {
-        var options = this.options, old = options[option];
-        if (options[option] == value && option != "mode") { return }
-        options[option] = value;
-        if (optionHandlers.hasOwnProperty(option))
-          { operation(this, optionHandlers[option])(this, value, old); }
-        signal(this, "optionChange", this, option);
-      },
-
-      getOption: function(option) {return this.options[option]},
-      getDoc: function() {return this.doc},
-
-      addKeyMap: function(map, bottom) {
-        this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map));
-      },
-      removeKeyMap: function(map) {
-        var maps = this.state.keyMaps;
-        for (var i = 0; i < maps.length; ++i)
-          { if (maps[i] == map || maps[i].name == map) {
-            maps.splice(i, 1);
-            return true
-          } }
-      },
-
-      addOverlay: methodOp(function(spec, options) {
-        var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
-        if (mode.startState) { throw new Error("Overlays may not be stateful.") }
-        insertSorted(this.state.overlays,
-                     {mode: mode, modeSpec: spec, opaque: options && options.opaque,
-                      priority: (options && options.priority) || 0},
-                     function (overlay) { return overlay.priority; });
-        this.state.modeGen++;
-        regChange(this);
-      }),
-      removeOverlay: methodOp(function(spec) {
-        var overlays = this.state.overlays;
-        for (var i = 0; i < overlays.length; ++i) {
-          var cur = overlays[i].modeSpec;
-          if (cur == spec || typeof spec == "string" && cur.name == spec) {
-            overlays.splice(i, 1);
-            this.state.modeGen++;
-            regChange(this);
-            return
-          }
-        }
-      }),
-
-      indentLine: methodOp(function(n, dir, aggressive) {
-        if (typeof dir != "string" && typeof dir != "number") {
-          if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; }
-          else { dir = dir ? "add" : "subtract"; }
-        }
-        if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }
-      }),
-      indentSelection: methodOp(function(how) {
-        var ranges = this.doc.sel.ranges, end = -1;
-        for (var i = 0; i < ranges.length; i++) {
-          var range = ranges[i];
-          if (!range.empty()) {
-            var from = range.from(), to = range.to();
-            var start = Math.max(end, from.line);
-            end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
-            for (var j = start; j < end; ++j)
-              { indentLine(this, j, how); }
-            var newRanges = this.doc.sel.ranges;
-            if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
-              { replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }
-          } else if (range.head.line > end) {
-            indentLine(this, range.head.line, how, true);
-            end = range.head.line;
-            if (i == this.doc.sel.primIndex) { ensureCursorVisible(this); }
-          }
-        }
-      }),
-
-      // Fetch the parser token for a given character. Useful for hacks
-      // that want to inspect the mode state (say, for completion).
-      getTokenAt: function(pos, precise) {
-        return takeToken(this, pos, precise)
-      },
-
-      getLineTokens: function(line, precise) {
-        return takeToken(this, Pos(line), precise, true)
-      },
-
-      getTokenTypeAt: function(pos) {
-        pos = clipPos(this.doc, pos);
-        var styles = getLineStyles(this, getLine(this.doc, pos.line));
-        var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
-        var type;
-        if (ch == 0) { type = styles[2]; }
-        else { for (;;) {
-          var mid = (before + after) >> 1;
-          if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }
-          else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }
-          else { type = styles[mid * 2 + 2]; break }
-        } }
-        var cut = type ? type.indexOf("overlay ") : -1;
-        return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
-      },
-
-      getModeAt: function(pos) {
-        var mode = this.doc.mode;
-        if (!mode.innerMode) { return mode }
-        return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
-      },
-
-      getHelper: function(pos, type) {
-        return this.getHelpers(pos, type)[0]
-      },
-
-      getHelpers: function(pos, type) {
-        var found = [];
-        if (!helpers.hasOwnProperty(type)) { return found }
-        var help = helpers[type], mode = this.getModeAt(pos);
-        if (typeof mode[type] == "string") {
-          if (help[mode[type]]) { found.push(help[mode[type]]); }
-        } else if (mode[type]) {
-          for (var i = 0; i < mode[type].length; i++) {
-            var val = help[mode[type][i]];
-            if (val) { found.push(val); }
-          }
-        } else if (mode.helperType && help[mode.helperType]) {
-          found.push(help[mode.helperType]);
-        } else if (help[mode.name]) {
-          found.push(help[mode.name]);
-        }
-        for (var i$1 = 0; i$1 < help._global.length; i$1++) {
-          var cur = help._global[i$1];
-          if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)
-            { found.push(cur.val); }
-        }
-        return found
-      },
-
-      getStateAfter: function(line, precise) {
-        var doc = this.doc;
-        line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
-        return getContextBefore(this, line + 1, precise).state
-      },
-
-      cursorCoords: function(start, mode) {
-        var pos, range = this.doc.sel.primary();
-        if (start == null) { pos = range.head; }
-        else if (typeof start == "object") { pos = clipPos(this.doc, start); }
-        else { pos = start ? range.from() : range.to(); }
-        return cursorCoords(this, pos, mode || "page")
-      },
-
-      charCoords: function(pos, mode) {
-        return charCoords(this, clipPos(this.doc, pos), mode || "page")
-      },
-
-      coordsChar: function(coords, mode) {
-        coords = fromCoordSystem(this, coords, mode || "page");
-        return coordsChar(this, coords.left, coords.top)
-      },
-
-      lineAtHeight: function(height, mode) {
-        height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
-        return lineAtHeight(this.doc, height + this.display.viewOffset)
-      },
-      heightAtLine: function(line, mode, includeWidgets) {
-        var end = false, lineObj;
-        if (typeof line == "number") {
-          var last = this.doc.first + this.doc.size - 1;
-          if (line < this.doc.first) { line = this.doc.first; }
-          else if (line > last) { line = last; end = true; }
-          lineObj = getLine(this.doc, line);
-        } else {
-          lineObj = line;
-        }
-        return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
-          (end ? this.doc.height - heightAtLine(lineObj) : 0)
-      },
-
-      defaultTextHeight: function() { return textHeight(this.display) },
-      defaultCharWidth: function() { return charWidth(this.display) },
-
-      getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
-
-      addWidget: function(pos, node, scroll, vert, horiz) {
-        var display = this.display;
-        pos = cursorCoords(this, clipPos(this.doc, pos));
-        var top = pos.bottom, left = pos.left;
-        node.style.position = "absolute";
-        node.setAttribute("cm-ignore-events", "true");
-        this.display.input.setUneditable(node);
-        display.sizer.appendChild(node);
-        if (vert == "over") {
-          top = pos.top;
-        } else if (vert == "above" || vert == "near") {
-          var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
-          hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
-          // Default to positioning above (if specified and possible); otherwise default to positioning below
-          if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
-            { top = pos.top - node.offsetHeight; }
-          else if (pos.bottom + node.offsetHeight <= vspace)
-            { top = pos.bottom; }
-          if (left + node.offsetWidth > hspace)
-            { left = hspace - node.offsetWidth; }
-        }
-        node.style.top = top + "px";
-        node.style.left = node.style.right = "";
-        if (horiz == "right") {
-          left = display.sizer.clientWidth - node.offsetWidth;
-          node.style.right = "0px";
-        } else {
-          if (horiz == "left") { left = 0; }
-          else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }
-          node.style.left = left + "px";
-        }
-        if (scroll)
-          { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }
-      },
-
-      triggerOnKeyDown: methodOp(onKeyDown),
-      triggerOnKeyPress: methodOp(onKeyPress),
-      triggerOnKeyUp: onKeyUp,
-      triggerOnMouseDown: methodOp(onMouseDown),
-
-      execCommand: function(cmd) {
-        if (commands.hasOwnProperty(cmd))
-          { return commands[cmd].call(null, this) }
-      },
-
-      triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
-
-      findPosH: function(from, amount, unit, visually) {
-        var dir = 1;
-        if (amount < 0) { dir = -1; amount = -amount; }
-        var cur = clipPos(this.doc, from);
-        for (var i = 0; i < amount; ++i) {
-          cur = findPosH(this.doc, cur, dir, unit, visually);
-          if (cur.hitSide) { break }
-        }
-        return cur
-      },
-
-      moveH: methodOp(function(dir, unit) {
-        var this$1 = this;
-
-        this.extendSelectionsBy(function (range) {
-          if (this$1.display.shift || this$1.doc.extend || range.empty())
-            { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) }
-          else
-            { return dir < 0 ? range.from() : range.to() }
-        }, sel_move);
-      }),
-
-      deleteH: methodOp(function(dir, unit) {
-        var sel = this.doc.sel, doc = this.doc;
-        if (sel.somethingSelected())
-          { doc.replaceSelection("", null, "+delete"); }
-        else
-          { deleteNearSelection(this, function (range) {
-            var other = findPosH(doc, range.head, dir, unit, false);
-            return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other}
-          }); }
-      }),
-
-      findPosV: function(from, amount, unit, goalColumn) {
-        var dir = 1, x = goalColumn;
-        if (amount < 0) { dir = -1; amount = -amount; }
-        var cur = clipPos(this.doc, from);
-        for (var i = 0; i < amount; ++i) {
-          var coords = cursorCoords(this, cur, "div");
-          if (x == null) { x = coords.left; }
-          else { coords.left = x; }
-          cur = findPosV(this, coords, dir, unit);
-          if (cur.hitSide) { break }
-        }
-        return cur
-      },
-
-      moveV: methodOp(function(dir, unit) {
-        var this$1 = this;
-
-        var doc = this.doc, goals = [];
-        var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();
-        doc.extendSelectionsBy(function (range) {
-          if (collapse)
-            { return dir < 0 ? range.from() : range.to() }
-          var headPos = cursorCoords(this$1, range.head, "div");
-          if (range.goalColumn != null) { headPos.left = range.goalColumn; }
-          goals.push(headPos.left);
-          var pos = findPosV(this$1, headPos, dir, unit);
-          if (unit == "page" && range == doc.sel.primary())
-            { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); }
-          return pos
-        }, sel_move);
-        if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
-          { doc.sel.ranges[i].goalColumn = goals[i]; } }
-      }),
-
-      // Find the word at the given position (as returned by coordsChar).
-      findWordAt: function(pos) {
-        var doc = this.doc, line = getLine(doc, pos.line).text;
-        var start = pos.ch, end = pos.ch;
-        if (line) {
-          var helper = this.getHelper(pos, "wordChars");
-          if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; }
-          var startChar = line.charAt(start);
-          var check = isWordChar(startChar, helper)
-            ? function (ch) { return isWordChar(ch, helper); }
-            : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
-            : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); };
-          while (start > 0 && check(line.charAt(start - 1))) { --start; }
-          while (end < line.length && check(line.charAt(end))) { ++end; }
-        }
-        return new Range(Pos(pos.line, start), Pos(pos.line, end))
-      },
-
-      toggleOverwrite: function(value) {
-        if (value != null && value == this.state.overwrite) { return }
-        if (this.state.overwrite = !this.state.overwrite)
-          { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
-        else
-          { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
-
-        signal(this, "overwriteToggle", this, this.state.overwrite);
-      },
-      hasFocus: function() { return this.display.input.getField() == activeElt() },
-      isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
-
-      scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),
-      getScrollInfo: function() {
-        var scroller = this.display.scroller;
-        return {left: scroller.scrollLeft, top: scroller.scrollTop,
-                height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
-                width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
-                clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
-      },
-
-      scrollIntoView: methodOp(function(range, margin) {
-        if (range == null) {
-          range = {from: this.doc.sel.primary().head, to: null};
-          if (margin == null) { margin = this.options.cursorScrollMargin; }
-        } else if (typeof range == "number") {
-          range = {from: Pos(range, 0), to: null};
-        } else if (range.from == null) {
-          range = {from: range, to: null};
-        }
-        if (!range.to) { range.to = range.from; }
-        range.margin = margin || 0;
-
-        if (range.from.line != null) {
-          scrollToRange(this, range);
-        } else {
-          scrollToCoordsRange(this, range.from, range.to, range.margin);
-        }
-      }),
-
-      setSize: methodOp(function(width, height) {
-        var this$1 = this;
-
-        var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; };
-        if (width != null) { this.display.wrapper.style.width = interpret(width); }
-        if (height != null) { this.display.wrapper.style.height = interpret(height); }
-        if (this.options.lineWrapping) { clearLineMeasurementCache(this); }
-        var lineNo = this.display.viewFrom;
-        this.doc.iter(lineNo, this.display.viewTo, function (line) {
-          if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
-            { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } }
-          ++lineNo;
-        });
-        this.curOp.forceUpdate = true;
-        signal(this, "refresh", this);
-      }),
-
-      operation: function(f){return runInOp(this, f)},
-      startOperation: function(){return startOperation(this)},
-      endOperation: function(){return endOperation(this)},
-
-      refresh: methodOp(function() {
-        var oldHeight = this.display.cachedTextHeight;
-        regChange(this);
-        this.curOp.forceUpdate = true;
-        clearCaches(this);
-        scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);
-        updateGutterSpace(this.display);
-        if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5 || this.options.lineWrapping)
-          { estimateLineHeights(this); }
-        signal(this, "refresh", this);
-      }),
-
-      swapDoc: methodOp(function(doc) {
-        var old = this.doc;
-        old.cm = null;
-        // Cancel the current text selection if any (#5821)
-        if (this.state.selectingText) { this.state.selectingText(); }
-        attachDoc(this, doc);
-        clearCaches(this);
-        this.display.input.reset();
-        scrollToCoords(this, doc.scrollLeft, doc.scrollTop);
-        this.curOp.forceScroll = true;
-        signalLater(this, "swapDoc", this, old);
-        return old
-      }),
-
-      phrase: function(phraseText) {
-        var phrases = this.options.phrases;
-        return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText
-      },
-
-      getInputField: function(){return this.display.input.getField()},
-      getWrapperElement: function(){return this.display.wrapper},
-      getScrollerElement: function(){return this.display.scroller},
-      getGutterElement: function(){return this.display.gutters}
-    };
-    eventMixin(CodeMirror);
-
-    CodeMirror.registerHelper = function(type, name, value) {
-      if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }
-      helpers[type][name] = value;
-    };
-    CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
-      CodeMirror.registerHelper(type, name, value);
-      helpers[type]._global.push({pred: predicate, val: value});
-    };
-  }
-
-  // Used for horizontal relative motion. Dir is -1 or 1 (left or
-  // right), unit can be "char", "column" (like char, but doesn't
-  // cross line boundaries), "word" (across next word), or "group" (to
-  // the start of next group of word or non-word-non-whitespace
-  // chars). The visually param controls whether, in right-to-left
-  // text, direction 1 means to move towards the next index in the
-  // string, or towards the character to the right of the current
-  // position. The resulting position will have a hitSide=true
-  // property if it reached the end of the document.
-  function findPosH(doc, pos, dir, unit, visually) {
-    var oldPos = pos;
-    var origDir = dir;
-    var lineObj = getLine(doc, pos.line);
-    var lineDir = visually && doc.direction == "rtl" ? -dir : dir;
-    function findNextLine() {
-      var l = pos.line + lineDir;
-      if (l < doc.first || l >= doc.first + doc.size) { return false }
-      pos = new Pos(l, pos.ch, pos.sticky);
-      return lineObj = getLine(doc, l)
-    }
-    function moveOnce(boundToLine) {
-      var next;
-      if (visually) {
-        next = moveVisually(doc.cm, lineObj, pos, dir);
-      } else {
-        next = moveLogically(lineObj, pos, dir);
-      }
-      if (next == null) {
-        if (!boundToLine && findNextLine())
-          { pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir); }
-        else
-          { return false }
-      } else {
-        pos = next;
-      }
-      return true
-    }
-
-    if (unit == "char") {
-      moveOnce();
-    } else if (unit == "column") {
-      moveOnce(true);
-    } else if (unit == "word" || unit == "group") {
-      var sawType = null, group = unit == "group";
-      var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
-      for (var first = true;; first = false) {
-        if (dir < 0 && !moveOnce(!first)) { break }
-        var cur = lineObj.text.charAt(pos.ch) || "\n";
-        var type = isWordChar(cur, helper) ? "w"
-          : group && cur == "\n" ? "n"
-          : !group || /\s/.test(cur) ? null
-          : "p";
-        if (group && !first && !type) { type = "s"; }
-        if (sawType && sawType != type) {
-          if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";}
-          break
-        }
-
-        if (type) { sawType = type; }
-        if (dir > 0 && !moveOnce(!first)) { break }
-      }
-    }
-    var result = skipAtomic(doc, pos, oldPos, origDir, true);
-    if (equalCursorPos(oldPos, result)) { result.hitSide = true; }
-    return result
-  }
-
-  // For relative vertical movement. Dir may be -1 or 1. Unit can be
-  // "page" or "line". The resulting position will have a hitSide=true
-  // property if it reached the end of the document.
-  function findPosV(cm, pos, dir, unit) {
-    var doc = cm.doc, x = pos.left, y;
-    if (unit == "page") {
-      var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
-      var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);
-      y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;
-
-    } else if (unit == "line") {
-      y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
-    }
-    var target;
-    for (;;) {
-      target = coordsChar(cm, x, y);
-      if (!target.outside) { break }
-      if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
-      y += dir * 5;
-    }
-    return target
-  }
-
-  // CONTENTEDITABLE INPUT STYLE
-
-  var ContentEditableInput = function(cm) {
-    this.cm = cm;
-    this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
-    this.polling = new Delayed();
-    this.composing = null;
-    this.gracePeriod = false;
-    this.readDOMTimeout = null;
-  };
-
-  ContentEditableInput.prototype.init = function (display) {
-      var this$1 = this;
-
-    var input = this, cm = input.cm;
-    var div = input.div = display.lineDiv;
-    disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize);
-
-    function belongsToInput(e) {
-      for (var t = e.target; t; t = t.parentNode) {
-        if (t == div) { return true }
-        if (/\bCodeMirror-(?:line)?widget\b/.test(t.className)) { break }
-      }
-      return false
-    }
-
-    on(div, "paste", function (e) {
-      if (!belongsToInput(e) || signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
-      // IE doesn't fire input events, so we schedule a read for the pasted content in this way
-      if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }
-    });
-
-    on(div, "compositionstart", function (e) {
-      this$1.composing = {data: e.data, done: false};
-    });
-    on(div, "compositionupdate", function (e) {
-      if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }
-    });
-    on(div, "compositionend", function (e) {
-      if (this$1.composing) {
-        if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }
-        this$1.composing.done = true;
-      }
-    });
-
-    on(div, "touchstart", function () { return input.forceCompositionEnd(); });
-
-    on(div, "input", function () {
-      if (!this$1.composing) { this$1.readFromDOMSoon(); }
-    });
-
-    function onCopyCut(e) {
-      if (!belongsToInput(e) || signalDOMEvent(cm, e)) { return }
-      if (cm.somethingSelected()) {
-        setLastCopied({lineWise: false, text: cm.getSelections()});
-        if (e.type == "cut") { cm.replaceSelection("", null, "cut"); }
-      } else if (!cm.options.lineWiseCopyCut) {
-        return
-      } else {
-        var ranges = copyableRanges(cm);
-        setLastCopied({lineWise: true, text: ranges.text});
-        if (e.type == "cut") {
-          cm.operation(function () {
-            cm.setSelections(ranges.ranges, 0, sel_dontScroll);
-            cm.replaceSelection("", null, "cut");
-          });
-        }
-      }
-      if (e.clipboardData) {
-        e.clipboardData.clearData();
-        var content = lastCopied.text.join("\n");
-        // iOS exposes the clipboard API, but seems to discard content inserted into it
-        e.clipboardData.setData("Text", content);
-        if (e.clipboardData.getData("Text") == content) {
-          e.preventDefault();
-          return
-        }
-      }
-      // Old-fashioned briefly-focus-a-textarea hack
-      var kludge = hiddenTextarea(), te = kludge.firstChild;
-      cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
-      te.value = lastCopied.text.join("\n");
-      var hadFocus = document.activeElement;
-      selectInput(te);
-      setTimeout(function () {
-        cm.display.lineSpace.removeChild(kludge);
-        hadFocus.focus();
-        if (hadFocus == div) { input.showPrimarySelection(); }
-      }, 50);
-    }
-    on(div, "copy", onCopyCut);
-    on(div, "cut", onCopyCut);
-  };
-
-  ContentEditableInput.prototype.screenReaderLabelChanged = function (label) {
-    // Label for screenreaders, accessibility
-    if(label) {
-      this.div.setAttribute('aria-label', label);
-    } else {
-      this.div.removeAttribute('aria-label');
-    }
-  };
-
-  ContentEditableInput.prototype.prepareSelection = function () {
-    var result = prepareSelection(this.cm, false);
-    result.focus = document.activeElement == this.div;
-    return result
-  };
-
-  ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
-    if (!info || !this.cm.display.view.length) { return }
-    if (info.focus || takeFocus) { this.showPrimarySelection(); }
-    this.showMultipleSelections(info);
-  };
-
-  ContentEditableInput.prototype.getSelection = function () {
-    return this.cm.display.wrapper.ownerDocument.getSelection()
-  };
-
-  ContentEditableInput.prototype.showPrimarySelection = function () {
-    var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();
-    var from = prim.from(), to = prim.to();
-
-    if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
-      sel.removeAllRanges();
-      return
-    }
-
-    var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
-    var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);
-    if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
-        cmp(minPos(curAnchor, curFocus), from) == 0 &&
-        cmp(maxPos(curAnchor, curFocus), to) == 0)
-      { return }
-
-    var view = cm.display.view;
-    var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
-        {node: view[0].measure.map[2], offset: 0};
-    var end = to.line < cm.display.viewTo && posToDOM(cm, to);
-    if (!end) {
-      var measure = view[view.length - 1].measure;
-      var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
-      end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]};
-    }
-
-    if (!start || !end) {
-      sel.removeAllRanges();
-      return
-    }
-
-    var old = sel.rangeCount && sel.getRangeAt(0), rng;
-    try { rng = range(start.node, start.offset, end.offset, end.node); }
-    catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
-    if (rng) {
-      if (!gecko && cm.state.focused) {
-        sel.collapse(start.node, start.offset);
-        if (!rng.collapsed) {
-          sel.removeAllRanges();
-          sel.addRange(rng);
-        }
-      } else {
-        sel.removeAllRanges();
-        sel.addRange(rng);
-      }
-      if (old && sel.anchorNode == null) { sel.addRange(old); }
-      else if (gecko) { this.startGracePeriod(); }
-    }
-    this.rememberSelection();
-  };
-
-  ContentEditableInput.prototype.startGracePeriod = function () {
-      var this$1 = this;
-
-    clearTimeout(this.gracePeriod);
-    this.gracePeriod = setTimeout(function () {
-      this$1.gracePeriod = false;
-      if (this$1.selectionChanged())
-        { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }
-    }, 20);
-  };
-
-  ContentEditableInput.prototype.showMultipleSelections = function (info) {
-    removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
-    removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
-  };
-
-  ContentEditableInput.prototype.rememberSelection = function () {
-    var sel = this.getSelection();
-    this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
-    this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
-  };
-
-  ContentEditableInput.prototype.selectionInEditor = function () {
-    var sel = this.getSelection();
-    if (!sel.rangeCount) { return false }
-    var node = sel.getRangeAt(0).commonAncestorContainer;
-    return contains(this.div, node)
-  };
-
-  ContentEditableInput.prototype.focus = function () {
-    if (this.cm.options.readOnly != "nocursor") {
-      if (!this.selectionInEditor() || document.activeElement != this.div)
-        { this.showSelection(this.prepareSelection(), true); }
-      this.div.focus();
-    }
-  };
-  ContentEditableInput.prototype.blur = function () { this.div.blur(); };
-  ContentEditableInput.prototype.getField = function () { return this.div };
-
-  ContentEditableInput.prototype.supportsTouch = function () { return true };
-
-  ContentEditableInput.prototype.receivedFocus = function () {
-    var input = this;
-    if (this.selectionInEditor())
-      { this.pollSelection(); }
-    else
-      { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }
-
-    function poll() {
-      if (input.cm.state.focused) {
-        input.pollSelection();
-        input.polling.set(input.cm.options.pollInterval, poll);
-      }
-    }
-    this.polling.set(this.cm.options.pollInterval, poll);
-  };
-
-  ContentEditableInput.prototype.selectionChanged = function () {
-    var sel = this.getSelection();
-    return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
-      sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
-  };
-
-  ContentEditableInput.prototype.pollSelection = function () {
-    if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
-    var sel = this.getSelection(), cm = this.cm;
-    // On Android Chrome (version 56, at least), backspacing into an
-    // uneditable block element will put the cursor in that element,
-    // and then, because it's not editable, hide the virtual keyboard.
-    // Because Android doesn't allow us to actually detect backspace
-    // presses in a sane way, this code checks for when that happens
-    // and simulates a backspace press in this case.
-    if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) {
-      this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs});
-      this.blur();
-      this.focus();
-      return
-    }
-    if (this.composing) { return }
-    this.rememberSelection();
-    var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
-    var head = domToPos(cm, sel.focusNode, sel.focusOffset);
-    if (anchor && head) { runInOp(cm, function () {
-      setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
-      if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }
-    }); }
-  };
-
-  ContentEditableInput.prototype.pollContent = function () {
-    if (this.readDOMTimeout != null) {
-      clearTimeout(this.readDOMTimeout);
-      this.readDOMTimeout = null;
-    }
-
-    var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
-    var from = sel.from(), to = sel.to();
-    if (from.ch == 0 && from.line > cm.firstLine())
-      { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }
-    if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
-      { to = Pos(to.line + 1, 0); }
-    if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
-
-    var fromIndex, fromLine, fromNode;
-    if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
-      fromLine = lineNo(display.view[0].line);
-      fromNode = display.view[0].node;
-    } else {
-      fromLine = lineNo(display.view[fromIndex].line);
-      fromNode = display.view[fromIndex - 1].node.nextSibling;
-    }
-    var toIndex = findViewIndex(cm, to.line);
-    var toLine, toNode;
-    if (toIndex == display.view.length - 1) {
-      toLine = display.viewTo - 1;
-      toNode = display.lineDiv.lastChild;
-    } else {
-      toLine = lineNo(display.view[toIndex + 1].line) - 1;
-      toNode = display.view[toIndex + 1].node.previousSibling;
-    }
-
-    if (!fromNode) { return false }
-    var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
-    var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
-    while (newText.length > 1 && oldText.length > 1) {
-      if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
-      else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
-      else { break }
-    }
-
-    var cutFront = 0, cutEnd = 0;
-    var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
-    while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
-      { ++cutFront; }
-    var newBot = lst(newText), oldBot = lst(oldText);
-    var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
-                             oldBot.length - (oldText.length == 1 ? cutFront : 0));
-    while (cutEnd < maxCutEnd &&
-           newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
-      { ++cutEnd; }
-    // Try to move start of change to start of selection if ambiguous
-    if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
-      while (cutFront && cutFront > from.ch &&
-             newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
-        cutFront--;
-        cutEnd++;
-      }
-    }
-
-    newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "");
-    newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "");
-
-    var chFrom = Pos(fromLine, cutFront);
-    var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
-    if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
-      replaceRange(cm.doc, newText, chFrom, chTo, "+input");
-      return true
-    }
-  };
-
-  ContentEditableInput.prototype.ensurePolled = function () {
-    this.forceCompositionEnd();
-  };
-  ContentEditableInput.prototype.reset = function () {
-    this.forceCompositionEnd();
-  };
-  ContentEditableInput.prototype.forceCompositionEnd = function () {
-    if (!this.composing) { return }
-    clearTimeout(this.readDOMTimeout);
-    this.composing = null;
-    this.updateFromDOM();
-    this.div.blur();
-    this.div.focus();
-  };
-  ContentEditableInput.prototype.readFromDOMSoon = function () {
-      var this$1 = this;
-
-    if (this.readDOMTimeout != null) { return }
-    this.readDOMTimeout = setTimeout(function () {
-      this$1.readDOMTimeout = null;
-      if (this$1.composing) {
-        if (this$1.composing.done) { this$1.composing = null; }
-        else { return }
-      }
-      this$1.updateFromDOM();
-    }, 80);
-  };
-
-  ContentEditableInput.prototype.updateFromDOM = function () {
-      var this$1 = this;
-
-    if (this.cm.isReadOnly() || !this.pollContent())
-      { runInOp(this.cm, function () { return regChange(this$1.cm); }); }
-  };
-
-  ContentEditableInput.prototype.setUneditable = function (node) {
-    node.contentEditable = "false";
-  };
-
-  ContentEditableInput.prototype.onKeyPress = function (e) {
-    if (e.charCode == 0 || this.composing) { return }
-    e.preventDefault();
-    if (!this.cm.isReadOnly())
-      { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }
-  };
-
-  ContentEditableInput.prototype.readOnlyChanged = function (val) {
-    this.div.contentEditable = String(val != "nocursor");
-  };
-
-  ContentEditableInput.prototype.onContextMenu = function () {};
-  ContentEditableInput.prototype.resetPosition = function () {};
-
-  ContentEditableInput.prototype.needsContentAttribute = true;
-
-  function posToDOM(cm, pos) {
-    var view = findViewForLine(cm, pos.line);
-    if (!view || view.hidden) { return null }
-    var line = getLine(cm.doc, pos.line);
-    var info = mapFromLineView(view, line, pos.line);
-
-    var order = getOrder(line, cm.doc.direction), side = "left";
-    if (order) {
-      var partPos = getBidiPartAt(order, pos.ch);
-      side = partPos % 2 ? "right" : "left";
-    }
-    var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
-    result.offset = result.collapse == "right" ? result.end : result.start;
-    return result
-  }
-
-  function isInGutter(node) {
-    for (var scan = node; scan; scan = scan.parentNode)
-      { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
-    return false
-  }
-
-  function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
-
-  function domTextBetween(cm, from, to, fromLine, toLine) {
-    var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false;
-    function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
-    function close() {
-      if (closing) {
-        text += lineSep;
-        if (extraLinebreak) { text += lineSep; }
-        closing = extraLinebreak = false;
-      }
-    }
-    function addText(str) {
-      if (str) {
-        close();
-        text += str;
-      }
-    }
-    function walk(node) {
-      if (node.nodeType == 1) {
-        var cmText = node.getAttribute("cm-text");
-        if (cmText) {
-          addText(cmText);
-          return
-        }
-        var markerID = node.getAttribute("cm-marker"), range;
-        if (markerID) {
-          var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
-          if (found.length && (range = found[0].find(0)))
-            { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)); }
-          return
-        }
-        if (node.getAttribute("contenteditable") == "false") { return }
-        var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName);
-        if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return }
-
-        if (isBlock) { close(); }
-        for (var i = 0; i < node.childNodes.length; i++)
-          { walk(node.childNodes[i]); }
-
-        if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; }
-        if (isBlock) { closing = true; }
-      } else if (node.nodeType == 3) {
-        addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " "));
-      }
-    }
-    for (;;) {
-      walk(from);
-      if (from == to) { break }
-      from = from.nextSibling;
-      extraLinebreak = false;
-    }
-    return text
-  }
-
-  function domToPos(cm, node, offset) {
-    var lineNode;
-    if (node == cm.display.lineDiv) {
-      lineNode = cm.display.lineDiv.childNodes[offset];
-      if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
-      node = null; offset = 0;
-    } else {
-      for (lineNode = node;; lineNode = lineNode.parentNode) {
-        if (!lineNode || lineNode == cm.display.lineDiv) { return null }
-        if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
-      }
-    }
-    for (var i = 0; i < cm.display.view.length; i++) {
-      var lineView = cm.display.view[i];
-      if (lineView.node == lineNode)
-        { return locateNodeInLineView(lineView, node, offset) }
-    }
-  }
-
-  function locateNodeInLineView(lineView, node, offset) {
-    var wrapper = lineView.text.firstChild, bad = false;
-    if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
-    if (node == wrapper) {
-      bad = true;
-      node = wrapper.childNodes[offset];
-      offset = 0;
-      if (!node) {
-        var line = lineView.rest ? lst(lineView.rest) : lineView.line;
-        return badPos(Pos(lineNo(line), line.text.length), bad)
-      }
-    }
-
-    var textNode = node.nodeType == 3 ? node : null, topNode = node;
-    if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
-      textNode = node.firstChild;
-      if (offset) { offset = textNode.nodeValue.length; }
-    }
-    while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }
-    var measure = lineView.measure, maps = measure.maps;
-
-    function find(textNode, topNode, offset) {
-      for (var i = -1; i < (maps ? maps.length : 0); i++) {
-        var map = i < 0 ? measure.map : maps[i];
-        for (var j = 0; j < map.length; j += 3) {
-          var curNode = map[j + 2];
-          if (curNode == textNode || curNode == topNode) {
-            var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
-            var ch = map[j] + offset;
-            if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)]; }
-            return Pos(line, ch)
-          }
-        }
-      }
-    }
-    var found = find(textNode, topNode, offset);
-    if (found) { return badPos(found, bad) }
-
-    // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
-    for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
-      found = find(after, after.firstChild, 0);
-      if (found)
-        { return badPos(Pos(found.line, found.ch - dist), bad) }
-      else
-        { dist += after.textContent.length; }
-    }
-    for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
-      found = find(before, before.firstChild, -1);
-      if (found)
-        { return badPos(Pos(found.line, found.ch + dist$1), bad) }
-      else
-        { dist$1 += before.textContent.length; }
-    }
-  }
-
-  // TEXTAREA INPUT STYLE
-
-  var TextareaInput = function(cm) {
-    this.cm = cm;
-    // See input.poll and input.reset
-    this.prevInput = "";
-
-    // Flag that indicates whether we expect input to appear real soon
-    // now (after some event like 'keypress' or 'input') and are
-    // polling intensively.
-    this.pollingFast = false;
-    // Self-resetting timeout for the poller
-    this.polling = new Delayed();
-    // Used to work around IE issue with selection being forgotten when focus moves away from textarea
-    this.hasSelection = false;
-    this.composing = null;
-  };
-
-  TextareaInput.prototype.init = function (display) {
-      var this$1 = this;
-
-    var input = this, cm = this.cm;
-    this.createField(display);
-    var te = this.textarea;
-
-    display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild);
-
-    // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
-    if (ios) { te.style.width = "0px"; }
-
-    on(te, "input", function () {
-      if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }
-      input.poll();
-    });
-
-    on(te, "paste", function (e) {
-      if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
-
-      cm.state.pasteIncoming = +new Date;
-      input.fastPoll();
-    });
-
-    function prepareCopyCut(e) {
-      if (signalDOMEvent(cm, e)) { return }
-      if (cm.somethingSelected()) {
-        setLastCopied({lineWise: false, text: cm.getSelections()});
-      } else if (!cm.options.lineWiseCopyCut) {
-        return
-      } else {
-        var ranges = copyableRanges(cm);
-        setLastCopied({lineWise: true, text: ranges.text});
-        if (e.type == "cut") {
-          cm.setSelections(ranges.ranges, null, sel_dontScroll);
-        } else {
-          input.prevInput = "";
-          te.value = ranges.text.join("\n");
-          selectInput(te);
-        }
-      }
-      if (e.type == "cut") { cm.state.cutIncoming = +new Date; }
-    }
-    on(te, "cut", prepareCopyCut);
-    on(te, "copy", prepareCopyCut);
-
-    on(display.scroller, "paste", function (e) {
-      if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
-      if (!te.dispatchEvent) {
-        cm.state.pasteIncoming = +new Date;
-        input.focus();
-        return
-      }
-
-      // Pass the `paste` event to the textarea so it's handled by its event listener.
-      var event = new Event("paste");
-      event.clipboardData = e.clipboardData;
-      te.dispatchEvent(event);
-    });
-
-    // Prevent normal selection in the editor (we handle our own)
-    on(display.lineSpace, "selectstart", function (e) {
-      if (!eventInWidget(display, e)) { e_preventDefault(e); }
-    });
-
-    on(te, "compositionstart", function () {
-      var start = cm.getCursor("from");
-      if (input.composing) { input.composing.range.clear(); }
-      input.composing = {
-        start: start,
-        range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
-      };
-    });
-    on(te, "compositionend", function () {
-      if (input.composing) {
-        input.poll();
-        input.composing.range.clear();
-        input.composing = null;
-      }
-    });
-  };
-
-  TextareaInput.prototype.createField = function (_display) {
-    // Wraps and hides input textarea
-    this.wrapper = hiddenTextarea();
-    // The semihidden textarea that is focused when the editor is
-    // focused, and receives input.
-    this.textarea = this.wrapper.firstChild;
-  };
-
-  TextareaInput.prototype.screenReaderLabelChanged = function (label) {
-    // Label for screenreaders, accessibility
-    if(label) {
-      this.textarea.setAttribute('aria-label', label);
-    } else {
-      this.textarea.removeAttribute('aria-label');
-    }
-  };
-
-  TextareaInput.prototype.prepareSelection = function () {
-    // Redraw the selection and/or cursor
-    var cm = this.cm, display = cm.display, doc = cm.doc;
-    var result = prepareSelection(cm);
-
-    // Move the hidden textarea near the cursor to prevent scrolling artifacts
-    if (cm.options.moveInputWithCursor) {
-      var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
-      var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
-      result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
-                                          headPos.top + lineOff.top - wrapOff.top));
-      result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
-                                           headPos.left + lineOff.left - wrapOff.left));
-    }
-
-    return result
-  };
-
-  TextareaInput.prototype.showSelection = function (drawn) {
-    var cm = this.cm, display = cm.display;
-    removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
-    removeChildrenAndAdd(display.selectionDiv, drawn.selection);
-    if (drawn.teTop != null) {
-      this.wrapper.style.top = drawn.teTop + "px";
-      this.wrapper.style.left = drawn.teLeft + "px";
-    }
-  };
-
-  // Reset the input to correspond to the selection (or to be empty,
-  // when not typing and nothing is selected)
-  TextareaInput.prototype.reset = function (typing) {
-    if (this.contextMenuPending || this.composing) { return }
-    var cm = this.cm;
-    if (cm.somethingSelected()) {
-      this.prevInput = "";
-      var content = cm.getSelection();
-      this.textarea.value = content;
-      if (cm.state.focused) { selectInput(this.textarea); }
-      if (ie && ie_version >= 9) { this.hasSelection = content; }
-    } else if (!typing) {
-      this.prevInput = this.textarea.value = "";
-      if (ie && ie_version >= 9) { this.hasSelection = null; }
-    }
-  };
-
-  TextareaInput.prototype.getField = function () { return this.textarea };
-
-  TextareaInput.prototype.supportsTouch = function () { return false };
-
-  TextareaInput.prototype.focus = function () {
-    if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
-      try { this.textarea.focus(); }
-      catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
-    }
-  };
-
-  TextareaInput.prototype.blur = function () { this.textarea.blur(); };
-
-  TextareaInput.prototype.resetPosition = function () {
-    this.wrapper.style.top = this.wrapper.style.left = 0;
-  };
-
-  TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };
-
-  // Poll for input changes, using the normal rate of polling. This
-  // runs as long as the editor is focused.
-  TextareaInput.prototype.slowPoll = function () {
-      var this$1 = this;
-
-    if (this.pollingFast) { return }
-    this.polling.set(this.cm.options.pollInterval, function () {
-      this$1.poll();
-      if (this$1.cm.state.focused) { this$1.slowPoll(); }
-    });
-  };
-
-  // When an event has just come in that is likely to add or change
-  // something in the input textarea, we poll faster, to ensure that
-  // the change appears on the screen quickly.
-  TextareaInput.prototype.fastPoll = function () {
-    var missed = false, input = this;
-    input.pollingFast = true;
-    function p() {
-      var changed = input.poll();
-      if (!changed && !missed) {missed = true; input.polling.set(60, p);}
-      else {input.pollingFast = false; input.slowPoll();}
-    }
-    input.polling.set(20, p);
-  };
-
-  // Read input from the textarea, and update the document to match.
-  // When something is selected, it is present in the textarea, and
-  // selected (unless it is huge, in which case a placeholder is
-  // used). When nothing is selected, the cursor sits after previously
-  // seen text (can be empty), which is stored in prevInput (we must
-  // not reset the textarea when typing, because that breaks IME).
-  TextareaInput.prototype.poll = function () {
-      var this$1 = this;
-
-    var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
-    // Since this is called a *lot*, try to bail out as cheaply as
-    // possible when it is clear that nothing happened. hasSelection
-    // will be the case when there is a lot of text in the textarea,
-    // in which case reading its value would be expensive.
-    if (this.contextMenuPending || !cm.state.focused ||
-        (hasSelection(input) && !prevInput && !this.composing) ||
-        cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
-      { return false }
-
-    var text = input.value;
-    // If nothing changed, bail.
-    if (text == prevInput && !cm.somethingSelected()) { return false }
-    // Work around nonsensical selection resetting in IE9/10, and
-    // inexplicable appearance of private area unicode characters on
-    // some key combos in Mac (#2689).
-    if (ie && ie_version >= 9 && this.hasSelection === text ||
-        mac && /[\uf700-\uf7ff]/.test(text)) {
-      cm.display.input.reset();
-      return false
-    }
-
-    if (cm.doc.sel == cm.display.selForContextMenu) {
-      var first = text.charCodeAt(0);
-      if (first == 0x200b && !prevInput) { prevInput = "\u200b"; }
-      if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
-    }
-    // Find the part of the input that is actually new
-    var same = 0, l = Math.min(prevInput.length, text.length);
-    while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }
-
-    runInOp(cm, function () {
-      applyTextInput(cm, text.slice(same), prevInput.length - same,
-                     null, this$1.composing ? "*compose" : null);
-
-      // Don't leave long text in the textarea, since it makes further polling slow
-      if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; }
-      else { this$1.prevInput = text; }
-
-      if (this$1.composing) {
-        this$1.composing.range.clear();
-        this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
-                                           {className: "CodeMirror-composing"});
-      }
-    });
-    return true
-  };
-
-  TextareaInput.prototype.ensurePolled = function () {
-    if (this.pollingFast && this.poll()) { this.pollingFast = false; }
-  };
-
-  TextareaInput.prototype.onKeyPress = function () {
-    if (ie && ie_version >= 9) { this.hasSelection = null; }
-    this.fastPoll();
-  };
-
-  TextareaInput.prototype.onContextMenu = function (e) {
-    var input = this, cm = input.cm, display = cm.display, te = input.textarea;
-    if (input.contextMenuPending) { input.contextMenuPending(); }
-    var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
-    if (!pos || presto) { return } // Opera is difficult.
-
-    // Reset the current text selection only if the click is done outside of the selection
-    // and 'resetSelectionOnContextMenu' option is true.
-    var reset = cm.options.resetSelectionOnContextMenu;
-    if (reset && cm.doc.sel.contains(pos) == -1)
-      { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }
-
-    var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;
-    var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect();
-    input.wrapper.style.cssText = "position: static";
-    te.style.cssText = "position: absolute; width: 30px; height: 30px;\n      top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n      z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n      outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
-    var oldScrollY;
-    if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712)
-    display.input.focus();
-    if (webkit) { window.scrollTo(null, oldScrollY); }
-    display.input.reset();
-    // Adds "Select all" to context menu in FF
-    if (!cm.somethingSelected()) { te.value = input.prevInput = " "; }
-    input.contextMenuPending = rehide;
-    display.selForContextMenu = cm.doc.sel;
-    clearTimeout(display.detectingSelectAll);
-
-    // Select-all will be greyed out if there's nothing to select, so
-    // this adds a zero-width space so that we can later check whether
-    // it got selected.
-    function prepareSelectAllHack() {
-      if (te.selectionStart != null) {
-        var selected = cm.somethingSelected();
-        var extval = "\u200b" + (selected ? te.value : "");
-        te.value = "\u21da"; // Used to catch context-menu undo
-        te.value = extval;
-        input.prevInput = selected ? "" : "\u200b";
-        te.selectionStart = 1; te.selectionEnd = extval.length;
-        // Re-set this, in case some other handler touched the
-        // selection in the meantime.
-        display.selForContextMenu = cm.doc.sel;
-      }
-    }
-    function rehide() {
-      if (input.contextMenuPending != rehide) { return }
-      input.contextMenuPending = false;
-      input.wrapper.style.cssText = oldWrapperCSS;
-      te.style.cssText = oldCSS;
-      if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }
-
-      // Try to detect the user choosing select-all
-      if (te.selectionStart != null) {
-        if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }
-        var i = 0, poll = function () {
-          if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
-              te.selectionEnd > 0 && input.prevInput == "\u200b") {
-            operation(cm, selectAll)(cm);
-          } else if (i++ < 10) {
-            display.detectingSelectAll = setTimeout(poll, 500);
-          } else {
-            display.selForContextMenu = null;
-            display.input.reset();
-          }
-        };
-        display.detectingSelectAll = setTimeout(poll, 200);
-      }
-    }
-
-    if (ie && ie_version >= 9) { prepareSelectAllHack(); }
-    if (captureRightClick) {
-      e_stop(e);
-      var mouseup = function () {
-        off(window, "mouseup", mouseup);
-        setTimeout(rehide, 20);
-      };
-      on(window, "mouseup", mouseup);
-    } else {
-      setTimeout(rehide, 50);
-    }
-  };
-
-  TextareaInput.prototype.readOnlyChanged = function (val) {
-    if (!val) { this.reset(); }
-    this.textarea.disabled = val == "nocursor";
-  };
-
-  TextareaInput.prototype.setUneditable = function () {};
-
-  TextareaInput.prototype.needsContentAttribute = false;
-
-  function fromTextArea(textarea, options) {
-    options = options ? copyObj(options) : {};
-    options.value = textarea.value;
-    if (!options.tabindex && textarea.tabIndex)
-      { options.tabindex = textarea.tabIndex; }
-    if (!options.placeholder && textarea.placeholder)
-      { options.placeholder = textarea.placeholder; }
-    // Set autofocus to true if this textarea is focused, or if it has
-    // autofocus and no other element is focused.
-    if (options.autofocus == null) {
-      var hasFocus = activeElt();
-      options.autofocus = hasFocus == textarea ||
-        textarea.getAttribute("autofocus") != null && hasFocus == document.body;
-    }
-
-    function save() {textarea.value = cm.getValue();}
-
-    var realSubmit;
-    if (textarea.form) {
-      on(textarea.form, "submit", save);
-      // Deplorable hack to make the submit method do the right thing.
-      if (!options.leaveSubmitMethodAlone) {
-        var form = textarea.form;
-        realSubmit = form.submit;
-        try {
-          var wrappedSubmit = form.submit = function () {
-            save();
-            form.submit = realSubmit;
-            form.submit();
-            form.submit = wrappedSubmit;
-          };
-        } catch(e) {}
-      }
-    }
-
-    options.finishInit = function (cm) {
-      cm.save = save;
-      cm.getTextArea = function () { return textarea; };
-      cm.toTextArea = function () {
-        cm.toTextArea = isNaN; // Prevent this from being ran twice
-        save();
-        textarea.parentNode.removeChild(cm.getWrapperElement());
-        textarea.style.display = "";
-        if (textarea.form) {
-          off(textarea.form, "submit", save);
-          if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function")
-            { textarea.form.submit = realSubmit; }
-        }
-      };
-    };
-
-    textarea.style.display = "none";
-    var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
-      options);
-    return cm
-  }
-
-  function addLegacyProps(CodeMirror) {
-    CodeMirror.off = off;
-    CodeMirror.on = on;
-    CodeMirror.wheelEventPixels = wheelEventPixels;
-    CodeMirror.Doc = Doc;
-    CodeMirror.splitLines = splitLinesAuto;
-    CodeMirror.countColumn = countColumn;
-    CodeMirror.findColumn = findColumn;
-    CodeMirror.isWordChar = isWordCharBasic;
-    CodeMirror.Pass = Pass;
-    CodeMirror.signal = signal;
-    CodeMirror.Line = Line;
-    CodeMirror.changeEnd = changeEnd;
-    CodeMirror.scrollbarModel = scrollbarModel;
-    CodeMirror.Pos = Pos;
-    CodeMirror.cmpPos = cmp;
-    CodeMirror.modes = modes;
-    CodeMirror.mimeModes = mimeModes;
-    CodeMirror.resolveMode = resolveMode;
-    CodeMirror.getMode = getMode;
-    CodeMirror.modeExtensions = modeExtensions;
-    CodeMirror.extendMode = extendMode;
-    CodeMirror.copyState = copyState;
-    CodeMirror.startState = startState;
-    CodeMirror.innerMode = innerMode;
-    CodeMirror.commands = commands;
-    CodeMirror.keyMap = keyMap;
-    CodeMirror.keyName = keyName;
-    CodeMirror.isModifierKey = isModifierKey;
-    CodeMirror.lookupKey = lookupKey;
-    CodeMirror.normalizeKeyMap = normalizeKeyMap;
-    CodeMirror.StringStream = StringStream;
-    CodeMirror.SharedTextMarker = SharedTextMarker;
-    CodeMirror.TextMarker = TextMarker;
-    CodeMirror.LineWidget = LineWidget;
-    CodeMirror.e_preventDefault = e_preventDefault;
-    CodeMirror.e_stopPropagation = e_stopPropagation;
-    CodeMirror.e_stop = e_stop;
-    CodeMirror.addClass = addClass;
-    CodeMirror.contains = contains;
-    CodeMirror.rmClass = rmClass;
-    CodeMirror.keyNames = keyNames;
-  }
-
-  // EDITOR CONSTRUCTOR
-
-  defineOptions(CodeMirror);
-
-  addEditorMethods(CodeMirror);
-
-  // Set up methods on CodeMirror's prototype to redirect to the editor's document.
-  var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
-  for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
-    { CodeMirror.prototype[prop] = (function(method) {
-      return function() {return method.apply(this.doc, arguments)}
-    })(Doc.prototype[prop]); } }
-
-  eventMixin(Doc);
-  CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
-
-  // Extra arguments are stored as the mode's dependencies, which is
-  // used by (legacy) mechanisms like loadmode.js to automatically
-  // load a mode. (Preferred mechanism is the require/define calls.)
-  CodeMirror.defineMode = function(name/*, mode, …*/) {
-    if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; }
-    defineMode.apply(this, arguments);
-  };
-
-  CodeMirror.defineMIME = defineMIME;
-
-  // Minimal default mode.
-  CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
-  CodeMirror.defineMIME("text/plain", "null");
-
-  // EXTENSIONS
-
-  CodeMirror.defineExtension = function (name, func) {
-    CodeMirror.prototype[name] = func;
-  };
-  CodeMirror.defineDocExtension = function (name, func) {
-    Doc.prototype[name] = func;
-  };
-
-  CodeMirror.fromTextArea = fromTextArea;
-
-  addLegacyProps(CodeMirror);
-
-  CodeMirror.version = "5.56.0";
-
-  return CodeMirror;
-
-})));
diff --git a/plugins/UiFileManager/media/codemirror/extension/dialog/dialog.css b/plugins/UiFileManager/media/codemirror/extension/dialog/dialog.css
deleted file mode 100644
index 677c0783..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/dialog/dialog.css
+++ /dev/null
@@ -1,32 +0,0 @@
-.CodeMirror-dialog {
-  position: absolute;
-  left: 0; right: 0;
-  background: inherit;
-  z-index: 15;
-  padding: .1em .8em;
-  overflow: hidden;
-  color: inherit;
-}
-
-.CodeMirror-dialog-top {
-  border-bottom: 1px solid #eee;
-  top: 0;
-}
-
-.CodeMirror-dialog-bottom {
-  border-top: 1px solid #eee;
-  bottom: 0;
-}
-
-.CodeMirror-dialog input {
-  border: none;
-  outline: none;
-  background: transparent;
-  width: 20em;
-  color: inherit;
-  font-family: monospace;
-}
-
-.CodeMirror-dialog button {
-  font-size: 70%;
-}
diff --git a/plugins/UiFileManager/media/codemirror/extension/dialog/dialog.js b/plugins/UiFileManager/media/codemirror/extension/dialog/dialog.js
deleted file mode 100644
index 5f1f4aa4..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/dialog/dialog.js
+++ /dev/null
@@ -1,163 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-// Open simple dialogs on top of an editor. Relies on dialog.css.
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  function dialogDiv(cm, template, bottom) {
-    var wrap = cm.getWrapperElement();
-    var dialog;
-    dialog = wrap.appendChild(document.createElement("div"));
-    if (bottom)
-      dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom";
-    else
-      dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";
-
-    if (typeof template == "string") {
-      dialog.innerHTML = template;
-    } else { // Assuming it's a detached DOM element.
-      dialog.appendChild(template);
-    }
-    CodeMirror.addClass(wrap, 'dialog-opened');
-    return dialog;
-  }
-
-  function closeNotification(cm, newVal) {
-    if (cm.state.currentNotificationClose)
-      cm.state.currentNotificationClose();
-    cm.state.currentNotificationClose = newVal;
-  }
-
-  CodeMirror.defineExtension("openDialog", function(template, callback, options) {
-    if (!options) options = {};
-
-    closeNotification(this, null);
-
-    var dialog = dialogDiv(this, template, options.bottom);
-    var closed = false, me = this;
-    function close(newVal) {
-      if (typeof newVal == 'string') {
-        inp.value = newVal;
-      } else {
-        if (closed) return;
-        closed = true;
-        CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
-        dialog.parentNode.removeChild(dialog);
-        me.focus();
-
-        if (options.onClose) options.onClose(dialog);
-      }
-    }
-
-    var inp = dialog.getElementsByTagName("input")[0], button;
-    if (inp) {
-      inp.focus();
-
-      if (options.value) {
-        inp.value = options.value;
-        if (options.selectValueOnOpen !== false) {
-          inp.select();
-        }
-      }
-
-      if (options.onInput)
-        CodeMirror.on(inp, "input", function(e) { options.onInput(e, inp.value, close);});
-      if (options.onKeyUp)
-        CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);});
-
-      CodeMirror.on(inp, "keydown", function(e) {
-        if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }
-        if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) {
-          inp.blur();
-          CodeMirror.e_stop(e);
-          close();
-        }
-        if (e.keyCode == 13) callback(inp.value, e);
-      });
-
-      if (options.closeOnBlur !== false) CodeMirror.on(dialog, "focusout", function (evt) {
-        if (evt.relatedTarget !== null) close();
-      });
-    } else if (button = dialog.getElementsByTagName("button")[0]) {
-      CodeMirror.on(button, "click", function() {
-        close();
-        me.focus();
-      });
-
-      if (options.closeOnBlur !== false) CodeMirror.on(button, "blur", close);
-
-      button.focus();
-    }
-    return close;
-  });
-
-  CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) {
-    closeNotification(this, null);
-    var dialog = dialogDiv(this, template, options && options.bottom);
-    var buttons = dialog.getElementsByTagName("button");
-    var closed = false, me = this, blurring = 1;
-    function close() {
-      if (closed) return;
-      closed = true;
-      CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
-      dialog.parentNode.removeChild(dialog);
-      me.focus();
-    }
-    buttons[0].focus();
-    for (var i = 0; i < buttons.length; ++i) {
-      var b = buttons[i];
-      (function(callback) {
-        CodeMirror.on(b, "click", function(e) {
-          CodeMirror.e_preventDefault(e);
-          close();
-          if (callback) callback(me);
-        });
-      })(callbacks[i]);
-      CodeMirror.on(b, "blur", function() {
-        --blurring;
-        setTimeout(function() { if (blurring <= 0) close(); }, 200);
-      });
-      CodeMirror.on(b, "focus", function() { ++blurring; });
-    }
-  });
-
-  /*
-   * openNotification
-   * Opens a notification, that can be closed with an optional timer
-   * (default 5000ms timer) and always closes on click.
-   *
-   * If a notification is opened while another is opened, it will close the
-   * currently opened one and open the new one immediately.
-   */
-  CodeMirror.defineExtension("openNotification", function(template, options) {
-    closeNotification(this, close);
-    var dialog = dialogDiv(this, template, options && options.bottom);
-    var closed = false, doneTimer;
-    var duration = options && typeof options.duration !== "undefined" ? options.duration : 5000;
-
-    function close() {
-      if (closed) return;
-      closed = true;
-      clearTimeout(doneTimer);
-      CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
-      dialog.parentNode.removeChild(dialog);
-    }
-
-    CodeMirror.on(dialog, 'click', function(e) {
-      CodeMirror.e_preventDefault(e);
-      close();
-    });
-
-    if (duration)
-      doneTimer = setTimeout(close, duration);
-
-    return close;
-  });
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/edit/closebrackets.js b/plugins/UiFileManager/media/codemirror/extension/edit/closebrackets.js
deleted file mode 100644
index 4415c393..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/edit/closebrackets.js
+++ /dev/null
@@ -1,191 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  var defaults = {
-    pairs: "()[]{}''\"\"",
-    closeBefore: ")]}'\":;>",
-    triples: "",
-    explode: "[]{}"
-  };
-
-  var Pos = CodeMirror.Pos;
-
-  CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
-    if (old && old != CodeMirror.Init) {
-      cm.removeKeyMap(keyMap);
-      cm.state.closeBrackets = null;
-    }
-    if (val) {
-      ensureBound(getOption(val, "pairs"))
-      cm.state.closeBrackets = val;
-      cm.addKeyMap(keyMap);
-    }
-  });
-
-  function getOption(conf, name) {
-    if (name == "pairs" && typeof conf == "string") return conf;
-    if (typeof conf == "object" && conf[name] != null) return conf[name];
-    return defaults[name];
-  }
-
-  var keyMap = {Backspace: handleBackspace, Enter: handleEnter};
-  function ensureBound(chars) {
-    for (var i = 0; i < chars.length; i++) {
-      var ch = chars.charAt(i), key = "'" + ch + "'"
-      if (!keyMap[key]) keyMap[key] = handler(ch)
-    }
-  }
-  ensureBound(defaults.pairs + "`")
-
-  function handler(ch) {
-    return function(cm) { return handleChar(cm, ch); };
-  }
-
-  function getConfig(cm) {
-    var deflt = cm.state.closeBrackets;
-    if (!deflt || deflt.override) return deflt;
-    var mode = cm.getModeAt(cm.getCursor());
-    return mode.closeBrackets || deflt;
-  }
-
-  function handleBackspace(cm) {
-    var conf = getConfig(cm);
-    if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
-
-    var pairs = getOption(conf, "pairs");
-    var ranges = cm.listSelections();
-    for (var i = 0; i < ranges.length; i++) {
-      if (!ranges[i].empty()) return CodeMirror.Pass;
-      var around = charsAround(cm, ranges[i].head);
-      if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
-    }
-    for (var i = ranges.length - 1; i >= 0; i--) {
-      var cur = ranges[i].head;
-      cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1), "+delete");
-    }
-  }
-
-  function handleEnter(cm) {
-    var conf = getConfig(cm);
-    var explode = conf && getOption(conf, "explode");
-    if (!explode || cm.getOption("disableInput")) return CodeMirror.Pass;
-
-    var ranges = cm.listSelections();
-    for (var i = 0; i < ranges.length; i++) {
-      if (!ranges[i].empty()) return CodeMirror.Pass;
-      var around = charsAround(cm, ranges[i].head);
-      if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass;
-    }
-    cm.operation(function() {
-      var linesep = cm.lineSeparator() || "\n";
-      cm.replaceSelection(linesep + linesep, null);
-      cm.execCommand("goCharLeft");
-      ranges = cm.listSelections();
-      for (var i = 0; i < ranges.length; i++) {
-        var line = ranges[i].head.line;
-        cm.indentLine(line, null, true);
-        cm.indentLine(line + 1, null, true);
-      }
-    });
-  }
-
-  function contractSelection(sel) {
-    var inverted = CodeMirror.cmpPos(sel.anchor, sel.head) > 0;
-    return {anchor: new Pos(sel.anchor.line, sel.anchor.ch + (inverted ? -1 : 1)),
-            head: new Pos(sel.head.line, sel.head.ch + (inverted ? 1 : -1))};
-  }
-
-  function handleChar(cm, ch) {
-    var conf = getConfig(cm);
-    if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
-
-    var pairs = getOption(conf, "pairs");
-    var pos = pairs.indexOf(ch);
-    if (pos == -1) return CodeMirror.Pass;
-
-    var closeBefore = getOption(conf,"closeBefore");
-
-    var triples = getOption(conf, "triples");
-
-    var identical = pairs.charAt(pos + 1) == ch;
-    var ranges = cm.listSelections();
-    var opening = pos % 2 == 0;
-
-    var type;
-    for (var i = 0; i < ranges.length; i++) {
-      var range = ranges[i], cur = range.head, curType;
-      var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1));
-      if (opening && !range.empty()) {
-        curType = "surround";
-      } else if ((identical || !opening) && next == ch) {
-        if (identical && stringStartsAfter(cm, cur))
-          curType = "both";
-        else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch)
-          curType = "skipThree";
-        else
-          curType = "skip";
-      } else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
-                 cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch) {
-        if (cur.ch > 2 && /\bstring/.test(cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2)))) return CodeMirror.Pass;
-        curType = "addFour";
-      } else if (identical) {
-        var prev = cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur)
-        if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = "both";
-        else return CodeMirror.Pass;
-      } else if (opening && (next.length === 0 || /\s/.test(next) || closeBefore.indexOf(next) > -1)) {
-        curType = "both";
-      } else {
-        return CodeMirror.Pass;
-      }
-      if (!type) type = curType;
-      else if (type != curType) return CodeMirror.Pass;
-    }
-
-    var left = pos % 2 ? pairs.charAt(pos - 1) : ch;
-    var right = pos % 2 ? ch : pairs.charAt(pos + 1);
-    cm.operation(function() {
-      if (type == "skip") {
-        cm.execCommand("goCharRight");
-      } else if (type == "skipThree") {
-        for (var i = 0; i < 3; i++)
-          cm.execCommand("goCharRight");
-      } else if (type == "surround") {
-        var sels = cm.getSelections();
-        for (var i = 0; i < sels.length; i++)
-          sels[i] = left + sels[i] + right;
-        cm.replaceSelections(sels, "around");
-        sels = cm.listSelections().slice();
-        for (var i = 0; i < sels.length; i++)
-          sels[i] = contractSelection(sels[i]);
-        cm.setSelections(sels);
-      } else if (type == "both") {
-        cm.replaceSelection(left + right, null);
-        cm.triggerElectric(left + right);
-        cm.execCommand("goCharLeft");
-      } else if (type == "addFour") {
-        cm.replaceSelection(left + left + left + left, "before");
-        cm.execCommand("goCharRight");
-      }
-    });
-  }
-
-  function charsAround(cm, pos) {
-    var str = cm.getRange(Pos(pos.line, pos.ch - 1),
-                          Pos(pos.line, pos.ch + 1));
-    return str.length == 2 ? str : null;
-  }
-
-  function stringStartsAfter(cm, pos) {
-    var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1))
-    return /\bstring/.test(token.type) && token.start == pos.ch &&
-      (pos.ch == 0 || !/\bstring/.test(cm.getTokenTypeAt(pos)))
-  }
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/edit/closetag.js b/plugins/UiFileManager/media/codemirror/extension/edit/closetag.js
deleted file mode 100644
index 8689765e..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/edit/closetag.js
+++ /dev/null
@@ -1,184 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-/**
- * Tag-closer extension for CodeMirror.
- *
- * This extension adds an "autoCloseTags" option that can be set to
- * either true to get the default behavior, or an object to further
- * configure its behavior.
- *
- * These are supported options:
- *
- * `whenClosing` (default true)
- *   Whether to autoclose when the '/' of a closing tag is typed.
- * `whenOpening` (default true)
- *   Whether to autoclose the tag when the final '>' of an opening
- *   tag is typed.
- * `dontCloseTags` (default is empty tags for HTML, none for XML)
- *   An array of tag names that should not be autoclosed.
- * `indentTags` (default is block tags for HTML, none for XML)
- *   An array of tag names that should, when opened, cause a
- *   blank line to be added inside the tag, and the blank line and
- *   closing line to be indented.
- * `emptyTags` (default is none)
- *   An array of XML tag names that should be autoclosed with '/>'.
- *
- * See demos/closetag.html for a usage example.
- */
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "../fold/xml-fold"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) {
-    if (old != CodeMirror.Init && old)
-      cm.removeKeyMap("autoCloseTags");
-    if (!val) return;
-    var map = {name: "autoCloseTags"};
-    if (typeof val != "object" || val.whenClosing !== false)
-      map["'/'"] = function(cm) { return autoCloseSlash(cm); };
-    if (typeof val != "object" || val.whenOpening !== false)
-      map["'>'"] = function(cm) { return autoCloseGT(cm); };
-    cm.addKeyMap(map);
-  });
-
-  var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
-                       "source", "track", "wbr"];
-  var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4",
-                    "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"];
-
-  function autoCloseGT(cm) {
-    if (cm.getOption("disableInput")) return CodeMirror.Pass;
-    var ranges = cm.listSelections(), replacements = [];
-    var opt = cm.getOption("autoCloseTags");
-    for (var i = 0; i < ranges.length; i++) {
-      if (!ranges[i].empty()) return CodeMirror.Pass;
-      var pos = ranges[i].head, tok = cm.getTokenAt(pos);
-      var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
-      var tagInfo = inner.mode.xmlCurrentTag && inner.mode.xmlCurrentTag(state)
-      var tagName = tagInfo && tagInfo.name
-      if (!tagName) return CodeMirror.Pass
-
-      var html = inner.mode.configuration == "html";
-      var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
-      var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
-
-      if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
-      var lowerTagName = tagName.toLowerCase();
-      // Don't process the '>' at the end of an end-tag or self-closing tag
-      if (!tagName ||
-          tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) ||
-          tok.type == "tag" && tagInfo.close ||
-          tok.string.indexOf("/") == (pos.ch - tok.start - 1) || // match something like <someTagName />
-          dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 ||
-          closingTagExists(cm, inner.mode.xmlCurrentContext && inner.mode.xmlCurrentContext(state) || [], tagName, pos, true))
-        return CodeMirror.Pass;
-
-      var emptyTags = typeof opt == "object" && opt.emptyTags;
-      if (emptyTags && indexOf(emptyTags, tagName) > -1) {
-        replacements[i] = { text: "/>", newPos: CodeMirror.Pos(pos.line, pos.ch + 2) };
-        continue;
-      }
-
-      var indent = indentTags && indexOf(indentTags, lowerTagName) > -1;
-      replacements[i] = {indent: indent,
-                         text: ">" + (indent ? "\n\n" : "") + "</" + tagName + ">",
-                         newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1)};
-    }
-
-    var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnAutoClose);
-    for (var i = ranges.length - 1; i >= 0; i--) {
-      var info = replacements[i];
-      cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, "+insert");
-      var sel = cm.listSelections().slice(0);
-      sel[i] = {head: info.newPos, anchor: info.newPos};
-      cm.setSelections(sel);
-      if (!dontIndentOnAutoClose && info.indent) {
-        cm.indentLine(info.newPos.line, null, true);
-        cm.indentLine(info.newPos.line + 1, null, true);
-      }
-    }
-  }
-
-  function autoCloseCurrent(cm, typingSlash) {
-    var ranges = cm.listSelections(), replacements = [];
-    var head = typingSlash ? "/" : "</";
-    var opt = cm.getOption("autoCloseTags");
-    var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnSlash);
-    for (var i = 0; i < ranges.length; i++) {
-      if (!ranges[i].empty()) return CodeMirror.Pass;
-      var pos = ranges[i].head, tok = cm.getTokenAt(pos);
-      var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
-      if (typingSlash && (tok.type == "string" || tok.string.charAt(0) != "<" ||
-                          tok.start != pos.ch - 1))
-        return CodeMirror.Pass;
-      // Kludge to get around the fact that we are not in XML mode
-      // when completing in JS/CSS snippet in htmlmixed mode. Does not
-      // work for other XML embedded languages (there is no general
-      // way to go from a mixed mode to its current XML state).
-      var replacement, mixed = inner.mode.name != "xml" && cm.getMode().name == "htmlmixed"
-      if (mixed && inner.mode.name == "javascript") {
-        replacement = head + "script";
-      } else if (mixed && inner.mode.name == "css") {
-        replacement = head + "style";
-      } else {
-        var context = inner.mode.xmlCurrentContext && inner.mode.xmlCurrentContext(state)
-        if (!context || (context.length && closingTagExists(cm, context, context[context.length - 1], pos)))
-          return CodeMirror.Pass;
-        replacement = head + context[context.length - 1]
-      }
-      if (cm.getLine(pos.line).charAt(tok.end) != ">") replacement += ">";
-      replacements[i] = replacement;
-    }
-    cm.replaceSelections(replacements);
-    ranges = cm.listSelections();
-    if (!dontIndentOnAutoClose) {
-        for (var i = 0; i < ranges.length; i++)
-            if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
-                cm.indentLine(ranges[i].head.line);
-    }
-  }
-
-  function autoCloseSlash(cm) {
-    if (cm.getOption("disableInput")) return CodeMirror.Pass;
-    return autoCloseCurrent(cm, true);
-  }
-
-  CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); };
-
-  function indexOf(collection, elt) {
-    if (collection.indexOf) return collection.indexOf(elt);
-    for (var i = 0, e = collection.length; i < e; ++i)
-      if (collection[i] == elt) return i;
-    return -1;
-  }
-
-  // If xml-fold is loaded, we use its functionality to try and verify
-  // whether a given tag is actually unclosed.
-  function closingTagExists(cm, context, tagName, pos, newTag) {
-    if (!CodeMirror.scanForClosingTag) return false;
-    var end = Math.min(cm.lastLine() + 1, pos.line + 500);
-    var nextClose = CodeMirror.scanForClosingTag(cm, pos, null, end);
-    if (!nextClose || nextClose.tag != tagName) return false;
-    // If the immediate wrapping context contains onCx instances of
-    // the same tag, a closing tag only exists if there are at least
-    // that many closing tags of that type following.
-    var onCx = newTag ? 1 : 0
-    for (var i = context.length - 1; i >= 0; i--) {
-      if (context[i] == tagName) ++onCx
-      else break
-    }
-    pos = nextClose.to;
-    for (var i = 1; i < onCx; i++) {
-      var next = CodeMirror.scanForClosingTag(cm, pos, null, end);
-      if (!next || next.tag != tagName) return false;
-      pos = next.to;
-    }
-    return true;
-  }
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/edit/continuelist.js b/plugins/UiFileManager/media/codemirror/extension/edit/continuelist.js
deleted file mode 100644
index 2e5625ad..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/edit/continuelist.js
+++ /dev/null
@@ -1,101 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  var listRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/,
-      emptyListRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/,
-      unorderedListRE = /[*+-]\s/;
-
-  CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
-    if (cm.getOption("disableInput")) return CodeMirror.Pass;
-    var ranges = cm.listSelections(), replacements = [];
-    for (var i = 0; i < ranges.length; i++) {
-      var pos = ranges[i].head;
-
-      // If we're not in Markdown mode, fall back to normal newlineAndIndent
-      var eolState = cm.getStateAfter(pos.line);
-      var inner = CodeMirror.innerMode(cm.getMode(), eolState);
-      if (inner.mode.name !== "markdown") {
-        cm.execCommand("newlineAndIndent");
-        return;
-      } else {
-        eolState = inner.state;
-      }
-
-      var inList = eolState.list !== false;
-      var inQuote = eolState.quote !== 0;
-
-      var line = cm.getLine(pos.line), match = listRE.exec(line);
-      var cursorBeforeBullet = /^\s*$/.test(line.slice(0, pos.ch));
-      if (!ranges[i].empty() || (!inList && !inQuote) || !match || cursorBeforeBullet) {
-        cm.execCommand("newlineAndIndent");
-        return;
-      }
-      if (emptyListRE.test(line)) {
-        var endOfQuote = inQuote && />\s*$/.test(line)
-        var endOfList = !/>\s*$/.test(line)
-        if (endOfQuote || endOfList) cm.replaceRange("", {
-          line: pos.line, ch: 0
-        }, {
-          line: pos.line, ch: pos.ch + 1
-        });
-        replacements[i] = "\n";
-      } else {
-        var indent = match[1], after = match[5];
-        var numbered = !(unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0);
-        var bullet = numbered ? (parseInt(match[3], 10) + 1) + match[4] : match[2].replace("x", " ");
-        replacements[i] = "\n" + indent + bullet + after;
-
-        if (numbered) incrementRemainingMarkdownListNumbers(cm, pos);
-      }
-    }
-
-    cm.replaceSelections(replacements);
-  };
-
-  // Auto-updating Markdown list numbers when a new item is added to the
-  // middle of a list
-  function incrementRemainingMarkdownListNumbers(cm, pos) {
-    var startLine = pos.line, lookAhead = 0, skipCount = 0;
-    var startItem = listRE.exec(cm.getLine(startLine)), startIndent = startItem[1];
-
-    do {
-      lookAhead += 1;
-      var nextLineNumber = startLine + lookAhead;
-      var nextLine = cm.getLine(nextLineNumber), nextItem = listRE.exec(nextLine);
-
-      if (nextItem) {
-        var nextIndent = nextItem[1];
-        var newNumber = (parseInt(startItem[3], 10) + lookAhead - skipCount);
-        var nextNumber = (parseInt(nextItem[3], 10)), itemNumber = nextNumber;
-
-        if (startIndent === nextIndent && !isNaN(nextNumber)) {
-          if (newNumber === nextNumber) itemNumber = nextNumber + 1;
-          if (newNumber > nextNumber) itemNumber = newNumber + 1;
-          cm.replaceRange(
-            nextLine.replace(listRE, nextIndent + itemNumber + nextItem[4] + nextItem[5]),
-          {
-            line: nextLineNumber, ch: 0
-          }, {
-            line: nextLineNumber, ch: nextLine.length
-          });
-        } else {
-          if (startIndent.length > nextIndent.length) return;
-          // This doesn't run if the next line immediatley indents, as it is
-          // not clear of the users intention (new indented item or same level)
-          if ((startIndent.length < nextIndent.length) && (lookAhead === 1)) return;
-          skipCount += 1;
-        }
-      }
-    } while (nextItem);
-  }
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/edit/matchbrackets.js b/plugins/UiFileManager/media/codemirror/extension/edit/matchbrackets.js
deleted file mode 100644
index 2c47e070..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/edit/matchbrackets.js
+++ /dev/null
@@ -1,158 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
-    (document.documentMode == null || document.documentMode < 8);
-
-  var Pos = CodeMirror.Pos;
-
-  var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<"};
-
-  function bracketRegex(config) {
-    return config && config.bracketRegex || /[(){}[\]]/
-  }
-
-  function findMatchingBracket(cm, where, config) {
-    var line = cm.getLineHandle(where.line), pos = where.ch - 1;
-    var afterCursor = config && config.afterCursor
-    if (afterCursor == null)
-      afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className)
-    var re = bracketRegex(config)
-
-    // A cursor is defined as between two characters, but in in vim command mode
-    // (i.e. not insert mode), the cursor is visually represented as a
-    // highlighted box on top of the 2nd character. Otherwise, we allow matches
-    // from before or after the cursor.
-    var match = (!afterCursor && pos >= 0 && re.test(line.text.charAt(pos)) && matching[line.text.charAt(pos)]) ||
-        re.test(line.text.charAt(pos + 1)) && matching[line.text.charAt(++pos)];
-    if (!match) return null;
-    var dir = match.charAt(1) == ">" ? 1 : -1;
-    if (config && config.strict && (dir > 0) != (pos == where.ch)) return null;
-    var style = cm.getTokenTypeAt(Pos(where.line, pos + 1));
-
-    var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config);
-    if (found == null) return null;
-    return {from: Pos(where.line, pos), to: found && found.pos,
-            match: found && found.ch == match.charAt(0), forward: dir > 0};
-  }
-
-  // bracketRegex is used to specify which type of bracket to scan
-  // should be a regexp, e.g. /[[\]]/
-  //
-  // Note: If "where" is on an open bracket, then this bracket is ignored.
-  //
-  // Returns false when no bracket was found, null when it reached
-  // maxScanLines and gave up
-  function scanForBracket(cm, where, dir, style, config) {
-    var maxScanLen = (config && config.maxScanLineLength) || 10000;
-    var maxScanLines = (config && config.maxScanLines) || 1000;
-
-    var stack = [];
-    var re = bracketRegex(config)
-    var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1)
-                          : Math.max(cm.firstLine() - 1, where.line - maxScanLines);
-    for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) {
-      var line = cm.getLine(lineNo);
-      if (!line) continue;
-      var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1;
-      if (line.length > maxScanLen) continue;
-      if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0);
-      for (; pos != end; pos += dir) {
-        var ch = line.charAt(pos);
-        if (re.test(ch) && (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) {
-          var match = matching[ch];
-          if (match && (match.charAt(1) == ">") == (dir > 0)) stack.push(ch);
-          else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch};
-          else stack.pop();
-        }
-      }
-    }
-    return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null;
-  }
-
-  function matchBrackets(cm, autoclear, config) {
-    // Disable brace matching in long lines, since it'll cause hugely slow updates
-    var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000;
-    var marks = [], ranges = cm.listSelections();
-    for (var i = 0; i < ranges.length; i++) {
-      var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config);
-      if (match && cm.getLine(match.from.line).length <= maxHighlightLen) {
-        var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
-        marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style}));
-        if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen)
-          marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style}));
-      }
-    }
-
-    if (marks.length) {
-      // Kludge to work around the IE bug from issue #1193, where text
-      // input stops going to the textare whever this fires.
-      if (ie_lt8 && cm.state.focused) cm.focus();
-
-      var clear = function() {
-        cm.operation(function() {
-          for (var i = 0; i < marks.length; i++) marks[i].clear();
-        });
-      };
-      if (autoclear) setTimeout(clear, 800);
-      else return clear;
-    }
-  }
-
-  function doMatchBrackets(cm) {
-    cm.operation(function() {
-      if (cm.state.matchBrackets.currentlyHighlighted) {
-        cm.state.matchBrackets.currentlyHighlighted();
-        cm.state.matchBrackets.currentlyHighlighted = null;
-      }
-      cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
-    });
-  }
-
-  CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
-    function clear(cm) {
-      if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) {
-        cm.state.matchBrackets.currentlyHighlighted();
-        cm.state.matchBrackets.currentlyHighlighted = null;
-      }
-    }
-
-    if (old && old != CodeMirror.Init) {
-      cm.off("cursorActivity", doMatchBrackets);
-      cm.off("focus", doMatchBrackets)
-      cm.off("blur", clear)
-      clear(cm);
-    }
-    if (val) {
-      cm.state.matchBrackets = typeof val == "object" ? val : {};
-      cm.on("cursorActivity", doMatchBrackets);
-      cm.on("focus", doMatchBrackets)
-      cm.on("blur", clear)
-    }
-  });
-
-  CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
-  CodeMirror.defineExtension("findMatchingBracket", function(pos, config, oldConfig){
-    // Backwards-compatibility kludge
-    if (oldConfig || typeof config == "boolean") {
-      if (!oldConfig) {
-        config = config ? {strict: true} : null
-      } else {
-        oldConfig.strict = config
-        config = oldConfig
-      }
-    }
-    return findMatchingBracket(this, pos, config)
-  });
-  CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){
-    return scanForBracket(this, pos, dir, style, config);
-  });
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/edit/matchtags.js b/plugins/UiFileManager/media/codemirror/extension/edit/matchtags.js
deleted file mode 100644
index 2203d939..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/edit/matchtags.js
+++ /dev/null
@@ -1,66 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "../fold/xml-fold"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  CodeMirror.defineOption("matchTags", false, function(cm, val, old) {
-    if (old && old != CodeMirror.Init) {
-      cm.off("cursorActivity", doMatchTags);
-      cm.off("viewportChange", maybeUpdateMatch);
-      clear(cm);
-    }
-    if (val) {
-      cm.state.matchBothTags = typeof val == "object" && val.bothTags;
-      cm.on("cursorActivity", doMatchTags);
-      cm.on("viewportChange", maybeUpdateMatch);
-      doMatchTags(cm);
-    }
-  });
-
-  function clear(cm) {
-    if (cm.state.tagHit) cm.state.tagHit.clear();
-    if (cm.state.tagOther) cm.state.tagOther.clear();
-    cm.state.tagHit = cm.state.tagOther = null;
-  }
-
-  function doMatchTags(cm) {
-    cm.state.failedTagMatch = false;
-    cm.operation(function() {
-      clear(cm);
-      if (cm.somethingSelected()) return;
-      var cur = cm.getCursor(), range = cm.getViewport();
-      range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to);
-      var match = CodeMirror.findMatchingTag(cm, cur, range);
-      if (!match) return;
-      if (cm.state.matchBothTags) {
-        var hit = match.at == "open" ? match.open : match.close;
-        if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"});
-      }
-      var other = match.at == "close" ? match.open : match.close;
-      if (other)
-        cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"});
-      else
-        cm.state.failedTagMatch = true;
-    });
-  }
-
-  function maybeUpdateMatch(cm) {
-    if (cm.state.failedTagMatch) doMatchTags(cm);
-  }
-
-  CodeMirror.commands.toMatchingTag = function(cm) {
-    var found = CodeMirror.findMatchingTag(cm, cm.getCursor());
-    if (found) {
-      var other = found.at == "close" ? found.open : found.close;
-      if (other) cm.extendSelection(other.to, other.from);
-    }
-  };
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/edit/trailingspace.js b/plugins/UiFileManager/media/codemirror/extension/edit/trailingspace.js
deleted file mode 100644
index c39c310a..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/edit/trailingspace.js
+++ /dev/null
@@ -1,27 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) {
-    if (prev == CodeMirror.Init) prev = false;
-    if (prev && !val)
-      cm.removeOverlay("trailingspace");
-    else if (!prev && val)
-      cm.addOverlay({
-        token: function(stream) {
-          for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {}
-          if (i > stream.pos) { stream.pos = i; return null; }
-          stream.pos = l;
-          return "trailingspace";
-        },
-        name: "trailingspace"
-      });
-  });
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/fold/brace-fold.js b/plugins/UiFileManager/media/codemirror/extension/fold/brace-fold.js
deleted file mode 100644
index 654d1fb6..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/fold/brace-fold.js
+++ /dev/null
@@ -1,105 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-CodeMirror.registerHelper("fold", "brace", function(cm, start) {
-  var line = start.line, lineText = cm.getLine(line);
-  var tokenType;
-
-  function findOpening(openCh) {
-    for (var at = start.ch, pass = 0;;) {
-      var found = at <= 0 ? -1 : lineText.lastIndexOf(openCh, at - 1);
-      if (found == -1) {
-        if (pass == 1) break;
-        pass = 1;
-        at = lineText.length;
-        continue;
-      }
-      if (pass == 1 && found < start.ch) break;
-      tokenType = cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1));
-      if (!/^(comment|string)/.test(tokenType)) return found + 1;
-      at = found - 1;
-    }
-  }
-
-  var startToken = "{", endToken = "}", startCh = findOpening("{");
-  if (startCh == null) {
-    startToken = "[", endToken = "]";
-    startCh = findOpening("[");
-  }
-
-  if (startCh == null) return;
-  var count = 1, lastLine = cm.lastLine(), end, endCh;
-  outer: for (var i = line; i <= lastLine; ++i) {
-    var text = cm.getLine(i), pos = i == line ? startCh : 0;
-    for (;;) {
-      var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);
-      if (nextOpen < 0) nextOpen = text.length;
-      if (nextClose < 0) nextClose = text.length;
-      pos = Math.min(nextOpen, nextClose);
-      if (pos == text.length) break;
-      if (cm.getTokenTypeAt(CodeMirror.Pos(i, pos + 1)) == tokenType) {
-        if (pos == nextOpen) ++count;
-        else if (!--count) { end = i; endCh = pos; break outer; }
-      }
-      ++pos;
-    }
-  }
-  if (end == null || line == end) return;
-  return {from: CodeMirror.Pos(line, startCh),
-          to: CodeMirror.Pos(end, endCh)};
-});
-
-CodeMirror.registerHelper("fold", "import", function(cm, start) {
-  function hasImport(line) {
-    if (line < cm.firstLine() || line > cm.lastLine()) return null;
-    var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
-    if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
-    if (start.type != "keyword" || start.string != "import") return null;
-    // Now find closing semicolon, return its position
-    for (var i = line, e = Math.min(cm.lastLine(), line + 10); i <= e; ++i) {
-      var text = cm.getLine(i), semi = text.indexOf(";");
-      if (semi != -1) return {startCh: start.end, end: CodeMirror.Pos(i, semi)};
-    }
-  }
-
-  var startLine = start.line, has = hasImport(startLine), prev;
-  if (!has || hasImport(startLine - 1) || ((prev = hasImport(startLine - 2)) && prev.end.line == startLine - 1))
-    return null;
-  for (var end = has.end;;) {
-    var next = hasImport(end.line + 1);
-    if (next == null) break;
-    end = next.end;
-  }
-  return {from: cm.clipPos(CodeMirror.Pos(startLine, has.startCh + 1)), to: end};
-});
-
-CodeMirror.registerHelper("fold", "include", function(cm, start) {
-  function hasInclude(line) {
-    if (line < cm.firstLine() || line > cm.lastLine()) return null;
-    var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
-    if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
-    if (start.type == "meta" && start.string.slice(0, 8) == "#include") return start.start + 8;
-  }
-
-  var startLine = start.line, has = hasInclude(startLine);
-  if (has == null || hasInclude(startLine - 1) != null) return null;
-  for (var end = startLine;;) {
-    var next = hasInclude(end + 1);
-    if (next == null) break;
-    ++end;
-  }
-  return {from: CodeMirror.Pos(startLine, has + 1),
-          to: cm.clipPos(CodeMirror.Pos(end))};
-});
-
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/fold/comment-fold.js b/plugins/UiFileManager/media/codemirror/extension/fold/comment-fold.js
deleted file mode 100644
index 836101d8..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/fold/comment-fold.js
+++ /dev/null
@@ -1,59 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-CodeMirror.registerGlobalHelper("fold", "comment", function(mode) {
-  return mode.blockCommentStart && mode.blockCommentEnd;
-}, function(cm, start) {
-  var mode = cm.getModeAt(start), startToken = mode.blockCommentStart, endToken = mode.blockCommentEnd;
-  if (!startToken || !endToken) return;
-  var line = start.line, lineText = cm.getLine(line);
-
-  var startCh;
-  for (var at = start.ch, pass = 0;;) {
-    var found = at <= 0 ? -1 : lineText.lastIndexOf(startToken, at - 1);
-    if (found == -1) {
-      if (pass == 1) return;
-      pass = 1;
-      at = lineText.length;
-      continue;
-    }
-    if (pass == 1 && found < start.ch) return;
-    if (/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1))) &&
-        (found == 0 || lineText.slice(found - endToken.length, found) == endToken ||
-         !/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found))))) {
-      startCh = found + startToken.length;
-      break;
-    }
-    at = found - 1;
-  }
-
-  var depth = 1, lastLine = cm.lastLine(), end, endCh;
-  outer: for (var i = line; i <= lastLine; ++i) {
-    var text = cm.getLine(i), pos = i == line ? startCh : 0;
-    for (;;) {
-      var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);
-      if (nextOpen < 0) nextOpen = text.length;
-      if (nextClose < 0) nextClose = text.length;
-      pos = Math.min(nextOpen, nextClose);
-      if (pos == text.length) break;
-      if (pos == nextOpen) ++depth;
-      else if (!--depth) { end = i; endCh = pos; break outer; }
-      ++pos;
-    }
-  }
-  if (end == null || line == end && endCh == startCh) return;
-  return {from: CodeMirror.Pos(line, startCh),
-          to: CodeMirror.Pos(end, endCh)};
-});
-
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/fold/foldcode.js b/plugins/UiFileManager/media/codemirror/extension/fold/foldcode.js
deleted file mode 100644
index 887df3fe..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/fold/foldcode.js
+++ /dev/null
@@ -1,157 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  function doFold(cm, pos, options, force) {
-    if (options && options.call) {
-      var finder = options;
-      options = null;
-    } else {
-      var finder = getOption(cm, options, "rangeFinder");
-    }
-    if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0);
-    var minSize = getOption(cm, options, "minFoldSize");
-
-    function getRange(allowFolded) {
-      var range = finder(cm, pos);
-      if (!range || range.to.line - range.from.line < minSize) return null;
-      var marks = cm.findMarksAt(range.from);
-      for (var i = 0; i < marks.length; ++i) {
-        if (marks[i].__isFold && force !== "fold") {
-          if (!allowFolded) return null;
-          range.cleared = true;
-          marks[i].clear();
-        }
-      }
-      return range;
-    }
-
-    var range = getRange(true);
-    if (getOption(cm, options, "scanUp")) while (!range && pos.line > cm.firstLine()) {
-      pos = CodeMirror.Pos(pos.line - 1, 0);
-      range = getRange(false);
-    }
-    if (!range || range.cleared || force === "unfold") return;
-
-    var myWidget = makeWidget(cm, options, range);
-    CodeMirror.on(myWidget, "mousedown", function(e) {
-      myRange.clear();
-      CodeMirror.e_preventDefault(e);
-    });
-    var myRange = cm.markText(range.from, range.to, {
-      replacedWith: myWidget,
-      clearOnEnter: getOption(cm, options, "clearOnEnter"),
-      __isFold: true
-    });
-    myRange.on("clear", function(from, to) {
-      CodeMirror.signal(cm, "unfold", cm, from, to);
-    });
-    CodeMirror.signal(cm, "fold", cm, range.from, range.to);
-  }
-
-  function makeWidget(cm, options, range) {
-    var widget = getOption(cm, options, "widget");
-
-    if (typeof widget == "function") {
-      widget = widget(range.from, range.to);
-    }
-
-    if (typeof widget == "string") {
-      var text = document.createTextNode(widget);
-      widget = document.createElement("span");
-      widget.appendChild(text);
-      widget.className = "CodeMirror-foldmarker";
-    } else if (widget) {
-      widget = widget.cloneNode(true)
-    }
-    return widget;
-  }
-
-  // Clumsy backwards-compatible interface
-  CodeMirror.newFoldFunction = function(rangeFinder, widget) {
-    return function(cm, pos) { doFold(cm, pos, {rangeFinder: rangeFinder, widget: widget}); };
-  };
-
-  // New-style interface
-  CodeMirror.defineExtension("foldCode", function(pos, options, force) {
-    doFold(this, pos, options, force);
-  });
-
-  CodeMirror.defineExtension("isFolded", function(pos) {
-    var marks = this.findMarksAt(pos);
-    for (var i = 0; i < marks.length; ++i)
-      if (marks[i].__isFold) return true;
-  });
-
-  CodeMirror.commands.toggleFold = function(cm) {
-    cm.foldCode(cm.getCursor());
-  };
-  CodeMirror.commands.fold = function(cm) {
-    cm.foldCode(cm.getCursor(), null, "fold");
-  };
-  CodeMirror.commands.unfold = function(cm) {
-    cm.foldCode(cm.getCursor(), null, "unfold");
-  };
-  CodeMirror.commands.foldAll = function(cm) {
-    cm.operation(function() {
-      for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)
-        cm.foldCode(CodeMirror.Pos(i, 0), null, "fold");
-    });
-  };
-  CodeMirror.commands.unfoldAll = function(cm) {
-    cm.operation(function() {
-      for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)
-        cm.foldCode(CodeMirror.Pos(i, 0), null, "unfold");
-    });
-  };
-
-  CodeMirror.registerHelper("fold", "combine", function() {
-    var funcs = Array.prototype.slice.call(arguments, 0);
-    return function(cm, start) {
-      for (var i = 0; i < funcs.length; ++i) {
-        var found = funcs[i](cm, start);
-        if (found) return found;
-      }
-    };
-  });
-
-  CodeMirror.registerHelper("fold", "auto", function(cm, start) {
-    var helpers = cm.getHelpers(start, "fold");
-    for (var i = 0; i < helpers.length; i++) {
-      var cur = helpers[i](cm, start);
-      if (cur) return cur;
-    }
-  });
-
-  var defaultOptions = {
-    rangeFinder: CodeMirror.fold.auto,
-    widget: "\u2194",
-    minFoldSize: 0,
-    scanUp: false,
-    clearOnEnter: true
-  };
-
-  CodeMirror.defineOption("foldOptions", null);
-
-  function getOption(cm, options, name) {
-    if (options && options[name] !== undefined)
-      return options[name];
-    var editorOptions = cm.options.foldOptions;
-    if (editorOptions && editorOptions[name] !== undefined)
-      return editorOptions[name];
-    return defaultOptions[name];
-  }
-
-  CodeMirror.defineExtension("foldOption", function(options, name) {
-    return getOption(this, options, name);
-  });
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/fold/foldgutter.css b/plugins/UiFileManager/media/codemirror/extension/fold/foldgutter.css
deleted file mode 100644
index ad19ae2d..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/fold/foldgutter.css
+++ /dev/null
@@ -1,20 +0,0 @@
-.CodeMirror-foldmarker {
-  color: blue;
-  text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;
-  font-family: arial;
-  line-height: .3;
-  cursor: pointer;
-}
-.CodeMirror-foldgutter {
-  width: .7em;
-}
-.CodeMirror-foldgutter-open,
-.CodeMirror-foldgutter-folded {
-  cursor: pointer;
-}
-.CodeMirror-foldgutter-open:after {
-  content: "\25BE";
-}
-.CodeMirror-foldgutter-folded:after {
-  content: "\25B8";
-}
diff --git a/plugins/UiFileManager/media/codemirror/extension/fold/foldgutter.js b/plugins/UiFileManager/media/codemirror/extension/fold/foldgutter.js
deleted file mode 100644
index 7d46a609..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/fold/foldgutter.js
+++ /dev/null
@@ -1,163 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("./foldcode"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "./foldcode"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  CodeMirror.defineOption("foldGutter", false, function(cm, val, old) {
-    if (old && old != CodeMirror.Init) {
-      cm.clearGutter(cm.state.foldGutter.options.gutter);
-      cm.state.foldGutter = null;
-      cm.off("gutterClick", onGutterClick);
-      cm.off("changes", onChange);
-      cm.off("viewportChange", onViewportChange);
-      cm.off("fold", onFold);
-      cm.off("unfold", onFold);
-      cm.off("swapDoc", onChange);
-    }
-    if (val) {
-      cm.state.foldGutter = new State(parseOptions(val));
-      updateInViewport(cm);
-      cm.on("gutterClick", onGutterClick);
-      cm.on("changes", onChange);
-      cm.on("viewportChange", onViewportChange);
-      cm.on("fold", onFold);
-      cm.on("unfold", onFold);
-      cm.on("swapDoc", onChange);
-    }
-  });
-
-  var Pos = CodeMirror.Pos;
-
-  function State(options) {
-    this.options = options;
-    this.from = this.to = 0;
-  }
-
-  function parseOptions(opts) {
-    if (opts === true) opts = {};
-    if (opts.gutter == null) opts.gutter = "CodeMirror-foldgutter";
-    if (opts.indicatorOpen == null) opts.indicatorOpen = "CodeMirror-foldgutter-open";
-    if (opts.indicatorFolded == null) opts.indicatorFolded = "CodeMirror-foldgutter-folded";
-    return opts;
-  }
-
-  function isFolded(cm, line) {
-    var marks = cm.findMarks(Pos(line, 0), Pos(line + 1, 0));
-    for (var i = 0; i < marks.length; ++i) {
-      if (marks[i].__isFold) {
-        var fromPos = marks[i].find(-1);
-        if (fromPos && fromPos.line === line)
-          return marks[i];
-      }
-    }
-  }
-
-  function marker(spec) {
-    if (typeof spec == "string") {
-      var elt = document.createElement("div");
-      elt.className = spec + " CodeMirror-guttermarker-subtle";
-      return elt;
-    } else {
-      return spec.cloneNode(true);
-    }
-  }
-
-  function updateFoldInfo(cm, from, to) {
-    var opts = cm.state.foldGutter.options, cur = from - 1;
-    var minSize = cm.foldOption(opts, "minFoldSize");
-    var func = cm.foldOption(opts, "rangeFinder");
-    // we can reuse the built-in indicator element if its className matches the new state
-    var clsFolded = typeof opts.indicatorFolded == "string" && classTest(opts.indicatorFolded);
-    var clsOpen = typeof opts.indicatorOpen == "string" && classTest(opts.indicatorOpen);
-    cm.eachLine(from, to, function(line) {
-      ++cur;
-      var mark = null;
-      var old = line.gutterMarkers;
-      if (old) old = old[opts.gutter];
-      if (isFolded(cm, cur)) {
-        if (clsFolded && old && clsFolded.test(old.className)) return;
-        mark = marker(opts.indicatorFolded);
-      } else {
-        var pos = Pos(cur, 0);
-        var range = func && func(cm, pos);
-        if (range && range.to.line - range.from.line >= minSize) {
-          if (clsOpen && old && clsOpen.test(old.className)) return;
-          mark = marker(opts.indicatorOpen);
-        }
-      }
-      if (!mark && !old) return;
-      cm.setGutterMarker(line, opts.gutter, mark);
-    });
-  }
-
-  // copied from CodeMirror/src/util/dom.js
-  function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
-
-  function updateInViewport(cm) {
-    var vp = cm.getViewport(), state = cm.state.foldGutter;
-    if (!state) return;
-    cm.operation(function() {
-      updateFoldInfo(cm, vp.from, vp.to);
-    });
-    state.from = vp.from; state.to = vp.to;
-  }
-
-  function onGutterClick(cm, line, gutter) {
-    var state = cm.state.foldGutter;
-    if (!state) return;
-    var opts = state.options;
-    if (gutter != opts.gutter) return;
-    var folded = isFolded(cm, line);
-    if (folded) folded.clear();
-    else cm.foldCode(Pos(line, 0), opts);
-  }
-
-  function onChange(cm) {
-    var state = cm.state.foldGutter;
-    if (!state) return;
-    var opts = state.options;
-    state.from = state.to = 0;
-    clearTimeout(state.changeUpdate);
-    state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, opts.foldOnChangeTimeSpan || 600);
-  }
-
-  function onViewportChange(cm) {
-    var state = cm.state.foldGutter;
-    if (!state) return;
-    var opts = state.options;
-    clearTimeout(state.changeUpdate);
-    state.changeUpdate = setTimeout(function() {
-      var vp = cm.getViewport();
-      if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {
-        updateInViewport(cm);
-      } else {
-        cm.operation(function() {
-          if (vp.from < state.from) {
-            updateFoldInfo(cm, vp.from, state.from);
-            state.from = vp.from;
-          }
-          if (vp.to > state.to) {
-            updateFoldInfo(cm, state.to, vp.to);
-            state.to = vp.to;
-          }
-        });
-      }
-    }, opts.updateViewportTimeSpan || 400);
-  }
-
-  function onFold(cm, from) {
-    var state = cm.state.foldGutter;
-    if (!state) return;
-    var line = from.line;
-    if (line >= state.from && line < state.to)
-      updateFoldInfo(cm, line, line + 1);
-  }
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/fold/indent-fold.js b/plugins/UiFileManager/media/codemirror/extension/fold/indent-fold.js
deleted file mode 100644
index 0cc11264..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/fold/indent-fold.js
+++ /dev/null
@@ -1,48 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-function lineIndent(cm, lineNo) {
-  var text = cm.getLine(lineNo)
-  var spaceTo = text.search(/\S/)
-  if (spaceTo == -1 || /\bcomment\b/.test(cm.getTokenTypeAt(CodeMirror.Pos(lineNo, spaceTo + 1))))
-    return -1
-  return CodeMirror.countColumn(text, null, cm.getOption("tabSize"))
-}
-
-CodeMirror.registerHelper("fold", "indent", function(cm, start) {
-  var myIndent = lineIndent(cm, start.line)
-  if (myIndent < 0) return
-  var lastLineInFold = null
-
-  // Go through lines until we find a line that definitely doesn't belong in
-  // the block we're folding, or to the end.
-  for (var i = start.line + 1, end = cm.lastLine(); i <= end; ++i) {
-    var indent = lineIndent(cm, i)
-    if (indent == -1) {
-    } else if (indent > myIndent) {
-      // Lines with a greater indent are considered part of the block.
-      lastLineInFold = i;
-    } else {
-      // If this line has non-space, non-comment content, and is
-      // indented less or equal to the start line, it is the start of
-      // another block.
-      break;
-    }
-  }
-  if (lastLineInFold) return {
-    from: CodeMirror.Pos(start.line, cm.getLine(start.line).length),
-    to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length)
-  };
-});
-
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/fold/markdown-fold.js b/plugins/UiFileManager/media/codemirror/extension/fold/markdown-fold.js
deleted file mode 100644
index 6a551786..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/fold/markdown-fold.js
+++ /dev/null
@@ -1,49 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-CodeMirror.registerHelper("fold", "markdown", function(cm, start) {
-  var maxDepth = 100;
-
-  function isHeader(lineNo) {
-    var tokentype = cm.getTokenTypeAt(CodeMirror.Pos(lineNo, 0));
-    return tokentype && /\bheader\b/.test(tokentype);
-  }
-
-  function headerLevel(lineNo, line, nextLine) {
-    var match = line && line.match(/^#+/);
-    if (match && isHeader(lineNo)) return match[0].length;
-    match = nextLine && nextLine.match(/^[=\-]+\s*$/);
-    if (match && isHeader(lineNo + 1)) return nextLine[0] == "=" ? 1 : 2;
-    return maxDepth;
-  }
-
-  var firstLine = cm.getLine(start.line), nextLine = cm.getLine(start.line + 1);
-  var level = headerLevel(start.line, firstLine, nextLine);
-  if (level === maxDepth) return undefined;
-
-  var lastLineNo = cm.lastLine();
-  var end = start.line, nextNextLine = cm.getLine(end + 2);
-  while (end < lastLineNo) {
-    if (headerLevel(end + 1, nextLine, nextNextLine) <= level) break;
-    ++end;
-    nextLine = nextNextLine;
-    nextNextLine = cm.getLine(end + 2);
-  }
-
-  return {
-    from: CodeMirror.Pos(start.line, firstLine.length),
-    to: CodeMirror.Pos(end, cm.getLine(end).length)
-  };
-});
-
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/fold/xml-fold.js b/plugins/UiFileManager/media/codemirror/extension/fold/xml-fold.js
deleted file mode 100644
index 13bc3838..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/fold/xml-fold.js
+++ /dev/null
@@ -1,184 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  var Pos = CodeMirror.Pos;
-  function cmp(a, b) { return a.line - b.line || a.ch - b.ch; }
-
-  var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
-  var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
-  var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g");
-
-  function Iter(cm, line, ch, range) {
-    this.line = line; this.ch = ch;
-    this.cm = cm; this.text = cm.getLine(line);
-    this.min = range ? Math.max(range.from, cm.firstLine()) : cm.firstLine();
-    this.max = range ? Math.min(range.to - 1, cm.lastLine()) : cm.lastLine();
-  }
-
-  function tagAt(iter, ch) {
-    var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch));
-    return type && /\btag\b/.test(type);
-  }
-
-  function nextLine(iter) {
-    if (iter.line >= iter.max) return;
-    iter.ch = 0;
-    iter.text = iter.cm.getLine(++iter.line);
-    return true;
-  }
-  function prevLine(iter) {
-    if (iter.line <= iter.min) return;
-    iter.text = iter.cm.getLine(--iter.line);
-    iter.ch = iter.text.length;
-    return true;
-  }
-
-  function toTagEnd(iter) {
-    for (;;) {
-      var gt = iter.text.indexOf(">", iter.ch);
-      if (gt == -1) { if (nextLine(iter)) continue; else return; }
-      if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; }
-      var lastSlash = iter.text.lastIndexOf("/", gt);
-      var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
-      iter.ch = gt + 1;
-      return selfClose ? "selfClose" : "regular";
-    }
-  }
-  function toTagStart(iter) {
-    for (;;) {
-      var lt = iter.ch ? iter.text.lastIndexOf("<", iter.ch - 1) : -1;
-      if (lt == -1) { if (prevLine(iter)) continue; else return; }
-      if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; }
-      xmlTagStart.lastIndex = lt;
-      iter.ch = lt;
-      var match = xmlTagStart.exec(iter.text);
-      if (match && match.index == lt) return match;
-    }
-  }
-
-  function toNextTag(iter) {
-    for (;;) {
-      xmlTagStart.lastIndex = iter.ch;
-      var found = xmlTagStart.exec(iter.text);
-      if (!found) { if (nextLine(iter)) continue; else return; }
-      if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; }
-      iter.ch = found.index + found[0].length;
-      return found;
-    }
-  }
-  function toPrevTag(iter) {
-    for (;;) {
-      var gt = iter.ch ? iter.text.lastIndexOf(">", iter.ch - 1) : -1;
-      if (gt == -1) { if (prevLine(iter)) continue; else return; }
-      if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; }
-      var lastSlash = iter.text.lastIndexOf("/", gt);
-      var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
-      iter.ch = gt + 1;
-      return selfClose ? "selfClose" : "regular";
-    }
-  }
-
-  function findMatchingClose(iter, tag) {
-    var stack = [];
-    for (;;) {
-      var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0);
-      if (!next || !(end = toTagEnd(iter))) return;
-      if (end == "selfClose") continue;
-      if (next[1]) { // closing tag
-        for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {
-          stack.length = i;
-          break;
-        }
-        if (i < 0 && (!tag || tag == next[2])) return {
-          tag: next[2],
-          from: Pos(startLine, startCh),
-          to: Pos(iter.line, iter.ch)
-        };
-      } else { // opening tag
-        stack.push(next[2]);
-      }
-    }
-  }
-  function findMatchingOpen(iter, tag) {
-    var stack = [];
-    for (;;) {
-      var prev = toPrevTag(iter);
-      if (!prev) return;
-      if (prev == "selfClose") { toTagStart(iter); continue; }
-      var endLine = iter.line, endCh = iter.ch;
-      var start = toTagStart(iter);
-      if (!start) return;
-      if (start[1]) { // closing tag
-        stack.push(start[2]);
-      } else { // opening tag
-        for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) {
-          stack.length = i;
-          break;
-        }
-        if (i < 0 && (!tag || tag == start[2])) return {
-          tag: start[2],
-          from: Pos(iter.line, iter.ch),
-          to: Pos(endLine, endCh)
-        };
-      }
-    }
-  }
-
-  CodeMirror.registerHelper("fold", "xml", function(cm, start) {
-    var iter = new Iter(cm, start.line, 0);
-    for (;;) {
-      var openTag = toNextTag(iter)
-      if (!openTag || iter.line != start.line) return
-      var end = toTagEnd(iter)
-      if (!end) return
-      if (!openTag[1] && end != "selfClose") {
-        var startPos = Pos(iter.line, iter.ch);
-        var endPos = findMatchingClose(iter, openTag[2]);
-        return endPos && cmp(endPos.from, startPos) > 0 ? {from: startPos, to: endPos.from} : null
-      }
-    }
-  });
-  CodeMirror.findMatchingTag = function(cm, pos, range) {
-    var iter = new Iter(cm, pos.line, pos.ch, range);
-    if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return;
-    var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch);
-    var start = end && toTagStart(iter);
-    if (!end || !start || cmp(iter, pos) > 0) return;
-    var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]};
-    if (end == "selfClose") return {open: here, close: null, at: "open"};
-
-    if (start[1]) { // closing tag
-      return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"};
-    } else { // opening tag
-      iter = new Iter(cm, to.line, to.ch, range);
-      return {open: here, close: findMatchingClose(iter, start[2]), at: "open"};
-    }
-  };
-
-  CodeMirror.findEnclosingTag = function(cm, pos, range, tag) {
-    var iter = new Iter(cm, pos.line, pos.ch, range);
-    for (;;) {
-      var open = findMatchingOpen(iter, tag);
-      if (!open) break;
-      var forward = new Iter(cm, pos.line, pos.ch, range);
-      var close = findMatchingClose(forward, open.tag);
-      if (close) return {open: open, close: close};
-    }
-  };
-
-  // Used by addon/edit/closetag.js
-  CodeMirror.scanForClosingTag = function(cm, pos, name, end) {
-    var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null);
-    return findMatchingClose(iter, name);
-  };
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/hint/anyword-hint.js b/plugins/UiFileManager/media/codemirror/extension/hint/anyword-hint.js
deleted file mode 100644
index d27a9ec0..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/hint/anyword-hint.js
+++ /dev/null
@@ -1,41 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  var WORD = /[\w$]+/, RANGE = 500;
-
-  CodeMirror.registerHelper("hint", "anyword", function(editor, options) {
-    var word = options && options.word || WORD;
-    var range = options && options.range || RANGE;
-    var cur = editor.getCursor(), curLine = editor.getLine(cur.line);
-    var end = cur.ch, start = end;
-    while (start && word.test(curLine.charAt(start - 1))) --start;
-    var curWord = start != end && curLine.slice(start, end);
-
-    var list = options && options.list || [], seen = {};
-    var re = new RegExp(word.source, "g");
-    for (var dir = -1; dir <= 1; dir += 2) {
-      var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir;
-      for (; line != endLine; line += dir) {
-        var text = editor.getLine(line), m;
-        while (m = re.exec(text)) {
-          if (line == cur.line && m[0] === curWord) continue;
-          if ((!curWord || m[0].lastIndexOf(curWord, 0) == 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) {
-            seen[m[0]] = true;
-            list.push(m[0]);
-          }
-        }
-      }
-    }
-    return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)};
-  });
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/hint/html-hint.js b/plugins/UiFileManager/media/codemirror/extension/hint/html-hint.js
deleted file mode 100644
index d0cca4f6..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/hint/html-hint.js
+++ /dev/null
@@ -1,350 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("./xml-hint"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "./xml-hint"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  var langs = "ab aa af ak sq am ar an hy as av ae ay az bm ba eu be bn bh bi bs br bg my ca ch ce ny zh cv kw co cr hr cs da dv nl dz en eo et ee fo fj fi fr ff gl ka de el gn gu ht ha he hz hi ho hu ia id ie ga ig ik io is it iu ja jv kl kn kr ks kk km ki rw ky kv kg ko ku kj la lb lg li ln lo lt lu lv gv mk mg ms ml mt mi mr mh mn na nv nb nd ne ng nn no ii nr oc oj cu om or os pa pi fa pl ps pt qu rm rn ro ru sa sc sd se sm sg sr gd sn si sk sl so st es su sw ss sv ta te tg th ti bo tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa cy wo fy xh yi yo za zu".split(" ");
-  var targets = ["_blank", "_self", "_top", "_parent"];
-  var charsets = ["ascii", "utf-8", "utf-16", "latin1", "latin1"];
-  var methods = ["get", "post", "put", "delete"];
-  var encs = ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"];
-  var media = ["all", "screen", "print", "embossed", "braille", "handheld", "print", "projection", "screen", "tty", "tv", "speech",
-               "3d-glasses", "resolution [>][<][=] [X]", "device-aspect-ratio: X/Y", "orientation:portrait",
-               "orientation:landscape", "device-height: [X]", "device-width: [X]"];
-  var s = { attrs: {} }; // Simple tag, reused for a whole lot of tags
-
-  var data = {
-    a: {
-      attrs: {
-        href: null, ping: null, type: null,
-        media: media,
-        target: targets,
-        hreflang: langs
-      }
-    },
-    abbr: s,
-    acronym: s,
-    address: s,
-    applet: s,
-    area: {
-      attrs: {
-        alt: null, coords: null, href: null, target: null, ping: null,
-        media: media, hreflang: langs, type: null,
-        shape: ["default", "rect", "circle", "poly"]
-      }
-    },
-    article: s,
-    aside: s,
-    audio: {
-      attrs: {
-        src: null, mediagroup: null,
-        crossorigin: ["anonymous", "use-credentials"],
-        preload: ["none", "metadata", "auto"],
-        autoplay: ["", "autoplay"],
-        loop: ["", "loop"],
-        controls: ["", "controls"]
-      }
-    },
-    b: s,
-    base: { attrs: { href: null, target: targets } },
-    basefont: s,
-    bdi: s,
-    bdo: s,
-    big: s,
-    blockquote: { attrs: { cite: null } },
-    body: s,
-    br: s,
-    button: {
-      attrs: {
-        form: null, formaction: null, name: null, value: null,
-        autofocus: ["", "autofocus"],
-        disabled: ["", "autofocus"],
-        formenctype: encs,
-        formmethod: methods,
-        formnovalidate: ["", "novalidate"],
-        formtarget: targets,
-        type: ["submit", "reset", "button"]
-      }
-    },
-    canvas: { attrs: { width: null, height: null } },
-    caption: s,
-    center: s,
-    cite: s,
-    code: s,
-    col: { attrs: { span: null } },
-    colgroup: { attrs: { span: null } },
-    command: {
-      attrs: {
-        type: ["command", "checkbox", "radio"],
-        label: null, icon: null, radiogroup: null, command: null, title: null,
-        disabled: ["", "disabled"],
-        checked: ["", "checked"]
-      }
-    },
-    data: { attrs: { value: null } },
-    datagrid: { attrs: { disabled: ["", "disabled"], multiple: ["", "multiple"] } },
-    datalist: { attrs: { data: null } },
-    dd: s,
-    del: { attrs: { cite: null, datetime: null } },
-    details: { attrs: { open: ["", "open"] } },
-    dfn: s,
-    dir: s,
-    div: s,
-    dl: s,
-    dt: s,
-    em: s,
-    embed: { attrs: { src: null, type: null, width: null, height: null } },
-    eventsource: { attrs: { src: null } },
-    fieldset: { attrs: { disabled: ["", "disabled"], form: null, name: null } },
-    figcaption: s,
-    figure: s,
-    font: s,
-    footer: s,
-    form: {
-      attrs: {
-        action: null, name: null,
-        "accept-charset": charsets,
-        autocomplete: ["on", "off"],
-        enctype: encs,
-        method: methods,
-        novalidate: ["", "novalidate"],
-        target: targets
-      }
-    },
-    frame: s,
-    frameset: s,
-    h1: s, h2: s, h3: s, h4: s, h5: s, h6: s,
-    head: {
-      attrs: {},
-      children: ["title", "base", "link", "style", "meta", "script", "noscript", "command"]
-    },
-    header: s,
-    hgroup: s,
-    hr: s,
-    html: {
-      attrs: { manifest: null },
-      children: ["head", "body"]
-    },
-    i: s,
-    iframe: {
-      attrs: {
-        src: null, srcdoc: null, name: null, width: null, height: null,
-        sandbox: ["allow-top-navigation", "allow-same-origin", "allow-forms", "allow-scripts"],
-        seamless: ["", "seamless"]
-      }
-    },
-    img: {
-      attrs: {
-        alt: null, src: null, ismap: null, usemap: null, width: null, height: null,
-        crossorigin: ["anonymous", "use-credentials"]
-      }
-    },
-    input: {
-      attrs: {
-        alt: null, dirname: null, form: null, formaction: null,
-        height: null, list: null, max: null, maxlength: null, min: null,
-        name: null, pattern: null, placeholder: null, size: null, src: null,
-        step: null, value: null, width: null,
-        accept: ["audio/*", "video/*", "image/*"],
-        autocomplete: ["on", "off"],
-        autofocus: ["", "autofocus"],
-        checked: ["", "checked"],
-        disabled: ["", "disabled"],
-        formenctype: encs,
-        formmethod: methods,
-        formnovalidate: ["", "novalidate"],
-        formtarget: targets,
-        multiple: ["", "multiple"],
-        readonly: ["", "readonly"],
-        required: ["", "required"],
-        type: ["hidden", "text", "search", "tel", "url", "email", "password", "datetime", "date", "month",
-               "week", "time", "datetime-local", "number", "range", "color", "checkbox", "radio",
-               "file", "submit", "image", "reset", "button"]
-      }
-    },
-    ins: { attrs: { cite: null, datetime: null } },
-    kbd: s,
-    keygen: {
-      attrs: {
-        challenge: null, form: null, name: null,
-        autofocus: ["", "autofocus"],
-        disabled: ["", "disabled"],
-        keytype: ["RSA"]
-      }
-    },
-    label: { attrs: { "for": null, form: null } },
-    legend: s,
-    li: { attrs: { value: null } },
-    link: {
-      attrs: {
-        href: null, type: null,
-        hreflang: langs,
-        media: media,
-        sizes: ["all", "16x16", "16x16 32x32", "16x16 32x32 64x64"]
-      }
-    },
-    map: { attrs: { name: null } },
-    mark: s,
-    menu: { attrs: { label: null, type: ["list", "context", "toolbar"] } },
-    meta: {
-      attrs: {
-        content: null,
-        charset: charsets,
-        name: ["viewport", "application-name", "author", "description", "generator", "keywords"],
-        "http-equiv": ["content-language", "content-type", "default-style", "refresh"]
-      }
-    },
-    meter: { attrs: { value: null, min: null, low: null, high: null, max: null, optimum: null } },
-    nav: s,
-    noframes: s,
-    noscript: s,
-    object: {
-      attrs: {
-        data: null, type: null, name: null, usemap: null, form: null, width: null, height: null,
-        typemustmatch: ["", "typemustmatch"]
-      }
-    },
-    ol: { attrs: { reversed: ["", "reversed"], start: null, type: ["1", "a", "A", "i", "I"] } },
-    optgroup: { attrs: { disabled: ["", "disabled"], label: null } },
-    option: { attrs: { disabled: ["", "disabled"], label: null, selected: ["", "selected"], value: null } },
-    output: { attrs: { "for": null, form: null, name: null } },
-    p: s,
-    param: { attrs: { name: null, value: null } },
-    pre: s,
-    progress: { attrs: { value: null, max: null } },
-    q: { attrs: { cite: null } },
-    rp: s,
-    rt: s,
-    ruby: s,
-    s: s,
-    samp: s,
-    script: {
-      attrs: {
-        type: ["text/javascript"],
-        src: null,
-        async: ["", "async"],
-        defer: ["", "defer"],
-        charset: charsets
-      }
-    },
-    section: s,
-    select: {
-      attrs: {
-        form: null, name: null, size: null,
-        autofocus: ["", "autofocus"],
-        disabled: ["", "disabled"],
-        multiple: ["", "multiple"]
-      }
-    },
-    small: s,
-    source: { attrs: { src: null, type: null, media: null } },
-    span: s,
-    strike: s,
-    strong: s,
-    style: {
-      attrs: {
-        type: ["text/css"],
-        media: media,
-        scoped: null
-      }
-    },
-    sub: s,
-    summary: s,
-    sup: s,
-    table: s,
-    tbody: s,
-    td: { attrs: { colspan: null, rowspan: null, headers: null } },
-    textarea: {
-      attrs: {
-        dirname: null, form: null, maxlength: null, name: null, placeholder: null,
-        rows: null, cols: null,
-        autofocus: ["", "autofocus"],
-        disabled: ["", "disabled"],
-        readonly: ["", "readonly"],
-        required: ["", "required"],
-        wrap: ["soft", "hard"]
-      }
-    },
-    tfoot: s,
-    th: { attrs: { colspan: null, rowspan: null, headers: null, scope: ["row", "col", "rowgroup", "colgroup"] } },
-    thead: s,
-    time: { attrs: { datetime: null } },
-    title: s,
-    tr: s,
-    track: {
-      attrs: {
-        src: null, label: null, "default": null,
-        kind: ["subtitles", "captions", "descriptions", "chapters", "metadata"],
-        srclang: langs
-      }
-    },
-    tt: s,
-    u: s,
-    ul: s,
-    "var": s,
-    video: {
-      attrs: {
-        src: null, poster: null, width: null, height: null,
-        crossorigin: ["anonymous", "use-credentials"],
-        preload: ["auto", "metadata", "none"],
-        autoplay: ["", "autoplay"],
-        mediagroup: ["movie"],
-        muted: ["", "muted"],
-        controls: ["", "controls"]
-      }
-    },
-    wbr: s
-  };
-
-  var globalAttrs = {
-    accesskey: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
-    "class": null,
-    contenteditable: ["true", "false"],
-    contextmenu: null,
-    dir: ["ltr", "rtl", "auto"],
-    draggable: ["true", "false", "auto"],
-    dropzone: ["copy", "move", "link", "string:", "file:"],
-    hidden: ["hidden"],
-    id: null,
-    inert: ["inert"],
-    itemid: null,
-    itemprop: null,
-    itemref: null,
-    itemscope: ["itemscope"],
-    itemtype: null,
-    lang: ["en", "es"],
-    spellcheck: ["true", "false"],
-    autocorrect: ["true", "false"],
-    autocapitalize: ["true", "false"],
-    style: null,
-    tabindex: ["1", "2", "3", "4", "5", "6", "7", "8", "9"],
-    title: null,
-    translate: ["yes", "no"],
-    onclick: null,
-    rel: ["stylesheet", "alternate", "author", "bookmark", "help", "license", "next", "nofollow", "noreferrer", "prefetch", "prev", "search", "tag"]
-  };
-  function populate(obj) {
-    for (var attr in globalAttrs) if (globalAttrs.hasOwnProperty(attr))
-      obj.attrs[attr] = globalAttrs[attr];
-  }
-
-  populate(s);
-  for (var tag in data) if (data.hasOwnProperty(tag) && data[tag] != s)
-    populate(data[tag]);
-
-  CodeMirror.htmlSchema = data;
-  function htmlHint(cm, options) {
-    var local = {schemaInfo: data};
-    if (options) for (var opt in options) local[opt] = options[opt];
-    return CodeMirror.hint.xml(cm, local);
-  }
-  CodeMirror.registerHelper("hint", "html", htmlHint);
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/hint/show-hint.css b/plugins/UiFileManager/media/codemirror/extension/hint/show-hint.css
deleted file mode 100644
index 5617ccca..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/hint/show-hint.css
+++ /dev/null
@@ -1,36 +0,0 @@
-.CodeMirror-hints {
-  position: absolute;
-  z-index: 10;
-  overflow: hidden;
-  list-style: none;
-
-  margin: 0;
-  padding: 2px;
-
-  -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
-  -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
-  box-shadow: 2px 3px 5px rgba(0,0,0,.2);
-  border-radius: 3px;
-  border: 1px solid silver;
-
-  background: white;
-  font-size: 90%;
-  font-family: monospace;
-
-  max-height: 20em;
-  overflow-y: auto;
-}
-
-.CodeMirror-hint {
-  margin: 0;
-  padding: 0 4px;
-  border-radius: 2px;
-  white-space: pre;
-  color: black;
-  cursor: pointer;
-}
-
-li.CodeMirror-hint-active {
-  background: #08f;
-  color: white;
-}
diff --git a/plugins/UiFileManager/media/codemirror/extension/hint/show-hint.js b/plugins/UiFileManager/media/codemirror/extension/hint/show-hint.js
deleted file mode 100644
index cd0d6a7b..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/hint/show-hint.js
+++ /dev/null
@@ -1,479 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  var HINT_ELEMENT_CLASS        = "CodeMirror-hint";
-  var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";
-
-  // This is the old interface, kept around for now to stay
-  // backwards-compatible.
-  CodeMirror.showHint = function(cm, getHints, options) {
-    if (!getHints) return cm.showHint(options);
-    if (options && options.async) getHints.async = true;
-    var newOpts = {hint: getHints};
-    if (options) for (var prop in options) newOpts[prop] = options[prop];
-    return cm.showHint(newOpts);
-  };
-
-  CodeMirror.defineExtension("showHint", function(options) {
-    options = parseOptions(this, this.getCursor("start"), options);
-    var selections = this.listSelections()
-    if (selections.length > 1) return;
-    // By default, don't allow completion when something is selected.
-    // A hint function can have a `supportsSelection` property to
-    // indicate that it can handle selections.
-    if (this.somethingSelected()) {
-      if (!options.hint.supportsSelection) return;
-      // Don't try with cross-line selections
-      for (var i = 0; i < selections.length; i++)
-        if (selections[i].head.line != selections[i].anchor.line) return;
-    }
-
-    if (this.state.completionActive) this.state.completionActive.close();
-    var completion = this.state.completionActive = new Completion(this, options);
-    if (!completion.options.hint) return;
-
-    CodeMirror.signal(this, "startCompletion", this);
-    completion.update(true);
-  });
-
-  CodeMirror.defineExtension("closeHint", function() {
-    if (this.state.completionActive) this.state.completionActive.close()
-  })
-
-  function Completion(cm, options) {
-    this.cm = cm;
-    this.options = options;
-    this.widget = null;
-    this.debounce = 0;
-    this.tick = 0;
-    this.startPos = this.cm.getCursor("start");
-    this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length;
-
-    var self = this;
-    cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); });
-  }
-
-  var requestAnimationFrame = window.requestAnimationFrame || function(fn) {
-    return setTimeout(fn, 1000/60);
-  };
-  var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;
-
-  Completion.prototype = {
-    close: function() {
-      if (!this.active()) return;
-      this.cm.state.completionActive = null;
-      this.tick = null;
-      this.cm.off("cursorActivity", this.activityFunc);
-
-      if (this.widget && this.data) CodeMirror.signal(this.data, "close");
-      if (this.widget) this.widget.close();
-      CodeMirror.signal(this.cm, "endCompletion", this.cm);
-    },
-
-    active: function() {
-      return this.cm.state.completionActive == this;
-    },
-
-    pick: function(data, i) {
-      var completion = data.list[i], self = this;
-      this.cm.operation(function() {
-        if (completion.hint)
-          completion.hint(self.cm, data, completion);
-        else
-          self.cm.replaceRange(getText(completion), completion.from || data.from,
-                               completion.to || data.to, "complete");
-        CodeMirror.signal(data, "pick", completion);
-        self.cm.scrollIntoView();
-      })
-      this.close();
-    },
-
-    cursorActivity: function() {
-      if (this.debounce) {
-        cancelAnimationFrame(this.debounce);
-        this.debounce = 0;
-      }
-
-      var identStart = this.startPos;
-      if(this.data) {
-        identStart = this.data.from;
-      }
-
-      var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line);
-      if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||
-          pos.ch < identStart.ch || this.cm.somethingSelected() ||
-          (!pos.ch || this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {
-        this.close();
-      } else {
-        var self = this;
-        this.debounce = requestAnimationFrame(function() {self.update();});
-        if (this.widget) this.widget.disable();
-      }
-    },
-
-    update: function(first) {
-      if (this.tick == null) return
-      var self = this, myTick = ++this.tick
-      fetchHints(this.options.hint, this.cm, this.options, function(data) {
-        if (self.tick == myTick) self.finishUpdate(data, first)
-      })
-    },
-
-    finishUpdate: function(data, first) {
-      if (this.data) CodeMirror.signal(this.data, "update");
-
-      var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);
-      if (this.widget) this.widget.close();
-
-      this.data = data;
-
-      if (data && data.list.length) {
-        if (picked && data.list.length == 1) {
-          this.pick(data, 0);
-        } else {
-          this.widget = new Widget(this, data);
-          CodeMirror.signal(data, "shown");
-        }
-      }
-    }
-  };
-
-  function parseOptions(cm, pos, options) {
-    var editor = cm.options.hintOptions;
-    var out = {};
-    for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
-    if (editor) for (var prop in editor)
-      if (editor[prop] !== undefined) out[prop] = editor[prop];
-    if (options) for (var prop in options)
-      if (options[prop] !== undefined) out[prop] = options[prop];
-    if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos)
-    return out;
-  }
-
-  function getText(completion) {
-    if (typeof completion == "string") return completion;
-    else return completion.text;
-  }
-
-  function buildKeyMap(completion, handle) {
-    var baseMap = {
-      Up: function() {handle.moveFocus(-1);},
-      Down: function() {handle.moveFocus(1);},
-      PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);},
-      PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);},
-      Home: function() {handle.setFocus(0);},
-      End: function() {handle.setFocus(handle.length - 1);},
-      Enter: handle.pick,
-      Tab: handle.pick,
-      Esc: handle.close
-    };
-
-    var mac = /Mac/.test(navigator.platform);
-
-    if (mac) {
-      baseMap["Ctrl-P"] = function() {handle.moveFocus(-1);};
-      baseMap["Ctrl-N"] = function() {handle.moveFocus(1);};
-    }
-
-    var custom = completion.options.customKeys;
-    var ourMap = custom ? {} : baseMap;
-    function addBinding(key, val) {
-      var bound;
-      if (typeof val != "string")
-        bound = function(cm) { return val(cm, handle); };
-      // This mechanism is deprecated
-      else if (baseMap.hasOwnProperty(val))
-        bound = baseMap[val];
-      else
-        bound = val;
-      ourMap[key] = bound;
-    }
-    if (custom)
-      for (var key in custom) if (custom.hasOwnProperty(key))
-        addBinding(key, custom[key]);
-    var extra = completion.options.extraKeys;
-    if (extra)
-      for (var key in extra) if (extra.hasOwnProperty(key))
-        addBinding(key, extra[key]);
-    return ourMap;
-  }
-
-  function getHintElement(hintsElement, el) {
-    while (el && el != hintsElement) {
-      if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el;
-      el = el.parentNode;
-    }
-  }
-
-  function Widget(completion, data) {
-    this.completion = completion;
-    this.data = data;
-    this.picked = false;
-    var widget = this, cm = completion.cm;
-    var ownerDocument = cm.getInputField().ownerDocument;
-    var parentWindow = ownerDocument.defaultView || ownerDocument.parentWindow;
-
-    var hints = this.hints = ownerDocument.createElement("ul");
-    var theme = completion.cm.options.theme;
-    hints.className = "CodeMirror-hints " + theme;
-    this.selectedHint = data.selectedHint || 0;
-
-    var completions = data.list;
-    for (var i = 0; i < completions.length; ++i) {
-      var elt = hints.appendChild(ownerDocument.createElement("li")), cur = completions[i];
-      var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS);
-      if (cur.className != null) className = cur.className + " " + className;
-      elt.className = className;
-      if (cur.render) cur.render(elt, data, cur);
-      else elt.appendChild(ownerDocument.createTextNode(cur.displayText || getText(cur)));
-      elt.hintId = i;
-    }
-
-    var container = completion.options.container || ownerDocument.body;
-    var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);
-    var left = pos.left, top = pos.bottom, below = true;
-    var offsetLeft = 0, offsetTop = 0;
-    if (container !== ownerDocument.body) {
-      // We offset the cursor position because left and top are relative to the offsetParent's top left corner.
-      var isContainerPositioned = ['absolute', 'relative', 'fixed'].indexOf(parentWindow.getComputedStyle(container).position) !== -1;
-      var offsetParent = isContainerPositioned ? container : container.offsetParent;
-      var offsetParentPosition = offsetParent.getBoundingClientRect();
-      var bodyPosition = ownerDocument.body.getBoundingClientRect();
-      offsetLeft = (offsetParentPosition.left - bodyPosition.left - offsetParent.scrollLeft);
-      offsetTop = (offsetParentPosition.top - bodyPosition.top - offsetParent.scrollTop);
-    }
-    hints.style.left = (left - offsetLeft) + "px";
-    hints.style.top = (top - offsetTop) + "px";
-
-    // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
-    var winW = parentWindow.innerWidth || Math.max(ownerDocument.body.offsetWidth, ownerDocument.documentElement.offsetWidth);
-    var winH = parentWindow.innerHeight || Math.max(ownerDocument.body.offsetHeight, ownerDocument.documentElement.offsetHeight);
-    container.appendChild(hints);
-    var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
-    var scrolls = hints.scrollHeight > hints.clientHeight + 1
-    var startScroll = cm.getScrollInfo();
-
-    if (overlapY > 0) {
-      var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
-      if (curTop - height > 0) { // Fits above cursor
-        hints.style.top = (top = pos.top - height - offsetTop) + "px";
-        below = false;
-      } else if (height > winH) {
-        hints.style.height = (winH - 5) + "px";
-        hints.style.top = (top = pos.bottom - box.top - offsetTop) + "px";
-        var cursor = cm.getCursor();
-        if (data.from.ch != cursor.ch) {
-          pos = cm.cursorCoords(cursor);
-          hints.style.left = (left = pos.left - offsetLeft) + "px";
-          box = hints.getBoundingClientRect();
-        }
-      }
-    }
-    var overlapX = box.right - winW;
-    if (overlapX > 0) {
-      if (box.right - box.left > winW) {
-        hints.style.width = (winW - 5) + "px";
-        overlapX -= (box.right - box.left) - winW;
-      }
-      hints.style.left = (left = pos.left - overlapX - offsetLeft) + "px";
-    }
-    if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling)
-      node.style.paddingRight = cm.display.nativeBarWidth + "px"
-
-    cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
-      moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
-      setFocus: function(n) { widget.changeActive(n); },
-      menuSize: function() { return widget.screenAmount(); },
-      length: completions.length,
-      close: function() { completion.close(); },
-      pick: function() { widget.pick(); },
-      data: data
-    }));
-
-    if (completion.options.closeOnUnfocus) {
-      var closingOnBlur;
-      cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
-      cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
-    }
-
-    cm.on("scroll", this.onScroll = function() {
-      var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
-      var newTop = top + startScroll.top - curScroll.top;
-      var point = newTop - (parentWindow.pageYOffset || (ownerDocument.documentElement || ownerDocument.body).scrollTop);
-      if (!below) point += hints.offsetHeight;
-      if (point <= editor.top || point >= editor.bottom) return completion.close();
-      hints.style.top = newTop + "px";
-      hints.style.left = (left + startScroll.left - curScroll.left) + "px";
-    });
-
-    CodeMirror.on(hints, "dblclick", function(e) {
-      var t = getHintElement(hints, e.target || e.srcElement);
-      if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
-    });
-
-    CodeMirror.on(hints, "click", function(e) {
-      var t = getHintElement(hints, e.target || e.srcElement);
-      if (t && t.hintId != null) {
-        widget.changeActive(t.hintId);
-        if (completion.options.completeOnSingleClick) widget.pick();
-      }
-    });
-
-    CodeMirror.on(hints, "mousedown", function() {
-      setTimeout(function(){cm.focus();}, 20);
-    });
-    this.scrollToActive()
-
-    CodeMirror.signal(data, "select", completions[this.selectedHint], hints.childNodes[this.selectedHint]);
-    return true;
-  }
-
-  Widget.prototype = {
-    close: function() {
-      if (this.completion.widget != this) return;
-      this.completion.widget = null;
-      this.hints.parentNode.removeChild(this.hints);
-      this.completion.cm.removeKeyMap(this.keyMap);
-
-      var cm = this.completion.cm;
-      if (this.completion.options.closeOnUnfocus) {
-        cm.off("blur", this.onBlur);
-        cm.off("focus", this.onFocus);
-      }
-      cm.off("scroll", this.onScroll);
-    },
-
-    disable: function() {
-      this.completion.cm.removeKeyMap(this.keyMap);
-      var widget = this;
-      this.keyMap = {Enter: function() { widget.picked = true; }};
-      this.completion.cm.addKeyMap(this.keyMap);
-    },
-
-    pick: function() {
-      this.completion.pick(this.data, this.selectedHint);
-    },
-
-    changeActive: function(i, avoidWrap) {
-      if (i >= this.data.list.length)
-        i = avoidWrap ? this.data.list.length - 1 : 0;
-      else if (i < 0)
-        i = avoidWrap ? 0  : this.data.list.length - 1;
-      if (this.selectedHint == i) return;
-      var node = this.hints.childNodes[this.selectedHint];
-      if (node) node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, "");
-      node = this.hints.childNodes[this.selectedHint = i];
-      node.className += " " + ACTIVE_HINT_ELEMENT_CLASS;
-      this.scrollToActive()
-      CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node);
-    },
-
-    scrollToActive: function() {
-      var margin = this.completion.options.scrollMargin || 0;
-      var node1 = this.hints.childNodes[Math.max(0, this.selectedHint - margin)];
-      var node2 = this.hints.childNodes[Math.min(this.data.list.length - 1, this.selectedHint + margin)];
-      var firstNode = this.hints.firstChild;
-      if (node1.offsetTop < this.hints.scrollTop)
-        this.hints.scrollTop = node1.offsetTop - firstNode.offsetTop;
-      else if (node2.offsetTop + node2.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
-        this.hints.scrollTop = node2.offsetTop + node2.offsetHeight - this.hints.clientHeight + firstNode.offsetTop;
-    },
-
-    screenAmount: function() {
-      return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1;
-    }
-  };
-
-  function applicableHelpers(cm, helpers) {
-    if (!cm.somethingSelected()) return helpers
-    var result = []
-    for (var i = 0; i < helpers.length; i++)
-      if (helpers[i].supportsSelection) result.push(helpers[i])
-    return result
-  }
-
-  function fetchHints(hint, cm, options, callback) {
-    if (hint.async) {
-      hint(cm, callback, options)
-    } else {
-      var result = hint(cm, options)
-      if (result && result.then) result.then(callback)
-      else callback(result)
-    }
-  }
-
-  function resolveAutoHints(cm, pos) {
-    var helpers = cm.getHelpers(pos, "hint"), words
-    if (helpers.length) {
-      var resolved = function(cm, callback, options) {
-        var app = applicableHelpers(cm, helpers);
-        function run(i) {
-          if (i == app.length) return callback(null)
-          fetchHints(app[i], cm, options, function(result) {
-            if (result && result.list.length > 0) callback(result)
-            else run(i + 1)
-          })
-        }
-        run(0)
-      }
-      resolved.async = true
-      resolved.supportsSelection = true
-      return resolved
-    } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) {
-      return function(cm) { return CodeMirror.hint.fromList(cm, {words: words}) }
-    } else if (CodeMirror.hint.anyword) {
-      return function(cm, options) { return CodeMirror.hint.anyword(cm, options) }
-    } else {
-      return function() {}
-    }
-  }
-
-  CodeMirror.registerHelper("hint", "auto", {
-    resolve: resolveAutoHints
-  });
-
-  CodeMirror.registerHelper("hint", "fromList", function(cm, options) {
-    var cur = cm.getCursor(), token = cm.getTokenAt(cur)
-    var term, from = CodeMirror.Pos(cur.line, token.start), to = cur
-    if (token.start < cur.ch && /\w/.test(token.string.charAt(cur.ch - token.start - 1))) {
-      term = token.string.substr(0, cur.ch - token.start)
-    } else {
-      term = ""
-      from = cur
-    }
-    var found = [];
-    for (var i = 0; i < options.words.length; i++) {
-      var word = options.words[i];
-      if (word.slice(0, term.length) == term)
-        found.push(word);
-    }
-
-    if (found.length) return {list: found, from: from, to: to};
-  });
-
-  CodeMirror.commands.autocomplete = CodeMirror.showHint;
-
-  var defaultOptions = {
-    hint: CodeMirror.hint.auto,
-    completeSingle: true,
-    alignWithWord: true,
-    closeCharacters: /[\s()\[\]{};:>,]/,
-    closeOnUnfocus: true,
-    completeOnSingleClick: true,
-    container: null,
-    customKeys: null,
-    extraKeys: null
-  };
-
-  CodeMirror.defineOption("hintOptions", null);
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/hint/sql-hint.js b/plugins/UiFileManager/media/codemirror/extension/hint/sql-hint.js
deleted file mode 100644
index de84707d..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/hint/sql-hint.js
+++ /dev/null
@@ -1,304 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("../../mode/sql/sql"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "../../mode/sql/sql"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  var tables;
-  var defaultTable;
-  var keywords;
-  var identifierQuote;
-  var CONS = {
-    QUERY_DIV: ";",
-    ALIAS_KEYWORD: "AS"
-  };
-  var Pos = CodeMirror.Pos, cmpPos = CodeMirror.cmpPos;
-
-  function isArray(val) { return Object.prototype.toString.call(val) == "[object Array]" }
-
-  function getKeywords(editor) {
-    var mode = editor.doc.modeOption;
-    if (mode === "sql") mode = "text/x-sql";
-    return CodeMirror.resolveMode(mode).keywords;
-  }
-
-  function getIdentifierQuote(editor) {
-    var mode = editor.doc.modeOption;
-    if (mode === "sql") mode = "text/x-sql";
-    return CodeMirror.resolveMode(mode).identifierQuote || "`";
-  }
-
-  function getText(item) {
-    return typeof item == "string" ? item : item.text;
-  }
-
-  function wrapTable(name, value) {
-    if (isArray(value)) value = {columns: value}
-    if (!value.text) value.text = name
-    return value
-  }
-
-  function parseTables(input) {
-    var result = {}
-    if (isArray(input)) {
-      for (var i = input.length - 1; i >= 0; i--) {
-        var item = input[i]
-        result[getText(item).toUpperCase()] = wrapTable(getText(item), item)
-      }
-    } else if (input) {
-      for (var name in input)
-        result[name.toUpperCase()] = wrapTable(name, input[name])
-    }
-    return result
-  }
-
-  function getTable(name) {
-    return tables[name.toUpperCase()]
-  }
-
-  function shallowClone(object) {
-    var result = {};
-    for (var key in object) if (object.hasOwnProperty(key))
-      result[key] = object[key];
-    return result;
-  }
-
-  function match(string, word) {
-    var len = string.length;
-    var sub = getText(word).substr(0, len);
-    return string.toUpperCase() === sub.toUpperCase();
-  }
-
-  function addMatches(result, search, wordlist, formatter) {
-    if (isArray(wordlist)) {
-      for (var i = 0; i < wordlist.length; i++)
-        if (match(search, wordlist[i])) result.push(formatter(wordlist[i]))
-    } else {
-      for (var word in wordlist) if (wordlist.hasOwnProperty(word)) {
-        var val = wordlist[word]
-        if (!val || val === true)
-          val = word
-        else
-          val = val.displayText ? {text: val.text, displayText: val.displayText} : val.text
-        if (match(search, val)) result.push(formatter(val))
-      }
-    }
-  }
-
-  function cleanName(name) {
-    // Get rid name from identifierQuote and preceding dot(.)
-    if (name.charAt(0) == ".") {
-      name = name.substr(1);
-    }
-    // replace doublicated identifierQuotes with single identifierQuotes
-    // and remove single identifierQuotes
-    var nameParts = name.split(identifierQuote+identifierQuote);
-    for (var i = 0; i < nameParts.length; i++)
-      nameParts[i] = nameParts[i].replace(new RegExp(identifierQuote,"g"), "");
-    return nameParts.join(identifierQuote);
-  }
-
-  function insertIdentifierQuotes(name) {
-    var nameParts = getText(name).split(".");
-    for (var i = 0; i < nameParts.length; i++)
-      nameParts[i] = identifierQuote +
-        // doublicate identifierQuotes
-        nameParts[i].replace(new RegExp(identifierQuote,"g"), identifierQuote+identifierQuote) +
-        identifierQuote;
-    var escaped = nameParts.join(".");
-    if (typeof name == "string") return escaped;
-    name = shallowClone(name);
-    name.text = escaped;
-    return name;
-  }
-
-  function nameCompletion(cur, token, result, editor) {
-    // Try to complete table, column names and return start position of completion
-    var useIdentifierQuotes = false;
-    var nameParts = [];
-    var start = token.start;
-    var cont = true;
-    while (cont) {
-      cont = (token.string.charAt(0) == ".");
-      useIdentifierQuotes = useIdentifierQuotes || (token.string.charAt(0) == identifierQuote);
-
-      start = token.start;
-      nameParts.unshift(cleanName(token.string));
-
-      token = editor.getTokenAt(Pos(cur.line, token.start));
-      if (token.string == ".") {
-        cont = true;
-        token = editor.getTokenAt(Pos(cur.line, token.start));
-      }
-    }
-
-    // Try to complete table names
-    var string = nameParts.join(".");
-    addMatches(result, string, tables, function(w) {
-      return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
-    });
-
-    // Try to complete columns from defaultTable
-    addMatches(result, string, defaultTable, function(w) {
-      return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
-    });
-
-    // Try to complete columns
-    string = nameParts.pop();
-    var table = nameParts.join(".");
-
-    var alias = false;
-    var aliasTable = table;
-    // Check if table is available. If not, find table by Alias
-    if (!getTable(table)) {
-      var oldTable = table;
-      table = findTableByAlias(table, editor);
-      if (table !== oldTable) alias = true;
-    }
-
-    var columns = getTable(table);
-    if (columns && columns.columns)
-      columns = columns.columns;
-
-    if (columns) {
-      addMatches(result, string, columns, function(w) {
-        var tableInsert = table;
-        if (alias == true) tableInsert = aliasTable;
-        if (typeof w == "string") {
-          w = tableInsert + "." + w;
-        } else {
-          w = shallowClone(w);
-          w.text = tableInsert + "." + w.text;
-        }
-        return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
-      });
-    }
-
-    return start;
-  }
-
-  function eachWord(lineText, f) {
-    var words = lineText.split(/\s+/)
-    for (var i = 0; i < words.length; i++)
-      if (words[i]) f(words[i].replace(/[,;]/g, ''))
-  }
-
-  function findTableByAlias(alias, editor) {
-    var doc = editor.doc;
-    var fullQuery = doc.getValue();
-    var aliasUpperCase = alias.toUpperCase();
-    var previousWord = "";
-    var table = "";
-    var separator = [];
-    var validRange = {
-      start: Pos(0, 0),
-      end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length)
-    };
-
-    //add separator
-    var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV);
-    while(indexOfSeparator != -1) {
-      separator.push(doc.posFromIndex(indexOfSeparator));
-      indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1);
-    }
-    separator.unshift(Pos(0, 0));
-    separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length));
-
-    //find valid range
-    var prevItem = null;
-    var current = editor.getCursor()
-    for (var i = 0; i < separator.length; i++) {
-      if ((prevItem == null || cmpPos(current, prevItem) > 0) && cmpPos(current, separator[i]) <= 0) {
-        validRange = {start: prevItem, end: separator[i]};
-        break;
-      }
-      prevItem = separator[i];
-    }
-
-    if (validRange.start) {
-      var query = doc.getRange(validRange.start, validRange.end, false);
-
-      for (var i = 0; i < query.length; i++) {
-        var lineText = query[i];
-        eachWord(lineText, function(word) {
-          var wordUpperCase = word.toUpperCase();
-          if (wordUpperCase === aliasUpperCase && getTable(previousWord))
-            table = previousWord;
-          if (wordUpperCase !== CONS.ALIAS_KEYWORD)
-            previousWord = word;
-        });
-        if (table) break;
-      }
-    }
-    return table;
-  }
-
-  CodeMirror.registerHelper("hint", "sql", function(editor, options) {
-    tables = parseTables(options && options.tables)
-    var defaultTableName = options && options.defaultTable;
-    var disableKeywords = options && options.disableKeywords;
-    defaultTable = defaultTableName && getTable(defaultTableName);
-    keywords = getKeywords(editor);
-    identifierQuote = getIdentifierQuote(editor);
-
-    if (defaultTableName && !defaultTable)
-      defaultTable = findTableByAlias(defaultTableName, editor);
-
-    defaultTable = defaultTable || [];
-
-    if (defaultTable.columns)
-      defaultTable = defaultTable.columns;
-
-    var cur = editor.getCursor();
-    var result = [];
-    var token = editor.getTokenAt(cur), start, end, search;
-    if (token.end > cur.ch) {
-      token.end = cur.ch;
-      token.string = token.string.slice(0, cur.ch - token.start);
-    }
-
-    if (token.string.match(/^[.`"'\w@][\w$#]*$/g)) {
-      search = token.string;
-      start = token.start;
-      end = token.end;
-    } else {
-      start = end = cur.ch;
-      search = "";
-    }
-    if (search.charAt(0) == "." || search.charAt(0) == identifierQuote) {
-      start = nameCompletion(cur, token, result, editor);
-    } else {
-      var objectOrClass = function(w, className) {
-        if (typeof w === "object") {
-          w.className = className;
-        } else {
-          w = { text: w, className: className };
-        }
-        return w;
-      };
-    addMatches(result, search, defaultTable, function(w) {
-        return objectOrClass(w, "CodeMirror-hint-table CodeMirror-hint-default-table");
-    });
-    addMatches(
-        result,
-        search,
-        tables, function(w) {
-          return objectOrClass(w, "CodeMirror-hint-table");
-        }
-    );
-    if (!disableKeywords)
-      addMatches(result, search, keywords, function(w) {
-          return objectOrClass(w.toUpperCase(), "CodeMirror-hint-keyword");
-      });
-  }
-
-    return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)};
-  });
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/hint/xml-hint.js b/plugins/UiFileManager/media/codemirror/extension/hint/xml-hint.js
deleted file mode 100644
index 7575b370..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/hint/xml-hint.js
+++ /dev/null
@@ -1,123 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  var Pos = CodeMirror.Pos;
-
-  function matches(hint, typed, matchInMiddle) {
-    if (matchInMiddle) return hint.indexOf(typed) >= 0;
-    else return hint.lastIndexOf(typed, 0) == 0;
-  }
-
-  function getHints(cm, options) {
-    var tags = options && options.schemaInfo;
-    var quote = (options && options.quoteChar) || '"';
-    var matchInMiddle = options && options.matchInMiddle;
-    if (!tags) return;
-    var cur = cm.getCursor(), token = cm.getTokenAt(cur);
-    if (token.end > cur.ch) {
-      token.end = cur.ch;
-      token.string = token.string.slice(0, cur.ch - token.start);
-    }
-    var inner = CodeMirror.innerMode(cm.getMode(), token.state);
-    if (!inner.mode.xmlCurrentTag) return
-    var result = [], replaceToken = false, prefix;
-    var tag = /\btag\b/.test(token.type) && !/>$/.test(token.string);
-    var tagName = tag && /^\w/.test(token.string), tagStart;
-
-    if (tagName) {
-      var before = cm.getLine(cur.line).slice(Math.max(0, token.start - 2), token.start);
-      var tagType = /<\/$/.test(before) ? "close" : /<$/.test(before) ? "open" : null;
-      if (tagType) tagStart = token.start - (tagType == "close" ? 2 : 1);
-    } else if (tag && token.string == "<") {
-      tagType = "open";
-    } else if (tag && token.string == "</") {
-      tagType = "close";
-    }
-
-    var tagInfo = inner.mode.xmlCurrentTag(inner.state)
-    if (!tag && !tagInfo || tagType) {
-      if (tagName)
-        prefix = token.string;
-      replaceToken = tagType;
-      var context = inner.mode.xmlCurrentContext ? inner.mode.xmlCurrentContext(inner.state) : []
-      var inner = context.length && context[context.length - 1]
-      var curTag = inner && tags[inner]
-      var childList = inner ? curTag && curTag.children : tags["!top"];
-      if (childList && tagType != "close") {
-        for (var i = 0; i < childList.length; ++i) if (!prefix || matches(childList[i], prefix, matchInMiddle))
-          result.push("<" + childList[i]);
-      } else if (tagType != "close") {
-        for (var name in tags)
-          if (tags.hasOwnProperty(name) && name != "!top" && name != "!attrs" && (!prefix || matches(name, prefix, matchInMiddle)))
-            result.push("<" + name);
-      }
-      if (inner && (!prefix || tagType == "close" && matches(inner, prefix, matchInMiddle)))
-        result.push("</" + inner + ">");
-    } else {
-      // Attribute completion
-      var curTag = tagInfo && tags[tagInfo.name], attrs = curTag && curTag.attrs;
-      var globalAttrs = tags["!attrs"];
-      if (!attrs && !globalAttrs) return;
-      if (!attrs) {
-        attrs = globalAttrs;
-      } else if (globalAttrs) { // Combine tag-local and global attributes
-        var set = {};
-        for (var nm in globalAttrs) if (globalAttrs.hasOwnProperty(nm)) set[nm] = globalAttrs[nm];
-        for (var nm in attrs) if (attrs.hasOwnProperty(nm)) set[nm] = attrs[nm];
-        attrs = set;
-      }
-      if (token.type == "string" || token.string == "=") { // A value
-        var before = cm.getRange(Pos(cur.line, Math.max(0, cur.ch - 60)),
-                                 Pos(cur.line, token.type == "string" ? token.start : token.end));
-        var atName = before.match(/([^\s\u00a0=<>\"\']+)=$/), atValues;
-        if (!atName || !attrs.hasOwnProperty(atName[1]) || !(atValues = attrs[atName[1]])) return;
-        if (typeof atValues == 'function') atValues = atValues.call(this, cm); // Functions can be used to supply values for autocomplete widget
-        if (token.type == "string") {
-          prefix = token.string;
-          var n = 0;
-          if (/['"]/.test(token.string.charAt(0))) {
-            quote = token.string.charAt(0);
-            prefix = token.string.slice(1);
-            n++;
-          }
-          var len = token.string.length;
-          if (/['"]/.test(token.string.charAt(len - 1))) {
-            quote = token.string.charAt(len - 1);
-            prefix = token.string.substr(n, len - 2);
-          }
-          if (n) { // an opening quote
-            var line = cm.getLine(cur.line);
-            if (line.length > token.end && line.charAt(token.end) == quote) token.end++; // include a closing quote
-          }
-          replaceToken = true;
-        }
-        for (var i = 0; i < atValues.length; ++i) if (!prefix || matches(atValues[i], prefix, matchInMiddle))
-          result.push(quote + atValues[i] + quote);
-      } else { // An attribute name
-        if (token.type == "attribute") {
-          prefix = token.string;
-          replaceToken = true;
-        }
-        for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || matches(attr, prefix, matchInMiddle)))
-          result.push(attr);
-      }
-    }
-    return {
-      list: result,
-      from: replaceToken ? Pos(cur.line, tagStart == null ? token.start : tagStart) : cur,
-      to: replaceToken ? Pos(cur.line, token.end) : cur
-    };
-  }
-
-  CodeMirror.registerHelper("hint", "xml", getHints);
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/lint/json-lint.js b/plugins/UiFileManager/media/codemirror/extension/lint/json-lint.js
deleted file mode 100644
index ac1d6ec2..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/lint/json-lint.js
+++ /dev/null
@@ -1,40 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-// Depends on jsonlint.js from https://github.com/zaach/jsonlint
-
-// declare global: jsonlint
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-CodeMirror.registerHelper("lint", "json", function(text) {
-  var found = [];
-  if (!window.jsonlint) {
-    if (window.console) {
-      window.console.error("Error: window.jsonlint not defined, CodeMirror JSON linting cannot run.");
-    }
-    return found;
-  }
-  // for jsonlint's web dist jsonlint is exported as an object with a single property parser, of which parseError
-  // is a subproperty
-  var jsonlint = window.jsonlint.parser || window.jsonlint
-  jsonlint.parseError = function(str, hash) {
-    var loc = hash.loc;
-    found.push({from: CodeMirror.Pos(loc.first_line - 1, loc.first_column),
-                to: CodeMirror.Pos(loc.last_line - 1, loc.last_column),
-                message: str});
-  };
-  try { jsonlint.parse(text); }
-  catch(e) {}
-  return found;
-});
-
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/lint/jsonlint.js b/plugins/UiFileManager/media/codemirror/extension/lint/jsonlint.js
deleted file mode 100644
index f34759e8..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/lint/jsonlint.js
+++ /dev/null
@@ -1 +0,0 @@
-var jsonlint=function(){var a=!0,b=!1,c={},d=function(){var a={trace:function(){},yy:{},symbols_:{error:2,JSONString:3,STRING:4,JSONNumber:5,NUMBER:6,JSONNullLiteral:7,NULL:8,JSONBooleanLiteral:9,TRUE:10,FALSE:11,JSONText:12,JSONValue:13,EOF:14,JSONObject:15,JSONArray:16,"{":17,"}":18,JSONMemberList:19,JSONMember:20,":":21,",":22,"[":23,"]":24,JSONElementList:25,$accept:0,$end:1},terminals_:{2:"error",4:"STRING",6:"NUMBER",8:"NULL",10:"TRUE",11:"FALSE",14:"EOF",17:"{",18:"}",21:":",22:",",23:"[",24:"]"},productions_:[0,[3,1],[5,1],[7,1],[9,1],[9,1],[12,2],[13,1],[13,1],[13,1],[13,1],[13,1],[13,1],[15,2],[15,3],[20,3],[19,1],[19,3],[16,2],[16,3],[25,1],[25,3]],performAction:function(b,c,d,e,f,g,h){var i=g.length-1;switch(f){case 1:this.$=b.replace(/\\(\\|")/g,"$1").replace(/\\n/g,"\n").replace(/\\r/g,"\r").replace(/\\t/g,"	").replace(/\\v/g,"").replace(/\\f/g,"\f").replace(/\\b/g,"\b");break;case 2:this.$=Number(b);break;case 3:this.$=null;break;case 4:this.$=!0;break;case 5:this.$=!1;break;case 6:return this.$=g[i-1];case 13:this.$={};break;case 14:this.$=g[i-1];break;case 15:this.$=[g[i-2],g[i]];break;case 16:this.$={},this.$[g[i][0]]=g[i][1];break;case 17:this.$=g[i-2],g[i-2][g[i][0]]=g[i][1];break;case 18:this.$=[];break;case 19:this.$=g[i-1];break;case 20:this.$=[g[i]];break;case 21:this.$=g[i-2],g[i-2].push(g[i])}},table:[{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],12:1,13:2,15:7,16:8,17:[1,14],23:[1,15]},{1:[3]},{14:[1,16]},{14:[2,7],18:[2,7],22:[2,7],24:[2,7]},{14:[2,8],18:[2,8],22:[2,8],24:[2,8]},{14:[2,9],18:[2,9],22:[2,9],24:[2,9]},{14:[2,10],18:[2,10],22:[2,10],24:[2,10]},{14:[2,11],18:[2,11],22:[2,11],24:[2,11]},{14:[2,12],18:[2,12],22:[2,12],24:[2,12]},{14:[2,3],18:[2,3],22:[2,3],24:[2,3]},{14:[2,4],18:[2,4],22:[2,4],24:[2,4]},{14:[2,5],18:[2,5],22:[2,5],24:[2,5]},{14:[2,1],18:[2,1],21:[2,1],22:[2,1],24:[2,1]},{14:[2,2],18:[2,2],22:[2,2],24:[2,2]},{3:20,4:[1,12],18:[1,17],19:18,20:19},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:23,15:7,16:8,17:[1,14],23:[1,15],24:[1,21],25:22},{1:[2,6]},{14:[2,13],18:[2,13],22:[2,13],24:[2,13]},{18:[1,24],22:[1,25]},{18:[2,16],22:[2,16]},{21:[1,26]},{14:[2,18],18:[2,18],22:[2,18],24:[2,18]},{22:[1,28],24:[1,27]},{22:[2,20],24:[2,20]},{14:[2,14],18:[2,14],22:[2,14],24:[2,14]},{3:20,4:[1,12],20:29},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:30,15:7,16:8,17:[1,14],23:[1,15]},{14:[2,19],18:[2,19],22:[2,19],24:[2,19]},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:31,15:7,16:8,17:[1,14],23:[1,15]},{18:[2,17],22:[2,17]},{18:[2,15],22:[2,15]},{22:[2,21],24:[2,21]}],defaultActions:{16:[2,6]},parseError:function(b,c){throw new Error(b)},parse:function(b){function o(a){d.length=d.length-2*a,e.length=e.length-a,f.length=f.length-a}function p(){var a;return a=c.lexer.lex()||1,typeof a!="number"&&(a=c.symbols_[a]||a),a}var c=this,d=[0],e=[null],f=[],g=this.table,h="",i=0,j=0,k=0,l=2,m=1;this.lexer.setInput(b),this.lexer.yy=this.yy,this.yy.lexer=this.lexer,typeof this.lexer.yylloc=="undefined"&&(this.lexer.yylloc={});var n=this.lexer.yylloc;f.push(n),typeof this.yy.parseError=="function"&&(this.parseError=this.yy.parseError);var q,r,s,t,u,v,w={},x,y,z,A;for(;;){s=d[d.length-1],this.defaultActions[s]?t=this.defaultActions[s]:(q==null&&(q=p()),t=g[s]&&g[s][q]);if(typeof t=="undefined"||!t.length||!t[0]){if(!k){A=[];for(x in g[s])this.terminals_[x]&&x>2&&A.push("'"+this.terminals_[x]+"'");var B="";this.lexer.showPosition?B="Parse error on line "+(i+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+A.join(", ")+", got '"+this.terminals_[q]+"'":B="Parse error on line "+(i+1)+": Unexpected "+(q==1?"end of input":"'"+(this.terminals_[q]||q)+"'"),this.parseError(B,{text:this.lexer.match,token:this.terminals_[q]||q,line:this.lexer.yylineno,loc:n,expected:A})}if(k==3){if(q==m)throw new Error(B||"Parsing halted.");j=this.lexer.yyleng,h=this.lexer.yytext,i=this.lexer.yylineno,n=this.lexer.yylloc,q=p()}for(;;){if(l.toString()in g[s])break;if(s==0)throw new Error(B||"Parsing halted.");o(1),s=d[d.length-1]}r=q,q=l,s=d[d.length-1],t=g[s]&&g[s][l],k=3}if(t[0]instanceof Array&&t.length>1)throw new Error("Parse Error: multiple actions possible at state: "+s+", token: "+q);switch(t[0]){case 1:d.push(q),e.push(this.lexer.yytext),f.push(this.lexer.yylloc),d.push(t[1]),q=null,r?(q=r,r=null):(j=this.lexer.yyleng,h=this.lexer.yytext,i=this.lexer.yylineno,n=this.lexer.yylloc,k>0&&k--);break;case 2:y=this.productions_[t[1]][1],w.$=e[e.length-y],w._$={first_line:f[f.length-(y||1)].first_line,last_line:f[f.length-1].last_line,first_column:f[f.length-(y||1)].first_column,last_column:f[f.length-1].last_column},v=this.performAction.call(w,h,j,i,this.yy,t[1],e,f);if(typeof v!="undefined")return v;y&&(d=d.slice(0,-1*y*2),e=e.slice(0,-1*y),f=f.slice(0,-1*y)),d.push(this.productions_[t[1]][0]),e.push(w.$),f.push(w._$),z=g[d[d.length-2]][d[d.length-1]],d.push(z);break;case 3:return!0}}return!0}},b=function(){var a={EOF:1,parseError:function(b,c){if(!this.yy.parseError)throw new Error(b);this.yy.parseError(b,c)},setInput:function(a){return this._input=a,this._more=this._less=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this},input:function(){var a=this._input[0];this.yytext+=a,this.yyleng++,this.match+=a,this.matched+=a;var b=a.match(/\n/);return b&&this.yylineno++,this._input=this._input.slice(1),a},unput:function(a){return this._input=a+this._input,this},more:function(){return this._more=!0,this},less:function(a){this._input=this.match.slice(a)+this._input},pastInput:function(){var a=this.matched.substr(0,this.matched.length-this.match.length);return(a.length>20?"...":"")+a.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var a=this.match;return a.length<20&&(a+=this._input.substr(0,20-a.length)),(a.substr(0,20)+(a.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var a=this.pastInput(),b=(new Array(a.length+1)).join("-");return a+this.upcomingInput()+"\n"+b+"^"},next:function(){if(this.done)return this.EOF;this._input||(this.done=!0);var a,b,c,d,e,f;this._more||(this.yytext="",this.match="");var g=this._currentRules();for(var h=0;h<g.length;h++){c=this._input.match(this.rules[g[h]]);if(c&&(!b||c[0].length>b[0].length)){b=c,d=h;if(!this.options.flex)break}}if(b){f=b[0].match(/\n.*/g),f&&(this.yylineno+=f.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:f?f[f.length-1].length-1:this.yylloc.last_column+b[0].length},this.yytext+=b[0],this.match+=b[0],this.yyleng=this.yytext.length,this._more=!1,this._input=this._input.slice(b[0].length),this.matched+=b[0],a=this.performAction.call(this,this.yy,this,g[d],this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1);if(a)return a;return}if(this._input==="")return this.EOF;this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var b=this.next();return typeof b!="undefined"?b:this.lex()},begin:function(b){this.conditionStack.push(b)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules},topState:function(){return this.conditionStack[this.conditionStack.length-2]},pushState:function(b){this.begin(b)}};return a.options={},a.performAction=function(b,c,d,e){var f=e;switch(d){case 0:break;case 1:return 6;case 2:return c.yytext=c.yytext.substr(1,c.yyleng-2),4;case 3:return 17;case 4:return 18;case 5:return 23;case 6:return 24;case 7:return 22;case 8:return 21;case 9:return 10;case 10:return 11;case 11:return 8;case 12:return 14;case 13:return"INVALID"}},a.rules=[/^(?:\s+)/,/^(?:(-?([0-9]|[1-9][0-9]+))(\.[0-9]+)?([eE][-+]?[0-9]+)?\b)/,/^(?:"(?:\\[\\"bfnrt/]|\\u[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*")/,/^(?:\{)/,/^(?:\})/,/^(?:\[)/,/^(?:\])/,/^(?:,)/,/^(?::)/,/^(?:true\b)/,/^(?:false\b)/,/^(?:null\b)/,/^(?:$)/,/^(?:.)/],a.conditions={INITIAL:{rules:[0,1,2,3,4,5,6,7,8,9,10,11,12,13],inclusive:!0}},a}();return a.lexer=b,a}();return typeof a!="undefined"&&typeof c!="undefined"&&(c.parser=d,c.parse=function(){return d.parse.apply(d,arguments)},c.main=function(d){if(!d[1])throw new Error("Usage: "+d[0]+" FILE");if(typeof process!="undefined")var e=a("fs").readFileSync(a("path").join(process.cwd(),d[1]),"utf8");else var f=a("file").path(a("file").cwd()),e=f.join(d[1]).read({charset:"utf-8"});return c.parser.parse(e)},typeof b!="undefined"&&a.main===b&&c.main(typeof process!="undefined"?process.argv.slice(1):a("system").args)),c}();
\ No newline at end of file
diff --git a/plugins/UiFileManager/media/codemirror/extension/lint/lint.css b/plugins/UiFileManager/media/codemirror/extension/lint/lint.css
deleted file mode 100644
index f097cfe3..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/lint/lint.css
+++ /dev/null
@@ -1,73 +0,0 @@
-/* The lint marker gutter */
-.CodeMirror-lint-markers {
-  width: 16px;
-}
-
-.CodeMirror-lint-tooltip {
-  background-color: #ffd;
-  border: 1px solid black;
-  border-radius: 4px 4px 4px 4px;
-  color: black;
-  font-family: monospace;
-  font-size: 10pt;
-  overflow: hidden;
-  padding: 2px 5px;
-  position: fixed;
-  white-space: pre;
-  white-space: pre-wrap;
-  z-index: 100;
-  max-width: 600px;
-  opacity: 0;
-  transition: opacity .4s;
-  -moz-transition: opacity .4s;
-  -webkit-transition: opacity .4s;
-  -o-transition: opacity .4s;
-  -ms-transition: opacity .4s;
-}
-
-.CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning {
-  background-position: left bottom;
-  background-repeat: repeat-x;
-}
-
-.CodeMirror-lint-mark-error {
-  background-image:
-  url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==")
-  ;
-}
-
-.CodeMirror-lint-mark-warning {
-  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
-}
-
-.CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning {
-  background-position: center center;
-  background-repeat: no-repeat;
-  cursor: pointer;
-  display: inline-block;
-  height: 16px;
-  width: 16px;
-  vertical-align: middle;
-  position: relative;
-}
-
-.CodeMirror-lint-message-error, .CodeMirror-lint-message-warning {
-  padding-left: 18px;
-  background-position: top left;
-  background-repeat: no-repeat;
-}
-
-.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error {
-  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=");
-}
-
-.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning {
-  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=");
-}
-
-.CodeMirror-lint-marker-multiple {
-  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC");
-  background-repeat: no-repeat;
-  background-position: right bottom;
-  width: 100%; height: 100%;
-}
diff --git a/plugins/UiFileManager/media/codemirror/extension/lint/lint.js b/plugins/UiFileManager/media/codemirror/extension/lint/lint.js
deleted file mode 100644
index 5bc1af18..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/lint/lint.js
+++ /dev/null
@@ -1,255 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-  var GUTTER_ID = "CodeMirror-lint-markers";
-
-  function showTooltip(cm, e, content) {
-    var tt = document.createElement("div");
-    tt.className = "CodeMirror-lint-tooltip cm-s-" + cm.options.theme;
-    tt.appendChild(content.cloneNode(true));
-    if (cm.state.lint.options.selfContain)
-      cm.getWrapperElement().appendChild(tt);
-    else
-      document.body.appendChild(tt);
-
-    function position(e) {
-      if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position);
-      tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px";
-      tt.style.left = (e.clientX + 5) + "px";
-    }
-    CodeMirror.on(document, "mousemove", position);
-    position(e);
-    if (tt.style.opacity != null) tt.style.opacity = 1;
-    return tt;
-  }
-  function rm(elt) {
-    if (elt.parentNode) elt.parentNode.removeChild(elt);
-  }
-  function hideTooltip(tt) {
-    if (!tt.parentNode) return;
-    if (tt.style.opacity == null) rm(tt);
-    tt.style.opacity = 0;
-    setTimeout(function() { rm(tt); }, 600);
-  }
-
-  function showTooltipFor(cm, e, content, node) {
-    var tooltip = showTooltip(cm, e, content);
-    function hide() {
-      CodeMirror.off(node, "mouseout", hide);
-      if (tooltip) { hideTooltip(tooltip); tooltip = null; }
-    }
-    var poll = setInterval(function() {
-      if (tooltip) for (var n = node;; n = n.parentNode) {
-        if (n && n.nodeType == 11) n = n.host;
-        if (n == document.body) return;
-        if (!n) { hide(); break; }
-      }
-      if (!tooltip) return clearInterval(poll);
-    }, 400);
-    CodeMirror.on(node, "mouseout", hide);
-  }
-
-  function LintState(cm, options, hasGutter) {
-    this.marked = [];
-    this.options = options;
-    this.timeout = null;
-    this.hasGutter = hasGutter;
-    this.onMouseOver = function(e) { onMouseOver(cm, e); };
-    this.waitingFor = 0
-  }
-
-  function parseOptions(_cm, options) {
-    if (options instanceof Function) return {getAnnotations: options};
-    if (!options || options === true) options = {};
-    return options;
-  }
-
-  function clearMarks(cm) {
-    var state = cm.state.lint;
-    if (state.hasGutter) cm.clearGutter(GUTTER_ID);
-    for (var i = 0; i < state.marked.length; ++i)
-      state.marked[i].clear();
-    state.marked.length = 0;
-  }
-
-  function makeMarker(cm, labels, severity, multiple, tooltips) {
-    var marker = document.createElement("div"), inner = marker;
-    marker.className = "CodeMirror-lint-marker-" + severity;
-    if (multiple) {
-      inner = marker.appendChild(document.createElement("div"));
-      inner.className = "CodeMirror-lint-marker-multiple";
-    }
-
-    if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) {
-      showTooltipFor(cm, e, labels, inner);
-    });
-
-    return marker;
-  }
-
-  function getMaxSeverity(a, b) {
-    if (a == "error") return a;
-    else return b;
-  }
-
-  function groupByLine(annotations) {
-    var lines = [];
-    for (var i = 0; i < annotations.length; ++i) {
-      var ann = annotations[i], line = ann.from.line;
-      (lines[line] || (lines[line] = [])).push(ann);
-    }
-    return lines;
-  }
-
-  function annotationTooltip(ann) {
-    var severity = ann.severity;
-    if (!severity) severity = "error";
-    var tip = document.createElement("div");
-    tip.className = "CodeMirror-lint-message-" + severity;
-    if (typeof ann.messageHTML != 'undefined') {
-      tip.innerHTML = ann.messageHTML;
-    } else {
-      tip.appendChild(document.createTextNode(ann.message));
-    }
-    return tip;
-  }
-
-  function lintAsync(cm, getAnnotations, passOptions) {
-    var state = cm.state.lint
-    var id = ++state.waitingFor
-    function abort() {
-      id = -1
-      cm.off("change", abort)
-    }
-    cm.on("change", abort)
-    getAnnotations(cm.getValue(), function(annotations, arg2) {
-      cm.off("change", abort)
-      if (state.waitingFor != id) return
-      if (arg2 && annotations instanceof CodeMirror) annotations = arg2
-      cm.operation(function() {updateLinting(cm, annotations)})
-    }, passOptions, cm);
-  }
-
-  function startLinting(cm) {
-    var state = cm.state.lint, options = state.options;
-    /*
-     * Passing rules in `options` property prevents JSHint (and other linters) from complaining
-     * about unrecognized rules like `onUpdateLinting`, `delay`, `lintOnChange`, etc.
-     */
-    var passOptions = options.options || options;
-    var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), "lint");
-    if (!getAnnotations) return;
-    if (options.async || getAnnotations.async) {
-      lintAsync(cm, getAnnotations, passOptions)
-    } else {
-      var annotations = getAnnotations(cm.getValue(), passOptions, cm);
-      if (!annotations) return;
-      if (annotations.then) annotations.then(function(issues) {
-        cm.operation(function() {updateLinting(cm, issues)})
-      });
-      else cm.operation(function() {updateLinting(cm, annotations)})
-    }
-  }
-
-  function updateLinting(cm, annotationsNotSorted) {
-    clearMarks(cm);
-    var state = cm.state.lint, options = state.options;
-
-    var annotations = groupByLine(annotationsNotSorted);
-
-    for (var line = 0; line < annotations.length; ++line) {
-      var anns = annotations[line];
-      if (!anns) continue;
-
-      var maxSeverity = null;
-      var tipLabel = state.hasGutter && document.createDocumentFragment();
-
-      for (var i = 0; i < anns.length; ++i) {
-        var ann = anns[i];
-        var severity = ann.severity;
-        if (!severity) severity = "error";
-        maxSeverity = getMaxSeverity(maxSeverity, severity);
-
-        if (options.formatAnnotation) ann = options.formatAnnotation(ann);
-        if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann));
-
-        if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, {
-          className: "CodeMirror-lint-mark-" + severity,
-          __annotation: ann
-        }));
-      }
-
-      if (state.hasGutter)
-        cm.setGutterMarker(line, GUTTER_ID, makeMarker(cm, tipLabel, maxSeverity, anns.length > 1,
-                                                       state.options.tooltips));
-    }
-    if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm);
-  }
-
-  function onChange(cm) {
-    var state = cm.state.lint;
-    if (!state) return;
-    clearTimeout(state.timeout);
-    state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500);
-  }
-
-  function popupTooltips(cm, annotations, e) {
-    var target = e.target || e.srcElement;
-    var tooltip = document.createDocumentFragment();
-    for (var i = 0; i < annotations.length; i++) {
-      var ann = annotations[i];
-      tooltip.appendChild(annotationTooltip(ann));
-    }
-    showTooltipFor(cm, e, tooltip, target);
-  }
-
-  function onMouseOver(cm, e) {
-    var target = e.target || e.srcElement;
-    if (!/\bCodeMirror-lint-mark-/.test(target.className)) return;
-    var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2;
-    var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client"));
-
-    var annotations = [];
-    for (var i = 0; i < spans.length; ++i) {
-      var ann = spans[i].__annotation;
-      if (ann) annotations.push(ann);
-    }
-    if (annotations.length) popupTooltips(cm, annotations, e);
-  }
-
-  CodeMirror.defineOption("lint", false, function(cm, val, old) {
-    if (old && old != CodeMirror.Init) {
-      clearMarks(cm);
-      if (cm.state.lint.options.lintOnChange !== false)
-        cm.off("change", onChange);
-      CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver);
-      clearTimeout(cm.state.lint.timeout);
-      delete cm.state.lint;
-    }
-
-    if (val) {
-      var gutters = cm.getOption("gutters"), hasLintGutter = false;
-      for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true;
-      var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter);
-      if (state.options.lintOnChange !== false)
-        cm.on("change", onChange);
-      if (state.options.tooltips != false && state.options.tooltips != "gutter")
-        CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver);
-
-      startLinting(cm);
-    }
-  });
-
-  CodeMirror.defineExtension("performLint", function() {
-    if (this.state.lint) startLinting(this);
-  });
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/mdn-like-custom.css b/plugins/UiFileManager/media/codemirror/extension/mdn-like-custom.css
deleted file mode 100644
index 7a523119..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/mdn-like-custom.css
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
-  MDN-LIKE Theme - Mozilla
-  Ported to CodeMirror by Peter Kroon <plakroon@gmail.com>
-  Report bugs/issues here: https://github.com/codemirror/CodeMirror/issues
-  GitHub: @peterkroon
-
-  The mdn-like theme is inspired on the displayed code examples at: https://developer.mozilla.org/en-US/docs/Web/CSS/animation
-
-*/
-.cm-s-mdn-like.CodeMirror { color: #666; background-color: #fff; }
-.cm-s-mdn-like div.CodeMirror-selected { background: #fefdb5; }
-.cm-s-mdn-like .CodeMirror-line::selection, .cm-s-mdn-like .CodeMirror-line > span::selection, .cm-s-mdn-like .CodeMirror-line > span > span::selection { background: #fefdb5; }
-.cm-s-mdn-like .CodeMirror-line::-moz-selection, .cm-s-mdn-like .CodeMirror-line > span::-moz-selection, .cm-s-mdn-like .CodeMirror-line > span > span::-moz-selection { background: #fefdb5; }
-
-.cm-s-mdn-like .CodeMirror-gutters { background: #f8f8f8; color: #333; }
-.cm-s-mdn-like .CodeMirror-linenumber { color: #aaa; padding-left: 8px; }
-.cm-s-mdn-like .CodeMirror-cursor { border-left: 2px solid #222; }
-
-.cm-s-mdn-like .cm-keyword { color: #6262FF; }
-.cm-s-mdn-like .cm-atom { color: #F90; }
-.cm-s-mdn-like .cm-number { color:  #ca7841; }
-.cm-s-mdn-like .cm-def { color: #8DA6CE; }
-.cm-s-mdn-like span.cm-variable-2, .cm-s-mdn-like span.cm-tag { color: #690; }
-.cm-s-mdn-like span.cm-variable-3, .cm-s-mdn-like span.cm-def, .cm-s-mdn-like span.cm-type { color: #07a; }
-
-.cm-s-mdn-like .cm-variable { color: #07a; }
-.cm-s-mdn-like .cm-property { color: #905; }
-.cm-s-mdn-like .cm-qualifier { color: #690; }
-
-.cm-s-mdn-like .cm-operator { color: #cda869; }
-.cm-s-mdn-like .cm-comment { color:#777; font-weight:normal; }
-.cm-s-mdn-like .cm-string { color:#07a; }
-.cm-s-mdn-like .cm-string-2 { color:#bd6b18; } /*?*/
-.cm-s-mdn-like .cm-meta { color: #000; } /*?*/
-.cm-s-mdn-like .cm-builtin { color: #9B7536; } /*?*/
-.cm-s-mdn-like .cm-tag { color: #997643; }
-.cm-s-mdn-like .cm-attribute { color: #d6bb6d; } /*?*/
-.cm-s-mdn-like .cm-header { color: #FF6400; }
-.cm-s-mdn-like .cm-hr { color: #AEAEAE; }
-.cm-s-mdn-like .cm-link { color:#ad9361; font-style:italic; text-decoration:none; }
-.cm-s-mdn-like .cm-error { border-bottom: 1px solid red; }
-
-div.cm-s-mdn-like .CodeMirror-activeline-background { background: #efefff; }
-div.cm-s-mdn-like span.CodeMirror-matchingbracket { outline:1px solid grey; color: inherit; }
diff --git a/plugins/UiFileManager/media/codemirror/extension/scroll/annotatescrollbar.js b/plugins/UiFileManager/media/codemirror/extension/scroll/annotatescrollbar.js
deleted file mode 100644
index 9fe61ec1..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/scroll/annotatescrollbar.js
+++ /dev/null
@@ -1,122 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  CodeMirror.defineExtension("annotateScrollbar", function(options) {
-    if (typeof options == "string") options = {className: options};
-    return new Annotation(this, options);
-  });
-
-  CodeMirror.defineOption("scrollButtonHeight", 0);
-
-  function Annotation(cm, options) {
-    this.cm = cm;
-    this.options = options;
-    this.buttonHeight = options.scrollButtonHeight || cm.getOption("scrollButtonHeight");
-    this.annotations = [];
-    this.doRedraw = this.doUpdate = null;
-    this.div = cm.getWrapperElement().appendChild(document.createElement("div"));
-    this.div.style.cssText = "position: absolute; right: 0; top: 0; z-index: 7; pointer-events: none";
-    this.computeScale();
-
-    function scheduleRedraw(delay) {
-      clearTimeout(self.doRedraw);
-      self.doRedraw = setTimeout(function() { self.redraw(); }, delay);
-    }
-
-    var self = this;
-    cm.on("refresh", this.resizeHandler = function() {
-      clearTimeout(self.doUpdate);
-      self.doUpdate = setTimeout(function() {
-        if (self.computeScale()) scheduleRedraw(20);
-      }, 100);
-    });
-    cm.on("markerAdded", this.resizeHandler);
-    cm.on("markerCleared", this.resizeHandler);
-    if (options.listenForChanges !== false)
-      cm.on("changes", this.changeHandler = function() {
-        scheduleRedraw(250);
-      });
-  }
-
-  Annotation.prototype.computeScale = function() {
-    var cm = this.cm;
-    var hScale = (cm.getWrapperElement().clientHeight - cm.display.barHeight - this.buttonHeight * 2) /
-      cm.getScrollerElement().scrollHeight
-    if (hScale != this.hScale) {
-      this.hScale = hScale;
-      return true;
-    }
-  };
-
-  Annotation.prototype.update = function(annotations) {
-    this.annotations = annotations;
-    this.redraw();
-  };
-
-  Annotation.prototype.redraw = function(compute) {
-    if (compute !== false) this.computeScale();
-    var cm = this.cm, hScale = this.hScale;
-
-    var frag = document.createDocumentFragment(), anns = this.annotations;
-
-    var wrapping = cm.getOption("lineWrapping");
-    var singleLineH = wrapping && cm.defaultTextHeight() * 1.5;
-    var curLine = null, curLineObj = null;
-    function getY(pos, top) {
-      if (curLine != pos.line) {
-        curLine = pos.line;
-        curLineObj = cm.getLineHandle(curLine);
-      }
-      if ((curLineObj.widgets && curLineObj.widgets.length) ||
-          (wrapping && curLineObj.height > singleLineH))
-        return cm.charCoords(pos, "local")[top ? "top" : "bottom"];
-      var topY = cm.heightAtLine(curLineObj, "local");
-      return topY + (top ? 0 : curLineObj.height);
-    }
-
-    var lastLine = cm.lastLine()
-    if (cm.display.barWidth) for (var i = 0, nextTop; i < anns.length; i++) {
-      var ann = anns[i];
-      if (ann.to.line > lastLine) continue;
-      var top = nextTop || getY(ann.from, true) * hScale;
-      var bottom = getY(ann.to, false) * hScale;
-      while (i < anns.length - 1) {
-        if (anns[i + 1].to.line > lastLine) break;
-        nextTop = getY(anns[i + 1].from, true) * hScale;
-        if (nextTop > bottom + .9) break;
-        ann = anns[++i];
-        bottom = getY(ann.to, false) * hScale;
-      }
-      if (bottom == top) continue;
-      var height = Math.max(bottom - top, 3);
-
-      var elt = frag.appendChild(document.createElement("div"));
-      elt.style.cssText = "position: absolute; right: 0px; width: " + Math.max(cm.display.barWidth - 1, 2) + "px; top: "
-        + (top + this.buttonHeight) + "px; height: " + height + "px";
-      elt.className = this.options.className;
-      if (ann.id) {
-        elt.setAttribute("annotation-id", ann.id);
-      }
-    }
-    this.div.textContent = "";
-    this.div.appendChild(frag);
-  };
-
-  Annotation.prototype.clear = function() {
-    this.cm.off("refresh", this.resizeHandler);
-    this.cm.off("markerAdded", this.resizeHandler);
-    this.cm.off("markerCleared", this.resizeHandler);
-    if (this.changeHandler) this.cm.off("changes", this.changeHandler);
-    this.div.parentNode.removeChild(this.div);
-  };
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/scroll/scrollpastend.js b/plugins/UiFileManager/media/codemirror/extension/scroll/scrollpastend.js
deleted file mode 100644
index 2ed9d95e..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/scroll/scrollpastend.js
+++ /dev/null
@@ -1,48 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  CodeMirror.defineOption("scrollPastEnd", false, function(cm, val, old) {
-    if (old && old != CodeMirror.Init) {
-      cm.off("change", onChange);
-      cm.off("refresh", updateBottomMargin);
-      cm.display.lineSpace.parentNode.style.paddingBottom = "";
-      cm.state.scrollPastEndPadding = null;
-    }
-    if (val) {
-      cm.on("change", onChange);
-      cm.on("refresh", updateBottomMargin);
-      updateBottomMargin(cm);
-    }
-  });
-
-  function onChange(cm, change) {
-    if (CodeMirror.changeEnd(change).line == cm.lastLine())
-      updateBottomMargin(cm);
-  }
-
-  function updateBottomMargin(cm) {
-    var padding = "";
-    if (cm.lineCount() > 1) {
-      var totalH = cm.display.scroller.clientHeight - 30,
-          lastLineH = cm.getLineHandle(cm.lastLine()).height;
-      padding = (totalH - lastLineH) + "px";
-    }
-    if (cm.state.scrollPastEndPadding != padding) {
-      cm.state.scrollPastEndPadding = padding;
-      cm.display.lineSpace.parentNode.style.paddingBottom = padding;
-      cm.off("refresh", updateBottomMargin);
-      cm.setSize();
-      cm.on("refresh", updateBottomMargin);
-    }
-  }
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/scroll/simplescrollbars.css b/plugins/UiFileManager/media/codemirror/extension/scroll/simplescrollbars.css
deleted file mode 100644
index 5eea7aa1..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/scroll/simplescrollbars.css
+++ /dev/null
@@ -1,66 +0,0 @@
-.CodeMirror-simplescroll-horizontal div, .CodeMirror-simplescroll-vertical div {
-  position: absolute;
-  background: #ccc;
-  -moz-box-sizing: border-box;
-  box-sizing: border-box;
-  border: 1px solid #bbb;
-  border-radius: 2px;
-}
-
-.CodeMirror-simplescroll-horizontal, .CodeMirror-simplescroll-vertical {
-  position: absolute;
-  z-index: 6;
-  background: #eee;
-}
-
-.CodeMirror-simplescroll-horizontal {
-  bottom: 0; left: 0;
-  height: 8px;
-}
-.CodeMirror-simplescroll-horizontal div {
-  bottom: 0;
-  height: 100%;
-}
-
-.CodeMirror-simplescroll-vertical {
-  right: 0; top: 0;
-  width: 8px;
-}
-.CodeMirror-simplescroll-vertical div {
-  right: 0;
-  width: 100%;
-}
-
-
-.CodeMirror-overlayscroll .CodeMirror-scrollbar-filler, .CodeMirror-overlayscroll .CodeMirror-gutter-filler {
-  display: none;
-}
-
-.CodeMirror-overlayscroll-horizontal div, .CodeMirror-overlayscroll-vertical div {
-  position: absolute;
-  background: #bcd;
-  border-radius: 3px;
-}
-
-.CodeMirror-overlayscroll-horizontal, .CodeMirror-overlayscroll-vertical {
-  position: absolute;
-  z-index: 6;
-}
-
-.CodeMirror-overlayscroll-horizontal {
-  bottom: 0; left: 0;
-  height: 6px;
-}
-.CodeMirror-overlayscroll-horizontal div {
-  bottom: 0;
-  height: 100%;
-}
-
-.CodeMirror-overlayscroll-vertical {
-  right: 0; top: 0;
-  width: 6px;
-}
-.CodeMirror-overlayscroll-vertical div {
-  right: 0;
-  width: 100%;
-}
diff --git a/plugins/UiFileManager/media/codemirror/extension/scroll/simplescrollbars.js b/plugins/UiFileManager/media/codemirror/extension/scroll/simplescrollbars.js
deleted file mode 100644
index 750a2bd3..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/scroll/simplescrollbars.js
+++ /dev/null
@@ -1,152 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  function Bar(cls, orientation, scroll) {
-    this.orientation = orientation;
-    this.scroll = scroll;
-    this.screen = this.total = this.size = 1;
-    this.pos = 0;
-
-    this.node = document.createElement("div");
-    this.node.className = cls + "-" + orientation;
-    this.inner = this.node.appendChild(document.createElement("div"));
-
-    var self = this;
-    CodeMirror.on(this.inner, "mousedown", function(e) {
-      if (e.which != 1) return;
-      CodeMirror.e_preventDefault(e);
-      var axis = self.orientation == "horizontal" ? "pageX" : "pageY";
-      var start = e[axis], startpos = self.pos;
-      function done() {
-        CodeMirror.off(document, "mousemove", move);
-        CodeMirror.off(document, "mouseup", done);
-      }
-      function move(e) {
-        if (e.which != 1) return done();
-        self.moveTo(startpos + (e[axis] - start) * (self.total / self.size));
-      }
-      CodeMirror.on(document, "mousemove", move);
-      CodeMirror.on(document, "mouseup", done);
-    });
-
-    CodeMirror.on(this.node, "click", function(e) {
-      CodeMirror.e_preventDefault(e);
-      var innerBox = self.inner.getBoundingClientRect(), where;
-      if (self.orientation == "horizontal")
-        where = e.clientX < innerBox.left ? -1 : e.clientX > innerBox.right ? 1 : 0;
-      else
-        where = e.clientY < innerBox.top ? -1 : e.clientY > innerBox.bottom ? 1 : 0;
-      self.moveTo(self.pos + where * self.screen);
-    });
-
-    function onWheel(e) {
-      var moved = CodeMirror.wheelEventPixels(e)[self.orientation == "horizontal" ? "x" : "y"];
-      var oldPos = self.pos;
-      self.moveTo(self.pos + moved);
-      if (self.pos != oldPos) CodeMirror.e_preventDefault(e);
-    }
-    CodeMirror.on(this.node, "mousewheel", onWheel);
-    CodeMirror.on(this.node, "DOMMouseScroll", onWheel);
-  }
-
-  Bar.prototype.setPos = function(pos, force) {
-    if (pos < 0) pos = 0;
-    if (pos > this.total - this.screen) pos = this.total - this.screen;
-    if (!force && pos == this.pos) return false;
-    this.pos = pos;
-    this.inner.style[this.orientation == "horizontal" ? "left" : "top"] =
-      (pos * (this.size / this.total)) + "px";
-    return true
-  };
-
-  Bar.prototype.moveTo = function(pos) {
-    if (this.setPos(pos)) this.scroll(pos, this.orientation);
-  }
-
-  var minButtonSize = 10;
-
-  Bar.prototype.update = function(scrollSize, clientSize, barSize) {
-    var sizeChanged = this.screen != clientSize || this.total != scrollSize || this.size != barSize
-    if (sizeChanged) {
-      this.screen = clientSize;
-      this.total = scrollSize;
-      this.size = barSize;
-    }
-
-    var buttonSize = this.screen * (this.size / this.total);
-    if (buttonSize < minButtonSize) {
-      this.size -= minButtonSize - buttonSize;
-      buttonSize = minButtonSize;
-    }
-    this.inner.style[this.orientation == "horizontal" ? "width" : "height"] =
-      buttonSize + "px";
-    this.setPos(this.pos, sizeChanged);
-  };
-
-  function SimpleScrollbars(cls, place, scroll) {
-    this.addClass = cls;
-    this.horiz = new Bar(cls, "horizontal", scroll);
-    place(this.horiz.node);
-    this.vert = new Bar(cls, "vertical", scroll);
-    place(this.vert.node);
-    this.width = null;
-  }
-
-  SimpleScrollbars.prototype.update = function(measure) {
-    if (this.width == null) {
-      var style = window.getComputedStyle ? window.getComputedStyle(this.horiz.node) : this.horiz.node.currentStyle;
-      if (style) this.width = parseInt(style.height);
-    }
-    var width = this.width || 0;
-
-    var needsH = measure.scrollWidth > measure.clientWidth + 1;
-    var needsV = measure.scrollHeight > measure.clientHeight + 1;
-    this.vert.node.style.display = needsV ? "block" : "none";
-    this.horiz.node.style.display = needsH ? "block" : "none";
-
-    if (needsV) {
-      this.vert.update(measure.scrollHeight, measure.clientHeight,
-                       measure.viewHeight - (needsH ? width : 0));
-      this.vert.node.style.bottom = needsH ? width + "px" : "0";
-    }
-    if (needsH) {
-      this.horiz.update(measure.scrollWidth, measure.clientWidth,
-                        measure.viewWidth - (needsV ? width : 0) - measure.barLeft);
-      this.horiz.node.style.right = needsV ? width + "px" : "0";
-      this.horiz.node.style.left = measure.barLeft + "px";
-    }
-
-    return {right: needsV ? width : 0, bottom: needsH ? width : 0};
-  };
-
-  SimpleScrollbars.prototype.setScrollTop = function(pos) {
-    this.vert.setPos(pos);
-  };
-
-  SimpleScrollbars.prototype.setScrollLeft = function(pos) {
-    this.horiz.setPos(pos);
-  };
-
-  SimpleScrollbars.prototype.clear = function() {
-    var parent = this.horiz.node.parentNode;
-    parent.removeChild(this.horiz.node);
-    parent.removeChild(this.vert.node);
-  };
-
-  CodeMirror.scrollbarModel.simple = function(place, scroll) {
-    return new SimpleScrollbars("CodeMirror-simplescroll", place, scroll);
-  };
-  CodeMirror.scrollbarModel.overlay = function(place, scroll) {
-    return new SimpleScrollbars("CodeMirror-overlayscroll", place, scroll);
-  };
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/search/jump-to-line.js b/plugins/UiFileManager/media/codemirror/extension/search/jump-to-line.js
deleted file mode 100644
index 1f3526d2..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/search/jump-to-line.js
+++ /dev/null
@@ -1,50 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-// Defines jumpToLine command. Uses dialog.js if present.
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("../dialog/dialog"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "../dialog/dialog"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  function dialog(cm, text, shortText, deflt, f) {
-    if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true});
-    else f(prompt(shortText, deflt));
-  }
-
-  function getJumpDialog(cm) {
-    return cm.phrase("Jump to line:") + ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">' + cm.phrase("(Use line:column or scroll% syntax)") + '</span>';
-  }
-
-  function interpretLine(cm, string) {
-    var num = Number(string)
-    if (/^[-+]/.test(string)) return cm.getCursor().line + num
-    else return num - 1
-  }
-
-  CodeMirror.commands.jumpToLine = function(cm) {
-    var cur = cm.getCursor();
-    dialog(cm, getJumpDialog(cm), cm.phrase("Jump to line:"), (cur.line + 1) + ":" + cur.ch, function(posStr) {
-      if (!posStr) return;
-
-      var match;
-      if (match = /^\s*([\+\-]?\d+)\s*\:\s*(\d+)\s*$/.exec(posStr)) {
-        cm.setCursor(interpretLine(cm, match[1]), Number(match[2]))
-      } else if (match = /^\s*([\+\-]?\d+(\.\d+)?)\%\s*/.exec(posStr)) {
-        var line = Math.round(cm.lineCount() * Number(match[1]) / 100);
-        if (/^[-+]/.test(match[1])) line = cur.line + line + 1;
-        cm.setCursor(line - 1, cur.ch);
-      } else if (match = /^\s*\:?\s*([\+\-]?\d+)\s*/.exec(posStr)) {
-        cm.setCursor(interpretLine(cm, match[1]), cur.ch);
-      }
-    });
-  };
-
-  CodeMirror.keyMap["default"]["Alt-G"] = "jumpToLine";
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/search/match-highlighter.js b/plugins/UiFileManager/media/codemirror/extension/search/match-highlighter.js
deleted file mode 100644
index 3a4a7ded..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/search/match-highlighter.js
+++ /dev/null
@@ -1,167 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-// Highlighting text that matches the selection
-//
-// Defines an option highlightSelectionMatches, which, when enabled,
-// will style strings that match the selection throughout the
-// document.
-//
-// The option can be set to true to simply enable it, or to a
-// {minChars, style, wordsOnly, showToken, delay} object to explicitly
-// configure it. minChars is the minimum amount of characters that should be
-// selected for the behavior to occur, and style is the token style to
-// apply to the matches. This will be prefixed by "cm-" to create an
-// actual CSS class name. If wordsOnly is enabled, the matches will be
-// highlighted only if the selected text is a word. showToken, when enabled,
-// will cause the current token to be highlighted when nothing is selected.
-// delay is used to specify how much time to wait, in milliseconds, before
-// highlighting the matches. If annotateScrollbar is enabled, the occurences
-// will be highlighted on the scrollbar via the matchesonscrollbar addon.
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("./matchesonscrollbar"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "./matchesonscrollbar"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  var defaults = {
-    style: "matchhighlight",
-    minChars: 2,
-    delay: 100,
-    wordsOnly: false,
-    annotateScrollbar: false,
-    showToken: false,
-    trim: true
-  }
-
-  function State(options) {
-    this.options = {}
-    for (var name in defaults)
-      this.options[name] = (options && options.hasOwnProperty(name) ? options : defaults)[name]
-    this.overlay = this.timeout = null;
-    this.matchesonscroll = null;
-    this.active = false;
-  }
-
-  CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) {
-    if (old && old != CodeMirror.Init) {
-      removeOverlay(cm);
-      clearTimeout(cm.state.matchHighlighter.timeout);
-      cm.state.matchHighlighter = null;
-      cm.off("cursorActivity", cursorActivity);
-      cm.off("focus", onFocus)
-    }
-    if (val) {
-      var state = cm.state.matchHighlighter = new State(val);
-      if (cm.hasFocus()) {
-        state.active = true
-        highlightMatches(cm)
-      } else {
-        cm.on("focus", onFocus)
-      }
-      cm.on("cursorActivity", cursorActivity);
-    }
-  });
-
-  function cursorActivity(cm) {
-    var state = cm.state.matchHighlighter;
-    if (state.active || cm.hasFocus()) scheduleHighlight(cm, state)
-  }
-
-  function onFocus(cm) {
-    var state = cm.state.matchHighlighter
-    if (!state.active) {
-      state.active = true
-      scheduleHighlight(cm, state)
-    }
-  }
-
-  function scheduleHighlight(cm, state) {
-    clearTimeout(state.timeout);
-    state.timeout = setTimeout(function() {highlightMatches(cm);}, state.options.delay);
-  }
-
-  function addOverlay(cm, query, hasBoundary, style) {
-    var state = cm.state.matchHighlighter;
-    cm.addOverlay(state.overlay = makeOverlay(query, hasBoundary, style));
-    if (state.options.annotateScrollbar && cm.showMatchesOnScrollbar) {
-      var searchFor = hasBoundary ? new RegExp((/\w/.test(query.charAt(0)) ? "\\b" : "") +
-                                               query.replace(/[\\\[.+*?(){|^$]/g, "\\$&") +
-                                               (/\w/.test(query.charAt(query.length - 1)) ? "\\b" : "")) : query;
-      state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, false,
-        {className: "CodeMirror-selection-highlight-scrollbar"});
-    }
-  }
-
-  function removeOverlay(cm) {
-    var state = cm.state.matchHighlighter;
-    if (state.overlay) {
-      cm.removeOverlay(state.overlay);
-      state.overlay = null;
-      if (state.matchesonscroll) {
-        state.matchesonscroll.clear();
-        state.matchesonscroll = null;
-      }
-    }
-  }
-
-  function highlightMatches(cm) {
-    cm.operation(function() {
-      var state = cm.state.matchHighlighter;
-      removeOverlay(cm);
-      if (!cm.somethingSelected() && state.options.showToken) {
-        var re = state.options.showToken === true ? /[\w$]/ : state.options.showToken;
-        var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start;
-        while (start && re.test(line.charAt(start - 1))) --start;
-        while (end < line.length && re.test(line.charAt(end))) ++end;
-        if (start < end)
-          addOverlay(cm, line.slice(start, end), re, state.options.style);
-        return;
-      }
-      var from = cm.getCursor("from"), to = cm.getCursor("to");
-      if (from.line != to.line) return;
-      if (state.options.wordsOnly && !isWord(cm, from, to)) return;
-      var selection = cm.getRange(from, to)
-      if (state.options.trim) selection = selection.replace(/^\s+|\s+$/g, "")
-      if (selection.length >= state.options.minChars)
-        addOverlay(cm, selection, false, state.options.style);
-    });
-  }
-
-  function isWord(cm, from, to) {
-    var str = cm.getRange(from, to);
-    if (str.match(/^\w+$/) !== null) {
-        if (from.ch > 0) {
-            var pos = {line: from.line, ch: from.ch - 1};
-            var chr = cm.getRange(pos, from);
-            if (chr.match(/\W/) === null) return false;
-        }
-        if (to.ch < cm.getLine(from.line).length) {
-            var pos = {line: to.line, ch: to.ch + 1};
-            var chr = cm.getRange(to, pos);
-            if (chr.match(/\W/) === null) return false;
-        }
-        return true;
-    } else return false;
-  }
-
-  function boundariesAround(stream, re) {
-    return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) &&
-      (stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos)));
-  }
-
-  function makeOverlay(query, hasBoundary, style) {
-    return {token: function(stream) {
-      if (stream.match(query) &&
-          (!hasBoundary || boundariesAround(stream, hasBoundary)))
-        return style;
-      stream.next();
-      stream.skipTo(query.charAt(0)) || stream.skipToEnd();
-    }};
-  }
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/search/matchesonscrollbar.css b/plugins/UiFileManager/media/codemirror/extension/search/matchesonscrollbar.css
deleted file mode 100644
index 77932cc9..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/search/matchesonscrollbar.css
+++ /dev/null
@@ -1,8 +0,0 @@
-.CodeMirror-search-match {
-  background: gold;
-  border-top: 1px solid orange;
-  border-bottom: 1px solid orange;
-  -moz-box-sizing: border-box;
-  box-sizing: border-box;
-  opacity: .5;
-}
diff --git a/plugins/UiFileManager/media/codemirror/extension/search/matchesonscrollbar.js b/plugins/UiFileManager/media/codemirror/extension/search/matchesonscrollbar.js
deleted file mode 100644
index 8a4a8275..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/search/matchesonscrollbar.js
+++ /dev/null
@@ -1,97 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("./searchcursor"), require("../scroll/annotatescrollbar"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "./searchcursor", "../scroll/annotatescrollbar"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  CodeMirror.defineExtension("showMatchesOnScrollbar", function(query, caseFold, options) {
-    if (typeof options == "string") options = {className: options};
-    if (!options) options = {};
-    return new SearchAnnotation(this, query, caseFold, options);
-  });
-
-  function SearchAnnotation(cm, query, caseFold, options) {
-    this.cm = cm;
-    this.options = options;
-    var annotateOptions = {listenForChanges: false};
-    for (var prop in options) annotateOptions[prop] = options[prop];
-    if (!annotateOptions.className) annotateOptions.className = "CodeMirror-search-match";
-    this.annotation = cm.annotateScrollbar(annotateOptions);
-    this.query = query;
-    this.caseFold = caseFold;
-    this.gap = {from: cm.firstLine(), to: cm.lastLine() + 1};
-    this.matches = [];
-    this.update = null;
-
-    this.findMatches();
-    this.annotation.update(this.matches);
-
-    var self = this;
-    cm.on("change", this.changeHandler = function(_cm, change) { self.onChange(change); });
-  }
-
-  var MAX_MATCHES = 1000;
-
-  SearchAnnotation.prototype.findMatches = function() {
-    if (!this.gap) return;
-    for (var i = 0; i < this.matches.length; i++) {
-      var match = this.matches[i];
-      if (match.from.line >= this.gap.to) break;
-      if (match.to.line >= this.gap.from) this.matches.splice(i--, 1);
-    }
-    var cursor = this.cm.getSearchCursor(this.query, CodeMirror.Pos(this.gap.from, 0), {caseFold: this.caseFold, multiline: this.options.multiline});
-    var maxMatches = this.options && this.options.maxMatches || MAX_MATCHES;
-    while (cursor.findNext()) {
-      var match = {from: cursor.from(), to: cursor.to()};
-      if (match.from.line >= this.gap.to) break;
-      this.matches.splice(i++, 0, match);
-      if (this.matches.length > maxMatches) break;
-    }
-    this.gap = null;
-  };
-
-  function offsetLine(line, changeStart, sizeChange) {
-    if (line <= changeStart) return line;
-    return Math.max(changeStart, line + sizeChange);
-  }
-
-  SearchAnnotation.prototype.onChange = function(change) {
-    var startLine = change.from.line;
-    var endLine = CodeMirror.changeEnd(change).line;
-    var sizeChange = endLine - change.to.line;
-    if (this.gap) {
-      this.gap.from = Math.min(offsetLine(this.gap.from, startLine, sizeChange), change.from.line);
-      this.gap.to = Math.max(offsetLine(this.gap.to, startLine, sizeChange), change.from.line);
-    } else {
-      this.gap = {from: change.from.line, to: endLine + 1};
-    }
-
-    if (sizeChange) for (var i = 0; i < this.matches.length; i++) {
-      var match = this.matches[i];
-      var newFrom = offsetLine(match.from.line, startLine, sizeChange);
-      if (newFrom != match.from.line) match.from = CodeMirror.Pos(newFrom, match.from.ch);
-      var newTo = offsetLine(match.to.line, startLine, sizeChange);
-      if (newTo != match.to.line) match.to = CodeMirror.Pos(newTo, match.to.ch);
-    }
-    clearTimeout(this.update);
-    var self = this;
-    this.update = setTimeout(function() { self.updateAfterChange(); }, 250);
-  };
-
-  SearchAnnotation.prototype.updateAfterChange = function() {
-    this.findMatches();
-    this.annotation.update(this.matches);
-  };
-
-  SearchAnnotation.prototype.clear = function() {
-    this.cm.off("change", this.changeHandler);
-    this.annotation.clear();
-  };
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/search/search.js b/plugins/UiFileManager/media/codemirror/extension/search/search.js
deleted file mode 100644
index cecdd52e..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/search/search.js
+++ /dev/null
@@ -1,260 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-// Define search commands. Depends on dialog.js or another
-// implementation of the openDialog method.
-
-// Replace works a little oddly -- it will do the replace on the next
-// Ctrl-G (or whatever is bound to findNext) press. You prevent a
-// replace by making sure the match is no longer selected when hitting
-// Ctrl-G.
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("./searchcursor"), require("../dialog/dialog"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "./searchcursor", "../dialog/dialog"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  function searchOverlay(query, caseInsensitive) {
-    if (typeof query == "string")
-      query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), caseInsensitive ? "gi" : "g");
-    else if (!query.global)
-      query = new RegExp(query.source, query.ignoreCase ? "gi" : "g");
-
-    return {token: function(stream) {
-      query.lastIndex = stream.pos;
-      var match = query.exec(stream.string);
-      if (match && match.index == stream.pos) {
-        stream.pos += match[0].length || 1;
-        return "searching";
-      } else if (match) {
-        stream.pos = match.index;
-      } else {
-        stream.skipToEnd();
-      }
-    }};
-  }
-
-  function SearchState() {
-    this.posFrom = this.posTo = this.lastQuery = this.query = null;
-    this.overlay = null;
-  }
-
-  function getSearchState(cm) {
-    return cm.state.search || (cm.state.search = new SearchState());
-  }
-
-  function queryCaseInsensitive(query) {
-    return typeof query == "string" && query == query.toLowerCase();
-  }
-
-  function getSearchCursor(cm, query, pos) {
-    // Heuristic: if the query string is all lowercase, do a case insensitive search.
-    return cm.getSearchCursor(query, pos, {caseFold: queryCaseInsensitive(query), multiline: true});
-  }
-
-  function persistentDialog(cm, text, deflt, onEnter, onKeyDown) {
-    cm.openDialog(text, onEnter, {
-      value: deflt,
-      selectValueOnOpen: true,
-      closeOnEnter: false,
-      onClose: function() { clearSearch(cm); },
-      onKeyDown: onKeyDown
-    });
-  }
-
-  function dialog(cm, text, shortText, deflt, f) {
-    if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true});
-    else f(prompt(shortText, deflt));
-  }
-
-  function confirmDialog(cm, text, shortText, fs) {
-    if (cm.openConfirm) cm.openConfirm(text, fs);
-    else if (confirm(shortText)) fs[0]();
-  }
-
-  function parseString(string) {
-    return string.replace(/\\([nrt\\])/g, function(match, ch) {
-      if (ch == "n") return "\n"
-      if (ch == "r") return "\r"
-      if (ch == "t") return "\t"
-      if (ch == "\\") return "\\"
-      return match
-    })
-  }
-
-  function parseQuery(query) {
-    var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
-    if (isRE) {
-      try { query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i"); }
-      catch(e) {} // Not a regular expression after all, do a string search
-    } else {
-      query = parseString(query)
-    }
-    if (typeof query == "string" ? query == "" : query.test(""))
-      query = /x^/;
-    return query;
-  }
-
-  function startSearch(cm, state, query) {
-    state.queryText = query;
-    state.query = parseQuery(query);
-    cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
-    state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
-    cm.addOverlay(state.overlay);
-    if (cm.showMatchesOnScrollbar) {
-      if (state.annotate) { state.annotate.clear(); state.annotate = null; }
-      state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));
-    }
-  }
-
-  function doSearch(cm, rev, persistent, immediate) {
-    var state = getSearchState(cm);
-    if (state.query) return findNext(cm, rev);
-    var q = cm.getSelection() || state.lastQuery;
-    if (q instanceof RegExp && q.source == "x^") q = null
-    if (persistent && cm.openDialog) {
-      var hiding = null
-      var searchNext = function(query, event) {
-        CodeMirror.e_stop(event);
-        if (!query) return;
-        if (query != state.queryText) {
-          startSearch(cm, state, query);
-          state.posFrom = state.posTo = cm.getCursor();
-        }
-        if (hiding) hiding.style.opacity = 1
-        findNext(cm, event.shiftKey, function(_, to) {
-          var dialog
-          if (to.line < 3 && document.querySelector &&
-              (dialog = cm.display.wrapper.querySelector(".CodeMirror-dialog")) &&
-              dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, "window").top)
-            (hiding = dialog).style.opacity = .4
-        })
-      };
-      persistentDialog(cm, getQueryDialog(cm), q, searchNext, function(event, query) {
-        var keyName = CodeMirror.keyName(event)
-        var extra = cm.getOption('extraKeys'), cmd = (extra && extra[keyName]) || CodeMirror.keyMap[cm.getOption("keyMap")][keyName]
-        if (cmd == "findNext" || cmd == "findPrev" ||
-          cmd == "findPersistentNext" || cmd == "findPersistentPrev") {
-          CodeMirror.e_stop(event);
-          startSearch(cm, getSearchState(cm), query);
-          cm.execCommand(cmd);
-        } else if (cmd == "find" || cmd == "findPersistent") {
-          CodeMirror.e_stop(event);
-          searchNext(query, event);
-        }
-      });
-      if (immediate && q) {
-        startSearch(cm, state, q);
-        findNext(cm, rev);
-      }
-    } else {
-      dialog(cm, getQueryDialog(cm), "Search for:", q, function(query) {
-        if (query && !state.query) cm.operation(function() {
-          startSearch(cm, state, query);
-          state.posFrom = state.posTo = cm.getCursor();
-          findNext(cm, rev);
-        });
-      });
-    }
-  }
-
-  function findNext(cm, rev, callback) {cm.operation(function() {
-    var state = getSearchState(cm);
-    var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
-    if (!cursor.find(rev)) {
-      cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
-      if (!cursor.find(rev)) return;
-    }
-    cm.setSelection(cursor.from(), cursor.to());
-    cm.scrollIntoView({from: cursor.from(), to: cursor.to()}, 20);
-    state.posFrom = cursor.from(); state.posTo = cursor.to();
-    if (callback) callback(cursor.from(), cursor.to())
-  });}
-
-  function clearSearch(cm) {cm.operation(function() {
-    var state = getSearchState(cm);
-    state.lastQuery = state.query;
-    if (!state.query) return;
-    state.query = state.queryText = null;
-    cm.removeOverlay(state.overlay);
-    if (state.annotate) { state.annotate.clear(); state.annotate = null; }
-  });}
-
-
-  function getQueryDialog(cm)  {
-    return '<span class="CodeMirror-search-label">' + cm.phrase("Search:") + '</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">' + cm.phrase("(Use /re/ syntax for regexp search)") + '</span>';
-  }
-  function getReplaceQueryDialog(cm) {
-    return ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">' + cm.phrase("(Use /re/ syntax for regexp search)") + '</span>';
-  }
-  function getReplacementQueryDialog(cm) {
-    return '<span class="CodeMirror-search-label">' + cm.phrase("With:") + '</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/>';
-  }
-  function getDoReplaceConfirm(cm) {
-    return '<span class="CodeMirror-search-label">' + cm.phrase("Replace?") + '</span> <button>' + cm.phrase("Yes") + '</button> <button>' + cm.phrase("No") + '</button> <button>' + cm.phrase("All") + '</button> <button>' + cm.phrase("Stop") + '</button> ';
-  }
-
-  function replaceAll(cm, query, text) {
-    cm.operation(function() {
-      for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
-        if (typeof query != "string") {
-          var match = cm.getRange(cursor.from(), cursor.to()).match(query);
-          cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
-        } else cursor.replace(text);
-      }
-    });
-  }
-
-  function replace(cm, all) {
-    if (cm.getOption("readOnly")) return;
-    var query = cm.getSelection() || getSearchState(cm).lastQuery;
-    var dialogText = '<span class="CodeMirror-search-label">' + (all ? cm.phrase("Replace all:") : cm.phrase("Replace:")) + '</span>';
-    dialog(cm, dialogText + getReplaceQueryDialog(cm), dialogText, query, function(query) {
-      if (!query) return;
-      query = parseQuery(query);
-      dialog(cm, getReplacementQueryDialog(cm), cm.phrase("Replace with:"), "", function(text) {
-        text = parseString(text)
-        if (all) {
-          replaceAll(cm, query, text)
-        } else {
-          clearSearch(cm);
-          var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
-          var advance = function() {
-            var start = cursor.from(), match;
-            if (!(match = cursor.findNext())) {
-              cursor = getSearchCursor(cm, query);
-              if (!(match = cursor.findNext()) ||
-                  (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
-            }
-            cm.setSelection(cursor.from(), cursor.to());
-            cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
-            confirmDialog(cm, getDoReplaceConfirm(cm), cm.phrase("Replace?"),
-                          [function() {doReplace(match);}, advance,
-                           function() {replaceAll(cm, query, text)}]);
-          };
-          var doReplace = function(match) {
-            cursor.replace(typeof query == "string" ? text :
-                           text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
-            advance();
-          };
-          advance();
-        }
-      });
-    });
-  }
-
-  CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
-  CodeMirror.commands.findPersistent = function(cm) {clearSearch(cm); doSearch(cm, false, true);};
-  CodeMirror.commands.findPersistentNext = function(cm) {doSearch(cm, false, true, true);};
-  CodeMirror.commands.findPersistentPrev = function(cm) {doSearch(cm, true, true, true);};
-  CodeMirror.commands.findNext = doSearch;
-  CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
-  CodeMirror.commands.clearSearch = clearSearch;
-  CodeMirror.commands.replace = replace;
-  CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/search/searchcursor.js b/plugins/UiFileManager/media/codemirror/extension/search/searchcursor.js
deleted file mode 100644
index d5869578..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/search/searchcursor.js
+++ /dev/null
@@ -1,296 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"))
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod)
-  else // Plain browser env
-    mod(CodeMirror)
-})(function(CodeMirror) {
-  "use strict"
-  var Pos = CodeMirror.Pos
-
-  function regexpFlags(regexp) {
-    var flags = regexp.flags
-    return flags != null ? flags : (regexp.ignoreCase ? "i" : "")
-      + (regexp.global ? "g" : "")
-      + (regexp.multiline ? "m" : "")
-  }
-
-  function ensureFlags(regexp, flags) {
-    var current = regexpFlags(regexp), target = current
-    for (var i = 0; i < flags.length; i++) if (target.indexOf(flags.charAt(i)) == -1)
-      target += flags.charAt(i)
-    return current == target ? regexp : new RegExp(regexp.source, target)
-  }
-
-  function maybeMultiline(regexp) {
-    return /\\s|\\n|\n|\\W|\\D|\[\^/.test(regexp.source)
-  }
-
-  function searchRegexpForward(doc, regexp, start) {
-    regexp = ensureFlags(regexp, "g")
-    for (var line = start.line, ch = start.ch, last = doc.lastLine(); line <= last; line++, ch = 0) {
-      regexp.lastIndex = ch
-      var string = doc.getLine(line), match = regexp.exec(string)
-      if (match)
-        return {from: Pos(line, match.index),
-                to: Pos(line, match.index + match[0].length),
-                match: match}
-    }
-  }
-
-  function searchRegexpForwardMultiline(doc, regexp, start) {
-    if (!maybeMultiline(regexp)) return searchRegexpForward(doc, regexp, start)
-
-    regexp = ensureFlags(regexp, "gm")
-    var string, chunk = 1
-    for (var line = start.line, last = doc.lastLine(); line <= last;) {
-      // This grows the search buffer in exponentially-sized chunks
-      // between matches, so that nearby matches are fast and don't
-      // require concatenating the whole document (in case we're
-      // searching for something that has tons of matches), but at the
-      // same time, the amount of retries is limited.
-      for (var i = 0; i < chunk; i++) {
-        if (line > last) break
-        var curLine = doc.getLine(line++)
-        string = string == null ? curLine : string + "\n" + curLine
-      }
-      chunk = chunk * 2
-      regexp.lastIndex = start.ch
-      var match = regexp.exec(string)
-      if (match) {
-        var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n")
-        var startLine = start.line + before.length - 1, startCh = before[before.length - 1].length
-        return {from: Pos(startLine, startCh),
-                to: Pos(startLine + inside.length - 1,
-                        inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),
-                match: match}
-      }
-    }
-  }
-
-  function lastMatchIn(string, regexp, endMargin) {
-    var match, from = 0
-    while (from <= string.length) {
-      regexp.lastIndex = from
-      var newMatch = regexp.exec(string)
-      if (!newMatch) break
-      var end = newMatch.index + newMatch[0].length
-      if (end > string.length - endMargin) break
-      if (!match || end > match.index + match[0].length)
-        match = newMatch
-      from = newMatch.index + 1
-    }
-    return match
-  }
-
-  function searchRegexpBackward(doc, regexp, start) {
-    regexp = ensureFlags(regexp, "g")
-    for (var line = start.line, ch = start.ch, first = doc.firstLine(); line >= first; line--, ch = -1) {
-      var string = doc.getLine(line)
-      var match = lastMatchIn(string, regexp, ch < 0 ? 0 : string.length - ch)
-      if (match)
-        return {from: Pos(line, match.index),
-                to: Pos(line, match.index + match[0].length),
-                match: match}
-    }
-  }
-
-  function searchRegexpBackwardMultiline(doc, regexp, start) {
-    if (!maybeMultiline(regexp)) return searchRegexpBackward(doc, regexp, start)
-    regexp = ensureFlags(regexp, "gm")
-    var string, chunkSize = 1, endMargin = doc.getLine(start.line).length - start.ch
-    for (var line = start.line, first = doc.firstLine(); line >= first;) {
-      for (var i = 0; i < chunkSize && line >= first; i++) {
-        var curLine = doc.getLine(line--)
-        string = string == null ? curLine : curLine + "\n" + string
-      }
-      chunkSize *= 2
-
-      var match = lastMatchIn(string, regexp, endMargin)
-      if (match) {
-        var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n")
-        var startLine = line + before.length, startCh = before[before.length - 1].length
-        return {from: Pos(startLine, startCh),
-                to: Pos(startLine + inside.length - 1,
-                        inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),
-                match: match}
-      }
-    }
-  }
-
-  var doFold, noFold
-  if (String.prototype.normalize) {
-    doFold = function(str) { return str.normalize("NFD").toLowerCase() }
-    noFold = function(str) { return str.normalize("NFD") }
-  } else {
-    doFold = function(str) { return str.toLowerCase() }
-    noFold = function(str) { return str }
-  }
-
-  // Maps a position in a case-folded line back to a position in the original line
-  // (compensating for codepoints increasing in number during folding)
-  function adjustPos(orig, folded, pos, foldFunc) {
-    if (orig.length == folded.length) return pos
-    for (var min = 0, max = pos + Math.max(0, orig.length - folded.length);;) {
-      if (min == max) return min
-      var mid = (min + max) >> 1
-      var len = foldFunc(orig.slice(0, mid)).length
-      if (len == pos) return mid
-      else if (len > pos) max = mid
-      else min = mid + 1
-    }
-  }
-
-  function searchStringForward(doc, query, start, caseFold) {
-    // Empty string would match anything and never progress, so we
-    // define it to match nothing instead.
-    if (!query.length) return null
-    var fold = caseFold ? doFold : noFold
-    var lines = fold(query).split(/\r|\n\r?/)
-
-    search: for (var line = start.line, ch = start.ch, last = doc.lastLine() + 1 - lines.length; line <= last; line++, ch = 0) {
-      var orig = doc.getLine(line).slice(ch), string = fold(orig)
-      if (lines.length == 1) {
-        var found = string.indexOf(lines[0])
-        if (found == -1) continue search
-        var start = adjustPos(orig, string, found, fold) + ch
-        return {from: Pos(line, adjustPos(orig, string, found, fold) + ch),
-                to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold) + ch)}
-      } else {
-        var cutFrom = string.length - lines[0].length
-        if (string.slice(cutFrom) != lines[0]) continue search
-        for (var i = 1; i < lines.length - 1; i++)
-          if (fold(doc.getLine(line + i)) != lines[i]) continue search
-        var end = doc.getLine(line + lines.length - 1), endString = fold(end), lastLine = lines[lines.length - 1]
-        if (endString.slice(0, lastLine.length) != lastLine) continue search
-        return {from: Pos(line, adjustPos(orig, string, cutFrom, fold) + ch),
-                to: Pos(line + lines.length - 1, adjustPos(end, endString, lastLine.length, fold))}
-      }
-    }
-  }
-
-  function searchStringBackward(doc, query, start, caseFold) {
-    if (!query.length) return null
-    var fold = caseFold ? doFold : noFold
-    var lines = fold(query).split(/\r|\n\r?/)
-
-    search: for (var line = start.line, ch = start.ch, first = doc.firstLine() - 1 + lines.length; line >= first; line--, ch = -1) {
-      var orig = doc.getLine(line)
-      if (ch > -1) orig = orig.slice(0, ch)
-      var string = fold(orig)
-      if (lines.length == 1) {
-        var found = string.lastIndexOf(lines[0])
-        if (found == -1) continue search
-        return {from: Pos(line, adjustPos(orig, string, found, fold)),
-                to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold))}
-      } else {
-        var lastLine = lines[lines.length - 1]
-        if (string.slice(0, lastLine.length) != lastLine) continue search
-        for (var i = 1, start = line - lines.length + 1; i < lines.length - 1; i++)
-          if (fold(doc.getLine(start + i)) != lines[i]) continue search
-        var top = doc.getLine(line + 1 - lines.length), topString = fold(top)
-        if (topString.slice(topString.length - lines[0].length) != lines[0]) continue search
-        return {from: Pos(line + 1 - lines.length, adjustPos(top, topString, top.length - lines[0].length, fold)),
-                to: Pos(line, adjustPos(orig, string, lastLine.length, fold))}
-      }
-    }
-  }
-
-  function SearchCursor(doc, query, pos, options) {
-    this.atOccurrence = false
-    this.doc = doc
-    pos = pos ? doc.clipPos(pos) : Pos(0, 0)
-    this.pos = {from: pos, to: pos}
-
-    var caseFold
-    if (typeof options == "object") {
-      caseFold = options.caseFold
-    } else { // Backwards compat for when caseFold was the 4th argument
-      caseFold = options
-      options = null
-    }
-
-    if (typeof query == "string") {
-      if (caseFold == null) caseFold = false
-      this.matches = function(reverse, pos) {
-        return (reverse ? searchStringBackward : searchStringForward)(doc, query, pos, caseFold)
-      }
-    } else {
-      query = ensureFlags(query, "gm")
-      if (!options || options.multiline !== false)
-        this.matches = function(reverse, pos) {
-          return (reverse ? searchRegexpBackwardMultiline : searchRegexpForwardMultiline)(doc, query, pos)
-        }
-      else
-        this.matches = function(reverse, pos) {
-          return (reverse ? searchRegexpBackward : searchRegexpForward)(doc, query, pos)
-        }
-    }
-  }
-
-  SearchCursor.prototype = {
-    findNext: function() {return this.find(false)},
-    findPrevious: function() {return this.find(true)},
-
-    find: function(reverse) {
-      var result = this.matches(reverse, this.doc.clipPos(reverse ? this.pos.from : this.pos.to))
-
-      // Implements weird auto-growing behavior on null-matches for
-      // backwards-compatibility with the vim code (unfortunately)
-      while (result && CodeMirror.cmpPos(result.from, result.to) == 0) {
-        if (reverse) {
-          if (result.from.ch) result.from = Pos(result.from.line, result.from.ch - 1)
-          else if (result.from.line == this.doc.firstLine()) result = null
-          else result = this.matches(reverse, this.doc.clipPos(Pos(result.from.line - 1)))
-        } else {
-          if (result.to.ch < this.doc.getLine(result.to.line).length) result.to = Pos(result.to.line, result.to.ch + 1)
-          else if (result.to.line == this.doc.lastLine()) result = null
-          else result = this.matches(reverse, Pos(result.to.line + 1, 0))
-        }
-      }
-
-      if (result) {
-        this.pos = result
-        this.atOccurrence = true
-        return this.pos.match || true
-      } else {
-        var end = Pos(reverse ? this.doc.firstLine() : this.doc.lastLine() + 1, 0)
-        this.pos = {from: end, to: end}
-        return this.atOccurrence = false
-      }
-    },
-
-    from: function() {if (this.atOccurrence) return this.pos.from},
-    to: function() {if (this.atOccurrence) return this.pos.to},
-
-    replace: function(newText, origin) {
-      if (!this.atOccurrence) return
-      var lines = CodeMirror.splitLines(newText)
-      this.doc.replaceRange(lines, this.pos.from, this.pos.to, origin)
-      this.pos.to = Pos(this.pos.from.line + lines.length - 1,
-                        lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0))
-    }
-  }
-
-  CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
-    return new SearchCursor(this.doc, query, pos, caseFold)
-  })
-  CodeMirror.defineDocExtension("getSearchCursor", function(query, pos, caseFold) {
-    return new SearchCursor(this, query, pos, caseFold)
-  })
-
-  CodeMirror.defineExtension("selectMatches", function(query, caseFold) {
-    var ranges = []
-    var cur = this.getSearchCursor(query, this.getCursor("from"), caseFold)
-    while (cur.findNext()) {
-      if (CodeMirror.cmpPos(cur.to(), this.getCursor("to")) > 0) break
-      ranges.push({anchor: cur.from(), head: cur.to()})
-    }
-    if (ranges.length)
-      this.setSelections(ranges, 0)
-  })
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/selection/active-line.js b/plugins/UiFileManager/media/codemirror/extension/selection/active-line.js
deleted file mode 100644
index c7b14ce0..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/selection/active-line.js
+++ /dev/null
@@ -1,72 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-  var WRAP_CLASS = "CodeMirror-activeline";
-  var BACK_CLASS = "CodeMirror-activeline-background";
-  var GUTT_CLASS = "CodeMirror-activeline-gutter";
-
-  CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) {
-    var prev = old == CodeMirror.Init ? false : old;
-    if (val == prev) return
-    if (prev) {
-      cm.off("beforeSelectionChange", selectionChange);
-      clearActiveLines(cm);
-      delete cm.state.activeLines;
-    }
-    if (val) {
-      cm.state.activeLines = [];
-      updateActiveLines(cm, cm.listSelections());
-      cm.on("beforeSelectionChange", selectionChange);
-    }
-  });
-
-  function clearActiveLines(cm) {
-    for (var i = 0; i < cm.state.activeLines.length; i++) {
-      cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS);
-      cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS);
-      cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS);
-    }
-  }
-
-  function sameArray(a, b) {
-    if (a.length != b.length) return false;
-    for (var i = 0; i < a.length; i++)
-      if (a[i] != b[i]) return false;
-    return true;
-  }
-
-  function updateActiveLines(cm, ranges) {
-    var active = [];
-    for (var i = 0; i < ranges.length; i++) {
-      var range = ranges[i];
-      var option = cm.getOption("styleActiveLine");
-      if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty())
-        continue
-      var line = cm.getLineHandleVisualStart(range.head.line);
-      if (active[active.length - 1] != line) active.push(line);
-    }
-    if (sameArray(cm.state.activeLines, active)) return;
-    cm.operation(function() {
-      clearActiveLines(cm);
-      for (var i = 0; i < active.length; i++) {
-        cm.addLineClass(active[i], "wrap", WRAP_CLASS);
-        cm.addLineClass(active[i], "background", BACK_CLASS);
-        cm.addLineClass(active[i], "gutter", GUTT_CLASS);
-      }
-      cm.state.activeLines = active;
-    });
-  }
-
-  function selectionChange(cm, sel) {
-    updateActiveLines(cm, sel.ranges);
-  }
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/selection/mark-selection.js b/plugins/UiFileManager/media/codemirror/extension/selection/mark-selection.js
deleted file mode 100644
index adfaa62d..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/selection/mark-selection.js
+++ /dev/null
@@ -1,119 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-// Because sometimes you need to mark the selected *text*.
-//
-// Adds an option 'styleSelectedText' which, when enabled, gives
-// selected text the CSS class given as option value, or
-// "CodeMirror-selectedtext" when the value is not a string.
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) {
-    var prev = old && old != CodeMirror.Init;
-    if (val && !prev) {
-      cm.state.markedSelection = [];
-      cm.state.markedSelectionStyle = typeof val == "string" ? val : "CodeMirror-selectedtext";
-      reset(cm);
-      cm.on("cursorActivity", onCursorActivity);
-      cm.on("change", onChange);
-    } else if (!val && prev) {
-      cm.off("cursorActivity", onCursorActivity);
-      cm.off("change", onChange);
-      clear(cm);
-      cm.state.markedSelection = cm.state.markedSelectionStyle = null;
-    }
-  });
-
-  function onCursorActivity(cm) {
-    if (cm.state.markedSelection)
-      cm.operation(function() { update(cm); });
-  }
-
-  function onChange(cm) {
-    if (cm.state.markedSelection && cm.state.markedSelection.length)
-      cm.operation(function() { clear(cm); });
-  }
-
-  var CHUNK_SIZE = 8;
-  var Pos = CodeMirror.Pos;
-  var cmp = CodeMirror.cmpPos;
-
-  function coverRange(cm, from, to, addAt) {
-    if (cmp(from, to) == 0) return;
-    var array = cm.state.markedSelection;
-    var cls = cm.state.markedSelectionStyle;
-    for (var line = from.line;;) {
-      var start = line == from.line ? from : Pos(line, 0);
-      var endLine = line + CHUNK_SIZE, atEnd = endLine >= to.line;
-      var end = atEnd ? to : Pos(endLine, 0);
-      var mark = cm.markText(start, end, {className: cls});
-      if (addAt == null) array.push(mark);
-      else array.splice(addAt++, 0, mark);
-      if (atEnd) break;
-      line = endLine;
-    }
-  }
-
-  function clear(cm) {
-    var array = cm.state.markedSelection;
-    for (var i = 0; i < array.length; ++i) array[i].clear();
-    array.length = 0;
-  }
-
-  function reset(cm) {
-    clear(cm);
-    var ranges = cm.listSelections();
-    for (var i = 0; i < ranges.length; i++)
-      coverRange(cm, ranges[i].from(), ranges[i].to());
-  }
-
-  function update(cm) {
-    if (!cm.somethingSelected()) return clear(cm);
-    if (cm.listSelections().length > 1) return reset(cm);
-
-    var from = cm.getCursor("start"), to = cm.getCursor("end");
-
-    var array = cm.state.markedSelection;
-    if (!array.length) return coverRange(cm, from, to);
-
-    var coverStart = array[0].find(), coverEnd = array[array.length - 1].find();
-    if (!coverStart || !coverEnd || to.line - from.line <= CHUNK_SIZE ||
-        cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0)
-      return reset(cm);
-
-    while (cmp(from, coverStart.from) > 0) {
-      array.shift().clear();
-      coverStart = array[0].find();
-    }
-    if (cmp(from, coverStart.from) < 0) {
-      if (coverStart.to.line - from.line < CHUNK_SIZE) {
-        array.shift().clear();
-        coverRange(cm, from, coverStart.to, 0);
-      } else {
-        coverRange(cm, from, coverStart.from, 0);
-      }
-    }
-
-    while (cmp(to, coverEnd.to) < 0) {
-      array.pop().clear();
-      coverEnd = array[array.length - 1].find();
-    }
-    if (cmp(to, coverEnd.to) > 0) {
-      if (to.line - coverEnd.from.line < CHUNK_SIZE) {
-        array.pop().clear();
-        coverRange(cm, coverEnd.from, to);
-      } else {
-        coverRange(cm, coverEnd.to, to);
-      }
-    }
-  }
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/selection/selection-pointer.js b/plugins/UiFileManager/media/codemirror/extension/selection/selection-pointer.js
deleted file mode 100644
index f0bd61a3..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/selection/selection-pointer.js
+++ /dev/null
@@ -1,98 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  CodeMirror.defineOption("selectionPointer", false, function(cm, val) {
-    var data = cm.state.selectionPointer;
-    if (data) {
-      CodeMirror.off(cm.getWrapperElement(), "mousemove", data.mousemove);
-      CodeMirror.off(cm.getWrapperElement(), "mouseout", data.mouseout);
-      CodeMirror.off(window, "scroll", data.windowScroll);
-      cm.off("cursorActivity", reset);
-      cm.off("scroll", reset);
-      cm.state.selectionPointer = null;
-      cm.display.lineDiv.style.cursor = "";
-    }
-    if (val) {
-      data = cm.state.selectionPointer = {
-        value: typeof val == "string" ? val : "default",
-        mousemove: function(event) { mousemove(cm, event); },
-        mouseout: function(event) { mouseout(cm, event); },
-        windowScroll: function() { reset(cm); },
-        rects: null,
-        mouseX: null, mouseY: null,
-        willUpdate: false
-      };
-      CodeMirror.on(cm.getWrapperElement(), "mousemove", data.mousemove);
-      CodeMirror.on(cm.getWrapperElement(), "mouseout", data.mouseout);
-      CodeMirror.on(window, "scroll", data.windowScroll);
-      cm.on("cursorActivity", reset);
-      cm.on("scroll", reset);
-    }
-  });
-
-  function mousemove(cm, event) {
-    var data = cm.state.selectionPointer;
-    if (event.buttons == null ? event.which : event.buttons) {
-      data.mouseX = data.mouseY = null;
-    } else {
-      data.mouseX = event.clientX;
-      data.mouseY = event.clientY;
-    }
-    scheduleUpdate(cm);
-  }
-
-  function mouseout(cm, event) {
-    if (!cm.getWrapperElement().contains(event.relatedTarget)) {
-      var data = cm.state.selectionPointer;
-      data.mouseX = data.mouseY = null;
-      scheduleUpdate(cm);
-    }
-  }
-
-  function reset(cm) {
-    cm.state.selectionPointer.rects = null;
-    scheduleUpdate(cm);
-  }
-
-  function scheduleUpdate(cm) {
-    if (!cm.state.selectionPointer.willUpdate) {
-      cm.state.selectionPointer.willUpdate = true;
-      setTimeout(function() {
-        update(cm);
-        cm.state.selectionPointer.willUpdate = false;
-      }, 50);
-    }
-  }
-
-  function update(cm) {
-    var data = cm.state.selectionPointer;
-    if (!data) return;
-    if (data.rects == null && data.mouseX != null) {
-      data.rects = [];
-      if (cm.somethingSelected()) {
-        for (var sel = cm.display.selectionDiv.firstChild; sel; sel = sel.nextSibling)
-          data.rects.push(sel.getBoundingClientRect());
-      }
-    }
-    var inside = false;
-    if (data.mouseX != null) for (var i = 0; i < data.rects.length; i++) {
-      var rect = data.rects[i];
-      if (rect.left <= data.mouseX && rect.right >= data.mouseX &&
-          rect.top <= data.mouseY && rect.bottom >= data.mouseY)
-        inside = true;
-    }
-    var cursor = inside ? data.value : "";
-    if (cm.display.lineDiv.style.cursor != cursor)
-      cm.display.lineDiv.style.cursor = cursor;
-  }
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/simple.js b/plugins/UiFileManager/media/codemirror/extension/simple.js
deleted file mode 100644
index 655f9914..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/simple.js
+++ /dev/null
@@ -1,216 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  CodeMirror.defineSimpleMode = function(name, states) {
-    CodeMirror.defineMode(name, function(config) {
-      return CodeMirror.simpleMode(config, states);
-    });
-  };
-
-  CodeMirror.simpleMode = function(config, states) {
-    ensureState(states, "start");
-    var states_ = {}, meta = states.meta || {}, hasIndentation = false;
-    for (var state in states) if (state != meta && states.hasOwnProperty(state)) {
-      var list = states_[state] = [], orig = states[state];
-      for (var i = 0; i < orig.length; i++) {
-        var data = orig[i];
-        list.push(new Rule(data, states));
-        if (data.indent || data.dedent) hasIndentation = true;
-      }
-    }
-    var mode = {
-      startState: function() {
-        return {state: "start", pending: null,
-                local: null, localState: null,
-                indent: hasIndentation ? [] : null};
-      },
-      copyState: function(state) {
-        var s = {state: state.state, pending: state.pending,
-                 local: state.local, localState: null,
-                 indent: state.indent && state.indent.slice(0)};
-        if (state.localState)
-          s.localState = CodeMirror.copyState(state.local.mode, state.localState);
-        if (state.stack)
-          s.stack = state.stack.slice(0);
-        for (var pers = state.persistentStates; pers; pers = pers.next)
-          s.persistentStates = {mode: pers.mode,
-                                spec: pers.spec,
-                                state: pers.state == state.localState ? s.localState : CodeMirror.copyState(pers.mode, pers.state),
-                                next: s.persistentStates};
-        return s;
-      },
-      token: tokenFunction(states_, config),
-      innerMode: function(state) { return state.local && {mode: state.local.mode, state: state.localState}; },
-      indent: indentFunction(states_, meta)
-    };
-    if (meta) for (var prop in meta) if (meta.hasOwnProperty(prop))
-      mode[prop] = meta[prop];
-    return mode;
-  };
-
-  function ensureState(states, name) {
-    if (!states.hasOwnProperty(name))
-      throw new Error("Undefined state " + name + " in simple mode");
-  }
-
-  function toRegex(val, caret) {
-    if (!val) return /(?:)/;
-    var flags = "";
-    if (val instanceof RegExp) {
-      if (val.ignoreCase) flags = "i";
-      val = val.source;
-    } else {
-      val = String(val);
-    }
-    return new RegExp((caret === false ? "" : "^") + "(?:" + val + ")", flags);
-  }
-
-  function asToken(val) {
-    if (!val) return null;
-    if (val.apply) return val
-    if (typeof val == "string") return val.replace(/\./g, " ");
-    var result = [];
-    for (var i = 0; i < val.length; i++)
-      result.push(val[i] && val[i].replace(/\./g, " "));
-    return result;
-  }
-
-  function Rule(data, states) {
-    if (data.next || data.push) ensureState(states, data.next || data.push);
-    this.regex = toRegex(data.regex);
-    this.token = asToken(data.token);
-    this.data = data;
-  }
-
-  function tokenFunction(states, config) {
-    return function(stream, state) {
-      if (state.pending) {
-        var pend = state.pending.shift();
-        if (state.pending.length == 0) state.pending = null;
-        stream.pos += pend.text.length;
-        return pend.token;
-      }
-
-      if (state.local) {
-        if (state.local.end && stream.match(state.local.end)) {
-          var tok = state.local.endToken || null;
-          state.local = state.localState = null;
-          return tok;
-        } else {
-          var tok = state.local.mode.token(stream, state.localState), m;
-          if (state.local.endScan && (m = state.local.endScan.exec(stream.current())))
-            stream.pos = stream.start + m.index;
-          return tok;
-        }
-      }
-
-      var curState = states[state.state];
-      for (var i = 0; i < curState.length; i++) {
-        var rule = curState[i];
-        var matches = (!rule.data.sol || stream.sol()) && stream.match(rule.regex);
-        if (matches) {
-          if (rule.data.next) {
-            state.state = rule.data.next;
-          } else if (rule.data.push) {
-            (state.stack || (state.stack = [])).push(state.state);
-            state.state = rule.data.push;
-          } else if (rule.data.pop && state.stack && state.stack.length) {
-            state.state = state.stack.pop();
-          }
-
-          if (rule.data.mode)
-            enterLocalMode(config, state, rule.data.mode, rule.token);
-          if (rule.data.indent)
-            state.indent.push(stream.indentation() + config.indentUnit);
-          if (rule.data.dedent)
-            state.indent.pop();
-          var token = rule.token
-          if (token && token.apply) token = token(matches)
-          if (matches.length > 2 && rule.token && typeof rule.token != "string") {
-            state.pending = [];
-            for (var j = 2; j < matches.length; j++)
-              if (matches[j])
-                state.pending.push({text: matches[j], token: rule.token[j - 1]});
-            stream.backUp(matches[0].length - (matches[1] ? matches[1].length : 0));
-            return token[0];
-          } else if (token && token.join) {
-            return token[0];
-          } else {
-            return token;
-          }
-        }
-      }
-      stream.next();
-      return null;
-    };
-  }
-
-  function cmp(a, b) {
-    if (a === b) return true;
-    if (!a || typeof a != "object" || !b || typeof b != "object") return false;
-    var props = 0;
-    for (var prop in a) if (a.hasOwnProperty(prop)) {
-      if (!b.hasOwnProperty(prop) || !cmp(a[prop], b[prop])) return false;
-      props++;
-    }
-    for (var prop in b) if (b.hasOwnProperty(prop)) props--;
-    return props == 0;
-  }
-
-  function enterLocalMode(config, state, spec, token) {
-    var pers;
-    if (spec.persistent) for (var p = state.persistentStates; p && !pers; p = p.next)
-      if (spec.spec ? cmp(spec.spec, p.spec) : spec.mode == p.mode) pers = p;
-    var mode = pers ? pers.mode : spec.mode || CodeMirror.getMode(config, spec.spec);
-    var lState = pers ? pers.state : CodeMirror.startState(mode);
-    if (spec.persistent && !pers)
-      state.persistentStates = {mode: mode, spec: spec.spec, state: lState, next: state.persistentStates};
-
-    state.localState = lState;
-    state.local = {mode: mode,
-                   end: spec.end && toRegex(spec.end),
-                   endScan: spec.end && spec.forceEnd !== false && toRegex(spec.end, false),
-                   endToken: token && token.join ? token[token.length - 1] : token};
-  }
-
-  function indexOf(val, arr) {
-    for (var i = 0; i < arr.length; i++) if (arr[i] === val) return true;
-  }
-
-  function indentFunction(states, meta) {
-    return function(state, textAfter, line) {
-      if (state.local && state.local.mode.indent)
-        return state.local.mode.indent(state.localState, textAfter, line);
-      if (state.indent == null || state.local || meta.dontIndentStates && indexOf(state.state, meta.dontIndentStates) > -1)
-        return CodeMirror.Pass;
-
-      var pos = state.indent.length - 1, rules = states[state.state];
-      scan: for (;;) {
-        for (var i = 0; i < rules.length; i++) {
-          var rule = rules[i];
-          if (rule.data.dedent && rule.data.dedentIfLineStart !== false) {
-            var m = rule.regex.exec(textAfter);
-            if (m && m[0]) {
-              pos--;
-              if (rule.next || rule.push) rules = states[rule.next || rule.push];
-              textAfter = textAfter.slice(m[0].length);
-              continue scan;
-            }
-          }
-        }
-        break;
-      }
-      return pos < 0 ? 0 : state.indent[pos];
-    };
-  }
-});
diff --git a/plugins/UiFileManager/media/codemirror/extension/sublime.js b/plugins/UiFileManager/media/codemirror/extension/sublime.js
deleted file mode 100644
index 7edf172e..00000000
--- a/plugins/UiFileManager/media/codemirror/extension/sublime.js
+++ /dev/null
@@ -1,714 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-// A rough approximation of Sublime Text's keybindings
-// Depends on addon/search/searchcursor.js and optionally addon/dialog/dialogs.js
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../lib/codemirror"), require("../addon/search/searchcursor"), require("../addon/edit/matchbrackets"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../lib/codemirror", "../addon/search/searchcursor", "../addon/edit/matchbrackets"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  var cmds = CodeMirror.commands;
-  var Pos = CodeMirror.Pos;
-
-  // This is not exactly Sublime's algorithm. I couldn't make heads or tails of that.
-  function findPosSubword(doc, start, dir) {
-    if (dir < 0 && start.ch == 0) return doc.clipPos(Pos(start.line - 1));
-    var line = doc.getLine(start.line);
-    if (dir > 0 && start.ch >= line.length) return doc.clipPos(Pos(start.line + 1, 0));
-    var state = "start", type, startPos = start.ch;
-    for (var pos = startPos, e = dir < 0 ? 0 : line.length, i = 0; pos != e; pos += dir, i++) {
-      var next = line.charAt(dir < 0 ? pos - 1 : pos);
-      var cat = next != "_" && CodeMirror.isWordChar(next) ? "w" : "o";
-      if (cat == "w" && next.toUpperCase() == next) cat = "W";
-      if (state == "start") {
-        if (cat != "o") { state = "in"; type = cat; }
-        else startPos = pos + dir
-      } else if (state == "in") {
-        if (type != cat) {
-          if (type == "w" && cat == "W" && dir < 0) pos--;
-          if (type == "W" && cat == "w" && dir > 0) { // From uppercase to lowercase
-            if (pos == startPos + 1) { type = "w"; continue; }
-            else pos--;
-          }
-          break;
-        }
-      }
-    }
-    return Pos(start.line, pos);
-  }
-
-  function moveSubword(cm, dir) {
-    cm.extendSelectionsBy(function(range) {
-      if (cm.display.shift || cm.doc.extend || range.empty())
-        return findPosSubword(cm.doc, range.head, dir);
-      else
-        return dir < 0 ? range.from() : range.to();
-    });
-  }
-
-  cmds.goSubwordLeft = function(cm) { moveSubword(cm, -1); };
-  cmds.goSubwordRight = function(cm) { moveSubword(cm, 1); };
-
-  cmds.scrollLineUp = function(cm) {
-    var info = cm.getScrollInfo();
-    if (!cm.somethingSelected()) {
-      var visibleBottomLine = cm.lineAtHeight(info.top + info.clientHeight, "local");
-      if (cm.getCursor().line >= visibleBottomLine)
-        cm.execCommand("goLineUp");
-    }
-    cm.scrollTo(null, info.top - cm.defaultTextHeight());
-  };
-  cmds.scrollLineDown = function(cm) {
-    var info = cm.getScrollInfo();
-    if (!cm.somethingSelected()) {
-      var visibleTopLine = cm.lineAtHeight(info.top, "local")+1;
-      if (cm.getCursor().line <= visibleTopLine)
-        cm.execCommand("goLineDown");
-    }
-    cm.scrollTo(null, info.top + cm.defaultTextHeight());
-  };
-
-  cmds.splitSelectionByLine = function(cm) {
-    var ranges = cm.listSelections(), lineRanges = [];
-    for (var i = 0; i < ranges.length; i++) {
-      var from = ranges[i].from(), to = ranges[i].to();
-      for (var line = from.line; line <= to.line; ++line)
-        if (!(to.line > from.line && line == to.line && to.ch == 0))
-          lineRanges.push({anchor: line == from.line ? from : Pos(line, 0),
-                           head: line == to.line ? to : Pos(line)});
-    }
-    cm.setSelections(lineRanges, 0);
-  };
-
-  cmds.singleSelectionTop = function(cm) {
-    var range = cm.listSelections()[0];
-    cm.setSelection(range.anchor, range.head, {scroll: false});
-  };
-
-  cmds.selectLine = function(cm) {
-    var ranges = cm.listSelections(), extended = [];
-    for (var i = 0; i < ranges.length; i++) {
-      var range = ranges[i];
-      extended.push({anchor: Pos(range.from().line, 0),
-                     head: Pos(range.to().line + 1, 0)});
-    }
-    cm.setSelections(extended);
-  };
-
-  function insertLine(cm, above) {
-    if (cm.isReadOnly()) return CodeMirror.Pass
-    cm.operation(function() {
-      var len = cm.listSelections().length, newSelection = [], last = -1;
-      for (var i = 0; i < len; i++) {
-        var head = cm.listSelections()[i].head;
-        if (head.line <= last) continue;
-        var at = Pos(head.line + (above ? 0 : 1), 0);
-        cm.replaceRange("\n", at, null, "+insertLine");
-        cm.indentLine(at.line, null, true);
-        newSelection.push({head: at, anchor: at});
-        last = head.line + 1;
-      }
-      cm.setSelections(newSelection);
-    });
-    cm.execCommand("indentAuto");
-  }
-
-  cmds.insertLineAfter = function(cm) { return insertLine(cm, false); };
-
-  cmds.insertLineBefore = function(cm) { return insertLine(cm, true); };
-
-  function wordAt(cm, pos) {
-    var start = pos.ch, end = start, line = cm.getLine(pos.line);
-    while (start && CodeMirror.isWordChar(line.charAt(start - 1))) --start;
-    while (end < line.length && CodeMirror.isWordChar(line.charAt(end))) ++end;
-    return {from: Pos(pos.line, start), to: Pos(pos.line, end), word: line.slice(start, end)};
-  }
-
-  cmds.selectNextOccurrence = function(cm) {
-    var from = cm.getCursor("from"), to = cm.getCursor("to");
-    var fullWord = cm.state.sublimeFindFullWord == cm.doc.sel;
-    if (CodeMirror.cmpPos(from, to) == 0) {
-      var word = wordAt(cm, from);
-      if (!word.word) return;
-      cm.setSelection(word.from, word.to);
-      fullWord = true;
-    } else {
-      var text = cm.getRange(from, to);
-      var query = fullWord ? new RegExp("\\b" + text + "\\b") : text;
-      var cur = cm.getSearchCursor(query, to);
-      var found = cur.findNext();
-      if (!found) {
-        cur = cm.getSearchCursor(query, Pos(cm.firstLine(), 0));
-        found = cur.findNext();
-      }
-      if (!found || isSelectedRange(cm.listSelections(), cur.from(), cur.to())) return
-      cm.addSelection(cur.from(), cur.to());
-    }
-    if (fullWord)
-      cm.state.sublimeFindFullWord = cm.doc.sel;
-  };
-
-  cmds.skipAndSelectNextOccurrence = function(cm) {
-    var prevAnchor = cm.getCursor("anchor"), prevHead = cm.getCursor("head");
-    cmds.selectNextOccurrence(cm);
-    if (CodeMirror.cmpPos(prevAnchor, prevHead) != 0) {
-      cm.doc.setSelections(cm.doc.listSelections()
-          .filter(function (sel) {
-            return sel.anchor != prevAnchor || sel.head != prevHead;
-          }));
-    }
-  }
-
-  function addCursorToSelection(cm, dir) {
-    var ranges = cm.listSelections(), newRanges = [];
-    for (var i = 0; i < ranges.length; i++) {
-      var range = ranges[i];
-      var newAnchor = cm.findPosV(
-          range.anchor, dir, "line", range.anchor.goalColumn);
-      var newHead = cm.findPosV(
-          range.head, dir, "line", range.head.goalColumn);
-      newAnchor.goalColumn = range.anchor.goalColumn != null ?
-          range.anchor.goalColumn : cm.cursorCoords(range.anchor, "div").left;
-      newHead.goalColumn = range.head.goalColumn != null ?
-          range.head.goalColumn : cm.cursorCoords(range.head, "div").left;
-      var newRange = {anchor: newAnchor, head: newHead};
-      newRanges.push(range);
-      newRanges.push(newRange);
-    }
-    cm.setSelections(newRanges);
-  }
-  cmds.addCursorToPrevLine = function(cm) { addCursorToSelection(cm, -1); };
-  cmds.addCursorToNextLine = function(cm) { addCursorToSelection(cm, 1); };
-
-  function isSelectedRange(ranges, from, to) {
-    for (var i = 0; i < ranges.length; i++)
-      if (CodeMirror.cmpPos(ranges[i].from(), from) == 0 &&
-          CodeMirror.cmpPos(ranges[i].to(), to) == 0) return true
-    return false
-  }
-
-  var mirror = "(){}[]";
-  function selectBetweenBrackets(cm) {
-    var ranges = cm.listSelections(), newRanges = []
-    for (var i = 0; i < ranges.length; i++) {
-      var range = ranges[i], pos = range.head, opening = cm.scanForBracket(pos, -1);
-      if (!opening) return false;
-      for (;;) {
-        var closing = cm.scanForBracket(pos, 1);
-        if (!closing) return false;
-        if (closing.ch == mirror.charAt(mirror.indexOf(opening.ch) + 1)) {
-          var startPos = Pos(opening.pos.line, opening.pos.ch + 1);
-          if (CodeMirror.cmpPos(startPos, range.from()) == 0 &&
-              CodeMirror.cmpPos(closing.pos, range.to()) == 0) {
-            opening = cm.scanForBracket(opening.pos, -1);
-            if (!opening) return false;
-          } else {
-            newRanges.push({anchor: startPos, head: closing.pos});
-            break;
-          }
-        }
-        pos = Pos(closing.pos.line, closing.pos.ch + 1);
-      }
-    }
-    cm.setSelections(newRanges);
-    return true;
-  }
-
-  cmds.selectScope = function(cm) {
-    selectBetweenBrackets(cm) || cm.execCommand("selectAll");
-  };
-  cmds.selectBetweenBrackets = function(cm) {
-    if (!selectBetweenBrackets(cm)) return CodeMirror.Pass;
-  };
-
-  function puncType(type) {
-    return !type ? null : /\bpunctuation\b/.test(type) ? type : undefined
-  }
-
-  cmds.goToBracket = function(cm) {
-    cm.extendSelectionsBy(function(range) {
-      var next = cm.scanForBracket(range.head, 1, puncType(cm.getTokenTypeAt(range.head)));
-      if (next && CodeMirror.cmpPos(next.pos, range.head) != 0) return next.pos;
-      var prev = cm.scanForBracket(range.head, -1, puncType(cm.getTokenTypeAt(Pos(range.head.line, range.head.ch + 1))));
-      return prev && Pos(prev.pos.line, prev.pos.ch + 1) || range.head;
-    });
-  };
-
-  cmds.swapLineUp = function(cm) {
-    if (cm.isReadOnly()) return CodeMirror.Pass
-    var ranges = cm.listSelections(), linesToMove = [], at = cm.firstLine() - 1, newSels = [];
-    for (var i = 0; i < ranges.length; i++) {
-      var range = ranges[i], from = range.from().line - 1, to = range.to().line;
-      newSels.push({anchor: Pos(range.anchor.line - 1, range.anchor.ch),
-                    head: Pos(range.head.line - 1, range.head.ch)});
-      if (range.to().ch == 0 && !range.empty()) --to;
-      if (from > at) linesToMove.push(from, to);
-      else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to;
-      at = to;
-    }
-    cm.operation(function() {
-      for (var i = 0; i < linesToMove.length; i += 2) {
-        var from = linesToMove[i], to = linesToMove[i + 1];
-        var line = cm.getLine(from);
-        cm.replaceRange("", Pos(from, 0), Pos(from + 1, 0), "+swapLine");
-        if (to > cm.lastLine())
-          cm.replaceRange("\n" + line, Pos(cm.lastLine()), null, "+swapLine");
-        else
-          cm.replaceRange(line + "\n", Pos(to, 0), null, "+swapLine");
-      }
-      cm.setSelections(newSels);
-      cm.scrollIntoView();
-    });
-  };
-
-  cmds.swapLineDown = function(cm) {
-    if (cm.isReadOnly()) return CodeMirror.Pass
-    var ranges = cm.listSelections(), linesToMove = [], at = cm.lastLine() + 1;
-    for (var i = ranges.length - 1; i >= 0; i--) {
-      var range = ranges[i], from = range.to().line + 1, to = range.from().line;
-      if (range.to().ch == 0 && !range.empty()) from--;
-      if (from < at) linesToMove.push(from, to);
-      else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to;
-      at = to;
-    }
-    cm.operation(function() {
-      for (var i = linesToMove.length - 2; i >= 0; i -= 2) {
-        var from = linesToMove[i], to = linesToMove[i + 1];
-        var line = cm.getLine(from);
-        if (from == cm.lastLine())
-          cm.replaceRange("", Pos(from - 1), Pos(from), "+swapLine");
-        else
-          cm.replaceRange("", Pos(from, 0), Pos(from + 1, 0), "+swapLine");
-        cm.replaceRange(line + "\n", Pos(to, 0), null, "+swapLine");
-      }
-      cm.scrollIntoView();
-    });
-  };
-
-  cmds.toggleCommentIndented = function(cm) {
-    cm.toggleComment({ indent: true });
-  }
-
-  cmds.joinLines = function(cm) {
-    var ranges = cm.listSelections(), joined = [];
-    for (var i = 0; i < ranges.length; i++) {
-      var range = ranges[i], from = range.from();
-      var start = from.line, end = range.to().line;
-      while (i < ranges.length - 1 && ranges[i + 1].from().line == end)
-        end = ranges[++i].to().line;
-      joined.push({start: start, end: end, anchor: !range.empty() && from});
-    }
-    cm.operation(function() {
-      var offset = 0, ranges = [];
-      for (var i = 0; i < joined.length; i++) {
-        var obj = joined[i];
-        var anchor = obj.anchor && Pos(obj.anchor.line - offset, obj.anchor.ch), head;
-        for (var line = obj.start; line <= obj.end; line++) {
-          var actual = line - offset;
-          if (line == obj.end) head = Pos(actual, cm.getLine(actual).length + 1);
-          if (actual < cm.lastLine()) {
-            cm.replaceRange(" ", Pos(actual), Pos(actual + 1, /^\s*/.exec(cm.getLine(actual + 1))[0].length));
-            ++offset;
-          }
-        }
-        ranges.push({anchor: anchor || head, head: head});
-      }
-      cm.setSelections(ranges, 0);
-    });
-  };
-
-  cmds.duplicateLine = function(cm) {
-    cm.operation(function() {
-      var rangeCount = cm.listSelections().length;
-      for (var i = 0; i < rangeCount; i++) {
-        var range = cm.listSelections()[i];
-        if (range.empty())
-          cm.replaceRange(cm.getLine(range.head.line) + "\n", Pos(range.head.line, 0));
-        else
-          cm.replaceRange(cm.getRange(range.from(), range.to()), range.from());
-      }
-      cm.scrollIntoView();
-    });
-  };
-
-
-  function sortLines(cm, caseSensitive) {
-    if (cm.isReadOnly()) return CodeMirror.Pass
-    var ranges = cm.listSelections(), toSort = [], selected;
-    for (var i = 0; i < ranges.length; i++) {
-      var range = ranges[i];
-      if (range.empty()) continue;
-      var from = range.from().line, to = range.to().line;
-      while (i < ranges.length - 1 && ranges[i + 1].from().line == to)
-        to = ranges[++i].to().line;
-      if (!ranges[i].to().ch) to--;
-      toSort.push(from, to);
-    }
-    if (toSort.length) selected = true;
-    else toSort.push(cm.firstLine(), cm.lastLine());
-
-    cm.operation(function() {
-      var ranges = [];
-      for (var i = 0; i < toSort.length; i += 2) {
-        var from = toSort[i], to = toSort[i + 1];
-        var start = Pos(from, 0), end = Pos(to);
-        var lines = cm.getRange(start, end, false);
-        if (caseSensitive)
-          lines.sort();
-        else
-          lines.sort(function(a, b) {
-            var au = a.toUpperCase(), bu = b.toUpperCase();
-            if (au != bu) { a = au; b = bu; }
-            return a < b ? -1 : a == b ? 0 : 1;
-          });
-        cm.replaceRange(lines, start, end);
-        if (selected) ranges.push({anchor: start, head: Pos(to + 1, 0)});
-      }
-      if (selected) cm.setSelections(ranges, 0);
-    });
-  }
-
-  cmds.sortLines = function(cm) { sortLines(cm, true); };
-  cmds.sortLinesInsensitive = function(cm) { sortLines(cm, false); };
-
-  cmds.nextBookmark = function(cm) {
-    var marks = cm.state.sublimeBookmarks;
-    if (marks) while (marks.length) {
-      var current = marks.shift();
-      var found = current.find();
-      if (found) {
-        marks.push(current);
-        return cm.setSelection(found.from, found.to);
-      }
-    }
-  };
-
-  cmds.prevBookmark = function(cm) {
-    var marks = cm.state.sublimeBookmarks;
-    if (marks) while (marks.length) {
-      marks.unshift(marks.pop());
-      var found = marks[marks.length - 1].find();
-      if (!found)
-        marks.pop();
-      else
-        return cm.setSelection(found.from, found.to);
-    }
-  };
-
-  cmds.toggleBookmark = function(cm) {
-    var ranges = cm.listSelections();
-    var marks = cm.state.sublimeBookmarks || (cm.state.sublimeBookmarks = []);
-    for (var i = 0; i < ranges.length; i++) {
-      var from = ranges[i].from(), to = ranges[i].to();
-      var found = ranges[i].empty() ? cm.findMarksAt(from) : cm.findMarks(from, to);
-      for (var j = 0; j < found.length; j++) {
-        if (found[j].sublimeBookmark) {
-          found[j].clear();
-          for (var k = 0; k < marks.length; k++)
-            if (marks[k] == found[j])
-              marks.splice(k--, 1);
-          break;
-        }
-      }
-      if (j == found.length)
-        marks.push(cm.markText(from, to, {sublimeBookmark: true, clearWhenEmpty: false}));
-    }
-  };
-
-  cmds.clearBookmarks = function(cm) {
-    var marks = cm.state.sublimeBookmarks;
-    if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear();
-    marks.length = 0;
-  };
-
-  cmds.selectBookmarks = function(cm) {
-    var marks = cm.state.sublimeBookmarks, ranges = [];
-    if (marks) for (var i = 0; i < marks.length; i++) {
-      var found = marks[i].find();
-      if (!found)
-        marks.splice(i--, 0);
-      else
-        ranges.push({anchor: found.from, head: found.to});
-    }
-    if (ranges.length)
-      cm.setSelections(ranges, 0);
-  };
-
-  function modifyWordOrSelection(cm, mod) {
-    cm.operation(function() {
-      var ranges = cm.listSelections(), indices = [], replacements = [];
-      for (var i = 0; i < ranges.length; i++) {
-        var range = ranges[i];
-        if (range.empty()) { indices.push(i); replacements.push(""); }
-        else replacements.push(mod(cm.getRange(range.from(), range.to())));
-      }
-      cm.replaceSelections(replacements, "around", "case");
-      for (var i = indices.length - 1, at; i >= 0; i--) {
-        var range = ranges[indices[i]];
-        if (at && CodeMirror.cmpPos(range.head, at) > 0) continue;
-        var word = wordAt(cm, range.head);
-        at = word.from;
-        cm.replaceRange(mod(word.word), word.from, word.to);
-      }
-    });
-  }
-
-  cmds.smartBackspace = function(cm) {
-    if (cm.somethingSelected()) return CodeMirror.Pass;
-
-    cm.operation(function() {
-      var cursors = cm.listSelections();
-      var indentUnit = cm.getOption("indentUnit");
-
-      for (var i = cursors.length - 1; i >= 0; i--) {
-        var cursor = cursors[i].head;
-        var toStartOfLine = cm.getRange({line: cursor.line, ch: 0}, cursor);
-        var column = CodeMirror.countColumn(toStartOfLine, null, cm.getOption("tabSize"));
-
-        // Delete by one character by default
-        var deletePos = cm.findPosH(cursor, -1, "char", false);
-
-        if (toStartOfLine && !/\S/.test(toStartOfLine) && column % indentUnit == 0) {
-          var prevIndent = new Pos(cursor.line,
-            CodeMirror.findColumn(toStartOfLine, column - indentUnit, indentUnit));
-
-          // Smart delete only if we found a valid prevIndent location
-          if (prevIndent.ch != cursor.ch) deletePos = prevIndent;
-        }
-
-        cm.replaceRange("", deletePos, cursor, "+delete");
-      }
-    });
-  };
-
-  cmds.delLineRight = function(cm) {
-    cm.operation(function() {
-      var ranges = cm.listSelections();
-      for (var i = ranges.length - 1; i >= 0; i--)
-        cm.replaceRange("", ranges[i].anchor, Pos(ranges[i].to().line), "+delete");
-      cm.scrollIntoView();
-    });
-  };
-
-  cmds.upcaseAtCursor = function(cm) {
-    modifyWordOrSelection(cm, function(str) { return str.toUpperCase(); });
-  };
-  cmds.downcaseAtCursor = function(cm) {
-    modifyWordOrSelection(cm, function(str) { return str.toLowerCase(); });
-  };
-
-  cmds.setSublimeMark = function(cm) {
-    if (cm.state.sublimeMark) cm.state.sublimeMark.clear();
-    cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
-  };
-  cmds.selectToSublimeMark = function(cm) {
-    var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
-    if (found) cm.setSelection(cm.getCursor(), found);
-  };
-  cmds.deleteToSublimeMark = function(cm) {
-    var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
-    if (found) {
-      var from = cm.getCursor(), to = found;
-      if (CodeMirror.cmpPos(from, to) > 0) { var tmp = to; to = from; from = tmp; }
-      cm.state.sublimeKilled = cm.getRange(from, to);
-      cm.replaceRange("", from, to);
-    }
-  };
-  cmds.swapWithSublimeMark = function(cm) {
-    var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
-    if (found) {
-      cm.state.sublimeMark.clear();
-      cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
-      cm.setCursor(found);
-    }
-  };
-  cmds.sublimeYank = function(cm) {
-    if (cm.state.sublimeKilled != null)
-      cm.replaceSelection(cm.state.sublimeKilled, null, "paste");
-  };
-
-  cmds.showInCenter = function(cm) {
-    var pos = cm.cursorCoords(null, "local");
-    cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2);
-  };
-
-  function getTarget(cm) {
-    var from = cm.getCursor("from"), to = cm.getCursor("to");
-    if (CodeMirror.cmpPos(from, to) == 0) {
-      var word = wordAt(cm, from);
-      if (!word.word) return;
-      from = word.from;
-      to = word.to;
-    }
-    return {from: from, to: to, query: cm.getRange(from, to), word: word};
-  }
-
-  function findAndGoTo(cm, forward) {
-    var target = getTarget(cm);
-    if (!target) return;
-    var query = target.query;
-    var cur = cm.getSearchCursor(query, forward ? target.to : target.from);
-
-    if (forward ? cur.findNext() : cur.findPrevious()) {
-      cm.setSelection(cur.from(), cur.to());
-    } else {
-      cur = cm.getSearchCursor(query, forward ? Pos(cm.firstLine(), 0)
-                                              : cm.clipPos(Pos(cm.lastLine())));
-      if (forward ? cur.findNext() : cur.findPrevious())
-        cm.setSelection(cur.from(), cur.to());
-      else if (target.word)
-        cm.setSelection(target.from, target.to);
-    }
-  };
-  cmds.findUnder = function(cm) { findAndGoTo(cm, true); };
-  cmds.findUnderPrevious = function(cm) { findAndGoTo(cm,false); };
-  cmds.findAllUnder = function(cm) {
-    var target = getTarget(cm);
-    if (!target) return;
-    var cur = cm.getSearchCursor(target.query);
-    var matches = [];
-    var primaryIndex = -1;
-    while (cur.findNext()) {
-      matches.push({anchor: cur.from(), head: cur.to()});
-      if (cur.from().line <= target.from.line && cur.from().ch <= target.from.ch)
-        primaryIndex++;
-    }
-    cm.setSelections(matches, primaryIndex);
-  };
-
-
-  var keyMap = CodeMirror.keyMap;
-  keyMap.macSublime = {
-    "Cmd-Left": "goLineStartSmart",
-    "Shift-Tab": "indentLess",
-    "Shift-Ctrl-K": "deleteLine",
-    "Alt-Q": "wrapLines",
-    "Ctrl-Left": "goSubwordLeft",
-    "Ctrl-Right": "goSubwordRight",
-    "Ctrl-Alt-Up": "scrollLineUp",
-    "Ctrl-Alt-Down": "scrollLineDown",
-    "Cmd-L": "selectLine",
-    "Shift-Cmd-L": "splitSelectionByLine",
-    "Esc": "singleSelectionTop",
-    "Cmd-Enter": "insertLineAfter",
-    "Shift-Cmd-Enter": "insertLineBefore",
-    "Cmd-D": "selectNextOccurrence",
-    "Shift-Cmd-Space": "selectScope",
-    "Shift-Cmd-M": "selectBetweenBrackets",
-    "Cmd-M": "goToBracket",
-    "Cmd-Ctrl-Up": "swapLineUp",
-    "Cmd-Ctrl-Down": "swapLineDown",
-    "Cmd-/": "toggleCommentIndented",
-    "Cmd-J": "joinLines",
-    "Shift-Cmd-D": "duplicateLine",
-    "F5": "sortLines",
-    "Cmd-F5": "sortLinesInsensitive",
-    "F2": "nextBookmark",
-    "Shift-F2": "prevBookmark",
-    "Cmd-F2": "toggleBookmark",
-    "Shift-Cmd-F2": "clearBookmarks",
-    "Alt-F2": "selectBookmarks",
-    "Backspace": "smartBackspace",
-    "Cmd-K Cmd-D": "skipAndSelectNextOccurrence",
-    "Cmd-K Cmd-K": "delLineRight",
-    "Cmd-K Cmd-U": "upcaseAtCursor",
-    "Cmd-K Cmd-L": "downcaseAtCursor",
-    "Cmd-K Cmd-Space": "setSublimeMark",
-    "Cmd-K Cmd-A": "selectToSublimeMark",
-    "Cmd-K Cmd-W": "deleteToSublimeMark",
-    "Cmd-K Cmd-X": "swapWithSublimeMark",
-    "Cmd-K Cmd-Y": "sublimeYank",
-    "Cmd-K Cmd-C": "showInCenter",
-    "Cmd-K Cmd-G": "clearBookmarks",
-    "Cmd-K Cmd-Backspace": "delLineLeft",
-    "Cmd-K Cmd-1": "foldAll",
-    "Cmd-K Cmd-0": "unfoldAll",
-    "Cmd-K Cmd-J": "unfoldAll",
-    "Ctrl-Shift-Up": "addCursorToPrevLine",
-    "Ctrl-Shift-Down": "addCursorToNextLine",
-    "Cmd-F3": "findUnder",
-    "Shift-Cmd-F3": "findUnderPrevious",
-    "Alt-F3": "findAllUnder",
-    "Shift-Cmd-[": "fold",
-    "Shift-Cmd-]": "unfold",
-    "Cmd-I": "findIncremental",
-    "Shift-Cmd-I": "findIncrementalReverse",
-    "Cmd-H": "replace",
-    "F3": "findNext",
-    "Shift-F3": "findPrev",
-    "fallthrough": "macDefault"
-  };
-  CodeMirror.normalizeKeyMap(keyMap.macSublime);
-
-  keyMap.pcSublime = {
-    "Shift-Tab": "indentLess",
-    "Shift-Ctrl-K": "deleteLine",
-    "Alt-Q": "wrapLines",
-    "Ctrl-T": "transposeChars",
-    "Alt-Left": "goSubwordLeft",
-    "Alt-Right": "goSubwordRight",
-    "Ctrl-Up": "scrollLineUp",
-    "Ctrl-Down": "scrollLineDown",
-    "Ctrl-L": "selectLine",
-    "Shift-Ctrl-L": "splitSelectionByLine",
-    "Esc": "singleSelectionTop",
-    "Ctrl-Enter": "insertLineAfter",
-    "Shift-Ctrl-Enter": "insertLineBefore",
-    "Ctrl-D": "selectNextOccurrence",
-    "Shift-Ctrl-Space": "selectScope",
-    "Shift-Ctrl-M": "selectBetweenBrackets",
-    "Ctrl-M": "goToBracket",
-    "Shift-Ctrl-Up": "swapLineUp",
-    "Shift-Ctrl-Down": "swapLineDown",
-    "Ctrl-/": "toggleCommentIndented",
-    "Ctrl-J": "joinLines",
-    "Shift-Ctrl-D": "duplicateLine",
-    "F9": "sortLines",
-    "Ctrl-F9": "sortLinesInsensitive",
-    "F2": "nextBookmark",
-    "Shift-F2": "prevBookmark",
-    "Ctrl-F2": "toggleBookmark",
-    "Shift-Ctrl-F2": "clearBookmarks",
-    "Alt-F2": "selectBookmarks",
-    "Backspace": "smartBackspace",
-    "Ctrl-K Ctrl-D": "skipAndSelectNextOccurrence",
-    "Ctrl-K Ctrl-K": "delLineRight",
-    "Ctrl-K Ctrl-U": "upcaseAtCursor",
-    "Ctrl-K Ctrl-L": "downcaseAtCursor",
-    "Ctrl-K Ctrl-Space": "setSublimeMark",
-    "Ctrl-K Ctrl-A": "selectToSublimeMark",
-    "Ctrl-K Ctrl-W": "deleteToSublimeMark",
-    "Ctrl-K Ctrl-X": "swapWithSublimeMark",
-    "Ctrl-K Ctrl-Y": "sublimeYank",
-    "Ctrl-K Ctrl-C": "showInCenter",
-    "Ctrl-K Ctrl-G": "clearBookmarks",
-    "Ctrl-K Ctrl-Backspace": "delLineLeft",
-    "Ctrl-K Ctrl-1": "foldAll",
-    "Ctrl-K Ctrl-0": "unfoldAll",
-    "Ctrl-K Ctrl-J": "unfoldAll",
-    "Ctrl-Alt-Up": "addCursorToPrevLine",
-    "Ctrl-Alt-Down": "addCursorToNextLine",
-    "Ctrl-F3": "findUnder",
-    "Shift-Ctrl-F3": "findUnderPrevious",
-    "Alt-F3": "findAllUnder",
-    "Shift-Ctrl-[": "fold",
-    "Shift-Ctrl-]": "unfold",
-    "Ctrl-I": "findIncremental",
-    "Shift-Ctrl-I": "findIncrementalReverse",
-    "Ctrl-H": "replace",
-    "F3": "findNext",
-    "Shift-F3": "findPrev",
-    "fallthrough": "pcDefault"
-  };
-  CodeMirror.normalizeKeyMap(keyMap.pcSublime);
-
-  var mac = keyMap.default == keyMap.macDefault;
-  keyMap.sublime = mac ? keyMap.macSublime : keyMap.pcSublime;
-});
diff --git a/plugins/UiFileManager/media/codemirror/mode/coffeescript.js b/plugins/UiFileManager/media/codemirror/mode/coffeescript.js
deleted file mode 100644
index a54e9d5e..00000000
--- a/plugins/UiFileManager/media/codemirror/mode/coffeescript.js
+++ /dev/null
@@ -1,359 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-/**
- * Link to the project's GitHub page:
- * https://github.com/pickhardt/coffeescript-codemirror-mode
- */
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-CodeMirror.defineMode("coffeescript", function(conf, parserConf) {
-  var ERRORCLASS = "error";
-
-  function wordRegexp(words) {
-    return new RegExp("^((" + words.join(")|(") + "))\\b");
-  }
-
-  var operators = /^(?:->|=>|\+[+=]?|-[\-=]?|\*[\*=]?|\/[\/=]?|[=!]=|<[><]?=?|>>?=?|%=?|&=?|\|=?|\^=?|\~|!|\?|(or|and|\|\||&&|\?)=)/;
-  var delimiters = /^(?:[()\[\]{},:`=;]|\.\.?\.?)/;
-  var identifiers = /^[_A-Za-z$][_A-Za-z$0-9]*/;
-  var atProp = /^@[_A-Za-z$][_A-Za-z$0-9]*/;
-
-  var wordOperators = wordRegexp(["and", "or", "not",
-                                  "is", "isnt", "in",
-                                  "instanceof", "typeof"]);
-  var indentKeywords = ["for", "while", "loop", "if", "unless", "else",
-                        "switch", "try", "catch", "finally", "class"];
-  var commonKeywords = ["break", "by", "continue", "debugger", "delete",
-                        "do", "in", "of", "new", "return", "then",
-                        "this", "@", "throw", "when", "until", "extends"];
-
-  var keywords = wordRegexp(indentKeywords.concat(commonKeywords));
-
-  indentKeywords = wordRegexp(indentKeywords);
-
-
-  var stringPrefixes = /^('{3}|\"{3}|['\"])/;
-  var regexPrefixes = /^(\/{3}|\/)/;
-  var commonConstants = ["Infinity", "NaN", "undefined", "null", "true", "false", "on", "off", "yes", "no"];
-  var constants = wordRegexp(commonConstants);
-
-  // Tokenizers
-  function tokenBase(stream, state) {
-    // Handle scope changes
-    if (stream.sol()) {
-      if (state.scope.align === null) state.scope.align = false;
-      var scopeOffset = state.scope.offset;
-      if (stream.eatSpace()) {
-        var lineOffset = stream.indentation();
-        if (lineOffset > scopeOffset && state.scope.type == "coffee") {
-          return "indent";
-        } else if (lineOffset < scopeOffset) {
-          return "dedent";
-        }
-        return null;
-      } else {
-        if (scopeOffset > 0) {
-          dedent(stream, state);
-        }
-      }
-    }
-    if (stream.eatSpace()) {
-      return null;
-    }
-
-    var ch = stream.peek();
-
-    // Handle docco title comment (single line)
-    if (stream.match("####")) {
-      stream.skipToEnd();
-      return "comment";
-    }
-
-    // Handle multi line comments
-    if (stream.match("###")) {
-      state.tokenize = longComment;
-      return state.tokenize(stream, state);
-    }
-
-    // Single line comment
-    if (ch === "#") {
-      stream.skipToEnd();
-      return "comment";
-    }
-
-    // Handle number literals
-    if (stream.match(/^-?[0-9\.]/, false)) {
-      var floatLiteral = false;
-      // Floats
-      if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) {
-        floatLiteral = true;
-      }
-      if (stream.match(/^-?\d+\.\d*/)) {
-        floatLiteral = true;
-      }
-      if (stream.match(/^-?\.\d+/)) {
-        floatLiteral = true;
-      }
-
-      if (floatLiteral) {
-        // prevent from getting extra . on 1..
-        if (stream.peek() == "."){
-          stream.backUp(1);
-        }
-        return "number";
-      }
-      // Integers
-      var intLiteral = false;
-      // Hex
-      if (stream.match(/^-?0x[0-9a-f]+/i)) {
-        intLiteral = true;
-      }
-      // Decimal
-      if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) {
-        intLiteral = true;
-      }
-      // Zero by itself with no other piece of number.
-      if (stream.match(/^-?0(?![\dx])/i)) {
-        intLiteral = true;
-      }
-      if (intLiteral) {
-        return "number";
-      }
-    }
-
-    // Handle strings
-    if (stream.match(stringPrefixes)) {
-      state.tokenize = tokenFactory(stream.current(), false, "string");
-      return state.tokenize(stream, state);
-    }
-    // Handle regex literals
-    if (stream.match(regexPrefixes)) {
-      if (stream.current() != "/" || stream.match(/^.*\//, false)) { // prevent highlight of division
-        state.tokenize = tokenFactory(stream.current(), true, "string-2");
-        return state.tokenize(stream, state);
-      } else {
-        stream.backUp(1);
-      }
-    }
-
-
-
-    // Handle operators and delimiters
-    if (stream.match(operators) || stream.match(wordOperators)) {
-      return "operator";
-    }
-    if (stream.match(delimiters)) {
-      return "punctuation";
-    }
-
-    if (stream.match(constants)) {
-      return "atom";
-    }
-
-    if (stream.match(atProp) || state.prop && stream.match(identifiers)) {
-      return "property";
-    }
-
-    if (stream.match(keywords)) {
-      return "keyword";
-    }
-
-    if (stream.match(identifiers)) {
-      return "variable";
-    }
-
-    // Handle non-detected items
-    stream.next();
-    return ERRORCLASS;
-  }
-
-  function tokenFactory(delimiter, singleline, outclass) {
-    return function(stream, state) {
-      while (!stream.eol()) {
-        stream.eatWhile(/[^'"\/\\]/);
-        if (stream.eat("\\")) {
-          stream.next();
-          if (singleline && stream.eol()) {
-            return outclass;
-          }
-        } else if (stream.match(delimiter)) {
-          state.tokenize = tokenBase;
-          return outclass;
-        } else {
-          stream.eat(/['"\/]/);
-        }
-      }
-      if (singleline) {
-        if (parserConf.singleLineStringErrors) {
-          outclass = ERRORCLASS;
-        } else {
-          state.tokenize = tokenBase;
-        }
-      }
-      return outclass;
-    };
-  }
-
-  function longComment(stream, state) {
-    while (!stream.eol()) {
-      stream.eatWhile(/[^#]/);
-      if (stream.match("###")) {
-        state.tokenize = tokenBase;
-        break;
-      }
-      stream.eatWhile("#");
-    }
-    return "comment";
-  }
-
-  function indent(stream, state, type) {
-    type = type || "coffee";
-    var offset = 0, align = false, alignOffset = null;
-    for (var scope = state.scope; scope; scope = scope.prev) {
-      if (scope.type === "coffee" || scope.type == "}") {
-        offset = scope.offset + conf.indentUnit;
-        break;
-      }
-    }
-    if (type !== "coffee") {
-      align = null;
-      alignOffset = stream.column() + stream.current().length;
-    } else if (state.scope.align) {
-      state.scope.align = false;
-    }
-    state.scope = {
-      offset: offset,
-      type: type,
-      prev: state.scope,
-      align: align,
-      alignOffset: alignOffset
-    };
-  }
-
-  function dedent(stream, state) {
-    if (!state.scope.prev) return;
-    if (state.scope.type === "coffee") {
-      var _indent = stream.indentation();
-      var matched = false;
-      for (var scope = state.scope; scope; scope = scope.prev) {
-        if (_indent === scope.offset) {
-          matched = true;
-          break;
-        }
-      }
-      if (!matched) {
-        return true;
-      }
-      while (state.scope.prev && state.scope.offset !== _indent) {
-        state.scope = state.scope.prev;
-      }
-      return false;
-    } else {
-      state.scope = state.scope.prev;
-      return false;
-    }
-  }
-
-  function tokenLexer(stream, state) {
-    var style = state.tokenize(stream, state);
-    var current = stream.current();
-
-    // Handle scope changes.
-    if (current === "return") {
-      state.dedent = true;
-    }
-    if (((current === "->" || current === "=>") && stream.eol())
-        || style === "indent") {
-      indent(stream, state);
-    }
-    var delimiter_index = "[({".indexOf(current);
-    if (delimiter_index !== -1) {
-      indent(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
-    }
-    if (indentKeywords.exec(current)){
-      indent(stream, state);
-    }
-    if (current == "then"){
-      dedent(stream, state);
-    }
-
-
-    if (style === "dedent") {
-      if (dedent(stream, state)) {
-        return ERRORCLASS;
-      }
-    }
-    delimiter_index = "])}".indexOf(current);
-    if (delimiter_index !== -1) {
-      while (state.scope.type == "coffee" && state.scope.prev)
-        state.scope = state.scope.prev;
-      if (state.scope.type == current)
-        state.scope = state.scope.prev;
-    }
-    if (state.dedent && stream.eol()) {
-      if (state.scope.type == "coffee" && state.scope.prev)
-        state.scope = state.scope.prev;
-      state.dedent = false;
-    }
-
-    return style;
-  }
-
-  var external = {
-    startState: function(basecolumn) {
-      return {
-        tokenize: tokenBase,
-        scope: {offset:basecolumn || 0, type:"coffee", prev: null, align: false},
-        prop: false,
-        dedent: 0
-      };
-    },
-
-    token: function(stream, state) {
-      var fillAlign = state.scope.align === null && state.scope;
-      if (fillAlign && stream.sol()) fillAlign.align = false;
-
-      var style = tokenLexer(stream, state);
-      if (style && style != "comment") {
-        if (fillAlign) fillAlign.align = true;
-        state.prop = style == "punctuation" && stream.current() == "."
-      }
-
-      return style;
-    },
-
-    indent: function(state, text) {
-      if (state.tokenize != tokenBase) return 0;
-      var scope = state.scope;
-      var closer = text && "])}".indexOf(text.charAt(0)) > -1;
-      if (closer) while (scope.type == "coffee" && scope.prev) scope = scope.prev;
-      var closes = closer && scope.type === text.charAt(0);
-      if (scope.align)
-        return scope.alignOffset - (closes ? 1 : 0);
-      else
-        return (closes ? scope.prev : scope).offset;
-    },
-
-    lineComment: "#",
-    fold: "indent"
-  };
-  return external;
-});
-
-// IANA registered media type
-// https://www.iana.org/assignments/media-types/
-CodeMirror.defineMIME("application/vnd.coffeescript", "coffeescript");
-
-CodeMirror.defineMIME("text/x-coffeescript", "coffeescript");
-CodeMirror.defineMIME("text/coffeescript", "coffeescript");
-
-});
diff --git a/plugins/UiFileManager/media/codemirror/mode/css.js b/plugins/UiFileManager/media/codemirror/mode/css.js
deleted file mode 100644
index 441ba4ab..00000000
--- a/plugins/UiFileManager/media/codemirror/mode/css.js
+++ /dev/null
@@ -1,860 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-CodeMirror.defineMode("css", function(config, parserConfig) {
-  var inline = parserConfig.inline
-  if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
-
-  var indentUnit = config.indentUnit,
-      tokenHooks = parserConfig.tokenHooks,
-      documentTypes = parserConfig.documentTypes || {},
-      mediaTypes = parserConfig.mediaTypes || {},
-      mediaFeatures = parserConfig.mediaFeatures || {},
-      mediaValueKeywords = parserConfig.mediaValueKeywords || {},
-      propertyKeywords = parserConfig.propertyKeywords || {},
-      nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
-      fontProperties = parserConfig.fontProperties || {},
-      counterDescriptors = parserConfig.counterDescriptors || {},
-      colorKeywords = parserConfig.colorKeywords || {},
-      valueKeywords = parserConfig.valueKeywords || {},
-      allowNested = parserConfig.allowNested,
-      lineComment = parserConfig.lineComment,
-      supportsAtComponent = parserConfig.supportsAtComponent === true;
-
-  var type, override;
-  function ret(style, tp) { type = tp; return style; }
-
-  // Tokenizers
-
-  function tokenBase(stream, state) {
-    var ch = stream.next();
-    if (tokenHooks[ch]) {
-      var result = tokenHooks[ch](stream, state);
-      if (result !== false) return result;
-    }
-    if (ch == "@") {
-      stream.eatWhile(/[\w\\\-]/);
-      return ret("def", stream.current());
-    } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
-      return ret(null, "compare");
-    } else if (ch == "\"" || ch == "'") {
-      state.tokenize = tokenString(ch);
-      return state.tokenize(stream, state);
-    } else if (ch == "#") {
-      stream.eatWhile(/[\w\\\-]/);
-      return ret("atom", "hash");
-    } else if (ch == "!") {
-      stream.match(/^\s*\w*/);
-      return ret("keyword", "important");
-    } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
-      stream.eatWhile(/[\w.%]/);
-      return ret("number", "unit");
-    } else if (ch === "-") {
-      if (/[\d.]/.test(stream.peek())) {
-        stream.eatWhile(/[\w.%]/);
-        return ret("number", "unit");
-      } else if (stream.match(/^-[\w\\\-]*/)) {
-        stream.eatWhile(/[\w\\\-]/);
-        if (stream.match(/^\s*:/, false))
-          return ret("variable-2", "variable-definition");
-        return ret("variable-2", "variable");
-      } else if (stream.match(/^\w+-/)) {
-        return ret("meta", "meta");
-      }
-    } else if (/[,+>*\/]/.test(ch)) {
-      return ret(null, "select-op");
-    } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
-      return ret("qualifier", "qualifier");
-    } else if (/[:;{}\[\]\(\)]/.test(ch)) {
-      return ret(null, ch);
-    } else if (stream.match(/[\w-.]+(?=\()/)) {
-      if (/^(url(-prefix)?|domain|regexp)$/.test(stream.current().toLowerCase())) {
-        state.tokenize = tokenParenthesized;
-      }
-      return ret("variable callee", "variable");
-    } else if (/[\w\\\-]/.test(ch)) {
-      stream.eatWhile(/[\w\\\-]/);
-      return ret("property", "word");
-    } else {
-      return ret(null, null);
-    }
-  }
-
-  function tokenString(quote) {
-    return function(stream, state) {
-      var escaped = false, ch;
-      while ((ch = stream.next()) != null) {
-        if (ch == quote && !escaped) {
-          if (quote == ")") stream.backUp(1);
-          break;
-        }
-        escaped = !escaped && ch == "\\";
-      }
-      if (ch == quote || !escaped && quote != ")") state.tokenize = null;
-      return ret("string", "string");
-    };
-  }
-
-  function tokenParenthesized(stream, state) {
-    stream.next(); // Must be '('
-    if (!stream.match(/\s*[\"\')]/, false))
-      state.tokenize = tokenString(")");
-    else
-      state.tokenize = null;
-    return ret(null, "(");
-  }
-
-  // Context management
-
-  function Context(type, indent, prev) {
-    this.type = type;
-    this.indent = indent;
-    this.prev = prev;
-  }
-
-  function pushContext(state, stream, type, indent) {
-    state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);
-    return type;
-  }
-
-  function popContext(state) {
-    if (state.context.prev)
-      state.context = state.context.prev;
-    return state.context.type;
-  }
-
-  function pass(type, stream, state) {
-    return states[state.context.type](type, stream, state);
-  }
-  function popAndPass(type, stream, state, n) {
-    for (var i = n || 1; i > 0; i--)
-      state.context = state.context.prev;
-    return pass(type, stream, state);
-  }
-
-  // Parser
-
-  function wordAsValue(stream) {
-    var word = stream.current().toLowerCase();
-    if (valueKeywords.hasOwnProperty(word))
-      override = "atom";
-    else if (colorKeywords.hasOwnProperty(word))
-      override = "keyword";
-    else
-      override = "variable";
-  }
-
-  var states = {};
-
-  states.top = function(type, stream, state) {
-    if (type == "{") {
-      return pushContext(state, stream, "block");
-    } else if (type == "}" && state.context.prev) {
-      return popContext(state);
-    } else if (supportsAtComponent && /@component/i.test(type)) {
-      return pushContext(state, stream, "atComponentBlock");
-    } else if (/^@(-moz-)?document$/i.test(type)) {
-      return pushContext(state, stream, "documentTypes");
-    } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {
-      return pushContext(state, stream, "atBlock");
-    } else if (/^@(font-face|counter-style)/i.test(type)) {
-      state.stateArg = type;
-      return "restricted_atBlock_before";
-    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {
-      return "keyframes";
-    } else if (type && type.charAt(0) == "@") {
-      return pushContext(state, stream, "at");
-    } else if (type == "hash") {
-      override = "builtin";
-    } else if (type == "word") {
-      override = "tag";
-    } else if (type == "variable-definition") {
-      return "maybeprop";
-    } else if (type == "interpolation") {
-      return pushContext(state, stream, "interpolation");
-    } else if (type == ":") {
-      return "pseudo";
-    } else if (allowNested && type == "(") {
-      return pushContext(state, stream, "parens");
-    }
-    return state.context.type;
-  };
-
-  states.block = function(type, stream, state) {
-    if (type == "word") {
-      var word = stream.current().toLowerCase();
-      if (propertyKeywords.hasOwnProperty(word)) {
-        override = "property";
-        return "maybeprop";
-      } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
-        override = "string-2";
-        return "maybeprop";
-      } else if (allowNested) {
-        override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
-        return "block";
-      } else {
-        override += " error";
-        return "maybeprop";
-      }
-    } else if (type == "meta") {
-      return "block";
-    } else if (!allowNested && (type == "hash" || type == "qualifier")) {
-      override = "error";
-      return "block";
-    } else {
-      return states.top(type, stream, state);
-    }
-  };
-
-  states.maybeprop = function(type, stream, state) {
-    if (type == ":") return pushContext(state, stream, "prop");
-    return pass(type, stream, state);
-  };
-
-  states.prop = function(type, stream, state) {
-    if (type == ";") return popContext(state);
-    if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
-    if (type == "}" || type == "{") return popAndPass(type, stream, state);
-    if (type == "(") return pushContext(state, stream, "parens");
-
-    if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) {
-      override += " error";
-    } else if (type == "word") {
-      wordAsValue(stream);
-    } else if (type == "interpolation") {
-      return pushContext(state, stream, "interpolation");
-    }
-    return "prop";
-  };
-
-  states.propBlock = function(type, _stream, state) {
-    if (type == "}") return popContext(state);
-    if (type == "word") { override = "property"; return "maybeprop"; }
-    return state.context.type;
-  };
-
-  states.parens = function(type, stream, state) {
-    if (type == "{" || type == "}") return popAndPass(type, stream, state);
-    if (type == ")") return popContext(state);
-    if (type == "(") return pushContext(state, stream, "parens");
-    if (type == "interpolation") return pushContext(state, stream, "interpolation");
-    if (type == "word") wordAsValue(stream);
-    return "parens";
-  };
-
-  states.pseudo = function(type, stream, state) {
-    if (type == "meta") return "pseudo";
-
-    if (type == "word") {
-      override = "variable-3";
-      return state.context.type;
-    }
-    return pass(type, stream, state);
-  };
-
-  states.documentTypes = function(type, stream, state) {
-    if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
-      override = "tag";
-      return state.context.type;
-    } else {
-      return states.atBlock(type, stream, state);
-    }
-  };
-
-  states.atBlock = function(type, stream, state) {
-    if (type == "(") return pushContext(state, stream, "atBlock_parens");
-    if (type == "}" || type == ";") return popAndPass(type, stream, state);
-    if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
-
-    if (type == "interpolation") return pushContext(state, stream, "interpolation");
-
-    if (type == "word") {
-      var word = stream.current().toLowerCase();
-      if (word == "only" || word == "not" || word == "and" || word == "or")
-        override = "keyword";
-      else if (mediaTypes.hasOwnProperty(word))
-        override = "attribute";
-      else if (mediaFeatures.hasOwnProperty(word))
-        override = "property";
-      else if (mediaValueKeywords.hasOwnProperty(word))
-        override = "keyword";
-      else if (propertyKeywords.hasOwnProperty(word))
-        override = "property";
-      else if (nonStandardPropertyKeywords.hasOwnProperty(word))
-        override = "string-2";
-      else if (valueKeywords.hasOwnProperty(word))
-        override = "atom";
-      else if (colorKeywords.hasOwnProperty(word))
-        override = "keyword";
-      else
-        override = "error";
-    }
-    return state.context.type;
-  };
-
-  states.atComponentBlock = function(type, stream, state) {
-    if (type == "}")
-      return popAndPass(type, stream, state);
-    if (type == "{")
-      return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false);
-    if (type == "word")
-      override = "error";
-    return state.context.type;
-  };
-
-  states.atBlock_parens = function(type, stream, state) {
-    if (type == ")") return popContext(state);
-    if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
-    return states.atBlock(type, stream, state);
-  };
-
-  states.restricted_atBlock_before = function(type, stream, state) {
-    if (type == "{")
-      return pushContext(state, stream, "restricted_atBlock");
-    if (type == "word" && state.stateArg == "@counter-style") {
-      override = "variable";
-      return "restricted_atBlock_before";
-    }
-    return pass(type, stream, state);
-  };
-
-  states.restricted_atBlock = function(type, stream, state) {
-    if (type == "}") {
-      state.stateArg = null;
-      return popContext(state);
-    }
-    if (type == "word") {
-      if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
-          (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
-        override = "error";
-      else
-        override = "property";
-      return "maybeprop";
-    }
-    return "restricted_atBlock";
-  };
-
-  states.keyframes = function(type, stream, state) {
-    if (type == "word") { override = "variable"; return "keyframes"; }
-    if (type == "{") return pushContext(state, stream, "top");
-    return pass(type, stream, state);
-  };
-
-  states.at = function(type, stream, state) {
-    if (type == ";") return popContext(state);
-    if (type == "{" || type == "}") return popAndPass(type, stream, state);
-    if (type == "word") override = "tag";
-    else if (type == "hash") override = "builtin";
-    return "at";
-  };
-
-  states.interpolation = function(type, stream, state) {
-    if (type == "}") return popContext(state);
-    if (type == "{" || type == ";") return popAndPass(type, stream, state);
-    if (type == "word") override = "variable";
-    else if (type != "variable" && type != "(" && type != ")") override = "error";
-    return "interpolation";
-  };
-
-  return {
-    startState: function(base) {
-      return {tokenize: null,
-              state: inline ? "block" : "top",
-              stateArg: null,
-              context: new Context(inline ? "block" : "top", base || 0, null)};
-    },
-
-    token: function(stream, state) {
-      if (!state.tokenize && stream.eatSpace()) return null;
-      var style = (state.tokenize || tokenBase)(stream, state);
-      if (style && typeof style == "object") {
-        type = style[1];
-        style = style[0];
-      }
-      override = style;
-      if (type != "comment")
-        state.state = states[state.state](type, stream, state);
-      return override;
-    },
-
-    indent: function(state, textAfter) {
-      var cx = state.context, ch = textAfter && textAfter.charAt(0);
-      var indent = cx.indent;
-      if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
-      if (cx.prev) {
-        if (ch == "}" && (cx.type == "block" || cx.type == "top" ||
-                          cx.type == "interpolation" || cx.type == "restricted_atBlock")) {
-          // Resume indentation from parent context.
-          cx = cx.prev;
-          indent = cx.indent;
-        } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
-            ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
-          // Dedent relative to current context.
-          indent = Math.max(0, cx.indent - indentUnit);
-        }
-      }
-      return indent;
-    },
-
-    electricChars: "}",
-    blockCommentStart: "/*",
-    blockCommentEnd: "*/",
-    blockCommentContinue: " * ",
-    lineComment: lineComment,
-    fold: "brace"
-  };
-});
-
-  function keySet(array) {
-    var keys = {};
-    for (var i = 0; i < array.length; ++i) {
-      keys[array[i].toLowerCase()] = true;
-    }
-    return keys;
-  }
-
-  var documentTypes_ = [
-    "domain", "regexp", "url", "url-prefix"
-  ], documentTypes = keySet(documentTypes_);
-
-  var mediaTypes_ = [
-    "all", "aural", "braille", "handheld", "print", "projection", "screen",
-    "tty", "tv", "embossed"
-  ], mediaTypes = keySet(mediaTypes_);
-
-  var mediaFeatures_ = [
-    "width", "min-width", "max-width", "height", "min-height", "max-height",
-    "device-width", "min-device-width", "max-device-width", "device-height",
-    "min-device-height", "max-device-height", "aspect-ratio",
-    "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
-    "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
-    "max-color", "color-index", "min-color-index", "max-color-index",
-    "monochrome", "min-monochrome", "max-monochrome", "resolution",
-    "min-resolution", "max-resolution", "scan", "grid", "orientation",
-    "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio",
-    "pointer", "any-pointer", "hover", "any-hover"
-  ], mediaFeatures = keySet(mediaFeatures_);
-
-  var mediaValueKeywords_ = [
-    "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover",
-    "interlace", "progressive"
-  ], mediaValueKeywords = keySet(mediaValueKeywords_);
-
-  var propertyKeywords_ = [
-    "align-content", "align-items", "align-self", "alignment-adjust",
-    "alignment-baseline", "anchor-point", "animation", "animation-delay",
-    "animation-direction", "animation-duration", "animation-fill-mode",
-    "animation-iteration-count", "animation-name", "animation-play-state",
-    "animation-timing-function", "appearance", "azimuth", "backdrop-filter",
-    "backface-visibility", "background", "background-attachment",
-    "background-blend-mode", "background-clip", "background-color",
-    "background-image", "background-origin", "background-position",
-    "background-position-x", "background-position-y", "background-repeat",
-    "background-size", "baseline-shift", "binding", "bleed", "block-size",
-    "bookmark-label", "bookmark-level", "bookmark-state", "bookmark-target",
-    "border", "border-bottom", "border-bottom-color", "border-bottom-left-radius",
-    "border-bottom-right-radius", "border-bottom-style", "border-bottom-width",
-    "border-collapse", "border-color", "border-image", "border-image-outset",
-    "border-image-repeat", "border-image-slice", "border-image-source",
-    "border-image-width", "border-left", "border-left-color", "border-left-style",
-    "border-left-width", "border-radius", "border-right", "border-right-color",
-    "border-right-style", "border-right-width", "border-spacing", "border-style",
-    "border-top", "border-top-color", "border-top-left-radius",
-    "border-top-right-radius", "border-top-style", "border-top-width",
-    "border-width", "bottom", "box-decoration-break", "box-shadow", "box-sizing",
-    "break-after", "break-before", "break-inside", "caption-side", "caret-color",
-    "clear", "clip", "color", "color-profile", "column-count", "column-fill",
-    "column-gap", "column-rule", "column-rule-color", "column-rule-style",
-    "column-rule-width", "column-span", "column-width", "columns", "contain",
-    "content", "counter-increment", "counter-reset", "crop", "cue", "cue-after",
-    "cue-before", "cursor", "direction", "display", "dominant-baseline",
-    "drop-initial-after-adjust", "drop-initial-after-align",
-    "drop-initial-before-adjust", "drop-initial-before-align", "drop-initial-size",
-    "drop-initial-value", "elevation", "empty-cells", "fit", "fit-position",
-    "flex", "flex-basis", "flex-direction", "flex-flow", "flex-grow",
-    "flex-shrink", "flex-wrap", "float", "float-offset", "flow-from", "flow-into",
-    "font", "font-family", "font-feature-settings", "font-kerning",
-    "font-language-override", "font-optical-sizing", "font-size",
-    "font-size-adjust", "font-stretch", "font-style", "font-synthesis",
-    "font-variant", "font-variant-alternates", "font-variant-caps",
-    "font-variant-east-asian", "font-variant-ligatures", "font-variant-numeric",
-    "font-variant-position", "font-variation-settings", "font-weight", "gap",
-    "grid", "grid-area", "grid-auto-columns", "grid-auto-flow", "grid-auto-rows",
-    "grid-column", "grid-column-end", "grid-column-gap", "grid-column-start",
-    "grid-gap", "grid-row", "grid-row-end", "grid-row-gap", "grid-row-start",
-    "grid-template", "grid-template-areas", "grid-template-columns",
-    "grid-template-rows", "hanging-punctuation", "height", "hyphens", "icon",
-    "image-orientation", "image-rendering", "image-resolution", "inline-box-align",
-    "inset", "inset-block", "inset-block-end", "inset-block-start", "inset-inline",
-    "inset-inline-end", "inset-inline-start", "isolation", "justify-content",
-    "justify-items", "justify-self", "left", "letter-spacing", "line-break",
-    "line-height", "line-height-step", "line-stacking", "line-stacking-ruby",
-    "line-stacking-shift", "line-stacking-strategy", "list-style",
-    "list-style-image", "list-style-position", "list-style-type", "margin",
-    "margin-bottom", "margin-left", "margin-right", "margin-top", "marks",
-    "marquee-direction", "marquee-loop", "marquee-play-count", "marquee-speed",
-    "marquee-style", "max-block-size", "max-height", "max-inline-size",
-    "max-width", "min-block-size", "min-height", "min-inline-size", "min-width",
-    "mix-blend-mode", "move-to", "nav-down", "nav-index", "nav-left", "nav-right",
-    "nav-up", "object-fit", "object-position", "offset", "offset-anchor",
-    "offset-distance", "offset-path", "offset-position", "offset-rotate",
-    "opacity", "order", "orphans", "outline", "outline-color", "outline-offset",
-    "outline-style", "outline-width", "overflow", "overflow-style",
-    "overflow-wrap", "overflow-x", "overflow-y", "padding", "padding-bottom",
-    "padding-left", "padding-right", "padding-top", "page", "page-break-after",
-    "page-break-before", "page-break-inside", "page-policy", "pause",
-    "pause-after", "pause-before", "perspective", "perspective-origin", "pitch",
-    "pitch-range", "place-content", "place-items", "place-self", "play-during",
-    "position", "presentation-level", "punctuation-trim", "quotes",
-    "region-break-after", "region-break-before", "region-break-inside",
-    "region-fragment", "rendering-intent", "resize", "rest", "rest-after",
-    "rest-before", "richness", "right", "rotate", "rotation", "rotation-point",
-    "row-gap", "ruby-align", "ruby-overhang", "ruby-position", "ruby-span",
-    "scale", "scroll-behavior", "scroll-margin", "scroll-margin-block",
-    "scroll-margin-block-end", "scroll-margin-block-start", "scroll-margin-bottom",
-    "scroll-margin-inline", "scroll-margin-inline-end",
-    "scroll-margin-inline-start", "scroll-margin-left", "scroll-margin-right",
-    "scroll-margin-top", "scroll-padding", "scroll-padding-block",
-    "scroll-padding-block-end", "scroll-padding-block-start",
-    "scroll-padding-bottom", "scroll-padding-inline", "scroll-padding-inline-end",
-    "scroll-padding-inline-start", "scroll-padding-left", "scroll-padding-right",
-    "scroll-padding-top", "scroll-snap-align", "scroll-snap-type",
-    "shape-image-threshold", "shape-inside", "shape-margin", "shape-outside",
-    "size", "speak", "speak-as", "speak-header", "speak-numeral",
-    "speak-punctuation", "speech-rate", "stress", "string-set", "tab-size",
-    "table-layout", "target", "target-name", "target-new", "target-position",
-    "text-align", "text-align-last", "text-combine-upright", "text-decoration",
-    "text-decoration-color", "text-decoration-line", "text-decoration-skip",
-    "text-decoration-skip-ink", "text-decoration-style", "text-emphasis",
-    "text-emphasis-color", "text-emphasis-position", "text-emphasis-style",
-    "text-height", "text-indent", "text-justify", "text-orientation",
-    "text-outline", "text-overflow", "text-rendering", "text-shadow",
-    "text-size-adjust", "text-space-collapse", "text-transform",
-    "text-underline-position", "text-wrap", "top", "transform", "transform-origin",
-    "transform-style", "transition", "transition-delay", "transition-duration",
-    "transition-property", "transition-timing-function", "translate",
-    "unicode-bidi", "user-select", "vertical-align", "visibility", "voice-balance",
-    "voice-duration", "voice-family", "voice-pitch", "voice-range", "voice-rate",
-    "voice-stress", "voice-volume", "volume", "white-space", "widows", "width",
-    "will-change", "word-break", "word-spacing", "word-wrap", "writing-mode", "z-index",
-    // SVG-specific
-    "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
-    "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
-    "color-interpolation", "color-interpolation-filters",
-    "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
-    "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
-    "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
-    "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
-    "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
-    "glyph-orientation-vertical", "text-anchor", "writing-mode"
-  ], propertyKeywords = keySet(propertyKeywords_);
-
-  var nonStandardPropertyKeywords_ = [
-    "border-block", "border-block-color", "border-block-end",
-    "border-block-end-color", "border-block-end-style", "border-block-end-width",
-    "border-block-start", "border-block-start-color", "border-block-start-style",
-    "border-block-start-width", "border-block-style", "border-block-width",
-    "border-inline", "border-inline-color", "border-inline-end",
-    "border-inline-end-color", "border-inline-end-style",
-    "border-inline-end-width", "border-inline-start", "border-inline-start-color",
-    "border-inline-start-style", "border-inline-start-width",
-    "border-inline-style", "border-inline-width", "margin-block",
-    "margin-block-end", "margin-block-start", "margin-inline", "margin-inline-end",
-    "margin-inline-start", "padding-block", "padding-block-end",
-    "padding-block-start", "padding-inline", "padding-inline-end",
-    "padding-inline-start", "scroll-snap-stop", "scrollbar-3d-light-color",
-    "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
-    "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
-    "scrollbar-track-color", "searchfield-cancel-button", "searchfield-decoration",
-    "searchfield-results-button", "searchfield-results-decoration", "shape-inside", "zoom"
-  ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
-
-  var fontProperties_ = [
-    "font-display", "font-family", "src", "unicode-range", "font-variant",
-     "font-feature-settings", "font-stretch", "font-weight", "font-style"
-  ], fontProperties = keySet(fontProperties_);
-
-  var counterDescriptors_ = [
-    "additive-symbols", "fallback", "negative", "pad", "prefix", "range",
-    "speak-as", "suffix", "symbols", "system"
-  ], counterDescriptors = keySet(counterDescriptors_);
-
-  var colorKeywords_ = [
-    "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
-    "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
-    "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
-    "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
-    "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
-    "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
-    "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
-    "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
-    "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
-    "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
-    "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
-    "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
-    "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
-    "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
-    "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
-    "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
-    "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
-    "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
-    "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
-    "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
-    "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
-    "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
-    "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
-    "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
-    "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
-    "whitesmoke", "yellow", "yellowgreen"
-  ], colorKeywords = keySet(colorKeywords_);
-
-  var valueKeywords_ = [
-    "above", "absolute", "activeborder", "additive", "activecaption", "afar",
-    "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
-    "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
-    "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page",
-    "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
-    "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
-    "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
-    "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian",
-    "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
-    "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
-    "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
-    "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
-    "compact", "condensed", "contain", "content", "contents",
-    "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
-    "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
-    "decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
-    "destination-in", "destination-out", "destination-over", "devanagari", "difference",
-    "disc", "discard", "disclosure-closed", "disclosure-open", "document",
-    "dot-dash", "dot-dot-dash",
-    "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
-    "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
-    "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
-    "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
-    "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
-    "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
-    "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
-    "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
-    "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
-    "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
-    "forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove",
-    "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
-    "help", "hidden", "hide", "higher", "highlight", "highlighttext",
-    "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
-    "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
-    "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
-    "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert",
-    "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
-    "katakana", "katakana-iroha", "keep-all", "khmer",
-    "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
-    "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
-    "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
-    "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
-    "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
-    "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d",
-    "media-controls-background", "media-current-time-display",
-    "media-fullscreen-button", "media-mute-button", "media-play-button",
-    "media-return-to-realtime-button", "media-rewind-button",
-    "media-seek-back-button", "media-seek-forward-button", "media-slider",
-    "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
-    "media-volume-slider-container", "media-volume-sliderthumb", "medium",
-    "menu", "menulist", "menulist-button", "menulist-text",
-    "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
-    "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
-    "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
-    "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
-    "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote",
-    "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
-    "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
-    "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
-    "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d",
-    "progress", "push-button", "radial-gradient", "radio", "read-only",
-    "read-write", "read-write-plaintext-only", "rectangle", "region",
-    "relative", "repeat", "repeating-linear-gradient",
-    "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
-    "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
-    "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
-    "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
-    "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield",
-    "searchfield-cancel-button", "searchfield-decoration",
-    "searchfield-results-button", "searchfield-results-decoration", "self-start", "self-end",
-    "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
-    "simp-chinese-formal", "simp-chinese-informal", "single",
-    "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
-    "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
-    "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
-    "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "space-evenly", "spell-out", "square",
-    "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
-    "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table",
-    "table-caption", "table-cell", "table-column", "table-column-group",
-    "table-footer-group", "table-header-group", "table-row", "table-row-group",
-    "tamil",
-    "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
-    "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
-    "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
-    "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
-    "trad-chinese-formal", "trad-chinese-informal", "transform",
-    "translate", "translate3d", "translateX", "translateY", "translateZ",
-    "transparent", "ultra-condensed", "ultra-expanded", "underline", "unset", "up",
-    "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
-    "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
-    "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
-    "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
-    "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor",
-    "xx-large", "xx-small"
-  ], valueKeywords = keySet(valueKeywords_);
-
-  var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)
-    .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)
-    .concat(valueKeywords_);
-  CodeMirror.registerHelper("hintWords", "css", allWords);
-
-  function tokenCComment(stream, state) {
-    var maybeEnd = false, ch;
-    while ((ch = stream.next()) != null) {
-      if (maybeEnd && ch == "/") {
-        state.tokenize = null;
-        break;
-      }
-      maybeEnd = (ch == "*");
-    }
-    return ["comment", "comment"];
-  }
-
-  CodeMirror.defineMIME("text/css", {
-    documentTypes: documentTypes,
-    mediaTypes: mediaTypes,
-    mediaFeatures: mediaFeatures,
-    mediaValueKeywords: mediaValueKeywords,
-    propertyKeywords: propertyKeywords,
-    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
-    fontProperties: fontProperties,
-    counterDescriptors: counterDescriptors,
-    colorKeywords: colorKeywords,
-    valueKeywords: valueKeywords,
-    tokenHooks: {
-      "/": function(stream, state) {
-        if (!stream.eat("*")) return false;
-        state.tokenize = tokenCComment;
-        return tokenCComment(stream, state);
-      }
-    },
-    name: "css"
-  });
-
-  CodeMirror.defineMIME("text/x-scss", {
-    mediaTypes: mediaTypes,
-    mediaFeatures: mediaFeatures,
-    mediaValueKeywords: mediaValueKeywords,
-    propertyKeywords: propertyKeywords,
-    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
-    colorKeywords: colorKeywords,
-    valueKeywords: valueKeywords,
-    fontProperties: fontProperties,
-    allowNested: true,
-    lineComment: "//",
-    tokenHooks: {
-      "/": function(stream, state) {
-        if (stream.eat("/")) {
-          stream.skipToEnd();
-          return ["comment", "comment"];
-        } else if (stream.eat("*")) {
-          state.tokenize = tokenCComment;
-          return tokenCComment(stream, state);
-        } else {
-          return ["operator", "operator"];
-        }
-      },
-      ":": function(stream) {
-        if (stream.match(/\s*\{/, false))
-          return [null, null]
-        return false;
-      },
-      "$": function(stream) {
-        stream.match(/^[\w-]+/);
-        if (stream.match(/^\s*:/, false))
-          return ["variable-2", "variable-definition"];
-        return ["variable-2", "variable"];
-      },
-      "#": function(stream) {
-        if (!stream.eat("{")) return false;
-        return [null, "interpolation"];
-      }
-    },
-    name: "css",
-    helperType: "scss"
-  });
-
-  CodeMirror.defineMIME("text/x-less", {
-    mediaTypes: mediaTypes,
-    mediaFeatures: mediaFeatures,
-    mediaValueKeywords: mediaValueKeywords,
-    propertyKeywords: propertyKeywords,
-    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
-    colorKeywords: colorKeywords,
-    valueKeywords: valueKeywords,
-    fontProperties: fontProperties,
-    allowNested: true,
-    lineComment: "//",
-    tokenHooks: {
-      "/": function(stream, state) {
-        if (stream.eat("/")) {
-          stream.skipToEnd();
-          return ["comment", "comment"];
-        } else if (stream.eat("*")) {
-          state.tokenize = tokenCComment;
-          return tokenCComment(stream, state);
-        } else {
-          return ["operator", "operator"];
-        }
-      },
-      "@": function(stream) {
-        if (stream.eat("{")) return [null, "interpolation"];
-        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false;
-        stream.eatWhile(/[\w\\\-]/);
-        if (stream.match(/^\s*:/, false))
-          return ["variable-2", "variable-definition"];
-        return ["variable-2", "variable"];
-      },
-      "&": function() {
-        return ["atom", "atom"];
-      }
-    },
-    name: "css",
-    helperType: "less"
-  });
-
-  CodeMirror.defineMIME("text/x-gss", {
-    documentTypes: documentTypes,
-    mediaTypes: mediaTypes,
-    mediaFeatures: mediaFeatures,
-    propertyKeywords: propertyKeywords,
-    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
-    fontProperties: fontProperties,
-    counterDescriptors: counterDescriptors,
-    colorKeywords: colorKeywords,
-    valueKeywords: valueKeywords,
-    supportsAtComponent: true,
-    tokenHooks: {
-      "/": function(stream, state) {
-        if (!stream.eat("*")) return false;
-        state.tokenize = tokenCComment;
-        return tokenCComment(stream, state);
-      }
-    },
-    name: "css",
-    helperType: "gss"
-  });
-
-});
diff --git a/plugins/UiFileManager/media/codemirror/mode/go.js b/plugins/UiFileManager/media/codemirror/mode/go.js
deleted file mode 100644
index c005e42d..00000000
--- a/plugins/UiFileManager/media/codemirror/mode/go.js
+++ /dev/null
@@ -1,187 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-CodeMirror.defineMode("go", function(config) {
-  var indentUnit = config.indentUnit;
-
-  var keywords = {
-    "break":true, "case":true, "chan":true, "const":true, "continue":true,
-    "default":true, "defer":true, "else":true, "fallthrough":true, "for":true,
-    "func":true, "go":true, "goto":true, "if":true, "import":true,
-    "interface":true, "map":true, "package":true, "range":true, "return":true,
-    "select":true, "struct":true, "switch":true, "type":true, "var":true,
-    "bool":true, "byte":true, "complex64":true, "complex128":true,
-    "float32":true, "float64":true, "int8":true, "int16":true, "int32":true,
-    "int64":true, "string":true, "uint8":true, "uint16":true, "uint32":true,
-    "uint64":true, "int":true, "uint":true, "uintptr":true, "error": true,
-    "rune":true
-  };
-
-  var atoms = {
-    "true":true, "false":true, "iota":true, "nil":true, "append":true,
-    "cap":true, "close":true, "complex":true, "copy":true, "delete":true, "imag":true,
-    "len":true, "make":true, "new":true, "panic":true, "print":true,
-    "println":true, "real":true, "recover":true
-  };
-
-  var isOperatorChar = /[+\-*&^%:=<>!|\/]/;
-
-  var curPunc;
-
-  function tokenBase(stream, state) {
-    var ch = stream.next();
-    if (ch == '"' || ch == "'" || ch == "`") {
-      state.tokenize = tokenString(ch);
-      return state.tokenize(stream, state);
-    }
-    if (/[\d\.]/.test(ch)) {
-      if (ch == ".") {
-        stream.match(/^[0-9]+([eE][\-+]?[0-9]+)?/);
-      } else if (ch == "0") {
-        stream.match(/^[xX][0-9a-fA-F]+/) || stream.match(/^0[0-7]+/);
-      } else {
-        stream.match(/^[0-9]*\.?[0-9]*([eE][\-+]?[0-9]+)?/);
-      }
-      return "number";
-    }
-    if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
-      curPunc = ch;
-      return null;
-    }
-    if (ch == "/") {
-      if (stream.eat("*")) {
-        state.tokenize = tokenComment;
-        return tokenComment(stream, state);
-      }
-      if (stream.eat("/")) {
-        stream.skipToEnd();
-        return "comment";
-      }
-    }
-    if (isOperatorChar.test(ch)) {
-      stream.eatWhile(isOperatorChar);
-      return "operator";
-    }
-    stream.eatWhile(/[\w\$_\xa1-\uffff]/);
-    var cur = stream.current();
-    if (keywords.propertyIsEnumerable(cur)) {
-      if (cur == "case" || cur == "default") curPunc = "case";
-      return "keyword";
-    }
-    if (atoms.propertyIsEnumerable(cur)) return "atom";
-    return "variable";
-  }
-
-  function tokenString(quote) {
-    return function(stream, state) {
-      var escaped = false, next, end = false;
-      while ((next = stream.next()) != null) {
-        if (next == quote && !escaped) {end = true; break;}
-        escaped = !escaped && quote != "`" && next == "\\";
-      }
-      if (end || !(escaped || quote == "`"))
-        state.tokenize = tokenBase;
-      return "string";
-    };
-  }
-
-  function tokenComment(stream, state) {
-    var maybeEnd = false, ch;
-    while (ch = stream.next()) {
-      if (ch == "/" && maybeEnd) {
-        state.tokenize = tokenBase;
-        break;
-      }
-      maybeEnd = (ch == "*");
-    }
-    return "comment";
-  }
-
-  function Context(indented, column, type, align, prev) {
-    this.indented = indented;
-    this.column = column;
-    this.type = type;
-    this.align = align;
-    this.prev = prev;
-  }
-  function pushContext(state, col, type) {
-    return state.context = new Context(state.indented, col, type, null, state.context);
-  }
-  function popContext(state) {
-    if (!state.context.prev) return;
-    var t = state.context.type;
-    if (t == ")" || t == "]" || t == "}")
-      state.indented = state.context.indented;
-    return state.context = state.context.prev;
-  }
-
-  // Interface
-
-  return {
-    startState: function(basecolumn) {
-      return {
-        tokenize: null,
-        context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
-        indented: 0,
-        startOfLine: true
-      };
-    },
-
-    token: function(stream, state) {
-      var ctx = state.context;
-      if (stream.sol()) {
-        if (ctx.align == null) ctx.align = false;
-        state.indented = stream.indentation();
-        state.startOfLine = true;
-        if (ctx.type == "case") ctx.type = "}";
-      }
-      if (stream.eatSpace()) return null;
-      curPunc = null;
-      var style = (state.tokenize || tokenBase)(stream, state);
-      if (style == "comment") return style;
-      if (ctx.align == null) ctx.align = true;
-
-      if (curPunc == "{") pushContext(state, stream.column(), "}");
-      else if (curPunc == "[") pushContext(state, stream.column(), "]");
-      else if (curPunc == "(") pushContext(state, stream.column(), ")");
-      else if (curPunc == "case") ctx.type = "case";
-      else if (curPunc == "}" && ctx.type == "}") popContext(state);
-      else if (curPunc == ctx.type) popContext(state);
-      state.startOfLine = false;
-      return style;
-    },
-
-    indent: function(state, textAfter) {
-      if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
-      var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
-      if (ctx.type == "case" && /^(?:case|default)\b/.test(textAfter)) {
-        state.context.type = "}";
-        return ctx.indented;
-      }
-      var closing = firstChar == ctx.type;
-      if (ctx.align) return ctx.column + (closing ? 0 : 1);
-      else return ctx.indented + (closing ? 0 : indentUnit);
-    },
-
-    electricChars: "{}):",
-    closeBrackets: "()[]{}''\"\"``",
-    fold: "brace",
-    blockCommentStart: "/*",
-    blockCommentEnd: "*/",
-    lineComment: "//"
-  };
-});
-
-CodeMirror.defineMIME("text/x-go", "go");
-
-});
diff --git a/plugins/UiFileManager/media/codemirror/mode/htmlembedded.js b/plugins/UiFileManager/media/codemirror/mode/htmlembedded.js
deleted file mode 100644
index 439e63a4..00000000
--- a/plugins/UiFileManager/media/codemirror/mode/htmlembedded.js
+++ /dev/null
@@ -1,37 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"),
-        require("../../addon/mode/multiplex"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "../htmlmixed/htmlmixed",
-            "../../addon/mode/multiplex"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  CodeMirror.defineMode("htmlembedded", function(config, parserConfig) {
-    var closeComment = parserConfig.closeComment || "--%>"
-    return CodeMirror.multiplexingMode(CodeMirror.getMode(config, "htmlmixed"), {
-      open: parserConfig.openComment || "<%--",
-      close: closeComment,
-      delimStyle: "comment",
-      mode: {token: function(stream) {
-        stream.skipTo(closeComment) || stream.skipToEnd()
-        return "comment"
-      }}
-    }, {
-      open: parserConfig.open || parserConfig.scriptStartRegex || "<%",
-      close: parserConfig.close || parserConfig.scriptEndRegex || "%>",
-      mode: CodeMirror.getMode(config, parserConfig.scriptingModeSpec)
-    });
-  }, "htmlmixed");
-
-  CodeMirror.defineMIME("application/x-ejs", {name: "htmlembedded", scriptingModeSpec:"javascript"});
-  CodeMirror.defineMIME("application/x-aspx", {name: "htmlembedded", scriptingModeSpec:"text/x-csharp"});
-  CodeMirror.defineMIME("application/x-jsp", {name: "htmlembedded", scriptingModeSpec:"text/x-java"});
-  CodeMirror.defineMIME("application/x-erb", {name: "htmlembedded", scriptingModeSpec:"ruby"});
-});
diff --git a/plugins/UiFileManager/media/codemirror/mode/htmlmixed.js b/plugins/UiFileManager/media/codemirror/mode/htmlmixed.js
deleted file mode 100644
index 8341ac82..00000000
--- a/plugins/UiFileManager/media/codemirror/mode/htmlmixed.js
+++ /dev/null
@@ -1,152 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  var defaultTags = {
-    script: [
-      ["lang", /(javascript|babel)/i, "javascript"],
-      ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, "javascript"],
-      ["type", /./, "text/plain"],
-      [null, null, "javascript"]
-    ],
-    style:  [
-      ["lang", /^css$/i, "css"],
-      ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
-      ["type", /./, "text/plain"],
-      [null, null, "css"]
-    ]
-  };
-
-  function maybeBackup(stream, pat, style) {
-    var cur = stream.current(), close = cur.search(pat);
-    if (close > -1) {
-      stream.backUp(cur.length - close);
-    } else if (cur.match(/<\/?$/)) {
-      stream.backUp(cur.length);
-      if (!stream.match(pat, false)) stream.match(cur);
-    }
-    return style;
-  }
-
-  var attrRegexpCache = {};
-  function getAttrRegexp(attr) {
-    var regexp = attrRegexpCache[attr];
-    if (regexp) return regexp;
-    return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
-  }
-
-  function getAttrValue(text, attr) {
-    var match = text.match(getAttrRegexp(attr))
-    return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
-  }
-
-  function getTagRegexp(tagName, anchored) {
-    return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i");
-  }
-
-  function addTags(from, to) {
-    for (var tag in from) {
-      var dest = to[tag] || (to[tag] = []);
-      var source = from[tag];
-      for (var i = source.length - 1; i >= 0; i--)
-        dest.unshift(source[i])
-    }
-  }
-
-  function findMatchingMode(tagInfo, tagText) {
-    for (var i = 0; i < tagInfo.length; i++) {
-      var spec = tagInfo[i];
-      if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
-    }
-  }
-
-  CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
-    var htmlMode = CodeMirror.getMode(config, {
-      name: "xml",
-      htmlMode: true,
-      multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
-      multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag
-    });
-
-    var tags = {};
-    var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
-    addTags(defaultTags, tags);
-    if (configTags) addTags(configTags, tags);
-    if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
-      tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
-
-    function html(stream, state) {
-      var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
-      if (tag && !/[<>\s\/]/.test(stream.current()) &&
-          (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
-          tags.hasOwnProperty(tagName)) {
-        state.inTag = tagName + " "
-      } else if (state.inTag && tag && />$/.test(stream.current())) {
-        var inTag = /^([\S]+) (.*)/.exec(state.inTag)
-        state.inTag = null
-        var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
-        var mode = CodeMirror.getMode(config, modeSpec)
-        var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
-        state.token = function (stream, state) {
-          if (stream.match(endTagA, false)) {
-            state.token = html;
-            state.localState = state.localMode = null;
-            return null;
-          }
-          return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
-        };
-        state.localMode = mode;
-        state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "", ""));
-      } else if (state.inTag) {
-        state.inTag += stream.current()
-        if (stream.eol()) state.inTag += " "
-      }
-      return style;
-    };
-
-    return {
-      startState: function () {
-        var state = CodeMirror.startState(htmlMode);
-        return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
-      },
-
-      copyState: function (state) {
-        var local;
-        if (state.localState) {
-          local = CodeMirror.copyState(state.localMode, state.localState);
-        }
-        return {token: state.token, inTag: state.inTag,
-                localMode: state.localMode, localState: local,
-                htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
-      },
-
-      token: function (stream, state) {
-        return state.token(stream, state);
-      },
-
-      indent: function (state, textAfter, line) {
-        if (!state.localMode || /^\s*<\//.test(textAfter))
-          return htmlMode.indent(state.htmlState, textAfter, line);
-        else if (state.localMode.indent)
-          return state.localMode.indent(state.localState, textAfter, line);
-        else
-          return CodeMirror.Pass;
-      },
-
-      innerMode: function (state) {
-        return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
-      }
-    };
-  }, "xml", "javascript", "css");
-
-  CodeMirror.defineMIME("text/html", "htmlmixed");
-});
diff --git a/plugins/UiFileManager/media/codemirror/mode/javascript.js b/plugins/UiFileManager/media/codemirror/mode/javascript.js
deleted file mode 100644
index 9c751d23..00000000
--- a/plugins/UiFileManager/media/codemirror/mode/javascript.js
+++ /dev/null
@@ -1,934 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-CodeMirror.defineMode("javascript", function(config, parserConfig) {
-  var indentUnit = config.indentUnit;
-  var statementIndent = parserConfig.statementIndent;
-  var jsonldMode = parserConfig.jsonld;
-  var jsonMode = parserConfig.json || jsonldMode;
-  var isTS = parserConfig.typescript;
-  var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
-
-  // Tokenizer
-
-  var keywords = function(){
-    function kw(type) {return {type: type, style: "keyword"};}
-    var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
-    var operator = kw("operator"), atom = {type: "atom", style: "atom"};
-
-    return {
-      "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
-      "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
-      "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
-      "function": kw("function"), "catch": kw("catch"),
-      "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
-      "in": operator, "typeof": operator, "instanceof": operator,
-      "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
-      "this": kw("this"), "class": kw("class"), "super": kw("atom"),
-      "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
-      "await": C
-    };
-  }();
-
-  var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
-  var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
-
-  function readRegexp(stream) {
-    var escaped = false, next, inSet = false;
-    while ((next = stream.next()) != null) {
-      if (!escaped) {
-        if (next == "/" && !inSet) return;
-        if (next == "[") inSet = true;
-        else if (inSet && next == "]") inSet = false;
-      }
-      escaped = !escaped && next == "\\";
-    }
-  }
-
-  // Used as scratch variables to communicate multiple values without
-  // consing up tons of objects.
-  var type, content;
-  function ret(tp, style, cont) {
-    type = tp; content = cont;
-    return style;
-  }
-  function tokenBase(stream, state) {
-    var ch = stream.next();
-    if (ch == '"' || ch == "'") {
-      state.tokenize = tokenString(ch);
-      return state.tokenize(stream, state);
-    } else if (ch == "." && stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)) {
-      return ret("number", "number");
-    } else if (ch == "." && stream.match("..")) {
-      return ret("spread", "meta");
-    } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
-      return ret(ch);
-    } else if (ch == "=" && stream.eat(">")) {
-      return ret("=>", "operator");
-    } else if (ch == "0" && stream.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/)) {
-      return ret("number", "number");
-    } else if (/\d/.test(ch)) {
-      stream.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/);
-      return ret("number", "number");
-    } else if (ch == "/") {
-      if (stream.eat("*")) {
-        state.tokenize = tokenComment;
-        return tokenComment(stream, state);
-      } else if (stream.eat("/")) {
-        stream.skipToEnd();
-        return ret("comment", "comment");
-      } else if (expressionAllowed(stream, state, 1)) {
-        readRegexp(stream);
-        stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/);
-        return ret("regexp", "string-2");
-      } else {
-        stream.eat("=");
-        return ret("operator", "operator", stream.current());
-      }
-    } else if (ch == "`") {
-      state.tokenize = tokenQuasi;
-      return tokenQuasi(stream, state);
-    } else if (ch == "#" && stream.peek() == "!") {
-      stream.skipToEnd();
-      return ret("meta", "meta");
-    } else if (ch == "#" && stream.eatWhile(wordRE)) {
-      return ret("variable", "property")
-    } else if (ch == "<" && stream.match("!--") ||
-               (ch == "-" && stream.match("->") && !/\S/.test(stream.string.slice(0, stream.start)))) {
-      stream.skipToEnd()
-      return ret("comment", "comment")
-    } else if (isOperatorChar.test(ch)) {
-      if (ch != ">" || !state.lexical || state.lexical.type != ">") {
-        if (stream.eat("=")) {
-          if (ch == "!" || ch == "=") stream.eat("=")
-        } else if (/[<>*+\-]/.test(ch)) {
-          stream.eat(ch)
-          if (ch == ">") stream.eat(ch)
-        }
-      }
-      if (ch == "?" && stream.eat(".")) return ret(".")
-      return ret("operator", "operator", stream.current());
-    } else if (wordRE.test(ch)) {
-      stream.eatWhile(wordRE);
-      var word = stream.current()
-      if (state.lastType != ".") {
-        if (keywords.propertyIsEnumerable(word)) {
-          var kw = keywords[word]
-          return ret(kw.type, kw.style, word)
-        }
-        if (word == "async" && stream.match(/^(\s|\/\*.*?\*\/)*[\[\(\w]/, false))
-          return ret("async", "keyword", word)
-      }
-      return ret("variable", "variable", word)
-    }
-  }
-
-  function tokenString(quote) {
-    return function(stream, state) {
-      var escaped = false, next;
-      if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
-        state.tokenize = tokenBase;
-        return ret("jsonld-keyword", "meta");
-      }
-      while ((next = stream.next()) != null) {
-        if (next == quote && !escaped) break;
-        escaped = !escaped && next == "\\";
-      }
-      if (!escaped) state.tokenize = tokenBase;
-      return ret("string", "string");
-    };
-  }
-
-  function tokenComment(stream, state) {
-    var maybeEnd = false, ch;
-    while (ch = stream.next()) {
-      if (ch == "/" && maybeEnd) {
-        state.tokenize = tokenBase;
-        break;
-      }
-      maybeEnd = (ch == "*");
-    }
-    return ret("comment", "comment");
-  }
-
-  function tokenQuasi(stream, state) {
-    var escaped = false, next;
-    while ((next = stream.next()) != null) {
-      if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
-        state.tokenize = tokenBase;
-        break;
-      }
-      escaped = !escaped && next == "\\";
-    }
-    return ret("quasi", "string-2", stream.current());
-  }
-
-  var brackets = "([{}])";
-  // This is a crude lookahead trick to try and notice that we're
-  // parsing the argument patterns for a fat-arrow function before we
-  // actually hit the arrow token. It only works if the arrow is on
-  // the same line as the arguments and there's no strange noise
-  // (comments) in between. Fallback is to only notice when we hit the
-  // arrow, and not declare the arguments as locals for the arrow
-  // body.
-  function findFatArrow(stream, state) {
-    if (state.fatArrowAt) state.fatArrowAt = null;
-    var arrow = stream.string.indexOf("=>", stream.start);
-    if (arrow < 0) return;
-
-    if (isTS) { // Try to skip TypeScript return type declarations after the arguments
-      var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
-      if (m) arrow = m.index
-    }
-
-    var depth = 0, sawSomething = false;
-    for (var pos = arrow - 1; pos >= 0; --pos) {
-      var ch = stream.string.charAt(pos);
-      var bracket = brackets.indexOf(ch);
-      if (bracket >= 0 && bracket < 3) {
-        if (!depth) { ++pos; break; }
-        if (--depth == 0) { if (ch == "(") sawSomething = true; break; }
-      } else if (bracket >= 3 && bracket < 6) {
-        ++depth;
-      } else if (wordRE.test(ch)) {
-        sawSomething = true;
-      } else if (/["'\/`]/.test(ch)) {
-        for (;; --pos) {
-          if (pos == 0) return
-          var next = stream.string.charAt(pos - 1)
-          if (next == ch && stream.string.charAt(pos - 2) != "\\") { pos--; break }
-        }
-      } else if (sawSomething && !depth) {
-        ++pos;
-        break;
-      }
-    }
-    if (sawSomething && !depth) state.fatArrowAt = pos;
-  }
-
-  // Parser
-
-  var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
-
-  function JSLexical(indented, column, type, align, prev, info) {
-    this.indented = indented;
-    this.column = column;
-    this.type = type;
-    this.prev = prev;
-    this.info = info;
-    if (align != null) this.align = align;
-  }
-
-  function inScope(state, varname) {
-    for (var v = state.localVars; v; v = v.next)
-      if (v.name == varname) return true;
-    for (var cx = state.context; cx; cx = cx.prev) {
-      for (var v = cx.vars; v; v = v.next)
-        if (v.name == varname) return true;
-    }
-  }
-
-  function parseJS(state, style, type, content, stream) {
-    var cc = state.cc;
-    // Communicate our context to the combinators.
-    // (Less wasteful than consing up a hundred closures on every call.)
-    cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
-
-    if (!state.lexical.hasOwnProperty("align"))
-      state.lexical.align = true;
-
-    while(true) {
-      var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
-      if (combinator(type, content)) {
-        while(cc.length && cc[cc.length - 1].lex)
-          cc.pop()();
-        if (cx.marked) return cx.marked;
-        if (type == "variable" && inScope(state, content)) return "variable-2";
-        return style;
-      }
-    }
-  }
-
-  // Combinator utils
-
-  var cx = {state: null, column: null, marked: null, cc: null};
-  function pass() {
-    for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
-  }
-  function cont() {
-    pass.apply(null, arguments);
-    return true;
-  }
-  function inList(name, list) {
-    for (var v = list; v; v = v.next) if (v.name == name) return true
-    return false;
-  }
-  function register(varname) {
-    var state = cx.state;
-    cx.marked = "def";
-    if (state.context) {
-      if (state.lexical.info == "var" && state.context && state.context.block) {
-        // FIXME function decls are also not block scoped
-        var newContext = registerVarScoped(varname, state.context)
-        if (newContext != null) {
-          state.context = newContext
-          return
-        }
-      } else if (!inList(varname, state.localVars)) {
-        state.localVars = new Var(varname, state.localVars)
-        return
-      }
-    }
-    // Fall through means this is global
-    if (parserConfig.globalVars && !inList(varname, state.globalVars))
-      state.globalVars = new Var(varname, state.globalVars)
-  }
-  function registerVarScoped(varname, context) {
-    if (!context) {
-      return null
-    } else if (context.block) {
-      var inner = registerVarScoped(varname, context.prev)
-      if (!inner) return null
-      if (inner == context.prev) return context
-      return new Context(inner, context.vars, true)
-    } else if (inList(varname, context.vars)) {
-      return context
-    } else {
-      return new Context(context.prev, new Var(varname, context.vars), false)
-    }
-  }
-
-  function isModifier(name) {
-    return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
-  }
-
-  // Combinators
-
-  function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block }
-  function Var(name, next) { this.name = name; this.next = next }
-
-  var defaultVars = new Var("this", new Var("arguments", null))
-  function pushcontext() {
-    cx.state.context = new Context(cx.state.context, cx.state.localVars, false)
-    cx.state.localVars = defaultVars
-  }
-  function pushblockcontext() {
-    cx.state.context = new Context(cx.state.context, cx.state.localVars, true)
-    cx.state.localVars = null
-  }
-  function popcontext() {
-    cx.state.localVars = cx.state.context.vars
-    cx.state.context = cx.state.context.prev
-  }
-  popcontext.lex = true
-  function pushlex(type, info) {
-    var result = function() {
-      var state = cx.state, indent = state.indented;
-      if (state.lexical.type == "stat") indent = state.lexical.indented;
-      else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
-        indent = outer.indented;
-      state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
-    };
-    result.lex = true;
-    return result;
-  }
-  function poplex() {
-    var state = cx.state;
-    if (state.lexical.prev) {
-      if (state.lexical.type == ")")
-        state.indented = state.lexical.indented;
-      state.lexical = state.lexical.prev;
-    }
-  }
-  poplex.lex = true;
-
-  function expect(wanted) {
-    function exp(type) {
-      if (type == wanted) return cont();
-      else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass();
-      else return cont(exp);
-    };
-    return exp;
-  }
-
-  function statement(type, value) {
-    if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex);
-    if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
-    if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
-    if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex);
-    if (type == "debugger") return cont(expect(";"));
-    if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext);
-    if (type == ";") return cont();
-    if (type == "if") {
-      if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
-        cx.state.cc.pop()();
-      return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
-    }
-    if (type == "function") return cont(functiondef);
-    if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
-    if (type == "class" || (isTS && value == "interface")) {
-      cx.marked = "keyword"
-      return cont(pushlex("form", type == "class" ? type : value), className, poplex)
-    }
-    if (type == "variable") {
-      if (isTS && value == "declare") {
-        cx.marked = "keyword"
-        return cont(statement)
-      } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) {
-        cx.marked = "keyword"
-        if (value == "enum") return cont(enumdef);
-        else if (value == "type") return cont(typename, expect("operator"), typeexpr, expect(";"));
-        else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
-      } else if (isTS && value == "namespace") {
-        cx.marked = "keyword"
-        return cont(pushlex("form"), expression, statement, poplex)
-      } else if (isTS && value == "abstract") {
-        cx.marked = "keyword"
-        return cont(statement)
-      } else {
-        return cont(pushlex("stat"), maybelabel);
-      }
-    }
-    if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext,
-                                      block, poplex, poplex, popcontext);
-    if (type == "case") return cont(expression, expect(":"));
-    if (type == "default") return cont(expect(":"));
-    if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext);
-    if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
-    if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
-    if (type == "async") return cont(statement)
-    if (value == "@") return cont(expression, statement)
-    return pass(pushlex("stat"), expression, expect(";"), poplex);
-  }
-  function maybeCatchBinding(type) {
-    if (type == "(") return cont(funarg, expect(")"))
-  }
-  function expression(type, value) {
-    return expressionInner(type, value, false);
-  }
-  function expressionNoComma(type, value) {
-    return expressionInner(type, value, true);
-  }
-  function parenExpr(type) {
-    if (type != "(") return pass()
-    return cont(pushlex(")"), maybeexpression, expect(")"), poplex)
-  }
-  function expressionInner(type, value, noComma) {
-    if (cx.state.fatArrowAt == cx.stream.start) {
-      var body = noComma ? arrowBodyNoComma : arrowBody;
-      if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
-      else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
-    }
-
-    var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
-    if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
-    if (type == "function") return cont(functiondef, maybeop);
-    if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
-    if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
-    if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
-    if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
-    if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
-    if (type == "{") return contCommasep(objprop, "}", null, maybeop);
-    if (type == "quasi") return pass(quasi, maybeop);
-    if (type == "new") return cont(maybeTarget(noComma));
-    if (type == "import") return cont(expression);
-    return cont();
-  }
-  function maybeexpression(type) {
-    if (type.match(/[;\}\)\],]/)) return pass();
-    return pass(expression);
-  }
-
-  function maybeoperatorComma(type, value) {
-    if (type == ",") return cont(maybeexpression);
-    return maybeoperatorNoComma(type, value, false);
-  }
-  function maybeoperatorNoComma(type, value, noComma) {
-    var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
-    var expr = noComma == false ? expression : expressionNoComma;
-    if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
-    if (type == "operator") {
-      if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
-      if (isTS && value == "<" && cx.stream.match(/^([^<>]|<[^<>]*>)*>\s*\(/, false))
-        return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me);
-      if (value == "?") return cont(expression, expect(":"), expr);
-      return cont(expr);
-    }
-    if (type == "quasi") { return pass(quasi, me); }
-    if (type == ";") return;
-    if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
-    if (type == ".") return cont(property, me);
-    if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
-    if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
-    if (type == "regexp") {
-      cx.state.lastType = cx.marked = "operator"
-      cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
-      return cont(expr)
-    }
-  }
-  function quasi(type, value) {
-    if (type != "quasi") return pass();
-    if (value.slice(value.length - 2) != "${") return cont(quasi);
-    return cont(expression, continueQuasi);
-  }
-  function continueQuasi(type) {
-    if (type == "}") {
-      cx.marked = "string-2";
-      cx.state.tokenize = tokenQuasi;
-      return cont(quasi);
-    }
-  }
-  function arrowBody(type) {
-    findFatArrow(cx.stream, cx.state);
-    return pass(type == "{" ? statement : expression);
-  }
-  function arrowBodyNoComma(type) {
-    findFatArrow(cx.stream, cx.state);
-    return pass(type == "{" ? statement : expressionNoComma);
-  }
-  function maybeTarget(noComma) {
-    return function(type) {
-      if (type == ".") return cont(noComma ? targetNoComma : target);
-      else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)
-      else return pass(noComma ? expressionNoComma : expression);
-    };
-  }
-  function target(_, value) {
-    if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
-  }
-  function targetNoComma(_, value) {
-    if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
-  }
-  function maybelabel(type) {
-    if (type == ":") return cont(poplex, statement);
-    return pass(maybeoperatorComma, expect(";"), poplex);
-  }
-  function property(type) {
-    if (type == "variable") {cx.marked = "property"; return cont();}
-  }
-  function objprop(type, value) {
-    if (type == "async") {
-      cx.marked = "property";
-      return cont(objprop);
-    } else if (type == "variable" || cx.style == "keyword") {
-      cx.marked = "property";
-      if (value == "get" || value == "set") return cont(getterSetter);
-      var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params
-      if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
-        cx.state.fatArrowAt = cx.stream.pos + m[0].length
-      return cont(afterprop);
-    } else if (type == "number" || type == "string") {
-      cx.marked = jsonldMode ? "property" : (cx.style + " property");
-      return cont(afterprop);
-    } else if (type == "jsonld-keyword") {
-      return cont(afterprop);
-    } else if (isTS && isModifier(value)) {
-      cx.marked = "keyword"
-      return cont(objprop)
-    } else if (type == "[") {
-      return cont(expression, maybetype, expect("]"), afterprop);
-    } else if (type == "spread") {
-      return cont(expressionNoComma, afterprop);
-    } else if (value == "*") {
-      cx.marked = "keyword";
-      return cont(objprop);
-    } else if (type == ":") {
-      return pass(afterprop)
-    }
-  }
-  function getterSetter(type) {
-    if (type != "variable") return pass(afterprop);
-    cx.marked = "property";
-    return cont(functiondef);
-  }
-  function afterprop(type) {
-    if (type == ":") return cont(expressionNoComma);
-    if (type == "(") return pass(functiondef);
-  }
-  function commasep(what, end, sep) {
-    function proceed(type, value) {
-      if (sep ? sep.indexOf(type) > -1 : type == ",") {
-        var lex = cx.state.lexical;
-        if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
-        return cont(function(type, value) {
-          if (type == end || value == end) return pass()
-          return pass(what)
-        }, proceed);
-      }
-      if (type == end || value == end) return cont();
-      if (sep && sep.indexOf(";") > -1) return pass(what)
-      return cont(expect(end));
-    }
-    return function(type, value) {
-      if (type == end || value == end) return cont();
-      return pass(what, proceed);
-    };
-  }
-  function contCommasep(what, end, info) {
-    for (var i = 3; i < arguments.length; i++)
-      cx.cc.push(arguments[i]);
-    return cont(pushlex(end, info), commasep(what, end), poplex);
-  }
-  function block(type) {
-    if (type == "}") return cont();
-    return pass(statement, block);
-  }
-  function maybetype(type, value) {
-    if (isTS) {
-      if (type == ":") return cont(typeexpr);
-      if (value == "?") return cont(maybetype);
-    }
-  }
-  function maybetypeOrIn(type, value) {
-    if (isTS && (type == ":" || value == "in")) return cont(typeexpr)
-  }
-  function mayberettype(type) {
-    if (isTS && type == ":") {
-      if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr)
-      else return cont(typeexpr)
-    }
-  }
-  function isKW(_, value) {
-    if (value == "is") {
-      cx.marked = "keyword"
-      return cont()
-    }
-  }
-  function typeexpr(type, value) {
-    if (value == "keyof" || value == "typeof" || value == "infer") {
-      cx.marked = "keyword"
-      return cont(value == "typeof" ? expressionNoComma : typeexpr)
-    }
-    if (type == "variable" || value == "void") {
-      cx.marked = "type"
-      return cont(afterType)
-    }
-    if (value == "|" || value == "&") return cont(typeexpr)
-    if (type == "string" || type == "number" || type == "atom") return cont(afterType);
-    if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType)
-    if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType)
-    if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType, afterType)
-    if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr)
-  }
-  function maybeReturnType(type) {
-    if (type == "=>") return cont(typeexpr)
-  }
-  function typeprop(type, value) {
-    if (type == "variable" || cx.style == "keyword") {
-      cx.marked = "property"
-      return cont(typeprop)
-    } else if (value == "?" || type == "number" || type == "string") {
-      return cont(typeprop)
-    } else if (type == ":") {
-      return cont(typeexpr)
-    } else if (type == "[") {
-      return cont(expect("variable"), maybetypeOrIn, expect("]"), typeprop)
-    } else if (type == "(") {
-      return pass(functiondecl, typeprop)
-    }
-  }
-  function typearg(type, value) {
-    if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg)
-    if (type == ":") return cont(typeexpr)
-    if (type == "spread") return cont(typearg)
-    return pass(typeexpr)
-  }
-  function afterType(type, value) {
-    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
-    if (value == "|" || type == "." || value == "&") return cont(typeexpr)
-    if (type == "[") return cont(typeexpr, expect("]"), afterType)
-    if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
-    if (value == "?") return cont(typeexpr, expect(":"), typeexpr)
-  }
-  function maybeTypeArgs(_, value) {
-    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
-  }
-  function typeparam() {
-    return pass(typeexpr, maybeTypeDefault)
-  }
-  function maybeTypeDefault(_, value) {
-    if (value == "=") return cont(typeexpr)
-  }
-  function vardef(_, value) {
-    if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)}
-    return pass(pattern, maybetype, maybeAssign, vardefCont);
-  }
-  function pattern(type, value) {
-    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
-    if (type == "variable") { register(value); return cont(); }
-    if (type == "spread") return cont(pattern);
-    if (type == "[") return contCommasep(eltpattern, "]");
-    if (type == "{") return contCommasep(proppattern, "}");
-  }
-  function proppattern(type, value) {
-    if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
-      register(value);
-      return cont(maybeAssign);
-    }
-    if (type == "variable") cx.marked = "property";
-    if (type == "spread") return cont(pattern);
-    if (type == "}") return pass();
-    if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern);
-    return cont(expect(":"), pattern, maybeAssign);
-  }
-  function eltpattern() {
-    return pass(pattern, maybeAssign)
-  }
-  function maybeAssign(_type, value) {
-    if (value == "=") return cont(expressionNoComma);
-  }
-  function vardefCont(type) {
-    if (type == ",") return cont(vardef);
-  }
-  function maybeelse(type, value) {
-    if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
-  }
-  function forspec(type, value) {
-    if (value == "await") return cont(forspec);
-    if (type == "(") return cont(pushlex(")"), forspec1, poplex);
-  }
-  function forspec1(type) {
-    if (type == "var") return cont(vardef, forspec2);
-    if (type == "variable") return cont(forspec2);
-    return pass(forspec2)
-  }
-  function forspec2(type, value) {
-    if (type == ")") return cont()
-    if (type == ";") return cont(forspec2)
-    if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression, forspec2) }
-    return pass(expression, forspec2)
-  }
-  function functiondef(type, value) {
-    if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
-    if (type == "variable") {register(value); return cont(functiondef);}
-    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
-    if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
-  }
-  function functiondecl(type, value) {
-    if (value == "*") {cx.marked = "keyword"; return cont(functiondecl);}
-    if (type == "variable") {register(value); return cont(functiondecl);}
-    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext);
-    if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl)
-  }
-  function typename(type, value) {
-    if (type == "keyword" || type == "variable") {
-      cx.marked = "type"
-      return cont(typename)
-    } else if (value == "<") {
-      return cont(pushlex(">"), commasep(typeparam, ">"), poplex)
-    }
-  }
-  function funarg(type, value) {
-    if (value == "@") cont(expression, funarg)
-    if (type == "spread") return cont(funarg);
-    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
-    if (isTS && type == "this") return cont(maybetype, maybeAssign)
-    return pass(pattern, maybetype, maybeAssign);
-  }
-  function classExpression(type, value) {
-    // Class expressions may have an optional name.
-    if (type == "variable") return className(type, value);
-    return classNameAfter(type, value);
-  }
-  function className(type, value) {
-    if (type == "variable") {register(value); return cont(classNameAfter);}
-  }
-  function classNameAfter(type, value) {
-    if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
-    if (value == "extends" || value == "implements" || (isTS && type == ",")) {
-      if (value == "implements") cx.marked = "keyword";
-      return cont(isTS ? typeexpr : expression, classNameAfter);
-    }
-    if (type == "{") return cont(pushlex("}"), classBody, poplex);
-  }
-  function classBody(type, value) {
-    if (type == "async" ||
-        (type == "variable" &&
-         (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
-         cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
-      cx.marked = "keyword";
-      return cont(classBody);
-    }
-    if (type == "variable" || cx.style == "keyword") {
-      cx.marked = "property";
-      return cont(classfield, classBody);
-    }
-    if (type == "number" || type == "string") return cont(classfield, classBody);
-    if (type == "[")
-      return cont(expression, maybetype, expect("]"), classfield, classBody)
-    if (value == "*") {
-      cx.marked = "keyword";
-      return cont(classBody);
-    }
-    if (isTS && type == "(") return pass(functiondecl, classBody)
-    if (type == ";" || type == ",") return cont(classBody);
-    if (type == "}") return cont();
-    if (value == "@") return cont(expression, classBody)
-  }
-  function classfield(type, value) {
-    if (value == "?") return cont(classfield)
-    if (type == ":") return cont(typeexpr, maybeAssign)
-    if (value == "=") return cont(expressionNoComma)
-    var context = cx.state.lexical.prev, isInterface = context && context.info == "interface"
-    return pass(isInterface ? functiondecl : functiondef)
-  }
-  function afterExport(type, value) {
-    if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
-    if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
-    if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
-    return pass(statement);
-  }
-  function exportField(type, value) {
-    if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
-    if (type == "variable") return pass(expressionNoComma, exportField);
-  }
-  function afterImport(type) {
-    if (type == "string") return cont();
-    if (type == "(") return pass(expression);
-    return pass(importSpec, maybeMoreImports, maybeFrom);
-  }
-  function importSpec(type, value) {
-    if (type == "{") return contCommasep(importSpec, "}");
-    if (type == "variable") register(value);
-    if (value == "*") cx.marked = "keyword";
-    return cont(maybeAs);
-  }
-  function maybeMoreImports(type) {
-    if (type == ",") return cont(importSpec, maybeMoreImports)
-  }
-  function maybeAs(_type, value) {
-    if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
-  }
-  function maybeFrom(_type, value) {
-    if (value == "from") { cx.marked = "keyword"; return cont(expression); }
-  }
-  function arrayLiteral(type) {
-    if (type == "]") return cont();
-    return pass(commasep(expressionNoComma, "]"));
-  }
-  function enumdef() {
-    return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex)
-  }
-  function enummember() {
-    return pass(pattern, maybeAssign);
-  }
-
-  function isContinuedStatement(state, textAfter) {
-    return state.lastType == "operator" || state.lastType == "," ||
-      isOperatorChar.test(textAfter.charAt(0)) ||
-      /[,.]/.test(textAfter.charAt(0));
-  }
-
-  function expressionAllowed(stream, state, backUp) {
-    return state.tokenize == tokenBase &&
-      /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
-      (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
-  }
-
-  // Interface
-
-  return {
-    startState: function(basecolumn) {
-      var state = {
-        tokenize: tokenBase,
-        lastType: "sof",
-        cc: [],
-        lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
-        localVars: parserConfig.localVars,
-        context: parserConfig.localVars && new Context(null, null, false),
-        indented: basecolumn || 0
-      };
-      if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
-        state.globalVars = parserConfig.globalVars;
-      return state;
-    },
-
-    token: function(stream, state) {
-      if (stream.sol()) {
-        if (!state.lexical.hasOwnProperty("align"))
-          state.lexical.align = false;
-        state.indented = stream.indentation();
-        findFatArrow(stream, state);
-      }
-      if (state.tokenize != tokenComment && stream.eatSpace()) return null;
-      var style = state.tokenize(stream, state);
-      if (type == "comment") return style;
-      state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
-      return parseJS(state, style, type, content, stream);
-    },
-
-    indent: function(state, textAfter) {
-      if (state.tokenize == tokenComment) return CodeMirror.Pass;
-      if (state.tokenize != tokenBase) return 0;
-      var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
-      // Kludge to prevent 'maybelse' from blocking lexical scope pops
-      if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
-        var c = state.cc[i];
-        if (c == poplex) lexical = lexical.prev;
-        else if (c != maybeelse) break;
-      }
-      while ((lexical.type == "stat" || lexical.type == "form") &&
-             (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
-                                   (top == maybeoperatorComma || top == maybeoperatorNoComma) &&
-                                   !/^[,\.=+\-*:?[\(]/.test(textAfter))))
-        lexical = lexical.prev;
-      if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
-        lexical = lexical.prev;
-      var type = lexical.type, closing = firstChar == type;
-
-      if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0);
-      else if (type == "form" && firstChar == "{") return lexical.indented;
-      else if (type == "form") return lexical.indented + indentUnit;
-      else if (type == "stat")
-        return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
-      else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
-        return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
-      else if (lexical.align) return lexical.column + (closing ? 0 : 1);
-      else return lexical.indented + (closing ? 0 : indentUnit);
-    },
-
-    electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
-    blockCommentStart: jsonMode ? null : "/*",
-    blockCommentEnd: jsonMode ? null : "*/",
-    blockCommentContinue: jsonMode ? null : " * ",
-    lineComment: jsonMode ? null : "//",
-    fold: "brace",
-    closeBrackets: "()[]{}''\"\"``",
-
-    helperType: jsonMode ? "json" : "javascript",
-    jsonldMode: jsonldMode,
-    jsonMode: jsonMode,
-
-    expressionAllowed: expressionAllowed,
-
-    skipExpression: function(state) {
-      var top = state.cc[state.cc.length - 1]
-      if (top == expression || top == expressionNoComma) state.cc.pop()
-    }
-  };
-});
-
-CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
-
-CodeMirror.defineMIME("text/javascript", "javascript");
-CodeMirror.defineMIME("text/ecmascript", "javascript");
-CodeMirror.defineMIME("application/javascript", "javascript");
-CodeMirror.defineMIME("application/x-javascript", "javascript");
-CodeMirror.defineMIME("application/ecmascript", "javascript");
-CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
-CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
-CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
-CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
-CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
-
-});
diff --git a/plugins/UiFileManager/media/codemirror/mode/markdown.js b/plugins/UiFileManager/media/codemirror/mode/markdown.js
deleted file mode 100644
index 287f39b5..00000000
--- a/plugins/UiFileManager/media/codemirror/mode/markdown.js
+++ /dev/null
@@ -1,886 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("../xml/xml"), require("../meta"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "../xml/xml", "../meta"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
-
-  var htmlMode = CodeMirror.getMode(cmCfg, "text/html");
-  var htmlModeMissing = htmlMode.name == "null"
-
-  function getMode(name) {
-    if (CodeMirror.findModeByName) {
-      var found = CodeMirror.findModeByName(name);
-      if (found) name = found.mime || found.mimes[0];
-    }
-    var mode = CodeMirror.getMode(cmCfg, name);
-    return mode.name == "null" ? null : mode;
-  }
-
-  // Should characters that affect highlighting be highlighted separate?
-  // Does not include characters that will be output (such as `1.` and `-` for lists)
-  if (modeCfg.highlightFormatting === undefined)
-    modeCfg.highlightFormatting = false;
-
-  // Maximum number of nested blockquotes. Set to 0 for infinite nesting.
-  // Excess `>` will emit `error` token.
-  if (modeCfg.maxBlockquoteDepth === undefined)
-    modeCfg.maxBlockquoteDepth = 0;
-
-  // Turn on task lists? ("- [ ] " and "- [x] ")
-  if (modeCfg.taskLists === undefined) modeCfg.taskLists = false;
-
-  // Turn on strikethrough syntax
-  if (modeCfg.strikethrough === undefined)
-    modeCfg.strikethrough = false;
-
-  if (modeCfg.emoji === undefined)
-    modeCfg.emoji = false;
-
-  if (modeCfg.fencedCodeBlockHighlighting === undefined)
-    modeCfg.fencedCodeBlockHighlighting = true;
-
-  if (modeCfg.fencedCodeBlockDefaultMode === undefined)
-    modeCfg.fencedCodeBlockDefaultMode = 'text/plain';
-
-  if (modeCfg.xml === undefined)
-    modeCfg.xml = true;
-
-  // Allow token types to be overridden by user-provided token types.
-  if (modeCfg.tokenTypeOverrides === undefined)
-    modeCfg.tokenTypeOverrides = {};
-
-  var tokenTypes = {
-    header: "header",
-    code: "comment",
-    quote: "quote",
-    list1: "variable-2",
-    list2: "variable-3",
-    list3: "keyword",
-    hr: "hr",
-    image: "image",
-    imageAltText: "image-alt-text",
-    imageMarker: "image-marker",
-    formatting: "formatting",
-    linkInline: "link",
-    linkEmail: "link",
-    linkText: "link",
-    linkHref: "string",
-    em: "em",
-    strong: "strong",
-    strikethrough: "strikethrough",
-    emoji: "builtin"
-  };
-
-  for (var tokenType in tokenTypes) {
-    if (tokenTypes.hasOwnProperty(tokenType) && modeCfg.tokenTypeOverrides[tokenType]) {
-      tokenTypes[tokenType] = modeCfg.tokenTypeOverrides[tokenType];
-    }
-  }
-
-  var hrRE = /^([*\-_])(?:\s*\1){2,}\s*$/
-  ,   listRE = /^(?:[*\-+]|^[0-9]+([.)]))\s+/
-  ,   taskListRE = /^\[(x| )\](?=\s)/i // Must follow listRE
-  ,   atxHeaderRE = modeCfg.allowAtxHeaderWithoutSpace ? /^(#+)/ : /^(#+)(?: |$)/
-  ,   setextHeaderRE = /^ {0,3}(?:\={1,}|-{2,})\s*$/
-  ,   textRE = /^[^#!\[\]*_\\<>` "'(~:]+/
-  ,   fencedCodeRE = /^(~~~+|```+)[ \t]*([\w\/+#-]*)[^\n`]*$/
-  ,   linkDefRE = /^\s*\[[^\]]+?\]:.*$/ // naive link-definition
-  ,   punctuation = /[!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E42\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC9\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDF3C-\uDF3E]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]/
-  ,   expandedTab = "    " // CommonMark specifies tab as 4 spaces
-
-  function switchInline(stream, state, f) {
-    state.f = state.inline = f;
-    return f(stream, state);
-  }
-
-  function switchBlock(stream, state, f) {
-    state.f = state.block = f;
-    return f(stream, state);
-  }
-
-  function lineIsEmpty(line) {
-    return !line || !/\S/.test(line.string)
-  }
-
-  // Blocks
-
-  function blankLine(state) {
-    // Reset linkTitle state
-    state.linkTitle = false;
-    state.linkHref = false;
-    state.linkText = false;
-    // Reset EM state
-    state.em = false;
-    // Reset STRONG state
-    state.strong = false;
-    // Reset strikethrough state
-    state.strikethrough = false;
-    // Reset state.quote
-    state.quote = 0;
-    // Reset state.indentedCode
-    state.indentedCode = false;
-    if (state.f == htmlBlock) {
-      var exit = htmlModeMissing
-      if (!exit) {
-        var inner = CodeMirror.innerMode(htmlMode, state.htmlState)
-        exit = inner.mode.name == "xml" && inner.state.tagStart === null &&
-          (!inner.state.context && inner.state.tokenize.isInText)
-      }
-      if (exit) {
-        state.f = inlineNormal;
-        state.block = blockNormal;
-        state.htmlState = null;
-      }
-    }
-    // Reset state.trailingSpace
-    state.trailingSpace = 0;
-    state.trailingSpaceNewLine = false;
-    // Mark this line as blank
-    state.prevLine = state.thisLine
-    state.thisLine = {stream: null}
-    return null;
-  }
-
-  function blockNormal(stream, state) {
-    var firstTokenOnLine = stream.column() === state.indentation;
-    var prevLineLineIsEmpty = lineIsEmpty(state.prevLine.stream);
-    var prevLineIsIndentedCode = state.indentedCode;
-    var prevLineIsHr = state.prevLine.hr;
-    var prevLineIsList = state.list !== false;
-    var maxNonCodeIndentation = (state.listStack[state.listStack.length - 1] || 0) + 3;
-
-    state.indentedCode = false;
-
-    var lineIndentation = state.indentation;
-    // compute once per line (on first token)
-    if (state.indentationDiff === null) {
-      state.indentationDiff = state.indentation;
-      if (prevLineIsList) {
-        state.list = null;
-        // While this list item's marker's indentation is less than the deepest
-        //  list item's content's indentation,pop the deepest list item
-        //  indentation off the stack, and update block indentation state
-        while (lineIndentation < state.listStack[state.listStack.length - 1]) {
-          state.listStack.pop();
-          if (state.listStack.length) {
-            state.indentation = state.listStack[state.listStack.length - 1];
-          // less than the first list's indent -> the line is no longer a list
-          } else {
-            state.list = false;
-          }
-        }
-        if (state.list !== false) {
-          state.indentationDiff = lineIndentation - state.listStack[state.listStack.length - 1]
-        }
-      }
-    }
-
-    // not comprehensive (currently only for setext detection purposes)
-    var allowsInlineContinuation = (
-        !prevLineLineIsEmpty && !prevLineIsHr && !state.prevLine.header &&
-        (!prevLineIsList || !prevLineIsIndentedCode) &&
-        !state.prevLine.fencedCodeEnd
-    );
-
-    var isHr = (state.list === false || prevLineIsHr || prevLineLineIsEmpty) &&
-      state.indentation <= maxNonCodeIndentation && stream.match(hrRE);
-
-    var match = null;
-    if (state.indentationDiff >= 4 && (prevLineIsIndentedCode || state.prevLine.fencedCodeEnd ||
-         state.prevLine.header || prevLineLineIsEmpty)) {
-      stream.skipToEnd();
-      state.indentedCode = true;
-      return tokenTypes.code;
-    } else if (stream.eatSpace()) {
-      return null;
-    } else if (firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(atxHeaderRE)) && match[1].length <= 6) {
-      state.quote = 0;
-      state.header = match[1].length;
-      state.thisLine.header = true;
-      if (modeCfg.highlightFormatting) state.formatting = "header";
-      state.f = state.inline;
-      return getType(state);
-    } else if (state.indentation <= maxNonCodeIndentation && stream.eat('>')) {
-      state.quote = firstTokenOnLine ? 1 : state.quote + 1;
-      if (modeCfg.highlightFormatting) state.formatting = "quote";
-      stream.eatSpace();
-      return getType(state);
-    } else if (!isHr && !state.setext && firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(listRE))) {
-      var listType = match[1] ? "ol" : "ul";
-
-      state.indentation = lineIndentation + stream.current().length;
-      state.list = true;
-      state.quote = 0;
-
-      // Add this list item's content's indentation to the stack
-      state.listStack.push(state.indentation);
-      // Reset inline styles which shouldn't propagate aross list items
-      state.em = false;
-      state.strong = false;
-      state.code = false;
-      state.strikethrough = false;
-
-      if (modeCfg.taskLists && stream.match(taskListRE, false)) {
-        state.taskList = true;
-      }
-      state.f = state.inline;
-      if (modeCfg.highlightFormatting) state.formatting = ["list", "list-" + listType];
-      return getType(state);
-    } else if (firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(fencedCodeRE, true))) {
-      state.quote = 0;
-      state.fencedEndRE = new RegExp(match[1] + "+ *$");
-      // try switching mode
-      state.localMode = modeCfg.fencedCodeBlockHighlighting && getMode(match[2] || modeCfg.fencedCodeBlockDefaultMode );
-      if (state.localMode) state.localState = CodeMirror.startState(state.localMode);
-      state.f = state.block = local;
-      if (modeCfg.highlightFormatting) state.formatting = "code-block";
-      state.code = -1
-      return getType(state);
-    // SETEXT has lowest block-scope precedence after HR, so check it after
-    //  the others (code, blockquote, list...)
-    } else if (
-      // if setext set, indicates line after ---/===
-      state.setext || (
-        // line before ---/===
-        (!allowsInlineContinuation || !prevLineIsList) && !state.quote && state.list === false &&
-        !state.code && !isHr && !linkDefRE.test(stream.string) &&
-        (match = stream.lookAhead(1)) && (match = match.match(setextHeaderRE))
-      )
-    ) {
-      if ( !state.setext ) {
-        state.header = match[0].charAt(0) == '=' ? 1 : 2;
-        state.setext = state.header;
-      } else {
-        state.header = state.setext;
-        // has no effect on type so we can reset it now
-        state.setext = 0;
-        stream.skipToEnd();
-        if (modeCfg.highlightFormatting) state.formatting = "header";
-      }
-      state.thisLine.header = true;
-      state.f = state.inline;
-      return getType(state);
-    } else if (isHr) {
-      stream.skipToEnd();
-      state.hr = true;
-      state.thisLine.hr = true;
-      return tokenTypes.hr;
-    } else if (stream.peek() === '[') {
-      return switchInline(stream, state, footnoteLink);
-    }
-
-    return switchInline(stream, state, state.inline);
-  }
-
-  function htmlBlock(stream, state) {
-    var style = htmlMode.token(stream, state.htmlState);
-    if (!htmlModeMissing) {
-      var inner = CodeMirror.innerMode(htmlMode, state.htmlState)
-      if ((inner.mode.name == "xml" && inner.state.tagStart === null &&
-           (!inner.state.context && inner.state.tokenize.isInText)) ||
-          (state.md_inside && stream.current().indexOf(">") > -1)) {
-        state.f = inlineNormal;
-        state.block = blockNormal;
-        state.htmlState = null;
-      }
-    }
-    return style;
-  }
-
-  function local(stream, state) {
-    var currListInd = state.listStack[state.listStack.length - 1] || 0;
-    var hasExitedList = state.indentation < currListInd;
-    var maxFencedEndInd = currListInd + 3;
-    if (state.fencedEndRE && state.indentation <= maxFencedEndInd && (hasExitedList || stream.match(state.fencedEndRE))) {
-      if (modeCfg.highlightFormatting) state.formatting = "code-block";
-      var returnType;
-      if (!hasExitedList) returnType = getType(state)
-      state.localMode = state.localState = null;
-      state.block = blockNormal;
-      state.f = inlineNormal;
-      state.fencedEndRE = null;
-      state.code = 0
-      state.thisLine.fencedCodeEnd = true;
-      if (hasExitedList) return switchBlock(stream, state, state.block);
-      return returnType;
-    } else if (state.localMode) {
-      return state.localMode.token(stream, state.localState);
-    } else {
-      stream.skipToEnd();
-      return tokenTypes.code;
-    }
-  }
-
-  // Inline
-  function getType(state) {
-    var styles = [];
-
-    if (state.formatting) {
-      styles.push(tokenTypes.formatting);
-
-      if (typeof state.formatting === "string") state.formatting = [state.formatting];
-
-      for (var i = 0; i < state.formatting.length; i++) {
-        styles.push(tokenTypes.formatting + "-" + state.formatting[i]);
-
-        if (state.formatting[i] === "header") {
-          styles.push(tokenTypes.formatting + "-" + state.formatting[i] + "-" + state.header);
-        }
-
-        // Add `formatting-quote` and `formatting-quote-#` for blockquotes
-        // Add `error` instead if the maximum blockquote nesting depth is passed
-        if (state.formatting[i] === "quote") {
-          if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
-            styles.push(tokenTypes.formatting + "-" + state.formatting[i] + "-" + state.quote);
-          } else {
-            styles.push("error");
-          }
-        }
-      }
-    }
-
-    if (state.taskOpen) {
-      styles.push("meta");
-      return styles.length ? styles.join(' ') : null;
-    }
-    if (state.taskClosed) {
-      styles.push("property");
-      return styles.length ? styles.join(' ') : null;
-    }
-
-    if (state.linkHref) {
-      styles.push(tokenTypes.linkHref, "url");
-    } else { // Only apply inline styles to non-url text
-      if (state.strong) { styles.push(tokenTypes.strong); }
-      if (state.em) { styles.push(tokenTypes.em); }
-      if (state.strikethrough) { styles.push(tokenTypes.strikethrough); }
-      if (state.emoji) { styles.push(tokenTypes.emoji); }
-      if (state.linkText) { styles.push(tokenTypes.linkText); }
-      if (state.code) { styles.push(tokenTypes.code); }
-      if (state.image) { styles.push(tokenTypes.image); }
-      if (state.imageAltText) { styles.push(tokenTypes.imageAltText, "link"); }
-      if (state.imageMarker) { styles.push(tokenTypes.imageMarker); }
-    }
-
-    if (state.header) { styles.push(tokenTypes.header, tokenTypes.header + "-" + state.header); }
-
-    if (state.quote) {
-      styles.push(tokenTypes.quote);
-
-      // Add `quote-#` where the maximum for `#` is modeCfg.maxBlockquoteDepth
-      if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
-        styles.push(tokenTypes.quote + "-" + state.quote);
-      } else {
-        styles.push(tokenTypes.quote + "-" + modeCfg.maxBlockquoteDepth);
-      }
-    }
-
-    if (state.list !== false) {
-      var listMod = (state.listStack.length - 1) % 3;
-      if (!listMod) {
-        styles.push(tokenTypes.list1);
-      } else if (listMod === 1) {
-        styles.push(tokenTypes.list2);
-      } else {
-        styles.push(tokenTypes.list3);
-      }
-    }
-
-    if (state.trailingSpaceNewLine) {
-      styles.push("trailing-space-new-line");
-    } else if (state.trailingSpace) {
-      styles.push("trailing-space-" + (state.trailingSpace % 2 ? "a" : "b"));
-    }
-
-    return styles.length ? styles.join(' ') : null;
-  }
-
-  function handleText(stream, state) {
-    if (stream.match(textRE, true)) {
-      return getType(state);
-    }
-    return undefined;
-  }
-
-  function inlineNormal(stream, state) {
-    var style = state.text(stream, state);
-    if (typeof style !== 'undefined')
-      return style;
-
-    if (state.list) { // List marker (*, +, -, 1., etc)
-      state.list = null;
-      return getType(state);
-    }
-
-    if (state.taskList) {
-      var taskOpen = stream.match(taskListRE, true)[1] === " ";
-      if (taskOpen) state.taskOpen = true;
-      else state.taskClosed = true;
-      if (modeCfg.highlightFormatting) state.formatting = "task";
-      state.taskList = false;
-      return getType(state);
-    }
-
-    state.taskOpen = false;
-    state.taskClosed = false;
-
-    if (state.header && stream.match(/^#+$/, true)) {
-      if (modeCfg.highlightFormatting) state.formatting = "header";
-      return getType(state);
-    }
-
-    var ch = stream.next();
-
-    // Matches link titles present on next line
-    if (state.linkTitle) {
-      state.linkTitle = false;
-      var matchCh = ch;
-      if (ch === '(') {
-        matchCh = ')';
-      }
-      matchCh = (matchCh+'').replace(/([.?*+^\[\]\\(){}|-])/g, "\\$1");
-      var regex = '^\\s*(?:[^' + matchCh + '\\\\]+|\\\\\\\\|\\\\.)' + matchCh;
-      if (stream.match(new RegExp(regex), true)) {
-        return tokenTypes.linkHref;
-      }
-    }
-
-    // If this block is changed, it may need to be updated in GFM mode
-    if (ch === '`') {
-      var previousFormatting = state.formatting;
-      if (modeCfg.highlightFormatting) state.formatting = "code";
-      stream.eatWhile('`');
-      var count = stream.current().length
-      if (state.code == 0 && (!state.quote || count == 1)) {
-        state.code = count
-        return getType(state)
-      } else if (count == state.code) { // Must be exact
-        var t = getType(state)
-        state.code = 0
-        return t
-      } else {
-        state.formatting = previousFormatting
-        return getType(state)
-      }
-    } else if (state.code) {
-      return getType(state);
-    }
-
-    if (ch === '\\') {
-      stream.next();
-      if (modeCfg.highlightFormatting) {
-        var type = getType(state);
-        var formattingEscape = tokenTypes.formatting + "-escape";
-        return type ? type + " " + formattingEscape : formattingEscape;
-      }
-    }
-
-    if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) {
-      state.imageMarker = true;
-      state.image = true;
-      if (modeCfg.highlightFormatting) state.formatting = "image";
-      return getType(state);
-    }
-
-    if (ch === '[' && state.imageMarker && stream.match(/[^\]]*\](\(.*?\)| ?\[.*?\])/, false)) {
-      state.imageMarker = false;
-      state.imageAltText = true
-      if (modeCfg.highlightFormatting) state.formatting = "image";
-      return getType(state);
-    }
-
-    if (ch === ']' && state.imageAltText) {
-      if (modeCfg.highlightFormatting) state.formatting = "image";
-      var type = getType(state);
-      state.imageAltText = false;
-      state.image = false;
-      state.inline = state.f = linkHref;
-      return type;
-    }
-
-    if (ch === '[' && !state.image) {
-      if (state.linkText && stream.match(/^.*?\]/)) return getType(state)
-      state.linkText = true;
-      if (modeCfg.highlightFormatting) state.formatting = "link";
-      return getType(state);
-    }
-
-    if (ch === ']' && state.linkText) {
-      if (modeCfg.highlightFormatting) state.formatting = "link";
-      var type = getType(state);
-      state.linkText = false;
-      state.inline = state.f = stream.match(/\(.*?\)| ?\[.*?\]/, false) ? linkHref : inlineNormal
-      return type;
-    }
-
-    if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, false)) {
-      state.f = state.inline = linkInline;
-      if (modeCfg.highlightFormatting) state.formatting = "link";
-      var type = getType(state);
-      if (type){
-        type += " ";
-      } else {
-        type = "";
-      }
-      return type + tokenTypes.linkInline;
-    }
-
-    if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, false)) {
-      state.f = state.inline = linkInline;
-      if (modeCfg.highlightFormatting) state.formatting = "link";
-      var type = getType(state);
-      if (type){
-        type += " ";
-      } else {
-        type = "";
-      }
-      return type + tokenTypes.linkEmail;
-    }
-
-    if (modeCfg.xml && ch === '<' && stream.match(/^(!--|\?|!\[CDATA\[|[a-z][a-z0-9-]*(?:\s+[a-z_:.\-]+(?:\s*=\s*[^>]+)?)*\s*(?:>|$))/i, false)) {
-      var end = stream.string.indexOf(">", stream.pos);
-      if (end != -1) {
-        var atts = stream.string.substring(stream.start, end);
-        if (/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(atts)) state.md_inside = true;
-      }
-      stream.backUp(1);
-      state.htmlState = CodeMirror.startState(htmlMode);
-      return switchBlock(stream, state, htmlBlock);
-    }
-
-    if (modeCfg.xml && ch === '<' && stream.match(/^\/\w*?>/)) {
-      state.md_inside = false;
-      return "tag";
-    } else if (ch === "*" || ch === "_") {
-      var len = 1, before = stream.pos == 1 ? " " : stream.string.charAt(stream.pos - 2)
-      while (len < 3 && stream.eat(ch)) len++
-      var after = stream.peek() || " "
-      // See http://spec.commonmark.org/0.27/#emphasis-and-strong-emphasis
-      var leftFlanking = !/\s/.test(after) && (!punctuation.test(after) || /\s/.test(before) || punctuation.test(before))
-      var rightFlanking = !/\s/.test(before) && (!punctuation.test(before) || /\s/.test(after) || punctuation.test(after))
-      var setEm = null, setStrong = null
-      if (len % 2) { // Em
-        if (!state.em && leftFlanking && (ch === "*" || !rightFlanking || punctuation.test(before)))
-          setEm = true
-        else if (state.em == ch && rightFlanking && (ch === "*" || !leftFlanking || punctuation.test(after)))
-          setEm = false
-      }
-      if (len > 1) { // Strong
-        if (!state.strong && leftFlanking && (ch === "*" || !rightFlanking || punctuation.test(before)))
-          setStrong = true
-        else if (state.strong == ch && rightFlanking && (ch === "*" || !leftFlanking || punctuation.test(after)))
-          setStrong = false
-      }
-      if (setStrong != null || setEm != null) {
-        if (modeCfg.highlightFormatting) state.formatting = setEm == null ? "strong" : setStrong == null ? "em" : "strong em"
-        if (setEm === true) state.em = ch
-        if (setStrong === true) state.strong = ch
-        var t = getType(state)
-        if (setEm === false) state.em = false
-        if (setStrong === false) state.strong = false
-        return t
-      }
-    } else if (ch === ' ') {
-      if (stream.eat('*') || stream.eat('_')) { // Probably surrounded by spaces
-        if (stream.peek() === ' ') { // Surrounded by spaces, ignore
-          return getType(state);
-        } else { // Not surrounded by spaces, back up pointer
-          stream.backUp(1);
-        }
-      }
-    }
-
-    if (modeCfg.strikethrough) {
-      if (ch === '~' && stream.eatWhile(ch)) {
-        if (state.strikethrough) {// Remove strikethrough
-          if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
-          var t = getType(state);
-          state.strikethrough = false;
-          return t;
-        } else if (stream.match(/^[^\s]/, false)) {// Add strikethrough
-          state.strikethrough = true;
-          if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
-          return getType(state);
-        }
-      } else if (ch === ' ') {
-        if (stream.match(/^~~/, true)) { // Probably surrounded by space
-          if (stream.peek() === ' ') { // Surrounded by spaces, ignore
-            return getType(state);
-          } else { // Not surrounded by spaces, back up pointer
-            stream.backUp(2);
-          }
-        }
-      }
-    }
-
-    if (modeCfg.emoji && ch === ":" && stream.match(/^(?:[a-z_\d+][a-z_\d+-]*|\-[a-z_\d+][a-z_\d+-]*):/)) {
-      state.emoji = true;
-      if (modeCfg.highlightFormatting) state.formatting = "emoji";
-      var retType = getType(state);
-      state.emoji = false;
-      return retType;
-    }
-
-    if (ch === ' ') {
-      if (stream.match(/^ +$/, false)) {
-        state.trailingSpace++;
-      } else if (state.trailingSpace) {
-        state.trailingSpaceNewLine = true;
-      }
-    }
-
-    return getType(state);
-  }
-
-  function linkInline(stream, state) {
-    var ch = stream.next();
-
-    if (ch === ">") {
-      state.f = state.inline = inlineNormal;
-      if (modeCfg.highlightFormatting) state.formatting = "link";
-      var type = getType(state);
-      if (type){
-        type += " ";
-      } else {
-        type = "";
-      }
-      return type + tokenTypes.linkInline;
-    }
-
-    stream.match(/^[^>]+/, true);
-
-    return tokenTypes.linkInline;
-  }
-
-  function linkHref(stream, state) {
-    // Check if space, and return NULL if so (to avoid marking the space)
-    if(stream.eatSpace()){
-      return null;
-    }
-    var ch = stream.next();
-    if (ch === '(' || ch === '[') {
-      state.f = state.inline = getLinkHrefInside(ch === "(" ? ")" : "]");
-      if (modeCfg.highlightFormatting) state.formatting = "link-string";
-      state.linkHref = true;
-      return getType(state);
-    }
-    return 'error';
-  }
-
-  var linkRE = {
-    ")": /^(?:[^\\\(\)]|\\.|\((?:[^\\\(\)]|\\.)*\))*?(?=\))/,
-    "]": /^(?:[^\\\[\]]|\\.|\[(?:[^\\\[\]]|\\.)*\])*?(?=\])/
-  }
-
-  function getLinkHrefInside(endChar) {
-    return function(stream, state) {
-      var ch = stream.next();
-
-      if (ch === endChar) {
-        state.f = state.inline = inlineNormal;
-        if (modeCfg.highlightFormatting) state.formatting = "link-string";
-        var returnState = getType(state);
-        state.linkHref = false;
-        return returnState;
-      }
-
-      stream.match(linkRE[endChar])
-      state.linkHref = true;
-      return getType(state);
-    };
-  }
-
-  function footnoteLink(stream, state) {
-    if (stream.match(/^([^\]\\]|\\.)*\]:/, false)) {
-      state.f = footnoteLinkInside;
-      stream.next(); // Consume [
-      if (modeCfg.highlightFormatting) state.formatting = "link";
-      state.linkText = true;
-      return getType(state);
-    }
-    return switchInline(stream, state, inlineNormal);
-  }
-
-  function footnoteLinkInside(stream, state) {
-    if (stream.match(/^\]:/, true)) {
-      state.f = state.inline = footnoteUrl;
-      if (modeCfg.highlightFormatting) state.formatting = "link";
-      var returnType = getType(state);
-      state.linkText = false;
-      return returnType;
-    }
-
-    stream.match(/^([^\]\\]|\\.)+/, true);
-
-    return tokenTypes.linkText;
-  }
-
-  function footnoteUrl(stream, state) {
-    // Check if space, and return NULL if so (to avoid marking the space)
-    if(stream.eatSpace()){
-      return null;
-    }
-    // Match URL
-    stream.match(/^[^\s]+/, true);
-    // Check for link title
-    if (stream.peek() === undefined) { // End of line, set flag to check next line
-      state.linkTitle = true;
-    } else { // More content on line, check if link title
-      stream.match(/^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/, true);
-    }
-    state.f = state.inline = inlineNormal;
-    return tokenTypes.linkHref + " url";
-  }
-
-  var mode = {
-    startState: function() {
-      return {
-        f: blockNormal,
-
-        prevLine: {stream: null},
-        thisLine: {stream: null},
-
-        block: blockNormal,
-        htmlState: null,
-        indentation: 0,
-
-        inline: inlineNormal,
-        text: handleText,
-
-        formatting: false,
-        linkText: false,
-        linkHref: false,
-        linkTitle: false,
-        code: 0,
-        em: false,
-        strong: false,
-        header: 0,
-        setext: 0,
-        hr: false,
-        taskList: false,
-        list: false,
-        listStack: [],
-        quote: 0,
-        trailingSpace: 0,
-        trailingSpaceNewLine: false,
-        strikethrough: false,
-        emoji: false,
-        fencedEndRE: null
-      };
-    },
-
-    copyState: function(s) {
-      return {
-        f: s.f,
-
-        prevLine: s.prevLine,
-        thisLine: s.thisLine,
-
-        block: s.block,
-        htmlState: s.htmlState && CodeMirror.copyState(htmlMode, s.htmlState),
-        indentation: s.indentation,
-
-        localMode: s.localMode,
-        localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null,
-
-        inline: s.inline,
-        text: s.text,
-        formatting: false,
-        linkText: s.linkText,
-        linkTitle: s.linkTitle,
-        linkHref: s.linkHref,
-        code: s.code,
-        em: s.em,
-        strong: s.strong,
-        strikethrough: s.strikethrough,
-        emoji: s.emoji,
-        header: s.header,
-        setext: s.setext,
-        hr: s.hr,
-        taskList: s.taskList,
-        list: s.list,
-        listStack: s.listStack.slice(0),
-        quote: s.quote,
-        indentedCode: s.indentedCode,
-        trailingSpace: s.trailingSpace,
-        trailingSpaceNewLine: s.trailingSpaceNewLine,
-        md_inside: s.md_inside,
-        fencedEndRE: s.fencedEndRE
-      };
-    },
-
-    token: function(stream, state) {
-
-      // Reset state.formatting
-      state.formatting = false;
-
-      if (stream != state.thisLine.stream) {
-        state.header = 0;
-        state.hr = false;
-
-        if (stream.match(/^\s*$/, true)) {
-          blankLine(state);
-          return null;
-        }
-
-        state.prevLine = state.thisLine
-        state.thisLine = {stream: stream}
-
-        // Reset state.taskList
-        state.taskList = false;
-
-        // Reset state.trailingSpace
-        state.trailingSpace = 0;
-        state.trailingSpaceNewLine = false;
-
-        if (!state.localState) {
-          state.f = state.block;
-          if (state.f != htmlBlock) {
-            var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, expandedTab).length;
-            state.indentation = indentation;
-            state.indentationDiff = null;
-            if (indentation > 0) return null;
-          }
-        }
-      }
-      return state.f(stream, state);
-    },
-
-    innerMode: function(state) {
-      if (state.block == htmlBlock) return {state: state.htmlState, mode: htmlMode};
-      if (state.localState) return {state: state.localState, mode: state.localMode};
-      return {state: state, mode: mode};
-    },
-
-    indent: function(state, textAfter, line) {
-      if (state.block == htmlBlock && htmlMode.indent) return htmlMode.indent(state.htmlState, textAfter, line)
-      if (state.localState && state.localMode.indent) return state.localMode.indent(state.localState, textAfter, line)
-      return CodeMirror.Pass
-    },
-
-    blankLine: blankLine,
-
-    getType: getType,
-
-    blockCommentStart: "<!--",
-    blockCommentEnd: "-->",
-    closeBrackets: "()[]{}''\"\"``",
-    fold: "markdown"
-  };
-  return mode;
-}, "xml");
-
-CodeMirror.defineMIME("text/markdown", "markdown");
-
-CodeMirror.defineMIME("text/x-markdown", "markdown");
-
-});
diff --git a/plugins/UiFileManager/media/codemirror/mode/python.js b/plugins/UiFileManager/media/codemirror/mode/python.js
deleted file mode 100644
index de5fd38a..00000000
--- a/plugins/UiFileManager/media/codemirror/mode/python.js
+++ /dev/null
@@ -1,399 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  function wordRegexp(words) {
-    return new RegExp("^((" + words.join(")|(") + "))\\b");
-  }
-
-  var wordOperators = wordRegexp(["and", "or", "not", "is"]);
-  var commonKeywords = ["as", "assert", "break", "class", "continue",
-                        "def", "del", "elif", "else", "except", "finally",
-                        "for", "from", "global", "if", "import",
-                        "lambda", "pass", "raise", "return",
-                        "try", "while", "with", "yield", "in"];
-  var commonBuiltins = ["abs", "all", "any", "bin", "bool", "bytearray", "callable", "chr",
-                        "classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod",
-                        "enumerate", "eval", "filter", "float", "format", "frozenset",
-                        "getattr", "globals", "hasattr", "hash", "help", "hex", "id",
-                        "input", "int", "isinstance", "issubclass", "iter", "len",
-                        "list", "locals", "map", "max", "memoryview", "min", "next",
-                        "object", "oct", "open", "ord", "pow", "property", "range",
-                        "repr", "reversed", "round", "set", "setattr", "slice",
-                        "sorted", "staticmethod", "str", "sum", "super", "tuple",
-                        "type", "vars", "zip", "__import__", "NotImplemented",
-                        "Ellipsis", "__debug__"];
-  CodeMirror.registerHelper("hintWords", "python", commonKeywords.concat(commonBuiltins));
-
-  function top(state) {
-    return state.scopes[state.scopes.length - 1];
-  }
-
-  CodeMirror.defineMode("python", function(conf, parserConf) {
-    var ERRORCLASS = "error";
-
-    var delimiters = parserConf.delimiters || parserConf.singleDelimiters || /^[\(\)\[\]\{\}@,:`=;\.\\]/;
-    //               (Backwards-compatibility with old, cumbersome config system)
-    var operators = [parserConf.singleOperators, parserConf.doubleOperators, parserConf.doubleDelimiters, parserConf.tripleDelimiters,
-                     parserConf.operators || /^([-+*/%\/&|^]=?|[<>=]+|\/\/=?|\*\*=?|!=|[~!@]|\.\.\.)/]
-    for (var i = 0; i < operators.length; i++) if (!operators[i]) operators.splice(i--, 1)
-
-    var hangingIndent = parserConf.hangingIndent || conf.indentUnit;
-
-    var myKeywords = commonKeywords, myBuiltins = commonBuiltins;
-    if (parserConf.extra_keywords != undefined)
-      myKeywords = myKeywords.concat(parserConf.extra_keywords);
-
-    if (parserConf.extra_builtins != undefined)
-      myBuiltins = myBuiltins.concat(parserConf.extra_builtins);
-
-    var py3 = !(parserConf.version && Number(parserConf.version) < 3)
-    if (py3) {
-      // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator
-      var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*/;
-      myKeywords = myKeywords.concat(["nonlocal", "False", "True", "None", "async", "await"]);
-      myBuiltins = myBuiltins.concat(["ascii", "bytes", "exec", "print"]);
-      var stringPrefixes = new RegExp("^(([rbuf]|(br)|(fr))?('{3}|\"{3}|['\"]))", "i");
-    } else {
-      var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/;
-      myKeywords = myKeywords.concat(["exec", "print"]);
-      myBuiltins = myBuiltins.concat(["apply", "basestring", "buffer", "cmp", "coerce", "execfile",
-                                      "file", "intern", "long", "raw_input", "reduce", "reload",
-                                      "unichr", "unicode", "xrange", "False", "True", "None"]);
-      var stringPrefixes = new RegExp("^(([rubf]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
-    }
-    var keywords = wordRegexp(myKeywords);
-    var builtins = wordRegexp(myBuiltins);
-
-    // tokenizers
-    function tokenBase(stream, state) {
-      var sol = stream.sol() && state.lastToken != "\\"
-      if (sol) state.indent = stream.indentation()
-      // Handle scope changes
-      if (sol && top(state).type == "py") {
-        var scopeOffset = top(state).offset;
-        if (stream.eatSpace()) {
-          var lineOffset = stream.indentation();
-          if (lineOffset > scopeOffset)
-            pushPyScope(state);
-          else if (lineOffset < scopeOffset && dedent(stream, state) && stream.peek() != "#")
-            state.errorToken = true;
-          return null;
-        } else {
-          var style = tokenBaseInner(stream, state);
-          if (scopeOffset > 0 && dedent(stream, state))
-            style += " " + ERRORCLASS;
-          return style;
-        }
-      }
-      return tokenBaseInner(stream, state);
-    }
-
-    function tokenBaseInner(stream, state, inFormat) {
-      if (stream.eatSpace()) return null;
-
-      // Handle Comments
-      if (!inFormat && stream.match(/^#.*/)) return "comment";
-
-      // Handle Number Literals
-      if (stream.match(/^[0-9\.]/, false)) {
-        var floatLiteral = false;
-        // Floats
-        if (stream.match(/^[\d_]*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
-        if (stream.match(/^[\d_]+\.\d*/)) { floatLiteral = true; }
-        if (stream.match(/^\.\d+/)) { floatLiteral = true; }
-        if (floatLiteral) {
-          // Float literals may be "imaginary"
-          stream.eat(/J/i);
-          return "number";
-        }
-        // Integers
-        var intLiteral = false;
-        // Hex
-        if (stream.match(/^0x[0-9a-f_]+/i)) intLiteral = true;
-        // Binary
-        if (stream.match(/^0b[01_]+/i)) intLiteral = true;
-        // Octal
-        if (stream.match(/^0o[0-7_]+/i)) intLiteral = true;
-        // Decimal
-        if (stream.match(/^[1-9][\d_]*(e[\+\-]?[\d_]+)?/)) {
-          // Decimal literals may be "imaginary"
-          stream.eat(/J/i);
-          // TODO - Can you have imaginary longs?
-          intLiteral = true;
-        }
-        // Zero by itself with no other piece of number.
-        if (stream.match(/^0(?![\dx])/i)) intLiteral = true;
-        if (intLiteral) {
-          // Integer literals may be "long"
-          stream.eat(/L/i);
-          return "number";
-        }
-      }
-
-      // Handle Strings
-      if (stream.match(stringPrefixes)) {
-        var isFmtString = stream.current().toLowerCase().indexOf('f') !== -1;
-        if (!isFmtString) {
-          state.tokenize = tokenStringFactory(stream.current(), state.tokenize);
-          return state.tokenize(stream, state);
-        } else {
-          state.tokenize = formatStringFactory(stream.current(), state.tokenize);
-          return state.tokenize(stream, state);
-        }
-      }
-
-      for (var i = 0; i < operators.length; i++)
-        if (stream.match(operators[i])) return "operator"
-
-      if (stream.match(delimiters)) return "punctuation";
-
-      if (state.lastToken == "." && stream.match(identifiers))
-        return "property";
-
-      if (stream.match(keywords) || stream.match(wordOperators))
-        return "keyword";
-
-      if (stream.match(builtins))
-        return "builtin";
-
-      if (stream.match(/^(self|cls)\b/))
-        return "variable-2";
-
-      if (stream.match(identifiers)) {
-        if (state.lastToken == "def" || state.lastToken == "class")
-          return "def";
-        return "variable";
-      }
-
-      // Handle non-detected items
-      stream.next();
-      return inFormat ? null :ERRORCLASS;
-    }
-
-    function formatStringFactory(delimiter, tokenOuter) {
-      while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
-        delimiter = delimiter.substr(1);
-
-      var singleline = delimiter.length == 1;
-      var OUTCLASS = "string";
-
-      function tokenNestedExpr(depth) {
-        return function(stream, state) {
-          var inner = tokenBaseInner(stream, state, true)
-          if (inner == "punctuation") {
-            if (stream.current() == "{") {
-              state.tokenize = tokenNestedExpr(depth + 1)
-            } else if (stream.current() == "}") {
-              if (depth > 1) state.tokenize = tokenNestedExpr(depth - 1)
-              else state.tokenize = tokenString
-            }
-          }
-          return inner
-        }
-      }
-
-      function tokenString(stream, state) {
-        while (!stream.eol()) {
-          stream.eatWhile(/[^'"\{\}\\]/);
-          if (stream.eat("\\")) {
-            stream.next();
-            if (singleline && stream.eol())
-              return OUTCLASS;
-          } else if (stream.match(delimiter)) {
-            state.tokenize = tokenOuter;
-            return OUTCLASS;
-          } else if (stream.match('{{')) {
-            // ignore {{ in f-str
-            return OUTCLASS;
-          } else if (stream.match('{', false)) {
-            // switch to nested mode
-            state.tokenize = tokenNestedExpr(0)
-            if (stream.current()) return OUTCLASS;
-            else return state.tokenize(stream, state)
-          } else if (stream.match('}}')) {
-            return OUTCLASS;
-          } else if (stream.match('}')) {
-            // single } in f-string is an error
-            return ERRORCLASS;
-          } else {
-            stream.eat(/['"]/);
-          }
-        }
-        if (singleline) {
-          if (parserConf.singleLineStringErrors)
-            return ERRORCLASS;
-          else
-            state.tokenize = tokenOuter;
-        }
-        return OUTCLASS;
-      }
-      tokenString.isString = true;
-      return tokenString;
-    }
-
-    function tokenStringFactory(delimiter, tokenOuter) {
-      while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
-        delimiter = delimiter.substr(1);
-
-      var singleline = delimiter.length == 1;
-      var OUTCLASS = "string";
-
-      function tokenString(stream, state) {
-        while (!stream.eol()) {
-          stream.eatWhile(/[^'"\\]/);
-          if (stream.eat("\\")) {
-            stream.next();
-            if (singleline && stream.eol())
-              return OUTCLASS;
-          } else if (stream.match(delimiter)) {
-            state.tokenize = tokenOuter;
-            return OUTCLASS;
-          } else {
-            stream.eat(/['"]/);
-          }
-        }
-        if (singleline) {
-          if (parserConf.singleLineStringErrors)
-            return ERRORCLASS;
-          else
-            state.tokenize = tokenOuter;
-        }
-        return OUTCLASS;
-      }
-      tokenString.isString = true;
-      return tokenString;
-    }
-
-    function pushPyScope(state) {
-      while (top(state).type != "py") state.scopes.pop()
-      state.scopes.push({offset: top(state).offset + conf.indentUnit,
-                         type: "py",
-                         align: null})
-    }
-
-    function pushBracketScope(stream, state, type) {
-      var align = stream.match(/^([\s\[\{\(]|#.*)*$/, false) ? null : stream.column() + 1
-      state.scopes.push({offset: state.indent + hangingIndent,
-                         type: type,
-                         align: align})
-    }
-
-    function dedent(stream, state) {
-      var indented = stream.indentation();
-      while (state.scopes.length > 1 && top(state).offset > indented) {
-        if (top(state).type != "py") return true;
-        state.scopes.pop();
-      }
-      return top(state).offset != indented;
-    }
-
-    function tokenLexer(stream, state) {
-      if (stream.sol()) state.beginningOfLine = true;
-
-      var style = state.tokenize(stream, state);
-      var current = stream.current();
-
-      // Handle decorators
-      if (state.beginningOfLine && current == "@")
-        return stream.match(identifiers, false) ? "meta" : py3 ? "operator" : ERRORCLASS;
-
-      if (/\S/.test(current)) state.beginningOfLine = false;
-
-      if ((style == "variable" || style == "builtin")
-          && state.lastToken == "meta")
-        style = "meta";
-
-      // Handle scope changes.
-      if (current == "pass" || current == "return")
-        state.dedent += 1;
-
-      if (current == "lambda") state.lambda = true;
-      if (current == ":" && !state.lambda && top(state).type == "py")
-        pushPyScope(state);
-
-      if (current.length == 1 && !/string|comment/.test(style)) {
-        var delimiter_index = "[({".indexOf(current);
-        if (delimiter_index != -1)
-          pushBracketScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
-
-        delimiter_index = "])}".indexOf(current);
-        if (delimiter_index != -1) {
-          if (top(state).type == current) state.indent = state.scopes.pop().offset - hangingIndent
-          else return ERRORCLASS;
-        }
-      }
-      if (state.dedent > 0 && stream.eol() && top(state).type == "py") {
-        if (state.scopes.length > 1) state.scopes.pop();
-        state.dedent -= 1;
-      }
-
-      return style;
-    }
-
-    var external = {
-      startState: function(basecolumn) {
-        return {
-          tokenize: tokenBase,
-          scopes: [{offset: basecolumn || 0, type: "py", align: null}],
-          indent: basecolumn || 0,
-          lastToken: null,
-          lambda: false,
-          dedent: 0
-        };
-      },
-
-      token: function(stream, state) {
-        var addErr = state.errorToken;
-        if (addErr) state.errorToken = false;
-        var style = tokenLexer(stream, state);
-
-        if (style && style != "comment")
-          state.lastToken = (style == "keyword" || style == "punctuation") ? stream.current() : style;
-        if (style == "punctuation") style = null;
-
-        if (stream.eol() && state.lambda)
-          state.lambda = false;
-        return addErr ? style + " " + ERRORCLASS : style;
-      },
-
-      indent: function(state, textAfter) {
-        if (state.tokenize != tokenBase)
-          return state.tokenize.isString ? CodeMirror.Pass : 0;
-
-        var scope = top(state), closing = scope.type == textAfter.charAt(0)
-        if (scope.align != null)
-          return scope.align - (closing ? 1 : 0)
-        else
-          return scope.offset - (closing ? hangingIndent : 0)
-      },
-
-      electricInput: /^\s*[\}\]\)]$/,
-      closeBrackets: {triples: "'\""},
-      lineComment: "#",
-      fold: "indent"
-    };
-    return external;
-  });
-
-  CodeMirror.defineMIME("text/x-python", "python");
-
-  var words = function(str) { return str.split(" "); };
-
-  CodeMirror.defineMIME("text/x-cython", {
-    name: "python",
-    extra_keywords: words("by cdef cimport cpdef ctypedef enum except "+
-                          "extern gil include nogil property public "+
-                          "readonly struct union DEF IF ELIF ELSE")
-  });
-
-});
diff --git a/plugins/UiFileManager/media/codemirror/mode/rust.js b/plugins/UiFileManager/media/codemirror/mode/rust.js
deleted file mode 100644
index f95f320d..00000000
--- a/plugins/UiFileManager/media/codemirror/mode/rust.js
+++ /dev/null
@@ -1,72 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"), require("../../addon/mode/simple"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror", "../../addon/mode/simple"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-CodeMirror.defineSimpleMode("rust",{
-  start: [
-    // string and byte string
-    {regex: /b?"/, token: "string", next: "string"},
-    // raw string and raw byte string
-    {regex: /b?r"/, token: "string", next: "string_raw"},
-    {regex: /b?r#+"/, token: "string", next: "string_raw_hash"},
-    // character
-    {regex: /'(?:[^'\\]|\\(?:[nrt0'"]|x[\da-fA-F]{2}|u\{[\da-fA-F]{6}\}))'/, token: "string-2"},
-    // byte
-    {regex: /b'(?:[^']|\\(?:['\\nrt0]|x[\da-fA-F]{2}))'/, token: "string-2"},
-
-    {regex: /(?:(?:[0-9][0-9_]*)(?:(?:[Ee][+-]?[0-9_]+)|\.[0-9_]+(?:[Ee][+-]?[0-9_]+)?)(?:f32|f64)?)|(?:0(?:b[01_]+|(?:o[0-7_]+)|(?:x[0-9a-fA-F_]+))|(?:[0-9][0-9_]*))(?:u8|u16|u32|u64|i8|i16|i32|i64|isize|usize)?/,
-     token: "number"},
-    {regex: /(let(?:\s+mut)?|fn|enum|mod|struct|type|union)(\s+)([a-zA-Z_][a-zA-Z0-9_]*)/, token: ["keyword", null, "def"]},
-    {regex: /(?:abstract|alignof|as|async|await|box|break|continue|const|crate|do|dyn|else|enum|extern|fn|for|final|if|impl|in|loop|macro|match|mod|move|offsetof|override|priv|proc|pub|pure|ref|return|self|sizeof|static|struct|super|trait|type|typeof|union|unsafe|unsized|use|virtual|where|while|yield)\b/, token: "keyword"},
-    {regex: /\b(?:Self|isize|usize|char|bool|u8|u16|u32|u64|f16|f32|f64|i8|i16|i32|i64|str|Option)\b/, token: "atom"},
-    {regex: /\b(?:true|false|Some|None|Ok|Err)\b/, token: "builtin"},
-    {regex: /\b(fn)(\s+)([a-zA-Z_][a-zA-Z0-9_]*)/,
-     token: ["keyword", null ,"def"]},
-    {regex: /#!?\[.*\]/, token: "meta"},
-    {regex: /\/\/.*/, token: "comment"},
-    {regex: /\/\*/, token: "comment", next: "comment"},
-    {regex: /[-+\/*=<>!]+/, token: "operator"},
-    {regex: /[a-zA-Z_]\w*!/,token: "variable-3"},
-    {regex: /[a-zA-Z_]\w*/, token: "variable"},
-    {regex: /[\{\[\(]/, indent: true},
-    {regex: /[\}\]\)]/, dedent: true}
-  ],
-  string: [
-    {regex: /"/, token: "string", next: "start"},
-    {regex: /(?:[^\\"]|\\(?:.|$))*/, token: "string"}
-  ],
-  string_raw: [
-    {regex: /"/, token: "string", next: "start"},
-    {regex: /[^"]*/, token: "string"}
-  ],
-  string_raw_hash: [
-    {regex: /"#+/, token: "string", next: "start"},
-    {regex: /(?:[^"]|"(?!#))*/, token: "string"}
-  ],
-  comment: [
-    {regex: /.*?\*\//, token: "comment", next: "start"},
-    {regex: /.*/, token: "comment"}
-  ],
-  meta: {
-    dontIndentStates: ["comment"],
-    electricInput: /^\s*\}$/,
-    blockCommentStart: "/*",
-    blockCommentEnd: "*/",
-    lineComment: "//",
-    fold: "brace"
-  }
-});
-
-
-CodeMirror.defineMIME("text/x-rustsrc", "rust");
-CodeMirror.defineMIME("text/rust", "rust");
-});
diff --git a/plugins/UiFileManager/media/codemirror/mode/xml.js b/plugins/UiFileManager/media/codemirror/mode/xml.js
deleted file mode 100644
index 73c6e0e0..00000000
--- a/plugins/UiFileManager/media/codemirror/mode/xml.js
+++ /dev/null
@@ -1,413 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-var htmlConfig = {
-  autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
-                    'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
-                    'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
-                    'track': true, 'wbr': true, 'menuitem': true},
-  implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
-                     'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
-                     'th': true, 'tr': true},
-  contextGrabbers: {
-    'dd': {'dd': true, 'dt': true},
-    'dt': {'dd': true, 'dt': true},
-    'li': {'li': true},
-    'option': {'option': true, 'optgroup': true},
-    'optgroup': {'optgroup': true},
-    'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
-          'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
-          'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
-          'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
-          'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
-    'rp': {'rp': true, 'rt': true},
-    'rt': {'rp': true, 'rt': true},
-    'tbody': {'tbody': true, 'tfoot': true},
-    'td': {'td': true, 'th': true},
-    'tfoot': {'tbody': true},
-    'th': {'td': true, 'th': true},
-    'thead': {'tbody': true, 'tfoot': true},
-    'tr': {'tr': true}
-  },
-  doNotIndent: {"pre": true},
-  allowUnquoted: true,
-  allowMissing: true,
-  caseFold: true
-}
-
-var xmlConfig = {
-  autoSelfClosers: {},
-  implicitlyClosed: {},
-  contextGrabbers: {},
-  doNotIndent: {},
-  allowUnquoted: false,
-  allowMissing: false,
-  allowMissingTagName: false,
-  caseFold: false
-}
-
-CodeMirror.defineMode("xml", function(editorConf, config_) {
-  var indentUnit = editorConf.indentUnit
-  var config = {}
-  var defaults = config_.htmlMode ? htmlConfig : xmlConfig
-  for (var prop in defaults) config[prop] = defaults[prop]
-  for (var prop in config_) config[prop] = config_[prop]
-
-  // Return variables for tokenizers
-  var type, setStyle;
-
-  function inText(stream, state) {
-    function chain(parser) {
-      state.tokenize = parser;
-      return parser(stream, state);
-    }
-
-    var ch = stream.next();
-    if (ch == "<") {
-      if (stream.eat("!")) {
-        if (stream.eat("[")) {
-          if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
-          else return null;
-        } else if (stream.match("--")) {
-          return chain(inBlock("comment", "-->"));
-        } else if (stream.match("DOCTYPE", true, true)) {
-          stream.eatWhile(/[\w\._\-]/);
-          return chain(doctype(1));
-        } else {
-          return null;
-        }
-      } else if (stream.eat("?")) {
-        stream.eatWhile(/[\w\._\-]/);
-        state.tokenize = inBlock("meta", "?>");
-        return "meta";
-      } else {
-        type = stream.eat("/") ? "closeTag" : "openTag";
-        state.tokenize = inTag;
-        return "tag bracket";
-      }
-    } else if (ch == "&") {
-      var ok;
-      if (stream.eat("#")) {
-        if (stream.eat("x")) {
-          ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
-        } else {
-          ok = stream.eatWhile(/[\d]/) && stream.eat(";");
-        }
-      } else {
-        ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
-      }
-      return ok ? "atom" : "error";
-    } else {
-      stream.eatWhile(/[^&<]/);
-      return null;
-    }
-  }
-  inText.isInText = true;
-
-  function inTag(stream, state) {
-    var ch = stream.next();
-    if (ch == ">" || (ch == "/" && stream.eat(">"))) {
-      state.tokenize = inText;
-      type = ch == ">" ? "endTag" : "selfcloseTag";
-      return "tag bracket";
-    } else if (ch == "=") {
-      type = "equals";
-      return null;
-    } else if (ch == "<") {
-      state.tokenize = inText;
-      state.state = baseState;
-      state.tagName = state.tagStart = null;
-      var next = state.tokenize(stream, state);
-      return next ? next + " tag error" : "tag error";
-    } else if (/[\'\"]/.test(ch)) {
-      state.tokenize = inAttribute(ch);
-      state.stringStartCol = stream.column();
-      return state.tokenize(stream, state);
-    } else {
-      stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
-      return "word";
-    }
-  }
-
-  function inAttribute(quote) {
-    var closure = function(stream, state) {
-      while (!stream.eol()) {
-        if (stream.next() == quote) {
-          state.tokenize = inTag;
-          break;
-        }
-      }
-      return "string";
-    };
-    closure.isInAttribute = true;
-    return closure;
-  }
-
-  function inBlock(style, terminator) {
-    return function(stream, state) {
-      while (!stream.eol()) {
-        if (stream.match(terminator)) {
-          state.tokenize = inText;
-          break;
-        }
-        stream.next();
-      }
-      return style;
-    }
-  }
-
-  function doctype(depth) {
-    return function(stream, state) {
-      var ch;
-      while ((ch = stream.next()) != null) {
-        if (ch == "<") {
-          state.tokenize = doctype(depth + 1);
-          return state.tokenize(stream, state);
-        } else if (ch == ">") {
-          if (depth == 1) {
-            state.tokenize = inText;
-            break;
-          } else {
-            state.tokenize = doctype(depth - 1);
-            return state.tokenize(stream, state);
-          }
-        }
-      }
-      return "meta";
-    };
-  }
-
-  function Context(state, tagName, startOfLine) {
-    this.prev = state.context;
-    this.tagName = tagName;
-    this.indent = state.indented;
-    this.startOfLine = startOfLine;
-    if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
-      this.noIndent = true;
-  }
-  function popContext(state) {
-    if (state.context) state.context = state.context.prev;
-  }
-  function maybePopContext(state, nextTagName) {
-    var parentTagName;
-    while (true) {
-      if (!state.context) {
-        return;
-      }
-      parentTagName = state.context.tagName;
-      if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
-          !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
-        return;
-      }
-      popContext(state);
-    }
-  }
-
-  function baseState(type, stream, state) {
-    if (type == "openTag") {
-      state.tagStart = stream.column();
-      return tagNameState;
-    } else if (type == "closeTag") {
-      return closeTagNameState;
-    } else {
-      return baseState;
-    }
-  }
-  function tagNameState(type, stream, state) {
-    if (type == "word") {
-      state.tagName = stream.current();
-      setStyle = "tag";
-      return attrState;
-    } else if (config.allowMissingTagName && type == "endTag") {
-      setStyle = "tag bracket";
-      return attrState(type, stream, state);
-    } else {
-      setStyle = "error";
-      return tagNameState;
-    }
-  }
-  function closeTagNameState(type, stream, state) {
-    if (type == "word") {
-      var tagName = stream.current();
-      if (state.context && state.context.tagName != tagName &&
-          config.implicitlyClosed.hasOwnProperty(state.context.tagName))
-        popContext(state);
-      if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
-        setStyle = "tag";
-        return closeState;
-      } else {
-        setStyle = "tag error";
-        return closeStateErr;
-      }
-    } else if (config.allowMissingTagName && type == "endTag") {
-      setStyle = "tag bracket";
-      return closeState(type, stream, state);
-    } else {
-      setStyle = "error";
-      return closeStateErr;
-    }
-  }
-
-  function closeState(type, _stream, state) {
-    if (type != "endTag") {
-      setStyle = "error";
-      return closeState;
-    }
-    popContext(state);
-    return baseState;
-  }
-  function closeStateErr(type, stream, state) {
-    setStyle = "error";
-    return closeState(type, stream, state);
-  }
-
-  function attrState(type, _stream, state) {
-    if (type == "word") {
-      setStyle = "attribute";
-      return attrEqState;
-    } else if (type == "endTag" || type == "selfcloseTag") {
-      var tagName = state.tagName, tagStart = state.tagStart;
-      state.tagName = state.tagStart = null;
-      if (type == "selfcloseTag" ||
-          config.autoSelfClosers.hasOwnProperty(tagName)) {
-        maybePopContext(state, tagName);
-      } else {
-        maybePopContext(state, tagName);
-        state.context = new Context(state, tagName, tagStart == state.indented);
-      }
-      return baseState;
-    }
-    setStyle = "error";
-    return attrState;
-  }
-  function attrEqState(type, stream, state) {
-    if (type == "equals") return attrValueState;
-    if (!config.allowMissing) setStyle = "error";
-    return attrState(type, stream, state);
-  }
-  function attrValueState(type, stream, state) {
-    if (type == "string") return attrContinuedState;
-    if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
-    setStyle = "error";
-    return attrState(type, stream, state);
-  }
-  function attrContinuedState(type, stream, state) {
-    if (type == "string") return attrContinuedState;
-    return attrState(type, stream, state);
-  }
-
-  return {
-    startState: function(baseIndent) {
-      var state = {tokenize: inText,
-                   state: baseState,
-                   indented: baseIndent || 0,
-                   tagName: null, tagStart: null,
-                   context: null}
-      if (baseIndent != null) state.baseIndent = baseIndent
-      return state
-    },
-
-    token: function(stream, state) {
-      if (!state.tagName && stream.sol())
-        state.indented = stream.indentation();
-
-      if (stream.eatSpace()) return null;
-      type = null;
-      var style = state.tokenize(stream, state);
-      if ((style || type) && style != "comment") {
-        setStyle = null;
-        state.state = state.state(type || style, stream, state);
-        if (setStyle)
-          style = setStyle == "error" ? style + " error" : setStyle;
-      }
-      return style;
-    },
-
-    indent: function(state, textAfter, fullLine) {
-      var context = state.context;
-      // Indent multi-line strings (e.g. css).
-      if (state.tokenize.isInAttribute) {
-        if (state.tagStart == state.indented)
-          return state.stringStartCol + 1;
-        else
-          return state.indented + indentUnit;
-      }
-      if (context && context.noIndent) return CodeMirror.Pass;
-      if (state.tokenize != inTag && state.tokenize != inText)
-        return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
-      // Indent the starts of attribute names.
-      if (state.tagName) {
-        if (config.multilineTagIndentPastTag !== false)
-          return state.tagStart + state.tagName.length + 2;
-        else
-          return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
-      }
-      if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
-      var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
-      if (tagAfter && tagAfter[1]) { // Closing tag spotted
-        while (context) {
-          if (context.tagName == tagAfter[2]) {
-            context = context.prev;
-            break;
-          } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
-            context = context.prev;
-          } else {
-            break;
-          }
-        }
-      } else if (tagAfter) { // Opening tag spotted
-        while (context) {
-          var grabbers = config.contextGrabbers[context.tagName];
-          if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
-            context = context.prev;
-          else
-            break;
-        }
-      }
-      while (context && context.prev && !context.startOfLine)
-        context = context.prev;
-      if (context) return context.indent + indentUnit;
-      else return state.baseIndent || 0;
-    },
-
-    electricInput: /<\/[\s\w:]+>$/,
-    blockCommentStart: "<!--",
-    blockCommentEnd: "-->",
-
-    configuration: config.htmlMode ? "html" : "xml",
-    helperType: config.htmlMode ? "html" : "xml",
-
-    skipAttribute: function(state) {
-      if (state.state == attrValueState)
-        state.state = attrState
-    },
-
-    xmlCurrentTag: function(state) {
-      return state.tagName ? {name: state.tagName, close: state.type == "closeTag"} : null
-    },
-
-    xmlCurrentContext: function(state) {
-      var context = []
-      for (var cx = state.context; cx; cx = cx.prev)
-        if (cx.tagName) context.push(cx.tagName)
-      return context.reverse()
-    }
-  };
-});
-
-CodeMirror.defineMIME("text/xml", "xml");
-CodeMirror.defineMIME("application/xml", "xml");
-if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
-  CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
-
-});
diff --git a/plugins/UiFileManager/media/css/Menu.css b/plugins/UiFileManager/media/css/Menu.css
deleted file mode 100644
index b61b8be6..00000000
--- a/plugins/UiFileManager/media/css/Menu.css
+++ /dev/null
@@ -1,33 +0,0 @@
-.menu {
-	background-color: white; padding: 10px 0px; position: absolute; top: 0px; max-height: 0px; overflow: hidden; transform: translate(-100%, -30px); pointer-events: none;
-	box-shadow: 0px 2px 8px rgba(0,0,0,0.3); border-radius: 2px; opacity: 0; transition: opacity 0.2s ease-out, transform 1s ease-out, max-height 0.2s ease-in-out; z-index: 99;
-	display: inline-block; z-index: 999; transform-style: preserve-3d;
-}
-.menu.menu-left { transform: translate(0%, -30px); }
-.menu.menu-left.visible { transform: translate(0%, 0px); }
-.menu.visible {
-	opacity: 1; transform: translate(-100%, 0px); pointer-events: all;
-	transition: opacity 0.1s ease-out, transform 0.3s ease-out, max-height 0.3s cubic-bezier(0.86, 0, 0.07, 1);
-}
-
-.menu-item {
-	display: block; text-decoration: none; color: black; padding: 6px 24px; transition: all 0.2s; border-bottom: none; font-weight: normal;
-    max-height: 150px; overflow: hidden; text-overflow: ellipsis; -webkit-line-clamp: 6; -webkit-box-orient: vertical; display: -webkit-box;
-}
-.menu-item-separator { margin-top: 3px; margin-bottom: 3px; border-top: 1px solid #eee }
-
-.menu-item.noaction { cursor: default }
-.menu-item:hover:not(.noaction) { background-color: #F6F6F6; transition: none; color: inherit; cursor: pointer; color: black }
-.menu-item:active:not(.noaction), .menu-item:focus:not(.noaction) { background-color: #AF3BFF !important; color: white !important; transition: none }
-.menu-item.selected:before {
-	content: "L"; display: inline-block; transform: rotateZ(45deg) scaleX(-1);
-	font-weight: bold; position: absolute; margin-left: -14px; font-size: 12px; margin-top: 2px;
-}
-
-.menu-radio { white-space: normal; line-height: 26px }
-.menu-radio a {
-	background-color: #EEE; width: 18.5%;; text-align: center; margin-top: 2px; margin-bottom: 2px; color: #666; font-weight: bold;
-	text-decoration: none; font-size: 13px; transition: all 0.3s; text-transform: uppercase; display: inline-block;
-}
-.menu-radio a:hover, .menu-radio a.selected { transition: none; background-color: #AF3BFF !important; color: white !important }
-.menu-radio a.long { font-size: 10px; vertical-align: -1px; }
diff --git a/plugins/UiFileManager/media/css/Selectbar.css b/plugins/UiFileManager/media/css/Selectbar.css
deleted file mode 100644
index ceaf72e0..00000000
--- a/plugins/UiFileManager/media/css/Selectbar.css
+++ /dev/null
@@ -1,17 +0,0 @@
-.selectbar.visible { margin-top: 0px; visibility: visible }
-.selectbar {
-    position: fixed; top: 0; left: 0; background-color: white; box-shadow: 0px 0px 25px rgba(22, 39, 97, 0.2); margin-top: -75px;
-    transition: all 0.3s; visibility: hidden; z-index: 9999; color: black; border-left: 5px solid #ede1f582; width: 100%;
-    padding: 13px; font-size: 13px; font-weight: lighter; backface-visibility: hidden;
-}
-
-.selectbar .num { margin-left: 15px; min-width: 30px; text-align: right; display: inline-block; }
-.selectbar .size { margin-left: 10px; color: #9f9ba2; min-width: 75px; display: inline-block; }
-.selectbar .actions { display: inline-block; margin-left: 20px; font-size: 13px; text-transform: uppercase; line-height: 20px; }
-.selectbar .action { padding: 5px 20px; border: 1px solid #edd4ff; margin-left: 10px; border-radius: 30px; color: #af3bff; text-decoration: none; transition: all 0.3s }
-.selectbar .action:hover { border-color: #c788f3; transition: none; color: #9700ff }
-.selectbar .delete { color: #AAA; border-color: #DDD; }
-.selectbar .delete:hover { color: #333; border-color: #AAA }
-.selectbar .action:active { background-color: #af3bff; color: white; border-color: #af3bff; transition: none }
-.selectbar .cancel { margin: 20px; font-size: 10px; text-decoration: none; color: #999; text-transform: uppercase; }
-.selectbar .cancel:hover { color: #333; transition: none }
\ No newline at end of file
diff --git a/plugins/UiFileManager/media/css/UiFileManager.css b/plugins/UiFileManager/media/css/UiFileManager.css
deleted file mode 100644
index be884bcb..00000000
--- a/plugins/UiFileManager/media/css/UiFileManager.css
+++ /dev/null
@@ -1,148 +0,0 @@
-body { background-color: #EEEEF5; font-family: "Segoe UI", Helvetica, Arial; height: 95000px; overflow: hidden; }
-body.loaded { height: auto; overflow: auto }
-h1 { font-weight: lighter; }
-
-a { color: #333 }
-a:hover { text-decoration: none }
-input::placeholder { color: rgba(255, 255, 255, 0.3) }
-
-h2 { font-weight: lighter; }
-
-.link { background-color: transparent; outline: 5px solid transparent; transition: all 0.3s }
-.link:active { background-color: #fbf5ff; outline: 5px solid #fbf5ff; transition: none }
-
-.manager.editing .files { float: left; width: 280px; }
-
-.sidebar-button {
-	display: inline-block; padding: 25px 19px; text-decoration: none; position: absolute;
-	border-right: 1px solid #EEE; line-height: 10px; color: #7801F5; transition: all 0.3s
-}
-.sidebar-button:active { background-color: #f5e7ff; transition: none }
-/*.sidebar-button:hover { background-color: #fbf5ff; }*/
-.sidebar-button span { transition: 1s all; transform-origin: 2.5px 7px; display: inline-block; }
-.manager.sidebar_closed .sidebar-button span { transform: rotateZ(180deg); }
-.manager.sidebar_closed .files { margin-left: -300px; }
-.manager.sidebar_closed .editor { width: 100%; }
-
-.button {
-	padding: 5px 10px; margin-left: 10px; background-color: #752ff2; border-bottom: 2px solid #caadff; background-position: -50px center;
-	border-radius: 2px; text-decoration: none; transition: all 0.5s ease-out; display: inline-block;
-	color: #333; font-size: 12px; vertical-align: 2px; text-transform: uppercase; color: white; max-width: 100px;
-}
-.button:hover { background-color: #9e71ed; transition: none; }
-.button:active { position: relative; top: 1px }
-.button.loading, .button.disabled { color: rgba(255,255,255,0.7);; pointer-events: none; border-bottom: 2px solid #666; background-color: #999; }
-.button.loading { background: #999 url(../img/loading.gif) no-repeat center center; transition: all 0.5s ease-out; color: rgba(0,0,0,0); }
-.button.done { background-color: #4dc758; transition: all 0.3s; border-color: #4dc758; pointer-events: none; }
-.button.hidden { max-width: 0px; display: inline-block; padding-left: 0px; padding-right: 0px; margin: 0px; }
-
-/* List */
-
-.files {
-	width: 97%; box-sizing: border-box; color: #555; position: relative; z-index: 1; transition: all 0.6s;
-	font-size: 14px; box-shadow: 0px 9px 20px -15px #a5cbec; max-width: 400px; border: 1px solid #EEEEF5;
-}
-.files .tr { white-space: nowrap }
-.files .td { display: inline-block; width: 60px }
-.files .tbody .td { line-height: 18px; vertical-align: bottom; }
-.files .td.name { min-width: 100px }
-.files .td.size { width: 60px; text-align: right; padding-left: 5px; }
-.files .td.status { text-align: right; }
-.files .td.peer { width: 60px }
-.files .td.uploaded { width: 130px; text-align: right; }
-.files .td.added { width: 90px }
-.files .orderby { color: inherit; text-decoration: none; transition: all 0.3s; outline: 5px solid transparent; }
-.files .orderby:hover { text-decoration: underline; }
-.files .orderby .icon-arrow-down { opacity: 0; transition: all 0.3s ease-in-out; }
-.files .orderby.selected .icon-arrow-down { opacity: 0.3; }
-.files .orderby:active { background-color: rgba(133, 239, 255, 0.09); outline: 5px solid rgba(133, 239, 255, 0.09); transition: none; }
-.files .orderby:hover .icon-arrow-down { opacity: 0.5; }
-.files .orderby:not(.desc) .icon-arrow-down { transform: rotateZ(180deg); }
-.files .tr.editing .td { background-color: #ede1f582; border-top-color: #ece9ef; }
-.files .thead { /*background: linear-gradient(358deg, #e7f1f7, #e9f2f72e);*/ }
-.files .thead .td {
-	border-top: none; color: #8984c2; background-color: #f7f7fc;
-	font-size: 12px; /*text-transform: uppercase; background-color: transparent; font-weight: bold;*/
-}
-.files .thead .td a:last-of-type { font-weight: bold; }
-.files .thead .td a { text-decoration: none; }
-.files .thead .td a:hover { text-decoration: underline; }
-.files .tbody { max-height: calc(100vh - 95px); overflow-y: auto; overflow-x: hidden; }
-.files .tr { background-color: white; }
-.files .td { padding: 10px 20px; border-top: 1px solid #EEE; font-size: 13px; white-space: nowrap; }
-.files .td.full { width: 100%; box-sizing: border-box;  white-space: pre-line; }
-.files .td.pre { width: 0px; color: transparent; padding-left: 0px; border-left: 2px solid transparent; }
-.files .tbody .td { height: 18px; }
-.files .tbody .td.full { height: auto; }
-.files .td.pre .checkbox-outer { opacity: 0.6; margin-left: -11px; margin-top: -15px; width: 18px; height: 12px; display: inline-block; }
-.files .tr.modified .td.pre { border-left-color: #7801F5 }
-.files .tr.added .td.pre { border-left-color: #00ec93 }
-.files .tr.ignored .td.pre { border-left-color: #999; }
-.files .tr.ignored { opacity: 0.5; }
-.files .tr.optional { background: linear-gradient(90deg, #fff6dd, 30%, white, 10%, white); }
-.files .tr.optional_empty { color: #999; font-style: italic; }
-.files .td.error { background-color: #F44336; color: white; }
-.files .td.site { width: 70px }
-.files .td.site .link { color: inherit; text-decoration: none }
-.files .td.status .percent {
-	transition: all 1s ease-in-out; display: inline-block; width: 80px; background-color: #EEE; font-size: 10px;
-	height: 15px; line-height: 15px; text-align: center; margin-right: 20px;
-}
-.files .td.name { padding-left: 10px; width: calc(100% - 167px); max-height: 18px; padding-right: 10px; }
-.files .tr.nobuttons .td.name { width: calc(100% - 127px); }
-.files .tr.nobuttons .td.buttons { width: 0px; }
-.files .td.name .title { color: inherit; text-decoration: none }
-.files .td.name .link { display: inline-block; overflow: hidden; text-overflow: ellipsis; vertical-align: -4px; max-width: 100%; }
-.files .pinned .td.name .link { max-width: calc(100% - 40px); }
-.files .thead .td.uploaded { text-align: left }
-.files .thead .td.uploaded .title { padding-left: 7px; }
-.files .peer .icon-profile { background: currentColor; color: #47d094; font-size: 10px; top: 1px; margin-right: 13px }
-.files .peer .icon-profile:before { background: currentColor }
-.files .peer .num { color: #969696; }
-.files .uploaded .uploaded-text { display: inline-block; text-align: right; }
-.files .uploaded .dots-container { display: inline-block; width: 0px; padding-right: 65px;; }
-.files .td.buttons { width: 40px; padding-left: 0px; padding-right: 0px; }
-.files .td.buttons .edit {
-	background-color: #2196f336; border-radius: 15px; padding: 1px 9px; font-size: 80%; text-decoration: none; color: #1976D2;
-}
-.files .checkbox-outer { padding: 15px; padding-left: 20px; padding-right: 0px; }
-.files .checkbox {
-	display: inline-block; width: 12px; height: 12px; border: 2px solid #00000014;
-	border-radius: 3px; vertical-align: -3px; margin-right: 10px;
-}
-.files .selected .checkbox { border-color: #dedede }
-.files .selected .checkbox:after {
-	background-color: #dedede; content: ""; text-decoration: none; display: block; width: 10px; height: 10px; margin-left: 1px; margin-top: 1px;
-}
-.files .tbody .td.size { font-size: 13px }
-.files .tbody .td.added, #PageFiles .files .td.access { font-size: 12px; color: #999 }
-.files .tr.type-dir .name { font-weight: bold; }
-.files .tr.type-parent .name .link { display: inline-block; width: 100%; padding: 5px; margin-top: -5px; }
-.files .foot .td { color: #a4a4a4; background-color: #f7f7fc; }
-.files .foot .create { float: right; text-decoration: none; position: relative; }
-.files .foot .create .link { color: #8c42ed; text-decoration: none; }
-.files .foot .create .link:active { background-color: #8c42ed3b; outline: 5px solid #8c42ed3b; }
-.files .foot .create .menu { top: 40px; }
-
-
-/* Editor */
-
-.editor { background-color: #F7F7FC; float: left; width: calc(100% - 280px); box-sizing: border-box; transition: all 0.6s; }
-.editor .CodeMirror { height: calc(100vh - 79px); visibility: hidden; }
-.editor textarea { width: 100%; height: 800px; white-space: pre; }
-.editor .title { margin-left: 20px; }
-.editor .editor-head {
-	padding: 15px 20px; padding-left: 45px; font-size: 18px; font-weight: lighter; border: 1px solid #EEEEF5;
-    white-space: nowrap; overflow: hidden;
-}
-.editor.loaded .CodeMirror { visibility: inherit; }
-.editor.error .CodeMirror { display: none; }
-.editor .button.save { min-width: 30px; text-align: center; transition: all 0.3s; }
-.editor .button.save.done { min-width: 80px; }
-.editor .error-message { text-align: center; padding: 50px; }
-
-.editor .CodeMirror-foldmarker {
-	line-height: .3; cursor: pointer; background-color: #ffeb3b61; text-shadow: none; font-family: inherit;
-	color: #050505; border: 1px solid #ffdf7f; padding: 0px 5px;
-}
-.editor .CodeMirror-activeline-background { background-color: #F6F6F6 !important; }
\ No newline at end of file
diff --git a/plugins/UiFileManager/media/css/all.css b/plugins/UiFileManager/media/css/all.css
deleted file mode 100644
index 2ff099c8..00000000
--- a/plugins/UiFileManager/media/css/all.css
+++ /dev/null
@@ -1,211 +0,0 @@
-
-/* ---- Menu.css ---- */
-
-
-.menu {
-	background-color: white; padding: 10px 0px; position: absolute; top: 0px; max-height: 0px; overflow: hidden; -webkit-transform: translate(-100%, -30px); -moz-transform: translate(-100%, -30px); -o-transform: translate(-100%, -30px); -ms-transform: translate(-100%, -30px); transform: translate(-100%, -30px) ; pointer-events: none;
-	-webkit-box-shadow: 0px 2px 8px rgba(0,0,0,0.3); -moz-box-shadow: 0px 2px 8px rgba(0,0,0,0.3); -o-box-shadow: 0px 2px 8px rgba(0,0,0,0.3); -ms-box-shadow: 0px 2px 8px rgba(0,0,0,0.3); box-shadow: 0px 2px 8px rgba(0,0,0,0.3) ; -webkit-border-radius: 2px; -moz-border-radius: 2px; -o-border-radius: 2px; -ms-border-radius: 2px; border-radius: 2px ; opacity: 0; -webkit-transition: opacity 0.2s ease-out, transform 1s ease-out, max-height 0.2s ease-in-out; -moz-transition: opacity 0.2s ease-out, transform 1s ease-out, max-height 0.2s ease-in-out; -o-transition: opacity 0.2s ease-out, transform 1s ease-out, max-height 0.2s ease-in-out; -ms-transition: opacity 0.2s ease-out, transform 1s ease-out, max-height 0.2s ease-in-out; transition: opacity 0.2s ease-out, transform 1s ease-out, max-height 0.2s ease-in-out ; z-index: 99;
-	display: inline-block; z-index: 999; transform-style: preserve-3d;
-}
-.menu.menu-left { -webkit-transform: translate(0%, -30px); -moz-transform: translate(0%, -30px); -o-transform: translate(0%, -30px); -ms-transform: translate(0%, -30px); transform: translate(0%, -30px) ; }
-.menu.menu-left.visible { -webkit-transform: translate(0%, 0px); -moz-transform: translate(0%, 0px); -o-transform: translate(0%, 0px); -ms-transform: translate(0%, 0px); transform: translate(0%, 0px) ; }
-.menu.visible {
-	opacity: 1; -webkit-transform: translate(-100%, 0px); -moz-transform: translate(-100%, 0px); -o-transform: translate(-100%, 0px); -ms-transform: translate(-100%, 0px); transform: translate(-100%, 0px) ; pointer-events: all;
-	-webkit-transition: opacity 0.1s ease-out, transform 0.3s ease-out, max-height 0.3s cubic-bezier(0.86, 0, 0.07, 1); -moz-transition: opacity 0.1s ease-out, transform 0.3s ease-out, max-height 0.3s cubic-bezier(0.86, 0, 0.07, 1); -o-transition: opacity 0.1s ease-out, transform 0.3s ease-out, max-height 0.3s cubic-bezier(0.86, 0, 0.07, 1); -ms-transition: opacity 0.1s ease-out, transform 0.3s ease-out, max-height 0.3s cubic-bezier(0.86, 0, 0.07, 1); transition: opacity 0.1s ease-out, transform 0.3s ease-out, max-height 0.3s cubic-bezier(0.86, 0, 0.07, 1) ;
-}
-
-.menu-item {
-	display: block; text-decoration: none; color: black; padding: 6px 24px; -webkit-transition: all 0.2s; -moz-transition: all 0.2s; -o-transition: all 0.2s; -ms-transition: all 0.2s; transition: all 0.2s ; border-bottom: none; font-weight: normal;
-    max-height: 150px; overflow: hidden; text-overflow: ellipsis; -webkit-line-clamp: 6; -webkit-box-orient: vertical; display: -webkit-box;
-}
-.menu-item-separator { margin-top: 3px; margin-bottom: 3px; border-top: 1px solid #eee }
-
-.menu-item.noaction { cursor: default }
-.menu-item:hover:not(.noaction) { background-color: #F6F6F6; -webkit-transition: none; -moz-transition: none; -o-transition: none; -ms-transition: none; transition: none ; color: inherit; cursor: pointer; color: black }
-.menu-item:active:not(.noaction), .menu-item:focus:not(.noaction) { background-color: #AF3BFF !important; color: white !important; -webkit-transition: none ; -moz-transition: none ; -o-transition: none ; -ms-transition: none ; transition: none  }
-.menu-item.selected:before {
-	content: "L"; display: inline-block; -webkit-transform: rotateZ(45deg) scaleX(-1); -moz-transform: rotateZ(45deg) scaleX(-1); -o-transform: rotateZ(45deg) scaleX(-1); -ms-transform: rotateZ(45deg) scaleX(-1); transform: rotateZ(45deg) scaleX(-1) ;
-	font-weight: bold; position: absolute; margin-left: -14px; font-size: 12px; margin-top: 2px;
-}
-
-.menu-radio { white-space: normal; line-height: 26px }
-.menu-radio a {
-	background-color: #EEE; width: 18.5%;; text-align: center; margin-top: 2px; margin-bottom: 2px; color: #666; font-weight: bold;
-	text-decoration: none; font-size: 13px; -webkit-transition: all 0.3s; -moz-transition: all 0.3s; -o-transition: all 0.3s; -ms-transition: all 0.3s; transition: all 0.3s ; text-transform: uppercase; display: inline-block;
-}
-.menu-radio a:hover, .menu-radio a.selected { -webkit-transition: none; -moz-transition: none; -o-transition: none; -ms-transition: none; transition: none ; background-color: #AF3BFF !important; color: white !important }
-.menu-radio a.long { font-size: 10px; vertical-align: -1px; }
-
-
-/* ---- Selectbar.css ---- */
-
-
-.selectbar.visible { margin-top: 0px; visibility: visible }
-.selectbar {
-    position: fixed; top: 0; left: 0; background-color: white; -webkit-box-shadow: 0px 0px 25px rgba(22, 39, 97, 0.2); -moz-box-shadow: 0px 0px 25px rgba(22, 39, 97, 0.2); -o-box-shadow: 0px 0px 25px rgba(22, 39, 97, 0.2); -ms-box-shadow: 0px 0px 25px rgba(22, 39, 97, 0.2); box-shadow: 0px 0px 25px rgba(22, 39, 97, 0.2) ; margin-top: -75px;
-    -webkit-transition: all 0.3s; -moz-transition: all 0.3s; -o-transition: all 0.3s; -ms-transition: all 0.3s; transition: all 0.3s ; visibility: hidden; z-index: 9999; color: black; border-left: 5px solid #ede1f582; width: 100%;
-    padding: 13px; font-size: 13px; font-weight: lighter; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; -o-backface-visibility: hidden; -ms-backface-visibility: hidden; backface-visibility: hidden ;
-}
-
-.selectbar .num { margin-left: 15px; min-width: 30px; text-align: right; display: inline-block; }
-.selectbar .size { margin-left: 10px; color: #9f9ba2; min-width: 75px; display: inline-block; }
-.selectbar .actions { display: inline-block; margin-left: 20px; font-size: 13px; text-transform: uppercase; line-height: 20px; }
-.selectbar .action { padding: 5px 20px; border: 1px solid #edd4ff; margin-left: 10px; -webkit-border-radius: 30px; -moz-border-radius: 30px; -o-border-radius: 30px; -ms-border-radius: 30px; border-radius: 30px ; color: #af3bff; text-decoration: none; -webkit-transition: all 0.3s ; -moz-transition: all 0.3s ; -o-transition: all 0.3s ; -ms-transition: all 0.3s ; transition: all 0.3s  }
-.selectbar .action:hover { border-color: #c788f3; -webkit-transition: none; -moz-transition: none; -o-transition: none; -ms-transition: none; transition: none ; color: #9700ff }
-.selectbar .delete { color: #AAA; border-color: #DDD; }
-.selectbar .delete:hover { color: #333; border-color: #AAA }
-.selectbar .action:active { background-color: #af3bff; color: white; border-color: #af3bff; -webkit-transition: none ; -moz-transition: none ; -o-transition: none ; -ms-transition: none ; transition: none  }
-.selectbar .cancel { margin: 20px; font-size: 10px; text-decoration: none; color: #999; text-transform: uppercase; }
-.selectbar .cancel:hover { color: #333; -webkit-transition: none ; -moz-transition: none ; -o-transition: none ; -ms-transition: none ; transition: none  }
-
-/* ---- UiFileManager.css ---- */
-
-
-body { background-color: #EEEEF5; font-family: "Segoe UI", Helvetica, Arial; height: 95000px; overflow: hidden; }
-body.loaded { height: auto; overflow: auto }
-h1 { font-weight: lighter; }
-
-a { color: #333 }
-a:hover { text-decoration: none }
-input::placeholder { color: rgba(255, 255, 255, 0.3) }
-
-h2 { font-weight: lighter; }
-
-.link { background-color: transparent; outline: 5px solid transparent; -webkit-transition: all 0.3s ; -moz-transition: all 0.3s ; -o-transition: all 0.3s ; -ms-transition: all 0.3s ; transition: all 0.3s  }
-.link:active { background-color: #fbf5ff; outline: 5px solid #fbf5ff; -webkit-transition: none ; -moz-transition: none ; -o-transition: none ; -ms-transition: none ; transition: none  }
-
-.manager.editing .files { float: left; width: 280px; }
-
-.sidebar-button {
-	display: inline-block; padding: 25px 19px; text-decoration: none; position: absolute;
-	border-right: 1px solid #EEE; line-height: 10px; color: #7801F5; transition: all 0.3s
-}
-.sidebar-button:active { background-color: #f5e7ff; -webkit-transition: none ; -moz-transition: none ; -o-transition: none ; -ms-transition: none ; transition: none  }
-/*.sidebar-button:hover { background-color: #fbf5ff; }*/
-.sidebar-button span { -webkit-transition: 1s all; -moz-transition: 1s all; -o-transition: 1s all; -ms-transition: 1s all; transition: 1s all ; transform-origin: 2.5px 7px; display: inline-block; }
-.manager.sidebar_closed .sidebar-button span { -webkit-transform: rotateZ(180deg); -moz-transform: rotateZ(180deg); -o-transform: rotateZ(180deg); -ms-transform: rotateZ(180deg); transform: rotateZ(180deg) ; }
-.manager.sidebar_closed .files { margin-left: -300px; }
-.manager.sidebar_closed .editor { width: 100%; }
-
-.button {
-	padding: 5px 10px; margin-left: 10px; background-color: #752ff2; border-bottom: 2px solid #caadff; background-position: -50px center;
-	-webkit-border-radius: 2px; -moz-border-radius: 2px; -o-border-radius: 2px; -ms-border-radius: 2px; border-radius: 2px ; text-decoration: none; -webkit-transition: all 0.5s ease-out; -moz-transition: all 0.5s ease-out; -o-transition: all 0.5s ease-out; -ms-transition: all 0.5s ease-out; transition: all 0.5s ease-out ; display: inline-block;
-	color: #333; font-size: 12px; vertical-align: 2px; text-transform: uppercase; color: white; max-width: 100px;
-}
-.button:hover { background-color: #9e71ed; -webkit-transition: none; -moz-transition: none; -o-transition: none; -ms-transition: none; transition: none ; }
-.button:active { position: relative; top: 1px }
-.button.loading, .button.disabled { color: rgba(255,255,255,0.7);; pointer-events: none; border-bottom: 2px solid #666; background-color: #999; }
-.button.loading { background: #999 url(../img/loading.gif) no-repeat center center; -webkit-transition: all 0.5s ease-out; -moz-transition: all 0.5s ease-out; -o-transition: all 0.5s ease-out; -ms-transition: all 0.5s ease-out; transition: all 0.5s ease-out ; color: rgba(0,0,0,0); }
-.button.done { background-color: #4dc758; -webkit-transition: all 0.3s; -moz-transition: all 0.3s; -o-transition: all 0.3s; -ms-transition: all 0.3s; transition: all 0.3s ; border-color: #4dc758; pointer-events: none; }
-.button.hidden { max-width: 0px; display: inline-block; padding-left: 0px; padding-right: 0px; margin: 0px; }
-
-/* List */
-
-.files {
-	width: 97%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ; color: #555; position: relative; z-index: 1; -webkit-transition: all 0.6s; -moz-transition: all 0.6s; -o-transition: all 0.6s; -ms-transition: all 0.6s; transition: all 0.6s ;
-	font-size: 14px; -webkit-box-shadow: 0px 9px 20px -15px #a5cbec; -moz-box-shadow: 0px 9px 20px -15px #a5cbec; -o-box-shadow: 0px 9px 20px -15px #a5cbec; -ms-box-shadow: 0px 9px 20px -15px #a5cbec; box-shadow: 0px 9px 20px -15px #a5cbec ; max-width: 400px; border: 1px solid #EEEEF5;
-}
-.files .tr { white-space: nowrap }
-.files .td { display: inline-block; width: 60px }
-.files .tbody .td { line-height: 18px; vertical-align: bottom; }
-.files .td.name { min-width: 100px }
-.files .td.size { width: 60px; text-align: right; padding-left: 5px; }
-.files .td.status { text-align: right; }
-.files .td.peer { width: 60px }
-.files .td.uploaded { width: 130px; text-align: right; }
-.files .td.added { width: 90px }
-.files .orderby { color: inherit; text-decoration: none; -webkit-transition: all 0.3s; -moz-transition: all 0.3s; -o-transition: all 0.3s; -ms-transition: all 0.3s; transition: all 0.3s ; outline: 5px solid transparent; }
-.files .orderby:hover { text-decoration: underline; }
-.files .orderby .icon-arrow-down { opacity: 0; -webkit-transition: all 0.3s ease-in-out; -moz-transition: all 0.3s ease-in-out; -o-transition: all 0.3s ease-in-out; -ms-transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out ; }
-.files .orderby.selected .icon-arrow-down { opacity: 0.3; }
-.files .orderby:active { background-color: rgba(133, 239, 255, 0.09); outline: 5px solid rgba(133, 239, 255, 0.09); -webkit-transition: none; -moz-transition: none; -o-transition: none; -ms-transition: none; transition: none ; }
-.files .orderby:hover .icon-arrow-down { opacity: 0.5; }
-.files .orderby:not(.desc) .icon-arrow-down { -webkit-transform: rotateZ(180deg); -moz-transform: rotateZ(180deg); -o-transform: rotateZ(180deg); -ms-transform: rotateZ(180deg); transform: rotateZ(180deg) ; }
-.files .tr.editing .td { background-color: #ede1f582; border-top-color: #ece9ef; }
-.files .thead { /*background: -webkit-linear-gradient(358deg, #e7f1f7, #e9f2f72e);background: -moz-linear-gradient(358deg, #e7f1f7, #e9f2f72e);background: -o-linear-gradient(358deg, #e7f1f7, #e9f2f72e);background: -ms-linear-gradient(358deg, #e7f1f7, #e9f2f72e);background: linear-gradient(358deg, #e7f1f7, #e9f2f72e);*/ }
-.files .thead .td {
-	border-top: none; color: #8984c2; background-color: #f7f7fc;
-	font-size: 12px; /*text-transform: uppercase; background-color: transparent; font-weight: bold;*/
-}
-.files .thead .td a:last-of-type { font-weight: bold; }
-.files .thead .td a { text-decoration: none; }
-.files .thead .td a:hover { text-decoration: underline; }
-.files .tbody { max-height: calc(100vh - 95px); overflow-y: auto; overflow-x: hidden; }
-.files .tr { background-color: white; }
-.files .td { padding: 10px 20px; border-top: 1px solid #EEE; font-size: 13px; white-space: nowrap; }
-.files .td.full { width: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ;  white-space: pre-line; }
-.files .td.pre { width: 0px; color: transparent; padding-left: 0px; border-left: 2px solid transparent; }
-.files .tbody .td { height: 18px; }
-.files .tbody .td.full { height: auto; }
-.files .td.pre .checkbox-outer { opacity: 0.6; margin-left: -11px; margin-top: -15px; width: 18px; height: 12px; display: inline-block; }
-.files .tr.modified .td.pre { border-left-color: #7801F5 }
-.files .tr.added .td.pre { border-left-color: #00ec93 }
-.files .tr.ignored .td.pre { border-left-color: #999; }
-.files .tr.ignored { opacity: 0.5; }
-.files .tr.optional { background: -webkit-linear-gradient(90deg, #fff6dd, 30%, white, 10%, white);background: -moz-linear-gradient(90deg, #fff6dd, 30%, white, 10%, white);background: -o-linear-gradient(90deg, #fff6dd, 30%, white, 10%, white);background: -ms-linear-gradient(90deg, #fff6dd, 30%, white, 10%, white);background: linear-gradient(90deg, #fff6dd, 30%, white, 10%, white); }
-.files .tr.optional_empty { color: #999; font-style: italic; }
-.files .td.error { background-color: #F44336; color: white; }
-.files .td.site { width: 70px }
-.files .td.site .link { color: inherit; text-decoration: none }
-.files .td.status .percent {
-	-webkit-transition: all 1s ease-in-out; -moz-transition: all 1s ease-in-out; -o-transition: all 1s ease-in-out; -ms-transition: all 1s ease-in-out; transition: all 1s ease-in-out ; display: inline-block; width: 80px; background-color: #EEE; font-size: 10px;
-	height: 15px; line-height: 15px; text-align: center; margin-right: 20px;
-}
-.files .td.name { padding-left: 10px; width: calc(100% - 167px); max-height: 18px; padding-right: 10px; }
-.files .tr.nobuttons .td.name { width: calc(100% - 127px); }
-.files .tr.nobuttons .td.buttons { width: 0px; }
-.files .td.name .title { color: inherit; text-decoration: none }
-.files .td.name .link { display: inline-block; overflow: hidden; text-overflow: ellipsis; vertical-align: -4px; max-width: 100%; }
-.files .pinned .td.name .link { max-width: calc(100% - 40px); }
-.files .thead .td.uploaded { text-align: left }
-.files .thead .td.uploaded .title { padding-left: 7px; }
-.files .peer .icon-profile { background: currentColor; color: #47d094; font-size: 10px; top: 1px; margin-right: 13px }
-.files .peer .icon-profile:before { background: currentColor }
-.files .peer .num { color: #969696; }
-.files .uploaded .uploaded-text { display: inline-block; text-align: right; }
-.files .uploaded .dots-container { display: inline-block; width: 0px; padding-right: 65px;; }
-.files .td.buttons { width: 40px; padding-left: 0px; padding-right: 0px; }
-.files .td.buttons .edit {
-	background-color: #2196f336; -webkit-border-radius: 15px; -moz-border-radius: 15px; -o-border-radius: 15px; -ms-border-radius: 15px; border-radius: 15px ; padding: 1px 9px; font-size: 80%; text-decoration: none; color: #1976D2;
-}
-.files .checkbox-outer { padding: 15px; padding-left: 20px; padding-right: 0px; }
-.files .checkbox {
-	display: inline-block; width: 12px; height: 12px; border: 2px solid #00000014;
-	-webkit-border-radius: 3px; -moz-border-radius: 3px; -o-border-radius: 3px; -ms-border-radius: 3px; border-radius: 3px ; vertical-align: -3px; margin-right: 10px;
-}
-.files .selected .checkbox { border-color: #dedede }
-.files .selected .checkbox:after {
-	background-color: #dedede; content: ""; text-decoration: none; display: block; width: 10px; height: 10px; margin-left: 1px; margin-top: 1px;
-}
-.files .tbody .td.size { font-size: 13px }
-.files .tbody .td.added, #PageFiles .files .td.access { font-size: 12px; color: #999 }
-.files .tr.type-dir .name { font-weight: bold; }
-.files .tr.type-parent .name .link { display: inline-block; width: 100%; padding: 5px; margin-top: -5px; }
-.files .foot .td { color: #a4a4a4; background-color: #f7f7fc; }
-.files .foot .create { float: right; text-decoration: none; position: relative; }
-.files .foot .create .link { color: #8c42ed; text-decoration: none; }
-.files .foot .create .link:active { background-color: #8c42ed3b; outline: 5px solid #8c42ed3b; }
-.files .foot .create .menu { top: 40px; }
-
-
-/* Editor */
-
-.editor { background-color: #F7F7FC; float: left; width: calc(100% - 280px); -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ; -webkit-transition: all 0.6s; -moz-transition: all 0.6s; -o-transition: all 0.6s; -ms-transition: all 0.6s; transition: all 0.6s ; }
-.editor .CodeMirror { height: calc(100vh - 79px); visibility: hidden; }
-.editor textarea { width: 100%; height: 800px; white-space: pre; }
-.editor .title { margin-left: 20px; }
-.editor .editor-head {
-	padding: 15px 20px; padding-left: 45px; font-size: 18px; font-weight: lighter; border: 1px solid #EEEEF5;
-    white-space: nowrap; overflow: hidden;
-}
-.editor.loaded .CodeMirror { visibility: inherit; }
-.editor.error .CodeMirror { display: none; }
-.editor .button.save { min-width: 30px; text-align: center; -webkit-transition: all 0.3s; -moz-transition: all 0.3s; -o-transition: all 0.3s; -ms-transition: all 0.3s; transition: all 0.3s ; }
-.editor .button.save.done { min-width: 80px; }
-.editor .error-message { text-align: center; padding: 50px; }
-
-.editor .CodeMirror-foldmarker {
-	line-height: .3; cursor: pointer; background-color: #ffeb3b61; text-shadow: none; font-family: inherit;
-	color: #050505; border: 1px solid #ffdf7f; padding: 0px 5px;
-}
-.editor .CodeMirror-activeline-background { background-color: #F6F6F6 !important; }
\ No newline at end of file
diff --git a/plugins/UiFileManager/media/img/loading.gif b/plugins/UiFileManager/media/img/loading.gif
deleted file mode 100644
index 27d0aa8108b0800f9cddcf613f787347d9981e05..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 723
zcmZ?wbhEHb6ky<H_`<;O9}NB>0iXa9k4z>53@HBR_Hzvhc6JPKHPSO+W(0~W{*!Vp
zN=+<DO;IS%EXhzv%u1}t$xlqt%gjs5XHfjf!pRL(r2{en<VXfqT?K`{l+1Zc7H~Z}
z#k9^rpxNS#X~E^{d$)JY=VN~&*uLeF!wDX};&s=!T-Q!>!e)X>LZSp~!n_rkGVK%h
z9k_L9<(o^(d!N7A`+9eTzQ!EZMr*-N2_|eB&45;SC+a-zP~lXP;z?eTv`FKm^!Y8l
zuZ^S*OlLmOv^V<W32(v1vllP#5cZpSD3n`EWSZY00c=K@0*zY2;VKxy)ce>ZNyYQx
zE1Q1d^QD!~t!MErXFkzlm$bqCmuUZ)iN%&IQkAQ(b??%e8>EQMBqK<8T-y}!%q4L0
z4v$MoL7}cEx5PfOihDclHe=f1_`ny+jJ+qGonTF#=e6?cS1GK1Glv+XQW)E^VpGzx
z%$u!=(=#3~+Lk*jmQUf$-=^(}f)AMWru(Y&&oE(%*JUs>JH24vgCGuUPSS^%^#tgi
z6`S6zDw0tR+QR$5bp7w`G6mDQzjYm%RoE)?D^8cegv~i}{SvGJM6wyypd<nY06JwR
Aod5s;

diff --git a/plugins/UiFileManager/media/js/Config.coffee b/plugins/UiFileManager/media/js/Config.coffee
deleted file mode 100644
index 0e156bef..00000000
--- a/plugins/UiFileManager/media/js/Config.coffee
+++ /dev/null
@@ -1,15 +0,0 @@
-window.BINARY_EXTENSIONS = [
-	"3dm", "3ds", "3g2", "3gp", "7z", "a", "aac", "adp", "ai", "aif", "aiff", "alz", "ape", "apk", "appimage", "ar", "arj", "asc", "asf", "au", "avi", "bak",
-	"baml", "bh", "bin", "bk", "bmp", "btif", "bz2", "bzip2", "cab", "caf", "cgm", "class", "cmx", "cpio", "cr2", "cur", "dat", "dcm", "deb", "dex", "djvu",
-	"dll", "dmg", "dng", "doc", "docm", "docx", "dot", "dotm", "dra", "DS_Store", "dsk", "dts", "dtshd", "dvb", "dwg", "dxf", "ecelp4800", "ecelp7470",
-	"ecelp9600", "egg", "eol", "eot", "epub", "exe", "f4v", "fbs", "fh", "fla", "flac", "flatpak", "fli", "flv", "fpx", "fst", "fvt", "g3", "gh", "gif",
-	"gpg", "graffle", "gz", "gzip", "h261", "h263", "h264", "icns", "ico", "ief", "img", "ipa", "iso", "jar", "jpeg", "jpg", "jpgv", "jpm", "jxr", "key",
-	"ktx", "lha", "lib", "lvp", "lz", "lzh", "lzma", "lzo", "m3u", "m4a", "m4v", "mar", "mdi", "mht", "mid", "midi", "mj2", "mka", "mkv", "mmr", "mng",
-	"mobi", "mov", "movie", "mp3", "mp4", "mp4a", "mpeg", "mpg", "mpga", "msgpack", "mxu", "nef", "npx", "numbers", "nupkg", "o", "oga", "ogg", "ogv",
-	"otf", "pages", "pbm", "pcx", "pdb", "pdf", "pea", "pgm", "pic", "png", "pnm", "pot", "potm", "potx", "ppa", "ppam", "ppm", "pps", "ppsm", "ppsx",
-	"ppt", "pptm", "pptx", "psd", "pya", "pyc", "pyo", "pyv", "qt", "rar", "ras", "raw", "resources", "rgb", "rip", "rlc", "rmf", "rmvb", "rpm", "rtf",
-	"rz", "s3m", "s7z", "scpt", "sgi", "shar", "sig", "sil", "sketch", "slk", "smv", "snap", "snk", "so", "stl", "sub", "suo", "swf", "tar", "tbz2", "tbz",
-	"tga", "tgz", "thmx", "tif", "tiff", "tlz", "ttc", "ttf", "txz", "udf", "uvh", "uvi", "uvm", "uvp", "uvs", "uvu", "viv", "vob", "war", "wav", "wax",
-	"wbmp", "wdp", "weba", "webm", "webp", "whl", "wim", "wm", "wma", "wmv", "wmx", "woff2", "woff", "wrm", "wvx", "xbm", "xif", "xla", "xlam", "xls",
-	"xlsb", "xlsm", "xlsx", "xlt", "xltm", "xltx", "xm", "xmind", "xpi", "xpm", "xwd", "xz", "z", "zip", "zipx"
-]
diff --git a/plugins/UiFileManager/media/js/FileEditor.coffee b/plugins/UiFileManager/media/js/FileEditor.coffee
deleted file mode 100644
index 3166881d..00000000
--- a/plugins/UiFileManager/media/js/FileEditor.coffee
+++ /dev/null
@@ -1,179 +0,0 @@
-class FileEditor extends Class
-	constructor: (@inner_path) ->
-		@need_update = true
-		@on_loaded = new Promise()
-		@is_loading = false
-		@content = ""
-		@node_cm = null
-		@cm = null
-		@error = null
-		@is_loaded = false
-		@is_modified = false
-		@is_saving = false
-		@mode = "Loading"
-
-	update: ->
-		is_required = Page.url_params.get("edit_mode") != "new"
-
-		Page.cmd "fileGet", {inner_path: @inner_path, required: is_required}, (res) =>
-			if res?.error
-				@error = res.error
-				@content = res.error
-				@log "Error loading: #{@error}"
-			else
-				if res
-					@content = res
-				else
-					@content = ""
-					@mode = "Create"
-			if not @content
-				@cm.getDoc().clearHistory()
-			@cm.setValue(@content)
-			if not @error
-				@is_loaded = true
-			Page.projector.scheduleRender()
-
-	isModified: =>
-		return @content != @cm.getValue()
-
-	storeCmNode: (node) =>
-		@node_cm = node
-
-	getMode: (inner_path) ->
-		ext = inner_path.split(".").pop()
-		types = {
-			"py": "python",
-			"json": "application/json",
-			"js": "javascript",
-			"coffee": "coffeescript",
-			"html": "htmlmixed",
-			"htm": "htmlmixed",
-			"php": "htmlmixed",
-			"rs": "rust",
-			"css": "css",
-			"md": "markdown",
-			"xml": "xml",
-			"svg": "xml"
-		}
-		return types[ext]
-
-	foldJson: (from, to) =>
-		@log "foldJson", from, to
-		# Get open / close token
-		startToken = '{'
-		endToken = '}'
-		prevLine = @cm.getLine(from.line)
-		if prevLine.lastIndexOf('[') > prevLine.lastIndexOf('{')
-		  startToken = '['
-		  endToken = ']'
-
-		# Get json content
-		internal = @cm.getRange(from, to)
-		toParse = startToken + internal + endToken
-
-		#Get key count
-		try
-			parsed = JSON.parse(toParse)
-			count = Object.keys(parsed).length
-		catch e
-			null
-
-		return if count then "\u21A4#{count}\u21A6" else "\u2194"
-
-	createCodeMirror: ->
-		mode = @getMode(@inner_path)
-		@log "Creating CodeMirror", @inner_path, mode
-		options = {
-			value: "Loading...",
-			mode: mode,
-			lineNumbers: true,
-			styleActiveLine: true,
-			matchBrackets: true,
-			keyMap: "sublime",
-			theme: "mdn-like",
-			extraKeys: {"Ctrl-Space": "autocomplete"},
-			foldGutter: true,
-			gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"]
-
-		}
-		if mode == "application/json"
-			options.gutters.unshift("CodeMirror-lint-markers")
-			options.lint = true
-			options.foldOptions = { widget: @foldJson }
-
-		@cm = CodeMirror(@node_cm, options)
-		@cm.on "changes", (changes) =>
-			if @is_loaded and not @is_modified
-				@is_modified = true
-				Page.projector.scheduleRender()
-
-
-	loadEditor: ->
-		if not @is_loading
-			document.getElementsByTagName("head")[0].insertAdjacentHTML(
-				"beforeend",
-				"""<link rel="stylesheet" href="codemirror/all.css" />"""
-			)
-			script = document.createElement('script')
-			script.src = "codemirror/all.js"
-			script.onload = =>
-				@createCodeMirror()
-				@on_loaded.resolve()
-			document.head.appendChild(script)
-		return @on_loaded
-
-	handleSidebarButtonClick: =>
-		Page.is_sidebar_closed = not Page.is_sidebar_closed
-		return false
-
-	handleSaveClick: =>
-		num_errors = (mark for mark in Page.file_editor.cm.getAllMarks() when mark.className == "CodeMirror-lint-mark-error").length
-		if num_errors > 0
-			Page.cmd "wrapperConfirm", ["<b>Warning:</b> The file looks invalid.", "Save anyway"], @save
-		else
-			@save()
-		return false
-
-	save: =>
-		Page.projector.scheduleRender()
-		@is_saving = true
-		Page.cmd "fileWrite", [@inner_path, Text.fileEncode(@cm.getValue())], (res) =>
-			@is_saving = false
-			if res.error
-				Page.cmd "wrapperNotification", ["error", "Error saving #{res.error}"]
-			else
-				@is_save_done = true
-				setTimeout (() =>
-					@is_save_done = false
-					Page.projector.scheduleRender()
-				), 2000
-				@content = @cm.getValue()
-				@is_modified = false
-				if @mode == "Create"
-					@mode = "Edit"
-				Page.file_list.need_update = true
-			Page.projector.scheduleRender()
-
-	render: ->
-		if @need_update
-			@loadEditor().then =>
-				@update()
-			@need_update = false
-		h("div.editor", {afterCreate: @storeCmNode, classes: {error: @error, loaded: @is_loaded}}, [
-			h("a.sidebar-button", {href: "#Sidebar", onclick: @handleSidebarButtonClick}, h("span", "\u2039")),
-			h("div.editor-head", [
-				if @mode in ["Edit", "Create"]
-					h("a.save.button",
-						{href: "#Save", classes: {loading: @is_saving, done: @is_save_done, disabled: not @is_modified}, onclick: @handleSaveClick},
-						if @is_save_done then "Save: done!" else "Save"
-					)
-				h("span.title", @mode, ": ", @inner_path)
-			]),
-			if @error
-				h("div.error-message",
-					h("h2", "Unable to load the file: #{@error}")
-					h("a", {href: Page.file_list.getHref(@inner_path)}, "View in browser")
-				)
-		])
-
-window.FileEditor = FileEditor
\ No newline at end of file
diff --git a/plugins/UiFileManager/media/js/FileItemList.coffee b/plugins/UiFileManager/media/js/FileItemList.coffee
deleted file mode 100644
index df22b81a..00000000
--- a/plugins/UiFileManager/media/js/FileItemList.coffee
+++ /dev/null
@@ -1,194 +0,0 @@
-class FileItemList extends Class
-	constructor: (@inner_path) ->
-		@items = []
-		@updating = false
-		@files_modified = {}
-		@dirs_modified = {}
-		@files_added = {}
-		@dirs_added = {}
-		@files_optional = {}
-		@items_by_name = {}
-
-	# Update item list
-	update: (cb) ->
-		@updating = true
-		@logStart("Updating dirlist")
-		Page.cmd "dirList", {inner_path: @inner_path, stats: true}, (res) =>
-			if res.error
-				@error = res.error
-			else
-				@error = null
-				pattern_ignore = RegExp("^" + Page.site_info.content?.ignore)
-
-				@items.splice(0, @items.length)  # Remove all items
-
-				@items_by_name = {}
-				for row in res
-					row.type = @getFileType(row)
-					row.inner_path = @inner_path + row.name
-					if Page.site_info.content?.ignore and row.inner_path.match(pattern_ignore)
-						row.ignored = true
-					@items.push(row)
-					@items_by_name[row.name] = row
-
-				@sort()
-
-			if Page.site_info?.settings?.own
-				@updateAddedFiles()
-
-			@updateOptionalFiles =>
-				@updating = false
-				cb?()
-				@logEnd("Updating dirlist", @inner_path)
-				Page.projector.scheduleRender()
-
-				@updateModifiedFiles =>
-					Page.projector.scheduleRender()
-
-
-	updateModifiedFiles: (cb) =>
-		# Add modified attribute to changed files
-		Page.cmd "siteListModifiedFiles", [], (res) =>
-			@files_modified = {}
-			@dirs_modified = {}
-			for inner_path in res.modified_files
-				@files_modified[inner_path] = true
-				dir_inner_path = ""
-				dir_parts = inner_path.split("/")
-				for dir_part in dir_parts[..-2]
-					if dir_inner_path
-						dir_inner_path += "/#{dir_part}"
-					else
-						dir_inner_path = dir_part
-					@dirs_modified[dir_inner_path] = true
-
-			cb?()
-
-	# Update newly added items list since last sign
-	updateAddedFiles: =>
-		Page.cmd "fileGet", "content.json", (res) =>
-			if not res
-				return false
-
-			content = JSON.parse(res)
-
-			# Check new files
-			if not content.files?
-				return false
-
-			@files_added = {}
-
-			for file in @items
-				if file.name == "content.json" or file.is_dir
-					continue
-				if not content.files[@inner_path + file.name]
-					@files_added[@inner_path + file.name] = true
-
-			# Check new dirs
-			@dirs_added = {}
-
-			dirs_content = {}
-			for file_name of Object.assign({}, content.files, content.files_optional)
-				if not file_name.startsWith(@inner_path)
-					continue
-
-				pattern = new RegExp("#{@inner_path}(.*?)/")
-				match = file_name.match(pattern)
-
-				if not match
-					continue
-
-				dirs_content[match[1]] = true
-
-			for file in @items
-				if not file.is_dir
-					continue
-				if not dirs_content[file.name]
-					@dirs_added[@inner_path + file.name] = true
-
-	# Update optional files list
-	updateOptionalFiles: (cb) =>
-		Page.cmd "optionalFileList", {filter: ""}, (res) =>
-			@files_optional = {}
-			for optional_file in res
-				@files_optional[optional_file.inner_path] = optional_file
-
-			@addOptionalFilesToItems()
-
-			cb?()
-
-	# Add optional files to item list
-	addOptionalFilesToItems: =>
-		is_added = false
-		for inner_path, optional_file of @files_optional
-			if optional_file.inner_path.startsWith(@inner_path)
-				if @getDirectory(optional_file.inner_path) == @inner_path
-					# Add optional file to list
-					file_name = @getFileName(optional_file.inner_path)
-					if not @items_by_name[file_name]
-						row = {
-							"name": file_name, "type": "file", "optional_empty": true,
-							"size": optional_file.size, "is_dir": false, "inner_path": optional_file.inner_path
-						}
-						@items.push(row)
-						@items_by_name[file_name] = row
-						is_added = true
-				else
-					# Add optional dir to list
-					dir_name = optional_file.inner_path.replace(@inner_path, "").match(/(.*?)\//, "")?[1]
-					if dir_name and not @items_by_name[dir_name]
-						row = {
-							"name": dir_name, "type": "dir", "optional_empty": true,
-							"size": 0, "is_dir": true, "inner_path": optional_file.inner_path
-						}
-						@items.push(row)
-						@items_by_name[dir_name] = row
-						is_added = true
-
-		if is_added
-			@sort()
-
-	getFileType: (file) =>
-		if file.is_dir
-			return "dir"
-		else
-			return "unknown"
-
-	getDirectory: (inner_path) ->
-		if inner_path.indexOf("/") != -1
-			return inner_path.replace(/^(.*\/)(.*?)$/, "$1")
-		else
-			return ""
-
-	getFileName: (inner_path) ->
-		return inner_path.replace(/^(.*\/)(.*?)$/, "$2")
-
-
-	isModified: (inner_path) =>
-		return @files_modified[inner_path] or @dirs_modified[inner_path]
-
-	isAdded: (inner_path) =>
-		return @files_added[inner_path] or @dirs_added[inner_path]
-
-	hasPermissionDelete: (file) =>
-		if file.type in ["dir", "parent"]
-			return false
-
-		if file.inner_path == "content.json"
-			return false
-
-		optional_info = @getOptionalInfo(file.inner_path)
-		if optional_info and optional_info.downloaded_percent > 0
-			return true
-		else
-			return Page.site_info?.settings?.own
-
-	getOptionalInfo: (inner_path) =>
-		return @files_optional[inner_path]
-
-	sort: =>
-		@items.sort (a, b) ->
-			return (b.is_dir - a.is_dir) || a.name.localeCompare(b.name)
-
-
-window.FileItemList = FileItemList
\ No newline at end of file
diff --git a/plugins/UiFileManager/media/js/FileList.coffee b/plugins/UiFileManager/media/js/FileList.coffee
deleted file mode 100644
index 1b5089b0..00000000
--- a/plugins/UiFileManager/media/js/FileList.coffee
+++ /dev/null
@@ -1,268 +0,0 @@
-class FileList extends Class
-	constructor: (@site, @inner_path, @is_owner=false) ->
-		@need_update = true
-		@error = null
-		@url_root = "/list/" + @site + "/"
-		if @inner_path
-			@inner_path += "/"
-			@url_root += @inner_path
-		@log("inited", @url_root)
-		@item_list = new FileItemList(@inner_path)
-		@item_list.items = @item_list.items
-		@menu_create = new Menu()
-
-		@select_action = null
-		@selected = {}
-		@selected_items_num = 0
-		@selected_items_size = 0
-		@selected_optional_empty_num = 0
-
-	isSelectedAll: ->
-		false
-
-	update: =>
-		@item_list.update =>
-			document.body.classList.add("loaded")
-
-	getHref: (inner_path) =>
-		return "/" + @site + "/" + inner_path
-
-	getListHref: (inner_path) =>
-		return "/list/" + @site + "/" + inner_path
-
-	getEditHref: (inner_path, mode=null) =>
-		href = @url_root + "?file=" + inner_path
-		if mode
-			href += "&edit_mode=#{mode}"
-		return href
-
-	checkSelectedItems: =>
-		@selected_items_num = 0
-		@selected_items_size = 0
-		@selected_optional_empty_num = 0
-		for item in @item_list.items
-			if @selected[item.inner_path]
-				@selected_items_num += 1
-				@selected_items_size += item.size
-				optional_info = @item_list.getOptionalInfo(item.inner_path)
-				if optional_info and not optional_info.downloaded_percent > 0
-					@selected_optional_empty_num += 1
-
-	handleMenuCreateClick: =>
-		@menu_create.items = []
-		@menu_create.items.push ["File", @handleNewFileClick]
-		@menu_create.items.push ["Directory", @handleNewDirectoryClick]
-		@menu_create.toggle()
-		return false
-
-	handleNewFileClick: =>
-		Page.cmd "wrapperPrompt", "New file name:", (file_name) =>
-			window.top.location.href = @getEditHref(@inner_path + file_name, "new")
-		return false
-
-	handleNewDirectoryClick: =>
-		Page.cmd "wrapperPrompt", "New directory name:", (res) =>
-			alert("directory name #{res}")
-		return false
-
-	handleSelectClick: (e) =>
-		return false
-
-	handleSelectEnd: (e) =>
-		document.body.removeEventListener('mouseup', @handleSelectEnd)
-		@select_action = null
-
-	handleSelectMousedown: (e) =>
-		inner_path = e.currentTarget.attributes.inner_path.value
-		if @selected[inner_path]
-			delete @selected[inner_path]
-			@select_action = "deselect"
-		else
-			@selected[inner_path] = true
-			@select_action = "select"
-		@checkSelectedItems()
-		document.body.addEventListener('mouseup', @handleSelectEnd)
-		e.stopPropagation()
-		Page.projector.scheduleRender()
-		return false
-
-	handleRowMouseenter: (e) =>
-		if e.buttons and @select_action
-			inner_path = e.target.attributes.inner_path.value
-			if @select_action == "select"
-				@selected[inner_path] = true
-			else
-				delete @selected[inner_path]
-			@checkSelectedItems()
-			Page.projector.scheduleRender()
-		return false
-
-	handleSelectbarCancel: =>
-		@selected = {}
-		@checkSelectedItems()
-		Page.projector.scheduleRender()
-		return false
-
-	handleSelectbarDelete: (e, remove_optional=false) =>
-		for inner_path of @selected
-			optional_info = @item_list.getOptionalInfo(inner_path)
-			delete @selected[inner_path]
-			if optional_info and not remove_optional
-				Page.cmd "optionalFileDelete", inner_path
-			else
-				Page.cmd "fileDelete", inner_path
-		@need_update = true
-		Page.projector.scheduleRender()
-		@checkSelectedItems()
-		return false
-
-	handleSelectbarRemoveOptional: (e) =>
-		return @handleSelectbarDelete(e, true)
-
-	renderSelectbar: =>
-		h("div.selectbar", {classes: {visible: @selected_items_num > 0}}, [
-			"Selected:",
-			h("span.info", [
-				h("span.num", "#{@selected_items_num} files"),
-				h("span.size", "(#{Text.formatSize(@selected_items_size)})"),
-			])
-			h("div.actions", [
-				if @selected_optional_empty_num > 0
-					h("a.action.delete.remove_optional", {href: "#", onclick: @handleSelectbarRemoveOptional}, "Delete and remove optional")
-				else
-					h("a.action.delete", {href: "#", onclick: @handleSelectbarDelete}, "Delete")
-			])
-			h("a.cancel.link", {href: "#", onclick: @handleSelectbarCancel}, "Cancel")
-		])
-
-	renderHead: =>
-		parent_links = []
-		inner_path_parent = ""
-		for parent_dir in @inner_path.split("/")
-			if not parent_dir
-				continue
-			if inner_path_parent
-				inner_path_parent += "/"
-			inner_path_parent += "#{parent_dir}"
-			parent_links.push(
-				[" / ", h("a", {href: @getListHref(inner_path_parent)}, parent_dir)]
-			)
-		return h("div.tr.thead", h("div.td.full",
-			h("a", {href: @getListHref("")}, "root"),
-			parent_links
-		))
-
-	renderItemCheckbox: (item) =>
-		if not @item_list.hasPermissionDelete(item)
-			return [" "]
-
-		return h("a.checkbox-outer", {
-			href: "#Select",
-			onmousedown: @handleSelectMousedown,
-			onclick: @handleSelectClick,
-			inner_path: item.inner_path
-		}, h("span.checkbox"))
-
-	renderItem: (item) =>
-		if item.type == "parent"
-			href = @url_root.replace(/^(.*)\/.{2,255}?$/, "$1/")
-		else if item.type == "dir"
-			href = @url_root + item.name
-		else
-			href = @url_root.replace(/^\/list\//, "/") + item.name
-
-		inner_path = @inner_path + item.name
-		href_edit = @getEditHref(inner_path)
-		is_dir = item.type in ["dir", "parent"]
-		ext = item.name.split(".").pop()
-
-		is_editing = inner_path == Page.file_editor?.inner_path
-		is_editable = not is_dir and item.size < 1024 * 1024 and ext not in window.BINARY_EXTENSIONS
-		is_modified = @item_list.isModified(inner_path)
-		is_added = @item_list.isAdded(inner_path)
-		optional_info = @item_list.getOptionalInfo(inner_path)
-
-		style = ""
-		title = ""
-
-		if optional_info
-			downloaded_percent = optional_info.downloaded_percent
-			if not downloaded_percent
-				downloaded_percent = 0
-			style += "background: linear-gradient(90deg, #fff6dd, #{downloaded_percent}%, white, #{downloaded_percent}%, white);"
-			is_added = false
-
-		if item.ignored
-			is_added = false
-
-		if is_modified then title += " (modified)"
-		if is_added then title += " (new)"
-		if optional_info or item.optional_empty then title += " (optional)"
-		if item.ignored then title += " (ignored from content.json)"
-
-		classes = {
-			"type-#{item.type}": true, editing: is_editing, nobuttons: not is_editable, selected: @selected[inner_path],
-			modified: is_modified, added: is_added, ignored: item.ignored, optional: optional_info, optional_empty: item.optional_empty
-		}
-
-		h("div.tr", {key: item.name, classes: classes, style: style, onmouseenter: @handleRowMouseenter, inner_path: inner_path}, [
-			h("div.td.pre", {title: title},
-				@renderItemCheckbox(item)
-			),
-			h("div.td.name", h("a.link", {href: href}, item.name))
-			h("div.td.buttons", if is_editable then h("a.edit", {href: href_edit}, if Page.site_info.settings.own then "Edit" else "View"))
-			h("div.td.size", if is_dir then "[DIR]" else Text.formatSize(item.size))
-		])
-
-
-	renderItems: =>
-		return [
-			if @item_list.error and not @item_list.items.length and not @item_list.updating then [
-					h("div.tr", {key: "error"}, h("div.td.full.error", @item_list.error))
-				],
-			if @inner_path then @renderItem({"name": "..", type: "parent", size: 0})
-			@item_list.items.map @renderItem
-		]
-
-	renderFoot: =>
-		files = (item for item in @item_list.items when item.type not in ["parent", "dir"])
-		dirs = (item for item in @item_list.items when item.type == "dir")
-		if files.length
-			total_size = (item.size for file in files).reduce (a, b) -> a + b
-		else
-			total_size = 0
-
-		foot_text = "Total: "
-		foot_text += "#{dirs.length} dir, #{files.length} file in #{Text.formatSize(total_size)}"
-
-		return [
-			if dirs.length or files.length or Page.site_info?.settings?.own
-				h("div.tr.foot-info.foot", h("div.td.full", [
-					if @item_list.updating
-						"Updating file list..."
-					else
-						if dirs.length or files.length then foot_text
-					if Page.site_info?.settings?.own
-						h("div.create", [
-							h("a.link", {href: "#Create+new+file", onclick: @handleNewFileClick}, "+ New")
-							@menu_create.render()
-						])
-				]))
-		]
-
-	render: =>
-		if @need_update
-			@update()
-			@need_update = false
-
-			if not @item_list.items
-				return []
-
-		return h("div.files", [
-			@renderSelectbar(),
-			@renderHead(),
-			h("div.tbody", @renderItems()),
-			@renderFoot()
-		])
-
-window.FileList = FileList
diff --git a/plugins/UiFileManager/media/js/UiFileManager.coffee b/plugins/UiFileManager/media/js/UiFileManager.coffee
deleted file mode 100644
index 2126f3b1..00000000
--- a/plugins/UiFileManager/media/js/UiFileManager.coffee
+++ /dev/null
@@ -1,79 +0,0 @@
-window.h = maquette.h
-
-class UiFileManager extends ZeroFrame
-	init: ->
-		@url_params = new URLSearchParams(window.location.search)
-		@list_site = @url_params.get("site")
-		@list_address = @url_params.get("address")
-		@list_inner_path = @url_params.get("inner_path")
-		@editor_inner_path = @url_params.get("file")
-		@file_list = new FileList(@list_site, @list_inner_path)
-
-		@site_info = null
-		@server_info = null
-
-		@is_sidebar_closed = false
-
-		if @editor_inner_path
-			@file_editor = new FileEditor(@editor_inner_path)
-
-		window.onbeforeunload = =>
-			if @file_editor?.isModified()
-				return true
-			else
-				return null
-
-		window.onresize = =>
-			@checkBodyWidth()
-
-		@checkBodyWidth()
-
-		@cmd("wrapperSetViewport", "width=device-width, initial-scale=0.8")
-
-		@cmd "serverInfo", {}, (server_info) =>
-			@server_info = server_info
-		@cmd "siteInfo", {}, (site_info) =>
-			@cmd("wrapperSetTitle", "List: /#{@list_inner_path} - #{site_info.content.title} - ZeroNet")
-			@site_info = site_info
-			if @file_editor then @file_editor.on_loaded.then =>
-				@file_editor.cm.setOption("readOnly", not site_info.settings.own)
-				@file_editor.mode = if site_info.settings.own then "Edit" else "View"
-			@projector.scheduleRender()
-
-	checkBodyWidth: =>
-		if not @file_editor
-			return false
-
-		if document.body.offsetWidth < 960 and not @is_sidebar_closed
-			@is_sidebar_closed = true
-			@projector?.scheduleRender()
-		else if document.body.offsetWidth > 960 and @is_sidebar_closed
-			@is_sidebar_closed = false
-			@projector?.scheduleRender()
-
-	onRequest: (cmd, message) =>
-		if cmd == "setSiteInfo"
-			@site_info = message
-			RateLimitCb 1000, (cb_done) =>
-				@file_list.update(cb_done)
-			@projector.scheduleRender()
-		else if cmd == "setServerInfo"
-			@server_info = message
-			@projector.scheduleRender()
-		else
-			@log "Unknown incoming message:", cmd
-
-	createProjector: =>
-		@projector = maquette.createProjector()
-		@projector.replace($("#content"), @render)
-
-	render: =>
-		return h("div.content#content", [
-			h("div.manager", {classes: {editing: @file_editor, sidebar_closed: @is_sidebar_closed}}, [
-				@file_list.render(),
-				if @file_editor then @file_editor.render()
-			])
-		])
-
-window.Page = new UiFileManager()
-window.Page.createProjector()
diff --git a/plugins/UiFileManager/media/js/all.js b/plugins/UiFileManager/media/js/all.js
deleted file mode 100644
index 4f9b2cbd..00000000
--- a/plugins/UiFileManager/media/js/all.js
+++ /dev/null
@@ -1,3042 +0,0 @@
-
-/* ---- lib/Animation.coffee ---- */
-
-
-(function() {
-  var Animation;
-
-  Animation = (function() {
-    function Animation() {}
-
-    Animation.prototype.slideDown = function(elem, props) {
-      var cstyle, h, margin_bottom, margin_top, padding_bottom, padding_top, transition;
-      if (elem.offsetTop > 2000) {
-        return;
-      }
-      h = elem.offsetHeight;
-      cstyle = window.getComputedStyle(elem);
-      margin_top = cstyle.marginTop;
-      margin_bottom = cstyle.marginBottom;
-      padding_top = cstyle.paddingTop;
-      padding_bottom = cstyle.paddingBottom;
-      transition = cstyle.transition;
-      elem.style.boxSizing = "border-box";
-      elem.style.overflow = "hidden";
-      elem.style.transform = "scale(0.6)";
-      elem.style.opacity = "0";
-      elem.style.height = "0px";
-      elem.style.marginTop = "0px";
-      elem.style.marginBottom = "0px";
-      elem.style.paddingTop = "0px";
-      elem.style.paddingBottom = "0px";
-      elem.style.transition = "none";
-      setTimeout((function() {
-        elem.className += " animate-inout";
-        elem.style.height = h + "px";
-        elem.style.transform = "scale(1)";
-        elem.style.opacity = "1";
-        elem.style.marginTop = margin_top;
-        elem.style.marginBottom = margin_bottom;
-        elem.style.paddingTop = padding_top;
-        return elem.style.paddingBottom = padding_bottom;
-      }), 1);
-      return elem.addEventListener("transitionend", function() {
-        elem.classList.remove("animate-inout");
-        elem.style.transition = elem.style.transform = elem.style.opacity = elem.style.height = null;
-        elem.style.boxSizing = elem.style.marginTop = elem.style.marginBottom = null;
-        elem.style.paddingTop = elem.style.paddingBottom = elem.style.overflow = null;
-        return elem.removeEventListener("transitionend", arguments.callee, false);
-      });
-    };
-
-    Animation.prototype.slideUp = function(elem, remove_func, props) {
-      if (elem.offsetTop > 1000) {
-        return remove_func();
-      }
-      elem.className += " animate-back";
-      elem.style.boxSizing = "border-box";
-      elem.style.height = elem.offsetHeight + "px";
-      elem.style.overflow = "hidden";
-      elem.style.transform = "scale(1)";
-      elem.style.opacity = "1";
-      elem.style.pointerEvents = "none";
-      setTimeout((function() {
-        elem.style.height = "0px";
-        elem.style.marginTop = "0px";
-        elem.style.marginBottom = "0px";
-        elem.style.paddingTop = "0px";
-        elem.style.paddingBottom = "0px";
-        elem.style.transform = "scale(0.8)";
-        elem.style.borderTopWidth = "0px";
-        elem.style.borderBottomWidth = "0px";
-        return elem.style.opacity = "0";
-      }), 1);
-      return elem.addEventListener("transitionend", function(e) {
-        if (e.propertyName === "opacity" || e.elapsedTime >= 0.6) {
-          elem.removeEventListener("transitionend", arguments.callee, false);
-          return remove_func();
-        }
-      });
-    };
-
-    Animation.prototype.slideUpInout = function(elem, remove_func, props) {
-      elem.className += " animate-inout";
-      elem.style.boxSizing = "border-box";
-      elem.style.height = elem.offsetHeight + "px";
-      elem.style.overflow = "hidden";
-      elem.style.transform = "scale(1)";
-      elem.style.opacity = "1";
-      elem.style.pointerEvents = "none";
-      setTimeout((function() {
-        elem.style.height = "0px";
-        elem.style.marginTop = "0px";
-        elem.style.marginBottom = "0px";
-        elem.style.paddingTop = "0px";
-        elem.style.paddingBottom = "0px";
-        elem.style.transform = "scale(0.8)";
-        elem.style.borderTopWidth = "0px";
-        elem.style.borderBottomWidth = "0px";
-        return elem.style.opacity = "0";
-      }), 1);
-      return elem.addEventListener("transitionend", function(e) {
-        if (e.propertyName === "opacity" || e.elapsedTime >= 0.6) {
-          elem.removeEventListener("transitionend", arguments.callee, false);
-          return remove_func();
-        }
-      });
-    };
-
-    Animation.prototype.showRight = function(elem, props) {
-      elem.className += " animate";
-      elem.style.opacity = 0;
-      elem.style.transform = "TranslateX(-20px) Scale(1.01)";
-      setTimeout((function() {
-        elem.style.opacity = 1;
-        return elem.style.transform = "TranslateX(0px) Scale(1)";
-      }), 1);
-      return elem.addEventListener("transitionend", function() {
-        elem.classList.remove("animate");
-        return elem.style.transform = elem.style.opacity = null;
-      });
-    };
-
-    Animation.prototype.show = function(elem, props) {
-      var delay, ref;
-      delay = ((ref = arguments[arguments.length - 2]) != null ? ref.delay : void 0) * 1000 || 1;
-      elem.style.opacity = 0;
-      setTimeout((function() {
-        return elem.className += " animate";
-      }), 1);
-      setTimeout((function() {
-        return elem.style.opacity = 1;
-      }), delay);
-      return elem.addEventListener("transitionend", function() {
-        elem.classList.remove("animate");
-        elem.style.opacity = null;
-        return elem.removeEventListener("transitionend", arguments.callee, false);
-      });
-    };
-
-    Animation.prototype.hide = function(elem, remove_func, props) {
-      var delay, ref;
-      delay = ((ref = arguments[arguments.length - 2]) != null ? ref.delay : void 0) * 1000 || 1;
-      elem.className += " animate";
-      setTimeout((function() {
-        return elem.style.opacity = 0;
-      }), delay);
-      return elem.addEventListener("transitionend", function(e) {
-        if (e.propertyName === "opacity") {
-          return remove_func();
-        }
-      });
-    };
-
-    Animation.prototype.addVisibleClass = function(elem, props) {
-      return setTimeout(function() {
-        return elem.classList.add("visible");
-      });
-    };
-
-    return Animation;
-
-  })();
-
-  window.Animation = new Animation();
-
-}).call(this);
-
-/* ---- lib/Class.coffee ---- */
-
-
-(function() {
-  var Class,
-    slice = [].slice;
-
-  Class = (function() {
-    function Class() {}
-
-    Class.prototype.trace = true;
-
-    Class.prototype.log = function() {
-      var args;
-      args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
-      if (!this.trace) {
-        return;
-      }
-      if (typeof console === 'undefined') {
-        return;
-      }
-      args.unshift("[" + this.constructor.name + "]");
-      console.log.apply(console, args);
-      return this;
-    };
-
-    Class.prototype.logStart = function() {
-      var args, name;
-      name = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
-      if (!this.trace) {
-        return;
-      }
-      this.logtimers || (this.logtimers = {});
-      this.logtimers[name] = +(new Date);
-      if (args.length > 0) {
-        this.log.apply(this, ["" + name].concat(slice.call(args), ["(started)"]));
-      }
-      return this;
-    };
-
-    Class.prototype.logEnd = function() {
-      var args, ms, name;
-      name = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
-      ms = +(new Date) - this.logtimers[name];
-      this.log.apply(this, ["" + name].concat(slice.call(args), ["(Done in " + ms + "ms)"]));
-      return this;
-    };
-
-    return Class;
-
-  })();
-
-  window.Class = Class;
-
-}).call(this);
-
-/* ---- lib/Dollar.coffee ---- */
-
-
-(function() {
-  window.$ = function(selector) {
-    if (selector.startsWith("#")) {
-      return document.getElementById(selector.replace("#", ""));
-    }
-  };
-
-}).call(this);
-
-/* ---- lib/ItemList.coffee ---- */
-
-
-(function() {
-  var ItemList;
-
-  ItemList = (function() {
-    function ItemList(item_class1, key1) {
-      this.item_class = item_class1;
-      this.key = key1;
-      this.items = [];
-      this.items_bykey = {};
-    }
-
-    ItemList.prototype.sync = function(rows, item_class, key) {
-      var current_obj, i, item, len, results, row;
-      this.items.splice(0, this.items.length);
-      results = [];
-      for (i = 0, len = rows.length; i < len; i++) {
-        row = rows[i];
-        current_obj = this.items_bykey[row[this.key]];
-        if (current_obj) {
-          current_obj.row = row;
-          results.push(this.items.push(current_obj));
-        } else {
-          item = new this.item_class(row, this);
-          this.items_bykey[row[this.key]] = item;
-          results.push(this.items.push(item));
-        }
-      }
-      return results;
-    };
-
-    ItemList.prototype.deleteItem = function(item) {
-      var index;
-      index = this.items.indexOf(item);
-      if (index > -1) {
-        this.items.splice(index, 1);
-      } else {
-        console.log("Can't delete item", item);
-      }
-      return delete this.items_bykey[item.row[this.key]];
-    };
-
-    return ItemList;
-
-  })();
-
-  window.ItemList = ItemList;
-
-}).call(this);
-
-/* ---- lib/Menu.coffee ---- */
-
-
-(function() {
-  var Menu,
-    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
-    indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
-
-  Menu = (function() {
-    function Menu() {
-      this.render = bind(this.render, this);
-      this.getStyle = bind(this.getStyle, this);
-      this.renderItem = bind(this.renderItem, this);
-      this.handleClick = bind(this.handleClick, this);
-      this.getDirection = bind(this.getDirection, this);
-      this.storeNode = bind(this.storeNode, this);
-      this.toggle = bind(this.toggle, this);
-      this.hide = bind(this.hide, this);
-      this.show = bind(this.show, this);
-      this.visible = false;
-      this.items = [];
-      this.node = null;
-      this.height = 0;
-      this.direction = "bottom";
-    }
-
-    Menu.prototype.show = function() {
-      var ref;
-      if ((ref = window.visible_menu) != null) {
-        ref.hide();
-      }
-      this.visible = true;
-      window.visible_menu = this;
-      return this.direction = this.getDirection();
-    };
-
-    Menu.prototype.hide = function() {
-      return this.visible = false;
-    };
-
-    Menu.prototype.toggle = function() {
-      if (this.visible) {
-        this.hide();
-      } else {
-        this.show();
-      }
-      return Page.projector.scheduleRender();
-    };
-
-    Menu.prototype.addItem = function(title, cb, selected) {
-      if (selected == null) {
-        selected = false;
-      }
-      return this.items.push([title, cb, selected]);
-    };
-
-    Menu.prototype.storeNode = function(node) {
-      this.node = node;
-      if (this.visible) {
-        node.className = node.className.replace("visible", "");
-        setTimeout(((function(_this) {
-          return function() {
-            node.className += " visible";
-            return node.attributes.style.value = _this.getStyle();
-          };
-        })(this)), 20);
-        node.style.maxHeight = "none";
-        this.height = node.offsetHeight;
-        node.style.maxHeight = "0px";
-        return this.direction = this.getDirection();
-      }
-    };
-
-    Menu.prototype.getDirection = function() {
-      if (this.node && this.node.parentNode.getBoundingClientRect().top + this.height + 60 > document.body.clientHeight && this.node.parentNode.getBoundingClientRect().top - this.height > 0) {
-        return "top";
-      } else {
-        return "bottom";
-      }
-    };
-
-    Menu.prototype.handleClick = function(e) {
-      var cb, i, item, keep_menu, len, ref, selected, title;
-      keep_menu = false;
-      ref = this.items;
-      for (i = 0, len = ref.length; i < len; i++) {
-        item = ref[i];
-        title = item[0], cb = item[1], selected = item[2];
-        if (title === e.currentTarget.textContent || e.currentTarget["data-title"] === title) {
-          keep_menu = typeof cb === "function" ? cb(item) : void 0;
-          break;
-        }
-      }
-      if (keep_menu !== true && cb !== null) {
-        this.hide();
-      }
-      return false;
-    };
-
-    Menu.prototype.renderItem = function(item) {
-      var cb, classes, href, onclick, selected, title;
-      title = item[0], cb = item[1], selected = item[2];
-      if (typeof selected === "function") {
-        selected = selected();
-      }
-      if (title === "---") {
-        return h("div.menu-item-separator", {
-          key: Time.timestamp()
-        });
-      } else {
-        if (cb === null) {
-          href = void 0;
-          onclick = this.handleClick;
-        } else if (typeof cb === "string") {
-          href = cb;
-          onclick = true;
-        } else {
-          href = "#" + title;
-          onclick = this.handleClick;
-        }
-        classes = {
-          "selected": selected,
-          "noaction": cb === null
-        };
-        return h("a.menu-item", {
-          href: href,
-          onclick: onclick,
-          "data-title": title,
-          key: title,
-          classes: classes
-        }, title);
-      }
-    };
-
-    Menu.prototype.getStyle = function() {
-      var max_height, style;
-      if (this.visible) {
-        max_height = this.height;
-      } else {
-        max_height = 0;
-      }
-      style = "max-height: " + max_height + "px";
-      if (this.direction === "top") {
-        style += ";margin-top: " + (0 - this.height - 50) + "px";
-      } else {
-        style += ";margin-top: 0px";
-      }
-      return style;
-    };
-
-    Menu.prototype.render = function(class_name) {
-      if (class_name == null) {
-        class_name = "";
-      }
-      if (this.visible || this.node) {
-        return h("div.menu" + class_name, {
-          classes: {
-            "visible": this.visible
-          },
-          style: this.getStyle(),
-          afterCreate: this.storeNode
-        }, this.items.map(this.renderItem));
-      }
-    };
-
-    return Menu;
-
-  })();
-
-  window.Menu = Menu;
-
-  document.body.addEventListener("mouseup", function(e) {
-    var menu_node, menu_parents, ref, ref1;
-    if (!window.visible_menu || !window.visible_menu.node) {
-      return false;
-    }
-    menu_node = window.visible_menu.node;
-    menu_parents = [menu_node, menu_node.parentNode];
-    if ((ref = e.target.parentNode, indexOf.call(menu_parents, ref) < 0) && (ref1 = e.target.parentNode.parentNode, indexOf.call(menu_parents, ref1) < 0)) {
-      window.visible_menu.hide();
-      return Page.projector.scheduleRender();
-    }
-  });
-
-}).call(this);
-
-/* ---- lib/Promise.coffee ---- */
-
-
-(function() {
-  var Promise,
-    slice = [].slice;
-
-  Promise = (function() {
-    Promise.when = function() {
-      var args, fn, i, len, num_uncompleted, promise, task, task_id, tasks;
-      tasks = 1 <= arguments.length ? slice.call(arguments, 0) : [];
-      num_uncompleted = tasks.length;
-      args = new Array(num_uncompleted);
-      promise = new Promise();
-      fn = function(task_id) {
-        return task.then(function() {
-          args[task_id] = Array.prototype.slice.call(arguments);
-          num_uncompleted--;
-          if (num_uncompleted === 0) {
-            return promise.complete.apply(promise, args);
-          }
-        });
-      };
-      for (task_id = i = 0, len = tasks.length; i < len; task_id = ++i) {
-        task = tasks[task_id];
-        fn(task_id);
-      }
-      return promise;
-    };
-
-    function Promise() {
-      this.resolved = false;
-      this.end_promise = null;
-      this.result = null;
-      this.callbacks = [];
-    }
-
-    Promise.prototype.resolve = function() {
-      var back, callback, i, len, ref;
-      if (this.resolved) {
-        return false;
-      }
-      this.resolved = true;
-      this.data = arguments;
-      if (!arguments.length) {
-        this.data = [true];
-      }
-      this.result = this.data[0];
-      ref = this.callbacks;
-      for (i = 0, len = ref.length; i < len; i++) {
-        callback = ref[i];
-        back = callback.apply(callback, this.data);
-      }
-      if (this.end_promise) {
-        return this.end_promise.resolve(back);
-      }
-    };
-
-    Promise.prototype.fail = function() {
-      return this.resolve(false);
-    };
-
-    Promise.prototype.then = function(callback) {
-      if (this.resolved === true) {
-        callback.apply(callback, this.data);
-        return;
-      }
-      this.callbacks.push(callback);
-      return this.end_promise = new Promise();
-    };
-
-    return Promise;
-
-  })();
-
-  window.Promise = Promise;
-
-
-  /*
-  s = Date.now()
-  log = (text) ->
-  	console.log Date.now()-s, Array.prototype.slice.call(arguments).join(", ")
-  
-  log "Started"
-  
-  cmd = (query) ->
-  	p = new Promise()
-  	setTimeout ( ->
-  		p.resolve query+" Result"
-  	), 100
-  	return p
-  
-  back = cmd("SELECT * FROM message").then (res) ->
-  	log res
-  	return "Return from query"
-  .then (res) ->
-  	log "Back then", res
-  
-  log "Query started", back
-   */
-
-}).call(this);
-
-/* ---- lib/Prototypes.coffee ---- */
-
-
-(function() {
-  String.prototype.startsWith = function(s) {
-    return this.slice(0, s.length) === s;
-  };
-
-  String.prototype.endsWith = function(s) {
-    return s === '' || this.slice(-s.length) === s;
-  };
-
-  String.prototype.repeat = function(count) {
-    return new Array(count + 1).join(this);
-  };
-
-  window.isEmpty = function(obj) {
-    var key;
-    for (key in obj) {
-      return false;
-    }
-    return true;
-  };
-
-}).call(this);
-
-/* ---- lib/RateLimitCb.coffee ---- */
-
-
-(function() {
-  var call_after_interval, calling, calling_iterval, last_time,
-    slice = [].slice;
-
-  last_time = {};
-
-  calling = {};
-
-  calling_iterval = {};
-
-  call_after_interval = {};
-
-  window.RateLimitCb = function(interval, fn, args) {
-    var cb;
-    if (args == null) {
-      args = [];
-    }
-    cb = function() {
-      var left;
-      left = interval - (Date.now() - last_time[fn]);
-      if (left <= 0) {
-        delete last_time[fn];
-        if (calling[fn]) {
-          RateLimitCb(interval, fn, calling[fn]);
-        }
-        return delete calling[fn];
-      } else {
-        return setTimeout((function() {
-          delete last_time[fn];
-          if (calling[fn]) {
-            RateLimitCb(interval, fn, calling[fn]);
-          }
-          return delete calling[fn];
-        }), left);
-      }
-    };
-    if (last_time[fn]) {
-      return calling[fn] = args;
-    } else {
-      last_time[fn] = Date.now();
-      return fn.apply(this, [cb].concat(slice.call(args)));
-    }
-  };
-
-  window.RateLimit = function(interval, fn) {
-    if (calling_iterval[fn] > interval) {
-      clearInterval(calling[fn]);
-      delete calling[fn];
-    }
-    if (!calling[fn]) {
-      call_after_interval[fn] = false;
-      fn();
-      calling_iterval[fn] = interval;
-      return calling[fn] = setTimeout((function() {
-        if (call_after_interval[fn]) {
-          fn();
-        }
-        delete calling[fn];
-        return delete call_after_interval[fn];
-      }), interval);
-    } else {
-      return call_after_interval[fn] = true;
-    }
-  };
-
-
-  /*
-  window.s = Date.now()
-  window.load = (done, num) ->
-    console.log "Loading #{num}...", Date.now()-window.s
-    setTimeout (-> done()), 1000
-  
-  RateLimit 500, window.load, [0] # Called instantly
-  RateLimit 500, window.load, [1]
-  setTimeout (-> RateLimit 500, window.load, [300]), 300
-  setTimeout (-> RateLimit 500, window.load, [600]), 600 # Called after 1000ms
-  setTimeout (-> RateLimit 500, window.load, [1000]), 1000
-  setTimeout (-> RateLimit 500, window.load, [1200]), 1200  # Called after 2000ms
-  setTimeout (-> RateLimit 500, window.load, [3000]), 3000  # Called after 3000ms
-   */
-
-}).call(this);
-
-/* ---- lib/Text.coffee ---- */
-
-
-(function() {
-  var Text,
-    indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
-
-  Text = (function() {
-    function Text() {}
-
-    Text.prototype.toColor = function(text, saturation, lightness) {
-      var hash, i, j, ref;
-      if (saturation == null) {
-        saturation = 30;
-      }
-      if (lightness == null) {
-        lightness = 50;
-      }
-      hash = 0;
-      for (i = j = 0, ref = text.length - 1; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) {
-        hash += text.charCodeAt(i) * i;
-        hash = hash % 1777;
-      }
-      return "hsl(" + (hash % 360) + ("," + saturation + "%," + lightness + "%)");
-    };
-
-    Text.prototype.renderMarked = function(text, options) {
-      if (options == null) {
-        options = {};
-      }
-      options["gfm"] = true;
-      options["breaks"] = true;
-      options["sanitize"] = true;
-      options["renderer"] = marked_renderer;
-      text = marked(text, options);
-      return this.fixHtmlLinks(text);
-    };
-
-    Text.prototype.emailLinks = function(text) {
-      return text.replace(/([a-zA-Z0-9]+)@zeroid.bit/g, "<a href='?to=$1' onclick='return Page.message_create.show(\"$1\")'>$1@zeroid.bit</a>");
-    };
-
-    Text.prototype.fixHtmlLinks = function(text) {
-      if (window.is_proxy) {
-        return text.replace(/href="http:\/\/(127.0.0.1|localhost):43110/g, 'href="http://zero');
-      } else {
-        return text.replace(/href="http:\/\/(127.0.0.1|localhost):43110/g, 'href="');
-      }
-    };
-
-    Text.prototype.fixLink = function(link) {
-      var back;
-      if (window.is_proxy) {
-        back = link.replace(/http:\/\/(127.0.0.1|localhost):43110/, 'http://zero');
-        return back.replace(/http:\/\/zero\/([^\/]+\.bit)/, "http://$1");
-      } else {
-        return link.replace(/http:\/\/(127.0.0.1|localhost):43110/, '');
-      }
-    };
-
-    Text.prototype.toUrl = function(text) {
-      return text.replace(/[^A-Za-z0-9]/g, "+").replace(/[+]+/g, "+").replace(/[+]+$/, "");
-    };
-
-    Text.prototype.getSiteUrl = function(address) {
-      if (window.is_proxy) {
-        if (indexOf.call(address, ".") >= 0) {
-          return "http://" + address + "/";
-        } else {
-          return "http://zero/" + address + "/";
-        }
-      } else {
-        return "/" + address + "/";
-      }
-    };
-
-    Text.prototype.fixReply = function(text) {
-      return text.replace(/(>.*\n)([^\n>])/gm, "$1\n$2");
-    };
-
-    Text.prototype.toBitcoinAddress = function(text) {
-      return text.replace(/[^A-Za-z0-9]/g, "");
-    };
-
-    Text.prototype.jsonEncode = function(obj) {
-      return unescape(encodeURIComponent(JSON.stringify(obj)));
-    };
-
-    Text.prototype.jsonDecode = function(obj) {
-      return JSON.parse(decodeURIComponent(escape(obj)));
-    };
-
-    Text.prototype.fileEncode = function(obj) {
-      if (typeof obj === "string") {
-        return btoa(unescape(encodeURIComponent(obj)));
-      } else {
-        return btoa(unescape(encodeURIComponent(JSON.stringify(obj, void 0, '\t'))));
-      }
-    };
-
-    Text.prototype.utf8Encode = function(s) {
-      return unescape(encodeURIComponent(s));
-    };
-
-    Text.prototype.utf8Decode = function(s) {
-      return decodeURIComponent(escape(s));
-    };
-
-    Text.prototype.distance = function(s1, s2) {
-      var char, extra_parts, j, key, len, match, next_find, next_find_i, val;
-      s1 = s1.toLocaleLowerCase();
-      s2 = s2.toLocaleLowerCase();
-      next_find_i = 0;
-      next_find = s2[0];
-      match = true;
-      extra_parts = {};
-      for (j = 0, len = s1.length; j < len; j++) {
-        char = s1[j];
-        if (char !== next_find) {
-          if (extra_parts[next_find_i]) {
-            extra_parts[next_find_i] += char;
-          } else {
-            extra_parts[next_find_i] = char;
-          }
-        } else {
-          next_find_i++;
-          next_find = s2[next_find_i];
-        }
-      }
-      if (extra_parts[next_find_i]) {
-        extra_parts[next_find_i] = "";
-      }
-      extra_parts = (function() {
-        var results;
-        results = [];
-        for (key in extra_parts) {
-          val = extra_parts[key];
-          results.push(val);
-        }
-        return results;
-      })();
-      if (next_find_i >= s2.length) {
-        return extra_parts.length + extra_parts.join("").length;
-      } else {
-        return false;
-      }
-    };
-
-    Text.prototype.parseQuery = function(query) {
-      var j, key, len, params, part, parts, ref, val;
-      params = {};
-      parts = query.split('&');
-      for (j = 0, len = parts.length; j < len; j++) {
-        part = parts[j];
-        ref = part.split("="), key = ref[0], val = ref[1];
-        if (val) {
-          params[decodeURIComponent(key)] = decodeURIComponent(val);
-        } else {
-          params["url"] = decodeURIComponent(key);
-        }
-      }
-      return params;
-    };
-
-    Text.prototype.encodeQuery = function(params) {
-      var back, key, val;
-      back = [];
-      if (params.url) {
-        back.push(params.url);
-      }
-      for (key in params) {
-        val = params[key];
-        if (!val || key === "url") {
-          continue;
-        }
-        back.push((encodeURIComponent(key)) + "=" + (encodeURIComponent(val)));
-      }
-      return back.join("&");
-    };
-
-    Text.prototype.highlight = function(text, search) {
-      var back, i, j, len, part, parts;
-      if (!text) {
-        return [""];
-      }
-      parts = text.split(RegExp(search, "i"));
-      back = [];
-      for (i = j = 0, len = parts.length; j < len; i = ++j) {
-        part = parts[i];
-        back.push(part);
-        if (i < parts.length - 1) {
-          back.push(h("span.highlight", {
-            key: i
-          }, search));
-        }
-      }
-      return back;
-    };
-
-    Text.prototype.formatSize = function(size) {
-      var size_mb;
-      if (isNaN(parseInt(size))) {
-        return "";
-      }
-      size_mb = size / 1024 / 1024;
-      if (size_mb >= 1000) {
-        return (size_mb / 1024).toFixed(1) + " GB";
-      } else if (size_mb >= 100) {
-        return size_mb.toFixed(0) + " MB";
-      } else if (size / 1024 >= 1000) {
-        return size_mb.toFixed(2) + " MB";
-      } else {
-        return (parseInt(size) / 1024).toFixed(2) + " KB";
-      }
-    };
-
-    return Text;
-
-  })();
-
-  window.is_proxy = document.location.host === "zero" || window.location.pathname === "/";
-
-  window.Text = new Text();
-
-}).call(this);
-
-/* ---- lib/Time.coffee ---- */
-
-
-(function() {
-  var Time;
-
-  Time = (function() {
-    function Time() {}
-
-    Time.prototype.since = function(timestamp) {
-      var back, minutes, now, secs;
-      now = +(new Date) / 1000;
-      if (timestamp > 1000000000000) {
-        timestamp = timestamp / 1000;
-      }
-      secs = now - timestamp;
-      if (secs < 60) {
-        back = "Just now";
-      } else if (secs < 60 * 60) {
-        minutes = Math.round(secs / 60);
-        back = "" + minutes + " minutes ago";
-      } else if (secs < 60 * 60 * 24) {
-        back = (Math.round(secs / 60 / 60)) + " hours ago";
-      } else if (secs < 60 * 60 * 24 * 3) {
-        back = (Math.round(secs / 60 / 60 / 24)) + " days ago";
-      } else {
-        back = "on " + this.date(timestamp);
-      }
-      back = back.replace(/^1 ([a-z]+)s/, "1 $1");
-      return back;
-    };
-
-    Time.prototype.dateIso = function(timestamp) {
-      var tzoffset;
-      if (timestamp == null) {
-        timestamp = null;
-      }
-      if (!timestamp) {
-        timestamp = window.Time.timestamp();
-      }
-      if (timestamp > 1000000000000) {
-        timestamp = timestamp / 1000;
-      }
-      tzoffset = (new Date()).getTimezoneOffset() * 60;
-      return (new Date((timestamp - tzoffset) * 1000)).toISOString().split("T")[0];
-    };
-
-    Time.prototype.date = function(timestamp, format) {
-      var display, parts;
-      if (timestamp == null) {
-        timestamp = null;
-      }
-      if (format == null) {
-        format = "short";
-      }
-      if (!timestamp) {
-        timestamp = window.Time.timestamp();
-      }
-      if (timestamp > 1000000000000) {
-        timestamp = timestamp / 1000;
-      }
-      parts = (new Date(timestamp * 1000)).toString().split(" ");
-      if (format === "short") {
-        display = parts.slice(1, 4);
-      } else if (format === "day") {
-        display = parts.slice(1, 3);
-      } else if (format === "month") {
-        display = [parts[1], parts[3]];
-      } else if (format === "long") {
-        display = parts.slice(1, 5);
-      }
-      return display.join(" ").replace(/( [0-9]{4})/, ",$1");
-    };
-
-    Time.prototype.weekDay = function(timestamp) {
-      if (timestamp > 1000000000000) {
-        timestamp = timestamp / 1000;
-      }
-      return ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][(new Date(timestamp * 1000)).getDay()];
-    };
-
-    Time.prototype.timestamp = function(date) {
-      if (date == null) {
-        date = "";
-      }
-      if (date === "now" || date === "") {
-        return parseInt(+(new Date) / 1000);
-      } else {
-        return parseInt(Date.parse(date) / 1000);
-      }
-    };
-
-    return Time;
-
-  })();
-
-  window.Time = new Time;
-
-}).call(this);
-
-/* ---- lib/ZeroFrame.coffee ---- */
-
-
-(function() {
-  var ZeroFrame,
-    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
-    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
-    hasProp = {}.hasOwnProperty;
-
-  ZeroFrame = (function(superClass) {
-    extend(ZeroFrame, superClass);
-
-    function ZeroFrame(url) {
-      this.onCloseWebsocket = bind(this.onCloseWebsocket, this);
-      this.onOpenWebsocket = bind(this.onOpenWebsocket, this);
-      this.onRequest = bind(this.onRequest, this);
-      this.onMessage = bind(this.onMessage, this);
-      this.url = url;
-      this.waiting_cb = {};
-      this.wrapper_nonce = document.location.href.replace(/.*wrapper_nonce=([A-Za-z0-9]+).*/, "$1");
-      this.connect();
-      this.next_message_id = 1;
-      this.history_state = {};
-      this.init();
-    }
-
-    ZeroFrame.prototype.init = function() {
-      return this;
-    };
-
-    ZeroFrame.prototype.connect = function() {
-      this.target = window.parent;
-      window.addEventListener("message", this.onMessage, false);
-      this.cmd("innerReady");
-      window.addEventListener("beforeunload", (function(_this) {
-        return function(e) {
-          _this.log("save scrollTop", window.pageYOffset);
-          _this.history_state["scrollTop"] = window.pageYOffset;
-          return _this.cmd("wrapperReplaceState", [_this.history_state, null]);
-        };
-      })(this));
-      return this.cmd("wrapperGetState", [], (function(_this) {
-        return function(state) {
-          if (state != null) {
-            _this.history_state = state;
-          }
-          _this.log("restore scrollTop", state, window.pageYOffset);
-          if (window.pageYOffset === 0 && state) {
-            return window.scroll(window.pageXOffset, state.scrollTop);
-          }
-        };
-      })(this));
-    };
-
-    ZeroFrame.prototype.onMessage = function(e) {
-      var cmd, message;
-      message = e.data;
-      cmd = message.cmd;
-      if (cmd === "response") {
-        if (this.waiting_cb[message.to] != null) {
-          return this.waiting_cb[message.to](message.result);
-        } else {
-          return this.log("Websocket callback not found:", message);
-        }
-      } else if (cmd === "wrapperReady") {
-        return this.cmd("innerReady");
-      } else if (cmd === "ping") {
-        return this.response(message.id, "pong");
-      } else if (cmd === "wrapperOpenedWebsocket") {
-        return this.onOpenWebsocket();
-      } else if (cmd === "wrapperClosedWebsocket") {
-        return this.onCloseWebsocket();
-      } else {
-        return this.onRequest(cmd, message.params);
-      }
-    };
-
-    ZeroFrame.prototype.onRequest = function(cmd, message) {
-      return this.log("Unknown request", message);
-    };
-
-    ZeroFrame.prototype.response = function(to, result) {
-      return this.send({
-        "cmd": "response",
-        "to": to,
-        "result": result
-      });
-    };
-
-    ZeroFrame.prototype.cmd = function(cmd, params, cb) {
-      if (params == null) {
-        params = {};
-      }
-      if (cb == null) {
-        cb = null;
-      }
-      return this.send({
-        "cmd": cmd,
-        "params": params
-      }, cb);
-    };
-
-    ZeroFrame.prototype.send = function(message, cb) {
-      if (cb == null) {
-        cb = null;
-      }
-      message.wrapper_nonce = this.wrapper_nonce;
-      message.id = this.next_message_id;
-      this.next_message_id += 1;
-      this.target.postMessage(message, "*");
-      if (cb) {
-        return this.waiting_cb[message.id] = cb;
-      }
-    };
-
-    ZeroFrame.prototype.onOpenWebsocket = function() {
-      return this.log("Websocket open");
-    };
-
-    ZeroFrame.prototype.onCloseWebsocket = function() {
-      return this.log("Websocket close");
-    };
-
-    return ZeroFrame;
-
-  })(Class);
-
-  window.ZeroFrame = ZeroFrame;
-
-}).call(this);
-
-/* ---- lib/maquette.js ---- */
-
-
-(function (root, factory) {
-    if (typeof define === 'function' && define.amd) {
-        // AMD. Register as an anonymous module.
-        define(['exports'], factory);
-    } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
-        // CommonJS
-        factory(exports);
-    } else {
-        // Browser globals
-        factory(root.maquette = {});
-    }
-}(this, function (exports) {
-    'use strict';
-    ;
-    ;
-    ;
-    ;
-    var NAMESPACE_W3 = 'http://www.w3.org/';
-    var NAMESPACE_SVG = NAMESPACE_W3 + '2000/svg';
-    var NAMESPACE_XLINK = NAMESPACE_W3 + '1999/xlink';
-    // Utilities
-    var emptyArray = [];
-    var extend = function (base, overrides) {
-        var result = {};
-        Object.keys(base).forEach(function (key) {
-            result[key] = base[key];
-        });
-        if (overrides) {
-            Object.keys(overrides).forEach(function (key) {
-                result[key] = overrides[key];
-            });
-        }
-        return result;
-    };
-    // Hyperscript helper functions
-    var same = function (vnode1, vnode2) {
-        if (vnode1.vnodeSelector !== vnode2.vnodeSelector) {
-            return false;
-        }
-        if (vnode1.properties && vnode2.properties) {
-            if (vnode1.properties.key !== vnode2.properties.key) {
-                return false;
-            }
-            return vnode1.properties.bind === vnode2.properties.bind;
-        }
-        return !vnode1.properties && !vnode2.properties;
-    };
-    var toTextVNode = function (data) {
-        return {
-            vnodeSelector: '',
-            properties: undefined,
-            children: undefined,
-            text: data.toString(),
-            domNode: null
-        };
-    };
-    var appendChildren = function (parentSelector, insertions, main) {
-        for (var i = 0; i < insertions.length; i++) {
-            var item = insertions[i];
-            if (Array.isArray(item)) {
-                appendChildren(parentSelector, item, main);
-            } else {
-                if (item !== null && item !== undefined) {
-                    if (!item.hasOwnProperty('vnodeSelector')) {
-                        item = toTextVNode(item);
-                    }
-                    main.push(item);
-                }
-            }
-        }
-    };
-    // Render helper functions
-    var missingTransition = function () {
-        throw new Error('Provide a transitions object to the projectionOptions to do animations');
-    };
-    var DEFAULT_PROJECTION_OPTIONS = {
-        namespace: undefined,
-        eventHandlerInterceptor: undefined,
-        styleApplyer: function (domNode, styleName, value) {
-            // Provides a hook to add vendor prefixes for browsers that still need it.
-            domNode.style[styleName] = value;
-        },
-        transitions: {
-            enter: missingTransition,
-            exit: missingTransition
-        }
-    };
-    var applyDefaultProjectionOptions = function (projectorOptions) {
-        return extend(DEFAULT_PROJECTION_OPTIONS, projectorOptions);
-    };
-    var checkStyleValue = function (styleValue) {
-        if (typeof styleValue !== 'string') {
-            throw new Error('Style values must be strings');
-        }
-    };
-    var setProperties = function (domNode, properties, projectionOptions) {
-        if (!properties) {
-            return;
-        }
-        var eventHandlerInterceptor = projectionOptions.eventHandlerInterceptor;
-        var propNames = Object.keys(properties);
-        var propCount = propNames.length;
-        for (var i = 0; i < propCount; i++) {
-            var propName = propNames[i];
-            /* tslint:disable:no-var-keyword: edge case */
-            var propValue = properties[propName];
-            /* tslint:enable:no-var-keyword */
-            if (propName === 'className') {
-                throw new Error('Property "className" is not supported, use "class".');
-            } else if (propName === 'class') {
-                if (domNode.className) {
-                    // May happen if classes is specified before class
-                    domNode.className += ' ' + propValue;
-                } else {
-                    domNode.className = propValue;
-                }
-            } else if (propName === 'classes') {
-                // object with string keys and boolean values
-                var classNames = Object.keys(propValue);
-                var classNameCount = classNames.length;
-                for (var j = 0; j < classNameCount; j++) {
-                    var className = classNames[j];
-                    if (propValue[className]) {
-                        domNode.classList.add(className);
-                    }
-                }
-            } else if (propName === 'styles') {
-                // object with string keys and string (!) values
-                var styleNames = Object.keys(propValue);
-                var styleCount = styleNames.length;
-                for (var j = 0; j < styleCount; j++) {
-                    var styleName = styleNames[j];
-                    var styleValue = propValue[styleName];
-                    if (styleValue) {
-                        checkStyleValue(styleValue);
-                        projectionOptions.styleApplyer(domNode, styleName, styleValue);
-                    }
-                }
-            } else if (propName === 'key') {
-                continue;
-            } else if (propValue === null || propValue === undefined) {
-                continue;
-            } else {
-                var type = typeof propValue;
-                if (type === 'function') {
-                    if (propName.lastIndexOf('on', 0) === 0) {
-                        if (eventHandlerInterceptor) {
-                            propValue = eventHandlerInterceptor(propName, propValue, domNode, properties);    // intercept eventhandlers
-                        }
-                        if (propName === 'oninput') {
-                            (function () {
-                                // record the evt.target.value, because IE and Edge sometimes do a requestAnimationFrame between changing value and running oninput
-                                var oldPropValue = propValue;
-                                propValue = function (evt) {
-                                    evt.target['oninput-value'] = evt.target.value;
-                                    // may be HTMLTextAreaElement as well
-                                    oldPropValue.apply(this, [evt]);
-                                };
-                            }());
-                        }
-                        domNode[propName] = propValue;
-                    }
-                } else if (type === 'string' && propName !== 'value' && propName !== 'innerHTML') {
-                    if (projectionOptions.namespace === NAMESPACE_SVG && propName === 'href') {
-                        domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);
-                    } else {
-                        domNode.setAttribute(propName, propValue);
-                    }
-                } else {
-                    domNode[propName] = propValue;
-                }
-            }
-        }
-    };
-    var updateProperties = function (domNode, previousProperties, properties, projectionOptions) {
-        if (!properties) {
-            return;
-        }
-        var propertiesUpdated = false;
-        var propNames = Object.keys(properties);
-        var propCount = propNames.length;
-        for (var i = 0; i < propCount; i++) {
-            var propName = propNames[i];
-            // assuming that properties will be nullified instead of missing is by design
-            var propValue = properties[propName];
-            var previousValue = previousProperties[propName];
-            if (propName === 'class') {
-                if (previousValue !== propValue) {
-                    throw new Error('"class" property may not be updated. Use the "classes" property for conditional css classes.');
-                }
-            } else if (propName === 'classes') {
-                var classList = domNode.classList;
-                var classNames = Object.keys(propValue);
-                var classNameCount = classNames.length;
-                for (var j = 0; j < classNameCount; j++) {
-                    var className = classNames[j];
-                    var on = !!propValue[className];
-                    var previousOn = !!previousValue[className];
-                    if (on === previousOn) {
-                        continue;
-                    }
-                    propertiesUpdated = true;
-                    if (on) {
-                        classList.add(className);
-                    } else {
-                        classList.remove(className);
-                    }
-                }
-            } else if (propName === 'styles') {
-                var styleNames = Object.keys(propValue);
-                var styleCount = styleNames.length;
-                for (var j = 0; j < styleCount; j++) {
-                    var styleName = styleNames[j];
-                    var newStyleValue = propValue[styleName];
-                    var oldStyleValue = previousValue[styleName];
-                    if (newStyleValue === oldStyleValue) {
-                        continue;
-                    }
-                    propertiesUpdated = true;
-                    if (newStyleValue) {
-                        checkStyleValue(newStyleValue);
-                        projectionOptions.styleApplyer(domNode, styleName, newStyleValue);
-                    } else {
-                        projectionOptions.styleApplyer(domNode, styleName, '');
-                    }
-                }
-            } else {
-                if (!propValue && typeof previousValue === 'string') {
-                    propValue = '';
-                }
-                if (propName === 'value') {
-                    if (domNode[propName] !== propValue && domNode['oninput-value'] !== propValue) {
-                        domNode[propName] = propValue;
-                        // Reset the value, even if the virtual DOM did not change
-                        domNode['oninput-value'] = undefined;
-                    }
-                    // else do not update the domNode, otherwise the cursor position would be changed
-                    if (propValue !== previousValue) {
-                        propertiesUpdated = true;
-                    }
-                } else if (propValue !== previousValue) {
-                    var type = typeof propValue;
-                    if (type === 'function') {
-                        throw new Error('Functions may not be updated on subsequent renders (property: ' + propName + '). Hint: declare event handler functions outside the render() function.');
-                    }
-                    if (type === 'string' && propName !== 'innerHTML') {
-                        if (projectionOptions.namespace === NAMESPACE_SVG && propName === 'href') {
-                            domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);
-                        } else {
-                            domNode.setAttribute(propName, propValue);
-                        }
-                    } else {
-                        if (domNode[propName] !== propValue) {
-                            domNode[propName] = propValue;
-                        }
-                    }
-                    propertiesUpdated = true;
-                }
-            }
-        }
-        return propertiesUpdated;
-    };
-    var findIndexOfChild = function (children, sameAs, start) {
-        if (sameAs.vnodeSelector !== '') {
-            // Never scan for text-nodes
-            for (var i = start; i < children.length; i++) {
-                if (same(children[i], sameAs)) {
-                    return i;
-                }
-            }
-        }
-        return -1;
-    };
-    var nodeAdded = function (vNode, transitions) {
-        if (vNode.properties) {
-            var enterAnimation = vNode.properties.enterAnimation;
-            if (enterAnimation) {
-                if (typeof enterAnimation === 'function') {
-                    enterAnimation(vNode.domNode, vNode.properties);
-                } else {
-                    transitions.enter(vNode.domNode, vNode.properties, enterAnimation);
-                }
-            }
-        }
-    };
-    var nodeToRemove = function (vNode, transitions) {
-        var domNode = vNode.domNode;
-        if (vNode.properties) {
-            var exitAnimation = vNode.properties.exitAnimation;
-            if (exitAnimation) {
-                domNode.style.pointerEvents = 'none';
-                var removeDomNode = function () {
-                    if (domNode.parentNode) {
-                        domNode.parentNode.removeChild(domNode);
-                    }
-                };
-                if (typeof exitAnimation === 'function') {
-                    exitAnimation(domNode, removeDomNode, vNode.properties);
-                    return;
-                } else {
-                    transitions.exit(vNode.domNode, vNode.properties, exitAnimation, removeDomNode);
-                    return;
-                }
-            }
-        }
-        if (domNode.parentNode) {
-            domNode.parentNode.removeChild(domNode);
-        }
-    };
-    var checkDistinguishable = function (childNodes, indexToCheck, parentVNode, operation) {
-        var childNode = childNodes[indexToCheck];
-        if (childNode.vnodeSelector === '') {
-            return;    // Text nodes need not be distinguishable
-        }
-        var properties = childNode.properties;
-        var key = properties ? properties.key === undefined ? properties.bind : properties.key : undefined;
-        if (!key) {
-            for (var i = 0; i < childNodes.length; i++) {
-                if (i !== indexToCheck) {
-                    var node = childNodes[i];
-                    if (same(node, childNode)) {
-                        if (operation === 'added') {
-                            throw new Error(parentVNode.vnodeSelector + ' had a ' + childNode.vnodeSelector + ' child ' + 'added, but there is now more than one. You must add unique key properties to make them distinguishable.');
-                        } else {
-                            throw new Error(parentVNode.vnodeSelector + ' had a ' + childNode.vnodeSelector + ' child ' + 'removed, but there were more than one. You must add unique key properties to make them distinguishable.');
-                        }
-                    }
-                }
-            }
-        }
-    };
-    var createDom;
-    var updateDom;
-    var updateChildren = function (vnode, domNode, oldChildren, newChildren, projectionOptions) {
-        if (oldChildren === newChildren) {
-            return false;
-        }
-        oldChildren = oldChildren || emptyArray;
-        newChildren = newChildren || emptyArray;
-        var oldChildrenLength = oldChildren.length;
-        var newChildrenLength = newChildren.length;
-        var transitions = projectionOptions.transitions;
-        var oldIndex = 0;
-        var newIndex = 0;
-        var i;
-        var textUpdated = false;
-        while (newIndex < newChildrenLength) {
-            var oldChild = oldIndex < oldChildrenLength ? oldChildren[oldIndex] : undefined;
-            var newChild = newChildren[newIndex];
-            if (oldChild !== undefined && same(oldChild, newChild)) {
-                textUpdated = updateDom(oldChild, newChild, projectionOptions) || textUpdated;
-                oldIndex++;
-            } else {
-                var findOldIndex = findIndexOfChild(oldChildren, newChild, oldIndex + 1);
-                if (findOldIndex >= 0) {
-                    // Remove preceding missing children
-                    for (i = oldIndex; i < findOldIndex; i++) {
-                        nodeToRemove(oldChildren[i], transitions);
-                        checkDistinguishable(oldChildren, i, vnode, 'removed');
-                    }
-                    textUpdated = updateDom(oldChildren[findOldIndex], newChild, projectionOptions) || textUpdated;
-                    oldIndex = findOldIndex + 1;
-                } else {
-                    // New child
-                    createDom(newChild, domNode, oldIndex < oldChildrenLength ? oldChildren[oldIndex].domNode : undefined, projectionOptions);
-                    nodeAdded(newChild, transitions);
-                    checkDistinguishable(newChildren, newIndex, vnode, 'added');
-                }
-            }
-            newIndex++;
-        }
-        if (oldChildrenLength > oldIndex) {
-            // Remove child fragments
-            for (i = oldIndex; i < oldChildrenLength; i++) {
-                nodeToRemove(oldChildren[i], transitions);
-                checkDistinguishable(oldChildren, i, vnode, 'removed');
-            }
-        }
-        return textUpdated;
-    };
-    var addChildren = function (domNode, children, projectionOptions) {
-        if (!children) {
-            return;
-        }
-        for (var i = 0; i < children.length; i++) {
-            createDom(children[i], domNode, undefined, projectionOptions);
-        }
-    };
-    var initPropertiesAndChildren = function (domNode, vnode, projectionOptions) {
-        addChildren(domNode, vnode.children, projectionOptions);
-        // children before properties, needed for value property of <select>.
-        if (vnode.text) {
-            domNode.textContent = vnode.text;
-        }
-        setProperties(domNode, vnode.properties, projectionOptions);
-        if (vnode.properties && vnode.properties.afterCreate) {
-            vnode.properties.afterCreate(domNode, projectionOptions, vnode.vnodeSelector, vnode.properties, vnode.children);
-        }
-    };
-    createDom = function (vnode, parentNode, insertBefore, projectionOptions) {
-        var domNode, i, c, start = 0, type, found;
-        var vnodeSelector = vnode.vnodeSelector;
-        if (vnodeSelector === '') {
-            domNode = vnode.domNode = document.createTextNode(vnode.text);
-            if (insertBefore !== undefined) {
-                parentNode.insertBefore(domNode, insertBefore);
-            } else {
-                parentNode.appendChild(domNode);
-            }
-        } else {
-            for (i = 0; i <= vnodeSelector.length; ++i) {
-                c = vnodeSelector.charAt(i);
-                if (i === vnodeSelector.length || c === '.' || c === '#') {
-                    type = vnodeSelector.charAt(start - 1);
-                    found = vnodeSelector.slice(start, i);
-                    if (type === '.') {
-                        domNode.classList.add(found);
-                    } else if (type === '#') {
-                        domNode.id = found;
-                    } else {
-                        if (found === 'svg') {
-                            projectionOptions = extend(projectionOptions, { namespace: NAMESPACE_SVG });
-                        }
-                        if (projectionOptions.namespace !== undefined) {
-                            domNode = vnode.domNode = document.createElementNS(projectionOptions.namespace, found);
-                        } else {
-                            domNode = vnode.domNode = document.createElement(found);
-                        }
-                        if (insertBefore !== undefined) {
-                            parentNode.insertBefore(domNode, insertBefore);
-                        } else {
-                            parentNode.appendChild(domNode);
-                        }
-                    }
-                    start = i + 1;
-                }
-            }
-            initPropertiesAndChildren(domNode, vnode, projectionOptions);
-        }
-    };
-    updateDom = function (previous, vnode, projectionOptions) {
-        var domNode = previous.domNode;
-        var textUpdated = false;
-        if (previous === vnode) {
-            return false;    // By contract, VNode objects may not be modified anymore after passing them to maquette
-        }
-        var updated = false;
-        if (vnode.vnodeSelector === '') {
-            if (vnode.text !== previous.text) {
-                var newVNode = document.createTextNode(vnode.text);
-                domNode.parentNode.replaceChild(newVNode, domNode);
-                vnode.domNode = newVNode;
-                textUpdated = true;
-                return textUpdated;
-            }
-        } else {
-            if (vnode.vnodeSelector.lastIndexOf('svg', 0) === 0) {
-                projectionOptions = extend(projectionOptions, { namespace: NAMESPACE_SVG });
-            }
-            if (previous.text !== vnode.text) {
-                updated = true;
-                if (vnode.text === undefined) {
-                    domNode.removeChild(domNode.firstChild);    // the only textnode presumably
-                } else {
-                    domNode.textContent = vnode.text;
-                }
-            }
-            updated = updateChildren(vnode, domNode, previous.children, vnode.children, projectionOptions) || updated;
-            updated = updateProperties(domNode, previous.properties, vnode.properties, projectionOptions) || updated;
-            if (vnode.properties && vnode.properties.afterUpdate) {
-                vnode.properties.afterUpdate(domNode, projectionOptions, vnode.vnodeSelector, vnode.properties, vnode.children);
-            }
-        }
-        if (updated && vnode.properties && vnode.properties.updateAnimation) {
-            vnode.properties.updateAnimation(domNode, vnode.properties, previous.properties);
-        }
-        vnode.domNode = previous.domNode;
-        return textUpdated;
-    };
-    var createProjection = function (vnode, projectionOptions) {
-        return {
-            update: function (updatedVnode) {
-                if (vnode.vnodeSelector !== updatedVnode.vnodeSelector) {
-                    throw new Error('The selector for the root VNode may not be changed. (consider using dom.merge and add one extra level to the virtual DOM)');
-                }
-                updateDom(vnode, updatedVnode, projectionOptions);
-                vnode = updatedVnode;
-            },
-            domNode: vnode.domNode
-        };
-    };
-    ;
-    // The other two parameters are not added here, because the Typescript compiler creates surrogate code for desctructuring 'children'.
-    exports.h = function (selector) {
-        var properties = arguments[1];
-        if (typeof selector !== 'string') {
-            throw new Error();
-        }
-        var childIndex = 1;
-        if (properties && !properties.hasOwnProperty('vnodeSelector') && !Array.isArray(properties) && typeof properties === 'object') {
-            childIndex = 2;
-        } else {
-            // Optional properties argument was omitted
-            properties = undefined;
-        }
-        var text = undefined;
-        var children = undefined;
-        var argsLength = arguments.length;
-        // Recognize a common special case where there is only a single text node
-        if (argsLength === childIndex + 1) {
-            var onlyChild = arguments[childIndex];
-            if (typeof onlyChild === 'string') {
-                text = onlyChild;
-            } else if (onlyChild !== undefined && onlyChild.length === 1 && typeof onlyChild[0] === 'string') {
-                text = onlyChild[0];
-            }
-        }
-        if (text === undefined) {
-            children = [];
-            for (; childIndex < arguments.length; childIndex++) {
-                var child = arguments[childIndex];
-                if (child === null || child === undefined) {
-                    continue;
-                } else if (Array.isArray(child)) {
-                    appendChildren(selector, child, children);
-                } else if (child.hasOwnProperty('vnodeSelector')) {
-                    children.push(child);
-                } else {
-                    children.push(toTextVNode(child));
-                }
-            }
-        }
-        return {
-            vnodeSelector: selector,
-            properties: properties,
-            children: children,
-            text: text === '' ? undefined : text,
-            domNode: null
-        };
-    };
-    /**
- * Contains simple low-level utility functions to manipulate the real DOM.
- */
-    exports.dom = {
-        /**
-     * Creates a real DOM tree from `vnode`. The [[Projection]] object returned will contain the resulting DOM Node in
-     * its [[Projection.domNode|domNode]] property.
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
-     * objects may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the projection.
-     * @returns The [[Projection]] which also contains the DOM Node that was created.
-     */
-        create: function (vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            createDom(vnode, document.createElement('div'), undefined, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        },
-        /**
-     * Appends a new childnode to the DOM which is generated from a [[VNode]].
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param parentNode - The parent node for the new childNode.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
-     * objects may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the [[Projection]].
-     * @returns The [[Projection]] that was created.
-     */
-        append: function (parentNode, vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            createDom(vnode, parentNode, undefined, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        },
-        /**
-     * Inserts a new DOM node which is generated from a [[VNode]].
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param beforeNode - The node that the DOM Node is inserted before.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function.
-     * NOTE: [[VNode]] objects may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the projection, see [[createProjector]].
-     * @returns The [[Projection]] that was created.
-     */
-        insertBefore: function (beforeNode, vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            createDom(vnode, beforeNode.parentNode, beforeNode, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        },
-        /**
-     * Merges a new DOM node which is generated from a [[VNode]] with an existing DOM Node.
-     * This means that the virtual DOM and the real DOM will have one overlapping element.
-     * Therefore the selector for the root [[VNode]] will be ignored, but its properties and children will be applied to the Element provided.
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param domNode - The existing element to adopt as the root of the new virtual DOM. Existing attributes and childnodes are preserved.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]] objects
-     * may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the projection, see [[createProjector]].
-     * @returns The [[Projection]] that was created.
-     */
-        merge: function (element, vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            vnode.domNode = element;
-            initPropertiesAndChildren(element, vnode, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        }
-    };
-    /**
- * Creates a [[CalculationCache]] object, useful for caching [[VNode]] trees.
- * In practice, caching of [[VNode]] trees is not needed, because achieving 60 frames per second is almost never a problem.
- * For more information, see [[CalculationCache]].
- *
- * @param <Result> The type of the value that is cached.
- */
-    exports.createCache = function () {
-        var cachedInputs = undefined;
-        var cachedOutcome = undefined;
-        var result = {
-            invalidate: function () {
-                cachedOutcome = undefined;
-                cachedInputs = undefined;
-            },
-            result: function (inputs, calculation) {
-                if (cachedInputs) {
-                    for (var i = 0; i < inputs.length; i++) {
-                        if (cachedInputs[i] !== inputs[i]) {
-                            cachedOutcome = undefined;
-                        }
-                    }
-                }
-                if (!cachedOutcome) {
-                    cachedOutcome = calculation();
-                    cachedInputs = inputs;
-                }
-                return cachedOutcome;
-            }
-        };
-        return result;
-    };
-    /**
- * Creates a {@link Mapping} instance that keeps an array of result objects synchronized with an array of source objects.
- * See {@link http://maquettejs.org/docs/arrays.html|Working with arrays}.
- *
- * @param <Source>       The type of source items. A database-record for instance.
- * @param <Target>       The type of target items. A [[Component]] for instance.
- * @param getSourceKey   `function(source)` that must return a key to identify each source object. The result must either be a string or a number.
- * @param createResult   `function(source, index)` that must create a new result object from a given source. This function is identical
- *                       to the `callback` argument in `Array.map(callback)`.
- * @param updateResult   `function(source, target, index)` that updates a result to an updated source.
- */
-    exports.createMapping = function (getSourceKey, createResult, updateResult) {
-        var keys = [];
-        var results = [];
-        return {
-            results: results,
-            map: function (newSources) {
-                var newKeys = newSources.map(getSourceKey);
-                var oldTargets = results.slice();
-                var oldIndex = 0;
-                for (var i = 0; i < newSources.length; i++) {
-                    var source = newSources[i];
-                    var sourceKey = newKeys[i];
-                    if (sourceKey === keys[oldIndex]) {
-                        results[i] = oldTargets[oldIndex];
-                        updateResult(source, oldTargets[oldIndex], i);
-                        oldIndex++;
-                    } else {
-                        var found = false;
-                        for (var j = 1; j < keys.length; j++) {
-                            var searchIndex = (oldIndex + j) % keys.length;
-                            if (keys[searchIndex] === sourceKey) {
-                                results[i] = oldTargets[searchIndex];
-                                updateResult(newSources[i], oldTargets[searchIndex], i);
-                                oldIndex = searchIndex + 1;
-                                found = true;
-                                break;
-                            }
-                        }
-                        if (!found) {
-                            results[i] = createResult(source, i);
-                        }
-                    }
-                }
-                results.length = newSources.length;
-                keys = newKeys;
-            }
-        };
-    };
-    /**
- * Creates a [[Projector]] instance using the provided projectionOptions.
- *
- * For more information, see [[Projector]].
- *
- * @param projectionOptions   Options that influence how the DOM is rendered and updated.
- */
-    exports.createProjector = function (projectorOptions) {
-        var projector;
-        var projectionOptions = applyDefaultProjectionOptions(projectorOptions);
-        projectionOptions.eventHandlerInterceptor = function (propertyName, eventHandler, domNode, properties) {
-            return function () {
-                // intercept function calls (event handlers) to do a render afterwards.
-                projector.scheduleRender();
-                return eventHandler.apply(properties.bind || this, arguments);
-            };
-        };
-        var renderCompleted = true;
-        var scheduled;
-        var stopped = false;
-        var projections = [];
-        var renderFunctions = [];
-        // matches the projections array
-        var doRender = function () {
-            scheduled = undefined;
-            if (!renderCompleted) {
-                return;    // The last render threw an error, it should be logged in the browser console.
-            }
-            renderCompleted = false;
-            for (var i = 0; i < projections.length; i++) {
-                var updatedVnode = renderFunctions[i]();
-                projections[i].update(updatedVnode);
-            }
-            renderCompleted = true;
-        };
-        projector = {
-            scheduleRender: function () {
-                if (!scheduled && !stopped) {
-                    scheduled = requestAnimationFrame(doRender);
-                }
-            },
-            stop: function () {
-                if (scheduled) {
-                    cancelAnimationFrame(scheduled);
-                    scheduled = undefined;
-                }
-                stopped = true;
-            },
-            resume: function () {
-                stopped = false;
-                renderCompleted = true;
-                projector.scheduleRender();
-            },
-            append: function (parentNode, renderMaquetteFunction) {
-                projections.push(exports.dom.append(parentNode, renderMaquetteFunction(), projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            insertBefore: function (beforeNode, renderMaquetteFunction) {
-                projections.push(exports.dom.insertBefore(beforeNode, renderMaquetteFunction(), projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            merge: function (domNode, renderMaquetteFunction) {
-                projections.push(exports.dom.merge(domNode, renderMaquetteFunction(), projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            replace: function (domNode, renderMaquetteFunction) {
-                var vnode = renderMaquetteFunction();
-                createDom(vnode, domNode.parentNode, domNode, projectionOptions);
-                domNode.parentNode.removeChild(domNode);
-                projections.push(createProjection(vnode, projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            detach: function (renderMaquetteFunction) {
-                for (var i = 0; i < renderFunctions.length; i++) {
-                    if (renderFunctions[i] === renderMaquetteFunction) {
-                        renderFunctions.splice(i, 1);
-                        return projections.splice(i, 1)[0];
-                    }
-                }
-                throw new Error('renderMaquetteFunction was not found');
-            }
-        };
-        return projector;
-    };
-}));
-
-
-/* ---- Config.coffee ---- */
-
-
-(function() {
-  window.BINARY_EXTENSIONS = ["3dm", "3ds", "3g2", "3gp", "7z", "a", "aac", "adp", "ai", "aif", "aiff", "alz", "ape", "apk", "appimage", "ar", "arj", "asc", "asf", "au", "avi", "bak", "baml", "bh", "bin", "bk", "bmp", "btif", "bz2", "bzip2", "cab", "caf", "cgm", "class", "cmx", "cpio", "cr2", "cur", "dat", "dcm", "deb", "dex", "djvu", "dll", "dmg", "dng", "doc", "docm", "docx", "dot", "dotm", "dra", "DS_Store", "dsk", "dts", "dtshd", "dvb", "dwg", "dxf", "ecelp4800", "ecelp7470", "ecelp9600", "egg", "eol", "eot", "epub", "exe", "f4v", "fbs", "fh", "fla", "flac", "flatpak", "fli", "flv", "fpx", "fst", "fvt", "g3", "gh", "gif", "gpg", "graffle", "gz", "gzip", "h261", "h263", "h264", "icns", "ico", "ief", "img", "ipa", "iso", "jar", "jpeg", "jpg", "jpgv", "jpm", "jxr", "key", "ktx", "lha", "lib", "lvp", "lz", "lzh", "lzma", "lzo", "m3u", "m4a", "m4v", "mar", "mdi", "mht", "mid", "midi", "mj2", "mka", "mkv", "mmr", "mng", "mobi", "mov", "movie", "mp3", "mp4", "mp4a", "mpeg", "mpg", "mpga", "msgpack", "mxu", "nef", "npx", "numbers", "nupkg", "o", "oga", "ogg", "ogv", "otf", "pages", "pbm", "pcx", "pdb", "pdf", "pea", "pgm", "pic", "png", "pnm", "pot", "potm", "potx", "ppa", "ppam", "ppm", "pps", "ppsm", "ppsx", "ppt", "pptm", "pptx", "psd", "pya", "pyc", "pyo", "pyv", "qt", "rar", "ras", "raw", "resources", "rgb", "rip", "rlc", "rmf", "rmvb", "rpm", "rtf", "rz", "s3m", "s7z", "scpt", "sgi", "shar", "sig", "sil", "sketch", "slk", "smv", "snap", "snk", "so", "stl", "sub", "suo", "swf", "tar", "tbz2", "tbz", "tga", "tgz", "thmx", "tif", "tiff", "tlz", "ttc", "ttf", "txz", "udf", "uvh", "uvi", "uvm", "uvp", "uvs", "uvu", "viv", "vob", "war", "wav", "wax", "wbmp", "wdp", "weba", "webm", "webp", "whl", "wim", "wm", "wma", "wmv", "wmx", "woff2", "woff", "wrm", "wvx", "xbm", "xif", "xla", "xlam", "xls", "xlsb", "xlsm", "xlsx", "xlt", "xltm", "xltx", "xm", "xmind", "xpi", "xpm", "xwd", "xz", "z", "zip", "zipx"];
-
-}).call(this);
-
-
-/* ---- FileEditor.coffee ---- */
-
-
-(function() {
-  var FileEditor,
-    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
-    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
-    hasProp = {}.hasOwnProperty;
-
-  FileEditor = (function(superClass) {
-    extend(FileEditor, superClass);
-
-    function FileEditor(inner_path1) {
-      this.inner_path = inner_path1;
-      this.save = bind(this.save, this);
-      this.handleSaveClick = bind(this.handleSaveClick, this);
-      this.handleSidebarButtonClick = bind(this.handleSidebarButtonClick, this);
-      this.foldJson = bind(this.foldJson, this);
-      this.storeCmNode = bind(this.storeCmNode, this);
-      this.isModified = bind(this.isModified, this);
-      this.need_update = true;
-      this.on_loaded = new Promise();
-      this.is_loading = false;
-      this.content = "";
-      this.node_cm = null;
-      this.cm = null;
-      this.error = null;
-      this.is_loaded = false;
-      this.is_modified = false;
-      this.is_saving = false;
-      this.mode = "Loading";
-    }
-
-    FileEditor.prototype.update = function() {
-      var is_required;
-      is_required = Page.url_params.get("edit_mode") !== "new";
-      return Page.cmd("fileGet", {
-        inner_path: this.inner_path,
-        required: is_required
-      }, (function(_this) {
-        return function(res) {
-          if (res != null ? res.error : void 0) {
-            _this.error = res.error;
-            _this.content = res.error;
-            _this.log("Error loading: " + _this.error);
-          } else {
-            if (res) {
-              _this.content = res;
-            } else {
-              _this.content = "";
-              _this.mode = "Create";
-            }
-          }
-          if (!_this.content) {
-            _this.cm.getDoc().clearHistory();
-          }
-          _this.cm.setValue(_this.content);
-          if (!_this.error) {
-            _this.is_loaded = true;
-          }
-          return Page.projector.scheduleRender();
-        };
-      })(this));
-    };
-
-    FileEditor.prototype.isModified = function() {
-      return this.content !== this.cm.getValue();
-    };
-
-    FileEditor.prototype.storeCmNode = function(node) {
-      return this.node_cm = node;
-    };
-
-    FileEditor.prototype.getMode = function(inner_path) {
-      var ext, types;
-      ext = inner_path.split(".").pop();
-      types = {
-        "py": "python",
-        "json": "application/json",
-        "js": "javascript",
-        "coffee": "coffeescript",
-        "html": "htmlmixed",
-        "htm": "htmlmixed",
-        "php": "htmlmixed",
-        "rs": "rust",
-        "css": "css",
-        "md": "markdown",
-        "xml": "xml",
-        "svg": "xml"
-      };
-      return types[ext];
-    };
-
-    FileEditor.prototype.foldJson = function(from, to) {
-      var count, e, endToken, internal, parsed, prevLine, startToken, toParse;
-      this.log("foldJson", from, to);
-      startToken = '{';
-      endToken = '}';
-      prevLine = this.cm.getLine(from.line);
-      if (prevLine.lastIndexOf('[') > prevLine.lastIndexOf('{')) {
-        startToken = '[';
-        endToken = ']';
-      }
-      internal = this.cm.getRange(from, to);
-      toParse = startToken + internal + endToken;
-      try {
-        parsed = JSON.parse(toParse);
-        count = Object.keys(parsed).length;
-      } catch (error) {
-        e = error;
-        null;
-      }
-      if (count) {
-        return "\u21A4" + count + "\u21A6";
-      } else {
-        return "\u2194";
-      }
-    };
-
-    FileEditor.prototype.createCodeMirror = function() {
-      var mode, options;
-      mode = this.getMode(this.inner_path);
-      this.log("Creating CodeMirror", this.inner_path, mode);
-      options = {
-        value: "Loading...",
-        mode: mode,
-        lineNumbers: true,
-        styleActiveLine: true,
-        matchBrackets: true,
-        keyMap: "sublime",
-        theme: "mdn-like",
-        extraKeys: {
-          "Ctrl-Space": "autocomplete"
-        },
-        foldGutter: true,
-        gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"]
-      };
-      if (mode === "application/json") {
-        options.gutters.unshift("CodeMirror-lint-markers");
-        options.lint = true;
-        options.foldOptions = {
-          widget: this.foldJson
-        };
-      }
-      this.cm = CodeMirror(this.node_cm, options);
-      return this.cm.on("changes", (function(_this) {
-        return function(changes) {
-          if (_this.is_loaded && !_this.is_modified) {
-            _this.is_modified = true;
-            return Page.projector.scheduleRender();
-          }
-        };
-      })(this));
-    };
-
-    FileEditor.prototype.loadEditor = function() {
-      var script;
-      if (!this.is_loading) {
-        document.getElementsByTagName("head")[0].insertAdjacentHTML("beforeend", "<link rel=\"stylesheet\" href=\"codemirror/all.css\" />");
-        script = document.createElement('script');
-        script.src = "codemirror/all.js";
-        script.onload = (function(_this) {
-          return function() {
-            _this.createCodeMirror();
-            return _this.on_loaded.resolve();
-          };
-        })(this);
-        document.head.appendChild(script);
-      }
-      return this.on_loaded;
-    };
-
-    FileEditor.prototype.handleSidebarButtonClick = function() {
-      Page.is_sidebar_closed = !Page.is_sidebar_closed;
-      return false;
-    };
-
-    FileEditor.prototype.handleSaveClick = function() {
-      var mark, num_errors;
-      num_errors = ((function() {
-        var i, len, ref, results;
-        ref = Page.file_editor.cm.getAllMarks();
-        results = [];
-        for (i = 0, len = ref.length; i < len; i++) {
-          mark = ref[i];
-          if (mark.className === "CodeMirror-lint-mark-error") {
-            results.push(mark);
-          }
-        }
-        return results;
-      })()).length;
-      if (num_errors > 0) {
-        Page.cmd("wrapperConfirm", ["<b>Warning:</b> The file looks invalid.", "Save anyway"], this.save);
-      } else {
-        this.save();
-      }
-      return false;
-    };
-
-    FileEditor.prototype.save = function() {
-      Page.projector.scheduleRender();
-      this.is_saving = true;
-      return Page.cmd("fileWrite", [this.inner_path, Text.fileEncode(this.cm.getValue())], (function(_this) {
-        return function(res) {
-          _this.is_saving = false;
-          if (res.error) {
-            Page.cmd("wrapperNotification", ["error", "Error saving " + res.error]);
-          } else {
-            _this.is_save_done = true;
-            setTimeout((function() {
-              _this.is_save_done = false;
-              return Page.projector.scheduleRender();
-            }), 2000);
-            _this.content = _this.cm.getValue();
-            _this.is_modified = false;
-            if (_this.mode === "Create") {
-              _this.mode = "Edit";
-            }
-            Page.file_list.need_update = true;
-          }
-          return Page.projector.scheduleRender();
-        };
-      })(this));
-    };
-
-    FileEditor.prototype.render = function() {
-      var ref;
-      if (this.need_update) {
-        this.loadEditor().then((function(_this) {
-          return function() {
-            return _this.update();
-          };
-        })(this));
-        this.need_update = false;
-      }
-      return h("div.editor", {
-        afterCreate: this.storeCmNode,
-        classes: {
-          error: this.error,
-          loaded: this.is_loaded
-        }
-      }, [
-        h("a.sidebar-button", {
-          href: "#Sidebar",
-          onclick: this.handleSidebarButtonClick
-        }, h("span", "\u2039")), h("div.editor-head", [
-          (ref = this.mode) === "Edit" || ref === "Create" ? h("a.save.button", {
-            href: "#Save",
-            classes: {
-              loading: this.is_saving,
-              done: this.is_save_done,
-              disabled: !this.is_modified
-            },
-            onclick: this.handleSaveClick
-          }, this.is_save_done ? "Save: done!" : "Save") : void 0, h("span.title", this.mode, ": ", this.inner_path)
-        ]), this.error ? h("div.error-message", h("h2", "Unable to load the file: " + this.error), h("a", {
-          href: Page.file_list.getHref(this.inner_path)
-        }, "View in browser")) : void 0
-      ]);
-    };
-
-    return FileEditor;
-
-  })(Class);
-
-  window.FileEditor = FileEditor;
-
-}).call(this);
-
-/* ---- FileItemList.coffee ---- */
-
-
-(function() {
-  var FileItemList,
-    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
-    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
-    hasProp = {}.hasOwnProperty;
-
-  FileItemList = (function(superClass) {
-    extend(FileItemList, superClass);
-
-    function FileItemList(inner_path1) {
-      this.inner_path = inner_path1;
-      this.sort = bind(this.sort, this);
-      this.getOptionalInfo = bind(this.getOptionalInfo, this);
-      this.hasPermissionDelete = bind(this.hasPermissionDelete, this);
-      this.isAdded = bind(this.isAdded, this);
-      this.isModified = bind(this.isModified, this);
-      this.getFileType = bind(this.getFileType, this);
-      this.addOptionalFilesToItems = bind(this.addOptionalFilesToItems, this);
-      this.updateOptionalFiles = bind(this.updateOptionalFiles, this);
-      this.updateAddedFiles = bind(this.updateAddedFiles, this);
-      this.updateModifiedFiles = bind(this.updateModifiedFiles, this);
-      this.items = [];
-      this.updating = false;
-      this.files_modified = {};
-      this.dirs_modified = {};
-      this.files_added = {};
-      this.dirs_added = {};
-      this.files_optional = {};
-      this.items_by_name = {};
-    }
-
-    FileItemList.prototype.update = function(cb) {
-      this.updating = true;
-      this.logStart("Updating dirlist");
-      return Page.cmd("dirList", {
-        inner_path: this.inner_path,
-        stats: true
-      }, (function(_this) {
-        return function(res) {
-          var i, len, pattern_ignore, ref, ref1, ref2, ref3, row;
-          if (res.error) {
-            _this.error = res.error;
-          } else {
-            _this.error = null;
-            pattern_ignore = RegExp("^" + ((ref = Page.site_info.content) != null ? ref.ignore : void 0));
-            _this.items.splice(0, _this.items.length);
-            _this.items_by_name = {};
-            for (i = 0, len = res.length; i < len; i++) {
-              row = res[i];
-              row.type = _this.getFileType(row);
-              row.inner_path = _this.inner_path + row.name;
-              if (((ref1 = Page.site_info.content) != null ? ref1.ignore : void 0) && row.inner_path.match(pattern_ignore)) {
-                row.ignored = true;
-              }
-              _this.items.push(row);
-              _this.items_by_name[row.name] = row;
-            }
-            _this.sort();
-          }
-          if ((ref2 = Page.site_info) != null ? (ref3 = ref2.settings) != null ? ref3.own : void 0 : void 0) {
-            _this.updateAddedFiles();
-          }
-          return _this.updateOptionalFiles(function() {
-            _this.updating = false;
-            if (typeof cb === "function") {
-              cb();
-            }
-            _this.logEnd("Updating dirlist", _this.inner_path);
-            Page.projector.scheduleRender();
-            return _this.updateModifiedFiles(function() {
-              return Page.projector.scheduleRender();
-            });
-          });
-        };
-      })(this));
-    };
-
-    FileItemList.prototype.updateModifiedFiles = function(cb) {
-      return Page.cmd("siteListModifiedFiles", [], (function(_this) {
-        return function(res) {
-          var dir_inner_path, dir_part, dir_parts, i, inner_path, j, len, len1, ref, ref1;
-          _this.files_modified = {};
-          _this.dirs_modified = {};
-          ref = res.modified_files;
-          for (i = 0, len = ref.length; i < len; i++) {
-            inner_path = ref[i];
-            _this.files_modified[inner_path] = true;
-            dir_inner_path = "";
-            dir_parts = inner_path.split("/");
-            ref1 = dir_parts.slice(0, -1);
-            for (j = 0, len1 = ref1.length; j < len1; j++) {
-              dir_part = ref1[j];
-              if (dir_inner_path) {
-                dir_inner_path += "/" + dir_part;
-              } else {
-                dir_inner_path = dir_part;
-              }
-              _this.dirs_modified[dir_inner_path] = true;
-            }
-          }
-          return typeof cb === "function" ? cb() : void 0;
-        };
-      })(this));
-    };
-
-    FileItemList.prototype.updateAddedFiles = function() {
-      return Page.cmd("fileGet", "content.json", (function(_this) {
-        return function(res) {
-          var content, dirs_content, file, file_name, i, j, len, len1, match, pattern, ref, ref1, results;
-          if (!res) {
-            return false;
-          }
-          content = JSON.parse(res);
-          if (content.files == null) {
-            return false;
-          }
-          _this.files_added = {};
-          ref = _this.items;
-          for (i = 0, len = ref.length; i < len; i++) {
-            file = ref[i];
-            if (file.name === "content.json" || file.is_dir) {
-              continue;
-            }
-            if (!content.files[_this.inner_path + file.name]) {
-              _this.files_added[_this.inner_path + file.name] = true;
-            }
-          }
-          _this.dirs_added = {};
-          dirs_content = {};
-          for (file_name in Object.assign({}, content.files, content.files_optional)) {
-            if (!file_name.startsWith(_this.inner_path)) {
-              continue;
-            }
-            pattern = new RegExp(_this.inner_path + "(.*?)/");
-            match = file_name.match(pattern);
-            if (!match) {
-              continue;
-            }
-            dirs_content[match[1]] = true;
-          }
-          ref1 = _this.items;
-          results = [];
-          for (j = 0, len1 = ref1.length; j < len1; j++) {
-            file = ref1[j];
-            if (!file.is_dir) {
-              continue;
-            }
-            if (!dirs_content[file.name]) {
-              results.push(_this.dirs_added[_this.inner_path + file.name] = true);
-            } else {
-              results.push(void 0);
-            }
-          }
-          return results;
-        };
-      })(this));
-    };
-
-    FileItemList.prototype.updateOptionalFiles = function(cb) {
-      return Page.cmd("optionalFileList", {
-        filter: ""
-      }, (function(_this) {
-        return function(res) {
-          var i, len, optional_file;
-          _this.files_optional = {};
-          for (i = 0, len = res.length; i < len; i++) {
-            optional_file = res[i];
-            _this.files_optional[optional_file.inner_path] = optional_file;
-          }
-          _this.addOptionalFilesToItems();
-          return typeof cb === "function" ? cb() : void 0;
-        };
-      })(this));
-    };
-
-    FileItemList.prototype.addOptionalFilesToItems = function() {
-      var dir_name, file_name, inner_path, is_added, optional_file, ref, ref1, row;
-      is_added = false;
-      ref = this.files_optional;
-      for (inner_path in ref) {
-        optional_file = ref[inner_path];
-        if (optional_file.inner_path.startsWith(this.inner_path)) {
-          if (this.getDirectory(optional_file.inner_path) === this.inner_path) {
-            file_name = this.getFileName(optional_file.inner_path);
-            if (!this.items_by_name[file_name]) {
-              row = {
-                "name": file_name,
-                "type": "file",
-                "optional_empty": true,
-                "size": optional_file.size,
-                "is_dir": false,
-                "inner_path": optional_file.inner_path
-              };
-              this.items.push(row);
-              this.items_by_name[file_name] = row;
-              is_added = true;
-            }
-          } else {
-            dir_name = (ref1 = optional_file.inner_path.replace(this.inner_path, "").match(/(.*?)\//, "")) != null ? ref1[1] : void 0;
-            if (dir_name && !this.items_by_name[dir_name]) {
-              row = {
-                "name": dir_name,
-                "type": "dir",
-                "optional_empty": true,
-                "size": 0,
-                "is_dir": true,
-                "inner_path": optional_file.inner_path
-              };
-              this.items.push(row);
-              this.items_by_name[dir_name] = row;
-              is_added = true;
-            }
-          }
-        }
-      }
-      if (is_added) {
-        return this.sort();
-      }
-    };
-
-    FileItemList.prototype.getFileType = function(file) {
-      if (file.is_dir) {
-        return "dir";
-      } else {
-        return "unknown";
-      }
-    };
-
-    FileItemList.prototype.getDirectory = function(inner_path) {
-      if (inner_path.indexOf("/") !== -1) {
-        return inner_path.replace(/^(.*\/)(.*?)$/, "$1");
-      } else {
-        return "";
-      }
-    };
-
-    FileItemList.prototype.getFileName = function(inner_path) {
-      return inner_path.replace(/^(.*\/)(.*?)$/, "$2");
-    };
-
-    FileItemList.prototype.isModified = function(inner_path) {
-      return this.files_modified[inner_path] || this.dirs_modified[inner_path];
-    };
-
-    FileItemList.prototype.isAdded = function(inner_path) {
-      return this.files_added[inner_path] || this.dirs_added[inner_path];
-    };
-
-    FileItemList.prototype.hasPermissionDelete = function(file) {
-      var optional_info, ref, ref1, ref2;
-      if ((ref = file.type) === "dir" || ref === "parent") {
-        return false;
-      }
-      if (file.inner_path === "content.json") {
-        return false;
-      }
-      optional_info = this.getOptionalInfo(file.inner_path);
-      if (optional_info && optional_info.downloaded_percent > 0) {
-        return true;
-      } else {
-        return (ref1 = Page.site_info) != null ? (ref2 = ref1.settings) != null ? ref2.own : void 0 : void 0;
-      }
-    };
-
-    FileItemList.prototype.getOptionalInfo = function(inner_path) {
-      return this.files_optional[inner_path];
-    };
-
-    FileItemList.prototype.sort = function() {
-      return this.items.sort(function(a, b) {
-        return (b.is_dir - a.is_dir) || a.name.localeCompare(b.name);
-      });
-    };
-
-    return FileItemList;
-
-  })(Class);
-
-  window.FileItemList = FileItemList;
-
-}).call(this);
-
-/* ---- FileList.coffee ---- */
-
-
-(function() {
-  var FileList,
-    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
-    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
-    hasProp = {}.hasOwnProperty,
-    indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
-
-  FileList = (function(superClass) {
-    extend(FileList, superClass);
-
-    function FileList(site, inner_path1, is_owner) {
-      this.site = site;
-      this.inner_path = inner_path1;
-      this.is_owner = is_owner != null ? is_owner : false;
-      this.render = bind(this.render, this);
-      this.renderFoot = bind(this.renderFoot, this);
-      this.renderItems = bind(this.renderItems, this);
-      this.renderItem = bind(this.renderItem, this);
-      this.renderItemCheckbox = bind(this.renderItemCheckbox, this);
-      this.renderHead = bind(this.renderHead, this);
-      this.renderSelectbar = bind(this.renderSelectbar, this);
-      this.handleSelectbarRemoveOptional = bind(this.handleSelectbarRemoveOptional, this);
-      this.handleSelectbarDelete = bind(this.handleSelectbarDelete, this);
-      this.handleSelectbarCancel = bind(this.handleSelectbarCancel, this);
-      this.handleRowMouseenter = bind(this.handleRowMouseenter, this);
-      this.handleSelectMousedown = bind(this.handleSelectMousedown, this);
-      this.handleSelectEnd = bind(this.handleSelectEnd, this);
-      this.handleSelectClick = bind(this.handleSelectClick, this);
-      this.handleNewDirectoryClick = bind(this.handleNewDirectoryClick, this);
-      this.handleNewFileClick = bind(this.handleNewFileClick, this);
-      this.handleMenuCreateClick = bind(this.handleMenuCreateClick, this);
-      this.checkSelectedItems = bind(this.checkSelectedItems, this);
-      this.getEditHref = bind(this.getEditHref, this);
-      this.getListHref = bind(this.getListHref, this);
-      this.getHref = bind(this.getHref, this);
-      this.update = bind(this.update, this);
-      this.need_update = true;
-      this.error = null;
-      this.url_root = "/list/" + this.site + "/";
-      if (this.inner_path) {
-        this.inner_path += "/";
-        this.url_root += this.inner_path;
-      }
-      this.log("inited", this.url_root);
-      this.item_list = new FileItemList(this.inner_path);
-      this.item_list.items = this.item_list.items;
-      this.menu_create = new Menu();
-      this.select_action = null;
-      this.selected = {};
-      this.selected_items_num = 0;
-      this.selected_items_size = 0;
-      this.selected_optional_empty_num = 0;
-    }
-
-    FileList.prototype.isSelectedAll = function() {
-      return false;
-    };
-
-    FileList.prototype.update = function() {
-      return this.item_list.update((function(_this) {
-        return function() {
-          return document.body.classList.add("loaded");
-        };
-      })(this));
-    };
-
-    FileList.prototype.getHref = function(inner_path) {
-      return "/" + this.site + "/" + inner_path;
-    };
-
-    FileList.prototype.getListHref = function(inner_path) {
-      return "/list/" + this.site + "/" + inner_path;
-    };
-
-    FileList.prototype.getEditHref = function(inner_path, mode) {
-      var href;
-      if (mode == null) {
-        mode = null;
-      }
-      href = this.url_root + "?file=" + inner_path;
-      if (mode) {
-        href += "&edit_mode=" + mode;
-      }
-      return href;
-    };
-
-    FileList.prototype.checkSelectedItems = function() {
-      var i, item, len, optional_info, ref, results;
-      this.selected_items_num = 0;
-      this.selected_items_size = 0;
-      this.selected_optional_empty_num = 0;
-      ref = this.item_list.items;
-      results = [];
-      for (i = 0, len = ref.length; i < len; i++) {
-        item = ref[i];
-        if (this.selected[item.inner_path]) {
-          this.selected_items_num += 1;
-          this.selected_items_size += item.size;
-          optional_info = this.item_list.getOptionalInfo(item.inner_path);
-          if (optional_info && !optional_info.downloaded_percent > 0) {
-            results.push(this.selected_optional_empty_num += 1);
-          } else {
-            results.push(void 0);
-          }
-        } else {
-          results.push(void 0);
-        }
-      }
-      return results;
-    };
-
-    FileList.prototype.handleMenuCreateClick = function() {
-      this.menu_create.items = [];
-      this.menu_create.items.push(["File", this.handleNewFileClick]);
-      this.menu_create.items.push(["Directory", this.handleNewDirectoryClick]);
-      this.menu_create.toggle();
-      return false;
-    };
-
-    FileList.prototype.handleNewFileClick = function() {
-      Page.cmd("wrapperPrompt", "New file name:", (function(_this) {
-        return function(file_name) {
-          return window.top.location.href = _this.getEditHref(_this.inner_path + file_name, "new");
-        };
-      })(this));
-      return false;
-    };
-
-    FileList.prototype.handleNewDirectoryClick = function() {
-      Page.cmd("wrapperPrompt", "New directory name:", (function(_this) {
-        return function(res) {
-          return alert("directory name " + res);
-        };
-      })(this));
-      return false;
-    };
-
-    FileList.prototype.handleSelectClick = function(e) {
-      return false;
-    };
-
-    FileList.prototype.handleSelectEnd = function(e) {
-      document.body.removeEventListener('mouseup', this.handleSelectEnd);
-      return this.select_action = null;
-    };
-
-    FileList.prototype.handleSelectMousedown = function(e) {
-      var inner_path;
-      inner_path = e.currentTarget.attributes.inner_path.value;
-      if (this.selected[inner_path]) {
-        delete this.selected[inner_path];
-        this.select_action = "deselect";
-      } else {
-        this.selected[inner_path] = true;
-        this.select_action = "select";
-      }
-      this.checkSelectedItems();
-      document.body.addEventListener('mouseup', this.handleSelectEnd);
-      e.stopPropagation();
-      Page.projector.scheduleRender();
-      return false;
-    };
-
-    FileList.prototype.handleRowMouseenter = function(e) {
-      var inner_path;
-      if (e.buttons && this.select_action) {
-        inner_path = e.target.attributes.inner_path.value;
-        if (this.select_action === "select") {
-          this.selected[inner_path] = true;
-        } else {
-          delete this.selected[inner_path];
-        }
-        this.checkSelectedItems();
-        Page.projector.scheduleRender();
-      }
-      return false;
-    };
-
-    FileList.prototype.handleSelectbarCancel = function() {
-      this.selected = {};
-      this.checkSelectedItems();
-      Page.projector.scheduleRender();
-      return false;
-    };
-
-    FileList.prototype.handleSelectbarDelete = function(e, remove_optional) {
-      var inner_path, optional_info;
-      if (remove_optional == null) {
-        remove_optional = false;
-      }
-      for (inner_path in this.selected) {
-        optional_info = this.item_list.getOptionalInfo(inner_path);
-        delete this.selected[inner_path];
-        if (optional_info && !remove_optional) {
-          Page.cmd("optionalFileDelete", inner_path);
-        } else {
-          Page.cmd("fileDelete", inner_path);
-        }
-      }
-      this.need_update = true;
-      Page.projector.scheduleRender();
-      this.checkSelectedItems();
-      return false;
-    };
-
-    FileList.prototype.handleSelectbarRemoveOptional = function(e) {
-      return this.handleSelectbarDelete(e, true);
-    };
-
-    FileList.prototype.renderSelectbar = function() {
-      return h("div.selectbar", {
-        classes: {
-          visible: this.selected_items_num > 0
-        }
-      }, [
-        "Selected:", h("span.info", [h("span.num", this.selected_items_num + " files"), h("span.size", "(" + (Text.formatSize(this.selected_items_size)) + ")")]), h("div.actions", [
-          this.selected_optional_empty_num > 0 ? h("a.action.delete.remove_optional", {
-            href: "#",
-            onclick: this.handleSelectbarRemoveOptional
-          }, "Delete and remove optional") : h("a.action.delete", {
-            href: "#",
-            onclick: this.handleSelectbarDelete
-          }, "Delete")
-        ]), h("a.cancel.link", {
-          href: "#",
-          onclick: this.handleSelectbarCancel
-        }, "Cancel")
-      ]);
-    };
-
-    FileList.prototype.renderHead = function() {
-      var i, inner_path_parent, len, parent_dir, parent_links, ref;
-      parent_links = [];
-      inner_path_parent = "";
-      ref = this.inner_path.split("/");
-      for (i = 0, len = ref.length; i < len; i++) {
-        parent_dir = ref[i];
-        if (!parent_dir) {
-          continue;
-        }
-        if (inner_path_parent) {
-          inner_path_parent += "/";
-        }
-        inner_path_parent += "" + parent_dir;
-        parent_links.push([
-          " / ", h("a", {
-            href: this.getListHref(inner_path_parent)
-          }, parent_dir)
-        ]);
-      }
-      return h("div.tr.thead", h("div.td.full", h("a", {
-        href: this.getListHref("")
-      }, "root"), parent_links));
-    };
-
-    FileList.prototype.renderItemCheckbox = function(item) {
-      if (!this.item_list.hasPermissionDelete(item)) {
-        return [" "];
-      }
-      return h("a.checkbox-outer", {
-        href: "#Select",
-        onmousedown: this.handleSelectMousedown,
-        onclick: this.handleSelectClick,
-        inner_path: item.inner_path
-      }, h("span.checkbox"));
-    };
-
-    FileList.prototype.renderItem = function(item) {
-      var classes, downloaded_percent, ext, href, href_edit, inner_path, is_added, is_dir, is_editable, is_editing, is_modified, obj, optional_info, ref, ref1, style, title;
-      if (item.type === "parent") {
-        href = this.url_root.replace(/^(.*)\/.{2,255}?$/, "$1/");
-      } else if (item.type === "dir") {
-        href = this.url_root + item.name;
-      } else {
-        href = this.url_root.replace(/^\/list\//, "/") + item.name;
-      }
-      inner_path = this.inner_path + item.name;
-      href_edit = this.getEditHref(inner_path);
-      is_dir = (ref = item.type) === "dir" || ref === "parent";
-      ext = item.name.split(".").pop();
-      is_editing = inner_path === ((ref1 = Page.file_editor) != null ? ref1.inner_path : void 0);
-      is_editable = !is_dir && item.size < 1024 * 1024 && indexOf.call(window.BINARY_EXTENSIONS, ext) < 0;
-      is_modified = this.item_list.isModified(inner_path);
-      is_added = this.item_list.isAdded(inner_path);
-      optional_info = this.item_list.getOptionalInfo(inner_path);
-      style = "";
-      title = "";
-      if (optional_info) {
-        downloaded_percent = optional_info.downloaded_percent;
-        if (!downloaded_percent) {
-          downloaded_percent = 0;
-        }
-        style += "background: linear-gradient(90deg, #fff6dd, " + downloaded_percent + "%, white, " + downloaded_percent + "%, white);";
-        is_added = false;
-      }
-      if (item.ignored) {
-        is_added = false;
-      }
-      if (is_modified) {
-        title += " (modified)";
-      }
-      if (is_added) {
-        title += " (new)";
-      }
-      if (optional_info || item.optional_empty) {
-        title += " (optional)";
-      }
-      if (item.ignored) {
-        title += " (ignored from content.json)";
-      }
-      classes = (
-        obj = {},
-        obj["type-" + item.type] = true,
-        obj.editing = is_editing,
-        obj.nobuttons = !is_editable,
-        obj.selected = this.selected[inner_path],
-        obj.modified = is_modified,
-        obj.added = is_added,
-        obj.ignored = item.ignored,
-        obj.optional = optional_info,
-        obj.optional_empty = item.optional_empty,
-        obj
-      );
-      return h("div.tr", {
-        key: item.name,
-        classes: classes,
-        style: style,
-        onmouseenter: this.handleRowMouseenter,
-        inner_path: inner_path
-      }, [
-        h("div.td.pre", {
-          title: title
-        }, this.renderItemCheckbox(item)), h("div.td.name", h("a.link", {
-          href: href
-        }, item.name)), h("div.td.buttons", is_editable ? h("a.edit", {
-          href: href_edit
-        }, Page.site_info.settings.own ? "Edit" : "View") : void 0), h("div.td.size", is_dir ? "[DIR]" : Text.formatSize(item.size))
-      ]);
-    };
-
-    FileList.prototype.renderItems = function() {
-      return [
-        this.item_list.error && !this.item_list.items.length && !this.item_list.updating ? [
-          h("div.tr", {
-            key: "error"
-          }, h("div.td.full.error", this.item_list.error))
-        ] : void 0, this.inner_path ? this.renderItem({
-          "name": "..",
-          type: "parent",
-          size: 0
-        }) : void 0, this.item_list.items.map(this.renderItem)
-      ];
-    };
-
-    FileList.prototype.renderFoot = function() {
-      var dirs, file, files, foot_text, item, ref, ref1, ref2, ref3, total_size;
-      files = (function() {
-        var i, len, ref, ref1, results;
-        ref = this.item_list.items;
-        results = [];
-        for (i = 0, len = ref.length; i < len; i++) {
-          item = ref[i];
-          if ((ref1 = item.type) !== "parent" && ref1 !== "dir") {
-            results.push(item);
-          }
-        }
-        return results;
-      }).call(this);
-      dirs = (function() {
-        var i, len, ref, results;
-        ref = this.item_list.items;
-        results = [];
-        for (i = 0, len = ref.length; i < len; i++) {
-          item = ref[i];
-          if (item.type === "dir") {
-            results.push(item);
-          }
-        }
-        return results;
-      }).call(this);
-      if (files.length) {
-        total_size = ((function() {
-          var i, len, results;
-          results = [];
-          for (i = 0, len = files.length; i < len; i++) {
-            file = files[i];
-            results.push(item.size);
-          }
-          return results;
-        })()).reduce(function(a, b) {
-          return a + b;
-        });
-      } else {
-        total_size = 0;
-      }
-      foot_text = "Total: ";
-      foot_text += dirs.length + " dir, " + files.length + " file in " + (Text.formatSize(total_size));
-      return [
-        dirs.length || files.length || ((ref = Page.site_info) != null ? (ref1 = ref.settings) != null ? ref1.own : void 0 : void 0) ? h("div.tr.foot-info.foot", h("div.td.full", [
-          this.item_list.updating ? "Updating file list..." : dirs.length || files.length ? foot_text : void 0, ((ref2 = Page.site_info) != null ? (ref3 = ref2.settings) != null ? ref3.own : void 0 : void 0) ? h("div.create", [
-            h("a.link", {
-              href: "#Create+new+file",
-              onclick: this.handleNewFileClick
-            }, "+ New"), this.menu_create.render()
-          ]) : void 0
-        ])) : void 0
-      ];
-    };
-
-    FileList.prototype.render = function() {
-      if (this.need_update) {
-        this.update();
-        this.need_update = false;
-        if (!this.item_list.items) {
-          return [];
-        }
-      }
-      return h("div.files", [this.renderSelectbar(), this.renderHead(), h("div.tbody", this.renderItems()), this.renderFoot()]);
-    };
-
-    return FileList;
-
-  })(Class);
-
-  window.FileList = FileList;
-
-}).call(this);
-
-/* ---- UiFileManager.coffee ---- */
-
-
-(function() {
-  var UiFileManager,
-    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
-    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
-    hasProp = {}.hasOwnProperty;
-
-  window.h = maquette.h;
-
-  UiFileManager = (function(superClass) {
-    extend(UiFileManager, superClass);
-
-    function UiFileManager() {
-      this.render = bind(this.render, this);
-      this.createProjector = bind(this.createProjector, this);
-      this.onRequest = bind(this.onRequest, this);
-      this.checkBodyWidth = bind(this.checkBodyWidth, this);
-      return UiFileManager.__super__.constructor.apply(this, arguments);
-    }
-
-    UiFileManager.prototype.init = function() {
-      this.url_params = new URLSearchParams(window.location.search);
-      this.list_site = this.url_params.get("site");
-      this.list_address = this.url_params.get("address");
-      this.list_inner_path = this.url_params.get("inner_path");
-      this.editor_inner_path = this.url_params.get("file");
-      this.file_list = new FileList(this.list_site, this.list_inner_path);
-      this.site_info = null;
-      this.server_info = null;
-      this.is_sidebar_closed = false;
-      if (this.editor_inner_path) {
-        this.file_editor = new FileEditor(this.editor_inner_path);
-      }
-      window.onbeforeunload = (function(_this) {
-        return function() {
-          var ref;
-          if ((ref = _this.file_editor) != null ? ref.isModified() : void 0) {
-            return true;
-          } else {
-            return null;
-          }
-        };
-      })(this);
-      window.onresize = (function(_this) {
-        return function() {
-          return _this.checkBodyWidth();
-        };
-      })(this);
-      this.checkBodyWidth();
-      this.cmd("wrapperSetViewport", "width=device-width, initial-scale=0.8");
-      this.cmd("serverInfo", {}, (function(_this) {
-        return function(server_info) {
-          return _this.server_info = server_info;
-        };
-      })(this));
-      return this.cmd("siteInfo", {}, (function(_this) {
-        return function(site_info) {
-          _this.cmd("wrapperSetTitle", "List: /" + _this.list_inner_path + " - " + site_info.content.title + " - ZeroNet");
-          _this.site_info = site_info;
-          if (_this.file_editor) {
-            _this.file_editor.on_loaded.then(function() {
-              _this.file_editor.cm.setOption("readOnly", !site_info.settings.own);
-              return _this.file_editor.mode = site_info.settings.own ? "Edit" : "View";
-            });
-          }
-          return _this.projector.scheduleRender();
-        };
-      })(this));
-    };
-
-    UiFileManager.prototype.checkBodyWidth = function() {
-      var ref, ref1;
-      if (!this.file_editor) {
-        return false;
-      }
-      if (document.body.offsetWidth < 960 && !this.is_sidebar_closed) {
-        this.is_sidebar_closed = true;
-        return (ref = this.projector) != null ? ref.scheduleRender() : void 0;
-      } else if (document.body.offsetWidth > 960 && this.is_sidebar_closed) {
-        this.is_sidebar_closed = false;
-        return (ref1 = this.projector) != null ? ref1.scheduleRender() : void 0;
-      }
-    };
-
-    UiFileManager.prototype.onRequest = function(cmd, message) {
-      if (cmd === "setSiteInfo") {
-        this.site_info = message;
-        RateLimitCb(1000, (function(_this) {
-          return function(cb_done) {
-            return _this.file_list.update(cb_done);
-          };
-        })(this));
-        return this.projector.scheduleRender();
-      } else if (cmd === "setServerInfo") {
-        this.server_info = message;
-        return this.projector.scheduleRender();
-      } else {
-        return this.log("Unknown incoming message:", cmd);
-      }
-    };
-
-    UiFileManager.prototype.createProjector = function() {
-      this.projector = maquette.createProjector();
-      return this.projector.replace($("#content"), this.render);
-    };
-
-    UiFileManager.prototype.render = function() {
-      return h("div.content#content", [
-        h("div.manager", {
-          classes: {
-            editing: this.file_editor,
-            sidebar_closed: this.is_sidebar_closed
-          }
-        }, [this.file_list.render(), this.file_editor ? this.file_editor.render() : void 0])
-      ]);
-    };
-
-    return UiFileManager;
-
-  })(ZeroFrame);
-
-  window.Page = new UiFileManager();
-
-  window.Page.createProjector();
-
-}).call(this);
\ No newline at end of file
diff --git a/plugins/UiFileManager/media/js/lib/Animation.coffee b/plugins/UiFileManager/media/js/lib/Animation.coffee
deleted file mode 100644
index 271b88c1..00000000
--- a/plugins/UiFileManager/media/js/lib/Animation.coffee
+++ /dev/null
@@ -1,138 +0,0 @@
-class Animation
-	slideDown: (elem, props) ->
-		if elem.offsetTop > 2000
-			return
-
-		h = elem.offsetHeight
-		cstyle = window.getComputedStyle(elem)
-		margin_top = cstyle.marginTop
-		margin_bottom = cstyle.marginBottom
-		padding_top = cstyle.paddingTop
-		padding_bottom = cstyle.paddingBottom
-		transition = cstyle.transition
-
-		elem.style.boxSizing = "border-box"
-		elem.style.overflow = "hidden"
-		elem.style.transform = "scale(0.6)"
-		elem.style.opacity = "0"
-		elem.style.height = "0px"
-		elem.style.marginTop = "0px"
-		elem.style.marginBottom = "0px"
-		elem.style.paddingTop = "0px"
-		elem.style.paddingBottom = "0px"
-		elem.style.transition = "none"
-
-		setTimeout (->
-			elem.className += " animate-inout"
-			elem.style.height = h+"px"
-			elem.style.transform = "scale(1)"
-			elem.style.opacity = "1"
-			elem.style.marginTop = margin_top
-			elem.style.marginBottom = margin_bottom
-			elem.style.paddingTop = padding_top
-			elem.style.paddingBottom = padding_bottom
-		), 1
-
-		elem.addEventListener "transitionend", ->
-			elem.classList.remove("animate-inout")
-			elem.style.transition = elem.style.transform = elem.style.opacity = elem.style.height = null
-			elem.style.boxSizing = elem.style.marginTop = elem.style.marginBottom = null
-			elem.style.paddingTop = elem.style.paddingBottom = elem.style.overflow = null
-			elem.removeEventListener "transitionend", arguments.callee, false
-
-
-	slideUp: (elem, remove_func, props) ->
-		if elem.offsetTop > 1000
-			return remove_func()
-
-		elem.className += " animate-back"
-		elem.style.boxSizing = "border-box"
-		elem.style.height = elem.offsetHeight+"px"
-		elem.style.overflow = "hidden"
-		elem.style.transform = "scale(1)"
-		elem.style.opacity = "1"
-		elem.style.pointerEvents = "none"
-		setTimeout (->
-			elem.style.height = "0px"
-			elem.style.marginTop = "0px"
-			elem.style.marginBottom = "0px"
-			elem.style.paddingTop = "0px"
-			elem.style.paddingBottom = "0px"
-			elem.style.transform = "scale(0.8)"
-			elem.style.borderTopWidth = "0px"
-			elem.style.borderBottomWidth = "0px"
-			elem.style.opacity = "0"
-		), 1
-		elem.addEventListener "transitionend", (e) ->
-			if e.propertyName == "opacity" or e.elapsedTime >= 0.6
-				elem.removeEventListener "transitionend", arguments.callee, false
-				remove_func()
-
-
-	slideUpInout: (elem, remove_func, props) ->
-		elem.className += " animate-inout"
-		elem.style.boxSizing = "border-box"
-		elem.style.height = elem.offsetHeight+"px"
-		elem.style.overflow = "hidden"
-		elem.style.transform = "scale(1)"
-		elem.style.opacity = "1"
-		elem.style.pointerEvents = "none"
-		setTimeout (->
-			elem.style.height = "0px"
-			elem.style.marginTop = "0px"
-			elem.style.marginBottom = "0px"
-			elem.style.paddingTop = "0px"
-			elem.style.paddingBottom = "0px"
-			elem.style.transform = "scale(0.8)"
-			elem.style.borderTopWidth = "0px"
-			elem.style.borderBottomWidth = "0px"
-			elem.style.opacity = "0"
-		), 1
-		elem.addEventListener "transitionend", (e) ->
-			if e.propertyName == "opacity" or e.elapsedTime >= 0.6
-				elem.removeEventListener "transitionend", arguments.callee, false
-				remove_func()
-
-
-	showRight: (elem, props) ->
-		elem.className += " animate"
-		elem.style.opacity = 0
-		elem.style.transform = "TranslateX(-20px) Scale(1.01)"
-		setTimeout (->
-			elem.style.opacity = 1
-			elem.style.transform = "TranslateX(0px) Scale(1)"
-		), 1
-		elem.addEventListener "transitionend", ->
-			elem.classList.remove("animate")
-			elem.style.transform = elem.style.opacity = null
-
-
-	show: (elem, props) ->
-		delay = arguments[arguments.length-2]?.delay*1000 or 1
-		elem.style.opacity = 0
-		setTimeout (->
-			elem.className += " animate"
-		), 1
-		setTimeout (->
-			elem.style.opacity = 1
-		), delay
-		elem.addEventListener "transitionend", ->
-			elem.classList.remove("animate")
-			elem.style.opacity = null
-			elem.removeEventListener "transitionend", arguments.callee, false
-
-	hide: (elem, remove_func, props) ->
-		delay = arguments[arguments.length-2]?.delay*1000 or 1
-		elem.className += " animate"
-		setTimeout (->
-			elem.style.opacity = 0
-		), delay
-		elem.addEventListener "transitionend", (e) ->
-			if e.propertyName == "opacity"
-				remove_func()
-
-	addVisibleClass: (elem, props) ->
-		setTimeout ->
-			elem.classList.add("visible")
-
-window.Animation = new Animation()
\ No newline at end of file
diff --git a/plugins/UiFileManager/media/js/lib/Class.coffee b/plugins/UiFileManager/media/js/lib/Class.coffee
deleted file mode 100644
index d62ab25c..00000000
--- a/plugins/UiFileManager/media/js/lib/Class.coffee
+++ /dev/null
@@ -1,23 +0,0 @@
-class Class
-	trace: true
-
-	log: (args...) ->
-		return unless @trace
-		return if typeof console is 'undefined'
-		args.unshift("[#{@.constructor.name}]")
-		console.log(args...)
-		@
-		
-	logStart: (name, args...) ->
-		return unless @trace
-		@logtimers or= {}
-		@logtimers[name] = +(new Date)
-		@log "#{name}", args..., "(started)" if args.length > 0
-		@
-		
-	logEnd: (name, args...) ->
-		ms = +(new Date)-@logtimers[name]
-		@log "#{name}", args..., "(Done in #{ms}ms)"
-		@ 
-
-window.Class = Class
\ No newline at end of file
diff --git a/plugins/UiFileManager/media/js/lib/Dollar.coffee b/plugins/UiFileManager/media/js/lib/Dollar.coffee
deleted file mode 100644
index 7f19f551..00000000
--- a/plugins/UiFileManager/media/js/lib/Dollar.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-window.$ = (selector) ->
-	if selector.startsWith("#")
-		return document.getElementById(selector.replace("#", ""))
diff --git a/plugins/UiFileManager/media/js/lib/ItemList.coffee b/plugins/UiFileManager/media/js/lib/ItemList.coffee
deleted file mode 100644
index 902e76cd..00000000
--- a/plugins/UiFileManager/media/js/lib/ItemList.coffee
+++ /dev/null
@@ -1,26 +0,0 @@
-class ItemList
-	constructor: (@item_class, @key) ->
-		@items = []
-		@items_bykey = {}
-
-	sync: (rows, item_class, key) ->
-		@items.splice(0, @items.length)  # Empty items
-		for row in rows
-			current_obj = @items_bykey[row[@key]]
-			if current_obj
-				current_obj.row = row
-				@items.push current_obj
-			else
-				item = new @item_class(row, @)
-				@items_bykey[row[@key]] = item
-				@items.push item
-
-	deleteItem: (item) ->
-		index = @items.indexOf(item)
-		if index > -1
-			@items.splice(index, 1)
-		else
-			console.log "Can't delete item", item
-		delete @items_bykey[item.row[@key]]
-
-window.ItemList = ItemList
\ No newline at end of file
diff --git a/plugins/UiFileManager/media/js/lib/Menu.coffee b/plugins/UiFileManager/media/js/lib/Menu.coffee
deleted file mode 100644
index ed5fbd54..00000000
--- a/plugins/UiFileManager/media/js/lib/Menu.coffee
+++ /dev/null
@@ -1,110 +0,0 @@
-class Menu
-	constructor: ->
-		@visible = false
-		@items = []
-		@node = null
-		@height = 0
-		@direction = "bottom"
-
-	show: =>
-		window.visible_menu?.hide()
-		@visible = true
-		window.visible_menu = @
-		@direction = @getDirection()
-
-	hide: =>
-		@visible = false
-
-	toggle: =>
-		if @visible
-			@hide()
-		else
-			@show()
-		Page.projector.scheduleRender()
-
-
-	addItem: (title, cb, selected=false) ->
-		@items.push([title, cb, selected])
-
-
-	storeNode: (node) =>
-		@node = node
-		# Animate visible
-		if @visible
-			node.className = node.className.replace("visible", "")
-			setTimeout (=>
-				node.className += " visible"
-				node.attributes.style.value = @getStyle()
-			), 20
-			node.style.maxHeight = "none"
-			@height = node.offsetHeight
-			node.style.maxHeight = "0px"
-			@direction = @getDirection()
-
-	getDirection: =>
-		if @node and @node.parentNode.getBoundingClientRect().top + @height + 60 > document.body.clientHeight and @node.parentNode.getBoundingClientRect().top - @height > 0
-			return "top"
-		else
-			return "bottom"
-
-	handleClick: (e) =>
-		keep_menu = false
-		for item in @items
-			[title, cb, selected] = item
-			if title == e.currentTarget.textContent or e.currentTarget["data-title"] == title
-				keep_menu = cb?(item)
-				break
-		if keep_menu != true and cb != null
-			@hide()
-		return false
-
-	renderItem: (item) =>
-		[title, cb, selected] = item
-		if typeof(selected) == "function"
-			selected = selected()
-
-		if title == "---"
-			return h("div.menu-item-separator", {key: Time.timestamp()})
-		else
-			if cb == null
-				href = undefined
-				onclick = @handleClick
-			else if typeof(cb) == "string"  # Url
-				href = cb
-				onclick = true
-			else  # Callback
-				href = "#"+title
-				onclick = @handleClick
-			classes = {
-				"selected": selected,
-				"noaction": (cb == null)
-			}
-			return h("a.menu-item", {href: href, onclick: onclick, "data-title": title, key: title, classes: classes}, title)
-
-	getStyle: =>
-		if @visible
-			max_height = @height
-		else
-			max_height = 0
-		style = "max-height: #{max_height}px"
-		if @direction == "top"
-			style += ";margin-top: #{0 - @height - 50}px"
-		else
-			style += ";margin-top: 0px"
-		return style
-
-	render: (class_name="") =>
-		if @visible or @node
-			h("div.menu#{class_name}", {classes: {"visible": @visible}, style: @getStyle(), afterCreate: @storeNode}, @items.map(@renderItem))
-
-window.Menu = Menu
-
-# Hide menu on outside click
-document.body.addEventListener "mouseup", (e) ->
-	if not window.visible_menu or not window.visible_menu.node
-		return false
-	menu_node = window.visible_menu.node
-	menu_parents = [menu_node, menu_node.parentNode]
-	if e.target.parentNode not in menu_parents and e.target.parentNode.parentNode not in menu_parents
-		window.visible_menu.hide()
-		Page.projector.scheduleRender()
diff --git a/plugins/UiFileManager/media/js/lib/Promise.coffee b/plugins/UiFileManager/media/js/lib/Promise.coffee
deleted file mode 100644
index 136e3ec7..00000000
--- a/plugins/UiFileManager/media/js/lib/Promise.coffee
+++ /dev/null
@@ -1,74 +0,0 @@
-# From: http://dev.bizo.com/2011/12/promises-in-javascriptcoffeescript.html
-
-class Promise
-	@when: (tasks...) ->
-		num_uncompleted = tasks.length
-		args = new Array(num_uncompleted)
-		promise = new Promise()
-
-		for task, task_id in tasks
-			((task_id) ->
-				task.then(() ->
-					args[task_id] = Array.prototype.slice.call(arguments)
-					num_uncompleted--
-					promise.complete.apply(promise, args) if num_uncompleted == 0
-				)
-			)(task_id)
-
-		return promise
-
-	constructor: ->
-		@resolved = false
-		@end_promise = null
-		@result = null
-		@callbacks = []
-
-	resolve: ->
-		if @resolved
-			return false
-		@resolved = true
-		@data = arguments
-		if not arguments.length
-			@data = [true]
-		@result = @data[0]
-		for callback in @callbacks
-			back = callback.apply callback, @data
-		if @end_promise
-			@end_promise.resolve(back)
-
-	fail: ->
-		@resolve(false)
-
-	then: (callback) ->
-		if @resolved == true
-			callback.apply callback, @data
-			return
-
-		@callbacks.push callback
-
-		@end_promise = new Promise()
-
-window.Promise = Promise
-
-###
-s = Date.now()
-log = (text) ->
-	console.log Date.now()-s, Array.prototype.slice.call(arguments).join(", ")
-
-log "Started"
-
-cmd = (query) ->
-	p = new Promise()
-	setTimeout ( ->
-		p.resolve query+" Result"
-	), 100
-	return p
-
-back = cmd("SELECT * FROM message").then (res) ->
-	log res
-	return "Return from query"
-.then (res) ->
-	log "Back then", res
-
-log "Query started", back
-###
\ No newline at end of file
diff --git a/plugins/UiFileManager/media/js/lib/Prototypes.coffee b/plugins/UiFileManager/media/js/lib/Prototypes.coffee
deleted file mode 100644
index 8026ee5d..00000000
--- a/plugins/UiFileManager/media/js/lib/Prototypes.coffee
+++ /dev/null
@@ -1,9 +0,0 @@
-String::startsWith = (s) -> @[...s.length] is s
-String::endsWith = (s) -> s is '' or @[-s.length..] is s
-String::repeat = (count) -> new Array( count + 1 ).join(@)
-
-window.isEmpty = (obj) ->
-	for key of obj
-		return false
-	return true
-
diff --git a/plugins/UiFileManager/media/js/lib/RateLimitCb.coffee b/plugins/UiFileManager/media/js/lib/RateLimitCb.coffee
deleted file mode 100644
index a7316f53..00000000
--- a/plugins/UiFileManager/media/js/lib/RateLimitCb.coffee
+++ /dev/null
@@ -1,62 +0,0 @@
-last_time = {}
-calling = {}
-calling_iterval = {}
-call_after_interval = {}
-
-# Rate limit function call and don't allow to run in parallel (until callback is called)
-window.RateLimitCb = (interval, fn, args=[]) ->
-    cb = ->  # Callback when function finished
-        left = interval - (Date.now() - last_time[fn])  # Time life until next call
-        # console.log "CB, left", left, "Calling:", calling[fn]
-        if left <= 0  # No time left from rate limit interval
-            delete last_time[fn]
-            if calling[fn]  # Function called within interval
-                RateLimitCb(interval, fn, calling[fn])
-            delete calling[fn]
-        else  # Time left from rate limit interval
-            setTimeout (->
-                delete last_time[fn]
-                if calling[fn]  # Function called within interval
-                    RateLimitCb(interval, fn, calling[fn])
-                delete calling[fn]
-            ), left
-    if last_time[fn]  # Function called within interval
-        calling[fn] = args  # Schedule call and update arguments
-    else  # Not called within interval, call instantly
-        last_time[fn] = Date.now()
-        fn.apply(this, [cb, args...])
-
-
-window.RateLimit = (interval, fn) ->
-    if calling_iterval[fn] > interval
-        clearInterval calling[fn]
-        delete calling[fn]
-
-    if not calling[fn]
-        call_after_interval[fn] = false
-        fn() # First call is not delayed
-        calling_iterval[fn] = interval
-        calling[fn] = setTimeout (->
-            if call_after_interval[fn]
-                fn()
-            delete calling[fn]
-            delete call_after_interval[fn]
-        ), interval
-    else # Called within iterval, delay the call
-        call_after_interval[fn] = true
-
-
-###
-window.s = Date.now()
-window.load = (done, num) ->
-  console.log "Loading #{num}...", Date.now()-window.s
-  setTimeout (-> done()), 1000
-
-RateLimit 500, window.load, [0] # Called instantly
-RateLimit 500, window.load, [1]
-setTimeout (-> RateLimit 500, window.load, [300]), 300
-setTimeout (-> RateLimit 500, window.load, [600]), 600 # Called after 1000ms
-setTimeout (-> RateLimit 500, window.load, [1000]), 1000
-setTimeout (-> RateLimit 500, window.load, [1200]), 1200  # Called after 2000ms
-setTimeout (-> RateLimit 500, window.load, [3000]), 3000  # Called after 3000ms
-###
\ No newline at end of file
diff --git a/plugins/UiFileManager/media/js/lib/Text.coffee b/plugins/UiFileManager/media/js/lib/Text.coffee
deleted file mode 100644
index a9338983..00000000
--- a/plugins/UiFileManager/media/js/lib/Text.coffee
+++ /dev/null
@@ -1,147 +0,0 @@
-class Text
-	toColor: (text, saturation=30, lightness=50) ->
-		hash = 0
-		for i in [0..text.length-1]
-			hash += text.charCodeAt(i)*i
-			hash = hash % 1777
-		return "hsl(" + (hash % 360) + ",#{saturation}%,#{lightness}%)";
-
-
-	renderMarked: (text, options={}) ->
-		options["gfm"] = true
-		options["breaks"] = true
-		options["sanitize"] = true
-		options["renderer"] = marked_renderer
-		text = marked(text, options)
-		return @fixHtmlLinks text
-
-	emailLinks: (text) ->
-		return text.replace(/([a-zA-Z0-9]+)@zeroid.bit/g, "<a href='?to=$1' onclick='return Page.message_create.show(\"$1\")'>$1@zeroid.bit</a>")
-
-	# Convert zeronet html links to relaitve
-	fixHtmlLinks: (text) ->
-		if window.is_proxy
-			return text.replace(/href="http:\/\/(127.0.0.1|localhost):43110/g, 'href="http://zero')
-		else
-			return text.replace(/href="http:\/\/(127.0.0.1|localhost):43110/g, 'href="')
-
-	# Convert a single link to relative
-	fixLink: (link) ->
-		if window.is_proxy
-			back = link.replace(/http:\/\/(127.0.0.1|localhost):43110/, 'http://zero')
-			return back.replace(/http:\/\/zero\/([^\/]+\.bit)/, "http://$1")  # Domain links
-		else
-			return link.replace(/http:\/\/(127.0.0.1|localhost):43110/, '')
-
-	toUrl: (text) ->
-		return text.replace(/[^A-Za-z0-9]/g, "+").replace(/[+]+/g, "+").replace(/[+]+$/, "")
-
-	getSiteUrl: (address) ->
-		if window.is_proxy
-			if "." in address # Domain
-				return "http://"+address+"/"
-			else
-				return "http://zero/"+address+"/"
-		else
-			return "/"+address+"/"
-
-
-	fixReply: (text) ->
-		return text.replace(/(>.*\n)([^\n>])/gm, "$1\n$2")
-
-	toBitcoinAddress: (text) ->
-		return text.replace(/[^A-Za-z0-9]/g, "")
-
-
-	jsonEncode: (obj) ->
-		return unescape(encodeURIComponent(JSON.stringify(obj)))
-
-	jsonDecode: (obj) ->
-		return JSON.parse(decodeURIComponent(escape(obj)))
-
-	fileEncode: (obj) ->
-		if typeof(obj) == "string"
-			return btoa(unescape(encodeURIComponent(obj)))
-		else
-			return btoa(unescape(encodeURIComponent(JSON.stringify(obj, undefined, '\t'))))
-
-	utf8Encode: (s) ->
-		return unescape(encodeURIComponent(s))
-
-	utf8Decode: (s) ->
-		return decodeURIComponent(escape(s))
-
-
-	distance: (s1, s2) ->
-		s1 = s1.toLocaleLowerCase()
-		s2 = s2.toLocaleLowerCase()
-		next_find_i = 0
-		next_find = s2[0]
-		match = true
-		extra_parts = {}
-		for char in s1
-			if char != next_find
-				if extra_parts[next_find_i]
-					extra_parts[next_find_i] += char
-				else
-					extra_parts[next_find_i] = char
-			else
-				next_find_i++
-				next_find = s2[next_find_i]
-
-		if extra_parts[next_find_i]
-			extra_parts[next_find_i] = ""  # Extra chars on the end doesnt matter
-		extra_parts = (val for key, val of extra_parts)
-		if next_find_i >= s2.length
-			return extra_parts.length + extra_parts.join("").length
-		else
-			return false
-
-
-	parseQuery: (query) ->
-		params = {}
-		parts = query.split('&')
-		for part in parts
-			[key, val] = part.split("=")
-			if val
-				params[decodeURIComponent(key)] = decodeURIComponent(val)
-			else
-				params["url"] = decodeURIComponent(key)
-		return params
-
-	encodeQuery: (params) ->
-		back = []
-		if params.url
-			back.push(params.url)
-		for key, val of params
-			if not val or key == "url"
-				continue
-			back.push("#{encodeURIComponent(key)}=#{encodeURIComponent(val)}")
-		return back.join("&")
-
-	highlight: (text, search) ->
-		if not text
-			return [""]
-		parts = text.split(RegExp(search, "i"))
-		back = []
-		for part, i in parts
-			back.push(part)
-			if i < parts.length-1
-				back.push(h("span.highlight", {key: i}, search))
-		return back
-
-	formatSize: (size) ->
-		if isNaN(parseInt(size))
-			return ""
-		size_mb = size/1024/1024
-		if size_mb >= 1000
-			return (size_mb/1024).toFixed(1)+" GB"
-		else if size_mb >= 100
-			return size_mb.toFixed(0)+" MB"
-		else if size/1024 >= 1000
-			return size_mb.toFixed(2)+" MB"
-		else
-			return (parseInt(size)/1024).toFixed(2)+" KB"
-
-window.is_proxy = (document.location.host == "zero" or window.location.pathname == "/")
-window.Text = new Text()
diff --git a/plugins/UiFileManager/media/js/lib/Time.coffee b/plugins/UiFileManager/media/js/lib/Time.coffee
deleted file mode 100644
index 7adf3d6b..00000000
--- a/plugins/UiFileManager/media/js/lib/Time.coffee
+++ /dev/null
@@ -1,59 +0,0 @@
-class Time
-	since: (timestamp) ->
-		now = +(new Date)/1000
-		if timestamp > 1000000000000  # In ms
-			timestamp = timestamp/1000
-		secs = now - timestamp
-		if secs < 60
-			back = "Just now"
-		else if secs < 60*60
-			minutes = Math.round(secs/60)
-			back = "" + minutes + " minutes ago"
-		else if secs < 60*60*24
-			back = "#{Math.round(secs/60/60)} hours ago"
-		else if secs < 60*60*24*3
-			back = "#{Math.round(secs/60/60/24)} days ago"
-		else
-			back = "on "+@date(timestamp)
-		back = back.replace(/^1 ([a-z]+)s/, "1 $1") # 1 days ago fix
-		return back
-
-	dateIso: (timestamp=null) ->
-		if not timestamp
-			timestamp = window.Time.timestamp()
-
-		if timestamp > 1000000000000  # In ms
-			timestamp = timestamp/1000
-		tzoffset = (new Date()).getTimezoneOffset() * 60
-		return (new Date((timestamp - tzoffset) * 1000)).toISOString().split("T")[0]
-
-	date: (timestamp=null, format="short") ->
-		if not timestamp
-			timestamp = window.Time.timestamp()
-
-		if timestamp > 1000000000000  # In ms
-			timestamp = timestamp/1000
-		parts = (new Date(timestamp * 1000)).toString().split(" ")
-		if format == "short"
-			display = parts.slice(1, 4)
-		else if format == "day"
-			display = parts.slice(1, 3)
-		else if format == "month"
-			display = [parts[1], parts[3]]
-		else if format == "long"
-			display = parts.slice(1, 5)
-		return display.join(" ").replace(/( [0-9]{4})/, ",$1")
-
-	weekDay: (timestamp) ->
-		if timestamp > 1000000000000  # In ms
-			timestamp = timestamp/1000
-		return ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"][ (new Date(timestamp * 1000)).getDay() ]
-
-	timestamp: (date="") ->
-		if date == "now" or date == ""
-			return parseInt(+(new Date)/1000)
-		else
-			return parseInt(Date.parse(date)/1000)
-
-
-window.Time = new Time
\ No newline at end of file
diff --git a/plugins/UiFileManager/media/js/lib/ZeroFrame.coffee b/plugins/UiFileManager/media/js/lib/ZeroFrame.coffee
deleted file mode 100644
index 11512d16..00000000
--- a/plugins/UiFileManager/media/js/lib/ZeroFrame.coffee
+++ /dev/null
@@ -1,85 +0,0 @@
-class ZeroFrame extends Class
-	constructor: (url) ->
-		@url = url
-		@waiting_cb = {}
-		@wrapper_nonce = document.location.href.replace(/.*wrapper_nonce=([A-Za-z0-9]+).*/, "$1")
-		@connect()
-		@next_message_id = 1
-		@history_state = {}
-		@init()
-
-
-	init: ->
-		@
-
-
-	connect: ->
-		@target = window.parent
-		window.addEventListener("message", @onMessage, false)
-		@cmd("innerReady")
-
-		# Save scrollTop
-		window.addEventListener "beforeunload", (e) =>
-			@log "save scrollTop", window.pageYOffset
-			@history_state["scrollTop"] = window.pageYOffset
-			@cmd "wrapperReplaceState", [@history_state, null]
-
-		# Restore scrollTop
-		@cmd "wrapperGetState", [], (state) =>
-			@history_state = state if state?
-			@log "restore scrollTop", state, window.pageYOffset
-			if window.pageYOffset == 0 and state
-				window.scroll(window.pageXOffset, state.scrollTop)
-
-
-	onMessage: (e) =>
-		message = e.data
-		cmd = message.cmd
-		if cmd == "response"
-			if @waiting_cb[message.to]?
-				@waiting_cb[message.to](message.result)
-			else
-				@log "Websocket callback not found:", message
-		else if cmd == "wrapperReady" # Wrapper inited later
-			@cmd("innerReady")
-		else if cmd == "ping"
-			@response message.id, "pong"
-		else if cmd == "wrapperOpenedWebsocket"
-			@onOpenWebsocket()
-		else if cmd == "wrapperClosedWebsocket"
-			@onCloseWebsocket()
-		else
-			@onRequest cmd, message.params
-
-
-	onRequest: (cmd, message) =>
-		@log "Unknown request", message
-
-
-	response: (to, result) ->
-		@send {"cmd": "response", "to": to, "result": result}
-
-
-	cmd: (cmd, params={}, cb=null) ->
-		@send {"cmd": cmd, "params": params}, cb
-
-
-	send: (message, cb=null) ->
-		message.wrapper_nonce = @wrapper_nonce
-		message.id = @next_message_id
-		@next_message_id += 1
-		@target.postMessage(message, "*")
-		if cb
-			@waiting_cb[message.id] = cb
-
-
-	onOpenWebsocket: =>
-		@log "Websocket open"
-
-
-	onCloseWebsocket: =>
-		@log "Websocket close"
-
-
-
-window.ZeroFrame = ZeroFrame
diff --git a/plugins/UiFileManager/media/js/lib/maquette.js b/plugins/UiFileManager/media/js/lib/maquette.js
deleted file mode 100644
index 84d14471..00000000
--- a/plugins/UiFileManager/media/js/lib/maquette.js
+++ /dev/null
@@ -1,770 +0,0 @@
-(function (root, factory) {
-    if (typeof define === 'function' && define.amd) {
-        // AMD. Register as an anonymous module.
-        define(['exports'], factory);
-    } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
-        // CommonJS
-        factory(exports);
-    } else {
-        // Browser globals
-        factory(root.maquette = {});
-    }
-}(this, function (exports) {
-    'use strict';
-    ;
-    ;
-    ;
-    ;
-    var NAMESPACE_W3 = 'http://www.w3.org/';
-    var NAMESPACE_SVG = NAMESPACE_W3 + '2000/svg';
-    var NAMESPACE_XLINK = NAMESPACE_W3 + '1999/xlink';
-    // Utilities
-    var emptyArray = [];
-    var extend = function (base, overrides) {
-        var result = {};
-        Object.keys(base).forEach(function (key) {
-            result[key] = base[key];
-        });
-        if (overrides) {
-            Object.keys(overrides).forEach(function (key) {
-                result[key] = overrides[key];
-            });
-        }
-        return result;
-    };
-    // Hyperscript helper functions
-    var same = function (vnode1, vnode2) {
-        if (vnode1.vnodeSelector !== vnode2.vnodeSelector) {
-            return false;
-        }
-        if (vnode1.properties && vnode2.properties) {
-            if (vnode1.properties.key !== vnode2.properties.key) {
-                return false;
-            }
-            return vnode1.properties.bind === vnode2.properties.bind;
-        }
-        return !vnode1.properties && !vnode2.properties;
-    };
-    var toTextVNode = function (data) {
-        return {
-            vnodeSelector: '',
-            properties: undefined,
-            children: undefined,
-            text: data.toString(),
-            domNode: null
-        };
-    };
-    var appendChildren = function (parentSelector, insertions, main) {
-        for (var i = 0; i < insertions.length; i++) {
-            var item = insertions[i];
-            if (Array.isArray(item)) {
-                appendChildren(parentSelector, item, main);
-            } else {
-                if (item !== null && item !== undefined) {
-                    if (!item.hasOwnProperty('vnodeSelector')) {
-                        item = toTextVNode(item);
-                    }
-                    main.push(item);
-                }
-            }
-        }
-    };
-    // Render helper functions
-    var missingTransition = function () {
-        throw new Error('Provide a transitions object to the projectionOptions to do animations');
-    };
-    var DEFAULT_PROJECTION_OPTIONS = {
-        namespace: undefined,
-        eventHandlerInterceptor: undefined,
-        styleApplyer: function (domNode, styleName, value) {
-            // Provides a hook to add vendor prefixes for browsers that still need it.
-            domNode.style[styleName] = value;
-        },
-        transitions: {
-            enter: missingTransition,
-            exit: missingTransition
-        }
-    };
-    var applyDefaultProjectionOptions = function (projectorOptions) {
-        return extend(DEFAULT_PROJECTION_OPTIONS, projectorOptions);
-    };
-    var checkStyleValue = function (styleValue) {
-        if (typeof styleValue !== 'string') {
-            throw new Error('Style values must be strings');
-        }
-    };
-    var setProperties = function (domNode, properties, projectionOptions) {
-        if (!properties) {
-            return;
-        }
-        var eventHandlerInterceptor = projectionOptions.eventHandlerInterceptor;
-        var propNames = Object.keys(properties);
-        var propCount = propNames.length;
-        for (var i = 0; i < propCount; i++) {
-            var propName = propNames[i];
-            /* tslint:disable:no-var-keyword: edge case */
-            var propValue = properties[propName];
-            /* tslint:enable:no-var-keyword */
-            if (propName === 'className') {
-                throw new Error('Property "className" is not supported, use "class".');
-            } else if (propName === 'class') {
-                if (domNode.className) {
-                    // May happen if classes is specified before class
-                    domNode.className += ' ' + propValue;
-                } else {
-                    domNode.className = propValue;
-                }
-            } else if (propName === 'classes') {
-                // object with string keys and boolean values
-                var classNames = Object.keys(propValue);
-                var classNameCount = classNames.length;
-                for (var j = 0; j < classNameCount; j++) {
-                    var className = classNames[j];
-                    if (propValue[className]) {
-                        domNode.classList.add(className);
-                    }
-                }
-            } else if (propName === 'styles') {
-                // object with string keys and string (!) values
-                var styleNames = Object.keys(propValue);
-                var styleCount = styleNames.length;
-                for (var j = 0; j < styleCount; j++) {
-                    var styleName = styleNames[j];
-                    var styleValue = propValue[styleName];
-                    if (styleValue) {
-                        checkStyleValue(styleValue);
-                        projectionOptions.styleApplyer(domNode, styleName, styleValue);
-                    }
-                }
-            } else if (propName === 'key') {
-                continue;
-            } else if (propValue === null || propValue === undefined) {
-                continue;
-            } else {
-                var type = typeof propValue;
-                if (type === 'function') {
-                    if (propName.lastIndexOf('on', 0) === 0) {
-                        if (eventHandlerInterceptor) {
-                            propValue = eventHandlerInterceptor(propName, propValue, domNode, properties);    // intercept eventhandlers
-                        }
-                        if (propName === 'oninput') {
-                            (function () {
-                                // record the evt.target.value, because IE and Edge sometimes do a requestAnimationFrame between changing value and running oninput
-                                var oldPropValue = propValue;
-                                propValue = function (evt) {
-                                    evt.target['oninput-value'] = evt.target.value;
-                                    // may be HTMLTextAreaElement as well
-                                    oldPropValue.apply(this, [evt]);
-                                };
-                            }());
-                        }
-                        domNode[propName] = propValue;
-                    }
-                } else if (type === 'string' && propName !== 'value' && propName !== 'innerHTML') {
-                    if (projectionOptions.namespace === NAMESPACE_SVG && propName === 'href') {
-                        domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);
-                    } else {
-                        domNode.setAttribute(propName, propValue);
-                    }
-                } else {
-                    domNode[propName] = propValue;
-                }
-            }
-        }
-    };
-    var updateProperties = function (domNode, previousProperties, properties, projectionOptions) {
-        if (!properties) {
-            return;
-        }
-        var propertiesUpdated = false;
-        var propNames = Object.keys(properties);
-        var propCount = propNames.length;
-        for (var i = 0; i < propCount; i++) {
-            var propName = propNames[i];
-            // assuming that properties will be nullified instead of missing is by design
-            var propValue = properties[propName];
-            var previousValue = previousProperties[propName];
-            if (propName === 'class') {
-                if (previousValue !== propValue) {
-                    throw new Error('"class" property may not be updated. Use the "classes" property for conditional css classes.');
-                }
-            } else if (propName === 'classes') {
-                var classList = domNode.classList;
-                var classNames = Object.keys(propValue);
-                var classNameCount = classNames.length;
-                for (var j = 0; j < classNameCount; j++) {
-                    var className = classNames[j];
-                    var on = !!propValue[className];
-                    var previousOn = !!previousValue[className];
-                    if (on === previousOn) {
-                        continue;
-                    }
-                    propertiesUpdated = true;
-                    if (on) {
-                        classList.add(className);
-                    } else {
-                        classList.remove(className);
-                    }
-                }
-            } else if (propName === 'styles') {
-                var styleNames = Object.keys(propValue);
-                var styleCount = styleNames.length;
-                for (var j = 0; j < styleCount; j++) {
-                    var styleName = styleNames[j];
-                    var newStyleValue = propValue[styleName];
-                    var oldStyleValue = previousValue[styleName];
-                    if (newStyleValue === oldStyleValue) {
-                        continue;
-                    }
-                    propertiesUpdated = true;
-                    if (newStyleValue) {
-                        checkStyleValue(newStyleValue);
-                        projectionOptions.styleApplyer(domNode, styleName, newStyleValue);
-                    } else {
-                        projectionOptions.styleApplyer(domNode, styleName, '');
-                    }
-                }
-            } else {
-                if (!propValue && typeof previousValue === 'string') {
-                    propValue = '';
-                }
-                if (propName === 'value') {
-                    if (domNode[propName] !== propValue && domNode['oninput-value'] !== propValue) {
-                        domNode[propName] = propValue;
-                        // Reset the value, even if the virtual DOM did not change
-                        domNode['oninput-value'] = undefined;
-                    }
-                    // else do not update the domNode, otherwise the cursor position would be changed
-                    if (propValue !== previousValue) {
-                        propertiesUpdated = true;
-                    }
-                } else if (propValue !== previousValue) {
-                    var type = typeof propValue;
-                    if (type === 'function') {
-                        throw new Error('Functions may not be updated on subsequent renders (property: ' + propName + '). Hint: declare event handler functions outside the render() function.');
-                    }
-                    if (type === 'string' && propName !== 'innerHTML') {
-                        if (projectionOptions.namespace === NAMESPACE_SVG && propName === 'href') {
-                            domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);
-                        } else {
-                            domNode.setAttribute(propName, propValue);
-                        }
-                    } else {
-                        if (domNode[propName] !== propValue) {
-                            domNode[propName] = propValue;
-                        }
-                    }
-                    propertiesUpdated = true;
-                }
-            }
-        }
-        return propertiesUpdated;
-    };
-    var findIndexOfChild = function (children, sameAs, start) {
-        if (sameAs.vnodeSelector !== '') {
-            // Never scan for text-nodes
-            for (var i = start; i < children.length; i++) {
-                if (same(children[i], sameAs)) {
-                    return i;
-                }
-            }
-        }
-        return -1;
-    };
-    var nodeAdded = function (vNode, transitions) {
-        if (vNode.properties) {
-            var enterAnimation = vNode.properties.enterAnimation;
-            if (enterAnimation) {
-                if (typeof enterAnimation === 'function') {
-                    enterAnimation(vNode.domNode, vNode.properties);
-                } else {
-                    transitions.enter(vNode.domNode, vNode.properties, enterAnimation);
-                }
-            }
-        }
-    };
-    var nodeToRemove = function (vNode, transitions) {
-        var domNode = vNode.domNode;
-        if (vNode.properties) {
-            var exitAnimation = vNode.properties.exitAnimation;
-            if (exitAnimation) {
-                domNode.style.pointerEvents = 'none';
-                var removeDomNode = function () {
-                    if (domNode.parentNode) {
-                        domNode.parentNode.removeChild(domNode);
-                    }
-                };
-                if (typeof exitAnimation === 'function') {
-                    exitAnimation(domNode, removeDomNode, vNode.properties);
-                    return;
-                } else {
-                    transitions.exit(vNode.domNode, vNode.properties, exitAnimation, removeDomNode);
-                    return;
-                }
-            }
-        }
-        if (domNode.parentNode) {
-            domNode.parentNode.removeChild(domNode);
-        }
-    };
-    var checkDistinguishable = function (childNodes, indexToCheck, parentVNode, operation) {
-        var childNode = childNodes[indexToCheck];
-        if (childNode.vnodeSelector === '') {
-            return;    // Text nodes need not be distinguishable
-        }
-        var properties = childNode.properties;
-        var key = properties ? properties.key === undefined ? properties.bind : properties.key : undefined;
-        if (!key) {
-            for (var i = 0; i < childNodes.length; i++) {
-                if (i !== indexToCheck) {
-                    var node = childNodes[i];
-                    if (same(node, childNode)) {
-                        if (operation === 'added') {
-                            throw new Error(parentVNode.vnodeSelector + ' had a ' + childNode.vnodeSelector + ' child ' + 'added, but there is now more than one. You must add unique key properties to make them distinguishable.');
-                        } else {
-                            throw new Error(parentVNode.vnodeSelector + ' had a ' + childNode.vnodeSelector + ' child ' + 'removed, but there were more than one. You must add unique key properties to make them distinguishable.');
-                        }
-                    }
-                }
-            }
-        }
-    };
-    var createDom;
-    var updateDom;
-    var updateChildren = function (vnode, domNode, oldChildren, newChildren, projectionOptions) {
-        if (oldChildren === newChildren) {
-            return false;
-        }
-        oldChildren = oldChildren || emptyArray;
-        newChildren = newChildren || emptyArray;
-        var oldChildrenLength = oldChildren.length;
-        var newChildrenLength = newChildren.length;
-        var transitions = projectionOptions.transitions;
-        var oldIndex = 0;
-        var newIndex = 0;
-        var i;
-        var textUpdated = false;
-        while (newIndex < newChildrenLength) {
-            var oldChild = oldIndex < oldChildrenLength ? oldChildren[oldIndex] : undefined;
-            var newChild = newChildren[newIndex];
-            if (oldChild !== undefined && same(oldChild, newChild)) {
-                textUpdated = updateDom(oldChild, newChild, projectionOptions) || textUpdated;
-                oldIndex++;
-            } else {
-                var findOldIndex = findIndexOfChild(oldChildren, newChild, oldIndex + 1);
-                if (findOldIndex >= 0) {
-                    // Remove preceding missing children
-                    for (i = oldIndex; i < findOldIndex; i++) {
-                        nodeToRemove(oldChildren[i], transitions);
-                        checkDistinguishable(oldChildren, i, vnode, 'removed');
-                    }
-                    textUpdated = updateDom(oldChildren[findOldIndex], newChild, projectionOptions) || textUpdated;
-                    oldIndex = findOldIndex + 1;
-                } else {
-                    // New child
-                    createDom(newChild, domNode, oldIndex < oldChildrenLength ? oldChildren[oldIndex].domNode : undefined, projectionOptions);
-                    nodeAdded(newChild, transitions);
-                    checkDistinguishable(newChildren, newIndex, vnode, 'added');
-                }
-            }
-            newIndex++;
-        }
-        if (oldChildrenLength > oldIndex) {
-            // Remove child fragments
-            for (i = oldIndex; i < oldChildrenLength; i++) {
-                nodeToRemove(oldChildren[i], transitions);
-                checkDistinguishable(oldChildren, i, vnode, 'removed');
-            }
-        }
-        return textUpdated;
-    };
-    var addChildren = function (domNode, children, projectionOptions) {
-        if (!children) {
-            return;
-        }
-        for (var i = 0; i < children.length; i++) {
-            createDom(children[i], domNode, undefined, projectionOptions);
-        }
-    };
-    var initPropertiesAndChildren = function (domNode, vnode, projectionOptions) {
-        addChildren(domNode, vnode.children, projectionOptions);
-        // children before properties, needed for value property of <select>.
-        if (vnode.text) {
-            domNode.textContent = vnode.text;
-        }
-        setProperties(domNode, vnode.properties, projectionOptions);
-        if (vnode.properties && vnode.properties.afterCreate) {
-            vnode.properties.afterCreate(domNode, projectionOptions, vnode.vnodeSelector, vnode.properties, vnode.children);
-        }
-    };
-    createDom = function (vnode, parentNode, insertBefore, projectionOptions) {
-        var domNode, i, c, start = 0, type, found;
-        var vnodeSelector = vnode.vnodeSelector;
-        if (vnodeSelector === '') {
-            domNode = vnode.domNode = document.createTextNode(vnode.text);
-            if (insertBefore !== undefined) {
-                parentNode.insertBefore(domNode, insertBefore);
-            } else {
-                parentNode.appendChild(domNode);
-            }
-        } else {
-            for (i = 0; i <= vnodeSelector.length; ++i) {
-                c = vnodeSelector.charAt(i);
-                if (i === vnodeSelector.length || c === '.' || c === '#') {
-                    type = vnodeSelector.charAt(start - 1);
-                    found = vnodeSelector.slice(start, i);
-                    if (type === '.') {
-                        domNode.classList.add(found);
-                    } else if (type === '#') {
-                        domNode.id = found;
-                    } else {
-                        if (found === 'svg') {
-                            projectionOptions = extend(projectionOptions, { namespace: NAMESPACE_SVG });
-                        }
-                        if (projectionOptions.namespace !== undefined) {
-                            domNode = vnode.domNode = document.createElementNS(projectionOptions.namespace, found);
-                        } else {
-                            domNode = vnode.domNode = document.createElement(found);
-                        }
-                        if (insertBefore !== undefined) {
-                            parentNode.insertBefore(domNode, insertBefore);
-                        } else {
-                            parentNode.appendChild(domNode);
-                        }
-                    }
-                    start = i + 1;
-                }
-            }
-            initPropertiesAndChildren(domNode, vnode, projectionOptions);
-        }
-    };
-    updateDom = function (previous, vnode, projectionOptions) {
-        var domNode = previous.domNode;
-        var textUpdated = false;
-        if (previous === vnode) {
-            return false;    // By contract, VNode objects may not be modified anymore after passing them to maquette
-        }
-        var updated = false;
-        if (vnode.vnodeSelector === '') {
-            if (vnode.text !== previous.text) {
-                var newVNode = document.createTextNode(vnode.text);
-                domNode.parentNode.replaceChild(newVNode, domNode);
-                vnode.domNode = newVNode;
-                textUpdated = true;
-                return textUpdated;
-            }
-        } else {
-            if (vnode.vnodeSelector.lastIndexOf('svg', 0) === 0) {
-                projectionOptions = extend(projectionOptions, { namespace: NAMESPACE_SVG });
-            }
-            if (previous.text !== vnode.text) {
-                updated = true;
-                if (vnode.text === undefined) {
-                    domNode.removeChild(domNode.firstChild);    // the only textnode presumably
-                } else {
-                    domNode.textContent = vnode.text;
-                }
-            }
-            updated = updateChildren(vnode, domNode, previous.children, vnode.children, projectionOptions) || updated;
-            updated = updateProperties(domNode, previous.properties, vnode.properties, projectionOptions) || updated;
-            if (vnode.properties && vnode.properties.afterUpdate) {
-                vnode.properties.afterUpdate(domNode, projectionOptions, vnode.vnodeSelector, vnode.properties, vnode.children);
-            }
-        }
-        if (updated && vnode.properties && vnode.properties.updateAnimation) {
-            vnode.properties.updateAnimation(domNode, vnode.properties, previous.properties);
-        }
-        vnode.domNode = previous.domNode;
-        return textUpdated;
-    };
-    var createProjection = function (vnode, projectionOptions) {
-        return {
-            update: function (updatedVnode) {
-                if (vnode.vnodeSelector !== updatedVnode.vnodeSelector) {
-                    throw new Error('The selector for the root VNode may not be changed. (consider using dom.merge and add one extra level to the virtual DOM)');
-                }
-                updateDom(vnode, updatedVnode, projectionOptions);
-                vnode = updatedVnode;
-            },
-            domNode: vnode.domNode
-        };
-    };
-    ;
-    // The other two parameters are not added here, because the Typescript compiler creates surrogate code for desctructuring 'children'.
-    exports.h = function (selector) {
-        var properties = arguments[1];
-        if (typeof selector !== 'string') {
-            throw new Error();
-        }
-        var childIndex = 1;
-        if (properties && !properties.hasOwnProperty('vnodeSelector') && !Array.isArray(properties) && typeof properties === 'object') {
-            childIndex = 2;
-        } else {
-            // Optional properties argument was omitted
-            properties = undefined;
-        }
-        var text = undefined;
-        var children = undefined;
-        var argsLength = arguments.length;
-        // Recognize a common special case where there is only a single text node
-        if (argsLength === childIndex + 1) {
-            var onlyChild = arguments[childIndex];
-            if (typeof onlyChild === 'string') {
-                text = onlyChild;
-            } else if (onlyChild !== undefined && onlyChild.length === 1 && typeof onlyChild[0] === 'string') {
-                text = onlyChild[0];
-            }
-        }
-        if (text === undefined) {
-            children = [];
-            for (; childIndex < arguments.length; childIndex++) {
-                var child = arguments[childIndex];
-                if (child === null || child === undefined) {
-                    continue;
-                } else if (Array.isArray(child)) {
-                    appendChildren(selector, child, children);
-                } else if (child.hasOwnProperty('vnodeSelector')) {
-                    children.push(child);
-                } else {
-                    children.push(toTextVNode(child));
-                }
-            }
-        }
-        return {
-            vnodeSelector: selector,
-            properties: properties,
-            children: children,
-            text: text === '' ? undefined : text,
-            domNode: null
-        };
-    };
-    /**
- * Contains simple low-level utility functions to manipulate the real DOM.
- */
-    exports.dom = {
-        /**
-     * Creates a real DOM tree from `vnode`. The [[Projection]] object returned will contain the resulting DOM Node in
-     * its [[Projection.domNode|domNode]] property.
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
-     * objects may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the projection.
-     * @returns The [[Projection]] which also contains the DOM Node that was created.
-     */
-        create: function (vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            createDom(vnode, document.createElement('div'), undefined, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        },
-        /**
-     * Appends a new childnode to the DOM which is generated from a [[VNode]].
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param parentNode - The parent node for the new childNode.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
-     * objects may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the [[Projection]].
-     * @returns The [[Projection]] that was created.
-     */
-        append: function (parentNode, vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            createDom(vnode, parentNode, undefined, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        },
-        /**
-     * Inserts a new DOM node which is generated from a [[VNode]].
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param beforeNode - The node that the DOM Node is inserted before.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function.
-     * NOTE: [[VNode]] objects may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the projection, see [[createProjector]].
-     * @returns The [[Projection]] that was created.
-     */
-        insertBefore: function (beforeNode, vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            createDom(vnode, beforeNode.parentNode, beforeNode, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        },
-        /**
-     * Merges a new DOM node which is generated from a [[VNode]] with an existing DOM Node.
-     * This means that the virtual DOM and the real DOM will have one overlapping element.
-     * Therefore the selector for the root [[VNode]] will be ignored, but its properties and children will be applied to the Element provided.
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param domNode - The existing element to adopt as the root of the new virtual DOM. Existing attributes and childnodes are preserved.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]] objects
-     * may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the projection, see [[createProjector]].
-     * @returns The [[Projection]] that was created.
-     */
-        merge: function (element, vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            vnode.domNode = element;
-            initPropertiesAndChildren(element, vnode, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        }
-    };
-    /**
- * Creates a [[CalculationCache]] object, useful for caching [[VNode]] trees.
- * In practice, caching of [[VNode]] trees is not needed, because achieving 60 frames per second is almost never a problem.
- * For more information, see [[CalculationCache]].
- *
- * @param <Result> The type of the value that is cached.
- */
-    exports.createCache = function () {
-        var cachedInputs = undefined;
-        var cachedOutcome = undefined;
-        var result = {
-            invalidate: function () {
-                cachedOutcome = undefined;
-                cachedInputs = undefined;
-            },
-            result: function (inputs, calculation) {
-                if (cachedInputs) {
-                    for (var i = 0; i < inputs.length; i++) {
-                        if (cachedInputs[i] !== inputs[i]) {
-                            cachedOutcome = undefined;
-                        }
-                    }
-                }
-                if (!cachedOutcome) {
-                    cachedOutcome = calculation();
-                    cachedInputs = inputs;
-                }
-                return cachedOutcome;
-            }
-        };
-        return result;
-    };
-    /**
- * Creates a {@link Mapping} instance that keeps an array of result objects synchronized with an array of source objects.
- * See {@link http://maquettejs.org/docs/arrays.html|Working with arrays}.
- *
- * @param <Source>       The type of source items. A database-record for instance.
- * @param <Target>       The type of target items. A [[Component]] for instance.
- * @param getSourceKey   `function(source)` that must return a key to identify each source object. The result must either be a string or a number.
- * @param createResult   `function(source, index)` that must create a new result object from a given source. This function is identical
- *                       to the `callback` argument in `Array.map(callback)`.
- * @param updateResult   `function(source, target, index)` that updates a result to an updated source.
- */
-    exports.createMapping = function (getSourceKey, createResult, updateResult) {
-        var keys = [];
-        var results = [];
-        return {
-            results: results,
-            map: function (newSources) {
-                var newKeys = newSources.map(getSourceKey);
-                var oldTargets = results.slice();
-                var oldIndex = 0;
-                for (var i = 0; i < newSources.length; i++) {
-                    var source = newSources[i];
-                    var sourceKey = newKeys[i];
-                    if (sourceKey === keys[oldIndex]) {
-                        results[i] = oldTargets[oldIndex];
-                        updateResult(source, oldTargets[oldIndex], i);
-                        oldIndex++;
-                    } else {
-                        var found = false;
-                        for (var j = 1; j < keys.length; j++) {
-                            var searchIndex = (oldIndex + j) % keys.length;
-                            if (keys[searchIndex] === sourceKey) {
-                                results[i] = oldTargets[searchIndex];
-                                updateResult(newSources[i], oldTargets[searchIndex], i);
-                                oldIndex = searchIndex + 1;
-                                found = true;
-                                break;
-                            }
-                        }
-                        if (!found) {
-                            results[i] = createResult(source, i);
-                        }
-                    }
-                }
-                results.length = newSources.length;
-                keys = newKeys;
-            }
-        };
-    };
-    /**
- * Creates a [[Projector]] instance using the provided projectionOptions.
- *
- * For more information, see [[Projector]].
- *
- * @param projectionOptions   Options that influence how the DOM is rendered and updated.
- */
-    exports.createProjector = function (projectorOptions) {
-        var projector;
-        var projectionOptions = applyDefaultProjectionOptions(projectorOptions);
-        projectionOptions.eventHandlerInterceptor = function (propertyName, eventHandler, domNode, properties) {
-            return function () {
-                // intercept function calls (event handlers) to do a render afterwards.
-                projector.scheduleRender();
-                return eventHandler.apply(properties.bind || this, arguments);
-            };
-        };
-        var renderCompleted = true;
-        var scheduled;
-        var stopped = false;
-        var projections = [];
-        var renderFunctions = [];
-        // matches the projections array
-        var doRender = function () {
-            scheduled = undefined;
-            if (!renderCompleted) {
-                return;    // The last render threw an error, it should be logged in the browser console.
-            }
-            renderCompleted = false;
-            for (var i = 0; i < projections.length; i++) {
-                var updatedVnode = renderFunctions[i]();
-                projections[i].update(updatedVnode);
-            }
-            renderCompleted = true;
-        };
-        projector = {
-            scheduleRender: function () {
-                if (!scheduled && !stopped) {
-                    scheduled = requestAnimationFrame(doRender);
-                }
-            },
-            stop: function () {
-                if (scheduled) {
-                    cancelAnimationFrame(scheduled);
-                    scheduled = undefined;
-                }
-                stopped = true;
-            },
-            resume: function () {
-                stopped = false;
-                renderCompleted = true;
-                projector.scheduleRender();
-            },
-            append: function (parentNode, renderMaquetteFunction) {
-                projections.push(exports.dom.append(parentNode, renderMaquetteFunction(), projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            insertBefore: function (beforeNode, renderMaquetteFunction) {
-                projections.push(exports.dom.insertBefore(beforeNode, renderMaquetteFunction(), projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            merge: function (domNode, renderMaquetteFunction) {
-                projections.push(exports.dom.merge(domNode, renderMaquetteFunction(), projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            replace: function (domNode, renderMaquetteFunction) {
-                var vnode = renderMaquetteFunction();
-                createDom(vnode, domNode.parentNode, domNode, projectionOptions);
-                domNode.parentNode.removeChild(domNode);
-                projections.push(createProjection(vnode, projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            detach: function (renderMaquetteFunction) {
-                for (var i = 0; i < renderFunctions.length; i++) {
-                    if (renderFunctions[i] === renderMaquetteFunction) {
-                        renderFunctions.splice(i, 1);
-                        return projections.splice(i, 1)[0];
-                    }
-                }
-                throw new Error('renderMaquetteFunction was not found');
-            }
-        };
-        return projector;
-    };
-}));
diff --git a/plugins/UiFileManager/media/list.html b/plugins/UiFileManager/media/list.html
deleted file mode 100644
index 26851b4c..00000000
--- a/plugins/UiFileManager/media/list.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!DOCTYPE html>
-
-<html>
-<head>
- <title>List - ZeroNet</title>
- <meta charset="utf-8" />
- <meta http-equiv="content-type" content="text/html; charset=utf-8" />
- <link rel="stylesheet" href="css/all.css?site_modified={rev}" />
- <base href="" target="_top" id="base">
- <script>base.href = document.location.href.replace("/media", "").replace("index.html", "").replace(/[&?]wrapper=False/, "").replace(/[&?]wrapper_nonce=[A-Za-z0-9]+/, "")</script>
-</head>
-<body class="{themeclass}">
-
-<div class="content" id="content"></div>
-
-<script type="text/javascript" src="js/all.js?rev={rev}&lang={lang}"></script>
-</body>
-</html>
\ No newline at end of file
diff --git a/plugins/UiPluginManager/UiPluginManagerPlugin.py b/plugins/UiPluginManager/UiPluginManagerPlugin.py
deleted file mode 100644
index 1ab80f53..00000000
--- a/plugins/UiPluginManager/UiPluginManagerPlugin.py
+++ /dev/null
@@ -1,221 +0,0 @@
-import io
-import os
-import json
-import shutil
-import time
-
-from Plugin import PluginManager
-from Config import config
-from Debug import Debug
-from Translate import Translate
-from util.Flag import flag
-
-
-plugin_dir = os.path.dirname(__file__)
-
-if "_" not in locals():
-    _ = Translate(plugin_dir + "/languages/")
-
-
-# Convert non-str,int,float values to str in a dict
-def restrictDictValues(input_dict):
-    allowed_types = (int, str, float)
-    return {
-        key: val if type(val) in allowed_types else str(val)
-        for key, val in input_dict.items()
-    }
-
-
-@PluginManager.registerTo("UiRequest")
-class UiRequestPlugin(object):
-    def actionWrapper(self, path, extra_headers=None):
-        if path.strip("/") != "Plugins":
-            return super(UiRequestPlugin, self).actionWrapper(path, extra_headers)
-
-        if not extra_headers:
-            extra_headers = {}
-
-        script_nonce = self.getScriptNonce()
-
-        self.sendHeader(extra_headers=extra_headers, script_nonce=script_nonce)
-        site = self.server.site_manager.get(config.homepage)
-        return iter([super(UiRequestPlugin, self).renderWrapper(
-            site, path, "uimedia/plugins/plugin_manager/plugin_manager.html",
-            "Plugin Manager", extra_headers, show_loadingscreen=False, script_nonce=script_nonce
-        )])
-
-    def actionUiMedia(self, path, *args, **kwargs):
-        if path.startswith("/uimedia/plugins/plugin_manager/"):
-            file_path = path.replace("/uimedia/plugins/plugin_manager/", plugin_dir + "/media/")
-            if config.debug and (file_path.endswith("all.js") or file_path.endswith("all.css")):
-                # If debugging merge *.css to all.css and *.js to all.js
-                from Debug import DebugMedia
-                DebugMedia.merge(file_path)
-
-            if file_path.endswith("js"):
-                data = _.translateData(open(file_path).read(), mode="js").encode("utf8")
-            elif file_path.endswith("html"):
-                data = _.translateData(open(file_path).read(), mode="html").encode("utf8")
-            else:
-                data = open(file_path, "rb").read()
-
-            return self.actionFile(file_path, file_obj=io.BytesIO(data), file_size=len(data))
-        else:
-            return super(UiRequestPlugin, self).actionUiMedia(path)
-
-
-@PluginManager.registerTo("UiWebsocket")
-class UiWebsocketPlugin(object):
-    @flag.admin
-    def actionPluginList(self, to):
-        plugins = []
-        for plugin in PluginManager.plugin_manager.listPlugins(list_disabled=True):
-            plugin_info_path = plugin["dir_path"] + "/plugin_info.json"
-            plugin_info = {}
-            if os.path.isfile(plugin_info_path):
-                try:
-                    plugin_info = json.load(open(plugin_info_path))
-                except Exception as err:
-                    self.log.error(
-                        "Error loading plugin info for %s: %s" %
-                        (plugin["name"], Debug.formatException(err))
-                    )
-            if plugin_info:
-                plugin_info = restrictDictValues(plugin_info)  # For security reasons don't allow complex values
-                plugin["info"] = plugin_info
-
-            if plugin["source"] != "builtin":
-                plugin_site = self.server.sites.get(plugin["source"])
-                if plugin_site:
-                    try:
-                        plugin_site_info = plugin_site.storage.loadJson(plugin["inner_path"] + "/plugin_info.json")
-                        plugin_site_info = restrictDictValues(plugin_site_info)
-                        plugin["site_info"] = plugin_site_info
-                        plugin["site_title"] = plugin_site.content_manager.contents["content.json"].get("title")
-                        plugin_key = "%s/%s" % (plugin["source"], plugin["inner_path"])
-                        plugin["updated"] = plugin_key in PluginManager.plugin_manager.plugins_updated
-                    except Exception:
-                        pass
-
-            plugins.append(plugin)
-
-        return {"plugins": plugins}
-
-    @flag.admin
-    @flag.no_multiuser
-    def actionPluginConfigSet(self, to, source, inner_path, key, value):
-        plugin_manager = PluginManager.plugin_manager
-        plugins = plugin_manager.listPlugins(list_disabled=True)
-        plugin = None
-        for item in plugins:
-            if item["source"] == source and item["inner_path"] in (inner_path, "disabled-" + inner_path):
-                plugin = item
-                break
-
-        if not plugin:
-            return {"error": "Plugin not found"}
-
-        config_source = plugin_manager.config.setdefault(source, {})
-        config_plugin = config_source.setdefault(inner_path, {})
-
-        if key in config_plugin and value is None:
-            del config_plugin[key]
-        else:
-            config_plugin[key] = value
-
-        plugin_manager.saveConfig()
-
-        return "ok"
-
-    def pluginAction(self, action, address, inner_path):
-        site = self.server.sites.get(address)
-        plugin_manager = PluginManager.plugin_manager
-
-        # Install/update path should exists
-        if action in ("add", "update", "add_request"):
-            if not site:
-                raise Exception("Site not found")
-
-            if not site.storage.isDir(inner_path):
-                raise Exception("Directory not found on the site")
-
-            try:
-                plugin_info = site.storage.loadJson(inner_path + "/plugin_info.json")
-                plugin_data = (plugin_info["rev"], plugin_info["description"], plugin_info["name"])
-            except Exception as err:
-                raise Exception("Invalid plugin_info.json: %s" % Debug.formatExceptionMessage(err))
-
-            source_path = site.storage.getPath(inner_path)
-
-        target_path = plugin_manager.path_installed_plugins + "/" + address + "/" + inner_path
-        plugin_config = plugin_manager.config.setdefault(site.address, {}).setdefault(inner_path, {})
-
-        # Make sure plugin (not)installed
-        if action in ("add", "add_request") and os.path.isdir(target_path):
-            raise Exception("Plugin already installed")
-
-        if action in ("update", "remove") and not os.path.isdir(target_path):
-            raise Exception("Plugin not installed")
-
-        # Do actions
-        if action == "add":
-            shutil.copytree(source_path, target_path)
-
-            plugin_config["date_added"] = int(time.time())
-            plugin_config["rev"] = plugin_info["rev"]
-            plugin_config["enabled"] = True
-
-        if action == "update":
-            shutil.rmtree(target_path)
-
-            shutil.copytree(source_path, target_path)
-
-            plugin_config["rev"] = plugin_info["rev"]
-            plugin_config["date_updated"] = time.time()
-
-        if action == "remove":
-            del plugin_manager.config[address][inner_path]
-            shutil.rmtree(target_path)
-
-    def doPluginAdd(self, to, inner_path, res):
-        if not res:
-            return None
-
-        self.pluginAction("add", self.site.address, inner_path)
-        PluginManager.plugin_manager.saveConfig()
-
-        self.cmd(
-            "confirm",
-            ["Plugin installed!<br>You have to restart the client to load the plugin", "Restart"],
-            lambda res: self.actionServerShutdown(to, restart=True)
-        )
-
-        self.response(to, "ok")
-
-    @flag.no_multiuser
-    def actionPluginAddRequest(self, to, inner_path):
-        self.pluginAction("add_request", self.site.address, inner_path)
-        plugin_info = self.site.storage.loadJson(inner_path + "/plugin_info.json")
-        warning = "<b>Warning!<br/>Plugins has the same permissions as the ZeroNet client.<br/>"
-        warning += "Do not install it if you don't trust the developer.</b>"
-
-        self.cmd(
-            "confirm",
-            ["Install new plugin: %s?<br>%s" % (plugin_info["name"], warning), "Trust & Install"],
-            lambda res: self.doPluginAdd(to, inner_path, res)
-        )
-
-    @flag.admin
-    @flag.no_multiuser
-    def actionPluginRemove(self, to, address, inner_path):
-        self.pluginAction("remove", address, inner_path)
-        PluginManager.plugin_manager.saveConfig()
-        return "ok"
-
-    @flag.admin
-    @flag.no_multiuser
-    def actionPluginUpdate(self, to, address, inner_path):
-        self.pluginAction("update", address, inner_path)
-        PluginManager.plugin_manager.saveConfig()
-        PluginManager.plugin_manager.plugins_updated["%s/%s" % (address, inner_path)] = True
-        return "ok"
diff --git a/plugins/UiPluginManager/__init__.py b/plugins/UiPluginManager/__init__.py
deleted file mode 100644
index d29ae44b..00000000
--- a/plugins/UiPluginManager/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import UiPluginManagerPlugin
diff --git a/plugins/UiPluginManager/media/css/PluginManager.css b/plugins/UiPluginManager/media/css/PluginManager.css
deleted file mode 100644
index 30f36717..00000000
--- a/plugins/UiPluginManager/media/css/PluginManager.css
+++ /dev/null
@@ -1,75 +0,0 @@
-body { background-color: #EDF2F5; font-family: Roboto, 'Segoe UI', Arial, 'Helvetica Neue'; margin: 0px; padding: 0px; backface-visibility: hidden; }
-h1, h2, h3, h4 { font-family: 'Roboto', Arial, sans-serif; font-weight: 200; font-size: 30px; margin: 0px; padding: 0px }
-h1 { background: linear-gradient(33deg,#af3bff,#0d99c9); color: white; padding: 16px 30px; }
-h2 { margin-top: 10px; }
-h3 { font-weight: normal }
-h4 { font-size: 19px; font-weight: lighter; margin-right: 100px; margin-top: 30px; }
-a { color: #9760F9 }
-a:hover { text-decoration: none }
-
-.link { background-color: transparent; outline: 5px solid transparent; transition: all 0.3s }
-.link:active { background-color: #EFEFEF; outline: 5px solid #EFEFEF; transition: none }
-
-.content { max-width: 800px; margin: auto; background-color: white; padding: 60px 20px; box-sizing: border-box; padding-bottom: 150px; }
-.section { margin: 0px 10%; }
-.plugins { font-size: 19px; margin-top: 25px; margin-bottom: 75px; }
-.plugin { transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1); position: relative; padding-bottom: 20px; padding-top: 10px; }
-.plugin.hidden { opacity: 0; height: 0px; padding: 0px; }
-.plugin .title { display: inline-block; line-height: 36px; }
-.plugin .title h3 { font-size: 20px; font-weight: lighter; margin-right: 100px; }
-.plugin .title .version { font-size: 70%; margin-left: 5px; }
-.plugin .title .version .version-latest { color: #2ecc71; font-weight: normal; }
-.plugin .title .version .version-missing { color: #ffa200; font-weight: normal; }
-.plugin .title .version .version-update { padding: 0px 15px; margin-left: 5px; line-height: 28px; }
-.plugin .description { font-size: 14px; color: #666; line-height: 24px; }
-.plugin .description .source { color: #999; font-size: 90%; }
-.plugin .description .source a { color: #666; }
-.plugin .value { display: inline-block;  white-space: nowrap; }
-.plugin .value-right { right: 0px; position: absolute; }
-.plugin .value-fullwidth { width: 100% }
-.plugin .marker {
-	font-weight: bold; text-decoration: none; font-size: 25px; position: absolute; padding: 2px 15px; line-height: 32px;
-	opacity: 0; pointer-events: none; transition: all 0.6s; transform: scale(2); color: #9760F9;
-}
-.plugin .marker.visible { opacity: 1; pointer-events: all; transform: scale(1); }
-.plugin .marker.changed { color: #2ecc71; }
-.plugin .marker.pending { color: #ffa200; }
-
-
-.input-text, .input-select { padding: 8px 18px; border: 1px solid #CCC; border-radius: 3px; font-size: 17px; box-sizing: border-box; }
-.input-text:focus, .input-select:focus { border: 1px solid #3396ff; outline: none; }
-.input-textarea {  overflow-x: auto; overflow-y: hidden; white-space: pre; line-height: 22px; }
-
-.input-select { width: initial; font-size: 14px; padding-right: 10px; padding-left: 10px; }
-
-.value-right .input-text { text-align: right; width: 100px; }
-.value-fullwidth .input-text { width: 100%; font-size: 14px; font-family: 'Segoe UI', Arial, 'Helvetica Neue'; }
-.value-fullwidth { margin-top: 10px; }
-
-/* Checkbox */
-.checkbox-skin { background-color: #CCC; width: 50px; height: 24px; border-radius: 15px; transition: all 0.3s ease-in-out; display: inline-block; }
-.checkbox-skin:before {
-	content: ""; position: relative; width: 20px; background-color: white; height: 20px; display: block; border-radius: 100%; margin-top: 2px; margin-left: 2px;
-	transition: all 0.5s cubic-bezier(0.785, 0.135, 0.15, 0.86);
-}
-.checkbox { font-size: 14px; font-weight: normal; display: inline-block; cursor: pointer; margin-top: 5px; }
-.checkbox .title { display: inline; line-height: 30px; vertical-align: 4px; margin-left: 11px }
-.checkbox.checked .checkbox-skin:before { margin-left: 27px; }
-.checkbox.checked .checkbox-skin { background-color: #2ECC71 }
-
-/* Bottom */
-
-.bottom {
-	width: 100%; text-align: center; background-color: #ffffffde; padding: 25px; bottom: -120px; -webkit-backdrop-filter: blur(5px); backdrop-filter: blur(5px);
-	transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1); position: fixed; backface-visibility: hidden; box-sizing: border-box;
-}
-.bottom-content { max-width: 750px; width: 100%; margin: 0px auto; }
-.bottom .button { float: right; }
-.bottom.visible { bottom: 0px; box-shadow: 0px 0px 35px #dcdcdc; }
-.bottom .title { padding: 10px 10px; color: #363636; float: left; text-transform: uppercase; letter-spacing: 1px; }
-.bottom .title:before { content: "•"; display: inline-block; color: #2ecc71; font-size: 31px; vertical-align: -7px; margin-right: 8px; line-height: 25px; }
-.bottom-restart .title:before { color: #ffa200; }
-
-.animate { transition: all 0.3s ease-out !important; }
-.animate-back { transition: all 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) !important; }
-.animate-inout { transition: all 0.6s cubic-bezier(0.77, 0, 0.175, 1) !important; }
\ No newline at end of file
diff --git a/plugins/UiPluginManager/media/css/all.css b/plugins/UiPluginManager/media/css/all.css
deleted file mode 100644
index ba72fa0d..00000000
--- a/plugins/UiPluginManager/media/css/all.css
+++ /dev/null
@@ -1,129 +0,0 @@
-
-/* ---- PluginManager.css ---- */
-
-
-body { background-color: #EDF2F5; font-family: Roboto, 'Segoe UI', Arial, 'Helvetica Neue'; margin: 0px; padding: 0px; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; -o-backface-visibility: hidden; -ms-backface-visibility: hidden; backface-visibility: hidden ; }
-h1, h2, h3, h4 { font-family: 'Roboto', Arial, sans-serif; font-weight: 200; font-size: 30px; margin: 0px; padding: 0px }
-h1 { background: -webkit-linear-gradient(33deg,#af3bff,#0d99c9);background: -moz-linear-gradient(33deg,#af3bff,#0d99c9);background: -o-linear-gradient(33deg,#af3bff,#0d99c9);background: -ms-linear-gradient(33deg,#af3bff,#0d99c9);background: linear-gradient(33deg,#af3bff,#0d99c9); color: white; padding: 16px 30px; }
-h2 { margin-top: 10px; }
-h3 { font-weight: normal }
-h4 { font-size: 19px; font-weight: lighter; margin-right: 100px; margin-top: 30px; }
-a { color: #9760F9 }
-a:hover { text-decoration: none }
-
-.link { background-color: transparent; outline: 5px solid transparent; -webkit-transition: all 0.3s ; -moz-transition: all 0.3s ; -o-transition: all 0.3s ; -ms-transition: all 0.3s ; transition: all 0.3s  }
-.link:active { background-color: #EFEFEF; outline: 5px solid #EFEFEF; -webkit-transition: none ; -moz-transition: none ; -o-transition: none ; -ms-transition: none ; transition: none  }
-
-.content { max-width: 800px; margin: auto; background-color: white; padding: 60px 20px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ; padding-bottom: 150px; }
-.section { margin: 0px 10%; }
-.plugins { font-size: 19px; margin-top: 25px; margin-bottom: 75px; }
-.plugin { -webkit-transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1); -moz-transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1); -o-transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1); -ms-transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1); transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1) ; position: relative; padding-bottom: 20px; padding-top: 10px; }
-.plugin.hidden { opacity: 0; height: 0px; padding: 0px; }
-.plugin .title { display: inline-block; line-height: 36px; }
-.plugin .title h3 { font-size: 20px; font-weight: lighter; margin-right: 100px; }
-.plugin .title .version { font-size: 70%; margin-left: 5px; }
-.plugin .title .version .version-latest { color: #2ecc71; font-weight: normal; }
-.plugin .title .version .version-missing { color: #ffa200; font-weight: normal; }
-.plugin .title .version .version-update { padding: 0px 15px; margin-left: 5px; line-height: 28px; }
-.plugin .description { font-size: 14px; color: #666; line-height: 24px; }
-.plugin .description .source { color: #999; font-size: 90%; }
-.plugin .description .source a { color: #666; }
-.plugin .value { display: inline-block;  white-space: nowrap; }
-.plugin .value-right { right: 0px; position: absolute; }
-.plugin .value-fullwidth { width: 100% }
-.plugin .marker {
-	font-weight: bold; text-decoration: none; font-size: 25px; position: absolute; padding: 2px 15px; line-height: 32px;
-	opacity: 0; pointer-events: none; -webkit-transition: all 0.6s; -moz-transition: all 0.6s; -o-transition: all 0.6s; -ms-transition: all 0.6s; transition: all 0.6s ; -webkit-transform: scale(2); -moz-transform: scale(2); -o-transform: scale(2); -ms-transform: scale(2); transform: scale(2) ; color: #9760F9;
-}
-.plugin .marker.visible { opacity: 1; pointer-events: all; -webkit-transform: scale(1); -moz-transform: scale(1); -o-transform: scale(1); -ms-transform: scale(1); transform: scale(1) ; }
-.plugin .marker.changed { color: #2ecc71; }
-.plugin .marker.pending { color: #ffa200; }
-
-
-.input-text, .input-select { padding: 8px 18px; border: 1px solid #CCC; -webkit-border-radius: 3px; -moz-border-radius: 3px; -o-border-radius: 3px; -ms-border-radius: 3px; border-radius: 3px ; font-size: 17px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ; }
-.input-text:focus, .input-select:focus { border: 1px solid #3396ff; outline: none; }
-.input-textarea {  overflow-x: auto; overflow-y: hidden; white-space: pre; line-height: 22px; }
-
-.input-select { width: initial; font-size: 14px; padding-right: 10px; padding-left: 10px; }
-
-.value-right .input-text { text-align: right; width: 100px; }
-.value-fullwidth .input-text { width: 100%; font-size: 14px; font-family: 'Segoe UI', Arial, 'Helvetica Neue'; }
-.value-fullwidth { margin-top: 10px; }
-
-/* Checkbox */
-.checkbox-skin { background-color: #CCC; width: 50px; height: 24px; -webkit-border-radius: 15px; -moz-border-radius: 15px; -o-border-radius: 15px; -ms-border-radius: 15px; border-radius: 15px ; -webkit-transition: all 0.3s ease-in-out; -moz-transition: all 0.3s ease-in-out; -o-transition: all 0.3s ease-in-out; -ms-transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out ; display: inline-block; }
-.checkbox-skin:before {
-	content: ""; position: relative; width: 20px; background-color: white; height: 20px; display: block; -webkit-border-radius: 100%; -moz-border-radius: 100%; -o-border-radius: 100%; -ms-border-radius: 100%; border-radius: 100% ; margin-top: 2px; margin-left: 2px;
-	-webkit-transition: all 0.5s cubic-bezier(0.785, 0.135, 0.15, 0.86); -moz-transition: all 0.5s cubic-bezier(0.785, 0.135, 0.15, 0.86); -o-transition: all 0.5s cubic-bezier(0.785, 0.135, 0.15, 0.86); -ms-transition: all 0.5s cubic-bezier(0.785, 0.135, 0.15, 0.86); transition: all 0.5s cubic-bezier(0.785, 0.135, 0.15, 0.86) ;
-}
-.checkbox { font-size: 14px; font-weight: normal; display: inline-block; cursor: pointer; margin-top: 5px; }
-.checkbox .title { display: inline; line-height: 30px; vertical-align: 4px; margin-left: 11px }
-.checkbox.checked .checkbox-skin:before { margin-left: 27px; }
-.checkbox.checked .checkbox-skin { background-color: #2ECC71 }
-
-/* Bottom */
-
-.bottom {
-	width: 100%; text-align: center; background-color: #ffffffde; padding: 25px; bottom: -120px; -webkit-backdrop-filter: blur(5px); backdrop-filter: blur(5px);
-	-webkit-transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1); -moz-transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1); -o-transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1); -ms-transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1); transition: all 0.8s cubic-bezier(0.86, 0, 0.07, 1) ; position: fixed; -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; -o-backface-visibility: hidden; -ms-backface-visibility: hidden; backface-visibility: hidden ; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ;
-}
-.bottom-content { max-width: 750px; width: 100%; margin: 0px auto; }
-.bottom .button { float: right; }
-.bottom.visible { bottom: 0px; -webkit-box-shadow: 0px 0px 35px #dcdcdc; -moz-box-shadow: 0px 0px 35px #dcdcdc; -o-box-shadow: 0px 0px 35px #dcdcdc; -ms-box-shadow: 0px 0px 35px #dcdcdc; box-shadow: 0px 0px 35px #dcdcdc ; }
-.bottom .title { padding: 10px 10px; color: #363636; float: left; text-transform: uppercase; letter-spacing: 1px; }
-.bottom .title:before { content: "•"; display: inline-block; color: #2ecc71; font-size: 31px; vertical-align: -7px; margin-right: 8px; line-height: 25px; }
-.bottom-restart .title:before { color: #ffa200; }
-
-.animate { -webkit-transition: all 0.3s ease-out !important; -moz-transition: all 0.3s ease-out !important; -o-transition: all 0.3s ease-out !important; -ms-transition: all 0.3s ease-out !important; transition: all 0.3s ease-out !important ; }
-.animate-back { -webkit-transition: all 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) !important; -moz-transition: all 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) !important; -o-transition: all 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) !important; -ms-transition: all 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) !important; transition: all 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) !important ; }
-.animate-inout { -webkit-transition: all 0.6s cubic-bezier(0.77, 0, 0.175, 1) !important; -moz-transition: all 0.6s cubic-bezier(0.77, 0, 0.175, 1) !important; -o-transition: all 0.6s cubic-bezier(0.77, 0, 0.175, 1) !important; -ms-transition: all 0.6s cubic-bezier(0.77, 0, 0.175, 1) !important; transition: all 0.6s cubic-bezier(0.77, 0, 0.175, 1) !important ; }
-
-/* ---- button.css ---- */
-
-
-/* Button */
-.button {
-	background-color: #FFDC00; color: black; padding: 10px 20px; display: inline-block; background-position: left center;
-	-webkit-border-radius: 2px; -moz-border-radius: 2px; -o-border-radius: 2px; -ms-border-radius: 2px; border-radius: 2px ; border-bottom: 2px solid #E8BE29; -webkit-transition: all 0.5s ease-out; -moz-transition: all 0.5s ease-out; -o-transition: all 0.5s ease-out; -ms-transition: all 0.5s ease-out; transition: all 0.5s ease-out ; text-decoration: none;
-}
-.button:hover { border-color: white; border-bottom: 2px solid #BD960C; -webkit-transition: none ; -moz-transition: none ; -o-transition: none ; -ms-transition: none ; transition: none  ; background-color: #FDEB07 }
-.button:active { position: relative; top: 1px }
-.button.loading {
-	color: rgba(0,0,0,0); background: #999 url(../img/loading.gif) no-repeat center center;
-	-webkit-transition: all 0.5s ease-out  ; -moz-transition: all 0.5s ease-out  ; -o-transition: all 0.5s ease-out  ; -ms-transition: all 0.5s ease-out  ; transition: all 0.5s ease-out   ; pointer-events: none; border-bottom: 2px solid #666
-}
-.button.disabled { color: #DDD; background-color: #999;  pointer-events: none; border-bottom: 2px solid #666 }
-
-/* ---- fonts.css ---- */
-
-
-/* Base64 encoder: http://www.motobit.com/util/base64-decoder-encoder.asp */
-/* Generated by Font Squirrel (http://www.fontsquirrel.com) on January 21, 2015 */
-
-
-@font-face {
-    font-family: 'Roboto';
-    font-style: normal;
-    font-weight: 400;
-    src:
-        local('Roboto'),
-        url(data:application/x-font-woff;charset=utf-8;base64,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) format('woff');
-}
-
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: bold;
-  src:
-  	local('Roboto Medium'),
-  	url(data:application/x-font-woff;charset=utf-8;base64,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) format('woff'),
-}
-
-@font-face {
-    font-family: 'Roboto';
-    font-style: normal;
-    font-weight: 200;
-    src:
-        local('Roboto Light'),
-        url(data:application/x-font-woff;charset=utf-8;base64,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) format('woff');
-}
\ No newline at end of file
diff --git a/plugins/UiPluginManager/media/css/button.css b/plugins/UiPluginManager/media/css/button.css
deleted file mode 100644
index 9f46d478..00000000
--- a/plugins/UiPluginManager/media/css/button.css
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Button */
-.button {
-	background-color: #FFDC00; color: black; padding: 10px 20px; display: inline-block; background-position: left center;
-	border-radius: 2px; border-bottom: 2px solid #E8BE29; transition: all 0.5s ease-out; text-decoration: none;
-}
-.button:hover { border-color: white; border-bottom: 2px solid #BD960C; transition: none ; background-color: #FDEB07 }
-.button:active { position: relative; top: 1px }
-.button.loading {
-	color: rgba(0,0,0,0); background: #999 url(../img/loading.gif) no-repeat center center;
-	transition: all 0.5s ease-out  ; pointer-events: none; border-bottom: 2px solid #666
-}
-.button.disabled { color: #DDD; background-color: #999;  pointer-events: none; border-bottom: 2px solid #666 }
\ No newline at end of file
diff --git a/plugins/UiPluginManager/media/css/fonts.css b/plugins/UiPluginManager/media/css/fonts.css
deleted file mode 100644
index f5576c5a..00000000
--- a/plugins/UiPluginManager/media/css/fonts.css
+++ /dev/null
@@ -1,30 +0,0 @@
-/* Base64 encoder: http://www.motobit.com/util/base64-decoder-encoder.asp */
-/* Generated by Font Squirrel (http://www.fontsquirrel.com) on January 21, 2015 */
-
-
-@font-face {
-    font-family: 'Roboto';
-    font-style: normal;
-    font-weight: 400;
-    src:
-        local('Roboto'),
-        url(data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAGfcABIAAAAAx5wAAQABAAAAAAAAAAAAAAAAAAAAAAAAAABHREVGAAABlAAAAEcAAABYB30Hd0dQT1MAAAHcAAAH8AAAFLywggk9R1NVQgAACcwAAACmAAABFMK7zVBPUy8yAAAKdAAAAFYAAABgoKexpmNtYXAAAArMAAADZAAABnjIFMucY3Z0IAAADjAAAABMAAAATCRBBuVmcGdtAAAOfAAAATsAAAG8Z/Rcq2dhc3AAAA+4AAAADAAAAAwACAATZ2x5ZgAAD8QAAE7fAACZfgdaOmpoZG14AABepAAAAJoAAAGo8AnZfGhlYWQAAF9AAAAANgAAADb4RqsOaGhlYQAAX3gAAAAgAAAAJAq6BzxobXR4AABfmAAAA4cAAAZwzpCM0GxvY2EAAGMgAAADKQAAAzowggjbbWF4cAAAZkwAAAAgAAAAIAPMAvluYW1lAABmbAAAAJkAAAEQEG8sqXBvc3QAAGcIAAAAEwAAACD/bQBkcHJlcAAAZxwAAAC9AAAA23Sgj+x4AQXBsQFBMQAFwHvRZg0bgEpnDXukA4AWYBvqv9O/E1RAUQ3NxcJSNM3A2lpsbcXBQZydxdVdPH3Fz1/RZSyZ5Ss9lqEL+AB4AWSOA4ydQRgAZ7a2bdu2bdu2bduI07hubF2s2gxqxbX+p7anzO5nIZCfkawkZ8/eA0dSfsa65QupPWf5rAU0Xzht5WI6kxMgihAy2GawQwY7BzkXzFq+mPLZJSAkO0NyVuEchXPXzjMfTU3eEJqGpv4IV0LrMD70DITBYWTcyh0Wh6LhdEgLR8O5UD3+U0wNP+I0/cv4OIvjvRlpHZ+SYvx/0uKd2YlP+t+TJHnBuWz/XPKmJP97x2f4U5MsTpC8+Efi6iSn46Qi58KVhP73kQ3kpgAlqEUd6lKP+jShKS1oSVva04FOdKYf/RnIMIYzgtGMZxLnucAlLnON69zkNne4yz3u84CHPOIxT3jKM17wkle85g0f+cwXvvKN3/whEjWYx7zms4CFLGIxS1jKMpazvBWsaCUrW8WqVrO6DW1vRzvb1e72so/97O8ABzrIwQ5xqMMd6WinOcNZrnCVq13jWte70e3udLd73edBD3nEox7zuCc8iZSIqiKjo9cExlKYbdEZclKIknQjRik9xkmSNHEc/9fY01Nr27Zt27Zt294HZ9u2bWttjGc1OHXc70Wt+tQb9fl2dkZmRuTUdBL5ExrDewn1Mq6YsX+YYkWOU23sksZYFqe7WqaGWapYtXfEp90vh3pH2dlViVSvy7kkRSnM9lH5BXZ8pBn+l7XcKrOvhzbaTm2xe8RZOy1uwak2imNvGn0TyD9qT5MvZ+9pMD2HUfsWy2QlhntyQyXYV+KW3CWVU/s0mJEba4Y9SZcv6HI3Xd6hy9t6yr6jYlfOOSpMVSlSVdVcC51jIVX5Df2ffCT5OLIN1FCt1JVZY9vnjME4TKBDgprStxk9W6ig0lXQmSfXWcC4CGv5vh4bsZn5LuzBf9g7VD4rKBcVbKBq+vPUmEod7Ig6WZo6owu6oR8GYIilaqglawT+w/xm3EruMWo8iW+p8x2+xw/4ET9hHzKom4ksnMN5XMBFXKJONnKQizz4YZbmCA5CEGqpThjCEYFIS3aiEG0DnRg74sQyxjHGMyYw+jjjIj8KojCKojhKojTKojwqojKqorE/z+nO2BO9MUb5nXGYgMn0nYrpmInZmIuF3GMLdtB7J713830v/mvJctXYflBTO6Vmlq4Wdljpdpj/4g/OOEzAPEt3FpBbhLV8X4+N2Mx8F/bgP5yLp9LTVMqgytdU+ZoqTzvjMAELmC/CZuzCHvyHffGqaZlqgmSkIBVpluk0xiRMwTTMwCzMYb20IuRTLDpZsjqjC7phAP6Dm/EI64/icTyBS+SykYNc5PEOfHCRHwVRGEVRHCVRGmVRHhVRGVU56yi/wiSFq6y261m9r1/kMOulwRqmUfQtyt3S1Rld0A0D8B/cjEvIRg5ykccb9cFFfhREYRRFcZREaZRFeVREZVTlbLT68emHkREchKA7eqI3a2Hy2Xq5eAxPgndPvgmSkYJUpLG/MSZhCqZhBmZhDuuuuqu0eqE3+tlqDbLd8jOarXYEByHojp7ojcG22xmK4RiJ0ZwJCe/NrRSxN/pFFVdhyb60bMuyzXbJXrNVlq04e8TuVVBhp0VYsn0S5P6T3nhKrpKCrp9qP1gan7daSjD1/znsjDdmSMpvWQGrZAMyL3Nbwu5Qonx2j70vH+MzZCqKrD1nhe0/ds522Xbzkdlnx6+5e0pgd7x9bdaW2Vv2qf9pyeb4M+x7xj6WpHz6u0gEYRevq7vQjvtftzNXs5aNxvqbsNS/XcmmBmHfev8pgvEFlML3OHh1nfG4nRVhaVc+EwL+XnZek0m3k3Y341tKUpLttxNy5dq9ircaImsp9rnt432+ZB+y70rwVqlsGd7sB2wQWbwvwo56K6fpefU+3n7Fw8teH3ZehL2hGwrLvrGddvL6ftLfzb23f0E3FHazgguvny2+Mj8XsJ721786zgWE/Q8XFfh3uJB8lq6AsA3IuDLbF7Dq7Q8i6907+Ky4q7133XyzN34gr4t9aU9fsz5QwUWIGiiCR4rlceTjCZHLE6oKqqIwVVd9RauxWpLroE4qoi48xdWdp4T6qL9KaiBPWQ3lKafhGqny2srzB6PljBAAAEbh9+U6QJyybXPPWLJt27bdmK8SLpPtsd/zr/dcdaRzuX3weR9dvqmfrnUrfz1hoBxMsVIeNjioHk+81YkvvurBH3/1Ekig+ggmWP2EEaYBIojQIFFEaYgYYjRMHHEaIYEEjZJEisZII03LZJChFbLI0iqFFGqNYoq1Timl2qCccm1SSaW2qKZa29RSqx3qqdcujTRqj2aatU8rvTpgiCEdMcKIjhljTCdMMKlTplnRuZAJ87LVl/yp7D78f4KMZCjjr5kYyEKmMvuoDGWu19rpAlV6GACA8Lf19Xp/uf89XyA0hH1uM0wcJ5HGydnNxdVdTm80YAKznTm4GLGJrPgTxr9+h9F3+Bf8L47foQzSeKRSixbJMnkSverlDibRndmS3FmD9KnKIK9EbXrWI4U55Fmc0KJ7qDDvBUtLii3rOU3W6ZVuuFpDd39TO7dYekVhRi/sUvGPVHbSys0Y+ggXFJDmjbSPzVqlk8bV2V3Ogl4QocQUrEM9VnQOGMJ49FMU79z28lXnNcZgFbzF8Yf+6UVu4TnPf8vZIrdP7kzqZCd6CF4sqUIvzys9f/cam9eY9oKFOpUzW5/Vkip1L9bg7BC6O6agQJOKr2BysQi7vSdc5EV5eAFNizNiBAEYhb/3T+ykje1U08RsYtu2c5X4Nrv3Wo+a54eAErb4Qg+nH08UUUfe4vJCE21Lk1tN9K0tLzbhbmyuNTECySQCj81jx+M8j0X+w+31KU1Z7Hp4Pn9gIItuFocAwyEPkIdk0SD3p4wyWpjhCAGiCFGAIUz7OghSo4I8/ehXf/pH5KlcFWpUE3nBr8/jPGIYi5GmJmjiGCsIMZcC7Q8igwAAeAE1xTcBwlAABuEvvYhI0cDGxJYxqHg2mNhZ6RawggOE0Ntf7iTpMlrJyDbZhKj9OjkLMWL/XNSPuX6BHoZxHMx43HJ3QrGJdaIjpNPspNOJn5pGDpMAAHgBhdIDsCRJFIXhcxpjm7U5tm3bCK5tKzS2bdu2bdszNbb5mHveZq1CeyO+/tu3u6oAhAN5dMugqYDQXERCAwF8hbqIojiAtOiMqViIRdiC3TiCW3iMRKZnRhZiEZZlB77Pz9mZXTiEwzmNS/mENpQ7VCW0O3Q+dNGjV8fr5T33YkwWk8t4Jr+pbhqaX8xMM98sNMvMerMpfyZrodEuo13TtGsxtmIPjuI2nsAyAzOxMIuyHDvyA34R7JrKJdoVG8rx9y54tb2u3jPvhclscpg82lXtz10zzGyzQLvWmY1Ju0D7yt5ACbsdb9ltADJJWkkpySUK2ASxNqtNZiOJrxPv2fHQJH6ScDphd8Lu64Out7oeujb62gR/pD/MH+oP8n/3v/PrAH56SeWH/dDlxSD+O+/IZzJU5v/LA/nX6PEr/N9cdP6e4ziBkziF0ziDbjiMa7iOG7iJW7iN7uiBO7iLe7iv7+6JXniIR3iMJ3iKZ+iNPkhAIixBMoS+6McwI4wyGZOjPw5xFAbgCAayMquwKquxOmtgEGuyFmuzDuuyHuuzAQZjCBuyERuzCZuyGZvrfw5jC7ZkK7ZmG7bFcIzg+/yAH/MTfsrPcBTHcBbPqauHXdmN7/I9fsiPOAYrORrrkQaa8FG4aSvBgJI2EBYjnSUiUwMHZJoslI9lUeCgLJYt8r1slV1yXHYHuskeOSLn5GjgsByT03JNzshZ6S7n5JLckctyRXqKLzflodwK9Jbb8lheyJNAH3kqryRBXssb6Ssx7jmG1cRAf7EA00sKyeDgkJoxMEoySSHJKYUdDFCLODiiFpWyUkrKORiolpcqUlmqOhikVpO6UlPqSX0Ag9UG0kwaSnNp4a54tpR27jHbSwcAw9WO8n7w2gfyYfD4I/lUPpbP5HMAR9UvpLN7zC4ORqpDHIxShzsYrU6VaQDGqEtkKYBx6pNAf4l1cFaNc/BcjRfr9oVySE6A76q5JDfAD9UqDiaoux1MVM87mKpedDAd8CAEOEitLXUADlC7Si+A3dVnov3sq76QGPffTGbJAmCOmkNyAZin5hEPwEI1v4MlajWpDmCp2tDBcvUXByvUGQ7HqDMdrFRny3wAq9QFDkerCx2sV5c52KCuEz2HjWqSTQA2A/kzOdj6B09lNjIAKgCdAIAAigB4ANQAZABOAFoAhwBgAFYANAI8ALwAxAAAABT+YAAUApsAIAMhAAsEOgAUBI0AEAWwABQGGAAVAaYAEQbAAA4AAAAAeAFdjgUOE0EUhmeoW0IUqc1UkZk0LsQqu8Wh3nm4W4wD4E7tLP9Gt9Eep4fAVvCR5+/LD6bOIzUwDucbcvn393hXdFKRmzc0uBLCfmyB39I4oMBPSI2IEn1E6v2RqZJYiMXZewvRF49u30O0HnivcX9BLQE2No89OzESbcr/Du8TndKI+phogFmQB3gSAAIflFpfNWLqvECkMTBDg1dWHm2L8lIKG7uBwc7KSyKN+G+Nnn/++HCoNqEQP6GRDAljg3YejBaLMKtKvFos8osq/c53/+YuZ/8X2n8XEKnbLn81CDqvqjLvF6qyKj2FZGmk1PmxsT2JkjTSCjVbI6NQ91xWOU3+SSzGZttmUXbXTbJPE7Nltcj+KeVR9eDik3uQ/a6Rh8gptD+5gl0xTp1Z+S2rR/YW6R+/xokBAAABAAIACAAC//8AD3gBjHoHeBPHFu45s0WSC15JlmWqLQtLdAOybEhPXqhphBvqvfSSZzqG0LvB2DTTYgyhpoFNAsumAgnYN/QW0et1ICHd6Y1ijd/MykZap3wvXzyjmS3zn39OnQUkGAogNJFUEEAGC8RAHIzXYhSr1dZejVFUCPBW1luL3sYGQIUOvVWSVn8XafBQH30AbADKQ300kQB7UpNCnSnUmfVuV1TMr1pMaCZW71Si7KoT82vrNi6X1SVYEa0ouNCPLqFJ8AFyIIN+T/dgzE0iUIokGJTUO69KpuBMMvmulUwJ9if980h/ILC56jecrksQA2l/AS6aDaI5OFmKat7bdan+r300lAkD0LoNugWfkJ7RNiFeTvHgv7fG/vdo5qh27UZl4kui486bLR98sO/99wOBPNFG3DKAyDiqC6qQppEoQRchTTUFVEFRzQH2NsFt90m8QUejsbgE6/BWmkLX4fd5vAECkwHEswxtfUiCghDaGAYwpgatwgYKG4TlUKoH9digHpejYQwHP0NtmJaogVAjkyoG1IZ8r3gbHWBia+bwxWhFrRPgrS2gmhU1Xr8rIaCCoibqM404fhfD7va77C725xP4n8/h1v/cApslQXqrW0G3H9DSgVJs2L2gO5q7L+9+4ssON+52W74RzR3oLVxHh+O6fBy8GDfTgfxvMd2YT4cTNw4GQBhT1Vq0yuuhOQwPSW9hYllqBE5hgxQuI0mxcHotihoT4K3CW82O9wQiilY3PEpR1KQAbz281Zreu8KESvd4PR5/ekam3+dISHC40z3uFNkRnyCyQbxscrj97LIvPsHXNkPoPXft+Y/2b31x2973c7Mnz1qAbbY/e/y91XvO7l6Zm1OIk/8zy/fo6S2vnom/es1ZcXLp69PHDJ86ZPLGEcWn7Pv3W788tLhwFkiQVfWtlCMdhFioBx5Ih3YwJSSrwMQTamR1s4Gbycq1JyqgRqVpVrEaNp/TEsMjt6I2DLD9Zj+0ZuHphorW5t5I87t1jfSnaZmCm//KTGvdxp6e4Wub4GCCulM8fqcupd+f7mEMYHpGsn4lOfIC50byojNra86C17bOnVeyqHfXTr16ru5J7t+K8rattJLPdO7Zq0unPtSURQ5niUU5JdvzOs3funWx6elhg3t0eXr48O6Vp3OKty3ulFO8dbH8zLAhPbo+M3TIc788JmY/BgIMq6oQf5EOQCPwgg8W/IUeNGCDBjWKn8gGiVwpUhpwpdCaWRrwTkhpxjulWQrvrKFJe+iWuqEuwVqXE9FA0ZLwHk+uJKuuWoy8sJpwojK5mnC6uFqYMIMphcnp9sqMusZS20w0ca0R4p2ZGRkhooa98Nqgxw5sKzzQZ+xIfPzxrdMD5YO6Hn7+PKV4cdU0usG1dW3KpEmPtx36ZPeBuDBLfWHS8k6vf7BzQe8Xuz9DZ87bVLXt9oTHOnz6xDgsTpw+b9Iy4fOBy//VutdD/6fPWEB4XnRBUPc5SsjjSNUeh4HlPibomIsvSivocvwEEBbQZuRFeSRYwQJqnTRV1DffZst0ykQwKfYEp8njJQum/jjXs3KvBZf2eMGzYGoFeeZT3IzPdZw2jqbTz3rQWfRmycDxXXfgcwAIHvbOzFrvxHhCTN4Mm92fTog3M8FmI5kv/DTfu24v6b1hsHf+D5NJh0/o8/T1LuMn4U+YlnwGs7BRt/FdaAkdCggNyCChh6RCHUgO7bvIdlfU9z1QlwWSRNXCektaIlsqNVNi7jnVKdlNguDFrvRMK2xlWRuFTVvRk4dm7Hl7pnCx75px2Ju+Mqbo3/Sn/phMv/w3R/40rBTTxXchGuoBe5kKuvuQMWxfurtzuKxuK3N2Vh/ZiIV0xB46Agv3CLE7aTqe2InFgNCQlmM6XAUzOPmbNPFeEOEvBc6yV3ct8XJuVn/xnSG0vHPO4q0rhh3jOFJJEokl74LAOGQ7p2GkY2ILk1iaiF+RpDWAsJzFsUlwmnFdP8SMiTFj0p2hFH4qk0crBw9Xy9tn339/dvtBrR95pHWrhx4CBFtVjqDokdAODFpkKGRPOt3o27WJDNw4U24JQGACs8IoZoWxbL32oRWj2M1R7Oaws+I2GKVoVjR4pkgpFOJOIYJfsfna2uxe3S5MVt2dZIpR5RVfXxfLv/u2XNg9v2DZPJK/OH+BQEbTvfQA+tH3Bz6K7ehZeij224sXyumlihvnbgJCCQC5LL0Hcg0uiUGR/pxsgMQNQkzThLB1E4FPspzCbZX8qT5yeQ9dTGwNxdP52w4DIPQDEH1Maic8BcaAa3i3MyLSBDRBcfKVFEWzhOcVHps0h1MJrefyY41fYDGmse5GEF2ir7Ij3hrXY9GERWt3o3D5eAVLa6aRqwtI69mbemSv3LDk6K3zuy7Si7QPIPSvqhBuM3SemogRywDF1qCrywZ1OTqI1f0apGkfA/bTNgGO19L4rwGA2WqsQdNj9cwNFM0TJsnuAf58XUVtEGCtlhS5oT4mhhKSosYZ8kgpJjcORUkupNeNuYtzCqumFOwOfnTqm+kjpuRUAR1Oq/YUzspdtn7VYqEtyc1GyB//5udX/jtAa+FRZx/4ovzdCYuW5MzOI0DADyB2Y7oaBXWgizEChN0ClxUtIseKzAGGhWJZDvIsRzPL0XpCqd/EwTvcukmjD11Wk5B77NieYBZZcjA4Fw8m4Ndr6A7sPlr4qbI9OdYEENYxG2jJUDSEQSEMyJZFhiFMPrcAVDQxzJ4pFjkiU5pWLzwpmeqxSc62NcB3ID4M1sSjN/MTduZvBEapzRFPWDT2+hKq2XSnmEynupJvgm+1GJl3+JtfrpT9at1pXT5p7qpN86d2aEOukAvb6YSH6e3rN2jwwoczZ6svrdzlbwIE5jP8DaRdEA8u5vPCKlxbAr7/GCkBVEvgiFQUrUGkHjjcsmi6Bxf8fgVSBWbcjholEJ5JuVQF8RMO7/vst1OnaSX2wn+dGbA56eWpMwtWSLs2iLduzKe/nrtBf8ZHg51wJRZLwXHZPR9/+9r7LxbuBmQWCGIqY1+GtkY7D28Fxy4pkQYO1QaO6OYeVEwNvvZf0qeyQrgkdb7zvpRYBCDAOMZLHd3KXdC8Zm8d7IUO9vawsnH98locnAsvsyUv9ovcUqGel+tWnFffWUukmagORUuJJCtkJKEsKyKTEHimpfOFes7ZNoPRVjFhcPaCqsCZ4NzsQeMqykq/W/PSnTWrcuatpt+MXrigfMEiMX10Ses2H0z+8PqNDybta9O6ZNT7ly5Vbpm2rujWsgKx3sKJY/Pzy5cAEBhaVSXc0uVsDL0hXO7USGlnAzuXUrBzO+FpBAj6L7tBRQ1OXY2u5RF4BqRLxLXB6lBAcvuZl0hlLt5fk00LD923ZeCsvcPHnsi7dJuq9M3G3s9/p9/329B449RpqwvInA7PzbiRt/KbGfRD+nUG7UWnSuvFL+9kP9f13Zt7175YBlVVkMsi4GjxcfCA7XdAE4tnfwgTQInwhIk8kLE7m7Ko3IPd6WX3fCJMQBmUGAAlIsvW7wSEzvCRME3sCjIkROgYu8r8up5LoeRAPzrQTLIrTzG3NT94AKevxGkHOL9FWCBcET4GAUyQCsxgWOKgkxhp3ZpYK6rzlEK4UrlPeIz/Ca22BEs3AyDkwgHhmvhEGIsenDkWKaBKHIuOxC/UD44UelaWkEUo7KO5K+mCUiDwRNVvwiS214nggmf/InYls0Ey3+v6UthY6itchUUF/jZ+QSh+seCVmXkvfmWEPL+Jpbzh8ngYaftUznNjsobP2E0+e/fDsy+P7lJWXS2vm7zouYUDRmdNHvXvlw8f37WzZNSzRfSj6vIZCIyg98sXpDXgh8fg/4LaNpSbmBlis14BBbS4tmYOMS5Nk8xx/JdZ0dqTsL0F1LaKVj88wUrWZgG1WZrmDs/FKdojJFJvmd/y6sqbmWHjEjkFmeclNnCliMQk20Q+cuoJPrHbbCxoizaU9dwl086ZkI/FXHpnrz9jcddlK+1xU/dnPTunW7p91fglsp3uptpReuTt6Jjl6D3d950HUh86mXWHFr0VE1OOM364jUN33P25zrO9HxjbGFu1e+SFtfj7z/SrbT3+9dXJ11BY3fzh4IUvr7+NC7DoMM37/RZdVdbCPcHb9gZuxfpox/d+uE770uXLioYPsOAfDb/nLDYAkBpKKpggCjrWzp5rHxfIbCBzdbCIRPdfkVqrRemToZIffehmvXAyuDH/EGmxjbQ8GHwKf7iFM+h8dujSjdQjxSBAMYCYp2fuCZAEPQzxsnb2BHqEdKZpceElzXE8ieKRSAkrIRpdjc/qCmccshvZkCUjrlRXKE66ivHadz9MHDopn35FD+ODuS/RT2kppsxas6SA3pTUA6XDNzR37Z5z4DopDv66eBqa1s0aNWU0AMJkFhEuSQcYhx2MftKY67ITkrgAd4A2g3OsGzliSRNXLtGdDFZ/OtcacLo9TF0Iq6ZteuJ7qT698T2l9OgKjNr5FSY6y+puLXz/9CFt8/YGeOrLu5iNGUuOY/prNPj5jvX0x7tLv6NfrXgbiM7yIcZyNDig/T9wzJmLCaNirMbW4lG0OVnkFk2ClXltVtoTbzG+tA8bb8JN9PKBs8fK//j6gqRuo8eO9jtFj71OJNvdxRhf1eMW2gkA6kg66kiehrBG/Sk/ixZlvq3RBqcoKoZsTdHMBhdpdTmq/4TrwXzyv8ohwqpgSzKZbAlWbpDUjbRF9fppbH0LPPIPuq5ZiBhW74j1ZeOK7ur1TgQ3lAq5wfvIEJITnMnXqgMI05h2XGPakQSD/7+04+/qIa1RKLo2Sns7rlFSI9Lv7YcbPcM6rWEEmlRZ5A7H61eA7ZLTTVwpRKjWHB46xGtd6R+qRivWEPRhwk1MSCrNoOVlh/H6/lEv++lOouwfkbUV04/Pxi444usL6KI/0arJv9FPWrfHTutD3Elmfe96GPfOUOYZFMqwqyrwqoGTusmC2VqaBftFbKheXXFKfaz1SeayYEppKSkvY9s3QFKDy0g215/3WDNZr0Yb/sORsf4uH04uLZVU/pSfVUAn2M84aGXMZ8PBm+Nj4KRIA+CpvzWUfvlCxacQXXb39OWfS/PnTV6Fknr39umK8iMzlxQuhGp+JJ2ficbMM1x411Y041kyEJ6FPmLtCn1hBEyDRbAOSmAPmPtp7YGRJUuEX7dnyB3lnvJweZKcKxfKr8vvypZ+DKtJJw99iG5SX2PkLfwq+BEZ8QV5bTeNZxS2JoHgzMqz1VbQgCGVoMk/WQFE6hfXdB+OIFrl0rINzJ6qJZa76967j5FXw9YYlMAQo8Mn1Xw5BFE/4A91URCqvizEx+SyoxvtrMcteA2v3S610ZRV1G0vZXvwH/FVFk4yydC7w8Si4KbgUY4trK0WeFLDKG5Axk0JA6mtPQbz1IgEOiq944qFnGYMqai7rIx8sl8cfHcjA7JWfB4ITKqqkCzM6q2QBO2N9baRiFglslASaxVK8aTantNDGYTDq5+JmHSTtmVKluX0lvoG/X0VWYnRb+zE6OX7A3vfPS2c3b3nhECKL9CybcXY/lTWGXxsezHdf56ggA767e8j79IbGBeE6qhQqlfLdnhKi4rXS5YonsBBmILahZMWLeCfXbMQjm0cPaeIeSFW37uro6zXhVmlpO4PGEf/+IMWY591r75aQNeT+4IsLv169NznG1bkz1svAIHRVVGSzPhzQApDZXY3DuVtat1qVFYGxGrYP45KMFv5fVZDVGXZXrKRU5NkSpX/jtdkRivmTkUxh57s3O0etyrjtvTkvndOC6dxIuf2LP2454mpv9ru8VtCy84j+8/J+b1Dr1fzuw1APKpbhxMGaVKifrwi8S8k/2B0hgpbU0JplmJIs6J1y+Aak2AMR9WkyyZ0uLGGd7KflpThp7+jZVUO9jwVHIPeguItRfQKeSr4lqRev5B3rG2wMIZ8s3rGwuUIgNCNxa1sfl7EUIO3CVvL4O6NH45UmR+ZsFarE0boqaeHb4+hHKzHP6ew1ljj8hKQbcSfvqFw7a9xu+ke0vOPG2i/Vvjt3LJta5dtWoMjTw6hFV8WUuaMPnql6OVCkt/p46I3bkw8MXX+mplj+0wfPv3VsbvOTzgye/7aGRde4FK1ARDX6HluK6M4RvplxRDyA9XE8gi6hrbYT1uKwyXbne8l20ZAWMKYKmHvtMEDmmSPZzIb3aDhBMoQa7Q6BnORwWRKAS9z36FzEKtYgrTqmu8HepPs27HllTcltTLlFL2jECSfCtcrPRt37tgoXAVAnr+LQf28o50GJl7vGBM8g9MzujZAQfdpqXqy7iPs69qZ4M2S4Oenq8Rdd7qF/OiDAPJ3uox9DG7B6EANphnOB2oUOo4N4nQfL0RxbyqHuli9YwQ4M9HHGjvH4TVxMPhZg6aY/DLWbZL0aRndtJOeczrp0Z10cykeL31TuFVpVg8IN+90E1PHjr17leFDaA8gntLj70gjBWE8tZ2w8UgcUOTx1ZILhfA6vAsiC7nVU/nyWrlY3i2zKQFkjt0iQwi7HnD1/31kPvb7lKbjxZt0HS36DC9R3w1hHmkVbBVMIe2CR0g5OcM5jWNI9zKkZmhjRBrGY0AaBhdajwdCHxmGM67QqFIadY2cJ1crxwZvkCRhBX9/TwBxmh77Hoe/Tz4ifYoI3NHwcwcpPGmRTGwyFPv9/AzCge2FR+9eExpV/iD8sWHDcnHexqV8vZX0CImW54AJUoAhVk2182YhUttZ+ORZM4nev58uxKnSV7enFJne5+9pwr41tKv51kDSIm2JPci1o4lKBqqSeptnMRZ6BHP0VVP1uzFNJZH4VTQm7HZ+hsKSCQtOo7llZfKcW52L5Dy+7iPkshCv25DXYENhVQ9oaOLGwheRuFOornBL9r2BzWdjs+3iXtqIXAw2BQSxKksoAgAB6ke8pnZCJfHznKLKUcLqNWuAa694Ca9IFARwg4q8yMV+9z5foRI6WXo7jiQRwpM9vvyVTZR+wh7zgB43K4RvxKehETSBqZqzaTO9WFbU5Opo42QgnIm19d9QYROnnnlF845HePZ4ZK1ti3ZWx50kw7GeOzKH93h5vsx9uu/edwv94MdpjXc69NM9dzI/2muiRM19a/NJxK/fnjh+SO6eCQcn7T0nemh0r/XuFfSNicndc99ZXLy3x6AJQzs9u6b33ldpnRd7K0v7di4/3GswEN33JssAdaAuDNVs9epzbDZFFQLAvFI4s0w0er1a5xiSWdCTzRjeqTG1S3SnMX1gJz8mnmNnJNusXi6dycrdtZh8s/TkOEvJ7nG46Mbulfnvdevx9oLVxHqLnl0xU4bgR4vpBRqUPjxVQluUnAKE/7C9qmB71RC6aEqjJLZ0xNFbYu3cBiIzGiYfP2SLZ60RHqfWV4dBBKu/mnG3R98AxjZ5aMhq805p0sEx/6N3J15e/e5P5p3mgqylL63LmdK337ah6EVI2vh73pUdWQuPl7r3HuMaNYCh/FEGiIN6jOHE+g04RYkhhuU0w6moIZE3opeEGJ1hveMM2//2s589neW2TsavmysRCf0DgkwrF2JAxf59Y3eXWMYe+uC73UW56rP/eiOviHhuY9o8kn4HJuZh+i3T+4GN+NPaMxx7P4b9F8awg3GcpZl1jjl7LPcKw0usbQD1zMDvq5f29v56H9cj/WodhigRH7tCd5qNOZiUAv57J9quhITQSSCmyCaX3+MhT12jFdP/N/fsN0G3+NaiwXm+8Xn08rgiG2lkzotH188pW4IF9BsafGrzwW6P9T4tHHtlVZ2lLwHCAwDkmOxg0gzR4hK4FUZI0ShSwRMjQ3Ft+TjfaEiPYyOdpWoPML3i5zzsJF7/1OA0hRSIfwD7cvv2PSWPPByV5u87+Msvhe0FY3fssxZasgZnF1T2AAIDaU/hZ8Z4XWgMOVpKqofzk8KTQzDAC9tfYmT9a+ODGjcV0hsup/b/uHsP8CiO5H24umdmV1mbFwSKC1qSESjawiByjiYbBJIJJgsRDrCQwRiTBAibIJJE8JGxEWPSioyJ4mxEOM5gnI/D2RecpW193T0rNL3Ahef7PekvPTubd7t7qqqr3nqrNtzJQjcRHlHt/DlmniIFYYp7RJjSfAG8O03jojC5SqsVq6yvz17MCdzz242Zn7bKmrV/cVHOmVPflK1bfOC5gXsXU/nyoqbLZ1d+euOfowfnrF6/LHM+SvzX0etb0Peb+D6+HED6xABgpnocZLHy82JKEFB4wevjd8LonbDacJ/tWUF6M5OaFMMiXa67PKRHnfIuoMGSB43PeX5JvMcjHS0i+d4U/KeZU7N6VzE2Bwa2DY9TznO+WhvVEBpGP5m55kjPrHtEHnANScigCDCMjr420OO5rOHxcjqKfqpNm+effRZw9WnSAw2l3xcCDmbDnHV4mMK4ffAE00tPsA6wo4aAwe/2BNWk6B1hU2ycO0VzgSUmgdogepD7rZNjktu0s6alpNKxpMrpld3IZcuagA795eMoulkGHxYgtg5yiAHouGbqgiymIqLWPxmDCeAYiz0d/FGYcgii/qDv6UchmIuGoFoQJk1zCstmeDyjUL/PyDB0+w76aQ5ZaICqkbPQaPKsdxkg2AyABhrAD82Keiyaxc6EAdgcCwAMs/nuMUuVuWUTNewJBk5Qt5p52+gdW82devROPe6lB/AEuMKvSgMEcL0O836czDik+iRVo2ewG644doXSlVnlXzyX+tYf0GiDZ0L+i0uCyx4c6eCR02cvf7t3FlnsbYrLZ0zPG+dNxBe+3VT1tZxeo0t0VmborwZbrOKsxIkIm/ijEQZzz5k1CNZrldNfrVArw9zLOrWS05ds1qsVHRRgGEa9jGQ6qnCoBx3UkPqRPg6rVR/D+2+AqlVwfuuKjDC6dMAYctQUQQ1Hji/hsPxPCj9C5jmfvXGP/FC2a/mKnXuWL92N3VvIMvI+CS2pXI4SqwIP3f3okvrRXeYBkSw5io8tAqaoVm1/tjL8RtBBXRQqrJzFPxxUQkRf6DE7tegLMVFnkiA6Q1Gfn72Q69kTmHvl3S88m5fsHtB/32vF2PwLuZHv/UW5O3s5uUt+l4/eWuutXHOT+xkkS/rBN4+Jop/xH3YOLuQWYfX9PY7/6G6kMXjxEXfj6wtncgKoQ1d2/itP8Ws7Bg/ZvqgEx1ejxq9M/j0ey7NRy6qAsltvYEvhnzXZxUV0BqHQWZXDWKZRB/gLg/XbEbj/jHURV7CPh8CX07e8TlzUpOWRdp5D0rBdqfWlNcZNXpDT818PA8R9tONyb47VBGpYjXC6BeKjKtWvIcCGUhxeUGtJQCPrm0pjK+hRbSCSXhvUcBD8Ga88l69xTyScSx7s6PPZgWP3y155Ycy0Cci+v/+XngWXcz1KwbTx81B0j/7PDpjR97Vjp9b0nDKkS4eObQbNGfz6geE7sjInD2RxXfW3eJDSFuwwUg1zOEVEo46ehFDnUU6NRqBjoZ8ksFAC9FNldBoLs2Nm5tnw027nYQvzfMxocXl5aruYp7t1mvvyhQtKW/J7oTe7XbuQdbZ1y/CWQmQABEvout+jJsJErRXFMESMTBiWuN3oCdka6Qo/xgdoyAbD0SAmkFRApUaTrr91GHku3+rsKZ0478oFfMbb6ecSyVp5EQBBLIBUJqc/HgMSRK7OIxiQImBAlF0ZcpLMXUFmn6yUMiovMiuIoCmAcpPeDIEsVQkN8/98Ub5FyX9y6AXBEt9ktKugYN84OAbEhmK1JsndKzzkwjryWzWsIxeP/blqbbXUqvKilFz1Jzm96rbUBBA0BpDK6diCob8wKB3qU+ffoz5BMoek+NUj6I6VbeSSxNAd9MvfPyAlaPLt33//C5pMSm7jA6jA+5X3I7SWTMQu7AQEDtJDKqWjCadeEZjM/iul8wCF08KcIwhjuq8nUwDTU20M2OV2pzgZhYCO4/uqi6TXmHuuTokjxsc1Ji+Xo3CpaWU0+acUuk7uOWaK3BwQDAGQ3qEjETGgOv8HGFA6nlO1Aw/0HpKSi4qWSHU3vMoxFPIGLjG0hjrQUrXWjeAzD02guqgjhkUbWRZLqo2iDPzDOQqckuxKSUxJSWURk5myRCiL3OLEsw++c+sWPvBO/PVdu6T3yRuJ909c+tfr/6w4+lnS9A7kb+VfDH3+/vvku/ZsBAcoJ6zjE5mqiPlQHdeuJf80nGKvttLxTvONV9HGyyCPOpQxH8y9WTMdr5mO11I7XsVi5uN1plKmchods4nGFQ6aEU+yx7Et3Wi9ajx8+Hr8QRXdunX4QGU7FHTvwYDnvrqKIjpMT/zMc+OH1/9VfuLzRPb9r6I35B+kOHBCe9XMcwNQ68g4OOZUGs4DfVuC3paF+9uyYCYizAI3x8wiG7l9djipsKTIPxxf2nX+nu5Neg/Ydqyg5/LStpE9R0qBJXdS1jSYOAJvfb/ttiA8YyRgKCDr0Vi5F48fEnXxA1QwaE1QaaHkBTNtYdCc1WVlrjqLG/bufljxgvdXfqv09EUNiNYwBFMmajzEwnMqxLnYnGu90Dr+wLGxQg99BHHow8ZsNzvWYUe1nj8AYtBqLzAVJwuvzRBQkO6jKQpiuLjK887l8oOedWcMGgiy6dU5Q1++EvHV13Go/j3XLRQZ+/knzlvraqAQBMMAZBZdxcJctb7/uB+B9qNtPK6LTlBHRtM8d2E0ylVPR6NM/WwE+iGr9gmo0NS9NJrRAR4/Q+S0GWONsYwml5bipluVJOzFlAqKzga0wR+hyl97NUrEATu2Bv50+dTHp+fljF8QiDLwlHsbhxUXB76aFfBRMZIvfX/r4MS5G/NJVTEApufmvjJM/gfUgyaQoeKmzbR9qdRdAeL+ZapgMS4WUECKRbn99i+30Z0WT7XEncZ9mDSnkXG/nEZkczgSOamZc6HkPluuX9uyaEHBuKmrF6wueff8lrULi6aMLVxYlTX9/Ofnc3MvTM09P33qwgVLFq/YXP7+m0VL1s2es37pxjevnt+yagnOy7v1Ut7NvJduzpl9i2lVNIBMkyXgqMkBOOiwHUISs76/vxhulZqqEOKgEz4Ubo224sxSKxM2elQtWEcPZvpoZEc1DNfKZQXH5Bnv317D/ef/KAmPRZM+JCPQ02Q+mk/mnyWLGPKMniEj7klheLu3Rf6OueQUaj93Rz6uYOdgNbVgvbgFM0IdZsOERJWqIKkp1TXqEDDXcHVZWRk1+c6qr6TL+GfA8Dwxy3OolCZDR5ivujp1phNiVT4ptYgoLw9iH+UI4NU8DpOaoaO5OzJ8MFkYFUgBcWnh4ky6FiY1rfbByLQW/CuYkPAqIiFC0AjezJGJT0l7yPFujqlM+JJ+cq0X6ZCjcEOKHWu3nVw+5DllnbqSqr9OvdK5oOzQ5iU7V14/cibzSPsuKPjjL5Hs2V2wctvTi1H0ntx072fP9+jbI/U1VL9Z7wEF6MDJgS2XjN596elnct/DC4pmZg0d36ZFzqacsiH04Z2XP38vf9P0Fzr1bde3a/Yr++rUs47p1Llv++fMtjGdhkxm52Gs/Hf8g3IBKMgHkYyhqauWYNlOo0nTAh7PaRhFw5obY33sxbe1a2UYJSxS69fUZwRBgmG0kutvynmuac/AWtWd3oqThZnMsWOqT+Oa05PVvEZaU+mdVO7DpzbXSLeHwqVoCWeqQc1TeeI+4RAEmYLoA2FBEi9ewkLg8/CeWo9n3UpTaXa8tuyrOdVgWX/6uD8sOvs+knZDm4Xy9i2U/NXAxSiPNJMeQxPpPsaCPPKtkuKTpzdt3f/GyGEjJk0aMTzTi7YiK2qLLFtLyHfbtpJvt0w/jnqg+aj78UPk8MUL5PARPHDDtptHppTe/OPaUQOX5eXOXjZgzML95MOdO1HD/XtR3K4d5N7ecvT8pUtkZ/kFsvv6NTSEawx+Rwrna9kQJqlh8W42szDGjRfp2aocb9fqOlguB8t2nujgV2zXt1OVrt3mzcHscU7JkPSJjhj9AtUkOlJZooOtjltbK5rm0LIcTJbxhBBDz/mzFuzaP2lupz7b9i99bWME+WPTIfWn9h+Kz8bFD5r7Ys7s5MWpSSEvLihcRM5n98trVG8lykgaQfnIY6FIGi29A/FQ+jsBI5SijtUEEMxDs6RTUgwoEMGzbaiCGjaRHcfcHU4YPlXmzZMy0CwUsA1keJ5K3n26WmEQBcnQGvaoqW24yqcyN4IdrfzoEhkgfhCZVagorFdbLBjDfXjKGVbjNMZaHJXJOFMclcmUmDhfHeHpFJR5CFJMKfTR6FqhbBSdwt9rKk2oKE1IYAWXrbEuVheFLM3GaLa1Mqgws8vJxcwbc9pd8cnueLc7SSuecT3vL27TqUBu3YZsxcXkWy6Q6MwKZNuwZ/5LyPx6mGSaXrq565Deo5fhO34yd4nJ5B4Ut38fimUy+RN5W+r3an5eu8SNrQfFmxp4zFnyfNw+tVtrAASzlVipPbfnZuDFJpLI6Zbae1NxuRJbCBgWSGfwXHpugsEBCeLys3LVkAQ1EAt8G2F1uOhxnXXWwEk2x4K1E8atXj1u/Lrq1O7dU9N69JDPjNu8afyEdescXZ5J79FnUnfAkA0g/ST/C4IhHDqzajQxog40Pa7OrTRU4HsoYQa2eQYr9RScKdbA8YK0pWgSWbOLzEOv7ELtqk5KHaRBReQFVFKEiitD17OVao834X3KcXDAADWAo8lQGyoJBC0b272wUEgV5tC0Xg2ofTyMV/LYHMyR5YuNauuoWImqLRzH4n3ePajZ5LbP9uhSvAsFbJw4oBQV4k2TUMTYTi1b93xm2pp5U8ZN7PM6IGiDC/FGpQziYaka424kjk8opWLjg7phWinVkRyYB4UgZaoZgHKPhEM0JICklVSxARtxLXk6rK6PyRxfq1E2XlOlRmqfV5eaID0VXdtSxaoqnxQ8rKpyu1DggO5dMzo/06P4zblLN3duv3bvkoU7S/p06Nxt8xB5TOsWT6UnNX4hb864tGF1GxdOyH954lPPPpuUy9m6efIHuH5NThrTnDRGmRrAcohNBWcyB1GiOWqJl1ayyP3ZT8mPaxVC7rL3b6TI3vdyOligrxoq8GN0MK4Ql3JgxOJPg5J15CdjqHZGzQ6O1mnJQo5Fov7oxRmX2pTtCszcu7ofBXS9i9/cvF6Kqbw4fXE30lS5Cwg6AEhtOeetqYqDQ8RM2iOUcwQBGunPTI0Oc1lizXjRgL+RX1DQ31AoDiC3/1z9e18209V4IpojdYNAcKiSj22IEw4G0HF/UO8eV9GaEsvVWoklvsNqLBMyqGDADNIL7QWWy26nKuEmcZ1MfqDtIavBZaDGE3GI4qDR9xWlSEMLYjURcGvuVhqKDNmwtdDYZ3DbF2KS672RnTsxOaFZk8BFjJ+Mt6MfeEVkWxUx1OiJhZE2sTAS+xdGst3GSAsj0Q/FH6BRFrwdD31m/kwATL9Dldw8TxRBv0XSsF2JuU+iiVOD6kmaF6OaJCEDL/mZucdWlxtfOrFx04nj5E+n3swe0H9kdv9+WVgeVfLu2Z3dt5w7t8Mwetr0Mb1HTZuSDXxfXS/Nlg5DPBwMBTDCQTQB2OMDAZTXlbfADReqP8Tr6bWK6kAAMsJlfBsATOLy8JqhvgDKFf4eFb6FAP7e23g9MsJFKYq/R+CA8ffkACjfKcf55xfx91yWGCRghEvQEm+qeU8sfU8sfw9g6EjmSbNpfF4H4mCwGqixIgNZ1QDLONa+nsXnYIrlSNZ/qs8pjaW7tz77FiYZjdqqJhk054ZV7/C4PoWJL+6JGmcdC8YzJo/O9+DPjp6/vXVye1+1Dt49Yd4fzo5qOHl67rBtf7ryzlsHcnu/gVpTr/epZjxj+E8A42DOwbbALJGB92TKuGo2gIbFPJH6rwaDr1ZAyNYL+5PFAL56WilWcrHtycovKFYyDq5aEe7903ufS1Olo95eNtzbe8yBz/5+AF2ORtlki1K6njQu8n6HZuOPAMFQeF/6SB4FwfA0r58PDJF8hQJBgdzrlqVAdoWCZJ+kKxWqUQ7iL9KwGitCaQg5ETIiNBR1J8dmoW6o2yxyDHWfRQ6Tw/ReX9QnjxzkB1Kah/qRAwASZRa/SSt1vgUnxEBjGKvKTZpyjWTeLjvGV4gFXOJKRpg4vuliVzxmq8cpJJECQbMB+yA13p+IzGgvafG8LoVnTIwOq2JzsiQFNirJbuSopSTvezV75apTjDd7e82LK7YsxVXNXsDJY3dSarJkf9r74bA5D/nJz216cAaN688YtPk7qo+Tu6N+XCEtyaEk2tAjr1YVtmU0Wgw7AeRMKjeh4GCSz30DrXmHyLUUfVQEwb4CX5N2y0TPlcAMEwmYsYlatMr8FqvZx51FWci5+t4s8usX5PuyMmRfuXUrrVUiH44/9/K5B+QSvdnB+3HR7LwixLKyNFM4wWCBJpRvEtu0mWhNo4TSSf9tJsjKkd8wxapl8PT1ojHacy7+HIONGokVEzUbv90Whe01VAdt62ehtuYgmFFHz7WyQxfm9zgx6OqRfofjm7ZcnDIxt/vJwQXjhtyVB1d8886W/KudkkauWtJzi9qs/qaYZiOeS85avazf0GsDRkwkH4IEvau/NcyVe9P5pUBruKhiHjkwB6B5BTs+8zieWSS9EynSDvzRMhzJXZwQxcmzjpR6E3IthHoWTpFvE8LZIBHai9P5VWk6fXH6tXS6F8YKmt8Q1YYV2iubVrB8ZoJgB1OpLioxboMujIuvjeOcnMVj11g8aRSTrg3qHJzQwwCK70nlknafr9h14ouPPpkybvzyY/88Pr00MePt8Te+9DYyvr12zZyEtiVVgV1LEv86c/kEqe/0tWYcsch2aNCIt4qK3x44MW9KP2vh4f79+wwm1V9NLz3dM3rJnHXdU7/DU/r3ypSS9xVEL1wNgOFlVlFuaAaR0JT6x8ZmT2k4fWmjCqh1PKP8ExvhdY2+6kczv6XG6RBHUZCQhULu+opcZzzD75gsUeROcnOszhf+S8m/zfxg0eJ7c6Zee+XNOS1W3O12ZuHRZ344cLLbOBxbMPz17bvm529Q7ORX8mJmiXfVK58uWv3Vgmnvrlgz6tVhLbekFrwyuupfT7fudnrX8vOfH2N2rQvsl5+Sy+itUHBCb9WoMeWNPPIwMsDXr80F6/EU4nN7Dhpq/Z+DppoHHdoNX5iFHvpe5oe35KeqIqS/ebdqzph2xEOOoXTulbVpU0V4C4yMDA2xeYmyAI5xNlk85WDJPAIolZkRZUeXyAbwYyS4dG1iXDLfeDm6K+vRXbVuvXDu4zPGZg1PgJtaMz8x3AJbNaNr8Nnc1JRheZ8VThnRbe7Yd+d+umrcoO5zR7/nyUaD23RdthuPHUz2p7Uv2EUJBN6CJmve20jOlJClrrVX16K0czn4SMzdw0dyvH3rfugBDGspl8D9GK5fiD+b8v+eQWB+hEHg5gwCT+65xxAIjFu95Qv9GQSRAAqrIrWCEybq0iiPlInYeBkwy6iYbPwW8538qJSlEu9dpXD43Vj7sJOTpUwcpA9nPa9qO0PQC0scJ5l9Aa+CFy1ixUH0iD86W/UC/ogy/laurAJWzCbDShRHPkZx3pXnAMEmxgGS0/04QHWewAEqK9MyshsB5AyekR0nit5/yXMqxbyrl4HW4hkoHnPacI2FFAn0tlrNDkhX1YsMPh+fn60kjdp0emJZ2TC04hPyLPryK/QeSZLTSSoq9/7Le5ONLw5Arsd37WFiPzIxB4xCuO+G+FlAQn2nREenr4LX+qHxtiMcrOK4e0O7wkswjSlpdGDjkZH8xgrU6LpLPQbkD/BeK8avN8lvgrf7xoSDDADB0F3XmSbqkd4gctC/GxM1SRW+Skbeni3Nzoga2gAmlZSUrVpVJo1pndfa68BvpuWl4c8BwXbSQ/4Hl8/nVYPN/vg6kUfdNosfY7BU1vvyamgYr8O3hPlS1ZzpyImOKSm+IjX5H/s2t04Na9h6iTeJFgS+R5nz3t1llo1hFV3kCZXraNHaenkcW5vXSQ/p73R3j4BsNZRp/39kX/HFs/h300J1tDBOTxwXuSU+9pjDqRsup5BxUlZa6Iyr7xzDuzbRUbvaL83JP9CPSvzGtyuuVv34x2OW4tBz+JeC+a9V3aKyj2Fc9TfGQN6pwgWvq6hBQ37iTKURFYLQ6Vbx39b6lYaJPgeEcX8sQbUJ7oXjSS0uQvTuNIs22IaK3eZkC7PlD8uTFY1kxDsaGQOrStVp28lyVEC2z90rdWYVy6x6uXJ57tjJk946h9+1r0Ph+1DKfmQustEi5mJvVb0weWX4/Wvk0s1v2O6UXf2tEei5i4FmkAzrVENKqi97G1/Bji2E3UkgRgikW73Pxs6lMYj7XC35VWnLBDVMbwx1THnVpr0ygl/xIEKfDCp96uGG5nDyY41b5eT+6qNMuIY+Byt7zocrl15p3e781GtfexONf1x0Ynb3pT8tfi+jzaVF98ivnq0FS7duW7Z4u/zUqHUOHLYUu7eSpTNHj51Ovpmx98KklxdOHT0qF7UggUc/+Mv7R+7cvv3msoj8dUzetwLgBQY7z3ZLPNst0kVFIRH0jhGkU2vI0XbzVlS6vdUAZ6Oko/Lbe07ZVwZ/VJnlY6ArFi6b0TBMhZhYvqNW/Lv+UIoWsSsJfkE7CFKmiElhhTUMiE1hVYxG6rKlJtH7DCZ305AsliW9PeQLclb68cePdhS0TnCUfImao9Gbyde79nwcXnXtpg0NRZ1mGhFG9dMjCkOHkMXk4IAL5PSREqR8GHf3r4Cq/0p64BN0raIgV7VFx9Ah6nIrUXrrJbr9IsGFdxYUM+BB+imynGN4BcvERAhpjFozkZrCiekP195oT8JZV3dvbJ0YFtWhXZd9+/CBba0GOOKf3SdflfZVkl1HLatDxw2X5cLZu07YVwe9+xIAZn0ClWJDGjihIfSnaSG3z5OLq/g3xbpqeKjMfWnOWg7VnwEmHHFPrtxlqcwkk+JwGvX1u2b5Vx4sk5/XIhYr/31TVuYu8ls2OnXtJC/iPX1Vi5F3ozbXRt9A7fZvMr66kLzTev/PMsLIUVPIG4FQDUu1TGZZbxedk1Wzg1ZmB0XNF9v3GGSrz06EVIhRJ5tTrD9r1TcVo8OfvKrpLHNFry3p0nbdtW7UF/2Y/MOza0XBrj0Fy3ZzB3RZwOj55KOkZXsc1AlFSZWUx/qhx3T47l3Q6igNkQYMEdBTDdHtPhY6VItQcVrfHxpGoRE+ox/AToxYEmtnI7ZRQ2vAj9RXTs/ecvAc+vFmN12N5Z+Dl66+cT3E+/IlUuWQxVJLzvlTwuVVUBeyVCOvN4InUBEFP+yRiNcewNfdzqBz1cDvaBxrsfUTA7YFGqC9DU5RwldvLZVryYAdO0bKqw6tlquO61mBr2JX10mAqg+RHmiMnA6h0EgE3gUfQ7BtSNA3NGbv+lbJTL26Usr95L2qplGrWX29/FfJYAAIgGSt5o86RjQtYIw2UkdSkVnAWbdUYbVrND+A6LVs4ska/gzvBEZDmhRrkmTYsG7thp+nyt8H7d0bgkxcHuQv8M9KNQRATG2G81A4ikb0s0FGfMUq6PIy/yvJLrmklCR0Zt1WkltZrAzcG0S+R5YgQPCKfBV/oPwFQiBeDeRWnoN24RLKVANrs5jcEaZKwNc95mHuBH+wg/y4s6hnt859lL/MWb1mduc+vbuwGgP5ezROOUdHV0fFgcxZ9KMI6GgBK3wsgME1lRMwRz6E3Ya+EAg2aKJKdp67krQeyJJvGdUMI8rkD/IA2FLD8OL0KoWPjuscds8dNjwv71geOdyhZYuOHVomtlfmD575h/0vvTQooWP7Fzp1ZquZSPqgN+BpMEFzlYJJvioVwYlTlYcw+5FwU7QpwSRlslQCjfn5Nu3rQIZeTs/t3SI5tPPzQ19clPfUsEFdI+Y0Gzdo6MantWzRHamN8iU4oQ2fCj9Dh8IDogMwnwzvH8wkPVxA+G2196h5dYpsNg7GRGGOO7TJG9742eym9Runz52T6Xo6Kym66TPKvUmLbG1CM1oaJy63pVs6PgUYRsgVUjOlmrNoWjHo4EkpK7br8CZZD6MhNkwjfdJYk8+SkiQXzrxG/rVn8oW765Rqch0lkOsckyET0Z+rD/N8bTKbb9tgkExSjNRCaispmVqnk7aBLQLbBvYNzAqUqeAGoky2y0kmXmbl1CVtKT+mxvd5eXT3Li9kdev5wuDkzi1auBom/rNzdlaXzpkjOrno3QaJyYC8I+Q7ZI1hBoTxWnYq0IAyueTQL2QamGDMMMqZdEoq0uisoeDTOncqk5w0Xzta7wzUo/OwHsa1G3v3QvKdDUpUb/eEFwe27htM5dz7NNlOrNV/gABfn1GjTsCVGgH3Pq1J+E+agLM8ynZcIK+Q4qAznLkDPd9ryx5bhQuUK9pjC2Hs2LZMXrLklmi2wQoBEKsGBAaJUVEUE8pAnz/EYgZO7EtORWETMqVj2QZr13mrl8wYexkQtJAdqIsBhM/R+3Iq8EaO+r6qBsOG8ZnSUZQtO7ouWLVqwehLgKABuY9awWEIgCjf5/yn5qwrxg+TPKPI/W7z3vjD6DHldJ7j5Jb4OJ1TPOwJYLmlPagDzy09KzvwIgPQx/eGsMf3ogxgUtSA3MSj4We+xi18NWSM6qhQa2B59Ls1qSqVmWXQjcMpDugjeizLJje7Lt3g+eOkm2359UQqtQiWYSeOk64yNJ1mnMN9FvFgUG2eUujtvCxn+LBpU0Zk5kjy4KmTMxsOnpIzBBBMgg04RjoMBparUqjpMyo1XYQZNsAaZUYhvILcQe4VOJ5MRwut6DWePVmPw7T3cbmVjMCtH1tTZGe87wfITe6sRJgQ6TDJs5I8tBIVAqJ6PEWaoMSBBIHsnfyr0tzI+eY4fGncFNYCmq1yKl6Fjys7JJqxA8CrwCpm3/iigY7P2ZhGS7E8i6LDUR8BKRrX5SBF4wQVdGxAAZuoASaYejfm5LDGvvq2I+H2aHuCXcrUUwnrspQNT+frmz+ywMnCgjaGWvpTPflFYGOxgNIZK9nJQamW8ynt3SlvLzY8pH0a0HCyR0b90e2ONdzPTvlL8o/WkD+P5i8BhbEmDam+/vEuiKfrclAH5osOmB97Uux7aQpx+lA1zls+FG6LtuFMNrEGCQzyrJPgk2ObgA1GV1AIlVc28+ax9RMoBkppRKz7vMyDoXCkp981ZhiMGu/k9T3uwIiHXVrtHI9DPjwuhV4YHscubpeSlBLbMMmNUlzK4E/o3zlylrxw5g79O4P6ocLTVdmoVfZdbPsTuUV6zpqFPx0n7V+/Zj1rpcwu9CaWvVVYrqpYs2bN+iNVD7Yw/d1FPVeJrlw0NILtqkuruncxzFqgn+oWsMb7iqJ3ovw5z2JNXpRJJECryqMBkxpr4x5EbIK+dD2qpre7QyTmIl+1i9NX7ULp0i6NOuVM4theTSdehdASGFcy6tZ57suFtgeXrnjQnPLvbIVl5ZUvnCkoWLyQRli6opijJ7H3qlJ65ggykN/JGyuK1q/EVB93V38bwHpHx0MqMKs3WB7Ir5+hh8Z81VzghqbQAlIgHY5C7cLU15ck+jeUEiIAsZ7GZqrHAV6ftDFpSq1gMifTuwLK6+Yy15TDeTame0zmGnEitiiciWyZKYbB+ETJpij28cmMpaY+E+Xrcun7TQMjbWshuSR+4QpLH7Wy57j0pcWyi9XldKY1ZAeU5HYb5cWo/6Sz09eWJXxF/jnjwBKycMWBmeTn+wlHXp9+ZgoatGTbF6hB2iHy0o408quUsaMZ+c0zNKRxdNVXgw2RjVDHTKfTKd1C90iD9efWkyj0ObvQm+wRdK+q/Bz7IzubqBcdzjNv4fr9cnKAVQ4CKCU8LqgHo3WC+m/rRQUoUs8NVsw1sAXoY3o1nPNgSsPZrkAFjFeKupluIoaU03QavaICiMsO7JY9Y3LISQ9a6kFtcl9EHrzjLTn97GnyJuo5bzaqGkmDj4sURD8+82V8wNv73HnOThrJ+xSfBxcsVu085hV1TjRNrkAH103BigcKVhxYJMy0N5wdmVWKpvY7Ojo6IVrK1FGvmH2P5lxJhx9BvxbWAslngSxQU0dv5ARxqR+ZLx/aMWOsbfbsX8kXBpX+BaHIf01YbJs85Y8HDWgeY4vjyHdvxG2NQg1RyNyl+ciAoqO3u66eyF8KMrPWygmqPXUhClzQCI6J3QXFPsfB+kSf2qAR4ghdgjq1AeWjQQNTg5gGUqau9Ri3G/TpSPZ0pCkyJpJNvfbp2ApmaqbGolw1JlasaYjhBObIGle6PifLN+BZkwZsTdkjFvYCvjkwqai10yncBNldTiM9GGKRm64UW69EFEs7dKIdZy7SP1z34Dep374r4XP3J5LlqKPsnYzXZnj3oqH7vZW4+4ASsps1FJNaFI0o+nHh1KLEZkU/o6PJI4qGovuDmMQ0AZB+pSsXAWPFDV/c0uoKeBtilkMbcqnkZxzYVK3cEoclCNB8oI936KKzMlIz62ItudxsN49Noz1S6EEq/7at+Urz9ZafP0TffeH9Hv2Wv9nuPdkcW1v8TB4kSMWKpd/MEvWQ93wIHp+PJg4vORVQAghiqr+XI+gcomCF2BBNBBmsZkUDr2lExXqmghNl6mdVt8LntDhZUwwtoeLXv9lewdQhlM/Qwowgm6cisBOiFLPWmZIF9AbOFGGpkBR6YVXwdqOdXsypFnOKHIFXkV8O9J30I/07U0n/Tl2RpNE3yKWdFvx8jpqzgV7QUFI9XZ2+gV68H2NkQoFDfN31v6HWygnDVahTV9Rz/9o+cTsVay2DuAUAgQkSwt02O/O5HGDmtUMsK2nALNywAHWrcfUDpHhwyWpP4RbskZDxE4+UG0tWkLtHL3+ClBhvMi6PJT99cPECikST464A5hoq8SqUaJgspiLEhKmB1yizNJwiCJzB15jhUHhQNKP06wZs48/a6bMmdmpDxF63gu+jteBjalTbDa6KHDx9jf7hul8jC/ntn9TE9iEH0fObtu8uJJQVTb5D1pKlxfjO91f//AAtRfFvLJ9XjADBblwgfSMxD7yeLk/pYBAc8mM1f8MovrigiHe6GYkGww8MydHFVJpjd6it3FfGmTVR1cMg5sL4rvhgn21dJ88b3nPYO6Ctp/Qe739SF15VA7RePwFs/v9THxSepXosG4WL0v/fDiksQ1u+b9+1k1P3Refnzhr/0Ue4W1kZ7ZQy/HB5682JEyeOKKximV7ez0X6is7HAcN1QGeUWOIu7l/iMC3+rXCNgoNsYCZJqyLXhuZ6iJxTprzUYm7Pyw8eePbtQ2cOjkFNPcoo242JdGx0qH9461jr3xsBINgir0TrDK0gAELoGLVTJgTiTSe2kjwDDK36j8pZsqDXW8AYpfTwg2QHA6ToyE8O/xaSsoIeoZKWYsZdFWmknESKoD0A3ifFPJ4b7vBPotgFbrjNHsa5kGG2x1PE2Zf+99zwxzLDq3/CG+no4iFXHJb46xoaJXwu6+Z1ZD6sgq0gZfozwMFYwwDHIgPcj/qtRsazLMz/CQMcXf03DHDM/HZ8XLI/8osajn/zixr4Mb+oEWzw/0UNKkSxbkQjDrMR9504sZgsNaA528jCT8yo6YI9e8ZiA3Gg2PqAoJBanmAp7om/dyMFexfiuczeSFAit8VTDNNA4h07pold/msgsgxjH+NIYw6DyHhXtSMZuA8eiSWfKWpr1nj6GdAHRgJj8AcIqGEo9QCMeiZVXaOelG90GUVk7+FJQgdP3pu2YHTXjqOyO3cdPTCpgYsDfIZpx/7SOXtEty7DKcaX2LJBfGJydXXNr/xgA5g5UtQQQP4r589Gwtj/7hdsrsmIcjrYYYuMcnXrxmpoQeh1pviltErr+8ycvuk3baDHiJ6s6ze1dpe2b9e1/u5C/nbl41/QV7c/RRF4YxGeV9sDHG8kErL8lsl6gJPo/7fmgoD+SawHU12YANTREvJtgv8hMpESmD8Wzg52E8dM7EIAjypUbKpp8xoioER1tJ6kYj8bzcDTABTPJQ+EdlF793pQXfkGuS80jZJvFBUV6bqihkNPHSfmkU6R4UGYh3JiX0fOgzIwT0To7FTh4wrxBU/hfaOlvQ9O377NmqeSZg+ktKorUloR6lhSQk4Aqv6R9vuYqrSFSJguNEvQ7eBibw8haEM+DF8FBWXqx2EWFi6A+0yKj3jH3F/0/zV2FeBx3Ep4dN7TnYOGMzc5s8PwHEOYmZMyM1zytYFXZmbm1hSnjD6XufUXfFRmZmau69snjeRZ7WkLHyS2/N9/o9nRrDSSZpRhYA6QvIA8IHW9uUA+/bQ3G8hrr+l8IA9fnerUwQ+25OqHL2bcdVUlhci4ULW0bxaBWWwMq4eYP9lvsl9UFKcMQB/JniA0jYZkfx+6ntBNsD2AeyA30eWEbofNbILFPcAx0Lyb0An4VXAXpHFnOz90lMj4KfFfSp9oY8vYdOsTA/gPaKzeJ65Qn4AIiGt1rFy0H52aJSsoiPYabD+WPef+LNqxTkBkmmgfqnQJ3WwGxMx7A6QdG30kOy8APcCHnkHoJrgiAJ3FTXSE0AnYJNAFaegcTzvuOwJ3KkozUsnu3kz8FMNKhrU0HQCh5Qb6SKgjNF2PSXKFdj8VaJRdo5vcaQHcUa7QLwn0PpEIoRPuGk92QvcRsseU7CprOlrOP7TldLMJtt615WCuc7TKWm3xK1ijRtNBimRZNBh9JHs3AF3uQzcSugk+D0JzE11J6Hb4mE2y0BWm3LyH0AlWIrgL0tA1Qi9jtF4w0zOO1vG6p8Np/JHPTMZQdht9JHuY0HSoIZnnQ9cTugk2BXAXcAPNuwmdgB+80UroIiF7hZYdsw2jNJO1NOcQP6VESPbV0mAe2XBKoGfrkfcigEbT4f7ksEwLrbkPDEAPN9EcNJpD0+EBWGYyf0HY9oRjYUf4sJtJigS0AEBBGnoM+6FjvNQJSbIHfaINfoS+1idGCC3W+z6xD34CPZho/FK075maJXO5iva52oNNRQ+GGUhRM/O1HjeTZuiAbjKOmrHRR7IdA9ClJpoDolGPewdgmcm8mZgTcBHpxkNXCd2M0v5LppQ6JCxHxwXIPutC1+dhJD6sJbkKINRgYI8scX2+S2K5wrpPC6zYl1dY9F3Vrs0cZQr9qEDPDm8idMLdWaAL0tB9GfkulUEQLWaFspj9HEuWPMWu8vqhvlfqpyOk871PJXpQZjD6SLZ3AHqwieaAaHw6hwZgfXJ8Qdj2Ax0LG/dhN5MUCbjGe5KErhAaGaE1glnKUO7ddC+3ktx07zaZg3Lb6CPZzoSmNVQy10RzQDT2cl+bGbVNzJuJOQGXeJITulBIXqYlxzxaKMteWpYSAJ/PIskJvVmjOSR2Ina8ByCxBYK91JyN8K9o/rIGtrIpkJtWlqHfG8bIDz9InmjN6ihizctOwzQWmSMDiLkFfmANFnN/H/MrihnR1wKzuIcLNFbqSi3FSl35UASHBGx10L4h6chXYkUe84lkmPPm7GfkxUpxik/X1co1bqPkx3oLIvoPATXgDUrxT+ib0Mhq7zjQrWerQl8bRY0vWd+LDgddspqtlyW/fk+EbsU85amlmKd8JDTAJX+Wmpz2Ant/GSp+GZqD+6JqJdAZcgr+RsLyoSKNYYZ5tHGUL315rZm46M/Tl6fposbLZl45MBKUzbzMU9A5Oq95pHp2UGJzT1/f6BTnrqvqi0V2UrNjHAVb2C4Q8+/3JOP6zY1ZxXHMzNXoWhozahVK7xDi3oW4m+CZIG5ucHNAbhztkwOYmclcRMyt7K4A5grHlLoLmRW6JEDqShYsdTN8xHa1uMv+QOrmlcxiLtfMWCMNZ9ZDNHMrm2nNkko0s9h7DA/nIaiGeYh+KuOFcK74ufMbmfIrHpdxCvGP/GntvU/H346H1na+Lf+EKcGWitbOp8Xf710a3ycu4vv7Suw7olX+s5e37uC/0bpjDVzGFkCuMRMnT0Jv+QdpRrBmT/JRdBkojljNHCkm5hZ4gs20mAf6mF9BZoU+F5jFXebjdoi7la0LWFvlOubcpAu5FXoSPntrboJVN29NLcXacSVwlOX99Gl0XzbgHOsKtDpsWaxDiFR0NeTLrtfH8xX5XvJeqjGX7g99Nefme+P9+p69jPpzNLzPOwxL0eENgdShmKO+CkbCcWCfEMFXruwErRrwLgIec46SkJ3DcvAE9DBxGXbY08OEMQ32upNjnk3vrFLIYv8N7yoeqU3rU7Wdxr43iX3Gh3PXM6+X+7+W+tGX0j7VpRPaP3Z4PXV69e4OK/u6zExvH9qgktsHrMeb4TY207KZbB48923+J0u3GBrTWIEPvcVw7eO22Z6I1pCYwR6ZFyoftxNY88caH/NoYm6B79mukOtn7ijXowKZcQwt1OhTaAwRd0eNRBN3EXG3spsCpK5xDKlxDC3U6Fqw5R7RK3ePK2sSKm4QfottTLVR3y8nlk1sOOzql1DPcihKgE9shNbrtzTKqdYMRVBwXh6ZLtCLNHoQmw6ZICYfHTHF6D4AEDouMooiFe3uJDbHioJEVJ/dZoHeN/yZWhsguhxCVp8jTKHvF+hT+G/EvcadQp7UO1MU1pI0CfTB4fuRW6ErgfvQhQb6C4GeGSkm7hZ3FZtpcUc0+jmBHhp+GbkVejmAxa3RUJjalR0T7lDcwGHDR5mCozu1lB2KT3Cxat0usbcJvjMjDsnRCoMC4kJ9tc08IN5evwpPimhZESs0EiTLhWIevQArfy3G9iXsW2yvExZ5WqROsI9ST5CdwOo0O11iTMY4sstbB6HxaO3XK7Rb675irSNytCy39rjhMPZytLbIK9AiLxSW2g9H41Ldno3tG2TtQhx5Y3S8rJqNtWKbUT0nktfnx2HccZlGF7KrfJYyGFeoJIusi4jc6jtX43fu0uPKPP3Igu1uN7arOopJLYvEv+h0QZY/FoPM0qru5CFABkTuHM4VP3fGo3KqIP65Nx4dHRWzhLujYsYwOjpVlI7ufDvK1t2/T/SI6MnRjHX3Ph19WwKWRuXkQX5iaXSfqJw8SIpvBJTmDWYfWtmjPZu1BG0clATY3thzP43lcRTxO5L9yOp9HpWi1rTGTuEaW6H3CPA2MU+fsgaj4kZ9PoN6u6DHlbn+FQu212K7kqWeZGlmeazBehMMNP0KB1rvNx/PLEnyKZogsQ7J/ZS7bzgPuNyxMSKC31BEcA18yqZBri8iqGc5tBJ/kFbtaw6m2RZt/QzSWGSOZBFzC8tn4y3mch/zK8iMaGHBzOKO+7gbiHsjWxUQx6yO/iBut5n8LvFvhE8CYgjlmT90DNafwCqGaB/1+omfErDzUOzZR+g5tI+dFRruB/C9uyR/lraPW3pcWSFRcaMdHIB2sLLHlfn0kQXb3Z+xXclST7I0QxtrsGQZpO3jACHLfzkgC9rHy8ySJIcpLNY8ROYG3csLWaNleUN1LzHrPvZyF41eTr3UqfclOtPkbiTuJrg6iJsb3ByQG2chewQwM82cWiwrNSKzij22AkiO1GxZFUBxYPte7i8S3+MSXun7SNTrPj0u4Wk8BkjeDHey8Zbkw/9A8ua1LF1yiu6OFZJcjU++UX/jwfiNmT2uzP0v2ndV7bAZ28eKnhIee3QJgMSnFoeuNfDHwtfYjvua+DwbteTtAZ6kv5IcKw58wY8F+lZ2Zfg8isyXU6y9HZ5kE6w4fr5jRrm+oIhY+56O9daLMTOK/xUxr4EuikARc0euHOfE/CAxr9mb/A1lz8uRWJJ5ADG3wNdeBIp2d/N9zK8gs0KfD8zijvm4LyXuNraQTbf2HvI5RdoUP9+D+NvgY+hrRf5ijvY39B119B0b2Szc37D2TjqKvO9w+oVd+o6N8A76NCtuiZfL8H5h6nis21kKK8E7GbZD0LqLMjYVysQsnU6uPHnjX4F15KbV7s3mPG1BZRX3PO/063uXUEvzzSqfZVe8N3HdvmrZtN9KZt1BFdGzj5wJdK7wT9ItxcUv8az05eMf3PrTacfFBn9WDta4yfHfwy5L61Da1dTsjOe8NeFNxv1UWgJenDjIV7bCdVVlURyjE/WscjOrT5/z074X1qBA77KHRleSz6XcNMmBTKFxzwu5Jys0XBa058WN+DEHih83VREzxY9jJjPvJuYEdJF9evOlLIfsU1XjxDfoFP22OJtkodUSzbCwbgO+W/bW6LKAmH0/fLdobv4LcbeyIwK4sx2Tuwu5FTozgDubGdyReuJuhptZg8U9kBvcHJAbvf90ZjHrp6NyAeKe96mqj6HtdpSI9kcx8xiO77M0+jhAbtPkk9O0RjBLXuQkgT5d6+9Tdoov6ie5R2huzOyE2j5XoxusnR16k2uLHUcWOys0IsBiY1HDYpF7D4Vm5wfMhQbY3LqXjwTMs/Jsbo0uDhoNJjfvJu4EzvEL0uQu9vaMNf9m4k/gfmSBT3YcEx2D/mCXeRb8GrCO6IPyW/s7An0B2GMuO9NbUU41VpTN7nz3VXtnyovk8hUoyVitm2tZvbUWztaSYDU1lGS5Rt9pr2goar5DapXcg6FzLDewkwF3clKr5K4G7Q7fAFsBtZJqdx5B/GRsv8l5BAD7H5Z1YrD/2B7ewT2AtPgwafFG5wE2x9JipqlFfgayKPQCyLK0mOXzieXE3Q4XsQmWT+znmE/oC/KJ7WWOD0saV5VCnTu4tI9yOBk6YkYO6T+vATQwJk/1yX9yM2I62U6W7xScw/tjGcj+HP+MlxW474Bf/7Qq7xW95UPrsL4XlmOozatlXnUv545HVSVRWVQ09SuLPPTo76t7i4o6z3WPwnKiA2RxUcbFObnfb9GVRdXc+r/YV4z8Qw1sZxtCc1kEZkKreyBEoXP0YB3BzwFwRuOzH4bPeLt7eupktKGlPhvawE7QNrTUZ0MbYBO235razZmD+KEaPwH6yEiowH+P+Pm6nQP8H+dLiG0AeAFVyIlBAzEUA1EjafSd9F8ApbIGcr3Zw/Ja6+t6vm/3rCXJZSo7SApPEpDdC7SinPG3dkFRYg6DhDaArzJJLFdQ1LOZGNtEcjIz2RQ2QAUqt626tEoiK/ZSR5J9xMzc9zDQItDftdSC+w9Alz7xTheekvJReeozPUxQQQjjcqJ/+cSLT+XVHgI57X3miegMwgkKrPUDInsISgAAAAEAAAACAADiktOWXw889QAbCAAAAAAAxPARLgAAAADQ206a+hv91QkwCHMAAAAJAAIAAAAAAAB4AWNgZGBgz/nHw8DA6flL+p8XpwFQBAUwzgEAcBwFBXgBjZQDsCXJEoa/qsrq897atu2xbdu2bXum79iztm3btm3bu72ZEbcjTow74o+vXZWZf2ZI6U3p4f4Ck9+V8/0S5ss3jJOpDI1vM0D+oI/rQz9/N3P84xwTRnKQLKCpW87BvgxH+wNZGhqzh74/SnWlqouqq6qMar1qtqqJariqt/ueue4GjpfdqS+9WSunMDc8RqPCqQyM5fXff3FFLMO4WI0rJFUN1utRTIw3c4U/mdtkIGWi6P2mXJH8rc9uVk1nbNwJ4xDd++VyH83lUU6Pp5HGfTmosD9VolBBnmVXeZK2/lCWh/ocp/x/aE/1cDbiJ+jzjvr9FFI5jc4yi25ShS7+MSrrve7Sn9T9QIn7IrtPdlH+wNmFwCIZqO8vpZPYdynd/C3Kw5Tn8H8ZwPzwPocngRPDbxwfnmAfZXt9p7r7ieuUe8YRzNLzRdJdc30pneLNytc51H3FCvmcjrq/vkkDOoUVrAgP0FeGMi1pqPevZLz/h5lSlx7+O2qqqvqZTJL5rA9fUMvvwwqt6Wi9PzFcpLqfvlrPNkkZmicVGKZ7qV2YmP0otelg+ZM7uVQeZFHyAE3leqbKMurpvzrJ2ayK6znY/ckGGcV6acYR/niOiIu4UJ8vK1xA/0Jteri/OT/O03zdkX0cp9JHlmssS0nlJ+b7kN0cHuaKUEIaBjLD8uivYYI/gTPCo0zyf9PVd2Qq/NPVffdP+VidC5NqLHXr6K46za3hKP8y/f1bVPYP6PmNLPR9GazqoLFV0hjLWu6SNhyaLOWy/43l8kIvKiQnkspUusU3OVSO4AQZzWGxPl1iM71ezuU+aJ2H6vkiKrt/OM9ylefS/hlWs0RrdK71hnk9dlGpZC6Yv/w52c/m2S1KfWweLpY/OXtffXy98gvVq7l/N5Z5t1jmXfPnFmWeVb8Wy/2ZPap1W618TnV37tWNZT4tlvnUZDHYvzemxWXrbZHau3F/ulm8to9t0frbemyL1BxZ/2m+btM4zlHeqjxb+bXyRc3nfu6H7C/llckabgtvUmJzwnxns8L6VZpygfpuhfIKZTujn8fZYnyGs20Ny8/GlIHZ3VYPy9PGtFlj/V7KVqXsZfPHZsA2aR6yOVHMR/i/1dvqsL20+WYzxjxidcvnnM2ajWk9bz1uMVh/599uzPxflkObszbr8vrnzzbhBRqTaTB75O/mNf4PGySVPAB4ATzBAxBbWQAAwNi2bfw4ebyr7UFt27ZtY1Dbtm3btu1Rd1ksVsN/J7O2sAF7GQdxTnIecBVcwG3NncBdzT3IfcT9ySvH68E7zCf8/vzbgv8ErQW3haWEtYUdhOOFm4QXRRnRJbFe3EV8RCKXVJQMljyXxqVlpL2lZ6QfZMVk/WTn5Q75YPltRTlFF8UmxSMlVk5Q7lF+UdlUGVUNVX/VLNU2dVo9QX1fU1SzRPNN20W7VftWR3VTdKv1Fn1T/XqD0dDDsNHoNHY0bjE+MeVNfU37TN/M2FzNPMl81SKztLBcs1LrHOt2WwPbeHvOPt++2n7CMcQxy3HJaXa2dD5w8VwVXT1dM1zn3Xx3ZXdtd1f3ePdSj8TT1rPcG/D28j7zLfEb/S38VwMgMC2wNsgOlg+OCF4NZUObw1XDg8KPI5UiW6KmaOvogei7mCtWItY+Ni52OPY9/n+8U3xN/H78NyNmtEyBqc30ZUYyU5mTzJuELBFOkESVxJVk1xQvpUqdSWfSqzMVMquyweyA7LMcPxfKTcjdy/3IB/Pd8g8LwQItzPt7GVCBbuAiNMLecBJcCvfAy/ANEiM9ciOAKqNmqD+ahlaiA+gm+oCl2IMhroJb4gF4Ol6FD+Nb+COREQ8BpCppRbqRQWQmWUMOkdvkI5VSD8W0Kv1TEDzACAEFAADNNWTbtvltZHPItm3btm3btn22hjPeGwbmgs3gJHgEfoIEmA9Whq1gJzgUzoab4ElUAB1CN9EHFI4ycQlcH3PcB4/HB/B1/BaH4HRSjNQlG2lJ2oBy2peOp8voXnqFvqbfaRzLy0qzRkyxAWwyW8UOsjPsOnvHfrEwlslL8Cq8ARe8Hx/GJ/Hl/A5/wb/waJFLFBLlRFNhRG8xTiwRu8Ul8VqEiHRZTFaS9SSTveU4uVTukZfkPflKfpNBMlUVVuVVbdVcEdVLDVIz1Xp1TN1Rn1WUzq0r6Ja6kz5tipo6hpheZoxZavaYy+aVCTQptpCtaaHtbkfZhXaHPW+f2f82xRV2tRxyPdxoN90tduvdbnfJvXQBLsmP8Qv9Wr/TH/UX/d0sCRMZsgAAAAABAAABnACPABYAVAAFAAEAAAAAAA4AAAIAAhQABgABeAFdjjN7AwAYhN/a3evuZTAlW2x7im3+/VyM5zPvgCtynHFyfsMJ97DOT3lUtcrP9vrne/kF3zyv80teca3zRxIUidGT7zGWxahQY0KbAkNSVORHNDTp8omRX/4lBok8VtRbZuaDLz9Hf+qMJX0s/ElmS/nVpC8raVpR1WNITdM2DfUqdBlRkf0RwIsdJyHi8j8rFnNKFSE1AAAAeAFjYGYAg/9ZDCkMWAAAKh8B0QB4AdvAo72BQZthEyMfkzbjJn5GILmd38pAVVqAgUObYTujh7WeogiQuZ0pwsNCA8xiDnI2URUDsVjifG20JUEsVjMdJUl+EIutMNbNSBrEYp9YHmOlDGJx1KUHWEqBWJwhrmZq4iAWV1mCt5ksiMXdnOIHUcdzc1NXsg2IxSsiyMvJBmLx2RipywiCHLNJgIsd6FgF19pMCZdNBkKMxZs2iACJABHGkk0NIKJAhLF0E78MUCxfhrEUAOkaMm8AAAA=) format('woff');
-}
-
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: bold;
-  src:
-  	local('Roboto Medium'),
-  	url(data:application/x-font-woff;charset=utf-8;base64,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) format('woff'),
-}
-
-@font-face {
-    font-family: 'Roboto';
-    font-style: normal;
-    font-weight: 200;
-    src:
-        local('Roboto Light'),
-        url(data:application/x-font-woff;charset=utf-8;base64,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) format('woff');
-}
\ No newline at end of file
diff --git a/plugins/UiPluginManager/media/img/loading.gif b/plugins/UiPluginManager/media/img/loading.gif
deleted file mode 100644
index 27d0aa8108b0800f9cddcf613f787347d9981e05..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 723
zcmZ?wbhEHb6ky<H_`<;O9}NB>0iXa9k4z>53@HBR_Hzvhc6JPKHPSO+W(0~W{*!Vp
zN=+<DO;IS%EXhzv%u1}t$xlqt%gjs5XHfjf!pRL(r2{en<VXfqT?K`{l+1Zc7H~Z}
z#k9^rpxNS#X~E^{d$)JY=VN~&*uLeF!wDX};&s=!T-Q!>!e)X>LZSp~!n_rkGVK%h
z9k_L9<(o^(d!N7A`+9eTzQ!EZMr*-N2_|eB&45;SC+a-zP~lXP;z?eTv`FKm^!Y8l
zuZ^S*OlLmOv^V<W32(v1vllP#5cZpSD3n`EWSZY00c=K@0*zY2;VKxy)ce>ZNyYQx
zE1Q1d^QD!~t!MErXFkzlm$bqCmuUZ)iN%&IQkAQ(b??%e8>EQMBqK<8T-y}!%q4L0
z4v$MoL7}cEx5PfOihDclHe=f1_`ny+jJ+qGonTF#=e6?cS1GK1Glv+XQW)E^VpGzx
z%$u!=(=#3~+Lk*jmQUf$-=^(}f)AMWru(Y&&oE(%*JUs>JH24vgCGuUPSS^%^#tgi
z6`S6zDw0tR+QR$5bp7w`G6mDQzjYm%RoE)?D^8cegv~i}{SvGJM6wyypd<nY06JwR
Aod5s;

diff --git a/plugins/UiPluginManager/media/js/PluginList.coffee b/plugins/UiPluginManager/media/js/PluginList.coffee
deleted file mode 100644
index 45e352f0..00000000
--- a/plugins/UiPluginManager/media/js/PluginList.coffee
+++ /dev/null
@@ -1,132 +0,0 @@
-class PluginList extends Class
-	constructor: (plugins) ->
-		@plugins = plugins
-
-	savePluginStatus: (plugin, is_enabled) =>
-		Page.cmd "pluginConfigSet", [plugin.source, plugin.inner_path, "enabled", is_enabled], (res) =>
-			if res == "ok"
-				Page.updatePlugins()
-			else
-				Page.cmd "wrapperNotification", ["error", res.error]
-
-		Page.projector.scheduleRender()
-
-	handleCheckboxChange: (e) =>
-		node = e.currentTarget
-		plugin = node["data-plugin"]
-		node.classList.toggle("checked")
-		value = node.classList.contains("checked")
-
-		@savePluginStatus(plugin, value)
-
-	handleResetClick: (e) =>
-		node = e.currentTarget
-		plugin = node["data-plugin"]
-
-		@savePluginStatus(plugin, null)
-
-	handleUpdateClick: (e) =>
-		node = e.currentTarget
-		plugin = node["data-plugin"]
-		node.classList.add("loading")
-
-		Page.cmd "pluginUpdate", [plugin.source, plugin.inner_path], (res) =>
-			if res == "ok"
-				Page.cmd "wrapperNotification", ["done", "Plugin #{plugin.name} updated to latest version"]
-				Page.updatePlugins()
-			else
-				Page.cmd "wrapperNotification", ["error", res.error]
-			node.classList.remove("loading")
-
-		return false
-
-	handleDeleteClick: (e) =>
-		node = e.currentTarget
-		plugin = node["data-plugin"]
-		if plugin.loaded
-			Page.cmd "wrapperNotification", ["info", "You can only delete plugin that are not currently active"]
-			return false
-
-		node.classList.add("loading")
-
-		Page.cmd "wrapperConfirm", ["Delete #{plugin.name} plugin?", "Delete"], (res) =>
-			if not res
-				node.classList.remove("loading")
-				return false
-
-			Page.cmd "pluginRemove", [plugin.source, plugin.inner_path], (res) =>
-				if res == "ok"
-					Page.cmd "wrapperNotification", ["done", "Plugin #{plugin.name} deleted"]
-					Page.updatePlugins()
-				else
-					Page.cmd "wrapperNotification", ["error", res.error]
-				node.classList.remove("loading")
-
-		return false
-
-	render: ->
-		h("div.plugins", @plugins.map (plugin) =>
-			if not plugin.info
-				return
-			descr = plugin.info.description
-			plugin.info.default ?= "enabled"
-			if plugin.info.default
-				descr += " (default: #{plugin.info.default})"
-
-			tag_version = ""
-			tag_source = ""
-			tag_delete = ""
-			if plugin.source != "builtin"
-				tag_update = ""
-				if plugin.site_info?.rev
-					if plugin.site_info.rev > plugin.info.rev
-						tag_update = h("a.version-update.button",
-							{href: "#Update+plugin", onclick: @handleUpdateClick, "data-plugin": plugin},
-							"Update to rev#{plugin.site_info.rev}"
-						)
-
-				else
-					tag_update = h("span.version-missing", "(unable to get latest vesion: update site missing)")
-
-				tag_version = h("span.version",[
-					"rev#{plugin.info.rev} ",
-					tag_update,
-				])
-
-				tag_source = h("div.source",[
-					"Source: ",
-					h("a", {"href": "/#{plugin.source}", "target": "_top"}, if plugin.site_title then plugin.site_title else plugin.source),
-					" /" + plugin.inner_path
-				])
-
-				tag_delete = h("a.delete", {"href": "#Delete+plugin", onclick: @handleDeleteClick, "data-plugin": plugin}, "Delete plugin")
-
-
-			enabled_default = plugin.info.default == "enabled"
-			if plugin.enabled != plugin.loaded or plugin.updated
-				marker_title = "Change pending"
-				is_pending = true
-			else
-				marker_title = "Changed from default status (click to reset to #{plugin.info.default})"
-				is_pending = false
-
-			is_changed = plugin.enabled != enabled_default and plugin.owner == "builtin"
-
-			h("div.plugin", {key: plugin.name}, [
-				h("div.title", [
-					h("h3", [plugin.name, tag_version]),
-					h("div.description", [descr, tag_source, tag_delete]),
-				])
-				h("div.value.value-right",
-					h("div.checkbox", {onclick: @handleCheckboxChange, "data-plugin": plugin, classes: {checked: plugin.enabled}}, h("div.checkbox-skin"))
-				h("a.marker", {
-					href: "#Reset", title: marker_title,
-					onclick: @handleResetClick, "data-plugin": plugin,
-					classes: {visible: is_pending or is_changed, pending: is_pending}
-				}, "\u2022")
-				)
-			])
-		)
-
-
-window.PluginList = PluginList
\ No newline at end of file
diff --git a/plugins/UiPluginManager/media/js/UiPluginManager.coffee b/plugins/UiPluginManager/media/js/UiPluginManager.coffee
deleted file mode 100644
index 6a0adee5..00000000
--- a/plugins/UiPluginManager/media/js/UiPluginManager.coffee
+++ /dev/null
@@ -1,71 +0,0 @@
-window.h = maquette.h
-
-class UiPluginManager extends ZeroFrame
-	init: ->
-		@plugin_list_builtin = new PluginList()
-		@plugin_list_custom = new PluginList()
-		@plugins_changed = null
-		@need_restart = null
-		@
-
-	onOpenWebsocket: =>
-		@cmd("wrapperSetTitle", "Plugin manager - ZeroNet")
-		@cmd "serverInfo", {}, (server_info) =>
-			@server_info = server_info
-		@updatePlugins()
-
-	updatePlugins: (cb) =>
-		@cmd "pluginList", [], (res) =>
-			@plugins_changed = (item for item in res.plugins when item.enabled != item.loaded or item.updated)
-
-			plugins_builtin = (item for item in res.plugins when item.source == "builtin")
-			@plugin_list_builtin.plugins = plugins_builtin.sort (a, b) ->
-				return a.name.localeCompare(b.name)
-
-			plugins_custom = (item for item in res.plugins when item.source != "builtin")
-			@plugin_list_custom.plugins = plugins_custom.sort (a, b) ->
-				return a.name.localeCompare(b.name)
-
-			@projector.scheduleRender()
-			cb?()
-
-	createProjector: =>
-		@projector = maquette.createProjector()
-		@projector.replace($("#content"), @render)
-		@projector.replace($("#bottom-restart"), @renderBottomRestart)
-
-	render: =>
-		if not @plugin_list_builtin.plugins
-			return h("div.content")
-
-		h("div.content", [
-			h("div.section", [
-				if @plugin_list_custom.plugins?.length
-					[
-						h("h2", "Installed third-party plugins"),
-						@plugin_list_custom.render()
-					]
-				h("h2", "Built-in plugins")
-				@plugin_list_builtin.render()
-			])
-		])
-
-	handleRestartClick: =>
-		@restart_loading = true
-		setTimeout ( =>
-			Page.cmd("serverShutdown", {restart: true})
-		), 300
-		Page.projector.scheduleRender()
-		return false
-
-	renderBottomRestart: =>
-		h("div.bottom.bottom-restart", {classes: {visible: @plugins_changed?.length}}, h("div.bottom-content", [
-			h("div.title", "Some plugins status has been changed"),
-			h("a.button.button-submit.button-restart",
-				{href: "#Restart", classes: {loading: @restart_loading}, onclick: @handleRestartClick},
-				"Restart ZeroNet client"
-			)
-		]))
-
-window.Page = new UiPluginManager()
-window.Page.createProjector()
diff --git a/plugins/UiPluginManager/media/js/all.js b/plugins/UiPluginManager/media/js/all.js
deleted file mode 100644
index 25632862..00000000
--- a/plugins/UiPluginManager/media/js/all.js
+++ /dev/null
@@ -1,1606 +0,0 @@
-
-/* ---- lib/Class.coffee ---- */
-
-
-(function() {
-  var Class,
-    slice = [].slice;
-
-  Class = (function() {
-    function Class() {}
-
-    Class.prototype.trace = true;
-
-    Class.prototype.log = function() {
-      var args;
-      args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
-      if (!this.trace) {
-        return;
-      }
-      if (typeof console === 'undefined') {
-        return;
-      }
-      args.unshift("[" + this.constructor.name + "]");
-      console.log.apply(console, args);
-      return this;
-    };
-
-    Class.prototype.logStart = function() {
-      var args, name;
-      name = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
-      if (!this.trace) {
-        return;
-      }
-      this.logtimers || (this.logtimers = {});
-      this.logtimers[name] = +(new Date);
-      if (args.length > 0) {
-        this.log.apply(this, ["" + name].concat(slice.call(args), ["(started)"]));
-      }
-      return this;
-    };
-
-    Class.prototype.logEnd = function() {
-      var args, ms, name;
-      name = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
-      ms = +(new Date) - this.logtimers[name];
-      this.log.apply(this, ["" + name].concat(slice.call(args), ["(Done in " + ms + "ms)"]));
-      return this;
-    };
-
-    return Class;
-
-  })();
-
-  window.Class = Class;
-
-}).call(this);
-
-
-/* ---- lib/Promise.coffee ---- */
-
-
-(function() {
-  var Promise,
-    slice = [].slice;
-
-  Promise = (function() {
-    Promise.when = function() {
-      var args, fn, i, len, num_uncompleted, promise, task, task_id, tasks;
-      tasks = 1 <= arguments.length ? slice.call(arguments, 0) : [];
-      num_uncompleted = tasks.length;
-      args = new Array(num_uncompleted);
-      promise = new Promise();
-      fn = function(task_id) {
-        return task.then(function() {
-          args[task_id] = Array.prototype.slice.call(arguments);
-          num_uncompleted--;
-          if (num_uncompleted === 0) {
-            return promise.complete.apply(promise, args);
-          }
-        });
-      };
-      for (task_id = i = 0, len = tasks.length; i < len; task_id = ++i) {
-        task = tasks[task_id];
-        fn(task_id);
-      }
-      return promise;
-    };
-
-    function Promise() {
-      this.resolved = false;
-      this.end_promise = null;
-      this.result = null;
-      this.callbacks = [];
-    }
-
-    Promise.prototype.resolve = function() {
-      var back, callback, i, len, ref;
-      if (this.resolved) {
-        return false;
-      }
-      this.resolved = true;
-      this.data = arguments;
-      if (!arguments.length) {
-        this.data = [true];
-      }
-      this.result = this.data[0];
-      ref = this.callbacks;
-      for (i = 0, len = ref.length; i < len; i++) {
-        callback = ref[i];
-        back = callback.apply(callback, this.data);
-      }
-      if (this.end_promise) {
-        return this.end_promise.resolve(back);
-      }
-    };
-
-    Promise.prototype.fail = function() {
-      return this.resolve(false);
-    };
-
-    Promise.prototype.then = function(callback) {
-      if (this.resolved === true) {
-        callback.apply(callback, this.data);
-        return;
-      }
-      this.callbacks.push(callback);
-      return this.end_promise = new Promise();
-    };
-
-    return Promise;
-
-  })();
-
-  window.Promise = Promise;
-
-
-  /*
-  s = Date.now()
-  log = (text) ->
-  	console.log Date.now()-s, Array.prototype.slice.call(arguments).join(", ")
-  
-  log "Started"
-  
-  cmd = (query) ->
-  	p = new Promise()
-  	setTimeout ( ->
-  		p.resolve query+" Result"
-  	), 100
-  	return p
-  
-  back = cmd("SELECT * FROM message").then (res) ->
-  	log res
-  	return "Return from query"
-  .then (res) ->
-  	log "Back then", res
-  
-  log "Query started", back
-   */
-
-}).call(this);
-
-
-/* ---- lib/Prototypes.coffee ---- */
-
-
-(function() {
-  String.prototype.startsWith = function(s) {
-    return this.slice(0, s.length) === s;
-  };
-
-  String.prototype.endsWith = function(s) {
-    return s === '' || this.slice(-s.length) === s;
-  };
-
-  String.prototype.repeat = function(count) {
-    return new Array(count + 1).join(this);
-  };
-
-  window.isEmpty = function(obj) {
-    var key;
-    for (key in obj) {
-      return false;
-    }
-    return true;
-  };
-
-}).call(this);
-
-
-/* ---- lib/maquette.js ---- */
-
-
-(function (root, factory) {
-    if (typeof define === 'function' && define.amd) {
-        // AMD. Register as an anonymous module.
-        define(['exports'], factory);
-    } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
-        // CommonJS
-        factory(exports);
-    } else {
-        // Browser globals
-        factory(root.maquette = {});
-    }
-}(this, function (exports) {
-    'use strict';
-    ;
-    ;
-    ;
-    ;
-    var NAMESPACE_W3 = 'http://www.w3.org/';
-    var NAMESPACE_SVG = NAMESPACE_W3 + '2000/svg';
-    var NAMESPACE_XLINK = NAMESPACE_W3 + '1999/xlink';
-    // Utilities
-    var emptyArray = [];
-    var extend = function (base, overrides) {
-        var result = {};
-        Object.keys(base).forEach(function (key) {
-            result[key] = base[key];
-        });
-        if (overrides) {
-            Object.keys(overrides).forEach(function (key) {
-                result[key] = overrides[key];
-            });
-        }
-        return result;
-    };
-    // Hyperscript helper functions
-    var same = function (vnode1, vnode2) {
-        if (vnode1.vnodeSelector !== vnode2.vnodeSelector) {
-            return false;
-        }
-        if (vnode1.properties && vnode2.properties) {
-            if (vnode1.properties.key !== vnode2.properties.key) {
-                return false;
-            }
-            return vnode1.properties.bind === vnode2.properties.bind;
-        }
-        return !vnode1.properties && !vnode2.properties;
-    };
-    var toTextVNode = function (data) {
-        return {
-            vnodeSelector: '',
-            properties: undefined,
-            children: undefined,
-            text: data.toString(),
-            domNode: null
-        };
-    };
-    var appendChildren = function (parentSelector, insertions, main) {
-        for (var i = 0; i < insertions.length; i++) {
-            var item = insertions[i];
-            if (Array.isArray(item)) {
-                appendChildren(parentSelector, item, main);
-            } else {
-                if (item !== null && item !== undefined) {
-                    if (!item.hasOwnProperty('vnodeSelector')) {
-                        item = toTextVNode(item);
-                    }
-                    main.push(item);
-                }
-            }
-        }
-    };
-    // Render helper functions
-    var missingTransition = function () {
-        throw new Error('Provide a transitions object to the projectionOptions to do animations');
-    };
-    var DEFAULT_PROJECTION_OPTIONS = {
-        namespace: undefined,
-        eventHandlerInterceptor: undefined,
-        styleApplyer: function (domNode, styleName, value) {
-            // Provides a hook to add vendor prefixes for browsers that still need it.
-            domNode.style[styleName] = value;
-        },
-        transitions: {
-            enter: missingTransition,
-            exit: missingTransition
-        }
-    };
-    var applyDefaultProjectionOptions = function (projectorOptions) {
-        return extend(DEFAULT_PROJECTION_OPTIONS, projectorOptions);
-    };
-    var checkStyleValue = function (styleValue) {
-        if (typeof styleValue !== 'string') {
-            throw new Error('Style values must be strings');
-        }
-    };
-    var setProperties = function (domNode, properties, projectionOptions) {
-        if (!properties) {
-            return;
-        }
-        var eventHandlerInterceptor = projectionOptions.eventHandlerInterceptor;
-        var propNames = Object.keys(properties);
-        var propCount = propNames.length;
-        for (var i = 0; i < propCount; i++) {
-            var propName = propNames[i];
-            /* tslint:disable:no-var-keyword: edge case */
-            var propValue = properties[propName];
-            /* tslint:enable:no-var-keyword */
-            if (propName === 'className') {
-                throw new Error('Property "className" is not supported, use "class".');
-            } else if (propName === 'class') {
-                if (domNode.className) {
-                    // May happen if classes is specified before class
-                    domNode.className += ' ' + propValue;
-                } else {
-                    domNode.className = propValue;
-                }
-            } else if (propName === 'classes') {
-                // object with string keys and boolean values
-                var classNames = Object.keys(propValue);
-                var classNameCount = classNames.length;
-                for (var j = 0; j < classNameCount; j++) {
-                    var className = classNames[j];
-                    if (propValue[className]) {
-                        domNode.classList.add(className);
-                    }
-                }
-            } else if (propName === 'styles') {
-                // object with string keys and string (!) values
-                var styleNames = Object.keys(propValue);
-                var styleCount = styleNames.length;
-                for (var j = 0; j < styleCount; j++) {
-                    var styleName = styleNames[j];
-                    var styleValue = propValue[styleName];
-                    if (styleValue) {
-                        checkStyleValue(styleValue);
-                        projectionOptions.styleApplyer(domNode, styleName, styleValue);
-                    }
-                }
-            } else if (propName === 'key') {
-                continue;
-            } else if (propValue === null || propValue === undefined) {
-                continue;
-            } else {
-                var type = typeof propValue;
-                if (type === 'function') {
-                    if (propName.lastIndexOf('on', 0) === 0) {
-                        if (eventHandlerInterceptor) {
-                            propValue = eventHandlerInterceptor(propName, propValue, domNode, properties);    // intercept eventhandlers
-                        }
-                        if (propName === 'oninput') {
-                            (function () {
-                                // record the evt.target.value, because IE and Edge sometimes do a requestAnimationFrame between changing value and running oninput
-                                var oldPropValue = propValue;
-                                propValue = function (evt) {
-                                    evt.target['oninput-value'] = evt.target.value;
-                                    // may be HTMLTextAreaElement as well
-                                    oldPropValue.apply(this, [evt]);
-                                };
-                            }());
-                        }
-                        domNode[propName] = propValue;
-                    }
-                } else if (type === 'string' && propName !== 'value' && propName !== 'innerHTML') {
-                    if (projectionOptions.namespace === NAMESPACE_SVG && propName === 'href') {
-                        domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);
-                    } else {
-                        domNode.setAttribute(propName, propValue);
-                    }
-                } else {
-                    domNode[propName] = propValue;
-                }
-            }
-        }
-    };
-    var updateProperties = function (domNode, previousProperties, properties, projectionOptions) {
-        if (!properties) {
-            return;
-        }
-        var propertiesUpdated = false;
-        var propNames = Object.keys(properties);
-        var propCount = propNames.length;
-        for (var i = 0; i < propCount; i++) {
-            var propName = propNames[i];
-            // assuming that properties will be nullified instead of missing is by design
-            var propValue = properties[propName];
-            var previousValue = previousProperties[propName];
-            if (propName === 'class') {
-                if (previousValue !== propValue) {
-                    throw new Error('"class" property may not be updated. Use the "classes" property for conditional css classes.');
-                }
-            } else if (propName === 'classes') {
-                var classList = domNode.classList;
-                var classNames = Object.keys(propValue);
-                var classNameCount = classNames.length;
-                for (var j = 0; j < classNameCount; j++) {
-                    var className = classNames[j];
-                    var on = !!propValue[className];
-                    var previousOn = !!previousValue[className];
-                    if (on === previousOn) {
-                        continue;
-                    }
-                    propertiesUpdated = true;
-                    if (on) {
-                        classList.add(className);
-                    } else {
-                        classList.remove(className);
-                    }
-                }
-            } else if (propName === 'styles') {
-                var styleNames = Object.keys(propValue);
-                var styleCount = styleNames.length;
-                for (var j = 0; j < styleCount; j++) {
-                    var styleName = styleNames[j];
-                    var newStyleValue = propValue[styleName];
-                    var oldStyleValue = previousValue[styleName];
-                    if (newStyleValue === oldStyleValue) {
-                        continue;
-                    }
-                    propertiesUpdated = true;
-                    if (newStyleValue) {
-                        checkStyleValue(newStyleValue);
-                        projectionOptions.styleApplyer(domNode, styleName, newStyleValue);
-                    } else {
-                        projectionOptions.styleApplyer(domNode, styleName, '');
-                    }
-                }
-            } else {
-                if (!propValue && typeof previousValue === 'string') {
-                    propValue = '';
-                }
-                if (propName === 'value') {
-                    if (domNode[propName] !== propValue && domNode['oninput-value'] !== propValue) {
-                        domNode[propName] = propValue;
-                        // Reset the value, even if the virtual DOM did not change
-                        domNode['oninput-value'] = undefined;
-                    }
-                    // else do not update the domNode, otherwise the cursor position would be changed
-                    if (propValue !== previousValue) {
-                        propertiesUpdated = true;
-                    }
-                } else if (propValue !== previousValue) {
-                    var type = typeof propValue;
-                    if (type === 'function') {
-                        throw new Error('Functions may not be updated on subsequent renders (property: ' + propName + '). Hint: declare event handler functions outside the render() function.');
-                    }
-                    if (type === 'string' && propName !== 'innerHTML') {
-                        if (projectionOptions.namespace === NAMESPACE_SVG && propName === 'href') {
-                            domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);
-                        } else {
-                            domNode.setAttribute(propName, propValue);
-                        }
-                    } else {
-                        if (domNode[propName] !== propValue) {
-                            domNode[propName] = propValue;
-                        }
-                    }
-                    propertiesUpdated = true;
-                }
-            }
-        }
-        return propertiesUpdated;
-    };
-    var findIndexOfChild = function (children, sameAs, start) {
-        if (sameAs.vnodeSelector !== '') {
-            // Never scan for text-nodes
-            for (var i = start; i < children.length; i++) {
-                if (same(children[i], sameAs)) {
-                    return i;
-                }
-            }
-        }
-        return -1;
-    };
-    var nodeAdded = function (vNode, transitions) {
-        if (vNode.properties) {
-            var enterAnimation = vNode.properties.enterAnimation;
-            if (enterAnimation) {
-                if (typeof enterAnimation === 'function') {
-                    enterAnimation(vNode.domNode, vNode.properties);
-                } else {
-                    transitions.enter(vNode.domNode, vNode.properties, enterAnimation);
-                }
-            }
-        }
-    };
-    var nodeToRemove = function (vNode, transitions) {
-        var domNode = vNode.domNode;
-        if (vNode.properties) {
-            var exitAnimation = vNode.properties.exitAnimation;
-            if (exitAnimation) {
-                domNode.style.pointerEvents = 'none';
-                var removeDomNode = function () {
-                    if (domNode.parentNode) {
-                        domNode.parentNode.removeChild(domNode);
-                    }
-                };
-                if (typeof exitAnimation === 'function') {
-                    exitAnimation(domNode, removeDomNode, vNode.properties);
-                    return;
-                } else {
-                    transitions.exit(vNode.domNode, vNode.properties, exitAnimation, removeDomNode);
-                    return;
-                }
-            }
-        }
-        if (domNode.parentNode) {
-            domNode.parentNode.removeChild(domNode);
-        }
-    };
-    var checkDistinguishable = function (childNodes, indexToCheck, parentVNode, operation) {
-        var childNode = childNodes[indexToCheck];
-        if (childNode.vnodeSelector === '') {
-            return;    // Text nodes need not be distinguishable
-        }
-        var properties = childNode.properties;
-        var key = properties ? properties.key === undefined ? properties.bind : properties.key : undefined;
-        if (!key) {
-            for (var i = 0; i < childNodes.length; i++) {
-                if (i !== indexToCheck) {
-                    var node = childNodes[i];
-                    if (same(node, childNode)) {
-                        if (operation === 'added') {
-                            throw new Error(parentVNode.vnodeSelector + ' had a ' + childNode.vnodeSelector + ' child ' + 'added, but there is now more than one. You must add unique key properties to make them distinguishable.');
-                        } else {
-                            throw new Error(parentVNode.vnodeSelector + ' had a ' + childNode.vnodeSelector + ' child ' + 'removed, but there were more than one. You must add unique key properties to make them distinguishable.');
-                        }
-                    }
-                }
-            }
-        }
-    };
-    var createDom;
-    var updateDom;
-    var updateChildren = function (vnode, domNode, oldChildren, newChildren, projectionOptions) {
-        if (oldChildren === newChildren) {
-            return false;
-        }
-        oldChildren = oldChildren || emptyArray;
-        newChildren = newChildren || emptyArray;
-        var oldChildrenLength = oldChildren.length;
-        var newChildrenLength = newChildren.length;
-        var transitions = projectionOptions.transitions;
-        var oldIndex = 0;
-        var newIndex = 0;
-        var i;
-        var textUpdated = false;
-        while (newIndex < newChildrenLength) {
-            var oldChild = oldIndex < oldChildrenLength ? oldChildren[oldIndex] : undefined;
-            var newChild = newChildren[newIndex];
-            if (oldChild !== undefined && same(oldChild, newChild)) {
-                textUpdated = updateDom(oldChild, newChild, projectionOptions) || textUpdated;
-                oldIndex++;
-            } else {
-                var findOldIndex = findIndexOfChild(oldChildren, newChild, oldIndex + 1);
-                if (findOldIndex >= 0) {
-                    // Remove preceding missing children
-                    for (i = oldIndex; i < findOldIndex; i++) {
-                        nodeToRemove(oldChildren[i], transitions);
-                        checkDistinguishable(oldChildren, i, vnode, 'removed');
-                    }
-                    textUpdated = updateDom(oldChildren[findOldIndex], newChild, projectionOptions) || textUpdated;
-                    oldIndex = findOldIndex + 1;
-                } else {
-                    // New child
-                    createDom(newChild, domNode, oldIndex < oldChildrenLength ? oldChildren[oldIndex].domNode : undefined, projectionOptions);
-                    nodeAdded(newChild, transitions);
-                    checkDistinguishable(newChildren, newIndex, vnode, 'added');
-                }
-            }
-            newIndex++;
-        }
-        if (oldChildrenLength > oldIndex) {
-            // Remove child fragments
-            for (i = oldIndex; i < oldChildrenLength; i++) {
-                nodeToRemove(oldChildren[i], transitions);
-                checkDistinguishable(oldChildren, i, vnode, 'removed');
-            }
-        }
-        return textUpdated;
-    };
-    var addChildren = function (domNode, children, projectionOptions) {
-        if (!children) {
-            return;
-        }
-        for (var i = 0; i < children.length; i++) {
-            createDom(children[i], domNode, undefined, projectionOptions);
-        }
-    };
-    var initPropertiesAndChildren = function (domNode, vnode, projectionOptions) {
-        addChildren(domNode, vnode.children, projectionOptions);
-        // children before properties, needed for value property of <select>.
-        if (vnode.text) {
-            domNode.textContent = vnode.text;
-        }
-        setProperties(domNode, vnode.properties, projectionOptions);
-        if (vnode.properties && vnode.properties.afterCreate) {
-            vnode.properties.afterCreate(domNode, projectionOptions, vnode.vnodeSelector, vnode.properties, vnode.children);
-        }
-    };
-    createDom = function (vnode, parentNode, insertBefore, projectionOptions) {
-        var domNode, i, c, start = 0, type, found;
-        var vnodeSelector = vnode.vnodeSelector;
-        if (vnodeSelector === '') {
-            domNode = vnode.domNode = document.createTextNode(vnode.text);
-            if (insertBefore !== undefined) {
-                parentNode.insertBefore(domNode, insertBefore);
-            } else {
-                parentNode.appendChild(domNode);
-            }
-        } else {
-            for (i = 0; i <= vnodeSelector.length; ++i) {
-                c = vnodeSelector.charAt(i);
-                if (i === vnodeSelector.length || c === '.' || c === '#') {
-                    type = vnodeSelector.charAt(start - 1);
-                    found = vnodeSelector.slice(start, i);
-                    if (type === '.') {
-                        domNode.classList.add(found);
-                    } else if (type === '#') {
-                        domNode.id = found;
-                    } else {
-                        if (found === 'svg') {
-                            projectionOptions = extend(projectionOptions, { namespace: NAMESPACE_SVG });
-                        }
-                        if (projectionOptions.namespace !== undefined) {
-                            domNode = vnode.domNode = document.createElementNS(projectionOptions.namespace, found);
-                        } else {
-                            domNode = vnode.domNode = document.createElement(found);
-                        }
-                        if (insertBefore !== undefined) {
-                            parentNode.insertBefore(domNode, insertBefore);
-                        } else {
-                            parentNode.appendChild(domNode);
-                        }
-                    }
-                    start = i + 1;
-                }
-            }
-            initPropertiesAndChildren(domNode, vnode, projectionOptions);
-        }
-    };
-    updateDom = function (previous, vnode, projectionOptions) {
-        var domNode = previous.domNode;
-        var textUpdated = false;
-        if (previous === vnode) {
-            return false;    // By contract, VNode objects may not be modified anymore after passing them to maquette
-        }
-        var updated = false;
-        if (vnode.vnodeSelector === '') {
-            if (vnode.text !== previous.text) {
-                var newVNode = document.createTextNode(vnode.text);
-                domNode.parentNode.replaceChild(newVNode, domNode);
-                vnode.domNode = newVNode;
-                textUpdated = true;
-                return textUpdated;
-            }
-        } else {
-            if (vnode.vnodeSelector.lastIndexOf('svg', 0) === 0) {
-                projectionOptions = extend(projectionOptions, { namespace: NAMESPACE_SVG });
-            }
-            if (previous.text !== vnode.text) {
-                updated = true;
-                if (vnode.text === undefined) {
-                    domNode.removeChild(domNode.firstChild);    // the only textnode presumably
-                } else {
-                    domNode.textContent = vnode.text;
-                }
-            }
-            updated = updateChildren(vnode, domNode, previous.children, vnode.children, projectionOptions) || updated;
-            updated = updateProperties(domNode, previous.properties, vnode.properties, projectionOptions) || updated;
-            if (vnode.properties && vnode.properties.afterUpdate) {
-                vnode.properties.afterUpdate(domNode, projectionOptions, vnode.vnodeSelector, vnode.properties, vnode.children);
-            }
-        }
-        if (updated && vnode.properties && vnode.properties.updateAnimation) {
-            vnode.properties.updateAnimation(domNode, vnode.properties, previous.properties);
-        }
-        vnode.domNode = previous.domNode;
-        return textUpdated;
-    };
-    var createProjection = function (vnode, projectionOptions) {
-        return {
-            update: function (updatedVnode) {
-                if (vnode.vnodeSelector !== updatedVnode.vnodeSelector) {
-                    throw new Error('The selector for the root VNode may not be changed. (consider using dom.merge and add one extra level to the virtual DOM)');
-                }
-                updateDom(vnode, updatedVnode, projectionOptions);
-                vnode = updatedVnode;
-            },
-            domNode: vnode.domNode
-        };
-    };
-    ;
-    // The other two parameters are not added here, because the Typescript compiler creates surrogate code for desctructuring 'children'.
-    exports.h = function (selector) {
-        var properties = arguments[1];
-        if (typeof selector !== 'string') {
-            throw new Error();
-        }
-        var childIndex = 1;
-        if (properties && !properties.hasOwnProperty('vnodeSelector') && !Array.isArray(properties) && typeof properties === 'object') {
-            childIndex = 2;
-        } else {
-            // Optional properties argument was omitted
-            properties = undefined;
-        }
-        var text = undefined;
-        var children = undefined;
-        var argsLength = arguments.length;
-        // Recognize a common special case where there is only a single text node
-        if (argsLength === childIndex + 1) {
-            var onlyChild = arguments[childIndex];
-            if (typeof onlyChild === 'string') {
-                text = onlyChild;
-            } else if (onlyChild !== undefined && onlyChild.length === 1 && typeof onlyChild[0] === 'string') {
-                text = onlyChild[0];
-            }
-        }
-        if (text === undefined) {
-            children = [];
-            for (; childIndex < arguments.length; childIndex++) {
-                var child = arguments[childIndex];
-                if (child === null || child === undefined) {
-                    continue;
-                } else if (Array.isArray(child)) {
-                    appendChildren(selector, child, children);
-                } else if (child.hasOwnProperty('vnodeSelector')) {
-                    children.push(child);
-                } else {
-                    children.push(toTextVNode(child));
-                }
-            }
-        }
-        return {
-            vnodeSelector: selector,
-            properties: properties,
-            children: children,
-            text: text === '' ? undefined : text,
-            domNode: null
-        };
-    };
-    /**
- * Contains simple low-level utility functions to manipulate the real DOM.
- */
-    exports.dom = {
-        /**
-     * Creates a real DOM tree from `vnode`. The [[Projection]] object returned will contain the resulting DOM Node in
-     * its [[Projection.domNode|domNode]] property.
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
-     * objects may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the projection.
-     * @returns The [[Projection]] which also contains the DOM Node that was created.
-     */
-        create: function (vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            createDom(vnode, document.createElement('div'), undefined, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        },
-        /**
-     * Appends a new childnode to the DOM which is generated from a [[VNode]].
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param parentNode - The parent node for the new childNode.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
-     * objects may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the [[Projection]].
-     * @returns The [[Projection]] that was created.
-     */
-        append: function (parentNode, vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            createDom(vnode, parentNode, undefined, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        },
-        /**
-     * Inserts a new DOM node which is generated from a [[VNode]].
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param beforeNode - The node that the DOM Node is inserted before.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function.
-     * NOTE: [[VNode]] objects may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the projection, see [[createProjector]].
-     * @returns The [[Projection]] that was created.
-     */
-        insertBefore: function (beforeNode, vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            createDom(vnode, beforeNode.parentNode, beforeNode, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        },
-        /**
-     * Merges a new DOM node which is generated from a [[VNode]] with an existing DOM Node.
-     * This means that the virtual DOM and the real DOM will have one overlapping element.
-     * Therefore the selector for the root [[VNode]] will be ignored, but its properties and children will be applied to the Element provided.
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param domNode - The existing element to adopt as the root of the new virtual DOM. Existing attributes and childnodes are preserved.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]] objects
-     * may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the projection, see [[createProjector]].
-     * @returns The [[Projection]] that was created.
-     */
-        merge: function (element, vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            vnode.domNode = element;
-            initPropertiesAndChildren(element, vnode, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        }
-    };
-    /**
- * Creates a [[CalculationCache]] object, useful for caching [[VNode]] trees.
- * In practice, caching of [[VNode]] trees is not needed, because achieving 60 frames per second is almost never a problem.
- * For more information, see [[CalculationCache]].
- *
- * @param <Result> The type of the value that is cached.
- */
-    exports.createCache = function () {
-        var cachedInputs = undefined;
-        var cachedOutcome = undefined;
-        var result = {
-            invalidate: function () {
-                cachedOutcome = undefined;
-                cachedInputs = undefined;
-            },
-            result: function (inputs, calculation) {
-                if (cachedInputs) {
-                    for (var i = 0; i < inputs.length; i++) {
-                        if (cachedInputs[i] !== inputs[i]) {
-                            cachedOutcome = undefined;
-                        }
-                    }
-                }
-                if (!cachedOutcome) {
-                    cachedOutcome = calculation();
-                    cachedInputs = inputs;
-                }
-                return cachedOutcome;
-            }
-        };
-        return result;
-    };
-    /**
- * Creates a {@link Mapping} instance that keeps an array of result objects synchronized with an array of source objects.
- * See {@link http://maquettejs.org/docs/arrays.html|Working with arrays}.
- *
- * @param <Source>       The type of source items. A database-record for instance.
- * @param <Target>       The type of target items. A [[Component]] for instance.
- * @param getSourceKey   `function(source)` that must return a key to identify each source object. The result must either be a string or a number.
- * @param createResult   `function(source, index)` that must create a new result object from a given source. This function is identical
- *                       to the `callback` argument in `Array.map(callback)`.
- * @param updateResult   `function(source, target, index)` that updates a result to an updated source.
- */
-    exports.createMapping = function (getSourceKey, createResult, updateResult) {
-        var keys = [];
-        var results = [];
-        return {
-            results: results,
-            map: function (newSources) {
-                var newKeys = newSources.map(getSourceKey);
-                var oldTargets = results.slice();
-                var oldIndex = 0;
-                for (var i = 0; i < newSources.length; i++) {
-                    var source = newSources[i];
-                    var sourceKey = newKeys[i];
-                    if (sourceKey === keys[oldIndex]) {
-                        results[i] = oldTargets[oldIndex];
-                        updateResult(source, oldTargets[oldIndex], i);
-                        oldIndex++;
-                    } else {
-                        var found = false;
-                        for (var j = 1; j < keys.length; j++) {
-                            var searchIndex = (oldIndex + j) % keys.length;
-                            if (keys[searchIndex] === sourceKey) {
-                                results[i] = oldTargets[searchIndex];
-                                updateResult(newSources[i], oldTargets[searchIndex], i);
-                                oldIndex = searchIndex + 1;
-                                found = true;
-                                break;
-                            }
-                        }
-                        if (!found) {
-                            results[i] = createResult(source, i);
-                        }
-                    }
-                }
-                results.length = newSources.length;
-                keys = newKeys;
-            }
-        };
-    };
-    /**
- * Creates a [[Projector]] instance using the provided projectionOptions.
- *
- * For more information, see [[Projector]].
- *
- * @param projectionOptions   Options that influence how the DOM is rendered and updated.
- */
-    exports.createProjector = function (projectorOptions) {
-        var projector;
-        var projectionOptions = applyDefaultProjectionOptions(projectorOptions);
-        projectionOptions.eventHandlerInterceptor = function (propertyName, eventHandler, domNode, properties) {
-            return function () {
-                // intercept function calls (event handlers) to do a render afterwards.
-                projector.scheduleRender();
-                return eventHandler.apply(properties.bind || this, arguments);
-            };
-        };
-        var renderCompleted = true;
-        var scheduled;
-        var stopped = false;
-        var projections = [];
-        var renderFunctions = [];
-        // matches the projections array
-        var doRender = function () {
-            scheduled = undefined;
-            if (!renderCompleted) {
-                return;    // The last render threw an error, it should be logged in the browser console.
-            }
-            renderCompleted = false;
-            for (var i = 0; i < projections.length; i++) {
-                var updatedVnode = renderFunctions[i]();
-                projections[i].update(updatedVnode);
-            }
-            renderCompleted = true;
-        };
-        projector = {
-            scheduleRender: function () {
-                if (!scheduled && !stopped) {
-                    scheduled = requestAnimationFrame(doRender);
-                }
-            },
-            stop: function () {
-                if (scheduled) {
-                    cancelAnimationFrame(scheduled);
-                    scheduled = undefined;
-                }
-                stopped = true;
-            },
-            resume: function () {
-                stopped = false;
-                renderCompleted = true;
-                projector.scheduleRender();
-            },
-            append: function (parentNode, renderMaquetteFunction) {
-                projections.push(exports.dom.append(parentNode, renderMaquetteFunction(), projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            insertBefore: function (beforeNode, renderMaquetteFunction) {
-                projections.push(exports.dom.insertBefore(beforeNode, renderMaquetteFunction(), projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            merge: function (domNode, renderMaquetteFunction) {
-                projections.push(exports.dom.merge(domNode, renderMaquetteFunction(), projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            replace: function (domNode, renderMaquetteFunction) {
-                var vnode = renderMaquetteFunction();
-                createDom(vnode, domNode.parentNode, domNode, projectionOptions);
-                domNode.parentNode.removeChild(domNode);
-                projections.push(createProjection(vnode, projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            detach: function (renderMaquetteFunction) {
-                for (var i = 0; i < renderFunctions.length; i++) {
-                    if (renderFunctions[i] === renderMaquetteFunction) {
-                        renderFunctions.splice(i, 1);
-                        return projections.splice(i, 1)[0];
-                    }
-                }
-                throw new Error('renderMaquetteFunction was not found');
-            }
-        };
-        return projector;
-    };
-}));
-
-
-/* ---- utils/Animation.coffee ---- */
-
-
-(function() {
-  var Animation;
-
-  Animation = (function() {
-    function Animation() {}
-
-    Animation.prototype.slideDown = function(elem, props) {
-      var cstyle, h, margin_bottom, margin_top, padding_bottom, padding_top, transition;
-      if (elem.offsetTop > 2000) {
-        return;
-      }
-      h = elem.offsetHeight;
-      cstyle = window.getComputedStyle(elem);
-      margin_top = cstyle.marginTop;
-      margin_bottom = cstyle.marginBottom;
-      padding_top = cstyle.paddingTop;
-      padding_bottom = cstyle.paddingBottom;
-      transition = cstyle.transition;
-      elem.style.boxSizing = "border-box";
-      elem.style.overflow = "hidden";
-      elem.style.transform = "scale(0.6)";
-      elem.style.opacity = "0";
-      elem.style.height = "0px";
-      elem.style.marginTop = "0px";
-      elem.style.marginBottom = "0px";
-      elem.style.paddingTop = "0px";
-      elem.style.paddingBottom = "0px";
-      elem.style.transition = "none";
-      setTimeout((function() {
-        elem.className += " animate-inout";
-        elem.style.height = h + "px";
-        elem.style.transform = "scale(1)";
-        elem.style.opacity = "1";
-        elem.style.marginTop = margin_top;
-        elem.style.marginBottom = margin_bottom;
-        elem.style.paddingTop = padding_top;
-        return elem.style.paddingBottom = padding_bottom;
-      }), 1);
-      return elem.addEventListener("transitionend", function() {
-        elem.classList.remove("animate-inout");
-        elem.style.transition = elem.style.transform = elem.style.opacity = elem.style.height = null;
-        elem.style.boxSizing = elem.style.marginTop = elem.style.marginBottom = null;
-        elem.style.paddingTop = elem.style.paddingBottom = elem.style.overflow = null;
-        return elem.removeEventListener("transitionend", arguments.callee, false);
-      });
-    };
-
-    Animation.prototype.slideUp = function(elem, remove_func, props) {
-      if (elem.offsetTop > 1000) {
-        return remove_func();
-      }
-      elem.className += " animate-back";
-      elem.style.boxSizing = "border-box";
-      elem.style.height = elem.offsetHeight + "px";
-      elem.style.overflow = "hidden";
-      elem.style.transform = "scale(1)";
-      elem.style.opacity = "1";
-      elem.style.pointerEvents = "none";
-      setTimeout((function() {
-        elem.style.height = "0px";
-        elem.style.marginTop = "0px";
-        elem.style.marginBottom = "0px";
-        elem.style.paddingTop = "0px";
-        elem.style.paddingBottom = "0px";
-        elem.style.transform = "scale(0.8)";
-        elem.style.borderTopWidth = "0px";
-        elem.style.borderBottomWidth = "0px";
-        return elem.style.opacity = "0";
-      }), 1);
-      return elem.addEventListener("transitionend", function(e) {
-        if (e.propertyName === "opacity" || e.elapsedTime >= 0.6) {
-          elem.removeEventListener("transitionend", arguments.callee, false);
-          return remove_func();
-        }
-      });
-    };
-
-    Animation.prototype.slideUpInout = function(elem, remove_func, props) {
-      elem.className += " animate-inout";
-      elem.style.boxSizing = "border-box";
-      elem.style.height = elem.offsetHeight + "px";
-      elem.style.overflow = "hidden";
-      elem.style.transform = "scale(1)";
-      elem.style.opacity = "1";
-      elem.style.pointerEvents = "none";
-      setTimeout((function() {
-        elem.style.height = "0px";
-        elem.style.marginTop = "0px";
-        elem.style.marginBottom = "0px";
-        elem.style.paddingTop = "0px";
-        elem.style.paddingBottom = "0px";
-        elem.style.transform = "scale(0.8)";
-        elem.style.borderTopWidth = "0px";
-        elem.style.borderBottomWidth = "0px";
-        return elem.style.opacity = "0";
-      }), 1);
-      return elem.addEventListener("transitionend", function(e) {
-        if (e.propertyName === "opacity" || e.elapsedTime >= 0.6) {
-          elem.removeEventListener("transitionend", arguments.callee, false);
-          return remove_func();
-        }
-      });
-    };
-
-    Animation.prototype.showRight = function(elem, props) {
-      elem.className += " animate";
-      elem.style.opacity = 0;
-      elem.style.transform = "TranslateX(-20px) Scale(1.01)";
-      setTimeout((function() {
-        elem.style.opacity = 1;
-        return elem.style.transform = "TranslateX(0px) Scale(1)";
-      }), 1);
-      return elem.addEventListener("transitionend", function() {
-        elem.classList.remove("animate");
-        return elem.style.transform = elem.style.opacity = null;
-      });
-    };
-
-    Animation.prototype.show = function(elem, props) {
-      var delay, ref;
-      delay = ((ref = arguments[arguments.length - 2]) != null ? ref.delay : void 0) * 1000 || 1;
-      elem.style.opacity = 0;
-      setTimeout((function() {
-        return elem.className += " animate";
-      }), 1);
-      setTimeout((function() {
-        return elem.style.opacity = 1;
-      }), delay);
-      return elem.addEventListener("transitionend", function() {
-        elem.classList.remove("animate");
-        elem.style.opacity = null;
-        return elem.removeEventListener("transitionend", arguments.callee, false);
-      });
-    };
-
-    Animation.prototype.hide = function(elem, remove_func, props) {
-      var delay, ref;
-      delay = ((ref = arguments[arguments.length - 2]) != null ? ref.delay : void 0) * 1000 || 1;
-      elem.className += " animate";
-      setTimeout((function() {
-        return elem.style.opacity = 0;
-      }), delay);
-      return elem.addEventListener("transitionend", function(e) {
-        if (e.propertyName === "opacity") {
-          return remove_func();
-        }
-      });
-    };
-
-    Animation.prototype.addVisibleClass = function(elem, props) {
-      return setTimeout(function() {
-        return elem.classList.add("visible");
-      });
-    };
-
-    return Animation;
-
-  })();
-
-  window.Animation = new Animation();
-
-}).call(this);
-
-
-/* ---- utils/Dollar.coffee ---- */
-
-
-(function() {
-  window.$ = function(selector) {
-    if (selector.startsWith("#")) {
-      return document.getElementById(selector.replace("#", ""));
-    }
-  };
-
-}).call(this);
-
-
-/* ---- utils/ZeroFrame.coffee ---- */
-
-
-(function() {
-  var ZeroFrame,
-    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
-    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
-    hasProp = {}.hasOwnProperty;
-
-  ZeroFrame = (function(superClass) {
-    extend(ZeroFrame, superClass);
-
-    function ZeroFrame(url) {
-      this.onCloseWebsocket = bind(this.onCloseWebsocket, this);
-      this.onOpenWebsocket = bind(this.onOpenWebsocket, this);
-      this.onRequest = bind(this.onRequest, this);
-      this.onMessage = bind(this.onMessage, this);
-      this.url = url;
-      this.waiting_cb = {};
-      this.wrapper_nonce = document.location.href.replace(/.*wrapper_nonce=([A-Za-z0-9]+).*/, "$1");
-      this.connect();
-      this.next_message_id = 1;
-      this.history_state = {};
-      this.init();
-    }
-
-    ZeroFrame.prototype.init = function() {
-      return this;
-    };
-
-    ZeroFrame.prototype.connect = function() {
-      this.target = window.parent;
-      window.addEventListener("message", this.onMessage, false);
-      this.cmd("innerReady");
-      window.addEventListener("beforeunload", (function(_this) {
-        return function(e) {
-          _this.log("save scrollTop", window.pageYOffset);
-          _this.history_state["scrollTop"] = window.pageYOffset;
-          return _this.cmd("wrapperReplaceState", [_this.history_state, null]);
-        };
-      })(this));
-      return this.cmd("wrapperGetState", [], (function(_this) {
-        return function(state) {
-          if (state != null) {
-            _this.history_state = state;
-          }
-          _this.log("restore scrollTop", state, window.pageYOffset);
-          if (window.pageYOffset === 0 && state) {
-            return window.scroll(window.pageXOffset, state.scrollTop);
-          }
-        };
-      })(this));
-    };
-
-    ZeroFrame.prototype.onMessage = function(e) {
-      var cmd, message;
-      message = e.data;
-      cmd = message.cmd;
-      if (cmd === "response") {
-        if (this.waiting_cb[message.to] != null) {
-          return this.waiting_cb[message.to](message.result);
-        } else {
-          return this.log("Websocket callback not found:", message);
-        }
-      } else if (cmd === "wrapperReady") {
-        return this.cmd("innerReady");
-      } else if (cmd === "ping") {
-        return this.response(message.id, "pong");
-      } else if (cmd === "wrapperOpenedWebsocket") {
-        return this.onOpenWebsocket();
-      } else if (cmd === "wrapperClosedWebsocket") {
-        return this.onCloseWebsocket();
-      } else {
-        return this.onRequest(cmd, message.params);
-      }
-    };
-
-    ZeroFrame.prototype.onRequest = function(cmd, message) {
-      return this.log("Unknown request", message);
-    };
-
-    ZeroFrame.prototype.response = function(to, result) {
-      return this.send({
-        "cmd": "response",
-        "to": to,
-        "result": result
-      });
-    };
-
-    ZeroFrame.prototype.cmd = function(cmd, params, cb) {
-      if (params == null) {
-        params = {};
-      }
-      if (cb == null) {
-        cb = null;
-      }
-      return this.send({
-        "cmd": cmd,
-        "params": params
-      }, cb);
-    };
-
-    ZeroFrame.prototype.send = function(message, cb) {
-      if (cb == null) {
-        cb = null;
-      }
-      message.wrapper_nonce = this.wrapper_nonce;
-      message.id = this.next_message_id;
-      this.next_message_id += 1;
-      this.target.postMessage(message, "*");
-      if (cb) {
-        return this.waiting_cb[message.id] = cb;
-      }
-    };
-
-    ZeroFrame.prototype.onOpenWebsocket = function() {
-      return this.log("Websocket open");
-    };
-
-    ZeroFrame.prototype.onCloseWebsocket = function() {
-      return this.log("Websocket close");
-    };
-
-    return ZeroFrame;
-
-  })(Class);
-
-  window.ZeroFrame = ZeroFrame;
-
-}).call(this);
-
-
-/* ---- PluginList.coffee ---- */
-
-
-(function() {
-  var PluginList,
-    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
-    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
-    hasProp = {}.hasOwnProperty;
-
-  PluginList = (function(superClass) {
-    extend(PluginList, superClass);
-
-    function PluginList(plugins) {
-      this.handleDeleteClick = bind(this.handleDeleteClick, this);
-      this.handleUpdateClick = bind(this.handleUpdateClick, this);
-      this.handleResetClick = bind(this.handleResetClick, this);
-      this.handleCheckboxChange = bind(this.handleCheckboxChange, this);
-      this.savePluginStatus = bind(this.savePluginStatus, this);
-      this.plugins = plugins;
-    }
-
-    PluginList.prototype.savePluginStatus = function(plugin, is_enabled) {
-      Page.cmd("pluginConfigSet", [plugin.source, plugin.inner_path, "enabled", is_enabled], (function(_this) {
-        return function(res) {
-          if (res === "ok") {
-            return Page.updatePlugins();
-          } else {
-            return Page.cmd("wrapperNotification", ["error", res.error]);
-          }
-        };
-      })(this));
-      return Page.projector.scheduleRender();
-    };
-
-    PluginList.prototype.handleCheckboxChange = function(e) {
-      var node, plugin, value;
-      node = e.currentTarget;
-      plugin = node["data-plugin"];
-      node.classList.toggle("checked");
-      value = node.classList.contains("checked");
-      return this.savePluginStatus(plugin, value);
-    };
-
-    PluginList.prototype.handleResetClick = function(e) {
-      var node, plugin;
-      node = e.currentTarget;
-      plugin = node["data-plugin"];
-      return this.savePluginStatus(plugin, null);
-    };
-
-    PluginList.prototype.handleUpdateClick = function(e) {
-      var node, plugin;
-      node = e.currentTarget;
-      plugin = node["data-plugin"];
-      node.classList.add("loading");
-      Page.cmd("pluginUpdate", [plugin.source, plugin.inner_path], (function(_this) {
-        return function(res) {
-          if (res === "ok") {
-            Page.cmd("wrapperNotification", ["done", "Plugin " + plugin.name + " updated to latest version"]);
-            Page.updatePlugins();
-          } else {
-            Page.cmd("wrapperNotification", ["error", res.error]);
-          }
-          return node.classList.remove("loading");
-        };
-      })(this));
-      return false;
-    };
-
-    PluginList.prototype.handleDeleteClick = function(e) {
-      var node, plugin;
-      node = e.currentTarget;
-      plugin = node["data-plugin"];
-      if (plugin.loaded) {
-        Page.cmd("wrapperNotification", ["info", "You can only delete plugin that are not currently active"]);
-        return false;
-      }
-      node.classList.add("loading");
-      Page.cmd("wrapperConfirm", ["Delete " + plugin.name + " plugin?", "Delete"], (function(_this) {
-        return function(res) {
-          if (!res) {
-            node.classList.remove("loading");
-            return false;
-          }
-          return Page.cmd("pluginRemove", [plugin.source, plugin.inner_path], function(res) {
-            if (res === "ok") {
-              Page.cmd("wrapperNotification", ["done", "Plugin " + plugin.name + " deleted"]);
-              Page.updatePlugins();
-            } else {
-              Page.cmd("wrapperNotification", ["error", res.error]);
-            }
-            return node.classList.remove("loading");
-          });
-        };
-      })(this));
-      return false;
-    };
-
-    PluginList.prototype.render = function() {
-      return h("div.plugins", this.plugins.map((function(_this) {
-        return function(plugin) {
-          var base, descr, enabled_default, is_changed, is_pending, marker_title, ref, tag_delete, tag_source, tag_update, tag_version;
-          if (!plugin.info) {
-            return;
-          }
-          descr = plugin.info.description;
-          if ((base = plugin.info)["default"] == null) {
-            base["default"] = "enabled";
-          }
-          if (plugin.info["default"]) {
-            descr += " (default: " + plugin.info["default"] + ")";
-          }
-          tag_version = "";
-          tag_source = "";
-          tag_delete = "";
-          if (plugin.source !== "builtin") {
-            tag_update = "";
-            if ((ref = plugin.site_info) != null ? ref.rev : void 0) {
-              if (plugin.site_info.rev > plugin.info.rev) {
-                tag_update = h("a.version-update.button", {
-                  href: "#Update+plugin",
-                  onclick: _this.handleUpdateClick,
-                  "data-plugin": plugin
-                }, "Update to rev" + plugin.site_info.rev);
-              }
-            } else {
-              tag_update = h("span.version-missing", "(unable to get latest vesion: update site missing)");
-            }
-            tag_version = h("span.version", ["rev" + plugin.info.rev + " ", tag_update]);
-            tag_source = h("div.source", [
-              "Source: ", h("a", {
-                "href": "/" + plugin.source,
-                "target": "_top"
-              }, plugin.site_title ? plugin.site_title : plugin.source), " /" + plugin.inner_path
-            ]);
-            tag_delete = h("a.delete", {
-              "href": "#Delete+plugin",
-              onclick: _this.handleDeleteClick,
-              "data-plugin": plugin
-            }, "Delete plugin");
-          }
-          enabled_default = plugin.info["default"] === "enabled";
-          if (plugin.enabled !== plugin.loaded || plugin.updated) {
-            marker_title = "Change pending";
-            is_pending = true;
-          } else {
-            marker_title = "Changed from default status (click to reset to " + plugin.info["default"] + ")";
-            is_pending = false;
-          }
-          is_changed = plugin.enabled !== enabled_default && plugin.owner === "builtin";
-          return h("div.plugin", {
-            key: plugin.name
-          }, [
-            h("div.title", [h("h3", [plugin.name, tag_version]), h("div.description", [descr, tag_source, tag_delete])]), h("div.value.value-right", h("div.checkbox", {
-              onclick: _this.handleCheckboxChange,
-              "data-plugin": plugin,
-              classes: {
-                checked: plugin.enabled
-              }
-            }, h("div.checkbox-skin")), h("a.marker", {
-              href: "#Reset",
-              title: marker_title,
-              onclick: _this.handleResetClick,
-              "data-plugin": plugin,
-              classes: {
-                visible: is_pending || is_changed,
-                pending: is_pending
-              }
-            }, "\u2022"))
-          ]);
-        };
-      })(this)));
-    };
-
-    return PluginList;
-
-  })(Class);
-
-  window.PluginList = PluginList;
-
-}).call(this);
-
-
-/* ---- UiPluginManager.coffee ---- */
-
-
-(function() {
-  var UiPluginManager,
-    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
-    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
-    hasProp = {}.hasOwnProperty;
-
-  window.h = maquette.h;
-
-  UiPluginManager = (function(superClass) {
-    extend(UiPluginManager, superClass);
-
-    function UiPluginManager() {
-      this.renderBottomRestart = bind(this.renderBottomRestart, this);
-      this.handleRestartClick = bind(this.handleRestartClick, this);
-      this.render = bind(this.render, this);
-      this.createProjector = bind(this.createProjector, this);
-      this.updatePlugins = bind(this.updatePlugins, this);
-      this.onOpenWebsocket = bind(this.onOpenWebsocket, this);
-      return UiPluginManager.__super__.constructor.apply(this, arguments);
-    }
-
-    UiPluginManager.prototype.init = function() {
-      this.plugin_list_builtin = new PluginList();
-      this.plugin_list_custom = new PluginList();
-      this.plugins_changed = null;
-      this.need_restart = null;
-      return this;
-    };
-
-    UiPluginManager.prototype.onOpenWebsocket = function() {
-      this.cmd("wrapperSetTitle", "Plugin manager - ZeroNet");
-      this.cmd("serverInfo", {}, (function(_this) {
-        return function(server_info) {
-          return _this.server_info = server_info;
-        };
-      })(this));
-      return this.updatePlugins();
-    };
-
-    UiPluginManager.prototype.updatePlugins = function(cb) {
-      return this.cmd("pluginList", [], (function(_this) {
-        return function(res) {
-          var item, plugins_builtin, plugins_custom;
-          _this.plugins_changed = (function() {
-            var i, len, ref, results;
-            ref = res.plugins;
-            results = [];
-            for (i = 0, len = ref.length; i < len; i++) {
-              item = ref[i];
-              if (item.enabled !== item.loaded || item.updated) {
-                results.push(item);
-              }
-            }
-            return results;
-          })();
-          plugins_builtin = (function() {
-            var i, len, ref, results;
-            ref = res.plugins;
-            results = [];
-            for (i = 0, len = ref.length; i < len; i++) {
-              item = ref[i];
-              if (item.source === "builtin") {
-                results.push(item);
-              }
-            }
-            return results;
-          })();
-          _this.plugin_list_builtin.plugins = plugins_builtin.sort(function(a, b) {
-            return a.name.localeCompare(b.name);
-          });
-          plugins_custom = (function() {
-            var i, len, ref, results;
-            ref = res.plugins;
-            results = [];
-            for (i = 0, len = ref.length; i < len; i++) {
-              item = ref[i];
-              if (item.source !== "builtin") {
-                results.push(item);
-              }
-            }
-            return results;
-          })();
-          _this.plugin_list_custom.plugins = plugins_custom.sort(function(a, b) {
-            return a.name.localeCompare(b.name);
-          });
-          _this.projector.scheduleRender();
-          return typeof cb === "function" ? cb() : void 0;
-        };
-      })(this));
-    };
-
-    UiPluginManager.prototype.createProjector = function() {
-      this.projector = maquette.createProjector();
-      this.projector.replace($("#content"), this.render);
-      return this.projector.replace($("#bottom-restart"), this.renderBottomRestart);
-    };
-
-    UiPluginManager.prototype.render = function() {
-      var ref;
-      if (!this.plugin_list_builtin.plugins) {
-        return h("div.content");
-      }
-      return h("div.content", [h("div.section", [((ref = this.plugin_list_custom.plugins) != null ? ref.length : void 0) ? [h("h2", "Installed third-party plugins"), this.plugin_list_custom.render()] : void 0, h("h2", "Built-in plugins"), this.plugin_list_builtin.render()])]);
-    };
-
-    UiPluginManager.prototype.handleRestartClick = function() {
-      this.restart_loading = true;
-      setTimeout(((function(_this) {
-        return function() {
-          return Page.cmd("serverShutdown", {
-            restart: true
-          });
-        };
-      })(this)), 300);
-      Page.projector.scheduleRender();
-      return false;
-    };
-
-    UiPluginManager.prototype.renderBottomRestart = function() {
-      var ref;
-      return h("div.bottom.bottom-restart", {
-        classes: {
-          visible: (ref = this.plugins_changed) != null ? ref.length : void 0
-        }
-      }, h("div.bottom-content", [
-        h("div.title", "Some plugins status has been changed"), h("a.button.button-submit.button-restart", {
-          href: "#Restart",
-          classes: {
-            loading: this.restart_loading
-          },
-          onclick: this.handleRestartClick
-        }, "Restart ZeroNet client")
-      ]));
-    };
-
-    return UiPluginManager;
-
-  })(ZeroFrame);
-
-  window.Page = new UiPluginManager();
-
-  window.Page.createProjector();
-
-}).call(this);
diff --git a/plugins/UiPluginManager/media/js/lib/Class.coffee b/plugins/UiPluginManager/media/js/lib/Class.coffee
deleted file mode 100644
index d62ab25c..00000000
--- a/plugins/UiPluginManager/media/js/lib/Class.coffee
+++ /dev/null
@@ -1,23 +0,0 @@
-class Class
-	trace: true
-
-	log: (args...) ->
-		return unless @trace
-		return if typeof console is 'undefined'
-		args.unshift("[#{@.constructor.name}]")
-		console.log(args...)
-		@
-		
-	logStart: (name, args...) ->
-		return unless @trace
-		@logtimers or= {}
-		@logtimers[name] = +(new Date)
-		@log "#{name}", args..., "(started)" if args.length > 0
-		@
-		
-	logEnd: (name, args...) ->
-		ms = +(new Date)-@logtimers[name]
-		@log "#{name}", args..., "(Done in #{ms}ms)"
-		@ 
-
-window.Class = Class
\ No newline at end of file
diff --git a/plugins/UiPluginManager/media/js/lib/Promise.coffee b/plugins/UiPluginManager/media/js/lib/Promise.coffee
deleted file mode 100644
index 136e3ec7..00000000
--- a/plugins/UiPluginManager/media/js/lib/Promise.coffee
+++ /dev/null
@@ -1,74 +0,0 @@
-# From: http://dev.bizo.com/2011/12/promises-in-javascriptcoffeescript.html
-
-class Promise
-	@when: (tasks...) ->
-		num_uncompleted = tasks.length
-		args = new Array(num_uncompleted)
-		promise = new Promise()
-
-		for task, task_id in tasks
-			((task_id) ->
-				task.then(() ->
-					args[task_id] = Array.prototype.slice.call(arguments)
-					num_uncompleted--
-					promise.complete.apply(promise, args) if num_uncompleted == 0
-				)
-			)(task_id)
-
-		return promise
-
-	constructor: ->
-		@resolved = false
-		@end_promise = null
-		@result = null
-		@callbacks = []
-
-	resolve: ->
-		if @resolved
-			return false
-		@resolved = true
-		@data = arguments
-		if not arguments.length
-			@data = [true]
-		@result = @data[0]
-		for callback in @callbacks
-			back = callback.apply callback, @data
-		if @end_promise
-			@end_promise.resolve(back)
-
-	fail: ->
-		@resolve(false)
-
-	then: (callback) ->
-		if @resolved == true
-			callback.apply callback, @data
-			return
-
-		@callbacks.push callback
-
-		@end_promise = new Promise()
-
-window.Promise = Promise
-
-###
-s = Date.now()
-log = (text) ->
-	console.log Date.now()-s, Array.prototype.slice.call(arguments).join(", ")
-
-log "Started"
-
-cmd = (query) ->
-	p = new Promise()
-	setTimeout ( ->
-		p.resolve query+" Result"
-	), 100
-	return p
-
-back = cmd("SELECT * FROM message").then (res) ->
-	log res
-	return "Return from query"
-.then (res) ->
-	log "Back then", res
-
-log "Query started", back
-###
\ No newline at end of file
diff --git a/plugins/UiPluginManager/media/js/lib/Prototypes.coffee b/plugins/UiPluginManager/media/js/lib/Prototypes.coffee
deleted file mode 100644
index 034add50..00000000
--- a/plugins/UiPluginManager/media/js/lib/Prototypes.coffee
+++ /dev/null
@@ -1,8 +0,0 @@
-String::startsWith = (s) -> @[...s.length] is s
-String::endsWith = (s) -> s is '' or @[-s.length..] is s
-String::repeat = (count) -> new Array( count + 1 ).join(@)
-
-window.isEmpty = (obj) ->
-	for key of obj
-		return false
-	return true
diff --git a/plugins/UiPluginManager/media/js/lib/maquette.js b/plugins/UiPluginManager/media/js/lib/maquette.js
deleted file mode 100644
index 84d14471..00000000
--- a/plugins/UiPluginManager/media/js/lib/maquette.js
+++ /dev/null
@@ -1,770 +0,0 @@
-(function (root, factory) {
-    if (typeof define === 'function' && define.amd) {
-        // AMD. Register as an anonymous module.
-        define(['exports'], factory);
-    } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
-        // CommonJS
-        factory(exports);
-    } else {
-        // Browser globals
-        factory(root.maquette = {});
-    }
-}(this, function (exports) {
-    'use strict';
-    ;
-    ;
-    ;
-    ;
-    var NAMESPACE_W3 = 'http://www.w3.org/';
-    var NAMESPACE_SVG = NAMESPACE_W3 + '2000/svg';
-    var NAMESPACE_XLINK = NAMESPACE_W3 + '1999/xlink';
-    // Utilities
-    var emptyArray = [];
-    var extend = function (base, overrides) {
-        var result = {};
-        Object.keys(base).forEach(function (key) {
-            result[key] = base[key];
-        });
-        if (overrides) {
-            Object.keys(overrides).forEach(function (key) {
-                result[key] = overrides[key];
-            });
-        }
-        return result;
-    };
-    // Hyperscript helper functions
-    var same = function (vnode1, vnode2) {
-        if (vnode1.vnodeSelector !== vnode2.vnodeSelector) {
-            return false;
-        }
-        if (vnode1.properties && vnode2.properties) {
-            if (vnode1.properties.key !== vnode2.properties.key) {
-                return false;
-            }
-            return vnode1.properties.bind === vnode2.properties.bind;
-        }
-        return !vnode1.properties && !vnode2.properties;
-    };
-    var toTextVNode = function (data) {
-        return {
-            vnodeSelector: '',
-            properties: undefined,
-            children: undefined,
-            text: data.toString(),
-            domNode: null
-        };
-    };
-    var appendChildren = function (parentSelector, insertions, main) {
-        for (var i = 0; i < insertions.length; i++) {
-            var item = insertions[i];
-            if (Array.isArray(item)) {
-                appendChildren(parentSelector, item, main);
-            } else {
-                if (item !== null && item !== undefined) {
-                    if (!item.hasOwnProperty('vnodeSelector')) {
-                        item = toTextVNode(item);
-                    }
-                    main.push(item);
-                }
-            }
-        }
-    };
-    // Render helper functions
-    var missingTransition = function () {
-        throw new Error('Provide a transitions object to the projectionOptions to do animations');
-    };
-    var DEFAULT_PROJECTION_OPTIONS = {
-        namespace: undefined,
-        eventHandlerInterceptor: undefined,
-        styleApplyer: function (domNode, styleName, value) {
-            // Provides a hook to add vendor prefixes for browsers that still need it.
-            domNode.style[styleName] = value;
-        },
-        transitions: {
-            enter: missingTransition,
-            exit: missingTransition
-        }
-    };
-    var applyDefaultProjectionOptions = function (projectorOptions) {
-        return extend(DEFAULT_PROJECTION_OPTIONS, projectorOptions);
-    };
-    var checkStyleValue = function (styleValue) {
-        if (typeof styleValue !== 'string') {
-            throw new Error('Style values must be strings');
-        }
-    };
-    var setProperties = function (domNode, properties, projectionOptions) {
-        if (!properties) {
-            return;
-        }
-        var eventHandlerInterceptor = projectionOptions.eventHandlerInterceptor;
-        var propNames = Object.keys(properties);
-        var propCount = propNames.length;
-        for (var i = 0; i < propCount; i++) {
-            var propName = propNames[i];
-            /* tslint:disable:no-var-keyword: edge case */
-            var propValue = properties[propName];
-            /* tslint:enable:no-var-keyword */
-            if (propName === 'className') {
-                throw new Error('Property "className" is not supported, use "class".');
-            } else if (propName === 'class') {
-                if (domNode.className) {
-                    // May happen if classes is specified before class
-                    domNode.className += ' ' + propValue;
-                } else {
-                    domNode.className = propValue;
-                }
-            } else if (propName === 'classes') {
-                // object with string keys and boolean values
-                var classNames = Object.keys(propValue);
-                var classNameCount = classNames.length;
-                for (var j = 0; j < classNameCount; j++) {
-                    var className = classNames[j];
-                    if (propValue[className]) {
-                        domNode.classList.add(className);
-                    }
-                }
-            } else if (propName === 'styles') {
-                // object with string keys and string (!) values
-                var styleNames = Object.keys(propValue);
-                var styleCount = styleNames.length;
-                for (var j = 0; j < styleCount; j++) {
-                    var styleName = styleNames[j];
-                    var styleValue = propValue[styleName];
-                    if (styleValue) {
-                        checkStyleValue(styleValue);
-                        projectionOptions.styleApplyer(domNode, styleName, styleValue);
-                    }
-                }
-            } else if (propName === 'key') {
-                continue;
-            } else if (propValue === null || propValue === undefined) {
-                continue;
-            } else {
-                var type = typeof propValue;
-                if (type === 'function') {
-                    if (propName.lastIndexOf('on', 0) === 0) {
-                        if (eventHandlerInterceptor) {
-                            propValue = eventHandlerInterceptor(propName, propValue, domNode, properties);    // intercept eventhandlers
-                        }
-                        if (propName === 'oninput') {
-                            (function () {
-                                // record the evt.target.value, because IE and Edge sometimes do a requestAnimationFrame between changing value and running oninput
-                                var oldPropValue = propValue;
-                                propValue = function (evt) {
-                                    evt.target['oninput-value'] = evt.target.value;
-                                    // may be HTMLTextAreaElement as well
-                                    oldPropValue.apply(this, [evt]);
-                                };
-                            }());
-                        }
-                        domNode[propName] = propValue;
-                    }
-                } else if (type === 'string' && propName !== 'value' && propName !== 'innerHTML') {
-                    if (projectionOptions.namespace === NAMESPACE_SVG && propName === 'href') {
-                        domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);
-                    } else {
-                        domNode.setAttribute(propName, propValue);
-                    }
-                } else {
-                    domNode[propName] = propValue;
-                }
-            }
-        }
-    };
-    var updateProperties = function (domNode, previousProperties, properties, projectionOptions) {
-        if (!properties) {
-            return;
-        }
-        var propertiesUpdated = false;
-        var propNames = Object.keys(properties);
-        var propCount = propNames.length;
-        for (var i = 0; i < propCount; i++) {
-            var propName = propNames[i];
-            // assuming that properties will be nullified instead of missing is by design
-            var propValue = properties[propName];
-            var previousValue = previousProperties[propName];
-            if (propName === 'class') {
-                if (previousValue !== propValue) {
-                    throw new Error('"class" property may not be updated. Use the "classes" property for conditional css classes.');
-                }
-            } else if (propName === 'classes') {
-                var classList = domNode.classList;
-                var classNames = Object.keys(propValue);
-                var classNameCount = classNames.length;
-                for (var j = 0; j < classNameCount; j++) {
-                    var className = classNames[j];
-                    var on = !!propValue[className];
-                    var previousOn = !!previousValue[className];
-                    if (on === previousOn) {
-                        continue;
-                    }
-                    propertiesUpdated = true;
-                    if (on) {
-                        classList.add(className);
-                    } else {
-                        classList.remove(className);
-                    }
-                }
-            } else if (propName === 'styles') {
-                var styleNames = Object.keys(propValue);
-                var styleCount = styleNames.length;
-                for (var j = 0; j < styleCount; j++) {
-                    var styleName = styleNames[j];
-                    var newStyleValue = propValue[styleName];
-                    var oldStyleValue = previousValue[styleName];
-                    if (newStyleValue === oldStyleValue) {
-                        continue;
-                    }
-                    propertiesUpdated = true;
-                    if (newStyleValue) {
-                        checkStyleValue(newStyleValue);
-                        projectionOptions.styleApplyer(domNode, styleName, newStyleValue);
-                    } else {
-                        projectionOptions.styleApplyer(domNode, styleName, '');
-                    }
-                }
-            } else {
-                if (!propValue && typeof previousValue === 'string') {
-                    propValue = '';
-                }
-                if (propName === 'value') {
-                    if (domNode[propName] !== propValue && domNode['oninput-value'] !== propValue) {
-                        domNode[propName] = propValue;
-                        // Reset the value, even if the virtual DOM did not change
-                        domNode['oninput-value'] = undefined;
-                    }
-                    // else do not update the domNode, otherwise the cursor position would be changed
-                    if (propValue !== previousValue) {
-                        propertiesUpdated = true;
-                    }
-                } else if (propValue !== previousValue) {
-                    var type = typeof propValue;
-                    if (type === 'function') {
-                        throw new Error('Functions may not be updated on subsequent renders (property: ' + propName + '). Hint: declare event handler functions outside the render() function.');
-                    }
-                    if (type === 'string' && propName !== 'innerHTML') {
-                        if (projectionOptions.namespace === NAMESPACE_SVG && propName === 'href') {
-                            domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);
-                        } else {
-                            domNode.setAttribute(propName, propValue);
-                        }
-                    } else {
-                        if (domNode[propName] !== propValue) {
-                            domNode[propName] = propValue;
-                        }
-                    }
-                    propertiesUpdated = true;
-                }
-            }
-        }
-        return propertiesUpdated;
-    };
-    var findIndexOfChild = function (children, sameAs, start) {
-        if (sameAs.vnodeSelector !== '') {
-            // Never scan for text-nodes
-            for (var i = start; i < children.length; i++) {
-                if (same(children[i], sameAs)) {
-                    return i;
-                }
-            }
-        }
-        return -1;
-    };
-    var nodeAdded = function (vNode, transitions) {
-        if (vNode.properties) {
-            var enterAnimation = vNode.properties.enterAnimation;
-            if (enterAnimation) {
-                if (typeof enterAnimation === 'function') {
-                    enterAnimation(vNode.domNode, vNode.properties);
-                } else {
-                    transitions.enter(vNode.domNode, vNode.properties, enterAnimation);
-                }
-            }
-        }
-    };
-    var nodeToRemove = function (vNode, transitions) {
-        var domNode = vNode.domNode;
-        if (vNode.properties) {
-            var exitAnimation = vNode.properties.exitAnimation;
-            if (exitAnimation) {
-                domNode.style.pointerEvents = 'none';
-                var removeDomNode = function () {
-                    if (domNode.parentNode) {
-                        domNode.parentNode.removeChild(domNode);
-                    }
-                };
-                if (typeof exitAnimation === 'function') {
-                    exitAnimation(domNode, removeDomNode, vNode.properties);
-                    return;
-                } else {
-                    transitions.exit(vNode.domNode, vNode.properties, exitAnimation, removeDomNode);
-                    return;
-                }
-            }
-        }
-        if (domNode.parentNode) {
-            domNode.parentNode.removeChild(domNode);
-        }
-    };
-    var checkDistinguishable = function (childNodes, indexToCheck, parentVNode, operation) {
-        var childNode = childNodes[indexToCheck];
-        if (childNode.vnodeSelector === '') {
-            return;    // Text nodes need not be distinguishable
-        }
-        var properties = childNode.properties;
-        var key = properties ? properties.key === undefined ? properties.bind : properties.key : undefined;
-        if (!key) {
-            for (var i = 0; i < childNodes.length; i++) {
-                if (i !== indexToCheck) {
-                    var node = childNodes[i];
-                    if (same(node, childNode)) {
-                        if (operation === 'added') {
-                            throw new Error(parentVNode.vnodeSelector + ' had a ' + childNode.vnodeSelector + ' child ' + 'added, but there is now more than one. You must add unique key properties to make them distinguishable.');
-                        } else {
-                            throw new Error(parentVNode.vnodeSelector + ' had a ' + childNode.vnodeSelector + ' child ' + 'removed, but there were more than one. You must add unique key properties to make them distinguishable.');
-                        }
-                    }
-                }
-            }
-        }
-    };
-    var createDom;
-    var updateDom;
-    var updateChildren = function (vnode, domNode, oldChildren, newChildren, projectionOptions) {
-        if (oldChildren === newChildren) {
-            return false;
-        }
-        oldChildren = oldChildren || emptyArray;
-        newChildren = newChildren || emptyArray;
-        var oldChildrenLength = oldChildren.length;
-        var newChildrenLength = newChildren.length;
-        var transitions = projectionOptions.transitions;
-        var oldIndex = 0;
-        var newIndex = 0;
-        var i;
-        var textUpdated = false;
-        while (newIndex < newChildrenLength) {
-            var oldChild = oldIndex < oldChildrenLength ? oldChildren[oldIndex] : undefined;
-            var newChild = newChildren[newIndex];
-            if (oldChild !== undefined && same(oldChild, newChild)) {
-                textUpdated = updateDom(oldChild, newChild, projectionOptions) || textUpdated;
-                oldIndex++;
-            } else {
-                var findOldIndex = findIndexOfChild(oldChildren, newChild, oldIndex + 1);
-                if (findOldIndex >= 0) {
-                    // Remove preceding missing children
-                    for (i = oldIndex; i < findOldIndex; i++) {
-                        nodeToRemove(oldChildren[i], transitions);
-                        checkDistinguishable(oldChildren, i, vnode, 'removed');
-                    }
-                    textUpdated = updateDom(oldChildren[findOldIndex], newChild, projectionOptions) || textUpdated;
-                    oldIndex = findOldIndex + 1;
-                } else {
-                    // New child
-                    createDom(newChild, domNode, oldIndex < oldChildrenLength ? oldChildren[oldIndex].domNode : undefined, projectionOptions);
-                    nodeAdded(newChild, transitions);
-                    checkDistinguishable(newChildren, newIndex, vnode, 'added');
-                }
-            }
-            newIndex++;
-        }
-        if (oldChildrenLength > oldIndex) {
-            // Remove child fragments
-            for (i = oldIndex; i < oldChildrenLength; i++) {
-                nodeToRemove(oldChildren[i], transitions);
-                checkDistinguishable(oldChildren, i, vnode, 'removed');
-            }
-        }
-        return textUpdated;
-    };
-    var addChildren = function (domNode, children, projectionOptions) {
-        if (!children) {
-            return;
-        }
-        for (var i = 0; i < children.length; i++) {
-            createDom(children[i], domNode, undefined, projectionOptions);
-        }
-    };
-    var initPropertiesAndChildren = function (domNode, vnode, projectionOptions) {
-        addChildren(domNode, vnode.children, projectionOptions);
-        // children before properties, needed for value property of <select>.
-        if (vnode.text) {
-            domNode.textContent = vnode.text;
-        }
-        setProperties(domNode, vnode.properties, projectionOptions);
-        if (vnode.properties && vnode.properties.afterCreate) {
-            vnode.properties.afterCreate(domNode, projectionOptions, vnode.vnodeSelector, vnode.properties, vnode.children);
-        }
-    };
-    createDom = function (vnode, parentNode, insertBefore, projectionOptions) {
-        var domNode, i, c, start = 0, type, found;
-        var vnodeSelector = vnode.vnodeSelector;
-        if (vnodeSelector === '') {
-            domNode = vnode.domNode = document.createTextNode(vnode.text);
-            if (insertBefore !== undefined) {
-                parentNode.insertBefore(domNode, insertBefore);
-            } else {
-                parentNode.appendChild(domNode);
-            }
-        } else {
-            for (i = 0; i <= vnodeSelector.length; ++i) {
-                c = vnodeSelector.charAt(i);
-                if (i === vnodeSelector.length || c === '.' || c === '#') {
-                    type = vnodeSelector.charAt(start - 1);
-                    found = vnodeSelector.slice(start, i);
-                    if (type === '.') {
-                        domNode.classList.add(found);
-                    } else if (type === '#') {
-                        domNode.id = found;
-                    } else {
-                        if (found === 'svg') {
-                            projectionOptions = extend(projectionOptions, { namespace: NAMESPACE_SVG });
-                        }
-                        if (projectionOptions.namespace !== undefined) {
-                            domNode = vnode.domNode = document.createElementNS(projectionOptions.namespace, found);
-                        } else {
-                            domNode = vnode.domNode = document.createElement(found);
-                        }
-                        if (insertBefore !== undefined) {
-                            parentNode.insertBefore(domNode, insertBefore);
-                        } else {
-                            parentNode.appendChild(domNode);
-                        }
-                    }
-                    start = i + 1;
-                }
-            }
-            initPropertiesAndChildren(domNode, vnode, projectionOptions);
-        }
-    };
-    updateDom = function (previous, vnode, projectionOptions) {
-        var domNode = previous.domNode;
-        var textUpdated = false;
-        if (previous === vnode) {
-            return false;    // By contract, VNode objects may not be modified anymore after passing them to maquette
-        }
-        var updated = false;
-        if (vnode.vnodeSelector === '') {
-            if (vnode.text !== previous.text) {
-                var newVNode = document.createTextNode(vnode.text);
-                domNode.parentNode.replaceChild(newVNode, domNode);
-                vnode.domNode = newVNode;
-                textUpdated = true;
-                return textUpdated;
-            }
-        } else {
-            if (vnode.vnodeSelector.lastIndexOf('svg', 0) === 0) {
-                projectionOptions = extend(projectionOptions, { namespace: NAMESPACE_SVG });
-            }
-            if (previous.text !== vnode.text) {
-                updated = true;
-                if (vnode.text === undefined) {
-                    domNode.removeChild(domNode.firstChild);    // the only textnode presumably
-                } else {
-                    domNode.textContent = vnode.text;
-                }
-            }
-            updated = updateChildren(vnode, domNode, previous.children, vnode.children, projectionOptions) || updated;
-            updated = updateProperties(domNode, previous.properties, vnode.properties, projectionOptions) || updated;
-            if (vnode.properties && vnode.properties.afterUpdate) {
-                vnode.properties.afterUpdate(domNode, projectionOptions, vnode.vnodeSelector, vnode.properties, vnode.children);
-            }
-        }
-        if (updated && vnode.properties && vnode.properties.updateAnimation) {
-            vnode.properties.updateAnimation(domNode, vnode.properties, previous.properties);
-        }
-        vnode.domNode = previous.domNode;
-        return textUpdated;
-    };
-    var createProjection = function (vnode, projectionOptions) {
-        return {
-            update: function (updatedVnode) {
-                if (vnode.vnodeSelector !== updatedVnode.vnodeSelector) {
-                    throw new Error('The selector for the root VNode may not be changed. (consider using dom.merge and add one extra level to the virtual DOM)');
-                }
-                updateDom(vnode, updatedVnode, projectionOptions);
-                vnode = updatedVnode;
-            },
-            domNode: vnode.domNode
-        };
-    };
-    ;
-    // The other two parameters are not added here, because the Typescript compiler creates surrogate code for desctructuring 'children'.
-    exports.h = function (selector) {
-        var properties = arguments[1];
-        if (typeof selector !== 'string') {
-            throw new Error();
-        }
-        var childIndex = 1;
-        if (properties && !properties.hasOwnProperty('vnodeSelector') && !Array.isArray(properties) && typeof properties === 'object') {
-            childIndex = 2;
-        } else {
-            // Optional properties argument was omitted
-            properties = undefined;
-        }
-        var text = undefined;
-        var children = undefined;
-        var argsLength = arguments.length;
-        // Recognize a common special case where there is only a single text node
-        if (argsLength === childIndex + 1) {
-            var onlyChild = arguments[childIndex];
-            if (typeof onlyChild === 'string') {
-                text = onlyChild;
-            } else if (onlyChild !== undefined && onlyChild.length === 1 && typeof onlyChild[0] === 'string') {
-                text = onlyChild[0];
-            }
-        }
-        if (text === undefined) {
-            children = [];
-            for (; childIndex < arguments.length; childIndex++) {
-                var child = arguments[childIndex];
-                if (child === null || child === undefined) {
-                    continue;
-                } else if (Array.isArray(child)) {
-                    appendChildren(selector, child, children);
-                } else if (child.hasOwnProperty('vnodeSelector')) {
-                    children.push(child);
-                } else {
-                    children.push(toTextVNode(child));
-                }
-            }
-        }
-        return {
-            vnodeSelector: selector,
-            properties: properties,
-            children: children,
-            text: text === '' ? undefined : text,
-            domNode: null
-        };
-    };
-    /**
- * Contains simple low-level utility functions to manipulate the real DOM.
- */
-    exports.dom = {
-        /**
-     * Creates a real DOM tree from `vnode`. The [[Projection]] object returned will contain the resulting DOM Node in
-     * its [[Projection.domNode|domNode]] property.
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
-     * objects may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the projection.
-     * @returns The [[Projection]] which also contains the DOM Node that was created.
-     */
-        create: function (vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            createDom(vnode, document.createElement('div'), undefined, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        },
-        /**
-     * Appends a new childnode to the DOM which is generated from a [[VNode]].
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param parentNode - The parent node for the new childNode.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
-     * objects may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the [[Projection]].
-     * @returns The [[Projection]] that was created.
-     */
-        append: function (parentNode, vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            createDom(vnode, parentNode, undefined, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        },
-        /**
-     * Inserts a new DOM node which is generated from a [[VNode]].
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param beforeNode - The node that the DOM Node is inserted before.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function.
-     * NOTE: [[VNode]] objects may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the projection, see [[createProjector]].
-     * @returns The [[Projection]] that was created.
-     */
-        insertBefore: function (beforeNode, vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            createDom(vnode, beforeNode.parentNode, beforeNode, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        },
-        /**
-     * Merges a new DOM node which is generated from a [[VNode]] with an existing DOM Node.
-     * This means that the virtual DOM and the real DOM will have one overlapping element.
-     * Therefore the selector for the root [[VNode]] will be ignored, but its properties and children will be applied to the Element provided.
-     * This is a low-level method. Users wil typically use a [[Projector]] instead.
-     * @param domNode - The existing element to adopt as the root of the new virtual DOM. Existing attributes and childnodes are preserved.
-     * @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]] objects
-     * may only be rendered once.
-     * @param projectionOptions - Options to be used to create and update the projection, see [[createProjector]].
-     * @returns The [[Projection]] that was created.
-     */
-        merge: function (element, vnode, projectionOptions) {
-            projectionOptions = applyDefaultProjectionOptions(projectionOptions);
-            vnode.domNode = element;
-            initPropertiesAndChildren(element, vnode, projectionOptions);
-            return createProjection(vnode, projectionOptions);
-        }
-    };
-    /**
- * Creates a [[CalculationCache]] object, useful for caching [[VNode]] trees.
- * In practice, caching of [[VNode]] trees is not needed, because achieving 60 frames per second is almost never a problem.
- * For more information, see [[CalculationCache]].
- *
- * @param <Result> The type of the value that is cached.
- */
-    exports.createCache = function () {
-        var cachedInputs = undefined;
-        var cachedOutcome = undefined;
-        var result = {
-            invalidate: function () {
-                cachedOutcome = undefined;
-                cachedInputs = undefined;
-            },
-            result: function (inputs, calculation) {
-                if (cachedInputs) {
-                    for (var i = 0; i < inputs.length; i++) {
-                        if (cachedInputs[i] !== inputs[i]) {
-                            cachedOutcome = undefined;
-                        }
-                    }
-                }
-                if (!cachedOutcome) {
-                    cachedOutcome = calculation();
-                    cachedInputs = inputs;
-                }
-                return cachedOutcome;
-            }
-        };
-        return result;
-    };
-    /**
- * Creates a {@link Mapping} instance that keeps an array of result objects synchronized with an array of source objects.
- * See {@link http://maquettejs.org/docs/arrays.html|Working with arrays}.
- *
- * @param <Source>       The type of source items. A database-record for instance.
- * @param <Target>       The type of target items. A [[Component]] for instance.
- * @param getSourceKey   `function(source)` that must return a key to identify each source object. The result must either be a string or a number.
- * @param createResult   `function(source, index)` that must create a new result object from a given source. This function is identical
- *                       to the `callback` argument in `Array.map(callback)`.
- * @param updateResult   `function(source, target, index)` that updates a result to an updated source.
- */
-    exports.createMapping = function (getSourceKey, createResult, updateResult) {
-        var keys = [];
-        var results = [];
-        return {
-            results: results,
-            map: function (newSources) {
-                var newKeys = newSources.map(getSourceKey);
-                var oldTargets = results.slice();
-                var oldIndex = 0;
-                for (var i = 0; i < newSources.length; i++) {
-                    var source = newSources[i];
-                    var sourceKey = newKeys[i];
-                    if (sourceKey === keys[oldIndex]) {
-                        results[i] = oldTargets[oldIndex];
-                        updateResult(source, oldTargets[oldIndex], i);
-                        oldIndex++;
-                    } else {
-                        var found = false;
-                        for (var j = 1; j < keys.length; j++) {
-                            var searchIndex = (oldIndex + j) % keys.length;
-                            if (keys[searchIndex] === sourceKey) {
-                                results[i] = oldTargets[searchIndex];
-                                updateResult(newSources[i], oldTargets[searchIndex], i);
-                                oldIndex = searchIndex + 1;
-                                found = true;
-                                break;
-                            }
-                        }
-                        if (!found) {
-                            results[i] = createResult(source, i);
-                        }
-                    }
-                }
-                results.length = newSources.length;
-                keys = newKeys;
-            }
-        };
-    };
-    /**
- * Creates a [[Projector]] instance using the provided projectionOptions.
- *
- * For more information, see [[Projector]].
- *
- * @param projectionOptions   Options that influence how the DOM is rendered and updated.
- */
-    exports.createProjector = function (projectorOptions) {
-        var projector;
-        var projectionOptions = applyDefaultProjectionOptions(projectorOptions);
-        projectionOptions.eventHandlerInterceptor = function (propertyName, eventHandler, domNode, properties) {
-            return function () {
-                // intercept function calls (event handlers) to do a render afterwards.
-                projector.scheduleRender();
-                return eventHandler.apply(properties.bind || this, arguments);
-            };
-        };
-        var renderCompleted = true;
-        var scheduled;
-        var stopped = false;
-        var projections = [];
-        var renderFunctions = [];
-        // matches the projections array
-        var doRender = function () {
-            scheduled = undefined;
-            if (!renderCompleted) {
-                return;    // The last render threw an error, it should be logged in the browser console.
-            }
-            renderCompleted = false;
-            for (var i = 0; i < projections.length; i++) {
-                var updatedVnode = renderFunctions[i]();
-                projections[i].update(updatedVnode);
-            }
-            renderCompleted = true;
-        };
-        projector = {
-            scheduleRender: function () {
-                if (!scheduled && !stopped) {
-                    scheduled = requestAnimationFrame(doRender);
-                }
-            },
-            stop: function () {
-                if (scheduled) {
-                    cancelAnimationFrame(scheduled);
-                    scheduled = undefined;
-                }
-                stopped = true;
-            },
-            resume: function () {
-                stopped = false;
-                renderCompleted = true;
-                projector.scheduleRender();
-            },
-            append: function (parentNode, renderMaquetteFunction) {
-                projections.push(exports.dom.append(parentNode, renderMaquetteFunction(), projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            insertBefore: function (beforeNode, renderMaquetteFunction) {
-                projections.push(exports.dom.insertBefore(beforeNode, renderMaquetteFunction(), projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            merge: function (domNode, renderMaquetteFunction) {
-                projections.push(exports.dom.merge(domNode, renderMaquetteFunction(), projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            replace: function (domNode, renderMaquetteFunction) {
-                var vnode = renderMaquetteFunction();
-                createDom(vnode, domNode.parentNode, domNode, projectionOptions);
-                domNode.parentNode.removeChild(domNode);
-                projections.push(createProjection(vnode, projectionOptions));
-                renderFunctions.push(renderMaquetteFunction);
-            },
-            detach: function (renderMaquetteFunction) {
-                for (var i = 0; i < renderFunctions.length; i++) {
-                    if (renderFunctions[i] === renderMaquetteFunction) {
-                        renderFunctions.splice(i, 1);
-                        return projections.splice(i, 1)[0];
-                    }
-                }
-                throw new Error('renderMaquetteFunction was not found');
-            }
-        };
-        return projector;
-    };
-}));
diff --git a/plugins/UiPluginManager/media/js/utils/Animation.coffee b/plugins/UiPluginManager/media/js/utils/Animation.coffee
deleted file mode 100644
index 271b88c1..00000000
--- a/plugins/UiPluginManager/media/js/utils/Animation.coffee
+++ /dev/null
@@ -1,138 +0,0 @@
-class Animation
-	slideDown: (elem, props) ->
-		if elem.offsetTop > 2000
-			return
-
-		h = elem.offsetHeight
-		cstyle = window.getComputedStyle(elem)
-		margin_top = cstyle.marginTop
-		margin_bottom = cstyle.marginBottom
-		padding_top = cstyle.paddingTop
-		padding_bottom = cstyle.paddingBottom
-		transition = cstyle.transition
-
-		elem.style.boxSizing = "border-box"
-		elem.style.overflow = "hidden"
-		elem.style.transform = "scale(0.6)"
-		elem.style.opacity = "0"
-		elem.style.height = "0px"
-		elem.style.marginTop = "0px"
-		elem.style.marginBottom = "0px"
-		elem.style.paddingTop = "0px"
-		elem.style.paddingBottom = "0px"
-		elem.style.transition = "none"
-
-		setTimeout (->
-			elem.className += " animate-inout"
-			elem.style.height = h+"px"
-			elem.style.transform = "scale(1)"
-			elem.style.opacity = "1"
-			elem.style.marginTop = margin_top
-			elem.style.marginBottom = margin_bottom
-			elem.style.paddingTop = padding_top
-			elem.style.paddingBottom = padding_bottom
-		), 1
-
-		elem.addEventListener "transitionend", ->
-			elem.classList.remove("animate-inout")
-			elem.style.transition = elem.style.transform = elem.style.opacity = elem.style.height = null
-			elem.style.boxSizing = elem.style.marginTop = elem.style.marginBottom = null
-			elem.style.paddingTop = elem.style.paddingBottom = elem.style.overflow = null
-			elem.removeEventListener "transitionend", arguments.callee, false
-
-
-	slideUp: (elem, remove_func, props) ->
-		if elem.offsetTop > 1000
-			return remove_func()
-
-		elem.className += " animate-back"
-		elem.style.boxSizing = "border-box"
-		elem.style.height = elem.offsetHeight+"px"
-		elem.style.overflow = "hidden"
-		elem.style.transform = "scale(1)"
-		elem.style.opacity = "1"
-		elem.style.pointerEvents = "none"
-		setTimeout (->
-			elem.style.height = "0px"
-			elem.style.marginTop = "0px"
-			elem.style.marginBottom = "0px"
-			elem.style.paddingTop = "0px"
-			elem.style.paddingBottom = "0px"
-			elem.style.transform = "scale(0.8)"
-			elem.style.borderTopWidth = "0px"
-			elem.style.borderBottomWidth = "0px"
-			elem.style.opacity = "0"
-		), 1
-		elem.addEventListener "transitionend", (e) ->
-			if e.propertyName == "opacity" or e.elapsedTime >= 0.6
-				elem.removeEventListener "transitionend", arguments.callee, false
-				remove_func()
-
-
-	slideUpInout: (elem, remove_func, props) ->
-		elem.className += " animate-inout"
-		elem.style.boxSizing = "border-box"
-		elem.style.height = elem.offsetHeight+"px"
-		elem.style.overflow = "hidden"
-		elem.style.transform = "scale(1)"
-		elem.style.opacity = "1"
-		elem.style.pointerEvents = "none"
-		setTimeout (->
-			elem.style.height = "0px"
-			elem.style.marginTop = "0px"
-			elem.style.marginBottom = "0px"
-			elem.style.paddingTop = "0px"
-			elem.style.paddingBottom = "0px"
-			elem.style.transform = "scale(0.8)"
-			elem.style.borderTopWidth = "0px"
-			elem.style.borderBottomWidth = "0px"
-			elem.style.opacity = "0"
-		), 1
-		elem.addEventListener "transitionend", (e) ->
-			if e.propertyName == "opacity" or e.elapsedTime >= 0.6
-				elem.removeEventListener "transitionend", arguments.callee, false
-				remove_func()
-
-
-	showRight: (elem, props) ->
-		elem.className += " animate"
-		elem.style.opacity = 0
-		elem.style.transform = "TranslateX(-20px) Scale(1.01)"
-		setTimeout (->
-			elem.style.opacity = 1
-			elem.style.transform = "TranslateX(0px) Scale(1)"
-		), 1
-		elem.addEventListener "transitionend", ->
-			elem.classList.remove("animate")
-			elem.style.transform = elem.style.opacity = null
-
-
-	show: (elem, props) ->
-		delay = arguments[arguments.length-2]?.delay*1000 or 1
-		elem.style.opacity = 0
-		setTimeout (->
-			elem.className += " animate"
-		), 1
-		setTimeout (->
-			elem.style.opacity = 1
-		), delay
-		elem.addEventListener "transitionend", ->
-			elem.classList.remove("animate")
-			elem.style.opacity = null
-			elem.removeEventListener "transitionend", arguments.callee, false
-
-	hide: (elem, remove_func, props) ->
-		delay = arguments[arguments.length-2]?.delay*1000 or 1
-		elem.className += " animate"
-		setTimeout (->
-			elem.style.opacity = 0
-		), delay
-		elem.addEventListener "transitionend", (e) ->
-			if e.propertyName == "opacity"
-				remove_func()
-
-	addVisibleClass: (elem, props) ->
-		setTimeout ->
-			elem.classList.add("visible")
-
-window.Animation = new Animation()
\ No newline at end of file
diff --git a/plugins/UiPluginManager/media/js/utils/Dollar.coffee b/plugins/UiPluginManager/media/js/utils/Dollar.coffee
deleted file mode 100644
index 7f19f551..00000000
--- a/plugins/UiPluginManager/media/js/utils/Dollar.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-window.$ = (selector) ->
-	if selector.startsWith("#")
-		return document.getElementById(selector.replace("#", ""))
diff --git a/plugins/UiPluginManager/media/js/utils/ZeroFrame.coffee b/plugins/UiPluginManager/media/js/utils/ZeroFrame.coffee
deleted file mode 100644
index 11512d16..00000000
--- a/plugins/UiPluginManager/media/js/utils/ZeroFrame.coffee
+++ /dev/null
@@ -1,85 +0,0 @@
-class ZeroFrame extends Class
-	constructor: (url) ->
-		@url = url
-		@waiting_cb = {}
-		@wrapper_nonce = document.location.href.replace(/.*wrapper_nonce=([A-Za-z0-9]+).*/, "$1")
-		@connect()
-		@next_message_id = 1
-		@history_state = {}
-		@init()
-
-
-	init: ->
-		@
-
-
-	connect: ->
-		@target = window.parent
-		window.addEventListener("message", @onMessage, false)
-		@cmd("innerReady")
-
-		# Save scrollTop
-		window.addEventListener "beforeunload", (e) =>
-			@log "save scrollTop", window.pageYOffset
-			@history_state["scrollTop"] = window.pageYOffset
-			@cmd "wrapperReplaceState", [@history_state, null]
-
-		# Restore scrollTop
-		@cmd "wrapperGetState", [], (state) =>
-			@history_state = state if state?
-			@log "restore scrollTop", state, window.pageYOffset
-			if window.pageYOffset == 0 and state
-				window.scroll(window.pageXOffset, state.scrollTop)
-
-
-	onMessage: (e) =>
-		message = e.data
-		cmd = message.cmd
-		if cmd == "response"
-			if @waiting_cb[message.to]?
-				@waiting_cb[message.to](message.result)
-			else
-				@log "Websocket callback not found:", message
-		else if cmd == "wrapperReady" # Wrapper inited later
-			@cmd("innerReady")
-		else if cmd == "ping"
-			@response message.id, "pong"
-		else if cmd == "wrapperOpenedWebsocket"
-			@onOpenWebsocket()
-		else if cmd == "wrapperClosedWebsocket"
-			@onCloseWebsocket()
-		else
-			@onRequest cmd, message.params
-
-
-	onRequest: (cmd, message) =>
-		@log "Unknown request", message
-
-
-	response: (to, result) ->
-		@send {"cmd": "response", "to": to, "result": result}
-
-
-	cmd: (cmd, params={}, cb=null) ->
-		@send {"cmd": cmd, "params": params}, cb
-
-
-	send: (message, cb=null) ->
-		message.wrapper_nonce = @wrapper_nonce
-		message.id = @next_message_id
-		@next_message_id += 1
-		@target.postMessage(message, "*")
-		if cb
-			@waiting_cb[message.id] = cb
-
-
-	onOpenWebsocket: =>
-		@log "Websocket open"
-
-
-	onCloseWebsocket: =>
-		@log "Websocket close"
-
-
-
-window.ZeroFrame = ZeroFrame
diff --git a/plugins/UiPluginManager/media/plugin_manager.html b/plugins/UiPluginManager/media/plugin_manager.html
deleted file mode 100644
index 321cbbb3..00000000
--- a/plugins/UiPluginManager/media/plugin_manager.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!DOCTYPE html>
-
-<html>
-<head>
- <title>Settings - ZeroNet</title>
- <meta charset="utf-8" />
- <meta http-equiv="content-type" content="text/html; charset=utf-8" />
- <link rel="stylesheet" href="css/all.css?rev={rev}" />
-</head>
-
-
-<h1>ZeroNet plugin manager</h1>
-
-<div class="content" id="content"></div>
-<div class="bottom" id="bottom-restart"></div>
-
-<script type="text/javascript" src="js/all.js?rev={rev}&lang={lang}"></script>
-</body>
-</html>
\ No newline at end of file
diff --git a/plugins/Zeroname/README.md b/plugins/Zeroname/README.md
deleted file mode 100644
index baa43db5..00000000
--- a/plugins/Zeroname/README.md
+++ /dev/null
@@ -1,55 +0,0 @@
-# ZeroName
-
-Zeroname plugin to connect Namecoin and register all the .bit domain name.
-
-## Start
-
-You can create your own Zeroname.
-
-### Namecoin node
-
-You need to run a namecoin node.
-
-[Namecoin](https://namecoin.org/download/)
-
-You will need to start it as a RPC server.
-
-Example of `~/.namecoin/namecoin.conf` minimal setup:
-```
-daemon=1
-rpcuser=your-name
-rpcpassword=your-password
-rpcport=8336
-server=1
-txindex=1
-valueencoding=utf8
-```
-
-Don't forget to change the `rpcuser` value and `rpcpassword` value!
-
-You can start your node : `./namecoind`
-
-### Create a Zeroname site
-
-You will also need to create a site `python zeronet.py createSite` and regitser the info.
-
-In the site you will need to create a file `./data/<your-site>/data/names.json` with this is it:
-```
-{}
-```
-
-### `zeroname_config.json` file
-
-In `~/.namecoin/zeroname_config.json`
-```
-{
-  "lastprocessed": 223910,
-  "zeronet_path": "/root/ZeroNet", # Update with your path
-  "privatekey": "", # Update with your private key of your site
-  "site": "" # Update with the address of your site
-}
-```
-
-### Run updater
-
-You can now run the script : `updater/zeroname_updater.py` and wait until it is fully sync (it might take a while).
diff --git a/plugins/Zeroname/SiteManagerPlugin.py b/plugins/Zeroname/SiteManagerPlugin.py
deleted file mode 100644
index 2553a50c..00000000
--- a/plugins/Zeroname/SiteManagerPlugin.py
+++ /dev/null
@@ -1,69 +0,0 @@
-import logging
-import re
-import time
-
-from Config import config
-from Plugin import PluginManager
-
-allow_reload = False  # No reload supported
-
-log = logging.getLogger("ZeronamePlugin")
-
-
-@PluginManager.registerTo("SiteManager")
-class SiteManagerPlugin(object):
-    site_zeroname = None
-    db_domains = {}
-    db_domains_modified = None
-
-    def load(self, *args, **kwargs):
-        super(SiteManagerPlugin, self).load(*args, **kwargs)
-        if not self.get(config.bit_resolver):
-            self.need(config.bit_resolver)  # Need ZeroName site
-
-    # Return: True if the address is .bit domain
-    def isBitDomain(self, address):
-        return re.match(r"(.*?)([A-Za-z0-9_-]+\.bit)$", address)
-
-    # Resolve domain
-    # Return: The address or None
-    def resolveBitDomain(self, domain):
-        domain = domain.lower()
-        if not self.site_zeroname:
-            self.site_zeroname = self.need(config.bit_resolver)
-
-        site_zeroname_modified = self.site_zeroname.content_manager.contents.get("content.json", {}).get("modified", 0)
-        if not self.db_domains or self.db_domains_modified != site_zeroname_modified:
-            self.site_zeroname.needFile("data/names.json", priority=10)
-            s = time.time()
-            try:
-                self.db_domains = self.site_zeroname.storage.loadJson("data/names.json")
-            except Exception as err:
-                log.error("Error loading names.json: %s" % err)
-
-            log.debug(
-                "Domain db with %s entries loaded in %.3fs (modification: %s -> %s)" %
-                (len(self.db_domains), time.time() - s, self.db_domains_modified, site_zeroname_modified)
-            )
-            self.db_domains_modified = site_zeroname_modified
-        return self.db_domains.get(domain)
-
-    # Turn domain into address
-    def resolveDomain(self, domain):
-        return self.resolveBitDomain(domain) or super(SiteManagerPlugin, self).resolveDomain(domain)
-
-    # Return: True if the address is domain
-    def isDomain(self, address):
-        return self.isBitDomain(address) or super(SiteManagerPlugin, self).isDomain(address)
-
-
-@PluginManager.registerTo("ConfigPlugin")
-class ConfigPlugin(object):
-    def createArguments(self):
-        group = self.parser.add_argument_group("Zeroname plugin")
-        group.add_argument(
-            "--bit_resolver", help="ZeroNet site to resolve .bit domains",
-            default="1Name2NXVi1RDPDgf5617UoW7xA6YrhM9F", metavar="address"
-        )
-
-        return super(ConfigPlugin, self).createArguments()
diff --git a/plugins/Zeroname/__init__.py b/plugins/Zeroname/__init__.py
deleted file mode 100644
index 76826d9a..00000000
--- a/plugins/Zeroname/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import SiteManagerPlugin
diff --git a/plugins/Zeroname/updater/zeroname_updater.py b/plugins/Zeroname/updater/zeroname_updater.py
deleted file mode 100644
index 1e00332c..00000000
--- a/plugins/Zeroname/updater/zeroname_updater.py
+++ /dev/null
@@ -1,249 +0,0 @@
-from __future__ import print_function
-import time
-import json
-import os
-import sys
-import re
-import socket
-
-from six import string_types
-
-from subprocess import call
-from bitcoinrpc.authproxy import AuthServiceProxy
-
-
-def publish():
-    print("* Signing and Publishing...")
-    call(" ".join(command_sign_publish), shell=True)
-
-
-def processNameOp(domain, value, test=False):
-    if not value.strip().startswith("{"):
-        return False
-    try:
-        data = json.loads(value)
-    except Exception as err:
-        print("Json load error: %s" % err)
-        return False
-    if "zeronet" not in data and "map" not in data:
-    # Namecoin standard use {"map": { "blog": {"zeronet": "1D..."} }}
-        print("No zeronet and no map in ", data.keys())
-        return False
-    if "map" in data:
-    # If subdomains using the Namecoin standard is present, just re-write in the Zeronet way
-    # and call the function again
-        data_map = data["map"]
-        new_value = {}
-        for subdomain in data_map:
-            if "zeronet" in data_map[subdomain]:
-                new_value[subdomain] = data_map[subdomain]["zeronet"]
-        if "zeronet" in data and isinstance(data["zeronet"], string_types):
-        # {
-        #    "zeronet":"19rXKeKptSdQ9qt7omwN82smehzTuuq6S9",
-        #    ....
-        # }
-            new_value[""] = data["zeronet"]
-        if len(new_value) > 0:
-            return processNameOp(domain, json.dumps({"zeronet": new_value}), test)
-        else:
-            return False
-    if "zeronet" in data and isinstance(data["zeronet"], string_types):
-    # {
-    #    "zeronet":"19rXKeKptSdQ9qt7omwN82smehzTuuq6S9"
-    # } is valid
-        return processNameOp(domain, json.dumps({"zeronet": { "": data["zeronet"]}}), test)
-    if not isinstance(data["zeronet"], dict):
-        print("Not dict: ", data["zeronet"])
-        return False
-    if not re.match("^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$", domain):
-        print("Invalid domain: ", domain)
-        return False
-
-    if test:
-        return True
-
-    if "slave" in sys.argv:
-        print("Waiting for master update arrive")
-        time.sleep(30)  # Wait 30 sec to allow master updater
-
-    # Note: Requires the file data/names.json to exist and contain "{}" to work
-    names_raw = open(names_path, "rb").read()
-    names = json.loads(names_raw)
-    for subdomain, address in data["zeronet"].items():
-        subdomain = subdomain.lower()
-        address = re.sub("[^A-Za-z0-9]", "", address)
-        print(subdomain, domain, "->", address)
-        if subdomain:
-            if re.match("^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$", subdomain):
-                names["%s.%s.bit" % (subdomain, domain)] = address
-            else:
-                print("Invalid subdomain:", domain, subdomain)
-        else:
-            names["%s.bit" % domain] = address
-
-    new_names_raw = json.dumps(names, indent=2, sort_keys=True)
-    if new_names_raw != names_raw:
-        open(names_path, "wb").write(new_names_raw)
-        print("-", domain, "Changed")
-        return True
-    else:
-        print("-", domain, "Not changed")
-        return False
-
-
-def processBlock(block_id, test=False):
-    print("Processing block #%s..." % block_id)
-    s = time.time()
-    block_hash = rpc.getblockhash(block_id)
-    block = rpc.getblock(block_hash)
-
-    print("Checking %s tx" % len(block["tx"]))
-    updated = 0
-    for tx in block["tx"]:
-        try:
-            transaction = rpc.getrawtransaction(tx, 1)
-            for vout in transaction.get("vout", []):
-                if "scriptPubKey" in vout and "nameOp" in vout["scriptPubKey"] and "name" in vout["scriptPubKey"]["nameOp"]:
-                    name_op = vout["scriptPubKey"]["nameOp"]
-                    updated += processNameOp(name_op["name"].replace("d/", ""), name_op["value"], test)
-        except Exception as err:
-            print("Error processing tx #%s %s" % (tx, err))
-    print("Done in %.3fs (updated %s)." % (time.time() - s, updated))
-    return updated
-
-# Connecting to RPC
-def initRpc(config):
-    """Initialize Namecoin RPC"""
-    rpc_data = {
-        'connect': '127.0.0.1',
-        'port': '8336',
-        'user': 'PLACEHOLDER',
-        'password': 'PLACEHOLDER',
-        'clienttimeout': '900'
-    }
-    try:
-        fptr = open(config, 'r')
-        lines = fptr.readlines()
-        fptr.close()
-    except:
-        return None  # Or take some other appropriate action
-
-    for line in lines:
-        if not line.startswith('rpc'):
-            continue
-        key_val = line.split(None, 1)[0]
-        (key, val) = key_val.split('=', 1)
-        if not key or not val:
-            continue
-        rpc_data[key[3:]] = val
-
-    url = 'http://%(user)s:%(password)s@%(connect)s:%(port)s' % rpc_data
-
-    return url, int(rpc_data['clienttimeout'])
-
-# Loading config...
-
-# Check whether platform is on windows or linux
-# On linux namecoin is installed under ~/.namecoin, while on on windows it is in %appdata%/Namecoin
-
-if sys.platform == "win32":
-    namecoin_location = os.getenv('APPDATA') + "/Namecoin/"
-else:
-    namecoin_location = os.path.expanduser("~/.namecoin/")
-
-config_path = namecoin_location + 'zeroname_config.json'
-if not os.path.isfile(config_path):  # Create sample config
-    open(config_path, "w").write(
-        json.dumps({'site': 'site', 'zeronet_path': '/home/zeronet', 'privatekey': '', 'lastprocessed': 223910}, indent=2)
-    )
-    print("* Example config written to %s" % config_path)
-    sys.exit(0)
-
-config = json.load(open(config_path))
-names_path = "%s/data/%s/data/names.json" % (config["zeronet_path"], config["site"])
-os.chdir(config["zeronet_path"])  # Change working dir - tells script where Zeronet install is.
-
-# Parameters to sign and publish
-command_sign_publish = [sys.executable, "zeronet.py", "siteSign", config["site"], config["privatekey"], "--publish"]
-if sys.platform == 'win32':
-    command_sign_publish = ['"%s"' % param for param in command_sign_publish]
-
-# Initialize rpc connection
-rpc_auth, rpc_timeout = initRpc(namecoin_location + "namecoin.conf")
-rpc = AuthServiceProxy(rpc_auth, timeout=rpc_timeout)
-
-node_version = rpc.getnetworkinfo()['version']
-
-while 1:
-    try:
-        time.sleep(1)
-        if node_version < 160000 :
-            last_block = int(rpc.getinfo()["blocks"])
-        else:
-            last_block = int(rpc.getblockchaininfo()["blocks"])
-        break # Connection succeeded
-    except socket.timeout:  # Timeout
-        print(".", end=' ')
-        sys.stdout.flush()
-    except Exception as err:
-        print("Exception", err.__class__, err)
-        time.sleep(5)
-        rpc = AuthServiceProxy(rpc_auth, timeout=rpc_timeout)
-
-if not config["lastprocessed"]:  # First startup: Start processing from last block
-    config["lastprocessed"] = last_block
-
-
-print("- Testing domain parsing...")
-assert processBlock(223911, test=True) # Testing zeronetwork.bit
-assert processBlock(227052, test=True) # Testing brainwallets.bit
-assert not processBlock(236824, test=True) # Utf8 domain name (invalid should skip)
-assert not processBlock(236752, test=True) # Uppercase domain (invalid should skip)
-assert processBlock(236870, test=True) # Encoded domain (should pass)
-assert processBlock(438317, test=True) # Testing namecoin standard artifaxradio.bit (should pass)
-# sys.exit(0)
-
-print("- Parsing skipped blocks...")
-should_publish = False
-for block_id in range(config["lastprocessed"], last_block + 1):
-    if processBlock(block_id):
-        should_publish = True
-config["lastprocessed"] = last_block
-
-if should_publish:
-    publish()
-
-while 1:
-    print("- Waiting for new block")
-    sys.stdout.flush()
-    while 1:
-        try:
-            time.sleep(1)
-            if node_version < 160000 :
-                rpc.waitforblock()
-            else:
-                rpc.waitfornewblock()
-            print("Found")
-            break  # Block found
-        except socket.timeout:  # Timeout
-            print(".", end=' ')
-            sys.stdout.flush()
-        except Exception as err:
-            print("Exception", err.__class__, err)
-            time.sleep(5)
-            rpc = AuthServiceProxy(rpc_auth, timeout=rpc_timeout)
-
-    if node_version < 160000 :
-        last_block = int(rpc.getinfo()["blocks"])
-    else:
-        last_block = int(rpc.getblockchaininfo()["blocks"])
-    should_publish = False
-    for block_id in range(config["lastprocessed"] + 1, last_block + 1):
-        if processBlock(block_id):
-            should_publish = True
-
-    config["lastprocessed"] = last_block
-    open(config_path, "w").write(json.dumps(config, indent=2))
-
-    if should_publish:
-        publish()
diff --git a/plugins/__init__.py b/plugins/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/plugins/disabled-Bootstrapper/BootstrapperDb.py b/plugins/disabled-Bootstrapper/BootstrapperDb.py
deleted file mode 100644
index 0866dc3e..00000000
--- a/plugins/disabled-Bootstrapper/BootstrapperDb.py
+++ /dev/null
@@ -1,156 +0,0 @@
-import time
-import re
-
-import gevent
-
-from Config import config
-from Db import Db
-from util import helper
-
-
-class BootstrapperDb(Db.Db):
-    def __init__(self):
-        self.version = 7
-        self.hash_ids = {}  # hash -> id cache
-        super(BootstrapperDb, self).__init__({"db_name": "Bootstrapper"}, "%s/bootstrapper.db" % config.data_dir)
-        self.foreign_keys = True
-        self.checkTables()
-        self.updateHashCache()
-        gevent.spawn(self.cleanup)
-
-    def cleanup(self):
-        while 1:
-            time.sleep(4 * 60)
-            timeout = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time() - 60 * 40))
-            self.execute("DELETE FROM peer WHERE date_announced < ?", [timeout])
-
-    def updateHashCache(self):
-        res = self.execute("SELECT * FROM hash")
-        self.hash_ids = {row["hash"]: row["hash_id"] for row in res}
-        self.log.debug("Loaded %s hash_ids" % len(self.hash_ids))
-
-    def checkTables(self):
-        version = int(self.execute("PRAGMA user_version").fetchone()[0])
-        self.log.debug("Db version: %s, needed: %s" % (version, self.version))
-        if version < self.version:
-            self.createTables()
-        else:
-            self.execute("VACUUM")
-
-    def createTables(self):
-        # Delete all tables
-        self.execute("PRAGMA writable_schema = 1")
-        self.execute("DELETE FROM sqlite_master WHERE type IN ('table', 'index', 'trigger')")
-        self.execute("PRAGMA writable_schema = 0")
-        self.execute("VACUUM")
-        self.execute("PRAGMA INTEGRITY_CHECK")
-        # Create new tables
-        self.execute("""
-            CREATE TABLE peer (
-                peer_id        INTEGER PRIMARY KEY ASC AUTOINCREMENT NOT NULL UNIQUE,
-                type           TEXT,
-                address        TEXT,
-                port           INTEGER NOT NULL,
-                date_added     DATETIME DEFAULT (CURRENT_TIMESTAMP),
-                date_announced DATETIME DEFAULT (CURRENT_TIMESTAMP)
-            );
-        """)
-        self.execute("CREATE UNIQUE INDEX peer_key ON peer (address, port);")
-
-        self.execute("""
-            CREATE TABLE peer_to_hash (
-                peer_to_hash_id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL,
-                peer_id         INTEGER REFERENCES peer (peer_id) ON DELETE CASCADE,
-                hash_id         INTEGER REFERENCES hash (hash_id)
-            );
-        """)
-        self.execute("CREATE INDEX peer_id ON peer_to_hash (peer_id);")
-        self.execute("CREATE INDEX hash_id ON peer_to_hash (hash_id);")
-
-        self.execute("""
-            CREATE TABLE hash (
-                hash_id    INTEGER  PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL,
-                hash       BLOB     UNIQUE NOT NULL,
-                date_added DATETIME DEFAULT (CURRENT_TIMESTAMP)
-            );
-        """)
-        self.execute("PRAGMA user_version = %s" % self.version)
-
-    def getHashId(self, hash):
-        if hash not in self.hash_ids:
-            self.log.debug("New hash: %s" % repr(hash))
-            res = self.execute("INSERT OR IGNORE INTO hash ?", {"hash": hash})
-            self.hash_ids[hash] = res.lastrowid
-        return self.hash_ids[hash]
-
-    def peerAnnounce(self, ip_type, address, port=None, hashes=[], onion_signed=False, delete_missing_hashes=False):
-        hashes_ids_announced = []
-        for hash in hashes:
-            hashes_ids_announced.append(self.getHashId(hash))
-
-        # Check user
-        res = self.execute("SELECT peer_id FROM peer WHERE ? LIMIT 1", {"address": address, "port": port})
-
-        user_row = res.fetchone()
-        now = time.strftime("%Y-%m-%d %H:%M:%S")
-        if user_row:
-            peer_id = user_row["peer_id"]
-            self.execute("UPDATE peer SET date_announced = ? WHERE peer_id = ?", (now, peer_id))
-        else:
-            self.log.debug("New peer: %s signed: %s" % (address, onion_signed))
-            if ip_type == "onion" and not onion_signed:
-                return len(hashes)
-            res = self.execute("INSERT INTO peer ?", {"type": ip_type, "address": address, "port": port, "date_announced": now})
-            peer_id = res.lastrowid
-
-        # Check user's hashes
-        res = self.execute("SELECT * FROM peer_to_hash WHERE ?", {"peer_id": peer_id})
-        hash_ids_db = [row["hash_id"] for row in res]
-        if hash_ids_db != hashes_ids_announced:
-            hash_ids_added = set(hashes_ids_announced) - set(hash_ids_db)
-            hash_ids_removed = set(hash_ids_db) - set(hashes_ids_announced)
-            if ip_type != "onion" or onion_signed:
-                for hash_id in hash_ids_added:
-                    self.execute("INSERT INTO peer_to_hash ?", {"peer_id": peer_id, "hash_id": hash_id})
-                if hash_ids_removed and delete_missing_hashes:
-                    self.execute("DELETE FROM peer_to_hash WHERE ?", {"peer_id": peer_id, "hash_id": list(hash_ids_removed)})
-
-            return len(hash_ids_added) + len(hash_ids_removed)
-        else:
-            return 0
-
-    def peerList(self, hash, address=None, onions=[], port=None, limit=30, need_types=["ipv4", "onion"], order=True):
-        back = {"ipv4": [], "ipv6": [], "onion": []}
-        if limit == 0:
-            return back
-        hashid = self.getHashId(hash)
-
-        if order:
-            order_sql = "ORDER BY date_announced DESC"
-        else:
-            order_sql = ""
-        where_sql = "hash_id = :hashid"
-        if onions:
-            onions_escaped = ["'%s'" % re.sub("[^a-z0-9,]", "", onion) for onion in onions if type(onion) is str]
-            where_sql += " AND address NOT IN (%s)" % ",".join(onions_escaped)
-        elif address:
-            where_sql += " AND NOT (address = :address AND port = :port)"
-
-        query = """
-            SELECT type, address, port
-            FROM peer_to_hash
-            LEFT JOIN peer USING (peer_id)
-            WHERE %s
-            %s
-            LIMIT :limit
-        """ % (where_sql, order_sql)
-        res = self.execute(query, {"hashid": hashid, "address": address, "port": port, "limit": limit})
-
-        for row in res:
-            if row["type"] in need_types:
-                if row["type"] == "onion":
-                    packed = helper.packOnionAddress(row["address"], row["port"])
-                else:
-                    packed = helper.packAddress(str(row["address"]), row["port"])
-                back[row["type"]].append(packed)
-        return back
diff --git a/plugins/disabled-Bootstrapper/BootstrapperPlugin.py b/plugins/disabled-Bootstrapper/BootstrapperPlugin.py
deleted file mode 100644
index 59e7af7b..00000000
--- a/plugins/disabled-Bootstrapper/BootstrapperPlugin.py
+++ /dev/null
@@ -1,156 +0,0 @@
-import time
-
-from util import helper
-
-from Plugin import PluginManager
-from .BootstrapperDb import BootstrapperDb
-from Crypt import CryptRsa
-from Config import config
-
-if "db" not in locals().keys():  # Share during reloads
-    db = BootstrapperDb()
-
-
-@PluginManager.registerTo("FileRequest")
-class FileRequestPlugin(object):
-    def checkOnionSigns(self, onions, onion_signs, onion_sign_this):
-        if not onion_signs or len(onion_signs) != len(set(onions)):
-            return False
-
-        if time.time() - float(onion_sign_this) > 3 * 60:
-            return False  # Signed out of allowed 3 minutes
-
-        onions_signed = []
-        # Check onion signs
-        for onion_publickey, onion_sign in onion_signs.items():
-            if CryptRsa.verify(onion_sign_this.encode(), onion_publickey, onion_sign):
-                onions_signed.append(CryptRsa.publickeyToOnion(onion_publickey))
-            else:
-                break
-
-        # Check if the same onion addresses signed as the announced onces
-        if sorted(onions_signed) == sorted(set(onions)):
-            return True
-        else:
-            return False
-
-    def actionAnnounce(self, params):
-        time_started = time.time()
-        s = time.time()
-        # Backward compatibility
-        if "ip4" in params["add"]:
-            params["add"].append("ipv4")
-        if "ip4" in params["need_types"]:
-            params["need_types"].append("ipv4")
-
-        hashes = params["hashes"]
-
-        all_onions_signed = self.checkOnionSigns(params.get("onions", []), params.get("onion_signs"), params.get("onion_sign_this"))
-
-        time_onion_check = time.time() - s
-
-        ip_type = helper.getIpType(self.connection.ip)
-
-        if ip_type == "onion" or self.connection.ip in config.ip_local:
-            is_port_open = False
-        elif ip_type in params["add"]:
-            is_port_open = True
-        else:
-            is_port_open = False
-
-        s = time.time()
-        # Separatley add onions to sites or at once if no onions present
-        i = 0
-        onion_to_hash = {}
-        for onion in params.get("onions", []):
-            if onion not in onion_to_hash:
-                onion_to_hash[onion] = []
-            onion_to_hash[onion].append(hashes[i])
-            i += 1
-
-        hashes_changed = 0
-        for onion, onion_hashes in onion_to_hash.items():
-            hashes_changed += db.peerAnnounce(
-                ip_type="onion",
-                address=onion,
-                port=params["port"],
-                hashes=onion_hashes,
-                onion_signed=all_onions_signed
-            )
-        time_db_onion = time.time() - s
-
-        s = time.time()
-
-        if is_port_open:
-            hashes_changed += db.peerAnnounce(
-                ip_type=ip_type,
-                address=self.connection.ip,
-                port=params["port"],
-                hashes=hashes,
-                delete_missing_hashes=params.get("delete")
-            )
-        time_db_ip = time.time() - s
-
-        s = time.time()
-        # Query sites
-        back = {}
-        peers = []
-        if params.get("onions") and not all_onions_signed and hashes_changed:
-            back["onion_sign_this"] = "%.0f" % time.time()  # Send back nonce for signing
-
-        if len(hashes) > 500 or not hashes_changed:
-            limit = 5
-            order = False
-        else:
-            limit = 30
-            order = True
-        for hash in hashes:
-            if time.time() - time_started > 1:  # 1 sec limit on request
-                self.connection.log("Announce time limit exceeded after %s/%s sites" % (len(peers), len(hashes)))
-                break
-
-            hash_peers = db.peerList(
-                hash,
-                address=self.connection.ip, onions=list(onion_to_hash.keys()), port=params["port"],
-                limit=min(limit, params["need_num"]), need_types=params["need_types"], order=order
-            )
-            if "ip4" in params["need_types"]:  # Backward compatibility
-                hash_peers["ip4"] = hash_peers["ipv4"]
-                del(hash_peers["ipv4"])
-            peers.append(hash_peers)
-        time_peerlist = time.time() - s
-
-        back["peers"] = peers
-        self.connection.log(
-            "Announce %s sites (onions: %s, onion_check: %.3fs, db_onion: %.3fs, db_ip: %.3fs, peerlist: %.3fs, limit: %s)" %
-            (len(hashes), len(onion_to_hash), time_onion_check, time_db_onion, time_db_ip, time_peerlist, limit)
-        )
-        self.response(back)
-
-
-@PluginManager.registerTo("UiRequest")
-class UiRequestPlugin(object):
-    @helper.encodeResponse
-    def actionStatsBootstrapper(self):
-        self.sendHeader()
-
-        # Style
-        yield """
-        <style>
-         * { font-family: monospace; white-space: pre }
-         table td, table th { text-align: right; padding: 0px 10px }
-        </style>
-        """
-
-        hash_rows = db.execute("SELECT * FROM hash").fetchall()
-        for hash_row in hash_rows:
-            peer_rows = db.execute(
-                "SELECT * FROM peer LEFT JOIN peer_to_hash USING (peer_id) WHERE hash_id = :hash_id",
-                {"hash_id": hash_row["hash_id"]}
-            ).fetchall()
-
-            yield "<br>%s (added: %s, peers: %s)<br>" % (
-                str(hash_row["hash"]).encode().hex(), hash_row["date_added"], len(peer_rows)
-            )
-            for peer_row in peer_rows:
-                yield " - {type} {address}:{port} added: {date_added}, announced: {date_announced}<br>".format(**dict(peer_row))
diff --git a/plugins/disabled-Bootstrapper/Test/TestBootstrapper.py b/plugins/disabled-Bootstrapper/Test/TestBootstrapper.py
deleted file mode 100644
index 69bdc54c..00000000
--- a/plugins/disabled-Bootstrapper/Test/TestBootstrapper.py
+++ /dev/null
@@ -1,246 +0,0 @@
-import hashlib
-import os
-
-import pytest
-
-from Bootstrapper import BootstrapperPlugin
-from Bootstrapper.BootstrapperDb import BootstrapperDb
-from Peer import Peer
-from Crypt import CryptRsa
-from util import helper
-
-
-@pytest.fixture()
-def bootstrapper_db(request):
-    BootstrapperPlugin.db.close()
-    BootstrapperPlugin.db = BootstrapperDb()
-    BootstrapperPlugin.db.createTables()  # Reset db
-    BootstrapperPlugin.db.cur.logging = True
-
-    def cleanup():
-        BootstrapperPlugin.db.close()
-        os.unlink(BootstrapperPlugin.db.db_path)
-
-    request.addfinalizer(cleanup)
-    return BootstrapperPlugin.db
-
-
-@pytest.mark.usefixtures("resetSettings")
-class TestBootstrapper:
-    def testHashCache(self, file_server, bootstrapper_db):
-        ip_type = helper.getIpType(file_server.ip)
-        peer = Peer(file_server.ip, 1544, connection_server=file_server)
-        hash1 = hashlib.sha256(b"site1").digest()
-        hash2 = hashlib.sha256(b"site2").digest()
-        hash3 = hashlib.sha256(b"site3").digest()
-
-        # Verify empty result
-        res = peer.request("announce", {
-            "hashes": [hash1, hash2],
-            "port": 15441, "need_types": [ip_type], "need_num": 10, "add": [ip_type]
-        })
-
-        assert len(res["peers"][0][ip_type]) == 0  # Empty result
-
-        hash_ids_before = bootstrapper_db.hash_ids.copy()
-
-        bootstrapper_db.updateHashCache()
-
-        assert hash_ids_before == bootstrapper_db.hash_ids
-
-
-    def testBootstrapperDb(self, file_server, bootstrapper_db):
-        ip_type = helper.getIpType(file_server.ip)
-        peer = Peer(file_server.ip, 1544, connection_server=file_server)
-        hash1 = hashlib.sha256(b"site1").digest()
-        hash2 = hashlib.sha256(b"site2").digest()
-        hash3 = hashlib.sha256(b"site3").digest()
-
-        # Verify empty result
-        res = peer.request("announce", {
-            "hashes": [hash1, hash2],
-            "port": 15441, "need_types": [ip_type], "need_num": 10, "add": [ip_type]
-        })
-
-        assert len(res["peers"][0][ip_type]) == 0  # Empty result
-
-        # Verify added peer on previous request
-        bootstrapper_db.peerAnnounce(ip_type, file_server.ip_external, port=15441, hashes=[hash1, hash2], delete_missing_hashes=True)
-
-        res = peer.request("announce", {
-            "hashes": [hash1, hash2],
-            "port": 15441, "need_types": [ip_type], "need_num": 10, "add": [ip_type]
-        })
-        assert len(res["peers"][0][ip_type]) == 1
-        assert len(res["peers"][1][ip_type]) == 1
-
-        # hash2 deleted from 1.2.3.4
-        bootstrapper_db.peerAnnounce(ip_type, file_server.ip_external, port=15441, hashes=[hash1], delete_missing_hashes=True)
-        res = peer.request("announce", {
-            "hashes": [hash1, hash2],
-            "port": 15441, "need_types": [ip_type], "need_num": 10, "add": [ip_type]
-        })
-        assert len(res["peers"][0][ip_type]) == 1
-        assert len(res["peers"][1][ip_type]) == 0
-
-        # Announce 3 hash again
-        bootstrapper_db.peerAnnounce(ip_type, file_server.ip_external, port=15441, hashes=[hash1, hash2, hash3], delete_missing_hashes=True)
-        res = peer.request("announce", {
-            "hashes": [hash1, hash2, hash3],
-            "port": 15441, "need_types": [ip_type], "need_num": 10, "add": [ip_type]
-        })
-        assert len(res["peers"][0][ip_type]) == 1
-        assert len(res["peers"][1][ip_type]) == 1
-        assert len(res["peers"][2][ip_type]) == 1
-
-        # Single hash announce
-        res = peer.request("announce", {
-            "hashes": [hash1], "port": 15441, "need_types": [ip_type], "need_num": 10, "add": [ip_type]
-        })
-        assert len(res["peers"][0][ip_type]) == 1
-
-        # Test DB cleanup
-        assert [row[0] for row in bootstrapper_db.execute("SELECT address FROM peer").fetchall()] == [file_server.ip_external]  # 127.0.0.1 never get added to db
-
-        # Delete peers
-        bootstrapper_db.execute("DELETE FROM peer WHERE address = ?", [file_server.ip_external])
-        assert bootstrapper_db.execute("SELECT COUNT(*) AS num FROM peer_to_hash").fetchone()["num"] == 0
-
-        assert bootstrapper_db.execute("SELECT COUNT(*) AS num FROM hash").fetchone()["num"] == 3  # 3 sites
-        assert bootstrapper_db.execute("SELECT COUNT(*) AS num FROM peer").fetchone()["num"] == 0  # 0 peer
-
-    def testPassive(self, file_server, bootstrapper_db):
-        peer = Peer(file_server.ip, 1544, connection_server=file_server)
-        ip_type = helper.getIpType(file_server.ip)
-        hash1 = hashlib.sha256(b"hash1").digest()
-
-        bootstrapper_db.peerAnnounce(ip_type, address=None, port=15441, hashes=[hash1])
-        res = peer.request("announce", {
-            "hashes": [hash1], "port": 15441, "need_types": [ip_type], "need_num": 10, "add": []
-        })
-
-        assert len(res["peers"][0]["ipv4"]) == 0  # Empty result
-
-    def testAddOnion(self, file_server, site, bootstrapper_db, tor_manager):
-        onion1 = tor_manager.addOnion()
-        onion2 = tor_manager.addOnion()
-        peer = Peer(file_server.ip, 1544, connection_server=file_server)
-        hash1 = hashlib.sha256(b"site1").digest()
-        hash2 = hashlib.sha256(b"site2").digest()
-        hash3 = hashlib.sha256(b"site3").digest()
-
-        bootstrapper_db.peerAnnounce(ip_type="ipv4", address="1.2.3.4", port=1234, hashes=[hash1, hash2, hash3])
-        res = peer.request("announce", {
-            "onions": [onion1, onion1, onion2],
-            "hashes": [hash1, hash2, hash3], "port": 15441, "need_types": ["ipv4", "onion"], "need_num": 10, "add": ["onion"]
-        })
-        assert len(res["peers"][0]["ipv4"]) == 1
-
-        # Onion address not added yet
-        site_peers = bootstrapper_db.peerList(address="1.2.3.4", port=1234, hash=hash1)
-        assert len(site_peers["onion"]) == 0
-        assert "onion_sign_this" in res
-
-        # Sign the nonces
-        sign1 = CryptRsa.sign(res["onion_sign_this"].encode(), tor_manager.getPrivatekey(onion1))
-        sign2 = CryptRsa.sign(res["onion_sign_this"].encode(), tor_manager.getPrivatekey(onion2))
-
-        # Bad sign (different address)
-        res = peer.request("announce", {
-            "onions": [onion1], "onion_sign_this": res["onion_sign_this"],
-            "onion_signs": {tor_manager.getPublickey(onion2): sign2},
-            "hashes": [hash1], "port": 15441, "need_types": ["ipv4", "onion"], "need_num": 10, "add": ["onion"]
-        })
-        assert "onion_sign_this" in res
-        site_peers1 = bootstrapper_db.peerList(address="1.2.3.4", port=1234, hash=hash1)
-        assert len(site_peers1["onion"]) == 0  # Not added
-
-        # Bad sign (missing one)
-        res = peer.request("announce", {
-            "onions": [onion1, onion1, onion2], "onion_sign_this": res["onion_sign_this"],
-            "onion_signs": {tor_manager.getPublickey(onion1): sign1},
-            "hashes": [hash1, hash2, hash3], "port": 15441, "need_types": ["ipv4", "onion"], "need_num": 10, "add": ["onion"]
-        })
-        assert "onion_sign_this" in res
-        site_peers1 = bootstrapper_db.peerList(address="1.2.3.4", port=1234, hash=hash1)
-        assert len(site_peers1["onion"]) == 0  # Not added
-
-        # Good sign
-        res = peer.request("announce", {
-            "onions": [onion1, onion1, onion2], "onion_sign_this": res["onion_sign_this"],
-            "onion_signs": {tor_manager.getPublickey(onion1): sign1, tor_manager.getPublickey(onion2): sign2},
-            "hashes": [hash1, hash2, hash3], "port": 15441, "need_types": ["ipv4", "onion"], "need_num": 10, "add": ["onion"]
-        })
-        assert "onion_sign_this" not in res
-
-        # Onion addresses added
-        site_peers1 = bootstrapper_db.peerList(address="1.2.3.4", port=1234, hash=hash1)
-        assert len(site_peers1["onion"]) == 1
-        site_peers2 = bootstrapper_db.peerList(address="1.2.3.4", port=1234, hash=hash2)
-        assert len(site_peers2["onion"]) == 1
-        site_peers3 = bootstrapper_db.peerList(address="1.2.3.4", port=1234, hash=hash3)
-        assert len(site_peers3["onion"]) == 1
-
-        assert site_peers1["onion"][0] == site_peers2["onion"][0]
-        assert site_peers2["onion"][0] != site_peers3["onion"][0]
-        assert helper.unpackOnionAddress(site_peers1["onion"][0])[0] == onion1 + ".onion"
-        assert helper.unpackOnionAddress(site_peers2["onion"][0])[0] == onion1 + ".onion"
-        assert helper.unpackOnionAddress(site_peers3["onion"][0])[0] == onion2 + ".onion"
-
-        tor_manager.delOnion(onion1)
-        tor_manager.delOnion(onion2)
-
-    def testRequestPeers(self, file_server, site, bootstrapper_db, tor_manager):
-        site.connection_server = file_server
-        file_server.tor_manager = tor_manager
-        hash = hashlib.sha256(site.address.encode()).digest()
-
-        # Request peers from tracker
-        assert len(site.peers) == 0
-        bootstrapper_db.peerAnnounce(ip_type="ipv4", address="1.2.3.4", port=1234, hashes=[hash])
-        site.announcer.announceTracker("zero://%s:%s" % (file_server.ip, file_server.port))
-        assert len(site.peers) == 1
-
-        # Test onion address store
-        bootstrapper_db.peerAnnounce(ip_type="onion", address="bka4ht2bzxchy44r", port=1234, hashes=[hash], onion_signed=True)
-        site.announcer.announceTracker("zero://%s:%s" % (file_server.ip, file_server.port))
-        assert len(site.peers) == 2
-        assert "bka4ht2bzxchy44r.onion:1234" in site.peers
-
-    @pytest.mark.slow
-    def testAnnounce(self, file_server, tor_manager):
-        file_server.tor_manager = tor_manager
-        hash1 = hashlib.sha256(b"1Nekos4fiBqfcazyG1bAxdBT5oBvA76Z").digest()
-        hash2 = hashlib.sha256(b"1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr").digest()
-        peer = Peer("zero.booth.moe", 443, connection_server=file_server)
-        assert peer.request("ping")
-        peer = Peer("boot3rdez4rzn36x.onion", 15441, connection_server=file_server)
-        assert peer.request("ping")
-        res = peer.request("announce", {
-            "hashes": [hash1, hash2],
-            "port": 15441, "need_types": ["ip4", "onion"], "need_num": 100, "add": [""]
-        })
-
-        assert res
-
-    def testBackwardCompatibility(self, file_server, bootstrapper_db):
-        peer = Peer(file_server.ip, 1544, connection_server=file_server)
-        hash1 = hashlib.sha256(b"site1").digest()
-
-        bootstrapper_db.peerAnnounce("ipv4", file_server.ip_external, port=15441, hashes=[hash1], delete_missing_hashes=True)
-
-        # Test with ipv4 need type
-        res = peer.request("announce", {
-            "hashes": [hash1],
-            "port": 15441, "need_types": ["ipv4"], "need_num": 10, "add": []
-        })
-
-        assert len(res["peers"][0]["ipv4"]) == 1
-
-        # Test with ip4 need type
-        res = peer.request("announce", {
-            "hashes": [hash1],
-            "port": 15441, "need_types": ["ip4"], "need_num": 10, "add": []
-        })
-
-        assert len(res["peers"][0]["ip4"]) == 1
diff --git a/plugins/disabled-Bootstrapper/Test/conftest.py b/plugins/disabled-Bootstrapper/Test/conftest.py
deleted file mode 100644
index 8c1df5b2..00000000
--- a/plugins/disabled-Bootstrapper/Test/conftest.py
+++ /dev/null
@@ -1 +0,0 @@
-from src.Test.conftest import *
\ No newline at end of file
diff --git a/plugins/disabled-Bootstrapper/Test/pytest.ini b/plugins/disabled-Bootstrapper/Test/pytest.ini
deleted file mode 100644
index 8ee21268..00000000
--- a/plugins/disabled-Bootstrapper/Test/pytest.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[pytest]
-python_files = Test*.py
-addopts = -rsxX -v --durations=6
-markers =
-    slow: mark a tests as slow.
-    webtest: mark a test as a webtest.
\ No newline at end of file
diff --git a/plugins/disabled-Bootstrapper/__init__.py b/plugins/disabled-Bootstrapper/__init__.py
deleted file mode 100644
index cce30eea..00000000
--- a/plugins/disabled-Bootstrapper/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import BootstrapperPlugin
\ No newline at end of file
diff --git a/plugins/disabled-Bootstrapper/plugin_info.json b/plugins/disabled-Bootstrapper/plugin_info.json
deleted file mode 100644
index 06915d4d..00000000
--- a/plugins/disabled-Bootstrapper/plugin_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"name": "Bootstrapper",
-	"description": "Add BitTorrent tracker server like features to your ZeroNet client.",
-	"default": "disabled"
-}
\ No newline at end of file
diff --git a/plugins/disabled-Dnschain/SiteManagerPlugin.py b/plugins/disabled-Dnschain/SiteManagerPlugin.py
deleted file mode 100644
index 8b9508f1..00000000
--- a/plugins/disabled-Dnschain/SiteManagerPlugin.py
+++ /dev/null
@@ -1,153 +0,0 @@
-import logging, json, os, re, sys, time
-import gevent
-from Plugin import PluginManager
-from Config import config
-from util import Http
-from Debug import Debug
-
-allow_reload = False # No reload supported
-
-log = logging.getLogger("DnschainPlugin")
-
-@PluginManager.registerTo("SiteManager")
-class SiteManagerPlugin(object):
-	dns_cache_path = "%s/dns_cache.json" % config.data_dir
-	dns_cache = None
-
-	# Checks if its a valid address
-	def isAddress(self, address):
-		if self.isDomain(address): 
-			return True
-		else:
-			return super(SiteManagerPlugin, self).isAddress(address)
-
-
-	# Return: True if the address is domain
-	def isDomain(self, address):
-		return re.match(r"(.*?)([A-Za-z0-9_-]+\.[A-Za-z0-9]+)$", address)
-
-
-	# Load dns entries from data/dns_cache.json
-	def loadDnsCache(self):
-		if os.path.isfile(self.dns_cache_path):
-			self.dns_cache = json.load(open(self.dns_cache_path))
-		else:
-			self.dns_cache = {}
-		log.debug("Loaded dns cache, entries: %s" % len(self.dns_cache))
-
-
-	# Save dns entries to data/dns_cache.json
-	def saveDnsCache(self):
-		json.dump(self.dns_cache, open(self.dns_cache_path, "wb"), indent=2)
-
-
-	# Resolve domain using dnschain.net
-	# Return: The address or None
-	def resolveDomainDnschainNet(self, domain):
-		try:
-			match = self.isDomain(domain)
-			sub_domain = match.group(1).strip(".")
-			top_domain = match.group(2)
-			if not sub_domain: sub_domain = "@"
-			address = None
-			with gevent.Timeout(5, Exception("Timeout: 5s")):
-				res = Http.get("https://api.dnschain.net/v1/namecoin/key/%s" % top_domain).read()
-				data = json.loads(res)["data"]["value"]
-				if "zeronet" in data:
-					for key, val in data["zeronet"].items():
-						self.dns_cache[key+"."+top_domain] = [val, time.time()+60*60*5] # Cache for 5 hours
-					self.saveDnsCache()
-					return data["zeronet"].get(sub_domain)
-			# Not found
-			return address
-		except Exception as err:
-			log.debug("Dnschain.net %s resolve error: %s" % (domain, Debug.formatException(err)))
-
-
-	# Resolve domain using dnschain.info
-	# Return: The address or None
-	def resolveDomainDnschainInfo(self, domain):
-		try:
-			match = self.isDomain(domain)
-			sub_domain = match.group(1).strip(".")
-			top_domain = match.group(2)
-			if not sub_domain: sub_domain = "@"
-			address = None
-			with gevent.Timeout(5, Exception("Timeout: 5s")):
-				res = Http.get("https://dnschain.info/bit/d/%s" % re.sub(r"\.bit$", "", top_domain)).read()
-				data = json.loads(res)["value"]
-				for key, val in data["zeronet"].items():
-					self.dns_cache[key+"."+top_domain] = [val, time.time()+60*60*5] # Cache for 5 hours
-				self.saveDnsCache()
-				return data["zeronet"].get(sub_domain)
-			# Not found
-			return address
-		except Exception as err:
-			log.debug("Dnschain.info %s resolve error: %s" % (domain, Debug.formatException(err)))
-
-
-	# Resolve domain
-	# Return: The address or None
-	def resolveDomain(self, domain):
-		domain = domain.lower()
-		if self.dns_cache == None:
-			self.loadDnsCache()
-		if domain.count(".") < 2: # Its a topleved request, prepend @. to it
-			domain = "@."+domain
-
-		domain_details = self.dns_cache.get(domain)
-		if domain_details and time.time() < domain_details[1]: # Found in cache and its not expired
-			return domain_details[0]
-		else:
-			# Resovle dns using dnschain
-			thread_dnschain_info = gevent.spawn(self.resolveDomainDnschainInfo, domain)
-			thread_dnschain_net = gevent.spawn(self.resolveDomainDnschainNet, domain)
-			gevent.joinall([thread_dnschain_net, thread_dnschain_info]) # Wait for finish
-
-			if thread_dnschain_info.value and thread_dnschain_net.value: # Booth successfull
-				if thread_dnschain_info.value == thread_dnschain_net.value: # Same returned value
-					return thread_dnschain_info.value 
-				else:
-					log.error("Dns %s missmatch: %s != %s" % (domain, thread_dnschain_info.value, thread_dnschain_net.value))
-
-			# Problem during resolve
-			if domain_details: # Resolve failed, but we have it in the cache
-				domain_details[1] = time.time()+60*60 # Dont try again for 1 hour
-				return domain_details[0]
-			else: # Not found in cache
-				self.dns_cache[domain] = [None, time.time()+60] # Don't check again for 1 min
-				return None
-
-
-	# Return or create site and start download site files
-	# Return: Site or None if dns resolve failed
-	def need(self, address, all_file=True):
-		if self.isDomain(address): # Its looks like a domain
-			address_resolved = self.resolveDomain(address)
-			if address_resolved:
-				address = address_resolved
-			else:
-				return None
-		
-		return super(SiteManagerPlugin, self).need(address, all_file)
-
-
-	# Return: Site object or None if not found
-	def get(self, address):
-		if self.sites == None: # Not loaded yet
-			self.load()
-		if self.isDomain(address): # Its looks like a domain
-			address_resolved = self.resolveDomain(address)
-			if address_resolved: # Domain found
-				site = self.sites.get(address_resolved)
-				if site:
-					site_domain = site.settings.get("domain")
-					if site_domain != address:
-						site.settings["domain"] = address
-			else: # Domain not found
-				site = self.sites.get(address)
-
-		else: # Access by site address
-			site = self.sites.get(address)
-		return site
-
diff --git a/plugins/disabled-Dnschain/UiRequestPlugin.py b/plugins/disabled-Dnschain/UiRequestPlugin.py
deleted file mode 100644
index 8ab9d5c5..00000000
--- a/plugins/disabled-Dnschain/UiRequestPlugin.py
+++ /dev/null
@@ -1,34 +0,0 @@
-import re
-from Plugin import PluginManager
-
-@PluginManager.registerTo("UiRequest")
-class UiRequestPlugin(object):
-	def __init__(self, server = None):
-		from Site import SiteManager
-		self.site_manager = SiteManager.site_manager
-		super(UiRequestPlugin, self).__init__(server)
-
-
-	# Media request
-	def actionSiteMedia(self, path):
-		match = re.match(r"/media/(?P<address>[A-Za-z0-9-]+\.[A-Za-z0-9\.-]+)(?P<inner_path>/.*|$)", path)
-		if match: # Its a valid domain, resolve first
-			domain = match.group("address")
-			address = self.site_manager.resolveDomain(domain)
-			if address:
-				path = "/media/"+address+match.group("inner_path")
-		return super(UiRequestPlugin, self).actionSiteMedia(path) # Get the wrapper frame output
-
-
-	# Is mediarequest allowed from that referer
-	def isMediaRequestAllowed(self, site_address, referer):
-		referer_path = re.sub("http[s]{0,1}://.*?/", "/", referer).replace("/media", "") # Remove site address
-		referer_site_address = re.match(r"/(?P<address>[A-Za-z0-9\.-]+)(?P<inner_path>/.*|$)", referer_path).group("address")
-
-		if referer_site_address == site_address: # Referer site address as simple address
-			return True
-		elif self.site_manager.resolveDomain(referer_site_address) == site_address: # Referer site address as dns
-			return True
-		else: # Invalid referer
-			return False
-
diff --git a/plugins/disabled-Dnschain/__init__.py b/plugins/disabled-Dnschain/__init__.py
deleted file mode 100644
index 2b36af5d..00000000
--- a/plugins/disabled-Dnschain/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# This plugin is experimental, if you really want to enable uncomment the following lines:
-# import DnschainPlugin
-# import SiteManagerPlugin
\ No newline at end of file
diff --git a/plugins/disabled-DonationMessage/DonationMessagePlugin.py b/plugins/disabled-DonationMessage/DonationMessagePlugin.py
deleted file mode 100644
index 8cf0d541..00000000
--- a/plugins/disabled-DonationMessage/DonationMessagePlugin.py
+++ /dev/null
@@ -1,22 +0,0 @@
-import re
-from Plugin import PluginManager
-
-# Warning: If you modify the donation address then renmae the plugin's directory to "MyDonationMessage" to prevent the update script overwrite
-
-
-@PluginManager.registerTo("UiRequest")
-class UiRequestPlugin(object):
-    # Inject a donation message to every page top right corner
-    def renderWrapper(self, *args, **kwargs):
-        body = super(UiRequestPlugin, self).renderWrapper(*args, **kwargs)  # Get the wrapper frame output
-
-        inject_html = """
-            <style>
-             #donation_message { position: absolute; bottom: 0px; right: 20px; padding: 7px; font-family: Arial; font-size: 11px }
-            </style>
-            <a id='donation_message' href='https://blockchain.info/address/1QDhxQ6PraUZa21ET5fYUCPgdrwBomnFgX' target='_blank'>Please donate to help to keep this ZeroProxy alive</a>
-            </body>
-            </html>
-        """
-
-        return re.sub(r"</body>\s*</html>\s*$", inject_html, body)
diff --git a/plugins/disabled-DonationMessage/__init__.py b/plugins/disabled-DonationMessage/__init__.py
deleted file mode 100644
index 1d4b47c3..00000000
--- a/plugins/disabled-DonationMessage/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import DonationMessagePlugin
diff --git a/plugins/disabled-Multiuser/MultiuserPlugin.py b/plugins/disabled-Multiuser/MultiuserPlugin.py
deleted file mode 100644
index 799c3337..00000000
--- a/plugins/disabled-Multiuser/MultiuserPlugin.py
+++ /dev/null
@@ -1,275 +0,0 @@
-import re
-import sys
-import json
-
-from Config import config
-from Plugin import PluginManager
-from Crypt import CryptBitcoin
-from . import UserPlugin
-from util.Flag import flag
-from Translate import translate as _
-
-# We can only import plugin host clases after the plugins are loaded
-@PluginManager.afterLoad
-def importPluginnedClasses():
-    global UserManager
-    from User import UserManager
-
-try:
-    local_master_addresses = set(json.load(open("%s/users.json" % config.data_dir)).keys())  # Users in users.json
-except Exception as err:
-    local_master_addresses = set()
-
-
-@PluginManager.registerTo("UiRequest")
-class UiRequestPlugin(object):
-    def __init__(self, *args, **kwargs):
-        self.user_manager = UserManager.user_manager
-        super(UiRequestPlugin, self).__init__(*args, **kwargs)
-
-    # Create new user and inject user welcome message if necessary
-    # Return: Html body also containing the injection
-    def actionWrapper(self, path, extra_headers=None):
-
-        match = re.match("/(?P<address>[A-Za-z0-9\._-]+)(?P<inner_path>/.*|$)", path)
-        if not match:
-            return False
-
-        inner_path = match.group("inner_path").lstrip("/")
-        html_request = "." not in inner_path or inner_path.endswith(".html")  # Only inject html to html requests
-
-        user_created = False
-        if html_request:
-            user = self.getCurrentUser()  # Get user from cookie
-            if not user:  # No user found by cookie
-                user = self.user_manager.create()
-                user_created = True
-        else:
-            user = None
-
-        # Disable new site creation if --multiuser_no_new_sites enabled
-        if config.multiuser_no_new_sites:
-            path_parts = self.parsePath(path)
-            if not self.server.site_manager.get(match.group("address")) and (not user or user.master_address not in local_master_addresses):
-                self.sendHeader(404)
-                return self.formatError("Not Found", "Adding new sites disabled on this proxy", details=False)
-
-        if user_created:
-            if not extra_headers:
-                extra_headers = {}
-            extra_headers['Set-Cookie'] = "master_address=%s;path=/;max-age=2592000;" % user.master_address  # = 30 days
-
-        loggedin = self.get.get("login") == "done"
-
-        back_generator = super(UiRequestPlugin, self).actionWrapper(path, extra_headers)  # Get the wrapper frame output
-
-        if not back_generator:  # Wrapper error or not string returned, injection not possible
-            return False
-
-        elif loggedin:
-            back = next(back_generator)
-            inject_html = """
-                <!-- Multiser plugin -->
-                <script nonce="{script_nonce}">
-                 setTimeout(function() {
-                    zeroframe.cmd("wrapperNotification", ["done", "{message}<br><small>You have been logged in successfully</small>", 5000])
-                 }, 1000)
-                </script>
-                </body>
-                </html>
-            """.replace("\t", "")
-            if user.master_address in local_master_addresses:
-                message = "Hello master!"
-            else:
-                message = "Hello again!"
-            inject_html = inject_html.replace("{message}", message)
-            inject_html = inject_html.replace("{script_nonce}", self.getScriptNonce())
-            return iter([re.sub(b"</body>\s*</html>\s*$", inject_html.encode(), back)])  # Replace the </body></html> tags with the injection
-
-        else:  # No injection necessary
-            return back_generator
-
-    # Get the current user based on request's cookies
-    # Return: User object or None if no match
-    def getCurrentUser(self):
-        cookies = self.getCookies()
-        user = None
-        if "master_address" in cookies:
-            users = self.user_manager.list()
-            user = users.get(cookies["master_address"])
-        return user
-
-
-@PluginManager.registerTo("UiWebsocket")
-class UiWebsocketPlugin(object):
-    def __init__(self, *args, **kwargs):
-        if config.multiuser_no_new_sites:
-            flag.no_multiuser(self.actionMergerSiteAdd)
-
-        super(UiWebsocketPlugin, self).__init__(*args, **kwargs)
-
-    # Let the page know we running in multiuser mode
-    def formatServerInfo(self):
-        server_info = super(UiWebsocketPlugin, self).formatServerInfo()
-        server_info["multiuser"] = True
-        if "ADMIN" in self.site.settings["permissions"]:
-            server_info["master_address"] = self.user.master_address
-            is_multiuser_admin = config.multiuser_local or self.user.master_address in local_master_addresses
-            server_info["multiuser_admin"] = is_multiuser_admin
-        return server_info
-
-    # Show current user's master seed
-    @flag.admin
-    def actionUserShowMasterSeed(self, to):
-        message = "<b style='padding-top: 5px; display: inline-block'>Your unique private key:</b>"
-        message += "<div style='font-size: 84%%; background-color: #FFF0AD; padding: 5px 8px; margin: 9px 0px'>%s</div>" % self.user.master_seed
-        message += "<small>(Save it, you can access your account using this information)</small>"
-        self.cmd("notification", ["info", message])
-
-    # Logout user
-    @flag.admin
-    def actionUserLogout(self, to):
-        message = "<b>You have been logged out.</b> <a href='#Login' class='button' id='button_notification'>Login to another account</a>"
-        self.cmd("notification", ["done", message, 1000000])  # 1000000 = Show ~forever :)
-
-        script = "document.cookie = 'master_address=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/';"
-        script += "$('#button_notification').on('click', function() { zeroframe.cmd(\"userLoginForm\", []); });"
-        self.cmd("injectScript", script)
-        # Delete from user_manager
-        user_manager = UserManager.user_manager
-        if self.user.master_address in user_manager.users:
-            if not config.multiuser_local:
-                del user_manager.users[self.user.master_address]
-            self.response(to, "Successful logout")
-        else:
-            self.response(to, "User not found")
-
-    @flag.admin
-    def actionUserSet(self, to, master_address):
-        user_manager = UserManager.user_manager
-        user = user_manager.get(master_address)
-        if not user:
-            raise Exception("No user found")
-
-        script = "document.cookie = 'master_address=%s;path=/;max-age=2592000;';" % master_address
-        script += "zeroframe.cmd('wrapperReload', ['login=done']);"
-        self.cmd("notification", ["done", "Successful login, reloading page..."])
-        self.cmd("injectScript", script)
-
-        self.response(to, "ok")
-
-    @flag.admin
-    def actionUserSelectForm(self, to):
-        if not config.multiuser_local:
-            raise Exception("Only allowed in multiuser local mode")
-        user_manager = UserManager.user_manager
-        body = "<span style='padding-bottom: 5px; display: inline-block'>" + "Change account:" + "</span>"
-        for master_address, user in user_manager.list().items():
-            is_active = self.user.master_address == master_address
-            if user.certs:
-                first_cert = next(iter(user.certs.keys()))
-                title = "%s@%s" % (user.certs[first_cert]["auth_user_name"], first_cert)
-            else:
-                title = user.master_address
-                if len(user.sites) < 2 and not is_active:  # Avoid listing ad-hoc created users
-                    continue
-            if is_active:
-                css_class = "active"
-            else:
-                css_class = "noclass"
-            body += "<a href='#Select+user' class='select select-close user %s' title='%s'>%s</a>" % (css_class, user.master_address, title)
-
-        script = """
-             $(".notification .select.user").on("click", function() {
-                $(".notification .select").removeClass('active')
-                zeroframe.response(%s, this.title)
-                return false
-             })
-        """ % self.next_message_id
-
-        self.cmd("notification", ["ask", body], lambda master_address: self.actionUserSet(to, master_address))
-        self.cmd("injectScript", script)
-
-    # Show login form
-    def actionUserLoginForm(self, to):
-        self.cmd("prompt", ["<b>Login</b><br>Your private key:", "password", "Login"], self.responseUserLogin)
-
-    # Login form submit
-    def responseUserLogin(self, master_seed):
-        user_manager = UserManager.user_manager
-        user = user_manager.get(CryptBitcoin.privatekeyToAddress(master_seed))
-        if not user:
-            user = user_manager.create(master_seed=master_seed)
-        if user.master_address:
-            script = "document.cookie = 'master_address=%s;path=/;max-age=2592000;';" % user.master_address
-            script += "zeroframe.cmd('wrapperReload', ['login=done']);"
-            self.cmd("notification", ["done", "Successful login, reloading page..."])
-            self.cmd("injectScript", script)
-        else:
-            self.cmd("notification", ["error", "Error: Invalid master seed"])
-            self.actionUserLoginForm(0)
-
-    def hasCmdPermission(self, cmd):
-        flags = flag.db.get(self.getCmdFuncName(cmd), ())
-        is_public_proxy_user = not config.multiuser_local and self.user.master_address not in local_master_addresses
-        if is_public_proxy_user and "no_multiuser" in flags:
-            self.cmd("notification", ["info", _("This function ({cmd}) is disabled on this proxy!")])
-            return False
-        else:
-            return super(UiWebsocketPlugin, self).hasCmdPermission(cmd)
-
-    def actionCertAdd(self, *args, **kwargs):
-        super(UiWebsocketPlugin, self).actionCertAdd(*args, **kwargs)
-        master_seed = self.user.master_seed
-        message = """
-            <style>
-            .masterseed {
-                font-size: 85%; background-color: #FFF0AD; padding: 5px 8px; margin: 9px 0px; width: 100%;
-                box-sizing: border-box; border: 0px; text-align: center; cursor: pointer;
-            }
-            </style>
-            <b>Hello, welcome to ZeroProxy!</b><div style='margin-top: 8px'>A new, unique account created for you:</div>
-            <input type='text' class='masterseed' id='button_notification_masterseed' value='Click here to show' readonly/>
-            <div style='text-align: center; font-size: 85%; margin-bottom: 10px;'>
-             or <a href='#Download' id='button_notification_download'
-             class='masterseed_download' download='zeronet_private_key.backup'>Download backup as text file</a>
-            </div>
-            <div>
-             This is your private key, <b>save it</b>, so you can login next time.<br>
-             <b>Warning: Without this key, your account will be lost forever!</b>
-            </div><br>
-            <a href='#' class='button' style='margin-left: 0px'>Ok, Saved it!</a><br><br>
-            <small>This site allows you to browse ZeroNet content, but if you want to secure your account <br>
-            and help to keep the network alive, then please run your own <a href='https://zeronet.io' target='_blank'>ZeroNet client</a>.</small>
-        """
-
-        self.cmd("notification", ["info", message])
-
-        script = """
-            $("#button_notification_masterseed").on("click", function() {
-                this.value = "{master_seed}"; this.setSelectionRange(0,100);
-            })
-            $("#button_notification_download").on("mousedown", function() {
-                this.href = window.URL.createObjectURL(new Blob(["ZeroNet user master seed:\\r\\n{master_seed}"]))
-            })
-        """.replace("{master_seed}", master_seed)
-        self.cmd("injectScript", script)
-
-    def actionPermissionAdd(self, to, permission):
-        is_public_proxy_user = not config.multiuser_local and self.user.master_address not in local_master_addresses
-        if permission == "NOSANDBOX" and is_public_proxy_user:
-            self.cmd("notification", ["info", "You can't disable sandbox on this proxy!"])
-            self.response(to, {"error": "Denied by proxy"})
-            return False
-        else:
-            return super(UiWebsocketPlugin, self).actionPermissionAdd(to, permission)
-
-
-@PluginManager.registerTo("ConfigPlugin")
-class ConfigPlugin(object):
-    def createArguments(self):
-        group = self.parser.add_argument_group("Multiuser plugin")
-        group.add_argument('--multiuser_local', help="Enable unsafe Ui functions and write users to disk", action='store_true')
-        group.add_argument('--multiuser_no_new_sites', help="Denies adding new sites by normal users", action='store_true')
-
-        return super(ConfigPlugin, self).createArguments()
diff --git a/plugins/disabled-Multiuser/Test/TestMultiuser.py b/plugins/disabled-Multiuser/Test/TestMultiuser.py
deleted file mode 100644
index b8ff4267..00000000
--- a/plugins/disabled-Multiuser/Test/TestMultiuser.py
+++ /dev/null
@@ -1,14 +0,0 @@
-import pytest
-import json
-from Config import config
-from User import UserManager
-
-@pytest.mark.usefixtures("resetSettings")
-@pytest.mark.usefixtures("resetTempSettings")
-class TestMultiuser:
-    def testMemorySave(self, user):
-        # It should not write users to disk
-        users_before = open("%s/users.json" % config.data_dir).read()
-        user = UserManager.user_manager.create()
-        user.save()
-        assert open("%s/users.json" % config.data_dir).read() == users_before
diff --git a/plugins/disabled-Multiuser/Test/conftest.py b/plugins/disabled-Multiuser/Test/conftest.py
deleted file mode 100644
index 634e66e2..00000000
--- a/plugins/disabled-Multiuser/Test/conftest.py
+++ /dev/null
@@ -1 +0,0 @@
-from src.Test.conftest import *
diff --git a/plugins/disabled-Multiuser/Test/pytest.ini b/plugins/disabled-Multiuser/Test/pytest.ini
deleted file mode 100644
index d09210d1..00000000
--- a/plugins/disabled-Multiuser/Test/pytest.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[pytest]
-python_files = Test*.py
-addopts = -rsxX -v --durations=6
-markers =
-    webtest: mark a test as a webtest.
\ No newline at end of file
diff --git a/plugins/disabled-Multiuser/UserPlugin.py b/plugins/disabled-Multiuser/UserPlugin.py
deleted file mode 100644
index 3c9ebae8..00000000
--- a/plugins/disabled-Multiuser/UserPlugin.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from Config import config
-from Plugin import PluginManager
-
-allow_reload = False
-
-@PluginManager.registerTo("UserManager")
-class UserManagerPlugin(object):
-    def load(self):
-        if not config.multiuser_local:
-            # In multiuser mode do not load the users
-            if not self.users:
-                self.users = {}
-            return self.users
-        else:
-            return super(UserManagerPlugin, self).load()
-
-    # Find user by master address
-    # Return: User or None
-    def get(self, master_address=None):
-        users = self.list()
-        if master_address in users:
-            user = users[master_address]
-        else:
-            user = None
-        return user
-
-
-@PluginManager.registerTo("User")
-class UserPlugin(object):
-    # In multiuser mode users data only exits in memory, dont write to data/user.json
-    def save(self):
-        if not config.multiuser_local:
-            return False
-        else:
-            return super(UserPlugin, self).save()
diff --git a/plugins/disabled-Multiuser/__init__.py b/plugins/disabled-Multiuser/__init__.py
deleted file mode 100644
index c56ddf84..00000000
--- a/plugins/disabled-Multiuser/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import MultiuserPlugin
diff --git a/plugins/disabled-Multiuser/plugin_info.json b/plugins/disabled-Multiuser/plugin_info.json
deleted file mode 100644
index e440ed8e..00000000
--- a/plugins/disabled-Multiuser/plugin_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"name": "MultiUser",
-	"description": "Cookie based multi-users support on your ZeroNet web interface.",
-	"default": "disabled"
-}
\ No newline at end of file
diff --git a/plugins/disabled-StemPort/StemPortPlugin.py b/plugins/disabled-StemPort/StemPortPlugin.py
deleted file mode 100644
index c53d38e6..00000000
--- a/plugins/disabled-StemPort/StemPortPlugin.py
+++ /dev/null
@@ -1,135 +0,0 @@
-import logging
-import traceback
-
-import socket
-import stem
-from stem import Signal
-from stem.control import Controller
-from stem.socket import ControlPort
-
-from Plugin import PluginManager
-from Config import config
-from Debug import Debug
-
-if config.tor != "disable":
-    from gevent import monkey
-    monkey.patch_time()
-    monkey.patch_socket(dns=False)
-    monkey.patch_thread()
-    print("Stem Port Plugin: modules are patched.")
-else:
-    print("Stem Port Plugin: Tor mode disabled. Module patching skipped.")
-
-
-class PatchedControlPort(ControlPort):
-    def _make_socket(self):
-        try:
-            if "socket_noproxy" in dir(socket):  # Socket proxy-patched, use non-proxy one
-                control_socket = socket.socket_noproxy(socket.AF_INET, socket.SOCK_STREAM)
-            else:
-                control_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-
-            # TODO: repeated code - consider making a separate method
-
-            control_socket.connect((self._control_addr, self._control_port))
-            return control_socket
-        except socket.error as exc:
-            raise stem.SocketError(exc)
-
-def from_port(address = '127.0.0.1', port = 'default'):
-    import stem.connection
-
-    if not stem.util.connection.is_valid_ipv4_address(address):
-        raise ValueError('Invalid IP address: %s' % address)
-    elif port != 'default' and not stem.util.connection.is_valid_port(port):
-        raise ValueError('Invalid port: %s' % port)
-
-    if port == 'default':
-        raise ValueError('Must specify a port')
-    else:
-        control_port = PatchedControlPort(address, port)
-
-    return Controller(control_port)
-
-
-@PluginManager.registerTo("TorManager")
-class TorManagerPlugin(object):
-
-    def connectController(self):
-        self.log.info("Authenticate using Stem... %s:%s" % (self.ip, self.port))
-
-        try:
-            with self.lock:
-                if config.tor_password:
-                    controller = from_port(port=self.port, password=config.tor_password)
-                else:
-                    controller = from_port(port=self.port)
-                controller.authenticate()
-                self.controller = controller
-                self.status = "Connected (via Stem)"
-        except Exception as err:
-            print("\n")
-            traceback.print_exc()
-            print("\n")
-
-            self.controller = None
-            self.status = "Error (%s)" % err
-            self.log.error("Tor stem connect error: %s" % Debug.formatException(err))
-
-        return self.controller
-
-
-    def disconnect(self):
-        self.controller.close()
-        self.controller = None
-
-
-    def resetCircuits(self):
-        try:
-            self.controller.signal(Signal.NEWNYM)
-        except Exception as err:
-            self.status = "Stem reset circuits error (%s)" % err
-            self.log.error("Stem reset circuits error: %s" % err)
-
-
-    def makeOnionAndKey(self):
-        try:
-            service = self.controller.create_ephemeral_hidden_service(
-                {self.fileserver_port: self.fileserver_port},
-                await_publication = False
-            )
-            if service.private_key_type != "RSA1024":
-                raise Exception("ZeroNet doesn't support crypto " + service.private_key_type)
-
-            self.log.debug("Stem created %s.onion (async descriptor publication)" % service.service_id)
-
-            return (service.service_id, service.private_key)
-
-        except Exception as err:
-            self.status = "AddOnion error (Stem: %s)" % err
-            self.log.error("Failed to create hidden service with Stem: " + err)
-            return False
-
-
-    def delOnion(self, address):
-        try:
-            self.controller.remove_ephemeral_hidden_service(address)
-            return True
-        except Exception as err:
-            self.status = "DelOnion error (Stem: %s)" % err
-            self.log.error("Stem failed to delete %s.onion: %s" % (address, err))
-            self.disconnect() # Why?
-            return False
-
-
-    def request(self, cmd):
-        with self.lock:
-            if not self.enabled:
-                return False
-            else:
-                self.log.error("[WARNING] StemPort self.request should not be called")
-                return ""
-
-    def send(self, cmd, conn=None):
-        self.log.error("[WARNING] StemPort self.send should not be called")
-        return ""
diff --git a/plugins/disabled-StemPort/__init__.py b/plugins/disabled-StemPort/__init__.py
deleted file mode 100644
index 33f8e034..00000000
--- a/plugins/disabled-StemPort/__init__.py
+++ /dev/null
@@ -1,10 +0,0 @@
-try:
-    from stem.control import Controller
-    stem_found = True
-except Exception as err:
-    print(("STEM NOT FOUND! %s" % err))
-    stem_found = False
-
-if stem_found:
-    print("Starting Stem plugin...")
-    from . import StemPortPlugin
diff --git a/plugins/disabled-UiPassword/UiPasswordPlugin.py b/plugins/disabled-UiPassword/UiPasswordPlugin.py
deleted file mode 100644
index e8a4e4fe..00000000
--- a/plugins/disabled-UiPassword/UiPasswordPlugin.py
+++ /dev/null
@@ -1,183 +0,0 @@
-import string
-import random
-import time
-import json
-import re
-import os
-
-from Config import config
-from Plugin import PluginManager
-from util import helper
-
-
-plugin_dir = os.path.dirname(__file__)
-
-if "sessions" not in locals().keys():  # To keep sessions between module reloads
-    sessions = {}
-    whitelisted_client_ids = {}
-
-
-def showPasswordAdvice(password):
-    error_msgs = []
-    if not password or not isinstance(password, str):
-        error_msgs.append("You have enabled <b>UiPassword</b> plugin, but you forgot to set a password!")
-    elif len(password) < 8:
-        error_msgs.append("You are using a very short UI password!")
-    return error_msgs
-
-
-@PluginManager.registerTo("UiRequest")
-class UiRequestPlugin(object):
-    sessions = sessions
-    whitelisted_client_ids = whitelisted_client_ids
-    last_cleanup = time.time()
-
-    def getClientId(self):
-        return self.env["REMOTE_ADDR"] + " - " + self.env["HTTP_USER_AGENT"]
-
-    def whitelistClientId(self, session_id=None):
-        if not session_id:
-            session_id = self.getCookies().get("session_id")
-        client_id = self.getClientId()
-        if client_id in self.whitelisted_client_ids:
-            self.whitelisted_client_ids[client_id]["updated"] = time.time()
-            return False
-
-        self.whitelisted_client_ids[client_id] = {
-            "added": time.time(),
-            "updated": time.time(),
-            "session_id": session_id
-        }
-
-    def route(self, path):
-        # Restict Ui access by ip
-        if config.ui_restrict and self.env['REMOTE_ADDR'] not in config.ui_restrict:
-            return self.error403(details=False)
-        if path.endswith("favicon.ico"):
-            return self.actionFile("src/Ui/media/img/favicon.ico")
-        else:
-            if config.ui_password:
-                if time.time() - self.last_cleanup > 60 * 60:  # Cleanup expired sessions every hour
-                    self.sessionCleanup()
-                # Validate session
-                session_id = self.getCookies().get("session_id")
-                if session_id not in self.sessions and self.getClientId() not in self.whitelisted_client_ids:
-                    # Invalid session id and not whitelisted ip: display login
-                    return self.actionLogin()
-            return super(UiRequestPlugin, self).route(path)
-
-    def actionWrapper(self, path, *args, **kwargs):
-        if config.ui_password and self.isWrapperNecessary(path):
-            session_id = self.getCookies().get("session_id")
-            if session_id not in self.sessions:
-                # We only accept cookie based auth on wrapper
-                return self.actionLogin()
-            else:
-                self.whitelistClientId()
-
-        return super().actionWrapper(path, *args, **kwargs)
-
-    # Action: Login
-    @helper.encodeResponse
-    def actionLogin(self):
-        template = open(plugin_dir + "/login.html").read()
-        self.sendHeader()
-        posted = self.getPosted()
-        if posted:  # Validate http posted data
-            if self.sessionCheckPassword(posted.get("password")):
-                # Valid password, create session
-                session_id = self.randomString(26)
-                self.sessions[session_id] = {
-                    "added": time.time(),
-                    "keep": posted.get("keep")
-                }
-                self.whitelistClientId(session_id)
-
-                # Redirect to homepage or referer
-                url = self.env.get("HTTP_REFERER", "")
-                if not url or re.sub(r"\?.*", "", url).endswith("/Login"):
-                    url = "/" + config.homepage
-                cookie_header = ('Set-Cookie', "session_id=%s;path=/;max-age=2592000;" % session_id)  # Max age = 30 days
-                self.start_response('301 Redirect', [('Location', url), cookie_header])
-                yield "Redirecting..."
-
-            else:
-                # Invalid password, show login form again
-                template = template.replace("{result}", "bad_password")
-        yield template
-
-    def randomString(self, nchars):
-        return ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(nchars))
-
-    def sessionCheckPassword(self, password):
-        return password == config.ui_password
-
-    def sessionDelete(self, session_id):
-       del self.sessions[session_id]
-
-       for client_id in list(self.whitelisted_client_ids):
-            if self.whitelisted_client_ids[client_id]["session_id"] == session_id:
-                del self.whitelisted_client_ids[client_id]
-
-    def sessionCleanup(self):
-        self.last_cleanup = time.time()
-        for session_id, session in list(self.sessions.items()):
-            if session["keep"] and time.time() - session["added"] > 60 * 60 * 24 * 60:  # Max 60days for keep sessions
-                self.sessionDelete(session_id)
-            elif not session["keep"] and time.time() - session["added"] > 60 * 60 * 24:  # Max 24h for non-keep sessions
-                self.sessionDelete(session_id)
-
-    # Action: Display sessions
-    @helper.encodeResponse
-    def actionSessions(self):
-        self.sendHeader()
-        yield "<pre>"
-        yield json.dumps(self.sessions, indent=4)
-        yield "\r\n"
-        yield json.dumps(self.whitelisted_client_ids, indent=4)
-
-    # Action: Logout
-    @helper.encodeResponse
-    def actionLogout(self):
-        # Session id has to passed as get parameter or called without referer to avoid remote logout
-        session_id = self.getCookies().get("session_id")
-        if not self.env.get("HTTP_REFERER") or session_id == self.get.get("session_id"):
-            if session_id in self.sessions:
-                self.sessionDelete(session_id)
-
-            self.start_response('301 Redirect', [
-                ('Location', "/"),
-                ('Set-Cookie', "session_id=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT")
-            ])
-            yield "Redirecting..."
-        else:
-            self.sendHeader()
-            yield "Error: Invalid session id"
-
-
-@PluginManager.registerTo("ConfigPlugin")
-class ConfigPlugin(object):
-    def createArguments(self):
-        group = self.parser.add_argument_group("UiPassword plugin")
-        group.add_argument('--ui_password', help='Password to access UiServer', default=None, metavar="password")
-
-        return super(ConfigPlugin, self).createArguments()
-
-
-from Translate import translate as lang
-@PluginManager.registerTo("UiWebsocket")
-class UiWebsocketPlugin(object):
-    def actionUiLogout(self, to):
-        permissions = self.getPermissions(to)
-        if "ADMIN" not in permissions:
-            return self.response(to, "You don't have permission to run this command")
-
-        session_id = self.request.getCookies().get("session_id", "")
-        self.cmd("redirect", '/Logout?session_id=%s' % session_id)
-
-    def addHomepageNotifications(self):
-        error_msgs = showPasswordAdvice(config.ui_password)
-        for msg in error_msgs:
-            self.site.notifications.append(["error", lang[msg]])
-
-        return super(UiWebsocketPlugin, self).addHomepageNotifications()
diff --git a/plugins/disabled-UiPassword/__init__.py b/plugins/disabled-UiPassword/__init__.py
deleted file mode 100644
index 1779c597..00000000
--- a/plugins/disabled-UiPassword/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import UiPasswordPlugin
\ No newline at end of file
diff --git a/plugins/disabled-UiPassword/login.html b/plugins/disabled-UiPassword/login.html
deleted file mode 100644
index 12d0889d..00000000
--- a/plugins/disabled-UiPassword/login.html
+++ /dev/null
@@ -1,116 +0,0 @@
-<html>
-<head>
- <title>Log In</title>
- <meta name="viewport" id="viewport" content="width=device-width, initial-scale=1.0">
-</head>
-
-<style>
-body {
-	background-color: #323C4D; font-family: "Segoe UI", Helvetica, Arial; font-weight: lighter;
-    font-size: 22px; color: #333; letter-spacing: 1px; color: white; overflow: hidden;
-}
-.login { left: 50%; position: absolute; top: 50%; transform: translateX(-50%) translateY(-50%); -webkit-transform: translateX(-50%) translateY(-50%); width: 100%; max-width: 370px; text-align: center; }
-
-*:focus { outline: 0; }
-input[type=text], input[type=password] {
-	padding: 10px 0px; border: 0px; display: block; margin: 15px 0px; width: 100%; border-radius: 30px; transition: 0.3s ease-out; background-color: #DDD;
-	text-align: center; font-family: "Segoe UI", Helvetica, Arial; font-weight: lighter; font-size: 28px; border: 2px solid #323C4D;
-}
-input[type=text]:focus, input[type=password]:focus {
-	border: 2px solid #FFF; background-color: #FFF;
-}
-input[type=checkbox] { opacity: 0; }
-input[type=checkbox]:checked + label { color: white; }
-input[type=checkbox]:focus + label::before { background-color: #435065; }
-input[type=checkbox]:checked + label::before { box-shadow: inset 0px 0px 0px 5px white; background-color: #4DCC6E; }
-input.error { border: 2px solid #F44336 !important; animation: shake 1s }
-label::before {
-	content: ""; width: 20px; height: 20px; background-color: #323C4D;
-	display: inline-block; margin-left: -20px; border-radius: 15px; box-shadow: inset 0px 0px 0px 2px #9EA5B3;
-	transition: all 0.1s; margin-right: 7px; position: relative; top: 2px;
-}
-label { vertical-align: -1px; color: #9EA5B3; transition: all 0.3s; }
-
-.button {
-	padding: 13px; display: inline-block; margin: 15px 0px; width: 100%; border-radius: 30px; text-align: center; white-space: nowrap;
-	font-size: 28px; color: #333; background: linear-gradient(45deg, #6B14D3 0, #7A26E2 25%, #4962DD 90%);
-    box-sizing: border-box; margin-top: 50px; color: white; text-decoration: none; transition: 0.3s ease-out;
-}
-.button:hover, .button:focus { box-shadow: 0px 5px 30px rgba(0,0,0,0.3); }
-.button:active { transform: translateY(1px); box-shadow: 0px 0px 20px rgba(0,0,0,0.5); transition: none; }
-
-#login_form_submit { display: none; }
-
-.login-anim { animation: login 1s cubic-bezier(0.785, 0.135, 0.15, 0.86) forwards; }
-
-@keyframes login {
-    0%   { width: 100%; }
-    60%  { width: 63px; transform: scale(1); color: rgba(255,255,255,0); }
-    70%  { width: 63px; transform: scale(1); color: rgba(255,255,255,0); }
-    100% { transform: scale(80); width: 63px; color: rgba(255,255,255,0); }
-}
-
-@keyframes shake {
-    0%, 100% { transform: translateX(0); }
-    10%, 30%, 50%, 70%, 90% { transform: translateX(-10px); }
-    20%, 40%, 60%, 80% { transform: translateX(10px); }
-}
-</style>
-
-<body>
-
-
-<div class="login">
- <form action="" method="post" id="login_form" onkeypress="return onFormKeypress(event)">
-  <!--<input type="text" name="username" placeholder="Username" required/>-->
-  <input type="password" name="password" placeholder="Password" required/>
-  <input type="checkbox" name="keep" id="keep"><label for="keep">Keep me logged in</label>
-  <div style="clear: both"></div>
-  <a href="#" class="button" onclick="return submit()" id="login_button"><span>Log In</span></a>
-  <input type="submit" id="login_form_submit"/>
- </form>
-</div>
-
-
-<script>
-
-function onFormKeypress(event) {
-	if (event.keyCode == 13) {
-		submit()
-		return false
-	}
-}
-
-function submit() {
-	var form = document.getElementById("login_form")
-	if (form.checkValidity()) {
-		document.getElementById("login_button").className = "button login-anim"
-		setTimeout(function() {
-			form.submit()
-		}, 1000)
-	} else {
-		form.submit()
-	}
-	return false
-}
-
-function badPassword() {
-	var elem = document.getElementsByName("password")[0]
-	elem.className = "error"
-	elem.placeholder = "Wrong Password"
-	elem.focus()
-	elem.addEventListener('input', function() {
-		elem.className = ""
-		elem.placeholder = "Password"
-	})
-}
-
-result = "{result}"
-
-if (result == "bad_password")
-	badPassword()
-
-</script>
-
-</body>
-</html>
diff --git a/plugins/disabled-UiPassword/plugin_info.json b/plugins/disabled-UiPassword/plugin_info.json
deleted file mode 100644
index d3649a17..00000000
--- a/plugins/disabled-UiPassword/plugin_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-	"name": "UiPassword",
-	"description": "Password based autentication on the web interface.",
-	"default": "disabled"
-}
\ No newline at end of file
diff --git a/plugins/disabled-ZeronameLocal/SiteManagerPlugin.py b/plugins/disabled-ZeronameLocal/SiteManagerPlugin.py
deleted file mode 100644
index 579e31c1..00000000
--- a/plugins/disabled-ZeronameLocal/SiteManagerPlugin.py
+++ /dev/null
@@ -1,180 +0,0 @@
-import logging, json, os, re, sys, time, socket
-from Plugin import PluginManager
-from Config import config
-from Debug import Debug
-from http.client import HTTPSConnection, HTTPConnection, HTTPException
-from base64 import b64encode
-
-allow_reload = False # No reload supported
-
-@PluginManager.registerTo("SiteManager")
-class SiteManagerPlugin(object):
-    def load(self, *args, **kwargs):
-        super(SiteManagerPlugin, self).load(*args, **kwargs)
-        self.log = logging.getLogger("ZeronetLocal Plugin")
-        self.error_message = None
-        if not config.namecoin_host or not config.namecoin_rpcport or not config.namecoin_rpcuser or not config.namecoin_rpcpassword:
-            self.error_message = "Missing parameters"
-            self.log.error("Missing parameters to connect to namecoin node. Please check all the arguments needed with '--help'. Zeronet will continue working without it.")
-            return
-
-        url = "%(host)s:%(port)s" % {"host": config.namecoin_host, "port": config.namecoin_rpcport}
-        self.c = HTTPConnection(url, timeout=3)
-        user_pass = "%(user)s:%(password)s" % {"user": config.namecoin_rpcuser, "password": config.namecoin_rpcpassword}
-        userAndPass = b64encode(bytes(user_pass, "utf-8")).decode("ascii")
-        self.headers = {"Authorization" : "Basic %s" %  userAndPass, "Content-Type": " application/json " }
-
-        payload = json.dumps({
-            "jsonrpc": "2.0",
-            "id": "zeronet",
-            "method": "ping",
-            "params": []
-        })
-
-        try:
-            self.c.request("POST", "/", payload, headers=self.headers)
-            response = self.c.getresponse()
-            data = response.read()
-            self.c.close()
-            if response.status == 200:
-                result = json.loads(data.decode())["result"]
-            else:
-                raise Exception(response.reason)
-        except Exception as err:
-            self.log.error("The Namecoin node is unreachable. Please check the configuration value are correct. Zeronet will continue working without it.")
-            self.error_message = err
-        self.cache = dict()
-
-    # Checks if it's a valid address
-    def isAddress(self, address):
-        return self.isBitDomain(address) or super(SiteManagerPlugin, self).isAddress(address)
-
-    # Return: True if the address is domain
-    def isDomain(self, address):
-        return self.isBitDomain(address) or super(SiteManagerPlugin, self).isDomain(address)
-
-    # Return: True if the address is .bit domain
-    def isBitDomain(self, address):
-        return re.match(r"(.*?)([A-Za-z0-9_-]+\.bit)$", address)
-
-    # Return: Site object or None if not found
-    def get(self, address):
-        if self.isBitDomain(address):  # Its looks like a domain
-            address_resolved = self.resolveDomain(address)
-            if address_resolved:  # Domain found
-                site = self.sites.get(address_resolved)
-                if site:
-                    site_domain = site.settings.get("domain")
-                    if site_domain != address:
-                        site.settings["domain"] = address
-            else:  # Domain not found
-                site = self.sites.get(address)
-
-        else:  # Access by site address
-            site = super(SiteManagerPlugin, self).get(address)
-        return site
-
-    # Return or create site and start download site files
-    # Return: Site or None if dns resolve failed
-    def need(self, address, *args, **kwargs):
-        if self.isBitDomain(address):  # Its looks like a domain
-            address_resolved = self.resolveDomain(address)
-            if address_resolved:
-                address = address_resolved
-            else:
-                return None
-
-        return super(SiteManagerPlugin, self).need(address, *args, **kwargs)
-
-    # Resolve domain
-    # Return: The address or None
-    def resolveDomain(self, domain):
-        domain = domain.lower()
-
-        #remove .bit on end
-        if domain[-4:] == ".bit":
-            domain = domain[0:-4]
-
-        domain_array = domain.split(".")
-
-        if self.error_message:
-            self.log.error("Not able to connect to Namecoin node : {!s}".format(self.error_message))
-            return None
-
-        if len(domain_array) > 2:
-            self.log.error("Too many subdomains! Can only handle one level (eg. staging.mixtape.bit)")
-            return None
-
-        subdomain = ""
-        if len(domain_array) == 1:
-            domain = domain_array[0]
-        else:
-            subdomain = domain_array[0]
-            domain = domain_array[1]
-
-        if domain in self.cache:
-            delta = time.time() - self.cache[domain]["time"]
-            if delta < 3600:
-                # Must have been less than 1hour
-                return self.cache[domain]["addresses_resolved"][subdomain]
-
-        payload = json.dumps({
-            "jsonrpc": "2.0",
-            "id": "zeronet",
-            "method": "name_show",
-            "params": ["d/"+domain]
-        })
-
-        try:
-            self.c.request("POST", "/", payload, headers=self.headers)
-            response = self.c.getresponse()
-            data = response.read()
-            self.c.close()
-            domain_object = json.loads(data.decode())["result"]
-        except Exception as err:
-            #domain doesn't exist
-            return None
-
-        if "zeronet" in domain_object["value"]:
-            zeronet_domains = json.loads(domain_object["value"])["zeronet"]
-
-            if isinstance(zeronet_domains, str):
-                # {
-                #    "zeronet":"19rXKeKptSdQ9qt7omwN82smehzTuuq6S9"
-                # } is valid
-                zeronet_domains = {"": zeronet_domains}
-
-            self.cache[domain] = {"addresses_resolved": zeronet_domains, "time": time.time()}
-
-        elif "map" in domain_object["value"]:
-            # Namecoin standard use {"map": { "blog": {"zeronet": "1D..."} }}
-            data_map = json.loads(domain_object["value"])["map"]
-
-            zeronet_domains = dict()
-            for subdomain in data_map:
-                if "zeronet" in data_map[subdomain]:
-                    zeronet_domains[subdomain] = data_map[subdomain]["zeronet"]
-            if "zeronet" in data_map and isinstance(data_map["zeronet"], str):
-                # {"map":{
-                #    "zeronet":"19rXKeKptSdQ9qt7omwN82smehzTuuq6S9",
-                # }}
-                zeronet_domains[""] = data_map["zeronet"]
-
-            self.cache[domain] = {"addresses_resolved": zeronet_domains, "time": time.time()}
-
-        else:
-            # No Zeronet address registered
-            return None
-
-        return self.cache[domain]["addresses_resolved"][subdomain]
-
-@PluginManager.registerTo("ConfigPlugin")
-class ConfigPlugin(object):
-    def createArguments(self):
-        group = self.parser.add_argument_group("Zeroname Local plugin")
-        group.add_argument('--namecoin_host', help="Host to namecoin node (eg. 127.0.0.1)")
-        group.add_argument('--namecoin_rpcport', help="Port to connect (eg. 8336)")
-        group.add_argument('--namecoin_rpcuser', help="RPC user to connect to the namecoin node (eg. nofish)")
-        group.add_argument('--namecoin_rpcpassword', help="RPC password to connect to namecoin node")
-
-        return super(ConfigPlugin, self).createArguments()
diff --git a/plugins/disabled-ZeronameLocal/UiRequestPlugin.py b/plugins/disabled-ZeronameLocal/UiRequestPlugin.py
deleted file mode 100644
index 0ccfb530..00000000
--- a/plugins/disabled-ZeronameLocal/UiRequestPlugin.py
+++ /dev/null
@@ -1,39 +0,0 @@
-import re
-from Plugin import PluginManager
-
-@PluginManager.registerTo("UiRequest")
-class UiRequestPlugin(object):
-    def __init__(self, *args, **kwargs):
-        from Site import SiteManager
-        self.site_manager = SiteManager.site_manager
-        super(UiRequestPlugin, self).__init__(*args, **kwargs)
-
-
-    # Media request
-    def actionSiteMedia(self, path):
-        match = re.match(r"/media/(?P<address>[A-Za-z0-9-]+\.[A-Za-z0-9\.-]+)(?P<inner_path>/.*|$)", path)
-        if match: # Its a valid domain, resolve first
-            domain = match.group("address")
-            address = self.site_manager.resolveDomain(domain)
-            if address:
-                path = "/media/"+address+match.group("inner_path")
-        return super(UiRequestPlugin, self).actionSiteMedia(path) # Get the wrapper frame output
-
-
-    # Is mediarequest allowed from that referer
-    def isMediaRequestAllowed(self, site_address, referer):
-        referer_path = re.sub("http[s]{0,1}://.*?/", "/", referer).replace("/media", "") # Remove site address
-        referer_path = re.sub(r"\?.*", "", referer_path) # Remove http params
-
-        if self.isProxyRequest(): # Match to site domain
-            referer = re.sub("^http://zero[/]+", "http://", referer) # Allow /zero access
-            referer_site_address = re.match("http[s]{0,1}://(.*?)(/|$)", referer).group(1)
-        else: # Match to request path
-            referer_site_address = re.match(r"/(?P<address>[A-Za-z0-9\.-]+)(?P<inner_path>/.*|$)", referer_path).group("address")
-
-        if referer_site_address == site_address: # Referer site address as simple address
-            return True
-        elif self.site_manager.resolveDomain(referer_site_address) == site_address: # Referer site address as dns
-            return True
-        else: # Invalid referer
-            return False
diff --git a/plugins/disabled-ZeronameLocal/__init__.py b/plugins/disabled-ZeronameLocal/__init__.py
deleted file mode 100644
index cf724069..00000000
--- a/plugins/disabled-ZeronameLocal/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-from . import UiRequestPlugin
-from . import SiteManagerPlugin
\ No newline at end of file
diff --git a/src/Config.py b/src/Config.py
index 7555f65d..71a725cc 100644
--- a/src/Config.py
+++ b/src/Config.py
@@ -13,7 +13,7 @@ import time
 class Config(object):
 
     def __init__(self, argv):
-        self.version = "0.7.6-internal"
+        self.version = "0.7.6-internal 2"
         self.rev = 4560
         self.argv = argv
         self.action = None