From cd608140992163fabbc6fcbfedc7c1f280f156af Mon Sep 17 00:00:00 2001
From: PhantomX
Date: Thu, 15 Oct 2015 13:01:16 +0300
Subject: [PATCH] 1. move the non-minified compiled JS file to the /debug/
directory 2. Corrected dependency for "marked"
---
debug/simplemde.css | 647 ++
debug/simplemde.debug.js | 14028 +++++++++++++++++++++++++++++++++++++
debug/simplemde.js | 14024 ++++++++++++++++++++++++++++++++++++
dist/simplemde.min.css | 7 +
dist/simplemde.min.js | 14 +
gulpfile.js | 10 +-
src/js/simplemde.js | 13 +-
7 files changed, 28732 insertions(+), 11 deletions(-)
create mode 100644 debug/simplemde.css
create mode 100644 debug/simplemde.debug.js
create mode 100644 debug/simplemde.js
create mode 100644 dist/simplemde.min.css
create mode 100644 dist/simplemde.min.js
diff --git a/debug/simplemde.css b/debug/simplemde.css
new file mode 100644
index 0000000..6c2570a
--- /dev/null
+++ b/debug/simplemde.css
@@ -0,0 +1,647 @@
+/* BASICS */
+
+.CodeMirror {
+ /* Set height, width, borders, and global font properties here */
+ font-family: monospace;
+ height: 300px;
+ color: black;
+}
+
+/* PADDING */
+
+.CodeMirror-lines {
+ padding: 4px 0; /* Vertical padding around content */
+}
+.CodeMirror pre {
+ 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;
+ background: #7e7;
+}
+.cm-fat-cursor div.CodeMirror-cursors {
+ z-index: 1;
+}
+
+.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-ruler {
+ border-left: 1px solid #ccc;
+ 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 {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: #0f0;}
+div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
+.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 */
+ /* 30px is the magic margin used to hide the element's real scrollbars */
+ /* See overflow: hidden in .CodeMirror */
+ margin-bottom: -30px; margin-right: -30px;
+ padding-bottom: 30px;
+ height: 100%;
+ outline: none; /* Prevent dragging from highlighting the element */
+ position: relative;
+}
+.CodeMirror-sizer {
+ position: relative;
+ border-right: 30px solid transparent;
+}
+
+/* The fake, visible scrollbars. Used to force redraw during scrolling
+ before actuall 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;
+ z-index: 3;
+}
+.CodeMirror-gutter {
+ white-space: normal;
+ height: 100%;
+ display: inline-block;
+ margin-bottom: -30px;
+ /* Hack to make IE7 behave */
+ *zoom:1;
+ *display:inline;
+}
+.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 {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+}
+
+.CodeMirror-lines {
+ cursor: text;
+ min-height: 1px; /* prevents collapsing before first draw */
+}
+.CodeMirror pre {
+ /* 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;
+}
+.CodeMirror-wrap pre {
+ 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;
+ overflow: auto;
+}
+
+.CodeMirror-widget {}
+
+.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; }
+.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: #ffa;
+ background: rgba(255, 255, 0, .4);
+}
+
+/* IE7 hack to prevent it from returning funny offsetTops on the spans */
+.CodeMirror span { *vertical-align: text-bottom; }
+
+/* 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; }
+
+.CodeMirror {
+ height: auto;
+ min-height: 300px;
+ border: 1px solid #ddd;
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+ padding: 10px;
+ font: inherit;
+}
+
+.CodeMirror-scroll {
+ min-height: 300px
+}
+
+.CodeMirror-fullscreen {
+ background: #fff;
+ position: fixed !important;
+ top: 50px;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ height: auto;
+ z-index: 9;
+}
+
+.CodeMirror-sided {
+ width: 50% !important;
+}
+
+.editor-toolbar {
+ position: relative;
+ opacity: .6;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+ padding: 0 10px;
+ border-top: 1px solid #bbb;
+ border-left: 1px solid #bbb;
+ border-right: 1px solid #bbb;
+ border-top-left-radius: 4px;
+ border-top-right-radius: 4px;
+}
+
+.editor-toolbar:after,
+.editor-toolbar:before {
+ display: block;
+ content: ' ';
+ height: 1px;
+}
+
+.editor-toolbar:before {
+ margin-bottom: 8px
+}
+
+.editor-toolbar:after {
+ margin-top: 8px
+}
+
+.editor-toolbar:hover,
+.editor-wrapper input.title:focus,
+.editor-wrapper input.title:hover {
+ opacity: .8
+}
+
+.editor-toolbar.fullscreen {
+ width: 100%;
+ height: 50px;
+ overflow-x: auto;
+ overflow-y: hidden;
+ white-space: nowrap;
+ padding-top: 10px;
+ padding-bottom: 10px;
+ box-sizing: border-box;
+ background: #fff;
+ border: 0;
+ position: fixed;
+ top: 0;
+ left: 0;
+ opacity: 1;
+ z-index: 9;
+}
+
+.editor-toolbar.fullscreen::before {
+ width: 20px;
+ height: 50px;
+ background: -moz-linear-gradient(left, rgba(255, 255, 255, 1) 0%, rgba(125, 185, 232, 0.01) 100%);
+ background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255, 255, 255, 1)), color-stop(100%, rgba(125, 185, 232, 0.01)));
+ background: -webkit-linear-gradient(left, rgba(255, 255, 255, 1) 0%, rgba(125, 185, 232, 0.01) 100%);
+ background: -o-linear-gradient(left, rgba(255, 255, 255, 1) 0%, rgba(125, 185, 232, 0.01) 100%);
+ background: -ms-linear-gradient(left, rgba(255, 255, 255, 1) 0%, rgba(125, 185, 232, 0.01) 100%);
+ background: linear-gradient(to right, rgba(255, 255, 255, 1) 0%, rgba(125, 185, 232, 0.01) 100%);
+ filter: progid: DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#037db9e8', GradientType=1);
+ position: fixed;
+ top: 0;
+ left: 0;
+ margin: 0;
+ padding: 0;
+}
+
+.editor-toolbar.fullscreen::after {
+ width: 20px;
+ height: 50px;
+ background: -moz-linear-gradient(left, rgba(125, 185, 232, 0.01) 0%, rgba(254, 254, 255, 1) 99%, rgba(255, 255, 255, 1) 100%);
+ background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(125, 185, 232, 0.01)), color-stop(99%, rgba(254, 254, 255, 1)), color-stop(100%, rgba(255, 255, 255, 1)));
+ background: -webkit-linear-gradient(left, rgba(125, 185, 232, 0.01) 0%, rgba(254, 254, 255, 1) 99%, rgba(255, 255, 255, 1) 100%);
+ background: -o-linear-gradient(left, rgba(125, 185, 232, 0.01) 0%, rgba(254, 254, 255, 1) 99%, rgba(255, 255, 255, 1) 100%);
+ background: -ms-linear-gradient(left, rgba(125, 185, 232, 0.01) 0%, rgba(254, 254, 255, 1) 99%, rgba(255, 255, 255, 1) 100%);
+ background: linear-gradient(to right, rgba(125, 185, 232, 0.01) 0%, rgba(254, 254, 255, 1) 99%, rgba(255, 255, 255, 1) 100%);
+ filter: progid: DXImageTransform.Microsoft.gradient( startColorstr='#037db9e8', endColorstr='#ffffff', GradientType=1);
+ position: fixed;
+ top: 0;
+ right: 0;
+ margin: 0;
+ padding: 0;
+}
+
+.editor-toolbar a {
+ display: inline-block;
+ text-align: center;
+ text-decoration: none!important;
+ color: #2c3e50!important;
+ width: 30px;
+ height: 30px;
+ margin: 0;
+ border: 1px solid transparent;
+ border-radius: 3px;
+ cursor: pointer;
+}
+
+.editor-toolbar a.active,
+.editor-toolbar a:hover {
+ background: #fcfcfc;
+ border-color: #95a5a6;
+}
+
+.editor-toolbar a:before {
+ line-height: 30px
+}
+
+.editor-toolbar i.separator {
+ display: inline-block;
+ width: 0;
+ border-left: 1px solid #d9d9d9;
+ border-right: 1px solid #fff;
+ color: transparent;
+ text-indent: -10px;
+ margin: 0 6px;
+}
+
+.editor-toolbar a.fa-header-x:after {
+ font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
+ font-size: 65%;
+ vertical-align: text-bottom;
+ position: relative;
+ top: 2px;
+}
+
+.editor-toolbar a.fa-header-1:after {
+ content: "1";
+}
+
+.editor-toolbar a.fa-header-2:after {
+ content: "2";
+}
+
+.editor-toolbar a.fa-header-3:after {
+ content: "3";
+}
+
+.editor-toolbar a.fa-header-bigger:after {
+ content: "▲";
+}
+
+.editor-toolbar a.fa-header-smaller:after {
+ content: "▼";
+}
+
+.editor-toolbar.disabled-for-preview a:not(.no-disable) {
+ pointer-events: none;
+ background: #fff;
+ border-color: transparent;
+ text-shadow: inherit;
+}
+
+@media only screen and (max-width: 700px) {
+ .editor-toolbar a.no-mobile {
+ display: none;
+ }
+}
+
+.editor-statusbar {
+ padding: 8px 10px;
+ font-size: 12px;
+ color: #959694;
+ text-align: right;
+}
+
+.editor-statusbar span {
+ display: inline-block;
+ min-width: 4em;
+ margin-left: 1em;
+}
+
+.editor-statusbar .lines:before {
+ content: 'lines: '
+}
+
+.editor-statusbar .words:before {
+ content: 'words: '
+}
+
+.editor-preview {
+ padding: 10px;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ background: #fafafa;
+ z-index: 9999;
+ overflow: auto;
+ display: none;
+ box-sizing: border-box;
+}
+
+.editor-preview-side {
+ padding: 10px;
+ position: fixed;
+ bottom: 0;
+ width: 50%;
+ top: 50px;
+ right: 0;
+ background: #fafafa;
+ z-index: 9999;
+ overflow: auto;
+ display: none;
+ box-sizing: border-box;
+ border: 1px solid #ddd;
+}
+
+.editor-preview-active-side {
+ display: block
+}
+
+.editor-preview-active {
+ display: block
+}
+
+.editor-preview>p,
+.editor-preview-side>p {
+ margin-top: 0
+}
+
+.editor-preview pre,
+.editor-preview-side pre {
+ background: #eee;
+ margin-bottom: 10px;
+}
+
+.editor-preview table td,
+.editor-preview table th,
+.editor-preview-side table td,
+.editor-preview-side table th {
+ border: 1px solid #ddd;
+ padding: 5px;
+}
+
+.CodeMirror .CodeMirror-selected {
+ background: #d9d9d9;
+}
+
+.CodeMirror .CodeMirror-code .cm-header-1 {
+ font-size: 200%;
+ line-height: 200%;
+}
+
+.CodeMirror .CodeMirror-code .cm-header-2 {
+ font-size: 160%;
+ line-height: 160%;
+}
+
+.CodeMirror .CodeMirror-code .cm-header-3 {
+ font-size: 125%;
+ line-height: 125%;
+}
+
+.CodeMirror .CodeMirror-code .cm-header-4 {
+ font-size: 110%;
+ line-height: 110%;
+}
+
+.CodeMirror .CodeMirror-code .cm-comment {
+ background: rgba(0, 0, 0, .05);
+ border-radius: 2px;
+}
+
+.CodeMirror .CodeMirror-code .cm-link {
+ color: #7f8c8d;
+}
+
+.CodeMirror .CodeMirror-code .cm-url {
+ color: #aab2b3;
+}
+
+.CodeMirror .CodeMirror-code .cm-strikethrough {
+ text-decoration: line-through;
+}
+.CodeMirror .cm-spell-error:not(.cm-url):not(.cm-comment) {
+ background: rgba(255, 0, 0, .15);
+}
\ No newline at end of file
diff --git a/debug/simplemde.debug.js b/debug/simplemde.debug.js
new file mode 100644
index 0000000..3e88c52
--- /dev/null
+++ b/debug/simplemde.debug.js
@@ -0,0 +1,14028 @@
+(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.SimpleMDE = 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?@[\\]^_`{|}~ ";
+
+
+ // Create the overlay and such
+ var overlay = {
+ token: function(stream, state) {
+ var ch = stream.peek();
+ var word = "";
+
+ if(rx_word.includes(ch)) {
+ stream.next();
+ return null;
+ }
+
+ while((ch = stream.peek()) != null && !rx_word.includes(ch)) {
+ word += ch;
+ stream.next();
+ }
+
+ if(typo && !typo.check(word))
+ return "spell-error"; // CSS class: cm-spell-error
+
+ return null;
+ }
+ };
+
+ var mode = CodeMirror.getMode(
+ config, config.backdrop || "text/plain"
+ );
+
+ return CodeMirror.overlayMode(mode, overlay, true);
+});
+
+
+// Because some browsers don't support this functionality yet
+if(!String.prototype.includes) {
+ String.prototype.includes = function() {'use strict';
+ return String.prototype.indexOf.apply(this, arguments) !== -1;
+ };
+}
+}).call(global, module, undefined, undefined);
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+
+},{"C:\\phantomx\\projects\\simplemde-markdown-editor\\node_modules\\codemirror-spell-checker\\src\\js\\typo.js":2,"codemirror":6}],2:[function(require,module,exports){
+(function (global){
+; var __browserify_shim_require__=require;(function browserifyShim(module, exports, require, define, browserify_shim__define__module__export__) {
+'use strict';
+
+/**
+ * Typo is a JavaScript implementation of a spellchecker using hunspell-style
+ * dictionaries.
+ */
+
+/**
+ * Typo constructor.
+ *
+ * @param {String} [dictionary] The locale code of the dictionary being used. e.g.,
+ * "en_US". This is only used to auto-load dictionaries.
+ * @param {String} [affData] The data from the dictionary's .aff file. If omitted
+ * and the first argument is supplied, in "chrome" platform,
+ * the .aff file will be loaded automatically from
+ * lib/typo/dictionaries/[dictionary]/[dictionary].aff
+ * In other platform, it will be loaded from
+ * [setting.path]/dictionaries/[dictionary]/[dictionary].aff
+ * @param {String} [wordsData] The data from the dictionary's .dic file. If omitted,
+ * and the first argument is supplied, in "chrome" platform,
+ * the .dic file will be loaded automatically from
+ * lib/typo/dictionaries/[dictionary]/[dictionary].dic
+ * In other platform, it will be loaded from
+ * [setting.path]/dictionaries/[dictionary]/[dictionary].dic
+ * @param {Object} [settings] Constructor settings. Available properties are:
+ * {String} [platform]: "chrome" for Chrome Extension or other
+ * value for the usual web.
+ * {String} [dictionaryPath]: path to load dictionary from in non-chrome
+ * environment.
+ * {Object} [flags]: flag information.
+ *
+ *
+ * @returns {Typo} A Typo object.
+ */
+
+var Typo = function (dictionary, affData, wordsData, settings) {
+ settings = settings || {};
+
+ /** Determines the method used for auto-loading .aff and .dic files. **/
+ this.platform = settings.platform || "chrome";
+
+ this.dictionary = null;
+
+ this.rules = {};
+ this.dictionaryTable = {};
+
+ this.compoundRules = [];
+ this.compoundRuleCodes = {};
+
+ this.replacementTable = [];
+
+ this.flags = settings.flags || {};
+
+ if (dictionary) {
+ this.dictionary = dictionary;
+
+ if (this.platform == "chrome") {
+ if (!affData) affData = this._readFile(chrome.extension.getURL("lib/typo/dictionaries/" + dictionary + "/" + dictionary + ".aff"));
+ if (!wordsData) wordsData = this._readFile(chrome.extension.getURL("lib/typo/dictionaries/" + dictionary + "/" + dictionary + ".dic"));
+ } else {
+ var path = settings.dictionaryPath || '';
+
+ if (!affData) affData = this._readFile(path + "/" + dictionary + "/" + dictionary + ".aff");
+ if (!wordsData) wordsData = this._readFile(path + "/" + dictionary + "/" + dictionary + ".dic");
+ }
+
+ this.rules = this._parseAFF(affData);
+
+ // Save the rule codes that are used in compound rules.
+ this.compoundRuleCodes = {};
+
+ for (var i = 0, _len = this.compoundRules.length; i < _len; i++) {
+ var rule = this.compoundRules[i];
+
+ for (var j = 0, _jlen = rule.length; j < _jlen; j++) {
+ this.compoundRuleCodes[rule[j]] = [];
+ }
+ }
+
+ // If we add this ONLYINCOMPOUND flag to this.compoundRuleCodes, then _parseDIC
+ // will do the work of saving the list of words that are compound-only.
+ if ("ONLYINCOMPOUND" in this.flags) {
+ this.compoundRuleCodes[this.flags.ONLYINCOMPOUND] = [];
+ }
+
+ this.dictionaryTable = this._parseDIC(wordsData);
+
+ // Get rid of any codes from the compound rule codes that are never used
+ // (or that were special regex characters). Not especially necessary...
+ for (var i in this.compoundRuleCodes) {
+ if (this.compoundRuleCodes[i].length == 0) {
+ delete this.compoundRuleCodes[i];
+ }
+ }
+
+ // Build the full regular expressions for each compound rule.
+ // I have a feeling (but no confirmation yet) that this method of
+ // testing for compound words is probably slow.
+ for (var i = 0, _len = this.compoundRules.length; i < _len; i++) {
+ var ruleText = this.compoundRules[i];
+
+ var expressionText = "";
+
+ for (var j = 0, _jlen = ruleText.length; j < _jlen; j++) {
+ var character = ruleText[j];
+
+ if (character in this.compoundRuleCodes) {
+ expressionText += "(" + this.compoundRuleCodes[character].join("|") + ")";
+ }
+ else {
+ expressionText += character;
+ }
+ }
+
+ this.compoundRules[i] = new RegExp(expressionText, "i");
+ }
+ }
+
+ return this;
+};
+
+Typo.prototype = {
+ /**
+ * Loads a Typo instance from a hash of all of the Typo properties.
+ *
+ * @param object obj A hash of Typo properties, probably gotten from a JSON.parse(JSON.stringify(typo_instance)).
+ */
+
+ load : function (obj) {
+ for (var i in obj) {
+ this[i] = obj[i];
+ }
+
+ return this;
+ },
+
+ /**
+ * Read the contents of a file.
+ *
+ * @param {String} path The path (relative) to the file.
+ * @param {String} [charset="ISO8859-1"] The expected charset of the file
+ * @returns string The file data.
+ */
+
+ _readFile : function (path, charset) {
+ if (!charset) charset = "ISO8859-1";
+
+ var req = new XMLHttpRequest();
+ req.open("GET", path, false);
+
+ if (req.overrideMimeType)
+ req.overrideMimeType("text/plain; charset=" + charset);
+
+ req.send(null);
+
+ return req.responseText;
+ },
+
+ /**
+ * Parse the rules out from a .aff file.
+ *
+ * @param {String} data The contents of the affix file.
+ * @returns object The rules from the file.
+ */
+
+ _parseAFF : function (data) {
+ var rules = {};
+
+ // Remove comment lines
+ data = this._removeAffixComments(data);
+
+ var lines = data.split("\n");
+
+ for (var i = 0, _len = lines.length; i < _len; i++) {
+ var line = lines[i];
+
+ var definitionParts = line.split(/\s+/);
+
+ var ruleType = definitionParts[0];
+
+ if (ruleType == "PFX" || ruleType == "SFX") {
+ var ruleCode = definitionParts[1];
+ var combineable = definitionParts[2];
+ var numEntries = parseInt(definitionParts[3], 10);
+
+ var entries = [];
+
+ for (var j = i + 1, _jlen = i + 1 + numEntries; j < _jlen; j++) {
+ var line = lines[j];
+
+ var lineParts = line.split(/\s+/);
+ var charactersToRemove = lineParts[2];
+
+ var additionParts = lineParts[3].split("/");
+
+ var charactersToAdd = additionParts[0];
+ if (charactersToAdd === "0") charactersToAdd = "";
+
+ var continuationClasses = this.parseRuleCodes(additionParts[1]);
+
+ var regexToMatch = lineParts[4];
+
+ var entry = {};
+ entry.add = charactersToAdd;
+
+ if (continuationClasses.length > 0) entry.continuationClasses = continuationClasses;
+
+ if (regexToMatch !== ".") {
+ if (ruleType === "SFX") {
+ entry.match = new RegExp(regexToMatch + "$");
+ }
+ else {
+ entry.match = new RegExp("^" + regexToMatch);
+ }
+ }
+
+ if (charactersToRemove != "0") {
+ if (ruleType === "SFX") {
+ entry.remove = new RegExp(charactersToRemove + "$");
+ }
+ else {
+ entry.remove = charactersToRemove;
+ }
+ }
+
+ entries.push(entry);
+ }
+
+ rules[ruleCode] = { "type" : ruleType, "combineable" : (combineable == "Y"), "entries" : entries };
+
+ i += numEntries;
+ }
+ else if (ruleType === "COMPOUNDRULE") {
+ var numEntries = parseInt(definitionParts[1], 10);
+
+ for (var j = i + 1, _jlen = i + 1 + numEntries; j < _jlen; j++) {
+ var line = lines[j];
+
+ var lineParts = line.split(/\s+/);
+ this.compoundRules.push(lineParts[1]);
+ }
+
+ i += numEntries;
+ }
+ else if (ruleType === "REP") {
+ var lineParts = line.split(/\s+/);
+
+ if (lineParts.length === 3) {
+ this.replacementTable.push([ lineParts[1], lineParts[2] ]);
+ }
+ }
+ else {
+ // ONLYINCOMPOUND
+ // COMPOUNDMIN
+ // FLAG
+ // KEEPCASE
+ // NEEDAFFIX
+
+ this.flags[ruleType] = definitionParts[1];
+ }
+ }
+
+ return rules;
+ },
+
+ /**
+ * Removes comment lines and then cleans up blank lines and trailing whitespace.
+ *
+ * @param {String} data The data from an affix file.
+ * @return {String} The cleaned-up data.
+ */
+
+ _removeAffixComments : function (data) {
+ // Remove comments
+ data = data.replace(/#.*$/mg, "");
+
+ // Trim each line
+ data = data.replace(/^\s\s*/m, '').replace(/\s\s*$/m, '');
+
+ // Remove blank lines.
+ data = data.replace(/\n{2,}/g, "\n");
+
+ // Trim the entire string
+ data = data.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
+
+ return data;
+ },
+
+ /**
+ * Parses the words out from the .dic file.
+ *
+ * @param {String} data The data from the dictionary file.
+ * @returns object The lookup table containing all of the words and
+ * word forms from the dictionary.
+ */
+
+ _parseDIC : function (data) {
+ data = this._removeDicComments(data);
+
+ var lines = data.split("\n");
+ var dictionaryTable = {};
+
+ function addWord(word, rules) {
+ // Some dictionaries will list the same word multiple times with different rule sets.
+ if (!(word in dictionaryTable) || typeof dictionaryTable[word] != 'object') {
+ dictionaryTable[word] = [];
+ }
+
+ dictionaryTable[word].push(rules);
+ }
+
+ // The first line is the number of words in the dictionary.
+ for (var i = 1, _len = lines.length; i < _len; i++) {
+ var line = lines[i];
+
+ var parts = line.split("/", 2);
+
+ var word = parts[0];
+
+ // Now for each affix rule, generate that form of the word.
+ if (parts.length > 1) {
+ var ruleCodesArray = this.parseRuleCodes(parts[1]);
+
+ // Save the ruleCodes for compound word situations.
+ if (!("NEEDAFFIX" in this.flags) || ruleCodesArray.indexOf(this.flags.NEEDAFFIX) == -1) {
+ addWord(word, ruleCodesArray);
+ }
+
+ for (var j = 0, _jlen = ruleCodesArray.length; j < _jlen; j++) {
+ var code = ruleCodesArray[j];
+
+ var rule = this.rules[code];
+
+ if (rule) {
+ var newWords = this._applyRule(word, rule);
+
+ for (var ii = 0, _iilen = newWords.length; ii < _iilen; ii++) {
+ var newWord = newWords[ii];
+
+ addWord(newWord, []);
+
+ if (rule.combineable) {
+ for (var k = j + 1; k < _jlen; k++) {
+ var combineCode = ruleCodesArray[k];
+
+ var combineRule = this.rules[combineCode];
+
+ if (combineRule) {
+ if (combineRule.combineable && (rule.type != combineRule.type)) {
+ var otherNewWords = this._applyRule(newWord, combineRule);
+
+ for (var iii = 0, _iiilen = otherNewWords.length; iii < _iiilen; iii++) {
+ var otherNewWord = otherNewWords[iii];
+ addWord(otherNewWord, []);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (code in this.compoundRuleCodes) {
+ this.compoundRuleCodes[code].push(word);
+ }
+ }
+ }
+ else {
+ addWord(word.trim(), []);
+ }
+ }
+
+ return dictionaryTable;
+ },
+
+
+ /**
+ * Removes comment lines and then cleans up blank lines and trailing whitespace.
+ *
+ * @param {String} data The data from a .dic file.
+ * @return {String} The cleaned-up data.
+ */
+
+ _removeDicComments : function (data) {
+ // I can't find any official documentation on it, but at least the de_DE
+ // dictionary uses tab-indented lines as comments.
+
+ // Remove comments
+ data = data.replace(/^\t.*$/mg, "");
+
+ return data;
+
+ // Trim each line
+ data = data.replace(/^\s\s*/m, '').replace(/\s\s*$/m, '');
+
+ // Remove blank lines.
+ data = data.replace(/\n{2,}/g, "\n");
+
+ // Trim the entire string
+ data = data.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
+
+ return data;
+ },
+
+ parseRuleCodes : function (textCodes) {
+ if (!textCodes) {
+ return [];
+ }
+ else if (!("FLAG" in this.flags)) {
+ return textCodes.split("");
+ }
+ else if (this.flags.FLAG === "long") {
+ var flags = [];
+
+ for (var i = 0, _len = textCodes.length; i < _len; i += 2) {
+ flags.push(textCodes.substr(i, 2));
+ }
+
+ return flags;
+ }
+ else if (this.flags.FLAG === "num") {
+ return textCode.split(",");
+ }
+ },
+
+ /**
+ * Applies an affix rule to a word.
+ *
+ * @param {String} word The base word.
+ * @param {Object} rule The affix rule.
+ * @returns {String[]} The new words generated by the rule.
+ */
+
+ _applyRule : function (word, rule) {
+ var entries = rule.entries;
+ var newWords = [];
+
+ for (var i = 0, _len = entries.length; i < _len; i++) {
+ var entry = entries[i];
+
+ if (!entry.match || word.match(entry.match)) {
+ var newWord = word;
+
+ if (entry.remove) {
+ newWord = newWord.replace(entry.remove, "");
+ }
+
+ if (rule.type === "SFX") {
+ newWord = newWord + entry.add;
+ }
+ else {
+ newWord = entry.add + newWord;
+ }
+
+ newWords.push(newWord);
+
+ if ("continuationClasses" in entry) {
+ for (var j = 0, _jlen = entry.continuationClasses.length; j < _jlen; j++) {
+ var continuationRule = this.rules[entry.continuationClasses[j]];
+
+ if (continuationRule) {
+ newWords = newWords.concat(this._applyRule(newWord, continuationRule));
+ }
+ /*
+ else {
+ // This shouldn't happen, but it does, at least in the de_DE dictionary.
+ // I think the author mistakenly supplied lower-case rule codes instead
+ // of upper-case.
+ }
+ */
+ }
+ }
+ }
+ }
+
+ return newWords;
+ },
+
+ /**
+ * Checks whether a word or a capitalization variant exists in the current dictionary.
+ * The word is trimmed and several variations of capitalizations are checked.
+ * If you want to check a word without any changes made to it, call checkExact()
+ *
+ * @see http://blog.stevenlevithan.com/archives/faster-trim-javascript re:trimming function
+ *
+ * @param {String} aWord The word to check.
+ * @returns {Boolean}
+ */
+
+ check : function (aWord) {
+ // Remove leading and trailing whitespace
+ var trimmedWord = aWord.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
+
+ if (this.checkExact(trimmedWord)) {
+ return true;
+ }
+
+ // The exact word is not in the dictionary.
+ if (trimmedWord.toUpperCase() === trimmedWord) {
+ // The word was supplied in all uppercase.
+ // Check for a capitalized form of the word.
+ var capitalizedWord = trimmedWord[0] + trimmedWord.substring(1).toLowerCase();
+
+ if (this.hasFlag(capitalizedWord, "KEEPCASE")) {
+ // Capitalization variants are not allowed for this word.
+ return false;
+ }
+
+ if (this.checkExact(capitalizedWord)) {
+ return true;
+ }
+ }
+
+ var lowercaseWord = trimmedWord.toLowerCase();
+
+ if (lowercaseWord !== trimmedWord) {
+ if (this.hasFlag(lowercaseWord, "KEEPCASE")) {
+ // Capitalization variants are not allowed for this word.
+ return false;
+ }
+
+ // Check for a lowercase form
+ if (this.checkExact(lowercaseWord)) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ /**
+ * Checks whether a word exists in the current dictionary.
+ *
+ * @param {String} word The word to check.
+ * @returns {Boolean}
+ */
+
+ checkExact : function (word) {
+ var ruleCodes = this.dictionaryTable[word];
+
+ if (typeof ruleCodes === 'undefined') {
+ // Check if this might be a compound word.
+ if ("COMPOUNDMIN" in this.flags && word.length >= this.flags.COMPOUNDMIN) {
+ for (var i = 0, _len = this.compoundRules.length; i < _len; i++) {
+ if (word.match(this.compoundRules[i])) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+ else {
+ for (var i = 0, _len = ruleCodes.length; i < _len; i++) {
+ if (!this.hasFlag(word, "ONLYINCOMPOUND", ruleCodes[i])) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ },
+
+ /**
+ * Looks up whether a given word is flagged with a given flag.
+ *
+ * @param {String} word The word in question.
+ * @param {String} flag The flag in question.
+ * @return {Boolean}
+ */
+
+ hasFlag : function (word, flag, wordFlags) {
+ if (flag in this.flags) {
+ if (typeof wordFlags === 'undefined') {
+ var wordFlags = Array.prototype.concat.apply([], this.dictionaryTable[word]);
+ }
+
+ if (wordFlags && wordFlags.indexOf(this.flags[flag]) !== -1) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ /**
+ * Returns a list of suggestions for a misspelled word.
+ *
+ * @see http://www.norvig.com/spell-correct.html for the basis of this suggestor.
+ * This suggestor is primitive, but it works.
+ *
+ * @param {String} word The misspelling.
+ * @param {Number} [limit=5] The maximum number of suggestions to return.
+ * @returns {String[]} The array of suggestions.
+ */
+
+ alphabet : "",
+
+ suggest : function (word, limit) {
+ if (!limit) limit = 5;
+
+ if (this.check(word)) return [];
+
+ // Check the replacement table.
+ for (var i = 0, _len = this.replacementTable.length; i < _len; i++) {
+ var replacementEntry = this.replacementTable[i];
+
+ if (word.indexOf(replacementEntry[0]) !== -1) {
+ var correctedWord = word.replace(replacementEntry[0], replacementEntry[1]);
+
+ if (this.check(correctedWord)) {
+ return [ correctedWord ];
+ }
+ }
+ }
+
+ var self = this;
+ self.alphabet = "abcdefghijklmnopqrstuvwxyz";
+
+ /*
+ if (!self.alphabet) {
+ // Use the alphabet as implicitly defined by the words in the dictionary.
+ var alphaHash = {};
+
+ for (var i in self.dictionaryTable) {
+ for (var j = 0, _len = i.length; j < _len; j++) {
+ alphaHash[i[j]] = true;
+ }
+ }
+
+ for (var i in alphaHash) {
+ self.alphabet += i;
+ }
+
+ var alphaArray = self.alphabet.split("");
+ alphaArray.sort();
+ self.alphabet = alphaArray.join("");
+ }
+ */
+
+ function edits1(words) {
+ var rv = [];
+
+ for (var ii = 0, _iilen = words.length; ii < _iilen; ii++) {
+ var word = words[ii];
+
+ var splits = [];
+
+ for (var i = 0, _len = word.length + 1; i < _len; i++) {
+ splits.push([ word.substring(0, i), word.substring(i, word.length) ]);
+ }
+
+ var deletes = [];
+
+ for (var i = 0, _len = splits.length; i < _len; i++) {
+ var s = splits[i];
+
+ if (s[1]) {
+ deletes.push(s[0] + s[1].substring(1));
+ }
+ }
+
+ var transposes = [];
+
+ for (var i = 0, _len = splits.length; i < _len; i++) {
+ var s = splits[i];
+
+ if (s[1].length > 1) {
+ transposes.push(s[0] + s[1][1] + s[1][0] + s[1].substring(2));
+ }
+ }
+
+ var replaces = [];
+
+ for (var i = 0, _len = splits.length; i < _len; i++) {
+ var s = splits[i];
+
+ if (s[1]) {
+ for (var j = 0, _jlen = self.alphabet.length; j < _jlen; j++) {
+ replaces.push(s[0] + self.alphabet[j] + s[1].substring(1));
+ }
+ }
+ }
+
+ var inserts = [];
+
+ for (var i = 0, _len = splits.length; i < _len; i++) {
+ var s = splits[i];
+
+ if (s[1]) {
+ for (var j = 0, _jlen = self.alphabet.length; j < _jlen; j++) {
+ replaces.push(s[0] + self.alphabet[j] + s[1]);
+ }
+ }
+ }
+
+ rv = rv.concat(deletes);
+ rv = rv.concat(transposes);
+ rv = rv.concat(replaces);
+ rv = rv.concat(inserts);
+ }
+
+ return rv;
+ }
+
+ function known(words) {
+ var rv = [];
+
+ for (var i = 0; i < words.length; i++) {
+ if (self.check(words[i])) {
+ rv.push(words[i]);
+ }
+ }
+
+ return rv;
+ }
+
+ function correct(word) {
+ // Get the edit-distance-1 and edit-distance-2 forms of this word.
+ var ed1 = edits1([word]);
+ var ed2 = edits1(ed1);
+
+ var corrections = known(ed1).concat(known(ed2));
+
+ // Sort the edits based on how many different ways they were created.
+ var weighted_corrections = {};
+
+ for (var i = 0, _len = corrections.length; i < _len; i++) {
+ if (!(corrections[i] in weighted_corrections)) {
+ weighted_corrections[corrections[i]] = 1;
+ }
+ else {
+ weighted_corrections[corrections[i]] += 1;
+ }
+ }
+
+ var sorted_corrections = [];
+
+ for (var i in weighted_corrections) {
+ sorted_corrections.push([ i, weighted_corrections[i] ]);
+ }
+
+ function sorter(a, b) {
+ if (a[1] < b[1]) {
+ return -1;
+ }
+
+ return 1;
+ }
+
+ sorted_corrections.sort(sorter).reverse();
+
+ var rv = [];
+
+ for (var i = 0, _len = Math.min(limit, sorted_corrections.length); i < _len; i++) {
+ if (!self.hasFlag(sorted_corrections[i][0], "NOSUGGEST")) {
+ rv.push(sorted_corrections[i][0]);
+ }
+ }
+
+ return rv;
+ }
+
+ return correct(word);
+ }
+};
+; browserify_shim__define__module__export__(typeof Typo != "undefined" ? Typo : window.Typo);
+
+}).call(global, undefined, undefined, undefined, undefined, function defineExport(ex) { module.exports = ex; });
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+
+},{}],3:[function(require,module,exports){
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://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("fullScreen", false, function(cm, val, old) {
+ if (old == CodeMirror.Init) old = false;
+ if (!old == !val) return;
+ if (val) setFullscreen(cm);
+ else setNormal(cm);
+ });
+
+ function setFullscreen(cm) {
+ var wrap = cm.getWrapperElement();
+ cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset,
+ width: wrap.style.width, height: wrap.style.height};
+ wrap.style.width = "";
+ wrap.style.height = "auto";
+ wrap.className += " CodeMirror-fullscreen";
+ document.documentElement.style.overflow = "hidden";
+ cm.refresh();
+ }
+
+ function setNormal(cm) {
+ var wrap = cm.getWrapperElement();
+ wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, "");
+ document.documentElement.style.overflow = "";
+ var info = cm.state.fullScreenRestore;
+ wrap.style.width = info.width; wrap.style.height = info.height;
+ window.scrollTo(info.scrollLeft, info.scrollTop);
+ cm.refresh();
+ }
+});
+
+},{"../../lib/codemirror":6}],4:[function(require,module,exports){
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://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*)(>[> ]*|[*+-]\s|(\d+)([.)]))(\s*)/,
+ emptyListRE = /^(\s*)(>[> ]*|[*+-]|(\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;
+ var eolState = cm.getStateAfter(pos.line);
+ var inList = eolState.list !== false;
+ var inQuote = eolState.quote !== 0;
+
+ var line = cm.getLine(pos.line), match = listRE.exec(line);
+ if (!ranges[i].empty() || (!inList && !inQuote) || !match) {
+ cm.execCommand("newlineAndIndent");
+ return;
+ }
+ if (emptyListRE.test(line)) {
+ 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 bullet = unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0
+ ? match[2]
+ : (parseInt(match[3], 10) + 1) + match[4];
+
+ replacements[i] = "\n" + indent + bullet + after;
+ }
+ }
+
+ cm.replaceSelections(replacements);
+ };
+});
+
+},{"../../lib/codemirror":6}],5:[function(require,module,exports){
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+// Utility function that allows modes to be combined. The mode given
+// as the base argument takes care of most of the normal mode
+// functionality, but a second (typically simple) mode is used, which
+// can override the style of text. Both modes get to parse all of the
+// text, but when both assign a non-null style to a piece of code, the
+// overlay wins, unless the combine argument was true and not overridden,
+// or state.overlay.combineTokens was true, in which case the styles are
+// combined.
+
+(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.overlayMode = function(base, overlay, combine) {
+ return {
+ startState: function() {
+ return {
+ base: CodeMirror.startState(base),
+ overlay: CodeMirror.startState(overlay),
+ basePos: 0, baseCur: null,
+ overlayPos: 0, overlayCur: null,
+ streamSeen: null
+ };
+ },
+ copyState: function(state) {
+ return {
+ base: CodeMirror.copyState(base, state.base),
+ overlay: CodeMirror.copyState(overlay, state.overlay),
+ basePos: state.basePos, baseCur: null,
+ overlayPos: state.overlayPos, overlayCur: null
+ };
+ },
+
+ token: function(stream, state) {
+ if (stream != state.streamSeen ||
+ Math.min(state.basePos, state.overlayPos) < stream.start) {
+ state.streamSeen = stream;
+ state.basePos = state.overlayPos = stream.start;
+ }
+
+ if (stream.start == state.basePos) {
+ state.baseCur = base.token(stream, state.base);
+ state.basePos = stream.pos;
+ }
+ if (stream.start == state.overlayPos) {
+ stream.pos = stream.start;
+ state.overlayCur = overlay.token(stream, state.overlay);
+ state.overlayPos = stream.pos;
+ }
+ stream.pos = Math.min(state.basePos, state.overlayPos);
+
+ // state.overlay.combineTokens always takes precedence over combine,
+ // unless set to null
+ if (state.overlayCur == null) return state.baseCur;
+ else if (state.baseCur != null &&
+ state.overlay.combineTokens ||
+ combine && state.overlay.combineTokens == null)
+ return state.baseCur + " " + state.overlayCur;
+ else return state.overlayCur;
+ },
+
+ indent: base.indent && function(state, textAfter) {
+ return base.indent(state.base, textAfter);
+ },
+ electricChars: base.electricChars,
+
+ innerMode: function(state) { return {state: state.base, mode: base}; },
+
+ blankLine: function(state) {
+ if (base.blankLine) base.blankLine(state.base);
+ if (overlay.blankLine) overlay.blankLine(state.overlay);
+ }
+ };
+};
+
+});
+
+},{"../../lib/codemirror":6}],6:[function(require,module,exports){
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+// This is CodeMirror (http://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(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ module.exports = mod();
+ else if (typeof define == "function" && define.amd) // AMD
+ return define([], mod);
+ else // Plain browser env
+ this.CodeMirror = mod();
+})(function() {
+ "use strict";
+
+ // BROWSER SNIFFING
+
+ // 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 ie = ie_upto10 || ie_11up;
+ var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]);
+ var webkit = /WebKit\//.test(userAgent);
+ var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
+ var chrome = /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 = /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.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 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);
+
+ // Optimize some code when these features are not used.
+ var sawReadOnlySpans = false, sawCollapsedSpans = false;
+
+ // EDITOR CONSTRUCTOR
+
+ // A CodeMirror instance represents an editor. This is the object
+ // that user code is usually dealing with.
+
+ function CodeMirror(place, options) {
+ 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);
+ setGuttersForLineNumbers(options);
+
+ var doc = options.value;
+ if (typeof doc == "string") doc = new Doc(doc, options.mode, null, options.lineSeparator);
+ this.doc = doc;
+
+ var input = new CodeMirror.inputStyles[options.inputStyle](this);
+ var display = this.display = new Display(place, doc, input);
+ display.wrapper.CodeMirror = this;
+ updateGutters(this);
+ themeChanged(this);
+ if (options.lineWrapping)
+ this.display.wrapper.className += " CodeMirror-wrap";
+ if (options.autofocus && !mobile) display.input.focus();
+ 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: false, cutIncoming: false, // 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
+ };
+
+ var cm = this;
+
+ // Override magic textarea content restore that IE sometimes does
+ // on our hidden textarea on reload
+ if (ie && ie_version < 11) setTimeout(function() { cm.display.input.reset(true); }, 20);
+
+ registerEventHandlers(this);
+ ensureGlobalHandlers();
+
+ startOperation(this);
+ this.curOp.forceUpdate = true;
+ attachDoc(this, doc);
+
+ if ((options.autofocus && !mobile) || cm.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";
+ }
+
+ // DISPLAY CONSTRUCTOR
+
+ // 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) {
+ 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 = elt("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 = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
+ null, "position: relative; outline: none");
+ // Moved around its parent to cover visible view.
+ d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-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;
+
+ input.init(d);
+ }
+
+ // STATE UPDATES
+
+ // Used to get the editor into a consistent state again when options change.
+
+ function loadMode(cm) {
+ cm.doc.mode = CodeMirror.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.frontier = cm.doc.first;
+ startWorker(cm, 100);
+ cm.state.modeGen++;
+ if (cm.curOp) regChange(cm);
+ }
+
+ 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(){updateScrollbars(cm);}, 100);
+ }
+
+ // 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);
+ });
+ }
+
+ 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);
+ }
+
+ function guttersChanged(cm) {
+ updateGutters(cm);
+ regChange(cm);
+ setTimeout(function(){alignHorizontally(cm);}, 20);
+ }
+
+ // Rebuild the gutter elements, ensure the margin to the left of the
+ // code matches their width.
+ function updateGutters(cm) {
+ var gutters = cm.display.gutters, specs = cm.options.gutters;
+ removeChildren(gutters);
+ for (var i = 0; i < specs.length; ++i) {
+ var gutterClass = specs[i];
+ var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
+ if (gutterClass == "CodeMirror-linenumbers") {
+ cm.display.lineGutter = gElt;
+ gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
+ }
+ }
+ gutters.style.display = i ? "" : "none";
+ updateGutterSpace(cm);
+ }
+
+ function updateGutterSpace(cm) {
+ var width = cm.display.gutters.offsetWidth;
+ cm.display.sizer.style.marginLeft = width + "px";
+ }
+
+ // 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 = merged.find(0, true);
+ len -= cur.text.length - found.from.ch;
+ cur = found.to.line;
+ len += cur.text.length - found.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;
+ }
+ });
+ }
+
+ // Make sure the gutters options contains the element
+ // "CodeMirror-linenumbers" when the lineNumbers option is true.
+ function setGuttersForLineNumbers(options) {
+ var found = indexOf(options.gutters, "CodeMirror-linenumbers");
+ if (found == -1 && options.lineNumbers) {
+ options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
+ } else if (found > -1 && !options.lineNumbers) {
+ options.gutters = options.gutters.slice(0);
+ options.gutters.splice(found, 1);
+ }
+ }
+
+ // 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
+ };
+ }
+
+ function NativeScrollbars(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");
+ 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.checkedOverlay = 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 = copyObj({
+ 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 =
+ (measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
+ } else {
+ this.horiz.style.display = "";
+ this.horiz.firstChild.style.width = "0";
+ }
+
+ if (!this.checkedOverlay && measure.clientHeight > 0) {
+ if (sWidth == 0) this.overlayHack();
+ this.checkedOverlay = true;
+ }
+
+ return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0};
+ },
+ setScrollLeft: function(pos) {
+ if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos;
+ },
+ setScrollTop: function(pos) {
+ if (this.vert.scrollTop != pos) this.vert.scrollTop = pos;
+ },
+ overlayHack: function() {
+ var w = mac && !mac_geMountainLion ? "12px" : "18px";
+ this.horiz.style.minHeight = this.vert.style.minWidth = w;
+ var self = this;
+ var barMouseDown = function(e) {
+ if (e_target(e) != self.vert && e_target(e) != self.horiz)
+ operation(self.cm, onMouseDown)(e);
+ };
+ on(this.vert, "mousedown", barMouseDown);
+ on(this.horiz, "mousedown", barMouseDown);
+ },
+ clear: function() {
+ var parent = this.horiz.parentNode;
+ parent.removeChild(this.horiz);
+ parent.removeChild(this.vert);
+ }
+ }, NativeScrollbars.prototype);
+
+ function NullScrollbars() {}
+
+ NullScrollbars.prototype = copyObj({
+ update: function() { return {bottom: 0, right: 0}; },
+ setScrollLeft: function() {},
+ setScrollTop: function() {},
+ clear: function() {}
+ }, NullScrollbars.prototype);
+
+ CodeMirror.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 CodeMirror.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() { cm.display.input.focus(); }, 0);
+ });
+ node.setAttribute("cm-not-content", "true");
+ }, function(pos, axis) {
+ if (axis == "horizontal") setScrollLeft(cm, pos);
+ else setScrollTop(cm, pos);
+ }, cm);
+ if (cm.display.scrollbars.addClass)
+ addClass(cm.display.wrapper, cm.display.scrollbars.addClass);
+ }
+
+ 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";
+
+ 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 = "";
+ }
+
+ // 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)};
+ }
+
+ // LINE NUMBERS
+
+ // 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 && view[i].gutter)
+ view[i].gutter.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);
+ return true;
+ }
+ return false;
+ }
+
+ function lineNumberFor(options, i) {
+ return String(options.lineNumberFormatter(i + options.firstLineNumber));
+ }
+
+ // 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;
+ }
+
+ // DISPLAY DRAWING
+
+ function DisplayUpdate(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;
+ }
+ }
+
+ // 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 focused = activeElt();
+ 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.
+ if (focused && activeElt() != focused && focused.offsetHeight) focused.focus();
+
+ // 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;
+ }
+ if (!updateDisplayIfNeeded(cm, update)) break;
+ updateHeightsInViewport(cm);
+ var barMeasure = measureForScrollbars(cm);
+ updateSelection(cm);
+ setDocumentHeight(cm, barMeasure);
+ updateScrollbars(cm, barMeasure);
+ }
+
+ 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);
+ setDocumentHeight(cm, barMeasure);
+ updateScrollbars(cm, barMeasure);
+ update.finish();
+ }
+ }
+
+ function setDocumentHeight(cm, measure) {
+ cm.display.sizer.style.minHeight = measure.docHeight + "px";
+ var total = measure.docHeight + cm.display.barHeight;
+ cm.display.heightForcer.style.top = total + "px";
+ cm.display.gutters.style.height = Math.max(total + scrollGap(cm), measure.clientHeight) + "px";
+ }
+
+ // 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], height;
+ 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;
+ }
+ var diff = cur.line.height - height;
+ if (height < 2) height = textHeight(display);
+ if (diff > .001 || diff < -.001) {
+ updateLineHeight(cur.line, height);
+ updateWidgetHeight(cur.line);
+ if (cur.rest) for (var j = 0; j < cur.rest.length; j++)
+ updateWidgetHeight(cur.rest[j]);
+ }
+ }
+ }
+
+ // 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)
+ line.widgets[i].height = line.widgets[i].node.offsetHeight;
+ }
+
+ // 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) {
+ left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
+ width[cm.options.gutters[i]] = n.clientWidth;
+ }
+ return {fixedPos: compensateForHScroll(d),
+ gutterTotalWidth: d.gutters.offsetWidth,
+ gutterLeft: left,
+ gutterWidth: width,
+ wrapperWidth: d.wrapper.clientWidth};
+ }
+
+ // 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);
+ }
+
+ // 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(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(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);
+ }
+ }
+
+ // 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(lineView);
+ } else if (cls) {
+ lineView.text.className = cls;
+ }
+ }
+
+ function updateLineClasses(lineView) {
+ updateLineBackground(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");
+ wrap.insertBefore(lineView.gutterBackground, lineView.text);
+ }
+ var markers = lineView.line.gutterMarkers;
+ if (cm.options.lineNumbers || markers) {
+ var wrap = 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.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.options.gutters.length; ++k) {
+ var id = cm.options.gutters[k], 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;
+ for (var node = lineView.node.firstChild, next; node; node = next) {
+ var next = node.nextSibling;
+ if (node.className == "CodeMirror-linewidget")
+ 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(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");
+ 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";
+ }
+ }
+
+ // POSITION OBJECT
+
+ // A Pos instance represents a position within the text.
+ var Pos = CodeMirror.Pos = function(line, ch) {
+ if (!(this instanceof Pos)) return new Pos(line, ch);
+ this.line = line; this.ch = ch;
+ };
+
+ // Compare two positions, return 0 if they are the same, a negative
+ // number when a is less, and a positive number otherwise.
+ var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; };
+
+ 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; }
+
+ // INPUT HANDLING
+
+ function ensureFocus(cm) {
+ if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
+ }
+
+ function isReadOnly(cm) {
+ return cm.options.readOnly || cm.doc.cantEdit;
+ }
+
+ // This will be set to an array of strings when copying, so that,
+ // when pasting, we know what kind of selections the copied text
+ // was made out of.
+ var lastCopied = null;
+
+ function applyTextInput(cm, inserted, deleted, sel, origin) {
+ var doc = cm.doc;
+ cm.display.shift = false;
+ if (!sel) sel = doc.sel;
+
+ var paste = cm.state.pasteIncoming || origin == "paste";
+ var textLines = doc.splitLines(inserted), multiPaste = null;
+ // When pasing N lines into N selections, insert one line per selection
+ if (paste && sel.ranges.length > 1) {
+ if (lastCopied && lastCopied.join("\n") == inserted) {
+ if (sel.ranges.length % lastCopied.length == 0) {
+ multiPaste = [];
+ for (var i = 0; i < lastCopied.length; i++)
+ multiPaste.push(doc.splitLines(lastCopied[i]));
+ }
+ } else if (textLines.length == sel.ranges.length) {
+ multiPaste = map(textLines, function(l) { return [l]; });
+ }
+ }
+
+ // Normal behavior is to insert the new text into every selection
+ for (var i = sel.ranges.length - 1; i >= 0; i--) {
+ var range = sel.ranges[i];
+ 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));
+ }
+ var updateInput = cm.curOp.updateInput;
+ var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
+ origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")};
+ makeChange(cm.doc, changeEvent);
+ signalLater(cm, "inputRead", cm, changeEvent);
+ }
+ if (inserted && !paste)
+ triggerElectric(cm, inserted);
+
+ ensureCursorVisible(cm);
+ cm.curOp.updateInput = updateInput;
+ cm.curOp.typing = true;
+ cm.state.pasteIncoming = cm.state.cutIncoming = false;
+ }
+
+ function handlePaste(e, cm) {
+ var pasted = e.clipboardData && e.clipboardData.getData("text/plain");
+ if (pasted) {
+ e.preventDefault();
+ if (!isReadOnly(cm) && !cm.options.disableInput)
+ runInOp(cm, function() { 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) {
+ field.setAttribute("autocorrect", "off");
+ field.setAttribute("autocapitalize", "off");
+ field.setAttribute("spellcheck", "false");
+ }
+
+ // TEXTAREA INPUT STYLE
+
+ function TextareaInput(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();
+ // Tracks when input.reset has punted to just putting a short
+ // string into the textarea instead of the full selection.
+ this.inaccurateSelection = false;
+ // Used to work around IE issue with selection being forgotten when focus moves away from textarea
+ this.hasSelection = false;
+ this.composing = null;
+ };
+
+ function hiddenTextarea() {
+ var te = elt("textarea", null, null, "position: absolute; 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;
+ }
+
+ TextareaInput.prototype = copyObj({
+ init: function(display) {
+ var input = this, cm = this.cm;
+
+ // Wraps and hides input textarea
+ var div = this.wrapper = hiddenTextarea();
+ // The semihidden textarea that is focused when the editor is
+ // focused, and receives input.
+ var te = this.textarea = div.firstChild;
+ display.wrapper.insertBefore(div, 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 && input.hasSelection) input.hasSelection = null;
+ input.poll();
+ });
+
+ on(te, "paste", function(e) {
+ if (handlePaste(e, cm)) return true;
+
+ cm.state.pasteIncoming = true;
+ input.fastPoll();
+ });
+
+ function prepareCopyCut(e) {
+ if (cm.somethingSelected()) {
+ lastCopied = cm.getSelections();
+ if (input.inaccurateSelection) {
+ input.prevInput = "";
+ input.inaccurateSelection = false;
+ te.value = lastCopied.join("\n");
+ selectInput(te);
+ }
+ } else if (!cm.options.lineWiseCopyCut) {
+ return;
+ } else {
+ var ranges = copyableRanges(cm);
+ lastCopied = 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 = true;
+ }
+ on(te, "cut", prepareCopyCut);
+ on(te, "copy", prepareCopyCut);
+
+ on(display.scroller, "paste", function(e) {
+ if (eventInWidget(display, e)) return;
+ cm.state.pasteIncoming = true;
+ input.focus();
+ });
+
+ // 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;
+ }
+ });
+ },
+
+ 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;
+ },
+
+ 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)
+ reset: function(typing) {
+ if (this.contextMenuPending) return;
+ var minimal, selected, cm = this.cm, doc = cm.doc;
+ if (cm.somethingSelected()) {
+ this.prevInput = "";
+ var range = doc.sel.primary();
+ minimal = hasCopyEvent &&
+ (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000);
+ var content = minimal ? "-" : selected || 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;
+ }
+ this.inaccurateSelection = minimal;
+ },
+
+ getField: function() { return this.textarea; },
+
+ supportsTouch: function() { return false; },
+
+ 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
+ }
+ },
+
+ blur: function() { this.textarea.blur(); },
+
+ resetPosition: function() {
+ this.wrapper.style.top = this.wrapper.style.left = 0;
+ },
+
+ receivedFocus: function() { this.slowPoll(); },
+
+ // Poll for input changes, using the normal rate of polling. This
+ // runs as long as the editor is focused.
+ slowPoll: function() {
+ var input = this;
+ if (input.pollingFast) return;
+ input.polling.set(this.cm.options.pollInterval, function() {
+ input.poll();
+ if (input.cm.state.focused) input.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.
+ 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).
+ poll: function() {
+ 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) ||
+ isReadOnly(cm) || 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;
+
+ var self = this;
+ runInOp(cm, function() {
+ applyTextInput(cm, text.slice(same), prevInput.length - same,
+ null, self.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 = self.prevInput = "";
+ else self.prevInput = text;
+
+ if (self.composing) {
+ self.composing.range.clear();
+ self.composing.range = cm.markText(self.composing.start, cm.getCursor("to"),
+ {className: "CodeMirror-composing"});
+ }
+ });
+ return true;
+ },
+
+ ensurePolled: function() {
+ if (this.pollingFast && this.poll()) this.pollingFast = false;
+ },
+
+ onKeyPress: function() {
+ if (ie && ie_version >= 9) this.hasSelection = null;
+ this.fastPoll();
+ },
+
+ onContextMenu: function(e) {
+ var input = this, cm = input.cm, display = cm.display, te = input.textarea;
+ 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;
+ input.wrapper.style.position = "absolute";
+ te.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
+ "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " +
+ (ie ? "rgba(255, 255, 255, .05)" : "transparent") +
+ "; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
+ if (webkit) var 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 = true;
+ 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() {
+ input.contextMenuPending = false;
+ input.wrapper.style.position = "relative";
+ 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, commands.selectAll)(cm);
+ else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500);
+ else 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);
+ }
+ },
+
+ readOnlyChanged: function(val) {
+ if (!val) this.reset();
+ },
+
+ setUneditable: nothing,
+
+ needsContentAttribute: false
+ }, TextareaInput.prototype);
+
+ // CONTENTEDITABLE INPUT STYLE
+
+ function ContentEditableInput(cm) {
+ this.cm = cm;
+ this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
+ this.polling = new Delayed();
+ this.gracePeriod = false;
+ }
+
+ ContentEditableInput.prototype = copyObj({
+ init: function(display) {
+ var input = this, cm = input.cm;
+ var div = input.div = display.lineDiv;
+ disableBrowserMagic(div);
+
+ on(div, "paste", function(e) { handlePaste(e, cm); })
+
+ on(div, "compositionstart", function(e) {
+ var data = e.data;
+ input.composing = {sel: cm.doc.sel, data: data, startData: data};
+ if (!data) return;
+ var prim = cm.doc.sel.primary();
+ var line = cm.getLine(prim.head.line);
+ var found = line.indexOf(data, Math.max(0, prim.head.ch - data.length));
+ if (found > -1 && found <= prim.head.ch)
+ input.composing.sel = simpleSelection(Pos(prim.head.line, found),
+ Pos(prim.head.line, found + data.length));
+ });
+ on(div, "compositionupdate", function(e) {
+ input.composing.data = e.data;
+ });
+ on(div, "compositionend", function(e) {
+ var ours = input.composing;
+ if (!ours) return;
+ if (e.data != ours.startData && !/\u200b/.test(e.data))
+ ours.data = e.data;
+ // Need a small delay to prevent other code (input event,
+ // selection polling) from doing damage when fired right after
+ // compositionend.
+ setTimeout(function() {
+ if (!ours.handled)
+ input.applyComposition(ours);
+ if (input.composing == ours)
+ input.composing = null;
+ }, 50);
+ });
+
+ on(div, "touchstart", function() {
+ input.forceCompositionEnd();
+ });
+
+ on(div, "input", function() {
+ if (input.composing) return;
+ if (isReadOnly(cm) || !input.pollContent())
+ runInOp(input.cm, function() {regChange(cm);});
+ });
+
+ function onCopyCut(e) {
+ if (cm.somethingSelected()) {
+ lastCopied = cm.getSelections();
+ if (e.type == "cut") cm.replaceSelection("", null, "cut");
+ } else if (!cm.options.lineWiseCopyCut) {
+ return;
+ } else {
+ var ranges = copyableRanges(cm);
+ lastCopied = ranges.text;
+ if (e.type == "cut") {
+ cm.operation(function() {
+ cm.setSelections(ranges.ranges, 0, sel_dontScroll);
+ cm.replaceSelection("", null, "cut");
+ });
+ }
+ }
+ // iOS exposes the clipboard API, but seems to discard content inserted into it
+ if (e.clipboardData && !ios) {
+ e.preventDefault();
+ e.clipboardData.clearData();
+ e.clipboardData.setData("text/plain", lastCopied.join("\n"));
+ } else {
+ // 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.join("\n");
+ var hadFocus = document.activeElement;
+ selectInput(te);
+ setTimeout(function() {
+ cm.display.lineSpace.removeChild(kludge);
+ hadFocus.focus();
+ }, 50);
+ }
+ }
+ on(div, "copy", onCopyCut);
+ on(div, "cut", onCopyCut);
+ },
+
+ prepareSelection: function() {
+ var result = prepareSelection(this.cm, false);
+ result.focus = this.cm.state.focused;
+ return result;
+ },
+
+ showSelection: function(info) {
+ if (!info || !this.cm.display.view.length) return;
+ if (info.focus) this.showPrimarySelection();
+ this.showMultipleSelections(info);
+ },
+
+ showPrimarySelection: function() {
+ var sel = window.getSelection(), prim = this.cm.doc.sel.primary();
+ var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset);
+ var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset);
+ if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
+ cmp(minPos(curAnchor, curFocus), prim.from()) == 0 &&
+ cmp(maxPos(curAnchor, curFocus), prim.to()) == 0)
+ return;
+
+ var start = posToDOM(this.cm, prim.from());
+ var end = posToDOM(this.cm, prim.to());
+ if (!start && !end) return;
+
+ var view = this.cm.display.view;
+ var old = sel.rangeCount && sel.getRangeAt(0);
+ if (!start) {
+ start = {node: view[0].measure.map[2], offset: 0};
+ } else if (!end) { // FIXME dangerously hacky
+ 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]};
+ }
+
+ try { var 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) {
+ sel.removeAllRanges();
+ sel.addRange(rng);
+ if (old && sel.anchorNode == null) sel.addRange(old);
+ else if (gecko) this.startGracePeriod();
+ }
+ this.rememberSelection();
+ },
+
+ startGracePeriod: function() {
+ var input = this;
+ clearTimeout(this.gracePeriod);
+ this.gracePeriod = setTimeout(function() {
+ input.gracePeriod = false;
+ if (input.selectionChanged())
+ input.cm.operation(function() { input.cm.curOp.selectionChanged = true; });
+ }, 20);
+ },
+
+ showMultipleSelections: function(info) {
+ removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
+ removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
+ },
+
+ rememberSelection: function() {
+ var sel = window.getSelection();
+ this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
+ this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
+ },
+
+ selectionInEditor: function() {
+ var sel = window.getSelection();
+ if (!sel.rangeCount) return false;
+ var node = sel.getRangeAt(0).commonAncestorContainer;
+ return contains(this.div, node);
+ },
+
+ focus: function() {
+ if (this.cm.options.readOnly != "nocursor") this.div.focus();
+ },
+ blur: function() { this.div.blur(); },
+ getField: function() { return this.div; },
+
+ supportsTouch: function() { return true; },
+
+ receivedFocus: function() {
+ var input = this;
+ if (this.selectionInEditor())
+ this.pollSelection();
+ else
+ runInOp(this.cm, function() { 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);
+ },
+
+ selectionChanged: function() {
+ var sel = window.getSelection();
+ return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
+ sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset;
+ },
+
+ pollSelection: function() {
+ if (!this.composing && !this.gracePeriod && this.selectionChanged()) {
+ var sel = window.getSelection(), cm = this.cm;
+ 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;
+ });
+ }
+ },
+
+ pollContent: function() {
+ var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
+ var from = sel.from(), to = sel.to();
+ if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false;
+
+ var fromIndex;
+ if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
+ var fromLine = lineNo(display.view[0].line);
+ var fromNode = display.view[0].node;
+ } else {
+ var fromLine = lineNo(display.view[fromIndex].line);
+ var fromNode = display.view[fromIndex - 1].node.nextSibling;
+ }
+ var toIndex = findViewIndex(cm, to.line);
+ if (toIndex == display.view.length - 1) {
+ var toLine = display.viewTo - 1;
+ var toNode = display.lineDiv.lastChild;
+ } else {
+ var toLine = lineNo(display.view[toIndex + 1].line) - 1;
+ var toNode = display.view[toIndex + 1].node.previousSibling;
+ }
+
+ 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;
+
+ newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd);
+ newText[0] = newText[0].slice(cutFront);
+
+ 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;
+ }
+ },
+
+ ensurePolled: function() {
+ this.forceCompositionEnd();
+ },
+ reset: function() {
+ this.forceCompositionEnd();
+ },
+ forceCompositionEnd: function() {
+ if (!this.composing || this.composing.handled) return;
+ this.applyComposition(this.composing);
+ this.composing.handled = true;
+ this.div.blur();
+ this.div.focus();
+ },
+ applyComposition: function(composing) {
+ if (isReadOnly(this.cm))
+ operation(this.cm, regChange)(this.cm)
+ else if (composing.data && composing.data != composing.startData)
+ operation(this.cm, applyTextInput)(this.cm, composing.data, 0, composing.sel);
+ },
+
+ setUneditable: function(node) {
+ node.contentEditable = "false"
+ },
+
+ onKeyPress: function(e) {
+ e.preventDefault();
+ if (!isReadOnly(this.cm))
+ operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0);
+ },
+
+ readOnlyChanged: function(val) {
+ this.div.contentEditable = String(val != "nocursor")
+ },
+
+ onContextMenu: nothing,
+ resetPosition: nothing,
+
+ needsContentAttribute: true
+ }, ContentEditableInput.prototype);
+
+ 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), 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 badPos(pos, bad) { if (bad) pos.bad = true; return pos; }
+
+ 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 = offset; before; before = before.previousSibling) {
+ found = find(before, before.firstChild, -1);
+ if (found)
+ return badPos(Pos(found.line, found.ch + dist), bad);
+ else
+ dist += after.textContent.length;
+ }
+ }
+
+ function domTextBetween(cm, from, to, fromLine, toLine) {
+ var text = "", closing = false, lineSep = cm.doc.lineSeparator();
+ function recognizeMarker(id) { return function(marker) { return marker.id == id; }; }
+ function walk(node) {
+ if (node.nodeType == 1) {
+ var cmText = node.getAttribute("cm-text");
+ if (cmText != null) {
+ if (cmText == "") cmText = node.textContent.replace(/\u200b/g, "");
+ text += 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()))
+ text += getBetween(cm.doc, range.from, range.to).join(lineSep);
+ return;
+ }
+ if (node.getAttribute("contenteditable") == "false") return;
+ for (var i = 0; i < node.childNodes.length; i++)
+ walk(node.childNodes[i]);
+ if (/^(pre|div|p)$/i.test(node.nodeName))
+ closing = true;
+ } else if (node.nodeType == 3) {
+ var val = node.nodeValue;
+ if (!val) return;
+ if (closing) {
+ text += lineSep;
+ closing = false;
+ }
+ text += val;
+ }
+ }
+ for (;;) {
+ walk(from);
+ if (from == to) break;
+ from = from.nextSibling;
+ }
+ return text;
+ }
+
+ CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
+
+ // SELECTION / CURSOR
+
+ // 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).
+ function Selection(ranges, primIndex) {
+ this.ranges = ranges;
+ this.primIndex = primIndex;
+ }
+
+ Selection.prototype = {
+ primary: function() { return this.ranges[this.primIndex]; },
+ 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 (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false;
+ }
+ return true;
+ },
+ deepCopy: function() {
+ for (var out = [], 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);
+ },
+ somethingSelected: function() {
+ for (var i = 0; i < this.ranges.length; i++)
+ if (!this.ranges[i].empty()) return true;
+ return false;
+ },
+ 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;
+ }
+ };
+
+ function Range(anchor, head) {
+ this.anchor = anchor; this.head = head;
+ }
+
+ Range.prototype = {
+ from: function() { return minPos(this.anchor, this.head); },
+ to: function() { return maxPos(this.anchor, this.head); },
+ 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(ranges, primIndex) {
+ 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];
+ if (cmp(prev.to(), cur.from()) >= 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);
+ }
+
+ // 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 isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
+ function clipPosArray(doc, array) {
+ for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]);
+ return out;
+ }
+
+ // SELECTION UPDATES
+
+ // 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(doc, range, head, other) {
+ if (doc.cm && doc.cm.display.shift || doc.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) {
+ setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options);
+ }
+
+ // Extend all selections (pos is an array of selections with length
+ // equal the number of selections)
+ function extendSelections(doc, heads, options) {
+ for (var out = [], i = 0; i < doc.sel.ranges.length; i++)
+ out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null);
+ var newSel = normalizeSelection(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(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) {
+ 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));
+ }
+ };
+ signal(doc, "beforeSelectionChange", doc, obj);
+ if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
+ if (obj.ranges != sel.ranges) return normalizeSelection(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);
+
+ 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 = 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), sel_dontScroll);
+ }
+
+ // 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 newAnchor = skipAtomic(doc, range.anchor, bias, mayClear);
+ var newHead = skipAtomic(doc, range.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(out, sel.primIndex) : sel;
+ }
+
+ // Ensure a given position is not inside an atomic range.
+ function skipAtomic(doc, pos, bias, mayClear) {
+ var flipped = false, curPos = pos;
+ var dir = bias || 1;
+ doc.cantEdit = false;
+ search: for (;;) {
+ var line = getLine(doc, curPos.line);
+ if (line.markedSpans) {
+ for (var i = 0; i < line.markedSpans.length; ++i) {
+ var sp = line.markedSpans[i], m = sp.marker;
+ if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
+ (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
+ if (mayClear) {
+ signal(m, "beforeCursorEnter");
+ if (m.explicitlyCleared) {
+ if (!line.markedSpans) break;
+ else {--i; continue;}
+ }
+ }
+ if (!m.atomic) continue;
+ var newPos = m.find(dir < 0 ? -1 : 1);
+ if (cmp(newPos, curPos) == 0) {
+ newPos.ch += dir;
+ if (newPos.ch < 0) {
+ if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
+ else newPos = null;
+ } else if (newPos.ch > line.text.length) {
+ if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
+ else newPos = null;
+ }
+ if (!newPos) {
+ if (flipped) {
+ // Driven in a corner -- no valid cursor position found at all
+ // -- try again *with* clearing, if we didn't already
+ if (!mayClear) return skipAtomic(doc, pos, bias, true);
+ // Otherwise, turn off editing until further notice, and return the start of the doc
+ doc.cantEdit = true;
+ return Pos(doc.first, 0);
+ }
+ flipped = true; newPos = pos; dir = -dir;
+ }
+ }
+ curPos = newPos;
+ continue search;
+ }
+ }
+ }
+ return curPos;
+ }
+ }
+
+ // SELECTION DRAWING
+
+ function updateSelection(cm) {
+ cm.display.input.showSelection(cm.display.input.prepareSelection());
+ }
+
+ function prepareSelection(cm, primary) {
+ 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 === false && i == doc.sel.primIndex) continue;
+ var range = doc.sel.ranges[i];
+ 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";
+ }
+ }
+
+ // 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;
+
+ 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; top: " + top + "px; width: " + (width == null ? rightSide - left : width) +
+ "px; 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);
+ }
+
+ iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
+ var leftPos = coords(from, "left"), rightPos, left, right;
+ if (from == to) {
+ rightPos = leftPos;
+ left = right = leftPos.left;
+ } else {
+ rightPos = coords(to - 1, "right");
+ if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
+ left = leftPos.left;
+ right = rightPos.right;
+ }
+ if (fromArg == null && from == 0) left = leftSide;
+ if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
+ add(left, leftPos.top, null, leftPos.bottom);
+ left = leftSide;
+ if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
+ }
+ if (toArg == null && to == lineLen) right = rightSide;
+ if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
+ start = leftPos;
+ if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
+ end = rightPos;
+ if (left < leftSide + 1) left = leftSide;
+ add(left, rightPos.top, right - left, rightPos.bottom);
+ });
+ 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() {
+ display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden";
+ }, cm.options.cursorBlinkRate);
+ else if (cm.options.cursorBlinkRate < 0)
+ display.cursorDiv.style.visibility = "hidden";
+ }
+
+ // HIGHLIGHT WORKER
+
+ function startWorker(cm, time) {
+ if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)
+ cm.state.highlight.set(time, bind(highlightWorker, cm));
+ }
+
+ function highlightWorker(cm) {
+ var doc = cm.doc;
+ if (doc.frontier < doc.first) doc.frontier = doc.first;
+ if (doc.frontier >= cm.display.viewTo) return;
+ var end = +new Date + cm.options.workTime;
+ var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
+ var changedLines = [];
+
+ doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) {
+ if (doc.frontier >= cm.display.viewFrom) { // Visible
+ var oldStyles = line.styles, tooLong = line.text.length > cm.options.maxHighlightLength;
+ var highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, state) : state, true);
+ 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(doc.frontier);
+ line.stateAfter = tooLong ? state : copyState(doc.mode, state);
+ } else {
+ if (line.text.length <= cm.options.maxHighlightLength)
+ processLine(cm, line.text, state);
+ line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
+ }
+ ++doc.frontier;
+ if (+new Date > end) {
+ startWorker(cm, cm.options.workDelay);
+ return true;
+ }
+ });
+ if (changedLines.length) runInOp(cm, function() {
+ for (var i = 0; i < changedLines.length; i++)
+ regLineChange(cm, changedLines[i], "text");
+ });
+ }
+
+ // 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);
+ if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
+ var indented = countColumn(line.text, null, cm.options.tabSize);
+ if (minline == null || minindent > indented) {
+ minline = search - 1;
+ minindent = indented;
+ }
+ }
+ return minline;
+ }
+
+ function getStateBefore(cm, n, precise) {
+ var doc = cm.doc, display = cm.display;
+ if (!doc.mode.startState) return true;
+ var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
+ if (!state) state = startState(doc.mode);
+ else state = copyState(doc.mode, state);
+ doc.iter(pos, n, function(line) {
+ processLine(cm, line.text, state);
+ var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo;
+ line.stateAfter = save ? copyState(doc.mode, state) : null;
+ ++pos;
+ });
+ if (precise) doc.frontier = pos;
+ return state;
+ }
+
+ // 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"));
+ 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 = 0; i < lineView.rest.length; i++)
+ if (lineNo(lineView.rest[i]) > lineN)
+ return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], 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;
+ // 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) {
+ var 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 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 = 0; i < 4; i++) { // 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 if (ie && cm.options.lineWrapping) {
+ var rects = range(node, start, end).getClientRects();
+ if (rects.length)
+ rect = rects[bias == "right" ? rects.length - 1 : 0];
+ else
+ rect = nullRect;
+ } else {
+ rect = range(node, start, end).getBoundingClientRect() || nullRect;
+ }
+ 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;
+ for (var i = 0; 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() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
+ function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
+
+ // 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) {
+ if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
+ var size = widgetHeight(lineObj.widgets[i]);
+ rect.top += size; rect.bottom += size;
+ }
+ 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.
+ 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);
+ }
+ function getBidi(ch, partPos) {
+ var part = order[partPos], right = part.level % 2;
+ if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
+ part = order[--partPos];
+ ch = bidiRight(part) - (part.level % 2 ? 0 : 1);
+ right = true;
+ } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
+ part = order[++partPos];
+ ch = bidiLeft(part) - part.level % 2;
+ right = false;
+ }
+ if (right && ch == part.to && ch > part.from) return get(ch - 1);
+ return get(ch, right);
+ }
+ var order = getOrder(lineObj), ch = pos.ch;
+ if (!order) return get(ch);
+ var partPos = getBidiPartAt(order, ch);
+ var val = getBidi(ch, partPos);
+ if (bidiOther != null) val.other = getBidi(ch, bidiOther);
+ 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, outside, xRel) {
+ var pos = Pos(line, ch);
+ pos.xRel = xRel;
+ if (outside) pos.outside = true;
+ 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, true, -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, true, 1);
+ if (x < 0) x = 0;
+
+ var lineObj = getLine(doc, lineN);
+ for (;;) {
+ var found = coordsCharInner(cm, lineObj, lineN, x, y);
+ var merged = collapsedSpanAtEnd(lineObj);
+ var mergedPos = merged && merged.find(0, true);
+ if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
+ lineN = lineNo(lineObj = mergedPos.to.line);
+ else
+ return found;
+ }
+ }
+
+ function coordsCharInner(cm, lineObj, lineNo, x, y) {
+ var innerOff = y - heightAtLine(lineObj);
+ var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
+ var preparedMeasure = prepareMeasureForLine(cm, lineObj);
+
+ function getX(ch) {
+ var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure);
+ wrongLine = true;
+ if (innerOff > sp.bottom) return sp.left - adjust;
+ else if (innerOff < sp.top) return sp.left + adjust;
+ else wrongLine = false;
+ return sp.left;
+ }
+
+ var bidi = getOrder(lineObj), dist = lineObj.text.length;
+ var from = lineLeft(lineObj), to = lineRight(lineObj);
+ var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
+
+ if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
+ // Do a binary search between these bounds.
+ for (;;) {
+ if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
+ var ch = x < fromX || x - fromX <= toX - x ? from : to;
+ var xDiff = x - (ch == from ? fromX : toX);
+ while (isExtendingChar(lineObj.text.charAt(ch))) ++ch;
+ var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
+ xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0);
+ return pos;
+ }
+ var step = Math.ceil(dist / 2), middle = from + step;
+ if (bidi) {
+ middle = from;
+ for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
+ }
+ var middleX = getX(middle);
+ if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
+ else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
+ }
+ }
+
+ var measureText;
+ // Compute the default text height.
+ function textHeight(display) {
+ if (display.cachedTextHeight != null) return display.cachedTextHeight;
+ if (measureText == null) {
+ measureText = elt("pre");
+ // 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]);
+ removeChildrenAndAdd(display.measure, pre);
+ var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
+ if (width > 2) display.cachedCharWidth = width;
+ return width || 10;
+ }
+
+ // OPERATIONS
+
+ // 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 operationGroup = null;
+
+ 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: null, // 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
+ };
+ if (operationGroup) {
+ operationGroup.ops.push(cm.curOp);
+ } else {
+ cm.curOp.ownsGroup = operationGroup = {
+ ops: [cm.curOp],
+ 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);
+ }
+
+ // Finish an operation, updating the display and signalling delayed events
+ function endOperation(cm) {
+ var op = cm.curOp, group = op.ownsGroup;
+ if (!group) return;
+
+ try { fireCallbacksForOps(group); }
+ finally {
+ operationGroup = null;
+ 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 = 0; i < ops.length; i++) // Write DOM (maybe)
+ endOperation_W1(ops[i]);
+ for (var i = 0; i < ops.length; i++) // Read DOM
+ endOperation_R2(ops[i]);
+ for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
+ endOperation_W2(ops[i]);
+ for (var i = 0; i < ops.length; i++) // Read DOM
+ endOperation_finish(ops[i]);
+ }
+
+ 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;
+ }
+
+ if (op.preparedSelection)
+ cm.display.input.showSelection(op.preparedSelection);
+ if (op.updatedDisplay)
+ setDocumentHeight(cm, op.barMeasure);
+ if (op.updatedDisplay || op.startHeight != cm.doc.height)
+ updateScrollbars(cm, op.barMeasure);
+
+ if (op.selectionChanged) restartBlink(cm);
+
+ if (cm.state.focused && op.updateInput)
+ cm.display.input.reset(op.typing);
+ if (op.focus && op.focus == activeElt()) 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 && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) {
+ doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop));
+ display.scrollbars.setScrollTop(doc.scrollTop);
+ display.scroller.scrollTop = doc.scrollTop;
+ }
+ if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {
+ doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - displayWidth(cm), op.scrollLeft));
+ display.scrollbars.setScrollLeft(doc.scrollLeft);
+ display.scroller.scrollLeft = doc.scrollLeft;
+ alignHorizontally(cm);
+ }
+ // If we need to scroll a specific position into view, do so.
+ if (op.scrollToPos) {
+ var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
+ clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
+ if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords);
+ }
+
+ // 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 = 0; i < unhidden.length; ++i)
+ if (unhidden[i].lines.length) signal(unhidden[i], "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); }
+ };
+ }
+
+ // VIEW TRACKING
+
+ // 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;
+ }
+
+ // 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 = viewCuttingPoint(cm, from, from, -1);
+ if (cut) {
+ display.view = display.view.slice(0, cut.index);
+ display.viewTo = cut.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;
+ }
+
+ // 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;
+ }
+ }
+
+ 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};
+ for (var i = 0, n = cm.display.viewFrom; 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;
+ }
+
+ // EVENT HANDLERS
+
+ // 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) { 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.
+ if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {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() {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 (!isMouseLikeTouchEvent(e)) {
+ 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) {
+ setScrollTop(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){onScrollWheel(cm, e);});
+ on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
+
+ // Prevent wrapper from ever scrolling
+ on(d.wrapper, "scroll", function() { 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){onDragStart(cm, e);},
+ drop: operation(cm, onDrop),
+ leave: function() {clearDragCursor(cm);}
+ };
+
+ var inp = d.input.getField();
+ on(inp, "keyup", function(e) { onKeyUp.call(cm, e); });
+ on(inp, "keydown", operation(cm, onKeyDown));
+ on(inp, "keypress", operation(cm, onKeyPress));
+ on(inp, "focus", bind(onFocus, cm));
+ on(inp, "blur", bind(onBlur, cm));
+ }
+
+ function dragDropChanged(cm, value, old) {
+ var wasOn = old && old != CodeMirror.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);
+ }
+ }
+
+ // Called when the window resizes
+ function onResize(cm) {
+ var d = cm.display;
+ if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
+ return;
+ // Might be a text scaling operation, clear size caches.
+ d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
+ d.scrollbarsClipped = false;
+ cm.setSize();
+ }
+
+ // MOUSE EVENTS
+
+ // 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;
+ }
+ }
+
+ // 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) { return null; }
+ var coords = coordsChar(cm, x, y), line;
+ if (forRect && coords.xRel == 1 && (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;
+ }
+
+ // 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 (display.activeTouch && display.input.supportsTouch() || signalDOMEvent(cm, e)) return;
+ 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(){display.scroller.draggable = true;}, 100);
+ }
+ return;
+ }
+ if (clickInGutter(cm, e)) return;
+ var start = posFromMouse(cm, e);
+ window.focus();
+
+ switch (e_button(e)) {
+ case 1:
+ // #3261: make sure, that we're not starting a second selection
+ if (cm.state.selectingText)
+ cm.state.selectingText(e);
+ else if (start)
+ leftButtonDown(cm, e, start);
+ else if (e_target(e) == display.scroller)
+ e_preventDefault(e);
+ break;
+ case 2:
+ if (webkit) cm.state.lastMiddleDown = +new Date;
+ if (start) extendSelection(cm.doc, start);
+ setTimeout(function() {display.input.focus();}, 20);
+ e_preventDefault(e);
+ break;
+ case 3:
+ if (captureRightClick) onContextMenu(cm, e);
+ else delayBlurEvent(cm);
+ break;
+ }
+ }
+
+ var lastClick, lastDoubleClick;
+ function leftButtonDown(cm, e, start) {
+ if (ie) setTimeout(bind(ensureFocus, cm), 0);
+ else cm.curOp.focus = activeElt();
+
+ var now = +new Date, type;
+ if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) {
+ type = "triple";
+ } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) {
+ type = "double";
+ lastDoubleClick = {time: now, pos: start};
+ } else {
+ type = "single";
+ lastClick = {time: now, pos: start};
+ }
+
+ var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained;
+ if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) &&
+ type == "single" && (contained = sel.contains(start)) > -1 &&
+ (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRel > 0) &&
+ (cmp(contained.to(), start) > 0 || start.xRel < 0))
+ leftButtonStartDrag(cm, e, start, modifier);
+ else
+ leftButtonSelect(cm, e, start, type, modifier);
+ }
+
+ // 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, e, start, modifier) {
+ var display = cm.display, startTime = +new Date;
+ var dragEnd = operation(cm, function(e2) {
+ if (webkit) display.scroller.draggable = false;
+ cm.state.draggingText = false;
+ off(document, "mouseup", dragEnd);
+ off(display.scroller, "drop", dragEnd);
+ if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
+ e_preventDefault(e2);
+ if (!modifier && +new Date - 200 < startTime)
+ extendSelection(cm.doc, start);
+ // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
+ if (webkit || ie && ie_version == 9)
+ setTimeout(function() {document.body.focus(); display.input.focus();}, 20);
+ else
+ display.input.focus();
+ }
+ });
+ // Let the drag handler handle this.
+ if (webkit) display.scroller.draggable = true;
+ cm.state.draggingText = dragEnd;
+ // IE's approach to draggable
+ if (display.scroller.dragDrop) display.scroller.dragDrop();
+ on(document, "mouseup", dragEnd);
+ on(display.scroller, "drop", dragEnd);
+ }
+
+ // Normal selection, as opposed to text dragging.
+ function leftButtonSelect(cm, e, start, type, addNew) {
+ var display = cm.display, doc = cm.doc;
+ e_preventDefault(e);
+
+ var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
+ if (addNew && !e.shiftKey) {
+ 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 (e.altKey) {
+ type = "rect";
+ if (!addNew) ourRange = new Range(start, start);
+ start = posFromMouse(cm, e, true, true);
+ ourIndex = -1;
+ } else if (type == "double") {
+ var word = cm.findWordAt(start);
+ if (cm.display.shift || doc.extend)
+ ourRange = extendRange(doc, ourRange, word.anchor, word.head);
+ else
+ ourRange = word;
+ } else if (type == "triple") {
+ var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)));
+ if (cm.display.shift || doc.extend)
+ ourRange = extendRange(doc, ourRange, line.anchor, line.head);
+ else
+ ourRange = line;
+ } else {
+ ourRange = extendRange(doc, ourRange, start);
+ }
+
+ if (!addNew) {
+ ourIndex = 0;
+ setSelection(doc, new Selection([ourRange], 0), sel_mouse);
+ startSel = doc.sel;
+ } else if (ourIndex == -1) {
+ ourIndex = ranges.length;
+ setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
+ {scroll: false, origin: "*mouse"});
+ } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) {
+ setSelection(doc, normalizeSelection(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 (type == "rect") {
+ 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(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
+ {origin: "*mouse", scroll: false});
+ cm.scrollIntoView(pos);
+ } else {
+ var oldRange = ourRange;
+ var anchor = oldRange.anchor, head = pos;
+ if (type != "single") {
+ if (type == "double")
+ var range = cm.findWordAt(pos);
+ else
+ var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0)));
+ 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 = startSel.ranges.slice(0);
+ ranges[ourIndex] = new Range(clipPos(doc, anchor), head);
+ setSelection(doc, normalizeSelection(ranges, 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, type == "rect");
+ 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;
+ e_preventDefault(e);
+ display.input.focus();
+ off(document, "mousemove", move);
+ off(document, "mouseup", up);
+ doc.history.lastSelOrigin = null;
+ }
+
+ var move = operation(cm, function(e) {
+ if (!e_button(e)) done(e);
+ else extend(e);
+ });
+ var up = operation(cm, done);
+ cm.state.selectingText = up;
+ on(document, "mousemove", move);
+ on(document, "mouseup", up);
+ }
+
+ // Determines whether an event happened in the gutter, and fires the
+ // handlers for the corresponding event.
+ function gutterEvent(cm, e, type, prevent, signalfn) {
+ try { var mX = e.clientX, mY = e.clientY; }
+ catch(e) { 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.options.gutters.length; ++i) {
+ var g = display.gutters.childNodes[i];
+ if (g && g.getBoundingClientRect().right >= mX) {
+ var line = lineAtHeight(cm.doc, mY);
+ var gutter = cm.options.gutters[i];
+ signalfn(cm, type, cm, line, gutter, e);
+ return e_defaultPrevented(e);
+ }
+ }
+ }
+
+ function clickInGutter(cm, e) {
+ return gutterEvent(cm, e, "gutterClick", true, signalLater);
+ }
+
+ // 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 || isReadOnly(cm)) 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 loadFile = function(file, i) {
+ if (cm.options.allowDropFileTypes &&
+ indexOf(cm.options.allowDropFileTypes, file.type) == -1)
+ return;
+
+ var reader = new FileReader;
+ reader.onload = operation(cm, function() {
+ var content = reader.result;
+ if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) content = "";
+ text[i] = content;
+ if (++read == n) {
+ pos = clipPos(cm.doc, pos);
+ var change = {from: pos, to: pos,
+ text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
+ origin: "paste"};
+ makeChange(cm.doc, change);
+ setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
+ }
+ });
+ reader.readAsText(file);
+ };
+ for (var i = 0; i < n; ++i) loadFile(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() {cm.display.input.focus();}, 20);
+ return;
+ }
+ try {
+ var text = e.dataTransfer.getData("Text");
+ if (text) {
+ if (cm.state.draggingText && !(mac ? e.altKey : e.ctrlKey))
+ var selected = cm.listSelections();
+ setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
+ if (selected) for (var i = 0; i < selected.length; ++i)
+ replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag");
+ cm.replaceSelection(text, "around", "paste");
+ cm.display.input.focus();
+ }
+ }
+ catch(e){}
+ }
+ }
+
+ 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());
+
+ // 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;
+ }
+ }
+
+ // SCROLL EVENTS
+
+ // Sync the scrollable area and scrollbars, ensure the viewport
+ // covers the visible area.
+ function setScrollTop(cm, val) {
+ if (Math.abs(cm.doc.scrollTop - val) < 2) return;
+ cm.doc.scrollTop = val;
+ if (!gecko) updateDisplaySimple(cm, {top: val});
+ if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
+ cm.display.scrollbars.setScrollTop(val);
+ if (gecko) updateDisplaySimple(cm);
+ startWorker(cm, 100);
+ }
+ // Sync scroller and scrollbar, ensure the gutter elements are
+ // aligned.
+ function setScrollLeft(cm, val, isScroller) {
+ if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
+ val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
+ cm.doc.scrollLeft = val;
+ alignHorizontally(cm);
+ if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
+ cm.display.scrollbars.setScrollLeft(val);
+ }
+
+ // 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;
+
+ var wheelEventDelta = function(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};
+ };
+ CodeMirror.wheelEventPixels = function(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)
+ setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
+ setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
+ // 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;
+ }
+ }
+ }
+
+ // KEY EVENTS
+
+ // 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 (isReadOnly(cm)) 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);
+ }
+
+ var stopSeq = new Delayed;
+ function dispatchKey(cm, name, e, handle) {
+ var seq = cm.state.keySeq;
+ if (seq) {
+ if (isModifierKey(name)) return "handled";
+ stopSeq.set(50, function() {
+ if (cm.state.keySeq == seq) {
+ cm.state.keySeq = null;
+ cm.display.input.reset();
+ }
+ });
+ name = seq + " " + name;
+ }
+ 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);
+ }
+
+ if (seq && !result && /\'$/.test(name)) {
+ e_preventDefault(e);
+ return true;
+ }
+ 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;
+ 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");
+ }
+
+ // 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 (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);
+ if (handleCharBinding(cm, e, ch)) return;
+ cm.display.input.onKeyPress(e);
+ }
+
+ // FOCUS/BLUR EVENTS
+
+ function delayBlurEvent(cm) {
+ cm.state.delayingBlurEvent = true;
+ setTimeout(function() {
+ if (cm.state.delayingBlurEvent) {
+ cm.state.delayingBlurEvent = false;
+ onBlur(cm);
+ }
+ }, 100);
+ }
+
+ function onFocus(cm) {
+ if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false;
+
+ if (cm.options.readOnly == "nocursor") return;
+ if (!cm.state.focused) {
+ signal(cm, "focus", cm);
+ 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() { cm.display.input.reset(true); }, 20); // Issue #1730
+ }
+ cm.display.input.receivedFocus();
+ }
+ restartBlink(cm);
+ }
+ function onBlur(cm) {
+ if (cm.state.delayingBlurEvent) return;
+
+ if (cm.state.focused) {
+ signal(cm, "blur", cm);
+ 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);
+ }
+
+ // 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;
+ cm.display.input.onContextMenu(e);
+ }
+
+ function contextMenuInGutter(cm, e) {
+ if (!hasHandler(cm, "gutterContextMenu")) return false;
+ return gutterEvent(cm, e, "gutterContextMenu", false, signal);
+ }
+
+ // UPDATING
+
+ // Compute the position of the end of a change (its 'to' property
+ // refers to the pre-change end).
+ var changeEnd = CodeMirror.changeEnd = function(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(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);
+ }
+
+ // 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() { this.canceled = true; }
+ };
+ if (update) obj.update = function(from, to, text, origin) {
+ if (from) this.from = clipPos(doc, from);
+ if (to) this.to = clipPos(doc, to);
+ if (text) this.text = text;
+ if (origin !== undefined) this.origin = origin;
+ };
+ signal(doc, "beforeChange", doc, obj);
+ if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
+
+ if (obj.canceled) 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});
+ } 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) {
+ if (doc.cm && doc.cm.state.suppressEdits) 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)
+ for (var i = 0; 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 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");
+
+ for (var i = event.changes.length - 1; i >= 0; --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));
+ });
+ }
+ }
+
+ // 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);
+ }
+
+ // 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;
+ }
+
+ // Adjust frontier, schedule worker
+ doc.frontier = Math.min(doc.frontier, 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) {
+ if (!to) to = from;
+ if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; }
+ if (typeof code == "string") code = doc.splitLines(code);
+ makeChange(doc, {from: from, to: to, text: code, origin: origin});
+ }
+
+ // 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, coords) {
+ if (signalDOMEvent(cm, "scrollCursorIntoView")) return;
+
+ var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
+ if (coords.top + box.top < 0) doScroll = true;
+ else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
+ if (doScroll != null && !phantom) {
+ var scrollNode = elt("div", "\u200b", null, "position: absolute; top: " +
+ (coords.top - display.viewOffset - paddingTop(cm.display)) + "px; height: " +
+ (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + "px; left: " +
+ coords.left + "px; width: 2px;");
+ 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;
+ for (var limit = 0; limit < 5; limit++) {
+ var changed = false, coords = cursorCoords(cm, pos);
+ var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
+ var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),
+ Math.min(coords.top, endCoords.top) - margin,
+ Math.max(coords.left, endCoords.left),
+ Math.max(coords.bottom, endCoords.bottom) + margin);
+ var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
+ if (scrollPos.scrollTop != null) {
+ setScrollTop(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 coords;
+ }
+
+ // Scroll a given set of coordinates into view (immediately).
+ function scrollIntoView(cm, x1, y1, x2, y2) {
+ var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
+ if (scrollPos.scrollTop != null) setScrollTop(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, x1, y1, x2, y2) {
+ var display = cm.display, snapMargin = textHeight(cm.display);
+ if (y1 < 0) y1 = 0;
+ var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
+ var screen = displayHeight(cm), result = {};
+ if (y2 - y1 > screen) y2 = y1 + screen;
+ var docBottom = cm.doc.height + paddingVert(display);
+ var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
+ if (y1 < screentop) {
+ result.scrollTop = atTop ? 0 : y1;
+ } else if (y2 > screentop + screen) {
+ var newTop = Math.min(y1, (atBottom ? docBottom : y2) - 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 = x2 - x1 > screenw;
+ if (tooWide) x2 = x1 + screenw;
+ if (x1 < 10)
+ result.scrollLeft = 0;
+ else if (x1 < screenleft)
+ result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10));
+ else if (x2 > screenw + screenleft - 3)
+ result.scrollLeft = x2 + (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 addToScrollPos(cm, left, top) {
+ if (left != null || top != null) resolveScrollToPos(cm);
+ if (left != null)
+ cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left;
+ if (top != null)
+ 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(), from = cur, to = cur;
+ if (!cm.options.lineWrapping) {
+ from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur;
+ to = Pos(cur.line, cur.ch + 1);
+ }
+ cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true};
+ }
+
+ // 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);
+ var sPos = calculateScrollPos(cm, Math.min(from.left, to.left),
+ Math.min(from.top, to.top) - range.margin,
+ Math.max(from.right, to.right),
+ Math.max(from.bottom, to.bottom) + range.margin);
+ cm.scrollTo(sPos.scrollLeft, sPos.scrollTop);
+ }
+ }
+
+ // API UTILITIES
+
+ // 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 = getStateBefore(cm, n);
+ }
+
+ 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 = 0; i < doc.sel.ranges.length; i++) {
+ var range = doc.sel.ranges[i];
+ if (range.head.line == n && range.head.ch < curSpaceString.length) {
+ var pos = Pos(n, curSpaceString.length);
+ replaceOneSelection(doc, i, new Range(pos, pos));
+ break;
+ }
+ }
+ }
+ }
+
+ // 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;
+ }
+
+ // 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);
+ });
+ }
+
+ // 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 line = pos.line, ch = pos.ch, origDir = dir;
+ var lineObj = getLine(doc, line);
+ var possible = true;
+ function findNextLine() {
+ var l = line + dir;
+ if (l < doc.first || l >= doc.first + doc.size) return (possible = false);
+ line = l;
+ return lineObj = getLine(doc, l);
+ }
+ function moveOnce(boundToLine) {
+ var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
+ if (next == null) {
+ if (!boundToLine && findNextLine()) {
+ if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
+ else ch = dir < 0 ? lineObj.text.length : 0;
+ } else return (possible = false);
+ } else ch = 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(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();}
+ break;
+ }
+
+ if (type) sawType = type;
+ if (dir > 0 && !moveOnce(!first)) break;
+ }
+ }
+ var result = skipAtomic(doc, Pos(line, ch), origDir, true);
+ if (!possible) 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);
+ y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
+ } else if (unit == "line") {
+ y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
+ }
+ for (;;) {
+ var 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;
+ }
+
+ // EDITOR METHODS
+
+ // 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.
+
+ 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);
+ },
+
+ 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.");
+ this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
+ 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("cm-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 = 0; i < help._global.length; i++) {
+ var cur = help._global[i];
+ 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 getStateBefore(this, line + 1, precise);
+ },
+
+ 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) {
+ 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").top +
+ (end ? this.doc.height - heightAtLine(lineObj) : 0);
+ },
+
+ defaultTextHeight: function() { return textHeight(this.display); },
+ defaultCharWidth: function() { return charWidth(this.display); },
+
+ setGutterMarker: methodOp(function(line, gutterID, value) {
+ return changeLine(this.doc, line, "gutter", function(line) {
+ var markers = line.gutterMarkers || (line.gutterMarkers = {});
+ markers[gutterID] = value;
+ if (!value && isEmpty(markers)) line.gutterMarkers = null;
+ return true;
+ });
+ }),
+
+ clearGutter: methodOp(function(gutterID) {
+ var cm = this, doc = cm.doc, i = doc.first;
+ doc.iter(function(line) {
+ if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
+ line.gutterMarkers[gutterID] = null;
+ regLineChange(cm, i, "gutter");
+ if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
+ }
+ ++i;
+ });
+ }),
+
+ lineInfo: function(line) {
+ if (typeof line == "number") {
+ if (!isLine(this.doc, line)) return null;
+ var n = line;
+ line = getLine(this.doc, line);
+ if (!line) return null;
+ } else {
+ var 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};
+ },
+
+ 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, top, left + node.offsetWidth, top + node.offsetHeight);
+ },
+
+ triggerOnKeyDown: methodOp(onKeyDown),
+ triggerOnKeyPress: methodOp(onKeyPress),
+ triggerOnKeyUp: onKeyUp,
+
+ 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; }
+ for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
+ cur = findPosH(this.doc, cur, dir, unit, visually);
+ if (cur.hitSide) break;
+ }
+ return cur;
+ },
+
+ moveH: methodOp(function(dir, unit) {
+ var cm = this;
+ cm.extendSelectionsBy(function(range) {
+ if (cm.display.shift || cm.doc.extend || range.empty())
+ return findPosH(cm.doc, range.head, dir, unit, cm.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; }
+ for (var i = 0, cur = clipPos(this.doc, from); 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 cm = this, doc = this.doc, goals = [];
+ var collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected();
+ doc.extendSelectionsBy(function(range) {
+ if (collapse)
+ return dir < 0 ? range.from() : range.to();
+ var headPos = cursorCoords(cm, range.head, "div");
+ if (range.goalColumn != null) headPos.left = range.goalColumn;
+ goals.push(headPos.left);
+ var pos = findPosV(cm, headPos, dir, unit);
+ if (unit == "page" && range == doc.sel.primary())
+ addToScrollPos(cm, null, charCoords(cm, 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.xRel < 0 || 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(); },
+
+ scrollTo: methodOp(function(x, y) {
+ if (x != null || y != null) resolveScrollToPos(this);
+ if (x != null) this.curOp.scrollLeft = x;
+ if (y != null) this.curOp.scrollTop = 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) {
+ resolveScrollToPos(this);
+ this.curOp.scrollToPos = range;
+ } else {
+ var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left),
+ Math.min(range.from.top, range.to.top) - range.margin,
+ Math.max(range.from.right, range.to.right),
+ Math.max(range.from.bottom, range.to.bottom) + range.margin);
+ this.scrollTo(sPos.scrollLeft, sPos.scrollTop);
+ }
+ }),
+
+ setSize: methodOp(function(width, height) {
+ var cm = this;
+ function interpret(val) {
+ return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
+ }
+ if (width != null) cm.display.wrapper.style.width = interpret(width);
+ if (height != null) cm.display.wrapper.style.height = interpret(height);
+ if (cm.options.lineWrapping) clearLineMeasurementCache(this);
+ var lineNo = cm.display.viewFrom;
+ cm.doc.iter(lineNo, cm.display.viewTo, function(line) {
+ if (line.widgets) for (var i = 0; i < line.widgets.length; i++)
+ if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break; }
+ ++lineNo;
+ });
+ cm.curOp.forceUpdate = true;
+ signal(cm, "refresh", this);
+ }),
+
+ operation: function(f){return runInOp(this, f);},
+
+ refresh: methodOp(function() {
+ var oldHeight = this.display.cachedTextHeight;
+ regChange(this);
+ this.curOp.forceUpdate = true;
+ clearCaches(this);
+ this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop);
+ updateGutterSpace(this);
+ if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
+ estimateLineHeights(this);
+ signal(this, "refresh", this);
+ }),
+
+ swapDoc: methodOp(function(doc) {
+ var old = this.doc;
+ old.cm = null;
+ attachDoc(this, doc);
+ clearCaches(this);
+ this.display.input.reset();
+ this.scrollTo(doc.scrollLeft, doc.scrollTop);
+ this.curOp.forceScroll = true;
+ signalLater(this, "swapDoc", this, old);
+ return old;
+ }),
+
+ 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);
+
+ // OPTION DEFAULTS
+
+ // The default configuration options.
+ var defaults = CodeMirror.defaults = {};
+ // Functions to run when options are changed.
+ 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;
+ }
+
+ // Passed to option handlers when there is no old value.
+ var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.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) {
+ 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", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val, old) {
+ cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
+ if (old != CodeMirror.Init) cm.refresh();
+ });
+ option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {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("rtlMoveVisually", !windows);
+ option("wholeLineUpdateBefore", true);
+
+ option("theme", "default", function(cm) {
+ themeChanged(cm);
+ guttersChanged(cm);
+ }, true);
+ option("keyMap", "default", function(cm, val, old) {
+ var next = getKeyMap(val);
+ var prev = old != CodeMirror.Init && getKeyMap(old);
+ if (prev && prev.detach) prev.detach(cm, next);
+ if (next.attach) next.attach(cm, prev || null);
+ });
+ option("extraKeys", null);
+
+ option("lineWrapping", false, wrappingChanged, true);
+ option("gutters", [], function(cm) {
+ setGuttersForLineNumbers(cm.options);
+ guttersChanged(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) {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) {
+ setGuttersForLineNumbers(cm.options);
+ guttersChanged(cm);
+ }, true);
+ option("firstLineNumber", 1, guttersChanged, true);
+ option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
+ option("showCursorWhenSelecting", false, updateSelection, true);
+
+ option("resetSelectionOnContextMenu", true);
+ option("lineWiseCopyCut", true);
+
+ option("readOnly", false, function(cm, val) {
+ if (val == "nocursor") {
+ onBlur(cm);
+ cm.display.input.blur();
+ cm.display.disabled = true;
+ } else {
+ cm.display.disabled = false;
+ }
+ cm.display.input.readOnlyChanged(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){cm.doc.history.undoDepth = val;});
+ option("historyEventDelay", 1250);
+ option("viewportMargin", 10, function(cm){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) {
+ cm.display.input.getField().tabIndex = val || "";
+ });
+ option("autofocus", null);
+
+ // MODE DEFINITION AND QUERYING
+
+ // Known modes, by name and by MIME
+ var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.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.)
+ CodeMirror.defineMode = function(name, mode) {
+ if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
+ if (arguments.length > 2)
+ mode.dependencies = Array.prototype.slice.call(arguments, 2);
+ modes[name] = mode;
+ };
+
+ CodeMirror.defineMIME = function(mime, spec) {
+ mimeModes[mime] = spec;
+ };
+
+ // Given a MIME type, a {name, ...options} config object, or a name
+ // string, return a mode config object.
+ CodeMirror.resolveMode = function(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 CodeMirror.resolveMode("application/xml");
+ }
+ 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.
+ CodeMirror.getMode = function(options, spec) {
+ var spec = CodeMirror.resolveMode(spec);
+ var mfactory = modes[spec.name];
+ if (!mfactory) return CodeMirror.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 in spec.modeProps)
+ modeObj[prop] = spec.modeProps[prop];
+
+ return modeObj;
+ };
+
+ // Minimal default mode.
+ CodeMirror.defineMode("null", function() {
+ return {token: function(stream) {stream.skipToEnd();}};
+ });
+ CodeMirror.defineMIME("text/plain", "null");
+
+ // This can be used to attach properties to mode objects from
+ // outside the actual mode definition.
+ var modeExtensions = CodeMirror.modeExtensions = {};
+ CodeMirror.extendMode = function(mode, properties) {
+ var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
+ copyObj(properties, exts);
+ };
+
+ // EXTENSIONS
+
+ CodeMirror.defineExtension = function(name, func) {
+ CodeMirror.prototype[name] = func;
+ };
+ CodeMirror.defineDocExtension = function(name, func) {
+ Doc.prototype[name] = func;
+ };
+ CodeMirror.defineOption = option;
+
+ var initHooks = [];
+ CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
+
+ var helpers = CodeMirror.helpers = {};
+ 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});
+ };
+
+ // MODE STATE HANDLING
+
+ // Utility functions for working with state. Exported because nested
+ // modes need to do this for their inner modes.
+
+ var copyState = CodeMirror.copyState = function(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;
+ };
+
+ var startState = CodeMirror.startState = function(mode, a1, a2) {
+ return mode.startState ? mode.startState(a1, a2) : true;
+ };
+
+ // Given a mode and a state (for that mode), find the inner mode and
+ // state at the position that the state refers to.
+ CodeMirror.innerMode = function(mode, state) {
+ while (mode.innerMode) {
+ var info = mode.innerMode(state);
+ if (!info || info.mode == mode) break;
+ state = info.state;
+ mode = info.mode;
+ }
+ return info || {mode: mode, state: state};
+ };
+
+ // STANDARD COMMANDS
+
+ // Commands are parameter-less actions that can be performed on an
+ // editor, mostly used for keybindings.
+ var commands = CodeMirror.commands = {
+ selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);},
+ singleSelection: function(cm) {
+ cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll);
+ },
+ killLine: function(cm) {
+ 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) {
+ deleteNearSelection(cm, function(range) {
+ return {from: Pos(range.from().line, 0),
+ to: clipPos(cm.doc, Pos(range.to().line + 1, 0))};
+ });
+ },
+ delLineLeft: function(cm) {
+ deleteNearSelection(cm, function(range) {
+ return {from: Pos(range.from().line, 0), to: range.from()};
+ });
+ },
+ delWrappedLineLeft: function(cm) {
+ 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) {
+ 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) {cm.undo();},
+ redo: function(cm) {cm.redo();},
+ undoSelection: function(cm) {cm.undoSelection();},
+ redoSelection: function(cm) {cm.redoSelection();},
+ goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
+ goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
+ goLineStart: function(cm) {
+ cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); },
+ {origin: "+move", bias: 1});
+ },
+ goLineStartSmart: function(cm) {
+ cm.extendSelectionsBy(function(range) {
+ return lineStartSmart(cm, range.head);
+ }, {origin: "+move", bias: 1});
+ },
+ goLineEnd: function(cm) {
+ cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); },
+ {origin: "+move", bias: -1});
+ },
+ goLineRight: function(cm) {
+ cm.extendSelectionsBy(function(range) {
+ var top = cm.charCoords(range.head, "div").top + 5;
+ return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
+ }, sel_move);
+ },
+ goLineLeft: function(cm) {
+ cm.extendSelectionsBy(function(range) {
+ var top = cm.charCoords(range.head, "div").top + 5;
+ return cm.coordsChar({left: 0, top: top}, "div");
+ }, sel_move);
+ },
+ goLineLeftSmart: function(cm) {
+ cm.extendSelectionsBy(function(range) {
+ var top = cm.charCoords(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) {cm.moveV(-1, "line");},
+ goLineDown: function(cm) {cm.moveV(1, "line");},
+ goPageUp: function(cm) {cm.moveV(-1, "page");},
+ goPageDown: function(cm) {cm.moveV(1, "page");},
+ goCharLeft: function(cm) {cm.moveH(-1, "char");},
+ goCharRight: function(cm) {cm.moveH(1, "char");},
+ goColumnLeft: function(cm) {cm.moveH(-1, "column");},
+ goColumnRight: function(cm) {cm.moveH(1, "column");},
+ goWordLeft: function(cm) {cm.moveH(-1, "word");},
+ goGroupRight: function(cm) {cm.moveH(1, "group");},
+ goGroupLeft: function(cm) {cm.moveH(-1, "group");},
+ goWordRight: function(cm) {cm.moveH(1, "word");},
+ delCharBefore: function(cm) {cm.deleteH(-1, "char");},
+ delCharAfter: function(cm) {cm.deleteH(1, "char");},
+ delWordBefore: function(cm) {cm.deleteH(-1, "word");},
+ delWordAfter: function(cm) {cm.deleteH(1, "word");},
+ delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
+ delGroupAfter: function(cm) {cm.deleteH(1, "group");},
+ indentAuto: function(cm) {cm.indentSelection("smart");},
+ indentMore: function(cm) {cm.indentSelection("add");},
+ indentLess: function(cm) {cm.indentSelection("subtract");},
+ insertTab: function(cm) {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(new Array(tabSize - col % tabSize + 1).join(" "));
+ }
+ cm.replaceSelections(spaces);
+ },
+ defaultTab: function(cm) {
+ if (cm.somethingSelected()) cm.indentSelection("add");
+ else cm.execCommand("insertTab");
+ },
+ transposeChars: function(cm) {
+ runInOp(cm, function() {
+ var ranges = cm.listSelections(), newSel = [];
+ for (var i = 0; i < ranges.length; i++) {
+ 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)
+ cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
+ prev.charAt(prev.length - 1),
+ Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose");
+ }
+ }
+ newSel.push(new Range(cur, cur));
+ }
+ cm.setSelections(newSel);
+ });
+ },
+ newlineAndIndent: function(cm) {
+ runInOp(cm, function() {
+ var len = cm.listSelections().length;
+ for (var i = 0; i < len; i++) {
+ var range = cm.listSelections()[i];
+ cm.replaceRange(cm.doc.lineSeparator(), range.anchor, range.head, "+input");
+ cm.indentLine(range.from().line + 1, null, true);
+ }
+ ensureCursorVisible(cm);
+ });
+ },
+ toggleOverwrite: function(cm) {cm.toggleOverwrite();}
+ };
+
+
+ // STANDARD KEYMAPS
+
+ var keyMap = CodeMirror.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"
+ };
+ 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.
+ CodeMirror.normalizeKeyMap = function(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, name;
+ 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;
+ };
+
+ var lookupKey = CodeMirror.lookupKey = function(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.
+ var isModifierKey = CodeMirror.isModifierKey = function(value) {
+ var name = typeof value == "string" ? value : keyNames[value.keyCode];
+ return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
+ };
+
+ // Look up the name of a key as indicated by an event object.
+ var keyName = CodeMirror.keyName = function(event, noShift) {
+ if (presto && event.keyCode == 34 && event["char"]) return false;
+ var base = keyNames[event.keyCode], name = base;
+ if (name == null || event.altGraphKey) return false;
+ 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;
+ };
+
+ function getKeyMap(val) {
+ return typeof val == "string" ? keyMap[val] : val;
+ }
+
+ // FROMTEXTAREA
+
+ CodeMirror.fromTextArea = function(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();}
+ 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 (typeof textarea.form.submit == "function")
+ textarea.form.submit = realSubmit;
+ }
+ };
+ };
+
+ textarea.style.display = "none";
+ var cm = CodeMirror(function(node) {
+ textarea.parentNode.insertBefore(node, textarea.nextSibling);
+ }, options);
+ return cm;
+ };
+
+ // STRING STREAM
+
+ // Fed to the mode parsers, provides helper functions to make
+ // parsers more succinct.
+
+ var StringStream = CodeMirror.StringStream = function(string, tabSize) {
+ this.pos = this.start = 0;
+ this.string = string;
+ this.tabSize = tabSize || 8;
+ this.lastColumnPos = this.lastColumnValue = 0;
+ this.lineStart = 0;
+ };
+
+ StringStream.prototype = {
+ eol: function() {return this.pos >= this.string.length;},
+ sol: function() {return this.pos == this.lineStart;},
+ peek: function() {return this.string.charAt(this.pos) || undefined;},
+ next: function() {
+ if (this.pos < this.string.length)
+ return this.string.charAt(this.pos++);
+ },
+ eat: function(match) {
+ var ch = this.string.charAt(this.pos);
+ if (typeof match == "string") var ok = ch == match;
+ else var ok = ch && (match.test ? match.test(ch) : match(ch));
+ if (ok) {++this.pos; return ch;}
+ },
+ eatWhile: function(match) {
+ var start = this.pos;
+ while (this.eat(match)){}
+ return this.pos > start;
+ },
+ eatSpace: function() {
+ var start = this.pos;
+ while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
+ return this.pos > start;
+ },
+ skipToEnd: function() {this.pos = this.string.length;},
+ skipTo: function(ch) {
+ var found = this.string.indexOf(ch, this.pos);
+ if (found > -1) {this.pos = found; return true;}
+ },
+ backUp: function(n) {this.pos -= n;},
+ 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);
+ },
+ indentation: function() {
+ return countColumn(this.string, null, this.tabSize) -
+ (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
+ },
+ 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;
+ }
+ },
+ current: function(){return this.string.slice(this.start, this.pos);},
+ hideFirstChars: function(n, inner) {
+ this.lineStart += n;
+ try { return inner(); }
+ finally { this.lineStart -= n; }
+ }
+ };
+
+ // 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.
+
+ var nextMarkerId = 0;
+
+ var TextMarker = CodeMirror.TextMarker = function(doc, type) {
+ this.lines = [];
+ this.type = type;
+ this.doc = doc;
+ this.id = ++nextMarkerId;
+ };
+ eventMixin(TextMarker);
+
+ // 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 = 0; i < this.lines.length; ++i) {
+ var visual = visualLine(this.lines[i]), 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);
+ 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 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);
+ }
+ });
+ };
+
+ 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);
+ }
+ };
+
+ // 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;
+
+ // 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 = elt("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");
+ sawCollapsedSpans = true;
+ }
+
+ 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() { marker.clear(); });
+
+ if (marker.readOnly) {
+ sawReadOnlySpans = true;
+ 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.title || marker.startStyle || marker.endStyle || marker.css)
+ 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 = CodeMirror.SharedTextMarker = function(markers, primary) {
+ this.markers = markers;
+ this.primary = primary;
+ for (var i = 0; i < markers.length; ++i)
+ markers[i].parent = this;
+ };
+ eventMixin(SharedTextMarker);
+
+ 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);
+ };
+
+ 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) {
+ for (var i = 0; i < markers.length; i++) {
+ var marker = markers[i], linked = [marker.primary.doc];;
+ linkedDocs(marker.primary.doc, function(d) { 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);
+ }
+ }
+ }
+ }
+
+ // 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) {
+ for (var r, 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) {
+ if (old) for (var i = 0, nw; 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) {
+ if (old) for (var i = 0, nw; 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 = 0; i < last.length; ++i) {
+ var span = last[i];
+ if (span.to != null) span.to += offset;
+ if (span.from == null) {
+ var found = getMarkedSpanFor(first, span.marker);
+ if (!found) {
+ span.from = offset;
+ if (sameLine) (first || (first = [])).push(span);
+ }
+ } else {
+ span.from += offset;
+ if (sameLine) (first || (first = [])).push(span);
+ }
+ }
+ }
+ // 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 = 0; i < first.length; ++i)
+ if (first[i].to == null)
+ (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null));
+ for (var i = 0; i < gap; ++i)
+ 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 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 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 - 1;
+ }
+ }
+ 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, 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); }
+
+ // 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 && (cmp(found.to, from) > 0 || (sp.marker.inclusiveRight && marker.inclusiveLeft)) ||
+ fromCmp >= 0 && (cmp(found.from, to) < 0 || (sp.marker.inclusiveLeft && marker.inclusiveRight)))
+ 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;
+ }
+
+ // 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, 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, 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;
+ }
+ }
+
+ // LINE WIDGETS
+
+ // Line widgets are block elements displayed above or below a line.
+
+ var LineWidget = CodeMirror.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;
+ };
+ eventMixin(LineWidget);
+
+ function adjustScrollWhenAboveVisible(cm, line, diff) {
+ if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
+ addToScrollPos(cm, null, diff);
+ }
+
+ 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");
+ });
+ };
+ LineWidget.prototype.changed = function() {
+ var oldH = this.height, cm = this.doc.cm, line = this.line;
+ this.height = null;
+ var diff = widgetHeight(this) - oldH;
+ if (!diff) return;
+ updateLineHeight(line, line.height + diff);
+ if (cm) runInOp(cm, function() {
+ cm.curOp.forceUpdate = true;
+ adjustScrollWhenAboveVisible(cm, line, diff);
+ });
+ };
+
+ 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.offsetHeight;
+ }
+
+ 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) addToScrollPos(cm, null, widget.height);
+ cm.curOp.forceUpdate = true;
+ }
+ return true;
+ });
+ return widget;
+ }
+
+ // LINE DATA STRUCTURE
+
+ // Line objects. These hold state related to a line, including
+ // highlighting info (the styles array).
+ var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) {
+ this.text = text;
+ attachMarkedSpans(this, markedSpans);
+ this.height = estimateHeight ? estimateHeight(this) : 1;
+ };
+ eventMixin(Line);
+ Line.prototype.lineNo = function() { return lineNo(this); };
+
+ // 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);
+ }
+
+ 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;
+ }
+
+ function callBlankLine(mode, state) {
+ if (mode.blankLine) return mode.blankLine(state);
+ if (!mode.innerMode) return;
+ var inner = CodeMirror.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] = CodeMirror.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.");
+ }
+
+ // Utility for getTokenAt and getLineTokens
+ function takeToken(cm, pos, precise, asArray) {
+ function getObj(copy) {
+ return {start: stream.start, end: stream.pos,
+ string: stream.current(),
+ type: style || null,
+ state: copy ? copyState(doc.mode, state) : state};
+ }
+
+ var doc = cm.doc, mode = doc.mode, style;
+ pos = clipPos(doc, pos);
+ var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise);
+ var stream = new StringStream(line.text, cm.options.tabSize), tokens;
+ if (asArray) tokens = [];
+ while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
+ stream.start = stream.pos;
+ style = readToken(mode, stream, state);
+ if (asArray) tokens.push(getObj(true));
+ }
+ return asArray ? tokens : getObj();
+ }
+
+ // Run the given mode's parser over a line, calling f for each token.
+ function runMode(cm, text, mode, state, 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), style;
+ var inner = cm.options.addModeClass && [null];
+ if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses);
+ while (!stream.eol()) {
+ if (stream.pos > cm.options.maxHighlightLength) {
+ flattenSpans = false;
+ if (forceToEnd) processLine(cm, text, state, stream.pos);
+ stream.pos = text.length;
+ style = null;
+ } else {
+ style = extractLineClasses(readToken(mode, stream, 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 + 50000);
+ 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
+ var pos = Math.min(stream.pos, curStart + 50000);
+ f(pos, curStyle);
+ curStart = pos;
+ }
+ }
+
+ // 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, state, 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, state, function(end, style) {
+ st.push(end, style);
+ }, lineClasses, forceToEnd);
+
+ // Run overlays, adjust style array.
+ for (var o = 0; o < cm.state.overlays.length; ++o) {
+ var overlay = cm.state.overlays[o], i = 1, at = 0;
+ runMode(cm, line.text, overlay.mode, true, 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, "cm-overlay " + style);
+ i = start + 2;
+ } else {
+ for (; start < i; start += 2) {
+ var cur = st[start+1];
+ st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style;
+ }
+ }
+ }, lineClasses);
+ }
+
+ 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 state = getStateBefore(cm, lineNo(line));
+ var result = highlightLine(cm, line, line.text.length > cm.options.maxHighlightLength ? copyState(cm.doc.mode, state) : state);
+ line.stateAfter = state;
+ line.styles = result.styles;
+ if (result.classes) line.styleClasses = result.classes;
+ else if (line.styleClasses) line.styleClasses = null;
+ if (updateFrontier === cm.doc.frontier) cm.doc.frontier++;
+ }
+ return line.styles;
+ }
+
+ // 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, state, startAt) {
+ var mode = cm.doc.mode;
+ var stream = new StringStream(text, cm.options.tabSize);
+ stream.start = stream.pos = startAt || 0;
+ if (text == "") callBlankLine(mode, state);
+ while (!stream.eol()) {
+ readToken(mode, stream, state);
+ stream.start = stream.pos;
+ }
+ }
+
+ // 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 = elt("span", null, null, webkit ? "padding-right: .1px" : null);
+ var builder = {pre: elt("pre", [content], "CodeMirror-line"), content: content,
+ col: 0, pos: 0, cm: cm,
+ splitSpaces: (ie || webkit) && 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;
+ 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)))
+ 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 && /\bcm-tab\b/.test(builder.content.lastChild.className))
+ 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, title, css) {
+ if (!text) return;
+ var displayText = builder.splitSpaces ? text.replace(/ {3,}/g, splitSpaces) : text;
+ var special = builder.cm.state.specialChars, mustWrap = false;
+ if (!special.test(text)) {
+ builder.col += text.length;
+ var 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 {
+ var content = document.createDocumentFragment(), 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;
+ if (m[0] == "\t") {
+ var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
+ var txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
+ txt.setAttribute("role", "presentation");
+ txt.setAttribute("cm-text", "\t");
+ builder.col += tabWidth;
+ } else if (m[0] == "\r" || m[0] == "\n") {
+ var txt = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
+ txt.setAttribute("cm-text", m[0]);
+ builder.col += 1;
+ } else {
+ var txt = builder.cm.options.specialCharPlaceholder(m[0]);
+ txt.setAttribute("cm-text", m[0]);
+ if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
+ else content.appendChild(txt);
+ builder.col += 1;
+ }
+ builder.map.push(builder.pos, builder.pos + 1, txt);
+ builder.pos++;
+ }
+ }
+ 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 (title) token.title = title;
+ return builder.content.appendChild(token);
+ }
+ builder.content.appendChild(content);
+ }
+
+ function splitSpaces(old) {
+ var out = " ";
+ for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
+ out += " ";
+ return out;
+ }
+
+ // Work around nonsense dimensions being reported for stretches of
+ // right-to-left text.
+ function buildTokenBadBidi(inner, order) {
+ return function(builder, text, style, startStyle, endStyle, title, css) {
+ 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
+ for (var i = 0; i < order.length; i++) {
+ var part = order[i];
+ if (part.to > start && part.from <= start) break;
+ }
+ if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title, css);
+ inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css);
+ 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;
+ }
+
+ // 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; i < styles.length; i+=2)
+ builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options));
+ return;
+ }
+
+ var len = allText.length, pos = 0, i = 1, text = "", style, css;
+ var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;
+ for (;;) {
+ if (nextChange == pos) { // Update current marker set
+ spanStyle = spanEndStyle = spanStartStyle = title = css = "";
+ collapsed = null; nextChange = Infinity;
+ var foundBookmarks = [];
+ 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 = m.css;
+ if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
+ if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
+ if (m.title && !title) title = m.title;
+ if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
+ collapsed = sp;
+ } else if (sp.from > pos && nextChange > sp.from) {
+ nextChange = sp.from;
+ }
+ }
+ 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 (!collapsed && foundBookmarks.length) for (var j = 0; j < foundBookmarks.length; ++j)
+ buildCollapsedSpan(builder, 0, foundBookmarks[j]);
+ }
+ 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 : "", title, css);
+ }
+ 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);
+ }
+ }
+ }
+
+ // 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) {
+ for (var i = start, result = []; 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 = linesFor(1, text.length - 1);
+ added.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);
+ }
+ } 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 = linesFor(1, text.length - 1);
+ if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
+ doc.insert(from.line + 1, added);
+ }
+
+ signalLater(doc, "change", doc, change);
+ }
+
+ // 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;
+ for (var i = 0, height = 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) {
+ while (child.lines.length > 50) {
+ var spilled = child.lines.splice(child.lines.length - 25, 25);
+ var newleaf = new LeafChunk(spilled);
+ child.height -= newleaf.height;
+ this.children.splice(i + 1, 0, newleaf);
+ newleaf.parent = this;
+ }
+ 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;
+ }
+ }
+ };
+
+ var nextDocId = 0;
+ var Doc = CodeMirror.Doc = function(text, mode, firstLine, lineSep) {
+ if (!(this instanceof Doc)) return new Doc(text, mode, firstLine, lineSep);
+ 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.frontier = firstLine;
+ var start = Pos(firstLine, 0);
+ this.sel = simpleSelection(start);
+ this.history = new History(null);
+ this.id = ++nextDocId;
+ this.modeOption = mode;
+ this.lineSep = lineSep;
+
+ 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);
+ setSelection(this, simpleSelection(top));
+ }),
+ 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) {
+ extendSelections(this, map(this.sel.ranges, f), options);
+ }),
+ setSelections: docMethodOp(function(ranges, primary, options) {
+ if (!ranges.length) return;
+ for (var i = 0, out = []; 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(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(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 = changes.length - 1; i >= 0; i--)
+ makeChange(this, changes[i]);
+ 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 = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone;
+ return {undo: done, redo: undone};
+ },
+ clearHistory: function() {this.history = new History(this.history.maxGeneration);},
+
+ 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);
+ },
+
+ 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, "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 (!(lineNo == from.line && from.ch > span.to ||
+ span.from == null && lineNo != from.line||
+ 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;
+ this.iter(function(line) {
+ var sz = line.text.length + 1;
+ 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;
+ this.iter(this.first, coords.line, function (line) {
+ index += line.text.length + 1;
+ });
+ return index;
+ },
+
+ copy: function(copyHistory) {
+ var doc = new Doc(getLines(this, this.first, this.first + this.size),
+ this.modeOption, this.first, this.lineSep);
+ 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);
+ 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) {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"; }
+ });
+
+ // Public alias.
+ Doc.prototype.eachLine = Doc.prototype.iter;
+
+ // 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);
+
+ // 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);
+ if (!cm.options.lineWrapping) findMaxLine(cm);
+ cm.options.mode = doc.modeOption;
+ regChange(cm);
+ }
+
+ // LINE UTILITIES
+
+ // 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.");
+ for (var chunk = doc; !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); });
+ 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 = 0; i < chunk.children.length; ++i) {
+ var child = chunk.children[i], ch = child.height;
+ if (h < ch) { chunk = child; continue outer; }
+ h -= ch;
+ n += child.chunkSize();
+ }
+ return n;
+ } while (!chunk.lines);
+ for (var i = 0; i < chunk.lines.length; ++i) {
+ var line = chunk.lines[i], lh = line.height;
+ if (h < lh) break;
+ h -= lh;
+ }
+ return n + i;
+ }
+
+
+ // 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 = 0; i < p.children.length; ++i) {
+ var cur = p.children[i];
+ if (cur == chunk) break;
+ else h += cur.height;
+ }
+ }
+ return h;
+ }
+
+ // 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) {
+ var order = line.order;
+ if (order == null) order = line.order = bidiOrdering(line.text);
+ return order;
+ }
+
+ // HISTORY
+
+ 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) {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, ore 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;
+
+ if ((hist.lastOp == opId ||
+ hist.lastOrigin == change.origin && change.origin &&
+ ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
+ change.origin.charAt(0) == "*")) &&
+ (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
+ // Merge this change into the last event
+ var 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;
+ for (var i = 0, out; 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;
+ for (var i = 0, nw = []; i < change.text.length; ++i)
+ nw.push(removeClearedSpans(found[i]));
+ return nw;
+ }
+
+ // 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) {
+ for (var i = 0, copy = []; 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;
+ 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;
+ }
+
+ // 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 = 0; j < sub.changes.length; ++j) {
+ var cur = sub.changes[j];
+ 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);
+ }
+
+ // EVENT UTILITIES
+
+ // Due to the fact that we still support jurassic IE versions, some
+ // compatibility wrappers are needed.
+
+ var e_preventDefault = CodeMirror.e_preventDefault = function(e) {
+ if (e.preventDefault) e.preventDefault();
+ else e.returnValue = false;
+ };
+ var e_stopPropagation = CodeMirror.e_stopPropagation = function(e) {
+ if (e.stopPropagation) e.stopPropagation();
+ else e.cancelBubble = true;
+ };
+ function e_defaultPrevented(e) {
+ return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
+ }
+ var e_stop = CodeMirror.e_stop = function(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;
+ }
+
+ // EVENT HANDLING
+
+ // Lightweight event framework. on/off also work on DOM nodes,
+ // registering native DOM handlers.
+
+ var on = CodeMirror.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 = {});
+ var arr = map[type] || (map[type] = []);
+ arr.push(f);
+ }
+ };
+
+ var noHandlers = []
+ function getHandlers(emitter, type, copy) {
+ var arr = emitter._handlers && emitter._handlers[type]
+ if (copy) return arr && arr.length > 0 ? arr.slice() : noHandlers
+ else return arr || noHandlers
+ }
+
+ var off = CodeMirror.off = function(emitter, type, f) {
+ if (emitter.removeEventListener)
+ emitter.removeEventListener(type, f, false);
+ else if (emitter.detachEvent)
+ emitter.detachEvent("on" + type, f);
+ else {
+ var handlers = getHandlers(emitter, type, false)
+ for (var i = 0; i < handlers.length; ++i)
+ if (handlers[i] == f) { handlers.splice(i, 1); break; }
+ }
+ };
+
+ var signal = CodeMirror.signal = function(emitter, type /*, values...*/) {
+ var handlers = getHandlers(emitter, type, true)
+ 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);
+ };
+
+ 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, false)
+ 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);
+ }
+ function bnd(f) {return function(){f.apply(null, args);};};
+ for (var i = 0; i < arr.length; ++i)
+ list.push(bnd(arr[i]));
+ }
+
+ function fireOrphanDelayed() {
+ var delayed = orphanDelayedCallbacks;
+ orphanDelayedCallbacks = null;
+ for (var i = 0; i < delayed.length; ++i) delayed[i]();
+ }
+
+ // 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);};
+ }
+
+ // MISC UTILITIES
+
+ // Number of pixels added to scroller and sizer to hide scrollbar
+ var scrollerGap = 30;
+
+ // Returned or thrown by various protocols to signal 'I'm not
+ // handling this'.
+ var Pass = CodeMirror.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"};
+
+ function Delayed() {this.id = null;}
+ Delayed.prototype.set = function(ms, f) {
+ clearTimeout(this.id);
+ this.id = setTimeout(f, ms);
+ };
+
+ // Counts the column offset in a string, taking tabs into account.
+ // Used mostly to find indentation.
+ var countColumn = CodeMirror.countColumn = function(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;
+ }
+ };
+
+ // The inverse of countColumn -- find the offset that corresponds to
+ // a particular column.
+ var findColumn = CodeMirror.findColumn = function(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]; }
+
+ 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 indexOf(array, elt) {
+ for (var i = 0; i < array.length; ++i)
+ if (array[i] == elt) return i;
+ return -1;
+ }
+ function map(array, f) {
+ var out = [];
+ for (var i = 0; i < array.length; i++) out[i] = f(array[i], i);
+ return out;
+ }
+
+ 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;
+ };
+
+ 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;
+ }
+
+ function bind(f) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ return function(){return f.apply(null, args);};
+ }
+
+ var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
+ var isWordCharBasic = CodeMirror.isWordChar = function(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); }
+
+ // DOM UTILITIES
+
+ 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;
+ }
+
+ 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 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);
+ }
+
+ var contains = CodeMirror.contains = function(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() {
+ var activeElement = document.activeElement;
+ while (activeElement && activeElement.root && activeElement.root.activeElement)
+ activeElement = activeElement.root.activeElement;
+ return activeElement;
+ }
+ // Older versions of IE throws unspecified error when touching
+ // document.activeElement in some cases (during loading, in iframe)
+ if (ie && ie_version < 11) activeElt = function() {
+ try { return document.activeElement; }
+ catch(e) { return document.body; }
+ };
+
+ function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*"); }
+ var rmClass = CodeMirror.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 : "");
+ }
+ };
+ var addClass = CodeMirror.addClass = function(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;
+ }
+
+ // WINDOW-WIDE EVENTS
+
+ // 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.body.getElementsByClassName) return;
+ var byClass = document.body.getElementsByClassName("CodeMirror");
+ for (var i = 0; i < byClass.length; i++) {
+ var cm = byClass[i].CodeMirror;
+ if (cm) f(cm);
+ }
+ }
+
+ 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() {
+ forEachCodeMirror(onBlur);
+ });
+ }
+
+ // FEATURE DETECTION
+
+ // 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();
+ if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780)
+ var r1 = range(txt, 1, 2).getBoundingClientRect();
+ 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 = CodeMirror.splitLines = "\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) {
+ try {var 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;
+ }
+
+ // KEY NAMES
+
+ var keyNames = CodeMirror.keyNames = {
+ 3: "Enter", 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: "/", 127: "Delete",
+ 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"
+ };
+ (function() {
+ // Number keys
+ for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i);
+ // Alphabetic keys
+ for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
+ // Function keys
+ for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
+ })();
+
+ // BIDI HELPERS
+
+ function iterateBidiSections(order, from, to, f) {
+ if (!order) return f(from, to, "ltr");
+ 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");
+ found = true;
+ }
+ }
+ if (!found) f(from, to, "ltr");
+ }
+
+ function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
+ function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
+
+ function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
+ function lineRight(line) {
+ var order = getOrder(line);
+ if (!order) return line.text.length;
+ return bidiRight(lst(order));
+ }
+
+ function lineStart(cm, lineN) {
+ var line = getLine(cm.doc, lineN);
+ var visual = visualLine(line);
+ if (visual != line) lineN = lineNo(visual);
+ var order = getOrder(visual);
+ var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
+ return Pos(lineN, ch);
+ }
+ function lineEnd(cm, lineN) {
+ var merged, line = getLine(cm.doc, lineN);
+ while (merged = collapsedSpanAtEnd(line)) {
+ line = merged.find(1, true).line;
+ lineN = null;
+ }
+ var order = getOrder(line);
+ var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
+ return Pos(lineN == null ? lineNo(line) : lineN, ch);
+ }
+ function lineStartSmart(cm, pos) {
+ var start = lineStart(cm, pos.line);
+ var line = getLine(cm.doc, start.line);
+ var order = getOrder(line);
+ if (!order || order[0].level == 0) {
+ var firstNonWS = Math.max(0, line.text.search(/\S/));
+ var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
+ return Pos(start.line, inWS ? 0 : firstNonWS);
+ }
+ return start;
+ }
+
+ function compareBidiLevel(order, a, b) {
+ var linedir = order[0].level;
+ if (a == linedir) return true;
+ if (b == linedir) return false;
+ return a < b;
+ }
+ var bidiOther;
+ function getBidiPartAt(order, pos) {
+ bidiOther = null;
+ for (var i = 0, found; i < order.length; ++i) {
+ var cur = order[i];
+ if (cur.from < pos && cur.to > pos) return i;
+ if ((cur.from == pos || cur.to == pos)) {
+ if (found == null) {
+ found = i;
+ } else if (compareBidiLevel(order, cur.level, order[found].level)) {
+ if (cur.from != cur.to) bidiOther = found;
+ return i;
+ } else {
+ if (cur.from != cur.to) bidiOther = i;
+ return found;
+ }
+ }
+ }
+ return found;
+ }
+
+ function moveInLine(line, pos, dir, byUnit) {
+ if (!byUnit) return pos + dir;
+ do pos += dir;
+ while (pos > 0 && isExtendingChar(line.text.charAt(pos)));
+ return pos;
+ }
+
+ // This is needed in order to move 'visually' through bi-directional
+ // text -- i.e., pressing left should make the cursor go left, even
+ // when in RTL text. The tricky part is the 'jumps', where RTL and
+ // LTR text touch each other. This often requires the cursor offset
+ // to move more than one unit, in order to visually move one unit.
+ function moveVisually(line, start, dir, byUnit) {
+ var bidi = getOrder(line);
+ if (!bidi) return moveLogically(line, start, dir, byUnit);
+ var pos = getBidiPartAt(bidi, start), part = bidi[pos];
+ var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit);
+
+ for (;;) {
+ if (target > part.from && target < part.to) return target;
+ if (target == part.from || target == part.to) {
+ if (getBidiPartAt(bidi, target) == pos) return target;
+ part = bidi[pos += dir];
+ return (dir > 0) == part.level % 2 ? part.to : part.from;
+ } else {
+ part = bidi[pos += dir];
+ if (!part) return null;
+ if ((dir > 0) == part.level % 2)
+ target = moveInLine(line, part.to, -1, byUnit);
+ else
+ target = moveInLine(line, part.from, 1, byUnit);
+ }
+ }
+ }
+
+ function moveLogically(line, start, dir, byUnit) {
+ var target = start + dir;
+ if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir;
+ return target < 0 || target > line.text.length ? null : target;
+ }
+
+ // 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 0x6ff
+ var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm";
+ function charType(code) {
+ if (code <= 0xf7) return lowTypes.charAt(code);
+ else if (0x590 <= code && code <= 0x5f4) return "R";
+ else if (0x600 <= code && code <= 0x6ed) 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]/;
+ // Browsers seem to always treat the boundaries of block elements as being L.
+ var outerType = "L";
+
+ function BidiSpan(level, from, to) {
+ this.level = level;
+ this.from = from; this.to = to;
+ }
+
+ return function(str) {
+ if (!bidiRE.test(str)) return false;
+ var len = str.length, types = [];
+ for (var i = 0, type; i < len; ++i)
+ types.push(type = 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 = 0, prev = outerType; i < len; ++i) {
+ var type = types[i];
+ if (type == "m") types[i] = 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 = 0, cur = outerType; i < len; ++i) {
+ var type = types[i];
+ if (type == "1" && cur == "r") types[i] = "n";
+ else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "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 = 1, prev = types[0]; i < len - 1; ++i) {
+ var type = types[i];
+ if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
+ else if (type == "," && prev == types[i+1] &&
+ (prev == "1" || prev == "n")) types[i] = prev;
+ prev = type;
+ }
+
+ // 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 = 0; i < len; ++i) {
+ var type = types[i];
+ if (type == ",") types[i] = "N";
+ else if (type == "%") {
+ for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
+ var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
+ for (var j = i; j < end; ++j) types[j] = replace;
+ i = 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 = 0, cur = outerType; i < len; ++i) {
+ var type = types[i];
+ if (cur == "L" && type == "1") types[i] = "L";
+ else if (isStrong.test(type)) cur = type;
+ }
+
+ // 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 = 0; i < len; ++i) {
+ if (isNeutral.test(types[i])) {
+ for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
+ var before = (i ? types[i-1] : outerType) == "L";
+ var after = (end < len ? types[end] : outerType) == "L";
+ var replace = before || after ? "L" : "R";
+ for (var j = i; j < end; ++j) types[j] = replace;
+ i = end - 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 = 0; i < len;) {
+ if (countsAsLeft.test(types[i])) {
+ var start = i;
+ for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
+ order.push(new BidiSpan(0, start, i));
+ } else {
+ var pos = i, at = order.length;
+ for (++i; i < len && types[i] != "L"; ++i) {}
+ for (var j = pos; j < i;) {
+ if (countsAsNum.test(types[j])) {
+ if (pos < j) order.splice(at, 0, new BidiSpan(1, pos, j));
+ var nstart = j;
+ for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
+ order.splice(at, 0, new BidiSpan(2, nstart, j));
+ pos = j;
+ } else ++j;
+ }
+ if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i));
+ }
+ }
+ 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));
+ }
+ if (order[0].level == 2)
+ order.unshift(new BidiSpan(1, order[0].to, order[0].to));
+ if (order[0].level != lst(order).level)
+ order.push(new BidiSpan(order[0].level, len, len));
+
+ return order;
+ };
+ })();
+
+ // THE END
+
+ CodeMirror.version = "5.7.1";
+
+ return CodeMirror;
+});
+
+},{}],7:[function(require,module,exports){
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"), require("../markdown/markdown"), require("../../addon/mode/overlay"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "../markdown/markdown", "../../addon/mode/overlay"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+var urlRE = /^((?:(?:aaas?|about|acap|adiumxtra|af[ps]|aim|apt|attachment|aw|beshare|bitcoin|bolo|callto|cap|chrome(?:-extension)?|cid|coap|com-eventbrite-attendee|content|crid|cvs|data|dav|dict|dlna-(?:playcontainer|playsingle)|dns|doi|dtn|dvb|ed2k|facetime|feed|file|finger|fish|ftp|geo|gg|git|gizmoproject|go|gopher|gtalk|h323|hcp|https?|iax|icap|icon|im|imap|info|ipn|ipp|irc[6s]?|iris(?:\.beep|\.lwz|\.xpc|\.xpcs)?|itms|jar|javascript|jms|keyparc|lastfm|ldaps?|magnet|mailto|maps|market|message|mid|mms|ms-help|msnim|msrps?|mtqp|mumble|mupdate|mvn|news|nfs|nih?|nntp|notes|oid|opaquelocktoken|palm|paparazzi|platform|pop|pres|proxy|psyc|query|res(?:ource)?|rmi|rsync|rtmp|rtsp|secondlife|service|session|sftp|sgn|shttp|sieve|sips?|skype|sm[bs]|snmp|soap\.beeps?|soldat|spotify|ssh|steam|svn|tag|teamspeak|tel(?:net)?|tftp|things|thismessage|tip|tn3270|tv|udp|unreal|urn|ut2004|vemmi|ventrilo|view-source|webcal|wss?|wtai|wyciwyg|xcon(?:-userid)?|xfire|xmlrpc\.beeps?|xmpp|xri|ymsgr|z39\.50[rs]?):(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]|\([^\s()<>]*\))+(?:\([^\s()<>]*\)|[^\s`*!()\[\]{};:'".,<>?«»“”‘’]))/i
+
+CodeMirror.defineMode("gfm", function(config, modeConfig) {
+ var codeDepth = 0;
+ function blankLine(state) {
+ state.code = false;
+ return null;
+ }
+ var gfmOverlay = {
+ startState: function() {
+ return {
+ code: false,
+ codeBlock: false,
+ ateSpace: false
+ };
+ },
+ copyState: function(s) {
+ return {
+ code: s.code,
+ codeBlock: s.codeBlock,
+ ateSpace: s.ateSpace
+ };
+ },
+ token: function(stream, state) {
+ state.combineTokens = null;
+
+ // Hack to prevent formatting override inside code blocks (block and inline)
+ if (state.codeBlock) {
+ if (stream.match(/^```+/)) {
+ state.codeBlock = false;
+ return null;
+ }
+ stream.skipToEnd();
+ return null;
+ }
+ if (stream.sol()) {
+ state.code = false;
+ }
+ if (stream.sol() && stream.match(/^```+/)) {
+ stream.skipToEnd();
+ state.codeBlock = true;
+ return null;
+ }
+ // If this block is changed, it may need to be updated in Markdown mode
+ if (stream.peek() === '`') {
+ stream.next();
+ var before = stream.pos;
+ stream.eatWhile('`');
+ var difference = 1 + stream.pos - before;
+ if (!state.code) {
+ codeDepth = difference;
+ state.code = true;
+ } else {
+ if (difference === codeDepth) { // Must be exact
+ state.code = false;
+ }
+ }
+ return null;
+ } else if (state.code) {
+ stream.next();
+ return null;
+ }
+ // Check if space. If so, links can be formatted later on
+ if (stream.eatSpace()) {
+ state.ateSpace = true;
+ return null;
+ }
+ if (stream.sol() || state.ateSpace) {
+ state.ateSpace = false;
+ if (modeConfig.gitHubSpice !== false) {
+ if(stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+@)?(?:[a-f0-9]{7,40}\b)/)) {
+ // User/Project@SHA
+ // User@SHA
+ // SHA
+ state.combineTokens = true;
+ return "link";
+ } else if (stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+)?#[0-9]+\b/)) {
+ // User/Project#Num
+ // User#Num
+ // #Num
+ state.combineTokens = true;
+ return "link";
+ }
+ }
+ }
+ if (stream.match(urlRE) &&
+ stream.string.slice(stream.start - 2, stream.start) != "](" &&
+ (stream.start == 0 || /\W/.test(stream.string.charAt(stream.start - 1)))) {
+ // URLs
+ // Taken from http://daringfireball.net/2010/07/improved_regex_for_matching_urls
+ // And then (issue #1160) simplified to make it not crash the Chrome Regexp engine
+ // And then limited url schemes to the CommonMark list, so foo:bar isn't matched as a URL
+ state.combineTokens = true;
+ return "link";
+ }
+ stream.next();
+ return null;
+ },
+ blankLine: blankLine
+ };
+
+ var markdownConfig = {
+ underscoresBreakWords: false,
+ taskLists: true,
+ fencedCodeBlocks: '```',
+ strikethrough: true
+ };
+ for (var attr in modeConfig) {
+ markdownConfig[attr] = modeConfig[attr];
+ }
+ markdownConfig.name = "markdown";
+ return CodeMirror.overlayMode(CodeMirror.getMode(config, markdownConfig), gfmOverlay);
+
+}, "markdown");
+
+ CodeMirror.defineMIME("text/x-gfm", "gfm");
+});
+
+},{"../../addon/mode/overlay":5,"../../lib/codemirror":6,"../markdown/markdown":8}],8:[function(require,module,exports){
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://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 htmlFound = CodeMirror.modes.hasOwnProperty("xml");
+ var htmlMode = CodeMirror.getMode(cmCfg, htmlFound ? {name: "xml", htmlMode: true} : "text/plain");
+
+ 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;
+
+ // Should underscores in words open/close em/strong?
+ if (modeCfg.underscoresBreakWords === undefined)
+ modeCfg.underscoresBreakWords = true;
+
+ // Use `fencedCodeBlocks` to configure fenced code blocks. false to
+ // disable, string to specify a precise regexp that the fence should
+ // match, and true to allow three or more backticks or tildes (as
+ // per CommonMark).
+
+ // Turn on task lists? ("- [ ] " and "- [x] ")
+ if (modeCfg.taskLists === undefined) modeCfg.taskLists = false;
+
+ // Turn on strikethrough syntax
+ if (modeCfg.strikethrough === undefined)
+ modeCfg.strikethrough = false;
+
+ var codeDepth = 0;
+
+ var header = 'header'
+ , code = 'comment'
+ , quote = 'quote'
+ , list1 = 'variable-2'
+ , list2 = 'variable-3'
+ , list3 = 'keyword'
+ , hr = 'hr'
+ , image = 'tag'
+ , formatting = 'formatting'
+ , linkinline = 'link'
+ , linkemail = 'link'
+ , linktext = 'link'
+ , linkhref = 'string'
+ , em = 'em'
+ , strong = 'strong'
+ , strikethrough = 'strikethrough';
+
+ var hrRE = /^([*\-_])(?:\s*\1){2,}\s*$/
+ , ulRE = /^[*\-+]\s+/
+ , olRE = /^[0-9]+([.)])\s+/
+ , taskListRE = /^\[(x| )\](?=\s)/ // Must follow ulRE or olRE
+ , atxHeaderRE = modeCfg.allowAtxHeaderWithoutSpace ? /^(#+)/ : /^(#+)(?: |$)/
+ , setextHeaderRE = /^ *(?:\={1,}|-{1,})\s*$/
+ , textRE = /^[^#!\[\]*_\\<>` "'(~]+/
+ , fencedCodeRE = new RegExp("^(" + (modeCfg.fencedCodeBlocks === true ? "~~~+|```+" : modeCfg.fencedCodeBlocks) +
+ ")[ \\t]*([\\w+#]*)");
+
+ 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;
+ // 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 (!htmlFound && state.f == htmlBlock) {
+ state.f = inlineNormal;
+ state.block = blockNormal;
+ }
+ // Reset state.trailingSpace
+ state.trailingSpace = 0;
+ state.trailingSpaceNewLine = false;
+ // Mark this line as blank
+ state.prevLine = state.thisLine
+ state.thisLine = null
+ return null;
+ }
+
+ function blockNormal(stream, state) {
+
+ var sol = stream.sol();
+
+ var prevLineIsList = state.list !== false,
+ prevLineIsIndentedCode = state.indentedCode;
+
+ state.indentedCode = false;
+
+ if (prevLineIsList) {
+ if (state.indentationDiff >= 0) { // Continued list
+ if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block
+ state.indentation -= state.indentationDiff;
+ }
+ state.list = null;
+ } else if (state.indentation > 0) {
+ state.list = null;
+ state.listDepth = Math.floor(state.indentation / 4);
+ } else { // No longer a list
+ state.list = false;
+ state.listDepth = 0;
+ }
+ }
+
+ var match = null;
+ if (state.indentationDiff >= 4) {
+ stream.skipToEnd();
+ if (prevLineIsIndentedCode || lineIsEmpty(state.prevLine)) {
+ state.indentation -= 4;
+ state.indentedCode = true;
+ return code;
+ } else {
+ return null;
+ }
+ } else if (stream.eatSpace()) {
+ return null;
+ } else if ((match = stream.match(atxHeaderRE)) && match[1].length <= 6) {
+ state.header = match[1].length;
+ if (modeCfg.highlightFormatting) state.formatting = "header";
+ state.f = state.inline;
+ return getType(state);
+ } else if (!lineIsEmpty(state.prevLine) && !state.quote && !prevLineIsList &&
+ !prevLineIsIndentedCode && (match = stream.match(setextHeaderRE))) {
+ state.header = match[0].charAt(0) == '=' ? 1 : 2;
+ if (modeCfg.highlightFormatting) state.formatting = "header";
+ state.f = state.inline;
+ return getType(state);
+ } else if (stream.eat('>')) {
+ state.quote = sol ? 1 : state.quote + 1;
+ if (modeCfg.highlightFormatting) state.formatting = "quote";
+ stream.eatSpace();
+ return getType(state);
+ } else if (stream.peek() === '[') {
+ return switchInline(stream, state, footnoteLink);
+ } else if (stream.match(hrRE, true)) {
+ state.hr = true;
+ return hr;
+ } else if ((lineIsEmpty(state.prevLine) || prevLineIsList) && (stream.match(ulRE, false) || stream.match(olRE, false))) {
+ var listType = null;
+ if (stream.match(ulRE, true)) {
+ listType = 'ul';
+ } else {
+ stream.match(olRE, true);
+ listType = 'ol';
+ }
+ state.indentation = stream.column() + stream.current().length;
+ state.list = true;
+ state.listDepth++;
+ 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 (modeCfg.fencedCodeBlocks && (match = stream.match(fencedCodeRE, true))) {
+ state.fencedChars = match[1]
+ // try switching mode
+ state.localMode = getMode(match[2]);
+ if (state.localMode) state.localState = state.localMode.startState();
+ state.f = state.block = local;
+ if (modeCfg.highlightFormatting) state.formatting = "code-block";
+ state.code = true;
+ return getType(state);
+ }
+
+ return switchInline(stream, state, state.inline);
+ }
+
+ function htmlBlock(stream, state) {
+ var style = htmlMode.token(stream, state.htmlState);
+ if ((htmlFound && state.htmlState.tagStart === null &&
+ (!state.htmlState.context && state.htmlState.tokenize.isInText)) ||
+ (state.md_inside && stream.current().indexOf(">") > -1)) {
+ state.f = inlineNormal;
+ state.block = blockNormal;
+ state.htmlState = null;
+ }
+ return style;
+ }
+
+ function local(stream, state) {
+ if (stream.sol() && state.fencedChars && stream.match(state.fencedChars, false)) {
+ state.localMode = state.localState = null;
+ state.f = state.block = leavingLocal;
+ return null;
+ } else if (state.localMode) {
+ return state.localMode.token(stream, state.localState);
+ } else {
+ stream.skipToEnd();
+ return code;
+ }
+ }
+
+ function leavingLocal(stream, state) {
+ stream.match(state.fencedChars);
+ state.block = blockNormal;
+ state.f = inlineNormal;
+ state.fencedChars = null;
+ if (modeCfg.highlightFormatting) state.formatting = "code-block";
+ state.code = true;
+ var returnType = getType(state);
+ state.code = false;
+ return returnType;
+ }
+
+ // Inline
+ function getType(state) {
+ var styles = [];
+
+ if (state.formatting) {
+ styles.push(formatting);
+
+ if (typeof state.formatting === "string") state.formatting = [state.formatting];
+
+ for (var i = 0; i < state.formatting.length; i++) {
+ styles.push(formatting + "-" + state.formatting[i]);
+
+ if (state.formatting[i] === "header") {
+ styles.push(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(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(linkhref, "url");
+ } else { // Only apply inline styles to non-url text
+ if (state.strong) { styles.push(strong); }
+ if (state.em) { styles.push(em); }
+ if (state.strikethrough) { styles.push(strikethrough); }
+
+ if (state.linkText) { styles.push(linktext); }
+
+ if (state.code) { styles.push(code); }
+ }
+
+ if (state.header) { styles.push(header); styles.push(header + "-" + state.header); }
+
+ if (state.quote) {
+ styles.push(quote);
+
+ // Add `quote-#` where the maximum for `#` is modeCfg.maxBlockquoteDepth
+ if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
+ styles.push(quote + "-" + state.quote);
+ } else {
+ styles.push(quote + "-" + modeCfg.maxBlockquoteDepth);
+ }
+ }
+
+ if (state.list !== false) {
+ var listMod = (state.listDepth - 1) % 3;
+ if (!listMod) {
+ styles.push(list1);
+ } else if (listMod === 1) {
+ styles.push(list2);
+ } else {
+ styles.push(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] !== "x";
+ 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);
+ }
+
+ // Get sol() value now, before character is consumed
+ var sol = stream.sol();
+
+ var ch = stream.next();
+
+ if (ch === '\\') {
+ stream.next();
+ if (modeCfg.highlightFormatting) {
+ var type = getType(state);
+ return type ? type + " formatting-escape" : "formatting-escape";
+ }
+ }
+
+ // 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 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";
+ var t = getType(state);
+ var before = stream.pos;
+ stream.eatWhile('`');
+ var difference = 1 + stream.pos - before;
+ if (!state.code) {
+ codeDepth = difference;
+ state.code = true;
+ return getType(state);
+ } else {
+ if (difference === codeDepth) { // Must be exact
+ state.code = false;
+ return t;
+ }
+ state.formatting = previousFormatting;
+ return getType(state);
+ }
+ } else if (state.code) {
+ return getType(state);
+ }
+
+ if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) {
+ stream.match(/\[[^\]]*\]/);
+ state.inline = state.f = linkHref;
+ return image;
+ }
+
+ if (ch === '[' && stream.match(/.*\](\(.*\)| ?\[.*\])/, false)) {
+ state.linkText = true;
+ if (modeCfg.highlightFormatting) state.formatting = "link";
+ return getType(state);
+ }
+
+ if (ch === ']' && state.linkText && stream.match(/\(.*\)| ?\[.*\]/, false)) {
+ if (modeCfg.highlightFormatting) state.formatting = "link";
+ var type = getType(state);
+ state.linkText = false;
+ state.inline = state.f = linkHref;
+ 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 + 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 + linkemail;
+ }
+
+ if (ch === '<' && stream.match(/^(!--|\w)/, 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 (ch === '<' && stream.match(/^\/\w*?>/)) {
+ state.md_inside = false;
+ return "tag";
+ }
+
+ var ignoreUnderscore = false;
+ if (!modeCfg.underscoresBreakWords) {
+ if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) {
+ var prevPos = stream.pos - 2;
+ if (prevPos >= 0) {
+ var prevCh = stream.string.charAt(prevPos);
+ if (prevCh !== '_' && prevCh.match(/(\w)/, false)) {
+ ignoreUnderscore = true;
+ }
+ }
+ }
+ }
+ if (ch === '*' || (ch === '_' && !ignoreUnderscore)) {
+ if (sol && stream.peek() === ' ') {
+ // Do nothing, surrounded by newline and space
+ } else if (state.strong === ch && stream.eat(ch)) { // Remove STRONG
+ if (modeCfg.highlightFormatting) state.formatting = "strong";
+ var t = getType(state);
+ state.strong = false;
+ return t;
+ } else if (!state.strong && stream.eat(ch)) { // Add STRONG
+ state.strong = ch;
+ if (modeCfg.highlightFormatting) state.formatting = "strong";
+ return getType(state);
+ } else if (state.em === ch) { // Remove EM
+ if (modeCfg.highlightFormatting) state.formatting = "em";
+ var t = getType(state);
+ state.em = false;
+ return t;
+ } else if (!state.em) { // Add EM
+ state.em = ch;
+ if (modeCfg.highlightFormatting) state.formatting = "em";
+ return getType(state);
+ }
+ } 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 (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 + linkinline;
+ }
+
+ stream.match(/^[^>]+/, true);
+
+ return 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';
+ }
+
+ 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;
+ }
+
+ if (stream.match(inlineRE(endChar), true)) {
+ stream.backUp(1);
+ }
+
+ 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 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 linkhref + " url";
+ }
+
+ var savedInlineRE = [];
+ function inlineRE(endChar) {
+ if (!savedInlineRE[endChar]) {
+ // Escape endChar for RegExp (taken from http://stackoverflow.com/a/494122/526741)
+ endChar = (endChar+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
+ // Match any non-endChar, escaped character, as well as the closing
+ // endChar.
+ savedInlineRE[endChar] = new RegExp('^(?:[^\\\\]|\\\\.)*?(' + endChar + ')');
+ }
+ return savedInlineRE[endChar];
+ }
+
+ var mode = {
+ startState: function() {
+ return {
+ f: blockNormal,
+
+ prevLine: null,
+ thisLine: null,
+
+ block: blockNormal,
+ htmlState: null,
+ indentation: 0,
+
+ inline: inlineNormal,
+ text: handleText,
+
+ formatting: false,
+ linkText: false,
+ linkHref: false,
+ linkTitle: false,
+ em: false,
+ strong: false,
+ header: 0,
+ hr: false,
+ taskList: false,
+ list: false,
+ listDepth: 0,
+ quote: 0,
+ trailingSpace: 0,
+ trailingSpaceNewLine: false,
+ strikethrough: false,
+ fencedChars: null
+ };
+ },
+
+ copyState: function(s) {
+ return {
+ f: s.f,
+
+ prevLine: s.prevLine,
+ thisLine: s.this,
+
+ 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,
+ linkTitle: s.linkTitle,
+ code: s.code,
+ em: s.em,
+ strong: s.strong,
+ strikethrough: s.strikethrough,
+ header: s.header,
+ hr: s.hr,
+ taskList: s.taskList,
+ list: s.list,
+ listDepth: s.listDepth,
+ quote: s.quote,
+ indentedCode: s.indentedCode,
+ trailingSpace: s.trailingSpace,
+ trailingSpaceNewLine: s.trailingSpaceNewLine,
+ md_inside: s.md_inside,
+ fencedChars: s.fencedChars
+ };
+ },
+
+ token: function(stream, state) {
+
+ // Reset state.formatting
+ state.formatting = false;
+
+ if (stream != state.thisLine) {
+ var forceBlankLine = state.header || state.hr;
+
+ // Reset state.header and state.hr
+ state.header = 0;
+ state.hr = false;
+
+ if (stream.match(/^\s*$/, true) || forceBlankLine) {
+ blankLine(state);
+ if (!forceBlankLine) return null
+ state.prevLine = null
+ }
+
+ state.prevLine = state.thisLine
+ state.thisLine = stream
+
+ // Reset state.taskList
+ state.taskList = false;
+
+ // Reset state.trailingSpace
+ state.trailingSpace = 0;
+ state.trailingSpaceNewLine = false;
+
+ state.f = state.block;
+ var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, ' ').length;
+ var difference = Math.floor((indentation - state.indentation) / 4) * 4;
+ if (difference > 4) difference = 4;
+ var adjustedIndentation = state.indentation + difference;
+ state.indentationDiff = adjustedIndentation - state.indentation;
+ state.indentation = adjustedIndentation;
+ 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};
+ },
+
+ blankLine: blankLine,
+
+ getType: getType,
+
+ fold: "markdown"
+ };
+ return mode;
+}, "xml");
+
+CodeMirror.defineMIME("text/x-markdown", "markdown");
+
+});
+
+},{"../../lib/codemirror":6,"../meta":9,"../xml/xml":10}],9:[function(require,module,exports){
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://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.modeInfo = [
+ {name: "APL", mime: "text/apl", mode: "apl", ext: ["dyalog", "apl"]},
+ {name: "PGP", mimes: ["application/pgp", "application/pgp-keys", "application/pgp-signature"], mode: "asciiarmor", ext: ["pgp"]},
+ {name: "ASN.1", mime: "text/x-ttcn-asn", mode: "asn.1", ext: ["asn", "asn1"]},
+ {name: "Asterisk", mime: "text/x-asterisk", mode: "asterisk", file: /^extensions\.conf$/i},
+ {name: "Brainfuck", mime: "text/x-brainfuck", mode: "brainfuck", ext: ["b", "bf"]},
+ {name: "C", mime: "text/x-csrc", mode: "clike", ext: ["c", "h"]},
+ {name: "C++", mime: "text/x-c++src", mode: "clike", ext: ["cpp", "c++", "cc", "cxx", "hpp", "h++", "hh", "hxx"], alias: ["cpp"]},
+ {name: "Cobol", mime: "text/x-cobol", mode: "cobol", ext: ["cob", "cpy"]},
+ {name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp"]},
+ {name: "Clojure", mime: "text/x-clojure", mode: "clojure", ext: ["clj"]},
+ {name: "Closure Stylesheets (GSS)", mime: "text/x-gss", mode: "css", ext: ["gss"]},
+ {name: "CMake", mime: "text/x-cmake", mode: "cmake", ext: ["cmake", "cmake.in"], file: /^CMakeLists.txt$/},
+ {name: "CoffeeScript", mime: "text/x-coffeescript", mode: "coffeescript", ext: ["coffee"], alias: ["coffee", "coffee-script"]},
+ {name: "Common Lisp", mime: "text/x-common-lisp", mode: "commonlisp", ext: ["cl", "lisp", "el"], alias: ["lisp"]},
+ {name: "Cypher", mime: "application/x-cypher-query", mode: "cypher", ext: ["cyp", "cypher"]},
+ {name: "Cython", mime: "text/x-cython", mode: "python", ext: ["pyx", "pxd", "pxi"]},
+ {name: "CSS", mime: "text/css", mode: "css", ext: ["css"]},
+ {name: "CQL", mime: "text/x-cassandra", mode: "sql", ext: ["cql"]},
+ {name: "D", mime: "text/x-d", mode: "d", ext: ["d"]},
+ {name: "Dart", mimes: ["application/dart", "text/x-dart"], mode: "dart", ext: ["dart"]},
+ {name: "diff", mime: "text/x-diff", mode: "diff", ext: ["diff", "patch"]},
+ {name: "Django", mime: "text/x-django", mode: "django"},
+ {name: "Dockerfile", mime: "text/x-dockerfile", mode: "dockerfile", file: /^Dockerfile$/},
+ {name: "DTD", mime: "application/xml-dtd", mode: "dtd", ext: ["dtd"]},
+ {name: "Dylan", mime: "text/x-dylan", mode: "dylan", ext: ["dylan", "dyl", "intr"]},
+ {name: "EBNF", mime: "text/x-ebnf", mode: "ebnf"},
+ {name: "ECL", mime: "text/x-ecl", mode: "ecl", ext: ["ecl"]},
+ {name: "Eiffel", mime: "text/x-eiffel", mode: "eiffel", ext: ["e"]},
+ {name: "Elm", mime: "text/x-elm", mode: "elm", ext: ["elm"]},
+ {name: "Embedded Javascript", mime: "application/x-ejs", mode: "htmlembedded", ext: ["ejs"]},
+ {name: "Embedded Ruby", mime: "application/x-erb", mode: "htmlembedded", ext: ["erb"]},
+ {name: "Erlang", mime: "text/x-erlang", mode: "erlang", ext: ["erl"]},
+ {name: "Factor", mime: "text/x-factor", mode: "factor", ext: ["factor"]},
+ {name: "Forth", mime: "text/x-forth", mode: "forth", ext: ["forth", "fth", "4th"]},
+ {name: "Fortran", mime: "text/x-fortran", mode: "fortran", ext: ["f", "for", "f77", "f90"]},
+ {name: "F#", mime: "text/x-fsharp", mode: "mllike", ext: ["fs"], alias: ["fsharp"]},
+ {name: "Gas", mime: "text/x-gas", mode: "gas", ext: ["s"]},
+ {name: "Gherkin", mime: "text/x-feature", mode: "gherkin", ext: ["feature"]},
+ {name: "GitHub Flavored Markdown", mime: "text/x-gfm", mode: "gfm", file: /^(readme|contributing|history).md$/i},
+ {name: "Go", mime: "text/x-go", mode: "go", ext: ["go"]},
+ {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy"]},
+ {name: "HAML", mime: "text/x-haml", mode: "haml", ext: ["haml"]},
+ {name: "Haskell", mime: "text/x-haskell", mode: "haskell", ext: ["hs"]},
+ {name: "Haxe", mime: "text/x-haxe", mode: "haxe", ext: ["hx"]},
+ {name: "HXML", mime: "text/x-hxml", mode: "haxe", ext: ["hxml"]},
+ {name: "ASP.NET", mime: "application/x-aspx", mode: "htmlembedded", ext: ["aspx"], alias: ["asp", "aspx"]},
+ {name: "HTML", mime: "text/html", mode: "htmlmixed", ext: ["html", "htm"], alias: ["xhtml"]},
+ {name: "HTTP", mime: "message/http", mode: "http"},
+ {name: "IDL", mime: "text/x-idl", mode: "idl", ext: ["pro"]},
+ {name: "Jade", mime: "text/x-jade", mode: "jade", ext: ["jade"]},
+ {name: "Java", mime: "text/x-java", mode: "clike", ext: ["java"]},
+ {name: "Java Server Pages", mime: "application/x-jsp", mode: "htmlembedded", ext: ["jsp"], alias: ["jsp"]},
+ {name: "JavaScript", mimes: ["text/javascript", "text/ecmascript", "application/javascript", "application/x-javascript", "application/ecmascript"],
+ mode: "javascript", ext: ["js"], alias: ["ecmascript", "js", "node"]},
+ {name: "JSON", mimes: ["application/json", "application/x-json"], mode: "javascript", ext: ["json", "map"], alias: ["json5"]},
+ {name: "JSON-LD", mime: "application/ld+json", mode: "javascript", ext: ["jsonld"], alias: ["jsonld"]},
+ {name: "Jinja2", mime: "null", mode: "jinja2"},
+ {name: "Julia", mime: "text/x-julia", mode: "julia", ext: ["jl"]},
+ {name: "Kotlin", mime: "text/x-kotlin", mode: "clike", ext: ["kt"]},
+ {name: "LESS", mime: "text/x-less", mode: "css", ext: ["less"]},
+ {name: "LiveScript", mime: "text/x-livescript", mode: "livescript", ext: ["ls"], alias: ["ls"]},
+ {name: "Lua", mime: "text/x-lua", mode: "lua", ext: ["lua"]},
+ {name: "Markdown", mime: "text/x-markdown", mode: "markdown", ext: ["markdown", "md", "mkd"]},
+ {name: "mIRC", mime: "text/mirc", mode: "mirc"},
+ {name: "MariaDB SQL", mime: "text/x-mariadb", mode: "sql"},
+ {name: "Mathematica", mime: "text/x-mathematica", mode: "mathematica", ext: ["m", "nb"]},
+ {name: "Modelica", mime: "text/x-modelica", mode: "modelica", ext: ["mo"]},
+ {name: "MUMPS", mime: "text/x-mumps", mode: "mumps"},
+ {name: "MS SQL", mime: "text/x-mssql", mode: "sql"},
+ {name: "MySQL", mime: "text/x-mysql", mode: "sql"},
+ {name: "Nginx", mime: "text/x-nginx-conf", mode: "nginx", file: /nginx.*\.conf$/i},
+ {name: "NTriples", mime: "text/n-triples", mode: "ntriples", ext: ["nt"]},
+ {name: "Objective C", mime: "text/x-objectivec", mode: "clike", ext: ["m", "mm"]},
+ {name: "OCaml", mime: "text/x-ocaml", mode: "mllike", ext: ["ml", "mli", "mll", "mly"]},
+ {name: "Octave", mime: "text/x-octave", mode: "octave", ext: ["m"]},
+ {name: "Oz", mime: "text/x-oz", mode: "oz", ext: ["oz"]},
+ {name: "Pascal", mime: "text/x-pascal", mode: "pascal", ext: ["p", "pas"]},
+ {name: "PEG.js", mime: "null", mode: "pegjs", ext: ["jsonld"]},
+ {name: "Perl", mime: "text/x-perl", mode: "perl", ext: ["pl", "pm"]},
+ {name: "PHP", mime: "application/x-httpd-php", mode: "php", ext: ["php", "php3", "php4", "php5", "phtml"]},
+ {name: "Pig", mime: "text/x-pig", mode: "pig", ext: ["pig"]},
+ {name: "Plain Text", mime: "text/plain", mode: "null", ext: ["txt", "text", "conf", "def", "list", "log"]},
+ {name: "PLSQL", mime: "text/x-plsql", mode: "sql", ext: ["pls"]},
+ {name: "Properties files", mime: "text/x-properties", mode: "properties", ext: ["properties", "ini", "in"], alias: ["ini", "properties"]},
+ {name: "Python", mime: "text/x-python", mode: "python", ext: ["py", "pyw"]},
+ {name: "Puppet", mime: "text/x-puppet", mode: "puppet", ext: ["pp"]},
+ {name: "Q", mime: "text/x-q", mode: "q", ext: ["q"]},
+ {name: "R", mime: "text/x-rsrc", mode: "r", ext: ["r"], alias: ["rscript"]},
+ {name: "reStructuredText", mime: "text/x-rst", mode: "rst", ext: ["rst"], alias: ["rst"]},
+ {name: "RPM Changes", mime: "text/x-rpm-changes", mode: "rpm"},
+ {name: "RPM Spec", mime: "text/x-rpm-spec", mode: "rpm", ext: ["spec"]},
+ {name: "Ruby", mime: "text/x-ruby", mode: "ruby", ext: ["rb"], alias: ["jruby", "macruby", "rake", "rb", "rbx"]},
+ {name: "Rust", mime: "text/x-rustsrc", mode: "rust", ext: ["rs"]},
+ {name: "Sass", mime: "text/x-sass", mode: "sass", ext: ["sass"]},
+ {name: "Scala", mime: "text/x-scala", mode: "clike", ext: ["scala"]},
+ {name: "Scheme", mime: "text/x-scheme", mode: "scheme", ext: ["scm", "ss"]},
+ {name: "SCSS", mime: "text/x-scss", mode: "css", ext: ["scss"]},
+ {name: "Shell", mime: "text/x-sh", mode: "shell", ext: ["sh", "ksh", "bash"], alias: ["bash", "sh", "zsh"]},
+ {name: "Sieve", mime: "application/sieve", mode: "sieve", ext: ["siv", "sieve"]},
+ {name: "Slim", mimes: ["text/x-slim", "application/x-slim"], mode: "slim", ext: ["slim"]},
+ {name: "Smalltalk", mime: "text/x-stsrc", mode: "smalltalk", ext: ["st"]},
+ {name: "Smarty", mime: "text/x-smarty", mode: "smarty", ext: ["tpl"]},
+ {name: "Solr", mime: "text/x-solr", mode: "solr"},
+ {name: "Soy", mime: "text/x-soy", mode: "soy", ext: ["soy"], alias: ["closure template"]},
+ {name: "SPARQL", mime: "application/sparql-query", mode: "sparql", ext: ["rq", "sparql"], alias: ["sparul"]},
+ {name: "Spreadsheet", mime: "text/x-spreadsheet", mode: "spreadsheet", alias: ["excel", "formula"]},
+ {name: "SQL", mime: "text/x-sql", mode: "sql", ext: ["sql"]},
+ {name: "Squirrel", mime: "text/x-squirrel", mode: "clike", ext: ["nut"]},
+ {name: "Swift", mime: "text/x-swift", mode: "swift", ext: ["swift"]},
+ {name: "MariaDB", mime: "text/x-mariadb", mode: "sql"},
+ {name: "sTeX", mime: "text/x-stex", mode: "stex"},
+ {name: "LaTeX", mime: "text/x-latex", mode: "stex", ext: ["text", "ltx"], alias: ["tex"]},
+ {name: "SystemVerilog", mime: "text/x-systemverilog", mode: "verilog", ext: ["v"]},
+ {name: "Tcl", mime: "text/x-tcl", mode: "tcl", ext: ["tcl"]},
+ {name: "Textile", mime: "text/x-textile", mode: "textile", ext: ["textile"]},
+ {name: "TiddlyWiki ", mime: "text/x-tiddlywiki", mode: "tiddlywiki"},
+ {name: "Tiki wiki", mime: "text/tiki", mode: "tiki"},
+ {name: "TOML", mime: "text/x-toml", mode: "toml", ext: ["toml"]},
+ {name: "Tornado", mime: "text/x-tornado", mode: "tornado"},
+ {name: "troff", mime: "troff", mode: "troff", ext: ["1", "2", "3", "4", "5", "6", "7", "8", "9"]},
+ {name: "TTCN", mime: "text/x-ttcn", mode: "ttcn", ext: ["ttcn", "ttcn3", "ttcnpp"]},
+ {name: "TTCN_CFG", mime: "text/x-ttcn-cfg", mode: "ttcn-cfg", ext: ["cfg"]},
+ {name: "Turtle", mime: "text/turtle", mode: "turtle", ext: ["ttl"]},
+ {name: "TypeScript", mime: "application/typescript", mode: "javascript", ext: ["ts"], alias: ["ts"]},
+ {name: "Twig", mime: "text/x-twig", mode: "twig"},
+ {name: "VB.NET", mime: "text/x-vb", mode: "vb", ext: ["vb"]},
+ {name: "VBScript", mime: "text/vbscript", mode: "vbscript", ext: ["vbs"]},
+ {name: "Velocity", mime: "text/velocity", mode: "velocity", ext: ["vtl"]},
+ {name: "Verilog", mime: "text/x-verilog", mode: "verilog", ext: ["v"]},
+ {name: "VHDL", mime: "text/x-vhdl", mode: "vhdl", ext: ["vhd", "vhdl"]},
+ {name: "XML", mimes: ["application/xml", "text/xml"], mode: "xml", ext: ["xml", "xsl", "xsd"], alias: ["rss", "wsdl", "xsd"]},
+ {name: "XQuery", mime: "application/xquery", mode: "xquery", ext: ["xy", "xquery"]},
+ {name: "YAML", mime: "text/x-yaml", mode: "yaml", ext: ["yaml", "yml"], alias: ["yml"]},
+ {name: "Z80", mime: "text/x-z80", mode: "z80", ext: ["z80"]},
+ {name: "mscgen", mime: "text/x-mscgen", mode: "mscgen", ext: ["mscgen", "mscin", "msc"]},
+ {name: "xu", mime: "text/x-xu", mode: "mscgen", ext: ["xu"]},
+ {name: "msgenny", mime: "text/x-msgenny", mode: "mscgen", ext: ["msgenny"]}
+ ];
+ // Ensure all modes have a mime property for backwards compatibility
+ for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
+ var info = CodeMirror.modeInfo[i];
+ if (info.mimes) info.mime = info.mimes[0];
+ }
+
+ CodeMirror.findModeByMIME = function(mime) {
+ mime = mime.toLowerCase();
+ for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
+ var info = CodeMirror.modeInfo[i];
+ if (info.mime == mime) return info;
+ if (info.mimes) for (var j = 0; j < info.mimes.length; j++)
+ if (info.mimes[j] == mime) return info;
+ }
+ };
+
+ CodeMirror.findModeByExtension = function(ext) {
+ for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
+ var info = CodeMirror.modeInfo[i];
+ if (info.ext) for (var j = 0; j < info.ext.length; j++)
+ if (info.ext[j] == ext) return info;
+ }
+ };
+
+ CodeMirror.findModeByFileName = function(filename) {
+ for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
+ var info = CodeMirror.modeInfo[i];
+ if (info.file && info.file.test(filename)) return info;
+ }
+ var dot = filename.lastIndexOf(".");
+ var ext = dot > -1 && filename.substring(dot + 1, filename.length);
+ if (ext) return CodeMirror.findModeByExtension(ext);
+ };
+
+ CodeMirror.findModeByName = function(name) {
+ name = name.toLowerCase();
+ for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
+ var info = CodeMirror.modeInfo[i];
+ if (info.name.toLowerCase() == name) return info;
+ if (info.alias) for (var j = 0; j < info.alias.length; j++)
+ if (info.alias[j].toLowerCase() == name) return info;
+ }
+ };
+});
+
+},{"../lib/codemirror":6}],10:[function(require,module,exports){
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://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("xml", function(config, parserConfig) {
+ var indentUnit = config.indentUnit;
+ var multilineTagIndentFactor = parserConfig.multilineTagIndentFactor || 1;
+ var multilineTagIndentPastTag = parserConfig.multilineTagIndentPastTag;
+ if (multilineTagIndentPastTag == null) multilineTagIndentPastTag = true;
+
+ var Kludges = parserConfig.htmlMode ? {
+ 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
+ } : {
+ autoSelfClosers: {},
+ implicitlyClosed: {},
+ contextGrabbers: {},
+ doNotIndent: {},
+ allowUnquoted: false,
+ allowMissing: false,
+ caseFold: false
+ };
+ var alignCDATA = parserConfig.alignCDATA;
+
+ // 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 (Kludges.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 (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
+ !Kludges.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 {
+ setStyle = "error";
+ return tagNameState;
+ }
+ }
+ function closeTagNameState(type, stream, state) {
+ if (type == "word") {
+ var tagName = stream.current();
+ if (state.context && state.context.tagName != tagName &&
+ Kludges.implicitlyClosed.hasOwnProperty(state.context.tagName))
+ popContext(state);
+ if (state.context && state.context.tagName == tagName) {
+ setStyle = "tag";
+ return closeState;
+ } else {
+ setStyle = "tag error";
+ return closeStateErr;
+ }
+ } 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" ||
+ Kludges.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 (!Kludges.allowMissing) setStyle = "error";
+ return attrState(type, stream, state);
+ }
+ function attrValueState(type, stream, state) {
+ if (type == "string") return attrContinuedState;
+ if (type == "word" && Kludges.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() {
+ return {tokenize: inText,
+ state: baseState,
+ indented: 0,
+ tagName: null, tagStart: null,
+ context: null};
+ },
+
+ 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 (multilineTagIndentPastTag)
+ return state.tagStart + state.tagName.length + 2;
+ else
+ return state.tagStart + indentUnit * multilineTagIndentFactor;
+ }
+ if (alignCDATA && /$/,
+ blockCommentStart: "",
+
+ configuration: parserConfig.htmlMode ? "html" : "xml",
+ helperType: parserConfig.htmlMode ? "html" : "xml"
+ };
+});
+
+CodeMirror.defineMIME("text/xml", "xml");
+CodeMirror.defineMIME("application/xml", "xml");
+if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
+ CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
+
+});
+
+},{"../../lib/codemirror":6}],11:[function(require,module,exports){
+(function (global){
+/**
+ * marked - a markdown parser
+ * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
+ * https://github.com/chjj/marked
+ */
+
+;(function() {
+
+/**
+ * Block-Level Grammar
+ */
+
+var block = {
+ newline: /^\n+/,
+ code: /^( {4}[^\n]+\n*)+/,
+ fences: noop,
+ hr: /^( *[-*_]){3,} *(?:\n+|$)/,
+ heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
+ nptable: noop,
+ lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
+ blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
+ list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
+ html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
+ def: /^ *\[([^\]]+)\]: *([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
+ table: noop,
+ paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
+ text: /^[^\n]+/
+};
+
+block.bullet = /(?:[*+-]|\d+\.)/;
+block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
+block.item = replace(block.item, 'gm')
+ (/bull/g, block.bullet)
+ ();
+
+block.list = replace(block.list)
+ (/bull/g, block.bullet)
+ ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
+ ('def', '\\n+(?=' + block.def.source + ')')
+ ();
+
+block.blockquote = replace(block.blockquote)
+ ('def', block.def)
+ ();
+
+block._tag = '(?!(?:'
+ + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
+ + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
+ + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
+
+block.html = replace(block.html)
+ ('comment', //)
+ ('closed', /<(tag)[\s\S]+?<\/\1>/)
+ ('closing', /])*?>/)
+ (/tag/g, block._tag)
+ ();
+
+block.paragraph = replace(block.paragraph)
+ ('hr', block.hr)
+ ('heading', block.heading)
+ ('lheading', block.lheading)
+ ('blockquote', block.blockquote)
+ ('tag', '<' + block._tag)
+ ('def', block.def)
+ ();
+
+/**
+ * Normal Block Grammar
+ */
+
+block.normal = merge({}, block);
+
+/**
+ * GFM Block Grammar
+ */
+
+block.gfm = merge({}, block.normal, {
+ fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
+ paragraph: /^/,
+ heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
+});
+
+block.gfm.paragraph = replace(block.paragraph)
+ ('(?!', '(?!'
+ + block.gfm.fences.source.replace('\\1', '\\2') + '|'
+ + block.list.source.replace('\\1', '\\3') + '|')
+ ();
+
+/**
+ * GFM + Tables Block Grammar
+ */
+
+block.tables = merge({}, block.gfm, {
+ nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
+ table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
+});
+
+/**
+ * Block Lexer
+ */
+
+function Lexer(options) {
+ this.tokens = [];
+ this.tokens.links = {};
+ this.options = options || marked.defaults;
+ this.rules = block.normal;
+
+ if (this.options.gfm) {
+ if (this.options.tables) {
+ this.rules = block.tables;
+ } else {
+ this.rules = block.gfm;
+ }
+ }
+}
+
+/**
+ * Expose Block Rules
+ */
+
+Lexer.rules = block;
+
+/**
+ * Static Lex Method
+ */
+
+Lexer.lex = function(src, options) {
+ var lexer = new Lexer(options);
+ return lexer.lex(src);
+};
+
+/**
+ * Preprocessing
+ */
+
+Lexer.prototype.lex = function(src) {
+ src = src
+ .replace(/\r\n|\r/g, '\n')
+ .replace(/\t/g, ' ')
+ .replace(/\u00a0/g, ' ')
+ .replace(/\u2424/g, '\n');
+
+ return this.token(src, true);
+};
+
+/**
+ * Lexing
+ */
+
+Lexer.prototype.token = function(src, top, bq) {
+ var src = src.replace(/^ +$/gm, '')
+ , next
+ , loose
+ , cap
+ , bull
+ , b
+ , item
+ , space
+ , i
+ , l;
+
+ while (src) {
+ // newline
+ if (cap = this.rules.newline.exec(src)) {
+ src = src.substring(cap[0].length);
+ if (cap[0].length > 1) {
+ this.tokens.push({
+ type: 'space'
+ });
+ }
+ }
+
+ // code
+ if (cap = this.rules.code.exec(src)) {
+ src = src.substring(cap[0].length);
+ cap = cap[0].replace(/^ {4}/gm, '');
+ this.tokens.push({
+ type: 'code',
+ text: !this.options.pedantic
+ ? cap.replace(/\n+$/, '')
+ : cap
+ });
+ continue;
+ }
+
+ // fences (gfm)
+ if (cap = this.rules.fences.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'code',
+ lang: cap[2],
+ text: cap[3] || ''
+ });
+ continue;
+ }
+
+ // heading
+ if (cap = this.rules.heading.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'heading',
+ depth: cap[1].length,
+ text: cap[2]
+ });
+ continue;
+ }
+
+ // table no leading pipe (gfm)
+ if (top && (cap = this.rules.nptable.exec(src))) {
+ src = src.substring(cap[0].length);
+
+ item = {
+ type: 'table',
+ header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
+ align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
+ cells: cap[3].replace(/\n$/, '').split('\n')
+ };
+
+ for (i = 0; i < item.align.length; i++) {
+ if (/^ *-+: *$/.test(item.align[i])) {
+ item.align[i] = 'right';
+ } else if (/^ *:-+: *$/.test(item.align[i])) {
+ item.align[i] = 'center';
+ } else if (/^ *:-+ *$/.test(item.align[i])) {
+ item.align[i] = 'left';
+ } else {
+ item.align[i] = null;
+ }
+ }
+
+ for (i = 0; i < item.cells.length; i++) {
+ item.cells[i] = item.cells[i].split(/ *\| */);
+ }
+
+ this.tokens.push(item);
+
+ continue;
+ }
+
+ // lheading
+ if (cap = this.rules.lheading.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'heading',
+ depth: cap[2] === '=' ? 1 : 2,
+ text: cap[1]
+ });
+ continue;
+ }
+
+ // hr
+ if (cap = this.rules.hr.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'hr'
+ });
+ continue;
+ }
+
+ // blockquote
+ if (cap = this.rules.blockquote.exec(src)) {
+ src = src.substring(cap[0].length);
+
+ this.tokens.push({
+ type: 'blockquote_start'
+ });
+
+ cap = cap[0].replace(/^ *> ?/gm, '');
+
+ // Pass `top` to keep the current
+ // "toplevel" state. This is exactly
+ // how markdown.pl works.
+ this.token(cap, top, true);
+
+ this.tokens.push({
+ type: 'blockquote_end'
+ });
+
+ continue;
+ }
+
+ // list
+ if (cap = this.rules.list.exec(src)) {
+ src = src.substring(cap[0].length);
+ bull = cap[2];
+
+ this.tokens.push({
+ type: 'list_start',
+ ordered: bull.length > 1
+ });
+
+ // Get each top-level item.
+ cap = cap[0].match(this.rules.item);
+
+ next = false;
+ l = cap.length;
+ i = 0;
+
+ for (; i < l; i++) {
+ item = cap[i];
+
+ // Remove the list item's bullet
+ // so it is seen as the next token.
+ space = item.length;
+ item = item.replace(/^ *([*+-]|\d+\.) +/, '');
+
+ // Outdent whatever the
+ // list item contains. Hacky.
+ if (~item.indexOf('\n ')) {
+ space -= item.length;
+ item = !this.options.pedantic
+ ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
+ : item.replace(/^ {1,4}/gm, '');
+ }
+
+ // Determine whether the next list item belongs here.
+ // Backpedal if it does not belong in this list.
+ if (this.options.smartLists && i !== l - 1) {
+ b = block.bullet.exec(cap[i + 1])[0];
+ if (bull !== b && !(bull.length > 1 && b.length > 1)) {
+ src = cap.slice(i + 1).join('\n') + src;
+ i = l - 1;
+ }
+ }
+
+ // Determine whether item is loose or not.
+ // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
+ // for discount behavior.
+ loose = next || /\n\n(?!\s*$)/.test(item);
+ if (i !== l - 1) {
+ next = item.charAt(item.length - 1) === '\n';
+ if (!loose) loose = next;
+ }
+
+ this.tokens.push({
+ type: loose
+ ? 'loose_item_start'
+ : 'list_item_start'
+ });
+
+ // Recurse.
+ this.token(item, false, bq);
+
+ this.tokens.push({
+ type: 'list_item_end'
+ });
+ }
+
+ this.tokens.push({
+ type: 'list_end'
+ });
+
+ continue;
+ }
+
+ // html
+ if (cap = this.rules.html.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: this.options.sanitize
+ ? 'paragraph'
+ : 'html',
+ pre: !this.options.sanitizer
+ && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
+ text: cap[0]
+ });
+ continue;
+ }
+
+ // def
+ if ((!bq && top) && (cap = this.rules.def.exec(src))) {
+ src = src.substring(cap[0].length);
+ this.tokens.links[cap[1].toLowerCase()] = {
+ href: cap[2],
+ title: cap[3]
+ };
+ continue;
+ }
+
+ // table (gfm)
+ if (top && (cap = this.rules.table.exec(src))) {
+ src = src.substring(cap[0].length);
+
+ item = {
+ type: 'table',
+ header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
+ align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
+ cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
+ };
+
+ for (i = 0; i < item.align.length; i++) {
+ if (/^ *-+: *$/.test(item.align[i])) {
+ item.align[i] = 'right';
+ } else if (/^ *:-+: *$/.test(item.align[i])) {
+ item.align[i] = 'center';
+ } else if (/^ *:-+ *$/.test(item.align[i])) {
+ item.align[i] = 'left';
+ } else {
+ item.align[i] = null;
+ }
+ }
+
+ for (i = 0; i < item.cells.length; i++) {
+ item.cells[i] = item.cells[i]
+ .replace(/^ *\| *| *\| *$/g, '')
+ .split(/ *\| */);
+ }
+
+ this.tokens.push(item);
+
+ continue;
+ }
+
+ // top-level paragraph
+ if (top && (cap = this.rules.paragraph.exec(src))) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'paragraph',
+ text: cap[1].charAt(cap[1].length - 1) === '\n'
+ ? cap[1].slice(0, -1)
+ : cap[1]
+ });
+ continue;
+ }
+
+ // text
+ if (cap = this.rules.text.exec(src)) {
+ // Top-level should never reach here.
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'text',
+ text: cap[0]
+ });
+ continue;
+ }
+
+ if (src) {
+ throw new
+ Error('Infinite loop on byte: ' + src.charCodeAt(0));
+ }
+ }
+
+ return this.tokens;
+};
+
+/**
+ * Inline-Level Grammar
+ */
+
+var inline = {
+ escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
+ autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
+ url: noop,
+ tag: /^|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
+ link: /^!?\[(inside)\]\(href\)/,
+ reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
+ nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
+ strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
+ em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
+ code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
+ br: /^ {2,}\n(?!\s*$)/,
+ del: noop,
+ text: /^[\s\S]+?(?=[\\?(?:\s+['"]([\s\S]*?)['"])?\s*/;
+
+inline.link = replace(inline.link)
+ ('inside', inline._inside)
+ ('href', inline._href)
+ ();
+
+inline.reflink = replace(inline.reflink)
+ ('inside', inline._inside)
+ ();
+
+/**
+ * Normal Inline Grammar
+ */
+
+inline.normal = merge({}, inline);
+
+/**
+ * Pedantic Inline Grammar
+ */
+
+inline.pedantic = merge({}, inline.normal, {
+ strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
+ em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
+});
+
+/**
+ * GFM Inline Grammar
+ */
+
+inline.gfm = merge({}, inline.normal, {
+ escape: replace(inline.escape)('])', '~|])')(),
+ url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
+ del: /^~~(?=\S)([\s\S]*?\S)~~/,
+ text: replace(inline.text)
+ (']|', '~]|')
+ ('|', '|https?://|')
+ ()
+});
+
+/**
+ * GFM + Line Breaks Inline Grammar
+ */
+
+inline.breaks = merge({}, inline.gfm, {
+ br: replace(inline.br)('{2,}', '*')(),
+ text: replace(inline.gfm.text)('{2,}', '*')()
+});
+
+/**
+ * Inline Lexer & Compiler
+ */
+
+function InlineLexer(links, options) {
+ this.options = options || marked.defaults;
+ this.links = links;
+ this.rules = inline.normal;
+ this.renderer = this.options.renderer || new Renderer;
+ this.renderer.options = this.options;
+
+ if (!this.links) {
+ throw new
+ Error('Tokens array requires a `links` property.');
+ }
+
+ if (this.options.gfm) {
+ if (this.options.breaks) {
+ this.rules = inline.breaks;
+ } else {
+ this.rules = inline.gfm;
+ }
+ } else if (this.options.pedantic) {
+ this.rules = inline.pedantic;
+ }
+}
+
+/**
+ * Expose Inline Rules
+ */
+
+InlineLexer.rules = inline;
+
+/**
+ * Static Lexing/Compiling Method
+ */
+
+InlineLexer.output = function(src, links, options) {
+ var inline = new InlineLexer(links, options);
+ return inline.output(src);
+};
+
+/**
+ * Lexing/Compiling
+ */
+
+InlineLexer.prototype.output = function(src) {
+ var out = ''
+ , link
+ , text
+ , href
+ , cap;
+
+ while (src) {
+ // escape
+ if (cap = this.rules.escape.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += cap[1];
+ continue;
+ }
+
+ // autolink
+ if (cap = this.rules.autolink.exec(src)) {
+ src = src.substring(cap[0].length);
+ if (cap[2] === '@') {
+ text = cap[1].charAt(6) === ':'
+ ? this.mangle(cap[1].substring(7))
+ : this.mangle(cap[1]);
+ href = this.mangle('mailto:') + text;
+ } else {
+ text = escape(cap[1]);
+ href = text;
+ }
+ out += this.renderer.link(href, null, text);
+ continue;
+ }
+
+ // url (gfm)
+ if (!this.inLink && (cap = this.rules.url.exec(src))) {
+ src = src.substring(cap[0].length);
+ text = escape(cap[1]);
+ href = text;
+ out += this.renderer.link(href, null, text);
+ continue;
+ }
+
+ // tag
+ if (cap = this.rules.tag.exec(src)) {
+ if (!this.inLink && /^/i.test(cap[0])) {
+ this.inLink = false;
+ }
+ src = src.substring(cap[0].length);
+ out += this.options.sanitize
+ ? this.options.sanitizer
+ ? this.options.sanitizer(cap[0])
+ : escape(cap[0])
+ : cap[0]
+ continue;
+ }
+
+ // link
+ if (cap = this.rules.link.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.inLink = true;
+ out += this.outputLink(cap, {
+ href: cap[2],
+ title: cap[3]
+ });
+ this.inLink = false;
+ continue;
+ }
+
+ // reflink, nolink
+ if ((cap = this.rules.reflink.exec(src))
+ || (cap = this.rules.nolink.exec(src))) {
+ src = src.substring(cap[0].length);
+ link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
+ link = this.links[link.toLowerCase()];
+ if (!link || !link.href) {
+ out += cap[0].charAt(0);
+ src = cap[0].substring(1) + src;
+ continue;
+ }
+ this.inLink = true;
+ out += this.outputLink(cap, link);
+ this.inLink = false;
+ continue;
+ }
+
+ // strong
+ if (cap = this.rules.strong.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.strong(this.output(cap[2] || cap[1]));
+ continue;
+ }
+
+ // em
+ if (cap = this.rules.em.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.em(this.output(cap[2] || cap[1]));
+ continue;
+ }
+
+ // code
+ if (cap = this.rules.code.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.codespan(escape(cap[2], true));
+ continue;
+ }
+
+ // br
+ if (cap = this.rules.br.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.br();
+ continue;
+ }
+
+ // del (gfm)
+ if (cap = this.rules.del.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.del(this.output(cap[1]));
+ continue;
+ }
+
+ // text
+ if (cap = this.rules.text.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.text(escape(this.smartypants(cap[0])));
+ continue;
+ }
+
+ if (src) {
+ throw new
+ Error('Infinite loop on byte: ' + src.charCodeAt(0));
+ }
+ }
+
+ return out;
+};
+
+/**
+ * Compile Link
+ */
+
+InlineLexer.prototype.outputLink = function(cap, link) {
+ var href = escape(link.href)
+ , title = link.title ? escape(link.title) : null;
+
+ return cap[0].charAt(0) !== '!'
+ ? this.renderer.link(href, title, this.output(cap[1]))
+ : this.renderer.image(href, title, escape(cap[1]));
+};
+
+/**
+ * Smartypants Transformations
+ */
+
+InlineLexer.prototype.smartypants = function(text) {
+ if (!this.options.smartypants) return text;
+ return text
+ // em-dashes
+ .replace(/---/g, '\u2014')
+ // en-dashes
+ .replace(/--/g, '\u2013')
+ // opening singles
+ .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
+ // closing singles & apostrophes
+ .replace(/'/g, '\u2019')
+ // opening doubles
+ .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
+ // closing doubles
+ .replace(/"/g, '\u201d')
+ // ellipses
+ .replace(/\.{3}/g, '\u2026');
+};
+
+/**
+ * Mangle Links
+ */
+
+InlineLexer.prototype.mangle = function(text) {
+ if (!this.options.mangle) return text;
+ var out = ''
+ , l = text.length
+ , i = 0
+ , ch;
+
+ for (; i < l; i++) {
+ ch = text.charCodeAt(i);
+ if (Math.random() > 0.5) {
+ ch = 'x' + ch.toString(16);
+ }
+ out += '' + ch + ';';
+ }
+
+ return out;
+};
+
+/**
+ * Renderer
+ */
+
+function Renderer(options) {
+ this.options = options || {};
+}
+
+Renderer.prototype.code = function(code, lang, escaped) {
+ if (this.options.highlight) {
+ var out = this.options.highlight(code, lang);
+ if (out != null && out !== code) {
+ escaped = true;
+ code = out;
+ }
+ }
+
+ if (!lang) {
+ return ''
+ + (escaped ? code : escape(code, true))
+ + '\n
';
+ }
+
+ return ''
+ + (escaped ? code : escape(code, true))
+ + '\n
\n';
+};
+
+Renderer.prototype.blockquote = function(quote) {
+ return '\n' + quote + '
\n';
+};
+
+Renderer.prototype.html = function(html) {
+ return html;
+};
+
+Renderer.prototype.heading = function(text, level, raw) {
+ return '\n';
+};
+
+Renderer.prototype.hr = function() {
+ return this.options.xhtml ? '
\n' : '
\n';
+};
+
+Renderer.prototype.list = function(body, ordered) {
+ var type = ordered ? 'ol' : 'ul';
+ return '<' + type + '>\n' + body + '' + type + '>\n';
+};
+
+Renderer.prototype.listitem = function(text) {
+ return '' + text + '\n';
+};
+
+Renderer.prototype.paragraph = function(text) {
+ return '' + text + '
\n';
+};
+
+Renderer.prototype.table = function(header, body) {
+ return '\n'
+ + '\n'
+ + header
+ + '\n'
+ + '\n'
+ + body
+ + '\n'
+ + '
\n';
+};
+
+Renderer.prototype.tablerow = function(content) {
+ return '\n' + content + '
\n';
+};
+
+Renderer.prototype.tablecell = function(content, flags) {
+ var type = flags.header ? 'th' : 'td';
+ var tag = flags.align
+ ? '<' + type + ' style="text-align:' + flags.align + '">'
+ : '<' + type + '>';
+ return tag + content + '' + type + '>\n';
+};
+
+// span level renderer
+Renderer.prototype.strong = function(text) {
+ return '' + text + '';
+};
+
+Renderer.prototype.em = function(text) {
+ return '' + text + '';
+};
+
+Renderer.prototype.codespan = function(text) {
+ return '' + text + '
';
+};
+
+Renderer.prototype.br = function() {
+ return this.options.xhtml ? '
' : '
';
+};
+
+Renderer.prototype.del = function(text) {
+ return '' + text + '';
+};
+
+Renderer.prototype.link = function(href, title, text) {
+ if (this.options.sanitize) {
+ try {
+ var prot = decodeURIComponent(unescape(href))
+ .replace(/[^\w:]/g, '')
+ .toLowerCase();
+ } catch (e) {
+ return '';
+ }
+ if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
+ return '';
+ }
+ }
+ var out = '' + text + '';
+ return out;
+};
+
+Renderer.prototype.image = function(href, title, text) {
+ var out = '' : '>';
+ return out;
+};
+
+Renderer.prototype.text = function(text) {
+ return text;
+};
+
+/**
+ * Parsing & Compiling
+ */
+
+function Parser(options) {
+ this.tokens = [];
+ this.token = null;
+ this.options = options || marked.defaults;
+ this.options.renderer = this.options.renderer || new Renderer;
+ this.renderer = this.options.renderer;
+ this.renderer.options = this.options;
+}
+
+/**
+ * Static Parse Method
+ */
+
+Parser.parse = function(src, options, renderer) {
+ var parser = new Parser(options, renderer);
+ return parser.parse(src);
+};
+
+/**
+ * Parse Loop
+ */
+
+Parser.prototype.parse = function(src) {
+ this.inline = new InlineLexer(src.links, this.options, this.renderer);
+ this.tokens = src.reverse();
+
+ var out = '';
+ while (this.next()) {
+ out += this.tok();
+ }
+
+ return out;
+};
+
+/**
+ * Next Token
+ */
+
+Parser.prototype.next = function() {
+ return this.token = this.tokens.pop();
+};
+
+/**
+ * Preview Next Token
+ */
+
+Parser.prototype.peek = function() {
+ return this.tokens[this.tokens.length - 1] || 0;
+};
+
+/**
+ * Parse Text Tokens
+ */
+
+Parser.prototype.parseText = function() {
+ var body = this.token.text;
+
+ while (this.peek().type === 'text') {
+ body += '\n' + this.next().text;
+ }
+
+ return this.inline.output(body);
+};
+
+/**
+ * Parse Current Token
+ */
+
+Parser.prototype.tok = function() {
+ switch (this.token.type) {
+ case 'space': {
+ return '';
+ }
+ case 'hr': {
+ return this.renderer.hr();
+ }
+ case 'heading': {
+ return this.renderer.heading(
+ this.inline.output(this.token.text),
+ this.token.depth,
+ this.token.text);
+ }
+ case 'code': {
+ return this.renderer.code(this.token.text,
+ this.token.lang,
+ this.token.escaped);
+ }
+ case 'table': {
+ var header = ''
+ , body = ''
+ , i
+ , row
+ , cell
+ , flags
+ , j;
+
+ // header
+ cell = '';
+ for (i = 0; i < this.token.header.length; i++) {
+ flags = { header: true, align: this.token.align[i] };
+ cell += this.renderer.tablecell(
+ this.inline.output(this.token.header[i]),
+ { header: true, align: this.token.align[i] }
+ );
+ }
+ header += this.renderer.tablerow(cell);
+
+ for (i = 0; i < this.token.cells.length; i++) {
+ row = this.token.cells[i];
+
+ cell = '';
+ for (j = 0; j < row.length; j++) {
+ cell += this.renderer.tablecell(
+ this.inline.output(row[j]),
+ { header: false, align: this.token.align[j] }
+ );
+ }
+
+ body += this.renderer.tablerow(cell);
+ }
+ return this.renderer.table(header, body);
+ }
+ case 'blockquote_start': {
+ var body = '';
+
+ while (this.next().type !== 'blockquote_end') {
+ body += this.tok();
+ }
+
+ return this.renderer.blockquote(body);
+ }
+ case 'list_start': {
+ var body = ''
+ , ordered = this.token.ordered;
+
+ while (this.next().type !== 'list_end') {
+ body += this.tok();
+ }
+
+ return this.renderer.list(body, ordered);
+ }
+ case 'list_item_start': {
+ var body = '';
+
+ while (this.next().type !== 'list_item_end') {
+ body += this.token.type === 'text'
+ ? this.parseText()
+ : this.tok();
+ }
+
+ return this.renderer.listitem(body);
+ }
+ case 'loose_item_start': {
+ var body = '';
+
+ while (this.next().type !== 'list_item_end') {
+ body += this.tok();
+ }
+
+ return this.renderer.listitem(body);
+ }
+ case 'html': {
+ var html = !this.token.pre && !this.options.pedantic
+ ? this.inline.output(this.token.text)
+ : this.token.text;
+ return this.renderer.html(html);
+ }
+ case 'paragraph': {
+ return this.renderer.paragraph(this.inline.output(this.token.text));
+ }
+ case 'text': {
+ return this.renderer.paragraph(this.parseText());
+ }
+ }
+};
+
+/**
+ * Helpers
+ */
+
+function escape(html, encode) {
+ return html
+ .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+}
+
+function unescape(html) {
+ return html.replace(/&([#\w]+);/g, function(_, n) {
+ n = n.toLowerCase();
+ if (n === 'colon') return ':';
+ if (n.charAt(0) === '#') {
+ return n.charAt(1) === 'x'
+ ? String.fromCharCode(parseInt(n.substring(2), 16))
+ : String.fromCharCode(+n.substring(1));
+ }
+ return '';
+ });
+}
+
+function replace(regex, opt) {
+ regex = regex.source;
+ opt = opt || '';
+ return function self(name, val) {
+ if (!name) return new RegExp(regex, opt);
+ val = val.source || val;
+ val = val.replace(/(^|[^\[])\^/g, '$1');
+ regex = regex.replace(name, val);
+ return self;
+ };
+}
+
+function noop() {}
+noop.exec = noop;
+
+function merge(obj) {
+ var i = 1
+ , target
+ , key;
+
+ for (; i < arguments.length; i++) {
+ target = arguments[i];
+ for (key in target) {
+ if (Object.prototype.hasOwnProperty.call(target, key)) {
+ obj[key] = target[key];
+ }
+ }
+ }
+
+ return obj;
+}
+
+
+/**
+ * Marked
+ */
+
+function marked(src, opt, callback) {
+ if (callback || typeof opt === 'function') {
+ if (!callback) {
+ callback = opt;
+ opt = null;
+ }
+
+ opt = merge({}, marked.defaults, opt || {});
+
+ var highlight = opt.highlight
+ , tokens
+ , pending
+ , i = 0;
+
+ try {
+ tokens = Lexer.lex(src, opt)
+ } catch (e) {
+ return callback(e);
+ }
+
+ pending = tokens.length;
+
+ var done = function(err) {
+ if (err) {
+ opt.highlight = highlight;
+ return callback(err);
+ }
+
+ var out;
+
+ try {
+ out = Parser.parse(tokens, opt);
+ } catch (e) {
+ err = e;
+ }
+
+ opt.highlight = highlight;
+
+ return err
+ ? callback(err)
+ : callback(null, out);
+ };
+
+ if (!highlight || highlight.length < 3) {
+ return done();
+ }
+
+ delete opt.highlight;
+
+ if (!pending) return done();
+
+ for (; i < tokens.length; i++) {
+ (function(token) {
+ if (token.type !== 'code') {
+ return --pending || done();
+ }
+ return highlight(token.text, token.lang, function(err, code) {
+ if (err) return done(err);
+ if (code == null || code === token.text) {
+ return --pending || done();
+ }
+ token.text = code;
+ token.escaped = true;
+ --pending || done();
+ });
+ })(tokens[i]);
+ }
+
+ return;
+ }
+ try {
+ if (opt) opt = merge({}, marked.defaults, opt);
+ return Parser.parse(Lexer.lex(src, opt), opt);
+ } catch (e) {
+ e.message += '\nPlease report this to https://github.com/chjj/marked.';
+ if ((opt || marked.defaults).silent) {
+ return 'An error occured:
'
+ + escape(e.message + '', true)
+ + '
';
+ }
+ throw e;
+ }
+}
+
+/**
+ * Options
+ */
+
+marked.options =
+marked.setOptions = function(opt) {
+ merge(marked.defaults, opt);
+ return marked;
+};
+
+marked.defaults = {
+ gfm: true,
+ tables: true,
+ breaks: false,
+ pedantic: false,
+ sanitize: false,
+ sanitizer: null,
+ mangle: true,
+ smartLists: false,
+ silent: false,
+ highlight: null,
+ langPrefix: 'lang-',
+ smartypants: false,
+ headerPrefix: '',
+ renderer: new Renderer,
+ xhtml: false
+};
+
+/**
+ * Expose
+ */
+
+marked.Parser = Parser;
+marked.parser = Parser.parse;
+
+marked.Renderer = Renderer;
+
+marked.Lexer = Lexer;
+marked.lexer = Lexer.lex;
+
+marked.InlineLexer = InlineLexer;
+marked.inlineLexer = InlineLexer.output;
+
+marked.parse = marked;
+
+if (typeof module !== 'undefined' && typeof exports === 'object') {
+ module.exports = marked;
+} else if (typeof define === 'function' && define.amd) {
+ define(function() { return marked; });
+} else {
+ this.marked = marked;
+}
+
+}).call(function() {
+ return this || (typeof window !== 'undefined' ? window : global);
+}());
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+
+},{}],12:[function(require,module,exports){
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+var CodeMirror = require("codemirror");
+
+CodeMirror.commands.tabAndIndentMarkdownList = function (cm) {
+ var ranges = cm.listSelections();
+ var pos = ranges[0].head;
+ var eolState = cm.getStateAfter(pos.line);
+ var inList = eolState.list !== false;
+
+ if (inList) {
+ cm.execCommand("indentMore");
+ return;
+ }
+
+ if (cm.options.indentWithTabs) {
+ cm.execCommand("insertTab");
+ }
+ else {
+ var spaces = Array(cm.options.tabSize + 1).join(" ");
+ cm.replaceSelection(spaces);
+ }
+};
+
+CodeMirror.commands.shiftTabAndUnindentMarkdownList = function (cm) {
+ var ranges = cm.listSelections();
+ var pos = ranges[0].head;
+ var eolState = cm.getStateAfter(pos.line);
+ var inList = eolState.list !== false;
+
+ if (inList) {
+ cm.execCommand("indentLess");
+ return;
+ }
+
+ if (cm.options.indentWithTabs) {
+ cm.execCommand("insertTab");
+ }
+ else {
+ var spaces = Array(cm.options.tabSize + 1).join(" ");
+ cm.replaceSelection(spaces);
+ }
+};
+
+},{"codemirror":6}],13:[function(require,module,exports){
+/*global require,module*/
+"use strict";
+var CodeMirror = require("codemirror");
+require("codemirror/addon/edit/continuelist.js");
+require("./codemirror/tablist");
+require("codemirror/addon/display/fullscreen.js");
+require("codemirror/mode/markdown/markdown.js");
+require("codemirror/addon/mode/overlay.js");
+require("codemirror/mode/gfm/gfm.js");
+require("codemirror/mode/xml/xml.js");
+require("spell-checker");
+var marked = require("marked");
+
+
+var isMac = /Mac/.test(navigator.platform);
+
+var shortcuts = {
+ "Cmd-B": toggleBold,
+ "Cmd-I": toggleItalic,
+ "Cmd-K": drawLink,
+ "Cmd-H": toggleHeadingSmaller,
+ "Shift-Cmd-H": toggleHeadingBigger,
+ "Cmd-Alt-I": drawImage,
+ "Cmd-'": toggleBlockquote,
+ "Cmd-Alt-L": toggleOrderedList,
+ "Cmd-L": toggleUnorderedList,
+ "Cmd-Alt-C": toggleCodeBlock,
+ "Cmd-P": togglePreview
+};
+
+
+/**
+ * Fix shortcut. Mac use Command, others use Ctrl.
+ */
+function fixShortcut(name) {
+ if(isMac) {
+ name = name.replace("Ctrl", "Cmd");
+ } else {
+ name = name.replace("Cmd", "Ctrl");
+ }
+ return name;
+}
+
+
+/**
+ * Create icon element for toolbar.
+ */
+function createIcon(options, enableTooltips) {
+ options = options || {};
+ var el = document.createElement("a");
+ enableTooltips = (enableTooltips == undefined) ? true : enableTooltips;
+
+ if(options.title && enableTooltips) {
+ el.title = options.title;
+
+ if(isMac) {
+ el.title = el.title.replace("Ctrl", "⌘");
+ el.title = el.title.replace("Alt", "⌥");
+ }
+ }
+
+ el.className = options.className;
+ return el;
+}
+
+function createSep() {
+ var el = document.createElement("i");
+ el.className = "separator";
+ el.innerHTML = "|";
+ return el;
+}
+
+
+/**
+ * The state of CodeMirror at the given position.
+ */
+function getState(cm, pos) {
+ pos = pos || cm.getCursor("start");
+ var stat = cm.getTokenAt(pos);
+ if(!stat.type) return {};
+
+ var types = stat.type.split(" ");
+
+ var ret = {},
+ data, text;
+ for(var i = 0; i < types.length; i++) {
+ data = types[i];
+ if(data === "strong") {
+ ret.bold = true;
+ } else if(data === "variable-2") {
+ text = cm.getLine(pos.line);
+ if(/^\s*\d+\.\s/.test(text)) {
+ ret["ordered-list"] = true;
+ } else {
+ ret["unordered-list"] = true;
+ }
+ } else if(data === "atom") {
+ ret.quote = true;
+ } else if(data === "em") {
+ ret.italic = true;
+ } else if(data === "quote") {
+ ret.quote = true;
+ } else if(data === "strikethrough") {
+ ret.strikethrough = true;
+ } else if(data === "comment") {
+ ret.code = true;
+ }
+ }
+ return ret;
+}
+
+
+// Saved overflow setting
+var saved_overflow = "";
+
+/**
+ * Toggle full screen of the editor.
+ */
+function toggleFullScreen(editor) {
+ // Set fullscreen
+ var cm = editor.codemirror;
+ cm.setOption("fullScreen", !cm.getOption("fullScreen"));
+
+
+ // Prevent scrolling on body during fullscreen active
+ if(cm.getOption("fullScreen")) {
+ saved_overflow = document.body.style.overflow;
+ document.body.style.overflow = "hidden";
+ } else {
+ document.body.style.overflow = saved_overflow;
+ }
+
+
+ // Update toolbar class
+ var wrap = cm.getWrapperElement();
+
+ if(!/fullscreen/.test(wrap.previousSibling.className)) {
+ wrap.previousSibling.className += " fullscreen";
+ } else {
+ wrap.previousSibling.className = wrap.previousSibling.className.replace(/\s*fullscreen\b/, "");
+ }
+
+
+ // Update toolbar button
+ var toolbarButton = editor.toolbarElements.fullscreen;
+
+ if(!/active/.test(toolbarButton.className)) {
+ toolbarButton.className += " active";
+ } else {
+ toolbarButton.className = toolbarButton.className.replace(/\s*active\s*/g, "");
+ }
+
+
+ // Hide side by side if needed
+ var sidebyside = cm.getWrapperElement().nextSibling;
+ if(/editor-preview-active-side/.test(sidebyside.className))
+ toggleSideBySide(editor);
+}
+
+
+/**
+ * Action for toggling bold.
+ */
+function toggleBold(editor) {
+ _toggleBlock(editor, "bold", "**");
+}
+
+
+/**
+ * Action for toggling italic.
+ */
+function toggleItalic(editor) {
+ _toggleBlock(editor, "italic", "*");
+}
+
+
+/**
+ * Action for toggling strikethrough.
+ */
+function toggleStrikethrough(editor) {
+ _toggleBlock(editor, "strikethrough", "~~");
+}
+
+/**
+ * Action for toggling code block.
+ */
+function toggleCodeBlock(editor) {
+ _toggleBlock(editor, "code", "```\r\n", "\r\n```");
+}
+
+/**
+ * Action for toggling blockquote.
+ */
+function toggleBlockquote(editor) {
+ var cm = editor.codemirror;
+ _toggleLine(cm, "quote");
+}
+
+/**
+ * Action for toggling heading size: normal -> h1 -> h2 -> h3 -> h4 -> h5 -> h6 -> normal
+ */
+function toggleHeadingSmaller(editor) {
+ var cm = editor.codemirror;
+ _toggleHeading(cm, "smaller");
+}
+
+/**
+ * Action for toggling heading size: normal -> h6 -> h5 -> h4 -> h3 -> h2 -> h1 -> normal
+ */
+function toggleHeadingBigger(editor) {
+ var cm = editor.codemirror;
+ _toggleHeading(cm, "bigger");
+}
+
+/**
+ * Action for toggling heading size 1
+ */
+function toggleHeading1(editor) {
+ var cm = editor.codemirror;
+ _toggleHeading(cm, undefined, 1);
+}
+
+/**
+ * Action for toggling heading size 2
+ */
+function toggleHeading2(editor) {
+ var cm = editor.codemirror;
+ _toggleHeading(cm, undefined, 2);
+}
+
+/**
+ * Action for toggling heading size 3
+ */
+function toggleHeading3(editor) {
+ var cm = editor.codemirror;
+ _toggleHeading(cm, undefined, 3);
+}
+
+
+/**
+ * Action for toggling ul.
+ */
+function toggleUnorderedList(editor) {
+ var cm = editor.codemirror;
+ _toggleLine(cm, "unordered-list");
+}
+
+
+/**
+ * Action for toggling ol.
+ */
+function toggleOrderedList(editor) {
+ var cm = editor.codemirror;
+ _toggleLine(cm, "ordered-list");
+}
+
+
+/**
+ * Action for drawing a link.
+ */
+function drawLink(editor) {
+ var cm = editor.codemirror;
+ var stat = getState(cm);
+ _replaceSelection(cm, stat.link, "[", "](http://)");
+}
+
+
+/**
+ * Action for drawing an img.
+ */
+function drawImage(editor) {
+ var cm = editor.codemirror;
+ var stat = getState(cm);
+ _replaceSelection(cm, stat.image, "![](http://", ")");
+}
+
+
+/**
+ * Action for drawing a horizontal rule.
+ */
+function drawHorizontalRule(editor) {
+ var cm = editor.codemirror;
+ var stat = getState(cm);
+ _replaceSelection(cm, stat.image, "", "\n\n-----\n\n");
+}
+
+
+/**
+ * Undo action.
+ */
+function undo(editor) {
+ var cm = editor.codemirror;
+ cm.undo();
+ cm.focus();
+}
+
+
+/**
+ * Redo action.
+ */
+function redo(editor) {
+ var cm = editor.codemirror;
+ cm.redo();
+ cm.focus();
+}
+
+
+/**
+ * Toggle side by side preview
+ */
+function toggleSideBySide(editor) {
+ var cm = editor.codemirror;
+ var wrapper = cm.getWrapperElement();
+ var preview = wrapper.nextSibling;
+ var toolbarButton = editor.toolbarElements["side-by-side"];
+
+ if(/editor-preview-active-side/.test(preview.className)) {
+ preview.className = preview.className.replace(
+ /\s*editor-preview-active-side\s*/g, ""
+ );
+ toolbarButton.className = toolbarButton.className.replace(/\s*active\s*/g, "");
+ wrapper.className = wrapper.className.replace(/\s*CodeMirror-sided\s*/g, " ");
+ } else {
+ /* When the preview button is clicked for the first time,
+ * give some time for the transition from editor.css to fire and the view to slide from right to left,
+ * instead of just appearing.
+ */
+ setTimeout(function() {
+ if(!cm.getOption("fullScreen"))
+ toggleFullScreen(editor);
+ preview.className += " editor-preview-active-side";
+ }, 1);
+ toolbarButton.className += " active";
+ wrapper.className += " CodeMirror-sided";
+ }
+
+ // Hide normal preview if active
+ var previewNormal = wrapper.lastChild;
+ if(/editor-preview-active/.test(previewNormal.className)) {
+ previewNormal.className = previewNormal.className.replace(
+ /\s*editor-preview-active\s*/g, ""
+ );
+ var toolbar = editor.toolbarElements.preview;
+ var toolbar_div = wrapper.previousSibling;
+ toolbar.className = toolbar.className.replace(/\s*active\s*/g, "");
+ toolbar_div.className = toolbar_div.className.replace(/\s*disabled-for-preview*/g, "");
+ }
+
+ // Start preview with the current text
+ preview.innerHTML = editor.options.previewRender(editor.value(), preview);
+
+ // Updates preview
+ cm.on("update", function() {
+ preview.innerHTML = editor.options.previewRender(editor.value(), preview);
+ });
+}
+
+
+/**
+ * Preview action.
+ */
+function togglePreview(editor) {
+ var cm = editor.codemirror;
+ var wrapper = cm.getWrapperElement();
+ var toolbar_div = wrapper.previousSibling;
+ var toolbar = editor.toolbarElements.preview;
+ var preview = wrapper.lastChild;
+ if(!preview || !/editor-preview/.test(preview.className)) {
+ preview = document.createElement("div");
+ preview.className = "editor-preview";
+ wrapper.appendChild(preview);
+ }
+ if(/editor-preview-active/.test(preview.className)) {
+ preview.className = preview.className.replace(
+ /\s*editor-preview-active\s*/g, ""
+ );
+ toolbar.className = toolbar.className.replace(/\s*active\s*/g, "");
+ toolbar_div.className = toolbar_div.className.replace(/\s*disabled-for-preview*/g, "");
+ } else {
+ /* When the preview button is clicked for the first time,
+ * give some time for the transition from editor.css to fire and the view to slide from right to left,
+ * instead of just appearing.
+ */
+ setTimeout(function() {
+ preview.className += " editor-preview-active";
+ }, 1);
+ toolbar.className += " active";
+ toolbar_div.className += " disabled-for-preview";
+ }
+ preview.innerHTML = editor.options.previewRender(editor.value(), preview);
+
+ // Turn off side by side if needed
+ var sidebyside = cm.getWrapperElement().nextSibling;
+ if(/editor-preview-active-side/.test(sidebyside.className))
+ toggleSideBySide(editor);
+}
+
+function _replaceSelection(cm, active, start, end) {
+ if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className))
+ return;
+
+ var text;
+ var startPoint = cm.getCursor("start");
+ var endPoint = cm.getCursor("end");
+ if(active) {
+ text = cm.getLine(startPoint.line);
+ start = text.slice(0, startPoint.ch);
+ end = text.slice(startPoint.ch);
+ cm.replaceRange(start + end, {
+ line: startPoint.line,
+ ch: 0
+ });
+ } else {
+ text = cm.getSelection();
+ cm.replaceSelection(start + text + end);
+
+ startPoint.ch += start.length;
+ if(startPoint !== endPoint) {
+ endPoint.ch += start.length;
+ }
+ }
+ cm.setSelection(startPoint, endPoint);
+ cm.focus();
+}
+
+
+function _toggleHeading(cm, direction, size) {
+ if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className))
+ return;
+
+ var startPoint = cm.getCursor("start");
+ var endPoint = cm.getCursor("end");
+ for(var i = startPoint.line; i <= endPoint.line; i++) {
+ (function(i) {
+ var text = cm.getLine(i);
+ var currHeadingLevel = text.search(/[^#]/);
+
+ if(direction !== undefined) {
+ if(currHeadingLevel <= 0) {
+ if(direction == "bigger") {
+ text = "###### " + text;
+ } else {
+ text = "# " + text;
+ }
+ } else if(currHeadingLevel == 6 && direction == "smaller") {
+ text = text.substr(7);
+ } else if(currHeadingLevel == 1 && direction == "bigger") {
+ text = text.substr(2);
+ } else {
+ if(direction == "bigger") {
+ text = text.substr(1);
+ } else {
+ text = "#" + text;
+ }
+ }
+ } else {
+ if(size == 1) {
+ if(currHeadingLevel <= 0) {
+ text = "# " + text;
+ } else if(currHeadingLevel == size) {
+ text = text.substr(currHeadingLevel + 1);
+ } else {
+ text = "# " + text.substr(currHeadingLevel + 1);
+ }
+ } else if(size == 2) {
+ if(currHeadingLevel <= 0) {
+ text = "## " + text;
+ } else if(currHeadingLevel == size) {
+ text = text.substr(currHeadingLevel + 1);
+ } else {
+ text = "## " + text.substr(currHeadingLevel + 1);
+ }
+ } else {
+ if(currHeadingLevel <= 0) {
+ text = "### " + text;
+ } else if(currHeadingLevel == size) {
+ text = text.substr(currHeadingLevel + 1);
+ } else {
+ text = "### " + text.substr(currHeadingLevel + 1);
+ }
+ }
+ }
+
+ cm.replaceRange(text, {
+ line: i,
+ ch: 0
+ }, {
+ line: i,
+ ch: 99999999999999
+ });
+ })(i);
+ }
+ cm.focus();
+}
+
+
+function _toggleLine(cm, name) {
+ if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className))
+ return;
+
+ var stat = getState(cm);
+ var startPoint = cm.getCursor("start");
+ var endPoint = cm.getCursor("end");
+ var repl = {
+ "quote": /^(\s*)\>\s+/,
+ "unordered-list": /^(\s*)(\*|\-|\+)\s+/,
+ "ordered-list": /^(\s*)\d+\.\s+/
+ };
+ var map = {
+ "quote": "> ",
+ "unordered-list": "* ",
+ "ordered-list": "1. "
+ };
+ for(var i = startPoint.line; i <= endPoint.line; i++) {
+ (function(i) {
+ var text = cm.getLine(i);
+ if(stat[name]) {
+ text = text.replace(repl[name], "$1");
+ } else {
+ text = map[name] + text;
+ }
+ cm.replaceRange(text, {
+ line: i,
+ ch: 0
+ }, {
+ line: i,
+ ch: 99999999999999
+ });
+ })(i);
+ }
+ cm.focus();
+}
+
+function _toggleBlock(editor, type, start_chars, end_chars) {
+ if(/editor-preview-active/.test(editor.codemirror.getWrapperElement().lastChild.className))
+ return;
+
+ end_chars = (typeof end_chars === "undefined") ? start_chars : end_chars;
+ var cm = editor.codemirror;
+ var stat = getState(cm);
+
+ var text;
+ var start = start_chars;
+ var end = end_chars;
+
+ var startPoint = cm.getCursor("start");
+ var endPoint = cm.getCursor("end");
+
+ if(stat[type]) {
+ text = cm.getLine(startPoint.line);
+ start = text.slice(0, startPoint.ch);
+ end = text.slice(startPoint.ch);
+ if(type == "bold") {
+ start = start.replace(/(\*\*|__)(?![\s\S]*(\*\*|__))/, "");
+ end = end.replace(/(\*\*|__)/, "");
+ } else if(type == "italic") {
+ start = start.replace(/(\*|_)(?![\s\S]*(\*|_))/, "");
+ end = end.replace(/(\*|_)/, "");
+ } else if(type == "strikethrough") {
+ start = start.replace(/(\*\*|~~)(?![\s\S]*(\*\*|~~))/, "");
+ end = end.replace(/(\*\*|~~)/, "");
+ }
+ cm.replaceRange(start + end, {
+ line: startPoint.line,
+ ch: 0
+ }, {
+ line: startPoint.line,
+ ch: 99999999999999
+ });
+
+ if(type == "bold" || type == "strikethrough") {
+ startPoint.ch -= 2;
+ if(startPoint !== endPoint) {
+ endPoint.ch -= 2;
+ }
+ } else if(type == "italic") {
+ startPoint.ch -= 1;
+ if(startPoint !== endPoint) {
+ endPoint.ch -= 1;
+ }
+ }
+ } else {
+ text = cm.getSelection();
+ if(type == "bold") {
+ text = text.split("**").join("");
+ text = text.split("__").join("");
+ } else if(type == "italic") {
+ text = text.split("*").join("");
+ text = text.split("_").join("");
+ } else if(type == "strikethrough") {
+ text = text.split("~~").join("");
+ }
+ cm.replaceSelection(start + text + end);
+
+ startPoint.ch += start_chars.length;
+ endPoint.ch = startPoint.ch + text.length;
+ }
+
+ cm.setSelection(startPoint, endPoint);
+ cm.focus();
+}
+
+
+/* The right word count in respect for CJK. */
+function wordCount(data) {
+ var pattern = /[a-zA-Z0-9_\u0392-\u03c9]+|[\u4E00-\u9FFF\u3400-\u4dbf\uf900-\ufaff\u3040-\u309f\uac00-\ud7af]+/g;
+ var m = data.match(pattern);
+ var count = 0;
+ if(m === null) return count;
+ for(var i = 0; i < m.length; i++) {
+ if(m[i].charCodeAt(0) >= 0x4E00) {
+ count += m[i].length;
+ } else {
+ count += 1;
+ }
+ }
+ return count;
+}
+
+
+var toolbarBuiltInButtons = {
+ "bold": {
+ name: "bold",
+ action: toggleBold,
+ className: "fa fa-bold",
+ title: "Bold (Ctrl+B)"
+ },
+ "italic": {
+ name: "italic",
+ action: toggleItalic,
+ className: "fa fa-italic",
+ title: "Italic (Ctrl+I)"
+ },
+ "strikethrough": {
+ name: "strikethrough",
+ action: toggleStrikethrough,
+ className: "fa fa-strikethrough",
+ title: "Strikethrough"
+ },
+ "heading": {
+ name: "heading",
+ action: toggleHeadingSmaller,
+ className: "fa fa-header",
+ title: "Heading (Ctrl+H)"
+ },
+ "heading-smaller": {
+ name: "heading-smaller",
+ action: toggleHeadingSmaller,
+ className: "fa fa-header fa-header-x fa-header-smaller",
+ title: "Smaller Heading (Ctrl+H)"
+ },
+ "heading-bigger": {
+ name: "heading-bigger",
+ action: toggleHeadingBigger,
+ className: "fa fa-header fa-header-x fa-header-bigger",
+ title: "Bigger Heading (Shift+Ctrl+H)"
+ },
+ "heading-1": {
+ name: "heading-1",
+ action: toggleHeading1,
+ className: "fa fa-header fa-header-x fa-header-1",
+ title: "Big Heading"
+ },
+ "heading-2": {
+ name: "heading-2",
+ action: toggleHeading2,
+ className: "fa fa-header fa-header-x fa-header-2",
+ title: "Medium Heading"
+ },
+ "heading-3": {
+ name: "heading-3",
+ action: toggleHeading3,
+ className: "fa fa-header fa-header-x fa-header-3",
+ title: "Small Heading"
+ },
+ "code": {
+ name: "code",
+ action: toggleCodeBlock,
+ className: "fa fa-code",
+ title: "Code (Ctrl+Alt+C)"
+ },
+ "quote": {
+ name: "quote",
+ action: toggleBlockquote,
+ className: "fa fa-quote-left",
+ title: "Quote (Ctrl+')"
+ },
+ "unordered-list": {
+ name: "unordered-list",
+ action: toggleUnorderedList,
+ className: "fa fa-list-ul",
+ title: "Generic List (Ctrl+L)"
+ },
+ "ordered-list": {
+ name: "ordered-list",
+ action: toggleOrderedList,
+ className: "fa fa-list-ol",
+ title: "Numbered List (Ctrl+Alt+L)"
+ },
+ "link": {
+ name: "link",
+ action: drawLink,
+ className: "fa fa-link",
+ title: "Create Link (Ctrl+K)"
+ },
+ "image": {
+ name: "image",
+ action: drawImage,
+ className: "fa fa-picture-o",
+ title: "Insert Image (Ctrl+Alt+I)"
+ },
+ "horizontal-rule": {
+ name: "horizontal-rule",
+ action: drawHorizontalRule,
+ className: "fa fa-minus",
+ title: "Insert Horizontal Line"
+ },
+ "preview": {
+ name: "preview",
+ action: togglePreview,
+ className: "fa fa-eye no-disable",
+ title: "Toggle Preview (Ctrl+P)"
+ },
+ "side-by-side": {
+ name: "side-by-side",
+ action: toggleSideBySide,
+ className: "fa fa-columns no-disable no-mobile",
+ title: "Toggle Side by Side (F9)"
+ },
+ "fullscreen": {
+ name: "fullscreen",
+ action: toggleFullScreen,
+ className: "fa fa-arrows-alt no-disable no-mobile",
+ title: "Toggle Fullscreen (F11)"
+ },
+ "guide": {
+ name: "guide",
+ action: "http://nextstepwebs.github.io/simplemde-markdown-editor/markdown-guide",
+ className: "fa fa-question-circle",
+ title: "Markdown Guide"
+ }
+};
+
+
+/**
+ * Interface of SimpleMDE.
+ */
+function SimpleMDE(options) {
+ // Handle options parameter
+ options = options || {};
+
+
+ // Used later to refer to it"s parent
+ options.parent = this;
+
+
+ // Check if Font Awesome needs to be auto downloaded
+ var autoDownloadFA = true;
+
+ if(options.autoDownloadFontAwesome === false) {
+ autoDownloadFA = false;
+ }
+
+ if(options.autoDownloadFontAwesome !== true) {
+ var styleSheets = document.styleSheets;
+ for(var i = 0; i < styleSheets.length; i++) {
+ if(!styleSheets[i].href)
+ continue;
+
+ if(styleSheets[i].href.indexOf("//maxcdn.bootstrapcdn.com/font-awesome/") > -1) {
+ autoDownloadFA = false;
+ }
+ }
+ }
+
+ if(autoDownloadFA) {
+ var link = document.createElement("link");
+ link.rel = "stylesheet";
+ link.href = "https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css";
+ document.getElementsByTagName("head")[0].appendChild(link);
+ }
+
+
+ // Find the textarea to use
+ if(options.element) {
+ this.element = options.element;
+ } else if(options.element === null) {
+ // This means that the element option was specified, but no element was found
+ console.log("SimpleMDE: Error. No element was found.");
+ return;
+ }
+
+
+ // Handle toolbar and status bar
+ if(options.toolbar !== false)
+ options.toolbar = options.toolbar || SimpleMDE.toolbar;
+
+ if(!options.hasOwnProperty("status")) {
+ options.status = ["autosave", "lines", "words", "cursor"];
+ }
+
+
+ // Add default preview rendering function
+ if(!options.previewRender) {
+ options.previewRender = function(plainText) {
+ // Note: "this" refers to the options object
+ return this.parent.markdown(plainText);
+ };
+ }
+
+
+ // Set default options for parsing config
+ options.parsingConfig = options.parsingConfig || {};
+
+
+ // Update this options
+ this.options = options;
+
+
+ // Auto render
+ this.render();
+
+
+ // The codemirror component is only available after rendering
+ // so, the setter for the initialValue can only run after
+ // the element has been rendered
+ if(options.initialValue) {
+ this.value(options.initialValue);
+ }
+}
+
+/**
+ * Default toolbar elements.
+ */
+SimpleMDE.toolbar = ["bold", "italic", "heading", "|", "quote", "unordered-list", "ordered-list", "|", "link", "image", "|", "preview", "side-by-side", "fullscreen", "guide"];
+
+/**
+ * Default markdown render.
+ */
+SimpleMDE.prototype.markdown = function(text) {
+ if(marked) {
+ // Initialize
+ var markedOptions = {};
+
+
+ // Update options
+ if(this.options && this.options.renderingConfig && this.options.renderingConfig.singleLineBreaks !== false) {
+ markedOptions.breaks = true;
+ }
+
+ if(this.options && this.options.renderingConfig && this.options.renderingConfig.codeSyntaxHighlighting === true && window.hljs) {
+ markedOptions.highlight = function(code) {
+ return window.hljs.highlightAuto(code).value;
+ };
+ }
+
+
+ // Set options
+ marked.setOptions(markedOptions);
+
+
+ // Return
+ return marked(text);
+ }
+};
+
+/**
+ * Render editor to the given element.
+ */
+SimpleMDE.prototype.render = function(el) {
+ if(!el) {
+ el = this.element || document.getElementsByTagName("textarea")[0];
+ }
+
+ if(this._rendered && this._rendered === el) {
+ // Already rendered.
+ return;
+ }
+
+ this.element = el;
+ var options = this.options;
+
+ var self = this;
+ var keyMaps = {};
+
+ for(var key in shortcuts) {
+ (function(key) {
+ keyMaps[fixShortcut(key)] = function() {
+ shortcuts[key](self);
+ };
+ })(key);
+ }
+
+ keyMaps["Enter"] = "newlineAndIndentContinueMarkdownList";
+ keyMaps["Tab"] = "tabAndIndentMarkdownList";
+ keyMaps["Shift-Tab"] = "shiftTabAndUnindentMarkdownList";
+ keyMaps["F11"] = function() {
+ toggleFullScreen(self);
+ };
+ keyMaps["F9"] = function() {
+ toggleSideBySide(self);
+ };
+ keyMaps["Esc"] = function(cm) {
+ if(cm.getOption("fullScreen")) toggleFullScreen(self);
+ };
+
+ var mode, backdrop;
+ if(options.spellChecker !== false) {
+ mode = "spell-checker";
+ backdrop = options.parsingConfig;
+ backdrop.name = "gfm";
+ backdrop.gitHubSpice = false;
+ } else {
+ mode = options.parsingConfig;
+ mode.name = "gfm";
+ mode.gitHubSpice = false;
+ }
+
+ this.codemirror = CodeMirror.fromTextArea(el, {
+ mode: mode,
+ backdrop: backdrop,
+ theme: "paper",
+ tabSize: (options.tabSize != undefined) ? options.tabSize : 2,
+ indentUnit: (options.tabSize != undefined) ? options.tabSize : 2,
+ indentWithTabs: (options.indentWithTabs === false) ? false : true,
+ lineNumbers: false,
+ autofocus: (options.autofocus === true) ? true : false,
+ extraKeys: keyMaps,
+ lineWrapping: (options.lineWrapping === false) ? false : true,
+ allowDropFileTypes: ["text/plain"]
+ });
+
+ if(options.toolbar !== false) {
+ this.createToolbar();
+ }
+ if(options.status !== false) {
+ this.createStatusbar();
+ }
+ if(options.autosave != undefined && options.autosave.enabled === true) {
+ this.autosave();
+ }
+
+ this.createSideBySide();
+
+ this._rendered = this.element;
+};
+
+SimpleMDE.prototype.autosave = function() {
+ var content = this.value();
+ var simplemde = this;
+
+ if(this.options.autosave.unique_id == undefined || this.options.autosave.unique_id == "") {
+ console.log("SimpleMDE: You must set a unique_id to use the autosave feature");
+ return;
+ }
+
+ if(simplemde.element.form != null && simplemde.element.form != undefined) {
+ simplemde.element.form.addEventListener("submit", function() {
+ localStorage.setItem(simplemde.options.autosave.unique_id, "");
+ });
+ }
+
+ if(this.options.autosave.loaded !== true) {
+ if(localStorage.getItem(this.options.autosave.unique_id) != null)
+ this.codemirror.setValue(localStorage.getItem(this.options.autosave.unique_id));
+
+ this.options.autosave.loaded = true;
+ }
+
+ if(localStorage) {
+ localStorage.setItem(this.options.autosave.unique_id, content);
+ }
+
+ var el = document.getElementById("autosaved");
+ if(el != null && el != undefined && el != "") {
+ var d = new Date();
+ var hh = d.getHours();
+ var m = d.getMinutes();
+ var dd = "am";
+ var h = hh;
+ if(h >= 12) {
+ h = hh - 12;
+ dd = "pm";
+ }
+ if(h == 0) {
+ h = 12;
+ }
+ m = m < 10 ? "0" + m : m;
+
+ el.innerHTML = "Autosaved: " + h + ":" + m + " " + dd;
+ }
+
+ setTimeout(function() {
+ simplemde.autosave();
+ }, this.options.autosave.delay || 10000);
+};
+
+SimpleMDE.prototype.createSideBySide = function() {
+ var cm = this.codemirror;
+ var wrapper = cm.getWrapperElement();
+ var preview = wrapper.nextSibling;
+
+ if(!preview || !/editor-preview-side/.test(preview.className)) {
+ preview = document.createElement("div");
+ preview.className = "editor-preview-side";
+ wrapper.parentNode.insertBefore(preview, wrapper.nextSibling);
+ }
+
+ // Syncs scroll editor -> preview
+ var cScroll = false;
+ var pScroll = false;
+ cm.on("scroll", function(v) {
+ if(cScroll) {
+ cScroll = false;
+ return;
+ }
+ pScroll = true;
+ var height = v.getScrollInfo().height - v.getScrollInfo().clientHeight;
+ var ratio = parseFloat(v.getScrollInfo().top) / height;
+ var move = (preview.scrollHeight - preview.clientHeight) * ratio;
+ preview.scrollTop = move;
+ });
+
+ // Syncs scroll preview -> editor
+ preview.onscroll = function() {
+ if(pScroll) {
+ pScroll = false;
+ return;
+ }
+ cScroll = true;
+ var height = preview.scrollHeight - preview.clientHeight;
+ var ratio = parseFloat(preview.scrollTop) / height;
+ var move = (cm.getScrollInfo().height - cm.getScrollInfo().clientHeight) * ratio;
+ cm.scrollTo(0, move);
+ };
+ return true;
+};
+
+SimpleMDE.prototype.createToolbar = function(items) {
+ items = items || this.options.toolbar;
+
+ if(!items || items.length === 0) {
+ return;
+ }
+ var i;
+ for(i = 0; i < items.length; i++) {
+ if(toolbarBuiltInButtons[items[i]] != undefined) {
+ items[i] = toolbarBuiltInButtons[items[i]];
+ }
+ }
+
+ var bar = document.createElement("div");
+ bar.className = "editor-toolbar";
+
+ var self = this;
+
+ var toolbar_data = {};
+ self.toolbar = items;
+
+ for(i = 0; i < items.length; i++) {
+ if(items[i].name == "guide" && self.options.toolbarGuideIcon === false)
+ continue;
+
+ if(self.options.hideIcons && self.options.hideIcons.indexOf(items[i].name) != -1)
+ continue;
+
+ (function(item) {
+ var el;
+ if(item === "|") {
+ el = createSep();
+ } else {
+ el = createIcon(item, self.options.toolbarTips);
+ }
+
+ // bind events, special for info
+ if(item.action) {
+ if(typeof item.action === "function") {
+ el.onclick = function() {
+ item.action(self);
+ };
+ } else if(typeof item.action === "string") {
+ el.href = item.action;
+ el.target = "_blank";
+ }
+ }
+ toolbar_data[item.name || item] = el;
+ bar.appendChild(el);
+ })(items[i]);
+ }
+
+ self.toolbarElements = toolbar_data;
+
+ var cm = this.codemirror;
+ cm.on("cursorActivity", function() {
+ var stat = getState(cm);
+
+ for(var key in toolbar_data) {
+ (function(key) {
+ var el = toolbar_data[key];
+ if(stat[key]) {
+ el.className += " active";
+ } else if(key != "fullscreen" && key != "side-by-side") {
+ el.className = el.className.replace(/\s*active\s*/g, "");
+ }
+ })(key);
+ }
+ });
+
+ var cmWrapper = cm.getWrapperElement();
+ cmWrapper.parentNode.insertBefore(bar, cmWrapper);
+ return bar;
+};
+
+SimpleMDE.prototype.createStatusbar = function(status) {
+ status = status || this.options.status;
+ var options = this.options;
+
+ if(!status || status.length === 0) return;
+
+ var bar = document.createElement("div");
+ bar.className = "editor-statusbar";
+
+ var pos, cm = this.codemirror;
+ for(var i = 0; i < status.length; i++) {
+ (function(name) {
+ var el = document.createElement("span");
+ el.className = name;
+ if(name === "words") {
+ el.innerHTML = "0";
+ cm.on("update", function() {
+ el.innerHTML = wordCount(cm.getValue());
+ });
+ } else if(name === "lines") {
+ el.innerHTML = "0";
+ cm.on("update", function() {
+ el.innerHTML = cm.lineCount();
+ });
+ } else if(name === "cursor") {
+ el.innerHTML = "0:0";
+ cm.on("cursorActivity", function() {
+ pos = cm.getCursor();
+ el.innerHTML = pos.line + ":" + pos.ch;
+ });
+ } else if(name === "autosave") {
+ if(options.autosave != undefined && options.autosave.enabled === true) {
+ el.setAttribute("id", "autosaved");
+ }
+ }
+ bar.appendChild(el);
+ })(status[i]);
+ }
+
+ var cmWrapper = this.codemirror.getWrapperElement();
+ cmWrapper.parentNode.insertBefore(bar, cmWrapper.nextSibling);
+ return bar;
+};
+
+/**
+ * Get or set the text content.
+ */
+SimpleMDE.prototype.value = function(val) {
+ if(val === undefined) {
+ return this.codemirror.getValue();
+ } else {
+ this.codemirror.getDoc().setValue(val);
+ return this;
+ }
+};
+
+
+/**
+ * Bind static methods for exports.
+ */
+SimpleMDE.toggleBold = toggleBold;
+SimpleMDE.toggleItalic = toggleItalic;
+SimpleMDE.toggleStrikethrough = toggleStrikethrough;
+SimpleMDE.toggleBlockquote = toggleBlockquote;
+SimpleMDE.toggleHeadingSmaller = toggleHeadingSmaller;
+SimpleMDE.toggleHeadingBigger = toggleHeadingBigger;
+SimpleMDE.toggleHeading1 = toggleHeading1;
+SimpleMDE.toggleHeading2 = toggleHeading2;
+SimpleMDE.toggleHeading3 = toggleHeading3;
+SimpleMDE.toggleCodeBlock = toggleCodeBlock;
+SimpleMDE.toggleUnorderedList = toggleUnorderedList;
+SimpleMDE.toggleOrderedList = toggleOrderedList;
+SimpleMDE.drawLink = drawLink;
+SimpleMDE.drawImage = drawImage;
+SimpleMDE.drawHorizontalRule = drawHorizontalRule;
+SimpleMDE.undo = undo;
+SimpleMDE.redo = redo;
+SimpleMDE.togglePreview = togglePreview;
+SimpleMDE.toggleSideBySide = toggleSideBySide;
+SimpleMDE.toggleFullScreen = toggleFullScreen;
+
+/**
+ * Bind instance methods for exports.
+ */
+SimpleMDE.prototype.toggleBold = function() {
+ toggleBold(this);
+};
+SimpleMDE.prototype.toggleItalic = function() {
+ toggleItalic(this);
+};
+SimpleMDE.prototype.toggleStrikethrough = function() {
+ toggleStrikethrough(this);
+};
+SimpleMDE.prototype.toggleBlockquote = function() {
+ toggleBlockquote(this);
+};
+SimpleMDE.prototype.toggleHeadingSmaller = function() {
+ toggleHeadingSmaller(this);
+};
+SimpleMDE.prototype.toggleHeadingBigger = function() {
+ toggleHeadingBigger(this);
+};
+SimpleMDE.prototype.toggleHeading1 = function() {
+ toggleHeading1(this);
+};
+SimpleMDE.prototype.toggleHeading2 = function() {
+ toggleHeading2(this);
+};
+SimpleMDE.prototype.toggleHeading3 = function() {
+ toggleHeading3(this);
+};
+SimpleMDE.prototype.toggleCodeBlock = function() {
+ toggleCodeBlock(this);
+};
+SimpleMDE.prototype.toggleUnorderedList = function() {
+ toggleUnorderedList(this);
+};
+SimpleMDE.prototype.toggleOrderedList = function() {
+ toggleOrderedList(this);
+};
+SimpleMDE.prototype.drawLink = function() {
+ drawLink(this);
+};
+SimpleMDE.prototype.drawImage = function() {
+ drawImage(this);
+};
+SimpleMDE.prototype.drawHorizontalRule = function() {
+ drawHorizontalRule(this);
+};
+SimpleMDE.prototype.undo = function() {
+ undo(this);
+};
+SimpleMDE.prototype.redo = function() {
+ redo(this);
+};
+SimpleMDE.prototype.togglePreview = function() {
+ togglePreview(this);
+};
+SimpleMDE.prototype.toggleSideBySide = function() {
+ toggleSideBySide(this);
+};
+SimpleMDE.prototype.toggleFullScreen = function() {
+ toggleFullScreen(this);
+};
+module.exports = SimpleMDE;
+
+},{"./codemirror/tablist":12,"codemirror":6,"codemirror/addon/display/fullscreen.js":3,"codemirror/addon/edit/continuelist.js":4,"codemirror/addon/mode/overlay.js":5,"codemirror/mode/gfm/gfm.js":7,"codemirror/mode/markdown/markdown.js":8,"codemirror/mode/xml/xml.js":10,"marked":11,"spell-checker":1}]},{},[13])(13)
+});
+//# sourceMappingURL=data:application/json;charset:utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJub2RlX21vZHVsZXMvY29kZW1pcnJvci1zcGVsbC1jaGVja2VyL3NyYy9qcy9zcGVsbC1jaGVja2VyLmpzIiwibm9kZV9tb2R1bGVzL2NvZGVtaXJyb3Itc3BlbGwtY2hlY2tlci9zcmMvanMvdHlwby5qcyIsIm5vZGVfbW9kdWxlcy9jb2RlbWlycm9yL2FkZG9uL2Rpc3BsYXkvZnVsbHNjcmVlbi5qcyIsIm5vZGVfbW9kdWxlcy9jb2RlbWlycm9yL2FkZG9uL2VkaXQvY29udGludWVsaXN0LmpzIiwibm9kZV9tb2R1bGVzL2NvZGVtaXJyb3IvYWRkb24vbW9kZS9vdmVybGF5LmpzIiwibm9kZV9tb2R1bGVzL2NvZGVtaXJyb3IvbGliL2NvZGVtaXJyb3IuanMiLCJub2RlX21vZHVsZXMvY29kZW1pcnJvci9tb2RlL2dmbS9nZm0uanMiLCJub2RlX21vZHVsZXMvY29kZW1pcnJvci9tb2RlL21hcmtkb3duL21hcmtkb3duLmpzIiwibm9kZV9tb2R1bGVzL2NvZGVtaXJyb3IvbW9kZS9tZXRhLmpzIiwibm9kZV9tb2R1bGVzL2NvZGVtaXJyb3IvbW9kZS94bWwveG1sLmpzIiwibm9kZV9tb2R1bGVzL21hcmtlZC9saWIvbWFya2VkLmpzIiwic3JjL2pzL2NvZGVtaXJyb3IvdGFibGlzdC5qcyIsInNyYy9qcy9zaW1wbGVtZGUuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0FDQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7QUNqR0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7O0FDbHdCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDekNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ25EQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3JGQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN4cFJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbElBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3h4QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwTUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FDallBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7QUNyd0NBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsImZpbGUiOiJnZW5lcmF0ZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlc0NvbnRlbnQiOlsiKGZ1bmN0aW9uIGUodCxuLHIpe2Z1bmN0aW9uIHMobyx1KXtpZighbltvXSl7aWYoIXRbb10pe3ZhciBhPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7aWYoIXUmJmEpcmV0dXJuIGEobywhMCk7aWYoaSlyZXR1cm4gaShvLCEwKTt2YXIgZj1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK28rXCInXCIpO3Rocm93IGYuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixmfXZhciBsPW5bb109e2V4cG9ydHM6e319O3Rbb11bMF0uY2FsbChsLmV4cG9ydHMsZnVuY3Rpb24oZSl7dmFyIG49dFtvXVsxXVtlXTtyZXR1cm4gcyhuP246ZSl9LGwsbC5leHBvcnRzLGUsdCxuLHIpfXJldHVybiBuW29dLmV4cG9ydHN9dmFyIGk9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtmb3IodmFyIG89MDtvPHIubGVuZ3RoO28rKylzKHJbb10pO3JldHVybiBzfSkiLCJcbjsgVHlwbyA9IGdsb2JhbC5UeXBvID0gcmVxdWlyZShcIkM6XFxcXHBoYW50b214XFxcXHByb2plY3RzXFxcXHNpbXBsZW1kZS1tYXJrZG93bi1lZGl0b3JcXFxcbm9kZV9tb2R1bGVzXFxcXGNvZGVtaXJyb3Itc3BlbGwtY2hlY2tlclxcXFxzcmNcXFxcanNcXFxcdHlwby5qc1wiKTtcbkNvZGVNaXJyb3IgPSBnbG9iYWwuQ29kZU1pcnJvciA9IHJlcXVpcmUoXCJjb2RlbWlycm9yXCIpO1xuOyB2YXIgX19icm93c2VyaWZ5X3NoaW1fcmVxdWlyZV9fPXJlcXVpcmU7KGZ1bmN0aW9uIGJyb3dzZXJpZnlTaGltKG1vZHVsZSwgZGVmaW5lLCByZXF1aXJlKSB7XG4vLyBJbml0aWFsaXplIGRhdGEgZ2xvYmFsbHkgdG8gcmVkdWNlIG1lbW9yeSBjb25zdW1wdGlvblxyXG52YXIgbnVtX2xvYWRlZCA9IDA7XHJcbnZhciBhZmZfbG9hZGluZyA9IGZhbHNlO1xyXG52YXIgZGljX2xvYWRpbmcgPSBmYWxzZTtcclxudmFyIGFmZl9kYXRhID0gXCJcIjtcclxudmFyIGRpY19kYXRhID0gXCJcIjtcclxudmFyIHR5cG87XHJcblxyXG5cclxuQ29kZU1pcnJvci5kZWZpbmVNb2RlKFwic3BlbGwtY2hlY2tlclwiLCBmdW5jdGlvbihjb25maWcsIHBhcnNlckNvbmZpZykge1xyXG5cdC8vIExvYWQgQUZGL0RJQyBkYXRhXHJcblx0aWYoIWFmZl9sb2FkaW5nKXtcclxuXHRcdGFmZl9sb2FkaW5nID0gdHJ1ZTtcclxuXHRcdHZhciB4aHJfYWZmID0gbmV3IFhNTEh0dHBSZXF1ZXN0KCk7XHJcblx0XHR4aHJfYWZmLm9wZW4oXCJHRVRcIiwgXCJodHRwczovL2Nkbi5qc2RlbGl2ci5uZXQvY29kZW1pcnJvci5zcGVsbC1jaGVja2VyL2xhdGVzdC9lbl9VUy5hZmZcIiwgdHJ1ZSk7XHJcblx0XHR4aHJfYWZmLm9ubG9hZCA9IGZ1bmN0aW9uIChlKSB7XHJcblx0XHRcdGlmICh4aHJfYWZmLnJlYWR5U3RhdGUgPT09IDQgJiYgeGhyX2FmZi5zdGF0dXMgPT09IDIwMCkge1xyXG5cdFx0XHRcdGFmZl9kYXRhID0geGhyX2FmZi5yZXNwb25zZVRleHQ7XHJcblx0XHRcdFx0bnVtX2xvYWRlZCsrO1xyXG5cdFx0XHRcdFxyXG5cdFx0XHRcdGlmKG51bV9sb2FkZWQgPT0gMil7XHJcblx0XHRcdFx0XHR0eXBvID0gbmV3IFR5cG8oXCJlbl9VU1wiLCBhZmZfZGF0YSwgZGljX2RhdGEsIHtcclxuXHRcdFx0XHRcdFx0cGxhdGZvcm06ICdhbnknXHJcblx0XHRcdFx0XHR9KTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHRcdH07XHJcblx0XHR4aHJfYWZmLnNlbmQobnVsbCk7XHJcblx0fVxyXG5cdFxyXG5cdGlmKCFkaWNfbG9hZGluZyl7XHJcblx0XHRkaWNfbG9hZGluZyA9IHRydWU7XHJcblx0XHR2YXIgeGhyX2RpYyA9IG5ldyBYTUxIdHRwUmVxdWVzdCgpO1xyXG5cdFx0eGhyX2RpYy5vcGVuKFwiR0VUXCIsIFwiaHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L2NvZGVtaXJyb3Iuc3BlbGwtY2hlY2tlci9sYXRlc3QvZW5fVVMuZGljXCIsIHRydWUpO1xyXG5cdFx0eGhyX2RpYy5vbmxvYWQgPSBmdW5jdGlvbiAoZSkge1xyXG5cdFx0XHRpZiAoeGhyX2RpYy5yZWFkeVN0YXRlID09PSA0ICYmIHhocl9kaWMuc3RhdHVzID09PSAyMDApIHtcclxuXHRcdFx0XHRkaWNfZGF0YSA9IHhocl9kaWMucmVzcG9uc2VUZXh0O1xyXG5cdFx0XHRcdG51bV9sb2FkZWQrKztcclxuXHRcdFx0XHRcclxuXHRcdFx0XHRpZihudW1fbG9hZGVkID09IDIpe1xyXG5cdFx0XHRcdFx0dHlwbyA9IG5ldyBUeXBvKFwiZW5fVVNcIiwgYWZmX2RhdGEsIGRpY19kYXRhLCB7XHJcblx0XHRcdFx0XHRcdHBsYXRmb3JtOiAnYW55J1xyXG5cdFx0XHRcdFx0fSk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHR9O1xyXG5cdFx0eGhyX2RpYy5zZW5kKG51bGwpO1xyXG5cdH1cclxuXHJcblx0XHJcblx0XHJcblx0Ly8gRGVmaW5lIHdoYXQgc2VwYXJhdGVzIGEgd29yZFxyXG5cdHZhciByeF93b3JkID0gXCIhXFxcIiMkJSYoKSorLC0uLzo7PD0+P0BbXFxcXF1eX2B7fH1+IFwiO1xyXG5cdFxyXG5cdFxyXG5cdC8vIENyZWF0ZSB0aGUgb3ZlcmxheSBhbmQgc3VjaFxyXG5cdHZhciBvdmVybGF5ID0ge1xyXG5cdFx0dG9rZW46IGZ1bmN0aW9uKHN0cmVhbSwgc3RhdGUpIHtcclxuXHRcdFx0dmFyIGNoID0gc3RyZWFtLnBlZWsoKTtcclxuXHRcdFx0dmFyIHdvcmQgPSBcIlwiO1xyXG5cclxuXHRcdFx0aWYocnhfd29yZC5pbmNsdWRlcyhjaCkpIHtcclxuXHRcdFx0XHRzdHJlYW0ubmV4dCgpO1xyXG5cdFx0XHRcdHJldHVybiBudWxsO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHR3aGlsZSgoY2ggPSBzdHJlYW0ucGVlaygpKSAhPSBudWxsICYmICFyeF93b3JkLmluY2x1ZGVzKGNoKSkge1xyXG5cdFx0XHRcdHdvcmQgKz0gY2g7XHJcblx0XHRcdFx0c3RyZWFtLm5leHQoKTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0aWYodHlwbyAmJiAhdHlwby5jaGVjayh3b3JkKSlcclxuXHRcdFx0XHRyZXR1cm4gXCJzcGVsbC1lcnJvclwiOyAvLyBDU1MgY2xhc3M6IGNtLXNwZWxsLWVycm9yXHJcblxyXG5cdFx0XHRyZXR1cm4gbnVsbDtcclxuXHRcdH1cclxuXHR9O1xyXG5cclxuXHR2YXIgbW9kZSA9IENvZGVNaXJyb3IuZ2V0TW9kZShcclxuXHRcdGNvbmZpZywgY29uZmlnLmJhY2tkcm9wIHx8IFwidGV4dC9wbGFpblwiXHJcblx0KTtcclxuXHJcblx0cmV0dXJuIENvZGVNaXJyb3Iub3ZlcmxheU1vZGUobW9kZSwgb3ZlcmxheSwgdHJ1ZSk7XHJcbn0pO1xyXG5cclxuXHJcbi8vIEJlY2F1c2Ugc29tZSBicm93c2VycyBkb24ndCBzdXBwb3J0IHRoaXMgZnVuY3Rpb25hbGl0eSB5ZXRcclxuaWYoIVN0cmluZy5wcm90b3R5cGUuaW5jbHVkZXMpIHtcclxuXHRTdHJpbmcucHJvdG90eXBlLmluY2x1ZGVzID0gZnVuY3Rpb24oKSB7J3VzZSBzdHJpY3QnO1xyXG5cdFx0cmV0dXJuIFN0cmluZy5wcm90b3R5cGUuaW5kZXhPZi5hcHBseSh0aGlzLCBhcmd1bWVudHMpICE9PSAtMTtcclxuXHR9O1xyXG59XG59KS5jYWxsKGdsb2JhbCwgbW9kdWxlLCB1bmRlZmluZWQsIHVuZGVmaW5lZCk7XG4iLCI7IHZhciBfX2Jyb3dzZXJpZnlfc2hpbV9yZXF1aXJlX189cmVxdWlyZTsoZnVuY3Rpb24gYnJvd3NlcmlmeVNoaW0obW9kdWxlLCBleHBvcnRzLCByZXF1aXJlLCBkZWZpbmUsIGJyb3dzZXJpZnlfc2hpbV9fZGVmaW5lX19tb2R1bGVfX2V4cG9ydF9fKSB7XG4ndXNlIHN0cmljdCc7XHJcblxyXG4vKipcclxuICogVHlwbyBpcyBhIEphdmFTY3JpcHQgaW1wbGVtZW50YXRpb24gb2YgYSBzcGVsbGNoZWNrZXIgdXNpbmcgaHVuc3BlbGwtc3R5bGVcclxuICogZGljdGlvbmFyaWVzLlxyXG4gKi9cclxuXHJcbi8qKlxyXG4gKiBUeXBvIGNvbnN0cnVjdG9yLlxyXG4gKlxyXG4gKiBAcGFyYW0ge1N0cmluZ30gW2RpY3Rpb25hcnldIFRoZSBsb2NhbGUgY29kZSBvZiB0aGUgZGljdGlvbmFyeSBiZWluZyB1c2VkLiBlLmcuLFxyXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwiZW5fVVNcIi4gVGhpcyBpcyBvbmx5IHVzZWQgdG8gYXV0by1sb2FkIGRpY3Rpb25hcmllcy5cclxuICogQHBhcmFtIHtTdHJpbmd9IFthZmZEYXRhXSBUaGUgZGF0YSBmcm9tIHRoZSBkaWN0aW9uYXJ5J3MgLmFmZiBmaWxlLiBJZiBvbWl0dGVkXHJcbiAqICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5kIHRoZSBmaXJzdCBhcmd1bWVudCBpcyBzdXBwbGllZCwgaW4gXCJjaHJvbWVcIiBwbGF0Zm9ybSxcclxuICogICAgICAgICAgICAgICAgICAgICAgICAgICB0aGUgLmFmZiBmaWxlIHdpbGwgYmUgbG9hZGVkIGF1dG9tYXRpY2FsbHkgZnJvbVxyXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpYi90eXBvL2RpY3Rpb25hcmllcy9bZGljdGlvbmFyeV0vW2RpY3Rpb25hcnldLmFmZlxyXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgIEluIG90aGVyIHBsYXRmb3JtLCBpdCB3aWxsIGJlIGxvYWRlZCBmcm9tXHJcbiAqICAgICAgICAgICAgICAgICAgICAgICAgICAgW3NldHRpbmcucGF0aF0vZGljdGlvbmFyaWVzL1tkaWN0aW9uYXJ5XS9bZGljdGlvbmFyeV0uYWZmXHJcbiAqIEBwYXJhbSB7U3RyaW5nfSBbd29yZHNEYXRhXSBUaGUgZGF0YSBmcm9tIHRoZSBkaWN0aW9uYXJ5J3MgLmRpYyBmaWxlLiBJZiBvbWl0dGVkLFxyXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuZCB0aGUgZmlyc3QgYXJndW1lbnQgaXMgc3VwcGxpZWQsIGluIFwiY2hyb21lXCIgcGxhdGZvcm0sXHJcbiAqICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlIC5kaWMgZmlsZSB3aWxsIGJlIGxvYWRlZCBhdXRvbWF0aWNhbGx5IGZyb21cclxuICogICAgICAgICAgICAgICAgICAgICAgICAgICBsaWIvdHlwby9kaWN0aW9uYXJpZXMvW2RpY3Rpb25hcnldL1tkaWN0aW9uYXJ5XS5kaWNcclxuICogICAgICAgICAgICAgICAgICAgICAgICAgICBJbiBvdGhlciBwbGF0Zm9ybSwgaXQgd2lsbCBiZSBsb2FkZWQgZnJvbVxyXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgIFtzZXR0aW5nLnBhdGhdL2RpY3Rpb25hcmllcy9bZGljdGlvbmFyeV0vW2RpY3Rpb25hcnldLmRpY1xyXG4gKiBAcGFyYW0ge09iamVjdH0gW3NldHRpbmdzXSBDb25zdHJ1Y3RvciBzZXR0aW5ncy4gQXZhaWxhYmxlIHByb3BlcnRpZXMgYXJlOlxyXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7U3RyaW5nfSBbcGxhdGZvcm1dOiBcImNocm9tZVwiIGZvciBDaHJvbWUgRXh0ZW5zaW9uIG9yIG90aGVyXHJcbiAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgZm9yIHRoZSB1c3VhbCB3ZWIuXHJcbiAqICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtTdHJpbmd9IFtkaWN0aW9uYXJ5UGF0aF06IHBhdGggdG8gbG9hZCBkaWN0aW9uYXJ5IGZyb20gaW4gbm9uLWNocm9tZVxyXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVudmlyb25tZW50LlxyXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7T2JqZWN0fSBbZmxhZ3NdOiBmbGFnIGluZm9ybWF0aW9uLlxyXG4gKlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7VHlwb30gQSBUeXBvIG9iamVjdC5cclxuICovXHJcblxyXG52YXIgVHlwbyA9IGZ1bmN0aW9uIChkaWN0aW9uYXJ5LCBhZmZEYXRhLCB3b3Jkc0RhdGEsIHNldHRpbmdzKSB7XHJcblx0c2V0dGluZ3MgPSBzZXR0aW5ncyB8fCB7fTtcclxuXHRcclxuXHQvKiogRGV0ZXJtaW5lcyB0aGUgbWV0aG9kIHVzZWQgZm9yIGF1dG8tbG9hZGluZyAuYWZmIGFuZCAuZGljIGZpbGVzLiAqKi9cclxuXHR0aGlzLnBsYXRmb3JtID0gc2V0dGluZ3MucGxhdGZvcm0gfHwgXCJjaHJvbWVcIjtcclxuXHRcclxuXHR0aGlzLmRpY3Rpb25hcnkgPSBudWxsO1xyXG5cdFxyXG5cdHRoaXMucnVsZXMgPSB7fTtcclxuXHR0aGlzLmRpY3Rpb25hcnlUYWJsZSA9IHt9O1xyXG5cdFxyXG5cdHRoaXMuY29tcG91bmRSdWxlcyA9IFtdO1xyXG5cdHRoaXMuY29tcG91bmRSdWxlQ29kZXMgPSB7fTtcclxuXHRcclxuXHR0aGlzLnJlcGxhY2VtZW50VGFibGUgPSBbXTtcclxuXHRcclxuXHR0aGlzLmZsYWdzID0gc2V0dGluZ3MuZmxhZ3MgfHwge307XHJcblx0XHJcblx0aWYgKGRpY3Rpb25hcnkpIHtcclxuXHRcdHRoaXMuZGljdGlvbmFyeSA9IGRpY3Rpb25hcnk7XHJcblx0XHRcclxuXHRcdGlmICh0aGlzLnBsYXRmb3JtID09IFwiY2hyb21lXCIpIHtcclxuXHRcdFx0aWYgKCFhZmZEYXRhKSBhZmZEYXRhID0gdGhpcy5fcmVhZEZpbGUoY2hyb21lLmV4dGVuc2lvbi5nZXRVUkwoXCJsaWIvdHlwby9kaWN0aW9uYXJpZXMvXCIgKyBkaWN0aW9uYXJ5ICsgXCIvXCIgKyBkaWN0aW9uYXJ5ICsgXCIuYWZmXCIpKTtcclxuXHRcdFx0aWYgKCF3b3Jkc0RhdGEpIHdvcmRzRGF0YSA9IHRoaXMuX3JlYWRGaWxlKGNocm9tZS5leHRlbnNpb24uZ2V0VVJMKFwibGliL3R5cG8vZGljdGlvbmFyaWVzL1wiICsgZGljdGlvbmFyeSArIFwiL1wiICsgZGljdGlvbmFyeSArIFwiLmRpY1wiKSk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR2YXIgcGF0aCA9IHNldHRpbmdzLmRpY3Rpb25hcnlQYXRoIHx8ICcnO1xyXG5cdFx0XHRcclxuXHRcdFx0aWYgKCFhZmZEYXRhKSBhZmZEYXRhID0gdGhpcy5fcmVhZEZpbGUocGF0aCArIFwiL1wiICsgZGljdGlvbmFyeSArIFwiL1wiICsgZGljdGlvbmFyeSArIFwiLmFmZlwiKTtcclxuXHRcdFx0aWYgKCF3b3Jkc0RhdGEpIHdvcmRzRGF0YSA9IHRoaXMuX3JlYWRGaWxlKHBhdGggKyBcIi9cIiArIGRpY3Rpb25hcnkgKyBcIi9cIiArIGRpY3Rpb25hcnkgKyBcIi5kaWNcIik7XHJcblx0XHR9XHJcblx0XHRcclxuXHRcdHRoaXMucnVsZXMgPSB0aGlzLl9wYXJzZUFGRihhZmZEYXRhKTtcclxuXHRcdFxyXG5cdFx0Ly8gU2F2ZSB0aGUgcnVsZSBjb2RlcyB0aGF0IGFyZSB1c2VkIGluIGNvbXBvdW5kIHJ1bGVzLlxyXG5cdFx0dGhpcy5jb21wb3VuZFJ1bGVDb2RlcyA9IHt9O1xyXG5cdFx0XHJcblx0XHRmb3IgKHZhciBpID0gMCwgX2xlbiA9IHRoaXMuY29tcG91bmRSdWxlcy5sZW5ndGg7IGkgPCBfbGVuOyBpKyspIHtcclxuXHRcdFx0dmFyIHJ1bGUgPSB0aGlzLmNvbXBvdW5kUnVsZXNbaV07XHJcblx0XHRcdFxyXG5cdFx0XHRmb3IgKHZhciBqID0gMCwgX2psZW4gPSBydWxlLmxlbmd0aDsgaiA8IF9qbGVuOyBqKyspIHtcclxuXHRcdFx0XHR0aGlzLmNvbXBvdW5kUnVsZUNvZGVzW3J1bGVbal1dID0gW107XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHRcdFxyXG5cdFx0Ly8gSWYgd2UgYWRkIHRoaXMgT05MWUlOQ09NUE9VTkQgZmxhZyB0byB0aGlzLmNvbXBvdW5kUnVsZUNvZGVzLCB0aGVuIF9wYXJzZURJQ1xyXG5cdFx0Ly8gd2lsbCBkbyB0aGUgd29yayBvZiBzYXZpbmcgdGhlIGxpc3Qgb2Ygd29yZHMgdGhhdCBhcmUgY29tcG91bmQtb25seS5cclxuXHRcdGlmIChcIk9OTFlJTkNPTVBPVU5EXCIgaW4gdGhpcy5mbGFncykge1xyXG5cdFx0XHR0aGlzLmNvbXBvdW5kUnVsZUNvZGVzW3RoaXMuZmxhZ3MuT05MWUlOQ09NUE9VTkRdID0gW107XHJcblx0XHR9XHJcblx0XHRcclxuXHRcdHRoaXMuZGljdGlvbmFyeVRhYmxlID0gdGhpcy5fcGFyc2VESUMod29yZHNEYXRhKTtcclxuXHRcdFxyXG5cdFx0Ly8gR2V0IHJpZCBvZiBhbnkgY29kZXMgZnJvbSB0aGUgY29tcG91bmQgcnVsZSBjb2RlcyB0aGF0IGFyZSBuZXZlciB1c2VkXHJcblx0XHQvLyAob3IgdGhhdCB3ZXJlIHNwZWNpYWwgcmVnZXggY2hhcmFjdGVycykuICBOb3QgZXNwZWNpYWxseSBuZWNlc3NhcnkuLi5cclxuXHRcdGZvciAodmFyIGkgaW4gdGhpcy5jb21wb3VuZFJ1bGVDb2Rlcykge1xyXG5cdFx0XHRpZiAodGhpcy5jb21wb3VuZFJ1bGVDb2Rlc1tpXS5sZW5ndGggPT0gMCkge1xyXG5cdFx0XHRcdGRlbGV0ZSB0aGlzLmNvbXBvdW5kUnVsZUNvZGVzW2ldO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0XHRcclxuXHRcdC8vIEJ1aWxkIHRoZSBmdWxsIHJlZ3VsYXIgZXhwcmVzc2lvbnMgZm9yIGVhY2ggY29tcG91bmQgcnVsZS5cclxuXHRcdC8vIEkgaGF2ZSBhIGZlZWxpbmcgKGJ1dCBubyBjb25maXJtYXRpb24geWV0KSB0aGF0IHRoaXMgbWV0aG9kIG9mXHJcblx0XHQvLyB0ZXN0aW5nIGZvciBjb21wb3VuZCB3b3JkcyBpcyBwcm9iYWJseSBzbG93LlxyXG5cdFx0Zm9yICh2YXIgaSA9IDAsIF9sZW4gPSB0aGlzLmNvbXBvdW5kUnVsZXMubGVuZ3RoOyBpIDwgX2xlbjsgaSsrKSB7XHJcblx0XHRcdHZhciBydWxlVGV4dCA9IHRoaXMuY29tcG91bmRSdWxlc1tpXTtcclxuXHRcdFx0XHJcblx0XHRcdHZhciBleHByZXNzaW9uVGV4dCA9IFwiXCI7XHJcblx0XHRcdFxyXG5cdFx0XHRmb3IgKHZhciBqID0gMCwgX2psZW4gPSBydWxlVGV4dC5sZW5ndGg7IGogPCBfamxlbjsgaisrKSB7XHJcblx0XHRcdFx0dmFyIGNoYXJhY3RlciA9IHJ1bGVUZXh0W2pdO1xyXG5cdFx0XHRcdFxyXG5cdFx0XHRcdGlmIChjaGFyYWN0ZXIgaW4gdGhpcy5jb21wb3VuZFJ1bGVDb2Rlcykge1xyXG5cdFx0XHRcdFx0ZXhwcmVzc2lvblRleHQgKz0gXCIoXCIgKyB0aGlzLmNvbXBvdW5kUnVsZUNvZGVzW2NoYXJhY3Rlcl0uam9pbihcInxcIikgKyBcIilcIjtcclxuXHRcdFx0XHR9XHJcblx0XHRcdFx0ZWxzZSB7XHJcblx0XHRcdFx0XHRleHByZXNzaW9uVGV4dCArPSBjaGFyYWN0ZXI7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHRcdFxyXG5cdFx0XHR0aGlzLmNvbXBvdW5kUnVsZXNbaV0gPSBuZXcgUmVnRXhwKGV4cHJlc3Npb25UZXh0LCBcImlcIik7XHJcblx0XHR9XHJcblx0fVxyXG5cdFxyXG5cdHJldHVybiB0aGlzO1xyXG59O1xyXG5cclxuVHlwby5wcm90b3R5cGUgPSB7XHJcblx0LyoqXHJcblx0ICogTG9hZHMgYSBUeXBvIGluc3RhbmNlIGZyb20gYSBoYXNoIG9mIGFsbCBvZiB0aGUgVHlwbyBwcm9wZXJ0aWVzLlxyXG5cdCAqXHJcblx0ICogQHBhcmFtIG9iamVjdCBvYmogQSBoYXNoIG9mIFR5cG8gcHJvcGVydGllcywgcHJvYmFibHkgZ290dGVuIGZyb20gYSBKU09OLnBhcnNlKEpTT04uc3RyaW5naWZ5KHR5cG9faW5zdGFuY2UpKS5cclxuXHQgKi9cclxuXHRcclxuXHRsb2FkIDogZnVuY3Rpb24gKG9iaikge1xyXG5cdFx0Zm9yICh2YXIgaSBpbiBvYmopIHtcclxuXHRcdFx0dGhpc1tpXSA9IG9ialtpXTtcclxuXHRcdH1cclxuXHRcdFxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHRcclxuXHQvKipcclxuXHQgKiBSZWFkIHRoZSBjb250ZW50cyBvZiBhIGZpbGUuXHJcblx0ICpcclxuXHQgKiBAcGFyYW0ge1N0cmluZ30gcGF0aCBUaGUgcGF0aCAocmVsYXRpdmUpIHRvIHRoZSBmaWxlLlxyXG5cdCAqIEBwYXJhbSB7U3RyaW5nfSBbY2hhcnNldD1cIklTTzg4NTktMVwiXSBUaGUgZXhwZWN0ZWQgY2hhcnNldCBvZiB0aGUgZmlsZVxyXG5cdCAqIEByZXR1cm5zIHN0cmluZyBUaGUgZmlsZSBkYXRhLlxyXG5cdCAqL1xyXG5cdFxyXG5cdF9yZWFkRmlsZSA6IGZ1bmN0aW9uIChwYXRoLCBjaGFyc2V0KSB7XHJcblx0XHRpZiAoIWNoYXJzZXQpIGNoYXJzZXQgPSBcIklTTzg4NTktMVwiO1xyXG5cdFx0XHJcblx0XHR2YXIgcmVxID0gbmV3IFhNTEh0dHBSZXF1ZXN0KCk7XHJcblx0XHRyZXEub3BlbihcIkdFVFwiLCBwYXRoLCBmYWxzZSk7XHJcblx0XHRcclxuXHRcdGlmIChyZXEub3ZlcnJpZGVNaW1lVHlwZSlcclxuXHRcdFx0cmVxLm92ZXJyaWRlTWltZVR5cGUoXCJ0ZXh0L3BsYWluOyBjaGFyc2V0PVwiICsgY2hhcnNldCk7XHJcblx0XHRcclxuXHRcdHJlcS5zZW5kKG51bGwpO1xyXG5cdFx0XHJcblx0XHRyZXR1cm4gcmVxLnJlc3BvbnNlVGV4dDtcclxuXHR9LFxyXG5cdFxyXG5cdC8qKlxyXG5cdCAqIFBhcnNlIHRoZSBydWxlcyBvdXQgZnJvbSBhIC5hZmYgZmlsZS5cclxuXHQgKlxyXG5cdCAqIEBwYXJhbSB7U3RyaW5nfSBkYXRhIFRoZSBjb250ZW50cyBvZiB0aGUgYWZmaXggZmlsZS5cclxuXHQgKiBAcmV0dXJucyBvYmplY3QgVGhlIHJ1bGVzIGZyb20gdGhlIGZpbGUuXHJcblx0ICovXHJcblx0XHJcblx0X3BhcnNlQUZGIDogZnVuY3Rpb24gKGRhdGEpIHtcclxuXHRcdHZhciBydWxlcyA9IHt9O1xyXG5cdFx0XHJcblx0XHQvLyBSZW1vdmUgY29tbWVudCBsaW5lc1xyXG5cdFx0ZGF0YSA9IHRoaXMuX3JlbW92ZUFmZml4Q29tbWVudHMoZGF0YSk7XHJcblx0XHRcclxuXHRcdHZhciBsaW5lcyA9IGRhdGEuc3BsaXQoXCJcXG5cIik7XHJcblx0XHRcclxuXHRcdGZvciAodmFyIGkgPSAwLCBfbGVuID0gbGluZXMubGVuZ3RoOyBpIDwgX2xlbjsgaSsrKSB7XHJcblx0XHRcdHZhciBsaW5lID0gbGluZXNbaV07XHJcblx0XHRcdFxyXG5cdFx0XHR2YXIgZGVmaW5pdGlvblBhcnRzID0gbGluZS5zcGxpdCgvXFxzKy8pO1xyXG5cdFx0XHRcclxuXHRcdFx0dmFyIHJ1bGVUeXBlID0gZGVmaW5pdGlvblBhcnRzWzBdO1xyXG5cdFx0XHRcclxuXHRcdFx0aWYgKHJ1bGVUeXBlID09IFwiUEZYXCIgfHwgcnVsZVR5cGUgPT0gXCJTRlhcIikge1xyXG5cdFx0XHRcdHZhciBydWxlQ29kZSA9IGRlZmluaXRpb25QYXJ0c1sxXTtcclxuXHRcdFx0XHR2YXIgY29tYmluZWFibGUgPSBkZWZpbml0aW9uUGFydHNbMl07XHJcblx0XHRcdFx0dmFyIG51bUVudHJpZXMgPSBwYXJzZUludChkZWZpbml0aW9uUGFydHNbM10sIDEwKTtcclxuXHRcdFx0XHRcclxuXHRcdFx0XHR2YXIgZW50cmllcyA9IFtdO1xyXG5cdFx0XHRcdFxyXG5cdFx0XHRcdGZvciAodmFyIGogPSBpICsgMSwgX2psZW4gPSBpICsgMSArIG51bUVudHJpZXM7IGogPCBfamxlbjsgaisrKSB7XHJcblx0XHRcdFx0XHR2YXIgbGluZSA9IGxpbmVzW2pdO1xyXG5cdFx0XHRcdFx0XHJcblx0XHRcdFx0XHR2YXIgbGluZVBhcnRzID0gbGluZS5zcGxpdCgvXFxzKy8pO1xyXG5cdFx0XHRcdFx0dmFyIGNoYXJhY3RlcnNUb1JlbW92ZSA9IGxpbmVQYXJ0c1syXTtcclxuXHRcdFx0XHRcdFxyXG5cdFx0XHRcdFx0dmFyIGFkZGl0aW9uUGFydHMgPSBsaW5lUGFydHNbM10uc3BsaXQoXCIvXCIpO1xyXG5cdFx0XHRcdFx0XHJcblx0XHRcdFx0XHR2YXIgY2hhcmFjdGVyc1RvQWRkID0gYWRkaXRpb25QYXJ0c1swXTtcclxuXHRcdFx0XHRcdGlmIChjaGFyYWN0ZXJzVG9BZGQgPT09IFwiMFwiKSBjaGFyYWN0ZXJzVG9BZGQgPSBcIlwiO1xyXG5cdFx0XHRcdFx0XHJcblx0XHRcdFx0XHR2YXIgY29udGludWF0aW9uQ2xhc3NlcyA9IHRoaXMucGFyc2VSdWxlQ29kZXMoYWRkaXRpb25QYXJ0c1sxXSk7XHJcblx0XHRcdFx0XHRcclxuXHRcdFx0XHRcdHZhciByZWdleFRvTWF0Y2ggPSBsaW5lUGFydHNbNF07XHJcblx0XHRcdFx0XHRcclxuXHRcdFx0XHRcdHZhciBlbnRyeSA9IHt9O1xyXG5cdFx0XHRcdFx0ZW50cnkuYWRkID0gY2hhcmFjdGVyc1RvQWRkO1xyXG5cdFx0XHRcdFx0XHJcblx0XHRcdFx0XHRpZiAoY29udGludWF0aW9uQ2xhc3Nlcy5sZW5ndGggPiAwKSBlbnRyeS5jb250aW51YXRpb25DbGFzc2VzID0gY29udGludWF0aW9uQ2xhc3NlcztcclxuXHRcdFx0XHRcdFxyXG5cdFx0XHRcdFx0aWYgKHJlZ2V4VG9NYXRjaCAhPT0gXCIuXCIpIHtcclxuXHRcdFx0XHRcdFx0aWYgKHJ1bGVUeXBlID09PSBcIlNGWFwiKSB7XHJcblx0XHRcdFx0XHRcdFx0ZW50cnkubWF0Y2ggPSBuZXcgUmVnRXhwKHJlZ2V4VG9NYXRjaCArIFwiJFwiKTtcclxuXHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0XHRlbHNlIHtcclxuXHRcdFx0XHRcdFx0XHRlbnRyeS5tYXRjaCA9IG5ldyBSZWdFeHAoXCJeXCIgKyByZWdleFRvTWF0Y2gpO1xyXG5cdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRcclxuXHRcdFx0XHRcdGlmIChjaGFyYWN0ZXJzVG9SZW1vdmUgIT0gXCIwXCIpIHtcclxuXHRcdFx0XHRcdFx0aWYgKHJ1bGVUeXBlID09PSBcIlNGWFwiKSB7XHJcblx0XHRcdFx0XHRcdFx0ZW50cnkucmVtb3ZlID0gbmV3IFJlZ0V4cChjaGFyYWN0ZXJzVG9SZW1vdmUgICsgXCIkXCIpO1xyXG5cdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRcdGVsc2Uge1xyXG5cdFx0XHRcdFx0XHRcdGVudHJ5LnJlbW92ZSA9IGNoYXJhY3RlcnNUb1JlbW92ZTtcclxuXHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0XHJcblx0XHRcdFx0XHRlbnRyaWVzLnB1c2goZW50cnkpO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHRcclxuXHRcdFx0XHRydWxlc1tydWxlQ29kZV0gPSB7IFwidHlwZVwiIDogcnVsZVR5cGUsIFwiY29tYmluZWFibGVcIiA6IChjb21iaW5lYWJsZSA9PSBcIllcIiksIFwiZW50cmllc1wiIDogZW50cmllcyB9O1xyXG5cdFx0XHRcdFxyXG5cdFx0XHRcdGkgKz0gbnVtRW50cmllcztcclxuXHRcdFx0fVxyXG5cdFx0XHRlbHNlIGlmIChydWxlVHlwZSA9PT0gXCJDT01QT1VORFJVTEVcIikge1xyXG5cdFx0XHRcdHZhciBudW1FbnRyaWVzID0gcGFyc2VJbnQoZGVmaW5pdGlvblBhcnRzWzFdLCAxMCk7XHJcblx0XHRcdFx0XHJcblx0XHRcdFx0Zm9yICh2YXIgaiA9IGkgKyAxLCBfamxlbiA9IGkgKyAxICsgbnVtRW50cmllczsgaiA8IF9qbGVuOyBqKyspIHtcclxuXHRcdFx0XHRcdHZhciBsaW5lID0gbGluZXNbal07XHJcblx0XHRcdFx0XHRcclxuXHRcdFx0XHRcdHZhciBsaW5lUGFydHMgPSBsaW5lLnNwbGl0KC9cXHMrLyk7XHJcblx0XHRcdFx0XHR0aGlzLmNvbXBvdW5kUnVsZXMucHVzaChsaW5lUGFydHNbMV0pO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHRcclxuXHRcdFx0XHRpICs9IG51bUVudHJpZXM7XHJcblx0XHRcdH1cclxuXHRcdFx0ZWxzZSBpZiAocnVsZVR5cGUgPT09IFwiUkVQXCIpIHtcclxuXHRcdFx0XHR2YXIgbGluZVBhcnRzID0gbGluZS5zcGxpdCgvXFxzKy8pO1xyXG5cdFx0XHRcdFxyXG5cdFx0XHRcdGlmIChsaW5lUGFydHMubGVuZ3RoID09PSAzKSB7XHJcblx0XHRcdFx0XHR0aGlzLnJlcGxhY2VtZW50VGFibGUucHVzaChbIGxpbmVQYXJ0c1sxXSwgbGluZVBhcnRzWzJdIF0pO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fVxyXG5cdFx0XHRlbHNlIHtcclxuXHRcdFx0XHQvLyBPTkxZSU5DT01QT1VORFxyXG5cdFx0XHRcdC8vIENPTVBPVU5ETUlOXHJcblx0XHRcdFx0Ly8gRkxBR1xyXG5cdFx0XHRcdC8vIEtFRVBDQVNFXHJcblx0XHRcdFx0Ly8gTkVFREFGRklYXHJcblx0XHRcdFx0XHJcblx0XHRcdFx0dGhpcy5mbGFnc1tydWxlVHlwZV0gPSBkZWZpbml0aW9uUGFydHNbMV07XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHRcdFxyXG5cdFx0cmV0dXJuIHJ1bGVzO1xyXG5cdH0sXHJcblx0XHJcblx0LyoqXHJcblx0ICogUmVtb3ZlcyBjb21tZW50IGxpbmVzIGFuZCB0aGVuIGNsZWFucyB1cCBibGFuayBsaW5lcyBhbmQgdHJhaWxpbmcgd2hpdGVzcGFjZS5cclxuXHQgKlxyXG5cdCAqIEBwYXJhbSB7U3RyaW5nfSBkYXRhIFRoZSBkYXRhIGZyb20gYW4gYWZmaXggZmlsZS5cclxuXHQgKiBAcmV0dXJuIHtTdHJpbmd9IFRoZSBjbGVhbmVkLXVwIGRhdGEuXHJcblx0ICovXHJcblx0XHJcblx0X3JlbW92ZUFmZml4Q29tbWVudHMgOiBmdW5jdGlvbiAoZGF0YSkge1xyXG5cdFx0Ly8gUmVtb3ZlIGNvbW1lbnRzXHJcblx0XHRkYXRhID0gZGF0YS5yZXBsYWNlKC8jLiokL21nLCBcIlwiKTtcclxuXHRcdFxyXG5cdFx0Ly8gVHJpbSBlYWNoIGxpbmVcclxuXHRcdGRhdGEgPSBkYXRhLnJlcGxhY2UoL15cXHNcXHMqL20sICcnKS5yZXBsYWNlKC9cXHNcXHMqJC9tLCAnJyk7XHJcblx0XHRcclxuXHRcdC8vIFJlbW92ZSBibGFuayBsaW5lcy5cclxuXHRcdGRhdGEgPSBkYXRhLnJlcGxhY2UoL1xcbnsyLH0vZywgXCJcXG5cIik7XHJcblx0XHRcclxuXHRcdC8vIFRyaW0gdGhlIGVudGlyZSBzdHJpbmdcclxuXHRcdGRhdGEgPSBkYXRhLnJlcGxhY2UoL15cXHNcXHMqLywgJycpLnJlcGxhY2UoL1xcc1xccyokLywgJycpO1xyXG5cdFx0XHJcblx0XHRyZXR1cm4gZGF0YTtcclxuXHR9LFxyXG5cdFxyXG5cdC8qKlxyXG5cdCAqIFBhcnNlcyB0aGUgd29yZHMgb3V0IGZyb20gdGhlIC5kaWMgZmlsZS5cclxuXHQgKlxyXG5cdCAqIEBwYXJhbSB7U3RyaW5nfSBkYXRhIFRoZSBkYXRhIGZyb20gdGhlIGRpY3Rpb25hcnkgZmlsZS5cclxuXHQgKiBAcmV0dXJucyBvYmplY3QgVGhlIGxvb2t1cCB0YWJsZSBjb250YWluaW5nIGFsbCBvZiB0aGUgd29yZHMgYW5kXHJcblx0ICogICAgICAgICAgICAgICAgIHdvcmQgZm9ybXMgZnJvbSB0aGUgZGljdGlvbmFyeS5cclxuXHQgKi9cclxuXHRcclxuXHRfcGFyc2VESUMgOiBmdW5jdGlvbiAoZGF0YSkge1xyXG5cdFx0ZGF0YSA9IHRoaXMuX3JlbW92ZURpY0NvbW1lbnRzKGRhdGEpO1xyXG5cdFx0XHJcblx0XHR2YXIgbGluZXMgPSBkYXRhLnNwbGl0KFwiXFxuXCIpO1xyXG5cdFx0dmFyIGRpY3Rpb25hcnlUYWJsZSA9IHt9O1xyXG5cdFx0XHJcblx0XHRmdW5jdGlvbiBhZGRXb3JkKHdvcmQsIHJ1bGVzKSB7XHJcblx0XHRcdC8vIFNvbWUgZGljdGlvbmFyaWVzIHdpbGwgbGlzdCB0aGUgc2FtZSB3b3JkIG11bHRpcGxlIHRpbWVzIHdpdGggZGlmZmVyZW50IHJ1bGUgc2V0cy5cclxuXHRcdFx0aWYgKCEod29yZCBpbiBkaWN0aW9uYXJ5VGFibGUpIHx8IHR5cGVvZiBkaWN0aW9uYXJ5VGFibGVbd29yZF0gIT0gJ29iamVjdCcpIHtcclxuXHRcdFx0XHRkaWN0aW9uYXJ5VGFibGVbd29yZF0gPSBbXTtcclxuXHRcdFx0fVxyXG5cdFx0XHRcclxuXHRcdFx0ZGljdGlvbmFyeVRhYmxlW3dvcmRdLnB1c2gocnVsZXMpO1xyXG5cdFx0fVxyXG5cdFx0XHJcblx0XHQvLyBUaGUgZmlyc3QgbGluZSBpcyB0aGUgbnVtYmVyIG9mIHdvcmRzIGluIHRoZSBkaWN0aW9uYXJ5LlxyXG5cdFx0Zm9yICh2YXIgaSA9IDEsIF9sZW4gPSBsaW5lcy5sZW5ndGg7IGkgPCBfbGVuOyBpKyspIHtcclxuXHRcdFx0dmFyIGxpbmUgPSBsaW5lc1tpXTtcclxuXHRcdFx0XHJcblx0XHRcdHZhciBwYXJ0cyA9IGxpbmUuc3BsaXQoXCIvXCIsIDIpO1xyXG5cdFx0XHRcclxuXHRcdFx0dmFyIHdvcmQgPSBwYXJ0c1swXTtcclxuXHJcblx0XHRcdC8vIE5vdyBmb3IgZWFjaCBhZmZpeCBydWxlLCBnZW5lcmF0ZSB0aGF0IGZvcm0gb2YgdGhlIHdvcmQuXHJcblx0XHRcdGlmIChwYXJ0cy5sZW5ndGggPiAxKSB7XHJcblx0XHRcdFx0dmFyIHJ1bGVDb2Rlc0FycmF5ID0gdGhpcy5wYXJzZVJ1bGVDb2RlcyhwYXJ0c1sxXSk7XHJcblx0XHRcdFx0XHJcblx0XHRcdFx0Ly8gU2F2ZSB0aGUgcnVsZUNvZGVzIGZvciBjb21wb3VuZCB3b3JkIHNpdHVhdGlvbnMuXHJcblx0XHRcdFx0aWYgKCEoXCJORUVEQUZGSVhcIiBpbiB0aGlzLmZsYWdzKSB8fCBydWxlQ29kZXNBcnJheS5pbmRleE9mKHRoaXMuZmxhZ3MuTkVFREFGRklYKSA9PSAtMSkge1xyXG5cdFx0XHRcdFx0YWRkV29yZCh3b3JkLCBydWxlQ29kZXNBcnJheSk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdFxyXG5cdFx0XHRcdGZvciAodmFyIGogPSAwLCBfamxlbiA9IHJ1bGVDb2Rlc0FycmF5Lmxlbmd0aDsgaiA8IF9qbGVuOyBqKyspIHtcclxuXHRcdFx0XHRcdHZhciBjb2RlID0gcnVsZUNvZGVzQXJyYXlbal07XHJcblx0XHRcdFx0XHRcclxuXHRcdFx0XHRcdHZhciBydWxlID0gdGhpcy5ydWxlc1tjb2RlXTtcclxuXHRcdFx0XHRcdFxyXG5cdFx0XHRcdFx0aWYgKHJ1bGUpIHtcclxuXHRcdFx0XHRcdFx0dmFyIG5ld1dvcmRzID0gdGhpcy5fYXBwbHlSdWxlKHdvcmQsIHJ1bGUpO1xyXG5cdFx0XHRcdFx0XHRcclxuXHRcdFx0XHRcdFx0Zm9yICh2YXIgaWkgPSAwLCBfaWlsZW4gPSBuZXdXb3Jkcy5sZW5ndGg7IGlpIDwgX2lpbGVuOyBpaSsrKSB7XHJcblx0XHRcdFx0XHRcdFx0dmFyIG5ld1dvcmQgPSBuZXdXb3Jkc1tpaV07XHJcblx0XHRcdFx0XHRcdFx0XHJcblx0XHRcdFx0XHRcdFx0YWRkV29yZChuZXdXb3JkLCBbXSk7XHJcblx0XHRcdFx0XHRcdFx0XHJcblx0XHRcdFx0XHRcdFx0aWYgKHJ1bGUuY29tYmluZWFibGUpIHtcclxuXHRcdFx0XHRcdFx0XHRcdGZvciAodmFyIGsgPSBqICsgMTsgayA8IF9qbGVuOyBrKyspIHtcclxuXHRcdFx0XHRcdFx0XHRcdFx0dmFyIGNvbWJpbmVDb2RlID0gcnVsZUNvZGVzQXJyYXlba107XHJcblx0XHRcdFx0XHRcdFx0XHRcdFxyXG5cdFx0XHRcdFx0XHRcdFx0XHR2YXIgY29tYmluZVJ1bGUgPSB0aGlzLnJ1bGVzW2NvbWJpbmVDb2RlXTtcclxuXHRcdFx0XHRcdFx0XHRcdFx0XHJcblx0XHRcdFx0XHRcdFx0XHRcdGlmIChjb21iaW5lUnVsZSkge1xyXG5cdFx0XHRcdFx0XHRcdFx0XHRcdGlmIChjb21iaW5lUnVsZS5jb21iaW5lYWJsZSAmJiAocnVsZS50eXBlICE9IGNvbWJpbmVSdWxlLnR5cGUpKSB7XHJcblx0XHRcdFx0XHRcdFx0XHRcdFx0XHR2YXIgb3RoZXJOZXdXb3JkcyA9IHRoaXMuX2FwcGx5UnVsZShuZXdXb3JkLCBjb21iaW5lUnVsZSk7XHJcblx0XHRcdFx0XHRcdFx0XHRcdFx0XHRcclxuXHRcdFx0XHRcdFx0XHRcdFx0XHRcdGZvciAodmFyIGlpaSA9IDAsIF9paWlsZW4gPSBvdGhlck5ld1dvcmRzLmxlbmd0aDsgaWlpIDwgX2lpaWxlbjsgaWlpKyspIHtcclxuXHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0dmFyIG90aGVyTmV3V29yZCA9IG90aGVyTmV3V29yZHNbaWlpXTtcclxuXHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0YWRkV29yZChvdGhlck5ld1dvcmQsIFtdKTtcclxuXHRcdFx0XHRcdFx0XHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdFxyXG5cdFx0XHRcdFx0aWYgKGNvZGUgaW4gdGhpcy5jb21wb3VuZFJ1bGVDb2Rlcykge1xyXG5cdFx0XHRcdFx0XHR0aGlzLmNvbXBvdW5kUnVsZUNvZGVzW2NvZGVdLnB1c2god29yZCk7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHRcdGVsc2Uge1xyXG5cdFx0XHRcdGFkZFdvcmQod29yZC50cmltKCksIFtdKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdFx0XHJcblx0XHRyZXR1cm4gZGljdGlvbmFyeVRhYmxlO1xyXG5cdH0sXHJcblx0XHJcblx0XHJcblx0LyoqXHJcblx0ICogUmVtb3ZlcyBjb21tZW50IGxpbmVzIGFuZCB0aGVuIGNsZWFucyB1cCBibGFuayBsaW5lcyBhbmQgdHJhaWxpbmcgd2hpdGVzcGFjZS5cclxuXHQgKlxyXG5cdCAqIEBwYXJhbSB7U3RyaW5nfSBkYXRhIFRoZSBkYXRhIGZyb20gYSAuZGljIGZpbGUuXHJcblx0ICogQHJldHVybiB7U3RyaW5nfSBUaGUgY2xlYW5lZC11cCBkYXRhLlxyXG5cdCAqL1xyXG5cdFxyXG5cdF9yZW1vdmVEaWNDb21tZW50cyA6IGZ1bmN0aW9uIChkYXRhKSB7XHJcblx0XHQvLyBJIGNhbid0IGZpbmQgYW55IG9mZmljaWFsIGRvY3VtZW50YXRpb24gb24gaXQsIGJ1dCBhdCBsZWFzdCB0aGUgZGVfREVcclxuXHRcdC8vIGRpY3Rpb25hcnkgdXNlcyB0YWItaW5kZW50ZWQgbGluZXMgYXMgY29tbWVudHMuXHJcblx0XHRcclxuXHRcdC8vIFJlbW92ZSBjb21tZW50c1xyXG5cdFx0ZGF0YSA9IGRhdGEucmVwbGFjZSgvXlxcdC4qJC9tZywgXCJcIik7XHJcblx0XHRcclxuXHRcdHJldHVybiBkYXRhO1xyXG5cdFx0XHJcblx0XHQvLyBUcmltIGVhY2ggbGluZVxyXG5cdFx0ZGF0YSA9IGRhdGEucmVwbGFjZSgvXlxcc1xccyovbSwgJycpLnJlcGxhY2UoL1xcc1xccyokL20sICcnKTtcclxuXHRcdFxyXG5cdFx0Ly8gUmVtb3ZlIGJsYW5rIGxpbmVzLlxyXG5cdFx0ZGF0YSA9IGRhdGEucmVwbGFjZSgvXFxuezIsfS9nLCBcIlxcblwiKTtcclxuXHRcdFxyXG5cdFx0Ly8gVHJpbSB0aGUgZW50aXJlIHN0cmluZ1xyXG5cdFx0ZGF0YSA9IGRhdGEucmVwbGFjZSgvXlxcc1xccyovLCAnJykucmVwbGFjZSgvXFxzXFxzKiQvLCAnJyk7XHJcblx0XHRcclxuXHRcdHJldHVybiBkYXRhO1xyXG5cdH0sXHJcblx0XHJcblx0cGFyc2VSdWxlQ29kZXMgOiBmdW5jdGlvbiAodGV4dENvZGVzKSB7XHJcblx0XHRpZiAoIXRleHRDb2Rlcykge1xyXG5cdFx0XHRyZXR1cm4gW107XHJcblx0XHR9XHJcblx0XHRlbHNlIGlmICghKFwiRkxBR1wiIGluIHRoaXMuZmxhZ3MpKSB7XHJcblx0XHRcdHJldHVybiB0ZXh0Q29kZXMuc3BsaXQoXCJcIik7XHJcblx0XHR9XHJcblx0XHRlbHNlIGlmICh0aGlzLmZsYWdzLkZMQUcgPT09IFwibG9uZ1wiKSB7XHJcblx0XHRcdHZhciBmbGFncyA9IFtdO1xyXG5cdFx0XHRcclxuXHRcdFx0Zm9yICh2YXIgaSA9IDAsIF9sZW4gPSB0ZXh0Q29kZXMubGVuZ3RoOyBpIDwgX2xlbjsgaSArPSAyKSB7XHJcblx0XHRcdFx0ZmxhZ3MucHVzaCh0ZXh0Q29kZXMuc3Vic3RyKGksIDIpKTtcclxuXHRcdFx0fVxyXG5cdFx0XHRcclxuXHRcdFx0cmV0dXJuIGZsYWdzO1xyXG5cdFx0fVxyXG5cdFx0ZWxzZSBpZiAodGhpcy5mbGFncy5GTEFHID09PSBcIm51bVwiKSB7XHJcblx0XHRcdHJldHVybiB0ZXh0Q29kZS5zcGxpdChcIixcIik7XHJcblx0XHR9XHJcblx0fSxcclxuXHRcclxuXHQvKipcclxuXHQgKiBBcHBsaWVzIGFuIGFmZml4IHJ1bGUgdG8gYSB3b3JkLlxyXG5cdCAqXHJcblx0ICogQHBhcmFtIHtTdHJpbmd9IHdvcmQgVGhlIGJhc2Ugd29yZC5cclxuXHQgKiBAcGFyYW0ge09iamVjdH0gcnVsZSBUaGUgYWZmaXggcnVsZS5cclxuXHQgKiBAcmV0dXJucyB7U3RyaW5nW119IFRoZSBuZXcgd29yZHMgZ2VuZXJhdGVkIGJ5IHRoZSBydWxlLlxyXG5cdCAqL1xyXG5cdFxyXG5cdF9hcHBseVJ1bGUgOiBmdW5jdGlvbiAod29yZCwgcnVsZSkge1xyXG5cdFx0dmFyIGVudHJpZXMgPSBydWxlLmVudHJpZXM7XHJcblx0XHR2YXIgbmV3V29yZHMgPSBbXTtcclxuXHRcdFxyXG5cdFx0Zm9yICh2YXIgaSA9IDAsIF9sZW4gPSBlbnRyaWVzLmxlbmd0aDsgaSA8IF9sZW47IGkrKykge1xyXG5cdFx0XHR2YXIgZW50cnkgPSBlbnRyaWVzW2ldO1xyXG5cdFx0XHRcclxuXHRcdFx0aWYgKCFlbnRyeS5tYXRjaCB8fCB3b3JkLm1hdGNoKGVudHJ5Lm1hdGNoKSkge1xyXG5cdFx0XHRcdHZhciBuZXdXb3JkID0gd29yZDtcclxuXHRcdFx0XHRcclxuXHRcdFx0XHRpZiAoZW50cnkucmVtb3ZlKSB7XHJcblx0XHRcdFx0XHRuZXdXb3JkID0gbmV3V29yZC5yZXBsYWNlKGVudHJ5LnJlbW92ZSwgXCJcIik7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdFxyXG5cdFx0XHRcdGlmIChydWxlLnR5cGUgPT09IFwiU0ZYXCIpIHtcclxuXHRcdFx0XHRcdG5ld1dvcmQgPSBuZXdXb3JkICsgZW50cnkuYWRkO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHRlbHNlIHtcclxuXHRcdFx0XHRcdG5ld1dvcmQgPSBlbnRyeS5hZGQgKyBuZXdXb3JkO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHRcclxuXHRcdFx0XHRuZXdXb3Jkcy5wdXNoKG5ld1dvcmQpO1xyXG5cdFx0XHRcdFxyXG5cdFx0XHRcdGlmIChcImNvbnRpbnVhdGlvbkNsYXNzZXNcIiBpbiBlbnRyeSkge1xyXG5cdFx0XHRcdFx0Zm9yICh2YXIgaiA9IDAsIF9qbGVuID0gZW50cnkuY29udGludWF0aW9uQ2xhc3Nlcy5sZW5ndGg7IGogPCBfamxlbjsgaisrKSB7XHJcblx0XHRcdFx0XHRcdHZhciBjb250aW51YXRpb25SdWxlID0gdGhpcy5ydWxlc1tlbnRyeS5jb250aW51YXRpb25DbGFzc2VzW2pdXTtcclxuXHRcdFx0XHRcdFx0XHJcblx0XHRcdFx0XHRcdGlmIChjb250aW51YXRpb25SdWxlKSB7XHJcblx0XHRcdFx0XHRcdFx0bmV3V29yZHMgPSBuZXdXb3Jkcy5jb25jYXQodGhpcy5fYXBwbHlSdWxlKG5ld1dvcmQsIGNvbnRpbnVhdGlvblJ1bGUpKTtcclxuXHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0XHQvKlxyXG5cdFx0XHRcdFx0XHRlbHNlIHtcclxuXHRcdFx0XHRcdFx0XHQvLyBUaGlzIHNob3VsZG4ndCBoYXBwZW4sIGJ1dCBpdCBkb2VzLCBhdCBsZWFzdCBpbiB0aGUgZGVfREUgZGljdGlvbmFyeS5cclxuXHRcdFx0XHRcdFx0XHQvLyBJIHRoaW5rIHRoZSBhdXRob3IgbWlzdGFrZW5seSBzdXBwbGllZCBsb3dlci1jYXNlIHJ1bGUgY29kZXMgaW5zdGVhZFxyXG5cdFx0XHRcdFx0XHRcdC8vIG9mIHVwcGVyLWNhc2UuXHJcblx0XHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdFx0Ki9cclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHRcdFxyXG5cdFx0cmV0dXJuIG5ld1dvcmRzO1xyXG5cdH0sXHJcblx0XHJcblx0LyoqXHJcblx0ICogQ2hlY2tzIHdoZXRoZXIgYSB3b3JkIG9yIGEgY2FwaXRhbGl6YXRpb24gdmFyaWFudCBleGlzdHMgaW4gdGhlIGN1cnJlbnQgZGljdGlvbmFyeS5cclxuXHQgKiBUaGUgd29yZCBpcyB0cmltbWVkIGFuZCBzZXZlcmFsIHZhcmlhdGlvbnMgb2YgY2FwaXRhbGl6YXRpb25zIGFyZSBjaGVja2VkLlxyXG5cdCAqIElmIHlvdSB3YW50IHRvIGNoZWNrIGEgd29yZCB3aXRob3V0IGFueSBjaGFuZ2VzIG1hZGUgdG8gaXQsIGNhbGwgY2hlY2tFeGFjdCgpXHJcblx0ICpcclxuXHQgKiBAc2VlIGh0dHA6Ly9ibG9nLnN0ZXZlbmxldml0aGFuLmNvbS9hcmNoaXZlcy9mYXN0ZXItdHJpbS1qYXZhc2NyaXB0IHJlOnRyaW1taW5nIGZ1bmN0aW9uXHJcblx0ICpcclxuXHQgKiBAcGFyYW0ge1N0cmluZ30gYVdvcmQgVGhlIHdvcmQgdG8gY2hlY2suXHJcblx0ICogQHJldHVybnMge0Jvb2xlYW59XHJcblx0ICovXHJcblx0XHJcblx0Y2hlY2sgOiBmdW5jdGlvbiAoYVdvcmQpIHtcclxuXHRcdC8vIFJlbW92ZSBsZWFkaW5nIGFuZCB0cmFpbGluZyB3aGl0ZXNwYWNlXHJcblx0XHR2YXIgdHJpbW1lZFdvcmQgPSBhV29yZC5yZXBsYWNlKC9eXFxzXFxzKi8sICcnKS5yZXBsYWNlKC9cXHNcXHMqJC8sICcnKTtcclxuXHRcdFxyXG5cdFx0aWYgKHRoaXMuY2hlY2tFeGFjdCh0cmltbWVkV29yZCkpIHtcclxuXHRcdFx0cmV0dXJuIHRydWU7XHJcblx0XHR9XHJcblx0XHRcclxuXHRcdC8vIFRoZSBleGFjdCB3b3JkIGlzIG5vdCBpbiB0aGUgZGljdGlvbmFyeS5cclxuXHRcdGlmICh0cmltbWVkV29yZC50b1VwcGVyQ2FzZSgpID09PSB0cmltbWVkV29yZCkge1xyXG5cdFx0XHQvLyBUaGUgd29yZCB3YXMgc3VwcGxpZWQgaW4gYWxsIHVwcGVyY2FzZS5cclxuXHRcdFx0Ly8gQ2hlY2sgZm9yIGEgY2FwaXRhbGl6ZWQgZm9ybSBvZiB0aGUgd29yZC5cclxuXHRcdFx0dmFyIGNhcGl0YWxpemVkV29yZCA9IHRyaW1tZWRXb3JkWzBdICsgdHJpbW1lZFdvcmQuc3Vic3RyaW5nKDEpLnRvTG93ZXJDYXNlKCk7XHJcblx0XHRcdFxyXG5cdFx0XHRpZiAodGhpcy5oYXNGbGFnKGNhcGl0YWxpemVkV29yZCwgXCJLRUVQQ0FTRVwiKSkge1xyXG5cdFx0XHRcdC8vIENhcGl0YWxpemF0aW9uIHZhcmlhbnRzIGFyZSBub3QgYWxsb3dlZCBmb3IgdGhpcyB3b3JkLlxyXG5cdFx0XHRcdHJldHVybiBmYWxzZTtcclxuXHRcdFx0fVxyXG5cdFx0XHRcclxuXHRcdFx0aWYgKHRoaXMuY2hlY2tFeGFjdChjYXBpdGFsaXplZFdvcmQpKSB7XHJcblx0XHRcdFx0cmV0dXJuIHRydWU7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHRcdFxyXG5cdFx0dmFyIGxvd2VyY2FzZVdvcmQgPSB0cmltbWVkV29yZC50b0xvd2VyQ2FzZSgpO1xyXG5cdFx0XHJcblx0XHRpZiAobG93ZXJjYXNlV29yZCAhPT0gdHJpbW1lZFdvcmQpIHtcclxuXHRcdFx0aWYgKHRoaXMuaGFzRmxhZyhsb3dlcmNhc2VXb3JkLCBcIktFRVBDQVNFXCIpKSB7XHJcblx0XHRcdFx0Ly8gQ2FwaXRhbGl6YXRpb24gdmFyaWFudHMgYXJlIG5vdCBhbGxvd2VkIGZvciB0aGlzIHdvcmQuXHJcblx0XHRcdFx0cmV0dXJuIGZhbHNlO1xyXG5cdFx0XHR9XHJcblx0XHRcdFxyXG5cdFx0XHQvLyBDaGVjayBmb3IgYSBsb3dlcmNhc2UgZm9ybVxyXG5cdFx0XHRpZiAodGhpcy5jaGVja0V4YWN0KGxvd2VyY2FzZVdvcmQpKSB7XHJcblx0XHRcdFx0cmV0dXJuIHRydWU7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHRcdFxyXG5cdFx0cmV0dXJuIGZhbHNlO1xyXG5cdH0sXHJcblx0XHJcblx0LyoqXHJcblx0ICogQ2hlY2tzIHdoZXRoZXIgYSB3b3JkIGV4aXN0cyBpbiB0aGUgY3VycmVudCBkaWN0aW9uYXJ5LlxyXG5cdCAqXHJcblx0ICogQHBhcmFtIHtTdHJpbmd9IHdvcmQgVGhlIHdvcmQgdG8gY2hlY2suXHJcblx0ICogQHJldHVybnMge0Jvb2xlYW59XHJcblx0ICovXHJcblx0XHJcblx0Y2hlY2tFeGFjdCA6IGZ1bmN0aW9uICh3b3JkKSB7XHJcblx0XHR2YXIgcnVsZUNvZGVzID0gdGhpcy5kaWN0aW9uYXJ5VGFibGVbd29yZF07XHJcblx0XHRcclxuXHRcdGlmICh0eXBlb2YgcnVsZUNvZGVzID09PSAndW5kZWZpbmVkJykge1xyXG5cdFx0XHQvLyBDaGVjayBpZiB0aGlzIG1pZ2h0IGJlIGEgY29tcG91bmQgd29yZC5cclxuXHRcdFx0aWYgKFwiQ09NUE9VTkRNSU5cIiBpbiB0aGlzLmZsYWdzICYmIHdvcmQubGVuZ3RoID49IHRoaXMuZmxhZ3MuQ09NUE9VTkRNSU4pIHtcclxuXHRcdFx0XHRmb3IgKHZhciBpID0gMCwgX2xlbiA9IHRoaXMuY29tcG91bmRSdWxlcy5sZW5ndGg7IGkgPCBfbGVuOyBpKyspIHtcclxuXHRcdFx0XHRcdGlmICh3b3JkLm1hdGNoKHRoaXMuY29tcG91bmRSdWxlc1tpXSkpIHtcclxuXHRcdFx0XHRcdFx0cmV0dXJuIHRydWU7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHRcdFxyXG5cdFx0XHRyZXR1cm4gZmFsc2U7XHJcblx0XHR9XHJcblx0XHRlbHNlIHtcclxuXHRcdFx0Zm9yICh2YXIgaSA9IDAsIF9sZW4gPSBydWxlQ29kZXMubGVuZ3RoOyBpIDwgX2xlbjsgaSsrKSB7XHJcblx0XHRcdFx0aWYgKCF0aGlzLmhhc0ZsYWcod29yZCwgXCJPTkxZSU5DT01QT1VORFwiLCBydWxlQ29kZXNbaV0pKSB7XHJcblx0XHRcdFx0XHRyZXR1cm4gdHJ1ZTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHRcdFx0XHJcblx0XHRcdHJldHVybiBmYWxzZTtcclxuXHRcdH1cclxuXHR9LFxyXG5cdFxyXG5cdC8qKlxyXG5cdCAqIExvb2tzIHVwIHdoZXRoZXIgYSBnaXZlbiB3b3JkIGlzIGZsYWdnZWQgd2l0aCBhIGdpdmVuIGZsYWcuXHJcblx0ICpcclxuXHQgKiBAcGFyYW0ge1N0cmluZ30gd29yZCBUaGUgd29yZCBpbiBxdWVzdGlvbi5cclxuXHQgKiBAcGFyYW0ge1N0cmluZ30gZmxhZyBUaGUgZmxhZyBpbiBxdWVzdGlvbi5cclxuXHQgKiBAcmV0dXJuIHtCb29sZWFufVxyXG5cdCAqL1xyXG5cdCBcclxuXHRoYXNGbGFnIDogZnVuY3Rpb24gKHdvcmQsIGZsYWcsIHdvcmRGbGFncykge1xyXG5cdFx0aWYgKGZsYWcgaW4gdGhpcy5mbGFncykge1xyXG5cdFx0XHRpZiAodHlwZW9mIHdvcmRGbGFncyA9PT0gJ3VuZGVmaW5lZCcpIHtcclxuXHRcdFx0XHR2YXIgd29yZEZsYWdzID0gQXJyYXkucHJvdG90eXBlLmNvbmNhdC5hcHBseShbXSwgdGhpcy5kaWN0aW9uYXJ5VGFibGVbd29yZF0pO1xyXG5cdFx0XHR9XHJcblx0XHRcdFxyXG5cdFx0XHRpZiAod29yZEZsYWdzICYmIHdvcmRGbGFncy5pbmRleE9mKHRoaXMuZmxhZ3NbZmxhZ10pICE9PSAtMSkge1xyXG5cdFx0XHRcdHJldHVybiB0cnVlO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0XHRcclxuXHRcdHJldHVybiBmYWxzZTtcclxuXHR9LFxyXG5cdFxyXG5cdC8qKlxyXG5cdCAqIFJldHVybnMgYSBsaXN0IG9mIHN1Z2dlc3Rpb25zIGZvciBhIG1pc3NwZWxsZWQgd29yZC5cclxuXHQgKlxyXG5cdCAqIEBzZWUgaHR0cDovL3d3dy5ub3J2aWcuY29tL3NwZWxsLWNvcnJlY3QuaHRtbCBmb3IgdGhlIGJhc2lzIG9mIHRoaXMgc3VnZ2VzdG9yLlxyXG5cdCAqIFRoaXMgc3VnZ2VzdG9yIGlzIHByaW1pdGl2ZSwgYnV0IGl0IHdvcmtzLlxyXG5cdCAqXHJcblx0ICogQHBhcmFtIHtTdHJpbmd9IHdvcmQgVGhlIG1pc3NwZWxsaW5nLlxyXG5cdCAqIEBwYXJhbSB7TnVtYmVyfSBbbGltaXQ9NV0gVGhlIG1heGltdW0gbnVtYmVyIG9mIHN1Z2dlc3Rpb25zIHRvIHJldHVybi5cclxuXHQgKiBAcmV0dXJucyB7U3RyaW5nW119IFRoZSBhcnJheSBvZiBzdWdnZXN0aW9ucy5cclxuXHQgKi9cclxuXHRcclxuXHRhbHBoYWJldCA6IFwiXCIsXHJcblx0XHJcblx0c3VnZ2VzdCA6IGZ1bmN0aW9uICh3b3JkLCBsaW1pdCkge1xyXG5cdFx0aWYgKCFsaW1pdCkgbGltaXQgPSA1O1xyXG5cdFx0XHJcblx0XHRpZiAodGhpcy5jaGVjayh3b3JkKSkgcmV0dXJuIFtdO1xyXG5cdFx0XHJcblx0XHQvLyBDaGVjayB0aGUgcmVwbGFjZW1lbnQgdGFibGUuXHJcblx0XHRmb3IgKHZhciBpID0gMCwgX2xlbiA9IHRoaXMucmVwbGFjZW1lbnRUYWJsZS5sZW5ndGg7IGkgPCBfbGVuOyBpKyspIHtcclxuXHRcdFx0dmFyIHJlcGxhY2VtZW50RW50cnkgPSB0aGlzLnJlcGxhY2VtZW50VGFibGVbaV07XHJcblx0XHRcdFxyXG5cdFx0XHRpZiAod29yZC5pbmRleE9mKHJlcGxhY2VtZW50RW50cnlbMF0pICE9PSAtMSkge1xyXG5cdFx0XHRcdHZhciBjb3JyZWN0ZWRXb3JkID0gd29yZC5yZXBsYWNlKHJlcGxhY2VtZW50RW50cnlbMF0sIHJlcGxhY2VtZW50RW50cnlbMV0pO1xyXG5cdFx0XHRcdFxyXG5cdFx0XHRcdGlmICh0aGlzLmNoZWNrKGNvcnJlY3RlZFdvcmQpKSB7XHJcblx0XHRcdFx0XHRyZXR1cm4gWyBjb3JyZWN0ZWRXb3JkIF07XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0XHRcclxuXHRcdHZhciBzZWxmID0gdGhpcztcclxuXHRcdHNlbGYuYWxwaGFiZXQgPSBcImFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6XCI7XHJcblx0XHRcclxuXHRcdC8qXHJcblx0XHRpZiAoIXNlbGYuYWxwaGFiZXQpIHtcclxuXHRcdFx0Ly8gVXNlIHRoZSBhbHBoYWJldCBhcyBpbXBsaWNpdGx5IGRlZmluZWQgYnkgdGhlIHdvcmRzIGluIHRoZSBkaWN0aW9uYXJ5LlxyXG5cdFx0XHR2YXIgYWxwaGFIYXNoID0ge307XHJcblx0XHRcdFxyXG5cdFx0XHRmb3IgKHZhciBpIGluIHNlbGYuZGljdGlvbmFyeVRhYmxlKSB7XHJcblx0XHRcdFx0Zm9yICh2YXIgaiA9IDAsIF9sZW4gPSBpLmxlbmd0aDsgaiA8IF9sZW47IGorKykge1xyXG5cdFx0XHRcdFx0YWxwaGFIYXNoW2lbal1dID0gdHJ1ZTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHRcdFx0XHJcblx0XHRcdGZvciAodmFyIGkgaW4gYWxwaGFIYXNoKSB7XHJcblx0XHRcdFx0c2VsZi5hbHBoYWJldCArPSBpO1xyXG5cdFx0XHR9XHJcblx0XHRcdFxyXG5cdFx0XHR2YXIgYWxwaGFBcnJheSA9IHNlbGYuYWxwaGFiZXQuc3BsaXQoXCJcIik7XHJcblx0XHRcdGFscGhhQXJyYXkuc29ydCgpO1xyXG5cdFx0XHRzZWxmLmFscGhhYmV0ID0gYWxwaGFBcnJheS5qb2luKFwiXCIpO1xyXG5cdFx0fVxyXG5cdFx0Ki9cclxuXHRcdFxyXG5cdFx0ZnVuY3Rpb24gZWRpdHMxKHdvcmRzKSB7XHJcblx0XHRcdHZhciBydiA9IFtdO1xyXG5cdFx0XHRcclxuXHRcdFx0Zm9yICh2YXIgaWkgPSAwLCBfaWlsZW4gPSB3b3Jkcy5sZW5ndGg7IGlpIDwgX2lpbGVuOyBpaSsrKSB7XHJcblx0XHRcdFx0dmFyIHdvcmQgPSB3b3Jkc1tpaV07XHJcblx0XHRcdFx0XHJcblx0XHRcdFx0dmFyIHNwbGl0cyA9IFtdO1xyXG5cdFx0XHRcclxuXHRcdFx0XHRmb3IgKHZhciBpID0gMCwgX2xlbiA9IHdvcmQubGVuZ3RoICsgMTsgaSA8IF9sZW47IGkrKykge1xyXG5cdFx0XHRcdFx0c3BsaXRzLnB1c2goWyB3b3JkLnN1YnN0cmluZygwLCBpKSwgd29yZC5zdWJzdHJpbmcoaSwgd29yZC5sZW5ndGgpIF0pO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHJcblx0XHRcdFx0dmFyIGRlbGV0ZXMgPSBbXTtcclxuXHRcdFx0XHJcblx0XHRcdFx0Zm9yICh2YXIgaSA9IDAsIF9sZW4gPSBzcGxpdHMubGVuZ3RoOyBpIDwgX2xlbjsgaSsrKSB7XHJcblx0XHRcdFx0XHR2YXIgcyA9IHNwbGl0c1tpXTtcclxuXHRcdFx0XHRcclxuXHRcdFx0XHRcdGlmIChzWzFdKSB7XHJcblx0XHRcdFx0XHRcdGRlbGV0ZXMucHVzaChzWzBdICsgc1sxXS5zdWJzdHJpbmcoMSkpO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHJcblx0XHRcdFx0dmFyIHRyYW5zcG9zZXMgPSBbXTtcclxuXHRcdFx0XHJcblx0XHRcdFx0Zm9yICh2YXIgaSA9IDAsIF9sZW4gPSBzcGxpdHMubGVuZ3RoOyBpIDwgX2xlbjsgaSsrKSB7XHJcblx0XHRcdFx0XHR2YXIgcyA9IHNwbGl0c1tpXTtcclxuXHRcdFx0XHRcclxuXHRcdFx0XHRcdGlmIChzWzFdLmxlbmd0aCA+IDEpIHtcclxuXHRcdFx0XHRcdFx0dHJhbnNwb3Nlcy5wdXNoKHNbMF0gKyBzWzFdWzFdICsgc1sxXVswXSArIHNbMV0uc3Vic3RyaW5nKDIpKTtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9XHJcblx0XHRcdFxyXG5cdFx0XHRcdHZhciByZXBsYWNlcyA9IFtdO1xyXG5cdFx0XHRcclxuXHRcdFx0XHRmb3IgKHZhciBpID0gMCwgX2xlbiA9IHNwbGl0cy5sZW5ndGg7IGkgPCBfbGVuOyBpKyspIHtcclxuXHRcdFx0XHRcdHZhciBzID0gc3BsaXRzW2ldO1xyXG5cdFx0XHRcdFxyXG5cdFx0XHRcdFx0aWYgKHNbMV0pIHtcclxuXHRcdFx0XHRcdFx0Zm9yICh2YXIgaiA9IDAsIF9qbGVuID0gc2VsZi5hbHBoYWJldC5sZW5ndGg7IGogPCBfamxlbjsgaisrKSB7XHJcblx0XHRcdFx0XHRcdFx0cmVwbGFjZXMucHVzaChzWzBdICsgc2VsZi5hbHBoYWJldFtqXSArIHNbMV0uc3Vic3RyaW5nKDEpKTtcclxuXHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHJcblx0XHRcdFx0dmFyIGluc2VydHMgPSBbXTtcclxuXHRcdFx0XHJcblx0XHRcdFx0Zm9yICh2YXIgaSA9IDAsIF9sZW4gPSBzcGxpdHMubGVuZ3RoOyBpIDwgX2xlbjsgaSsrKSB7XHJcblx0XHRcdFx0XHR2YXIgcyA9IHNwbGl0c1tpXTtcclxuXHRcdFx0XHRcclxuXHRcdFx0XHRcdGlmIChzWzFdKSB7XHJcblx0XHRcdFx0XHRcdGZvciAodmFyIGogPSAwLCBfamxlbiA9IHNlbGYuYWxwaGFiZXQubGVuZ3RoOyBqIDwgX2psZW47IGorKykge1xyXG5cdFx0XHRcdFx0XHRcdHJlcGxhY2VzLnB1c2goc1swXSArIHNlbGYuYWxwaGFiZXRbal0gKyBzWzFdKTtcclxuXHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHJcblx0XHRcdFx0cnYgPSBydi5jb25jYXQoZGVsZXRlcyk7XHJcblx0XHRcdFx0cnYgPSBydi5jb25jYXQodHJhbnNwb3Nlcyk7XHJcblx0XHRcdFx0cnYgPSBydi5jb25jYXQocmVwbGFjZXMpO1xyXG5cdFx0XHRcdHJ2ID0gcnYuY29uY2F0KGluc2VydHMpO1xyXG5cdFx0XHR9XHJcblx0XHRcdFxyXG5cdFx0XHRyZXR1cm4gcnY7XHJcblx0XHR9XHJcblx0XHRcclxuXHRcdGZ1bmN0aW9uIGtub3duKHdvcmRzKSB7XHJcblx0XHRcdHZhciBydiA9IFtdO1xyXG5cdFx0XHRcclxuXHRcdFx0Zm9yICh2YXIgaSA9IDA7IGkgPCB3b3Jkcy5sZW5ndGg7IGkrKykge1xyXG5cdFx0XHRcdGlmIChzZWxmLmNoZWNrKHdvcmRzW2ldKSkge1xyXG5cdFx0XHRcdFx0cnYucHVzaCh3b3Jkc1tpXSk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHRcdFxyXG5cdFx0XHRyZXR1cm4gcnY7XHJcblx0XHR9XHJcblx0XHRcclxuXHRcdGZ1bmN0aW9uIGNvcnJlY3Qod29yZCkge1xyXG5cdFx0XHQvLyBHZXQgdGhlIGVkaXQtZGlzdGFuY2UtMSBhbmQgZWRpdC1kaXN0YW5jZS0yIGZvcm1zIG9mIHRoaXMgd29yZC5cclxuXHRcdFx0dmFyIGVkMSA9IGVkaXRzMShbd29yZF0pO1xyXG5cdFx0XHR2YXIgZWQyID0gZWRpdHMxKGVkMSk7XHJcblx0XHRcdFxyXG5cdFx0XHR2YXIgY29ycmVjdGlvbnMgPSBrbm93bihlZDEpLmNvbmNhdChrbm93bihlZDIpKTtcclxuXHRcdFx0XHJcblx0XHRcdC8vIFNvcnQgdGhlIGVkaXRzIGJhc2VkIG9uIGhvdyBtYW55IGRpZmZlcmVudCB3YXlzIHRoZXkgd2VyZSBjcmVhdGVkLlxyXG5cdFx0XHR2YXIgd2VpZ2h0ZWRfY29ycmVjdGlvbnMgPSB7fTtcclxuXHRcdFx0XHJcblx0XHRcdGZvciAodmFyIGkgPSAwLCBfbGVuID0gY29ycmVjdGlvbnMubGVuZ3RoOyBpIDwgX2xlbjsgaSsrKSB7XHJcblx0XHRcdFx0aWYgKCEoY29ycmVjdGlvbnNbaV0gaW4gd2VpZ2h0ZWRfY29ycmVjdGlvbnMpKSB7XHJcblx0XHRcdFx0XHR3ZWlnaHRlZF9jb3JyZWN0aW9uc1tjb3JyZWN0aW9uc1tpXV0gPSAxO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHRlbHNlIHtcclxuXHRcdFx0XHRcdHdlaWdodGVkX2NvcnJlY3Rpb25zW2NvcnJlY3Rpb25zW2ldXSArPSAxO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fVxyXG5cdFx0XHRcclxuXHRcdFx0dmFyIHNvcnRlZF9jb3JyZWN0aW9ucyA9IFtdO1xyXG5cdFx0XHRcclxuXHRcdFx0Zm9yICh2YXIgaSBpbiB3ZWlnaHRlZF9jb3JyZWN0aW9ucykge1xyXG5cdFx0XHRcdHNvcnRlZF9jb3JyZWN0aW9ucy5wdXNoKFsgaSwgd2VpZ2h0ZWRfY29ycmVjdGlvbnNbaV0gXSk7XHJcblx0XHRcdH1cclxuXHRcdFx0XHJcblx0XHRcdGZ1bmN0aW9uIHNvcnRlcihhLCBiKSB7XHJcblx0XHRcdFx0aWYgKGFbMV0gPCBiWzFdKSB7XHJcblx0XHRcdFx0XHRyZXR1cm4gLTE7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdFxyXG5cdFx0XHRcdHJldHVybiAxO1xyXG5cdFx0XHR9XHJcblx0XHRcdFxyXG5cdFx0XHRzb3J0ZWRfY29ycmVjdGlvbnMuc29ydChzb3J0ZXIpLnJldmVyc2UoKTtcclxuXHRcdFx0XHJcblx0XHRcdHZhciBydiA9IFtdO1xyXG5cdFx0XHRcclxuXHRcdFx0Zm9yICh2YXIgaSA9IDAsIF9sZW4gPSBNYXRoLm1pbihsaW1pdCwgc29ydGVkX2NvcnJlY3Rpb25zLmxlbmd0aCk7IGkgPCBfbGVuOyBpKyspIHtcclxuXHRcdFx0XHRpZiAoIXNlbGYuaGFzRmxhZyhzb3J0ZWRfY29ycmVjdGlvbnNbaV1bMF0sIFwiTk9TVUdHRVNUXCIpKSB7XHJcblx0XHRcdFx0XHRydi5wdXNoKHNvcnRlZF9jb3JyZWN0aW9uc1tpXVswXSk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHRcdFxyXG5cdFx0XHRyZXR1cm4gcnY7XHJcblx0XHR9XHJcblx0XHRcclxuXHRcdHJldHVybiBjb3JyZWN0KHdvcmQpO1xyXG5cdH1cclxufTtcbjsgYnJvd3NlcmlmeV9zaGltX19kZWZpbmVfX21vZHVsZV9fZXhwb3J0X18odHlwZW9mIFR5cG8gIT0gXCJ1bmRlZmluZWRcIiA/IFR5cG8gOiB3aW5kb3cuVHlwbyk7XG5cbn0pLmNhbGwoZ2xvYmFsLCB1bmRlZmluZWQsIHVuZGVmaW5lZCwgdW5kZWZpbmVkLCB1bmRlZmluZWQsIGZ1bmN0aW9uIGRlZmluZUV4cG9ydChleCkgeyBtb2R1bGUuZXhwb3J0cyA9IGV4OyB9KTtcbiIsIi8vIENvZGVNaXJyb3IsIGNvcHlyaWdodCAoYykgYnkgTWFyaWpuIEhhdmVyYmVrZSBhbmQgb3RoZXJzXHJcbi8vIERpc3RyaWJ1dGVkIHVuZGVyIGFuIE1JVCBsaWNlbnNlOiBodHRwOi8vY29kZW1pcnJvci5uZXQvTElDRU5TRVxyXG5cclxuKGZ1bmN0aW9uKG1vZCkge1xyXG4gIGlmICh0eXBlb2YgZXhwb3J0cyA9PSBcIm9iamVjdFwiICYmIHR5cGVvZiBtb2R1bGUgPT0gXCJvYmplY3RcIikgLy8gQ29tbW9uSlNcclxuICAgIG1vZChyZXF1aXJlKFwiLi4vLi4vbGliL2NvZGVtaXJyb3JcIikpO1xyXG4gIGVsc2UgaWYgKHR5cGVvZiBkZWZpbmUgPT0gXCJmdW5jdGlvblwiICYmIGRlZmluZS5hbWQpIC8vIEFNRFxyXG4gICAgZGVmaW5lKFtcIi4uLy4uL2xpYi9jb2RlbWlycm9yXCJdLCBtb2QpO1xyXG4gIGVsc2UgLy8gUGxhaW4gYnJvd3NlciBlbnZcclxuICAgIG1vZChDb2RlTWlycm9yKTtcclxufSkoZnVuY3Rpb24oQ29kZU1pcnJvcikge1xyXG4gIFwidXNlIHN0cmljdFwiO1xyXG5cclxuICBDb2RlTWlycm9yLmRlZmluZU9wdGlvbihcImZ1bGxTY3JlZW5cIiwgZmFsc2UsIGZ1bmN0aW9uKGNtLCB2YWwsIG9sZCkge1xyXG4gICAgaWYgKG9sZCA9PSBDb2RlTWlycm9yLkluaXQpIG9sZCA9IGZhbHNlO1xyXG4gICAgaWYgKCFvbGQgPT0gIXZhbCkgcmV0dXJuO1xyXG4gICAgaWYgKHZhbCkgc2V0RnVsbHNjcmVlbihjbSk7XHJcbiAgICBlbHNlIHNldE5vcm1hbChjbSk7XHJcbiAgfSk7XHJcblxyXG4gIGZ1bmN0aW9uIHNldEZ1bGxzY3JlZW4oY20pIHtcclxuICAgIHZhciB3cmFwID0gY20uZ2V0V3JhcHBlckVsZW1lbnQoKTtcclxuICAgIGNtLnN0YXRlLmZ1bGxTY3JlZW5SZXN0b3JlID0ge3Njcm9sbFRvcDogd2luZG93LnBhZ2VZT2Zmc2V0LCBzY3JvbGxMZWZ0OiB3aW5kb3cucGFnZVhPZmZzZXQsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aWR0aDogd3JhcC5zdHlsZS53aWR0aCwgaGVpZ2h0OiB3cmFwLnN0eWxlLmhlaWdodH07XHJcbiAgICB3cmFwLnN0eWxlLndpZHRoID0gXCJcIjtcclxuICAgIHdyYXAuc3R5bGUuaGVpZ2h0ID0gXCJhdXRvXCI7XHJcbiAgICB3cmFwLmNsYXNzTmFtZSArPSBcIiBDb2RlTWlycm9yLWZ1bGxzY3JlZW5cIjtcclxuICAgIGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5zdHlsZS5vdmVyZmxvdyA9IFwiaGlkZGVuXCI7XHJcbiAgICBjbS5yZWZyZXNoKCk7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBzZXROb3JtYWwoY20pIHtcclxuICAgIHZhciB3cmFwID0gY20uZ2V0V3JhcHBlckVsZW1lbnQoKTtcclxuICAgIHdyYXAuY2xhc3NOYW1lID0gd3JhcC5jbGFzc05hbWUucmVwbGFjZSgvXFxzKkNvZGVNaXJyb3ItZnVsbHNjcmVlblxcYi8sIFwiXCIpO1xyXG4gICAgZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LnN0eWxlLm92ZXJmbG93ID0gXCJcIjtcclxuICAgIHZhciBpbmZvID0gY20uc3RhdGUuZnVsbFNjcmVlblJlc3RvcmU7XHJcbiAgICB3cmFwLnN0eWxlLndpZHRoID0gaW5mby53aWR0aDsgd3JhcC5zdHlsZS5oZWlnaHQgPSBpbmZvLmhlaWdodDtcclxuICAgIHdpbmRvdy5zY3JvbGxUbyhpbmZvLnNjcm9sbExlZnQsIGluZm8uc2Nyb2xsVG9wKTtcclxuICAgIGNtLnJlZnJlc2goKTtcclxuICB9XHJcbn0pO1xyXG4iLCIvLyBDb2RlTWlycm9yLCBjb3B5cmlnaHQgKGMpIGJ5IE1hcmlqbiBIYXZlcmJla2UgYW5kIG90aGVyc1xyXG4vLyBEaXN0cmlidXRlZCB1bmRlciBhbiBNSVQgbGljZW5zZTogaHR0cDovL2NvZGVtaXJyb3IubmV0L0xJQ0VOU0VcclxuXHJcbihmdW5jdGlvbihtb2QpIHtcclxuICBpZiAodHlwZW9mIGV4cG9ydHMgPT0gXCJvYmplY3RcIiAmJiB0eXBlb2YgbW9kdWxlID09IFwib2JqZWN0XCIpIC8vIENvbW1vbkpTXHJcbiAgICBtb2QocmVxdWlyZShcIi4uLy4uL2xpYi9jb2RlbWlycm9yXCIpKTtcclxuICBlbHNlIGlmICh0eXBlb2YgZGVmaW5lID09IFwiZnVuY3Rpb25cIiAmJiBkZWZpbmUuYW1kKSAvLyBBTURcclxuICAgIGRlZmluZShbXCIuLi8uLi9saWIvY29kZW1pcnJvclwiXSwgbW9kKTtcclxuICBlbHNlIC8vIFBsYWluIGJyb3dzZXIgZW52XHJcbiAgICBtb2QoQ29kZU1pcnJvcik7XHJcbn0pKGZ1bmN0aW9uKENvZGVNaXJyb3IpIHtcclxuICBcInVzZSBzdHJpY3RcIjtcclxuXHJcbiAgdmFyIGxpc3RSRSA9IC9eKFxccyopKD5bPiBdKnxbKistXVxcc3woXFxkKykoWy4pXSkpKFxccyopLyxcclxuICAgICAgZW1wdHlMaXN0UkUgPSAvXihcXHMqKSg+Wz4gXSp8WyorLV18KFxcZCspWy4pXSkoXFxzKikkLyxcclxuICAgICAgdW5vcmRlcmVkTGlzdFJFID0gL1sqKy1dXFxzLztcclxuXHJcbiAgQ29kZU1pcnJvci5jb21tYW5kcy5uZXdsaW5lQW5kSW5kZW50Q29udGludWVNYXJrZG93bkxpc3QgPSBmdW5jdGlvbihjbSkge1xyXG4gICAgaWYgKGNtLmdldE9wdGlvbihcImRpc2FibGVJbnB1dFwiKSkgcmV0dXJuIENvZGVNaXJyb3IuUGFzcztcclxuICAgIHZhciByYW5nZXMgPSBjbS5saXN0U2VsZWN0aW9ucygpLCByZXBsYWNlbWVudHMgPSBbXTtcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgcmFuZ2VzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgIHZhciBwb3MgPSByYW5nZXNbaV0uaGVhZDtcclxuICAgICAgdmFyIGVvbFN0YXRlID0gY20uZ2V0U3RhdGVBZnRlcihwb3MubGluZSk7XHJcbiAgICAgIHZhciBpbkxpc3QgPSBlb2xTdGF0ZS5saXN0ICE9PSBmYWxzZTtcclxuICAgICAgdmFyIGluUXVvdGUgPSBlb2xTdGF0ZS5xdW90ZSAhPT0gMDtcclxuXHJcbiAgICAgIHZhciBsaW5lID0gY20uZ2V0TGluZShwb3MubGluZSksIG1hdGNoID0gbGlzdFJFLmV4ZWMobGluZSk7XHJcbiAgICAgIGlmICghcmFuZ2VzW2ldLmVtcHR5KCkgfHwgKCFpbkxpc3QgJiYgIWluUXVvdGUpIHx8ICFtYXRjaCkge1xyXG4gICAgICAgIGNtLmV4ZWNDb21tYW5kKFwibmV3bGluZUFuZEluZGVudFwiKTtcclxuICAgICAgICByZXR1cm47XHJcbiAgICAgIH1cclxuICAgICAgaWYgKGVtcHR5TGlzdFJFLnRlc3QobGluZSkpIHtcclxuICAgICAgICBjbS5yZXBsYWNlUmFuZ2UoXCJcIiwge1xyXG4gICAgICAgICAgbGluZTogcG9zLmxpbmUsIGNoOiAwXHJcbiAgICAgICAgfSwge1xyXG4gICAgICAgICAgbGluZTogcG9zLmxpbmUsIGNoOiBwb3MuY2ggKyAxXHJcbiAgICAgICAgfSk7XHJcbiAgICAgICAgcmVwbGFjZW1lbnRzW2ldID0gXCJcXG5cIjtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICB2YXIgaW5kZW50ID0gbWF0Y2hbMV0sIGFmdGVyID0gbWF0Y2hbNV07XHJcbiAgICAgICAgdmFyIGJ1bGxldCA9IHVub3JkZXJlZExpc3RSRS50ZXN0KG1hdGNoWzJdKSB8fCBtYXRjaFsyXS5pbmRleE9mKFwiPlwiKSA+PSAwXHJcbiAgICAgICAgICA/IG1hdGNoWzJdXHJcbiAgICAgICAgICA6IChwYXJzZUludChtYXRjaFszXSwgMTApICsgMSkgKyBtYXRjaFs0XTtcclxuXHJcbiAgICAgICAgcmVwbGFjZW1lbnRzW2ldID0gXCJcXG5cIiArIGluZGVudCArIGJ1bGxldCArIGFmdGVyO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgY20ucmVwbGFjZVNlbGVjdGlvbnMocmVwbGFjZW1lbnRzKTtcclxuICB9O1xyXG59KTtcclxuIiwiLy8gQ29kZU1pcnJvciwgY29weXJpZ2h0IChjKSBieSBNYXJpam4gSGF2ZXJiZWtlIGFuZCBvdGhlcnNcclxuLy8gRGlzdHJpYnV0ZWQgdW5kZXIgYW4gTUlUIGxpY2Vuc2U6IGh0dHA6Ly9jb2RlbWlycm9yLm5ldC9MSUNFTlNFXHJcblxyXG4vLyBVdGlsaXR5IGZ1bmN0aW9uIHRoYXQgYWxsb3dzIG1vZGVzIHRvIGJlIGNvbWJpbmVkLiBUaGUgbW9kZSBnaXZlblxyXG4vLyBhcyB0aGUgYmFzZSBhcmd1bWVudCB0YWtlcyBjYXJlIG9mIG1vc3Qgb2YgdGhlIG5vcm1hbCBtb2RlXHJcbi8vIGZ1bmN0aW9uYWxpdHksIGJ1dCBhIHNlY29uZCAodHlwaWNhbGx5IHNpbXBsZSkgbW9kZSBpcyB1c2VkLCB3aGljaFxyXG4vLyBjYW4gb3ZlcnJpZGUgdGhlIHN0eWxlIG9mIHRleHQuIEJvdGggbW9kZXMgZ2V0IHRvIHBhcnNlIGFsbCBvZiB0aGVcclxuLy8gdGV4dCwgYnV0IHdoZW4gYm90aCBhc3NpZ24gYSBub24tbnVsbCBzdHlsZSB0byBhIHBpZWNlIG9mIGNvZGUsIHRoZVxyXG4vLyBvdmVybGF5IHdpbnMsIHVubGVzcyB0aGUgY29tYmluZSBhcmd1bWVudCB3YXMgdHJ1ZSBhbmQgbm90IG92ZXJyaWRkZW4sXHJcbi8vIG9yIHN0YXRlLm92ZXJsYXkuY29tYmluZVRva2VucyB3YXMgdHJ1ZSwgaW4gd2hpY2ggY2FzZSB0aGUgc3R5bGVzIGFyZVxyXG4vLyBjb21iaW5lZC5cclxuXHJcbihmdW5jdGlvbihtb2QpIHtcclxuICBpZiAodHlwZW9mIGV4cG9ydHMgPT0gXCJvYmplY3RcIiAmJiB0eXBlb2YgbW9kdWxlID09IFwib2JqZWN0XCIpIC8vIENvbW1vbkpTXHJcbiAgICBtb2QocmVxdWlyZShcIi4uLy4uL2xpYi9jb2RlbWlycm9yXCIpKTtcclxuICBlbHNlIGlmICh0eXBlb2YgZGVmaW5lID09IFwiZnVuY3Rpb25cIiAmJiBkZWZpbmUuYW1kKSAvLyBBTURcclxuICAgIGRlZmluZShbXCIuLi8uLi9saWIvY29kZW1pcnJvclwiXSwgbW9kKTtcclxuICBlbHNlIC8vIFBsYWluIGJyb3dzZXIgZW52XHJcbiAgICBtb2QoQ29kZU1pcnJvcik7XHJcbn0pKGZ1bmN0aW9uKENvZGVNaXJyb3IpIHtcclxuXCJ1c2Ugc3RyaWN0XCI7XHJcblxyXG5Db2RlTWlycm9yLm92ZXJsYXlNb2RlID0gZnVuY3Rpb24oYmFzZSwgb3ZlcmxheSwgY29tYmluZSkge1xyXG4gIHJldHVybiB7XHJcbiAgICBzdGFydFN0YXRlOiBmdW5jdGlvbigpIHtcclxuICAgICAgcmV0dXJuIHtcclxuICAgICAgICBiYXNlOiBDb2RlTWlycm9yLnN0YXJ0U3RhdGUoYmFzZSksXHJcbiAgICAgICAgb3ZlcmxheTogQ29kZU1pcnJvci5zdGFydFN0YXRlKG92ZXJsYXkpLFxyXG4gICAgICAgIGJhc2VQb3M6IDAsIGJhc2VDdXI6IG51bGwsXHJcbiAgICAgICAgb3ZlcmxheVBvczogMCwgb3ZlcmxheUN1cjogbnVsbCxcclxuICAgICAgICBzdHJlYW1TZWVuOiBudWxsXHJcbiAgICAgIH07XHJcbiAgICB9LFxyXG4gICAgY29weVN0YXRlOiBmdW5jdGlvbihzdGF0ZSkge1xyXG4gICAgICByZXR1cm4ge1xyXG4gICAgICAgIGJhc2U6IENvZGVNaXJyb3IuY29weVN0YXRlKGJhc2UsIHN0YXRlLmJhc2UpLFxyXG4gICAgICAgIG92ZXJsYXk6IENvZGVNaXJyb3IuY29weVN0YXRlKG92ZXJsYXksIHN0YXRlLm92ZXJsYXkpLFxyXG4gICAgICAgIGJhc2VQb3M6IHN0YXRlLmJhc2VQb3MsIGJhc2VDdXI6IG51bGwsXHJcbiAgICAgICAgb3ZlcmxheVBvczogc3RhdGUub3ZlcmxheVBvcywgb3ZlcmxheUN1cjogbnVsbFxyXG4gICAgICB9O1xyXG4gICAgfSxcclxuXHJcbiAgICB0b2tlbjogZnVuY3Rpb24oc3RyZWFtLCBzdGF0ZSkge1xyXG4gICAgICBpZiAoc3RyZWFtICE9IHN0YXRlLnN0cmVhbVNlZW4gfHxcclxuICAgICAgICAgIE1hdGgubWluKHN0YXRlLmJhc2VQb3MsIHN0YXRlLm92ZXJsYXlQb3MpIDwgc3RyZWFtLnN0YXJ0KSB7XHJcbiAgICAgICAgc3RhdGUuc3RyZWFtU2VlbiA9IHN0cmVhbTtcclxuICAgICAgICBzdGF0ZS5iYXNlUG9zID0gc3RhdGUub3ZlcmxheVBvcyA9IHN0cmVhbS5zdGFydDtcclxuICAgICAgfVxyXG5cclxuICAgICAgaWYgKHN0cmVhbS5zdGFydCA9PSBzdGF0ZS5iYXNlUG9zKSB7XHJcbiAgICAgICAgc3RhdGUuYmFzZUN1ciA9IGJhc2UudG9rZW4oc3RyZWFtLCBzdGF0ZS5iYXNlKTtcclxuICAgICAgICBzdGF0ZS5iYXNlUG9zID0gc3RyZWFtLnBvcztcclxuICAgICAgfVxyXG4gICAgICBpZiAoc3RyZWFtLnN0YXJ0ID09IHN0YXRlLm92ZXJsYXlQb3MpIHtcclxuICAgICAgICBzdHJlYW0ucG9zID0gc3RyZWFtLnN0YXJ0O1xyXG4gICAgICAgIHN0YXRlLm92ZXJsYXlDdXIgPSBvdmVybGF5LnRva2VuKHN0cmVhbSwgc3RhdGUub3ZlcmxheSk7XHJcbiAgICAgICAgc3RhdGUub3ZlcmxheVBvcyA9IHN0cmVhbS5wb3M7XHJcbiAgICAgIH1cclxuICAgICAgc3RyZWFtLnBvcyA9IE1hdGgubWluKHN0YXRlLmJhc2VQb3MsIHN0YXRlLm92ZXJsYXlQb3MpO1xyXG5cclxuICAgICAgLy8gc3RhdGUub3ZlcmxheS5jb21iaW5lVG9rZW5zIGFsd2F5cyB0YWtlcyBwcmVjZWRlbmNlIG92ZXIgY29tYmluZSxcclxuICAgICAgLy8gdW5sZXNzIHNldCB0byBudWxsXHJcbiAgICAgIGlmIChzdGF0ZS5vdmVybGF5Q3VyID09IG51bGwpIHJldHVybiBzdGF0ZS5iYXNlQ3VyO1xyXG4gICAgICBlbHNlIGlmIChzdGF0ZS5iYXNlQ3VyICE9IG51bGwgJiZcclxuICAgICAgICAgICAgICAgc3RhdGUub3ZlcmxheS5jb21iaW5lVG9rZW5zIHx8XHJcbiAgICAgICAgICAgICAgIGNvbWJpbmUgJiYgc3RhdGUub3ZlcmxheS5jb21iaW5lVG9rZW5zID09IG51bGwpXHJcbiAgICAgICAgcmV0dXJuIHN0YXRlLmJhc2VDdXIgKyBcIiBcIiArIHN0YXRlLm92ZXJsYXlDdXI7XHJcbiAgICAgIGVsc2UgcmV0dXJuIHN0YXRlLm92ZXJsYXlDdXI7XHJcbiAgICB9LFxyXG5cclxuICAgIGluZGVudDogYmFzZS5pbmRlbnQgJiYgZnVuY3Rpb24oc3RhdGUsIHRleHRBZnRlcikge1xyXG4gICAgICByZXR1cm4gYmFzZS5pbmRlbnQoc3RhdGUuYmFzZSwgdGV4dEFmdGVyKTtcclxuICAgIH0sXHJcbiAgICBlbGVjdHJpY0NoYXJzOiBiYXNlLmVsZWN0cmljQ2hhcnMsXHJcblxyXG4gICAgaW5uZXJNb2RlOiBmdW5jdGlvbihzdGF0ZSkgeyByZXR1cm4ge3N0YXRlOiBzdGF0ZS5iYXNlLCBtb2RlOiBiYXNlfTsgfSxcclxuXHJcbiAgICBibGFua0xpbmU6IGZ1bmN0aW9uKHN0YXRlKSB7XHJcbiAgICAgIGlmIChiYXNlLmJsYW5rTGluZSkgYmFzZS5ibGFua0xpbmUoc3RhdGUuYmFzZSk7XHJcbiAgICAgIGlmIChvdmVybGF5LmJsYW5rTGluZSkgb3ZlcmxheS5ibGFua0xpbmUoc3RhdGUub3ZlcmxheSk7XHJcbiAgICB9XHJcbiAgfTtcclxufTtcclxuXHJcbn0pO1xyXG4iLCIvLyBDb2RlTWlycm9yLCBjb3B5cmlnaHQgKGMpIGJ5IE1hcmlqbiBIYXZlcmJla2UgYW5kIG90aGVyc1xyXG4vLyBEaXN0cmlidXRlZCB1bmRlciBhbiBNSVQgbGljZW5zZTogaHR0cDovL2NvZGVtaXJyb3IubmV0L0xJQ0VOU0VcclxuXHJcbi8vIFRoaXMgaXMgQ29kZU1pcnJvciAoaHR0cDovL2NvZGVtaXJyb3IubmV0KSwgYSBjb2RlIGVkaXRvclxyXG4vLyBpbXBsZW1lbnRlZCBpbiBKYXZhU2NyaXB0IG9uIHRvcCBvZiB0aGUgYnJvd3NlcidzIERPTS5cclxuLy9cclxuLy8gWW91IGNhbiBmaW5kIHNvbWUgdGVjaG5pY2FsIGJhY2tncm91bmQgZm9yIHNvbWUgb2YgdGhlIGNvZGUgYmVsb3dcclxuLy8gYXQgaHR0cDovL21hcmlqbmhhdmVyYmVrZS5ubC9ibG9nLyNjbS1pbnRlcm5hbHMgLlxyXG5cclxuKGZ1bmN0aW9uKG1vZCkge1xyXG4gIGlmICh0eXBlb2YgZXhwb3J0cyA9PSBcIm9iamVjdFwiICYmIHR5cGVvZiBtb2R1bGUgPT0gXCJvYmplY3RcIikgLy8gQ29tbW9uSlNcclxuICAgIG1vZHVsZS5leHBvcnRzID0gbW9kKCk7XHJcbiAgZWxzZSBpZiAodHlwZW9mIGRlZmluZSA9PSBcImZ1bmN0aW9uXCIgJiYgZGVmaW5lLmFtZCkgLy8gQU1EXHJcbiAgICByZXR1cm4gZGVmaW5lKFtdLCBtb2QpO1xyXG4gIGVsc2UgLy8gUGxhaW4gYnJvd3NlciBlbnZcclxuICAgIHRoaXMuQ29kZU1pcnJvciA9IG1vZCgpO1xyXG59KShmdW5jdGlvbigpIHtcclxuICBcInVzZSBzdHJpY3RcIjtcclxuXHJcbiAgLy8gQlJPV1NFUiBTTklGRklOR1xyXG5cclxuICAvLyBLbHVkZ2VzIGZvciBidWdzIGFuZCBiZWhhdmlvciBkaWZmZXJlbmNlcyB0aGF0IGNhbid0IGJlIGZlYXR1cmVcclxuICAvLyBkZXRlY3RlZCBhcmUgZW5hYmxlZCBiYXNlZCBvbiB1c2VyQWdlbnQgZXRjIHNuaWZmaW5nLlxyXG4gIHZhciB1c2VyQWdlbnQgPSBuYXZpZ2F0b3IudXNlckFnZW50O1xyXG4gIHZhciBwbGF0Zm9ybSA9IG5hdmlnYXRvci5wbGF0Zm9ybTtcclxuXHJcbiAgdmFyIGdlY2tvID0gL2dlY2tvXFwvXFxkL2kudGVzdCh1c2VyQWdlbnQpO1xyXG4gIHZhciBpZV91cHRvMTAgPSAvTVNJRSBcXGQvLnRlc3QodXNlckFnZW50KTtcclxuICB2YXIgaWVfMTF1cCA9IC9UcmlkZW50XFwvKD86WzctOV18XFxkezIsfSlcXC4uKnJ2OihcXGQrKS8uZXhlYyh1c2VyQWdlbnQpO1xyXG4gIHZhciBpZSA9IGllX3VwdG8xMCB8fCBpZV8xMXVwO1xyXG4gIHZhciBpZV92ZXJzaW9uID0gaWUgJiYgKGllX3VwdG8xMCA/IGRvY3VtZW50LmRvY3VtZW50TW9kZSB8fCA2IDogaWVfMTF1cFsxXSk7XHJcbiAgdmFyIHdlYmtpdCA9IC9XZWJLaXRcXC8vLnRlc3QodXNlckFnZW50KTtcclxuICB2YXIgcXR3ZWJraXQgPSB3ZWJraXQgJiYgL1F0XFwvXFxkK1xcLlxcZCsvLnRlc3QodXNlckFnZW50KTtcclxuICB2YXIgY2hyb21lID0gL0Nocm9tZVxcLy8udGVzdCh1c2VyQWdlbnQpO1xyXG4gIHZhciBwcmVzdG8gPSAvT3BlcmFcXC8vLnRlc3QodXNlckFnZW50KTtcclxuICB2YXIgc2FmYXJpID0gL0FwcGxlIENvbXB1dGVyLy50ZXN0KG5hdmlnYXRvci52ZW5kb3IpO1xyXG4gIHZhciBtYWNfZ2VNb3VudGFpbkxpb24gPSAvTWFjIE9TIFggMVxcZFxcRChbOC05XXxcXGRcXGQpXFxELy50ZXN0KHVzZXJBZ2VudCk7XHJcbiAgdmFyIHBoYW50b20gPSAvUGhhbnRvbUpTLy50ZXN0KHVzZXJBZ2VudCk7XHJcblxyXG4gIHZhciBpb3MgPSAvQXBwbGVXZWJLaXQvLnRlc3QodXNlckFnZW50KSAmJiAvTW9iaWxlXFwvXFx3Ky8udGVzdCh1c2VyQWdlbnQpO1xyXG4gIC8vIFRoaXMgaXMgd29lZnVsbHkgaW5jb21wbGV0ZS4gU3VnZ2VzdGlvbnMgZm9yIGFsdGVybmF0aXZlIG1ldGhvZHMgd2VsY29tZS5cclxuICB2YXIgbW9iaWxlID0gaW9zIHx8IC9BbmRyb2lkfHdlYk9TfEJsYWNrQmVycnl8T3BlcmEgTWluaXxPcGVyYSBNb2JpfElFTW9iaWxlL2kudGVzdCh1c2VyQWdlbnQpO1xyXG4gIHZhciBtYWMgPSBpb3MgfHwgL01hYy8udGVzdChwbGF0Zm9ybSk7XHJcbiAgdmFyIHdpbmRvd3MgPSAvd2luL2kudGVzdChwbGF0Zm9ybSk7XHJcblxyXG4gIHZhciBwcmVzdG9fdmVyc2lvbiA9IHByZXN0byAmJiB1c2VyQWdlbnQubWF0Y2goL1ZlcnNpb25cXC8oXFxkKlxcLlxcZCopLyk7XHJcbiAgaWYgKHByZXN0b192ZXJzaW9uKSBwcmVzdG9fdmVyc2lvbiA9IE51bWJlcihwcmVzdG9fdmVyc2lvblsxXSk7XHJcbiAgaWYgKHByZXN0b192ZXJzaW9uICYmIHByZXN0b192ZXJzaW9uID49IDE1KSB7IHByZXN0byA9IGZhbHNlOyB3ZWJraXQgPSB0cnVlOyB9XHJcbiAgLy8gU29tZSBicm93c2VycyB1c2UgdGhlIHdyb25nIGV2ZW50IHByb3BlcnRpZXMgdG8gc2lnbmFsIGNtZC9jdHJsIG9uIE9TIFhcclxuICB2YXIgZmxpcEN0cmxDbWQgPSBtYWMgJiYgKHF0d2Via2l0IHx8IHByZXN0byAmJiAocHJlc3RvX3ZlcnNpb24gPT0gbnVsbCB8fCBwcmVzdG9fdmVyc2lvbiA8IDEyLjExKSk7XHJcbiAgdmFyIGNhcHR1cmVSaWdodENsaWNrID0gZ2Vja28gfHwgKGllICYmIGllX3ZlcnNpb24gPj0gOSk7XHJcblxyXG4gIC8vIE9wdGltaXplIHNvbWUgY29kZSB3aGVuIHRoZXNlIGZlYXR1cmVzIGFyZSBub3QgdXNlZC5cclxuICB2YXIgc2F3UmVhZE9ubHlTcGFucyA9IGZhbHNlLCBzYXdDb2xsYXBzZWRTcGFucyA9IGZhbHNlO1xyXG5cclxuICAvLyBFRElUT1IgQ09OU1RSVUNUT1JcclxuXHJcbiAgLy8gQSBDb2RlTWlycm9yIGluc3RhbmNlIHJlcHJlc2VudHMgYW4gZWRpdG9yLiBUaGlzIGlzIHRoZSBvYmplY3RcclxuICAvLyB0aGF0IHVzZXIgY29kZSBpcyB1c3VhbGx5IGRlYWxpbmcgd2l0aC5cclxuXHJcbiAgZnVuY3Rpb24gQ29kZU1pcnJvcihwbGFjZSwgb3B0aW9ucykge1xyXG4gICAgaWYgKCEodGhpcyBpbnN0YW5jZW9mIENvZGVNaXJyb3IpKSByZXR1cm4gbmV3IENvZGVNaXJyb3IocGxhY2UsIG9wdGlvbnMpO1xyXG5cclxuICAgIHRoaXMub3B0aW9ucyA9IG9wdGlvbnMgPSBvcHRpb25zID8gY29weU9iaihvcHRpb25zKSA6IHt9O1xyXG4gICAgLy8gRGV0ZXJtaW5lIGVmZmVjdGl2ZSBvcHRpb25zIGJhc2VkIG9uIGdpdmVuIHZhbHVlcyBhbmQgZGVmYXVsdHMuXHJcbiAgICBjb3B5T2JqKGRlZmF1bHRzLCBvcHRpb25zLCBmYWxzZSk7XHJcbiAgICBzZXRHdXR0ZXJzRm9yTGluZU51bWJlcnMob3B0aW9ucyk7XHJcblxyXG4gICAgdmFyIGRvYyA9IG9wdGlvbnMudmFsdWU7XHJcbiAgICBpZiAodHlwZW9mIGRvYyA9PSBcInN0cmluZ1wiKSBkb2MgPSBuZXcgRG9jKGRvYywgb3B0aW9ucy5tb2RlLCBudWxsLCBvcHRpb25zLmxpbmVTZXBhcmF0b3IpO1xyXG4gICAgdGhpcy5kb2MgPSBkb2M7XHJcblxyXG4gICAgdmFyIGlucHV0ID0gbmV3IENvZGVNaXJyb3IuaW5wdXRTdHlsZXNbb3B0aW9ucy5pbnB1dFN0eWxlXSh0aGlzKTtcclxuICAgIHZhciBkaXNwbGF5ID0gdGhpcy5kaXNwbGF5ID0gbmV3IERpc3BsYXkocGxhY2UsIGRvYywgaW5wdXQpO1xyXG4gICAgZGlzcGxheS53cmFwcGVyLkNvZGVNaXJyb3IgPSB0aGlzO1xyXG4gICAgdXBkYXRlR3V0dGVycyh0aGlzKTtcclxuICAgIHRoZW1lQ2hhbmdlZCh0aGlzKTtcclxuICAgIGlmIChvcHRpb25zLmxpbmVXcmFwcGluZylcclxuICAgICAgdGhpcy5kaXNwbGF5LndyYXBwZXIuY2xhc3NOYW1lICs9IFwiIENvZGVNaXJyb3Itd3JhcFwiO1xyXG4gICAgaWYgKG9wdGlvbnMuYXV0b2ZvY3VzICYmICFtb2JpbGUpIGRpc3BsYXkuaW5wdXQuZm9jdXMoKTtcclxuICAgIGluaXRTY3JvbGxiYXJzKHRoaXMpO1xyXG5cclxuICAgIHRoaXMuc3RhdGUgPSB7XHJcbiAgICAgIGtleU1hcHM6IFtdLCAgLy8gc3RvcmVzIG1hcHMgYWRkZWQgYnkgYWRkS2V5TWFwXHJcbiAgICAgIG92ZXJsYXlzOiBbXSwgLy8gaGlnaGxpZ2h0aW5nIG92ZXJsYXlzLCBhcyBhZGRlZCBieSBhZGRPdmVybGF5XHJcbiAgICAgIG1vZGVHZW46IDAsICAgLy8gYnVtcGVkIHdoZW4gbW9kZS9vdmVybGF5IGNoYW5nZXMsIHVzZWQgdG8gaW52YWxpZGF0ZSBoaWdobGlnaHRpbmcgaW5mb1xyXG4gICAgICBvdmVyd3JpdGU6IGZhbHNlLFxyXG4gICAgICBkZWxheWluZ0JsdXJFdmVudDogZmFsc2UsXHJcbiAgICAgIGZvY3VzZWQ6IGZhbHNlLFxyXG4gICAgICBzdXBwcmVzc0VkaXRzOiBmYWxzZSwgLy8gdXNlZCB0byBkaXNhYmxlIGVkaXRpbmcgZHVyaW5nIGtleSBoYW5kbGVycyB3aGVuIGluIHJlYWRPbmx5IG1vZGVcclxuICAgICAgcGFzdGVJbmNvbWluZzogZmFsc2UsIGN1dEluY29taW5nOiBmYWxzZSwgLy8gaGVscCByZWNvZ25pemUgcGFzdGUvY3V0IGVkaXRzIGluIGlucHV0LnBvbGxcclxuICAgICAgc2VsZWN0aW5nVGV4dDogZmFsc2UsXHJcbiAgICAgIGRyYWdnaW5nVGV4dDogZmFsc2UsXHJcbiAgICAgIGhpZ2hsaWdodDogbmV3IERlbGF5ZWQoKSwgLy8gc3RvcmVzIGhpZ2hsaWdodCB3b3JrZXIgdGltZW91dFxyXG4gICAgICBrZXlTZXE6IG51bGwsICAvLyBVbmZpbmlzaGVkIGtleSBzZXF1ZW5jZVxyXG4gICAgICBzcGVjaWFsQ2hhcnM6IG51bGxcclxuICAgIH07XHJcblxyXG4gICAgdmFyIGNtID0gdGhpcztcclxuXHJcbiAgICAvLyBPdmVycmlkZSBtYWdpYyB0ZXh0YXJlYSBjb250ZW50IHJlc3RvcmUgdGhhdCBJRSBzb21ldGltZXMgZG9lc1xyXG4gICAgLy8gb24gb3VyIGhpZGRlbiB0ZXh0YXJlYSBvbiByZWxvYWRcclxuICAgIGlmIChpZSAmJiBpZV92ZXJzaW9uIDwgMTEpIHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7IGNtLmRpc3BsYXkuaW5wdXQucmVzZXQodHJ1ZSk7IH0sIDIwKTtcclxuXHJcbiAgICByZWdpc3RlckV2ZW50SGFuZGxlcnModGhpcyk7XHJcbiAgICBlbnN1cmVHbG9iYWxIYW5kbGVycygpO1xyXG5cclxuICAgIHN0YXJ0T3BlcmF0aW9uKHRoaXMpO1xyXG4gICAgdGhpcy5jdXJPcC5mb3JjZVVwZGF0ZSA9IHRydWU7XHJcbiAgICBhdHRhY2hEb2ModGhpcywgZG9jKTtcclxuXHJcbiAgICBpZiAoKG9wdGlvbnMuYXV0b2ZvY3VzICYmICFtb2JpbGUpIHx8IGNtLmhhc0ZvY3VzKCkpXHJcbiAgICAgIHNldFRpbWVvdXQoYmluZChvbkZvY3VzLCB0aGlzKSwgMjApO1xyXG4gICAgZWxzZVxyXG4gICAgICBvbkJsdXIodGhpcyk7XHJcblxyXG4gICAgZm9yICh2YXIgb3B0IGluIG9wdGlvbkhhbmRsZXJzKSBpZiAob3B0aW9uSGFuZGxlcnMuaGFzT3duUHJvcGVydHkob3B0KSlcclxuICAgICAgb3B0aW9uSGFuZGxlcnNbb3B0XSh0aGlzLCBvcHRpb25zW29wdF0sIEluaXQpO1xyXG4gICAgbWF5YmVVcGRhdGVMaW5lTnVtYmVyV2lkdGgodGhpcyk7XHJcbiAgICBpZiAob3B0aW9ucy5maW5pc2hJbml0KSBvcHRpb25zLmZpbmlzaEluaXQodGhpcyk7XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGluaXRIb29rcy5sZW5ndGg7ICsraSkgaW5pdEhvb2tzW2ldKHRoaXMpO1xyXG4gICAgZW5kT3BlcmF0aW9uKHRoaXMpO1xyXG4gICAgLy8gU3VwcHJlc3Mgb3B0aW1pemVsZWdpYmlsaXR5IGluIFdlYmtpdCwgc2luY2UgaXQgYnJlYWtzIHRleHRcclxuICAgIC8vIG1lYXN1cmluZyBvbiBsaW5lIHdyYXBwaW5nIGJvdW5kYXJpZXMuXHJcbiAgICBpZiAod2Via2l0ICYmIG9wdGlvbnMubGluZVdyYXBwaW5nICYmXHJcbiAgICAgICAgZ2V0Q29tcHV0ZWRTdHlsZShkaXNwbGF5LmxpbmVEaXYpLnRleHRSZW5kZXJpbmcgPT0gXCJvcHRpbWl6ZWxlZ2liaWxpdHlcIilcclxuICAgICAgZGlzcGxheS5saW5lRGl2LnN0eWxlLnRleHRSZW5kZXJpbmcgPSBcImF1dG9cIjtcclxuICB9XHJcblxyXG4gIC8vIERJU1BMQVkgQ09OU1RSVUNUT1JcclxuXHJcbiAgLy8gVGhlIGRpc3BsYXkgaGFuZGxlcyB0aGUgRE9NIGludGVncmF0aW9uLCBib3RoIGZvciBpbnB1dCByZWFkaW5nXHJcbiAgLy8gYW5kIGNvbnRlbnQgZHJhd2luZy4gSXQgaG9sZHMgcmVmZXJlbmNlcyB0byBET00gbm9kZXMgYW5kXHJcbiAgLy8gZGlzcGxheS1yZWxhdGVkIHN0YXRlLlxyXG5cclxuICBmdW5jdGlvbiBEaXNwbGF5KHBsYWNlLCBkb2MsIGlucHV0KSB7XHJcbiAgICB2YXIgZCA9IHRoaXM7XHJcbiAgICB0aGlzLmlucHV0ID0gaW5wdXQ7XHJcblxyXG4gICAgLy8gQ292ZXJzIGJvdHRvbS1yaWdodCBzcXVhcmUgd2hlbiBib3RoIHNjcm9sbGJhcnMgYXJlIHByZXNlbnQuXHJcbiAgICBkLnNjcm9sbGJhckZpbGxlciA9IGVsdChcImRpdlwiLCBudWxsLCBcIkNvZGVNaXJyb3Itc2Nyb2xsYmFyLWZpbGxlclwiKTtcclxuICAgIGQuc2Nyb2xsYmFyRmlsbGVyLnNldEF0dHJpYnV0ZShcImNtLW5vdC1jb250ZW50XCIsIFwidHJ1ZVwiKTtcclxuICAgIC8vIENvdmVycyBib3R0b20gb2YgZ3V0dGVyIHdoZW4gY292ZXJHdXR0ZXJOZXh0VG9TY3JvbGxiYXIgaXMgb25cclxuICAgIC8vIGFuZCBoIHNjcm9sbGJhciBpcyBwcmVzZW50LlxyXG4gICAgZC5ndXR0ZXJGaWxsZXIgPSBlbHQoXCJkaXZcIiwgbnVsbCwgXCJDb2RlTWlycm9yLWd1dHRlci1maWxsZXJcIik7XHJcbiAgICBkLmd1dHRlckZpbGxlci5zZXRBdHRyaWJ1dGUoXCJjbS1ub3QtY29udGVudFwiLCBcInRydWVcIik7XHJcbiAgICAvLyBXaWxsIGNvbnRhaW4gdGhlIGFjdHVhbCBjb2RlLCBwb3NpdGlvbmVkIHRvIGNvdmVyIHRoZSB2aWV3cG9ydC5cclxuICAgIGQubGluZURpdiA9IGVsdChcImRpdlwiLCBudWxsLCBcIkNvZGVNaXJyb3ItY29kZVwiKTtcclxuICAgIC8vIEVsZW1lbnRzIGFyZSBhZGRlZCB0byB0aGVzZSB0byByZXByZXNlbnQgc2VsZWN0aW9uIGFuZCBjdXJzb3JzLlxyXG4gICAgZC5zZWxlY3Rpb25EaXYgPSBlbHQoXCJkaXZcIiwgbnVsbCwgbnVsbCwgXCJwb3NpdGlvbjogcmVsYXRpdmU7IHotaW5kZXg6IDFcIik7XHJcbiAgICBkLmN1cnNvckRpdiA9IGVsdChcImRpdlwiLCBudWxsLCBcIkNvZGVNaXJyb3ItY3Vyc29yc1wiKTtcclxuICAgIC8vIEEgdmlzaWJpbGl0eTogaGlkZGVuIGVsZW1lbnQgdXNlZCB0byBmaW5kIHRoZSBzaXplIG9mIHRoaW5ncy5cclxuICAgIGQubWVhc3VyZSA9IGVsdChcImRpdlwiLCBudWxsLCBcIkNvZGVNaXJyb3ItbWVhc3VyZVwiKTtcclxuICAgIC8vIFdoZW4gbGluZXMgb3V0c2lkZSBvZiB0aGUgdmlld3BvcnQgYXJlIG1lYXN1cmVkLCB0aGV5IGFyZSBkcmF3biBpbiB0aGlzLlxyXG4gICAgZC5saW5lTWVhc3VyZSA9IGVsdChcImRpdlwiLCBudWxsLCBcIkNvZGVNaXJyb3ItbWVhc3VyZVwiKTtcclxuICAgIC8vIFdyYXBzIGV2ZXJ5dGhpbmcgdGhhdCBuZWVkcyB0byBleGlzdCBpbnNpZGUgdGhlIHZlcnRpY2FsbHktcGFkZGVkIGNvb3JkaW5hdGUgc3lzdGVtXHJcbiAgICBkLmxpbmVTcGFjZSA9IGVsdChcImRpdlwiLCBbZC5tZWFzdXJlLCBkLmxpbmVNZWFzdXJlLCBkLnNlbGVjdGlvbkRpdiwgZC5jdXJzb3JEaXYsIGQubGluZURpdl0sXHJcbiAgICAgICAgICAgICAgICAgICAgICBudWxsLCBcInBvc2l0aW9uOiByZWxhdGl2ZTsgb3V0bGluZTogbm9uZVwiKTtcclxuICAgIC8vIE1vdmVkIGFyb3VuZCBpdHMgcGFyZW50IHRvIGNvdmVyIHZpc2libGUgdmlldy5cclxuICAgIGQubW92ZXIgPSBlbHQoXCJkaXZcIiwgW2VsdChcImRpdlwiLCBbZC5saW5lU3BhY2VdLCBcIkNvZGVNaXJyb3ItbGluZXNcIildLCBudWxsLCBcInBvc2l0aW9uOiByZWxhdGl2ZVwiKTtcclxuICAgIC8vIFNldCB0byB0aGUgaGVpZ2h0IG9mIHRoZSBkb2N1bWVudCwgYWxsb3dpbmcgc2Nyb2xsaW5nLlxyXG4gICAgZC5zaXplciA9IGVsdChcImRpdlwiLCBbZC5tb3Zlcl0sIFwiQ29kZU1pcnJvci1zaXplclwiKTtcclxuICAgIGQuc2l6ZXJXaWR0aCA9IG51bGw7XHJcbiAgICAvLyBCZWhhdmlvciBvZiBlbHRzIHdpdGggb3ZlcmZsb3c6IGF1dG8gYW5kIHBhZGRpbmcgaXNcclxuICAgIC8vIGluY29uc2lzdGVudCBhY3Jvc3MgYnJvd3NlcnMuIFRoaXMgaXMgdXNlZCB0byBlbnN1cmUgdGhlXHJcbiAgICAvLyBzY3JvbGxhYmxlIGFyZWEgaXMgYmlnIGVub3VnaC5cclxuICAgIGQuaGVpZ2h0Rm9yY2VyID0gZWx0KFwiZGl2XCIsIG51bGwsIG51bGwsIFwicG9zaXRpb246IGFic29sdXRlOyBoZWlnaHQ6IFwiICsgc2Nyb2xsZXJHYXAgKyBcInB4OyB3aWR0aDogMXB4O1wiKTtcclxuICAgIC8vIFdpbGwgY29udGFpbiB0aGUgZ3V0dGVycywgaWYgYW55LlxyXG4gICAgZC5ndXR0ZXJzID0gZWx0KFwiZGl2XCIsIG51bGwsIFwiQ29kZU1pcnJvci1ndXR0ZXJzXCIpO1xyXG4gICAgZC5saW5lR3V0dGVyID0gbnVsbDtcclxuICAgIC8vIEFjdHVhbCBzY3JvbGxhYmxlIGVsZW1lbnQuXHJcbiAgICBkLnNjcm9sbGVyID0gZWx0KFwiZGl2XCIsIFtkLnNpemVyLCBkLmhlaWdodEZvcmNlciwgZC5ndXR0ZXJzXSwgXCJDb2RlTWlycm9yLXNjcm9sbFwiKTtcclxuICAgIGQuc2Nyb2xsZXIuc2V0QXR0cmlidXRlKFwidGFiSW5kZXhcIiwgXCItMVwiKTtcclxuICAgIC8vIFRoZSBlbGVtZW50IGluIHdoaWNoIHRoZSBlZGl0b3IgbGl2ZXMuXHJcbiAgICBkLndyYXBwZXIgPSBlbHQoXCJkaXZcIiwgW2Quc2Nyb2xsYmFyRmlsbGVyLCBkLmd1dHRlckZpbGxlciwgZC5zY3JvbGxlcl0sIFwiQ29kZU1pcnJvclwiKTtcclxuXHJcbiAgICAvLyBXb3JrIGFyb3VuZCBJRTcgei1pbmRleCBidWcgKG5vdCBwZXJmZWN0LCBoZW5jZSBJRTcgbm90IHJlYWxseSBiZWluZyBzdXBwb3J0ZWQpXHJcbiAgICBpZiAoaWUgJiYgaWVfdmVyc2lvbiA8IDgpIHsgZC5ndXR0ZXJzLnN0eWxlLnpJbmRleCA9IC0xOyBkLnNjcm9sbGVyLnN0eWxlLnBhZGRpbmdSaWdodCA9IDA7IH1cclxuICAgIGlmICghd2Via2l0ICYmICEoZ2Vja28gJiYgbW9iaWxlKSkgZC5zY3JvbGxlci5kcmFnZ2FibGUgPSB0cnVlO1xyXG5cclxuICAgIGlmIChwbGFjZSkge1xyXG4gICAgICBpZiAocGxhY2UuYXBwZW5kQ2hpbGQpIHBsYWNlLmFwcGVuZENoaWxkKGQud3JhcHBlcik7XHJcbiAgICAgIGVsc2UgcGxhY2UoZC53cmFwcGVyKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDdXJyZW50IHJlbmRlcmVkIHJhbmdlIChtYXkgYmUgYmlnZ2VyIHRoYW4gdGhlIHZpZXcgd2luZG93KS5cclxuICAgIGQudmlld0Zyb20gPSBkLnZpZXdUbyA9IGRvYy5maXJzdDtcclxuICAgIGQucmVwb3J0ZWRWaWV3RnJvbSA9IGQucmVwb3J0ZWRWaWV3VG8gPSBkb2MuZmlyc3Q7XHJcbiAgICAvLyBJbmZvcm1hdGlvbiBhYm91dCB0aGUgcmVuZGVyZWQgbGluZXMuXHJcbiAgICBkLnZpZXcgPSBbXTtcclxuICAgIGQucmVuZGVyZWRWaWV3ID0gbnVsbDtcclxuICAgIC8vIEhvbGRzIGluZm8gYWJvdXQgYSBzaW5nbGUgcmVuZGVyZWQgbGluZSB3aGVuIGl0IHdhcyByZW5kZXJlZFxyXG4gICAgLy8gZm9yIG1lYXN1cmVtZW50LCB3aGlsZSBub3QgaW4gdmlldy5cclxuICAgIGQuZXh0ZXJuYWxNZWFzdXJlZCA9IG51bGw7XHJcbiAgICAvLyBFbXB0eSBzcGFjZSAoaW4gcGl4ZWxzKSBhYm92ZSB0aGUgdmlld1xyXG4gICAgZC52aWV3T2Zmc2V0ID0gMDtcclxuICAgIGQubGFzdFdyYXBIZWlnaHQgPSBkLmxhc3RXcmFwV2lkdGggPSAwO1xyXG4gICAgZC51cGRhdGVMaW5lTnVtYmVycyA9IG51bGw7XHJcblxyXG4gICAgZC5uYXRpdmVCYXJXaWR0aCA9IGQuYmFySGVpZ2h0ID0gZC5iYXJXaWR0aCA9IDA7XHJcbiAgICBkLnNjcm9sbGJhcnNDbGlwcGVkID0gZmFsc2U7XHJcblxyXG4gICAgLy8gVXNlZCB0byBvbmx5IHJlc2l6ZSB0aGUgbGluZSBudW1iZXIgZ3V0dGVyIHdoZW4gbmVjZXNzYXJ5ICh3aGVuXHJcbiAgICAvLyB0aGUgYW1vdW50IG9mIGxpbmVzIGNyb3NzZXMgYSBib3VuZGFyeSB0aGF0IG1ha2VzIGl0cyB3aWR0aCBjaGFuZ2UpXHJcbiAgICBkLmxpbmVOdW1XaWR0aCA9IGQubGluZU51bUlubmVyV2lkdGggPSBkLmxpbmVOdW1DaGFycyA9IG51bGw7XHJcbiAgICAvLyBTZXQgdG8gdHJ1ZSB3aGVuIGEgbm9uLWhvcml6b250YWwtc2Nyb2xsaW5nIGxpbmUgd2lkZ2V0IGlzXHJcbiAgICAvLyBhZGRlZC4gQXMgYW4gb3B0aW1pemF0aW9uLCBsaW5lIHdpZGdldCBhbGlnbmluZyBpcyBza2lwcGVkIHdoZW5cclxuICAgIC8vIHRoaXMgaXMgZmFsc2UuXHJcbiAgICBkLmFsaWduV2lkZ2V0cyA9IGZhbHNlO1xyXG5cclxuICAgIGQuY2FjaGVkQ2hhcldpZHRoID0gZC5jYWNoZWRUZXh0SGVpZ2h0ID0gZC5jYWNoZWRQYWRkaW5nSCA9IG51bGw7XHJcblxyXG4gICAgLy8gVHJhY2tzIHRoZSBtYXhpbXVtIGxpbmUgbGVuZ3RoIHNvIHRoYXQgdGhlIGhvcml6b250YWwgc2Nyb2xsYmFyXHJcbiAgICAvLyBjYW4gYmUga2VwdCBzdGF0aWMgd2hlbiBzY3JvbGxpbmcuXHJcbiAgICBkLm1heExpbmUgPSBudWxsO1xyXG4gICAgZC5tYXhMaW5lTGVuZ3RoID0gMDtcclxuICAgIGQubWF4TGluZUNoYW5nZWQgPSBmYWxzZTtcclxuXHJcbiAgICAvLyBVc2VkIGZvciBtZWFzdXJpbmcgd2hlZWwgc2Nyb2xsaW5nIGdyYW51bGFyaXR5XHJcbiAgICBkLndoZWVsRFggPSBkLndoZWVsRFkgPSBkLndoZWVsU3RhcnRYID0gZC53aGVlbFN0YXJ0WSA9IG51bGw7XHJcblxyXG4gICAgLy8gVHJ1ZSB3aGVuIHNoaWZ0IGlzIGhlbGQgZG93bi5cclxuICAgIGQuc2hpZnQgPSBmYWxzZTtcclxuXHJcbiAgICAvLyBVc2VkIHRvIHRyYWNrIHdoZXRoZXIgYW55dGhpbmcgaGFwcGVuZWQgc2luY2UgdGhlIGNvbnRleHQgbWVudVxyXG4gICAgLy8gd2FzIG9wZW5lZC5cclxuICAgIGQuc2VsRm9yQ29udGV4dE1lbnUgPSBudWxsO1xyXG5cclxuICAgIGQuYWN0aXZlVG91Y2ggPSBudWxsO1xyXG5cclxuICAgIGlucHV0LmluaXQoZCk7XHJcbiAgfVxyXG5cclxuICAvLyBTVEFURSBVUERBVEVTXHJcblxyXG4gIC8vIFVzZWQgdG8gZ2V0IHRoZSBlZGl0b3IgaW50byBhIGNvbnNpc3RlbnQgc3RhdGUgYWdhaW4gd2hlbiBvcHRpb25zIGNoYW5nZS5cclxuXHJcbiAgZnVuY3Rpb24gbG9hZE1vZGUoY20pIHtcclxuICAgIGNtLmRvYy5tb2RlID0gQ29kZU1pcnJvci5nZXRNb2RlKGNtLm9wdGlvbnMsIGNtLmRvYy5tb2RlT3B0aW9uKTtcclxuICAgIHJlc2V0TW9kZVN0YXRlKGNtKTtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIHJlc2V0TW9kZVN0YXRlKGNtKSB7XHJcbiAgICBjbS5kb2MuaXRlcihmdW5jdGlvbihsaW5lKSB7XHJcbiAgICAgIGlmIChsaW5lLnN0YXRlQWZ0ZXIpIGxpbmUuc3RhdGVBZnRlciA9IG51bGw7XHJcbiAgICAgIGlmIChsaW5lLnN0eWxlcykgbGluZS5zdHlsZXMgPSBudWxsO1xyXG4gICAgfSk7XHJcbiAgICBjbS5kb2MuZnJvbnRpZXIgPSBjbS5kb2MuZmlyc3Q7XHJcbiAgICBzdGFydFdvcmtlcihjbSwgMTAwKTtcclxuICAgIGNtLnN0YXRlLm1vZGVHZW4rKztcclxuICAgIGlmIChjbS5jdXJPcCkgcmVnQ2hhbmdlKGNtKTtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIHdyYXBwaW5nQ2hhbmdlZChjbSkge1xyXG4gICAgaWYgKGNtLm9wdGlvbnMubGluZVdyYXBwaW5nKSB7XHJcbiAgICAgIGFkZENsYXNzKGNtLmRpc3BsYXkud3JhcHBlciwgXCJDb2RlTWlycm9yLXdyYXBcIik7XHJcbiAgICAgIGNtLmRpc3BsYXkuc2l6ZXIuc3R5bGUubWluV2lkdGggPSBcIlwiO1xyXG4gICAgICBjbS5kaXNwbGF5LnNpemVyV2lkdGggPSBudWxsO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgcm1DbGFzcyhjbS5kaXNwbGF5LndyYXBwZXIsIFwiQ29kZU1pcnJvci13cmFwXCIpO1xyXG4gICAgICBmaW5kTWF4TGluZShjbSk7XHJcbiAgICB9XHJcbiAgICBlc3RpbWF0ZUxpbmVIZWlnaHRzKGNtKTtcclxuICAgIHJlZ0NoYW5nZShjbSk7XHJcbiAgICBjbGVhckNhY2hlcyhjbSk7XHJcbiAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCl7dXBkYXRlU2Nyb2xsYmFycyhjbSk7fSwgMTAwKTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybnMgYSBmdW5jdGlvbiB0aGF0IGVzdGltYXRlcyB0aGUgaGVpZ2h0IG9mIGEgbGluZSwgdG8gdXNlIGFzXHJcbiAgLy8gZmlyc3QgYXBwcm94aW1hdGlvbiB1bnRpbCB0aGUgbGluZSBiZWNvbWVzIHZpc2libGUgKGFuZCBpcyB0aHVzXHJcbiAgLy8gcHJvcGVybHkgbWVhc3VyYWJsZSkuXHJcbiAgZnVuY3Rpb24gZXN0aW1hdGVIZWlnaHQoY20pIHtcclxuICAgIHZhciB0aCA9IHRleHRIZWlnaHQoY20uZGlzcGxheSksIHdyYXBwaW5nID0gY20ub3B0aW9ucy5saW5lV3JhcHBpbmc7XHJcbiAgICB2YXIgcGVyTGluZSA9IHdyYXBwaW5nICYmIE1hdGgubWF4KDUsIGNtLmRpc3BsYXkuc2Nyb2xsZXIuY2xpZW50V2lkdGggLyBjaGFyV2lkdGgoY20uZGlzcGxheSkgLSAzKTtcclxuICAgIHJldHVybiBmdW5jdGlvbihsaW5lKSB7XHJcbiAgICAgIGlmIChsaW5lSXNIaWRkZW4oY20uZG9jLCBsaW5lKSkgcmV0dXJuIDA7XHJcblxyXG4gICAgICB2YXIgd2lkZ2V0c0hlaWdodCA9IDA7XHJcbiAgICAgIGlmIChsaW5lLndpZGdldHMpIGZvciAodmFyIGkgPSAwOyBpIDwgbGluZS53aWRnZXRzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgaWYgKGxpbmUud2lkZ2V0c1tpXS5oZWlnaHQpIHdpZGdldHNIZWlnaHQgKz0gbGluZS53aWRnZXRzW2ldLmhlaWdodDtcclxuICAgICAgfVxyXG5cclxuICAgICAgaWYgKHdyYXBwaW5nKVxyXG4gICAgICAgIHJldHVybiB3aWRnZXRzSGVpZ2h0ICsgKE1hdGguY2VpbChsaW5lLnRleHQubGVuZ3RoIC8gcGVyTGluZSkgfHwgMSkgKiB0aDtcclxuICAgICAgZWxzZVxyXG4gICAgICAgIHJldHVybiB3aWRnZXRzSGVpZ2h0ICsgdGg7XHJcbiAgICB9O1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gZXN0aW1hdGVMaW5lSGVpZ2h0cyhjbSkge1xyXG4gICAgdmFyIGRvYyA9IGNtLmRvYywgZXN0ID0gZXN0aW1hdGVIZWlnaHQoY20pO1xyXG4gICAgZG9jLml0ZXIoZnVuY3Rpb24obGluZSkge1xyXG4gICAgICB2YXIgZXN0SGVpZ2h0ID0gZXN0KGxpbmUpO1xyXG4gICAgICBpZiAoZXN0SGVpZ2h0ICE9IGxpbmUuaGVpZ2h0KSB1cGRhdGVMaW5lSGVpZ2h0KGxpbmUsIGVzdEhlaWdodCk7XHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIHRoZW1lQ2hhbmdlZChjbSkge1xyXG4gICAgY20uZGlzcGxheS53cmFwcGVyLmNsYXNzTmFtZSA9IGNtLmRpc3BsYXkud3JhcHBlci5jbGFzc05hbWUucmVwbGFjZSgvXFxzKmNtLXMtXFxTKy9nLCBcIlwiKSArXHJcbiAgICAgIGNtLm9wdGlvbnMudGhlbWUucmVwbGFjZSgvKF58XFxzKVxccyovZywgXCIgY20tcy1cIik7XHJcbiAgICBjbGVhckNhY2hlcyhjbSk7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBndXR0ZXJzQ2hhbmdlZChjbSkge1xyXG4gICAgdXBkYXRlR3V0dGVycyhjbSk7XHJcbiAgICByZWdDaGFuZ2UoY20pO1xyXG4gICAgc2V0VGltZW91dChmdW5jdGlvbigpe2FsaWduSG9yaXpvbnRhbGx5KGNtKTt9LCAyMCk7XHJcbiAgfVxyXG5cclxuICAvLyBSZWJ1aWxkIHRoZSBndXR0ZXIgZWxlbWVudHMsIGVuc3VyZSB0aGUgbWFyZ2luIHRvIHRoZSBsZWZ0IG9mIHRoZVxyXG4gIC8vIGNvZGUgbWF0Y2hlcyB0aGVpciB3aWR0aC5cclxuICBmdW5jdGlvbiB1cGRhdGVHdXR0ZXJzKGNtKSB7XHJcbiAgICB2YXIgZ3V0dGVycyA9IGNtLmRpc3BsYXkuZ3V0dGVycywgc3BlY3MgPSBjbS5vcHRpb25zLmd1dHRlcnM7XHJcbiAgICByZW1vdmVDaGlsZHJlbihndXR0ZXJzKTtcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgc3BlY3MubGVuZ3RoOyArK2kpIHtcclxuICAgICAgdmFyIGd1dHRlckNsYXNzID0gc3BlY3NbaV07XHJcbiAgICAgIHZhciBnRWx0ID0gZ3V0dGVycy5hcHBlbmRDaGlsZChlbHQoXCJkaXZcIiwgbnVsbCwgXCJDb2RlTWlycm9yLWd1dHRlciBcIiArIGd1dHRlckNsYXNzKSk7XHJcbiAgICAgIGlmIChndXR0ZXJDbGFzcyA9PSBcIkNvZGVNaXJyb3ItbGluZW51bWJlcnNcIikge1xyXG4gICAgICAgIGNtLmRpc3BsYXkubGluZUd1dHRlciA9IGdFbHQ7XHJcbiAgICAgICAgZ0VsdC5zdHlsZS53aWR0aCA9IChjbS5kaXNwbGF5LmxpbmVOdW1XaWR0aCB8fCAxKSArIFwicHhcIjtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgZ3V0dGVycy5zdHlsZS5kaXNwbGF5ID0gaSA/IFwiXCIgOiBcIm5vbmVcIjtcclxuICAgIHVwZGF0ZUd1dHRlclNwYWNlKGNtKTtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIHVwZGF0ZUd1dHRlclNwYWNlKGNtKSB7XHJcbiAgICB2YXIgd2lkdGggPSBjbS5kaXNwbGF5Lmd1dHRlcnMub2Zmc2V0V2lkdGg7XHJcbiAgICBjbS5kaXNwbGF5LnNpemVyLnN0eWxlLm1hcmdpbkxlZnQgPSB3aWR0aCArIFwicHhcIjtcclxuICB9XHJcblxyXG4gIC8vIENvbXB1dGUgdGhlIGNoYXJhY3RlciBsZW5ndGggb2YgYSBsaW5lLCB0YWtpbmcgaW50byBhY2NvdW50XHJcbiAgLy8gY29sbGFwc2VkIHJhbmdlcyAoc2VlIG1hcmtUZXh0KSB0aGF0IG1pZ2h0IGhpZGUgcGFydHMsIGFuZCBqb2luXHJcbiAgLy8gb3RoZXIgbGluZXMgb250byBpdC5cclxuICBmdW5jdGlvbiBsaW5lTGVuZ3RoKGxpbmUpIHtcclxuICAgIGlmIChsaW5lLmhlaWdodCA9PSAwKSByZXR1cm4gMDtcclxuICAgIHZhciBsZW4gPSBsaW5lLnRleHQubGVuZ3RoLCBtZXJnZWQsIGN1ciA9IGxpbmU7XHJcbiAgICB3aGlsZSAobWVyZ2VkID0gY29sbGFwc2VkU3BhbkF0U3RhcnQoY3VyKSkge1xyXG4gICAgICB2YXIgZm91bmQgPSBtZXJnZWQuZmluZCgwLCB0cnVlKTtcclxuICAgICAgY3VyID0gZm91bmQuZnJvbS5saW5lO1xyXG4gICAgICBsZW4gKz0gZm91bmQuZnJvbS5jaCAtIGZvdW5kLnRvLmNoO1xyXG4gICAgfVxyXG4gICAgY3VyID0gbGluZTtcclxuICAgIHdoaWxlIChtZXJnZWQgPSBjb2xsYXBzZWRTcGFuQXRFbmQoY3VyKSkge1xyXG4gICAgICB2YXIgZm91bmQgPSBtZXJnZWQuZmluZCgwLCB0cnVlKTtcclxuICAgICAgbGVuIC09IGN1ci50ZXh0Lmxlbmd0aCAtIGZvdW5kLmZyb20uY2g7XHJcbiAgICAgIGN1ciA9IGZvdW5kLnRvLmxpbmU7XHJcbiAgICAgIGxlbiArPSBjdXIudGV4dC5sZW5ndGggLSBmb3VuZC50by5jaDtcclxuICAgIH1cclxuICAgIHJldHVybiBsZW47XHJcbiAgfVxyXG5cclxuICAvLyBGaW5kIHRoZSBsb25nZXN0IGxpbmUgaW4gdGhlIGRvY3VtZW50LlxyXG4gIGZ1bmN0aW9uIGZpbmRNYXhMaW5lKGNtKSB7XHJcbiAgICB2YXIgZCA9IGNtLmRpc3BsYXksIGRvYyA9IGNtLmRvYztcclxuICAgIGQubWF4TGluZSA9IGdldExpbmUoZG9jLCBkb2MuZmlyc3QpO1xyXG4gICAgZC5tYXhMaW5lTGVuZ3RoID0gbGluZUxlbmd0aChkLm1heExpbmUpO1xyXG4gICAgZC5tYXhMaW5lQ2hhbmdlZCA9IHRydWU7XHJcbiAgICBkb2MuaXRlcihmdW5jdGlvbihsaW5lKSB7XHJcbiAgICAgIHZhciBsZW4gPSBsaW5lTGVuZ3RoKGxpbmUpO1xyXG4gICAgICBpZiAobGVuID4gZC5tYXhMaW5lTGVuZ3RoKSB7XHJcbiAgICAgICAgZC5tYXhMaW5lTGVuZ3RoID0gbGVuO1xyXG4gICAgICAgIGQubWF4TGluZSA9IGxpbmU7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgLy8gTWFrZSBzdXJlIHRoZSBndXR0ZXJzIG9wdGlvbnMgY29udGFpbnMgdGhlIGVsZW1lbnRcclxuICAvLyBcIkNvZGVNaXJyb3ItbGluZW51bWJlcnNcIiB3aGVuIHRoZSBsaW5lTnVtYmVycyBvcHRpb24gaXMgdHJ1ZS5cclxuICBmdW5jdGlvbiBzZXRHdXR0ZXJzRm9yTGluZU51bWJlcnMob3B0aW9ucykge1xyXG4gICAgdmFyIGZvdW5kID0gaW5kZXhPZihvcHRpb25zLmd1dHRlcnMsIFwiQ29kZU1pcnJvci1saW5lbnVtYmVyc1wiKTtcclxuICAgIGlmIChmb3VuZCA9PSAtMSAmJiBvcHRpb25zLmxpbmVOdW1iZXJzKSB7XHJcbiAgICAgIG9wdGlvbnMuZ3V0dGVycyA9IG9wdGlvbnMuZ3V0dGVycy5jb25jYXQoW1wiQ29kZU1pcnJvci1saW5lbnVtYmVyc1wiXSk7XHJcbiAgICB9IGVsc2UgaWYgKGZvdW5kID4gLTEgJiYgIW9wdGlvbnMubGluZU51bWJlcnMpIHtcclxuICAgICAgb3B0aW9ucy5ndXR0ZXJzID0gb3B0aW9ucy5ndXR0ZXJzLnNsaWNlKDApO1xyXG4gICAgICBvcHRpb25zLmd1dHRlcnMuc3BsaWNlKGZvdW5kLCAxKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFNDUk9MTEJBUlNcclxuXHJcbiAgLy8gUHJlcGFyZSBET00gcmVhZHMgbmVlZGVkIHRvIHVwZGF0ZSB0aGUgc2Nyb2xsYmFycy4gRG9uZSBpbiBvbmVcclxuICAvLyBzaG90IHRvIG1pbmltaXplIHVwZGF0ZS9tZWFzdXJlIHJvdW5kdHJpcHMuXHJcbiAgZnVuY3Rpb24gbWVhc3VyZUZvclNjcm9sbGJhcnMoY20pIHtcclxuICAgIHZhciBkID0gY20uZGlzcGxheSwgZ3V0dGVyVyA9IGQuZ3V0dGVycy5vZmZzZXRXaWR0aDtcclxuICAgIHZhciBkb2NIID0gTWF0aC5yb3VuZChjbS5kb2MuaGVpZ2h0ICsgcGFkZGluZ1ZlcnQoY20uZGlzcGxheSkpO1xyXG4gICAgcmV0dXJuIHtcclxuICAgICAgY2xpZW50SGVpZ2h0OiBkLnNjcm9sbGVyLmNsaWVudEhlaWdodCxcclxuICAgICAgdmlld0hlaWdodDogZC53cmFwcGVyLmNsaWVudEhlaWdodCxcclxuICAgICAgc2Nyb2xsV2lkdGg6IGQuc2Nyb2xsZXIuc2Nyb2xsV2lkdGgsIGNsaWVudFdpZHRoOiBkLnNjcm9sbGVyLmNsaWVudFdpZHRoLFxyXG4gICAgICB2aWV3V2lkdGg6IGQud3JhcHBlci5jbGllbnRXaWR0aCxcclxuICAgICAgYmFyTGVmdDogY20ub3B0aW9ucy5maXhlZEd1dHRlciA/IGd1dHRlclcgOiAwLFxyXG4gICAgICBkb2NIZWlnaHQ6IGRvY0gsXHJcbiAgICAgIHNjcm9sbEhlaWdodDogZG9jSCArIHNjcm9sbEdhcChjbSkgKyBkLmJhckhlaWdodCxcclxuICAgICAgbmF0aXZlQmFyV2lkdGg6IGQubmF0aXZlQmFyV2lkdGgsXHJcbiAgICAgIGd1dHRlcldpZHRoOiBndXR0ZXJXXHJcbiAgICB9O1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gTmF0aXZlU2Nyb2xsYmFycyhwbGFjZSwgc2Nyb2xsLCBjbSkge1xyXG4gICAgdGhpcy5jbSA9IGNtO1xyXG4gICAgdmFyIHZlcnQgPSB0aGlzLnZlcnQgPSBlbHQoXCJkaXZcIiwgW2VsdChcImRpdlwiLCBudWxsLCBudWxsLCBcIm1pbi13aWR0aDogMXB4XCIpXSwgXCJDb2RlTWlycm9yLXZzY3JvbGxiYXJcIik7XHJcbiAgICB2YXIgaG9yaXogPSB0aGlzLmhvcml6ID0gZWx0KFwiZGl2XCIsIFtlbHQoXCJkaXZcIiwgbnVsbCwgbnVsbCwgXCJoZWlnaHQ6IDEwMCU7IG1pbi1oZWlnaHQ6IDFweFwiKV0sIFwiQ29kZU1pcnJvci1oc2Nyb2xsYmFyXCIpO1xyXG4gICAgcGxhY2UodmVydCk7IHBsYWNlKGhvcml6KTtcclxuXHJcbiAgICBvbih2ZXJ0LCBcInNjcm9sbFwiLCBmdW5jdGlvbigpIHtcclxuICAgICAgaWYgKHZlcnQuY2xpZW50SGVpZ2h0KSBzY3JvbGwodmVydC5zY3JvbGxUb3AsIFwidmVydGljYWxcIik7XHJcbiAgICB9KTtcclxuICAgIG9uKGhvcml6LCBcInNjcm9sbFwiLCBmdW5jdGlvbigpIHtcclxuICAgICAgaWYgKGhvcml6LmNsaWVudFdpZHRoKSBzY3JvbGwoaG9yaXouc2Nyb2xsTGVmdCwgXCJob3Jpem9udGFsXCIpO1xyXG4gICAgfSk7XHJcblxyXG4gICAgdGhpcy5jaGVja2VkT3ZlcmxheSA9IGZhbHNlO1xyXG4gICAgLy8gTmVlZCB0byBzZXQgYSBtaW5pbXVtIHdpZHRoIHRvIHNlZSB0aGUgc2Nyb2xsYmFyIG9uIElFNyAoYnV0IG11c3Qgbm90IHNldCBpdCBvbiBJRTgpLlxyXG4gICAgaWYgKGllICYmIGllX3ZlcnNpb24gPCA4KSB0aGlzLmhvcml6LnN0eWxlLm1pbkhlaWdodCA9IHRoaXMudmVydC5zdHlsZS5taW5XaWR0aCA9IFwiMThweFwiO1xyXG4gIH1cclxuXHJcbiAgTmF0aXZlU2Nyb2xsYmFycy5wcm90b3R5cGUgPSBjb3B5T2JqKHtcclxuICAgIHVwZGF0ZTogZnVuY3Rpb24obWVhc3VyZSkge1xyXG4gICAgICB2YXIgbmVlZHNIID0gbWVhc3VyZS5zY3JvbGxXaWR0aCA+IG1lYXN1cmUuY2xpZW50V2lkdGggKyAxO1xyXG4gICAgICB2YXIgbmVlZHNWID0gbWVhc3VyZS5zY3JvbGxIZWlnaHQgPiBtZWFzdXJlLmNsaWVudEhlaWdodCArIDE7XHJcbiAgICAgIHZhciBzV2lkdGggPSBtZWFzdXJlLm5hdGl2ZUJhcldpZHRoO1xyXG5cclxuICAgICAgaWYgKG5lZWRzVikge1xyXG4gICAgICAgIHRoaXMudmVydC5zdHlsZS5kaXNwbGF5ID0gXCJibG9ja1wiO1xyXG4gICAgICAgIHRoaXMudmVydC5zdHlsZS5ib3R0b20gPSBuZWVkc0ggPyBzV2lkdGggKyBcInB4XCIgOiBcIjBcIjtcclxuICAgICAgICB2YXIgdG90YWxIZWlnaHQgPSBtZWFzdXJlLnZpZXdIZWlnaHQgLSAobmVlZHNIID8gc1dpZHRoIDogMCk7XHJcbiAgICAgICAgLy8gQSBidWcgaW4gSUU4IGNhbiBjYXVzZSB0aGlzIHZhbHVlIHRvIGJlIG5lZ2F0aXZlLCBzbyBndWFyZCBpdC5cclxuICAgICAgICB0aGlzLnZlcnQuZmlyc3RDaGlsZC5zdHlsZS5oZWlnaHQgPVxyXG4gICAgICAgICAgTWF0aC5tYXgoMCwgbWVhc3VyZS5zY3JvbGxIZWlnaHQgLSBtZWFzdXJlLmNsaWVudEhlaWdodCArIHRvdGFsSGVpZ2h0KSArIFwicHhcIjtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICB0aGlzLnZlcnQuc3R5bGUuZGlzcGxheSA9IFwiXCI7XHJcbiAgICAgICAgdGhpcy52ZXJ0LmZpcnN0Q2hpbGQuc3R5bGUuaGVpZ2h0ID0gXCIwXCI7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGlmIChuZWVkc0gpIHtcclxuICAgICAgICB0aGlzLmhvcml6LnN0eWxlLmRpc3BsYXkgPSBcImJsb2NrXCI7XHJcbiAgICAgICAgdGhpcy5ob3Jpei5zdHlsZS5yaWdodCA9IG5lZWRzViA/IHNXaWR0aCArIFwicHhcIiA6IFwiMFwiO1xyXG4gICAgICAgIHRoaXMuaG9yaXouc3R5bGUubGVmdCA9IG1lYXN1cmUuYmFyTGVmdCArIFwicHhcIjtcclxuICAgICAgICB2YXIgdG90YWxXaWR0aCA9IG1lYXN1cmUudmlld1dpZHRoIC0gbWVhc3VyZS5iYXJMZWZ0IC0gKG5lZWRzViA/IHNXaWR0aCA6IDApO1xyXG4gICAgICAgIHRoaXMuaG9yaXouZmlyc3RDaGlsZC5zdHlsZS53aWR0aCA9XHJcbiAgICAgICAgICAobWVhc3VyZS5zY3JvbGxXaWR0aCAtIG1lYXN1cmUuY2xpZW50V2lkdGggKyB0b3RhbFdpZHRoKSArIFwicHhcIjtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICB0aGlzLmhvcml6LnN0eWxlLmRpc3BsYXkgPSBcIlwiO1xyXG4gICAgICAgIHRoaXMuaG9yaXouZmlyc3RDaGlsZC5zdHlsZS53aWR0aCA9IFwiMFwiO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBpZiAoIXRoaXMuY2hlY2tlZE92ZXJsYXkgJiYgbWVhc3VyZS5jbGllbnRIZWlnaHQgPiAwKSB7XHJcbiAgICAgICAgaWYgKHNXaWR0aCA9PSAwKSB0aGlzLm92ZXJsYXlIYWNrKCk7XHJcbiAgICAgICAgdGhpcy5jaGVja2VkT3ZlcmxheSA9IHRydWU7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIHJldHVybiB7cmlnaHQ6IG5lZWRzViA/IHNXaWR0aCA6IDAsIGJvdHRvbTogbmVlZHNIID8gc1dpZHRoIDogMH07XHJcbiAgICB9LFxyXG4gICAgc2V0U2Nyb2xsTGVmdDogZnVuY3Rpb24ocG9zKSB7XHJcbiAgICAgIGlmICh0aGlzLmhvcml6LnNjcm9sbExlZnQgIT0gcG9zKSB0aGlzLmhvcml6LnNjcm9sbExlZnQgPSBwb3M7XHJcbiAgICB9LFxyXG4gICAgc2V0U2Nyb2xsVG9wOiBmdW5jdGlvbihwb3MpIHtcclxuICAgICAgaWYgKHRoaXMudmVydC5zY3JvbGxUb3AgIT0gcG9zKSB0aGlzLnZlcnQuc2Nyb2xsVG9wID0gcG9zO1xyXG4gICAgfSxcclxuICAgIG92ZXJsYXlIYWNrOiBmdW5jdGlvbigpIHtcclxuICAgICAgdmFyIHcgPSBtYWMgJiYgIW1hY19nZU1vdW50YWluTGlvbiA/IFwiMTJweFwiIDogXCIxOHB4XCI7XHJcbiAgICAgIHRoaXMuaG9yaXouc3R5bGUubWluSGVpZ2h0ID0gdGhpcy52ZXJ0LnN0eWxlLm1pbldpZHRoID0gdztcclxuICAgICAgdmFyIHNlbGYgPSB0aGlzO1xyXG4gICAgICB2YXIgYmFyTW91c2VEb3duID0gZnVuY3Rpb24oZSkge1xyXG4gICAgICAgIGlmIChlX3RhcmdldChlKSAhPSBzZWxmLnZlcnQgJiYgZV90YXJnZXQoZSkgIT0gc2VsZi5ob3JpeilcclxuICAgICAgICAgIG9wZXJhdGlvbihzZWxmLmNtLCBvbk1vdXNlRG93bikoZSk7XHJcbiAgICAgIH07XHJcbiAgICAgIG9uKHRoaXMudmVydCwgXCJtb3VzZWRvd25cIiwgYmFyTW91c2VEb3duKTtcclxuICAgICAgb24odGhpcy5ob3JpeiwgXCJtb3VzZWRvd25cIiwgYmFyTW91c2VEb3duKTtcclxuICAgIH0sXHJcbiAgICBjbGVhcjogZnVuY3Rpb24oKSB7XHJcbiAgICAgIHZhciBwYXJlbnQgPSB0aGlzLmhvcml6LnBhcmVudE5vZGU7XHJcbiAgICAgIHBhcmVudC5yZW1vdmVDaGlsZCh0aGlzLmhvcml6KTtcclxuICAgICAgcGFyZW50LnJlbW92ZUNoaWxkKHRoaXMudmVydCk7XHJcbiAgICB9XHJcbiAgfSwgTmF0aXZlU2Nyb2xsYmFycy5wcm90b3R5cGUpO1xyXG5cclxuICBmdW5jdGlvbiBOdWxsU2Nyb2xsYmFycygpIHt9XHJcblxyXG4gIE51bGxTY3JvbGxiYXJzLnByb3RvdHlwZSA9IGNvcHlPYmooe1xyXG4gICAgdXBkYXRlOiBmdW5jdGlvbigpIHsgcmV0dXJuIHtib3R0b206IDAsIHJpZ2h0OiAwfTsgfSxcclxuICAgIHNldFNjcm9sbExlZnQ6IGZ1bmN0aW9uKCkge30sXHJcbiAgICBzZXRTY3JvbGxUb3A6IGZ1bmN0aW9uKCkge30sXHJcbiAgICBjbGVhcjogZnVuY3Rpb24oKSB7fVxyXG4gIH0sIE51bGxTY3JvbGxiYXJzLnByb3RvdHlwZSk7XHJcblxyXG4gIENvZGVNaXJyb3Iuc2Nyb2xsYmFyTW9kZWwgPSB7XCJuYXRpdmVcIjogTmF0aXZlU2Nyb2xsYmFycywgXCJudWxsXCI6IE51bGxTY3JvbGxiYXJzfTtcclxuXHJcbiAgZnVuY3Rpb24gaW5pdFNjcm9sbGJhcnMoY20pIHtcclxuICAgIGlmIChjbS5kaXNwbGF5LnNjcm9sbGJhcnMpIHtcclxuICAgICAgY20uZGlzcGxheS5zY3JvbGxiYXJzLmNsZWFyKCk7XHJcbiAgICAgIGlmIChjbS5kaXNwbGF5LnNjcm9sbGJhcnMuYWRkQ2xhc3MpXHJcbiAgICAgICAgcm1DbGFzcyhjbS5kaXNwbGF5LndyYXBwZXIsIGNtLmRpc3BsYXkuc2Nyb2xsYmFycy5hZGRDbGFzcyk7XHJcbiAgICB9XHJcblxyXG4gICAgY20uZGlzcGxheS5zY3JvbGxiYXJzID0gbmV3IENvZGVNaXJyb3Iuc2Nyb2xsYmFyTW9kZWxbY20ub3B0aW9ucy5zY3JvbGxiYXJTdHlsZV0oZnVuY3Rpb24obm9kZSkge1xyXG4gICAgICBjbS5kaXNwbGF5LndyYXBwZXIuaW5zZXJ0QmVmb3JlKG5vZGUsIGNtLmRpc3BsYXkuc2Nyb2xsYmFyRmlsbGVyKTtcclxuICAgICAgLy8gUHJldmVudCBjbGlja3MgaW4gdGhlIHNjcm9sbGJhcnMgZnJvbSBraWxsaW5nIGZvY3VzXHJcbiAgICAgIG9uKG5vZGUsIFwibW91c2Vkb3duXCIsIGZ1bmN0aW9uKCkge1xyXG4gICAgICAgIGlmIChjbS5zdGF0ZS5mb2N1c2VkKSBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkgeyBjbS5kaXNwbGF5LmlucHV0LmZvY3VzKCk7IH0sIDApO1xyXG4gICAgICB9KTtcclxuICAgICAgbm9kZS5zZXRBdHRyaWJ1dGUoXCJjbS1ub3QtY29udGVudFwiLCBcInRydWVcIik7XHJcbiAgICB9LCBmdW5jdGlvbihwb3MsIGF4aXMpIHtcclxuICAgICAgaWYgKGF4aXMgPT0gXCJob3Jpem9udGFsXCIpIHNldFNjcm9sbExlZnQoY20sIHBvcyk7XHJcbiAgICAgIGVsc2Ugc2V0U2Nyb2xsVG9wKGNtLCBwb3MpO1xyXG4gICAgfSwgY20pO1xyXG4gICAgaWYgKGNtLmRpc3BsYXkuc2Nyb2xsYmFycy5hZGRDbGFzcylcclxuICAgICAgYWRkQ2xhc3MoY20uZGlzcGxheS53cmFwcGVyLCBjbS5kaXNwbGF5LnNjcm9sbGJhcnMuYWRkQ2xhc3MpO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gdXBkYXRlU2Nyb2xsYmFycyhjbSwgbWVhc3VyZSkge1xyXG4gICAgaWYgKCFtZWFzdXJlKSBtZWFzdXJlID0gbWVhc3VyZUZvclNjcm9sbGJhcnMoY20pO1xyXG4gICAgdmFyIHN0YXJ0V2lkdGggPSBjbS5kaXNwbGF5LmJhcldpZHRoLCBzdGFydEhlaWdodCA9IGNtLmRpc3BsYXkuYmFySGVpZ2h0O1xyXG4gICAgdXBkYXRlU2Nyb2xsYmFyc0lubmVyKGNtLCBtZWFzdXJlKTtcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgNCAmJiBzdGFydFdpZHRoICE9IGNtLmRpc3BsYXkuYmFyV2lkdGggfHwgc3RhcnRIZWlnaHQgIT0gY20uZGlzcGxheS5iYXJIZWlnaHQ7IGkrKykge1xyXG4gICAgICBpZiAoc3RhcnRXaWR0aCAhPSBjbS5kaXNwbGF5LmJhcldpZHRoICYmIGNtLm9wdGlvbnMubGluZVdyYXBwaW5nKVxyXG4gICAgICAgIHVwZGF0ZUhlaWdodHNJblZpZXdwb3J0KGNtKTtcclxuICAgICAgdXBkYXRlU2Nyb2xsYmFyc0lubmVyKGNtLCBtZWFzdXJlRm9yU2Nyb2xsYmFycyhjbSkpO1xyXG4gICAgICBzdGFydFdpZHRoID0gY20uZGlzcGxheS5iYXJXaWR0aDsgc3RhcnRIZWlnaHQgPSBjbS5kaXNwbGF5LmJhckhlaWdodDtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJlLXN5bmNocm9uaXplIHRoZSBmYWtlIHNjcm9sbGJhcnMgd2l0aCB0aGUgYWN0dWFsIHNpemUgb2YgdGhlXHJcbiAgLy8gY29udGVudC5cclxuICBmdW5jdGlvbiB1cGRhdGVTY3JvbGxiYXJzSW5uZXIoY20sIG1lYXN1cmUpIHtcclxuICAgIHZhciBkID0gY20uZGlzcGxheTtcclxuICAgIHZhciBzaXplcyA9IGQuc2Nyb2xsYmFycy51cGRhdGUobWVhc3VyZSk7XHJcblxyXG4gICAgZC5zaXplci5zdHlsZS5wYWRkaW5nUmlnaHQgPSAoZC5iYXJXaWR0aCA9IHNpemVzLnJpZ2h0KSArIFwicHhcIjtcclxuICAgIGQuc2l6ZXIuc3R5bGUucGFkZGluZ0JvdHRvbSA9IChkLmJhckhlaWdodCA9IHNpemVzLmJvdHRvbSkgKyBcInB4XCI7XHJcblxyXG4gICAgaWYgKHNpemVzLnJpZ2h0ICYmIHNpemVzLmJvdHRvbSkge1xyXG4gICAgICBkLnNjcm9sbGJhckZpbGxlci5zdHlsZS5kaXNwbGF5ID0gXCJibG9ja1wiO1xyXG4gICAgICBkLnNjcm9sbGJhckZpbGxlci5zdHlsZS5oZWlnaHQgPSBzaXplcy5ib3R0b20gKyBcInB4XCI7XHJcbiAgICAgIGQuc2Nyb2xsYmFyRmlsbGVyLnN0eWxlLndpZHRoID0gc2l6ZXMucmlnaHQgKyBcInB4XCI7XHJcbiAgICB9IGVsc2UgZC5zY3JvbGxiYXJGaWxsZXIuc3R5bGUuZGlzcGxheSA9IFwiXCI7XHJcbiAgICBpZiAoc2l6ZXMuYm90dG9tICYmIGNtLm9wdGlvbnMuY292ZXJHdXR0ZXJOZXh0VG9TY3JvbGxiYXIgJiYgY20ub3B0aW9ucy5maXhlZEd1dHRlcikge1xyXG4gICAgICBkLmd1dHRlckZpbGxlci5zdHlsZS5kaXNwbGF5ID0gXCJibG9ja1wiO1xyXG4gICAgICBkLmd1dHRlckZpbGxlci5zdHlsZS5oZWlnaHQgPSBzaXplcy5ib3R0b20gKyBcInB4XCI7XHJcbiAgICAgIGQuZ3V0dGVyRmlsbGVyLnN0eWxlLndpZHRoID0gbWVhc3VyZS5ndXR0ZXJXaWR0aCArIFwicHhcIjtcclxuICAgIH0gZWxzZSBkLmd1dHRlckZpbGxlci5zdHlsZS5kaXNwbGF5ID0gXCJcIjtcclxuICB9XHJcblxyXG4gIC8vIENvbXB1dGUgdGhlIGxpbmVzIHRoYXQgYXJlIHZpc2libGUgaW4gYSBnaXZlbiB2aWV3cG9ydCAoZGVmYXVsdHNcclxuICAvLyB0aGUgdGhlIGN1cnJlbnQgc2Nyb2xsIHBvc2l0aW9uKS4gdmlld3BvcnQgbWF5IGNvbnRhaW4gdG9wLFxyXG4gIC8vIGhlaWdodCwgYW5kIGVuc3VyZSAoc2VlIG9wLnNjcm9sbFRvUG9zKSBwcm9wZXJ0aWVzLlxyXG4gIGZ1bmN0aW9uIHZpc2libGVMaW5lcyhkaXNwbGF5LCBkb2MsIHZpZXdwb3J0KSB7XHJcbiAgICB2YXIgdG9wID0gdmlld3BvcnQgJiYgdmlld3BvcnQudG9wICE9IG51bGwgPyBNYXRoLm1heCgwLCB2aWV3cG9ydC50b3ApIDogZGlzcGxheS5zY3JvbGxlci5zY3JvbGxUb3A7XHJcbiAgICB0b3AgPSBNYXRoLmZsb29yKHRvcCAtIHBhZGRpbmdUb3AoZGlzcGxheSkpO1xyXG4gICAgdmFyIGJvdHRvbSA9IHZpZXdwb3J0ICYmIHZpZXdwb3J0LmJvdHRvbSAhPSBudWxsID8gdmlld3BvcnQuYm90dG9tIDogdG9wICsgZGlzcGxheS53cmFwcGVyLmNsaWVudEhlaWdodDtcclxuXHJcbiAgICB2YXIgZnJvbSA9IGxpbmVBdEhlaWdodChkb2MsIHRvcCksIHRvID0gbGluZUF0SGVpZ2h0KGRvYywgYm90dG9tKTtcclxuICAgIC8vIEVuc3VyZSBpcyBhIHtmcm9tOiB7bGluZSwgY2h9LCB0bzoge2xpbmUsIGNofX0gb2JqZWN0LCBhbmRcclxuICAgIC8vIGZvcmNlcyB0aG9zZSBsaW5lcyBpbnRvIHRoZSB2aWV3cG9ydCAoaWYgcG9zc2libGUpLlxyXG4gICAgaWYgKHZpZXdwb3J0ICYmIHZpZXdwb3J0LmVuc3VyZSkge1xyXG4gICAgICB2YXIgZW5zdXJlRnJvbSA9IHZpZXdwb3J0LmVuc3VyZS5mcm9tLmxpbmUsIGVuc3VyZVRvID0gdmlld3BvcnQuZW5zdXJlLnRvLmxpbmU7XHJcbiAgICAgIGlmIChlbnN1cmVGcm9tIDwgZnJvbSkge1xyXG4gICAgICAgIGZyb20gPSBlbnN1cmVGcm9tO1xyXG4gICAgICAgIHRvID0gbGluZUF0SGVpZ2h0KGRvYywgaGVpZ2h0QXRMaW5lKGdldExpbmUoZG9jLCBlbnN1cmVGcm9tKSkgKyBkaXNwbGF5LndyYXBwZXIuY2xpZW50SGVpZ2h0KTtcclxuICAgICAgfSBlbHNlIGlmIChNYXRoLm1pbihlbnN1cmVUbywgZG9jLmxhc3RMaW5lKCkpID49IHRvKSB7XHJcbiAgICAgICAgZnJvbSA9IGxpbmVBdEhlaWdodChkb2MsIGhlaWdodEF0TGluZShnZXRMaW5lKGRvYywgZW5zdXJlVG8pKSAtIGRpc3BsYXkud3JhcHBlci5jbGllbnRIZWlnaHQpO1xyXG4gICAgICAgIHRvID0gZW5zdXJlVG87XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIHJldHVybiB7ZnJvbTogZnJvbSwgdG86IE1hdGgubWF4KHRvLCBmcm9tICsgMSl9O1xyXG4gIH1cclxuXHJcbiAgLy8gTElORSBOVU1CRVJTXHJcblxyXG4gIC8vIFJlLWFsaWduIGxpbmUgbnVtYmVycyBhbmQgZ3V0dGVyIG1hcmtzIHRvIGNvbXBlbnNhdGUgZm9yXHJcbiAgLy8gaG9yaXpvbnRhbCBzY3JvbGxpbmcuXHJcbiAgZnVuY3Rpb24gYWxpZ25Ib3Jpem9udGFsbHkoY20pIHtcclxuICAgIHZhciBkaXNwbGF5ID0gY20uZGlzcGxheSwgdmlldyA9IGRpc3BsYXkudmlldztcclxuICAgIGlmICghZGlzcGxheS5hbGlnbldpZGdldHMgJiYgKCFkaXNwbGF5Lmd1dHRlcnMuZmlyc3RDaGlsZCB8fCAhY20ub3B0aW9ucy5maXhlZEd1dHRlcikpIHJldHVybjtcclxuICAgIHZhciBjb21wID0gY29tcGVuc2F0ZUZvckhTY3JvbGwoZGlzcGxheSkgLSBkaXNwbGF5LnNjcm9sbGVyLnNjcm9sbExlZnQgKyBjbS5kb2Muc2Nyb2xsTGVmdDtcclxuICAgIHZhciBndXR0ZXJXID0gZGlzcGxheS5ndXR0ZXJzLm9mZnNldFdpZHRoLCBsZWZ0ID0gY29tcCArIFwicHhcIjtcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdmlldy5sZW5ndGg7IGkrKykgaWYgKCF2aWV3W2ldLmhpZGRlbikge1xyXG4gICAgICBpZiAoY20ub3B0aW9ucy5maXhlZEd1dHRlciAmJiB2aWV3W2ldLmd1dHRlcilcclxuICAgICAgICB2aWV3W2ldLmd1dHRlci5zdHlsZS5sZWZ0ID0gbGVmdDtcclxuICAgICAgdmFyIGFsaWduID0gdmlld1tpXS5hbGlnbmFibGU7XHJcbiAgICAgIGlmIChhbGlnbikgZm9yICh2YXIgaiA9IDA7IGogPCBhbGlnbi5sZW5ndGg7IGorKylcclxuICAgICAgICBhbGlnbltqXS5zdHlsZS5sZWZ0ID0gbGVmdDtcclxuICAgIH1cclxuICAgIGlmIChjbS5vcHRpb25zLmZpeGVkR3V0dGVyKVxyXG4gICAgICBkaXNwbGF5Lmd1dHRlcnMuc3R5bGUubGVmdCA9IChjb21wICsgZ3V0dGVyVykgKyBcInB4XCI7XHJcbiAgfVxyXG5cclxuICAvLyBVc2VkIHRvIGVuc3VyZSB0aGF0IHRoZSBsaW5lIG51bWJlciBndXR0ZXIgaXMgc3RpbGwgdGhlIHJpZ2h0XHJcbiAgLy8gc2l6ZSBmb3IgdGhlIGN1cnJlbnQgZG9jdW1lbnQgc2l6ZS4gUmV0dXJucyB0cnVlIHdoZW4gYW4gdXBkYXRlXHJcbiAgLy8gaXMgbmVlZGVkLlxyXG4gIGZ1bmN0aW9uIG1heWJlVXBkYXRlTGluZU51bWJlcldpZHRoKGNtKSB7XHJcbiAgICBpZiAoIWNtLm9wdGlvbnMubGluZU51bWJlcnMpIHJldHVybiBmYWxzZTtcclxuICAgIHZhciBkb2MgPSBjbS5kb2MsIGxhc3QgPSBsaW5lTnVtYmVyRm9yKGNtLm9wdGlvbnMsIGRvYy5maXJzdCArIGRvYy5zaXplIC0gMSksIGRpc3BsYXkgPSBjbS5kaXNwbGF5O1xyXG4gICAgaWYgKGxhc3QubGVuZ3RoICE9IGRpc3BsYXkubGluZU51bUNoYXJzKSB7XHJcbiAgICAgIHZhciB0ZXN0ID0gZGlzcGxheS5tZWFzdXJlLmFwcGVuZENoaWxkKGVsdChcImRpdlwiLCBbZWx0KFwiZGl2XCIsIGxhc3QpXSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwiQ29kZU1pcnJvci1saW5lbnVtYmVyIENvZGVNaXJyb3ItZ3V0dGVyLWVsdFwiKSk7XHJcbiAgICAgIHZhciBpbm5lclcgPSB0ZXN0LmZpcnN0Q2hpbGQub2Zmc2V0V2lkdGgsIHBhZGRpbmcgPSB0ZXN0Lm9mZnNldFdpZHRoIC0gaW5uZXJXO1xyXG4gICAgICBkaXNwbGF5LmxpbmVHdXR0ZXIuc3R5bGUud2lkdGggPSBcIlwiO1xyXG4gICAgICBkaXNwbGF5LmxpbmVOdW1Jbm5lcldpZHRoID0gTWF0aC5tYXgoaW5uZXJXLCBkaXNwbGF5LmxpbmVHdXR0ZXIub2Zmc2V0V2lkdGggLSBwYWRkaW5nKSArIDE7XHJcbiAgICAgIGRpc3BsYXkubGluZU51bVdpZHRoID0gZGlzcGxheS5saW5lTnVtSW5uZXJXaWR0aCArIHBhZGRpbmc7XHJcbiAgICAgIGRpc3BsYXkubGluZU51bUNoYXJzID0gZGlzcGxheS5saW5lTnVtSW5uZXJXaWR0aCA/IGxhc3QubGVuZ3RoIDogLTE7XHJcbiAgICAgIGRpc3BsYXkubGluZUd1dHRlci5zdHlsZS53aWR0aCA9IGRpc3BsYXkubGluZU51bVdpZHRoICsgXCJweFwiO1xyXG4gICAgICB1cGRhdGVHdXR0ZXJTcGFjZShjbSk7XHJcbiAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIGZhbHNlO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gbGluZU51bWJlckZvcihvcHRpb25zLCBpKSB7XHJcbiAgICByZXR1cm4gU3RyaW5nKG9wdGlvbnMubGluZU51bWJlckZvcm1hdHRlcihpICsgb3B0aW9ucy5maXJzdExpbmVOdW1iZXIpKTtcclxuICB9XHJcblxyXG4gIC8vIENvbXB1dGVzIGRpc3BsYXkuc2Nyb2xsZXIuc2Nyb2xsTGVmdCArIGRpc3BsYXkuZ3V0dGVycy5vZmZzZXRXaWR0aCxcclxuICAvLyBidXQgdXNpbmcgZ2V0Qm91bmRpbmdDbGllbnRSZWN0IHRvIGdldCBhIHN1Yi1waXhlbC1hY2N1cmF0ZVxyXG4gIC8vIHJlc3VsdC5cclxuICBmdW5jdGlvbiBjb21wZW5zYXRlRm9ySFNjcm9sbChkaXNwbGF5KSB7XHJcbiAgICByZXR1cm4gZGlzcGxheS5zY3JvbGxlci5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS5sZWZ0IC0gZGlzcGxheS5zaXplci5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS5sZWZ0O1xyXG4gIH1cclxuXHJcbiAgLy8gRElTUExBWSBEUkFXSU5HXHJcblxyXG4gIGZ1bmN0aW9uIERpc3BsYXlVcGRhdGUoY20sIHZpZXdwb3J0LCBmb3JjZSkge1xyXG4gICAgdmFyIGRpc3BsYXkgPSBjbS5kaXNwbGF5O1xyXG5cclxuICAgIHRoaXMudmlld3BvcnQgPSB2aWV3cG9ydDtcclxuICAgIC8vIFN0b3JlIHNvbWUgdmFsdWVzIHRoYXQgd2UnbGwgbmVlZCBsYXRlciAoYnV0IGRvbid0IHdhbnQgdG8gZm9yY2UgYSByZWxheW91dCBmb3IpXHJcbiAgICB0aGlzLnZpc2libGUgPSB2aXNpYmxlTGluZXMoZGlzcGxheSwgY20uZG9jLCB2aWV3cG9ydCk7XHJcbiAgICB0aGlzLmVkaXRvcklzSGlkZGVuID0gIWRpc3BsYXkud3JhcHBlci5vZmZzZXRXaWR0aDtcclxuICAgIHRoaXMud3JhcHBlckhlaWdodCA9IGRpc3BsYXkud3JhcHBlci5jbGllbnRIZWlnaHQ7XHJcbiAgICB0aGlzLndyYXBwZXJXaWR0aCA9IGRpc3BsYXkud3JhcHBlci5jbGllbnRXaWR0aDtcclxuICAgIHRoaXMub2xkRGlzcGxheVdpZHRoID0gZGlzcGxheVdpZHRoKGNtKTtcclxuICAgIHRoaXMuZm9yY2UgPSBmb3JjZTtcclxuICAgIHRoaXMuZGltcyA9IGdldERpbWVuc2lvbnMoY20pO1xyXG4gICAgdGhpcy5ldmVudHMgPSBbXTtcclxuICB9XHJcblxyXG4gIERpc3BsYXlVcGRhdGUucHJvdG90eXBlLnNpZ25hbCA9IGZ1bmN0aW9uKGVtaXR0ZXIsIHR5cGUpIHtcclxuICAgIGlmIChoYXNIYW5kbGVyKGVtaXR0ZXIsIHR5cGUpKVxyXG4gICAgICB0aGlzLmV2ZW50cy5wdXNoKGFyZ3VtZW50cyk7XHJcbiAgfTtcclxuICBEaXNwbGF5VXBkYXRlLnByb3RvdHlwZS5maW5pc2ggPSBmdW5jdGlvbigpIHtcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy5ldmVudHMubGVuZ3RoOyBpKyspXHJcbiAgICAgIHNpZ25hbC5hcHBseShudWxsLCB0aGlzLmV2ZW50c1tpXSk7XHJcbiAgfTtcclxuXHJcbiAgZnVuY3Rpb24gbWF5YmVDbGlwU2Nyb2xsYmFycyhjbSkge1xyXG4gICAgdmFyIGRpc3BsYXkgPSBjbS5kaXNwbGF5O1xyXG4gICAgaWYgKCFkaXNwbGF5LnNjcm9sbGJhcnNDbGlwcGVkICYmIGRpc3BsYXkuc2Nyb2xsZXIub2Zmc2V0V2lkdGgpIHtcclxuICAgICAgZGlzcGxheS5uYXRpdmVCYXJXaWR0aCA9IGRpc3BsYXkuc2Nyb2xsZXIub2Zmc2V0V2lkdGggLSBkaXNwbGF5LnNjcm9sbGVyLmNsaWVudFdpZHRoO1xyXG4gICAgICBkaXNwbGF5LmhlaWdodEZvcmNlci5zdHlsZS5oZWlnaHQgPSBzY3JvbGxHYXAoY20pICsgXCJweFwiO1xyXG4gICAgICBkaXNwbGF5LnNpemVyLnN0eWxlLm1hcmdpbkJvdHRvbSA9IC1kaXNwbGF5Lm5hdGl2ZUJhcldpZHRoICsgXCJweFwiO1xyXG4gICAgICBkaXNwbGF5LnNpemVyLnN0eWxlLmJvcmRlclJpZ2h0V2lkdGggPSBzY3JvbGxHYXAoY20pICsgXCJweFwiO1xyXG4gICAgICBkaXNwbGF5LnNjcm9sbGJhcnNDbGlwcGVkID0gdHJ1ZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIERvZXMgdGhlIGFjdHVhbCB1cGRhdGluZyBvZiB0aGUgbGluZSBkaXNwbGF5LiBCYWlscyBvdXRcclxuICAvLyAocmV0dXJuaW5nIGZhbHNlKSB3aGVuIHRoZXJlIGlzIG5vdGhpbmcgdG8gYmUgZG9uZSBhbmQgZm9yY2VkIGlzXHJcbiAgLy8gZmFsc2UuXHJcbiAgZnVuY3Rpb24gdXBkYXRlRGlzcGxheUlmTmVlZGVkKGNtLCB1cGRhdGUpIHtcclxuICAgIHZhciBkaXNwbGF5ID0gY20uZGlzcGxheSwgZG9jID0gY20uZG9jO1xyXG5cclxuICAgIGlmICh1cGRhdGUuZWRpdG9ySXNIaWRkZW4pIHtcclxuICAgICAgcmVzZXRWaWV3KGNtKTtcclxuICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEJhaWwgb3V0IGlmIHRoZSB2aXNpYmxlIGFyZWEgaXMgYWxyZWFkeSByZW5kZXJlZCBhbmQgbm90aGluZyBjaGFuZ2VkLlxyXG4gICAgaWYgKCF1cGRhdGUuZm9yY2UgJiZcclxuICAgICAgICB1cGRhdGUudmlzaWJsZS5mcm9tID49IGRpc3BsYXkudmlld0Zyb20gJiYgdXBkYXRlLnZpc2libGUudG8gPD0gZGlzcGxheS52aWV3VG8gJiZcclxuICAgICAgICAoZGlzcGxheS51cGRhdGVMaW5lTnVtYmVycyA9PSBudWxsIHx8IGRpc3BsYXkudXBkYXRlTGluZU51bWJlcnMgPj0gZGlzcGxheS52aWV3VG8pICYmXHJcbiAgICAgICAgZGlzcGxheS5yZW5kZXJlZFZpZXcgPT0gZGlzcGxheS52aWV3ICYmIGNvdW50RGlydHlWaWV3KGNtKSA9PSAwKVxyXG4gICAgICByZXR1cm4gZmFsc2U7XHJcblxyXG4gICAgaWYgKG1heWJlVXBkYXRlTGluZU51bWJlcldpZHRoKGNtKSkge1xyXG4gICAgICByZXNldFZpZXcoY20pO1xyXG4gICAgICB1cGRhdGUuZGltcyA9IGdldERpbWVuc2lvbnMoY20pO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENvbXB1dGUgYSBzdWl0YWJsZSBuZXcgdmlld3BvcnQgKGZyb20gJiB0bylcclxuICAgIHZhciBlbmQgPSBkb2MuZmlyc3QgKyBkb2Muc2l6ZTtcclxuICAgIHZhciBmcm9tID0gTWF0aC5tYXgodXBkYXRlLnZpc2libGUuZnJvbSAtIGNtLm9wdGlvbnMudmlld3BvcnRNYXJnaW4sIGRvYy5maXJzdCk7XHJcbiAgICB2YXIgdG8gPSBNYXRoLm1pbihlbmQsIHVwZGF0ZS52aXNpYmxlLnRvICsgY20ub3B0aW9ucy52aWV3cG9ydE1hcmdpbik7XHJcbiAgICBpZiAoZGlzcGxheS52aWV3RnJvbSA8IGZyb20gJiYgZnJvbSAtIGRpc3BsYXkudmlld0Zyb20gPCAyMCkgZnJvbSA9IE1hdGgubWF4KGRvYy5maXJzdCwgZGlzcGxheS52aWV3RnJvbSk7XHJcbiAgICBpZiAoZGlzcGxheS52aWV3VG8gPiB0byAmJiBkaXNwbGF5LnZpZXdUbyAtIHRvIDwgMjApIHRvID0gTWF0aC5taW4oZW5kLCBkaXNwbGF5LnZpZXdUbyk7XHJcbiAgICBpZiAoc2F3Q29sbGFwc2VkU3BhbnMpIHtcclxuICAgICAgZnJvbSA9IHZpc3VhbExpbmVObyhjbS5kb2MsIGZyb20pO1xyXG4gICAgICB0byA9IHZpc3VhbExpbmVFbmRObyhjbS5kb2MsIHRvKTtcclxuICAgIH1cclxuXHJcbiAgICB2YXIgZGlmZmVyZW50ID0gZnJvbSAhPSBkaXNwbGF5LnZpZXdGcm9tIHx8IHRvICE9IGRpc3BsYXkudmlld1RvIHx8XHJcbiAgICAgIGRpc3BsYXkubGFzdFdyYXBIZWlnaHQgIT0gdXBkYXRlLndyYXBwZXJIZWlnaHQgfHwgZGlzcGxheS5sYXN0V3JhcFdpZHRoICE9IHVwZGF0ZS53cmFwcGVyV2lkdGg7XHJcbiAgICBhZGp1c3RWaWV3KGNtLCBmcm9tLCB0byk7XHJcblxyXG4gICAgZGlzcGxheS52aWV3T2Zmc2V0ID0gaGVpZ2h0QXRMaW5lKGdldExpbmUoY20uZG9jLCBkaXNwbGF5LnZpZXdGcm9tKSk7XHJcbiAgICAvLyBQb3NpdGlvbiB0aGUgbW92ZXIgZGl2IHRvIGFsaWduIHdpdGggdGhlIGN1cnJlbnQgc2Nyb2xsIHBvc2l0aW9uXHJcbiAgICBjbS5kaXNwbGF5Lm1vdmVyLnN0eWxlLnRvcCA9IGRpc3BsYXkudmlld09mZnNldCArIFwicHhcIjtcclxuXHJcbiAgICB2YXIgdG9VcGRhdGUgPSBjb3VudERpcnR5VmlldyhjbSk7XHJcbiAgICBpZiAoIWRpZmZlcmVudCAmJiB0b1VwZGF0ZSA9PSAwICYmICF1cGRhdGUuZm9yY2UgJiYgZGlzcGxheS5yZW5kZXJlZFZpZXcgPT0gZGlzcGxheS52aWV3ICYmXHJcbiAgICAgICAgKGRpc3BsYXkudXBkYXRlTGluZU51bWJlcnMgPT0gbnVsbCB8fCBkaXNwbGF5LnVwZGF0ZUxpbmVOdW1iZXJzID49IGRpc3BsYXkudmlld1RvKSlcclxuICAgICAgcmV0dXJuIGZhbHNlO1xyXG5cclxuICAgIC8vIEZvciBiaWcgY2hhbmdlcywgd2UgaGlkZSB0aGUgZW5jbG9zaW5nIGVsZW1lbnQgZHVyaW5nIHRoZVxyXG4gICAgLy8gdXBkYXRlLCBzaW5jZSB0aGF0IHNwZWVkcyB1cCB0aGUgb3BlcmF0aW9ucyBvbiBtb3N0IGJyb3dzZXJzLlxyXG4gICAgdmFyIGZvY3VzZWQgPSBhY3RpdmVFbHQoKTtcclxuICAgIGlmICh0b1VwZGF0ZSA+IDQpIGRpc3BsYXkubGluZURpdi5zdHlsZS5kaXNwbGF5ID0gXCJub25lXCI7XHJcbiAgICBwYXRjaERpc3BsYXkoY20sIGRpc3BsYXkudXBkYXRlTGluZU51bWJlcnMsIHVwZGF0ZS5kaW1zKTtcclxuICAgIGlmICh0b1VwZGF0ZSA+IDQpIGRpc3BsYXkubGluZURpdi5zdHlsZS5kaXNwbGF5ID0gXCJcIjtcclxuICAgIGRpc3BsYXkucmVuZGVyZWRWaWV3ID0gZGlzcGxheS52aWV3O1xyXG4gICAgLy8gVGhlcmUgbWlnaHQgaGF2ZSBiZWVuIGEgd2lkZ2V0IHdpdGggYSBmb2N1c2VkIGVsZW1lbnQgdGhhdCBnb3RcclxuICAgIC8vIGhpZGRlbiBvciB1cGRhdGVkLCBpZiBzbyByZS1mb2N1cyBpdC5cclxuICAgIGlmIChmb2N1c2VkICYmIGFjdGl2ZUVsdCgpICE9IGZvY3VzZWQgJiYgZm9jdXNlZC5vZmZzZXRIZWlnaHQpIGZvY3VzZWQuZm9jdXMoKTtcclxuXHJcbiAgICAvLyBQcmV2ZW50IHNlbGVjdGlvbiBhbmQgY3Vyc29ycyBmcm9tIGludGVyZmVyaW5nIHdpdGggdGhlIHNjcm9sbFxyXG4gICAgLy8gd2lkdGggYW5kIGhlaWdodC5cclxuICAgIHJlbW92ZUNoaWxkcmVuKGRpc3BsYXkuY3Vyc29yRGl2KTtcclxuICAgIHJlbW92ZUNoaWxkcmVuKGRpc3BsYXkuc2VsZWN0aW9uRGl2KTtcclxuICAgIGRpc3BsYXkuZ3V0dGVycy5zdHlsZS5oZWlnaHQgPSBkaXNwbGF5LnNpemVyLnN0eWxlLm1pbkhlaWdodCA9IDA7XHJcblxyXG4gICAgaWYgKGRpZmZlcmVudCkge1xyXG4gICAgICBkaXNwbGF5Lmxhc3RXcmFwSGVpZ2h0ID0gdXBkYXRlLndyYXBwZXJIZWlnaHQ7XHJcbiAgICAgIGRpc3BsYXkubGFzdFdyYXBXaWR0aCA9IHVwZGF0ZS53cmFwcGVyV2lkdGg7XHJcbiAgICAgIHN0YXJ0V29ya2VyKGNtLCA0MDApO1xyXG4gICAgfVxyXG5cclxuICAgIGRpc3BsYXkudXBkYXRlTGluZU51bWJlcnMgPSBudWxsO1xyXG5cclxuICAgIHJldHVybiB0cnVlO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gcG9zdFVwZGF0ZURpc3BsYXkoY20sIHVwZGF0ZSkge1xyXG4gICAgdmFyIHZpZXdwb3J0ID0gdXBkYXRlLnZpZXdwb3J0O1xyXG4gICAgZm9yICh2YXIgZmlyc3QgPSB0cnVlOzsgZmlyc3QgPSBmYWxzZSkge1xyXG4gICAgICBpZiAoIWZpcnN0IHx8ICFjbS5vcHRpb25zLmxpbmVXcmFwcGluZyB8fCB1cGRhdGUub2xkRGlzcGxheVdpZHRoID09IGRpc3BsYXlXaWR0aChjbSkpIHtcclxuICAgICAgICAvLyBDbGlwIGZvcmNlZCB2aWV3cG9ydCB0byBhY3R1YWwgc2Nyb2xsYWJsZSBhcmVhLlxyXG4gICAgICAgIGlmICh2aWV3cG9ydCAmJiB2aWV3cG9ydC50b3AgIT0gbnVsbClcclxuICAgICAgICAgIHZpZXdwb3J0ID0ge3RvcDogTWF0aC5taW4oY20uZG9jLmhlaWdodCArIHBhZGRpbmdWZXJ0KGNtLmRpc3BsYXkpIC0gZGlzcGxheUhlaWdodChjbSksIHZpZXdwb3J0LnRvcCl9O1xyXG4gICAgICAgIC8vIFVwZGF0ZWQgbGluZSBoZWlnaHRzIG1pZ2h0IHJlc3VsdCBpbiB0aGUgZHJhd24gYXJlYSBub3RcclxuICAgICAgICAvLyBhY3R1YWxseSBjb3ZlcmluZyB0aGUgdmlld3BvcnQuIEtlZXAgbG9vcGluZyB1bnRpbCBpdCBkb2VzLlxyXG4gICAgICAgIHVwZGF0ZS52aXNpYmxlID0gdmlzaWJsZUxpbmVzKGNtLmRpc3BsYXksIGNtLmRvYywgdmlld3BvcnQpO1xyXG4gICAgICAgIGlmICh1cGRhdGUudmlzaWJsZS5mcm9tID49IGNtLmRpc3BsYXkudmlld0Zyb20gJiYgdXBkYXRlLnZpc2libGUudG8gPD0gY20uZGlzcGxheS52aWV3VG8pXHJcbiAgICAgICAgICBicmVhaztcclxuICAgICAgfVxyXG4gICAgICBpZiAoIXVwZGF0ZURpc3BsYXlJZk5lZWRlZChjbSwgdXBkYXRlKSkgYnJlYWs7XHJcbiAgICAgIHVwZGF0ZUhlaWdodHNJblZpZXdwb3J0KGNtKTtcclxuICAgICAgdmFyIGJhck1lYXN1cmUgPSBtZWFzdXJlRm9yU2Nyb2xsYmFycyhjbSk7XHJcbiAgICAgIHVwZGF0ZVNlbGVjdGlvbihjbSk7XHJcbiAgICAgIHNldERvY3VtZW50SGVpZ2h0KGNtLCBiYXJNZWFzdXJlKTtcclxuICAgICAgdXBkYXRlU2Nyb2xsYmFycyhjbSwgYmFyTWVhc3VyZSk7XHJcbiAgICB9XHJcblxyXG4gICAgdXBkYXRlLnNpZ25hbChjbSwgXCJ1cGRhdGVcIiwgY20pO1xyXG4gICAgaWYgKGNtLmRpc3BsYXkudmlld0Zyb20gIT0gY20uZGlzcGxheS5yZXBvcnRlZFZpZXdGcm9tIHx8IGNtLmRpc3BsYXkudmlld1RvICE9IGNtLmRpc3BsYXkucmVwb3J0ZWRWaWV3VG8pIHtcclxuICAgICAgdXBkYXRlLnNpZ25hbChjbSwgXCJ2aWV3cG9ydENoYW5nZVwiLCBjbSwgY20uZGlzcGxheS52aWV3RnJvbSwgY20uZGlzcGxheS52aWV3VG8pO1xyXG4gICAgICBjbS5kaXNwbGF5LnJlcG9ydGVkVmlld0Zyb20gPSBjbS5kaXNwbGF5LnZpZXdGcm9tOyBjbS5kaXNwbGF5LnJlcG9ydGVkVmlld1RvID0gY20uZGlzcGxheS52aWV3VG87XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiB1cGRhdGVEaXNwbGF5U2ltcGxlKGNtLCB2aWV3cG9ydCkge1xyXG4gICAgdmFyIHVwZGF0ZSA9IG5ldyBEaXNwbGF5VXBkYXRlKGNtLCB2aWV3cG9ydCk7XHJcbiAgICBpZiAodXBkYXRlRGlzcGxheUlmTmVlZGVkKGNtLCB1cGRhdGUpKSB7XHJcbiAgICAgIHVwZGF0ZUhlaWdodHNJblZpZXdwb3J0KGNtKTtcclxuICAgICAgcG9zdFVwZGF0ZURpc3BsYXkoY20sIHVwZGF0ZSk7XHJcbiAgICAgIHZhciBiYXJNZWFzdXJlID0gbWVhc3VyZUZvclNjcm9sbGJhcnMoY20pO1xyXG4gICAgICB1cGRhdGVTZWxlY3Rpb24oY20pO1xyXG4gICAgICBzZXREb2N1bWVudEhlaWdodChjbSwgYmFyTWVhc3VyZSk7XHJcbiAgICAgIHVwZGF0ZVNjcm9sbGJhcnMoY20sIGJhck1lYXN1cmUpO1xyXG4gICAgICB1cGRhdGUuZmluaXNoKCk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBzZXREb2N1bWVudEhlaWdodChjbSwgbWVhc3VyZSkge1xyXG4gICAgY20uZGlzcGxheS5zaXplci5zdHlsZS5taW5IZWlnaHQgPSBtZWFzdXJlLmRvY0hlaWdodCArIFwicHhcIjtcclxuICAgIHZhciB0b3RhbCA9IG1lYXN1cmUuZG9jSGVpZ2h0ICsgY20uZGlzcGxheS5iYXJIZWlnaHQ7XHJcbiAgICBjbS5kaXNwbGF5LmhlaWdodEZvcmNlci5zdHlsZS50b3AgPSB0b3RhbCArIFwicHhcIjtcclxuICAgIGNtLmRpc3BsYXkuZ3V0dGVycy5zdHlsZS5oZWlnaHQgPSBNYXRoLm1heCh0b3RhbCArIHNjcm9sbEdhcChjbSksIG1lYXN1cmUuY2xpZW50SGVpZ2h0KSArIFwicHhcIjtcclxuICB9XHJcblxyXG4gIC8vIFJlYWQgdGhlIGFjdHVhbCBoZWlnaHRzIG9mIHRoZSByZW5kZXJlZCBsaW5lcywgYW5kIHVwZGF0ZSB0aGVpclxyXG4gIC8vIHN0b3JlZCBoZWlnaHRzIHRvIG1hdGNoLlxyXG4gIGZ1bmN0aW9uIHVwZGF0ZUhlaWdodHNJblZpZXdwb3J0KGNtKSB7XHJcbiAgICB2YXIgZGlzcGxheSA9IGNtLmRpc3BsYXk7XHJcbiAgICB2YXIgcHJldkJvdHRvbSA9IGRpc3BsYXkubGluZURpdi5vZmZzZXRUb3A7XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGRpc3BsYXkudmlldy5sZW5ndGg7IGkrKykge1xyXG4gICAgICB2YXIgY3VyID0gZGlzcGxheS52aWV3W2ldLCBoZWlnaHQ7XHJcbiAgICAgIGlmIChjdXIuaGlkZGVuKSBjb250aW51ZTtcclxuICAgICAgaWYgKGllICYmIGllX3ZlcnNpb24gPCA4KSB7XHJcbiAgICAgICAgdmFyIGJvdCA9IGN1ci5ub2RlLm9mZnNldFRvcCArIGN1ci5ub2RlLm9mZnNldEhlaWdodDtcclxuICAgICAgICBoZWlnaHQgPSBib3QgLSBwcmV2Qm90dG9tO1xyXG4gICAgICAgIHByZXZCb3R0b20gPSBib3Q7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgdmFyIGJveCA9IGN1ci5ub2RlLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xyXG4gICAgICAgIGhlaWdodCA9IGJveC5ib3R0b20gLSBib3gudG9wO1xyXG4gICAgICB9XHJcbiAgICAgIHZhciBkaWZmID0gY3VyLmxpbmUuaGVpZ2h0IC0gaGVpZ2h0O1xyXG4gICAgICBpZiAoaGVpZ2h0IDwgMikgaGVpZ2h0ID0gdGV4dEhlaWdodChkaXNwbGF5KTtcclxuICAgICAgaWYgKGRpZmYgPiAuMDAxIHx8IGRpZmYgPCAtLjAwMSkge1xyXG4gICAgICAgIHVwZGF0ZUxpbmVIZWlnaHQoY3VyLmxpbmUsIGhlaWdodCk7XHJcbiAgICAgICAgdXBkYXRlV2lkZ2V0SGVpZ2h0KGN1ci5saW5lKTtcclxuICAgICAgICBpZiAoY3VyLnJlc3QpIGZvciAodmFyIGogPSAwOyBqIDwgY3VyLnJlc3QubGVuZ3RoOyBqKyspXHJcbiAgICAgICAgICB1cGRhdGVXaWRnZXRIZWlnaHQoY3VyLnJlc3Rbal0pO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZWFkIGFuZCBzdG9yZSB0aGUgaGVpZ2h0IG9mIGxpbmUgd2lkZ2V0cyBhc3NvY2lhdGVkIHdpdGggdGhlXHJcbiAgLy8gZ2l2ZW4gbGluZS5cclxuICBmdW5jdGlvbiB1cGRhdGVXaWRnZXRIZWlnaHQobGluZSkge1xyXG4gICAgaWYgKGxpbmUud2lkZ2V0cykgZm9yICh2YXIgaSA9IDA7IGkgPCBsaW5lLndpZGdldHMubGVuZ3RoOyArK2kpXHJcbiAgICAgIGxpbmUud2lkZ2V0c1tpXS5oZWlnaHQgPSBsaW5lLndpZGdldHNbaV0ubm9kZS5vZmZzZXRIZWlnaHQ7XHJcbiAgfVxyXG5cclxuICAvLyBEbyBhIGJ1bGstcmVhZCBvZiB0aGUgRE9NIHBvc2l0aW9ucyBhbmQgc2l6ZXMgbmVlZGVkIHRvIGRyYXcgdGhlXHJcbiAgLy8gdmlldywgc28gdGhhdCB3ZSBkb24ndCBpbnRlcmxlYXZlIHJlYWRpbmcgYW5kIHdyaXRpbmcgdG8gdGhlIERPTS5cclxuICBmdW5jdGlvbiBnZXREaW1lbnNpb25zKGNtKSB7XHJcbiAgICB2YXIgZCA9IGNtLmRpc3BsYXksIGxlZnQgPSB7fSwgd2lkdGggPSB7fTtcclxuICAgIHZhciBndXR0ZXJMZWZ0ID0gZC5ndXR0ZXJzLmNsaWVudExlZnQ7XHJcbiAgICBmb3IgKHZhciBuID0gZC5ndXR0ZXJzLmZpcnN0Q2hpbGQsIGkgPSAwOyBuOyBuID0gbi5uZXh0U2libGluZywgKytpKSB7XHJcbiAgICAgIGxlZnRbY20ub3B0aW9ucy5ndXR0ZXJzW2ldXSA9IG4ub2Zmc2V0TGVmdCArIG4uY2xpZW50TGVmdCArIGd1dHRlckxlZnQ7XHJcbiAgICAgIHdpZHRoW2NtLm9wdGlvbnMuZ3V0dGVyc1tpXV0gPSBuLmNsaWVudFdpZHRoO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIHtmaXhlZFBvczogY29tcGVuc2F0ZUZvckhTY3JvbGwoZCksXHJcbiAgICAgICAgICAgIGd1dHRlclRvdGFsV2lkdGg6IGQuZ3V0dGVycy5vZmZzZXRXaWR0aCxcclxuICAgICAgICAgICAgZ3V0dGVyTGVmdDogbGVmdCxcclxuICAgICAgICAgICAgZ3V0dGVyV2lkdGg6IHdpZHRoLFxyXG4gICAgICAgICAgICB3cmFwcGVyV2lkdGg6IGQud3JhcHBlci5jbGllbnRXaWR0aH07XHJcbiAgfVxyXG5cclxuICAvLyBTeW5jIHRoZSBhY3R1YWwgZGlzcGxheSBET00gc3RydWN0dXJlIHdpdGggZGlzcGxheS52aWV3LCByZW1vdmluZ1xyXG4gIC8vIG5vZGVzIGZvciBsaW5lcyB0aGF0IGFyZSBubyBsb25nZXIgaW4gdmlldywgYW5kIGNyZWF0aW5nIHRoZSBvbmVzXHJcbiAgLy8gdGhhdCBhcmUgbm90IHRoZXJlIHlldCwgYW5kIHVwZGF0aW5nIHRoZSBvbmVzIHRoYXQgYXJlIG91dCBvZlxyXG4gIC8vIGRhdGUuXHJcbiAgZnVuY3Rpb24gcGF0Y2hEaXNwbGF5KGNtLCB1cGRhdGVOdW1iZXJzRnJvbSwgZGltcykge1xyXG4gICAgdmFyIGRpc3BsYXkgPSBjbS5kaXNwbGF5LCBsaW5lTnVtYmVycyA9IGNtLm9wdGlvbnMubGluZU51bWJlcnM7XHJcbiAgICB2YXIgY29udGFpbmVyID0gZGlzcGxheS5saW5lRGl2LCBjdXIgPSBjb250YWluZXIuZmlyc3RDaGlsZDtcclxuXHJcbiAgICBmdW5jdGlvbiBybShub2RlKSB7XHJcbiAgICAgIHZhciBuZXh0ID0gbm9kZS5uZXh0U2libGluZztcclxuICAgICAgLy8gV29ya3MgYXJvdW5kIGEgdGhyb3ctc2Nyb2xsIGJ1ZyBpbiBPUyBYIFdlYmtpdFxyXG4gICAgICBpZiAod2Via2l0ICYmIG1hYyAmJiBjbS5kaXNwbGF5LmN1cnJlbnRXaGVlbFRhcmdldCA9PSBub2RlKVxyXG4gICAgICAgIG5vZGUuc3R5bGUuZGlzcGxheSA9IFwibm9uZVwiO1xyXG4gICAgICBlbHNlXHJcbiAgICAgICAgbm9kZS5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKG5vZGUpO1xyXG4gICAgICByZXR1cm4gbmV4dDtcclxuICAgIH1cclxuXHJcbiAgICB2YXIgdmlldyA9IGRpc3BsYXkudmlldywgbGluZU4gPSBkaXNwbGF5LnZpZXdGcm9tO1xyXG4gICAgLy8gTG9vcCBvdmVyIHRoZSBlbGVtZW50cyBpbiB0aGUgdmlldywgc3luY2luZyBjdXIgKHRoZSBET00gbm9kZXNcclxuICAgIC8vIGluIGRpc3BsYXkubGluZURpdikgd2l0aCB0aGUgdmlldyBhcyB3ZSBnby5cclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdmlldy5sZW5ndGg7IGkrKykge1xyXG4gICAgICB2YXIgbGluZVZpZXcgPSB2aWV3W2ldO1xyXG4gICAgICBpZiAobGluZVZpZXcuaGlkZGVuKSB7XHJcbiAgICAgIH0gZWxzZSBpZiAoIWxpbmVWaWV3Lm5vZGUgfHwgbGluZVZpZXcubm9kZS5wYXJlbnROb2RlICE9IGNvbnRhaW5lcikgeyAvLyBOb3QgZHJhd24geWV0XHJcbiAgICAgICAgdmFyIG5vZGUgPSBidWlsZExpbmVFbGVtZW50KGNtLCBsaW5lVmlldywgbGluZU4sIGRpbXMpO1xyXG4gICAgICAgIGNvbnRhaW5lci5pbnNlcnRCZWZvcmUobm9kZSwgY3VyKTtcclxuICAgICAgfSBlbHNlIHsgLy8gQWxyZWFkeSBkcmF3blxyXG4gICAgICAgIHdoaWxlIChjdXIgIT0gbGluZVZpZXcubm9kZSkgY3VyID0gcm0oY3VyKTtcclxuICAgICAgICB2YXIgdXBkYXRlTnVtYmVyID0gbGluZU51bWJlcnMgJiYgdXBkYXRlTnVtYmVyc0Zyb20gIT0gbnVsbCAmJlxyXG4gICAgICAgICAgdXBkYXRlTnVtYmVyc0Zyb20gPD0gbGluZU4gJiYgbGluZVZpZXcubGluZU51bWJlcjtcclxuICAgICAgICBpZiAobGluZVZpZXcuY2hhbmdlcykge1xyXG4gICAgICAgICAgaWYgKGluZGV4T2YobGluZVZpZXcuY2hhbmdlcywgXCJndXR0ZXJcIikgPiAtMSkgdXBkYXRlTnVtYmVyID0gZmFsc2U7XHJcbiAgICAgICAgICB1cGRhdGVMaW5lRm9yQ2hhbmdlcyhjbSwgbGluZVZpZXcsIGxpbmVOLCBkaW1zKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYgKHVwZGF0ZU51bWJlcikge1xyXG4gICAgICAgICAgcmVtb3ZlQ2hpbGRyZW4obGluZVZpZXcubGluZU51bWJlcik7XHJcbiAgICAgICAgICBsaW5lVmlldy5saW5lTnVtYmVyLmFwcGVuZENoaWxkKGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKGxpbmVOdW1iZXJGb3IoY20ub3B0aW9ucywgbGluZU4pKSk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGN1ciA9IGxpbmVWaWV3Lm5vZGUubmV4dFNpYmxpbmc7XHJcbiAgICAgIH1cclxuICAgICAgbGluZU4gKz0gbGluZVZpZXcuc2l6ZTtcclxuICAgIH1cclxuICAgIHdoaWxlIChjdXIpIGN1ciA9IHJtKGN1cik7XHJcbiAgfVxyXG5cclxuICAvLyBXaGVuIGFuIGFzcGVjdCBvZiBhIGxpbmUgY2hhbmdlcywgYSBzdHJpbmcgaXMgYWRkZWQgdG9cclxuICAvLyBsaW5lVmlldy5jaGFuZ2VzLiBUaGlzIHVwZGF0ZXMgdGhlIHJlbGV2YW50IHBhcnQgb2YgdGhlIGxpbmUnc1xyXG4gIC8vIERPTSBzdHJ1Y3R1cmUuXHJcbiAgZnVuY3Rpb24gdXBkYXRlTGluZUZvckNoYW5nZXMoY20sIGxpbmVWaWV3LCBsaW5lTiwgZGltcykge1xyXG4gICAgZm9yICh2YXIgaiA9IDA7IGogPCBsaW5lVmlldy5jaGFuZ2VzLmxlbmd0aDsgaisrKSB7XHJcbiAgICAgIHZhciB0eXBlID0gbGluZVZpZXcuY2hhbmdlc1tqXTtcclxuICAgICAgaWYgKHR5cGUgPT0gXCJ0ZXh0XCIpIHVwZGF0ZUxpbmVUZXh0KGNtLCBsaW5lVmlldyk7XHJcbiAgICAgIGVsc2UgaWYgKHR5cGUgPT0gXCJndXR0ZXJcIikgdXBkYXRlTGluZUd1dHRlcihjbSwgbGluZVZpZXcsIGxpbmVOLCBkaW1zKTtcclxuICAgICAgZWxzZSBpZiAodHlwZSA9PSBcImNsYXNzXCIpIHVwZGF0ZUxpbmVDbGFzc2VzKGxpbmVWaWV3KTtcclxuICAgICAgZWxzZSBpZiAodHlwZSA9PSBcIndpZGdldFwiKSB1cGRhdGVMaW5lV2lkZ2V0cyhjbSwgbGluZVZpZXcsIGRpbXMpO1xyXG4gICAgfVxyXG4gICAgbGluZVZpZXcuY2hhbmdlcyA9IG51bGw7XHJcbiAgfVxyXG5cclxuICAvLyBMaW5lcyB3aXRoIGd1dHRlciBlbGVtZW50cywgd2lkZ2V0cyBvciBhIGJhY2tncm91bmQgY2xhc3MgbmVlZCB0b1xyXG4gIC8vIGJlIHdyYXBwZWQsIGFuZCBoYXZlIHRoZSBleHRyYSBlbGVtZW50cyBhZGRlZCB0byB0aGUgd3JhcHBlciBkaXZcclxuICBmdW5jdGlvbiBlbnN1cmVMaW5lV3JhcHBlZChsaW5lVmlldykge1xyXG4gICAgaWYgKGxpbmVWaWV3Lm5vZGUgPT0gbGluZVZpZXcudGV4dCkge1xyXG4gICAgICBsaW5lVmlldy5ub2RlID0gZWx0KFwiZGl2XCIsIG51bGwsIG51bGwsIFwicG9zaXRpb246IHJlbGF0aXZlXCIpO1xyXG4gICAgICBpZiAobGluZVZpZXcudGV4dC5wYXJlbnROb2RlKVxyXG4gICAgICAgIGxpbmVWaWV3LnRleHQucGFyZW50Tm9kZS5yZXBsYWNlQ2hpbGQobGluZVZpZXcubm9kZSwgbGluZVZpZXcudGV4dCk7XHJcbiAgICAgIGxpbmVWaWV3Lm5vZGUuYXBwZW5kQ2hpbGQobGluZVZpZXcudGV4dCk7XHJcbiAgICAgIGlmIChpZSAmJiBpZV92ZXJzaW9uIDwgOCkgbGluZVZpZXcubm9kZS5zdHlsZS56SW5kZXggPSAyO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIGxpbmVWaWV3Lm5vZGU7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiB1cGRhdGVMaW5lQmFja2dyb3VuZChsaW5lVmlldykge1xyXG4gICAgdmFyIGNscyA9IGxpbmVWaWV3LmJnQ2xhc3MgPyBsaW5lVmlldy5iZ0NsYXNzICsgXCIgXCIgKyAobGluZVZpZXcubGluZS5iZ0NsYXNzIHx8IFwiXCIpIDogbGluZVZpZXcubGluZS5iZ0NsYXNzO1xyXG4gICAgaWYgKGNscykgY2xzICs9IFwiIENvZGVNaXJyb3ItbGluZWJhY2tncm91bmRcIjtcclxuICAgIGlmIChsaW5lVmlldy5iYWNrZ3JvdW5kKSB7XHJcbiAgICAgIGlmIChjbHMpIGxpbmVWaWV3LmJhY2tncm91bmQuY2xhc3NOYW1lID0gY2xzO1xyXG4gICAgICBlbHNlIHsgbGluZVZpZXcuYmFja2dyb3VuZC5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKGxpbmVWaWV3LmJhY2tncm91bmQpOyBsaW5lVmlldy5iYWNrZ3JvdW5kID0gbnVsbDsgfVxyXG4gICAgfSBlbHNlIGlmIChjbHMpIHtcclxuICAgICAgdmFyIHdyYXAgPSBlbnN1cmVMaW5lV3JhcHBlZChsaW5lVmlldyk7XHJcbiAgICAgIGxpbmVWaWV3LmJhY2tncm91bmQgPSB3cmFwLmluc2VydEJlZm9yZShlbHQoXCJkaXZcIiwgbnVsbCwgY2xzKSwgd3JhcC5maXJzdENoaWxkKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFdyYXBwZXIgYXJvdW5kIGJ1aWxkTGluZUNvbnRlbnQgd2hpY2ggd2lsbCByZXVzZSB0aGUgc3RydWN0dXJlXHJcbiAgLy8gaW4gZGlzcGxheS5leHRlcm5hbE1lYXN1cmVkIHdoZW4gcG9zc2libGUuXHJcbiAgZnVuY3Rpb24gZ2V0TGluZUNvbnRlbnQoY20sIGxpbmVWaWV3KSB7XHJcbiAgICB2YXIgZXh0ID0gY20uZGlzcGxheS5leHRlcm5hbE1lYXN1cmVkO1xyXG4gICAgaWYgKGV4dCAmJiBleHQubGluZSA9PSBsaW5lVmlldy5saW5lKSB7XHJcbiAgICAgIGNtLmRpc3BsYXkuZXh0ZXJuYWxNZWFzdXJlZCA9IG51bGw7XHJcbiAgICAgIGxpbmVWaWV3Lm1lYXN1cmUgPSBleHQubWVhc3VyZTtcclxuICAgICAgcmV0dXJuIGV4dC5idWlsdDtcclxuICAgIH1cclxuICAgIHJldHVybiBidWlsZExpbmVDb250ZW50KGNtLCBsaW5lVmlldyk7XHJcbiAgfVxyXG5cclxuICAvLyBSZWRyYXcgdGhlIGxpbmUncyB0ZXh0LiBJbnRlcmFjdHMgd2l0aCB0aGUgYmFja2dyb3VuZCBhbmQgdGV4dFxyXG4gIC8vIGNsYXNzZXMgYmVjYXVzZSB0aGUgbW9kZSBtYXkgb3V0cHV0IHRva2VucyB0aGF0IGluZmx1ZW5jZSB0aGVzZVxyXG4gIC8vIGNsYXNzZXMuXHJcbiAgZnVuY3Rpb24gdXBkYXRlTGluZVRleHQoY20sIGxpbmVWaWV3KSB7XHJcbiAgICB2YXIgY2xzID0gbGluZVZpZXcudGV4dC5jbGFzc05hbWU7XHJcbiAgICB2YXIgYnVpbHQgPSBnZXRMaW5lQ29udGVudChjbSwgbGluZVZpZXcpO1xyXG4gICAgaWYgKGxpbmVWaWV3LnRleHQgPT0gbGluZVZpZXcubm9kZSkgbGluZVZpZXcubm9kZSA9IGJ1aWx0LnByZTtcclxuICAgIGxpbmVWaWV3LnRleHQucGFyZW50Tm9kZS5yZXBsYWNlQ2hpbGQoYnVpbHQucHJlLCBsaW5lVmlldy50ZXh0KTtcclxuICAgIGxpbmVWaWV3LnRleHQgPSBidWlsdC5wcmU7XHJcbiAgICBpZiAoYnVpbHQuYmdDbGFzcyAhPSBsaW5lVmlldy5iZ0NsYXNzIHx8IGJ1aWx0LnRleHRDbGFzcyAhPSBsaW5lVmlldy50ZXh0Q2xhc3MpIHtcclxuICAgICAgbGluZVZpZXcuYmdDbGFzcyA9IGJ1aWx0LmJnQ2xhc3M7XHJcbiAgICAgIGxpbmVWaWV3LnRleHRDbGFzcyA9IGJ1aWx0LnRleHRDbGFzcztcclxuICAgICAgdXBkYXRlTGluZUNsYXNzZXMobGluZVZpZXcpO1xyXG4gICAgfSBlbHNlIGlmIChjbHMpIHtcclxuICAgICAgbGluZVZpZXcudGV4dC5jbGFzc05hbWUgPSBjbHM7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiB1cGRhdGVMaW5lQ2xhc3NlcyhsaW5lVmlldykge1xyXG4gICAgdXBkYXRlTGluZUJhY2tncm91bmQobGluZVZpZXcpO1xyXG4gICAgaWYgKGxpbmVWaWV3LmxpbmUud3JhcENsYXNzKVxyXG4gICAgICBlbnN1cmVMaW5lV3JhcHBlZChsaW5lVmlldykuY2xhc3NOYW1lID0gbGluZVZpZXcubGluZS53cmFwQ2xhc3M7XHJcbiAgICBlbHNlIGlmIChsaW5lVmlldy5ub2RlICE9IGxpbmVWaWV3LnRleHQpXHJcbiAgICAgIGxpbmVWaWV3Lm5vZGUuY2xhc3NOYW1lID0gXCJcIjtcclxuICAgIHZhciB0ZXh0Q2xhc3MgPSBsaW5lVmlldy50ZXh0Q2xhc3MgPyBsaW5lVmlldy50ZXh0Q2xhc3MgKyBcIiBcIiArIChsaW5lVmlldy5saW5lLnRleHRDbGFzcyB8fCBcIlwiKSA6IGxpbmVWaWV3LmxpbmUudGV4dENsYXNzO1xyXG4gICAgbGluZVZpZXcudGV4dC5jbGFzc05hbWUgPSB0ZXh0Q2xhc3MgfHwgXCJcIjtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIHVwZGF0ZUxpbmVHdXR0ZXIoY20sIGxpbmVWaWV3LCBsaW5lTiwgZGltcykge1xyXG4gICAgaWYgKGxpbmVWaWV3Lmd1dHRlcikge1xyXG4gICAgICBsaW5lVmlldy5ub2RlLnJlbW92ZUNoaWxkKGxpbmVWaWV3Lmd1dHRlcik7XHJcbiAgICAgIGxpbmVWaWV3Lmd1dHRlciA9IG51bGw7XHJcbiAgICB9XHJcbiAgICBpZiAobGluZVZpZXcuZ3V0dGVyQmFja2dyb3VuZCkge1xyXG4gICAgICBsaW5lVmlldy5ub2RlLnJlbW92ZUNoaWxkKGxpbmVWaWV3Lmd1dHRlckJhY2tncm91bmQpO1xyXG4gICAgICBsaW5lVmlldy5ndXR0ZXJCYWNrZ3JvdW5kID0gbnVsbDtcclxuICAgIH1cclxuICAgIGlmIChsaW5lVmlldy5saW5lLmd1dHRlckNsYXNzKSB7XHJcbiAgICAgIHZhciB3cmFwID0gZW5zdXJlTGluZVdyYXBwZWQobGluZVZpZXcpO1xyXG4gICAgICBsaW5lVmlldy5ndXR0ZXJCYWNrZ3JvdW5kID0gZWx0KFwiZGl2XCIsIG51bGwsIFwiQ29kZU1pcnJvci1ndXR0ZXItYmFja2dyb3VuZCBcIiArIGxpbmVWaWV3LmxpbmUuZ3V0dGVyQ2xhc3MsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXCJsZWZ0OiBcIiArIChjbS5vcHRpb25zLmZpeGVkR3V0dGVyID8gZGltcy5maXhlZFBvcyA6IC1kaW1zLmd1dHRlclRvdGFsV2lkdGgpICtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcInB4OyB3aWR0aDogXCIgKyBkaW1zLmd1dHRlclRvdGFsV2lkdGggKyBcInB4XCIpO1xyXG4gICAgICB3cmFwLmluc2VydEJlZm9yZShsaW5lVmlldy5ndXR0ZXJCYWNrZ3JvdW5kLCBsaW5lVmlldy50ZXh0KTtcclxuICAgIH1cclxuICAgIHZhciBtYXJrZXJzID0gbGluZVZpZXcubGluZS5ndXR0ZXJNYXJrZXJzO1xyXG4gICAgaWYgKGNtLm9wdGlvbnMubGluZU51bWJlcnMgfHwgbWFya2Vycykge1xyXG4gICAgICB2YXIgd3JhcCA9IGVuc3VyZUxpbmVXcmFwcGVkKGxpbmVWaWV3KTtcclxuICAgICAgdmFyIGd1dHRlcldyYXAgPSBsaW5lVmlldy5ndXR0ZXIgPSBlbHQoXCJkaXZcIiwgbnVsbCwgXCJDb2RlTWlycm9yLWd1dHRlci13cmFwcGVyXCIsIFwibGVmdDogXCIgK1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoY20ub3B0aW9ucy5maXhlZEd1dHRlciA/IGRpbXMuZml4ZWRQb3MgOiAtZGltcy5ndXR0ZXJUb3RhbFdpZHRoKSArIFwicHhcIik7XHJcbiAgICAgIGNtLmRpc3BsYXkuaW5wdXQuc2V0VW5lZGl0YWJsZShndXR0ZXJXcmFwKTtcclxuICAgICAgd3JhcC5pbnNlcnRCZWZvcmUoZ3V0dGVyV3JhcCwgbGluZVZpZXcudGV4dCk7XHJcbiAgICAgIGlmIChsaW5lVmlldy5saW5lLmd1dHRlckNsYXNzKVxyXG4gICAgICAgIGd1dHRlcldyYXAuY2xhc3NOYW1lICs9IFwiIFwiICsgbGluZVZpZXcubGluZS5ndXR0ZXJDbGFzcztcclxuICAgICAgaWYgKGNtLm9wdGlvbnMubGluZU51bWJlcnMgJiYgKCFtYXJrZXJzIHx8ICFtYXJrZXJzW1wiQ29kZU1pcnJvci1saW5lbnVtYmVyc1wiXSkpXHJcbiAgICAgICAgbGluZVZpZXcubGluZU51bWJlciA9IGd1dHRlcldyYXAuYXBwZW5kQ2hpbGQoXHJcbiAgICAgICAgICBlbHQoXCJkaXZcIiwgbGluZU51bWJlckZvcihjbS5vcHRpb25zLCBsaW5lTiksXHJcbiAgICAgICAgICAgICAgXCJDb2RlTWlycm9yLWxpbmVudW1iZXIgQ29kZU1pcnJvci1ndXR0ZXItZWx0XCIsXHJcbiAgICAgICAgICAgICAgXCJsZWZ0OiBcIiArIGRpbXMuZ3V0dGVyTGVmdFtcIkNvZGVNaXJyb3ItbGluZW51bWJlcnNcIl0gKyBcInB4OyB3aWR0aDogXCJcclxuICAgICAgICAgICAgICArIGNtLmRpc3BsYXkubGluZU51bUlubmVyV2lkdGggKyBcInB4XCIpKTtcclxuICAgICAgaWYgKG1hcmtlcnMpIGZvciAodmFyIGsgPSAwOyBrIDwgY20ub3B0aW9ucy5ndXR0ZXJzLmxlbmd0aDsgKytrKSB7XHJcbiAgICAgICAgdmFyIGlkID0gY20ub3B0aW9ucy5ndXR0ZXJzW2tdLCBmb3VuZCA9IG1hcmtlcnMuaGFzT3duUHJvcGVydHkoaWQpICYmIG1hcmtlcnNbaWRdO1xyXG4gICAgICAgIGlmIChmb3VuZClcclxuICAgICAgICAgIGd1dHRlcldyYXAuYXBwZW5kQ2hpbGQoZWx0KFwiZGl2XCIsIFtmb3VuZF0sIFwiQ29kZU1pcnJvci1ndXR0ZXItZWx0XCIsIFwibGVmdDogXCIgK1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGltcy5ndXR0ZXJMZWZ0W2lkXSArIFwicHg7IHdpZHRoOiBcIiArIGRpbXMuZ3V0dGVyV2lkdGhbaWRdICsgXCJweFwiKSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIHVwZGF0ZUxpbmVXaWRnZXRzKGNtLCBsaW5lVmlldywgZGltcykge1xyXG4gICAgaWYgKGxpbmVWaWV3LmFsaWduYWJsZSkgbGluZVZpZXcuYWxpZ25hYmxlID0gbnVsbDtcclxuICAgIGZvciAodmFyIG5vZGUgPSBsaW5lVmlldy5ub2RlLmZpcnN0Q2hpbGQsIG5leHQ7IG5vZGU7IG5vZGUgPSBuZXh0KSB7XHJcbiAgICAgIHZhciBuZXh0ID0gbm9kZS5uZXh0U2libGluZztcclxuICAgICAgaWYgKG5vZGUuY2xhc3NOYW1lID09IFwiQ29kZU1pcnJvci1saW5ld2lkZ2V0XCIpXHJcbiAgICAgICAgbGluZVZpZXcubm9kZS5yZW1vdmVDaGlsZChub2RlKTtcclxuICAgIH1cclxuICAgIGluc2VydExpbmVXaWRnZXRzKGNtLCBsaW5lVmlldywgZGltcyk7XHJcbiAgfVxyXG5cclxuICAvLyBCdWlsZCBhIGxpbmUncyBET00gcmVwcmVzZW50YXRpb24gZnJvbSBzY3JhdGNoXHJcbiAgZnVuY3Rpb24gYnVpbGRMaW5lRWxlbWVudChjbSwgbGluZVZpZXcsIGxpbmVOLCBkaW1zKSB7XHJcbiAgICB2YXIgYnVpbHQgPSBnZXRMaW5lQ29udGVudChjbSwgbGluZVZpZXcpO1xyXG4gICAgbGluZVZpZXcudGV4dCA9IGxpbmVWaWV3Lm5vZGUgPSBidWlsdC5wcmU7XHJcbiAgICBpZiAoYnVpbHQuYmdDbGFzcykgbGluZVZpZXcuYmdDbGFzcyA9IGJ1aWx0LmJnQ2xhc3M7XHJcbiAgICBpZiAoYnVpbHQudGV4dENsYXNzKSBsaW5lVmlldy50ZXh0Q2xhc3MgPSBidWlsdC50ZXh0Q2xhc3M7XHJcblxyXG4gICAgdXBkYXRlTGluZUNsYXNzZXMobGluZVZpZXcpO1xyXG4gICAgdXBkYXRlTGluZUd1dHRlcihjbSwgbGluZVZpZXcsIGxpbmVOLCBkaW1zKTtcclxuICAgIGluc2VydExpbmVXaWRnZXRzKGNtLCBsaW5lVmlldywgZGltcyk7XHJcbiAgICByZXR1cm4gbGluZVZpZXcubm9kZTtcclxuICB9XHJcblxyXG4gIC8vIEEgbGluZVZpZXcgbWF5IGNvbnRhaW4gbXVsdGlwbGUgbG9naWNhbCBsaW5lcyAod2hlbiBtZXJnZWQgYnlcclxuICAvLyBjb2xsYXBzZWQgc3BhbnMpLiBUaGUgd2lkZ2V0cyBmb3IgYWxsIG9mIHRoZW0gbmVlZCB0byBiZSBkcmF3bi5cclxuICBmdW5jdGlvbiBpbnNlcnRMaW5lV2lkZ2V0cyhjbSwgbGluZVZpZXcsIGRpbXMpIHtcclxuICAgIGluc2VydExpbmVXaWRnZXRzRm9yKGNtLCBsaW5lVmlldy5saW5lLCBsaW5lVmlldywgZGltcywgdHJ1ZSk7XHJcbiAgICBpZiAobGluZVZpZXcucmVzdCkgZm9yICh2YXIgaSA9IDA7IGkgPCBsaW5lVmlldy5yZXN0Lmxlbmd0aDsgaSsrKVxyXG4gICAgICBpbnNlcnRMaW5lV2lkZ2V0c0ZvcihjbSwgbGluZVZpZXcucmVzdFtpXSwgbGluZVZpZXcsIGRpbXMsIGZhbHNlKTtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIGluc2VydExpbmVXaWRnZXRzRm9yKGNtLCBsaW5lLCBsaW5lVmlldywgZGltcywgYWxsb3dBYm92ZSkge1xyXG4gICAgaWYgKCFsaW5lLndpZGdldHMpIHJldHVybjtcclxuICAgIHZhciB3cmFwID0gZW5zdXJlTGluZVdyYXBwZWQobGluZVZpZXcpO1xyXG4gICAgZm9yICh2YXIgaSA9IDAsIHdzID0gbGluZS53aWRnZXRzOyBpIDwgd3MubGVuZ3RoOyArK2kpIHtcclxuICAgICAgdmFyIHdpZGdldCA9IHdzW2ldLCBub2RlID0gZWx0KFwiZGl2XCIsIFt3aWRnZXQubm9kZV0sIFwiQ29kZU1pcnJvci1saW5ld2lkZ2V0XCIpO1xyXG4gICAgICBpZiAoIXdpZGdldC5oYW5kbGVNb3VzZUV2ZW50cykgbm9kZS5zZXRBdHRyaWJ1dGUoXCJjbS1pZ25vcmUtZXZlbnRzXCIsIFwidHJ1ZVwiKTtcclxuICAgICAgcG9zaXRpb25MaW5lV2lkZ2V0KHdpZGdldCwgbm9kZSwgbGluZVZpZXcsIGRpbXMpO1xyXG4gICAgICBjbS5kaXNwbGF5LmlucHV0LnNldFVuZWRpdGFibGUobm9kZSk7XHJcbiAgICAgIGlmIChhbGxvd0Fib3ZlICYmIHdpZGdldC5hYm92ZSlcclxuICAgICAgICB3cmFwLmluc2VydEJlZm9yZShub2RlLCBsaW5lVmlldy5ndXR0ZXIgfHwgbGluZVZpZXcudGV4dCk7XHJcbiAgICAgIGVsc2VcclxuICAgICAgICB3cmFwLmFwcGVuZENoaWxkKG5vZGUpO1xyXG4gICAgICBzaWduYWxMYXRlcih3aWRnZXQsIFwicmVkcmF3XCIpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gcG9zaXRpb25MaW5lV2lkZ2V0KHdpZGdldCwgbm9kZSwgbGluZVZpZXcsIGRpbXMpIHtcclxuICAgIGlmICh3aWRnZXQubm9IU2Nyb2xsKSB7XHJcbiAgICAgIChsaW5lVmlldy5hbGlnbmFibGUgfHwgKGxpbmVWaWV3LmFsaWduYWJsZSA9IFtdKSkucHVzaChub2RlKTtcclxuICAgICAgdmFyIHdpZHRoID0gZGltcy53cmFwcGVyV2lkdGg7XHJcbiAgICAgIG5vZGUuc3R5bGUubGVmdCA9IGRpbXMuZml4ZWRQb3MgKyBcInB4XCI7XHJcbiAgICAgIGlmICghd2lkZ2V0LmNvdmVyR3V0dGVyKSB7XHJcbiAgICAgICAgd2lkdGggLT0gZGltcy5ndXR0ZXJUb3RhbFdpZHRoO1xyXG4gICAgICAgIG5vZGUuc3R5bGUucGFkZGluZ0xlZnQgPSBkaW1zLmd1dHRlclRvdGFsV2lkdGggKyBcInB4XCI7XHJcbiAgICAgIH1cclxuICAgICAgbm9kZS5zdHlsZS53aWR0aCA9IHdpZHRoICsgXCJweFwiO1xyXG4gICAgfVxyXG4gICAgaWYgKHdpZGdldC5jb3Zlckd1dHRlcikge1xyXG4gICAgICBub2RlLnN0eWxlLnpJbmRleCA9IDU7XHJcbiAgICAgIG5vZGUuc3R5bGUucG9zaXRpb24gPSBcInJlbGF0aXZlXCI7XHJcbiAgICAgIGlmICghd2lkZ2V0Lm5vSFNjcm9sbCkgbm9kZS5zdHlsZS5tYXJnaW5MZWZ0ID0gLWRpbXMuZ3V0dGVyVG90YWxXaWR0aCArIFwicHhcIjtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFBPU0lUSU9OIE9CSkVDVFxyXG5cclxuICAvLyBBIFBvcyBpbnN0YW5jZSByZXByZXNlbnRzIGEgcG9zaXRpb24gd2l0aGluIHRoZSB0ZXh0LlxyXG4gIHZhciBQb3MgPSBDb2RlTWlycm9yLlBvcyA9IGZ1bmN0aW9uKGxpbmUsIGNoKSB7XHJcbiAgICBpZiAoISh0aGlzIGluc3RhbmNlb2YgUG9zKSkgcmV0dXJuIG5ldyBQb3MobGluZSwgY2gpO1xyXG4gICAgdGhpcy5saW5lID0gbGluZTsgdGhpcy5jaCA9IGNoO1xyXG4gIH07XHJcblxyXG4gIC8vIENvbXBhcmUgdHdvIHBvc2l0aW9ucywgcmV0dXJuIDAgaWYgdGhleSBhcmUgdGhlIHNhbWUsIGEgbmVnYXRpdmVcclxuICAvLyBudW1iZXIgd2hlbiBhIGlzIGxlc3MsIGFuZCBhIHBvc2l0aXZlIG51bWJlciBvdGhlcndpc2UuXHJcbiAgdmFyIGNtcCA9IENvZGVNaXJyb3IuY21wUG9zID0gZnVuY3Rpb24oYSwgYikgeyByZXR1cm4gYS5saW5lIC0gYi5saW5lIHx8IGEuY2ggLSBiLmNoOyB9O1xyXG5cclxuICBmdW5jdGlvbiBjb3B5UG9zKHgpIHtyZXR1cm4gUG9zKHgubGluZSwgeC5jaCk7fVxyXG4gIGZ1bmN0aW9uIG1heFBvcyhhLCBiKSB7IHJldHVybiBjbXAoYSwgYikgPCAwID8gYiA6IGE7IH1cclxuICBmdW5jdGlvbiBtaW5Qb3MoYSwgYikgeyByZXR1cm4gY21wKGEsIGIpIDwgMCA/IGEgOiBiOyB9XHJcblxyXG4gIC8vIElOUFVUIEhBTkRMSU5HXHJcblxyXG4gIGZ1bmN0aW9uIGVuc3VyZUZvY3VzKGNtKSB7XHJcbiAgICBpZiAoIWNtLnN0YXRlLmZvY3VzZWQpIHsgY20uZGlzcGxheS5pbnB1dC5mb2N1cygpOyBvbkZvY3VzKGNtKTsgfVxyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gaXNSZWFkT25seShjbSkge1xyXG4gICAgcmV0dXJuIGNtLm9wdGlvbnMucmVhZE9ubHkgfHwgY20uZG9jLmNhbnRFZGl0O1xyXG4gIH1cclxuXHJcbiAgLy8gVGhpcyB3aWxsIGJlIHNldCB0byBhbiBhcnJheSBvZiBzdHJpbmdzIHdoZW4gY29weWluZywgc28gdGhhdCxcclxuICAvLyB3aGVuIHBhc3RpbmcsIHdlIGtub3cgd2hhdCBraW5kIG9mIHNlbGVjdGlvbnMgdGhlIGNvcGllZCB0ZXh0XHJcbiAgLy8gd2FzIG1hZGUgb3V0IG9mLlxyXG4gIHZhciBsYXN0Q29waWVkID0gbnVsbDtcclxuXHJcbiAgZnVuY3Rpb24gYXBwbHlUZXh0SW5wdXQoY20sIGluc2VydGVkLCBkZWxldGVkLCBzZWwsIG9yaWdpbikge1xyXG4gICAgdmFyIGRvYyA9IGNtLmRvYztcclxuICAgIGNtLmRpc3BsYXkuc2hpZnQgPSBmYWxzZTtcclxuICAgIGlmICghc2VsKSBzZWwgPSBkb2Muc2VsO1xyXG5cclxuICAgIHZhciBwYXN0ZSA9IGNtLnN0YXRlLnBhc3RlSW5jb21pbmcgfHwgb3JpZ2luID09IFwicGFzdGVcIjtcclxuICAgIHZhciB0ZXh0TGluZXMgPSBkb2Muc3BsaXRMaW5lcyhpbnNlcnRlZCksIG11bHRpUGFzdGUgPSBudWxsO1xyXG4gICAgLy8gV2hlbiBwYXNpbmcgTiBsaW5lcyBpbnRvIE4gc2VsZWN0aW9ucywgaW5zZXJ0IG9uZSBsaW5lIHBlciBzZWxlY3Rpb25cclxuICAgIGlmIChwYXN0ZSAmJiBzZWwucmFuZ2VzLmxlbmd0aCA+IDEpIHtcclxuICAgICAgaWYgKGxhc3RDb3BpZWQgJiYgbGFzdENvcGllZC5qb2luKFwiXFxuXCIpID09IGluc2VydGVkKSB7XHJcbiAgICAgICAgaWYgKHNlbC5yYW5nZXMubGVuZ3RoICUgbGFzdENvcGllZC5sZW5ndGggPT0gMCkge1xyXG4gICAgICAgICAgbXVsdGlQYXN0ZSA9IFtdO1xyXG4gICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsYXN0Q29waWVkLmxlbmd0aDsgaSsrKVxyXG4gICAgICAgICAgICBtdWx0aVBhc3RlLnB1c2goZG9jLnNwbGl0TGluZXMobGFzdENvcGllZFtpXSkpO1xyXG4gICAgICAgIH1cclxuICAgICAgfSBlbHNlIGlmICh0ZXh0TGluZXMubGVuZ3RoID09IHNlbC5yYW5nZXMubGVuZ3RoKSB7XHJcbiAgICAgICAgbXVsdGlQYXN0ZSA9IG1hcCh0ZXh0TGluZXMsIGZ1bmN0aW9uKGwpIHsgcmV0dXJuIFtsXTsgfSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBOb3JtYWwgYmVoYXZpb3IgaXMgdG8gaW5zZXJ0IHRoZSBuZXcgdGV4dCBpbnRvIGV2ZXJ5IHNlbGVjdGlvblxyXG4gICAgZm9yICh2YXIgaSA9IHNlbC5yYW5nZXMubGVuZ3RoIC0gMTsgaSA+PSAwOyBpLS0pIHtcclxuICAgICAgdmFyIHJhbmdlID0gc2VsLnJhbmdlc1tpXTtcclxuICAgICAgdmFyIGZyb20gPSByYW5nZS5mcm9tKCksIHRvID0gcmFuZ2UudG8oKTtcclxuICAgICAgaWYgKHJhbmdlLmVtcHR5KCkpIHtcclxuICAgICAgICBpZiAoZGVsZXRlZCAmJiBkZWxldGVkID4gMCkgLy8gSGFuZGxlIGRlbGV0aW9uXHJcbiAgICAgICAgICBmcm9tID0gUG9zKGZyb20ubGluZSwgZnJvbS5jaCAtIGRlbGV0ZWQpO1xyXG4gICAgICAgIGVsc2UgaWYgKGNtLnN0YXRlLm92ZXJ3cml0ZSAmJiAhcGFzdGUpIC8vIEhhbmRsZSBvdmVyd3JpdGVcclxuICAgICAgICAgIHRvID0gUG9zKHRvLmxpbmUsIE1hdGgubWluKGdldExpbmUoZG9jLCB0by5saW5lKS50ZXh0Lmxlbmd0aCwgdG8uY2ggKyBsc3QodGV4dExpbmVzKS5sZW5ndGgpKTtcclxuICAgICAgfVxyXG4gICAgICB2YXIgdXBkYXRlSW5wdXQgPSBjbS5jdXJPcC51cGRhdGVJbnB1dDtcclxuICAgICAgdmFyIGNoYW5nZUV2ZW50ID0ge2Zyb206IGZyb20sIHRvOiB0bywgdGV4dDogbXVsdGlQYXN0ZSA/IG11bHRpUGFzdGVbaSAlIG11bHRpUGFzdGUubGVuZ3RoXSA6IHRleHRMaW5lcyxcclxuICAgICAgICAgICAgICAgICAgICAgICAgIG9yaWdpbjogb3JpZ2luIHx8IChwYXN0ZSA/IFwicGFzdGVcIiA6IGNtLnN0YXRlLmN1dEluY29taW5nID8gXCJjdXRcIiA6IFwiK2lucHV0XCIpfTtcclxuICAgICAgbWFrZUNoYW5nZShjbS5kb2MsIGNoYW5nZUV2ZW50KTtcclxuICAgICAgc2lnbmFsTGF0ZXIoY20sIFwiaW5wdXRSZWFkXCIsIGNtLCBjaGFuZ2VFdmVudCk7XHJcbiAgICB9XHJcbiAgICBpZiAoaW5zZXJ0ZWQgJiYgIXBhc3RlKVxyXG4gICAgICB0cmlnZ2VyRWxlY3RyaWMoY20sIGluc2VydGVkKTtcclxuXHJcbiAgICBlbnN1cmVDdXJzb3JWaXNpYmxlKGNtKTtcclxuICAgIGNtLmN1ck9wLnVwZGF0ZUlucHV0ID0gdXBkYXRlSW5wdXQ7XHJcbiAgICBjbS5jdXJPcC50eXBpbmcgPSB0cnVlO1xyXG4gICAgY20uc3RhdGUucGFzdGVJbmNvbWluZyA9IGNtLnN0YXRlLmN1dEluY29taW5nID0gZmFsc2U7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBoYW5kbGVQYXN0ZShlLCBjbSkge1xyXG4gICAgdmFyIHBhc3RlZCA9IGUuY2xpcGJvYXJkRGF0YSAmJiBlLmNsaXBib2FyZERhdGEuZ2V0RGF0YShcInRleHQvcGxhaW5cIik7XHJcbiAgICBpZiAocGFzdGVkKSB7XHJcbiAgICAgIGUucHJldmVudERlZmF1bHQoKTtcclxuICAgICAgaWYgKCFpc1JlYWRPbmx5KGNtKSAmJiAhY20ub3B0aW9ucy5kaXNhYmxlSW5wdXQpXHJcbiAgICAgICAgcnVuSW5PcChjbSwgZnVuY3Rpb24oKSB7IGFwcGx5VGV4dElucHV0KGNtLCBwYXN0ZWQsIDAsIG51bGwsIFwicGFzdGVcIik7IH0pO1xyXG4gICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIHRyaWdnZXJFbGVjdHJpYyhjbSwgaW5zZXJ0ZWQpIHtcclxuICAgIC8vIFdoZW4gYW4gJ2VsZWN0cmljJyBjaGFyYWN0ZXIgaXMgaW5zZXJ0ZWQsIGltbWVkaWF0ZWx5IHRyaWdnZXIgYSByZWluZGVudFxyXG4gICAgaWYgKCFjbS5vcHRpb25zLmVsZWN0cmljQ2hhcnMgfHwgIWNtLm9wdGlvbnMuc21hcnRJbmRlbnQpIHJldHVybjtcclxuICAgIHZhciBzZWwgPSBjbS5kb2Muc2VsO1xyXG5cclxuICAgIGZvciAodmFyIGkgPSBzZWwucmFuZ2VzLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tKSB7XHJcbiAgICAgIHZhciByYW5nZSA9IHNlbC5yYW5nZXNbaV07XHJcbiAgICAgIGlmIChyYW5nZS5oZWFkLmNoID4gMTAwIHx8IChpICYmIHNlbC5yYW5nZXNbaSAtIDFdLmhlYWQubGluZSA9PSByYW5nZS5oZWFkLmxpbmUpKSBjb250aW51ZTtcclxuICAgICAgdmFyIG1vZGUgPSBjbS5nZXRNb2RlQXQocmFuZ2UuaGVhZCk7XHJcbiAgICAgIHZhciBpbmRlbnRlZCA9IGZhbHNlO1xyXG4gICAgICBpZiAobW9kZS5lbGVjdHJpY0NoYXJzKSB7XHJcbiAgICAgICAgZm9yICh2YXIgaiA9IDA7IGogPCBtb2RlLmVsZWN0cmljQ2hhcnMubGVuZ3RoOyBqKyspXHJcbiAgICAgICAgICBpZiAoaW5zZXJ0ZWQuaW5kZXhPZihtb2RlLmVsZWN0cmljQ2hhcnMuY2hhckF0KGopKSA+IC0xKSB7XHJcbiAgICAgICAgICAgIGluZGVudGVkID0gaW5kZW50TGluZShjbSwgcmFuZ2UuaGVhZC5saW5lLCBcInNtYXJ0XCIpO1xyXG4gICAgICAgICAgICBicmVhaztcclxuICAgICAgICAgIH1cclxuICAgICAgfSBlbHNlIGlmIChtb2RlLmVsZWN0cmljSW5wdXQpIHtcclxuICAgICAgICBpZiAobW9kZS5lbGVjdHJpY0lucHV0LnRlc3QoZ2V0TGluZShjbS5kb2MsIHJhbmdlLmhlYWQubGluZSkudGV4dC5zbGljZSgwLCByYW5nZS5oZWFkLmNoKSkpXHJcbiAgICAgICAgICBpbmRlbnRlZCA9IGluZGVudExpbmUoY20sIHJhbmdlLmhlYWQubGluZSwgXCJzbWFydFwiKTtcclxuICAgICAgfVxyXG4gICAgICBpZiAoaW5kZW50ZWQpIHNpZ25hbExhdGVyKGNtLCBcImVsZWN0cmljSW5wdXRcIiwgY20sIHJhbmdlLmhlYWQubGluZSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBjb3B5YWJsZVJhbmdlcyhjbSkge1xyXG4gICAgdmFyIHRleHQgPSBbXSwgcmFuZ2VzID0gW107XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGNtLmRvYy5zZWwucmFuZ2VzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgIHZhciBsaW5lID0gY20uZG9jLnNlbC5yYW5nZXNbaV0uaGVhZC5saW5lO1xyXG4gICAgICB2YXIgbGluZVJhbmdlID0ge2FuY2hvcjogUG9zKGxpbmUsIDApLCBoZWFkOiBQb3MobGluZSArIDEsIDApfTtcclxuICAgICAgcmFuZ2VzLnB1c2gobGluZVJhbmdlKTtcclxuICAgICAgdGV4dC5wdXNoKGNtLmdldFJhbmdlKGxpbmVSYW5nZS5hbmNob3IsIGxpbmVSYW5nZS5oZWFkKSk7XHJcbiAgICB9XHJcbiAgICByZXR1cm4ge3RleHQ6IHRleHQsIHJhbmdlczogcmFuZ2VzfTtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIGRpc2FibGVCcm93c2VyTWFnaWMoZmllbGQpIHtcclxuICAgIGZpZWxkLnNldEF0dHJpYnV0ZShcImF1dG9jb3JyZWN0XCIsIFwib2ZmXCIpO1xyXG4gICAgZmllbGQuc2V0QXR0cmlidXRlKFwiYXV0b2NhcGl0YWxpemVcIiwgXCJvZmZcIik7XHJcbiAgICBmaWVsZC5zZXRBdHRyaWJ1dGUoXCJzcGVsbGNoZWNrXCIsIFwiZmFsc2VcIik7XHJcbiAgfVxyXG5cclxuICAvLyBURVhUQVJFQSBJTlBVVCBTVFlMRVxyXG5cclxuICBmdW5jdGlvbiBUZXh0YXJlYUlucHV0KGNtKSB7XHJcbiAgICB0aGlzLmNtID0gY207XHJcbiAgICAvLyBTZWUgaW5wdXQucG9sbCBhbmQgaW5wdXQucmVzZXRcclxuICAgIHRoaXMucHJldklucHV0ID0gXCJcIjtcclxuXHJcbiAgICAvLyBGbGFnIHRoYXQgaW5kaWNhdGVzIHdoZXRoZXIgd2UgZXhwZWN0IGlucHV0IHRvIGFwcGVhciByZWFsIHNvb25cclxuICAgIC8vIG5vdyAoYWZ0ZXIgc29tZSBldmVudCBsaWtlICdrZXlwcmVzcycgb3IgJ2lucHV0JykgYW5kIGFyZVxyXG4gICAgLy8gcG9sbGluZyBpbnRlbnNpdmVseS5cclxuICAgIHRoaXMucG9sbGluZ0Zhc3QgPSBmYWxzZTtcclxuICAgIC8vIFNlbGYtcmVzZXR0aW5nIHRpbWVvdXQgZm9yIHRoZSBwb2xsZXJcclxuICAgIHRoaXMucG9sbGluZyA9IG5ldyBEZWxheWVkKCk7XHJcbiAgICAvLyBUcmFja3Mgd2hlbiBpbnB1dC5yZXNldCBoYXMgcHVudGVkIHRvIGp1c3QgcHV0dGluZyBhIHNob3J0XHJcbiAgICAvLyBzdHJpbmcgaW50byB0aGUgdGV4dGFyZWEgaW5zdGVhZCBvZiB0aGUgZnVsbCBzZWxlY3Rpb24uXHJcbiAgICB0aGlzLmluYWNjdXJhdGVTZWxlY3Rpb24gPSBmYWxzZTtcclxuICAgIC8vIFVzZWQgdG8gd29yayBhcm91bmQgSUUgaXNzdWUgd2l0aCBzZWxlY3Rpb24gYmVpbmcgZm9yZ290dGVuIHdoZW4gZm9jdXMgbW92ZXMgYXdheSBmcm9tIHRleHRhcmVhXHJcbiAgICB0aGlzLmhhc1NlbGVjdGlvbiA9IGZhbHNlO1xyXG4gICAgdGhpcy5jb21wb3NpbmcgPSBudWxsO1xyXG4gIH07XHJcblxyXG4gIGZ1bmN0aW9uIGhpZGRlblRleHRhcmVhKCkge1xyXG4gICAgdmFyIHRlID0gZWx0KFwidGV4dGFyZWFcIiwgbnVsbCwgbnVsbCwgXCJwb3NpdGlvbjogYWJzb2x1dGU7IHBhZGRpbmc6IDA7IHdpZHRoOiAxcHg7IGhlaWdodDogMWVtOyBvdXRsaW5lOiBub25lXCIpO1xyXG4gICAgdmFyIGRpdiA9IGVsdChcImRpdlwiLCBbdGVdLCBudWxsLCBcIm92ZXJmbG93OiBoaWRkZW47IHBvc2l0aW9uOiByZWxhdGl2ZTsgd2lkdGg6IDNweDsgaGVpZ2h0OiAwcHg7XCIpO1xyXG4gICAgLy8gVGhlIHRleHRhcmVhIGlzIGtlcHQgcG9zaXRpb25lZCBuZWFyIHRoZSBjdXJzb3IgdG8gcHJldmVudCB0aGVcclxuICAgIC8vIGZhY3QgdGhhdCBpdCdsbCBiZSBzY3JvbGxlZCBpbnRvIHZpZXcgb24gaW5wdXQgZnJvbSBzY3JvbGxpbmdcclxuICAgIC8vIG91ciBmYWtlIGN1cnNvciBvdXQgb2Ygdmlldy4gT24gd2Via2l0LCB3aGVuIHdyYXA9b2ZmLCBwYXN0ZSBpc1xyXG4gICAgLy8gdmVyeSBzbG93LiBTbyBtYWtlIHRoZSBhcmVhIHdpZGUgaW5zdGVhZC5cclxuICAgIGlmICh3ZWJraXQpIHRlLnN0eWxlLndpZHRoID0gXCIxMDAwcHhcIjtcclxuICAgIGVsc2UgdGUuc2V0QXR0cmlidXRlKFwid3JhcFwiLCBcIm9mZlwiKTtcclxuICAgIC8vIElmIGJvcmRlcjogMDsgLS0gaU9TIGZhaWxzIHRvIG9wZW4ga2V5Ym9hcmQgKGlzc3VlICMxMjg3KVxyXG4gICAgaWYgKGlvcykgdGUuc3R5bGUuYm9yZGVyID0gXCIxcHggc29saWQgYmxhY2tcIjtcclxuICAgIGRpc2FibGVCcm93c2VyTWFnaWModGUpO1xyXG4gICAgcmV0dXJuIGRpdjtcclxuICB9XHJcblxyXG4gIFRleHRhcmVhSW5wdXQucHJvdG90eXBlID0gY29weU9iaih7XHJcbiAgICBpbml0OiBmdW5jdGlvbihkaXNwbGF5KSB7XHJcbiAgICAgIHZhciBpbnB1dCA9IHRoaXMsIGNtID0gdGhpcy5jbTtcclxuXHJcbiAgICAgIC8vIFdyYXBzIGFuZCBoaWRlcyBpbnB1dCB0ZXh0YXJlYVxyXG4gICAgICB2YXIgZGl2ID0gdGhpcy53cmFwcGVyID0gaGlkZGVuVGV4dGFyZWEoKTtcclxuICAgICAgLy8gVGhlIHNlbWloaWRkZW4gdGV4dGFyZWEgdGhhdCBpcyBmb2N1c2VkIHdoZW4gdGhlIGVkaXRvciBpc1xyXG4gICAgICAvLyBmb2N1c2VkLCBhbmQgcmVjZWl2ZXMgaW5wdXQuXHJcbiAgICAgIHZhciB0ZSA9IHRoaXMudGV4dGFyZWEgPSBkaXYuZmlyc3RDaGlsZDtcclxuICAgICAgZGlzcGxheS53cmFwcGVyLmluc2VydEJlZm9yZShkaXYsIGRpc3BsYXkud3JhcHBlci5maXJzdENoaWxkKTtcclxuXHJcbiAgICAgIC8vIE5lZWRlZCB0byBoaWRlIGJpZyBibHVlIGJsaW5raW5nIGN1cnNvciBvbiBNb2JpbGUgU2FmYXJpIChkb2Vzbid0IHNlZW0gdG8gd29yayBpbiBpT1MgOCBhbnltb3JlKVxyXG4gICAgICBpZiAoaW9zKSB0ZS5zdHlsZS53aWR0aCA9IFwiMHB4XCI7XHJcblxyXG4gICAgICBvbih0ZSwgXCJpbnB1dFwiLCBmdW5jdGlvbigpIHtcclxuICAgICAgICBpZiAoaWUgJiYgaWVfdmVyc2lvbiA+PSA5ICYmIGlucHV0Lmhhc1NlbGVjdGlvbikgaW5wdXQuaGFzU2VsZWN0aW9uID0gbnVsbDtcclxuICAgICAgICBpbnB1dC5wb2xsKCk7XHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgb24odGUsIFwicGFzdGVcIiwgZnVuY3Rpb24oZSkge1xyXG4gICAgICAgIGlmIChoYW5kbGVQYXN0ZShlLCBjbSkpIHJldHVybiB0cnVlO1xyXG5cclxuICAgICAgICBjbS5zdGF0ZS5wYXN0ZUluY29taW5nID0gdHJ1ZTtcclxuICAgICAgICBpbnB1dC5mYXN0UG9sbCgpO1xyXG4gICAgICB9KTtcclxuXHJcbiAgICAgIGZ1bmN0aW9uIHByZXBhcmVDb3B5Q3V0KGUpIHtcclxuICAgICAgICBpZiAoY20uc29tZXRoaW5nU2VsZWN0ZWQoKSkge1xyXG4gICAgICAgICAgbGFzdENvcGllZCA9IGNtLmdldFNlbGVjdGlvbnMoKTtcclxuICAgICAgICAgIGlmIChpbnB1dC5pbmFjY3VyYXRlU2VsZWN0aW9uKSB7XHJcbiAgICAgICAgICAgIGlucHV0LnByZXZJbnB1dCA9IFwiXCI7XHJcbiAgICAgICAgICAgIGlucHV0LmluYWNjdXJhdGVTZWxlY3Rpb24gPSBmYWxzZTtcclxuICAgICAgICAgICAgdGUudmFsdWUgPSBsYXN0Q29waWVkLmpvaW4oXCJcXG5cIik7XHJcbiAgICAgICAgICAgIHNlbGVjdElucHV0KHRlKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9IGVsc2UgaWYgKCFjbS5vcHRpb25zLmxpbmVXaXNlQ29weUN1dCkge1xyXG4gICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICB2YXIgcmFuZ2VzID0gY29weWFibGVSYW5nZXMoY20pO1xyXG4gICAgICAgICAgbGFzdENvcGllZCA9IHJhbmdlcy50ZXh0O1xyXG4gICAgICAgICAgaWYgKGUudHlwZSA9PSBcImN1dFwiKSB7XHJcbiAgICAgICAgICAgIGNtLnNldFNlbGVjdGlvbnMocmFuZ2VzLnJhbmdlcywgbnVsbCwgc2VsX2RvbnRTY3JvbGwpO1xyXG4gICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgaW5wdXQucHJldklucHV0ID0gXCJcIjtcclxuICAgICAgICAgICAgdGUudmFsdWUgPSByYW5nZXMudGV4dC5qb2luKFwiXFxuXCIpO1xyXG4gICAgICAgICAgICBzZWxlY3RJbnB1dCh0ZSk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmIChlLnR5cGUgPT0gXCJjdXRcIikgY20uc3RhdGUuY3V0SW5jb21pbmcgPSB0cnVlO1xyXG4gICAgICB9XHJcbiAgICAgIG9uKHRlLCBcImN1dFwiLCBwcmVwYXJlQ29weUN1dCk7XHJcbiAgICAgIG9uKHRlLCBcImNvcHlcIiwgcHJlcGFyZUNvcHlDdXQpO1xyXG5cclxuICAgICAgb24oZGlzcGxheS5zY3JvbGxlciwgXCJwYXN0ZVwiLCBmdW5jdGlvbihlKSB7XHJcbiAgICAgICAgaWYgKGV2ZW50SW5XaWRnZXQoZGlzcGxheSwgZSkpIHJldHVybjtcclxuICAgICAgICBjbS5zdGF0ZS5wYXN0ZUluY29taW5nID0gdHJ1ZTtcclxuICAgICAgICBpbnB1dC5mb2N1cygpO1xyXG4gICAgICB9KTtcclxuXHJcbiAgICAgIC8vIFByZXZlbnQgbm9ybWFsIHNlbGVjdGlvbiBpbiB0aGUgZWRpdG9yICh3ZSBoYW5kbGUgb3VyIG93bilcclxuICAgICAgb24oZGlzcGxheS5saW5lU3BhY2UsIFwic2VsZWN0c3RhcnRcIiwgZnVuY3Rpb24oZSkge1xyXG4gICAgICAgIGlmICghZXZlbnRJbldpZGdldChkaXNwbGF5LCBlKSkgZV9wcmV2ZW50RGVmYXVsdChlKTtcclxuICAgICAgfSk7XHJcblxyXG4gICAgICBvbih0ZSwgXCJjb21wb3NpdGlvbnN0YXJ0XCIsIGZ1bmN0aW9uKCkge1xyXG4gICAgICAgIHZhciBzdGFydCA9IGNtLmdldEN1cnNvcihcImZyb21cIik7XHJcbiAgICAgICAgaWYgKGlucHV0LmNvbXBvc2luZykgaW5wdXQuY29tcG9zaW5nLnJhbmdlLmNsZWFyKClcclxuICAgICAgICBpbnB1dC5jb21wb3NpbmcgPSB7XHJcbiAgICAgICAgICBzdGFydDogc3RhcnQsXHJcbiAgICAgICAgICByYW5nZTogY20ubWFya1RleHQoc3RhcnQsIGNtLmdldEN1cnNvcihcInRvXCIpLCB7Y2xhc3NOYW1lOiBcIkNvZGVNaXJyb3ItY29tcG9zaW5nXCJ9KVxyXG4gICAgICAgIH07XHJcbiAgICAgIH0pO1xyXG4gICAgICBvbih0ZSwgXCJjb21wb3NpdGlvbmVuZFwiLCBmdW5jdGlvbigpIHtcclxuICAgICAgICBpZiAoaW5wdXQuY29tcG9zaW5nKSB7XHJcbiAgICAgICAgICBpbnB1dC5wb2xsKCk7XHJcbiAgICAgICAgICBpbnB1dC5jb21wb3NpbmcucmFuZ2UuY2xlYXIoKTtcclxuICAgICAgICAgIGlucHV0LmNvbXBvc2luZyA9IG51bGw7XHJcbiAgICAgICAgfVxyXG4gICAgICB9KTtcclxuICAgIH0sXHJcblxyXG4gICAgcHJlcGFyZVNlbGVjdGlvbjogZnVuY3Rpb24oKSB7XHJcbiAgICAgIC8vIFJlZHJhdyB0aGUgc2VsZWN0aW9uIGFuZC9vciBjdXJzb3JcclxuICAgICAgdmFyIGNtID0gdGhpcy5jbSwgZGlzcGxheSA9IGNtLmRpc3BsYXksIGRvYyA9IGNtLmRvYztcclxuICAgICAgdmFyIHJlc3VsdCA9IHByZXBhcmVTZWxlY3Rpb24oY20pO1xyXG5cclxuICAgICAgLy8gTW92ZSB0aGUgaGlkZGVuIHRleHRhcmVhIG5lYXIgdGhlIGN1cnNvciB0byBwcmV2ZW50IHNjcm9sbGluZyBhcnRpZmFjdHNcclxuICAgICAgaWYgKGNtLm9wdGlvbnMubW92ZUlucHV0V2l0aEN1cnNvcikge1xyXG4gICAgICAgIHZhciBoZWFkUG9zID0gY3Vyc29yQ29vcmRzKGNtLCBkb2Muc2VsLnByaW1hcnkoKS5oZWFkLCBcImRpdlwiKTtcclxuICAgICAgICB2YXIgd3JhcE9mZiA9IGRpc3BsYXkud3JhcHBlci5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKSwgbGluZU9mZiA9IGRpc3BsYXkubGluZURpdi5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcclxuICAgICAgICByZXN1bHQudGVUb3AgPSBNYXRoLm1heCgwLCBNYXRoLm1pbihkaXNwbGF5LndyYXBwZXIuY2xpZW50SGVpZ2h0IC0gMTAsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhZFBvcy50b3AgKyBsaW5lT2ZmLnRvcCAtIHdyYXBPZmYudG9wKSk7XHJcbiAgICAgICAgcmVzdWx0LnRlTGVmdCA9IE1hdGgubWF4KDAsIE1hdGgubWluKGRpc3BsYXkud3JhcHBlci5jbGllbnRXaWR0aCAtIDEwLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkUG9zLmxlZnQgKyBsaW5lT2ZmLmxlZnQgLSB3cmFwT2ZmLmxlZnQpKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgcmV0dXJuIHJlc3VsdDtcclxuICAgIH0sXHJcblxyXG4gICAgc2hvd1NlbGVjdGlvbjogZnVuY3Rpb24oZHJhd24pIHtcclxuICAgICAgdmFyIGNtID0gdGhpcy5jbSwgZGlzcGxheSA9IGNtLmRpc3BsYXk7XHJcbiAgICAgIHJlbW92ZUNoaWxkcmVuQW5kQWRkKGRpc3BsYXkuY3Vyc29yRGl2LCBkcmF3bi5jdXJzb3JzKTtcclxuICAgICAgcmVtb3ZlQ2hpbGRyZW5BbmRBZGQoZGlzcGxheS5zZWxlY3Rpb25EaXYsIGRyYXduLnNlbGVjdGlvbik7XHJcbiAgICAgIGlmIChkcmF3bi50ZVRvcCAhPSBudWxsKSB7XHJcbiAgICAgICAgdGhpcy53cmFwcGVyLnN0eWxlLnRvcCA9IGRyYXduLnRlVG9wICsgXCJweFwiO1xyXG4gICAgICAgIHRoaXMud3JhcHBlci5zdHlsZS5sZWZ0ID0gZHJhd24udGVMZWZ0ICsgXCJweFwiO1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG5cclxuICAgIC8vIFJlc2V0IHRoZSBpbnB1dCB0byBjb3JyZXNwb25kIHRvIHRoZSBzZWxlY3Rpb24gKG9yIHRvIGJlIGVtcHR5LFxyXG4gICAgLy8gd2hlbiBub3QgdHlwaW5nIGFuZCBub3RoaW5nIGlzIHNlbGVjdGVkKVxyXG4gICAgcmVzZXQ6IGZ1bmN0aW9uKHR5cGluZykge1xyXG4gICAgICBpZiAodGhpcy5jb250ZXh0TWVudVBlbmRpbmcpIHJldHVybjtcclxuICAgICAgdmFyIG1pbmltYWwsIHNlbGVjdGVkLCBjbSA9IHRoaXMuY20sIGRvYyA9IGNtLmRvYztcclxuICAgICAgaWYgKGNtLnNvbWV0aGluZ1NlbGVjdGVkKCkpIHtcclxuICAgICAgICB0aGlzLnByZXZJbnB1dCA9IFwiXCI7XHJcbiAgICAgICAgdmFyIHJhbmdlID0gZG9jLnNlbC5wcmltYXJ5KCk7XHJcbiAgICAgICAgbWluaW1hbCA9IGhhc0NvcHlFdmVudCAmJlxyXG4gICAgICAgICAgKHJhbmdlLnRvKCkubGluZSAtIHJhbmdlLmZyb20oKS5saW5lID4gMTAwIHx8IChzZWxlY3RlZCA9IGNtLmdldFNlbGVjdGlvbigpKS5sZW5ndGggPiAxMDAwKTtcclxuICAgICAgICB2YXIgY29udGVudCA9IG1pbmltYWwgPyBcIi1cIiA6IHNlbGVjdGVkIHx8IGNtLmdldFNlbGVjdGlvbigpO1xyXG4gICAgICAgIHRoaXMudGV4dGFyZWEudmFsdWUgPSBjb250ZW50O1xyXG4gICAgICAgIGlmIChjbS5zdGF0ZS5mb2N1c2VkKSBzZWxlY3RJbnB1dCh0aGlzLnRleHRhcmVhKTtcclxuICAgICAgICBpZiAoaWUgJiYgaWVfdmVyc2lvbiA+PSA5KSB0aGlzLmhhc1NlbGVjdGlvbiA9IGNvbnRlbnQ7XHJcbiAgICAgIH0gZWxzZSBpZiAoIXR5cGluZykge1xyXG4gICAgICAgIHRoaXMucHJldklucHV0ID0gdGhpcy50ZXh0YXJlYS52YWx1ZSA9IFwiXCI7XHJcbiAgICAgICAgaWYgKGllICYmIGllX3ZlcnNpb24gPj0gOSkgdGhpcy5oYXNTZWxlY3Rpb24gPSBudWxsO1xyXG4gICAgICB9XHJcbiAgICAgIHRoaXMuaW5hY2N1cmF0ZVNlbGVjdGlvbiA9IG1pbmltYWw7XHJcbiAgICB9LFxyXG5cclxuICAgIGdldEZpZWxkOiBmdW5jdGlvbigpIHsgcmV0dXJuIHRoaXMudGV4dGFyZWE7IH0sXHJcblxyXG4gICAgc3VwcG9ydHNUb3VjaDogZnVuY3Rpb24oKSB7IHJldHVybiBmYWxzZTsgfSxcclxuXHJcbiAgICBmb2N1czogZnVuY3Rpb24oKSB7XHJcbiAgICAgIGlmICh0aGlzLmNtLm9wdGlvbnMucmVhZE9ubHkgIT0gXCJub2N1cnNvclwiICYmICghbW9iaWxlIHx8IGFjdGl2ZUVsdCgpICE9IHRoaXMudGV4dGFyZWEpKSB7XHJcbiAgICAgICAgdHJ5IHsgdGhpcy50ZXh0YXJlYS5mb2N1cygpOyB9XHJcbiAgICAgICAgY2F0Y2ggKGUpIHt9IC8vIElFOCB3aWxsIHRocm93IGlmIHRoZSB0ZXh0YXJlYSBpcyBkaXNwbGF5OiBub25lIG9yIG5vdCBpbiBET01cclxuICAgICAgfVxyXG4gICAgfSxcclxuXHJcbiAgICBibHVyOiBmdW5jdGlvbigpIHsgdGhpcy50ZXh0YXJlYS5ibHVyKCk7IH0sXHJcblxyXG4gICAgcmVzZXRQb3NpdGlvbjogZnVuY3Rpb24oKSB7XHJcbiAgICAgIHRoaXMud3JhcHBlci5zdHlsZS50b3AgPSB0aGlzLndyYXBwZXIuc3R5bGUubGVmdCA9IDA7XHJcbiAgICB9LFxyXG5cclxuICAgIHJlY2VpdmVkRm9jdXM6IGZ1bmN0aW9uKCkgeyB0aGlzLnNsb3dQb2xsKCk7IH0sXHJcblxyXG4gICAgLy8gUG9sbCBmb3IgaW5wdXQgY2hhbmdlcywgdXNpbmcgdGhlIG5vcm1hbCByYXRlIG9mIHBvbGxpbmcuIFRoaXNcclxuICAgIC8vIHJ1bnMgYXMgbG9uZyBhcyB0aGUgZWRpdG9yIGlzIGZvY3VzZWQuXHJcbiAgICBzbG93UG9sbDogZnVuY3Rpb24oKSB7XHJcbiAgICAgIHZhciBpbnB1dCA9IHRoaXM7XHJcbiAgICAgIGlmIChpbnB1dC5wb2xsaW5nRmFzdCkgcmV0dXJuO1xyXG4gICAgICBpbnB1dC5wb2xsaW5nLnNldCh0aGlzLmNtLm9wdGlvbnMucG9sbEludGVydmFsLCBmdW5jdGlvbigpIHtcclxuICAgICAgICBpbnB1dC5wb2xsKCk7XHJcbiAgICAgICAgaWYgKGlucHV0LmNtLnN0YXRlLmZvY3VzZWQpIGlucHV0LnNsb3dQb2xsKCk7XHJcbiAgICAgIH0pO1xyXG4gICAgfSxcclxuXHJcbiAgICAvLyBXaGVuIGFuIGV2ZW50IGhhcyBqdXN0IGNvbWUgaW4gdGhhdCBpcyBsaWtlbHkgdG8gYWRkIG9yIGNoYW5nZVxyXG4gICAgLy8gc29tZXRoaW5nIGluIHRoZSBpbnB1dCB0ZXh0YXJlYSwgd2UgcG9sbCBmYXN0ZXIsIHRvIGVuc3VyZSB0aGF0XHJcbiAgICAvLyB0aGUgY2hhbmdlIGFwcGVhcnMgb24gdGhlIHNjcmVlbiBxdWlja2x5LlxyXG4gICAgZmFzdFBvbGw6IGZ1bmN0aW9uKCkge1xyXG4gICAgICB2YXIgbWlzc2VkID0gZmFsc2UsIGlucHV0ID0gdGhpcztcclxuICAgICAgaW5wdXQucG9sbGluZ0Zhc3QgPSB0cnVlO1xyXG4gICAgICBmdW5jdGlvbiBwKCkge1xyXG4gICAgICAgIHZhciBjaGFuZ2VkID0gaW5wdXQucG9sbCgpO1xyXG4gICAgICAgIGlmICghY2hhbmdlZCAmJiAhbWlzc2VkKSB7bWlzc2VkID0gdHJ1ZTsgaW5wdXQucG9sbGluZy5zZXQoNjAsIHApO31cclxuICAgICAgICBlbHNlIHtpbnB1dC5wb2xsaW5nRmFzdCA9IGZhbHNlOyBpbnB1dC5zbG93UG9sbCgpO31cclxuICAgICAgfVxyXG4gICAgICBpbnB1dC5wb2xsaW5nLnNldCgyMCwgcCk7XHJcbiAgICB9LFxyXG5cclxuICAgIC8vIFJlYWQgaW5wdXQgZnJvbSB0aGUgdGV4dGFyZWEsIGFuZCB1cGRhdGUgdGhlIGRvY3VtZW50IHRvIG1hdGNoLlxyXG4gICAgLy8gV2hlbiBzb21ldGhpbmcgaXMgc2VsZWN0ZWQsIGl0IGlzIHByZXNlbnQgaW4gdGhlIHRleHRhcmVhLCBhbmRcclxuICAgIC8vIHNlbGVjdGVkICh1bmxlc3MgaXQgaXMgaHVnZSwgaW4gd2hpY2ggY2FzZSBhIHBsYWNlaG9sZGVyIGlzXHJcbiAgICAvLyB1c2VkKS4gV2hlbiBub3RoaW5nIGlzIHNlbGVjdGVkLCB0aGUgY3Vyc29yIHNpdHMgYWZ0ZXIgcHJldmlvdXNseVxyXG4gICAgLy8gc2VlbiB0ZXh0IChjYW4gYmUgZW1wdHkpLCB3aGljaCBpcyBzdG9yZWQgaW4gcHJldklucHV0ICh3ZSBtdXN0XHJcbiAgICAvLyBub3QgcmVzZXQgdGhlIHRleHRhcmVhIHdoZW4gdHlwaW5nLCBiZWNhdXNlIHRoYXQgYnJlYWtzIElNRSkuXHJcbiAgICBwb2xsOiBmdW5jdGlvbigpIHtcclxuICAgICAgdmFyIGNtID0gdGhpcy5jbSwgaW5wdXQgPSB0aGlzLnRleHRhcmVhLCBwcmV2SW5wdXQgPSB0aGlzLnByZXZJbnB1dDtcclxuICAgICAgLy8gU2luY2UgdGhpcyBpcyBjYWxsZWQgYSAqbG90KiwgdHJ5IHRvIGJhaWwgb3V0IGFzIGNoZWFwbHkgYXNcclxuICAgICAgLy8gcG9zc2libGUgd2hlbiBpdCBpcyBjbGVhciB0aGF0IG5vdGhpbmcgaGFwcGVuZWQuIGhhc1NlbGVjdGlvblxyXG4gICAgICAvLyB3aWxsIGJlIHRoZSBjYXNlIHdoZW4gdGhlcmUgaXMgYSBsb3Qgb2YgdGV4dCBpbiB0aGUgdGV4dGFyZWEsXHJcbiAgICAgIC8vIGluIHdoaWNoIGNhc2UgcmVhZGluZyBpdHMgdmFsdWUgd291bGQgYmUgZXhwZW5zaXZlLlxyXG4gICAgICBpZiAodGhpcy5jb250ZXh0TWVudVBlbmRpbmcgfHwgIWNtLnN0YXRlLmZvY3VzZWQgfHxcclxuICAgICAgICAgIChoYXNTZWxlY3Rpb24oaW5wdXQpICYmICFwcmV2SW5wdXQgJiYgIXRoaXMuY29tcG9zaW5nKSB8fFxyXG4gICAgICAgICAgaXNSZWFkT25seShjbSkgfHwgY20ub3B0aW9ucy5kaXNhYmxlSW5wdXQgfHwgY20uc3RhdGUua2V5U2VxKVxyXG4gICAgICAgIHJldHVybiBmYWxzZTtcclxuXHJcbiAgICAgIHZhciB0ZXh0ID0gaW5wdXQudmFsdWU7XHJcbiAgICAgIC8vIElmIG5vdGhpbmcgY2hhbmdlZCwgYmFpbC5cclxuICAgICAgaWYgKHRleHQgPT0gcHJldklucHV0ICYmICFjbS5zb21ldGhpbmdTZWxlY3RlZCgpKSByZXR1cm4gZmFsc2U7XHJcbiAgICAgIC8vIFdvcmsgYXJvdW5kIG5vbnNlbnNpY2FsIHNlbGVjdGlvbiByZXNldHRpbmcgaW4gSUU5LzEwLCBhbmRcclxuICAgICAgLy8gaW5leHBsaWNhYmxlIGFwcGVhcmFuY2Ugb2YgcHJpdmF0ZSBhcmVhIHVuaWNvZGUgY2hhcmFjdGVycyBvblxyXG4gICAgICAvLyBzb21lIGtleSBjb21ib3MgaW4gTWFjICgjMjY4OSkuXHJcbiAgICAgIGlmIChpZSAmJiBpZV92ZXJzaW9uID49IDkgJiYgdGhpcy5oYXNTZWxlY3Rpb24gPT09IHRleHQgfHxcclxuICAgICAgICAgIG1hYyAmJiAvW1xcdWY3MDAtXFx1ZjdmZl0vLnRlc3QodGV4dCkpIHtcclxuICAgICAgICBjbS5kaXNwbGF5LmlucHV0LnJlc2V0KCk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBpZiAoY20uZG9jLnNlbCA9PSBjbS5kaXNwbGF5LnNlbEZvckNvbnRleHRNZW51KSB7XHJcbiAgICAgICAgdmFyIGZpcnN0ID0gdGV4dC5jaGFyQ29kZUF0KDApO1xyXG4gICAgICAgIGlmIChmaXJzdCA9PSAweDIwMGIgJiYgIXByZXZJbnB1dCkgcHJldklucHV0ID0gXCJcXHUyMDBiXCI7XHJcbiAgICAgICAgaWYgKGZpcnN0ID09IDB4MjFkYSkgeyB0aGlzLnJlc2V0KCk7IHJldHVybiB0aGlzLmNtLmV4ZWNDb21tYW5kKFwidW5kb1wiKTsgfVxyXG4gICAgICB9XHJcbiAgICAgIC8vIEZpbmQgdGhlIHBhcnQgb2YgdGhlIGlucHV0IHRoYXQgaXMgYWN0dWFsbHkgbmV3XHJcbiAgICAgIHZhciBzYW1lID0gMCwgbCA9IE1hdGgubWluKHByZXZJbnB1dC5sZW5ndGgsIHRleHQubGVuZ3RoKTtcclxuICAgICAgd2hpbGUgKHNhbWUgPCBsICYmIHByZXZJbnB1dC5jaGFyQ29kZUF0KHNhbWUpID09IHRleHQuY2hhckNvZGVBdChzYW1lKSkgKytzYW1lO1xyXG5cclxuICAgICAgdmFyIHNlbGYgPSB0aGlzO1xyXG4gICAgICBydW5Jbk9wKGNtLCBmdW5jdGlvbigpIHtcclxuICAgICAgICBhcHBseVRleHRJbnB1dChjbSwgdGV4dC5zbGljZShzYW1lKSwgcHJldklucHV0Lmxlbmd0aCAtIHNhbWUsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgbnVsbCwgc2VsZi5jb21wb3NpbmcgPyBcIipjb21wb3NlXCIgOiBudWxsKTtcclxuXHJcbiAgICAgICAgLy8gRG9uJ3QgbGVhdmUgbG9uZyB0ZXh0IGluIHRoZSB0ZXh0YXJlYSwgc2luY2UgaXQgbWFrZXMgZnVydGhlciBwb2xsaW5nIHNsb3dcclxuICAgICAgICBpZiAodGV4dC5sZW5ndGggPiAxMDAwIHx8IHRleHQuaW5kZXhPZihcIlxcblwiKSA+IC0xKSBpbnB1dC52YWx1ZSA9IHNlbGYucHJldklucHV0ID0gXCJcIjtcclxuICAgICAgICBlbHNlIHNlbGYucHJldklucHV0ID0gdGV4dDtcclxuXHJcbiAgICAgICAgaWYgKHNlbGYuY29tcG9zaW5nKSB7XHJcbiAgICAgICAgICBzZWxmLmNvbXBvc2luZy5yYW5nZS5jbGVhcigpO1xyXG4gICAgICAgICAgc2VsZi5jb21wb3NpbmcucmFuZ2UgPSBjbS5tYXJrVGV4dChzZWxmLmNvbXBvc2luZy5zdGFydCwgY20uZ2V0Q3Vyc29yKFwidG9cIiksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtjbGFzc05hbWU6IFwiQ29kZU1pcnJvci1jb21wb3NpbmdcIn0pO1xyXG4gICAgICAgIH1cclxuICAgICAgfSk7XHJcbiAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfSxcclxuXHJcbiAgICBlbnN1cmVQb2xsZWQ6IGZ1bmN0aW9uKCkge1xyXG4gICAgICBpZiAodGhpcy5wb2xsaW5nRmFzdCAmJiB0aGlzLnBvbGwoKSkgdGhpcy5wb2xsaW5nRmFzdCA9IGZhbHNlO1xyXG4gICAgfSxcclxuXHJcbiAgICBvbktleVByZXNzOiBmdW5jdGlvbigpIHtcclxuICAgICAgaWYgKGllICYmIGllX3ZlcnNpb24gPj0gOSkgdGhpcy5oYXNTZWxlY3Rpb24gPSBudWxsO1xyXG4gICAgICB0aGlzLmZhc3RQb2xsKCk7XHJcbiAgICB9LFxyXG5cclxuICAgIG9uQ29udGV4dE1lbnU6IGZ1bmN0aW9uKGUpIHtcclxuICAgICAgdmFyIGlucHV0ID0gdGhpcywgY20gPSBpbnB1dC5jbSwgZGlzcGxheSA9IGNtLmRpc3BsYXksIHRlID0gaW5wdXQudGV4dGFyZWE7XHJcbiAgICAgIHZhciBwb3MgPSBwb3NGcm9tTW91c2UoY20sIGUpLCBzY3JvbGxQb3MgPSBkaXNwbGF5LnNjcm9sbGVyLnNjcm9sbFRvcDtcclxuICAgICAgaWYgKCFwb3MgfHwgcHJlc3RvKSByZXR1cm47IC8vIE9wZXJhIGlzIGRpZmZpY3VsdC5cclxuXHJcbiAgICAgIC8vIFJlc2V0IHRoZSBjdXJyZW50IHRleHQgc2VsZWN0aW9uIG9ubHkgaWYgdGhlIGNsaWNrIGlzIGRvbmUgb3V0c2lkZSBvZiB0aGUgc2VsZWN0aW9uXHJcbiAgICAgIC8vIGFuZCAncmVzZXRTZWxlY3Rpb25PbkNvbnRleHRNZW51JyBvcHRpb24gaXMgdHJ1ZS5cclxuICAgICAgdmFyIHJlc2V0ID0gY20ub3B0aW9ucy5yZXNldFNlbGVjdGlvbk9uQ29udGV4dE1lbnU7XHJcbiAgICAgIGlmIChyZXNldCAmJiBjbS5kb2Muc2VsLmNvbnRhaW5zKHBvcykgPT0gLTEpXHJcbiAgICAgICAgb3BlcmF0aW9uKGNtLCBzZXRTZWxlY3Rpb24pKGNtLmRvYywgc2ltcGxlU2VsZWN0aW9uKHBvcyksIHNlbF9kb250U2Nyb2xsKTtcclxuXHJcbiAgICAgIHZhciBvbGRDU1MgPSB0ZS5zdHlsZS5jc3NUZXh0O1xyXG4gICAgICBpbnB1dC53cmFwcGVyLnN0eWxlLnBvc2l0aW9uID0gXCJhYnNvbHV0ZVwiO1xyXG4gICAgICB0ZS5zdHlsZS5jc3NUZXh0ID0gXCJwb3NpdGlvbjogZml4ZWQ7IHdpZHRoOiAzMHB4OyBoZWlnaHQ6IDMwcHg7IHRvcDogXCIgKyAoZS5jbGllbnRZIC0gNSkgK1xyXG4gICAgICAgIFwicHg7IGxlZnQ6IFwiICsgKGUuY2xpZW50WCAtIDUpICsgXCJweDsgei1pbmRleDogMTAwMDsgYmFja2dyb3VuZDogXCIgK1xyXG4gICAgICAgIChpZSA/IFwicmdiYSgyNTUsIDI1NSwgMjU1LCAuMDUpXCIgOiBcInRyYW5zcGFyZW50XCIpICtcclxuICAgICAgICBcIjsgb3V0bGluZTogbm9uZTsgYm9yZGVyLXdpZHRoOiAwOyBvdXRsaW5lOiBub25lOyBvdmVyZmxvdzogaGlkZGVuOyBvcGFjaXR5OiAuMDU7IGZpbHRlcjogYWxwaGEob3BhY2l0eT01KTtcIjtcclxuICAgICAgaWYgKHdlYmtpdCkgdmFyIG9sZFNjcm9sbFkgPSB3aW5kb3cuc2Nyb2xsWTsgLy8gV29yayBhcm91bmQgQ2hyb21lIGlzc3VlICgjMjcxMilcclxuICAgICAgZGlzcGxheS5pbnB1dC5mb2N1cygpO1xyXG4gICAgICBpZiAod2Via2l0KSB3aW5kb3cuc2Nyb2xsVG8obnVsbCwgb2xkU2Nyb2xsWSk7XHJcbiAgICAgIGRpc3BsYXkuaW5wdXQucmVzZXQoKTtcclxuICAgICAgLy8gQWRkcyBcIlNlbGVjdCBhbGxcIiB0byBjb250ZXh0IG1lbnUgaW4gRkZcclxuICAgICAgaWYgKCFjbS5zb21ldGhpbmdTZWxlY3RlZCgpKSB0ZS52YWx1ZSA9IGlucHV0LnByZXZJbnB1dCA9IFwiIFwiO1xyXG4gICAgICBpbnB1dC5jb250ZXh0TWVudVBlbmRpbmcgPSB0cnVlO1xyXG4gICAgICBkaXNwbGF5LnNlbEZvckNvbnRleHRNZW51ID0gY20uZG9jLnNlbDtcclxuICAgICAgY2xlYXJUaW1lb3V0KGRpc3BsYXkuZGV0ZWN0aW5nU2VsZWN0QWxsKTtcclxuXHJcbiAgICAgIC8vIFNlbGVjdC1hbGwgd2lsbCBiZSBncmV5ZWQgb3V0IGlmIHRoZXJlJ3Mgbm90aGluZyB0byBzZWxlY3QsIHNvXHJcbiAgICAgIC8vIHRoaXMgYWRkcyBhIHplcm8td2lkdGggc3BhY2Ugc28gdGhhdCB3ZSBjYW4gbGF0ZXIgY2hlY2sgd2hldGhlclxyXG4gICAgICAvLyBpdCBnb3Qgc2VsZWN0ZWQuXHJcbiAgICAgIGZ1bmN0aW9uIHByZXBhcmVTZWxlY3RBbGxIYWNrKCkge1xyXG4gICAgICAgIGlmICh0ZS5zZWxlY3Rpb25TdGFydCAhPSBudWxsKSB7XHJcbiAgICAgICAgICB2YXIgc2VsZWN0ZWQgPSBjbS5zb21ldGhpbmdTZWxlY3RlZCgpO1xyXG4gICAgICAgICAgdmFyIGV4dHZhbCA9IFwiXFx1MjAwYlwiICsgKHNlbGVjdGVkID8gdGUudmFsdWUgOiBcIlwiKTtcclxuICAgICAgICAgIHRlLnZhbHVlID0gXCJcXHUyMWRhXCI7IC8vIFVzZWQgdG8gY2F0Y2ggY29udGV4dC1tZW51IHVuZG9cclxuICAgICAgICAgIHRlLnZhbHVlID0gZXh0dmFsO1xyXG4gICAgICAgICAgaW5wdXQucHJldklucHV0ID0gc2VsZWN0ZWQgPyBcIlwiIDogXCJcXHUyMDBiXCI7XHJcbiAgICAgICAgICB0ZS5zZWxlY3Rpb25TdGFydCA9IDE7IHRlLnNlbGVjdGlvbkVuZCA9IGV4dHZhbC5sZW5ndGg7XHJcbiAgICAgICAgICAvLyBSZS1zZXQgdGhpcywgaW4gY2FzZSBzb21lIG90aGVyIGhhbmRsZXIgdG91Y2hlZCB0aGVcclxuICAgICAgICAgIC8vIHNlbGVjdGlvbiBpbiB0aGUgbWVhbnRpbWUuXHJcbiAgICAgICAgICBkaXNwbGF5LnNlbEZvckNvbnRleHRNZW51ID0gY20uZG9jLnNlbDtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICAgZnVuY3Rpb24gcmVoaWRlKCkge1xyXG4gICAgICAgIGlucHV0LmNvbnRleHRNZW51UGVuZGluZyA9IGZhbHNlO1xyXG4gICAgICAgIGlucHV0LndyYXBwZXIuc3R5bGUucG9zaXRpb24gPSBcInJlbGF0aXZlXCI7XHJcbiAgICAgICAgdGUuc3R5bGUuY3NzVGV4dCA9IG9sZENTUztcclxuICAgICAgICBpZiAoaWUgJiYgaWVfdmVyc2lvbiA8IDkpIGRpc3BsYXkuc2Nyb2xsYmFycy5zZXRTY3JvbGxUb3AoZGlzcGxheS5zY3JvbGxlci5zY3JvbGxUb3AgPSBzY3JvbGxQb3MpO1xyXG5cclxuICAgICAgICAvLyBUcnkgdG8gZGV0ZWN0IHRoZSB1c2VyIGNob29zaW5nIHNlbGVjdC1hbGxcclxuICAgICAgICBpZiAodGUuc2VsZWN0aW9uU3RhcnQgIT0gbnVsbCkge1xyXG4gICAgICAgICAgaWYgKCFpZSB8fCAoaWUgJiYgaWVfdmVyc2lvbiA8IDkpKSBwcmVwYXJlU2VsZWN0QWxsSGFjaygpO1xyXG4gICAgICAgICAgdmFyIGkgPSAwLCBwb2xsID0gZnVuY3Rpb24oKSB7XHJcbiAgICAgICAgICAgIGlmIChkaXNwbGF5LnNlbEZvckNvbnRleHRNZW51ID09IGNtLmRvYy5zZWwgJiYgdGUuc2VsZWN0aW9uU3RhcnQgPT0gMCAmJlxyXG4gICAgICAgICAgICAgICAgdGUuc2VsZWN0aW9uRW5kID4gMCAmJiBpbnB1dC5wcmV2SW5wdXQgPT0gXCJcXHUyMDBiXCIpXHJcbiAgICAgICAgICAgICAgb3BlcmF0aW9uKGNtLCBjb21tYW5kcy5zZWxlY3RBbGwpKGNtKTtcclxuICAgICAgICAgICAgZWxzZSBpZiAoaSsrIDwgMTApIGRpc3BsYXkuZGV0ZWN0aW5nU2VsZWN0QWxsID0gc2V0VGltZW91dChwb2xsLCA1MDApO1xyXG4gICAgICAgICAgICBlbHNlIGRpc3BsYXkuaW5wdXQucmVzZXQoKTtcclxuICAgICAgICAgIH07XHJcbiAgICAgICAgICBkaXNwbGF5LmRldGVjdGluZ1NlbGVjdEFsbCA9IHNldFRpbWVvdXQocG9sbCwgMjAwKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGlmIChpZSAmJiBpZV92ZXJzaW9uID49IDkpIHByZXBhcmVTZWxlY3RBbGxIYWNrKCk7XHJcbiAgICAgIGlmIChjYXB0dXJlUmlnaHRDbGljaykge1xyXG4gICAgICAgIGVfc3RvcChlKTtcclxuICAgICAgICB2YXIgbW91c2V1cCA9IGZ1bmN0aW9uKCkge1xyXG4gICAgICAgICAgb2ZmKHdpbmRvdywgXCJtb3VzZXVwXCIsIG1vdXNldXApO1xyXG4gICAgICAgICAgc2V0VGltZW91dChyZWhpZGUsIDIwKTtcclxuICAgICAgICB9O1xyXG4gICAgICAgIG9uKHdpbmRvdywgXCJtb3VzZXVwXCIsIG1vdXNldXApO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHNldFRpbWVvdXQocmVoaWRlLCA1MCk7XHJcbiAgICAgIH1cclxuICAgIH0sXHJcblxyXG4gICAgcmVhZE9ubHlDaGFuZ2VkOiBmdW5jdGlvbih2YWwpIHtcclxuICAgICAgaWYgKCF2YWwpIHRoaXMucmVzZXQoKTtcclxuICAgIH0sXHJcblxyXG4gICAgc2V0VW5lZGl0YWJsZTogbm90aGluZyxcclxuXHJcbiAgICBuZWVkc0NvbnRlbnRBdHRyaWJ1dGU6IGZhbHNlXHJcbiAgfSwgVGV4dGFyZWFJbnB1dC5wcm90b3R5cGUpO1xyXG5cclxuICAvLyBDT05URU5URURJVEFCTEUgSU5QVVQgU1RZTEVcclxuXHJcbiAgZnVuY3Rpb24gQ29udGVudEVkaXRhYmxlSW5wdXQoY20pIHtcclxuICAgIHRoaXMuY20gPSBjbTtcclxuICAgIHRoaXMubGFzdEFuY2hvck5vZGUgPSB0aGlzLmxhc3RBbmNob3JPZmZzZXQgPSB0aGlzLmxhc3RGb2N1c05vZGUgPSB0aGlzLmxhc3RGb2N1c09mZnNldCA9IG51bGw7XHJcbiAgICB0aGlzLnBvbGxpbmcgPSBuZXcgRGVsYXllZCgpO1xyXG4gICAgdGhpcy5ncmFjZVBlcmlvZCA9IGZhbHNlO1xyXG4gIH1cclxuXHJcbiAgQ29udGVudEVkaXRhYmxlSW5wdXQucHJvdG90eXBlID0gY29weU9iaih7XHJcbiAgICBpbml0OiBmdW5jdGlvbihkaXNwbGF5KSB7XHJcbiAgICAgIHZhciBpbnB1dCA9IHRoaXMsIGNtID0gaW5wdXQuY207XHJcbiAgICAgIHZhciBkaXYgPSBpbnB1dC5kaXYgPSBkaXNwbGF5LmxpbmVEaXY7XHJcbiAgICAgIGRpc2FibGVCcm93c2VyTWFnaWMoZGl2KTtcclxuXHJcbiAgICAgIG9uKGRpdiwgXCJwYXN0ZVwiLCBmdW5jdGlvbihlKSB7IGhhbmRsZVBhc3RlKGUsIGNtKTsgfSlcclxuXHJcbiAgICAgIG9uKGRpdiwgXCJjb21wb3NpdGlvbnN0YXJ0XCIsIGZ1bmN0aW9uKGUpIHtcclxuICAgICAgICB2YXIgZGF0YSA9IGUuZGF0YTtcclxuICAgICAgICBpbnB1dC5jb21wb3NpbmcgPSB7c2VsOiBjbS5kb2Muc2VsLCBkYXRhOiBkYXRhLCBzdGFydERhdGE6IGRhdGF9O1xyXG4gICAgICAgIGlmICghZGF0YSkgcmV0dXJuO1xyXG4gICAgICAgIHZhciBwcmltID0gY20uZG9jLnNlbC5wcmltYXJ5KCk7XHJcbiAgICAgICAgdmFyIGxpbmUgPSBjbS5nZXRMaW5lKHByaW0uaGVhZC5saW5lKTtcclxuICAgICAgICB2YXIgZm91bmQgPSBsaW5lLmluZGV4T2YoZGF0YSwgTWF0aC5tYXgoMCwgcHJpbS5oZWFkLmNoIC0gZGF0YS5sZW5ndGgpKTtcclxuICAgICAgICBpZiAoZm91bmQgPiAtMSAmJiBmb3VuZCA8PSBwcmltLmhlYWQuY2gpXHJcbiAgICAgICAgICBpbnB1dC5jb21wb3Npbmcuc2VsID0gc2ltcGxlU2VsZWN0aW9uKFBvcyhwcmltLmhlYWQubGluZSwgZm91bmQpLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQb3MocHJpbS5oZWFkLmxpbmUsIGZvdW5kICsgZGF0YS5sZW5ndGgpKTtcclxuICAgICAgfSk7XHJcbiAgICAgIG9uKGRpdiwgXCJjb21wb3NpdGlvbnVwZGF0ZVwiLCBmdW5jdGlvbihlKSB7XHJcbiAgICAgICAgaW5wdXQuY29tcG9zaW5nLmRhdGEgPSBlLmRhdGE7XHJcbiAgICAgIH0pO1xyXG4gICAgICBvbihkaXYsIFwiY29tcG9zaXRpb25lbmRcIiwgZnVuY3Rpb24oZSkge1xyXG4gICAgICAgIHZhciBvdXJzID0gaW5wdXQuY29tcG9zaW5nO1xyXG4gICAgICAgIGlmICghb3VycykgcmV0dXJuO1xyXG4gICAgICAgIGlmIChlLmRhdGEgIT0gb3Vycy5zdGFydERhdGEgJiYgIS9cXHUyMDBiLy50ZXN0KGUuZGF0YSkpXHJcbiAgICAgICAgICBvdXJzLmRhdGEgPSBlLmRhdGE7XHJcbiAgICAgICAgLy8gTmVlZCBhIHNtYWxsIGRlbGF5IHRvIHByZXZlbnQgb3RoZXIgY29kZSAoaW5wdXQgZXZlbnQsXHJcbiAgICAgICAgLy8gc2VsZWN0aW9uIHBvbGxpbmcpIGZyb20gZG9pbmcgZGFtYWdlIHdoZW4gZmlyZWQgcmlnaHQgYWZ0ZXJcclxuICAgICAgICAvLyBjb21wb3NpdGlvbmVuZC5cclxuICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xyXG4gICAgICAgICAgaWYgKCFvdXJzLmhhbmRsZWQpXHJcbiAgICAgICAgICAgIGlucHV0LmFwcGx5Q29tcG9zaXRpb24ob3Vycyk7XHJcbiAgICAgICAgICBpZiAoaW5wdXQuY29tcG9zaW5nID09IG91cnMpXHJcbiAgICAgICAgICAgIGlucHV0LmNvbXBvc2luZyA9IG51bGw7XHJcbiAgICAgICAgfSwgNTApO1xyXG4gICAgICB9KTtcclxuXHJcbiAgICAgIG9uKGRpdiwgXCJ0b3VjaHN0YXJ0XCIsIGZ1bmN0aW9uKCkge1xyXG4gICAgICAgIGlucHV0LmZvcmNlQ29tcG9zaXRpb25FbmQoKTtcclxuICAgICAgfSk7XHJcblxyXG4gICAgICBvbihkaXYsIFwiaW5wdXRcIiwgZnVuY3Rpb24oKSB7XHJcbiAgICAgICAgaWYgKGlucHV0LmNvbXBvc2luZykgcmV0dXJuO1xyXG4gICAgICAgIGlmIChpc1JlYWRPbmx5KGNtKSB8fCAhaW5wdXQucG9sbENvbnRlbnQoKSlcclxuICAgICAgICAgIHJ1bkluT3AoaW5wdXQuY20sIGZ1bmN0aW9uKCkge3JlZ0NoYW5nZShjbSk7fSk7XHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgZnVuY3Rpb24gb25Db3B5Q3V0KGUpIHtcclxuICAgICAgICBpZiAoY20uc29tZXRoaW5nU2VsZWN0ZWQoKSkge1xyXG4gICAgICAgICAgbGFzdENvcGllZCA9IGNtLmdldFNlbGVjdGlvbnMoKTtcclxuICAgICAgICAgIGlmIChlLnR5cGUgPT0gXCJjdXRcIikgY20ucmVwbGFjZVNlbGVjdGlvbihcIlwiLCBudWxsLCBcImN1dFwiKTtcclxuICAgICAgICB9IGVsc2UgaWYgKCFjbS5vcHRpb25zLmxpbmVXaXNlQ29weUN1dCkge1xyXG4gICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICB2YXIgcmFuZ2VzID0gY29weWFibGVSYW5nZXMoY20pO1xyXG4gICAgICAgICAgbGFzdENvcGllZCA9IHJhbmdlcy50ZXh0O1xyXG4gICAgICAgICAgaWYgKGUudHlwZSA9PSBcImN1dFwiKSB7XHJcbiAgICAgICAgICAgIGNtLm9wZXJhdGlvbihmdW5jdGlvbigpIHtcclxuICAgICAgICAgICAgICBjbS5zZXRTZWxlY3Rpb25zKHJhbmdlcy5yYW5nZXMsIDAsIHNlbF9kb250U2Nyb2xsKTtcclxuICAgICAgICAgICAgICBjbS5yZXBsYWNlU2VsZWN0aW9uKFwiXCIsIG51bGwsIFwiY3V0XCIpO1xyXG4gICAgICAgICAgICB9KTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgICAgLy8gaU9TIGV4cG9zZXMgdGhlIGNsaXBib2FyZCBBUEksIGJ1dCBzZWVtcyB0byBkaXNjYXJkIGNvbnRlbnQgaW5zZXJ0ZWQgaW50byBpdFxyXG4gICAgICAgIGlmIChlLmNsaXBib2FyZERhdGEgJiYgIWlvcykge1xyXG4gICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xyXG4gICAgICAgICAgZS5jbGlwYm9hcmREYXRhLmNsZWFyRGF0YSgpO1xyXG4gICAgICAgICAgZS5jbGlwYm9hcmREYXRhLnNldERhdGEoXCJ0ZXh0L3BsYWluXCIsIGxhc3RDb3BpZWQuam9pbihcIlxcblwiKSk7XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgIC8vIE9sZC1mYXNoaW9uZWQgYnJpZWZseS1mb2N1cy1hLXRleHRhcmVhIGhhY2tcclxuICAgICAgICAgIHZhciBrbHVkZ2UgPSBoaWRkZW5UZXh0YXJlYSgpLCB0ZSA9IGtsdWRnZS5maXJzdENoaWxkO1xyXG4gICAgICAgICAgY20uZGlzcGxheS5saW5lU3BhY2UuaW5zZXJ0QmVmb3JlKGtsdWRnZSwgY20uZGlzcGxheS5saW5lU3BhY2UuZmlyc3RDaGlsZCk7XHJcbiAgICAgICAgICB0ZS52YWx1ZSA9IGxhc3RDb3BpZWQuam9pbihcIlxcblwiKTtcclxuICAgICAgICAgIHZhciBoYWRGb2N1cyA9IGRvY3VtZW50LmFjdGl2ZUVsZW1lbnQ7XHJcbiAgICAgICAgICBzZWxlY3RJbnB1dCh0ZSk7XHJcbiAgICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xyXG4gICAgICAgICAgICBjbS5kaXNwbGF5LmxpbmVTcGFjZS5yZW1vdmVDaGlsZChrbHVkZ2UpO1xyXG4gICAgICAgICAgICBoYWRGb2N1cy5mb2N1cygpO1xyXG4gICAgICAgICAgfSwgNTApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgICBvbihkaXYsIFwiY29weVwiLCBvbkNvcHlDdXQpO1xyXG4gICAgICBvbihkaXYsIFwiY3V0XCIsIG9uQ29weUN1dCk7XHJcbiAgICB9LFxyXG5cclxuICAgIHByZXBhcmVTZWxlY3Rpb246IGZ1bmN0aW9uKCkge1xyXG4gICAgICB2YXIgcmVzdWx0ID0gcHJlcGFyZVNlbGVjdGlvbih0aGlzLmNtLCBmYWxzZSk7XHJcbiAgICAgIHJlc3VsdC5mb2N1cyA9IHRoaXMuY20uc3RhdGUuZm9jdXNlZDtcclxuICAgICAgcmV0dXJuIHJlc3VsdDtcclxuICAgIH0sXHJcblxyXG4gICAgc2hvd1NlbGVjdGlvbjogZnVuY3Rpb24oaW5mbykge1xyXG4gICAgICBpZiAoIWluZm8gfHwgIXRoaXMuY20uZGlzcGxheS52aWV3Lmxlbmd0aCkgcmV0dXJuO1xyXG4gICAgICBpZiAoaW5mby5mb2N1cykgdGhpcy5zaG93UHJpbWFyeVNlbGVjdGlvbigpO1xyXG4gICAgICB0aGlzLnNob3dNdWx0aXBsZVNlbGVjdGlvbnMoaW5mbyk7XHJcbiAgICB9LFxyXG5cclxuICAgIHNob3dQcmltYXJ5U2VsZWN0aW9uOiBmdW5jdGlvbigpIHtcclxuICAgICAgdmFyIHNlbCA9IHdpbmRvdy5nZXRTZWxlY3Rpb24oKSwgcHJpbSA9IHRoaXMuY20uZG9jLnNlbC5wcmltYXJ5KCk7XHJcbiAgICAgIHZhciBjdXJBbmNob3IgPSBkb21Ub1Bvcyh0aGlzLmNtLCBzZWwuYW5jaG9yTm9kZSwgc2VsLmFuY2hvck9mZnNldCk7XHJcbiAgICAgIHZhciBjdXJGb2N1cyA9IGRvbVRvUG9zKHRoaXMuY20sIHNlbC5mb2N1c05vZGUsIHNlbC5mb2N1c09mZnNldCk7XHJcbiAgICAgIGlmIChjdXJBbmNob3IgJiYgIWN1ckFuY2hvci5iYWQgJiYgY3VyRm9jdXMgJiYgIWN1ckZvY3VzLmJhZCAmJlxyXG4gICAgICAgICAgY21wKG1pblBvcyhjdXJBbmNob3IsIGN1ckZvY3VzKSwgcHJpbS5mcm9tKCkpID09IDAgJiZcclxuICAgICAgICAgIGNtcChtYXhQb3MoY3VyQW5jaG9yLCBjdXJGb2N1cyksIHByaW0udG8oKSkgPT0gMClcclxuICAgICAgICByZXR1cm47XHJcblxyXG4gICAgICB2YXIgc3RhcnQgPSBwb3NUb0RPTSh0aGlzLmNtLCBwcmltLmZyb20oKSk7XHJcbiAgICAgIHZhciBlbmQgPSBwb3NUb0RPTSh0aGlzLmNtLCBwcmltLnRvKCkpO1xyXG4gICAgICBpZiAoIXN0YXJ0ICYmICFlbmQpIHJldHVybjtcclxuXHJcbiAgICAgIHZhciB2aWV3ID0gdGhpcy5jbS5kaXNwbGF5LnZpZXc7XHJcbiAgICAgIHZhciBvbGQgPSBzZWwucmFuZ2VDb3VudCAmJiBzZWwuZ2V0UmFuZ2VBdCgwKTtcclxuICAgICAgaWYgKCFzdGFydCkge1xyXG4gICAgICAgIHN0YXJ0ID0ge25vZGU6IHZpZXdbMF0ubWVhc3VyZS5tYXBbMl0sIG9mZnNldDogMH07XHJcbiAgICAgIH0gZWxzZSBpZiAoIWVuZCkgeyAvLyBGSVhNRSBkYW5nZXJvdXNseSBoYWNreVxyXG4gICAgICAgIHZhciBtZWFzdXJlID0gdmlld1t2aWV3Lmxlbmd0aCAtIDFdLm1lYXN1cmU7XHJcbiAgICAgICAgdmFyIG1hcCA9IG1lYXN1cmUubWFwcyA/IG1lYXN1cmUubWFwc1ttZWFzdXJlLm1hcHMubGVuZ3RoIC0gMV0gOiBtZWFzdXJlLm1hcDtcclxuICAgICAgICBlbmQgPSB7bm9kZTogbWFwW21hcC5sZW5ndGggLSAxXSwgb2Zmc2V0OiBtYXBbbWFwLmxlbmd0aCAtIDJdIC0gbWFwW21hcC5sZW5ndGggLSAzXX07XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIHRyeSB7IHZhciBybmcgPSByYW5nZShzdGFydC5ub2RlLCBzdGFydC5vZmZzZXQsIGVuZC5vZmZzZXQsIGVuZC5ub2RlKTsgfVxyXG4gICAgICBjYXRjaChlKSB7fSAvLyBPdXIgbW9kZWwgb2YgdGhlIERPTSBtaWdodCBiZSBvdXRkYXRlZCwgaW4gd2hpY2ggY2FzZSB0aGUgcmFuZ2Ugd2UgdHJ5IHRvIHNldCBjYW4gYmUgaW1wb3NzaWJsZVxyXG4gICAgICBpZiAocm5nKSB7XHJcbiAgICAgICAgc2VsLnJlbW92ZUFsbFJhbmdlcygpO1xyXG4gICAgICAgIHNlbC5hZGRSYW5nZShybmcpO1xyXG4gICAgICAgIGlmIChvbGQgJiYgc2VsLmFuY2hvck5vZGUgPT0gbnVsbCkgc2VsLmFkZFJhbmdlKG9sZCk7XHJcbiAgICAgICAgZWxzZSBpZiAoZ2Vja28pIHRoaXMuc3RhcnRHcmFjZVBlcmlvZCgpO1xyXG4gICAgICB9XHJcbiAgICAgIHRoaXMucmVtZW1iZXJTZWxlY3Rpb24oKTtcclxuICAgIH0sXHJcblxyXG4gICAgc3RhcnRHcmFjZVBlcmlvZDogZnVuY3Rpb24oKSB7XHJcbiAgICAgIHZhciBpbnB1dCA9IHRoaXM7XHJcbiAgICAgIGNsZWFyVGltZW91dCh0aGlzLmdyYWNlUGVyaW9kKTtcclxuICAgICAgdGhpcy5ncmFjZVBlcmlvZCA9IHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XHJcbiAgICAgICAgaW5wdXQuZ3JhY2VQZXJpb2QgPSBmYWxzZTtcclxuICAgICAgICBpZiAoaW5wdXQuc2VsZWN0aW9uQ2hhbmdlZCgpKVxyXG4gICAgICAgICAgaW5wdXQuY20ub3BlcmF0aW9uKGZ1bmN0aW9uKCkgeyBpbnB1dC5jbS5jdXJPcC5zZWxlY3Rpb25DaGFuZ2VkID0gdHJ1ZTsgfSk7XHJcbiAgICAgIH0sIDIwKTtcclxuICAgIH0sXHJcblxyXG4gICAgc2hvd011bHRpcGxlU2VsZWN0aW9uczogZnVuY3Rpb24oaW5mbykge1xyXG4gICAgICByZW1vdmVDaGlsZHJlbkFuZEFkZCh0aGlzLmNtLmRpc3BsYXkuY3Vyc29yRGl2LCBpbmZvLmN1cnNvcnMpO1xyXG4gICAgICByZW1vdmVDaGlsZHJlbkFuZEFkZCh0aGlzLmNtLmRpc3BsYXkuc2VsZWN0aW9uRGl2LCBpbmZvLnNlbGVjdGlvbik7XHJcbiAgICB9LFxyXG5cclxuICAgIHJlbWVtYmVyU2VsZWN0aW9uOiBmdW5jdGlvbigpIHtcclxuICAgICAgdmFyIHNlbCA9IHdpbmRvdy5nZXRTZWxlY3Rpb24oKTtcclxuICAgICAgdGhpcy5sYXN0QW5jaG9yTm9kZSA9IHNlbC5hbmNob3JOb2RlOyB0aGlzLmxhc3RBbmNob3JPZmZzZXQgPSBzZWwuYW5jaG9yT2Zmc2V0O1xyXG4gICAgICB0aGlzLmxhc3RGb2N1c05vZGUgPSBzZWwuZm9jdXNOb2RlOyB0aGlzLmxhc3RGb2N1c09mZnNldCA9IHNlbC5mb2N1c09mZnNldDtcclxuICAgIH0sXHJcblxyXG4gICAgc2VsZWN0aW9uSW5FZGl0b3I6IGZ1bmN0aW9uKCkge1xyXG4gICAgICB2YXIgc2VsID0gd2luZG93LmdldFNlbGVjdGlvbigpO1xyXG4gICAgICBpZiAoIXNlbC5yYW5nZUNvdW50KSByZXR1cm4gZmFsc2U7XHJcbiAgICAgIHZhciBub2RlID0gc2VsLmdldFJhbmdlQXQoMCkuY29tbW9uQW5jZXN0b3JDb250YWluZXI7XHJcbiAgICAgIHJldHVybiBjb250YWlucyh0aGlzLmRpdiwgbm9kZSk7XHJcbiAgICB9LFxyXG5cclxuICAgIGZvY3VzOiBmdW5jdGlvbigpIHtcclxuICAgICAgaWYgKHRoaXMuY20ub3B0aW9ucy5yZWFkT25seSAhPSBcIm5vY3Vyc29yXCIpIHRoaXMuZGl2LmZvY3VzKCk7XHJcbiAgICB9LFxyXG4gICAgYmx1cjogZnVuY3Rpb24oKSB7IHRoaXMuZGl2LmJsdXIoKTsgfSxcclxuICAgIGdldEZpZWxkOiBmdW5jdGlvbigpIHsgcmV0dXJuIHRoaXMuZGl2OyB9LFxyXG5cclxuICAgIHN1cHBvcnRzVG91Y2g6IGZ1bmN0aW9uKCkgeyByZXR1cm4gdHJ1ZTsgfSxcclxuXHJcbiAgICByZWNlaXZlZEZvY3VzOiBmdW5jdGlvbigpIHtcclxuICAgICAgdmFyIGlucHV0ID0gdGhpcztcclxuICAgICAgaWYgKHRoaXMuc2VsZWN0aW9uSW5FZGl0b3IoKSlcclxuICAgICAgICB0aGlzLnBvbGxTZWxlY3Rpb24oKTtcclxuICAgICAgZWxzZVxyXG4gICAgICAgIHJ1bkluT3AodGhpcy5jbSwgZnVuY3Rpb24oKSB7IGlucHV0LmNtLmN1ck9wLnNlbGVjdGlvbkNoYW5nZWQgPSB0cnVlOyB9KTtcclxuXHJcbiAgICAgIGZ1bmN0aW9uIHBvbGwoKSB7XHJcbiAgICAgICAgaWYgKGlucHV0LmNtLnN0YXRlLmZvY3VzZWQpIHtcclxuICAgICAgICAgIGlucHV0LnBvbGxTZWxlY3Rpb24oKTtcclxuICAgICAgICAgIGlucHV0LnBvbGxpbmcuc2V0KGlucHV0LmNtLm9wdGlvbnMucG9sbEludGVydmFsLCBwb2xsKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICAgdGhpcy5wb2xsaW5nLnNldCh0aGlzLmNtLm9wdGlvbnMucG9sbEludGVydmFsLCBwb2xsKTtcclxuICAgIH0sXHJcblxyXG4gICAgc2VsZWN0aW9uQ2hhbmdlZDogZnVuY3Rpb24oKSB7XHJcbiAgICAgIHZhciBzZWwgPSB3aW5kb3cuZ2V0U2VsZWN0aW9uKCk7XHJcbiAgICAgIHJldHVybiBzZWwuYW5jaG9yTm9kZSAhPSB0aGlzLmxhc3RBbmNob3JOb2RlIHx8IHNlbC5hbmNob3JPZmZzZXQgIT0gdGhpcy5sYXN0QW5jaG9yT2Zmc2V0IHx8XHJcbiAgICAgICAgc2VsLmZvY3VzTm9kZSAhPSB0aGlzLmxhc3RGb2N1c05vZGUgfHwgc2VsLmZvY3VzT2Zmc2V0ICE9IHRoaXMubGFzdEZvY3VzT2Zmc2V0O1xyXG4gICAgfSxcclxuXHJcbiAgICBwb2xsU2VsZWN0aW9uOiBmdW5jdGlvbigpIHtcclxuICAgICAgaWYgKCF0aGlzLmNvbXBvc2luZyAmJiAhdGhpcy5ncmFjZVBlcmlvZCAmJiB0aGlzLnNlbGVjdGlvbkNoYW5nZWQoKSkge1xyXG4gICAgICAgIHZhciBzZWwgPSB3aW5kb3cuZ2V0U2VsZWN0aW9uKCksIGNtID0gdGhpcy5jbTtcclxuICAgICAgICB0aGlzLnJlbWVtYmVyU2VsZWN0aW9uKCk7XHJcbiAgICAgICAgdmFyIGFuY2hvciA9IGRvbVRvUG9zKGNtLCBzZWwuYW5jaG9yTm9kZSwgc2VsLmFuY2hvck9mZnNldCk7XHJcbiAgICAgICAgdmFyIGhlYWQgPSBkb21Ub1BvcyhjbSwgc2VsLmZvY3VzTm9kZSwgc2VsLmZvY3VzT2Zmc2V0KTtcclxuICAgICAgICBpZiAoYW5jaG9yICYmIGhlYWQpIHJ1bkluT3AoY20sIGZ1bmN0aW9uKCkge1xyXG4gICAgICAgICAgc2V0U2VsZWN0aW9uKGNtLmRvYywgc2ltcGxlU2VsZWN0aW9uKGFuY2hvciwgaGVhZCksIHNlbF9kb250U2Nyb2xsKTtcclxuICAgICAgICAgIGlmIChhbmNob3IuYmFkIHx8IGhlYWQuYmFkKSBjbS5jdXJPcC5zZWxlY3Rpb25DaGFuZ2VkID0gdHJ1ZTtcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgfSxcclxuXHJcbiAgICBwb2xsQ29udGVudDogZnVuY3Rpb24oKSB7XHJcbiAgICAgIHZhciBjbSA9IHRoaXMuY20sIGRpc3BsYXkgPSBjbS5kaXNwbGF5LCBzZWwgPSBjbS5kb2Muc2VsLnByaW1hcnkoKTtcclxuICAgICAgdmFyIGZyb20gPSBzZWwuZnJvbSgpLCB0byA9IHNlbC50bygpO1xyXG4gICAgICBpZiAoZnJvbS5saW5lIDwgZGlzcGxheS52aWV3RnJvbSB8fCB0by5saW5lID4gZGlzcGxheS52aWV3VG8gLSAxKSByZXR1cm4gZmFsc2U7XHJcblxyXG4gICAgICB2YXIgZnJvbUluZGV4O1xyXG4gICAgICBpZiAoZnJvbS5saW5lID09IGRpc3BsYXkudmlld0Zyb20gfHwgKGZyb21JbmRleCA9IGZpbmRWaWV3SW5kZXgoY20sIGZyb20ubGluZSkpID09IDApIHtcclxuICAgICAgICB2YXIgZnJvbUxpbmUgPSBsaW5lTm8oZGlzcGxheS52aWV3WzBdLmxpbmUpO1xyXG4gICAgICAgIHZhciBmcm9tTm9kZSA9IGRpc3BsYXkudmlld1swXS5ub2RlO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHZhciBmcm9tTGluZSA9IGxpbmVObyhkaXNwbGF5LnZpZXdbZnJvbUluZGV4XS5saW5lKTtcclxuICAgICAgICB2YXIgZnJvbU5vZGUgPSBkaXNwbGF5LnZpZXdbZnJvbUluZGV4IC0gMV0ubm9kZS5uZXh0U2libGluZztcclxuICAgICAgfVxyXG4gICAgICB2YXIgdG9JbmRleCA9IGZpbmRWaWV3SW5kZXgoY20sIHRvLmxpbmUpO1xyXG4gICAgICBpZiAodG9JbmRleCA9PSBkaXNwbGF5LnZpZXcubGVuZ3RoIC0gMSkge1xyXG4gICAgICAgIHZhciB0b0xpbmUgPSBkaXNwbGF5LnZpZXdUbyAtIDE7XHJcbiAgICAgICAgdmFyIHRvTm9kZSA9IGRpc3BsYXkubGluZURpdi5sYXN0Q2hpbGQ7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgdmFyIHRvTGluZSA9IGxpbmVObyhkaXNwbGF5LnZpZXdbdG9JbmRleCArIDFdLmxpbmUpIC0gMTtcclxuICAgICAgICB2YXIgdG9Ob2RlID0gZGlzcGxheS52aWV3W3RvSW5kZXggKyAxXS5ub2RlLnByZXZpb3VzU2libGluZztcclxuICAgICAgfVxyXG5cclxuICAgICAgdmFyIG5ld1RleHQgPSBjbS5kb2Muc3BsaXRMaW5lcyhkb21UZXh0QmV0d2VlbihjbSwgZnJvbU5vZGUsIHRvTm9kZSwgZnJvbUxpbmUsIHRvTGluZSkpO1xyXG4gICAgICB2YXIgb2xkVGV4dCA9IGdldEJldHdlZW4oY20uZG9jLCBQb3MoZnJvbUxpbmUsIDApLCBQb3ModG9MaW5lLCBnZXRMaW5lKGNtLmRvYywgdG9MaW5lKS50ZXh0Lmxlbmd0aCkpO1xyXG4gICAgICB3aGlsZSAobmV3VGV4dC5sZW5ndGggPiAxICYmIG9sZFRleHQubGVuZ3RoID4gMSkge1xyXG4gICAgICAgIGlmIChsc3QobmV3VGV4dCkgPT0gbHN0KG9sZFRleHQpKSB7IG5ld1RleHQucG9wKCk7IG9sZFRleHQucG9wKCk7IHRvTGluZS0tOyB9XHJcbiAgICAgICAgZWxzZSBpZiAobmV3VGV4dFswXSA9PSBvbGRUZXh0WzBdKSB7IG5ld1RleHQuc2hpZnQoKTsgb2xkVGV4dC5zaGlmdCgpOyBmcm9tTGluZSsrOyB9XHJcbiAgICAgICAgZWxzZSBicmVhaztcclxuICAgICAgfVxyXG5cclxuICAgICAgdmFyIGN1dEZyb250ID0gMCwgY3V0RW5kID0gMDtcclxuICAgICAgdmFyIG5ld1RvcCA9IG5ld1RleHRbMF0sIG9sZFRvcCA9IG9sZFRleHRbMF0sIG1heEN1dEZyb250ID0gTWF0aC5taW4obmV3VG9wLmxlbmd0aCwgb2xkVG9wLmxlbmd0aCk7XHJcbiAgICAgIHdoaWxlIChjdXRGcm9udCA8IG1heEN1dEZyb250ICYmIG5ld1RvcC5jaGFyQ29kZUF0KGN1dEZyb250KSA9PSBvbGRUb3AuY2hhckNvZGVBdChjdXRGcm9udCkpXHJcbiAgICAgICAgKytjdXRGcm9udDtcclxuICAgICAgdmFyIG5ld0JvdCA9IGxzdChuZXdUZXh0KSwgb2xkQm90ID0gbHN0KG9sZFRleHQpO1xyXG4gICAgICB2YXIgbWF4Q3V0RW5kID0gTWF0aC5taW4obmV3Qm90Lmxlbmd0aCAtIChuZXdUZXh0Lmxlbmd0aCA9PSAxID8gY3V0RnJvbnQgOiAwKSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9sZEJvdC5sZW5ndGggLSAob2xkVGV4dC5sZW5ndGggPT0gMSA/IGN1dEZyb250IDogMCkpO1xyXG4gICAgICB3aGlsZSAoY3V0RW5kIDwgbWF4Q3V0RW5kICYmXHJcbiAgICAgICAgICAgICBuZXdCb3QuY2hhckNvZGVBdChuZXdCb3QubGVuZ3RoIC0gY3V0RW5kIC0gMSkgPT0gb2xkQm90LmNoYXJDb2RlQXQob2xkQm90Lmxlbmd0aCAtIGN1dEVuZCAtIDEpKVxyXG4gICAgICAgICsrY3V0RW5kO1xyXG5cclxuICAgICAgbmV3VGV4dFtuZXdUZXh0Lmxlbmd0aCAtIDFdID0gbmV3Qm90LnNsaWNlKDAsIG5ld0JvdC5sZW5ndGggLSBjdXRFbmQpO1xyXG4gICAgICBuZXdUZXh0WzBdID0gbmV3VGV4dFswXS5zbGljZShjdXRGcm9udCk7XHJcblxyXG4gICAgICB2YXIgY2hGcm9tID0gUG9zKGZyb21MaW5lLCBjdXRGcm9udCk7XHJcbiAgICAgIHZhciBjaFRvID0gUG9zKHRvTGluZSwgb2xkVGV4dC5sZW5ndGggPyBsc3Qob2xkVGV4dCkubGVuZ3RoIC0gY3V0RW5kIDogMCk7XHJcbiAgICAgIGlmIChuZXdUZXh0Lmxlbmd0aCA+IDEgfHwgbmV3VGV4dFswXSB8fCBjbXAoY2hGcm9tLCBjaFRvKSkge1xyXG4gICAgICAgIHJlcGxhY2VSYW5nZShjbS5kb2MsIG5ld1RleHQsIGNoRnJvbSwgY2hUbywgXCIraW5wdXRcIik7XHJcbiAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgIH1cclxuICAgIH0sXHJcblxyXG4gICAgZW5zdXJlUG9sbGVkOiBmdW5jdGlvbigpIHtcclxuICAgICAgdGhpcy5mb3JjZUNvbXBvc2l0aW9uRW5kKCk7XHJcbiAgICB9LFxyXG4gICAgcmVzZXQ6IGZ1bmN0aW9uKCkge1xyXG4gICAgICB0aGlzLmZvcmNlQ29tcG9zaXRpb25FbmQoKTtcclxuICAgIH0sXHJcbiAgICBmb3JjZUNvbXBvc2l0aW9uRW5kOiBmdW5jdGlvbigpIHtcclxuICAgICAgaWYgKCF0aGlzLmNvbXBvc2luZyB8fCB0aGlzLmNvbXBvc2luZy5oYW5kbGVkKSByZXR1cm47XHJcbiAgICAgIHRoaXMuYXBwbHlDb21wb3NpdGlvbih0aGlzLmNvbXBvc2luZyk7XHJcbiAgICAgIHRoaXMuY29tcG9zaW5nLmhhbmRsZWQgPSB0cnVlO1xyXG4gICAgICB0aGlzLmRpdi5ibHVyKCk7XHJcbiAgICAgIHRoaXMuZGl2LmZvY3VzKCk7XHJcbiAgICB9LFxyXG4gICAgYXBwbHlDb21wb3NpdGlvbjogZnVuY3Rpb24oY29tcG9zaW5nKSB7XHJcbiAgICAgIGlmIChpc1JlYWRPbmx5KHRoaXMuY20pKVxyXG4gICAgICAgIG9wZXJhdGlvbih0aGlzLmNtLCByZWdDaGFuZ2UpKHRoaXMuY20pXHJcbiAgICAgIGVsc2UgaWYgKGNvbXBvc2luZy5kYXRhICYmIGNvbXBvc2luZy5kYXRhICE9IGNvbXBvc2luZy5zdGFydERhdGEpXHJcbiAgICAgICAgb3BlcmF0aW9uKHRoaXMuY20sIGFwcGx5VGV4dElucHV0KSh0aGlzLmNtLCBjb21wb3NpbmcuZGF0YSwgMCwgY29tcG9zaW5nLnNlbCk7XHJcbiAgICB9LFxyXG5cclxuICAgIHNldFVuZWRpdGFibGU6IGZ1bmN0aW9uKG5vZGUpIHtcclxuICAgICAgbm9kZS5jb250ZW50RWRpdGFibGUgPSBcImZhbHNlXCJcclxuICAgIH0sXHJcblxyXG4gICAgb25LZXlQcmVzczogZnVuY3Rpb24oZSkge1xyXG4gICAgICBlLnByZXZlbnREZWZhdWx0KCk7XHJcbiAgICAgIGlmICghaXNSZWFkT25seSh0aGlzLmNtKSlcclxuICAgICAgICBvcGVyYXRpb24odGhpcy5jbSwgYXBwbHlUZXh0SW5wdXQpKHRoaXMuY20sIFN0cmluZy5mcm9tQ2hhckNvZGUoZS5jaGFyQ29kZSA9PSBudWxsID8gZS5rZXlDb2RlIDogZS5jaGFyQ29kZSksIDApO1xyXG4gICAgfSxcclxuXHJcbiAgICByZWFkT25seUNoYW5nZWQ6IGZ1bmN0aW9uKHZhbCkge1xyXG4gICAgICB0aGlzLmRpdi5jb250ZW50RWRpdGFibGUgPSBTdHJpbmcodmFsICE9IFwibm9jdXJzb3JcIilcclxuICAgIH0sXHJcblxyXG4gICAgb25Db250ZXh0TWVudTogbm90aGluZyxcclxuICAgIHJlc2V0UG9zaXRpb246IG5vdGhpbmcsXHJcblxyXG4gICAgbmVlZHNDb250ZW50QXR0cmlidXRlOiB0cnVlXHJcbiAgfSwgQ29udGVudEVkaXRhYmxlSW5wdXQucHJvdG90eXBlKTtcclxuXHJcbiAgZnVuY3Rpb24gcG9zVG9ET00oY20sIHBvcykge1xyXG4gICAgdmFyIHZpZXcgPSBmaW5kVmlld0ZvckxpbmUoY20sIHBvcy5saW5lKTtcclxuICAgIGlmICghdmlldyB8fCB2aWV3LmhpZGRlbikgcmV0dXJuIG51bGw7XHJcbiAgICB2YXIgbGluZSA9IGdldExpbmUoY20uZG9jLCBwb3MubGluZSk7XHJcbiAgICB2YXIgaW5mbyA9IG1hcEZyb21MaW5lVmlldyh2aWV3LCBsaW5lLCBwb3MubGluZSk7XHJcblxyXG4gICAgdmFyIG9yZGVyID0gZ2V0T3JkZXIobGluZSksIHNpZGUgPSBcImxlZnRcIjtcclxuICAgIGlmIChvcmRlcikge1xyXG4gICAgICB2YXIgcGFydFBvcyA9IGdldEJpZGlQYXJ0QXQob3JkZXIsIHBvcy5jaCk7XHJcbiAgICAgIHNpZGUgPSBwYXJ0UG9zICUgMiA/IFwicmlnaHRcIiA6IFwibGVmdFwiO1xyXG4gICAgfVxyXG4gICAgdmFyIHJlc3VsdCA9IG5vZGVBbmRPZmZzZXRJbkxpbmVNYXAoaW5mby5tYXAsIHBvcy5jaCwgc2lkZSk7XHJcbiAgICByZXN1bHQub2Zmc2V0ID0gcmVzdWx0LmNvbGxhcHNlID09IFwicmlnaHRcIiA/IHJlc3VsdC5lbmQgOiByZXN1bHQuc3RhcnQ7XHJcbiAgICByZXR1cm4gcmVzdWx0O1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gYmFkUG9zKHBvcywgYmFkKSB7IGlmIChiYWQpIHBvcy5iYWQgPSB0cnVlOyByZXR1cm4gcG9zOyB9XHJcblxyXG4gIGZ1bmN0aW9uIGRvbVRvUG9zKGNtLCBub2RlLCBvZmZzZXQpIHtcclxuICAgIHZhciBsaW5lTm9kZTtcclxuICAgIGlmIChub2RlID09IGNtLmRpc3BsYXkubGluZURpdikge1xyXG4gICAgICBsaW5lTm9kZSA9IGNtLmRpc3BsYXkubGluZURpdi5jaGlsZE5vZGVzW29mZnNldF07XHJcbiAgICAgIGlmICghbGluZU5vZGUpIHJldHVybiBiYWRQb3MoY20uY2xpcFBvcyhQb3MoY20uZGlzcGxheS52aWV3VG8gLSAxKSksIHRydWUpO1xyXG4gICAgICBub2RlID0gbnVsbDsgb2Zmc2V0ID0gMDtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGZvciAobGluZU5vZGUgPSBub2RlOzsgbGluZU5vZGUgPSBsaW5lTm9kZS5wYXJlbnROb2RlKSB7XHJcbiAgICAgICAgaWYgKCFsaW5lTm9kZSB8fCBsaW5lTm9kZSA9PSBjbS5kaXNwbGF5LmxpbmVEaXYpIHJldHVybiBudWxsO1xyXG4gICAgICAgIGlmIChsaW5lTm9kZS5wYXJlbnROb2RlICYmIGxpbmVOb2RlLnBhcmVudE5vZGUgPT0gY20uZGlzcGxheS5saW5lRGl2KSBicmVhaztcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBjbS5kaXNwbGF5LnZpZXcubGVuZ3RoOyBpKyspIHtcclxuICAgICAgdmFyIGxpbmVWaWV3ID0gY20uZGlzcGxheS52aWV3W2ldO1xyXG4gICAgICBpZiAobGluZVZpZXcubm9kZSA9PSBsaW5lTm9kZSlcclxuICAgICAgICByZXR1cm4gbG9jYXRlTm9kZUluTGluZVZpZXcobGluZVZpZXcsIG5vZGUsIG9mZnNldCk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBsb2NhdGVOb2RlSW5MaW5lVmlldyhsaW5lVmlldywgbm9kZSwgb2Zmc2V0KSB7XHJcbiAgICB2YXIgd3JhcHBlciA9IGxpbmVWaWV3LnRleHQuZmlyc3RDaGlsZCwgYmFkID0gZmFsc2U7XHJcbiAgICBpZiAoIW5vZGUgfHwgIWNvbnRhaW5zKHdyYXBwZXIsIG5vZGUpKSByZXR1cm4gYmFkUG9zKFBvcyhsaW5lTm8obGluZVZpZXcubGluZSksIDApLCB0cnVlKTtcclxuICAgIGlmIChub2RlID09IHdyYXBwZXIpIHtcclxuICAgICAgYmFkID0gdHJ1ZTtcclxuICAgICAgbm9kZSA9IHdyYXBwZXIuY2hpbGROb2Rlc1tvZmZzZXRdO1xyXG4gICAgICBvZmZzZXQgPSAwO1xyXG4gICAgICBpZiAoIW5vZGUpIHtcclxuICAgICAgICB2YXIgbGluZSA9IGxpbmVWaWV3LnJlc3QgPyBsc3QobGluZVZpZXcucmVzdCkgOiBsaW5lVmlldy5saW5lO1xyXG4gICAgICAgIHJldHVybiBiYWRQb3MoUG9zKGxpbmVObyhsaW5lKSwgbGluZS50ZXh0Lmxlbmd0aCksIGJhZCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICB2YXIgdGV4dE5vZGUgPSBub2RlLm5vZGVUeXBlID09IDMgPyBub2RlIDogbnVsbCwgdG9wTm9kZSA9IG5vZGU7XHJcbiAgICBpZiAoIXRleHROb2RlICYmIG5vZGUuY2hpbGROb2Rlcy5sZW5ndGggPT0gMSAmJiBub2RlLmZpcnN0Q2hpbGQubm9kZVR5cGUgPT0gMykge1xyXG4gICAgICB0ZXh0Tm9kZSA9IG5vZGUuZmlyc3RDaGlsZDtcclxuICAgICAgaWYgKG9mZnNldCkgb2Zmc2V0ID0gdGV4dE5vZGUubm9kZVZhbHVlLmxlbmd0aDtcclxuICAgIH1cclxuICAgIHdoaWxlICh0b3BOb2RlLnBhcmVudE5vZGUgIT0gd3JhcHBlcikgdG9wTm9kZSA9IHRvcE5vZGUucGFyZW50Tm9kZTtcclxuICAgIHZhciBtZWFzdXJlID0gbGluZVZpZXcubWVhc3VyZSwgbWFwcyA9IG1lYXN1cmUubWFwcztcclxuXHJcbiAgICBmdW5jdGlvbiBmaW5kKHRleHROb2RlLCB0b3BOb2RlLCBvZmZzZXQpIHtcclxuICAgICAgZm9yICh2YXIgaSA9IC0xOyBpIDwgKG1hcHMgPyBtYXBzLmxlbmd0aCA6IDApOyBpKyspIHtcclxuICAgICAgICB2YXIgbWFwID0gaSA8IDAgPyBtZWFzdXJlLm1hcCA6IG1hcHNbaV07XHJcbiAgICAgICAgZm9yICh2YXIgaiA9IDA7IGogPCBtYXAubGVuZ3RoOyBqICs9IDMpIHtcclxuICAgICAgICAgIHZhciBjdXJOb2RlID0gbWFwW2ogKyAyXTtcclxuICAgICAgICAgIGlmIChjdXJOb2RlID09IHRleHROb2RlIHx8IGN1ck5vZGUgPT0gdG9wTm9kZSkge1xyXG4gICAgICAgICAgICB2YXIgbGluZSA9IGxpbmVObyhpIDwgMCA/IGxpbmVWaWV3LmxpbmUgOiBsaW5lVmlldy5yZXN0W2ldKTtcclxuICAgICAgICAgICAgdmFyIGNoID0gbWFwW2pdICsgb2Zmc2V0O1xyXG4gICAgICAgICAgICBpZiAob2Zmc2V0IDwgMCB8fCBjdXJOb2RlICE9IHRleHROb2RlKSBjaCA9IG1hcFtqICsgKG9mZnNldCA/IDEgOiAwKV07XHJcbiAgICAgICAgICAgIHJldHVybiBQb3MobGluZSwgY2gpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgdmFyIGZvdW5kID0gZmluZCh0ZXh0Tm9kZSwgdG9wTm9kZSwgb2Zmc2V0KTtcclxuICAgIGlmIChmb3VuZCkgcmV0dXJuIGJhZFBvcyhmb3VuZCwgYmFkKTtcclxuXHJcbiAgICAvLyBGSVhNRSB0aGlzIGlzIGFsbCByZWFsbHkgc2hha3kuIG1pZ2h0IGhhbmRsZSB0aGUgZmV3IGNhc2VzIGl0IG5lZWRzIHRvIGhhbmRsZSwgYnV0IGxpa2VseSB0byBjYXVzZSBwcm9ibGVtc1xyXG4gICAgZm9yICh2YXIgYWZ0ZXIgPSB0b3BOb2RlLm5leHRTaWJsaW5nLCBkaXN0ID0gdGV4dE5vZGUgPyB0ZXh0Tm9kZS5ub2RlVmFsdWUubGVuZ3RoIC0gb2Zmc2V0IDogMDsgYWZ0ZXI7IGFmdGVyID0gYWZ0ZXIubmV4dFNpYmxpbmcpIHtcclxuICAgICAgZm91bmQgPSBmaW5kKGFmdGVyLCBhZnRlci5maXJzdENoaWxkLCAwKTtcclxuICAgICAgaWYgKGZvdW5kKVxyXG4gICAgICAgIHJldHVybiBiYWRQb3MoUG9zKGZvdW5kLmxpbmUsIGZvdW5kLmNoIC0gZGlzdCksIGJhZCk7XHJcbiAgICAgIGVsc2VcclxuICAgICAgICBkaXN0ICs9IGFmdGVyLnRleHRDb250ZW50Lmxlbmd0aDtcclxuICAgIH1cclxuICAgIGZvciAodmFyIGJlZm9yZSA9IHRvcE5vZGUucHJldmlvdXNTaWJsaW5nLCBkaXN0ID0gb2Zmc2V0OyBiZWZvcmU7IGJlZm9yZSA9IGJlZm9yZS5wcmV2aW91c1NpYmxpbmcpIHtcclxuICAgICAgZm91bmQgPSBmaW5kKGJlZm9yZSwgYmVmb3JlLmZpcnN0Q2hpbGQsIC0xKTtcclxuICAgICAgaWYgKGZvdW5kKVxyXG4gICAgICAgIHJldHVybiBiYWRQb3MoUG9zKGZvdW5kLmxpbmUsIGZvdW5kLmNoICsgZGlzdCksIGJhZCk7XHJcbiAgICAgIGVsc2VcclxuICAgICAgICBkaXN0ICs9IGFmdGVyLnRleHRDb250ZW50Lmxlbmd0aDtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIGRvbVRleHRCZXR3ZWVuKGNtLCBmcm9tLCB0bywgZnJvbUxpbmUsIHRvTGluZSkge1xyXG4gICAgdmFyIHRleHQgPSBcIlwiLCBjbG9zaW5nID0gZmFsc2UsIGxpbmVTZXAgPSBjbS5kb2MubGluZVNlcGFyYXRvcigpO1xyXG4gICAgZnVuY3Rpb24gcmVjb2duaXplTWFya2VyKGlkKSB7IHJldHVybiBmdW5jdGlvbihtYXJrZXIpIHsgcmV0dXJuIG1hcmtlci5pZCA9PSBpZDsgfTsgfVxyXG4gICAgZnVuY3Rpb24gd2Fsayhub2RlKSB7XHJcbiAgICAgIGlmIChub2RlLm5vZGVUeXBlID09IDEpIHtcclxuICAgICAgICB2YXIgY21UZXh0ID0gbm9kZS5nZXRBdHRyaWJ1dGUoXCJjbS10ZXh0XCIpO1xyXG4gICAgICAgIGlmIChjbVRleHQgIT0gbnVsbCkge1xyXG4gICAgICAgICAgaWYgKGNtVGV4dCA9PSBcIlwiKSBjbVRleHQgPSBub2RlLnRleHRDb250ZW50LnJlcGxhY2UoL1xcdTIwMGIvZywgXCJcIik7XHJcbiAgICAgICAgICB0ZXh0ICs9IGNtVGV4dDtcclxuICAgICAgICAgIHJldHVybjtcclxuICAgICAgICB9XHJcbiAgICAgICAgdmFyIG1hcmtlcklEID0gbm9kZS5nZXRBdHRyaWJ1dGUoXCJjbS1tYXJrZXJcIiksIHJhbmdlO1xyXG4gICAgICAgIGlmIChtYXJrZXJJRCkge1xyXG4gICAgICAgICAgdmFyIGZvdW5kID0gY20uZmluZE1hcmtzKFBvcyhmcm9tTGluZSwgMCksIFBvcyh0b0xpbmUgKyAxLCAwKSwgcmVjb2duaXplTWFya2VyKCttYXJrZXJJRCkpO1xyXG4gICAgICAgICAgaWYgKGZvdW5kLmxlbmd0aCAmJiAocmFuZ2UgPSBmb3VuZFswXS5maW5kKCkpKVxyXG4gICAgICAgICAgICB0ZXh0ICs9IGdldEJldHdlZW4oY20uZG9jLCByYW5nZS5mcm9tLCByYW5nZS50bykuam9pbihsaW5lU2VwKTtcclxuICAgICAgICAgIHJldHVybjtcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYgKG5vZGUuZ2V0QXR0cmlidXRlKFwiY29udGVudGVkaXRhYmxlXCIpID09IFwiZmFsc2VcIikgcmV0dXJuO1xyXG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbm9kZS5jaGlsZE5vZGVzLmxlbmd0aDsgaSsrKVxyXG4gICAgICAgICAgd2Fsayhub2RlLmNoaWxkTm9kZXNbaV0pO1xyXG4gICAgICAgIGlmICgvXihwcmV8ZGl2fHApJC9pLnRlc3Qobm9kZS5ub2RlTmFtZSkpXHJcbiAgICAgICAgICBjbG9zaW5nID0gdHJ1ZTtcclxuICAgICAgfSBlbHNlIGlmIChub2RlLm5vZGVUeXBlID09IDMpIHtcclxuICAgICAgICB2YXIgdmFsID0gbm9kZS5ub2RlVmFsdWU7XHJcbiAgICAgICAgaWYgKCF2YWwpIHJldHVybjtcclxuICAgICAgICBpZiAoY2xvc2luZykge1xyXG4gICAgICAgICAgdGV4dCArPSBsaW5lU2VwO1xyXG4gICAgICAgICAgY2xvc2luZyA9IGZhbHNlO1xyXG4gICAgICAgIH1cclxuICAgICAgICB0ZXh0ICs9IHZhbDtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgZm9yICg7Oykge1xyXG4gICAgICB3YWxrKGZyb20pO1xyXG4gICAgICBpZiAoZnJvbSA9PSB0bykgYnJlYWs7XHJcbiAgICAgIGZyb20gPSBmcm9tLm5leHRTaWJsaW5nO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIHRleHQ7XHJcbiAgfVxyXG5cclxuICBDb2RlTWlycm9yLmlucHV0U3R5bGVzID0ge1widGV4dGFyZWFcIjogVGV4dGFyZWFJbnB1dCwgXCJjb250ZW50ZWRpdGFibGVcIjogQ29udGVudEVkaXRhYmxlSW5wdXR9O1xyXG5cclxuICAvLyBTRUxFQ1RJT04gLyBDVVJTT1JcclxuXHJcbiAgLy8gU2VsZWN0aW9uIG9iamVjdHMgYXJlIGltbXV0YWJsZS4gQSBuZXcgb25lIGlzIGNyZWF0ZWQgZXZlcnkgdGltZVxyXG4gIC8vIHRoZSBzZWxlY3Rpb24gY2hhbmdlcy4gQSBzZWxlY3Rpb24gaXMgb25lIG9yIG1vcmUgbm9uLW92ZXJsYXBwaW5nXHJcbiAgLy8gKGFuZCBub24tdG91Y2hpbmcpIHJhbmdlcywgc29ydGVkLCBhbmQgYW4gaW50ZWdlciB0aGF0IGluZGljYXRlc1xyXG4gIC8vIHdoaWNoIG9uZSBpcyB0aGUgcHJpbWFyeSBzZWxlY3Rpb24gKHRoZSBvbmUgdGhhdCdzIHNjcm9sbGVkIGludG9cclxuICAvLyB2aWV3LCB0aGF0IGdldEN1cnNvciByZXR1cm5zLCBldGMpLlxyXG4gIGZ1bmN0aW9uIFNlbGVjdGlvbihyYW5nZXMsIHByaW1JbmRleCkge1xyXG4gICAgdGhpcy5yYW5nZXMgPSByYW5nZXM7XHJcbiAgICB0aGlzLnByaW1JbmRleCA9IHByaW1JbmRleDtcclxuICB9XHJcblxyXG4gIFNlbGVjdGlvbi5wcm90b3R5cGUgPSB7XHJcbiAgICBwcmltYXJ5OiBmdW5jdGlvbigpIHsgcmV0dXJuIHRoaXMucmFuZ2VzW3RoaXMucHJpbUluZGV4XTsgfSxcclxuICAgIGVxdWFsczogZnVuY3Rpb24ob3RoZXIpIHtcclxuICAgICAgaWYgKG90aGVyID09IHRoaXMpIHJldHVybiB0cnVlO1xyXG4gICAgICBpZiAob3RoZXIucHJpbUluZGV4ICE9IHRoaXMucHJpbUluZGV4IHx8IG90aGVyLnJhbmdlcy5sZW5ndGggIT0gdGhpcy5yYW5nZXMubGVuZ3RoKSByZXR1cm4gZmFsc2U7XHJcbiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy5yYW5nZXMubGVuZ3RoOyBpKyspIHtcclxuICAgICAgICB2YXIgaGVyZSA9IHRoaXMucmFuZ2VzW2ldLCB0aGVyZSA9IG90aGVyLnJhbmdlc1tpXTtcclxuICAgICAgICBpZiAoY21wKGhlcmUuYW5jaG9yLCB0aGVyZS5hbmNob3IpICE9IDAgfHwgY21wKGhlcmUuaGVhZCwgdGhlcmUuaGVhZCkgIT0gMCkgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfSxcclxuICAgIGRlZXBDb3B5OiBmdW5jdGlvbigpIHtcclxuICAgICAgZm9yICh2YXIgb3V0ID0gW10sIGkgPSAwOyBpIDwgdGhpcy5yYW5nZXMubGVuZ3RoOyBpKyspXHJcbiAgICAgICAgb3V0W2ldID0gbmV3IFJhbmdlKGNvcHlQb3ModGhpcy5yYW5nZXNbaV0uYW5jaG9yKSwgY29weVBvcyh0aGlzLnJhbmdlc1tpXS5oZWFkKSk7XHJcbiAgICAgIHJldHVybiBuZXcgU2VsZWN0aW9uKG91dCwgdGhpcy5wcmltSW5kZXgpO1xyXG4gICAgfSxcclxuICAgIHNvbWV0aGluZ1NlbGVjdGVkOiBmdW5jdGlvbigpIHtcclxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB0aGlzLnJhbmdlcy5sZW5ndGg7IGkrKylcclxuICAgICAgICBpZiAoIXRoaXMucmFuZ2VzW2ldLmVtcHR5KCkpIHJldHVybiB0cnVlO1xyXG4gICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICB9LFxyXG4gICAgY29udGFpbnM6IGZ1bmN0aW9uKHBvcywgZW5kKSB7XHJcbiAgICAgIGlmICghZW5kKSBlbmQgPSBwb3M7XHJcbiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy5yYW5nZXMubGVuZ3RoOyBpKyspIHtcclxuICAgICAgICB2YXIgcmFuZ2UgPSB0aGlzLnJhbmdlc1tpXTtcclxuICAgICAgICBpZiAoY21wKGVuZCwgcmFuZ2UuZnJvbSgpKSA+PSAwICYmIGNtcChwb3MsIHJhbmdlLnRvKCkpIDw9IDApXHJcbiAgICAgICAgICByZXR1cm4gaTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gLTE7XHJcbiAgICB9XHJcbiAgfTtcclxuXHJcbiAgZnVuY3Rpb24gUmFuZ2UoYW5jaG9yLCBoZWFkKSB7XHJcbiAgICB0aGlzLmFuY2hvciA9IGFuY2hvcjsgdGhpcy5oZWFkID0gaGVhZDtcclxuICB9XHJcblxyXG4gIFJhbmdlLnByb3RvdHlwZSA9IHtcclxuICAgIGZyb206IGZ1bmN0aW9uKCkgeyByZXR1cm4gbWluUG9zKHRoaXMuYW5jaG9yLCB0aGlzLmhlYWQpOyB9LFxyXG4gICAgdG86IGZ1bmN0aW9uKCkgeyByZXR1cm4gbWF4UG9zKHRoaXMuYW5jaG9yLCB0aGlzLmhlYWQpOyB9LFxyXG4gICAgZW1wdHk6IGZ1bmN0aW9uKCkge1xyXG4gICAgICByZXR1cm4gdGhpcy5oZWFkLmxpbmUgPT0gdGhpcy5hbmNob3IubGluZSAmJiB0aGlzLmhlYWQuY2ggPT0gdGhpcy5hbmNob3IuY2g7XHJcbiAgICB9XHJcbiAgfTtcclxuXHJcbiAgLy8gVGFrZSBhbiB1bnNvcnRlZCwgcG90ZW50aWFsbHkgb3ZlcmxhcHBpbmcgc2V0IG9mIHJhbmdlcywgYW5kXHJcbiAgLy8gYnVpbGQgYSBzZWxlY3Rpb24gb3V0IG9mIGl0LiAnQ29uc3VtZXMnIHJhbmdlcyBhcnJheSAobW9kaWZ5aW5nXHJcbiAgLy8gaXQpLlxyXG4gIGZ1bmN0aW9uIG5vcm1hbGl6ZVNlbGVjdGlvbihyYW5nZXMsIHByaW1JbmRleCkge1xyXG4gICAgdmFyIHByaW0gPSByYW5nZXNbcHJpbUluZGV4XTtcclxuICAgIHJhbmdlcy5zb3J0KGZ1bmN0aW9uKGEsIGIpIHsgcmV0dXJuIGNtcChhLmZyb20oKSwgYi5mcm9tKCkpOyB9KTtcclxuICAgIHByaW1JbmRleCA9IGluZGV4T2YocmFuZ2VzLCBwcmltKTtcclxuICAgIGZvciAodmFyIGkgPSAxOyBpIDwgcmFuZ2VzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgIHZhciBjdXIgPSByYW5nZXNbaV0sIHByZXYgPSByYW5nZXNbaSAtIDFdO1xyXG4gICAgICBpZiAoY21wKHByZXYudG8oKSwgY3VyLmZyb20oKSkgPj0gMCkge1xyXG4gICAgICAgIHZhciBmcm9tID0gbWluUG9zKHByZXYuZnJvbSgpLCBjdXIuZnJvbSgpKSwgdG8gPSBtYXhQb3MocHJldi50bygpLCBjdXIudG8oKSk7XHJcbiAgICAgICAgdmFyIGludiA9IHByZXYuZW1wdHkoKSA/IGN1ci5mcm9tKCkgPT0gY3VyLmhlYWQgOiBwcmV2LmZyb20oKSA9PSBwcmV2LmhlYWQ7XHJcbiAgICAgICAgaWYgKGkgPD0gcHJpbUluZGV4KSAtLXByaW1JbmRleDtcclxuICAgICAgICByYW5nZXMuc3BsaWNlKC0taSwgMiwgbmV3IFJhbmdlKGludiA/IHRvIDogZnJvbSwgaW52ID8gZnJvbSA6IHRvKSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIHJldHVybiBuZXcgU2VsZWN0aW9uKHJhbmdlcywgcHJpbUluZGV4KTtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIHNpbXBsZVNlbGVjdGlvbihhbmNob3IsIGhlYWQpIHtcclxuICAgIHJldHVybiBuZXcgU2VsZWN0aW9uKFtuZXcgUmFuZ2UoYW5jaG9yLCBoZWFkIHx8IGFuY2hvcildLCAwKTtcclxuICB9XHJcblxyXG4gIC8vIE1vc3Qgb2YgdGhlIGV4dGVybmFsIEFQSSBjbGlwcyBnaXZlbiBwb3NpdGlvbnMgdG8gbWFrZSBzdXJlIHRoZXlcclxuICAvLyBhY3R1YWxseSBleGlzdCB3aXRoaW4gdGhlIGRvY3VtZW50LlxyXG4gIGZ1bmN0aW9uIGNsaXBMaW5lKGRvYywgbikge3JldHVybiBNYXRoLm1heChkb2MuZmlyc3QsIE1hdGgubWluKG4sIGRvYy5maXJzdCArIGRvYy5zaXplIC0gMSkpO31cclxuICBmdW5jdGlvbiBjbGlwUG9zKGRvYywgcG9zKSB7XHJcbiAgICBpZiAocG9zLmxpbmUgPCBkb2MuZmlyc3QpIHJldHVybiBQb3MoZG9jLmZpcnN0LCAwKTtcclxuICAgIHZhciBsYXN0ID0gZG9jLmZpcnN0ICsgZG9jLnNpemUgLSAxO1xyXG4gICAgaWYgKHBvcy5saW5lID4gbGFzdCkgcmV0dXJuIFBvcyhsYXN0LCBnZXRMaW5lKGRvYywgbGFzdCkudGV4dC5sZW5ndGgpO1xyXG4gICAgcmV0dXJuIGNsaXBUb0xlbihwb3MsIGdldExpbmUoZG9jLCBwb3MubGluZSkudGV4dC5sZW5ndGgpO1xyXG4gIH1cclxuICBmdW5jdGlvbiBjbGlwVG9MZW4ocG9zLCBsaW5lbGVuKSB7XHJcbiAgICB2YXIgY2ggPSBwb3MuY2g7XHJcbiAgICBpZiAoY2ggPT0gbnVsbCB8fCBjaCA+IGxpbmVsZW4pIHJldHVybiBQb3MocG9zLmxpbmUsIGxpbmVsZW4pO1xyXG4gICAgZWxzZSBpZiAoY2ggPCAwKSByZXR1cm4gUG9zKHBvcy5saW5lLCAwKTtcclxuICAgIGVsc2UgcmV0dXJuIHBvcztcclxuICB9XHJcbiAgZnVuY3Rpb24gaXNMaW5lKGRvYywgbCkge3JldHVybiBsID49IGRvYy5maXJzdCAmJiBsIDwgZG9jLmZpcnN0ICsgZG9jLnNpemU7fVxyXG4gIGZ1bmN0aW9uIGNsaXBQb3NBcnJheShkb2MsIGFycmF5KSB7XHJcbiAgICBmb3IgKHZhciBvdXQgPSBbXSwgaSA9IDA7IGkgPCBhcnJheS5sZW5ndGg7IGkrKykgb3V0W2ldID0gY2xpcFBvcyhkb2MsIGFycmF5W2ldKTtcclxuICAgIHJldHVybiBvdXQ7XHJcbiAgfVxyXG5cclxuICAvLyBTRUxFQ1RJT04gVVBEQVRFU1xyXG5cclxuICAvLyBUaGUgJ3Njcm9sbCcgcGFyYW1ldGVyIGdpdmVuIHRvIG1hbnkgb2YgdGhlc2UgaW5kaWNhdGVkIHdoZXRoZXJcclxuICAvLyB0aGUgbmV3IGN1cnNvciBwb3NpdGlvbiBzaG91bGQgYmUgc2Nyb2xsZWQgaW50byB2aWV3IGFmdGVyXHJcbiAgLy8gbW9kaWZ5aW5nIHRoZSBzZWxlY3Rpb24uXHJcblxyXG4gIC8vIElmIHNoaWZ0IGlzIGhlbGQgb3IgdGhlIGV4dGVuZCBmbGFnIGlzIHNldCwgZXh0ZW5kcyBhIHJhbmdlIHRvXHJcbiAgLy8gaW5jbHVkZSBhIGdpdmVuIHBvc2l0aW9uIChhbmQgb3B0aW9uYWxseSBhIHNlY29uZCBwb3NpdGlvbikuXHJcbiAgLy8gT3RoZXJ3aXNlLCBzaW1wbHkgcmV0dXJucyB0aGUgcmFuZ2UgYmV0d2VlbiB0aGUgZ2l2ZW4gcG9zaXRpb25zLlxyXG4gIC8vIFVzZWQgZm9yIGN1cnNvciBtb3Rpb24gYW5kIHN1Y2guXHJcbiAgZnVuY3Rpb24gZXh0ZW5kUmFuZ2UoZG9jLCByYW5nZSwgaGVhZCwgb3RoZXIpIHtcclxuICAgIGlmIChkb2MuY20gJiYgZG9jLmNtLmRpc3BsYXkuc2hpZnQgfHwgZG9jLmV4dGVuZCkge1xyXG4gICAgICB2YXIgYW5jaG9yID0gcmFuZ2UuYW5jaG9yO1xyXG4gICAgICBpZiAob3RoZXIpIHtcclxuICAgICAgICB2YXIgcG9zQmVmb3JlID0gY21wKGhlYWQsIGFuY2hvcikgPCAwO1xyXG4gICAgICAgIGlmIChwb3NCZWZvcmUgIT0gKGNtcChvdGhlciwgYW5jaG9yKSA8IDApKSB7XHJcbiAgICAgICAgICBhbmNob3IgPSBoZWFkO1xyXG4gICAgICAgICAgaGVhZCA9IG90aGVyO1xyXG4gICAgICAgIH0gZWxzZSBpZiAocG9zQmVmb3JlICE9IChjbXAoaGVhZCwgb3RoZXIpIDwgMCkpIHtcclxuICAgICAgICAgIGhlYWQgPSBvdGhlcjtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIG5ldyBSYW5nZShhbmNob3IsIGhlYWQpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgcmV0dXJuIG5ldyBSYW5nZShvdGhlciB8fCBoZWFkLCBoZWFkKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIEV4dGVuZCB0aGUgcHJpbWFyeSBzZWxlY3Rpb24gcmFuZ2UsIGRpc2NhcmQgdGhlIHJlc3QuXHJcbiAgZnVuY3Rpb24gZXh0ZW5kU2VsZWN0aW9uKGRvYywgaGVhZCwgb3RoZXIsIG9wdGlvbnMpIHtcclxuICAgIHNldFNlbGVjdGlvbihkb2MsIG5ldyBTZWxlY3Rpb24oW2V4dGVuZFJhbmdlKGRvYywgZG9jLnNlbC5wcmltYXJ5KCksIGhlYWQsIG90aGVyKV0sIDApLCBvcHRpb25zKTtcclxuICB9XHJcblxyXG4gIC8vIEV4dGVuZCBhbGwgc2VsZWN0aW9ucyAocG9zIGlzIGFuIGFycmF5IG9mIHNlbGVjdGlvbnMgd2l0aCBsZW5ndGhcclxuICAvLyBlcXVhbCB0aGUgbnVtYmVyIG9mIHNlbGVjdGlvbnMpXHJcbiAgZnVuY3Rpb24gZXh0ZW5kU2VsZWN0aW9ucyhkb2MsIGhlYWRzLCBvcHRpb25zKSB7XHJcbiAgICBmb3IgKHZhciBvdXQgPSBbXSwgaSA9IDA7IGkgPCBkb2Muc2VsLnJhbmdlcy5sZW5ndGg7IGkrKylcclxuICAgICAgb3V0W2ldID0gZXh0ZW5kUmFuZ2UoZG9jLCBkb2Muc2VsLnJhbmdlc1tpXSwgaGVhZHNbaV0sIG51bGwpO1xyXG4gICAgdmFyIG5ld1NlbCA9IG5vcm1hbGl6ZVNlbGVjdGlvbihvdXQsIGRvYy5zZWwucHJpbUluZGV4KTtcclxuICAgIHNldFNlbGVjdGlvbihkb2MsIG5ld1NlbCwgb3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBVcGRhdGVzIGEgc2luZ2xlIHJhbmdlIGluIHRoZSBzZWxlY3Rpb24uXHJcbiAgZnVuY3Rpb24gcmVwbGFjZU9uZVNlbGVjdGlvbihkb2MsIGksIHJhbmdlLCBvcHRpb25zKSB7XHJcbiAgICB2YXIgcmFuZ2VzID0gZG9jLnNlbC5yYW5nZXMuc2xpY2UoMCk7XHJcbiAgICByYW5nZXNbaV0gPSByYW5nZTtcclxuICAgIHNldFNlbGVjdGlvbihkb2MsIG5vcm1hbGl6ZVNlbGVjdGlvbihyYW5nZXMsIGRvYy5zZWwucHJpbUluZGV4KSwgb3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBSZXNldCB0aGUgc2VsZWN0aW9uIHRvIGEgc2luZ2xlIHJhbmdlLlxyXG4gIGZ1bmN0aW9uIHNldFNpbXBsZVNlbGVjdGlvbihkb2MsIGFuY2hvciwgaGVhZCwgb3B0aW9ucykge1xyXG4gICAgc2V0U2VsZWN0aW9uKGRvYywgc2ltcGxlU2VsZWN0aW9uKGFuY2hvciwgaGVhZCksIG9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgLy8gR2l2ZSBiZWZvcmVTZWxlY3Rpb25DaGFuZ2UgaGFuZGxlcnMgYSBjaGFuZ2UgdG8gaW5mbHVlbmNlIGFcclxuICAvLyBzZWxlY3Rpb24gdXBkYXRlLlxyXG4gIGZ1bmN0aW9uIGZpbHRlclNlbGVjdGlvbkNoYW5nZShkb2MsIHNlbCkge1xyXG4gICAgdmFyIG9iaiA9IHtcclxuICAgICAgcmFuZ2VzOiBzZWwucmFuZ2VzLFxyXG4gICAgICB1cGRhdGU6IGZ1bmN0aW9uKHJhbmdlcykge1xyXG4gICAgICAgIHRoaXMucmFuZ2VzID0gW107XHJcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCByYW5nZXMubGVuZ3RoOyBpKyspXHJcbiAgICAgICAgICB0aGlzLnJhbmdlc1tpXSA9IG5ldyBSYW5nZShjbGlwUG9zKGRvYywgcmFuZ2VzW2ldLmFuY2hvciksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGlwUG9zKGRvYywgcmFuZ2VzW2ldLmhlYWQpKTtcclxuICAgICAgfVxyXG4gICAgfTtcclxuICAgIHNpZ25hbChkb2MsIFwiYmVmb3JlU2VsZWN0aW9uQ2hhbmdlXCIsIGRvYywgb2JqKTtcclxuICAgIGlmIChkb2MuY20pIHNpZ25hbChkb2MuY20sIFwiYmVmb3JlU2VsZWN0aW9uQ2hhbmdlXCIsIGRvYy5jbSwgb2JqKTtcclxuICAgIGlmIChvYmoucmFuZ2VzICE9IHNlbC5yYW5nZXMpIHJldHVybiBub3JtYWxpemVTZWxlY3Rpb24ob2JqLnJhbmdlcywgb2JqLnJhbmdlcy5sZW5ndGggLSAxKTtcclxuICAgIGVsc2UgcmV0dXJuIHNlbDtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIHNldFNlbGVjdGlvblJlcGxhY2VIaXN0b3J5KGRvYywgc2VsLCBvcHRpb25zKSB7XHJcbiAgICB2YXIgZG9uZSA9IGRvYy5oaXN0b3J5LmRvbmUsIGxhc3QgPSBsc3QoZG9uZSk7XHJcbiAgICBpZiAobGFzdCAmJiBsYXN0LnJhbmdlcykge1xyXG4gICAgICBkb25lW2RvbmUubGVuZ3RoIC0gMV0gPSBzZWw7XHJcbiAgICAgIHNldFNlbGVjdGlvbk5vVW5kbyhkb2MsIHNlbCwgb3B0aW9ucyk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICBzZXRTZWxlY3Rpb24oZG9jLCBzZWwsIG9wdGlvbnMpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gU2V0IGEgbmV3IHNlbGVjdGlvbi5cclxuICBmdW5jdGlvbiBzZXRTZWxlY3Rpb24oZG9jLCBzZWwsIG9wdGlvbnMpIHtcclxuICAgIHNldFNlbGVjdGlvbk5vVW5kbyhkb2MsIHNlbCwgb3B0aW9ucyk7XHJcbiAgICBhZGRTZWxlY3Rpb25Ub0hpc3RvcnkoZG9jLCBkb2Muc2VsLCBkb2MuY20gPyBkb2MuY20uY3VyT3AuaWQgOiBOYU4sIG9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gc2V0U2VsZWN0aW9uTm9VbmRvKGRvYywgc2VsLCBvcHRpb25zKSB7XHJcbiAgICBpZiAoaGFzSGFuZGxlcihkb2MsIFwiYmVmb3JlU2VsZWN0aW9uQ2hhbmdlXCIpIHx8IGRvYy5jbSAmJiBoYXNIYW5kbGVyKGRvYy5jbSwgXCJiZWZvcmVTZWxlY3Rpb25DaGFuZ2VcIikpXHJcbiAgICAgIHNlbCA9IGZpbHRlclNlbGVjdGlvbkNoYW5nZShkb2MsIHNlbCk7XHJcblxyXG4gICAgdmFyIGJpYXMgPSBvcHRpb25zICYmIG9wdGlvbnMuYmlhcyB8fFxyXG4gICAgICAoY21wKHNlbC5wcmltYXJ5KCkuaGVhZCwgZG9jLnNlbC5wcmltYXJ5KCkuaGVhZCkgPCAwID8gLTEgOiAxKTtcclxuICAgIHNldFNlbGVjdGlvbklubmVyKGRvYywgc2tpcEF0b21pY0luU2VsZWN0aW9uKGRvYywgc2VsLCBiaWFzLCB0cnVlKSk7XHJcblxyXG4gICAgaWYgKCEob3B0aW9ucyAmJiBvcHRpb25zLnNjcm9sbCA9PT0gZmFsc2UpICYmIGRvYy5jbSlcclxuICAgICAgZW5zdXJlQ3Vyc29yVmlzaWJsZShkb2MuY20pO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gc2V0U2VsZWN0aW9uSW5uZXIoZG9jLCBzZWwpIHtcclxuICAgIGlmIChzZWwuZXF1YWxzKGRvYy5zZWwpKSByZXR1cm47XHJcblxyXG4gICAgZG9jLnNlbCA9IHNlbDtcclxuXHJcbiAgICBpZiAoZG9jLmNtKSB7XHJcbiAgICAgIGRvYy5jbS5jdXJPcC51cGRhdGVJbnB1dCA9IGRvYy5jbS5jdXJPcC5zZWxlY3Rpb25DaGFuZ2VkID0gdHJ1ZTtcclxuICAgICAgc2lnbmFsQ3Vyc29yQWN0aXZpdHkoZG9jLmNtKTtcclxuICAgIH1cclxuICAgIHNpZ25hbExhdGVyKGRvYywgXCJjdXJzb3JBY3Rpdml0eVwiLCBkb2MpO1xyXG4gIH1cclxuXHJcbiAgLy8gVmVyaWZ5IHRoYXQgdGhlIHNlbGVjdGlvbiBkb2VzIG5vdCBwYXJ0aWFsbHkgc2VsZWN0IGFueSBhdG9taWNcclxuICAvLyBtYXJrZWQgcmFuZ2VzLlxyXG4gIGZ1bmN0aW9uIHJlQ2hlY2tTZWxlY3Rpb24oZG9jKSB7XHJcbiAgICBzZXRTZWxlY3Rpb25Jbm5lcihkb2MsIHNraXBBdG9taWNJblNlbGVjdGlvbihkb2MsIGRvYy5zZWwsIG51bGwsIGZhbHNlKSwgc2VsX2RvbnRTY3JvbGwpO1xyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGEgc2VsZWN0aW9uIHRoYXQgZG9lcyBub3QgcGFydGlhbGx5IHNlbGVjdCBhbnkgYXRvbWljXHJcbiAgLy8gcmFuZ2VzLlxyXG4gIGZ1bmN0aW9uIHNraXBBdG9taWNJblNlbGVjdGlvbihkb2MsIHNlbCwgYmlhcywgbWF5Q2xlYXIpIHtcclxuICAgIHZhciBvdXQ7XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IHNlbC5yYW5nZXMubGVuZ3RoOyBpKyspIHtcclxuICAgICAgdmFyIHJhbmdlID0gc2VsLnJhbmdlc1tpXTtcclxuICAgICAgdmFyIG5ld0FuY2hvciA9IHNraXBBdG9taWMoZG9jLCByYW5nZS5hbmNob3IsIGJpYXMsIG1heUNsZWFyKTtcclxuICAgICAgdmFyIG5ld0hlYWQgPSBza2lwQXRvbWljKGRvYywgcmFuZ2UuaGVhZCwgYmlhcywgbWF5Q2xlYXIpO1xyXG4gICAgICBpZiAob3V0IHx8IG5ld0FuY2hvciAhPSByYW5nZS5hbmNob3IgfHwgbmV3SGVhZCAhPSByYW5nZS5oZWFkKSB7XHJcbiAgICAgICAgaWYgKCFvdXQpIG91dCA9IHNlbC5yYW5nZXMuc2xpY2UoMCwgaSk7XHJcbiAgICAgICAgb3V0W2ldID0gbmV3IFJhbmdlKG5ld0FuY2hvciwgbmV3SGVhZCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIHJldHVybiBvdXQgPyBub3JtYWxpemVTZWxlY3Rpb24ob3V0LCBzZWwucHJpbUluZGV4KSA6IHNlbDtcclxuICB9XHJcblxyXG4gIC8vIEVuc3VyZSBhIGdpdmVuIHBvc2l0aW9uIGlzIG5vdCBpbnNpZGUgYW4gYXRvbWljIHJhbmdlLlxyXG4gIGZ1bmN0aW9uIHNraXBBdG9taWMoZG9jLCBwb3MsIGJpYXMsIG1heUNsZWFyKSB7XHJcbiAgICB2YXIgZmxpcHBlZCA9IGZhbHNlLCBjdXJQb3MgPSBwb3M7XHJcbiAgICB2YXIgZGlyID0gYmlhcyB8fCAxO1xyXG4gICAgZG9jLmNhbnRFZGl0ID0gZmFsc2U7XHJcbiAgICBzZWFyY2g6IGZvciAoOzspIHtcclxuICAgICAgdmFyIGxpbmUgPSBnZXRMaW5lKGRvYywgY3VyUG9zLmxpbmUpO1xyXG4gICAgICBpZiAobGluZS5tYXJrZWRTcGFucykge1xyXG4gICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbGluZS5tYXJrZWRTcGFucy5sZW5ndGg7ICsraSkge1xyXG4gICAgICAgICAgdmFyIHNwID0gbGluZS5tYXJrZWRTcGFuc1tpXSwgbSA9IHNwLm1hcmtlcjtcclxuICAgICAgICAgIGlmICgoc3AuZnJvbSA9PSBudWxsIHx8IChtLmluY2x1c2l2ZUxlZnQgPyBzcC5mcm9tIDw9IGN1clBvcy5jaCA6IHNwLmZyb20gPCBjdXJQb3MuY2gpKSAmJlxyXG4gICAgICAgICAgICAgIChzcC50byA9PSBudWxsIHx8IChtLmluY2x1c2l2ZVJpZ2h0ID8gc3AudG8gPj0gY3VyUG9zLmNoIDogc3AudG8gPiBjdXJQb3MuY2gpKSkge1xyXG4gICAgICAgICAgICBpZiAobWF5Q2xlYXIpIHtcclxuICAgICAgICAgICAgICBzaWduYWwobSwgXCJiZWZvcmVDdXJzb3JFbnRlclwiKTtcclxuICAgICAgICAgICAgICBpZiAobS5leHBsaWNpdGx5Q2xlYXJlZCkge1xyXG4gICAgICAgICAgICAgICAgaWYgKCFsaW5lLm1hcmtlZFNwYW5zKSBicmVhaztcclxuICAgICAgICAgICAgICAgIGVsc2Ugey0taTsgY29udGludWU7fVxyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBpZiAoIW0uYXRvbWljKSBjb250aW51ZTtcclxuICAgICAgICAgICAgdmFyIG5ld1BvcyA9IG0uZmluZChkaXIgPCAwID8gLTEgOiAxKTtcclxuICAgICAgICAgICAgaWYgKGNtcChuZXdQb3MsIGN1clBvcykgPT0gMCkge1xyXG4gICAgICAgICAgICAgIG5ld1Bvcy5jaCArPSBkaXI7XHJcbiAgICAgICAgICAgICAgaWYgKG5ld1Bvcy5jaCA8IDApIHtcclxuICAgICAgICAgICAgICAgIGlmIChuZXdQb3MubGluZSA+IGRvYy5maXJzdCkgbmV3UG9zID0gY2xpcFBvcyhkb2MsIFBvcyhuZXdQb3MubGluZSAtIDEpKTtcclxuICAgICAgICAgICAgICAgIGVsc2UgbmV3UG9zID0gbnVsbDtcclxuICAgICAgICAgICAgICB9IGVsc2UgaWYgKG5ld1Bvcy5jaCA+IGxpbmUudGV4dC5sZW5ndGgpIHtcclxuICAgICAgICAgICAgICAgIGlmIChuZXdQb3MubGluZSA8IGRvYy5maXJzdCArIGRvYy5zaXplIC0gMSkgbmV3UG9zID0gUG9zKG5ld1Bvcy5saW5lICsgMSwgMCk7XHJcbiAgICAgICAgICAgICAgICBlbHNlIG5ld1BvcyA9IG51bGw7XHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgIGlmICghbmV3UG9zKSB7XHJcbiAgICAgICAgICAgICAgICBpZiAoZmxpcHBlZCkge1xyXG4gICAgICAgICAgICAgICAgICAvLyBEcml2ZW4gaW4gYSBjb3JuZXIgLS0gbm8gdmFsaWQgY3Vyc29yIHBvc2l0aW9uIGZvdW5kIGF0IGFsbFxyXG4gICAgICAgICAgICAgICAgICAvLyAtLSB0cnkgYWdhaW4gKndpdGgqIGNsZWFyaW5nLCBpZiB3ZSBkaWRuJ3QgYWxyZWFkeVxyXG4gICAgICAgICAgICAgICAgICBpZiAoIW1heUNsZWFyKSByZXR1cm4gc2tpcEF0b21pYyhkb2MsIHBvcywgYmlhcywgdHJ1ZSk7XHJcbiAgICAgICAgICAgICAgICAgIC8vIE90aGVyd2lzZSwgdHVybiBvZmYgZWRpdGluZyB1bnRpbCBmdXJ0aGVyIG5vdGljZSwgYW5kIHJldHVybiB0aGUgc3RhcnQgb2YgdGhlIGRvY1xyXG4gICAgICAgICAgICAgICAgICBkb2MuY2FudEVkaXQgPSB0cnVlO1xyXG4gICAgICAgICAgICAgICAgICByZXR1cm4gUG9zKGRvYy5maXJzdCwgMCk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBmbGlwcGVkID0gdHJ1ZTsgbmV3UG9zID0gcG9zOyBkaXIgPSAtZGlyO1xyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBjdXJQb3MgPSBuZXdQb3M7XHJcbiAgICAgICAgICAgIGNvbnRpbnVlIHNlYXJjaDtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIGN1clBvcztcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFNFTEVDVElPTiBEUkFXSU5HXHJcblxyXG4gIGZ1bmN0aW9uIHVwZGF0ZVNlbGVjdGlvbihjbSkge1xyXG4gICAgY20uZGlzcGxheS5pbnB1dC5zaG93U2VsZWN0aW9uKGNtLmRpc3BsYXkuaW5wdXQucHJlcGFyZVNlbGVjdGlvbigpKTtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIHByZXBhcmVTZWxlY3Rpb24oY20sIHByaW1hcnkpIHtcclxuICAgIHZhciBkb2MgPSBjbS5kb2MsIHJlc3VsdCA9IHt9O1xyXG4gICAgdmFyIGN1ckZyYWdtZW50ID0gcmVzdWx0LmN1cnNvcnMgPSBkb2N1bWVudC5jcmVhdGVEb2N1bWVudEZyYWdtZW50KCk7XHJcbiAgICB2YXIgc2VsRnJhZ21lbnQgPSByZXN1bHQuc2VsZWN0aW9uID0gZG9jdW1lbnQuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpO1xyXG5cclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgZG9jLnNlbC5yYW5nZXMubGVuZ3RoOyBpKyspIHtcclxuICAgICAgaWYgKHByaW1hcnkgPT09IGZhbHNlICYmIGkgPT0gZG9jLnNlbC5wcmltSW5kZXgpIGNvbnRpbnVlO1xyXG4gICAgICB2YXIgcmFuZ2UgPSBkb2Muc2VsLnJhbmdlc1tpXTtcclxuICAgICAgdmFyIGNvbGxhcHNlZCA9IHJhbmdlLmVtcHR5KCk7XHJcbiAgICAgIGlmIChjb2xsYXBzZWQgfHwgY20ub3B0aW9ucy5zaG93Q3Vyc29yV2hlblNlbGVjdGluZylcclxuICAgICAgICBkcmF3U2VsZWN0aW9uQ3Vyc29yKGNtLCByYW5nZS5oZWFkLCBjdXJGcmFnbWVudCk7XHJcbiAgICAgIGlmICghY29sbGFwc2VkKVxyXG4gICAgICAgIGRyYXdTZWxlY3Rpb25SYW5nZShjbSwgcmFuZ2UsIHNlbEZyYWdtZW50KTtcclxuICAgIH1cclxuICAgIHJldHVybiByZXN1bHQ7XHJcbiAgfVxyXG5cclxuICAvLyBEcmF3cyBhIGN1cnNvciBmb3IgdGhlIGdpdmVuIHJhbmdlXHJcbiAgZnVuY3Rpb24gZHJhd1NlbGVjdGlvbkN1cnNvcihjbSwgaGVhZCwgb3V0cHV0KSB7XHJcbiAgICB2YXIgcG9zID0gY3Vyc29yQ29vcmRzKGNtLCBoZWFkLCBcImRpdlwiLCBudWxsLCBudWxsLCAhY20ub3B0aW9ucy5zaW5nbGVDdXJzb3JIZWlnaHRQZXJMaW5lKTtcclxuXHJcbiAgICB2YXIgY3Vyc29yID0gb3V0cHV0LmFwcGVuZENoaWxkKGVsdChcImRpdlwiLCBcIlxcdTAwYTBcIiwgXCJDb2RlTWlycm9yLWN1cnNvclwiKSk7XHJcbiAgICBjdXJzb3Iuc3R5bGUubGVmdCA9IHBvcy5sZWZ0ICsgXCJweFwiO1xyXG4gICAgY3Vyc29yLnN0eWxlLnRvcCA9IHBvcy50b3AgKyBcInB4XCI7XHJcbiAgICBjdXJzb3Iuc3R5bGUuaGVpZ2h0ID0gTWF0aC5tYXgoMCwgcG9zLmJvdHRvbSAtIHBvcy50b3ApICogY20ub3B0aW9ucy5jdXJzb3JIZWlnaHQgKyBcInB4XCI7XHJcblxyXG4gICAgaWYgKHBvcy5vdGhlcikge1xyXG4gICAgICAvLyBTZWNvbmRhcnkgY3Vyc29yLCBzaG93biB3aGVuIG9uIGEgJ2p1bXAnIGluIGJpLWRpcmVjdGlvbmFsIHRleHRcclxuICAgICAgdmFyIG90aGVyQ3Vyc29yID0gb3V0cHV0LmFwcGVuZENoaWxkKGVsdChcImRpdlwiLCBcIlxcdTAwYTBcIiwgXCJDb2RlTWlycm9yLWN1cnNvciBDb2RlTWlycm9yLXNlY29uZGFyeWN1cnNvclwiKSk7XHJcbiAgICAgIG90aGVyQ3Vyc29yLnN0eWxlLmRpc3BsYXkgPSBcIlwiO1xyXG4gICAgICBvdGhlckN1cnNvci5zdHlsZS5sZWZ0ID0gcG9zLm90aGVyLmxlZnQgKyBcInB4XCI7XHJcbiAgICAgIG90aGVyQ3Vyc29yLnN0eWxlLnRvcCA9IHBvcy5vdGhlci50b3AgKyBcInB4XCI7XHJcbiAgICAgIG90aGVyQ3Vyc29yLnN0eWxlLmhlaWdodCA9IChwb3Mub3RoZXIuYm90dG9tIC0gcG9zLm90aGVyLnRvcCkgKiAuODUgKyBcInB4XCI7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBEcmF3cyB0aGUgZ2l2ZW4gcmFuZ2UgYXMgYSBoaWdobGlnaHRlZCBzZWxlY3Rpb25cclxuICBmdW5jdGlvbiBkcmF3U2VsZWN0aW9uUmFuZ2UoY20sIHJhbmdlLCBvdXRwdXQpIHtcclxuICAgIHZhciBkaXNwbGF5ID0gY20uZGlzcGxheSwgZG9jID0gY20uZG9jO1xyXG4gICAgdmFyIGZyYWdtZW50ID0gZG9jdW1lbnQuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpO1xyXG4gICAgdmFyIHBhZGRpbmcgPSBwYWRkaW5nSChjbS5kaXNwbGF5KSwgbGVmdFNpZGUgPSBwYWRkaW5nLmxlZnQ7XHJcbiAgICB2YXIgcmlnaHRTaWRlID0gTWF0aC5tYXgoZGlzcGxheS5zaXplcldpZHRoLCBkaXNwbGF5V2lkdGgoY20pIC0gZGlzcGxheS5zaXplci5vZmZzZXRMZWZ0KSAtIHBhZGRpbmcucmlnaHQ7XHJcblxyXG4gICAgZnVuY3Rpb24gYWRkKGxlZnQsIHRvcCwgd2lkdGgsIGJvdHRvbSkge1xyXG4gICAgICBpZiAodG9wIDwgMCkgdG9wID0gMDtcclxuICAgICAgdG9wID0gTWF0aC5yb3VuZCh0b3ApO1xyXG4gICAgICBib3R0b20gPSBNYXRoLnJvdW5kKGJvdHRvbSk7XHJcbiAgICAgIGZyYWdtZW50LmFwcGVuZENoaWxkKGVsdChcImRpdlwiLCBudWxsLCBcIkNvZGVNaXJyb3Itc2VsZWN0ZWRcIiwgXCJwb3NpdGlvbjogYWJzb2x1dGU7IGxlZnQ6IFwiICsgbGVmdCArXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcInB4OyB0b3A6IFwiICsgdG9wICsgXCJweDsgd2lkdGg6IFwiICsgKHdpZHRoID09IG51bGwgPyByaWdodFNpZGUgLSBsZWZ0IDogd2lkdGgpICtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwicHg7IGhlaWdodDogXCIgKyAoYm90dG9tIC0gdG9wKSArIFwicHhcIikpO1xyXG4gICAgfVxyXG5cclxuICAgIGZ1bmN0aW9uIGRyYXdGb3JMaW5lKGxpbmUsIGZyb21BcmcsIHRvQXJnKSB7XHJcbiAgICAgIHZhciBsaW5lT2JqID0gZ2V0TGluZShkb2MsIGxpbmUpO1xyXG4gICAgICB2YXIgbGluZUxlbiA9IGxpbmVPYmoudGV4dC5sZW5ndGg7XHJcbiAgICAgIHZhciBzdGFydCwgZW5kO1xyXG4gICAgICBmdW5jdGlvbiBjb29yZHMoY2gsIGJpYXMpIHtcclxuICAgICAgICByZXR1cm4gY2hhckNvb3JkcyhjbSwgUG9zKGxpbmUsIGNoKSwgXCJkaXZcIiwgbGluZU9iaiwgYmlhcyk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGl0ZXJhdGVCaWRpU2VjdGlvbnMoZ2V0T3JkZXIobGluZU9iaiksIGZyb21BcmcgfHwgMCwgdG9BcmcgPT0gbnVsbCA/IGxpbmVMZW4gOiB0b0FyZywgZnVuY3Rpb24oZnJvbSwgdG8sIGRpcikge1xyXG4gICAgICAgIHZhciBsZWZ0UG9zID0gY29vcmRzKGZyb20sIFwibGVmdFwiKSwgcmlnaHRQb3MsIGxlZnQsIHJpZ2h0O1xyXG4gICAgICAgIGlmIChmcm9tID09IHRvKSB7XHJcbiAgICAgICAgICByaWdodFBvcyA9IGxlZnRQb3M7XHJcbiAgICAgICAgICBsZWZ0ID0gcmlnaHQgPSBsZWZ0UG9zLmxlZnQ7XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgIHJpZ2h0UG9zID0gY29vcmRzKHRvIC0gMSwgXCJyaWdodFwiKTtcclxuICAgICAgICAgIGlmIChkaXIgPT0gXCJydGxcIikgeyB2YXIgdG1wID0gbGVmdFBvczsgbGVmdFBvcyA9IHJpZ2h0UG9zOyByaWdodFBvcyA9IHRtcDsgfVxyXG4gICAgICAgICAgbGVmdCA9IGxlZnRQb3MubGVmdDtcclxuICAgICAgICAgIHJpZ2h0ID0gcmlnaHRQb3MucmlnaHQ7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmIChmcm9tQXJnID09IG51bGwgJiYgZnJvbSA9PSAwKSBsZWZ0ID0gbGVmdFNpZGU7XHJcbiAgICAgICAgaWYgKHJpZ2h0UG9zLnRvcCAtIGxlZnRQb3MudG9wID4gMykgeyAvLyBEaWZmZXJlbnQgbGluZXMsIGRyYXcgdG9wIHBhcnRcclxuICAgICAgICAgIGFkZChsZWZ0LCBsZWZ0UG9zLnRvcCwgbnVsbCwgbGVmdFBvcy5ib3R0b20pO1xyXG4gICAgICAgICAgbGVmdCA9IGxlZnRTaWRlO1xyXG4gICAgICAgICAgaWYgKGxlZnRQb3MuYm90dG9tIDwgcmlnaHRQb3MudG9wKSBhZGQobGVmdCwgbGVmdFBvcy5ib3R0b20sIG51bGwsIHJpZ2h0UG9zLnRvcCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmICh0b0FyZyA9PSBudWxsICYmIHRvID09IGxpbmVMZW4pIHJpZ2h0ID0gcmlnaHRTaWRlO1xyXG4gICAgICAgIGlmICghc3RhcnQgfHwgbGVmdFBvcy50b3AgPCBzdGFydC50b3AgfHwgbGVmdFBvcy50b3AgPT0gc3RhcnQudG9wICYmIGxlZnRQb3MubGVmdCA8IHN0YXJ0LmxlZnQpXHJcbiAgICAgICAgICBzdGFydCA9IGxlZnRQb3M7XHJcbiAgICAgICAgaWYgKCFlbmQgfHwgcmlnaHRQb3MuYm90dG9tID4gZW5kLmJvdHRvbSB8fCByaWdodFBvcy5ib3R0b20gPT0gZW5kLmJvdHRvbSAmJiByaWdodFBvcy5yaWdodCA+IGVuZC5yaWdodClcclxuICAgICAgICAgIGVuZCA9IHJpZ2h0UG9zO1xyXG4gICAgICAgIGlmIChsZWZ0IDwgbGVmdFNpZGUgKyAxKSBsZWZ0ID0gbGVmdFNpZGU7XHJcbiAgICAgICAgYWRkKGxlZnQsIHJpZ2h0UG9zLnRvcCwgcmlnaHQgLSBsZWZ0LCByaWdodFBvcy5ib3R0b20pO1xyXG4gICAgICB9KTtcclxuICAgICAgcmV0dXJuIHtzdGFydDogc3RhcnQsIGVuZDogZW5kfTtcclxuICAgIH1cclxuXHJcbiAgICB2YXIgc0Zyb20gPSByYW5nZS5mcm9tKCksIHNUbyA9IHJhbmdlLnRvKCk7XHJcbiAgICBpZiAoc0Zyb20ubGluZSA9PSBzVG8ubGluZSkge1xyXG4gICAgICBkcmF3Rm9yTGluZShzRnJvbS5saW5lLCBzRnJvbS5jaCwgc1RvLmNoKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIHZhciBmcm9tTGluZSA9IGdldExpbmUoZG9jLCBzRnJvbS5saW5lKSwgdG9MaW5lID0gZ2V0TGluZShkb2MsIHNUby5saW5lKTtcclxuICAgICAgdmFyIHNpbmdsZVZMaW5lID0gdmlzdWFsTGluZShmcm9tTGluZSkgPT0gdmlzdWFsTGluZSh0b0xpbmUpO1xyXG4gICAgICB2YXIgbGVmdEVuZCA9IGRyYXdGb3JMaW5lKHNGcm9tLmxpbmUsIHNGcm9tLmNoLCBzaW5nbGVWTGluZSA/IGZyb21MaW5lLnRleHQubGVuZ3RoICsgMSA6IG51bGwpLmVuZDtcclxuICAgICAgdmFyIHJpZ2h0U3RhcnQgPSBkcmF3Rm9yTGluZShzVG8ubGluZSwgc2luZ2xlVkxpbmUgPyAwIDogbnVsbCwgc1RvLmNoKS5zdGFydDtcclxuICAgICAgaWYgKHNpbmdsZVZMaW5lKSB7XHJcbiAgICAgICAgaWYgKGxlZnRFbmQudG9wIDwgcmlnaHRTdGFydC50b3AgLSAyKSB7XHJcbiAgICAgICAgICBhZGQobGVmdEVuZC5yaWdodCwgbGVmdEVuZC50b3AsIG51bGwsIGxlZnRFbmQuYm90dG9tKTtcclxuICAgICAgICAgIGFkZChsZWZ0U2lkZSwgcmlnaHRTdGFydC50b3AsIHJpZ2h0U3RhcnQubGVmdCwgcmlnaHRTdGFydC5ib3R0b20pO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICBhZGQobGVmdEVuZC5yaWdodCwgbGVmdEVuZC50b3AsIHJpZ2h0U3RhcnQubGVmdCAtIGxlZnRFbmQucmlnaHQsIGxlZnRFbmQuYm90dG9tKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICAgaWYgKGxlZnRFbmQuYm90dG9tIDwgcmlnaHRTdGFydC50b3ApXHJcbiAgICAgICAgYWRkKGxlZnRTaWRlLCBsZWZ0RW5kLmJvdHRvbSwgbnVsbCwgcmlnaHRTdGFydC50b3ApO1xyXG4gICAgfVxyXG5cclxuICAgIG91dHB1dC5hcHBlbmRDaGlsZChmcmFnbWVudCk7XHJcbiAgfVxyXG5cclxuICAvLyBDdXJzb3ItYmxpbmtpbmdcclxuICBmdW5jdGlvbiByZXN0YXJ0QmxpbmsoY20pIHtcclxuICAgIGlmICghY20uc3RhdGUuZm9jdXNlZCkgcmV0dXJuO1xyXG4gICAgdmFyIGRpc3BsYXkgPSBjbS5kaXNwbGF5O1xyXG4gICAgY2xlYXJJbnRlcnZhbChkaXNwbGF5LmJsaW5rZXIpO1xyXG4gICAgdmFyIG9uID0gdHJ1ZTtcclxuICAgIGRpc3BsYXkuY3Vyc29yRGl2LnN0eWxlLnZpc2liaWxpdHkgPSBcIlwiO1xyXG4gICAgaWYgKGNtLm9wdGlvbnMuY3Vyc29yQmxpbmtSYXRlID4gMClcclxuICAgICAgZGlzcGxheS5ibGlua2VyID0gc2V0SW50ZXJ2YWwoZnVuY3Rpb24oKSB7XHJcbiAgICAgICAgZGlzcGxheS5jdXJzb3JEaXYuc3R5bGUudmlzaWJpbGl0eSA9IChvbiA9ICFvbikgPyBcIlwiIDogXCJoaWRkZW5cIjtcclxuICAgICAgfSwgY20ub3B0aW9ucy5jdXJzb3JCbGlua1JhdGUpO1xyXG4gICAgZWxzZSBpZiAoY20ub3B0aW9ucy5jdXJzb3JCbGlua1JhdGUgPCAwKVxyXG4gICAgICBkaXNwbGF5LmN1cnNvckRpdi5zdHlsZS52aXNpYmlsaXR5ID0gXCJoaWRkZW5cIjtcclxuICB9XHJcblxyXG4gIC8vIEhJR0hMSUdIVCBXT1JLRVJcclxuXHJcbiAgZnVuY3Rpb24gc3RhcnRXb3JrZXIoY20sIHRpbWUpIHtcclxuICAgIGlmIChjbS5kb2MubW9kZS5zdGFydFN0YXRlICYmIGNtLmRvYy5mcm9udGllciA8IGNtLmRpc3BsYXkudmlld1RvKVxyXG4gICAgICBjbS5zdGF0ZS5oaWdobGlnaHQuc2V0KHRpbWUsIGJpbmQoaGlnaGxpZ2h0V29ya2VyLCBjbSkpO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gaGlnaGxpZ2h0V29ya2VyKGNtKSB7XHJcbiAgICB2YXIgZG9jID0gY20uZG9jO1xyXG4gICAgaWYgKGRvYy5mcm9udGllciA8IGRvYy5maXJzdCkgZG9jLmZyb250aWVyID0gZG9jLmZpcnN0O1xyXG4gICAgaWYgKGRvYy5mcm9udGllciA+PSBjbS5kaXNwbGF5LnZpZXdUbykgcmV0dXJuO1xyXG4gICAgdmFyIGVuZCA9ICtuZXcgRGF0ZSArIGNtLm9wdGlvbnMud29ya1RpbWU7XHJcbiAgICB2YXIgc3RhdGUgPSBjb3B5U3RhdGUoZG9jLm1vZGUsIGdldFN0YXRlQmVmb3JlKGNtLCBkb2MuZnJvbnRpZXIpKTtcclxuICAgIHZhciBjaGFuZ2VkTGluZXMgPSBbXTtcclxuXHJcbiAgICBkb2MuaXRlcihkb2MuZnJvbnRpZXIsIE1hdGgubWluKGRvYy5maXJzdCArIGRvYy5zaXplLCBjbS5kaXNwbGF5LnZpZXdUbyArIDUwMCksIGZ1bmN0aW9uKGxpbmUpIHtcclxuICAgICAgaWYgKGRvYy5mcm9udGllciA+PSBjbS5kaXNwbGF5LnZpZXdGcm9tKSB7IC8vIFZpc2libGVcclxuICAgICAgICB2YXIgb2xkU3R5bGVzID0gbGluZS5zdHlsZXMsIHRvb0xvbmcgPSBsaW5lLnRleHQubGVuZ3RoID4gY20ub3B0aW9ucy5tYXhIaWdobGlnaHRMZW5ndGg7XHJcbiAgICAgICAgdmFyIGhpZ2hsaWdodGVkID0gaGlnaGxpZ2h0TGluZShjbSwgbGluZSwgdG9vTG9uZyA/IGNvcHlTdGF0ZShkb2MubW9kZSwgc3RhdGUpIDogc3RhdGUsIHRydWUpO1xyXG4gICAgICAgIGxpbmUuc3R5bGVzID0gaGlnaGxpZ2h0ZWQuc3R5bGVzO1xyXG4gICAgICAgIHZhciBvbGRDbHMgPSBsaW5lLnN0eWxlQ2xhc3NlcywgbmV3Q2xzID0gaGlnaGxpZ2h0ZWQuY2xhc3NlcztcclxuICAgICAgICBpZiAobmV3Q2xzKSBsaW5lLnN0eWxlQ2xhc3NlcyA9IG5ld0NscztcclxuICAgICAgICBlbHNlIGlmIChvbGRDbHMpIGxpbmUuc3R5bGVDbGFzc2VzID0gbnVsbDtcclxuICAgICAgICB2YXIgaXNjaGFuZ2UgPSAhb2xkU3R5bGVzIHx8IG9sZFN0eWxlcy5sZW5ndGggIT0gbGluZS5zdHlsZXMubGVuZ3RoIHx8XHJcbiAgICAgICAgICBvbGRDbHMgIT0gbmV3Q2xzICYmICghb2xkQ2xzIHx8ICFuZXdDbHMgfHwgb2xkQ2xzLmJnQ2xhc3MgIT0gbmV3Q2xzLmJnQ2xhc3MgfHwgb2xkQ2xzLnRleHRDbGFzcyAhPSBuZXdDbHMudGV4dENsYXNzKTtcclxuICAgICAgICBmb3IgKHZhciBpID0gMDsgIWlzY2hhbmdlICYmIGkgPCBvbGRTdHlsZXMubGVuZ3RoOyArK2kpIGlzY2hhbmdlID0gb2xkU3R5bGVzW2ldICE9IGxpbmUuc3R5bGVzW2ldO1xyXG4gICAgICAgIGlmIChpc2NoYW5nZSkgY2hhbmdlZExpbmVzLnB1c2goZG9jLmZyb250aWVyKTtcclxuICAgICAgICBsaW5lLnN0YXRlQWZ0ZXIgPSB0b29Mb25nID8gc3RhdGUgOiBjb3B5U3RhdGUoZG9jLm1vZGUsIHN0YXRlKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBpZiAobGluZS50ZXh0Lmxlbmd0aCA8PSBjbS5vcHRpb25zLm1heEhpZ2hsaWdodExlbmd0aClcclxuICAgICAgICAgIHByb2Nlc3NMaW5lKGNtLCBsaW5lLnRleHQsIHN0YXRlKTtcclxuICAgICAgICBsaW5lLnN0YXRlQWZ0ZXIgPSBkb2MuZnJvbnRpZXIgJSA1ID09IDAgPyBjb3B5U3RhdGUoZG9jLm1vZGUsIHN0YXRlKSA6IG51bGw7XHJcbiAgICAgIH1cclxuICAgICAgKytkb2MuZnJvbnRpZXI7XHJcbiAgICAgIGlmICgrbmV3IERhdGUgPiBlbmQpIHtcclxuICAgICAgICBzdGFydFdvcmtlcihjbSwgY20ub3B0aW9ucy53b3JrRGVsYXkpO1xyXG4gICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICAgIGlmIChjaGFuZ2VkTGluZXMubGVuZ3RoKSBydW5Jbk9wKGNtLCBmdW5jdGlvbigpIHtcclxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBjaGFuZ2VkTGluZXMubGVuZ3RoOyBpKyspXHJcbiAgICAgICAgcmVnTGluZUNoYW5nZShjbSwgY2hhbmdlZExpbmVzW2ldLCBcInRleHRcIik7XHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIC8vIEZpbmRzIHRoZSBsaW5lIHRvIHN0YXJ0IHdpdGggd2hlbiBzdGFydGluZyBhIHBhcnNlLiBUcmllcyB0b1xyXG4gIC8vIGZpbmQgYSBsaW5lIHdpdGggYSBzdGF0ZUFmdGVyLCBzbyB0aGF0IGl0IGNhbiBzdGFydCB3aXRoIGFcclxuICAvLyB2YWxpZCBzdGF0ZS4gSWYgdGhhdCBmYWlscywgaXQgcmV0dXJucyB0aGUgbGluZSB3aXRoIHRoZVxyXG4gIC8vIHNtYWxsZXN0IGluZGVudGF0aW9uLCB3aGljaCB0ZW5kcyB0byBuZWVkIHRoZSBsZWFzdCBjb250ZXh0IHRvXHJcbiAgLy8gcGFyc2UgY29ycmVjdGx5LlxyXG4gIGZ1bmN0aW9uIGZpbmRTdGFydExpbmUoY20sIG4sIHByZWNpc2UpIHtcclxuICAgIHZhciBtaW5pbmRlbnQsIG1pbmxpbmUsIGRvYyA9IGNtLmRvYztcclxuICAgIHZhciBsaW0gPSBwcmVjaXNlID8gLTEgOiBuIC0gKGNtLmRvYy5tb2RlLmlubmVyTW9kZSA/IDEwMDAgOiAxMDApO1xyXG4gICAgZm9yICh2YXIgc2VhcmNoID0gbjsgc2VhcmNoID4gbGltOyAtLXNlYXJjaCkge1xyXG4gICAgICBpZiAoc2VhcmNoIDw9IGRvYy5maXJzdCkgcmV0dXJuIGRvYy5maXJzdDtcclxuICAgICAgdmFyIGxpbmUgPSBnZXRMaW5lKGRvYywgc2VhcmNoIC0gMSk7XHJcbiAgICAgIGlmIChsaW5lLnN0YXRlQWZ0ZXIgJiYgKCFwcmVjaXNlIHx8IHNlYXJjaCA8PSBkb2MuZnJvbnRpZXIpKSByZXR1cm4gc2VhcmNoO1xyXG4gICAgICB2YXIgaW5kZW50ZWQgPSBjb3VudENvbHVtbihsaW5lLnRleHQsIG51bGwsIGNtLm9wdGlvbnMudGFiU2l6ZSk7XHJcbiAgICAgIGlmIChtaW5saW5lID09IG51bGwgfHwgbWluaW5kZW50ID4gaW5kZW50ZWQpIHtcclxuICAgICAgICBtaW5saW5lID0gc2VhcmNoIC0gMTtcclxuICAgICAgICBtaW5pbmRlbnQgPSBpbmRlbnRlZDtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgcmV0dXJuIG1pbmxpbmU7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBnZXRTdGF0ZUJlZm9yZShjbSwgbiwgcHJlY2lzZSkge1xyXG4gICAgdmFyIGRvYyA9IGNtLmRvYywgZGlzcGxheSA9IGNtLmRpc3BsYXk7XHJcbiAgICBpZiAoIWRvYy5tb2RlLnN0YXJ0U3RhdGUpIHJldHVybiB0cnVlO1xyXG4gICAgdmFyIHBvcyA9IGZpbmRTdGFydExpbmUoY20sIG4sIHByZWNpc2UpLCBzdGF0ZSA9IHBvcyA+IGRvYy5maXJzdCAmJiBnZXRMaW5lKGRvYywgcG9zLTEpLnN0YXRlQWZ0ZXI7XHJcbiAgICBpZiAoIXN0YXRlKSBzdGF0ZSA9IHN0YXJ0U3RhdGUoZG9jLm1vZGUpO1xyXG4gICAgZWxzZSBzdGF0ZSA9IGNvcHlTdGF0ZShkb2MubW9kZSwgc3RhdGUpO1xyXG4gICAgZG9jLml0ZXIocG9zLCBuLCBmdW5jdGlvbihsaW5lKSB7XHJcbiAgICAgIHByb2Nlc3NMaW5lKGNtLCBsaW5lLnRleHQsIHN0YXRlKTtcclxuICAgICAgdmFyIHNhdmUgPSBwb3MgPT0gbiAtIDEgfHwgcG9zICUgNSA9PSAwIHx8IHBvcyA+PSBkaXNwbGF5LnZpZXdGcm9tICYmIHBvcyA8IGRpc3BsYXkudmlld1RvO1xyXG4gICAgICBsaW5lLnN0YXRlQWZ0ZXIgPSBzYXZlID8gY29weVN0YXRlKGRvYy5tb2RlLCBzdGF0ZSkgOiBudWxsO1xyXG4gICAgICArK3BvcztcclxuICAgIH0pO1xyXG4gICAgaWYgKHByZWNpc2UpIGRvYy5mcm9udGllciA9IHBvcztcclxuICAgIHJldHVybiBzdGF0ZTtcclxuICB9XHJcblxyXG4gIC8vIFBPU0lUSU9OIE1FQVNVUkVNRU5UXHJcblxyXG4gIGZ1bmN0aW9uIHBhZGRpbmdUb3AoZGlzcGxheSkge3JldHVybiBkaXNwbGF5LmxpbmVTcGFjZS5vZmZzZXRUb3A7fVxyXG4gIGZ1bmN0aW9uIHBhZGRpbmdWZXJ0KGRpc3BsYXkpIHtyZXR1cm4gZGlzcGxheS5tb3Zlci5vZmZzZXRIZWlnaHQgLSBkaXNwbGF5LmxpbmVTcGFjZS5vZmZzZXRIZWlnaHQ7fVxyXG4gIGZ1bmN0aW9uIHBhZGRpbmdIKGRpc3BsYXkpIHtcclxuICAgIGlmIChkaXNwbGF5LmNhY2hlZFBhZGRpbmdIKSByZXR1cm4gZGlzcGxheS5jYWNoZWRQYWRkaW5nSDtcclxuICAgIHZhciBlID0gcmVtb3ZlQ2hpbGRyZW5BbmRBZGQoZGlzcGxheS5tZWFzdXJlLCBlbHQoXCJwcmVcIiwgXCJ4XCIpKTtcclxuICAgIHZhciBzdHlsZSA9IHdpbmRvdy5nZXRDb21wdXRlZFN0eWxlID8gd2luZG93LmdldENvbXB1dGVkU3R5bGUoZSkgOiBlLmN1cnJlbnRTdHlsZTtcclxuICAgIHZhciBkYXRhID0ge2xlZnQ6IHBhcnNlSW50KHN0eWxlLnBhZGRpbmdMZWZ0KSwgcmlnaHQ6IHBhcnNlSW50KHN0eWxlLnBhZGRpbmdSaWdodCl9O1xyXG4gICAgaWYgKCFpc05hTihkYXRhLmxlZnQpICYmICFpc05hTihkYXRhLnJpZ2h0KSkgZGlzcGxheS5jYWNoZWRQYWRkaW5nSCA9IGRhdGE7XHJcbiAgICByZXR1cm4gZGF0YTtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIHNjcm9sbEdhcChjbSkgeyByZXR1cm4gc2Nyb2xsZXJHYXAgLSBjbS5kaXNwbGF5Lm5hdGl2ZUJhcldpZHRoOyB9XHJcbiAgZnVuY3Rpb24gZGlzcGxheVdpZHRoKGNtKSB7XHJcbiAgICByZXR1cm4gY20uZGlzcGxheS5zY3JvbGxlci5jbGllbnRXaWR0aCAtIHNjcm9sbEdhcChjbSkgLSBjbS5kaXNwbGF5LmJhcldpZHRoO1xyXG4gIH1cclxuICBmdW5jdGlvbiBkaXNwbGF5SGVpZ2h0KGNtKSB7XHJcbiAgICByZXR1cm4gY20uZGlzcGxheS5zY3JvbGxlci5jbGllbnRIZWlnaHQgLSBzY3JvbGxHYXAoY20pIC0gY20uZGlzcGxheS5iYXJIZWlnaHQ7XHJcbiAgfVxyXG5cclxuICAvLyBFbnN1cmUgdGhlIGxpbmVWaWV3LndyYXBwaW5nLmhlaWdodHMgYXJyYXkgaXMgcG9wdWxhdGVkLiBUaGlzIGlzXHJcbiAgLy8gYW4gYXJyYXkgb2YgYm90dG9tIG9mZnNldHMgZm9yIHRoZSBsaW5lcyB0aGF0IG1ha2UgdXAgYSBkcmF3blxyXG4gIC8vIGxpbmUuIFdoZW4gbGluZVdyYXBwaW5nIGlzIG9uLCB0aGVyZSBtaWdodCBiZSBtb3JlIHRoYW4gb25lXHJcbiAgLy8gaGVpZ2h0LlxyXG4gIGZ1bmN0aW9uIGVuc3VyZUxpbmVIZWlnaHRzKGNtLCBsaW5lVmlldywgcmVjdCkge1xyXG4gICAgdmFyIHdyYXBwaW5nID0gY20ub3B0aW9ucy5saW5lV3JhcHBpbmc7XHJcbiAgICB2YXIgY3VyV2lkdGggPSB3cmFwcGluZyAmJiBkaXNwbGF5V2lkdGgoY20pO1xyXG4gICAgaWYgKCFsaW5lVmlldy5tZWFzdXJlLmhlaWdodHMgfHwgd3JhcHBpbmcgJiYgbGluZVZpZXcubWVhc3VyZS53aWR0aCAhPSBjdXJXaWR0aCkge1xyXG4gICAgICB2YXIgaGVpZ2h0cyA9IGxpbmVWaWV3Lm1lYXN1cmUuaGVpZ2h0cyA9IFtdO1xyXG4gICAgICBpZiAod3JhcHBpbmcpIHtcclxuICAgICAgICBsaW5lVmlldy5tZWFzdXJlLndpZHRoID0gY3VyV2lkdGg7XHJcbiAgICAgICAgdmFyIHJlY3RzID0gbGluZVZpZXcudGV4dC5maXJzdENoaWxkLmdldENsaWVudFJlY3RzKCk7XHJcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCByZWN0cy5sZW5ndGggLSAxOyBpKyspIHtcclxuICAgICAgICAgIHZhciBjdXIgPSByZWN0c1tpXSwgbmV4dCA9IHJlY3RzW2kgKyAxXTtcclxuICAgICAgICAgIGlmIChNYXRoLmFicyhjdXIuYm90dG9tIC0gbmV4dC5ib3R0b20pID4gMilcclxuICAgICAgICAgICAgaGVpZ2h0cy5wdXNoKChjdXIuYm90dG9tICsgbmV4dC50b3ApIC8gMiAtIHJlY3QudG9wKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICAgaGVpZ2h0cy5wdXNoKHJlY3QuYm90dG9tIC0gcmVjdC50b3ApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gRmluZCBhIGxpbmUgbWFwIChtYXBwaW5nIGNoYXJhY3RlciBvZmZzZXRzIHRvIHRleHQgbm9kZXMpIGFuZCBhXHJcbiAgLy8gbWVhc3VyZW1lbnQgY2FjaGUgZm9yIHRoZSBnaXZlbiBsaW5lIG51bWJlci4gKEEgbGluZSB2aWV3IG1pZ2h0XHJcbiAgLy8gY29udGFpbiBtdWx0aXBsZSBsaW5lcyB3aGVuIGNvbGxhcHNlZCByYW5nZXMgYXJlIHByZXNlbnQuKVxyXG4gIGZ1bmN0aW9uIG1hcEZyb21MaW5lVmlldyhsaW5lVmlldywgbGluZSwgbGluZU4pIHtcclxuICAgIGlmIChsaW5lVmlldy5saW5lID09IGxpbmUpXHJcbiAgICAgIHJldHVybiB7bWFwOiBsaW5lVmlldy5tZWFzdXJlLm1hcCwgY2FjaGU6IGxpbmVWaWV3Lm1lYXN1cmUuY2FjaGV9O1xyXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsaW5lVmlldy5yZXN0Lmxlbmd0aDsgaSsrKVxyXG4gICAgICBpZiAobGluZVZpZXcucmVzdFtpXSA9PSBsaW5lKVxyXG4gICAgICAgIHJldHVybiB7bWFwOiBsaW5lVmlldy5tZWFzdXJlLm1hcHNbaV0sIGNhY2hlOiBsaW5lVmlldy5tZWFzdXJlLmNhY2hlc1tpXX07XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxpbmVWaWV3LnJlc3QubGVuZ3RoOyBpKyspXHJcbiAgICAgIGlmIChsaW5lTm8obGluZVZpZXcucmVzdFtpXSkgPiBsaW5lTilcclxuICAgICAgICByZXR1cm4ge21hcDogbGluZVZpZXcubWVhc3VyZS5tYXBzW2ldLCBjYWNoZTogbGluZVZpZXcubWVhc3VyZS5jYWNoZXNbaV0sIGJlZm9yZTogdHJ1ZX07XHJcbiAgfVxyXG5cclxuICAvLyBSZW5kZXIgYSBsaW5lIGludG8gdGhlIGhpZGRlbiBub2RlIGRpc3BsYXkuZXh0ZXJuYWxNZWFzdXJlZC4gVXNlZFxyXG4gIC8vIHdoZW4gbWVhc3VyZW1lbnQgaXMgbmVlZGVkIGZvciBhIGxpbmUgdGhhdCdzIG5vdCBpbiB0aGUgdmlld3BvcnQuXHJcbiAgZnVuY3Rpb24gdXBkYXRlRXh0ZXJuYWxNZWFzdXJlbWVudChjbSwgbGluZSkge1xyXG4gICAgbGluZSA9IHZpc3VhbExpbmUobGluZSk7XHJcbiAgICB2YXIgbGluZU4gPSBsaW5lTm8obGluZSk7XHJcbiAgICB2YXIgdmlldyA9IGNtLmRpc3BsYXkuZXh0ZXJuYWxNZWFzdXJlZCA9IG5ldyBMaW5lVmlldyhjbS5kb2MsIGxpbmUsIGxpbmVOKTtcclxuICAgIHZpZXcubGluZU4gPSBsaW5lTjtcclxuICAgIHZhciBidWlsdCA9IHZpZXcuYnVpbHQgPSBidWlsZExpbmVDb250ZW50KGNtLCB2aWV3KTtcclxuICAgIHZpZXcudGV4dCA9IGJ1aWx0LnByZTtcclxuICAgIHJlbW92ZUNoaWxkcmVuQW5kQWRkKGNtLmRpc3BsYXkubGluZU1lYXN1cmUsIGJ1aWx0LnByZSk7XHJcbiAgICByZXR1cm4gdmlldztcclxuICB9XHJcblxyXG4gIC8vIEdldCBhIHt0b3AsIGJvdHRvbSwgbGVmdCwgcmlnaHR9IGJveCAoaW4gbGluZS1sb2NhbCBjb29yZGluYXRlcylcclxuICAvLyBmb3IgYSBnaXZlbiBjaGFyYWN0ZXIuXHJcbiAgZnVuY3Rpb24gbWVhc3VyZUNoYXIoY20sIGxpbmUsIGNoLCBiaWFzKSB7XHJcbiAgICByZXR1cm4gbWVhc3VyZUNoYXJQcmVwYXJlZChjbSwgcHJlcGFyZU1lYXN1cmVGb3JMaW5lKGNtLCBsaW5lKSwgY2gsIGJpYXMpO1xyXG4gIH1cclxuXHJcbiAgLy8gRmluZCBhIGxpbmUgdmlldyB0aGF0IGNvcnJlc3BvbmRzIHRvIHRoZSBnaXZlbiBsaW5lIG51bWJlci5cclxuICBmdW5jdGlvbiBmaW5kVmlld0ZvckxpbmUoY20sIGxpbmVOKSB7XHJcbiAgICBpZiAobGluZU4gPj0gY20uZGlzcGxheS52aWV3RnJvbSAmJiBsaW5lTiA8IGNtLmRpc3BsYXkudmlld1RvKVxyXG4gICAgICByZXR1cm4gY20uZGlzcGxheS52aWV3W2ZpbmRWaWV3SW5kZXgoY20sIGxpbmVOKV07XHJcbiAgICB2YXIgZXh0ID0gY20uZGlzcGxheS5leHRlcm5hbE1lYXN1cmVkO1xyXG4gICAgaWYgKGV4dCAmJiBsaW5lTiA+PSBleHQubGluZU4gJiYgbGluZU4gPCBleHQubGluZU4gKyBleHQuc2l6ZSlcclxuICAgICAgcmV0dXJuIGV4dDtcclxuICB9XHJcblxyXG4gIC8vIE1lYXN1cmVtZW50IGNhbiBiZSBzcGxpdCBpbiB0d28gc3RlcHMsIHRoZSBzZXQtdXAgd29yayB0aGF0XHJcbiAgLy8gYXBwbGllcyB0byB0aGUgd2hvbGUgbGluZSwgYW5kIHRoZSBtZWFzdXJlbWVudCBvZiB0aGUgYWN0dWFsXHJcbiAgLy8gY2hhcmFjdGVyLiBGdW5jdGlvbnMgbGlrZSBjb29yZHNDaGFyLCB0aGF0IG5lZWQgdG8gZG8gYSBsb3Qgb2ZcclxuICAvLyBtZWFzdXJlbWVudHMgaW4gYSByb3csIGNhbiB0aHVzIGVuc3VyZSB0aGF0IHRoZSBzZXQtdXAgd29yayBpc1xyXG4gIC8vIG9ubHkgZG9uZSBvbmNlLlxyXG4gIGZ1bmN0aW9uIHByZXBhcmVNZWFzdXJlRm9yTGluZShjbSwgbGluZSkge1xyXG4gICAgdmFyIGxpbmVOID0gbGluZU5vKGxpbmUpO1xyXG4gICAgdmFyIHZpZXcgPSBmaW5kVmlld0ZvckxpbmUoY20sIGxpbmVOKTtcclxuICAgIGlmICh2aWV3ICYmICF2aWV3LnRleHQpIHtcclxuICAgICAgdmlldyA9IG51bGw7XHJcbiAgICB9IGVsc2UgaWYgKHZpZXcgJiYgdmlldy5jaGFuZ2VzKSB7XHJcbiAgICAgIHVwZGF0ZUxpbmVGb3JDaGFuZ2VzKGNtLCB2aWV3LCBsaW5lTiwgZ2V0RGltZW5zaW9ucyhjbSkpO1xyXG4gICAgICBjbS5jdXJPcC5mb3JjZVVwZGF0ZSA9IHRydWU7XHJcbiAgICB9XHJcbiAgICBpZiAoIXZpZXcpXHJcbiAgICAgIHZpZXcgPSB1cGRhdGVFeHRlcm5hbE1lYXN1cmVtZW50KGNtLCBsaW5lKTtcclxuXHJcbiAgICB2YXIgaW5mbyA9IG1hcEZyb21MaW5lVmlldyh2aWV3LCBsaW5lLCBsaW5lTik7XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICBsaW5lOiBsaW5lLCB2aWV3OiB2aWV3LCByZWN0OiBudWxsLFxyXG4gICAgICBtYXA6IGluZm8ubWFwLCBjYWNoZTogaW5mby5jYWNoZSwgYmVmb3JlOiBpbmZvLmJlZm9yZSxcclxuICAgICAgaGFzSGVpZ2h0czogZmFsc2VcclxuICAgIH07XHJcbiAgfVxyXG5cclxuICAvLyBHaXZlbiBhIHByZXBhcmVkIG1lYXN1cmVtZW50IG9iamVjdCwgbWVhc3VyZXMgdGhlIHBvc2l0aW9uIG9mIGFuXHJcbiAgLy8gYWN0dWFsIGNoYXJhY3RlciAob3IgZmV0Y2hlcyBpdCBmcm9tIHRoZSBjYWNoZSkuXHJcbiAgZnVuY3Rpb24gbWVhc3VyZUNoYXJQcmVwYXJlZChjbSwgcHJlcGFyZWQsIGNoLCBiaWFzLCB2YXJIZWlnaHQpIHtcclxuICAgIGlmIChwcmVwYXJlZC5iZWZvcmUpIGNoID0gLTE7XHJcbiAgICB2YXIga2V5ID0gY2ggKyAoYmlhcyB8fCBcIlwiKSwgZm91bmQ7XHJcbiAgICBpZiAocHJlcGFyZWQuY2FjaGUuaGFzT3duUHJvcGVydHkoa2V5KSkge1xyXG4gICAgICBmb3VuZCA9IHByZXBhcmVkLmNhY2hlW2tleV07XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICBpZiAoIXByZXBhcmVkLnJlY3QpXHJcbiAgICAgICAgcHJlcGFyZWQucmVjdCA9IHByZXBhcmVkLnZpZXcudGV4dC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcclxuICAgICAgaWYgKCFwcmVwYXJlZC5oYXNIZWlnaHRzKSB7XHJcbiAgICAgICAgZW5zdXJlTGluZUhlaWdodHMoY20sIHByZXBhcmVkLnZpZXcsIHByZXBhcmVkLnJlY3QpO1xyXG4gICAgICAgIHByZXBhcmVkLmhhc0hlaWdodHMgPSB0cnVlO1xyXG4gICAgICB9XHJcbiAgICAgIGZvdW5kID0gbWVhc3VyZUNoYXJJbm5lcihjbSwgcHJlcGFyZWQsIGNoLCBiaWFzKTtcclxuICAgICAgaWYgKCFmb3VuZC5ib2d1cykgcHJlcGFyZWQuY2FjaGVba2V5XSA9IGZvdW5kO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIHtsZWZ0OiBmb3VuZC5sZWZ0LCByaWdodDogZm91bmQucmlnaHQsXHJcbiAgICAgICAgICAgIHRvcDogdmFySGVpZ2h0ID8gZm91bmQucnRvcCA6IGZvdW5kLnRvcCxcclxuICAgICAgICAgICAgYm90dG9tOiB2YXJIZWlnaHQgPyBmb3VuZC5yYm90dG9tIDogZm91bmQuYm90dG9tfTtcclxuICB9XHJcblxyXG4gIHZhciBudWxsUmVjdCA9IHtsZWZ0OiAwLCByaWdodDogMCwgdG9wOiAwLCBib3R0b206IDB9O1xyXG5cclxuICBmdW5jdGlvbiBub2RlQW5kT2Zmc2V0SW5MaW5lTWFwKG1hcCwgY2gsIGJpYXMpIHtcclxuICAgIHZhciBub2RlLCBzdGFydCwgZW5kLCBjb2xsYXBzZTtcclxuICAgIC8vIEZpcnN0LCBzZWFyY2ggdGhlIGxpbmUgbWFwIGZvciB0aGUgdGV4dCBub2RlIGNvcnJlc3BvbmRpbmcgdG8sXHJcbiAgICAvLyBvciBjbG9zZXN0IHRvLCB0aGUgdGFyZ2V0IGNoYXJhY3Rlci5cclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbWFwLmxlbmd0aDsgaSArPSAzKSB7XHJcbiAgICAgIHZhciBtU3RhcnQgPSBtYXBbaV0sIG1FbmQgPSBtYXBbaSArIDFdO1xyXG4gICAgICBpZiAoY2ggPCBtU3RhcnQpIHtcclxuICAgICAgICBzdGFydCA9IDA7IGVuZCA9IDE7XHJcbiAgICAgICAgY29sbGFwc2UgPSBcImxlZnRcIjtcclxuICAgICAgfSBlbHNlIGlmIChjaCA8IG1FbmQpIHtcclxuICAgICAgICBzdGFydCA9IGNoIC0gbVN0YXJ0O1xyXG4gICAgICAgIGVuZCA9IHN0YXJ0ICsgMTtcclxuICAgICAgfSBlbHNlIGlmIChpID09IG1hcC5sZW5ndGggLSAzIHx8IGNoID09IG1FbmQgJiYgbWFwW2kgKyAzXSA+IGNoKSB7XHJcbiAgICAgICAgZW5kID0gbUVuZCAtIG1TdGFydDtcclxuICAgICAgICBzdGFydCA9IGVuZCAtIDE7XHJcbiAgICAgICAgaWYgKGNoID49IG1FbmQpIGNvbGxhcHNlID0gXCJyaWdodFwiO1xyXG4gICAgICB9XHJcbiAgICAgIGlmIChzdGFydCAhPSBudWxsKSB7XHJcbiAgICAgICAgbm9kZSA9IG1hcFtpICsgMl07XHJcbiAgICAgICAgaWYgKG1TdGFydCA9PSBtRW5kICYmIGJpYXMgPT0gKG5vZGUuaW5zZXJ0TGVmdCA/IFwibGVmdFwiIDogXCJyaWdodFwiKSlcclxuICAgICAgICAgIGNvbGxhcHNlID0gYmlhcztcclxuICAgICAgICBpZiAoYmlhcyA9PSBcImxlZnRcIiAmJiBzdGFydCA9PSAwKVxyXG4gICAgICAgICAgd2hpbGUgKGkgJiYgbWFwW2kgLSAyXSA9PSBtYXBbaSAtIDNdICYmIG1hcFtpIC0gMV0uaW5zZXJ0TGVmdCkge1xyXG4gICAgICAgICAgICBub2RlID0gbWFwWyhpIC09IDMpICsgMl07XHJcbiAgICAgICAgICAgIGNvbGxhcHNlID0gXCJsZWZ0XCI7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgaWYgKGJpYXMgPT0gXCJyaWdodFwiICYmIHN0YXJ0ID09IG1FbmQgLSBtU3RhcnQpXHJcbiAgICAgICAgICB3aGlsZSAoaSA8IG1hcC5sZW5ndGggLSAzICYmIG1hcFtpICsgM10gPT0gbWFwW2kgKyA0XSAmJiAhbWFwW2kgKyA1XS5pbnNlcnRMZWZ0KSB7XHJcbiAgICAgICAgICAgIG5vZGUgPSBtYXBbKGkgKz0gMykgKyAyXTtcclxuICAgICAgICAgICAgY29sbGFwc2UgPSBcInJpZ2h0XCI7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIHJldHVybiB7bm9kZTogbm9kZSwgc3RhcnQ6IHN0YXJ0LCBlbmQ6IGVuZCwgY29sbGFwc2U6IGNvbGxhcHNlLCBjb3ZlclN0YXJ0OiBtU3RhcnQsIGNvdmVyRW5kOiBtRW5kfTtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIG1lYXN1cmVDaGFySW5uZXIoY20sIHByZXBhcmVkLCBjaCwgYmlhcykge1xyXG4gICAgdmFyIHBsYWNlID0gbm9kZUFuZE9mZnNldEluTGluZU1hcChwcmVwYXJlZC5tYXAsIGNoLCBiaWFzKTtcclxuICAgIHZhciBub2RlID0gcGxhY2Uubm9kZSwgc3RhcnQgPSBwbGFjZS5zdGFydCwgZW5kID0gcGxhY2UuZW5kLCBjb2xsYXBzZSA9IHBsYWNlLmNvbGxhcHNlO1xyXG5cclxuICAgIHZhciByZWN0O1xyXG4gICAgaWYgKG5vZGUubm9kZVR5cGUgPT0gMykgeyAvLyBJZiBpdCBpcyBhIHRleHQgbm9kZSwgdXNlIGEgcmFuZ2UgdG8gcmV0cmlldmUgdGhlIGNvb3JkaW5hdGVzLlxyXG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IDQ7IGkrKykgeyAvLyBSZXRyeSBhIG1heGltdW0gb2YgNCB0aW1lcyB3aGVuIG5vbnNlbnNlIHJlY3RhbmdsZXMgYXJlIHJldHVybmVkXHJcbiAgICAgICAgd2hpbGUgKHN0YXJ0ICYmIGlzRXh0ZW5kaW5nQ2hhcihwcmVwYXJlZC5saW5lLnRleHQuY2hhckF0KHBsYWNlLmNvdmVyU3RhcnQgKyBzdGFydCkpKSAtLXN0YXJ0O1xyXG4gICAgICAgIHdoaWxlIChwbGFjZS5jb3ZlclN0YXJ0ICsgZW5kIDwgcGxhY2UuY292ZXJFbmQgJiYgaXNFeHRlbmRpbmdDaGFyKHByZXBhcmVkLmxpbmUudGV4dC5jaGFyQXQocGxhY2UuY292ZXJTdGFydCArIGVuZCkpKSArK2VuZDtcclxuICAgICAgICBpZiAoaWUgJiYgaWVfdmVyc2lvbiA8IDkgJiYgc3RhcnQgPT0gMCAmJiBlbmQgPT0gcGxhY2UuY292ZXJFbmQgLSBwbGFjZS5jb3ZlclN0YXJ0KSB7XHJcbiAgICAgICAgICByZWN0ID0gbm9kZS5wYXJlbnROb2RlLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xyXG4gICAgICAgIH0gZWxzZSBpZiAoaWUgJiYgY20ub3B0aW9ucy5saW5lV3JhcHBpbmcpIHtcclxuICAgICAgICAgIHZhciByZWN0cyA9IHJhbmdlKG5vZGUsIHN0YXJ0LCBlbmQpLmdldENsaWVudFJlY3RzKCk7XHJcbiAgICAgICAgICBpZiAocmVjdHMubGVuZ3RoKVxyXG4gICAgICAgICAgICByZWN0ID0gcmVjdHNbYmlhcyA9PSBcInJpZ2h0XCIgPyByZWN0cy5sZW5ndGggLSAxIDogMF07XHJcbiAgICAgICAgICBlbHNlXHJcbiAgICAgICAgICAgIHJlY3QgPSBudWxsUmVjdDtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgcmVjdCA9IHJhbmdlKG5vZGUsIHN0YXJ0LCBlbmQpLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpIHx8IG51bGxSZWN0O1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAocmVjdC5sZWZ0IHx8IHJlY3QucmlnaHQgfHwgc3RhcnQgPT0gMCkgYnJlYWs7XHJcbiAgICAgICAgZW5kID0gc3RhcnQ7XHJcbiAgICAgICAgc3RhcnQgPSBzdGFydCAtIDE7XHJcbiAgICAgICAgY29sbGFwc2UgPSBcInJpZ2h0XCI7XHJcbiAgICAgIH1cclxuICAgICAgaWYgKGllICYmIGllX3ZlcnNpb24gPCAxMSkgcmVjdCA9IG1heWJlVXBkYXRlUmVjdEZvclpvb21pbmcoY20uZGlzcGxheS5tZWFzdXJlLCByZWN0KTtcclxuICAgIH0gZWxzZSB7IC8vIElmIGl0IGlzIGEgd2lkZ2V0LCBzaW1wbHkgZ2V0IHRoZSBib3ggZm9yIHRoZSB3aG9sZSB3aWRnZXQuXHJcbiAgICAgIGlmIChzdGFydCA+IDApIGNvbGxhcHNlID0gYmlhcyA9IFwicmlnaHRcIjtcclxuICAgICAgdmFyIHJlY3RzO1xyXG4gICAgICBpZiAoY20ub3B0aW9ucy5saW5lV3JhcHBpbmcgJiYgKHJlY3RzID0gbm9kZS5nZXRDbGllbnRSZWN0cygpKS5sZW5ndGggPiAxKVxyXG4gICAgICAgIHJlY3QgPSByZWN0c1tiaWFzID09IFwicmlnaHRcIiA/IHJlY3RzLmxlbmd0aCAtIDEgOiAwXTtcclxuICAgICAgZWxzZVxyXG4gICAgICAgIHJlY3QgPSBub2RlLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xyXG4gICAgfVxyXG4gICAgaWYgKGllICYmIGllX3ZlcnNpb24gPCA5ICYmICFzdGFydCAmJiAoIXJlY3QgfHwgIXJlY3QubGVmdCAmJiAhcmVjdC5yaWdodCkpIHtcclxuICAgICAgdmFyIHJTcGFuID0gbm9kZS5wYXJlbnROb2RlLmdldENsaWVudFJlY3RzKClbMF07XHJcbiAgICAgIGlmIChyU3BhbilcclxuICAgICAgICByZWN0ID0ge2xlZnQ6IHJTcGFuLmxlZnQsIHJpZ2h0OiByU3Bhbi5sZWZ0ICsgY2hhcldpZHRoKGNtLmRpc3BsYXkpLCB0b3A6IHJTcGFuLnRvcCwgYm90dG9tOiByU3Bhbi5ib3R0b219O1xyXG4gICAgICBlbHNlXHJcbiAgICAgICAgcmVjdCA9IG51bGxSZWN0O1xyXG4gICAgfVxyXG5cclxuICAgIHZhciBydG9wID0gcmVjdC50b3AgLSBwcmVwYXJlZC5yZWN0LnRvcCwgcmJvdCA9IHJlY3QuYm90dG9tIC0gcHJlcGFyZWQucmVjdC50b3A7XHJcbiAgICB2YXIgbWlkID0gKHJ0b3AgKyByYm90KSAvIDI7XHJcbiAgICB2YXIgaGVpZ2h0cyA9IHByZXBhcmVkLnZpZXcubWVhc3VyZS5oZWlnaHRzO1xyXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBoZWlnaHRzLmxlbmd0aCAtIDE7IGkrKylcclxuICAgICAgaWYgKG1pZCA8IGhlaWdodHNbaV0pIGJyZWFrO1xyXG4gICAgdmFyIHRvcCA9IGkgPyBoZWlnaHRzW2kgLSAxXSA6IDAsIGJvdCA9IGhlaWdodHNbaV07XHJcbiAgICB2YXIgcmVzdWx0ID0ge2xlZnQ6IChjb2xsYXBzZSA9PSBcInJpZ2h0XCIgPyByZWN0LnJpZ2h0IDogcmVjdC5sZWZ0KSAtIHByZXBhcmVkLnJlY3QubGVmdCxcclxuICAgICAgICAgICAgICAgICAgcmlnaHQ6IChjb2xsYXBzZSA9PSBcImxlZnRcIiA/IHJlY3QubGVmdCA6IHJlY3QucmlnaHQpIC0gcHJlcGFyZWQucmVjdC5sZWZ0LFxyXG4gICAgICAgICAgICAgICAgICB0b3A6IHRvcCwgYm90dG9tOiBib3R9O1xyXG4gICAgaWYgKCFyZWN0LmxlZnQgJiYgIXJlY3QucmlnaHQpIHJlc3VsdC5ib2d1cyA9IHRydWU7XHJcbiAgICBpZiAoIWNtLm9wdGlvbnMuc2luZ2xlQ3Vyc29ySGVpZ2h0UGVyTGluZSkgeyByZXN1bHQucnRvcCA9IHJ0b3A7IHJlc3VsdC5yYm90dG9tID0gcmJvdDsgfVxyXG5cclxuICAgIHJldHVybiByZXN1bHQ7XHJcbiAgfVxyXG5cclxuICAvLyBXb3JrIGFyb3VuZCBwcm9ibGVtIHdpdGggYm91bmRpbmcgY2xpZW50IHJlY3RzIG9uIHJhbmdlcyBiZWluZ1xyXG4gIC8vIHJldHVybmVkIGluY29ycmVjdGx5IHdoZW4gem9vbWVkIG9uIElFMTAgYW5kIGJlbG93LlxyXG4gIGZ1bmN0aW9uIG1heWJlVXBkYXRlUmVjdEZvclpvb21pbmcobWVhc3VyZSwgcmVjdCkge1xyXG4gICAgaWYgKCF3aW5kb3cuc2NyZWVuIHx8IHNjcmVlbi5sb2dpY2FsWERQSSA9PSBudWxsIHx8XHJcbiAgICAgICAgc2NyZWVuLmxvZ2ljYWxYRFBJID09IHNjcmVlbi5kZXZpY2VYRFBJIHx8ICFoYXNCYWRab29tZWRSZWN0cyhtZWFzdXJlKSlcclxuICAgICAgcmV0dXJuIHJlY3Q7XHJcbiAgICB2YXIgc2NhbGVYID0gc2NyZWVuLmxvZ2ljYWxYRFBJIC8gc2NyZWVuLmRldmljZVhEUEk7XHJcbiAgICB2YXIgc2NhbGVZID0gc2NyZWVuLmxvZ2ljYWxZRFBJIC8gc2NyZWVuLmRldmljZVlEUEk7XHJcbiAgICByZXR1cm4ge2xlZnQ6IHJlY3QubGVmdCAqIHNjYWxlWCwgcmlnaHQ6IHJlY3QucmlnaHQgKiBzY2FsZVgsXHJcbiAgICAgICAgICAgIHRvcDogcmVjdC50b3AgKiBzY2FsZVksIGJvdHRvbTogcmVjdC5ib3R0b20gKiBzY2FsZVl9O1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gY2xlYXJMaW5lTWVhc3VyZW1lbnRDYWNoZUZvcihsaW5lVmlldykge1xyXG4gICAgaWYgKGxpbmVWaWV3Lm1lYXN1cmUpIHtcclxuICAgICAgbGluZVZpZXcubWVhc3VyZS5jYWNoZSA9IHt9O1xyXG4gICAgICBsaW5lVmlldy5tZWFzdXJlLmhlaWdodHMgPSBudWxsO1xyXG4gICAgICBpZiAobGluZVZpZXcucmVzdCkgZm9yICh2YXIgaSA9IDA7IGkgPCBsaW5lVmlldy5yZXN0Lmxlbmd0aDsgaSsrKVxyXG4gICAgICAgIGxpbmVWaWV3Lm1lYXN1cmUuY2FjaGVzW2ldID0ge307XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBjbGVhckxpbmVNZWFzdXJlbWVudENhY2hlKGNtKSB7XHJcbiAgICBjbS5kaXNwbGF5LmV4dGVybmFsTWVhc3VyZSA9IG51bGw7XHJcbiAgICByZW1vdmVDaGlsZHJlbihjbS5kaXNwbGF5LmxpbmVNZWFzdXJlKTtcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgY20uZGlzcGxheS52aWV3Lmxlbmd0aDsgaSsrKVxyXG4gICAgICBjbGVhckxpbmVNZWFzdXJlbWVudENhY2hlRm9yKGNtLmRpc3BsYXkudmlld1tpXSk7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBjbGVhckNhY2hlcyhjbSkge1xyXG4gICAgY2xlYXJMaW5lTWVhc3VyZW1lbnRDYWNoZShjbSk7XHJcbiAgICBjbS5kaXNwbGF5LmNhY2hlZENoYXJXaWR0aCA9IGNtLmRpc3BsYXkuY2FjaGVkVGV4dEhlaWdodCA9IGNtLmRpc3BsYXkuY2FjaGVkUGFkZGluZ0ggPSBudWxsO1xyXG4gICAgaWYgKCFjbS5vcHRpb25zLmxpbmVXcmFwcGluZykgY20uZGlzcGxheS5tYXhMaW5lQ2hhbmdlZCA9IHRydWU7XHJcbiAgICBjbS5kaXNwbGF5LmxpbmVOdW1DaGFycyA9IG51bGw7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBwYWdlU2Nyb2xsWCgpIHsgcmV0dXJuIHdpbmRvdy5wYWdlWE9mZnNldCB8fCAoZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50IHx8IGRvY3VtZW50LmJvZHkpLnNjcm9sbExlZnQ7IH1cclxuICBmdW5jdGlvbiBwYWdlU2Nyb2xsWSgpIHsgcmV0dXJuIHdpbmRvdy5wYWdlWU9mZnNldCB8fCAoZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50IHx8IGRvY3VtZW50LmJvZHkpLnNjcm9sbFRvcDsgfVxyXG5cclxuICAvLyBDb252ZXJ0cyBhIHt0b3AsIGJvdHRvbSwgbGVmdCwgcmlnaHR9IGJveCBmcm9tIGxpbmUtbG9jYWxcclxuICAvLyBjb29yZGluYXRlcyBpbnRvIGFub3RoZXIgY29vcmRpbmF0ZSBzeXN0ZW0uIENvbnRleHQgbWF5IGJlIG9uZSBvZlxyXG4gIC8vIFwibGluZVwiLCBcImRpdlwiIChkaXNwbGF5LmxpbmVEaXYpLCBcImxvY2FsXCIvbnVsbCAoZWRpdG9yKSwgXCJ3aW5kb3dcIixcclxuICAvLyBvciBcInBhZ2VcIi5cclxuICBmdW5jdGlvbiBpbnRvQ29vcmRTeXN0ZW0oY20sIGxpbmVPYmosIHJlY3QsIGNvbnRleHQpIHtcclxuICAgIGlmIChsaW5lT2JqLndpZGdldHMpIGZvciAodmFyIGkgPSAwOyBpIDwgbGluZU9iai53aWRnZXRzLmxlbmd0aDsgKytpKSBpZiAobGluZU9iai53aWRnZXRzW2ldLmFib3ZlKSB7XHJcbiAgICAgIHZhciBzaXplID0gd2lkZ2V0SGVpZ2h0KGxpbmVPYmoud2lkZ2V0c1tpXSk7XHJcbiAgICAgIHJlY3QudG9wICs9IHNpemU7IHJlY3QuYm90dG9tICs9IHNpemU7XHJcbiAgICB9XHJcbiAgICBpZiAoY29udGV4dCA9PSBcImxpbmVcIikgcmV0dXJuIHJlY3Q7XHJcbiAgICBpZiAoIWNvbnRleHQpIGNvbnRleHQgPSBcImxvY2FsXCI7XHJcbiAgICB2YXIgeU9mZiA9IGhlaWdodEF0TGluZShsaW5lT2JqKTtcclxuICAgIGlmIChjb250ZXh0ID09IFwibG9jYWxcIikgeU9mZiArPSBwYWRkaW5nVG9wKGNtLmRpc3BsYXkpO1xyXG4gICAgZWxzZSB5T2ZmIC09IGNtLmRpc3BsYXkudmlld09mZnNldDtcclxuICAgIGlmIChjb250ZXh0ID09IFwicGFnZVwiIHx8IGNvbnRleHQgPT0gXCJ3aW5kb3dcIikge1xyXG4gICAgICB2YXIgbE9mZiA9IGNtLmRpc3BsYXkubGluZVNwYWNlLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xyXG4gICAgICB5T2ZmICs9IGxPZmYudG9wICsgKGNvbnRleHQgPT0gXCJ3aW5kb3dcIiA/IDAgOiBwYWdlU2Nyb2xsWSgpKTtcclxuICAgICAgdmFyIHhPZmYgPSBsT2ZmLmxlZnQgKyAoY29udGV4dCA9PSBcIndpbmRvd1wiID8gMCA6IHBhZ2VTY3JvbGxYKCkpO1xyXG4gICAgICByZWN0LmxlZnQgKz0geE9mZjsgcmVjdC5yaWdodCArPSB4T2ZmO1xyXG4gICAgfVxyXG4gICAgcmVjdC50b3AgKz0geU9mZjsgcmVjdC5ib3R0b20gKz0geU9mZjtcclxuICAgIHJldHVybiByZWN0O1xyXG4gIH1cclxuXHJcbiAgLy8gQ292ZXJ0cyBhIGJveCBmcm9tIFwiZGl2XCIgY29vcmRzIHRvIGFub3RoZXIgY29vcmRpbmF0ZSBzeXN0ZW0uXHJcbiAgLy8gQ29udGV4dCBtYXkgYmUgXCJ3aW5kb3dcIiwgXCJwYWdlXCIsIFwiZGl2XCIsIG9yIFwibG9jYWxcIi9udWxsLlxyXG4gIGZ1bmN0aW9uIGZyb21Db29yZFN5c3RlbShjbSwgY29vcmRzLCBjb250ZXh0KSB7XHJcbiAgICBpZiAoY29udGV4dCA9PSBcImRpdlwiKSByZXR1cm4gY29vcmRzO1xyXG4gICAgdmFyIGxlZnQgPSBjb29yZHMubGVmdCwgdG9wID0gY29vcmRzLnRvcDtcclxuICAgIC8vIEZpcnN0IG1vdmUgaW50byBcInBhZ2VcIiBjb29yZGluYXRlIHN5c3RlbVxyXG4gICAgaWYgKGNvbnRleHQgPT0gXCJwYWdlXCIpIHtcclxuICAgICAgbGVmdCAtPSBwYWdlU2Nyb2xsWCgpO1xyXG4gICAgICB0b3AgLT0gcGFnZVNjcm9sbFkoKTtcclxuICAgIH0gZWxzZSBpZiAoY29udGV4dCA9PSBcImxvY2FsXCIgfHwgIWNvbnRleHQpIHtcclxuICAgICAgdmFyIGxvY2FsQm94ID0gY20uZGlzcGxheS5zaXplci5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcclxuICAgICAgbGVmdCArPSBsb2NhbEJveC5sZWZ0O1xyXG4gICAgICB0b3AgKz0gbG9jYWxCb3gudG9wO1xyXG4gICAgfVxyXG5cclxuICAgIHZhciBsaW5lU3BhY2VCb3ggPSBjbS5kaXNwbGF5LmxpbmVTcGFjZS5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcclxuICAgIHJldHVybiB7bGVmdDogbGVmdCAtIGxpbmVTcGFjZUJveC5sZWZ0LCB0b3A6IHRvcCAtIGxpbmVTcGFjZUJveC50b3B9O1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gY2hhckNvb3JkcyhjbSwgcG9zLCBjb250ZXh0LCBsaW5lT2JqLCBiaWFzKSB7XHJcbiAgICBpZiAoIWxpbmVPYmopIGxpbmVPYmogPSBnZXRMaW5lKGNtLmRvYywgcG9zLmxpbmUpO1xyXG4gICAgcmV0dXJuIGludG9Db29yZFN5c3RlbShjbSwgbGluZU9iaiwgbWVhc3VyZUNoYXIoY20sIGxpbmVPYmosIHBvcy5jaCwgYmlhcyksIGNvbnRleHQpO1xyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJucyBhIGJveCBmb3IgYSBnaXZlbiBjdXJzb3IgcG9zaXRpb24sIHdoaWNoIG1heSBoYXZlIGFuXHJcbiAgLy8gJ290aGVyJyBwcm9wZXJ0eSBjb250YWluaW5nIHRoZSBwb3NpdGlvbiBvZiB0aGUgc2Vjb25kYXJ5IGN1cnNvclxyXG4gIC8vIG9uIGEgYmlkaSBib3VuZGFyeS5cclxuICBmdW5jdGlvbiBjdXJzb3JDb29yZHMoY20sIHBvcywgY29udGV4dCwgbGluZU9iaiwgcHJlcGFyZWRNZWFzdXJlLCB2YXJIZWlnaHQpIHtcclxuICAgIGxpbmVPYmogPSBsaW5lT2JqIHx8IGdldExpbmUoY20uZG9jLCBwb3MubGluZSk7XHJcbiAgICBpZiAoIXByZXBhcmVkTWVhc3VyZSkgcHJlcGFyZWRNZWFzdXJlID0gcHJlcGFyZU1lYXN1cmVGb3JMaW5lKGNtLCBsaW5lT2JqKTtcclxuICAgIGZ1bmN0aW9uIGdldChjaCwgcmlnaHQpIHtcclxuICAgICAgdmFyIG0gPSBtZWFzdXJlQ2hhclByZXBhcmVkKGNtLCBwcmVwYXJlZE1lYXN1cmUsIGNoLCByaWdodCA/IFwicmlnaHRcIiA6IFwibGVmdFwiLCB2YXJIZWlnaHQpO1xyXG4gICAgICBpZiAocmlnaHQpIG0ubGVmdCA9IG0ucmlnaHQ7IGVsc2UgbS5yaWdodCA9IG0ubGVmdDtcclxuICAgICAgcmV0dXJuIGludG9Db29yZFN5c3RlbShjbSwgbGluZU9iaiwgbSwgY29udGV4dCk7XHJcbiAgICB9XHJcbiAgICBmdW5jdGlvbiBnZXRCaWRpKGNoLCBwYXJ0UG9zKSB7XHJcbiAgICAgIHZhciBwYXJ0ID0gb3JkZXJbcGFydFBvc10sIHJpZ2h0ID0gcGFydC5sZXZlbCAlIDI7XHJcbiAgICAgIGlmIChjaCA9PSBiaWRpTGVmdChwYXJ0KSAmJiBwYXJ0UG9zICYmIHBhcnQubGV2ZWwgPCBvcmRlcltwYXJ0UG9zIC0gMV0ubGV2ZWwpIHtcclxuICAgICAgICBwYXJ0ID0gb3JkZXJbLS1wYXJ0UG9zXTtcclxuICAgICAgICBjaCA9IGJpZGlSaWdodChwYXJ0KSAtIChwYXJ0LmxldmVsICUgMiA/IDAgOiAxKTtcclxuICAgICAgICByaWdodCA9IHRydWU7XHJcbiAgICAgIH0gZWxzZSBpZiAoY2ggPT0gYmlkaVJpZ2h0KHBhcnQpICYmIHBhcnRQb3MgPCBvcmRlci5sZW5ndGggLSAxICYmIHBhcnQubGV2ZWwgPCBvcmRlcltwYXJ0UG9zICsgMV0ubGV2ZWwpIHtcclxuICAgICAgICBwYXJ0ID0gb3JkZXJbKytwYXJ0UG9zXTtcclxuICAgICAgICBjaCA9IGJpZGlMZWZ0KHBhcnQpIC0gcGFydC5sZXZlbCAlIDI7XHJcbiAgICAgICAgcmlnaHQgPSBmYWxzZTtcclxuICAgICAgfVxyXG4gICAgICBpZiAocmlnaHQgJiYgY2ggPT0gcGFydC50byAmJiBjaCA+IHBhcnQuZnJvbSkgcmV0dXJuIGdldChjaCAtIDEpO1xyXG4gICAgICByZXR1cm4gZ2V0KGNoLCByaWdodCk7XHJcbiAgICB9XHJcbiAgICB2YXIgb3JkZXIgPSBnZXRPcmRlcihsaW5lT2JqKSwgY2ggPSBwb3MuY2g7XHJcbiAgICBpZiAoIW9yZGVyKSByZXR1cm4gZ2V0KGNoKTtcclxuICAgIHZhciBwYXJ0UG9zID0gZ2V0QmlkaVBhcnRBdChvcmRlciwgY2gpO1xyXG4gICAgdmFyIHZhbCA9IGdldEJpZGkoY2gsIHBhcnRQb3MpO1xyXG4gICAgaWYgKGJpZGlPdGhlciAhPSBudWxsKSB2YWwub3RoZXIgPSBnZXRCaWRpKGNoLCBiaWRpT3RoZXIpO1xyXG4gICAgcmV0dXJuIHZhbDtcclxuICB9XHJcblxyXG4gIC8vIFVzZWQgdG8gY2hlYXBseSBlc3RpbWF0ZSB0aGUgY29vcmRpbmF0ZXMgZm9yIGEgcG9zaXRpb24uIFVzZWQgZm9yXHJcbiAgLy8gaW50ZXJtZWRpYXRlIHNjcm9sbCB1cGRhdGVzLlxyXG4gIGZ1bmN0aW9uIGVzdGltYXRlQ29vcmRzKGNtLCBwb3MpIHtcclxuICAgIHZhciBsZWZ0ID0gMCwgcG9zID0gY2xpcFBvcyhjbS5kb2MsIHBvcyk7XHJcbiAgICBpZiAoIWNtLm9wdGlvbnMubGluZVdyYXBwaW5nKSBsZWZ0ID0gY2hhcldpZHRoKGNtLmRpc3BsYXkpICogcG9zLmNoO1xyXG4gICAgdmFyIGxpbmVPYmogPSBnZXRMaW5lKGNtLmRvYywgcG9zLmxpbmUpO1xyXG4gICAgdmFyIHRvcCA9IGhlaWdodEF0TGluZShsaW5lT2JqKSArIHBhZGRpbmdUb3AoY20uZGlzcGxheSk7XHJcbiAgICByZXR1cm4ge2xlZnQ6IGxlZnQsIHJpZ2h0OiBsZWZ0LCB0b3A6IHRvcCwgYm90dG9tOiB0b3AgKyBsaW5lT2JqLmhlaWdodH07XHJcbiAgfVxyXG5cclxuICAvLyBQb3NpdGlvbnMgcmV0dXJuZWQgYnkgY29vcmRzQ2hhciBjb250YWluIHNvbWUgZXh0cmEgaW5mb3JtYXRpb24uXHJcbiAgLy8geFJlbCBpcyB0aGUgcmVsYXRpdmUgeCBwb3NpdGlvbiBvZiB0aGUgaW5wdXQgY29vcmRpbmF0ZXMgY29tcGFyZWRcclxuICAvLyB0byB0aGUgZm91bmQgcG9zaXRpb24gKHNvIHhSZWwgPiAwIG1lYW5zIHRoZSBjb29yZGluYXRlcyBhcmUgdG9cclxuICAvLyB0aGUgcmlnaHQgb2YgdGhlIGNoYXJhY3RlciBwb3NpdGlvbiwgZm9yIGV4YW1wbGUpLiBXaGVuIG91dHNpZGVcclxuICAvLyBpcyB0cnVlLCB0aGF0IG1lYW5zIHRoZSBjb29yZGluYXRlcyBsaWUgb3V0c2lkZSB0aGUgbGluZSdzXHJcbiAgLy8gdmVydGljYWwgcmFuZ2UuXHJcbiAgZnVuY3Rpb24gUG9zV2l0aEluZm8obGluZSwgY2gsIG91dHNpZGUsIHhSZWwpIHtcclxuICAgIHZhciBwb3MgPSBQb3MobGluZSwgY2gpO1xyXG4gICAgcG9zLnhSZWwgPSB4UmVsO1xyXG4gICAgaWYgKG91dHNpZGUpIHBvcy5vdXRzaWRlID0gdHJ1ZTtcclxuICAgIHJldHVybiBwb3M7XHJcbiAgfVxyXG5cclxuICAvLyBDb21wdXRlIHRoZSBjaGFyYWN0ZXIgcG9zaXRpb24gY2xvc2VzdCB0byB0aGUgZ2l2ZW4gY29vcmRpbmF0ZXMuXHJcbiAgLy8gSW5wdXQgbXVzdCBiZSBsaW5lU3BhY2UtbG9jYWwgKFwiZGl2XCIgY29vcmRpbmF0ZSBzeXN0ZW0pLlxyXG4gIGZ1bmN0aW9uIGNvb3Jkc0NoYXIoY20sIHgsIHkpIHtcclxuICAgIHZhciBkb2MgPSBjbS5kb2M7XHJcbiAgICB5ICs9IGNtLmRpc3BsYXkudmlld09mZnNldDtcclxuICAgIGlmICh5IDwgMCkgcmV0dXJuIFBvc1dpdGhJbmZvKGRvYy5maXJzdCwgMCwgdHJ1ZSwgLTEpO1xyXG4gICAgdmFyIGxpbmVOID0gbGluZUF0SGVpZ2h0KGRvYywgeSksIGxhc3QgPSBkb2MuZmlyc3QgKyBkb2Muc2l6ZSAtIDE7XHJcbiAgICBpZiAobGluZU4gPiBsYXN0KVxyXG4gICAgICByZXR1cm4gUG9zV2l0aEluZm8oZG9jLmZpcnN0ICsgZG9jLnNpemUgLSAxLCBnZXRMaW5lKGRvYywgbGFzdCkudGV4dC5sZW5ndGgsIHRydWUsIDEpO1xyXG4gICAgaWYgKHggPCAwKSB4ID0gMDtcclxuXHJcbiAgICB2YXIgbGluZU9iaiA9IGdldExpbmUoZG9jLCBsaW5lTik7XHJcbiAgICBmb3IgKDs7KSB7XHJcbiAgICAgIHZhciBmb3VuZCA9IGNvb3Jkc0NoYXJJbm5lcihjbSwgbGluZU9iaiwgbGluZU4sIHgsIHkpO1xyXG4gICAgICB2YXIgbWVyZ2VkID0gY29sbGFwc2VkU3BhbkF0RW5kKGxpbmVPYmopO1xyXG4gICAgICB2YXIgbWVyZ2VkUG9zID0gbWVyZ2VkICYmIG1lcmdlZC5maW5kKDAsIHRydWUpO1xyXG4gICAgICBpZiAobWVyZ2VkICYmIChmb3VuZC5jaCA+IG1lcmdlZFBvcy5mcm9tLmNoIHx8IGZvdW5kLmNoID09IG1lcmdlZFBvcy5mcm9tLmNoICYmIGZvdW5kLnhSZWwgPiAwKSlcclxuICAgICAgICBsaW5lTiA9IGxpbmVObyhsaW5lT2JqID0gbWVyZ2VkUG9zLnRvLmxpbmUpO1xyXG4gICAgICBlbHNlXHJcbiAgICAgICAgcmV0dXJuIGZvdW5kO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gY29vcmRzQ2hhcklubmVyKGNtLCBsaW5lT2JqLCBsaW5lTm8sIHgsIHkpIHtcclxuICAgIHZhciBpbm5lck9mZiA9IHkgLSBoZWlnaHRBdExpbmUobGluZU9iaik7XHJcbiAgICB2YXIgd3JvbmdMaW5lID0gZmFsc2UsIGFkanVzdCA9IDIgKiBjbS5kaXNwbGF5LndyYXBwZXIuY2xpZW50V2lkdGg7XHJcbiAgICB2YXIgcHJlcGFyZWRNZWFzdXJlID0gcHJlcGFyZU1lYXN1cmVGb3JMaW5lKGNtLCBsaW5lT2JqKTtcclxuXHJcbiAgICBmdW5jdGlvbiBnZXRYKGNoKSB7XHJcbiAgICAgIHZhciBzcCA9IGN1cnNvckNvb3JkcyhjbSwgUG9zKGxpbmVObywgY2gpLCBcImxpbmVcIiwgbGluZU9iaiwgcHJlcGFyZWRNZWFzdXJlKTtcclxuICAgICAgd3JvbmdMaW5lID0gdHJ1ZTtcclxuICAgICAgaWYgKGlubmVyT2ZmID4gc3AuYm90dG9tKSByZXR1cm4gc3AubGVmdCAtIGFkanVzdDtcclxuICAgICAgZWxzZSBpZiAoaW5uZXJPZmYgPCBzcC50b3ApIHJldHVybiBzcC5sZWZ0ICsgYWRqdXN0O1xyXG4gICAgICBlbHNlIHdyb25nTGluZSA9IGZhbHNlO1xyXG4gICAgICByZXR1cm4gc3AubGVmdDtcclxuICAgIH1cclxuXHJcbiAgICB2YXIgYmlkaSA9IGdldE9yZGVyKGxpbmVPYmopLCBkaXN0ID0gbGluZU9iai50ZXh0Lmxlbmd0aDtcclxuICAgIHZhciBmcm9tID0gbGluZUxlZnQobGluZU9iaiksIHRvID0gbGluZVJpZ2h0KGxpbmVPYmopO1xyXG4gICAgdmFyIGZyb21YID0gZ2V0WChmcm9tKSwgZnJvbU91dHNpZGUgPSB3cm9uZ0xpbmUsIHRvWCA9IGdldFgodG8pLCB0b091dHNpZGUgPSB3cm9uZ0xpbmU7XHJcblxyXG4gICAgaWYgKHggPiB0b1gpIHJldHVybiBQb3NXaXRoSW5mbyhsaW5lTm8sIHRvLCB0b091dHNpZGUsIDEpO1xyXG4gICAgLy8gRG8gYSBiaW5hcnkgc2VhcmNoIGJldHdlZW4gdGhlc2UgYm91bmRzLlxyXG4gICAgZm9yICg7Oykge1xyXG4gICAgICBpZiAoYmlkaSA/IHRvID09IGZyb20gfHwgdG8gPT0gbW92ZVZpc3VhbGx5KGxpbmVPYmosIGZyb20sIDEpIDogdG8gLSBmcm9tIDw9IDEpIHtcclxuICAgICAgICB2YXIgY2ggPSB4IDwgZnJvbVggfHwgeCAtIGZyb21YIDw9IHRvWCAtIHggPyBmcm9tIDogdG87XHJcbiAgICAgICAgdmFyIHhEaWZmID0geCAtIChjaCA9PSBmcm9tID8gZnJvbVggOiB0b1gpO1xyXG4gICAgICAgIHdoaWxlIChpc0V4dGVuZGluZ0NoYXIobGluZU9iai50ZXh0LmNoYXJBdChjaCkpKSArK2NoO1xyXG4gICAgICAgIHZhciBwb3MgPSBQb3NXaXRoSW5mbyhsaW5lTm8sIGNoLCBjaCA9PSBmcm9tID8gZnJvbU91dHNpZGUgOiB0b091dHNpZGUsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhEaWZmIDwgLTEgPyAtMSA6IHhEaWZmID4gMSA/IDEgOiAwKTtcclxuICAgICAgICByZXR1cm4gcG9zO1xyXG4gICAgICB9XHJcbiAgICAgIHZhciBzdGVwID0gTWF0aC5jZWlsKGRpc3QgLyAyKSwgbWlkZGxlID0gZnJvbSArIHN0ZXA7XHJcbiAgICAgIGlmIChiaWRpKSB7XHJcbiAgICAgICAgbWlkZGxlID0gZnJvbTtcclxuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHN0ZXA7ICsraSkgbWlkZGxlID0gbW92ZVZpc3VhbGx5KGxpbmVPYmosIG1pZGRsZSwgMSk7XHJcbiAgICAgIH1cclxuICAgICAgdmFyIG1pZGRsZVggPSBnZXRYKG1pZGRsZSk7XHJcbiAgICAgIGlmIChtaWRkbGVYID4geCkge3RvID0gbWlkZGxlOyB0b1ggPSBtaWRkbGVYOyBpZiAodG9PdXRzaWRlID0gd3JvbmdMaW5lKSB0b1ggKz0gMTAwMDsgZGlzdCA9IHN0ZXA7fVxyXG4gICAgICBlbHNlIHtmcm9tID0gbWlkZGxlOyBmcm9tWCA9IG1pZGRsZVg7IGZyb21PdXRzaWRlID0gd3JvbmdMaW5lOyBkaXN0IC09IHN0ZXA7fVxyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgdmFyIG1lYXN1cmVUZXh0O1xyXG4gIC8vIENvbXB1dGUgdGhlIGRlZmF1bHQgdGV4dCBoZWlnaHQuXHJcbiAgZnVuY3Rpb24gdGV4dEhlaWdodChkaXNwbGF5KSB7XHJcbiAgICBpZiAoZGlzcGxheS5jYWNoZWRUZXh0SGVpZ2h0ICE9IG51bGwpIHJldHVybiBkaXNwbGF5LmNhY2hlZFRleHRIZWlnaHQ7XHJcbiAgICBpZiAobWVhc3VyZVRleHQgPT0gbnVsbCkge1xyXG4gICAgICBtZWFzdXJlVGV4dCA9IGVsdChcInByZVwiKTtcclxuICAgICAgLy8gTWVhc3VyZSBhIGJ1bmNoIG9mIGxpbmVzLCBmb3IgYnJvd3NlcnMgdGhhdCBjb21wdXRlXHJcbiAgICAgIC8vIGZyYWN0aW9uYWwgaGVpZ2h0cy5cclxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCA0OTsgKytpKSB7XHJcbiAgICAgICAgbWVhc3VyZVRleHQuYXBwZW5kQ2hpbGQoZG9jdW1lbnQuY3JlYXRlVGV4dE5vZGUoXCJ4XCIpKTtcclxuICAgICAgICBtZWFzdXJlVGV4dC5hcHBlbmRDaGlsZChlbHQoXCJiclwiKSk7XHJcbiAgICAgIH1cclxuICAgICAgbWVhc3VyZVRleHQuYXBwZW5kQ2hpbGQoZG9jdW1lbnQuY3JlYXRlVGV4dE5vZGUoXCJ4XCIpKTtcclxuICAgIH1cclxuICAgIHJlbW92ZUNoaWxkcmVuQW5kQWRkKGRpc3BsYXkubWVhc3VyZSwgbWVhc3VyZVRleHQpO1xyXG4gICAgdmFyIGhlaWdodCA9IG1lYXN1cmVUZXh0Lm9mZnNldEhlaWdodCAvIDUwO1xyXG4gICAgaWYgKGhlaWdodCA+IDMpIGRpc3BsYXkuY2FjaGVkVGV4dEhlaWdodCA9IGhlaWdodDtcclxuICAgIHJlbW92ZUNoaWxkcmVuKGRpc3BsYXkubWVhc3VyZSk7XHJcbiAgICByZXR1cm4gaGVpZ2h0IHx8IDE7XHJcbiAgfVxyXG5cclxuICAvLyBDb21wdXRlIHRoZSBkZWZhdWx0IGNoYXJhY3RlciB3aWR0aC5cclxuICBmdW5jdGlvbiBjaGFyV2lkdGgoZGlzcGxheSkge1xyXG4gICAgaWYgKGRpc3BsYXkuY2FjaGVkQ2hhcldpZHRoICE9IG51bGwpIHJldHVybiBkaXNwbGF5LmNhY2hlZENoYXJXaWR0aDtcclxuICAgIHZhciBhbmNob3IgPSBlbHQoXCJzcGFuXCIsIFwieHh4eHh4eHh4eFwiKTtcclxuICAgIHZhciBwcmUgPSBlbHQoXCJwcmVcIiwgW2FuY2hvcl0pO1xyXG4gICAgcmVtb3ZlQ2hpbGRyZW5BbmRBZGQoZGlzcGxheS5tZWFzdXJlLCBwcmUpO1xyXG4gICAgdmFyIHJlY3QgPSBhbmNob3IuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCksIHdpZHRoID0gKHJlY3QucmlnaHQgLSByZWN0LmxlZnQpIC8gMTA7XHJcbiAgICBpZiAod2lkdGggPiAyKSBkaXNwbGF5LmNhY2hlZENoYXJXaWR0aCA9IHdpZHRoO1xyXG4gICAgcmV0dXJuIHdpZHRoIHx8IDEwO1xyXG4gIH1cclxuXHJcbiAgLy8gT1BFUkFUSU9OU1xyXG5cclxuICAvLyBPcGVyYXRpb25zIGFyZSB1c2VkIHRvIHdyYXAgYSBzZXJpZXMgb2YgY2hhbmdlcyB0byB0aGUgZWRpdG9yXHJcbiAgLy8gc3RhdGUgaW4gc3VjaCBhIHdheSB0aGF0IGVhY2ggY2hhbmdlIHdvbid0IGhhdmUgdG8gdXBkYXRlIHRoZVxyXG4gIC8vIGN1cnNvciBhbmQgZGlzcGxheSAod2hpY2ggd291bGQgYmUgYXdrd2FyZCwgc2xvdywgYW5kXHJcbiAgLy8gZXJyb3ItcHJvbmUpLiBJbnN0ZWFkLCBkaXNwbGF5IHVwZGF0ZXMgYXJlIGJhdGNoZWQgYW5kIHRoZW4gYWxsXHJcbiAgLy8gY29tYmluZWQgYW5kIGV4ZWN1dGVkIGF0IG9uY2UuXHJcblxyXG4gIHZhciBvcGVyYXRpb25Hcm91cCA9IG51bGw7XHJcblxyXG4gIHZhciBuZXh0T3BJZCA9IDA7XHJcbiAgLy8gU3RhcnQgYSBuZXcgb3BlcmF0aW9uLlxyXG4gIGZ1bmN0aW9uIHN0YXJ0T3BlcmF0aW9uKGNtKSB7XHJcbiAgICBjbS5jdXJPcCA9IHtcclxuICAgICAgY206IGNtLFxyXG4gICAgICB2aWV3Q2hhbmdlZDogZmFsc2UsICAgICAgLy8gRmxhZyB0aGF0IGluZGljYXRlcyB0aGF0IGxpbmVzIG1pZ2h0IG5lZWQgdG8gYmUgcmVkcmF3blxyXG4gICAgICBzdGFydEhlaWdodDogY20uZG9jLmhlaWdodCwgLy8gVXNlZCB0byBkZXRlY3QgbmVlZCB0byB1cGRhdGUgc2Nyb2xsYmFyXHJcbiAgICAgIGZvcmNlVXBkYXRlOiBmYWxzZSwgICAgICAvLyBVc2VkIHRvIGZvcmNlIGEgcmVkcmF3XHJcbiAgICAgIHVwZGF0ZUlucHV0OiBudWxsLCAgICAgICAvLyBXaGV0aGVyIHRvIHJlc2V0IHRoZSBpbnB1dCB0ZXh0YXJlYVxyXG4gICAgICB0eXBpbmc6IGZhbHNlLCAgICAgICAgICAgLy8gV2hldGhlciB0aGlzIHJlc2V0IHNob3VsZCBiZSBjYXJlZnVsIHRvIGxlYXZlIGV4aXN0aW5nIHRleHQgKGZvciBjb21wb3NpdGluZylcclxuICAgICAgY2hhbmdlT2JqczogbnVsbCwgICAgICAgIC8vIEFjY3VtdWxhdGVkIGNoYW5nZXMsIGZvciBmaXJpbmcgY2hhbmdlIGV2ZW50c1xyXG4gICAgICBjdXJzb3JBY3Rpdml0eUhhbmRsZXJzOiBudWxsLCAvLyBTZXQgb2YgaGFuZGxlcnMgdG8gZmlyZSBjdXJzb3JBY3Rpdml0eSBvblxyXG4gICAgICBjdXJzb3JBY3Rpdml0eUNhbGxlZDogMCwgLy8gVHJhY2tzIHdoaWNoIGN1cnNvckFjdGl2aXR5IGhhbmRsZXJzIGhhdmUgYmVlbiBjYWxsZWQgYWxyZWFkeVxyXG4gICAgICBzZWxlY3Rpb25DaGFuZ2VkOiBmYWxzZSwgLy8gV2hldGhlciB0aGUgc2VsZWN0aW9uIG5lZWRzIHRvIGJlIHJlZHJhd25cclxuICAgICAgdXBkYXRlTWF4TGluZTogZmFsc2UsICAgIC8vIFNldCB3aGVuIHRoZSB3aWRlc3QgbGluZSBuZWVkcyB0byBiZSBkZXRlcm1pbmVkIGFuZXdcclxuICAgICAgc2Nyb2xsTGVmdDogbnVsbCwgc2Nyb2xsVG9wOiBudWxsLCAvLyBJbnRlcm1lZGlhdGUgc2Nyb2xsIHBvc2l0aW9uLCBub3QgcHVzaGVkIHRvIERPTSB5ZXRcclxuICAgICAgc2Nyb2xsVG9Qb3M6IG51bGwsICAgICAgIC8vIFVzZWQgdG8gc2Nyb2xsIHRvIGEgc3BlY2lmaWMgcG9zaXRpb25cclxuICAgICAgZm9jdXM6IGZhbHNlLFxyXG4gICAgICBpZDogKytuZXh0T3BJZCAgICAgICAgICAgLy8gVW5pcXVlIElEXHJcbiAgICB9O1xyXG4gICAgaWYgKG9wZXJhdGlvbkdyb3VwKSB7XHJcbiAgICAgIG9wZXJhdGlvbkdyb3VwLm9wcy5wdXNoKGNtLmN1ck9wKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGNtLmN1ck9wLm93bnNHcm91cCA9IG9wZXJhdGlvbkdyb3VwID0ge1xyXG4gICAgICAgIG9wczogW2NtLmN1ck9wXSxcclxuICAgICAgICBkZWxheWVkQ2FsbGJhY2tzOiBbXVxyXG4gICAgICB9O1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gZmlyZUNhbGxiYWNrc0Zvck9wcyhncm91cCkge1xyXG4gICAgLy8gQ2FsbHMgZGVsYXllZCBjYWxsYmFja3MgYW5kIGN1cnNvckFjdGl2aXR5IGhhbmRsZXJzIHVudGlsIG5vXHJcbiAgICAvLyBuZXcgb25lcyBhcHBlYXJcclxuICAgIHZhciBjYWxsYmFja3MgPSBncm91cC5kZWxheWVkQ2FsbGJhY2tzLCBpID0gMDtcclxuICAgIGRvIHtcclxuICAgICAgZm9yICg7IGkgPCBjYWxsYmFja3MubGVuZ3RoOyBpKyspXHJcbiAgICAgICAgY2FsbGJhY2tzW2ldLmNhbGwobnVsbCk7XHJcbiAgICAgIGZvciAodmFyIGogPSAwOyBqIDwgZ3JvdXAub3BzLmxlbmd0aDsgaisrKSB7XHJcbiAgICAgICAgdmFyIG9wID0gZ3JvdXAub3BzW2pdO1xyXG4gICAgICAgIGlmIChvcC5jdXJzb3JBY3Rpdml0eUhhbmRsZXJzKVxyXG4gICAgICAgICAgd2hpbGUgKG9wLmN1cnNvckFjdGl2aXR5Q2FsbGVkIDwgb3AuY3Vyc29yQWN0aXZpdHlIYW5kbGVycy5sZW5ndGgpXHJcbiAgICAgICAgICAgIG9wLmN1cnNvckFjdGl2aXR5SGFuZGxlcnNbb3AuY3Vyc29yQWN0aXZpdHlDYWxsZWQrK10uY2FsbChudWxsLCBvcC5jbSk7XHJcbiAgICAgIH1cclxuICAgIH0gd2hpbGUgKGkgPCBjYWxsYmFja3MubGVuZ3RoKTtcclxuICB9XHJcblxyXG4gIC8vIEZpbmlzaCBhbiBvcGVyYXRpb24sIHVwZGF0aW5nIHRoZSBkaXNwbGF5IGFuZCBzaWduYWxsaW5nIGRlbGF5ZWQgZXZlbnRzXHJcbiAgZnVuY3Rpb24gZW5kT3BlcmF0aW9uKGNtKSB7XHJcbiAgICB2YXIgb3AgPSBjbS5jdXJPcCwgZ3JvdXAgPSBvcC5vd25zR3JvdXA7XHJcbiAgICBpZiAoIWdyb3VwKSByZXR1cm47XHJcblxyXG4gICAgdHJ5IHsgZmlyZUNhbGxiYWNrc0Zvck9wcyhncm91cCk7IH1cclxuICAgIGZpbmFsbHkge1xyXG4gICAgICBvcGVyYXRpb25Hcm91cCA9IG51bGw7XHJcbiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgZ3JvdXAub3BzLmxlbmd0aDsgaSsrKVxyXG4gICAgICAgIGdyb3VwLm9wc1tpXS5jbS5jdXJPcCA9IG51bGw7XHJcbiAgICAgIGVuZE9wZXJhdGlvbnMoZ3JvdXApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gVGhlIERPTSB1cGRhdGVzIGRvbmUgd2hlbiBhbiBvcGVyYXRpb24gZmluaXNoZXMgYXJlIGJhdGNoZWQgc29cclxuICAvLyB0aGF0IHRoZSBtaW5pbXVtIG51bWJlciBvZiByZWxheW91dHMgYXJlIHJlcXVpcmVkLlxyXG4gIGZ1bmN0aW9uIGVuZE9wZXJhdGlvbnMoZ3JvdXApIHtcclxuICAgIHZhciBvcHMgPSBncm91cC5vcHM7XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IG9wcy5sZW5ndGg7IGkrKykgLy8gUmVhZCBET01cclxuICAgICAgZW5kT3BlcmF0aW9uX1IxKG9wc1tpXSk7XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IG9wcy5sZW5ndGg7IGkrKykgLy8gV3JpdGUgRE9NIChtYXliZSlcclxuICAgICAgZW5kT3BlcmF0aW9uX1cxKG9wc1tpXSk7XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IG9wcy5sZW5ndGg7IGkrKykgLy8gUmVhZCBET01cclxuICAgICAgZW5kT3BlcmF0aW9uX1IyKG9wc1tpXSk7XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IG9wcy5sZW5ndGg7IGkrKykgLy8gV3JpdGUgRE9NIChtYXliZSlcclxuICAgICAgZW5kT3BlcmF0aW9uX1cyKG9wc1tpXSk7XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IG9wcy5sZW5ndGg7IGkrKykgLy8gUmVhZCBET01cclxuICAgICAgZW5kT3BlcmF0aW9uX2ZpbmlzaChvcHNbaV0pO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gZW5kT3BlcmF0aW9uX1IxKG9wKSB7XHJcbiAgICB2YXIgY20gPSBvcC5jbSwgZGlzcGxheSA9IGNtLmRpc3BsYXk7XHJcbiAgICBtYXliZUNsaXBTY3JvbGxiYXJzKGNtKTtcclxuICAgIGlmIChvcC51cGRhdGVNYXhMaW5lKSBmaW5kTWF4TGluZShjbSk7XHJcblxyXG4gICAgb3AubXVzdFVwZGF0ZSA9IG9wLnZpZXdDaGFuZ2VkIHx8IG9wLmZvcmNlVXBkYXRlIHx8IG9wLnNjcm9sbFRvcCAhPSBudWxsIHx8XHJcbiAgICAgIG9wLnNjcm9sbFRvUG9zICYmIChvcC5zY3JvbGxUb1Bvcy5mcm9tLmxpbmUgPCBkaXNwbGF5LnZpZXdGcm9tIHx8XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICBvcC5zY3JvbGxUb1Bvcy50by5saW5lID49IGRpc3BsYXkudmlld1RvKSB8fFxyXG4gICAgICBkaXNwbGF5Lm1heExpbmVDaGFuZ2VkICYmIGNtLm9wdGlvbnMubGluZVdyYXBwaW5nO1xyXG4gICAgb3AudXBkYXRlID0gb3AubXVzdFVwZGF0ZSAmJlxyXG4gICAgICBuZXcgRGlzcGxheVVwZGF0ZShjbSwgb3AubXVzdFVwZGF0ZSAmJiB7dG9wOiBvcC5zY3JvbGxUb3AsIGVuc3VyZTogb3Auc2Nyb2xsVG9Qb3N9LCBvcC5mb3JjZVVwZGF0ZSk7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBlbmRPcGVyYXRpb25fVzEob3ApIHtcclxuICAgIG9wLnVwZGF0ZWREaXNwbGF5ID0gb3AubXVzdFVwZGF0ZSAmJiB1cGRhdGVEaXNwbGF5SWZOZWVkZWQob3AuY20sIG9wLnVwZGF0ZSk7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBlbmRPcGVyYXRpb25fUjIob3ApIHtcclxuICAgIHZhciBjbSA9IG9wLmNtLCBkaXNwbGF5ID0gY20uZGlzcGxheTtcclxuICAgIGlmIChvcC51cGRhdGVkRGlzcGxheSkgdXBkYXRlSGVpZ2h0c0luVmlld3BvcnQoY20pO1xyXG5cclxuICAgIG9wLmJhck1lYXN1cmUgPSBtZWFzdXJlRm9yU2Nyb2xsYmFycyhjbSk7XHJcblxyXG4gICAgLy8gSWYgdGhlIG1heCBsaW5lIGNoYW5nZWQgc2luY2UgaXQgd2FzIGxhc3QgbWVhc3VyZWQsIG1lYXN1cmUgaXQsXHJcbiAgICAvLyBhbmQgZW5zdXJlIHRoZSBkb2N1bWVudCdzIHdpZHRoIG1hdGNoZXMgaXQuXHJcbiAgICAvLyB1cGRhdGVEaXNwbGF5X1cyIHdpbGwgdXNlIHRoZXNlIHByb3BlcnRpZXMgdG8gZG8gdGhlIGFjdHVhbCByZXNpemluZ1xyXG4gICAgaWYgKGRpc3BsYXkubWF4TGluZUNoYW5nZWQgJiYgIWNtLm9wdGlvbnMubGluZVdyYXBwaW5nKSB7XHJcbiAgICAgIG9wLmFkanVzdFdpZHRoVG8gPSBtZWFzdXJlQ2hhcihjbSwgZGlzcGxheS5tYXhMaW5lLCBkaXNwbGF5Lm1heExpbmUudGV4dC5sZW5ndGgpLmxlZnQgKyAzO1xyXG4gICAgICBjbS5kaXNwbGF5LnNpemVyV2lkdGggPSBvcC5hZGp1c3RXaWR0aFRvO1xyXG4gICAgICBvcC5iYXJNZWFzdXJlLnNjcm9sbFdpZHRoID1cclxuICAgICAgICBNYXRoLm1heChkaXNwbGF5LnNjcm9sbGVyLmNsaWVudFdpZHRoLCBkaXNwbGF5LnNpemVyLm9mZnNldExlZnQgKyBvcC5hZGp1c3RXaWR0aFRvICsgc2Nyb2xsR2FwKGNtKSArIGNtLmRpc3BsYXkuYmFyV2lkdGgpO1xyXG4gICAgICBvcC5tYXhTY3JvbGxMZWZ0ID0gTWF0aC5tYXgoMCwgZGlzcGxheS5zaXplci5vZmZzZXRMZWZ0ICsgb3AuYWRqdXN0V2lkdGhUbyAtIGRpc3BsYXlXaWR0aChjbSkpO1xyXG4gICAgfVxyXG5cclxuICAgIGlmIChvcC51cGRhdGVkRGlzcGxheSB8fCBvcC5zZWxlY3Rpb25DaGFuZ2VkKVxyXG4gICAgICBvcC5wcmVwYXJlZFNlbGVjdGlvbiA9IGRpc3BsYXkuaW5wdXQucHJlcGFyZVNlbGVjdGlvbigpO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gZW5kT3BlcmF0aW9uX1cyKG9wKSB7XHJcbiAgICB2YXIgY20gPSBvcC5jbTtcclxuXHJcbiAgICBpZiAob3AuYWRqdXN0V2lkdGhUbyAhPSBudWxsKSB7XHJcbiAgICAgIGNtLmRpc3BsYXkuc2l6ZXIuc3R5bGUubWluV2lkdGggPSBvcC5hZGp1c3RXaWR0aFRvICsgXCJweFwiO1xyXG4gICAgICBpZiAob3AubWF4U2Nyb2xsTGVmdCA8IGNtLmRvYy5zY3JvbGxMZWZ0KVxyXG4gICAgICAgIHNldFNjcm9sbExlZnQoY20sIE1hdGgubWluKGNtLmRpc3BsYXkuc2Nyb2xsZXIuc2Nyb2xsTGVmdCwgb3AubWF4U2Nyb2xsTGVmdCksIHRydWUpO1xyXG4gICAgICBjbS5kaXNwbGF5Lm1heExpbmVDaGFuZ2VkID0gZmFsc2U7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKG9wLnByZXBhcmVkU2VsZWN0aW9uKVxyXG4gICAgICBjbS5kaXNwbGF5LmlucHV0LnNob3dTZWxlY3Rpb24ob3AucHJlcGFyZWRTZWxlY3Rpb24pO1xyXG4gICAgaWYgKG9wLnVwZGF0ZWREaXNwbGF5KVxyXG4gICAgICBzZXREb2N1bWVudEhlaWdodChjbSwgb3AuYmFyTWVhc3VyZSk7XHJcbiAgICBpZiAob3AudXBkYXRlZERpc3BsYXkgfHwgb3Auc3RhcnRIZWlnaHQgIT0gY20uZG9jLmhlaWdodClcclxuICAgICAgdXBkYXRlU2Nyb2xsYmFycyhjbSwgb3AuYmFyTWVhc3VyZSk7XHJcblxyXG4gICAgaWYgKG9wLnNlbGVjdGlvbkNoYW5nZWQpIHJlc3RhcnRCbGluayhjbSk7XHJcblxyXG4gICAgaWYgKGNtLnN0YXRlLmZvY3VzZWQgJiYgb3AudXBkYXRlSW5wdXQpXHJcbiAgICAgIGNtLmRpc3BsYXkuaW5wdXQucmVzZXQob3AudHlwaW5nKTtcclxuICAgIGlmIChvcC5mb2N1cyAmJiBvcC5mb2N1cyA9PSBhY3RpdmVFbHQoKSkgZW5zdXJlRm9jdXMob3AuY20pO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gZW5kT3BlcmF0aW9uX2ZpbmlzaChvcCkge1xyXG4gICAgdmFyIGNtID0gb3AuY20sIGRpc3BsYXkgPSBjbS5kaXNwbGF5LCBkb2MgPSBjbS5kb2M7XHJcblxyXG4gICAgaWYgKG9wLnVwZGF0ZWREaXNwbGF5KSBwb3N0VXBkYXRlRGlzcGxheShjbSwgb3AudXBkYXRlKTtcclxuXHJcbiAgICAvLyBBYm9ydCBtb3VzZSB3aGVlbCBkZWx0YSBtZWFzdXJlbWVudCwgd2hlbiBzY3JvbGxpbmcgZXhwbGljaXRseVxyXG4gICAgaWYgKGRpc3BsYXkud2hlZWxTdGFydFggIT0gbnVsbCAmJiAob3Auc2Nyb2xsVG9wICE9IG51bGwgfHwgb3Auc2Nyb2xsTGVmdCAhPSBudWxsIHx8IG9wLnNjcm9sbFRvUG9zKSlcclxuICAgICAgZGlzcGxheS53aGVlbFN0YXJ0WCA9IGRpc3BsYXkud2hlZWxTdGFydFkgPSBudWxsO1xyXG5cclxuICAgIC8vIFByb3BhZ2F0ZSB0aGUgc2Nyb2xsIHBvc2l0aW9uIHRvIHRoZSBhY3R1YWwgRE9NIHNjcm9sbGVyXHJcbiAgICBpZiAob3Auc2Nyb2xsVG9wICE9IG51bGwgJiYgKGRpc3BsYXkuc2Nyb2xsZXIuc2Nyb2xsVG9wICE9IG9wLnNjcm9sbFRvcCB8fCBvcC5mb3JjZVNjcm9sbCkpIHtcclxuICAgICAgZG9jLnNjcm9sbFRvcCA9IE1hdGgubWF4KDAsIE1hdGgubWluKGRpc3BsYXkuc2Nyb2xsZXIuc2Nyb2xsSGVpZ2h0IC0gZGlzcGxheS5zY3JvbGxlci5jbGllbnRIZWlnaHQsIG9wLnNjcm9sbFRvcCkpO1xyXG4gICAgICBkaXNwbGF5LnNjcm9sbGJhcnMuc2V0U2Nyb2xsVG9wKGRvYy5zY3JvbGxUb3ApO1xyXG4gICAgICBkaXNwbGF5LnNjcm9sbGVyLnNjcm9sbFRvcCA9IGRvYy5zY3JvbGxUb3A7XHJcbiAgICB9XHJcbiAgICBpZiAob3Auc2Nyb2xsTGVmdCAhPSBudWxsICYmIChkaXNwbGF5LnNjcm9sbGVyLnNjcm9sbExlZnQgIT0gb3Auc2Nyb2xsTGVmdCB8fCBvcC5mb3JjZVNjcm9sbCkpIHtcclxuICAgICAgZG9jLnNjcm9sbExlZnQgPSBNYXRoLm1heCgwLCBNYXRoLm1pbihkaXNwbGF5LnNjcm9sbGVyLnNjcm9sbFdpZHRoIC0gZGlzcGxheVdpZHRoKGNtKSwgb3Auc2Nyb2xsTGVmdCkpO1xyXG4gICAgICBkaXNwbGF5LnNjcm9sbGJhcnMuc2V0U2Nyb2xsTGVmdChkb2Muc2Nyb2xsTGVmdCk7XHJcbiAgICAgIGRpc3BsYXkuc2Nyb2xsZXIuc2Nyb2xsTGVmdCA9IGRvYy5zY3JvbGxMZWZ0O1xyXG4gICAgICBhbGlnbkhvcml6b250YWxseShjbSk7XHJcbiAgICB9XHJcbiAgICAvLyBJZiB3ZSBuZWVkIHRvIHNjcm9sbCBhIHNwZWNpZmljIHBvc2l0aW9uIGludG8gdmlldywgZG8gc28uXHJcbiAgICBpZiAob3Auc2Nyb2xsVG9Qb3MpIHtcclxuICAgICAgdmFyIGNvb3JkcyA9IHNjcm9sbFBvc0ludG9WaWV3KGNtLCBjbGlwUG9zKGRvYywgb3Auc2Nyb2xsVG9Qb3MuZnJvbSksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGlwUG9zKGRvYywgb3Auc2Nyb2xsVG9Qb3MudG8pLCBvcC5zY3JvbGxUb1Bvcy5tYXJnaW4pO1xyXG4gICAgICBpZiAob3Auc2Nyb2xsVG9Qb3MuaXNDdXJzb3IgJiYgY20uc3RhdGUuZm9jdXNlZCkgbWF5YmVTY3JvbGxXaW5kb3coY20sIGNvb3Jkcyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRmlyZSBldmVudHMgZm9yIG1hcmtlcnMgdGhhdCBhcmUgaGlkZGVuL3VuaWRkZW4gYnkgZWRpdGluZyBvclxyXG4gICAgLy8gdW5kb2luZ1xyXG4gICAgdmFyIGhpZGRlbiA9IG9wLm1heWJlSGlkZGVuTWFya2VycywgdW5oaWRkZW4gPSBvcC5tYXliZVVuaGlkZGVuTWFya2VycztcclxuICAgIGlmIChoaWRkZW4pIGZvciAodmFyIGkgPSAwOyBpIDwgaGlkZGVuLmxlbmd0aDsgKytpKVxyXG4gICAgICBpZiAoIWhpZGRlbltpXS5saW5lcy5sZW5ndGgpIHNpZ25hbChoaWRkZW5baV0sIFwiaGlkZVwiKTtcclxuICAgIGlmICh1bmhpZGRlbikgZm9yICh2YXIgaSA9IDA7IGkgPCB1bmhpZGRlbi5sZW5ndGg7ICsraSlcclxuICAgICAgaWYgKHVuaGlkZGVuW2ldLmxpbmVzLmxlbmd0aCkgc2lnbmFsKHVuaGlkZGVuW2ldLCBcInVuaGlkZVwiKTtcclxuXHJcbiAgICBpZiAoZGlzcGxheS53cmFwcGVyLm9mZnNldEhlaWdodClcclxuICAgICAgZG9jLnNjcm9sbFRvcCA9IGNtLmRpc3BsYXkuc2Nyb2xsZXIuc2Nyb2xsVG9wO1xyXG5cclxuICAgIC8vIEZpcmUgY2hhbmdlIGV2ZW50cywgYW5kIGRlbGF5ZWQgZXZlbnQgaGFuZGxlcnNcclxuICAgIGlmIChvcC5jaGFuZ2VPYmpzKVxyXG4gICAgICBzaWduYWwoY20sIFwiY2hhbmdlc1wiLCBjbSwgb3AuY2hhbmdlT2Jqcyk7XHJcbiAgICBpZiAob3AudXBkYXRlKVxyXG4gICAgICBvcC51cGRhdGUuZmluaXNoKCk7XHJcbiAgfVxyXG5cclxuICAvLyBSdW4gdGhlIGdpdmVuIGZ1bmN0aW9uIGluIGFuIG9wZXJhdGlvblxyXG4gIGZ1bmN0aW9uIHJ1bkluT3AoY20sIGYpIHtcclxuICAgIGlmIChjbS5jdXJPcCkgcmV0dXJuIGYoKTtcclxuICAgIHN0YXJ0T3BlcmF0aW9uKGNtKTtcclxuICAgIHRyeSB7IHJldHVybiBmKCk7IH1cclxuICAgIGZpbmFsbHkgeyBlbmRPcGVyYXRpb24oY20pOyB9XHJcbiAgfVxyXG4gIC8vIFdyYXBzIGEgZnVuY3Rpb24gaW4gYW4gb3BlcmF0aW9uLiBSZXR1cm5zIHRoZSB3cmFwcGVkIGZ1bmN0aW9uLlxyXG4gIGZ1bmN0aW9uIG9wZXJhdGlvbihjbSwgZikge1xyXG4gICAgcmV0dXJuIGZ1bmN0aW9uKCkge1xyXG4gICAgICBpZiAoY20uY3VyT3ApIHJldHVybiBmLmFwcGx5KGNtLCBhcmd1bWVudHMpO1xyXG4gICAgICBzdGFydE9wZXJhdGlvbihjbSk7XHJcbiAgICAgIHRyeSB7IHJldHVybiBmLmFwcGx5KGNtLCBhcmd1bWVudHMpOyB9XHJcbiAgICAgIGZpbmFsbHkgeyBlbmRPcGVyYXRpb24oY20pOyB9XHJcbiAgICB9O1xyXG4gIH1cclxuICAvLyBVc2VkIHRvIGFkZCBtZXRob2RzIHRvIGVkaXRvciBhbmQgZG9jIGluc3RhbmNlcywgd3JhcHBpbmcgdGhlbSBpblxyXG4gIC8vIG9wZXJhdGlvbnMuXHJcbiAgZnVuY3Rpb24gbWV0aG9kT3AoZikge1xyXG4gICAgcmV0dXJuIGZ1bmN0aW9uKCkge1xyXG4gICAgICBpZiAodGhpcy5jdXJPcCkgcmV0dXJuIGYuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcclxuICAgICAgc3RhcnRPcGVyYXRpb24odGhpcyk7XHJcbiAgICAgIHRyeSB7IHJldHVybiBmLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7IH1cclxuICAgICAgZmluYWxseSB7IGVuZE9wZXJhdGlvbih0aGlzKTsgfVxyXG4gICAgfTtcclxuICB9XHJcbiAgZnVuY3Rpb24gZG9jTWV0aG9kT3AoZikge1xyXG4gICAgcmV0dXJuIGZ1bmN0aW9uKCkge1xyXG4gICAgICB2YXIgY20gPSB0aGlzLmNtO1xyXG4gICAgICBpZiAoIWNtIHx8IGNtLmN1ck9wKSByZXR1cm4gZi5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xyXG4gICAgICBzdGFydE9wZXJhdGlvbihjbSk7XHJcbiAgICAgIHRyeSB7IHJldHVybiBmLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7IH1cclxuICAgICAgZmluYWxseSB7IGVuZE9wZXJhdGlvbihjbSk7IH1cclxuICAgIH07XHJcbiAgfVxyXG5cclxuICAvLyBWSUVXIFRSQUNLSU5HXHJcblxyXG4gIC8vIFRoZXNlIG9iamVjdHMgYXJlIHVzZWQgdG8gcmVwcmVzZW50IHRoZSB2aXNpYmxlIChjdXJyZW50bHkgZHJhd24pXHJcbiAgLy8gcGFydCBvZiB0aGUgZG9jdW1lbnQuIEEgTGluZVZpZXcgbWF5IGNvcnJlc3BvbmQgdG8gbXVsdGlwbGVcclxuICAvLyBsb2dpY2FsIGxpbmVzLCBpZiB0aG9zZSBhcmUgY29ubmVjdGVkIGJ5IGNvbGxhcHNlZCByYW5nZXMuXHJcbiAgZnVuY3Rpb24gTGluZVZpZXcoZG9jLCBsaW5lLCBsaW5lTikge1xyXG4gICAgLy8gVGhlIHN0YXJ0aW5nIGxpbmVcclxuICAgIHRoaXMubGluZSA9IGxpbmU7XHJcbiAgICAvLyBDb250aW51aW5nIGxpbmVzLCBpZiBhbnlcclxuICAgIHRoaXMucmVzdCA9IHZpc3VhbExpbmVDb250aW51ZWQobGluZSk7XHJcbiAgICAvLyBOdW1iZXIgb2YgbG9naWNhbCBsaW5lcyBpbiB0aGlzIHZpc3VhbCBsaW5lXHJcbiAgICB0aGlzLnNpemUgPSB0aGlzLnJlc3QgPyBsaW5lTm8obHN0KHRoaXMucmVzdCkpIC0gbGluZU4gKyAxIDogMTtcclxuICAgIHRoaXMubm9kZSA9IHRoaXMudGV4dCA9IG51bGw7XHJcbiAgICB0aGlzLmhpZGRlbiA9IGxpbmVJc0hpZGRlbihkb2MsIGxpbmUpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ3JlYXRlIGEgcmFuZ2Ugb2YgTGluZVZpZXcgb2JqZWN0cyBmb3IgdGhlIGdpdmVuIGxpbmVzLlxyXG4gIGZ1bmN0aW9uIGJ1aWxkVmlld0FycmF5KGNtLCBmcm9tLCB0bykge1xyXG4gICAgdmFyIGFycmF5ID0gW10sIG5leHRQb3M7XHJcbiAgICBmb3IgKHZhciBwb3MgPSBmcm9tOyBwb3MgPCB0bzsgcG9zID0gbmV4dFBvcykge1xyXG4gICAgICB2YXIgdmlldyA9IG5ldyBMaW5lVmlldyhjbS5kb2MsIGdldExpbmUoY20uZG9jLCBwb3MpLCBwb3MpO1xyXG4gICAgICBuZXh0UG9zID0gcG9zICsgdmlldy5zaXplO1xyXG4gICAgICBhcnJheS5wdXNoKHZpZXcpO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIGFycmF5O1xyXG4gIH1cclxuXHJcbiAgLy8gVXBkYXRlcyB0aGUgZGlzcGxheS52aWV3IGRhdGEgc3RydWN0dXJlIGZvciBhIGdpdmVuIGNoYW5nZSB0byB0aGVcclxuICAvLyBkb2N1bWVudC4gRnJvbSBhbmQgdG8gYXJlIGluIHByZS1jaGFuZ2UgY29vcmRpbmF0ZXMuIExlbmRpZmYgaXNcclxuICAvLyB0aGUgYW1vdW50IG9mIGxpbmVzIGFkZGVkIG9yIHN1YnRyYWN0ZWQgYnkgdGhlIGNoYW5nZS4gVGhpcyBpc1xyXG4gIC8vIHVzZWQgZm9yIGNoYW5nZXMgdGhhdCBzcGFuIG11bHRpcGxlIGxpbmVzLCBvciBjaGFuZ2UgdGhlIHdheVxyXG4gIC8vIGxpbmVzIGFyZSBkaXZpZGVkIGludG8gdmlzdWFsIGxpbmVzLiByZWdMaW5lQ2hhbmdlIChiZWxvdylcclxuICAvLyByZWdpc3RlcnMgc2luZ2xlLWxpbmUgY2hhbmdlcy5cclxuICBmdW5jdGlvbiByZWdDaGFuZ2UoY20sIGZyb20sIHRvLCBsZW5kaWZmKSB7XHJcbiAgICBpZiAoZnJvbSA9PSBudWxsKSBmcm9tID0gY20uZG9jLmZpcnN0O1xyXG4gICAgaWYgKHRvID09IG51bGwpIHRvID0gY20uZG9jLmZpcnN0ICsgY20uZG9jLnNpemU7XHJcbiAgICBpZiAoIWxlbmRpZmYpIGxlbmRpZmYgPSAwO1xyXG5cclxuICAgIHZhciBkaXNwbGF5ID0gY20uZGlzcGxheTtcclxuICAgIGlmIChsZW5kaWZmICYmIHRvIDwgZGlzcGxheS52aWV3VG8gJiZcclxuICAgICAgICAoZGlzcGxheS51cGRhdGVMaW5lTnVtYmVycyA9PSBudWxsIHx8IGRpc3BsYXkudXBkYXRlTGluZU51bWJlcnMgPiBmcm9tKSlcclxuICAgICAgZGlzcGxheS51cGRhdGVMaW5lTnVtYmVycyA9IGZyb207XHJcblxyXG4gICAgY20uY3VyT3Audmlld0NoYW5nZWQgPSB0cnVlO1xyXG5cclxuICAgIGlmIChmcm9tID49IGRpc3BsYXkudmlld1RvKSB7IC8vIENoYW5nZSBhZnRlclxyXG4gICAgICBpZiAoc2F3Q29sbGFwc2VkU3BhbnMgJiYgdmlzdWFsTGluZU5vKGNtLmRvYywgZnJvbSkgPCBkaXNwbGF5LnZpZXdUbylcclxuICAgICAgICByZXNldFZpZXcoY20pO1xyXG4gICAgfSBlbHNlIGlmICh0byA8PSBkaXNwbGF5LnZpZXdGcm9tKSB7IC8vIENoYW5nZSBiZWZvcmVcclxuICAgICAgaWYgKHNhd0NvbGxhcHNlZFNwYW5zICYmIHZpc3VhbExpbmVFbmRObyhjbS5kb2MsIHRvICsgbGVuZGlmZikgPiBkaXNwbGF5LnZpZXdGcm9tKSB7XHJcbiAgICAgICAgcmVzZXRWaWV3KGNtKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBkaXNwbGF5LnZpZXdGcm9tICs9IGxlbmRpZmY7XHJcbiAgICAgICAgZGlzcGxheS52aWV3VG8gKz0gbGVuZGlmZjtcclxuICAgICAgfVxyXG4gICAgfSBlbHNlIGlmIChmcm9tIDw9IGRpc3BsYXkudmlld0Zyb20gJiYgdG8gPj0gZGlzcGxheS52aWV3VG8pIHsgLy8gRnVsbCBvdmVybGFwXHJcbiAgICAgIHJlc2V0VmlldyhjbSk7XHJcbiAgICB9IGVsc2UgaWYgKGZyb20gPD0gZGlzcGxheS52aWV3RnJvbSkgeyAvLyBUb3Agb3ZlcmxhcFxyXG4gICAgICB2YXIgY3V0ID0gdmlld0N1dHRpbmdQb2ludChjbSwgdG8sIHRvICsgbGVuZGlmZiwgMSk7XHJcbiAgICAgIGlmIChjdXQpIHtcclxuICAgICAgICBkaXNwbGF5LnZpZXcgPSBkaXNwbGF5LnZpZXcuc2xpY2UoY3V0LmluZGV4KTtcclxuICAgICAgICBkaXNwbGF5LnZpZXdGcm9tID0gY3V0LmxpbmVOO1xyXG4gICAgICAgIGRpc3BsYXkudmlld1RvICs9IGxlbmRpZmY7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgcmVzZXRWaWV3KGNtKTtcclxuICAgICAgfVxyXG4gICAgfSBlbHNlIGlmICh0byA+PSBkaXNwbGF5LnZpZXdUbykgeyAvLyBCb3R0b20gb3ZlcmxhcFxyXG4gICAgICB2YXIgY3V0ID0gdmlld0N1dHRpbmdQb2ludChjbSwgZnJvbSwgZnJvbSwgLTEpO1xyXG4gICAgICBpZiAoY3V0KSB7XHJcbiAgICAgICAgZGlzcGxheS52aWV3ID0gZGlzcGxheS52aWV3LnNsaWNlKDAsIGN1dC5pbmRleCk7XHJcbiAgICAgICAgZGlzcGxheS52aWV3VG8gPSBjdXQubGluZU47XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgcmVzZXRWaWV3KGNtKTtcclxuICAgICAgfVxyXG4gICAgfSBlbHNlIHsgLy8gR2FwIGluIHRoZSBtaWRkbGVcclxuICAgICAgdmFyIGN1dFRvcCA9IHZpZXdDdXR0aW5nUG9pbnQoY20sIGZyb20sIGZyb20sIC0xKTtcclxuICAgICAgdmFyIGN1dEJvdCA9IHZpZXdDdXR0aW5nUG9pbnQoY20sIHRvLCB0byArIGxlbmRpZmYsIDEpO1xyXG4gICAgICBpZiAoY3V0VG9wICYmIGN1dEJvdCkge1xyXG4gICAgICAgIGRpc3BsYXkudmlldyA9IGRpc3BsYXkudmlldy5zbGljZSgwLCBjdXRUb3AuaW5kZXgpXHJcbiAgICAgICAgICAuY29uY2F0KGJ1aWxkVmlld0FycmF5KGNtLCBjdXRUb3AubGluZU4sIGN1dEJvdC5saW5lTikpXHJcbiAgICAgICAgICAuY29uY2F0KGRpc3BsYXkudmlldy5zbGljZShjdXRCb3QuaW5kZXgpKTtcclxuICAgICAgICBkaXNwbGF5LnZpZXdUbyArPSBsZW5kaWZmO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHJlc2V0VmlldyhjbSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICB2YXIgZXh0ID0gZGlzcGxheS5leHRlcm5hbE1lYXN1cmVkO1xyXG4gICAgaWYgKGV4dCkge1xyXG4gICAgICBpZiAodG8gPCBleHQubGluZU4pXHJcbiAgICAgICAgZXh0LmxpbmVOICs9IGxlbmRpZmY7XHJcbiAgICAgIGVsc2UgaWYgKGZyb20gPCBleHQubGluZU4gKyBleHQuc2l6ZSlcclxuICAgICAgICBkaXNwbGF5LmV4dGVybmFsTWVhc3VyZWQgPSBudWxsO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmVnaXN0ZXIgYSBjaGFuZ2UgdG8gYSBzaW5nbGUgbGluZS4gVHlwZSBtdXN0IGJlIG9uZSBvZiBcInRleHRcIixcclxuICAvLyBcImd1dHRlclwiLCBcImNsYXNzXCIsIFwid2lkZ2V0XCJcclxuICBmdW5jdGlvbiByZWdMaW5lQ2hhbmdlKGNtLCBsaW5lLCB0eXBlKSB7XHJcbiAgICBjbS5jdXJPcC52aWV3Q2hhbmdlZCA9IHRydWU7XHJcbiAgICB2YXIgZGlzcGxheSA9IGNtLmRpc3BsYXksIGV4dCA9IGNtLmRpc3BsYXkuZXh0ZXJuYWxNZWFzdXJlZDtcclxuICAgIGlmIChleHQgJiYgbGluZSA+PSBleHQubGluZU4gJiYgbGluZSA8IGV4dC5saW5lTiArIGV4dC5zaXplKVxyXG4gICAgICBkaXNwbGF5LmV4dGVybmFsTWVhc3VyZWQgPSBudWxsO1xyXG5cclxuICAgIGlmIChsaW5lIDwgZGlzcGxheS52aWV3RnJvbSB8fCBsaW5lID49IGRpc3BsYXkudmlld1RvKSByZXR1cm47XHJcbiAgICB2YXIgbGluZVZpZXcgPSBkaXNwbGF5LnZpZXdbZmluZFZpZXdJbmRleChjbSwgbGluZSldO1xyXG4gICAgaWYgKGxpbmVWaWV3Lm5vZGUgPT0gbnVsbCkgcmV0dXJuO1xyXG4gICAgdmFyIGFyciA9IGxpbmVWaWV3LmNoYW5nZXMgfHwgKGxpbmVWaWV3LmNoYW5nZXMgPSBbXSk7XHJcbiAgICBpZiAoaW5kZXhPZihhcnIsIHR5cGUpID09IC0xKSBhcnIucHVzaCh0eXBlKTtcclxuICB9XHJcblxyXG4gIC8vIENsZWFyIHRoZSB2aWV3LlxyXG4gIGZ1bmN0aW9uIHJlc2V0VmlldyhjbSkge1xyXG4gICAgY20uZGlzcGxheS52aWV3RnJvbSA9IGNtLmRpc3BsYXkudmlld1RvID0gY20uZG9jLmZpcnN0O1xyXG4gICAgY20uZGlzcGxheS52aWV3ID0gW107XHJcbiAgICBjbS5kaXNwbGF5LnZpZXdPZmZzZXQgPSAwO1xyXG4gIH1cclxuXHJcbiAgLy8gRmluZCB0aGUgdmlldyBlbGVtZW50IGNvcnJlc3BvbmRpbmcgdG8gYSBnaXZlbiBsaW5lLiBSZXR1cm4gbnVsbFxyXG4gIC8vIHdoZW4gdGhlIGxpbmUgaXNuJ3QgdmlzaWJsZS5cclxuICBmdW5jdGlvbiBmaW5kVmlld0luZGV4KGNtLCBuKSB7XHJcbiAgICBpZiAobiA+PSBjbS5kaXNwbGF5LnZpZXdUbykgcmV0dXJuIG51bGw7XHJcbiAgICBuIC09IGNtLmRpc3BsYXkudmlld0Zyb207XHJcbiAgICBpZiAobiA8IDApIHJldHVybiBudWxsO1xyXG4gICAgdmFyIHZpZXcgPSBjbS5kaXNwbGF5LnZpZXc7XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IHZpZXcubGVuZ3RoOyBpKyspIHtcclxuICAgICAgbiAtPSB2aWV3W2ldLnNpemU7XHJcbiAgICAgIGlmIChuIDwgMCkgcmV0dXJuIGk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiB2aWV3Q3V0dGluZ1BvaW50KGNtLCBvbGROLCBuZXdOLCBkaXIpIHtcclxuICAgIHZhciBpbmRleCA9IGZpbmRWaWV3SW5kZXgoY20sIG9sZE4pLCBkaWZmLCB2aWV3ID0gY20uZGlzcGxheS52aWV3O1xyXG4gICAgaWYgKCFzYXdDb2xsYXBzZWRTcGFucyB8fCBuZXdOID09IGNtLmRvYy5maXJzdCArIGNtLmRvYy5zaXplKVxyXG4gICAgICByZXR1cm4ge2luZGV4OiBpbmRleCwgbGluZU46IG5ld059O1xyXG4gICAgZm9yICh2YXIgaSA9IDAsIG4gPSBjbS5kaXNwbGF5LnZpZXdGcm9tOyBpIDwgaW5kZXg7IGkrKylcclxuICAgICAgbiArPSB2aWV3W2ldLnNpemU7XHJcbiAgICBpZiAobiAhPSBvbGROKSB7XHJcbiAgICAgIGlmIChkaXIgPiAwKSB7XHJcbiAgICAgICAgaWYgKGluZGV4ID09IHZpZXcubGVuZ3RoIC0gMSkgcmV0dXJuIG51bGw7XHJcbiAgICAgICAgZGlmZiA9IChuICsgdmlld1tpbmRleF0uc2l6ZSkgLSBvbGROO1xyXG4gICAgICAgIGluZGV4Kys7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgZGlmZiA9IG4gLSBvbGROO1xyXG4gICAgICB9XHJcbiAgICAgIG9sZE4gKz0gZGlmZjsgbmV3TiArPSBkaWZmO1xyXG4gICAgfVxyXG4gICAgd2hpbGUgKHZpc3VhbExpbmVObyhjbS5kb2MsIG5ld04pICE9IG5ld04pIHtcclxuICAgICAgaWYgKGluZGV4ID09IChkaXIgPCAwID8gMCA6IHZpZXcubGVuZ3RoIC0gMSkpIHJldHVybiBudWxsO1xyXG4gICAgICBuZXdOICs9IGRpciAqIHZpZXdbaW5kZXggLSAoZGlyIDwgMCA/IDEgOiAwKV0uc2l6ZTtcclxuICAgICAgaW5kZXggKz0gZGlyO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIHtpbmRleDogaW5kZXgsIGxpbmVOOiBuZXdOfTtcclxuICB9XHJcblxyXG4gIC8vIEZvcmNlIHRoZSB2aWV3IHRvIGNvdmVyIGEgZ2l2ZW4gcmFuZ2UsIGFkZGluZyBlbXB0eSB2aWV3IGVsZW1lbnRcclxuICAvLyBvciBjbGlwcGluZyBvZmYgZXhpc3Rpbmcgb25lcyBhcyBuZWVkZWQuXHJcbiAgZnVuY3Rpb24gYWRqdXN0VmlldyhjbSwgZnJvbSwgdG8pIHtcclxuICAgIHZhciBkaXNwbGF5ID0gY20uZGlzcGxheSwgdmlldyA9IGRpc3BsYXkudmlldztcclxuICAgIGlmICh2aWV3Lmxlbmd0aCA9PSAwIHx8IGZyb20gPj0gZGlzcGxheS52aWV3VG8gfHwgdG8gPD0gZGlzcGxheS52aWV3RnJvbSkge1xyXG4gICAgICBkaXNwbGF5LnZpZXcgPSBidWlsZFZpZXdBcnJheShjbSwgZnJvbSwgdG8pO1xyXG4gICAgICBkaXNwbGF5LnZpZXdGcm9tID0gZnJvbTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGlmIChkaXNwbGF5LnZpZXdGcm9tID4gZnJvbSlcclxuICAgICAgICBkaXNwbGF5LnZpZXcgPSBidWlsZFZpZXdBcnJheShjbSwgZnJvbSwgZGlzcGxheS52aWV3RnJvbSkuY29uY2F0KGRpc3BsYXkudmlldyk7XHJcbiAgICAgIGVsc2UgaWYgKGRpc3BsYXkudmlld0Zyb20gPCBmcm9tKVxyXG4gICAgICAgIGRpc3BsYXkudmlldyA9IGRpc3BsYXkudmlldy5zbGljZShmaW5kVmlld0luZGV4KGNtLCBmcm9tKSk7XHJcbiAgICAgIGRpc3BsYXkudmlld0Zyb20gPSBmcm9tO1xyXG4gICAgICBpZiAoZGlzcGxheS52aWV3VG8gPCB0bylcclxuICAgICAgICBkaXNwbGF5LnZpZXcgPSBkaXNwbGF5LnZpZXcuY29uY2F0KGJ1aWxkVmlld0FycmF5KGNtLCBkaXNwbGF5LnZpZXdUbywgdG8pKTtcclxuICAgICAgZWxzZSBpZiAoZGlzcGxheS52aWV3VG8gPiB0bylcclxuICAgICAgICBkaXNwbGF5LnZpZXcgPSBkaXNwbGF5LnZpZXcuc2xpY2UoMCwgZmluZFZpZXdJbmRleChjbSwgdG8pKTtcclxuICAgIH1cclxuICAgIGRpc3BsYXkudmlld1RvID0gdG87XHJcbiAgfVxyXG5cclxuICAvLyBDb3VudCB0aGUgbnVtYmVyIG9mIGxpbmVzIGluIHRoZSB2aWV3IHdob3NlIERPTSByZXByZXNlbnRhdGlvbiBpc1xyXG4gIC8vIG91dCBvZiBkYXRlIChvciBub25leGlzdGVudCkuXHJcbiAgZnVuY3Rpb24gY291bnREaXJ0eVZpZXcoY20pIHtcclxuICAgIHZhciB2aWV3ID0gY20uZGlzcGxheS52aWV3LCBkaXJ0eSA9IDA7XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IHZpZXcubGVuZ3RoOyBpKyspIHtcclxuICAgICAgdmFyIGxpbmVWaWV3ID0gdmlld1tpXTtcclxuICAgICAgaWYgKCFsaW5lVmlldy5oaWRkZW4gJiYgKCFsaW5lVmlldy5ub2RlIHx8IGxpbmVWaWV3LmNoYW5nZXMpKSArK2RpcnR5O1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIGRpcnR5O1xyXG4gIH1cclxuXHJcbiAgLy8gRVZFTlQgSEFORExFUlNcclxuXHJcbiAgLy8gQXR0YWNoIHRoZSBuZWNlc3NhcnkgZXZlbnQgaGFuZGxlcnMgd2hlbiBpbml0aWFsaXppbmcgdGhlIGVkaXRvclxyXG4gIGZ1bmN0aW9uIHJlZ2lzdGVyRXZlbnRIYW5kbGVycyhjbSkge1xyXG4gICAgdmFyIGQgPSBjbS5kaXNwbGF5O1xyXG4gICAgb24oZC5zY3JvbGxlciwgXCJtb3VzZWRvd25cIiwgb3BlcmF0aW9uKGNtLCBvbk1vdXNlRG93bikpO1xyXG4gICAgLy8gT2xkZXIgSUUncyB3aWxsIG5vdCBmaXJlIGEgc2Vjb25kIG1vdXNlZG93biBmb3IgYSBkb3VibGUgY2xpY2tcclxuICAgIGlmIChpZSAmJiBpZV92ZXJzaW9uIDwgMTEpXHJcbiAgICAgIG9uKGQuc2Nyb2xsZXIsIFwiZGJsY2xpY2tcIiwgb3BlcmF0aW9uKGNtLCBmdW5jdGlvbihlKSB7XHJcbiAgICAgICAgaWYgKHNpZ25hbERPTUV2ZW50KGNtLCBlKSkgcmV0dXJuO1xyXG4gICAgICAgIHZhciBwb3MgPSBwb3NGcm9tTW91c2UoY20sIGUpO1xyXG4gICAgICAgIGlmICghcG9zIHx8IGNsaWNrSW5HdXR0ZXIoY20sIGUpIHx8IGV2ZW50SW5XaWRnZXQoY20uZGlzcGxheSwgZSkpIHJldHVybjtcclxuICAgICAgICBlX3ByZXZlbnREZWZhdWx0KGUpO1xyXG4gICAgICAgIHZhciB3b3JkID0gY20uZmluZFdvcmRBdChwb3MpO1xyXG4gICAgICAgIGV4dGVuZFNlbGVjdGlvbihjbS5kb2MsIHdvcmQuYW5jaG9yLCB3b3JkLmhlYWQpO1xyXG4gICAgICB9KSk7XHJcbiAgICBlbHNlXHJcbiAgICAgIG9uKGQuc2Nyb2xsZXIsIFwiZGJsY2xpY2tcIiwgZnVuY3Rpb24oZSkgeyBzaWduYWxET01FdmVudChjbSwgZSkgfHwgZV9wcmV2ZW50RGVmYXVsdChlKTsgfSk7XHJcbiAgICAvLyBTb21lIGJyb3dzZXJzIGZpcmUgY29udGV4dG1lbnUgKmFmdGVyKiBvcGVuaW5nIHRoZSBtZW51LCBhdFxyXG4gICAgLy8gd2hpY2ggcG9pbnQgd2UgY2FuJ3QgbWVzcyB3aXRoIGl0IGFueW1vcmUuIENvbnRleHQgbWVudSBpc1xyXG4gICAgLy8gaGFuZGxlZCBpbiBvbk1vdXNlRG93biBmb3IgdGhlc2UgYnJvd3NlcnMuXHJcbiAgICBpZiAoIWNhcHR1cmVSaWdodENsaWNrKSBvbihkLnNjcm9sbGVyLCBcImNvbnRleHRtZW51XCIsIGZ1bmN0aW9uKGUpIHtvbkNvbnRleHRNZW51KGNtLCBlKTt9KTtcclxuXHJcbiAgICAvLyBVc2VkIHRvIHN1cHByZXNzIG1vdXNlIGV2ZW50IGhhbmRsaW5nIHdoZW4gYSB0b3VjaCBoYXBwZW5zXHJcbiAgICB2YXIgdG91Y2hGaW5pc2hlZCwgcHJldlRvdWNoID0ge2VuZDogMH07XHJcbiAgICBmdW5jdGlvbiBmaW5pc2hUb3VjaCgpIHtcclxuICAgICAgaWYgKGQuYWN0aXZlVG91Y2gpIHtcclxuICAgICAgICB0b3VjaEZpbmlzaGVkID0gc2V0VGltZW91dChmdW5jdGlvbigpIHtkLmFjdGl2ZVRvdWNoID0gbnVsbDt9LCAxMDAwKTtcclxuICAgICAgICBwcmV2VG91Y2ggPSBkLmFjdGl2ZVRvdWNoO1xyXG4gICAgICAgIHByZXZUb3VjaC5lbmQgPSArbmV3IERhdGU7XHJcbiAgICAgIH1cclxuICAgIH07XHJcbiAgICBmdW5jdGlvbiBpc01vdXNlTGlrZVRvdWNoRXZlbnQoZSkge1xyXG4gICAgICBpZiAoZS50b3VjaGVzLmxlbmd0aCAhPSAxKSByZXR1cm4gZmFsc2U7XHJcbiAgICAgIHZhciB0b3VjaCA9IGUudG91Y2hlc1swXTtcclxuICAgICAgcmV0dXJuIHRvdWNoLnJhZGl1c1ggPD0gMSAmJiB0b3VjaC5yYWRpdXNZIDw9IDE7XHJcbiAgICB9XHJcbiAgICBmdW5jdGlvbiBmYXJBd2F5KHRvdWNoLCBvdGhlcikge1xyXG4gICAgICBpZiAob3RoZXIubGVmdCA9PSBudWxsKSByZXR1cm4gdHJ1ZTtcclxuICAgICAgdmFyIGR4ID0gb3RoZXIubGVmdCAtIHRvdWNoLmxlZnQsIGR5ID0gb3RoZXIudG9wIC0gdG91Y2gudG9wO1xyXG4gICAgICByZXR1cm4gZHggKiBkeCArIGR5ICogZHkgPiAyMCAqIDIwO1xyXG4gICAgfVxyXG4gICAgb24oZC5zY3JvbGxlciwgXCJ0b3VjaHN0YXJ0XCIsIGZ1bmN0aW9uKGUpIHtcclxuICAgICAgaWYgKCFpc01vdXNlTGlrZVRvdWNoRXZlbnQoZSkpIHtcclxuICAgICAgICBjbGVhclRpbWVvdXQodG91Y2hGaW5pc2hlZCk7XHJcbiAgICAgICAgdmFyIG5vdyA9ICtuZXcgRGF0ZTtcclxuICAgICAgICBkLmFjdGl2ZVRvdWNoID0ge3N0YXJ0OiBub3csIG1vdmVkOiBmYWxzZSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgIHByZXY6IG5vdyAtIHByZXZUb3VjaC5lbmQgPD0gMzAwID8gcHJldlRvdWNoIDogbnVsbH07XHJcbiAgICAgICAgaWYgKGUudG91Y2hlcy5sZW5ndGggPT0gMSkge1xyXG4gICAgICAgICAgZC5hY3RpdmVUb3VjaC5sZWZ0ID0gZS50b3VjaGVzWzBdLnBhZ2VYO1xyXG4gICAgICAgICAgZC5hY3RpdmVUb3VjaC50b3AgPSBlLnRvdWNoZXNbMF0ucGFnZVk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICAgIG9uKGQuc2Nyb2xsZXIsIFwidG91Y2htb3ZlXCIsIGZ1bmN0aW9uKCkge1xyXG4gICAgICBpZiAoZC5hY3RpdmVUb3VjaCkgZC5hY3RpdmVUb3VjaC5tb3ZlZCA9IHRydWU7XHJcbiAgICB9KTtcclxuICAgIG9uKGQuc2Nyb2xsZXIsIFwidG91Y2hlbmRcIiwgZnVuY3Rpb24oZSkge1xyXG4gICAgICB2YXIgdG91Y2ggPSBkLmFjdGl2ZVRvdWNoO1xyXG4gICAgICBpZiAodG91Y2ggJiYgIWV2ZW50SW5XaWRnZXQoZCwgZSkgJiYgdG91Y2gubGVmdCAhPSBudWxsICYmXHJcbiAgICAgICAgICAhdG91Y2gubW92ZWQgJiYgbmV3IERhdGUgLSB0b3VjaC5zdGFydCA8IDMwMCkge1xyXG4gICAgICAgIHZhciBwb3MgPSBjbS5jb29yZHNDaGFyKGQuYWN0aXZlVG91Y2gsIFwicGFnZVwiKSwgcmFuZ2U7XHJcbiAgICAgICAgaWYgKCF0b3VjaC5wcmV2IHx8IGZhckF3YXkodG91Y2gsIHRvdWNoLnByZXYpKSAvLyBTaW5nbGUgdGFwXHJcbiAgICAgICAgICByYW5nZSA9IG5ldyBSYW5nZShwb3MsIHBvcyk7XHJcbiAgICAgICAgZWxzZSBpZiAoIXRvdWNoLnByZXYucHJldiB8fCBmYXJBd2F5KHRvdWNoLCB0b3VjaC5wcmV2LnByZXYpKSAvLyBEb3VibGUgdGFwXHJcbiAgICAgICAgICByYW5nZSA9IGNtLmZpbmRXb3JkQXQocG9zKTtcclxuICAgICAgICBlbHNlIC8vIFRyaXBsZSB0YXBcclxuICAgICAgICAgIHJhbmdlID0gbmV3IFJhbmdlKFBvcyhwb3MubGluZSwgMCksIGNsaXBQb3MoY20uZG9jLCBQb3MocG9zLmxpbmUgKyAxLCAwKSkpO1xyXG4gICAgICAgIGNtLnNldFNlbGVjdGlvbihyYW5nZS5hbmNob3IsIHJhbmdlLmhlYWQpO1xyXG4gICAgICAgIGNtLmZvY3VzKCk7XHJcbiAgICAgICAgZV9wcmV2ZW50RGVmYXVsdChlKTtcclxuICAgICAgfVxyXG4gICAgICBmaW5pc2hUb3VjaCgpO1xyXG4gICAgfSk7XHJcbiAgICBvbihkLnNjcm9sbGVyLCBcInRvdWNoY2FuY2VsXCIsIGZpbmlzaFRvdWNoKTtcclxuXHJcbiAgICAvLyBTeW5jIHNjcm9sbGluZyBiZXR3ZWVuIGZha2Ugc2Nyb2xsYmFycyBhbmQgcmVhbCBzY3JvbGxhYmxlXHJcbiAgICAvLyBhcmVhLCBlbnN1cmUgdmlld3BvcnQgaXMgdXBkYXRlZCB3aGVuIHNjcm9sbGluZy5cclxuICAgIG9uKGQuc2Nyb2xsZXIsIFwic2Nyb2xsXCIsIGZ1bmN0aW9uKCkge1xyXG4gICAgICBpZiAoZC5zY3JvbGxlci5jbGllbnRIZWlnaHQpIHtcclxuICAgICAgICBzZXRTY3JvbGxUb3AoY20sIGQuc2Nyb2xsZXIuc2Nyb2xsVG9wKTtcclxuICAgICAgICBzZXRTY3JvbGxMZWZ0KGNtLCBkLnNjcm9sbGVyLnNjcm9sbExlZnQsIHRydWUpO1xyXG4gICAgICAgIHNpZ25hbChjbSwgXCJzY3JvbGxcIiwgY20pO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBMaXN0ZW4gdG8gd2hlZWwgZXZlbnRzIGluIG9yZGVyIHRvIHRyeSBhbmQgdXBkYXRlIHRoZSB2aWV3cG9ydCBvbiB0aW1lLlxyXG4gICAgb24oZC5zY3JvbGxlciwgXCJtb3VzZXdoZWVsXCIsIGZ1bmN0aW9uKGUpe29uU2Nyb2xsV2hlZWwoY20sIGUpO30pO1xyXG4gICAgb24oZC5zY3JvbGxlciwgXCJET01Nb3VzZVNjcm9sbFwiLCBmdW5jdGlvbihlKXtvblNjcm9sbFdoZWVsKGNtLCBlKTt9KTtcclxuXHJcbiAgICAvLyBQcmV2ZW50IHdyYXBwZXIgZnJvbSBldmVyIHNjcm9sbGluZ1xyXG4gICAgb24oZC53cmFwcGVyLCBcInNjcm9sbFwiLCBmdW5jdGlvbigpIHsgZC53cmFwcGVyLnNjcm9sbFRvcCA9IGQud3JhcHBlci5zY3JvbGxMZWZ0ID0gMDsgfSk7XHJcblxyXG4gICAgZC5kcmFnRnVuY3Rpb25zID0ge1xyXG4gICAgICBlbnRlcjogZnVuY3Rpb24oZSkge2lmICghc2lnbmFsRE9NRXZlbnQoY20sIGUpKSBlX3N0b3AoZSk7fSxcclxuICAgICAgb3ZlcjogZnVuY3Rpb24oZSkge2lmICghc2lnbmFsRE9NRXZlbnQoY20sIGUpKSB7IG9uRHJhZ092ZXIoY20sIGUpOyBlX3N0b3AoZSk7IH19LFxyXG4gICAgICBzdGFydDogZnVuY3Rpb24oZSl7b25EcmFnU3RhcnQoY20sIGUpO30sXHJcbiAgICAgIGRyb3A6IG9wZXJhdGlvbihjbSwgb25Ecm9wKSxcclxuICAgICAgbGVhdmU6IGZ1bmN0aW9uKCkge2NsZWFyRHJhZ0N1cnNvcihjbSk7fVxyXG4gICAgfTtcclxuXHJcbiAgICB2YXIgaW5wID0gZC5pbnB1dC5nZXRGaWVsZCgpO1xyXG4gICAgb24oaW5wLCBcImtleXVwXCIsIGZ1bmN0aW9uKGUpIHsgb25LZXlVcC5jYWxsKGNtLCBlKTsgfSk7XHJcbiAgICBvbihpbnAsIFwia2V5ZG93blwiLCBvcGVyYXRpb24oY20sIG9uS2V5RG93bikpO1xyXG4gICAgb24oaW5wLCBcImtleXByZXNzXCIsIG9wZXJhdGlvbihjbSwgb25LZXlQcmVzcykpO1xyXG4gICAgb24oaW5wLCBcImZvY3VzXCIsIGJpbmQob25Gb2N1cywgY20pKTtcclxuICAgIG9uKGlucCwgXCJibHVyXCIsIGJpbmQob25CbHVyLCBjbSkpO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gZHJhZ0Ryb3BDaGFuZ2VkKGNtLCB2YWx1ZSwgb2xkKSB7XHJcbiAgICB2YXIgd2FzT24gPSBvbGQgJiYgb2xkICE9IENvZGVNaXJyb3IuSW5pdDtcclxuICAgIGlmICghdmFsdWUgIT0gIXdhc09uKSB7XHJcbiAgICAgIHZhciBmdW5jcyA9IGNtLmRpc3BsYXkuZHJhZ0Z1bmN0aW9ucztcclxuICAgICAgdmFyIHRvZ2dsZSA9IHZhbHVlID8gb24gOiBvZmY7XHJcbiAgICAgIHRvZ2dsZShjbS5kaXNwbGF5LnNjcm9sbGVyLCBcImRyYWdzdGFydFwiLCBmdW5jcy5zdGFydCk7XHJcbiAgICAgIHRvZ2dsZShjbS5kaXNwbGF5LnNjcm9sbGVyLCBcImRyYWdlbnRlclwiLCBmdW5jcy5lbnRlcik7XHJcbiAgICAgIHRvZ2dsZShjbS5kaXNwbGF5LnNjcm9sbGVyLCBcImRyYWdvdmVyXCIsIGZ1bmNzLm92ZXIpO1xyXG4gICAgICB0b2dnbGUoY20uZGlzcGxheS5zY3JvbGxlciwgXCJkcmFnbGVhdmVcIiwgZnVuY3MubGVhdmUpO1xyXG4gICAgICB0b2dnbGUoY20uZGlzcGxheS5zY3JvbGxlciwgXCJkcm9wXCIsIGZ1bmNzLmRyb3ApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gQ2FsbGVkIHdoZW4gdGhlIHdpbmRvdyByZXNpemVzXHJcbiAgZnVuY3Rpb24gb25SZXNpemUoY20pIHtcclxuICAgIHZhciBkID0gY20uZGlzcGxheTtcclxuICAgIGlmIChkLmxhc3RXcmFwSGVpZ2h0ID09IGQud3JhcHBlci5jbGllbnRIZWlnaHQgJiYgZC5sYXN0V3JhcFdpZHRoID09IGQud3JhcHBlci5jbGllbnRXaWR0aClcclxuICAgICAgcmV0dXJuO1xyXG4gICAgLy8gTWlnaHQgYmUgYSB0ZXh0IHNjYWxpbmcgb3BlcmF0aW9uLCBjbGVhciBzaXplIGNhY2hlcy5cclxuICAgIGQuY2FjaGVkQ2hhcldpZHRoID0gZC5jYWNoZWRUZXh0SGVpZ2h0ID0gZC5jYWNoZWRQYWRkaW5nSCA9IG51bGw7XHJcbiAgICBkLnNjcm9sbGJhcnNDbGlwcGVkID0gZmFsc2U7XHJcbiAgICBjbS5zZXRTaXplKCk7XHJcbiAgfVxyXG5cclxuICAvLyBNT1VTRSBFVkVOVFNcclxuXHJcbiAgLy8gUmV0dXJuIHRydWUgd2hlbiB0aGUgZ2l2ZW4gbW91c2UgZXZlbnQgaGFwcGVuZWQgaW4gYSB3aWRnZXRcclxuICBmdW5jdGlvbiBldmVudEluV2lkZ2V0KGRpc3BsYXksIGUpIHtcclxuICAgIGZvciAodmFyIG4gPSBlX3RhcmdldChlKTsgbiAhPSBkaXNwbGF5LndyYXBwZXI7IG4gPSBuLnBhcmVudE5vZGUpIHtcclxuICAgICAgaWYgKCFuIHx8IChuLm5vZGVUeXBlID09IDEgJiYgbi5nZXRBdHRyaWJ1dGUoXCJjbS1pZ25vcmUtZXZlbnRzXCIpID09IFwidHJ1ZVwiKSB8fFxyXG4gICAgICAgICAgKG4ucGFyZW50Tm9kZSA9PSBkaXNwbGF5LnNpemVyICYmIG4gIT0gZGlzcGxheS5tb3ZlcikpXHJcbiAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBHaXZlbiBhIG1vdXNlIGV2ZW50LCBmaW5kIHRoZSBjb3JyZXNwb25kaW5nIHBvc2l0aW9uLiBJZiBsaWJlcmFsXHJcbiAgLy8gaXMgZmFsc2UsIGl0IGNoZWNrcyB3aGV0aGVyIGEgZ3V0dGVyIG9yIHNjcm9sbGJhciB3YXMgY2xpY2tlZCxcclxuICAvLyBhbmQgcmV0dXJucyBudWxsIGlmIGl0IHdhcy4gZm9yUmVjdCBpcyB1c2VkIGJ5IHJlY3Rhbmd1bGFyXHJcbiAgLy8gc2VsZWN0aW9ucywgYW5kIHRyaWVzIHRvIGVzdGltYXRlIGEgY2hhcmFjdGVyIHBvc2l0aW9uIGV2ZW4gZm9yXHJcbiAgLy8gY29vcmRpbmF0ZXMgYmV5b25kIHRoZSByaWdodCBvZiB0aGUgdGV4dC5cclxuICBmdW5jdGlvbiBwb3NGcm9tTW91c2UoY20sIGUsIGxpYmVyYWwsIGZvclJlY3QpIHtcclxuICAgIHZhciBkaXNwbGF5ID0gY20uZGlzcGxheTtcclxuICAgIGlmICghbGliZXJhbCAmJiBlX3RhcmdldChlKS5nZXRBdHRyaWJ1dGUoXCJjbS1ub3QtY29udGVudFwiKSA9PSBcInRydWVcIikgcmV0dXJuIG51bGw7XHJcblxyXG4gICAgdmFyIHgsIHksIHNwYWNlID0gZGlzcGxheS5saW5lU3BhY2UuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XHJcbiAgICAvLyBGYWlscyB1bnByZWRpY3RhYmx5IG9uIElFWzY3XSB3aGVuIG1vdXNlIGlzIGRyYWdnZWQgYXJvdW5kIHF1aWNrbHkuXHJcbiAgICB0cnkgeyB4ID0gZS5jbGllbnRYIC0gc3BhY2UubGVmdDsgeSA9IGUuY2xpZW50WSAtIHNwYWNlLnRvcDsgfVxyXG4gICAgY2F0Y2ggKGUpIHsgcmV0dXJuIG51bGw7IH1cclxuICAgIHZhciBjb29yZHMgPSBjb29yZHNDaGFyKGNtLCB4LCB5KSwgbGluZTtcclxuICAgIGlmIChmb3JSZWN0ICYmIGNvb3Jkcy54UmVsID09IDEgJiYgKGxpbmUgPSBnZXRMaW5lKGNtLmRvYywgY29vcmRzLmxpbmUpLnRleHQpLmxlbmd0aCA9PSBjb29yZHMuY2gpIHtcclxuICAgICAgdmFyIGNvbERpZmYgPSBjb3VudENvbHVtbihsaW5lLCBsaW5lLmxlbmd0aCwgY20ub3B0aW9ucy50YWJTaXplKSAtIGxpbmUubGVuZ3RoO1xyXG4gICAgICBjb29yZHMgPSBQb3MoY29vcmRzLmxpbmUsIE1hdGgubWF4KDAsIE1hdGgucm91bmQoKHggLSBwYWRkaW5nSChjbS5kaXNwbGF5KS5sZWZ0KSAvIGNoYXJXaWR0aChjbS5kaXNwbGF5KSkgLSBjb2xEaWZmKSk7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gY29vcmRzO1xyXG4gIH1cclxuXHJcbiAgLy8gQSBtb3VzZSBkb3duIGNhbiBiZSBhIHNpbmdsZSBjbGljaywgZG91YmxlIGNsaWNrLCB0cmlwbGUgY2xpY2ssXHJcbiAgLy8gc3RhcnQgb2Ygc2VsZWN0aW9uIGRyYWcsIHN0YXJ0IG9mIHRleHQgZHJhZywgbmV3IGN1cnNvclxyXG4gIC8vIChjdHJsLWNsaWNrKSwgcmVjdGFuZ2xlIGRyYWcgKGFsdC1kcmFnKSwgb3IgeHdpblxyXG4gIC8vIG1pZGRsZS1jbGljay1wYXN0ZS4gT3IgaXQgbWlnaHQgYmUgYSBjbGljayBvbiBzb21ldGhpbmcgd2Ugc2hvdWxkXHJcbiAgLy8gbm90IGludGVyZmVyZSB3aXRoLCBzdWNoIGFzIGEgc2Nyb2xsYmFyIG9yIHdpZGdldC5cclxuICBmdW5jdGlvbiBvbk1vdXNlRG93bihlKSB7XHJcbiAgICB2YXIgY20gPSB0aGlzLCBkaXNwbGF5ID0gY20uZGlzcGxheTtcclxuICAgIGlmIChkaXNwbGF5LmFjdGl2ZVRvdWNoICYmIGRpc3BsYXkuaW5wdXQuc3VwcG9ydHNUb3VjaCgpIHx8IHNpZ25hbERPTUV2ZW50KGNtLCBlKSkgcmV0dXJuO1xyXG4gICAgZGlzcGxheS5zaGlmdCA9IGUuc2hpZnRLZXk7XHJcblxyXG4gICAgaWYgKGV2ZW50SW5XaWRnZXQoZGlzcGxheSwgZSkpIHtcclxuICAgICAgaWYgKCF3ZWJraXQpIHtcclxuICAgICAgICAvLyBCcmllZmx5IHR1cm4gb2ZmIGRyYWdnYWJpbGl0eSwgdG8gYWxsb3cgd2lkZ2V0cyB0byBkb1xyXG4gICAgICAgIC8vIG5vcm1hbCBkcmFnZ2luZyB0aGluZ3MuXHJcbiAgICAgICAgZGlzcGxheS5zY3JvbGxlci5kcmFnZ2FibGUgPSBmYWxzZTtcclxuICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCl7ZGlzcGxheS5zY3JvbGxlci5kcmFnZ2FibGUgPSB0cnVlO30sIDEwMCk7XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG4gICAgaWYgKGNsaWNrSW5HdXR0ZXIoY20sIGUpKSByZXR1cm47XHJcbiAgICB2YXIgc3RhcnQgPSBwb3NGcm9tTW91c2UoY20sIGUpO1xyXG4gICAgd2luZG93LmZvY3VzKCk7XHJcblxyXG4gICAgc3dpdGNoIChlX2J1dHRvbihlKSkge1xyXG4gICAgY2FzZSAxOlxyXG4gICAgICAvLyAjMzI2MTogbWFrZSBzdXJlLCB0aGF0IHdlJ3JlIG5vdCBzdGFydGluZyBhIHNlY29uZCBzZWxlY3Rpb25cclxuICAgICAgaWYgKGNtLnN0YXRlLnNlbGVjdGluZ1RleHQpXHJcbiAgICAgICAgY20uc3RhdGUuc2VsZWN0aW5nVGV4dChlKTtcclxuICAgICAgZWxzZSBpZiAoc3RhcnQpXHJcbiAgICAgICAgbGVmdEJ1dHRvbkRvd24oY20sIGUsIHN0YXJ0KTtcclxuICAgICAgZWxzZSBpZiAoZV90YXJnZXQoZSkgPT0gZGlzcGxheS5zY3JvbGxlcilcclxuICAgICAgICBlX3ByZXZlbnREZWZhdWx0KGUpO1xyXG4gICAgICBicmVhaztcclxuICAgIGNhc2UgMjpcclxuICAgICAgaWYgKHdlYmtpdCkgY20uc3RhdGUubGFzdE1pZGRsZURvd24gPSArbmV3IERhdGU7XHJcbiAgICAgIGlmIChzdGFydCkgZXh0ZW5kU2VsZWN0aW9uKGNtLmRvYywgc3RhcnQpO1xyXG4gICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge2Rpc3BsYXkuaW5wdXQuZm9jdXMoKTt9LCAyMCk7XHJcbiAgICAgIGVfcHJldmVudERlZmF1bHQoZSk7XHJcbiAgICAgIGJyZWFrO1xyXG4gICAgY2FzZSAzOlxyXG4gICAgICBpZiAoY2FwdHVyZVJpZ2h0Q2xpY2spIG9uQ29udGV4dE1lbnUoY20sIGUpO1xyXG4gICAgICBlbHNlIGRlbGF5Qmx1ckV2ZW50KGNtKTtcclxuICAgICAgYnJlYWs7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICB2YXIgbGFzdENsaWNrLCBsYXN0RG91YmxlQ2xpY2s7XHJcbiAgZnVuY3Rpb24gbGVmdEJ1dHRvbkRvd24oY20sIGUsIHN0YXJ0KSB7XHJcbiAgICBpZiAoaWUpIHNldFRpbWVvdXQoYmluZChlbnN1cmVGb2N1cywgY20pLCAwKTtcclxuICAgIGVsc2UgY20uY3VyT3AuZm9jdXMgPSBhY3RpdmVFbHQoKTtcclxuXHJcbiAgICB2YXIgbm93ID0gK25ldyBEYXRlLCB0eXBlO1xyXG4gICAgaWYgKGxhc3REb3VibGVDbGljayAmJiBsYXN0RG91YmxlQ2xpY2sudGltZSA+IG5vdyAtIDQwMCAmJiBjbXAobGFzdERvdWJsZUNsaWNrLnBvcywgc3RhcnQpID09IDApIHtcclxuICAgICAgdHlwZSA9IFwidHJpcGxlXCI7XHJcbiAgICB9IGVsc2UgaWYgKGxhc3RDbGljayAmJiBsYXN0Q2xpY2sudGltZSA+IG5vdyAtIDQwMCAmJiBjbXAobGFzdENsaWNrLnBvcywgc3RhcnQpID09IDApIHtcclxuICAgICAgdHlwZSA9IFwiZG91YmxlXCI7XHJcbiAgICAgIGxhc3REb3VibGVDbGljayA9IHt0aW1lOiBub3csIHBvczogc3RhcnR9O1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgdHlwZSA9IFwic2luZ2xlXCI7XHJcbiAgICAgIGxhc3RDbGljayA9IHt0aW1lOiBub3csIHBvczogc3RhcnR9O1xyXG4gICAgfVxyXG5cclxuICAgIHZhciBzZWwgPSBjbS5kb2Muc2VsLCBtb2RpZmllciA9IG1hYyA/IGUubWV0YUtleSA6IGUuY3RybEtleSwgY29udGFpbmVkO1xyXG4gICAgaWYgKGNtLm9wdGlvbnMuZHJhZ0Ryb3AgJiYgZHJhZ0FuZERyb3AgJiYgIWlzUmVhZE9ubHkoY20pICYmXHJcbiAgICAgICAgdHlwZSA9PSBcInNpbmdsZVwiICYmIChjb250YWluZWQgPSBzZWwuY29udGFpbnMoc3RhcnQpKSA+IC0xICYmXHJcbiAgICAgICAgKGNtcCgoY29udGFpbmVkID0gc2VsLnJhbmdlc1tjb250YWluZWRdKS5mcm9tKCksIHN0YXJ0KSA8IDAgfHwgc3RhcnQueFJlbCA+IDApICYmXHJcbiAgICAgICAgKGNtcChjb250YWluZWQudG8oKSwgc3RhcnQpID4gMCB8fCBzdGFydC54UmVsIDwgMCkpXHJcbiAgICAgIGxlZnRCdXR0b25TdGFydERyYWcoY20sIGUsIHN0YXJ0LCBtb2RpZmllcik7XHJcbiAgICBlbHNlXHJcbiAgICAgIGxlZnRCdXR0b25TZWxlY3QoY20sIGUsIHN0YXJ0LCB0eXBlLCBtb2RpZmllcik7XHJcbiAgfVxyXG5cclxuICAvLyBTdGFydCBhIHRleHQgZHJhZy4gV2hlbiBpdCBlbmRzLCBzZWUgaWYgYW55IGRyYWdnaW5nIGFjdHVhbGx5XHJcbiAgLy8gaGFwcGVuLCBhbmQgdHJlYXQgYXMgYSBjbGljayBpZiBpdCBkaWRuJ3QuXHJcbiAgZnVuY3Rpb24gbGVmdEJ1dHRvblN0YXJ0RHJhZyhjbSwgZSwgc3RhcnQsIG1vZGlmaWVyKSB7XHJcbiAgICB2YXIgZGlzcGxheSA9IGNtLmRpc3BsYXksIHN0YXJ0VGltZSA9ICtuZXcgRGF0ZTtcclxuICAgIHZhciBkcmFnRW5kID0gb3BlcmF0aW9uKGNtLCBmdW5jdGlvbihlMikge1xyXG4gICAgICBpZiAod2Via2l0KSBkaXNwbGF5LnNjcm9sbGVyLmRyYWdnYWJsZSA9IGZhbHNlO1xyXG4gICAgICBjbS5zdGF0ZS5kcmFnZ2luZ1RleHQgPSBmYWxzZTtcclxuICAgICAgb2ZmKGRvY3VtZW50LCBcIm1vdXNldXBcIiwgZHJhZ0VuZCk7XHJcbiAgICAgIG9mZihkaXNwbGF5LnNjcm9sbGVyLCBcImRyb3BcIiwgZHJhZ0VuZCk7XHJcbiAgICAgIGlmIChNYXRoLmFicyhlLmNsaWVudFggLSBlMi5jbGllbnRYKSArIE1hdGguYWJzKGUuY2xpZW50WSAtIGUyLmNsaWVudFkpIDwgMTApIHtcclxuICAgICAgICBlX3ByZXZlbnREZWZhdWx0KGUyKTtcclxuICAgICAgICBpZiAoIW1vZGlmaWVyICYmICtuZXcgRGF0ZSAtIDIwMCA8IHN0YXJ0VGltZSlcclxuICAgICAgICAgIGV4dGVuZFNlbGVjdGlvbihjbS5kb2MsIHN0YXJ0KTtcclxuICAgICAgICAvLyBXb3JrIGFyb3VuZCB1bmV4cGxhaW5hYmxlIGZvY3VzIHByb2JsZW0gaW4gSUU5ICgjMjEyNykgYW5kIENocm9tZSAoIzMwODEpXHJcbiAgICAgICAgaWYgKHdlYmtpdCB8fCBpZSAmJiBpZV92ZXJzaW9uID09IDkpXHJcbiAgICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge2RvY3VtZW50LmJvZHkuZm9jdXMoKTsgZGlzcGxheS5pbnB1dC5mb2N1cygpO30sIDIwKTtcclxuICAgICAgICBlbHNlXHJcbiAgICAgICAgICBkaXNwbGF5LmlucHV0LmZvY3VzKCk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gICAgLy8gTGV0IHRoZSBkcmFnIGhhbmRsZXIgaGFuZGxlIHRoaXMuXHJcbiAgICBpZiAod2Via2l0KSBkaXNwbGF5LnNjcm9sbGVyLmRyYWdnYWJsZSA9IHRydWU7XHJcbiAgICBjbS5zdGF0ZS5kcmFnZ2luZ1RleHQgPSBkcmFnRW5kO1xyXG4gICAgLy8gSUUncyBhcHByb2FjaCB0byBkcmFnZ2FibGVcclxuICAgIGlmIChkaXNwbGF5LnNjcm9sbGVyLmRyYWdEcm9wKSBkaXNwbGF5LnNjcm9sbGVyLmRyYWdEcm9wKCk7XHJcbiAgICBvbihkb2N1bWVudCwgXCJtb3VzZXVwXCIsIGRyYWdFbmQpO1xyXG4gICAgb24oZGlzcGxheS5zY3JvbGxlciwgXCJkcm9wXCIsIGRyYWdFbmQpO1xyXG4gIH1cclxuXHJcbiAgLy8gTm9ybWFsIHNlbGVjdGlvbiwgYXMgb3Bwb3NlZCB0byB0ZXh0IGRyYWdnaW5nLlxyXG4gIGZ1bmN0aW9uIGxlZnRCdXR0b25TZWxlY3QoY20sIGUsIHN0YXJ0LCB0eXBlLCBhZGROZXcpIHtcclxuICAgIHZhciBkaXNwbGF5ID0gY20uZGlzcGxheSwgZG9jID0gY20uZG9jO1xyXG4gICAgZV9wcmV2ZW50RGVmYXVsdChlKTtcclxuXHJcbiAgICB2YXIgb3VyUmFuZ2UsIG91ckluZGV4LCBzdGFydFNlbCA9IGRvYy5zZWwsIHJhbmdlcyA9IHN0YXJ0U2VsLnJhbmdlcztcclxuICAgIGlmIChhZGROZXcgJiYgIWUuc2hpZnRLZXkpIHtcclxuICAgICAgb3VySW5kZXggPSBkb2Muc2VsLmNvbnRhaW5zKHN0YXJ0KTtcclxuICAgICAgaWYgKG91ckluZGV4ID4gLTEpXHJcbiAgICAgICAgb3VyUmFuZ2UgPSByYW5nZXNbb3VySW5kZXhdO1xyXG4gICAgICBlbHNlXHJcbiAgICAgICAgb3VyUmFuZ2UgPSBuZXcgUmFuZ2Uoc3RhcnQsIHN0YXJ0KTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIG91clJhbmdlID0gZG9jLnNlbC5wcmltYXJ5KCk7XHJcbiAgICAgIG91ckluZGV4ID0gZG9jLnNlbC5wcmltSW5kZXg7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKGUuYWx0S2V5KSB7XHJcbiAgICAgIHR5cGUgPSBcInJlY3RcIjtcclxuICAgICAgaWYgKCFhZGROZXcpIG91clJhbmdlID0gbmV3IFJhbmdlKHN0YXJ0LCBzdGFydCk7XHJcbiAgICAgIHN0YXJ0ID0gcG9zRnJvbU1vdXNlKGNtLCBlLCB0cnVlLCB0cnVlKTtcclxuICAgICAgb3VySW5kZXggPSAtMTtcclxuICAgIH0gZWxzZSBpZiAodHlwZSA9PSBcImRvdWJsZVwiKSB7XHJcbiAgICAgIHZhciB3b3JkID0gY20uZmluZFdvcmRBdChzdGFydCk7XHJcbiAgICAgIGlmIChjbS5kaXNwbGF5LnNoaWZ0IHx8IGRvYy5leHRlbmQpXHJcbiAgICAgICAgb3VyUmFuZ2UgPSBleHRlbmRSYW5nZShkb2MsIG91clJhbmdlLCB3b3JkLmFuY2hvciwgd29yZC5oZWFkKTtcclxuICAgICAgZWxzZVxyXG4gICAgICAgIG91clJhbmdlID0gd29yZDtcclxuICAgIH0gZWxzZSBpZiAodHlwZSA9PSBcInRyaXBsZVwiKSB7XHJcbiAgICAgIHZhciBsaW5lID0gbmV3IFJhbmdlKFBvcyhzdGFydC5saW5lLCAwKSwgY2xpcFBvcyhkb2MsIFBvcyhzdGFydC5saW5lICsgMSwgMCkpKTtcclxuICAgICAgaWYgKGNtLmRpc3BsYXkuc2hpZnQgfHwgZG9jLmV4dGVuZClcclxuICAgICAgICBvdXJSYW5nZSA9IGV4dGVuZFJhbmdlKGRvYywgb3VyUmFuZ2UsIGxpbmUuYW5jaG9yLCBsaW5lLmhlYWQpO1xyXG4gICAgICBlbHNlXHJcbiAgICAgICAgb3VyUmFuZ2UgPSBsaW5lO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgb3VyUmFuZ2UgPSBleHRlbmRSYW5nZShkb2MsIG91clJhbmdlLCBzdGFydCk7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKCFhZGROZXcpIHtcclxuICAgICAgb3VySW5kZXggPSAwO1xyXG4gICAgICBzZXRTZWxlY3Rpb24oZG9jLCBuZXcgU2VsZWN0aW9uKFtvdXJSYW5nZV0sIDApLCBzZWxfbW91c2UpO1xyXG4gICAgICBzdGFydFNlbCA9IGRvYy5zZWw7XHJcbiAgICB9IGVsc2UgaWYgKG91ckluZGV4ID09IC0xKSB7XHJcbiAgICAgIG91ckluZGV4ID0gcmFuZ2VzLmxlbmd0aDtcclxuICAgICAgc2V0U2VsZWN0aW9uKGRvYywgbm9ybWFsaXplU2VsZWN0aW9uKHJhbmdlcy5jb25jYXQoW291clJhbmdlXSksIG91ckluZGV4KSxcclxuICAgICAgICAgICAgICAgICAgIHtzY3JvbGw6IGZhbHNlLCBvcmlnaW46IFwiKm1vdXNlXCJ9KTtcclxuICAgIH0gZWxzZSBpZiAocmFuZ2VzLmxlbmd0aCA+IDEgJiYgcmFuZ2VzW291ckluZGV4XS5lbXB0eSgpICYmIHR5cGUgPT0gXCJzaW5nbGVcIiAmJiAhZS5zaGlmdEtleSkge1xyXG4gICAgICBzZXRTZWxlY3Rpb24oZG9jLCBub3JtYWxpemVTZWxlY3Rpb24ocmFuZ2VzLnNsaWNlKDAsIG91ckluZGV4KS5jb25jYXQocmFuZ2VzLnNsaWNlKG91ckluZGV4ICsgMSkpLCAwKSxcclxuICAgICAgICAgICAgICAgICAgIHtzY3JvbGw6IGZhbHNlLCBvcmlnaW46IFwiKm1vdXNlXCJ9KTtcclxuICAgICAgc3RhcnRTZWwgPSBkb2Muc2VsO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgcmVwbGFjZU9uZVNlbGVjdGlvbihkb2MsIG91ckluZGV4LCBvdXJSYW5nZSwgc2VsX21vdXNlKTtcclxuICAgIH1cclxuXHJcbiAgICB2YXIgbGFzdFBvcyA9IHN0YXJ0O1xyXG4gICAgZnVuY3Rpb24gZXh0ZW5kVG8ocG9zKSB7XHJcbiAgICAgIGlmIChjbXAobGFzdFBvcywgcG9zKSA9PSAwKSByZXR1cm47XHJcbiAgICAgIGxhc3RQb3MgPSBwb3M7XHJcblxyXG4gICAgICBpZiAodHlwZSA9PSBcInJlY3RcIikge1xyXG4gICAgICAgIHZhciByYW5nZXMgPSBbXSwgdGFiU2l6ZSA9IGNtLm9wdGlvbnMudGFiU2l6ZTtcclxuICAgICAgICB2YXIgc3RhcnRDb2wgPSBjb3VudENvbHVtbihnZXRMaW5lKGRvYywgc3RhcnQubGluZSkudGV4dCwgc3RhcnQuY2gsIHRhYlNpemUpO1xyXG4gICAgICAgIHZhciBwb3NDb2wgPSBjb3VudENvbHVtbihnZXRMaW5lKGRvYywgcG9zLmxpbmUpLnRleHQsIHBvcy5jaCwgdGFiU2l6ZSk7XHJcbiAgICAgICAgdmFyIGxlZnQgPSBNYXRoLm1pbihzdGFydENvbCwgcG9zQ29sKSwgcmlnaHQgPSBNYXRoLm1heChzdGFydENvbCwgcG9zQ29sKTtcclxuICAgICAgICBmb3IgKHZhciBsaW5lID0gTWF0aC5taW4oc3RhcnQubGluZSwgcG9zLmxpbmUpLCBlbmQgPSBNYXRoLm1pbihjbS5sYXN0TGluZSgpLCBNYXRoLm1heChzdGFydC5saW5lLCBwb3MubGluZSkpO1xyXG4gICAgICAgICAgICAgbGluZSA8PSBlbmQ7IGxpbmUrKykge1xyXG4gICAgICAgICAgdmFyIHRleHQgPSBnZXRMaW5lKGRvYywgbGluZSkudGV4dCwgbGVmdFBvcyA9IGZpbmRDb2x1bW4odGV4dCwgbGVmdCwgdGFiU2l6ZSk7XHJcbiAgICAgICAgICBpZiAobGVmdCA9PSByaWdodClcclxuICAgICAgICAgICAgcmFuZ2VzLnB1c2gobmV3IFJhbmdlKFBvcyhsaW5lLCBsZWZ0UG9zKSwgUG9zKGxpbmUsIGxlZnRQb3MpKSk7XHJcbiAgICAgICAgICBlbHNlIGlmICh0ZXh0Lmxlbmd0aCA+IGxlZnRQb3MpXHJcbiAgICAgICAgICAgIHJhbmdlcy5wdXNoKG5ldyBSYW5nZShQb3MobGluZSwgbGVmdFBvcyksIFBvcyhsaW5lLCBmaW5kQ29sdW1uKHRleHQsIHJpZ2h0LCB0YWJTaXplKSkpKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYgKCFyYW5nZXMubGVuZ3RoKSByYW5nZXMucHVzaChuZXcgUmFuZ2Uoc3RhcnQsIHN0YXJ0KSk7XHJcbiAgICAgICAgc2V0U2VsZWN0aW9uKGRvYywgbm9ybWFsaXplU2VsZWN0aW9uKHN0YXJ0U2VsLnJhbmdlcy5zbGljZSgwLCBvdXJJbmRleCkuY29uY2F0KHJhbmdlcyksIG91ckluZGV4KSxcclxuICAgICAgICAgICAgICAgICAgICAge29yaWdpbjogXCIqbW91c2VcIiwgc2Nyb2xsOiBmYWxzZX0pO1xyXG4gICAgICAgIGNtLnNjcm9sbEludG9WaWV3KHBvcyk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgdmFyIG9sZFJhbmdlID0gb3VyUmFuZ2U7XHJcbiAgICAgICAgdmFyIGFuY2hvciA9IG9sZFJhbmdlLmFuY2hvciwgaGVhZCA9IHBvcztcclxuICAgICAgICBpZiAodHlwZSAhPSBcInNpbmdsZVwiKSB7XHJcbiAgICAgICAgICBpZiAodHlwZSA9PSBcImRvdWJsZVwiKVxyXG4gICAgICAgICAgICB2YXIgcmFuZ2UgPSBjbS5maW5kV29yZEF0KHBvcyk7XHJcbiAgICAgICAgICBlbHNlXHJcbiAgICAgICAgICAgIHZhciByYW5nZSA9IG5ldyBSYW5nZShQb3MocG9zLmxpbmUsIDApLCBjbGlwUG9zKGRvYywgUG9zKHBvcy5saW5lICsgMSwgMCkpKTtcclxuICAgICAgICAgIGlmIChjbXAocmFuZ2UuYW5jaG9yLCBhbmNob3IpID4gMCkge1xyXG4gICAgICAgICAgICBoZWFkID0gcmFuZ2UuaGVhZDtcclxuICAgICAgICAgICAgYW5jaG9yID0gbWluUG9zKG9sZFJhbmdlLmZyb20oKSwgcmFuZ2UuYW5jaG9yKTtcclxuICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIGhlYWQgPSByYW5nZS5hbmNob3I7XHJcbiAgICAgICAgICAgIGFuY2hvciA9IG1heFBvcyhvbGRSYW5nZS50bygpLCByYW5nZS5oZWFkKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgICAgdmFyIHJhbmdlcyA9IHN0YXJ0U2VsLnJhbmdlcy5zbGljZSgwKTtcclxuICAgICAgICByYW5nZXNbb3VySW5kZXhdID0gbmV3IFJhbmdlKGNsaXBQb3MoZG9jLCBhbmNob3IpLCBoZWFkKTtcclxuICAgICAgICBzZXRTZWxlY3Rpb24oZG9jLCBub3JtYWxpemVTZWxlY3Rpb24ocmFuZ2VzLCBvdXJJbmRleCksIHNlbF9tb3VzZSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICB2YXIgZWRpdG9yU2l6ZSA9IGRpc3BsYXkud3JhcHBlci5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcclxuICAgIC8vIFVzZWQgdG8gZW5zdXJlIHRpbWVvdXQgcmUtdHJpZXMgZG9uJ3QgZmlyZSB3aGVuIGFub3RoZXIgZXh0ZW5kXHJcbiAgICAvLyBoYXBwZW5lZCBpbiB0aGUgbWVhbnRpbWUgKGNsZWFyVGltZW91dCBpc24ndCByZWxpYWJsZSAtLSBhdFxyXG4gICAgLy8gbGVhc3Qgb24gQ2hyb21lLCB0aGUgdGltZW91dHMgc3RpbGwgaGFwcGVuIGV2ZW4gd2hlbiBjbGVhcmVkLFxyXG4gICAgLy8gaWYgdGhlIGNsZWFyIGhhcHBlbnMgYWZ0ZXIgdGhlaXIgc2NoZWR1bGVkIGZpcmluZyB0aW1lKS5cclxuICAgIHZhciBjb3VudGVyID0gMDtcclxuXHJcbiAgICBmdW5jdGlvbiBleHRlbmQoZSkge1xyXG4gICAgICB2YXIgY3VyQ291bnQgPSArK2NvdW50ZXI7XHJcbiAgICAgIHZhciBjdXIgPSBwb3NGcm9tTW91c2UoY20sIGUsIHRydWUsIHR5cGUgPT0gXCJyZWN0XCIpO1xyXG4gICAgICBpZiAoIWN1cikgcmV0dXJuO1xyXG4gICAgICBpZiAoY21wKGN1ciwgbGFzdFBvcykgIT0gMCkge1xyXG4gICAgICAgIGNtLmN1ck9wLmZvY3VzID0gYWN0aXZlRWx0KCk7XHJcbiAgICAgICAgZXh0ZW5kVG8oY3VyKTtcclxuICAgICAgICB2YXIgdmlzaWJsZSA9IHZpc2libGVMaW5lcyhkaXNwbGF5LCBkb2MpO1xyXG4gICAgICAgIGlmIChjdXIubGluZSA+PSB2aXNpYmxlLnRvIHx8IGN1ci5saW5lIDwgdmlzaWJsZS5mcm9tKVxyXG4gICAgICAgICAgc2V0VGltZW91dChvcGVyYXRpb24oY20sIGZ1bmN0aW9uKCl7aWYgKGNvdW50ZXIgPT0gY3VyQ291bnQpIGV4dGVuZChlKTt9KSwgMTUwKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICB2YXIgb3V0c2lkZSA9IGUuY2xpZW50WSA8IGVkaXRvclNpemUudG9wID8gLTIwIDogZS5jbGllbnRZID4gZWRpdG9yU2l6ZS5ib3R0b20gPyAyMCA6IDA7XHJcbiAgICAgICAgaWYgKG91dHNpZGUpIHNldFRpbWVvdXQob3BlcmF0aW9uKGNtLCBmdW5jdGlvbigpIHtcclxuICAgICAgICAgIGlmIChjb3VudGVyICE9IGN1ckNvdW50KSByZXR1cm47XHJcbiAgICAgICAgICBkaXNwbGF5LnNjcm9sbGVyLnNjcm9sbFRvcCArPSBvdXRzaWRlO1xyXG4gICAgICAgICAgZXh0ZW5kKGUpO1xyXG4gICAgICAgIH0pLCA1MCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBmdW5jdGlvbiBkb25lKGUpIHtcclxuICAgICAgY20uc3RhdGUuc2VsZWN0aW5nVGV4dCA9IGZhbHNlO1xyXG4gICAgICBjb3VudGVyID0gSW5maW5pdHk7XHJcbiAgICAgIGVfcHJldmVudERlZmF1bHQoZSk7XHJcbiAgICAgIGRpc3BsYXkuaW5wdXQuZm9jdXMoKTtcclxuICAgICAgb2ZmKGRvY3VtZW50LCBcIm1vdXNlbW92ZVwiLCBtb3ZlKTtcclxuICAgICAgb2ZmKGRvY3VtZW50LCBcIm1vdXNldXBcIiwgdXApO1xyXG4gICAgICBkb2MuaGlzdG9yeS5sYXN0U2VsT3JpZ2luID0gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICB2YXIgbW92ZSA9IG9wZXJhdGlvbihjbSwgZnVuY3Rpb24oZSkge1xyXG4gICAgICBpZiAoIWVfYnV0dG9uKGUpKSBkb25lKGUpO1xyXG4gICAgICBlbHNlIGV4dGVuZChlKTtcclxuICAgIH0pO1xyXG4gICAgdmFyIHVwID0gb3BlcmF0aW9uKGNtLCBkb25lKTtcclxuICAgIGNtLnN0YXRlLnNlbGVjdGluZ1RleHQgPSB1cDtcclxuICAgIG9uKGRvY3VtZW50LCBcIm1vdXNlbW92ZVwiLCBtb3ZlKTtcclxuICAgIG9uKGRvY3VtZW50LCBcIm1vdXNldXBcIiwgdXApO1xyXG4gIH1cclxuXHJcbiAgLy8gRGV0ZXJtaW5lcyB3aGV0aGVyIGFuIGV2ZW50IGhhcHBlbmVkIGluIHRoZSBndXR0ZXIsIGFuZCBmaXJlcyB0aGVcclxuICAvLyBoYW5kbGVycyBmb3IgdGhlIGNvcnJlc3BvbmRpbmcgZXZlbnQuXHJcbiAgZnVuY3Rpb24gZ3V0dGVyRXZlbnQoY20sIGUsIHR5cGUsIHByZXZlbnQsIHNpZ25hbGZuKSB7XHJcbiAgICB0cnkgeyB2YXIgbVggPSBlLmNsaWVudFgsIG1ZID0gZS5jbGllbnRZOyB9XHJcbiAgICBjYXRjaChlKSB7IHJldHVybiBmYWxzZTsgfVxyXG4gICAgaWYgKG1YID49IE1hdGguZmxvb3IoY20uZGlzcGxheS5ndXR0ZXJzLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLnJpZ2h0KSkgcmV0dXJuIGZhbHNlO1xyXG4gICAgaWYgKHByZXZlbnQpIGVfcHJldmVudERlZmF1bHQoZSk7XHJcblxyXG4gICAgdmFyIGRpc3BsYXkgPSBjbS5kaXNwbGF5O1xyXG4gICAgdmFyIGxpbmVCb3ggPSBkaXNwbGF5LmxpbmVEaXYuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XHJcblxyXG4gICAgaWYgKG1ZID4gbGluZUJveC5ib3R0b20gfHwgIWhhc0hhbmRsZXIoY20sIHR5cGUpKSByZXR1cm4gZV9kZWZhdWx0UHJldmVudGVkKGUpO1xyXG4gICAgbVkgLT0gbGluZUJveC50b3AgLSBkaXNwbGF5LnZpZXdPZmZzZXQ7XHJcblxyXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBjbS5vcHRpb25zLmd1dHRlcnMubGVuZ3RoOyArK2kpIHtcclxuICAgICAgdmFyIGcgPSBkaXNwbGF5Lmd1dHRlcnMuY2hpbGROb2Rlc1tpXTtcclxuICAgICAgaWYgKGcgJiYgZy5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS5yaWdodCA+PSBtWCkge1xyXG4gICAgICAgIHZhciBsaW5lID0gbGluZUF0SGVpZ2h0KGNtLmRvYywgbVkpO1xyXG4gICAgICAgIHZhciBndXR0ZXIgPSBjbS5vcHRpb25zLmd1dHRlcnNbaV07XHJcbiAgICAgICAgc2lnbmFsZm4oY20sIHR5cGUsIGNtLCBsaW5lLCBndXR0ZXIsIGUpO1xyXG4gICAgICAgIHJldHVybiBlX2RlZmF1bHRQcmV2ZW50ZWQoZSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIGNsaWNrSW5HdXR0ZXIoY20sIGUpIHtcclxuICAgIHJldHVybiBndXR0ZXJFdmVudChjbSwgZSwgXCJndXR0ZXJDbGlja1wiLCB0cnVlLCBzaWduYWxMYXRlcik7XHJcbiAgfVxyXG5cclxuICAvLyBLbHVkZ2UgdG8gd29yayBhcm91bmQgc3RyYW5nZSBJRSBiZWhhdmlvciB3aGVyZSBpdCdsbCBzb21ldGltZXNcclxuICAvLyByZS1maXJlIGEgc2VyaWVzIG9mIGRyYWctcmVsYXRlZCBldmVudHMgcmlnaHQgYWZ0ZXIgdGhlIGRyb3AgKCMxNTUxKVxyXG4gIHZhciBsYXN0RHJvcCA9IDA7XHJcblxyXG4gIGZ1bmN0aW9uIG9uRHJvcChlKSB7XHJcbiAgICB2YXIgY20gPSB0aGlzO1xyXG4gICAgY2xlYXJEcmFnQ3Vyc29yKGNtKTtcclxuICAgIGlmIChzaWduYWxET01FdmVudChjbSwgZSkgfHwgZXZlbnRJbldpZGdldChjbS5kaXNwbGF5LCBlKSlcclxuICAgICAgcmV0dXJuO1xyXG4gICAgZV9wcmV2ZW50RGVmYXVsdChlKTtcclxuICAgIGlmIChpZSkgbGFzdERyb3AgPSArbmV3IERhdGU7XHJcbiAgICB2YXIgcG9zID0gcG9zRnJvbU1vdXNlKGNtLCBlLCB0cnVlKSwgZmlsZXMgPSBlLmRhdGFUcmFuc2Zlci5maWxlcztcclxuICAgIGlmICghcG9zIHx8IGlzUmVhZE9ubHkoY20pKSByZXR1cm47XHJcbiAgICAvLyBNaWdodCBiZSBhIGZpbGUgZHJvcCwgaW4gd2hpY2ggY2FzZSB3ZSBzaW1wbHkgZXh0cmFjdCB0aGUgdGV4dFxyXG4gICAgLy8gYW5kIGluc2VydCBpdC5cclxuICAgIGlmIChmaWxlcyAmJiBmaWxlcy5sZW5ndGggJiYgd2luZG93LkZpbGVSZWFkZXIgJiYgd2luZG93LkZpbGUpIHtcclxuICAgICAgdmFyIG4gPSBmaWxlcy5sZW5ndGgsIHRleHQgPSBBcnJheShuKSwgcmVhZCA9IDA7XHJcbiAgICAgIHZhciBsb2FkRmlsZSA9IGZ1bmN0aW9uKGZpbGUsIGkpIHtcclxuICAgICAgICBpZiAoY20ub3B0aW9ucy5hbGxvd0Ryb3BGaWxlVHlwZXMgJiZcclxuICAgICAgICAgICAgaW5kZXhPZihjbS5vcHRpb25zLmFsbG93RHJvcEZpbGVUeXBlcywgZmlsZS50eXBlKSA9PSAtMSlcclxuICAgICAgICAgIHJldHVybjtcclxuXHJcbiAgICAgICAgdmFyIHJlYWRlciA9IG5ldyBGaWxlUmVhZGVyO1xyXG4gICAgICAgIHJlYWRlci5vbmxvYWQgPSBvcGVyYXRpb24oY20sIGZ1bmN0aW9uKCkge1xyXG4gICAgICAgICAgdmFyIGNvbnRlbnQgPSByZWFkZXIucmVzdWx0O1xyXG4gICAgICAgICAgaWYgKC9bXFx4MDAtXFx4MDhcXHgwZS1cXHgxZl17Mn0vLnRlc3QoY29udGVudCkpIGNvbnRlbnQgPSBcIlwiO1xyXG4gICAgICAgICAgdGV4dFtpXSA9IGNvbnRlbnQ7XHJcbiAgICAgICAgICBpZiAoKytyZWFkID09IG4pIHtcclxuICAgICAgICAgICAgcG9zID0gY2xpcFBvcyhjbS5kb2MsIHBvcyk7XHJcbiAgICAgICAgICAgIHZhciBjaGFuZ2UgPSB7ZnJvbTogcG9zLCB0bzogcG9zLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgIHRleHQ6IGNtLmRvYy5zcGxpdExpbmVzKHRleHQuam9pbihjbS5kb2MubGluZVNlcGFyYXRvcigpKSksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgb3JpZ2luOiBcInBhc3RlXCJ9O1xyXG4gICAgICAgICAgICBtYWtlQ2hhbmdlKGNtLmRvYywgY2hhbmdlKTtcclxuICAgICAgICAgICAgc2V0U2VsZWN0aW9uUmVwbGFjZUhpc3RvcnkoY20uZG9jLCBzaW1wbGVTZWxlY3Rpb24ocG9zLCBjaGFuZ2VFbmQoY2hhbmdlKSkpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH0pO1xyXG4gICAgICAgIHJlYWRlci5yZWFkQXNUZXh0KGZpbGUpO1xyXG4gICAgICB9O1xyXG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IG47ICsraSkgbG9hZEZpbGUoZmlsZXNbaV0sIGkpO1xyXG4gICAgfSBlbHNlIHsgLy8gTm9ybWFsIGRyb3BcclxuICAgICAgLy8gRG9uJ3QgZG8gYSByZXBsYWNlIGlmIHRoZSBkcm9wIGhhcHBlbmVkIGluc2lkZSBvZiB0aGUgc2VsZWN0ZWQgdGV4dC5cclxuICAgICAgaWYgKGNtLnN0YXRlLmRyYWdnaW5nVGV4dCAmJiBjbS5kb2Muc2VsLmNvbnRhaW5zKHBvcykgPiAtMSkge1xyXG4gICAgICAgIGNtLnN0YXRlLmRyYWdnaW5nVGV4dChlKTtcclxuICAgICAgICAvLyBFbnN1cmUgdGhlIGVkaXRvciBpcyByZS1mb2N1c2VkXHJcbiAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbigpIHtjbS5kaXNwbGF5LmlucHV0LmZvY3VzKCk7fSwgMjApO1xyXG4gICAgICAgIHJldHVybjtcclxuICAgICAgfVxyXG4gICAgICB0cnkge1xyXG4gICAgICAgIHZhciB0ZXh0ID0gZS5kYXRhVHJhbnNmZXIuZ2V0RGF0YShcIlRleHRcIik7XHJcbiAgICAgICAgaWYgKHRleHQpIHtcclxuICAgICAgICAgIGlmIChjbS5zdGF0ZS5kcmFnZ2luZ1RleHQgJiYgIShtYWMgPyBlLmFsdEtleSA6IGUuY3RybEtleSkpXHJcbiAgICAgICAgICAgIHZhciBzZWxlY3RlZCA9IGNtLmxpc3RTZWxlY3Rpb25zKCk7XHJcbiAgICAgICAgICBzZXRTZWxlY3Rpb25Ob1VuZG8oY20uZG9jLCBzaW1wbGVTZWxlY3Rpb24ocG9zLCBwb3MpKTtcclxuICAgICAgICAgIGlmIChzZWxlY3RlZCkgZm9yICh2YXIgaSA9IDA7IGkgPCBzZWxlY3RlZC5sZW5ndGg7ICsraSlcclxuICAgICAgICAgICAgcmVwbGFjZVJhbmdlKGNtLmRvYywgXCJcIiwgc2VsZWN0ZWRbaV0uYW5jaG9yLCBzZWxlY3RlZFtpXS5oZWFkLCBcImRyYWdcIik7XHJcbiAgICAgICAgICBjbS5yZXBsYWNlU2VsZWN0aW9uKHRleHQsIFwiYXJvdW5kXCIsIFwicGFzdGVcIik7XHJcbiAgICAgICAgICBjbS5kaXNwbGF5LmlucHV0LmZvY3VzKCk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICAgIGNhdGNoKGUpe31cclxuICAgIH1cclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIG9uRHJhZ1N0YXJ0KGNtLCBlKSB7XHJcbiAgICBpZiAoaWUgJiYgKCFjbS5zdGF0ZS5kcmFnZ2luZ1RleHQgfHwgK25ldyBEYXRlIC0gbGFzdERyb3AgPCAxMDApKSB7IGVfc3RvcChlKTsgcmV0dXJuOyB9XHJcbiAgICBpZiAoc2lnbmFsRE9NRXZlbnQoY20sIGUpIHx8IGV2ZW50SW5XaWRnZXQoY20uZGlzcGxheSwgZSkpIHJldHVybjtcclxuXHJcbiAgICBlLmRhdGFUcmFuc2Zlci5zZXREYXRhKFwiVGV4dFwiLCBjbS5nZXRTZWxlY3Rpb24oKSk7XHJcblxyXG4gICAgLy8gVXNlIGR1bW15IGltYWdlIGluc3RlYWQgb2YgZGVmYXVsdCBicm93c2VycyBpbWFnZS5cclxuICAgIC8vIFJlY2VudCBTYWZhcmkgKH42LjAuMikgaGF2ZSBhIHRlbmRlbmN5IHRvIHNlZ2ZhdWx0IHdoZW4gdGhpcyBoYXBwZW5zLCBzbyB3ZSBkb24ndCBkbyBpdCB0aGVyZS5cclxuICAgIGlmIChlLmRhdGFUcmFuc2Zlci5zZXREcmFnSW1hZ2UgJiYgIXNhZmFyaSkge1xyXG4gICAgICB2YXIgaW1nID0gZWx0KFwiaW1nXCIsIG51bGwsIG51bGwsIFwicG9zaXRpb246IGZpeGVkOyBsZWZ0OiAwOyB0b3A6IDA7XCIpO1xyXG4gICAgICBpbWcuc3JjID0gXCJkYXRhOmltYWdlL2dpZjtiYXNlNjQsUjBsR09EbGhBUUFCQUFBQUFDSDVCQUVLQUFFQUxBQUFBQUFCQUFFQUFBSUNUQUVBT3c9PVwiO1xyXG4gICAgICBpZiAocHJlc3RvKSB7XHJcbiAgICAgICAgaW1nLndpZHRoID0gaW1nLmhlaWdodCA9IDE7XHJcbiAgICAgICAgY20uZGlzcGxheS53cmFwcGVyLmFwcGVuZENoaWxkKGltZyk7XHJcbiAgICAgICAgLy8gRm9yY2UgYSByZWxheW91dCwgb3IgT3BlcmEgd29uJ3QgdXNlIG91ciBpbWFnZSBmb3Igc29tZSBvYnNjdXJlIHJlYXNvblxyXG4gICAgICAgIGltZy5fdG9wID0gaW1nLm9mZnNldFRvcDtcclxuICAgICAgfVxyXG4gICAgICBlLmRhdGFUcmFuc2Zlci5zZXREcmFnSW1hZ2UoaW1nLCAwLCAwKTtcclxuICAgICAgaWYgKHByZXN0bykgaW1nLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoaW1nKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIG9uRHJhZ092ZXIoY20sIGUpIHtcclxuICAgIHZhciBwb3MgPSBwb3NGcm9tTW91c2UoY20sIGUpO1xyXG4gICAgaWYgKCFwb3MpIHJldHVybjtcclxuICAgIHZhciBmcmFnID0gZG9jdW1lbnQuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpO1xyXG4gICAgZHJhd1NlbGVjdGlvbkN1cnNvcihjbSwgcG9zLCBmcmFnKTtcclxuICAgIGlmICghY20uZGlzcGxheS5kcmFnQ3Vyc29yKSB7XHJcbiAgICAgIGNtLmRpc3BsYXkuZHJhZ0N1cnNvciA9IGVsdChcImRpdlwiLCBudWxsLCBcIkNvZGVNaXJyb3ItY3Vyc29ycyBDb2RlTWlycm9yLWRyYWdjdXJzb3JzXCIpO1xyXG4gICAgICBjbS5kaXNwbGF5LmxpbmVTcGFjZS5pbnNlcnRCZWZvcmUoY20uZGlzcGxheS5kcmFnQ3Vyc29yLCBjbS5kaXNwbGF5LmN1cnNvckRpdik7XHJcbiAgICB9XHJcbiAgICByZW1vdmVDaGlsZHJlbkFuZEFkZChjbS5kaXNwbGF5LmRyYWdDdXJzb3IsIGZyYWcpO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gY2xlYXJEcmFnQ3Vyc29yKGNtKSB7XHJcbiAgICBpZiAoY20uZGlzcGxheS5kcmFnQ3Vyc29yKSB7XHJcbiAgICAgIGNtLmRpc3BsYXkubGluZVNwYWNlLnJlbW92ZUNoaWxkKGNtLmRpc3BsYXkuZHJhZ0N1cnNvcik7XHJcbiAgICAgIGNtLmRpc3BsYXkuZHJhZ0N1cnNvciA9IG51bGw7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBTQ1JPTEwgRVZFTlRTXHJcblxyXG4gIC8vIFN5bmMgdGhlIHNjcm9sbGFibGUgYXJlYSBhbmQgc2Nyb2xsYmFycywgZW5zdXJlIHRoZSB2aWV3cG9ydFxyXG4gIC8vIGNvdmVycyB0aGUgdmlzaWJsZSBhcmVhLlxyXG4gIGZ1bmN0aW9uIHNldFNjcm9sbFRvcChjbSwgdmFsKSB7XHJcbiAgICBpZiAoTWF0aC5hYnMoY20uZG9jLnNjcm9sbFRvcCAtIHZhbCkgPCAyKSByZXR1cm47XHJcbiAgICBjbS5kb2Muc2Nyb2xsVG9wID0gdmFsO1xyXG4gICAgaWYgKCFnZWNrbykgdXBkYXRlRGlzcGxheVNpbXBsZShjbSwge3RvcDogdmFsfSk7XHJcbiAgICBpZiAoY20uZGlzcGxheS5zY3JvbGxlci5zY3JvbGxUb3AgIT0gdmFsKSBjbS5kaXNwbGF5LnNjcm9sbGVyLnNjcm9sbFRvcCA9IHZhbDtcclxuICAgIGNtLmRpc3BsYXkuc2Nyb2xsYmFycy5zZXRTY3JvbGxUb3AodmFsKTtcclxuICAgIGlmIChnZWNrbykgdXBkYXRlRGlzcGxheVNpbXBsZShjbSk7XHJcbiAgICBzdGFydFdvcmtlcihjbSwgMTAwKTtcclxuICB9XHJcbiAgLy8gU3luYyBzY3JvbGxlciBhbmQgc2Nyb2xsYmFyLCBlbnN1cmUgdGhlIGd1dHRlciBlbGVtZW50cyBhcmVcclxuICAvLyBhbGlnbmVkLlxyXG4gIGZ1bmN0aW9uIHNldFNjcm9sbExlZnQoY20sIHZhbCwgaXNTY3JvbGxlcikge1xyXG4gICAgaWYgKGlzU2Nyb2xsZXIgPyB2YWwgPT0gY20uZG9jLnNjcm9sbExlZnQgOiBNYXRoLmFicyhjbS5kb2Muc2Nyb2xsTGVmdCAtIHZhbCkgPCAyKSByZXR1cm47XHJcbiAgICB2YWwgPSBNYXRoLm1pbih2YWwsIGNtLmRpc3BsYXkuc2Nyb2xsZXIuc2Nyb2xsV2lkdGggLSBjbS5kaXNwbGF5LnNjcm9sbGVyLmNsaWVudFdpZHRoKTtcclxuICAgIGNtLmRvYy5zY3JvbGxMZWZ0ID0gdmFsO1xyXG4gICAgYWxpZ25Ib3Jpem9udGFsbHkoY20pO1xyXG4gICAgaWYgKGNtLmRpc3BsYXkuc2Nyb2xsZXIuc2Nyb2xsTGVmdCAhPSB2YWwpIGNtLmRpc3BsYXkuc2Nyb2xsZXIuc2Nyb2xsTGVmdCA9IHZhbDtcclxuICAgIGNtLmRpc3BsYXkuc2Nyb2xsYmFycy5zZXRTY3JvbGxMZWZ0KHZhbCk7XHJcbiAgfVxyXG5cclxuICAvLyBTaW5jZSB0aGUgZGVsdGEgdmFsdWVzIHJlcG9ydGVkIG9uIG1vdXNlIHdoZWVsIGV2ZW50cyBhcmVcclxuICAvLyB1bnN0YW5kYXJkaXplZCBiZXR3ZWVuIGJyb3dzZXJzIGFuZCBldmVuIGJyb3dzZXIgdmVyc2lvbnMsIGFuZFxyXG4gIC8vIGdlbmVyYWxseSBob3JyaWJseSB1bnByZWRpY3RhYmxlLCB0aGlzIGNvZGUgc3RhcnRzIGJ5IG1lYXN1cmluZ1xyXG4gIC8vIHRoZSBzY3JvbGwgZWZmZWN0IHRoYXQgdGhlIGZpcnN0IGZldyBtb3VzZSB3aGVlbCBldmVudHMgaGF2ZSxcclxuICAvLyBhbmQsIGZyb20gdGhhdCwgZGV0ZWN0cyB0aGUgd2F5IGl0IGNhbiBjb252ZXJ0IGRlbHRhcyB0byBwaXhlbFxyXG4gIC8vIG9mZnNldHMgYWZ0ZXJ3YXJkcy5cclxuICAvL1xyXG4gIC8vIFRoZSByZWFzb24gd2Ugd2FudCB0byBrbm93IHRoZSBhbW91bnQgYSB3aGVlbCBldmVudCB3aWxsIHNjcm9sbFxyXG4gIC8vIGlzIHRoYXQgaXQgZ2l2ZXMgdXMgYSBjaGFuY2UgdG8gdXBkYXRlIHRoZSBkaXNwbGF5IGJlZm9yZSB0aGVcclxuICAvLyBhY3R1YWwgc2Nyb2xsaW5nIGhhcHBlbnMsIHJlZHVjaW5nIGZsaWNrZXJpbmcuXHJcblxyXG4gIHZhciB3aGVlbFNhbXBsZXMgPSAwLCB3aGVlbFBpeGVsc1BlclVuaXQgPSBudWxsO1xyXG4gIC8vIEZpbGwgaW4gYSBicm93c2VyLWRldGVjdGVkIHN0YXJ0aW5nIHZhbHVlIG9uIGJyb3dzZXJzIHdoZXJlIHdlXHJcbiAgLy8ga25vdyBvbmUuIFRoZXNlIGRvbid0IGhhdmUgdG8gYmUgYWNjdXJhdGUgLS0gdGhlIHJlc3VsdCBvZiB0aGVtXHJcbiAgLy8gYmVpbmcgd3Jvbmcgd291bGQganVzdCBiZSBhIHNsaWdodCBmbGlja2VyIG9uIHRoZSBmaXJzdCB3aGVlbFxyXG4gIC8vIHNjcm9sbCAoaWYgaXQgaXMgbGFyZ2UgZW5vdWdoKS5cclxuICBpZiAoaWUpIHdoZWVsUGl4ZWxzUGVyVW5pdCA9IC0uNTM7XHJcbiAgZWxzZSBpZiAoZ2Vja28pIHdoZWVsUGl4ZWxzUGVyVW5pdCA9IDE1O1xyXG4gIGVsc2UgaWYgKGNocm9tZSkgd2hlZWxQaXhlbHNQZXJVbml0ID0gLS43O1xyXG4gIGVsc2UgaWYgKHNhZmFyaSkgd2hlZWxQaXhlbHNQZXJVbml0ID0gLTEvMztcclxuXHJcbiAgdmFyIHdoZWVsRXZlbnREZWx0YSA9IGZ1bmN0aW9uKGUpIHtcclxuICAgIHZhciBkeCA9IGUud2hlZWxEZWx0YVgsIGR5ID0gZS53aGVlbERlbHRhWTtcclxuICAgIGlmIChkeCA9PSBudWxsICYmIGUuZGV0YWlsICYmIGUuYXhpcyA9PSBlLkhPUklaT05UQUxfQVhJUykgZHggPSBlLmRldGFpbDtcclxuICAgIGlmIChkeSA9PSBudWxsICYmIGUuZGV0YWlsICYmIGUuYXhpcyA9PSBlLlZFUlRJQ0FMX0FYSVMpIGR5ID0gZS5kZXRhaWw7XHJcbiAgICBlbHNlIGlmIChkeSA9PSBudWxsKSBkeSA9IGUud2hlZWxEZWx0YTtcclxuICAgIHJldHVybiB7eDogZHgsIHk6IGR5fTtcclxuICB9O1xyXG4gIENvZGVNaXJyb3Iud2hlZWxFdmVudFBpeGVscyA9IGZ1bmN0aW9uKGUpIHtcclxuICAgIHZhciBkZWx0YSA9IHdoZWVsRXZlbnREZWx0YShlKTtcclxuICAgIGRlbHRhLnggKj0gd2hlZWxQaXhlbHNQZXJVbml0O1xyXG4gICAgZGVsdGEueSAqPSB3aGVlbFBpeGVsc1BlclVuaXQ7XHJcbiAgICByZXR1cm4gZGVsdGE7XHJcbiAgfTtcclxuXHJcbiAgZnVuY3Rpb24gb25TY3JvbGxXaGVlbChjbSwgZSkge1xyXG4gICAgdmFyIGRlbHRhID0gd2hlZWxFdmVudERlbHRhKGUpLCBkeCA9IGRlbHRhLngsIGR5ID0gZGVsdGEueTtcclxuXHJcbiAgICB2YXIgZGlzcGxheSA9IGNtLmRpc3BsYXksIHNjcm9sbCA9IGRpc3BsYXkuc2Nyb2xsZXI7XHJcbiAgICAvLyBRdWl0IGlmIHRoZXJlJ3Mgbm90aGluZyB0byBzY3JvbGwgaGVyZVxyXG4gICAgdmFyIGNhblNjcm9sbFggPSBzY3JvbGwuc2Nyb2xsV2lkdGggPiBzY3JvbGwuY2xpZW50V2lkdGg7XHJcbiAgICB2YXIgY2FuU2Nyb2xsWSA9IHNjcm9sbC5zY3JvbGxIZWlnaHQgPiBzY3JvbGwuY2xpZW50SGVpZ2h0O1xyXG4gICAgaWYgKCEoZHggJiYgY2FuU2Nyb2xsWCB8fCBkeSAmJiBjYW5TY3JvbGxZKSkgcmV0dXJuO1xyXG5cclxuICAgIC8vIFdlYmtpdCBicm93c2VycyBvbiBPUyBYIGFib3J0IG1vbWVudHVtIHNjcm9sbHMgd2hlbiB0aGUgdGFyZ2V0XHJcbiAgICAvLyBvZiB0aGUgc2Nyb2xsIGV2ZW50IGlzIHJlbW92ZWQgZnJvbSB0aGUgc2Nyb2xsYWJsZSBlbGVtZW50LlxyXG4gICAgLy8gVGhpcyBoYWNrIChzZWUgcmVsYXRlZCBjb2RlIGluIHBhdGNoRGlzcGxheSkgbWFrZXMgc3VyZSB0aGVcclxuICAgIC8vIGVsZW1lbnQgaXMga2VwdCBhcm91bmQuXHJcbiAgICBpZiAoZHkgJiYgbWFjICYmIHdlYmtpdCkge1xyXG4gICAgICBvdXRlcjogZm9yICh2YXIgY3VyID0gZS50YXJnZXQsIHZpZXcgPSBkaXNwbGF5LnZpZXc7IGN1ciAhPSBzY3JvbGw7IGN1ciA9IGN1ci5wYXJlbnROb2RlKSB7XHJcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB2aWV3Lmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgICBpZiAodmlld1tpXS5ub2RlID09IGN1cikge1xyXG4gICAgICAgICAgICBjbS5kaXNwbGF5LmN1cnJlbnRXaGVlbFRhcmdldCA9IGN1cjtcclxuICAgICAgICAgICAgYnJlYWsgb3V0ZXI7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gT24gc29tZSBicm93c2VycywgaG9yaXpvbnRhbCBzY3JvbGxpbmcgd2lsbCBjYXVzZSByZWRyYXdzIHRvXHJcbiAgICAvLyBoYXBwZW4gYmVmb3JlIHRoZSBndXR0ZXIgaGFzIGJlZW4gcmVhbGlnbmVkLCBjYXVzaW5nIGl0IHRvXHJcbiAgICAvLyB3cmlnZ2xlIGFyb3VuZCBpbiBhIG1vc3QgdW5zZWVtbHkgd2F5LiBXaGVuIHdlIGhhdmUgYW5cclxuICAgIC8vIGVzdGltYXRlZCBwaXhlbHMvZGVsdGEgdmFsdWUsIHdlIGp1c3QgaGFuZGxlIGhvcml6b250YWxcclxuICAgIC8vIHNjcm9sbGluZyBlbnRpcmVseSBoZXJlLiBJdCdsbCBiZSBzbGlnaHRseSBvZmYgZnJvbSBuYXRpdmUsIGJ1dFxyXG4gICAgLy8gYmV0dGVyIHRoYW4gZ2xpdGNoaW5nIG91dC5cclxuICAgIGlmIChkeCAmJiAhZ2Vja28gJiYgIXByZXN0byAmJiB3aGVlbFBpeGVsc1BlclVuaXQgIT0gbnVsbCkge1xyXG4gICAgICBpZiAoZHkgJiYgY2FuU2Nyb2xsWSlcclxuICAgICAgICBzZXRTY3JvbGxUb3AoY20sIE1hdGgubWF4KDAsIE1hdGgubWluKHNjcm9sbC5zY3JvbGxUb3AgKyBkeSAqIHdoZWVsUGl4ZWxzUGVyVW5pdCwgc2Nyb2xsLnNjcm9sbEhlaWdodCAtIHNjcm9sbC5jbGllbnRIZWlnaHQpKSk7XHJcbiAgICAgIHNldFNjcm9sbExlZnQoY20sIE1hdGgubWF4KDAsIE1hdGgubWluKHNjcm9sbC5zY3JvbGxMZWZ0ICsgZHggKiB3aGVlbFBpeGVsc1BlclVuaXQsIHNjcm9sbC5zY3JvbGxXaWR0aCAtIHNjcm9sbC5jbGllbnRXaWR0aCkpKTtcclxuICAgICAgLy8gT25seSBwcmV2ZW50IGRlZmF1bHQgc2Nyb2xsaW5nIGlmIHZlcnRpY2FsIHNjcm9sbGluZyBpc1xyXG4gICAgICAvLyBhY3R1YWxseSBwb3NzaWJsZS4gT3RoZXJ3aXNlLCBpdCBjYXVzZXMgdmVydGljYWwgc2Nyb2xsXHJcbiAgICAgIC8vIGppdHRlciBvbiBPU1ggdHJhY2twYWRzIHdoZW4gZGVsdGFYIGlzIHNtYWxsIGFuZCBkZWx0YVlcclxuICAgICAgLy8gaXMgbGFyZ2UgKGlzc3VlICMzNTc5KVxyXG4gICAgICBpZiAoIWR5IHx8IChkeSAmJiBjYW5TY3JvbGxZKSlcclxuICAgICAgICBlX3ByZXZlbnREZWZhdWx0KGUpO1xyXG4gICAgICBkaXNwbGF5LndoZWVsU3RhcnRYID0gbnVsbDsgLy8gQWJvcnQgbWVhc3VyZW1lbnQsIGlmIGluIHByb2dyZXNzXHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuXHJcbiAgICAvLyAnUHJvamVjdCcgdGhlIHZpc2libGUgdmlld3BvcnQgdG8gY292ZXIgdGhlIGFyZWEgdGhhdCBpcyBiZWluZ1xyXG4gICAgLy8gc2Nyb2xsZWQgaW50byB2aWV3IChpZiB3ZSBrbm93IGVub3VnaCB0byBlc3RpbWF0ZSBpdCkuXHJcbiAgICBpZiAoZHkgJiYgd2hlZWxQaXhlbHNQZXJVbml0ICE9IG51bGwpIHtcclxuICAgICAgdmFyIHBpeGVscyA9IGR5ICogd2hlZWxQaXhlbHNQZXJVbml0O1xyXG4gICAgICB2YXIgdG9wID0gY20uZG9jLnNjcm9sbFRvcCwgYm90ID0gdG9wICsgZGlzcGxheS53cmFwcGVyLmNsaWVudEhlaWdodDtcclxuICAgICAgaWYgKHBpeGVscyA8IDApIHRvcCA9IE1hdGgubWF4KDAsIHRvcCArIHBpeGVscyAtIDUwKTtcclxuICAgICAgZWxzZSBib3QgPSBNYXRoLm1pbihjbS5kb2MuaGVpZ2h0LCBib3QgKyBwaXhlbHMgKyA1MCk7XHJcbiAgICAgIHVwZGF0ZURpc3BsYXlTaW1wbGUoY20sIHt0b3A6IHRvcCwgYm90dG9tOiBib3R9KTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAod2hlZWxTYW1wbGVzIDwgMjApIHtcclxuICAgICAgaWYgKGRpc3BsYXkud2hlZWxTdGFydFggPT0gbnVsbCkge1xyXG4gICAgICAgIGRpc3BsYXkud2hlZWxTdGFydFggPSBzY3JvbGwuc2Nyb2xsTGVmdDsgZGlzcGxheS53aGVlbFN0YXJ0WSA9IHNjcm9sbC5zY3JvbGxUb3A7XHJcbiAgICAgICAgZGlzcGxheS53aGVlbERYID0gZHg7IGRpc3BsYXkud2hlZWxEWSA9IGR5O1xyXG4gICAgICAgIHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XHJcbiAgICAgICAgICBpZiAoZGlzcGxheS53aGVlbFN0YXJ0WCA9PSBudWxsKSByZXR1cm47XHJcbiAgICAgICAgICB2YXIgbW92ZWRYID0gc2Nyb2xsLnNjcm9sbExlZnQgLSBkaXNwbGF5LndoZWVsU3RhcnRYO1xyXG4gICAgICAgICAgdmFyIG1vdmVkWSA9IHNjcm9sbC5zY3JvbGxUb3AgLSBkaXNwbGF5LndoZWVsU3RhcnRZO1xyXG4gICAgICAgICAgdmFyIHNhbXBsZSA9IChtb3ZlZFkgJiYgZGlzcGxheS53aGVlbERZICYmIG1vdmVkWSAvIGRpc3BsYXkud2hlZWxEWSkgfHxcclxuICAgICAgICAgICAgKG1vdmVkWCAmJiBkaXNwbGF5LndoZWVsRFggJiYgbW92ZWRYIC8gZGlzcGxheS53aGVlbERYKTtcclxuICAgICAgICAgIGRpc3BsYXkud2hlZWxTdGFydFggPSBkaXNwbGF5LndoZWVsU3RhcnRZID0gbnVsbDtcclxuICAgICAgICAgIGlmICghc2FtcGxlKSByZXR1cm47XHJcbiAgICAgICAgICB3aGVlbFBpeGVsc1BlclVuaXQgPSAod2hlZWxQaXhlbHNQZXJVbml0ICogd2hlZWxTYW1wbGVzICsgc2FtcGxlKSAvICh3aGVlbFNhbXBsZXMgKyAxKTtcclxuICAgICAgICAgICsrd2hlZWxTYW1wbGVzO1xyXG4gICAgICAgIH0sIDIwMCk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgZGlzcGxheS53aGVlbERYICs9IGR4OyBkaXNwbGF5LndoZWVsRFkgKz0gZHk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIEtFWSBFVkVOVFNcclxuXHJcbiAgLy8gUnVuIGEgaGFuZGxlciB0aGF0IHdhcyBib3VuZCB0byBhIGtleS5cclxuICBmdW5jdGlvbiBkb0hhbmRsZUJpbmRpbmcoY20sIGJvdW5kLCBkcm9wU2hpZnQpIHtcclxuICAgIGlmICh0eXBlb2YgYm91bmQgPT0gXCJzdHJpbmdcIikge1xyXG4gICAgICBib3VuZCA9IGNvbW1hbmRzW2JvdW5kXTtcclxuICAgICAgaWYgKCFib3VuZCkgcmV0dXJuIGZhbHNlO1xyXG4gICAgfVxyXG4gICAgLy8gRW5zdXJlIHByZXZpb3VzIGlucHV0IGhhcyBiZWVuIHJlYWQsIHNvIHRoYXQgdGhlIGhhbmRsZXIgc2VlcyBhXHJcbiAgICAvLyBjb25zaXN0ZW50IHZpZXcgb2YgdGhlIGRvY3VtZW50XHJcbiAgICBjbS5kaXNwbGF5LmlucHV0LmVuc3VyZVBvbGxlZCgpO1xyXG4gICAgdmFyIHByZXZTaGlmdCA9IGNtLmRpc3BsYXkuc2hpZnQsIGRvbmUgPSBmYWxzZTtcclxuICAgIHRyeSB7XHJcbiAgICAgIGlmIChpc1JlYWRPbmx5KGNtKSkgY20uc3RhdGUuc3VwcHJlc3NFZGl0cyA9IHRydWU7XHJcbiAgICAgIGlmIChkcm9wU2hpZnQpIGNtLmRpc3BsYXkuc2hpZnQgPSBmYWxzZTtcclxuICAgICAgZG9uZSA9IGJvdW5kKGNtKSAhPSBQYXNzO1xyXG4gICAgfSBmaW5hbGx5IHtcclxuICAgICAgY20uZGlzcGxheS5zaGlmdCA9IHByZXZTaGlmdDtcclxuICAgICAgY20uc3RhdGUuc3VwcHJlc3NFZGl0cyA9IGZhbHNlO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIGRvbmU7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBsb29rdXBLZXlGb3JFZGl0b3IoY20sIG5hbWUsIGhhbmRsZSkge1xyXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBjbS5zdGF0ZS5rZXlNYXBzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgIHZhciByZXN1bHQgPSBsb29rdXBLZXkobmFtZSwgY20uc3RhdGUua2V5TWFwc1tpXSwgaGFuZGxlLCBjbSk7XHJcbiAgICAgIGlmIChyZXN1bHQpIHJldHVybiByZXN1bHQ7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gKGNtLm9wdGlvbnMuZXh0cmFLZXlzICYmIGxvb2t1cEtleShuYW1lLCBjbS5vcHRpb25zLmV4dHJhS2V5cywgaGFuZGxlLCBjbSkpXHJcbiAgICAgIHx8IGxvb2t1cEtleShuYW1lLCBjbS5vcHRpb25zLmtleU1hcCwgaGFuZGxlLCBjbSk7XHJcbiAgfVxyXG5cclxuICB2YXIgc3RvcFNlcSA9IG5ldyBEZWxheWVkO1xyXG4gIGZ1bmN0aW9uIGRpc3BhdGNoS2V5KGNtLCBuYW1lLCBlLCBoYW5kbGUpIHtcclxuICAgIHZhciBzZXEgPSBjbS5zdGF0ZS5rZXlTZXE7XHJcbiAgICBpZiAoc2VxKSB7XHJcbiAgICAgIGlmIChpc01vZGlmaWVyS2V5KG5hbWUpKSByZXR1cm4gXCJoYW5kbGVkXCI7XHJcbiAgICAgIHN0b3BTZXEuc2V0KDUwLCBmdW5jdGlvbigpIHtcclxuICAgICAgICBpZiAoY20uc3RhdGUua2V5U2VxID09IHNlcSkge1xyXG4gICAgICAgICAgY20uc3RhdGUua2V5U2VxID0gbnVsbDtcclxuICAgICAgICAgIGNtLmRpc3BsYXkuaW5wdXQucmVzZXQoKTtcclxuICAgICAgICB9XHJcbiAgICAgIH0pO1xyXG4gICAgICBuYW1lID0gc2VxICsgXCIgXCIgKyBuYW1lO1xyXG4gICAgfVxyXG4gICAgdmFyIHJlc3VsdCA9IGxvb2t1cEtleUZvckVkaXRvcihjbSwgbmFtZSwgaGFuZGxlKTtcclxuXHJcbiAgICBpZiAocmVzdWx0ID09IFwibXVsdGlcIilcclxuICAgICAgY20uc3RhdGUua2V5U2VxID0gbmFtZTtcclxuICAgIGlmIChyZXN1bHQgPT0gXCJoYW5kbGVkXCIpXHJcbiAgICAgIHNpZ25hbExhdGVyKGNtLCBcImtleUhhbmRsZWRcIiwgY20sIG5hbWUsIGUpO1xyXG5cclxuICAgIGlmIChyZXN1bHQgPT0gXCJoYW5kbGVkXCIgfHwgcmVzdWx0ID09IFwibXVsdGlcIikge1xyXG4gICAgICBlX3ByZXZlbnREZWZhdWx0KGUpO1xyXG4gICAgICByZXN0YXJ0QmxpbmsoY20pO1xyXG4gICAgfVxyXG5cclxuICAgIGlmIChzZXEgJiYgIXJlc3VsdCAmJiAvXFwnJC8udGVzdChuYW1lKSkge1xyXG4gICAgICBlX3ByZXZlbnREZWZhdWx0KGUpO1xyXG4gICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuICAgIHJldHVybiAhIXJlc3VsdDtcclxuICB9XHJcblxyXG4gIC8vIEhhbmRsZSBhIGtleSBmcm9tIHRoZSBrZXlkb3duIGV2ZW50LlxyXG4gIGZ1bmN0aW9uIGhhbmRsZUtleUJpbmRpbmcoY20sIGUpIHtcclxuICAgIHZhciBuYW1lID0ga2V5TmFtZShlLCB0cnVlKTtcclxuICAgIGlmICghbmFtZSkgcmV0dXJuIGZhbHNlO1xyXG5cclxuICAgIGlmIChlLnNoaWZ0S2V5ICYmICFjbS5zdGF0ZS5rZXlTZXEpIHtcclxuICAgICAgLy8gRmlyc3QgdHJ5IHRvIHJlc29sdmUgZnVsbCBuYW1lIChpbmNsdWRpbmcgJ1NoaWZ0LScpLiBGYWlsaW5nXHJcbiAgICAgIC8vIHRoYXQsIHNlZSBpZiB0aGVyZSBpcyBhIGN1cnNvci1tb3Rpb24gY29tbWFuZCAoc3RhcnRpbmcgd2l0aFxyXG4gICAgICAvLyAnZ28nKSBib3VuZCB0byB0aGUga2V5bmFtZSB3aXRob3V0ICdTaGlmdC0nLlxyXG4gICAgICByZXR1cm4gZGlzcGF0Y2hLZXkoY20sIFwiU2hpZnQtXCIgKyBuYW1lLCBlLCBmdW5jdGlvbihiKSB7cmV0dXJuIGRvSGFuZGxlQmluZGluZyhjbSwgYiwgdHJ1ZSk7fSlcclxuICAgICAgICAgIHx8IGRpc3BhdGNoS2V5KGNtLCBuYW1lLCBlLCBmdW5jdGlvbihiKSB7XHJcbiAgICAgICAgICAgICAgIGlmICh0eXBlb2YgYiA9PSBcInN0cmluZ1wiID8gL15nb1tBLVpdLy50ZXN0KGIpIDogYi5tb3Rpb24pXHJcbiAgICAgICAgICAgICAgICAgcmV0dXJuIGRvSGFuZGxlQmluZGluZyhjbSwgYik7XHJcbiAgICAgICAgICAgICB9KTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIHJldHVybiBkaXNwYXRjaEtleShjbSwgbmFtZSwgZSwgZnVuY3Rpb24oYikgeyByZXR1cm4gZG9IYW5kbGVCaW5kaW5nKGNtLCBiKTsgfSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBIYW5kbGUgYSBrZXkgZnJvbSB0aGUga2V5cHJlc3MgZXZlbnRcclxuICBmdW5jdGlvbiBoYW5kbGVDaGFyQmluZGluZyhjbSwgZSwgY2gpIHtcclxuICAgIHJldHVybiBkaXNwYXRjaEtleShjbSwgXCInXCIgKyBjaCArIFwiJ1wiLCBlLFxyXG4gICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKGIpIHsgcmV0dXJuIGRvSGFuZGxlQmluZGluZyhjbSwgYiwgdHJ1ZSk7IH0pO1xyXG4gIH1cclxuXHJcbiAgdmFyIGxhc3RTdG9wcGVkS2V5ID0gbnVsbDtcclxuICBmdW5jdGlvbiBvbktleURvd24oZSkge1xyXG4gICAgdmFyIGNtID0gdGhpcztcclxuICAgIGNtLmN1ck9wLmZvY3VzID0gYWN0aXZlRWx0KCk7XHJcbiAgICBpZiAoc2lnbmFsRE9NRXZlbnQoY20sIGUpKSByZXR1cm47XHJcbiAgICAvLyBJRSBkb2VzIHN0cmFuZ2UgdGhpbmdzIHdpdGggZXNjYXBlLlxyXG4gICAgaWYgKGllICYmIGllX3ZlcnNpb24gPCAxMSAmJiBlLmtleUNvZGUgPT0gMjcpIGUucmV0dXJuVmFsdWUgPSBmYWxzZTtcclxuICAgIHZhciBjb2RlID0gZS5rZXlDb2RlO1xyXG4gICAgY20uZGlzcGxheS5zaGlmdCA9IGNvZGUgPT0gMTYgfHwgZS5zaGlmdEtleTtcclxuICAgIHZhciBoYW5kbGVkID0gaGFuZGxlS2V5QmluZGluZyhjbSwgZSk7XHJcbiAgICBpZiAocHJlc3RvKSB7XHJcbiAgICAgIGxhc3RTdG9wcGVkS2V5ID0gaGFuZGxlZCA/IGNvZGUgOiBudWxsO1xyXG4gICAgICAvLyBPcGVyYSBoYXMgbm8gY3V0IGV2ZW50Li4uIHdlIHRyeSB0byBhdCBsZWFzdCBjYXRjaCB0aGUga2V5IGNvbWJvXHJcbiAgICAgIGlmICghaGFuZGxlZCAmJiBjb2RlID09IDg4ICYmICFoYXNDb3B5RXZlbnQgJiYgKG1hYyA/IGUubWV0YUtleSA6IGUuY3RybEtleSkpXHJcbiAgICAgICAgY20ucmVwbGFjZVNlbGVjdGlvbihcIlwiLCBudWxsLCBcImN1dFwiKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBUdXJuIG1vdXNlIGludG8gY3Jvc3NoYWlyIHdoZW4gQWx0IGlzIGhlbGQgb24gTWFjLlxyXG4gICAgaWYgKGNvZGUgPT0gMTggJiYgIS9cXGJDb2RlTWlycm9yLWNyb3NzaGFpclxcYi8udGVzdChjbS5kaXNwbGF5LmxpbmVEaXYuY2xhc3NOYW1lKSlcclxuICAgICAgc2hvd0Nyb3NzSGFpcihjbSk7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBzaG93Q3Jvc3NIYWlyKGNtKSB7XHJcbiAgICB2YXIgbGluZURpdiA9IGNtLmRpc3BsYXkubGluZURpdjtcclxuICAgIGFkZENsYXNzKGxpbmVEaXYsIFwiQ29kZU1pcnJvci1jcm9zc2hhaXJcIik7XHJcblxyXG4gICAgZnVuY3Rpb24gdXAoZSkge1xyXG4gICAgICBpZiAoZS5rZXlDb2RlID09IDE4IHx8ICFlLmFsdEtleSkge1xyXG4gICAgICAgIHJtQ2xhc3MobGluZURpdiwgXCJDb2RlTWlycm9yLWNyb3NzaGFpclwiKTtcclxuICAgICAgICBvZmYoZG9jdW1lbnQsIFwia2V5dXBcIiwgdXApO1xyXG4gICAgICAgIG9mZihkb2N1bWVudCwgXCJtb3VzZW92ZXJcIiwgdXApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICBvbihkb2N1bWVudCwgXCJrZXl1cFwiLCB1cCk7XHJcbiAgICBvbihkb2N1bWVudCwgXCJtb3VzZW92ZXJcIiwgdXApO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gb25LZXlVcChlKSB7XHJcbiAgICBpZiAoZS5rZXlDb2RlID09IDE2KSB0aGlzLmRvYy5zZWwuc2hpZnQgPSBmYWxzZTtcclxuICAgIHNpZ25hbERPTUV2ZW50KHRoaXMsIGUpO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gb25LZXlQcmVzcyhlKSB7XHJcbiAgICB2YXIgY20gPSB0aGlzO1xyXG4gICAgaWYgKGV2ZW50SW5XaWRnZXQoY20uZGlzcGxheSwgZSkgfHwgc2lnbmFsRE9NRXZlbnQoY20sIGUpIHx8IGUuY3RybEtleSAmJiAhZS5hbHRLZXkgfHwgbWFjICYmIGUubWV0YUtleSkgcmV0dXJuO1xyXG4gICAgdmFyIGtleUNvZGUgPSBlLmtleUNvZGUsIGNoYXJDb2RlID0gZS5jaGFyQ29kZTtcclxuICAgIGlmIChwcmVzdG8gJiYga2V5Q29kZSA9PSBsYXN0U3RvcHBlZEtleSkge2xhc3RTdG9wcGVkS2V5ID0gbnVsbDsgZV9wcmV2ZW50RGVmYXVsdChlKTsgcmV0dXJuO31cclxuICAgIGlmICgocHJlc3RvICYmICghZS53aGljaCB8fCBlLndoaWNoIDwgMTApKSAmJiBoYW5kbGVLZXlCaW5kaW5nKGNtLCBlKSkgcmV0dXJuO1xyXG4gICAgdmFyIGNoID0gU3RyaW5nLmZyb21DaGFyQ29kZShjaGFyQ29kZSA9PSBudWxsID8ga2V5Q29kZSA6IGNoYXJDb2RlKTtcclxuICAgIGlmIChoYW5kbGVDaGFyQmluZGluZyhjbSwgZSwgY2gpKSByZXR1cm47XHJcbiAgICBjbS5kaXNwbGF5LmlucHV0Lm9uS2V5UHJlc3MoZSk7XHJcbiAgfVxyXG5cclxuICAvLyBGT0NVUy9CTFVSIEVWRU5UU1xyXG5cclxuICBmdW5jdGlvbiBkZWxheUJsdXJFdmVudChjbSkge1xyXG4gICAgY20uc3RhdGUuZGVsYXlpbmdCbHVyRXZlbnQgPSB0cnVlO1xyXG4gICAgc2V0VGltZW91dChmdW5jdGlvbigpIHtcclxuICAgICAgaWYgKGNtLnN0YXRlLmRlbGF5aW5nQmx1ckV2ZW50KSB7XHJcbiAgICAgICAgY20uc3RhdGUuZGVsYXlpbmdCbHVyRXZlbnQgPSBmYWxzZTtcclxuICAgICAgICBvbkJsdXIoY20pO1xyXG4gICAgICB9XHJcbiAgICB9LCAxMDApO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gb25Gb2N1cyhjbSkge1xyXG4gICAgaWYgKGNtLnN0YXRlLmRlbGF5aW5nQmx1ckV2ZW50KSBjbS5zdGF0ZS5kZWxheWluZ0JsdXJFdmVudCA9IGZhbHNlO1xyXG5cclxuICAgIGlmIChjbS5vcHRpb25zLnJlYWRPbmx5ID09IFwibm9jdXJzb3JcIikgcmV0dXJuO1xyXG4gICAgaWYgKCFjbS5zdGF0ZS5mb2N1c2VkKSB7XHJcbiAgICAgIHNpZ25hbChjbSwgXCJmb2N1c1wiLCBjbSk7XHJcbiAgICAgIGNtLnN0YXRlLmZvY3VzZWQgPSB0cnVlO1xyXG4gICAgICBhZGRDbGFzcyhjbS5kaXNwbGF5LndyYXBwZXIsIFwiQ29kZU1pcnJvci1mb2N1c2VkXCIpO1xyXG4gICAgICAvLyBUaGlzIHRlc3QgcHJldmVudHMgdGhpcyBmcm9tIGZpcmluZyB3aGVuIGEgY29udGV4dFxyXG4gICAgICAvLyBtZW51IGlzIGNsb3NlZCAoc2luY2UgdGhlIGlucHV0IHJlc2V0IHdvdWxkIGtpbGwgdGhlXHJcbiAgICAgIC8vIHNlbGVjdC1hbGwgZGV0ZWN0aW9uIGhhY2spXHJcbiAgICAgIGlmICghY20uY3VyT3AgJiYgY20uZGlzcGxheS5zZWxGb3JDb250ZXh0TWVudSAhPSBjbS5kb2Muc2VsKSB7XHJcbiAgICAgICAgY20uZGlzcGxheS5pbnB1dC5yZXNldCgpO1xyXG4gICAgICAgIGlmICh3ZWJraXQpIHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7IGNtLmRpc3BsYXkuaW5wdXQucmVzZXQodHJ1ZSk7IH0sIDIwKTsgLy8gSXNzdWUgIzE3MzBcclxuICAgICAgfVxyXG4gICAgICBjbS5kaXNwbGF5LmlucHV0LnJlY2VpdmVkRm9jdXMoKTtcclxuICAgIH1cclxuICAgIHJlc3RhcnRCbGluayhjbSk7XHJcbiAgfVxyXG4gIGZ1bmN0aW9uIG9uQmx1cihjbSkge1xyXG4gICAgaWYgKGNtLnN0YXRlLmRlbGF5aW5nQmx1ckV2ZW50KSByZXR1cm47XHJcblxyXG4gICAgaWYgKGNtLnN0YXRlLmZvY3VzZWQpIHtcclxuICAgICAgc2lnbmFsKGNtLCBcImJsdXJcIiwgY20pO1xyXG4gICAgICBjbS5zdGF0ZS5mb2N1c2VkID0gZmFsc2U7XHJcbiAgICAgIHJtQ2xhc3MoY20uZGlzcGxheS53cmFwcGVyLCBcIkNvZGVNaXJyb3ItZm9jdXNlZFwiKTtcclxuICAgIH1cclxuICAgIGNsZWFySW50ZXJ2YWwoY20uZGlzcGxheS5ibGlua2VyKTtcclxuICAgIHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7aWYgKCFjbS5zdGF0ZS5mb2N1c2VkKSBjbS5kaXNwbGF5LnNoaWZ0ID0gZmFsc2U7fSwgMTUwKTtcclxuICB9XHJcblxyXG4gIC8vIENPTlRFWFQgTUVOVSBIQU5ETElOR1xyXG5cclxuICAvLyBUbyBtYWtlIHRoZSBjb250ZXh0IG1lbnUgd29yaywgd2UgbmVlZCB0byBicmllZmx5IHVuaGlkZSB0aGVcclxuICAvLyB0ZXh0YXJlYSAobWFraW5nIGl0IGFzIHVub2J0cnVzaXZlIGFzIHBvc3NpYmxlKSB0byBsZXQgdGhlXHJcbiAgLy8gcmlnaHQtY2xpY2sgdGFrZSBlZmZlY3Qgb24gaXQuXHJcbiAgZnVuY3Rpb24gb25Db250ZXh0TWVudShjbSwgZSkge1xyXG4gICAgaWYgKGV2ZW50SW5XaWRnZXQoY20uZGlzcGxheSwgZSkgfHwgY29udGV4dE1lbnVJbkd1dHRlcihjbSwgZSkpIHJldHVybjtcclxuICAgIGlmIChzaWduYWxET01FdmVudChjbSwgZSwgXCJjb250ZXh0bWVudVwiKSkgcmV0dXJuO1xyXG4gICAgY20uZGlzcGxheS5pbnB1dC5vbkNvbnRleHRNZW51KGUpO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gY29udGV4dE1lbnVJbkd1dHRlcihjbSwgZSkge1xyXG4gICAgaWYgKCFoYXNIYW5kbGVyKGNtLCBcImd1dHRlckNvbnRleHRNZW51XCIpKSByZXR1cm4gZmFsc2U7XHJcbiAgICByZXR1cm4gZ3V0dGVyRXZlbnQoY20sIGUsIFwiZ3V0dGVyQ29udGV4dE1lbnVcIiwgZmFsc2UsIHNpZ25hbCk7XHJcbiAgfVxyXG5cclxuICAvLyBVUERBVElOR1xyXG5cclxuICAvLyBDb21wdXRlIHRoZSBwb3NpdGlvbiBvZiB0aGUgZW5kIG9mIGEgY2hhbmdlIChpdHMgJ3RvJyBwcm9wZXJ0eVxyXG4gIC8vIHJlZmVycyB0byB0aGUgcHJlLWNoYW5nZSBlbmQpLlxyXG4gIHZhciBjaGFuZ2VFbmQgPSBDb2RlTWlycm9yLmNoYW5nZUVuZCA9IGZ1bmN0aW9uKGNoYW5nZSkge1xyXG4gICAgaWYgKCFjaGFuZ2UudGV4dCkgcmV0dXJuIGNoYW5nZS50bztcclxuICAgIHJldHVybiBQb3MoY2hhbmdlLmZyb20ubGluZSArIGNoYW5nZS50ZXh0Lmxlbmd0aCAtIDEsXHJcbiAgICAgICAgICAgICAgIGxzdChjaGFuZ2UudGV4dCkubGVuZ3RoICsgKGNoYW5nZS50ZXh0Lmxlbmd0aCA9PSAxID8gY2hhbmdlLmZyb20uY2ggOiAwKSk7XHJcbiAgfTtcclxuXHJcbiAgLy8gQWRqdXN0IGEgcG9zaXRpb24gdG8gcmVmZXIgdG8gdGhlIHBvc3QtY2hhbmdlIHBvc2l0aW9uIG9mIHRoZVxyXG4gIC8vIHNhbWUgdGV4dCwgb3IgdGhlIGVuZCBvZiB0aGUgY2hhbmdlIGlmIHRoZSBjaGFuZ2UgY292ZXJzIGl0LlxyXG4gIGZ1bmN0aW9uIGFkanVzdEZvckNoYW5nZShwb3MsIGNoYW5nZSkge1xyXG4gICAgaWYgKGNtcChwb3MsIGNoYW5nZS5mcm9tKSA8IDApIHJldHVybiBwb3M7XHJcbiAgICBpZiAoY21wKHBvcywgY2hhbmdlLnRvKSA8PSAwKSByZXR1cm4gY2hhbmdlRW5kKGNoYW5nZSk7XHJcblxyXG4gICAgdmFyIGxpbmUgPSBwb3MubGluZSArIGNoYW5nZS50ZXh0Lmxlbmd0aCAtIChjaGFuZ2UudG8ubGluZSAtIGNoYW5nZS5mcm9tLmxpbmUpIC0gMSwgY2ggPSBwb3MuY2g7XHJcbiAgICBpZiAocG9zLmxpbmUgPT0gY2hhbmdlLnRvLmxpbmUpIGNoICs9IGNoYW5nZUVuZChjaGFuZ2UpLmNoIC0gY2hhbmdlLnRvLmNoO1xyXG4gICAgcmV0dXJuIFBvcyhsaW5lLCBjaCk7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBjb21wdXRlU2VsQWZ0ZXJDaGFuZ2UoZG9jLCBjaGFuZ2UpIHtcclxuICAgIHZhciBvdXQgPSBbXTtcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgZG9jLnNlbC5yYW5nZXMubGVuZ3RoOyBpKyspIHtcclxuICAgICAgdmFyIHJhbmdlID0gZG9jLnNlbC5yYW5nZXNbaV07XHJcbiAgICAgIG91dC5wdXNoKG5ldyBSYW5nZShhZGp1c3RGb3JDaGFuZ2UocmFuZ2UuYW5jaG9yLCBjaGFuZ2UpLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgYWRqdXN0Rm9yQ2hhbmdlKHJhbmdlLmhlYWQsIGNoYW5nZSkpKTtcclxuICAgIH1cclxuICAgIHJldHVybiBub3JtYWxpemVTZWxlY3Rpb24ob3V0LCBkb2Muc2VsLnByaW1JbmRleCk7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBvZmZzZXRQb3MocG9zLCBvbGQsIG53KSB7XHJcbiAgICBpZiAocG9zLmxpbmUgPT0gb2xkLmxpbmUpXHJcbiAgICAgIHJldHVybiBQb3MobncubGluZSwgcG9zLmNoIC0gb2xkLmNoICsgbncuY2gpO1xyXG4gICAgZWxzZVxyXG4gICAgICByZXR1cm4gUG9zKG53LmxpbmUgKyAocG9zLmxpbmUgLSBvbGQubGluZSksIHBvcy5jaCk7XHJcbiAgfVxyXG5cclxuICAvLyBVc2VkIGJ5IHJlcGxhY2VTZWxlY3Rpb25zIHRvIGFsbG93IG1vdmluZyB0aGUgc2VsZWN0aW9uIHRvIHRoZVxyXG4gIC8vIHN0YXJ0IG9yIGFyb3VuZCB0aGUgcmVwbGFjZWQgdGVzdC4gSGludCBtYXkgYmUgXCJzdGFydFwiIG9yIFwiYXJvdW5kXCIuXHJcbiAgZnVuY3Rpb24gY29tcHV0ZVJlcGxhY2VkU2VsKGRvYywgY2hhbmdlcywgaGludCkge1xyXG4gICAgdmFyIG91dCA9IFtdO1xyXG4gICAgdmFyIG9sZFByZXYgPSBQb3MoZG9jLmZpcnN0LCAwKSwgbmV3UHJldiA9IG9sZFByZXY7XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGNoYW5nZXMubGVuZ3RoOyBpKyspIHtcclxuICAgICAgdmFyIGNoYW5nZSA9IGNoYW5nZXNbaV07XHJcbiAgICAgIHZhciBmcm9tID0gb2Zmc2V0UG9zKGNoYW5nZS5mcm9tLCBvbGRQcmV2LCBuZXdQcmV2KTtcclxuICAgICAgdmFyIHRvID0gb2Zmc2V0UG9zKGNoYW5nZUVuZChjaGFuZ2UpLCBvbGRQcmV2LCBuZXdQcmV2KTtcclxuICAgICAgb2xkUHJldiA9IGNoYW5nZS50bztcclxuICAgICAgbmV3UHJldiA9IHRvO1xyXG4gICAgICBpZiAoaGludCA9PSBcImFyb3VuZFwiKSB7XHJcbiAgICAgICAgdmFyIHJhbmdlID0gZG9jLnNlbC5yYW5nZXNbaV0sIGludiA9IGNtcChyYW5nZS5oZWFkLCByYW5nZS5hbmNob3IpIDwgMDtcclxuICAgICAgICBvdXRbaV0gPSBuZXcgUmFuZ2UoaW52ID8gdG8gOiBmcm9tLCBpbnYgPyBmcm9tIDogdG8pO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIG91dFtpXSA9IG5ldyBSYW5nZShmcm9tLCBmcm9tKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgcmV0dXJuIG5ldyBTZWxlY3Rpb24ob3V0LCBkb2Muc2VsLnByaW1JbmRleCk7XHJcbiAgfVxyXG5cclxuICAvLyBBbGxvdyBcImJlZm9yZUNoYW5nZVwiIGV2ZW50IGhhbmRsZXJzIHRvIGluZmx1ZW5jZSBhIGNoYW5nZVxyXG4gIGZ1bmN0aW9uIGZpbHRlckNoYW5nZShkb2MsIGNoYW5nZSwgdXBkYXRlKSB7XHJcbiAgICB2YXIgb2JqID0ge1xyXG4gICAgICBjYW5jZWxlZDogZmFsc2UsXHJcbiAgICAgIGZyb206IGNoYW5nZS5mcm9tLFxyXG4gICAgICB0bzogY2hhbmdlLnRvLFxyXG4gICAgICB0ZXh0OiBjaGFuZ2UudGV4dCxcclxuICAgICAgb3JpZ2luOiBjaGFuZ2Uub3JpZ2luLFxyXG4gICAgICBjYW5jZWw6IGZ1bmN0aW9uKCkgeyB0aGlzLmNhbmNlbGVkID0gdHJ1ZTsgfVxyXG4gICAgfTtcclxuICAgIGlmICh1cGRhdGUpIG9iai51cGRhdGUgPSBmdW5jdGlvbihmcm9tLCB0bywgdGV4dCwgb3JpZ2luKSB7XHJcbiAgICAgIGlmIChmcm9tKSB0aGlzLmZyb20gPSBjbGlwUG9zKGRvYywgZnJvbSk7XHJcbiAgICAgIGlmICh0bykgdGhpcy50byA9IGNsaXBQb3MoZG9jLCB0byk7XHJcbiAgICAgIGlmICh0ZXh0KSB0aGlzLnRleHQgPSB0ZXh0O1xyXG4gICAgICBpZiAob3JpZ2luICE9PSB1bmRlZmluZWQpIHRoaXMub3JpZ2luID0gb3JpZ2luO1xyXG4gICAgfTtcclxuICAgIHNpZ25hbChkb2MsIFwiYmVmb3JlQ2hhbmdlXCIsIGRvYywgb2JqKTtcclxuICAgIGlmIChkb2MuY20pIHNpZ25hbChkb2MuY20sIFwiYmVmb3JlQ2hhbmdlXCIsIGRvYy5jbSwgb2JqKTtcclxuXHJcbiAgICBpZiAob2JqLmNhbmNlbGVkKSByZXR1cm4gbnVsbDtcclxuICAgIHJldHVybiB7ZnJvbTogb2JqLmZyb20sIHRvOiBvYmoudG8sIHRleHQ6IG9iai50ZXh0LCBvcmlnaW46IG9iai5vcmlnaW59O1xyXG4gIH1cclxuXHJcbiAgLy8gQXBwbHkgYSBjaGFuZ2UgdG8gYSBkb2N1bWVudCwgYW5kIGFkZCBpdCB0byB0aGUgZG9jdW1lbnQnc1xyXG4gIC8vIGhpc3RvcnksIGFuZCBwcm9wYWdhdGluZyBpdCB0byBhbGwgbGlua2VkIGRvY3VtZW50cy5cclxuICBmdW5jdGlvbiBtYWtlQ2hhbmdlKGRvYywgY2hhbmdlLCBpZ25vcmVSZWFkT25seSkge1xyXG4gICAgaWYgKGRvYy5jbSkge1xyXG4gICAgICBpZiAoIWRvYy5jbS5jdXJPcCkgcmV0dXJuIG9wZXJhdGlvbihkb2MuY20sIG1ha2VDaGFuZ2UpKGRvYywgY2hhbmdlLCBpZ25vcmVSZWFkT25seSk7XHJcbiAgICAgIGlmIChkb2MuY20uc3RhdGUuc3VwcHJlc3NFZGl0cykgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIGlmIChoYXNIYW5kbGVyKGRvYywgXCJiZWZvcmVDaGFuZ2VcIikgfHwgZG9jLmNtICYmIGhhc0hhbmRsZXIoZG9jLmNtLCBcImJlZm9yZUNoYW5nZVwiKSkge1xyXG4gICAgICBjaGFuZ2UgPSBmaWx0ZXJDaGFuZ2UoZG9jLCBjaGFuZ2UsIHRydWUpO1xyXG4gICAgICBpZiAoIWNoYW5nZSkgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFBvc3NpYmx5IHNwbGl0IG9yIHN1cHByZXNzIHRoZSB1cGRhdGUgYmFzZWQgb24gdGhlIHByZXNlbmNlXHJcbiAgICAvLyBvZiByZWFkLW9ubHkgc3BhbnMgaW4gaXRzIHJhbmdlLlxyXG4gICAgdmFyIHNwbGl0ID0gc2F3UmVhZE9ubHlTcGFucyAmJiAhaWdub3JlUmVhZE9ubHkgJiYgcmVtb3ZlUmVhZE9ubHlSYW5nZXMoZG9jLCBjaGFuZ2UuZnJvbSwgY2hhbmdlLnRvKTtcclxuICAgIGlmIChzcGxpdCkge1xyXG4gICAgICBmb3IgKHZhciBpID0gc3BsaXQubGVuZ3RoIC0gMTsgaSA+PSAwOyAtLWkpXHJcbiAgICAgICAgbWFrZUNoYW5nZUlubmVyKGRvYywge2Zyb206IHNwbGl0W2ldLmZyb20sIHRvOiBzcGxpdFtpXS50bywgdGV4dDogaSA/IFtcIlwiXSA6IGNoYW5nZS50ZXh0fSk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICBtYWtlQ2hhbmdlSW5uZXIoZG9jLCBjaGFuZ2UpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gbWFrZUNoYW5nZUlubmVyKGRvYywgY2hhbmdlKSB7XHJcbiAgICBpZiAoY2hhbmdlLnRleHQubGVuZ3RoID09IDEgJiYgY2hhbmdlLnRleHRbMF0gPT0gXCJcIiAmJiBjbXAoY2hhbmdlLmZyb20sIGNoYW5nZS50bykgPT0gMCkgcmV0dXJuO1xyXG4gICAgdmFyIHNlbEFmdGVyID0gY29tcHV0ZVNlbEFmdGVyQ2hhbmdlKGRvYywgY2hhbmdlKTtcclxuICAgIGFkZENoYW5nZVRvSGlzdG9yeShkb2MsIGNoYW5nZSwgc2VsQWZ0ZXIsIGRvYy5jbSA/IGRvYy5jbS5jdXJPcC5pZCA6IE5hTik7XHJcblxyXG4gICAgbWFrZUNoYW5nZVNpbmdsZURvYyhkb2MsIGNoYW5nZSwgc2VsQWZ0ZXIsIHN0cmV0Y2hTcGFuc092ZXJDaGFuZ2UoZG9jLCBjaGFuZ2UpKTtcclxuICAgIHZhciByZWJhc2VkID0gW107XHJcblxyXG4gICAgbGlua2VkRG9jcyhkb2MsIGZ1bmN0aW9uKGRvYywgc2hhcmVkSGlzdCkge1xyXG4gICAgICBpZiAoIXNoYXJlZEhpc3QgJiYgaW5kZXhPZihyZWJhc2VkLCBkb2MuaGlzdG9yeSkgPT0gLTEpIHtcclxuICAgICAgICByZWJhc2VIaXN0KGRvYy5oaXN0b3J5LCBjaGFuZ2UpO1xyXG4gICAgICAgIHJlYmFzZWQucHVzaChkb2MuaGlzdG9yeSk7XHJcbiAgICAgIH1cclxuICAgICAgbWFrZUNoYW5nZVNpbmdsZURvYyhkb2MsIGNoYW5nZSwgbnVsbCwgc3RyZXRjaFNwYW5zT3ZlckNoYW5nZShkb2MsIGNoYW5nZSkpO1xyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuICAvLyBSZXZlcnQgYSBjaGFuZ2Ugc3RvcmVkIGluIGEgZG9jdW1lbnQncyBoaXN0b3J5LlxyXG4gIGZ1bmN0aW9uIG1ha2VDaGFuZ2VGcm9tSGlzdG9yeShkb2MsIHR5cGUsIGFsbG93U2VsZWN0aW9uT25seSkge1xyXG4gICAgaWYgKGRvYy5jbSAmJiBkb2MuY20uc3RhdGUuc3VwcHJlc3NFZGl0cykgcmV0dXJuO1xyXG5cclxuICAgIHZhciBoaXN0ID0gZG9jLmhpc3RvcnksIGV2ZW50LCBzZWxBZnRlciA9IGRvYy5zZWw7XHJcbiAgICB2YXIgc291cmNlID0gdHlwZSA9PSBcInVuZG9cIiA/IGhpc3QuZG9uZSA6IGhpc3QudW5kb25lLCBkZXN0ID0gdHlwZSA9PSBcInVuZG9cIiA/IGhpc3QudW5kb25lIDogaGlzdC5kb25lO1xyXG5cclxuICAgIC8vIFZlcmlmeSB0aGF0IHRoZXJlIGlzIGEgdXNlYWJsZSBldmVudCAoc28gdGhhdCBjdHJsLXogd29uJ3RcclxuICAgIC8vIG5lZWRsZXNzbHkgY2xlYXIgc2VsZWN0aW9uIGV2ZW50cylcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgc291cmNlLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgIGV2ZW50ID0gc291cmNlW2ldO1xyXG4gICAgICBpZiAoYWxsb3dTZWxlY3Rpb25Pbmx5ID8gZXZlbnQucmFuZ2VzICYmICFldmVudC5lcXVhbHMoZG9jLnNlbCkgOiAhZXZlbnQucmFuZ2VzKVxyXG4gICAgICAgIGJyZWFrO1xyXG4gICAgfVxyXG4gICAgaWYgKGkgPT0gc291cmNlLmxlbmd0aCkgcmV0dXJuO1xyXG4gICAgaGlzdC5sYXN0T3JpZ2luID0gaGlzdC5sYXN0U2VsT3JpZ2luID0gbnVsbDtcclxuXHJcbiAgICBmb3IgKDs7KSB7XHJcbiAgICAgIGV2ZW50ID0gc291cmNlLnBvcCgpO1xyXG4gICAgICBpZiAoZXZlbnQucmFuZ2VzKSB7XHJcbiAgICAgICAgcHVzaFNlbGVjdGlvblRvSGlzdG9yeShldmVudCwgZGVzdCk7XHJcbiAgICAgICAgaWYgKGFsbG93U2VsZWN0aW9uT25seSAmJiAhZXZlbnQuZXF1YWxzKGRvYy5zZWwpKSB7XHJcbiAgICAgICAgICBzZXRTZWxlY3Rpb24oZG9jLCBldmVudCwge2NsZWFyUmVkbzogZmFsc2V9KTtcclxuICAgICAgICAgIHJldHVybjtcclxuICAgICAgICB9XHJcbiAgICAgICAgc2VsQWZ0ZXIgPSBldmVudDtcclxuICAgICAgfVxyXG4gICAgICBlbHNlIGJyZWFrO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEJ1aWxkIHVwIGEgcmV2ZXJzZSBjaGFuZ2Ugb2JqZWN0IHRvIGFkZCB0byB0aGUgb3Bwb3NpdGUgaGlzdG9yeVxyXG4gICAgLy8gc3RhY2sgKHJlZG8gd2hlbiB1bmRvaW5nLCBhbmQgdmljZSB2ZXJzYSkuXHJcbiAgICB2YXIgYW50aUNoYW5nZXMgPSBbXTtcclxuICAgIHB1c2hTZWxlY3Rpb25Ub0hpc3Rvcnkoc2VsQWZ0ZXIsIGRlc3QpO1xyXG4gICAgZGVzdC5wdXNoKHtjaGFuZ2VzOiBhbnRpQ2hhbmdlcywgZ2VuZXJhdGlvbjogaGlzdC5nZW5lcmF0aW9ufSk7XHJcbiAgICBoaXN0LmdlbmVyYXRpb24gPSBldmVudC5nZW5lcmF0aW9uIHx8ICsraGlzdC5tYXhHZW5lcmF0aW9uO1xyXG5cclxuICAgIHZhciBmaWx0ZXIgPSBoYXNIYW5kbGVyKGRvYywgXCJiZWZvcmVDaGFuZ2VcIikgfHwgZG9jLmNtICYmIGhhc0hhbmRsZXIoZG9jLmNtLCBcImJlZm9yZUNoYW5nZVwiKTtcclxuXHJcbiAgICBmb3IgKHZhciBpID0gZXZlbnQuY2hhbmdlcy5sZW5ndGggLSAxOyBpID49IDA7IC0taSkge1xyXG4gICAgICB2YXIgY2hhbmdlID0gZXZlbnQuY2hhbmdlc1tpXTtcclxuICAgICAgY2hhbmdlLm9yaWdpbiA9IHR5cGU7XHJcbiAgICAgIGlmIChmaWx0ZXIgJiYgIWZpbHRlckNoYW5nZShkb2MsIGNoYW5nZSwgZmFsc2UpKSB7XHJcbiAgICAgICAgc291cmNlLmxlbmd0aCA9IDA7XHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBhbnRpQ2hhbmdlcy5wdXNoKGhpc3RvcnlDaGFuZ2VGcm9tQ2hhbmdlKGRvYywgY2hhbmdlKSk7XHJcblxyXG4gICAgICB2YXIgYWZ0ZXIgPSBpID8gY29tcHV0ZVNlbEFmdGVyQ2hhbmdlKGRvYywgY2hhbmdlKSA6IGxzdChzb3VyY2UpO1xyXG4gICAgICBtYWtlQ2hhbmdlU2luZ2xlRG9jKGRvYywgY2hhbmdlLCBhZnRlciwgbWVyZ2VPbGRTcGFucyhkb2MsIGNoYW5nZSkpO1xyXG4gICAgICBpZiAoIWkgJiYgZG9jLmNtKSBkb2MuY20uc2Nyb2xsSW50b1ZpZXcoe2Zyb206IGNoYW5nZS5mcm9tLCB0bzogY2hhbmdlRW5kKGNoYW5nZSl9KTtcclxuICAgICAgdmFyIHJlYmFzZWQgPSBbXTtcclxuXHJcbiAgICAgIC8vIFByb3BhZ2F0ZSB0byB0aGUgbGlua2VkIGRvY3VtZW50c1xyXG4gICAgICBsaW5rZWREb2NzKGRvYywgZnVuY3Rpb24oZG9jLCBzaGFyZWRIaXN0KSB7XHJcbiAgICAgICAgaWYgKCFzaGFyZWRIaXN0ICYmIGluZGV4T2YocmViYXNlZCwgZG9jLmhpc3RvcnkpID09IC0xKSB7XHJcbiAgICAgICAgICByZWJhc2VIaXN0KGRvYy5oaXN0b3J5LCBjaGFuZ2UpO1xyXG4gICAgICAgICAgcmViYXNlZC5wdXNoKGRvYy5oaXN0b3J5KTtcclxuICAgICAgICB9XHJcbiAgICAgICAgbWFrZUNoYW5nZVNpbmdsZURvYyhkb2MsIGNoYW5nZSwgbnVsbCwgbWVyZ2VPbGRTcGFucyhkb2MsIGNoYW5nZSkpO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFN1Yi12aWV3cyBuZWVkIHRoZWlyIGxpbmUgbnVtYmVycyBzaGlmdGVkIHdoZW4gdGV4dCBpcyBhZGRlZFxyXG4gIC8vIGFib3ZlIG9yIGJlbG93IHRoZW0gaW4gdGhlIHBhcmVudCBkb2N1bWVudC5cclxuICBmdW5jdGlvbiBzaGlmdERvYyhkb2MsIGRpc3RhbmNlKSB7XHJcbiAgICBpZiAoZGlzdGFuY2UgPT0gMCkgcmV0dXJuO1xyXG4gICAgZG9jLmZpcnN0ICs9IGRpc3RhbmNlO1xyXG4gICAgZG9jLnNlbCA9IG5ldyBTZWxlY3Rpb24obWFwKGRvYy5zZWwucmFuZ2VzLCBmdW5jdGlvbihyYW5nZSkge1xyXG4gICAgICByZXR1cm4gbmV3IFJhbmdlKFBvcyhyYW5nZS5hbmNob3IubGluZSArIGRpc3RhbmNlLCByYW5nZS5hbmNob3IuY2gpLFxyXG4gICAgICAgICAgICAgICAgICAgICAgIFBvcyhyYW5nZS5oZWFkLmxpbmUgKyBkaXN0YW5jZSwgcmFuZ2UuaGVhZC5jaCkpO1xyXG4gICAgfSksIGRvYy5zZWwucHJpbUluZGV4KTtcclxuICAgIGlmIChkb2MuY20pIHtcclxuICAgICAgcmVnQ2hhbmdlKGRvYy5jbSwgZG9jLmZpcnN0LCBkb2MuZmlyc3QgLSBkaXN0YW5jZSwgZGlzdGFuY2UpO1xyXG4gICAgICBmb3IgKHZhciBkID0gZG9jLmNtLmRpc3BsYXksIGwgPSBkLnZpZXdGcm9tOyBsIDwgZC52aWV3VG87IGwrKylcclxuICAgICAgICByZWdMaW5lQ2hhbmdlKGRvYy5jbSwgbCwgXCJndXR0ZXJcIik7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBNb3JlIGxvd2VyLWxldmVsIGNoYW5nZSBmdW5jdGlvbiwgaGFuZGxpbmcgb25seSBhIHNpbmdsZSBkb2N1bWVudFxyXG4gIC8vIChub3QgbGlua2VkIG9uZXMpLlxyXG4gIGZ1bmN0aW9uIG1ha2VDaGFuZ2VTaW5nbGVEb2MoZG9jLCBjaGFuZ2UsIHNlbEFmdGVyLCBzcGFucykge1xyXG4gICAgaWYgKGRvYy5jbSAmJiAhZG9jLmNtLmN1ck9wKVxyXG4gICAgICByZXR1cm4gb3BlcmF0aW9uKGRvYy5jbSwgbWFrZUNoYW5nZVNpbmdsZURvYykoZG9jLCBjaGFuZ2UsIHNlbEFmdGVyLCBzcGFucyk7XHJcblxyXG4gICAgaWYgKGNoYW5nZS50by5saW5lIDwgZG9jLmZpcnN0KSB7XHJcbiAgICAgIHNoaWZ0RG9jKGRvYywgY2hhbmdlLnRleHQubGVuZ3RoIC0gMSAtIChjaGFuZ2UudG8ubGluZSAtIGNoYW5nZS5mcm9tLmxpbmUpKTtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG4gICAgaWYgKGNoYW5nZS5mcm9tLmxpbmUgPiBkb2MubGFzdExpbmUoKSkgcmV0dXJuO1xyXG5cclxuICAgIC8vIENsaXAgdGhlIGNoYW5nZSB0byB0aGUgc2l6ZSBvZiB0aGlzIGRvY1xyXG4gICAgaWYgKGNoYW5nZS5mcm9tLmxpbmUgPCBkb2MuZmlyc3QpIHtcclxuICAgICAgdmFyIHNoaWZ0ID0gY2hhbmdlLnRleHQubGVuZ3RoIC0gMSAtIChkb2MuZmlyc3QgLSBjaGFuZ2UuZnJvbS5saW5lKTtcclxuICAgICAgc2hpZnREb2MoZG9jLCBzaGlmdCk7XHJcbiAgICAgIGNoYW5nZSA9IHtmcm9tOiBQb3MoZG9jLmZpcnN0LCAwKSwgdG86IFBvcyhjaGFuZ2UudG8ubGluZSArIHNoaWZ0LCBjaGFuZ2UudG8uY2gpLFxyXG4gICAgICAgICAgICAgICAgdGV4dDogW2xzdChjaGFuZ2UudGV4dCldLCBvcmlnaW46IGNoYW5nZS5vcmlnaW59O1xyXG4gICAgfVxyXG4gICAgdmFyIGxhc3QgPSBkb2MubGFzdExpbmUoKTtcclxuICAgIGlmIChjaGFuZ2UudG8ubGluZSA+IGxhc3QpIHtcclxuICAgICAgY2hhbmdlID0ge2Zyb206IGNoYW5nZS5mcm9tLCB0bzogUG9zKGxhc3QsIGdldExpbmUoZG9jLCBsYXN0KS50ZXh0Lmxlbmd0aCksXHJcbiAgICAgICAgICAgICAgICB0ZXh0OiBbY2hhbmdlLnRleHRbMF1dLCBvcmlnaW46IGNoYW5nZS5vcmlnaW59O1xyXG4gICAgfVxyXG5cclxuICAgIGNoYW5nZS5yZW1vdmVkID0gZ2V0QmV0d2Vlbihkb2MsIGNoYW5nZS5mcm9tLCBjaGFuZ2UudG8pO1xyXG5cclxuICAgIGlmICghc2VsQWZ0ZXIpIHNlbEFmdGVyID0gY29tcHV0ZVNlbEFmdGVyQ2hhbmdlKGRvYywgY2hhbmdlKTtcclxuICAgIGlmIChkb2MuY20pIG1ha2VDaGFuZ2VTaW5nbGVEb2NJbkVkaXRvcihkb2MuY20sIGNoYW5nZSwgc3BhbnMpO1xyXG4gICAgZWxzZSB1cGRhdGVEb2MoZG9jLCBjaGFuZ2UsIHNwYW5zKTtcclxuICAgIHNldFNlbGVjdGlvbk5vVW5kbyhkb2MsIHNlbEFmdGVyLCBzZWxfZG9udFNjcm9sbCk7XHJcbiAgfVxyXG5cclxuICAvLyBIYW5kbGUgdGhlIGludGVyYWN0aW9uIG9mIGEgY2hhbmdlIHRvIGEgZG9jdW1lbnQgd2l0aCB0aGUgZWRpdG9yXHJcbiAgLy8gdGhhdCB0aGlzIGRvY3VtZW50IGlzIHBhcnQgb2YuXHJcbiAgZnVuY3Rpb24gbWFrZUNoYW5nZVNpbmdsZURvY0luRWRpdG9yKGNtLCBjaGFuZ2UsIHNwYW5zKSB7XHJcbiAgICB2YXIgZG9jID0gY20uZG9jLCBkaXNwbGF5ID0gY20uZGlzcGxheSwgZnJvbSA9IGNoYW5nZS5mcm9tLCB0byA9IGNoYW5nZS50bztcclxuXHJcbiAgICB2YXIgcmVjb21wdXRlTWF4TGVuZ3RoID0gZmFsc2UsIGNoZWNrV2lkdGhTdGFydCA9IGZyb20ubGluZTtcclxuICAgIGlmICghY20ub3B0aW9ucy5saW5lV3JhcHBpbmcpIHtcclxuICAgICAgY2hlY2tXaWR0aFN0YXJ0ID0gbGluZU5vKHZpc3VhbExpbmUoZ2V0TGluZShkb2MsIGZyb20ubGluZSkpKTtcclxuICAgICAgZG9jLml0ZXIoY2hlY2tXaWR0aFN0YXJ0LCB0by5saW5lICsgMSwgZnVuY3Rpb24obGluZSkge1xyXG4gICAgICAgIGlmIChsaW5lID09IGRpc3BsYXkubWF4TGluZSkge1xyXG4gICAgICAgICAgcmVjb21wdXRlTWF4TGVuZ3RoID0gdHJ1ZTtcclxuICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgIH1cclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKGRvYy5zZWwuY29udGFpbnMoY2hhbmdlLmZyb20sIGNoYW5nZS50bykgPiAtMSlcclxuICAgICAgc2lnbmFsQ3Vyc29yQWN0aXZpdHkoY20pO1xyXG5cclxuICAgIHVwZGF0ZURvYyhkb2MsIGNoYW5nZSwgc3BhbnMsIGVzdGltYXRlSGVpZ2h0KGNtKSk7XHJcblxyXG4gICAgaWYgKCFjbS5vcHRpb25zLmxpbmVXcmFwcGluZykge1xyXG4gICAgICBkb2MuaXRlcihjaGVja1dpZHRoU3RhcnQsIGZyb20ubGluZSArIGNoYW5nZS50ZXh0Lmxlbmd0aCwgZnVuY3Rpb24obGluZSkge1xyXG4gICAgICAgIHZhciBsZW4gPSBsaW5lTGVuZ3RoKGxpbmUpO1xyXG4gICAgICAgIGlmIChsZW4gPiBkaXNwbGF5Lm1heExpbmVMZW5ndGgpIHtcclxuICAgICAgICAgIGRpc3BsYXkubWF4TGluZSA9IGxpbmU7XHJcbiAgICAgICAgICBkaXNwbGF5Lm1heExpbmVMZW5ndGggPSBsZW47XHJcbiAgICAgICAgICBkaXNwbGF5Lm1heExpbmVDaGFuZ2VkID0gdHJ1ZTtcclxuICAgICAgICAgIHJlY29tcHV0ZU1heExlbmd0aCA9IGZhbHNlO1xyXG4gICAgICAgIH1cclxuICAgICAgfSk7XHJcbiAgICAgIGlmIChyZWNvbXB1dGVNYXhMZW5ndGgpIGNtLmN1ck9wLnVwZGF0ZU1heExpbmUgPSB0cnVlO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEFkanVzdCBmcm9udGllciwgc2NoZWR1bGUgd29ya2VyXHJcbiAgICBkb2MuZnJvbnRpZXIgPSBNYXRoLm1pbihkb2MuZnJvbnRpZXIsIGZyb20ubGluZSk7XHJcbiAgICBzdGFydFdvcmtlcihjbSwgNDAwKTtcclxuXHJcbiAgICB2YXIgbGVuZGlmZiA9IGNoYW5nZS50ZXh0Lmxlbmd0aCAtICh0by5saW5lIC0gZnJvbS5saW5lKSAtIDE7XHJcbiAgICAvLyBSZW1lbWJlciB0aGF0IHRoZXNlIGxpbmVzIGNoYW5nZWQsIGZvciB1cGRhdGluZyB0aGUgZGlzcGxheVxyXG4gICAgaWYgKGNoYW5nZS5mdWxsKVxyXG4gICAgICByZWdDaGFuZ2UoY20pO1xyXG4gICAgZWxzZSBpZiAoZnJvbS5saW5lID09IHRvLmxpbmUgJiYgY2hhbmdlLnRleHQubGVuZ3RoID09IDEgJiYgIWlzV2hvbGVMaW5lVXBkYXRlKGNtLmRvYywgY2hhbmdlKSlcclxuICAgICAgcmVnTGluZUNoYW5nZShjbSwgZnJvbS5saW5lLCBcInRleHRcIik7XHJcbiAgICBlbHNlXHJcbiAgICAgIHJlZ0NoYW5nZShjbSwgZnJvbS5saW5lLCB0by5saW5lICsgMSwgbGVuZGlmZik7XHJcblxyXG4gICAgdmFyIGNoYW5nZXNIYW5kbGVyID0gaGFzSGFuZGxlcihjbSwgXCJjaGFuZ2VzXCIpLCBjaGFuZ2VIYW5kbGVyID0gaGFzSGFuZGxlcihjbSwgXCJjaGFuZ2VcIik7XHJcbiAgICBpZiAoY2hhbmdlSGFuZGxlciB8fCBjaGFuZ2VzSGFuZGxlcikge1xyXG4gICAgICB2YXIgb2JqID0ge1xyXG4gICAgICAgIGZyb206IGZyb20sIHRvOiB0byxcclxuICAgICAgICB0ZXh0OiBjaGFuZ2UudGV4dCxcclxuICAgICAgICByZW1vdmVkOiBjaGFuZ2UucmVtb3ZlZCxcclxuICAgICAgICBvcmlnaW46IGNoYW5nZS5vcmlnaW5cclxuICAgICAgfTtcclxuICAgICAgaWYgKGNoYW5nZUhhbmRsZXIpIHNpZ25hbExhdGVyKGNtLCBcImNoYW5nZVwiLCBjbSwgb2JqKTtcclxuICAgICAgaWYgKGNoYW5nZXNIYW5kbGVyKSAoY20uY3VyT3AuY2hhbmdlT2JqcyB8fCAoY20uY3VyT3AuY2hhbmdlT2JqcyA9IFtdKSkucHVzaChvYmopO1xyXG4gICAgfVxyXG4gICAgY20uZGlzcGxheS5zZWxGb3JDb250ZXh0TWVudSA9IG51bGw7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiByZXBsYWNlUmFuZ2UoZG9jLCBjb2RlLCBmcm9tLCB0bywgb3JpZ2luKSB7XHJcbiAgICBpZiAoIXRvKSB0byA9IGZyb207XHJcbiAgICBpZiAoY21wKHRvLCBmcm9tKSA8IDApIHsgdmFyIHRtcCA9IHRvOyB0byA9IGZyb207IGZyb20gPSB0bXA7IH1cclxuICAgIGlmICh0eXBlb2YgY29kZSA9PSBcInN0cmluZ1wiKSBjb2RlID0gZG9jLnNwbGl0TGluZXMoY29kZSk7XHJcbiAgICBtYWtlQ2hhbmdlKGRvYywge2Zyb206IGZyb20sIHRvOiB0bywgdGV4dDogY29kZSwgb3JpZ2luOiBvcmlnaW59KTtcclxuICB9XHJcblxyXG4gIC8vIFNDUk9MTElORyBUSElOR1MgSU5UTyBWSUVXXHJcblxyXG4gIC8vIElmIGFuIGVkaXRvciBzaXRzIG9uIHRoZSB0b3Agb3IgYm90dG9tIG9mIHRoZSB3aW5kb3csIHBhcnRpYWxseVxyXG4gIC8vIHNjcm9sbGVkIG91dCBvZiB2aWV3LCB0aGlzIGVuc3VyZXMgdGhhdCB0aGUgY3Vyc29yIGlzIHZpc2libGUuXHJcbiAgZnVuY3Rpb24gbWF5YmVTY3JvbGxXaW5kb3coY20sIGNvb3Jkcykge1xyXG4gICAgaWYgKHNpZ25hbERPTUV2ZW50KGNtLCBcInNjcm9sbEN1cnNvckludG9WaWV3XCIpKSByZXR1cm47XHJcblxyXG4gICAgdmFyIGRpc3BsYXkgPSBjbS5kaXNwbGF5LCBib3ggPSBkaXNwbGF5LnNpemVyLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLCBkb1Njcm9sbCA9IG51bGw7XHJcbiAgICBpZiAoY29vcmRzLnRvcCArIGJveC50b3AgPCAwKSBkb1Njcm9sbCA9IHRydWU7XHJcbiAgICBlbHNlIGlmIChjb29yZHMuYm90dG9tICsgYm94LnRvcCA+ICh3aW5kb3cuaW5uZXJIZWlnaHQgfHwgZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LmNsaWVudEhlaWdodCkpIGRvU2Nyb2xsID0gZmFsc2U7XHJcbiAgICBpZiAoZG9TY3JvbGwgIT0gbnVsbCAmJiAhcGhhbnRvbSkge1xyXG4gICAgICB2YXIgc2Nyb2xsTm9kZSA9IGVsdChcImRpdlwiLCBcIlxcdTIwMGJcIiwgbnVsbCwgXCJwb3NpdGlvbjogYWJzb2x1dGU7IHRvcDogXCIgK1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAoY29vcmRzLnRvcCAtIGRpc3BsYXkudmlld09mZnNldCAtIHBhZGRpbmdUb3AoY20uZGlzcGxheSkpICsgXCJweDsgaGVpZ2h0OiBcIiArXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgIChjb29yZHMuYm90dG9tIC0gY29vcmRzLnRvcCArIHNjcm9sbEdhcChjbSkgKyBkaXNwbGF5LmJhckhlaWdodCkgKyBcInB4OyBsZWZ0OiBcIiArXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvb3Jkcy5sZWZ0ICsgXCJweDsgd2lkdGg6IDJweDtcIik7XHJcbiAgICAgIGNtLmRpc3BsYXkubGluZVNwYWNlLmFwcGVuZENoaWxkKHNjcm9sbE5vZGUpO1xyXG4gICAgICBzY3JvbGxOb2RlLnNjcm9sbEludG9WaWV3KGRvU2Nyb2xsKTtcclxuICAgICAgY20uZGlzcGxheS5saW5lU3BhY2UucmVtb3ZlQ2hpbGQoc2Nyb2xsTm9kZSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBTY3JvbGwgYSBnaXZlbiBwb3NpdGlvbiBpbnRvIHZpZXcgKGltbWVkaWF0ZWx5KSwgdmVyaWZ5aW5nIHRoYXRcclxuICAvLyBpdCBhY3R1YWxseSBiZWNhbWUgdmlzaWJsZSAoYXMgbGluZSBoZWlnaHRzIGFyZSBhY2N1cmF0ZWx5XHJcbiAgLy8gbWVhc3VyZWQsIHRoZSBwb3NpdGlvbiBvZiBzb21ldGhpbmcgbWF5ICdkcmlmdCcgZHVyaW5nIGRyYXdpbmcpLlxyXG4gIGZ1bmN0aW9uIHNjcm9sbFBvc0ludG9WaWV3KGNtLCBwb3MsIGVuZCwgbWFyZ2luKSB7XHJcbiAgICBpZiAobWFyZ2luID09IG51bGwpIG1hcmdpbiA9IDA7XHJcbiAgICBmb3IgKHZhciBsaW1pdCA9IDA7IGxpbWl0IDwgNTsgbGltaXQrKykge1xyXG4gICAgICB2YXIgY2hhbmdlZCA9IGZhbHNlLCBjb29yZHMgPSBjdXJzb3JDb29yZHMoY20sIHBvcyk7XHJcbiAgICAgIHZhciBlbmRDb29yZHMgPSAhZW5kIHx8IGVuZCA9PSBwb3MgPyBjb29yZHMgOiBjdXJzb3JDb29yZHMoY20sIGVuZCk7XHJcbiAgICAgIHZhciBzY3JvbGxQb3MgPSBjYWxjdWxhdGVTY3JvbGxQb3MoY20sIE1hdGgubWluKGNvb3Jkcy5sZWZ0LCBlbmRDb29yZHMubGVmdCksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTWF0aC5taW4oY29vcmRzLnRvcCwgZW5kQ29vcmRzLnRvcCkgLSBtYXJnaW4sXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTWF0aC5tYXgoY29vcmRzLmxlZnQsIGVuZENvb3Jkcy5sZWZ0KSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNYXRoLm1heChjb29yZHMuYm90dG9tLCBlbmRDb29yZHMuYm90dG9tKSArIG1hcmdpbik7XHJcbiAgICAgIHZhciBzdGFydFRvcCA9IGNtLmRvYy5zY3JvbGxUb3AsIHN0YXJ0TGVmdCA9IGNtLmRvYy5zY3JvbGxMZWZ0O1xyXG4gICAgICBpZiAoc2Nyb2xsUG9zLnNjcm9sbFRvcCAhPSBudWxsKSB7XHJcbiAgICAgICAgc2V0U2Nyb2xsVG9wKGNtLCBzY3JvbGxQb3Muc2Nyb2xsVG9wKTtcclxuICAgICAgICBpZiAoTWF0aC5hYnMoY20uZG9jLnNjcm9sbFRvcCAtIHN0YXJ0VG9wKSA+IDEpIGNoYW5nZWQgPSB0cnVlO1xyXG4gICAgICB9XHJcbiAgICAgIGlmIChzY3JvbGxQb3Muc2Nyb2xsTGVmdCAhPSBudWxsKSB7XHJcbiAgICAgICAgc2V0U2Nyb2xsTGVmdChjbSwgc2Nyb2xsUG9zLnNjcm9sbExlZnQpO1xyXG4gICAgICAgIGlmIChNYXRoLmFicyhjbS5kb2Muc2Nyb2xsTGVmdCAtIHN0YXJ0TGVmdCkgPiAxKSBjaGFuZ2VkID0gdHJ1ZTtcclxuICAgICAgfVxyXG4gICAgICBpZiAoIWNoYW5nZWQpIGJyZWFrO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIGNvb3JkcztcclxuICB9XHJcblxyXG4gIC8vIFNjcm9sbCBhIGdpdmVuIHNldCBvZiBjb29yZGluYXRlcyBpbnRvIHZpZXcgKGltbWVkaWF0ZWx5KS5cclxuICBmdW5jdGlvbiBzY3JvbGxJbnRvVmlldyhjbSwgeDEsIHkxLCB4MiwgeTIpIHtcclxuICAgIHZhciBzY3JvbGxQb3MgPSBjYWxjdWxhdGVTY3JvbGxQb3MoY20sIHgxLCB5MSwgeDIsIHkyKTtcclxuICAgIGlmIChzY3JvbGxQb3Muc2Nyb2xsVG9wICE9IG51bGwpIHNldFNjcm9sbFRvcChjbSwgc2Nyb2xsUG9zLnNjcm9sbFRvcCk7XHJcbiAgICBpZiAoc2Nyb2xsUG9zLnNjcm9sbExlZnQgIT0gbnVsbCkgc2V0U2Nyb2xsTGVmdChjbSwgc2Nyb2xsUG9zLnNjcm9sbExlZnQpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2FsY3VsYXRlIGEgbmV3IHNjcm9sbCBwb3NpdGlvbiBuZWVkZWQgdG8gc2Nyb2xsIHRoZSBnaXZlblxyXG4gIC8vIHJlY3RhbmdsZSBpbnRvIHZpZXcuIFJldHVybnMgYW4gb2JqZWN0IHdpdGggc2Nyb2xsVG9wIGFuZFxyXG4gIC8vIHNjcm9sbExlZnQgcHJvcGVydGllcy4gV2hlbiB0aGVzZSBhcmUgdW5kZWZpbmVkLCB0aGVcclxuICAvLyB2ZXJ0aWNhbC9ob3Jpem9udGFsIHBvc2l0aW9uIGRvZXMgbm90IG5lZWQgdG8gYmUgYWRqdXN0ZWQuXHJcbiAgZnVuY3Rpb24gY2FsY3VsYXRlU2Nyb2xsUG9zKGNtLCB4MSwgeTEsIHgyLCB5Mikge1xyXG4gICAgdmFyIGRpc3BsYXkgPSBjbS5kaXNwbGF5LCBzbmFwTWFyZ2luID0gdGV4dEhlaWdodChjbS5kaXNwbGF5KTtcclxuICAgIGlmICh5MSA8IDApIHkxID0gMDtcclxuICAgIHZhciBzY3JlZW50b3AgPSBjbS5jdXJPcCAmJiBjbS5jdXJPcC5zY3JvbGxUb3AgIT0gbnVsbCA/IGNtLmN1ck9wLnNjcm9sbFRvcCA6IGRpc3BsYXkuc2Nyb2xsZXIuc2Nyb2xsVG9wO1xyXG4gICAgdmFyIHNjcmVlbiA9IGRpc3BsYXlIZWlnaHQoY20pLCByZXN1bHQgPSB7fTtcclxuICAgIGlmICh5MiAtIHkxID4gc2NyZWVuKSB5MiA9IHkxICsgc2NyZWVuO1xyXG4gICAgdmFyIGRvY0JvdHRvbSA9IGNtLmRvYy5oZWlnaHQgKyBwYWRkaW5nVmVydChkaXNwbGF5KTtcclxuICAgIHZhciBhdFRvcCA9IHkxIDwgc25hcE1hcmdpbiwgYXRCb3R0b20gPSB5MiA+IGRvY0JvdHRvbSAtIHNuYXBNYXJnaW47XHJcbiAgICBpZiAoeTEgPCBzY3JlZW50b3ApIHtcclxuICAgICAgcmVzdWx0LnNjcm9sbFRvcCA9IGF0VG9wID8gMCA6IHkxO1xyXG4gICAgfSBlbHNlIGlmICh5MiA+IHNjcmVlbnRvcCArIHNjcmVlbikge1xyXG4gICAgICB2YXIgbmV3VG9wID0gTWF0aC5taW4oeTEsIChhdEJvdHRvbSA/IGRvY0JvdHRvbSA6IHkyKSAtIHNjcmVlbik7XHJcbiAgICAgIGlmIChuZXdUb3AgIT0gc2NyZWVudG9wKSByZXN1bHQuc2Nyb2xsVG9wID0gbmV3VG9wO1xyXG4gICAgfVxyXG5cclxuICAgIHZhciBzY3JlZW5sZWZ0ID0gY20uY3VyT3AgJiYgY20uY3VyT3Auc2Nyb2xsTGVmdCAhPSBudWxsID8gY20uY3VyT3Auc2Nyb2xsTGVmdCA6IGRpc3BsYXkuc2Nyb2xsZXIuc2Nyb2xsTGVmdDtcclxuICAgIHZhciBzY3JlZW53ID0gZGlzcGxheVdpZHRoKGNtKSAtIChjbS5vcHRpb25zLmZpeGVkR3V0dGVyID8gZGlzcGxheS5ndXR0ZXJzLm9mZnNldFdpZHRoIDogMCk7XHJcbiAgICB2YXIgdG9vV2lkZSA9IHgyIC0geDEgPiBzY3JlZW53O1xyXG4gICAgaWYgKHRvb1dpZGUpIHgyID0geDEgKyBzY3JlZW53O1xyXG4gICAgaWYgKHgxIDwgMTApXHJcbiAgICAgIHJlc3VsdC5zY3JvbGxMZWZ0ID0gMDtcclxuICAgIGVsc2UgaWYgKHgxIDwgc2NyZWVubGVmdClcclxuICAgICAgcmVzdWx0LnNjcm9sbExlZnQgPSBNYXRoLm1heCgwLCB4MSAtICh0b29XaWRlID8gMCA6IDEwKSk7XHJcbiAgICBlbHNlIGlmICh4MiA+IHNjcmVlbncgKyBzY3JlZW5sZWZ0IC0gMylcclxuICAgICAgcmVzdWx0LnNjcm9sbExlZnQgPSB4MiArICh0b29XaWRlID8gMCA6IDEwKSAtIHNjcmVlbnc7XHJcbiAgICByZXR1cm4gcmVzdWx0O1xyXG4gIH1cclxuXHJcbiAgLy8gU3RvcmUgYSByZWxhdGl2ZSBhZGp1c3RtZW50IHRvIHRoZSBzY3JvbGwgcG9zaXRpb24gaW4gdGhlIGN1cnJlbnRcclxuICAvLyBvcGVyYXRpb24gKHRvIGJlIGFwcGxpZWQgd2hlbiB0aGUgb3BlcmF0aW9uIGZpbmlzaGVzKS5cclxuICBmdW5jdGlvbiBhZGRUb1Njcm9sbFBvcyhjbSwgbGVmdCwgdG9wKSB7XHJcbiAgICBpZiAobGVmdCAhPSBudWxsIHx8IHRvcCAhPSBudWxsKSByZXNvbHZlU2Nyb2xsVG9Qb3MoY20pO1xyXG4gICAgaWYgKGxlZnQgIT0gbnVsbClcclxuICAgICAgY20uY3VyT3Auc2Nyb2xsTGVmdCA9IChjbS5jdXJPcC5zY3JvbGxMZWZ0ID09IG51bGwgPyBjbS5kb2Muc2Nyb2xsTGVmdCA6IGNtLmN1ck9wLnNjcm9sbExlZnQpICsgbGVmdDtcclxuICAgIGlmICh0b3AgIT0gbnVsbClcclxuICAgICAgY20uY3VyT3Auc2Nyb2xsVG9wID0gKGNtLmN1ck9wLnNjcm9sbFRvcCA9PSBudWxsID8gY20uZG9jLnNjcm9sbFRvcCA6IGNtLmN1ck9wLnNjcm9sbFRvcCkgKyB0b3A7XHJcbiAgfVxyXG5cclxuICAvLyBNYWtlIHN1cmUgdGhhdCBhdCB0aGUgZW5kIG9mIHRoZSBvcGVyYXRpb24gdGhlIGN1cnJlbnQgY3Vyc29yIGlzXHJcbiAgLy8gc2hvd24uXHJcbiAgZnVuY3Rpb24gZW5zdXJlQ3Vyc29yVmlzaWJsZShjbSkge1xyXG4gICAgcmVzb2x2ZVNjcm9sbFRvUG9zKGNtKTtcclxuICAgIHZhciBjdXIgPSBjbS5nZXRDdXJzb3IoKSwgZnJvbSA9IGN1ciwgdG8gPSBjdXI7XHJcbiAgICBpZiAoIWNtLm9wdGlvbnMubGluZVdyYXBwaW5nKSB7XHJcbiAgICAgIGZyb20gPSBjdXIuY2ggPyBQb3MoY3VyLmxpbmUsIGN1ci5jaCAtIDEpIDogY3VyO1xyXG4gICAgICB0byA9IFBvcyhjdXIubGluZSwgY3VyLmNoICsgMSk7XHJcbiAgICB9XHJcbiAgICBjbS5jdXJPcC5zY3JvbGxUb1BvcyA9IHtmcm9tOiBmcm9tLCB0bzogdG8sIG1hcmdpbjogY20ub3B0aW9ucy5jdXJzb3JTY3JvbGxNYXJnaW4sIGlzQ3Vyc29yOiB0cnVlfTtcclxuICB9XHJcblxyXG4gIC8vIFdoZW4gYW4gb3BlcmF0aW9uIGhhcyBpdHMgc2Nyb2xsVG9Qb3MgcHJvcGVydHkgc2V0LCBhbmQgYW5vdGhlclxyXG4gIC8vIHNjcm9sbCBhY3Rpb24gaXMgYXBwbGllZCBiZWZvcmUgdGhlIGVuZCBvZiB0aGUgb3BlcmF0aW9uLCB0aGlzXHJcbiAgLy8gJ3NpbXVsYXRlcycgc2Nyb2xsaW5nIHRoYXQgcG9zaXRpb24gaW50byB2aWV3IGluIGEgY2hlYXAgd2F5LCBzb1xyXG4gIC8vIHRoYXQgdGhlIGVmZmVjdCBvZiBpbnRlcm1lZGlhdGUgc2Nyb2xsIGNvbW1hbmRzIGlzIG5vdCBpZ25vcmVkLlxyXG4gIGZ1bmN0aW9uIHJlc29sdmVTY3JvbGxUb1BvcyhjbSkge1xyXG4gICAgdmFyIHJhbmdlID0gY20uY3VyT3Auc2Nyb2xsVG9Qb3M7XHJcbiAgICBpZiAocmFuZ2UpIHtcclxuICAgICAgY20uY3VyT3Auc2Nyb2xsVG9Qb3MgPSBudWxsO1xyXG4gICAgICB2YXIgZnJvbSA9IGVzdGltYXRlQ29vcmRzKGNtLCByYW5nZS5mcm9tKSwgdG8gPSBlc3RpbWF0ZUNvb3JkcyhjbSwgcmFuZ2UudG8pO1xyXG4gICAgICB2YXIgc1BvcyA9IGNhbGN1bGF0ZVNjcm9sbFBvcyhjbSwgTWF0aC5taW4oZnJvbS5sZWZ0LCB0by5sZWZ0KSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTWF0aC5taW4oZnJvbS50b3AsIHRvLnRvcCkgLSByYW5nZS5tYXJnaW4sXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE1hdGgubWF4KGZyb20ucmlnaHQsIHRvLnJpZ2h0KSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTWF0aC5tYXgoZnJvbS5ib3R0b20sIHRvLmJvdHRvbSkgKyByYW5nZS5tYXJnaW4pO1xyXG4gICAgICBjbS5zY3JvbGxUbyhzUG9zLnNjcm9sbExlZnQsIHNQb3Muc2Nyb2xsVG9wKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIEFQSSBVVElMSVRJRVNcclxuXHJcbiAgLy8gSW5kZW50IHRoZSBnaXZlbiBsaW5lLiBUaGUgaG93IHBhcmFtZXRlciBjYW4gYmUgXCJzbWFydFwiLFxyXG4gIC8vIFwiYWRkXCIvbnVsbCwgXCJzdWJ0cmFjdFwiLCBvciBcInByZXZcIi4gV2hlbiBhZ2dyZXNzaXZlIGlzIGZhbHNlXHJcbiAgLy8gKHR5cGljYWxseSBzZXQgdG8gdHJ1ZSBmb3IgZm9yY2VkIHNpbmdsZS1saW5lIGluZGVudHMpLCBlbXB0eVxyXG4gIC8vIGxpbmVzIGFyZSBub3QgaW5kZW50ZWQsIGFuZCBwbGFjZXMgd2hlcmUgdGhlIG1vZGUgcmV0dXJucyBQYXNzXHJcbiAgLy8gYXJlIGxlZnQgYWxvbmUuXHJcbiAgZnVuY3Rpb24gaW5kZW50TGluZShjbSwgbiwgaG93LCBhZ2dyZXNzaXZlKSB7XHJcbiAgICB2YXIgZG9jID0gY20uZG9jLCBzdGF0ZTtcclxuICAgIGlmIChob3cgPT0gbnVsbCkgaG93ID0gXCJhZGRcIjtcclxuICAgIGlmIChob3cgPT0gXCJzbWFydFwiKSB7XHJcbiAgICAgIC8vIEZhbGwgYmFjayB0byBcInByZXZcIiB3aGVuIHRoZSBtb2RlIGRvZXNuJ3QgaGF2ZSBhbiBpbmRlbnRhdGlvblxyXG4gICAgICAvLyBtZXRob2QuXHJcbiAgICAgIGlmICghZG9jLm1vZGUuaW5kZW50KSBob3cgPSBcInByZXZcIjtcclxuICAgICAgZWxzZSBzdGF0ZSA9IGdldFN0YXRlQmVmb3JlKGNtLCBuKTtcclxuICAgIH1cclxuXHJcbiAgICB2YXIgdGFiU2l6ZSA9IGNtLm9wdGlvbnMudGFiU2l6ZTtcclxuICAgIHZhciBsaW5lID0gZ2V0TGluZShkb2MsIG4pLCBjdXJTcGFjZSA9IGNvdW50Q29sdW1uKGxpbmUudGV4dCwgbnVsbCwgdGFiU2l6ZSk7XHJcbiAgICBpZiAobGluZS5zdGF0ZUFmdGVyKSBsaW5lLnN0YXRlQWZ0ZXIgPSBudWxsO1xyXG4gICAgdmFyIGN1clNwYWNlU3RyaW5nID0gbGluZS50ZXh0Lm1hdGNoKC9eXFxzKi8pWzBdLCBpbmRlbnRhdGlvbjtcclxuICAgIGlmICghYWdncmVzc2l2ZSAmJiAhL1xcUy8udGVzdChsaW5lLnRleHQpKSB7XHJcbiAgICAgIGluZGVudGF0aW9uID0gMDtcclxuICAgICAgaG93ID0gXCJub3RcIjtcclxuICAgIH0gZWxzZSBpZiAoaG93ID09IFwic21hcnRcIikge1xyXG4gICAgICBpbmRlbnRhdGlvbiA9IGRvYy5tb2RlLmluZGVudChzdGF0ZSwgbGluZS50ZXh0LnNsaWNlKGN1clNwYWNlU3RyaW5nLmxlbmd0aCksIGxpbmUudGV4dCk7XHJcbiAgICAgIGlmIChpbmRlbnRhdGlvbiA9PSBQYXNzIHx8IGluZGVudGF0aW9uID4gMTUwKSB7XHJcbiAgICAgICAgaWYgKCFhZ2dyZXNzaXZlKSByZXR1cm47XHJcbiAgICAgICAgaG93ID0gXCJwcmV2XCI7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIGlmIChob3cgPT0gXCJwcmV2XCIpIHtcclxuICAgICAgaWYgKG4gPiBkb2MuZmlyc3QpIGluZGVudGF0aW9uID0gY291bnRDb2x1bW4oZ2V0TGluZShkb2MsIG4tMSkudGV4dCwgbnVsbCwgdGFiU2l6ZSk7XHJcbiAgICAgIGVsc2UgaW5kZW50YXRpb24gPSAwO1xyXG4gICAgfSBlbHNlIGlmIChob3cgPT0gXCJhZGRcIikge1xyXG4gICAgICBpbmRlbnRhdGlvbiA9IGN1clNwYWNlICsgY20ub3B0aW9ucy5pbmRlbnRVbml0O1xyXG4gICAgfSBlbHNlIGlmIChob3cgPT0gXCJzdWJ0cmFjdFwiKSB7XHJcbiAgICAgIGluZGVudGF0aW9uID0gY3VyU3BhY2UgLSBjbS5vcHRpb25zLmluZGVudFVuaXQ7XHJcbiAgICB9IGVsc2UgaWYgKHR5cGVvZiBob3cgPT0gXCJudW1iZXJcIikge1xyXG4gICAgICBpbmRlbnRhdGlvbiA9IGN1clNwYWNlICsgaG93O1xyXG4gICAgfVxyXG4gICAgaW5kZW50YXRpb24gPSBNYXRoLm1heCgwLCBpbmRlbnRhdGlvbik7XHJcblxyXG4gICAgdmFyIGluZGVudFN0cmluZyA9IFwiXCIsIHBvcyA9IDA7XHJcbiAgICBpZiAoY20ub3B0aW9ucy5pbmRlbnRXaXRoVGFicylcclxuICAgICAgZm9yICh2YXIgaSA9IE1hdGguZmxvb3IoaW5kZW50YXRpb24gLyB0YWJTaXplKTsgaTsgLS1pKSB7cG9zICs9IHRhYlNpemU7IGluZGVudFN0cmluZyArPSBcIlxcdFwiO31cclxuICAgIGlmIChwb3MgPCBpbmRlbnRhdGlvbikgaW5kZW50U3RyaW5nICs9IHNwYWNlU3RyKGluZGVudGF0aW9uIC0gcG9zKTtcclxuXHJcbiAgICBpZiAoaW5kZW50U3RyaW5nICE9IGN1clNwYWNlU3RyaW5nKSB7XHJcbiAgICAgIHJlcGxhY2VSYW5nZShkb2MsIGluZGVudFN0cmluZywgUG9zKG4sIDApLCBQb3MobiwgY3VyU3BhY2VTdHJpbmcubGVuZ3RoKSwgXCIraW5wdXRcIik7XHJcbiAgICAgIGxpbmUuc3RhdGVBZnRlciA9IG51bGw7XHJcbiAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gRW5zdXJlIHRoYXQsIGlmIHRoZSBjdXJzb3Igd2FzIGluIHRoZSB3aGl0ZXNwYWNlIGF0IHRoZSBzdGFydFxyXG4gICAgICAvLyBvZiB0aGUgbGluZSwgaXQgaXMgbW92ZWQgdG8gdGhlIGVuZCBvZiB0aGF0IHNwYWNlLlxyXG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGRvYy5zZWwucmFuZ2VzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgdmFyIHJhbmdlID0gZG9jLnNlbC5yYW5nZXNbaV07XHJcbiAgICAgICAgaWYgKHJhbmdlLmhlYWQubGluZSA9PSBuICYmIHJhbmdlLmhlYWQuY2ggPCBjdXJTcGFjZVN0cmluZy5sZW5ndGgpIHtcclxuICAgICAgICAgIHZhciBwb3MgPSBQb3MobiwgY3VyU3BhY2VTdHJpbmcubGVuZ3RoKTtcclxuICAgICAgICAgIHJlcGxhY2VPbmVTZWxlY3Rpb24oZG9jLCBpLCBuZXcgUmFuZ2UocG9zLCBwb3MpKTtcclxuICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gVXRpbGl0eSBmb3IgYXBwbHlpbmcgYSBjaGFuZ2UgdG8gYSBsaW5lIGJ5IGhhbmRsZSBvciBudW1iZXIsXHJcbiAgLy8gcmV0dXJuaW5nIHRoZSBudW1iZXIgYW5kIG9wdGlvbmFsbHkgcmVnaXN0ZXJpbmcgdGhlIGxpbmUgYXNcclxuICAvLyBjaGFuZ2VkLlxyXG4gIGZ1bmN0aW9uIGNoYW5nZUxpbmUoZG9jLCBoYW5kbGUsIGNoYW5nZVR5cGUsIG9wKSB7XHJcbiAgICB2YXIgbm8gPSBoYW5kbGUsIGxpbmUgPSBoYW5kbGU7XHJcbiAgICBpZiAodHlwZW9mIGhhbmRsZSA9PSBcIm51bWJlclwiKSBsaW5lID0gZ2V0TGluZShkb2MsIGNsaXBMaW5lKGRvYywgaGFuZGxlKSk7XHJcbiAgICBlbHNlIG5vID0gbGluZU5vKGhhbmRsZSk7XHJcbiAgICBpZiAobm8gPT0gbnVsbCkgcmV0dXJuIG51bGw7XHJcbiAgICBpZiAob3AobGluZSwgbm8pICYmIGRvYy5jbSkgcmVnTGluZUNoYW5nZShkb2MuY20sIG5vLCBjaGFuZ2VUeXBlKTtcclxuICAgIHJldHVybiBsaW5lO1xyXG4gIH1cclxuXHJcbiAgLy8gSGVscGVyIGZvciBkZWxldGluZyB0ZXh0IG5lYXIgdGhlIHNlbGVjdGlvbihzKSwgdXNlZCB0byBpbXBsZW1lbnRcclxuICAvLyBiYWNrc3BhY2UsIGRlbGV0ZSwgYW5kIHNpbWlsYXIgZnVuY3Rpb25hbGl0eS5cclxuICBmdW5jdGlvbiBkZWxldGVOZWFyU2VsZWN0aW9uKGNtLCBjb21wdXRlKSB7XHJcbiAgICB2YXIgcmFuZ2VzID0gY20uZG9jLnNlbC5yYW5nZXMsIGtpbGwgPSBbXTtcclxuICAgIC8vIEJ1aWxkIHVwIGEgc2V0IG9mIHJhbmdlcyB0byBraWxsIGZpcnN0LCBtZXJnaW5nIG92ZXJsYXBwaW5nXHJcbiAgICAvLyByYW5nZXMuXHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IHJhbmdlcy5sZW5ndGg7IGkrKykge1xyXG4gICAgICB2YXIgdG9LaWxsID0gY29tcHV0ZShyYW5nZXNbaV0pO1xyXG4gICAgICB3aGlsZSAoa2lsbC5sZW5ndGggJiYgY21wKHRvS2lsbC5mcm9tLCBsc3Qoa2lsbCkudG8pIDw9IDApIHtcclxuICAgICAgICB2YXIgcmVwbGFjZWQgPSBraWxsLnBvcCgpO1xyXG4gICAgICAgIGlmIChjbXAocmVwbGFjZWQuZnJvbSwgdG9LaWxsLmZyb20pIDwgMCkge1xyXG4gICAgICAgICAgdG9LaWxsLmZyb20gPSByZXBsYWNlZC5mcm9tO1xyXG4gICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICAgIGtpbGwucHVzaCh0b0tpbGwpO1xyXG4gICAgfVxyXG4gICAgLy8gTmV4dCwgcmVtb3ZlIHRob3NlIGFjdHVhbCByYW5nZXMuXHJcbiAgICBydW5Jbk9wKGNtLCBmdW5jdGlvbigpIHtcclxuICAgICAgZm9yICh2YXIgaSA9IGtpbGwubGVuZ3RoIC0gMTsgaSA+PSAwOyBpLS0pXHJcbiAgICAgICAgcmVwbGFjZVJhbmdlKGNtLmRvYywgXCJcIiwga2lsbFtpXS5mcm9tLCBraWxsW2ldLnRvLCBcIitkZWxldGVcIik7XHJcbiAgICAgIGVuc3VyZUN1cnNvclZpc2libGUoY20pO1xyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuICAvLyBVc2VkIGZvciBob3Jpem9udGFsIHJlbGF0aXZlIG1vdGlvbi4gRGlyIGlzIC0xIG9yIDEgKGxlZnQgb3JcclxuICAvLyByaWdodCksIHVuaXQgY2FuIGJlIFwiY2hhclwiLCBcImNvbHVtblwiIChsaWtlIGNoYXIsIGJ1dCBkb2Vzbid0XHJcbiAgLy8gY3Jvc3MgbGluZSBib3VuZGFyaWVzKSwgXCJ3b3JkXCIgKGFjcm9zcyBuZXh0IHdvcmQpLCBvciBcImdyb3VwXCIgKHRvXHJcbiAgLy8gdGhlIHN0YXJ0IG9mIG5leHQgZ3JvdXAgb2Ygd29yZCBvciBub24td29yZC1ub24td2hpdGVzcGFjZVxyXG4gIC8vIGNoYXJzKS4gVGhlIHZpc3VhbGx5IHBhcmFtIGNvbnRyb2xzIHdoZXRoZXIsIGluIHJpZ2h0LXRvLWxlZnRcclxuICAvLyB0ZXh0LCBkaXJlY3Rpb24gMSBtZWFucyB0byBtb3ZlIHRvd2FyZHMgdGhlIG5leHQgaW5kZXggaW4gdGhlXHJcbiAgLy8gc3RyaW5nLCBvciB0b3dhcmRzIHRoZSBjaGFyYWN0ZXIgdG8gdGhlIHJpZ2h0IG9mIHRoZSBjdXJyZW50XHJcbiAgLy8gcG9zaXRpb24uIFRoZSByZXN1bHRpbmcgcG9zaXRpb24gd2lsbCBoYXZlIGEgaGl0U2lkZT10cnVlXHJcbiAgLy8gcHJvcGVydHkgaWYgaXQgcmVhY2hlZCB0aGUgZW5kIG9mIHRoZSBkb2N1bWVudC5cclxuICBmdW5jdGlvbiBmaW5kUG9zSChkb2MsIHBvcywgZGlyLCB1bml0LCB2aXN1YWxseSkge1xyXG4gICAgdmFyIGxpbmUgPSBwb3MubGluZSwgY2ggPSBwb3MuY2gsIG9yaWdEaXIgPSBkaXI7XHJcbiAgICB2YXIgbGluZU9iaiA9IGdldExpbmUoZG9jLCBsaW5lKTtcclxuICAgIHZhciBwb3NzaWJsZSA9IHRydWU7XHJcbiAgICBmdW5jdGlvbiBmaW5kTmV4dExpbmUoKSB7XHJcbiAgICAgIHZhciBsID0gbGluZSArIGRpcjtcclxuICAgICAgaWYgKGwgPCBkb2MuZmlyc3QgfHwgbCA+PSBkb2MuZmlyc3QgKyBkb2Muc2l6ZSkgcmV0dXJuIChwb3NzaWJsZSA9IGZhbHNlKTtcclxuICAgICAgbGluZSA9IGw7XHJcbiAgICAgIHJldHVybiBsaW5lT2JqID0gZ2V0TGluZShkb2MsIGwpO1xyXG4gICAgfVxyXG4gICAgZnVuY3Rpb24gbW92ZU9uY2UoYm91bmRUb0xpbmUpIHtcclxuICAgICAgdmFyIG5leHQgPSAodmlzdWFsbHkgPyBtb3ZlVmlzdWFsbHkgOiBtb3ZlTG9naWNhbGx5KShsaW5lT2JqLCBjaCwgZGlyLCB0cnVlKTtcclxuICAgICAgaWYgKG5leHQgPT0gbnVsbCkge1xyXG4gICAgICAgIGlmICghYm91bmRUb0xpbmUgJiYgZmluZE5leHRMaW5lKCkpIHtcclxuICAgICAgICAgIGlmICh2aXN1YWxseSkgY2ggPSAoZGlyIDwgMCA/IGxpbmVSaWdodCA6IGxpbmVMZWZ0KShsaW5lT2JqKTtcclxuICAgICAgICAgIGVsc2UgY2ggPSBkaXIgPCAwID8gbGluZU9iai50ZXh0Lmxlbmd0aCA6IDA7XHJcbiAgICAgICAgfSBlbHNlIHJldHVybiAocG9zc2libGUgPSBmYWxzZSk7XHJcbiAgICAgIH0gZWxzZSBjaCA9IG5leHQ7XHJcbiAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfVxyXG5cclxuICAgIGlmICh1bml0ID09IFwiY2hhclwiKSBtb3ZlT25jZSgpO1xyXG4gICAgZWxzZSBpZiAodW5pdCA9PSBcImNvbHVtblwiKSBtb3ZlT25jZSh0cnVlKTtcclxuICAgIGVsc2UgaWYgKHVuaXQgPT0gXCJ3b3JkXCIgfHwgdW5pdCA9PSBcImdyb3VwXCIpIHtcclxuICAgICAgdmFyIHNhd1R5cGUgPSBudWxsLCBncm91cCA9IHVuaXQgPT0gXCJncm91cFwiO1xyXG4gICAgICB2YXIgaGVscGVyID0gZG9jLmNtICYmIGRvYy5jbS5nZXRIZWxwZXIocG9zLCBcIndvcmRDaGFyc1wiKTtcclxuICAgICAgZm9yICh2YXIgZmlyc3QgPSB0cnVlOzsgZmlyc3QgPSBmYWxzZSkge1xyXG4gICAgICAgIGlmIChkaXIgPCAwICYmICFtb3ZlT25jZSghZmlyc3QpKSBicmVhaztcclxuICAgICAgICB2YXIgY3VyID0gbGluZU9iai50ZXh0LmNoYXJBdChjaCkgfHwgXCJcXG5cIjtcclxuICAgICAgICB2YXIgdHlwZSA9IGlzV29yZENoYXIoY3VyLCBoZWxwZXIpID8gXCJ3XCJcclxuICAgICAgICAgIDogZ3JvdXAgJiYgY3VyID09IFwiXFxuXCIgPyBcIm5cIlxyXG4gICAgICAgICAgOiAhZ3JvdXAgfHwgL1xccy8udGVzdChjdXIpID8gbnVsbFxyXG4gICAgICAgICAgOiBcInBcIjtcclxuICAgICAgICBpZiAoZ3JvdXAgJiYgIWZpcnN0ICYmICF0eXBlKSB0eXBlID0gXCJzXCI7XHJcbiAgICAgICAgaWYgKHNhd1R5cGUgJiYgc2F3VHlwZSAhPSB0eXBlKSB7XHJcbiAgICAgICAgICBpZiAoZGlyIDwgMCkge2RpciA9IDE7IG1vdmVPbmNlKCk7fVxyXG4gICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZiAodHlwZSkgc2F3VHlwZSA9IHR5cGU7XHJcbiAgICAgICAgaWYgKGRpciA+IDAgJiYgIW1vdmVPbmNlKCFmaXJzdCkpIGJyZWFrO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICB2YXIgcmVzdWx0ID0gc2tpcEF0b21pYyhkb2MsIFBvcyhsaW5lLCBjaCksIG9yaWdEaXIsIHRydWUpO1xyXG4gICAgaWYgKCFwb3NzaWJsZSkgcmVzdWx0LmhpdFNpZGUgPSB0cnVlO1xyXG4gICAgcmV0dXJuIHJlc3VsdDtcclxuICB9XHJcblxyXG4gIC8vIEZvciByZWxhdGl2ZSB2ZXJ0aWNhbCBtb3ZlbWVudC4gRGlyIG1heSBiZSAtMSBvciAxLiBVbml0IGNhbiBiZVxyXG4gIC8vIFwicGFnZVwiIG9yIFwibGluZVwiLiBUaGUgcmVzdWx0aW5nIHBvc2l0aW9uIHdpbGwgaGF2ZSBhIGhpdFNpZGU9dHJ1ZVxyXG4gIC8vIHByb3BlcnR5IGlmIGl0IHJlYWNoZWQgdGhlIGVuZCBvZiB0aGUgZG9jdW1lbnQuXHJcbiAgZnVuY3Rpb24gZmluZFBvc1YoY20sIHBvcywgZGlyLCB1bml0KSB7XHJcbiAgICB2YXIgZG9jID0gY20uZG9jLCB4ID0gcG9zLmxlZnQsIHk7XHJcbiAgICBpZiAodW5pdCA9PSBcInBhZ2VcIikge1xyXG4gICAgICB2YXIgcGFnZVNpemUgPSBNYXRoLm1pbihjbS5kaXNwbGF5LndyYXBwZXIuY2xpZW50SGVpZ2h0LCB3aW5kb3cuaW5uZXJIZWlnaHQgfHwgZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LmNsaWVudEhlaWdodCk7XHJcbiAgICAgIHkgPSBwb3MudG9wICsgZGlyICogKHBhZ2VTaXplIC0gKGRpciA8IDAgPyAxLjUgOiAuNSkgKiB0ZXh0SGVpZ2h0KGNtLmRpc3BsYXkpKTtcclxuICAgIH0gZWxzZSBpZiAodW5pdCA9PSBcImxpbmVcIikge1xyXG4gICAgICB5ID0gZGlyID4gMCA/IHBvcy5ib3R0b20gKyAzIDogcG9zLnRvcCAtIDM7XHJcbiAgICB9XHJcbiAgICBmb3IgKDs7KSB7XHJcbiAgICAgIHZhciB0YXJnZXQgPSBjb29yZHNDaGFyKGNtLCB4LCB5KTtcclxuICAgICAgaWYgKCF0YXJnZXQub3V0c2lkZSkgYnJlYWs7XHJcbiAgICAgIGlmIChkaXIgPCAwID8geSA8PSAwIDogeSA+PSBkb2MuaGVpZ2h0KSB7IHRhcmdldC5oaXRTaWRlID0gdHJ1ZTsgYnJlYWs7IH1cclxuICAgICAgeSArPSBkaXIgKiA1O1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIHRhcmdldDtcclxuICB9XHJcblxyXG4gIC8vIEVESVRPUiBNRVRIT0RTXHJcblxyXG4gIC8vIFRoZSBwdWJsaWNseSB2aXNpYmxlIEFQSS4gTm90ZSB0aGF0IG1ldGhvZE9wKGYpIG1lYW5zXHJcbiAgLy8gJ3dyYXAgZiBpbiBhbiBvcGVyYXRpb24sIHBlcmZvcm1lZCBvbiBpdHMgYHRoaXNgIHBhcmFtZXRlcicuXHJcblxyXG4gIC8vIFRoaXMgaXMgbm90IHRoZSBjb21wbGV0ZSBzZXQgb2YgZWRpdG9yIG1ldGhvZHMuIE1vc3Qgb2YgdGhlXHJcbiAgLy8gbWV0aG9kcyBkZWZpbmVkIG9uIHRoZSBEb2MgdHlwZSBhcmUgYWxzbyBpbmplY3RlZCBpbnRvXHJcbiAgLy8gQ29kZU1pcnJvci5wcm90b3R5cGUsIGZvciBiYWNrd2FyZHMgY29tcGF0aWJpbGl0eSBhbmRcclxuICAvLyBjb252ZW5pZW5jZS5cclxuXHJcbiAgQ29kZU1pcnJvci5wcm90b3R5cGUgPSB7XHJcbiAgICBjb25zdHJ1Y3RvcjogQ29kZU1pcnJvcixcclxuICAgIGZvY3VzOiBmdW5jdGlvbigpe3dpbmRvdy5mb2N1cygpOyB0aGlzLmRpc3BsYXkuaW5wdXQuZm9jdXMoKTt9LFxyXG5cclxuICAgIHNldE9wdGlvbjogZnVuY3Rpb24ob3B0aW9uLCB2YWx1ZSkge1xyXG4gICAgICB2YXIgb3B0aW9ucyA9IHRoaXMub3B0aW9ucywgb2xkID0gb3B0aW9uc1tvcHRpb25dO1xyXG4gICAgICBpZiAob3B0aW9uc1tvcHRpb25dID09IHZhbHVlICYmIG9wdGlvbiAhPSBcIm1vZGVcIikgcmV0dXJuO1xyXG4gICAgICBvcHRpb25zW29wdGlvbl0gPSB2YWx1ZTtcclxuICAgICAgaWYgKG9wdGlvbkhhbmRsZXJzLmhhc093blByb3BlcnR5KG9wdGlvbikpXHJcbiAgICAgICAgb3BlcmF0aW9uKHRoaXMsIG9wdGlvbkhhbmRsZXJzW29wdGlvbl0pKHRoaXMsIHZhbHVlLCBvbGQpO1xyXG4gICAgfSxcclxuXHJcbiAgICBnZXRPcHRpb246IGZ1bmN0aW9uKG9wdGlvbikge3JldHVybiB0aGlzLm9wdGlvbnNbb3B0aW9uXTt9LFxyXG4gICAgZ2V0RG9jOiBmdW5jdGlvbigpIHtyZXR1cm4gdGhpcy5kb2M7fSxcclxuXHJcbiAgICBhZGRLZXlNYXA6IGZ1bmN0aW9uKG1hcCwgYm90dG9tKSB7XHJcbiAgICAgIHRoaXMuc3RhdGUua2V5TWFwc1tib3R0b20gPyBcInB1c2hcIiA6IFwidW5zaGlmdFwiXShnZXRLZXlNYXAobWFwKSk7XHJcbiAgICB9LFxyXG4gICAgcmVtb3ZlS2V5TWFwOiBmdW5jdGlvbihtYXApIHtcclxuICAgICAgdmFyIG1hcHMgPSB0aGlzLnN0YXRlLmtleU1hcHM7XHJcbiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbWFwcy5sZW5ndGg7ICsraSlcclxuICAgICAgICBpZiAobWFwc1tpXSA9PSBtYXAgfHwgbWFwc1tpXS5uYW1lID09IG1hcCkge1xyXG4gICAgICAgICAgbWFwcy5zcGxpY2UoaSwgMSk7XHJcbiAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICB9XHJcbiAgICB9LFxyXG5cclxuICAgIGFkZE92ZXJsYXk6IG1ldGhvZE9wKGZ1bmN0aW9uKHNwZWMsIG9wdGlvbnMpIHtcclxuICAgICAgdmFyIG1vZGUgPSBzcGVjLnRva2VuID8gc3BlYyA6IENvZGVNaXJyb3IuZ2V0TW9kZSh0aGlzLm9wdGlvbnMsIHNwZWMpO1xyXG4gICAgICBpZiAobW9kZS5zdGFydFN0YXRlKSB0aHJvdyBuZXcgRXJyb3IoXCJPdmVybGF5cyBtYXkgbm90IGJlIHN0YXRlZnVsLlwiKTtcclxuICAgICAgdGhpcy5zdGF0ZS5vdmVybGF5cy5wdXNoKHttb2RlOiBtb2RlLCBtb2RlU3BlYzogc3BlYywgb3BhcXVlOiBvcHRpb25zICYmIG9wdGlvbnMub3BhcXVlfSk7XHJcbiAgICAgIHRoaXMuc3RhdGUubW9kZUdlbisrO1xyXG4gICAgICByZWdDaGFuZ2UodGhpcyk7XHJcbiAgICB9KSxcclxuICAgIHJlbW92ZU92ZXJsYXk6IG1ldGhvZE9wKGZ1bmN0aW9uKHNwZWMpIHtcclxuICAgICAgdmFyIG92ZXJsYXlzID0gdGhpcy5zdGF0ZS5vdmVybGF5cztcclxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBvdmVybGF5cy5sZW5ndGg7ICsraSkge1xyXG4gICAgICAgIHZhciBjdXIgPSBvdmVybGF5c1tpXS5tb2RlU3BlYztcclxuICAgICAgICBpZiAoY3VyID09IHNwZWMgfHwgdHlwZW9mIHNwZWMgPT0gXCJzdHJpbmdcIiAmJiBjdXIubmFtZSA9PSBzcGVjKSB7XHJcbiAgICAgICAgICBvdmVybGF5cy5zcGxpY2UoaSwgMSk7XHJcbiAgICAgICAgICB0aGlzLnN0YXRlLm1vZGVHZW4rKztcclxuICAgICAgICAgIHJlZ0NoYW5nZSh0aGlzKTtcclxuICAgICAgICAgIHJldHVybjtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0pLFxyXG5cclxuICAgIGluZGVudExpbmU6IG1ldGhvZE9wKGZ1bmN0aW9uKG4sIGRpciwgYWdncmVzc2l2ZSkge1xyXG4gICAgICBpZiAodHlwZW9mIGRpciAhPSBcInN0cmluZ1wiICYmIHR5cGVvZiBkaXIgIT0gXCJudW1iZXJcIikge1xyXG4gICAgICAgIGlmIChkaXIgPT0gbnVsbCkgZGlyID0gdGhpcy5vcHRpb25zLnNtYXJ0SW5kZW50ID8gXCJzbWFydFwiIDogXCJwcmV2XCI7XHJcbiAgICAgICAgZWxzZSBkaXIgPSBkaXIgPyBcImFkZFwiIDogXCJzdWJ0cmFjdFwiO1xyXG4gICAgICB9XHJcbiAgICAgIGlmIChpc0xpbmUodGhpcy5kb2MsIG4pKSBpbmRlbnRMaW5lKHRoaXMsIG4sIGRpciwgYWdncmVzc2l2ZSk7XHJcbiAgICB9KSxcclxuICAgIGluZGVudFNlbGVjdGlvbjogbWV0aG9kT3AoZnVuY3Rpb24oaG93KSB7XHJcbiAgICAgIHZhciByYW5nZXMgPSB0aGlzLmRvYy5zZWwucmFuZ2VzLCBlbmQgPSAtMTtcclxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCByYW5nZXMubGVuZ3RoOyBpKyspIHtcclxuICAgICAgICB2YXIgcmFuZ2UgPSByYW5nZXNbaV07XHJcbiAgICAgICAgaWYgKCFyYW5nZS5lbXB0eSgpKSB7XHJcbiAgICAgICAgICB2YXIgZnJvbSA9IHJhbmdlLmZyb20oKSwgdG8gPSByYW5nZS50bygpO1xyXG4gICAgICAgICAgdmFyIHN0YXJ0ID0gTWF0aC5tYXgoZW5kLCBmcm9tLmxpbmUpO1xyXG4gICAgICAgICAgZW5kID0gTWF0aC5taW4odGhpcy5sYXN0TGluZSgpLCB0by5saW5lIC0gKHRvLmNoID8gMCA6IDEpKSArIDE7XHJcbiAgICAgICAgICBmb3IgKHZhciBqID0gc3RhcnQ7IGogPCBlbmQ7ICsrailcclxuICAgICAgICAgICAgaW5kZW50TGluZSh0aGlzLCBqLCBob3cpO1xyXG4gICAgICAgICAgdmFyIG5ld1JhbmdlcyA9IHRoaXMuZG9jLnNlbC5yYW5nZXM7XHJcbiAgICAgICAgICBpZiAoZnJvbS5jaCA9PSAwICYmIHJhbmdlcy5sZW5ndGggPT0gbmV3UmFuZ2VzLmxlbmd0aCAmJiBuZXdSYW5nZXNbaV0uZnJvbSgpLmNoID4gMClcclxuICAgICAgICAgICAgcmVwbGFjZU9uZVNlbGVjdGlvbih0aGlzLmRvYywgaSwgbmV3IFJhbmdlKGZyb20sIG5ld1Jhbmdlc1tpXS50bygpKSwgc2VsX2RvbnRTY3JvbGwpO1xyXG4gICAgICAgIH0gZWxzZSBpZiAocmFuZ2UuaGVhZC5saW5lID4gZW5kKSB7XHJcbiAgICAgICAgICBpbmRlbnRMaW5lKHRoaXMsIHJhbmdlLmhlYWQubGluZSwgaG93LCB0cnVlKTtcclxuICAgICAgICAgIGVuZCA9IHJhbmdlLmhlYWQubGluZTtcclxuICAgICAgICAgIGlmIChpID09IHRoaXMuZG9jLnNlbC5wcmltSW5kZXgpIGVuc3VyZUN1cnNvclZpc2libGUodGhpcyk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9KSxcclxuXHJcbiAgICAvLyBGZXRjaCB0aGUgcGFyc2VyIHRva2VuIGZvciBhIGdpdmVuIGNoYXJhY3Rlci4gVXNlZnVsIGZvciBoYWNrc1xyXG4gICAgLy8gdGhhdCB3YW50IHRvIGluc3BlY3QgdGhlIG1vZGUgc3RhdGUgKHNheSwgZm9yIGNvbXBsZXRpb24pLlxyXG4gICAgZ2V0VG9rZW5BdDogZnVuY3Rpb24ocG9zLCBwcmVjaXNlKSB7XHJcbiAgICAgIHJldHVybiB0YWtlVG9rZW4odGhpcywgcG9zLCBwcmVjaXNlKTtcclxuICAgIH0sXHJcblxyXG4gICAgZ2V0TGluZVRva2VuczogZnVuY3Rpb24obGluZSwgcHJlY2lzZSkge1xyXG4gICAgICByZXR1cm4gdGFrZVRva2VuKHRoaXMsIFBvcyhsaW5lKSwgcHJlY2lzZSwgdHJ1ZSk7XHJcbiAgICB9LFxyXG5cclxuICAgIGdldFRva2VuVHlwZUF0OiBmdW5jdGlvbihwb3MpIHtcclxuICAgICAgcG9zID0gY2xpcFBvcyh0aGlzLmRvYywgcG9zKTtcclxuICAgICAgdmFyIHN0eWxlcyA9IGdldExpbmVTdHlsZXModGhpcywgZ2V0TGluZSh0aGlzLmRvYywgcG9zLmxpbmUpKTtcclxuICAgICAgdmFyIGJlZm9yZSA9IDAsIGFmdGVyID0gKHN0eWxlcy5sZW5ndGggLSAxKSAvIDIsIGNoID0gcG9zLmNoO1xyXG4gICAgICB2YXIgdHlwZTtcclxuICAgICAgaWYgKGNoID09IDApIHR5cGUgPSBzdHlsZXNbMl07XHJcbiAgICAgIGVsc2UgZm9yICg7Oykge1xyXG4gICAgICAgIHZhciBtaWQgPSAoYmVmb3JlICsgYWZ0ZXIpID4+IDE7XHJcbiAgICAgICAgaWYgKChtaWQgPyBzdHlsZXNbbWlkICogMiAtIDFdIDogMCkgPj0gY2gpIGFmdGVyID0gbWlkO1xyXG4gICAgICAgIGVsc2UgaWYgKHN0eWxlc1ttaWQgKiAyICsgMV0gPCBjaCkgYmVmb3JlID0gbWlkICsgMTtcclxuICAgICAgICBlbHNlIHsgdHlwZSA9IHN0eWxlc1ttaWQgKiAyICsgMl07IGJyZWFrOyB9XHJcbiAgICAgIH1cclxuICAgICAgdmFyIGN1dCA9IHR5cGUgPyB0eXBlLmluZGV4T2YoXCJjbS1vdmVybGF5IFwiKSA6IC0xO1xyXG4gICAgICByZXR1cm4gY3V0IDwgMCA/IHR5cGUgOiBjdXQgPT0gMCA/IG51bGwgOiB0eXBlLnNsaWNlKDAsIGN1dCAtIDEpO1xyXG4gICAgfSxcclxuXHJcbiAgICBnZXRNb2RlQXQ6IGZ1bmN0aW9uKHBvcykge1xyXG4gICAgICB2YXIgbW9kZSA9IHRoaXMuZG9jLm1vZGU7XHJcbiAgICAgIGlmICghbW9kZS5pbm5lck1vZGUpIHJldHVybiBtb2RlO1xyXG4gICAgICByZXR1cm4gQ29kZU1pcnJvci5pbm5lck1vZGUobW9kZSwgdGhpcy5nZXRUb2tlbkF0KHBvcykuc3RhdGUpLm1vZGU7XHJcbiAgICB9LFxyXG5cclxuICAgIGdldEhlbHBlcjogZnVuY3Rpb24ocG9zLCB0eXBlKSB7XHJcbiAgICAgIHJldHVybiB0aGlzLmdldEhlbHBlcnMocG9zLCB0eXBlKVswXTtcclxuICAgIH0sXHJcblxyXG4gICAgZ2V0SGVscGVyczogZnVuY3Rpb24ocG9zLCB0eXBlKSB7XHJcbiAgICAgIHZhciBmb3VuZCA9IFtdO1xyXG4gICAgICBpZiAoIWhlbHBlcnMuaGFzT3duUHJvcGVydHkodHlwZSkpIHJldHVybiBmb3VuZDtcclxuICAgICAgdmFyIGhlbHAgPSBoZWxwZXJzW3R5cGVdLCBtb2RlID0gdGhpcy5nZXRNb2RlQXQocG9zKTtcclxuICAgICAgaWYgKHR5cGVvZiBtb2RlW3R5cGVdID09IFwic3RyaW5nXCIpIHtcclxuICAgICAgICBpZiAoaGVscFttb2RlW3R5cGVdXSkgZm91bmQucHVzaChoZWxwW21vZGVbdHlwZV1dKTtcclxuICAgICAgfSBlbHNlIGlmIChtb2RlW3R5cGVdKSB7XHJcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBtb2RlW3R5cGVdLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgICB2YXIgdmFsID0gaGVscFttb2RlW3R5cGVdW2ldXTtcclxuICAgICAgICAgIGlmICh2YWwpIGZvdW5kLnB1c2godmFsKTtcclxuICAgICAgICB9XHJcbiAgICAgIH0gZWxzZSBpZiAobW9kZS5oZWxwZXJUeXBlICYmIGhlbHBbbW9kZS5oZWxwZXJUeXBlXSkge1xyXG4gICAgICAgIGZvdW5kLnB1c2goaGVscFttb2RlLmhlbHBlclR5cGVdKTtcclxuICAgICAgfSBlbHNlIGlmIChoZWxwW21vZGUubmFtZV0pIHtcclxuICAgICAgICBmb3VuZC5wdXNoKGhlbHBbbW9kZS5uYW1lXSk7XHJcbiAgICAgIH1cclxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBoZWxwLl9nbG9iYWwubGVuZ3RoOyBpKyspIHtcclxuICAgICAgICB2YXIgY3VyID0gaGVscC5fZ2xvYmFsW2ldO1xyXG4gICAgICAgIGlmIChjdXIucHJlZChtb2RlLCB0aGlzKSAmJiBpbmRleE9mKGZvdW5kLCBjdXIudmFsKSA9PSAtMSlcclxuICAgICAgICAgIGZvdW5kLnB1c2goY3VyLnZhbCk7XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIGZvdW5kO1xyXG4gICAgfSxcclxuXHJcbiAgICBnZXRTdGF0ZUFmdGVyOiBmdW5jdGlvbihsaW5lLCBwcmVjaXNlKSB7XHJcbiAgICAgIHZhciBkb2MgPSB0aGlzLmRvYztcclxuICAgICAgbGluZSA9IGNsaXBMaW5lKGRvYywgbGluZSA9PSBudWxsID8gZG9jLmZpcnN0ICsgZG9jLnNpemUgLSAxOiBsaW5lKTtcclxuICAgICAgcmV0dXJuIGdldFN0YXRlQmVmb3JlKHRoaXMsIGxpbmUgKyAxLCBwcmVjaXNlKTtcclxuICAgIH0sXHJcblxyXG4gICAgY3Vyc29yQ29vcmRzOiBmdW5jdGlvbihzdGFydCwgbW9kZSkge1xyXG4gICAgICB2YXIgcG9zLCByYW5nZSA9IHRoaXMuZG9jLnNlbC5wcmltYXJ5KCk7XHJcbiAgICAgIGlmIChzdGFydCA9PSBudWxsKSBwb3MgPSByYW5nZS5oZWFkO1xyXG4gICAgICBlbHNlIGlmICh0eXBlb2Ygc3RhcnQgPT0gXCJvYmplY3RcIikgcG9zID0gY2xpcFBvcyh0aGlzLmRvYywgc3RhcnQpO1xyXG4gICAgICBlbHNlIHBvcyA9IHN0YXJ0ID8gcmFuZ2UuZnJvbSgpIDogcmFuZ2UudG8oKTtcclxuICAgICAgcmV0dXJuIGN1cnNvckNvb3Jkcyh0aGlzLCBwb3MsIG1vZGUgfHwgXCJwYWdlXCIpO1xyXG4gICAgfSxcclxuXHJcbiAgICBjaGFyQ29vcmRzOiBmdW5jdGlvbihwb3MsIG1vZGUpIHtcclxuICAgICAgcmV0dXJuIGNoYXJDb29yZHModGhpcywgY2xpcFBvcyh0aGlzLmRvYywgcG9zKSwgbW9kZSB8fCBcInBhZ2VcIik7XHJcbiAgICB9LFxyXG5cclxuICAgIGNvb3Jkc0NoYXI6IGZ1bmN0aW9uKGNvb3JkcywgbW9kZSkge1xyXG4gICAgICBjb29yZHMgPSBmcm9tQ29vcmRTeXN0ZW0odGhpcywgY29vcmRzLCBtb2RlIHx8IFwicGFnZVwiKTtcclxuICAgICAgcmV0dXJuIGNvb3Jkc0NoYXIodGhpcywgY29vcmRzLmxlZnQsIGNvb3Jkcy50b3ApO1xyXG4gICAgfSxcclxuXHJcbiAgICBsaW5lQXRIZWlnaHQ6IGZ1bmN0aW9uKGhlaWdodCwgbW9kZSkge1xyXG4gICAgICBoZWlnaHQgPSBmcm9tQ29vcmRTeXN0ZW0odGhpcywge3RvcDogaGVpZ2h0LCBsZWZ0OiAwfSwgbW9kZSB8fCBcInBhZ2VcIikudG9wO1xyXG4gICAgICByZXR1cm4gbGluZUF0SGVpZ2h0KHRoaXMuZG9jLCBoZWlnaHQgKyB0aGlzLmRpc3BsYXkudmlld09mZnNldCk7XHJcbiAgICB9LFxyXG4gICAgaGVpZ2h0QXRMaW5lOiBmdW5jdGlvbihsaW5lLCBtb2RlKSB7XHJcbiAgICAgIHZhciBlbmQgPSBmYWxzZSwgbGluZU9iajtcclxuICAgICAgaWYgKHR5cGVvZiBsaW5lID09IFwibnVtYmVyXCIpIHtcclxuICAgICAgICB2YXIgbGFzdCA9IHRoaXMuZG9jLmZpcnN0ICsgdGhpcy5kb2Muc2l6ZSAtIDE7XHJcbiAgICAgICAgaWYgKGxpbmUgPCB0aGlzLmRvYy5maXJzdCkgbGluZSA9IHRoaXMuZG9jLmZpcnN0O1xyXG4gICAgICAgIGVsc2UgaWYgKGxpbmUgPiBsYXN0KSB7IGxpbmUgPSBsYXN0OyBlbmQgPSB0cnVlOyB9XHJcbiAgICAgICAgbGluZU9iaiA9IGdldExpbmUodGhpcy5kb2MsIGxpbmUpO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIGxpbmVPYmogPSBsaW5lO1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiBpbnRvQ29vcmRTeXN0ZW0odGhpcywgbGluZU9iaiwge3RvcDogMCwgbGVmdDogMH0sIG1vZGUgfHwgXCJwYWdlXCIpLnRvcCArXHJcbiAgICAgICAgKGVuZCA/IHRoaXMuZG9jLmhlaWdodCAtIGhlaWdodEF0TGluZShsaW5lT2JqKSA6IDApO1xyXG4gICAgfSxcclxuXHJcbiAgICBkZWZhdWx0VGV4dEhlaWdodDogZnVuY3Rpb24oKSB7IHJldHVybiB0ZXh0SGVpZ2h0KHRoaXMuZGlzcGxheSk7IH0sXHJcbiAgICBkZWZhdWx0Q2hhcldpZHRoOiBmdW5jdGlvbigpIHsgcmV0dXJuIGNoYXJXaWR0aCh0aGlzLmRpc3BsYXkpOyB9LFxyXG5cclxuICAgIHNldEd1dHRlck1hcmtlcjogbWV0aG9kT3AoZnVuY3Rpb24obGluZSwgZ3V0dGVySUQsIHZhbHVlKSB7XHJcbiAgICAgIHJldHVybiBjaGFuZ2VMaW5lKHRoaXMuZG9jLCBsaW5lLCBcImd1dHRlclwiLCBmdW5jdGlvbihsaW5lKSB7XHJcbiAgICAgICAgdmFyIG1hcmtlcnMgPSBsaW5lLmd1dHRlck1hcmtlcnMgfHwgKGxpbmUuZ3V0dGVyTWFya2VycyA9IHt9KTtcclxuICAgICAgICBtYXJrZXJzW2d1dHRlcklEXSA9IHZhbHVlO1xyXG4gICAgICAgIGlmICghdmFsdWUgJiYgaXNFbXB0eShtYXJrZXJzKSkgbGluZS5ndXR0ZXJNYXJrZXJzID0gbnVsbDtcclxuICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgfSk7XHJcbiAgICB9KSxcclxuXHJcbiAgICBjbGVhckd1dHRlcjogbWV0aG9kT3AoZnVuY3Rpb24oZ3V0dGVySUQpIHtcclxuICAgICAgdmFyIGNtID0gdGhpcywgZG9jID0gY20uZG9jLCBpID0gZG9jLmZpcnN0O1xyXG4gICAgICBkb2MuaXRlcihmdW5jdGlvbihsaW5lKSB7XHJcbiAgICAgICAgaWYgKGxpbmUuZ3V0dGVyTWFya2VycyAmJiBsaW5lLmd1dHRlck1hcmtlcnNbZ3V0dGVySURdKSB7XHJcbiAgICAgICAgICBsaW5lLmd1dHRlck1hcmtlcnNbZ3V0dGVySURdID0gbnVsbDtcclxuICAgICAgICAgIHJlZ0xpbmVDaGFuZ2UoY20sIGksIFwiZ3V0dGVyXCIpO1xyXG4gICAgICAgICAgaWYgKGlzRW1wdHkobGluZS5ndXR0ZXJNYXJrZXJzKSkgbGluZS5ndXR0ZXJNYXJrZXJzID0gbnVsbDtcclxuICAgICAgICB9XHJcbiAgICAgICAgKytpO1xyXG4gICAgICB9KTtcclxuICAgIH0pLFxyXG5cclxuICAgIGxpbmVJbmZvOiBmdW5jdGlvbihsaW5lKSB7XHJcbiAgICAgIGlmICh0eXBlb2YgbGluZSA9PSBcIm51bWJlclwiKSB7XHJcbiAgICAgICAgaWYgKCFpc0xpbmUodGhpcy5kb2MsIGxpbmUpKSByZXR1cm4gbnVsbDtcclxuICAgICAgICB2YXIgbiA9IGxpbmU7XHJcbiAgICAgICAgbGluZSA9IGdldExpbmUodGhpcy5kb2MsIGxpbmUpO1xyXG4gICAgICAgIGlmICghbGluZSkgcmV0dXJuIG51bGw7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgdmFyIG4gPSBsaW5lTm8obGluZSk7XHJcbiAgICAgICAgaWYgKG4gPT0gbnVsbCkgcmV0dXJuIG51bGw7XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIHtsaW5lOiBuLCBoYW5kbGU6IGxpbmUsIHRleHQ6IGxpbmUudGV4dCwgZ3V0dGVyTWFya2VyczogbGluZS5ndXR0ZXJNYXJrZXJzLFxyXG4gICAgICAgICAgICAgIHRleHRDbGFzczogbGluZS50ZXh0Q2xhc3MsIGJnQ2xhc3M6IGxpbmUuYmdDbGFzcywgd3JhcENsYXNzOiBsaW5lLndyYXBDbGFzcyxcclxuICAgICAgICAgICAgICB3aWRnZXRzOiBsaW5lLndpZGdldHN9O1xyXG4gICAgfSxcclxuXHJcbiAgICBnZXRWaWV3cG9ydDogZnVuY3Rpb24oKSB7IHJldHVybiB7ZnJvbTogdGhpcy5kaXNwbGF5LnZpZXdGcm9tLCB0bzogdGhpcy5kaXNwbGF5LnZpZXdUb307fSxcclxuXHJcbiAgICBhZGRXaWRnZXQ6IGZ1bmN0aW9uKHBvcywgbm9kZSwgc2Nyb2xsLCB2ZXJ0LCBob3Jpeikge1xyXG4gICAgICB2YXIgZGlzcGxheSA9IHRoaXMuZGlzcGxheTtcclxuICAgICAgcG9zID0gY3Vyc29yQ29vcmRzKHRoaXMsIGNsaXBQb3ModGhpcy5kb2MsIHBvcykpO1xyXG4gICAgICB2YXIgdG9wID0gcG9zLmJvdHRvbSwgbGVmdCA9IHBvcy5sZWZ0O1xyXG4gICAgICBub2RlLnN0eWxlLnBvc2l0aW9uID0gXCJhYnNvbHV0ZVwiO1xyXG4gICAgICBub2RlLnNldEF0dHJpYnV0ZShcImNtLWlnbm9yZS1ldmVudHNcIiwgXCJ0cnVlXCIpO1xyXG4gICAgICB0aGlzLmRpc3BsYXkuaW5wdXQuc2V0VW5lZGl0YWJsZShub2RlKTtcclxuICAgICAgZGlzcGxheS5zaXplci5hcHBlbmRDaGlsZChub2RlKTtcclxuICAgICAgaWYgKHZlcnQgPT0gXCJvdmVyXCIpIHtcclxuICAgICAgICB0b3AgPSBwb3MudG9wO1xyXG4gICAgICB9IGVsc2UgaWYgKHZlcnQgPT0gXCJhYm92ZVwiIHx8IHZlcnQgPT0gXCJuZWFyXCIpIHtcclxuICAgICAgICB2YXIgdnNwYWNlID0gTWF0aC5tYXgoZGlzcGxheS53cmFwcGVyLmNsaWVudEhlaWdodCwgdGhpcy5kb2MuaGVpZ2h0KSxcclxuICAgICAgICBoc3BhY2UgPSBNYXRoLm1heChkaXNwbGF5LnNpemVyLmNsaWVudFdpZHRoLCBkaXNwbGF5LmxpbmVTcGFjZS5jbGllbnRXaWR0aCk7XHJcbiAgICAgICAgLy8gRGVmYXVsdCB0byBwb3NpdGlvbmluZyBhYm92ZSAoaWYgc3BlY2lmaWVkIGFuZCBwb3NzaWJsZSk7IG90aGVyd2lzZSBkZWZhdWx0IHRvIHBvc2l0aW9uaW5nIGJlbG93XHJcbiAgICAgICAgaWYgKCh2ZXJ0ID09ICdhYm92ZScgfHwgcG9zLmJvdHRvbSArIG5vZGUub2Zmc2V0SGVpZ2h0ID4gdnNwYWNlKSAmJiBwb3MudG9wID4gbm9kZS5vZmZzZXRIZWlnaHQpXHJcbiAgICAgICAgICB0b3AgPSBwb3MudG9wIC0gbm9kZS5vZmZzZXRIZWlnaHQ7XHJcbiAgICAgICAgZWxzZSBpZiAocG9zLmJvdHRvbSArIG5vZGUub2Zmc2V0SGVpZ2h0IDw9IHZzcGFjZSlcclxuICAgICAgICAgIHRvcCA9IHBvcy5ib3R0b207XHJcbiAgICAgICAgaWYgKGxlZnQgKyBub2RlLm9mZnNldFdpZHRoID4gaHNwYWNlKVxyXG4gICAgICAgICAgbGVmdCA9IGhzcGFjZSAtIG5vZGUub2Zmc2V0V2lkdGg7XHJcbiAgICAgIH1cclxuICAgICAgbm9kZS5zdHlsZS50b3AgPSB0b3AgKyBcInB4XCI7XHJcbiAgICAgIG5vZGUuc3R5bGUubGVmdCA9IG5vZGUuc3R5bGUucmlnaHQgPSBcIlwiO1xyXG4gICAgICBpZiAoaG9yaXogPT0gXCJyaWdodFwiKSB7XHJcbiAgICAgICAgbGVmdCA9IGRpc3BsYXkuc2l6ZXIuY2xpZW50V2lkdGggLSBub2RlLm9mZnNldFdpZHRoO1xyXG4gICAgICAgIG5vZGUuc3R5bGUucmlnaHQgPSBcIjBweFwiO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIGlmIChob3JpeiA9PSBcImxlZnRcIikgbGVmdCA9IDA7XHJcbiAgICAgICAgZWxzZSBpZiAoaG9yaXogPT0gXCJtaWRkbGVcIikgbGVmdCA9IChkaXNwbGF5LnNpemVyLmNsaWVudFdpZHRoIC0gbm9kZS5vZmZzZXRXaWR0aCkgLyAyO1xyXG4gICAgICAgIG5vZGUuc3R5bGUubGVmdCA9IGxlZnQgKyBcInB4XCI7XHJcbiAgICAgIH1cclxuICAgICAgaWYgKHNjcm9sbClcclxuICAgICAgICBzY3JvbGxJbnRvVmlldyh0aGlzLCBsZWZ0LCB0b3AsIGxlZnQgKyBub2RlLm9mZnNldFdpZHRoLCB0b3AgKyBub2RlLm9mZnNldEhlaWdodCk7XHJcbiAgICB9LFxyXG5cclxuICAgIHRyaWdnZXJPbktleURvd246IG1ldGhvZE9wKG9uS2V5RG93biksXHJcbiAgICB0cmlnZ2VyT25LZXlQcmVzczogbWV0aG9kT3Aob25LZXlQcmVzcyksXHJcbiAgICB0cmlnZ2VyT25LZXlVcDogb25LZXlVcCxcclxuXHJcbiAgICBleGVjQ29tbWFuZDogZnVuY3Rpb24oY21kKSB7XHJcbiAgICAgIGlmIChjb21tYW5kcy5oYXNPd25Qcm9wZXJ0eShjbWQpKVxyXG4gICAgICAgIHJldHVybiBjb21tYW5kc1tjbWRdLmNhbGwobnVsbCwgdGhpcyk7XHJcbiAgICB9LFxyXG5cclxuICAgIHRyaWdnZXJFbGVjdHJpYzogbWV0aG9kT3AoZnVuY3Rpb24odGV4dCkgeyB0cmlnZ2VyRWxlY3RyaWModGhpcywgdGV4dCk7IH0pLFxyXG5cclxuICAgIGZpbmRQb3NIOiBmdW5jdGlvbihmcm9tLCBhbW91bnQsIHVuaXQsIHZpc3VhbGx5KSB7XHJcbiAgICAgIHZhciBkaXIgPSAxO1xyXG4gICAgICBpZiAoYW1vdW50IDwgMCkgeyBkaXIgPSAtMTsgYW1vdW50ID0gLWFtb3VudDsgfVxyXG4gICAgICBmb3IgKHZhciBpID0gMCwgY3VyID0gY2xpcFBvcyh0aGlzLmRvYywgZnJvbSk7IGkgPCBhbW91bnQ7ICsraSkge1xyXG4gICAgICAgIGN1ciA9IGZpbmRQb3NIKHRoaXMuZG9jLCBjdXIsIGRpciwgdW5pdCwgdmlzdWFsbHkpO1xyXG4gICAgICAgIGlmIChjdXIuaGl0U2lkZSkgYnJlYWs7XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIGN1cjtcclxuICAgIH0sXHJcblxyXG4gICAgbW92ZUg6IG1ldGhvZE9wKGZ1bmN0aW9uKGRpciwgdW5pdCkge1xyXG4gICAgICB2YXIgY20gPSB0aGlzO1xyXG4gICAgICBjbS5leHRlbmRTZWxlY3Rpb25zQnkoZnVuY3Rpb24ocmFuZ2UpIHtcclxuICAgICAgICBpZiAoY20uZGlzcGxheS5zaGlmdCB8fCBjbS5kb2MuZXh0ZW5kIHx8IHJhbmdlLmVtcHR5KCkpXHJcbiAgICAgICAgICByZXR1cm4gZmluZFBvc0goY20uZG9jLCByYW5nZS5oZWFkLCBkaXIsIHVuaXQsIGNtLm9wdGlvbnMucnRsTW92ZVZpc3VhbGx5KTtcclxuICAgICAgICBlbHNlXHJcbiAgICAgICAgICByZXR1cm4gZGlyIDwgMCA/IHJhbmdlLmZyb20oKSA6IHJhbmdlLnRvKCk7XHJcbiAgICAgIH0sIHNlbF9tb3ZlKTtcclxuICAgIH0pLFxyXG5cclxuICAgIGRlbGV0ZUg6IG1ldGhvZE9wKGZ1bmN0aW9uKGRpciwgdW5pdCkge1xyXG4gICAgICB2YXIgc2VsID0gdGhpcy5kb2Muc2VsLCBkb2MgPSB0aGlzLmRvYztcclxuICAgICAgaWYgKHNlbC5zb21ldGhpbmdTZWxlY3RlZCgpKVxyXG4gICAgICAgIGRvYy5yZXBsYWNlU2VsZWN0aW9uKFwiXCIsIG51bGwsIFwiK2RlbGV0ZVwiKTtcclxuICAgICAgZWxzZVxyXG4gICAgICAgIGRlbGV0ZU5lYXJTZWxlY3Rpb24odGhpcywgZnVuY3Rpb24ocmFuZ2UpIHtcclxuICAgICAgICAgIHZhciBvdGhlciA9IGZpbmRQb3NIKGRvYywgcmFuZ2UuaGVhZCwgZGlyLCB1bml0LCBmYWxzZSk7XHJcbiAgICAgICAgICByZXR1cm4gZGlyIDwgMCA/IHtmcm9tOiBvdGhlciwgdG86IHJhbmdlLmhlYWR9IDoge2Zyb206IHJhbmdlLmhlYWQsIHRvOiBvdGhlcn07XHJcbiAgICAgICAgfSk7XHJcbiAgICB9KSxcclxuXHJcbiAgICBmaW5kUG9zVjogZnVuY3Rpb24oZnJvbSwgYW1vdW50LCB1bml0LCBnb2FsQ29sdW1uKSB7XHJcbiAgICAgIHZhciBkaXIgPSAxLCB4ID0gZ29hbENvbHVtbjtcclxuICAgICAgaWYgKGFtb3VudCA8IDApIHsgZGlyID0gLTE7IGFtb3VudCA9IC1hbW91bnQ7IH1cclxuICAgICAgZm9yICh2YXIgaSA9IDAsIGN1ciA9IGNsaXBQb3ModGhpcy5kb2MsIGZyb20pOyBpIDwgYW1vdW50OyArK2kpIHtcclxuICAgICAgICB2YXIgY29vcmRzID0gY3Vyc29yQ29vcmRzKHRoaXMsIGN1ciwgXCJkaXZcIik7XHJcbiAgICAgICAgaWYgKHggPT0gbnVsbCkgeCA9IGNvb3Jkcy5sZWZ0O1xyXG4gICAgICAgIGVsc2UgY29vcmRzLmxlZnQgPSB4O1xyXG4gICAgICAgIGN1ciA9IGZpbmRQb3NWKHRoaXMsIGNvb3JkcywgZGlyLCB1bml0KTtcclxuICAgICAgICBpZiAoY3VyLmhpdFNpZGUpIGJyZWFrO1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiBjdXI7XHJcbiAgICB9LFxyXG5cclxuICAgIG1vdmVWOiBtZXRob2RPcChmdW5jdGlvbihkaXIsIHVuaXQpIHtcclxuICAgICAgdmFyIGNtID0gdGhpcywgZG9jID0gdGhpcy5kb2MsIGdvYWxzID0gW107XHJcbiAgICAgIHZhciBjb2xsYXBzZSA9ICFjbS5kaXNwbGF5LnNoaWZ0ICYmICFkb2MuZXh0ZW5kICYmIGRvYy5zZWwuc29tZXRoaW5nU2VsZWN0ZWQoKTtcclxuICAgICAgZG9jLmV4dGVuZFNlbGVjdGlvbnNCeShmdW5jdGlvbihyYW5nZSkge1xyXG4gICAgICAgIGlmIChjb2xsYXBzZSlcclxuICAgICAgICAgIHJldHVybiBkaXIgPCAwID8gcmFuZ2UuZnJvbSgpIDogcmFuZ2UudG8oKTtcclxuICAgICAgICB2YXIgaGVhZFBvcyA9IGN1cnNvckNvb3JkcyhjbSwgcmFuZ2UuaGVhZCwgXCJkaXZcIik7XHJcbiAgICAgICAgaWYgKHJhbmdlLmdvYWxDb2x1bW4gIT0gbnVsbCkgaGVhZFBvcy5sZWZ0ID0gcmFuZ2UuZ29hbENvbHVtbjtcclxuICAgICAgICBnb2Fscy5wdXNoKGhlYWRQb3MubGVmdCk7XHJcbiAgICAgICAgdmFyIHBvcyA9IGZpbmRQb3NWKGNtLCBoZWFkUG9zLCBkaXIsIHVuaXQpO1xyXG4gICAgICAgIGlmICh1bml0ID09IFwicGFnZVwiICYmIHJhbmdlID09IGRvYy5zZWwucHJpbWFyeSgpKVxyXG4gICAgICAgICAgYWRkVG9TY3JvbGxQb3MoY20sIG51bGwsIGNoYXJDb29yZHMoY20sIHBvcywgXCJkaXZcIikudG9wIC0gaGVhZFBvcy50b3ApO1xyXG4gICAgICAgIHJldHVybiBwb3M7XHJcbiAgICAgIH0sIHNlbF9tb3ZlKTtcclxuICAgICAgaWYgKGdvYWxzLmxlbmd0aCkgZm9yICh2YXIgaSA9IDA7IGkgPCBkb2Muc2VsLnJhbmdlcy5sZW5ndGg7IGkrKylcclxuICAgICAgICBkb2Muc2VsLnJhbmdlc1tpXS5nb2FsQ29sdW1uID0gZ29hbHNbaV07XHJcbiAgICB9KSxcclxuXHJcbiAgICAvLyBGaW5kIHRoZSB3b3JkIGF0IHRoZSBnaXZlbiBwb3NpdGlvbiAoYXMgcmV0dXJuZWQgYnkgY29vcmRzQ2hhcikuXHJcbiAgICBmaW5kV29yZEF0OiBmdW5jdGlvbihwb3MpIHtcclxuICAgICAgdmFyIGRvYyA9IHRoaXMuZG9jLCBsaW5lID0gZ2V0TGluZShkb2MsIHBvcy5saW5lKS50ZXh0O1xyXG4gICAgICB2YXIgc3RhcnQgPSBwb3MuY2gsIGVuZCA9IHBvcy5jaDtcclxuICAgICAgaWYgKGxpbmUpIHtcclxuICAgICAgICB2YXIgaGVscGVyID0gdGhpcy5nZXRIZWxwZXIocG9zLCBcIndvcmRDaGFyc1wiKTtcclxuICAgICAgICBpZiAoKHBvcy54UmVsIDwgMCB8fCBlbmQgPT0gbGluZS5sZW5ndGgpICYmIHN0YXJ0KSAtLXN0YXJ0OyBlbHNlICsrZW5kO1xyXG4gICAgICAgIHZhciBzdGFydENoYXIgPSBsaW5lLmNoYXJBdChzdGFydCk7XHJcbiAgICAgICAgdmFyIGNoZWNrID0gaXNXb3JkQ2hhcihzdGFydENoYXIsIGhlbHBlcilcclxuICAgICAgICAgID8gZnVuY3Rpb24oY2gpIHsgcmV0dXJuIGlzV29yZENoYXIoY2gsIGhlbHBlcik7IH1cclxuICAgICAgICAgIDogL1xccy8udGVzdChzdGFydENoYXIpID8gZnVuY3Rpb24oY2gpIHtyZXR1cm4gL1xccy8udGVzdChjaCk7fVxyXG4gICAgICAgICAgOiBmdW5jdGlvbihjaCkge3JldHVybiAhL1xccy8udGVzdChjaCkgJiYgIWlzV29yZENoYXIoY2gpO307XHJcbiAgICAgICAgd2hpbGUgKHN0YXJ0ID4gMCAmJiBjaGVjayhsaW5lLmNoYXJBdChzdGFydCAtIDEpKSkgLS1zdGFydDtcclxuICAgICAgICB3aGlsZSAoZW5kIDwgbGluZS5sZW5ndGggJiYgY2hlY2sobGluZS5jaGFyQXQoZW5kKSkpICsrZW5kO1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiBuZXcgUmFuZ2UoUG9zKHBvcy5saW5lLCBzdGFydCksIFBvcyhwb3MubGluZSwgZW5kKSk7XHJcbiAgICB9LFxyXG5cclxuICAgIHRvZ2dsZU92ZXJ3cml0ZTogZnVuY3Rpb24odmFsdWUpIHtcclxuICAgICAgaWYgKHZhbHVlICE9IG51bGwgJiYgdmFsdWUgPT0gdGhpcy5zdGF0ZS5vdmVyd3JpdGUpIHJldHVybjtcclxuICAgICAgaWYgKHRoaXMuc3RhdGUub3ZlcndyaXRlID0gIXRoaXMuc3RhdGUub3ZlcndyaXRlKVxyXG4gICAgICAgIGFkZENsYXNzKHRoaXMuZGlzcGxheS5jdXJzb3JEaXYsIFwiQ29kZU1pcnJvci1vdmVyd3JpdGVcIik7XHJcbiAgICAgIGVsc2VcclxuICAgICAgICBybUNsYXNzKHRoaXMuZGlzcGxheS5jdXJzb3JEaXYsIFwiQ29kZU1pcnJvci1vdmVyd3JpdGVcIik7XHJcblxyXG4gICAgICBzaWduYWwodGhpcywgXCJvdmVyd3JpdGVUb2dnbGVcIiwgdGhpcywgdGhpcy5zdGF0ZS5vdmVyd3JpdGUpO1xyXG4gICAgfSxcclxuICAgIGhhc0ZvY3VzOiBmdW5jdGlvbigpIHsgcmV0dXJuIHRoaXMuZGlzcGxheS5pbnB1dC5nZXRGaWVsZCgpID09IGFjdGl2ZUVsdCgpOyB9LFxyXG5cclxuICAgIHNjcm9sbFRvOiBtZXRob2RPcChmdW5jdGlvbih4LCB5KSB7XHJcbiAgICAgIGlmICh4ICE9IG51bGwgfHwgeSAhPSBudWxsKSByZXNvbHZlU2Nyb2xsVG9Qb3ModGhpcyk7XHJcbiAgICAgIGlmICh4ICE9IG51bGwpIHRoaXMuY3VyT3Auc2Nyb2xsTGVmdCA9IHg7XHJcbiAgICAgIGlmICh5ICE9IG51bGwpIHRoaXMuY3VyT3Auc2Nyb2xsVG9wID0geTtcclxuICAgIH0pLFxyXG4gICAgZ2V0U2Nyb2xsSW5mbzogZnVuY3Rpb24oKSB7XHJcbiAgICAgIHZhciBzY3JvbGxlciA9IHRoaXMuZGlzcGxheS5zY3JvbGxlcjtcclxuICAgICAgcmV0dXJuIHtsZWZ0OiBzY3JvbGxlci5zY3JvbGxMZWZ0LCB0b3A6IHNjcm9sbGVyLnNjcm9sbFRvcCxcclxuICAgICAgICAgICAgICBoZWlnaHQ6IHNjcm9sbGVyLnNjcm9sbEhlaWdodCAtIHNjcm9sbEdhcCh0aGlzKSAtIHRoaXMuZGlzcGxheS5iYXJIZWlnaHQsXHJcbiAgICAgICAgICAgICAgd2lkdGg6IHNjcm9sbGVyLnNjcm9sbFdpZHRoIC0gc2Nyb2xsR2FwKHRoaXMpIC0gdGhpcy5kaXNwbGF5LmJhcldpZHRoLFxyXG4gICAgICAgICAgICAgIGNsaWVudEhlaWdodDogZGlzcGxheUhlaWdodCh0aGlzKSwgY2xpZW50V2lkdGg6IGRpc3BsYXlXaWR0aCh0aGlzKX07XHJcbiAgICB9LFxyXG5cclxuICAgIHNjcm9sbEludG9WaWV3OiBtZXRob2RPcChmdW5jdGlvbihyYW5nZSwgbWFyZ2luKSB7XHJcbiAgICAgIGlmIChyYW5nZSA9PSBudWxsKSB7XHJcbiAgICAgICAgcmFuZ2UgPSB7ZnJvbTogdGhpcy5kb2Muc2VsLnByaW1hcnkoKS5oZWFkLCB0bzogbnVsbH07XHJcbiAgICAgICAgaWYgKG1hcmdpbiA9PSBudWxsKSBtYXJnaW4gPSB0aGlzLm9wdGlvbnMuY3Vyc29yU2Nyb2xsTWFyZ2luO1xyXG4gICAgICB9IGVsc2UgaWYgKHR5cGVvZiByYW5nZSA9PSBcIm51bWJlclwiKSB7XHJcbiAgICAgICAgcmFuZ2UgPSB7ZnJvbTogUG9zKHJhbmdlLCAwKSwgdG86IG51bGx9O1xyXG4gICAgICB9IGVsc2UgaWYgKHJhbmdlLmZyb20gPT0gbnVsbCkge1xyXG4gICAgICAgIHJhbmdlID0ge2Zyb206IHJhbmdlLCB0bzogbnVsbH07XHJcbiAgICAgIH1cclxuICAgICAgaWYgKCFyYW5nZS50bykgcmFuZ2UudG8gPSByYW5nZS5mcm9tO1xyXG4gICAgICByYW5nZS5tYXJnaW4gPSBtYXJnaW4gfHwgMDtcclxuXHJcbiAgICAgIGlmIChyYW5nZS5mcm9tLmxpbmUgIT0gbnVsbCkge1xyXG4gICAgICAgIHJlc29sdmVTY3JvbGxUb1Bvcyh0aGlzKTtcclxuICAgICAgICB0aGlzLmN1ck9wLnNjcm9sbFRvUG9zID0gcmFuZ2U7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgdmFyIHNQb3MgPSBjYWxjdWxhdGVTY3JvbGxQb3ModGhpcywgTWF0aC5taW4ocmFuZ2UuZnJvbS5sZWZ0LCByYW5nZS50by5sZWZ0KSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNYXRoLm1pbihyYW5nZS5mcm9tLnRvcCwgcmFuZ2UudG8udG9wKSAtIHJhbmdlLm1hcmdpbixcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNYXRoLm1heChyYW5nZS5mcm9tLnJpZ2h0LCByYW5nZS50by5yaWdodCksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTWF0aC5tYXgocmFuZ2UuZnJvbS5ib3R0b20sIHJhbmdlLnRvLmJvdHRvbSkgKyByYW5nZS5tYXJnaW4pO1xyXG4gICAgICAgIHRoaXMuc2Nyb2xsVG8oc1Bvcy5zY3JvbGxMZWZ0LCBzUG9zLnNjcm9sbFRvcCk7XHJcbiAgICAgIH1cclxuICAgIH0pLFxyXG5cclxuICAgIHNldFNpemU6IG1ldGhvZE9wKGZ1bmN0aW9uKHdpZHRoLCBoZWlnaHQpIHtcclxuICAgICAgdmFyIGNtID0gdGhpcztcclxuICAgICAgZnVuY3Rpb24gaW50ZXJwcmV0KHZhbCkge1xyXG4gICAgICAgIHJldHVybiB0eXBlb2YgdmFsID09IFwibnVtYmVyXCIgfHwgL15cXGQrJC8udGVzdChTdHJpbmcodmFsKSkgPyB2YWwgKyBcInB4XCIgOiB2YWw7XHJcbiAgICAgIH1cclxuICAgICAgaWYgKHdpZHRoICE9IG51bGwpIGNtLmRpc3BsYXkud3JhcHBlci5zdHlsZS53aWR0aCA9IGludGVycHJldCh3aWR0aCk7XHJcbiAgICAgIGlmIChoZWlnaHQgIT0gbnVsbCkgY20uZGlzcGxheS53cmFwcGVyLnN0eWxlLmhlaWdodCA9IGludGVycHJldChoZWlnaHQpO1xyXG4gICAgICBpZiAoY20ub3B0aW9ucy5saW5lV3JhcHBpbmcpIGNsZWFyTGluZU1lYXN1cmVtZW50Q2FjaGUodGhpcyk7XHJcbiAgICAgIHZhciBsaW5lTm8gPSBjbS5kaXNwbGF5LnZpZXdGcm9tO1xyXG4gICAgICBjbS5kb2MuaXRlcihsaW5lTm8sIGNtLmRpc3BsYXkudmlld1RvLCBmdW5jdGlvbihsaW5lKSB7XHJcbiAgICAgICAgaWYgKGxpbmUud2lkZ2V0cykgZm9yICh2YXIgaSA9IDA7IGkgPCBsaW5lLndpZGdldHMubGVuZ3RoOyBpKyspXHJcbiAgICAgICAgICBpZiAobGluZS53aWRnZXRzW2ldLm5vSFNjcm9sbCkgeyByZWdMaW5lQ2hhbmdlKGNtLCBsaW5lTm8sIFwid2lkZ2V0XCIpOyBicmVhazsgfVxyXG4gICAgICAgICsrbGluZU5vO1xyXG4gICAgICB9KTtcclxuICAgICAgY20uY3VyT3AuZm9yY2VVcGRhdGUgPSB0cnVlO1xyXG4gICAgICBzaWduYWwoY20sIFwicmVmcmVzaFwiLCB0aGlzKTtcclxuICAgIH0pLFxyXG5cclxuICAgIG9wZXJhdGlvbjogZnVuY3Rpb24oZil7cmV0dXJuIHJ1bkluT3AodGhpcywgZik7fSxcclxuXHJcbiAgICByZWZyZXNoOiBtZXRob2RPcChmdW5jdGlvbigpIHtcclxuICAgICAgdmFyIG9sZEhlaWdodCA9IHRoaXMuZGlzcGxheS5jYWNoZWRUZXh0SGVpZ2h0O1xyXG4gICAgICByZWdDaGFuZ2UodGhpcyk7XHJcbiAgICAgIHRoaXMuY3VyT3AuZm9yY2VVcGRhdGUgPSB0cnVlO1xyXG4gICAgICBjbGVhckNhY2hlcyh0aGlzKTtcclxuICAgICAgdGhpcy5zY3JvbGxUbyh0aGlzLmRvYy5zY3JvbGxMZWZ0LCB0aGlzLmRvYy5zY3JvbGxUb3ApO1xyXG4gICAgICB1cGRhdGVHdXR0ZXJTcGFjZSh0aGlzKTtcclxuICAgICAgaWYgKG9sZEhlaWdodCA9PSBudWxsIHx8IE1hdGguYWJzKG9sZEhlaWdodCAtIHRleHRIZWlnaHQodGhpcy5kaXNwbGF5KSkgPiAuNSlcclxuICAgICAgICBlc3RpbWF0ZUxpbmVIZWlnaHRzKHRoaXMpO1xyXG4gICAgICBzaWduYWwodGhpcywgXCJyZWZyZXNoXCIsIHRoaXMpO1xyXG4gICAgfSksXHJcblxyXG4gICAgc3dhcERvYzogbWV0aG9kT3AoZnVuY3Rpb24oZG9jKSB7XHJcbiAgICAgIHZhciBvbGQgPSB0aGlzLmRvYztcclxuICAgICAgb2xkLmNtID0gbnVsbDtcclxuICAgICAgYXR0YWNoRG9jKHRoaXMsIGRvYyk7XHJcbiAgICAgIGNsZWFyQ2FjaGVzKHRoaXMpO1xyXG4gICAgICB0aGlzLmRpc3BsYXkuaW5wdXQucmVzZXQoKTtcclxuICAgICAgdGhpcy5zY3JvbGxUbyhkb2Muc2Nyb2xsTGVmdCwgZG9jLnNjcm9sbFRvcCk7XHJcbiAgICAgIHRoaXMuY3VyT3AuZm9yY2VTY3JvbGwgPSB0cnVlO1xyXG4gICAgICBzaWduYWxMYXRlcih0aGlzLCBcInN3YXBEb2NcIiwgdGhpcywgb2xkKTtcclxuICAgICAgcmV0dXJuIG9sZDtcclxuICAgIH0pLFxyXG5cclxuICAgIGdldElucHV0RmllbGQ6IGZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuZGlzcGxheS5pbnB1dC5nZXRGaWVsZCgpO30sXHJcbiAgICBnZXRXcmFwcGVyRWxlbWVudDogZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5kaXNwbGF5LndyYXBwZXI7fSxcclxuICAgIGdldFNjcm9sbGVyRWxlbWVudDogZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5kaXNwbGF5LnNjcm9sbGVyO30sXHJcbiAgICBnZXRHdXR0ZXJFbGVtZW50OiBmdW5jdGlvbigpe3JldHVybiB0aGlzLmRpc3BsYXkuZ3V0dGVyczt9XHJcbiAgfTtcclxuICBldmVudE1peGluKENvZGVNaXJyb3IpO1xyXG5cclxuICAvLyBPUFRJT04gREVGQVVMVFNcclxuXHJcbiAgLy8gVGhlIGRlZmF1bHQgY29uZmlndXJhdGlvbiBvcHRpb25zLlxyXG4gIHZhciBkZWZhdWx0cyA9IENvZGVNaXJyb3IuZGVmYXVsdHMgPSB7fTtcclxuICAvLyBGdW5jdGlvbnMgdG8gcnVuIHdoZW4gb3B0aW9ucyBhcmUgY2hhbmdlZC5cclxuICB2YXIgb3B0aW9uSGFuZGxlcnMgPSBDb2RlTWlycm9yLm9wdGlvbkhhbmRsZXJzID0ge307XHJcblxyXG4gIGZ1bmN0aW9uIG9wdGlvbihuYW1lLCBkZWZsdCwgaGFuZGxlLCBub3RPbkluaXQpIHtcclxuICAgIENvZGVNaXJyb3IuZGVmYXVsdHNbbmFtZV0gPSBkZWZsdDtcclxuICAgIGlmIChoYW5kbGUpIG9wdGlvbkhhbmRsZXJzW25hbWVdID1cclxuICAgICAgbm90T25Jbml0ID8gZnVuY3Rpb24oY20sIHZhbCwgb2xkKSB7aWYgKG9sZCAhPSBJbml0KSBoYW5kbGUoY20sIHZhbCwgb2xkKTt9IDogaGFuZGxlO1xyXG4gIH1cclxuXHJcbiAgLy8gUGFzc2VkIHRvIG9wdGlvbiBoYW5kbGVycyB3aGVuIHRoZXJlIGlzIG5vIG9sZCB2YWx1ZS5cclxuICB2YXIgSW5pdCA9IENvZGVNaXJyb3IuSW5pdCA9IHt0b1N0cmluZzogZnVuY3Rpb24oKXtyZXR1cm4gXCJDb2RlTWlycm9yLkluaXRcIjt9fTtcclxuXHJcbiAgLy8gVGhlc2UgdHdvIGFyZSwgb24gaW5pdCwgY2FsbGVkIGZyb20gdGhlIGNvbnN0cnVjdG9yIGJlY2F1c2UgdGhleVxyXG4gIC8vIGhhdmUgdG8gYmUgaW5pdGlhbGl6ZWQgYmVmb3JlIHRoZSBlZGl0b3IgY2FuIHN0YXJ0IGF0IGFsbC5cclxuICBvcHRpb24oXCJ2YWx1ZVwiLCBcIlwiLCBmdW5jdGlvbihjbSwgdmFsKSB7XHJcbiAgICBjbS5zZXRWYWx1ZSh2YWwpO1xyXG4gIH0sIHRydWUpO1xyXG4gIG9wdGlvbihcIm1vZGVcIiwgbnVsbCwgZnVuY3Rpb24oY20sIHZhbCkge1xyXG4gICAgY20uZG9jLm1vZGVPcHRpb24gPSB2YWw7XHJcbiAgICBsb2FkTW9kZShjbSk7XHJcbiAgfSwgdHJ1ZSk7XHJcblxyXG4gIG9wdGlvbihcImluZGVudFVuaXRcIiwgMiwgbG9hZE1vZGUsIHRydWUpO1xyXG4gIG9wdGlvbihcImluZGVudFdpdGhUYWJzXCIsIGZhbHNlKTtcclxuICBvcHRpb24oXCJzbWFydEluZGVudFwiLCB0cnVlKTtcclxuICBvcHRpb24oXCJ0YWJTaXplXCIsIDQsIGZ1bmN0aW9uKGNtKSB7XHJcbiAgICByZXNldE1vZGVTdGF0ZShjbSk7XHJcbiAgICBjbGVhckNhY2hlcyhjbSk7XHJcbiAgICByZWdDaGFuZ2UoY20pO1xyXG4gIH0sIHRydWUpO1xyXG4gIG9wdGlvbihcImxpbmVTZXBhcmF0b3JcIiwgbnVsbCwgZnVuY3Rpb24oY20sIHZhbCkge1xyXG4gICAgY20uZG9jLmxpbmVTZXAgPSB2YWw7XHJcbiAgICBpZiAoIXZhbCkgcmV0dXJuO1xyXG4gICAgdmFyIG5ld0JyZWFrcyA9IFtdLCBsaW5lTm8gPSBjbS5kb2MuZmlyc3Q7XHJcbiAgICBjbS5kb2MuaXRlcihmdW5jdGlvbihsaW5lKSB7XHJcbiAgICAgIGZvciAodmFyIHBvcyA9IDA7Oykge1xyXG4gICAgICAgIHZhciBmb3VuZCA9IGxpbmUudGV4dC5pbmRleE9mKHZhbCwgcG9zKTtcclxuICAgICAgICBpZiAoZm91bmQgPT0gLTEpIGJyZWFrO1xyXG4gICAgICAgIHBvcyA9IGZvdW5kICsgdmFsLmxlbmd0aDtcclxuICAgICAgICBuZXdCcmVha3MucHVzaChQb3MobGluZU5vLCBmb3VuZCkpO1xyXG4gICAgICB9XHJcbiAgICAgIGxpbmVObysrO1xyXG4gICAgfSk7XHJcbiAgICBmb3IgKHZhciBpID0gbmV3QnJlYWtzLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tKVxyXG4gICAgICByZXBsYWNlUmFuZ2UoY20uZG9jLCB2YWwsIG5ld0JyZWFrc1tpXSwgUG9zKG5ld0JyZWFrc1tpXS5saW5lLCBuZXdCcmVha3NbaV0uY2ggKyB2YWwubGVuZ3RoKSlcclxuICB9KTtcclxuICBvcHRpb24oXCJzcGVjaWFsQ2hhcnNcIiwgL1tcXHRcXHUwMDAwLVxcdTAwMTlcXHUwMGFkXFx1MjAwYi1cXHUyMDBmXFx1MjAyOFxcdTIwMjlcXHVmZWZmXS9nLCBmdW5jdGlvbihjbSwgdmFsLCBvbGQpIHtcclxuICAgIGNtLnN0YXRlLnNwZWNpYWxDaGFycyA9IG5ldyBSZWdFeHAodmFsLnNvdXJjZSArICh2YWwudGVzdChcIlxcdFwiKSA/IFwiXCIgOiBcInxcXHRcIiksIFwiZ1wiKTtcclxuICAgIGlmIChvbGQgIT0gQ29kZU1pcnJvci5Jbml0KSBjbS5yZWZyZXNoKCk7XHJcbiAgfSk7XHJcbiAgb3B0aW9uKFwic3BlY2lhbENoYXJQbGFjZWhvbGRlclwiLCBkZWZhdWx0U3BlY2lhbENoYXJQbGFjZWhvbGRlciwgZnVuY3Rpb24oY20pIHtjbS5yZWZyZXNoKCk7fSwgdHJ1ZSk7XHJcbiAgb3B0aW9uKFwiZWxlY3RyaWNDaGFyc1wiLCB0cnVlKTtcclxuICBvcHRpb24oXCJpbnB1dFN0eWxlXCIsIG1vYmlsZSA/IFwiY29udGVudGVkaXRhYmxlXCIgOiBcInRleHRhcmVhXCIsIGZ1bmN0aW9uKCkge1xyXG4gICAgdGhyb3cgbmV3IEVycm9yKFwiaW5wdXRTdHlsZSBjYW4gbm90ICh5ZXQpIGJlIGNoYW5nZWQgaW4gYSBydW5uaW5nIGVkaXRvclwiKTsgLy8gRklYTUVcclxuICB9LCB0cnVlKTtcclxuICBvcHRpb24oXCJydGxNb3ZlVmlzdWFsbHlcIiwgIXdpbmRvd3MpO1xyXG4gIG9wdGlvbihcIndob2xlTGluZVVwZGF0ZUJlZm9yZVwiLCB0cnVlKTtcclxuXHJcbiAgb3B0aW9uKFwidGhlbWVcIiwgXCJkZWZhdWx0XCIsIGZ1bmN0aW9uKGNtKSB7XHJcbiAgICB0aGVtZUNoYW5nZWQoY20pO1xyXG4gICAgZ3V0dGVyc0NoYW5nZWQoY20pO1xyXG4gIH0sIHRydWUpO1xyXG4gIG9wdGlvbihcImtleU1hcFwiLCBcImRlZmF1bHRcIiwgZnVuY3Rpb24oY20sIHZhbCwgb2xkKSB7XHJcbiAgICB2YXIgbmV4dCA9IGdldEtleU1hcCh2YWwpO1xyXG4gICAgdmFyIHByZXYgPSBvbGQgIT0gQ29kZU1pcnJvci5Jbml0ICYmIGdldEtleU1hcChvbGQpO1xyXG4gICAgaWYgKHByZXYgJiYgcHJldi5kZXRhY2gpIHByZXYuZGV0YWNoKGNtLCBuZXh0KTtcclxuICAgIGlmIChuZXh0LmF0dGFjaCkgbmV4dC5hdHRhY2goY20sIHByZXYgfHwgbnVsbCk7XHJcbiAgfSk7XHJcbiAgb3B0aW9uKFwiZXh0cmFLZXlzXCIsIG51bGwpO1xyXG5cclxuICBvcHRpb24oXCJsaW5lV3JhcHBpbmdcIiwgZmFsc2UsIHdyYXBwaW5nQ2hhbmdlZCwgdHJ1ZSk7XHJcbiAgb3B0aW9uKFwiZ3V0dGVyc1wiLCBbXSwgZnVuY3Rpb24oY20pIHtcclxuICAgIHNldEd1dHRlcnNGb3JMaW5lTnVtYmVycyhjbS5vcHRpb25zKTtcclxuICAgIGd1dHRlcnNDaGFuZ2VkKGNtKTtcclxuICB9LCB0cnVlKTtcclxuICBvcHRpb24oXCJmaXhlZEd1dHRlclwiLCB0cnVlLCBmdW5jdGlvbihjbSwgdmFsKSB7XHJcbiAgICBjbS5kaXNwbGF5Lmd1dHRlcnMuc3R5bGUubGVmdCA9IHZhbCA/IGNvbXBlbnNhdGVGb3JIU2Nyb2xsKGNtLmRpc3BsYXkpICsgXCJweFwiIDogXCIwXCI7XHJcbiAgICBjbS5yZWZyZXNoKCk7XHJcbiAgfSwgdHJ1ZSk7XHJcbiAgb3B0aW9uKFwiY292ZXJHdXR0ZXJOZXh0VG9TY3JvbGxiYXJcIiwgZmFsc2UsIGZ1bmN0aW9uKGNtKSB7dXBkYXRlU2Nyb2xsYmFycyhjbSk7fSwgdHJ1ZSk7XHJcbiAgb3B0aW9uKFwic2Nyb2xsYmFyU3R5bGVcIiwgXCJuYXRpdmVcIiwgZnVuY3Rpb24oY20pIHtcclxuICAgIGluaXRTY3JvbGxiYXJzKGNtKTtcclxuICAgIHVwZGF0ZVNjcm9sbGJhcnMoY20pO1xyXG4gICAgY20uZGlzcGxheS5zY3JvbGxiYXJzLnNldFNjcm9sbFRvcChjbS5kb2Muc2Nyb2xsVG9wKTtcclxuICAgIGNtLmRpc3BsYXkuc2Nyb2xsYmFycy5zZXRTY3JvbGxMZWZ0KGNtLmRvYy5zY3JvbGxMZWZ0KTtcclxuICB9LCB0cnVlKTtcclxuICBvcHRpb24oXCJsaW5lTnVtYmVyc1wiLCBmYWxzZSwgZnVuY3Rpb24oY20pIHtcclxuICAgIHNldEd1dHRlcnNGb3JMaW5lTnVtYmVycyhjbS5vcHRpb25zKTtcclxuICAgIGd1dHRlcnNDaGFuZ2VkKGNtKTtcclxuICB9LCB0cnVlKTtcclxuICBvcHRpb24oXCJmaXJzdExpbmVOdW1iZXJcIiwgMSwgZ3V0dGVyc0NoYW5nZWQsIHRydWUpO1xyXG4gIG9wdGlvbihcImxpbmVOdW1iZXJGb3JtYXR0ZXJcIiwgZnVuY3Rpb24oaW50ZWdlcikge3JldHVybiBpbnRlZ2VyO30sIGd1dHRlcnNDaGFuZ2VkLCB0cnVlKTtcclxuICBvcHRpb24oXCJzaG93Q3Vyc29yV2hlblNlbGVjdGluZ1wiLCBmYWxzZSwgdXBkYXRlU2VsZWN0aW9uLCB0cnVlKTtcclxuXHJcbiAgb3B0aW9uKFwicmVzZXRTZWxlY3Rpb25PbkNvbnRleHRNZW51XCIsIHRydWUpO1xyXG4gIG9wdGlvbihcImxpbmVXaXNlQ29weUN1dFwiLCB0cnVlKTtcclxuXHJcbiAgb3B0aW9uKFwicmVhZE9ubHlcIiwgZmFsc2UsIGZ1bmN0aW9uKGNtLCB2YWwpIHtcclxuICAgIGlmICh2YWwgPT0gXCJub2N1cnNvclwiKSB7XHJcbiAgICAgIG9uQmx1cihjbSk7XHJcbiAgICAgIGNtLmRpc3BsYXkuaW5wdXQuYmx1cigpO1xyXG4gICAgICBjbS5kaXNwbGF5LmRpc2FibGVkID0gdHJ1ZTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGNtLmRpc3BsYXkuZGlzYWJsZWQgPSBmYWxzZTtcclxuICAgIH1cclxuICAgIGNtLmRpc3BsYXkuaW5wdXQucmVhZE9ubHlDaGFuZ2VkKHZhbClcclxuICB9KTtcclxuICBvcHRpb24oXCJkaXNhYmxlSW5wdXRcIiwgZmFsc2UsIGZ1bmN0aW9uKGNtLCB2YWwpIHtpZiAoIXZhbCkgY20uZGlzcGxheS5pbnB1dC5yZXNldCgpO30sIHRydWUpO1xyXG4gIG9wdGlvbihcImRyYWdEcm9wXCIsIHRydWUsIGRyYWdEcm9wQ2hhbmdlZCk7XHJcbiAgb3B0aW9uKFwiYWxsb3dEcm9wRmlsZVR5cGVzXCIsIG51bGwpO1xyXG5cclxuICBvcHRpb24oXCJjdXJzb3JCbGlua1JhdGVcIiwgNTMwKTtcclxuICBvcHRpb24oXCJjdXJzb3JTY3JvbGxNYXJnaW5cIiwgMCk7XHJcbiAgb3B0aW9uKFwiY3Vyc29ySGVpZ2h0XCIsIDEsIHVwZGF0ZVNlbGVjdGlvbiwgdHJ1ZSk7XHJcbiAgb3B0aW9uKFwic2luZ2xlQ3Vyc29ySGVpZ2h0UGVyTGluZVwiLCB0cnVlLCB1cGRhdGVTZWxlY3Rpb24sIHRydWUpO1xyXG4gIG9wdGlvbihcIndvcmtUaW1lXCIsIDEwMCk7XHJcbiAgb3B0aW9uKFwid29ya0RlbGF5XCIsIDEwMCk7XHJcbiAgb3B0aW9uKFwiZmxhdHRlblNwYW5zXCIsIHRydWUsIHJlc2V0TW9kZVN0YXRlLCB0cnVlKTtcclxuICBvcHRpb24oXCJhZGRNb2RlQ2xhc3NcIiwgZmFsc2UsIHJlc2V0TW9kZVN0YXRlLCB0cnVlKTtcclxuICBvcHRpb24oXCJwb2xsSW50ZXJ2YWxcIiwgMTAwKTtcclxuICBvcHRpb24oXCJ1bmRvRGVwdGhcIiwgMjAwLCBmdW5jdGlvbihjbSwgdmFsKXtjbS5kb2MuaGlzdG9yeS51bmRvRGVwdGggPSB2YWw7fSk7XHJcbiAgb3B0aW9uKFwiaGlzdG9yeUV2ZW50RGVsYXlcIiwgMTI1MCk7XHJcbiAgb3B0aW9uKFwidmlld3BvcnRNYXJnaW5cIiwgMTAsIGZ1bmN0aW9uKGNtKXtjbS5yZWZyZXNoKCk7fSwgdHJ1ZSk7XHJcbiAgb3B0aW9uKFwibWF4SGlnaGxpZ2h0TGVuZ3RoXCIsIDEwMDAwLCByZXNldE1vZGVTdGF0ZSwgdHJ1ZSk7XHJcbiAgb3B0aW9uKFwibW92ZUlucHV0V2l0aEN1cnNvclwiLCB0cnVlLCBmdW5jdGlvbihjbSwgdmFsKSB7XHJcbiAgICBpZiAoIXZhbCkgY20uZGlzcGxheS5pbnB1dC5yZXNldFBvc2l0aW9uKCk7XHJcbiAgfSk7XHJcblxyXG4gIG9wdGlvbihcInRhYmluZGV4XCIsIG51bGwsIGZ1bmN0aW9uKGNtLCB2YWwpIHtcclxuICAgIGNtLmRpc3BsYXkuaW5wdXQuZ2V0RmllbGQoKS50YWJJbmRleCA9IHZhbCB8fCBcIlwiO1xyXG4gIH0pO1xyXG4gIG9wdGlvbihcImF1dG9mb2N1c1wiLCBudWxsKTtcclxuXHJcbiAgLy8gTU9ERSBERUZJTklUSU9OIEFORCBRVUVSWUlOR1xyXG5cclxuICAvLyBLbm93biBtb2RlcywgYnkgbmFtZSBhbmQgYnkgTUlNRVxyXG4gIHZhciBtb2RlcyA9IENvZGVNaXJyb3IubW9kZXMgPSB7fSwgbWltZU1vZGVzID0gQ29kZU1pcnJvci5taW1lTW9kZXMgPSB7fTtcclxuXHJcbiAgLy8gRXh0cmEgYXJndW1lbnRzIGFyZSBzdG9yZWQgYXMgdGhlIG1vZGUncyBkZXBlbmRlbmNpZXMsIHdoaWNoIGlzXHJcbiAgLy8gdXNlZCBieSAobGVnYWN5KSBtZWNoYW5pc21zIGxpa2UgbG9hZG1vZGUuanMgdG8gYXV0b21hdGljYWxseVxyXG4gIC8vIGxvYWQgYSBtb2RlLiAoUHJlZmVycmVkIG1lY2hhbmlzbSBpcyB0aGUgcmVxdWlyZS9kZWZpbmUgY2FsbHMuKVxyXG4gIENvZGVNaXJyb3IuZGVmaW5lTW9kZSA9IGZ1bmN0aW9uKG5hbWUsIG1vZGUpIHtcclxuICAgIGlmICghQ29kZU1pcnJvci5kZWZhdWx0cy5tb2RlICYmIG5hbWUgIT0gXCJudWxsXCIpIENvZGVNaXJyb3IuZGVmYXVsdHMubW9kZSA9IG5hbWU7XHJcbiAgICBpZiAoYXJndW1lbnRzLmxlbmd0aCA+IDIpXHJcbiAgICAgIG1vZGUuZGVwZW5kZW5jaWVzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzLCAyKTtcclxuICAgIG1vZGVzW25hbWVdID0gbW9kZTtcclxuICB9O1xyXG5cclxuICBDb2RlTWlycm9yLmRlZmluZU1JTUUgPSBmdW5jdGlvbihtaW1lLCBzcGVjKSB7XHJcbiAgICBtaW1lTW9kZXNbbWltZV0gPSBzcGVjO1xyXG4gIH07XHJcblxyXG4gIC8vIEdpdmVuIGEgTUlNRSB0eXBlLCBhIHtuYW1lLCAuLi5vcHRpb25zfSBjb25maWcgb2JqZWN0LCBvciBhIG5hbWVcclxuICAvLyBzdHJpbmcsIHJldHVybiBhIG1vZGUgY29uZmlnIG9iamVjdC5cclxuICBDb2RlTWlycm9yLnJlc29sdmVNb2RlID0gZnVuY3Rpb24oc3BlYykge1xyXG4gICAgaWYgKHR5cGVvZiBzcGVjID09IFwic3RyaW5nXCIgJiYgbWltZU1vZGVzLmhhc093blByb3BlcnR5KHNwZWMpKSB7XHJcbiAgICAgIHNwZWMgPSBtaW1lTW9kZXNbc3BlY107XHJcbiAgICB9IGVsc2UgaWYgKHNwZWMgJiYgdHlwZW9mIHNwZWMubmFtZSA9PSBcInN0cmluZ1wiICYmIG1pbWVNb2Rlcy5oYXNPd25Qcm9wZXJ0eShzcGVjLm5hbWUpKSB7XHJcbiAgICAgIHZhciBmb3VuZCA9IG1pbWVNb2Rlc1tzcGVjLm5hbWVdO1xyXG4gICAgICBpZiAodHlwZW9mIGZvdW5kID09IFwic3RyaW5nXCIpIGZvdW5kID0ge25hbWU6IGZvdW5kfTtcclxuICAgICAgc3BlYyA9IGNyZWF0ZU9iaihmb3VuZCwgc3BlYyk7XHJcbiAgICAgIHNwZWMubmFtZSA9IGZvdW5kLm5hbWU7XHJcbiAgICB9IGVsc2UgaWYgKHR5cGVvZiBzcGVjID09IFwic3RyaW5nXCIgJiYgL15bXFx3XFwtXStcXC9bXFx3XFwtXStcXCt4bWwkLy50ZXN0KHNwZWMpKSB7XHJcbiAgICAgIHJldHVybiBDb2RlTWlycm9yLnJlc29sdmVNb2RlKFwiYXBwbGljYXRpb24veG1sXCIpO1xyXG4gICAgfVxyXG4gICAgaWYgKHR5cGVvZiBzcGVjID09IFwic3RyaW5nXCIpIHJldHVybiB7bmFtZTogc3BlY307XHJcbiAgICBlbHNlIHJldHVybiBzcGVjIHx8IHtuYW1lOiBcIm51bGxcIn07XHJcbiAgfTtcclxuXHJcbiAgLy8gR2l2ZW4gYSBtb2RlIHNwZWMgKGFueXRoaW5nIHRoYXQgcmVzb2x2ZU1vZGUgYWNjZXB0cyksIGZpbmQgYW5kXHJcbiAgLy8gaW5pdGlhbGl6ZSBhbiBhY3R1YWwgbW9kZSBvYmplY3QuXHJcbiAgQ29kZU1pcnJvci5nZXRNb2RlID0gZnVuY3Rpb24ob3B0aW9ucywgc3BlYykge1xyXG4gICAgdmFyIHNwZWMgPSBDb2RlTWlycm9yLnJlc29sdmVNb2RlKHNwZWMpO1xyXG4gICAgdmFyIG1mYWN0b3J5ID0gbW9kZXNbc3BlYy5uYW1lXTtcclxuICAgIGlmICghbWZhY3RvcnkpIHJldHVybiBDb2RlTWlycm9yLmdldE1vZGUob3B0aW9ucywgXCJ0ZXh0L3BsYWluXCIpO1xyXG4gICAgdmFyIG1vZGVPYmogPSBtZmFjdG9yeShvcHRpb25zLCBzcGVjKTtcclxuICAgIGlmIChtb2RlRXh0ZW5zaW9ucy5oYXNPd25Qcm9wZXJ0eShzcGVjLm5hbWUpKSB7XHJcbiAgICAgIHZhciBleHRzID0gbW9kZUV4dGVuc2lvbnNbc3BlYy5uYW1lXTtcclxuICAgICAgZm9yICh2YXIgcHJvcCBpbiBleHRzKSB7XHJcbiAgICAgICAgaWYgKCFleHRzLmhhc093blByb3BlcnR5KHByb3ApKSBjb250aW51ZTtcclxuICAgICAgICBpZiAobW9kZU9iai5oYXNPd25Qcm9wZXJ0eShwcm9wKSkgbW9kZU9ialtcIl9cIiArIHByb3BdID0gbW9kZU9ialtwcm9wXTtcclxuICAgICAgICBtb2RlT2JqW3Byb3BdID0gZXh0c1twcm9wXTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgbW9kZU9iai5uYW1lID0gc3BlYy5uYW1lO1xyXG4gICAgaWYgKHNwZWMuaGVscGVyVHlwZSkgbW9kZU9iai5oZWxwZXJUeXBlID0gc3BlYy5oZWxwZXJUeXBlO1xyXG4gICAgaWYgKHNwZWMubW9kZVByb3BzKSBmb3IgKHZhciBwcm9wIGluIHNwZWMubW9kZVByb3BzKVxyXG4gICAgICBtb2RlT2JqW3Byb3BdID0gc3BlYy5tb2RlUHJvcHNbcHJvcF07XHJcblxyXG4gICAgcmV0dXJuIG1vZGVPYmo7XHJcbiAgfTtcclxuXHJcbiAgLy8gTWluaW1hbCBkZWZhdWx0IG1vZGUuXHJcbiAgQ29kZU1pcnJvci5kZWZpbmVNb2RlKFwibnVsbFwiLCBmdW5jdGlvbigpIHtcclxuICAgIHJldHVybiB7dG9rZW46IGZ1bmN0aW9uKHN0cmVhbSkge3N0cmVhbS5za2lwVG9FbmQoKTt9fTtcclxuICB9KTtcclxuICBDb2RlTWlycm9yLmRlZmluZU1JTUUoXCJ0ZXh0L3BsYWluXCIsIFwibnVsbFwiKTtcclxuXHJcbiAgLy8gVGhpcyBjYW4gYmUgdXNlZCB0byBhdHRhY2ggcHJvcGVydGllcyB0byBtb2RlIG9iamVjdHMgZnJvbVxyXG4gIC8vIG91dHNpZGUgdGhlIGFjdHVhbCBtb2RlIGRlZmluaXRpb24uXHJcbiAgdmFyIG1vZGVFeHRlbnNpb25zID0gQ29kZU1pcnJvci5tb2RlRXh0ZW5zaW9ucyA9IHt9O1xyXG4gIENvZGVNaXJyb3IuZXh0ZW5kTW9kZSA9IGZ1bmN0aW9uKG1vZGUsIHByb3BlcnRpZXMpIHtcclxuICAgIHZhciBleHRzID0gbW9kZUV4dGVuc2lvbnMuaGFzT3duUHJvcGVydHkobW9kZSkgPyBtb2RlRXh0ZW5zaW9uc1ttb2RlXSA6IChtb2RlRXh0ZW5zaW9uc1ttb2RlXSA9IHt9KTtcclxuICAgIGNvcHlPYmoocHJvcGVydGllcywgZXh0cyk7XHJcbiAgfTtcclxuXHJcbiAgLy8gRVhURU5TSU9OU1xyXG5cclxuICBDb2RlTWlycm9yLmRlZmluZUV4dGVuc2lvbiA9IGZ1bmN0aW9uKG5hbWUsIGZ1bmMpIHtcclxuICAgIENvZGVNaXJyb3IucHJvdG90eXBlW25hbWVdID0gZnVuYztcclxuICB9O1xyXG4gIENvZGVNaXJyb3IuZGVmaW5lRG9jRXh0ZW5zaW9uID0gZnVuY3Rpb24obmFtZSwgZnVuYykge1xyXG4gICAgRG9jLnByb3RvdHlwZVtuYW1lXSA9IGZ1bmM7XHJcbiAgfTtcclxuICBDb2RlTWlycm9yLmRlZmluZU9wdGlvbiA9IG9wdGlvbjtcclxuXHJcbiAgdmFyIGluaXRIb29rcyA9IFtdO1xyXG4gIENvZGVNaXJyb3IuZGVmaW5lSW5pdEhvb2sgPSBmdW5jdGlvbihmKSB7aW5pdEhvb2tzLnB1c2goZik7fTtcclxuXHJcbiAgdmFyIGhlbHBlcnMgPSBDb2RlTWlycm9yLmhlbHBlcnMgPSB7fTtcclxuICBDb2RlTWlycm9yLnJlZ2lzdGVySGVscGVyID0gZnVuY3Rpb24odHlwZSwgbmFtZSwgdmFsdWUpIHtcclxuICAgIGlmICghaGVscGVycy5oYXNPd25Qcm9wZXJ0eSh0eXBlKSkgaGVscGVyc1t0eXBlXSA9IENvZGVNaXJyb3JbdHlwZV0gPSB7X2dsb2JhbDogW119O1xyXG4gICAgaGVscGVyc1t0eXBlXVtuYW1lXSA9IHZhbHVlO1xyXG4gIH07XHJcbiAgQ29kZU1pcnJvci5yZWdpc3Rlckdsb2JhbEhlbHBlciA9IGZ1bmN0aW9uKHR5cGUsIG5hbWUsIHByZWRpY2F0ZSwgdmFsdWUpIHtcclxuICAgIENvZGVNaXJyb3IucmVnaXN0ZXJIZWxwZXIodHlwZSwgbmFtZSwgdmFsdWUpO1xyXG4gICAgaGVscGVyc1t0eXBlXS5fZ2xvYmFsLnB1c2goe3ByZWQ6IHByZWRpY2F0ZSwgdmFsOiB2YWx1ZX0pO1xyXG4gIH07XHJcblxyXG4gIC8vIE1PREUgU1RBVEUgSEFORExJTkdcclxuXHJcbiAgLy8gVXRpbGl0eSBmdW5jdGlvbnMgZm9yIHdvcmtpbmcgd2l0aCBzdGF0ZS4gRXhwb3J0ZWQgYmVjYXVzZSBuZXN0ZWRcclxuICAvLyBtb2RlcyBuZWVkIHRvIGRvIHRoaXMgZm9yIHRoZWlyIGlubmVyIG1vZGVzLlxyXG5cclxuICB2YXIgY29weVN0YXRlID0gQ29kZU1pcnJvci5jb3B5U3RhdGUgPSBmdW5jdGlvbihtb2RlLCBzdGF0ZSkge1xyXG4gICAgaWYgKHN0YXRlID09PSB0cnVlKSByZXR1cm4gc3RhdGU7XHJcbiAgICBpZiAobW9kZS5jb3B5U3RhdGUpIHJldHVybiBtb2RlLmNvcHlTdGF0ZShzdGF0ZSk7XHJcbiAgICB2YXIgbnN0YXRlID0ge307XHJcbiAgICBmb3IgKHZhciBuIGluIHN0YXRlKSB7XHJcbiAgICAgIHZhciB2YWwgPSBzdGF0ZVtuXTtcclxuICAgICAgaWYgKHZhbCBpbnN0YW5jZW9mIEFycmF5KSB2YWwgPSB2YWwuY29uY2F0KFtdKTtcclxuICAgICAgbnN0YXRlW25dID0gdmFsO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIG5zdGF0ZTtcclxuICB9O1xyXG5cclxuICB2YXIgc3RhcnRTdGF0ZSA9IENvZGVNaXJyb3Iuc3RhcnRTdGF0ZSA9IGZ1bmN0aW9uKG1vZGUsIGExLCBhMikge1xyXG4gICAgcmV0dXJuIG1vZGUuc3RhcnRTdGF0ZSA/IG1vZGUuc3RhcnRTdGF0ZShhMSwgYTIpIDogdHJ1ZTtcclxuICB9O1xyXG5cclxuICAvLyBHaXZlbiBhIG1vZGUgYW5kIGEgc3RhdGUgKGZvciB0aGF0IG1vZGUpLCBmaW5kIHRoZSBpbm5lciBtb2RlIGFuZFxyXG4gIC8vIHN0YXRlIGF0IHRoZSBwb3NpdGlvbiB0aGF0IHRoZSBzdGF0ZSByZWZlcnMgdG8uXHJcbiAgQ29kZU1pcnJvci5pbm5lck1vZGUgPSBmdW5jdGlvbihtb2RlLCBzdGF0ZSkge1xyXG4gICAgd2hpbGUgKG1vZGUuaW5uZXJNb2RlKSB7XHJcbiAgICAgIHZhciBpbmZvID0gbW9kZS5pbm5lck1vZGUoc3RhdGUpO1xyXG4gICAgICBpZiAoIWluZm8gfHwgaW5mby5tb2RlID09IG1vZGUpIGJyZWFrO1xyXG4gICAgICBzdGF0ZSA9IGluZm8uc3RhdGU7XHJcbiAgICAgIG1vZGUgPSBpbmZvLm1vZGU7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gaW5mbyB8fCB7bW9kZTogbW9kZSwgc3RhdGU6IHN0YXRlfTtcclxuICB9O1xyXG5cclxuICAvLyBTVEFOREFSRCBDT01NQU5EU1xyXG5cclxuICAvLyBDb21tYW5kcyBhcmUgcGFyYW1ldGVyLWxlc3MgYWN0aW9ucyB0aGF0IGNhbiBiZSBwZXJmb3JtZWQgb24gYW5cclxuICAvLyBlZGl0b3IsIG1vc3RseSB1c2VkIGZvciBrZXliaW5kaW5ncy5cclxuICB2YXIgY29tbWFuZHMgPSBDb2RlTWlycm9yLmNvbW1hbmRzID0ge1xyXG4gICAgc2VsZWN0QWxsOiBmdW5jdGlvbihjbSkge2NtLnNldFNlbGVjdGlvbihQb3MoY20uZmlyc3RMaW5lKCksIDApLCBQb3MoY20ubGFzdExpbmUoKSksIHNlbF9kb250U2Nyb2xsKTt9LFxyXG4gICAgc2luZ2xlU2VsZWN0aW9uOiBmdW5jdGlvbihjbSkge1xyXG4gICAgICBjbS5zZXRTZWxlY3Rpb24oY20uZ2V0Q3Vyc29yKFwiYW5jaG9yXCIpLCBjbS5nZXRDdXJzb3IoXCJoZWFkXCIpLCBzZWxfZG9udFNjcm9sbCk7XHJcbiAgICB9LFxyXG4gICAga2lsbExpbmU6IGZ1bmN0aW9uKGNtKSB7XHJcbiAgICAgIGRlbGV0ZU5lYXJTZWxlY3Rpb24oY20sIGZ1bmN0aW9uKHJhbmdlKSB7XHJcbiAgICAgICAgaWYgKHJhbmdlLmVtcHR5KCkpIHtcclxuICAgICAgICAgIHZhciBsZW4gPSBnZXRMaW5lKGNtLmRvYywgcmFuZ2UuaGVhZC5saW5lKS50ZXh0Lmxlbmd0aDtcclxuICAgICAgICAgIGlmIChyYW5nZS5oZWFkLmNoID09IGxlbiAmJiByYW5nZS5oZWFkLmxpbmUgPCBjbS5sYXN0TGluZSgpKVxyXG4gICAgICAgICAgICByZXR1cm4ge2Zyb206IHJhbmdlLmhlYWQsIHRvOiBQb3MocmFuZ2UuaGVhZC5saW5lICsgMSwgMCl9O1xyXG4gICAgICAgICAgZWxzZVxyXG4gICAgICAgICAgICByZXR1cm4ge2Zyb206IHJhbmdlLmhlYWQsIHRvOiBQb3MocmFuZ2UuaGVhZC5saW5lLCBsZW4pfTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgcmV0dXJuIHtmcm9tOiByYW5nZS5mcm9tKCksIHRvOiByYW5nZS50bygpfTtcclxuICAgICAgICB9XHJcbiAgICAgIH0pO1xyXG4gICAgfSxcclxuICAgIGRlbGV0ZUxpbmU6IGZ1bmN0aW9uKGNtKSB7XHJcbiAgICAgIGRlbGV0ZU5lYXJTZWxlY3Rpb24oY20sIGZ1bmN0aW9uKHJhbmdlKSB7XHJcbiAgICAgICAgcmV0dXJuIHtmcm9tOiBQb3MocmFuZ2UuZnJvbSgpLmxpbmUsIDApLFxyXG4gICAgICAgICAgICAgICAgdG86IGNsaXBQb3MoY20uZG9jLCBQb3MocmFuZ2UudG8oKS5saW5lICsgMSwgMCkpfTtcclxuICAgICAgfSk7XHJcbiAgICB9LFxyXG4gICAgZGVsTGluZUxlZnQ6IGZ1bmN0aW9uKGNtKSB7XHJcbiAgICAgIGRlbGV0ZU5lYXJTZWxlY3Rpb24oY20sIGZ1bmN0aW9uKHJhbmdlKSB7XHJcbiAgICAgICAgcmV0dXJuIHtmcm9tOiBQb3MocmFuZ2UuZnJvbSgpLmxpbmUsIDApLCB0bzogcmFuZ2UuZnJvbSgpfTtcclxuICAgICAgfSk7XHJcbiAgICB9LFxyXG4gICAgZGVsV3JhcHBlZExpbmVMZWZ0OiBmdW5jdGlvbihjbSkge1xyXG4gICAgICBkZWxldGVOZWFyU2VsZWN0aW9uKGNtLCBmdW5jdGlvbihyYW5nZSkge1xyXG4gICAgICAgIHZhciB0b3AgPSBjbS5jaGFyQ29vcmRzKHJhbmdlLmhlYWQsIFwiZGl2XCIpLnRvcCArIDU7XHJcbiAgICAgICAgdmFyIGxlZnRQb3MgPSBjbS5jb29yZHNDaGFyKHtsZWZ0OiAwLCB0b3A6IHRvcH0sIFwiZGl2XCIpO1xyXG4gICAgICAgIHJldHVybiB7ZnJvbTogbGVmdFBvcywgdG86IHJhbmdlLmZyb20oKX07XHJcbiAgICAgIH0pO1xyXG4gICAgfSxcclxuICAgIGRlbFdyYXBwZWRMaW5lUmlnaHQ6IGZ1bmN0aW9uKGNtKSB7XHJcbiAgICAgIGRlbGV0ZU5lYXJTZWxlY3Rpb24oY20sIGZ1bmN0aW9uKHJhbmdlKSB7XHJcbiAgICAgICAgdmFyIHRvcCA9IGNtLmNoYXJDb29yZHMocmFuZ2UuaGVhZCwgXCJkaXZcIikudG9wICsgNTtcclxuICAgICAgICB2YXIgcmlnaHRQb3MgPSBjbS5jb29yZHNDaGFyKHtsZWZ0OiBjbS5kaXNwbGF5LmxpbmVEaXYub2Zmc2V0V2lkdGggKyAxMDAsIHRvcDogdG9wfSwgXCJkaXZcIik7XHJcbiAgICAgICAgcmV0dXJuIHtmcm9tOiByYW5nZS5mcm9tKCksIHRvOiByaWdodFBvcyB9O1xyXG4gICAgICB9KTtcclxuICAgIH0sXHJcbiAgICB1bmRvOiBmdW5jdGlvbihjbSkge2NtLnVuZG8oKTt9LFxyXG4gICAgcmVkbzogZnVuY3Rpb24oY20pIHtjbS5yZWRvKCk7fSxcclxuICAgIHVuZG9TZWxlY3Rpb246IGZ1bmN0aW9uKGNtKSB7Y20udW5kb1NlbGVjdGlvbigpO30sXHJcbiAgICByZWRvU2VsZWN0aW9uOiBmdW5jdGlvbihjbSkge2NtLnJlZG9TZWxlY3Rpb24oKTt9LFxyXG4gICAgZ29Eb2NTdGFydDogZnVuY3Rpb24oY20pIHtjbS5leHRlbmRTZWxlY3Rpb24oUG9zKGNtLmZpcnN0TGluZSgpLCAwKSk7fSxcclxuICAgIGdvRG9jRW5kOiBmdW5jdGlvbihjbSkge2NtLmV4dGVuZFNlbGVjdGlvbihQb3MoY20ubGFzdExpbmUoKSkpO30sXHJcbiAgICBnb0xpbmVTdGFydDogZnVuY3Rpb24oY20pIHtcclxuICAgICAgY20uZXh0ZW5kU2VsZWN0aW9uc0J5KGZ1bmN0aW9uKHJhbmdlKSB7IHJldHVybiBsaW5lU3RhcnQoY20sIHJhbmdlLmhlYWQubGluZSk7IH0sXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7b3JpZ2luOiBcIittb3ZlXCIsIGJpYXM6IDF9KTtcclxuICAgIH0sXHJcbiAgICBnb0xpbmVTdGFydFNtYXJ0OiBmdW5jdGlvbihjbSkge1xyXG4gICAgICBjbS5leHRlbmRTZWxlY3Rpb25zQnkoZnVuY3Rpb24ocmFuZ2UpIHtcclxuICAgICAgICByZXR1cm4gbGluZVN0YXJ0U21hcnQoY20sIHJhbmdlLmhlYWQpO1xyXG4gICAgICB9LCB7b3JpZ2luOiBcIittb3ZlXCIsIGJpYXM6IDF9KTtcclxuICAgIH0sXHJcbiAgICBnb0xpbmVFbmQ6IGZ1bmN0aW9uKGNtKSB7XHJcbiAgICAgIGNtLmV4dGVuZFNlbGVjdGlvbnNCeShmdW5jdGlvbihyYW5nZSkgeyByZXR1cm4gbGluZUVuZChjbSwgcmFuZ2UuaGVhZC5saW5lKTsgfSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtvcmlnaW46IFwiK21vdmVcIiwgYmlhczogLTF9KTtcclxuICAgIH0sXHJcbiAgICBnb0xpbmVSaWdodDogZnVuY3Rpb24oY20pIHtcclxuICAgICAgY20uZXh0ZW5kU2VsZWN0aW9uc0J5KGZ1bmN0aW9uKHJhbmdlKSB7XHJcbiAgICAgICAgdmFyIHRvcCA9IGNtLmNoYXJDb29yZHMocmFuZ2UuaGVhZCwgXCJkaXZcIikudG9wICsgNTtcclxuICAgICAgICByZXR1cm4gY20uY29vcmRzQ2hhcih7bGVmdDogY20uZGlzcGxheS5saW5lRGl2Lm9mZnNldFdpZHRoICsgMTAwLCB0b3A6IHRvcH0sIFwiZGl2XCIpO1xyXG4gICAgICB9LCBzZWxfbW92ZSk7XHJcbiAgICB9LFxyXG4gICAgZ29MaW5lTGVmdDogZnVuY3Rpb24oY20pIHtcclxuICAgICAgY20uZXh0ZW5kU2VsZWN0aW9uc0J5KGZ1bmN0aW9uKHJhbmdlKSB7XHJcbiAgICAgICAgdmFyIHRvcCA9IGNtLmNoYXJDb29yZHMocmFuZ2UuaGVhZCwgXCJkaXZcIikudG9wICsgNTtcclxuICAgICAgICByZXR1cm4gY20uY29vcmRzQ2hhcih7bGVmdDogMCwgdG9wOiB0b3B9LCBcImRpdlwiKTtcclxuICAgICAgfSwgc2VsX21vdmUpO1xyXG4gICAgfSxcclxuICAgIGdvTGluZUxlZnRTbWFydDogZnVuY3Rpb24oY20pIHtcclxuICAgICAgY20uZXh0ZW5kU2VsZWN0aW9uc0J5KGZ1bmN0aW9uKHJhbmdlKSB7XHJcbiAgICAgICAgdmFyIHRvcCA9IGNtLmNoYXJDb29yZHMocmFuZ2UuaGVhZCwgXCJkaXZcIikudG9wICsgNTtcclxuICAgICAgICB2YXIgcG9zID0gY20uY29vcmRzQ2hhcih7bGVmdDogMCwgdG9wOiB0b3B9LCBcImRpdlwiKTtcclxuICAgICAgICBpZiAocG9zLmNoIDwgY20uZ2V0TGluZShwb3MubGluZSkuc2VhcmNoKC9cXFMvKSkgcmV0dXJuIGxpbmVTdGFydFNtYXJ0KGNtLCByYW5nZS5oZWFkKTtcclxuICAgICAgICByZXR1cm4gcG9zO1xyXG4gICAgICB9LCBzZWxfbW92ZSk7XHJcbiAgICB9LFxyXG4gICAgZ29MaW5lVXA6IGZ1bmN0aW9uKGNtKSB7Y20ubW92ZVYoLTEsIFwibGluZVwiKTt9LFxyXG4gICAgZ29MaW5lRG93bjogZnVuY3Rpb24oY20pIHtjbS5tb3ZlVigxLCBcImxpbmVcIik7fSxcclxuICAgIGdvUGFnZVVwOiBmdW5jdGlvbihjbSkge2NtLm1vdmVWKC0xLCBcInBhZ2VcIik7fSxcclxuICAgIGdvUGFnZURvd246IGZ1bmN0aW9uKGNtKSB7Y20ubW92ZVYoMSwgXCJwYWdlXCIpO30sXHJcbiAgICBnb0NoYXJMZWZ0OiBmdW5jdGlvbihjbSkge2NtLm1vdmVIKC0xLCBcImNoYXJcIik7fSxcclxuICAgIGdvQ2hhclJpZ2h0OiBmdW5jdGlvbihjbSkge2NtLm1vdmVIKDEsIFwiY2hhclwiKTt9LFxyXG4gICAgZ29Db2x1bW5MZWZ0OiBmdW5jdGlvbihjbSkge2NtLm1vdmVIKC0xLCBcImNvbHVtblwiKTt9LFxyXG4gICAgZ29Db2x1bW5SaWdodDogZnVuY3Rpb24oY20pIHtjbS5tb3ZlSCgxLCBcImNvbHVtblwiKTt9LFxyXG4gICAgZ29Xb3JkTGVmdDogZnVuY3Rpb24oY20pIHtjbS5tb3ZlSCgtMSwgXCJ3b3JkXCIpO30sXHJcbiAgICBnb0dyb3VwUmlnaHQ6IGZ1bmN0aW9uKGNtKSB7Y20ubW92ZUgoMSwgXCJncm91cFwiKTt9LFxyXG4gICAgZ29Hcm91cExlZnQ6IGZ1bmN0aW9uKGNtKSB7Y20ubW92ZUgoLTEsIFwiZ3JvdXBcIik7fSxcclxuICAgIGdvV29yZFJpZ2h0OiBmdW5jdGlvbihjbSkge2NtLm1vdmVIKDEsIFwid29yZFwiKTt9LFxyXG4gICAgZGVsQ2hhckJlZm9yZTogZnVuY3Rpb24oY20pIHtjbS5kZWxldGVIKC0xLCBcImNoYXJcIik7fSxcclxuICAgIGRlbENoYXJBZnRlcjogZnVuY3Rpb24oY20pIHtjbS5kZWxldGVIKDEsIFwiY2hhclwiKTt9LFxyXG4gICAgZGVsV29yZEJlZm9yZTogZnVuY3Rpb24oY20pIHtjbS5kZWxldGVIKC0xLCBcIndvcmRcIik7fSxcclxuICAgIGRlbFdvcmRBZnRlcjogZnVuY3Rpb24oY20pIHtjbS5kZWxldGVIKDEsIFwid29yZFwiKTt9LFxyXG4gICAgZGVsR3JvdXBCZWZvcmU6IGZ1bmN0aW9uKGNtKSB7Y20uZGVsZXRlSCgtMSwgXCJncm91cFwiKTt9LFxyXG4gICAgZGVsR3JvdXBBZnRlcjogZnVuY3Rpb24oY20pIHtjbS5kZWxldGVIKDEsIFwiZ3JvdXBcIik7fSxcclxuICAgIGluZGVudEF1dG86IGZ1bmN0aW9uKGNtKSB7Y20uaW5kZW50U2VsZWN0aW9uKFwic21hcnRcIik7fSxcclxuICAgIGluZGVudE1vcmU6IGZ1bmN0aW9uKGNtKSB7Y20uaW5kZW50U2VsZWN0aW9uKFwiYWRkXCIpO30sXHJcbiAgICBpbmRlbnRMZXNzOiBmdW5jdGlvbihjbSkge2NtLmluZGVudFNlbGVjdGlvbihcInN1YnRyYWN0XCIpO30sXHJcbiAgICBpbnNlcnRUYWI6IGZ1bmN0aW9uKGNtKSB7Y20ucmVwbGFjZVNlbGVjdGlvbihcIlxcdFwiKTt9LFxyXG4gICAgaW5zZXJ0U29mdFRhYjogZnVuY3Rpb24oY20pIHtcclxuICAgICAgdmFyIHNwYWNlcyA9IFtdLCByYW5nZXMgPSBjbS5saXN0U2VsZWN0aW9ucygpLCB0YWJTaXplID0gY20ub3B0aW9ucy50YWJTaXplO1xyXG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHJhbmdlcy5sZW5ndGg7IGkrKykge1xyXG4gICAgICAgIHZhciBwb3MgPSByYW5nZXNbaV0uZnJvbSgpO1xyXG4gICAgICAgIHZhciBjb2wgPSBjb3VudENvbHVtbihjbS5nZXRMaW5lKHBvcy5saW5lKSwgcG9zLmNoLCB0YWJTaXplKTtcclxuICAgICAgICBzcGFjZXMucHVzaChuZXcgQXJyYXkodGFiU2l6ZSAtIGNvbCAlIHRhYlNpemUgKyAxKS5qb2luKFwiIFwiKSk7XHJcbiAgICAgIH1cclxuICAgICAgY20ucmVwbGFjZVNlbGVjdGlvbnMoc3BhY2VzKTtcclxuICAgIH0sXHJcbiAgICBkZWZhdWx0VGFiOiBmdW5jdGlvbihjbSkge1xyXG4gICAgICBpZiAoY20uc29tZXRoaW5nU2VsZWN0ZWQoKSkgY20uaW5kZW50U2VsZWN0aW9uKFwiYWRkXCIpO1xyXG4gICAgICBlbHNlIGNtLmV4ZWNDb21tYW5kKFwiaW5zZXJ0VGFiXCIpO1xyXG4gICAgfSxcclxuICAgIHRyYW5zcG9zZUNoYXJzOiBmdW5jdGlvbihjbSkge1xyXG4gICAgICBydW5Jbk9wKGNtLCBmdW5jdGlvbigpIHtcclxuICAgICAgICB2YXIgcmFuZ2VzID0gY20ubGlzdFNlbGVjdGlvbnMoKSwgbmV3U2VsID0gW107XHJcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCByYW5nZXMubGVuZ3RoOyBpKyspIHtcclxuICAgICAgICAgIHZhciBjdXIgPSByYW5nZXNbaV0uaGVhZCwgbGluZSA9IGdldExpbmUoY20uZG9jLCBjdXIubGluZSkudGV4dDtcclxuICAgICAgICAgIGlmIChsaW5lKSB7XHJcbiAgICAgICAgICAgIGlmIChjdXIuY2ggPT0gbGluZS5sZW5ndGgpIGN1ciA9IG5ldyBQb3MoY3VyLmxpbmUsIGN1ci5jaCAtIDEpO1xyXG4gICAgICAgICAgICBpZiAoY3VyLmNoID4gMCkge1xyXG4gICAgICAgICAgICAgIGN1ciA9IG5ldyBQb3MoY3VyLmxpbmUsIGN1ci5jaCArIDEpO1xyXG4gICAgICAgICAgICAgIGNtLnJlcGxhY2VSYW5nZShsaW5lLmNoYXJBdChjdXIuY2ggLSAxKSArIGxpbmUuY2hhckF0KGN1ci5jaCAtIDIpLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQb3MoY3VyLmxpbmUsIGN1ci5jaCAtIDIpLCBjdXIsIFwiK3RyYW5zcG9zZVwiKTtcclxuICAgICAgICAgICAgfSBlbHNlIGlmIChjdXIubGluZSA+IGNtLmRvYy5maXJzdCkge1xyXG4gICAgICAgICAgICAgIHZhciBwcmV2ID0gZ2V0TGluZShjbS5kb2MsIGN1ci5saW5lIC0gMSkudGV4dDtcclxuICAgICAgICAgICAgICBpZiAocHJldilcclxuICAgICAgICAgICAgICAgIGNtLnJlcGxhY2VSYW5nZShsaW5lLmNoYXJBdCgwKSArIGNtLmRvYy5saW5lU2VwYXJhdG9yKCkgK1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByZXYuY2hhckF0KHByZXYubGVuZ3RoIC0gMSksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUG9zKGN1ci5saW5lIC0gMSwgcHJldi5sZW5ndGggLSAxKSwgUG9zKGN1ci5saW5lLCAxKSwgXCIrdHJhbnNwb3NlXCIpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICBuZXdTZWwucHVzaChuZXcgUmFuZ2UoY3VyLCBjdXIpKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgY20uc2V0U2VsZWN0aW9ucyhuZXdTZWwpO1xyXG4gICAgICB9KTtcclxuICAgIH0sXHJcbiAgICBuZXdsaW5lQW5kSW5kZW50OiBmdW5jdGlvbihjbSkge1xyXG4gICAgICBydW5Jbk9wKGNtLCBmdW5jdGlvbigpIHtcclxuICAgICAgICB2YXIgbGVuID0gY20ubGlzdFNlbGVjdGlvbnMoKS5sZW5ndGg7XHJcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsZW47IGkrKykge1xyXG4gICAgICAgICAgdmFyIHJhbmdlID0gY20ubGlzdFNlbGVjdGlvbnMoKVtpXTtcclxuICAgICAgICAgIGNtLnJlcGxhY2VSYW5nZShjbS5kb2MubGluZVNlcGFyYXRvcigpLCByYW5nZS5hbmNob3IsIHJhbmdlLmhlYWQsIFwiK2lucHV0XCIpO1xyXG4gICAgICAgICAgY20uaW5kZW50TGluZShyYW5nZS5mcm9tKCkubGluZSArIDEsIG51bGwsIHRydWUpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBlbnN1cmVDdXJzb3JWaXNpYmxlKGNtKTtcclxuICAgICAgfSk7XHJcbiAgICB9LFxyXG4gICAgdG9nZ2xlT3ZlcndyaXRlOiBmdW5jdGlvbihjbSkge2NtLnRvZ2dsZU92ZXJ3cml0ZSgpO31cclxuICB9O1xyXG5cclxuXHJcbiAgLy8gU1RBTkRBUkQgS0VZTUFQU1xyXG5cclxuICB2YXIga2V5TWFwID0gQ29kZU1pcnJvci5rZXlNYXAgPSB7fTtcclxuXHJcbiAga2V5TWFwLmJhc2ljID0ge1xyXG4gICAgXCJMZWZ0XCI6IFwiZ29DaGFyTGVmdFwiLCBcIlJpZ2h0XCI6IFwiZ29DaGFyUmlnaHRcIiwgXCJVcFwiOiBcImdvTGluZVVwXCIsIFwiRG93blwiOiBcImdvTGluZURvd25cIixcclxuICAgIFwiRW5kXCI6IFwiZ29MaW5lRW5kXCIsIFwiSG9tZVwiOiBcImdvTGluZVN0YXJ0U21hcnRcIiwgXCJQYWdlVXBcIjogXCJnb1BhZ2VVcFwiLCBcIlBhZ2VEb3duXCI6IFwiZ29QYWdlRG93blwiLFxyXG4gICAgXCJEZWxldGVcIjogXCJkZWxDaGFyQWZ0ZXJcIiwgXCJCYWNrc3BhY2VcIjogXCJkZWxDaGFyQmVmb3JlXCIsIFwiU2hpZnQtQmFja3NwYWNlXCI6IFwiZGVsQ2hhckJlZm9yZVwiLFxyXG4gICAgXCJUYWJcIjogXCJkZWZhdWx0VGFiXCIsIFwiU2hpZnQtVGFiXCI6IFwiaW5kZW50QXV0b1wiLFxyXG4gICAgXCJFbnRlclwiOiBcIm5ld2xpbmVBbmRJbmRlbnRcIiwgXCJJbnNlcnRcIjogXCJ0b2dnbGVPdmVyd3JpdGVcIixcclxuICAgIFwiRXNjXCI6IFwic2luZ2xlU2VsZWN0aW9uXCJcclxuICB9O1xyXG4gIC8vIE5vdGUgdGhhdCB0aGUgc2F2ZSBhbmQgZmluZC1yZWxhdGVkIGNvbW1hbmRzIGFyZW4ndCBkZWZpbmVkIGJ5XHJcbiAgLy8gZGVmYXVsdC4gVXNlciBjb2RlIG9yIGFkZG9ucyBjYW4gZGVmaW5lIHRoZW0uIFVua25vd24gY29tbWFuZHNcclxuICAvLyBhcmUgc2ltcGx5IGlnbm9yZWQuXHJcbiAga2V5TWFwLnBjRGVmYXVsdCA9IHtcclxuICAgIFwiQ3RybC1BXCI6IFwic2VsZWN0QWxsXCIsIFwiQ3RybC1EXCI6IFwiZGVsZXRlTGluZVwiLCBcIkN0cmwtWlwiOiBcInVuZG9cIiwgXCJTaGlmdC1DdHJsLVpcIjogXCJyZWRvXCIsIFwiQ3RybC1ZXCI6IFwicmVkb1wiLFxyXG4gICAgXCJDdHJsLUhvbWVcIjogXCJnb0RvY1N0YXJ0XCIsIFwiQ3RybC1FbmRcIjogXCJnb0RvY0VuZFwiLCBcIkN0cmwtVXBcIjogXCJnb0xpbmVVcFwiLCBcIkN0cmwtRG93blwiOiBcImdvTGluZURvd25cIixcclxuICAgIFwiQ3RybC1MZWZ0XCI6IFwiZ29Hcm91cExlZnRcIiwgXCJDdHJsLVJpZ2h0XCI6IFwiZ29Hcm91cFJpZ2h0XCIsIFwiQWx0LUxlZnRcIjogXCJnb0xpbmVTdGFydFwiLCBcIkFsdC1SaWdodFwiOiBcImdvTGluZUVuZFwiLFxyXG4gICAgXCJDdHJsLUJhY2tzcGFjZVwiOiBcImRlbEdyb3VwQmVmb3JlXCIsIFwiQ3RybC1EZWxldGVcIjogXCJkZWxHcm91cEFmdGVyXCIsIFwiQ3RybC1TXCI6IFwic2F2ZVwiLCBcIkN0cmwtRlwiOiBcImZpbmRcIixcclxuICAgIFwiQ3RybC1HXCI6IFwiZmluZE5leHRcIiwgXCJTaGlmdC1DdHJsLUdcIjogXCJmaW5kUHJldlwiLCBcIlNoaWZ0LUN0cmwtRlwiOiBcInJlcGxhY2VcIiwgXCJTaGlmdC1DdHJsLVJcIjogXCJyZXBsYWNlQWxsXCIsXHJcbiAgICBcIkN0cmwtW1wiOiBcImluZGVudExlc3NcIiwgXCJDdHJsLV1cIjogXCJpbmRlbnRNb3JlXCIsXHJcbiAgICBcIkN0cmwtVVwiOiBcInVuZG9TZWxlY3Rpb25cIiwgXCJTaGlmdC1DdHJsLVVcIjogXCJyZWRvU2VsZWN0aW9uXCIsIFwiQWx0LVVcIjogXCJyZWRvU2VsZWN0aW9uXCIsXHJcbiAgICBmYWxsdGhyb3VnaDogXCJiYXNpY1wiXHJcbiAgfTtcclxuICAvLyBWZXJ5IGJhc2ljIHJlYWRsaW5lL2VtYWNzLXN0eWxlIGJpbmRpbmdzLCB3aGljaCBhcmUgc3RhbmRhcmQgb24gTWFjLlxyXG4gIGtleU1hcC5lbWFjc3kgPSB7XHJcbiAgICBcIkN0cmwtRlwiOiBcImdvQ2hhclJpZ2h0XCIsIFwiQ3RybC1CXCI6IFwiZ29DaGFyTGVmdFwiLCBcIkN0cmwtUFwiOiBcImdvTGluZVVwXCIsIFwiQ3RybC1OXCI6IFwiZ29MaW5lRG93blwiLFxyXG4gICAgXCJBbHQtRlwiOiBcImdvV29yZFJpZ2h0XCIsIFwiQWx0LUJcIjogXCJnb1dvcmRMZWZ0XCIsIFwiQ3RybC1BXCI6IFwiZ29MaW5lU3RhcnRcIiwgXCJDdHJsLUVcIjogXCJnb0xpbmVFbmRcIixcclxuICAgIFwiQ3RybC1WXCI6IFwiZ29QYWdlRG93blwiLCBcIlNoaWZ0LUN0cmwtVlwiOiBcImdvUGFnZVVwXCIsIFwiQ3RybC1EXCI6IFwiZGVsQ2hhckFmdGVyXCIsIFwiQ3RybC1IXCI6IFwiZGVsQ2hhckJlZm9yZVwiLFxyXG4gICAgXCJBbHQtRFwiOiBcImRlbFdvcmRBZnRlclwiLCBcIkFsdC1CYWNrc3BhY2VcIjogXCJkZWxXb3JkQmVmb3JlXCIsIFwiQ3RybC1LXCI6IFwia2lsbExpbmVcIiwgXCJDdHJsLVRcIjogXCJ0cmFuc3Bvc2VDaGFyc1wiXHJcbiAgfTtcclxuICBrZXlNYXAubWFjRGVmYXVsdCA9IHtcclxuICAgIFwiQ21kLUFcIjogXCJzZWxlY3RBbGxcIiwgXCJDbWQtRFwiOiBcImRlbGV0ZUxpbmVcIiwgXCJDbWQtWlwiOiBcInVuZG9cIiwgXCJTaGlmdC1DbWQtWlwiOiBcInJlZG9cIiwgXCJDbWQtWVwiOiBcInJlZG9cIixcclxuICAgIFwiQ21kLUhvbWVcIjogXCJnb0RvY1N0YXJ0XCIsIFwiQ21kLVVwXCI6IFwiZ29Eb2NTdGFydFwiLCBcIkNtZC1FbmRcIjogXCJnb0RvY0VuZFwiLCBcIkNtZC1Eb3duXCI6IFwiZ29Eb2NFbmRcIiwgXCJBbHQtTGVmdFwiOiBcImdvR3JvdXBMZWZ0XCIsXHJcbiAgICBcIkFsdC1SaWdodFwiOiBcImdvR3JvdXBSaWdodFwiLCBcIkNtZC1MZWZ0XCI6IFwiZ29MaW5lTGVmdFwiLCBcIkNtZC1SaWdodFwiOiBcImdvTGluZVJpZ2h0XCIsIFwiQWx0LUJhY2tzcGFjZVwiOiBcImRlbEdyb3VwQmVmb3JlXCIsXHJcbiAgICBcIkN0cmwtQWx0LUJhY2tzcGFjZVwiOiBcImRlbEdyb3VwQWZ0ZXJcIiwgXCJBbHQtRGVsZXRlXCI6IFwiZGVsR3JvdXBBZnRlclwiLCBcIkNtZC1TXCI6IFwic2F2ZVwiLCBcIkNtZC1GXCI6IFwiZmluZFwiLFxyXG4gICAgXCJDbWQtR1wiOiBcImZpbmROZXh0XCIsIFwiU2hpZnQtQ21kLUdcIjogXCJmaW5kUHJldlwiLCBcIkNtZC1BbHQtRlwiOiBcInJlcGxhY2VcIiwgXCJTaGlmdC1DbWQtQWx0LUZcIjogXCJyZXBsYWNlQWxsXCIsXHJcbiAgICBcIkNtZC1bXCI6IFwiaW5kZW50TGVzc1wiLCBcIkNtZC1dXCI6IFwiaW5kZW50TW9yZVwiLCBcIkNtZC1CYWNrc3BhY2VcIjogXCJkZWxXcmFwcGVkTGluZUxlZnRcIiwgXCJDbWQtRGVsZXRlXCI6IFwiZGVsV3JhcHBlZExpbmVSaWdodFwiLFxyXG4gICAgXCJDbWQtVVwiOiBcInVuZG9TZWxlY3Rpb25cIiwgXCJTaGlmdC1DbWQtVVwiOiBcInJlZG9TZWxlY3Rpb25cIiwgXCJDdHJsLVVwXCI6IFwiZ29Eb2NTdGFydFwiLCBcIkN0cmwtRG93blwiOiBcImdvRG9jRW5kXCIsXHJcbiAgICBmYWxsdGhyb3VnaDogW1wiYmFzaWNcIiwgXCJlbWFjc3lcIl1cclxuICB9O1xyXG4gIGtleU1hcFtcImRlZmF1bHRcIl0gPSBtYWMgPyBrZXlNYXAubWFjRGVmYXVsdCA6IGtleU1hcC5wY0RlZmF1bHQ7XHJcblxyXG4gIC8vIEtFWU1BUCBESVNQQVRDSFxyXG5cclxuICBmdW5jdGlvbiBub3JtYWxpemVLZXlOYW1lKG5hbWUpIHtcclxuICAgIHZhciBwYXJ0cyA9IG5hbWUuc3BsaXQoLy0oPyEkKS8pLCBuYW1lID0gcGFydHNbcGFydHMubGVuZ3RoIC0gMV07XHJcbiAgICB2YXIgYWx0LCBjdHJsLCBzaGlmdCwgY21kO1xyXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBwYXJ0cy5sZW5ndGggLSAxOyBpKyspIHtcclxuICAgICAgdmFyIG1vZCA9IHBhcnRzW2ldO1xyXG4gICAgICBpZiAoL14oY21kfG1ldGF8bSkkL2kudGVzdChtb2QpKSBjbWQgPSB0cnVlO1xyXG4gICAgICBlbHNlIGlmICgvXmEobHQpPyQvaS50ZXN0KG1vZCkpIGFsdCA9IHRydWU7XHJcbiAgICAgIGVsc2UgaWYgKC9eKGN8Y3RybHxjb250cm9sKSQvaS50ZXN0KG1vZCkpIGN0cmwgPSB0cnVlO1xyXG4gICAgICBlbHNlIGlmICgvXnMoaGlmdCkkL2kudGVzdChtb2QpKSBzaGlmdCA9IHRydWU7XHJcbiAgICAgIGVsc2UgdGhyb3cgbmV3IEVycm9yKFwiVW5yZWNvZ25pemVkIG1vZGlmaWVyIG5hbWU6IFwiICsgbW9kKTtcclxuICAgIH1cclxuICAgIGlmIChhbHQpIG5hbWUgPSBcIkFsdC1cIiArIG5hbWU7XHJcbiAgICBpZiAoY3RybCkgbmFtZSA9IFwiQ3RybC1cIiArIG5hbWU7XHJcbiAgICBpZiAoY21kKSBuYW1lID0gXCJDbWQtXCIgKyBuYW1lO1xyXG4gICAgaWYgKHNoaWZ0KSBuYW1lID0gXCJTaGlmdC1cIiArIG5hbWU7XHJcbiAgICByZXR1cm4gbmFtZTtcclxuICB9XHJcblxyXG4gIC8vIFRoaXMgaXMgYSBrbHVkZ2UgdG8ga2VlcCBrZXltYXBzIG1vc3RseSB3b3JraW5nIGFzIHJhdyBvYmplY3RzXHJcbiAgLy8gKGJhY2t3YXJkcyBjb21wYXRpYmlsaXR5KSB3aGlsZSBhdCB0aGUgc2FtZSB0aW1lIHN1cHBvcnQgZmVhdHVyZXNcclxuICAvLyBsaWtlIG5vcm1hbGl6YXRpb24gYW5kIG11bHRpLXN0cm9rZSBrZXkgYmluZGluZ3MuIEl0IGNvbXBpbGVzIGFcclxuICAvLyBuZXcgbm9ybWFsaXplZCBrZXltYXAsIGFuZCB0aGVuIHVwZGF0ZXMgdGhlIG9sZCBvYmplY3QgdG8gcmVmbGVjdFxyXG4gIC8vIHRoaXMuXHJcbiAgQ29kZU1pcnJvci5ub3JtYWxpemVLZXlNYXAgPSBmdW5jdGlvbihrZXltYXApIHtcclxuICAgIHZhciBjb3B5ID0ge307XHJcbiAgICBmb3IgKHZhciBrZXluYW1lIGluIGtleW1hcCkgaWYgKGtleW1hcC5oYXNPd25Qcm9wZXJ0eShrZXluYW1lKSkge1xyXG4gICAgICB2YXIgdmFsdWUgPSBrZXltYXBba2V5bmFtZV07XHJcbiAgICAgIGlmICgvXihuYW1lfGZhbGx0aHJvdWdofChkZXxhdCl0YWNoKSQvLnRlc3Qoa2V5bmFtZSkpIGNvbnRpbnVlO1xyXG4gICAgICBpZiAodmFsdWUgPT0gXCIuLi5cIikgeyBkZWxldGUga2V5bWFwW2tleW5hbWVdOyBjb250aW51ZTsgfVxyXG5cclxuICAgICAgdmFyIGtleXMgPSBtYXAoa2V5bmFtZS5zcGxpdChcIiBcIiksIG5vcm1hbGl6ZUtleU5hbWUpO1xyXG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGtleXMubGVuZ3RoOyBpKyspIHtcclxuICAgICAgICB2YXIgdmFsLCBuYW1lO1xyXG4gICAgICAgIGlmIChpID09IGtleXMubGVuZ3RoIC0gMSkge1xyXG4gICAgICAgICAgbmFtZSA9IGtleXMuam9pbihcIiBcIik7XHJcbiAgICAgICAgICB2YWwgPSB2YWx1ZTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgbmFtZSA9IGtleXMuc2xpY2UoMCwgaSArIDEpLmpvaW4oXCIgXCIpO1xyXG4gICAgICAgICAgdmFsID0gXCIuLi5cIjtcclxuICAgICAgICB9XHJcbiAgICAgICAgdmFyIHByZXYgPSBjb3B5W25hbWVdO1xyXG4gICAgICAgIGlmICghcHJldikgY29weVtuYW1lXSA9IHZhbDtcclxuICAgICAgICBlbHNlIGlmIChwcmV2ICE9IHZhbCkgdGhyb3cgbmV3IEVycm9yKFwiSW5jb25zaXN0ZW50IGJpbmRpbmdzIGZvciBcIiArIG5hbWUpO1xyXG4gICAgICB9XHJcbiAgICAgIGRlbGV0ZSBrZXltYXBba2V5bmFtZV07XHJcbiAgICB9XHJcbiAgICBmb3IgKHZhciBwcm9wIGluIGNvcHkpIGtleW1hcFtwcm9wXSA9IGNvcHlbcHJvcF07XHJcbiAgICByZXR1cm4ga2V5bWFwO1xyXG4gIH07XHJcblxyXG4gIHZhciBsb29rdXBLZXkgPSBDb2RlTWlycm9yLmxvb2t1cEtleSA9IGZ1bmN0aW9uKGtleSwgbWFwLCBoYW5kbGUsIGNvbnRleHQpIHtcclxuICAgIG1hcCA9IGdldEtleU1hcChtYXApO1xyXG4gICAgdmFyIGZvdW5kID0gbWFwLmNhbGwgPyBtYXAuY2FsbChrZXksIGNvbnRleHQpIDogbWFwW2tleV07XHJcbiAgICBpZiAoZm91bmQgPT09IGZhbHNlKSByZXR1cm4gXCJub3RoaW5nXCI7XHJcbiAgICBpZiAoZm91bmQgPT09IFwiLi4uXCIpIHJldHVybiBcIm11bHRpXCI7XHJcbiAgICBpZiAoZm91bmQgIT0gbnVsbCAmJiBoYW5kbGUoZm91bmQpKSByZXR1cm4gXCJoYW5kbGVkXCI7XHJcblxyXG4gICAgaWYgKG1hcC5mYWxsdGhyb3VnaCkge1xyXG4gICAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKG1hcC5mYWxsdGhyb3VnaCkgIT0gXCJbb2JqZWN0IEFycmF5XVwiKVxyXG4gICAgICAgIHJldHVybiBsb29rdXBLZXkoa2V5LCBtYXAuZmFsbHRocm91Z2gsIGhhbmRsZSwgY29udGV4dCk7XHJcbiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbWFwLmZhbGx0aHJvdWdoLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgdmFyIHJlc3VsdCA9IGxvb2t1cEtleShrZXksIG1hcC5mYWxsdGhyb3VnaFtpXSwgaGFuZGxlLCBjb250ZXh0KTtcclxuICAgICAgICBpZiAocmVzdWx0KSByZXR1cm4gcmVzdWx0O1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfTtcclxuXHJcbiAgLy8gTW9kaWZpZXIga2V5IHByZXNzZXMgZG9uJ3QgY291bnQgYXMgJ3JlYWwnIGtleSBwcmVzc2VzIGZvciB0aGVcclxuICAvLyBwdXJwb3NlIG9mIGtleW1hcCBmYWxsdGhyb3VnaC5cclxuICB2YXIgaXNNb2RpZmllcktleSA9IENvZGVNaXJyb3IuaXNNb2RpZmllcktleSA9IGZ1bmN0aW9uKHZhbHVlKSB7XHJcbiAgICB2YXIgbmFtZSA9IHR5cGVvZiB2YWx1ZSA9PSBcInN0cmluZ1wiID8gdmFsdWUgOiBrZXlOYW1lc1t2YWx1ZS5rZXlDb2RlXTtcclxuICAgIHJldHVybiBuYW1lID09IFwiQ3RybFwiIHx8IG5hbWUgPT0gXCJBbHRcIiB8fCBuYW1lID09IFwiU2hpZnRcIiB8fCBuYW1lID09IFwiTW9kXCI7XHJcbiAgfTtcclxuXHJcbiAgLy8gTG9vayB1cCB0aGUgbmFtZSBvZiBhIGtleSBhcyBpbmRpY2F0ZWQgYnkgYW4gZXZlbnQgb2JqZWN0LlxyXG4gIHZhciBrZXlOYW1lID0gQ29kZU1pcnJvci5rZXlOYW1lID0gZnVuY3Rpb24oZXZlbnQsIG5vU2hpZnQpIHtcclxuICAgIGlmIChwcmVzdG8gJiYgZXZlbnQua2V5Q29kZSA9PSAzNCAmJiBldmVudFtcImNoYXJcIl0pIHJldHVybiBmYWxzZTtcclxuICAgIHZhciBiYXNlID0ga2V5TmFtZXNbZXZlbnQua2V5Q29kZV0sIG5hbWUgPSBiYXNlO1xyXG4gICAgaWYgKG5hbWUgPT0gbnVsbCB8fCBldmVudC5hbHRHcmFwaEtleSkgcmV0dXJuIGZhbHNlO1xyXG4gICAgaWYgKGV2ZW50LmFsdEtleSAmJiBiYXNlICE9IFwiQWx0XCIpIG5hbWUgPSBcIkFsdC1cIiArIG5hbWU7XHJcbiAgICBpZiAoKGZsaXBDdHJsQ21kID8gZXZlbnQubWV0YUtleSA6IGV2ZW50LmN0cmxLZXkpICYmIGJhc2UgIT0gXCJDdHJsXCIpIG5hbWUgPSBcIkN0cmwtXCIgKyBuYW1lO1xyXG4gICAgaWYgKChmbGlwQ3RybENtZCA/IGV2ZW50LmN0cmxLZXkgOiBldmVudC5tZXRhS2V5KSAmJiBiYXNlICE9IFwiQ21kXCIpIG5hbWUgPSBcIkNtZC1cIiArIG5hbWU7XHJcbiAgICBpZiAoIW5vU2hpZnQgJiYgZXZlbnQuc2hpZnRLZXkgJiYgYmFzZSAhPSBcIlNoaWZ0XCIpIG5hbWUgPSBcIlNoaWZ0LVwiICsgbmFtZTtcclxuICAgIHJldHVybiBuYW1lO1xyXG4gIH07XHJcblxyXG4gIGZ1bmN0aW9uIGdldEtleU1hcCh2YWwpIHtcclxuICAgIHJldHVybiB0eXBlb2YgdmFsID09IFwic3RyaW5nXCIgPyBrZXlNYXBbdmFsXSA6IHZhbDtcclxuICB9XHJcblxyXG4gIC8vIEZST01URVhUQVJFQVxyXG5cclxuICBDb2RlTWlycm9yLmZyb21UZXh0QXJlYSA9IGZ1bmN0aW9uKHRleHRhcmVhLCBvcHRpb25zKSB7XHJcbiAgICBvcHRpb25zID0gb3B0aW9ucyA/IGNvcHlPYmoob3B0aW9ucykgOiB7fTtcclxuICAgIG9wdGlvbnMudmFsdWUgPSB0ZXh0YXJlYS52YWx1ZTtcclxuICAgIGlmICghb3B0aW9ucy50YWJpbmRleCAmJiB0ZXh0YXJlYS50YWJJbmRleClcclxuICAgICAgb3B0aW9ucy50YWJpbmRleCA9IHRleHRhcmVhLnRhYkluZGV4O1xyXG4gICAgaWYgKCFvcHRpb25zLnBsYWNlaG9sZGVyICYmIHRleHRhcmVhLnBsYWNlaG9sZGVyKVxyXG4gICAgICBvcHRpb25zLnBsYWNlaG9sZGVyID0gdGV4dGFyZWEucGxhY2Vob2xkZXI7XHJcbiAgICAvLyBTZXQgYXV0b2ZvY3VzIHRvIHRydWUgaWYgdGhpcyB0ZXh0YXJlYSBpcyBmb2N1c2VkLCBvciBpZiBpdCBoYXNcclxuICAgIC8vIGF1dG9mb2N1cyBhbmQgbm8gb3RoZXIgZWxlbWVudCBpcyBmb2N1c2VkLlxyXG4gICAgaWYgKG9wdGlvbnMuYXV0b2ZvY3VzID09IG51bGwpIHtcclxuICAgICAgdmFyIGhhc0ZvY3VzID0gYWN0aXZlRWx0KCk7XHJcbiAgICAgIG9wdGlvbnMuYXV0b2ZvY3VzID0gaGFzRm9jdXMgPT0gdGV4dGFyZWEgfHxcclxuICAgICAgICB0ZXh0YXJlYS5nZXRBdHRyaWJ1dGUoXCJhdXRvZm9jdXNcIikgIT0gbnVsbCAmJiBoYXNGb2N1cyA9PSBkb2N1bWVudC5ib2R5O1xyXG4gICAgfVxyXG5cclxuICAgIGZ1bmN0aW9uIHNhdmUoKSB7dGV4dGFyZWEudmFsdWUgPSBjbS5nZXRWYWx1ZSgpO31cclxuICAgIGlmICh0ZXh0YXJlYS5mb3JtKSB7XHJcbiAgICAgIG9uKHRleHRhcmVhLmZvcm0sIFwic3VibWl0XCIsIHNhdmUpO1xyXG4gICAgICAvLyBEZXBsb3JhYmxlIGhhY2sgdG8gbWFrZSB0aGUgc3VibWl0IG1ldGhvZCBkbyB0aGUgcmlnaHQgdGhpbmcuXHJcbiAgICAgIGlmICghb3B0aW9ucy5sZWF2ZVN1Ym1pdE1ldGhvZEFsb25lKSB7XHJcbiAgICAgICAgdmFyIGZvcm0gPSB0ZXh0YXJlYS5mb3JtLCByZWFsU3VibWl0ID0gZm9ybS5zdWJtaXQ7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIHZhciB3cmFwcGVkU3VibWl0ID0gZm9ybS5zdWJtaXQgPSBmdW5jdGlvbigpIHtcclxuICAgICAgICAgICAgc2F2ZSgpO1xyXG4gICAgICAgICAgICBmb3JtLnN1Ym1pdCA9IHJlYWxTdWJtaXQ7XHJcbiAgICAgICAgICAgIGZvcm0uc3VibWl0KCk7XHJcbiAgICAgICAgICAgIGZvcm0uc3VibWl0ID0gd3JhcHBlZFN1Ym1pdDtcclxuICAgICAgICAgIH07XHJcbiAgICAgICAgfSBjYXRjaChlKSB7fVxyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgb3B0aW9ucy5maW5pc2hJbml0ID0gZnVuY3Rpb24oY20pIHtcclxuICAgICAgY20uc2F2ZSA9IHNhdmU7XHJcbiAgICAgIGNtLmdldFRleHRBcmVhID0gZnVuY3Rpb24oKSB7IHJldHVybiB0ZXh0YXJlYTsgfTtcclxuICAgICAgY20udG9UZXh0QXJlYSA9IGZ1bmN0aW9uKCkge1xyXG4gICAgICAgIGNtLnRvVGV4dEFyZWEgPSBpc05hTjsgLy8gUHJldmVudCB0aGlzIGZyb20gYmVpbmcgcmFuIHR3aWNlXHJcbiAgICAgICAgc2F2ZSgpO1xyXG4gICAgICAgIHRleHRhcmVhLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoY20uZ2V0V3JhcHBlckVsZW1lbnQoKSk7XHJcbiAgICAgICAgdGV4dGFyZWEuc3R5bGUuZGlzcGxheSA9IFwiXCI7XHJcbiAgICAgICAgaWYgKHRleHRhcmVhLmZvcm0pIHtcclxuICAgICAgICAgIG9mZih0ZXh0YXJlYS5mb3JtLCBcInN1Ym1pdFwiLCBzYXZlKTtcclxuICAgICAgICAgIGlmICh0eXBlb2YgdGV4dGFyZWEuZm9ybS5zdWJtaXQgPT0gXCJmdW5jdGlvblwiKVxyXG4gICAgICAgICAgICB0ZXh0YXJlYS5mb3JtLnN1Ym1pdCA9IHJlYWxTdWJtaXQ7XHJcbiAgICAgICAgfVxyXG4gICAgICB9O1xyXG4gICAgfTtcclxuXHJcbiAgICB0ZXh0YXJlYS5zdHlsZS5kaXNwbGF5ID0gXCJub25lXCI7XHJcbiAgICB2YXIgY20gPSBDb2RlTWlycm9yKGZ1bmN0aW9uKG5vZGUpIHtcclxuICAgICAgdGV4dGFyZWEucGFyZW50Tm9kZS5pbnNlcnRCZWZvcmUobm9kZSwgdGV4dGFyZWEubmV4dFNpYmxpbmcpO1xyXG4gICAgfSwgb3B0aW9ucyk7XHJcbiAgICByZXR1cm4gY207XHJcbiAgfTtcclxuXHJcbiAgLy8gU1RSSU5HIFNUUkVBTVxyXG5cclxuICAvLyBGZWQgdG8gdGhlIG1vZGUgcGFyc2VycywgcHJvdmlkZXMgaGVscGVyIGZ1bmN0aW9ucyB0byBtYWtlXHJcbiAgLy8gcGFyc2VycyBtb3JlIHN1Y2NpbmN0LlxyXG5cclxuICB2YXIgU3RyaW5nU3RyZWFtID0gQ29kZU1pcnJvci5TdHJpbmdTdHJlYW0gPSBmdW5jdGlvbihzdHJpbmcsIHRhYlNpemUpIHtcclxuICAgIHRoaXMucG9zID0gdGhpcy5zdGFydCA9IDA7XHJcbiAgICB0aGlzLnN0cmluZyA9IHN0cmluZztcclxuICAgIHRoaXMudGFiU2l6ZSA9IHRhYlNpemUgfHwgODtcclxuICAgIHRoaXMubGFzdENvbHVtblBvcyA9IHRoaXMubGFzdENvbHVtblZhbHVlID0gMDtcclxuICAgIHRoaXMubGluZVN0YXJ0ID0gMDtcclxuICB9O1xyXG5cclxuICBTdHJpbmdTdHJlYW0ucHJvdG90eXBlID0ge1xyXG4gICAgZW9sOiBmdW5jdGlvbigpIHtyZXR1cm4gdGhpcy5wb3MgPj0gdGhpcy5zdHJpbmcubGVuZ3RoO30sXHJcbiAgICBzb2w6IGZ1bmN0aW9uKCkge3JldHVybiB0aGlzLnBvcyA9PSB0aGlzLmxpbmVTdGFydDt9LFxyXG4gICAgcGVlazogZnVuY3Rpb24oKSB7cmV0dXJuIHRoaXMuc3RyaW5nLmNoYXJBdCh0aGlzLnBvcykgfHwgdW5kZWZpbmVkO30sXHJcbiAgICBuZXh0OiBmdW5jdGlvbigpIHtcclxuICAgICAgaWYgKHRoaXMucG9zIDwgdGhpcy5zdHJpbmcubGVuZ3RoKVxyXG4gICAgICAgIHJldHVybiB0aGlzLnN0cmluZy5jaGFyQXQodGhpcy5wb3MrKyk7XHJcbiAgICB9LFxyXG4gICAgZWF0OiBmdW5jdGlvbihtYXRjaCkge1xyXG4gICAgICB2YXIgY2ggPSB0aGlzLnN0cmluZy5jaGFyQXQodGhpcy5wb3MpO1xyXG4gICAgICBpZiAodHlwZW9mIG1hdGNoID09IFwic3RyaW5nXCIpIHZhciBvayA9IGNoID09IG1hdGNoO1xyXG4gICAgICBlbHNlIHZhciBvayA9IGNoICYmIChtYXRjaC50ZXN0ID8gbWF0Y2gudGVzdChjaCkgOiBtYXRjaChjaCkpO1xyXG4gICAgICBpZiAob2spIHsrK3RoaXMucG9zOyByZXR1cm4gY2g7fVxyXG4gICAgfSxcclxuICAgIGVhdFdoaWxlOiBmdW5jdGlvbihtYXRjaCkge1xyXG4gICAgICB2YXIgc3RhcnQgPSB0aGlzLnBvcztcclxuICAgICAgd2hpbGUgKHRoaXMuZWF0KG1hdGNoKSl7fVxyXG4gICAgICByZXR1cm4gdGhpcy5wb3MgPiBzdGFydDtcclxuICAgIH0sXHJcbiAgICBlYXRTcGFjZTogZnVuY3Rpb24oKSB7XHJcbiAgICAgIHZhciBzdGFydCA9IHRoaXMucG9zO1xyXG4gICAgICB3aGlsZSAoL1tcXHNcXHUwMGEwXS8udGVzdCh0aGlzLnN0cmluZy5jaGFyQXQodGhpcy5wb3MpKSkgKyt0aGlzLnBvcztcclxuICAgICAgcmV0dXJuIHRoaXMucG9zID4gc3RhcnQ7XHJcbiAgICB9LFxyXG4gICAgc2tpcFRvRW5kOiBmdW5jdGlvbigpIHt0aGlzLnBvcyA9IHRoaXMuc3RyaW5nLmxlbmd0aDt9LFxyXG4gICAgc2tpcFRvOiBmdW5jdGlvbihjaCkge1xyXG4gICAgICB2YXIgZm91bmQgPSB0aGlzLnN0cmluZy5pbmRleE9mKGNoLCB0aGlzLnBvcyk7XHJcbiAgICAgIGlmIChmb3VuZCA+IC0xKSB7dGhpcy5wb3MgPSBmb3VuZDsgcmV0dXJuIHRydWU7fVxyXG4gICAgfSxcclxuICAgIGJhY2tVcDogZnVuY3Rpb24obikge3RoaXMucG9zIC09IG47fSxcclxuICAgIGNvbHVtbjogZnVuY3Rpb24oKSB7XHJcbiAgICAgIGlmICh0aGlzLmxhc3RDb2x1bW5Qb3MgPCB0aGlzLnN0YXJ0KSB7XHJcbiAgICAgICAgdGhpcy5sYXN0Q29sdW1uVmFsdWUgPSBjb3VudENvbHVtbih0aGlzLnN0cmluZywgdGhpcy5zdGFydCwgdGhpcy50YWJTaXplLCB0aGlzLmxhc3RDb2x1bW5Qb3MsIHRoaXMubGFzdENvbHVtblZhbHVlKTtcclxuICAgICAgICB0aGlzLmxhc3RDb2x1bW5Qb3MgPSB0aGlzLnN0YXJ0O1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiB0aGlzLmxhc3RDb2x1bW5WYWx1ZSAtICh0aGlzLmxpbmVTdGFydCA/IGNvdW50Q29sdW1uKHRoaXMuc3RyaW5nLCB0aGlzLmxpbmVTdGFydCwgdGhpcy50YWJTaXplKSA6IDApO1xyXG4gICAgfSxcclxuICAgIGluZGVudGF0aW9uOiBmdW5jdGlvbigpIHtcclxuICAgICAgcmV0dXJuIGNvdW50Q29sdW1uKHRoaXMuc3RyaW5nLCBudWxsLCB0aGlzLnRhYlNpemUpIC1cclxuICAgICAgICAodGhpcy5saW5lU3RhcnQgPyBjb3VudENvbHVtbih0aGlzLnN0cmluZywgdGhpcy5saW5lU3RhcnQsIHRoaXMudGFiU2l6ZSkgOiAwKTtcclxuICAgIH0sXHJcbiAgICBtYXRjaDogZnVuY3Rpb24ocGF0dGVybiwgY29uc3VtZSwgY2FzZUluc2Vuc2l0aXZlKSB7XHJcbiAgICAgIGlmICh0eXBlb2YgcGF0dGVybiA9PSBcInN0cmluZ1wiKSB7XHJcbiAgICAgICAgdmFyIGNhc2VkID0gZnVuY3Rpb24oc3RyKSB7cmV0dXJuIGNhc2VJbnNlbnNpdGl2ZSA/IHN0ci50b0xvd2VyQ2FzZSgpIDogc3RyO307XHJcbiAgICAgICAgdmFyIHN1YnN0ciA9IHRoaXMuc3RyaW5nLnN1YnN0cih0aGlzLnBvcywgcGF0dGVybi5sZW5ndGgpO1xyXG4gICAgICAgIGlmIChjYXNlZChzdWJzdHIpID09IGNhc2VkKHBhdHRlcm4pKSB7XHJcbiAgICAgICAgICBpZiAoY29uc3VtZSAhPT0gZmFsc2UpIHRoaXMucG9zICs9IHBhdHRlcm4ubGVuZ3RoO1xyXG4gICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgfVxyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHZhciBtYXRjaCA9IHRoaXMuc3RyaW5nLnNsaWNlKHRoaXMucG9zKS5tYXRjaChwYXR0ZXJuKTtcclxuICAgICAgICBpZiAobWF0Y2ggJiYgbWF0Y2guaW5kZXggPiAwKSByZXR1cm4gbnVsbDtcclxuICAgICAgICBpZiAobWF0Y2ggJiYgY29uc3VtZSAhPT0gZmFsc2UpIHRoaXMucG9zICs9IG1hdGNoWzBdLmxlbmd0aDtcclxuICAgICAgICByZXR1cm4gbWF0Y2g7XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjdXJyZW50OiBmdW5jdGlvbigpe3JldHVybiB0aGlzLnN0cmluZy5zbGljZSh0aGlzLnN0YXJ0LCB0aGlzLnBvcyk7fSxcclxuICAgIGhpZGVGaXJzdENoYXJzOiBmdW5jdGlvbihuLCBpbm5lcikge1xyXG4gICAgICB0aGlzLmxpbmVTdGFydCArPSBuO1xyXG4gICAgICB0cnkgeyByZXR1cm4gaW5uZXIoKTsgfVxyXG4gICAgICBmaW5hbGx5IHsgdGhpcy5saW5lU3RhcnQgLT0gbjsgfVxyXG4gICAgfVxyXG4gIH07XHJcblxyXG4gIC8vIFRFWFRNQVJLRVJTXHJcblxyXG4gIC8vIENyZWF0ZWQgd2l0aCBtYXJrVGV4dCBhbmQgc2V0Qm9va21hcmsgbWV0aG9kcy4gQSBUZXh0TWFya2VyIGlzIGFcclxuICAvLyBoYW5kbGUgdGhhdCBjYW4gYmUgdXNlZCB0byBjbGVhciBvciBmaW5kIGEgbWFya2VkIHBvc2l0aW9uIGluIHRoZVxyXG4gIC8vIGRvY3VtZW50LiBMaW5lIG9iamVjdHMgaG9sZCBhcnJheXMgKG1hcmtlZFNwYW5zKSBjb250YWluaW5nXHJcbiAgLy8ge2Zyb20sIHRvLCBtYXJrZXJ9IG9iamVjdCBwb2ludGluZyB0byBzdWNoIG1hcmtlciBvYmplY3RzLCBhbmRcclxuICAvLyBpbmRpY2F0aW5nIHRoYXQgc3VjaCBhIG1hcmtlciBpcyBwcmVzZW50IG9uIHRoYXQgbGluZS4gTXVsdGlwbGVcclxuICAvLyBsaW5lcyBtYXkgcG9pbnQgdG8gdGhlIHNhbWUgbWFya2VyIHdoZW4gaXQgc3BhbnMgYWNyb3NzIGxpbmVzLlxyXG4gIC8vIFRoZSBzcGFucyB3aWxsIGhhdmUgbnVsbCBmb3IgdGhlaXIgZnJvbS90byBwcm9wZXJ0aWVzIHdoZW4gdGhlXHJcbiAgLy8gbWFya2VyIGNvbnRpbnVlcyBiZXlvbmQgdGhlIHN0YXJ0L2VuZCBvZiB0aGUgbGluZS4gTWFya2VycyBoYXZlXHJcbiAgLy8gbGlua3MgYmFjayB0byB0aGUgbGluZXMgdGhleSBjdXJyZW50bHkgdG91Y2guXHJcblxyXG4gIHZhciBuZXh0TWFya2VySWQgPSAwO1xyXG5cclxuICB2YXIgVGV4dE1hcmtlciA9IENvZGVNaXJyb3IuVGV4dE1hcmtlciA9IGZ1bmN0aW9uKGRvYywgdHlwZSkge1xyXG4gICAgdGhpcy5saW5lcyA9IFtdO1xyXG4gICAgdGhpcy50eXBlID0gdHlwZTtcclxuICAgIHRoaXMuZG9jID0gZG9jO1xyXG4gICAgdGhpcy5pZCA9ICsrbmV4dE1hcmtlcklkO1xyXG4gIH07XHJcbiAgZXZlbnRNaXhpbihUZXh0TWFya2VyKTtcclxuXHJcbiAgLy8gQ2xlYXIgdGhlIG1hcmtlci5cclxuICBUZXh0TWFya2VyLnByb3RvdHlwZS5jbGVhciA9IGZ1bmN0aW9uKCkge1xyXG4gICAgaWYgKHRoaXMuZXhwbGljaXRseUNsZWFyZWQpIHJldHVybjtcclxuICAgIHZhciBjbSA9IHRoaXMuZG9jLmNtLCB3aXRoT3AgPSBjbSAmJiAhY20uY3VyT3A7XHJcbiAgICBpZiAod2l0aE9wKSBzdGFydE9wZXJhdGlvbihjbSk7XHJcbiAgICBpZiAoaGFzSGFuZGxlcih0aGlzLCBcImNsZWFyXCIpKSB7XHJcbiAgICAgIHZhciBmb3VuZCA9IHRoaXMuZmluZCgpO1xyXG4gICAgICBpZiAoZm91bmQpIHNpZ25hbExhdGVyKHRoaXMsIFwiY2xlYXJcIiwgZm91bmQuZnJvbSwgZm91bmQudG8pO1xyXG4gICAgfVxyXG4gICAgdmFyIG1pbiA9IG51bGwsIG1heCA9IG51bGw7XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IHRoaXMubGluZXMubGVuZ3RoOyArK2kpIHtcclxuICAgICAgdmFyIGxpbmUgPSB0aGlzLmxpbmVzW2ldO1xyXG4gICAgICB2YXIgc3BhbiA9IGdldE1hcmtlZFNwYW5Gb3IobGluZS5tYXJrZWRTcGFucywgdGhpcyk7XHJcbiAgICAgIGlmIChjbSAmJiAhdGhpcy5jb2xsYXBzZWQpIHJlZ0xpbmVDaGFuZ2UoY20sIGxpbmVObyhsaW5lKSwgXCJ0ZXh0XCIpO1xyXG4gICAgICBlbHNlIGlmIChjbSkge1xyXG4gICAgICAgIGlmIChzcGFuLnRvICE9IG51bGwpIG1heCA9IGxpbmVObyhsaW5lKTtcclxuICAgICAgICBpZiAoc3Bhbi5mcm9tICE9IG51bGwpIG1pbiA9IGxpbmVObyhsaW5lKTtcclxuICAgICAgfVxyXG4gICAgICBsaW5lLm1hcmtlZFNwYW5zID0gcmVtb3ZlTWFya2VkU3BhbihsaW5lLm1hcmtlZFNwYW5zLCBzcGFuKTtcclxuICAgICAgaWYgKHNwYW4uZnJvbSA9PSBudWxsICYmIHRoaXMuY29sbGFwc2VkICYmICFsaW5lSXNIaWRkZW4odGhpcy5kb2MsIGxpbmUpICYmIGNtKVxyXG4gICAgICAgIHVwZGF0ZUxpbmVIZWlnaHQobGluZSwgdGV4dEhlaWdodChjbS5kaXNwbGF5KSk7XHJcbiAgICB9XHJcbiAgICBpZiAoY20gJiYgdGhpcy5jb2xsYXBzZWQgJiYgIWNtLm9wdGlvbnMubGluZVdyYXBwaW5nKSBmb3IgKHZhciBpID0gMDsgaSA8IHRoaXMubGluZXMubGVuZ3RoOyArK2kpIHtcclxuICAgICAgdmFyIHZpc3VhbCA9IHZpc3VhbExpbmUodGhpcy5saW5lc1tpXSksIGxlbiA9IGxpbmVMZW5ndGgodmlzdWFsKTtcclxuICAgICAgaWYgKGxlbiA+IGNtLmRpc3BsYXkubWF4TGluZUxlbmd0aCkge1xyXG4gICAgICAgIGNtLmRpc3BsYXkubWF4TGluZSA9IHZpc3VhbDtcclxuICAgICAgICBjbS5kaXNwbGF5Lm1heExpbmVMZW5ndGggPSBsZW47XHJcbiAgICAgICAgY20uZGlzcGxheS5tYXhMaW5lQ2hhbmdlZCA9IHRydWU7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBpZiAobWluICE9IG51bGwgJiYgY20gJiYgdGhpcy5jb2xsYXBzZWQpIHJlZ0NoYW5nZShjbSwgbWluLCBtYXggKyAxKTtcclxuICAgIHRoaXMubGluZXMubGVuZ3RoID0gMDtcclxuICAgIHRoaXMuZXhwbGljaXRseUNsZWFyZWQgPSB0cnVlO1xyXG4gICAgaWYgKHRoaXMuYXRvbWljICYmIHRoaXMuZG9jLmNhbnRFZGl0KSB7XHJcbiAgICAgIHRoaXMuZG9jLmNhbnRFZGl0ID0gZmFsc2U7XHJcbiAgICAgIGlmIChjbSkgcmVDaGVja1NlbGVjdGlvbihjbS5kb2MpO1xyXG4gICAgfVxyXG4gICAgaWYgKGNtKSBzaWduYWxMYXRlcihjbSwgXCJtYXJrZXJDbGVhcmVkXCIsIGNtLCB0aGlzKTtcclxuICAgIGlmICh3aXRoT3ApIGVuZE9wZXJhdGlvbihjbSk7XHJcbiAgICBpZiAodGhpcy5wYXJlbnQpIHRoaXMucGFyZW50LmNsZWFyKCk7XHJcbiAgfTtcclxuXHJcbiAgLy8gRmluZCB0aGUgcG9zaXRpb24gb2YgdGhlIG1hcmtlciBpbiB0aGUgZG9jdW1lbnQuIFJldHVybnMgYSB7ZnJvbSxcclxuICAvLyB0b30gb2JqZWN0IGJ5IGRlZmF1bHQuIFNpZGUgY2FuIGJlIHBhc3NlZCB0byBnZXQgYSBzcGVjaWZpYyBzaWRlXHJcbiAgLy8gLS0gMCAoYm90aCksIC0xIChsZWZ0KSwgb3IgMSAocmlnaHQpLiBXaGVuIGxpbmVPYmogaXMgdHJ1ZSwgdGhlXHJcbiAgLy8gUG9zIG9iamVjdHMgcmV0dXJuZWQgY29udGFpbiBhIGxpbmUgb2JqZWN0LCByYXRoZXIgdGhhbiBhIGxpbmVcclxuICAvLyBudW1iZXIgKHVzZWQgdG8gcHJldmVudCBsb29raW5nIHVwIHRoZSBzYW1lIGxpbmUgdHdpY2UpLlxyXG4gIFRleHRNYXJrZXIucHJvdG90eXBlLmZpbmQgPSBmdW5jdGlvbihzaWRlLCBsaW5lT2JqKSB7XHJcbiAgICBpZiAoc2lkZSA9PSBudWxsICYmIHRoaXMudHlwZSA9PSBcImJvb2ttYXJrXCIpIHNpZGUgPSAxO1xyXG4gICAgdmFyIGZyb20sIHRvO1xyXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCB0aGlzLmxpbmVzLmxlbmd0aDsgKytpKSB7XHJcbiAgICAgIHZhciBsaW5lID0gdGhpcy5saW5lc1tpXTtcclxuICAgICAgdmFyIHNwYW4gPSBnZXRNYXJrZWRTcGFuRm9yKGxpbmUubWFya2VkU3BhbnMsIHRoaXMpO1xyXG4gICAgICBpZiAoc3Bhbi5mcm9tICE9IG51bGwpIHtcclxuICAgICAgICBmcm9tID0gUG9zKGxpbmVPYmogPyBsaW5lIDogbGluZU5vKGxpbmUpLCBzcGFuLmZyb20pO1xyXG4gICAgICAgIGlmIChzaWRlID09IC0xKSByZXR1cm4gZnJvbTtcclxuICAgICAgfVxyXG4gICAgICBpZiAoc3Bhbi50byAhPSBudWxsKSB7XHJcbiAgICAgICAgdG8gPSBQb3MobGluZU9iaiA/IGxpbmUgOiBsaW5lTm8obGluZSksIHNwYW4udG8pO1xyXG4gICAgICAgIGlmIChzaWRlID09IDEpIHJldHVybiB0bztcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgcmV0dXJuIGZyb20gJiYge2Zyb206IGZyb20sIHRvOiB0b307XHJcbiAgfTtcclxuXHJcbiAgLy8gU2lnbmFscyB0aGF0IHRoZSBtYXJrZXIncyB3aWRnZXQgY2hhbmdlZCwgYW5kIHN1cnJvdW5kaW5nIGxheW91dFxyXG4gIC8vIHNob3VsZCBiZSByZWNvbXB1dGVkLlxyXG4gIFRleHRNYXJrZXIucHJvdG90eXBlLmNoYW5nZWQgPSBmdW5jdGlvbigpIHtcclxuICAgIHZhciBwb3MgPSB0aGlzLmZpbmQoLTEsIHRydWUpLCB3aWRnZXQgPSB0aGlzLCBjbSA9IHRoaXMuZG9jLmNtO1xyXG4gICAgaWYgKCFwb3MgfHwgIWNtKSByZXR1cm47XHJcbiAgICBydW5Jbk9wKGNtLCBmdW5jdGlvbigpIHtcclxuICAgICAgdmFyIGxpbmUgPSBwb3MubGluZSwgbGluZU4gPSBsaW5lTm8ocG9zLmxpbmUpO1xyXG4gICAgICB2YXIgdmlldyA9IGZpbmRWaWV3Rm9yTGluZShjbSwgbGluZU4pO1xyXG4gICAgICBpZiAodmlldykge1xyXG4gICAgICAgIGNsZWFyTGluZU1lYXN1cmVtZW50Q2FjaGVGb3Iodmlldyk7XHJcbiAgICAgICAgY20uY3VyT3Auc2VsZWN0aW9uQ2hhbmdlZCA9IGNtLmN1ck9wLmZvcmNlVXBkYXRlID0gdHJ1ZTtcclxuICAgICAgfVxyXG4gICAgICBjbS5jdXJPcC51cGRhdGVNYXhMaW5lID0gdHJ1ZTtcclxuICAgICAgaWYgKCFsaW5lSXNIaWRkZW4od2lkZ2V0LmRvYywgbGluZSkgJiYgd2lkZ2V0LmhlaWdodCAhPSBudWxsKSB7XHJcbiAgICAgICAgdmFyIG9sZEhlaWdodCA9IHdpZGdldC5oZWlnaHQ7XHJcbiAgICAgICAgd2lkZ2V0LmhlaWdodCA9IG51bGw7XHJcbiAgICAgICAgdmFyIGRIZWlnaHQgPSB3aWRnZXRIZWlnaHQod2lkZ2V0KSAtIG9sZEhlaWdodDtcclxuICAgICAgICBpZiAoZEhlaWdodClcclxuICAgICAgICAgIHVwZGF0ZUxpbmVIZWlnaHQobGluZSwgbGluZS5oZWlnaHQgKyBkSGVpZ2h0KTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfTtcclxuXHJcbiAgVGV4dE1hcmtlci5wcm90b3R5cGUuYXR0YWNoTGluZSA9IGZ1bmN0aW9uKGxpbmUpIHtcclxuICAgIGlmICghdGhpcy5saW5lcy5sZW5ndGggJiYgdGhpcy5kb2MuY20pIHtcclxuICAgICAgdmFyIG9wID0gdGhpcy5kb2MuY20uY3VyT3A7XHJcbiAgICAgIGlmICghb3AubWF5YmVIaWRkZW5NYXJrZXJzIHx8IGluZGV4T2Yob3AubWF5YmVIaWRkZW5NYXJrZXJzLCB0aGlzKSA9PSAtMSlcclxuICAgICAgICAob3AubWF5YmVVbmhpZGRlbk1hcmtlcnMgfHwgKG9wLm1heWJlVW5oaWRkZW5NYXJrZXJzID0gW10pKS5wdXNoKHRoaXMpO1xyXG4gICAgfVxyXG4gICAgdGhpcy5saW5lcy5wdXNoKGxpbmUpO1xyXG4gIH07XHJcbiAgVGV4dE1hcmtlci5wcm90b3R5cGUuZGV0YWNoTGluZSA9IGZ1bmN0aW9uKGxpbmUpIHtcclxuICAgIHRoaXMubGluZXMuc3BsaWNlKGluZGV4T2YodGhpcy5saW5lcywgbGluZSksIDEpO1xyXG4gICAgaWYgKCF0aGlzLmxpbmVzLmxlbmd0aCAmJiB0aGlzLmRvYy5jbSkge1xyXG4gICAgICB2YXIgb3AgPSB0aGlzLmRvYy5jbS5jdXJPcDtcclxuICAgICAgKG9wLm1heWJlSGlkZGVuTWFya2VycyB8fCAob3AubWF5YmVIaWRkZW5NYXJrZXJzID0gW10pKS5wdXNoKHRoaXMpO1xyXG4gICAgfVxyXG4gIH07XHJcblxyXG4gIC8vIENvbGxhcHNlZCBtYXJrZXJzIGhhdmUgdW5pcXVlIGlkcywgaW4gb3JkZXIgdG8gYmUgYWJsZSB0byBvcmRlclxyXG4gIC8vIHRoZW0sIHdoaWNoIGlzIG5lZWRlZCBmb3IgdW5pcXVlbHkgZGV0ZXJtaW5pbmcgYW4gb3V0ZXIgbWFya2VyXHJcbiAgLy8gd2hlbiB0aGV5IG92ZXJsYXAgKHRoZXkgbWF5IG5lc3QsIGJ1dCBub3QgcGFydGlhbGx5IG92ZXJsYXApLlxyXG4gIHZhciBuZXh0TWFya2VySWQgPSAwO1xyXG5cclxuICAvLyBDcmVhdGUgYSBtYXJrZXIsIHdpcmUgaXQgdXAgdG8gdGhlIHJpZ2h0IGxpbmVzLCBhbmRcclxuICBmdW5jdGlvbiBtYXJrVGV4dChkb2MsIGZyb20sIHRvLCBvcHRpb25zLCB0eXBlKSB7XHJcbiAgICAvLyBTaGFyZWQgbWFya2VycyAoYWNyb3NzIGxpbmtlZCBkb2N1bWVudHMpIGFyZSBoYW5kbGVkIHNlcGFyYXRlbHlcclxuICAgIC8vIChtYXJrVGV4dFNoYXJlZCB3aWxsIGNhbGwgb3V0IHRvIHRoaXMgYWdhaW4sIG9uY2UgcGVyXHJcbiAgICAvLyBkb2N1bWVudCkuXHJcbiAgICBpZiAob3B0aW9ucyAmJiBvcHRpb25zLnNoYXJlZCkgcmV0dXJuIG1hcmtUZXh0U2hhcmVkKGRvYywgZnJvbSwgdG8sIG9wdGlvbnMsIHR5cGUpO1xyXG4gICAgLy8gRW5zdXJlIHdlIGFyZSBpbiBhbiBvcGVyYXRpb24uXHJcbiAgICBpZiAoZG9jLmNtICYmICFkb2MuY20uY3VyT3ApIHJldHVybiBvcGVyYXRpb24oZG9jLmNtLCBtYXJrVGV4dCkoZG9jLCBmcm9tLCB0bywgb3B0aW9ucywgdHlwZSk7XHJcblxyXG4gICAgdmFyIG1hcmtlciA9IG5ldyBUZXh0TWFya2VyKGRvYywgdHlwZSksIGRpZmYgPSBjbXAoZnJvbSwgdG8pO1xyXG4gICAgaWYgKG9wdGlvbnMpIGNvcHlPYmoob3B0aW9ucywgbWFya2VyLCBmYWxzZSk7XHJcbiAgICAvLyBEb24ndCBjb25uZWN0IGVtcHR5IG1hcmtlcnMgdW5sZXNzIGNsZWFyV2hlbkVtcHR5IGlzIGZhbHNlXHJcbiAgICBpZiAoZGlmZiA+IDAgfHwgZGlmZiA9PSAwICYmIG1hcmtlci5jbGVhcldoZW5FbXB0eSAhPT0gZmFsc2UpXHJcbiAgICAgIHJldHVybiBtYXJrZXI7XHJcbiAgICBpZiAobWFya2VyLnJlcGxhY2VkV2l0aCkge1xyXG4gICAgICAvLyBTaG93aW5nIHVwIGFzIGEgd2lkZ2V0IGltcGxpZXMgY29sbGFwc2VkICh3aWRnZXQgcmVwbGFjZXMgdGV4dClcclxuICAgICAgbWFya2VyLmNvbGxhcHNlZCA9IHRydWU7XHJcbiAgICAgIG1hcmtlci53aWRnZXROb2RlID0gZWx0KFwic3BhblwiLCBbbWFya2VyLnJlcGxhY2VkV2l0aF0sIFwiQ29kZU1pcnJvci13aWRnZXRcIik7XHJcbiAgICAgIGlmICghb3B0aW9ucy5oYW5kbGVNb3VzZUV2ZW50cykgbWFya2VyLndpZGdldE5vZGUuc2V0QXR0cmlidXRlKFwiY20taWdub3JlLWV2ZW50c1wiLCBcInRydWVcIik7XHJcbiAgICAgIGlmIChvcHRpb25zLmluc2VydExlZnQpIG1hcmtlci53aWRnZXROb2RlLmluc2VydExlZnQgPSB0cnVlO1xyXG4gICAgfVxyXG4gICAgaWYgKG1hcmtlci5jb2xsYXBzZWQpIHtcclxuICAgICAgaWYgKGNvbmZsaWN0aW5nQ29sbGFwc2VkUmFuZ2UoZG9jLCBmcm9tLmxpbmUsIGZyb20sIHRvLCBtYXJrZXIpIHx8XHJcbiAgICAgICAgICBmcm9tLmxpbmUgIT0gdG8ubGluZSAmJiBjb25mbGljdGluZ0NvbGxhcHNlZFJhbmdlKGRvYywgdG8ubGluZSwgZnJvbSwgdG8sIG1hcmtlcikpXHJcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiSW5zZXJ0aW5nIGNvbGxhcHNlZCBtYXJrZXIgcGFydGlhbGx5IG92ZXJsYXBwaW5nIGFuIGV4aXN0aW5nIG9uZVwiKTtcclxuICAgICAgc2F3Q29sbGFwc2VkU3BhbnMgPSB0cnVlO1xyXG4gICAgfVxyXG5cclxuICAgIGlmIChtYXJrZXIuYWRkVG9IaXN0b3J5KVxyXG4gICAgICBhZGRDaGFuZ2VUb0hpc3RvcnkoZG9jLCB7ZnJvbTogZnJvbSwgdG86IHRvLCBvcmlnaW46IFwibWFya1RleHRcIn0sIGRvYy5zZWwsIE5hTik7XHJcblxyXG4gICAgdmFyIGN1ckxpbmUgPSBmcm9tLmxpbmUsIGNtID0gZG9jLmNtLCB1cGRhdGVNYXhMaW5lO1xyXG4gICAgZG9jLml0ZXIoY3VyTGluZSwgdG8ubGluZSArIDEsIGZ1bmN0aW9uKGxpbmUpIHtcclxuICAgICAgaWYgKGNtICYmIG1hcmtlci5jb2xsYXBzZWQgJiYgIWNtLm9wdGlvbnMubGluZVdyYXBwaW5nICYmIHZpc3VhbExpbmUobGluZSkgPT0gY20uZGlzcGxheS5tYXhMaW5lKVxyXG4gICAgICAgIHVwZGF0ZU1heExpbmUgPSB0cnVlO1xyXG4gICAgICBpZiAobWFya2VyLmNvbGxhcHNlZCAmJiBjdXJMaW5lICE9IGZyb20ubGluZSkgdXBkYXRlTGluZUhlaWdodChsaW5lLCAwKTtcclxuICAgICAgYWRkTWFya2VkU3BhbihsaW5lLCBuZXcgTWFya2VkU3BhbihtYXJrZXIsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3VyTGluZSA9PSBmcm9tLmxpbmUgPyBmcm9tLmNoIDogbnVsbCxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdXJMaW5lID09IHRvLmxpbmUgPyB0by5jaCA6IG51bGwpKTtcclxuICAgICAgKytjdXJMaW5lO1xyXG4gICAgfSk7XHJcbiAgICAvLyBsaW5lSXNIaWRkZW4gZGVwZW5kcyBvbiB0aGUgcHJlc2VuY2Ugb2YgdGhlIHNwYW5zLCBzbyBuZWVkcyBhIHNlY29uZCBwYXNzXHJcbiAgICBpZiAobWFya2VyLmNvbGxhcHNlZCkgZG9jLml0ZXIoZnJvbS5saW5lLCB0by5saW5lICsgMSwgZnVuY3Rpb24obGluZSkge1xyXG4gICAgICBpZiAobGluZUlzSGlkZGVuKGRvYywgbGluZSkpIHVwZGF0ZUxpbmVIZWlnaHQobGluZSwgMCk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBpZiAobWFya2VyLmNsZWFyT25FbnRlcikgb24obWFya2VyLCBcImJlZm9yZUN1cnNvckVudGVyXCIsIGZ1bmN0aW9uKCkgeyBtYXJrZXIuY2xlYXIoKTsgfSk7XHJcblxyXG4gICAgaWYgKG1hcmtlci5yZWFkT25seSkge1xyXG4gICAgICBzYXdSZWFkT25seVNwYW5zID0gdHJ1ZTtcclxuICAgICAgaWYgKGRvYy5oaXN0b3J5LmRvbmUubGVuZ3RoIHx8IGRvYy5oaXN0b3J5LnVuZG9uZS5sZW5ndGgpXHJcbiAgICAgICAgZG9jLmNsZWFySGlzdG9yeSgpO1xyXG4gICAgfVxyXG4gICAgaWYgKG1hcmtlci5jb2xsYXBzZWQpIHtcclxuICAgICAgbWFya2VyLmlkID0gKytuZXh0TWFya2VySWQ7XHJcbiAgICAgIG1hcmtlci5hdG9taWMgPSB0cnVlO1xyXG4gICAgfVxyXG4gICAgaWYgKGNtKSB7XHJcbiAgICAgIC8vIFN5bmMgZWRpdG9yIHN0YXRlXHJcbiAgICAgIGlmICh1cGRhdGVNYXhMaW5lKSBjbS5jdXJPcC51cGRhdGVNYXhMaW5lID0gdHJ1ZTtcclxuICAgICAgaWYgKG1hcmtlci5jb2xsYXBzZWQpXHJcbiAgICAgICAgcmVnQ2hhbmdlKGNtLCBmcm9tLmxpbmUsIHRvLmxpbmUgKyAxKTtcclxuICAgICAgZWxzZSBpZiAobWFya2VyLmNsYXNzTmFtZSB8fCBtYXJrZXIudGl0bGUgfHwgbWFya2VyLnN0YXJ0U3R5bGUgfHwgbWFya2VyLmVuZFN0eWxlIHx8IG1hcmtlci5jc3MpXHJcbiAgICAgICAgZm9yICh2YXIgaSA9IGZyb20ubGluZTsgaSA8PSB0by5saW5lOyBpKyspIHJlZ0xpbmVDaGFuZ2UoY20sIGksIFwidGV4dFwiKTtcclxuICAgICAgaWYgKG1hcmtlci5hdG9taWMpIHJlQ2hlY2tTZWxlY3Rpb24oY20uZG9jKTtcclxuICAgICAgc2lnbmFsTGF0ZXIoY20sIFwibWFya2VyQWRkZWRcIiwgY20sIG1hcmtlcik7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gbWFya2VyO1xyXG4gIH1cclxuXHJcbiAgLy8gU0hBUkVEIFRFWFRNQVJLRVJTXHJcblxyXG4gIC8vIEEgc2hhcmVkIG1hcmtlciBzcGFucyBtdWx0aXBsZSBsaW5rZWQgZG9jdW1lbnRzLiBJdCBpc1xyXG4gIC8vIGltcGxlbWVudGVkIGFzIGEgbWV0YS1tYXJrZXItb2JqZWN0IGNvbnRyb2xsaW5nIG11bHRpcGxlIG5vcm1hbFxyXG4gIC8vIG1hcmtlcnMuXHJcbiAgdmFyIFNoYXJlZFRleHRNYXJrZXIgPSBDb2RlTWlycm9yLlNoYXJlZFRleHRNYXJrZXIgPSBmdW5jdGlvbihtYXJrZXJzLCBwcmltYXJ5KSB7XHJcbiAgICB0aGlzLm1hcmtlcnMgPSBtYXJrZXJzO1xyXG4gICAgdGhpcy5wcmltYXJ5ID0gcHJpbWFyeTtcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbWFya2Vycy5sZW5ndGg7ICsraSlcclxuICAgICAgbWFya2Vyc1tpXS5wYXJlbnQgPSB0aGlzO1xyXG4gIH07XHJcbiAgZXZlbnRNaXhpbihTaGFyZWRUZXh0TWFya2VyKTtcclxuXHJcbiAgU2hhcmVkVGV4dE1hcmtlci5wcm90b3R5cGUuY2xlYXIgPSBmdW5jdGlvbigpIHtcclxuICAgIGlmICh0aGlzLmV4cGxpY2l0bHlDbGVhcmVkKSByZXR1cm47XHJcbiAgICB0aGlzLmV4cGxpY2l0bHlDbGVhcmVkID0gdHJ1ZTtcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy5tYXJrZXJzLmxlbmd0aDsgKytpKVxyXG4gICAgICB0aGlzLm1hcmtlcnNbaV0uY2xlYXIoKTtcclxuICAgIHNpZ25hbExhdGVyKHRoaXMsIFwiY2xlYXJcIik7XHJcbiAgfTtcclxuICBTaGFyZWRUZXh0TWFya2VyLnByb3RvdHlwZS5maW5kID0gZnVuY3Rpb24oc2lkZSwgbGluZU9iaikge1xyXG4gICAgcmV0dXJuIHRoaXMucHJpbWFyeS5maW5kKHNpZGUsIGxpbmVPYmopO1xyXG4gIH07XHJcblxyXG4gIGZ1bmN0aW9uIG1hcmtUZXh0U2hhcmVkKGRvYywgZnJvbSwgdG8sIG9wdGlvbnMsIHR5cGUpIHtcclxuICAgIG9wdGlvbnMgPSBjb3B5T2JqKG9wdGlvbnMpO1xyXG4gICAgb3B0aW9ucy5zaGFyZWQgPSBmYWxzZTtcclxuICAgIHZhciBtYXJrZXJzID0gW21hcmtUZXh0KGRvYywgZnJvbSwgdG8sIG9wdGlvbnMsIHR5cGUpXSwgcHJpbWFyeSA9IG1hcmtlcnNbMF07XHJcbiAgICB2YXIgd2lkZ2V0ID0gb3B0aW9ucy53aWRnZXROb2RlO1xyXG4gICAgbGlua2VkRG9jcyhkb2MsIGZ1bmN0aW9uKGRvYykge1xyXG4gICAgICBpZiAod2lkZ2V0KSBvcHRpb25zLndpZGdldE5vZGUgPSB3aWRnZXQuY2xvbmVOb2RlKHRydWUpO1xyXG4gICAgICBtYXJrZXJzLnB1c2gobWFya1RleHQoZG9jLCBjbGlwUG9zKGRvYywgZnJvbSksIGNsaXBQb3MoZG9jLCB0byksIG9wdGlvbnMsIHR5cGUpKTtcclxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBkb2MubGlua2VkLmxlbmd0aDsgKytpKVxyXG4gICAgICAgIGlmIChkb2MubGlua2VkW2ldLmlzUGFyZW50KSByZXR1cm47XHJcbiAgICAgIHByaW1hcnkgPSBsc3QobWFya2Vycyk7XHJcbiAgICB9KTtcclxuICAgIHJldHVybiBuZXcgU2hhcmVkVGV4dE1hcmtlcihtYXJrZXJzLCBwcmltYXJ5KTtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIGZpbmRTaGFyZWRNYXJrZXJzKGRvYykge1xyXG4gICAgcmV0dXJuIGRvYy5maW5kTWFya3MoUG9zKGRvYy5maXJzdCwgMCksIGRvYy5jbGlwUG9zKFBvcyhkb2MubGFzdExpbmUoKSkpLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24obSkgeyByZXR1cm4gbS5wYXJlbnQ7IH0pO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gY29weVNoYXJlZE1hcmtlcnMoZG9jLCBtYXJrZXJzKSB7XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IG1hcmtlcnMubGVuZ3RoOyBpKyspIHtcclxuICAgICAgdmFyIG1hcmtlciA9IG1hcmtlcnNbaV0sIHBvcyA9IG1hcmtlci5maW5kKCk7XHJcbiAgICAgIHZhciBtRnJvbSA9IGRvYy5jbGlwUG9zKHBvcy5mcm9tKSwgbVRvID0gZG9jLmNsaXBQb3MocG9zLnRvKTtcclxuICAgICAgaWYgKGNtcChtRnJvbSwgbVRvKSkge1xyXG4gICAgICAgIHZhciBzdWJNYXJrID0gbWFya1RleHQoZG9jLCBtRnJvbSwgbVRvLCBtYXJrZXIucHJpbWFyeSwgbWFya2VyLnByaW1hcnkudHlwZSk7XHJcbiAgICAgICAgbWFya2VyLm1hcmtlcnMucHVzaChzdWJNYXJrKTtcclxuICAgICAgICBzdWJNYXJrLnBhcmVudCA9IG1hcmtlcjtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gZGV0YWNoU2hhcmVkTWFya2VycyhtYXJrZXJzKSB7XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IG1hcmtlcnMubGVuZ3RoOyBpKyspIHtcclxuICAgICAgdmFyIG1hcmtlciA9IG1hcmtlcnNbaV0sIGxpbmtlZCA9IFttYXJrZXIucHJpbWFyeS5kb2NdOztcclxuICAgICAgbGlua2VkRG9jcyhtYXJrZXIucHJpbWFyeS5kb2MsIGZ1bmN0aW9uKGQpIHsgbGlua2VkLnB1c2goZCk7IH0pO1xyXG4gICAgICBmb3IgKHZhciBqID0gMDsgaiA8IG1hcmtlci5tYXJrZXJzLmxlbmd0aDsgaisrKSB7XHJcbiAgICAgICAgdmFyIHN1Yk1hcmtlciA9IG1hcmtlci5tYXJrZXJzW2pdO1xyXG4gICAgICAgIGlmIChpbmRleE9mKGxpbmtlZCwgc3ViTWFya2VyLmRvYykgPT0gLTEpIHtcclxuICAgICAgICAgIHN1Yk1hcmtlci5wYXJlbnQgPSBudWxsO1xyXG4gICAgICAgICAgbWFya2VyLm1hcmtlcnMuc3BsaWNlKGotLSwgMSk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBURVhUTUFSS0VSIFNQQU5TXHJcblxyXG4gIGZ1bmN0aW9uIE1hcmtlZFNwYW4obWFya2VyLCBmcm9tLCB0bykge1xyXG4gICAgdGhpcy5tYXJrZXIgPSBtYXJrZXI7XHJcbiAgICB0aGlzLmZyb20gPSBmcm9tOyB0aGlzLnRvID0gdG87XHJcbiAgfVxyXG5cclxuICAvLyBTZWFyY2ggYW4gYXJyYXkgb2Ygc3BhbnMgZm9yIGEgc3BhbiBtYXRjaGluZyB0aGUgZ2l2ZW4gbWFya2VyLlxyXG4gIGZ1bmN0aW9uIGdldE1hcmtlZFNwYW5Gb3Ioc3BhbnMsIG1hcmtlcikge1xyXG4gICAgaWYgKHNwYW5zKSBmb3IgKHZhciBpID0gMDsgaSA8IHNwYW5zLmxlbmd0aDsgKytpKSB7XHJcbiAgICAgIHZhciBzcGFuID0gc3BhbnNbaV07XHJcbiAgICAgIGlmIChzcGFuLm1hcmtlciA9PSBtYXJrZXIpIHJldHVybiBzcGFuO1xyXG4gICAgfVxyXG4gIH1cclxuICAvLyBSZW1vdmUgYSBzcGFuIGZyb20gYW4gYXJyYXksIHJldHVybmluZyB1bmRlZmluZWQgaWYgbm8gc3BhbnMgYXJlXHJcbiAgLy8gbGVmdCAod2UgZG9uJ3Qgc3RvcmUgYXJyYXlzIGZvciBsaW5lcyB3aXRob3V0IHNwYW5zKS5cclxuICBmdW5jdGlvbiByZW1vdmVNYXJrZWRTcGFuKHNwYW5zLCBzcGFuKSB7XHJcbiAgICBmb3IgKHZhciByLCBpID0gMDsgaSA8IHNwYW5zLmxlbmd0aDsgKytpKVxyXG4gICAgICBpZiAoc3BhbnNbaV0gIT0gc3BhbikgKHIgfHwgKHIgPSBbXSkpLnB1c2goc3BhbnNbaV0pO1xyXG4gICAgcmV0dXJuIHI7XHJcbiAgfVxyXG4gIC8vIEFkZCBhIHNwYW4gdG8gYSBsaW5lLlxyXG4gIGZ1bmN0aW9uIGFkZE1hcmtlZFNwYW4obGluZSwgc3Bhbikge1xyXG4gICAgbGluZS5tYXJrZWRTcGFucyA9IGxpbmUubWFya2VkU3BhbnMgPyBsaW5lLm1hcmtlZFNwYW5zLmNvbmNhdChbc3Bhbl0pIDogW3NwYW5dO1xyXG4gICAgc3Bhbi5tYXJrZXIuYXR0YWNoTGluZShsaW5lKTtcclxuICB9XHJcblxyXG4gIC8vIFVzZWQgZm9yIHRoZSBhbGdvcml0aG0gdGhhdCBhZGp1c3RzIG1hcmtlcnMgZm9yIGEgY2hhbmdlIGluIHRoZVxyXG4gIC8vIGRvY3VtZW50LiBUaGVzZSBmdW5jdGlvbnMgY3V0IGFuIGFycmF5IG9mIHNwYW5zIGF0IGEgZ2l2ZW5cclxuICAvLyBjaGFyYWN0ZXIgcG9zaXRpb24sIHJldHVybmluZyBhbiBhcnJheSBvZiByZW1haW5pbmcgY2h1bmtzIChvclxyXG4gIC8vIHVuZGVmaW5lZCBpZiBub3RoaW5nIHJlbWFpbnMpLlxyXG4gIGZ1bmN0aW9uIG1hcmtlZFNwYW5zQmVmb3JlKG9sZCwgc3RhcnRDaCwgaXNJbnNlcnQpIHtcclxuICAgIGlmIChvbGQpIGZvciAodmFyIGkgPSAwLCBudzsgaSA8IG9sZC5sZW5ndGg7ICsraSkge1xyXG4gICAgICB2YXIgc3BhbiA9IG9sZFtpXSwgbWFya2VyID0gc3Bhbi5tYXJrZXI7XHJcbiAgICAgIHZhciBzdGFydHNCZWZvcmUgPSBzcGFuLmZyb20gPT0gbnVsbCB8fCAobWFya2VyLmluY2x1c2l2ZUxlZnQgPyBzcGFuLmZyb20gPD0gc3RhcnRDaCA6IHNwYW4uZnJvbSA8IHN0YXJ0Q2gpO1xyXG4gICAgICBpZiAoc3RhcnRzQmVmb3JlIHx8IHNwYW4uZnJvbSA9PSBzdGFydENoICYmIG1hcmtlci50eXBlID09IFwiYm9va21hcmtcIiAmJiAoIWlzSW5zZXJ0IHx8ICFzcGFuLm1hcmtlci5pbnNlcnRMZWZ0KSkge1xyXG4gICAgICAgIHZhciBlbmRzQWZ0ZXIgPSBzcGFuLnRvID09IG51bGwgfHwgKG1hcmtlci5pbmNsdXNpdmVSaWdodCA/IHNwYW4udG8gPj0gc3RhcnRDaCA6IHNwYW4udG8gPiBzdGFydENoKTtcclxuICAgICAgICAobncgfHwgKG53ID0gW10pKS5wdXNoKG5ldyBNYXJrZWRTcGFuKG1hcmtlciwgc3Bhbi5mcm9tLCBlbmRzQWZ0ZXIgPyBudWxsIDogc3Bhbi50bykpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICByZXR1cm4gbnc7XHJcbiAgfVxyXG4gIGZ1bmN0aW9uIG1hcmtlZFNwYW5zQWZ0ZXIob2xkLCBlbmRDaCwgaXNJbnNlcnQpIHtcclxuICAgIGlmIChvbGQpIGZvciAodmFyIGkgPSAwLCBudzsgaSA8IG9sZC5sZW5ndGg7ICsraSkge1xyXG4gICAgICB2YXIgc3BhbiA9IG9sZFtpXSwgbWFya2VyID0gc3Bhbi5tYXJrZXI7XHJcbiAgICAgIHZhciBlbmRzQWZ0ZXIgPSBzcGFuLnRvID09IG51bGwgfHwgKG1hcmtlci5pbmNsdXNpdmVSaWdodCA/IHNwYW4udG8gPj0gZW5kQ2ggOiBzcGFuLnRvID4gZW5kQ2gpO1xyXG4gICAgICBpZiAoZW5kc0FmdGVyIHx8IHNwYW4uZnJvbSA9PSBlbmRDaCAmJiBtYXJrZXIudHlwZSA9PSBcImJvb2ttYXJrXCIgJiYgKCFpc0luc2VydCB8fCBzcGFuLm1hcmtlci5pbnNlcnRMZWZ0KSkge1xyXG4gICAgICAgIHZhciBzdGFydHNCZWZvcmUgPSBzcGFuLmZyb20gPT0gbnVsbCB8fCAobWFya2VyLmluY2x1c2l2ZUxlZnQgPyBzcGFuLmZyb20gPD0gZW5kQ2ggOiBzcGFuLmZyb20gPCBlbmRDaCk7XHJcbiAgICAgICAgKG53IHx8IChudyA9IFtdKSkucHVzaChuZXcgTWFya2VkU3BhbihtYXJrZXIsIHN0YXJ0c0JlZm9yZSA/IG51bGwgOiBzcGFuLmZyb20gLSBlbmRDaCxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwYW4udG8gPT0gbnVsbCA/IG51bGwgOiBzcGFuLnRvIC0gZW5kQ2gpKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgcmV0dXJuIG53O1xyXG4gIH1cclxuXHJcbiAgLy8gR2l2ZW4gYSBjaGFuZ2Ugb2JqZWN0LCBjb21wdXRlIHRoZSBuZXcgc2V0IG9mIG1hcmtlciBzcGFucyB0aGF0XHJcbiAgLy8gY292ZXIgdGhlIGxpbmUgaW4gd2hpY2ggdGhlIGNoYW5nZSB0b29rIHBsYWNlLiBSZW1vdmVzIHNwYW5zXHJcbiAgLy8gZW50aXJlbHkgd2l0aGluIHRoZSBjaGFuZ2UsIHJlY29ubmVjdHMgc3BhbnMgYmVsb25naW5nIHRvIHRoZVxyXG4gIC8vIHNhbWUgbWFya2VyIHRoYXQgYXBwZWFyIG9uIGJvdGggc2lkZXMgb2YgdGhlIGNoYW5nZSwgYW5kIGN1dHMgb2ZmXHJcbiAgLy8gc3BhbnMgcGFydGlhbGx5IHdpdGhpbiB0aGUgY2hhbmdlLiBSZXR1cm5zIGFuIGFycmF5IG9mIHNwYW5cclxuICAvLyBhcnJheXMgd2l0aCBvbmUgZWxlbWVudCBmb3IgZWFjaCBsaW5lIGluIChhZnRlcikgdGhlIGNoYW5nZS5cclxuICBmdW5jdGlvbiBzdHJldGNoU3BhbnNPdmVyQ2hhbmdlKGRvYywgY2hhbmdlKSB7XHJcbiAgICBpZiAoY2hhbmdlLmZ1bGwpIHJldHVybiBudWxsO1xyXG4gICAgdmFyIG9sZEZpcnN0ID0gaXNMaW5lKGRvYywgY2hhbmdlLmZyb20ubGluZSkgJiYgZ2V0TGluZShkb2MsIGNoYW5nZS5mcm9tLmxpbmUpLm1hcmtlZFNwYW5zO1xyXG4gICAgdmFyIG9sZExhc3QgPSBpc0xpbmUoZG9jLCBjaGFuZ2UudG8ubGluZSkgJiYgZ2V0TGluZShkb2MsIGNoYW5nZS50by5saW5lKS5tYXJrZWRTcGFucztcclxuICAgIGlmICghb2xkRmlyc3QgJiYgIW9sZExhc3QpIHJldHVybiBudWxsO1xyXG5cclxuICAgIHZhciBzdGFydENoID0gY2hhbmdlLmZyb20uY2gsIGVuZENoID0gY2hhbmdlLnRvLmNoLCBpc0luc2VydCA9IGNtcChjaGFuZ2UuZnJvbSwgY2hhbmdlLnRvKSA9PSAwO1xyXG4gICAgLy8gR2V0IHRoZSBzcGFucyB0aGF0ICdzdGljayBvdXQnIG9uIGJvdGggc2lkZXNcclxuICAgIHZhciBmaXJzdCA9IG1hcmtlZFNwYW5zQmVmb3JlKG9sZEZpcnN0LCBzdGFydENoLCBpc0luc2VydCk7XHJcbiAgICB2YXIgbGFzdCA9IG1hcmtlZFNwYW5zQWZ0ZXIob2xkTGFzdCwgZW5kQ2gsIGlzSW5zZXJ0KTtcclxuXHJcbiAgICAvLyBOZXh0LCBtZXJnZSB0aG9zZSB0d28gZW5kc1xyXG4gICAgdmFyIHNhbWVMaW5lID0gY2hhbmdlLnRleHQubGVuZ3RoID09IDEsIG9mZnNldCA9IGxzdChjaGFuZ2UudGV4dCkubGVuZ3RoICsgKHNhbWVMaW5lID8gc3RhcnRDaCA6IDApO1xyXG4gICAgaWYgKGZpcnN0KSB7XHJcbiAgICAgIC8vIEZpeCB1cCAudG8gcHJvcGVydGllcyBvZiBmaXJzdFxyXG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGZpcnN0Lmxlbmd0aDsgKytpKSB7XHJcbiAgICAgICAgdmFyIHNwYW4gPSBmaXJzdFtpXTtcclxuICAgICAgICBpZiAoc3Bhbi50byA9PSBudWxsKSB7XHJcbiAgICAgICAgICB2YXIgZm91bmQgPSBnZXRNYXJrZWRTcGFuRm9yKGxhc3QsIHNwYW4ubWFya2VyKTtcclxuICAgICAgICAgIGlmICghZm91bmQpIHNwYW4udG8gPSBzdGFydENoO1xyXG4gICAgICAgICAgZWxzZSBpZiAoc2FtZUxpbmUpIHNwYW4udG8gPSBmb3VuZC50byA9PSBudWxsID8gbnVsbCA6IGZvdW5kLnRvICsgb2Zmc2V0O1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgaWYgKGxhc3QpIHtcclxuICAgICAgLy8gRml4IHVwIC5mcm9tIGluIGxhc3QgKG9yIG1vdmUgdGhlbSBpbnRvIGZpcnN0IGluIGNhc2Ugb2Ygc2FtZUxpbmUpXHJcbiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbGFzdC5sZW5ndGg7ICsraSkge1xyXG4gICAgICAgIHZhciBzcGFuID0gbGFzdFtpXTtcclxuICAgICAgICBpZiAoc3Bhbi50byAhPSBudWxsKSBzcGFuLnRvICs9IG9mZnNldDtcclxuICAgICAgICBpZiAoc3Bhbi5mcm9tID09IG51bGwpIHtcclxuICAgICAgICAgIHZhciBmb3VuZCA9IGdldE1hcmtlZFNwYW5Gb3IoZmlyc3QsIHNwYW4ubWFya2VyKTtcclxuICAgICAgICAgIGlmICghZm91bmQpIHtcclxuICAgICAgICAgICAgc3Bhbi5mcm9tID0gb2Zmc2V0O1xyXG4gICAgICAgICAgICBpZiAoc2FtZUxpbmUpIChmaXJzdCB8fCAoZmlyc3QgPSBbXSkpLnB1c2goc3Bhbik7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgIHNwYW4uZnJvbSArPSBvZmZzZXQ7XHJcbiAgICAgICAgICBpZiAoc2FtZUxpbmUpIChmaXJzdCB8fCAoZmlyc3QgPSBbXSkpLnB1c2goc3Bhbik7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICAvLyBNYWtlIHN1cmUgd2UgZGlkbid0IGNyZWF0ZSBhbnkgemVyby1sZW5ndGggc3BhbnNcclxuICAgIGlmIChmaXJzdCkgZmlyc3QgPSBjbGVhckVtcHR5U3BhbnMoZmlyc3QpO1xyXG4gICAgaWYgKGxhc3QgJiYgbGFzdCAhPSBmaXJzdCkgbGFzdCA9IGNsZWFyRW1wdHlTcGFucyhsYXN0KTtcclxuXHJcbiAgICB2YXIgbmV3TWFya2VycyA9IFtmaXJzdF07XHJcbiAgICBpZiAoIXNhbWVMaW5lKSB7XHJcbiAgICAgIC8vIEZpbGwgZ2FwIHdpdGggd2hvbGUtbGluZS1zcGFuc1xyXG4gICAgICB2YXIgZ2FwID0gY2hhbmdlLnRleHQubGVuZ3RoIC0gMiwgZ2FwTWFya2VycztcclxuICAgICAgaWYgKGdhcCA+IDAgJiYgZmlyc3QpXHJcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBmaXJzdC5sZW5ndGg7ICsraSlcclxuICAgICAgICAgIGlmIChmaXJzdFtpXS50byA9PSBudWxsKVxyXG4gICAgICAgICAgICAoZ2FwTWFya2VycyB8fCAoZ2FwTWFya2VycyA9IFtdKSkucHVzaChuZXcgTWFya2VkU3BhbihmaXJzdFtpXS5tYXJrZXIsIG51bGwsIG51bGwpKTtcclxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBnYXA7ICsraSlcclxuICAgICAgICBuZXdNYXJrZXJzLnB1c2goZ2FwTWFya2Vycyk7XHJcbiAgICAgIG5ld01hcmtlcnMucHVzaChsYXN0KTtcclxuICAgIH1cclxuICAgIHJldHVybiBuZXdNYXJrZXJzO1xyXG4gIH1cclxuXHJcbiAgLy8gUmVtb3ZlIHNwYW5zIHRoYXQgYXJlIGVtcHR5IGFuZCBkb24ndCBoYXZlIGEgY2xlYXJXaGVuRW1wdHlcclxuICAvLyBvcHRpb24gb2YgZmFsc2UuXHJcbiAgZnVuY3Rpb24gY2xlYXJFbXB0eVNwYW5zKHNwYW5zKSB7XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IHNwYW5zLmxlbmd0aDsgKytpKSB7XHJcbiAgICAgIHZhciBzcGFuID0gc3BhbnNbaV07XHJcbiAgICAgIGlmIChzcGFuLmZyb20gIT0gbnVsbCAmJiBzcGFuLmZyb20gPT0gc3Bhbi50byAmJiBzcGFuLm1hcmtlci5jbGVhcldoZW5FbXB0eSAhPT0gZmFsc2UpXHJcbiAgICAgICAgc3BhbnMuc3BsaWNlKGktLSwgMSk7XHJcbiAgICB9XHJcbiAgICBpZiAoIXNwYW5zLmxlbmd0aCkgcmV0dXJuIG51bGw7XHJcbiAgICByZXR1cm4gc3BhbnM7XHJcbiAgfVxyXG5cclxuICAvLyBVc2VkIGZvciB1bi9yZS1kb2luZyBjaGFuZ2VzIGZyb20gdGhlIGhpc3RvcnkuIENvbWJpbmVzIHRoZVxyXG4gIC8vIHJlc3VsdCBvZiBjb21wdXRpbmcgdGhlIGV4aXN0aW5nIHNwYW5zIHdpdGggdGhlIHNldCBvZiBzcGFucyB0aGF0XHJcbiAgLy8gZXhpc3RlZCBpbiB0aGUgaGlzdG9yeSAoc28gdGhhdCBkZWxldGluZyBhcm91bmQgYSBzcGFuIGFuZCB0aGVuXHJcbiAgLy8gdW5kb2luZyBicmluZ3MgYmFjayB0aGUgc3BhbikuXHJcbiAgZnVuY3Rpb24gbWVyZ2VPbGRTcGFucyhkb2MsIGNoYW5nZSkge1xyXG4gICAgdmFyIG9sZCA9IGdldE9sZFNwYW5zKGRvYywgY2hhbmdlKTtcclxuICAgIHZhciBzdHJldGNoZWQgPSBzdHJldGNoU3BhbnNPdmVyQ2hhbmdlKGRvYywgY2hhbmdlKTtcclxuICAgIGlmICghb2xkKSByZXR1cm4gc3RyZXRjaGVkO1xyXG4gICAgaWYgKCFzdHJldGNoZWQpIHJldHVybiBvbGQ7XHJcblxyXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBvbGQubGVuZ3RoOyArK2kpIHtcclxuICAgICAgdmFyIG9sZEN1ciA9IG9sZFtpXSwgc3RyZXRjaEN1ciA9IHN0cmV0Y2hlZFtpXTtcclxuICAgICAgaWYgKG9sZEN1ciAmJiBzdHJldGNoQ3VyKSB7XHJcbiAgICAgICAgc3BhbnM6IGZvciAodmFyIGogPSAwOyBqIDwgc3RyZXRjaEN1ci5sZW5ndGg7ICsraikge1xyXG4gICAgICAgICAgdmFyIHNwYW4gPSBzdHJldGNoQ3VyW2pdO1xyXG4gICAgICAgICAgZm9yICh2YXIgayA9IDA7IGsgPCBvbGRDdXIubGVuZ3RoOyArK2spXHJcbiAgICAgICAgICAgIGlmIChvbGRDdXJba10ubWFya2VyID09IHNwYW4ubWFya2VyKSBjb250aW51ZSBzcGFucztcclxuICAgICAgICAgIG9sZEN1ci5wdXNoKHNwYW4pO1xyXG4gICAgICAgIH1cclxuICAgICAgfSBlbHNlIGlmIChzdHJldGNoQ3VyKSB7XHJcbiAgICAgICAgb2xkW2ldID0gc3RyZXRjaEN1cjtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgcmV0dXJuIG9sZDtcclxuICB9XHJcblxyXG4gIC8vIFVzZWQgdG8gJ2NsaXAnIG91dCByZWFkT25seSByYW5nZXMgd2hlbiBtYWtpbmcgYSBjaGFuZ2UuXHJcbiAgZnVuY3Rpb24gcmVtb3ZlUmVhZE9ubHlSYW5nZXMoZG9jLCBmcm9tLCB0bykge1xyXG4gICAgdmFyIG1hcmtlcnMgPSBudWxsO1xyXG4gICAgZG9jLml0ZXIoZnJvbS5saW5lLCB0by5saW5lICsgMSwgZnVuY3Rpb24obGluZSkge1xyXG4gICAgICBpZiAobGluZS5tYXJrZWRTcGFucykgZm9yICh2YXIgaSA9IDA7IGkgPCBsaW5lLm1hcmtlZFNwYW5zLmxlbmd0aDsgKytpKSB7XHJcbiAgICAgICAgdmFyIG1hcmsgPSBsaW5lLm1hcmtlZFNwYW5zW2ldLm1hcmtlcjtcclxuICAgICAgICBpZiAobWFyay5yZWFkT25seSAmJiAoIW1hcmtlcnMgfHwgaW5kZXhPZihtYXJrZXJzLCBtYXJrKSA9PSAtMSkpXHJcbiAgICAgICAgICAobWFya2VycyB8fCAobWFya2VycyA9IFtdKSkucHVzaChtYXJrKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgICBpZiAoIW1hcmtlcnMpIHJldHVybiBudWxsO1xyXG4gICAgdmFyIHBhcnRzID0gW3tmcm9tOiBmcm9tLCB0bzogdG99XTtcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbWFya2Vycy5sZW5ndGg7ICsraSkge1xyXG4gICAgICB2YXIgbWsgPSBtYXJrZXJzW2ldLCBtID0gbWsuZmluZCgwKTtcclxuICAgICAgZm9yICh2YXIgaiA9IDA7IGogPCBwYXJ0cy5sZW5ndGg7ICsraikge1xyXG4gICAgICAgIHZhciBwID0gcGFydHNbal07XHJcbiAgICAgICAgaWYgKGNtcChwLnRvLCBtLmZyb20pIDwgMCB8fCBjbXAocC5mcm9tLCBtLnRvKSA+IDApIGNvbnRpbnVlO1xyXG4gICAgICAgIHZhciBuZXdQYXJ0cyA9IFtqLCAxXSwgZGZyb20gPSBjbXAocC5mcm9tLCBtLmZyb20pLCBkdG8gPSBjbXAocC50bywgbS50byk7XHJcbiAgICAgICAgaWYgKGRmcm9tIDwgMCB8fCAhbWsuaW5jbHVzaXZlTGVmdCAmJiAhZGZyb20pXHJcbiAgICAgICAgICBuZXdQYXJ0cy5wdXNoKHtmcm9tOiBwLmZyb20sIHRvOiBtLmZyb219KTtcclxuICAgICAgICBpZiAoZHRvID4gMCB8fCAhbWsuaW5jbHVzaXZlUmlnaHQgJiYgIWR0bylcclxuICAgICAgICAgIG5ld1BhcnRzLnB1c2goe2Zyb206IG0udG8sIHRvOiBwLnRvfSk7XHJcbiAgICAgICAgcGFydHMuc3BsaWNlLmFwcGx5KHBhcnRzLCBuZXdQYXJ0cyk7XHJcbiAgICAgICAgaiArPSBuZXdQYXJ0cy5sZW5ndGggLSAxO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICByZXR1cm4gcGFydHM7XHJcbiAgfVxyXG5cclxuICAvLyBDb25uZWN0IG9yIGRpc2Nvbm5lY3Qgc3BhbnMgZnJvbSBhIGxpbmUuXHJcbiAgZnVuY3Rpb24gZGV0YWNoTWFya2VkU3BhbnMobGluZSkge1xyXG4gICAgdmFyIHNwYW5zID0gbGluZS5tYXJrZWRTcGFucztcclxuICAgIGlmICghc3BhbnMpIHJldHVybjtcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgc3BhbnMubGVuZ3RoOyArK2kpXHJcbiAgICAgIHNwYW5zW2ldLm1hcmtlci5kZXRhY2hMaW5lKGxpbmUpO1xyXG4gICAgbGluZS5tYXJrZWRTcGFucyA9IG51bGw7XHJcbiAgfVxyXG4gIGZ1bmN0aW9uIGF0dGFjaE1hcmtlZFNwYW5zKGxpbmUsIHNwYW5zKSB7XHJcbiAgICBpZiAoIXNwYW5zKSByZXR1cm47XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IHNwYW5zLmxlbmd0aDsgKytpKVxyXG4gICAgICBzcGFuc1tpXS5tYXJrZXIuYXR0YWNoTGluZShsaW5lKTtcclxuICAgIGxpbmUubWFya2VkU3BhbnMgPSBzcGFucztcclxuICB9XHJcblxyXG4gIC8vIEhlbHBlcnMgdXNlZCB3aGVuIGNvbXB1dGluZyB3aGljaCBvdmVybGFwcGluZyBjb2xsYXBzZWQgc3BhblxyXG4gIC8vIGNvdW50cyBhcyB0aGUgbGFyZ2VyIG9uZS5cclxuICBmdW5jdGlvbiBleHRyYUxlZnQobWFya2VyKSB7IHJldHVybiBtYXJrZXIuaW5jbHVzaXZlTGVmdCA/IC0xIDogMDsgfVxyXG4gIGZ1bmN0aW9uIGV4dHJhUmlnaHQobWFya2VyKSB7IHJldHVybiBtYXJrZXIuaW5jbHVzaXZlUmlnaHQgPyAxIDogMDsgfVxyXG5cclxuICAvLyBSZXR1cm5zIGEgbnVtYmVyIGluZGljYXRpbmcgd2hpY2ggb2YgdHdvIG92ZXJsYXBwaW5nIGNvbGxhcHNlZFxyXG4gIC8vIHNwYW5zIGlzIGxhcmdlciAoYW5kIHRodXMgaW5jbHVkZXMgdGhlIG90aGVyKS4gRmFsbHMgYmFjayB0b1xyXG4gIC8vIGNvbXBhcmluZyBpZHMgd2hlbiB0aGUgc3BhbnMgY292ZXIgZXhhY3RseSB0aGUgc2FtZSByYW5nZS5cclxuICBmdW5jdGlvbiBjb21wYXJlQ29sbGFwc2VkTWFya2VycyhhLCBiKSB7XHJcbiAgICB2YXIgbGVuRGlmZiA9IGEubGluZXMubGVuZ3RoIC0gYi5saW5lcy5sZW5ndGg7XHJcbiAgICBpZiAobGVuRGlmZiAhPSAwKSByZXR1cm4gbGVuRGlmZjtcclxuICAgIHZhciBhUG9zID0gYS5maW5kKCksIGJQb3MgPSBiLmZpbmQoKTtcclxuICAgIHZhciBmcm9tQ21wID0gY21wKGFQb3MuZnJvbSwgYlBvcy5mcm9tKSB8fCBleHRyYUxlZnQoYSkgLSBleHRyYUxlZnQoYik7XHJcbiAgICBpZiAoZnJvbUNtcCkgcmV0dXJuIC1mcm9tQ21wO1xyXG4gICAgdmFyIHRvQ21wID0gY21wKGFQb3MudG8sIGJQb3MudG8pIHx8IGV4dHJhUmlnaHQoYSkgLSBleHRyYVJpZ2h0KGIpO1xyXG4gICAgaWYgKHRvQ21wKSByZXR1cm4gdG9DbXA7XHJcbiAgICByZXR1cm4gYi5pZCAtIGEuaWQ7XHJcbiAgfVxyXG5cclxuICAvLyBGaW5kIG91dCB3aGV0aGVyIGEgbGluZSBlbmRzIG9yIHN0YXJ0cyBpbiBhIGNvbGxhcHNlZCBzcGFuLiBJZlxyXG4gIC8vIHNvLCByZXR1cm4gdGhlIG1hcmtlciBmb3IgdGhhdCBzcGFuLlxyXG4gIGZ1bmN0aW9uIGNvbGxhcHNlZFNwYW5BdFNpZGUobGluZSwgc3RhcnQpIHtcclxuICAgIHZhciBzcHMgPSBzYXdDb2xsYXBzZWRTcGFucyAmJiBsaW5lLm1hcmtlZFNwYW5zLCBmb3VuZDtcclxuICAgIGlmIChzcHMpIGZvciAodmFyIHNwLCBpID0gMDsgaSA8IHNwcy5sZW5ndGg7ICsraSkge1xyXG4gICAgICBzcCA9IHNwc1tpXTtcclxuICAgICAgaWYgKHNwLm1hcmtlci5jb2xsYXBzZWQgJiYgKHN0YXJ0ID8gc3AuZnJvbSA6IHNwLnRvKSA9PSBudWxsICYmXHJcbiAgICAgICAgICAoIWZvdW5kIHx8IGNvbXBhcmVDb2xsYXBzZWRNYXJrZXJzKGZvdW5kLCBzcC5tYXJrZXIpIDwgMCkpXHJcbiAgICAgICAgZm91bmQgPSBzcC5tYXJrZXI7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gZm91bmQ7XHJcbiAgfVxyXG4gIGZ1bmN0aW9uIGNvbGxhcHNlZFNwYW5BdFN0YXJ0KGxpbmUpIHsgcmV0dXJuIGNvbGxhcHNlZFNwYW5BdFNpZGUobGluZSwgdHJ1ZSk7IH1cclxuICBmdW5jdGlvbiBjb2xsYXBzZWRTcGFuQXRFbmQobGluZSkgeyByZXR1cm4gY29sbGFwc2VkU3BhbkF0U2lkZShsaW5lLCBmYWxzZSk7IH1cclxuXHJcbiAgLy8gVGVzdCB3aGV0aGVyIHRoZXJlIGV4aXN0cyBhIGNvbGxhcHNlZCBzcGFuIHRoYXQgcGFydGlhbGx5XHJcbiAgLy8gb3ZlcmxhcHMgKGNvdmVycyB0aGUgc3RhcnQgb3IgZW5kLCBidXQgbm90IGJvdGgpIG9mIGEgbmV3IHNwYW4uXHJcbiAgLy8gU3VjaCBvdmVybGFwIGlzIG5vdCBhbGxvd2VkLlxyXG4gIGZ1bmN0aW9uIGNvbmZsaWN0aW5nQ29sbGFwc2VkUmFuZ2UoZG9jLCBsaW5lTm8sIGZyb20sIHRvLCBtYXJrZXIpIHtcclxuICAgIHZhciBsaW5lID0gZ2V0TGluZShkb2MsIGxpbmVObyk7XHJcbiAgICB2YXIgc3BzID0gc2F3Q29sbGFwc2VkU3BhbnMgJiYgbGluZS5tYXJrZWRTcGFucztcclxuICAgIGlmIChzcHMpIGZvciAodmFyIGkgPSAwOyBpIDwgc3BzLmxlbmd0aDsgKytpKSB7XHJcbiAgICAgIHZhciBzcCA9IHNwc1tpXTtcclxuICAgICAgaWYgKCFzcC5tYXJrZXIuY29sbGFwc2VkKSBjb250aW51ZTtcclxuICAgICAgdmFyIGZvdW5kID0gc3AubWFya2VyLmZpbmQoMCk7XHJcbiAgICAgIHZhciBmcm9tQ21wID0gY21wKGZvdW5kLmZyb20sIGZyb20pIHx8IGV4dHJhTGVmdChzcC5tYXJrZXIpIC0gZXh0cmFMZWZ0KG1hcmtlcik7XHJcbiAgICAgIHZhciB0b0NtcCA9IGNtcChmb3VuZC50bywgdG8pIHx8IGV4dHJhUmlnaHQoc3AubWFya2VyKSAtIGV4dHJhUmlnaHQobWFya2VyKTtcclxuICAgICAgaWYgKGZyb21DbXAgPj0gMCAmJiB0b0NtcCA8PSAwIHx8IGZyb21DbXAgPD0gMCAmJiB0b0NtcCA+PSAwKSBjb250aW51ZTtcclxuICAgICAgaWYgKGZyb21DbXAgPD0gMCAmJiAoY21wKGZvdW5kLnRvLCBmcm9tKSA+IDAgfHwgKHNwLm1hcmtlci5pbmNsdXNpdmVSaWdodCAmJiBtYXJrZXIuaW5jbHVzaXZlTGVmdCkpIHx8XHJcbiAgICAgICAgICBmcm9tQ21wID49IDAgJiYgKGNtcChmb3VuZC5mcm9tLCB0bykgPCAwIHx8IChzcC5tYXJrZXIuaW5jbHVzaXZlTGVmdCAmJiBtYXJrZXIuaW5jbHVzaXZlUmlnaHQpKSlcclxuICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIEEgdmlzdWFsIGxpbmUgaXMgYSBsaW5lIGFzIGRyYXduIG9uIHRoZSBzY3JlZW4uIEZvbGRpbmcsIGZvclxyXG4gIC8vIGV4YW1wbGUsIGNhbiBjYXVzZSBtdWx0aXBsZSBsb2dpY2FsIGxpbmVzIHRvIGFwcGVhciBvbiB0aGUgc2FtZVxyXG4gIC8vIHZpc3VhbCBsaW5lLiBUaGlzIGZpbmRzIHRoZSBzdGFydCBvZiB0aGUgdmlzdWFsIGxpbmUgdGhhdCB0aGVcclxuICAvLyBnaXZlbiBsaW5lIGlzIHBhcnQgb2YgKHVzdWFsbHkgdGhhdCBpcyB0aGUgbGluZSBpdHNlbGYpLlxyXG4gIGZ1bmN0aW9uIHZpc3VhbExpbmUobGluZSkge1xyXG4gICAgdmFyIG1lcmdlZDtcclxuICAgIHdoaWxlIChtZXJnZWQgPSBjb2xsYXBzZWRTcGFuQXRTdGFydChsaW5lKSlcclxuICAgICAgbGluZSA9IG1lcmdlZC5maW5kKC0xLCB0cnVlKS5saW5lO1xyXG4gICAgcmV0dXJuIGxpbmU7XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm5zIGFuIGFycmF5IG9mIGxvZ2ljYWwgbGluZXMgdGhhdCBjb250aW51ZSB0aGUgdmlzdWFsIGxpbmVcclxuICAvLyBzdGFydGVkIGJ5IHRoZSBhcmd1bWVudCwgb3IgdW5kZWZpbmVkIGlmIHRoZXJlIGFyZSBubyBzdWNoIGxpbmVzLlxyXG4gIGZ1bmN0aW9uIHZpc3VhbExpbmVDb250aW51ZWQobGluZSkge1xyXG4gICAgdmFyIG1lcmdlZCwgbGluZXM7XHJcbiAgICB3aGlsZSAobWVyZ2VkID0gY29sbGFwc2VkU3BhbkF0RW5kKGxpbmUpKSB7XHJcbiAgICAgIGxpbmUgPSBtZXJnZWQuZmluZCgxLCB0cnVlKS5saW5lO1xyXG4gICAgICAobGluZXMgfHwgKGxpbmVzID0gW10pKS5wdXNoKGxpbmUpO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIGxpbmVzO1xyXG4gIH1cclxuXHJcbiAgLy8gR2V0IHRoZSBsaW5lIG51bWJlciBvZiB0aGUgc3RhcnQgb2YgdGhlIHZpc3VhbCBsaW5lIHRoYXQgdGhlXHJcbiAgLy8gZ2l2ZW4gbGluZSBudW1iZXIgaXMgcGFydCBvZi5cclxuICBmdW5jdGlvbiB2aXN1YWxMaW5lTm8oZG9jLCBsaW5lTikge1xyXG4gICAgdmFyIGxpbmUgPSBnZXRMaW5lKGRvYywgbGluZU4pLCB2aXMgPSB2aXN1YWxMaW5lKGxpbmUpO1xyXG4gICAgaWYgKGxpbmUgPT0gdmlzKSByZXR1cm4gbGluZU47XHJcbiAgICByZXR1cm4gbGluZU5vKHZpcyk7XHJcbiAgfVxyXG4gIC8vIEdldCB0aGUgbGluZSBudW1iZXIgb2YgdGhlIHN0YXJ0IG9mIHRoZSBuZXh0IHZpc3VhbCBsaW5lIGFmdGVyXHJcbiAgLy8gdGhlIGdpdmVuIGxpbmUuXHJcbiAgZnVuY3Rpb24gdmlzdWFsTGluZUVuZE5vKGRvYywgbGluZU4pIHtcclxuICAgIGlmIChsaW5lTiA+IGRvYy5sYXN0TGluZSgpKSByZXR1cm4gbGluZU47XHJcbiAgICB2YXIgbGluZSA9IGdldExpbmUoZG9jLCBsaW5lTiksIG1lcmdlZDtcclxuICAgIGlmICghbGluZUlzSGlkZGVuKGRvYywgbGluZSkpIHJldHVybiBsaW5lTjtcclxuICAgIHdoaWxlIChtZXJnZWQgPSBjb2xsYXBzZWRTcGFuQXRFbmQobGluZSkpXHJcbiAgICAgIGxpbmUgPSBtZXJnZWQuZmluZCgxLCB0cnVlKS5saW5lO1xyXG4gICAgcmV0dXJuIGxpbmVObyhsaW5lKSArIDE7XHJcbiAgfVxyXG5cclxuICAvLyBDb21wdXRlIHdoZXRoZXIgYSBsaW5lIGlzIGhpZGRlbi4gTGluZXMgY291bnQgYXMgaGlkZGVuIHdoZW4gdGhleVxyXG4gIC8vIGFyZSBwYXJ0IG9mIGEgdmlzdWFsIGxpbmUgdGhhdCBzdGFydHMgd2l0aCBhbm90aGVyIGxpbmUsIG9yIHdoZW5cclxuICAvLyB0aGV5IGFyZSBlbnRpcmVseSBjb3ZlcmVkIGJ5IGNvbGxhcHNlZCwgbm9uLXdpZGdldCBzcGFuLlxyXG4gIGZ1bmN0aW9uIGxpbmVJc0hpZGRlbihkb2MsIGxpbmUpIHtcclxuICAgIHZhciBzcHMgPSBzYXdDb2xsYXBzZWRTcGFucyAmJiBsaW5lLm1hcmtlZFNwYW5zO1xyXG4gICAgaWYgKHNwcykgZm9yICh2YXIgc3AsIGkgPSAwOyBpIDwgc3BzLmxlbmd0aDsgKytpKSB7XHJcbiAgICAgIHNwID0gc3BzW2ldO1xyXG4gICAgICBpZiAoIXNwLm1hcmtlci5jb2xsYXBzZWQpIGNvbnRpbnVlO1xyXG4gICAgICBpZiAoc3AuZnJvbSA9PSBudWxsKSByZXR1cm4gdHJ1ZTtcclxuICAgICAgaWYgKHNwLm1hcmtlci53aWRnZXROb2RlKSBjb250aW51ZTtcclxuICAgICAgaWYgKHNwLmZyb20gPT0gMCAmJiBzcC5tYXJrZXIuaW5jbHVzaXZlTGVmdCAmJiBsaW5lSXNIaWRkZW5Jbm5lcihkb2MsIGxpbmUsIHNwKSlcclxuICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuICB9XHJcbiAgZnVuY3Rpb24gbGluZUlzSGlkZGVuSW5uZXIoZG9jLCBsaW5lLCBzcGFuKSB7XHJcbiAgICBpZiAoc3Bhbi50byA9PSBudWxsKSB7XHJcbiAgICAgIHZhciBlbmQgPSBzcGFuLm1hcmtlci5maW5kKDEsIHRydWUpO1xyXG4gICAgICByZXR1cm4gbGluZUlzSGlkZGVuSW5uZXIoZG9jLCBlbmQubGluZSwgZ2V0TWFya2VkU3BhbkZvcihlbmQubGluZS5tYXJrZWRTcGFucywgc3Bhbi5tYXJrZXIpKTtcclxuICAgIH1cclxuICAgIGlmIChzcGFuLm1hcmtlci5pbmNsdXNpdmVSaWdodCAmJiBzcGFuLnRvID09IGxpbmUudGV4dC5sZW5ndGgpXHJcbiAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgZm9yICh2YXIgc3AsIGkgPSAwOyBpIDwgbGluZS5tYXJrZWRTcGFucy5sZW5ndGg7ICsraSkge1xyXG4gICAgICBzcCA9IGxpbmUubWFya2VkU3BhbnNbaV07XHJcbiAgICAgIGlmIChzcC5tYXJrZXIuY29sbGFwc2VkICYmICFzcC5tYXJrZXIud2lkZ2V0Tm9kZSAmJiBzcC5mcm9tID09IHNwYW4udG8gJiZcclxuICAgICAgICAgIChzcC50byA9PSBudWxsIHx8IHNwLnRvICE9IHNwYW4uZnJvbSkgJiZcclxuICAgICAgICAgIChzcC5tYXJrZXIuaW5jbHVzaXZlTGVmdCB8fCBzcGFuLm1hcmtlci5pbmNsdXNpdmVSaWdodCkgJiZcclxuICAgICAgICAgIGxpbmVJc0hpZGRlbklubmVyKGRvYywgbGluZSwgc3ApKSByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIExJTkUgV0lER0VUU1xyXG5cclxuICAvLyBMaW5lIHdpZGdldHMgYXJlIGJsb2NrIGVsZW1lbnRzIGRpc3BsYXllZCBhYm92ZSBvciBiZWxvdyBhIGxpbmUuXHJcblxyXG4gIHZhciBMaW5lV2lkZ2V0ID0gQ29kZU1pcnJvci5MaW5lV2lkZ2V0ID0gZnVuY3Rpb24oZG9jLCBub2RlLCBvcHRpb25zKSB7XHJcbiAgICBpZiAob3B0aW9ucykgZm9yICh2YXIgb3B0IGluIG9wdGlvbnMpIGlmIChvcHRpb25zLmhhc093blByb3BlcnR5KG9wdCkpXHJcbiAgICAgIHRoaXNbb3B0XSA9IG9wdGlvbnNbb3B0XTtcclxuICAgIHRoaXMuZG9jID0gZG9jO1xyXG4gICAgdGhpcy5ub2RlID0gbm9kZTtcclxuICB9O1xyXG4gIGV2ZW50TWl4aW4oTGluZVdpZGdldCk7XHJcblxyXG4gIGZ1bmN0aW9uIGFkanVzdFNjcm9sbFdoZW5BYm92ZVZpc2libGUoY20sIGxpbmUsIGRpZmYpIHtcclxuICAgIGlmIChoZWlnaHRBdExpbmUobGluZSkgPCAoKGNtLmN1ck9wICYmIGNtLmN1ck9wLnNjcm9sbFRvcCkgfHwgY20uZG9jLnNjcm9sbFRvcCkpXHJcbiAgICAgIGFkZFRvU2Nyb2xsUG9zKGNtLCBudWxsLCBkaWZmKTtcclxuICB9XHJcblxyXG4gIExpbmVXaWRnZXQucHJvdG90eXBlLmNsZWFyID0gZnVuY3Rpb24oKSB7XHJcbiAgICB2YXIgY20gPSB0aGlzLmRvYy5jbSwgd3MgPSB0aGlzLmxpbmUud2lkZ2V0cywgbGluZSA9IHRoaXMubGluZSwgbm8gPSBsaW5lTm8obGluZSk7XHJcbiAgICBpZiAobm8gPT0gbnVsbCB8fCAhd3MpIHJldHVybjtcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgd3MubGVuZ3RoOyArK2kpIGlmICh3c1tpXSA9PSB0aGlzKSB3cy5zcGxpY2UoaS0tLCAxKTtcclxuICAgIGlmICghd3MubGVuZ3RoKSBsaW5lLndpZGdldHMgPSBudWxsO1xyXG4gICAgdmFyIGhlaWdodCA9IHdpZGdldEhlaWdodCh0aGlzKTtcclxuICAgIHVwZGF0ZUxpbmVIZWlnaHQobGluZSwgTWF0aC5tYXgoMCwgbGluZS5oZWlnaHQgLSBoZWlnaHQpKTtcclxuICAgIGlmIChjbSkgcnVuSW5PcChjbSwgZnVuY3Rpb24oKSB7XHJcbiAgICAgIGFkanVzdFNjcm9sbFdoZW5BYm92ZVZpc2libGUoY20sIGxpbmUsIC1oZWlnaHQpO1xyXG4gICAgICByZWdMaW5lQ2hhbmdlKGNtLCBubywgXCJ3aWRnZXRcIik7XHJcbiAgICB9KTtcclxuICB9O1xyXG4gIExpbmVXaWRnZXQucHJvdG90eXBlLmNoYW5nZWQgPSBmdW5jdGlvbigpIHtcclxuICAgIHZhciBvbGRIID0gdGhpcy5oZWlnaHQsIGNtID0gdGhpcy5kb2MuY20sIGxpbmUgPSB0aGlzLmxpbmU7XHJcbiAgICB0aGlzLmhlaWdodCA9IG51bGw7XHJcbiAgICB2YXIgZGlmZiA9IHdpZGdldEhlaWdodCh0aGlzKSAtIG9sZEg7XHJcbiAgICBpZiAoIWRpZmYpIHJldHVybjtcclxuICAgIHVwZGF0ZUxpbmVIZWlnaHQobGluZSwgbGluZS5oZWlnaHQgKyBkaWZmKTtcclxuICAgIGlmIChjbSkgcnVuSW5PcChjbSwgZnVuY3Rpb24oKSB7XHJcbiAgICAgIGNtLmN1ck9wLmZvcmNlVXBkYXRlID0gdHJ1ZTtcclxuICAgICAgYWRqdXN0U2Nyb2xsV2hlbkFib3ZlVmlzaWJsZShjbSwgbGluZSwgZGlmZik7XHJcbiAgICB9KTtcclxuICB9O1xyXG5cclxuICBmdW5jdGlvbiB3aWRnZXRIZWlnaHQod2lkZ2V0KSB7XHJcbiAgICBpZiAod2lkZ2V0LmhlaWdodCAhPSBudWxsKSByZXR1cm4gd2lkZ2V0LmhlaWdodDtcclxuICAgIHZhciBjbSA9IHdpZGdldC5kb2MuY207XHJcbiAgICBpZiAoIWNtKSByZXR1cm4gMDtcclxuICAgIGlmICghY29udGFpbnMoZG9jdW1lbnQuYm9keSwgd2lkZ2V0Lm5vZGUpKSB7XHJcbiAgICAgIHZhciBwYXJlbnRTdHlsZSA9IFwicG9zaXRpb246IHJlbGF0aXZlO1wiO1xyXG4gICAgICBpZiAod2lkZ2V0LmNvdmVyR3V0dGVyKVxyXG4gICAgICAgIHBhcmVudFN0eWxlICs9IFwibWFyZ2luLWxlZnQ6IC1cIiArIGNtLmRpc3BsYXkuZ3V0dGVycy5vZmZzZXRXaWR0aCArIFwicHg7XCI7XHJcbiAgICAgIGlmICh3aWRnZXQubm9IU2Nyb2xsKVxyXG4gICAgICAgIHBhcmVudFN0eWxlICs9IFwid2lkdGg6IFwiICsgY20uZGlzcGxheS53cmFwcGVyLmNsaWVudFdpZHRoICsgXCJweDtcIjtcclxuICAgICAgcmVtb3ZlQ2hpbGRyZW5BbmRBZGQoY20uZGlzcGxheS5tZWFzdXJlLCBlbHQoXCJkaXZcIiwgW3dpZGdldC5ub2RlXSwgbnVsbCwgcGFyZW50U3R5bGUpKTtcclxuICAgIH1cclxuICAgIHJldHVybiB3aWRnZXQuaGVpZ2h0ID0gd2lkZ2V0Lm5vZGUub2Zmc2V0SGVpZ2h0O1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gYWRkTGluZVdpZGdldChkb2MsIGhhbmRsZSwgbm9kZSwgb3B0aW9ucykge1xyXG4gICAgdmFyIHdpZGdldCA9IG5ldyBMaW5lV2lkZ2V0KGRvYywgbm9kZSwgb3B0aW9ucyk7XHJcbiAgICB2YXIgY20gPSBkb2MuY207XHJcbiAgICBpZiAoY20gJiYgd2lkZ2V0Lm5vSFNjcm9sbCkgY20uZGlzcGxheS5hbGlnbldpZGdldHMgPSB0cnVlO1xyXG4gICAgY2hhbmdlTGluZShkb2MsIGhhbmRsZSwgXCJ3aWRnZXRcIiwgZnVuY3Rpb24obGluZSkge1xyXG4gICAgICB2YXIgd2lkZ2V0cyA9IGxpbmUud2lkZ2V0cyB8fCAobGluZS53aWRnZXRzID0gW10pO1xyXG4gICAgICBpZiAod2lkZ2V0Lmluc2VydEF0ID09IG51bGwpIHdpZGdldHMucHVzaCh3aWRnZXQpO1xyXG4gICAgICBlbHNlIHdpZGdldHMuc3BsaWNlKE1hdGgubWluKHdpZGdldHMubGVuZ3RoIC0gMSwgTWF0aC5tYXgoMCwgd2lkZ2V0Lmluc2VydEF0KSksIDAsIHdpZGdldCk7XHJcbiAgICAgIHdpZGdldC5saW5lID0gbGluZTtcclxuICAgICAgaWYgKGNtICYmICFsaW5lSXNIaWRkZW4oZG9jLCBsaW5lKSkge1xyXG4gICAgICAgIHZhciBhYm92ZVZpc2libGUgPSBoZWlnaHRBdExpbmUobGluZSkgPCBkb2Muc2Nyb2xsVG9wO1xyXG4gICAgICAgIHVwZGF0ZUxpbmVIZWlnaHQobGluZSwgbGluZS5oZWlnaHQgKyB3aWRnZXRIZWlnaHQod2lkZ2V0KSk7XHJcbiAgICAgICAgaWYgKGFib3ZlVmlzaWJsZSkgYWRkVG9TY3JvbGxQb3MoY20sIG51bGwsIHdpZGdldC5oZWlnaHQpO1xyXG4gICAgICAgIGNtLmN1ck9wLmZvcmNlVXBkYXRlID0gdHJ1ZTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH0pO1xyXG4gICAgcmV0dXJuIHdpZGdldDtcclxuICB9XHJcblxyXG4gIC8vIExJTkUgREFUQSBTVFJVQ1RVUkVcclxuXHJcbiAgLy8gTGluZSBvYmplY3RzLiBUaGVzZSBob2xkIHN0YXRlIHJlbGF0ZWQgdG8gYSBsaW5lLCBpbmNsdWRpbmdcclxuICAvLyBoaWdobGlnaHRpbmcgaW5mbyAodGhlIHN0eWxlcyBhcnJheSkuXHJcbiAgdmFyIExpbmUgPSBDb2RlTWlycm9yLkxpbmUgPSBmdW5jdGlvbih0ZXh0LCBtYXJrZWRTcGFucywgZXN0aW1hdGVIZWlnaHQpIHtcclxuICAgIHRoaXMudGV4dCA9IHRleHQ7XHJcbiAgICBhdHRhY2hNYXJrZWRTcGFucyh0aGlzLCBtYXJrZWRTcGFucyk7XHJcbiAgICB0aGlzLmhlaWdodCA9IGVzdGltYXRlSGVpZ2h0ID8gZXN0aW1hdGVIZWlnaHQodGhpcykgOiAxO1xyXG4gIH07XHJcbiAgZXZlbnRNaXhpbihMaW5lKTtcclxuICBMaW5lLnByb3RvdHlwZS5saW5lTm8gPSBmdW5jdGlvbigpIHsgcmV0dXJuIGxpbmVObyh0aGlzKTsgfTtcclxuXHJcbiAgLy8gQ2hhbmdlIHRoZSBjb250ZW50ICh0ZXh0LCBtYXJrZXJzKSBvZiBhIGxpbmUuIEF1dG9tYXRpY2FsbHlcclxuICAvLyBpbnZhbGlkYXRlcyBjYWNoZWQgaW5mb3JtYXRpb24gYW5kIHRyaWVzIHRvIHJlLWVzdGltYXRlIHRoZVxyXG4gIC8vIGxpbmUncyBoZWlnaHQuXHJcbiAgZnVuY3Rpb24gdXBkYXRlTGluZShsaW5lLCB0ZXh0LCBtYXJrZWRTcGFucywgZXN0aW1hdGVIZWlnaHQpIHtcclxuICAgIGxpbmUudGV4dCA9IHRleHQ7XHJcbiAgICBpZiAobGluZS5zdGF0ZUFmdGVyKSBsaW5lLnN0YXRlQWZ0ZXIgPSBudWxsO1xyXG4gICAgaWYgKGxpbmUuc3R5bGVzKSBsaW5lLnN0eWxlcyA9IG51bGw7XHJcbiAgICBpZiAobGluZS5vcmRlciAhPSBudWxsKSBsaW5lLm9yZGVyID0gbnVsbDtcclxuICAgIGRldGFjaE1hcmtlZFNwYW5zKGxpbmUpO1xyXG4gICAgYXR0YWNoTWFya2VkU3BhbnMobGluZSwgbWFya2VkU3BhbnMpO1xyXG4gICAgdmFyIGVzdEhlaWdodCA9IGVzdGltYXRlSGVpZ2h0ID8gZXN0aW1hdGVIZWlnaHQobGluZSkgOiAxO1xyXG4gICAgaWYgKGVzdEhlaWdodCAhPSBsaW5lLmhlaWdodCkgdXBkYXRlTGluZUhlaWdodChsaW5lLCBlc3RIZWlnaHQpO1xyXG4gIH1cclxuXHJcbiAgLy8gRGV0YWNoIGEgbGluZSBmcm9tIHRoZSBkb2N1bWVudCB0cmVlIGFuZCBpdHMgbWFya2Vycy5cclxuICBmdW5jdGlvbiBjbGVhblVwTGluZShsaW5lKSB7XHJcbiAgICBsaW5lLnBhcmVudCA9IG51bGw7XHJcbiAgICBkZXRhY2hNYXJrZWRTcGFucyhsaW5lKTtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIGV4dHJhY3RMaW5lQ2xhc3Nlcyh0eXBlLCBvdXRwdXQpIHtcclxuICAgIGlmICh0eXBlKSBmb3IgKDs7KSB7XHJcbiAgICAgIHZhciBsaW5lQ2xhc3MgPSB0eXBlLm1hdGNoKC8oPzpefFxccyspbGluZS0oYmFja2dyb3VuZC0pPyhcXFMrKS8pO1xyXG4gICAgICBpZiAoIWxpbmVDbGFzcykgYnJlYWs7XHJcbiAgICAgIHR5cGUgPSB0eXBlLnNsaWNlKDAsIGxpbmVDbGFzcy5pbmRleCkgKyB0eXBlLnNsaWNlKGxpbmVDbGFzcy5pbmRleCArIGxpbmVDbGFzc1swXS5sZW5ndGgpO1xyXG4gICAgICB2YXIgcHJvcCA9IGxpbmVDbGFzc1sxXSA/IFwiYmdDbGFzc1wiIDogXCJ0ZXh0Q2xhc3NcIjtcclxuICAgICAgaWYgKG91dHB1dFtwcm9wXSA9PSBudWxsKVxyXG4gICAgICAgIG91dHB1dFtwcm9wXSA9IGxpbmVDbGFzc1syXTtcclxuICAgICAgZWxzZSBpZiAoIShuZXcgUmVnRXhwKFwiKD86XnxcXHMpXCIgKyBsaW5lQ2xhc3NbMl0gKyBcIig/OiR8XFxzKVwiKSkudGVzdChvdXRwdXRbcHJvcF0pKVxyXG4gICAgICAgIG91dHB1dFtwcm9wXSArPSBcIiBcIiArIGxpbmVDbGFzc1syXTtcclxuICAgIH1cclxuICAgIHJldHVybiB0eXBlO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gY2FsbEJsYW5rTGluZShtb2RlLCBzdGF0ZSkge1xyXG4gICAgaWYgKG1vZGUuYmxhbmtMaW5lKSByZXR1cm4gbW9kZS5ibGFua0xpbmUoc3RhdGUpO1xyXG4gICAgaWYgKCFtb2RlLmlubmVyTW9kZSkgcmV0dXJuO1xyXG4gICAgdmFyIGlubmVyID0gQ29kZU1pcnJvci5pbm5lck1vZGUobW9kZSwgc3RhdGUpO1xyXG4gICAgaWYgKGlubmVyLm1vZGUuYmxhbmtMaW5lKSByZXR1cm4gaW5uZXIubW9kZS5ibGFua0xpbmUoaW5uZXIuc3RhdGUpO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gcmVhZFRva2VuKG1vZGUsIHN0cmVhbSwgc3RhdGUsIGlubmVyKSB7XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IDEwOyBpKyspIHtcclxuICAgICAgaWYgKGlubmVyKSBpbm5lclswXSA9IENvZGVNaXJyb3IuaW5uZXJNb2RlKG1vZGUsIHN0YXRlKS5tb2RlO1xyXG4gICAgICB2YXIgc3R5bGUgPSBtb2RlLnRva2VuKHN0cmVhbSwgc3RhdGUpO1xyXG4gICAgICBpZiAoc3RyZWFtLnBvcyA+IHN0cmVhbS5zdGFydCkgcmV0dXJuIHN0eWxlO1xyXG4gICAgfVxyXG4gICAgdGhyb3cgbmV3IEVycm9yKFwiTW9kZSBcIiArIG1vZGUubmFtZSArIFwiIGZhaWxlZCB0byBhZHZhbmNlIHN0cmVhbS5cIik7XHJcbiAgfVxyXG5cclxuICAvLyBVdGlsaXR5IGZvciBnZXRUb2tlbkF0IGFuZCBnZXRMaW5lVG9rZW5zXHJcbiAgZnVuY3Rpb24gdGFrZVRva2VuKGNtLCBwb3MsIHByZWNpc2UsIGFzQXJyYXkpIHtcclxuICAgIGZ1bmN0aW9uIGdldE9iaihjb3B5KSB7XHJcbiAgICAgIHJldHVybiB7c3RhcnQ6IHN0cmVhbS5zdGFydCwgZW5kOiBzdHJlYW0ucG9zLFxyXG4gICAgICAgICAgICAgIHN0cmluZzogc3RyZWFtLmN1cnJlbnQoKSxcclxuICAgICAgICAgICAgICB0eXBlOiBzdHlsZSB8fCBudWxsLFxyXG4gICAgICAgICAgICAgIHN0YXRlOiBjb3B5ID8gY29weVN0YXRlKGRvYy5tb2RlLCBzdGF0ZSkgOiBzdGF0ZX07XHJcbiAgICB9XHJcblxyXG4gICAgdmFyIGRvYyA9IGNtLmRvYywgbW9kZSA9IGRvYy5tb2RlLCBzdHlsZTtcclxuICAgIHBvcyA9IGNsaXBQb3MoZG9jLCBwb3MpO1xyXG4gICAgdmFyIGxpbmUgPSBnZXRMaW5lKGRvYywgcG9zLmxpbmUpLCBzdGF0ZSA9IGdldFN0YXRlQmVmb3JlKGNtLCBwb3MubGluZSwgcHJlY2lzZSk7XHJcbiAgICB2YXIgc3RyZWFtID0gbmV3IFN0cmluZ1N0cmVhbShsaW5lLnRleHQsIGNtLm9wdGlvbnMudGFiU2l6ZSksIHRva2VucztcclxuICAgIGlmIChhc0FycmF5KSB0b2tlbnMgPSBbXTtcclxuICAgIHdoaWxlICgoYXNBcnJheSB8fCBzdHJlYW0ucG9zIDwgcG9zLmNoKSAmJiAhc3RyZWFtLmVvbCgpKSB7XHJcbiAgICAgIHN0cmVhbS5zdGFydCA9IHN0cmVhbS5wb3M7XHJcbiAgICAgIHN0eWxlID0gcmVhZFRva2VuKG1vZGUsIHN0cmVhbSwgc3RhdGUpO1xyXG4gICAgICBpZiAoYXNBcnJheSkgdG9rZW5zLnB1c2goZ2V0T2JqKHRydWUpKTtcclxuICAgIH1cclxuICAgIHJldHVybiBhc0FycmF5ID8gdG9rZW5zIDogZ2V0T2JqKCk7XHJcbiAgfVxyXG5cclxuICAvLyBSdW4gdGhlIGdpdmVuIG1vZGUncyBwYXJzZXIgb3ZlciBhIGxpbmUsIGNhbGxpbmcgZiBmb3IgZWFjaCB0b2tlbi5cclxuICBmdW5jdGlvbiBydW5Nb2RlKGNtLCB0ZXh0LCBtb2RlLCBzdGF0ZSwgZiwgbGluZUNsYXNzZXMsIGZvcmNlVG9FbmQpIHtcclxuICAgIHZhciBmbGF0dGVuU3BhbnMgPSBtb2RlLmZsYXR0ZW5TcGFucztcclxuICAgIGlmIChmbGF0dGVuU3BhbnMgPT0gbnVsbCkgZmxhdHRlblNwYW5zID0gY20ub3B0aW9ucy5mbGF0dGVuU3BhbnM7XHJcbiAgICB2YXIgY3VyU3RhcnQgPSAwLCBjdXJTdHlsZSA9IG51bGw7XHJcbiAgICB2YXIgc3RyZWFtID0gbmV3IFN0cmluZ1N0cmVhbSh0ZXh0LCBjbS5vcHRpb25zLnRhYlNpemUpLCBzdHlsZTtcclxuICAgIHZhciBpbm5lciA9IGNtLm9wdGlvbnMuYWRkTW9kZUNsYXNzICYmIFtudWxsXTtcclxuICAgIGlmICh0ZXh0ID09IFwiXCIpIGV4dHJhY3RMaW5lQ2xhc3NlcyhjYWxsQmxhbmtMaW5lKG1vZGUsIHN0YXRlKSwgbGluZUNsYXNzZXMpO1xyXG4gICAgd2hpbGUgKCFzdHJlYW0uZW9sKCkpIHtcclxuICAgICAgaWYgKHN0cmVhbS5wb3MgPiBjbS5vcHRpb25zLm1heEhpZ2hsaWdodExlbmd0aCkge1xyXG4gICAgICAgIGZsYXR0ZW5TcGFucyA9IGZhbHNlO1xyXG4gICAgICAgIGlmIChmb3JjZVRvRW5kKSBwcm9jZXNzTGluZShjbSwgdGV4dCwgc3RhdGUsIHN0cmVhbS5wb3MpO1xyXG4gICAgICAgIHN0cmVhbS5wb3MgPSB0ZXh0Lmxlbmd0aDtcclxuICAgICAgICBzdHlsZSA9IG51bGw7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgc3R5bGUgPSBleHRyYWN0TGluZUNsYXNzZXMocmVhZFRva2VuKG1vZGUsIHN0cmVhbSwgc3RhdGUsIGlubmVyKSwgbGluZUNsYXNzZXMpO1xyXG4gICAgICB9XHJcbiAgICAgIGlmIChpbm5lcikge1xyXG4gICAgICAgIHZhciBtTmFtZSA9IGlubmVyWzBdLm5hbWU7XHJcbiAgICAgICAgaWYgKG1OYW1lKSBzdHlsZSA9IFwibS1cIiArIChzdHlsZSA/IG1OYW1lICsgXCIgXCIgKyBzdHlsZSA6IG1OYW1lKTtcclxuICAgICAgfVxyXG4gICAgICBpZiAoIWZsYXR0ZW5TcGFucyB8fCBjdXJTdHlsZSAhPSBzdHlsZSkge1xyXG4gICAgICAgIHdoaWxlIChjdXJTdGFydCA8IHN0cmVhbS5zdGFydCkge1xyXG4gICAgICAgICAgY3VyU3RhcnQgPSBNYXRoLm1pbihzdHJlYW0uc3RhcnQsIGN1clN0YXJ0ICsgNTAwMDApO1xyXG4gICAgICAgICAgZihjdXJTdGFydCwgY3VyU3R5bGUpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBjdXJTdHlsZSA9IHN0eWxlO1xyXG4gICAgICB9XHJcbiAgICAgIHN0cmVhbS5zdGFydCA9IHN0cmVhbS5wb3M7XHJcbiAgICB9XHJcbiAgICB3aGlsZSAoY3VyU3RhcnQgPCBzdHJlYW0ucG9zKSB7XHJcbiAgICAgIC8vIFdlYmtpdCBzZWVtcyB0byByZWZ1c2UgdG8gcmVuZGVyIHRleHQgbm9kZXMgbG9uZ2VyIHRoYW4gNTc0NDQgY2hhcmFjdGVyc1xyXG4gICAgICB2YXIgcG9zID0gTWF0aC5taW4oc3RyZWFtLnBvcywgY3VyU3RhcnQgKyA1MDAwMCk7XHJcbiAgICAgIGYocG9zLCBjdXJTdHlsZSk7XHJcbiAgICAgIGN1clN0YXJ0ID0gcG9zO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gQ29tcHV0ZSBhIHN0eWxlIGFycmF5IChhbiBhcnJheSBzdGFydGluZyB3aXRoIGEgbW9kZSBnZW5lcmF0aW9uXHJcbiAgLy8gLS0gZm9yIGludmFsaWRhdGlvbiAtLSBmb2xsb3dlZCBieSBwYWlycyBvZiBlbmQgcG9zaXRpb25zIGFuZFxyXG4gIC8vIHN0eWxlIHN0cmluZ3MpLCB3aGljaCBpcyB1c2VkIHRvIGhpZ2hsaWdodCB0aGUgdG9rZW5zIG9uIHRoZVxyXG4gIC8vIGxpbmUuXHJcbiAgZnVuY3Rpb24gaGlnaGxpZ2h0TGluZShjbSwgbGluZSwgc3RhdGUsIGZvcmNlVG9FbmQpIHtcclxuICAgIC8vIEEgc3R5bGVzIGFycmF5IGFsd2F5cyBzdGFydHMgd2l0aCBhIG51bWJlciBpZGVudGlmeWluZyB0aGVcclxuICAgIC8vIG1vZGUvb3ZlcmxheXMgdGhhdCBpdCBpcyBiYXNlZCBvbiAoZm9yIGVhc3kgaW52YWxpZGF0aW9uKS5cclxuICAgIHZhciBzdCA9IFtjbS5zdGF0ZS5tb2RlR2VuXSwgbGluZUNsYXNzZXMgPSB7fTtcclxuICAgIC8vIENvbXB1dGUgdGhlIGJhc2UgYXJyYXkgb2Ygc3R5bGVzXHJcbiAgICBydW5Nb2RlKGNtLCBsaW5lLnRleHQsIGNtLmRvYy5tb2RlLCBzdGF0ZSwgZnVuY3Rpb24oZW5kLCBzdHlsZSkge1xyXG4gICAgICBzdC5wdXNoKGVuZCwgc3R5bGUpO1xyXG4gICAgfSwgbGluZUNsYXNzZXMsIGZvcmNlVG9FbmQpO1xyXG5cclxuICAgIC8vIFJ1biBvdmVybGF5cywgYWRqdXN0IHN0eWxlIGFycmF5LlxyXG4gICAgZm9yICh2YXIgbyA9IDA7IG8gPCBjbS5zdGF0ZS5vdmVybGF5cy5sZW5ndGg7ICsrbykge1xyXG4gICAgICB2YXIgb3ZlcmxheSA9IGNtLnN0YXRlLm92ZXJsYXlzW29dLCBpID0gMSwgYXQgPSAwO1xyXG4gICAgICBydW5Nb2RlKGNtLCBsaW5lLnRleHQsIG92ZXJsYXkubW9kZSwgdHJ1ZSwgZnVuY3Rpb24oZW5kLCBzdHlsZSkge1xyXG4gICAgICAgIHZhciBzdGFydCA9IGk7XHJcbiAgICAgICAgLy8gRW5zdXJlIHRoZXJlJ3MgYSB0b2tlbiBlbmQgYXQgdGhlIGN1cnJlbnQgcG9zaXRpb24sIGFuZCB0aGF0IGkgcG9pbnRzIGF0IGl0XHJcbiAgICAgICAgd2hpbGUgKGF0IDwgZW5kKSB7XHJcbiAgICAgICAgICB2YXIgaV9lbmQgPSBzdFtpXTtcclxuICAgICAgICAgIGlmIChpX2VuZCA+IGVuZClcclxuICAgICAgICAgICAgc3Quc3BsaWNlKGksIDEsIGVuZCwgc3RbaSsxXSwgaV9lbmQpO1xyXG4gICAgICAgICAgaSArPSAyO1xyXG4gICAgICAgICAgYXQgPSBNYXRoLm1pbihlbmQsIGlfZW5kKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYgKCFzdHlsZSkgcmV0dXJuO1xyXG4gICAgICAgIGlmIChvdmVybGF5Lm9wYXF1ZSkge1xyXG4gICAgICAgICAgc3Quc3BsaWNlKHN0YXJ0LCBpIC0gc3RhcnQsIGVuZCwgXCJjbS1vdmVybGF5IFwiICsgc3R5bGUpO1xyXG4gICAgICAgICAgaSA9IHN0YXJ0ICsgMjtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgZm9yICg7IHN0YXJ0IDwgaTsgc3RhcnQgKz0gMikge1xyXG4gICAgICAgICAgICB2YXIgY3VyID0gc3Rbc3RhcnQrMV07XHJcbiAgICAgICAgICAgIHN0W3N0YXJ0KzFdID0gKGN1ciA/IGN1ciArIFwiIFwiIDogXCJcIikgKyBcImNtLW92ZXJsYXkgXCIgKyBzdHlsZTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH0sIGxpbmVDbGFzc2VzKTtcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4ge3N0eWxlczogc3QsIGNsYXNzZXM6IGxpbmVDbGFzc2VzLmJnQ2xhc3MgfHwgbGluZUNsYXNzZXMudGV4dENsYXNzID8gbGluZUNsYXNzZXMgOiBudWxsfTtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIGdldExpbmVTdHlsZXMoY20sIGxpbmUsIHVwZGF0ZUZyb250aWVyKSB7XHJcbiAgICBpZiAoIWxpbmUuc3R5bGVzIHx8IGxpbmUuc3R5bGVzWzBdICE9IGNtLnN0YXRlLm1vZGVHZW4pIHtcclxuICAgICAgdmFyIHN0YXRlID0gZ2V0U3RhdGVCZWZvcmUoY20sIGxpbmVObyhsaW5lKSk7XHJcbiAgICAgIHZhciByZXN1bHQgPSBoaWdobGlnaHRMaW5lKGNtLCBsaW5lLCBsaW5lLnRleHQubGVuZ3RoID4gY20ub3B0aW9ucy5tYXhIaWdobGlnaHRMZW5ndGggPyBjb3B5U3RhdGUoY20uZG9jLm1vZGUsIHN0YXRlKSA6IHN0YXRlKTtcclxuICAgICAgbGluZS5zdGF0ZUFmdGVyID0gc3RhdGU7XHJcbiAgICAgIGxpbmUuc3R5bGVzID0gcmVzdWx0LnN0eWxlcztcclxuICAgICAgaWYgKHJlc3VsdC5jbGFzc2VzKSBsaW5lLnN0eWxlQ2xhc3NlcyA9IHJlc3VsdC5jbGFzc2VzO1xyXG4gICAgICBlbHNlIGlmIChsaW5lLnN0eWxlQ2xhc3NlcykgbGluZS5zdHlsZUNsYXNzZXMgPSBudWxsO1xyXG4gICAgICBpZiAodXBkYXRlRnJvbnRpZXIgPT09IGNtLmRvYy5mcm9udGllcikgY20uZG9jLmZyb250aWVyKys7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gbGluZS5zdHlsZXM7XHJcbiAgfVxyXG5cclxuICAvLyBMaWdodHdlaWdodCBmb3JtIG9mIGhpZ2hsaWdodCAtLSBwcm9jZWVkIG92ZXIgdGhpcyBsaW5lIGFuZFxyXG4gIC8vIHVwZGF0ZSBzdGF0ZSwgYnV0IGRvbid0IHNhdmUgYSBzdHlsZSBhcnJheS4gVXNlZCBmb3IgbGluZXMgdGhhdFxyXG4gIC8vIGFyZW4ndCBjdXJyZW50bHkgdmlzaWJsZS5cclxuICBmdW5jdGlvbiBwcm9jZXNzTGluZShjbSwgdGV4dCwgc3RhdGUsIHN0YXJ0QXQpIHtcclxuICAgIHZhciBtb2RlID0gY20uZG9jLm1vZGU7XHJcbiAgICB2YXIgc3RyZWFtID0gbmV3IFN0cmluZ1N0cmVhbSh0ZXh0LCBjbS5vcHRpb25zLnRhYlNpemUpO1xyXG4gICAgc3RyZWFtLnN0YXJ0ID0gc3RyZWFtLnBvcyA9IHN0YXJ0QXQgfHwgMDtcclxuICAgIGlmICh0ZXh0ID09IFwiXCIpIGNhbGxCbGFua0xpbmUobW9kZSwgc3RhdGUpO1xyXG4gICAgd2hpbGUgKCFzdHJlYW0uZW9sKCkpIHtcclxuICAgICAgcmVhZFRva2VuKG1vZGUsIHN0cmVhbSwgc3RhdGUpO1xyXG4gICAgICBzdHJlYW0uc3RhcnQgPSBzdHJlYW0ucG9zO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gQ29udmVydCBhIHN0eWxlIGFzIHJldHVybmVkIGJ5IGEgbW9kZSAoZWl0aGVyIG51bGwsIG9yIGEgc3RyaW5nXHJcbiAgLy8gY29udGFpbmluZyBvbmUgb3IgbW9yZSBzdHlsZXMpIHRvIGEgQ1NTIHN0eWxlLiBUaGlzIGlzIGNhY2hlZCxcclxuICAvLyBhbmQgYWxzbyBsb29rcyBmb3IgbGluZS13aWRlIHN0eWxlcy5cclxuICB2YXIgc3R5bGVUb0NsYXNzQ2FjaGUgPSB7fSwgc3R5bGVUb0NsYXNzQ2FjaGVXaXRoTW9kZSA9IHt9O1xyXG4gIGZ1bmN0aW9uIGludGVycHJldFRva2VuU3R5bGUoc3R5bGUsIG9wdGlvbnMpIHtcclxuICAgIGlmICghc3R5bGUgfHwgL15cXHMqJC8udGVzdChzdHlsZSkpIHJldHVybiBudWxsO1xyXG4gICAgdmFyIGNhY2hlID0gb3B0aW9ucy5hZGRNb2RlQ2xhc3MgPyBzdHlsZVRvQ2xhc3NDYWNoZVdpdGhNb2RlIDogc3R5bGVUb0NsYXNzQ2FjaGU7XHJcbiAgICByZXR1cm4gY2FjaGVbc3R5bGVdIHx8XHJcbiAgICAgIChjYWNoZVtzdHlsZV0gPSBzdHlsZS5yZXBsYWNlKC9cXFMrL2csIFwiY20tJCZcIikpO1xyXG4gIH1cclxuXHJcbiAgLy8gUmVuZGVyIHRoZSBET00gcmVwcmVzZW50YXRpb24gb2YgdGhlIHRleHQgb2YgYSBsaW5lLiBBbHNvIGJ1aWxkc1xyXG4gIC8vIHVwIGEgJ2xpbmUgbWFwJywgd2hpY2ggcG9pbnRzIGF0IHRoZSBET00gbm9kZXMgdGhhdCByZXByZXNlbnRcclxuICAvLyBzcGVjaWZpYyBzdHJldGNoZXMgb2YgdGV4dCwgYW5kIGlzIHVzZWQgYnkgdGhlIG1lYXN1cmluZyBjb2RlLlxyXG4gIC8vIFRoZSByZXR1cm5lZCBvYmplY3QgY29udGFpbnMgdGhlIERPTSBub2RlLCB0aGlzIG1hcCwgYW5kXHJcbiAgLy8gaW5mb3JtYXRpb24gYWJvdXQgbGluZS13aWRlIHN0eWxlcyB0aGF0IHdlcmUgc2V0IGJ5IHRoZSBtb2RlLlxyXG4gIGZ1bmN0aW9uIGJ1aWxkTGluZUNvbnRlbnQoY20sIGxpbmVWaWV3KSB7XHJcbiAgICAvLyBUaGUgcGFkZGluZy1yaWdodCBmb3JjZXMgdGhlIGVsZW1lbnQgdG8gaGF2ZSBhICdib3JkZXInLCB3aGljaFxyXG4gICAgLy8gaXMgbmVlZGVkIG9uIFdlYmtpdCB0byBiZSBhYmxlIHRvIGdldCBsaW5lLWxldmVsIGJvdW5kaW5nXHJcbiAgICAvLyByZWN0YW5nbGVzIGZvciBpdCAoaW4gbWVhc3VyZUNoYXIpLlxyXG4gICAgdmFyIGNvbnRlbnQgPSBlbHQoXCJzcGFuXCIsIG51bGwsIG51bGwsIHdlYmtpdCA/IFwicGFkZGluZy1yaWdodDogLjFweFwiIDogbnVsbCk7XHJcbiAgICB2YXIgYnVpbGRlciA9IHtwcmU6IGVsdChcInByZVwiLCBbY29udGVudF0sIFwiQ29kZU1pcnJvci1saW5lXCIpLCBjb250ZW50OiBjb250ZW50LFxyXG4gICAgICAgICAgICAgICAgICAgY29sOiAwLCBwb3M6IDAsIGNtOiBjbSxcclxuICAgICAgICAgICAgICAgICAgIHNwbGl0U3BhY2VzOiAoaWUgfHwgd2Via2l0KSAmJiBjbS5nZXRPcHRpb24oXCJsaW5lV3JhcHBpbmdcIil9O1xyXG4gICAgbGluZVZpZXcubWVhc3VyZSA9IHt9O1xyXG5cclxuICAgIC8vIEl0ZXJhdGUgb3ZlciB0aGUgbG9naWNhbCBsaW5lcyB0aGF0IG1ha2UgdXAgdGhpcyB2aXN1YWwgbGluZS5cclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDw9IChsaW5lVmlldy5yZXN0ID8gbGluZVZpZXcucmVzdC5sZW5ndGggOiAwKTsgaSsrKSB7XHJcbiAgICAgIHZhciBsaW5lID0gaSA/IGxpbmVWaWV3LnJlc3RbaSAtIDFdIDogbGluZVZpZXcubGluZSwgb3JkZXI7XHJcbiAgICAgIGJ1aWxkZXIucG9zID0gMDtcclxuICAgICAgYnVpbGRlci5hZGRUb2tlbiA9IGJ1aWxkVG9rZW47XHJcbiAgICAgIC8vIE9wdGlvbmFsbHkgd2lyZSBpbiBzb21lIGhhY2tzIGludG8gdGhlIHRva2VuLXJlbmRlcmluZ1xyXG4gICAgICAvLyBhbGdvcml0aG0sIHRvIGRlYWwgd2l0aCBicm93c2VyIHF1aXJrcy5cclxuICAgICAgaWYgKGhhc0JhZEJpZGlSZWN0cyhjbS5kaXNwbGF5Lm1lYXN1cmUpICYmIChvcmRlciA9IGdldE9yZGVyKGxpbmUpKSlcclxuICAgICAgICBidWlsZGVyLmFkZFRva2VuID0gYnVpbGRUb2tlbkJhZEJpZGkoYnVpbGRlci5hZGRUb2tlbiwgb3JkZXIpO1xyXG4gICAgICBidWlsZGVyLm1hcCA9IFtdO1xyXG4gICAgICB2YXIgYWxsb3dGcm9udGllclVwZGF0ZSA9IGxpbmVWaWV3ICE9IGNtLmRpc3BsYXkuZXh0ZXJuYWxNZWFzdXJlZCAmJiBsaW5lTm8obGluZSk7XHJcbiAgICAgIGluc2VydExpbmVDb250ZW50KGxpbmUsIGJ1aWxkZXIsIGdldExpbmVTdHlsZXMoY20sIGxpbmUsIGFsbG93RnJvbnRpZXJVcGRhdGUpKTtcclxuICAgICAgaWYgKGxpbmUuc3R5bGVDbGFzc2VzKSB7XHJcbiAgICAgICAgaWYgKGxpbmUuc3R5bGVDbGFzc2VzLmJnQ2xhc3MpXHJcbiAgICAgICAgICBidWlsZGVyLmJnQ2xhc3MgPSBqb2luQ2xhc3NlcyhsaW5lLnN0eWxlQ2xhc3Nlcy5iZ0NsYXNzLCBidWlsZGVyLmJnQ2xhc3MgfHwgXCJcIik7XHJcbiAgICAgICAgaWYgKGxpbmUuc3R5bGVDbGFzc2VzLnRleHRDbGFzcylcclxuICAgICAgICAgIGJ1aWxkZXIudGV4dENsYXNzID0gam9pbkNsYXNzZXMobGluZS5zdHlsZUNsYXNzZXMudGV4dENsYXNzLCBidWlsZGVyLnRleHRDbGFzcyB8fCBcIlwiKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gRW5zdXJlIGF0IGxlYXN0IGEgc2luZ2xlIG5vZGUgaXMgcHJlc2VudCwgZm9yIG1lYXN1cmluZy5cclxuICAgICAgaWYgKGJ1aWxkZXIubWFwLmxlbmd0aCA9PSAwKVxyXG4gICAgICAgIGJ1aWxkZXIubWFwLnB1c2goMCwgMCwgYnVpbGRlci5jb250ZW50LmFwcGVuZENoaWxkKHplcm9XaWR0aEVsZW1lbnQoY20uZGlzcGxheS5tZWFzdXJlKSkpO1xyXG5cclxuICAgICAgLy8gU3RvcmUgdGhlIG1hcCBhbmQgYSBjYWNoZSBvYmplY3QgZm9yIHRoZSBjdXJyZW50IGxvZ2ljYWwgbGluZVxyXG4gICAgICBpZiAoaSA9PSAwKSB7XHJcbiAgICAgICAgbGluZVZpZXcubWVhc3VyZS5tYXAgPSBidWlsZGVyLm1hcDtcclxuICAgICAgICBsaW5lVmlldy5tZWFzdXJlLmNhY2hlID0ge307XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgKGxpbmVWaWV3Lm1lYXN1cmUubWFwcyB8fCAobGluZVZpZXcubWVhc3VyZS5tYXBzID0gW10pKS5wdXNoKGJ1aWxkZXIubWFwKTtcclxuICAgICAgICAobGluZVZpZXcubWVhc3VyZS5jYWNoZXMgfHwgKGxpbmVWaWV3Lm1lYXN1cmUuY2FjaGVzID0gW10pKS5wdXNoKHt9KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFNlZSBpc3N1ZSAjMjkwMVxyXG4gICAgaWYgKHdlYmtpdCAmJiAvXFxiY20tdGFiXFxiLy50ZXN0KGJ1aWxkZXIuY29udGVudC5sYXN0Q2hpbGQuY2xhc3NOYW1lKSlcclxuICAgICAgYnVpbGRlci5jb250ZW50LmNsYXNzTmFtZSA9IFwiY20tdGFiLXdyYXAtaGFja1wiO1xyXG5cclxuICAgIHNpZ25hbChjbSwgXCJyZW5kZXJMaW5lXCIsIGNtLCBsaW5lVmlldy5saW5lLCBidWlsZGVyLnByZSk7XHJcbiAgICBpZiAoYnVpbGRlci5wcmUuY2xhc3NOYW1lKVxyXG4gICAgICBidWlsZGVyLnRleHRDbGFzcyA9IGpvaW5DbGFzc2VzKGJ1aWxkZXIucHJlLmNsYXNzTmFtZSwgYnVpbGRlci50ZXh0Q2xhc3MgfHwgXCJcIik7XHJcblxyXG4gICAgcmV0dXJuIGJ1aWxkZXI7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBkZWZhdWx0U3BlY2lhbENoYXJQbGFjZWhvbGRlcihjaCkge1xyXG4gICAgdmFyIHRva2VuID0gZWx0KFwic3BhblwiLCBcIlxcdTIwMjJcIiwgXCJjbS1pbnZhbGlkY2hhclwiKTtcclxuICAgIHRva2VuLnRpdGxlID0gXCJcXFxcdVwiICsgY2guY2hhckNvZGVBdCgwKS50b1N0cmluZygxNik7XHJcbiAgICB0b2tlbi5zZXRBdHRyaWJ1dGUoXCJhcmlhLWxhYmVsXCIsIHRva2VuLnRpdGxlKTtcclxuICAgIHJldHVybiB0b2tlbjtcclxuICB9XHJcblxyXG4gIC8vIEJ1aWxkIHVwIHRoZSBET00gcmVwcmVzZW50YXRpb24gZm9yIGEgc2luZ2xlIHRva2VuLCBhbmQgYWRkIGl0IHRvXHJcbiAgLy8gdGhlIGxpbmUgbWFwLiBUYWtlcyBjYXJlIHRvIHJlbmRlciBzcGVjaWFsIGNoYXJhY3RlcnMgc2VwYXJhdGVseS5cclxuICBmdW5jdGlvbiBidWlsZFRva2VuKGJ1aWxkZXIsIHRleHQsIHN0eWxlLCBzdGFydFN0eWxlLCBlbmRTdHlsZSwgdGl0bGUsIGNzcykge1xyXG4gICAgaWYgKCF0ZXh0KSByZXR1cm47XHJcbiAgICB2YXIgZGlzcGxheVRleHQgPSBidWlsZGVyLnNwbGl0U3BhY2VzID8gdGV4dC5yZXBsYWNlKC8gezMsfS9nLCBzcGxpdFNwYWNlcykgOiB0ZXh0O1xyXG4gICAgdmFyIHNwZWNpYWwgPSBidWlsZGVyLmNtLnN0YXRlLnNwZWNpYWxDaGFycywgbXVzdFdyYXAgPSBmYWxzZTtcclxuICAgIGlmICghc3BlY2lhbC50ZXN0KHRleHQpKSB7XHJcbiAgICAgIGJ1aWxkZXIuY29sICs9IHRleHQubGVuZ3RoO1xyXG4gICAgICB2YXIgY29udGVudCA9IGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKGRpc3BsYXlUZXh0KTtcclxuICAgICAgYnVpbGRlci5tYXAucHVzaChidWlsZGVyLnBvcywgYnVpbGRlci5wb3MgKyB0ZXh0Lmxlbmd0aCwgY29udGVudCk7XHJcbiAgICAgIGlmIChpZSAmJiBpZV92ZXJzaW9uIDwgOSkgbXVzdFdyYXAgPSB0cnVlO1xyXG4gICAgICBidWlsZGVyLnBvcyArPSB0ZXh0Lmxlbmd0aDtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIHZhciBjb250ZW50ID0gZG9jdW1lbnQuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpLCBwb3MgPSAwO1xyXG4gICAgICB3aGlsZSAodHJ1ZSkge1xyXG4gICAgICAgIHNwZWNpYWwubGFzdEluZGV4ID0gcG9zO1xyXG4gICAgICAgIHZhciBtID0gc3BlY2lhbC5leGVjKHRleHQpO1xyXG4gICAgICAgIHZhciBza2lwcGVkID0gbSA/IG0uaW5kZXggLSBwb3MgOiB0ZXh0Lmxlbmd0aCAtIHBvcztcclxuICAgICAgICBpZiAoc2tpcHBlZCkge1xyXG4gICAgICAgICAgdmFyIHR4dCA9IGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKGRpc3BsYXlUZXh0LnNsaWNlKHBvcywgcG9zICsgc2tpcHBlZCkpO1xyXG4gICAgICAgICAgaWYgKGllICYmIGllX3ZlcnNpb24gPCA5KSBjb250ZW50LmFwcGVuZENoaWxkKGVsdChcInNwYW5cIiwgW3R4dF0pKTtcclxuICAgICAgICAgIGVsc2UgY29udGVudC5hcHBlbmRDaGlsZCh0eHQpO1xyXG4gICAgICAgICAgYnVpbGRlci5tYXAucHVzaChidWlsZGVyLnBvcywgYnVpbGRlci5wb3MgKyBza2lwcGVkLCB0eHQpO1xyXG4gICAgICAgICAgYnVpbGRlci5jb2wgKz0gc2tpcHBlZDtcclxuICAgICAgICAgIGJ1aWxkZXIucG9zICs9IHNraXBwZWQ7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmICghbSkgYnJlYWs7XHJcbiAgICAgICAgcG9zICs9IHNraXBwZWQgKyAxO1xyXG4gICAgICAgIGlmIChtWzBdID09IFwiXFx0XCIpIHtcclxuICAgICAgICAgIHZhciB0YWJTaXplID0gYnVpbGRlci5jbS5vcHRpb25zLnRhYlNpemUsIHRhYldpZHRoID0gdGFiU2l6ZSAtIGJ1aWxkZXIuY29sICUgdGFiU2l6ZTtcclxuICAgICAgICAgIHZhciB0eHQgPSBjb250ZW50LmFwcGVuZENoaWxkKGVsdChcInNwYW5cIiwgc3BhY2VTdHIodGFiV2lkdGgpLCBcImNtLXRhYlwiKSk7XHJcbiAgICAgICAgICB0eHQuc2V0QXR0cmlidXRlKFwicm9sZVwiLCBcInByZXNlbnRhdGlvblwiKTtcclxuICAgICAgICAgIHR4dC5zZXRBdHRyaWJ1dGUoXCJjbS10ZXh0XCIsIFwiXFx0XCIpO1xyXG4gICAgICAgICAgYnVpbGRlci5jb2wgKz0gdGFiV2lkdGg7XHJcbiAgICAgICAgfSBlbHNlIGlmIChtWzBdID09IFwiXFxyXCIgfHwgbVswXSA9PSBcIlxcblwiKSB7XHJcbiAgICAgICAgICB2YXIgdHh0ID0gY29udGVudC5hcHBlbmRDaGlsZChlbHQoXCJzcGFuXCIsIG1bMF0gPT0gXCJcXHJcIiA/IFwiXFx1MjQwZFwiIDogXCJcXHUyNDI0XCIsIFwiY20taW52YWxpZGNoYXJcIikpO1xyXG4gICAgICAgICAgdHh0LnNldEF0dHJpYnV0ZShcImNtLXRleHRcIiwgbVswXSk7XHJcbiAgICAgICAgICBidWlsZGVyLmNvbCArPSAxO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICB2YXIgdHh0ID0gYnVpbGRlci5jbS5vcHRpb25zLnNwZWNpYWxDaGFyUGxhY2Vob2xkZXIobVswXSk7XHJcbiAgICAgICAgICB0eHQuc2V0QXR0cmlidXRlKFwiY20tdGV4dFwiLCBtWzBdKTtcclxuICAgICAgICAgIGlmIChpZSAmJiBpZV92ZXJzaW9uIDwgOSkgY29udGVudC5hcHBlbmRDaGlsZChlbHQoXCJzcGFuXCIsIFt0eHRdKSk7XHJcbiAgICAgICAgICBlbHNlIGNvbnRlbnQuYXBwZW5kQ2hpbGQodHh0KTtcclxuICAgICAgICAgIGJ1aWxkZXIuY29sICs9IDE7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGJ1aWxkZXIubWFwLnB1c2goYnVpbGRlci5wb3MsIGJ1aWxkZXIucG9zICsgMSwgdHh0KTtcclxuICAgICAgICBidWlsZGVyLnBvcysrO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICBpZiAoc3R5bGUgfHwgc3RhcnRTdHlsZSB8fCBlbmRTdHlsZSB8fCBtdXN0V3JhcCB8fCBjc3MpIHtcclxuICAgICAgdmFyIGZ1bGxTdHlsZSA9IHN0eWxlIHx8IFwiXCI7XHJcbiAgICAgIGlmIChzdGFydFN0eWxlKSBmdWxsU3R5bGUgKz0gc3RhcnRTdHlsZTtcclxuICAgICAgaWYgKGVuZFN0eWxlKSBmdWxsU3R5bGUgKz0gZW5kU3R5bGU7XHJcbiAgICAgIHZhciB0b2tlbiA9IGVsdChcInNwYW5cIiwgW2NvbnRlbnRdLCBmdWxsU3R5bGUsIGNzcyk7XHJcbiAgICAgIGlmICh0aXRsZSkgdG9rZW4udGl0bGUgPSB0aXRsZTtcclxuICAgICAgcmV0dXJuIGJ1aWxkZXIuY29udGVudC5hcHBlbmRDaGlsZCh0b2tlbik7XHJcbiAgICB9XHJcbiAgICBidWlsZGVyLmNvbnRlbnQuYXBwZW5kQ2hpbGQoY29udGVudCk7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBzcGxpdFNwYWNlcyhvbGQpIHtcclxuICAgIHZhciBvdXQgPSBcIiBcIjtcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgb2xkLmxlbmd0aCAtIDI7ICsraSkgb3V0ICs9IGkgJSAyID8gXCIgXCIgOiBcIlxcdTAwYTBcIjtcclxuICAgIG91dCArPSBcIiBcIjtcclxuICAgIHJldHVybiBvdXQ7XHJcbiAgfVxyXG5cclxuICAvLyBXb3JrIGFyb3VuZCBub25zZW5zZSBkaW1lbnNpb25zIGJlaW5nIHJlcG9ydGVkIGZvciBzdHJldGNoZXMgb2ZcclxuICAvLyByaWdodC10by1sZWZ0IHRleHQuXHJcbiAgZnVuY3Rpb24gYnVpbGRUb2tlbkJhZEJpZGkoaW5uZXIsIG9yZGVyKSB7XHJcbiAgICByZXR1cm4gZnVuY3Rpb24oYnVpbGRlciwgdGV4dCwgc3R5bGUsIHN0YXJ0U3R5bGUsIGVuZFN0eWxlLCB0aXRsZSwgY3NzKSB7XHJcbiAgICAgIHN0eWxlID0gc3R5bGUgPyBzdHlsZSArIFwiIGNtLWZvcmNlLWJvcmRlclwiIDogXCJjbS1mb3JjZS1ib3JkZXJcIjtcclxuICAgICAgdmFyIHN0YXJ0ID0gYnVpbGRlci5wb3MsIGVuZCA9IHN0YXJ0ICsgdGV4dC5sZW5ndGg7XHJcbiAgICAgIGZvciAoOzspIHtcclxuICAgICAgICAvLyBGaW5kIHRoZSBwYXJ0IHRoYXQgb3ZlcmxhcHMgd2l0aCB0aGUgc3RhcnQgb2YgdGhpcyB0ZXh0XHJcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBvcmRlci5sZW5ndGg7IGkrKykge1xyXG4gICAgICAgICAgdmFyIHBhcnQgPSBvcmRlcltpXTtcclxuICAgICAgICAgIGlmIChwYXJ0LnRvID4gc3RhcnQgJiYgcGFydC5mcm9tIDw9IHN0YXJ0KSBicmVhaztcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYgKHBhcnQudG8gPj0gZW5kKSByZXR1cm4gaW5uZXIoYnVpbGRlciwgdGV4dCwgc3R5bGUsIHN0YXJ0U3R5bGUsIGVuZFN0eWxlLCB0aXRsZSwgY3NzKTtcclxuICAgICAgICBpbm5lcihidWlsZGVyLCB0ZXh0LnNsaWNlKDAsIHBhcnQudG8gLSBzdGFydCksIHN0eWxlLCBzdGFydFN0eWxlLCBudWxsLCB0aXRsZSwgY3NzKTtcclxuICAgICAgICBzdGFydFN0eWxlID0gbnVsbDtcclxuICAgICAgICB0ZXh0ID0gdGV4dC5zbGljZShwYXJ0LnRvIC0gc3RhcnQpO1xyXG4gICAgICAgIHN0YXJ0ID0gcGFydC50bztcclxuICAgICAgfVxyXG4gICAgfTtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIGJ1aWxkQ29sbGFwc2VkU3BhbihidWlsZGVyLCBzaXplLCBtYXJrZXIsIGlnbm9yZVdpZGdldCkge1xyXG4gICAgdmFyIHdpZGdldCA9ICFpZ25vcmVXaWRnZXQgJiYgbWFya2VyLndpZGdldE5vZGU7XHJcbiAgICBpZiAod2lkZ2V0KSBidWlsZGVyLm1hcC5wdXNoKGJ1aWxkZXIucG9zLCBidWlsZGVyLnBvcyArIHNpemUsIHdpZGdldCk7XHJcbiAgICBpZiAoIWlnbm9yZVdpZGdldCAmJiBidWlsZGVyLmNtLmRpc3BsYXkuaW5wdXQubmVlZHNDb250ZW50QXR0cmlidXRlKSB7XHJcbiAgICAgIGlmICghd2lkZ2V0KVxyXG4gICAgICAgIHdpZGdldCA9IGJ1aWxkZXIuY29udGVudC5hcHBlbmRDaGlsZChkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwic3BhblwiKSk7XHJcbiAgICAgIHdpZGdldC5zZXRBdHRyaWJ1dGUoXCJjbS1tYXJrZXJcIiwgbWFya2VyLmlkKTtcclxuICAgIH1cclxuICAgIGlmICh3aWRnZXQpIHtcclxuICAgICAgYnVpbGRlci5jbS5kaXNwbGF5LmlucHV0LnNldFVuZWRpdGFibGUod2lkZ2V0KTtcclxuICAgICAgYnVpbGRlci5jb250ZW50LmFwcGVuZENoaWxkKHdpZGdldCk7XHJcbiAgICB9XHJcbiAgICBidWlsZGVyLnBvcyArPSBzaXplO1xyXG4gIH1cclxuXHJcbiAgLy8gT3V0cHV0cyBhIG51bWJlciBvZiBzcGFucyB0byBtYWtlIHVwIGEgbGluZSwgdGFraW5nIGhpZ2hsaWdodGluZ1xyXG4gIC8vIGFuZCBtYXJrZWQgdGV4dCBpbnRvIGFjY291bnQuXHJcbiAgZnVuY3Rpb24gaW5zZXJ0TGluZUNvbnRlbnQobGluZSwgYnVpbGRlciwgc3R5bGVzKSB7XHJcbiAgICB2YXIgc3BhbnMgPSBsaW5lLm1hcmtlZFNwYW5zLCBhbGxUZXh0ID0gbGluZS50ZXh0LCBhdCA9IDA7XHJcbiAgICBpZiAoIXNwYW5zKSB7XHJcbiAgICAgIGZvciAodmFyIGkgPSAxOyBpIDwgc3R5bGVzLmxlbmd0aDsgaSs9MilcclxuICAgICAgICBidWlsZGVyLmFkZFRva2VuKGJ1aWxkZXIsIGFsbFRleHQuc2xpY2UoYXQsIGF0ID0gc3R5bGVzW2ldKSwgaW50ZXJwcmV0VG9rZW5TdHlsZShzdHlsZXNbaSsxXSwgYnVpbGRlci5jbS5vcHRpb25zKSk7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuXHJcbiAgICB2YXIgbGVuID0gYWxsVGV4dC5sZW5ndGgsIHBvcyA9IDAsIGkgPSAxLCB0ZXh0ID0gXCJcIiwgc3R5bGUsIGNzcztcclxuICAgIHZhciBuZXh0Q2hhbmdlID0gMCwgc3BhblN0eWxlLCBzcGFuRW5kU3R5bGUsIHNwYW5TdGFydFN0eWxlLCB0aXRsZSwgY29sbGFwc2VkO1xyXG4gICAgZm9yICg7Oykge1xyXG4gICAgICBpZiAobmV4dENoYW5nZSA9PSBwb3MpIHsgLy8gVXBkYXRlIGN1cnJlbnQgbWFya2VyIHNldFxyXG4gICAgICAgIHNwYW5TdHlsZSA9IHNwYW5FbmRTdHlsZSA9IHNwYW5TdGFydFN0eWxlID0gdGl0bGUgPSBjc3MgPSBcIlwiO1xyXG4gICAgICAgIGNvbGxhcHNlZCA9IG51bGw7IG5leHRDaGFuZ2UgPSBJbmZpbml0eTtcclxuICAgICAgICB2YXIgZm91bmRCb29rbWFya3MgPSBbXTtcclxuICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IHNwYW5zLmxlbmd0aDsgKytqKSB7XHJcbiAgICAgICAgICB2YXIgc3AgPSBzcGFuc1tqXSwgbSA9IHNwLm1hcmtlcjtcclxuICAgICAgICAgIGlmIChtLnR5cGUgPT0gXCJib29rbWFya1wiICYmIHNwLmZyb20gPT0gcG9zICYmIG0ud2lkZ2V0Tm9kZSkge1xyXG4gICAgICAgICAgICBmb3VuZEJvb2ttYXJrcy5wdXNoKG0pO1xyXG4gICAgICAgICAgfSBlbHNlIGlmIChzcC5mcm9tIDw9IHBvcyAmJiAoc3AudG8gPT0gbnVsbCB8fCBzcC50byA+IHBvcyB8fCBtLmNvbGxhcHNlZCAmJiBzcC50byA9PSBwb3MgJiYgc3AuZnJvbSA9PSBwb3MpKSB7XHJcbiAgICAgICAgICAgIGlmIChzcC50byAhPSBudWxsICYmIHNwLnRvICE9IHBvcyAmJiBuZXh0Q2hhbmdlID4gc3AudG8pIHtcclxuICAgICAgICAgICAgICBuZXh0Q2hhbmdlID0gc3AudG87XHJcbiAgICAgICAgICAgICAgc3BhbkVuZFN0eWxlID0gXCJcIjtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBpZiAobS5jbGFzc05hbWUpIHNwYW5TdHlsZSArPSBcIiBcIiArIG0uY2xhc3NOYW1lO1xyXG4gICAgICAgICAgICBpZiAobS5jc3MpIGNzcyA9IG0uY3NzO1xyXG4gICAgICAgICAgICBpZiAobS5zdGFydFN0eWxlICYmIHNwLmZyb20gPT0gcG9zKSBzcGFuU3RhcnRTdHlsZSArPSBcIiBcIiArIG0uc3RhcnRTdHlsZTtcclxuICAgICAgICAgICAgaWYgKG0uZW5kU3R5bGUgJiYgc3AudG8gPT0gbmV4dENoYW5nZSkgc3BhbkVuZFN0eWxlICs9IFwiIFwiICsgbS5lbmRTdHlsZTtcclxuICAgICAgICAgICAgaWYgKG0udGl0bGUgJiYgIXRpdGxlKSB0aXRsZSA9IG0udGl0bGU7XHJcbiAgICAgICAgICAgIGlmIChtLmNvbGxhcHNlZCAmJiAoIWNvbGxhcHNlZCB8fCBjb21wYXJlQ29sbGFwc2VkTWFya2Vycyhjb2xsYXBzZWQubWFya2VyLCBtKSA8IDApKVxyXG4gICAgICAgICAgICAgIGNvbGxhcHNlZCA9IHNwO1xyXG4gICAgICAgICAgfSBlbHNlIGlmIChzcC5mcm9tID4gcG9zICYmIG5leHRDaGFuZ2UgPiBzcC5mcm9tKSB7XHJcbiAgICAgICAgICAgIG5leHRDaGFuZ2UgPSBzcC5mcm9tO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAoY29sbGFwc2VkICYmIChjb2xsYXBzZWQuZnJvbSB8fCAwKSA9PSBwb3MpIHtcclxuICAgICAgICAgIGJ1aWxkQ29sbGFwc2VkU3BhbihidWlsZGVyLCAoY29sbGFwc2VkLnRvID09IG51bGwgPyBsZW4gKyAxIDogY29sbGFwc2VkLnRvKSAtIHBvcyxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZWQubWFya2VyLCBjb2xsYXBzZWQuZnJvbSA9PSBudWxsKTtcclxuICAgICAgICAgIGlmIChjb2xsYXBzZWQudG8gPT0gbnVsbCkgcmV0dXJuO1xyXG4gICAgICAgICAgaWYgKGNvbGxhcHNlZC50byA9PSBwb3MpIGNvbGxhcHNlZCA9IGZhbHNlO1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAoIWNvbGxhcHNlZCAmJiBmb3VuZEJvb2ttYXJrcy5sZW5ndGgpIGZvciAodmFyIGogPSAwOyBqIDwgZm91bmRCb29rbWFya3MubGVuZ3RoOyArK2opXHJcbiAgICAgICAgICBidWlsZENvbGxhcHNlZFNwYW4oYnVpbGRlciwgMCwgZm91bmRCb29rbWFya3Nbal0pO1xyXG4gICAgICB9XHJcbiAgICAgIGlmIChwb3MgPj0gbGVuKSBicmVhaztcclxuXHJcbiAgICAgIHZhciB1cHRvID0gTWF0aC5taW4obGVuLCBuZXh0Q2hhbmdlKTtcclxuICAgICAgd2hpbGUgKHRydWUpIHtcclxuICAgICAgICBpZiAodGV4dCkge1xyXG4gICAgICAgICAgdmFyIGVuZCA9IHBvcyArIHRleHQubGVuZ3RoO1xyXG4gICAgICAgICAgaWYgKCFjb2xsYXBzZWQpIHtcclxuICAgICAgICAgICAgdmFyIHRva2VuVGV4dCA9IGVuZCA+IHVwdG8gPyB0ZXh0LnNsaWNlKDAsIHVwdG8gLSBwb3MpIDogdGV4dDtcclxuICAgICAgICAgICAgYnVpbGRlci5hZGRUb2tlbihidWlsZGVyLCB0b2tlblRleHQsIHN0eWxlID8gc3R5bGUgKyBzcGFuU3R5bGUgOiBzcGFuU3R5bGUsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BhblN0YXJ0U3R5bGUsIHBvcyArIHRva2VuVGV4dC5sZW5ndGggPT0gbmV4dENoYW5nZSA/IHNwYW5FbmRTdHlsZSA6IFwiXCIsIHRpdGxlLCBjc3MpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgaWYgKGVuZCA+PSB1cHRvKSB7dGV4dCA9IHRleHQuc2xpY2UodXB0byAtIHBvcyk7IHBvcyA9IHVwdG87IGJyZWFrO31cclxuICAgICAgICAgIHBvcyA9IGVuZDtcclxuICAgICAgICAgIHNwYW5TdGFydFN0eWxlID0gXCJcIjtcclxuICAgICAgICB9XHJcbiAgICAgICAgdGV4dCA9IGFsbFRleHQuc2xpY2UoYXQsIGF0ID0gc3R5bGVzW2krK10pO1xyXG4gICAgICAgIHN0eWxlID0gaW50ZXJwcmV0VG9rZW5TdHlsZShzdHlsZXNbaSsrXSwgYnVpbGRlci5jbS5vcHRpb25zKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gRE9DVU1FTlQgREFUQSBTVFJVQ1RVUkVcclxuXHJcbiAgLy8gQnkgZGVmYXVsdCwgdXBkYXRlcyB0aGF0IHN0YXJ0IGFuZCBlbmQgYXQgdGhlIGJlZ2lubmluZyBvZiBhIGxpbmVcclxuICAvLyBhcmUgdHJlYXRlZCBzcGVjaWFsbHksIGluIG9yZGVyIHRvIG1ha2UgdGhlIGFzc29jaWF0aW9uIG9mIGxpbmVcclxuICAvLyB3aWRnZXRzIGFuZCBtYXJrZXIgZWxlbWVudHMgd2l0aCB0aGUgdGV4dCBiZWhhdmUgbW9yZSBpbnR1aXRpdmUuXHJcbiAgZnVuY3Rpb24gaXNXaG9sZUxpbmVVcGRhdGUoZG9jLCBjaGFuZ2UpIHtcclxuICAgIHJldHVybiBjaGFuZ2UuZnJvbS5jaCA9PSAwICYmIGNoYW5nZS50by5jaCA9PSAwICYmIGxzdChjaGFuZ2UudGV4dCkgPT0gXCJcIiAmJlxyXG4gICAgICAoIWRvYy5jbSB8fCBkb2MuY20ub3B0aW9ucy53aG9sZUxpbmVVcGRhdGVCZWZvcmUpO1xyXG4gIH1cclxuXHJcbiAgLy8gUGVyZm9ybSBhIGNoYW5nZSBvbiB0aGUgZG9jdW1lbnQgZGF0YSBzdHJ1Y3R1cmUuXHJcbiAgZnVuY3Rpb24gdXBkYXRlRG9jKGRvYywgY2hhbmdlLCBtYXJrZWRTcGFucywgZXN0aW1hdGVIZWlnaHQpIHtcclxuICAgIGZ1bmN0aW9uIHNwYW5zRm9yKG4pIHtyZXR1cm4gbWFya2VkU3BhbnMgPyBtYXJrZWRTcGFuc1tuXSA6IG51bGw7fVxyXG4gICAgZnVuY3Rpb24gdXBkYXRlKGxpbmUsIHRleHQsIHNwYW5zKSB7XHJcbiAgICAgIHVwZGF0ZUxpbmUobGluZSwgdGV4dCwgc3BhbnMsIGVzdGltYXRlSGVpZ2h0KTtcclxuICAgICAgc2lnbmFsTGF0ZXIobGluZSwgXCJjaGFuZ2VcIiwgbGluZSwgY2hhbmdlKTtcclxuICAgIH1cclxuICAgIGZ1bmN0aW9uIGxpbmVzRm9yKHN0YXJ0LCBlbmQpIHtcclxuICAgICAgZm9yICh2YXIgaSA9IHN0YXJ0LCByZXN1bHQgPSBbXTsgaSA8IGVuZDsgKytpKVxyXG4gICAgICAgIHJlc3VsdC5wdXNoKG5ldyBMaW5lKHRleHRbaV0sIHNwYW5zRm9yKGkpLCBlc3RpbWF0ZUhlaWdodCkpO1xyXG4gICAgICByZXR1cm4gcmVzdWx0O1xyXG4gICAgfVxyXG5cclxuICAgIHZhciBmcm9tID0gY2hhbmdlLmZyb20sIHRvID0gY2hhbmdlLnRvLCB0ZXh0ID0gY2hhbmdlLnRleHQ7XHJcbiAgICB2YXIgZmlyc3RMaW5lID0gZ2V0TGluZShkb2MsIGZyb20ubGluZSksIGxhc3RMaW5lID0gZ2V0TGluZShkb2MsIHRvLmxpbmUpO1xyXG4gICAgdmFyIGxhc3RUZXh0ID0gbHN0KHRleHQpLCBsYXN0U3BhbnMgPSBzcGFuc0Zvcih0ZXh0Lmxlbmd0aCAtIDEpLCBubGluZXMgPSB0by5saW5lIC0gZnJvbS5saW5lO1xyXG5cclxuICAgIC8vIEFkanVzdCB0aGUgbGluZSBzdHJ1Y3R1cmVcclxuICAgIGlmIChjaGFuZ2UuZnVsbCkge1xyXG4gICAgICBkb2MuaW5zZXJ0KDAsIGxpbmVzRm9yKDAsIHRleHQubGVuZ3RoKSk7XHJcbiAgICAgIGRvYy5yZW1vdmUodGV4dC5sZW5ndGgsIGRvYy5zaXplIC0gdGV4dC5sZW5ndGgpO1xyXG4gICAgfSBlbHNlIGlmIChpc1dob2xlTGluZVVwZGF0ZShkb2MsIGNoYW5nZSkpIHtcclxuICAgICAgLy8gVGhpcyBpcyBhIHdob2xlLWxpbmUgcmVwbGFjZS4gVHJlYXRlZCBzcGVjaWFsbHkgdG8gbWFrZVxyXG4gICAgICAvLyBzdXJlIGxpbmUgb2JqZWN0cyBtb3ZlIHRoZSB3YXkgdGhleSBhcmUgc3VwcG9zZWQgdG8uXHJcbiAgICAgIHZhciBhZGRlZCA9IGxpbmVzRm9yKDAsIHRleHQubGVuZ3RoIC0gMSk7XHJcbiAgICAgIHVwZGF0ZShsYXN0TGluZSwgbGFzdExpbmUudGV4dCwgbGFzdFNwYW5zKTtcclxuICAgICAgaWYgKG5saW5lcykgZG9jLnJlbW92ZShmcm9tLmxpbmUsIG5saW5lcyk7XHJcbiAgICAgIGlmIChhZGRlZC5sZW5ndGgpIGRvYy5pbnNlcnQoZnJvbS5saW5lLCBhZGRlZCk7XHJcbiAgICB9IGVsc2UgaWYgKGZpcnN0TGluZSA9PSBsYXN0TGluZSkge1xyXG4gICAgICBpZiAodGV4dC5sZW5ndGggPT0gMSkge1xyXG4gICAgICAgIHVwZGF0ZShmaXJzdExpbmUsIGZpcnN0TGluZS50ZXh0LnNsaWNlKDAsIGZyb20uY2gpICsgbGFzdFRleHQgKyBmaXJzdExpbmUudGV4dC5zbGljZSh0by5jaCksIGxhc3RTcGFucyk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgdmFyIGFkZGVkID0gbGluZXNGb3IoMSwgdGV4dC5sZW5ndGggLSAxKTtcclxuICAgICAgICBhZGRlZC5wdXNoKG5ldyBMaW5lKGxhc3RUZXh0ICsgZmlyc3RMaW5lLnRleHQuc2xpY2UodG8uY2gpLCBsYXN0U3BhbnMsIGVzdGltYXRlSGVpZ2h0KSk7XHJcbiAgICAgICAgdXBkYXRlKGZpcnN0TGluZSwgZmlyc3RMaW5lLnRleHQuc2xpY2UoMCwgZnJvbS5jaCkgKyB0ZXh0WzBdLCBzcGFuc0ZvcigwKSk7XHJcbiAgICAgICAgZG9jLmluc2VydChmcm9tLmxpbmUgKyAxLCBhZGRlZCk7XHJcbiAgICAgIH1cclxuICAgIH0gZWxzZSBpZiAodGV4dC5sZW5ndGggPT0gMSkge1xyXG4gICAgICB1cGRhdGUoZmlyc3RMaW5lLCBmaXJzdExpbmUudGV4dC5zbGljZSgwLCBmcm9tLmNoKSArIHRleHRbMF0gKyBsYXN0TGluZS50ZXh0LnNsaWNlKHRvLmNoKSwgc3BhbnNGb3IoMCkpO1xyXG4gICAgICBkb2MucmVtb3ZlKGZyb20ubGluZSArIDEsIG5saW5lcyk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICB1cGRhdGUoZmlyc3RMaW5lLCBmaXJzdExpbmUudGV4dC5zbGljZSgwLCBmcm9tLmNoKSArIHRleHRbMF0sIHNwYW5zRm9yKDApKTtcclxuICAgICAgdXBkYXRlKGxhc3RMaW5lLCBsYXN0VGV4dCArIGxhc3RMaW5lLnRleHQuc2xpY2UodG8uY2gpLCBsYXN0U3BhbnMpO1xyXG4gICAgICB2YXIgYWRkZWQgPSBsaW5lc0ZvcigxLCB0ZXh0Lmxlbmd0aCAtIDEpO1xyXG4gICAgICBpZiAobmxpbmVzID4gMSkgZG9jLnJlbW92ZShmcm9tLmxpbmUgKyAxLCBubGluZXMgLSAxKTtcclxuICAgICAgZG9jLmluc2VydChmcm9tLmxpbmUgKyAxLCBhZGRlZCk7XHJcbiAgICB9XHJcblxyXG4gICAgc2lnbmFsTGF0ZXIoZG9jLCBcImNoYW5nZVwiLCBkb2MsIGNoYW5nZSk7XHJcbiAgfVxyXG5cclxuICAvLyBUaGUgZG9jdW1lbnQgaXMgcmVwcmVzZW50ZWQgYXMgYSBCVHJlZSBjb25zaXN0aW5nIG9mIGxlYXZlcywgd2l0aFxyXG4gIC8vIGNodW5rIG9mIGxpbmVzIGluIHRoZW0sIGFuZCBicmFuY2hlcywgd2l0aCB1cCB0byB0ZW4gbGVhdmVzIG9yXHJcbiAgLy8gb3RoZXIgYnJhbmNoIG5vZGVzIGJlbG93IHRoZW0uIFRoZSB0b3Agbm9kZSBpcyBhbHdheXMgYSBicmFuY2hcclxuICAvLyBub2RlLCBhbmQgaXMgdGhlIGRvY3VtZW50IG9iamVjdCBpdHNlbGYgKG1lYW5pbmcgaXQgaGFzXHJcbiAgLy8gYWRkaXRpb25hbCBtZXRob2RzIGFuZCBwcm9wZXJ0aWVzKS5cclxuICAvL1xyXG4gIC8vIEFsbCBub2RlcyBoYXZlIHBhcmVudCBsaW5rcy4gVGhlIHRyZWUgaXMgdXNlZCBib3RoIHRvIGdvIGZyb21cclxuICAvLyBsaW5lIG51bWJlcnMgdG8gbGluZSBvYmplY3RzLCBhbmQgdG8gZ28gZnJvbSBvYmplY3RzIHRvIG51bWJlcnMuXHJcbiAgLy8gSXQgYWxzbyBpbmRleGVzIGJ5IGhlaWdodCwgYW5kIGlzIHVzZWQgdG8gY29udmVydCBiZXR3ZWVuIGhlaWdodFxyXG4gIC8vIGFuZCBsaW5lIG9iamVjdCwgYW5kIHRvIGZpbmQgdGhlIHRvdGFsIGhlaWdodCBvZiB0aGUgZG9jdW1lbnQuXHJcbiAgLy9cclxuICAvLyBTZWUgYWxzbyBodHRwOi8vbWFyaWpuaGF2ZXJiZWtlLm5sL2Jsb2cvY29kZW1pcnJvci1saW5lLXRyZWUuaHRtbFxyXG5cclxuICBmdW5jdGlvbiBMZWFmQ2h1bmsobGluZXMpIHtcclxuICAgIHRoaXMubGluZXMgPSBsaW5lcztcclxuICAgIHRoaXMucGFyZW50ID0gbnVsbDtcclxuICAgIGZvciAodmFyIGkgPSAwLCBoZWlnaHQgPSAwOyBpIDwgbGluZXMubGVuZ3RoOyArK2kpIHtcclxuICAgICAgbGluZXNbaV0ucGFyZW50ID0gdGhpcztcclxuICAgICAgaGVpZ2h0ICs9IGxpbmVzW2ldLmhlaWdodDtcclxuICAgIH1cclxuICAgIHRoaXMuaGVpZ2h0ID0gaGVpZ2h0O1xyXG4gIH1cclxuXHJcbiAgTGVhZkNodW5rLnByb3RvdHlwZSA9IHtcclxuICAgIGNodW5rU2l6ZTogZnVuY3Rpb24oKSB7IHJldHVybiB0aGlzLmxpbmVzLmxlbmd0aDsgfSxcclxuICAgIC8vIFJlbW92ZSB0aGUgbiBsaW5lcyBhdCBvZmZzZXQgJ2F0Jy5cclxuICAgIHJlbW92ZUlubmVyOiBmdW5jdGlvbihhdCwgbikge1xyXG4gICAgICBmb3IgKHZhciBpID0gYXQsIGUgPSBhdCArIG47IGkgPCBlOyArK2kpIHtcclxuICAgICAgICB2YXIgbGluZSA9IHRoaXMubGluZXNbaV07XHJcbiAgICAgICAgdGhpcy5oZWlnaHQgLT0gbGluZS5oZWlnaHQ7XHJcbiAgICAgICAgY2xlYW5VcExpbmUobGluZSk7XHJcbiAgICAgICAgc2lnbmFsTGF0ZXIobGluZSwgXCJkZWxldGVcIik7XHJcbiAgICAgIH1cclxuICAgICAgdGhpcy5saW5lcy5zcGxpY2UoYXQsIG4pO1xyXG4gICAgfSxcclxuICAgIC8vIEhlbHBlciB1c2VkIHRvIGNvbGxhcHNlIGEgc21hbGwgYnJhbmNoIGludG8gYSBzaW5nbGUgbGVhZi5cclxuICAgIGNvbGxhcHNlOiBmdW5jdGlvbihsaW5lcykge1xyXG4gICAgICBsaW5lcy5wdXNoLmFwcGx5KGxpbmVzLCB0aGlzLmxpbmVzKTtcclxuICAgIH0sXHJcbiAgICAvLyBJbnNlcnQgdGhlIGdpdmVuIGFycmF5IG9mIGxpbmVzIGF0IG9mZnNldCAnYXQnLCBjb3VudCB0aGVtIGFzXHJcbiAgICAvLyBoYXZpbmcgdGhlIGdpdmVuIGhlaWdodC5cclxuICAgIGluc2VydElubmVyOiBmdW5jdGlvbihhdCwgbGluZXMsIGhlaWdodCkge1xyXG4gICAgICB0aGlzLmhlaWdodCArPSBoZWlnaHQ7XHJcbiAgICAgIHRoaXMubGluZXMgPSB0aGlzLmxpbmVzLnNsaWNlKDAsIGF0KS5jb25jYXQobGluZXMpLmNvbmNhdCh0aGlzLmxpbmVzLnNsaWNlKGF0KSk7XHJcbiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbGluZXMubGVuZ3RoOyArK2kpIGxpbmVzW2ldLnBhcmVudCA9IHRoaXM7XHJcbiAgICB9LFxyXG4gICAgLy8gVXNlZCB0byBpdGVyYXRlIG92ZXIgYSBwYXJ0IG9mIHRoZSB0cmVlLlxyXG4gICAgaXRlck46IGZ1bmN0aW9uKGF0LCBuLCBvcCkge1xyXG4gICAgICBmb3IgKHZhciBlID0gYXQgKyBuOyBhdCA8IGU7ICsrYXQpXHJcbiAgICAgICAgaWYgKG9wKHRoaXMubGluZXNbYXRdKSkgcmV0dXJuIHRydWU7XHJcbiAgICB9XHJcbiAgfTtcclxuXHJcbiAgZnVuY3Rpb24gQnJhbmNoQ2h1bmsoY2hpbGRyZW4pIHtcclxuICAgIHRoaXMuY2hpbGRyZW4gPSBjaGlsZHJlbjtcclxuICAgIHZhciBzaXplID0gMCwgaGVpZ2h0ID0gMDtcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgY2hpbGRyZW4ubGVuZ3RoOyArK2kpIHtcclxuICAgICAgdmFyIGNoID0gY2hpbGRyZW5baV07XHJcbiAgICAgIHNpemUgKz0gY2guY2h1bmtTaXplKCk7IGhlaWdodCArPSBjaC5oZWlnaHQ7XHJcbiAgICAgIGNoLnBhcmVudCA9IHRoaXM7XHJcbiAgICB9XHJcbiAgICB0aGlzLnNpemUgPSBzaXplO1xyXG4gICAgdGhpcy5oZWlnaHQgPSBoZWlnaHQ7XHJcbiAgICB0aGlzLnBhcmVudCA9IG51bGw7XHJcbiAgfVxyXG5cclxuICBCcmFuY2hDaHVuay5wcm90b3R5cGUgPSB7XHJcbiAgICBjaHVua1NpemU6IGZ1bmN0aW9uKCkgeyByZXR1cm4gdGhpcy5zaXplOyB9LFxyXG4gICAgcmVtb3ZlSW5uZXI6IGZ1bmN0aW9uKGF0LCBuKSB7XHJcbiAgICAgIHRoaXMuc2l6ZSAtPSBuO1xyXG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHRoaXMuY2hpbGRyZW4ubGVuZ3RoOyArK2kpIHtcclxuICAgICAgICB2YXIgY2hpbGQgPSB0aGlzLmNoaWxkcmVuW2ldLCBzeiA9IGNoaWxkLmNodW5rU2l6ZSgpO1xyXG4gICAgICAgIGlmIChhdCA8IHN6KSB7XHJcbiAgICAgICAgICB2YXIgcm0gPSBNYXRoLm1pbihuLCBzeiAtIGF0KSwgb2xkSGVpZ2h0ID0gY2hpbGQuaGVpZ2h0O1xyXG4gICAgICAgICAgY2hpbGQucmVtb3ZlSW5uZXIoYXQsIHJtKTtcclxuICAgICAgICAgIHRoaXMuaGVpZ2h0IC09IG9sZEhlaWdodCAtIGNoaWxkLmhlaWdodDtcclxuICAgICAgICAgIGlmIChzeiA9PSBybSkgeyB0aGlzLmNoaWxkcmVuLnNwbGljZShpLS0sIDEpOyBjaGlsZC5wYXJlbnQgPSBudWxsOyB9XHJcbiAgICAgICAgICBpZiAoKG4gLT0gcm0pID09IDApIGJyZWFrO1xyXG4gICAgICAgICAgYXQgPSAwO1xyXG4gICAgICAgIH0gZWxzZSBhdCAtPSBzejtcclxuICAgICAgfVxyXG4gICAgICAvLyBJZiB0aGUgcmVzdWx0IGlzIHNtYWxsZXIgdGhhbiAyNSBsaW5lcywgZW5zdXJlIHRoYXQgaXQgaXMgYVxyXG4gICAgICAvLyBzaW5nbGUgbGVhZiBub2RlLlxyXG4gICAgICBpZiAodGhpcy5zaXplIC0gbiA8IDI1ICYmXHJcbiAgICAgICAgICAodGhpcy5jaGlsZHJlbi5sZW5ndGggPiAxIHx8ICEodGhpcy5jaGlsZHJlblswXSBpbnN0YW5jZW9mIExlYWZDaHVuaykpKSB7XHJcbiAgICAgICAgdmFyIGxpbmVzID0gW107XHJcbiAgICAgICAgdGhpcy5jb2xsYXBzZShsaW5lcyk7XHJcbiAgICAgICAgdGhpcy5jaGlsZHJlbiA9IFtuZXcgTGVhZkNodW5rKGxpbmVzKV07XHJcbiAgICAgICAgdGhpcy5jaGlsZHJlblswXS5wYXJlbnQgPSB0aGlzO1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY29sbGFwc2U6IGZ1bmN0aW9uKGxpbmVzKSB7XHJcbiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy5jaGlsZHJlbi5sZW5ndGg7ICsraSkgdGhpcy5jaGlsZHJlbltpXS5jb2xsYXBzZShsaW5lcyk7XHJcbiAgICB9LFxyXG4gICAgaW5zZXJ0SW5uZXI6IGZ1bmN0aW9uKGF0LCBsaW5lcywgaGVpZ2h0KSB7XHJcbiAgICAgIHRoaXMuc2l6ZSArPSBsaW5lcy5sZW5ndGg7XHJcbiAgICAgIHRoaXMuaGVpZ2h0ICs9IGhlaWdodDtcclxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB0aGlzLmNoaWxkcmVuLmxlbmd0aDsgKytpKSB7XHJcbiAgICAgICAgdmFyIGNoaWxkID0gdGhpcy5jaGlsZHJlbltpXSwgc3ogPSBjaGlsZC5jaHVua1NpemUoKTtcclxuICAgICAgICBpZiAoYXQgPD0gc3opIHtcclxuICAgICAgICAgIGNoaWxkLmluc2VydElubmVyKGF0LCBsaW5lcywgaGVpZ2h0KTtcclxuICAgICAgICAgIGlmIChjaGlsZC5saW5lcyAmJiBjaGlsZC5saW5lcy5sZW5ndGggPiA1MCkge1xyXG4gICAgICAgICAgICB3aGlsZSAoY2hpbGQubGluZXMubGVuZ3RoID4gNTApIHtcclxuICAgICAgICAgICAgICB2YXIgc3BpbGxlZCA9IGNoaWxkLmxpbmVzLnNwbGljZShjaGlsZC5saW5lcy5sZW5ndGggLSAyNSwgMjUpO1xyXG4gICAgICAgICAgICAgIHZhciBuZXdsZWFmID0gbmV3IExlYWZDaHVuayhzcGlsbGVkKTtcclxuICAgICAgICAgICAgICBjaGlsZC5oZWlnaHQgLT0gbmV3bGVhZi5oZWlnaHQ7XHJcbiAgICAgICAgICAgICAgdGhpcy5jaGlsZHJlbi5zcGxpY2UoaSArIDEsIDAsIG5ld2xlYWYpO1xyXG4gICAgICAgICAgICAgIG5ld2xlYWYucGFyZW50ID0gdGhpcztcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB0aGlzLm1heWJlU3BpbGwoKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgIH1cclxuICAgICAgICBhdCAtPSBzejtcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIC8vIFdoZW4gYSBub2RlIGhhcyBncm93biwgY2hlY2sgd2hldGhlciBpdCBzaG91bGQgYmUgc3BsaXQuXHJcbiAgICBtYXliZVNwaWxsOiBmdW5jdGlvbigpIHtcclxuICAgICAgaWYgKHRoaXMuY2hpbGRyZW4ubGVuZ3RoIDw9IDEwKSByZXR1cm47XHJcbiAgICAgIHZhciBtZSA9IHRoaXM7XHJcbiAgICAgIGRvIHtcclxuICAgICAgICB2YXIgc3BpbGxlZCA9IG1lLmNoaWxkcmVuLnNwbGljZShtZS5jaGlsZHJlbi5sZW5ndGggLSA1LCA1KTtcclxuICAgICAgICB2YXIgc2libGluZyA9IG5ldyBCcmFuY2hDaHVuayhzcGlsbGVkKTtcclxuICAgICAgICBpZiAoIW1lLnBhcmVudCkgeyAvLyBCZWNvbWUgdGhlIHBhcmVudCBub2RlXHJcbiAgICAgICAgICB2YXIgY29weSA9IG5ldyBCcmFuY2hDaHVuayhtZS5jaGlsZHJlbik7XHJcbiAgICAgICAgICBjb3B5LnBhcmVudCA9IG1lO1xyXG4gICAgICAgICAgbWUuY2hpbGRyZW4gPSBbY29weSwgc2libGluZ107XHJcbiAgICAgICAgICBtZSA9IGNvcHk7XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgIG1lLnNpemUgLT0gc2libGluZy5zaXplO1xyXG4gICAgICAgICAgbWUuaGVpZ2h0IC09IHNpYmxpbmcuaGVpZ2h0O1xyXG4gICAgICAgICAgdmFyIG15SW5kZXggPSBpbmRleE9mKG1lLnBhcmVudC5jaGlsZHJlbiwgbWUpO1xyXG4gICAgICAgICAgbWUucGFyZW50LmNoaWxkcmVuLnNwbGljZShteUluZGV4ICsgMSwgMCwgc2libGluZyk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHNpYmxpbmcucGFyZW50ID0gbWUucGFyZW50O1xyXG4gICAgICB9IHdoaWxlIChtZS5jaGlsZHJlbi5sZW5ndGggPiAxMCk7XHJcbiAgICAgIG1lLnBhcmVudC5tYXliZVNwaWxsKCk7XHJcbiAgICB9LFxyXG4gICAgaXRlck46IGZ1bmN0aW9uKGF0LCBuLCBvcCkge1xyXG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHRoaXMuY2hpbGRyZW4ubGVuZ3RoOyArK2kpIHtcclxuICAgICAgICB2YXIgY2hpbGQgPSB0aGlzLmNoaWxkcmVuW2ldLCBzeiA9IGNoaWxkLmNodW5rU2l6ZSgpO1xyXG4gICAgICAgIGlmIChhdCA8IHN6KSB7XHJcbiAgICAgICAgICB2YXIgdXNlZCA9IE1hdGgubWluKG4sIHN6IC0gYXQpO1xyXG4gICAgICAgICAgaWYgKGNoaWxkLml0ZXJOKGF0LCB1c2VkLCBvcCkpIHJldHVybiB0cnVlO1xyXG4gICAgICAgICAgaWYgKChuIC09IHVzZWQpID09IDApIGJyZWFrO1xyXG4gICAgICAgICAgYXQgPSAwO1xyXG4gICAgICAgIH0gZWxzZSBhdCAtPSBzejtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH07XHJcblxyXG4gIHZhciBuZXh0RG9jSWQgPSAwO1xyXG4gIHZhciBEb2MgPSBDb2RlTWlycm9yLkRvYyA9IGZ1bmN0aW9uKHRleHQsIG1vZGUsIGZpcnN0TGluZSwgbGluZVNlcCkge1xyXG4gICAgaWYgKCEodGhpcyBpbnN0YW5jZW9mIERvYykpIHJldHVybiBuZXcgRG9jKHRleHQsIG1vZGUsIGZpcnN0TGluZSwgbGluZVNlcCk7XHJcbiAgICBpZiAoZmlyc3RMaW5lID09IG51bGwpIGZpcnN0TGluZSA9IDA7XHJcblxyXG4gICAgQnJhbmNoQ2h1bmsuY2FsbCh0aGlzLCBbbmV3IExlYWZDaHVuayhbbmV3IExpbmUoXCJcIiwgbnVsbCldKV0pO1xyXG4gICAgdGhpcy5maXJzdCA9IGZpcnN0TGluZTtcclxuICAgIHRoaXMuc2Nyb2xsVG9wID0gdGhpcy5zY3JvbGxMZWZ0ID0gMDtcclxuICAgIHRoaXMuY2FudEVkaXQgPSBmYWxzZTtcclxuICAgIHRoaXMuY2xlYW5HZW5lcmF0aW9uID0gMTtcclxuICAgIHRoaXMuZnJvbnRpZXIgPSBmaXJzdExpbmU7XHJcbiAgICB2YXIgc3RhcnQgPSBQb3MoZmlyc3RMaW5lLCAwKTtcclxuICAgIHRoaXMuc2VsID0gc2ltcGxlU2VsZWN0aW9uKHN0YXJ0KTtcclxuICAgIHRoaXMuaGlzdG9yeSA9IG5ldyBIaXN0b3J5KG51bGwpO1xyXG4gICAgdGhpcy5pZCA9ICsrbmV4dERvY0lkO1xyXG4gICAgdGhpcy5tb2RlT3B0aW9uID0gbW9kZTtcclxuICAgIHRoaXMubGluZVNlcCA9IGxpbmVTZXA7XHJcblxyXG4gICAgaWYgKHR5cGVvZiB0ZXh0ID09IFwic3RyaW5nXCIpIHRleHQgPSB0aGlzLnNwbGl0TGluZXModGV4dCk7XHJcbiAgICB1cGRhdGVEb2ModGhpcywge2Zyb206IHN0YXJ0LCB0bzogc3RhcnQsIHRleHQ6IHRleHR9KTtcclxuICAgIHNldFNlbGVjdGlvbih0aGlzLCBzaW1wbGVTZWxlY3Rpb24oc3RhcnQpLCBzZWxfZG9udFNjcm9sbCk7XHJcbiAgfTtcclxuXHJcbiAgRG9jLnByb3RvdHlwZSA9IGNyZWF0ZU9iaihCcmFuY2hDaHVuay5wcm90b3R5cGUsIHtcclxuICAgIGNvbnN0cnVjdG9yOiBEb2MsXHJcbiAgICAvLyBJdGVyYXRlIG92ZXIgdGhlIGRvY3VtZW50LiBTdXBwb3J0cyB0d28gZm9ybXMgLS0gd2l0aCBvbmx5IG9uZVxyXG4gICAgLy8gYXJndW1lbnQsIGl0IGNhbGxzIHRoYXQgZm9yIGVhY2ggbGluZSBpbiB0aGUgZG9jdW1lbnQuIFdpdGhcclxuICAgIC8vIHRocmVlLCBpdCBpdGVyYXRlcyBvdmVyIHRoZSByYW5nZSBnaXZlbiBieSB0aGUgZmlyc3QgdHdvICh3aXRoXHJcbiAgICAvLyB0aGUgc2Vjb25kIGJlaW5nIG5vbi1pbmNsdXNpdmUpLlxyXG4gICAgaXRlcjogZnVuY3Rpb24oZnJvbSwgdG8sIG9wKSB7XHJcbiAgICAgIGlmIChvcCkgdGhpcy5pdGVyTihmcm9tIC0gdGhpcy5maXJzdCwgdG8gLSBmcm9tLCBvcCk7XHJcbiAgICAgIGVsc2UgdGhpcy5pdGVyTih0aGlzLmZpcnN0LCB0aGlzLmZpcnN0ICsgdGhpcy5zaXplLCBmcm9tKTtcclxuICAgIH0sXHJcblxyXG4gICAgLy8gTm9uLXB1YmxpYyBpbnRlcmZhY2UgZm9yIGFkZGluZyBhbmQgcmVtb3ZpbmcgbGluZXMuXHJcbiAgICBpbnNlcnQ6IGZ1bmN0aW9uKGF0LCBsaW5lcykge1xyXG4gICAgICB2YXIgaGVpZ2h0ID0gMDtcclxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsaW5lcy5sZW5ndGg7ICsraSkgaGVpZ2h0ICs9IGxpbmVzW2ldLmhlaWdodDtcclxuICAgICAgdGhpcy5pbnNlcnRJbm5lcihhdCAtIHRoaXMuZmlyc3QsIGxpbmVzLCBoZWlnaHQpO1xyXG4gICAgfSxcclxuICAgIHJlbW92ZTogZnVuY3Rpb24oYXQsIG4pIHsgdGhpcy5yZW1vdmVJbm5lcihhdCAtIHRoaXMuZmlyc3QsIG4pOyB9LFxyXG5cclxuICAgIC8vIEZyb20gaGVyZSwgdGhlIG1ldGhvZHMgYXJlIHBhcnQgb2YgdGhlIHB1YmxpYyBpbnRlcmZhY2UuIE1vc3RcclxuICAgIC8vIGFyZSBhbHNvIGF2YWlsYWJsZSBmcm9tIENvZGVNaXJyb3IgKGVkaXRvcikgaW5zdGFuY2VzLlxyXG5cclxuICAgIGdldFZhbHVlOiBmdW5jdGlvbihsaW5lU2VwKSB7XHJcbiAgICAgIHZhciBsaW5lcyA9IGdldExpbmVzKHRoaXMsIHRoaXMuZmlyc3QsIHRoaXMuZmlyc3QgKyB0aGlzLnNpemUpO1xyXG4gICAgICBpZiAobGluZVNlcCA9PT0gZmFsc2UpIHJldHVybiBsaW5lcztcclxuICAgICAgcmV0dXJuIGxpbmVzLmpvaW4obGluZVNlcCB8fCB0aGlzLmxpbmVTZXBhcmF0b3IoKSk7XHJcbiAgICB9LFxyXG4gICAgc2V0VmFsdWU6IGRvY01ldGhvZE9wKGZ1bmN0aW9uKGNvZGUpIHtcclxuICAgICAgdmFyIHRvcCA9IFBvcyh0aGlzLmZpcnN0LCAwKSwgbGFzdCA9IHRoaXMuZmlyc3QgKyB0aGlzLnNpemUgLSAxO1xyXG4gICAgICBtYWtlQ2hhbmdlKHRoaXMsIHtmcm9tOiB0b3AsIHRvOiBQb3MobGFzdCwgZ2V0TGluZSh0aGlzLCBsYXN0KS50ZXh0Lmxlbmd0aCksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHRleHQ6IHRoaXMuc3BsaXRMaW5lcyhjb2RlKSwgb3JpZ2luOiBcInNldFZhbHVlXCIsIGZ1bGw6IHRydWV9LCB0cnVlKTtcclxuICAgICAgc2V0U2VsZWN0aW9uKHRoaXMsIHNpbXBsZVNlbGVjdGlvbih0b3ApKTtcclxuICAgIH0pLFxyXG4gICAgcmVwbGFjZVJhbmdlOiBmdW5jdGlvbihjb2RlLCBmcm9tLCB0bywgb3JpZ2luKSB7XHJcbiAgICAgIGZyb20gPSBjbGlwUG9zKHRoaXMsIGZyb20pO1xyXG4gICAgICB0byA9IHRvID8gY2xpcFBvcyh0aGlzLCB0bykgOiBmcm9tO1xyXG4gICAgICByZXBsYWNlUmFuZ2UodGhpcywgY29kZSwgZnJvbSwgdG8sIG9yaWdpbik7XHJcbiAgICB9LFxyXG4gICAgZ2V0UmFuZ2U6IGZ1bmN0aW9uKGZyb20sIHRvLCBsaW5lU2VwKSB7XHJcbiAgICAgIHZhciBsaW5lcyA9IGdldEJldHdlZW4odGhpcywgY2xpcFBvcyh0aGlzLCBmcm9tKSwgY2xpcFBvcyh0aGlzLCB0bykpO1xyXG4gICAgICBpZiAobGluZVNlcCA9PT0gZmFsc2UpIHJldHVybiBsaW5lcztcclxuICAgICAgcmV0dXJuIGxpbmVzLmpvaW4obGluZVNlcCB8fCB0aGlzLmxpbmVTZXBhcmF0b3IoKSk7XHJcbiAgICB9LFxyXG5cclxuICAgIGdldExpbmU6IGZ1bmN0aW9uKGxpbmUpIHt2YXIgbCA9IHRoaXMuZ2V0TGluZUhhbmRsZShsaW5lKTsgcmV0dXJuIGwgJiYgbC50ZXh0O30sXHJcblxyXG4gICAgZ2V0TGluZUhhbmRsZTogZnVuY3Rpb24obGluZSkge2lmIChpc0xpbmUodGhpcywgbGluZSkpIHJldHVybiBnZXRMaW5lKHRoaXMsIGxpbmUpO30sXHJcbiAgICBnZXRMaW5lTnVtYmVyOiBmdW5jdGlvbihsaW5lKSB7cmV0dXJuIGxpbmVObyhsaW5lKTt9LFxyXG5cclxuICAgIGdldExpbmVIYW5kbGVWaXN1YWxTdGFydDogZnVuY3Rpb24obGluZSkge1xyXG4gICAgICBpZiAodHlwZW9mIGxpbmUgPT0gXCJudW1iZXJcIikgbGluZSA9IGdldExpbmUodGhpcywgbGluZSk7XHJcbiAgICAgIHJldHVybiB2aXN1YWxMaW5lKGxpbmUpO1xyXG4gICAgfSxcclxuXHJcbiAgICBsaW5lQ291bnQ6IGZ1bmN0aW9uKCkge3JldHVybiB0aGlzLnNpemU7fSxcclxuICAgIGZpcnN0TGluZTogZnVuY3Rpb24oKSB7cmV0dXJuIHRoaXMuZmlyc3Q7fSxcclxuICAgIGxhc3RMaW5lOiBmdW5jdGlvbigpIHtyZXR1cm4gdGhpcy5maXJzdCArIHRoaXMuc2l6ZSAtIDE7fSxcclxuXHJcbiAgICBjbGlwUG9zOiBmdW5jdGlvbihwb3MpIHtyZXR1cm4gY2xpcFBvcyh0aGlzLCBwb3MpO30sXHJcblxyXG4gICAgZ2V0Q3Vyc29yOiBmdW5jdGlvbihzdGFydCkge1xyXG4gICAgICB2YXIgcmFuZ2UgPSB0aGlzLnNlbC5wcmltYXJ5KCksIHBvcztcclxuICAgICAgaWYgKHN0YXJ0ID09IG51bGwgfHwgc3RhcnQgPT0gXCJoZWFkXCIpIHBvcyA9IHJhbmdlLmhlYWQ7XHJcbiAgICAgIGVsc2UgaWYgKHN0YXJ0ID09IFwiYW5jaG9yXCIpIHBvcyA9IHJhbmdlLmFuY2hvcjtcclxuICAgICAgZWxzZSBpZiAoc3RhcnQgPT0gXCJlbmRcIiB8fCBzdGFydCA9PSBcInRvXCIgfHwgc3RhcnQgPT09IGZhbHNlKSBwb3MgPSByYW5nZS50bygpO1xyXG4gICAgICBlbHNlIHBvcyA9IHJhbmdlLmZyb20oKTtcclxuICAgICAgcmV0dXJuIHBvcztcclxuICAgIH0sXHJcbiAgICBsaXN0U2VsZWN0aW9uczogZnVuY3Rpb24oKSB7IHJldHVybiB0aGlzLnNlbC5yYW5nZXM7IH0sXHJcbiAgICBzb21ldGhpbmdTZWxlY3RlZDogZnVuY3Rpb24oKSB7cmV0dXJuIHRoaXMuc2VsLnNvbWV0aGluZ1NlbGVjdGVkKCk7fSxcclxuXHJcbiAgICBzZXRDdXJzb3I6IGRvY01ldGhvZE9wKGZ1bmN0aW9uKGxpbmUsIGNoLCBvcHRpb25zKSB7XHJcbiAgICAgIHNldFNpbXBsZVNlbGVjdGlvbih0aGlzLCBjbGlwUG9zKHRoaXMsIHR5cGVvZiBsaW5lID09IFwibnVtYmVyXCIgPyBQb3MobGluZSwgY2ggfHwgMCkgOiBsaW5lKSwgbnVsbCwgb3B0aW9ucyk7XHJcbiAgICB9KSxcclxuICAgIHNldFNlbGVjdGlvbjogZG9jTWV0aG9kT3AoZnVuY3Rpb24oYW5jaG9yLCBoZWFkLCBvcHRpb25zKSB7XHJcbiAgICAgIHNldFNpbXBsZVNlbGVjdGlvbih0aGlzLCBjbGlwUG9zKHRoaXMsIGFuY2hvciksIGNsaXBQb3ModGhpcywgaGVhZCB8fCBhbmNob3IpLCBvcHRpb25zKTtcclxuICAgIH0pLFxyXG4gICAgZXh0ZW5kU2VsZWN0aW9uOiBkb2NNZXRob2RPcChmdW5jdGlvbihoZWFkLCBvdGhlciwgb3B0aW9ucykge1xyXG4gICAgICBleHRlbmRTZWxlY3Rpb24odGhpcywgY2xpcFBvcyh0aGlzLCBoZWFkKSwgb3RoZXIgJiYgY2xpcFBvcyh0aGlzLCBvdGhlciksIG9wdGlvbnMpO1xyXG4gICAgfSksXHJcbiAgICBleHRlbmRTZWxlY3Rpb25zOiBkb2NNZXRob2RPcChmdW5jdGlvbihoZWFkcywgb3B0aW9ucykge1xyXG4gICAgICBleHRlbmRTZWxlY3Rpb25zKHRoaXMsIGNsaXBQb3NBcnJheSh0aGlzLCBoZWFkcywgb3B0aW9ucykpO1xyXG4gICAgfSksXHJcbiAgICBleHRlbmRTZWxlY3Rpb25zQnk6IGRvY01ldGhvZE9wKGZ1bmN0aW9uKGYsIG9wdGlvbnMpIHtcclxuICAgICAgZXh0ZW5kU2VsZWN0aW9ucyh0aGlzLCBtYXAodGhpcy5zZWwucmFuZ2VzLCBmKSwgb3B0aW9ucyk7XHJcbiAgICB9KSxcclxuICAgIHNldFNlbGVjdGlvbnM6IGRvY01ldGhvZE9wKGZ1bmN0aW9uKHJhbmdlcywgcHJpbWFyeSwgb3B0aW9ucykge1xyXG4gICAgICBpZiAoIXJhbmdlcy5sZW5ndGgpIHJldHVybjtcclxuICAgICAgZm9yICh2YXIgaSA9IDAsIG91dCA9IFtdOyBpIDwgcmFuZ2VzLmxlbmd0aDsgaSsrKVxyXG4gICAgICAgIG91dFtpXSA9IG5ldyBSYW5nZShjbGlwUG9zKHRoaXMsIHJhbmdlc1tpXS5hbmNob3IpLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICBjbGlwUG9zKHRoaXMsIHJhbmdlc1tpXS5oZWFkKSk7XHJcbiAgICAgIGlmIChwcmltYXJ5ID09IG51bGwpIHByaW1hcnkgPSBNYXRoLm1pbihyYW5nZXMubGVuZ3RoIC0gMSwgdGhpcy5zZWwucHJpbUluZGV4KTtcclxuICAgICAgc2V0U2VsZWN0aW9uKHRoaXMsIG5vcm1hbGl6ZVNlbGVjdGlvbihvdXQsIHByaW1hcnkpLCBvcHRpb25zKTtcclxuICAgIH0pLFxyXG4gICAgYWRkU2VsZWN0aW9uOiBkb2NNZXRob2RPcChmdW5jdGlvbihhbmNob3IsIGhlYWQsIG9wdGlvbnMpIHtcclxuICAgICAgdmFyIHJhbmdlcyA9IHRoaXMuc2VsLnJhbmdlcy5zbGljZSgwKTtcclxuICAgICAgcmFuZ2VzLnB1c2gobmV3IFJhbmdlKGNsaXBQb3ModGhpcywgYW5jaG9yKSwgY2xpcFBvcyh0aGlzLCBoZWFkIHx8IGFuY2hvcikpKTtcclxuICAgICAgc2V0U2VsZWN0aW9uKHRoaXMsIG5vcm1hbGl6ZVNlbGVjdGlvbihyYW5nZXMsIHJhbmdlcy5sZW5ndGggLSAxKSwgb3B0aW9ucyk7XHJcbiAgICB9KSxcclxuXHJcbiAgICBnZXRTZWxlY3Rpb246IGZ1bmN0aW9uKGxpbmVTZXApIHtcclxuICAgICAgdmFyIHJhbmdlcyA9IHRoaXMuc2VsLnJhbmdlcywgbGluZXM7XHJcbiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgcmFuZ2VzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgdmFyIHNlbCA9IGdldEJldHdlZW4odGhpcywgcmFuZ2VzW2ldLmZyb20oKSwgcmFuZ2VzW2ldLnRvKCkpO1xyXG4gICAgICAgIGxpbmVzID0gbGluZXMgPyBsaW5lcy5jb25jYXQoc2VsKSA6IHNlbDtcclxuICAgICAgfVxyXG4gICAgICBpZiAobGluZVNlcCA9PT0gZmFsc2UpIHJldHVybiBsaW5lcztcclxuICAgICAgZWxzZSByZXR1cm4gbGluZXMuam9pbihsaW5lU2VwIHx8IHRoaXMubGluZVNlcGFyYXRvcigpKTtcclxuICAgIH0sXHJcbiAgICBnZXRTZWxlY3Rpb25zOiBmdW5jdGlvbihsaW5lU2VwKSB7XHJcbiAgICAgIHZhciBwYXJ0cyA9IFtdLCByYW5nZXMgPSB0aGlzLnNlbC5yYW5nZXM7XHJcbiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgcmFuZ2VzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgdmFyIHNlbCA9IGdldEJldHdlZW4odGhpcywgcmFuZ2VzW2ldLmZyb20oKSwgcmFuZ2VzW2ldLnRvKCkpO1xyXG4gICAgICAgIGlmIChsaW5lU2VwICE9PSBmYWxzZSkgc2VsID0gc2VsLmpvaW4obGluZVNlcCB8fCB0aGlzLmxpbmVTZXBhcmF0b3IoKSk7XHJcbiAgICAgICAgcGFydHNbaV0gPSBzZWw7XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIHBhcnRzO1xyXG4gICAgfSxcclxuICAgIHJlcGxhY2VTZWxlY3Rpb246IGZ1bmN0aW9uKGNvZGUsIGNvbGxhcHNlLCBvcmlnaW4pIHtcclxuICAgICAgdmFyIGR1cCA9IFtdO1xyXG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHRoaXMuc2VsLnJhbmdlcy5sZW5ndGg7IGkrKylcclxuICAgICAgICBkdXBbaV0gPSBjb2RlO1xyXG4gICAgICB0aGlzLnJlcGxhY2VTZWxlY3Rpb25zKGR1cCwgY29sbGFwc2UsIG9yaWdpbiB8fCBcIitpbnB1dFwiKTtcclxuICAgIH0sXHJcbiAgICByZXBsYWNlU2VsZWN0aW9uczogZG9jTWV0aG9kT3AoZnVuY3Rpb24oY29kZSwgY29sbGFwc2UsIG9yaWdpbikge1xyXG4gICAgICB2YXIgY2hhbmdlcyA9IFtdLCBzZWwgPSB0aGlzLnNlbDtcclxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBzZWwucmFuZ2VzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgdmFyIHJhbmdlID0gc2VsLnJhbmdlc1tpXTtcclxuICAgICAgICBjaGFuZ2VzW2ldID0ge2Zyb206IHJhbmdlLmZyb20oKSwgdG86IHJhbmdlLnRvKCksIHRleHQ6IHRoaXMuc3BsaXRMaW5lcyhjb2RlW2ldKSwgb3JpZ2luOiBvcmlnaW59O1xyXG4gICAgICB9XHJcbiAgICAgIHZhciBuZXdTZWwgPSBjb2xsYXBzZSAmJiBjb2xsYXBzZSAhPSBcImVuZFwiICYmIGNvbXB1dGVSZXBsYWNlZFNlbCh0aGlzLCBjaGFuZ2VzLCBjb2xsYXBzZSk7XHJcbiAgICAgIGZvciAodmFyIGkgPSBjaGFuZ2VzLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tKVxyXG4gICAgICAgIG1ha2VDaGFuZ2UodGhpcywgY2hhbmdlc1tpXSk7XHJcbiAgICAgIGlmIChuZXdTZWwpIHNldFNlbGVjdGlvblJlcGxhY2VIaXN0b3J5KHRoaXMsIG5ld1NlbCk7XHJcbiAgICAgIGVsc2UgaWYgKHRoaXMuY20pIGVuc3VyZUN1cnNvclZpc2libGUodGhpcy5jbSk7XHJcbiAgICB9KSxcclxuICAgIHVuZG86IGRvY01ldGhvZE9wKGZ1bmN0aW9uKCkge21ha2VDaGFuZ2VGcm9tSGlzdG9yeSh0aGlzLCBcInVuZG9cIik7fSksXHJcbiAgICByZWRvOiBkb2NNZXRob2RPcChmdW5jdGlvbigpIHttYWtlQ2hhbmdlRnJvbUhpc3RvcnkodGhpcywgXCJyZWRvXCIpO30pLFxyXG4gICAgdW5kb1NlbGVjdGlvbjogZG9jTWV0aG9kT3AoZnVuY3Rpb24oKSB7bWFrZUNoYW5nZUZyb21IaXN0b3J5KHRoaXMsIFwidW5kb1wiLCB0cnVlKTt9KSxcclxuICAgIHJlZG9TZWxlY3Rpb246IGRvY01ldGhvZE9wKGZ1bmN0aW9uKCkge21ha2VDaGFuZ2VGcm9tSGlzdG9yeSh0aGlzLCBcInJlZG9cIiwgdHJ1ZSk7fSksXHJcblxyXG4gICAgc2V0RXh0ZW5kaW5nOiBmdW5jdGlvbih2YWwpIHt0aGlzLmV4dGVuZCA9IHZhbDt9LFxyXG4gICAgZ2V0RXh0ZW5kaW5nOiBmdW5jdGlvbigpIHtyZXR1cm4gdGhpcy5leHRlbmQ7fSxcclxuXHJcbiAgICBoaXN0b3J5U2l6ZTogZnVuY3Rpb24oKSB7XHJcbiAgICAgIHZhciBoaXN0ID0gdGhpcy5oaXN0b3J5LCBkb25lID0gMCwgdW5kb25lID0gMDtcclxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBoaXN0LmRvbmUubGVuZ3RoOyBpKyspIGlmICghaGlzdC5kb25lW2ldLnJhbmdlcykgKytkb25lO1xyXG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGhpc3QudW5kb25lLmxlbmd0aDsgaSsrKSBpZiAoIWhpc3QudW5kb25lW2ldLnJhbmdlcykgKyt1bmRvbmU7XHJcbiAgICAgIHJldHVybiB7dW5kbzogZG9uZSwgcmVkbzogdW5kb25lfTtcclxuICAgIH0sXHJcbiAgICBjbGVhckhpc3Rvcnk6IGZ1bmN0aW9uKCkge3RoaXMuaGlzdG9yeSA9IG5ldyBIaXN0b3J5KHRoaXMuaGlzdG9yeS5tYXhHZW5lcmF0aW9uKTt9LFxyXG5cclxuICAgIG1hcmtDbGVhbjogZnVuY3Rpb24oKSB7XHJcbiAgICAgIHRoaXMuY2xlYW5HZW5lcmF0aW9uID0gdGhpcy5jaGFuZ2VHZW5lcmF0aW9uKHRydWUpO1xyXG4gICAgfSxcclxuICAgIGNoYW5nZUdlbmVyYXRpb246IGZ1bmN0aW9uKGZvcmNlU3BsaXQpIHtcclxuICAgICAgaWYgKGZvcmNlU3BsaXQpXHJcbiAgICAgICAgdGhpcy5oaXN0b3J5Lmxhc3RPcCA9IHRoaXMuaGlzdG9yeS5sYXN0U2VsT3AgPSB0aGlzLmhpc3RvcnkubGFzdE9yaWdpbiA9IG51bGw7XHJcbiAgICAgIHJldHVybiB0aGlzLmhpc3RvcnkuZ2VuZXJhdGlvbjtcclxuICAgIH0sXHJcbiAgICBpc0NsZWFuOiBmdW5jdGlvbiAoZ2VuKSB7XHJcbiAgICAgIHJldHVybiB0aGlzLmhpc3RvcnkuZ2VuZXJhdGlvbiA9PSAoZ2VuIHx8IHRoaXMuY2xlYW5HZW5lcmF0aW9uKTtcclxuICAgIH0sXHJcblxyXG4gICAgZ2V0SGlzdG9yeTogZnVuY3Rpb24oKSB7XHJcbiAgICAgIHJldHVybiB7ZG9uZTogY29weUhpc3RvcnlBcnJheSh0aGlzLmhpc3RvcnkuZG9uZSksXHJcbiAgICAgICAgICAgICAgdW5kb25lOiBjb3B5SGlzdG9yeUFycmF5KHRoaXMuaGlzdG9yeS51bmRvbmUpfTtcclxuICAgIH0sXHJcbiAgICBzZXRIaXN0b3J5OiBmdW5jdGlvbihoaXN0RGF0YSkge1xyXG4gICAgICB2YXIgaGlzdCA9IHRoaXMuaGlzdG9yeSA9IG5ldyBIaXN0b3J5KHRoaXMuaGlzdG9yeS5tYXhHZW5lcmF0aW9uKTtcclxuICAgICAgaGlzdC5kb25lID0gY29weUhpc3RvcnlBcnJheShoaXN0RGF0YS5kb25lLnNsaWNlKDApLCBudWxsLCB0cnVlKTtcclxuICAgICAgaGlzdC51bmRvbmUgPSBjb3B5SGlzdG9yeUFycmF5KGhpc3REYXRhLnVuZG9uZS5zbGljZSgwKSwgbnVsbCwgdHJ1ZSk7XHJcbiAgICB9LFxyXG5cclxuICAgIGFkZExpbmVDbGFzczogZG9jTWV0aG9kT3AoZnVuY3Rpb24oaGFuZGxlLCB3aGVyZSwgY2xzKSB7XHJcbiAgICAgIHJldHVybiBjaGFuZ2VMaW5lKHRoaXMsIGhhbmRsZSwgd2hlcmUgPT0gXCJndXR0ZXJcIiA/IFwiZ3V0dGVyXCIgOiBcImNsYXNzXCIsIGZ1bmN0aW9uKGxpbmUpIHtcclxuICAgICAgICB2YXIgcHJvcCA9IHdoZXJlID09IFwidGV4dFwiID8gXCJ0ZXh0Q2xhc3NcIlxyXG4gICAgICAgICAgICAgICAgIDogd2hlcmUgPT0gXCJiYWNrZ3JvdW5kXCIgPyBcImJnQ2xhc3NcIlxyXG4gICAgICAgICAgICAgICAgIDogd2hlcmUgPT0gXCJndXR0ZXJcIiA/IFwiZ3V0dGVyQ2xhc3NcIiA6IFwid3JhcENsYXNzXCI7XHJcbiAgICAgICAgaWYgKCFsaW5lW3Byb3BdKSBsaW5lW3Byb3BdID0gY2xzO1xyXG4gICAgICAgIGVsc2UgaWYgKGNsYXNzVGVzdChjbHMpLnRlc3QobGluZVtwcm9wXSkpIHJldHVybiBmYWxzZTtcclxuICAgICAgICBlbHNlIGxpbmVbcHJvcF0gKz0gXCIgXCIgKyBjbHM7XHJcbiAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgIH0pO1xyXG4gICAgfSksXHJcbiAgICByZW1vdmVMaW5lQ2xhc3M6IGRvY01ldGhvZE9wKGZ1bmN0aW9uKGhhbmRsZSwgd2hlcmUsIGNscykge1xyXG4gICAgICByZXR1cm4gY2hhbmdlTGluZSh0aGlzLCBoYW5kbGUsIHdoZXJlID09IFwiZ3V0dGVyXCIgPyBcImd1dHRlclwiIDogXCJjbGFzc1wiLCBmdW5jdGlvbihsaW5lKSB7XHJcbiAgICAgICAgdmFyIHByb3AgPSB3aGVyZSA9PSBcInRleHRcIiA/IFwidGV4dENsYXNzXCJcclxuICAgICAgICAgICAgICAgICA6IHdoZXJlID09IFwiYmFja2dyb3VuZFwiID8gXCJiZ0NsYXNzXCJcclxuICAgICAgICAgICAgICAgICA6IHdoZXJlID09IFwiZ3V0dGVyXCIgPyBcImd1dHRlckNsYXNzXCIgOiBcIndyYXBDbGFzc1wiO1xyXG4gICAgICAgIHZhciBjdXIgPSBsaW5lW3Byb3BdO1xyXG4gICAgICAgIGlmICghY3VyKSByZXR1cm4gZmFsc2U7XHJcbiAgICAgICAgZWxzZSBpZiAoY2xzID09IG51bGwpIGxpbmVbcHJvcF0gPSBudWxsO1xyXG4gICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgdmFyIGZvdW5kID0gY3VyLm1hdGNoKGNsYXNzVGVzdChjbHMpKTtcclxuICAgICAgICAgIGlmICghZm91bmQpIHJldHVybiBmYWxzZTtcclxuICAgICAgICAgIHZhciBlbmQgPSBmb3VuZC5pbmRleCArIGZvdW5kWzBdLmxlbmd0aDtcclxuICAgICAgICAgIGxpbmVbcHJvcF0gPSBjdXIuc2xpY2UoMCwgZm91bmQuaW5kZXgpICsgKCFmb3VuZC5pbmRleCB8fCBlbmQgPT0gY3VyLmxlbmd0aCA/IFwiXCIgOiBcIiBcIikgKyBjdXIuc2xpY2UoZW5kKSB8fCBudWxsO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgfSk7XHJcbiAgICB9KSxcclxuXHJcbiAgICBhZGRMaW5lV2lkZ2V0OiBkb2NNZXRob2RPcChmdW5jdGlvbihoYW5kbGUsIG5vZGUsIG9wdGlvbnMpIHtcclxuICAgICAgcmV0dXJuIGFkZExpbmVXaWRnZXQodGhpcywgaGFuZGxlLCBub2RlLCBvcHRpb25zKTtcclxuICAgIH0pLFxyXG4gICAgcmVtb3ZlTGluZVdpZGdldDogZnVuY3Rpb24od2lkZ2V0KSB7IHdpZGdldC5jbGVhcigpOyB9LFxyXG5cclxuICAgIG1hcmtUZXh0OiBmdW5jdGlvbihmcm9tLCB0bywgb3B0aW9ucykge1xyXG4gICAgICByZXR1cm4gbWFya1RleHQodGhpcywgY2xpcFBvcyh0aGlzLCBmcm9tKSwgY2xpcFBvcyh0aGlzLCB0byksIG9wdGlvbnMsIFwicmFuZ2VcIik7XHJcbiAgICB9LFxyXG4gICAgc2V0Qm9va21hcms6IGZ1bmN0aW9uKHBvcywgb3B0aW9ucykge1xyXG4gICAgICB2YXIgcmVhbE9wdHMgPSB7cmVwbGFjZWRXaXRoOiBvcHRpb25zICYmIChvcHRpb25zLm5vZGVUeXBlID09IG51bGwgPyBvcHRpb25zLndpZGdldCA6IG9wdGlvbnMpLFxyXG4gICAgICAgICAgICAgICAgICAgICAgaW5zZXJ0TGVmdDogb3B0aW9ucyAmJiBvcHRpb25zLmluc2VydExlZnQsXHJcbiAgICAgICAgICAgICAgICAgICAgICBjbGVhcldoZW5FbXB0eTogZmFsc2UsIHNoYXJlZDogb3B0aW9ucyAmJiBvcHRpb25zLnNoYXJlZCxcclxuICAgICAgICAgICAgICAgICAgICAgIGhhbmRsZU1vdXNlRXZlbnRzOiBvcHRpb25zICYmIG9wdGlvbnMuaGFuZGxlTW91c2VFdmVudHN9O1xyXG4gICAgICBwb3MgPSBjbGlwUG9zKHRoaXMsIHBvcyk7XHJcbiAgICAgIHJldHVybiBtYXJrVGV4dCh0aGlzLCBwb3MsIHBvcywgcmVhbE9wdHMsIFwiYm9va21hcmtcIik7XHJcbiAgICB9LFxyXG4gICAgZmluZE1hcmtzQXQ6IGZ1bmN0aW9uKHBvcykge1xyXG4gICAgICBwb3MgPSBjbGlwUG9zKHRoaXMsIHBvcyk7XHJcbiAgICAgIHZhciBtYXJrZXJzID0gW10sIHNwYW5zID0gZ2V0TGluZSh0aGlzLCBwb3MubGluZSkubWFya2VkU3BhbnM7XHJcbiAgICAgIGlmIChzcGFucykgZm9yICh2YXIgaSA9IDA7IGkgPCBzcGFucy5sZW5ndGg7ICsraSkge1xyXG4gICAgICAgIHZhciBzcGFuID0gc3BhbnNbaV07XHJcbiAgICAgICAgaWYgKChzcGFuLmZyb20gPT0gbnVsbCB8fCBzcGFuLmZyb20gPD0gcG9zLmNoKSAmJlxyXG4gICAgICAgICAgICAoc3Bhbi50byA9PSBudWxsIHx8IHNwYW4udG8gPj0gcG9zLmNoKSlcclxuICAgICAgICAgIG1hcmtlcnMucHVzaChzcGFuLm1hcmtlci5wYXJlbnQgfHwgc3Bhbi5tYXJrZXIpO1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiBtYXJrZXJzO1xyXG4gICAgfSxcclxuICAgIGZpbmRNYXJrczogZnVuY3Rpb24oZnJvbSwgdG8sIGZpbHRlcikge1xyXG4gICAgICBmcm9tID0gY2xpcFBvcyh0aGlzLCBmcm9tKTsgdG8gPSBjbGlwUG9zKHRoaXMsIHRvKTtcclxuICAgICAgdmFyIGZvdW5kID0gW10sIGxpbmVObyA9IGZyb20ubGluZTtcclxuICAgICAgdGhpcy5pdGVyKGZyb20ubGluZSwgdG8ubGluZSArIDEsIGZ1bmN0aW9uKGxpbmUpIHtcclxuICAgICAgICB2YXIgc3BhbnMgPSBsaW5lLm1hcmtlZFNwYW5zO1xyXG4gICAgICAgIGlmIChzcGFucykgZm9yICh2YXIgaSA9IDA7IGkgPCBzcGFucy5sZW5ndGg7IGkrKykge1xyXG4gICAgICAgICAgdmFyIHNwYW4gPSBzcGFuc1tpXTtcclxuICAgICAgICAgIGlmICghKGxpbmVObyA9PSBmcm9tLmxpbmUgJiYgZnJvbS5jaCA+IHNwYW4udG8gfHxcclxuICAgICAgICAgICAgICAgIHNwYW4uZnJvbSA9PSBudWxsICYmIGxpbmVObyAhPSBmcm9tLmxpbmV8fFxyXG4gICAgICAgICAgICAgICAgbGluZU5vID09IHRvLmxpbmUgJiYgc3Bhbi5mcm9tID4gdG8uY2gpICYmXHJcbiAgICAgICAgICAgICAgKCFmaWx0ZXIgfHwgZmlsdGVyKHNwYW4ubWFya2VyKSkpXHJcbiAgICAgICAgICAgIGZvdW5kLnB1c2goc3Bhbi5tYXJrZXIucGFyZW50IHx8IHNwYW4ubWFya2VyKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgKytsaW5lTm87XHJcbiAgICAgIH0pO1xyXG4gICAgICByZXR1cm4gZm91bmQ7XHJcbiAgICB9LFxyXG4gICAgZ2V0QWxsTWFya3M6IGZ1bmN0aW9uKCkge1xyXG4gICAgICB2YXIgbWFya2VycyA9IFtdO1xyXG4gICAgICB0aGlzLml0ZXIoZnVuY3Rpb24obGluZSkge1xyXG4gICAgICAgIHZhciBzcHMgPSBsaW5lLm1hcmtlZFNwYW5zO1xyXG4gICAgICAgIGlmIChzcHMpIGZvciAodmFyIGkgPSAwOyBpIDwgc3BzLmxlbmd0aDsgKytpKVxyXG4gICAgICAgICAgaWYgKHNwc1tpXS5mcm9tICE9IG51bGwpIG1hcmtlcnMucHVzaChzcHNbaV0ubWFya2VyKTtcclxuICAgICAgfSk7XHJcbiAgICAgIHJldHVybiBtYXJrZXJzO1xyXG4gICAgfSxcclxuXHJcbiAgICBwb3NGcm9tSW5kZXg6IGZ1bmN0aW9uKG9mZikge1xyXG4gICAgICB2YXIgY2gsIGxpbmVObyA9IHRoaXMuZmlyc3Q7XHJcbiAgICAgIHRoaXMuaXRlcihmdW5jdGlvbihsaW5lKSB7XHJcbiAgICAgICAgdmFyIHN6ID0gbGluZS50ZXh0Lmxlbmd0aCArIDE7XHJcbiAgICAgICAgaWYgKHN6ID4gb2ZmKSB7IGNoID0gb2ZmOyByZXR1cm4gdHJ1ZTsgfVxyXG4gICAgICAgIG9mZiAtPSBzejtcclxuICAgICAgICArK2xpbmVObztcclxuICAgICAgfSk7XHJcbiAgICAgIHJldHVybiBjbGlwUG9zKHRoaXMsIFBvcyhsaW5lTm8sIGNoKSk7XHJcbiAgICB9LFxyXG4gICAgaW5kZXhGcm9tUG9zOiBmdW5jdGlvbiAoY29vcmRzKSB7XHJcbiAgICAgIGNvb3JkcyA9IGNsaXBQb3ModGhpcywgY29vcmRzKTtcclxuICAgICAgdmFyIGluZGV4ID0gY29vcmRzLmNoO1xyXG4gICAgICBpZiAoY29vcmRzLmxpbmUgPCB0aGlzLmZpcnN0IHx8IGNvb3Jkcy5jaCA8IDApIHJldHVybiAwO1xyXG4gICAgICB0aGlzLml0ZXIodGhpcy5maXJzdCwgY29vcmRzLmxpbmUsIGZ1bmN0aW9uIChsaW5lKSB7XHJcbiAgICAgICAgaW5kZXggKz0gbGluZS50ZXh0Lmxlbmd0aCArIDE7XHJcbiAgICAgIH0pO1xyXG4gICAgICByZXR1cm4gaW5kZXg7XHJcbiAgICB9LFxyXG5cclxuICAgIGNvcHk6IGZ1bmN0aW9uKGNvcHlIaXN0b3J5KSB7XHJcbiAgICAgIHZhciBkb2MgPSBuZXcgRG9jKGdldExpbmVzKHRoaXMsIHRoaXMuZmlyc3QsIHRoaXMuZmlyc3QgKyB0aGlzLnNpemUpLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm1vZGVPcHRpb24sIHRoaXMuZmlyc3QsIHRoaXMubGluZVNlcCk7XHJcbiAgICAgIGRvYy5zY3JvbGxUb3AgPSB0aGlzLnNjcm9sbFRvcDsgZG9jLnNjcm9sbExlZnQgPSB0aGlzLnNjcm9sbExlZnQ7XHJcbiAgICAgIGRvYy5zZWwgPSB0aGlzLnNlbDtcclxuICAgICAgZG9jLmV4dGVuZCA9IGZhbHNlO1xyXG4gICAgICBpZiAoY29weUhpc3RvcnkpIHtcclxuICAgICAgICBkb2MuaGlzdG9yeS51bmRvRGVwdGggPSB0aGlzLmhpc3RvcnkudW5kb0RlcHRoO1xyXG4gICAgICAgIGRvYy5zZXRIaXN0b3J5KHRoaXMuZ2V0SGlzdG9yeSgpKTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gZG9jO1xyXG4gICAgfSxcclxuXHJcbiAgICBsaW5rZWREb2M6IGZ1bmN0aW9uKG9wdGlvbnMpIHtcclxuICAgICAgaWYgKCFvcHRpb25zKSBvcHRpb25zID0ge307XHJcbiAgICAgIHZhciBmcm9tID0gdGhpcy5maXJzdCwgdG8gPSB0aGlzLmZpcnN0ICsgdGhpcy5zaXplO1xyXG4gICAgICBpZiAob3B0aW9ucy5mcm9tICE9IG51bGwgJiYgb3B0aW9ucy5mcm9tID4gZnJvbSkgZnJvbSA9IG9wdGlvbnMuZnJvbTtcclxuICAgICAgaWYgKG9wdGlvbnMudG8gIT0gbnVsbCAmJiBvcHRpb25zLnRvIDwgdG8pIHRvID0gb3B0aW9ucy50bztcclxuICAgICAgdmFyIGNvcHkgPSBuZXcgRG9jKGdldExpbmVzKHRoaXMsIGZyb20sIHRvKSwgb3B0aW9ucy5tb2RlIHx8IHRoaXMubW9kZU9wdGlvbiwgZnJvbSwgdGhpcy5saW5lU2VwKTtcclxuICAgICAgaWYgKG9wdGlvbnMuc2hhcmVkSGlzdCkgY29weS5oaXN0b3J5ID0gdGhpcy5oaXN0b3J5O1xyXG4gICAgICAodGhpcy5saW5rZWQgfHwgKHRoaXMubGlua2VkID0gW10pKS5wdXNoKHtkb2M6IGNvcHksIHNoYXJlZEhpc3Q6IG9wdGlvbnMuc2hhcmVkSGlzdH0pO1xyXG4gICAgICBjb3B5LmxpbmtlZCA9IFt7ZG9jOiB0aGlzLCBpc1BhcmVudDogdHJ1ZSwgc2hhcmVkSGlzdDogb3B0aW9ucy5zaGFyZWRIaXN0fV07XHJcbiAgICAgIGNvcHlTaGFyZWRNYXJrZXJzKGNvcHksIGZpbmRTaGFyZWRNYXJrZXJzKHRoaXMpKTtcclxuICAgICAgcmV0dXJuIGNvcHk7XHJcbiAgICB9LFxyXG4gICAgdW5saW5rRG9jOiBmdW5jdGlvbihvdGhlcikge1xyXG4gICAgICBpZiAob3RoZXIgaW5zdGFuY2VvZiBDb2RlTWlycm9yKSBvdGhlciA9IG90aGVyLmRvYztcclxuICAgICAgaWYgKHRoaXMubGlua2VkKSBmb3IgKHZhciBpID0gMDsgaSA8IHRoaXMubGlua2VkLmxlbmd0aDsgKytpKSB7XHJcbiAgICAgICAgdmFyIGxpbmsgPSB0aGlzLmxpbmtlZFtpXTtcclxuICAgICAgICBpZiAobGluay5kb2MgIT0gb3RoZXIpIGNvbnRpbnVlO1xyXG4gICAgICAgIHRoaXMubGlua2VkLnNwbGljZShpLCAxKTtcclxuICAgICAgICBvdGhlci51bmxpbmtEb2ModGhpcyk7XHJcbiAgICAgICAgZGV0YWNoU2hhcmVkTWFya2VycyhmaW5kU2hhcmVkTWFya2Vycyh0aGlzKSk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIH1cclxuICAgICAgLy8gSWYgdGhlIGhpc3RvcmllcyB3ZXJlIHNoYXJlZCwgc3BsaXQgdGhlbSBhZ2FpblxyXG4gICAgICBpZiAob3RoZXIuaGlzdG9yeSA9PSB0aGlzLmhpc3RvcnkpIHtcclxuICAgICAgICB2YXIgc3BsaXRJZHMgPSBbb3RoZXIuaWRdO1xyXG4gICAgICAgIGxpbmtlZERvY3Mob3RoZXIsIGZ1bmN0aW9uKGRvYykge3NwbGl0SWRzLnB1c2goZG9jLmlkKTt9LCB0cnVlKTtcclxuICAgICAgICBvdGhlci5oaXN0b3J5ID0gbmV3IEhpc3RvcnkobnVsbCk7XHJcbiAgICAgICAgb3RoZXIuaGlzdG9yeS5kb25lID0gY29weUhpc3RvcnlBcnJheSh0aGlzLmhpc3RvcnkuZG9uZSwgc3BsaXRJZHMpO1xyXG4gICAgICAgIG90aGVyLmhpc3RvcnkudW5kb25lID0gY29weUhpc3RvcnlBcnJheSh0aGlzLmhpc3RvcnkudW5kb25lLCBzcGxpdElkcyk7XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpdGVyTGlua2VkRG9jczogZnVuY3Rpb24oZikge2xpbmtlZERvY3ModGhpcywgZik7fSxcclxuXHJcbiAgICBnZXRNb2RlOiBmdW5jdGlvbigpIHtyZXR1cm4gdGhpcy5tb2RlO30sXHJcbiAgICBnZXRFZGl0b3I6IGZ1bmN0aW9uKCkge3JldHVybiB0aGlzLmNtO30sXHJcblxyXG4gICAgc3BsaXRMaW5lczogZnVuY3Rpb24oc3RyKSB7XHJcbiAgICAgIGlmICh0aGlzLmxpbmVTZXApIHJldHVybiBzdHIuc3BsaXQodGhpcy5saW5lU2VwKTtcclxuICAgICAgcmV0dXJuIHNwbGl0TGluZXNBdXRvKHN0cik7XHJcbiAgICB9LFxyXG4gICAgbGluZVNlcGFyYXRvcjogZnVuY3Rpb24oKSB7IHJldHVybiB0aGlzLmxpbmVTZXAgfHwgXCJcXG5cIjsgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBQdWJsaWMgYWxpYXMuXHJcbiAgRG9jLnByb3RvdHlwZS5lYWNoTGluZSA9IERvYy5wcm90b3R5cGUuaXRlcjtcclxuXHJcbiAgLy8gU2V0IHVwIG1ldGhvZHMgb24gQ29kZU1pcnJvcidzIHByb3RvdHlwZSB0byByZWRpcmVjdCB0byB0aGUgZWRpdG9yJ3MgZG9jdW1lbnQuXHJcbiAgdmFyIGRvbnREZWxlZ2F0ZSA9IFwiaXRlciBpbnNlcnQgcmVtb3ZlIGNvcHkgZ2V0RWRpdG9yIGNvbnN0cnVjdG9yXCIuc3BsaXQoXCIgXCIpO1xyXG4gIGZvciAodmFyIHByb3AgaW4gRG9jLnByb3RvdHlwZSkgaWYgKERvYy5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkocHJvcCkgJiYgaW5kZXhPZihkb250RGVsZWdhdGUsIHByb3ApIDwgMClcclxuICAgIENvZGVNaXJyb3IucHJvdG90eXBlW3Byb3BdID0gKGZ1bmN0aW9uKG1ldGhvZCkge1xyXG4gICAgICByZXR1cm4gZnVuY3Rpb24oKSB7cmV0dXJuIG1ldGhvZC5hcHBseSh0aGlzLmRvYywgYXJndW1lbnRzKTt9O1xyXG4gICAgfSkoRG9jLnByb3RvdHlwZVtwcm9wXSk7XHJcblxyXG4gIGV2ZW50TWl4aW4oRG9jKTtcclxuXHJcbiAgLy8gQ2FsbCBmIGZvciBhbGwgbGlua2VkIGRvY3VtZW50cy5cclxuICBmdW5jdGlvbiBsaW5rZWREb2NzKGRvYywgZiwgc2hhcmVkSGlzdE9ubHkpIHtcclxuICAgIGZ1bmN0aW9uIHByb3BhZ2F0ZShkb2MsIHNraXAsIHNoYXJlZEhpc3QpIHtcclxuICAgICAgaWYgKGRvYy5saW5rZWQpIGZvciAodmFyIGkgPSAwOyBpIDwgZG9jLmxpbmtlZC5sZW5ndGg7ICsraSkge1xyXG4gICAgICAgIHZhciByZWwgPSBkb2MubGlua2VkW2ldO1xyXG4gICAgICAgIGlmIChyZWwuZG9jID09IHNraXApIGNvbnRpbnVlO1xyXG4gICAgICAgIHZhciBzaGFyZWQgPSBzaGFyZWRIaXN0ICYmIHJlbC5zaGFyZWRIaXN0O1xyXG4gICAgICAgIGlmIChzaGFyZWRIaXN0T25seSAmJiAhc2hhcmVkKSBjb250aW51ZTtcclxuICAgICAgICBmKHJlbC5kb2MsIHNoYXJlZCk7XHJcbiAgICAgICAgcHJvcGFnYXRlKHJlbC5kb2MsIGRvYywgc2hhcmVkKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgcHJvcGFnYXRlKGRvYywgbnVsbCwgdHJ1ZSk7XHJcbiAgfVxyXG5cclxuICAvLyBBdHRhY2ggYSBkb2N1bWVudCB0byBhbiBlZGl0b3IuXHJcbiAgZnVuY3Rpb24gYXR0YWNoRG9jKGNtLCBkb2MpIHtcclxuICAgIGlmIChkb2MuY20pIHRocm93IG5ldyBFcnJvcihcIlRoaXMgZG9jdW1lbnQgaXMgYWxyZWFkeSBpbiB1c2UuXCIpO1xyXG4gICAgY20uZG9jID0gZG9jO1xyXG4gICAgZG9jLmNtID0gY207XHJcbiAgICBlc3RpbWF0ZUxpbmVIZWlnaHRzKGNtKTtcclxuICAgIGxvYWRNb2RlKGNtKTtcclxuICAgIGlmICghY20ub3B0aW9ucy5saW5lV3JhcHBpbmcpIGZpbmRNYXhMaW5lKGNtKTtcclxuICAgIGNtLm9wdGlvbnMubW9kZSA9IGRvYy5tb2RlT3B0aW9uO1xyXG4gICAgcmVnQ2hhbmdlKGNtKTtcclxuICB9XHJcblxyXG4gIC8vIExJTkUgVVRJTElUSUVTXHJcblxyXG4gIC8vIEZpbmQgdGhlIGxpbmUgb2JqZWN0IGNvcnJlc3BvbmRpbmcgdG8gdGhlIGdpdmVuIGxpbmUgbnVtYmVyLlxyXG4gIGZ1bmN0aW9uIGdldExpbmUoZG9jLCBuKSB7XHJcbiAgICBuIC09IGRvYy5maXJzdDtcclxuICAgIGlmIChuIDwgMCB8fCBuID49IGRvYy5zaXplKSB0aHJvdyBuZXcgRXJyb3IoXCJUaGVyZSBpcyBubyBsaW5lIFwiICsgKG4gKyBkb2MuZmlyc3QpICsgXCIgaW4gdGhlIGRvY3VtZW50LlwiKTtcclxuICAgIGZvciAodmFyIGNodW5rID0gZG9jOyAhY2h1bmsubGluZXM7KSB7XHJcbiAgICAgIGZvciAodmFyIGkgPSAwOzsgKytpKSB7XHJcbiAgICAgICAgdmFyIGNoaWxkID0gY2h1bmsuY2hpbGRyZW5baV0sIHN6ID0gY2hpbGQuY2h1bmtTaXplKCk7XHJcbiAgICAgICAgaWYgKG4gPCBzeikgeyBjaHVuayA9IGNoaWxkOyBicmVhazsgfVxyXG4gICAgICAgIG4gLT0gc3o7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIHJldHVybiBjaHVuay5saW5lc1tuXTtcclxuICB9XHJcblxyXG4gIC8vIEdldCB0aGUgcGFydCBvZiBhIGRvY3VtZW50IGJldHdlZW4gdHdvIHBvc2l0aW9ucywgYXMgYW4gYXJyYXkgb2ZcclxuICAvLyBzdHJpbmdzLlxyXG4gIGZ1bmN0aW9uIGdldEJldHdlZW4oZG9jLCBzdGFydCwgZW5kKSB7XHJcbiAgICB2YXIgb3V0ID0gW10sIG4gPSBzdGFydC5saW5lO1xyXG4gICAgZG9jLml0ZXIoc3RhcnQubGluZSwgZW5kLmxpbmUgKyAxLCBmdW5jdGlvbihsaW5lKSB7XHJcbiAgICAgIHZhciB0ZXh0ID0gbGluZS50ZXh0O1xyXG4gICAgICBpZiAobiA9PSBlbmQubGluZSkgdGV4dCA9IHRleHQuc2xpY2UoMCwgZW5kLmNoKTtcclxuICAgICAgaWYgKG4gPT0gc3RhcnQubGluZSkgdGV4dCA9IHRleHQuc2xpY2Uoc3RhcnQuY2gpO1xyXG4gICAgICBvdXQucHVzaCh0ZXh0KTtcclxuICAgICAgKytuO1xyXG4gICAgfSk7XHJcbiAgICByZXR1cm4gb3V0O1xyXG4gIH1cclxuICAvLyBHZXQgdGhlIGxpbmVzIGJldHdlZW4gZnJvbSBhbmQgdG8sIGFzIGFycmF5IG9mIHN0cmluZ3MuXHJcbiAgZnVuY3Rpb24gZ2V0TGluZXMoZG9jLCBmcm9tLCB0bykge1xyXG4gICAgdmFyIG91dCA9IFtdO1xyXG4gICAgZG9jLml0ZXIoZnJvbSwgdG8sIGZ1bmN0aW9uKGxpbmUpIHsgb3V0LnB1c2gobGluZS50ZXh0KTsgfSk7XHJcbiAgICByZXR1cm4gb3V0O1xyXG4gIH1cclxuXHJcbiAgLy8gVXBkYXRlIHRoZSBoZWlnaHQgb2YgYSBsaW5lLCBwcm9wYWdhdGluZyB0aGUgaGVpZ2h0IGNoYW5nZVxyXG4gIC8vIHVwd2FyZHMgdG8gcGFyZW50IG5vZGVzLlxyXG4gIGZ1bmN0aW9uIHVwZGF0ZUxpbmVIZWlnaHQobGluZSwgaGVpZ2h0KSB7XHJcbiAgICB2YXIgZGlmZiA9IGhlaWdodCAtIGxpbmUuaGVpZ2h0O1xyXG4gICAgaWYgKGRpZmYpIGZvciAodmFyIG4gPSBsaW5lOyBuOyBuID0gbi5wYXJlbnQpIG4uaGVpZ2h0ICs9IGRpZmY7XHJcbiAgfVxyXG5cclxuICAvLyBHaXZlbiBhIGxpbmUgb2JqZWN0LCBmaW5kIGl0cyBsaW5lIG51bWJlciBieSB3YWxraW5nIHVwIHRocm91Z2hcclxuICAvLyBpdHMgcGFyZW50IGxpbmtzLlxyXG4gIGZ1bmN0aW9uIGxpbmVObyhsaW5lKSB7XHJcbiAgICBpZiAobGluZS5wYXJlbnQgPT0gbnVsbCkgcmV0dXJuIG51bGw7XHJcbiAgICB2YXIgY3VyID0gbGluZS5wYXJlbnQsIG5vID0gaW5kZXhPZihjdXIubGluZXMsIGxpbmUpO1xyXG4gICAgZm9yICh2YXIgY2h1bmsgPSBjdXIucGFyZW50OyBjaHVuazsgY3VyID0gY2h1bmssIGNodW5rID0gY2h1bmsucGFyZW50KSB7XHJcbiAgICAgIGZvciAodmFyIGkgPSAwOzsgKytpKSB7XHJcbiAgICAgICAgaWYgKGNodW5rLmNoaWxkcmVuW2ldID09IGN1cikgYnJlYWs7XHJcbiAgICAgICAgbm8gKz0gY2h1bmsuY2hpbGRyZW5baV0uY2h1bmtTaXplKCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIHJldHVybiBubyArIGN1ci5maXJzdDtcclxuICB9XHJcblxyXG4gIC8vIEZpbmQgdGhlIGxpbmUgYXQgdGhlIGdpdmVuIHZlcnRpY2FsIHBvc2l0aW9uLCB1c2luZyB0aGUgaGVpZ2h0XHJcbiAgLy8gaW5mb3JtYXRpb24gaW4gdGhlIGRvY3VtZW50IHRyZWUuXHJcbiAgZnVuY3Rpb24gbGluZUF0SGVpZ2h0KGNodW5rLCBoKSB7XHJcbiAgICB2YXIgbiA9IGNodW5rLmZpcnN0O1xyXG4gICAgb3V0ZXI6IGRvIHtcclxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBjaHVuay5jaGlsZHJlbi5sZW5ndGg7ICsraSkge1xyXG4gICAgICAgIHZhciBjaGlsZCA9IGNodW5rLmNoaWxkcmVuW2ldLCBjaCA9IGNoaWxkLmhlaWdodDtcclxuICAgICAgICBpZiAoaCA8IGNoKSB7IGNodW5rID0gY2hpbGQ7IGNvbnRpbnVlIG91dGVyOyB9XHJcbiAgICAgICAgaCAtPSBjaDtcclxuICAgICAgICBuICs9IGNoaWxkLmNodW5rU2l6ZSgpO1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiBuO1xyXG4gICAgfSB3aGlsZSAoIWNodW5rLmxpbmVzKTtcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgY2h1bmsubGluZXMubGVuZ3RoOyArK2kpIHtcclxuICAgICAgdmFyIGxpbmUgPSBjaHVuay5saW5lc1tpXSwgbGggPSBsaW5lLmhlaWdodDtcclxuICAgICAgaWYgKGggPCBsaCkgYnJlYWs7XHJcbiAgICAgIGggLT0gbGg7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gbiArIGk7XHJcbiAgfVxyXG5cclxuXHJcbiAgLy8gRmluZCB0aGUgaGVpZ2h0IGFib3ZlIHRoZSBnaXZlbiBsaW5lLlxyXG4gIGZ1bmN0aW9uIGhlaWdodEF0TGluZShsaW5lT2JqKSB7XHJcbiAgICBsaW5lT2JqID0gdmlzdWFsTGluZShsaW5lT2JqKTtcclxuXHJcbiAgICB2YXIgaCA9IDAsIGNodW5rID0gbGluZU9iai5wYXJlbnQ7XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGNodW5rLmxpbmVzLmxlbmd0aDsgKytpKSB7XHJcbiAgICAgIHZhciBsaW5lID0gY2h1bmsubGluZXNbaV07XHJcbiAgICAgIGlmIChsaW5lID09IGxpbmVPYmopIGJyZWFrO1xyXG4gICAgICBlbHNlIGggKz0gbGluZS5oZWlnaHQ7XHJcbiAgICB9XHJcbiAgICBmb3IgKHZhciBwID0gY2h1bmsucGFyZW50OyBwOyBjaHVuayA9IHAsIHAgPSBjaHVuay5wYXJlbnQpIHtcclxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBwLmNoaWxkcmVuLmxlbmd0aDsgKytpKSB7XHJcbiAgICAgICAgdmFyIGN1ciA9IHAuY2hpbGRyZW5baV07XHJcbiAgICAgICAgaWYgKGN1ciA9PSBjaHVuaykgYnJlYWs7XHJcbiAgICAgICAgZWxzZSBoICs9IGN1ci5oZWlnaHQ7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIHJldHVybiBoO1xyXG4gIH1cclxuXHJcbiAgLy8gR2V0IHRoZSBiaWRpIG9yZGVyaW5nIGZvciB0aGUgZ2l2ZW4gbGluZSAoYW5kIGNhY2hlIGl0KS4gUmV0dXJuc1xyXG4gIC8vIGZhbHNlIGZvciBsaW5lcyB0aGF0IGFyZSBmdWxseSBsZWZ0LXRvLXJpZ2h0LCBhbmQgYW4gYXJyYXkgb2ZcclxuICAvLyBCaWRpU3BhbiBvYmplY3RzIG90aGVyd2lzZS5cclxuICBmdW5jdGlvbiBnZXRPcmRlcihsaW5lKSB7XHJcbiAgICB2YXIgb3JkZXIgPSBsaW5lLm9yZGVyO1xyXG4gICAgaWYgKG9yZGVyID09IG51bGwpIG9yZGVyID0gbGluZS5vcmRlciA9IGJpZGlPcmRlcmluZyhsaW5lLnRleHQpO1xyXG4gICAgcmV0dXJuIG9yZGVyO1xyXG4gIH1cclxuXHJcbiAgLy8gSElTVE9SWVxyXG5cclxuICBmdW5jdGlvbiBIaXN0b3J5KHN0YXJ0R2VuKSB7XHJcbiAgICAvLyBBcnJheXMgb2YgY2hhbmdlIGV2ZW50cyBhbmQgc2VsZWN0aW9ucy4gRG9pbmcgc29tZXRoaW5nIGFkZHMgYW5cclxuICAgIC8vIGV2ZW50IHRvIGRvbmUgYW5kIGNsZWFycyB1bmRvLiBVbmRvaW5nIG1vdmVzIGV2ZW50cyBmcm9tIGRvbmVcclxuICAgIC8vIHRvIHVuZG9uZSwgcmVkb2luZyBtb3ZlcyB0aGVtIGluIHRoZSBvdGhlciBkaXJlY3Rpb24uXHJcbiAgICB0aGlzLmRvbmUgPSBbXTsgdGhpcy51bmRvbmUgPSBbXTtcclxuICAgIHRoaXMudW5kb0RlcHRoID0gSW5maW5pdHk7XHJcbiAgICAvLyBVc2VkIHRvIHRyYWNrIHdoZW4gY2hhbmdlcyBjYW4gYmUgbWVyZ2VkIGludG8gYSBzaW5nbGUgdW5kb1xyXG4gICAgLy8gZXZlbnRcclxuICAgIHRoaXMubGFzdE1vZFRpbWUgPSB0aGlzLmxhc3RTZWxUaW1lID0gMDtcclxuICAgIHRoaXMubGFzdE9wID0gdGhpcy5sYXN0U2VsT3AgPSBudWxsO1xyXG4gICAgdGhpcy5sYXN0T3JpZ2luID0gdGhpcy5sYXN0U2VsT3JpZ2luID0gbnVsbDtcclxuICAgIC8vIFVzZWQgYnkgdGhlIGlzQ2xlYW4oKSBtZXRob2RcclxuICAgIHRoaXMuZ2VuZXJhdGlvbiA9IHRoaXMubWF4R2VuZXJhdGlvbiA9IHN0YXJ0R2VuIHx8IDE7XHJcbiAgfVxyXG5cclxuICAvLyBDcmVhdGUgYSBoaXN0b3J5IGNoYW5nZSBldmVudCBmcm9tIGFuIHVwZGF0ZURvYy1zdHlsZSBjaGFuZ2VcclxuICAvLyBvYmplY3QuXHJcbiAgZnVuY3Rpb24gaGlzdG9yeUNoYW5nZUZyb21DaGFuZ2UoZG9jLCBjaGFuZ2UpIHtcclxuICAgIHZhciBoaXN0Q2hhbmdlID0ge2Zyb206IGNvcHlQb3MoY2hhbmdlLmZyb20pLCB0bzogY2hhbmdlRW5kKGNoYW5nZSksIHRleHQ6IGdldEJldHdlZW4oZG9jLCBjaGFuZ2UuZnJvbSwgY2hhbmdlLnRvKX07XHJcbiAgICBhdHRhY2hMb2NhbFNwYW5zKGRvYywgaGlzdENoYW5nZSwgY2hhbmdlLmZyb20ubGluZSwgY2hhbmdlLnRvLmxpbmUgKyAxKTtcclxuICAgIGxpbmtlZERvY3MoZG9jLCBmdW5jdGlvbihkb2MpIHthdHRhY2hMb2NhbFNwYW5zKGRvYywgaGlzdENoYW5nZSwgY2hhbmdlLmZyb20ubGluZSwgY2hhbmdlLnRvLmxpbmUgKyAxKTt9LCB0cnVlKTtcclxuICAgIHJldHVybiBoaXN0Q2hhbmdlO1xyXG4gIH1cclxuXHJcbiAgLy8gUG9wIGFsbCBzZWxlY3Rpb24gZXZlbnRzIG9mZiB0aGUgZW5kIG9mIGEgaGlzdG9yeSBhcnJheS4gU3RvcCBhdFxyXG4gIC8vIGEgY2hhbmdlIGV2ZW50LlxyXG4gIGZ1bmN0aW9uIGNsZWFyU2VsZWN0aW9uRXZlbnRzKGFycmF5KSB7XHJcbiAgICB3aGlsZSAoYXJyYXkubGVuZ3RoKSB7XHJcbiAgICAgIHZhciBsYXN0ID0gbHN0KGFycmF5KTtcclxuICAgICAgaWYgKGxhc3QucmFuZ2VzKSBhcnJheS5wb3AoKTtcclxuICAgICAgZWxzZSBicmVhaztcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIEZpbmQgdGhlIHRvcCBjaGFuZ2UgZXZlbnQgaW4gdGhlIGhpc3RvcnkuIFBvcCBvZmYgc2VsZWN0aW9uXHJcbiAgLy8gZXZlbnRzIHRoYXQgYXJlIGluIHRoZSB3YXkuXHJcbiAgZnVuY3Rpb24gbGFzdENoYW5nZUV2ZW50KGhpc3QsIGZvcmNlKSB7XHJcbiAgICBpZiAoZm9yY2UpIHtcclxuICAgICAgY2xlYXJTZWxlY3Rpb25FdmVudHMoaGlzdC5kb25lKTtcclxuICAgICAgcmV0dXJuIGxzdChoaXN0LmRvbmUpO1xyXG4gICAgfSBlbHNlIGlmIChoaXN0LmRvbmUubGVuZ3RoICYmICFsc3QoaGlzdC5kb25lKS5yYW5nZXMpIHtcclxuICAgICAgcmV0dXJuIGxzdChoaXN0LmRvbmUpO1xyXG4gICAgfSBlbHNlIGlmIChoaXN0LmRvbmUubGVuZ3RoID4gMSAmJiAhaGlzdC5kb25lW2hpc3QuZG9uZS5sZW5ndGggLSAyXS5yYW5nZXMpIHtcclxuICAgICAgaGlzdC5kb25lLnBvcCgpO1xyXG4gICAgICByZXR1cm4gbHN0KGhpc3QuZG9uZSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZWdpc3RlciBhIGNoYW5nZSBpbiB0aGUgaGlzdG9yeS4gTWVyZ2VzIGNoYW5nZXMgdGhhdCBhcmUgd2l0aGluXHJcbiAgLy8gYSBzaW5nbGUgb3BlcmF0aW9uLCBvcmUgYXJlIGNsb3NlIHRvZ2V0aGVyIHdpdGggYW4gb3JpZ2luIHRoYXRcclxuICAvLyBhbGxvd3MgbWVyZ2luZyAoc3RhcnRpbmcgd2l0aCBcIitcIikgaW50byBhIHNpbmdsZSBldmVudC5cclxuICBmdW5jdGlvbiBhZGRDaGFuZ2VUb0hpc3RvcnkoZG9jLCBjaGFuZ2UsIHNlbEFmdGVyLCBvcElkKSB7XHJcbiAgICB2YXIgaGlzdCA9IGRvYy5oaXN0b3J5O1xyXG4gICAgaGlzdC51bmRvbmUubGVuZ3RoID0gMDtcclxuICAgIHZhciB0aW1lID0gK25ldyBEYXRlLCBjdXI7XHJcblxyXG4gICAgaWYgKChoaXN0Lmxhc3RPcCA9PSBvcElkIHx8XHJcbiAgICAgICAgIGhpc3QubGFzdE9yaWdpbiA9PSBjaGFuZ2Uub3JpZ2luICYmIGNoYW5nZS5vcmlnaW4gJiZcclxuICAgICAgICAgKChjaGFuZ2Uub3JpZ2luLmNoYXJBdCgwKSA9PSBcIitcIiAmJiBkb2MuY20gJiYgaGlzdC5sYXN0TW9kVGltZSA+IHRpbWUgLSBkb2MuY20ub3B0aW9ucy5oaXN0b3J5RXZlbnREZWxheSkgfHxcclxuICAgICAgICAgIGNoYW5nZS5vcmlnaW4uY2hhckF0KDApID09IFwiKlwiKSkgJiZcclxuICAgICAgICAoY3VyID0gbGFzdENoYW5nZUV2ZW50KGhpc3QsIGhpc3QubGFzdE9wID09IG9wSWQpKSkge1xyXG4gICAgICAvLyBNZXJnZSB0aGlzIGNoYW5nZSBpbnRvIHRoZSBsYXN0IGV2ZW50XHJcbiAgICAgIHZhciBsYXN0ID0gbHN0KGN1ci5jaGFuZ2VzKTtcclxuICAgICAgaWYgKGNtcChjaGFuZ2UuZnJvbSwgY2hhbmdlLnRvKSA9PSAwICYmIGNtcChjaGFuZ2UuZnJvbSwgbGFzdC50bykgPT0gMCkge1xyXG4gICAgICAgIC8vIE9wdGltaXplZCBjYXNlIGZvciBzaW1wbGUgaW5zZXJ0aW9uIC0tIGRvbid0IHdhbnQgdG8gYWRkXHJcbiAgICAgICAgLy8gbmV3IGNoYW5nZXNldHMgZm9yIGV2ZXJ5IGNoYXJhY3RlciB0eXBlZFxyXG4gICAgICAgIGxhc3QudG8gPSBjaGFuZ2VFbmQoY2hhbmdlKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBBZGQgbmV3IHN1Yi1ldmVudFxyXG4gICAgICAgIGN1ci5jaGFuZ2VzLnB1c2goaGlzdG9yeUNoYW5nZUZyb21DaGFuZ2UoZG9jLCBjaGFuZ2UpKTtcclxuICAgICAgfVxyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gQ2FuIG5vdCBiZSBtZXJnZWQsIHN0YXJ0IGEgbmV3IGV2ZW50LlxyXG4gICAgICB2YXIgYmVmb3JlID0gbHN0KGhpc3QuZG9uZSk7XHJcbiAgICAgIGlmICghYmVmb3JlIHx8ICFiZWZvcmUucmFuZ2VzKVxyXG4gICAgICAgIHB1c2hTZWxlY3Rpb25Ub0hpc3RvcnkoZG9jLnNlbCwgaGlzdC5kb25lKTtcclxuICAgICAgY3VyID0ge2NoYW5nZXM6IFtoaXN0b3J5Q2hhbmdlRnJvbUNoYW5nZShkb2MsIGNoYW5nZSldLFxyXG4gICAgICAgICAgICAgZ2VuZXJhdGlvbjogaGlzdC5nZW5lcmF0aW9ufTtcclxuICAgICAgaGlzdC5kb25lLnB1c2goY3VyKTtcclxuICAgICAgd2hpbGUgKGhpc3QuZG9uZS5sZW5ndGggPiBoaXN0LnVuZG9EZXB0aCkge1xyXG4gICAgICAgIGhpc3QuZG9uZS5zaGlmdCgpO1xyXG4gICAgICAgIGlmICghaGlzdC5kb25lWzBdLnJhbmdlcykgaGlzdC5kb25lLnNoaWZ0KCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIGhpc3QuZG9uZS5wdXNoKHNlbEFmdGVyKTtcclxuICAgIGhpc3QuZ2VuZXJhdGlvbiA9ICsraGlzdC5tYXhHZW5lcmF0aW9uO1xyXG4gICAgaGlzdC5sYXN0TW9kVGltZSA9IGhpc3QubGFzdFNlbFRpbWUgPSB0aW1lO1xyXG4gICAgaGlzdC5sYXN0T3AgPSBoaXN0Lmxhc3RTZWxPcCA9IG9wSWQ7XHJcbiAgICBoaXN0Lmxhc3RPcmlnaW4gPSBoaXN0Lmxhc3RTZWxPcmlnaW4gPSBjaGFuZ2Uub3JpZ2luO1xyXG5cclxuICAgIGlmICghbGFzdCkgc2lnbmFsKGRvYywgXCJoaXN0b3J5QWRkZWRcIik7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBzZWxlY3Rpb25FdmVudENhbkJlTWVyZ2VkKGRvYywgb3JpZ2luLCBwcmV2LCBzZWwpIHtcclxuICAgIHZhciBjaCA9IG9yaWdpbi5jaGFyQXQoMCk7XHJcbiAgICByZXR1cm4gY2ggPT0gXCIqXCIgfHxcclxuICAgICAgY2ggPT0gXCIrXCIgJiZcclxuICAgICAgcHJldi5yYW5nZXMubGVuZ3RoID09IHNlbC5yYW5nZXMubGVuZ3RoICYmXHJcbiAgICAgIHByZXYuc29tZXRoaW5nU2VsZWN0ZWQoKSA9PSBzZWwuc29tZXRoaW5nU2VsZWN0ZWQoKSAmJlxyXG4gICAgICBuZXcgRGF0ZSAtIGRvYy5oaXN0b3J5Lmxhc3RTZWxUaW1lIDw9IChkb2MuY20gPyBkb2MuY20ub3B0aW9ucy5oaXN0b3J5RXZlbnREZWxheSA6IDUwMCk7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsZWQgd2hlbmV2ZXIgdGhlIHNlbGVjdGlvbiBjaGFuZ2VzLCBzZXRzIHRoZSBuZXcgc2VsZWN0aW9uIGFzXHJcbiAgLy8gdGhlIHBlbmRpbmcgc2VsZWN0aW9uIGluIHRoZSBoaXN0b3J5LCBhbmQgcHVzaGVzIHRoZSBvbGQgcGVuZGluZ1xyXG4gIC8vIHNlbGVjdGlvbiBpbnRvIHRoZSAnZG9uZScgYXJyYXkgd2hlbiBpdCB3YXMgc2lnbmlmaWNhbnRseVxyXG4gIC8vIGRpZmZlcmVudCAoaW4gbnVtYmVyIG9mIHNlbGVjdGVkIHJhbmdlcywgZW1wdGluZXNzLCBvciB0aW1lKS5cclxuICBmdW5jdGlvbiBhZGRTZWxlY3Rpb25Ub0hpc3RvcnkoZG9jLCBzZWwsIG9wSWQsIG9wdGlvbnMpIHtcclxuICAgIHZhciBoaXN0ID0gZG9jLmhpc3RvcnksIG9yaWdpbiA9IG9wdGlvbnMgJiYgb3B0aW9ucy5vcmlnaW47XHJcblxyXG4gICAgLy8gQSBuZXcgZXZlbnQgaXMgc3RhcnRlZCB3aGVuIHRoZSBwcmV2aW91cyBvcmlnaW4gZG9lcyBub3QgbWF0Y2hcclxuICAgIC8vIHRoZSBjdXJyZW50LCBvciB0aGUgb3JpZ2lucyBkb24ndCBhbGxvdyBtYXRjaGluZy4gT3JpZ2luc1xyXG4gICAgLy8gc3RhcnRpbmcgd2l0aCAqIGFyZSBhbHdheXMgbWVyZ2VkLCB0aG9zZSBzdGFydGluZyB3aXRoICsgYXJlXHJcbiAgICAvLyBtZXJnZWQgd2hlbiBzaW1pbGFyIGFuZCBjbG9zZSB0b2dldGhlciBpbiB0aW1lLlxyXG4gICAgaWYgKG9wSWQgPT0gaGlzdC5sYXN0U2VsT3AgfHxcclxuICAgICAgICAob3JpZ2luICYmIGhpc3QubGFzdFNlbE9yaWdpbiA9PSBvcmlnaW4gJiZcclxuICAgICAgICAgKGhpc3QubGFzdE1vZFRpbWUgPT0gaGlzdC5sYXN0U2VsVGltZSAmJiBoaXN0Lmxhc3RPcmlnaW4gPT0gb3JpZ2luIHx8XHJcbiAgICAgICAgICBzZWxlY3Rpb25FdmVudENhbkJlTWVyZ2VkKGRvYywgb3JpZ2luLCBsc3QoaGlzdC5kb25lKSwgc2VsKSkpKVxyXG4gICAgICBoaXN0LmRvbmVbaGlzdC5kb25lLmxlbmd0aCAtIDFdID0gc2VsO1xyXG4gICAgZWxzZVxyXG4gICAgICBwdXNoU2VsZWN0aW9uVG9IaXN0b3J5KHNlbCwgaGlzdC5kb25lKTtcclxuXHJcbiAgICBoaXN0Lmxhc3RTZWxUaW1lID0gK25ldyBEYXRlO1xyXG4gICAgaGlzdC5sYXN0U2VsT3JpZ2luID0gb3JpZ2luO1xyXG4gICAgaGlzdC5sYXN0U2VsT3AgPSBvcElkO1xyXG4gICAgaWYgKG9wdGlvbnMgJiYgb3B0aW9ucy5jbGVhclJlZG8gIT09IGZhbHNlKVxyXG4gICAgICBjbGVhclNlbGVjdGlvbkV2ZW50cyhoaXN0LnVuZG9uZSk7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBwdXNoU2VsZWN0aW9uVG9IaXN0b3J5KHNlbCwgZGVzdCkge1xyXG4gICAgdmFyIHRvcCA9IGxzdChkZXN0KTtcclxuICAgIGlmICghKHRvcCAmJiB0b3AucmFuZ2VzICYmIHRvcC5lcXVhbHMoc2VsKSkpXHJcbiAgICAgIGRlc3QucHVzaChzZWwpO1xyXG4gIH1cclxuXHJcbiAgLy8gVXNlZCB0byBzdG9yZSBtYXJrZWQgc3BhbiBpbmZvcm1hdGlvbiBpbiB0aGUgaGlzdG9yeS5cclxuICBmdW5jdGlvbiBhdHRhY2hMb2NhbFNwYW5zKGRvYywgY2hhbmdlLCBmcm9tLCB0bykge1xyXG4gICAgdmFyIGV4aXN0aW5nID0gY2hhbmdlW1wic3BhbnNfXCIgKyBkb2MuaWRdLCBuID0gMDtcclxuICAgIGRvYy5pdGVyKE1hdGgubWF4KGRvYy5maXJzdCwgZnJvbSksIE1hdGgubWluKGRvYy5maXJzdCArIGRvYy5zaXplLCB0byksIGZ1bmN0aW9uKGxpbmUpIHtcclxuICAgICAgaWYgKGxpbmUubWFya2VkU3BhbnMpXHJcbiAgICAgICAgKGV4aXN0aW5nIHx8IChleGlzdGluZyA9IGNoYW5nZVtcInNwYW5zX1wiICsgZG9jLmlkXSA9IHt9KSlbbl0gPSBsaW5lLm1hcmtlZFNwYW5zO1xyXG4gICAgICArK247XHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIC8vIFdoZW4gdW4vcmUtZG9pbmcgcmVzdG9yZXMgdGV4dCBjb250YWluaW5nIG1hcmtlZCBzcGFucywgdGhvc2VcclxuICAvLyB0aGF0IGhhdmUgYmVlbiBleHBsaWNpdGx5IGNsZWFyZWQgc2hvdWxkIG5vdCBiZSByZXN0b3JlZC5cclxuICBmdW5jdGlvbiByZW1vdmVDbGVhcmVkU3BhbnMoc3BhbnMpIHtcclxuICAgIGlmICghc3BhbnMpIHJldHVybiBudWxsO1xyXG4gICAgZm9yICh2YXIgaSA9IDAsIG91dDsgaSA8IHNwYW5zLmxlbmd0aDsgKytpKSB7XHJcbiAgICAgIGlmIChzcGFuc1tpXS5tYXJrZXIuZXhwbGljaXRseUNsZWFyZWQpIHsgaWYgKCFvdXQpIG91dCA9IHNwYW5zLnNsaWNlKDAsIGkpOyB9XHJcbiAgICAgIGVsc2UgaWYgKG91dCkgb3V0LnB1c2goc3BhbnNbaV0pO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuICFvdXQgPyBzcGFucyA6IG91dC5sZW5ndGggPyBvdXQgOiBudWxsO1xyXG4gIH1cclxuXHJcbiAgLy8gUmV0cmlldmUgYW5kIGZpbHRlciB0aGUgb2xkIG1hcmtlZCBzcGFucyBzdG9yZWQgaW4gYSBjaGFuZ2UgZXZlbnQuXHJcbiAgZnVuY3Rpb24gZ2V0T2xkU3BhbnMoZG9jLCBjaGFuZ2UpIHtcclxuICAgIHZhciBmb3VuZCA9IGNoYW5nZVtcInNwYW5zX1wiICsgZG9jLmlkXTtcclxuICAgIGlmICghZm91bmQpIHJldHVybiBudWxsO1xyXG4gICAgZm9yICh2YXIgaSA9IDAsIG53ID0gW107IGkgPCBjaGFuZ2UudGV4dC5sZW5ndGg7ICsraSlcclxuICAgICAgbncucHVzaChyZW1vdmVDbGVhcmVkU3BhbnMoZm91bmRbaV0pKTtcclxuICAgIHJldHVybiBudztcclxuICB9XHJcblxyXG4gIC8vIFVzZWQgYm90aCB0byBwcm92aWRlIGEgSlNPTi1zYWZlIG9iamVjdCBpbiAuZ2V0SGlzdG9yeSwgYW5kLCB3aGVuXHJcbiAgLy8gZGV0YWNoaW5nIGEgZG9jdW1lbnQsIHRvIHNwbGl0IHRoZSBoaXN0b3J5IGluIHR3b1xyXG4gIGZ1bmN0aW9uIGNvcHlIaXN0b3J5QXJyYXkoZXZlbnRzLCBuZXdHcm91cCwgaW5zdGFudGlhdGVTZWwpIHtcclxuICAgIGZvciAodmFyIGkgPSAwLCBjb3B5ID0gW107IGkgPCBldmVudHMubGVuZ3RoOyArK2kpIHtcclxuICAgICAgdmFyIGV2ZW50ID0gZXZlbnRzW2ldO1xyXG4gICAgICBpZiAoZXZlbnQucmFuZ2VzKSB7XHJcbiAgICAgICAgY29weS5wdXNoKGluc3RhbnRpYXRlU2VsID8gU2VsZWN0aW9uLnByb3RvdHlwZS5kZWVwQ29weS5jYWxsKGV2ZW50KSA6IGV2ZW50KTtcclxuICAgICAgICBjb250aW51ZTtcclxuICAgICAgfVxyXG4gICAgICB2YXIgY2hhbmdlcyA9IGV2ZW50LmNoYW5nZXMsIG5ld0NoYW5nZXMgPSBbXTtcclxuICAgICAgY29weS5wdXNoKHtjaGFuZ2VzOiBuZXdDaGFuZ2VzfSk7XHJcbiAgICAgIGZvciAodmFyIGogPSAwOyBqIDwgY2hhbmdlcy5sZW5ndGg7ICsraikge1xyXG4gICAgICAgIHZhciBjaGFuZ2UgPSBjaGFuZ2VzW2pdLCBtO1xyXG4gICAgICAgIG5ld0NoYW5nZXMucHVzaCh7ZnJvbTogY2hhbmdlLmZyb20sIHRvOiBjaGFuZ2UudG8sIHRleHQ6IGNoYW5nZS50ZXh0fSk7XHJcbiAgICAgICAgaWYgKG5ld0dyb3VwKSBmb3IgKHZhciBwcm9wIGluIGNoYW5nZSkgaWYgKG0gPSBwcm9wLm1hdGNoKC9ec3BhbnNfKFxcZCspJC8pKSB7XHJcbiAgICAgICAgICBpZiAoaW5kZXhPZihuZXdHcm91cCwgTnVtYmVyKG1bMV0pKSA+IC0xKSB7XHJcbiAgICAgICAgICAgIGxzdChuZXdDaGFuZ2VzKVtwcm9wXSA9IGNoYW5nZVtwcm9wXTtcclxuICAgICAgICAgICAgZGVsZXRlIGNoYW5nZVtwcm9wXTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIHJldHVybiBjb3B5O1xyXG4gIH1cclxuXHJcbiAgLy8gUmViYXNpbmcvcmVzZXR0aW5nIGhpc3RvcnkgdG8gZGVhbCB3aXRoIGV4dGVybmFsbHktc291cmNlZCBjaGFuZ2VzXHJcblxyXG4gIGZ1bmN0aW9uIHJlYmFzZUhpc3RTZWxTaW5nbGUocG9zLCBmcm9tLCB0bywgZGlmZikge1xyXG4gICAgaWYgKHRvIDwgcG9zLmxpbmUpIHtcclxuICAgICAgcG9zLmxpbmUgKz0gZGlmZjtcclxuICAgIH0gZWxzZSBpZiAoZnJvbSA8IHBvcy5saW5lKSB7XHJcbiAgICAgIHBvcy5saW5lID0gZnJvbTtcclxuICAgICAgcG9zLmNoID0gMDtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFRyaWVzIHRvIHJlYmFzZSBhbiBhcnJheSBvZiBoaXN0b3J5IGV2ZW50cyBnaXZlbiBhIGNoYW5nZSBpbiB0aGVcclxuICAvLyBkb2N1bWVudC4gSWYgdGhlIGNoYW5nZSB0b3VjaGVzIHRoZSBzYW1lIGxpbmVzIGFzIHRoZSBldmVudCwgdGhlXHJcbiAgLy8gZXZlbnQsIGFuZCBldmVyeXRoaW5nICdiZWhpbmQnIGl0LCBpcyBkaXNjYXJkZWQuIElmIHRoZSBjaGFuZ2UgaXNcclxuICAvLyBiZWZvcmUgdGhlIGV2ZW50LCB0aGUgZXZlbnQncyBwb3NpdGlvbnMgYXJlIHVwZGF0ZWQuIFVzZXMgYVxyXG4gIC8vIGNvcHktb24td3JpdGUgc2NoZW1lIGZvciB0aGUgcG9zaXRpb25zLCB0byBhdm9pZCBoYXZpbmcgdG9cclxuICAvLyByZWFsbG9jYXRlIHRoZW0gYWxsIG9uIGV2ZXJ5IHJlYmFzZSwgYnV0IGFsc28gYXZvaWQgcHJvYmxlbXMgd2l0aFxyXG4gIC8vIHNoYXJlZCBwb3NpdGlvbiBvYmplY3RzIGJlaW5nIHVuc2FmZWx5IHVwZGF0ZWQuXHJcbiAgZnVuY3Rpb24gcmViYXNlSGlzdEFycmF5KGFycmF5LCBmcm9tLCB0bywgZGlmZikge1xyXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBhcnJheS5sZW5ndGg7ICsraSkge1xyXG4gICAgICB2YXIgc3ViID0gYXJyYXlbaV0sIG9rID0gdHJ1ZTtcclxuICAgICAgaWYgKHN1Yi5yYW5nZXMpIHtcclxuICAgICAgICBpZiAoIXN1Yi5jb3BpZWQpIHsgc3ViID0gYXJyYXlbaV0gPSBzdWIuZGVlcENvcHkoKTsgc3ViLmNvcGllZCA9IHRydWU7IH1cclxuICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IHN1Yi5yYW5nZXMubGVuZ3RoOyBqKyspIHtcclxuICAgICAgICAgIHJlYmFzZUhpc3RTZWxTaW5nbGUoc3ViLnJhbmdlc1tqXS5hbmNob3IsIGZyb20sIHRvLCBkaWZmKTtcclxuICAgICAgICAgIHJlYmFzZUhpc3RTZWxTaW5nbGUoc3ViLnJhbmdlc1tqXS5oZWFkLCBmcm9tLCB0bywgZGlmZik7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGNvbnRpbnVlO1xyXG4gICAgICB9XHJcbiAgICAgIGZvciAodmFyIGogPSAwOyBqIDwgc3ViLmNoYW5nZXMubGVuZ3RoOyArK2opIHtcclxuICAgICAgICB2YXIgY3VyID0gc3ViLmNoYW5nZXNbal07XHJcbiAgICAgICAgaWYgKHRvIDwgY3VyLmZyb20ubGluZSkge1xyXG4gICAgICAgICAgY3VyLmZyb20gPSBQb3MoY3VyLmZyb20ubGluZSArIGRpZmYsIGN1ci5mcm9tLmNoKTtcclxuICAgICAgICAgIGN1ci50byA9IFBvcyhjdXIudG8ubGluZSArIGRpZmYsIGN1ci50by5jaCk7XHJcbiAgICAgICAgfSBlbHNlIGlmIChmcm9tIDw9IGN1ci50by5saW5lKSB7XHJcbiAgICAgICAgICBvayA9IGZhbHNlO1xyXG4gICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICAgIGlmICghb2spIHtcclxuICAgICAgICBhcnJheS5zcGxpY2UoMCwgaSArIDEpO1xyXG4gICAgICAgIGkgPSAwO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiByZWJhc2VIaXN0KGhpc3QsIGNoYW5nZSkge1xyXG4gICAgdmFyIGZyb20gPSBjaGFuZ2UuZnJvbS5saW5lLCB0byA9IGNoYW5nZS50by5saW5lLCBkaWZmID0gY2hhbmdlLnRleHQubGVuZ3RoIC0gKHRvIC0gZnJvbSkgLSAxO1xyXG4gICAgcmViYXNlSGlzdEFycmF5KGhpc3QuZG9uZSwgZnJvbSwgdG8sIGRpZmYpO1xyXG4gICAgcmViYXNlSGlzdEFycmF5KGhpc3QudW5kb25lLCBmcm9tLCB0bywgZGlmZik7XHJcbiAgfVxyXG5cclxuICAvLyBFVkVOVCBVVElMSVRJRVNcclxuXHJcbiAgLy8gRHVlIHRvIHRoZSBmYWN0IHRoYXQgd2Ugc3RpbGwgc3VwcG9ydCBqdXJhc3NpYyBJRSB2ZXJzaW9ucywgc29tZVxyXG4gIC8vIGNvbXBhdGliaWxpdHkgd3JhcHBlcnMgYXJlIG5lZWRlZC5cclxuXHJcbiAgdmFyIGVfcHJldmVudERlZmF1bHQgPSBDb2RlTWlycm9yLmVfcHJldmVudERlZmF1bHQgPSBmdW5jdGlvbihlKSB7XHJcbiAgICBpZiAoZS5wcmV2ZW50RGVmYXVsdCkgZS5wcmV2ZW50RGVmYXVsdCgpO1xyXG4gICAgZWxzZSBlLnJldHVyblZhbHVlID0gZmFsc2U7XHJcbiAgfTtcclxuICB2YXIgZV9zdG9wUHJvcGFnYXRpb24gPSBDb2RlTWlycm9yLmVfc3RvcFByb3BhZ2F0aW9uID0gZnVuY3Rpb24oZSkge1xyXG4gICAgaWYgKGUuc3RvcFByb3BhZ2F0aW9uKSBlLnN0b3BQcm9wYWdhdGlvbigpO1xyXG4gICAgZWxzZSBlLmNhbmNlbEJ1YmJsZSA9IHRydWU7XHJcbiAgfTtcclxuICBmdW5jdGlvbiBlX2RlZmF1bHRQcmV2ZW50ZWQoZSkge1xyXG4gICAgcmV0dXJuIGUuZGVmYXVsdFByZXZlbnRlZCAhPSBudWxsID8gZS5kZWZhdWx0UHJldmVudGVkIDogZS5yZXR1cm5WYWx1ZSA9PSBmYWxzZTtcclxuICB9XHJcbiAgdmFyIGVfc3RvcCA9IENvZGVNaXJyb3IuZV9zdG9wID0gZnVuY3Rpb24oZSkge2VfcHJldmVudERlZmF1bHQoZSk7IGVfc3RvcFByb3BhZ2F0aW9uKGUpO307XHJcblxyXG4gIGZ1bmN0aW9uIGVfdGFyZ2V0KGUpIHtyZXR1cm4gZS50YXJnZXQgfHwgZS5zcmNFbGVtZW50O31cclxuICBmdW5jdGlvbiBlX2J1dHRvbihlKSB7XHJcbiAgICB2YXIgYiA9IGUud2hpY2g7XHJcbiAgICBpZiAoYiA9PSBudWxsKSB7XHJcbiAgICAgIGlmIChlLmJ1dHRvbiAmIDEpIGIgPSAxO1xyXG4gICAgICBlbHNlIGlmIChlLmJ1dHRvbiAmIDIpIGIgPSAzO1xyXG4gICAgICBlbHNlIGlmIChlLmJ1dHRvbiAmIDQpIGIgPSAyO1xyXG4gICAgfVxyXG4gICAgaWYgKG1hYyAmJiBlLmN0cmxLZXkgJiYgYiA9PSAxKSBiID0gMztcclxuICAgIHJldHVybiBiO1xyXG4gIH1cclxuXHJcbiAgLy8gRVZFTlQgSEFORExJTkdcclxuXHJcbiAgLy8gTGlnaHR3ZWlnaHQgZXZlbnQgZnJhbWV3b3JrLiBvbi9vZmYgYWxzbyB3b3JrIG9uIERPTSBub2RlcyxcclxuICAvLyByZWdpc3RlcmluZyBuYXRpdmUgRE9NIGhhbmRsZXJzLlxyXG5cclxuICB2YXIgb24gPSBDb2RlTWlycm9yLm9uID0gZnVuY3Rpb24oZW1pdHRlciwgdHlwZSwgZikge1xyXG4gICAgaWYgKGVtaXR0ZXIuYWRkRXZlbnRMaXN0ZW5lcilcclxuICAgICAgZW1pdHRlci5hZGRFdmVudExpc3RlbmVyKHR5cGUsIGYsIGZhbHNlKTtcclxuICAgIGVsc2UgaWYgKGVtaXR0ZXIuYXR0YWNoRXZlbnQpXHJcbiAgICAgIGVtaXR0ZXIuYXR0YWNoRXZlbnQoXCJvblwiICsgdHlwZSwgZik7XHJcbiAgICBlbHNlIHtcclxuICAgICAgdmFyIG1hcCA9IGVtaXR0ZXIuX2hhbmRsZXJzIHx8IChlbWl0dGVyLl9oYW5kbGVycyA9IHt9KTtcclxuICAgICAgdmFyIGFyciA9IG1hcFt0eXBlXSB8fCAobWFwW3R5cGVdID0gW10pO1xyXG4gICAgICBhcnIucHVzaChmKTtcclxuICAgIH1cclxuICB9O1xyXG5cclxuICB2YXIgbm9IYW5kbGVycyA9IFtdXHJcbiAgZnVuY3Rpb24gZ2V0SGFuZGxlcnMoZW1pdHRlciwgdHlwZSwgY29weSkge1xyXG4gICAgdmFyIGFyciA9IGVtaXR0ZXIuX2hhbmRsZXJzICYmIGVtaXR0ZXIuX2hhbmRsZXJzW3R5cGVdXHJcbiAgICBpZiAoY29weSkgcmV0dXJuIGFyciAmJiBhcnIubGVuZ3RoID4gMCA/IGFyci5zbGljZSgpIDogbm9IYW5kbGVyc1xyXG4gICAgZWxzZSByZXR1cm4gYXJyIHx8IG5vSGFuZGxlcnNcclxuICB9XHJcblxyXG4gIHZhciBvZmYgPSBDb2RlTWlycm9yLm9mZiA9IGZ1bmN0aW9uKGVtaXR0ZXIsIHR5cGUsIGYpIHtcclxuICAgIGlmIChlbWl0dGVyLnJlbW92ZUV2ZW50TGlzdGVuZXIpXHJcbiAgICAgIGVtaXR0ZXIucmVtb3ZlRXZlbnRMaXN0ZW5lcih0eXBlLCBmLCBmYWxzZSk7XHJcbiAgICBlbHNlIGlmIChlbWl0dGVyLmRldGFjaEV2ZW50KVxyXG4gICAgICBlbWl0dGVyLmRldGFjaEV2ZW50KFwib25cIiArIHR5cGUsIGYpO1xyXG4gICAgZWxzZSB7XHJcbiAgICAgIHZhciBoYW5kbGVycyA9IGdldEhhbmRsZXJzKGVtaXR0ZXIsIHR5cGUsIGZhbHNlKVxyXG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGhhbmRsZXJzLmxlbmd0aDsgKytpKVxyXG4gICAgICAgIGlmIChoYW5kbGVyc1tpXSA9PSBmKSB7IGhhbmRsZXJzLnNwbGljZShpLCAxKTsgYnJlYWs7IH1cclxuICAgIH1cclxuICB9O1xyXG5cclxuICB2YXIgc2lnbmFsID0gQ29kZU1pcnJvci5zaWduYWwgPSBmdW5jdGlvbihlbWl0dGVyLCB0eXBlIC8qLCB2YWx1ZXMuLi4qLykge1xyXG4gICAgdmFyIGhhbmRsZXJzID0gZ2V0SGFuZGxlcnMoZW1pdHRlciwgdHlwZSwgdHJ1ZSlcclxuICAgIGlmICghaGFuZGxlcnMubGVuZ3RoKSByZXR1cm47XHJcbiAgICB2YXIgYXJncyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cywgMik7XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGhhbmRsZXJzLmxlbmd0aDsgKytpKSBoYW5kbGVyc1tpXS5hcHBseShudWxsLCBhcmdzKTtcclxuICB9O1xyXG5cclxuICB2YXIgb3JwaGFuRGVsYXllZENhbGxiYWNrcyA9IG51bGw7XHJcblxyXG4gIC8vIE9mdGVuLCB3ZSB3YW50IHRvIHNpZ25hbCBldmVudHMgYXQgYSBwb2ludCB3aGVyZSB3ZSBhcmUgaW4gdGhlXHJcbiAgLy8gbWlkZGxlIG9mIHNvbWUgd29yaywgYnV0IGRvbid0IHdhbnQgdGhlIGhhbmRsZXIgdG8gc3RhcnQgY2FsbGluZ1xyXG4gIC8vIG90aGVyIG1ldGhvZHMgb24gdGhlIGVkaXRvciwgd2hpY2ggbWlnaHQgYmUgaW4gYW4gaW5jb25zaXN0ZW50XHJcbiAgLy8gc3RhdGUgb3Igc2ltcGx5IG5vdCBleHBlY3QgYW55IG90aGVyIGV2ZW50cyB0byBoYXBwZW4uXHJcbiAgLy8gc2lnbmFsTGF0ZXIgbG9va3Mgd2hldGhlciB0aGVyZSBhcmUgYW55IGhhbmRsZXJzLCBhbmQgc2NoZWR1bGVzXHJcbiAgLy8gdGhlbSB0byBiZSBleGVjdXRlZCB3aGVuIHRoZSBsYXN0IG9wZXJhdGlvbiBlbmRzLCBvciwgaWYgbm9cclxuICAvLyBvcGVyYXRpb24gaXMgYWN0aXZlLCB3aGVuIGEgdGltZW91dCBmaXJlcy5cclxuICBmdW5jdGlvbiBzaWduYWxMYXRlcihlbWl0dGVyLCB0eXBlIC8qLCB2YWx1ZXMuLi4qLykge1xyXG4gICAgdmFyIGFyciA9IGdldEhhbmRsZXJzKGVtaXR0ZXIsIHR5cGUsIGZhbHNlKVxyXG4gICAgaWYgKCFhcnIubGVuZ3RoKSByZXR1cm47XHJcbiAgICB2YXIgYXJncyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cywgMiksIGxpc3Q7XHJcbiAgICBpZiAob3BlcmF0aW9uR3JvdXApIHtcclxuICAgICAgbGlzdCA9IG9wZXJhdGlvbkdyb3VwLmRlbGF5ZWRDYWxsYmFja3M7XHJcbiAgICB9IGVsc2UgaWYgKG9ycGhhbkRlbGF5ZWRDYWxsYmFja3MpIHtcclxuICAgICAgbGlzdCA9IG9ycGhhbkRlbGF5ZWRDYWxsYmFja3M7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICBsaXN0ID0gb3JwaGFuRGVsYXllZENhbGxiYWNrcyA9IFtdO1xyXG4gICAgICBzZXRUaW1lb3V0KGZpcmVPcnBoYW5EZWxheWVkLCAwKTtcclxuICAgIH1cclxuICAgIGZ1bmN0aW9uIGJuZChmKSB7cmV0dXJuIGZ1bmN0aW9uKCl7Zi5hcHBseShudWxsLCBhcmdzKTt9O307XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGFyci5sZW5ndGg7ICsraSlcclxuICAgICAgbGlzdC5wdXNoKGJuZChhcnJbaV0pKTtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIGZpcmVPcnBoYW5EZWxheWVkKCkge1xyXG4gICAgdmFyIGRlbGF5ZWQgPSBvcnBoYW5EZWxheWVkQ2FsbGJhY2tzO1xyXG4gICAgb3JwaGFuRGVsYXllZENhbGxiYWNrcyA9IG51bGw7XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGRlbGF5ZWQubGVuZ3RoOyArK2kpIGRlbGF5ZWRbaV0oKTtcclxuICB9XHJcblxyXG4gIC8vIFRoZSBET00gZXZlbnRzIHRoYXQgQ29kZU1pcnJvciBoYW5kbGVzIGNhbiBiZSBvdmVycmlkZGVuIGJ5XHJcbiAgLy8gcmVnaXN0ZXJpbmcgYSAobm9uLURPTSkgaGFuZGxlciBvbiB0aGUgZWRpdG9yIGZvciB0aGUgZXZlbnQgbmFtZSxcclxuICAvLyBhbmQgcHJldmVudERlZmF1bHQtaW5nIHRoZSBldmVudCBpbiB0aGF0IGhhbmRsZXIuXHJcbiAgZnVuY3Rpb24gc2lnbmFsRE9NRXZlbnQoY20sIGUsIG92ZXJyaWRlKSB7XHJcbiAgICBpZiAodHlwZW9mIGUgPT0gXCJzdHJpbmdcIilcclxuICAgICAgZSA9IHt0eXBlOiBlLCBwcmV2ZW50RGVmYXVsdDogZnVuY3Rpb24oKSB7IHRoaXMuZGVmYXVsdFByZXZlbnRlZCA9IHRydWU7IH19O1xyXG4gICAgc2lnbmFsKGNtLCBvdmVycmlkZSB8fCBlLnR5cGUsIGNtLCBlKTtcclxuICAgIHJldHVybiBlX2RlZmF1bHRQcmV2ZW50ZWQoZSkgfHwgZS5jb2RlbWlycm9ySWdub3JlO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gc2lnbmFsQ3Vyc29yQWN0aXZpdHkoY20pIHtcclxuICAgIHZhciBhcnIgPSBjbS5faGFuZGxlcnMgJiYgY20uX2hhbmRsZXJzLmN1cnNvckFjdGl2aXR5O1xyXG4gICAgaWYgKCFhcnIpIHJldHVybjtcclxuICAgIHZhciBzZXQgPSBjbS5jdXJPcC5jdXJzb3JBY3Rpdml0eUhhbmRsZXJzIHx8IChjbS5jdXJPcC5jdXJzb3JBY3Rpdml0eUhhbmRsZXJzID0gW10pO1xyXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBhcnIubGVuZ3RoOyArK2kpIGlmIChpbmRleE9mKHNldCwgYXJyW2ldKSA9PSAtMSlcclxuICAgICAgc2V0LnB1c2goYXJyW2ldKTtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIGhhc0hhbmRsZXIoZW1pdHRlciwgdHlwZSkge1xyXG4gICAgcmV0dXJuIGdldEhhbmRsZXJzKGVtaXR0ZXIsIHR5cGUpLmxlbmd0aCA+IDBcclxuICB9XHJcblxyXG4gIC8vIEFkZCBvbiBhbmQgb2ZmIG1ldGhvZHMgdG8gYSBjb25zdHJ1Y3RvcidzIHByb3RvdHlwZSwgdG8gbWFrZVxyXG4gIC8vIHJlZ2lzdGVyaW5nIGV2ZW50cyBvbiBzdWNoIG9iamVjdHMgbW9yZSBjb252ZW5pZW50LlxyXG4gIGZ1bmN0aW9uIGV2ZW50TWl4aW4oY3Rvcikge1xyXG4gICAgY3Rvci5wcm90b3R5cGUub24gPSBmdW5jdGlvbih0eXBlLCBmKSB7b24odGhpcywgdHlwZSwgZik7fTtcclxuICAgIGN0b3IucHJvdG90eXBlLm9mZiA9IGZ1bmN0aW9uKHR5cGUsIGYpIHtvZmYodGhpcywgdHlwZSwgZik7fTtcclxuICB9XHJcblxyXG4gIC8vIE1JU0MgVVRJTElUSUVTXHJcblxyXG4gIC8vIE51bWJlciBvZiBwaXhlbHMgYWRkZWQgdG8gc2Nyb2xsZXIgYW5kIHNpemVyIHRvIGhpZGUgc2Nyb2xsYmFyXHJcbiAgdmFyIHNjcm9sbGVyR2FwID0gMzA7XHJcblxyXG4gIC8vIFJldHVybmVkIG9yIHRocm93biBieSB2YXJpb3VzIHByb3RvY29scyB0byBzaWduYWwgJ0knbSBub3RcclxuICAvLyBoYW5kbGluZyB0aGlzJy5cclxuICB2YXIgUGFzcyA9IENvZGVNaXJyb3IuUGFzcyA9IHt0b1N0cmluZzogZnVuY3Rpb24oKXtyZXR1cm4gXCJDb2RlTWlycm9yLlBhc3NcIjt9fTtcclxuXHJcbiAgLy8gUmV1c2VkIG9wdGlvbiBvYmplY3RzIGZvciBzZXRTZWxlY3Rpb24gJiBmcmllbmRzXHJcbiAgdmFyIHNlbF9kb250U2Nyb2xsID0ge3Njcm9sbDogZmFsc2V9LCBzZWxfbW91c2UgPSB7b3JpZ2luOiBcIiptb3VzZVwifSwgc2VsX21vdmUgPSB7b3JpZ2luOiBcIittb3ZlXCJ9O1xyXG5cclxuICBmdW5jdGlvbiBEZWxheWVkKCkge3RoaXMuaWQgPSBudWxsO31cclxuICBEZWxheWVkLnByb3RvdHlwZS5zZXQgPSBmdW5jdGlvbihtcywgZikge1xyXG4gICAgY2xlYXJUaW1lb3V0KHRoaXMuaWQpO1xyXG4gICAgdGhpcy5pZCA9IHNldFRpbWVvdXQoZiwgbXMpO1xyXG4gIH07XHJcblxyXG4gIC8vIENvdW50cyB0aGUgY29sdW1uIG9mZnNldCBpbiBhIHN0cmluZywgdGFraW5nIHRhYnMgaW50byBhY2NvdW50LlxyXG4gIC8vIFVzZWQgbW9zdGx5IHRvIGZpbmQgaW5kZW50YXRpb24uXHJcbiAgdmFyIGNvdW50Q29sdW1uID0gQ29kZU1pcnJvci5jb3VudENvbHVtbiA9IGZ1bmN0aW9uKHN0cmluZywgZW5kLCB0YWJTaXplLCBzdGFydEluZGV4LCBzdGFydFZhbHVlKSB7XHJcbiAgICBpZiAoZW5kID09IG51bGwpIHtcclxuICAgICAgZW5kID0gc3RyaW5nLnNlYXJjaCgvW15cXHNcXHUwMGEwXS8pO1xyXG4gICAgICBpZiAoZW5kID09IC0xKSBlbmQgPSBzdHJpbmcubGVuZ3RoO1xyXG4gICAgfVxyXG4gICAgZm9yICh2YXIgaSA9IHN0YXJ0SW5kZXggfHwgMCwgbiA9IHN0YXJ0VmFsdWUgfHwgMDs7KSB7XHJcbiAgICAgIHZhciBuZXh0VGFiID0gc3RyaW5nLmluZGV4T2YoXCJcXHRcIiwgaSk7XHJcbiAgICAgIGlmIChuZXh0VGFiIDwgMCB8fCBuZXh0VGFiID49IGVuZClcclxuICAgICAgICByZXR1cm4gbiArIChlbmQgLSBpKTtcclxuICAgICAgbiArPSBuZXh0VGFiIC0gaTtcclxuICAgICAgbiArPSB0YWJTaXplIC0gKG4gJSB0YWJTaXplKTtcclxuICAgICAgaSA9IG5leHRUYWIgKyAxO1xyXG4gICAgfVxyXG4gIH07XHJcblxyXG4gIC8vIFRoZSBpbnZlcnNlIG9mIGNvdW50Q29sdW1uIC0tIGZpbmQgdGhlIG9mZnNldCB0aGF0IGNvcnJlc3BvbmRzIHRvXHJcbiAgLy8gYSBwYXJ0aWN1bGFyIGNvbHVtbi5cclxuICB2YXIgZmluZENvbHVtbiA9IENvZGVNaXJyb3IuZmluZENvbHVtbiA9IGZ1bmN0aW9uKHN0cmluZywgZ29hbCwgdGFiU2l6ZSkge1xyXG4gICAgZm9yICh2YXIgcG9zID0gMCwgY29sID0gMDs7KSB7XHJcbiAgICAgIHZhciBuZXh0VGFiID0gc3RyaW5nLmluZGV4T2YoXCJcXHRcIiwgcG9zKTtcclxuICAgICAgaWYgKG5leHRUYWIgPT0gLTEpIG5leHRUYWIgPSBzdHJpbmcubGVuZ3RoO1xyXG4gICAgICB2YXIgc2tpcHBlZCA9IG5leHRUYWIgLSBwb3M7XHJcbiAgICAgIGlmIChuZXh0VGFiID09IHN0cmluZy5sZW5ndGggfHwgY29sICsgc2tpcHBlZCA+PSBnb2FsKVxyXG4gICAgICAgIHJldHVybiBwb3MgKyBNYXRoLm1pbihza2lwcGVkLCBnb2FsIC0gY29sKTtcclxuICAgICAgY29sICs9IG5leHRUYWIgLSBwb3M7XHJcbiAgICAgIGNvbCArPSB0YWJTaXplIC0gKGNvbCAlIHRhYlNpemUpO1xyXG4gICAgICBwb3MgPSBuZXh0VGFiICsgMTtcclxuICAgICAgaWYgKGNvbCA+PSBnb2FsKSByZXR1cm4gcG9zO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgdmFyIHNwYWNlU3RycyA9IFtcIlwiXTtcclxuICBmdW5jdGlvbiBzcGFjZVN0cihuKSB7XHJcbiAgICB3aGlsZSAoc3BhY2VTdHJzLmxlbmd0aCA8PSBuKVxyXG4gICAgICBzcGFjZVN0cnMucHVzaChsc3Qoc3BhY2VTdHJzKSArIFwiIFwiKTtcclxuICAgIHJldHVybiBzcGFjZVN0cnNbbl07XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBsc3QoYXJyKSB7IHJldHVybiBhcnJbYXJyLmxlbmd0aC0xXTsgfVxyXG5cclxuICB2YXIgc2VsZWN0SW5wdXQgPSBmdW5jdGlvbihub2RlKSB7IG5vZGUuc2VsZWN0KCk7IH07XHJcbiAgaWYgKGlvcykgLy8gTW9iaWxlIFNhZmFyaSBhcHBhcmVudGx5IGhhcyBhIGJ1ZyB3aGVyZSBzZWxlY3QoKSBpcyBicm9rZW4uXHJcbiAgICBzZWxlY3RJbnB1dCA9IGZ1bmN0aW9uKG5vZGUpIHsgbm9kZS5zZWxlY3Rpb25TdGFydCA9IDA7IG5vZGUuc2VsZWN0aW9uRW5kID0gbm9kZS52YWx1ZS5sZW5ndGg7IH07XHJcbiAgZWxzZSBpZiAoaWUpIC8vIFN1cHByZXNzIG15c3RlcmlvdXMgSUUxMCBlcnJvcnNcclxuICAgIHNlbGVjdElucHV0ID0gZnVuY3Rpb24obm9kZSkgeyB0cnkgeyBub2RlLnNlbGVjdCgpOyB9IGNhdGNoKF9lKSB7fSB9O1xyXG5cclxuICBmdW5jdGlvbiBpbmRleE9mKGFycmF5LCBlbHQpIHtcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYXJyYXkubGVuZ3RoOyArK2kpXHJcbiAgICAgIGlmIChhcnJheVtpXSA9PSBlbHQpIHJldHVybiBpO1xyXG4gICAgcmV0dXJuIC0xO1xyXG4gIH1cclxuICBmdW5jdGlvbiBtYXAoYXJyYXksIGYpIHtcclxuICAgIHZhciBvdXQgPSBbXTtcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYXJyYXkubGVuZ3RoOyBpKyspIG91dFtpXSA9IGYoYXJyYXlbaV0sIGkpO1xyXG4gICAgcmV0dXJuIG91dDtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIG5vdGhpbmcoKSB7fVxyXG5cclxuICBmdW5jdGlvbiBjcmVhdGVPYmooYmFzZSwgcHJvcHMpIHtcclxuICAgIHZhciBpbnN0O1xyXG4gICAgaWYgKE9iamVjdC5jcmVhdGUpIHtcclxuICAgICAgaW5zdCA9IE9iamVjdC5jcmVhdGUoYmFzZSk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICBub3RoaW5nLnByb3RvdHlwZSA9IGJhc2U7XHJcbiAgICAgIGluc3QgPSBuZXcgbm90aGluZygpO1xyXG4gICAgfVxyXG4gICAgaWYgKHByb3BzKSBjb3B5T2JqKHByb3BzLCBpbnN0KTtcclxuICAgIHJldHVybiBpbnN0O1xyXG4gIH07XHJcblxyXG4gIGZ1bmN0aW9uIGNvcHlPYmoob2JqLCB0YXJnZXQsIG92ZXJ3cml0ZSkge1xyXG4gICAgaWYgKCF0YXJnZXQpIHRhcmdldCA9IHt9O1xyXG4gICAgZm9yICh2YXIgcHJvcCBpbiBvYmopXHJcbiAgICAgIGlmIChvYmouaGFzT3duUHJvcGVydHkocHJvcCkgJiYgKG92ZXJ3cml0ZSAhPT0gZmFsc2UgfHwgIXRhcmdldC5oYXNPd25Qcm9wZXJ0eShwcm9wKSkpXHJcbiAgICAgICAgdGFyZ2V0W3Byb3BdID0gb2JqW3Byb3BdO1xyXG4gICAgcmV0dXJuIHRhcmdldDtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIGJpbmQoZikge1xyXG4gICAgdmFyIGFyZ3MgPSBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChhcmd1bWVudHMsIDEpO1xyXG4gICAgcmV0dXJuIGZ1bmN0aW9uKCl7cmV0dXJuIGYuYXBwbHkobnVsbCwgYXJncyk7fTtcclxuICB9XHJcblxyXG4gIHZhciBub25BU0NJSVNpbmdsZUNhc2VXb3JkQ2hhciA9IC9bXFx1MDBkZlxcdTA1ODdcXHUwNTkwLVxcdTA1ZjRcXHUwNjAwLVxcdTA2ZmZcXHUzMDQwLVxcdTMwOWZcXHUzMGEwLVxcdTMwZmZcXHUzNDAwLVxcdTRkYjVcXHU0ZTAwLVxcdTlmY2NcXHVhYzAwLVxcdWQ3YWZdLztcclxuICB2YXIgaXNXb3JkQ2hhckJhc2ljID0gQ29kZU1pcnJvci5pc1dvcmRDaGFyID0gZnVuY3Rpb24oY2gpIHtcclxuICAgIHJldHVybiAvXFx3Ly50ZXN0KGNoKSB8fCBjaCA+IFwiXFx4ODBcIiAmJlxyXG4gICAgICAoY2gudG9VcHBlckNhc2UoKSAhPSBjaC50b0xvd2VyQ2FzZSgpIHx8IG5vbkFTQ0lJU2luZ2xlQ2FzZVdvcmRDaGFyLnRlc3QoY2gpKTtcclxuICB9O1xyXG4gIGZ1bmN0aW9uIGlzV29yZENoYXIoY2gsIGhlbHBlcikge1xyXG4gICAgaWYgKCFoZWxwZXIpIHJldHVybiBpc1dvcmRDaGFyQmFzaWMoY2gpO1xyXG4gICAgaWYgKGhlbHBlci5zb3VyY2UuaW5kZXhPZihcIlxcXFx3XCIpID4gLTEgJiYgaXNXb3JkQ2hhckJhc2ljKGNoKSkgcmV0dXJuIHRydWU7XHJcbiAgICByZXR1cm4gaGVscGVyLnRlc3QoY2gpO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gaXNFbXB0eShvYmopIHtcclxuICAgIGZvciAodmFyIG4gaW4gb2JqKSBpZiAob2JqLmhhc093blByb3BlcnR5KG4pICYmIG9ialtuXSkgcmV0dXJuIGZhbHNlO1xyXG4gICAgcmV0dXJuIHRydWU7XHJcbiAgfVxyXG5cclxuICAvLyBFeHRlbmRpbmcgdW5pY29kZSBjaGFyYWN0ZXJzLiBBIHNlcmllcyBvZiBhIG5vbi1leHRlbmRpbmcgY2hhciArXHJcbiAgLy8gYW55IG51bWJlciBvZiBleHRlbmRpbmcgY2hhcnMgaXMgdHJlYXRlZCBhcyBhIHNpbmdsZSB1bml0IGFzIGZhclxyXG4gIC8vIGFzIGVkaXRpbmcgYW5kIG1lYXN1cmluZyBpcyBjb25jZXJuZWQuIFRoaXMgaXMgbm90IGZ1bGx5IGNvcnJlY3QsXHJcbiAgLy8gc2luY2Ugc29tZSBzY3JpcHRzL2ZvbnRzL2Jyb3dzZXJzIGFsc28gdHJlYXQgb3RoZXIgY29uZmlndXJhdGlvbnNcclxuICAvLyBvZiBjb2RlIHBvaW50cyBhcyBhIGdyb3VwLlxyXG4gIHZhciBleHRlbmRpbmdDaGFycyA9IC9bXFx1MDMwMC1cXHUwMzZmXFx1MDQ4My1cXHUwNDg5XFx1MDU5MS1cXHUwNWJkXFx1MDViZlxcdTA1YzFcXHUwNWMyXFx1MDVjNFxcdTA1YzVcXHUwNWM3XFx1MDYxMC1cXHUwNjFhXFx1MDY0Yi1cXHUwNjVlXFx1MDY3MFxcdTA2ZDYtXFx1MDZkY1xcdTA2ZGUtXFx1MDZlNFxcdTA2ZTdcXHUwNmU4XFx1MDZlYS1cXHUwNmVkXFx1MDcxMVxcdTA3MzAtXFx1MDc0YVxcdTA3YTYtXFx1MDdiMFxcdTA3ZWItXFx1MDdmM1xcdTA4MTYtXFx1MDgxOVxcdTA4MWItXFx1MDgyM1xcdTA4MjUtXFx1MDgyN1xcdTA4MjktXFx1MDgyZFxcdTA5MDAtXFx1MDkwMlxcdTA5M2NcXHUwOTQxLVxcdTA5NDhcXHUwOTRkXFx1MDk1MS1cXHUwOTU1XFx1MDk2MlxcdTA5NjNcXHUwOTgxXFx1MDliY1xcdTA5YmVcXHUwOWMxLVxcdTA5YzRcXHUwOWNkXFx1MDlkN1xcdTA5ZTJcXHUwOWUzXFx1MGEwMVxcdTBhMDJcXHUwYTNjXFx1MGE0MVxcdTBhNDJcXHUwYTQ3XFx1MGE0OFxcdTBhNGItXFx1MGE0ZFxcdTBhNTFcXHUwYTcwXFx1MGE3MVxcdTBhNzVcXHUwYTgxXFx1MGE4MlxcdTBhYmNcXHUwYWMxLVxcdTBhYzVcXHUwYWM3XFx1MGFjOFxcdTBhY2RcXHUwYWUyXFx1MGFlM1xcdTBiMDFcXHUwYjNjXFx1MGIzZVxcdTBiM2ZcXHUwYjQxLVxcdTBiNDRcXHUwYjRkXFx1MGI1NlxcdTBiNTdcXHUwYjYyXFx1MGI2M1xcdTBiODJcXHUwYmJlXFx1MGJjMFxcdTBiY2RcXHUwYmQ3XFx1MGMzZS1cXHUwYzQwXFx1MGM0Ni1cXHUwYzQ4XFx1MGM0YS1cXHUwYzRkXFx1MGM1NVxcdTBjNTZcXHUwYzYyXFx1MGM2M1xcdTBjYmNcXHUwY2JmXFx1MGNjMlxcdTBjYzZcXHUwY2NjXFx1MGNjZFxcdTBjZDVcXHUwY2Q2XFx1MGNlMlxcdTBjZTNcXHUwZDNlXFx1MGQ0MS1cXHUwZDQ0XFx1MGQ0ZFxcdTBkNTdcXHUwZDYyXFx1MGQ2M1xcdTBkY2FcXHUwZGNmXFx1MGRkMi1cXHUwZGQ0XFx1MGRkNlxcdTBkZGZcXHUwZTMxXFx1MGUzNC1cXHUwZTNhXFx1MGU0Ny1cXHUwZTRlXFx1MGViMVxcdTBlYjQtXFx1MGViOVxcdTBlYmJcXHUwZWJjXFx1MGVjOC1cXHUwZWNkXFx1MGYxOFxcdTBmMTlcXHUwZjM1XFx1MGYzN1xcdTBmMzlcXHUwZjcxLVxcdTBmN2VcXHUwZjgwLVxcdTBmODRcXHUwZjg2XFx1MGY4N1xcdTBmOTAtXFx1MGY5N1xcdTBmOTktXFx1MGZiY1xcdTBmYzZcXHUxMDJkLVxcdTEwMzBcXHUxMDMyLVxcdTEwMzdcXHUxMDM5XFx1MTAzYVxcdTEwM2RcXHUxMDNlXFx1MTA1OFxcdTEwNTlcXHUxMDVlLVxcdTEwNjBcXHUxMDcxLVxcdTEwNzRcXHUxMDgyXFx1MTA4NVxcdTEwODZcXHUxMDhkXFx1MTA5ZFxcdTEzNWZcXHUxNzEyLVxcdTE3MTRcXHUxNzMyLVxcdTE3MzRcXHUxNzUyXFx1MTc1M1xcdTE3NzJcXHUxNzczXFx1MTdiNy1cXHUxN2JkXFx1MTdjNlxcdTE3YzktXFx1MTdkM1xcdTE3ZGRcXHUxODBiLVxcdTE4MGRcXHUxOGE5XFx1MTkyMC1cXHUxOTIyXFx1MTkyN1xcdTE5MjhcXHUxOTMyXFx1MTkzOS1cXHUxOTNiXFx1MWExN1xcdTFhMThcXHUxYTU2XFx1MWE1OC1cXHUxYTVlXFx1MWE2MFxcdTFhNjJcXHUxYTY1LVxcdTFhNmNcXHUxYTczLVxcdTFhN2NcXHUxYTdmXFx1MWIwMC1cXHUxYjAzXFx1MWIzNFxcdTFiMzYtXFx1MWIzYVxcdTFiM2NcXHUxYjQyXFx1MWI2Yi1cXHUxYjczXFx1MWI4MFxcdTFiODFcXHUxYmEyLVxcdTFiYTVcXHUxYmE4XFx1MWJhOVxcdTFjMmMtXFx1MWMzM1xcdTFjMzZcXHUxYzM3XFx1MWNkMC1cXHUxY2QyXFx1MWNkNC1cXHUxY2UwXFx1MWNlMi1cXHUxY2U4XFx1MWNlZFxcdTFkYzAtXFx1MWRlNlxcdTFkZmQtXFx1MWRmZlxcdTIwMGNcXHUyMDBkXFx1MjBkMC1cXHUyMGYwXFx1MmNlZi1cXHUyY2YxXFx1MmRlMC1cXHUyZGZmXFx1MzAyYS1cXHUzMDJmXFx1MzA5OVxcdTMwOWFcXHVhNjZmLVxcdWE2NzJcXHVhNjdjXFx1YTY3ZFxcdWE2ZjBcXHVhNmYxXFx1YTgwMlxcdWE4MDZcXHVhODBiXFx1YTgyNVxcdWE4MjZcXHVhOGM0XFx1YThlMC1cXHVhOGYxXFx1YTkyNi1cXHVhOTJkXFx1YTk0Ny1cXHVhOTUxXFx1YTk4MC1cXHVhOTgyXFx1YTliM1xcdWE5YjYtXFx1YTliOVxcdWE5YmNcXHVhYTI5LVxcdWFhMmVcXHVhYTMxXFx1YWEzMlxcdWFhMzVcXHVhYTM2XFx1YWE0M1xcdWFhNGNcXHVhYWIwXFx1YWFiMi1cXHVhYWI0XFx1YWFiN1xcdWFhYjhcXHVhYWJlXFx1YWFiZlxcdWFhYzFcXHVhYmU1XFx1YWJlOFxcdWFiZWRcXHVkYzAwLVxcdWRmZmZcXHVmYjFlXFx1ZmUwMC1cXHVmZTBmXFx1ZmUyMC1cXHVmZTI2XFx1ZmY5ZVxcdWZmOWZdLztcclxuICBmdW5jdGlvbiBpc0V4dGVuZGluZ0NoYXIoY2gpIHsgcmV0dXJuIGNoLmNoYXJDb2RlQXQoMCkgPj0gNzY4ICYmIGV4dGVuZGluZ0NoYXJzLnRlc3QoY2gpOyB9XHJcblxyXG4gIC8vIERPTSBVVElMSVRJRVNcclxuXHJcbiAgZnVuY3Rpb24gZWx0KHRhZywgY29udGVudCwgY2xhc3NOYW1lLCBzdHlsZSkge1xyXG4gICAgdmFyIGUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KHRhZyk7XHJcbiAgICBpZiAoY2xhc3NOYW1lKSBlLmNsYXNzTmFtZSA9IGNsYXNzTmFtZTtcclxuICAgIGlmIChzdHlsZSkgZS5zdHlsZS5jc3NUZXh0ID0gc3R5bGU7XHJcbiAgICBpZiAodHlwZW9mIGNvbnRlbnQgPT0gXCJzdHJpbmdcIikgZS5hcHBlbmRDaGlsZChkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZShjb250ZW50KSk7XHJcbiAgICBlbHNlIGlmIChjb250ZW50KSBmb3IgKHZhciBpID0gMDsgaSA8IGNvbnRlbnQubGVuZ3RoOyArK2kpIGUuYXBwZW5kQ2hpbGQoY29udGVudFtpXSk7XHJcbiAgICByZXR1cm4gZTtcclxuICB9XHJcblxyXG4gIHZhciByYW5nZTtcclxuICBpZiAoZG9jdW1lbnQuY3JlYXRlUmFuZ2UpIHJhbmdlID0gZnVuY3Rpb24obm9kZSwgc3RhcnQsIGVuZCwgZW5kTm9kZSkge1xyXG4gICAgdmFyIHIgPSBkb2N1bWVudC5jcmVhdGVSYW5nZSgpO1xyXG4gICAgci5zZXRFbmQoZW5kTm9kZSB8fCBub2RlLCBlbmQpO1xyXG4gICAgci5zZXRTdGFydChub2RlLCBzdGFydCk7XHJcbiAgICByZXR1cm4gcjtcclxuICB9O1xyXG4gIGVsc2UgcmFuZ2UgPSBmdW5jdGlvbihub2RlLCBzdGFydCwgZW5kKSB7XHJcbiAgICB2YXIgciA9IGRvY3VtZW50LmJvZHkuY3JlYXRlVGV4dFJhbmdlKCk7XHJcbiAgICB0cnkgeyByLm1vdmVUb0VsZW1lbnRUZXh0KG5vZGUucGFyZW50Tm9kZSk7IH1cclxuICAgIGNhdGNoKGUpIHsgcmV0dXJuIHI7IH1cclxuICAgIHIuY29sbGFwc2UodHJ1ZSk7XHJcbiAgICByLm1vdmVFbmQoXCJjaGFyYWN0ZXJcIiwgZW5kKTtcclxuICAgIHIubW92ZVN0YXJ0KFwiY2hhcmFjdGVyXCIsIHN0YXJ0KTtcclxuICAgIHJldHVybiByO1xyXG4gIH07XHJcblxyXG4gIGZ1bmN0aW9uIHJlbW92ZUNoaWxkcmVuKGUpIHtcclxuICAgIGZvciAodmFyIGNvdW50ID0gZS5jaGlsZE5vZGVzLmxlbmd0aDsgY291bnQgPiAwOyAtLWNvdW50KVxyXG4gICAgICBlLnJlbW92ZUNoaWxkKGUuZmlyc3RDaGlsZCk7XHJcbiAgICByZXR1cm4gZTtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIHJlbW92ZUNoaWxkcmVuQW5kQWRkKHBhcmVudCwgZSkge1xyXG4gICAgcmV0dXJuIHJlbW92ZUNoaWxkcmVuKHBhcmVudCkuYXBwZW5kQ2hpbGQoZSk7XHJcbiAgfVxyXG5cclxuICB2YXIgY29udGFpbnMgPSBDb2RlTWlycm9yLmNvbnRhaW5zID0gZnVuY3Rpb24ocGFyZW50LCBjaGlsZCkge1xyXG4gICAgaWYgKGNoaWxkLm5vZGVUeXBlID09IDMpIC8vIEFuZHJvaWQgYnJvd3NlciBhbHdheXMgcmV0dXJucyBmYWxzZSB3aGVuIGNoaWxkIGlzIGEgdGV4dG5vZGVcclxuICAgICAgY2hpbGQgPSBjaGlsZC5wYXJlbnROb2RlO1xyXG4gICAgaWYgKHBhcmVudC5jb250YWlucylcclxuICAgICAgcmV0dXJuIHBhcmVudC5jb250YWlucyhjaGlsZCk7XHJcbiAgICBkbyB7XHJcbiAgICAgIGlmIChjaGlsZC5ub2RlVHlwZSA9PSAxMSkgY2hpbGQgPSBjaGlsZC5ob3N0O1xyXG4gICAgICBpZiAoY2hpbGQgPT0gcGFyZW50KSByZXR1cm4gdHJ1ZTtcclxuICAgIH0gd2hpbGUgKGNoaWxkID0gY2hpbGQucGFyZW50Tm9kZSk7XHJcbiAgfTtcclxuXHJcbiAgZnVuY3Rpb24gYWN0aXZlRWx0KCkge1xyXG4gICAgdmFyIGFjdGl2ZUVsZW1lbnQgPSBkb2N1bWVudC5hY3RpdmVFbGVtZW50O1xyXG4gICAgd2hpbGUgKGFjdGl2ZUVsZW1lbnQgJiYgYWN0aXZlRWxlbWVudC5yb290ICYmIGFjdGl2ZUVsZW1lbnQucm9vdC5hY3RpdmVFbGVtZW50KVxyXG4gICAgICBhY3RpdmVFbGVtZW50ID0gYWN0aXZlRWxlbWVudC5yb290LmFjdGl2ZUVsZW1lbnQ7XHJcbiAgICByZXR1cm4gYWN0aXZlRWxlbWVudDtcclxuICB9XHJcbiAgLy8gT2xkZXIgdmVyc2lvbnMgb2YgSUUgdGhyb3dzIHVuc3BlY2lmaWVkIGVycm9yIHdoZW4gdG91Y2hpbmdcclxuICAvLyBkb2N1bWVudC5hY3RpdmVFbGVtZW50IGluIHNvbWUgY2FzZXMgKGR1cmluZyBsb2FkaW5nLCBpbiBpZnJhbWUpXHJcbiAgaWYgKGllICYmIGllX3ZlcnNpb24gPCAxMSkgYWN0aXZlRWx0ID0gZnVuY3Rpb24oKSB7XHJcbiAgICB0cnkgeyByZXR1cm4gZG9jdW1lbnQuYWN0aXZlRWxlbWVudDsgfVxyXG4gICAgY2F0Y2goZSkgeyByZXR1cm4gZG9jdW1lbnQuYm9keTsgfVxyXG4gIH07XHJcblxyXG4gIGZ1bmN0aW9uIGNsYXNzVGVzdChjbHMpIHsgcmV0dXJuIG5ldyBSZWdFeHAoXCIoXnxcXFxccylcIiArIGNscyArIFwiKD86JHxcXFxccylcXFxccypcIik7IH1cclxuICB2YXIgcm1DbGFzcyA9IENvZGVNaXJyb3Iucm1DbGFzcyA9IGZ1bmN0aW9uKG5vZGUsIGNscykge1xyXG4gICAgdmFyIGN1cnJlbnQgPSBub2RlLmNsYXNzTmFtZTtcclxuICAgIHZhciBtYXRjaCA9IGNsYXNzVGVzdChjbHMpLmV4ZWMoY3VycmVudCk7XHJcbiAgICBpZiAobWF0Y2gpIHtcclxuICAgICAgdmFyIGFmdGVyID0gY3VycmVudC5zbGljZShtYXRjaC5pbmRleCArIG1hdGNoWzBdLmxlbmd0aCk7XHJcbiAgICAgIG5vZGUuY2xhc3NOYW1lID0gY3VycmVudC5zbGljZSgwLCBtYXRjaC5pbmRleCkgKyAoYWZ0ZXIgPyBtYXRjaFsxXSArIGFmdGVyIDogXCJcIik7XHJcbiAgICB9XHJcbiAgfTtcclxuICB2YXIgYWRkQ2xhc3MgPSBDb2RlTWlycm9yLmFkZENsYXNzID0gZnVuY3Rpb24obm9kZSwgY2xzKSB7XHJcbiAgICB2YXIgY3VycmVudCA9IG5vZGUuY2xhc3NOYW1lO1xyXG4gICAgaWYgKCFjbGFzc1Rlc3QoY2xzKS50ZXN0KGN1cnJlbnQpKSBub2RlLmNsYXNzTmFtZSArPSAoY3VycmVudCA/IFwiIFwiIDogXCJcIikgKyBjbHM7XHJcbiAgfTtcclxuICBmdW5jdGlvbiBqb2luQ2xhc3NlcyhhLCBiKSB7XHJcbiAgICB2YXIgYXMgPSBhLnNwbGl0KFwiIFwiKTtcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYXMubGVuZ3RoOyBpKyspXHJcbiAgICAgIGlmIChhc1tpXSAmJiAhY2xhc3NUZXN0KGFzW2ldKS50ZXN0KGIpKSBiICs9IFwiIFwiICsgYXNbaV07XHJcbiAgICByZXR1cm4gYjtcclxuICB9XHJcblxyXG4gIC8vIFdJTkRPVy1XSURFIEVWRU5UU1xyXG5cclxuICAvLyBUaGVzZSBtdXN0IGJlIGhhbmRsZWQgY2FyZWZ1bGx5LCBiZWNhdXNlIG5haXZlbHkgcmVnaXN0ZXJpbmcgYVxyXG4gIC8vIGhhbmRsZXIgZm9yIGVhY2ggZWRpdG9yIHdpbGwgY2F1c2UgdGhlIGVkaXRvcnMgdG8gbmV2ZXIgYmVcclxuICAvLyBnYXJiYWdlIGNvbGxlY3RlZC5cclxuXHJcbiAgZnVuY3Rpb24gZm9yRWFjaENvZGVNaXJyb3IoZikge1xyXG4gICAgaWYgKCFkb2N1bWVudC5ib2R5LmdldEVsZW1lbnRzQnlDbGFzc05hbWUpIHJldHVybjtcclxuICAgIHZhciBieUNsYXNzID0gZG9jdW1lbnQuYm9keS5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKFwiQ29kZU1pcnJvclwiKTtcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYnlDbGFzcy5sZW5ndGg7IGkrKykge1xyXG4gICAgICB2YXIgY20gPSBieUNsYXNzW2ldLkNvZGVNaXJyb3I7XHJcbiAgICAgIGlmIChjbSkgZihjbSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICB2YXIgZ2xvYmFsc1JlZ2lzdGVyZWQgPSBmYWxzZTtcclxuICBmdW5jdGlvbiBlbnN1cmVHbG9iYWxIYW5kbGVycygpIHtcclxuICAgIGlmIChnbG9iYWxzUmVnaXN0ZXJlZCkgcmV0dXJuO1xyXG4gICAgcmVnaXN0ZXJHbG9iYWxIYW5kbGVycygpO1xyXG4gICAgZ2xvYmFsc1JlZ2lzdGVyZWQgPSB0cnVlO1xyXG4gIH1cclxuICBmdW5jdGlvbiByZWdpc3Rlckdsb2JhbEhhbmRsZXJzKCkge1xyXG4gICAgLy8gV2hlbiB0aGUgd2luZG93IHJlc2l6ZXMsIHdlIG5lZWQgdG8gcmVmcmVzaCBhY3RpdmUgZWRpdG9ycy5cclxuICAgIHZhciByZXNpemVUaW1lcjtcclxuICAgIG9uKHdpbmRvdywgXCJyZXNpemVcIiwgZnVuY3Rpb24oKSB7XHJcbiAgICAgIGlmIChyZXNpemVUaW1lciA9PSBudWxsKSByZXNpemVUaW1lciA9IHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XHJcbiAgICAgICAgcmVzaXplVGltZXIgPSBudWxsO1xyXG4gICAgICAgIGZvckVhY2hDb2RlTWlycm9yKG9uUmVzaXplKTtcclxuICAgICAgfSwgMTAwKTtcclxuICAgIH0pO1xyXG4gICAgLy8gV2hlbiB0aGUgd2luZG93IGxvc2VzIGZvY3VzLCB3ZSB3YW50IHRvIHNob3cgdGhlIGVkaXRvciBhcyBibHVycmVkXHJcbiAgICBvbih3aW5kb3csIFwiYmx1clwiLCBmdW5jdGlvbigpIHtcclxuICAgICAgZm9yRWFjaENvZGVNaXJyb3Iob25CbHVyKTtcclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgLy8gRkVBVFVSRSBERVRFQ1RJT05cclxuXHJcbiAgLy8gRGV0ZWN0IGRyYWctYW5kLWRyb3BcclxuICB2YXIgZHJhZ0FuZERyb3AgPSBmdW5jdGlvbigpIHtcclxuICAgIC8vIFRoZXJlIGlzICpzb21lKiBraW5kIG9mIGRyYWctYW5kLWRyb3Agc3VwcG9ydCBpbiBJRTYtOCwgYnV0IElcclxuICAgIC8vIGNvdWxkbid0IGdldCBpdCB0byB3b3JrIHlldC5cclxuICAgIGlmIChpZSAmJiBpZV92ZXJzaW9uIDwgOSkgcmV0dXJuIGZhbHNlO1xyXG4gICAgdmFyIGRpdiA9IGVsdCgnZGl2Jyk7XHJcbiAgICByZXR1cm4gXCJkcmFnZ2FibGVcIiBpbiBkaXYgfHwgXCJkcmFnRHJvcFwiIGluIGRpdjtcclxuICB9KCk7XHJcblxyXG4gIHZhciB6d3NwU3VwcG9ydGVkO1xyXG4gIGZ1bmN0aW9uIHplcm9XaWR0aEVsZW1lbnQobWVhc3VyZSkge1xyXG4gICAgaWYgKHp3c3BTdXBwb3J0ZWQgPT0gbnVsbCkge1xyXG4gICAgICB2YXIgdGVzdCA9IGVsdChcInNwYW5cIiwgXCJcXHUyMDBiXCIpO1xyXG4gICAgICByZW1vdmVDaGlsZHJlbkFuZEFkZChtZWFzdXJlLCBlbHQoXCJzcGFuXCIsIFt0ZXN0LCBkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZShcInhcIildKSk7XHJcbiAgICAgIGlmIChtZWFzdXJlLmZpcnN0Q2hpbGQub2Zmc2V0SGVpZ2h0ICE9IDApXHJcbiAgICAgICAgendzcFN1cHBvcnRlZCA9IHRlc3Qub2Zmc2V0V2lkdGggPD0gMSAmJiB0ZXN0Lm9mZnNldEhlaWdodCA+IDIgJiYgIShpZSAmJiBpZV92ZXJzaW9uIDwgOCk7XHJcbiAgICB9XHJcbiAgICB2YXIgbm9kZSA9IHp3c3BTdXBwb3J0ZWQgPyBlbHQoXCJzcGFuXCIsIFwiXFx1MjAwYlwiKSA6XHJcbiAgICAgIGVsdChcInNwYW5cIiwgXCJcXHUwMGEwXCIsIG51bGwsIFwiZGlzcGxheTogaW5saW5lLWJsb2NrOyB3aWR0aDogMXB4OyBtYXJnaW4tcmlnaHQ6IC0xcHhcIik7XHJcbiAgICBub2RlLnNldEF0dHJpYnV0ZShcImNtLXRleHRcIiwgXCJcIik7XHJcbiAgICByZXR1cm4gbm9kZTtcclxuICB9XHJcblxyXG4gIC8vIEZlYXR1cmUtZGV0ZWN0IElFJ3MgY3J1bW15IGNsaWVudCByZWN0IHJlcG9ydGluZyBmb3IgYmlkaSB0ZXh0XHJcbiAgdmFyIGJhZEJpZGlSZWN0cztcclxuICBmdW5jdGlvbiBoYXNCYWRCaWRpUmVjdHMobWVhc3VyZSkge1xyXG4gICAgaWYgKGJhZEJpZGlSZWN0cyAhPSBudWxsKSByZXR1cm4gYmFkQmlkaVJlY3RzO1xyXG4gICAgdmFyIHR4dCA9IHJlbW92ZUNoaWxkcmVuQW5kQWRkKG1lYXN1cmUsIGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKFwiQVxcdTA2MmVBXCIpKTtcclxuICAgIHZhciByMCA9IHJhbmdlKHR4dCwgMCwgMSkuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XHJcbiAgICBpZiAoIXIwIHx8IHIwLmxlZnQgPT0gcjAucmlnaHQpIHJldHVybiBmYWxzZTsgLy8gU2FmYXJpIHJldHVybnMgbnVsbCBpbiBzb21lIGNhc2VzICgjMjc4MClcclxuICAgIHZhciByMSA9IHJhbmdlKHR4dCwgMSwgMikuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XHJcbiAgICByZXR1cm4gYmFkQmlkaVJlY3RzID0gKHIxLnJpZ2h0IC0gcjAucmlnaHQgPCAzKTtcclxuICB9XHJcblxyXG4gIC8vIFNlZSBpZiBcIlwiLnNwbGl0IGlzIHRoZSBicm9rZW4gSUUgdmVyc2lvbiwgaWYgc28sIHByb3ZpZGUgYW5cclxuICAvLyBhbHRlcm5hdGl2ZSB3YXkgdG8gc3BsaXQgbGluZXMuXHJcbiAgdmFyIHNwbGl0TGluZXNBdXRvID0gQ29kZU1pcnJvci5zcGxpdExpbmVzID0gXCJcXG5cXG5iXCIuc3BsaXQoL1xcbi8pLmxlbmd0aCAhPSAzID8gZnVuY3Rpb24oc3RyaW5nKSB7XHJcbiAgICB2YXIgcG9zID0gMCwgcmVzdWx0ID0gW10sIGwgPSBzdHJpbmcubGVuZ3RoO1xyXG4gICAgd2hpbGUgKHBvcyA8PSBsKSB7XHJcbiAgICAgIHZhciBubCA9IHN0cmluZy5pbmRleE9mKFwiXFxuXCIsIHBvcyk7XHJcbiAgICAgIGlmIChubCA9PSAtMSkgbmwgPSBzdHJpbmcubGVuZ3RoO1xyXG4gICAgICB2YXIgbGluZSA9IHN0cmluZy5zbGljZShwb3MsIHN0cmluZy5jaGFyQXQobmwgLSAxKSA9PSBcIlxcclwiID8gbmwgLSAxIDogbmwpO1xyXG4gICAgICB2YXIgcnQgPSBsaW5lLmluZGV4T2YoXCJcXHJcIik7XHJcbiAgICAgIGlmIChydCAhPSAtMSkge1xyXG4gICAgICAgIHJlc3VsdC5wdXNoKGxpbmUuc2xpY2UoMCwgcnQpKTtcclxuICAgICAgICBwb3MgKz0gcnQgKyAxO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHJlc3VsdC5wdXNoKGxpbmUpO1xyXG4gICAgICAgIHBvcyA9IG5sICsgMTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgcmV0dXJuIHJlc3VsdDtcclxuICB9IDogZnVuY3Rpb24oc3RyaW5nKXtyZXR1cm4gc3RyaW5nLnNwbGl0KC9cXHJcXG4/fFxcbi8pO307XHJcblxyXG4gIHZhciBoYXNTZWxlY3Rpb24gPSB3aW5kb3cuZ2V0U2VsZWN0aW9uID8gZnVuY3Rpb24odGUpIHtcclxuICAgIHRyeSB7IHJldHVybiB0ZS5zZWxlY3Rpb25TdGFydCAhPSB0ZS5zZWxlY3Rpb25FbmQ7IH1cclxuICAgIGNhdGNoKGUpIHsgcmV0dXJuIGZhbHNlOyB9XHJcbiAgfSA6IGZ1bmN0aW9uKHRlKSB7XHJcbiAgICB0cnkge3ZhciByYW5nZSA9IHRlLm93bmVyRG9jdW1lbnQuc2VsZWN0aW9uLmNyZWF0ZVJhbmdlKCk7fVxyXG4gICAgY2F0Y2goZSkge31cclxuICAgIGlmICghcmFuZ2UgfHwgcmFuZ2UucGFyZW50RWxlbWVudCgpICE9IHRlKSByZXR1cm4gZmFsc2U7XHJcbiAgICByZXR1cm4gcmFuZ2UuY29tcGFyZUVuZFBvaW50cyhcIlN0YXJ0VG9FbmRcIiwgcmFuZ2UpICE9IDA7XHJcbiAgfTtcclxuXHJcbiAgdmFyIGhhc0NvcHlFdmVudCA9IChmdW5jdGlvbigpIHtcclxuICAgIHZhciBlID0gZWx0KFwiZGl2XCIpO1xyXG4gICAgaWYgKFwib25jb3B5XCIgaW4gZSkgcmV0dXJuIHRydWU7XHJcbiAgICBlLnNldEF0dHJpYnV0ZShcIm9uY29weVwiLCBcInJldHVybjtcIik7XHJcbiAgICByZXR1cm4gdHlwZW9mIGUub25jb3B5ID09IFwiZnVuY3Rpb25cIjtcclxuICB9KSgpO1xyXG5cclxuICB2YXIgYmFkWm9vbWVkUmVjdHMgPSBudWxsO1xyXG4gIGZ1bmN0aW9uIGhhc0JhZFpvb21lZFJlY3RzKG1lYXN1cmUpIHtcclxuICAgIGlmIChiYWRab29tZWRSZWN0cyAhPSBudWxsKSByZXR1cm4gYmFkWm9vbWVkUmVjdHM7XHJcbiAgICB2YXIgbm9kZSA9IHJlbW92ZUNoaWxkcmVuQW5kQWRkKG1lYXN1cmUsIGVsdChcInNwYW5cIiwgXCJ4XCIpKTtcclxuICAgIHZhciBub3JtYWwgPSBub2RlLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xyXG4gICAgdmFyIGZyb21SYW5nZSA9IHJhbmdlKG5vZGUsIDAsIDEpLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xyXG4gICAgcmV0dXJuIGJhZFpvb21lZFJlY3RzID0gTWF0aC5hYnMobm9ybWFsLmxlZnQgLSBmcm9tUmFuZ2UubGVmdCkgPiAxO1xyXG4gIH1cclxuXHJcbiAgLy8gS0VZIE5BTUVTXHJcblxyXG4gIHZhciBrZXlOYW1lcyA9IENvZGVNaXJyb3Iua2V5TmFtZXMgPSB7XHJcbiAgICAzOiBcIkVudGVyXCIsIDg6IFwiQmFja3NwYWNlXCIsIDk6IFwiVGFiXCIsIDEzOiBcIkVudGVyXCIsIDE2OiBcIlNoaWZ0XCIsIDE3OiBcIkN0cmxcIiwgMTg6IFwiQWx0XCIsXHJcbiAgICAxOTogXCJQYXVzZVwiLCAyMDogXCJDYXBzTG9ja1wiLCAyNzogXCJFc2NcIiwgMzI6IFwiU3BhY2VcIiwgMzM6IFwiUGFnZVVwXCIsIDM0OiBcIlBhZ2VEb3duXCIsIDM1OiBcIkVuZFwiLFxyXG4gICAgMzY6IFwiSG9tZVwiLCAzNzogXCJMZWZ0XCIsIDM4OiBcIlVwXCIsIDM5OiBcIlJpZ2h0XCIsIDQwOiBcIkRvd25cIiwgNDQ6IFwiUHJpbnRTY3JuXCIsIDQ1OiBcIkluc2VydFwiLFxyXG4gICAgNDY6IFwiRGVsZXRlXCIsIDU5OiBcIjtcIiwgNjE6IFwiPVwiLCA5MTogXCJNb2RcIiwgOTI6IFwiTW9kXCIsIDkzOiBcIk1vZFwiLFxyXG4gICAgMTA2OiBcIipcIiwgMTA3OiBcIj1cIiwgMTA5OiBcIi1cIiwgMTEwOiBcIi5cIiwgMTExOiBcIi9cIiwgMTI3OiBcIkRlbGV0ZVwiLFxyXG4gICAgMTczOiBcIi1cIiwgMTg2OiBcIjtcIiwgMTg3OiBcIj1cIiwgMTg4OiBcIixcIiwgMTg5OiBcIi1cIiwgMTkwOiBcIi5cIiwgMTkxOiBcIi9cIiwgMTkyOiBcImBcIiwgMjE5OiBcIltcIiwgMjIwOiBcIlxcXFxcIixcclxuICAgIDIyMTogXCJdXCIsIDIyMjogXCInXCIsIDYzMjMyOiBcIlVwXCIsIDYzMjMzOiBcIkRvd25cIiwgNjMyMzQ6IFwiTGVmdFwiLCA2MzIzNTogXCJSaWdodFwiLCA2MzI3MjogXCJEZWxldGVcIixcclxuICAgIDYzMjczOiBcIkhvbWVcIiwgNjMyNzU6IFwiRW5kXCIsIDYzMjc2OiBcIlBhZ2VVcFwiLCA2MzI3NzogXCJQYWdlRG93blwiLCA2MzMwMjogXCJJbnNlcnRcIlxyXG4gIH07XHJcbiAgKGZ1bmN0aW9uKCkge1xyXG4gICAgLy8gTnVtYmVyIGtleXNcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgMTA7IGkrKykga2V5TmFtZXNbaSArIDQ4XSA9IGtleU5hbWVzW2kgKyA5Nl0gPSBTdHJpbmcoaSk7XHJcbiAgICAvLyBBbHBoYWJldGljIGtleXNcclxuICAgIGZvciAodmFyIGkgPSA2NTsgaSA8PSA5MDsgaSsrKSBrZXlOYW1lc1tpXSA9IFN0cmluZy5mcm9tQ2hhckNvZGUoaSk7XHJcbiAgICAvLyBGdW5jdGlvbiBrZXlzXHJcbiAgICBmb3IgKHZhciBpID0gMTsgaSA8PSAxMjsgaSsrKSBrZXlOYW1lc1tpICsgMTExXSA9IGtleU5hbWVzW2kgKyA2MzIzNV0gPSBcIkZcIiArIGk7XHJcbiAgfSkoKTtcclxuXHJcbiAgLy8gQklESSBIRUxQRVJTXHJcblxyXG4gIGZ1bmN0aW9uIGl0ZXJhdGVCaWRpU2VjdGlvbnMob3JkZXIsIGZyb20sIHRvLCBmKSB7XHJcbiAgICBpZiAoIW9yZGVyKSByZXR1cm4gZihmcm9tLCB0bywgXCJsdHJcIik7XHJcbiAgICB2YXIgZm91bmQgPSBmYWxzZTtcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgb3JkZXIubGVuZ3RoOyArK2kpIHtcclxuICAgICAgdmFyIHBhcnQgPSBvcmRlcltpXTtcclxuICAgICAgaWYgKHBhcnQuZnJvbSA8IHRvICYmIHBhcnQudG8gPiBmcm9tIHx8IGZyb20gPT0gdG8gJiYgcGFydC50byA9PSBmcm9tKSB7XHJcbiAgICAgICAgZihNYXRoLm1heChwYXJ0LmZyb20sIGZyb20pLCBNYXRoLm1pbihwYXJ0LnRvLCB0byksIHBhcnQubGV2ZWwgPT0gMSA/IFwicnRsXCIgOiBcImx0clwiKTtcclxuICAgICAgICBmb3VuZCA9IHRydWU7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIGlmICghZm91bmQpIGYoZnJvbSwgdG8sIFwibHRyXCIpO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gYmlkaUxlZnQocGFydCkgeyByZXR1cm4gcGFydC5sZXZlbCAlIDIgPyBwYXJ0LnRvIDogcGFydC5mcm9tOyB9XHJcbiAgZnVuY3Rpb24gYmlkaVJpZ2h0KHBhcnQpIHsgcmV0dXJuIHBhcnQubGV2ZWwgJSAyID8gcGFydC5mcm9tIDogcGFydC50bzsgfVxyXG5cclxuICBmdW5jdGlvbiBsaW5lTGVmdChsaW5lKSB7IHZhciBvcmRlciA9IGdldE9yZGVyKGxpbmUpOyByZXR1cm4gb3JkZXIgPyBiaWRpTGVmdChvcmRlclswXSkgOiAwOyB9XHJcbiAgZnVuY3Rpb24gbGluZVJpZ2h0KGxpbmUpIHtcclxuICAgIHZhciBvcmRlciA9IGdldE9yZGVyKGxpbmUpO1xyXG4gICAgaWYgKCFvcmRlcikgcmV0dXJuIGxpbmUudGV4dC5sZW5ndGg7XHJcbiAgICByZXR1cm4gYmlkaVJpZ2h0KGxzdChvcmRlcikpO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gbGluZVN0YXJ0KGNtLCBsaW5lTikge1xyXG4gICAgdmFyIGxpbmUgPSBnZXRMaW5lKGNtLmRvYywgbGluZU4pO1xyXG4gICAgdmFyIHZpc3VhbCA9IHZpc3VhbExpbmUobGluZSk7XHJcbiAgICBpZiAodmlzdWFsICE9IGxpbmUpIGxpbmVOID0gbGluZU5vKHZpc3VhbCk7XHJcbiAgICB2YXIgb3JkZXIgPSBnZXRPcmRlcih2aXN1YWwpO1xyXG4gICAgdmFyIGNoID0gIW9yZGVyID8gMCA6IG9yZGVyWzBdLmxldmVsICUgMiA/IGxpbmVSaWdodCh2aXN1YWwpIDogbGluZUxlZnQodmlzdWFsKTtcclxuICAgIHJldHVybiBQb3MobGluZU4sIGNoKTtcclxuICB9XHJcbiAgZnVuY3Rpb24gbGluZUVuZChjbSwgbGluZU4pIHtcclxuICAgIHZhciBtZXJnZWQsIGxpbmUgPSBnZXRMaW5lKGNtLmRvYywgbGluZU4pO1xyXG4gICAgd2hpbGUgKG1lcmdlZCA9IGNvbGxhcHNlZFNwYW5BdEVuZChsaW5lKSkge1xyXG4gICAgICBsaW5lID0gbWVyZ2VkLmZpbmQoMSwgdHJ1ZSkubGluZTtcclxuICAgICAgbGluZU4gPSBudWxsO1xyXG4gICAgfVxyXG4gICAgdmFyIG9yZGVyID0gZ2V0T3JkZXIobGluZSk7XHJcbiAgICB2YXIgY2ggPSAhb3JkZXIgPyBsaW5lLnRleHQubGVuZ3RoIDogb3JkZXJbMF0ubGV2ZWwgJSAyID8gbGluZUxlZnQobGluZSkgOiBsaW5lUmlnaHQobGluZSk7XHJcbiAgICByZXR1cm4gUG9zKGxpbmVOID09IG51bGwgPyBsaW5lTm8obGluZSkgOiBsaW5lTiwgY2gpO1xyXG4gIH1cclxuICBmdW5jdGlvbiBsaW5lU3RhcnRTbWFydChjbSwgcG9zKSB7XHJcbiAgICB2YXIgc3RhcnQgPSBsaW5lU3RhcnQoY20sIHBvcy5saW5lKTtcclxuICAgIHZhciBsaW5lID0gZ2V0TGluZShjbS5kb2MsIHN0YXJ0LmxpbmUpO1xyXG4gICAgdmFyIG9yZGVyID0gZ2V0T3JkZXIobGluZSk7XHJcbiAgICBpZiAoIW9yZGVyIHx8IG9yZGVyWzBdLmxldmVsID09IDApIHtcclxuICAgICAgdmFyIGZpcnN0Tm9uV1MgPSBNYXRoLm1heCgwLCBsaW5lLnRleHQuc2VhcmNoKC9cXFMvKSk7XHJcbiAgICAgIHZhciBpbldTID0gcG9zLmxpbmUgPT0gc3RhcnQubGluZSAmJiBwb3MuY2ggPD0gZmlyc3ROb25XUyAmJiBwb3MuY2g7XHJcbiAgICAgIHJldHVybiBQb3Moc3RhcnQubGluZSwgaW5XUyA/IDAgOiBmaXJzdE5vbldTKTtcclxuICAgIH1cclxuICAgIHJldHVybiBzdGFydDtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIGNvbXBhcmVCaWRpTGV2ZWwob3JkZXIsIGEsIGIpIHtcclxuICAgIHZhciBsaW5lZGlyID0gb3JkZXJbMF0ubGV2ZWw7XHJcbiAgICBpZiAoYSA9PSBsaW5lZGlyKSByZXR1cm4gdHJ1ZTtcclxuICAgIGlmIChiID09IGxpbmVkaXIpIHJldHVybiBmYWxzZTtcclxuICAgIHJldHVybiBhIDwgYjtcclxuICB9XHJcbiAgdmFyIGJpZGlPdGhlcjtcclxuICBmdW5jdGlvbiBnZXRCaWRpUGFydEF0KG9yZGVyLCBwb3MpIHtcclxuICAgIGJpZGlPdGhlciA9IG51bGw7XHJcbiAgICBmb3IgKHZhciBpID0gMCwgZm91bmQ7IGkgPCBvcmRlci5sZW5ndGg7ICsraSkge1xyXG4gICAgICB2YXIgY3VyID0gb3JkZXJbaV07XHJcbiAgICAgIGlmIChjdXIuZnJvbSA8IHBvcyAmJiBjdXIudG8gPiBwb3MpIHJldHVybiBpO1xyXG4gICAgICBpZiAoKGN1ci5mcm9tID09IHBvcyB8fCBjdXIudG8gPT0gcG9zKSkge1xyXG4gICAgICAgIGlmIChmb3VuZCA9PSBudWxsKSB7XHJcbiAgICAgICAgICBmb3VuZCA9IGk7XHJcbiAgICAgICAgfSBlbHNlIGlmIChjb21wYXJlQmlkaUxldmVsKG9yZGVyLCBjdXIubGV2ZWwsIG9yZGVyW2ZvdW5kXS5sZXZlbCkpIHtcclxuICAgICAgICAgIGlmIChjdXIuZnJvbSAhPSBjdXIudG8pIGJpZGlPdGhlciA9IGZvdW5kO1xyXG4gICAgICAgICAgcmV0dXJuIGk7XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgIGlmIChjdXIuZnJvbSAhPSBjdXIudG8pIGJpZGlPdGhlciA9IGk7XHJcbiAgICAgICAgICByZXR1cm4gZm91bmQ7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICByZXR1cm4gZm91bmQ7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBtb3ZlSW5MaW5lKGxpbmUsIHBvcywgZGlyLCBieVVuaXQpIHtcclxuICAgIGlmICghYnlVbml0KSByZXR1cm4gcG9zICsgZGlyO1xyXG4gICAgZG8gcG9zICs9IGRpcjtcclxuICAgIHdoaWxlIChwb3MgPiAwICYmIGlzRXh0ZW5kaW5nQ2hhcihsaW5lLnRleHQuY2hhckF0KHBvcykpKTtcclxuICAgIHJldHVybiBwb3M7XHJcbiAgfVxyXG5cclxuICAvLyBUaGlzIGlzIG5lZWRlZCBpbiBvcmRlciB0byBtb3ZlICd2aXN1YWxseScgdGhyb3VnaCBiaS1kaXJlY3Rpb25hbFxyXG4gIC8vIHRleHQgLS0gaS5lLiwgcHJlc3NpbmcgbGVmdCBzaG91bGQgbWFrZSB0aGUgY3Vyc29yIGdvIGxlZnQsIGV2ZW5cclxuICAvLyB3aGVuIGluIFJUTCB0ZXh0LiBUaGUgdHJpY2t5IHBhcnQgaXMgdGhlICdqdW1wcycsIHdoZXJlIFJUTCBhbmRcclxuICAvLyBMVFIgdGV4dCB0b3VjaCBlYWNoIG90aGVyLiBUaGlzIG9mdGVuIHJlcXVpcmVzIHRoZSBjdXJzb3Igb2Zmc2V0XHJcbiAgLy8gdG8gbW92ZSBtb3JlIHRoYW4gb25lIHVuaXQsIGluIG9yZGVyIHRvIHZpc3VhbGx5IG1vdmUgb25lIHVuaXQuXHJcbiAgZnVuY3Rpb24gbW92ZVZpc3VhbGx5KGxpbmUsIHN0YXJ0LCBkaXIsIGJ5VW5pdCkge1xyXG4gICAgdmFyIGJpZGkgPSBnZXRPcmRlcihsaW5lKTtcclxuICAgIGlmICghYmlkaSkgcmV0dXJuIG1vdmVMb2dpY2FsbHkobGluZSwgc3RhcnQsIGRpciwgYnlVbml0KTtcclxuICAgIHZhciBwb3MgPSBnZXRCaWRpUGFydEF0KGJpZGksIHN0YXJ0KSwgcGFydCA9IGJpZGlbcG9zXTtcclxuICAgIHZhciB0YXJnZXQgPSBtb3ZlSW5MaW5lKGxpbmUsIHN0YXJ0LCBwYXJ0LmxldmVsICUgMiA/IC1kaXIgOiBkaXIsIGJ5VW5pdCk7XHJcblxyXG4gICAgZm9yICg7Oykge1xyXG4gICAgICBpZiAodGFyZ2V0ID4gcGFydC5mcm9tICYmIHRhcmdldCA8IHBhcnQudG8pIHJldHVybiB0YXJnZXQ7XHJcbiAgICAgIGlmICh0YXJnZXQgPT0gcGFydC5mcm9tIHx8IHRhcmdldCA9PSBwYXJ0LnRvKSB7XHJcbiAgICAgICAgaWYgKGdldEJpZGlQYXJ0QXQoYmlkaSwgdGFyZ2V0KSA9PSBwb3MpIHJldHVybiB0YXJnZXQ7XHJcbiAgICAgICAgcGFydCA9IGJpZGlbcG9zICs9IGRpcl07XHJcbiAgICAgICAgcmV0dXJuIChkaXIgPiAwKSA9PSBwYXJ0LmxldmVsICUgMiA/IHBhcnQudG8gOiBwYXJ0LmZyb207XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgcGFydCA9IGJpZGlbcG9zICs9IGRpcl07XHJcbiAgICAgICAgaWYgKCFwYXJ0KSByZXR1cm4gbnVsbDtcclxuICAgICAgICBpZiAoKGRpciA+IDApID09IHBhcnQubGV2ZWwgJSAyKVxyXG4gICAgICAgICAgdGFyZ2V0ID0gbW92ZUluTGluZShsaW5lLCBwYXJ0LnRvLCAtMSwgYnlVbml0KTtcclxuICAgICAgICBlbHNlXHJcbiAgICAgICAgICB0YXJnZXQgPSBtb3ZlSW5MaW5lKGxpbmUsIHBhcnQuZnJvbSwgMSwgYnlVbml0KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gbW92ZUxvZ2ljYWxseShsaW5lLCBzdGFydCwgZGlyLCBieVVuaXQpIHtcclxuICAgIHZhciB0YXJnZXQgPSBzdGFydCArIGRpcjtcclxuICAgIGlmIChieVVuaXQpIHdoaWxlICh0YXJnZXQgPiAwICYmIGlzRXh0ZW5kaW5nQ2hhcihsaW5lLnRleHQuY2hhckF0KHRhcmdldCkpKSB0YXJnZXQgKz0gZGlyO1xyXG4gICAgcmV0dXJuIHRhcmdldCA8IDAgfHwgdGFyZ2V0ID4gbGluZS50ZXh0Lmxlbmd0aCA/IG51bGwgOiB0YXJnZXQ7XHJcbiAgfVxyXG5cclxuICAvLyBCaWRpcmVjdGlvbmFsIG9yZGVyaW5nIGFsZ29yaXRobVxyXG4gIC8vIFNlZSBodHRwOi8vdW5pY29kZS5vcmcvcmVwb3J0cy90cjkvdHI5LTEzLmh0bWwgZm9yIHRoZSBhbGdvcml0aG1cclxuICAvLyB0aGF0IHRoaXMgKHBhcnRpYWxseSkgaW1wbGVtZW50cy5cclxuXHJcbiAgLy8gT25lLWNoYXIgY29kZXMgdXNlZCBmb3IgY2hhcmFjdGVyIHR5cGVzOlxyXG4gIC8vIEwgKEwpOiAgIExlZnQtdG8tUmlnaHRcclxuICAvLyBSIChSKTogICBSaWdodC10by1MZWZ0XHJcbiAgLy8gciAoQUwpOiAgUmlnaHQtdG8tTGVmdCBBcmFiaWNcclxuICAvLyAxIChFTik6ICBFdXJvcGVhbiBOdW1iZXJcclxuICAvLyArIChFUyk6ICBFdXJvcGVhbiBOdW1iZXIgU2VwYXJhdG9yXHJcbiAgLy8gJSAoRVQpOiAgRXVyb3BlYW4gTnVtYmVyIFRlcm1pbmF0b3JcclxuICAvLyBuIChBTik6ICBBcmFiaWMgTnVtYmVyXHJcbiAgLy8gLCAoQ1MpOiAgQ29tbW9uIE51bWJlciBTZXBhcmF0b3JcclxuICAvLyBtIChOU00pOiBOb24tU3BhY2luZyBNYXJrXHJcbiAgLy8gYiAoQk4pOiAgQm91bmRhcnkgTmV1dHJhbFxyXG4gIC8vIHMgKEIpOiAgIFBhcmFncmFwaCBTZXBhcmF0b3JcclxuICAvLyB0IChTKTogICBTZWdtZW50IFNlcGFyYXRvclxyXG4gIC8vIHcgKFdTKTogIFdoaXRlc3BhY2VcclxuICAvLyBOIChPTik6ICBPdGhlciBOZXV0cmFsc1xyXG5cclxuICAvLyBSZXR1cm5zIG51bGwgaWYgY2hhcmFjdGVycyBhcmUgb3JkZXJlZCBhcyB0aGV5IGFwcGVhclxyXG4gIC8vIChsZWZ0LXRvLXJpZ2h0KSwgb3IgYW4gYXJyYXkgb2Ygc2VjdGlvbnMgKHtmcm9tLCB0bywgbGV2ZWx9XHJcbiAgLy8gb2JqZWN0cykgaW4gdGhlIG9yZGVyIGluIHdoaWNoIHRoZXkgb2NjdXIgdmlzdWFsbHkuXHJcbiAgdmFyIGJpZGlPcmRlcmluZyA9IChmdW5jdGlvbigpIHtcclxuICAgIC8vIENoYXJhY3RlciB0eXBlcyBmb3IgY29kZXBvaW50cyAwIHRvIDB4ZmZcclxuICAgIHZhciBsb3dUeXBlcyA9IFwiYmJiYmJiYmJidHN0d3NiYmJiYmJiYmJiYmJiYnNzc3R3Tk4lJSVOTk5OTk4sTixOMTExMTExMTExMU5OTk5OTk5MTExMTExMTExMTExMTExMTExMTExMTExMTE5OTk5OTkxMTExMTExMTExMTExMTExMTExMTExMTExMTk5OTmJiYmJiYnNiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYixOJSUlJU5OTk5MTk5OTk4lJTExTkxOTk4xTE5OTk5OTExMTExMTExMTExMTExMTExMTExMTExOTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE5cIjtcclxuICAgIC8vIENoYXJhY3RlciB0eXBlcyBmb3IgY29kZXBvaW50cyAweDYwMCB0byAweDZmZlxyXG4gICAgdmFyIGFyYWJpY1R5cGVzID0gXCJycnJycnJycnJycnIsck5ObW1tbW1tcnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJtbW1tbW1tbW1tbW1tbXJycnJycnJubm5ubm5ubm5uJW5ucnJybXJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJybW1tbW1tbW1tbW1tbW1tbW1tbU5tbW1tXCI7XHJcbiAgICBmdW5jdGlvbiBjaGFyVHlwZShjb2RlKSB7XHJcbiAgICAgIGlmIChjb2RlIDw9IDB4ZjcpIHJldHVybiBsb3dUeXBlcy5jaGFyQXQoY29kZSk7XHJcbiAgICAgIGVsc2UgaWYgKDB4NTkwIDw9IGNvZGUgJiYgY29kZSA8PSAweDVmNCkgcmV0dXJuIFwiUlwiO1xyXG4gICAgICBlbHNlIGlmICgweDYwMCA8PSBjb2RlICYmIGNvZGUgPD0gMHg2ZWQpIHJldHVybiBhcmFiaWNUeXBlcy5jaGFyQXQoY29kZSAtIDB4NjAwKTtcclxuICAgICAgZWxzZSBpZiAoMHg2ZWUgPD0gY29kZSAmJiBjb2RlIDw9IDB4OGFjKSByZXR1cm4gXCJyXCI7XHJcbiAgICAgIGVsc2UgaWYgKDB4MjAwMCA8PSBjb2RlICYmIGNvZGUgPD0gMHgyMDBiKSByZXR1cm4gXCJ3XCI7XHJcbiAgICAgIGVsc2UgaWYgKGNvZGUgPT0gMHgyMDBjKSByZXR1cm4gXCJiXCI7XHJcbiAgICAgIGVsc2UgcmV0dXJuIFwiTFwiO1xyXG4gICAgfVxyXG5cclxuICAgIHZhciBiaWRpUkUgPSAvW1xcdTA1OTAtXFx1MDVmNFxcdTA2MDAtXFx1MDZmZlxcdTA3MDAtXFx1MDhhY10vO1xyXG4gICAgdmFyIGlzTmV1dHJhbCA9IC9bc3R3Tl0vLCBpc1N0cm9uZyA9IC9bTFJyXS8sIGNvdW50c0FzTGVmdCA9IC9bTGIxbl0vLCBjb3VudHNBc051bSA9IC9bMW5dLztcclxuICAgIC8vIEJyb3dzZXJzIHNlZW0gdG8gYWx3YXlzIHRyZWF0IHRoZSBib3VuZGFyaWVzIG9mIGJsb2NrIGVsZW1lbnRzIGFzIGJlaW5nIEwuXHJcbiAgICB2YXIgb3V0ZXJUeXBlID0gXCJMXCI7XHJcblxyXG4gICAgZnVuY3Rpb24gQmlkaVNwYW4obGV2ZWwsIGZyb20sIHRvKSB7XHJcbiAgICAgIHRoaXMubGV2ZWwgPSBsZXZlbDtcclxuICAgICAgdGhpcy5mcm9tID0gZnJvbTsgdGhpcy50byA9IHRvO1xyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiBmdW5jdGlvbihzdHIpIHtcclxuICAgICAgaWYgKCFiaWRpUkUudGVzdChzdHIpKSByZXR1cm4gZmFsc2U7XHJcbiAgICAgIHZhciBsZW4gPSBzdHIubGVuZ3RoLCB0eXBlcyA9IFtdO1xyXG4gICAgICBmb3IgKHZhciBpID0gMCwgdHlwZTsgaSA8IGxlbjsgKytpKVxyXG4gICAgICAgIHR5cGVzLnB1c2godHlwZSA9IGNoYXJUeXBlKHN0ci5jaGFyQ29kZUF0KGkpKSk7XHJcblxyXG4gICAgICAvLyBXMS4gRXhhbWluZSBlYWNoIG5vbi1zcGFjaW5nIG1hcmsgKE5TTSkgaW4gdGhlIGxldmVsIHJ1biwgYW5kXHJcbiAgICAgIC8vIGNoYW5nZSB0aGUgdHlwZSBvZiB0aGUgTlNNIHRvIHRoZSB0eXBlIG9mIHRoZSBwcmV2aW91c1xyXG4gICAgICAvLyBjaGFyYWN0ZXIuIElmIHRoZSBOU00gaXMgYXQgdGhlIHN0YXJ0IG9mIHRoZSBsZXZlbCBydW4sIGl0IHdpbGxcclxuICAgICAgLy8gZ2V0IHRoZSB0eXBlIG9mIHNvci5cclxuICAgICAgZm9yICh2YXIgaSA9IDAsIHByZXYgPSBvdXRlclR5cGU7IGkgPCBsZW47ICsraSkge1xyXG4gICAgICAgIHZhciB0eXBlID0gdHlwZXNbaV07XHJcbiAgICAgICAgaWYgKHR5cGUgPT0gXCJtXCIpIHR5cGVzW2ldID0gcHJldjtcclxuICAgICAgICBlbHNlIHByZXYgPSB0eXBlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBXMi4gU2VhcmNoIGJhY2t3YXJkcyBmcm9tIGVhY2ggaW5zdGFuY2Ugb2YgYSBFdXJvcGVhbiBudW1iZXJcclxuICAgICAgLy8gdW50aWwgdGhlIGZpcnN0IHN0cm9uZyB0eXBlIChSLCBMLCBBTCwgb3Igc29yKSBpcyBmb3VuZC4gSWYgYW5cclxuICAgICAgLy8gQUwgaXMgZm91bmQsIGNoYW5nZSB0aGUgdHlwZSBvZiB0aGUgRXVyb3BlYW4gbnVtYmVyIHRvIEFyYWJpY1xyXG4gICAgICAvLyBudW1iZXIuXHJcbiAgICAgIC8vIFczLiBDaGFuZ2UgYWxsIEFMcyB0byBSLlxyXG4gICAgICBmb3IgKHZhciBpID0gMCwgY3VyID0gb3V0ZXJUeXBlOyBpIDwgbGVuOyArK2kpIHtcclxuICAgICAgICB2YXIgdHlwZSA9IHR5cGVzW2ldO1xyXG4gICAgICAgIGlmICh0eXBlID09IFwiMVwiICYmIGN1ciA9PSBcInJcIikgdHlwZXNbaV0gPSBcIm5cIjtcclxuICAgICAgICBlbHNlIGlmIChpc1N0cm9uZy50ZXN0KHR5cGUpKSB7IGN1ciA9IHR5cGU7IGlmICh0eXBlID09IFwiclwiKSB0eXBlc1tpXSA9IFwiUlwiOyB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFc0LiBBIHNpbmdsZSBFdXJvcGVhbiBzZXBhcmF0b3IgYmV0d2VlbiB0d28gRXVyb3BlYW4gbnVtYmVyc1xyXG4gICAgICAvLyBjaGFuZ2VzIHRvIGEgRXVyb3BlYW4gbnVtYmVyLiBBIHNpbmdsZSBjb21tb24gc2VwYXJhdG9yIGJldHdlZW5cclxuICAgICAgLy8gdHdvIG51bWJlcnMgb2YgdGhlIHNhbWUgdHlwZSBjaGFuZ2VzIHRvIHRoYXQgdHlwZS5cclxuICAgICAgZm9yICh2YXIgaSA9IDEsIHByZXYgPSB0eXBlc1swXTsgaSA8IGxlbiAtIDE7ICsraSkge1xyXG4gICAgICAgIHZhciB0eXBlID0gdHlwZXNbaV07XHJcbiAgICAgICAgaWYgKHR5cGUgPT0gXCIrXCIgJiYgcHJldiA9PSBcIjFcIiAmJiB0eXBlc1tpKzFdID09IFwiMVwiKSB0eXBlc1tpXSA9IFwiMVwiO1xyXG4gICAgICAgIGVsc2UgaWYgKHR5cGUgPT0gXCIsXCIgJiYgcHJldiA9PSB0eXBlc1tpKzFdICYmXHJcbiAgICAgICAgICAgICAgICAgKHByZXYgPT0gXCIxXCIgfHwgcHJldiA9PSBcIm5cIikpIHR5cGVzW2ldID0gcHJldjtcclxuICAgICAgICBwcmV2ID0gdHlwZTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gVzUuIEEgc2VxdWVuY2Ugb2YgRXVyb3BlYW4gdGVybWluYXRvcnMgYWRqYWNlbnQgdG8gRXVyb3BlYW5cclxuICAgICAgLy8gbnVtYmVycyBjaGFuZ2VzIHRvIGFsbCBFdXJvcGVhbiBudW1iZXJzLlxyXG4gICAgICAvLyBXNi4gT3RoZXJ3aXNlLCBzZXBhcmF0b3JzIGFuZCB0ZXJtaW5hdG9ycyBjaGFuZ2UgdG8gT3RoZXJcclxuICAgICAgLy8gTmV1dHJhbC5cclxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsZW47ICsraSkge1xyXG4gICAgICAgIHZhciB0eXBlID0gdHlwZXNbaV07XHJcbiAgICAgICAgaWYgKHR5cGUgPT0gXCIsXCIpIHR5cGVzW2ldID0gXCJOXCI7XHJcbiAgICAgICAgZWxzZSBpZiAodHlwZSA9PSBcIiVcIikge1xyXG4gICAgICAgICAgZm9yICh2YXIgZW5kID0gaSArIDE7IGVuZCA8IGxlbiAmJiB0eXBlc1tlbmRdID09IFwiJVwiOyArK2VuZCkge31cclxuICAgICAgICAgIHZhciByZXBsYWNlID0gKGkgJiYgdHlwZXNbaS0xXSA9PSBcIiFcIikgfHwgKGVuZCA8IGxlbiAmJiB0eXBlc1tlbmRdID09IFwiMVwiKSA/IFwiMVwiIDogXCJOXCI7XHJcbiAgICAgICAgICBmb3IgKHZhciBqID0gaTsgaiA8IGVuZDsgKytqKSB0eXBlc1tqXSA9IHJlcGxhY2U7XHJcbiAgICAgICAgICBpID0gZW5kIC0gMTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFc3LiBTZWFyY2ggYmFja3dhcmRzIGZyb20gZWFjaCBpbnN0YW5jZSBvZiBhIEV1cm9wZWFuIG51bWJlclxyXG4gICAgICAvLyB1bnRpbCB0aGUgZmlyc3Qgc3Ryb25nIHR5cGUgKFIsIEwsIG9yIHNvcikgaXMgZm91bmQuIElmIGFuIEwgaXNcclxuICAgICAgLy8gZm91bmQsIHRoZW4gY2hhbmdlIHRoZSB0eXBlIG9mIHRoZSBFdXJvcGVhbiBudW1iZXIgdG8gTC5cclxuICAgICAgZm9yICh2YXIgaSA9IDAsIGN1ciA9IG91dGVyVHlwZTsgaSA8IGxlbjsgKytpKSB7XHJcbiAgICAgICAgdmFyIHR5cGUgPSB0eXBlc1tpXTtcclxuICAgICAgICBpZiAoY3VyID09IFwiTFwiICYmIHR5cGUgPT0gXCIxXCIpIHR5cGVzW2ldID0gXCJMXCI7XHJcbiAgICAgICAgZWxzZSBpZiAoaXNTdHJvbmcudGVzdCh0eXBlKSkgY3VyID0gdHlwZTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gTjEuIEEgc2VxdWVuY2Ugb2YgbmV1dHJhbHMgdGFrZXMgdGhlIGRpcmVjdGlvbiBvZiB0aGVcclxuICAgICAgLy8gc3Vycm91bmRpbmcgc3Ryb25nIHRleHQgaWYgdGhlIHRleHQgb24gYm90aCBzaWRlcyBoYXMgdGhlIHNhbWVcclxuICAgICAgLy8gZGlyZWN0aW9uLiBFdXJvcGVhbiBhbmQgQXJhYmljIG51bWJlcnMgYWN0IGFzIGlmIHRoZXkgd2VyZSBSIGluXHJcbiAgICAgIC8vIHRlcm1zIG9mIHRoZWlyIGluZmx1ZW5jZSBvbiBuZXV0cmFscy4gU3RhcnQtb2YtbGV2ZWwtcnVuIChzb3IpXHJcbiAgICAgIC8vIGFuZCBlbmQtb2YtbGV2ZWwtcnVuIChlb3IpIGFyZSB1c2VkIGF0IGxldmVsIHJ1biBib3VuZGFyaWVzLlxyXG4gICAgICAvLyBOMi4gQW55IHJlbWFpbmluZyBuZXV0cmFscyB0YWtlIHRoZSBlbWJlZGRpbmcgZGlyZWN0aW9uLlxyXG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxlbjsgKytpKSB7XHJcbiAgICAgICAgaWYgKGlzTmV1dHJhbC50ZXN0KHR5cGVzW2ldKSkge1xyXG4gICAgICAgICAgZm9yICh2YXIgZW5kID0gaSArIDE7IGVuZCA8IGxlbiAmJiBpc05ldXRyYWwudGVzdCh0eXBlc1tlbmRdKTsgKytlbmQpIHt9XHJcbiAgICAgICAgICB2YXIgYmVmb3JlID0gKGkgPyB0eXBlc1tpLTFdIDogb3V0ZXJUeXBlKSA9PSBcIkxcIjtcclxuICAgICAgICAgIHZhciBhZnRlciA9IChlbmQgPCBsZW4gPyB0eXBlc1tlbmRdIDogb3V0ZXJUeXBlKSA9PSBcIkxcIjtcclxuICAgICAgICAgIHZhciByZXBsYWNlID0gYmVmb3JlIHx8IGFmdGVyID8gXCJMXCIgOiBcIlJcIjtcclxuICAgICAgICAgIGZvciAodmFyIGogPSBpOyBqIDwgZW5kOyArK2opIHR5cGVzW2pdID0gcmVwbGFjZTtcclxuICAgICAgICAgIGkgPSBlbmQgLSAxO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSGVyZSB3ZSBkZXBhcnQgZnJvbSB0aGUgZG9jdW1lbnRlZCBhbGdvcml0aG0sIGluIG9yZGVyIHRvIGF2b2lkXHJcbiAgICAgIC8vIGJ1aWxkaW5nIHVwIGFuIGFjdHVhbCBsZXZlbHMgYXJyYXkuIFNpbmNlIHRoZXJlIGFyZSBvbmx5IHRocmVlXHJcbiAgICAgIC8vIGxldmVscyAoMCwgMSwgMikgaW4gYW4gaW1wbGVtZW50YXRpb24gdGhhdCBkb2Vzbid0IHRha2VcclxuICAgICAgLy8gZXhwbGljaXQgZW1iZWRkaW5nIGludG8gYWNjb3VudCwgd2UgY2FuIGJ1aWxkIHVwIHRoZSBvcmRlciBvblxyXG4gICAgICAvLyB0aGUgZmx5LCB3aXRob3V0IGZvbGxvd2luZyB0aGUgbGV2ZWwtYmFzZWQgYWxnb3JpdGhtLlxyXG4gICAgICB2YXIgb3JkZXIgPSBbXSwgbTtcclxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsZW47KSB7XHJcbiAgICAgICAgaWYgKGNvdW50c0FzTGVmdC50ZXN0KHR5cGVzW2ldKSkge1xyXG4gICAgICAgICAgdmFyIHN0YXJ0ID0gaTtcclxuICAgICAgICAgIGZvciAoKytpOyBpIDwgbGVuICYmIGNvdW50c0FzTGVmdC50ZXN0KHR5cGVzW2ldKTsgKytpKSB7fVxyXG4gICAgICAgICAgb3JkZXIucHVzaChuZXcgQmlkaVNwYW4oMCwgc3RhcnQsIGkpKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgdmFyIHBvcyA9IGksIGF0ID0gb3JkZXIubGVuZ3RoO1xyXG4gICAgICAgICAgZm9yICgrK2k7IGkgPCBsZW4gJiYgdHlwZXNbaV0gIT0gXCJMXCI7ICsraSkge31cclxuICAgICAgICAgIGZvciAodmFyIGogPSBwb3M7IGogPCBpOykge1xyXG4gICAgICAgICAgICBpZiAoY291bnRzQXNOdW0udGVzdCh0eXBlc1tqXSkpIHtcclxuICAgICAgICAgICAgICBpZiAocG9zIDwgaikgb3JkZXIuc3BsaWNlKGF0LCAwLCBuZXcgQmlkaVNwYW4oMSwgcG9zLCBqKSk7XHJcbiAgICAgICAgICAgICAgdmFyIG5zdGFydCA9IGo7XHJcbiAgICAgICAgICAgICAgZm9yICgrK2o7IGogPCBpICYmIGNvdW50c0FzTnVtLnRlc3QodHlwZXNbal0pOyArK2opIHt9XHJcbiAgICAgICAgICAgICAgb3JkZXIuc3BsaWNlKGF0LCAwLCBuZXcgQmlkaVNwYW4oMiwgbnN0YXJ0LCBqKSk7XHJcbiAgICAgICAgICAgICAgcG9zID0gajtcclxuICAgICAgICAgICAgfSBlbHNlICsrajtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIGlmIChwb3MgPCBpKSBvcmRlci5zcGxpY2UoYXQsIDAsIG5ldyBCaWRpU3BhbigxLCBwb3MsIGkpKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICAgaWYgKG9yZGVyWzBdLmxldmVsID09IDEgJiYgKG0gPSBzdHIubWF0Y2goL15cXHMrLykpKSB7XHJcbiAgICAgICAgb3JkZXJbMF0uZnJvbSA9IG1bMF0ubGVuZ3RoO1xyXG4gICAgICAgIG9yZGVyLnVuc2hpZnQobmV3IEJpZGlTcGFuKDAsIDAsIG1bMF0ubGVuZ3RoKSk7XHJcbiAgICAgIH1cclxuICAgICAgaWYgKGxzdChvcmRlcikubGV2ZWwgPT0gMSAmJiAobSA9IHN0ci5tYXRjaCgvXFxzKyQvKSkpIHtcclxuICAgICAgICBsc3Qob3JkZXIpLnRvIC09IG1bMF0ubGVuZ3RoO1xyXG4gICAgICAgIG9yZGVyLnB1c2gobmV3IEJpZGlTcGFuKDAsIGxlbiAtIG1bMF0ubGVuZ3RoLCBsZW4pKTtcclxuICAgICAgfVxyXG4gICAgICBpZiAob3JkZXJbMF0ubGV2ZWwgPT0gMilcclxuICAgICAgICBvcmRlci51bnNoaWZ0KG5ldyBCaWRpU3BhbigxLCBvcmRlclswXS50bywgb3JkZXJbMF0udG8pKTtcclxuICAgICAgaWYgKG9yZGVyWzBdLmxldmVsICE9IGxzdChvcmRlcikubGV2ZWwpXHJcbiAgICAgICAgb3JkZXIucHVzaChuZXcgQmlkaVNwYW4ob3JkZXJbMF0ubGV2ZWwsIGxlbiwgbGVuKSk7XHJcblxyXG4gICAgICByZXR1cm4gb3JkZXI7XHJcbiAgICB9O1xyXG4gIH0pKCk7XHJcblxyXG4gIC8vIFRIRSBFTkRcclxuXHJcbiAgQ29kZU1pcnJvci52ZXJzaW9uID0gXCI1LjcuMVwiO1xyXG5cclxuICByZXR1cm4gQ29kZU1pcnJvcjtcclxufSk7XHJcbiIsIi8vIENvZGVNaXJyb3IsIGNvcHlyaWdodCAoYykgYnkgTWFyaWpuIEhhdmVyYmVrZSBhbmQgb3RoZXJzXHJcbi8vIERpc3RyaWJ1dGVkIHVuZGVyIGFuIE1JVCBsaWNlbnNlOiBodHRwOi8vY29kZW1pcnJvci5uZXQvTElDRU5TRVxyXG5cclxuKGZ1bmN0aW9uKG1vZCkge1xyXG4gIGlmICh0eXBlb2YgZXhwb3J0cyA9PSBcIm9iamVjdFwiICYmIHR5cGVvZiBtb2R1bGUgPT0gXCJvYmplY3RcIikgLy8gQ29tbW9uSlNcclxuICAgIG1vZChyZXF1aXJlKFwiLi4vLi4vbGliL2NvZGVtaXJyb3JcIiksIHJlcXVpcmUoXCIuLi9tYXJrZG93bi9tYXJrZG93blwiKSwgcmVxdWlyZShcIi4uLy4uL2FkZG9uL21vZGUvb3ZlcmxheVwiKSk7XHJcbiAgZWxzZSBpZiAodHlwZW9mIGRlZmluZSA9PSBcImZ1bmN0aW9uXCIgJiYgZGVmaW5lLmFtZCkgLy8gQU1EXHJcbiAgICBkZWZpbmUoW1wiLi4vLi4vbGliL2NvZGVtaXJyb3JcIiwgXCIuLi9tYXJrZG93bi9tYXJrZG93blwiLCBcIi4uLy4uL2FkZG9uL21vZGUvb3ZlcmxheVwiXSwgbW9kKTtcclxuICBlbHNlIC8vIFBsYWluIGJyb3dzZXIgZW52XHJcbiAgICBtb2QoQ29kZU1pcnJvcik7XHJcbn0pKGZ1bmN0aW9uKENvZGVNaXJyb3IpIHtcclxuXCJ1c2Ugc3RyaWN0XCI7XHJcblxyXG52YXIgdXJsUkUgPSAvXigoPzooPzphYWFzP3xhYm91dHxhY2FwfGFkaXVteHRyYXxhZltwc118YWltfGFwdHxhdHRhY2htZW50fGF3fGJlc2hhcmV8Yml0Y29pbnxib2xvfGNhbGx0b3xjYXB8Y2hyb21lKD86LWV4dGVuc2lvbik/fGNpZHxjb2FwfGNvbS1ldmVudGJyaXRlLWF0dGVuZGVlfGNvbnRlbnR8Y3JpZHxjdnN8ZGF0YXxkYXZ8ZGljdHxkbG5hLSg/OnBsYXljb250YWluZXJ8cGxheXNpbmdsZSl8ZG5zfGRvaXxkdG58ZHZifGVkMmt8ZmFjZXRpbWV8ZmVlZHxmaWxlfGZpbmdlcnxmaXNofGZ0cHxnZW98Z2d8Z2l0fGdpem1vcHJvamVjdHxnb3xnb3BoZXJ8Z3RhbGt8aDMyM3xoY3B8aHR0cHM/fGlheHxpY2FwfGljb258aW18aW1hcHxpbmZvfGlwbnxpcHB8aXJjWzZzXT98aXJpcyg/OlxcLmJlZXB8XFwubHd6fFxcLnhwY3xcXC54cGNzKT98aXRtc3xqYXJ8amF2YXNjcmlwdHxqbXN8a2V5cGFyY3xsYXN0Zm18bGRhcHM/fG1hZ25ldHxtYWlsdG98bWFwc3xtYXJrZXR8bWVzc2FnZXxtaWR8bW1zfG1zLWhlbHB8bXNuaW18bXNycHM/fG10cXB8bXVtYmxlfG11cGRhdGV8bXZufG5ld3N8bmZzfG5paD98bm50cHxub3Rlc3xvaWR8b3BhcXVlbG9ja3Rva2VufHBhbG18cGFwYXJhenppfHBsYXRmb3JtfHBvcHxwcmVzfHByb3h5fHBzeWN8cXVlcnl8cmVzKD86b3VyY2UpP3xybWl8cnN5bmN8cnRtcHxydHNwfHNlY29uZGxpZmV8c2VydmljZXxzZXNzaW9ufHNmdHB8c2dufHNodHRwfHNpZXZlfHNpcHM/fHNreXBlfHNtW2JzXXxzbm1wfHNvYXBcXC5iZWVwcz98c29sZGF0fHNwb3RpZnl8c3NofHN0ZWFtfHN2bnx0YWd8dGVhbXNwZWFrfHRlbCg/Om5ldCk/fHRmdHB8dGhpbmdzfHRoaXNtZXNzYWdlfHRpcHx0bjMyNzB8dHZ8dWRwfHVucmVhbHx1cm58dXQyMDA0fHZlbW1pfHZlbnRyaWxvfHZpZXctc291cmNlfHdlYmNhbHx3c3M/fHd0YWl8d3ljaXd5Z3x4Y29uKD86LXVzZXJpZCk/fHhmaXJlfHhtbHJwY1xcLmJlZXBzP3x4bXBwfHhyaXx5bXNncnx6MzlcXC41MFtyc10/KTooPzpcXC97MSwzfXxbYS16MC05JV0pfHd3d1xcZHswLDN9Wy5dfFthLXowLTkuXFwtXStbLl1bYS16XXsyLDR9XFwvKSg/OlteXFxzKCk8Pl18XFwoW15cXHMoKTw+XSpcXCkpKyg/OlxcKFteXFxzKCk8Pl0qXFwpfFteXFxzYCohKClcXFtcXF17fTs6J1wiLiw8Pj/Cq8K74oCc4oCd4oCY4oCZXSkpL2lcclxuXHJcbkNvZGVNaXJyb3IuZGVmaW5lTW9kZShcImdmbVwiLCBmdW5jdGlvbihjb25maWcsIG1vZGVDb25maWcpIHtcclxuICB2YXIgY29kZURlcHRoID0gMDtcclxuICBmdW5jdGlvbiBibGFua0xpbmUoc3RhdGUpIHtcclxuICAgIHN0YXRlLmNvZGUgPSBmYWxzZTtcclxuICAgIHJldHVybiBudWxsO1xyXG4gIH1cclxuICB2YXIgZ2ZtT3ZlcmxheSA9IHtcclxuICAgIHN0YXJ0U3RhdGU6IGZ1bmN0aW9uKCkge1xyXG4gICAgICByZXR1cm4ge1xyXG4gICAgICAgIGNvZGU6IGZhbHNlLFxyXG4gICAgICAgIGNvZGVCbG9jazogZmFsc2UsXHJcbiAgICAgICAgYXRlU3BhY2U6IGZhbHNlXHJcbiAgICAgIH07XHJcbiAgICB9LFxyXG4gICAgY29weVN0YXRlOiBmdW5jdGlvbihzKSB7XHJcbiAgICAgIHJldHVybiB7XHJcbiAgICAgICAgY29kZTogcy5jb2RlLFxyXG4gICAgICAgIGNvZGVCbG9jazogcy5jb2RlQmxvY2ssXHJcbiAgICAgICAgYXRlU3BhY2U6IHMuYXRlU3BhY2VcclxuICAgICAgfTtcclxuICAgIH0sXHJcbiAgICB0b2tlbjogZnVuY3Rpb24oc3RyZWFtLCBzdGF0ZSkge1xyXG4gICAgICBzdGF0ZS5jb21iaW5lVG9rZW5zID0gbnVsbDtcclxuXHJcbiAgICAgIC8vIEhhY2sgdG8gcHJldmVudCBmb3JtYXR0aW5nIG92ZXJyaWRlIGluc2lkZSBjb2RlIGJsb2NrcyAoYmxvY2sgYW5kIGlubGluZSlcclxuICAgICAgaWYgKHN0YXRlLmNvZGVCbG9jaykge1xyXG4gICAgICAgIGlmIChzdHJlYW0ubWF0Y2goL15gYGArLykpIHtcclxuICAgICAgICAgIHN0YXRlLmNvZGVCbG9jayA9IGZhbHNlO1xyXG4gICAgICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHN0cmVhbS5za2lwVG9FbmQoKTtcclxuICAgICAgICByZXR1cm4gbnVsbDtcclxuICAgICAgfVxyXG4gICAgICBpZiAoc3RyZWFtLnNvbCgpKSB7XHJcbiAgICAgICAgc3RhdGUuY29kZSA9IGZhbHNlO1xyXG4gICAgICB9XHJcbiAgICAgIGlmIChzdHJlYW0uc29sKCkgJiYgc3RyZWFtLm1hdGNoKC9eYGBgKy8pKSB7XHJcbiAgICAgICAgc3RyZWFtLnNraXBUb0VuZCgpO1xyXG4gICAgICAgIHN0YXRlLmNvZGVCbG9jayA9IHRydWU7XHJcbiAgICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICAgIH1cclxuICAgICAgLy8gSWYgdGhpcyBibG9jayBpcyBjaGFuZ2VkLCBpdCBtYXkgbmVlZCB0byBiZSB1cGRhdGVkIGluIE1hcmtkb3duIG1vZGVcclxuICAgICAgaWYgKHN0cmVhbS5wZWVrKCkgPT09ICdgJykge1xyXG4gICAgICAgIHN0cmVhbS5uZXh0KCk7XHJcbiAgICAgICAgdmFyIGJlZm9yZSA9IHN0cmVhbS5wb3M7XHJcbiAgICAgICAgc3RyZWFtLmVhdFdoaWxlKCdgJyk7XHJcbiAgICAgICAgdmFyIGRpZmZlcmVuY2UgPSAxICsgc3RyZWFtLnBvcyAtIGJlZm9yZTtcclxuICAgICAgICBpZiAoIXN0YXRlLmNvZGUpIHtcclxuICAgICAgICAgIGNvZGVEZXB0aCA9IGRpZmZlcmVuY2U7XHJcbiAgICAgICAgICBzdGF0ZS5jb2RlID0gdHJ1ZTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgaWYgKGRpZmZlcmVuY2UgPT09IGNvZGVEZXB0aCkgeyAvLyBNdXN0IGJlIGV4YWN0XHJcbiAgICAgICAgICAgIHN0YXRlLmNvZGUgPSBmYWxzZTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICAgIH0gZWxzZSBpZiAoc3RhdGUuY29kZSkge1xyXG4gICAgICAgIHN0cmVhbS5uZXh0KCk7XHJcbiAgICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICAgIH1cclxuICAgICAgLy8gQ2hlY2sgaWYgc3BhY2UuIElmIHNvLCBsaW5rcyBjYW4gYmUgZm9ybWF0dGVkIGxhdGVyIG9uXHJcbiAgICAgIGlmIChzdHJlYW0uZWF0U3BhY2UoKSkge1xyXG4gICAgICAgIHN0YXRlLmF0ZVNwYWNlID0gdHJ1ZTtcclxuICAgICAgICByZXR1cm4gbnVsbDtcclxuICAgICAgfVxyXG4gICAgICBpZiAoc3RyZWFtLnNvbCgpIHx8IHN0YXRlLmF0ZVNwYWNlKSB7XHJcbiAgICAgICAgc3RhdGUuYXRlU3BhY2UgPSBmYWxzZTtcclxuICAgICAgICBpZiAobW9kZUNvbmZpZy5naXRIdWJTcGljZSAhPT0gZmFsc2UpIHtcclxuICAgICAgICAgIGlmKHN0cmVhbS5tYXRjaCgvXig/OlthLXpBLVowLTlcXC1fXStcXC8pPyg/OlthLXpBLVowLTlcXC1fXStAKT8oPzpbYS1mMC05XXs3LDQwfVxcYikvKSkge1xyXG4gICAgICAgICAgICAvLyBVc2VyL1Byb2plY3RAU0hBXHJcbiAgICAgICAgICAgIC8vIFVzZXJAU0hBXHJcbiAgICAgICAgICAgIC8vIFNIQVxyXG4gICAgICAgICAgICBzdGF0ZS5jb21iaW5lVG9rZW5zID0gdHJ1ZTtcclxuICAgICAgICAgICAgcmV0dXJuIFwibGlua1wiO1xyXG4gICAgICAgICAgfSBlbHNlIGlmIChzdHJlYW0ubWF0Y2goL14oPzpbYS16QS1aMC05XFwtX10rXFwvKT8oPzpbYS16QS1aMC05XFwtX10rKT8jWzAtOV0rXFxiLykpIHtcclxuICAgICAgICAgICAgLy8gVXNlci9Qcm9qZWN0I051bVxyXG4gICAgICAgICAgICAvLyBVc2VyI051bVxyXG4gICAgICAgICAgICAvLyAjTnVtXHJcbiAgICAgICAgICAgIHN0YXRlLmNvbWJpbmVUb2tlbnMgPSB0cnVlO1xyXG4gICAgICAgICAgICByZXR1cm4gXCJsaW5rXCI7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICAgIGlmIChzdHJlYW0ubWF0Y2godXJsUkUpICYmXHJcbiAgICAgICAgICBzdHJlYW0uc3RyaW5nLnNsaWNlKHN0cmVhbS5zdGFydCAtIDIsIHN0cmVhbS5zdGFydCkgIT0gXCJdKFwiICYmXHJcbiAgICAgICAgICAoc3RyZWFtLnN0YXJ0ID09IDAgfHwgL1xcVy8udGVzdChzdHJlYW0uc3RyaW5nLmNoYXJBdChzdHJlYW0uc3RhcnQgLSAxKSkpKSB7XHJcbiAgICAgICAgLy8gVVJMc1xyXG4gICAgICAgIC8vIFRha2VuIGZyb20gaHR0cDovL2RhcmluZ2ZpcmViYWxsLm5ldC8yMDEwLzA3L2ltcHJvdmVkX3JlZ2V4X2Zvcl9tYXRjaGluZ191cmxzXHJcbiAgICAgICAgLy8gQW5kIHRoZW4gKGlzc3VlICMxMTYwKSBzaW1wbGlmaWVkIHRvIG1ha2UgaXQgbm90IGNyYXNoIHRoZSBDaHJvbWUgUmVnZXhwIGVuZ2luZVxyXG4gICAgICAgIC8vIEFuZCB0aGVuIGxpbWl0ZWQgdXJsIHNjaGVtZXMgdG8gdGhlIENvbW1vbk1hcmsgbGlzdCwgc28gZm9vOmJhciBpc24ndCBtYXRjaGVkIGFzIGEgVVJMXHJcbiAgICAgICAgc3RhdGUuY29tYmluZVRva2VucyA9IHRydWU7XHJcbiAgICAgICAgcmV0dXJuIFwibGlua1wiO1xyXG4gICAgICB9XHJcbiAgICAgIHN0cmVhbS5uZXh0KCk7XHJcbiAgICAgIHJldHVybiBudWxsO1xyXG4gICAgfSxcclxuICAgIGJsYW5rTGluZTogYmxhbmtMaW5lXHJcbiAgfTtcclxuXHJcbiAgdmFyIG1hcmtkb3duQ29uZmlnID0ge1xyXG4gICAgdW5kZXJzY29yZXNCcmVha1dvcmRzOiBmYWxzZSxcclxuICAgIHRhc2tMaXN0czogdHJ1ZSxcclxuICAgIGZlbmNlZENvZGVCbG9ja3M6ICdgYGAnLFxyXG4gICAgc3RyaWtldGhyb3VnaDogdHJ1ZVxyXG4gIH07XHJcbiAgZm9yICh2YXIgYXR0ciBpbiBtb2RlQ29uZmlnKSB7XHJcbiAgICBtYXJrZG93bkNvbmZpZ1thdHRyXSA9IG1vZGVDb25maWdbYXR0cl07XHJcbiAgfVxyXG4gIG1hcmtkb3duQ29uZmlnLm5hbWUgPSBcIm1hcmtkb3duXCI7XHJcbiAgcmV0dXJuIENvZGVNaXJyb3Iub3ZlcmxheU1vZGUoQ29kZU1pcnJvci5nZXRNb2RlKGNvbmZpZywgbWFya2Rvd25Db25maWcpLCBnZm1PdmVybGF5KTtcclxuXHJcbn0sIFwibWFya2Rvd25cIik7XHJcblxyXG4gIENvZGVNaXJyb3IuZGVmaW5lTUlNRShcInRleHQveC1nZm1cIiwgXCJnZm1cIik7XHJcbn0pO1xyXG4iLCIvLyBDb2RlTWlycm9yLCBjb3B5cmlnaHQgKGMpIGJ5IE1hcmlqbiBIYXZlcmJla2UgYW5kIG90aGVyc1xyXG4vLyBEaXN0cmlidXRlZCB1bmRlciBhbiBNSVQgbGljZW5zZTogaHR0cDovL2NvZGVtaXJyb3IubmV0L0xJQ0VOU0VcclxuXHJcbihmdW5jdGlvbihtb2QpIHtcclxuICBpZiAodHlwZW9mIGV4cG9ydHMgPT0gXCJvYmplY3RcIiAmJiB0eXBlb2YgbW9kdWxlID09IFwib2JqZWN0XCIpIC8vIENvbW1vbkpTXHJcbiAgICBtb2QocmVxdWlyZShcIi4uLy4uL2xpYi9jb2RlbWlycm9yXCIpLCByZXF1aXJlKFwiLi4veG1sL3htbFwiKSwgcmVxdWlyZShcIi4uL21ldGFcIikpO1xyXG4gIGVsc2UgaWYgKHR5cGVvZiBkZWZpbmUgPT0gXCJmdW5jdGlvblwiICYmIGRlZmluZS5hbWQpIC8vIEFNRFxyXG4gICAgZGVmaW5lKFtcIi4uLy4uL2xpYi9jb2RlbWlycm9yXCIsIFwiLi4veG1sL3htbFwiLCBcIi4uL21ldGFcIl0sIG1vZCk7XHJcbiAgZWxzZSAvLyBQbGFpbiBicm93c2VyIGVudlxyXG4gICAgbW9kKENvZGVNaXJyb3IpO1xyXG59KShmdW5jdGlvbihDb2RlTWlycm9yKSB7XHJcblwidXNlIHN0cmljdFwiO1xyXG5cclxuQ29kZU1pcnJvci5kZWZpbmVNb2RlKFwibWFya2Rvd25cIiwgZnVuY3Rpb24oY21DZmcsIG1vZGVDZmcpIHtcclxuXHJcbiAgdmFyIGh0bWxGb3VuZCA9IENvZGVNaXJyb3IubW9kZXMuaGFzT3duUHJvcGVydHkoXCJ4bWxcIik7XHJcbiAgdmFyIGh0bWxNb2RlID0gQ29kZU1pcnJvci5nZXRNb2RlKGNtQ2ZnLCBodG1sRm91bmQgPyB7bmFtZTogXCJ4bWxcIiwgaHRtbE1vZGU6IHRydWV9IDogXCJ0ZXh0L3BsYWluXCIpO1xyXG5cclxuICBmdW5jdGlvbiBnZXRNb2RlKG5hbWUpIHtcclxuICAgIGlmIChDb2RlTWlycm9yLmZpbmRNb2RlQnlOYW1lKSB7XHJcbiAgICAgIHZhciBmb3VuZCA9IENvZGVNaXJyb3IuZmluZE1vZGVCeU5hbWUobmFtZSk7XHJcbiAgICAgIGlmIChmb3VuZCkgbmFtZSA9IGZvdW5kLm1pbWUgfHwgZm91bmQubWltZXNbMF07XHJcbiAgICB9XHJcbiAgICB2YXIgbW9kZSA9IENvZGVNaXJyb3IuZ2V0TW9kZShjbUNmZywgbmFtZSk7XHJcbiAgICByZXR1cm4gbW9kZS5uYW1lID09IFwibnVsbFwiID8gbnVsbCA6IG1vZGU7XHJcbiAgfVxyXG5cclxuICAvLyBTaG91bGQgY2hhcmFjdGVycyB0aGF0IGFmZmVjdCBoaWdobGlnaHRpbmcgYmUgaGlnaGxpZ2h0ZWQgc2VwYXJhdGU/XHJcbiAgLy8gRG9lcyBub3QgaW5jbHVkZSBjaGFyYWN0ZXJzIHRoYXQgd2lsbCBiZSBvdXRwdXQgKHN1Y2ggYXMgYDEuYCBhbmQgYC1gIGZvciBsaXN0cylcclxuICBpZiAobW9kZUNmZy5oaWdobGlnaHRGb3JtYXR0aW5nID09PSB1bmRlZmluZWQpXHJcbiAgICBtb2RlQ2ZnLmhpZ2hsaWdodEZvcm1hdHRpbmcgPSBmYWxzZTtcclxuXHJcbiAgLy8gTWF4aW11bSBudW1iZXIgb2YgbmVzdGVkIGJsb2NrcXVvdGVzLiBTZXQgdG8gMCBmb3IgaW5maW5pdGUgbmVzdGluZy5cclxuICAvLyBFeGNlc3MgYD5gIHdpbGwgZW1pdCBgZXJyb3JgIHRva2VuLlxyXG4gIGlmIChtb2RlQ2ZnLm1heEJsb2NrcXVvdGVEZXB0aCA9PT0gdW5kZWZpbmVkKVxyXG4gICAgbW9kZUNmZy5tYXhCbG9ja3F1b3RlRGVwdGggPSAwO1xyXG5cclxuICAvLyBTaG91bGQgdW5kZXJzY29yZXMgaW4gd29yZHMgb3Blbi9jbG9zZSBlbS9zdHJvbmc/XHJcbiAgaWYgKG1vZGVDZmcudW5kZXJzY29yZXNCcmVha1dvcmRzID09PSB1bmRlZmluZWQpXHJcbiAgICBtb2RlQ2ZnLnVuZGVyc2NvcmVzQnJlYWtXb3JkcyA9IHRydWU7XHJcblxyXG4gIC8vIFVzZSBgZmVuY2VkQ29kZUJsb2Nrc2AgdG8gY29uZmlndXJlIGZlbmNlZCBjb2RlIGJsb2Nrcy4gZmFsc2UgdG9cclxuICAvLyBkaXNhYmxlLCBzdHJpbmcgdG8gc3BlY2lmeSBhIHByZWNpc2UgcmVnZXhwIHRoYXQgdGhlIGZlbmNlIHNob3VsZFxyXG4gIC8vIG1hdGNoLCBhbmQgdHJ1ZSB0byBhbGxvdyB0aHJlZSBvciBtb3JlIGJhY2t0aWNrcyBvciB0aWxkZXMgKGFzXHJcbiAgLy8gcGVyIENvbW1vbk1hcmspLlxyXG5cclxuICAvLyBUdXJuIG9uIHRhc2sgbGlzdHM/IChcIi0gWyBdIFwiIGFuZCBcIi0gW3hdIFwiKVxyXG4gIGlmIChtb2RlQ2ZnLnRhc2tMaXN0cyA9PT0gdW5kZWZpbmVkKSBtb2RlQ2ZnLnRhc2tMaXN0cyA9IGZhbHNlO1xyXG5cclxuICAvLyBUdXJuIG9uIHN0cmlrZXRocm91Z2ggc3ludGF4XHJcbiAgaWYgKG1vZGVDZmcuc3RyaWtldGhyb3VnaCA9PT0gdW5kZWZpbmVkKVxyXG4gICAgbW9kZUNmZy5zdHJpa2V0aHJvdWdoID0gZmFsc2U7XHJcblxyXG4gIHZhciBjb2RlRGVwdGggPSAwO1xyXG5cclxuICB2YXIgaGVhZGVyICAgPSAnaGVhZGVyJ1xyXG4gICwgICBjb2RlICAgICA9ICdjb21tZW50J1xyXG4gICwgICBxdW90ZSAgICA9ICdxdW90ZSdcclxuICAsICAgbGlzdDEgICAgPSAndmFyaWFibGUtMidcclxuICAsICAgbGlzdDIgICAgPSAndmFyaWFibGUtMydcclxuICAsICAgbGlzdDMgICAgPSAna2V5d29yZCdcclxuICAsICAgaHIgICAgICAgPSAnaHInXHJcbiAgLCAgIGltYWdlICAgID0gJ3RhZydcclxuICAsICAgZm9ybWF0dGluZyA9ICdmb3JtYXR0aW5nJ1xyXG4gICwgICBsaW5raW5saW5lID0gJ2xpbmsnXHJcbiAgLCAgIGxpbmtlbWFpbCA9ICdsaW5rJ1xyXG4gICwgICBsaW5rdGV4dCA9ICdsaW5rJ1xyXG4gICwgICBsaW5raHJlZiA9ICdzdHJpbmcnXHJcbiAgLCAgIGVtICAgICAgID0gJ2VtJ1xyXG4gICwgICBzdHJvbmcgICA9ICdzdHJvbmcnXHJcbiAgLCAgIHN0cmlrZXRocm91Z2ggPSAnc3RyaWtldGhyb3VnaCc7XHJcblxyXG4gIHZhciBoclJFID0gL14oWypcXC1fXSkoPzpcXHMqXFwxKXsyLH1cXHMqJC9cclxuICAsICAgdWxSRSA9IC9eWypcXC0rXVxccysvXHJcbiAgLCAgIG9sUkUgPSAvXlswLTldKyhbLildKVxccysvXHJcbiAgLCAgIHRhc2tMaXN0UkUgPSAvXlxcWyh4fCApXFxdKD89XFxzKS8gLy8gTXVzdCBmb2xsb3cgdWxSRSBvciBvbFJFXHJcbiAgLCAgIGF0eEhlYWRlclJFID0gbW9kZUNmZy5hbGxvd0F0eEhlYWRlcldpdGhvdXRTcGFjZSA/IC9eKCMrKS8gOiAvXigjKykoPzogfCQpL1xyXG4gICwgICBzZXRleHRIZWFkZXJSRSA9IC9eICooPzpcXD17MSx9fC17MSx9KVxccyokL1xyXG4gICwgICB0ZXh0UkUgPSAvXlteIyFcXFtcXF0qX1xcXFw8PmAgXCInKH5dKy9cclxuICAsICAgZmVuY2VkQ29kZVJFID0gbmV3IFJlZ0V4cChcIl4oXCIgKyAobW9kZUNmZy5mZW5jZWRDb2RlQmxvY2tzID09PSB0cnVlID8gXCJ+fn4rfGBgYCtcIiA6IG1vZGVDZmcuZmVuY2VkQ29kZUJsb2NrcykgK1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwiKVsgXFxcXHRdKihbXFxcXHcrI10qKVwiKTtcclxuXHJcbiAgZnVuY3Rpb24gc3dpdGNoSW5saW5lKHN0cmVhbSwgc3RhdGUsIGYpIHtcclxuICAgIHN0YXRlLmYgPSBzdGF0ZS5pbmxpbmUgPSBmO1xyXG4gICAgcmV0dXJuIGYoc3RyZWFtLCBzdGF0ZSk7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBzd2l0Y2hCbG9jayhzdHJlYW0sIHN0YXRlLCBmKSB7XHJcbiAgICBzdGF0ZS5mID0gc3RhdGUuYmxvY2sgPSBmO1xyXG4gICAgcmV0dXJuIGYoc3RyZWFtLCBzdGF0ZSk7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBsaW5lSXNFbXB0eShsaW5lKSB7XHJcbiAgICByZXR1cm4gIWxpbmUgfHwgIS9cXFMvLnRlc3QobGluZS5zdHJpbmcpXHJcbiAgfVxyXG5cclxuICAvLyBCbG9ja3NcclxuXHJcbiAgZnVuY3Rpb24gYmxhbmtMaW5lKHN0YXRlKSB7XHJcbiAgICAvLyBSZXNldCBsaW5rVGl0bGUgc3RhdGVcclxuICAgIHN0YXRlLmxpbmtUaXRsZSA9IGZhbHNlO1xyXG4gICAgLy8gUmVzZXQgRU0gc3RhdGVcclxuICAgIHN0YXRlLmVtID0gZmFsc2U7XHJcbiAgICAvLyBSZXNldCBTVFJPTkcgc3RhdGVcclxuICAgIHN0YXRlLnN0cm9uZyA9IGZhbHNlO1xyXG4gICAgLy8gUmVzZXQgc3RyaWtldGhyb3VnaCBzdGF0ZVxyXG4gICAgc3RhdGUuc3RyaWtldGhyb3VnaCA9IGZhbHNlO1xyXG4gICAgLy8gUmVzZXQgc3RhdGUucXVvdGVcclxuICAgIHN0YXRlLnF1b3RlID0gMDtcclxuICAgIC8vIFJlc2V0IHN0YXRlLmluZGVudGVkQ29kZVxyXG4gICAgc3RhdGUuaW5kZW50ZWRDb2RlID0gZmFsc2U7XHJcbiAgICBpZiAoIWh0bWxGb3VuZCAmJiBzdGF0ZS5mID09IGh0bWxCbG9jaykge1xyXG4gICAgICBzdGF0ZS5mID0gaW5saW5lTm9ybWFsO1xyXG4gICAgICBzdGF0ZS5ibG9jayA9IGJsb2NrTm9ybWFsO1xyXG4gICAgfVxyXG4gICAgLy8gUmVzZXQgc3RhdGUudHJhaWxpbmdTcGFjZVxyXG4gICAgc3RhdGUudHJhaWxpbmdTcGFjZSA9IDA7XHJcbiAgICBzdGF0ZS50cmFpbGluZ1NwYWNlTmV3TGluZSA9IGZhbHNlO1xyXG4gICAgLy8gTWFyayB0aGlzIGxpbmUgYXMgYmxhbmtcclxuICAgIHN0YXRlLnByZXZMaW5lID0gc3RhdGUudGhpc0xpbmVcclxuICAgIHN0YXRlLnRoaXNMaW5lID0gbnVsbFxyXG4gICAgcmV0dXJuIG51bGw7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBibG9ja05vcm1hbChzdHJlYW0sIHN0YXRlKSB7XHJcblxyXG4gICAgdmFyIHNvbCA9IHN0cmVhbS5zb2woKTtcclxuXHJcbiAgICB2YXIgcHJldkxpbmVJc0xpc3QgPSBzdGF0ZS5saXN0ICE9PSBmYWxzZSxcclxuICAgICAgICBwcmV2TGluZUlzSW5kZW50ZWRDb2RlID0gc3RhdGUuaW5kZW50ZWRDb2RlO1xyXG5cclxuICAgIHN0YXRlLmluZGVudGVkQ29kZSA9IGZhbHNlO1xyXG5cclxuICAgIGlmIChwcmV2TGluZUlzTGlzdCkge1xyXG4gICAgICBpZiAoc3RhdGUuaW5kZW50YXRpb25EaWZmID49IDApIHsgLy8gQ29udGludWVkIGxpc3RcclxuICAgICAgICBpZiAoc3RhdGUuaW5kZW50YXRpb25EaWZmIDwgNCkgeyAvLyBPbmx5IGFkanVzdCBpbmRlbnRhdGlvbiBpZiAqbm90KiBhIGNvZGUgYmxvY2tcclxuICAgICAgICAgIHN0YXRlLmluZGVudGF0aW9uIC09IHN0YXRlLmluZGVudGF0aW9uRGlmZjtcclxuICAgICAgICB9XHJcbiAgICAgICAgc3RhdGUubGlzdCA9IG51bGw7XHJcbiAgICAgIH0gZWxzZSBpZiAoc3RhdGUuaW5kZW50YXRpb24gPiAwKSB7XHJcbiAgICAgICAgc3RhdGUubGlzdCA9IG51bGw7XHJcbiAgICAgICAgc3RhdGUubGlzdERlcHRoID0gTWF0aC5mbG9vcihzdGF0ZS5pbmRlbnRhdGlvbiAvIDQpO1xyXG4gICAgICB9IGVsc2UgeyAvLyBObyBsb25nZXIgYSBsaXN0XHJcbiAgICAgICAgc3RhdGUubGlzdCA9IGZhbHNlO1xyXG4gICAgICAgIHN0YXRlLmxpc3REZXB0aCA9IDA7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICB2YXIgbWF0Y2ggPSBudWxsO1xyXG4gICAgaWYgKHN0YXRlLmluZGVudGF0aW9uRGlmZiA+PSA0KSB7XHJcbiAgICAgIHN0cmVhbS5za2lwVG9FbmQoKTtcclxuICAgICAgaWYgKHByZXZMaW5lSXNJbmRlbnRlZENvZGUgfHwgbGluZUlzRW1wdHkoc3RhdGUucHJldkxpbmUpKSB7XHJcbiAgICAgICAgc3RhdGUuaW5kZW50YXRpb24gLT0gNDtcclxuICAgICAgICBzdGF0ZS5pbmRlbnRlZENvZGUgPSB0cnVlO1xyXG4gICAgICAgIHJldHVybiBjb2RlO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHJldHVybiBudWxsO1xyXG4gICAgICB9XHJcbiAgICB9IGVsc2UgaWYgKHN0cmVhbS5lYXRTcGFjZSgpKSB7XHJcbiAgICAgIHJldHVybiBudWxsO1xyXG4gICAgfSBlbHNlIGlmICgobWF0Y2ggPSBzdHJlYW0ubWF0Y2goYXR4SGVhZGVyUkUpKSAmJiBtYXRjaFsxXS5sZW5ndGggPD0gNikge1xyXG4gICAgICBzdGF0ZS5oZWFkZXIgPSBtYXRjaFsxXS5sZW5ndGg7XHJcbiAgICAgIGlmIChtb2RlQ2ZnLmhpZ2hsaWdodEZvcm1hdHRpbmcpIHN0YXRlLmZvcm1hdHRpbmcgPSBcImhlYWRlclwiO1xyXG4gICAgICBzdGF0ZS5mID0gc3RhdGUuaW5saW5lO1xyXG4gICAgICByZXR1cm4gZ2V0VHlwZShzdGF0ZSk7XHJcbiAgICB9IGVsc2UgaWYgKCFsaW5lSXNFbXB0eShzdGF0ZS5wcmV2TGluZSkgJiYgIXN0YXRlLnF1b3RlICYmICFwcmV2TGluZUlzTGlzdCAmJlxyXG4gICAgICAgICAgICAgICAhcHJldkxpbmVJc0luZGVudGVkQ29kZSAmJiAobWF0Y2ggPSBzdHJlYW0ubWF0Y2goc2V0ZXh0SGVhZGVyUkUpKSkge1xyXG4gICAgICBzdGF0ZS5oZWFkZXIgPSBtYXRjaFswXS5jaGFyQXQoMCkgPT0gJz0nID8gMSA6IDI7XHJcbiAgICAgIGlmIChtb2RlQ2ZnLmhpZ2hsaWdodEZvcm1hdHRpbmcpIHN0YXRlLmZvcm1hdHRpbmcgPSBcImhlYWRlclwiO1xyXG4gICAgICBzdGF0ZS5mID0gc3RhdGUuaW5saW5lO1xyXG4gICAgICByZXR1cm4gZ2V0VHlwZShzdGF0ZSk7XHJcbiAgICB9IGVsc2UgaWYgKHN0cmVhbS5lYXQoJz4nKSkge1xyXG4gICAgICBzdGF0ZS5xdW90ZSA9IHNvbCA/IDEgOiBzdGF0ZS5xdW90ZSArIDE7XHJcbiAgICAgIGlmIChtb2RlQ2ZnLmhpZ2hsaWdodEZvcm1hdHRpbmcpIHN0YXRlLmZvcm1hdHRpbmcgPSBcInF1b3RlXCI7XHJcbiAgICAgIHN0cmVhbS5lYXRTcGFjZSgpO1xyXG4gICAgICByZXR1cm4gZ2V0VHlwZShzdGF0ZSk7XHJcbiAgICB9IGVsc2UgaWYgKHN0cmVhbS5wZWVrKCkgPT09ICdbJykge1xyXG4gICAgICByZXR1cm4gc3dpdGNoSW5saW5lKHN0cmVhbSwgc3RhdGUsIGZvb3Rub3RlTGluayk7XHJcbiAgICB9IGVsc2UgaWYgKHN0cmVhbS5tYXRjaChoclJFLCB0cnVlKSkge1xyXG4gICAgICBzdGF0ZS5ociA9IHRydWU7XHJcbiAgICAgIHJldHVybiBocjtcclxuICAgIH0gZWxzZSBpZiAoKGxpbmVJc0VtcHR5KHN0YXRlLnByZXZMaW5lKSB8fCBwcmV2TGluZUlzTGlzdCkgJiYgKHN0cmVhbS5tYXRjaCh1bFJFLCBmYWxzZSkgfHwgc3RyZWFtLm1hdGNoKG9sUkUsIGZhbHNlKSkpIHtcclxuICAgICAgdmFyIGxpc3RUeXBlID0gbnVsbDtcclxuICAgICAgaWYgKHN0cmVhbS5tYXRjaCh1bFJFLCB0cnVlKSkge1xyXG4gICAgICAgIGxpc3RUeXBlID0gJ3VsJztcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBzdHJlYW0ubWF0Y2gob2xSRSwgdHJ1ZSk7XHJcbiAgICAgICAgbGlzdFR5cGUgPSAnb2wnO1xyXG4gICAgICB9XHJcbiAgICAgIHN0YXRlLmluZGVudGF0aW9uID0gc3RyZWFtLmNvbHVtbigpICsgc3RyZWFtLmN1cnJlbnQoKS5sZW5ndGg7XHJcbiAgICAgIHN0YXRlLmxpc3QgPSB0cnVlO1xyXG4gICAgICBzdGF0ZS5saXN0RGVwdGgrKztcclxuICAgICAgaWYgKG1vZGVDZmcudGFza0xpc3RzICYmIHN0cmVhbS5tYXRjaCh0YXNrTGlzdFJFLCBmYWxzZSkpIHtcclxuICAgICAgICBzdGF0ZS50YXNrTGlzdCA9IHRydWU7XHJcbiAgICAgIH1cclxuICAgICAgc3RhdGUuZiA9IHN0YXRlLmlubGluZTtcclxuICAgICAgaWYgKG1vZGVDZmcuaGlnaGxpZ2h0Rm9ybWF0dGluZykgc3RhdGUuZm9ybWF0dGluZyA9IFtcImxpc3RcIiwgXCJsaXN0LVwiICsgbGlzdFR5cGVdO1xyXG4gICAgICByZXR1cm4gZ2V0VHlwZShzdGF0ZSk7XHJcbiAgICB9IGVsc2UgaWYgKG1vZGVDZmcuZmVuY2VkQ29kZUJsb2NrcyAmJiAobWF0Y2ggPSBzdHJlYW0ubWF0Y2goZmVuY2VkQ29kZVJFLCB0cnVlKSkpIHtcclxuICAgICAgc3RhdGUuZmVuY2VkQ2hhcnMgPSBtYXRjaFsxXVxyXG4gICAgICAvLyB0cnkgc3dpdGNoaW5nIG1vZGVcclxuICAgICAgc3RhdGUubG9jYWxNb2RlID0gZ2V0TW9kZShtYXRjaFsyXSk7XHJcbiAgICAgIGlmIChzdGF0ZS5sb2NhbE1vZGUpIHN0YXRlLmxvY2FsU3RhdGUgPSBzdGF0ZS5sb2NhbE1vZGUuc3RhcnRTdGF0ZSgpO1xyXG4gICAgICBzdGF0ZS5mID0gc3RhdGUuYmxvY2sgPSBsb2NhbDtcclxuICAgICAgaWYgKG1vZGVDZmcuaGlnaGxpZ2h0Rm9ybWF0dGluZykgc3RhdGUuZm9ybWF0dGluZyA9IFwiY29kZS1ibG9ja1wiO1xyXG4gICAgICBzdGF0ZS5jb2RlID0gdHJ1ZTtcclxuICAgICAgcmV0dXJuIGdldFR5cGUoc3RhdGUpO1xyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiBzd2l0Y2hJbmxpbmUoc3RyZWFtLCBzdGF0ZSwgc3RhdGUuaW5saW5lKTtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIGh0bWxCbG9jayhzdHJlYW0sIHN0YXRlKSB7XHJcbiAgICB2YXIgc3R5bGUgPSBodG1sTW9kZS50b2tlbihzdHJlYW0sIHN0YXRlLmh0bWxTdGF0ZSk7XHJcbiAgICBpZiAoKGh0bWxGb3VuZCAmJiBzdGF0ZS5odG1sU3RhdGUudGFnU3RhcnQgPT09IG51bGwgJiZcclxuICAgICAgICAgKCFzdGF0ZS5odG1sU3RhdGUuY29udGV4dCAmJiBzdGF0ZS5odG1sU3RhdGUudG9rZW5pemUuaXNJblRleHQpKSB8fFxyXG4gICAgICAgIChzdGF0ZS5tZF9pbnNpZGUgJiYgc3RyZWFtLmN1cnJlbnQoKS5pbmRleE9mKFwiPlwiKSA+IC0xKSkge1xyXG4gICAgICBzdGF0ZS5mID0gaW5saW5lTm9ybWFsO1xyXG4gICAgICBzdGF0ZS5ibG9jayA9IGJsb2NrTm9ybWFsO1xyXG4gICAgICBzdGF0ZS5odG1sU3RhdGUgPSBudWxsO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIHN0eWxlO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gbG9jYWwoc3RyZWFtLCBzdGF0ZSkge1xyXG4gICAgaWYgKHN0cmVhbS5zb2woKSAmJiBzdGF0ZS5mZW5jZWRDaGFycyAmJiBzdHJlYW0ubWF0Y2goc3RhdGUuZmVuY2VkQ2hhcnMsIGZhbHNlKSkge1xyXG4gICAgICBzdGF0ZS5sb2NhbE1vZGUgPSBzdGF0ZS5sb2NhbFN0YXRlID0gbnVsbDtcclxuICAgICAgc3RhdGUuZiA9IHN0YXRlLmJsb2NrID0gbGVhdmluZ0xvY2FsO1xyXG4gICAgICByZXR1cm4gbnVsbDtcclxuICAgIH0gZWxzZSBpZiAoc3RhdGUubG9jYWxNb2RlKSB7XHJcbiAgICAgIHJldHVybiBzdGF0ZS5sb2NhbE1vZGUudG9rZW4oc3RyZWFtLCBzdGF0ZS5sb2NhbFN0YXRlKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIHN0cmVhbS5za2lwVG9FbmQoKTtcclxuICAgICAgcmV0dXJuIGNvZGU7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBsZWF2aW5nTG9jYWwoc3RyZWFtLCBzdGF0ZSkge1xyXG4gICAgc3RyZWFtLm1hdGNoKHN0YXRlLmZlbmNlZENoYXJzKTtcclxuICAgIHN0YXRlLmJsb2NrID0gYmxvY2tOb3JtYWw7XHJcbiAgICBzdGF0ZS5mID0gaW5saW5lTm9ybWFsO1xyXG4gICAgc3RhdGUuZmVuY2VkQ2hhcnMgPSBudWxsO1xyXG4gICAgaWYgKG1vZGVDZmcuaGlnaGxpZ2h0Rm9ybWF0dGluZykgc3RhdGUuZm9ybWF0dGluZyA9IFwiY29kZS1ibG9ja1wiO1xyXG4gICAgc3RhdGUuY29kZSA9IHRydWU7XHJcbiAgICB2YXIgcmV0dXJuVHlwZSA9IGdldFR5cGUoc3RhdGUpO1xyXG4gICAgc3RhdGUuY29kZSA9IGZhbHNlO1xyXG4gICAgcmV0dXJuIHJldHVyblR5cGU7XHJcbiAgfVxyXG5cclxuICAvLyBJbmxpbmVcclxuICBmdW5jdGlvbiBnZXRUeXBlKHN0YXRlKSB7XHJcbiAgICB2YXIgc3R5bGVzID0gW107XHJcblxyXG4gICAgaWYgKHN0YXRlLmZvcm1hdHRpbmcpIHtcclxuICAgICAgc3R5bGVzLnB1c2goZm9ybWF0dGluZyk7XHJcblxyXG4gICAgICBpZiAodHlwZW9mIHN0YXRlLmZvcm1hdHRpbmcgPT09IFwic3RyaW5nXCIpIHN0YXRlLmZvcm1hdHRpbmcgPSBbc3RhdGUuZm9ybWF0dGluZ107XHJcblxyXG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHN0YXRlLmZvcm1hdHRpbmcubGVuZ3RoOyBpKyspIHtcclxuICAgICAgICBzdHlsZXMucHVzaChmb3JtYXR0aW5nICsgXCItXCIgKyBzdGF0ZS5mb3JtYXR0aW5nW2ldKTtcclxuXHJcbiAgICAgICAgaWYgKHN0YXRlLmZvcm1hdHRpbmdbaV0gPT09IFwiaGVhZGVyXCIpIHtcclxuICAgICAgICAgIHN0eWxlcy5wdXNoKGZvcm1hdHRpbmcgKyBcIi1cIiArIHN0YXRlLmZvcm1hdHRpbmdbaV0gKyBcIi1cIiArIHN0YXRlLmhlYWRlcik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBBZGQgYGZvcm1hdHRpbmctcXVvdGVgIGFuZCBgZm9ybWF0dGluZy1xdW90ZS0jYCBmb3IgYmxvY2txdW90ZXNcclxuICAgICAgICAvLyBBZGQgYGVycm9yYCBpbnN0ZWFkIGlmIHRoZSBtYXhpbXVtIGJsb2NrcXVvdGUgbmVzdGluZyBkZXB0aCBpcyBwYXNzZWRcclxuICAgICAgICBpZiAoc3RhdGUuZm9ybWF0dGluZ1tpXSA9PT0gXCJxdW90ZVwiKSB7XHJcbiAgICAgICAgICBpZiAoIW1vZGVDZmcubWF4QmxvY2txdW90ZURlcHRoIHx8IG1vZGVDZmcubWF4QmxvY2txdW90ZURlcHRoID49IHN0YXRlLnF1b3RlKSB7XHJcbiAgICAgICAgICAgIHN0eWxlcy5wdXNoKGZvcm1hdHRpbmcgKyBcIi1cIiArIHN0YXRlLmZvcm1hdHRpbmdbaV0gKyBcIi1cIiArIHN0YXRlLnF1b3RlKTtcclxuICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIHN0eWxlcy5wdXNoKFwiZXJyb3JcIik7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKHN0YXRlLnRhc2tPcGVuKSB7XHJcbiAgICAgIHN0eWxlcy5wdXNoKFwibWV0YVwiKTtcclxuICAgICAgcmV0dXJuIHN0eWxlcy5sZW5ndGggPyBzdHlsZXMuam9pbignICcpIDogbnVsbDtcclxuICAgIH1cclxuICAgIGlmIChzdGF0ZS50YXNrQ2xvc2VkKSB7XHJcbiAgICAgIHN0eWxlcy5wdXNoKFwicHJvcGVydHlcIik7XHJcbiAgICAgIHJldHVybiBzdHlsZXMubGVuZ3RoID8gc3R5bGVzLmpvaW4oJyAnKSA6IG51bGw7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKHN0YXRlLmxpbmtIcmVmKSB7XHJcbiAgICAgIHN0eWxlcy5wdXNoKGxpbmtocmVmLCBcInVybFwiKTtcclxuICAgIH0gZWxzZSB7IC8vIE9ubHkgYXBwbHkgaW5saW5lIHN0eWxlcyB0byBub24tdXJsIHRleHRcclxuICAgICAgaWYgKHN0YXRlLnN0cm9uZykgeyBzdHlsZXMucHVzaChzdHJvbmcpOyB9XHJcbiAgICAgIGlmIChzdGF0ZS5lbSkgeyBzdHlsZXMucHVzaChlbSk7IH1cclxuICAgICAgaWYgKHN0YXRlLnN0cmlrZXRocm91Z2gpIHsgc3R5bGVzLnB1c2goc3RyaWtldGhyb3VnaCk7IH1cclxuXHJcbiAgICAgIGlmIChzdGF0ZS5saW5rVGV4dCkgeyBzdHlsZXMucHVzaChsaW5rdGV4dCk7IH1cclxuXHJcbiAgICAgIGlmIChzdGF0ZS5jb2RlKSB7IHN0eWxlcy5wdXNoKGNvZGUpOyB9XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKHN0YXRlLmhlYWRlcikgeyBzdHlsZXMucHVzaChoZWFkZXIpOyBzdHlsZXMucHVzaChoZWFkZXIgKyBcIi1cIiArIHN0YXRlLmhlYWRlcik7IH1cclxuXHJcbiAgICBpZiAoc3RhdGUucXVvdGUpIHtcclxuICAgICAgc3R5bGVzLnB1c2gocXVvdGUpO1xyXG5cclxuICAgICAgLy8gQWRkIGBxdW90ZS0jYCB3aGVyZSB0aGUgbWF4aW11bSBmb3IgYCNgIGlzIG1vZGVDZmcubWF4QmxvY2txdW90ZURlcHRoXHJcbiAgICAgIGlmICghbW9kZUNmZy5tYXhCbG9ja3F1b3RlRGVwdGggfHwgbW9kZUNmZy5tYXhCbG9ja3F1b3RlRGVwdGggPj0gc3RhdGUucXVvdGUpIHtcclxuICAgICAgICBzdHlsZXMucHVzaChxdW90ZSArIFwiLVwiICsgc3RhdGUucXVvdGUpO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHN0eWxlcy5wdXNoKHF1b3RlICsgXCItXCIgKyBtb2RlQ2ZnLm1heEJsb2NrcXVvdGVEZXB0aCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBpZiAoc3RhdGUubGlzdCAhPT0gZmFsc2UpIHtcclxuICAgICAgdmFyIGxpc3RNb2QgPSAoc3RhdGUubGlzdERlcHRoIC0gMSkgJSAzO1xyXG4gICAgICBpZiAoIWxpc3RNb2QpIHtcclxuICAgICAgICBzdHlsZXMucHVzaChsaXN0MSk7XHJcbiAgICAgIH0gZWxzZSBpZiAobGlzdE1vZCA9PT0gMSkge1xyXG4gICAgICAgIHN0eWxlcy5wdXNoKGxpc3QyKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBzdHlsZXMucHVzaChsaXN0Myk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBpZiAoc3RhdGUudHJhaWxpbmdTcGFjZU5ld0xpbmUpIHtcclxuICAgICAgc3R5bGVzLnB1c2goXCJ0cmFpbGluZy1zcGFjZS1uZXctbGluZVwiKTtcclxuICAgIH0gZWxzZSBpZiAoc3RhdGUudHJhaWxpbmdTcGFjZSkge1xyXG4gICAgICBzdHlsZXMucHVzaChcInRyYWlsaW5nLXNwYWNlLVwiICsgKHN0YXRlLnRyYWlsaW5nU3BhY2UgJSAyID8gXCJhXCIgOiBcImJcIikpO1xyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiBzdHlsZXMubGVuZ3RoID8gc3R5bGVzLmpvaW4oJyAnKSA6IG51bGw7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBoYW5kbGVUZXh0KHN0cmVhbSwgc3RhdGUpIHtcclxuICAgIGlmIChzdHJlYW0ubWF0Y2godGV4dFJFLCB0cnVlKSkge1xyXG4gICAgICByZXR1cm4gZ2V0VHlwZShzdGF0ZSk7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gaW5saW5lTm9ybWFsKHN0cmVhbSwgc3RhdGUpIHtcclxuICAgIHZhciBzdHlsZSA9IHN0YXRlLnRleHQoc3RyZWFtLCBzdGF0ZSk7XHJcbiAgICBpZiAodHlwZW9mIHN0eWxlICE9PSAndW5kZWZpbmVkJylcclxuICAgICAgcmV0dXJuIHN0eWxlO1xyXG5cclxuICAgIGlmIChzdGF0ZS5saXN0KSB7IC8vIExpc3QgbWFya2VyICgqLCArLCAtLCAxLiwgZXRjKVxyXG4gICAgICBzdGF0ZS5saXN0ID0gbnVsbDtcclxuICAgICAgcmV0dXJuIGdldFR5cGUoc3RhdGUpO1xyXG4gICAgfVxyXG5cclxuICAgIGlmIChzdGF0ZS50YXNrTGlzdCkge1xyXG4gICAgICB2YXIgdGFza09wZW4gPSBzdHJlYW0ubWF0Y2godGFza0xpc3RSRSwgdHJ1ZSlbMV0gIT09IFwieFwiO1xyXG4gICAgICBpZiAodGFza09wZW4pIHN0YXRlLnRhc2tPcGVuID0gdHJ1ZTtcclxuICAgICAgZWxzZSBzdGF0ZS50YXNrQ2xvc2VkID0gdHJ1ZTtcclxuICAgICAgaWYgKG1vZGVDZmcuaGlnaGxpZ2h0Rm9ybWF0dGluZykgc3RhdGUuZm9ybWF0dGluZyA9IFwidGFza1wiO1xyXG4gICAgICBzdGF0ZS50YXNrTGlzdCA9IGZhbHNlO1xyXG4gICAgICByZXR1cm4gZ2V0VHlwZShzdGF0ZSk7XHJcbiAgICB9XHJcblxyXG4gICAgc3RhdGUudGFza09wZW4gPSBmYWxzZTtcclxuICAgIHN0YXRlLnRhc2tDbG9zZWQgPSBmYWxzZTtcclxuXHJcbiAgICBpZiAoc3RhdGUuaGVhZGVyICYmIHN0cmVhbS5tYXRjaCgvXiMrJC8sIHRydWUpKSB7XHJcbiAgICAgIGlmIChtb2RlQ2ZnLmhpZ2hsaWdodEZvcm1hdHRpbmcpIHN0YXRlLmZvcm1hdHRpbmcgPSBcImhlYWRlclwiO1xyXG4gICAgICByZXR1cm4gZ2V0VHlwZShzdGF0ZSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gR2V0IHNvbCgpIHZhbHVlIG5vdywgYmVmb3JlIGNoYXJhY3RlciBpcyBjb25zdW1lZFxyXG4gICAgdmFyIHNvbCA9IHN0cmVhbS5zb2woKTtcclxuXHJcbiAgICB2YXIgY2ggPSBzdHJlYW0ubmV4dCgpO1xyXG5cclxuICAgIGlmIChjaCA9PT0gJ1xcXFwnKSB7XHJcbiAgICAgIHN0cmVhbS5uZXh0KCk7XHJcbiAgICAgIGlmIChtb2RlQ2ZnLmhpZ2hsaWdodEZvcm1hdHRpbmcpIHtcclxuICAgICAgICB2YXIgdHlwZSA9IGdldFR5cGUoc3RhdGUpO1xyXG4gICAgICAgIHJldHVybiB0eXBlID8gdHlwZSArIFwiIGZvcm1hdHRpbmctZXNjYXBlXCIgOiBcImZvcm1hdHRpbmctZXNjYXBlXCI7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBNYXRjaGVzIGxpbmsgdGl0bGVzIHByZXNlbnQgb24gbmV4dCBsaW5lXHJcbiAgICBpZiAoc3RhdGUubGlua1RpdGxlKSB7XHJcbiAgICAgIHN0YXRlLmxpbmtUaXRsZSA9IGZhbHNlO1xyXG4gICAgICB2YXIgbWF0Y2hDaCA9IGNoO1xyXG4gICAgICBpZiAoY2ggPT09ICcoJykge1xyXG4gICAgICAgIG1hdGNoQ2ggPSAnKSc7XHJcbiAgICAgIH1cclxuICAgICAgbWF0Y2hDaCA9IChtYXRjaENoKycnKS5yZXBsYWNlKC8oWy4/KiteJFtcXF1cXFxcKCl7fXwtXSkvZywgXCJcXFxcJDFcIik7XHJcbiAgICAgIHZhciByZWdleCA9ICdeXFxcXHMqKD86W14nICsgbWF0Y2hDaCArICdcXFxcXFxcXF0rfFxcXFxcXFxcXFxcXFxcXFx8XFxcXFxcXFwuKScgKyBtYXRjaENoO1xyXG4gICAgICBpZiAoc3RyZWFtLm1hdGNoKG5ldyBSZWdFeHAocmVnZXgpLCB0cnVlKSkge1xyXG4gICAgICAgIHJldHVybiBsaW5raHJlZjtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIElmIHRoaXMgYmxvY2sgaXMgY2hhbmdlZCwgaXQgbWF5IG5lZWQgdG8gYmUgdXBkYXRlZCBpbiBHRk0gbW9kZVxyXG4gICAgaWYgKGNoID09PSAnYCcpIHtcclxuICAgICAgdmFyIHByZXZpb3VzRm9ybWF0dGluZyA9IHN0YXRlLmZvcm1hdHRpbmc7XHJcbiAgICAgIGlmIChtb2RlQ2ZnLmhpZ2hsaWdodEZvcm1hdHRpbmcpIHN0YXRlLmZvcm1hdHRpbmcgPSBcImNvZGVcIjtcclxuICAgICAgdmFyIHQgPSBnZXRUeXBlKHN0YXRlKTtcclxuICAgICAgdmFyIGJlZm9yZSA9IHN0cmVhbS5wb3M7XHJcbiAgICAgIHN0cmVhbS5lYXRXaGlsZSgnYCcpO1xyXG4gICAgICB2YXIgZGlmZmVyZW5jZSA9IDEgKyBzdHJlYW0ucG9zIC0gYmVmb3JlO1xyXG4gICAgICBpZiAoIXN0YXRlLmNvZGUpIHtcclxuICAgICAgICBjb2RlRGVwdGggPSBkaWZmZXJlbmNlO1xyXG4gICAgICAgIHN0YXRlLmNvZGUgPSB0cnVlO1xyXG4gICAgICAgIHJldHVybiBnZXRUeXBlKHN0YXRlKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBpZiAoZGlmZmVyZW5jZSA9PT0gY29kZURlcHRoKSB7IC8vIE11c3QgYmUgZXhhY3RcclxuICAgICAgICAgIHN0YXRlLmNvZGUgPSBmYWxzZTtcclxuICAgICAgICAgIHJldHVybiB0O1xyXG4gICAgICAgIH1cclxuICAgICAgICBzdGF0ZS5mb3JtYXR0aW5nID0gcHJldmlvdXNGb3JtYXR0aW5nO1xyXG4gICAgICAgIHJldHVybiBnZXRUeXBlKHN0YXRlKTtcclxuICAgICAgfVxyXG4gICAgfSBlbHNlIGlmIChzdGF0ZS5jb2RlKSB7XHJcbiAgICAgIHJldHVybiBnZXRUeXBlKHN0YXRlKTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoY2ggPT09ICchJyAmJiBzdHJlYW0ubWF0Y2goL1xcW1teXFxdXSpcXF0gPyg/OlxcKHxcXFspLywgZmFsc2UpKSB7XHJcbiAgICAgIHN0cmVhbS5tYXRjaCgvXFxbW15cXF1dKlxcXS8pO1xyXG4gICAgICBzdGF0ZS5pbmxpbmUgPSBzdGF0ZS5mID0gbGlua0hyZWY7XHJcbiAgICAgIHJldHVybiBpbWFnZTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoY2ggPT09ICdbJyAmJiBzdHJlYW0ubWF0Y2goLy4qXFxdKFxcKC4qXFwpfCA/XFxbLipcXF0pLywgZmFsc2UpKSB7XHJcbiAgICAgIHN0YXRlLmxpbmtUZXh0ID0gdHJ1ZTtcclxuICAgICAgaWYgKG1vZGVDZmcuaGlnaGxpZ2h0Rm9ybWF0dGluZykgc3RhdGUuZm9ybWF0dGluZyA9IFwibGlua1wiO1xyXG4gICAgICByZXR1cm4gZ2V0VHlwZShzdGF0ZSk7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKGNoID09PSAnXScgJiYgc3RhdGUubGlua1RleHQgJiYgc3RyZWFtLm1hdGNoKC9cXCguKlxcKXwgP1xcWy4qXFxdLywgZmFsc2UpKSB7XHJcbiAgICAgIGlmIChtb2RlQ2ZnLmhpZ2hsaWdodEZvcm1hdHRpbmcpIHN0YXRlLmZvcm1hdHRpbmcgPSBcImxpbmtcIjtcclxuICAgICAgdmFyIHR5cGUgPSBnZXRUeXBlKHN0YXRlKTtcclxuICAgICAgc3RhdGUubGlua1RleHQgPSBmYWxzZTtcclxuICAgICAgc3RhdGUuaW5saW5lID0gc3RhdGUuZiA9IGxpbmtIcmVmO1xyXG4gICAgICByZXR1cm4gdHlwZTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoY2ggPT09ICc8JyAmJiBzdHJlYW0ubWF0Y2goL14oaHR0cHM/fGZ0cHM/KTpcXC9cXC8oPzpbXlxcXFw+XXxcXFxcLikrPi8sIGZhbHNlKSkge1xyXG4gICAgICBzdGF0ZS5mID0gc3RhdGUuaW5saW5lID0gbGlua0lubGluZTtcclxuICAgICAgaWYgKG1vZGVDZmcuaGlnaGxpZ2h0Rm9ybWF0dGluZykgc3RhdGUuZm9ybWF0dGluZyA9IFwibGlua1wiO1xyXG4gICAgICB2YXIgdHlwZSA9IGdldFR5cGUoc3RhdGUpO1xyXG4gICAgICBpZiAodHlwZSl7XHJcbiAgICAgICAgdHlwZSArPSBcIiBcIjtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICB0eXBlID0gXCJcIjtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gdHlwZSArIGxpbmtpbmxpbmU7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKGNoID09PSAnPCcgJiYgc3RyZWFtLm1hdGNoKC9eW14+IFxcXFxdK0AoPzpbXlxcXFw+XXxcXFxcLikrPi8sIGZhbHNlKSkge1xyXG4gICAgICBzdGF0ZS5mID0gc3RhdGUuaW5saW5lID0gbGlua0lubGluZTtcclxuICAgICAgaWYgKG1vZGVDZmcuaGlnaGxpZ2h0Rm9ybWF0dGluZykgc3RhdGUuZm9ybWF0dGluZyA9IFwibGlua1wiO1xyXG4gICAgICB2YXIgdHlwZSA9IGdldFR5cGUoc3RhdGUpO1xyXG4gICAgICBpZiAodHlwZSl7XHJcbiAgICAgICAgdHlwZSArPSBcIiBcIjtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICB0eXBlID0gXCJcIjtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gdHlwZSArIGxpbmtlbWFpbDtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoY2ggPT09ICc8JyAmJiBzdHJlYW0ubWF0Y2goL14oIS0tfFxcdykvLCBmYWxzZSkpIHtcclxuICAgICAgdmFyIGVuZCA9IHN0cmVhbS5zdHJpbmcuaW5kZXhPZihcIj5cIiwgc3RyZWFtLnBvcyk7XHJcbiAgICAgIGlmIChlbmQgIT0gLTEpIHtcclxuICAgICAgICB2YXIgYXR0cyA9IHN0cmVhbS5zdHJpbmcuc3Vic3RyaW5nKHN0cmVhbS5zdGFydCwgZW5kKTtcclxuICAgICAgICBpZiAoL21hcmtkb3duXFxzKj1cXHMqKCd8XCIpezAsMX0xKCd8XCIpezAsMX0vLnRlc3QoYXR0cykpIHN0YXRlLm1kX2luc2lkZSA9IHRydWU7XHJcbiAgICAgIH1cclxuICAgICAgc3RyZWFtLmJhY2tVcCgxKTtcclxuICAgICAgc3RhdGUuaHRtbFN0YXRlID0gQ29kZU1pcnJvci5zdGFydFN0YXRlKGh0bWxNb2RlKTtcclxuICAgICAgcmV0dXJuIHN3aXRjaEJsb2NrKHN0cmVhbSwgc3RhdGUsIGh0bWxCbG9jayk7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKGNoID09PSAnPCcgJiYgc3RyZWFtLm1hdGNoKC9eXFwvXFx3Kj8+LykpIHtcclxuICAgICAgc3RhdGUubWRfaW5zaWRlID0gZmFsc2U7XHJcbiAgICAgIHJldHVybiBcInRhZ1wiO1xyXG4gICAgfVxyXG5cclxuICAgIHZhciBpZ25vcmVVbmRlcnNjb3JlID0gZmFsc2U7XHJcbiAgICBpZiAoIW1vZGVDZmcudW5kZXJzY29yZXNCcmVha1dvcmRzKSB7XHJcbiAgICAgIGlmIChjaCA9PT0gJ18nICYmIHN0cmVhbS5wZWVrKCkgIT09ICdfJyAmJiBzdHJlYW0ubWF0Y2goLyhcXHcpLywgZmFsc2UpKSB7XHJcbiAgICAgICAgdmFyIHByZXZQb3MgPSBzdHJlYW0ucG9zIC0gMjtcclxuICAgICAgICBpZiAocHJldlBvcyA+PSAwKSB7XHJcbiAgICAgICAgICB2YXIgcHJldkNoID0gc3RyZWFtLnN0cmluZy5jaGFyQXQocHJldlBvcyk7XHJcbiAgICAgICAgICBpZiAocHJldkNoICE9PSAnXycgJiYgcHJldkNoLm1hdGNoKC8oXFx3KS8sIGZhbHNlKSkge1xyXG4gICAgICAgICAgICBpZ25vcmVVbmRlcnNjb3JlID0gdHJ1ZTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIGlmIChjaCA9PT0gJyonIHx8IChjaCA9PT0gJ18nICYmICFpZ25vcmVVbmRlcnNjb3JlKSkge1xyXG4gICAgICBpZiAoc29sICYmIHN0cmVhbS5wZWVrKCkgPT09ICcgJykge1xyXG4gICAgICAgIC8vIERvIG5vdGhpbmcsIHN1cnJvdW5kZWQgYnkgbmV3bGluZSBhbmQgc3BhY2VcclxuICAgICAgfSBlbHNlIGlmIChzdGF0ZS5zdHJvbmcgPT09IGNoICYmIHN0cmVhbS5lYXQoY2gpKSB7IC8vIFJlbW92ZSBTVFJPTkdcclxuICAgICAgICBpZiAobW9kZUNmZy5oaWdobGlnaHRGb3JtYXR0aW5nKSBzdGF0ZS5mb3JtYXR0aW5nID0gXCJzdHJvbmdcIjtcclxuICAgICAgICB2YXIgdCA9IGdldFR5cGUoc3RhdGUpO1xyXG4gICAgICAgIHN0YXRlLnN0cm9uZyA9IGZhbHNlO1xyXG4gICAgICAgIHJldHVybiB0O1xyXG4gICAgICB9IGVsc2UgaWYgKCFzdGF0ZS5zdHJvbmcgJiYgc3RyZWFtLmVhdChjaCkpIHsgLy8gQWRkIFNUUk9OR1xyXG4gICAgICAgIHN0YXRlLnN0cm9uZyA9IGNoO1xyXG4gICAgICAgIGlmIChtb2RlQ2ZnLmhpZ2hsaWdodEZvcm1hdHRpbmcpIHN0YXRlLmZvcm1hdHRpbmcgPSBcInN0cm9uZ1wiO1xyXG4gICAgICAgIHJldHVybiBnZXRUeXBlKHN0YXRlKTtcclxuICAgICAgfSBlbHNlIGlmIChzdGF0ZS5lbSA9PT0gY2gpIHsgLy8gUmVtb3ZlIEVNXHJcbiAgICAgICAgaWYgKG1vZGVDZmcuaGlnaGxpZ2h0Rm9ybWF0dGluZykgc3RhdGUuZm9ybWF0dGluZyA9IFwiZW1cIjtcclxuICAgICAgICB2YXIgdCA9IGdldFR5cGUoc3RhdGUpO1xyXG4gICAgICAgIHN0YXRlLmVtID0gZmFsc2U7XHJcbiAgICAgICAgcmV0dXJuIHQ7XHJcbiAgICAgIH0gZWxzZSBpZiAoIXN0YXRlLmVtKSB7IC8vIEFkZCBFTVxyXG4gICAgICAgIHN0YXRlLmVtID0gY2g7XHJcbiAgICAgICAgaWYgKG1vZGVDZmcuaGlnaGxpZ2h0Rm9ybWF0dGluZykgc3RhdGUuZm9ybWF0dGluZyA9IFwiZW1cIjtcclxuICAgICAgICByZXR1cm4gZ2V0VHlwZShzdGF0ZSk7XHJcbiAgICAgIH1cclxuICAgIH0gZWxzZSBpZiAoY2ggPT09ICcgJykge1xyXG4gICAgICBpZiAoc3RyZWFtLmVhdCgnKicpIHx8IHN0cmVhbS5lYXQoJ18nKSkgeyAvLyBQcm9iYWJseSBzdXJyb3VuZGVkIGJ5IHNwYWNlc1xyXG4gICAgICAgIGlmIChzdHJlYW0ucGVlaygpID09PSAnICcpIHsgLy8gU3Vycm91bmRlZCBieSBzcGFjZXMsIGlnbm9yZVxyXG4gICAgICAgICAgcmV0dXJuIGdldFR5cGUoc3RhdGUpO1xyXG4gICAgICAgIH0gZWxzZSB7IC8vIE5vdCBzdXJyb3VuZGVkIGJ5IHNwYWNlcywgYmFjayB1cCBwb2ludGVyXHJcbiAgICAgICAgICBzdHJlYW0uYmFja1VwKDEpO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGlmIChtb2RlQ2ZnLnN0cmlrZXRocm91Z2gpIHtcclxuICAgICAgaWYgKGNoID09PSAnficgJiYgc3RyZWFtLmVhdFdoaWxlKGNoKSkge1xyXG4gICAgICAgIGlmIChzdGF0ZS5zdHJpa2V0aHJvdWdoKSB7Ly8gUmVtb3ZlIHN0cmlrZXRocm91Z2hcclxuICAgICAgICAgIGlmIChtb2RlQ2ZnLmhpZ2hsaWdodEZvcm1hdHRpbmcpIHN0YXRlLmZvcm1hdHRpbmcgPSBcInN0cmlrZXRocm91Z2hcIjtcclxuICAgICAgICAgIHZhciB0ID0gZ2V0VHlwZShzdGF0ZSk7XHJcbiAgICAgICAgICBzdGF0ZS5zdHJpa2V0aHJvdWdoID0gZmFsc2U7XHJcbiAgICAgICAgICByZXR1cm4gdDtcclxuICAgICAgICB9IGVsc2UgaWYgKHN0cmVhbS5tYXRjaCgvXlteXFxzXS8sIGZhbHNlKSkgey8vIEFkZCBzdHJpa2V0aHJvdWdoXHJcbiAgICAgICAgICBzdGF0ZS5zdHJpa2V0aHJvdWdoID0gdHJ1ZTtcclxuICAgICAgICAgIGlmIChtb2RlQ2ZnLmhpZ2hsaWdodEZvcm1hdHRpbmcpIHN0YXRlLmZvcm1hdHRpbmcgPSBcInN0cmlrZXRocm91Z2hcIjtcclxuICAgICAgICAgIHJldHVybiBnZXRUeXBlKHN0YXRlKTtcclxuICAgICAgICB9XHJcbiAgICAgIH0gZWxzZSBpZiAoY2ggPT09ICcgJykge1xyXG4gICAgICAgIGlmIChzdHJlYW0ubWF0Y2goL15+fi8sIHRydWUpKSB7IC8vIFByb2JhYmx5IHN1cnJvdW5kZWQgYnkgc3BhY2VcclxuICAgICAgICAgIGlmIChzdHJlYW0ucGVlaygpID09PSAnICcpIHsgLy8gU3Vycm91bmRlZCBieSBzcGFjZXMsIGlnbm9yZVxyXG4gICAgICAgICAgICByZXR1cm4gZ2V0VHlwZShzdGF0ZSk7XHJcbiAgICAgICAgICB9IGVsc2UgeyAvLyBOb3Qgc3Vycm91bmRlZCBieSBzcGFjZXMsIGJhY2sgdXAgcG9pbnRlclxyXG4gICAgICAgICAgICBzdHJlYW0uYmFja1VwKDIpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGlmIChjaCA9PT0gJyAnKSB7XHJcbiAgICAgIGlmIChzdHJlYW0ubWF0Y2goLyArJC8sIGZhbHNlKSkge1xyXG4gICAgICAgIHN0YXRlLnRyYWlsaW5nU3BhY2UrKztcclxuICAgICAgfSBlbHNlIGlmIChzdGF0ZS50cmFpbGluZ1NwYWNlKSB7XHJcbiAgICAgICAgc3RhdGUudHJhaWxpbmdTcGFjZU5ld0xpbmUgPSB0cnVlO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIGdldFR5cGUoc3RhdGUpO1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gbGlua0lubGluZShzdHJlYW0sIHN0YXRlKSB7XHJcbiAgICB2YXIgY2ggPSBzdHJlYW0ubmV4dCgpO1xyXG5cclxuICAgIGlmIChjaCA9PT0gXCI+XCIpIHtcclxuICAgICAgc3RhdGUuZiA9IHN0YXRlLmlubGluZSA9IGlubGluZU5vcm1hbDtcclxuICAgICAgaWYgKG1vZGVDZmcuaGlnaGxpZ2h0Rm9ybWF0dGluZykgc3RhdGUuZm9ybWF0dGluZyA9IFwibGlua1wiO1xyXG4gICAgICB2YXIgdHlwZSA9IGdldFR5cGUoc3RhdGUpO1xyXG4gICAgICBpZiAodHlwZSl7XHJcbiAgICAgICAgdHlwZSArPSBcIiBcIjtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICB0eXBlID0gXCJcIjtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gdHlwZSArIGxpbmtpbmxpbmU7XHJcbiAgICB9XHJcblxyXG4gICAgc3RyZWFtLm1hdGNoKC9eW14+XSsvLCB0cnVlKTtcclxuXHJcbiAgICByZXR1cm4gbGlua2lubGluZTtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIGxpbmtIcmVmKHN0cmVhbSwgc3RhdGUpIHtcclxuICAgIC8vIENoZWNrIGlmIHNwYWNlLCBhbmQgcmV0dXJuIE5VTEwgaWYgc28gKHRvIGF2b2lkIG1hcmtpbmcgdGhlIHNwYWNlKVxyXG4gICAgaWYoc3RyZWFtLmVhdFNwYWNlKCkpe1xyXG4gICAgICByZXR1cm4gbnVsbDtcclxuICAgIH1cclxuICAgIHZhciBjaCA9IHN0cmVhbS5uZXh0KCk7XHJcbiAgICBpZiAoY2ggPT09ICcoJyB8fCBjaCA9PT0gJ1snKSB7XHJcbiAgICAgIHN0YXRlLmYgPSBzdGF0ZS5pbmxpbmUgPSBnZXRMaW5rSHJlZkluc2lkZShjaCA9PT0gXCIoXCIgPyBcIilcIiA6IFwiXVwiKTtcclxuICAgICAgaWYgKG1vZGVDZmcuaGlnaGxpZ2h0Rm9ybWF0dGluZykgc3RhdGUuZm9ybWF0dGluZyA9IFwibGluay1zdHJpbmdcIjtcclxuICAgICAgc3RhdGUubGlua0hyZWYgPSB0cnVlO1xyXG4gICAgICByZXR1cm4gZ2V0VHlwZShzdGF0ZSk7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gJ2Vycm9yJztcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIGdldExpbmtIcmVmSW5zaWRlKGVuZENoYXIpIHtcclxuICAgIHJldHVybiBmdW5jdGlvbihzdHJlYW0sIHN0YXRlKSB7XHJcbiAgICAgIHZhciBjaCA9IHN0cmVhbS5uZXh0KCk7XHJcblxyXG4gICAgICBpZiAoY2ggPT09IGVuZENoYXIpIHtcclxuICAgICAgICBzdGF0ZS5mID0gc3RhdGUuaW5saW5lID0gaW5saW5lTm9ybWFsO1xyXG4gICAgICAgIGlmIChtb2RlQ2ZnLmhpZ2hsaWdodEZvcm1hdHRpbmcpIHN0YXRlLmZvcm1hdHRpbmcgPSBcImxpbmstc3RyaW5nXCI7XHJcbiAgICAgICAgdmFyIHJldHVyblN0YXRlID0gZ2V0VHlwZShzdGF0ZSk7XHJcbiAgICAgICAgc3RhdGUubGlua0hyZWYgPSBmYWxzZTtcclxuICAgICAgICByZXR1cm4gcmV0dXJuU3RhdGU7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGlmIChzdHJlYW0ubWF0Y2goaW5saW5lUkUoZW5kQ2hhciksIHRydWUpKSB7XHJcbiAgICAgICAgc3RyZWFtLmJhY2tVcCgxKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgc3RhdGUubGlua0hyZWYgPSB0cnVlO1xyXG4gICAgICByZXR1cm4gZ2V0VHlwZShzdGF0ZSk7XHJcbiAgICB9O1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gZm9vdG5vdGVMaW5rKHN0cmVhbSwgc3RhdGUpIHtcclxuICAgIGlmIChzdHJlYW0ubWF0Y2goL15bXlxcXV0qXFxdOi8sIGZhbHNlKSkge1xyXG4gICAgICBzdGF0ZS5mID0gZm9vdG5vdGVMaW5rSW5zaWRlO1xyXG4gICAgICBzdHJlYW0ubmV4dCgpOyAvLyBDb25zdW1lIFtcclxuICAgICAgaWYgKG1vZGVDZmcuaGlnaGxpZ2h0Rm9ybWF0dGluZykgc3RhdGUuZm9ybWF0dGluZyA9IFwibGlua1wiO1xyXG4gICAgICBzdGF0ZS5saW5rVGV4dCA9IHRydWU7XHJcbiAgICAgIHJldHVybiBnZXRUeXBlKHN0YXRlKTtcclxuICAgIH1cclxuICAgIHJldHVybiBzd2l0Y2hJbmxpbmUoc3RyZWFtLCBzdGF0ZSwgaW5saW5lTm9ybWFsKTtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIGZvb3Rub3RlTGlua0luc2lkZShzdHJlYW0sIHN0YXRlKSB7XHJcbiAgICBpZiAoc3RyZWFtLm1hdGNoKC9eXFxdOi8sIHRydWUpKSB7XHJcbiAgICAgIHN0YXRlLmYgPSBzdGF0ZS5pbmxpbmUgPSBmb290bm90ZVVybDtcclxuICAgICAgaWYgKG1vZGVDZmcuaGlnaGxpZ2h0Rm9ybWF0dGluZykgc3RhdGUuZm9ybWF0dGluZyA9IFwibGlua1wiO1xyXG4gICAgICB2YXIgcmV0dXJuVHlwZSA9IGdldFR5cGUoc3RhdGUpO1xyXG4gICAgICBzdGF0ZS5saW5rVGV4dCA9IGZhbHNlO1xyXG4gICAgICByZXR1cm4gcmV0dXJuVHlwZTtcclxuICAgIH1cclxuXHJcbiAgICBzdHJlYW0ubWF0Y2goL15bXlxcXV0rLywgdHJ1ZSk7XHJcblxyXG4gICAgcmV0dXJuIGxpbmt0ZXh0O1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gZm9vdG5vdGVVcmwoc3RyZWFtLCBzdGF0ZSkge1xyXG4gICAgLy8gQ2hlY2sgaWYgc3BhY2UsIGFuZCByZXR1cm4gTlVMTCBpZiBzbyAodG8gYXZvaWQgbWFya2luZyB0aGUgc3BhY2UpXHJcbiAgICBpZihzdHJlYW0uZWF0U3BhY2UoKSl7XHJcbiAgICAgIHJldHVybiBudWxsO1xyXG4gICAgfVxyXG4gICAgLy8gTWF0Y2ggVVJMXHJcbiAgICBzdHJlYW0ubWF0Y2goL15bXlxcc10rLywgdHJ1ZSk7XHJcbiAgICAvLyBDaGVjayBmb3IgbGluayB0aXRsZVxyXG4gICAgaWYgKHN0cmVhbS5wZWVrKCkgPT09IHVuZGVmaW5lZCkgeyAvLyBFbmQgb2YgbGluZSwgc2V0IGZsYWcgdG8gY2hlY2sgbmV4dCBsaW5lXHJcbiAgICAgIHN0YXRlLmxpbmtUaXRsZSA9IHRydWU7XHJcbiAgICB9IGVsc2UgeyAvLyBNb3JlIGNvbnRlbnQgb24gbGluZSwgY2hlY2sgaWYgbGluayB0aXRsZVxyXG4gICAgICBzdHJlYW0ubWF0Y2goL14oPzpcXHMrKD86XCIoPzpbXlwiXFxcXF18XFxcXFxcXFx8XFxcXC4pK1wifCcoPzpbXidcXFxcXXxcXFxcXFxcXHxcXFxcLikrJ3xcXCgoPzpbXilcXFxcXXxcXFxcXFxcXHxcXFxcLikrXFwpKSk/LywgdHJ1ZSk7XHJcbiAgICB9XHJcbiAgICBzdGF0ZS5mID0gc3RhdGUuaW5saW5lID0gaW5saW5lTm9ybWFsO1xyXG4gICAgcmV0dXJuIGxpbmtocmVmICsgXCIgdXJsXCI7XHJcbiAgfVxyXG5cclxuICB2YXIgc2F2ZWRJbmxpbmVSRSA9IFtdO1xyXG4gIGZ1bmN0aW9uIGlubGluZVJFKGVuZENoYXIpIHtcclxuICAgIGlmICghc2F2ZWRJbmxpbmVSRVtlbmRDaGFyXSkge1xyXG4gICAgICAvLyBFc2NhcGUgZW5kQ2hhciBmb3IgUmVnRXhwICh0YWtlbiBmcm9tIGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9hLzQ5NDEyMi81MjY3NDEpXHJcbiAgICAgIGVuZENoYXIgPSAoZW5kQ2hhcisnJykucmVwbGFjZSgvKFsuPyorXiRbXFxdXFxcXCgpe318LV0pL2csIFwiXFxcXCQxXCIpO1xyXG4gICAgICAvLyBNYXRjaCBhbnkgbm9uLWVuZENoYXIsIGVzY2FwZWQgY2hhcmFjdGVyLCBhcyB3ZWxsIGFzIHRoZSBjbG9zaW5nXHJcbiAgICAgIC8vIGVuZENoYXIuXHJcbiAgICAgIHNhdmVkSW5saW5lUkVbZW5kQ2hhcl0gPSBuZXcgUmVnRXhwKCdeKD86W15cXFxcXFxcXF18XFxcXFxcXFwuKSo/KCcgKyBlbmRDaGFyICsgJyknKTtcclxuICAgIH1cclxuICAgIHJldHVybiBzYXZlZElubGluZVJFW2VuZENoYXJdO1xyXG4gIH1cclxuXHJcbiAgdmFyIG1vZGUgPSB7XHJcbiAgICBzdGFydFN0YXRlOiBmdW5jdGlvbigpIHtcclxuICAgICAgcmV0dXJuIHtcclxuICAgICAgICBmOiBibG9ja05vcm1hbCxcclxuXHJcbiAgICAgICAgcHJldkxpbmU6IG51bGwsXHJcbiAgICAgICAgdGhpc0xpbmU6IG51bGwsXHJcblxyXG4gICAgICAgIGJsb2NrOiBibG9ja05vcm1hbCxcclxuICAgICAgICBodG1sU3RhdGU6IG51bGwsXHJcbiAgICAgICAgaW5kZW50YXRpb246IDAsXHJcblxyXG4gICAgICAgIGlubGluZTogaW5saW5lTm9ybWFsLFxyXG4gICAgICAgIHRleHQ6IGhhbmRsZVRleHQsXHJcblxyXG4gICAgICAgIGZvcm1hdHRpbmc6IGZhbHNlLFxyXG4gICAgICAgIGxpbmtUZXh0OiBmYWxzZSxcclxuICAgICAgICBsaW5rSHJlZjogZmFsc2UsXHJcbiAgICAgICAgbGlua1RpdGxlOiBmYWxzZSxcclxuICAgICAgICBlbTogZmFsc2UsXHJcbiAgICAgICAgc3Ryb25nOiBmYWxzZSxcclxuICAgICAgICBoZWFkZXI6IDAsXHJcbiAgICAgICAgaHI6IGZhbHNlLFxyXG4gICAgICAgIHRhc2tMaXN0OiBmYWxzZSxcclxuICAgICAgICBsaXN0OiBmYWxzZSxcclxuICAgICAgICBsaXN0RGVwdGg6IDAsXHJcbiAgICAgICAgcXVvdGU6IDAsXHJcbiAgICAgICAgdHJhaWxpbmdTcGFjZTogMCxcclxuICAgICAgICB0cmFpbGluZ1NwYWNlTmV3TGluZTogZmFsc2UsXHJcbiAgICAgICAgc3RyaWtldGhyb3VnaDogZmFsc2UsXHJcbiAgICAgICAgZmVuY2VkQ2hhcnM6IG51bGxcclxuICAgICAgfTtcclxuICAgIH0sXHJcblxyXG4gICAgY29weVN0YXRlOiBmdW5jdGlvbihzKSB7XHJcbiAgICAgIHJldHVybiB7XHJcbiAgICAgICAgZjogcy5mLFxyXG5cclxuICAgICAgICBwcmV2TGluZTogcy5wcmV2TGluZSxcclxuICAgICAgICB0aGlzTGluZTogcy50aGlzLFxyXG5cclxuICAgICAgICBibG9jazogcy5ibG9jayxcclxuICAgICAgICBodG1sU3RhdGU6IHMuaHRtbFN0YXRlICYmIENvZGVNaXJyb3IuY29weVN0YXRlKGh0bWxNb2RlLCBzLmh0bWxTdGF0ZSksXHJcbiAgICAgICAgaW5kZW50YXRpb246IHMuaW5kZW50YXRpb24sXHJcblxyXG4gICAgICAgIGxvY2FsTW9kZTogcy5sb2NhbE1vZGUsXHJcbiAgICAgICAgbG9jYWxTdGF0ZTogcy5sb2NhbE1vZGUgPyBDb2RlTWlycm9yLmNvcHlTdGF0ZShzLmxvY2FsTW9kZSwgcy5sb2NhbFN0YXRlKSA6IG51bGwsXHJcblxyXG4gICAgICAgIGlubGluZTogcy5pbmxpbmUsXHJcbiAgICAgICAgdGV4dDogcy50ZXh0LFxyXG4gICAgICAgIGZvcm1hdHRpbmc6IGZhbHNlLFxyXG4gICAgICAgIGxpbmtUaXRsZTogcy5saW5rVGl0bGUsXHJcbiAgICAgICAgY29kZTogcy5jb2RlLFxyXG4gICAgICAgIGVtOiBzLmVtLFxyXG4gICAgICAgIHN0cm9uZzogcy5zdHJvbmcsXHJcbiAgICAgICAgc3RyaWtldGhyb3VnaDogcy5zdHJpa2V0aHJvdWdoLFxyXG4gICAgICAgIGhlYWRlcjogcy5oZWFkZXIsXHJcbiAgICAgICAgaHI6IHMuaHIsXHJcbiAgICAgICAgdGFza0xpc3Q6IHMudGFza0xpc3QsXHJcbiAgICAgICAgbGlzdDogcy5saXN0LFxyXG4gICAgICAgIGxpc3REZXB0aDogcy5saXN0RGVwdGgsXHJcbiAgICAgICAgcXVvdGU6IHMucXVvdGUsXHJcbiAgICAgICAgaW5kZW50ZWRDb2RlOiBzLmluZGVudGVkQ29kZSxcclxuICAgICAgICB0cmFpbGluZ1NwYWNlOiBzLnRyYWlsaW5nU3BhY2UsXHJcbiAgICAgICAgdHJhaWxpbmdTcGFjZU5ld0xpbmU6IHMudHJhaWxpbmdTcGFjZU5ld0xpbmUsXHJcbiAgICAgICAgbWRfaW5zaWRlOiBzLm1kX2luc2lkZSxcclxuICAgICAgICBmZW5jZWRDaGFyczogcy5mZW5jZWRDaGFyc1xyXG4gICAgICB9O1xyXG4gICAgfSxcclxuXHJcbiAgICB0b2tlbjogZnVuY3Rpb24oc3RyZWFtLCBzdGF0ZSkge1xyXG5cclxuICAgICAgLy8gUmVzZXQgc3RhdGUuZm9ybWF0dGluZ1xyXG4gICAgICBzdGF0ZS5mb3JtYXR0aW5nID0gZmFsc2U7XHJcblxyXG4gICAgICBpZiAoc3RyZWFtICE9IHN0YXRlLnRoaXNMaW5lKSB7XHJcbiAgICAgICAgdmFyIGZvcmNlQmxhbmtMaW5lID0gc3RhdGUuaGVhZGVyIHx8IHN0YXRlLmhyO1xyXG5cclxuICAgICAgICAvLyBSZXNldCBzdGF0ZS5oZWFkZXIgYW5kIHN0YXRlLmhyXHJcbiAgICAgICAgc3RhdGUuaGVhZGVyID0gMDtcclxuICAgICAgICBzdGF0ZS5ociA9IGZhbHNlO1xyXG5cclxuICAgICAgICBpZiAoc3RyZWFtLm1hdGNoKC9eXFxzKiQvLCB0cnVlKSB8fCBmb3JjZUJsYW5rTGluZSkge1xyXG4gICAgICAgICAgYmxhbmtMaW5lKHN0YXRlKTtcclxuICAgICAgICAgIGlmICghZm9yY2VCbGFua0xpbmUpIHJldHVybiBudWxsXHJcbiAgICAgICAgICBzdGF0ZS5wcmV2TGluZSA9IG51bGxcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHN0YXRlLnByZXZMaW5lID0gc3RhdGUudGhpc0xpbmVcclxuICAgICAgICBzdGF0ZS50aGlzTGluZSA9IHN0cmVhbVxyXG5cclxuICAgICAgICAvLyBSZXNldCBzdGF0ZS50YXNrTGlzdFxyXG4gICAgICAgIHN0YXRlLnRhc2tMaXN0ID0gZmFsc2U7XHJcblxyXG4gICAgICAgIC8vIFJlc2V0IHN0YXRlLnRyYWlsaW5nU3BhY2VcclxuICAgICAgICBzdGF0ZS50cmFpbGluZ1NwYWNlID0gMDtcclxuICAgICAgICBzdGF0ZS50cmFpbGluZ1NwYWNlTmV3TGluZSA9IGZhbHNlO1xyXG5cclxuICAgICAgICBzdGF0ZS5mID0gc3RhdGUuYmxvY2s7XHJcbiAgICAgICAgdmFyIGluZGVudGF0aW9uID0gc3RyZWFtLm1hdGNoKC9eXFxzKi8sIHRydWUpWzBdLnJlcGxhY2UoL1xcdC9nLCAnICAgICcpLmxlbmd0aDtcclxuICAgICAgICB2YXIgZGlmZmVyZW5jZSA9IE1hdGguZmxvb3IoKGluZGVudGF0aW9uIC0gc3RhdGUuaW5kZW50YXRpb24pIC8gNCkgKiA0O1xyXG4gICAgICAgIGlmIChkaWZmZXJlbmNlID4gNCkgZGlmZmVyZW5jZSA9IDQ7XHJcbiAgICAgICAgdmFyIGFkanVzdGVkSW5kZW50YXRpb24gPSBzdGF0ZS5pbmRlbnRhdGlvbiArIGRpZmZlcmVuY2U7XHJcbiAgICAgICAgc3RhdGUuaW5kZW50YXRpb25EaWZmID0gYWRqdXN0ZWRJbmRlbnRhdGlvbiAtIHN0YXRlLmluZGVudGF0aW9uO1xyXG4gICAgICAgIHN0YXRlLmluZGVudGF0aW9uID0gYWRqdXN0ZWRJbmRlbnRhdGlvbjtcclxuICAgICAgICBpZiAoaW5kZW50YXRpb24gPiAwKSByZXR1cm4gbnVsbDtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gc3RhdGUuZihzdHJlYW0sIHN0YXRlKTtcclxuICAgIH0sXHJcblxyXG4gICAgaW5uZXJNb2RlOiBmdW5jdGlvbihzdGF0ZSkge1xyXG4gICAgICBpZiAoc3RhdGUuYmxvY2sgPT0gaHRtbEJsb2NrKSByZXR1cm4ge3N0YXRlOiBzdGF0ZS5odG1sU3RhdGUsIG1vZGU6IGh0bWxNb2RlfTtcclxuICAgICAgaWYgKHN0YXRlLmxvY2FsU3RhdGUpIHJldHVybiB7c3RhdGU6IHN0YXRlLmxvY2FsU3RhdGUsIG1vZGU6IHN0YXRlLmxvY2FsTW9kZX07XHJcbiAgICAgIHJldHVybiB7c3RhdGU6IHN0YXRlLCBtb2RlOiBtb2RlfTtcclxuICAgIH0sXHJcblxyXG4gICAgYmxhbmtMaW5lOiBibGFua0xpbmUsXHJcblxyXG4gICAgZ2V0VHlwZTogZ2V0VHlwZSxcclxuXHJcbiAgICBmb2xkOiBcIm1hcmtkb3duXCJcclxuICB9O1xyXG4gIHJldHVybiBtb2RlO1xyXG59LCBcInhtbFwiKTtcclxuXHJcbkNvZGVNaXJyb3IuZGVmaW5lTUlNRShcInRleHQveC1tYXJrZG93blwiLCBcIm1hcmtkb3duXCIpO1xyXG5cclxufSk7XHJcbiIsIi8vIENvZGVNaXJyb3IsIGNvcHlyaWdodCAoYykgYnkgTWFyaWpuIEhhdmVyYmVrZSBhbmQgb3RoZXJzXHJcbi8vIERpc3RyaWJ1dGVkIHVuZGVyIGFuIE1JVCBsaWNlbnNlOiBodHRwOi8vY29kZW1pcnJvci5uZXQvTElDRU5TRVxyXG5cclxuKGZ1bmN0aW9uKG1vZCkge1xyXG4gIGlmICh0eXBlb2YgZXhwb3J0cyA9PSBcIm9iamVjdFwiICYmIHR5cGVvZiBtb2R1bGUgPT0gXCJvYmplY3RcIikgLy8gQ29tbW9uSlNcclxuICAgIG1vZChyZXF1aXJlKFwiLi4vbGliL2NvZGVtaXJyb3JcIikpO1xyXG4gIGVsc2UgaWYgKHR5cGVvZiBkZWZpbmUgPT0gXCJmdW5jdGlvblwiICYmIGRlZmluZS5hbWQpIC8vIEFNRFxyXG4gICAgZGVmaW5lKFtcIi4uL2xpYi9jb2RlbWlycm9yXCJdLCBtb2QpO1xyXG4gIGVsc2UgLy8gUGxhaW4gYnJvd3NlciBlbnZcclxuICAgIG1vZChDb2RlTWlycm9yKTtcclxufSkoZnVuY3Rpb24oQ29kZU1pcnJvcikge1xyXG4gIFwidXNlIHN0cmljdFwiO1xyXG5cclxuICBDb2RlTWlycm9yLm1vZGVJbmZvID0gW1xyXG4gICAge25hbWU6IFwiQVBMXCIsIG1pbWU6IFwidGV4dC9hcGxcIiwgbW9kZTogXCJhcGxcIiwgZXh0OiBbXCJkeWFsb2dcIiwgXCJhcGxcIl19LFxyXG4gICAge25hbWU6IFwiUEdQXCIsIG1pbWVzOiBbXCJhcHBsaWNhdGlvbi9wZ3BcIiwgXCJhcHBsaWNhdGlvbi9wZ3Ata2V5c1wiLCBcImFwcGxpY2F0aW9uL3BncC1zaWduYXR1cmVcIl0sIG1vZGU6IFwiYXNjaWlhcm1vclwiLCBleHQ6IFtcInBncFwiXX0sXHJcbiAgICB7bmFtZTogXCJBU04uMVwiLCBtaW1lOiBcInRleHQveC10dGNuLWFzblwiLCBtb2RlOiBcImFzbi4xXCIsIGV4dDogW1wiYXNuXCIsIFwiYXNuMVwiXX0sXHJcbiAgICB7bmFtZTogXCJBc3Rlcmlza1wiLCBtaW1lOiBcInRleHQveC1hc3Rlcmlza1wiLCBtb2RlOiBcImFzdGVyaXNrXCIsIGZpbGU6IC9eZXh0ZW5zaW9uc1xcLmNvbmYkL2l9LFxyXG4gICAge25hbWU6IFwiQnJhaW5mdWNrXCIsIG1pbWU6IFwidGV4dC94LWJyYWluZnVja1wiLCBtb2RlOiBcImJyYWluZnVja1wiLCBleHQ6IFtcImJcIiwgXCJiZlwiXX0sXHJcbiAgICB7bmFtZTogXCJDXCIsIG1pbWU6IFwidGV4dC94LWNzcmNcIiwgbW9kZTogXCJjbGlrZVwiLCBleHQ6IFtcImNcIiwgXCJoXCJdfSxcclxuICAgIHtuYW1lOiBcIkMrK1wiLCBtaW1lOiBcInRleHQveC1jKytzcmNcIiwgbW9kZTogXCJjbGlrZVwiLCBleHQ6IFtcImNwcFwiLCBcImMrK1wiLCBcImNjXCIsIFwiY3h4XCIsIFwiaHBwXCIsIFwiaCsrXCIsIFwiaGhcIiwgXCJoeHhcIl0sIGFsaWFzOiBbXCJjcHBcIl19LFxyXG4gICAge25hbWU6IFwiQ29ib2xcIiwgbWltZTogXCJ0ZXh0L3gtY29ib2xcIiwgbW9kZTogXCJjb2JvbFwiLCBleHQ6IFtcImNvYlwiLCBcImNweVwiXX0sXHJcbiAgICB7bmFtZTogXCJDI1wiLCBtaW1lOiBcInRleHQveC1jc2hhcnBcIiwgbW9kZTogXCJjbGlrZVwiLCBleHQ6IFtcImNzXCJdLCBhbGlhczogW1wiY3NoYXJwXCJdfSxcclxuICAgIHtuYW1lOiBcIkNsb2p1cmVcIiwgbWltZTogXCJ0ZXh0L3gtY2xvanVyZVwiLCBtb2RlOiBcImNsb2p1cmVcIiwgZXh0OiBbXCJjbGpcIl19LFxyXG4gICAge25hbWU6IFwiQ2xvc3VyZSBTdHlsZXNoZWV0cyAoR1NTKVwiLCBtaW1lOiBcInRleHQveC1nc3NcIiwgbW9kZTogXCJjc3NcIiwgZXh0OiBbXCJnc3NcIl19LFxyXG4gICAge25hbWU6IFwiQ01ha2VcIiwgbWltZTogXCJ0ZXh0L3gtY21ha2VcIiwgbW9kZTogXCJjbWFrZVwiLCBleHQ6IFtcImNtYWtlXCIsIFwiY21ha2UuaW5cIl0sIGZpbGU6IC9eQ01ha2VMaXN0cy50eHQkL30sXHJcbiAgICB7bmFtZTogXCJDb2ZmZWVTY3JpcHRcIiwgbWltZTogXCJ0ZXh0L3gtY29mZmVlc2NyaXB0XCIsIG1vZGU6IFwiY29mZmVlc2NyaXB0XCIsIGV4dDogW1wiY29mZmVlXCJdLCBhbGlhczogW1wiY29mZmVlXCIsIFwiY29mZmVlLXNjcmlwdFwiXX0sXHJcbiAgICB7bmFtZTogXCJDb21tb24gTGlzcFwiLCBtaW1lOiBcInRleHQveC1jb21tb24tbGlzcFwiLCBtb2RlOiBcImNvbW1vbmxpc3BcIiwgZXh0OiBbXCJjbFwiLCBcImxpc3BcIiwgXCJlbFwiXSwgYWxpYXM6IFtcImxpc3BcIl19LFxyXG4gICAge25hbWU6IFwiQ3lwaGVyXCIsIG1pbWU6IFwiYXBwbGljYXRpb24veC1jeXBoZXItcXVlcnlcIiwgbW9kZTogXCJjeXBoZXJcIiwgZXh0OiBbXCJjeXBcIiwgXCJjeXBoZXJcIl19LFxyXG4gICAge25hbWU6IFwiQ3l0aG9uXCIsIG1pbWU6IFwidGV4dC94LWN5dGhvblwiLCBtb2RlOiBcInB5dGhvblwiLCBleHQ6IFtcInB5eFwiLCBcInB4ZFwiLCBcInB4aVwiXX0sXHJcbiAgICB7bmFtZTogXCJDU1NcIiwgbWltZTogXCJ0ZXh0L2Nzc1wiLCBtb2RlOiBcImNzc1wiLCBleHQ6IFtcImNzc1wiXX0sXHJcbiAgICB7bmFtZTogXCJDUUxcIiwgbWltZTogXCJ0ZXh0L3gtY2Fzc2FuZHJhXCIsIG1vZGU6IFwic3FsXCIsIGV4dDogW1wiY3FsXCJdfSxcclxuICAgIHtuYW1lOiBcIkRcIiwgbWltZTogXCJ0ZXh0L3gtZFwiLCBtb2RlOiBcImRcIiwgZXh0OiBbXCJkXCJdfSxcclxuICAgIHtuYW1lOiBcIkRhcnRcIiwgbWltZXM6IFtcImFwcGxpY2F0aW9uL2RhcnRcIiwgXCJ0ZXh0L3gtZGFydFwiXSwgbW9kZTogXCJkYXJ0XCIsIGV4dDogW1wiZGFydFwiXX0sXHJcbiAgICB7bmFtZTogXCJkaWZmXCIsIG1pbWU6IFwidGV4dC94LWRpZmZcIiwgbW9kZTogXCJkaWZmXCIsIGV4dDogW1wiZGlmZlwiLCBcInBhdGNoXCJdfSxcclxuICAgIHtuYW1lOiBcIkRqYW5nb1wiLCBtaW1lOiBcInRleHQveC1kamFuZ29cIiwgbW9kZTogXCJkamFuZ29cIn0sXHJcbiAgICB7bmFtZTogXCJEb2NrZXJmaWxlXCIsIG1pbWU6IFwidGV4dC94LWRvY2tlcmZpbGVcIiwgbW9kZTogXCJkb2NrZXJmaWxlXCIsIGZpbGU6IC9eRG9ja2VyZmlsZSQvfSxcclxuICAgIHtuYW1lOiBcIkRURFwiLCBtaW1lOiBcImFwcGxpY2F0aW9uL3htbC1kdGRcIiwgbW9kZTogXCJkdGRcIiwgZXh0OiBbXCJkdGRcIl19LFxyXG4gICAge25hbWU6IFwiRHlsYW5cIiwgbWltZTogXCJ0ZXh0L3gtZHlsYW5cIiwgbW9kZTogXCJkeWxhblwiLCBleHQ6IFtcImR5bGFuXCIsIFwiZHlsXCIsIFwiaW50clwiXX0sXHJcbiAgICB7bmFtZTogXCJFQk5GXCIsIG1pbWU6IFwidGV4dC94LWVibmZcIiwgbW9kZTogXCJlYm5mXCJ9LFxyXG4gICAge25hbWU6IFwiRUNMXCIsIG1pbWU6IFwidGV4dC94LWVjbFwiLCBtb2RlOiBcImVjbFwiLCBleHQ6IFtcImVjbFwiXX0sXHJcbiAgICB7bmFtZTogXCJFaWZmZWxcIiwgbWltZTogXCJ0ZXh0L3gtZWlmZmVsXCIsIG1vZGU6IFwiZWlmZmVsXCIsIGV4dDogW1wiZVwiXX0sXHJcbiAgICB7bmFtZTogXCJFbG1cIiwgbWltZTogXCJ0ZXh0L3gtZWxtXCIsIG1vZGU6IFwiZWxtXCIsIGV4dDogW1wiZWxtXCJdfSxcclxuICAgIHtuYW1lOiBcIkVtYmVkZGVkIEphdmFzY3JpcHRcIiwgbWltZTogXCJhcHBsaWNhdGlvbi94LWVqc1wiLCBtb2RlOiBcImh0bWxlbWJlZGRlZFwiLCBleHQ6IFtcImVqc1wiXX0sXHJcbiAgICB7bmFtZTogXCJFbWJlZGRlZCBSdWJ5XCIsIG1pbWU6IFwiYXBwbGljYXRpb24veC1lcmJcIiwgbW9kZTogXCJodG1sZW1iZWRkZWRcIiwgZXh0OiBbXCJlcmJcIl19LFxyXG4gICAge25hbWU6IFwiRXJsYW5nXCIsIG1pbWU6IFwidGV4dC94LWVybGFuZ1wiLCBtb2RlOiBcImVybGFuZ1wiLCBleHQ6IFtcImVybFwiXX0sXHJcbiAgICB7bmFtZTogXCJGYWN0b3JcIiwgbWltZTogXCJ0ZXh0L3gtZmFjdG9yXCIsIG1vZGU6IFwiZmFjdG9yXCIsIGV4dDogW1wiZmFjdG9yXCJdfSxcclxuICAgIHtuYW1lOiBcIkZvcnRoXCIsIG1pbWU6IFwidGV4dC94LWZvcnRoXCIsIG1vZGU6IFwiZm9ydGhcIiwgZXh0OiBbXCJmb3J0aFwiLCBcImZ0aFwiLCBcIjR0aFwiXX0sXHJcbiAgICB7bmFtZTogXCJGb3J0cmFuXCIsIG1pbWU6IFwidGV4dC94LWZvcnRyYW5cIiwgbW9kZTogXCJmb3J0cmFuXCIsIGV4dDogW1wiZlwiLCBcImZvclwiLCBcImY3N1wiLCBcImY5MFwiXX0sXHJcbiAgICB7bmFtZTogXCJGI1wiLCBtaW1lOiBcInRleHQveC1mc2hhcnBcIiwgbW9kZTogXCJtbGxpa2VcIiwgZXh0OiBbXCJmc1wiXSwgYWxpYXM6IFtcImZzaGFycFwiXX0sXHJcbiAgICB7bmFtZTogXCJHYXNcIiwgbWltZTogXCJ0ZXh0L3gtZ2FzXCIsIG1vZGU6IFwiZ2FzXCIsIGV4dDogW1wic1wiXX0sXHJcbiAgICB7bmFtZTogXCJHaGVya2luXCIsIG1pbWU6IFwidGV4dC94LWZlYXR1cmVcIiwgbW9kZTogXCJnaGVya2luXCIsIGV4dDogW1wiZmVhdHVyZVwiXX0sXHJcbiAgICB7bmFtZTogXCJHaXRIdWIgRmxhdm9yZWQgTWFya2Rvd25cIiwgbWltZTogXCJ0ZXh0L3gtZ2ZtXCIsIG1vZGU6IFwiZ2ZtXCIsIGZpbGU6IC9eKHJlYWRtZXxjb250cmlidXRpbmd8aGlzdG9yeSkubWQkL2l9LFxyXG4gICAge25hbWU6IFwiR29cIiwgbWltZTogXCJ0ZXh0L3gtZ29cIiwgbW9kZTogXCJnb1wiLCBleHQ6IFtcImdvXCJdfSxcclxuICAgIHtuYW1lOiBcIkdyb292eVwiLCBtaW1lOiBcInRleHQveC1ncm9vdnlcIiwgbW9kZTogXCJncm9vdnlcIiwgZXh0OiBbXCJncm9vdnlcIl19LFxyXG4gICAge25hbWU6IFwiSEFNTFwiLCBtaW1lOiBcInRleHQveC1oYW1sXCIsIG1vZGU6IFwiaGFtbFwiLCBleHQ6IFtcImhhbWxcIl19LFxyXG4gICAge25hbWU6IFwiSGFza2VsbFwiLCBtaW1lOiBcInRleHQveC1oYXNrZWxsXCIsIG1vZGU6IFwiaGFza2VsbFwiLCBleHQ6IFtcImhzXCJdfSxcclxuICAgIHtuYW1lOiBcIkhheGVcIiwgbWltZTogXCJ0ZXh0L3gtaGF4ZVwiLCBtb2RlOiBcImhheGVcIiwgZXh0OiBbXCJoeFwiXX0sXHJcbiAgICB7bmFtZTogXCJIWE1MXCIsIG1pbWU6IFwidGV4dC94LWh4bWxcIiwgbW9kZTogXCJoYXhlXCIsIGV4dDogW1wiaHhtbFwiXX0sXHJcbiAgICB7bmFtZTogXCJBU1AuTkVUXCIsIG1pbWU6IFwiYXBwbGljYXRpb24veC1hc3B4XCIsIG1vZGU6IFwiaHRtbGVtYmVkZGVkXCIsIGV4dDogW1wiYXNweFwiXSwgYWxpYXM6IFtcImFzcFwiLCBcImFzcHhcIl19LFxyXG4gICAge25hbWU6IFwiSFRNTFwiLCBtaW1lOiBcInRleHQvaHRtbFwiLCBtb2RlOiBcImh0bWxtaXhlZFwiLCBleHQ6IFtcImh0bWxcIiwgXCJodG1cIl0sIGFsaWFzOiBbXCJ4aHRtbFwiXX0sXHJcbiAgICB7bmFtZTogXCJIVFRQXCIsIG1pbWU6IFwibWVzc2FnZS9odHRwXCIsIG1vZGU6IFwiaHR0cFwifSxcclxuICAgIHtuYW1lOiBcIklETFwiLCBtaW1lOiBcInRleHQveC1pZGxcIiwgbW9kZTogXCJpZGxcIiwgZXh0OiBbXCJwcm9cIl19LFxyXG4gICAge25hbWU6IFwiSmFkZVwiLCBtaW1lOiBcInRleHQveC1qYWRlXCIsIG1vZGU6IFwiamFkZVwiLCBleHQ6IFtcImphZGVcIl19LFxyXG4gICAge25hbWU6IFwiSmF2YVwiLCBtaW1lOiBcInRleHQveC1qYXZhXCIsIG1vZGU6IFwiY2xpa2VcIiwgZXh0OiBbXCJqYXZhXCJdfSxcclxuICAgIHtuYW1lOiBcIkphdmEgU2VydmVyIFBhZ2VzXCIsIG1pbWU6IFwiYXBwbGljYXRpb24veC1qc3BcIiwgbW9kZTogXCJodG1sZW1iZWRkZWRcIiwgZXh0OiBbXCJqc3BcIl0sIGFsaWFzOiBbXCJqc3BcIl19LFxyXG4gICAge25hbWU6IFwiSmF2YVNjcmlwdFwiLCBtaW1lczogW1widGV4dC9qYXZhc2NyaXB0XCIsIFwidGV4dC9lY21hc2NyaXB0XCIsIFwiYXBwbGljYXRpb24vamF2YXNjcmlwdFwiLCBcImFwcGxpY2F0aW9uL3gtamF2YXNjcmlwdFwiLCBcImFwcGxpY2F0aW9uL2VjbWFzY3JpcHRcIl0sXHJcbiAgICAgbW9kZTogXCJqYXZhc2NyaXB0XCIsIGV4dDogW1wianNcIl0sIGFsaWFzOiBbXCJlY21hc2NyaXB0XCIsIFwianNcIiwgXCJub2RlXCJdfSxcclxuICAgIHtuYW1lOiBcIkpTT05cIiwgbWltZXM6IFtcImFwcGxpY2F0aW9uL2pzb25cIiwgXCJhcHBsaWNhdGlvbi94LWpzb25cIl0sIG1vZGU6IFwiamF2YXNjcmlwdFwiLCBleHQ6IFtcImpzb25cIiwgXCJtYXBcIl0sIGFsaWFzOiBbXCJqc29uNVwiXX0sXHJcbiAgICB7bmFtZTogXCJKU09OLUxEXCIsIG1pbWU6IFwiYXBwbGljYXRpb24vbGQranNvblwiLCBtb2RlOiBcImphdmFzY3JpcHRcIiwgZXh0OiBbXCJqc29ubGRcIl0sIGFsaWFzOiBbXCJqc29ubGRcIl19LFxyXG4gICAge25hbWU6IFwiSmluamEyXCIsIG1pbWU6IFwibnVsbFwiLCBtb2RlOiBcImppbmphMlwifSxcclxuICAgIHtuYW1lOiBcIkp1bGlhXCIsIG1pbWU6IFwidGV4dC94LWp1bGlhXCIsIG1vZGU6IFwianVsaWFcIiwgZXh0OiBbXCJqbFwiXX0sXHJcbiAgICB7bmFtZTogXCJLb3RsaW5cIiwgbWltZTogXCJ0ZXh0L3gta290bGluXCIsIG1vZGU6IFwiY2xpa2VcIiwgZXh0OiBbXCJrdFwiXX0sXHJcbiAgICB7bmFtZTogXCJMRVNTXCIsIG1pbWU6IFwidGV4dC94LWxlc3NcIiwgbW9kZTogXCJjc3NcIiwgZXh0OiBbXCJsZXNzXCJdfSxcclxuICAgIHtuYW1lOiBcIkxpdmVTY3JpcHRcIiwgbWltZTogXCJ0ZXh0L3gtbGl2ZXNjcmlwdFwiLCBtb2RlOiBcImxpdmVzY3JpcHRcIiwgZXh0OiBbXCJsc1wiXSwgYWxpYXM6IFtcImxzXCJdfSxcclxuICAgIHtuYW1lOiBcIkx1YVwiLCBtaW1lOiBcInRleHQveC1sdWFcIiwgbW9kZTogXCJsdWFcIiwgZXh0OiBbXCJsdWFcIl19LFxyXG4gICAge25hbWU6IFwiTWFya2Rvd25cIiwgbWltZTogXCJ0ZXh0L3gtbWFya2Rvd25cIiwgbW9kZTogXCJtYXJrZG93blwiLCBleHQ6IFtcIm1hcmtkb3duXCIsIFwibWRcIiwgXCJta2RcIl19LFxyXG4gICAge25hbWU6IFwibUlSQ1wiLCBtaW1lOiBcInRleHQvbWlyY1wiLCBtb2RlOiBcIm1pcmNcIn0sXHJcbiAgICB7bmFtZTogXCJNYXJpYURCIFNRTFwiLCBtaW1lOiBcInRleHQveC1tYXJpYWRiXCIsIG1vZGU6IFwic3FsXCJ9LFxyXG4gICAge25hbWU6IFwiTWF0aGVtYXRpY2FcIiwgbWltZTogXCJ0ZXh0L3gtbWF0aGVtYXRpY2FcIiwgbW9kZTogXCJtYXRoZW1hdGljYVwiLCBleHQ6IFtcIm1cIiwgXCJuYlwiXX0sXHJcbiAgICB7bmFtZTogXCJNb2RlbGljYVwiLCBtaW1lOiBcInRleHQveC1tb2RlbGljYVwiLCBtb2RlOiBcIm1vZGVsaWNhXCIsIGV4dDogW1wibW9cIl19LFxyXG4gICAge25hbWU6IFwiTVVNUFNcIiwgbWltZTogXCJ0ZXh0L3gtbXVtcHNcIiwgbW9kZTogXCJtdW1wc1wifSxcclxuICAgIHtuYW1lOiBcIk1TIFNRTFwiLCBtaW1lOiBcInRleHQveC1tc3NxbFwiLCBtb2RlOiBcInNxbFwifSxcclxuICAgIHtuYW1lOiBcIk15U1FMXCIsIG1pbWU6IFwidGV4dC94LW15c3FsXCIsIG1vZGU6IFwic3FsXCJ9LFxyXG4gICAge25hbWU6IFwiTmdpbnhcIiwgbWltZTogXCJ0ZXh0L3gtbmdpbngtY29uZlwiLCBtb2RlOiBcIm5naW54XCIsIGZpbGU6IC9uZ2lueC4qXFwuY29uZiQvaX0sXHJcbiAgICB7bmFtZTogXCJOVHJpcGxlc1wiLCBtaW1lOiBcInRleHQvbi10cmlwbGVzXCIsIG1vZGU6IFwibnRyaXBsZXNcIiwgZXh0OiBbXCJudFwiXX0sXHJcbiAgICB7bmFtZTogXCJPYmplY3RpdmUgQ1wiLCBtaW1lOiBcInRleHQveC1vYmplY3RpdmVjXCIsIG1vZGU6IFwiY2xpa2VcIiwgZXh0OiBbXCJtXCIsIFwibW1cIl19LFxyXG4gICAge25hbWU6IFwiT0NhbWxcIiwgbWltZTogXCJ0ZXh0L3gtb2NhbWxcIiwgbW9kZTogXCJtbGxpa2VcIiwgZXh0OiBbXCJtbFwiLCBcIm1saVwiLCBcIm1sbFwiLCBcIm1seVwiXX0sXHJcbiAgICB7bmFtZTogXCJPY3RhdmVcIiwgbWltZTogXCJ0ZXh0L3gtb2N0YXZlXCIsIG1vZGU6IFwib2N0YXZlXCIsIGV4dDogW1wibVwiXX0sXHJcbiAgICB7bmFtZTogXCJPelwiLCBtaW1lOiBcInRleHQveC1velwiLCBtb2RlOiBcIm96XCIsIGV4dDogW1wib3pcIl19LFxyXG4gICAge25hbWU6IFwiUGFzY2FsXCIsIG1pbWU6IFwidGV4dC94LXBhc2NhbFwiLCBtb2RlOiBcInBhc2NhbFwiLCBleHQ6IFtcInBcIiwgXCJwYXNcIl19LFxyXG4gICAge25hbWU6IFwiUEVHLmpzXCIsIG1pbWU6IFwibnVsbFwiLCBtb2RlOiBcInBlZ2pzXCIsIGV4dDogW1wianNvbmxkXCJdfSxcclxuICAgIHtuYW1lOiBcIlBlcmxcIiwgbWltZTogXCJ0ZXh0L3gtcGVybFwiLCBtb2RlOiBcInBlcmxcIiwgZXh0OiBbXCJwbFwiLCBcInBtXCJdfSxcclxuICAgIHtuYW1lOiBcIlBIUFwiLCBtaW1lOiBcImFwcGxpY2F0aW9uL3gtaHR0cGQtcGhwXCIsIG1vZGU6IFwicGhwXCIsIGV4dDogW1wicGhwXCIsIFwicGhwM1wiLCBcInBocDRcIiwgXCJwaHA1XCIsIFwicGh0bWxcIl19LFxyXG4gICAge25hbWU6IFwiUGlnXCIsIG1pbWU6IFwidGV4dC94LXBpZ1wiLCBtb2RlOiBcInBpZ1wiLCBleHQ6IFtcInBpZ1wiXX0sXHJcbiAgICB7bmFtZTogXCJQbGFpbiBUZXh0XCIsIG1pbWU6IFwidGV4dC9wbGFpblwiLCBtb2RlOiBcIm51bGxcIiwgZXh0OiBbXCJ0eHRcIiwgXCJ0ZXh0XCIsIFwiY29uZlwiLCBcImRlZlwiLCBcImxpc3RcIiwgXCJsb2dcIl19LFxyXG4gICAge25hbWU6IFwiUExTUUxcIiwgbWltZTogXCJ0ZXh0L3gtcGxzcWxcIiwgbW9kZTogXCJzcWxcIiwgZXh0OiBbXCJwbHNcIl19LFxyXG4gICAge25hbWU6IFwiUHJvcGVydGllcyBmaWxlc1wiLCBtaW1lOiBcInRleHQveC1wcm9wZXJ0aWVzXCIsIG1vZGU6IFwicHJvcGVydGllc1wiLCBleHQ6IFtcInByb3BlcnRpZXNcIiwgXCJpbmlcIiwgXCJpblwiXSwgYWxpYXM6IFtcImluaVwiLCBcInByb3BlcnRpZXNcIl19LFxyXG4gICAge25hbWU6IFwiUHl0aG9uXCIsIG1pbWU6IFwidGV4dC94LXB5dGhvblwiLCBtb2RlOiBcInB5dGhvblwiLCBleHQ6IFtcInB5XCIsIFwicHl3XCJdfSxcclxuICAgIHtuYW1lOiBcIlB1cHBldFwiLCBtaW1lOiBcInRleHQveC1wdXBwZXRcIiwgbW9kZTogXCJwdXBwZXRcIiwgZXh0OiBbXCJwcFwiXX0sXHJcbiAgICB7bmFtZTogXCJRXCIsIG1pbWU6IFwidGV4dC94LXFcIiwgbW9kZTogXCJxXCIsIGV4dDogW1wicVwiXX0sXHJcbiAgICB7bmFtZTogXCJSXCIsIG1pbWU6IFwidGV4dC94LXJzcmNcIiwgbW9kZTogXCJyXCIsIGV4dDogW1wiclwiXSwgYWxpYXM6IFtcInJzY3JpcHRcIl19LFxyXG4gICAge25hbWU6IFwicmVTdHJ1Y3R1cmVkVGV4dFwiLCBtaW1lOiBcInRleHQveC1yc3RcIiwgbW9kZTogXCJyc3RcIiwgZXh0OiBbXCJyc3RcIl0sIGFsaWFzOiBbXCJyc3RcIl19LFxyXG4gICAge25hbWU6IFwiUlBNIENoYW5nZXNcIiwgbWltZTogXCJ0ZXh0L3gtcnBtLWNoYW5nZXNcIiwgbW9kZTogXCJycG1cIn0sXHJcbiAgICB7bmFtZTogXCJSUE0gU3BlY1wiLCBtaW1lOiBcInRleHQveC1ycG0tc3BlY1wiLCBtb2RlOiBcInJwbVwiLCBleHQ6IFtcInNwZWNcIl19LFxyXG4gICAge25hbWU6IFwiUnVieVwiLCBtaW1lOiBcInRleHQveC1ydWJ5XCIsIG1vZGU6IFwicnVieVwiLCBleHQ6IFtcInJiXCJdLCBhbGlhczogW1wianJ1YnlcIiwgXCJtYWNydWJ5XCIsIFwicmFrZVwiLCBcInJiXCIsIFwicmJ4XCJdfSxcclxuICAgIHtuYW1lOiBcIlJ1c3RcIiwgbWltZTogXCJ0ZXh0L3gtcnVzdHNyY1wiLCBtb2RlOiBcInJ1c3RcIiwgZXh0OiBbXCJyc1wiXX0sXHJcbiAgICB7bmFtZTogXCJTYXNzXCIsIG1pbWU6IFwidGV4dC94LXNhc3NcIiwgbW9kZTogXCJzYXNzXCIsIGV4dDogW1wic2Fzc1wiXX0sXHJcbiAgICB7bmFtZTogXCJTY2FsYVwiLCBtaW1lOiBcInRleHQveC1zY2FsYVwiLCBtb2RlOiBcImNsaWtlXCIsIGV4dDogW1wic2NhbGFcIl19LFxyXG4gICAge25hbWU6IFwiU2NoZW1lXCIsIG1pbWU6IFwidGV4dC94LXNjaGVtZVwiLCBtb2RlOiBcInNjaGVtZVwiLCBleHQ6IFtcInNjbVwiLCBcInNzXCJdfSxcclxuICAgIHtuYW1lOiBcIlNDU1NcIiwgbWltZTogXCJ0ZXh0L3gtc2Nzc1wiLCBtb2RlOiBcImNzc1wiLCBleHQ6IFtcInNjc3NcIl19LFxyXG4gICAge25hbWU6IFwiU2hlbGxcIiwgbWltZTogXCJ0ZXh0L3gtc2hcIiwgbW9kZTogXCJzaGVsbFwiLCBleHQ6IFtcInNoXCIsIFwia3NoXCIsIFwiYmFzaFwiXSwgYWxpYXM6IFtcImJhc2hcIiwgXCJzaFwiLCBcInpzaFwiXX0sXHJcbiAgICB7bmFtZTogXCJTaWV2ZVwiLCBtaW1lOiBcImFwcGxpY2F0aW9uL3NpZXZlXCIsIG1vZGU6IFwic2lldmVcIiwgZXh0OiBbXCJzaXZcIiwgXCJzaWV2ZVwiXX0sXHJcbiAgICB7bmFtZTogXCJTbGltXCIsIG1pbWVzOiBbXCJ0ZXh0L3gtc2xpbVwiLCBcImFwcGxpY2F0aW9uL3gtc2xpbVwiXSwgbW9kZTogXCJzbGltXCIsIGV4dDogW1wic2xpbVwiXX0sXHJcbiAgICB7bmFtZTogXCJTbWFsbHRhbGtcIiwgbWltZTogXCJ0ZXh0L3gtc3RzcmNcIiwgbW9kZTogXCJzbWFsbHRhbGtcIiwgZXh0OiBbXCJzdFwiXX0sXHJcbiAgICB7bmFtZTogXCJTbWFydHlcIiwgbWltZTogXCJ0ZXh0L3gtc21hcnR5XCIsIG1vZGU6IFwic21hcnR5XCIsIGV4dDogW1widHBsXCJdfSxcclxuICAgIHtuYW1lOiBcIlNvbHJcIiwgbWltZTogXCJ0ZXh0L3gtc29sclwiLCBtb2RlOiBcInNvbHJcIn0sXHJcbiAgICB7bmFtZTogXCJTb3lcIiwgbWltZTogXCJ0ZXh0L3gtc295XCIsIG1vZGU6IFwic295XCIsIGV4dDogW1wic295XCJdLCBhbGlhczogW1wiY2xvc3VyZSB0ZW1wbGF0ZVwiXX0sXHJcbiAgICB7bmFtZTogXCJTUEFSUUxcIiwgbWltZTogXCJhcHBsaWNhdGlvbi9zcGFycWwtcXVlcnlcIiwgbW9kZTogXCJzcGFycWxcIiwgZXh0OiBbXCJycVwiLCBcInNwYXJxbFwiXSwgYWxpYXM6IFtcInNwYXJ1bFwiXX0sXHJcbiAgICB7bmFtZTogXCJTcHJlYWRzaGVldFwiLCBtaW1lOiBcInRleHQveC1zcHJlYWRzaGVldFwiLCBtb2RlOiBcInNwcmVhZHNoZWV0XCIsIGFsaWFzOiBbXCJleGNlbFwiLCBcImZvcm11bGFcIl19LFxyXG4gICAge25hbWU6IFwiU1FMXCIsIG1pbWU6IFwidGV4dC94LXNxbFwiLCBtb2RlOiBcInNxbFwiLCBleHQ6IFtcInNxbFwiXX0sXHJcbiAgICB7bmFtZTogXCJTcXVpcnJlbFwiLCBtaW1lOiBcInRleHQveC1zcXVpcnJlbFwiLCBtb2RlOiBcImNsaWtlXCIsIGV4dDogW1wibnV0XCJdfSxcclxuICAgIHtuYW1lOiBcIlN3aWZ0XCIsIG1pbWU6IFwidGV4dC94LXN3aWZ0XCIsIG1vZGU6IFwic3dpZnRcIiwgZXh0OiBbXCJzd2lmdFwiXX0sXHJcbiAgICB7bmFtZTogXCJNYXJpYURCXCIsIG1pbWU6IFwidGV4dC94LW1hcmlhZGJcIiwgbW9kZTogXCJzcWxcIn0sXHJcbiAgICB7bmFtZTogXCJzVGVYXCIsIG1pbWU6IFwidGV4dC94LXN0ZXhcIiwgbW9kZTogXCJzdGV4XCJ9LFxyXG4gICAge25hbWU6IFwiTGFUZVhcIiwgbWltZTogXCJ0ZXh0L3gtbGF0ZXhcIiwgbW9kZTogXCJzdGV4XCIsIGV4dDogW1widGV4dFwiLCBcImx0eFwiXSwgYWxpYXM6IFtcInRleFwiXX0sXHJcbiAgICB7bmFtZTogXCJTeXN0ZW1WZXJpbG9nXCIsIG1pbWU6IFwidGV4dC94LXN5c3RlbXZlcmlsb2dcIiwgbW9kZTogXCJ2ZXJpbG9nXCIsIGV4dDogW1widlwiXX0sXHJcbiAgICB7bmFtZTogXCJUY2xcIiwgbWltZTogXCJ0ZXh0L3gtdGNsXCIsIG1vZGU6IFwidGNsXCIsIGV4dDogW1widGNsXCJdfSxcclxuICAgIHtuYW1lOiBcIlRleHRpbGVcIiwgbWltZTogXCJ0ZXh0L3gtdGV4dGlsZVwiLCBtb2RlOiBcInRleHRpbGVcIiwgZXh0OiBbXCJ0ZXh0aWxlXCJdfSxcclxuICAgIHtuYW1lOiBcIlRpZGRseVdpa2kgXCIsIG1pbWU6IFwidGV4dC94LXRpZGRseXdpa2lcIiwgbW9kZTogXCJ0aWRkbHl3aWtpXCJ9LFxyXG4gICAge25hbWU6IFwiVGlraSB3aWtpXCIsIG1pbWU6IFwidGV4dC90aWtpXCIsIG1vZGU6IFwidGlraVwifSxcclxuICAgIHtuYW1lOiBcIlRPTUxcIiwgbWltZTogXCJ0ZXh0L3gtdG9tbFwiLCBtb2RlOiBcInRvbWxcIiwgZXh0OiBbXCJ0b21sXCJdfSxcclxuICAgIHtuYW1lOiBcIlRvcm5hZG9cIiwgbWltZTogXCJ0ZXh0L3gtdG9ybmFkb1wiLCBtb2RlOiBcInRvcm5hZG9cIn0sXHJcbiAgICB7bmFtZTogXCJ0cm9mZlwiLCBtaW1lOiBcInRyb2ZmXCIsIG1vZGU6IFwidHJvZmZcIiwgZXh0OiBbXCIxXCIsIFwiMlwiLCBcIjNcIiwgXCI0XCIsIFwiNVwiLCBcIjZcIiwgXCI3XCIsIFwiOFwiLCBcIjlcIl19LFxyXG4gICAge25hbWU6IFwiVFRDTlwiLCBtaW1lOiBcInRleHQveC10dGNuXCIsIG1vZGU6IFwidHRjblwiLCBleHQ6IFtcInR0Y25cIiwgXCJ0dGNuM1wiLCBcInR0Y25wcFwiXX0sXHJcbiAgICB7bmFtZTogXCJUVENOX0NGR1wiLCBtaW1lOiBcInRleHQveC10dGNuLWNmZ1wiLCBtb2RlOiBcInR0Y24tY2ZnXCIsIGV4dDogW1wiY2ZnXCJdfSxcclxuICAgIHtuYW1lOiBcIlR1cnRsZVwiLCBtaW1lOiBcInRleHQvdHVydGxlXCIsIG1vZGU6IFwidHVydGxlXCIsIGV4dDogW1widHRsXCJdfSxcclxuICAgIHtuYW1lOiBcIlR5cGVTY3JpcHRcIiwgbWltZTogXCJhcHBsaWNhdGlvbi90eXBlc2NyaXB0XCIsIG1vZGU6IFwiamF2YXNjcmlwdFwiLCBleHQ6IFtcInRzXCJdLCBhbGlhczogW1widHNcIl19LFxyXG4gICAge25hbWU6IFwiVHdpZ1wiLCBtaW1lOiBcInRleHQveC10d2lnXCIsIG1vZGU6IFwidHdpZ1wifSxcclxuICAgIHtuYW1lOiBcIlZCLk5FVFwiLCBtaW1lOiBcInRleHQveC12YlwiLCBtb2RlOiBcInZiXCIsIGV4dDogW1widmJcIl19LFxyXG4gICAge25hbWU6IFwiVkJTY3JpcHRcIiwgbWltZTogXCJ0ZXh0L3Zic2NyaXB0XCIsIG1vZGU6IFwidmJzY3JpcHRcIiwgZXh0OiBbXCJ2YnNcIl19LFxyXG4gICAge25hbWU6IFwiVmVsb2NpdHlcIiwgbWltZTogXCJ0ZXh0L3ZlbG9jaXR5XCIsIG1vZGU6IFwidmVsb2NpdHlcIiwgZXh0OiBbXCJ2dGxcIl19LFxyXG4gICAge25hbWU6IFwiVmVyaWxvZ1wiLCBtaW1lOiBcInRleHQveC12ZXJpbG9nXCIsIG1vZGU6IFwidmVyaWxvZ1wiLCBleHQ6IFtcInZcIl19LFxyXG4gICAge25hbWU6IFwiVkhETFwiLCBtaW1lOiBcInRleHQveC12aGRsXCIsIG1vZGU6IFwidmhkbFwiLCBleHQ6IFtcInZoZFwiLCBcInZoZGxcIl19LFxyXG4gICAge25hbWU6IFwiWE1MXCIsIG1pbWVzOiBbXCJhcHBsaWNhdGlvbi94bWxcIiwgXCJ0ZXh0L3htbFwiXSwgbW9kZTogXCJ4bWxcIiwgZXh0OiBbXCJ4bWxcIiwgXCJ4c2xcIiwgXCJ4c2RcIl0sIGFsaWFzOiBbXCJyc3NcIiwgXCJ3c2RsXCIsIFwieHNkXCJdfSxcclxuICAgIHtuYW1lOiBcIlhRdWVyeVwiLCBtaW1lOiBcImFwcGxpY2F0aW9uL3hxdWVyeVwiLCBtb2RlOiBcInhxdWVyeVwiLCBleHQ6IFtcInh5XCIsIFwieHF1ZXJ5XCJdfSxcclxuICAgIHtuYW1lOiBcIllBTUxcIiwgbWltZTogXCJ0ZXh0L3gteWFtbFwiLCBtb2RlOiBcInlhbWxcIiwgZXh0OiBbXCJ5YW1sXCIsIFwieW1sXCJdLCBhbGlhczogW1wieW1sXCJdfSxcclxuICAgIHtuYW1lOiBcIlo4MFwiLCBtaW1lOiBcInRleHQveC16ODBcIiwgbW9kZTogXCJ6ODBcIiwgZXh0OiBbXCJ6ODBcIl19LFxyXG4gICAge25hbWU6IFwibXNjZ2VuXCIsIG1pbWU6IFwidGV4dC94LW1zY2dlblwiLCBtb2RlOiBcIm1zY2dlblwiLCBleHQ6IFtcIm1zY2dlblwiLCBcIm1zY2luXCIsIFwibXNjXCJdfSxcclxuICAgIHtuYW1lOiBcInh1XCIsIG1pbWU6IFwidGV4dC94LXh1XCIsIG1vZGU6IFwibXNjZ2VuXCIsIGV4dDogW1wieHVcIl19LFxyXG4gICAge25hbWU6IFwibXNnZW5ueVwiLCBtaW1lOiBcInRleHQveC1tc2dlbm55XCIsIG1vZGU6IFwibXNjZ2VuXCIsIGV4dDogW1wibXNnZW5ueVwiXX1cclxuICBdO1xyXG4gIC8vIEVuc3VyZSBhbGwgbW9kZXMgaGF2ZSBhIG1pbWUgcHJvcGVydHkgZm9yIGJhY2t3YXJkcyBjb21wYXRpYmlsaXR5XHJcbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBDb2RlTWlycm9yLm1vZGVJbmZvLmxlbmd0aDsgaSsrKSB7XHJcbiAgICB2YXIgaW5mbyA9IENvZGVNaXJyb3IubW9kZUluZm9baV07XHJcbiAgICBpZiAoaW5mby5taW1lcykgaW5mby5taW1lID0gaW5mby5taW1lc1swXTtcclxuICB9XHJcblxyXG4gIENvZGVNaXJyb3IuZmluZE1vZGVCeU1JTUUgPSBmdW5jdGlvbihtaW1lKSB7XHJcbiAgICBtaW1lID0gbWltZS50b0xvd2VyQ2FzZSgpO1xyXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBDb2RlTWlycm9yLm1vZGVJbmZvLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgIHZhciBpbmZvID0gQ29kZU1pcnJvci5tb2RlSW5mb1tpXTtcclxuICAgICAgaWYgKGluZm8ubWltZSA9PSBtaW1lKSByZXR1cm4gaW5mbztcclxuICAgICAgaWYgKGluZm8ubWltZXMpIGZvciAodmFyIGogPSAwOyBqIDwgaW5mby5taW1lcy5sZW5ndGg7IGorKylcclxuICAgICAgICBpZiAoaW5mby5taW1lc1tqXSA9PSBtaW1lKSByZXR1cm4gaW5mbztcclxuICAgIH1cclxuICB9O1xyXG5cclxuICBDb2RlTWlycm9yLmZpbmRNb2RlQnlFeHRlbnNpb24gPSBmdW5jdGlvbihleHQpIHtcclxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgQ29kZU1pcnJvci5tb2RlSW5mby5sZW5ndGg7IGkrKykge1xyXG4gICAgICB2YXIgaW5mbyA9IENvZGVNaXJyb3IubW9kZUluZm9baV07XHJcbiAgICAgIGlmIChpbmZvLmV4dCkgZm9yICh2YXIgaiA9IDA7IGogPCBpbmZvLmV4dC5sZW5ndGg7IGorKylcclxuICAgICAgICBpZiAoaW5mby5leHRbal0gPT0gZXh0KSByZXR1cm4gaW5mbztcclxuICAgIH1cclxuICB9O1xyXG5cclxuICBDb2RlTWlycm9yLmZpbmRNb2RlQnlGaWxlTmFtZSA9IGZ1bmN0aW9uKGZpbGVuYW1lKSB7XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IENvZGVNaXJyb3IubW9kZUluZm8ubGVuZ3RoOyBpKyspIHtcclxuICAgICAgdmFyIGluZm8gPSBDb2RlTWlycm9yLm1vZGVJbmZvW2ldO1xyXG4gICAgICBpZiAoaW5mby5maWxlICYmIGluZm8uZmlsZS50ZXN0KGZpbGVuYW1lKSkgcmV0dXJuIGluZm87XHJcbiAgICB9XHJcbiAgICB2YXIgZG90ID0gZmlsZW5hbWUubGFzdEluZGV4T2YoXCIuXCIpO1xyXG4gICAgdmFyIGV4dCA9IGRvdCA+IC0xICYmIGZpbGVuYW1lLnN1YnN0cmluZyhkb3QgKyAxLCBmaWxlbmFtZS5sZW5ndGgpO1xyXG4gICAgaWYgKGV4dCkgcmV0dXJuIENvZGVNaXJyb3IuZmluZE1vZGVCeUV4dGVuc2lvbihleHQpO1xyXG4gIH07XHJcblxyXG4gIENvZGVNaXJyb3IuZmluZE1vZGVCeU5hbWUgPSBmdW5jdGlvbihuYW1lKSB7XHJcbiAgICBuYW1lID0gbmFtZS50b0xvd2VyQ2FzZSgpO1xyXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBDb2RlTWlycm9yLm1vZGVJbmZvLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgIHZhciBpbmZvID0gQ29kZU1pcnJvci5tb2RlSW5mb1tpXTtcclxuICAgICAgaWYgKGluZm8ubmFtZS50b0xvd2VyQ2FzZSgpID09IG5hbWUpIHJldHVybiBpbmZvO1xyXG4gICAgICBpZiAoaW5mby5hbGlhcykgZm9yICh2YXIgaiA9IDA7IGogPCBpbmZvLmFsaWFzLmxlbmd0aDsgaisrKVxyXG4gICAgICAgIGlmIChpbmZvLmFsaWFzW2pdLnRvTG93ZXJDYXNlKCkgPT0gbmFtZSkgcmV0dXJuIGluZm87XHJcbiAgICB9XHJcbiAgfTtcclxufSk7XHJcbiIsIi8vIENvZGVNaXJyb3IsIGNvcHlyaWdodCAoYykgYnkgTWFyaWpuIEhhdmVyYmVrZSBhbmQgb3RoZXJzXHJcbi8vIERpc3RyaWJ1dGVkIHVuZGVyIGFuIE1JVCBsaWNlbnNlOiBodHRwOi8vY29kZW1pcnJvci5uZXQvTElDRU5TRVxyXG5cclxuKGZ1bmN0aW9uKG1vZCkge1xyXG4gIGlmICh0eXBlb2YgZXhwb3J0cyA9PSBcIm9iamVjdFwiICYmIHR5cGVvZiBtb2R1bGUgPT0gXCJvYmplY3RcIikgLy8gQ29tbW9uSlNcclxuICAgIG1vZChyZXF1aXJlKFwiLi4vLi4vbGliL2NvZGVtaXJyb3JcIikpO1xyXG4gIGVsc2UgaWYgKHR5cGVvZiBkZWZpbmUgPT0gXCJmdW5jdGlvblwiICYmIGRlZmluZS5hbWQpIC8vIEFNRFxyXG4gICAgZGVmaW5lKFtcIi4uLy4uL2xpYi9jb2RlbWlycm9yXCJdLCBtb2QpO1xyXG4gIGVsc2UgLy8gUGxhaW4gYnJvd3NlciBlbnZcclxuICAgIG1vZChDb2RlTWlycm9yKTtcclxufSkoZnVuY3Rpb24oQ29kZU1pcnJvcikge1xyXG5cInVzZSBzdHJpY3RcIjtcclxuXHJcbkNvZGVNaXJyb3IuZGVmaW5lTW9kZShcInhtbFwiLCBmdW5jdGlvbihjb25maWcsIHBhcnNlckNvbmZpZykge1xyXG4gIHZhciBpbmRlbnRVbml0ID0gY29uZmlnLmluZGVudFVuaXQ7XHJcbiAgdmFyIG11bHRpbGluZVRhZ0luZGVudEZhY3RvciA9IHBhcnNlckNvbmZpZy5tdWx0aWxpbmVUYWdJbmRlbnRGYWN0b3IgfHwgMTtcclxuICB2YXIgbXVsdGlsaW5lVGFnSW5kZW50UGFzdFRhZyA9IHBhcnNlckNvbmZpZy5tdWx0aWxpbmVUYWdJbmRlbnRQYXN0VGFnO1xyXG4gIGlmIChtdWx0aWxpbmVUYWdJbmRlbnRQYXN0VGFnID09IG51bGwpIG11bHRpbGluZVRhZ0luZGVudFBhc3RUYWcgPSB0cnVlO1xyXG5cclxuICB2YXIgS2x1ZGdlcyA9IHBhcnNlckNvbmZpZy5odG1sTW9kZSA/IHtcclxuICAgIGF1dG9TZWxmQ2xvc2VyczogeydhcmVhJzogdHJ1ZSwgJ2Jhc2UnOiB0cnVlLCAnYnInOiB0cnVlLCAnY29sJzogdHJ1ZSwgJ2NvbW1hbmQnOiB0cnVlLFxyXG4gICAgICAgICAgICAgICAgICAgICAgJ2VtYmVkJzogdHJ1ZSwgJ2ZyYW1lJzogdHJ1ZSwgJ2hyJzogdHJ1ZSwgJ2ltZyc6IHRydWUsICdpbnB1dCc6IHRydWUsXHJcbiAgICAgICAgICAgICAgICAgICAgICAna2V5Z2VuJzogdHJ1ZSwgJ2xpbmsnOiB0cnVlLCAnbWV0YSc6IHRydWUsICdwYXJhbSc6IHRydWUsICdzb3VyY2UnOiB0cnVlLFxyXG4gICAgICAgICAgICAgICAgICAgICAgJ3RyYWNrJzogdHJ1ZSwgJ3dicic6IHRydWUsICdtZW51aXRlbSc6IHRydWV9LFxyXG4gICAgaW1wbGljaXRseUNsb3NlZDogeydkZCc6IHRydWUsICdsaSc6IHRydWUsICdvcHRncm91cCc6IHRydWUsICdvcHRpb24nOiB0cnVlLCAncCc6IHRydWUsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgJ3JwJzogdHJ1ZSwgJ3J0JzogdHJ1ZSwgJ3Rib2R5JzogdHJ1ZSwgJ3RkJzogdHJ1ZSwgJ3Rmb290JzogdHJ1ZSxcclxuICAgICAgICAgICAgICAgICAgICAgICAndGgnOiB0cnVlLCAndHInOiB0cnVlfSxcclxuICAgIGNvbnRleHRHcmFiYmVyczoge1xyXG4gICAgICAnZGQnOiB7J2RkJzogdHJ1ZSwgJ2R0JzogdHJ1ZX0sXHJcbiAgICAgICdkdCc6IHsnZGQnOiB0cnVlLCAnZHQnOiB0cnVlfSxcclxuICAgICAgJ2xpJzogeydsaSc6IHRydWV9LFxyXG4gICAgICAnb3B0aW9uJzogeydvcHRpb24nOiB0cnVlLCAnb3B0Z3JvdXAnOiB0cnVlfSxcclxuICAgICAgJ29wdGdyb3VwJzogeydvcHRncm91cCc6IHRydWV9LFxyXG4gICAgICAncCc6IHsnYWRkcmVzcyc6IHRydWUsICdhcnRpY2xlJzogdHJ1ZSwgJ2FzaWRlJzogdHJ1ZSwgJ2Jsb2NrcXVvdGUnOiB0cnVlLCAnZGlyJzogdHJ1ZSxcclxuICAgICAgICAgICAgJ2Rpdic6IHRydWUsICdkbCc6IHRydWUsICdmaWVsZHNldCc6IHRydWUsICdmb290ZXInOiB0cnVlLCAnZm9ybSc6IHRydWUsXHJcbiAgICAgICAgICAgICdoMSc6IHRydWUsICdoMic6IHRydWUsICdoMyc6IHRydWUsICdoNCc6IHRydWUsICdoNSc6IHRydWUsICdoNic6IHRydWUsXHJcbiAgICAgICAgICAgICdoZWFkZXInOiB0cnVlLCAnaGdyb3VwJzogdHJ1ZSwgJ2hyJzogdHJ1ZSwgJ21lbnUnOiB0cnVlLCAnbmF2JzogdHJ1ZSwgJ29sJzogdHJ1ZSxcclxuICAgICAgICAgICAgJ3AnOiB0cnVlLCAncHJlJzogdHJ1ZSwgJ3NlY3Rpb24nOiB0cnVlLCAndGFibGUnOiB0cnVlLCAndWwnOiB0cnVlfSxcclxuICAgICAgJ3JwJzogeydycCc6IHRydWUsICdydCc6IHRydWV9LFxyXG4gICAgICAncnQnOiB7J3JwJzogdHJ1ZSwgJ3J0JzogdHJ1ZX0sXHJcbiAgICAgICd0Ym9keSc6IHsndGJvZHknOiB0cnVlLCAndGZvb3QnOiB0cnVlfSxcclxuICAgICAgJ3RkJzogeyd0ZCc6IHRydWUsICd0aCc6IHRydWV9LFxyXG4gICAgICAndGZvb3QnOiB7J3Rib2R5JzogdHJ1ZX0sXHJcbiAgICAgICd0aCc6IHsndGQnOiB0cnVlLCAndGgnOiB0cnVlfSxcclxuICAgICAgJ3RoZWFkJzogeyd0Ym9keSc6IHRydWUsICd0Zm9vdCc6IHRydWV9LFxyXG4gICAgICAndHInOiB7J3RyJzogdHJ1ZX1cclxuICAgIH0sXHJcbiAgICBkb05vdEluZGVudDoge1wicHJlXCI6IHRydWV9LFxyXG4gICAgYWxsb3dVbnF1b3RlZDogdHJ1ZSxcclxuICAgIGFsbG93TWlzc2luZzogdHJ1ZSxcclxuICAgIGNhc2VGb2xkOiB0cnVlXHJcbiAgfSA6IHtcclxuICAgIGF1dG9TZWxmQ2xvc2Vyczoge30sXHJcbiAgICBpbXBsaWNpdGx5Q2xvc2VkOiB7fSxcclxuICAgIGNvbnRleHRHcmFiYmVyczoge30sXHJcbiAgICBkb05vdEluZGVudDoge30sXHJcbiAgICBhbGxvd1VucXVvdGVkOiBmYWxzZSxcclxuICAgIGFsbG93TWlzc2luZzogZmFsc2UsXHJcbiAgICBjYXNlRm9sZDogZmFsc2VcclxuICB9O1xyXG4gIHZhciBhbGlnbkNEQVRBID0gcGFyc2VyQ29uZmlnLmFsaWduQ0RBVEE7XHJcblxyXG4gIC8vIFJldHVybiB2YXJpYWJsZXMgZm9yIHRva2VuaXplcnNcclxuICB2YXIgdHlwZSwgc2V0U3R5bGU7XHJcblxyXG4gIGZ1bmN0aW9uIGluVGV4dChzdHJlYW0sIHN0YXRlKSB7XHJcbiAgICBmdW5jdGlvbiBjaGFpbihwYXJzZXIpIHtcclxuICAgICAgc3RhdGUudG9rZW5pemUgPSBwYXJzZXI7XHJcbiAgICAgIHJldHVybiBwYXJzZXIoc3RyZWFtLCBzdGF0ZSk7XHJcbiAgICB9XHJcblxyXG4gICAgdmFyIGNoID0gc3RyZWFtLm5leHQoKTtcclxuICAgIGlmIChjaCA9PSBcIjxcIikge1xyXG4gICAgICBpZiAoc3RyZWFtLmVhdChcIiFcIikpIHtcclxuICAgICAgICBpZiAoc3RyZWFtLmVhdChcIltcIikpIHtcclxuICAgICAgICAgIGlmIChzdHJlYW0ubWF0Y2goXCJDREFUQVtcIikpIHJldHVybiBjaGFpbihpbkJsb2NrKFwiYXRvbVwiLCBcIl1dPlwiKSk7XHJcbiAgICAgICAgICBlbHNlIHJldHVybiBudWxsO1xyXG4gICAgICAgIH0gZWxzZSBpZiAoc3RyZWFtLm1hdGNoKFwiLS1cIikpIHtcclxuICAgICAgICAgIHJldHVybiBjaGFpbihpbkJsb2NrKFwiY29tbWVudFwiLCBcIi0tPlwiKSk7XHJcbiAgICAgICAgfSBlbHNlIGlmIChzdHJlYW0ubWF0Y2goXCJET0NUWVBFXCIsIHRydWUsIHRydWUpKSB7XHJcbiAgICAgICAgICBzdHJlYW0uZWF0V2hpbGUoL1tcXHdcXC5fXFwtXS8pO1xyXG4gICAgICAgICAgcmV0dXJuIGNoYWluKGRvY3R5cGUoMSkpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICByZXR1cm4gbnVsbDtcclxuICAgICAgICB9XHJcbiAgICAgIH0gZWxzZSBpZiAoc3RyZWFtLmVhdChcIj9cIikpIHtcclxuICAgICAgICBzdHJlYW0uZWF0V2hpbGUoL1tcXHdcXC5fXFwtXS8pO1xyXG4gICAgICAgIHN0YXRlLnRva2VuaXplID0gaW5CbG9jayhcIm1ldGFcIiwgXCI/PlwiKTtcclxuICAgICAgICByZXR1cm4gXCJtZXRhXCI7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgdHlwZSA9IHN0cmVhbS5lYXQoXCIvXCIpID8gXCJjbG9zZVRhZ1wiIDogXCJvcGVuVGFnXCI7XHJcbiAgICAgICAgc3RhdGUudG9rZW5pemUgPSBpblRhZztcclxuICAgICAgICByZXR1cm4gXCJ0YWcgYnJhY2tldFwiO1xyXG4gICAgICB9XHJcbiAgICB9IGVsc2UgaWYgKGNoID09IFwiJlwiKSB7XHJcbiAgICAgIHZhciBvaztcclxuICAgICAgaWYgKHN0cmVhbS5lYXQoXCIjXCIpKSB7XHJcbiAgICAgICAgaWYgKHN0cmVhbS5lYXQoXCJ4XCIpKSB7XHJcbiAgICAgICAgICBvayA9IHN0cmVhbS5lYXRXaGlsZSgvW2EtZkEtRlxcZF0vKSAmJiBzdHJlYW0uZWF0KFwiO1wiKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgb2sgPSBzdHJlYW0uZWF0V2hpbGUoL1tcXGRdLykgJiYgc3RyZWFtLmVhdChcIjtcIik7XHJcbiAgICAgICAgfVxyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIG9rID0gc3RyZWFtLmVhdFdoaWxlKC9bXFx3XFwuXFwtOl0vKSAmJiBzdHJlYW0uZWF0KFwiO1wiKTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gb2sgPyBcImF0b21cIiA6IFwiZXJyb3JcIjtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIHN0cmVhbS5lYXRXaGlsZSgvW14mPF0vKTtcclxuICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICB9XHJcbiAgfVxyXG4gIGluVGV4dC5pc0luVGV4dCA9IHRydWU7XHJcblxyXG4gIGZ1bmN0aW9uIGluVGFnKHN0cmVhbSwgc3RhdGUpIHtcclxuICAgIHZhciBjaCA9IHN0cmVhbS5uZXh0KCk7XHJcbiAgICBpZiAoY2ggPT0gXCI+XCIgfHwgKGNoID09IFwiL1wiICYmIHN0cmVhbS5lYXQoXCI+XCIpKSkge1xyXG4gICAgICBzdGF0ZS50b2tlbml6ZSA9IGluVGV4dDtcclxuICAgICAgdHlwZSA9IGNoID09IFwiPlwiID8gXCJlbmRUYWdcIiA6IFwic2VsZmNsb3NlVGFnXCI7XHJcbiAgICAgIHJldHVybiBcInRhZyBicmFja2V0XCI7XHJcbiAgICB9IGVsc2UgaWYgKGNoID09IFwiPVwiKSB7XHJcbiAgICAgIHR5cGUgPSBcImVxdWFsc1wiO1xyXG4gICAgICByZXR1cm4gbnVsbDtcclxuICAgIH0gZWxzZSBpZiAoY2ggPT0gXCI8XCIpIHtcclxuICAgICAgc3RhdGUudG9rZW5pemUgPSBpblRleHQ7XHJcbiAgICAgIHN0YXRlLnN0YXRlID0gYmFzZVN0YXRlO1xyXG4gICAgICBzdGF0ZS50YWdOYW1lID0gc3RhdGUudGFnU3RhcnQgPSBudWxsO1xyXG4gICAgICB2YXIgbmV4dCA9IHN0YXRlLnRva2VuaXplKHN0cmVhbSwgc3RhdGUpO1xyXG4gICAgICByZXR1cm4gbmV4dCA/IG5leHQgKyBcIiB0YWcgZXJyb3JcIiA6IFwidGFnIGVycm9yXCI7XHJcbiAgICB9IGVsc2UgaWYgKC9bXFwnXFxcIl0vLnRlc3QoY2gpKSB7XHJcbiAgICAgIHN0YXRlLnRva2VuaXplID0gaW5BdHRyaWJ1dGUoY2gpO1xyXG4gICAgICBzdGF0ZS5zdHJpbmdTdGFydENvbCA9IHN0cmVhbS5jb2x1bW4oKTtcclxuICAgICAgcmV0dXJuIHN0YXRlLnRva2VuaXplKHN0cmVhbSwgc3RhdGUpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgc3RyZWFtLm1hdGNoKC9eW15cXHNcXHUwMGEwPTw+XFxcIlxcJ10qW15cXHNcXHUwMGEwPTw+XFxcIlxcJ1xcL10vKTtcclxuICAgICAgcmV0dXJuIFwid29yZFwiO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gaW5BdHRyaWJ1dGUocXVvdGUpIHtcclxuICAgIHZhciBjbG9zdXJlID0gZnVuY3Rpb24oc3RyZWFtLCBzdGF0ZSkge1xyXG4gICAgICB3aGlsZSAoIXN0cmVhbS5lb2woKSkge1xyXG4gICAgICAgIGlmIChzdHJlYW0ubmV4dCgpID09IHF1b3RlKSB7XHJcbiAgICAgICAgICBzdGF0ZS50b2tlbml6ZSA9IGluVGFnO1xyXG4gICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiBcInN0cmluZ1wiO1xyXG4gICAgfTtcclxuICAgIGNsb3N1cmUuaXNJbkF0dHJpYnV0ZSA9IHRydWU7XHJcbiAgICByZXR1cm4gY2xvc3VyZTtcclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIGluQmxvY2soc3R5bGUsIHRlcm1pbmF0b3IpIHtcclxuICAgIHJldHVybiBmdW5jdGlvbihzdHJlYW0sIHN0YXRlKSB7XHJcbiAgICAgIHdoaWxlICghc3RyZWFtLmVvbCgpKSB7XHJcbiAgICAgICAgaWYgKHN0cmVhbS5tYXRjaCh0ZXJtaW5hdG9yKSkge1xyXG4gICAgICAgICAgc3RhdGUudG9rZW5pemUgPSBpblRleHQ7XHJcbiAgICAgICAgICBicmVhaztcclxuICAgICAgICB9XHJcbiAgICAgICAgc3RyZWFtLm5leHQoKTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gc3R5bGU7XHJcbiAgICB9O1xyXG4gIH1cclxuICBmdW5jdGlvbiBkb2N0eXBlKGRlcHRoKSB7XHJcbiAgICByZXR1cm4gZnVuY3Rpb24oc3RyZWFtLCBzdGF0ZSkge1xyXG4gICAgICB2YXIgY2g7XHJcbiAgICAgIHdoaWxlICgoY2ggPSBzdHJlYW0ubmV4dCgpKSAhPSBudWxsKSB7XHJcbiAgICAgICAgaWYgKGNoID09IFwiPFwiKSB7XHJcbiAgICAgICAgICBzdGF0ZS50b2tlbml6ZSA9IGRvY3R5cGUoZGVwdGggKyAxKTtcclxuICAgICAgICAgIHJldHVybiBzdGF0ZS50b2tlbml6ZShzdHJlYW0sIHN0YXRlKTtcclxuICAgICAgICB9IGVsc2UgaWYgKGNoID09IFwiPlwiKSB7XHJcbiAgICAgICAgICBpZiAoZGVwdGggPT0gMSkge1xyXG4gICAgICAgICAgICBzdGF0ZS50b2tlbml6ZSA9IGluVGV4dDtcclxuICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICBzdGF0ZS50b2tlbml6ZSA9IGRvY3R5cGUoZGVwdGggLSAxKTtcclxuICAgICAgICAgICAgcmV0dXJuIHN0YXRlLnRva2VuaXplKHN0cmVhbSwgc3RhdGUpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gXCJtZXRhXCI7XHJcbiAgICB9O1xyXG4gIH1cclxuXHJcbiAgZnVuY3Rpb24gQ29udGV4dChzdGF0ZSwgdGFnTmFtZSwgc3RhcnRPZkxpbmUpIHtcclxuICAgIHRoaXMucHJldiA9IHN0YXRlLmNvbnRleHQ7XHJcbiAgICB0aGlzLnRhZ05hbWUgPSB0YWdOYW1lO1xyXG4gICAgdGhpcy5pbmRlbnQgPSBzdGF0ZS5pbmRlbnRlZDtcclxuICAgIHRoaXMuc3RhcnRPZkxpbmUgPSBzdGFydE9mTGluZTtcclxuICAgIGlmIChLbHVkZ2VzLmRvTm90SW5kZW50Lmhhc093blByb3BlcnR5KHRhZ05hbWUpIHx8IChzdGF0ZS5jb250ZXh0ICYmIHN0YXRlLmNvbnRleHQubm9JbmRlbnQpKVxyXG4gICAgICB0aGlzLm5vSW5kZW50ID0gdHJ1ZTtcclxuICB9XHJcbiAgZnVuY3Rpb24gcG9wQ29udGV4dChzdGF0ZSkge1xyXG4gICAgaWYgKHN0YXRlLmNvbnRleHQpIHN0YXRlLmNvbnRleHQgPSBzdGF0ZS5jb250ZXh0LnByZXY7XHJcbiAgfVxyXG4gIGZ1bmN0aW9uIG1heWJlUG9wQ29udGV4dChzdGF0ZSwgbmV4dFRhZ05hbWUpIHtcclxuICAgIHZhciBwYXJlbnRUYWdOYW1lO1xyXG4gICAgd2hpbGUgKHRydWUpIHtcclxuICAgICAgaWYgKCFzdGF0ZS5jb250ZXh0KSB7XHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgICB9XHJcbiAgICAgIHBhcmVudFRhZ05hbWUgPSBzdGF0ZS5jb250ZXh0LnRhZ05hbWU7XHJcbiAgICAgIGlmICghS2x1ZGdlcy5jb250ZXh0R3JhYmJlcnMuaGFzT3duUHJvcGVydHkocGFyZW50VGFnTmFtZSkgfHxcclxuICAgICAgICAgICFLbHVkZ2VzLmNvbnRleHRHcmFiYmVyc1twYXJlbnRUYWdOYW1lXS5oYXNPd25Qcm9wZXJ0eShuZXh0VGFnTmFtZSkpIHtcclxuICAgICAgICByZXR1cm47XHJcbiAgICAgIH1cclxuICAgICAgcG9wQ29udGV4dChzdGF0ZSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBiYXNlU3RhdGUodHlwZSwgc3RyZWFtLCBzdGF0ZSkge1xyXG4gICAgaWYgKHR5cGUgPT0gXCJvcGVuVGFnXCIpIHtcclxuICAgICAgc3RhdGUudGFnU3RhcnQgPSBzdHJlYW0uY29sdW1uKCk7XHJcbiAgICAgIHJldHVybiB0YWdOYW1lU3RhdGU7XHJcbiAgICB9IGVsc2UgaWYgKHR5cGUgPT0gXCJjbG9zZVRhZ1wiKSB7XHJcbiAgICAgIHJldHVybiBjbG9zZVRhZ05hbWVTdGF0ZTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIHJldHVybiBiYXNlU3RhdGU7XHJcbiAgICB9XHJcbiAgfVxyXG4gIGZ1bmN0aW9uIHRhZ05hbWVTdGF0ZSh0eXBlLCBzdHJlYW0sIHN0YXRlKSB7XHJcbiAgICBpZiAodHlwZSA9PSBcIndvcmRcIikge1xyXG4gICAgICBzdGF0ZS50YWdOYW1lID0gc3RyZWFtLmN1cnJlbnQoKTtcclxuICAgICAgc2V0U3R5bGUgPSBcInRhZ1wiO1xyXG4gICAgICByZXR1cm4gYXR0clN0YXRlO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgc2V0U3R5bGUgPSBcImVycm9yXCI7XHJcbiAgICAgIHJldHVybiB0YWdOYW1lU3RhdGU7XHJcbiAgICB9XHJcbiAgfVxyXG4gIGZ1bmN0aW9uIGNsb3NlVGFnTmFtZVN0YXRlKHR5cGUsIHN0cmVhbSwgc3RhdGUpIHtcclxuICAgIGlmICh0eXBlID09IFwid29yZFwiKSB7XHJcbiAgICAgIHZhciB0YWdOYW1lID0gc3RyZWFtLmN1cnJlbnQoKTtcclxuICAgICAgaWYgKHN0YXRlLmNvbnRleHQgJiYgc3RhdGUuY29udGV4dC50YWdOYW1lICE9IHRhZ05hbWUgJiZcclxuICAgICAgICAgIEtsdWRnZXMuaW1wbGljaXRseUNsb3NlZC5oYXNPd25Qcm9wZXJ0eShzdGF0ZS5jb250ZXh0LnRhZ05hbWUpKVxyXG4gICAgICAgIHBvcENvbnRleHQoc3RhdGUpO1xyXG4gICAgICBpZiAoc3RhdGUuY29udGV4dCAmJiBzdGF0ZS5jb250ZXh0LnRhZ05hbWUgPT0gdGFnTmFtZSkge1xyXG4gICAgICAgIHNldFN0eWxlID0gXCJ0YWdcIjtcclxuICAgICAgICByZXR1cm4gY2xvc2VTdGF0ZTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBzZXRTdHlsZSA9IFwidGFnIGVycm9yXCI7XHJcbiAgICAgICAgcmV0dXJuIGNsb3NlU3RhdGVFcnI7XHJcbiAgICAgIH1cclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIHNldFN0eWxlID0gXCJlcnJvclwiO1xyXG4gICAgICByZXR1cm4gY2xvc2VTdGF0ZUVycjtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIGZ1bmN0aW9uIGNsb3NlU3RhdGUodHlwZSwgX3N0cmVhbSwgc3RhdGUpIHtcclxuICAgIGlmICh0eXBlICE9IFwiZW5kVGFnXCIpIHtcclxuICAgICAgc2V0U3R5bGUgPSBcImVycm9yXCI7XHJcbiAgICAgIHJldHVybiBjbG9zZVN0YXRlO1xyXG4gICAgfVxyXG4gICAgcG9wQ29udGV4dChzdGF0ZSk7XHJcbiAgICByZXR1cm4gYmFzZVN0YXRlO1xyXG4gIH1cclxuICBmdW5jdGlvbiBjbG9zZVN0YXRlRXJyKHR5cGUsIHN0cmVhbSwgc3RhdGUpIHtcclxuICAgIHNldFN0eWxlID0gXCJlcnJvclwiO1xyXG4gICAgcmV0dXJuIGNsb3NlU3RhdGUodHlwZSwgc3RyZWFtLCBzdGF0ZSk7XHJcbiAgfVxyXG5cclxuICBmdW5jdGlvbiBhdHRyU3RhdGUodHlwZSwgX3N0cmVhbSwgc3RhdGUpIHtcclxuICAgIGlmICh0eXBlID09IFwid29yZFwiKSB7XHJcbiAgICAgIHNldFN0eWxlID0gXCJhdHRyaWJ1dGVcIjtcclxuICAgICAgcmV0dXJuIGF0dHJFcVN0YXRlO1xyXG4gICAgfSBlbHNlIGlmICh0eXBlID09IFwiZW5kVGFnXCIgfHwgdHlwZSA9PSBcInNlbGZjbG9zZVRhZ1wiKSB7XHJcbiAgICAgIHZhciB0YWdOYW1lID0gc3RhdGUudGFnTmFtZSwgdGFnU3RhcnQgPSBzdGF0ZS50YWdTdGFydDtcclxuICAgICAgc3RhdGUudGFnTmFtZSA9IHN0YXRlLnRhZ1N0YXJ0ID0gbnVsbDtcclxuICAgICAgaWYgKHR5cGUgPT0gXCJzZWxmY2xvc2VUYWdcIiB8fFxyXG4gICAgICAgICAgS2x1ZGdlcy5hdXRvU2VsZkNsb3NlcnMuaGFzT3duUHJvcGVydHkodGFnTmFtZSkpIHtcclxuICAgICAgICBtYXliZVBvcENvbnRleHQoc3RhdGUsIHRhZ05hbWUpO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIG1heWJlUG9wQ29udGV4dChzdGF0ZSwgdGFnTmFtZSk7XHJcbiAgICAgICAgc3RhdGUuY29udGV4dCA9IG5ldyBDb250ZXh0KHN0YXRlLCB0YWdOYW1lLCB0YWdTdGFydCA9PSBzdGF0ZS5pbmRlbnRlZCk7XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIGJhc2VTdGF0ZTtcclxuICAgIH1cclxuICAgIHNldFN0eWxlID0gXCJlcnJvclwiO1xyXG4gICAgcmV0dXJuIGF0dHJTdGF0ZTtcclxuICB9XHJcbiAgZnVuY3Rpb24gYXR0ckVxU3RhdGUodHlwZSwgc3RyZWFtLCBzdGF0ZSkge1xyXG4gICAgaWYgKHR5cGUgPT0gXCJlcXVhbHNcIikgcmV0dXJuIGF0dHJWYWx1ZVN0YXRlO1xyXG4gICAgaWYgKCFLbHVkZ2VzLmFsbG93TWlzc2luZykgc2V0U3R5bGUgPSBcImVycm9yXCI7XHJcbiAgICByZXR1cm4gYXR0clN0YXRlKHR5cGUsIHN0cmVhbSwgc3RhdGUpO1xyXG4gIH1cclxuICBmdW5jdGlvbiBhdHRyVmFsdWVTdGF0ZSh0eXBlLCBzdHJlYW0sIHN0YXRlKSB7XHJcbiAgICBpZiAodHlwZSA9PSBcInN0cmluZ1wiKSByZXR1cm4gYXR0ckNvbnRpbnVlZFN0YXRlO1xyXG4gICAgaWYgKHR5cGUgPT0gXCJ3b3JkXCIgJiYgS2x1ZGdlcy5hbGxvd1VucXVvdGVkKSB7c2V0U3R5bGUgPSBcInN0cmluZ1wiOyByZXR1cm4gYXR0clN0YXRlO31cclxuICAgIHNldFN0eWxlID0gXCJlcnJvclwiO1xyXG4gICAgcmV0dXJuIGF0dHJTdGF0ZSh0eXBlLCBzdHJlYW0sIHN0YXRlKTtcclxuICB9XHJcbiAgZnVuY3Rpb24gYXR0ckNvbnRpbnVlZFN0YXRlKHR5cGUsIHN0cmVhbSwgc3RhdGUpIHtcclxuICAgIGlmICh0eXBlID09IFwic3RyaW5nXCIpIHJldHVybiBhdHRyQ29udGludWVkU3RhdGU7XHJcbiAgICByZXR1cm4gYXR0clN0YXRlKHR5cGUsIHN0cmVhbSwgc3RhdGUpO1xyXG4gIH1cclxuXHJcbiAgcmV0dXJuIHtcclxuICAgIHN0YXJ0U3RhdGU6IGZ1bmN0aW9uKCkge1xyXG4gICAgICByZXR1cm4ge3Rva2VuaXplOiBpblRleHQsXHJcbiAgICAgICAgICAgICAgc3RhdGU6IGJhc2VTdGF0ZSxcclxuICAgICAgICAgICAgICBpbmRlbnRlZDogMCxcclxuICAgICAgICAgICAgICB0YWdOYW1lOiBudWxsLCB0YWdTdGFydDogbnVsbCxcclxuICAgICAgICAgICAgICBjb250ZXh0OiBudWxsfTtcclxuICAgIH0sXHJcblxyXG4gICAgdG9rZW46IGZ1bmN0aW9uKHN0cmVhbSwgc3RhdGUpIHtcclxuICAgICAgaWYgKCFzdGF0ZS50YWdOYW1lICYmIHN0cmVhbS5zb2woKSlcclxuICAgICAgICBzdGF0ZS5pbmRlbnRlZCA9IHN0cmVhbS5pbmRlbnRhdGlvbigpO1xyXG5cclxuICAgICAgaWYgKHN0cmVhbS5lYXRTcGFjZSgpKSByZXR1cm4gbnVsbDtcclxuICAgICAgdHlwZSA9IG51bGw7XHJcbiAgICAgIHZhciBzdHlsZSA9IHN0YXRlLnRva2VuaXplKHN0cmVhbSwgc3RhdGUpO1xyXG4gICAgICBpZiAoKHN0eWxlIHx8IHR5cGUpICYmIHN0eWxlICE9IFwiY29tbWVudFwiKSB7XHJcbiAgICAgICAgc2V0U3R5bGUgPSBudWxsO1xyXG4gICAgICAgIHN0YXRlLnN0YXRlID0gc3RhdGUuc3RhdGUodHlwZSB8fCBzdHlsZSwgc3RyZWFtLCBzdGF0ZSk7XHJcbiAgICAgICAgaWYgKHNldFN0eWxlKVxyXG4gICAgICAgICAgc3R5bGUgPSBzZXRTdHlsZSA9PSBcImVycm9yXCIgPyBzdHlsZSArIFwiIGVycm9yXCIgOiBzZXRTdHlsZTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gc3R5bGU7XHJcbiAgICB9LFxyXG5cclxuICAgIGluZGVudDogZnVuY3Rpb24oc3RhdGUsIHRleHRBZnRlciwgZnVsbExpbmUpIHtcclxuICAgICAgdmFyIGNvbnRleHQgPSBzdGF0ZS5jb250ZXh0O1xyXG4gICAgICAvLyBJbmRlbnQgbXVsdGktbGluZSBzdHJpbmdzIChlLmcuIGNzcykuXHJcbiAgICAgIGlmIChzdGF0ZS50b2tlbml6ZS5pc0luQXR0cmlidXRlKSB7XHJcbiAgICAgICAgaWYgKHN0YXRlLnRhZ1N0YXJ0ID09IHN0YXRlLmluZGVudGVkKVxyXG4gICAgICAgICAgcmV0dXJuIHN0YXRlLnN0cmluZ1N0YXJ0Q29sICsgMTtcclxuICAgICAgICBlbHNlXHJcbiAgICAgICAgICByZXR1cm4gc3RhdGUuaW5kZW50ZWQgKyBpbmRlbnRVbml0O1xyXG4gICAgICB9XHJcbiAgICAgIGlmIChjb250ZXh0ICYmIGNvbnRleHQubm9JbmRlbnQpIHJldHVybiBDb2RlTWlycm9yLlBhc3M7XHJcbiAgICAgIGlmIChzdGF0ZS50b2tlbml6ZSAhPSBpblRhZyAmJiBzdGF0ZS50b2tlbml6ZSAhPSBpblRleHQpXHJcbiAgICAgICAgcmV0dXJuIGZ1bGxMaW5lID8gZnVsbExpbmUubWF0Y2goL14oXFxzKikvKVswXS5sZW5ndGggOiAwO1xyXG4gICAgICAvLyBJbmRlbnQgdGhlIHN0YXJ0cyBvZiBhdHRyaWJ1dGUgbmFtZXMuXHJcbiAgICAgIGlmIChzdGF0ZS50YWdOYW1lKSB7XHJcbiAgICAgICAgaWYgKG11bHRpbGluZVRhZ0luZGVudFBhc3RUYWcpXHJcbiAgICAgICAgICByZXR1cm4gc3RhdGUudGFnU3RhcnQgKyBzdGF0ZS50YWdOYW1lLmxlbmd0aCArIDI7XHJcbiAgICAgICAgZWxzZVxyXG4gICAgICAgICAgcmV0dXJuIHN0YXRlLnRhZ1N0YXJ0ICsgaW5kZW50VW5pdCAqIG11bHRpbGluZVRhZ0luZGVudEZhY3RvcjtcclxuICAgICAgfVxyXG4gICAgICBpZiAoYWxpZ25DREFUQSAmJiAvPCFcXFtDREFUQVxcWy8udGVzdCh0ZXh0QWZ0ZXIpKSByZXR1cm4gMDtcclxuICAgICAgdmFyIHRhZ0FmdGVyID0gdGV4dEFmdGVyICYmIC9ePChcXC8pPyhbXFx3XzpcXC4tXSopLy5leGVjKHRleHRBZnRlcik7XHJcbiAgICAgIGlmICh0YWdBZnRlciAmJiB0YWdBZnRlclsxXSkgeyAvLyBDbG9zaW5nIHRhZyBzcG90dGVkXHJcbiAgICAgICAgd2hpbGUgKGNvbnRleHQpIHtcclxuICAgICAgICAgIGlmIChjb250ZXh0LnRhZ05hbWUgPT0gdGFnQWZ0ZXJbMl0pIHtcclxuICAgICAgICAgICAgY29udGV4dCA9IGNvbnRleHQucHJldjtcclxuICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgICB9IGVsc2UgaWYgKEtsdWRnZXMuaW1wbGljaXRseUNsb3NlZC5oYXNPd25Qcm9wZXJ0eShjb250ZXh0LnRhZ05hbWUpKSB7XHJcbiAgICAgICAgICAgIGNvbnRleHQgPSBjb250ZXh0LnByZXY7XHJcbiAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICBicmVhaztcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH0gZWxzZSBpZiAodGFnQWZ0ZXIpIHsgLy8gT3BlbmluZyB0YWcgc3BvdHRlZFxyXG4gICAgICAgIHdoaWxlIChjb250ZXh0KSB7XHJcbiAgICAgICAgICB2YXIgZ3JhYmJlcnMgPSBLbHVkZ2VzLmNvbnRleHRHcmFiYmVyc1tjb250ZXh0LnRhZ05hbWVdO1xyXG4gICAgICAgICAgaWYgKGdyYWJiZXJzICYmIGdyYWJiZXJzLmhhc093blByb3BlcnR5KHRhZ0FmdGVyWzJdKSlcclxuICAgICAgICAgICAgY29udGV4dCA9IGNvbnRleHQucHJldjtcclxuICAgICAgICAgIGVsc2VcclxuICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICAgIHdoaWxlIChjb250ZXh0ICYmICFjb250ZXh0LnN0YXJ0T2ZMaW5lKVxyXG4gICAgICAgIGNvbnRleHQgPSBjb250ZXh0LnByZXY7XHJcbiAgICAgIGlmIChjb250ZXh0KSByZXR1cm4gY29udGV4dC5pbmRlbnQgKyBpbmRlbnRVbml0O1xyXG4gICAgICBlbHNlIHJldHVybiAwO1xyXG4gICAgfSxcclxuXHJcbiAgICBlbGVjdHJpY0lucHV0OiAvPFxcL1tcXHNcXHc6XSs+JC8sXHJcbiAgICBibG9ja0NvbW1lbnRTdGFydDogXCI8IS0tXCIsXHJcbiAgICBibG9ja0NvbW1lbnRFbmQ6IFwiLS0+XCIsXHJcblxyXG4gICAgY29uZmlndXJhdGlvbjogcGFyc2VyQ29uZmlnLmh0bWxNb2RlID8gXCJodG1sXCIgOiBcInhtbFwiLFxyXG4gICAgaGVscGVyVHlwZTogcGFyc2VyQ29uZmlnLmh0bWxNb2RlID8gXCJodG1sXCIgOiBcInhtbFwiXHJcbiAgfTtcclxufSk7XHJcblxyXG5Db2RlTWlycm9yLmRlZmluZU1JTUUoXCJ0ZXh0L3htbFwiLCBcInhtbFwiKTtcclxuQ29kZU1pcnJvci5kZWZpbmVNSU1FKFwiYXBwbGljYXRpb24veG1sXCIsIFwieG1sXCIpO1xyXG5pZiAoIUNvZGVNaXJyb3IubWltZU1vZGVzLmhhc093blByb3BlcnR5KFwidGV4dC9odG1sXCIpKVxyXG4gIENvZGVNaXJyb3IuZGVmaW5lTUlNRShcInRleHQvaHRtbFwiLCB7bmFtZTogXCJ4bWxcIiwgaHRtbE1vZGU6IHRydWV9KTtcclxuXHJcbn0pO1xyXG4iLCIvKipcclxuICogbWFya2VkIC0gYSBtYXJrZG93biBwYXJzZXJcclxuICogQ29weXJpZ2h0IChjKSAyMDExLTIwMTQsIENocmlzdG9waGVyIEplZmZyZXkuIChNSVQgTGljZW5zZWQpXHJcbiAqIGh0dHBzOi8vZ2l0aHViLmNvbS9jaGpqL21hcmtlZFxyXG4gKi9cclxuXHJcbjsoZnVuY3Rpb24oKSB7XHJcblxyXG4vKipcclxuICogQmxvY2stTGV2ZWwgR3JhbW1hclxyXG4gKi9cclxuXHJcbnZhciBibG9jayA9IHtcclxuICBuZXdsaW5lOiAvXlxcbisvLFxyXG4gIGNvZGU6IC9eKCB7NH1bXlxcbl0rXFxuKikrLyxcclxuICBmZW5jZXM6IG5vb3AsXHJcbiAgaHI6IC9eKCAqWy0qX10pezMsfSAqKD86XFxuK3wkKS8sXHJcbiAgaGVhZGluZzogL14gKigjezEsNn0pICooW15cXG5dKz8pICojKiAqKD86XFxuK3wkKS8sXHJcbiAgbnB0YWJsZTogbm9vcCxcclxuICBsaGVhZGluZzogL14oW15cXG5dKylcXG4gKig9fC0pezIsfSAqKD86XFxuK3wkKS8sXHJcbiAgYmxvY2txdW90ZTogL14oICo+W15cXG5dKyhcXG4oPyFkZWYpW15cXG5dKykqXFxuKikrLyxcclxuICBsaXN0OiAvXiggKikoYnVsbCkgW1xcc1xcU10rPyg/OmhyfGRlZnxcXG57Mix9KD8hICkoPyFcXDFidWxsIClcXG4qfFxccyokKS8sXHJcbiAgaHRtbDogL14gKig/OmNvbW1lbnQgKig/OlxcbnxcXHMqJCl8Y2xvc2VkICooPzpcXG57Mix9fFxccyokKXxjbG9zaW5nICooPzpcXG57Mix9fFxccyokKSkvLFxyXG4gIGRlZjogL14gKlxcWyhbXlxcXV0rKVxcXTogKjw/KFteXFxzPl0rKT4/KD86ICtbXCIoXShbXlxcbl0rKVtcIildKT8gKig/Olxcbit8JCkvLFxyXG4gIHRhYmxlOiBub29wLFxyXG4gIHBhcmFncmFwaDogL14oKD86W15cXG5dK1xcbj8oPyFocnxoZWFkaW5nfGxoZWFkaW5nfGJsb2NrcXVvdGV8dGFnfGRlZikpKylcXG4qLyxcclxuICB0ZXh0OiAvXlteXFxuXSsvXHJcbn07XHJcblxyXG5ibG9jay5idWxsZXQgPSAvKD86WyorLV18XFxkK1xcLikvO1xyXG5ibG9jay5pdGVtID0gL14oICopKGJ1bGwpIFteXFxuXSooPzpcXG4oPyFcXDFidWxsIClbXlxcbl0qKSovO1xyXG5ibG9jay5pdGVtID0gcmVwbGFjZShibG9jay5pdGVtLCAnZ20nKVxyXG4gICgvYnVsbC9nLCBibG9jay5idWxsZXQpXHJcbiAgKCk7XHJcblxyXG5ibG9jay5saXN0ID0gcmVwbGFjZShibG9jay5saXN0KVxyXG4gICgvYnVsbC9nLCBibG9jay5idWxsZXQpXHJcbiAgKCdocicsICdcXFxcbisoPz1cXFxcMT8oPzpbLSpfXSAqKXszLH0oPzpcXFxcbit8JCkpJylcclxuICAoJ2RlZicsICdcXFxcbisoPz0nICsgYmxvY2suZGVmLnNvdXJjZSArICcpJylcclxuICAoKTtcclxuXHJcbmJsb2NrLmJsb2NrcXVvdGUgPSByZXBsYWNlKGJsb2NrLmJsb2NrcXVvdGUpXHJcbiAgKCdkZWYnLCBibG9jay5kZWYpXHJcbiAgKCk7XHJcblxyXG5ibG9jay5fdGFnID0gJyg/ISg/OidcclxuICArICdhfGVtfHN0cm9uZ3xzbWFsbHxzfGNpdGV8cXxkZm58YWJicnxkYXRhfHRpbWV8Y29kZSdcclxuICArICd8dmFyfHNhbXB8a2JkfHN1YnxzdXB8aXxifHV8bWFya3xydWJ5fHJ0fHJwfGJkaXxiZG8nXHJcbiAgKyAnfHNwYW58YnJ8d2JyfGluc3xkZWx8aW1nKVxcXFxiKVxcXFx3Kyg/ITovfFteXFxcXHdcXFxcc0BdKkApXFxcXGInO1xyXG5cclxuYmxvY2suaHRtbCA9IHJlcGxhY2UoYmxvY2suaHRtbClcclxuICAoJ2NvbW1lbnQnLCAvPCEtLVtcXHNcXFNdKj8tLT4vKVxyXG4gICgnY2xvc2VkJywgLzwodGFnKVtcXHNcXFNdKz88XFwvXFwxPi8pXHJcbiAgKCdjbG9zaW5nJywgLzx0YWcoPzpcIlteXCJdKlwifCdbXiddKid8W14nXCI+XSkqPz4vKVxyXG4gICgvdGFnL2csIGJsb2NrLl90YWcpXHJcbiAgKCk7XHJcblxyXG5ibG9jay5wYXJhZ3JhcGggPSByZXBsYWNlKGJsb2NrLnBhcmFncmFwaClcclxuICAoJ2hyJywgYmxvY2suaHIpXHJcbiAgKCdoZWFkaW5nJywgYmxvY2suaGVhZGluZylcclxuICAoJ2xoZWFkaW5nJywgYmxvY2subGhlYWRpbmcpXHJcbiAgKCdibG9ja3F1b3RlJywgYmxvY2suYmxvY2txdW90ZSlcclxuICAoJ3RhZycsICc8JyArIGJsb2NrLl90YWcpXHJcbiAgKCdkZWYnLCBibG9jay5kZWYpXHJcbiAgKCk7XHJcblxyXG4vKipcclxuICogTm9ybWFsIEJsb2NrIEdyYW1tYXJcclxuICovXHJcblxyXG5ibG9jay5ub3JtYWwgPSBtZXJnZSh7fSwgYmxvY2spO1xyXG5cclxuLyoqXHJcbiAqIEdGTSBCbG9jayBHcmFtbWFyXHJcbiAqL1xyXG5cclxuYmxvY2suZ2ZtID0gbWVyZ2Uoe30sIGJsb2NrLm5vcm1hbCwge1xyXG4gIGZlbmNlczogL14gKihgezMsfXx+ezMsfSlbIFxcLl0qKFxcUyspPyAqXFxuKFtcXHNcXFNdKj8pXFxzKlxcMSAqKD86XFxuK3wkKS8sXHJcbiAgcGFyYWdyYXBoOiAvXi8sXHJcbiAgaGVhZGluZzogL14gKigjezEsNn0pICsoW15cXG5dKz8pICojKiAqKD86XFxuK3wkKS9cclxufSk7XHJcblxyXG5ibG9jay5nZm0ucGFyYWdyYXBoID0gcmVwbGFjZShibG9jay5wYXJhZ3JhcGgpXHJcbiAgKCcoPyEnLCAnKD8hJ1xyXG4gICAgKyBibG9jay5nZm0uZmVuY2VzLnNvdXJjZS5yZXBsYWNlKCdcXFxcMScsICdcXFxcMicpICsgJ3wnXHJcbiAgICArIGJsb2NrLmxpc3Quc291cmNlLnJlcGxhY2UoJ1xcXFwxJywgJ1xcXFwzJykgKyAnfCcpXHJcbiAgKCk7XHJcblxyXG4vKipcclxuICogR0ZNICsgVGFibGVzIEJsb2NrIEdyYW1tYXJcclxuICovXHJcblxyXG5ibG9jay50YWJsZXMgPSBtZXJnZSh7fSwgYmxvY2suZ2ZtLCB7XHJcbiAgbnB0YWJsZTogL14gKihcXFMuKlxcfC4qKVxcbiAqKFstOl0rICpcXHxbLXwgOl0qKVxcbigoPzouKlxcfC4qKD86XFxufCQpKSopXFxuKi8sXHJcbiAgdGFibGU6IC9eICpcXHwoLispXFxuICpcXHwoICpbLTpdK1stfCA6XSopXFxuKCg/OiAqXFx8LiooPzpcXG58JCkpKilcXG4qL1xyXG59KTtcclxuXHJcbi8qKlxyXG4gKiBCbG9jayBMZXhlclxyXG4gKi9cclxuXHJcbmZ1bmN0aW9uIExleGVyKG9wdGlvbnMpIHtcclxuICB0aGlzLnRva2VucyA9IFtdO1xyXG4gIHRoaXMudG9rZW5zLmxpbmtzID0ge307XHJcbiAgdGhpcy5vcHRpb25zID0gb3B0aW9ucyB8fCBtYXJrZWQuZGVmYXVsdHM7XHJcbiAgdGhpcy5ydWxlcyA9IGJsb2NrLm5vcm1hbDtcclxuXHJcbiAgaWYgKHRoaXMub3B0aW9ucy5nZm0pIHtcclxuICAgIGlmICh0aGlzLm9wdGlvbnMudGFibGVzKSB7XHJcbiAgICAgIHRoaXMucnVsZXMgPSBibG9jay50YWJsZXM7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICB0aGlzLnJ1bGVzID0gYmxvY2suZ2ZtO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEV4cG9zZSBCbG9jayBSdWxlc1xyXG4gKi9cclxuXHJcbkxleGVyLnJ1bGVzID0gYmxvY2s7XHJcblxyXG4vKipcclxuICogU3RhdGljIExleCBNZXRob2RcclxuICovXHJcblxyXG5MZXhlci5sZXggPSBmdW5jdGlvbihzcmMsIG9wdGlvbnMpIHtcclxuICB2YXIgbGV4ZXIgPSBuZXcgTGV4ZXIob3B0aW9ucyk7XHJcbiAgcmV0dXJuIGxleGVyLmxleChzcmMpO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIFByZXByb2Nlc3NpbmdcclxuICovXHJcblxyXG5MZXhlci5wcm90b3R5cGUubGV4ID0gZnVuY3Rpb24oc3JjKSB7XHJcbiAgc3JjID0gc3JjXHJcbiAgICAucmVwbGFjZSgvXFxyXFxufFxcci9nLCAnXFxuJylcclxuICAgIC5yZXBsYWNlKC9cXHQvZywgJyAgICAnKVxyXG4gICAgLnJlcGxhY2UoL1xcdTAwYTAvZywgJyAnKVxyXG4gICAgLnJlcGxhY2UoL1xcdTI0MjQvZywgJ1xcbicpO1xyXG5cclxuICByZXR1cm4gdGhpcy50b2tlbihzcmMsIHRydWUpO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIExleGluZ1xyXG4gKi9cclxuXHJcbkxleGVyLnByb3RvdHlwZS50b2tlbiA9IGZ1bmN0aW9uKHNyYywgdG9wLCBicSkge1xyXG4gIHZhciBzcmMgPSBzcmMucmVwbGFjZSgvXiArJC9nbSwgJycpXHJcbiAgICAsIG5leHRcclxuICAgICwgbG9vc2VcclxuICAgICwgY2FwXHJcbiAgICAsIGJ1bGxcclxuICAgICwgYlxyXG4gICAgLCBpdGVtXHJcbiAgICAsIHNwYWNlXHJcbiAgICAsIGlcclxuICAgICwgbDtcclxuXHJcbiAgd2hpbGUgKHNyYykge1xyXG4gICAgLy8gbmV3bGluZVxyXG4gICAgaWYgKGNhcCA9IHRoaXMucnVsZXMubmV3bGluZS5leGVjKHNyYykpIHtcclxuICAgICAgc3JjID0gc3JjLnN1YnN0cmluZyhjYXBbMF0ubGVuZ3RoKTtcclxuICAgICAgaWYgKGNhcFswXS5sZW5ndGggPiAxKSB7XHJcbiAgICAgICAgdGhpcy50b2tlbnMucHVzaCh7XHJcbiAgICAgICAgICB0eXBlOiAnc3BhY2UnXHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBjb2RlXHJcbiAgICBpZiAoY2FwID0gdGhpcy5ydWxlcy5jb2RlLmV4ZWMoc3JjKSkge1xyXG4gICAgICBzcmMgPSBzcmMuc3Vic3RyaW5nKGNhcFswXS5sZW5ndGgpO1xyXG4gICAgICBjYXAgPSBjYXBbMF0ucmVwbGFjZSgvXiB7NH0vZ20sICcnKTtcclxuICAgICAgdGhpcy50b2tlbnMucHVzaCh7XHJcbiAgICAgICAgdHlwZTogJ2NvZGUnLFxyXG4gICAgICAgIHRleHQ6ICF0aGlzLm9wdGlvbnMucGVkYW50aWNcclxuICAgICAgICAgID8gY2FwLnJlcGxhY2UoL1xcbiskLywgJycpXHJcbiAgICAgICAgICA6IGNhcFxyXG4gICAgICB9KTtcclxuICAgICAgY29udGludWU7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gZmVuY2VzIChnZm0pXHJcbiAgICBpZiAoY2FwID0gdGhpcy5ydWxlcy5mZW5jZXMuZXhlYyhzcmMpKSB7XHJcbiAgICAgIHNyYyA9IHNyYy5zdWJzdHJpbmcoY2FwWzBdLmxlbmd0aCk7XHJcbiAgICAgIHRoaXMudG9rZW5zLnB1c2goe1xyXG4gICAgICAgIHR5cGU6ICdjb2RlJyxcclxuICAgICAgICBsYW5nOiBjYXBbMl0sXHJcbiAgICAgICAgdGV4dDogY2FwWzNdIHx8ICcnXHJcbiAgICAgIH0pO1xyXG4gICAgICBjb250aW51ZTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBoZWFkaW5nXHJcbiAgICBpZiAoY2FwID0gdGhpcy5ydWxlcy5oZWFkaW5nLmV4ZWMoc3JjKSkge1xyXG4gICAgICBzcmMgPSBzcmMuc3Vic3RyaW5nKGNhcFswXS5sZW5ndGgpO1xyXG4gICAgICB0aGlzLnRva2Vucy5wdXNoKHtcclxuICAgICAgICB0eXBlOiAnaGVhZGluZycsXHJcbiAgICAgICAgZGVwdGg6IGNhcFsxXS5sZW5ndGgsXHJcbiAgICAgICAgdGV4dDogY2FwWzJdXHJcbiAgICAgIH0pO1xyXG4gICAgICBjb250aW51ZTtcclxuICAgIH1cclxuXHJcbiAgICAvLyB0YWJsZSBubyBsZWFkaW5nIHBpcGUgKGdmbSlcclxuICAgIGlmICh0b3AgJiYgKGNhcCA9IHRoaXMucnVsZXMubnB0YWJsZS5leGVjKHNyYykpKSB7XHJcbiAgICAgIHNyYyA9IHNyYy5zdWJzdHJpbmcoY2FwWzBdLmxlbmd0aCk7XHJcblxyXG4gICAgICBpdGVtID0ge1xyXG4gICAgICAgIHR5cGU6ICd0YWJsZScsXHJcbiAgICAgICAgaGVhZGVyOiBjYXBbMV0ucmVwbGFjZSgvXiAqfCAqXFx8ICokL2csICcnKS5zcGxpdCgvICpcXHwgKi8pLFxyXG4gICAgICAgIGFsaWduOiBjYXBbMl0ucmVwbGFjZSgvXiAqfFxcfCAqJC9nLCAnJykuc3BsaXQoLyAqXFx8ICovKSxcclxuICAgICAgICBjZWxsczogY2FwWzNdLnJlcGxhY2UoL1xcbiQvLCAnJykuc3BsaXQoJ1xcbicpXHJcbiAgICAgIH07XHJcblxyXG4gICAgICBmb3IgKGkgPSAwOyBpIDwgaXRlbS5hbGlnbi5sZW5ndGg7IGkrKykge1xyXG4gICAgICAgIGlmICgvXiAqLSs6ICokLy50ZXN0KGl0ZW0uYWxpZ25baV0pKSB7XHJcbiAgICAgICAgICBpdGVtLmFsaWduW2ldID0gJ3JpZ2h0JztcclxuICAgICAgICB9IGVsc2UgaWYgKC9eICo6LSs6ICokLy50ZXN0KGl0ZW0uYWxpZ25baV0pKSB7XHJcbiAgICAgICAgICBpdGVtLmFsaWduW2ldID0gJ2NlbnRlcic7XHJcbiAgICAgICAgfSBlbHNlIGlmICgvXiAqOi0rICokLy50ZXN0KGl0ZW0uYWxpZ25baV0pKSB7XHJcbiAgICAgICAgICBpdGVtLmFsaWduW2ldID0gJ2xlZnQnO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICBpdGVtLmFsaWduW2ldID0gbnVsbDtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGZvciAoaSA9IDA7IGkgPCBpdGVtLmNlbGxzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgaXRlbS5jZWxsc1tpXSA9IGl0ZW0uY2VsbHNbaV0uc3BsaXQoLyAqXFx8ICovKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgdGhpcy50b2tlbnMucHVzaChpdGVtKTtcclxuXHJcbiAgICAgIGNvbnRpbnVlO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIGxoZWFkaW5nXHJcbiAgICBpZiAoY2FwID0gdGhpcy5ydWxlcy5saGVhZGluZy5leGVjKHNyYykpIHtcclxuICAgICAgc3JjID0gc3JjLnN1YnN0cmluZyhjYXBbMF0ubGVuZ3RoKTtcclxuICAgICAgdGhpcy50b2tlbnMucHVzaCh7XHJcbiAgICAgICAgdHlwZTogJ2hlYWRpbmcnLFxyXG4gICAgICAgIGRlcHRoOiBjYXBbMl0gPT09ICc9JyA/IDEgOiAyLFxyXG4gICAgICAgIHRleHQ6IGNhcFsxXVxyXG4gICAgICB9KTtcclxuICAgICAgY29udGludWU7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gaHJcclxuICAgIGlmIChjYXAgPSB0aGlzLnJ1bGVzLmhyLmV4ZWMoc3JjKSkge1xyXG4gICAgICBzcmMgPSBzcmMuc3Vic3RyaW5nKGNhcFswXS5sZW5ndGgpO1xyXG4gICAgICB0aGlzLnRva2Vucy5wdXNoKHtcclxuICAgICAgICB0eXBlOiAnaHInXHJcbiAgICAgIH0pO1xyXG4gICAgICBjb250aW51ZTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBibG9ja3F1b3RlXHJcbiAgICBpZiAoY2FwID0gdGhpcy5ydWxlcy5ibG9ja3F1b3RlLmV4ZWMoc3JjKSkge1xyXG4gICAgICBzcmMgPSBzcmMuc3Vic3RyaW5nKGNhcFswXS5sZW5ndGgpO1xyXG5cclxuICAgICAgdGhpcy50b2tlbnMucHVzaCh7XHJcbiAgICAgICAgdHlwZTogJ2Jsb2NrcXVvdGVfc3RhcnQnXHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgY2FwID0gY2FwWzBdLnJlcGxhY2UoL14gKj4gPy9nbSwgJycpO1xyXG5cclxuICAgICAgLy8gUGFzcyBgdG9wYCB0byBrZWVwIHRoZSBjdXJyZW50XHJcbiAgICAgIC8vIFwidG9wbGV2ZWxcIiBzdGF0ZS4gVGhpcyBpcyBleGFjdGx5XHJcbiAgICAgIC8vIGhvdyBtYXJrZG93bi5wbCB3b3Jrcy5cclxuICAgICAgdGhpcy50b2tlbihjYXAsIHRvcCwgdHJ1ZSk7XHJcblxyXG4gICAgICB0aGlzLnRva2Vucy5wdXNoKHtcclxuICAgICAgICB0eXBlOiAnYmxvY2txdW90ZV9lbmQnXHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgY29udGludWU7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gbGlzdFxyXG4gICAgaWYgKGNhcCA9IHRoaXMucnVsZXMubGlzdC5leGVjKHNyYykpIHtcclxuICAgICAgc3JjID0gc3JjLnN1YnN0cmluZyhjYXBbMF0ubGVuZ3RoKTtcclxuICAgICAgYnVsbCA9IGNhcFsyXTtcclxuXHJcbiAgICAgIHRoaXMudG9rZW5zLnB1c2goe1xyXG4gICAgICAgIHR5cGU6ICdsaXN0X3N0YXJ0JyxcclxuICAgICAgICBvcmRlcmVkOiBidWxsLmxlbmd0aCA+IDFcclxuICAgICAgfSk7XHJcblxyXG4gICAgICAvLyBHZXQgZWFjaCB0b3AtbGV2ZWwgaXRlbS5cclxuICAgICAgY2FwID0gY2FwWzBdLm1hdGNoKHRoaXMucnVsZXMuaXRlbSk7XHJcblxyXG4gICAgICBuZXh0ID0gZmFsc2U7XHJcbiAgICAgIGwgPSBjYXAubGVuZ3RoO1xyXG4gICAgICBpID0gMDtcclxuXHJcbiAgICAgIGZvciAoOyBpIDwgbDsgaSsrKSB7XHJcbiAgICAgICAgaXRlbSA9IGNhcFtpXTtcclxuXHJcbiAgICAgICAgLy8gUmVtb3ZlIHRoZSBsaXN0IGl0ZW0ncyBidWxsZXRcclxuICAgICAgICAvLyBzbyBpdCBpcyBzZWVuIGFzIHRoZSBuZXh0IHRva2VuLlxyXG4gICAgICAgIHNwYWNlID0gaXRlbS5sZW5ndGg7XHJcbiAgICAgICAgaXRlbSA9IGl0ZW0ucmVwbGFjZSgvXiAqKFsqKy1dfFxcZCtcXC4pICsvLCAnJyk7XHJcblxyXG4gICAgICAgIC8vIE91dGRlbnQgd2hhdGV2ZXIgdGhlXHJcbiAgICAgICAgLy8gbGlzdCBpdGVtIGNvbnRhaW5zLiBIYWNreS5cclxuICAgICAgICBpZiAofml0ZW0uaW5kZXhPZignXFxuICcpKSB7XHJcbiAgICAgICAgICBzcGFjZSAtPSBpdGVtLmxlbmd0aDtcclxuICAgICAgICAgIGl0ZW0gPSAhdGhpcy5vcHRpb25zLnBlZGFudGljXHJcbiAgICAgICAgICAgID8gaXRlbS5yZXBsYWNlKG5ldyBSZWdFeHAoJ14gezEsJyArIHNwYWNlICsgJ30nLCAnZ20nKSwgJycpXHJcbiAgICAgICAgICAgIDogaXRlbS5yZXBsYWNlKC9eIHsxLDR9L2dtLCAnJyk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBEZXRlcm1pbmUgd2hldGhlciB0aGUgbmV4dCBsaXN0IGl0ZW0gYmVsb25ncyBoZXJlLlxyXG4gICAgICAgIC8vIEJhY2twZWRhbCBpZiBpdCBkb2VzIG5vdCBiZWxvbmcgaW4gdGhpcyBsaXN0LlxyXG4gICAgICAgIGlmICh0aGlzLm9wdGlvbnMuc21hcnRMaXN0cyAmJiBpICE9PSBsIC0gMSkge1xyXG4gICAgICAgICAgYiA9IGJsb2NrLmJ1bGxldC5leGVjKGNhcFtpICsgMV0pWzBdO1xyXG4gICAgICAgICAgaWYgKGJ1bGwgIT09IGIgJiYgIShidWxsLmxlbmd0aCA+IDEgJiYgYi5sZW5ndGggPiAxKSkge1xyXG4gICAgICAgICAgICBzcmMgPSBjYXAuc2xpY2UoaSArIDEpLmpvaW4oJ1xcbicpICsgc3JjO1xyXG4gICAgICAgICAgICBpID0gbCAtIDE7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBEZXRlcm1pbmUgd2hldGhlciBpdGVtIGlzIGxvb3NlIG9yIG5vdC5cclxuICAgICAgICAvLyBVc2U6IC8oXnxcXG4pKD8hIClbXlxcbl0rXFxuXFxuKD8hXFxzKiQpL1xyXG4gICAgICAgIC8vIGZvciBkaXNjb3VudCBiZWhhdmlvci5cclxuICAgICAgICBsb29zZSA9IG5leHQgfHwgL1xcblxcbig/IVxccyokKS8udGVzdChpdGVtKTtcclxuICAgICAgICBpZiAoaSAhPT0gbCAtIDEpIHtcclxuICAgICAgICAgIG5leHQgPSBpdGVtLmNoYXJBdChpdGVtLmxlbmd0aCAtIDEpID09PSAnXFxuJztcclxuICAgICAgICAgIGlmICghbG9vc2UpIGxvb3NlID0gbmV4dDtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHRoaXMudG9rZW5zLnB1c2goe1xyXG4gICAgICAgICAgdHlwZTogbG9vc2VcclxuICAgICAgICAgICAgPyAnbG9vc2VfaXRlbV9zdGFydCdcclxuICAgICAgICAgICAgOiAnbGlzdF9pdGVtX3N0YXJ0J1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgICAgICAvLyBSZWN1cnNlLlxyXG4gICAgICAgIHRoaXMudG9rZW4oaXRlbSwgZmFsc2UsIGJxKTtcclxuXHJcbiAgICAgICAgdGhpcy50b2tlbnMucHVzaCh7XHJcbiAgICAgICAgICB0eXBlOiAnbGlzdF9pdGVtX2VuZCdcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG5cclxuICAgICAgdGhpcy50b2tlbnMucHVzaCh7XHJcbiAgICAgICAgdHlwZTogJ2xpc3RfZW5kJ1xyXG4gICAgICB9KTtcclxuXHJcbiAgICAgIGNvbnRpbnVlO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIGh0bWxcclxuICAgIGlmIChjYXAgPSB0aGlzLnJ1bGVzLmh0bWwuZXhlYyhzcmMpKSB7XHJcbiAgICAgIHNyYyA9IHNyYy5zdWJzdHJpbmcoY2FwWzBdLmxlbmd0aCk7XHJcbiAgICAgIHRoaXMudG9rZW5zLnB1c2goe1xyXG4gICAgICAgIHR5cGU6IHRoaXMub3B0aW9ucy5zYW5pdGl6ZVxyXG4gICAgICAgICAgPyAncGFyYWdyYXBoJ1xyXG4gICAgICAgICAgOiAnaHRtbCcsXHJcbiAgICAgICAgcHJlOiAhdGhpcy5vcHRpb25zLnNhbml0aXplclxyXG4gICAgICAgICAgJiYgKGNhcFsxXSA9PT0gJ3ByZScgfHwgY2FwWzFdID09PSAnc2NyaXB0JyB8fCBjYXBbMV0gPT09ICdzdHlsZScpLFxyXG4gICAgICAgIHRleHQ6IGNhcFswXVxyXG4gICAgICB9KTtcclxuICAgICAgY29udGludWU7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gZGVmXHJcbiAgICBpZiAoKCFicSAmJiB0b3ApICYmIChjYXAgPSB0aGlzLnJ1bGVzLmRlZi5leGVjKHNyYykpKSB7XHJcbiAgICAgIHNyYyA9IHNyYy5zdWJzdHJpbmcoY2FwWzBdLmxlbmd0aCk7XHJcbiAgICAgIHRoaXMudG9rZW5zLmxpbmtzW2NhcFsxXS50b0xvd2VyQ2FzZSgpXSA9IHtcclxuICAgICAgICBocmVmOiBjYXBbMl0sXHJcbiAgICAgICAgdGl0bGU6IGNhcFszXVxyXG4gICAgICB9O1xyXG4gICAgICBjb250aW51ZTtcclxuICAgIH1cclxuXHJcbiAgICAvLyB0YWJsZSAoZ2ZtKVxyXG4gICAgaWYgKHRvcCAmJiAoY2FwID0gdGhpcy5ydWxlcy50YWJsZS5leGVjKHNyYykpKSB7XHJcbiAgICAgIHNyYyA9IHNyYy5zdWJzdHJpbmcoY2FwWzBdLmxlbmd0aCk7XHJcblxyXG4gICAgICBpdGVtID0ge1xyXG4gICAgICAgIHR5cGU6ICd0YWJsZScsXHJcbiAgICAgICAgaGVhZGVyOiBjYXBbMV0ucmVwbGFjZSgvXiAqfCAqXFx8ICokL2csICcnKS5zcGxpdCgvICpcXHwgKi8pLFxyXG4gICAgICAgIGFsaWduOiBjYXBbMl0ucmVwbGFjZSgvXiAqfFxcfCAqJC9nLCAnJykuc3BsaXQoLyAqXFx8ICovKSxcclxuICAgICAgICBjZWxsczogY2FwWzNdLnJlcGxhY2UoLyg/OiAqXFx8ICopP1xcbiQvLCAnJykuc3BsaXQoJ1xcbicpXHJcbiAgICAgIH07XHJcblxyXG4gICAgICBmb3IgKGkgPSAwOyBpIDwgaXRlbS5hbGlnbi5sZW5ndGg7IGkrKykge1xyXG4gICAgICAgIGlmICgvXiAqLSs6ICokLy50ZXN0KGl0ZW0uYWxpZ25baV0pKSB7XHJcbiAgICAgICAgICBpdGVtLmFsaWduW2ldID0gJ3JpZ2h0JztcclxuICAgICAgICB9IGVsc2UgaWYgKC9eICo6LSs6ICokLy50ZXN0KGl0ZW0uYWxpZ25baV0pKSB7XHJcbiAgICAgICAgICBpdGVtLmFsaWduW2ldID0gJ2NlbnRlcic7XHJcbiAgICAgICAgfSBlbHNlIGlmICgvXiAqOi0rICokLy50ZXN0KGl0ZW0uYWxpZ25baV0pKSB7XHJcbiAgICAgICAgICBpdGVtLmFsaWduW2ldID0gJ2xlZnQnO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICBpdGVtLmFsaWduW2ldID0gbnVsbDtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGZvciAoaSA9IDA7IGkgPCBpdGVtLmNlbGxzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgaXRlbS5jZWxsc1tpXSA9IGl0ZW0uY2VsbHNbaV1cclxuICAgICAgICAgIC5yZXBsYWNlKC9eICpcXHwgKnwgKlxcfCAqJC9nLCAnJylcclxuICAgICAgICAgIC5zcGxpdCgvICpcXHwgKi8pO1xyXG4gICAgICB9XHJcblxyXG4gICAgICB0aGlzLnRva2Vucy5wdXNoKGl0ZW0pO1xyXG5cclxuICAgICAgY29udGludWU7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gdG9wLWxldmVsIHBhcmFncmFwaFxyXG4gICAgaWYgKHRvcCAmJiAoY2FwID0gdGhpcy5ydWxlcy5wYXJhZ3JhcGguZXhlYyhzcmMpKSkge1xyXG4gICAgICBzcmMgPSBzcmMuc3Vic3RyaW5nKGNhcFswXS5sZW5ndGgpO1xyXG4gICAgICB0aGlzLnRva2Vucy5wdXNoKHtcclxuICAgICAgICB0eXBlOiAncGFyYWdyYXBoJyxcclxuICAgICAgICB0ZXh0OiBjYXBbMV0uY2hhckF0KGNhcFsxXS5sZW5ndGggLSAxKSA9PT0gJ1xcbidcclxuICAgICAgICAgID8gY2FwWzFdLnNsaWNlKDAsIC0xKVxyXG4gICAgICAgICAgOiBjYXBbMV1cclxuICAgICAgfSk7XHJcbiAgICAgIGNvbnRpbnVlO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIHRleHRcclxuICAgIGlmIChjYXAgPSB0aGlzLnJ1bGVzLnRleHQuZXhlYyhzcmMpKSB7XHJcbiAgICAgIC8vIFRvcC1sZXZlbCBzaG91bGQgbmV2ZXIgcmVhY2ggaGVyZS5cclxuICAgICAgc3JjID0gc3JjLnN1YnN0cmluZyhjYXBbMF0ubGVuZ3RoKTtcclxuICAgICAgdGhpcy50b2tlbnMucHVzaCh7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICAgIHRleHQ6IGNhcFswXVxyXG4gICAgICB9KTtcclxuICAgICAgY29udGludWU7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKHNyYykge1xyXG4gICAgICB0aHJvdyBuZXdcclxuICAgICAgICBFcnJvcignSW5maW5pdGUgbG9vcCBvbiBieXRlOiAnICsgc3JjLmNoYXJDb2RlQXQoMCkpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgcmV0dXJuIHRoaXMudG9rZW5zO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIElubGluZS1MZXZlbCBHcmFtbWFyXHJcbiAqL1xyXG5cclxudmFyIGlubGluZSA9IHtcclxuICBlc2NhcGU6IC9eXFxcXChbXFxcXGAqe31cXFtcXF0oKSMrXFwtLiFfPl0pLyxcclxuICBhdXRvbGluazogL148KFteID5dKyhAfDpcXC8pW14gPl0rKT4vLFxyXG4gIHVybDogbm9vcCxcclxuICB0YWc6IC9ePCEtLVtcXHNcXFNdKj8tLT58XjxcXC8/XFx3Kyg/OlwiW15cIl0qXCJ8J1teJ10qJ3xbXidcIj5dKSo/Pi8sXHJcbiAgbGluazogL14hP1xcWyhpbnNpZGUpXFxdXFwoaHJlZlxcKS8sXHJcbiAgcmVmbGluazogL14hP1xcWyhpbnNpZGUpXFxdXFxzKlxcWyhbXlxcXV0qKVxcXS8sXHJcbiAgbm9saW5rOiAvXiE/XFxbKCg/OlxcW1teXFxdXSpcXF18W15cXFtcXF1dKSopXFxdLyxcclxuICBzdHJvbmc6IC9eX18oW1xcc1xcU10rPylfXyg/IV8pfF5cXCpcXCooW1xcc1xcU10rPylcXCpcXCooPyFcXCopLyxcclxuICBlbTogL15cXGJfKCg/OlteX118X18pKz8pX1xcYnxeXFwqKCg/OlxcKlxcKnxbXFxzXFxTXSkrPylcXCooPyFcXCopLyxcclxuICBjb2RlOiAvXihgKylcXHMqKFtcXHNcXFNdKj9bXmBdKVxccypcXDEoPyFgKS8sXHJcbiAgYnI6IC9eIHsyLH1cXG4oPyFcXHMqJCkvLFxyXG4gIGRlbDogbm9vcCxcclxuICB0ZXh0OiAvXltcXHNcXFNdKz8oPz1bXFxcXDwhXFxbXypgXXwgezIsfVxcbnwkKS9cclxufTtcclxuXHJcbmlubGluZS5faW5zaWRlID0gLyg/OlxcW1teXFxdXSpcXF18W15cXFtcXF1dfFxcXSg/PVteXFxbXSpcXF0pKSovO1xyXG5pbmxpbmUuX2hyZWYgPSAvXFxzKjw/KFtcXHNcXFNdKj8pPj8oPzpcXHMrWydcIl0oW1xcc1xcU10qPylbJ1wiXSk/XFxzKi87XHJcblxyXG5pbmxpbmUubGluayA9IHJlcGxhY2UoaW5saW5lLmxpbmspXHJcbiAgKCdpbnNpZGUnLCBpbmxpbmUuX2luc2lkZSlcclxuICAoJ2hyZWYnLCBpbmxpbmUuX2hyZWYpXHJcbiAgKCk7XHJcblxyXG5pbmxpbmUucmVmbGluayA9IHJlcGxhY2UoaW5saW5lLnJlZmxpbmspXHJcbiAgKCdpbnNpZGUnLCBpbmxpbmUuX2luc2lkZSlcclxuICAoKTtcclxuXHJcbi8qKlxyXG4gKiBOb3JtYWwgSW5saW5lIEdyYW1tYXJcclxuICovXHJcblxyXG5pbmxpbmUubm9ybWFsID0gbWVyZ2Uoe30sIGlubGluZSk7XHJcblxyXG4vKipcclxuICogUGVkYW50aWMgSW5saW5lIEdyYW1tYXJcclxuICovXHJcblxyXG5pbmxpbmUucGVkYW50aWMgPSBtZXJnZSh7fSwgaW5saW5lLm5vcm1hbCwge1xyXG4gIHN0cm9uZzogL15fXyg/PVxcUykoW1xcc1xcU10qP1xcUylfXyg/IV8pfF5cXCpcXCooPz1cXFMpKFtcXHNcXFNdKj9cXFMpXFwqXFwqKD8hXFwqKS8sXHJcbiAgZW06IC9eXyg/PVxcUykoW1xcc1xcU10qP1xcUylfKD8hXyl8XlxcKig/PVxcUykoW1xcc1xcU10qP1xcUylcXCooPyFcXCopL1xyXG59KTtcclxuXHJcbi8qKlxyXG4gKiBHRk0gSW5saW5lIEdyYW1tYXJcclxuICovXHJcblxyXG5pbmxpbmUuZ2ZtID0gbWVyZ2Uoe30sIGlubGluZS5ub3JtYWwsIHtcclxuICBlc2NhcGU6IHJlcGxhY2UoaW5saW5lLmVzY2FwZSkoJ10pJywgJ358XSknKSgpLFxyXG4gIHVybDogL14oaHR0cHM/OlxcL1xcL1teXFxzPF0rW148Liw6O1wiJylcXF1cXHNdKS8sXHJcbiAgZGVsOiAvXn5+KD89XFxTKShbXFxzXFxTXSo/XFxTKX5+LyxcclxuICB0ZXh0OiByZXBsYWNlKGlubGluZS50ZXh0KVxyXG4gICAgKCddfCcsICd+XXwnKVxyXG4gICAgKCd8JywgJ3xodHRwcz86Ly98JylcclxuICAgICgpXHJcbn0pO1xyXG5cclxuLyoqXHJcbiAqIEdGTSArIExpbmUgQnJlYWtzIElubGluZSBHcmFtbWFyXHJcbiAqL1xyXG5cclxuaW5saW5lLmJyZWFrcyA9IG1lcmdlKHt9LCBpbmxpbmUuZ2ZtLCB7XHJcbiAgYnI6IHJlcGxhY2UoaW5saW5lLmJyKSgnezIsfScsICcqJykoKSxcclxuICB0ZXh0OiByZXBsYWNlKGlubGluZS5nZm0udGV4dCkoJ3syLH0nLCAnKicpKClcclxufSk7XHJcblxyXG4vKipcclxuICogSW5saW5lIExleGVyICYgQ29tcGlsZXJcclxuICovXHJcblxyXG5mdW5jdGlvbiBJbmxpbmVMZXhlcihsaW5rcywgb3B0aW9ucykge1xyXG4gIHRoaXMub3B0aW9ucyA9IG9wdGlvbnMgfHwgbWFya2VkLmRlZmF1bHRzO1xyXG4gIHRoaXMubGlua3MgPSBsaW5rcztcclxuICB0aGlzLnJ1bGVzID0gaW5saW5lLm5vcm1hbDtcclxuICB0aGlzLnJlbmRlcmVyID0gdGhpcy5vcHRpb25zLnJlbmRlcmVyIHx8IG5ldyBSZW5kZXJlcjtcclxuICB0aGlzLnJlbmRlcmVyLm9wdGlvbnMgPSB0aGlzLm9wdGlvbnM7XHJcblxyXG4gIGlmICghdGhpcy5saW5rcykge1xyXG4gICAgdGhyb3cgbmV3XHJcbiAgICAgIEVycm9yKCdUb2tlbnMgYXJyYXkgcmVxdWlyZXMgYSBgbGlua3NgIHByb3BlcnR5LicpO1xyXG4gIH1cclxuXHJcbiAgaWYgKHRoaXMub3B0aW9ucy5nZm0pIHtcclxuICAgIGlmICh0aGlzLm9wdGlvbnMuYnJlYWtzKSB7XHJcbiAgICAgIHRoaXMucnVsZXMgPSBpbmxpbmUuYnJlYWtzO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgdGhpcy5ydWxlcyA9IGlubGluZS5nZm07XHJcbiAgICB9XHJcbiAgfSBlbHNlIGlmICh0aGlzLm9wdGlvbnMucGVkYW50aWMpIHtcclxuICAgIHRoaXMucnVsZXMgPSBpbmxpbmUucGVkYW50aWM7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogRXhwb3NlIElubGluZSBSdWxlc1xyXG4gKi9cclxuXHJcbklubGluZUxleGVyLnJ1bGVzID0gaW5saW5lO1xyXG5cclxuLyoqXHJcbiAqIFN0YXRpYyBMZXhpbmcvQ29tcGlsaW5nIE1ldGhvZFxyXG4gKi9cclxuXHJcbklubGluZUxleGVyLm91dHB1dCA9IGZ1bmN0aW9uKHNyYywgbGlua3MsIG9wdGlvbnMpIHtcclxuICB2YXIgaW5saW5lID0gbmV3IElubGluZUxleGVyKGxpbmtzLCBvcHRpb25zKTtcclxuICByZXR1cm4gaW5saW5lLm91dHB1dChzcmMpO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIExleGluZy9Db21waWxpbmdcclxuICovXHJcblxyXG5JbmxpbmVMZXhlci5wcm90b3R5cGUub3V0cHV0ID0gZnVuY3Rpb24oc3JjKSB7XHJcbiAgdmFyIG91dCA9ICcnXHJcbiAgICAsIGxpbmtcclxuICAgICwgdGV4dFxyXG4gICAgLCBocmVmXHJcbiAgICAsIGNhcDtcclxuXHJcbiAgd2hpbGUgKHNyYykge1xyXG4gICAgLy8gZXNjYXBlXHJcbiAgICBpZiAoY2FwID0gdGhpcy5ydWxlcy5lc2NhcGUuZXhlYyhzcmMpKSB7XHJcbiAgICAgIHNyYyA9IHNyYy5zdWJzdHJpbmcoY2FwWzBdLmxlbmd0aCk7XHJcbiAgICAgIG91dCArPSBjYXBbMV07XHJcbiAgICAgIGNvbnRpbnVlO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIGF1dG9saW5rXHJcbiAgICBpZiAoY2FwID0gdGhpcy5ydWxlcy5hdXRvbGluay5leGVjKHNyYykpIHtcclxuICAgICAgc3JjID0gc3JjLnN1YnN0cmluZyhjYXBbMF0ubGVuZ3RoKTtcclxuICAgICAgaWYgKGNhcFsyXSA9PT0gJ0AnKSB7XHJcbiAgICAgICAgdGV4dCA9IGNhcFsxXS5jaGFyQXQoNikgPT09ICc6J1xyXG4gICAgICAgICAgPyB0aGlzLm1hbmdsZShjYXBbMV0uc3Vic3RyaW5nKDcpKVxyXG4gICAgICAgICAgOiB0aGlzLm1hbmdsZShjYXBbMV0pO1xyXG4gICAgICAgIGhyZWYgPSB0aGlzLm1hbmdsZSgnbWFpbHRvOicpICsgdGV4dDtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICB0ZXh0ID0gZXNjYXBlKGNhcFsxXSk7XHJcbiAgICAgICAgaHJlZiA9IHRleHQ7XHJcbiAgICAgIH1cclxuICAgICAgb3V0ICs9IHRoaXMucmVuZGVyZXIubGluayhocmVmLCBudWxsLCB0ZXh0KTtcclxuICAgICAgY29udGludWU7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gdXJsIChnZm0pXHJcbiAgICBpZiAoIXRoaXMuaW5MaW5rICYmIChjYXAgPSB0aGlzLnJ1bGVzLnVybC5leGVjKHNyYykpKSB7XHJcbiAgICAgIHNyYyA9IHNyYy5zdWJzdHJpbmcoY2FwWzBdLmxlbmd0aCk7XHJcbiAgICAgIHRleHQgPSBlc2NhcGUoY2FwWzFdKTtcclxuICAgICAgaHJlZiA9IHRleHQ7XHJcbiAgICAgIG91dCArPSB0aGlzLnJlbmRlcmVyLmxpbmsoaHJlZiwgbnVsbCwgdGV4dCk7XHJcbiAgICAgIGNvbnRpbnVlO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIHRhZ1xyXG4gICAgaWYgKGNhcCA9IHRoaXMucnVsZXMudGFnLmV4ZWMoc3JjKSkge1xyXG4gICAgICBpZiAoIXRoaXMuaW5MaW5rICYmIC9ePGEgL2kudGVzdChjYXBbMF0pKSB7XHJcbiAgICAgICAgdGhpcy5pbkxpbmsgPSB0cnVlO1xyXG4gICAgICB9IGVsc2UgaWYgKHRoaXMuaW5MaW5rICYmIC9ePFxcL2E+L2kudGVzdChjYXBbMF0pKSB7XHJcbiAgICAgICAgdGhpcy5pbkxpbmsgPSBmYWxzZTtcclxuICAgICAgfVxyXG4gICAgICBzcmMgPSBzcmMuc3Vic3RyaW5nKGNhcFswXS5sZW5ndGgpO1xyXG4gICAgICBvdXQgKz0gdGhpcy5vcHRpb25zLnNhbml0aXplXHJcbiAgICAgICAgPyB0aGlzLm9wdGlvbnMuc2FuaXRpemVyXHJcbiAgICAgICAgICA/IHRoaXMub3B0aW9ucy5zYW5pdGl6ZXIoY2FwWzBdKVxyXG4gICAgICAgICAgOiBlc2NhcGUoY2FwWzBdKVxyXG4gICAgICAgIDogY2FwWzBdXHJcbiAgICAgIGNvbnRpbnVlO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIGxpbmtcclxuICAgIGlmIChjYXAgPSB0aGlzLnJ1bGVzLmxpbmsuZXhlYyhzcmMpKSB7XHJcbiAgICAgIHNyYyA9IHNyYy5zdWJzdHJpbmcoY2FwWzBdLmxlbmd0aCk7XHJcbiAgICAgIHRoaXMuaW5MaW5rID0gdHJ1ZTtcclxuICAgICAgb3V0ICs9IHRoaXMub3V0cHV0TGluayhjYXAsIHtcclxuICAgICAgICBocmVmOiBjYXBbMl0sXHJcbiAgICAgICAgdGl0bGU6IGNhcFszXVxyXG4gICAgICB9KTtcclxuICAgICAgdGhpcy5pbkxpbmsgPSBmYWxzZTtcclxuICAgICAgY29udGludWU7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gcmVmbGluaywgbm9saW5rXHJcbiAgICBpZiAoKGNhcCA9IHRoaXMucnVsZXMucmVmbGluay5leGVjKHNyYykpXHJcbiAgICAgICAgfHwgKGNhcCA9IHRoaXMucnVsZXMubm9saW5rLmV4ZWMoc3JjKSkpIHtcclxuICAgICAgc3JjID0gc3JjLnN1YnN0cmluZyhjYXBbMF0ubGVuZ3RoKTtcclxuICAgICAgbGluayA9IChjYXBbMl0gfHwgY2FwWzFdKS5yZXBsYWNlKC9cXHMrL2csICcgJyk7XHJcbiAgICAgIGxpbmsgPSB0aGlzLmxpbmtzW2xpbmsudG9Mb3dlckNhc2UoKV07XHJcbiAgICAgIGlmICghbGluayB8fCAhbGluay5ocmVmKSB7XHJcbiAgICAgICAgb3V0ICs9IGNhcFswXS5jaGFyQXQoMCk7XHJcbiAgICAgICAgc3JjID0gY2FwWzBdLnN1YnN0cmluZygxKSArIHNyYztcclxuICAgICAgICBjb250aW51ZTtcclxuICAgICAgfVxyXG4gICAgICB0aGlzLmluTGluayA9IHRydWU7XHJcbiAgICAgIG91dCArPSB0aGlzLm91dHB1dExpbmsoY2FwLCBsaW5rKTtcclxuICAgICAgdGhpcy5pbkxpbmsgPSBmYWxzZTtcclxuICAgICAgY29udGludWU7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gc3Ryb25nXHJcbiAgICBpZiAoY2FwID0gdGhpcy5ydWxlcy5zdHJvbmcuZXhlYyhzcmMpKSB7XHJcbiAgICAgIHNyYyA9IHNyYy5zdWJzdHJpbmcoY2FwWzBdLmxlbmd0aCk7XHJcbiAgICAgIG91dCArPSB0aGlzLnJlbmRlcmVyLnN0cm9uZyh0aGlzLm91dHB1dChjYXBbMl0gfHwgY2FwWzFdKSk7XHJcbiAgICAgIGNvbnRpbnVlO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIGVtXHJcbiAgICBpZiAoY2FwID0gdGhpcy5ydWxlcy5lbS5leGVjKHNyYykpIHtcclxuICAgICAgc3JjID0gc3JjLnN1YnN0cmluZyhjYXBbMF0ubGVuZ3RoKTtcclxuICAgICAgb3V0ICs9IHRoaXMucmVuZGVyZXIuZW0odGhpcy5vdXRwdXQoY2FwWzJdIHx8IGNhcFsxXSkpO1xyXG4gICAgICBjb250aW51ZTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBjb2RlXHJcbiAgICBpZiAoY2FwID0gdGhpcy5ydWxlcy5jb2RlLmV4ZWMoc3JjKSkge1xyXG4gICAgICBzcmMgPSBzcmMuc3Vic3RyaW5nKGNhcFswXS5sZW5ndGgpO1xyXG4gICAgICBvdXQgKz0gdGhpcy5yZW5kZXJlci5jb2Rlc3Bhbihlc2NhcGUoY2FwWzJdLCB0cnVlKSk7XHJcbiAgICAgIGNvbnRpbnVlO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIGJyXHJcbiAgICBpZiAoY2FwID0gdGhpcy5ydWxlcy5ici5leGVjKHNyYykpIHtcclxuICAgICAgc3JjID0gc3JjLnN1YnN0cmluZyhjYXBbMF0ubGVuZ3RoKTtcclxuICAgICAgb3V0ICs9IHRoaXMucmVuZGVyZXIuYnIoKTtcclxuICAgICAgY29udGludWU7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gZGVsIChnZm0pXHJcbiAgICBpZiAoY2FwID0gdGhpcy5ydWxlcy5kZWwuZXhlYyhzcmMpKSB7XHJcbiAgICAgIHNyYyA9IHNyYy5zdWJzdHJpbmcoY2FwWzBdLmxlbmd0aCk7XHJcbiAgICAgIG91dCArPSB0aGlzLnJlbmRlcmVyLmRlbCh0aGlzLm91dHB1dChjYXBbMV0pKTtcclxuICAgICAgY29udGludWU7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gdGV4dFxyXG4gICAgaWYgKGNhcCA9IHRoaXMucnVsZXMudGV4dC5leGVjKHNyYykpIHtcclxuICAgICAgc3JjID0gc3JjLnN1YnN0cmluZyhjYXBbMF0ubGVuZ3RoKTtcclxuICAgICAgb3V0ICs9IHRoaXMucmVuZGVyZXIudGV4dChlc2NhcGUodGhpcy5zbWFydHlwYW50cyhjYXBbMF0pKSk7XHJcbiAgICAgIGNvbnRpbnVlO1xyXG4gICAgfVxyXG5cclxuICAgIGlmIChzcmMpIHtcclxuICAgICAgdGhyb3cgbmV3XHJcbiAgICAgICAgRXJyb3IoJ0luZmluaXRlIGxvb3Agb24gYnl0ZTogJyArIHNyYy5jaGFyQ29kZUF0KDApKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIHJldHVybiBvdXQ7XHJcbn07XHJcblxyXG4vKipcclxuICogQ29tcGlsZSBMaW5rXHJcbiAqL1xyXG5cclxuSW5saW5lTGV4ZXIucHJvdG90eXBlLm91dHB1dExpbmsgPSBmdW5jdGlvbihjYXAsIGxpbmspIHtcclxuICB2YXIgaHJlZiA9IGVzY2FwZShsaW5rLmhyZWYpXHJcbiAgICAsIHRpdGxlID0gbGluay50aXRsZSA/IGVzY2FwZShsaW5rLnRpdGxlKSA6IG51bGw7XHJcblxyXG4gIHJldHVybiBjYXBbMF0uY2hhckF0KDApICE9PSAnISdcclxuICAgID8gdGhpcy5yZW5kZXJlci5saW5rKGhyZWYsIHRpdGxlLCB0aGlzLm91dHB1dChjYXBbMV0pKVxyXG4gICAgOiB0aGlzLnJlbmRlcmVyLmltYWdlKGhyZWYsIHRpdGxlLCBlc2NhcGUoY2FwWzFdKSk7XHJcbn07XHJcblxyXG4vKipcclxuICogU21hcnR5cGFudHMgVHJhbnNmb3JtYXRpb25zXHJcbiAqL1xyXG5cclxuSW5saW5lTGV4ZXIucHJvdG90eXBlLnNtYXJ0eXBhbnRzID0gZnVuY3Rpb24odGV4dCkge1xyXG4gIGlmICghdGhpcy5vcHRpb25zLnNtYXJ0eXBhbnRzKSByZXR1cm4gdGV4dDtcclxuICByZXR1cm4gdGV4dFxyXG4gICAgLy8gZW0tZGFzaGVzXHJcbiAgICAucmVwbGFjZSgvLS0tL2csICdcXHUyMDE0JylcclxuICAgIC8vIGVuLWRhc2hlc1xyXG4gICAgLnJlcGxhY2UoLy0tL2csICdcXHUyMDEzJylcclxuICAgIC8vIG9wZW5pbmcgc2luZ2xlc1xyXG4gICAgLnJlcGxhY2UoLyhefFstXFx1MjAxNC8oXFxbe1wiXFxzXSknL2csICckMVxcdTIwMTgnKVxyXG4gICAgLy8gY2xvc2luZyBzaW5nbGVzICYgYXBvc3Ryb3BoZXNcclxuICAgIC5yZXBsYWNlKC8nL2csICdcXHUyMDE5JylcclxuICAgIC8vIG9wZW5pbmcgZG91Ymxlc1xyXG4gICAgLnJlcGxhY2UoLyhefFstXFx1MjAxNC8oXFxbe1xcdTIwMThcXHNdKVwiL2csICckMVxcdTIwMWMnKVxyXG4gICAgLy8gY2xvc2luZyBkb3VibGVzXHJcbiAgICAucmVwbGFjZSgvXCIvZywgJ1xcdTIwMWQnKVxyXG4gICAgLy8gZWxsaXBzZXNcclxuICAgIC5yZXBsYWNlKC9cXC57M30vZywgJ1xcdTIwMjYnKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBNYW5nbGUgTGlua3NcclxuICovXHJcblxyXG5JbmxpbmVMZXhlci5wcm90b3R5cGUubWFuZ2xlID0gZnVuY3Rpb24odGV4dCkge1xyXG4gIGlmICghdGhpcy5vcHRpb25zLm1hbmdsZSkgcmV0dXJuIHRleHQ7XHJcbiAgdmFyIG91dCA9ICcnXHJcbiAgICAsIGwgPSB0ZXh0Lmxlbmd0aFxyXG4gICAgLCBpID0gMFxyXG4gICAgLCBjaDtcclxuXHJcbiAgZm9yICg7IGkgPCBsOyBpKyspIHtcclxuICAgIGNoID0gdGV4dC5jaGFyQ29kZUF0KGkpO1xyXG4gICAgaWYgKE1hdGgucmFuZG9tKCkgPiAwLjUpIHtcclxuICAgICAgY2ggPSAneCcgKyBjaC50b1N0cmluZygxNik7XHJcbiAgICB9XHJcbiAgICBvdXQgKz0gJyYjJyArIGNoICsgJzsnO1xyXG4gIH1cclxuXHJcbiAgcmV0dXJuIG91dDtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBSZW5kZXJlclxyXG4gKi9cclxuXHJcbmZ1bmN0aW9uIFJlbmRlcmVyKG9wdGlvbnMpIHtcclxuICB0aGlzLm9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xyXG59XHJcblxyXG5SZW5kZXJlci5wcm90b3R5cGUuY29kZSA9IGZ1bmN0aW9uKGNvZGUsIGxhbmcsIGVzY2FwZWQpIHtcclxuICBpZiAodGhpcy5vcHRpb25zLmhpZ2hsaWdodCkge1xyXG4gICAgdmFyIG91dCA9IHRoaXMub3B0aW9ucy5oaWdobGlnaHQoY29kZSwgbGFuZyk7XHJcbiAgICBpZiAob3V0ICE9IG51bGwgJiYgb3V0ICE9PSBjb2RlKSB7XHJcbiAgICAgIGVzY2FwZWQgPSB0cnVlO1xyXG4gICAgICBjb2RlID0gb3V0O1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgaWYgKCFsYW5nKSB7XHJcbiAgICByZXR1cm4gJzxwcmU+PGNvZGU+J1xyXG4gICAgICArIChlc2NhcGVkID8gY29kZSA6IGVzY2FwZShjb2RlLCB0cnVlKSlcclxuICAgICAgKyAnXFxuPC9jb2RlPjwvcHJlPic7XHJcbiAgfVxyXG5cclxuICByZXR1cm4gJzxwcmU+PGNvZGUgY2xhc3M9XCInXHJcbiAgICArIHRoaXMub3B0aW9ucy5sYW5nUHJlZml4XHJcbiAgICArIGVzY2FwZShsYW5nLCB0cnVlKVxyXG4gICAgKyAnXCI+J1xyXG4gICAgKyAoZXNjYXBlZCA/IGNvZGUgOiBlc2NhcGUoY29kZSwgdHJ1ZSkpXHJcbiAgICArICdcXG48L2NvZGU+PC9wcmU+XFxuJztcclxufTtcclxuXHJcblJlbmRlcmVyLnByb3RvdHlwZS5ibG9ja3F1b3RlID0gZnVuY3Rpb24ocXVvdGUpIHtcclxuICByZXR1cm4gJzxibG9ja3F1b3RlPlxcbicgKyBxdW90ZSArICc8L2Jsb2NrcXVvdGU+XFxuJztcclxufTtcclxuXHJcblJlbmRlcmVyLnByb3RvdHlwZS5odG1sID0gZnVuY3Rpb24oaHRtbCkge1xyXG4gIHJldHVybiBodG1sO1xyXG59O1xyXG5cclxuUmVuZGVyZXIucHJvdG90eXBlLmhlYWRpbmcgPSBmdW5jdGlvbih0ZXh0LCBsZXZlbCwgcmF3KSB7XHJcbiAgcmV0dXJuICc8aCdcclxuICAgICsgbGV2ZWxcclxuICAgICsgJyBpZD1cIidcclxuICAgICsgdGhpcy5vcHRpb25zLmhlYWRlclByZWZpeFxyXG4gICAgKyByYXcudG9Mb3dlckNhc2UoKS5yZXBsYWNlKC9bXlxcd10rL2csICctJylcclxuICAgICsgJ1wiPidcclxuICAgICsgdGV4dFxyXG4gICAgKyAnPC9oJ1xyXG4gICAgKyBsZXZlbFxyXG4gICAgKyAnPlxcbic7XHJcbn07XHJcblxyXG5SZW5kZXJlci5wcm90b3R5cGUuaHIgPSBmdW5jdGlvbigpIHtcclxuICByZXR1cm4gdGhpcy5vcHRpb25zLnhodG1sID8gJzxoci8+XFxuJyA6ICc8aHI+XFxuJztcclxufTtcclxuXHJcblJlbmRlcmVyLnByb3RvdHlwZS5saXN0ID0gZnVuY3Rpb24oYm9keSwgb3JkZXJlZCkge1xyXG4gIHZhciB0eXBlID0gb3JkZXJlZCA/ICdvbCcgOiAndWwnO1xyXG4gIHJldHVybiAnPCcgKyB0eXBlICsgJz5cXG4nICsgYm9keSArICc8LycgKyB0eXBlICsgJz5cXG4nO1xyXG59O1xyXG5cclxuUmVuZGVyZXIucHJvdG90eXBlLmxpc3RpdGVtID0gZnVuY3Rpb24odGV4dCkge1xyXG4gIHJldHVybiAnPGxpPicgKyB0ZXh0ICsgJzwvbGk+XFxuJztcclxufTtcclxuXHJcblJlbmRlcmVyLnByb3RvdHlwZS5wYXJhZ3JhcGggPSBmdW5jdGlvbih0ZXh0KSB7XHJcbiAgcmV0dXJuICc8cD4nICsgdGV4dCArICc8L3A+XFxuJztcclxufTtcclxuXHJcblJlbmRlcmVyLnByb3RvdHlwZS50YWJsZSA9IGZ1bmN0aW9uKGhlYWRlciwgYm9keSkge1xyXG4gIHJldHVybiAnPHRhYmxlPlxcbidcclxuICAgICsgJzx0aGVhZD5cXG4nXHJcbiAgICArIGhlYWRlclxyXG4gICAgKyAnPC90aGVhZD5cXG4nXHJcbiAgICArICc8dGJvZHk+XFxuJ1xyXG4gICAgKyBib2R5XHJcbiAgICArICc8L3Rib2R5PlxcbidcclxuICAgICsgJzwvdGFibGU+XFxuJztcclxufTtcclxuXHJcblJlbmRlcmVyLnByb3RvdHlwZS50YWJsZXJvdyA9IGZ1bmN0aW9uKGNvbnRlbnQpIHtcclxuICByZXR1cm4gJzx0cj5cXG4nICsgY29udGVudCArICc8L3RyPlxcbic7XHJcbn07XHJcblxyXG5SZW5kZXJlci5wcm90b3R5cGUudGFibGVjZWxsID0gZnVuY3Rpb24oY29udGVudCwgZmxhZ3MpIHtcclxuICB2YXIgdHlwZSA9IGZsYWdzLmhlYWRlciA/ICd0aCcgOiAndGQnO1xyXG4gIHZhciB0YWcgPSBmbGFncy5hbGlnblxyXG4gICAgPyAnPCcgKyB0eXBlICsgJyBzdHlsZT1cInRleHQtYWxpZ246JyArIGZsYWdzLmFsaWduICsgJ1wiPidcclxuICAgIDogJzwnICsgdHlwZSArICc+JztcclxuICByZXR1cm4gdGFnICsgY29udGVudCArICc8LycgKyB0eXBlICsgJz5cXG4nO1xyXG59O1xyXG5cclxuLy8gc3BhbiBsZXZlbCByZW5kZXJlclxyXG5SZW5kZXJlci5wcm90b3R5cGUuc3Ryb25nID0gZnVuY3Rpb24odGV4dCkge1xyXG4gIHJldHVybiAnPHN0cm9uZz4nICsgdGV4dCArICc8L3N0cm9uZz4nO1xyXG59O1xyXG5cclxuUmVuZGVyZXIucHJvdG90eXBlLmVtID0gZnVuY3Rpb24odGV4dCkge1xyXG4gIHJldHVybiAnPGVtPicgKyB0ZXh0ICsgJzwvZW0+JztcclxufTtcclxuXHJcblJlbmRlcmVyLnByb3RvdHlwZS5jb2Rlc3BhbiA9IGZ1bmN0aW9uKHRleHQpIHtcclxuICByZXR1cm4gJzxjb2RlPicgKyB0ZXh0ICsgJzwvY29kZT4nO1xyXG59O1xyXG5cclxuUmVuZGVyZXIucHJvdG90eXBlLmJyID0gZnVuY3Rpb24oKSB7XHJcbiAgcmV0dXJuIHRoaXMub3B0aW9ucy54aHRtbCA/ICc8YnIvPicgOiAnPGJyPic7XHJcbn07XHJcblxyXG5SZW5kZXJlci5wcm90b3R5cGUuZGVsID0gZnVuY3Rpb24odGV4dCkge1xyXG4gIHJldHVybiAnPGRlbD4nICsgdGV4dCArICc8L2RlbD4nO1xyXG59O1xyXG5cclxuUmVuZGVyZXIucHJvdG90eXBlLmxpbmsgPSBmdW5jdGlvbihocmVmLCB0aXRsZSwgdGV4dCkge1xyXG4gIGlmICh0aGlzLm9wdGlvbnMuc2FuaXRpemUpIHtcclxuICAgIHRyeSB7XHJcbiAgICAgIHZhciBwcm90ID0gZGVjb2RlVVJJQ29tcG9uZW50KHVuZXNjYXBlKGhyZWYpKVxyXG4gICAgICAgIC5yZXBsYWNlKC9bXlxcdzpdL2csICcnKVxyXG4gICAgICAgIC50b0xvd2VyQ2FzZSgpO1xyXG4gICAgfSBjYXRjaCAoZSkge1xyXG4gICAgICByZXR1cm4gJyc7XHJcbiAgICB9XHJcbiAgICBpZiAocHJvdC5pbmRleE9mKCdqYXZhc2NyaXB0OicpID09PSAwIHx8IHByb3QuaW5kZXhPZigndmJzY3JpcHQ6JykgPT09IDApIHtcclxuICAgICAgcmV0dXJuICcnO1xyXG4gICAgfVxyXG4gIH1cclxuICB2YXIgb3V0ID0gJzxhIGhyZWY9XCInICsgaHJlZiArICdcIic7XHJcbiAgaWYgKHRpdGxlKSB7XHJcbiAgICBvdXQgKz0gJyB0aXRsZT1cIicgKyB0aXRsZSArICdcIic7XHJcbiAgfVxyXG4gIG91dCArPSAnPicgKyB0ZXh0ICsgJzwvYT4nO1xyXG4gIHJldHVybiBvdXQ7XHJcbn07XHJcblxyXG5SZW5kZXJlci5wcm90b3R5cGUuaW1hZ2UgPSBmdW5jdGlvbihocmVmLCB0aXRsZSwgdGV4dCkge1xyXG4gIHZhciBvdXQgPSAnPGltZyBzcmM9XCInICsgaHJlZiArICdcIiBhbHQ9XCInICsgdGV4dCArICdcIic7XHJcbiAgaWYgKHRpdGxlKSB7XHJcbiAgICBvdXQgKz0gJyB0aXRsZT1cIicgKyB0aXRsZSArICdcIic7XHJcbiAgfVxyXG4gIG91dCArPSB0aGlzLm9wdGlvbnMueGh0bWwgPyAnLz4nIDogJz4nO1xyXG4gIHJldHVybiBvdXQ7XHJcbn07XHJcblxyXG5SZW5kZXJlci5wcm90b3R5cGUudGV4dCA9IGZ1bmN0aW9uKHRleHQpIHtcclxuICByZXR1cm4gdGV4dDtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBQYXJzaW5nICYgQ29tcGlsaW5nXHJcbiAqL1xyXG5cclxuZnVuY3Rpb24gUGFyc2VyKG9wdGlvbnMpIHtcclxuICB0aGlzLnRva2VucyA9IFtdO1xyXG4gIHRoaXMudG9rZW4gPSBudWxsO1xyXG4gIHRoaXMub3B0aW9ucyA9IG9wdGlvbnMgfHwgbWFya2VkLmRlZmF1bHRzO1xyXG4gIHRoaXMub3B0aW9ucy5yZW5kZXJlciA9IHRoaXMub3B0aW9ucy5yZW5kZXJlciB8fCBuZXcgUmVuZGVyZXI7XHJcbiAgdGhpcy5yZW5kZXJlciA9IHRoaXMub3B0aW9ucy5yZW5kZXJlcjtcclxuICB0aGlzLnJlbmRlcmVyLm9wdGlvbnMgPSB0aGlzLm9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGF0aWMgUGFyc2UgTWV0aG9kXHJcbiAqL1xyXG5cclxuUGFyc2VyLnBhcnNlID0gZnVuY3Rpb24oc3JjLCBvcHRpb25zLCByZW5kZXJlcikge1xyXG4gIHZhciBwYXJzZXIgPSBuZXcgUGFyc2VyKG9wdGlvbnMsIHJlbmRlcmVyKTtcclxuICByZXR1cm4gcGFyc2VyLnBhcnNlKHNyYyk7XHJcbn07XHJcblxyXG4vKipcclxuICogUGFyc2UgTG9vcFxyXG4gKi9cclxuXHJcblBhcnNlci5wcm90b3R5cGUucGFyc2UgPSBmdW5jdGlvbihzcmMpIHtcclxuICB0aGlzLmlubGluZSA9IG5ldyBJbmxpbmVMZXhlcihzcmMubGlua3MsIHRoaXMub3B0aW9ucywgdGhpcy5yZW5kZXJlcik7XHJcbiAgdGhpcy50b2tlbnMgPSBzcmMucmV2ZXJzZSgpO1xyXG5cclxuICB2YXIgb3V0ID0gJyc7XHJcbiAgd2hpbGUgKHRoaXMubmV4dCgpKSB7XHJcbiAgICBvdXQgKz0gdGhpcy50b2soKTtcclxuICB9XHJcblxyXG4gIHJldHVybiBvdXQ7XHJcbn07XHJcblxyXG4vKipcclxuICogTmV4dCBUb2tlblxyXG4gKi9cclxuXHJcblBhcnNlci5wcm90b3R5cGUubmV4dCA9IGZ1bmN0aW9uKCkge1xyXG4gIHJldHVybiB0aGlzLnRva2VuID0gdGhpcy50b2tlbnMucG9wKCk7XHJcbn07XHJcblxyXG4vKipcclxuICogUHJldmlldyBOZXh0IFRva2VuXHJcbiAqL1xyXG5cclxuUGFyc2VyLnByb3RvdHlwZS5wZWVrID0gZnVuY3Rpb24oKSB7XHJcbiAgcmV0dXJuIHRoaXMudG9rZW5zW3RoaXMudG9rZW5zLmxlbmd0aCAtIDFdIHx8IDA7XHJcbn07XHJcblxyXG4vKipcclxuICogUGFyc2UgVGV4dCBUb2tlbnNcclxuICovXHJcblxyXG5QYXJzZXIucHJvdG90eXBlLnBhcnNlVGV4dCA9IGZ1bmN0aW9uKCkge1xyXG4gIHZhciBib2R5ID0gdGhpcy50b2tlbi50ZXh0O1xyXG5cclxuICB3aGlsZSAodGhpcy5wZWVrKCkudHlwZSA9PT0gJ3RleHQnKSB7XHJcbiAgICBib2R5ICs9ICdcXG4nICsgdGhpcy5uZXh0KCkudGV4dDtcclxuICB9XHJcblxyXG4gIHJldHVybiB0aGlzLmlubGluZS5vdXRwdXQoYm9keSk7XHJcbn07XHJcblxyXG4vKipcclxuICogUGFyc2UgQ3VycmVudCBUb2tlblxyXG4gKi9cclxuXHJcblBhcnNlci5wcm90b3R5cGUudG9rID0gZnVuY3Rpb24oKSB7XHJcbiAgc3dpdGNoICh0aGlzLnRva2VuLnR5cGUpIHtcclxuICAgIGNhc2UgJ3NwYWNlJzoge1xyXG4gICAgICByZXR1cm4gJyc7XHJcbiAgICB9XHJcbiAgICBjYXNlICdocic6IHtcclxuICAgICAgcmV0dXJuIHRoaXMucmVuZGVyZXIuaHIoKTtcclxuICAgIH1cclxuICAgIGNhc2UgJ2hlYWRpbmcnOiB7XHJcbiAgICAgIHJldHVybiB0aGlzLnJlbmRlcmVyLmhlYWRpbmcoXHJcbiAgICAgICAgdGhpcy5pbmxpbmUub3V0cHV0KHRoaXMudG9rZW4udGV4dCksXHJcbiAgICAgICAgdGhpcy50b2tlbi5kZXB0aCxcclxuICAgICAgICB0aGlzLnRva2VuLnRleHQpO1xyXG4gICAgfVxyXG4gICAgY2FzZSAnY29kZSc6IHtcclxuICAgICAgcmV0dXJuIHRoaXMucmVuZGVyZXIuY29kZSh0aGlzLnRva2VuLnRleHQsXHJcbiAgICAgICAgdGhpcy50b2tlbi5sYW5nLFxyXG4gICAgICAgIHRoaXMudG9rZW4uZXNjYXBlZCk7XHJcbiAgICB9XHJcbiAgICBjYXNlICd0YWJsZSc6IHtcclxuICAgICAgdmFyIGhlYWRlciA9ICcnXHJcbiAgICAgICAgLCBib2R5ID0gJydcclxuICAgICAgICAsIGlcclxuICAgICAgICAsIHJvd1xyXG4gICAgICAgICwgY2VsbFxyXG4gICAgICAgICwgZmxhZ3NcclxuICAgICAgICAsIGo7XHJcblxyXG4gICAgICAvLyBoZWFkZXJcclxuICAgICAgY2VsbCA9ICcnO1xyXG4gICAgICBmb3IgKGkgPSAwOyBpIDwgdGhpcy50b2tlbi5oZWFkZXIubGVuZ3RoOyBpKyspIHtcclxuICAgICAgICBmbGFncyA9IHsgaGVhZGVyOiB0cnVlLCBhbGlnbjogdGhpcy50b2tlbi5hbGlnbltpXSB9O1xyXG4gICAgICAgIGNlbGwgKz0gdGhpcy5yZW5kZXJlci50YWJsZWNlbGwoXHJcbiAgICAgICAgICB0aGlzLmlubGluZS5vdXRwdXQodGhpcy50b2tlbi5oZWFkZXJbaV0pLFxyXG4gICAgICAgICAgeyBoZWFkZXI6IHRydWUsIGFsaWduOiB0aGlzLnRva2VuLmFsaWduW2ldIH1cclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICAgIGhlYWRlciArPSB0aGlzLnJlbmRlcmVyLnRhYmxlcm93KGNlbGwpO1xyXG5cclxuICAgICAgZm9yIChpID0gMDsgaSA8IHRoaXMudG9rZW4uY2VsbHMubGVuZ3RoOyBpKyspIHtcclxuICAgICAgICByb3cgPSB0aGlzLnRva2VuLmNlbGxzW2ldO1xyXG5cclxuICAgICAgICBjZWxsID0gJyc7XHJcbiAgICAgICAgZm9yIChqID0gMDsgaiA8IHJvdy5sZW5ndGg7IGorKykge1xyXG4gICAgICAgICAgY2VsbCArPSB0aGlzLnJlbmRlcmVyLnRhYmxlY2VsbChcclxuICAgICAgICAgICAgdGhpcy5pbmxpbmUub3V0cHV0KHJvd1tqXSksXHJcbiAgICAgICAgICAgIHsgaGVhZGVyOiBmYWxzZSwgYWxpZ246IHRoaXMudG9rZW4uYWxpZ25bal0gfVxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGJvZHkgKz0gdGhpcy5yZW5kZXJlci50YWJsZXJvdyhjZWxsKTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gdGhpcy5yZW5kZXJlci50YWJsZShoZWFkZXIsIGJvZHkpO1xyXG4gICAgfVxyXG4gICAgY2FzZSAnYmxvY2txdW90ZV9zdGFydCc6IHtcclxuICAgICAgdmFyIGJvZHkgPSAnJztcclxuXHJcbiAgICAgIHdoaWxlICh0aGlzLm5leHQoKS50eXBlICE9PSAnYmxvY2txdW90ZV9lbmQnKSB7XHJcbiAgICAgICAgYm9keSArPSB0aGlzLnRvaygpO1xyXG4gICAgICB9XHJcblxyXG4gICAgICByZXR1cm4gdGhpcy5yZW5kZXJlci5ibG9ja3F1b3RlKGJvZHkpO1xyXG4gICAgfVxyXG4gICAgY2FzZSAnbGlzdF9zdGFydCc6IHtcclxuICAgICAgdmFyIGJvZHkgPSAnJ1xyXG4gICAgICAgICwgb3JkZXJlZCA9IHRoaXMudG9rZW4ub3JkZXJlZDtcclxuXHJcbiAgICAgIHdoaWxlICh0aGlzLm5leHQoKS50eXBlICE9PSAnbGlzdF9lbmQnKSB7XHJcbiAgICAgICAgYm9keSArPSB0aGlzLnRvaygpO1xyXG4gICAgICB9XHJcblxyXG4gICAgICByZXR1cm4gdGhpcy5yZW5kZXJlci5saXN0KGJvZHksIG9yZGVyZWQpO1xyXG4gICAgfVxyXG4gICAgY2FzZSAnbGlzdF9pdGVtX3N0YXJ0Jzoge1xyXG4gICAgICB2YXIgYm9keSA9ICcnO1xyXG5cclxuICAgICAgd2hpbGUgKHRoaXMubmV4dCgpLnR5cGUgIT09ICdsaXN0X2l0ZW1fZW5kJykge1xyXG4gICAgICAgIGJvZHkgKz0gdGhpcy50b2tlbi50eXBlID09PSAndGV4dCdcclxuICAgICAgICAgID8gdGhpcy5wYXJzZVRleHQoKVxyXG4gICAgICAgICAgOiB0aGlzLnRvaygpO1xyXG4gICAgICB9XHJcblxyXG4gICAgICByZXR1cm4gdGhpcy5yZW5kZXJlci5saXN0aXRlbShib2R5KTtcclxuICAgIH1cclxuICAgIGNhc2UgJ2xvb3NlX2l0ZW1fc3RhcnQnOiB7XHJcbiAgICAgIHZhciBib2R5ID0gJyc7XHJcblxyXG4gICAgICB3aGlsZSAodGhpcy5uZXh0KCkudHlwZSAhPT0gJ2xpc3RfaXRlbV9lbmQnKSB7XHJcbiAgICAgICAgYm9keSArPSB0aGlzLnRvaygpO1xyXG4gICAgICB9XHJcblxyXG4gICAgICByZXR1cm4gdGhpcy5yZW5kZXJlci5saXN0aXRlbShib2R5KTtcclxuICAgIH1cclxuICAgIGNhc2UgJ2h0bWwnOiB7XHJcbiAgICAgIHZhciBodG1sID0gIXRoaXMudG9rZW4ucHJlICYmICF0aGlzLm9wdGlvbnMucGVkYW50aWNcclxuICAgICAgICA/IHRoaXMuaW5saW5lLm91dHB1dCh0aGlzLnRva2VuLnRleHQpXHJcbiAgICAgICAgOiB0aGlzLnRva2VuLnRleHQ7XHJcbiAgICAgIHJldHVybiB0aGlzLnJlbmRlcmVyLmh0bWwoaHRtbCk7XHJcbiAgICB9XHJcbiAgICBjYXNlICdwYXJhZ3JhcGgnOiB7XHJcbiAgICAgIHJldHVybiB0aGlzLnJlbmRlcmVyLnBhcmFncmFwaCh0aGlzLmlubGluZS5vdXRwdXQodGhpcy50b2tlbi50ZXh0KSk7XHJcbiAgICB9XHJcbiAgICBjYXNlICd0ZXh0Jzoge1xyXG4gICAgICByZXR1cm4gdGhpcy5yZW5kZXJlci5wYXJhZ3JhcGgodGhpcy5wYXJzZVRleHQoKSk7XHJcbiAgICB9XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEhlbHBlcnNcclxuICovXHJcblxyXG5mdW5jdGlvbiBlc2NhcGUoaHRtbCwgZW5jb2RlKSB7XHJcbiAgcmV0dXJuIGh0bWxcclxuICAgIC5yZXBsYWNlKCFlbmNvZGUgPyAvJig/ISM/XFx3KzspL2cgOiAvJi9nLCAnJmFtcDsnKVxyXG4gICAgLnJlcGxhY2UoLzwvZywgJyZsdDsnKVxyXG4gICAgLnJlcGxhY2UoLz4vZywgJyZndDsnKVxyXG4gICAgLnJlcGxhY2UoL1wiL2csICcmcXVvdDsnKVxyXG4gICAgLnJlcGxhY2UoLycvZywgJyYjMzk7Jyk7XHJcbn1cclxuXHJcbmZ1bmN0aW9uIHVuZXNjYXBlKGh0bWwpIHtcclxuICByZXR1cm4gaHRtbC5yZXBsYWNlKC8mKFsjXFx3XSspOy9nLCBmdW5jdGlvbihfLCBuKSB7XHJcbiAgICBuID0gbi50b0xvd2VyQ2FzZSgpO1xyXG4gICAgaWYgKG4gPT09ICdjb2xvbicpIHJldHVybiAnOic7XHJcbiAgICBpZiAobi5jaGFyQXQoMCkgPT09ICcjJykge1xyXG4gICAgICByZXR1cm4gbi5jaGFyQXQoMSkgPT09ICd4J1xyXG4gICAgICAgID8gU3RyaW5nLmZyb21DaGFyQ29kZShwYXJzZUludChuLnN1YnN0cmluZygyKSwgMTYpKVxyXG4gICAgICAgIDogU3RyaW5nLmZyb21DaGFyQ29kZSgrbi5zdWJzdHJpbmcoMSkpO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuICcnO1xyXG4gIH0pO1xyXG59XHJcblxyXG5mdW5jdGlvbiByZXBsYWNlKHJlZ2V4LCBvcHQpIHtcclxuICByZWdleCA9IHJlZ2V4LnNvdXJjZTtcclxuICBvcHQgPSBvcHQgfHwgJyc7XHJcbiAgcmV0dXJuIGZ1bmN0aW9uIHNlbGYobmFtZSwgdmFsKSB7XHJcbiAgICBpZiAoIW5hbWUpIHJldHVybiBuZXcgUmVnRXhwKHJlZ2V4LCBvcHQpO1xyXG4gICAgdmFsID0gdmFsLnNvdXJjZSB8fCB2YWw7XHJcbiAgICB2YWwgPSB2YWwucmVwbGFjZSgvKF58W15cXFtdKVxcXi9nLCAnJDEnKTtcclxuICAgIHJlZ2V4ID0gcmVnZXgucmVwbGFjZShuYW1lLCB2YWwpO1xyXG4gICAgcmV0dXJuIHNlbGY7XHJcbiAgfTtcclxufVxyXG5cclxuZnVuY3Rpb24gbm9vcCgpIHt9XHJcbm5vb3AuZXhlYyA9IG5vb3A7XHJcblxyXG5mdW5jdGlvbiBtZXJnZShvYmopIHtcclxuICB2YXIgaSA9IDFcclxuICAgICwgdGFyZ2V0XHJcbiAgICAsIGtleTtcclxuXHJcbiAgZm9yICg7IGkgPCBhcmd1bWVudHMubGVuZ3RoOyBpKyspIHtcclxuICAgIHRhcmdldCA9IGFyZ3VtZW50c1tpXTtcclxuICAgIGZvciAoa2V5IGluIHRhcmdldCkge1xyXG4gICAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHRhcmdldCwga2V5KSkge1xyXG4gICAgICAgIG9ialtrZXldID0gdGFyZ2V0W2tleV07XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcblxyXG4gIHJldHVybiBvYmo7XHJcbn1cclxuXHJcblxyXG4vKipcclxuICogTWFya2VkXHJcbiAqL1xyXG5cclxuZnVuY3Rpb24gbWFya2VkKHNyYywgb3B0LCBjYWxsYmFjaykge1xyXG4gIGlmIChjYWxsYmFjayB8fCB0eXBlb2Ygb3B0ID09PSAnZnVuY3Rpb24nKSB7XHJcbiAgICBpZiAoIWNhbGxiYWNrKSB7XHJcbiAgICAgIGNhbGxiYWNrID0gb3B0O1xyXG4gICAgICBvcHQgPSBudWxsO1xyXG4gICAgfVxyXG5cclxuICAgIG9wdCA9IG1lcmdlKHt9LCBtYXJrZWQuZGVmYXVsdHMsIG9wdCB8fCB7fSk7XHJcblxyXG4gICAgdmFyIGhpZ2hsaWdodCA9IG9wdC5oaWdobGlnaHRcclxuICAgICAgLCB0b2tlbnNcclxuICAgICAgLCBwZW5kaW5nXHJcbiAgICAgICwgaSA9IDA7XHJcblxyXG4gICAgdHJ5IHtcclxuICAgICAgdG9rZW5zID0gTGV4ZXIubGV4KHNyYywgb3B0KVxyXG4gICAgfSBjYXRjaCAoZSkge1xyXG4gICAgICByZXR1cm4gY2FsbGJhY2soZSk7XHJcbiAgICB9XHJcblxyXG4gICAgcGVuZGluZyA9IHRva2Vucy5sZW5ndGg7XHJcblxyXG4gICAgdmFyIGRvbmUgPSBmdW5jdGlvbihlcnIpIHtcclxuICAgICAgaWYgKGVycikge1xyXG4gICAgICAgIG9wdC5oaWdobGlnaHQgPSBoaWdobGlnaHQ7XHJcbiAgICAgICAgcmV0dXJuIGNhbGxiYWNrKGVycik7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIHZhciBvdXQ7XHJcblxyXG4gICAgICB0cnkge1xyXG4gICAgICAgIG91dCA9IFBhcnNlci5wYXJzZSh0b2tlbnMsIG9wdCk7XHJcbiAgICAgIH0gY2F0Y2ggKGUpIHtcclxuICAgICAgICBlcnIgPSBlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBvcHQuaGlnaGxpZ2h0ID0gaGlnaGxpZ2h0O1xyXG5cclxuICAgICAgcmV0dXJuIGVyclxyXG4gICAgICAgID8gY2FsbGJhY2soZXJyKVxyXG4gICAgICAgIDogY2FsbGJhY2sobnVsbCwgb3V0KTtcclxuICAgIH07XHJcblxyXG4gICAgaWYgKCFoaWdobGlnaHQgfHwgaGlnaGxpZ2h0Lmxlbmd0aCA8IDMpIHtcclxuICAgICAgcmV0dXJuIGRvbmUoKTtcclxuICAgIH1cclxuXHJcbiAgICBkZWxldGUgb3B0LmhpZ2hsaWdodDtcclxuXHJcbiAgICBpZiAoIXBlbmRpbmcpIHJldHVybiBkb25lKCk7XHJcblxyXG4gICAgZm9yICg7IGkgPCB0b2tlbnMubGVuZ3RoOyBpKyspIHtcclxuICAgICAgKGZ1bmN0aW9uKHRva2VuKSB7XHJcbiAgICAgICAgaWYgKHRva2VuLnR5cGUgIT09ICdjb2RlJykge1xyXG4gICAgICAgICAgcmV0dXJuIC0tcGVuZGluZyB8fCBkb25lKCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiBoaWdobGlnaHQodG9rZW4udGV4dCwgdG9rZW4ubGFuZywgZnVuY3Rpb24oZXJyLCBjb2RlKSB7XHJcbiAgICAgICAgICBpZiAoZXJyKSByZXR1cm4gZG9uZShlcnIpO1xyXG4gICAgICAgICAgaWYgKGNvZGUgPT0gbnVsbCB8fCBjb2RlID09PSB0b2tlbi50ZXh0KSB7XHJcbiAgICAgICAgICAgIHJldHVybiAtLXBlbmRpbmcgfHwgZG9uZSgpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgdG9rZW4udGV4dCA9IGNvZGU7XHJcbiAgICAgICAgICB0b2tlbi5lc2NhcGVkID0gdHJ1ZTtcclxuICAgICAgICAgIC0tcGVuZGluZyB8fCBkb25lKCk7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0pKHRva2Vuc1tpXSk7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuO1xyXG4gIH1cclxuICB0cnkge1xyXG4gICAgaWYgKG9wdCkgb3B0ID0gbWVyZ2Uoe30sIG1hcmtlZC5kZWZhdWx0cywgb3B0KTtcclxuICAgIHJldHVybiBQYXJzZXIucGFyc2UoTGV4ZXIubGV4KHNyYywgb3B0KSwgb3B0KTtcclxuICB9IGNhdGNoIChlKSB7XHJcbiAgICBlLm1lc3NhZ2UgKz0gJ1xcblBsZWFzZSByZXBvcnQgdGhpcyB0byBodHRwczovL2dpdGh1Yi5jb20vY2hqai9tYXJrZWQuJztcclxuICAgIGlmICgob3B0IHx8IG1hcmtlZC5kZWZhdWx0cykuc2lsZW50KSB7XHJcbiAgICAgIHJldHVybiAnPHA+QW4gZXJyb3Igb2NjdXJlZDo8L3A+PHByZT4nXHJcbiAgICAgICAgKyBlc2NhcGUoZS5tZXNzYWdlICsgJycsIHRydWUpXHJcbiAgICAgICAgKyAnPC9wcmU+JztcclxuICAgIH1cclxuICAgIHRocm93IGU7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogT3B0aW9uc1xyXG4gKi9cclxuXHJcbm1hcmtlZC5vcHRpb25zID1cclxubWFya2VkLnNldE9wdGlvbnMgPSBmdW5jdGlvbihvcHQpIHtcclxuICBtZXJnZShtYXJrZWQuZGVmYXVsdHMsIG9wdCk7XHJcbiAgcmV0dXJuIG1hcmtlZDtcclxufTtcclxuXHJcbm1hcmtlZC5kZWZhdWx0cyA9IHtcclxuICBnZm06IHRydWUsXHJcbiAgdGFibGVzOiB0cnVlLFxyXG4gIGJyZWFrczogZmFsc2UsXHJcbiAgcGVkYW50aWM6IGZhbHNlLFxyXG4gIHNhbml0aXplOiBmYWxzZSxcclxuICBzYW5pdGl6ZXI6IG51bGwsXHJcbiAgbWFuZ2xlOiB0cnVlLFxyXG4gIHNtYXJ0TGlzdHM6IGZhbHNlLFxyXG4gIHNpbGVudDogZmFsc2UsXHJcbiAgaGlnaGxpZ2h0OiBudWxsLFxyXG4gIGxhbmdQcmVmaXg6ICdsYW5nLScsXHJcbiAgc21hcnR5cGFudHM6IGZhbHNlLFxyXG4gIGhlYWRlclByZWZpeDogJycsXHJcbiAgcmVuZGVyZXI6IG5ldyBSZW5kZXJlcixcclxuICB4aHRtbDogZmFsc2VcclxufTtcclxuXHJcbi8qKlxyXG4gKiBFeHBvc2VcclxuICovXHJcblxyXG5tYXJrZWQuUGFyc2VyID0gUGFyc2VyO1xyXG5tYXJrZWQucGFyc2VyID0gUGFyc2VyLnBhcnNlO1xyXG5cclxubWFya2VkLlJlbmRlcmVyID0gUmVuZGVyZXI7XHJcblxyXG5tYXJrZWQuTGV4ZXIgPSBMZXhlcjtcclxubWFya2VkLmxleGVyID0gTGV4ZXIubGV4O1xyXG5cclxubWFya2VkLklubGluZUxleGVyID0gSW5saW5lTGV4ZXI7XHJcbm1hcmtlZC5pbmxpbmVMZXhlciA9IElubGluZUxleGVyLm91dHB1dDtcclxuXHJcbm1hcmtlZC5wYXJzZSA9IG1hcmtlZDtcclxuXHJcbmlmICh0eXBlb2YgbW9kdWxlICE9PSAndW5kZWZpbmVkJyAmJiB0eXBlb2YgZXhwb3J0cyA9PT0gJ29iamVjdCcpIHtcclxuICBtb2R1bGUuZXhwb3J0cyA9IG1hcmtlZDtcclxufSBlbHNlIGlmICh0eXBlb2YgZGVmaW5lID09PSAnZnVuY3Rpb24nICYmIGRlZmluZS5hbWQpIHtcclxuICBkZWZpbmUoZnVuY3Rpb24oKSB7IHJldHVybiBtYXJrZWQ7IH0pO1xyXG59IGVsc2Uge1xyXG4gIHRoaXMubWFya2VkID0gbWFya2VkO1xyXG59XHJcblxyXG59KS5jYWxsKGZ1bmN0aW9uKCkge1xyXG4gIHJldHVybiB0aGlzIHx8ICh0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJyA/IHdpbmRvdyA6IGdsb2JhbCk7XHJcbn0oKSk7XHJcbiIsIi8vIENvZGVNaXJyb3IsIGNvcHlyaWdodCAoYykgYnkgTWFyaWpuIEhhdmVyYmVrZSBhbmQgb3RoZXJzXG4vLyBEaXN0cmlidXRlZCB1bmRlciBhbiBNSVQgbGljZW5zZTogaHR0cDovL2NvZGVtaXJyb3IubmV0L0xJQ0VOU0VcblxudmFyIENvZGVNaXJyb3IgPSByZXF1aXJlKFwiY29kZW1pcnJvclwiKTtcblxuQ29kZU1pcnJvci5jb21tYW5kcy50YWJBbmRJbmRlbnRNYXJrZG93bkxpc3QgPSBmdW5jdGlvbiAoY20pIHtcblx0dmFyIHJhbmdlcyA9IGNtLmxpc3RTZWxlY3Rpb25zKCk7XG5cdHZhciBwb3MgPSByYW5nZXNbMF0uaGVhZDtcblx0dmFyIGVvbFN0YXRlID0gY20uZ2V0U3RhdGVBZnRlcihwb3MubGluZSk7XG5cdHZhciBpbkxpc3QgPSBlb2xTdGF0ZS5saXN0ICE9PSBmYWxzZTtcblxuXHRpZiAoaW5MaXN0KSB7XG5cdFx0Y20uZXhlY0NvbW1hbmQoXCJpbmRlbnRNb3JlXCIpO1xuXHRcdHJldHVybjtcblx0fVxuXG5cdGlmIChjbS5vcHRpb25zLmluZGVudFdpdGhUYWJzKSB7XG5cdFx0Y20uZXhlY0NvbW1hbmQoXCJpbnNlcnRUYWJcIik7XG5cdH1cblx0ZWxzZSB7XG5cdFx0dmFyIHNwYWNlcyA9IEFycmF5KGNtLm9wdGlvbnMudGFiU2l6ZSArIDEpLmpvaW4oXCIgXCIpO1xuXHRcdGNtLnJlcGxhY2VTZWxlY3Rpb24oc3BhY2VzKTtcblx0fVxufTtcblxuQ29kZU1pcnJvci5jb21tYW5kcy5zaGlmdFRhYkFuZFVuaW5kZW50TWFya2Rvd25MaXN0ID0gZnVuY3Rpb24gKGNtKSB7XG5cdHZhciByYW5nZXMgPSBjbS5saXN0U2VsZWN0aW9ucygpO1xuXHR2YXIgcG9zID0gcmFuZ2VzWzBdLmhlYWQ7XG5cdHZhciBlb2xTdGF0ZSA9IGNtLmdldFN0YXRlQWZ0ZXIocG9zLmxpbmUpO1xuXHR2YXIgaW5MaXN0ID0gZW9sU3RhdGUubGlzdCAhPT0gZmFsc2U7XG5cblx0aWYgKGluTGlzdCkge1xuXHRcdGNtLmV4ZWNDb21tYW5kKFwiaW5kZW50TGVzc1wiKTtcblx0XHRyZXR1cm47XG5cdH1cblxuXHRpZiAoY20ub3B0aW9ucy5pbmRlbnRXaXRoVGFicykge1xuXHRcdGNtLmV4ZWNDb21tYW5kKFwiaW5zZXJ0VGFiXCIpO1xuXHR9XG5cdGVsc2Uge1xuXHRcdHZhciBzcGFjZXMgPSBBcnJheShjbS5vcHRpb25zLnRhYlNpemUgKyAxKS5qb2luKFwiIFwiKTtcblx0XHRjbS5yZXBsYWNlU2VsZWN0aW9uKHNwYWNlcyk7XG5cdH1cbn07XG4iLCIvKmdsb2JhbCByZXF1aXJlLG1vZHVsZSovXHJcblwidXNlIHN0cmljdFwiO1xyXG52YXIgQ29kZU1pcnJvciA9IHJlcXVpcmUoXCJjb2RlbWlycm9yXCIpO1xyXG5yZXF1aXJlKFwiY29kZW1pcnJvci9hZGRvbi9lZGl0L2NvbnRpbnVlbGlzdC5qc1wiKTtcclxucmVxdWlyZShcIi4vY29kZW1pcnJvci90YWJsaXN0XCIpO1xyXG5yZXF1aXJlKFwiY29kZW1pcnJvci9hZGRvbi9kaXNwbGF5L2Z1bGxzY3JlZW4uanNcIik7XHJcbnJlcXVpcmUoXCJjb2RlbWlycm9yL21vZGUvbWFya2Rvd24vbWFya2Rvd24uanNcIik7XHJcbnJlcXVpcmUoXCJjb2RlbWlycm9yL2FkZG9uL21vZGUvb3ZlcmxheS5qc1wiKTtcclxucmVxdWlyZShcImNvZGVtaXJyb3IvbW9kZS9nZm0vZ2ZtLmpzXCIpO1xyXG5yZXF1aXJlKFwiY29kZW1pcnJvci9tb2RlL3htbC94bWwuanNcIik7XHJcbnJlcXVpcmUoXCJzcGVsbC1jaGVja2VyXCIpO1xyXG52YXIgbWFya2VkID0gcmVxdWlyZShcIm1hcmtlZFwiKTtcclxuXHJcblxyXG52YXIgaXNNYWMgPSAvTWFjLy50ZXN0KG5hdmlnYXRvci5wbGF0Zm9ybSk7XHJcblxyXG52YXIgc2hvcnRjdXRzID0ge1xyXG5cdFwiQ21kLUJcIjogdG9nZ2xlQm9sZCxcclxuXHRcIkNtZC1JXCI6IHRvZ2dsZUl0YWxpYyxcclxuXHRcIkNtZC1LXCI6IGRyYXdMaW5rLFxyXG5cdFwiQ21kLUhcIjogdG9nZ2xlSGVhZGluZ1NtYWxsZXIsXHJcblx0XCJTaGlmdC1DbWQtSFwiOiB0b2dnbGVIZWFkaW5nQmlnZ2VyLFxyXG5cdFwiQ21kLUFsdC1JXCI6IGRyYXdJbWFnZSxcclxuXHRcIkNtZC0nXCI6IHRvZ2dsZUJsb2NrcXVvdGUsXHJcblx0XCJDbWQtQWx0LUxcIjogdG9nZ2xlT3JkZXJlZExpc3QsXHJcblx0XCJDbWQtTFwiOiB0b2dnbGVVbm9yZGVyZWRMaXN0LFxyXG5cdFwiQ21kLUFsdC1DXCI6IHRvZ2dsZUNvZGVCbG9jayxcclxuXHRcIkNtZC1QXCI6IHRvZ2dsZVByZXZpZXdcclxufTtcclxuXHJcblxyXG4vKipcclxuICogRml4IHNob3J0Y3V0LiBNYWMgdXNlIENvbW1hbmQsIG90aGVycyB1c2UgQ3RybC5cclxuICovXHJcbmZ1bmN0aW9uIGZpeFNob3J0Y3V0KG5hbWUpIHtcclxuXHRpZihpc01hYykge1xyXG5cdFx0bmFtZSA9IG5hbWUucmVwbGFjZShcIkN0cmxcIiwgXCJDbWRcIik7XHJcblx0fSBlbHNlIHtcclxuXHRcdG5hbWUgPSBuYW1lLnJlcGxhY2UoXCJDbWRcIiwgXCJDdHJsXCIpO1xyXG5cdH1cclxuXHRyZXR1cm4gbmFtZTtcclxufVxyXG5cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGUgaWNvbiBlbGVtZW50IGZvciB0b29sYmFyLlxyXG4gKi9cclxuZnVuY3Rpb24gY3JlYXRlSWNvbihvcHRpb25zLCBlbmFibGVUb29sdGlwcykge1xyXG5cdG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xyXG5cdHZhciBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJhXCIpO1xyXG5cdGVuYWJsZVRvb2x0aXBzID0gKGVuYWJsZVRvb2x0aXBzID09IHVuZGVmaW5lZCkgPyB0cnVlIDogZW5hYmxlVG9vbHRpcHM7XHJcblxyXG5cdGlmKG9wdGlvbnMudGl0bGUgJiYgZW5hYmxlVG9vbHRpcHMpIHtcclxuXHRcdGVsLnRpdGxlID0gb3B0aW9ucy50aXRsZTtcclxuXHJcblx0XHRpZihpc01hYykge1xyXG5cdFx0XHRlbC50aXRsZSA9IGVsLnRpdGxlLnJlcGxhY2UoXCJDdHJsXCIsIFwi4oyYXCIpO1xyXG5cdFx0XHRlbC50aXRsZSA9IGVsLnRpdGxlLnJlcGxhY2UoXCJBbHRcIiwgXCLijKVcIik7XHJcblx0XHR9XHJcblx0fVxyXG5cclxuXHRlbC5jbGFzc05hbWUgPSBvcHRpb25zLmNsYXNzTmFtZTtcclxuXHRyZXR1cm4gZWw7XHJcbn1cclxuXHJcbmZ1bmN0aW9uIGNyZWF0ZVNlcCgpIHtcclxuXHR2YXIgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwiaVwiKTtcclxuXHRlbC5jbGFzc05hbWUgPSBcInNlcGFyYXRvclwiO1xyXG5cdGVsLmlubmVySFRNTCA9IFwifFwiO1xyXG5cdHJldHVybiBlbDtcclxufVxyXG5cclxuXHJcbi8qKlxyXG4gKiBUaGUgc3RhdGUgb2YgQ29kZU1pcnJvciBhdCB0aGUgZ2l2ZW4gcG9zaXRpb24uXHJcbiAqL1xyXG5mdW5jdGlvbiBnZXRTdGF0ZShjbSwgcG9zKSB7XHJcblx0cG9zID0gcG9zIHx8IGNtLmdldEN1cnNvcihcInN0YXJ0XCIpO1xyXG5cdHZhciBzdGF0ID0gY20uZ2V0VG9rZW5BdChwb3MpO1xyXG5cdGlmKCFzdGF0LnR5cGUpIHJldHVybiB7fTtcclxuXHJcblx0dmFyIHR5cGVzID0gc3RhdC50eXBlLnNwbGl0KFwiIFwiKTtcclxuXHJcblx0dmFyIHJldCA9IHt9LFxyXG5cdFx0ZGF0YSwgdGV4dDtcclxuXHRmb3IodmFyIGkgPSAwOyBpIDwgdHlwZXMubGVuZ3RoOyBpKyspIHtcclxuXHRcdGRhdGEgPSB0eXBlc1tpXTtcclxuXHRcdGlmKGRhdGEgPT09IFwic3Ryb25nXCIpIHtcclxuXHRcdFx0cmV0LmJvbGQgPSB0cnVlO1xyXG5cdFx0fSBlbHNlIGlmKGRhdGEgPT09IFwidmFyaWFibGUtMlwiKSB7XHJcblx0XHRcdHRleHQgPSBjbS5nZXRMaW5lKHBvcy5saW5lKTtcclxuXHRcdFx0aWYoL15cXHMqXFxkK1xcLlxccy8udGVzdCh0ZXh0KSkge1xyXG5cdFx0XHRcdHJldFtcIm9yZGVyZWQtbGlzdFwiXSA9IHRydWU7XHJcblx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0cmV0W1widW5vcmRlcmVkLWxpc3RcIl0gPSB0cnVlO1xyXG5cdFx0XHR9XHJcblx0XHR9IGVsc2UgaWYoZGF0YSA9PT0gXCJhdG9tXCIpIHtcclxuXHRcdFx0cmV0LnF1b3RlID0gdHJ1ZTtcclxuXHRcdH0gZWxzZSBpZihkYXRhID09PSBcImVtXCIpIHtcclxuXHRcdFx0cmV0Lml0YWxpYyA9IHRydWU7XHJcblx0XHR9IGVsc2UgaWYoZGF0YSA9PT0gXCJxdW90ZVwiKSB7XHJcblx0XHRcdHJldC5xdW90ZSA9IHRydWU7XHJcblx0XHR9IGVsc2UgaWYoZGF0YSA9PT0gXCJzdHJpa2V0aHJvdWdoXCIpIHtcclxuXHRcdFx0cmV0LnN0cmlrZXRocm91Z2ggPSB0cnVlO1xyXG5cdFx0fSBlbHNlIGlmKGRhdGEgPT09IFwiY29tbWVudFwiKSB7XHJcblx0XHRcdHJldC5jb2RlID0gdHJ1ZTtcclxuXHRcdH1cclxuXHR9XHJcblx0cmV0dXJuIHJldDtcclxufVxyXG5cclxuXHJcbi8vIFNhdmVkIG92ZXJmbG93IHNldHRpbmdcclxudmFyIHNhdmVkX292ZXJmbG93ID0gXCJcIjtcclxuXHJcbi8qKlxyXG4gKiBUb2dnbGUgZnVsbCBzY3JlZW4gb2YgdGhlIGVkaXRvci5cclxuICovXHJcbmZ1bmN0aW9uIHRvZ2dsZUZ1bGxTY3JlZW4oZWRpdG9yKSB7XHJcblx0Ly8gU2V0IGZ1bGxzY3JlZW5cclxuXHR2YXIgY20gPSBlZGl0b3IuY29kZW1pcnJvcjtcclxuXHRjbS5zZXRPcHRpb24oXCJmdWxsU2NyZWVuXCIsICFjbS5nZXRPcHRpb24oXCJmdWxsU2NyZWVuXCIpKTtcclxuXHJcblxyXG5cdC8vIFByZXZlbnQgc2Nyb2xsaW5nIG9uIGJvZHkgZHVyaW5nIGZ1bGxzY3JlZW4gYWN0aXZlXHJcblx0aWYoY20uZ2V0T3B0aW9uKFwiZnVsbFNjcmVlblwiKSkge1xyXG5cdFx0c2F2ZWRfb3ZlcmZsb3cgPSBkb2N1bWVudC5ib2R5LnN0eWxlLm92ZXJmbG93O1xyXG5cdFx0ZG9jdW1lbnQuYm9keS5zdHlsZS5vdmVyZmxvdyA9IFwiaGlkZGVuXCI7XHJcblx0fSBlbHNlIHtcclxuXHRcdGRvY3VtZW50LmJvZHkuc3R5bGUub3ZlcmZsb3cgPSBzYXZlZF9vdmVyZmxvdztcclxuXHR9XHJcblxyXG5cclxuXHQvLyBVcGRhdGUgdG9vbGJhciBjbGFzc1xyXG5cdHZhciB3cmFwID0gY20uZ2V0V3JhcHBlckVsZW1lbnQoKTtcclxuXHJcblx0aWYoIS9mdWxsc2NyZWVuLy50ZXN0KHdyYXAucHJldmlvdXNTaWJsaW5nLmNsYXNzTmFtZSkpIHtcclxuXHRcdHdyYXAucHJldmlvdXNTaWJsaW5nLmNsYXNzTmFtZSArPSBcIiBmdWxsc2NyZWVuXCI7XHJcblx0fSBlbHNlIHtcclxuXHRcdHdyYXAucHJldmlvdXNTaWJsaW5nLmNsYXNzTmFtZSA9IHdyYXAucHJldmlvdXNTaWJsaW5nLmNsYXNzTmFtZS5yZXBsYWNlKC9cXHMqZnVsbHNjcmVlblxcYi8sIFwiXCIpO1xyXG5cdH1cclxuXHJcblxyXG5cdC8vIFVwZGF0ZSB0b29sYmFyIGJ1dHRvblxyXG5cdHZhciB0b29sYmFyQnV0dG9uID0gZWRpdG9yLnRvb2xiYXJFbGVtZW50cy5mdWxsc2NyZWVuO1xyXG5cclxuXHRpZighL2FjdGl2ZS8udGVzdCh0b29sYmFyQnV0dG9uLmNsYXNzTmFtZSkpIHtcclxuXHRcdHRvb2xiYXJCdXR0b24uY2xhc3NOYW1lICs9IFwiIGFjdGl2ZVwiO1xyXG5cdH0gZWxzZSB7XHJcblx0XHR0b29sYmFyQnV0dG9uLmNsYXNzTmFtZSA9IHRvb2xiYXJCdXR0b24uY2xhc3NOYW1lLnJlcGxhY2UoL1xccyphY3RpdmVcXHMqL2csIFwiXCIpO1xyXG5cdH1cclxuXHJcblxyXG5cdC8vIEhpZGUgc2lkZSBieSBzaWRlIGlmIG5lZWRlZFxyXG5cdHZhciBzaWRlYnlzaWRlID0gY20uZ2V0V3JhcHBlckVsZW1lbnQoKS5uZXh0U2libGluZztcclxuXHRpZigvZWRpdG9yLXByZXZpZXctYWN0aXZlLXNpZGUvLnRlc3Qoc2lkZWJ5c2lkZS5jbGFzc05hbWUpKVxyXG5cdFx0dG9nZ2xlU2lkZUJ5U2lkZShlZGl0b3IpO1xyXG59XHJcblxyXG5cclxuLyoqXHJcbiAqIEFjdGlvbiBmb3IgdG9nZ2xpbmcgYm9sZC5cclxuICovXHJcbmZ1bmN0aW9uIHRvZ2dsZUJvbGQoZWRpdG9yKSB7XHJcblx0X3RvZ2dsZUJsb2NrKGVkaXRvciwgXCJib2xkXCIsIFwiKipcIik7XHJcbn1cclxuXHJcblxyXG4vKipcclxuICogQWN0aW9uIGZvciB0b2dnbGluZyBpdGFsaWMuXHJcbiAqL1xyXG5mdW5jdGlvbiB0b2dnbGVJdGFsaWMoZWRpdG9yKSB7XHJcblx0X3RvZ2dsZUJsb2NrKGVkaXRvciwgXCJpdGFsaWNcIiwgXCIqXCIpO1xyXG59XHJcblxyXG5cclxuLyoqXHJcbiAqIEFjdGlvbiBmb3IgdG9nZ2xpbmcgc3RyaWtldGhyb3VnaC5cclxuICovXHJcbmZ1bmN0aW9uIHRvZ2dsZVN0cmlrZXRocm91Z2goZWRpdG9yKSB7XHJcblx0X3RvZ2dsZUJsb2NrKGVkaXRvciwgXCJzdHJpa2V0aHJvdWdoXCIsIFwifn5cIik7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBY3Rpb24gZm9yIHRvZ2dsaW5nIGNvZGUgYmxvY2suXHJcbiAqL1xyXG5mdW5jdGlvbiB0b2dnbGVDb2RlQmxvY2soZWRpdG9yKSB7XHJcblx0X3RvZ2dsZUJsb2NrKGVkaXRvciwgXCJjb2RlXCIsIFwiYGBgXFxyXFxuXCIsIFwiXFxyXFxuYGBgXCIpO1xyXG59XHJcblxyXG4vKipcclxuICogQWN0aW9uIGZvciB0b2dnbGluZyBibG9ja3F1b3RlLlxyXG4gKi9cclxuZnVuY3Rpb24gdG9nZ2xlQmxvY2txdW90ZShlZGl0b3IpIHtcclxuXHR2YXIgY20gPSBlZGl0b3IuY29kZW1pcnJvcjtcclxuXHRfdG9nZ2xlTGluZShjbSwgXCJxdW90ZVwiKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFjdGlvbiBmb3IgdG9nZ2xpbmcgaGVhZGluZyBzaXplOiBub3JtYWwgLT4gaDEgLT4gaDIgLT4gaDMgLT4gaDQgLT4gaDUgLT4gaDYgLT4gbm9ybWFsXHJcbiAqL1xyXG5mdW5jdGlvbiB0b2dnbGVIZWFkaW5nU21hbGxlcihlZGl0b3IpIHtcclxuXHR2YXIgY20gPSBlZGl0b3IuY29kZW1pcnJvcjtcclxuXHRfdG9nZ2xlSGVhZGluZyhjbSwgXCJzbWFsbGVyXCIpO1xyXG59XHJcblxyXG4vKipcclxuICogQWN0aW9uIGZvciB0b2dnbGluZyBoZWFkaW5nIHNpemU6IG5vcm1hbCAtPiBoNiAtPiBoNSAtPiBoNCAtPiBoMyAtPiBoMiAtPiBoMSAtPiBub3JtYWxcclxuICovXHJcbmZ1bmN0aW9uIHRvZ2dsZUhlYWRpbmdCaWdnZXIoZWRpdG9yKSB7XHJcblx0dmFyIGNtID0gZWRpdG9yLmNvZGVtaXJyb3I7XHJcblx0X3RvZ2dsZUhlYWRpbmcoY20sIFwiYmlnZ2VyXCIpO1xyXG59XHJcblxyXG4vKipcclxuICogQWN0aW9uIGZvciB0b2dnbGluZyBoZWFkaW5nIHNpemUgMVxyXG4gKi9cclxuZnVuY3Rpb24gdG9nZ2xlSGVhZGluZzEoZWRpdG9yKSB7XHJcblx0dmFyIGNtID0gZWRpdG9yLmNvZGVtaXJyb3I7XHJcblx0X3RvZ2dsZUhlYWRpbmcoY20sIHVuZGVmaW5lZCwgMSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBY3Rpb24gZm9yIHRvZ2dsaW5nIGhlYWRpbmcgc2l6ZSAyXHJcbiAqL1xyXG5mdW5jdGlvbiB0b2dnbGVIZWFkaW5nMihlZGl0b3IpIHtcclxuXHR2YXIgY20gPSBlZGl0b3IuY29kZW1pcnJvcjtcclxuXHRfdG9nZ2xlSGVhZGluZyhjbSwgdW5kZWZpbmVkLCAyKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFjdGlvbiBmb3IgdG9nZ2xpbmcgaGVhZGluZyBzaXplIDNcclxuICovXHJcbmZ1bmN0aW9uIHRvZ2dsZUhlYWRpbmczKGVkaXRvcikge1xyXG5cdHZhciBjbSA9IGVkaXRvci5jb2RlbWlycm9yO1xyXG5cdF90b2dnbGVIZWFkaW5nKGNtLCB1bmRlZmluZWQsIDMpO1xyXG59XHJcblxyXG5cclxuLyoqXHJcbiAqIEFjdGlvbiBmb3IgdG9nZ2xpbmcgdWwuXHJcbiAqL1xyXG5mdW5jdGlvbiB0b2dnbGVVbm9yZGVyZWRMaXN0KGVkaXRvcikge1xyXG5cdHZhciBjbSA9IGVkaXRvci5jb2RlbWlycm9yO1xyXG5cdF90b2dnbGVMaW5lKGNtLCBcInVub3JkZXJlZC1saXN0XCIpO1xyXG59XHJcblxyXG5cclxuLyoqXHJcbiAqIEFjdGlvbiBmb3IgdG9nZ2xpbmcgb2wuXHJcbiAqL1xyXG5mdW5jdGlvbiB0b2dnbGVPcmRlcmVkTGlzdChlZGl0b3IpIHtcclxuXHR2YXIgY20gPSBlZGl0b3IuY29kZW1pcnJvcjtcclxuXHRfdG9nZ2xlTGluZShjbSwgXCJvcmRlcmVkLWxpc3RcIik7XHJcbn1cclxuXHJcblxyXG4vKipcclxuICogQWN0aW9uIGZvciBkcmF3aW5nIGEgbGluay5cclxuICovXHJcbmZ1bmN0aW9uIGRyYXdMaW5rKGVkaXRvcikge1xyXG5cdHZhciBjbSA9IGVkaXRvci5jb2RlbWlycm9yO1xyXG5cdHZhciBzdGF0ID0gZ2V0U3RhdGUoY20pO1xyXG5cdF9yZXBsYWNlU2VsZWN0aW9uKGNtLCBzdGF0LmxpbmssIFwiW1wiLCBcIl0oaHR0cDovLylcIik7XHJcbn1cclxuXHJcblxyXG4vKipcclxuICogQWN0aW9uIGZvciBkcmF3aW5nIGFuIGltZy5cclxuICovXHJcbmZ1bmN0aW9uIGRyYXdJbWFnZShlZGl0b3IpIHtcclxuXHR2YXIgY20gPSBlZGl0b3IuY29kZW1pcnJvcjtcclxuXHR2YXIgc3RhdCA9IGdldFN0YXRlKGNtKTtcclxuXHRfcmVwbGFjZVNlbGVjdGlvbihjbSwgc3RhdC5pbWFnZSwgXCIhW10oaHR0cDovL1wiLCBcIilcIik7XHJcbn1cclxuXHJcblxyXG4vKipcclxuICogQWN0aW9uIGZvciBkcmF3aW5nIGEgaG9yaXpvbnRhbCBydWxlLlxyXG4gKi9cclxuZnVuY3Rpb24gZHJhd0hvcml6b250YWxSdWxlKGVkaXRvcikge1xyXG5cdHZhciBjbSA9IGVkaXRvci5jb2RlbWlycm9yO1xyXG5cdHZhciBzdGF0ID0gZ2V0U3RhdGUoY20pO1xyXG5cdF9yZXBsYWNlU2VsZWN0aW9uKGNtLCBzdGF0LmltYWdlLCBcIlwiLCBcIlxcblxcbi0tLS0tXFxuXFxuXCIpO1xyXG59XHJcblxyXG5cclxuLyoqXHJcbiAqIFVuZG8gYWN0aW9uLlxyXG4gKi9cclxuZnVuY3Rpb24gdW5kbyhlZGl0b3IpIHtcclxuXHR2YXIgY20gPSBlZGl0b3IuY29kZW1pcnJvcjtcclxuXHRjbS51bmRvKCk7XHJcblx0Y20uZm9jdXMoKTtcclxufVxyXG5cclxuXHJcbi8qKlxyXG4gKiBSZWRvIGFjdGlvbi5cclxuICovXHJcbmZ1bmN0aW9uIHJlZG8oZWRpdG9yKSB7XHJcblx0dmFyIGNtID0gZWRpdG9yLmNvZGVtaXJyb3I7XHJcblx0Y20ucmVkbygpO1xyXG5cdGNtLmZvY3VzKCk7XHJcbn1cclxuXHJcblxyXG4vKipcclxuICogVG9nZ2xlIHNpZGUgYnkgc2lkZSBwcmV2aWV3XHJcbiAqL1xyXG5mdW5jdGlvbiB0b2dnbGVTaWRlQnlTaWRlKGVkaXRvcikge1xyXG5cdHZhciBjbSA9IGVkaXRvci5jb2RlbWlycm9yO1xyXG5cdHZhciB3cmFwcGVyID0gY20uZ2V0V3JhcHBlckVsZW1lbnQoKTtcclxuXHR2YXIgcHJldmlldyA9IHdyYXBwZXIubmV4dFNpYmxpbmc7XHJcblx0dmFyIHRvb2xiYXJCdXR0b24gPSBlZGl0b3IudG9vbGJhckVsZW1lbnRzW1wic2lkZS1ieS1zaWRlXCJdO1xyXG5cclxuXHRpZigvZWRpdG9yLXByZXZpZXctYWN0aXZlLXNpZGUvLnRlc3QocHJldmlldy5jbGFzc05hbWUpKSB7XHJcblx0XHRwcmV2aWV3LmNsYXNzTmFtZSA9IHByZXZpZXcuY2xhc3NOYW1lLnJlcGxhY2UoXHJcblx0XHRcdC9cXHMqZWRpdG9yLXByZXZpZXctYWN0aXZlLXNpZGVcXHMqL2csIFwiXCJcclxuXHRcdCk7XHJcblx0XHR0b29sYmFyQnV0dG9uLmNsYXNzTmFtZSA9IHRvb2xiYXJCdXR0b24uY2xhc3NOYW1lLnJlcGxhY2UoL1xccyphY3RpdmVcXHMqL2csIFwiXCIpO1xyXG5cdFx0d3JhcHBlci5jbGFzc05hbWUgPSB3cmFwcGVyLmNsYXNzTmFtZS5yZXBsYWNlKC9cXHMqQ29kZU1pcnJvci1zaWRlZFxccyovZywgXCIgXCIpO1xyXG5cdH0gZWxzZSB7XHJcblx0XHQvKiBXaGVuIHRoZSBwcmV2aWV3IGJ1dHRvbiBpcyBjbGlja2VkIGZvciB0aGUgZmlyc3QgdGltZSxcclxuXHRcdCAqIGdpdmUgc29tZSB0aW1lIGZvciB0aGUgdHJhbnNpdGlvbiBmcm9tIGVkaXRvci5jc3MgdG8gZmlyZSBhbmQgdGhlIHZpZXcgdG8gc2xpZGUgZnJvbSByaWdodCB0byBsZWZ0LFxyXG5cdFx0ICogaW5zdGVhZCBvZiBqdXN0IGFwcGVhcmluZy5cclxuXHRcdCAqL1xyXG5cdFx0c2V0VGltZW91dChmdW5jdGlvbigpIHtcclxuXHRcdFx0aWYoIWNtLmdldE9wdGlvbihcImZ1bGxTY3JlZW5cIikpXHJcblx0XHRcdFx0dG9nZ2xlRnVsbFNjcmVlbihlZGl0b3IpO1xyXG5cdFx0XHRwcmV2aWV3LmNsYXNzTmFtZSArPSBcIiBlZGl0b3ItcHJldmlldy1hY3RpdmUtc2lkZVwiO1xyXG5cdFx0fSwgMSk7XHJcblx0XHR0b29sYmFyQnV0dG9uLmNsYXNzTmFtZSArPSBcIiBhY3RpdmVcIjtcclxuXHRcdHdyYXBwZXIuY2xhc3NOYW1lICs9IFwiIENvZGVNaXJyb3Itc2lkZWRcIjtcclxuXHR9XHJcblxyXG5cdC8vIEhpZGUgbm9ybWFsIHByZXZpZXcgaWYgYWN0aXZlXHJcblx0dmFyIHByZXZpZXdOb3JtYWwgPSB3cmFwcGVyLmxhc3RDaGlsZDtcclxuXHRpZigvZWRpdG9yLXByZXZpZXctYWN0aXZlLy50ZXN0KHByZXZpZXdOb3JtYWwuY2xhc3NOYW1lKSkge1xyXG5cdFx0cHJldmlld05vcm1hbC5jbGFzc05hbWUgPSBwcmV2aWV3Tm9ybWFsLmNsYXNzTmFtZS5yZXBsYWNlKFxyXG5cdFx0XHQvXFxzKmVkaXRvci1wcmV2aWV3LWFjdGl2ZVxccyovZywgXCJcIlxyXG5cdFx0KTtcclxuXHRcdHZhciB0b29sYmFyID0gZWRpdG9yLnRvb2xiYXJFbGVtZW50cy5wcmV2aWV3O1xyXG5cdFx0dmFyIHRvb2xiYXJfZGl2ID0gd3JhcHBlci5wcmV2aW91c1NpYmxpbmc7XHJcblx0XHR0b29sYmFyLmNsYXNzTmFtZSA9IHRvb2xiYXIuY2xhc3NOYW1lLnJlcGxhY2UoL1xccyphY3RpdmVcXHMqL2csIFwiXCIpO1xyXG5cdFx0dG9vbGJhcl9kaXYuY2xhc3NOYW1lID0gdG9vbGJhcl9kaXYuY2xhc3NOYW1lLnJlcGxhY2UoL1xccypkaXNhYmxlZC1mb3ItcHJldmlldyovZywgXCJcIik7XHJcblx0fVxyXG5cclxuXHQvLyBTdGFydCBwcmV2aWV3IHdpdGggdGhlIGN1cnJlbnQgdGV4dFxyXG5cdHByZXZpZXcuaW5uZXJIVE1MID0gZWRpdG9yLm9wdGlvbnMucHJldmlld1JlbmRlcihlZGl0b3IudmFsdWUoKSwgcHJldmlldyk7XHJcblxyXG5cdC8vIFVwZGF0ZXMgcHJldmlld1xyXG5cdGNtLm9uKFwidXBkYXRlXCIsIGZ1bmN0aW9uKCkge1xyXG5cdFx0cHJldmlldy5pbm5lckhUTUwgPSBlZGl0b3Iub3B0aW9ucy5wcmV2aWV3UmVuZGVyKGVkaXRvci52YWx1ZSgpLCBwcmV2aWV3KTtcclxuXHR9KTtcclxufVxyXG5cclxuXHJcbi8qKlxyXG4gKiBQcmV2aWV3IGFjdGlvbi5cclxuICovXHJcbmZ1bmN0aW9uIHRvZ2dsZVByZXZpZXcoZWRpdG9yKSB7XHJcblx0dmFyIGNtID0gZWRpdG9yLmNvZGVtaXJyb3I7XHJcblx0dmFyIHdyYXBwZXIgPSBjbS5nZXRXcmFwcGVyRWxlbWVudCgpO1xyXG5cdHZhciB0b29sYmFyX2RpdiA9IHdyYXBwZXIucHJldmlvdXNTaWJsaW5nO1xyXG5cdHZhciB0b29sYmFyID0gZWRpdG9yLnRvb2xiYXJFbGVtZW50cy5wcmV2aWV3O1xyXG5cdHZhciBwcmV2aWV3ID0gd3JhcHBlci5sYXN0Q2hpbGQ7XHJcblx0aWYoIXByZXZpZXcgfHwgIS9lZGl0b3ItcHJldmlldy8udGVzdChwcmV2aWV3LmNsYXNzTmFtZSkpIHtcclxuXHRcdHByZXZpZXcgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwiZGl2XCIpO1xyXG5cdFx0cHJldmlldy5jbGFzc05hbWUgPSBcImVkaXRvci1wcmV2aWV3XCI7XHJcblx0XHR3cmFwcGVyLmFwcGVuZENoaWxkKHByZXZpZXcpO1xyXG5cdH1cclxuXHRpZigvZWRpdG9yLXByZXZpZXctYWN0aXZlLy50ZXN0KHByZXZpZXcuY2xhc3NOYW1lKSkge1xyXG5cdFx0cHJldmlldy5jbGFzc05hbWUgPSBwcmV2aWV3LmNsYXNzTmFtZS5yZXBsYWNlKFxyXG5cdFx0XHQvXFxzKmVkaXRvci1wcmV2aWV3LWFjdGl2ZVxccyovZywgXCJcIlxyXG5cdFx0KTtcclxuXHRcdHRvb2xiYXIuY2xhc3NOYW1lID0gdG9vbGJhci5jbGFzc05hbWUucmVwbGFjZSgvXFxzKmFjdGl2ZVxccyovZywgXCJcIik7XHJcblx0XHR0b29sYmFyX2Rpdi5jbGFzc05hbWUgPSB0b29sYmFyX2Rpdi5jbGFzc05hbWUucmVwbGFjZSgvXFxzKmRpc2FibGVkLWZvci1wcmV2aWV3Ki9nLCBcIlwiKTtcclxuXHR9IGVsc2Uge1xyXG5cdFx0LyogV2hlbiB0aGUgcHJldmlldyBidXR0b24gaXMgY2xpY2tlZCBmb3IgdGhlIGZpcnN0IHRpbWUsXHJcblx0XHQgKiBnaXZlIHNvbWUgdGltZSBmb3IgdGhlIHRyYW5zaXRpb24gZnJvbSBlZGl0b3IuY3NzIHRvIGZpcmUgYW5kIHRoZSB2aWV3IHRvIHNsaWRlIGZyb20gcmlnaHQgdG8gbGVmdCxcclxuXHRcdCAqIGluc3RlYWQgb2YganVzdCBhcHBlYXJpbmcuXHJcblx0XHQgKi9cclxuXHRcdHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XHJcblx0XHRcdHByZXZpZXcuY2xhc3NOYW1lICs9IFwiIGVkaXRvci1wcmV2aWV3LWFjdGl2ZVwiO1xyXG5cdFx0fSwgMSk7XHJcblx0XHR0b29sYmFyLmNsYXNzTmFtZSArPSBcIiBhY3RpdmVcIjtcclxuXHRcdHRvb2xiYXJfZGl2LmNsYXNzTmFtZSArPSBcIiBkaXNhYmxlZC1mb3ItcHJldmlld1wiO1xyXG5cdH1cclxuXHRwcmV2aWV3LmlubmVySFRNTCA9IGVkaXRvci5vcHRpb25zLnByZXZpZXdSZW5kZXIoZWRpdG9yLnZhbHVlKCksIHByZXZpZXcpO1xyXG5cclxuXHQvLyBUdXJuIG9mZiBzaWRlIGJ5IHNpZGUgaWYgbmVlZGVkXHJcblx0dmFyIHNpZGVieXNpZGUgPSBjbS5nZXRXcmFwcGVyRWxlbWVudCgpLm5leHRTaWJsaW5nO1xyXG5cdGlmKC9lZGl0b3ItcHJldmlldy1hY3RpdmUtc2lkZS8udGVzdChzaWRlYnlzaWRlLmNsYXNzTmFtZSkpXHJcblx0XHR0b2dnbGVTaWRlQnlTaWRlKGVkaXRvcik7XHJcbn1cclxuXHJcbmZ1bmN0aW9uIF9yZXBsYWNlU2VsZWN0aW9uKGNtLCBhY3RpdmUsIHN0YXJ0LCBlbmQpIHtcclxuXHRpZigvZWRpdG9yLXByZXZpZXctYWN0aXZlLy50ZXN0KGNtLmdldFdyYXBwZXJFbGVtZW50KCkubGFzdENoaWxkLmNsYXNzTmFtZSkpXHJcblx0XHRyZXR1cm47XHJcblxyXG5cdHZhciB0ZXh0O1xyXG5cdHZhciBzdGFydFBvaW50ID0gY20uZ2V0Q3Vyc29yKFwic3RhcnRcIik7XHJcblx0dmFyIGVuZFBvaW50ID0gY20uZ2V0Q3Vyc29yKFwiZW5kXCIpO1xyXG5cdGlmKGFjdGl2ZSkge1xyXG5cdFx0dGV4dCA9IGNtLmdldExpbmUoc3RhcnRQb2ludC5saW5lKTtcclxuXHRcdHN0YXJ0ID0gdGV4dC5zbGljZSgwLCBzdGFydFBvaW50LmNoKTtcclxuXHRcdGVuZCA9IHRleHQuc2xpY2Uoc3RhcnRQb2ludC5jaCk7XHJcblx0XHRjbS5yZXBsYWNlUmFuZ2Uoc3RhcnQgKyBlbmQsIHtcclxuXHRcdFx0bGluZTogc3RhcnRQb2ludC5saW5lLFxyXG5cdFx0XHRjaDogMFxyXG5cdFx0fSk7XHJcblx0fSBlbHNlIHtcclxuXHRcdHRleHQgPSBjbS5nZXRTZWxlY3Rpb24oKTtcclxuXHRcdGNtLnJlcGxhY2VTZWxlY3Rpb24oc3RhcnQgKyB0ZXh0ICsgZW5kKTtcclxuXHJcblx0XHRzdGFydFBvaW50LmNoICs9IHN0YXJ0Lmxlbmd0aDtcclxuXHRcdGlmKHN0YXJ0UG9pbnQgIT09IGVuZFBvaW50KSB7XHJcblx0XHRcdGVuZFBvaW50LmNoICs9IHN0YXJ0Lmxlbmd0aDtcclxuXHRcdH1cclxuXHR9XHJcblx0Y20uc2V0U2VsZWN0aW9uKHN0YXJ0UG9pbnQsIGVuZFBvaW50KTtcclxuXHRjbS5mb2N1cygpO1xyXG59XHJcblxyXG5cclxuZnVuY3Rpb24gX3RvZ2dsZUhlYWRpbmcoY20sIGRpcmVjdGlvbiwgc2l6ZSkge1xyXG5cdGlmKC9lZGl0b3ItcHJldmlldy1hY3RpdmUvLnRlc3QoY20uZ2V0V3JhcHBlckVsZW1lbnQoKS5sYXN0Q2hpbGQuY2xhc3NOYW1lKSlcclxuXHRcdHJldHVybjtcclxuXHJcblx0dmFyIHN0YXJ0UG9pbnQgPSBjbS5nZXRDdXJzb3IoXCJzdGFydFwiKTtcclxuXHR2YXIgZW5kUG9pbnQgPSBjbS5nZXRDdXJzb3IoXCJlbmRcIik7XHJcblx0Zm9yKHZhciBpID0gc3RhcnRQb2ludC5saW5lOyBpIDw9IGVuZFBvaW50LmxpbmU7IGkrKykge1xyXG5cdFx0KGZ1bmN0aW9uKGkpIHtcclxuXHRcdFx0dmFyIHRleHQgPSBjbS5nZXRMaW5lKGkpO1xyXG5cdFx0XHR2YXIgY3VyckhlYWRpbmdMZXZlbCA9IHRleHQuc2VhcmNoKC9bXiNdLyk7XHJcblxyXG5cdFx0XHRpZihkaXJlY3Rpb24gIT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHRcdGlmKGN1cnJIZWFkaW5nTGV2ZWwgPD0gMCkge1xyXG5cdFx0XHRcdFx0aWYoZGlyZWN0aW9uID09IFwiYmlnZ2VyXCIpIHtcclxuXHRcdFx0XHRcdFx0dGV4dCA9IFwiIyMjIyMjIFwiICsgdGV4dDtcclxuXHRcdFx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0XHRcdHRleHQgPSBcIiMgXCIgKyB0ZXh0O1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH0gZWxzZSBpZihjdXJySGVhZGluZ0xldmVsID09IDYgJiYgZGlyZWN0aW9uID09IFwic21hbGxlclwiKSB7XHJcblx0XHRcdFx0XHR0ZXh0ID0gdGV4dC5zdWJzdHIoNyk7XHJcblx0XHRcdFx0fSBlbHNlIGlmKGN1cnJIZWFkaW5nTGV2ZWwgPT0gMSAmJiBkaXJlY3Rpb24gPT0gXCJiaWdnZXJcIikge1xyXG5cdFx0XHRcdFx0dGV4dCA9IHRleHQuc3Vic3RyKDIpO1xyXG5cdFx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0XHRpZihkaXJlY3Rpb24gPT0gXCJiaWdnZXJcIikge1xyXG5cdFx0XHRcdFx0XHR0ZXh0ID0gdGV4dC5zdWJzdHIoMSk7XHJcblx0XHRcdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdFx0XHR0ZXh0ID0gXCIjXCIgKyB0ZXh0O1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH1cclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHRpZihzaXplID09IDEpIHtcclxuXHRcdFx0XHRcdGlmKGN1cnJIZWFkaW5nTGV2ZWwgPD0gMCkge1xyXG5cdFx0XHRcdFx0XHR0ZXh0ID0gXCIjIFwiICsgdGV4dDtcclxuXHRcdFx0XHRcdH0gZWxzZSBpZihjdXJySGVhZGluZ0xldmVsID09IHNpemUpIHtcclxuXHRcdFx0XHRcdFx0dGV4dCA9IHRleHQuc3Vic3RyKGN1cnJIZWFkaW5nTGV2ZWwgKyAxKTtcclxuXHRcdFx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0XHRcdHRleHQgPSBcIiMgXCIgKyB0ZXh0LnN1YnN0cihjdXJySGVhZGluZ0xldmVsICsgMSk7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0fSBlbHNlIGlmKHNpemUgPT0gMikge1xyXG5cdFx0XHRcdFx0aWYoY3VyckhlYWRpbmdMZXZlbCA8PSAwKSB7XHJcblx0XHRcdFx0XHRcdHRleHQgPSBcIiMjIFwiICsgdGV4dDtcclxuXHRcdFx0XHRcdH0gZWxzZSBpZihjdXJySGVhZGluZ0xldmVsID09IHNpemUpIHtcclxuXHRcdFx0XHRcdFx0dGV4dCA9IHRleHQuc3Vic3RyKGN1cnJIZWFkaW5nTGV2ZWwgKyAxKTtcclxuXHRcdFx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0XHRcdHRleHQgPSBcIiMjIFwiICsgdGV4dC5zdWJzdHIoY3VyckhlYWRpbmdMZXZlbCArIDEpO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0XHRpZihjdXJySGVhZGluZ0xldmVsIDw9IDApIHtcclxuXHRcdFx0XHRcdFx0dGV4dCA9IFwiIyMjIFwiICsgdGV4dDtcclxuXHRcdFx0XHRcdH0gZWxzZSBpZihjdXJySGVhZGluZ0xldmVsID09IHNpemUpIHtcclxuXHRcdFx0XHRcdFx0dGV4dCA9IHRleHQuc3Vic3RyKGN1cnJIZWFkaW5nTGV2ZWwgKyAxKTtcclxuXHRcdFx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0XHRcdHRleHQgPSBcIiMjIyBcIiArIHRleHQuc3Vic3RyKGN1cnJIZWFkaW5nTGV2ZWwgKyAxKTtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdGNtLnJlcGxhY2VSYW5nZSh0ZXh0LCB7XHJcblx0XHRcdFx0bGluZTogaSxcclxuXHRcdFx0XHRjaDogMFxyXG5cdFx0XHR9LCB7XHJcblx0XHRcdFx0bGluZTogaSxcclxuXHRcdFx0XHRjaDogOTk5OTk5OTk5OTk5OTlcclxuXHRcdFx0fSk7XHJcblx0XHR9KShpKTtcclxuXHR9XHJcblx0Y20uZm9jdXMoKTtcclxufVxyXG5cclxuXHJcbmZ1bmN0aW9uIF90b2dnbGVMaW5lKGNtLCBuYW1lKSB7XHJcblx0aWYoL2VkaXRvci1wcmV2aWV3LWFjdGl2ZS8udGVzdChjbS5nZXRXcmFwcGVyRWxlbWVudCgpLmxhc3RDaGlsZC5jbGFzc05hbWUpKVxyXG5cdFx0cmV0dXJuO1xyXG5cclxuXHR2YXIgc3RhdCA9IGdldFN0YXRlKGNtKTtcclxuXHR2YXIgc3RhcnRQb2ludCA9IGNtLmdldEN1cnNvcihcInN0YXJ0XCIpO1xyXG5cdHZhciBlbmRQb2ludCA9IGNtLmdldEN1cnNvcihcImVuZFwiKTtcclxuXHR2YXIgcmVwbCA9IHtcclxuXHRcdFwicXVvdGVcIjogL14oXFxzKilcXD5cXHMrLyxcclxuXHRcdFwidW5vcmRlcmVkLWxpc3RcIjogL14oXFxzKikoXFwqfFxcLXxcXCspXFxzKy8sXHJcblx0XHRcIm9yZGVyZWQtbGlzdFwiOiAvXihcXHMqKVxcZCtcXC5cXHMrL1xyXG5cdH07XHJcblx0dmFyIG1hcCA9IHtcclxuXHRcdFwicXVvdGVcIjogXCI+IFwiLFxyXG5cdFx0XCJ1bm9yZGVyZWQtbGlzdFwiOiBcIiogXCIsXHJcblx0XHRcIm9yZGVyZWQtbGlzdFwiOiBcIjEuIFwiXHJcblx0fTtcclxuXHRmb3IodmFyIGkgPSBzdGFydFBvaW50LmxpbmU7IGkgPD0gZW5kUG9pbnQubGluZTsgaSsrKSB7XHJcblx0XHQoZnVuY3Rpb24oaSkge1xyXG5cdFx0XHR2YXIgdGV4dCA9IGNtLmdldExpbmUoaSk7XHJcblx0XHRcdGlmKHN0YXRbbmFtZV0pIHtcclxuXHRcdFx0XHR0ZXh0ID0gdGV4dC5yZXBsYWNlKHJlcGxbbmFtZV0sIFwiJDFcIik7XHJcblx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0dGV4dCA9IG1hcFtuYW1lXSArIHRleHQ7XHJcblx0XHRcdH1cclxuXHRcdFx0Y20ucmVwbGFjZVJhbmdlKHRleHQsIHtcclxuXHRcdFx0XHRsaW5lOiBpLFxyXG5cdFx0XHRcdGNoOiAwXHJcblx0XHRcdH0sIHtcclxuXHRcdFx0XHRsaW5lOiBpLFxyXG5cdFx0XHRcdGNoOiA5OTk5OTk5OTk5OTk5OVxyXG5cdFx0XHR9KTtcclxuXHRcdH0pKGkpO1xyXG5cdH1cclxuXHRjbS5mb2N1cygpO1xyXG59XHJcblxyXG5mdW5jdGlvbiBfdG9nZ2xlQmxvY2soZWRpdG9yLCB0eXBlLCBzdGFydF9jaGFycywgZW5kX2NoYXJzKSB7XHJcblx0aWYoL2VkaXRvci1wcmV2aWV3LWFjdGl2ZS8udGVzdChlZGl0b3IuY29kZW1pcnJvci5nZXRXcmFwcGVyRWxlbWVudCgpLmxhc3RDaGlsZC5jbGFzc05hbWUpKVxyXG5cdFx0cmV0dXJuO1xyXG5cclxuXHRlbmRfY2hhcnMgPSAodHlwZW9mIGVuZF9jaGFycyA9PT0gXCJ1bmRlZmluZWRcIikgPyBzdGFydF9jaGFycyA6IGVuZF9jaGFycztcclxuXHR2YXIgY20gPSBlZGl0b3IuY29kZW1pcnJvcjtcclxuXHR2YXIgc3RhdCA9IGdldFN0YXRlKGNtKTtcclxuXHJcblx0dmFyIHRleHQ7XHJcblx0dmFyIHN0YXJ0ID0gc3RhcnRfY2hhcnM7XHJcblx0dmFyIGVuZCA9IGVuZF9jaGFycztcclxuXHJcblx0dmFyIHN0YXJ0UG9pbnQgPSBjbS5nZXRDdXJzb3IoXCJzdGFydFwiKTtcclxuXHR2YXIgZW5kUG9pbnQgPSBjbS5nZXRDdXJzb3IoXCJlbmRcIik7XHJcblxyXG5cdGlmKHN0YXRbdHlwZV0pIHtcclxuXHRcdHRleHQgPSBjbS5nZXRMaW5lKHN0YXJ0UG9pbnQubGluZSk7XHJcblx0XHRzdGFydCA9IHRleHQuc2xpY2UoMCwgc3RhcnRQb2ludC5jaCk7XHJcblx0XHRlbmQgPSB0ZXh0LnNsaWNlKHN0YXJ0UG9pbnQuY2gpO1xyXG5cdFx0aWYodHlwZSA9PSBcImJvbGRcIikge1xyXG5cdFx0XHRzdGFydCA9IHN0YXJ0LnJlcGxhY2UoLyhcXCpcXCp8X18pKD8hW1xcc1xcU10qKFxcKlxcKnxfXykpLywgXCJcIik7XHJcblx0XHRcdGVuZCA9IGVuZC5yZXBsYWNlKC8oXFwqXFwqfF9fKS8sIFwiXCIpO1xyXG5cdFx0fSBlbHNlIGlmKHR5cGUgPT0gXCJpdGFsaWNcIikge1xyXG5cdFx0XHRzdGFydCA9IHN0YXJ0LnJlcGxhY2UoLyhcXCp8XykoPyFbXFxzXFxTXSooXFwqfF8pKS8sIFwiXCIpO1xyXG5cdFx0XHRlbmQgPSBlbmQucmVwbGFjZSgvKFxcKnxfKS8sIFwiXCIpO1xyXG5cdFx0fSBlbHNlIGlmKHR5cGUgPT0gXCJzdHJpa2V0aHJvdWdoXCIpIHtcclxuXHRcdFx0c3RhcnQgPSBzdGFydC5yZXBsYWNlKC8oXFwqXFwqfH5+KSg/IVtcXHNcXFNdKihcXCpcXCp8fn4pKS8sIFwiXCIpO1xyXG5cdFx0XHRlbmQgPSBlbmQucmVwbGFjZSgvKFxcKlxcKnx+fikvLCBcIlwiKTtcclxuXHRcdH1cclxuXHRcdGNtLnJlcGxhY2VSYW5nZShzdGFydCArIGVuZCwge1xyXG5cdFx0XHRsaW5lOiBzdGFydFBvaW50LmxpbmUsXHJcblx0XHRcdGNoOiAwXHJcblx0XHR9LCB7XHJcblx0XHRcdGxpbmU6IHN0YXJ0UG9pbnQubGluZSxcclxuXHRcdFx0Y2g6IDk5OTk5OTk5OTk5OTk5XHJcblx0XHR9KTtcclxuXHJcblx0XHRpZih0eXBlID09IFwiYm9sZFwiIHx8IHR5cGUgPT0gXCJzdHJpa2V0aHJvdWdoXCIpIHtcclxuXHRcdFx0c3RhcnRQb2ludC5jaCAtPSAyO1xyXG5cdFx0XHRpZihzdGFydFBvaW50ICE9PSBlbmRQb2ludCkge1xyXG5cdFx0XHRcdGVuZFBvaW50LmNoIC09IDI7XHJcblx0XHRcdH1cclxuXHRcdH0gZWxzZSBpZih0eXBlID09IFwiaXRhbGljXCIpIHtcclxuXHRcdFx0c3RhcnRQb2ludC5jaCAtPSAxO1xyXG5cdFx0XHRpZihzdGFydFBvaW50ICE9PSBlbmRQb2ludCkge1xyXG5cdFx0XHRcdGVuZFBvaW50LmNoIC09IDE7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHR9IGVsc2Uge1xyXG5cdFx0dGV4dCA9IGNtLmdldFNlbGVjdGlvbigpO1xyXG5cdFx0aWYodHlwZSA9PSBcImJvbGRcIikge1xyXG5cdFx0XHR0ZXh0ID0gdGV4dC5zcGxpdChcIioqXCIpLmpvaW4oXCJcIik7XHJcblx0XHRcdHRleHQgPSB0ZXh0LnNwbGl0KFwiX19cIikuam9pbihcIlwiKTtcclxuXHRcdH0gZWxzZSBpZih0eXBlID09IFwiaXRhbGljXCIpIHtcclxuXHRcdFx0dGV4dCA9IHRleHQuc3BsaXQoXCIqXCIpLmpvaW4oXCJcIik7XHJcblx0XHRcdHRleHQgPSB0ZXh0LnNwbGl0KFwiX1wiKS5qb2luKFwiXCIpO1xyXG5cdFx0fSBlbHNlIGlmKHR5cGUgPT0gXCJzdHJpa2V0aHJvdWdoXCIpIHtcclxuXHRcdFx0dGV4dCA9IHRleHQuc3BsaXQoXCJ+flwiKS5qb2luKFwiXCIpO1xyXG5cdFx0fVxyXG5cdFx0Y20ucmVwbGFjZVNlbGVjdGlvbihzdGFydCArIHRleHQgKyBlbmQpO1xyXG5cclxuXHRcdHN0YXJ0UG9pbnQuY2ggKz0gc3RhcnRfY2hhcnMubGVuZ3RoO1xyXG5cdFx0ZW5kUG9pbnQuY2ggPSBzdGFydFBvaW50LmNoICsgdGV4dC5sZW5ndGg7XHJcblx0fVxyXG5cclxuXHRjbS5zZXRTZWxlY3Rpb24oc3RhcnRQb2ludCwgZW5kUG9pbnQpO1xyXG5cdGNtLmZvY3VzKCk7XHJcbn1cclxuXHJcblxyXG4vKiBUaGUgcmlnaHQgd29yZCBjb3VudCBpbiByZXNwZWN0IGZvciBDSksuICovXHJcbmZ1bmN0aW9uIHdvcmRDb3VudChkYXRhKSB7XHJcblx0dmFyIHBhdHRlcm4gPSAvW2EtekEtWjAtOV9cXHUwMzkyLVxcdTAzYzldK3xbXFx1NEUwMC1cXHU5RkZGXFx1MzQwMC1cXHU0ZGJmXFx1ZjkwMC1cXHVmYWZmXFx1MzA0MC1cXHUzMDlmXFx1YWMwMC1cXHVkN2FmXSsvZztcclxuXHR2YXIgbSA9IGRhdGEubWF0Y2gocGF0dGVybik7XHJcblx0dmFyIGNvdW50ID0gMDtcclxuXHRpZihtID09PSBudWxsKSByZXR1cm4gY291bnQ7XHJcblx0Zm9yKHZhciBpID0gMDsgaSA8IG0ubGVuZ3RoOyBpKyspIHtcclxuXHRcdGlmKG1baV0uY2hhckNvZGVBdCgwKSA+PSAweDRFMDApIHtcclxuXHRcdFx0Y291bnQgKz0gbVtpXS5sZW5ndGg7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRjb3VudCArPSAxO1xyXG5cdFx0fVxyXG5cdH1cclxuXHRyZXR1cm4gY291bnQ7XHJcbn1cclxuXHJcblxyXG52YXIgdG9vbGJhckJ1aWx0SW5CdXR0b25zID0ge1xyXG5cdFwiYm9sZFwiOiB7XHJcblx0XHRuYW1lOiBcImJvbGRcIixcclxuXHRcdGFjdGlvbjogdG9nZ2xlQm9sZCxcclxuXHRcdGNsYXNzTmFtZTogXCJmYSBmYS1ib2xkXCIsXHJcblx0XHR0aXRsZTogXCJCb2xkIChDdHJsK0IpXCJcclxuXHR9LFxyXG5cdFwiaXRhbGljXCI6IHtcclxuXHRcdG5hbWU6IFwiaXRhbGljXCIsXHJcblx0XHRhY3Rpb246IHRvZ2dsZUl0YWxpYyxcclxuXHRcdGNsYXNzTmFtZTogXCJmYSBmYS1pdGFsaWNcIixcclxuXHRcdHRpdGxlOiBcIkl0YWxpYyAoQ3RybCtJKVwiXHJcblx0fSxcclxuXHRcInN0cmlrZXRocm91Z2hcIjoge1xyXG5cdFx0bmFtZTogXCJzdHJpa2V0aHJvdWdoXCIsXHJcblx0XHRhY3Rpb246IHRvZ2dsZVN0cmlrZXRocm91Z2gsXHJcblx0XHRjbGFzc05hbWU6IFwiZmEgZmEtc3RyaWtldGhyb3VnaFwiLFxyXG5cdFx0dGl0bGU6IFwiU3RyaWtldGhyb3VnaFwiXHJcblx0fSxcclxuXHRcImhlYWRpbmdcIjoge1xyXG5cdFx0bmFtZTogXCJoZWFkaW5nXCIsXHJcblx0XHRhY3Rpb246IHRvZ2dsZUhlYWRpbmdTbWFsbGVyLFxyXG5cdFx0Y2xhc3NOYW1lOiBcImZhIGZhLWhlYWRlclwiLFxyXG5cdFx0dGl0bGU6IFwiSGVhZGluZyAoQ3RybCtIKVwiXHJcblx0fSxcclxuXHRcImhlYWRpbmctc21hbGxlclwiOiB7XHJcblx0XHRuYW1lOiBcImhlYWRpbmctc21hbGxlclwiLFxyXG5cdFx0YWN0aW9uOiB0b2dnbGVIZWFkaW5nU21hbGxlcixcclxuXHRcdGNsYXNzTmFtZTogXCJmYSBmYS1oZWFkZXIgZmEtaGVhZGVyLXggZmEtaGVhZGVyLXNtYWxsZXJcIixcclxuXHRcdHRpdGxlOiBcIlNtYWxsZXIgSGVhZGluZyAoQ3RybCtIKVwiXHJcblx0fSxcclxuXHRcImhlYWRpbmctYmlnZ2VyXCI6IHtcclxuXHRcdG5hbWU6IFwiaGVhZGluZy1iaWdnZXJcIixcclxuXHRcdGFjdGlvbjogdG9nZ2xlSGVhZGluZ0JpZ2dlcixcclxuXHRcdGNsYXNzTmFtZTogXCJmYSBmYS1oZWFkZXIgZmEtaGVhZGVyLXggZmEtaGVhZGVyLWJpZ2dlclwiLFxyXG5cdFx0dGl0bGU6IFwiQmlnZ2VyIEhlYWRpbmcgKFNoaWZ0K0N0cmwrSClcIlxyXG5cdH0sXHJcblx0XCJoZWFkaW5nLTFcIjoge1xyXG5cdFx0bmFtZTogXCJoZWFkaW5nLTFcIixcclxuXHRcdGFjdGlvbjogdG9nZ2xlSGVhZGluZzEsXHJcblx0XHRjbGFzc05hbWU6IFwiZmEgZmEtaGVhZGVyIGZhLWhlYWRlci14IGZhLWhlYWRlci0xXCIsXHJcblx0XHR0aXRsZTogXCJCaWcgSGVhZGluZ1wiXHJcblx0fSxcclxuXHRcImhlYWRpbmctMlwiOiB7XHJcblx0XHRuYW1lOiBcImhlYWRpbmctMlwiLFxyXG5cdFx0YWN0aW9uOiB0b2dnbGVIZWFkaW5nMixcclxuXHRcdGNsYXNzTmFtZTogXCJmYSBmYS1oZWFkZXIgZmEtaGVhZGVyLXggZmEtaGVhZGVyLTJcIixcclxuXHRcdHRpdGxlOiBcIk1lZGl1bSBIZWFkaW5nXCJcclxuXHR9LFxyXG5cdFwiaGVhZGluZy0zXCI6IHtcclxuXHRcdG5hbWU6IFwiaGVhZGluZy0zXCIsXHJcblx0XHRhY3Rpb246IHRvZ2dsZUhlYWRpbmczLFxyXG5cdFx0Y2xhc3NOYW1lOiBcImZhIGZhLWhlYWRlciBmYS1oZWFkZXIteCBmYS1oZWFkZXItM1wiLFxyXG5cdFx0dGl0bGU6IFwiU21hbGwgSGVhZGluZ1wiXHJcblx0fSxcclxuXHRcImNvZGVcIjoge1xyXG5cdFx0bmFtZTogXCJjb2RlXCIsXHJcblx0XHRhY3Rpb246IHRvZ2dsZUNvZGVCbG9jayxcclxuXHRcdGNsYXNzTmFtZTogXCJmYSBmYS1jb2RlXCIsXHJcblx0XHR0aXRsZTogXCJDb2RlIChDdHJsK0FsdCtDKVwiXHJcblx0fSxcclxuXHRcInF1b3RlXCI6IHtcclxuXHRcdG5hbWU6IFwicXVvdGVcIixcclxuXHRcdGFjdGlvbjogdG9nZ2xlQmxvY2txdW90ZSxcclxuXHRcdGNsYXNzTmFtZTogXCJmYSBmYS1xdW90ZS1sZWZ0XCIsXHJcblx0XHR0aXRsZTogXCJRdW90ZSAoQ3RybCsnKVwiXHJcblx0fSxcclxuXHRcInVub3JkZXJlZC1saXN0XCI6IHtcclxuXHRcdG5hbWU6IFwidW5vcmRlcmVkLWxpc3RcIixcclxuXHRcdGFjdGlvbjogdG9nZ2xlVW5vcmRlcmVkTGlzdCxcclxuXHRcdGNsYXNzTmFtZTogXCJmYSBmYS1saXN0LXVsXCIsXHJcblx0XHR0aXRsZTogXCJHZW5lcmljIExpc3QgKEN0cmwrTClcIlxyXG5cdH0sXHJcblx0XCJvcmRlcmVkLWxpc3RcIjoge1xyXG5cdFx0bmFtZTogXCJvcmRlcmVkLWxpc3RcIixcclxuXHRcdGFjdGlvbjogdG9nZ2xlT3JkZXJlZExpc3QsXHJcblx0XHRjbGFzc05hbWU6IFwiZmEgZmEtbGlzdC1vbFwiLFxyXG5cdFx0dGl0bGU6IFwiTnVtYmVyZWQgTGlzdCAoQ3RybCtBbHQrTClcIlxyXG5cdH0sXHJcblx0XCJsaW5rXCI6IHtcclxuXHRcdG5hbWU6IFwibGlua1wiLFxyXG5cdFx0YWN0aW9uOiBkcmF3TGluayxcclxuXHRcdGNsYXNzTmFtZTogXCJmYSBmYS1saW5rXCIsXHJcblx0XHR0aXRsZTogXCJDcmVhdGUgTGluayAoQ3RybCtLKVwiXHJcblx0fSxcclxuXHRcImltYWdlXCI6IHtcclxuXHRcdG5hbWU6IFwiaW1hZ2VcIixcclxuXHRcdGFjdGlvbjogZHJhd0ltYWdlLFxyXG5cdFx0Y2xhc3NOYW1lOiBcImZhIGZhLXBpY3R1cmUtb1wiLFxyXG5cdFx0dGl0bGU6IFwiSW5zZXJ0IEltYWdlIChDdHJsK0FsdCtJKVwiXHJcblx0fSxcclxuXHRcImhvcml6b250YWwtcnVsZVwiOiB7XHJcblx0XHRuYW1lOiBcImhvcml6b250YWwtcnVsZVwiLFxyXG5cdFx0YWN0aW9uOiBkcmF3SG9yaXpvbnRhbFJ1bGUsXHJcblx0XHRjbGFzc05hbWU6IFwiZmEgZmEtbWludXNcIixcclxuXHRcdHRpdGxlOiBcIkluc2VydCBIb3Jpem9udGFsIExpbmVcIlxyXG5cdH0sXHJcblx0XCJwcmV2aWV3XCI6IHtcclxuXHRcdG5hbWU6IFwicHJldmlld1wiLFxyXG5cdFx0YWN0aW9uOiB0b2dnbGVQcmV2aWV3LFxyXG5cdFx0Y2xhc3NOYW1lOiBcImZhIGZhLWV5ZSBuby1kaXNhYmxlXCIsXHJcblx0XHR0aXRsZTogXCJUb2dnbGUgUHJldmlldyAoQ3RybCtQKVwiXHJcblx0fSxcclxuXHRcInNpZGUtYnktc2lkZVwiOiB7XHJcblx0XHRuYW1lOiBcInNpZGUtYnktc2lkZVwiLFxyXG5cdFx0YWN0aW9uOiB0b2dnbGVTaWRlQnlTaWRlLFxyXG5cdFx0Y2xhc3NOYW1lOiBcImZhIGZhLWNvbHVtbnMgbm8tZGlzYWJsZSBuby1tb2JpbGVcIixcclxuXHRcdHRpdGxlOiBcIlRvZ2dsZSBTaWRlIGJ5IFNpZGUgKEY5KVwiXHJcblx0fSxcclxuXHRcImZ1bGxzY3JlZW5cIjoge1xyXG5cdFx0bmFtZTogXCJmdWxsc2NyZWVuXCIsXHJcblx0XHRhY3Rpb246IHRvZ2dsZUZ1bGxTY3JlZW4sXHJcblx0XHRjbGFzc05hbWU6IFwiZmEgZmEtYXJyb3dzLWFsdCBuby1kaXNhYmxlIG5vLW1vYmlsZVwiLFxyXG5cdFx0dGl0bGU6IFwiVG9nZ2xlIEZ1bGxzY3JlZW4gKEYxMSlcIlxyXG5cdH0sXHJcblx0XCJndWlkZVwiOiB7XHJcblx0XHRuYW1lOiBcImd1aWRlXCIsXHJcblx0XHRhY3Rpb246IFwiaHR0cDovL25leHRzdGVwd2Vicy5naXRodWIuaW8vc2ltcGxlbWRlLW1hcmtkb3duLWVkaXRvci9tYXJrZG93bi1ndWlkZVwiLFxyXG5cdFx0Y2xhc3NOYW1lOiBcImZhIGZhLXF1ZXN0aW9uLWNpcmNsZVwiLFxyXG5cdFx0dGl0bGU6IFwiTWFya2Rvd24gR3VpZGVcIlxyXG5cdH1cclxufTtcclxuXHJcblxyXG4vKipcclxuICogSW50ZXJmYWNlIG9mIFNpbXBsZU1ERS5cclxuICovXHJcbmZ1bmN0aW9uIFNpbXBsZU1ERShvcHRpb25zKSB7XHJcblx0Ly8gSGFuZGxlIG9wdGlvbnMgcGFyYW1ldGVyXHJcblx0b3B0aW9ucyA9IG9wdGlvbnMgfHwge307XHJcblxyXG5cclxuXHQvLyBVc2VkIGxhdGVyIHRvIHJlZmVyIHRvIGl0XCJzIHBhcmVudFxyXG5cdG9wdGlvbnMucGFyZW50ID0gdGhpcztcclxuXHJcblxyXG5cdC8vIENoZWNrIGlmIEZvbnQgQXdlc29tZSBuZWVkcyB0byBiZSBhdXRvIGRvd25sb2FkZWRcclxuXHR2YXIgYXV0b0Rvd25sb2FkRkEgPSB0cnVlO1xyXG5cclxuXHRpZihvcHRpb25zLmF1dG9Eb3dubG9hZEZvbnRBd2Vzb21lID09PSBmYWxzZSkge1xyXG5cdFx0YXV0b0Rvd25sb2FkRkEgPSBmYWxzZTtcclxuXHR9XHJcblxyXG5cdGlmKG9wdGlvbnMuYXV0b0Rvd25sb2FkRm9udEF3ZXNvbWUgIT09IHRydWUpIHtcclxuXHRcdHZhciBzdHlsZVNoZWV0cyA9IGRvY3VtZW50LnN0eWxlU2hlZXRzO1xyXG5cdFx0Zm9yKHZhciBpID0gMDsgaSA8IHN0eWxlU2hlZXRzLmxlbmd0aDsgaSsrKSB7XHJcblx0XHRcdGlmKCFzdHlsZVNoZWV0c1tpXS5ocmVmKVxyXG5cdFx0XHRcdGNvbnRpbnVlO1xyXG5cclxuXHRcdFx0aWYoc3R5bGVTaGVldHNbaV0uaHJlZi5pbmRleE9mKFwiLy9tYXhjZG4uYm9vdHN0cmFwY2RuLmNvbS9mb250LWF3ZXNvbWUvXCIpID4gLTEpIHtcclxuXHRcdFx0XHRhdXRvRG93bmxvYWRGQSA9IGZhbHNlO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0fVxyXG5cclxuXHRpZihhdXRvRG93bmxvYWRGQSkge1xyXG5cdFx0dmFyIGxpbmsgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwibGlua1wiKTtcclxuXHRcdGxpbmsucmVsID0gXCJzdHlsZXNoZWV0XCI7XHJcblx0XHRsaW5rLmhyZWYgPSBcImh0dHBzOi8vbWF4Y2RuLmJvb3RzdHJhcGNkbi5jb20vZm9udC1hd2Vzb21lL2xhdGVzdC9jc3MvZm9udC1hd2Vzb21lLm1pbi5jc3NcIjtcclxuXHRcdGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKFwiaGVhZFwiKVswXS5hcHBlbmRDaGlsZChsaW5rKTtcclxuXHR9XHJcblxyXG5cclxuXHQvLyBGaW5kIHRoZSB0ZXh0YXJlYSB0byB1c2VcclxuXHRpZihvcHRpb25zLmVsZW1lbnQpIHtcclxuXHRcdHRoaXMuZWxlbWVudCA9IG9wdGlvbnMuZWxlbWVudDtcclxuXHR9IGVsc2UgaWYob3B0aW9ucy5lbGVtZW50ID09PSBudWxsKSB7XHJcblx0XHQvLyBUaGlzIG1lYW5zIHRoYXQgdGhlIGVsZW1lbnQgb3B0aW9uIHdhcyBzcGVjaWZpZWQsIGJ1dCBubyBlbGVtZW50IHdhcyBmb3VuZFxyXG5cdFx0Y29uc29sZS5sb2coXCJTaW1wbGVNREU6IEVycm9yLiBObyBlbGVtZW50IHdhcyBmb3VuZC5cIik7XHJcblx0XHRyZXR1cm47XHJcblx0fVxyXG5cclxuXHJcblx0Ly8gSGFuZGxlIHRvb2xiYXIgYW5kIHN0YXR1cyBiYXJcclxuXHRpZihvcHRpb25zLnRvb2xiYXIgIT09IGZhbHNlKVxyXG5cdFx0b3B0aW9ucy50b29sYmFyID0gb3B0aW9ucy50b29sYmFyIHx8IFNpbXBsZU1ERS50b29sYmFyO1xyXG5cclxuXHRpZighb3B0aW9ucy5oYXNPd25Qcm9wZXJ0eShcInN0YXR1c1wiKSkge1xyXG5cdFx0b3B0aW9ucy5zdGF0dXMgPSBbXCJhdXRvc2F2ZVwiLCBcImxpbmVzXCIsIFwid29yZHNcIiwgXCJjdXJzb3JcIl07XHJcblx0fVxyXG5cclxuXHJcblx0Ly8gQWRkIGRlZmF1bHQgcHJldmlldyByZW5kZXJpbmcgZnVuY3Rpb25cclxuXHRpZighb3B0aW9ucy5wcmV2aWV3UmVuZGVyKSB7XHJcblx0XHRvcHRpb25zLnByZXZpZXdSZW5kZXIgPSBmdW5jdGlvbihwbGFpblRleHQpIHtcclxuXHRcdFx0Ly8gTm90ZTogXCJ0aGlzXCIgcmVmZXJzIHRvIHRoZSBvcHRpb25zIG9iamVjdFxyXG5cdFx0XHRyZXR1cm4gdGhpcy5wYXJlbnQubWFya2Rvd24ocGxhaW5UZXh0KTtcclxuXHRcdH07XHJcblx0fVxyXG5cclxuXHJcblx0Ly8gU2V0IGRlZmF1bHQgb3B0aW9ucyBmb3IgcGFyc2luZyBjb25maWdcclxuXHRvcHRpb25zLnBhcnNpbmdDb25maWcgPSBvcHRpb25zLnBhcnNpbmdDb25maWcgfHwge307XHJcblxyXG5cclxuXHQvLyBVcGRhdGUgdGhpcyBvcHRpb25zXHJcblx0dGhpcy5vcHRpb25zID0gb3B0aW9ucztcclxuXHJcblxyXG5cdC8vIEF1dG8gcmVuZGVyXHJcblx0dGhpcy5yZW5kZXIoKTtcclxuXHJcblxyXG5cdC8vIFRoZSBjb2RlbWlycm9yIGNvbXBvbmVudCBpcyBvbmx5IGF2YWlsYWJsZSBhZnRlciByZW5kZXJpbmdcclxuXHQvLyBzbywgdGhlIHNldHRlciBmb3IgdGhlIGluaXRpYWxWYWx1ZSBjYW4gb25seSBydW4gYWZ0ZXJcclxuXHQvLyB0aGUgZWxlbWVudCBoYXMgYmVlbiByZW5kZXJlZFxyXG5cdGlmKG9wdGlvbnMuaW5pdGlhbFZhbHVlKSB7XHJcblx0XHR0aGlzLnZhbHVlKG9wdGlvbnMuaW5pdGlhbFZhbHVlKTtcclxuXHR9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBEZWZhdWx0IHRvb2xiYXIgZWxlbWVudHMuXHJcbiAqL1xyXG5TaW1wbGVNREUudG9vbGJhciA9IFtcImJvbGRcIiwgXCJpdGFsaWNcIiwgXCJoZWFkaW5nXCIsIFwifFwiLCBcInF1b3RlXCIsIFwidW5vcmRlcmVkLWxpc3RcIiwgXCJvcmRlcmVkLWxpc3RcIiwgXCJ8XCIsIFwibGlua1wiLCBcImltYWdlXCIsIFwifFwiLCBcInByZXZpZXdcIiwgXCJzaWRlLWJ5LXNpZGVcIiwgXCJmdWxsc2NyZWVuXCIsIFwiZ3VpZGVcIl07XHJcblxyXG4vKipcclxuICogRGVmYXVsdCBtYXJrZG93biByZW5kZXIuXHJcbiAqL1xyXG5TaW1wbGVNREUucHJvdG90eXBlLm1hcmtkb3duID0gZnVuY3Rpb24odGV4dCkge1xyXG5cdGlmKG1hcmtlZCkge1xyXG5cdFx0Ly8gSW5pdGlhbGl6ZVxyXG5cdFx0dmFyIG1hcmtlZE9wdGlvbnMgPSB7fTtcclxuXHJcblxyXG5cdFx0Ly8gVXBkYXRlIG9wdGlvbnNcclxuXHRcdGlmKHRoaXMub3B0aW9ucyAmJiB0aGlzLm9wdGlvbnMucmVuZGVyaW5nQ29uZmlnICYmIHRoaXMub3B0aW9ucy5yZW5kZXJpbmdDb25maWcuc2luZ2xlTGluZUJyZWFrcyAhPT0gZmFsc2UpIHtcclxuXHRcdFx0bWFya2VkT3B0aW9ucy5icmVha3MgPSB0cnVlO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmKHRoaXMub3B0aW9ucyAmJiB0aGlzLm9wdGlvbnMucmVuZGVyaW5nQ29uZmlnICYmIHRoaXMub3B0aW9ucy5yZW5kZXJpbmdDb25maWcuY29kZVN5bnRheEhpZ2hsaWdodGluZyA9PT0gdHJ1ZSAmJiB3aW5kb3cuaGxqcykge1xyXG5cdFx0XHRtYXJrZWRPcHRpb25zLmhpZ2hsaWdodCA9IGZ1bmN0aW9uKGNvZGUpIHtcclxuXHRcdFx0XHRyZXR1cm4gd2luZG93LmhsanMuaGlnaGxpZ2h0QXV0byhjb2RlKS52YWx1ZTtcclxuXHRcdFx0fTtcclxuXHRcdH1cclxuXHJcblxyXG5cdFx0Ly8gU2V0IG9wdGlvbnNcclxuXHRcdG1hcmtlZC5zZXRPcHRpb25zKG1hcmtlZE9wdGlvbnMpO1xyXG5cclxuXHJcblx0XHQvLyBSZXR1cm5cclxuXHRcdHJldHVybiBtYXJrZWQodGV4dCk7XHJcblx0fVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIFJlbmRlciBlZGl0b3IgdG8gdGhlIGdpdmVuIGVsZW1lbnQuXHJcbiAqL1xyXG5TaW1wbGVNREUucHJvdG90eXBlLnJlbmRlciA9IGZ1bmN0aW9uKGVsKSB7XHJcblx0aWYoIWVsKSB7XHJcblx0XHRlbCA9IHRoaXMuZWxlbWVudCB8fCBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZShcInRleHRhcmVhXCIpWzBdO1xyXG5cdH1cclxuXHJcblx0aWYodGhpcy5fcmVuZGVyZWQgJiYgdGhpcy5fcmVuZGVyZWQgPT09IGVsKSB7XHJcblx0XHQvLyBBbHJlYWR5IHJlbmRlcmVkLlxyXG5cdFx0cmV0dXJuO1xyXG5cdH1cclxuXHJcblx0dGhpcy5lbGVtZW50ID0gZWw7XHJcblx0dmFyIG9wdGlvbnMgPSB0aGlzLm9wdGlvbnM7XHJcblxyXG5cdHZhciBzZWxmID0gdGhpcztcclxuXHR2YXIga2V5TWFwcyA9IHt9O1xyXG5cclxuXHRmb3IodmFyIGtleSBpbiBzaG9ydGN1dHMpIHtcclxuXHRcdChmdW5jdGlvbihrZXkpIHtcclxuXHRcdFx0a2V5TWFwc1tmaXhTaG9ydGN1dChrZXkpXSA9IGZ1bmN0aW9uKCkge1xyXG5cdFx0XHRcdHNob3J0Y3V0c1trZXldKHNlbGYpO1xyXG5cdFx0XHR9O1xyXG5cdFx0fSkoa2V5KTtcclxuXHR9XHJcblxyXG5cdGtleU1hcHNbXCJFbnRlclwiXSA9IFwibmV3bGluZUFuZEluZGVudENvbnRpbnVlTWFya2Rvd25MaXN0XCI7XHJcblx0a2V5TWFwc1tcIlRhYlwiXSA9IFwidGFiQW5kSW5kZW50TWFya2Rvd25MaXN0XCI7XHJcblx0a2V5TWFwc1tcIlNoaWZ0LVRhYlwiXSA9IFwic2hpZnRUYWJBbmRVbmluZGVudE1hcmtkb3duTGlzdFwiO1xyXG5cdGtleU1hcHNbXCJGMTFcIl0gPSBmdW5jdGlvbigpIHtcclxuXHRcdHRvZ2dsZUZ1bGxTY3JlZW4oc2VsZik7XHJcblx0fTtcclxuXHRrZXlNYXBzW1wiRjlcIl0gPSBmdW5jdGlvbigpIHtcclxuXHRcdHRvZ2dsZVNpZGVCeVNpZGUoc2VsZik7XHJcblx0fTtcclxuXHRrZXlNYXBzW1wiRXNjXCJdID0gZnVuY3Rpb24oY20pIHtcclxuXHRcdGlmKGNtLmdldE9wdGlvbihcImZ1bGxTY3JlZW5cIikpIHRvZ2dsZUZ1bGxTY3JlZW4oc2VsZik7XHJcblx0fTtcclxuXHJcblx0dmFyIG1vZGUsIGJhY2tkcm9wO1xyXG5cdGlmKG9wdGlvbnMuc3BlbGxDaGVja2VyICE9PSBmYWxzZSkge1xyXG5cdFx0bW9kZSA9IFwic3BlbGwtY2hlY2tlclwiO1xyXG5cdFx0YmFja2Ryb3AgPSBvcHRpb25zLnBhcnNpbmdDb25maWc7XHJcblx0XHRiYWNrZHJvcC5uYW1lID0gXCJnZm1cIjtcclxuXHRcdGJhY2tkcm9wLmdpdEh1YlNwaWNlID0gZmFsc2U7XHJcblx0fSBlbHNlIHtcclxuXHRcdG1vZGUgPSBvcHRpb25zLnBhcnNpbmdDb25maWc7XHJcblx0XHRtb2RlLm5hbWUgPSBcImdmbVwiO1xyXG5cdFx0bW9kZS5naXRIdWJTcGljZSA9IGZhbHNlO1xyXG5cdH1cclxuXHJcblx0dGhpcy5jb2RlbWlycm9yID0gQ29kZU1pcnJvci5mcm9tVGV4dEFyZWEoZWwsIHtcclxuXHRcdG1vZGU6IG1vZGUsXHJcblx0XHRiYWNrZHJvcDogYmFja2Ryb3AsXHJcblx0XHR0aGVtZTogXCJwYXBlclwiLFxyXG5cdFx0dGFiU2l6ZTogKG9wdGlvbnMudGFiU2l6ZSAhPSB1bmRlZmluZWQpID8gb3B0aW9ucy50YWJTaXplIDogMixcclxuXHRcdGluZGVudFVuaXQ6IChvcHRpb25zLnRhYlNpemUgIT0gdW5kZWZpbmVkKSA/IG9wdGlvbnMudGFiU2l6ZSA6IDIsXHJcblx0XHRpbmRlbnRXaXRoVGFiczogKG9wdGlvbnMuaW5kZW50V2l0aFRhYnMgPT09IGZhbHNlKSA/IGZhbHNlIDogdHJ1ZSxcclxuXHRcdGxpbmVOdW1iZXJzOiBmYWxzZSxcclxuXHRcdGF1dG9mb2N1czogKG9wdGlvbnMuYXV0b2ZvY3VzID09PSB0cnVlKSA/IHRydWUgOiBmYWxzZSxcclxuXHRcdGV4dHJhS2V5czoga2V5TWFwcyxcclxuXHRcdGxpbmVXcmFwcGluZzogKG9wdGlvbnMubGluZVdyYXBwaW5nID09PSBmYWxzZSkgPyBmYWxzZSA6IHRydWUsXHJcblx0XHRhbGxvd0Ryb3BGaWxlVHlwZXM6IFtcInRleHQvcGxhaW5cIl1cclxuXHR9KTtcclxuXHJcblx0aWYob3B0aW9ucy50b29sYmFyICE9PSBmYWxzZSkge1xyXG5cdFx0dGhpcy5jcmVhdGVUb29sYmFyKCk7XHJcblx0fVxyXG5cdGlmKG9wdGlvbnMuc3RhdHVzICE9PSBmYWxzZSkge1xyXG5cdFx0dGhpcy5jcmVhdGVTdGF0dXNiYXIoKTtcclxuXHR9XHJcblx0aWYob3B0aW9ucy5hdXRvc2F2ZSAhPSB1bmRlZmluZWQgJiYgb3B0aW9ucy5hdXRvc2F2ZS5lbmFibGVkID09PSB0cnVlKSB7XHJcblx0XHR0aGlzLmF1dG9zYXZlKCk7XHJcblx0fVxyXG5cclxuXHR0aGlzLmNyZWF0ZVNpZGVCeVNpZGUoKTtcclxuXHJcblx0dGhpcy5fcmVuZGVyZWQgPSB0aGlzLmVsZW1lbnQ7XHJcbn07XHJcblxyXG5TaW1wbGVNREUucHJvdG90eXBlLmF1dG9zYXZlID0gZnVuY3Rpb24oKSB7XHJcblx0dmFyIGNvbnRlbnQgPSB0aGlzLnZhbHVlKCk7XHJcblx0dmFyIHNpbXBsZW1kZSA9IHRoaXM7XHJcblxyXG5cdGlmKHRoaXMub3B0aW9ucy5hdXRvc2F2ZS51bmlxdWVfaWQgPT0gdW5kZWZpbmVkIHx8IHRoaXMub3B0aW9ucy5hdXRvc2F2ZS51bmlxdWVfaWQgPT0gXCJcIikge1xyXG5cdFx0Y29uc29sZS5sb2coXCJTaW1wbGVNREU6IFlvdSBtdXN0IHNldCBhIHVuaXF1ZV9pZCB0byB1c2UgdGhlIGF1dG9zYXZlIGZlYXR1cmVcIik7XHJcblx0XHRyZXR1cm47XHJcblx0fVxyXG5cclxuXHRpZihzaW1wbGVtZGUuZWxlbWVudC5mb3JtICE9IG51bGwgJiYgc2ltcGxlbWRlLmVsZW1lbnQuZm9ybSAhPSB1bmRlZmluZWQpIHtcclxuXHRcdHNpbXBsZW1kZS5lbGVtZW50LmZvcm0uYWRkRXZlbnRMaXN0ZW5lcihcInN1Ym1pdFwiLCBmdW5jdGlvbigpIHtcclxuXHRcdFx0bG9jYWxTdG9yYWdlLnNldEl0ZW0oc2ltcGxlbWRlLm9wdGlvbnMuYXV0b3NhdmUudW5pcXVlX2lkLCBcIlwiKTtcclxuXHRcdH0pO1xyXG5cdH1cclxuXHJcblx0aWYodGhpcy5vcHRpb25zLmF1dG9zYXZlLmxvYWRlZCAhPT0gdHJ1ZSkge1xyXG5cdFx0aWYobG9jYWxTdG9yYWdlLmdldEl0ZW0odGhpcy5vcHRpb25zLmF1dG9zYXZlLnVuaXF1ZV9pZCkgIT0gbnVsbClcclxuXHRcdFx0dGhpcy5jb2RlbWlycm9yLnNldFZhbHVlKGxvY2FsU3RvcmFnZS5nZXRJdGVtKHRoaXMub3B0aW9ucy5hdXRvc2F2ZS51bmlxdWVfaWQpKTtcclxuXHJcblx0XHR0aGlzLm9wdGlvbnMuYXV0b3NhdmUubG9hZGVkID0gdHJ1ZTtcclxuXHR9XHJcblxyXG5cdGlmKGxvY2FsU3RvcmFnZSkge1xyXG5cdFx0bG9jYWxTdG9yYWdlLnNldEl0ZW0odGhpcy5vcHRpb25zLmF1dG9zYXZlLnVuaXF1ZV9pZCwgY29udGVudCk7XHJcblx0fVxyXG5cclxuXHR2YXIgZWwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcImF1dG9zYXZlZFwiKTtcclxuXHRpZihlbCAhPSBudWxsICYmIGVsICE9IHVuZGVmaW5lZCAmJiBlbCAhPSBcIlwiKSB7XHJcblx0XHR2YXIgZCA9IG5ldyBEYXRlKCk7XHJcblx0XHR2YXIgaGggPSBkLmdldEhvdXJzKCk7XHJcblx0XHR2YXIgbSA9IGQuZ2V0TWludXRlcygpO1xyXG5cdFx0dmFyIGRkID0gXCJhbVwiO1xyXG5cdFx0dmFyIGggPSBoaDtcclxuXHRcdGlmKGggPj0gMTIpIHtcclxuXHRcdFx0aCA9IGhoIC0gMTI7XHJcblx0XHRcdGRkID0gXCJwbVwiO1xyXG5cdFx0fVxyXG5cdFx0aWYoaCA9PSAwKSB7XHJcblx0XHRcdGggPSAxMjtcclxuXHRcdH1cclxuXHRcdG0gPSBtIDwgMTAgPyBcIjBcIiArIG0gOiBtO1xyXG5cclxuXHRcdGVsLmlubmVySFRNTCA9IFwiQXV0b3NhdmVkOiBcIiArIGggKyBcIjpcIiArIG0gKyBcIiBcIiArIGRkO1xyXG5cdH1cclxuXHJcblx0c2V0VGltZW91dChmdW5jdGlvbigpIHtcclxuXHRcdHNpbXBsZW1kZS5hdXRvc2F2ZSgpO1xyXG5cdH0sIHRoaXMub3B0aW9ucy5hdXRvc2F2ZS5kZWxheSB8fCAxMDAwMCk7XHJcbn07XHJcblxyXG5TaW1wbGVNREUucHJvdG90eXBlLmNyZWF0ZVNpZGVCeVNpZGUgPSBmdW5jdGlvbigpIHtcclxuXHR2YXIgY20gPSB0aGlzLmNvZGVtaXJyb3I7XHJcblx0dmFyIHdyYXBwZXIgPSBjbS5nZXRXcmFwcGVyRWxlbWVudCgpO1xyXG5cdHZhciBwcmV2aWV3ID0gd3JhcHBlci5uZXh0U2libGluZztcclxuXHJcblx0aWYoIXByZXZpZXcgfHwgIS9lZGl0b3ItcHJldmlldy1zaWRlLy50ZXN0KHByZXZpZXcuY2xhc3NOYW1lKSkge1xyXG5cdFx0cHJldmlldyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJkaXZcIik7XHJcblx0XHRwcmV2aWV3LmNsYXNzTmFtZSA9IFwiZWRpdG9yLXByZXZpZXctc2lkZVwiO1xyXG5cdFx0d3JhcHBlci5wYXJlbnROb2RlLmluc2VydEJlZm9yZShwcmV2aWV3LCB3cmFwcGVyLm5leHRTaWJsaW5nKTtcclxuXHR9XHJcblxyXG5cdC8vIFN5bmNzIHNjcm9sbCAgZWRpdG9yIC0+IHByZXZpZXdcclxuXHR2YXIgY1Njcm9sbCA9IGZhbHNlO1xyXG5cdHZhciBwU2Nyb2xsID0gZmFsc2U7XHJcblx0Y20ub24oXCJzY3JvbGxcIiwgZnVuY3Rpb24odikge1xyXG5cdFx0aWYoY1Njcm9sbCkge1xyXG5cdFx0XHRjU2Nyb2xsID0gZmFsc2U7XHJcblx0XHRcdHJldHVybjtcclxuXHRcdH1cclxuXHRcdHBTY3JvbGwgPSB0cnVlO1xyXG5cdFx0dmFyIGhlaWdodCA9IHYuZ2V0U2Nyb2xsSW5mbygpLmhlaWdodCAtIHYuZ2V0U2Nyb2xsSW5mbygpLmNsaWVudEhlaWdodDtcclxuXHRcdHZhciByYXRpbyA9IHBhcnNlRmxvYXQodi5nZXRTY3JvbGxJbmZvKCkudG9wKSAvIGhlaWdodDtcclxuXHRcdHZhciBtb3ZlID0gKHByZXZpZXcuc2Nyb2xsSGVpZ2h0IC0gcHJldmlldy5jbGllbnRIZWlnaHQpICogcmF0aW87XHJcblx0XHRwcmV2aWV3LnNjcm9sbFRvcCA9IG1vdmU7XHJcblx0fSk7XHJcblxyXG5cdC8vIFN5bmNzIHNjcm9sbCAgcHJldmlldyAtPiBlZGl0b3JcclxuXHRwcmV2aWV3Lm9uc2Nyb2xsID0gZnVuY3Rpb24oKSB7XHJcblx0XHRpZihwU2Nyb2xsKSB7XHJcblx0XHRcdHBTY3JvbGwgPSBmYWxzZTtcclxuXHRcdFx0cmV0dXJuO1xyXG5cdFx0fVxyXG5cdFx0Y1Njcm9sbCA9IHRydWU7XHJcblx0XHR2YXIgaGVpZ2h0ID0gcHJldmlldy5zY3JvbGxIZWlnaHQgLSBwcmV2aWV3LmNsaWVudEhlaWdodDtcclxuXHRcdHZhciByYXRpbyA9IHBhcnNlRmxvYXQocHJldmlldy5zY3JvbGxUb3ApIC8gaGVpZ2h0O1xyXG5cdFx0dmFyIG1vdmUgPSAoY20uZ2V0U2Nyb2xsSW5mbygpLmhlaWdodCAtIGNtLmdldFNjcm9sbEluZm8oKS5jbGllbnRIZWlnaHQpICogcmF0aW87XHJcblx0XHRjbS5zY3JvbGxUbygwLCBtb3ZlKTtcclxuXHR9O1xyXG5cdHJldHVybiB0cnVlO1xyXG59O1xyXG5cclxuU2ltcGxlTURFLnByb3RvdHlwZS5jcmVhdGVUb29sYmFyID0gZnVuY3Rpb24oaXRlbXMpIHtcclxuXHRpdGVtcyA9IGl0ZW1zIHx8IHRoaXMub3B0aW9ucy50b29sYmFyO1xyXG5cclxuXHRpZighaXRlbXMgfHwgaXRlbXMubGVuZ3RoID09PSAwKSB7XHJcblx0XHRyZXR1cm47XHJcblx0fVxyXG5cdHZhciBpO1xyXG5cdGZvcihpID0gMDsgaSA8IGl0ZW1zLmxlbmd0aDsgaSsrKSB7XHJcblx0XHRpZih0b29sYmFyQnVpbHRJbkJ1dHRvbnNbaXRlbXNbaV1dICE9IHVuZGVmaW5lZCkge1xyXG5cdFx0XHRpdGVtc1tpXSA9IHRvb2xiYXJCdWlsdEluQnV0dG9uc1tpdGVtc1tpXV07XHJcblx0XHR9XHJcblx0fVxyXG5cclxuXHR2YXIgYmFyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImRpdlwiKTtcclxuXHRiYXIuY2xhc3NOYW1lID0gXCJlZGl0b3ItdG9vbGJhclwiO1xyXG5cclxuXHR2YXIgc2VsZiA9IHRoaXM7XHJcblxyXG5cdHZhciB0b29sYmFyX2RhdGEgPSB7fTtcclxuXHRzZWxmLnRvb2xiYXIgPSBpdGVtcztcclxuXHJcblx0Zm9yKGkgPSAwOyBpIDwgaXRlbXMubGVuZ3RoOyBpKyspIHtcclxuXHRcdGlmKGl0ZW1zW2ldLm5hbWUgPT0gXCJndWlkZVwiICYmIHNlbGYub3B0aW9ucy50b29sYmFyR3VpZGVJY29uID09PSBmYWxzZSlcclxuXHRcdFx0Y29udGludWU7XHJcblxyXG5cdFx0aWYoc2VsZi5vcHRpb25zLmhpZGVJY29ucyAmJiBzZWxmLm9wdGlvbnMuaGlkZUljb25zLmluZGV4T2YoaXRlbXNbaV0ubmFtZSkgIT0gLTEpXHJcblx0XHRcdGNvbnRpbnVlO1xyXG5cclxuXHRcdChmdW5jdGlvbihpdGVtKSB7XHJcblx0XHRcdHZhciBlbDtcclxuXHRcdFx0aWYoaXRlbSA9PT0gXCJ8XCIpIHtcclxuXHRcdFx0XHRlbCA9IGNyZWF0ZVNlcCgpO1xyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdGVsID0gY3JlYXRlSWNvbihpdGVtLCBzZWxmLm9wdGlvbnMudG9vbGJhclRpcHMpO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHQvLyBiaW5kIGV2ZW50cywgc3BlY2lhbCBmb3IgaW5mb1xyXG5cdFx0XHRpZihpdGVtLmFjdGlvbikge1xyXG5cdFx0XHRcdGlmKHR5cGVvZiBpdGVtLmFjdGlvbiA9PT0gXCJmdW5jdGlvblwiKSB7XHJcblx0XHRcdFx0XHRlbC5vbmNsaWNrID0gZnVuY3Rpb24oKSB7XHJcblx0XHRcdFx0XHRcdGl0ZW0uYWN0aW9uKHNlbGYpO1xyXG5cdFx0XHRcdFx0fTtcclxuXHRcdFx0XHR9IGVsc2UgaWYodHlwZW9mIGl0ZW0uYWN0aW9uID09PSBcInN0cmluZ1wiKSB7XHJcblx0XHRcdFx0XHRlbC5ocmVmID0gaXRlbS5hY3Rpb247XHJcblx0XHRcdFx0XHRlbC50YXJnZXQgPSBcIl9ibGFua1wiO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fVxyXG5cdFx0XHR0b29sYmFyX2RhdGFbaXRlbS5uYW1lIHx8IGl0ZW1dID0gZWw7XHJcblx0XHRcdGJhci5hcHBlbmRDaGlsZChlbCk7XHJcblx0XHR9KShpdGVtc1tpXSk7XHJcblx0fVxyXG5cclxuXHRzZWxmLnRvb2xiYXJFbGVtZW50cyA9IHRvb2xiYXJfZGF0YTtcclxuXHJcblx0dmFyIGNtID0gdGhpcy5jb2RlbWlycm9yO1xyXG5cdGNtLm9uKFwiY3Vyc29yQWN0aXZpdHlcIiwgZnVuY3Rpb24oKSB7XHJcblx0XHR2YXIgc3RhdCA9IGdldFN0YXRlKGNtKTtcclxuXHJcblx0XHRmb3IodmFyIGtleSBpbiB0b29sYmFyX2RhdGEpIHtcclxuXHRcdFx0KGZ1bmN0aW9uKGtleSkge1xyXG5cdFx0XHRcdHZhciBlbCA9IHRvb2xiYXJfZGF0YVtrZXldO1xyXG5cdFx0XHRcdGlmKHN0YXRba2V5XSkge1xyXG5cdFx0XHRcdFx0ZWwuY2xhc3NOYW1lICs9IFwiIGFjdGl2ZVwiO1xyXG5cdFx0XHRcdH0gZWxzZSBpZihrZXkgIT0gXCJmdWxsc2NyZWVuXCIgJiYga2V5ICE9IFwic2lkZS1ieS1zaWRlXCIpIHtcclxuXHRcdFx0XHRcdGVsLmNsYXNzTmFtZSA9IGVsLmNsYXNzTmFtZS5yZXBsYWNlKC9cXHMqYWN0aXZlXFxzKi9nLCBcIlwiKTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdH0pKGtleSk7XHJcblx0XHR9XHJcblx0fSk7XHJcblxyXG5cdHZhciBjbVdyYXBwZXIgPSBjbS5nZXRXcmFwcGVyRWxlbWVudCgpO1xyXG5cdGNtV3JhcHBlci5wYXJlbnROb2RlLmluc2VydEJlZm9yZShiYXIsIGNtV3JhcHBlcik7XHJcblx0cmV0dXJuIGJhcjtcclxufTtcclxuXHJcblNpbXBsZU1ERS5wcm90b3R5cGUuY3JlYXRlU3RhdHVzYmFyID0gZnVuY3Rpb24oc3RhdHVzKSB7XHJcblx0c3RhdHVzID0gc3RhdHVzIHx8IHRoaXMub3B0aW9ucy5zdGF0dXM7XHJcblx0dmFyIG9wdGlvbnMgPSB0aGlzLm9wdGlvbnM7XHJcblxyXG5cdGlmKCFzdGF0dXMgfHwgc3RhdHVzLmxlbmd0aCA9PT0gMCkgcmV0dXJuO1xyXG5cclxuXHR2YXIgYmFyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImRpdlwiKTtcclxuXHRiYXIuY2xhc3NOYW1lID0gXCJlZGl0b3Itc3RhdHVzYmFyXCI7XHJcblxyXG5cdHZhciBwb3MsIGNtID0gdGhpcy5jb2RlbWlycm9yO1xyXG5cdGZvcih2YXIgaSA9IDA7IGkgPCBzdGF0dXMubGVuZ3RoOyBpKyspIHtcclxuXHRcdChmdW5jdGlvbihuYW1lKSB7XHJcblx0XHRcdHZhciBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJzcGFuXCIpO1xyXG5cdFx0XHRlbC5jbGFzc05hbWUgPSBuYW1lO1xyXG5cdFx0XHRpZihuYW1lID09PSBcIndvcmRzXCIpIHtcclxuXHRcdFx0XHRlbC5pbm5lckhUTUwgPSBcIjBcIjtcclxuXHRcdFx0XHRjbS5vbihcInVwZGF0ZVwiLCBmdW5jdGlvbigpIHtcclxuXHRcdFx0XHRcdGVsLmlubmVySFRNTCA9IHdvcmRDb3VudChjbS5nZXRWYWx1ZSgpKTtcclxuXHRcdFx0XHR9KTtcclxuXHRcdFx0fSBlbHNlIGlmKG5hbWUgPT09IFwibGluZXNcIikge1xyXG5cdFx0XHRcdGVsLmlubmVySFRNTCA9IFwiMFwiO1xyXG5cdFx0XHRcdGNtLm9uKFwidXBkYXRlXCIsIGZ1bmN0aW9uKCkge1xyXG5cdFx0XHRcdFx0ZWwuaW5uZXJIVE1MID0gY20ubGluZUNvdW50KCk7XHJcblx0XHRcdFx0fSk7XHJcblx0XHRcdH0gZWxzZSBpZihuYW1lID09PSBcImN1cnNvclwiKSB7XHJcblx0XHRcdFx0ZWwuaW5uZXJIVE1MID0gXCIwOjBcIjtcclxuXHRcdFx0XHRjbS5vbihcImN1cnNvckFjdGl2aXR5XCIsIGZ1bmN0aW9uKCkge1xyXG5cdFx0XHRcdFx0cG9zID0gY20uZ2V0Q3Vyc29yKCk7XHJcblx0XHRcdFx0XHRlbC5pbm5lckhUTUwgPSBwb3MubGluZSArIFwiOlwiICsgcG9zLmNoO1xyXG5cdFx0XHRcdH0pO1xyXG5cdFx0XHR9IGVsc2UgaWYobmFtZSA9PT0gXCJhdXRvc2F2ZVwiKSB7XHJcblx0XHRcdFx0aWYob3B0aW9ucy5hdXRvc2F2ZSAhPSB1bmRlZmluZWQgJiYgb3B0aW9ucy5hdXRvc2F2ZS5lbmFibGVkID09PSB0cnVlKSB7XHJcblx0XHRcdFx0XHRlbC5zZXRBdHRyaWJ1dGUoXCJpZFwiLCBcImF1dG9zYXZlZFwiKTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHRcdFx0YmFyLmFwcGVuZENoaWxkKGVsKTtcclxuXHRcdH0pKHN0YXR1c1tpXSk7XHJcblx0fVxyXG5cclxuXHR2YXIgY21XcmFwcGVyID0gdGhpcy5jb2RlbWlycm9yLmdldFdyYXBwZXJFbGVtZW50KCk7XHJcblx0Y21XcmFwcGVyLnBhcmVudE5vZGUuaW5zZXJ0QmVmb3JlKGJhciwgY21XcmFwcGVyLm5leHRTaWJsaW5nKTtcclxuXHRyZXR1cm4gYmFyO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEdldCBvciBzZXQgdGhlIHRleHQgY29udGVudC5cclxuICovXHJcblNpbXBsZU1ERS5wcm90b3R5cGUudmFsdWUgPSBmdW5jdGlvbih2YWwpIHtcclxuXHRpZih2YWwgPT09IHVuZGVmaW5lZCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuY29kZW1pcnJvci5nZXRWYWx1ZSgpO1xyXG5cdH0gZWxzZSB7XHJcblx0XHR0aGlzLmNvZGVtaXJyb3IuZ2V0RG9jKCkuc2V0VmFsdWUodmFsKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH1cclxufTtcclxuXHJcblxyXG4vKipcclxuICogQmluZCBzdGF0aWMgbWV0aG9kcyBmb3IgZXhwb3J0cy5cclxuICovXHJcblNpbXBsZU1ERS50b2dnbGVCb2xkID0gdG9nZ2xlQm9sZDtcclxuU2ltcGxlTURFLnRvZ2dsZUl0YWxpYyA9IHRvZ2dsZUl0YWxpYztcclxuU2ltcGxlTURFLnRvZ2dsZVN0cmlrZXRocm91Z2ggPSB0b2dnbGVTdHJpa2V0aHJvdWdoO1xyXG5TaW1wbGVNREUudG9nZ2xlQmxvY2txdW90ZSA9IHRvZ2dsZUJsb2NrcXVvdGU7XHJcblNpbXBsZU1ERS50b2dnbGVIZWFkaW5nU21hbGxlciA9IHRvZ2dsZUhlYWRpbmdTbWFsbGVyO1xyXG5TaW1wbGVNREUudG9nZ2xlSGVhZGluZ0JpZ2dlciA9IHRvZ2dsZUhlYWRpbmdCaWdnZXI7XHJcblNpbXBsZU1ERS50b2dnbGVIZWFkaW5nMSA9IHRvZ2dsZUhlYWRpbmcxO1xyXG5TaW1wbGVNREUudG9nZ2xlSGVhZGluZzIgPSB0b2dnbGVIZWFkaW5nMjtcclxuU2ltcGxlTURFLnRvZ2dsZUhlYWRpbmczID0gdG9nZ2xlSGVhZGluZzM7XHJcblNpbXBsZU1ERS50b2dnbGVDb2RlQmxvY2sgPSB0b2dnbGVDb2RlQmxvY2s7XHJcblNpbXBsZU1ERS50b2dnbGVVbm9yZGVyZWRMaXN0ID0gdG9nZ2xlVW5vcmRlcmVkTGlzdDtcclxuU2ltcGxlTURFLnRvZ2dsZU9yZGVyZWRMaXN0ID0gdG9nZ2xlT3JkZXJlZExpc3Q7XHJcblNpbXBsZU1ERS5kcmF3TGluayA9IGRyYXdMaW5rO1xyXG5TaW1wbGVNREUuZHJhd0ltYWdlID0gZHJhd0ltYWdlO1xyXG5TaW1wbGVNREUuZHJhd0hvcml6b250YWxSdWxlID0gZHJhd0hvcml6b250YWxSdWxlO1xyXG5TaW1wbGVNREUudW5kbyA9IHVuZG87XHJcblNpbXBsZU1ERS5yZWRvID0gcmVkbztcclxuU2ltcGxlTURFLnRvZ2dsZVByZXZpZXcgPSB0b2dnbGVQcmV2aWV3O1xyXG5TaW1wbGVNREUudG9nZ2xlU2lkZUJ5U2lkZSA9IHRvZ2dsZVNpZGVCeVNpZGU7XHJcblNpbXBsZU1ERS50b2dnbGVGdWxsU2NyZWVuID0gdG9nZ2xlRnVsbFNjcmVlbjtcclxuXHJcbi8qKlxyXG4gKiBCaW5kIGluc3RhbmNlIG1ldGhvZHMgZm9yIGV4cG9ydHMuXHJcbiAqL1xyXG5TaW1wbGVNREUucHJvdG90eXBlLnRvZ2dsZUJvbGQgPSBmdW5jdGlvbigpIHtcclxuXHR0b2dnbGVCb2xkKHRoaXMpO1xyXG59O1xyXG5TaW1wbGVNREUucHJvdG90eXBlLnRvZ2dsZUl0YWxpYyA9IGZ1bmN0aW9uKCkge1xyXG5cdHRvZ2dsZUl0YWxpYyh0aGlzKTtcclxufTtcclxuU2ltcGxlTURFLnByb3RvdHlwZS50b2dnbGVTdHJpa2V0aHJvdWdoID0gZnVuY3Rpb24oKSB7XHJcblx0dG9nZ2xlU3RyaWtldGhyb3VnaCh0aGlzKTtcclxufTtcclxuU2ltcGxlTURFLnByb3RvdHlwZS50b2dnbGVCbG9ja3F1b3RlID0gZnVuY3Rpb24oKSB7XHJcblx0dG9nZ2xlQmxvY2txdW90ZSh0aGlzKTtcclxufTtcclxuU2ltcGxlTURFLnByb3RvdHlwZS50b2dnbGVIZWFkaW5nU21hbGxlciA9IGZ1bmN0aW9uKCkge1xyXG5cdHRvZ2dsZUhlYWRpbmdTbWFsbGVyKHRoaXMpO1xyXG59O1xyXG5TaW1wbGVNREUucHJvdG90eXBlLnRvZ2dsZUhlYWRpbmdCaWdnZXIgPSBmdW5jdGlvbigpIHtcclxuXHR0b2dnbGVIZWFkaW5nQmlnZ2VyKHRoaXMpO1xyXG59O1xyXG5TaW1wbGVNREUucHJvdG90eXBlLnRvZ2dsZUhlYWRpbmcxID0gZnVuY3Rpb24oKSB7XHJcblx0dG9nZ2xlSGVhZGluZzEodGhpcyk7XHJcbn07XHJcblNpbXBsZU1ERS5wcm90b3R5cGUudG9nZ2xlSGVhZGluZzIgPSBmdW5jdGlvbigpIHtcclxuXHR0b2dnbGVIZWFkaW5nMih0aGlzKTtcclxufTtcclxuU2ltcGxlTURFLnByb3RvdHlwZS50b2dnbGVIZWFkaW5nMyA9IGZ1bmN0aW9uKCkge1xyXG5cdHRvZ2dsZUhlYWRpbmczKHRoaXMpO1xyXG59O1xyXG5TaW1wbGVNREUucHJvdG90eXBlLnRvZ2dsZUNvZGVCbG9jayA9IGZ1bmN0aW9uKCkge1xyXG5cdHRvZ2dsZUNvZGVCbG9jayh0aGlzKTtcclxufTtcclxuU2ltcGxlTURFLnByb3RvdHlwZS50b2dnbGVVbm9yZGVyZWRMaXN0ID0gZnVuY3Rpb24oKSB7XHJcblx0dG9nZ2xlVW5vcmRlcmVkTGlzdCh0aGlzKTtcclxufTtcclxuU2ltcGxlTURFLnByb3RvdHlwZS50b2dnbGVPcmRlcmVkTGlzdCA9IGZ1bmN0aW9uKCkge1xyXG5cdHRvZ2dsZU9yZGVyZWRMaXN0KHRoaXMpO1xyXG59O1xyXG5TaW1wbGVNREUucHJvdG90eXBlLmRyYXdMaW5rID0gZnVuY3Rpb24oKSB7XHJcblx0ZHJhd0xpbmsodGhpcyk7XHJcbn07XHJcblNpbXBsZU1ERS5wcm90b3R5cGUuZHJhd0ltYWdlID0gZnVuY3Rpb24oKSB7XHJcblx0ZHJhd0ltYWdlKHRoaXMpO1xyXG59O1xyXG5TaW1wbGVNREUucHJvdG90eXBlLmRyYXdIb3Jpem9udGFsUnVsZSA9IGZ1bmN0aW9uKCkge1xyXG5cdGRyYXdIb3Jpem9udGFsUnVsZSh0aGlzKTtcclxufTtcclxuU2ltcGxlTURFLnByb3RvdHlwZS51bmRvID0gZnVuY3Rpb24oKSB7XHJcblx0dW5kbyh0aGlzKTtcclxufTtcclxuU2ltcGxlTURFLnByb3RvdHlwZS5yZWRvID0gZnVuY3Rpb24oKSB7XHJcblx0cmVkbyh0aGlzKTtcclxufTtcclxuU2ltcGxlTURFLnByb3RvdHlwZS50b2dnbGVQcmV2aWV3ID0gZnVuY3Rpb24oKSB7XHJcblx0dG9nZ2xlUHJldmlldyh0aGlzKTtcclxufTtcclxuU2ltcGxlTURFLnByb3RvdHlwZS50b2dnbGVTaWRlQnlTaWRlID0gZnVuY3Rpb24oKSB7XHJcblx0dG9nZ2xlU2lkZUJ5U2lkZSh0aGlzKTtcclxufTtcclxuU2ltcGxlTURFLnByb3RvdHlwZS50b2dnbGVGdWxsU2NyZWVuID0gZnVuY3Rpb24oKSB7XHJcblx0dG9nZ2xlRnVsbFNjcmVlbih0aGlzKTtcclxufTtcclxubW9kdWxlLmV4cG9ydHMgPSBTaW1wbGVNREU7XHJcbiJdfQ==
diff --git a/debug/simplemde.js b/debug/simplemde.js
new file mode 100644
index 0000000..77dd455
--- /dev/null
+++ b/debug/simplemde.js
@@ -0,0 +1,14024 @@
+(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.SimpleMDE = 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?@[\\]^_`{|}~ ";
+
+
+ // Create the overlay and such
+ var overlay = {
+ token: function(stream, state) {
+ var ch = stream.peek();
+ var word = "";
+
+ if(rx_word.includes(ch)) {
+ stream.next();
+ return null;
+ }
+
+ while((ch = stream.peek()) != null && !rx_word.includes(ch)) {
+ word += ch;
+ stream.next();
+ }
+
+ if(typo && !typo.check(word))
+ return "spell-error"; // CSS class: cm-spell-error
+
+ return null;
+ }
+ };
+
+ var mode = CodeMirror.getMode(
+ config, config.backdrop || "text/plain"
+ );
+
+ return CodeMirror.overlayMode(mode, overlay, true);
+});
+
+
+// Because some browsers don't support this functionality yet
+if(!String.prototype.includes) {
+ String.prototype.includes = function() {'use strict';
+ return String.prototype.indexOf.apply(this, arguments) !== -1;
+ };
+}
+}).call(global, module, undefined, undefined);
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"C:\\phantomx\\projects\\simplemde-markdown-editor\\node_modules\\codemirror-spell-checker\\src\\js\\typo.js":2,"codemirror":6}],2:[function(require,module,exports){
+(function (global){
+; var __browserify_shim_require__=require;(function browserifyShim(module, exports, require, define, browserify_shim__define__module__export__) {
+'use strict';
+
+/**
+ * Typo is a JavaScript implementation of a spellchecker using hunspell-style
+ * dictionaries.
+ */
+
+/**
+ * Typo constructor.
+ *
+ * @param {String} [dictionary] The locale code of the dictionary being used. e.g.,
+ * "en_US". This is only used to auto-load dictionaries.
+ * @param {String} [affData] The data from the dictionary's .aff file. If omitted
+ * and the first argument is supplied, in "chrome" platform,
+ * the .aff file will be loaded automatically from
+ * lib/typo/dictionaries/[dictionary]/[dictionary].aff
+ * In other platform, it will be loaded from
+ * [setting.path]/dictionaries/[dictionary]/[dictionary].aff
+ * @param {String} [wordsData] The data from the dictionary's .dic file. If omitted,
+ * and the first argument is supplied, in "chrome" platform,
+ * the .dic file will be loaded automatically from
+ * lib/typo/dictionaries/[dictionary]/[dictionary].dic
+ * In other platform, it will be loaded from
+ * [setting.path]/dictionaries/[dictionary]/[dictionary].dic
+ * @param {Object} [settings] Constructor settings. Available properties are:
+ * {String} [platform]: "chrome" for Chrome Extension or other
+ * value for the usual web.
+ * {String} [dictionaryPath]: path to load dictionary from in non-chrome
+ * environment.
+ * {Object} [flags]: flag information.
+ *
+ *
+ * @returns {Typo} A Typo object.
+ */
+
+var Typo = function (dictionary, affData, wordsData, settings) {
+ settings = settings || {};
+
+ /** Determines the method used for auto-loading .aff and .dic files. **/
+ this.platform = settings.platform || "chrome";
+
+ this.dictionary = null;
+
+ this.rules = {};
+ this.dictionaryTable = {};
+
+ this.compoundRules = [];
+ this.compoundRuleCodes = {};
+
+ this.replacementTable = [];
+
+ this.flags = settings.flags || {};
+
+ if (dictionary) {
+ this.dictionary = dictionary;
+
+ if (this.platform == "chrome") {
+ if (!affData) affData = this._readFile(chrome.extension.getURL("lib/typo/dictionaries/" + dictionary + "/" + dictionary + ".aff"));
+ if (!wordsData) wordsData = this._readFile(chrome.extension.getURL("lib/typo/dictionaries/" + dictionary + "/" + dictionary + ".dic"));
+ } else {
+ var path = settings.dictionaryPath || '';
+
+ if (!affData) affData = this._readFile(path + "/" + dictionary + "/" + dictionary + ".aff");
+ if (!wordsData) wordsData = this._readFile(path + "/" + dictionary + "/" + dictionary + ".dic");
+ }
+
+ this.rules = this._parseAFF(affData);
+
+ // Save the rule codes that are used in compound rules.
+ this.compoundRuleCodes = {};
+
+ for (var i = 0, _len = this.compoundRules.length; i < _len; i++) {
+ var rule = this.compoundRules[i];
+
+ for (var j = 0, _jlen = rule.length; j < _jlen; j++) {
+ this.compoundRuleCodes[rule[j]] = [];
+ }
+ }
+
+ // If we add this ONLYINCOMPOUND flag to this.compoundRuleCodes, then _parseDIC
+ // will do the work of saving the list of words that are compound-only.
+ if ("ONLYINCOMPOUND" in this.flags) {
+ this.compoundRuleCodes[this.flags.ONLYINCOMPOUND] = [];
+ }
+
+ this.dictionaryTable = this._parseDIC(wordsData);
+
+ // Get rid of any codes from the compound rule codes that are never used
+ // (or that were special regex characters). Not especially necessary...
+ for (var i in this.compoundRuleCodes) {
+ if (this.compoundRuleCodes[i].length == 0) {
+ delete this.compoundRuleCodes[i];
+ }
+ }
+
+ // Build the full regular expressions for each compound rule.
+ // I have a feeling (but no confirmation yet) that this method of
+ // testing for compound words is probably slow.
+ for (var i = 0, _len = this.compoundRules.length; i < _len; i++) {
+ var ruleText = this.compoundRules[i];
+
+ var expressionText = "";
+
+ for (var j = 0, _jlen = ruleText.length; j < _jlen; j++) {
+ var character = ruleText[j];
+
+ if (character in this.compoundRuleCodes) {
+ expressionText += "(" + this.compoundRuleCodes[character].join("|") + ")";
+ }
+ else {
+ expressionText += character;
+ }
+ }
+
+ this.compoundRules[i] = new RegExp(expressionText, "i");
+ }
+ }
+
+ return this;
+};
+
+Typo.prototype = {
+ /**
+ * Loads a Typo instance from a hash of all of the Typo properties.
+ *
+ * @param object obj A hash of Typo properties, probably gotten from a JSON.parse(JSON.stringify(typo_instance)).
+ */
+
+ load : function (obj) {
+ for (var i in obj) {
+ this[i] = obj[i];
+ }
+
+ return this;
+ },
+
+ /**
+ * Read the contents of a file.
+ *
+ * @param {String} path The path (relative) to the file.
+ * @param {String} [charset="ISO8859-1"] The expected charset of the file
+ * @returns string The file data.
+ */
+
+ _readFile : function (path, charset) {
+ if (!charset) charset = "ISO8859-1";
+
+ var req = new XMLHttpRequest();
+ req.open("GET", path, false);
+
+ if (req.overrideMimeType)
+ req.overrideMimeType("text/plain; charset=" + charset);
+
+ req.send(null);
+
+ return req.responseText;
+ },
+
+ /**
+ * Parse the rules out from a .aff file.
+ *
+ * @param {String} data The contents of the affix file.
+ * @returns object The rules from the file.
+ */
+
+ _parseAFF : function (data) {
+ var rules = {};
+
+ // Remove comment lines
+ data = this._removeAffixComments(data);
+
+ var lines = data.split("\n");
+
+ for (var i = 0, _len = lines.length; i < _len; i++) {
+ var line = lines[i];
+
+ var definitionParts = line.split(/\s+/);
+
+ var ruleType = definitionParts[0];
+
+ if (ruleType == "PFX" || ruleType == "SFX") {
+ var ruleCode = definitionParts[1];
+ var combineable = definitionParts[2];
+ var numEntries = parseInt(definitionParts[3], 10);
+
+ var entries = [];
+
+ for (var j = i + 1, _jlen = i + 1 + numEntries; j < _jlen; j++) {
+ var line = lines[j];
+
+ var lineParts = line.split(/\s+/);
+ var charactersToRemove = lineParts[2];
+
+ var additionParts = lineParts[3].split("/");
+
+ var charactersToAdd = additionParts[0];
+ if (charactersToAdd === "0") charactersToAdd = "";
+
+ var continuationClasses = this.parseRuleCodes(additionParts[1]);
+
+ var regexToMatch = lineParts[4];
+
+ var entry = {};
+ entry.add = charactersToAdd;
+
+ if (continuationClasses.length > 0) entry.continuationClasses = continuationClasses;
+
+ if (regexToMatch !== ".") {
+ if (ruleType === "SFX") {
+ entry.match = new RegExp(regexToMatch + "$");
+ }
+ else {
+ entry.match = new RegExp("^" + regexToMatch);
+ }
+ }
+
+ if (charactersToRemove != "0") {
+ if (ruleType === "SFX") {
+ entry.remove = new RegExp(charactersToRemove + "$");
+ }
+ else {
+ entry.remove = charactersToRemove;
+ }
+ }
+
+ entries.push(entry);
+ }
+
+ rules[ruleCode] = { "type" : ruleType, "combineable" : (combineable == "Y"), "entries" : entries };
+
+ i += numEntries;
+ }
+ else if (ruleType === "COMPOUNDRULE") {
+ var numEntries = parseInt(definitionParts[1], 10);
+
+ for (var j = i + 1, _jlen = i + 1 + numEntries; j < _jlen; j++) {
+ var line = lines[j];
+
+ var lineParts = line.split(/\s+/);
+ this.compoundRules.push(lineParts[1]);
+ }
+
+ i += numEntries;
+ }
+ else if (ruleType === "REP") {
+ var lineParts = line.split(/\s+/);
+
+ if (lineParts.length === 3) {
+ this.replacementTable.push([ lineParts[1], lineParts[2] ]);
+ }
+ }
+ else {
+ // ONLYINCOMPOUND
+ // COMPOUNDMIN
+ // FLAG
+ // KEEPCASE
+ // NEEDAFFIX
+
+ this.flags[ruleType] = definitionParts[1];
+ }
+ }
+
+ return rules;
+ },
+
+ /**
+ * Removes comment lines and then cleans up blank lines and trailing whitespace.
+ *
+ * @param {String} data The data from an affix file.
+ * @return {String} The cleaned-up data.
+ */
+
+ _removeAffixComments : function (data) {
+ // Remove comments
+ data = data.replace(/#.*$/mg, "");
+
+ // Trim each line
+ data = data.replace(/^\s\s*/m, '').replace(/\s\s*$/m, '');
+
+ // Remove blank lines.
+ data = data.replace(/\n{2,}/g, "\n");
+
+ // Trim the entire string
+ data = data.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
+
+ return data;
+ },
+
+ /**
+ * Parses the words out from the .dic file.
+ *
+ * @param {String} data The data from the dictionary file.
+ * @returns object The lookup table containing all of the words and
+ * word forms from the dictionary.
+ */
+
+ _parseDIC : function (data) {
+ data = this._removeDicComments(data);
+
+ var lines = data.split("\n");
+ var dictionaryTable = {};
+
+ function addWord(word, rules) {
+ // Some dictionaries will list the same word multiple times with different rule sets.
+ if (!(word in dictionaryTable) || typeof dictionaryTable[word] != 'object') {
+ dictionaryTable[word] = [];
+ }
+
+ dictionaryTable[word].push(rules);
+ }
+
+ // The first line is the number of words in the dictionary.
+ for (var i = 1, _len = lines.length; i < _len; i++) {
+ var line = lines[i];
+
+ var parts = line.split("/", 2);
+
+ var word = parts[0];
+
+ // Now for each affix rule, generate that form of the word.
+ if (parts.length > 1) {
+ var ruleCodesArray = this.parseRuleCodes(parts[1]);
+
+ // Save the ruleCodes for compound word situations.
+ if (!("NEEDAFFIX" in this.flags) || ruleCodesArray.indexOf(this.flags.NEEDAFFIX) == -1) {
+ addWord(word, ruleCodesArray);
+ }
+
+ for (var j = 0, _jlen = ruleCodesArray.length; j < _jlen; j++) {
+ var code = ruleCodesArray[j];
+
+ var rule = this.rules[code];
+
+ if (rule) {
+ var newWords = this._applyRule(word, rule);
+
+ for (var ii = 0, _iilen = newWords.length; ii < _iilen; ii++) {
+ var newWord = newWords[ii];
+
+ addWord(newWord, []);
+
+ if (rule.combineable) {
+ for (var k = j + 1; k < _jlen; k++) {
+ var combineCode = ruleCodesArray[k];
+
+ var combineRule = this.rules[combineCode];
+
+ if (combineRule) {
+ if (combineRule.combineable && (rule.type != combineRule.type)) {
+ var otherNewWords = this._applyRule(newWord, combineRule);
+
+ for (var iii = 0, _iiilen = otherNewWords.length; iii < _iiilen; iii++) {
+ var otherNewWord = otherNewWords[iii];
+ addWord(otherNewWord, []);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (code in this.compoundRuleCodes) {
+ this.compoundRuleCodes[code].push(word);
+ }
+ }
+ }
+ else {
+ addWord(word.trim(), []);
+ }
+ }
+
+ return dictionaryTable;
+ },
+
+
+ /**
+ * Removes comment lines and then cleans up blank lines and trailing whitespace.
+ *
+ * @param {String} data The data from a .dic file.
+ * @return {String} The cleaned-up data.
+ */
+
+ _removeDicComments : function (data) {
+ // I can't find any official documentation on it, but at least the de_DE
+ // dictionary uses tab-indented lines as comments.
+
+ // Remove comments
+ data = data.replace(/^\t.*$/mg, "");
+
+ return data;
+
+ // Trim each line
+ data = data.replace(/^\s\s*/m, '').replace(/\s\s*$/m, '');
+
+ // Remove blank lines.
+ data = data.replace(/\n{2,}/g, "\n");
+
+ // Trim the entire string
+ data = data.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
+
+ return data;
+ },
+
+ parseRuleCodes : function (textCodes) {
+ if (!textCodes) {
+ return [];
+ }
+ else if (!("FLAG" in this.flags)) {
+ return textCodes.split("");
+ }
+ else if (this.flags.FLAG === "long") {
+ var flags = [];
+
+ for (var i = 0, _len = textCodes.length; i < _len; i += 2) {
+ flags.push(textCodes.substr(i, 2));
+ }
+
+ return flags;
+ }
+ else if (this.flags.FLAG === "num") {
+ return textCode.split(",");
+ }
+ },
+
+ /**
+ * Applies an affix rule to a word.
+ *
+ * @param {String} word The base word.
+ * @param {Object} rule The affix rule.
+ * @returns {String[]} The new words generated by the rule.
+ */
+
+ _applyRule : function (word, rule) {
+ var entries = rule.entries;
+ var newWords = [];
+
+ for (var i = 0, _len = entries.length; i < _len; i++) {
+ var entry = entries[i];
+
+ if (!entry.match || word.match(entry.match)) {
+ var newWord = word;
+
+ if (entry.remove) {
+ newWord = newWord.replace(entry.remove, "");
+ }
+
+ if (rule.type === "SFX") {
+ newWord = newWord + entry.add;
+ }
+ else {
+ newWord = entry.add + newWord;
+ }
+
+ newWords.push(newWord);
+
+ if ("continuationClasses" in entry) {
+ for (var j = 0, _jlen = entry.continuationClasses.length; j < _jlen; j++) {
+ var continuationRule = this.rules[entry.continuationClasses[j]];
+
+ if (continuationRule) {
+ newWords = newWords.concat(this._applyRule(newWord, continuationRule));
+ }
+ /*
+ else {
+ // This shouldn't happen, but it does, at least in the de_DE dictionary.
+ // I think the author mistakenly supplied lower-case rule codes instead
+ // of upper-case.
+ }
+ */
+ }
+ }
+ }
+ }
+
+ return newWords;
+ },
+
+ /**
+ * Checks whether a word or a capitalization variant exists in the current dictionary.
+ * The word is trimmed and several variations of capitalizations are checked.
+ * If you want to check a word without any changes made to it, call checkExact()
+ *
+ * @see http://blog.stevenlevithan.com/archives/faster-trim-javascript re:trimming function
+ *
+ * @param {String} aWord The word to check.
+ * @returns {Boolean}
+ */
+
+ check : function (aWord) {
+ // Remove leading and trailing whitespace
+ var trimmedWord = aWord.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
+
+ if (this.checkExact(trimmedWord)) {
+ return true;
+ }
+
+ // The exact word is not in the dictionary.
+ if (trimmedWord.toUpperCase() === trimmedWord) {
+ // The word was supplied in all uppercase.
+ // Check for a capitalized form of the word.
+ var capitalizedWord = trimmedWord[0] + trimmedWord.substring(1).toLowerCase();
+
+ if (this.hasFlag(capitalizedWord, "KEEPCASE")) {
+ // Capitalization variants are not allowed for this word.
+ return false;
+ }
+
+ if (this.checkExact(capitalizedWord)) {
+ return true;
+ }
+ }
+
+ var lowercaseWord = trimmedWord.toLowerCase();
+
+ if (lowercaseWord !== trimmedWord) {
+ if (this.hasFlag(lowercaseWord, "KEEPCASE")) {
+ // Capitalization variants are not allowed for this word.
+ return false;
+ }
+
+ // Check for a lowercase form
+ if (this.checkExact(lowercaseWord)) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ /**
+ * Checks whether a word exists in the current dictionary.
+ *
+ * @param {String} word The word to check.
+ * @returns {Boolean}
+ */
+
+ checkExact : function (word) {
+ var ruleCodes = this.dictionaryTable[word];
+
+ if (typeof ruleCodes === 'undefined') {
+ // Check if this might be a compound word.
+ if ("COMPOUNDMIN" in this.flags && word.length >= this.flags.COMPOUNDMIN) {
+ for (var i = 0, _len = this.compoundRules.length; i < _len; i++) {
+ if (word.match(this.compoundRules[i])) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+ else {
+ for (var i = 0, _len = ruleCodes.length; i < _len; i++) {
+ if (!this.hasFlag(word, "ONLYINCOMPOUND", ruleCodes[i])) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ },
+
+ /**
+ * Looks up whether a given word is flagged with a given flag.
+ *
+ * @param {String} word The word in question.
+ * @param {String} flag The flag in question.
+ * @return {Boolean}
+ */
+
+ hasFlag : function (word, flag, wordFlags) {
+ if (flag in this.flags) {
+ if (typeof wordFlags === 'undefined') {
+ var wordFlags = Array.prototype.concat.apply([], this.dictionaryTable[word]);
+ }
+
+ if (wordFlags && wordFlags.indexOf(this.flags[flag]) !== -1) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ /**
+ * Returns a list of suggestions for a misspelled word.
+ *
+ * @see http://www.norvig.com/spell-correct.html for the basis of this suggestor.
+ * This suggestor is primitive, but it works.
+ *
+ * @param {String} word The misspelling.
+ * @param {Number} [limit=5] The maximum number of suggestions to return.
+ * @returns {String[]} The array of suggestions.
+ */
+
+ alphabet : "",
+
+ suggest : function (word, limit) {
+ if (!limit) limit = 5;
+
+ if (this.check(word)) return [];
+
+ // Check the replacement table.
+ for (var i = 0, _len = this.replacementTable.length; i < _len; i++) {
+ var replacementEntry = this.replacementTable[i];
+
+ if (word.indexOf(replacementEntry[0]) !== -1) {
+ var correctedWord = word.replace(replacementEntry[0], replacementEntry[1]);
+
+ if (this.check(correctedWord)) {
+ return [ correctedWord ];
+ }
+ }
+ }
+
+ var self = this;
+ self.alphabet = "abcdefghijklmnopqrstuvwxyz";
+
+ /*
+ if (!self.alphabet) {
+ // Use the alphabet as implicitly defined by the words in the dictionary.
+ var alphaHash = {};
+
+ for (var i in self.dictionaryTable) {
+ for (var j = 0, _len = i.length; j < _len; j++) {
+ alphaHash[i[j]] = true;
+ }
+ }
+
+ for (var i in alphaHash) {
+ self.alphabet += i;
+ }
+
+ var alphaArray = self.alphabet.split("");
+ alphaArray.sort();
+ self.alphabet = alphaArray.join("");
+ }
+ */
+
+ function edits1(words) {
+ var rv = [];
+
+ for (var ii = 0, _iilen = words.length; ii < _iilen; ii++) {
+ var word = words[ii];
+
+ var splits = [];
+
+ for (var i = 0, _len = word.length + 1; i < _len; i++) {
+ splits.push([ word.substring(0, i), word.substring(i, word.length) ]);
+ }
+
+ var deletes = [];
+
+ for (var i = 0, _len = splits.length; i < _len; i++) {
+ var s = splits[i];
+
+ if (s[1]) {
+ deletes.push(s[0] + s[1].substring(1));
+ }
+ }
+
+ var transposes = [];
+
+ for (var i = 0, _len = splits.length; i < _len; i++) {
+ var s = splits[i];
+
+ if (s[1].length > 1) {
+ transposes.push(s[0] + s[1][1] + s[1][0] + s[1].substring(2));
+ }
+ }
+
+ var replaces = [];
+
+ for (var i = 0, _len = splits.length; i < _len; i++) {
+ var s = splits[i];
+
+ if (s[1]) {
+ for (var j = 0, _jlen = self.alphabet.length; j < _jlen; j++) {
+ replaces.push(s[0] + self.alphabet[j] + s[1].substring(1));
+ }
+ }
+ }
+
+ var inserts = [];
+
+ for (var i = 0, _len = splits.length; i < _len; i++) {
+ var s = splits[i];
+
+ if (s[1]) {
+ for (var j = 0, _jlen = self.alphabet.length; j < _jlen; j++) {
+ replaces.push(s[0] + self.alphabet[j] + s[1]);
+ }
+ }
+ }
+
+ rv = rv.concat(deletes);
+ rv = rv.concat(transposes);
+ rv = rv.concat(replaces);
+ rv = rv.concat(inserts);
+ }
+
+ return rv;
+ }
+
+ function known(words) {
+ var rv = [];
+
+ for (var i = 0; i < words.length; i++) {
+ if (self.check(words[i])) {
+ rv.push(words[i]);
+ }
+ }
+
+ return rv;
+ }
+
+ function correct(word) {
+ // Get the edit-distance-1 and edit-distance-2 forms of this word.
+ var ed1 = edits1([word]);
+ var ed2 = edits1(ed1);
+
+ var corrections = known(ed1).concat(known(ed2));
+
+ // Sort the edits based on how many different ways they were created.
+ var weighted_corrections = {};
+
+ for (var i = 0, _len = corrections.length; i < _len; i++) {
+ if (!(corrections[i] in weighted_corrections)) {
+ weighted_corrections[corrections[i]] = 1;
+ }
+ else {
+ weighted_corrections[corrections[i]] += 1;
+ }
+ }
+
+ var sorted_corrections = [];
+
+ for (var i in weighted_corrections) {
+ sorted_corrections.push([ i, weighted_corrections[i] ]);
+ }
+
+ function sorter(a, b) {
+ if (a[1] < b[1]) {
+ return -1;
+ }
+
+ return 1;
+ }
+
+ sorted_corrections.sort(sorter).reverse();
+
+ var rv = [];
+
+ for (var i = 0, _len = Math.min(limit, sorted_corrections.length); i < _len; i++) {
+ if (!self.hasFlag(sorted_corrections[i][0], "NOSUGGEST")) {
+ rv.push(sorted_corrections[i][0]);
+ }
+ }
+
+ return rv;
+ }
+
+ return correct(word);
+ }
+};
+; browserify_shim__define__module__export__(typeof Typo != "undefined" ? Typo : window.Typo);
+
+}).call(global, undefined, undefined, undefined, undefined, function defineExport(ex) { module.exports = ex; });
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}],3:[function(require,module,exports){
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://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("fullScreen", false, function(cm, val, old) {
+ if (old == CodeMirror.Init) old = false;
+ if (!old == !val) return;
+ if (val) setFullscreen(cm);
+ else setNormal(cm);
+ });
+
+ function setFullscreen(cm) {
+ var wrap = cm.getWrapperElement();
+ cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset,
+ width: wrap.style.width, height: wrap.style.height};
+ wrap.style.width = "";
+ wrap.style.height = "auto";
+ wrap.className += " CodeMirror-fullscreen";
+ document.documentElement.style.overflow = "hidden";
+ cm.refresh();
+ }
+
+ function setNormal(cm) {
+ var wrap = cm.getWrapperElement();
+ wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, "");
+ document.documentElement.style.overflow = "";
+ var info = cm.state.fullScreenRestore;
+ wrap.style.width = info.width; wrap.style.height = info.height;
+ window.scrollTo(info.scrollLeft, info.scrollTop);
+ cm.refresh();
+ }
+});
+
+},{"../../lib/codemirror":6}],4:[function(require,module,exports){
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://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*)(>[> ]*|[*+-]\s|(\d+)([.)]))(\s*)/,
+ emptyListRE = /^(\s*)(>[> ]*|[*+-]|(\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;
+ var eolState = cm.getStateAfter(pos.line);
+ var inList = eolState.list !== false;
+ var inQuote = eolState.quote !== 0;
+
+ var line = cm.getLine(pos.line), match = listRE.exec(line);
+ if (!ranges[i].empty() || (!inList && !inQuote) || !match) {
+ cm.execCommand("newlineAndIndent");
+ return;
+ }
+ if (emptyListRE.test(line)) {
+ 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 bullet = unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0
+ ? match[2]
+ : (parseInt(match[3], 10) + 1) + match[4];
+
+ replacements[i] = "\n" + indent + bullet + after;
+ }
+ }
+
+ cm.replaceSelections(replacements);
+ };
+});
+
+},{"../../lib/codemirror":6}],5:[function(require,module,exports){
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+// Utility function that allows modes to be combined. The mode given
+// as the base argument takes care of most of the normal mode
+// functionality, but a second (typically simple) mode is used, which
+// can override the style of text. Both modes get to parse all of the
+// text, but when both assign a non-null style to a piece of code, the
+// overlay wins, unless the combine argument was true and not overridden,
+// or state.overlay.combineTokens was true, in which case the styles are
+// combined.
+
+(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.overlayMode = function(base, overlay, combine) {
+ return {
+ startState: function() {
+ return {
+ base: CodeMirror.startState(base),
+ overlay: CodeMirror.startState(overlay),
+ basePos: 0, baseCur: null,
+ overlayPos: 0, overlayCur: null,
+ streamSeen: null
+ };
+ },
+ copyState: function(state) {
+ return {
+ base: CodeMirror.copyState(base, state.base),
+ overlay: CodeMirror.copyState(overlay, state.overlay),
+ basePos: state.basePos, baseCur: null,
+ overlayPos: state.overlayPos, overlayCur: null
+ };
+ },
+
+ token: function(stream, state) {
+ if (stream != state.streamSeen ||
+ Math.min(state.basePos, state.overlayPos) < stream.start) {
+ state.streamSeen = stream;
+ state.basePos = state.overlayPos = stream.start;
+ }
+
+ if (stream.start == state.basePos) {
+ state.baseCur = base.token(stream, state.base);
+ state.basePos = stream.pos;
+ }
+ if (stream.start == state.overlayPos) {
+ stream.pos = stream.start;
+ state.overlayCur = overlay.token(stream, state.overlay);
+ state.overlayPos = stream.pos;
+ }
+ stream.pos = Math.min(state.basePos, state.overlayPos);
+
+ // state.overlay.combineTokens always takes precedence over combine,
+ // unless set to null
+ if (state.overlayCur == null) return state.baseCur;
+ else if (state.baseCur != null &&
+ state.overlay.combineTokens ||
+ combine && state.overlay.combineTokens == null)
+ return state.baseCur + " " + state.overlayCur;
+ else return state.overlayCur;
+ },
+
+ indent: base.indent && function(state, textAfter) {
+ return base.indent(state.base, textAfter);
+ },
+ electricChars: base.electricChars,
+
+ innerMode: function(state) { return {state: state.base, mode: base}; },
+
+ blankLine: function(state) {
+ if (base.blankLine) base.blankLine(state.base);
+ if (overlay.blankLine) overlay.blankLine(state.overlay);
+ }
+ };
+};
+
+});
+
+},{"../../lib/codemirror":6}],6:[function(require,module,exports){
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+// This is CodeMirror (http://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(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ module.exports = mod();
+ else if (typeof define == "function" && define.amd) // AMD
+ return define([], mod);
+ else // Plain browser env
+ this.CodeMirror = mod();
+})(function() {
+ "use strict";
+
+ // BROWSER SNIFFING
+
+ // 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 ie = ie_upto10 || ie_11up;
+ var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]);
+ var webkit = /WebKit\//.test(userAgent);
+ var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
+ var chrome = /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 = /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.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 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);
+
+ // Optimize some code when these features are not used.
+ var sawReadOnlySpans = false, sawCollapsedSpans = false;
+
+ // EDITOR CONSTRUCTOR
+
+ // A CodeMirror instance represents an editor. This is the object
+ // that user code is usually dealing with.
+
+ function CodeMirror(place, options) {
+ 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);
+ setGuttersForLineNumbers(options);
+
+ var doc = options.value;
+ if (typeof doc == "string") doc = new Doc(doc, options.mode, null, options.lineSeparator);
+ this.doc = doc;
+
+ var input = new CodeMirror.inputStyles[options.inputStyle](this);
+ var display = this.display = new Display(place, doc, input);
+ display.wrapper.CodeMirror = this;
+ updateGutters(this);
+ themeChanged(this);
+ if (options.lineWrapping)
+ this.display.wrapper.className += " CodeMirror-wrap";
+ if (options.autofocus && !mobile) display.input.focus();
+ 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: false, cutIncoming: false, // 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
+ };
+
+ var cm = this;
+
+ // Override magic textarea content restore that IE sometimes does
+ // on our hidden textarea on reload
+ if (ie && ie_version < 11) setTimeout(function() { cm.display.input.reset(true); }, 20);
+
+ registerEventHandlers(this);
+ ensureGlobalHandlers();
+
+ startOperation(this);
+ this.curOp.forceUpdate = true;
+ attachDoc(this, doc);
+
+ if ((options.autofocus && !mobile) || cm.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";
+ }
+
+ // DISPLAY CONSTRUCTOR
+
+ // 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) {
+ 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 = elt("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 = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
+ null, "position: relative; outline: none");
+ // Moved around its parent to cover visible view.
+ d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-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;
+
+ input.init(d);
+ }
+
+ // STATE UPDATES
+
+ // Used to get the editor into a consistent state again when options change.
+
+ function loadMode(cm) {
+ cm.doc.mode = CodeMirror.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.frontier = cm.doc.first;
+ startWorker(cm, 100);
+ cm.state.modeGen++;
+ if (cm.curOp) regChange(cm);
+ }
+
+ 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(){updateScrollbars(cm);}, 100);
+ }
+
+ // 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);
+ });
+ }
+
+ 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);
+ }
+
+ function guttersChanged(cm) {
+ updateGutters(cm);
+ regChange(cm);
+ setTimeout(function(){alignHorizontally(cm);}, 20);
+ }
+
+ // Rebuild the gutter elements, ensure the margin to the left of the
+ // code matches their width.
+ function updateGutters(cm) {
+ var gutters = cm.display.gutters, specs = cm.options.gutters;
+ removeChildren(gutters);
+ for (var i = 0; i < specs.length; ++i) {
+ var gutterClass = specs[i];
+ var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
+ if (gutterClass == "CodeMirror-linenumbers") {
+ cm.display.lineGutter = gElt;
+ gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
+ }
+ }
+ gutters.style.display = i ? "" : "none";
+ updateGutterSpace(cm);
+ }
+
+ function updateGutterSpace(cm) {
+ var width = cm.display.gutters.offsetWidth;
+ cm.display.sizer.style.marginLeft = width + "px";
+ }
+
+ // 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 = merged.find(0, true);
+ len -= cur.text.length - found.from.ch;
+ cur = found.to.line;
+ len += cur.text.length - found.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;
+ }
+ });
+ }
+
+ // Make sure the gutters options contains the element
+ // "CodeMirror-linenumbers" when the lineNumbers option is true.
+ function setGuttersForLineNumbers(options) {
+ var found = indexOf(options.gutters, "CodeMirror-linenumbers");
+ if (found == -1 && options.lineNumbers) {
+ options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
+ } else if (found > -1 && !options.lineNumbers) {
+ options.gutters = options.gutters.slice(0);
+ options.gutters.splice(found, 1);
+ }
+ }
+
+ // 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
+ };
+ }
+
+ function NativeScrollbars(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");
+ 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.checkedOverlay = 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 = copyObj({
+ 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 =
+ (measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
+ } else {
+ this.horiz.style.display = "";
+ this.horiz.firstChild.style.width = "0";
+ }
+
+ if (!this.checkedOverlay && measure.clientHeight > 0) {
+ if (sWidth == 0) this.overlayHack();
+ this.checkedOverlay = true;
+ }
+
+ return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0};
+ },
+ setScrollLeft: function(pos) {
+ if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos;
+ },
+ setScrollTop: function(pos) {
+ if (this.vert.scrollTop != pos) this.vert.scrollTop = pos;
+ },
+ overlayHack: function() {
+ var w = mac && !mac_geMountainLion ? "12px" : "18px";
+ this.horiz.style.minHeight = this.vert.style.minWidth = w;
+ var self = this;
+ var barMouseDown = function(e) {
+ if (e_target(e) != self.vert && e_target(e) != self.horiz)
+ operation(self.cm, onMouseDown)(e);
+ };
+ on(this.vert, "mousedown", barMouseDown);
+ on(this.horiz, "mousedown", barMouseDown);
+ },
+ clear: function() {
+ var parent = this.horiz.parentNode;
+ parent.removeChild(this.horiz);
+ parent.removeChild(this.vert);
+ }
+ }, NativeScrollbars.prototype);
+
+ function NullScrollbars() {}
+
+ NullScrollbars.prototype = copyObj({
+ update: function() { return {bottom: 0, right: 0}; },
+ setScrollLeft: function() {},
+ setScrollTop: function() {},
+ clear: function() {}
+ }, NullScrollbars.prototype);
+
+ CodeMirror.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 CodeMirror.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() { cm.display.input.focus(); }, 0);
+ });
+ node.setAttribute("cm-not-content", "true");
+ }, function(pos, axis) {
+ if (axis == "horizontal") setScrollLeft(cm, pos);
+ else setScrollTop(cm, pos);
+ }, cm);
+ if (cm.display.scrollbars.addClass)
+ addClass(cm.display.wrapper, cm.display.scrollbars.addClass);
+ }
+
+ 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";
+
+ 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 = "";
+ }
+
+ // 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)};
+ }
+
+ // LINE NUMBERS
+
+ // 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 && view[i].gutter)
+ view[i].gutter.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);
+ return true;
+ }
+ return false;
+ }
+
+ function lineNumberFor(options, i) {
+ return String(options.lineNumberFormatter(i + options.firstLineNumber));
+ }
+
+ // 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;
+ }
+
+ // DISPLAY DRAWING
+
+ function DisplayUpdate(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;
+ }
+ }
+
+ // 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 focused = activeElt();
+ 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.
+ if (focused && activeElt() != focused && focused.offsetHeight) focused.focus();
+
+ // 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;
+ }
+ if (!updateDisplayIfNeeded(cm, update)) break;
+ updateHeightsInViewport(cm);
+ var barMeasure = measureForScrollbars(cm);
+ updateSelection(cm);
+ setDocumentHeight(cm, barMeasure);
+ updateScrollbars(cm, barMeasure);
+ }
+
+ 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);
+ setDocumentHeight(cm, barMeasure);
+ updateScrollbars(cm, barMeasure);
+ update.finish();
+ }
+ }
+
+ function setDocumentHeight(cm, measure) {
+ cm.display.sizer.style.minHeight = measure.docHeight + "px";
+ var total = measure.docHeight + cm.display.barHeight;
+ cm.display.heightForcer.style.top = total + "px";
+ cm.display.gutters.style.height = Math.max(total + scrollGap(cm), measure.clientHeight) + "px";
+ }
+
+ // 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], height;
+ 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;
+ }
+ var diff = cur.line.height - height;
+ if (height < 2) height = textHeight(display);
+ if (diff > .001 || diff < -.001) {
+ updateLineHeight(cur.line, height);
+ updateWidgetHeight(cur.line);
+ if (cur.rest) for (var j = 0; j < cur.rest.length; j++)
+ updateWidgetHeight(cur.rest[j]);
+ }
+ }
+ }
+
+ // 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)
+ line.widgets[i].height = line.widgets[i].node.offsetHeight;
+ }
+
+ // 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) {
+ left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
+ width[cm.options.gutters[i]] = n.clientWidth;
+ }
+ return {fixedPos: compensateForHScroll(d),
+ gutterTotalWidth: d.gutters.offsetWidth,
+ gutterLeft: left,
+ gutterWidth: width,
+ wrapperWidth: d.wrapper.clientWidth};
+ }
+
+ // 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);
+ }
+
+ // 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(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(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);
+ }
+ }
+
+ // 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(lineView);
+ } else if (cls) {
+ lineView.text.className = cls;
+ }
+ }
+
+ function updateLineClasses(lineView) {
+ updateLineBackground(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");
+ wrap.insertBefore(lineView.gutterBackground, lineView.text);
+ }
+ var markers = lineView.line.gutterMarkers;
+ if (cm.options.lineNumbers || markers) {
+ var wrap = 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.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.options.gutters.length; ++k) {
+ var id = cm.options.gutters[k], 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;
+ for (var node = lineView.node.firstChild, next; node; node = next) {
+ var next = node.nextSibling;
+ if (node.className == "CodeMirror-linewidget")
+ 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(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");
+ 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";
+ }
+ }
+
+ // POSITION OBJECT
+
+ // A Pos instance represents a position within the text.
+ var Pos = CodeMirror.Pos = function(line, ch) {
+ if (!(this instanceof Pos)) return new Pos(line, ch);
+ this.line = line; this.ch = ch;
+ };
+
+ // Compare two positions, return 0 if they are the same, a negative
+ // number when a is less, and a positive number otherwise.
+ var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; };
+
+ 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; }
+
+ // INPUT HANDLING
+
+ function ensureFocus(cm) {
+ if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
+ }
+
+ function isReadOnly(cm) {
+ return cm.options.readOnly || cm.doc.cantEdit;
+ }
+
+ // This will be set to an array of strings when copying, so that,
+ // when pasting, we know what kind of selections the copied text
+ // was made out of.
+ var lastCopied = null;
+
+ function applyTextInput(cm, inserted, deleted, sel, origin) {
+ var doc = cm.doc;
+ cm.display.shift = false;
+ if (!sel) sel = doc.sel;
+
+ var paste = cm.state.pasteIncoming || origin == "paste";
+ var textLines = doc.splitLines(inserted), multiPaste = null;
+ // When pasing N lines into N selections, insert one line per selection
+ if (paste && sel.ranges.length > 1) {
+ if (lastCopied && lastCopied.join("\n") == inserted) {
+ if (sel.ranges.length % lastCopied.length == 0) {
+ multiPaste = [];
+ for (var i = 0; i < lastCopied.length; i++)
+ multiPaste.push(doc.splitLines(lastCopied[i]));
+ }
+ } else if (textLines.length == sel.ranges.length) {
+ multiPaste = map(textLines, function(l) { return [l]; });
+ }
+ }
+
+ // Normal behavior is to insert the new text into every selection
+ for (var i = sel.ranges.length - 1; i >= 0; i--) {
+ var range = sel.ranges[i];
+ 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));
+ }
+ var updateInput = cm.curOp.updateInput;
+ var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
+ origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")};
+ makeChange(cm.doc, changeEvent);
+ signalLater(cm, "inputRead", cm, changeEvent);
+ }
+ if (inserted && !paste)
+ triggerElectric(cm, inserted);
+
+ ensureCursorVisible(cm);
+ cm.curOp.updateInput = updateInput;
+ cm.curOp.typing = true;
+ cm.state.pasteIncoming = cm.state.cutIncoming = false;
+ }
+
+ function handlePaste(e, cm) {
+ var pasted = e.clipboardData && e.clipboardData.getData("text/plain");
+ if (pasted) {
+ e.preventDefault();
+ if (!isReadOnly(cm) && !cm.options.disableInput)
+ runInOp(cm, function() { 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) {
+ field.setAttribute("autocorrect", "off");
+ field.setAttribute("autocapitalize", "off");
+ field.setAttribute("spellcheck", "false");
+ }
+
+ // TEXTAREA INPUT STYLE
+
+ function TextareaInput(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();
+ // Tracks when input.reset has punted to just putting a short
+ // string into the textarea instead of the full selection.
+ this.inaccurateSelection = false;
+ // Used to work around IE issue with selection being forgotten when focus moves away from textarea
+ this.hasSelection = false;
+ this.composing = null;
+ };
+
+ function hiddenTextarea() {
+ var te = elt("textarea", null, null, "position: absolute; 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;
+ }
+
+ TextareaInput.prototype = copyObj({
+ init: function(display) {
+ var input = this, cm = this.cm;
+
+ // Wraps and hides input textarea
+ var div = this.wrapper = hiddenTextarea();
+ // The semihidden textarea that is focused when the editor is
+ // focused, and receives input.
+ var te = this.textarea = div.firstChild;
+ display.wrapper.insertBefore(div, 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 && input.hasSelection) input.hasSelection = null;
+ input.poll();
+ });
+
+ on(te, "paste", function(e) {
+ if (handlePaste(e, cm)) return true;
+
+ cm.state.pasteIncoming = true;
+ input.fastPoll();
+ });
+
+ function prepareCopyCut(e) {
+ if (cm.somethingSelected()) {
+ lastCopied = cm.getSelections();
+ if (input.inaccurateSelection) {
+ input.prevInput = "";
+ input.inaccurateSelection = false;
+ te.value = lastCopied.join("\n");
+ selectInput(te);
+ }
+ } else if (!cm.options.lineWiseCopyCut) {
+ return;
+ } else {
+ var ranges = copyableRanges(cm);
+ lastCopied = 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 = true;
+ }
+ on(te, "cut", prepareCopyCut);
+ on(te, "copy", prepareCopyCut);
+
+ on(display.scroller, "paste", function(e) {
+ if (eventInWidget(display, e)) return;
+ cm.state.pasteIncoming = true;
+ input.focus();
+ });
+
+ // 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;
+ }
+ });
+ },
+
+ 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;
+ },
+
+ 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)
+ reset: function(typing) {
+ if (this.contextMenuPending) return;
+ var minimal, selected, cm = this.cm, doc = cm.doc;
+ if (cm.somethingSelected()) {
+ this.prevInput = "";
+ var range = doc.sel.primary();
+ minimal = hasCopyEvent &&
+ (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000);
+ var content = minimal ? "-" : selected || 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;
+ }
+ this.inaccurateSelection = minimal;
+ },
+
+ getField: function() { return this.textarea; },
+
+ supportsTouch: function() { return false; },
+
+ 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
+ }
+ },
+
+ blur: function() { this.textarea.blur(); },
+
+ resetPosition: function() {
+ this.wrapper.style.top = this.wrapper.style.left = 0;
+ },
+
+ receivedFocus: function() { this.slowPoll(); },
+
+ // Poll for input changes, using the normal rate of polling. This
+ // runs as long as the editor is focused.
+ slowPoll: function() {
+ var input = this;
+ if (input.pollingFast) return;
+ input.polling.set(this.cm.options.pollInterval, function() {
+ input.poll();
+ if (input.cm.state.focused) input.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.
+ 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).
+ poll: function() {
+ 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) ||
+ isReadOnly(cm) || 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;
+
+ var self = this;
+ runInOp(cm, function() {
+ applyTextInput(cm, text.slice(same), prevInput.length - same,
+ null, self.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 = self.prevInput = "";
+ else self.prevInput = text;
+
+ if (self.composing) {
+ self.composing.range.clear();
+ self.composing.range = cm.markText(self.composing.start, cm.getCursor("to"),
+ {className: "CodeMirror-composing"});
+ }
+ });
+ return true;
+ },
+
+ ensurePolled: function() {
+ if (this.pollingFast && this.poll()) this.pollingFast = false;
+ },
+
+ onKeyPress: function() {
+ if (ie && ie_version >= 9) this.hasSelection = null;
+ this.fastPoll();
+ },
+
+ onContextMenu: function(e) {
+ var input = this, cm = input.cm, display = cm.display, te = input.textarea;
+ 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;
+ input.wrapper.style.position = "absolute";
+ te.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
+ "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " +
+ (ie ? "rgba(255, 255, 255, .05)" : "transparent") +
+ "; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
+ if (webkit) var 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 = true;
+ 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() {
+ input.contextMenuPending = false;
+ input.wrapper.style.position = "relative";
+ 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, commands.selectAll)(cm);
+ else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500);
+ else 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);
+ }
+ },
+
+ readOnlyChanged: function(val) {
+ if (!val) this.reset();
+ },
+
+ setUneditable: nothing,
+
+ needsContentAttribute: false
+ }, TextareaInput.prototype);
+
+ // CONTENTEDITABLE INPUT STYLE
+
+ function ContentEditableInput(cm) {
+ this.cm = cm;
+ this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
+ this.polling = new Delayed();
+ this.gracePeriod = false;
+ }
+
+ ContentEditableInput.prototype = copyObj({
+ init: function(display) {
+ var input = this, cm = input.cm;
+ var div = input.div = display.lineDiv;
+ disableBrowserMagic(div);
+
+ on(div, "paste", function(e) { handlePaste(e, cm); })
+
+ on(div, "compositionstart", function(e) {
+ var data = e.data;
+ input.composing = {sel: cm.doc.sel, data: data, startData: data};
+ if (!data) return;
+ var prim = cm.doc.sel.primary();
+ var line = cm.getLine(prim.head.line);
+ var found = line.indexOf(data, Math.max(0, prim.head.ch - data.length));
+ if (found > -1 && found <= prim.head.ch)
+ input.composing.sel = simpleSelection(Pos(prim.head.line, found),
+ Pos(prim.head.line, found + data.length));
+ });
+ on(div, "compositionupdate", function(e) {
+ input.composing.data = e.data;
+ });
+ on(div, "compositionend", function(e) {
+ var ours = input.composing;
+ if (!ours) return;
+ if (e.data != ours.startData && !/\u200b/.test(e.data))
+ ours.data = e.data;
+ // Need a small delay to prevent other code (input event,
+ // selection polling) from doing damage when fired right after
+ // compositionend.
+ setTimeout(function() {
+ if (!ours.handled)
+ input.applyComposition(ours);
+ if (input.composing == ours)
+ input.composing = null;
+ }, 50);
+ });
+
+ on(div, "touchstart", function() {
+ input.forceCompositionEnd();
+ });
+
+ on(div, "input", function() {
+ if (input.composing) return;
+ if (isReadOnly(cm) || !input.pollContent())
+ runInOp(input.cm, function() {regChange(cm);});
+ });
+
+ function onCopyCut(e) {
+ if (cm.somethingSelected()) {
+ lastCopied = cm.getSelections();
+ if (e.type == "cut") cm.replaceSelection("", null, "cut");
+ } else if (!cm.options.lineWiseCopyCut) {
+ return;
+ } else {
+ var ranges = copyableRanges(cm);
+ lastCopied = ranges.text;
+ if (e.type == "cut") {
+ cm.operation(function() {
+ cm.setSelections(ranges.ranges, 0, sel_dontScroll);
+ cm.replaceSelection("", null, "cut");
+ });
+ }
+ }
+ // iOS exposes the clipboard API, but seems to discard content inserted into it
+ if (e.clipboardData && !ios) {
+ e.preventDefault();
+ e.clipboardData.clearData();
+ e.clipboardData.setData("text/plain", lastCopied.join("\n"));
+ } else {
+ // 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.join("\n");
+ var hadFocus = document.activeElement;
+ selectInput(te);
+ setTimeout(function() {
+ cm.display.lineSpace.removeChild(kludge);
+ hadFocus.focus();
+ }, 50);
+ }
+ }
+ on(div, "copy", onCopyCut);
+ on(div, "cut", onCopyCut);
+ },
+
+ prepareSelection: function() {
+ var result = prepareSelection(this.cm, false);
+ result.focus = this.cm.state.focused;
+ return result;
+ },
+
+ showSelection: function(info) {
+ if (!info || !this.cm.display.view.length) return;
+ if (info.focus) this.showPrimarySelection();
+ this.showMultipleSelections(info);
+ },
+
+ showPrimarySelection: function() {
+ var sel = window.getSelection(), prim = this.cm.doc.sel.primary();
+ var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset);
+ var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset);
+ if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
+ cmp(minPos(curAnchor, curFocus), prim.from()) == 0 &&
+ cmp(maxPos(curAnchor, curFocus), prim.to()) == 0)
+ return;
+
+ var start = posToDOM(this.cm, prim.from());
+ var end = posToDOM(this.cm, prim.to());
+ if (!start && !end) return;
+
+ var view = this.cm.display.view;
+ var old = sel.rangeCount && sel.getRangeAt(0);
+ if (!start) {
+ start = {node: view[0].measure.map[2], offset: 0};
+ } else if (!end) { // FIXME dangerously hacky
+ 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]};
+ }
+
+ try { var 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) {
+ sel.removeAllRanges();
+ sel.addRange(rng);
+ if (old && sel.anchorNode == null) sel.addRange(old);
+ else if (gecko) this.startGracePeriod();
+ }
+ this.rememberSelection();
+ },
+
+ startGracePeriod: function() {
+ var input = this;
+ clearTimeout(this.gracePeriod);
+ this.gracePeriod = setTimeout(function() {
+ input.gracePeriod = false;
+ if (input.selectionChanged())
+ input.cm.operation(function() { input.cm.curOp.selectionChanged = true; });
+ }, 20);
+ },
+
+ showMultipleSelections: function(info) {
+ removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
+ removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
+ },
+
+ rememberSelection: function() {
+ var sel = window.getSelection();
+ this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
+ this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
+ },
+
+ selectionInEditor: function() {
+ var sel = window.getSelection();
+ if (!sel.rangeCount) return false;
+ var node = sel.getRangeAt(0).commonAncestorContainer;
+ return contains(this.div, node);
+ },
+
+ focus: function() {
+ if (this.cm.options.readOnly != "nocursor") this.div.focus();
+ },
+ blur: function() { this.div.blur(); },
+ getField: function() { return this.div; },
+
+ supportsTouch: function() { return true; },
+
+ receivedFocus: function() {
+ var input = this;
+ if (this.selectionInEditor())
+ this.pollSelection();
+ else
+ runInOp(this.cm, function() { 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);
+ },
+
+ selectionChanged: function() {
+ var sel = window.getSelection();
+ return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
+ sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset;
+ },
+
+ pollSelection: function() {
+ if (!this.composing && !this.gracePeriod && this.selectionChanged()) {
+ var sel = window.getSelection(), cm = this.cm;
+ 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;
+ });
+ }
+ },
+
+ pollContent: function() {
+ var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
+ var from = sel.from(), to = sel.to();
+ if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false;
+
+ var fromIndex;
+ if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
+ var fromLine = lineNo(display.view[0].line);
+ var fromNode = display.view[0].node;
+ } else {
+ var fromLine = lineNo(display.view[fromIndex].line);
+ var fromNode = display.view[fromIndex - 1].node.nextSibling;
+ }
+ var toIndex = findViewIndex(cm, to.line);
+ if (toIndex == display.view.length - 1) {
+ var toLine = display.viewTo - 1;
+ var toNode = display.lineDiv.lastChild;
+ } else {
+ var toLine = lineNo(display.view[toIndex + 1].line) - 1;
+ var toNode = display.view[toIndex + 1].node.previousSibling;
+ }
+
+ 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;
+
+ newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd);
+ newText[0] = newText[0].slice(cutFront);
+
+ 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;
+ }
+ },
+
+ ensurePolled: function() {
+ this.forceCompositionEnd();
+ },
+ reset: function() {
+ this.forceCompositionEnd();
+ },
+ forceCompositionEnd: function() {
+ if (!this.composing || this.composing.handled) return;
+ this.applyComposition(this.composing);
+ this.composing.handled = true;
+ this.div.blur();
+ this.div.focus();
+ },
+ applyComposition: function(composing) {
+ if (isReadOnly(this.cm))
+ operation(this.cm, regChange)(this.cm)
+ else if (composing.data && composing.data != composing.startData)
+ operation(this.cm, applyTextInput)(this.cm, composing.data, 0, composing.sel);
+ },
+
+ setUneditable: function(node) {
+ node.contentEditable = "false"
+ },
+
+ onKeyPress: function(e) {
+ e.preventDefault();
+ if (!isReadOnly(this.cm))
+ operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0);
+ },
+
+ readOnlyChanged: function(val) {
+ this.div.contentEditable = String(val != "nocursor")
+ },
+
+ onContextMenu: nothing,
+ resetPosition: nothing,
+
+ needsContentAttribute: true
+ }, ContentEditableInput.prototype);
+
+ 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), 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 badPos(pos, bad) { if (bad) pos.bad = true; return pos; }
+
+ 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 = offset; before; before = before.previousSibling) {
+ found = find(before, before.firstChild, -1);
+ if (found)
+ return badPos(Pos(found.line, found.ch + dist), bad);
+ else
+ dist += after.textContent.length;
+ }
+ }
+
+ function domTextBetween(cm, from, to, fromLine, toLine) {
+ var text = "", closing = false, lineSep = cm.doc.lineSeparator();
+ function recognizeMarker(id) { return function(marker) { return marker.id == id; }; }
+ function walk(node) {
+ if (node.nodeType == 1) {
+ var cmText = node.getAttribute("cm-text");
+ if (cmText != null) {
+ if (cmText == "") cmText = node.textContent.replace(/\u200b/g, "");
+ text += 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()))
+ text += getBetween(cm.doc, range.from, range.to).join(lineSep);
+ return;
+ }
+ if (node.getAttribute("contenteditable") == "false") return;
+ for (var i = 0; i < node.childNodes.length; i++)
+ walk(node.childNodes[i]);
+ if (/^(pre|div|p)$/i.test(node.nodeName))
+ closing = true;
+ } else if (node.nodeType == 3) {
+ var val = node.nodeValue;
+ if (!val) return;
+ if (closing) {
+ text += lineSep;
+ closing = false;
+ }
+ text += val;
+ }
+ }
+ for (;;) {
+ walk(from);
+ if (from == to) break;
+ from = from.nextSibling;
+ }
+ return text;
+ }
+
+ CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
+
+ // SELECTION / CURSOR
+
+ // 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).
+ function Selection(ranges, primIndex) {
+ this.ranges = ranges;
+ this.primIndex = primIndex;
+ }
+
+ Selection.prototype = {
+ primary: function() { return this.ranges[this.primIndex]; },
+ 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 (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false;
+ }
+ return true;
+ },
+ deepCopy: function() {
+ for (var out = [], 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);
+ },
+ somethingSelected: function() {
+ for (var i = 0; i < this.ranges.length; i++)
+ if (!this.ranges[i].empty()) return true;
+ return false;
+ },
+ 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;
+ }
+ };
+
+ function Range(anchor, head) {
+ this.anchor = anchor; this.head = head;
+ }
+
+ Range.prototype = {
+ from: function() { return minPos(this.anchor, this.head); },
+ to: function() { return maxPos(this.anchor, this.head); },
+ 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(ranges, primIndex) {
+ 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];
+ if (cmp(prev.to(), cur.from()) >= 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);
+ }
+
+ // 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 isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
+ function clipPosArray(doc, array) {
+ for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]);
+ return out;
+ }
+
+ // SELECTION UPDATES
+
+ // 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(doc, range, head, other) {
+ if (doc.cm && doc.cm.display.shift || doc.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) {
+ setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options);
+ }
+
+ // Extend all selections (pos is an array of selections with length
+ // equal the number of selections)
+ function extendSelections(doc, heads, options) {
+ for (var out = [], i = 0; i < doc.sel.ranges.length; i++)
+ out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null);
+ var newSel = normalizeSelection(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(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) {
+ 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));
+ }
+ };
+ signal(doc, "beforeSelectionChange", doc, obj);
+ if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
+ if (obj.ranges != sel.ranges) return normalizeSelection(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);
+
+ 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 = 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), sel_dontScroll);
+ }
+
+ // 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 newAnchor = skipAtomic(doc, range.anchor, bias, mayClear);
+ var newHead = skipAtomic(doc, range.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(out, sel.primIndex) : sel;
+ }
+
+ // Ensure a given position is not inside an atomic range.
+ function skipAtomic(doc, pos, bias, mayClear) {
+ var flipped = false, curPos = pos;
+ var dir = bias || 1;
+ doc.cantEdit = false;
+ search: for (;;) {
+ var line = getLine(doc, curPos.line);
+ if (line.markedSpans) {
+ for (var i = 0; i < line.markedSpans.length; ++i) {
+ var sp = line.markedSpans[i], m = sp.marker;
+ if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
+ (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
+ if (mayClear) {
+ signal(m, "beforeCursorEnter");
+ if (m.explicitlyCleared) {
+ if (!line.markedSpans) break;
+ else {--i; continue;}
+ }
+ }
+ if (!m.atomic) continue;
+ var newPos = m.find(dir < 0 ? -1 : 1);
+ if (cmp(newPos, curPos) == 0) {
+ newPos.ch += dir;
+ if (newPos.ch < 0) {
+ if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
+ else newPos = null;
+ } else if (newPos.ch > line.text.length) {
+ if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
+ else newPos = null;
+ }
+ if (!newPos) {
+ if (flipped) {
+ // Driven in a corner -- no valid cursor position found at all
+ // -- try again *with* clearing, if we didn't already
+ if (!mayClear) return skipAtomic(doc, pos, bias, true);
+ // Otherwise, turn off editing until further notice, and return the start of the doc
+ doc.cantEdit = true;
+ return Pos(doc.first, 0);
+ }
+ flipped = true; newPos = pos; dir = -dir;
+ }
+ }
+ curPos = newPos;
+ continue search;
+ }
+ }
+ }
+ return curPos;
+ }
+ }
+
+ // SELECTION DRAWING
+
+ function updateSelection(cm) {
+ cm.display.input.showSelection(cm.display.input.prepareSelection());
+ }
+
+ function prepareSelection(cm, primary) {
+ 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 === false && i == doc.sel.primIndex) continue;
+ var range = doc.sel.ranges[i];
+ 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";
+ }
+ }
+
+ // 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;
+
+ 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; top: " + top + "px; width: " + (width == null ? rightSide - left : width) +
+ "px; 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);
+ }
+
+ iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
+ var leftPos = coords(from, "left"), rightPos, left, right;
+ if (from == to) {
+ rightPos = leftPos;
+ left = right = leftPos.left;
+ } else {
+ rightPos = coords(to - 1, "right");
+ if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
+ left = leftPos.left;
+ right = rightPos.right;
+ }
+ if (fromArg == null && from == 0) left = leftSide;
+ if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
+ add(left, leftPos.top, null, leftPos.bottom);
+ left = leftSide;
+ if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
+ }
+ if (toArg == null && to == lineLen) right = rightSide;
+ if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
+ start = leftPos;
+ if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
+ end = rightPos;
+ if (left < leftSide + 1) left = leftSide;
+ add(left, rightPos.top, right - left, rightPos.bottom);
+ });
+ 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() {
+ display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden";
+ }, cm.options.cursorBlinkRate);
+ else if (cm.options.cursorBlinkRate < 0)
+ display.cursorDiv.style.visibility = "hidden";
+ }
+
+ // HIGHLIGHT WORKER
+
+ function startWorker(cm, time) {
+ if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)
+ cm.state.highlight.set(time, bind(highlightWorker, cm));
+ }
+
+ function highlightWorker(cm) {
+ var doc = cm.doc;
+ if (doc.frontier < doc.first) doc.frontier = doc.first;
+ if (doc.frontier >= cm.display.viewTo) return;
+ var end = +new Date + cm.options.workTime;
+ var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
+ var changedLines = [];
+
+ doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) {
+ if (doc.frontier >= cm.display.viewFrom) { // Visible
+ var oldStyles = line.styles, tooLong = line.text.length > cm.options.maxHighlightLength;
+ var highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, state) : state, true);
+ 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(doc.frontier);
+ line.stateAfter = tooLong ? state : copyState(doc.mode, state);
+ } else {
+ if (line.text.length <= cm.options.maxHighlightLength)
+ processLine(cm, line.text, state);
+ line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
+ }
+ ++doc.frontier;
+ if (+new Date > end) {
+ startWorker(cm, cm.options.workDelay);
+ return true;
+ }
+ });
+ if (changedLines.length) runInOp(cm, function() {
+ for (var i = 0; i < changedLines.length; i++)
+ regLineChange(cm, changedLines[i], "text");
+ });
+ }
+
+ // 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);
+ if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
+ var indented = countColumn(line.text, null, cm.options.tabSize);
+ if (minline == null || minindent > indented) {
+ minline = search - 1;
+ minindent = indented;
+ }
+ }
+ return minline;
+ }
+
+ function getStateBefore(cm, n, precise) {
+ var doc = cm.doc, display = cm.display;
+ if (!doc.mode.startState) return true;
+ var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
+ if (!state) state = startState(doc.mode);
+ else state = copyState(doc.mode, state);
+ doc.iter(pos, n, function(line) {
+ processLine(cm, line.text, state);
+ var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo;
+ line.stateAfter = save ? copyState(doc.mode, state) : null;
+ ++pos;
+ });
+ if (precise) doc.frontier = pos;
+ return state;
+ }
+
+ // 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"));
+ 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 = 0; i < lineView.rest.length; i++)
+ if (lineNo(lineView.rest[i]) > lineN)
+ return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], 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;
+ // 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) {
+ var 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 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 = 0; i < 4; i++) { // 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 if (ie && cm.options.lineWrapping) {
+ var rects = range(node, start, end).getClientRects();
+ if (rects.length)
+ rect = rects[bias == "right" ? rects.length - 1 : 0];
+ else
+ rect = nullRect;
+ } else {
+ rect = range(node, start, end).getBoundingClientRect() || nullRect;
+ }
+ 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;
+ for (var i = 0; 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() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
+ function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
+
+ // 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) {
+ if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
+ var size = widgetHeight(lineObj.widgets[i]);
+ rect.top += size; rect.bottom += size;
+ }
+ 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.
+ 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);
+ }
+ function getBidi(ch, partPos) {
+ var part = order[partPos], right = part.level % 2;
+ if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
+ part = order[--partPos];
+ ch = bidiRight(part) - (part.level % 2 ? 0 : 1);
+ right = true;
+ } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
+ part = order[++partPos];
+ ch = bidiLeft(part) - part.level % 2;
+ right = false;
+ }
+ if (right && ch == part.to && ch > part.from) return get(ch - 1);
+ return get(ch, right);
+ }
+ var order = getOrder(lineObj), ch = pos.ch;
+ if (!order) return get(ch);
+ var partPos = getBidiPartAt(order, ch);
+ var val = getBidi(ch, partPos);
+ if (bidiOther != null) val.other = getBidi(ch, bidiOther);
+ 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, outside, xRel) {
+ var pos = Pos(line, ch);
+ pos.xRel = xRel;
+ if (outside) pos.outside = true;
+ 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, true, -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, true, 1);
+ if (x < 0) x = 0;
+
+ var lineObj = getLine(doc, lineN);
+ for (;;) {
+ var found = coordsCharInner(cm, lineObj, lineN, x, y);
+ var merged = collapsedSpanAtEnd(lineObj);
+ var mergedPos = merged && merged.find(0, true);
+ if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
+ lineN = lineNo(lineObj = mergedPos.to.line);
+ else
+ return found;
+ }
+ }
+
+ function coordsCharInner(cm, lineObj, lineNo, x, y) {
+ var innerOff = y - heightAtLine(lineObj);
+ var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
+ var preparedMeasure = prepareMeasureForLine(cm, lineObj);
+
+ function getX(ch) {
+ var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure);
+ wrongLine = true;
+ if (innerOff > sp.bottom) return sp.left - adjust;
+ else if (innerOff < sp.top) return sp.left + adjust;
+ else wrongLine = false;
+ return sp.left;
+ }
+
+ var bidi = getOrder(lineObj), dist = lineObj.text.length;
+ var from = lineLeft(lineObj), to = lineRight(lineObj);
+ var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
+
+ if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
+ // Do a binary search between these bounds.
+ for (;;) {
+ if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
+ var ch = x < fromX || x - fromX <= toX - x ? from : to;
+ var xDiff = x - (ch == from ? fromX : toX);
+ while (isExtendingChar(lineObj.text.charAt(ch))) ++ch;
+ var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
+ xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0);
+ return pos;
+ }
+ var step = Math.ceil(dist / 2), middle = from + step;
+ if (bidi) {
+ middle = from;
+ for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
+ }
+ var middleX = getX(middle);
+ if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
+ else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
+ }
+ }
+
+ var measureText;
+ // Compute the default text height.
+ function textHeight(display) {
+ if (display.cachedTextHeight != null) return display.cachedTextHeight;
+ if (measureText == null) {
+ measureText = elt("pre");
+ // 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]);
+ removeChildrenAndAdd(display.measure, pre);
+ var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
+ if (width > 2) display.cachedCharWidth = width;
+ return width || 10;
+ }
+
+ // OPERATIONS
+
+ // 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 operationGroup = null;
+
+ 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: null, // 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
+ };
+ if (operationGroup) {
+ operationGroup.ops.push(cm.curOp);
+ } else {
+ cm.curOp.ownsGroup = operationGroup = {
+ ops: [cm.curOp],
+ 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);
+ }
+
+ // Finish an operation, updating the display and signalling delayed events
+ function endOperation(cm) {
+ var op = cm.curOp, group = op.ownsGroup;
+ if (!group) return;
+
+ try { fireCallbacksForOps(group); }
+ finally {
+ operationGroup = null;
+ 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 = 0; i < ops.length; i++) // Write DOM (maybe)
+ endOperation_W1(ops[i]);
+ for (var i = 0; i < ops.length; i++) // Read DOM
+ endOperation_R2(ops[i]);
+ for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
+ endOperation_W2(ops[i]);
+ for (var i = 0; i < ops.length; i++) // Read DOM
+ endOperation_finish(ops[i]);
+ }
+
+ 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;
+ }
+
+ if (op.preparedSelection)
+ cm.display.input.showSelection(op.preparedSelection);
+ if (op.updatedDisplay)
+ setDocumentHeight(cm, op.barMeasure);
+ if (op.updatedDisplay || op.startHeight != cm.doc.height)
+ updateScrollbars(cm, op.barMeasure);
+
+ if (op.selectionChanged) restartBlink(cm);
+
+ if (cm.state.focused && op.updateInput)
+ cm.display.input.reset(op.typing);
+ if (op.focus && op.focus == activeElt()) 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 && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) {
+ doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop));
+ display.scrollbars.setScrollTop(doc.scrollTop);
+ display.scroller.scrollTop = doc.scrollTop;
+ }
+ if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {
+ doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - displayWidth(cm), op.scrollLeft));
+ display.scrollbars.setScrollLeft(doc.scrollLeft);
+ display.scroller.scrollLeft = doc.scrollLeft;
+ alignHorizontally(cm);
+ }
+ // If we need to scroll a specific position into view, do so.
+ if (op.scrollToPos) {
+ var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
+ clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
+ if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords);
+ }
+
+ // 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 = 0; i < unhidden.length; ++i)
+ if (unhidden[i].lines.length) signal(unhidden[i], "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); }
+ };
+ }
+
+ // VIEW TRACKING
+
+ // 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;
+ }
+
+ // 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 = viewCuttingPoint(cm, from, from, -1);
+ if (cut) {
+ display.view = display.view.slice(0, cut.index);
+ display.viewTo = cut.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;
+ }
+
+ // 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;
+ }
+ }
+
+ 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};
+ for (var i = 0, n = cm.display.viewFrom; 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;
+ }
+
+ // EVENT HANDLERS
+
+ // 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) { 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.
+ if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {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() {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 (!isMouseLikeTouchEvent(e)) {
+ 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) {
+ setScrollTop(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){onScrollWheel(cm, e);});
+ on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
+
+ // Prevent wrapper from ever scrolling
+ on(d.wrapper, "scroll", function() { 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){onDragStart(cm, e);},
+ drop: operation(cm, onDrop),
+ leave: function() {clearDragCursor(cm);}
+ };
+
+ var inp = d.input.getField();
+ on(inp, "keyup", function(e) { onKeyUp.call(cm, e); });
+ on(inp, "keydown", operation(cm, onKeyDown));
+ on(inp, "keypress", operation(cm, onKeyPress));
+ on(inp, "focus", bind(onFocus, cm));
+ on(inp, "blur", bind(onBlur, cm));
+ }
+
+ function dragDropChanged(cm, value, old) {
+ var wasOn = old && old != CodeMirror.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);
+ }
+ }
+
+ // Called when the window resizes
+ function onResize(cm) {
+ var d = cm.display;
+ if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
+ return;
+ // Might be a text scaling operation, clear size caches.
+ d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
+ d.scrollbarsClipped = false;
+ cm.setSize();
+ }
+
+ // MOUSE EVENTS
+
+ // 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;
+ }
+ }
+
+ // 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) { return null; }
+ var coords = coordsChar(cm, x, y), line;
+ if (forRect && coords.xRel == 1 && (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;
+ }
+
+ // 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 (display.activeTouch && display.input.supportsTouch() || signalDOMEvent(cm, e)) return;
+ 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(){display.scroller.draggable = true;}, 100);
+ }
+ return;
+ }
+ if (clickInGutter(cm, e)) return;
+ var start = posFromMouse(cm, e);
+ window.focus();
+
+ switch (e_button(e)) {
+ case 1:
+ // #3261: make sure, that we're not starting a second selection
+ if (cm.state.selectingText)
+ cm.state.selectingText(e);
+ else if (start)
+ leftButtonDown(cm, e, start);
+ else if (e_target(e) == display.scroller)
+ e_preventDefault(e);
+ break;
+ case 2:
+ if (webkit) cm.state.lastMiddleDown = +new Date;
+ if (start) extendSelection(cm.doc, start);
+ setTimeout(function() {display.input.focus();}, 20);
+ e_preventDefault(e);
+ break;
+ case 3:
+ if (captureRightClick) onContextMenu(cm, e);
+ else delayBlurEvent(cm);
+ break;
+ }
+ }
+
+ var lastClick, lastDoubleClick;
+ function leftButtonDown(cm, e, start) {
+ if (ie) setTimeout(bind(ensureFocus, cm), 0);
+ else cm.curOp.focus = activeElt();
+
+ var now = +new Date, type;
+ if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) {
+ type = "triple";
+ } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) {
+ type = "double";
+ lastDoubleClick = {time: now, pos: start};
+ } else {
+ type = "single";
+ lastClick = {time: now, pos: start};
+ }
+
+ var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained;
+ if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) &&
+ type == "single" && (contained = sel.contains(start)) > -1 &&
+ (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRel > 0) &&
+ (cmp(contained.to(), start) > 0 || start.xRel < 0))
+ leftButtonStartDrag(cm, e, start, modifier);
+ else
+ leftButtonSelect(cm, e, start, type, modifier);
+ }
+
+ // 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, e, start, modifier) {
+ var display = cm.display, startTime = +new Date;
+ var dragEnd = operation(cm, function(e2) {
+ if (webkit) display.scroller.draggable = false;
+ cm.state.draggingText = false;
+ off(document, "mouseup", dragEnd);
+ off(display.scroller, "drop", dragEnd);
+ if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
+ e_preventDefault(e2);
+ if (!modifier && +new Date - 200 < startTime)
+ extendSelection(cm.doc, start);
+ // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
+ if (webkit || ie && ie_version == 9)
+ setTimeout(function() {document.body.focus(); display.input.focus();}, 20);
+ else
+ display.input.focus();
+ }
+ });
+ // Let the drag handler handle this.
+ if (webkit) display.scroller.draggable = true;
+ cm.state.draggingText = dragEnd;
+ // IE's approach to draggable
+ if (display.scroller.dragDrop) display.scroller.dragDrop();
+ on(document, "mouseup", dragEnd);
+ on(display.scroller, "drop", dragEnd);
+ }
+
+ // Normal selection, as opposed to text dragging.
+ function leftButtonSelect(cm, e, start, type, addNew) {
+ var display = cm.display, doc = cm.doc;
+ e_preventDefault(e);
+
+ var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
+ if (addNew && !e.shiftKey) {
+ 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 (e.altKey) {
+ type = "rect";
+ if (!addNew) ourRange = new Range(start, start);
+ start = posFromMouse(cm, e, true, true);
+ ourIndex = -1;
+ } else if (type == "double") {
+ var word = cm.findWordAt(start);
+ if (cm.display.shift || doc.extend)
+ ourRange = extendRange(doc, ourRange, word.anchor, word.head);
+ else
+ ourRange = word;
+ } else if (type == "triple") {
+ var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)));
+ if (cm.display.shift || doc.extend)
+ ourRange = extendRange(doc, ourRange, line.anchor, line.head);
+ else
+ ourRange = line;
+ } else {
+ ourRange = extendRange(doc, ourRange, start);
+ }
+
+ if (!addNew) {
+ ourIndex = 0;
+ setSelection(doc, new Selection([ourRange], 0), sel_mouse);
+ startSel = doc.sel;
+ } else if (ourIndex == -1) {
+ ourIndex = ranges.length;
+ setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
+ {scroll: false, origin: "*mouse"});
+ } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) {
+ setSelection(doc, normalizeSelection(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 (type == "rect") {
+ 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(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
+ {origin: "*mouse", scroll: false});
+ cm.scrollIntoView(pos);
+ } else {
+ var oldRange = ourRange;
+ var anchor = oldRange.anchor, head = pos;
+ if (type != "single") {
+ if (type == "double")
+ var range = cm.findWordAt(pos);
+ else
+ var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0)));
+ 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 = startSel.ranges.slice(0);
+ ranges[ourIndex] = new Range(clipPos(doc, anchor), head);
+ setSelection(doc, normalizeSelection(ranges, 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, type == "rect");
+ 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;
+ e_preventDefault(e);
+ display.input.focus();
+ off(document, "mousemove", move);
+ off(document, "mouseup", up);
+ doc.history.lastSelOrigin = null;
+ }
+
+ var move = operation(cm, function(e) {
+ if (!e_button(e)) done(e);
+ else extend(e);
+ });
+ var up = operation(cm, done);
+ cm.state.selectingText = up;
+ on(document, "mousemove", move);
+ on(document, "mouseup", up);
+ }
+
+ // Determines whether an event happened in the gutter, and fires the
+ // handlers for the corresponding event.
+ function gutterEvent(cm, e, type, prevent, signalfn) {
+ try { var mX = e.clientX, mY = e.clientY; }
+ catch(e) { 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.options.gutters.length; ++i) {
+ var g = display.gutters.childNodes[i];
+ if (g && g.getBoundingClientRect().right >= mX) {
+ var line = lineAtHeight(cm.doc, mY);
+ var gutter = cm.options.gutters[i];
+ signalfn(cm, type, cm, line, gutter, e);
+ return e_defaultPrevented(e);
+ }
+ }
+ }
+
+ function clickInGutter(cm, e) {
+ return gutterEvent(cm, e, "gutterClick", true, signalLater);
+ }
+
+ // 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 || isReadOnly(cm)) 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 loadFile = function(file, i) {
+ if (cm.options.allowDropFileTypes &&
+ indexOf(cm.options.allowDropFileTypes, file.type) == -1)
+ return;
+
+ var reader = new FileReader;
+ reader.onload = operation(cm, function() {
+ var content = reader.result;
+ if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) content = "";
+ text[i] = content;
+ if (++read == n) {
+ pos = clipPos(cm.doc, pos);
+ var change = {from: pos, to: pos,
+ text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
+ origin: "paste"};
+ makeChange(cm.doc, change);
+ setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
+ }
+ });
+ reader.readAsText(file);
+ };
+ for (var i = 0; i < n; ++i) loadFile(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() {cm.display.input.focus();}, 20);
+ return;
+ }
+ try {
+ var text = e.dataTransfer.getData("Text");
+ if (text) {
+ if (cm.state.draggingText && !(mac ? e.altKey : e.ctrlKey))
+ var selected = cm.listSelections();
+ setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
+ if (selected) for (var i = 0; i < selected.length; ++i)
+ replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag");
+ cm.replaceSelection(text, "around", "paste");
+ cm.display.input.focus();
+ }
+ }
+ catch(e){}
+ }
+ }
+
+ 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());
+
+ // 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;
+ }
+ }
+
+ // SCROLL EVENTS
+
+ // Sync the scrollable area and scrollbars, ensure the viewport
+ // covers the visible area.
+ function setScrollTop(cm, val) {
+ if (Math.abs(cm.doc.scrollTop - val) < 2) return;
+ cm.doc.scrollTop = val;
+ if (!gecko) updateDisplaySimple(cm, {top: val});
+ if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
+ cm.display.scrollbars.setScrollTop(val);
+ if (gecko) updateDisplaySimple(cm);
+ startWorker(cm, 100);
+ }
+ // Sync scroller and scrollbar, ensure the gutter elements are
+ // aligned.
+ function setScrollLeft(cm, val, isScroller) {
+ if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
+ val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
+ cm.doc.scrollLeft = val;
+ alignHorizontally(cm);
+ if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
+ cm.display.scrollbars.setScrollLeft(val);
+ }
+
+ // 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;
+
+ var wheelEventDelta = function(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};
+ };
+ CodeMirror.wheelEventPixels = function(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)
+ setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
+ setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
+ // 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;
+ }
+ }
+ }
+
+ // KEY EVENTS
+
+ // 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 (isReadOnly(cm)) 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);
+ }
+
+ var stopSeq = new Delayed;
+ function dispatchKey(cm, name, e, handle) {
+ var seq = cm.state.keySeq;
+ if (seq) {
+ if (isModifierKey(name)) return "handled";
+ stopSeq.set(50, function() {
+ if (cm.state.keySeq == seq) {
+ cm.state.keySeq = null;
+ cm.display.input.reset();
+ }
+ });
+ name = seq + " " + name;
+ }
+ 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);
+ }
+
+ if (seq && !result && /\'$/.test(name)) {
+ e_preventDefault(e);
+ return true;
+ }
+ 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;
+ 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");
+ }
+
+ // 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 (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);
+ if (handleCharBinding(cm, e, ch)) return;
+ cm.display.input.onKeyPress(e);
+ }
+
+ // FOCUS/BLUR EVENTS
+
+ function delayBlurEvent(cm) {
+ cm.state.delayingBlurEvent = true;
+ setTimeout(function() {
+ if (cm.state.delayingBlurEvent) {
+ cm.state.delayingBlurEvent = false;
+ onBlur(cm);
+ }
+ }, 100);
+ }
+
+ function onFocus(cm) {
+ if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false;
+
+ if (cm.options.readOnly == "nocursor") return;
+ if (!cm.state.focused) {
+ signal(cm, "focus", cm);
+ 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() { cm.display.input.reset(true); }, 20); // Issue #1730
+ }
+ cm.display.input.receivedFocus();
+ }
+ restartBlink(cm);
+ }
+ function onBlur(cm) {
+ if (cm.state.delayingBlurEvent) return;
+
+ if (cm.state.focused) {
+ signal(cm, "blur", cm);
+ 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);
+ }
+
+ // 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;
+ cm.display.input.onContextMenu(e);
+ }
+
+ function contextMenuInGutter(cm, e) {
+ if (!hasHandler(cm, "gutterContextMenu")) return false;
+ return gutterEvent(cm, e, "gutterContextMenu", false, signal);
+ }
+
+ // UPDATING
+
+ // Compute the position of the end of a change (its 'to' property
+ // refers to the pre-change end).
+ var changeEnd = CodeMirror.changeEnd = function(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(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);
+ }
+
+ // 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() { this.canceled = true; }
+ };
+ if (update) obj.update = function(from, to, text, origin) {
+ if (from) this.from = clipPos(doc, from);
+ if (to) this.to = clipPos(doc, to);
+ if (text) this.text = text;
+ if (origin !== undefined) this.origin = origin;
+ };
+ signal(doc, "beforeChange", doc, obj);
+ if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
+
+ if (obj.canceled) 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});
+ } 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) {
+ if (doc.cm && doc.cm.state.suppressEdits) 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)
+ for (var i = 0; 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 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");
+
+ for (var i = event.changes.length - 1; i >= 0; --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));
+ });
+ }
+ }
+
+ // 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);
+ }
+
+ // 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;
+ }
+
+ // Adjust frontier, schedule worker
+ doc.frontier = Math.min(doc.frontier, 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) {
+ if (!to) to = from;
+ if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; }
+ if (typeof code == "string") code = doc.splitLines(code);
+ makeChange(doc, {from: from, to: to, text: code, origin: origin});
+ }
+
+ // 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, coords) {
+ if (signalDOMEvent(cm, "scrollCursorIntoView")) return;
+
+ var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
+ if (coords.top + box.top < 0) doScroll = true;
+ else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
+ if (doScroll != null && !phantom) {
+ var scrollNode = elt("div", "\u200b", null, "position: absolute; top: " +
+ (coords.top - display.viewOffset - paddingTop(cm.display)) + "px; height: " +
+ (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + "px; left: " +
+ coords.left + "px; width: 2px;");
+ 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;
+ for (var limit = 0; limit < 5; limit++) {
+ var changed = false, coords = cursorCoords(cm, pos);
+ var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
+ var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),
+ Math.min(coords.top, endCoords.top) - margin,
+ Math.max(coords.left, endCoords.left),
+ Math.max(coords.bottom, endCoords.bottom) + margin);
+ var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
+ if (scrollPos.scrollTop != null) {
+ setScrollTop(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 coords;
+ }
+
+ // Scroll a given set of coordinates into view (immediately).
+ function scrollIntoView(cm, x1, y1, x2, y2) {
+ var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
+ if (scrollPos.scrollTop != null) setScrollTop(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, x1, y1, x2, y2) {
+ var display = cm.display, snapMargin = textHeight(cm.display);
+ if (y1 < 0) y1 = 0;
+ var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
+ var screen = displayHeight(cm), result = {};
+ if (y2 - y1 > screen) y2 = y1 + screen;
+ var docBottom = cm.doc.height + paddingVert(display);
+ var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
+ if (y1 < screentop) {
+ result.scrollTop = atTop ? 0 : y1;
+ } else if (y2 > screentop + screen) {
+ var newTop = Math.min(y1, (atBottom ? docBottom : y2) - 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 = x2 - x1 > screenw;
+ if (tooWide) x2 = x1 + screenw;
+ if (x1 < 10)
+ result.scrollLeft = 0;
+ else if (x1 < screenleft)
+ result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10));
+ else if (x2 > screenw + screenleft - 3)
+ result.scrollLeft = x2 + (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 addToScrollPos(cm, left, top) {
+ if (left != null || top != null) resolveScrollToPos(cm);
+ if (left != null)
+ cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left;
+ if (top != null)
+ 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(), from = cur, to = cur;
+ if (!cm.options.lineWrapping) {
+ from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur;
+ to = Pos(cur.line, cur.ch + 1);
+ }
+ cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true};
+ }
+
+ // 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);
+ var sPos = calculateScrollPos(cm, Math.min(from.left, to.left),
+ Math.min(from.top, to.top) - range.margin,
+ Math.max(from.right, to.right),
+ Math.max(from.bottom, to.bottom) + range.margin);
+ cm.scrollTo(sPos.scrollLeft, sPos.scrollTop);
+ }
+ }
+
+ // API UTILITIES
+
+ // 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 = getStateBefore(cm, n);
+ }
+
+ 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 = 0; i < doc.sel.ranges.length; i++) {
+ var range = doc.sel.ranges[i];
+ if (range.head.line == n && range.head.ch < curSpaceString.length) {
+ var pos = Pos(n, curSpaceString.length);
+ replaceOneSelection(doc, i, new Range(pos, pos));
+ break;
+ }
+ }
+ }
+ }
+
+ // 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;
+ }
+
+ // 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);
+ });
+ }
+
+ // 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 line = pos.line, ch = pos.ch, origDir = dir;
+ var lineObj = getLine(doc, line);
+ var possible = true;
+ function findNextLine() {
+ var l = line + dir;
+ if (l < doc.first || l >= doc.first + doc.size) return (possible = false);
+ line = l;
+ return lineObj = getLine(doc, l);
+ }
+ function moveOnce(boundToLine) {
+ var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
+ if (next == null) {
+ if (!boundToLine && findNextLine()) {
+ if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
+ else ch = dir < 0 ? lineObj.text.length : 0;
+ } else return (possible = false);
+ } else ch = 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(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();}
+ break;
+ }
+
+ if (type) sawType = type;
+ if (dir > 0 && !moveOnce(!first)) break;
+ }
+ }
+ var result = skipAtomic(doc, Pos(line, ch), origDir, true);
+ if (!possible) 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);
+ y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
+ } else if (unit == "line") {
+ y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
+ }
+ for (;;) {
+ var 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;
+ }
+
+ // EDITOR METHODS
+
+ // 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.
+
+ 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);
+ },
+
+ 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.");
+ this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
+ 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("cm-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 = 0; i < help._global.length; i++) {
+ var cur = help._global[i];
+ 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 getStateBefore(this, line + 1, precise);
+ },
+
+ 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) {
+ 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").top +
+ (end ? this.doc.height - heightAtLine(lineObj) : 0);
+ },
+
+ defaultTextHeight: function() { return textHeight(this.display); },
+ defaultCharWidth: function() { return charWidth(this.display); },
+
+ setGutterMarker: methodOp(function(line, gutterID, value) {
+ return changeLine(this.doc, line, "gutter", function(line) {
+ var markers = line.gutterMarkers || (line.gutterMarkers = {});
+ markers[gutterID] = value;
+ if (!value && isEmpty(markers)) line.gutterMarkers = null;
+ return true;
+ });
+ }),
+
+ clearGutter: methodOp(function(gutterID) {
+ var cm = this, doc = cm.doc, i = doc.first;
+ doc.iter(function(line) {
+ if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
+ line.gutterMarkers[gutterID] = null;
+ regLineChange(cm, i, "gutter");
+ if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
+ }
+ ++i;
+ });
+ }),
+
+ lineInfo: function(line) {
+ if (typeof line == "number") {
+ if (!isLine(this.doc, line)) return null;
+ var n = line;
+ line = getLine(this.doc, line);
+ if (!line) return null;
+ } else {
+ var 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};
+ },
+
+ 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, top, left + node.offsetWidth, top + node.offsetHeight);
+ },
+
+ triggerOnKeyDown: methodOp(onKeyDown),
+ triggerOnKeyPress: methodOp(onKeyPress),
+ triggerOnKeyUp: onKeyUp,
+
+ 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; }
+ for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
+ cur = findPosH(this.doc, cur, dir, unit, visually);
+ if (cur.hitSide) break;
+ }
+ return cur;
+ },
+
+ moveH: methodOp(function(dir, unit) {
+ var cm = this;
+ cm.extendSelectionsBy(function(range) {
+ if (cm.display.shift || cm.doc.extend || range.empty())
+ return findPosH(cm.doc, range.head, dir, unit, cm.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; }
+ for (var i = 0, cur = clipPos(this.doc, from); 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 cm = this, doc = this.doc, goals = [];
+ var collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected();
+ doc.extendSelectionsBy(function(range) {
+ if (collapse)
+ return dir < 0 ? range.from() : range.to();
+ var headPos = cursorCoords(cm, range.head, "div");
+ if (range.goalColumn != null) headPos.left = range.goalColumn;
+ goals.push(headPos.left);
+ var pos = findPosV(cm, headPos, dir, unit);
+ if (unit == "page" && range == doc.sel.primary())
+ addToScrollPos(cm, null, charCoords(cm, 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.xRel < 0 || 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(); },
+
+ scrollTo: methodOp(function(x, y) {
+ if (x != null || y != null) resolveScrollToPos(this);
+ if (x != null) this.curOp.scrollLeft = x;
+ if (y != null) this.curOp.scrollTop = 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) {
+ resolveScrollToPos(this);
+ this.curOp.scrollToPos = range;
+ } else {
+ var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left),
+ Math.min(range.from.top, range.to.top) - range.margin,
+ Math.max(range.from.right, range.to.right),
+ Math.max(range.from.bottom, range.to.bottom) + range.margin);
+ this.scrollTo(sPos.scrollLeft, sPos.scrollTop);
+ }
+ }),
+
+ setSize: methodOp(function(width, height) {
+ var cm = this;
+ function interpret(val) {
+ return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
+ }
+ if (width != null) cm.display.wrapper.style.width = interpret(width);
+ if (height != null) cm.display.wrapper.style.height = interpret(height);
+ if (cm.options.lineWrapping) clearLineMeasurementCache(this);
+ var lineNo = cm.display.viewFrom;
+ cm.doc.iter(lineNo, cm.display.viewTo, function(line) {
+ if (line.widgets) for (var i = 0; i < line.widgets.length; i++)
+ if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break; }
+ ++lineNo;
+ });
+ cm.curOp.forceUpdate = true;
+ signal(cm, "refresh", this);
+ }),
+
+ operation: function(f){return runInOp(this, f);},
+
+ refresh: methodOp(function() {
+ var oldHeight = this.display.cachedTextHeight;
+ regChange(this);
+ this.curOp.forceUpdate = true;
+ clearCaches(this);
+ this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop);
+ updateGutterSpace(this);
+ if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
+ estimateLineHeights(this);
+ signal(this, "refresh", this);
+ }),
+
+ swapDoc: methodOp(function(doc) {
+ var old = this.doc;
+ old.cm = null;
+ attachDoc(this, doc);
+ clearCaches(this);
+ this.display.input.reset();
+ this.scrollTo(doc.scrollLeft, doc.scrollTop);
+ this.curOp.forceScroll = true;
+ signalLater(this, "swapDoc", this, old);
+ return old;
+ }),
+
+ 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);
+
+ // OPTION DEFAULTS
+
+ // The default configuration options.
+ var defaults = CodeMirror.defaults = {};
+ // Functions to run when options are changed.
+ 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;
+ }
+
+ // Passed to option handlers when there is no old value.
+ var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.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) {
+ 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", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val, old) {
+ cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
+ if (old != CodeMirror.Init) cm.refresh();
+ });
+ option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {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("rtlMoveVisually", !windows);
+ option("wholeLineUpdateBefore", true);
+
+ option("theme", "default", function(cm) {
+ themeChanged(cm);
+ guttersChanged(cm);
+ }, true);
+ option("keyMap", "default", function(cm, val, old) {
+ var next = getKeyMap(val);
+ var prev = old != CodeMirror.Init && getKeyMap(old);
+ if (prev && prev.detach) prev.detach(cm, next);
+ if (next.attach) next.attach(cm, prev || null);
+ });
+ option("extraKeys", null);
+
+ option("lineWrapping", false, wrappingChanged, true);
+ option("gutters", [], function(cm) {
+ setGuttersForLineNumbers(cm.options);
+ guttersChanged(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) {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) {
+ setGuttersForLineNumbers(cm.options);
+ guttersChanged(cm);
+ }, true);
+ option("firstLineNumber", 1, guttersChanged, true);
+ option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
+ option("showCursorWhenSelecting", false, updateSelection, true);
+
+ option("resetSelectionOnContextMenu", true);
+ option("lineWiseCopyCut", true);
+
+ option("readOnly", false, function(cm, val) {
+ if (val == "nocursor") {
+ onBlur(cm);
+ cm.display.input.blur();
+ cm.display.disabled = true;
+ } else {
+ cm.display.disabled = false;
+ }
+ cm.display.input.readOnlyChanged(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){cm.doc.history.undoDepth = val;});
+ option("historyEventDelay", 1250);
+ option("viewportMargin", 10, function(cm){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) {
+ cm.display.input.getField().tabIndex = val || "";
+ });
+ option("autofocus", null);
+
+ // MODE DEFINITION AND QUERYING
+
+ // Known modes, by name and by MIME
+ var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.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.)
+ CodeMirror.defineMode = function(name, mode) {
+ if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
+ if (arguments.length > 2)
+ mode.dependencies = Array.prototype.slice.call(arguments, 2);
+ modes[name] = mode;
+ };
+
+ CodeMirror.defineMIME = function(mime, spec) {
+ mimeModes[mime] = spec;
+ };
+
+ // Given a MIME type, a {name, ...options} config object, or a name
+ // string, return a mode config object.
+ CodeMirror.resolveMode = function(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 CodeMirror.resolveMode("application/xml");
+ }
+ 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.
+ CodeMirror.getMode = function(options, spec) {
+ var spec = CodeMirror.resolveMode(spec);
+ var mfactory = modes[spec.name];
+ if (!mfactory) return CodeMirror.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 in spec.modeProps)
+ modeObj[prop] = spec.modeProps[prop];
+
+ return modeObj;
+ };
+
+ // Minimal default mode.
+ CodeMirror.defineMode("null", function() {
+ return {token: function(stream) {stream.skipToEnd();}};
+ });
+ CodeMirror.defineMIME("text/plain", "null");
+
+ // This can be used to attach properties to mode objects from
+ // outside the actual mode definition.
+ var modeExtensions = CodeMirror.modeExtensions = {};
+ CodeMirror.extendMode = function(mode, properties) {
+ var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
+ copyObj(properties, exts);
+ };
+
+ // EXTENSIONS
+
+ CodeMirror.defineExtension = function(name, func) {
+ CodeMirror.prototype[name] = func;
+ };
+ CodeMirror.defineDocExtension = function(name, func) {
+ Doc.prototype[name] = func;
+ };
+ CodeMirror.defineOption = option;
+
+ var initHooks = [];
+ CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
+
+ var helpers = CodeMirror.helpers = {};
+ 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});
+ };
+
+ // MODE STATE HANDLING
+
+ // Utility functions for working with state. Exported because nested
+ // modes need to do this for their inner modes.
+
+ var copyState = CodeMirror.copyState = function(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;
+ };
+
+ var startState = CodeMirror.startState = function(mode, a1, a2) {
+ return mode.startState ? mode.startState(a1, a2) : true;
+ };
+
+ // Given a mode and a state (for that mode), find the inner mode and
+ // state at the position that the state refers to.
+ CodeMirror.innerMode = function(mode, state) {
+ while (mode.innerMode) {
+ var info = mode.innerMode(state);
+ if (!info || info.mode == mode) break;
+ state = info.state;
+ mode = info.mode;
+ }
+ return info || {mode: mode, state: state};
+ };
+
+ // STANDARD COMMANDS
+
+ // Commands are parameter-less actions that can be performed on an
+ // editor, mostly used for keybindings.
+ var commands = CodeMirror.commands = {
+ selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);},
+ singleSelection: function(cm) {
+ cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll);
+ },
+ killLine: function(cm) {
+ 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) {
+ deleteNearSelection(cm, function(range) {
+ return {from: Pos(range.from().line, 0),
+ to: clipPos(cm.doc, Pos(range.to().line + 1, 0))};
+ });
+ },
+ delLineLeft: function(cm) {
+ deleteNearSelection(cm, function(range) {
+ return {from: Pos(range.from().line, 0), to: range.from()};
+ });
+ },
+ delWrappedLineLeft: function(cm) {
+ 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) {
+ 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) {cm.undo();},
+ redo: function(cm) {cm.redo();},
+ undoSelection: function(cm) {cm.undoSelection();},
+ redoSelection: function(cm) {cm.redoSelection();},
+ goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
+ goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
+ goLineStart: function(cm) {
+ cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); },
+ {origin: "+move", bias: 1});
+ },
+ goLineStartSmart: function(cm) {
+ cm.extendSelectionsBy(function(range) {
+ return lineStartSmart(cm, range.head);
+ }, {origin: "+move", bias: 1});
+ },
+ goLineEnd: function(cm) {
+ cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); },
+ {origin: "+move", bias: -1});
+ },
+ goLineRight: function(cm) {
+ cm.extendSelectionsBy(function(range) {
+ var top = cm.charCoords(range.head, "div").top + 5;
+ return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
+ }, sel_move);
+ },
+ goLineLeft: function(cm) {
+ cm.extendSelectionsBy(function(range) {
+ var top = cm.charCoords(range.head, "div").top + 5;
+ return cm.coordsChar({left: 0, top: top}, "div");
+ }, sel_move);
+ },
+ goLineLeftSmart: function(cm) {
+ cm.extendSelectionsBy(function(range) {
+ var top = cm.charCoords(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) {cm.moveV(-1, "line");},
+ goLineDown: function(cm) {cm.moveV(1, "line");},
+ goPageUp: function(cm) {cm.moveV(-1, "page");},
+ goPageDown: function(cm) {cm.moveV(1, "page");},
+ goCharLeft: function(cm) {cm.moveH(-1, "char");},
+ goCharRight: function(cm) {cm.moveH(1, "char");},
+ goColumnLeft: function(cm) {cm.moveH(-1, "column");},
+ goColumnRight: function(cm) {cm.moveH(1, "column");},
+ goWordLeft: function(cm) {cm.moveH(-1, "word");},
+ goGroupRight: function(cm) {cm.moveH(1, "group");},
+ goGroupLeft: function(cm) {cm.moveH(-1, "group");},
+ goWordRight: function(cm) {cm.moveH(1, "word");},
+ delCharBefore: function(cm) {cm.deleteH(-1, "char");},
+ delCharAfter: function(cm) {cm.deleteH(1, "char");},
+ delWordBefore: function(cm) {cm.deleteH(-1, "word");},
+ delWordAfter: function(cm) {cm.deleteH(1, "word");},
+ delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
+ delGroupAfter: function(cm) {cm.deleteH(1, "group");},
+ indentAuto: function(cm) {cm.indentSelection("smart");},
+ indentMore: function(cm) {cm.indentSelection("add");},
+ indentLess: function(cm) {cm.indentSelection("subtract");},
+ insertTab: function(cm) {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(new Array(tabSize - col % tabSize + 1).join(" "));
+ }
+ cm.replaceSelections(spaces);
+ },
+ defaultTab: function(cm) {
+ if (cm.somethingSelected()) cm.indentSelection("add");
+ else cm.execCommand("insertTab");
+ },
+ transposeChars: function(cm) {
+ runInOp(cm, function() {
+ var ranges = cm.listSelections(), newSel = [];
+ for (var i = 0; i < ranges.length; i++) {
+ 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)
+ cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
+ prev.charAt(prev.length - 1),
+ Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose");
+ }
+ }
+ newSel.push(new Range(cur, cur));
+ }
+ cm.setSelections(newSel);
+ });
+ },
+ newlineAndIndent: function(cm) {
+ runInOp(cm, function() {
+ var len = cm.listSelections().length;
+ for (var i = 0; i < len; i++) {
+ var range = cm.listSelections()[i];
+ cm.replaceRange(cm.doc.lineSeparator(), range.anchor, range.head, "+input");
+ cm.indentLine(range.from().line + 1, null, true);
+ }
+ ensureCursorVisible(cm);
+ });
+ },
+ toggleOverwrite: function(cm) {cm.toggleOverwrite();}
+ };
+
+
+ // STANDARD KEYMAPS
+
+ var keyMap = CodeMirror.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"
+ };
+ 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.
+ CodeMirror.normalizeKeyMap = function(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, name;
+ 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;
+ };
+
+ var lookupKey = CodeMirror.lookupKey = function(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.
+ var isModifierKey = CodeMirror.isModifierKey = function(value) {
+ var name = typeof value == "string" ? value : keyNames[value.keyCode];
+ return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
+ };
+
+ // Look up the name of a key as indicated by an event object.
+ var keyName = CodeMirror.keyName = function(event, noShift) {
+ if (presto && event.keyCode == 34 && event["char"]) return false;
+ var base = keyNames[event.keyCode], name = base;
+ if (name == null || event.altGraphKey) return false;
+ 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;
+ };
+
+ function getKeyMap(val) {
+ return typeof val == "string" ? keyMap[val] : val;
+ }
+
+ // FROMTEXTAREA
+
+ CodeMirror.fromTextArea = function(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();}
+ 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 (typeof textarea.form.submit == "function")
+ textarea.form.submit = realSubmit;
+ }
+ };
+ };
+
+ textarea.style.display = "none";
+ var cm = CodeMirror(function(node) {
+ textarea.parentNode.insertBefore(node, textarea.nextSibling);
+ }, options);
+ return cm;
+ };
+
+ // STRING STREAM
+
+ // Fed to the mode parsers, provides helper functions to make
+ // parsers more succinct.
+
+ var StringStream = CodeMirror.StringStream = function(string, tabSize) {
+ this.pos = this.start = 0;
+ this.string = string;
+ this.tabSize = tabSize || 8;
+ this.lastColumnPos = this.lastColumnValue = 0;
+ this.lineStart = 0;
+ };
+
+ StringStream.prototype = {
+ eol: function() {return this.pos >= this.string.length;},
+ sol: function() {return this.pos == this.lineStart;},
+ peek: function() {return this.string.charAt(this.pos) || undefined;},
+ next: function() {
+ if (this.pos < this.string.length)
+ return this.string.charAt(this.pos++);
+ },
+ eat: function(match) {
+ var ch = this.string.charAt(this.pos);
+ if (typeof match == "string") var ok = ch == match;
+ else var ok = ch && (match.test ? match.test(ch) : match(ch));
+ if (ok) {++this.pos; return ch;}
+ },
+ eatWhile: function(match) {
+ var start = this.pos;
+ while (this.eat(match)){}
+ return this.pos > start;
+ },
+ eatSpace: function() {
+ var start = this.pos;
+ while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
+ return this.pos > start;
+ },
+ skipToEnd: function() {this.pos = this.string.length;},
+ skipTo: function(ch) {
+ var found = this.string.indexOf(ch, this.pos);
+ if (found > -1) {this.pos = found; return true;}
+ },
+ backUp: function(n) {this.pos -= n;},
+ 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);
+ },
+ indentation: function() {
+ return countColumn(this.string, null, this.tabSize) -
+ (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
+ },
+ 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;
+ }
+ },
+ current: function(){return this.string.slice(this.start, this.pos);},
+ hideFirstChars: function(n, inner) {
+ this.lineStart += n;
+ try { return inner(); }
+ finally { this.lineStart -= n; }
+ }
+ };
+
+ // 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.
+
+ var nextMarkerId = 0;
+
+ var TextMarker = CodeMirror.TextMarker = function(doc, type) {
+ this.lines = [];
+ this.type = type;
+ this.doc = doc;
+ this.id = ++nextMarkerId;
+ };
+ eventMixin(TextMarker);
+
+ // 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 = 0; i < this.lines.length; ++i) {
+ var visual = visualLine(this.lines[i]), 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);
+ 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 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);
+ }
+ });
+ };
+
+ 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);
+ }
+ };
+
+ // 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;
+
+ // 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 = elt("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");
+ sawCollapsedSpans = true;
+ }
+
+ 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() { marker.clear(); });
+
+ if (marker.readOnly) {
+ sawReadOnlySpans = true;
+ 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.title || marker.startStyle || marker.endStyle || marker.css)
+ 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 = CodeMirror.SharedTextMarker = function(markers, primary) {
+ this.markers = markers;
+ this.primary = primary;
+ for (var i = 0; i < markers.length; ++i)
+ markers[i].parent = this;
+ };
+ eventMixin(SharedTextMarker);
+
+ 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);
+ };
+
+ 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) {
+ for (var i = 0; i < markers.length; i++) {
+ var marker = markers[i], linked = [marker.primary.doc];;
+ linkedDocs(marker.primary.doc, function(d) { 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);
+ }
+ }
+ }
+ }
+
+ // 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) {
+ for (var r, 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) {
+ if (old) for (var i = 0, nw; 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) {
+ if (old) for (var i = 0, nw; 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 = 0; i < last.length; ++i) {
+ var span = last[i];
+ if (span.to != null) span.to += offset;
+ if (span.from == null) {
+ var found = getMarkedSpanFor(first, span.marker);
+ if (!found) {
+ span.from = offset;
+ if (sameLine) (first || (first = [])).push(span);
+ }
+ } else {
+ span.from += offset;
+ if (sameLine) (first || (first = [])).push(span);
+ }
+ }
+ }
+ // 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 = 0; i < first.length; ++i)
+ if (first[i].to == null)
+ (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null));
+ for (var i = 0; i < gap; ++i)
+ 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 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 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 - 1;
+ }
+ }
+ 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, 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); }
+
+ // 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 && (cmp(found.to, from) > 0 || (sp.marker.inclusiveRight && marker.inclusiveLeft)) ||
+ fromCmp >= 0 && (cmp(found.from, to) < 0 || (sp.marker.inclusiveLeft && marker.inclusiveRight)))
+ 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;
+ }
+
+ // 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, 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, 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;
+ }
+ }
+
+ // LINE WIDGETS
+
+ // Line widgets are block elements displayed above or below a line.
+
+ var LineWidget = CodeMirror.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;
+ };
+ eventMixin(LineWidget);
+
+ function adjustScrollWhenAboveVisible(cm, line, diff) {
+ if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
+ addToScrollPos(cm, null, diff);
+ }
+
+ 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");
+ });
+ };
+ LineWidget.prototype.changed = function() {
+ var oldH = this.height, cm = this.doc.cm, line = this.line;
+ this.height = null;
+ var diff = widgetHeight(this) - oldH;
+ if (!diff) return;
+ updateLineHeight(line, line.height + diff);
+ if (cm) runInOp(cm, function() {
+ cm.curOp.forceUpdate = true;
+ adjustScrollWhenAboveVisible(cm, line, diff);
+ });
+ };
+
+ 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.offsetHeight;
+ }
+
+ 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) addToScrollPos(cm, null, widget.height);
+ cm.curOp.forceUpdate = true;
+ }
+ return true;
+ });
+ return widget;
+ }
+
+ // LINE DATA STRUCTURE
+
+ // Line objects. These hold state related to a line, including
+ // highlighting info (the styles array).
+ var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) {
+ this.text = text;
+ attachMarkedSpans(this, markedSpans);
+ this.height = estimateHeight ? estimateHeight(this) : 1;
+ };
+ eventMixin(Line);
+ Line.prototype.lineNo = function() { return lineNo(this); };
+
+ // 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);
+ }
+
+ 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;
+ }
+
+ function callBlankLine(mode, state) {
+ if (mode.blankLine) return mode.blankLine(state);
+ if (!mode.innerMode) return;
+ var inner = CodeMirror.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] = CodeMirror.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.");
+ }
+
+ // Utility for getTokenAt and getLineTokens
+ function takeToken(cm, pos, precise, asArray) {
+ function getObj(copy) {
+ return {start: stream.start, end: stream.pos,
+ string: stream.current(),
+ type: style || null,
+ state: copy ? copyState(doc.mode, state) : state};
+ }
+
+ var doc = cm.doc, mode = doc.mode, style;
+ pos = clipPos(doc, pos);
+ var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise);
+ var stream = new StringStream(line.text, cm.options.tabSize), tokens;
+ if (asArray) tokens = [];
+ while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
+ stream.start = stream.pos;
+ style = readToken(mode, stream, state);
+ if (asArray) tokens.push(getObj(true));
+ }
+ return asArray ? tokens : getObj();
+ }
+
+ // Run the given mode's parser over a line, calling f for each token.
+ function runMode(cm, text, mode, state, 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), style;
+ var inner = cm.options.addModeClass && [null];
+ if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses);
+ while (!stream.eol()) {
+ if (stream.pos > cm.options.maxHighlightLength) {
+ flattenSpans = false;
+ if (forceToEnd) processLine(cm, text, state, stream.pos);
+ stream.pos = text.length;
+ style = null;
+ } else {
+ style = extractLineClasses(readToken(mode, stream, 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 + 50000);
+ 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
+ var pos = Math.min(stream.pos, curStart + 50000);
+ f(pos, curStyle);
+ curStart = pos;
+ }
+ }
+
+ // 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, state, 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, state, function(end, style) {
+ st.push(end, style);
+ }, lineClasses, forceToEnd);
+
+ // Run overlays, adjust style array.
+ for (var o = 0; o < cm.state.overlays.length; ++o) {
+ var overlay = cm.state.overlays[o], i = 1, at = 0;
+ runMode(cm, line.text, overlay.mode, true, 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, "cm-overlay " + style);
+ i = start + 2;
+ } else {
+ for (; start < i; start += 2) {
+ var cur = st[start+1];
+ st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style;
+ }
+ }
+ }, lineClasses);
+ }
+
+ 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 state = getStateBefore(cm, lineNo(line));
+ var result = highlightLine(cm, line, line.text.length > cm.options.maxHighlightLength ? copyState(cm.doc.mode, state) : state);
+ line.stateAfter = state;
+ line.styles = result.styles;
+ if (result.classes) line.styleClasses = result.classes;
+ else if (line.styleClasses) line.styleClasses = null;
+ if (updateFrontier === cm.doc.frontier) cm.doc.frontier++;
+ }
+ return line.styles;
+ }
+
+ // 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, state, startAt) {
+ var mode = cm.doc.mode;
+ var stream = new StringStream(text, cm.options.tabSize);
+ stream.start = stream.pos = startAt || 0;
+ if (text == "") callBlankLine(mode, state);
+ while (!stream.eol()) {
+ readToken(mode, stream, state);
+ stream.start = stream.pos;
+ }
+ }
+
+ // 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 = elt("span", null, null, webkit ? "padding-right: .1px" : null);
+ var builder = {pre: elt("pre", [content], "CodeMirror-line"), content: content,
+ col: 0, pos: 0, cm: cm,
+ splitSpaces: (ie || webkit) && 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;
+ 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)))
+ 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 && /\bcm-tab\b/.test(builder.content.lastChild.className))
+ 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, title, css) {
+ if (!text) return;
+ var displayText = builder.splitSpaces ? text.replace(/ {3,}/g, splitSpaces) : text;
+ var special = builder.cm.state.specialChars, mustWrap = false;
+ if (!special.test(text)) {
+ builder.col += text.length;
+ var 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 {
+ var content = document.createDocumentFragment(), 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;
+ if (m[0] == "\t") {
+ var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
+ var txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
+ txt.setAttribute("role", "presentation");
+ txt.setAttribute("cm-text", "\t");
+ builder.col += tabWidth;
+ } else if (m[0] == "\r" || m[0] == "\n") {
+ var txt = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
+ txt.setAttribute("cm-text", m[0]);
+ builder.col += 1;
+ } else {
+ var txt = builder.cm.options.specialCharPlaceholder(m[0]);
+ txt.setAttribute("cm-text", m[0]);
+ if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
+ else content.appendChild(txt);
+ builder.col += 1;
+ }
+ builder.map.push(builder.pos, builder.pos + 1, txt);
+ builder.pos++;
+ }
+ }
+ 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 (title) token.title = title;
+ return builder.content.appendChild(token);
+ }
+ builder.content.appendChild(content);
+ }
+
+ function splitSpaces(old) {
+ var out = " ";
+ for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
+ out += " ";
+ return out;
+ }
+
+ // Work around nonsense dimensions being reported for stretches of
+ // right-to-left text.
+ function buildTokenBadBidi(inner, order) {
+ return function(builder, text, style, startStyle, endStyle, title, css) {
+ 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
+ for (var i = 0; i < order.length; i++) {
+ var part = order[i];
+ if (part.to > start && part.from <= start) break;
+ }
+ if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title, css);
+ inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css);
+ 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;
+ }
+
+ // 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; i < styles.length; i+=2)
+ builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options));
+ return;
+ }
+
+ var len = allText.length, pos = 0, i = 1, text = "", style, css;
+ var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;
+ for (;;) {
+ if (nextChange == pos) { // Update current marker set
+ spanStyle = spanEndStyle = spanStartStyle = title = css = "";
+ collapsed = null; nextChange = Infinity;
+ var foundBookmarks = [];
+ 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 = m.css;
+ if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
+ if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
+ if (m.title && !title) title = m.title;
+ if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
+ collapsed = sp;
+ } else if (sp.from > pos && nextChange > sp.from) {
+ nextChange = sp.from;
+ }
+ }
+ 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 (!collapsed && foundBookmarks.length) for (var j = 0; j < foundBookmarks.length; ++j)
+ buildCollapsedSpan(builder, 0, foundBookmarks[j]);
+ }
+ 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 : "", title, css);
+ }
+ 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);
+ }
+ }
+ }
+
+ // 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) {
+ for (var i = start, result = []; 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 = linesFor(1, text.length - 1);
+ added.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);
+ }
+ } 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 = linesFor(1, text.length - 1);
+ if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
+ doc.insert(from.line + 1, added);
+ }
+
+ signalLater(doc, "change", doc, change);
+ }
+
+ // 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;
+ for (var i = 0, height = 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) {
+ while (child.lines.length > 50) {
+ var spilled = child.lines.splice(child.lines.length - 25, 25);
+ var newleaf = new LeafChunk(spilled);
+ child.height -= newleaf.height;
+ this.children.splice(i + 1, 0, newleaf);
+ newleaf.parent = this;
+ }
+ 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;
+ }
+ }
+ };
+
+ var nextDocId = 0;
+ var Doc = CodeMirror.Doc = function(text, mode, firstLine, lineSep) {
+ if (!(this instanceof Doc)) return new Doc(text, mode, firstLine, lineSep);
+ 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.frontier = firstLine;
+ var start = Pos(firstLine, 0);
+ this.sel = simpleSelection(start);
+ this.history = new History(null);
+ this.id = ++nextDocId;
+ this.modeOption = mode;
+ this.lineSep = lineSep;
+
+ 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);
+ setSelection(this, simpleSelection(top));
+ }),
+ 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) {
+ extendSelections(this, map(this.sel.ranges, f), options);
+ }),
+ setSelections: docMethodOp(function(ranges, primary, options) {
+ if (!ranges.length) return;
+ for (var i = 0, out = []; 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(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(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 = changes.length - 1; i >= 0; i--)
+ makeChange(this, changes[i]);
+ 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 = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone;
+ return {undo: done, redo: undone};
+ },
+ clearHistory: function() {this.history = new History(this.history.maxGeneration);},
+
+ 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);
+ },
+
+ 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, "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 (!(lineNo == from.line && from.ch > span.to ||
+ span.from == null && lineNo != from.line||
+ 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;
+ this.iter(function(line) {
+ var sz = line.text.length + 1;
+ 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;
+ this.iter(this.first, coords.line, function (line) {
+ index += line.text.length + 1;
+ });
+ return index;
+ },
+
+ copy: function(copyHistory) {
+ var doc = new Doc(getLines(this, this.first, this.first + this.size),
+ this.modeOption, this.first, this.lineSep);
+ 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);
+ 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) {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"; }
+ });
+
+ // Public alias.
+ Doc.prototype.eachLine = Doc.prototype.iter;
+
+ // 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);
+
+ // 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);
+ if (!cm.options.lineWrapping) findMaxLine(cm);
+ cm.options.mode = doc.modeOption;
+ regChange(cm);
+ }
+
+ // LINE UTILITIES
+
+ // 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.");
+ for (var chunk = doc; !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); });
+ 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 = 0; i < chunk.children.length; ++i) {
+ var child = chunk.children[i], ch = child.height;
+ if (h < ch) { chunk = child; continue outer; }
+ h -= ch;
+ n += child.chunkSize();
+ }
+ return n;
+ } while (!chunk.lines);
+ for (var i = 0; i < chunk.lines.length; ++i) {
+ var line = chunk.lines[i], lh = line.height;
+ if (h < lh) break;
+ h -= lh;
+ }
+ return n + i;
+ }
+
+
+ // 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 = 0; i < p.children.length; ++i) {
+ var cur = p.children[i];
+ if (cur == chunk) break;
+ else h += cur.height;
+ }
+ }
+ return h;
+ }
+
+ // 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) {
+ var order = line.order;
+ if (order == null) order = line.order = bidiOrdering(line.text);
+ return order;
+ }
+
+ // HISTORY
+
+ 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) {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, ore 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;
+
+ if ((hist.lastOp == opId ||
+ hist.lastOrigin == change.origin && change.origin &&
+ ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
+ change.origin.charAt(0) == "*")) &&
+ (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
+ // Merge this change into the last event
+ var 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;
+ for (var i = 0, out; 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;
+ for (var i = 0, nw = []; i < change.text.length; ++i)
+ nw.push(removeClearedSpans(found[i]));
+ return nw;
+ }
+
+ // 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) {
+ for (var i = 0, copy = []; 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;
+ 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;
+ }
+
+ // 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 = 0; j < sub.changes.length; ++j) {
+ var cur = sub.changes[j];
+ 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);
+ }
+
+ // EVENT UTILITIES
+
+ // Due to the fact that we still support jurassic IE versions, some
+ // compatibility wrappers are needed.
+
+ var e_preventDefault = CodeMirror.e_preventDefault = function(e) {
+ if (e.preventDefault) e.preventDefault();
+ else e.returnValue = false;
+ };
+ var e_stopPropagation = CodeMirror.e_stopPropagation = function(e) {
+ if (e.stopPropagation) e.stopPropagation();
+ else e.cancelBubble = true;
+ };
+ function e_defaultPrevented(e) {
+ return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
+ }
+ var e_stop = CodeMirror.e_stop = function(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;
+ }
+
+ // EVENT HANDLING
+
+ // Lightweight event framework. on/off also work on DOM nodes,
+ // registering native DOM handlers.
+
+ var on = CodeMirror.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 = {});
+ var arr = map[type] || (map[type] = []);
+ arr.push(f);
+ }
+ };
+
+ var noHandlers = []
+ function getHandlers(emitter, type, copy) {
+ var arr = emitter._handlers && emitter._handlers[type]
+ if (copy) return arr && arr.length > 0 ? arr.slice() : noHandlers
+ else return arr || noHandlers
+ }
+
+ var off = CodeMirror.off = function(emitter, type, f) {
+ if (emitter.removeEventListener)
+ emitter.removeEventListener(type, f, false);
+ else if (emitter.detachEvent)
+ emitter.detachEvent("on" + type, f);
+ else {
+ var handlers = getHandlers(emitter, type, false)
+ for (var i = 0; i < handlers.length; ++i)
+ if (handlers[i] == f) { handlers.splice(i, 1); break; }
+ }
+ };
+
+ var signal = CodeMirror.signal = function(emitter, type /*, values...*/) {
+ var handlers = getHandlers(emitter, type, true)
+ 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);
+ };
+
+ 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, false)
+ 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);
+ }
+ function bnd(f) {return function(){f.apply(null, args);};};
+ for (var i = 0; i < arr.length; ++i)
+ list.push(bnd(arr[i]));
+ }
+
+ function fireOrphanDelayed() {
+ var delayed = orphanDelayedCallbacks;
+ orphanDelayedCallbacks = null;
+ for (var i = 0; i < delayed.length; ++i) delayed[i]();
+ }
+
+ // 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);};
+ }
+
+ // MISC UTILITIES
+
+ // Number of pixels added to scroller and sizer to hide scrollbar
+ var scrollerGap = 30;
+
+ // Returned or thrown by various protocols to signal 'I'm not
+ // handling this'.
+ var Pass = CodeMirror.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"};
+
+ function Delayed() {this.id = null;}
+ Delayed.prototype.set = function(ms, f) {
+ clearTimeout(this.id);
+ this.id = setTimeout(f, ms);
+ };
+
+ // Counts the column offset in a string, taking tabs into account.
+ // Used mostly to find indentation.
+ var countColumn = CodeMirror.countColumn = function(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;
+ }
+ };
+
+ // The inverse of countColumn -- find the offset that corresponds to
+ // a particular column.
+ var findColumn = CodeMirror.findColumn = function(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]; }
+
+ 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 indexOf(array, elt) {
+ for (var i = 0; i < array.length; ++i)
+ if (array[i] == elt) return i;
+ return -1;
+ }
+ function map(array, f) {
+ var out = [];
+ for (var i = 0; i < array.length; i++) out[i] = f(array[i], i);
+ return out;
+ }
+
+ 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;
+ };
+
+ 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;
+ }
+
+ function bind(f) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ return function(){return f.apply(null, args);};
+ }
+
+ var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
+ var isWordCharBasic = CodeMirror.isWordChar = function(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); }
+
+ // DOM UTILITIES
+
+ 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;
+ }
+
+ 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 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);
+ }
+
+ var contains = CodeMirror.contains = function(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() {
+ var activeElement = document.activeElement;
+ while (activeElement && activeElement.root && activeElement.root.activeElement)
+ activeElement = activeElement.root.activeElement;
+ return activeElement;
+ }
+ // Older versions of IE throws unspecified error when touching
+ // document.activeElement in some cases (during loading, in iframe)
+ if (ie && ie_version < 11) activeElt = function() {
+ try { return document.activeElement; }
+ catch(e) { return document.body; }
+ };
+
+ function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*"); }
+ var rmClass = CodeMirror.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 : "");
+ }
+ };
+ var addClass = CodeMirror.addClass = function(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;
+ }
+
+ // WINDOW-WIDE EVENTS
+
+ // 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.body.getElementsByClassName) return;
+ var byClass = document.body.getElementsByClassName("CodeMirror");
+ for (var i = 0; i < byClass.length; i++) {
+ var cm = byClass[i].CodeMirror;
+ if (cm) f(cm);
+ }
+ }
+
+ 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() {
+ forEachCodeMirror(onBlur);
+ });
+ }
+
+ // FEATURE DETECTION
+
+ // 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();
+ if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780)
+ var r1 = range(txt, 1, 2).getBoundingClientRect();
+ 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 = CodeMirror.splitLines = "\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) {
+ try {var 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;
+ }
+
+ // KEY NAMES
+
+ var keyNames = CodeMirror.keyNames = {
+ 3: "Enter", 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: "/", 127: "Delete",
+ 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"
+ };
+ (function() {
+ // Number keys
+ for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i);
+ // Alphabetic keys
+ for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
+ // Function keys
+ for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
+ })();
+
+ // BIDI HELPERS
+
+ function iterateBidiSections(order, from, to, f) {
+ if (!order) return f(from, to, "ltr");
+ 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");
+ found = true;
+ }
+ }
+ if (!found) f(from, to, "ltr");
+ }
+
+ function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
+ function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
+
+ function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
+ function lineRight(line) {
+ var order = getOrder(line);
+ if (!order) return line.text.length;
+ return bidiRight(lst(order));
+ }
+
+ function lineStart(cm, lineN) {
+ var line = getLine(cm.doc, lineN);
+ var visual = visualLine(line);
+ if (visual != line) lineN = lineNo(visual);
+ var order = getOrder(visual);
+ var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
+ return Pos(lineN, ch);
+ }
+ function lineEnd(cm, lineN) {
+ var merged, line = getLine(cm.doc, lineN);
+ while (merged = collapsedSpanAtEnd(line)) {
+ line = merged.find(1, true).line;
+ lineN = null;
+ }
+ var order = getOrder(line);
+ var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
+ return Pos(lineN == null ? lineNo(line) : lineN, ch);
+ }
+ function lineStartSmart(cm, pos) {
+ var start = lineStart(cm, pos.line);
+ var line = getLine(cm.doc, start.line);
+ var order = getOrder(line);
+ if (!order || order[0].level == 0) {
+ var firstNonWS = Math.max(0, line.text.search(/\S/));
+ var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
+ return Pos(start.line, inWS ? 0 : firstNonWS);
+ }
+ return start;
+ }
+
+ function compareBidiLevel(order, a, b) {
+ var linedir = order[0].level;
+ if (a == linedir) return true;
+ if (b == linedir) return false;
+ return a < b;
+ }
+ var bidiOther;
+ function getBidiPartAt(order, pos) {
+ bidiOther = null;
+ for (var i = 0, found; i < order.length; ++i) {
+ var cur = order[i];
+ if (cur.from < pos && cur.to > pos) return i;
+ if ((cur.from == pos || cur.to == pos)) {
+ if (found == null) {
+ found = i;
+ } else if (compareBidiLevel(order, cur.level, order[found].level)) {
+ if (cur.from != cur.to) bidiOther = found;
+ return i;
+ } else {
+ if (cur.from != cur.to) bidiOther = i;
+ return found;
+ }
+ }
+ }
+ return found;
+ }
+
+ function moveInLine(line, pos, dir, byUnit) {
+ if (!byUnit) return pos + dir;
+ do pos += dir;
+ while (pos > 0 && isExtendingChar(line.text.charAt(pos)));
+ return pos;
+ }
+
+ // This is needed in order to move 'visually' through bi-directional
+ // text -- i.e., pressing left should make the cursor go left, even
+ // when in RTL text. The tricky part is the 'jumps', where RTL and
+ // LTR text touch each other. This often requires the cursor offset
+ // to move more than one unit, in order to visually move one unit.
+ function moveVisually(line, start, dir, byUnit) {
+ var bidi = getOrder(line);
+ if (!bidi) return moveLogically(line, start, dir, byUnit);
+ var pos = getBidiPartAt(bidi, start), part = bidi[pos];
+ var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit);
+
+ for (;;) {
+ if (target > part.from && target < part.to) return target;
+ if (target == part.from || target == part.to) {
+ if (getBidiPartAt(bidi, target) == pos) return target;
+ part = bidi[pos += dir];
+ return (dir > 0) == part.level % 2 ? part.to : part.from;
+ } else {
+ part = bidi[pos += dir];
+ if (!part) return null;
+ if ((dir > 0) == part.level % 2)
+ target = moveInLine(line, part.to, -1, byUnit);
+ else
+ target = moveInLine(line, part.from, 1, byUnit);
+ }
+ }
+ }
+
+ function moveLogically(line, start, dir, byUnit) {
+ var target = start + dir;
+ if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir;
+ return target < 0 || target > line.text.length ? null : target;
+ }
+
+ // 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 0x6ff
+ var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm";
+ function charType(code) {
+ if (code <= 0xf7) return lowTypes.charAt(code);
+ else if (0x590 <= code && code <= 0x5f4) return "R";
+ else if (0x600 <= code && code <= 0x6ed) 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]/;
+ // Browsers seem to always treat the boundaries of block elements as being L.
+ var outerType = "L";
+
+ function BidiSpan(level, from, to) {
+ this.level = level;
+ this.from = from; this.to = to;
+ }
+
+ return function(str) {
+ if (!bidiRE.test(str)) return false;
+ var len = str.length, types = [];
+ for (var i = 0, type; i < len; ++i)
+ types.push(type = 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 = 0, prev = outerType; i < len; ++i) {
+ var type = types[i];
+ if (type == "m") types[i] = 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 = 0, cur = outerType; i < len; ++i) {
+ var type = types[i];
+ if (type == "1" && cur == "r") types[i] = "n";
+ else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "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 = 1, prev = types[0]; i < len - 1; ++i) {
+ var type = types[i];
+ if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
+ else if (type == "," && prev == types[i+1] &&
+ (prev == "1" || prev == "n")) types[i] = prev;
+ prev = type;
+ }
+
+ // 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 = 0; i < len; ++i) {
+ var type = types[i];
+ if (type == ",") types[i] = "N";
+ else if (type == "%") {
+ for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
+ var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
+ for (var j = i; j < end; ++j) types[j] = replace;
+ i = 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 = 0, cur = outerType; i < len; ++i) {
+ var type = types[i];
+ if (cur == "L" && type == "1") types[i] = "L";
+ else if (isStrong.test(type)) cur = type;
+ }
+
+ // 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 = 0; i < len; ++i) {
+ if (isNeutral.test(types[i])) {
+ for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
+ var before = (i ? types[i-1] : outerType) == "L";
+ var after = (end < len ? types[end] : outerType) == "L";
+ var replace = before || after ? "L" : "R";
+ for (var j = i; j < end; ++j) types[j] = replace;
+ i = end - 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 = 0; i < len;) {
+ if (countsAsLeft.test(types[i])) {
+ var start = i;
+ for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
+ order.push(new BidiSpan(0, start, i));
+ } else {
+ var pos = i, at = order.length;
+ for (++i; i < len && types[i] != "L"; ++i) {}
+ for (var j = pos; j < i;) {
+ if (countsAsNum.test(types[j])) {
+ if (pos < j) order.splice(at, 0, new BidiSpan(1, pos, j));
+ var nstart = j;
+ for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
+ order.splice(at, 0, new BidiSpan(2, nstart, j));
+ pos = j;
+ } else ++j;
+ }
+ if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i));
+ }
+ }
+ 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));
+ }
+ if (order[0].level == 2)
+ order.unshift(new BidiSpan(1, order[0].to, order[0].to));
+ if (order[0].level != lst(order).level)
+ order.push(new BidiSpan(order[0].level, len, len));
+
+ return order;
+ };
+ })();
+
+ // THE END
+
+ CodeMirror.version = "5.7.1";
+
+ return CodeMirror;
+});
+
+},{}],7:[function(require,module,exports){
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"), require("../markdown/markdown"), require("../../addon/mode/overlay"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "../markdown/markdown", "../../addon/mode/overlay"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+var urlRE = /^((?:(?:aaas?|about|acap|adiumxtra|af[ps]|aim|apt|attachment|aw|beshare|bitcoin|bolo|callto|cap|chrome(?:-extension)?|cid|coap|com-eventbrite-attendee|content|crid|cvs|data|dav|dict|dlna-(?:playcontainer|playsingle)|dns|doi|dtn|dvb|ed2k|facetime|feed|file|finger|fish|ftp|geo|gg|git|gizmoproject|go|gopher|gtalk|h323|hcp|https?|iax|icap|icon|im|imap|info|ipn|ipp|irc[6s]?|iris(?:\.beep|\.lwz|\.xpc|\.xpcs)?|itms|jar|javascript|jms|keyparc|lastfm|ldaps?|magnet|mailto|maps|market|message|mid|mms|ms-help|msnim|msrps?|mtqp|mumble|mupdate|mvn|news|nfs|nih?|nntp|notes|oid|opaquelocktoken|palm|paparazzi|platform|pop|pres|proxy|psyc|query|res(?:ource)?|rmi|rsync|rtmp|rtsp|secondlife|service|session|sftp|sgn|shttp|sieve|sips?|skype|sm[bs]|snmp|soap\.beeps?|soldat|spotify|ssh|steam|svn|tag|teamspeak|tel(?:net)?|tftp|things|thismessage|tip|tn3270|tv|udp|unreal|urn|ut2004|vemmi|ventrilo|view-source|webcal|wss?|wtai|wyciwyg|xcon(?:-userid)?|xfire|xmlrpc\.beeps?|xmpp|xri|ymsgr|z39\.50[rs]?):(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]|\([^\s()<>]*\))+(?:\([^\s()<>]*\)|[^\s`*!()\[\]{};:'".,<>?«»“”‘’]))/i
+
+CodeMirror.defineMode("gfm", function(config, modeConfig) {
+ var codeDepth = 0;
+ function blankLine(state) {
+ state.code = false;
+ return null;
+ }
+ var gfmOverlay = {
+ startState: function() {
+ return {
+ code: false,
+ codeBlock: false,
+ ateSpace: false
+ };
+ },
+ copyState: function(s) {
+ return {
+ code: s.code,
+ codeBlock: s.codeBlock,
+ ateSpace: s.ateSpace
+ };
+ },
+ token: function(stream, state) {
+ state.combineTokens = null;
+
+ // Hack to prevent formatting override inside code blocks (block and inline)
+ if (state.codeBlock) {
+ if (stream.match(/^```+/)) {
+ state.codeBlock = false;
+ return null;
+ }
+ stream.skipToEnd();
+ return null;
+ }
+ if (stream.sol()) {
+ state.code = false;
+ }
+ if (stream.sol() && stream.match(/^```+/)) {
+ stream.skipToEnd();
+ state.codeBlock = true;
+ return null;
+ }
+ // If this block is changed, it may need to be updated in Markdown mode
+ if (stream.peek() === '`') {
+ stream.next();
+ var before = stream.pos;
+ stream.eatWhile('`');
+ var difference = 1 + stream.pos - before;
+ if (!state.code) {
+ codeDepth = difference;
+ state.code = true;
+ } else {
+ if (difference === codeDepth) { // Must be exact
+ state.code = false;
+ }
+ }
+ return null;
+ } else if (state.code) {
+ stream.next();
+ return null;
+ }
+ // Check if space. If so, links can be formatted later on
+ if (stream.eatSpace()) {
+ state.ateSpace = true;
+ return null;
+ }
+ if (stream.sol() || state.ateSpace) {
+ state.ateSpace = false;
+ if (modeConfig.gitHubSpice !== false) {
+ if(stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+@)?(?:[a-f0-9]{7,40}\b)/)) {
+ // User/Project@SHA
+ // User@SHA
+ // SHA
+ state.combineTokens = true;
+ return "link";
+ } else if (stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+)?#[0-9]+\b/)) {
+ // User/Project#Num
+ // User#Num
+ // #Num
+ state.combineTokens = true;
+ return "link";
+ }
+ }
+ }
+ if (stream.match(urlRE) &&
+ stream.string.slice(stream.start - 2, stream.start) != "](" &&
+ (stream.start == 0 || /\W/.test(stream.string.charAt(stream.start - 1)))) {
+ // URLs
+ // Taken from http://daringfireball.net/2010/07/improved_regex_for_matching_urls
+ // And then (issue #1160) simplified to make it not crash the Chrome Regexp engine
+ // And then limited url schemes to the CommonMark list, so foo:bar isn't matched as a URL
+ state.combineTokens = true;
+ return "link";
+ }
+ stream.next();
+ return null;
+ },
+ blankLine: blankLine
+ };
+
+ var markdownConfig = {
+ underscoresBreakWords: false,
+ taskLists: true,
+ fencedCodeBlocks: '```',
+ strikethrough: true
+ };
+ for (var attr in modeConfig) {
+ markdownConfig[attr] = modeConfig[attr];
+ }
+ markdownConfig.name = "markdown";
+ return CodeMirror.overlayMode(CodeMirror.getMode(config, markdownConfig), gfmOverlay);
+
+}, "markdown");
+
+ CodeMirror.defineMIME("text/x-gfm", "gfm");
+});
+
+},{"../../addon/mode/overlay":5,"../../lib/codemirror":6,"../markdown/markdown":8}],8:[function(require,module,exports){
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://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 htmlFound = CodeMirror.modes.hasOwnProperty("xml");
+ var htmlMode = CodeMirror.getMode(cmCfg, htmlFound ? {name: "xml", htmlMode: true} : "text/plain");
+
+ 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;
+
+ // Should underscores in words open/close em/strong?
+ if (modeCfg.underscoresBreakWords === undefined)
+ modeCfg.underscoresBreakWords = true;
+
+ // Use `fencedCodeBlocks` to configure fenced code blocks. false to
+ // disable, string to specify a precise regexp that the fence should
+ // match, and true to allow three or more backticks or tildes (as
+ // per CommonMark).
+
+ // Turn on task lists? ("- [ ] " and "- [x] ")
+ if (modeCfg.taskLists === undefined) modeCfg.taskLists = false;
+
+ // Turn on strikethrough syntax
+ if (modeCfg.strikethrough === undefined)
+ modeCfg.strikethrough = false;
+
+ var codeDepth = 0;
+
+ var header = 'header'
+ , code = 'comment'
+ , quote = 'quote'
+ , list1 = 'variable-2'
+ , list2 = 'variable-3'
+ , list3 = 'keyword'
+ , hr = 'hr'
+ , image = 'tag'
+ , formatting = 'formatting'
+ , linkinline = 'link'
+ , linkemail = 'link'
+ , linktext = 'link'
+ , linkhref = 'string'
+ , em = 'em'
+ , strong = 'strong'
+ , strikethrough = 'strikethrough';
+
+ var hrRE = /^([*\-_])(?:\s*\1){2,}\s*$/
+ , ulRE = /^[*\-+]\s+/
+ , olRE = /^[0-9]+([.)])\s+/
+ , taskListRE = /^\[(x| )\](?=\s)/ // Must follow ulRE or olRE
+ , atxHeaderRE = modeCfg.allowAtxHeaderWithoutSpace ? /^(#+)/ : /^(#+)(?: |$)/
+ , setextHeaderRE = /^ *(?:\={1,}|-{1,})\s*$/
+ , textRE = /^[^#!\[\]*_\\<>` "'(~]+/
+ , fencedCodeRE = new RegExp("^(" + (modeCfg.fencedCodeBlocks === true ? "~~~+|```+" : modeCfg.fencedCodeBlocks) +
+ ")[ \\t]*([\\w+#]*)");
+
+ 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;
+ // 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 (!htmlFound && state.f == htmlBlock) {
+ state.f = inlineNormal;
+ state.block = blockNormal;
+ }
+ // Reset state.trailingSpace
+ state.trailingSpace = 0;
+ state.trailingSpaceNewLine = false;
+ // Mark this line as blank
+ state.prevLine = state.thisLine
+ state.thisLine = null
+ return null;
+ }
+
+ function blockNormal(stream, state) {
+
+ var sol = stream.sol();
+
+ var prevLineIsList = state.list !== false,
+ prevLineIsIndentedCode = state.indentedCode;
+
+ state.indentedCode = false;
+
+ if (prevLineIsList) {
+ if (state.indentationDiff >= 0) { // Continued list
+ if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block
+ state.indentation -= state.indentationDiff;
+ }
+ state.list = null;
+ } else if (state.indentation > 0) {
+ state.list = null;
+ state.listDepth = Math.floor(state.indentation / 4);
+ } else { // No longer a list
+ state.list = false;
+ state.listDepth = 0;
+ }
+ }
+
+ var match = null;
+ if (state.indentationDiff >= 4) {
+ stream.skipToEnd();
+ if (prevLineIsIndentedCode || lineIsEmpty(state.prevLine)) {
+ state.indentation -= 4;
+ state.indentedCode = true;
+ return code;
+ } else {
+ return null;
+ }
+ } else if (stream.eatSpace()) {
+ return null;
+ } else if ((match = stream.match(atxHeaderRE)) && match[1].length <= 6) {
+ state.header = match[1].length;
+ if (modeCfg.highlightFormatting) state.formatting = "header";
+ state.f = state.inline;
+ return getType(state);
+ } else if (!lineIsEmpty(state.prevLine) && !state.quote && !prevLineIsList &&
+ !prevLineIsIndentedCode && (match = stream.match(setextHeaderRE))) {
+ state.header = match[0].charAt(0) == '=' ? 1 : 2;
+ if (modeCfg.highlightFormatting) state.formatting = "header";
+ state.f = state.inline;
+ return getType(state);
+ } else if (stream.eat('>')) {
+ state.quote = sol ? 1 : state.quote + 1;
+ if (modeCfg.highlightFormatting) state.formatting = "quote";
+ stream.eatSpace();
+ return getType(state);
+ } else if (stream.peek() === '[') {
+ return switchInline(stream, state, footnoteLink);
+ } else if (stream.match(hrRE, true)) {
+ state.hr = true;
+ return hr;
+ } else if ((lineIsEmpty(state.prevLine) || prevLineIsList) && (stream.match(ulRE, false) || stream.match(olRE, false))) {
+ var listType = null;
+ if (stream.match(ulRE, true)) {
+ listType = 'ul';
+ } else {
+ stream.match(olRE, true);
+ listType = 'ol';
+ }
+ state.indentation = stream.column() + stream.current().length;
+ state.list = true;
+ state.listDepth++;
+ 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 (modeCfg.fencedCodeBlocks && (match = stream.match(fencedCodeRE, true))) {
+ state.fencedChars = match[1]
+ // try switching mode
+ state.localMode = getMode(match[2]);
+ if (state.localMode) state.localState = state.localMode.startState();
+ state.f = state.block = local;
+ if (modeCfg.highlightFormatting) state.formatting = "code-block";
+ state.code = true;
+ return getType(state);
+ }
+
+ return switchInline(stream, state, state.inline);
+ }
+
+ function htmlBlock(stream, state) {
+ var style = htmlMode.token(stream, state.htmlState);
+ if ((htmlFound && state.htmlState.tagStart === null &&
+ (!state.htmlState.context && state.htmlState.tokenize.isInText)) ||
+ (state.md_inside && stream.current().indexOf(">") > -1)) {
+ state.f = inlineNormal;
+ state.block = blockNormal;
+ state.htmlState = null;
+ }
+ return style;
+ }
+
+ function local(stream, state) {
+ if (stream.sol() && state.fencedChars && stream.match(state.fencedChars, false)) {
+ state.localMode = state.localState = null;
+ state.f = state.block = leavingLocal;
+ return null;
+ } else if (state.localMode) {
+ return state.localMode.token(stream, state.localState);
+ } else {
+ stream.skipToEnd();
+ return code;
+ }
+ }
+
+ function leavingLocal(stream, state) {
+ stream.match(state.fencedChars);
+ state.block = blockNormal;
+ state.f = inlineNormal;
+ state.fencedChars = null;
+ if (modeCfg.highlightFormatting) state.formatting = "code-block";
+ state.code = true;
+ var returnType = getType(state);
+ state.code = false;
+ return returnType;
+ }
+
+ // Inline
+ function getType(state) {
+ var styles = [];
+
+ if (state.formatting) {
+ styles.push(formatting);
+
+ if (typeof state.formatting === "string") state.formatting = [state.formatting];
+
+ for (var i = 0; i < state.formatting.length; i++) {
+ styles.push(formatting + "-" + state.formatting[i]);
+
+ if (state.formatting[i] === "header") {
+ styles.push(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(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(linkhref, "url");
+ } else { // Only apply inline styles to non-url text
+ if (state.strong) { styles.push(strong); }
+ if (state.em) { styles.push(em); }
+ if (state.strikethrough) { styles.push(strikethrough); }
+
+ if (state.linkText) { styles.push(linktext); }
+
+ if (state.code) { styles.push(code); }
+ }
+
+ if (state.header) { styles.push(header); styles.push(header + "-" + state.header); }
+
+ if (state.quote) {
+ styles.push(quote);
+
+ // Add `quote-#` where the maximum for `#` is modeCfg.maxBlockquoteDepth
+ if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
+ styles.push(quote + "-" + state.quote);
+ } else {
+ styles.push(quote + "-" + modeCfg.maxBlockquoteDepth);
+ }
+ }
+
+ if (state.list !== false) {
+ var listMod = (state.listDepth - 1) % 3;
+ if (!listMod) {
+ styles.push(list1);
+ } else if (listMod === 1) {
+ styles.push(list2);
+ } else {
+ styles.push(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] !== "x";
+ 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);
+ }
+
+ // Get sol() value now, before character is consumed
+ var sol = stream.sol();
+
+ var ch = stream.next();
+
+ if (ch === '\\') {
+ stream.next();
+ if (modeCfg.highlightFormatting) {
+ var type = getType(state);
+ return type ? type + " formatting-escape" : "formatting-escape";
+ }
+ }
+
+ // 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 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";
+ var t = getType(state);
+ var before = stream.pos;
+ stream.eatWhile('`');
+ var difference = 1 + stream.pos - before;
+ if (!state.code) {
+ codeDepth = difference;
+ state.code = true;
+ return getType(state);
+ } else {
+ if (difference === codeDepth) { // Must be exact
+ state.code = false;
+ return t;
+ }
+ state.formatting = previousFormatting;
+ return getType(state);
+ }
+ } else if (state.code) {
+ return getType(state);
+ }
+
+ if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) {
+ stream.match(/\[[^\]]*\]/);
+ state.inline = state.f = linkHref;
+ return image;
+ }
+
+ if (ch === '[' && stream.match(/.*\](\(.*\)| ?\[.*\])/, false)) {
+ state.linkText = true;
+ if (modeCfg.highlightFormatting) state.formatting = "link";
+ return getType(state);
+ }
+
+ if (ch === ']' && state.linkText && stream.match(/\(.*\)| ?\[.*\]/, false)) {
+ if (modeCfg.highlightFormatting) state.formatting = "link";
+ var type = getType(state);
+ state.linkText = false;
+ state.inline = state.f = linkHref;
+ 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 + 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 + linkemail;
+ }
+
+ if (ch === '<' && stream.match(/^(!--|\w)/, 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 (ch === '<' && stream.match(/^\/\w*?>/)) {
+ state.md_inside = false;
+ return "tag";
+ }
+
+ var ignoreUnderscore = false;
+ if (!modeCfg.underscoresBreakWords) {
+ if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) {
+ var prevPos = stream.pos - 2;
+ if (prevPos >= 0) {
+ var prevCh = stream.string.charAt(prevPos);
+ if (prevCh !== '_' && prevCh.match(/(\w)/, false)) {
+ ignoreUnderscore = true;
+ }
+ }
+ }
+ }
+ if (ch === '*' || (ch === '_' && !ignoreUnderscore)) {
+ if (sol && stream.peek() === ' ') {
+ // Do nothing, surrounded by newline and space
+ } else if (state.strong === ch && stream.eat(ch)) { // Remove STRONG
+ if (modeCfg.highlightFormatting) state.formatting = "strong";
+ var t = getType(state);
+ state.strong = false;
+ return t;
+ } else if (!state.strong && stream.eat(ch)) { // Add STRONG
+ state.strong = ch;
+ if (modeCfg.highlightFormatting) state.formatting = "strong";
+ return getType(state);
+ } else if (state.em === ch) { // Remove EM
+ if (modeCfg.highlightFormatting) state.formatting = "em";
+ var t = getType(state);
+ state.em = false;
+ return t;
+ } else if (!state.em) { // Add EM
+ state.em = ch;
+ if (modeCfg.highlightFormatting) state.formatting = "em";
+ return getType(state);
+ }
+ } 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 (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 + linkinline;
+ }
+
+ stream.match(/^[^>]+/, true);
+
+ return 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';
+ }
+
+ 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;
+ }
+
+ if (stream.match(inlineRE(endChar), true)) {
+ stream.backUp(1);
+ }
+
+ 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 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 linkhref + " url";
+ }
+
+ var savedInlineRE = [];
+ function inlineRE(endChar) {
+ if (!savedInlineRE[endChar]) {
+ // Escape endChar for RegExp (taken from http://stackoverflow.com/a/494122/526741)
+ endChar = (endChar+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
+ // Match any non-endChar, escaped character, as well as the closing
+ // endChar.
+ savedInlineRE[endChar] = new RegExp('^(?:[^\\\\]|\\\\.)*?(' + endChar + ')');
+ }
+ return savedInlineRE[endChar];
+ }
+
+ var mode = {
+ startState: function() {
+ return {
+ f: blockNormal,
+
+ prevLine: null,
+ thisLine: null,
+
+ block: blockNormal,
+ htmlState: null,
+ indentation: 0,
+
+ inline: inlineNormal,
+ text: handleText,
+
+ formatting: false,
+ linkText: false,
+ linkHref: false,
+ linkTitle: false,
+ em: false,
+ strong: false,
+ header: 0,
+ hr: false,
+ taskList: false,
+ list: false,
+ listDepth: 0,
+ quote: 0,
+ trailingSpace: 0,
+ trailingSpaceNewLine: false,
+ strikethrough: false,
+ fencedChars: null
+ };
+ },
+
+ copyState: function(s) {
+ return {
+ f: s.f,
+
+ prevLine: s.prevLine,
+ thisLine: s.this,
+
+ 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,
+ linkTitle: s.linkTitle,
+ code: s.code,
+ em: s.em,
+ strong: s.strong,
+ strikethrough: s.strikethrough,
+ header: s.header,
+ hr: s.hr,
+ taskList: s.taskList,
+ list: s.list,
+ listDepth: s.listDepth,
+ quote: s.quote,
+ indentedCode: s.indentedCode,
+ trailingSpace: s.trailingSpace,
+ trailingSpaceNewLine: s.trailingSpaceNewLine,
+ md_inside: s.md_inside,
+ fencedChars: s.fencedChars
+ };
+ },
+
+ token: function(stream, state) {
+
+ // Reset state.formatting
+ state.formatting = false;
+
+ if (stream != state.thisLine) {
+ var forceBlankLine = state.header || state.hr;
+
+ // Reset state.header and state.hr
+ state.header = 0;
+ state.hr = false;
+
+ if (stream.match(/^\s*$/, true) || forceBlankLine) {
+ blankLine(state);
+ if (!forceBlankLine) return null
+ state.prevLine = null
+ }
+
+ state.prevLine = state.thisLine
+ state.thisLine = stream
+
+ // Reset state.taskList
+ state.taskList = false;
+
+ // Reset state.trailingSpace
+ state.trailingSpace = 0;
+ state.trailingSpaceNewLine = false;
+
+ state.f = state.block;
+ var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, ' ').length;
+ var difference = Math.floor((indentation - state.indentation) / 4) * 4;
+ if (difference > 4) difference = 4;
+ var adjustedIndentation = state.indentation + difference;
+ state.indentationDiff = adjustedIndentation - state.indentation;
+ state.indentation = adjustedIndentation;
+ 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};
+ },
+
+ blankLine: blankLine,
+
+ getType: getType,
+
+ fold: "markdown"
+ };
+ return mode;
+}, "xml");
+
+CodeMirror.defineMIME("text/x-markdown", "markdown");
+
+});
+
+},{"../../lib/codemirror":6,"../meta":9,"../xml/xml":10}],9:[function(require,module,exports){
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://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.modeInfo = [
+ {name: "APL", mime: "text/apl", mode: "apl", ext: ["dyalog", "apl"]},
+ {name: "PGP", mimes: ["application/pgp", "application/pgp-keys", "application/pgp-signature"], mode: "asciiarmor", ext: ["pgp"]},
+ {name: "ASN.1", mime: "text/x-ttcn-asn", mode: "asn.1", ext: ["asn", "asn1"]},
+ {name: "Asterisk", mime: "text/x-asterisk", mode: "asterisk", file: /^extensions\.conf$/i},
+ {name: "Brainfuck", mime: "text/x-brainfuck", mode: "brainfuck", ext: ["b", "bf"]},
+ {name: "C", mime: "text/x-csrc", mode: "clike", ext: ["c", "h"]},
+ {name: "C++", mime: "text/x-c++src", mode: "clike", ext: ["cpp", "c++", "cc", "cxx", "hpp", "h++", "hh", "hxx"], alias: ["cpp"]},
+ {name: "Cobol", mime: "text/x-cobol", mode: "cobol", ext: ["cob", "cpy"]},
+ {name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp"]},
+ {name: "Clojure", mime: "text/x-clojure", mode: "clojure", ext: ["clj"]},
+ {name: "Closure Stylesheets (GSS)", mime: "text/x-gss", mode: "css", ext: ["gss"]},
+ {name: "CMake", mime: "text/x-cmake", mode: "cmake", ext: ["cmake", "cmake.in"], file: /^CMakeLists.txt$/},
+ {name: "CoffeeScript", mime: "text/x-coffeescript", mode: "coffeescript", ext: ["coffee"], alias: ["coffee", "coffee-script"]},
+ {name: "Common Lisp", mime: "text/x-common-lisp", mode: "commonlisp", ext: ["cl", "lisp", "el"], alias: ["lisp"]},
+ {name: "Cypher", mime: "application/x-cypher-query", mode: "cypher", ext: ["cyp", "cypher"]},
+ {name: "Cython", mime: "text/x-cython", mode: "python", ext: ["pyx", "pxd", "pxi"]},
+ {name: "CSS", mime: "text/css", mode: "css", ext: ["css"]},
+ {name: "CQL", mime: "text/x-cassandra", mode: "sql", ext: ["cql"]},
+ {name: "D", mime: "text/x-d", mode: "d", ext: ["d"]},
+ {name: "Dart", mimes: ["application/dart", "text/x-dart"], mode: "dart", ext: ["dart"]},
+ {name: "diff", mime: "text/x-diff", mode: "diff", ext: ["diff", "patch"]},
+ {name: "Django", mime: "text/x-django", mode: "django"},
+ {name: "Dockerfile", mime: "text/x-dockerfile", mode: "dockerfile", file: /^Dockerfile$/},
+ {name: "DTD", mime: "application/xml-dtd", mode: "dtd", ext: ["dtd"]},
+ {name: "Dylan", mime: "text/x-dylan", mode: "dylan", ext: ["dylan", "dyl", "intr"]},
+ {name: "EBNF", mime: "text/x-ebnf", mode: "ebnf"},
+ {name: "ECL", mime: "text/x-ecl", mode: "ecl", ext: ["ecl"]},
+ {name: "Eiffel", mime: "text/x-eiffel", mode: "eiffel", ext: ["e"]},
+ {name: "Elm", mime: "text/x-elm", mode: "elm", ext: ["elm"]},
+ {name: "Embedded Javascript", mime: "application/x-ejs", mode: "htmlembedded", ext: ["ejs"]},
+ {name: "Embedded Ruby", mime: "application/x-erb", mode: "htmlembedded", ext: ["erb"]},
+ {name: "Erlang", mime: "text/x-erlang", mode: "erlang", ext: ["erl"]},
+ {name: "Factor", mime: "text/x-factor", mode: "factor", ext: ["factor"]},
+ {name: "Forth", mime: "text/x-forth", mode: "forth", ext: ["forth", "fth", "4th"]},
+ {name: "Fortran", mime: "text/x-fortran", mode: "fortran", ext: ["f", "for", "f77", "f90"]},
+ {name: "F#", mime: "text/x-fsharp", mode: "mllike", ext: ["fs"], alias: ["fsharp"]},
+ {name: "Gas", mime: "text/x-gas", mode: "gas", ext: ["s"]},
+ {name: "Gherkin", mime: "text/x-feature", mode: "gherkin", ext: ["feature"]},
+ {name: "GitHub Flavored Markdown", mime: "text/x-gfm", mode: "gfm", file: /^(readme|contributing|history).md$/i},
+ {name: "Go", mime: "text/x-go", mode: "go", ext: ["go"]},
+ {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy"]},
+ {name: "HAML", mime: "text/x-haml", mode: "haml", ext: ["haml"]},
+ {name: "Haskell", mime: "text/x-haskell", mode: "haskell", ext: ["hs"]},
+ {name: "Haxe", mime: "text/x-haxe", mode: "haxe", ext: ["hx"]},
+ {name: "HXML", mime: "text/x-hxml", mode: "haxe", ext: ["hxml"]},
+ {name: "ASP.NET", mime: "application/x-aspx", mode: "htmlembedded", ext: ["aspx"], alias: ["asp", "aspx"]},
+ {name: "HTML", mime: "text/html", mode: "htmlmixed", ext: ["html", "htm"], alias: ["xhtml"]},
+ {name: "HTTP", mime: "message/http", mode: "http"},
+ {name: "IDL", mime: "text/x-idl", mode: "idl", ext: ["pro"]},
+ {name: "Jade", mime: "text/x-jade", mode: "jade", ext: ["jade"]},
+ {name: "Java", mime: "text/x-java", mode: "clike", ext: ["java"]},
+ {name: "Java Server Pages", mime: "application/x-jsp", mode: "htmlembedded", ext: ["jsp"], alias: ["jsp"]},
+ {name: "JavaScript", mimes: ["text/javascript", "text/ecmascript", "application/javascript", "application/x-javascript", "application/ecmascript"],
+ mode: "javascript", ext: ["js"], alias: ["ecmascript", "js", "node"]},
+ {name: "JSON", mimes: ["application/json", "application/x-json"], mode: "javascript", ext: ["json", "map"], alias: ["json5"]},
+ {name: "JSON-LD", mime: "application/ld+json", mode: "javascript", ext: ["jsonld"], alias: ["jsonld"]},
+ {name: "Jinja2", mime: "null", mode: "jinja2"},
+ {name: "Julia", mime: "text/x-julia", mode: "julia", ext: ["jl"]},
+ {name: "Kotlin", mime: "text/x-kotlin", mode: "clike", ext: ["kt"]},
+ {name: "LESS", mime: "text/x-less", mode: "css", ext: ["less"]},
+ {name: "LiveScript", mime: "text/x-livescript", mode: "livescript", ext: ["ls"], alias: ["ls"]},
+ {name: "Lua", mime: "text/x-lua", mode: "lua", ext: ["lua"]},
+ {name: "Markdown", mime: "text/x-markdown", mode: "markdown", ext: ["markdown", "md", "mkd"]},
+ {name: "mIRC", mime: "text/mirc", mode: "mirc"},
+ {name: "MariaDB SQL", mime: "text/x-mariadb", mode: "sql"},
+ {name: "Mathematica", mime: "text/x-mathematica", mode: "mathematica", ext: ["m", "nb"]},
+ {name: "Modelica", mime: "text/x-modelica", mode: "modelica", ext: ["mo"]},
+ {name: "MUMPS", mime: "text/x-mumps", mode: "mumps"},
+ {name: "MS SQL", mime: "text/x-mssql", mode: "sql"},
+ {name: "MySQL", mime: "text/x-mysql", mode: "sql"},
+ {name: "Nginx", mime: "text/x-nginx-conf", mode: "nginx", file: /nginx.*\.conf$/i},
+ {name: "NTriples", mime: "text/n-triples", mode: "ntriples", ext: ["nt"]},
+ {name: "Objective C", mime: "text/x-objectivec", mode: "clike", ext: ["m", "mm"]},
+ {name: "OCaml", mime: "text/x-ocaml", mode: "mllike", ext: ["ml", "mli", "mll", "mly"]},
+ {name: "Octave", mime: "text/x-octave", mode: "octave", ext: ["m"]},
+ {name: "Oz", mime: "text/x-oz", mode: "oz", ext: ["oz"]},
+ {name: "Pascal", mime: "text/x-pascal", mode: "pascal", ext: ["p", "pas"]},
+ {name: "PEG.js", mime: "null", mode: "pegjs", ext: ["jsonld"]},
+ {name: "Perl", mime: "text/x-perl", mode: "perl", ext: ["pl", "pm"]},
+ {name: "PHP", mime: "application/x-httpd-php", mode: "php", ext: ["php", "php3", "php4", "php5", "phtml"]},
+ {name: "Pig", mime: "text/x-pig", mode: "pig", ext: ["pig"]},
+ {name: "Plain Text", mime: "text/plain", mode: "null", ext: ["txt", "text", "conf", "def", "list", "log"]},
+ {name: "PLSQL", mime: "text/x-plsql", mode: "sql", ext: ["pls"]},
+ {name: "Properties files", mime: "text/x-properties", mode: "properties", ext: ["properties", "ini", "in"], alias: ["ini", "properties"]},
+ {name: "Python", mime: "text/x-python", mode: "python", ext: ["py", "pyw"]},
+ {name: "Puppet", mime: "text/x-puppet", mode: "puppet", ext: ["pp"]},
+ {name: "Q", mime: "text/x-q", mode: "q", ext: ["q"]},
+ {name: "R", mime: "text/x-rsrc", mode: "r", ext: ["r"], alias: ["rscript"]},
+ {name: "reStructuredText", mime: "text/x-rst", mode: "rst", ext: ["rst"], alias: ["rst"]},
+ {name: "RPM Changes", mime: "text/x-rpm-changes", mode: "rpm"},
+ {name: "RPM Spec", mime: "text/x-rpm-spec", mode: "rpm", ext: ["spec"]},
+ {name: "Ruby", mime: "text/x-ruby", mode: "ruby", ext: ["rb"], alias: ["jruby", "macruby", "rake", "rb", "rbx"]},
+ {name: "Rust", mime: "text/x-rustsrc", mode: "rust", ext: ["rs"]},
+ {name: "Sass", mime: "text/x-sass", mode: "sass", ext: ["sass"]},
+ {name: "Scala", mime: "text/x-scala", mode: "clike", ext: ["scala"]},
+ {name: "Scheme", mime: "text/x-scheme", mode: "scheme", ext: ["scm", "ss"]},
+ {name: "SCSS", mime: "text/x-scss", mode: "css", ext: ["scss"]},
+ {name: "Shell", mime: "text/x-sh", mode: "shell", ext: ["sh", "ksh", "bash"], alias: ["bash", "sh", "zsh"]},
+ {name: "Sieve", mime: "application/sieve", mode: "sieve", ext: ["siv", "sieve"]},
+ {name: "Slim", mimes: ["text/x-slim", "application/x-slim"], mode: "slim", ext: ["slim"]},
+ {name: "Smalltalk", mime: "text/x-stsrc", mode: "smalltalk", ext: ["st"]},
+ {name: "Smarty", mime: "text/x-smarty", mode: "smarty", ext: ["tpl"]},
+ {name: "Solr", mime: "text/x-solr", mode: "solr"},
+ {name: "Soy", mime: "text/x-soy", mode: "soy", ext: ["soy"], alias: ["closure template"]},
+ {name: "SPARQL", mime: "application/sparql-query", mode: "sparql", ext: ["rq", "sparql"], alias: ["sparul"]},
+ {name: "Spreadsheet", mime: "text/x-spreadsheet", mode: "spreadsheet", alias: ["excel", "formula"]},
+ {name: "SQL", mime: "text/x-sql", mode: "sql", ext: ["sql"]},
+ {name: "Squirrel", mime: "text/x-squirrel", mode: "clike", ext: ["nut"]},
+ {name: "Swift", mime: "text/x-swift", mode: "swift", ext: ["swift"]},
+ {name: "MariaDB", mime: "text/x-mariadb", mode: "sql"},
+ {name: "sTeX", mime: "text/x-stex", mode: "stex"},
+ {name: "LaTeX", mime: "text/x-latex", mode: "stex", ext: ["text", "ltx"], alias: ["tex"]},
+ {name: "SystemVerilog", mime: "text/x-systemverilog", mode: "verilog", ext: ["v"]},
+ {name: "Tcl", mime: "text/x-tcl", mode: "tcl", ext: ["tcl"]},
+ {name: "Textile", mime: "text/x-textile", mode: "textile", ext: ["textile"]},
+ {name: "TiddlyWiki ", mime: "text/x-tiddlywiki", mode: "tiddlywiki"},
+ {name: "Tiki wiki", mime: "text/tiki", mode: "tiki"},
+ {name: "TOML", mime: "text/x-toml", mode: "toml", ext: ["toml"]},
+ {name: "Tornado", mime: "text/x-tornado", mode: "tornado"},
+ {name: "troff", mime: "troff", mode: "troff", ext: ["1", "2", "3", "4", "5", "6", "7", "8", "9"]},
+ {name: "TTCN", mime: "text/x-ttcn", mode: "ttcn", ext: ["ttcn", "ttcn3", "ttcnpp"]},
+ {name: "TTCN_CFG", mime: "text/x-ttcn-cfg", mode: "ttcn-cfg", ext: ["cfg"]},
+ {name: "Turtle", mime: "text/turtle", mode: "turtle", ext: ["ttl"]},
+ {name: "TypeScript", mime: "application/typescript", mode: "javascript", ext: ["ts"], alias: ["ts"]},
+ {name: "Twig", mime: "text/x-twig", mode: "twig"},
+ {name: "VB.NET", mime: "text/x-vb", mode: "vb", ext: ["vb"]},
+ {name: "VBScript", mime: "text/vbscript", mode: "vbscript", ext: ["vbs"]},
+ {name: "Velocity", mime: "text/velocity", mode: "velocity", ext: ["vtl"]},
+ {name: "Verilog", mime: "text/x-verilog", mode: "verilog", ext: ["v"]},
+ {name: "VHDL", mime: "text/x-vhdl", mode: "vhdl", ext: ["vhd", "vhdl"]},
+ {name: "XML", mimes: ["application/xml", "text/xml"], mode: "xml", ext: ["xml", "xsl", "xsd"], alias: ["rss", "wsdl", "xsd"]},
+ {name: "XQuery", mime: "application/xquery", mode: "xquery", ext: ["xy", "xquery"]},
+ {name: "YAML", mime: "text/x-yaml", mode: "yaml", ext: ["yaml", "yml"], alias: ["yml"]},
+ {name: "Z80", mime: "text/x-z80", mode: "z80", ext: ["z80"]},
+ {name: "mscgen", mime: "text/x-mscgen", mode: "mscgen", ext: ["mscgen", "mscin", "msc"]},
+ {name: "xu", mime: "text/x-xu", mode: "mscgen", ext: ["xu"]},
+ {name: "msgenny", mime: "text/x-msgenny", mode: "mscgen", ext: ["msgenny"]}
+ ];
+ // Ensure all modes have a mime property for backwards compatibility
+ for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
+ var info = CodeMirror.modeInfo[i];
+ if (info.mimes) info.mime = info.mimes[0];
+ }
+
+ CodeMirror.findModeByMIME = function(mime) {
+ mime = mime.toLowerCase();
+ for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
+ var info = CodeMirror.modeInfo[i];
+ if (info.mime == mime) return info;
+ if (info.mimes) for (var j = 0; j < info.mimes.length; j++)
+ if (info.mimes[j] == mime) return info;
+ }
+ };
+
+ CodeMirror.findModeByExtension = function(ext) {
+ for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
+ var info = CodeMirror.modeInfo[i];
+ if (info.ext) for (var j = 0; j < info.ext.length; j++)
+ if (info.ext[j] == ext) return info;
+ }
+ };
+
+ CodeMirror.findModeByFileName = function(filename) {
+ for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
+ var info = CodeMirror.modeInfo[i];
+ if (info.file && info.file.test(filename)) return info;
+ }
+ var dot = filename.lastIndexOf(".");
+ var ext = dot > -1 && filename.substring(dot + 1, filename.length);
+ if (ext) return CodeMirror.findModeByExtension(ext);
+ };
+
+ CodeMirror.findModeByName = function(name) {
+ name = name.toLowerCase();
+ for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
+ var info = CodeMirror.modeInfo[i];
+ if (info.name.toLowerCase() == name) return info;
+ if (info.alias) for (var j = 0; j < info.alias.length; j++)
+ if (info.alias[j].toLowerCase() == name) return info;
+ }
+ };
+});
+
+},{"../lib/codemirror":6}],10:[function(require,module,exports){
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://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("xml", function(config, parserConfig) {
+ var indentUnit = config.indentUnit;
+ var multilineTagIndentFactor = parserConfig.multilineTagIndentFactor || 1;
+ var multilineTagIndentPastTag = parserConfig.multilineTagIndentPastTag;
+ if (multilineTagIndentPastTag == null) multilineTagIndentPastTag = true;
+
+ var Kludges = parserConfig.htmlMode ? {
+ 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
+ } : {
+ autoSelfClosers: {},
+ implicitlyClosed: {},
+ contextGrabbers: {},
+ doNotIndent: {},
+ allowUnquoted: false,
+ allowMissing: false,
+ caseFold: false
+ };
+ var alignCDATA = parserConfig.alignCDATA;
+
+ // 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 (Kludges.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 (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
+ !Kludges.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 {
+ setStyle = "error";
+ return tagNameState;
+ }
+ }
+ function closeTagNameState(type, stream, state) {
+ if (type == "word") {
+ var tagName = stream.current();
+ if (state.context && state.context.tagName != tagName &&
+ Kludges.implicitlyClosed.hasOwnProperty(state.context.tagName))
+ popContext(state);
+ if (state.context && state.context.tagName == tagName) {
+ setStyle = "tag";
+ return closeState;
+ } else {
+ setStyle = "tag error";
+ return closeStateErr;
+ }
+ } 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" ||
+ Kludges.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 (!Kludges.allowMissing) setStyle = "error";
+ return attrState(type, stream, state);
+ }
+ function attrValueState(type, stream, state) {
+ if (type == "string") return attrContinuedState;
+ if (type == "word" && Kludges.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() {
+ return {tokenize: inText,
+ state: baseState,
+ indented: 0,
+ tagName: null, tagStart: null,
+ context: null};
+ },
+
+ 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 (multilineTagIndentPastTag)
+ return state.tagStart + state.tagName.length + 2;
+ else
+ return state.tagStart + indentUnit * multilineTagIndentFactor;
+ }
+ if (alignCDATA && /$/,
+ blockCommentStart: "",
+
+ configuration: parserConfig.htmlMode ? "html" : "xml",
+ helperType: parserConfig.htmlMode ? "html" : "xml"
+ };
+});
+
+CodeMirror.defineMIME("text/xml", "xml");
+CodeMirror.defineMIME("application/xml", "xml");
+if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
+ CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
+
+});
+
+},{"../../lib/codemirror":6}],11:[function(require,module,exports){
+(function (global){
+/**
+ * marked - a markdown parser
+ * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
+ * https://github.com/chjj/marked
+ */
+
+;(function() {
+
+/**
+ * Block-Level Grammar
+ */
+
+var block = {
+ newline: /^\n+/,
+ code: /^( {4}[^\n]+\n*)+/,
+ fences: noop,
+ hr: /^( *[-*_]){3,} *(?:\n+|$)/,
+ heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
+ nptable: noop,
+ lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
+ blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
+ list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
+ html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
+ def: /^ *\[([^\]]+)\]: *([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
+ table: noop,
+ paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
+ text: /^[^\n]+/
+};
+
+block.bullet = /(?:[*+-]|\d+\.)/;
+block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
+block.item = replace(block.item, 'gm')
+ (/bull/g, block.bullet)
+ ();
+
+block.list = replace(block.list)
+ (/bull/g, block.bullet)
+ ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
+ ('def', '\\n+(?=' + block.def.source + ')')
+ ();
+
+block.blockquote = replace(block.blockquote)
+ ('def', block.def)
+ ();
+
+block._tag = '(?!(?:'
+ + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
+ + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
+ + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
+
+block.html = replace(block.html)
+ ('comment', //)
+ ('closed', /<(tag)[\s\S]+?<\/\1>/)
+ ('closing', /])*?>/)
+ (/tag/g, block._tag)
+ ();
+
+block.paragraph = replace(block.paragraph)
+ ('hr', block.hr)
+ ('heading', block.heading)
+ ('lheading', block.lheading)
+ ('blockquote', block.blockquote)
+ ('tag', '<' + block._tag)
+ ('def', block.def)
+ ();
+
+/**
+ * Normal Block Grammar
+ */
+
+block.normal = merge({}, block);
+
+/**
+ * GFM Block Grammar
+ */
+
+block.gfm = merge({}, block.normal, {
+ fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
+ paragraph: /^/,
+ heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
+});
+
+block.gfm.paragraph = replace(block.paragraph)
+ ('(?!', '(?!'
+ + block.gfm.fences.source.replace('\\1', '\\2') + '|'
+ + block.list.source.replace('\\1', '\\3') + '|')
+ ();
+
+/**
+ * GFM + Tables Block Grammar
+ */
+
+block.tables = merge({}, block.gfm, {
+ nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
+ table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
+});
+
+/**
+ * Block Lexer
+ */
+
+function Lexer(options) {
+ this.tokens = [];
+ this.tokens.links = {};
+ this.options = options || marked.defaults;
+ this.rules = block.normal;
+
+ if (this.options.gfm) {
+ if (this.options.tables) {
+ this.rules = block.tables;
+ } else {
+ this.rules = block.gfm;
+ }
+ }
+}
+
+/**
+ * Expose Block Rules
+ */
+
+Lexer.rules = block;
+
+/**
+ * Static Lex Method
+ */
+
+Lexer.lex = function(src, options) {
+ var lexer = new Lexer(options);
+ return lexer.lex(src);
+};
+
+/**
+ * Preprocessing
+ */
+
+Lexer.prototype.lex = function(src) {
+ src = src
+ .replace(/\r\n|\r/g, '\n')
+ .replace(/\t/g, ' ')
+ .replace(/\u00a0/g, ' ')
+ .replace(/\u2424/g, '\n');
+
+ return this.token(src, true);
+};
+
+/**
+ * Lexing
+ */
+
+Lexer.prototype.token = function(src, top, bq) {
+ var src = src.replace(/^ +$/gm, '')
+ , next
+ , loose
+ , cap
+ , bull
+ , b
+ , item
+ , space
+ , i
+ , l;
+
+ while (src) {
+ // newline
+ if (cap = this.rules.newline.exec(src)) {
+ src = src.substring(cap[0].length);
+ if (cap[0].length > 1) {
+ this.tokens.push({
+ type: 'space'
+ });
+ }
+ }
+
+ // code
+ if (cap = this.rules.code.exec(src)) {
+ src = src.substring(cap[0].length);
+ cap = cap[0].replace(/^ {4}/gm, '');
+ this.tokens.push({
+ type: 'code',
+ text: !this.options.pedantic
+ ? cap.replace(/\n+$/, '')
+ : cap
+ });
+ continue;
+ }
+
+ // fences (gfm)
+ if (cap = this.rules.fences.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'code',
+ lang: cap[2],
+ text: cap[3] || ''
+ });
+ continue;
+ }
+
+ // heading
+ if (cap = this.rules.heading.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'heading',
+ depth: cap[1].length,
+ text: cap[2]
+ });
+ continue;
+ }
+
+ // table no leading pipe (gfm)
+ if (top && (cap = this.rules.nptable.exec(src))) {
+ src = src.substring(cap[0].length);
+
+ item = {
+ type: 'table',
+ header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
+ align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
+ cells: cap[3].replace(/\n$/, '').split('\n')
+ };
+
+ for (i = 0; i < item.align.length; i++) {
+ if (/^ *-+: *$/.test(item.align[i])) {
+ item.align[i] = 'right';
+ } else if (/^ *:-+: *$/.test(item.align[i])) {
+ item.align[i] = 'center';
+ } else if (/^ *:-+ *$/.test(item.align[i])) {
+ item.align[i] = 'left';
+ } else {
+ item.align[i] = null;
+ }
+ }
+
+ for (i = 0; i < item.cells.length; i++) {
+ item.cells[i] = item.cells[i].split(/ *\| */);
+ }
+
+ this.tokens.push(item);
+
+ continue;
+ }
+
+ // lheading
+ if (cap = this.rules.lheading.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'heading',
+ depth: cap[2] === '=' ? 1 : 2,
+ text: cap[1]
+ });
+ continue;
+ }
+
+ // hr
+ if (cap = this.rules.hr.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'hr'
+ });
+ continue;
+ }
+
+ // blockquote
+ if (cap = this.rules.blockquote.exec(src)) {
+ src = src.substring(cap[0].length);
+
+ this.tokens.push({
+ type: 'blockquote_start'
+ });
+
+ cap = cap[0].replace(/^ *> ?/gm, '');
+
+ // Pass `top` to keep the current
+ // "toplevel" state. This is exactly
+ // how markdown.pl works.
+ this.token(cap, top, true);
+
+ this.tokens.push({
+ type: 'blockquote_end'
+ });
+
+ continue;
+ }
+
+ // list
+ if (cap = this.rules.list.exec(src)) {
+ src = src.substring(cap[0].length);
+ bull = cap[2];
+
+ this.tokens.push({
+ type: 'list_start',
+ ordered: bull.length > 1
+ });
+
+ // Get each top-level item.
+ cap = cap[0].match(this.rules.item);
+
+ next = false;
+ l = cap.length;
+ i = 0;
+
+ for (; i < l; i++) {
+ item = cap[i];
+
+ // Remove the list item's bullet
+ // so it is seen as the next token.
+ space = item.length;
+ item = item.replace(/^ *([*+-]|\d+\.) +/, '');
+
+ // Outdent whatever the
+ // list item contains. Hacky.
+ if (~item.indexOf('\n ')) {
+ space -= item.length;
+ item = !this.options.pedantic
+ ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
+ : item.replace(/^ {1,4}/gm, '');
+ }
+
+ // Determine whether the next list item belongs here.
+ // Backpedal if it does not belong in this list.
+ if (this.options.smartLists && i !== l - 1) {
+ b = block.bullet.exec(cap[i + 1])[0];
+ if (bull !== b && !(bull.length > 1 && b.length > 1)) {
+ src = cap.slice(i + 1).join('\n') + src;
+ i = l - 1;
+ }
+ }
+
+ // Determine whether item is loose or not.
+ // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
+ // for discount behavior.
+ loose = next || /\n\n(?!\s*$)/.test(item);
+ if (i !== l - 1) {
+ next = item.charAt(item.length - 1) === '\n';
+ if (!loose) loose = next;
+ }
+
+ this.tokens.push({
+ type: loose
+ ? 'loose_item_start'
+ : 'list_item_start'
+ });
+
+ // Recurse.
+ this.token(item, false, bq);
+
+ this.tokens.push({
+ type: 'list_item_end'
+ });
+ }
+
+ this.tokens.push({
+ type: 'list_end'
+ });
+
+ continue;
+ }
+
+ // html
+ if (cap = this.rules.html.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: this.options.sanitize
+ ? 'paragraph'
+ : 'html',
+ pre: !this.options.sanitizer
+ && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
+ text: cap[0]
+ });
+ continue;
+ }
+
+ // def
+ if ((!bq && top) && (cap = this.rules.def.exec(src))) {
+ src = src.substring(cap[0].length);
+ this.tokens.links[cap[1].toLowerCase()] = {
+ href: cap[2],
+ title: cap[3]
+ };
+ continue;
+ }
+
+ // table (gfm)
+ if (top && (cap = this.rules.table.exec(src))) {
+ src = src.substring(cap[0].length);
+
+ item = {
+ type: 'table',
+ header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
+ align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
+ cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
+ };
+
+ for (i = 0; i < item.align.length; i++) {
+ if (/^ *-+: *$/.test(item.align[i])) {
+ item.align[i] = 'right';
+ } else if (/^ *:-+: *$/.test(item.align[i])) {
+ item.align[i] = 'center';
+ } else if (/^ *:-+ *$/.test(item.align[i])) {
+ item.align[i] = 'left';
+ } else {
+ item.align[i] = null;
+ }
+ }
+
+ for (i = 0; i < item.cells.length; i++) {
+ item.cells[i] = item.cells[i]
+ .replace(/^ *\| *| *\| *$/g, '')
+ .split(/ *\| */);
+ }
+
+ this.tokens.push(item);
+
+ continue;
+ }
+
+ // top-level paragraph
+ if (top && (cap = this.rules.paragraph.exec(src))) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'paragraph',
+ text: cap[1].charAt(cap[1].length - 1) === '\n'
+ ? cap[1].slice(0, -1)
+ : cap[1]
+ });
+ continue;
+ }
+
+ // text
+ if (cap = this.rules.text.exec(src)) {
+ // Top-level should never reach here.
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'text',
+ text: cap[0]
+ });
+ continue;
+ }
+
+ if (src) {
+ throw new
+ Error('Infinite loop on byte: ' + src.charCodeAt(0));
+ }
+ }
+
+ return this.tokens;
+};
+
+/**
+ * Inline-Level Grammar
+ */
+
+var inline = {
+ escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
+ autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
+ url: noop,
+ tag: /^|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
+ link: /^!?\[(inside)\]\(href\)/,
+ reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
+ nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
+ strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
+ em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
+ code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
+ br: /^ {2,}\n(?!\s*$)/,
+ del: noop,
+ text: /^[\s\S]+?(?=[\\?(?:\s+['"]([\s\S]*?)['"])?\s*/;
+
+inline.link = replace(inline.link)
+ ('inside', inline._inside)
+ ('href', inline._href)
+ ();
+
+inline.reflink = replace(inline.reflink)
+ ('inside', inline._inside)
+ ();
+
+/**
+ * Normal Inline Grammar
+ */
+
+inline.normal = merge({}, inline);
+
+/**
+ * Pedantic Inline Grammar
+ */
+
+inline.pedantic = merge({}, inline.normal, {
+ strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
+ em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
+});
+
+/**
+ * GFM Inline Grammar
+ */
+
+inline.gfm = merge({}, inline.normal, {
+ escape: replace(inline.escape)('])', '~|])')(),
+ url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
+ del: /^~~(?=\S)([\s\S]*?\S)~~/,
+ text: replace(inline.text)
+ (']|', '~]|')
+ ('|', '|https?://|')
+ ()
+});
+
+/**
+ * GFM + Line Breaks Inline Grammar
+ */
+
+inline.breaks = merge({}, inline.gfm, {
+ br: replace(inline.br)('{2,}', '*')(),
+ text: replace(inline.gfm.text)('{2,}', '*')()
+});
+
+/**
+ * Inline Lexer & Compiler
+ */
+
+function InlineLexer(links, options) {
+ this.options = options || marked.defaults;
+ this.links = links;
+ this.rules = inline.normal;
+ this.renderer = this.options.renderer || new Renderer;
+ this.renderer.options = this.options;
+
+ if (!this.links) {
+ throw new
+ Error('Tokens array requires a `links` property.');
+ }
+
+ if (this.options.gfm) {
+ if (this.options.breaks) {
+ this.rules = inline.breaks;
+ } else {
+ this.rules = inline.gfm;
+ }
+ } else if (this.options.pedantic) {
+ this.rules = inline.pedantic;
+ }
+}
+
+/**
+ * Expose Inline Rules
+ */
+
+InlineLexer.rules = inline;
+
+/**
+ * Static Lexing/Compiling Method
+ */
+
+InlineLexer.output = function(src, links, options) {
+ var inline = new InlineLexer(links, options);
+ return inline.output(src);
+};
+
+/**
+ * Lexing/Compiling
+ */
+
+InlineLexer.prototype.output = function(src) {
+ var out = ''
+ , link
+ , text
+ , href
+ , cap;
+
+ while (src) {
+ // escape
+ if (cap = this.rules.escape.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += cap[1];
+ continue;
+ }
+
+ // autolink
+ if (cap = this.rules.autolink.exec(src)) {
+ src = src.substring(cap[0].length);
+ if (cap[2] === '@') {
+ text = cap[1].charAt(6) === ':'
+ ? this.mangle(cap[1].substring(7))
+ : this.mangle(cap[1]);
+ href = this.mangle('mailto:') + text;
+ } else {
+ text = escape(cap[1]);
+ href = text;
+ }
+ out += this.renderer.link(href, null, text);
+ continue;
+ }
+
+ // url (gfm)
+ if (!this.inLink && (cap = this.rules.url.exec(src))) {
+ src = src.substring(cap[0].length);
+ text = escape(cap[1]);
+ href = text;
+ out += this.renderer.link(href, null, text);
+ continue;
+ }
+
+ // tag
+ if (cap = this.rules.tag.exec(src)) {
+ if (!this.inLink && /^/i.test(cap[0])) {
+ this.inLink = false;
+ }
+ src = src.substring(cap[0].length);
+ out += this.options.sanitize
+ ? this.options.sanitizer
+ ? this.options.sanitizer(cap[0])
+ : escape(cap[0])
+ : cap[0]
+ continue;
+ }
+
+ // link
+ if (cap = this.rules.link.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.inLink = true;
+ out += this.outputLink(cap, {
+ href: cap[2],
+ title: cap[3]
+ });
+ this.inLink = false;
+ continue;
+ }
+
+ // reflink, nolink
+ if ((cap = this.rules.reflink.exec(src))
+ || (cap = this.rules.nolink.exec(src))) {
+ src = src.substring(cap[0].length);
+ link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
+ link = this.links[link.toLowerCase()];
+ if (!link || !link.href) {
+ out += cap[0].charAt(0);
+ src = cap[0].substring(1) + src;
+ continue;
+ }
+ this.inLink = true;
+ out += this.outputLink(cap, link);
+ this.inLink = false;
+ continue;
+ }
+
+ // strong
+ if (cap = this.rules.strong.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.strong(this.output(cap[2] || cap[1]));
+ continue;
+ }
+
+ // em
+ if (cap = this.rules.em.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.em(this.output(cap[2] || cap[1]));
+ continue;
+ }
+
+ // code
+ if (cap = this.rules.code.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.codespan(escape(cap[2], true));
+ continue;
+ }
+
+ // br
+ if (cap = this.rules.br.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.br();
+ continue;
+ }
+
+ // del (gfm)
+ if (cap = this.rules.del.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.del(this.output(cap[1]));
+ continue;
+ }
+
+ // text
+ if (cap = this.rules.text.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.text(escape(this.smartypants(cap[0])));
+ continue;
+ }
+
+ if (src) {
+ throw new
+ Error('Infinite loop on byte: ' + src.charCodeAt(0));
+ }
+ }
+
+ return out;
+};
+
+/**
+ * Compile Link
+ */
+
+InlineLexer.prototype.outputLink = function(cap, link) {
+ var href = escape(link.href)
+ , title = link.title ? escape(link.title) : null;
+
+ return cap[0].charAt(0) !== '!'
+ ? this.renderer.link(href, title, this.output(cap[1]))
+ : this.renderer.image(href, title, escape(cap[1]));
+};
+
+/**
+ * Smartypants Transformations
+ */
+
+InlineLexer.prototype.smartypants = function(text) {
+ if (!this.options.smartypants) return text;
+ return text
+ // em-dashes
+ .replace(/---/g, '\u2014')
+ // en-dashes
+ .replace(/--/g, '\u2013')
+ // opening singles
+ .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
+ // closing singles & apostrophes
+ .replace(/'/g, '\u2019')
+ // opening doubles
+ .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
+ // closing doubles
+ .replace(/"/g, '\u201d')
+ // ellipses
+ .replace(/\.{3}/g, '\u2026');
+};
+
+/**
+ * Mangle Links
+ */
+
+InlineLexer.prototype.mangle = function(text) {
+ if (!this.options.mangle) return text;
+ var out = ''
+ , l = text.length
+ , i = 0
+ , ch;
+
+ for (; i < l; i++) {
+ ch = text.charCodeAt(i);
+ if (Math.random() > 0.5) {
+ ch = 'x' + ch.toString(16);
+ }
+ out += '' + ch + ';';
+ }
+
+ return out;
+};
+
+/**
+ * Renderer
+ */
+
+function Renderer(options) {
+ this.options = options || {};
+}
+
+Renderer.prototype.code = function(code, lang, escaped) {
+ if (this.options.highlight) {
+ var out = this.options.highlight(code, lang);
+ if (out != null && out !== code) {
+ escaped = true;
+ code = out;
+ }
+ }
+
+ if (!lang) {
+ return ''
+ + (escaped ? code : escape(code, true))
+ + '\n
';
+ }
+
+ return ''
+ + (escaped ? code : escape(code, true))
+ + '\n
\n';
+};
+
+Renderer.prototype.blockquote = function(quote) {
+ return '\n' + quote + '
\n';
+};
+
+Renderer.prototype.html = function(html) {
+ return html;
+};
+
+Renderer.prototype.heading = function(text, level, raw) {
+ return '\n';
+};
+
+Renderer.prototype.hr = function() {
+ return this.options.xhtml ? '
\n' : '
\n';
+};
+
+Renderer.prototype.list = function(body, ordered) {
+ var type = ordered ? 'ol' : 'ul';
+ return '<' + type + '>\n' + body + '' + type + '>\n';
+};
+
+Renderer.prototype.listitem = function(text) {
+ return '' + text + '\n';
+};
+
+Renderer.prototype.paragraph = function(text) {
+ return '' + text + '
\n';
+};
+
+Renderer.prototype.table = function(header, body) {
+ return '\n'
+ + '\n'
+ + header
+ + '\n'
+ + '\n'
+ + body
+ + '\n'
+ + '
\n';
+};
+
+Renderer.prototype.tablerow = function(content) {
+ return '\n' + content + '
\n';
+};
+
+Renderer.prototype.tablecell = function(content, flags) {
+ var type = flags.header ? 'th' : 'td';
+ var tag = flags.align
+ ? '<' + type + ' style="text-align:' + flags.align + '">'
+ : '<' + type + '>';
+ return tag + content + '' + type + '>\n';
+};
+
+// span level renderer
+Renderer.prototype.strong = function(text) {
+ return '' + text + '';
+};
+
+Renderer.prototype.em = function(text) {
+ return '' + text + '';
+};
+
+Renderer.prototype.codespan = function(text) {
+ return '' + text + '
';
+};
+
+Renderer.prototype.br = function() {
+ return this.options.xhtml ? '
' : '
';
+};
+
+Renderer.prototype.del = function(text) {
+ return '' + text + '';
+};
+
+Renderer.prototype.link = function(href, title, text) {
+ if (this.options.sanitize) {
+ try {
+ var prot = decodeURIComponent(unescape(href))
+ .replace(/[^\w:]/g, '')
+ .toLowerCase();
+ } catch (e) {
+ return '';
+ }
+ if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
+ return '';
+ }
+ }
+ var out = '' + text + '';
+ return out;
+};
+
+Renderer.prototype.image = function(href, title, text) {
+ var out = '' : '>';
+ return out;
+};
+
+Renderer.prototype.text = function(text) {
+ return text;
+};
+
+/**
+ * Parsing & Compiling
+ */
+
+function Parser(options) {
+ this.tokens = [];
+ this.token = null;
+ this.options = options || marked.defaults;
+ this.options.renderer = this.options.renderer || new Renderer;
+ this.renderer = this.options.renderer;
+ this.renderer.options = this.options;
+}
+
+/**
+ * Static Parse Method
+ */
+
+Parser.parse = function(src, options, renderer) {
+ var parser = new Parser(options, renderer);
+ return parser.parse(src);
+};
+
+/**
+ * Parse Loop
+ */
+
+Parser.prototype.parse = function(src) {
+ this.inline = new InlineLexer(src.links, this.options, this.renderer);
+ this.tokens = src.reverse();
+
+ var out = '';
+ while (this.next()) {
+ out += this.tok();
+ }
+
+ return out;
+};
+
+/**
+ * Next Token
+ */
+
+Parser.prototype.next = function() {
+ return this.token = this.tokens.pop();
+};
+
+/**
+ * Preview Next Token
+ */
+
+Parser.prototype.peek = function() {
+ return this.tokens[this.tokens.length - 1] || 0;
+};
+
+/**
+ * Parse Text Tokens
+ */
+
+Parser.prototype.parseText = function() {
+ var body = this.token.text;
+
+ while (this.peek().type === 'text') {
+ body += '\n' + this.next().text;
+ }
+
+ return this.inline.output(body);
+};
+
+/**
+ * Parse Current Token
+ */
+
+Parser.prototype.tok = function() {
+ switch (this.token.type) {
+ case 'space': {
+ return '';
+ }
+ case 'hr': {
+ return this.renderer.hr();
+ }
+ case 'heading': {
+ return this.renderer.heading(
+ this.inline.output(this.token.text),
+ this.token.depth,
+ this.token.text);
+ }
+ case 'code': {
+ return this.renderer.code(this.token.text,
+ this.token.lang,
+ this.token.escaped);
+ }
+ case 'table': {
+ var header = ''
+ , body = ''
+ , i
+ , row
+ , cell
+ , flags
+ , j;
+
+ // header
+ cell = '';
+ for (i = 0; i < this.token.header.length; i++) {
+ flags = { header: true, align: this.token.align[i] };
+ cell += this.renderer.tablecell(
+ this.inline.output(this.token.header[i]),
+ { header: true, align: this.token.align[i] }
+ );
+ }
+ header += this.renderer.tablerow(cell);
+
+ for (i = 0; i < this.token.cells.length; i++) {
+ row = this.token.cells[i];
+
+ cell = '';
+ for (j = 0; j < row.length; j++) {
+ cell += this.renderer.tablecell(
+ this.inline.output(row[j]),
+ { header: false, align: this.token.align[j] }
+ );
+ }
+
+ body += this.renderer.tablerow(cell);
+ }
+ return this.renderer.table(header, body);
+ }
+ case 'blockquote_start': {
+ var body = '';
+
+ while (this.next().type !== 'blockquote_end') {
+ body += this.tok();
+ }
+
+ return this.renderer.blockquote(body);
+ }
+ case 'list_start': {
+ var body = ''
+ , ordered = this.token.ordered;
+
+ while (this.next().type !== 'list_end') {
+ body += this.tok();
+ }
+
+ return this.renderer.list(body, ordered);
+ }
+ case 'list_item_start': {
+ var body = '';
+
+ while (this.next().type !== 'list_item_end') {
+ body += this.token.type === 'text'
+ ? this.parseText()
+ : this.tok();
+ }
+
+ return this.renderer.listitem(body);
+ }
+ case 'loose_item_start': {
+ var body = '';
+
+ while (this.next().type !== 'list_item_end') {
+ body += this.tok();
+ }
+
+ return this.renderer.listitem(body);
+ }
+ case 'html': {
+ var html = !this.token.pre && !this.options.pedantic
+ ? this.inline.output(this.token.text)
+ : this.token.text;
+ return this.renderer.html(html);
+ }
+ case 'paragraph': {
+ return this.renderer.paragraph(this.inline.output(this.token.text));
+ }
+ case 'text': {
+ return this.renderer.paragraph(this.parseText());
+ }
+ }
+};
+
+/**
+ * Helpers
+ */
+
+function escape(html, encode) {
+ return html
+ .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+}
+
+function unescape(html) {
+ return html.replace(/&([#\w]+);/g, function(_, n) {
+ n = n.toLowerCase();
+ if (n === 'colon') return ':';
+ if (n.charAt(0) === '#') {
+ return n.charAt(1) === 'x'
+ ? String.fromCharCode(parseInt(n.substring(2), 16))
+ : String.fromCharCode(+n.substring(1));
+ }
+ return '';
+ });
+}
+
+function replace(regex, opt) {
+ regex = regex.source;
+ opt = opt || '';
+ return function self(name, val) {
+ if (!name) return new RegExp(regex, opt);
+ val = val.source || val;
+ val = val.replace(/(^|[^\[])\^/g, '$1');
+ regex = regex.replace(name, val);
+ return self;
+ };
+}
+
+function noop() {}
+noop.exec = noop;
+
+function merge(obj) {
+ var i = 1
+ , target
+ , key;
+
+ for (; i < arguments.length; i++) {
+ target = arguments[i];
+ for (key in target) {
+ if (Object.prototype.hasOwnProperty.call(target, key)) {
+ obj[key] = target[key];
+ }
+ }
+ }
+
+ return obj;
+}
+
+
+/**
+ * Marked
+ */
+
+function marked(src, opt, callback) {
+ if (callback || typeof opt === 'function') {
+ if (!callback) {
+ callback = opt;
+ opt = null;
+ }
+
+ opt = merge({}, marked.defaults, opt || {});
+
+ var highlight = opt.highlight
+ , tokens
+ , pending
+ , i = 0;
+
+ try {
+ tokens = Lexer.lex(src, opt)
+ } catch (e) {
+ return callback(e);
+ }
+
+ pending = tokens.length;
+
+ var done = function(err) {
+ if (err) {
+ opt.highlight = highlight;
+ return callback(err);
+ }
+
+ var out;
+
+ try {
+ out = Parser.parse(tokens, opt);
+ } catch (e) {
+ err = e;
+ }
+
+ opt.highlight = highlight;
+
+ return err
+ ? callback(err)
+ : callback(null, out);
+ };
+
+ if (!highlight || highlight.length < 3) {
+ return done();
+ }
+
+ delete opt.highlight;
+
+ if (!pending) return done();
+
+ for (; i < tokens.length; i++) {
+ (function(token) {
+ if (token.type !== 'code') {
+ return --pending || done();
+ }
+ return highlight(token.text, token.lang, function(err, code) {
+ if (err) return done(err);
+ if (code == null || code === token.text) {
+ return --pending || done();
+ }
+ token.text = code;
+ token.escaped = true;
+ --pending || done();
+ });
+ })(tokens[i]);
+ }
+
+ return;
+ }
+ try {
+ if (opt) opt = merge({}, marked.defaults, opt);
+ return Parser.parse(Lexer.lex(src, opt), opt);
+ } catch (e) {
+ e.message += '\nPlease report this to https://github.com/chjj/marked.';
+ if ((opt || marked.defaults).silent) {
+ return 'An error occured:
'
+ + escape(e.message + '', true)
+ + '
';
+ }
+ throw e;
+ }
+}
+
+/**
+ * Options
+ */
+
+marked.options =
+marked.setOptions = function(opt) {
+ merge(marked.defaults, opt);
+ return marked;
+};
+
+marked.defaults = {
+ gfm: true,
+ tables: true,
+ breaks: false,
+ pedantic: false,
+ sanitize: false,
+ sanitizer: null,
+ mangle: true,
+ smartLists: false,
+ silent: false,
+ highlight: null,
+ langPrefix: 'lang-',
+ smartypants: false,
+ headerPrefix: '',
+ renderer: new Renderer,
+ xhtml: false
+};
+
+/**
+ * Expose
+ */
+
+marked.Parser = Parser;
+marked.parser = Parser.parse;
+
+marked.Renderer = Renderer;
+
+marked.Lexer = Lexer;
+marked.lexer = Lexer.lex;
+
+marked.InlineLexer = InlineLexer;
+marked.inlineLexer = InlineLexer.output;
+
+marked.parse = marked;
+
+if (typeof module !== 'undefined' && typeof exports === 'object') {
+ module.exports = marked;
+} else if (typeof define === 'function' && define.amd) {
+ define(function() { return marked; });
+} else {
+ this.marked = marked;
+}
+
+}).call(function() {
+ return this || (typeof window !== 'undefined' ? window : global);
+}());
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}],12:[function(require,module,exports){
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+var CodeMirror = require("codemirror");
+
+CodeMirror.commands.tabAndIndentMarkdownList = function (cm) {
+ var ranges = cm.listSelections();
+ var pos = ranges[0].head;
+ var eolState = cm.getStateAfter(pos.line);
+ var inList = eolState.list !== false;
+
+ if (inList) {
+ cm.execCommand("indentMore");
+ return;
+ }
+
+ if (cm.options.indentWithTabs) {
+ cm.execCommand("insertTab");
+ }
+ else {
+ var spaces = Array(cm.options.tabSize + 1).join(" ");
+ cm.replaceSelection(spaces);
+ }
+};
+
+CodeMirror.commands.shiftTabAndUnindentMarkdownList = function (cm) {
+ var ranges = cm.listSelections();
+ var pos = ranges[0].head;
+ var eolState = cm.getStateAfter(pos.line);
+ var inList = eolState.list !== false;
+
+ if (inList) {
+ cm.execCommand("indentLess");
+ return;
+ }
+
+ if (cm.options.indentWithTabs) {
+ cm.execCommand("insertTab");
+ }
+ else {
+ var spaces = Array(cm.options.tabSize + 1).join(" ");
+ cm.replaceSelection(spaces);
+ }
+};
+
+},{"codemirror":6}],13:[function(require,module,exports){
+/*global require,module*/
+"use strict";
+var CodeMirror = require("codemirror");
+require("codemirror/addon/edit/continuelist.js");
+require("./codemirror/tablist");
+require("codemirror/addon/display/fullscreen.js");
+require("codemirror/mode/markdown/markdown.js");
+require("codemirror/addon/mode/overlay.js");
+require("codemirror/mode/gfm/gfm.js");
+require("codemirror/mode/xml/xml.js");
+require("spell-checker");
+var marked = require("marked");
+
+
+var isMac = /Mac/.test(navigator.platform);
+
+var shortcuts = {
+ "Cmd-B": toggleBold,
+ "Cmd-I": toggleItalic,
+ "Cmd-K": drawLink,
+ "Cmd-H": toggleHeadingSmaller,
+ "Shift-Cmd-H": toggleHeadingBigger,
+ "Cmd-Alt-I": drawImage,
+ "Cmd-'": toggleBlockquote,
+ "Cmd-Alt-L": toggleOrderedList,
+ "Cmd-L": toggleUnorderedList,
+ "Cmd-Alt-C": toggleCodeBlock,
+ "Cmd-P": togglePreview
+};
+
+
+/**
+ * Fix shortcut. Mac use Command, others use Ctrl.
+ */
+function fixShortcut(name) {
+ if(isMac) {
+ name = name.replace("Ctrl", "Cmd");
+ } else {
+ name = name.replace("Cmd", "Ctrl");
+ }
+ return name;
+}
+
+
+/**
+ * Create icon element for toolbar.
+ */
+function createIcon(options, enableTooltips) {
+ options = options || {};
+ var el = document.createElement("a");
+ enableTooltips = (enableTooltips == undefined) ? true : enableTooltips;
+
+ if(options.title && enableTooltips) {
+ el.title = options.title;
+
+ if(isMac) {
+ el.title = el.title.replace("Ctrl", "⌘");
+ el.title = el.title.replace("Alt", "⌥");
+ }
+ }
+
+ el.className = options.className;
+ return el;
+}
+
+function createSep() {
+ var el = document.createElement("i");
+ el.className = "separator";
+ el.innerHTML = "|";
+ return el;
+}
+
+
+/**
+ * The state of CodeMirror at the given position.
+ */
+function getState(cm, pos) {
+ pos = pos || cm.getCursor("start");
+ var stat = cm.getTokenAt(pos);
+ if(!stat.type) return {};
+
+ var types = stat.type.split(" ");
+
+ var ret = {},
+ data, text;
+ for(var i = 0; i < types.length; i++) {
+ data = types[i];
+ if(data === "strong") {
+ ret.bold = true;
+ } else if(data === "variable-2") {
+ text = cm.getLine(pos.line);
+ if(/^\s*\d+\.\s/.test(text)) {
+ ret["ordered-list"] = true;
+ } else {
+ ret["unordered-list"] = true;
+ }
+ } else if(data === "atom") {
+ ret.quote = true;
+ } else if(data === "em") {
+ ret.italic = true;
+ } else if(data === "quote") {
+ ret.quote = true;
+ } else if(data === "strikethrough") {
+ ret.strikethrough = true;
+ } else if(data === "comment") {
+ ret.code = true;
+ }
+ }
+ return ret;
+}
+
+
+// Saved overflow setting
+var saved_overflow = "";
+
+/**
+ * Toggle full screen of the editor.
+ */
+function toggleFullScreen(editor) {
+ // Set fullscreen
+ var cm = editor.codemirror;
+ cm.setOption("fullScreen", !cm.getOption("fullScreen"));
+
+
+ // Prevent scrolling on body during fullscreen active
+ if(cm.getOption("fullScreen")) {
+ saved_overflow = document.body.style.overflow;
+ document.body.style.overflow = "hidden";
+ } else {
+ document.body.style.overflow = saved_overflow;
+ }
+
+
+ // Update toolbar class
+ var wrap = cm.getWrapperElement();
+
+ if(!/fullscreen/.test(wrap.previousSibling.className)) {
+ wrap.previousSibling.className += " fullscreen";
+ } else {
+ wrap.previousSibling.className = wrap.previousSibling.className.replace(/\s*fullscreen\b/, "");
+ }
+
+
+ // Update toolbar button
+ var toolbarButton = editor.toolbarElements.fullscreen;
+
+ if(!/active/.test(toolbarButton.className)) {
+ toolbarButton.className += " active";
+ } else {
+ toolbarButton.className = toolbarButton.className.replace(/\s*active\s*/g, "");
+ }
+
+
+ // Hide side by side if needed
+ var sidebyside = cm.getWrapperElement().nextSibling;
+ if(/editor-preview-active-side/.test(sidebyside.className))
+ toggleSideBySide(editor);
+}
+
+
+/**
+ * Action for toggling bold.
+ */
+function toggleBold(editor) {
+ _toggleBlock(editor, "bold", "**");
+}
+
+
+/**
+ * Action for toggling italic.
+ */
+function toggleItalic(editor) {
+ _toggleBlock(editor, "italic", "*");
+}
+
+
+/**
+ * Action for toggling strikethrough.
+ */
+function toggleStrikethrough(editor) {
+ _toggleBlock(editor, "strikethrough", "~~");
+}
+
+/**
+ * Action for toggling code block.
+ */
+function toggleCodeBlock(editor) {
+ _toggleBlock(editor, "code", "```\r\n", "\r\n```");
+}
+
+/**
+ * Action for toggling blockquote.
+ */
+function toggleBlockquote(editor) {
+ var cm = editor.codemirror;
+ _toggleLine(cm, "quote");
+}
+
+/**
+ * Action for toggling heading size: normal -> h1 -> h2 -> h3 -> h4 -> h5 -> h6 -> normal
+ */
+function toggleHeadingSmaller(editor) {
+ var cm = editor.codemirror;
+ _toggleHeading(cm, "smaller");
+}
+
+/**
+ * Action for toggling heading size: normal -> h6 -> h5 -> h4 -> h3 -> h2 -> h1 -> normal
+ */
+function toggleHeadingBigger(editor) {
+ var cm = editor.codemirror;
+ _toggleHeading(cm, "bigger");
+}
+
+/**
+ * Action for toggling heading size 1
+ */
+function toggleHeading1(editor) {
+ var cm = editor.codemirror;
+ _toggleHeading(cm, undefined, 1);
+}
+
+/**
+ * Action for toggling heading size 2
+ */
+function toggleHeading2(editor) {
+ var cm = editor.codemirror;
+ _toggleHeading(cm, undefined, 2);
+}
+
+/**
+ * Action for toggling heading size 3
+ */
+function toggleHeading3(editor) {
+ var cm = editor.codemirror;
+ _toggleHeading(cm, undefined, 3);
+}
+
+
+/**
+ * Action for toggling ul.
+ */
+function toggleUnorderedList(editor) {
+ var cm = editor.codemirror;
+ _toggleLine(cm, "unordered-list");
+}
+
+
+/**
+ * Action for toggling ol.
+ */
+function toggleOrderedList(editor) {
+ var cm = editor.codemirror;
+ _toggleLine(cm, "ordered-list");
+}
+
+
+/**
+ * Action for drawing a link.
+ */
+function drawLink(editor) {
+ var cm = editor.codemirror;
+ var stat = getState(cm);
+ _replaceSelection(cm, stat.link, "[", "](http://)");
+}
+
+
+/**
+ * Action for drawing an img.
+ */
+function drawImage(editor) {
+ var cm = editor.codemirror;
+ var stat = getState(cm);
+ _replaceSelection(cm, stat.image, "![](http://", ")");
+}
+
+
+/**
+ * Action for drawing a horizontal rule.
+ */
+function drawHorizontalRule(editor) {
+ var cm = editor.codemirror;
+ var stat = getState(cm);
+ _replaceSelection(cm, stat.image, "", "\n\n-----\n\n");
+}
+
+
+/**
+ * Undo action.
+ */
+function undo(editor) {
+ var cm = editor.codemirror;
+ cm.undo();
+ cm.focus();
+}
+
+
+/**
+ * Redo action.
+ */
+function redo(editor) {
+ var cm = editor.codemirror;
+ cm.redo();
+ cm.focus();
+}
+
+
+/**
+ * Toggle side by side preview
+ */
+function toggleSideBySide(editor) {
+ var cm = editor.codemirror;
+ var wrapper = cm.getWrapperElement();
+ var preview = wrapper.nextSibling;
+ var toolbarButton = editor.toolbarElements["side-by-side"];
+
+ if(/editor-preview-active-side/.test(preview.className)) {
+ preview.className = preview.className.replace(
+ /\s*editor-preview-active-side\s*/g, ""
+ );
+ toolbarButton.className = toolbarButton.className.replace(/\s*active\s*/g, "");
+ wrapper.className = wrapper.className.replace(/\s*CodeMirror-sided\s*/g, " ");
+ } else {
+ /* When the preview button is clicked for the first time,
+ * give some time for the transition from editor.css to fire and the view to slide from right to left,
+ * instead of just appearing.
+ */
+ setTimeout(function() {
+ if(!cm.getOption("fullScreen"))
+ toggleFullScreen(editor);
+ preview.className += " editor-preview-active-side";
+ }, 1);
+ toolbarButton.className += " active";
+ wrapper.className += " CodeMirror-sided";
+ }
+
+ // Hide normal preview if active
+ var previewNormal = wrapper.lastChild;
+ if(/editor-preview-active/.test(previewNormal.className)) {
+ previewNormal.className = previewNormal.className.replace(
+ /\s*editor-preview-active\s*/g, ""
+ );
+ var toolbar = editor.toolbarElements.preview;
+ var toolbar_div = wrapper.previousSibling;
+ toolbar.className = toolbar.className.replace(/\s*active\s*/g, "");
+ toolbar_div.className = toolbar_div.className.replace(/\s*disabled-for-preview*/g, "");
+ }
+
+ // Start preview with the current text
+ preview.innerHTML = editor.options.previewRender(editor.value(), preview);
+
+ // Updates preview
+ cm.on("update", function() {
+ preview.innerHTML = editor.options.previewRender(editor.value(), preview);
+ });
+}
+
+
+/**
+ * Preview action.
+ */
+function togglePreview(editor) {
+ var cm = editor.codemirror;
+ var wrapper = cm.getWrapperElement();
+ var toolbar_div = wrapper.previousSibling;
+ var toolbar = editor.toolbarElements.preview;
+ var preview = wrapper.lastChild;
+ if(!preview || !/editor-preview/.test(preview.className)) {
+ preview = document.createElement("div");
+ preview.className = "editor-preview";
+ wrapper.appendChild(preview);
+ }
+ if(/editor-preview-active/.test(preview.className)) {
+ preview.className = preview.className.replace(
+ /\s*editor-preview-active\s*/g, ""
+ );
+ toolbar.className = toolbar.className.replace(/\s*active\s*/g, "");
+ toolbar_div.className = toolbar_div.className.replace(/\s*disabled-for-preview*/g, "");
+ } else {
+ /* When the preview button is clicked for the first time,
+ * give some time for the transition from editor.css to fire and the view to slide from right to left,
+ * instead of just appearing.
+ */
+ setTimeout(function() {
+ preview.className += " editor-preview-active";
+ }, 1);
+ toolbar.className += " active";
+ toolbar_div.className += " disabled-for-preview";
+ }
+ preview.innerHTML = editor.options.previewRender(editor.value(), preview);
+
+ // Turn off side by side if needed
+ var sidebyside = cm.getWrapperElement().nextSibling;
+ if(/editor-preview-active-side/.test(sidebyside.className))
+ toggleSideBySide(editor);
+}
+
+function _replaceSelection(cm, active, start, end) {
+ if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className))
+ return;
+
+ var text;
+ var startPoint = cm.getCursor("start");
+ var endPoint = cm.getCursor("end");
+ if(active) {
+ text = cm.getLine(startPoint.line);
+ start = text.slice(0, startPoint.ch);
+ end = text.slice(startPoint.ch);
+ cm.replaceRange(start + end, {
+ line: startPoint.line,
+ ch: 0
+ });
+ } else {
+ text = cm.getSelection();
+ cm.replaceSelection(start + text + end);
+
+ startPoint.ch += start.length;
+ if(startPoint !== endPoint) {
+ endPoint.ch += start.length;
+ }
+ }
+ cm.setSelection(startPoint, endPoint);
+ cm.focus();
+}
+
+
+function _toggleHeading(cm, direction, size) {
+ if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className))
+ return;
+
+ var startPoint = cm.getCursor("start");
+ var endPoint = cm.getCursor("end");
+ for(var i = startPoint.line; i <= endPoint.line; i++) {
+ (function(i) {
+ var text = cm.getLine(i);
+ var currHeadingLevel = text.search(/[^#]/);
+
+ if(direction !== undefined) {
+ if(currHeadingLevel <= 0) {
+ if(direction == "bigger") {
+ text = "###### " + text;
+ } else {
+ text = "# " + text;
+ }
+ } else if(currHeadingLevel == 6 && direction == "smaller") {
+ text = text.substr(7);
+ } else if(currHeadingLevel == 1 && direction == "bigger") {
+ text = text.substr(2);
+ } else {
+ if(direction == "bigger") {
+ text = text.substr(1);
+ } else {
+ text = "#" + text;
+ }
+ }
+ } else {
+ if(size == 1) {
+ if(currHeadingLevel <= 0) {
+ text = "# " + text;
+ } else if(currHeadingLevel == size) {
+ text = text.substr(currHeadingLevel + 1);
+ } else {
+ text = "# " + text.substr(currHeadingLevel + 1);
+ }
+ } else if(size == 2) {
+ if(currHeadingLevel <= 0) {
+ text = "## " + text;
+ } else if(currHeadingLevel == size) {
+ text = text.substr(currHeadingLevel + 1);
+ } else {
+ text = "## " + text.substr(currHeadingLevel + 1);
+ }
+ } else {
+ if(currHeadingLevel <= 0) {
+ text = "### " + text;
+ } else if(currHeadingLevel == size) {
+ text = text.substr(currHeadingLevel + 1);
+ } else {
+ text = "### " + text.substr(currHeadingLevel + 1);
+ }
+ }
+ }
+
+ cm.replaceRange(text, {
+ line: i,
+ ch: 0
+ }, {
+ line: i,
+ ch: 99999999999999
+ });
+ })(i);
+ }
+ cm.focus();
+}
+
+
+function _toggleLine(cm, name) {
+ if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className))
+ return;
+
+ var stat = getState(cm);
+ var startPoint = cm.getCursor("start");
+ var endPoint = cm.getCursor("end");
+ var repl = {
+ "quote": /^(\s*)\>\s+/,
+ "unordered-list": /^(\s*)(\*|\-|\+)\s+/,
+ "ordered-list": /^(\s*)\d+\.\s+/
+ };
+ var map = {
+ "quote": "> ",
+ "unordered-list": "* ",
+ "ordered-list": "1. "
+ };
+ for(var i = startPoint.line; i <= endPoint.line; i++) {
+ (function(i) {
+ var text = cm.getLine(i);
+ if(stat[name]) {
+ text = text.replace(repl[name], "$1");
+ } else {
+ text = map[name] + text;
+ }
+ cm.replaceRange(text, {
+ line: i,
+ ch: 0
+ }, {
+ line: i,
+ ch: 99999999999999
+ });
+ })(i);
+ }
+ cm.focus();
+}
+
+function _toggleBlock(editor, type, start_chars, end_chars) {
+ if(/editor-preview-active/.test(editor.codemirror.getWrapperElement().lastChild.className))
+ return;
+
+ end_chars = (typeof end_chars === "undefined") ? start_chars : end_chars;
+ var cm = editor.codemirror;
+ var stat = getState(cm);
+
+ var text;
+ var start = start_chars;
+ var end = end_chars;
+
+ var startPoint = cm.getCursor("start");
+ var endPoint = cm.getCursor("end");
+
+ if(stat[type]) {
+ text = cm.getLine(startPoint.line);
+ start = text.slice(0, startPoint.ch);
+ end = text.slice(startPoint.ch);
+ if(type == "bold") {
+ start = start.replace(/(\*\*|__)(?![\s\S]*(\*\*|__))/, "");
+ end = end.replace(/(\*\*|__)/, "");
+ } else if(type == "italic") {
+ start = start.replace(/(\*|_)(?![\s\S]*(\*|_))/, "");
+ end = end.replace(/(\*|_)/, "");
+ } else if(type == "strikethrough") {
+ start = start.replace(/(\*\*|~~)(?![\s\S]*(\*\*|~~))/, "");
+ end = end.replace(/(\*\*|~~)/, "");
+ }
+ cm.replaceRange(start + end, {
+ line: startPoint.line,
+ ch: 0
+ }, {
+ line: startPoint.line,
+ ch: 99999999999999
+ });
+
+ if(type == "bold" || type == "strikethrough") {
+ startPoint.ch -= 2;
+ if(startPoint !== endPoint) {
+ endPoint.ch -= 2;
+ }
+ } else if(type == "italic") {
+ startPoint.ch -= 1;
+ if(startPoint !== endPoint) {
+ endPoint.ch -= 1;
+ }
+ }
+ } else {
+ text = cm.getSelection();
+ if(type == "bold") {
+ text = text.split("**").join("");
+ text = text.split("__").join("");
+ } else if(type == "italic") {
+ text = text.split("*").join("");
+ text = text.split("_").join("");
+ } else if(type == "strikethrough") {
+ text = text.split("~~").join("");
+ }
+ cm.replaceSelection(start + text + end);
+
+ startPoint.ch += start_chars.length;
+ endPoint.ch = startPoint.ch + text.length;
+ }
+
+ cm.setSelection(startPoint, endPoint);
+ cm.focus();
+}
+
+
+/* The right word count in respect for CJK. */
+function wordCount(data) {
+ var pattern = /[a-zA-Z0-9_\u0392-\u03c9]+|[\u4E00-\u9FFF\u3400-\u4dbf\uf900-\ufaff\u3040-\u309f\uac00-\ud7af]+/g;
+ var m = data.match(pattern);
+ var count = 0;
+ if(m === null) return count;
+ for(var i = 0; i < m.length; i++) {
+ if(m[i].charCodeAt(0) >= 0x4E00) {
+ count += m[i].length;
+ } else {
+ count += 1;
+ }
+ }
+ return count;
+}
+
+
+var toolbarBuiltInButtons = {
+ "bold": {
+ name: "bold",
+ action: toggleBold,
+ className: "fa fa-bold",
+ title: "Bold (Ctrl+B)"
+ },
+ "italic": {
+ name: "italic",
+ action: toggleItalic,
+ className: "fa fa-italic",
+ title: "Italic (Ctrl+I)"
+ },
+ "strikethrough": {
+ name: "strikethrough",
+ action: toggleStrikethrough,
+ className: "fa fa-strikethrough",
+ title: "Strikethrough"
+ },
+ "heading": {
+ name: "heading",
+ action: toggleHeadingSmaller,
+ className: "fa fa-header",
+ title: "Heading (Ctrl+H)"
+ },
+ "heading-smaller": {
+ name: "heading-smaller",
+ action: toggleHeadingSmaller,
+ className: "fa fa-header fa-header-x fa-header-smaller",
+ title: "Smaller Heading (Ctrl+H)"
+ },
+ "heading-bigger": {
+ name: "heading-bigger",
+ action: toggleHeadingBigger,
+ className: "fa fa-header fa-header-x fa-header-bigger",
+ title: "Bigger Heading (Shift+Ctrl+H)"
+ },
+ "heading-1": {
+ name: "heading-1",
+ action: toggleHeading1,
+ className: "fa fa-header fa-header-x fa-header-1",
+ title: "Big Heading"
+ },
+ "heading-2": {
+ name: "heading-2",
+ action: toggleHeading2,
+ className: "fa fa-header fa-header-x fa-header-2",
+ title: "Medium Heading"
+ },
+ "heading-3": {
+ name: "heading-3",
+ action: toggleHeading3,
+ className: "fa fa-header fa-header-x fa-header-3",
+ title: "Small Heading"
+ },
+ "code": {
+ name: "code",
+ action: toggleCodeBlock,
+ className: "fa fa-code",
+ title: "Code (Ctrl+Alt+C)"
+ },
+ "quote": {
+ name: "quote",
+ action: toggleBlockquote,
+ className: "fa fa-quote-left",
+ title: "Quote (Ctrl+')"
+ },
+ "unordered-list": {
+ name: "unordered-list",
+ action: toggleUnorderedList,
+ className: "fa fa-list-ul",
+ title: "Generic List (Ctrl+L)"
+ },
+ "ordered-list": {
+ name: "ordered-list",
+ action: toggleOrderedList,
+ className: "fa fa-list-ol",
+ title: "Numbered List (Ctrl+Alt+L)"
+ },
+ "link": {
+ name: "link",
+ action: drawLink,
+ className: "fa fa-link",
+ title: "Create Link (Ctrl+K)"
+ },
+ "image": {
+ name: "image",
+ action: drawImage,
+ className: "fa fa-picture-o",
+ title: "Insert Image (Ctrl+Alt+I)"
+ },
+ "horizontal-rule": {
+ name: "horizontal-rule",
+ action: drawHorizontalRule,
+ className: "fa fa-minus",
+ title: "Insert Horizontal Line"
+ },
+ "preview": {
+ name: "preview",
+ action: togglePreview,
+ className: "fa fa-eye no-disable",
+ title: "Toggle Preview (Ctrl+P)"
+ },
+ "side-by-side": {
+ name: "side-by-side",
+ action: toggleSideBySide,
+ className: "fa fa-columns no-disable no-mobile",
+ title: "Toggle Side by Side (F9)"
+ },
+ "fullscreen": {
+ name: "fullscreen",
+ action: toggleFullScreen,
+ className: "fa fa-arrows-alt no-disable no-mobile",
+ title: "Toggle Fullscreen (F11)"
+ },
+ "guide": {
+ name: "guide",
+ action: "http://nextstepwebs.github.io/simplemde-markdown-editor/markdown-guide",
+ className: "fa fa-question-circle",
+ title: "Markdown Guide"
+ }
+};
+
+
+/**
+ * Interface of SimpleMDE.
+ */
+function SimpleMDE(options) {
+ // Handle options parameter
+ options = options || {};
+
+
+ // Used later to refer to it"s parent
+ options.parent = this;
+
+
+ // Check if Font Awesome needs to be auto downloaded
+ var autoDownloadFA = true;
+
+ if(options.autoDownloadFontAwesome === false) {
+ autoDownloadFA = false;
+ }
+
+ if(options.autoDownloadFontAwesome !== true) {
+ var styleSheets = document.styleSheets;
+ for(var i = 0; i < styleSheets.length; i++) {
+ if(!styleSheets[i].href)
+ continue;
+
+ if(styleSheets[i].href.indexOf("//maxcdn.bootstrapcdn.com/font-awesome/") > -1) {
+ autoDownloadFA = false;
+ }
+ }
+ }
+
+ if(autoDownloadFA) {
+ var link = document.createElement("link");
+ link.rel = "stylesheet";
+ link.href = "https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css";
+ document.getElementsByTagName("head")[0].appendChild(link);
+ }
+
+
+ // Find the textarea to use
+ if(options.element) {
+ this.element = options.element;
+ } else if(options.element === null) {
+ // This means that the element option was specified, but no element was found
+ console.log("SimpleMDE: Error. No element was found.");
+ return;
+ }
+
+
+ // Handle toolbar and status bar
+ if(options.toolbar !== false)
+ options.toolbar = options.toolbar || SimpleMDE.toolbar;
+
+ if(!options.hasOwnProperty("status")) {
+ options.status = ["autosave", "lines", "words", "cursor"];
+ }
+
+
+ // Add default preview rendering function
+ if(!options.previewRender) {
+ options.previewRender = function(plainText) {
+ // Note: "this" refers to the options object
+ return this.parent.markdown(plainText);
+ };
+ }
+
+
+ // Set default options for parsing config
+ options.parsingConfig = options.parsingConfig || {};
+
+
+ // Update this options
+ this.options = options;
+
+
+ // Auto render
+ this.render();
+
+
+ // The codemirror component is only available after rendering
+ // so, the setter for the initialValue can only run after
+ // the element has been rendered
+ if(options.initialValue) {
+ this.value(options.initialValue);
+ }
+}
+
+/**
+ * Default toolbar elements.
+ */
+SimpleMDE.toolbar = ["bold", "italic", "heading", "|", "quote", "unordered-list", "ordered-list", "|", "link", "image", "|", "preview", "side-by-side", "fullscreen", "guide"];
+
+/**
+ * Default markdown render.
+ */
+SimpleMDE.prototype.markdown = function(text) {
+ if(marked) {
+ // Initialize
+ var markedOptions = {};
+
+
+ // Update options
+ if(this.options && this.options.renderingConfig && this.options.renderingConfig.singleLineBreaks !== false) {
+ markedOptions.breaks = true;
+ }
+
+ if(this.options && this.options.renderingConfig && this.options.renderingConfig.codeSyntaxHighlighting === true && window.hljs) {
+ markedOptions.highlight = function(code) {
+ return window.hljs.highlightAuto(code).value;
+ };
+ }
+
+
+ // Set options
+ marked.setOptions(markedOptions);
+
+
+ // Return
+ return marked(text);
+ }
+};
+
+/**
+ * Render editor to the given element.
+ */
+SimpleMDE.prototype.render = function(el) {
+ if(!el) {
+ el = this.element || document.getElementsByTagName("textarea")[0];
+ }
+
+ if(this._rendered && this._rendered === el) {
+ // Already rendered.
+ return;
+ }
+
+ this.element = el;
+ var options = this.options;
+
+ var self = this;
+ var keyMaps = {};
+
+ for(var key in shortcuts) {
+ (function(key) {
+ keyMaps[fixShortcut(key)] = function() {
+ shortcuts[key](self);
+ };
+ })(key);
+ }
+
+ keyMaps["Enter"] = "newlineAndIndentContinueMarkdownList";
+ keyMaps["Tab"] = "tabAndIndentMarkdownList";
+ keyMaps["Shift-Tab"] = "shiftTabAndUnindentMarkdownList";
+ keyMaps["F11"] = function() {
+ toggleFullScreen(self);
+ };
+ keyMaps["F9"] = function() {
+ toggleSideBySide(self);
+ };
+ keyMaps["Esc"] = function(cm) {
+ if(cm.getOption("fullScreen")) toggleFullScreen(self);
+ };
+
+ var mode, backdrop;
+ if(options.spellChecker !== false) {
+ mode = "spell-checker";
+ backdrop = options.parsingConfig;
+ backdrop.name = "gfm";
+ backdrop.gitHubSpice = false;
+ } else {
+ mode = options.parsingConfig;
+ mode.name = "gfm";
+ mode.gitHubSpice = false;
+ }
+
+ this.codemirror = CodeMirror.fromTextArea(el, {
+ mode: mode,
+ backdrop: backdrop,
+ theme: "paper",
+ tabSize: (options.tabSize != undefined) ? options.tabSize : 2,
+ indentUnit: (options.tabSize != undefined) ? options.tabSize : 2,
+ indentWithTabs: (options.indentWithTabs === false) ? false : true,
+ lineNumbers: false,
+ autofocus: (options.autofocus === true) ? true : false,
+ extraKeys: keyMaps,
+ lineWrapping: (options.lineWrapping === false) ? false : true,
+ allowDropFileTypes: ["text/plain"]
+ });
+
+ if(options.toolbar !== false) {
+ this.createToolbar();
+ }
+ if(options.status !== false) {
+ this.createStatusbar();
+ }
+ if(options.autosave != undefined && options.autosave.enabled === true) {
+ this.autosave();
+ }
+
+ this.createSideBySide();
+
+ this._rendered = this.element;
+};
+
+SimpleMDE.prototype.autosave = function() {
+ var content = this.value();
+ var simplemde = this;
+
+ if(this.options.autosave.unique_id == undefined || this.options.autosave.unique_id == "") {
+ console.log("SimpleMDE: You must set a unique_id to use the autosave feature");
+ return;
+ }
+
+ if(simplemde.element.form != null && simplemde.element.form != undefined) {
+ simplemde.element.form.addEventListener("submit", function() {
+ localStorage.setItem(simplemde.options.autosave.unique_id, "");
+ });
+ }
+
+ if(this.options.autosave.loaded !== true) {
+ if(localStorage.getItem(this.options.autosave.unique_id) != null)
+ this.codemirror.setValue(localStorage.getItem(this.options.autosave.unique_id));
+
+ this.options.autosave.loaded = true;
+ }
+
+ if(localStorage) {
+ localStorage.setItem(this.options.autosave.unique_id, content);
+ }
+
+ var el = document.getElementById("autosaved");
+ if(el != null && el != undefined && el != "") {
+ var d = new Date();
+ var hh = d.getHours();
+ var m = d.getMinutes();
+ var dd = "am";
+ var h = hh;
+ if(h >= 12) {
+ h = hh - 12;
+ dd = "pm";
+ }
+ if(h == 0) {
+ h = 12;
+ }
+ m = m < 10 ? "0" + m : m;
+
+ el.innerHTML = "Autosaved: " + h + ":" + m + " " + dd;
+ }
+
+ setTimeout(function() {
+ simplemde.autosave();
+ }, this.options.autosave.delay || 10000);
+};
+
+SimpleMDE.prototype.createSideBySide = function() {
+ var cm = this.codemirror;
+ var wrapper = cm.getWrapperElement();
+ var preview = wrapper.nextSibling;
+
+ if(!preview || !/editor-preview-side/.test(preview.className)) {
+ preview = document.createElement("div");
+ preview.className = "editor-preview-side";
+ wrapper.parentNode.insertBefore(preview, wrapper.nextSibling);
+ }
+
+ // Syncs scroll editor -> preview
+ var cScroll = false;
+ var pScroll = false;
+ cm.on("scroll", function(v) {
+ if(cScroll) {
+ cScroll = false;
+ return;
+ }
+ pScroll = true;
+ var height = v.getScrollInfo().height - v.getScrollInfo().clientHeight;
+ var ratio = parseFloat(v.getScrollInfo().top) / height;
+ var move = (preview.scrollHeight - preview.clientHeight) * ratio;
+ preview.scrollTop = move;
+ });
+
+ // Syncs scroll preview -> editor
+ preview.onscroll = function() {
+ if(pScroll) {
+ pScroll = false;
+ return;
+ }
+ cScroll = true;
+ var height = preview.scrollHeight - preview.clientHeight;
+ var ratio = parseFloat(preview.scrollTop) / height;
+ var move = (cm.getScrollInfo().height - cm.getScrollInfo().clientHeight) * ratio;
+ cm.scrollTo(0, move);
+ };
+ return true;
+};
+
+SimpleMDE.prototype.createToolbar = function(items) {
+ items = items || this.options.toolbar;
+
+ if(!items || items.length === 0) {
+ return;
+ }
+ var i;
+ for(i = 0; i < items.length; i++) {
+ if(toolbarBuiltInButtons[items[i]] != undefined) {
+ items[i] = toolbarBuiltInButtons[items[i]];
+ }
+ }
+
+ var bar = document.createElement("div");
+ bar.className = "editor-toolbar";
+
+ var self = this;
+
+ var toolbar_data = {};
+ self.toolbar = items;
+
+ for(i = 0; i < items.length; i++) {
+ if(items[i].name == "guide" && self.options.toolbarGuideIcon === false)
+ continue;
+
+ if(self.options.hideIcons && self.options.hideIcons.indexOf(items[i].name) != -1)
+ continue;
+
+ (function(item) {
+ var el;
+ if(item === "|") {
+ el = createSep();
+ } else {
+ el = createIcon(item, self.options.toolbarTips);
+ }
+
+ // bind events, special for info
+ if(item.action) {
+ if(typeof item.action === "function") {
+ el.onclick = function() {
+ item.action(self);
+ };
+ } else if(typeof item.action === "string") {
+ el.href = item.action;
+ el.target = "_blank";
+ }
+ }
+ toolbar_data[item.name || item] = el;
+ bar.appendChild(el);
+ })(items[i]);
+ }
+
+ self.toolbarElements = toolbar_data;
+
+ var cm = this.codemirror;
+ cm.on("cursorActivity", function() {
+ var stat = getState(cm);
+
+ for(var key in toolbar_data) {
+ (function(key) {
+ var el = toolbar_data[key];
+ if(stat[key]) {
+ el.className += " active";
+ } else if(key != "fullscreen" && key != "side-by-side") {
+ el.className = el.className.replace(/\s*active\s*/g, "");
+ }
+ })(key);
+ }
+ });
+
+ var cmWrapper = cm.getWrapperElement();
+ cmWrapper.parentNode.insertBefore(bar, cmWrapper);
+ return bar;
+};
+
+SimpleMDE.prototype.createStatusbar = function(status) {
+ status = status || this.options.status;
+ var options = this.options;
+
+ if(!status || status.length === 0) return;
+
+ var bar = document.createElement("div");
+ bar.className = "editor-statusbar";
+
+ var pos, cm = this.codemirror;
+ for(var i = 0; i < status.length; i++) {
+ (function(name) {
+ var el = document.createElement("span");
+ el.className = name;
+ if(name === "words") {
+ el.innerHTML = "0";
+ cm.on("update", function() {
+ el.innerHTML = wordCount(cm.getValue());
+ });
+ } else if(name === "lines") {
+ el.innerHTML = "0";
+ cm.on("update", function() {
+ el.innerHTML = cm.lineCount();
+ });
+ } else if(name === "cursor") {
+ el.innerHTML = "0:0";
+ cm.on("cursorActivity", function() {
+ pos = cm.getCursor();
+ el.innerHTML = pos.line + ":" + pos.ch;
+ });
+ } else if(name === "autosave") {
+ if(options.autosave != undefined && options.autosave.enabled === true) {
+ el.setAttribute("id", "autosaved");
+ }
+ }
+ bar.appendChild(el);
+ })(status[i]);
+ }
+
+ var cmWrapper = this.codemirror.getWrapperElement();
+ cmWrapper.parentNode.insertBefore(bar, cmWrapper.nextSibling);
+ return bar;
+};
+
+/**
+ * Get or set the text content.
+ */
+SimpleMDE.prototype.value = function(val) {
+ if(val === undefined) {
+ return this.codemirror.getValue();
+ } else {
+ this.codemirror.getDoc().setValue(val);
+ return this;
+ }
+};
+
+
+/**
+ * Bind static methods for exports.
+ */
+SimpleMDE.toggleBold = toggleBold;
+SimpleMDE.toggleItalic = toggleItalic;
+SimpleMDE.toggleStrikethrough = toggleStrikethrough;
+SimpleMDE.toggleBlockquote = toggleBlockquote;
+SimpleMDE.toggleHeadingSmaller = toggleHeadingSmaller;
+SimpleMDE.toggleHeadingBigger = toggleHeadingBigger;
+SimpleMDE.toggleHeading1 = toggleHeading1;
+SimpleMDE.toggleHeading2 = toggleHeading2;
+SimpleMDE.toggleHeading3 = toggleHeading3;
+SimpleMDE.toggleCodeBlock = toggleCodeBlock;
+SimpleMDE.toggleUnorderedList = toggleUnorderedList;
+SimpleMDE.toggleOrderedList = toggleOrderedList;
+SimpleMDE.drawLink = drawLink;
+SimpleMDE.drawImage = drawImage;
+SimpleMDE.drawHorizontalRule = drawHorizontalRule;
+SimpleMDE.undo = undo;
+SimpleMDE.redo = redo;
+SimpleMDE.togglePreview = togglePreview;
+SimpleMDE.toggleSideBySide = toggleSideBySide;
+SimpleMDE.toggleFullScreen = toggleFullScreen;
+
+/**
+ * Bind instance methods for exports.
+ */
+SimpleMDE.prototype.toggleBold = function() {
+ toggleBold(this);
+};
+SimpleMDE.prototype.toggleItalic = function() {
+ toggleItalic(this);
+};
+SimpleMDE.prototype.toggleStrikethrough = function() {
+ toggleStrikethrough(this);
+};
+SimpleMDE.prototype.toggleBlockquote = function() {
+ toggleBlockquote(this);
+};
+SimpleMDE.prototype.toggleHeadingSmaller = function() {
+ toggleHeadingSmaller(this);
+};
+SimpleMDE.prototype.toggleHeadingBigger = function() {
+ toggleHeadingBigger(this);
+};
+SimpleMDE.prototype.toggleHeading1 = function() {
+ toggleHeading1(this);
+};
+SimpleMDE.prototype.toggleHeading2 = function() {
+ toggleHeading2(this);
+};
+SimpleMDE.prototype.toggleHeading3 = function() {
+ toggleHeading3(this);
+};
+SimpleMDE.prototype.toggleCodeBlock = function() {
+ toggleCodeBlock(this);
+};
+SimpleMDE.prototype.toggleUnorderedList = function() {
+ toggleUnorderedList(this);
+};
+SimpleMDE.prototype.toggleOrderedList = function() {
+ toggleOrderedList(this);
+};
+SimpleMDE.prototype.drawLink = function() {
+ drawLink(this);
+};
+SimpleMDE.prototype.drawImage = function() {
+ drawImage(this);
+};
+SimpleMDE.prototype.drawHorizontalRule = function() {
+ drawHorizontalRule(this);
+};
+SimpleMDE.prototype.undo = function() {
+ undo(this);
+};
+SimpleMDE.prototype.redo = function() {
+ redo(this);
+};
+SimpleMDE.prototype.togglePreview = function() {
+ togglePreview(this);
+};
+SimpleMDE.prototype.toggleSideBySide = function() {
+ toggleSideBySide(this);
+};
+SimpleMDE.prototype.toggleFullScreen = function() {
+ toggleFullScreen(this);
+};
+module.exports = SimpleMDE;
+
+},{"./codemirror/tablist":12,"codemirror":6,"codemirror/addon/display/fullscreen.js":3,"codemirror/addon/edit/continuelist.js":4,"codemirror/addon/mode/overlay.js":5,"codemirror/mode/gfm/gfm.js":7,"codemirror/mode/markdown/markdown.js":8,"codemirror/mode/xml/xml.js":10,"marked":11,"spell-checker":1}]},{},[13])(13)
+});
\ No newline at end of file
diff --git a/dist/simplemde.min.css b/dist/simplemde.min.css
new file mode 100644
index 0000000..109e303
--- /dev/null
+++ b/dist/simplemde.min.css
@@ -0,0 +1,7 @@
+/**
+ * simplemde v1.7.6
+ * Copyright Next Step Webs, Inc.
+ * @link https://github.com/NextStepWebs/simplemde-markdown-editor
+ * @license MIT
+ */
+.CodeMirror{color:#000}.CodeMirror-lines{padding:4px 0}.CodeMirror pre{padding:0 4px}.CodeMirror-gutter-filler,.CodeMirror-scrollbar-filler{background-color:#fff}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;white-space:nowrap}.CodeMirror-guttermarker{color:#000}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid #000;border-right:none;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0;background:#7e7}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.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{50%{background-color:transparent}}@-webkit-keyframes blink{50%{background-color:transparent}}@keyframes blink{50%{background-color:transparent}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-ruler{border-left:1px solid #ccc;position:absolute}.cm-s-default .cm-header{color:#00f}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:700}.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-2{color:#05a}.cm-s-default .cm-variable-3{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,.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-invalidchar,.cm-s-default .cm-error{color:red}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0f0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#f22}.CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:#fff}.CodeMirror-scroll{overflow:scroll!important;margin-bottom:-30px;margin-right:-30px;padding-bottom:30px;height:100%;outline:0;position:relative}.CodeMirror-sizer{position:relative;border-right:30px solid transparent}.CodeMirror-gutter-filler,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-vscrollbar{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;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;margin-bottom:-30px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:0 0!important;border:none!important;-webkit-user-select:none;-moz-user-select:none;user-select:none}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror,.CodeMirror-scroll{min-height:300px}.CodeMirror pre{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:0 0;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}.CodeMirror-wrap pre{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;overflow:auto}.CodeMirror-code{outline:0}.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber,.CodeMirror-scroll,.CodeMirror-sizer{-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}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}.CodeMirror-focused div.CodeMirror-cursors,div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected,.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background:#ffa;background:rgba(255,255,0,.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:0 0}.CodeMirror{height:auto;border:1px solid #ddd;border-bottom-left-radius:4px;border-bottom-right-radius:4px;padding:10px;font:inherit}.CodeMirror-fullscreen{background:#fff;position:fixed!important;top:50px;left:0;right:0;bottom:0;height:auto;z-index:9}.CodeMirror-sided{width:50%!important}.editor-toolbar{position:relative;opacity:.6;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none;padding:0 10px;border-top:1px solid #bbb;border-left:1px solid #bbb;border-right:1px solid #bbb;border-top-left-radius:4px;border-top-right-radius:4px}.editor-toolbar:after,.editor-toolbar:before{display:block;content:' ';height:1px}.editor-toolbar:before{margin-bottom:8px}.editor-toolbar:after{margin-top:8px}.editor-toolbar:hover,.editor-wrapper input.title:focus,.editor-wrapper input.title:hover{opacity:.8}.editor-toolbar.fullscreen{width:100%;height:50px;overflow-x:auto;overflow-y:hidden;white-space:nowrap;padding-top:10px;padding-bottom:10px;box-sizing:border-box;background:#fff;border:0;position:fixed;top:0;left:0;opacity:1;z-index:9}.editor-toolbar.fullscreen::before{width:20px;height:50px;background:-moz-linear-gradient(left,rgba(255,255,255,1) 0,rgba(125,185,232,.01) 100%);background:-webkit-gradient(linear,left top,right top,color-stop(0,rgba(255,255,255,1)),color-stop(100%,rgba(125,185,232,.01)));background:-webkit-linear-gradient(left,rgba(255,255,255,1) 0,rgba(125,185,232,.01) 100%);background:-o-linear-gradient(left,rgba(255,255,255,1) 0,rgba(125,185,232,.01) 100%);background:-ms-linear-gradient(left,rgba(255,255,255,1) 0,rgba(125,185,232,.01) 100%);background:linear-gradient(to right,rgba(255,255,255,1) 0,rgba(125,185,232,.01) 100%);filter:progid: DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#037db9e8', GradientType=1);position:fixed;top:0;left:0;margin:0;padding:0}.editor-toolbar.fullscreen::after{width:20px;height:50px;background:-moz-linear-gradient(left,rgba(125,185,232,.01) 0,rgba(254,254,255,1) 99%,rgba(255,255,255,1) 100%);background:-webkit-gradient(linear,left top,right top,color-stop(0,rgba(125,185,232,.01)),color-stop(99%,rgba(254,254,255,1)),color-stop(100%,rgba(255,255,255,1)));background:-webkit-linear-gradient(left,rgba(125,185,232,.01) 0,rgba(254,254,255,1) 99%,rgba(255,255,255,1) 100%);background:-o-linear-gradient(left,rgba(125,185,232,.01) 0,rgba(254,254,255,1) 99%,rgba(255,255,255,1) 100%);background:-ms-linear-gradient(left,rgba(125,185,232,.01) 0,rgba(254,254,255,1) 99%,rgba(255,255,255,1) 100%);background:linear-gradient(to right,rgba(125,185,232,.01) 0,rgba(254,254,255,1) 99%,rgba(255,255,255,1) 100%);filter:progid: DXImageTransform.Microsoft.gradient( startColorstr='#037db9e8', endColorstr='#ffffff', GradientType=1);position:fixed;top:0;right:0;margin:0;padding:0}.editor-toolbar a{display:inline-block;text-align:center;text-decoration:none!important;color:#2c3e50!important;width:30px;height:30px;margin:0;border:1px solid transparent;border-radius:3px;cursor:pointer}.editor-toolbar a.active,.editor-toolbar a:hover{background:#fcfcfc;border-color:#95a5a6}.editor-toolbar a:before{line-height:30px}.editor-toolbar i.separator{display:inline-block;width:0;border-left:1px solid #d9d9d9;border-right:1px solid #fff;color:transparent;text-indent:-10px;margin:0 6px}.editor-toolbar a.fa-header-x:after{font-family:Arial,"Helvetica Neue",Helvetica,sans-serif;font-size:65%;vertical-align:text-bottom;position:relative;top:2px}.editor-toolbar a.fa-header-1:after{content:"1"}.editor-toolbar a.fa-header-2:after{content:"2"}.editor-toolbar a.fa-header-3:after{content:"3"}.editor-toolbar a.fa-header-bigger:after{content:"▲"}.editor-toolbar a.fa-header-smaller:after{content:"▼"}.editor-toolbar.disabled-for-preview a:not(.no-disable){pointer-events:none;background:#fff;border-color:transparent;text-shadow:inherit}@media only screen and (max-width:700px){.editor-toolbar a.no-mobile{display:none}}.editor-statusbar{padding:8px 10px;font-size:12px;color:#959694;text-align:right}.editor-statusbar span{display:inline-block;min-width:4em;margin-left:1em}.editor-preview,.editor-preview-side{padding:10px;background:#fafafa;z-index:9999;overflow:auto;display:none;box-sizing:border-box}.editor-statusbar .lines:before{content:'lines: '}.editor-statusbar .words:before{content:'words: '}.editor-preview{position:absolute;width:100%;height:100%;top:0;left:0}.editor-preview-side{position:fixed;bottom:0;width:50%;top:50px;right:0;border:1px solid #ddd}.editor-preview-active,.editor-preview-active-side{display:block}.editor-preview-side>p,.editor-preview>p{margin-top:0}.editor-preview pre,.editor-preview-side pre{background:#eee;margin-bottom:10px}.editor-preview table td,.editor-preview table th,.editor-preview-side table td,.editor-preview-side table th{border:1px solid #ddd;padding:5px}.CodeMirror .CodeMirror-selected{background:#d9d9d9}.CodeMirror .CodeMirror-code .cm-header-1{font-size:200%;line-height:200%}.CodeMirror .CodeMirror-code .cm-header-2{font-size:160%;line-height:160%}.CodeMirror .CodeMirror-code .cm-header-3{font-size:125%;line-height:125%}.CodeMirror .CodeMirror-code .cm-header-4{font-size:110%;line-height:110%}.CodeMirror .CodeMirror-code .cm-comment{background:rgba(0,0,0,.05);border-radius:2px}.CodeMirror .CodeMirror-code .cm-link{color:#7f8c8d}.CodeMirror .CodeMirror-code .cm-url{color:#aab2b3}.CodeMirror .CodeMirror-code .cm-strikethrough{text-decoration:line-through}.CodeMirror .cm-spell-error:not(.cm-url):not(.cm-comment){background:rgba(255,0,0,.15)}
\ No newline at end of file
diff --git a/dist/simplemde.min.js b/dist/simplemde.min.js
new file mode 100644
index 0000000..72e8161
--- /dev/null
+++ b/dist/simplemde.min.js
@@ -0,0 +1,14 @@
+/**
+ * simplemde v1.7.6
+ * Copyright Next Step Webs, Inc.
+ * @link https://github.com/NextStepWebs/simplemde-markdown-editor
+ * @license MIT
+ */
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.SimpleMDE=e()}}(function(){var e;return function t(e,n,r){function i(l,a){if(!n[l]){if(!e[l]){var s="function"==typeof require&&require;if(!a&&s)return s(l,!0);if(o)return o(l,!0);var c=new Error("Cannot find module '"+l+"'");throw c.code="MODULE_NOT_FOUND",c}var u=n[l]={exports:{}};e[l][0].call(u.exports,function(t){var n=e[l][1][t];return i(n?n:t)},u,u.exports,t,e,n,r)}return n[l].exports}for(var o="function"==typeof require&&require,l=0;lo;o++)for(var a=this.compoundRules[o],s=0,c=a.length;c>s;s++)this.compoundRuleCodes[a[s]]=[];"ONLYINCOMPOUND"in this.flags&&(this.compoundRuleCodes[this.flags.ONLYINCOMPOUND]=[]),this.dictionaryTable=this._parseDIC(n);for(var o in this.compoundRuleCodes)0==this.compoundRuleCodes[o].length&&delete this.compoundRuleCodes[o];for(var o=0,l=this.compoundRules.length;l>o;o++){for(var u=this.compoundRules[o],d="",s=0,c=u.length;c>s;s++){var h=u[s];d+=h in this.compoundRuleCodes?"("+this.compoundRuleCodes[h].join("|")+")":h}this.compoundRules[o]=new RegExp(d,"i")}}return this};o.prototype={load:function(e){for(var t in e)this[t]=e[t];return this},_readFile:function(e,t){t||(t="ISO8859-1");var n=new XMLHttpRequest;return n.open("GET",e,!1),n.overrideMimeType&&n.overrideMimeType("text/plain; charset="+t),n.send(null),n.responseText},_parseAFF:function(e){var t={};e=this._removeAffixComments(e);for(var n=e.split("\n"),r=0,i=n.length;i>r;r++){var o=n[r],l=o.split(/\s+/),a=l[0];if("PFX"==a||"SFX"==a){for(var s=l[1],c=l[2],u=parseInt(l[3],10),d=[],h=r+1,f=r+1+u;f>h;h++){var o=n[h],p=o.split(/\s+/),m=p[2],g=p[3].split("/"),v=g[0];"0"===v&&(v="");var y=this.parseRuleCodes(g[1]),x=p[4],b={};b.add=v,y.length>0&&(b.continuationClasses=y),"."!==x&&("SFX"===a?b.match=new RegExp(x+"$"):b.match=new RegExp("^"+x)),"0"!=m&&("SFX"===a?b.remove=new RegExp(m+"$"):b.remove=m),d.push(b)}t[s]={type:a,combineable:"Y"==c,entries:d},r+=u}else if("COMPOUNDRULE"===a){for(var u=parseInt(l[1],10),h=r+1,f=r+1+u;f>h;h++){var o=n[h],p=o.split(/\s+/);this.compoundRules.push(p[1])}r+=u}else if("REP"===a){var p=o.split(/\s+/);3===p.length&&this.replacementTable.push([p[1],p[2]])}else this.flags[a]=l[1]}return t},_removeAffixComments:function(e){return e=e.replace(/#.*$/gm,""),e=e.replace(/^\s\s*/m,"").replace(/\s\s*$/m,""),e=e.replace(/\n{2,}/g,"\n"),e=e.replace(/^\s\s*/,"").replace(/\s\s*$/,"")},_parseDIC:function(e){function t(e,t){e in r&&"object"==typeof r[e]||(r[e]=[]),r[e].push(t)}e=this._removeDicComments(e);for(var n=e.split("\n"),r={},i=1,o=n.length;o>i;i++){var l=n[i],a=l.split("/",2),s=a[0];if(a.length>1){var c=this.parseRuleCodes(a[1]);"NEEDAFFIX"in this.flags&&-1!=c.indexOf(this.flags.NEEDAFFIX)||t(s,c);for(var u=0,d=c.length;d>u;u++){var h=c[u],f=this.rules[h];if(f)for(var p=this._applyRule(s,f),m=0,g=p.length;g>m;m++){var v=p[m];if(t(v,[]),f.combineable)for(var y=u+1;d>y;y++){var x=c[y],b=this.rules[x];if(b&&b.combineable&&f.type!=b.type)for(var w=this._applyRule(v,b),C=0,k=w.length;k>C;C++){var S=w[C];t(S,[])}}}h in this.compoundRuleCodes&&this.compoundRuleCodes[h].push(s)}}else t(s.trim(),[])}return r},_removeDicComments:function(e){return e=e.replace(/^\t.*$/gm,"")},parseRuleCodes:function(e){if(!e)return[];if(!("FLAG"in this.flags))return e.split("");if("long"===this.flags.FLAG){for(var t=[],n=0,r=e.length;r>n;n+=2)t.push(e.substr(n,2));return t}return"num"===this.flags.FLAG?textCode.split(","):void 0},_applyRule:function(e,t){for(var n=t.entries,r=[],i=0,o=n.length;o>i;i++){var l=n[i];if(!l.match||e.match(l.match)){var a=e;if(l.remove&&(a=a.replace(l.remove,"")),"SFX"===t.type?a+=l.add:a=l.add+a,r.push(a),"continuationClasses"in l)for(var s=0,c=l.continuationClasses.length;c>s;s++){var u=this.rules[l.continuationClasses[s]];u&&(r=r.concat(this._applyRule(a,u)))}}}return r},check:function(e){var t=e.replace(/^\s\s*/,"").replace(/\s\s*$/,"");if(this.checkExact(t))return!0;if(t.toUpperCase()===t){var n=t[0]+t.substring(1).toLowerCase();if(this.hasFlag(n,"KEEPCASE"))return!1;if(this.checkExact(n))return!0}var r=t.toLowerCase();if(r!==t){if(this.hasFlag(r,"KEEPCASE"))return!1;if(this.checkExact(r))return!0}return!1},checkExact:function(e){var t=this.dictionaryTable[e];if("undefined"==typeof t){if("COMPOUNDMIN"in this.flags&&e.length>=this.flags.COMPOUNDMIN)for(var n=0,r=this.compoundRules.length;r>n;n++)if(e.match(this.compoundRules[n]))return!0;return!1}for(var n=0,r=t.length;r>n;n++)if(!this.hasFlag(e,"ONLYINCOMPOUND",t[n]))return!0;return!1},hasFlag:function(e,t,n){if(t in this.flags){if("undefined"==typeof n)var n=Array.prototype.concat.apply([],this.dictionaryTable[e]);if(n&&-1!==n.indexOf(this.flags[t]))return!0}return!1},alphabet:"",suggest:function(e,t){function n(e){for(var t=[],n=0,r=e.length;r>n;n++){for(var i=e[n],o=[],l=0,a=i.length+1;a>l;l++)o.push([i.substring(0,l),i.substring(l,i.length)]);for(var s=[],l=0,a=o.length;a>l;l++){var u=o[l];u[1]&&s.push(u[0]+u[1].substring(1))}for(var d=[],l=0,a=o.length;a>l;l++){var u=o[l];u[1].length>1&&d.push(u[0]+u[1][1]+u[1][0]+u[1].substring(2))}for(var h=[],l=0,a=o.length;a>l;l++){var u=o[l];if(u[1])for(var f=0,p=c.alphabet.length;p>f;f++)h.push(u[0]+c.alphabet[f]+u[1].substring(1))}for(var m=[],l=0,a=o.length;a>l;l++){var u=o[l];if(u[1])for(var f=0,p=c.alphabet.length;p>f;f++)h.push(u[0]+c.alphabet[f]+u[1])}t=t.concat(s),t=t.concat(d),t=t.concat(h),t=t.concat(m)}return t}function r(e){for(var t=[],n=0;nu;u++)a[u]in s?s[a[u]]+=1:s[a[u]]=1;var h=[];for(var u in s)h.push([u,s[u]]);h.sort(i).reverse();for(var f=[],u=0,d=Math.min(t,h.length);d>u;u++)c.hasFlag(h[u][0],"NOSUGGEST")||f.push(h[u][0]);return f}if(t||(t=5),this.check(e))return[];for(var o=0,l=this.replacementTable.length;l>o;o++){var a=this.replacementTable[o];if(-1!==e.indexOf(a[0])){var s=e.replace(a[0],a[1]);if(this.check(s))return[s]}}var c=this;return c.alphabet="abcdefghijklmnopqrstuvwxyz",i(e)}},i("undefined"!=typeof o?o:window.Typo)}).call(e,void 0,void 0,void 0,void 0,function(e){t.exports=e})}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],3:[function(t,n,r){!function(i){"object"==typeof r&&"object"==typeof n?i(t("../../lib/codemirror")):"function"==typeof e&&e.amd?e(["../../lib/codemirror"],i):i(CodeMirror)}(function(e){"use strict";function t(e){var t=e.getWrapperElement();e.state.fullScreenRestore={scrollTop:window.pageYOffset,scrollLeft:window.pageXOffset,width:t.style.width,height:t.style.height},t.style.width="",t.style.height="auto",t.className+=" CodeMirror-fullscreen",document.documentElement.style.overflow="hidden",e.refresh()}function n(e){var t=e.getWrapperElement();t.className=t.className.replace(/\s*CodeMirror-fullscreen\b/,""),document.documentElement.style.overflow="";var n=e.state.fullScreenRestore;t.style.width=n.width,t.style.height=n.height,window.scrollTo(n.scrollLeft,n.scrollTop),e.refresh()}e.defineOption("fullScreen",!1,function(r,i,o){o==e.Init&&(o=!1),!o!=!i&&(i?t(r):n(r))})})},{"../../lib/codemirror":6}],4:[function(t,n,r){!function(i){"object"==typeof r&&"object"==typeof n?i(t("../../lib/codemirror")):"function"==typeof e&&e.amd?e(["../../lib/codemirror"],i):i(CodeMirror)}(function(e){"use strict";var t=/^(\s*)(>[> ]*|[*+-]\s|(\d+)([.)]))(\s*)/,n=/^(\s*)(>[> ]*|[*+-]|(\d+)[.)])(\s*)$/,r=/[*+-]\s/;e.commands.newlineAndIndentContinueMarkdownList=function(i){if(i.getOption("disableInput"))return e.Pass;for(var o=i.listSelections(),l=[],a=0;a")>=0?f[2]:parseInt(f[3],10)+1+f[4];l[a]="\n"+p+g+m}}i.replaceSelections(l)}})},{"../../lib/codemirror":6}],5:[function(t,n,r){!function(i){"object"==typeof r&&"object"==typeof n?i(t("../../lib/codemirror")):"function"==typeof e&&e.amd?e(["../../lib/codemirror"],i):i(CodeMirror)}(function(e){"use strict";e.overlayMode=function(t,n,r){return{startState:function(){return{base:e.startState(t),overlay:e.startState(n),basePos:0,baseCur:null,overlayPos:0,overlayCur:null,streamSeen:null}},copyState:function(r){return{base:e.copyState(t,r.base),overlay:e.copyState(n,r.overlay),basePos:r.basePos,baseCur:null,overlayPos:r.overlayPos,overlayCur:null}},token:function(e,i){return(e!=i.streamSeen||Math.min(i.basePos,i.overlayPos)xo&&setTimeout(function(){s.display.input.reset(!0)},20),_t(this),Ki(),xt(this),this.curOp.forceUpdate=!0,Xr(this,i),r.autofocus&&!No||s.hasFocus()?setTimeout(Fi(gn,this),20):vn(this);for(var u in Jo)Jo.hasOwnProperty(u)&&Jo[u](this,r[u],el);C(this),r.finishInit&&r.finishInit(this);for(var d=0;dxo&&(r.gutters.style.zIndex=-1,r.scroller.style.paddingRight=0),bo||mo&&No||(r.scroller.draggable=!0),e&&(e.appendChild?e.appendChild(r.wrapper):e(r.wrapper)),r.viewFrom=r.viewTo=t.first,r.reportedViewFrom=r.reportedViewTo=t.first,r.view=[],r.renderedView=null,r.externalMeasured=null,r.viewOffset=0,r.lastWrapHeight=r.lastWrapWidth=0,r.updateLineNumbers=null,r.nativeBarWidth=r.barHeight=r.barWidth=0,r.scrollbarsClipped=!1,r.lineNumWidth=r.lineNumInnerWidth=r.lineNumChars=null,r.alignWidgets=!1,r.cachedCharWidth=r.cachedTextHeight=r.cachedPaddingH=null,r.maxLine=null,r.maxLineLength=0,r.maxLineChanged=!1,r.wheelDX=r.wheelDY=r.wheelStartX=r.wheelStartY=null,r.shift=!1,r.selForContextMenu=null,r.activeTouch=null,n.init(r)}function n(t){t.doc.mode=e.getMode(t.options,t.doc.modeOption),r(t)}function r(e){e.doc.iter(function(e){e.stateAfter&&(e.stateAfter=null),e.styles&&(e.styles=null)}),e.doc.frontier=e.doc.first,ze(e,100),e.state.modeGen++,e.curOp&&Et(e)}function i(e){e.options.lineWrapping?(Yl(e.display.wrapper,"CodeMirror-wrap"),e.display.sizer.style.minWidth="",e.display.sizerWidth=null):(Xl(e.display.wrapper,"CodeMirror-wrap"),h(e)),l(e),Et(e),lt(e),setTimeout(function(){y(e)},100)}function o(e){var t=vt(e.display),n=e.options.lineWrapping,r=n&&Math.max(5,e.display.scroller.clientWidth/yt(e.display)-3);return function(i){if(wr(e.doc,i))return 0;var o=0;if(i.widgets)for(var l=0;lt.maxLineLength&&(t.maxLineLength=n,t.maxLine=e)})}function f(e){var t=Di(e.gutters,"CodeMirror-linenumbers");-1==t&&e.lineNumbers?e.gutters=e.gutters.concat(["CodeMirror-linenumbers"]):t>-1&&!e.lineNumbers&&(e.gutters=e.gutters.slice(0),e.gutters.splice(t,1))}function p(e){var t=e.display,n=t.gutters.offsetWidth,r=Math.round(e.doc.height+qe(e.display));return{clientHeight:t.scroller.clientHeight,viewHeight:t.wrapper.clientHeight,scrollWidth:t.scroller.scrollWidth,clientWidth:t.scroller.clientWidth,viewWidth:t.wrapper.clientWidth,barLeft:e.options.fixedGutter?n:0,docHeight:r,scrollHeight:r+Ue(e)+t.barHeight,nativeBarWidth:t.nativeBarWidth,gutterWidth:n}}function m(e,t,n){this.cm=n;var r=this.vert=_i("div",[_i("div",null,null,"min-width: 1px")],"CodeMirror-vscrollbar"),i=this.horiz=_i("div",[_i("div",null,null,"height: 100%; min-height: 1px")],"CodeMirror-hscrollbar");e(r),e(i),Nl(r,"scroll",function(){r.clientHeight&&t(r.scrollTop,"vertical")}),Nl(i,"scroll",function(){i.clientWidth&&t(i.scrollLeft,"horizontal")}),this.checkedOverlay=!1,yo&&8>xo&&(this.horiz.style.minHeight=this.vert.style.minWidth="18px")}function g(){}function v(t){t.display.scrollbars&&(t.display.scrollbars.clear(),t.display.scrollbars.addClass&&Xl(t.display.wrapper,t.display.scrollbars.addClass)),t.display.scrollbars=new e.scrollbarModel[t.options.scrollbarStyle](function(e){t.display.wrapper.insertBefore(e,t.display.scrollbarFiller),Nl(e,"mousedown",function(){t.state.focused&&setTimeout(function(){t.display.input.focus()},0)}),e.setAttribute("cm-not-content","true")},function(e,n){"horizontal"==n?rn(t,e):nn(t,e)},t),t.display.scrollbars.addClass&&Yl(t.display.wrapper,t.display.scrollbars.addClass)}function y(e,t){t||(t=p(e));var n=e.display.barWidth,r=e.display.barHeight;x(e,t);for(var i=0;4>i&&n!=e.display.barWidth||r!=e.display.barHeight;i++)n!=e.display.barWidth&&e.options.lineWrapping&&H(e),x(e,p(e)),n=e.display.barWidth,r=e.display.barHeight}function x(e,t){var n=e.display,r=n.scrollbars.update(t);n.sizer.style.paddingRight=(n.barWidth=r.right)+"px",n.sizer.style.paddingBottom=(n.barHeight=r.bottom)+"px",r.right&&r.bottom?(n.scrollbarFiller.style.display="block",n.scrollbarFiller.style.height=r.bottom+"px",n.scrollbarFiller.style.width=r.right+"px"):n.scrollbarFiller.style.display="",r.bottom&&e.options.coverGutterNextToScrollbar&&e.options.fixedGutter?(n.gutterFiller.style.display="block",n.gutterFiller.style.height=r.bottom+"px",n.gutterFiller.style.width=t.gutterWidth+"px"):n.gutterFiller.style.display=""}function b(e,t,n){var r=n&&null!=n.top?Math.max(0,n.top):e.scroller.scrollTop;r=Math.floor(r-je(e));var i=n&&null!=n.bottom?n.bottom:r+e.wrapper.clientHeight,o=ti(t,r),l=ti(t,i);if(n&&n.ensure){var a=n.ensure.from.line,s=n.ensure.to.line;o>a?(o=a,l=ti(t,ni(Yr(t,a))+e.wrapper.clientHeight)):Math.min(s,t.lastLine())>=l&&(o=ti(t,ni(Yr(t,s))-e.wrapper.clientHeight),l=s)}return{from:o,to:Math.max(l,o+1)}}function w(e){var t=e.display,n=t.view;if(t.alignWidgets||t.gutters.firstChild&&e.options.fixedGutter){for(var r=S(t)-t.scroller.scrollLeft+e.doc.scrollLeft,i=t.gutters.offsetWidth,o=r+"px",l=0;l=n.viewFrom&&t.visible.to<=n.viewTo&&(null==n.updateLineNumbers||n.updateLineNumbers>=n.viewTo)&&n.renderedView==n.view&&0==Bt(e))return!1;C(e)&&(Pt(e),t.dims=W(e));var i=r.first+r.size,o=Math.max(t.visible.from-e.options.viewportMargin,r.first),l=Math.min(i,t.visible.to+e.options.viewportMargin);n.viewFroml&&n.viewTo-l<20&&(l=Math.min(i,n.viewTo)),Io&&(o=xr(e.doc,o),l=br(e.doc,l));var a=o!=n.viewFrom||l!=n.viewTo||n.lastWrapHeight!=t.wrapperHeight||n.lastWrapWidth!=t.wrapperWidth;Rt(e,o,l),n.viewOffset=ni(Yr(e.doc,n.viewFrom)),e.display.mover.style.top=n.viewOffset+"px";var s=Bt(e);if(!a&&0==s&&!t.force&&n.renderedView==n.view&&(null==n.updateLineNumbers||n.updateLineNumbers>=n.viewTo))return!1;var c=Gi();return s>4&&(n.lineDiv.style.display="none"),E(e,n.updateLineNumbers,t.dims),s>4&&(n.lineDiv.style.display=""),n.renderedView=n.view,c&&Gi()!=c&&c.offsetHeight&&c.focus(),ji(n.cursorDiv),ji(n.selectionDiv),n.gutters.style.height=n.sizer.style.minHeight=0,a&&(n.lastWrapHeight=t.wrapperHeight,n.lastWrapWidth=t.wrapperWidth,ze(e,400)),n.updateLineNumbers=null,!0}function N(e,t){for(var n=t.viewport,r=!0;(r&&e.options.lineWrapping&&t.oldDisplayWidth!=$e(e)||(n&&null!=n.top&&(n={top:Math.min(e.doc.height+qe(e.display)-Ve(e),n.top)}),t.visible=b(e.display,e.doc,n),!(t.visible.from>=e.display.viewFrom&&t.visible.to<=e.display.viewTo)))&&M(e,t);r=!1){H(e);var i=p(e);We(e),O(e,i),y(e,i)}t.signal(e,"update",e),(e.display.viewFrom!=e.display.reportedViewFrom||e.display.viewTo!=e.display.reportedViewTo)&&(t.signal(e,"viewportChange",e,e.display.viewFrom,e.display.viewTo),e.display.reportedViewFrom=e.display.viewFrom,e.display.reportedViewTo=e.display.viewTo)}function A(e,t){var n=new L(e,t);if(M(e,n)){H(e),N(e,n);var r=p(e);We(e),O(e,r),y(e,r),n.finish()}}function O(e,t){e.display.sizer.style.minHeight=t.docHeight+"px";var n=t.docHeight+e.display.barHeight;e.display.heightForcer.style.top=n+"px",e.display.gutters.style.height=Math.max(n+Ue(e),t.clientHeight)+"px"}function H(e){for(var t=e.display,n=t.lineDiv.offsetTop,r=0;rxo){var l=o.node.offsetTop+o.node.offsetHeight;i=l-n,n=l}else{var a=o.node.getBoundingClientRect();i=a.bottom-a.top}var s=o.line.height-i;if(2>i&&(i=vt(t)),(s>.001||-.001>s)&&(Jr(o.line,i),D(o.line),o.rest))for(var c=0;c=t&&d.lineNumber;d.changes&&(Di(d.changes,"gutter")>-1&&(h=!1),I(e,d,c,n)),h&&(ji(d.lineNumber),d.lineNumber.appendChild(document.createTextNode(k(e.options,c)))),a=d.node.nextSibling}else{var f=q(e,d,c,n);l.insertBefore(f,a)}c+=d.size}for(;a;)a=r(a)}function I(e,t,n,r){for(var i=0;ixo&&(e.node.style.zIndex=2)),e.node}function F(e){var t=e.bgClass?e.bgClass+" "+(e.line.bgClass||""):e.line.bgClass;if(t&&(t+=" CodeMirror-linebackground"),e.background)t?e.background.className=t:(e.background.parentNode.removeChild(e.background),e.background=null);else if(t){var n=P(e);e.background=n.insertBefore(_i("div",null,t),n.firstChild)}}function z(e,t){var n=e.display.externalMeasured;return n&&n.line==t.line?(e.display.externalMeasured=null,t.measure=n.measure,n.built):Fr(e,t)}function R(e,t){var n=t.text.className,r=z(e,t);t.text==t.node&&(t.node=r.pre),t.text.parentNode.replaceChild(r.pre,t.text),t.text=r.pre,r.bgClass!=t.bgClass||r.textClass!=t.textClass?(t.bgClass=r.bgClass,t.textClass=r.textClass,B(t)):n&&(t.text.className=n)}function B(e){F(e),e.line.wrapClass?P(e).className=e.line.wrapClass:e.node!=e.text&&(e.node.className="");var t=e.textClass?e.textClass+" "+(e.line.textClass||""):e.line.textClass;e.text.className=t||""}function _(e,t,n,r){if(t.gutter&&(t.node.removeChild(t.gutter),t.gutter=null),t.gutterBackground&&(t.node.removeChild(t.gutterBackground),t.gutterBackground=null),t.line.gutterClass){var i=P(t);t.gutterBackground=_i("div",null,"CodeMirror-gutter-background "+t.line.gutterClass,"left: "+(e.options.fixedGutter?r.fixedPos:-r.gutterTotalWidth)+"px; width: "+r.gutterTotalWidth+"px"),i.insertBefore(t.gutterBackground,t.text)}var o=t.line.gutterMarkers;if(e.options.lineNumbers||o){var i=P(t),l=t.gutter=_i("div",null,"CodeMirror-gutter-wrapper","left: "+(e.options.fixedGutter?r.fixedPos:-r.gutterTotalWidth)+"px");if(e.display.input.setUneditable(l),i.insertBefore(l,t.text),t.line.gutterClass&&(l.className+=" "+t.line.gutterClass),!e.options.lineNumbers||o&&o["CodeMirror-linenumbers"]||(t.lineNumber=l.appendChild(_i("div",k(e.options,n),"CodeMirror-linenumber CodeMirror-gutter-elt","left: "+r.gutterLeft["CodeMirror-linenumbers"]+"px; width: "+e.display.lineNumInnerWidth+"px"))),o)for(var a=0;a1)if(zo&&zo.join("\n")==t){if(r.ranges.length%zo.length==0){s=[];for(var c=0;c=0;c--){var u=r.ranges[c],d=u.from(),h=u.to();u.empty()&&(n&&n>0?d=Po(d.line,d.ch-n):e.state.overwrite&&!l&&(h=Po(h.line,Math.min(Yr(o,h.line).text.length,h.ch+Hi(a).length))));var f=e.curOp.updateInput,p={from:d,to:h,text:s?s[c%s.length]:a,origin:i||(l?"paste":e.state.cutIncoming?"cut":"+input")};Ln(e.doc,p),ki(e,"inputRead",e,p)}t&&!l&&ee(e,t),Fn(e),e.curOp.updateInput=f,e.curOp.typing=!0,e.state.pasteIncoming=e.state.cutIncoming=!1}function J(e,t){var n=e.clipboardData&&e.clipboardData.getData("text/plain");return n?(e.preventDefault(),Q(t)||t.options.disableInput||Nt(t,function(){Z(t,n,0,null,"paste")}),!0):void 0}function ee(e,t){if(e.options.electricChars&&e.options.smartIndent)for(var n=e.doc.sel,r=n.ranges.length-1;r>=0;r--){var i=n.ranges[r];if(!(i.head.ch>100||r&&n.ranges[r-1].head.line==i.head.line)){var o=e.getModeAt(i.head),l=!1;if(o.electricChars){for(var a=0;a-1){l=Rn(e,i.head.line,"smart");break}}else o.electricInput&&o.electricInput.test(Yr(e.doc,i.head.line).text.slice(0,i.head.ch))&&(l=Rn(e,i.head.line,"smart"));l&&ki(e,"electricInput",e,i.head.line)}}}function te(e){for(var t=[],n=[],r=0;ri?c.map:u[i],l=0;li?e.line:e.rest[i]),d=o[l]+r;return(0>r||a!=t)&&(d=o[l+(r?1:0)]),Po(s,d)}}}var i=e.text.firstChild,o=!1;if(!t||!$l(i,t))return ae(Po(ei(e.line),0),!0);if(t==i&&(o=!0,t=i.childNodes[n],n=0,!t)){var l=e.rest?Hi(e.rest):e.line;return ae(Po(ei(l),l.text.length),o)}var a=3==t.nodeType?t:null,s=t;for(a||1!=t.childNodes.length||3!=t.firstChild.nodeType||(a=t.firstChild,n&&(n=a.nodeValue.length));s.parentNode!=i;)s=s.parentNode;var c=e.measure,u=c.maps,d=r(a,s,n);if(d)return ae(d,o);for(var h=s.nextSibling,f=a?a.nodeValue.length-n:0;h;h=h.nextSibling){if(d=r(h,h.firstChild,0))return ae(Po(d.line,d.ch-f),o);f+=h.textContent.length}for(var p=s.previousSibling,f=n;p;p=p.previousSibling){if(d=r(p,p.firstChild,-1))return ae(Po(d.line,d.ch+f),o);f+=h.textContent.length}}function ue(e,t,n,r,i){function o(e){return function(t){return t.id==e}}function l(t){if(1==t.nodeType){var n=t.getAttribute("cm-text");if(null!=n)return""==n&&(n=t.textContent.replace(/\u200b/g,"")),void(a+=n);var u,d=t.getAttribute("cm-marker");if(d){var h=e.findMarks(Po(r,0),Po(i+1,0),o(+d));return void(h.length&&(u=h[0].find())&&(a+=Qr(e.doc,u.from,u.to).join(c)))}if("false"==t.getAttribute("contenteditable"))return;for(var f=0;f=0){var l=X(o.from(),i.from()),a=K(o.to(),i.to()),s=o.empty()?i.from()==i.head:o.from()==o.head;t>=r&&--t,e.splice(--r,2,new he(s?a:l,s?l:a))}}return new de(e,t)}function pe(e,t){return new de([new he(e,t||e)],0)}function me(e,t){return Math.max(e.first,Math.min(t,e.first+e.size-1))}function ge(e,t){if(t.linen?Po(n,Yr(e,n).text.length):ve(t,Yr(e,t.line).text.length)}function ve(e,t){var n=e.ch;return null==n||n>t?Po(e.line,t):0>n?Po(e.line,0):e}function ye(e,t){return t>=e.first&&t=o.ch:c.to>o.ch))){if(r&&(Hl(u,"beforeCursorEnter"),u.explicitlyCleared)){if(a.markedSpans){--s;continue}break}if(!u.atomic)continue;var d=u.find(0>l?-1:1);if(0==Fo(d,o)&&(d.ch+=l,d.ch<0?d=d.line>e.first?ge(e,Po(d.line-1)):null:d.ch>a.text.length&&(d=d.linet&&(t=0),t=Math.round(t),r=Math.round(r),a.appendChild(_i("div",null,"CodeMirror-selected","position: absolute; left: "+e+"px; top: "+t+"px; width: "+(null==n?u-e:n)+"px; height: "+(r-t)+"px"))}function i(t,n,i){function o(n,r){return dt(e,Po(t,n),"div",d,r)}var a,s,d=Yr(l,t),h=d.text.length;return Ji(ri(d),n||0,null==i?h:i,function(e,t,l){var d,f,p,m=o(e,"left");if(e==t)d=m,f=p=m.left;else{if(d=o(t-1,"right"),"rtl"==l){var g=m;m=d,d=g}f=m.left,p=d.right}null==n&&0==e&&(f=c),d.top-m.top>3&&(r(f,m.top,null,m.bottom),f=c,m.bottoms.bottom||d.bottom==s.bottom&&d.right>s.right)&&(s=d),c+1>f&&(f=c),r(f,d.top,p-f,d.bottom)}),{start:a,end:s}}var o=e.display,l=e.doc,a=document.createDocumentFragment(),s=Ge(e.display),c=s.left,u=Math.max(o.sizerWidth,$e(e)-o.sizer.offsetLeft)-s.right,d=t.from(),h=t.to();if(d.line==h.line)i(d.line,d.ch,h.ch);else{var f=Yr(l,d.line),p=Yr(l,h.line),m=vr(f)==vr(p),g=i(d.line,d.ch,m?f.text.length+1:null).end,v=i(h.line,m?0:null,h.ch).start;m&&(g.top0?t.blinker=setInterval(function(){t.cursorDiv.style.visibility=(n=!n)?"":"hidden"},e.options.cursorBlinkRate):e.options.cursorBlinkRate<0&&(t.cursorDiv.style.visibility="hidden")}}function ze(e,t){e.doc.mode.startState&&e.doc.frontier=e.display.viewTo)){var n=+new Date+e.options.workTime,r=ll(t.mode,_e(e,t.frontier)),i=[];t.iter(t.frontier,Math.min(t.first+t.size,e.display.viewTo+500),function(o){if(t.frontier>=e.display.viewFrom){var l=o.styles,a=o.text.length>e.options.maxHighlightLength,s=Wr(e,o,a?ll(t.mode,r):r,!0);o.styles=s.styles;var c=o.styleClasses,u=s.classes;u?o.styleClasses=u:c&&(o.styleClasses=null);for(var d=!l||l.length!=o.styles.length||c!=u&&(!c||!u||c.bgClass!=u.bgClass||c.textClass!=u.textClass),h=0;!d&&hn?(ze(e,e.options.workDelay),!0):void 0}),i.length&&Nt(e,function(){for(var t=0;tl;--a){if(a<=o.first)return o.first;var s=Yr(o,a-1);if(s.stateAfter&&(!n||a<=o.frontier))return a;var c=zl(s.text,null,e.options.tabSize);(null==i||r>c)&&(i=a-1,r=c)}return i}function _e(e,t,n){var r=e.doc,i=e.display;if(!r.mode.startState)return!0;var o=Be(e,t,n),l=o>r.first&&Yr(r,o-1).stateAfter;return l=l?ll(r.mode,l):al(r.mode),r.iter(o,t,function(n){Ir(e,n.text,l);var a=o==t-1||o%5==0||o>=i.viewFrom&&o2&&o.push((s.bottom+c.top)/2-n.top)}}o.push(n.bottom-n.top)}}function Xe(e,t,n){if(e.line==t)return{map:e.measure.map,cache:e.measure.cache};for(var r=0;rn)return{map:e.measure.maps[r],cache:e.measure.caches[r],before:!0}}function Ye(e,t){t=vr(t);var n=ei(t),r=e.display.externalMeasured=new Dt(e.doc,t,n);r.lineN=n;var i=r.built=Fr(e,r);return r.text=i.pre,qi(e.display.lineMeasure,i.pre),r}function Qe(e,t,n,r){return et(e,Je(e,t),n,r)}function Ze(e,t){if(t>=e.display.viewFrom&&t=n.lineN&&tt?(i=0,o=1,l="left"):c>t?(i=t-s,o=i+1):(a==e.length-3||t==c&&e[a+3]>t)&&(o=c-s,i=o-1,t>=c&&(l="right")),null!=i){if(r=e[a+2],s==c&&n==(r.insertLeft?"left":"right")&&(l=n),"left"==n&&0==i)for(;a&&e[a-2]==e[a-3]&&e[a-1].insertLeft;)r=e[(a-=3)+2],l="left";if("right"==n&&i==c-s)for(;au;u++){for(;a&&Bi(t.line.text.charAt(o.coverStart+a));)--a;for(;o.coverStart+sxo&&0==a&&s==o.coverEnd-o.coverStart)i=l.parentNode.getBoundingClientRect();else if(yo&&e.options.lineWrapping){var d=jl(l,a,s).getClientRects();i=d.length?d["right"==r?d.length-1:0]:jo}else i=jl(l,a,s).getBoundingClientRect()||jo;if(i.left||i.right||0==a)break;s=a,a-=1,c="right"}yo&&11>xo&&(i=rt(e.display.measure,i))}else{a>0&&(c=r="right");var d;i=e.options.lineWrapping&&(d=l.getClientRects()).length>1?d["right"==r?d.length-1:0]:l.getBoundingClientRect()}if(yo&&9>xo&&!a&&(!i||!i.left&&!i.right)){var h=l.parentNode.getClientRects()[0];i=h?{left:h.left,right:h.left+yt(e.display),top:h.top,bottom:h.bottom}:jo}for(var f=i.top-t.rect.top,p=i.bottom-t.rect.top,m=(f+p)/2,g=t.view.measure.heights,u=0;un.from?l(e-1):l(e,r)}r=r||Yr(e.doc,t.line),i||(i=Je(e,r));var s=ri(r),c=t.ch;if(!s)return l(c);var u=so(s,c),d=a(c,u);return null!=ia&&(d.other=a(c,ia)),d}function ft(e,t){var n=0,t=ge(e.doc,t);e.options.lineWrapping||(n=yt(e.display)*t.ch);var r=Yr(e.doc,t.line),i=ni(r)+je(e.display);return{left:n,right:n,top:i,bottom:i+r.height}}function pt(e,t,n,r){var i=Po(e,t);return i.xRel=r,n&&(i.outside=!0),i}function mt(e,t,n){var r=e.doc;if(n+=e.display.viewOffset,0>n)return pt(r.first,0,!0,-1);var i=ti(r,n),o=r.first+r.size-1;if(i>o)return pt(r.first+r.size-1,Yr(r,o).text.length,!0,1);0>t&&(t=0);for(var l=Yr(r,i);;){var a=gt(e,l,i,t,n),s=mr(l),c=s&&s.find(0,!0);if(!s||!(a.ch>c.from.ch||a.ch==c.from.ch&&a.xRel>0))return a;i=ei(l=c.to.line)}}function gt(e,t,n,r,i){function o(r){var i=ht(e,Po(n,r),"line",t,c);return a=!0,l>i.bottom?i.left-s:lg)return pt(n,f,v,1);for(;;){if(u?f==h||f==uo(t,h,1):1>=f-h){for(var y=p>r||g-r>=r-p?h:f,x=r-(y==h?p:g);Bi(t.text.charAt(y));)++y;var b=pt(n,y,y==h?m:v,-1>x?-1:x>1?1:0);return b}var w=Math.ceil(d/2),C=h+w;if(u){C=h;for(var k=0;w>k;++k)C=uo(t,C,1)}var S=o(C);S>r?(f=C,g=S,(v=a)&&(g+=1e3),d=w):(h=C,p=S,m=a,d-=w)}}function vt(e){if(null!=e.cachedTextHeight)return e.cachedTextHeight;if(null==Ro){Ro=_i("pre");for(var t=0;49>t;++t)Ro.appendChild(document.createTextNode("x")),Ro.appendChild(_i("br"));Ro.appendChild(document.createTextNode("x"))}qi(e.measure,Ro);var n=Ro.offsetHeight/50;return n>3&&(e.cachedTextHeight=n),ji(e.measure),n||1}function yt(e){if(null!=e.cachedCharWidth)return e.cachedCharWidth;var t=_i("span","xxxxxxxxxx"),n=_i("pre",[t]);qi(e.measure,n);var r=t.getBoundingClientRect(),i=(r.right-r.left)/10;return i>2&&(e.cachedCharWidth=i),i||10}function xt(e){e.curOp={cm:e,viewChanged:!1,startHeight:e.doc.height,forceUpdate:!1,updateInput:null,typing:!1,changeObjs:null,cursorActivityHandlers:null,cursorActivityCalled:0,selectionChanged:!1,updateMaxLine:!1,scrollLeft:null,scrollTop:null,scrollToPos:null,focus:!1,id:++Go},qo?qo.ops.push(e.curOp):e.curOp.ownsGroup=qo={ops:[e.curOp],delayedCallbacks:[]}}function bt(e){var t=e.delayedCallbacks,n=0;do{for(;n=n.viewTo)||n.maxLineChanged&&t.options.lineWrapping,e.update=e.mustUpdate&&new L(t,e.mustUpdate&&{top:e.scrollTop,ensure:e.scrollToPos},e.forceUpdate)}function St(e){e.updatedDisplay=e.mustUpdate&&M(e.cm,e.update)}function Lt(e){var t=e.cm,n=t.display;e.updatedDisplay&&H(t),e.barMeasure=p(t),n.maxLineChanged&&!t.options.lineWrapping&&(e.adjustWidthTo=Qe(t,n.maxLine,n.maxLine.text.length).left+3,t.display.sizerWidth=e.adjustWidthTo,e.barMeasure.scrollWidth=Math.max(n.scroller.clientWidth,n.sizer.offsetLeft+e.adjustWidthTo+Ue(t)+t.display.barWidth),e.maxScrollLeft=Math.max(0,n.sizer.offsetLeft+e.adjustWidthTo-$e(t))),(e.updatedDisplay||e.selectionChanged)&&(e.preparedSelection=n.input.prepareSelection())}function Tt(e){var t=e.cm;null!=e.adjustWidthTo&&(t.display.sizer.style.minWidth=e.adjustWidthTo+"px",e.maxScrollLefto;o=r){var l=new Dt(e.doc,Yr(e.doc,o),o);r=o+l.size,i.push(l)}return i}function Et(e,t,n,r){null==t&&(t=e.doc.first),null==n&&(n=e.doc.first+e.doc.size),r||(r=0);var i=e.display;if(r&&nt)&&(i.updateLineNumbers=t),e.curOp.viewChanged=!0,t>=i.viewTo)Io&&xr(e.doc,t)i.viewFrom?Pt(e):(i.viewFrom+=r,i.viewTo+=r);else if(t<=i.viewFrom&&n>=i.viewTo)Pt(e);else if(t<=i.viewFrom){var o=zt(e,n,n+r,1);o?(i.view=i.view.slice(o.index),i.viewFrom=o.lineN,i.viewTo+=r):Pt(e)}else if(n>=i.viewTo){var o=zt(e,t,t,-1);o?(i.view=i.view.slice(0,o.index),i.viewTo=o.lineN):Pt(e)}else{var l=zt(e,t,t,-1),a=zt(e,n,n+r,1);l&&a?(i.view=i.view.slice(0,l.index).concat(Wt(e,l.lineN,a.lineN)).concat(i.view.slice(a.index)),i.viewTo+=r):Pt(e)}var s=i.externalMeasured;s&&(n=i.lineN&&t=r.viewTo)){var o=r.view[Ft(e,t)];if(null!=o.node){var l=o.changes||(o.changes=[]);-1==Di(l,n)&&l.push(n)}}}function Pt(e){e.display.viewFrom=e.display.viewTo=e.doc.first,e.display.view=[],e.display.viewOffset=0}function Ft(e,t){if(t>=e.display.viewTo)return null;if(t-=e.display.viewFrom,0>t)return null;for(var n=e.display.view,r=0;rt)return r}function zt(e,t,n,r){var i,o=Ft(e,t),l=e.display.view;if(!Io||n==e.doc.first+e.doc.size)return{index:o,lineN:n};for(var a=0,s=e.display.viewFrom;o>a;a++)s+=l[a].size;if(s!=t){if(r>0){if(o==l.length-1)return null;i=s+l[o].size-t,o++}else i=s-t;t+=i,n+=i}for(;xr(e.doc,n)!=n;){if(o==(0>r?0:l.length-1))return null;n+=r*l[o-(0>r?1:0)].size,o+=r}return{index:o,lineN:n}}function Rt(e,t,n){var r=e.display,i=r.view;0==i.length||t>=r.viewTo||n<=r.viewFrom?(r.view=Wt(e,t,n),r.viewFrom=t):(r.viewFrom>t?r.view=Wt(e,t,r.viewFrom).concat(r.view):r.viewFromn&&(r.view=r.view.slice(0,Ft(e,n)))),r.viewTo=n}function Bt(e){for(var t=e.display.view,n=0,r=0;r400}var i=e.display;Nl(i.scroller,"mousedown",At(e,$t)),yo&&11>xo?Nl(i.scroller,"dblclick",At(e,function(t){if(!Li(e,t)){var n=Ut(e,t);if(n&&!Qt(e,t)&&!Gt(e.display,t)){Ll(t);var r=e.findWordAt(n);we(e.doc,r.anchor,r.head)}}})):Nl(i.scroller,"dblclick",function(t){Li(e,t)||Ll(t)}),Wo||Nl(i.scroller,"contextmenu",function(t){yn(e,t)});var o,l={end:0};Nl(i.scroller,"touchstart",function(e){if(!n(e)){clearTimeout(o);var t=+new Date;i.activeTouch={start:t,moved:!1,prev:t-l.end<=300?l:null},1==e.touches.length&&(i.activeTouch.left=e.touches[0].pageX,i.activeTouch.top=e.touches[0].pageY)}}),Nl(i.scroller,"touchmove",function(){i.activeTouch&&(i.activeTouch.moved=!0)}),Nl(i.scroller,"touchend",function(n){var o=i.activeTouch;if(o&&!Gt(i,n)&&null!=o.left&&!o.moved&&new Date-o.start<300){var l,a=e.coordsChar(i.activeTouch,"page");l=!o.prev||r(o,o.prev)?new he(a,a):!o.prev.prev||r(o,o.prev.prev)?e.findWordAt(a):new he(Po(a.line,0),ge(e.doc,Po(a.line+1,0))),e.setSelection(l.anchor,l.head),e.focus(),Ll(n)}t()}),Nl(i.scroller,"touchcancel",t),Nl(i.scroller,"scroll",function(){i.scroller.clientHeight&&(nn(e,i.scroller.scrollTop),rn(e,i.scroller.scrollLeft,!0),Hl(e,"scroll",e))}),Nl(i.scroller,"mousewheel",function(t){on(e,t)}),Nl(i.scroller,"DOMMouseScroll",function(t){on(e,t)}),Nl(i.wrapper,"scroll",function(){i.wrapper.scrollTop=i.wrapper.scrollLeft=0}),i.dragFunctions={enter:function(t){Li(e,t)||Ml(t)},over:function(t){Li(e,t)||(en(e,t),Ml(t))},start:function(t){Jt(e,t)},drop:At(e,Zt),leave:function(){tn(e)}};var a=i.input.getField();Nl(a,"keyup",function(t){fn.call(e,t)}),Nl(a,"keydown",At(e,dn)),Nl(a,"keypress",At(e,pn)),Nl(a,"focus",Fi(gn,e)),Nl(a,"blur",Fi(vn,e))}function jt(t,n,r){var i=r&&r!=e.Init;if(!n!=!i){var o=t.display.dragFunctions,l=n?Nl:Ol;l(t.display.scroller,"dragstart",o.start),l(t.display.scroller,"dragenter",o.enter),l(t.display.scroller,"dragover",o.over),l(t.display.scroller,"dragleave",o.leave),l(t.display.scroller,"drop",o.drop)}}function qt(e){var t=e.display;(t.lastWrapHeight!=t.wrapper.clientHeight||t.lastWrapWidth!=t.wrapper.clientWidth)&&(t.cachedCharWidth=t.cachedTextHeight=t.cachedPaddingH=null,t.scrollbarsClipped=!1,e.setSize())}function Gt(e,t){for(var n=bi(t);n!=e.wrapper;n=n.parentNode)if(!n||1==n.nodeType&&"true"==n.getAttribute("cm-ignore-events")||n.parentNode==e.sizer&&n!=e.mover)return!0}function Ut(e,t,n,r){var i=e.display;if(!n&&"true"==bi(t).getAttribute("cm-not-content"))return null;var o,l,a=i.lineSpace.getBoundingClientRect();try{o=t.clientX-a.left,l=t.clientY-a.top}catch(t){return null}var s,c=mt(e,o,l);if(r&&1==c.xRel&&(s=Yr(e.doc,c.line).text).length==c.ch){var u=zl(s,s.length,e.options.tabSize)-s.length;c=Po(c.line,Math.max(0,Math.round((o-Ge(e.display).left)/yt(e.display))-u))}return c}function $t(e){var t=this,n=t.display;if(!(n.activeTouch&&n.input.supportsTouch()||Li(t,e))){if(n.shift=e.shiftKey,Gt(n,e))return void(bo||(n.scroller.draggable=!1,setTimeout(function(){n.scroller.draggable=!0},100)));if(!Qt(t,e)){var r=Ut(t,e);switch(window.focus(),wi(e)){case 1:t.state.selectingText?t.state.selectingText(e):r?Vt(t,e,r):bi(e)==n.scroller&&Ll(e);break;case 2:bo&&(t.state.lastMiddleDown=+new Date),r&&we(t.doc,r),setTimeout(function(){n.input.focus()},20),Ll(e);break;case 3:Wo?yn(t,e):mn(t)}}}}function Vt(e,t,n){yo?setTimeout(Fi(Y,e),0):e.curOp.focus=Gi();var r,i=+new Date;_o&&_o.time>i-400&&0==Fo(_o.pos,n)?r="triple":Bo&&Bo.time>i-400&&0==Fo(Bo.pos,n)?(r="double",_o={time:i,pos:n}):(r="single",Bo={time:i,pos:n});var o,l=e.doc.sel,a=Ao?t.metaKey:t.ctrlKey;e.options.dragDrop&&Zl&&!Q(e)&&"single"==r&&(o=l.contains(n))>-1&&(Fo((o=l.ranges[o]).from(),n)<0||n.xRel>0)&&(Fo(o.to(),n)>0||n.xRel<0)?Kt(e,t,n,a):Xt(e,t,n,r,a)}function Kt(e,t,n,r){var i=e.display,o=+new Date,l=At(e,function(a){bo&&(i.scroller.draggable=!1),e.state.draggingText=!1,Ol(document,"mouseup",l),Ol(i.scroller,"drop",l),Math.abs(t.clientX-a.clientX)+Math.abs(t.clientY-a.clientY)<10&&(Ll(a),!r&&+new Date-200=p;p++){var v=Yr(c,p).text,y=Rl(v,s,o);s==f?i.push(new he(Po(p,y),Po(p,y))):v.length>y&&i.push(new he(Po(p,y),Po(p,Rl(v,f,o))))}i.length||i.push(new he(n,n)),Me(c,fe(h.ranges.slice(0,d).concat(i),d),{origin:"*mouse",scroll:!1}),e.scrollIntoView(t)}else{var x=u,b=x.anchor,w=t;if("single"!=r){if("double"==r)var C=e.findWordAt(t);else var C=new he(Po(t.line,0),ge(c,Po(t.line+1,0)));Fo(C.anchor,b)>0?(w=C.head,b=X(x.from(),C.anchor)):(w=C.anchor,b=K(x.to(),C.head))}var i=h.ranges.slice(0);i[d]=new he(ge(c,b),w),Me(c,fe(i,d),Pl)}}function l(t){var n=++y,i=Ut(e,t,!0,"rect"==r);if(i)if(0!=Fo(i,g)){e.curOp.focus=Gi(),o(i);var a=b(s,c);(i.line>=a.to||i.linev.bottom?20:0;u&&setTimeout(At(e,function(){y==n&&(s.scroller.scrollTop+=u,l(t))}),50)}}function a(t){e.state.selectingText=!1,y=1/0,Ll(t),s.input.focus(),Ol(document,"mousemove",x),Ol(document,"mouseup",w),c.history.lastSelOrigin=null}var s=e.display,c=e.doc;Ll(t);var u,d,h=c.sel,f=h.ranges;if(i&&!t.shiftKey?(d=c.sel.contains(n),u=d>-1?f[d]:new he(n,n)):(u=c.sel.primary(),d=c.sel.primIndex),t.altKey)r="rect",i||(u=new he(n,n)),n=Ut(e,t,!0,!0),d=-1;else if("double"==r){var p=e.findWordAt(n);u=e.display.shift||c.extend?be(c,u,p.anchor,p.head):p}else if("triple"==r){var m=new he(Po(n.line,0),ge(c,Po(n.line+1,0)));u=e.display.shift||c.extend?be(c,u,m.anchor,m.head):m}else u=be(c,u,n);i?-1==d?(d=f.length,Me(c,fe(f.concat([u]),d),{scroll:!1,origin:"*mouse"})):f.length>1&&f[d].empty()&&"single"==r&&!t.shiftKey?(Me(c,fe(f.slice(0,d).concat(f.slice(d+1)),0),{scroll:!1,origin:"*mouse"}),h=c.sel):ke(c,d,u,Pl):(d=0,Me(c,new de([u],0),Pl),h=c.sel);var g=n,v=s.wrapper.getBoundingClientRect(),y=0,x=At(e,function(e){wi(e)?l(e):a(e)}),w=At(e,a);e.state.selectingText=w,Nl(document,"mousemove",x),Nl(document,"mouseup",w)}function Yt(e,t,n,r,i){try{var o=t.clientX,l=t.clientY}catch(t){return!1}if(o>=Math.floor(e.display.gutters.getBoundingClientRect().right))return!1;r&&Ll(t);var a=e.display,s=a.lineDiv.getBoundingClientRect();if(l>s.bottom||!Mi(e,n))return xi(t);l-=s.top-a.viewOffset;for(var c=0;c=o){var d=ti(e.doc,l),h=e.options.gutters[c];return i(e,n,e,d,h,t),xi(t)}}}function Qt(e,t){return Yt(e,t,"gutterClick",!0,ki)}function Zt(e){var t=this;if(tn(t),!Li(t,e)&&!Gt(t.display,e)){Ll(e),yo&&(Uo=+new Date);var n=Ut(t,e,!0),r=e.dataTransfer.files;if(n&&!Q(t))if(r&&r.length&&window.FileReader&&window.File)for(var i=r.length,o=Array(i),l=0,a=function(e,r){if(!t.options.allowDropFileTypes||-1!=Di(t.options.allowDropFileTypes,e.type)){var a=new FileReader;a.onload=At(t,function(){var e=a.result;if(/[\x00-\x08\x0e-\x1f]{2}/.test(e)&&(e=""),o[r]=e,++l==i){n=ge(t.doc,n);var s={from:n,to:n,text:t.doc.splitLines(o.join(t.doc.lineSeparator())),origin:"paste"};Ln(t.doc,s),Te(t.doc,pe(n,Qo(s)))}}),a.readAsText(e)}},s=0;i>s;++s)a(r[s],s);else{if(t.state.draggingText&&t.doc.sel.contains(n)>-1)return t.state.draggingText(e),void setTimeout(function(){t.display.input.focus()},20);try{var o=e.dataTransfer.getData("Text");if(o){if(t.state.draggingText&&!(Ao?e.altKey:e.ctrlKey))var c=t.listSelections();if(Ne(t.doc,pe(n,n)),c)for(var s=0;sl.clientWidth,s=l.scrollHeight>l.clientHeight;if(r&&a||i&&s){if(i&&Ao&&bo)e:for(var c=t.target,u=o.view;c!=l;c=c.parentNode)for(var d=0;dh?f=Math.max(0,f+h-50):p=Math.min(e.doc.height,p+h+50),A(e,{top:f,bottom:p})}20>$o&&(null==o.wheelStartX?(o.wheelStartX=l.scrollLeft,o.wheelStartY=l.scrollTop,o.wheelDX=r,o.wheelDY=i,setTimeout(function(){if(null!=o.wheelStartX){var e=l.scrollLeft-o.wheelStartX,t=l.scrollTop-o.wheelStartY,n=t&&o.wheelDY&&t/o.wheelDY||e&&o.wheelDX&&e/o.wheelDX;o.wheelStartX=o.wheelStartY=null,n&&(Vo=(Vo*$o+n)/($o+1),++$o)}},200)):(o.wheelDX+=r,o.wheelDY+=i))}}function ln(e,t,n){if("string"==typeof t&&(t=sl[t],!t))return!1;e.display.input.ensurePolled();var r=e.display.shift,i=!1;try{Q(e)&&(e.state.suppressEdits=!0),n&&(e.display.shift=!1),i=t(e)!=El}finally{e.display.shift=r,e.state.suppressEdits=!1}return i}function an(e,t,n){for(var r=0;rxo&&27==e.keyCode&&(e.returnValue=!1);var n=e.keyCode;t.display.shift=16==n||e.shiftKey;var r=cn(t,e);ko&&(Yo=r?n:null,!r&&88==n&&!ta&&(Ao?e.metaKey:e.ctrlKey)&&t.replaceSelection("",null,"cut")),18!=n||/\bCodeMirror-crosshair\b/.test(t.display.lineDiv.className)||hn(t)}}function hn(e){function t(e){18!=e.keyCode&&e.altKey||(Xl(n,"CodeMirror-crosshair"),Ol(document,"keyup",t),Ol(document,"mouseover",t))}var n=e.display.lineDiv;Yl(n,"CodeMirror-crosshair"),Nl(document,"keyup",t),Nl(document,"mouseover",t)}function fn(e){16==e.keyCode&&(this.doc.sel.shift=!1),Li(this,e)}function pn(e){var t=this;if(!(Gt(t.display,e)||Li(t,e)||e.ctrlKey&&!e.altKey||Ao&&e.metaKey)){var n=e.keyCode,r=e.charCode;if(ko&&n==Yo)return Yo=null,void Ll(e);if(!ko||e.which&&!(e.which<10)||!cn(t,e)){var i=String.fromCharCode(null==r?n:r);un(t,e,i)||t.display.input.onKeyPress(e)}}}function mn(e){e.state.delayingBlurEvent=!0,setTimeout(function(){e.state.delayingBlurEvent&&(e.state.delayingBlurEvent=!1,vn(e))},100)}function gn(e){e.state.delayingBlurEvent&&(e.state.delayingBlurEvent=!1),"nocursor"!=e.options.readOnly&&(e.state.focused||(Hl(e,"focus",e),e.state.focused=!0,Yl(e.display.wrapper,"CodeMirror-focused"),e.curOp||e.display.selForContextMenu==e.doc.sel||(e.display.input.reset(),bo&&setTimeout(function(){e.display.input.reset(!0)},20)),e.display.input.receivedFocus()),Fe(e))}function vn(e){e.state.delayingBlurEvent||(e.state.focused&&(Hl(e,"blur",e),e.state.focused=!1,Xl(e.display.wrapper,"CodeMirror-focused")),clearInterval(e.display.blinker),setTimeout(function(){e.state.focused||(e.display.shift=!1)},150))}function yn(e,t){Gt(e.display,t)||xn(e,t)||Li(e,t,"contextmenu")||e.display.input.onContextMenu(t)}function xn(e,t){return Mi(e,"gutterContextMenu")?Yt(e,t,"gutterContextMenu",!1,Hl):!1}function bn(e,t){if(Fo(e,t.from)<0)return e;if(Fo(e,t.to)<=0)return Qo(t);var n=e.line+t.text.length-(t.to.line-t.from.line)-1,r=e.ch;return e.line==t.to.line&&(r+=Qo(t).ch-t.to.ch),Po(n,r)}function wn(e,t){for(var n=[],r=0;r=0;--i)Tn(e,{from:r[i].from,to:r[i].to,text:i?[""]:t.text});else Tn(e,t)}}function Tn(e,t){if(1!=t.text.length||""!=t.text[0]||0!=Fo(t.from,t.to)){var n=wn(e,t);si(e,t,n,e.cm?e.cm.curOp.id:NaN),An(e,t,n,ir(e,t));var r=[];Kr(e,function(e,n){n||-1!=Di(r,e.history)||(yi(e.history,t),r.push(e.history)),An(e,t,null,ir(e,t))})}}function Mn(e,t,n){if(!e.cm||!e.cm.state.suppressEdits){for(var r,i=e.history,o=e.sel,l="undo"==t?i.done:i.undone,a="undo"==t?i.undone:i.done,s=0;s=0;--s){var d=r.changes[s];if(d.origin=t,u&&!Sn(e,d,!1))return void(l.length=0);c.push(oi(e,d));var h=s?wn(e,d):Hi(l);An(e,d,h,lr(e,d)),!s&&e.cm&&e.cm.scrollIntoView({from:d.from,to:Qo(d)});var f=[];Kr(e,function(e,t){t||-1!=Di(f,e.history)||(yi(e.history,d),f.push(e.history)),An(e,d,null,lr(e,d))})}}}}function Nn(e,t){if(0!=t&&(e.first+=t,e.sel=new de(Wi(e.sel.ranges,function(e){return new he(Po(e.anchor.line+t,e.anchor.ch),Po(e.head.line+t,e.head.ch))}),e.sel.primIndex),e.cm)){Et(e.cm,e.first,e.first-t,t);for(var n=e.cm.display,r=n.viewFrom;re.lastLine())){if(t.from.lineo&&(t={from:t.from,to:Po(o,Yr(e,o).text.length),text:[t.text[0]],origin:t.origin}),t.removed=Qr(e,t.from,t.to),n||(n=wn(e,t)),e.cm?On(e.cm,t,r):Ur(e,t,r),Ne(e,n,Il)}}function On(e,t,n){var r=e.doc,i=e.display,l=t.from,a=t.to,s=!1,c=l.line;e.options.lineWrapping||(c=ei(vr(Yr(r,l.line))),r.iter(c,a.line+1,function(e){return e==i.maxLine?(s=!0,!0):void 0})),r.sel.contains(t.from,t.to)>-1&&Ti(e),Ur(r,t,n,o(e)),e.options.lineWrapping||(r.iter(c,l.line+t.text.length,function(e){var t=d(e);t>i.maxLineLength&&(i.maxLine=e,i.maxLineLength=t,i.maxLineChanged=!0,s=!1)}),s&&(e.curOp.updateMaxLine=!0)),r.frontier=Math.min(r.frontier,l.line),ze(e,400);var u=t.text.length-(a.line-l.line)-1;t.full?Et(e):l.line!=a.line||1!=t.text.length||Gr(e.doc,t)?Et(e,l.line,a.line+1,u):It(e,l.line,"text");var h=Mi(e,"changes"),f=Mi(e,"change");if(f||h){var p={from:l,to:a,text:t.text,removed:t.removed,origin:t.origin};f&&ki(e,"change",e,p),h&&(e.curOp.changeObjs||(e.curOp.changeObjs=[])).push(p)}e.display.selForContextMenu=null}function Hn(e,t,n,r,i){if(r||(r=n),Fo(r,n)<0){var o=r;r=n,n=o}"string"==typeof t&&(t=e.splitLines(t)),Ln(e,{from:n,to:r,text:t,origin:i})}function Dn(e,t){if(!Li(e,"scrollCursorIntoView")){var n=e.display,r=n.sizer.getBoundingClientRect(),i=null;if(t.top+r.top<0?i=!0:t.bottom+r.top>(window.innerHeight||document.documentElement.clientHeight)&&(i=!1),null!=i&&!To){var o=_i("div","",null,"position: absolute; top: "+(t.top-n.viewOffset-je(e.display))+"px; height: "+(t.bottom-t.top+Ue(e)+n.barHeight)+"px; left: "+t.left+"px; width: 2px;");e.display.lineSpace.appendChild(o),o.scrollIntoView(i),e.display.lineSpace.removeChild(o)}}}function Wn(e,t,n,r){null==r&&(r=0);for(var i=0;5>i;i++){var o=!1,l=ht(e,t),a=n&&n!=t?ht(e,n):l,s=In(e,Math.min(l.left,a.left),Math.min(l.top,a.top)-r,Math.max(l.left,a.left),Math.max(l.bottom,a.bottom)+r),c=e.doc.scrollTop,u=e.doc.scrollLeft;if(null!=s.scrollTop&&(nn(e,s.scrollTop),Math.abs(e.doc.scrollTop-c)>1&&(o=!0)),null!=s.scrollLeft&&(rn(e,s.scrollLeft),Math.abs(e.doc.scrollLeft-u)>1&&(o=!0)),!o)break}return l}function En(e,t,n,r,i){var o=In(e,t,n,r,i);null!=o.scrollTop&&nn(e,o.scrollTop),null!=o.scrollLeft&&rn(e,o.scrollLeft)}function In(e,t,n,r,i){var o=e.display,l=vt(e.display);0>n&&(n=0);var a=e.curOp&&null!=e.curOp.scrollTop?e.curOp.scrollTop:o.scroller.scrollTop,s=Ve(e),c={};i-n>s&&(i=n+s);var u=e.doc.height+qe(o),d=l>n,h=i>u-l;if(a>n)c.scrollTop=d?0:n;else if(i>a+s){var f=Math.min(n,(h?u:i)-s);f!=a&&(c.scrollTop=f)}var p=e.curOp&&null!=e.curOp.scrollLeft?e.curOp.scrollLeft:o.scroller.scrollLeft,m=$e(e)-(e.options.fixedGutter?o.gutters.offsetWidth:0),g=r-t>m;return g&&(r=t+m),10>t?c.scrollLeft=0:p>t?c.scrollLeft=Math.max(0,t-(g?0:10)):r>m+p-3&&(c.scrollLeft=r+(g?0:10)-m),c}function Pn(e,t,n){(null!=t||null!=n)&&zn(e),null!=t&&(e.curOp.scrollLeft=(null==e.curOp.scrollLeft?e.doc.scrollLeft:e.curOp.scrollLeft)+t),null!=n&&(e.curOp.scrollTop=(null==e.curOp.scrollTop?e.doc.scrollTop:e.curOp.scrollTop)+n)}function Fn(e){zn(e);var t=e.getCursor(),n=t,r=t;e.options.lineWrapping||(n=t.ch?Po(t.line,t.ch-1):t,r=Po(t.line,t.ch+1)),e.curOp.scrollToPos={from:n,to:r,margin:e.options.cursorScrollMargin,isCursor:!0}}function zn(e){var t=e.curOp.scrollToPos;if(t){e.curOp.scrollToPos=null;var n=ft(e,t.from),r=ft(e,t.to),i=In(e,Math.min(n.left,r.left),Math.min(n.top,r.top)-t.margin,Math.max(n.right,r.right),Math.max(n.bottom,r.bottom)+t.margin);e.scrollTo(i.scrollLeft,i.scrollTop)}}function Rn(e,t,n,r){var i,o=e.doc;null==n&&(n="add"),"smart"==n&&(o.mode.indent?i=_e(e,t):n="prev");var l=e.options.tabSize,a=Yr(o,t),s=zl(a.text,null,l);a.stateAfter&&(a.stateAfter=null);var c,u=a.text.match(/^\s*/)[0];if(r||/\S/.test(a.text)){if("smart"==n&&(c=o.mode.indent(i,a.text.slice(u.length),a.text),c==El||c>150)){if(!r)return;n="prev"}}else c=0,n="not";"prev"==n?c=t>o.first?zl(Yr(o,t-1).text,null,l):0:"add"==n?c=s+e.options.indentUnit:"subtract"==n?c=s-e.options.indentUnit:"number"==typeof n&&(c=s+n),c=Math.max(0,c);var d="",h=0;if(e.options.indentWithTabs)for(var f=Math.floor(c/l);f;--f)h+=l,d+=" ";if(c>h&&(d+=Oi(c-h)),d!=u)return Hn(o,d,Po(t,0),Po(t,u.length),"+input"),a.stateAfter=null,!0;for(var f=0;f=0;t--)Hn(e.doc,"",r[t].from,r[t].to,"+delete");Fn(e)})}function jn(e,t,n,r,i){function o(){var t=a+n;return t=e.first+e.size?d=!1:(a=t,u=Yr(e,t))}function l(e){var t=(i?uo:ho)(u,s,n,!0);if(null==t){if(e||!o())return d=!1;s=i?(0>n?ro:no)(u):0>n?u.text.length:0}else s=t;return!0}var a=t.line,s=t.ch,c=n,u=Yr(e,a),d=!0;if("char"==r)l();else if("column"==r)l(!0);else if("word"==r||"group"==r)for(var h=null,f="group"==r,p=e.cm&&e.cm.getHelper(t,"wordChars"),m=!0;!(0>n)||l(!m);m=!1){var g=u.text.charAt(s)||"\n",v=zi(g,p)?"w":f&&"\n"==g?"n":!f||/\s/.test(g)?null:"p";if(!f||m||v||(v="s"),h&&h!=v){0>n&&(n=1,l());break}if(v&&(h=v),n>0&&!l(!m))break}var y=De(e,Po(a,s),c,!0);return d||(y.hitSide=!0),y}function qn(e,t,n,r){var i,o=e.doc,l=t.left;if("page"==r){var a=Math.min(e.display.wrapper.clientHeight,window.innerHeight||document.documentElement.clientHeight);i=t.top+n*(a-(0>n?1.5:.5)*vt(e.display))}else"line"==r&&(i=n>0?t.bottom+3:t.top-3);for(;;){var s=mt(e,l,i);if(!s.outside)break;if(0>n?0>=i:i>=o.height){s.hitSide=!0;break}i+=5*n}return s}function Gn(t,n,r,i){e.defaults[t]=n,r&&(Jo[t]=i?function(e,t,n){n!=el&&r(e,t,n)}:r)}function Un(e){for(var t,n,r,i,o=e.split(/-(?!$)/),e=o[o.length-1],l=0;l0||0==l&&o.clearWhenEmpty!==!1)return o;if(o.replacedWith&&(o.collapsed=!0,o.widgetNode=_i("span",[o.replacedWith],"CodeMirror-widget"),r.handleMouseEvents||o.widgetNode.setAttribute("cm-ignore-events","true"),r.insertLeft&&(o.widgetNode.insertLeft=!0)),o.collapsed){if(gr(e,t.line,t,n,o)||t.line!=n.line&&gr(e,n.line,t,n,o))throw new Error("Inserting collapsed marker partially overlapping an existing one");Io=!0}o.addToHistory&&si(e,{from:t,to:n,origin:"markText"},e.sel,NaN);var a,s=t.line,c=e.cm;if(e.iter(s,n.line+1,function(e){c&&o.collapsed&&!c.options.lineWrapping&&vr(e)==c.display.maxLine&&(a=!0),o.collapsed&&s!=t.line&&Jr(e,0),tr(e,new Zn(o,s==t.line?t.ch:null,s==n.line?n.ch:null)),++s}),o.collapsed&&e.iter(t.line,n.line+1,function(t){wr(e,t)&&Jr(t,0)}),o.clearOnEnter&&Nl(o,"beforeCursorEnter",function(){o.clear()}),o.readOnly&&(Eo=!0,(e.history.done.length||e.history.undone.length)&&e.clearHistory()),o.collapsed&&(o.id=++pl,o.atomic=!0),c){if(a&&(c.curOp.updateMaxLine=!0),o.collapsed)Et(c,t.line,n.line+1);else if(o.className||o.title||o.startStyle||o.endStyle||o.css)for(var u=t.line;u<=n.line;u++)It(c,u,"text");o.atomic&&Oe(c.doc),ki(c,"markerAdded",c,o)}return o}function Kn(e,t,n,r,i){r=Pi(r),r.shared=!1;var o=[Vn(e,t,n,r,i)],l=o[0],a=r.widgetNode;return Kr(e,function(e){a&&(r.widgetNode=a.cloneNode(!0)),o.push(Vn(e,ge(e,t),ge(e,n),r,i));for(var s=0;s=t:o.to>t);(r||(r=[])).push(new Zn(l,o.from,s?null:o.to))}}return r}function rr(e,t,n){if(e)for(var r,i=0;i=t:o.to>t);if(a||o.from==t&&"bookmark"==l.type&&(!n||o.marker.insertLeft)){var s=null==o.from||(l.inclusiveLeft?o.from<=t:o.from0&&a)for(var d=0;dd;++d)p.push(m);p.push(s)}return p}function or(e){for(var t=0;t0)){var u=[s,1],d=Fo(c.from,a.from),h=Fo(c.to,a.to);(0>d||!l.inclusiveLeft&&!d)&&u.push({from:c.from,to:a.from}),(h>0||!l.inclusiveRight&&!h)&&u.push({from:a.to,to:c.to}),i.splice.apply(i,u),s+=u.length-1}}return i}function sr(e){var t=e.markedSpans;if(t){for(var n=0;n=0&&0>=d||0>=u&&d>=0)&&(0>=u&&(Fo(c.to,n)>0||s.marker.inclusiveRight&&i.inclusiveLeft)||u>=0&&(Fo(c.from,r)<0||s.marker.inclusiveLeft&&i.inclusiveRight)))return!0}}}function vr(e){for(var t;t=pr(e);)e=t.find(-1,!0).line;return e}function yr(e){for(var t,n;t=mr(e);)e=t.find(1,!0).line,(n||(n=[])).push(e);return n}function xr(e,t){var n=Yr(e,t),r=vr(n);return n==r?t:ei(r)}function br(e,t){if(t>e.lastLine())return t;var n,r=Yr(e,t);if(!wr(e,r))return t;for(;n=mr(r);)r=n.find(1,!0).line;return ei(r)+1}function wr(e,t){var n=Io&&t.markedSpans;if(n)for(var r,i=0;io;o++){i&&(i[0]=e.innerMode(t,r).mode);var l=t.token(n,r);if(n.pos>n.start)return l}throw new Error("Mode "+t.name+" failed to advance stream.")}function Hr(e,t,n,r){function i(e){return{start:d.start,end:d.pos,string:d.current(),type:o||null,state:e?ll(l.mode,u):u}}var o,l=e.doc,a=l.mode;t=ge(l,t);var s,c=Yr(l,t.line),u=_e(e,t.line,n),d=new fl(c.text,e.options.tabSize);for(r&&(s=[]);(r||d.pose.options.maxHighlightLength?(a=!1,l&&Ir(e,t,r,d.pos),d.pos=t.length,s=null):s=Nr(Or(n,d,r,h),o),h){var f=h[0].name;f&&(s="m-"+(s?f+" "+s:f))}if(!a||u!=s){for(;cc;){var r=i[s];r>e&&i.splice(s,1,e,i[s+1],r),s+=2,c=Math.min(e,r)}if(t)if(a.opaque)i.splice(n,s-n,e,"cm-overlay "+t),s=n+2;else for(;s>n;n+=2){var o=i[n+1];i[n+1]=(o?o+" ":"")+"cm-overlay "+t}},o)}return{styles:i,classes:o.bgClass||o.textClass?o:null}}function Er(e,t,n){if(!t.styles||t.styles[0]!=e.state.modeGen){var r=_e(e,ei(t)),i=Wr(e,t,t.text.length>e.options.maxHighlightLength?ll(e.doc.mode,r):r);t.stateAfter=r,t.styles=i.styles,i.classes?t.styleClasses=i.classes:t.styleClasses&&(t.styleClasses=null),n===e.doc.frontier&&e.doc.frontier++}return t.styles}function Ir(e,t,n,r){var i=e.doc.mode,o=new fl(t,e.options.tabSize);for(o.start=o.pos=r||0,""==t&&Ar(i,n);!o.eol();)Or(i,o,n),o.start=o.pos}function Pr(e,t){if(!e||/^\s*$/.test(e))return null;var n=t.addModeClass?bl:xl;return n[e]||(n[e]=e.replace(/\S+/g,"cm-$&"))}function Fr(e,t){var n=_i("span",null,null,bo?"padding-right: .1px":null),r={pre:_i("pre",[n],"CodeMirror-line"),content:n,col:0,pos:0,cm:e,splitSpaces:(yo||bo)&&e.getOption("lineWrapping")};t.measure={};for(var i=0;i<=(t.rest?t.rest.length:0);i++){var o,l=i?t.rest[i-1]:t.line;r.pos=0,r.addToken=Rr,Qi(e.display.measure)&&(o=ri(l))&&(r.addToken=_r(r.addToken,o)),r.map=[];var a=t!=e.display.externalMeasured&&ei(l);qr(l,r,Er(e,l,a)),l.styleClasses&&(l.styleClasses.bgClass&&(r.bgClass=$i(l.styleClasses.bgClass,r.bgClass||"")),l.styleClasses.textClass&&(r.textClass=$i(l.styleClasses.textClass,r.textClass||""))),0==r.map.length&&r.map.push(0,0,r.content.appendChild(Yi(e.display.measure))),0==i?(t.measure.map=r.map,t.measure.cache={}):((t.measure.maps||(t.measure.maps=[])).push(r.map),(t.measure.caches||(t.measure.caches=[])).push({}))}return bo&&/\bcm-tab\b/.test(r.content.lastChild.className)&&(r.content.className="cm-tab-wrap-hack"),Hl(e,"renderLine",e,t.line,r.pre),r.pre.className&&(r.textClass=$i(r.pre.className,r.textClass||"")),r}function zr(e){var t=_i("span","•","cm-invalidchar");return t.title="\\u"+e.charCodeAt(0).toString(16),t.setAttribute("aria-label",t.title),t}function Rr(e,t,n,r,i,o,l){if(t){var a=e.splitSpaces?t.replace(/ {3,}/g,Br):t,s=e.cm.state.specialChars,c=!1;if(s.test(t))for(var u=document.createDocumentFragment(),d=0;;){s.lastIndex=d;var h=s.exec(t),f=h?h.index-d:t.length-d;if(f){var p=document.createTextNode(a.slice(d,d+f));yo&&9>xo?u.appendChild(_i("span",[p])):u.appendChild(p),e.map.push(e.pos,e.pos+f,p),e.col+=f,e.pos+=f}if(!h)break;if(d+=f+1," "==h[0]){var m=e.cm.options.tabSize,g=m-e.col%m,p=u.appendChild(_i("span",Oi(g),"cm-tab"));p.setAttribute("role","presentation"),p.setAttribute("cm-text"," "),e.col+=g}else if("\r"==h[0]||"\n"==h[0]){var p=u.appendChild(_i("span","\r"==h[0]?"␍":"","cm-invalidchar"));p.setAttribute("cm-text",h[0]),e.col+=1}else{var p=e.cm.options.specialCharPlaceholder(h[0]);p.setAttribute("cm-text",h[0]),yo&&9>xo?u.appendChild(_i("span",[p])):u.appendChild(p),e.col+=1}e.map.push(e.pos,e.pos+1,p),e.pos++}else{e.col+=t.length;var u=document.createTextNode(a);e.map.push(e.pos,e.pos+t.length,u),yo&&9>xo&&(c=!0),e.pos+=t.length}if(n||r||i||c||l){var v=n||"";r&&(v+=r),i&&(v+=i);var y=_i("span",[u],v,l);return o&&(y.title=o),e.content.appendChild(y)}e.content.appendChild(u)}}function Br(e){for(var t=" ",n=0;nc&&h.from<=c)break}if(h.to>=u)return e(n,r,i,o,l,a,s);e(n,r.slice(0,h.to-c),i,o,null,a,s),o=null,r=r.slice(h.to-c),c=h.to}}}function jr(e,t,n,r){var i=!r&&n.widgetNode;i&&e.map.push(e.pos,e.pos+t,i),!r&&e.cm.display.input.needsContentAttribute&&(i||(i=e.content.appendChild(document.createElement("span"))),i.setAttribute("cm-marker",n.id)),i&&(e.cm.display.input.setUneditable(i),e.content.appendChild(i)),e.pos+=t}function qr(e,t,n){var r=e.markedSpans,i=e.text,o=0;if(r)for(var l,a,s,c,u,d,h,f=i.length,p=0,m=1,g="",v=0;;){if(v==p){s=c=u=d=a="",h=null,v=1/0;for(var y=[],x=0;xp||w.collapsed&&b.to==p&&b.from==p)?(null!=b.to&&b.to!=p&&v>b.to&&(v=b.to,c=""),w.className&&(s+=" "+w.className),w.css&&(a=w.css),w.startStyle&&b.from==p&&(u+=" "+w.startStyle),w.endStyle&&b.to==v&&(c+=" "+w.endStyle),w.title&&!d&&(d=w.title),w.collapsed&&(!h||hr(h.marker,w)<0)&&(h=b)):b.from>p&&v>b.from&&(v=b.from)}if(h&&(h.from||0)==p){if(jr(t,(null==h.to?f+1:h.to)-p,h.marker,null==h.from),null==h.to)return;h.to==p&&(h=!1)}if(!h&&y.length)for(var x=0;x=f)break;for(var C=Math.min(f,v);;){if(g){var k=p+g.length;if(!h){var S=k>C?g.slice(0,C-p):g;t.addToken(t,S,l?l+s:s,u,p+S.length==v?c:"",d,a)}if(k>=C){g=g.slice(C-p),p=C;break}p=k,u=""}g=i.slice(o,o=n[m++]),l=Pr(n[m++],t.cm.options)}}else for(var m=1;mn;++n)o.push(new yl(c[n],i(n),r));return o}var a=t.from,s=t.to,c=t.text,u=Yr(e,a.line),d=Yr(e,s.line),h=Hi(c),f=i(c.length-1),p=s.line-a.line;if(t.full)e.insert(0,l(0,c.length)),e.remove(c.length,e.size-c.length);else if(Gr(e,t)){var m=l(0,c.length-1);o(d,d.text,f),p&&e.remove(a.line,p),m.length&&e.insert(a.line,m)}else if(u==d)if(1==c.length)o(u,u.text.slice(0,a.ch)+h+u.text.slice(s.ch),f);else{var m=l(1,c.length-1);m.push(new yl(h+u.text.slice(s.ch),f,r)),o(u,u.text.slice(0,a.ch)+c[0],i(0)),e.insert(a.line+1,m)}else if(1==c.length)o(u,u.text.slice(0,a.ch)+c[0]+d.text.slice(s.ch),i(0)),e.remove(a.line+1,p);else{o(u,u.text.slice(0,a.ch)+c[0],i(0)),o(d,h+d.text.slice(s.ch),f);var m=l(1,c.length-1);p>1&&e.remove(a.line+1,p-1),e.insert(a.line+1,m)}ki(e,"change",e,t)}function $r(e){this.lines=e,this.parent=null;for(var t=0,n=0;tt||t>=e.size)throw new Error("There is no line "+(t+e.first)+" in the document.");for(var n=e;!n.lines;)for(var r=0;;++r){var i=n.children[r],o=i.chunkSize();if(o>t){n=i;break}t-=o}return n.lines[t]}function Qr(e,t,n){var r=[],i=t.line;return e.iter(t.line,n.line+1,function(e){var o=e.text;i==n.line&&(o=o.slice(0,n.ch)),i==t.line&&(o=o.slice(t.ch)),r.push(o),++i}),r}function Zr(e,t,n){var r=[];return e.iter(t,n,function(e){r.push(e.text)}),r}function Jr(e,t){var n=t-e.height;if(n)for(var r=e;r;r=r.parent)r.height+=n}function ei(e){if(null==e.parent)return null;for(var t=e.parent,n=Di(t.lines,e),r=t.parent;r;t=r,r=r.parent)for(var i=0;r.children[i]!=t;++i)n+=r.children[i].chunkSize();return n+t.first}function ti(e,t){var n=e.first;e:do{for(var r=0;rt){e=i;continue e}t-=o,n+=i.chunkSize()}return n}while(!e.lines);for(var r=0;rt)break;t-=a}return n+r}function ni(e){e=vr(e);for(var t=0,n=e.parent,r=0;r1&&!e.done[e.done.length-2].ranges?(e.done.pop(),Hi(e.done)):void 0}function si(e,t,n,r){var i=e.history;i.undone.length=0;var o,l=+new Date;if((i.lastOp==r||i.lastOrigin==t.origin&&t.origin&&("+"==t.origin.charAt(0)&&e.cm&&i.lastModTime>l-e.cm.options.historyEventDelay||"*"==t.origin.charAt(0)))&&(o=ai(i,i.lastOp==r))){var a=Hi(o.changes);0==Fo(t.from,t.to)&&0==Fo(t.from,a.to)?a.to=Qo(t):o.changes.push(oi(e,t))}else{var s=Hi(i.done);for(s&&s.ranges||di(e.sel,i.done),o={changes:[oi(e,t)],generation:i.generation},i.done.push(o);i.done.length>i.undoDepth;)i.done.shift(),i.done[0].ranges||i.done.shift()}i.done.push(n),i.generation=++i.maxGeneration,i.lastModTime=i.lastSelTime=l,i.lastOp=i.lastSelOp=r,i.lastOrigin=i.lastSelOrigin=t.origin,a||Hl(e,"historyAdded")}function ci(e,t,n,r){var i=t.charAt(0);return"*"==i||"+"==i&&n.ranges.length==r.ranges.length&&n.somethingSelected()==r.somethingSelected()&&new Date-e.history.lastSelTime<=(e.cm?e.cm.options.historyEventDelay:500)}function ui(e,t,n,r){var i=e.history,o=r&&r.origin;n==i.lastSelOp||o&&i.lastSelOrigin==o&&(i.lastModTime==i.lastSelTime&&i.lastOrigin==o||ci(e,o,Hi(i.done),t))?i.done[i.done.length-1]=t:di(t,i.done),i.lastSelTime=+new Date,i.lastSelOrigin=o,i.lastSelOp=n,r&&r.clearRedo!==!1&&li(i.undone)}function di(e,t){var n=Hi(t);n&&n.ranges&&n.equals(e)||t.push(e)}function hi(e,t,n,r){var i=t["spans_"+e.id],o=0;e.iter(Math.max(e.first,n),Math.min(e.first+e.size,r),function(n){n.markedSpans&&((i||(i=t["spans_"+e.id]={}))[o]=n.markedSpans),++o})}function fi(e){if(!e)return null;for(var t,n=0;n-1&&(Hi(a)[d]=u[d],delete u[d])}}}return i}function gi(e,t,n,r){n0?r.slice():Al:r||Al}function ki(e,t){function n(e){return function(){e.apply(null,o)}}var r=Ci(e,t,!1);if(r.length){var i,o=Array.prototype.slice.call(arguments,2);qo?i=qo.delayedCallbacks:Dl?i=Dl:(i=Dl=[],setTimeout(Si,0));for(var l=0;l0}function Ni(e){e.prototype.on=function(e,t){Nl(this,e,t)},e.prototype.off=function(e,t){Ol(this,e,t)}}function Ai(){this.id=null}function Oi(e){for(;Bl.length<=e;)Bl.push(Hi(Bl)+" ");return Bl[e]}function Hi(e){return e[e.length-1]}function Di(e,t){for(var n=0;n-1&&Gl(e)?!0:t.test(e):Gl(e)}function Ri(e){for(var t in e)if(e.hasOwnProperty(t)&&e[t])return!1;return!0}function Bi(e){return e.charCodeAt(0)>=768&&Ul.test(e)}function _i(e,t,n,r){var i=document.createElement(e);if(n&&(i.className=n),r&&(i.style.cssText=r),"string"==typeof t)i.appendChild(document.createTextNode(t));else if(t)for(var o=0;o0;--t)e.removeChild(e.firstChild);return e}function qi(e,t){return ji(e).appendChild(t)}function Gi(){for(var e=document.activeElement;e&&e.root&&e.root.activeElement;)e=e.root.activeElement;return e}function Ui(e){return new RegExp("(^|\\s)"+e+"(?:$|\\s)\\s*")}function $i(e,t){for(var n=e.split(" "),r=0;r2&&!(yo&&8>xo))}var n=Vl?_i("span",""):_i("span"," ",null,"display: inline-block; width: 1px; margin-right: -1px");return n.setAttribute("cm-text",""),n}function Qi(e){if(null!=Kl)return Kl;var t=qi(e,document.createTextNode("AخA")),n=jl(t,0,1).getBoundingClientRect();if(!n||n.left==n.right)return!1;var r=jl(t,1,2).getBoundingClientRect();return Kl=r.right-n.right<3}function Zi(e){if(null!=na)return na;var t=qi(e,_i("span","x")),n=t.getBoundingClientRect(),r=jl(t,0,1).getBoundingClientRect();return na=Math.abs(n.left-r.left)>1}function Ji(e,t,n,r){if(!e)return r(t,n,"ltr");for(var i=!1,o=0;ot||t==n&&l.to==t)&&(r(Math.max(l.from,t),Math.min(l.to,n),1==l.level?"rtl":"ltr"),i=!0)}i||r(t,n,"ltr")}function eo(e){return e.level%2?e.to:e.from}function to(e){return e.level%2?e.from:e.to}function no(e){var t=ri(e);return t?eo(t[0]):0}function ro(e){var t=ri(e);return t?to(Hi(t)):e.text.length}function io(e,t){var n=Yr(e.doc,t),r=vr(n);r!=n&&(t=ei(r));var i=ri(r),o=i?i[0].level%2?ro(r):no(r):0;return Po(t,o)}function oo(e,t){for(var n,r=Yr(e.doc,t);n=mr(r);)r=n.find(1,!0).line,t=null;var i=ri(r),o=i?i[0].level%2?no(r):ro(r):r.text.length;return Po(null==t?ei(r):t,o)}function lo(e,t){var n=io(e,t.line),r=Yr(e.doc,n.line),i=ri(r);if(!i||0==i[0].level){var o=Math.max(0,r.text.search(/\S/)),l=t.line==n.line&&t.ch<=o&&t.ch;return Po(n.line,l?0:o)}return n}function ao(e,t,n){var r=e[0].level;return t==r?!0:n==r?!1:n>t}function so(e,t){ia=null;for(var n,r=0;rt)return r;if(i.from==t||i.to==t){if(null!=n)return ao(e,i.level,e[n].level)?(i.from!=i.to&&(ia=n),r):(i.from!=i.to&&(ia=r),n);n=r}}return n}function co(e,t,n,r){if(!r)return t+n;do t+=n;while(t>0&&Bi(e.text.charAt(t)));return t}function uo(e,t,n,r){var i=ri(e);if(!i)return ho(e,t,n,r);for(var o=so(i,t),l=i[o],a=co(e,t,l.level%2?-n:n,r);;){if(a>l.from&&a0==l.level%2?l.to:l.from);if(l=i[o+=n],!l)return null;a=n>0==l.level%2?co(e,l.to,-1,r):co(e,l.from,1,r)}}function ho(e,t,n,r){var i=t+n;if(r)for(;i>0&&Bi(e.text.charAt(i));)i+=n;return 0>i||i>e.text.length?null:i}var fo=navigator.userAgent,po=navigator.platform,mo=/gecko\/\d/i.test(fo),go=/MSIE \d/.test(fo),vo=/Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(fo),yo=go||vo,xo=yo&&(go?document.documentMode||6:vo[1]),bo=/WebKit\//.test(fo),wo=bo&&/Qt\/\d+\.\d+/.test(fo),Co=/Chrome\//.test(fo),ko=/Opera\//.test(fo),So=/Apple Computer/.test(navigator.vendor),Lo=/Mac OS X 1\d\D([8-9]|\d\d)\D/.test(fo),To=/PhantomJS/.test(fo),Mo=/AppleWebKit/.test(fo)&&/Mobile\/\w+/.test(fo),No=Mo||/Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(fo),Ao=Mo||/Mac/.test(po),Oo=/win/i.test(po),Ho=ko&&fo.match(/Version\/(\d*\.\d*)/);Ho&&(Ho=Number(Ho[1])),Ho&&Ho>=15&&(ko=!1,bo=!0);var Do=Ao&&(wo||ko&&(null==Ho||12.11>Ho)),Wo=mo||yo&&xo>=9,Eo=!1,Io=!1;m.prototype=Pi({update:function(e){var t=e.scrollWidth>e.clientWidth+1,n=e.scrollHeight>e.clientHeight+1,r=e.nativeBarWidth;if(n){this.vert.style.display="block",this.vert.style.bottom=t?r+"px":"0";var i=e.viewHeight-(t?r:0);this.vert.firstChild.style.height=Math.max(0,e.scrollHeight-e.clientHeight+i)+"px"}else this.vert.style.display="",this.vert.firstChild.style.height="0";if(t){this.horiz.style.display="block",this.horiz.style.right=n?r+"px":"0",this.horiz.style.left=e.barLeft+"px";var o=e.viewWidth-e.barLeft-(n?r:0);this.horiz.firstChild.style.width=e.scrollWidth-e.clientWidth+o+"px"}else this.horiz.style.display="",this.horiz.firstChild.style.width="0";return!this.checkedOverlay&&e.clientHeight>0&&(0==r&&this.overlayHack(),this.checkedOverlay=!0),{right:n?r:0,bottom:t?r:0}},setScrollLeft:function(e){this.horiz.scrollLeft!=e&&(this.horiz.scrollLeft=e)},setScrollTop:function(e){this.vert.scrollTop!=e&&(this.vert.scrollTop=e)},overlayHack:function(){var e=Ao&&!Lo?"12px":"18px";this.horiz.style.minHeight=this.vert.style.minWidth=e;var t=this,n=function(e){bi(e)!=t.vert&&bi(e)!=t.horiz&&At(t.cm,$t)(e)};Nl(this.vert,"mousedown",n),Nl(this.horiz,"mousedown",n)},clear:function(){var e=this.horiz.parentNode;e.removeChild(this.horiz),e.removeChild(this.vert)}},m.prototype),g.prototype=Pi({update:function(){return{bottom:0,right:0}},setScrollLeft:function(){},setScrollTop:function(){},clear:function(){}},g.prototype),e.scrollbarModel={"native":m,"null":g},L.prototype.signal=function(e,t){Mi(e,t)&&this.events.push(arguments)},L.prototype.finish=function(){for(var e=0;e=9&&n.hasSelection&&(n.hasSelection=null),n.poll()}),Nl(o,"paste",function(e){return J(e,r)?!0:(r.state.pasteIncoming=!0,void n.fastPoll())}),Nl(o,"cut",t),Nl(o,"copy",t),Nl(e.scroller,"paste",function(t){Gt(e,t)||(r.state.pasteIncoming=!0,n.focus())}),Nl(e.lineSpace,"selectstart",function(t){Gt(e,t)||Ll(t)}),Nl(o,"compositionstart",function(){var e=r.getCursor("from");n.composing&&n.composing.range.clear(),n.composing={start:e,range:r.markText(e,r.getCursor("to"),{className:"CodeMirror-composing"})}}),Nl(o,"compositionend",function(){n.composing&&(n.poll(),n.composing.range.clear(),n.composing=null)})},prepareSelection:function(){var e=this.cm,t=e.display,n=e.doc,r=Ee(e);if(e.options.moveInputWithCursor){var i=ht(e,n.sel.primary().head,"div"),o=t.wrapper.getBoundingClientRect(),l=t.lineDiv.getBoundingClientRect();r.teTop=Math.max(0,Math.min(t.wrapper.clientHeight-10,i.top+l.top-o.top)),r.teLeft=Math.max(0,Math.min(t.wrapper.clientWidth-10,i.left+l.left-o.left))}return r},showSelection:function(e){var t=this.cm,n=t.display;qi(n.cursorDiv,e.cursors),qi(n.selectionDiv,e.selection),null!=e.teTop&&(this.wrapper.style.top=e.teTop+"px",this.wrapper.style.left=e.teLeft+"px")},reset:function(e){if(!this.contextMenuPending){var t,n,r=this.cm,i=r.doc;if(r.somethingSelected()){this.prevInput="";var o=i.sel.primary();t=ta&&(o.to().line-o.from().line>100||(n=r.getSelection()).length>1e3);var l=t?"-":n||r.getSelection();this.textarea.value=l,r.state.focused&&_l(this.textarea),yo&&xo>=9&&(this.hasSelection=l)}else e||(this.prevInput=this.textarea.value="",yo&&xo>=9&&(this.hasSelection=null));this.inaccurateSelection=t}},getField:function(){return this.textarea},supportsTouch:function(){return!1},focus:function(){if("nocursor"!=this.cm.options.readOnly&&(!No||Gi()!=this.textarea))try{this.textarea.focus()}catch(e){}},blur:function(){this.textarea.blur()},resetPosition:function(){this.wrapper.style.top=this.wrapper.style.left=0},receivedFocus:function(){this.slowPoll()},slowPoll:function(){var e=this;e.pollingFast||e.polling.set(this.cm.options.pollInterval,function(){e.poll(),e.cm.state.focused&&e.slowPoll()})},fastPoll:function(){function e(){var r=n.poll();r||t?(n.pollingFast=!1,n.slowPoll()):(t=!0,n.polling.set(60,e))}var t=!1,n=this;n.pollingFast=!0,n.polling.set(20,e)},poll:function(){var e=this.cm,t=this.textarea,n=this.prevInput;if(this.contextMenuPending||!e.state.focused||ea(t)&&!n&&!this.composing||Q(e)||e.options.disableInput||e.state.keySeq)return!1;var r=t.value;if(r==n&&!e.somethingSelected())return!1;if(yo&&xo>=9&&this.hasSelection===r||Ao&&/[\uf700-\uf7ff]/.test(r))return e.display.input.reset(),!1;if(e.doc.sel==e.display.selForContextMenu){var i=r.charCodeAt(0);if(8203!=i||n||(n=""),8666==i)return this.reset(),this.cm.execCommand("undo")}for(var o=0,l=Math.min(n.length,r.length);l>o&&n.charCodeAt(o)==r.charCodeAt(o);)++o;var a=this;return Nt(e,function(){Z(e,r.slice(o),n.length-o,null,a.composing?"*compose":null),r.length>1e3||r.indexOf("\n")>-1?t.value=a.prevInput="":a.prevInput=r,a.composing&&(a.composing.range.clear(),a.composing.range=e.markText(a.composing.start,e.getCursor("to"),{className:"CodeMirror-composing"}))}),!0},ensurePolled:function(){this.pollingFast&&this.poll()&&(this.pollingFast=!1)},onKeyPress:function(){yo&&xo>=9&&(this.hasSelection=null),this.fastPoll()},onContextMenu:function(e){function t(){if(null!=l.selectionStart){var e=i.somethingSelected(),t=""+(e?l.value:"");l.value="⇚",l.value=t,r.prevInput=e?"":"",l.selectionStart=1,l.selectionEnd=t.length,o.selForContextMenu=i.doc.sel}}function n(){if(r.contextMenuPending=!1,r.wrapper.style.position="relative",l.style.cssText=u,yo&&9>xo&&o.scrollbars.setScrollTop(o.scroller.scrollTop=s),null!=l.selectionStart){(!yo||yo&&9>xo)&&t();var e=0,n=function(){o.selForContextMenu==i.doc.sel&&0==l.selectionStart&&l.selectionEnd>0&&""==r.prevInput?At(i,sl.selectAll)(i):e++<10?o.detectingSelectAll=setTimeout(n,500):o.input.reset()};o.detectingSelectAll=setTimeout(n,200)}}var r=this,i=r.cm,o=i.display,l=r.textarea,a=Ut(i,e),s=o.scroller.scrollTop;if(a&&!ko){var c=i.options.resetSelectionOnContextMenu;c&&-1==i.doc.sel.contains(a)&&At(i,Me)(i.doc,pe(a),Il);var u=l.style.cssText;if(r.wrapper.style.position="absolute",l.style.cssText="position: fixed; width: 30px; height: 30px; top: "+(e.clientY-5)+"px; left: "+(e.clientX-5)+"px; z-index: 1000; background: "+(yo?"rgba(255, 255, 255, .05)":"transparent")+"; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);",bo)var d=window.scrollY;if(o.input.focus(),bo&&window.scrollTo(null,d),o.input.reset(),i.somethingSelected()||(l.value=r.prevInput=" "),r.contextMenuPending=!0,o.selForContextMenu=i.doc.sel,clearTimeout(o.detectingSelectAll),yo&&xo>=9&&t(),Wo){Ml(e);var h=function(){Ol(window,"mouseup",h),setTimeout(n,20)};Nl(window,"mouseup",h)}else setTimeout(n,50)}},readOnlyChanged:function(e){e||this.reset()},setUneditable:Ei,needsContentAttribute:!1},re.prototype),oe.prototype=Pi({init:function(e){function t(e){if(r.somethingSelected())zo=r.getSelections(),"cut"==e.type&&r.replaceSelection("",null,"cut");else{if(!r.options.lineWiseCopyCut)return;var t=te(r);zo=t.text,"cut"==e.type&&r.operation(function(){r.setSelections(t.ranges,0,Il),r.replaceSelection("",null,"cut")})}if(e.clipboardData&&!Mo)e.preventDefault(),e.clipboardData.clearData(),e.clipboardData.setData("text/plain",zo.join("\n"));else{var n=ie(),i=n.firstChild;r.display.lineSpace.insertBefore(n,r.display.lineSpace.firstChild),i.value=zo.join("\n");var o=document.activeElement;_l(i),setTimeout(function(){r.display.lineSpace.removeChild(n),o.focus()},50)}}var n=this,r=n.cm,i=n.div=e.lineDiv;ne(i),Nl(i,"paste",function(e){J(e,r)}),Nl(i,"compositionstart",function(e){var t=e.data;if(n.composing={sel:r.doc.sel,data:t,startData:t},t){var i=r.doc.sel.primary(),o=r.getLine(i.head.line),l=o.indexOf(t,Math.max(0,i.head.ch-t.length));l>-1&&l<=i.head.ch&&(n.composing.sel=pe(Po(i.head.line,l),Po(i.head.line,l+t.length)))}}),Nl(i,"compositionupdate",function(e){n.composing.data=e.data}),Nl(i,"compositionend",function(e){var t=n.composing;t&&(e.data==t.startData||/\u200b/.test(e.data)||(t.data=e.data),setTimeout(function(){t.handled||n.applyComposition(t),n.composing==t&&(n.composing=null)},50))}),Nl(i,"touchstart",function(){n.forceCompositionEnd()}),Nl(i,"input",function(){n.composing||(Q(r)||!n.pollContent())&&Nt(n.cm,function(){Et(r)})}),Nl(i,"copy",t),Nl(i,"cut",t)},prepareSelection:function(){var e=Ee(this.cm,!1);return e.focus=this.cm.state.focused,e},showSelection:function(e){e&&this.cm.display.view.length&&(e.focus&&this.showPrimarySelection(),this.showMultipleSelections(e))},showPrimarySelection:function(){var e=window.getSelection(),t=this.cm.doc.sel.primary(),n=se(this.cm,e.anchorNode,e.anchorOffset),r=se(this.cm,e.focusNode,e.focusOffset);if(!n||n.bad||!r||r.bad||0!=Fo(X(n,r),t.from())||0!=Fo(K(n,r),t.to())){var i=le(this.cm,t.from()),o=le(this.cm,t.to());if(i||o){var l=this.cm.display.view,a=e.rangeCount&&e.getRangeAt(0);if(i){if(!o){var s=l[l.length-1].measure,c=s.maps?s.maps[s.maps.length-1]:s.map;o={node:c[c.length-1],offset:c[c.length-2]-c[c.length-3]}}}else i={node:l[0].measure.map[2],offset:0};try{var u=jl(i.node,i.offset,o.offset,o.node)}catch(d){}u&&(e.removeAllRanges(),e.addRange(u),a&&null==e.anchorNode?e.addRange(a):mo&&this.startGracePeriod()),this.rememberSelection()}}},startGracePeriod:function(){var e=this;clearTimeout(this.gracePeriod),this.gracePeriod=setTimeout(function(){e.gracePeriod=!1,e.selectionChanged()&&e.cm.operation(function(){e.cm.curOp.selectionChanged=!0})},20)},showMultipleSelections:function(e){qi(this.cm.display.cursorDiv,e.cursors),qi(this.cm.display.selectionDiv,e.selection)},rememberSelection:function(){var e=window.getSelection();this.lastAnchorNode=e.anchorNode,this.lastAnchorOffset=e.anchorOffset,this.lastFocusNode=e.focusNode,this.lastFocusOffset=e.focusOffset},selectionInEditor:function(){var e=window.getSelection();if(!e.rangeCount)return!1;var t=e.getRangeAt(0).commonAncestorContainer;return $l(this.div,t)},focus:function(){"nocursor"!=this.cm.options.readOnly&&this.div.focus()},blur:function(){this.div.blur()},getField:function(){return this.div},supportsTouch:function(){return!0},receivedFocus:function(){function e(){t.cm.state.focused&&(t.pollSelection(),t.polling.set(t.cm.options.pollInterval,e))}var t=this;this.selectionInEditor()?this.pollSelection():Nt(this.cm,function(){t.cm.curOp.selectionChanged=!0}),this.polling.set(this.cm.options.pollInterval,e)},selectionChanged:function(){var e=window.getSelection();return e.anchorNode!=this.lastAnchorNode||e.anchorOffset!=this.lastAnchorOffset||e.focusNode!=this.lastFocusNode||e.focusOffset!=this.lastFocusOffset},pollSelection:function(){if(!this.composing&&!this.gracePeriod&&this.selectionChanged()){var e=window.getSelection(),t=this.cm;this.rememberSelection();var n=se(t,e.anchorNode,e.anchorOffset),r=se(t,e.focusNode,e.focusOffset);n&&r&&Nt(t,function(){Me(t.doc,pe(n,r),Il),(n.bad||r.bad)&&(t.curOp.selectionChanged=!0)})}},pollContent:function(){var e=this.cm,t=e.display,n=e.doc.sel.primary(),r=n.from(),i=n.to();if(r.linet.viewTo-1)return!1;var o;if(r.line==t.viewFrom||0==(o=Ft(e,r.line)))var l=ei(t.view[0].line),a=t.view[0].node;else var l=ei(t.view[o].line),a=t.view[o-1].node.nextSibling;var s=Ft(e,i.line);if(s==t.view.length-1)var c=t.viewTo-1,u=t.lineDiv.lastChild;else var c=ei(t.view[s+1].line)-1,u=t.view[s+1].node.previousSibling;for(var d=e.doc.splitLines(ue(e,a,u,l,c)),h=Qr(e.doc,Po(l,0),Po(c,Yr(e.doc,c).text.length));d.length>1&&h.length>1;)if(Hi(d)==Hi(h))d.pop(),h.pop(),c--;else{if(d[0]!=h[0])break;d.shift(),h.shift(),l++}for(var f=0,p=0,m=d[0],g=h[0],v=Math.min(m.length,g.length);v>f&&m.charCodeAt(f)==g.charCodeAt(f);)++f;for(var y=Hi(d),x=Hi(h),b=Math.min(y.length-(1==d.length?f:0),x.length-(1==h.length?f:0));b>p&&y.charCodeAt(y.length-p-1)==x.charCodeAt(x.length-p-1);)++p;d[d.length-1]=y.slice(0,y.length-p),d[0]=d[0].slice(f);var w=Po(l,f),C=Po(c,h.length?Hi(h).length-p:0);return d.length>1||d[0]||Fo(w,C)?(Hn(e.doc,d,w,C,"+input"),!0):void 0},ensurePolled:function(){this.forceCompositionEnd()},reset:function(){this.forceCompositionEnd()},forceCompositionEnd:function(){this.composing&&!this.composing.handled&&(this.applyComposition(this.composing),this.composing.handled=!0,this.div.blur(),this.div.focus())},applyComposition:function(e){Q(this.cm)?At(this.cm,Et)(this.cm):e.data&&e.data!=e.startData&&At(this.cm,Z)(this.cm,e.data,0,e.sel)},setUneditable:function(e){e.contentEditable="false"},onKeyPress:function(e){e.preventDefault(),Q(this.cm)||At(this.cm,Z)(this.cm,String.fromCharCode(null==e.charCode?e.keyCode:e.charCode),0)},readOnlyChanged:function(e){this.div.contentEditable=String("nocursor"!=e)},onContextMenu:Ei,resetPosition:Ei,needsContentAttribute:!0},oe.prototype),e.inputStyles={textarea:re,contenteditable:oe},de.prototype={primary:function(){return this.ranges[this.primIndex]},equals:function(e){if(e==this)return!0;if(e.primIndex!=this.primIndex||e.ranges.length!=this.ranges.length)return!1;for(var t=0;t=0&&Fo(e,r.to())<=0)return n}return-1}},he.prototype={from:function(){return X(this.anchor,this.head)},to:function(){return K(this.anchor,this.head)},empty:function(){return this.head.line==this.anchor.line&&this.head.ch==this.anchor.ch}};var Ro,Bo,_o,jo={left:0,right:0,top:0,bottom:0},qo=null,Go=0,Uo=0,$o=0,Vo=null;yo?Vo=-.53:mo?Vo=15:Co?Vo=-.7:So&&(Vo=-1/3);var Ko=function(e){var t=e.wheelDeltaX,n=e.wheelDeltaY;return null==t&&e.detail&&e.axis==e.HORIZONTAL_AXIS&&(t=e.detail),null==n&&e.detail&&e.axis==e.VERTICAL_AXIS?n=e.detail:null==n&&(n=e.wheelDelta),{x:t,y:n}};e.wheelEventPixels=function(e){var t=Ko(e);return t.x*=Vo,t.y*=Vo,t};var Xo=new Ai,Yo=null,Qo=e.changeEnd=function(e){return e.text?Po(e.from.line+e.text.length-1,Hi(e.text).length+(1==e.text.length?e.from.ch:0)):e.to};e.prototype={constructor:e,focus:function(){window.focus(),this.display.input.focus()},setOption:function(e,t){var n=this.options,r=n[e];(n[e]!=t||"mode"==e)&&(n[e]=t,Jo.hasOwnProperty(e)&&At(this,Jo[e])(this,t,r))},getOption:function(e){return this.options[e]},getDoc:function(){return this.doc},addKeyMap:function(e,t){this.state.keyMaps[t?"push":"unshift"]($n(e))},removeKeyMap:function(e){for(var t=this.state.keyMaps,n=0;nn&&(Rn(this,i.head.line,e,!0),n=i.head.line,r==this.doc.sel.primIndex&&Fn(this));else{var o=i.from(),l=i.to(),a=Math.max(n,o.line);n=Math.min(this.lastLine(),l.line-(l.ch?0:1))+1;for(var s=a;n>s;++s)Rn(this,s,e);var c=this.doc.sel.ranges;0==o.ch&&t.length==c.length&&c[r].from().ch>0&&ke(this.doc,r,new he(o,c[r].to()),Il)}}}),getTokenAt:function(e,t){return Hr(this,e,t)},getLineTokens:function(e,t){return Hr(this,Po(e),t,!0)},getTokenTypeAt:function(e){e=ge(this.doc,e);var t,n=Er(this,Yr(this.doc,e.line)),r=0,i=(n.length-1)/2,o=e.ch;if(0==o)t=n[2];else for(;;){var l=r+i>>1;if((l?n[2*l-1]:0)>=o)i=l;else{if(!(n[2*l+1]a?t:0==a?null:t.slice(0,a-1)},getModeAt:function(t){var n=this.doc.mode;return n.innerMode?e.innerMode(n,this.getTokenAt(t).state).mode:n},getHelper:function(e,t){return this.getHelpers(e,t)[0]},getHelpers:function(e,t){var n=[];if(!ol.hasOwnProperty(t))return n;var r=ol[t],i=this.getModeAt(e);if("string"==typeof i[t])r[i[t]]&&n.push(r[i[t]]);else if(i[t])for(var o=0;oi&&(e=i,r=!0),n=Yr(this.doc,e)}else n=e;return ct(this,n,{top:0,left:0},t||"page").top+(r?this.doc.height-ni(n):0)},defaultTextHeight:function(){return vt(this.display)},defaultCharWidth:function(){return yt(this.display)},setGutterMarker:Ot(function(e,t,n){return Bn(this.doc,e,"gutter",function(e){var r=e.gutterMarkers||(e.gutterMarkers={});return r[t]=n,!n&&Ri(r)&&(e.gutterMarkers=null),!0})}),clearGutter:Ot(function(e){var t=this,n=t.doc,r=n.first;n.iter(function(n){n.gutterMarkers&&n.gutterMarkers[e]&&(n.gutterMarkers[e]=null,It(t,r,"gutter"),Ri(n.gutterMarkers)&&(n.gutterMarkers=null)),++r})}),lineInfo:function(e){if("number"==typeof e){if(!ye(this.doc,e))return null;var t=e;if(e=Yr(this.doc,e),!e)return null}else{var t=ei(e);if(null==t)return null}return{line:t,handle:e,text:e.text,gutterMarkers:e.gutterMarkers,textClass:e.textClass,bgClass:e.bgClass,wrapClass:e.wrapClass,widgets:e.widgets}},getViewport:function(){return{from:this.display.viewFrom,to:this.display.viewTo}},addWidget:function(e,t,n,r,i){var o=this.display;e=ht(this,ge(this.doc,e));var l=e.bottom,a=e.left;if(t.style.position="absolute",t.setAttribute("cm-ignore-events","true"),this.display.input.setUneditable(t),o.sizer.appendChild(t),"over"==r)l=e.top;else if("above"==r||"near"==r){var s=Math.max(o.wrapper.clientHeight,this.doc.height),c=Math.max(o.sizer.clientWidth,o.lineSpace.clientWidth);("above"==r||e.bottom+t.offsetHeight>s)&&e.top>t.offsetHeight?l=e.top-t.offsetHeight:e.bottom+t.offsetHeight<=s&&(l=e.bottom),a+t.offsetWidth>c&&(a=c-t.offsetWidth)}t.style.top=l+"px",t.style.left=t.style.right="","right"==i?(a=o.sizer.clientWidth-t.offsetWidth,t.style.right="0px"):("left"==i?a=0:"middle"==i&&(a=(o.sizer.clientWidth-t.offsetWidth)/2),t.style.left=a+"px"),n&&En(this,a,l,a+t.offsetWidth,l+t.offsetHeight)},triggerOnKeyDown:Ot(dn),triggerOnKeyPress:Ot(pn),triggerOnKeyUp:fn,execCommand:function(e){return sl.hasOwnProperty(e)?sl[e].call(null,this):void 0},triggerElectric:Ot(function(e){ee(this,e)}),findPosH:function(e,t,n,r){var i=1;0>t&&(i=-1,t=-t);for(var o=0,l=ge(this.doc,e);t>o&&(l=jn(this.doc,l,i,n,r),!l.hitSide);++o);return l},moveH:Ot(function(e,t){var n=this;n.extendSelectionsBy(function(r){return n.display.shift||n.doc.extend||r.empty()?jn(n.doc,r.head,e,t,n.options.rtlMoveVisually):0>e?r.from():r.to()},Fl)}),deleteH:Ot(function(e,t){var n=this.doc.sel,r=this.doc;n.somethingSelected()?r.replaceSelection("",null,"+delete"):_n(this,function(n){var i=jn(r,n.head,e,t,!1);return 0>e?{from:i,to:n.head}:{from:n.head,to:i}})}),findPosV:function(e,t,n,r){var i=1,o=r;0>t&&(i=-1,t=-t);for(var l=0,a=ge(this.doc,e);t>l;++l){var s=ht(this,a,"div");if(null==o?o=s.left:s.left=o,a=qn(this,s,i,n),a.hitSide)break}return a},moveV:Ot(function(e,t){var n=this,r=this.doc,i=[],o=!n.display.shift&&!r.extend&&r.sel.somethingSelected();if(r.extendSelectionsBy(function(l){if(o)return 0>e?l.from():l.to();var a=ht(n,l.head,"div");null!=l.goalColumn&&(a.left=l.goalColumn),i.push(a.left);var s=qn(n,a,e,t);return"page"==t&&l==r.sel.primary()&&Pn(n,null,dt(n,s,"div").top-a.top),s},Fl),i.length)for(var l=0;l0&&a(n.charAt(r-1));)--r;for(;i.5)&&l(this),Hl(this,"refresh",this)}),swapDoc:Ot(function(e){var t=this.doc;return t.cm=null,Xr(this,e),lt(this),this.display.input.reset(),this.scrollTo(e.scrollLeft,e.scrollTop),this.curOp.forceScroll=!0,ki(this,"swapDoc",this,t),t}),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}},Ni(e);var Zo=e.defaults={},Jo=e.optionHandlers={},el=e.Init={toString:function(){return"CodeMirror.Init"}};Gn("value","",function(e,t){e.setValue(t)},!0),Gn("mode",null,function(e,t){e.doc.modeOption=t,n(e)},!0),Gn("indentUnit",2,n,!0),Gn("indentWithTabs",!1),Gn("smartIndent",!0),Gn("tabSize",4,function(e){r(e),lt(e),Et(e)},!0),Gn("lineSeparator",null,function(e,t){if(e.doc.lineSep=t,t){var n=[],r=e.doc.first;e.doc.iter(function(e){for(var i=0;;){var o=e.text.indexOf(t,i);if(-1==o)break;i=o+t.length,n.push(Po(r,o))}r++});for(var i=n.length-1;i>=0;i--)Hn(e.doc,t,n[i],Po(n[i].line,n[i].ch+t.length))}}),Gn("specialChars",/[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g,function(t,n,r){t.state.specialChars=new RegExp(n.source+(n.test(" ")?"":"| "),"g"),r!=e.Init&&t.refresh()}),Gn("specialCharPlaceholder",zr,function(e){e.refresh()},!0),Gn("electricChars",!0),Gn("inputStyle",No?"contenteditable":"textarea",function(){throw new Error("inputStyle can not (yet) be changed in a running editor")},!0),Gn("rtlMoveVisually",!Oo),Gn("wholeLineUpdateBefore",!0),Gn("theme","default",function(e){a(e),s(e)},!0),Gn("keyMap","default",function(t,n,r){var i=$n(n),o=r!=e.Init&&$n(r);o&&o.detach&&o.detach(t,i),i.attach&&i.attach(t,o||null)}),Gn("extraKeys",null),Gn("lineWrapping",!1,i,!0),Gn("gutters",[],function(e){f(e.options),s(e)},!0),Gn("fixedGutter",!0,function(e,t){e.display.gutters.style.left=t?S(e.display)+"px":"0",e.refresh()},!0),Gn("coverGutterNextToScrollbar",!1,function(e){y(e)},!0),Gn("scrollbarStyle","native",function(e){v(e),y(e),e.display.scrollbars.setScrollTop(e.doc.scrollTop),e.display.scrollbars.setScrollLeft(e.doc.scrollLeft)},!0),Gn("lineNumbers",!1,function(e){f(e.options),s(e)},!0),Gn("firstLineNumber",1,s,!0),Gn("lineNumberFormatter",function(e){return e},s,!0),Gn("showCursorWhenSelecting",!1,We,!0),Gn("resetSelectionOnContextMenu",!0),Gn("lineWiseCopyCut",!0),Gn("readOnly",!1,function(e,t){"nocursor"==t?(vn(e),e.display.input.blur(),e.display.disabled=!0):e.display.disabled=!1,e.display.input.readOnlyChanged(t)}),Gn("disableInput",!1,function(e,t){t||e.display.input.reset()},!0),Gn("dragDrop",!0,jt),Gn("allowDropFileTypes",null),Gn("cursorBlinkRate",530),Gn("cursorScrollMargin",0),Gn("cursorHeight",1,We,!0),Gn("singleCursorHeightPerLine",!0,We,!0),Gn("workTime",100),Gn("workDelay",100),Gn("flattenSpans",!0,r,!0),Gn("addModeClass",!1,r,!0),Gn("pollInterval",100),Gn("undoDepth",200,function(e,t){e.doc.history.undoDepth=t}),Gn("historyEventDelay",1250),Gn("viewportMargin",10,function(e){e.refresh()},!0),Gn("maxHighlightLength",1e4,r,!0),Gn("moveInputWithCursor",!0,function(e,t){t||e.display.input.resetPosition()}),Gn("tabindex",null,function(e,t){e.display.input.getField().tabIndex=t||""}),Gn("autofocus",null);var tl=e.modes={},nl=e.mimeModes={};e.defineMode=function(t,n){e.defaults.mode||"null"==t||(e.defaults.mode=t),arguments.length>2&&(n.dependencies=Array.prototype.slice.call(arguments,2)),tl[t]=n},e.defineMIME=function(e,t){nl[e]=t},e.resolveMode=function(t){if("string"==typeof t&&nl.hasOwnProperty(t))t=nl[t];else if(t&&"string"==typeof t.name&&nl.hasOwnProperty(t.name)){var n=nl[t.name];"string"==typeof n&&(n={name:n}),t=Ii(n,t),t.name=n.name}else if("string"==typeof t&&/^[\w\-]+\/[\w\-]+\+xml$/.test(t))return e.resolveMode("application/xml");return"string"==typeof t?{name:t}:t||{name:"null"}},e.getMode=function(t,n){var n=e.resolveMode(n),r=tl[n.name];if(!r)return e.getMode(t,"text/plain");var i=r(t,n);if(rl.hasOwnProperty(n.name)){var o=rl[n.name];for(var l in o)o.hasOwnProperty(l)&&(i.hasOwnProperty(l)&&(i["_"+l]=i[l]),i[l]=o[l])}if(i.name=n.name,n.helperType&&(i.helperType=n.helperType),n.modeProps)for(var l in n.modeProps)i[l]=n.modeProps[l];return i},e.defineMode("null",function(){return{token:function(e){e.skipToEnd()}}}),e.defineMIME("text/plain","null");var rl=e.modeExtensions={};e.extendMode=function(e,t){var n=rl.hasOwnProperty(e)?rl[e]:rl[e]={};Pi(t,n)},e.defineExtension=function(t,n){e.prototype[t]=n},e.defineDocExtension=function(e,t){Cl.prototype[e]=t},e.defineOption=Gn;var il=[];e.defineInitHook=function(e){il.push(e)};var ol=e.helpers={};e.registerHelper=function(t,n,r){ol.hasOwnProperty(t)||(ol[t]=e[t]={_global:[]}),ol[t][n]=r},e.registerGlobalHelper=function(t,n,r,i){e.registerHelper(t,n,i),ol[t]._global.push({pred:r,val:i})};var ll=e.copyState=function(e,t){if(t===!0)return t;if(e.copyState)return e.copyState(t);var n={};for(var r in t){var i=t[r];i instanceof Array&&(i=i.concat([])),n[r]=i}return n},al=e.startState=function(e,t,n){return e.startState?e.startState(t,n):!0};e.innerMode=function(e,t){for(;e.innerMode;){var n=e.innerMode(t);if(!n||n.mode==e)break;t=n.state,e=n.mode}return n||{mode:e,state:t}};var sl=e.commands={selectAll:function(e){e.setSelection(Po(e.firstLine(),0),Po(e.lastLine()),Il)},singleSelection:function(e){e.setSelection(e.getCursor("anchor"),e.getCursor("head"),Il)},killLine:function(e){_n(e,function(t){if(t.empty()){var n=Yr(e.doc,t.head.line).text.length;return t.head.ch==n&&t.head.line0)i=new Po(i.line,i.ch+1),e.replaceRange(o.charAt(i.ch-1)+o.charAt(i.ch-2),Po(i.line,i.ch-2),i,"+transpose");else if(i.line>e.doc.first){var l=Yr(e.doc,i.line-1).text;l&&e.replaceRange(o.charAt(0)+e.doc.lineSeparator()+l.charAt(l.length-1),Po(i.line-1,l.length-1),Po(i.line,1),"+transpose")}n.push(new he(i,i))}e.setSelections(n)})},newlineAndIndent:function(e){Nt(e,function(){for(var t=e.listSelections().length,n=0;t>n;n++){var r=e.listSelections()[n];e.replaceRange(e.doc.lineSeparator(),r.anchor,r.head,"+input"),e.indentLine(r.from().line+1,null,!0)}Fn(e)})},toggleOverwrite:function(e){e.toggleOverwrite()}},cl=e.keyMap={};cl.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"},cl.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"},cl.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"},cl.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"]},cl["default"]=Ao?cl.macDefault:cl.pcDefault,e.normalizeKeyMap=function(e){var t={};for(var n in e)if(e.hasOwnProperty(n)){var r=e[n];if(/^(name|fallthrough|(de|at)tach)$/.test(n))continue;if("..."==r){delete e[n];continue}for(var i=Wi(n.split(" "),Un),o=0;o=this.string.length},sol:function(){return this.pos==this.lineStart},peek:function(){return this.string.charAt(this.pos)||void 0},next:function(){return this.post},eatSpace:function(){for(var e=this.pos;/[\s\u00a0]/.test(this.string.charAt(this.pos));)++this.pos;return this.pos>e},skipToEnd:function(){this.pos=this.string.length},skipTo:function(e){var t=this.string.indexOf(e,this.pos);return t>-1?(this.pos=t,!0):void 0},backUp:function(e){this.pos-=e},column:function(){return this.lastColumnPos0?null:(r&&t!==!1&&(this.pos+=r[0].length),r)}var i=function(e){return n?e.toLowerCase():e},o=this.string.substr(this.pos,e.length);return i(o)==i(e)?(t!==!1&&(this.pos+=e.length),!0):void 0},current:function(){return this.string.slice(this.start,this.pos)},hideFirstChars:function(e,t){this.lineStart+=e;try{return t()}finally{this.lineStart-=e}}};var pl=0,ml=e.TextMarker=function(e,t){this.lines=[],this.type=t,this.doc=e,this.id=++pl};Ni(ml),ml.prototype.clear=function(){if(!this.explicitlyCleared){var e=this.doc.cm,t=e&&!e.curOp;if(t&&xt(e),Mi(this,"clear")){var n=this.find();n&&ki(this,"clear",n.from,n.to)}for(var r=null,i=null,o=0;oe.display.maxLineLength&&(e.display.maxLine=s,e.display.maxLineLength=c,e.display.maxLineChanged=!0)}null!=r&&e&&this.collapsed&&Et(e,r,i+1),this.lines.length=0,this.explicitlyCleared=!0,this.atomic&&this.doc.cantEdit&&(this.doc.cantEdit=!1,e&&Oe(e.doc)),e&&ki(e,"markerCleared",e,this),t&&wt(e),this.parent&&this.parent.clear()}},ml.prototype.find=function(e,t){null==e&&"bookmark"==this.type&&(e=1);for(var n,r,i=0;in;++n){var i=this.lines[n];this.height-=i.height,Mr(i),ki(i,"delete")}this.lines.splice(e,t)},collapse:function(e){e.push.apply(e,this.lines)},insertInner:function(e,t,n){this.height+=n,this.lines=this.lines.slice(0,e).concat(t).concat(this.lines.slice(e));for(var r=0;re;++e)if(n(this.lines[e]))return!0}},Vr.prototype={chunkSize:function(){return this.size},removeInner:function(e,t){this.size-=t;for(var n=0;ne){var o=Math.min(t,i-e),l=r.height;if(r.removeInner(e,o),this.height-=l-r.height,i==o&&(this.children.splice(n--,1),r.parent=null),0==(t-=o))break;e=0}else e-=i}if(this.size-t<25&&(this.children.length>1||!(this.children[0]instanceof $r))){var a=[];this.collapse(a),this.children=[new $r(a)],this.children[0].parent=this}},collapse:function(e){for(var t=0;t=e){if(i.insertInner(e,t,n),i.lines&&i.lines.length>50){for(;i.lines.length>50;){var l=i.lines.splice(i.lines.length-25,25),a=new $r(l);i.height-=a.height,this.children.splice(r+1,0,a),a.parent=this}this.maybeSpill()}break}e-=o}},maybeSpill:function(){if(!(this.children.length<=10)){var e=this;do{var t=e.children.splice(e.children.length-5,5),n=new Vr(t);if(e.parent){e.size-=n.size,e.height-=n.height;var r=Di(e.parent.children,e);e.parent.children.splice(r+1,0,n)}else{var i=new Vr(e.children);i.parent=e,e.children=[i,n],e=i}n.parent=e.parent}while(e.children.length>10);e.parent.maybeSpill()}},iterN:function(e,t,n){for(var r=0;re){var l=Math.min(t,o-e);if(i.iterN(e,l,n))return!0;if(0==(t-=l))break;e=0}else e-=o}}};var wl=0,Cl=e.Doc=function(e,t,n,r){if(!(this instanceof Cl))return new Cl(e,t,n,r);null==n&&(n=0),Vr.call(this,[new $r([new yl("",null)])]),this.first=n,this.scrollTop=this.scrollLeft=0,this.cantEdit=!1,this.cleanGeneration=1,this.frontier=n;var i=Po(n,0);this.sel=pe(i),this.history=new ii(null),this.id=++wl,this.modeOption=t,this.lineSep=r,"string"==typeof e&&(e=this.splitLines(e)),Ur(this,{from:i,to:i,text:e}),Me(this,pe(i),Il)};Cl.prototype=Ii(Vr.prototype,{constructor:Cl,iter:function(e,t,n){n?this.iterN(e-this.first,t-e,n):this.iterN(this.first,this.first+this.size,e)},insert:function(e,t){for(var n=0,r=0;r=0;o--)Ln(this,r[o]);a?Te(this,a):this.cm&&Fn(this.cm)}),undo:Ht(function(){Mn(this,"undo")}),redo:Ht(function(){Mn(this,"redo")}),undoSelection:Ht(function(){Mn(this,"undo",!0)}),redoSelection:Ht(function(){Mn(this,"redo",!0)}),setExtending:function(e){this.extend=e},getExtending:function(){return this.extend},historySize:function(){for(var e=this.history,t=0,n=0,r=0;r=e.ch)&&t.push(i.marker.parent||i.marker)}return t},findMarks:function(e,t,n){e=ge(this,e),t=ge(this,t);var r=[],i=e.line;return this.iter(e.line,t.line+1,function(o){var l=o.markedSpans;if(l)for(var a=0;as.to||null==s.from&&i!=e.line||i==t.line&&s.from>t.ch||n&&!n(s.marker)||r.push(s.marker.parent||s.marker)}++i}),r},getAllMarks:function(){var e=[];return this.iter(function(t){var n=t.markedSpans;if(n)for(var r=0;re?(t=e,!0):(e-=i,void++n)}),ge(this,Po(n,t))},indexFromPos:function(e){e=ge(this,e);var t=e.ch;return e.linet&&(t=e.from),null!=e.to&&e.toa||a>=t)return l+(t-o);l+=a-o,l+=n-l%n,o=a+1}},Rl=e.findColumn=function(e,t,n){for(var r=0,i=0;;){var o=e.indexOf(" ",r);-1==o&&(o=e.length);var l=o-r;if(o==e.length||i+l>=t)return r+Math.min(l,t-i);if(i+=o-r,i+=n-i%n,r=o+1,i>=t)return r}},Bl=[""],_l=function(e){e.select()};Mo?_l=function(e){e.selectionStart=0,e.selectionEnd=e.value.length}:yo&&(_l=function(e){try{e.select()}catch(t){}});var jl,ql=/[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/,Gl=e.isWordChar=function(e){return/\w/.test(e)||e>""&&(e.toUpperCase()!=e.toLowerCase()||ql.test(e))},Ul=/[\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]/;
+jl=document.createRange?function(e,t,n,r){var i=document.createRange();return i.setEnd(r||e,n),i.setStart(e,t),i}:function(e,t,n){var r=document.body.createTextRange();try{r.moveToElementText(e.parentNode)}catch(i){return r}return r.collapse(!0),r.moveEnd("character",n),r.moveStart("character",t),r};var $l=e.contains=function(e,t){if(3==t.nodeType&&(t=t.parentNode),e.contains)return e.contains(t);do if(11==t.nodeType&&(t=t.host),t==e)return!0;while(t=t.parentNode)};yo&&11>xo&&(Gi=function(){try{return document.activeElement}catch(e){return document.body}});var Vl,Kl,Xl=e.rmClass=function(e,t){var n=e.className,r=Ui(t).exec(n);if(r){var i=n.slice(r.index+r[0].length);e.className=n.slice(0,r.index)+(i?r[1]+i:"")}},Yl=e.addClass=function(e,t){var n=e.className;Ui(t).test(n)||(e.className+=(n?" ":"")+t)},Ql=!1,Zl=function(){if(yo&&9>xo)return!1;var e=_i("div");return"draggable"in e||"dragDrop"in e}(),Jl=e.splitLines=3!="\n\nb".split(/\n/).length?function(e){for(var t=0,n=[],r=e.length;r>=t;){var i=e.indexOf("\n",t);-1==i&&(i=e.length);var o=e.slice(t,"\r"==e.charAt(i-1)?i-1:i),l=o.indexOf("\r");-1!=l?(n.push(o.slice(0,l)),t+=l+1):(n.push(o),t=i+1)}return n}:function(e){return e.split(/\r\n?|\n/)},ea=window.getSelection?function(e){try{return e.selectionStart!=e.selectionEnd}catch(t){return!1}}:function(e){try{var t=e.ownerDocument.selection.createRange()}catch(n){}return t&&t.parentElement()==e?0!=t.compareEndPoints("StartToEnd",t):!1},ta=function(){var e=_i("div");return"oncopy"in e?!0:(e.setAttribute("oncopy","return;"),"function"==typeof e.oncopy)}(),na=null,ra=e.keyNames={3:"Enter",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:"/",127:"Delete",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"};!function(){for(var e=0;10>e;e++)ra[e+48]=ra[e+96]=String(e);for(var e=65;90>=e;e++)ra[e]=String.fromCharCode(e);for(var e=1;12>=e;e++)ra[e+111]=ra[e+63235]="F"+e}();var ia,oa=function(){function e(e){return 247>=e?n.charAt(e):e>=1424&&1524>=e?"R":e>=1536&&1773>=e?r.charAt(e-1536):e>=1774&&2220>=e?"r":e>=8192&&8203>=e?"w":8204==e?"b":"L"}function t(e,t,n){this.level=e,this.from=t,this.to=n}var n="bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN",r="rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm",i=/[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/,o=/[stwN]/,l=/[LRr]/,a=/[Lb1n]/,s=/[1n]/,c="L";return function(n){if(!i.test(n))return!1;for(var r,u=n.length,d=[],h=0;u>h;++h)d.push(r=e(n.charCodeAt(h)));for(var h=0,f=c;u>h;++h){var r=d[h];"m"==r?d[h]=f:f=r}for(var h=0,p=c;u>h;++h){var r=d[h];"1"==r&&"r"==p?d[h]="n":l.test(r)&&(p=r,"r"==r&&(d[h]="R"))}for(var h=1,f=d[0];u-1>h;++h){var r=d[h];"+"==r&&"1"==f&&"1"==d[h+1]?d[h]="1":","!=r||f!=d[h+1]||"1"!=f&&"n"!=f||(d[h]=f),f=r}for(var h=0;u>h;++h){var r=d[h];if(","==r)d[h]="N";else if("%"==r){for(var m=h+1;u>m&&"%"==d[m];++m);for(var g=h&&"!"==d[h-1]||u>m&&"1"==d[m]?"1":"N",v=h;m>v;++v)d[v]=g;h=m-1}}for(var h=0,p=c;u>h;++h){var r=d[h];"L"==p&&"1"==r?d[h]="L":l.test(r)&&(p=r)}for(var h=0;u>h;++h)if(o.test(d[h])){for(var m=h+1;u>m&&o.test(d[m]);++m);for(var y="L"==(h?d[h-1]:c),x="L"==(u>m?d[m]:c),g=y||x?"L":"R",v=h;m>v;++v)d[v]=g;h=m-1}for(var b,w=[],h=0;u>h;)if(a.test(d[h])){var C=h;for(++h;u>h&&a.test(d[h]);++h);w.push(new t(0,C,h))}else{var k=h,S=w.length;for(++h;u>h&&"L"!=d[h];++h);for(var v=k;h>v;)if(s.test(d[v])){v>k&&w.splice(S,0,new t(1,k,v));var L=v;for(++v;h>v&&s.test(d[v]);++v);w.splice(S,0,new t(2,L,v)),k=v}else++v;h>k&&w.splice(S,0,new t(1,k,h))}return 1==w[0].level&&(b=n.match(/^\s+/))&&(w[0].from=b[0].length,w.unshift(new t(0,0,b[0].length))),1==Hi(w).level&&(b=n.match(/\s+$/))&&(Hi(w).to-=b[0].length,w.push(new t(0,u-b[0].length,u))),2==w[0].level&&w.unshift(new t(1,w[0].to,w[0].to)),w[0].level!=Hi(w).level&&w.push(new t(w[0].level,u,u)),w}}();return e.version="5.7.1",e})},{}],7:[function(t,n,r){!function(i){"object"==typeof r&&"object"==typeof n?i(t("../../lib/codemirror"),t("../markdown/markdown"),t("../../addon/mode/overlay")):"function"==typeof e&&e.amd?e(["../../lib/codemirror","../markdown/markdown","../../addon/mode/overlay"],i):i(CodeMirror)}(function(e){"use strict";var t=/^((?:(?:aaas?|about|acap|adiumxtra|af[ps]|aim|apt|attachment|aw|beshare|bitcoin|bolo|callto|cap|chrome(?:-extension)?|cid|coap|com-eventbrite-attendee|content|crid|cvs|data|dav|dict|dlna-(?:playcontainer|playsingle)|dns|doi|dtn|dvb|ed2k|facetime|feed|file|finger|fish|ftp|geo|gg|git|gizmoproject|go|gopher|gtalk|h323|hcp|https?|iax|icap|icon|im|imap|info|ipn|ipp|irc[6s]?|iris(?:\.beep|\.lwz|\.xpc|\.xpcs)?|itms|jar|javascript|jms|keyparc|lastfm|ldaps?|magnet|mailto|maps|market|message|mid|mms|ms-help|msnim|msrps?|mtqp|mumble|mupdate|mvn|news|nfs|nih?|nntp|notes|oid|opaquelocktoken|palm|paparazzi|platform|pop|pres|proxy|psyc|query|res(?:ource)?|rmi|rsync|rtmp|rtsp|secondlife|service|session|sftp|sgn|shttp|sieve|sips?|skype|sm[bs]|snmp|soap\.beeps?|soldat|spotify|ssh|steam|svn|tag|teamspeak|tel(?:net)?|tftp|things|thismessage|tip|tn3270|tv|udp|unreal|urn|ut2004|vemmi|ventrilo|view-source|webcal|wss?|wtai|wyciwyg|xcon(?:-userid)?|xfire|xmlrpc\.beeps?|xmpp|xri|ymsgr|z39\.50[rs]?):(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]|\([^\s()<>]*\))+(?:\([^\s()<>]*\)|[^\s`*!()\[\]{};:'".,<>?«»“”‘’]))/i;e.defineMode("gfm",function(n,r){function i(e){return e.code=!1,null}var o=0,l={startState:function(){return{code:!1,codeBlock:!1,ateSpace:!1}},copyState:function(e){return{code:e.code,codeBlock:e.codeBlock,ateSpace:e.ateSpace}},token:function(e,n){if(n.combineTokens=null,n.codeBlock)return e.match(/^```+/)?(n.codeBlock=!1,null):(e.skipToEnd(),null);if(e.sol()&&(n.code=!1),e.sol()&&e.match(/^```+/))return e.skipToEnd(),n.codeBlock=!0,null;if("`"===e.peek()){e.next();var i=e.pos;e.eatWhile("`");var l=1+e.pos-i;return n.code?l===o&&(n.code=!1):(o=l,n.code=!0),null}if(n.code)return e.next(),null;if(e.eatSpace())return n.ateSpace=!0,null;if((e.sol()||n.ateSpace)&&(n.ateSpace=!1,r.gitHubSpice!==!1)){if(e.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+@)?(?:[a-f0-9]{7,40}\b)/))return n.combineTokens=!0,"link";if(e.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+)?#[0-9]+\b/))return n.combineTokens=!0,"link"}return e.match(t)&&"]("!=e.string.slice(e.start-2,e.start)&&(0==e.start||/\W/.test(e.string.charAt(e.start-1)))?(n.combineTokens=!0,"link"):(e.next(),null)},blankLine:i},a={underscoresBreakWords:!1,taskLists:!0,fencedCodeBlocks:"```",strikethrough:!0};for(var s in r)a[s]=r[s];return a.name="markdown",e.overlayMode(e.getMode(n,a),l)},"markdown"),e.defineMIME("text/x-gfm","gfm")})},{"../../addon/mode/overlay":5,"../../lib/codemirror":6,"../markdown/markdown":8}],8:[function(t,n,r){!function(i){"object"==typeof r&&"object"==typeof n?i(t("../../lib/codemirror"),t("../xml/xml"),t("../meta")):"function"==typeof e&&e.amd?e(["../../lib/codemirror","../xml/xml","../meta"],i):i(CodeMirror)}(function(e){"use strict";e.defineMode("markdown",function(t,n){function r(n){if(e.findModeByName){var r=e.findModeByName(n);r&&(n=r.mime||r.mimes[0])}var i=e.getMode(t,n);return"null"==i.name?null:i}function i(e,t,n){return t.f=t.inline=n,n(e,t)}function o(e,t,n){return t.f=t.block=n,n(e,t)}function l(e){return!e||!/\S/.test(e.string)}function a(e){return e.linkTitle=!1,e.em=!1,e.strong=!1,e.strikethrough=!1,e.quote=0,e.indentedCode=!1,C||e.f!=c||(e.f=p,e.block=s),e.trailingSpace=0,e.trailingSpaceNewLine=!1,e.prevLine=e.thisLine,e.thisLine=null,null}function s(e,t){var o=e.sol(),a=t.list!==!1,s=t.indentedCode;t.indentedCode=!1,a&&(t.indentationDiff>=0?(t.indentationDiff<4&&(t.indentation-=t.indentationDiff),t.list=null):t.indentation>0?(t.list=null,t.listDepth=Math.floor(t.indentation/4)):(t.list=!1,t.listDepth=0));var c=null;if(t.indentationDiff>=4)return e.skipToEnd(),s||l(t.prevLine)?(t.indentation-=4,t.indentedCode=!0,T):null;if(e.eatSpace())return null;if((c=e.match(U))&&c[1].length<=6)return t.header=c[1].length,n.highlightFormatting&&(t.formatting="header"),t.f=t.inline,h(t);if(!(l(t.prevLine)||t.quote||a||s)&&(c=e.match($)))return t.header="="==c[0].charAt(0)?1:2,n.highlightFormatting&&(t.formatting="header"),t.f=t.inline,h(t);if(e.eat(">"))return t.quote=o?1:t.quote+1,n.highlightFormatting&&(t.formatting="quote"),e.eatSpace(),h(t);if("["===e.peek())return i(e,t,y);if(e.match(_,!0))return t.hr=!0,H;if((l(t.prevLine)||a)&&(e.match(j,!1)||e.match(q,!1))){var d=null;return e.match(j,!0)?d="ul":(e.match(q,!0),d="ol"),t.indentation=e.column()+e.current().length,t.list=!0,t.listDepth++,n.taskLists&&e.match(G,!1)&&(t.taskList=!0),t.f=t.inline,n.highlightFormatting&&(t.formatting=["list","list-"+d]),h(t)}return n.fencedCodeBlocks&&(c=e.match(K,!0))?(t.fencedChars=c[1],t.localMode=r(c[2]),t.localMode&&(t.localState=t.localMode.startState()),t.f=t.block=u,n.highlightFormatting&&(t.formatting="code-block"),t.code=!0,h(t)):i(e,t,t.inline)}function c(e,t){var n=k.token(e,t.htmlState);return(C&&null===t.htmlState.tagStart&&!t.htmlState.context&&t.htmlState.tokenize.isInText||t.md_inside&&e.current().indexOf(">")>-1)&&(t.f=p,t.block=s,t.htmlState=null),n}function u(e,t){return e.sol()&&t.fencedChars&&e.match(t.fencedChars,!1)?(t.localMode=t.localState=null,t.f=t.block=d,null):t.localMode?t.localMode.token(e,t.localState):(e.skipToEnd(),T)}function d(e,t){e.match(t.fencedChars),t.block=s,t.f=p,t.fencedChars=null,n.highlightFormatting&&(t.formatting="code-block"),t.code=!0;var r=h(t);return t.code=!1,r}function h(e){var t=[];if(e.formatting){t.push(W),"string"==typeof e.formatting&&(e.formatting=[e.formatting]);for(var r=0;r=e.quote?t.push(W+"-"+e.formatting[r]+"-"+e.quote):t.push("error"))}if(e.taskOpen)return t.push("meta"),t.length?t.join(" "):null;if(e.taskClosed)return t.push("property"),t.length?t.join(" "):null;if(e.linkHref?t.push(F,"url"):(e.strong&&t.push(R),e.em&&t.push(z),e.strikethrough&&t.push(B),e.linkText&&t.push(P),e.code&&t.push(T)),e.header&&(t.push(L),t.push(L+"-"+e.header)),e.quote&&(t.push(M),!n.maxBlockquoteDepth||n.maxBlockquoteDepth>=e.quote?t.push(M+"-"+e.quote):t.push(M+"-"+n.maxBlockquoteDepth)),e.list!==!1){var i=(e.listDepth-1)%3;i?1===i?t.push(A):t.push(O):t.push(N)}return e.trailingSpaceNewLine?t.push("trailing-space-new-line"):e.trailingSpace&&t.push("trailing-space-"+(e.trailingSpace%2?"a":"b")),t.length?t.join(" "):null}function f(e,t){return e.match(V,!0)?h(t):void 0}function p(t,r){var i=r.text(t,r);if("undefined"!=typeof i)return i;if(r.list)return r.list=null,h(r);if(r.taskList){var l="x"!==t.match(G,!0)[1];return l?r.taskOpen=!0:r.taskClosed=!0,n.highlightFormatting&&(r.formatting="task"),r.taskList=!1,h(r)}if(r.taskOpen=!1,r.taskClosed=!1,r.header&&t.match(/^#+$/,!0))return n.highlightFormatting&&(r.formatting="header"),h(r);var a=t.sol(),s=t.next();if("\\"===s&&(t.next(),n.highlightFormatting)){var u=h(r);return u?u+" formatting-escape":"formatting-escape"}if(r.linkTitle){r.linkTitle=!1;var d=s;"("===s&&(d=")"),d=(d+"").replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1");var f="^\\s*(?:[^"+d+"\\\\]+|\\\\\\\\|\\\\.)"+d;if(t.match(new RegExp(f),!0))return F}if("`"===s){var p=r.formatting;n.highlightFormatting&&(r.formatting="code");var v=h(r),y=t.pos;t.eatWhile("`");var x=1+t.pos-y;return r.code?x===S?(r.code=!1,v):(r.formatting=p,h(r)):(S=x,r.code=!0,h(r))}if(r.code)return h(r);if("!"===s&&t.match(/\[[^\]]*\] ?(?:\(|\[)/,!1))return t.match(/\[[^\]]*\]/),r.inline=r.f=g,D;if("["===s&&t.match(/.*\](\(.*\)| ?\[.*\])/,!1))return r.linkText=!0,n.highlightFormatting&&(r.formatting="link"),h(r);if("]"===s&&r.linkText&&t.match(/\(.*\)| ?\[.*\]/,!1)){n.highlightFormatting&&(r.formatting="link");var u=h(r);return r.linkText=!1,r.inline=r.f=g,u}if("<"===s&&t.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/,!1)){r.f=r.inline=m,n.highlightFormatting&&(r.formatting="link");var u=h(r);return u?u+=" ":u="",u+E}if("<"===s&&t.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/,!1)){r.f=r.inline=m,n.highlightFormatting&&(r.formatting="link");var u=h(r);return u?u+=" ":u="",u+I}if("<"===s&&t.match(/^(!--|\w)/,!1)){var b=t.string.indexOf(">",t.pos);if(-1!=b){var w=t.string.substring(t.start,b);/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(w)&&(r.md_inside=!0)}return t.backUp(1),r.htmlState=e.startState(k),o(t,r,c)}if("<"===s&&t.match(/^\/\w*?>/))return r.md_inside=!1,"tag";var C=!1;if(!n.underscoresBreakWords&&"_"===s&&"_"!==t.peek()&&t.match(/(\w)/,!1)){var L=t.pos-2;if(L>=0){var T=t.string.charAt(L);"_"!==T&&T.match(/(\w)/,!1)&&(C=!0)}}if("*"===s||"_"===s&&!C)if(a&&" "===t.peek());else{if(r.strong===s&&t.eat(s)){n.highlightFormatting&&(r.formatting="strong");var v=h(r);return r.strong=!1,v}if(!r.strong&&t.eat(s))return r.strong=s,n.highlightFormatting&&(r.formatting="strong"),h(r);if(r.em===s){n.highlightFormatting&&(r.formatting="em");var v=h(r);return r.em=!1,v}if(!r.em)return r.em=s,n.highlightFormatting&&(r.formatting="em"),h(r)}else if(" "===s&&(t.eat("*")||t.eat("_"))){if(" "===t.peek())return h(r);t.backUp(1)}if(n.strikethrough)if("~"===s&&t.eatWhile(s)){if(r.strikethrough){n.highlightFormatting&&(r.formatting="strikethrough");var v=h(r);return r.strikethrough=!1,v}if(t.match(/^[^\s]/,!1))return r.strikethrough=!0,n.highlightFormatting&&(r.formatting="strikethrough"),h(r)}else if(" "===s&&t.match(/^~~/,!0)){if(" "===t.peek())return h(r);t.backUp(2)}return" "===s&&(t.match(/ +$/,!1)?r.trailingSpace++:r.trailingSpace&&(r.trailingSpaceNewLine=!0)),h(r)}function m(e,t){var r=e.next();if(">"===r){t.f=t.inline=p,n.highlightFormatting&&(t.formatting="link");var i=h(t);return i?i+=" ":i="",i+E}return e.match(/^[^>]+/,!0),E}function g(e,t){if(e.eatSpace())return null;var r=e.next();return"("===r||"["===r?(t.f=t.inline=v("("===r?")":"]"),n.highlightFormatting&&(t.formatting="link-string"),t.linkHref=!0,h(t)):"error"}function v(e){return function(t,r){var i=t.next();if(i===e){r.f=r.inline=p,n.highlightFormatting&&(r.formatting="link-string");var o=h(r);return r.linkHref=!1,o}return t.match(w(e),!0)&&t.backUp(1),r.linkHref=!0,h(r)}}function y(e,t){return e.match(/^[^\]]*\]:/,!1)?(t.f=x,e.next(),n.highlightFormatting&&(t.formatting="link"),t.linkText=!0,h(t)):i(e,t,p)}function x(e,t){if(e.match(/^\]:/,!0)){t.f=t.inline=b,n.highlightFormatting&&(t.formatting="link");var r=h(t);return t.linkText=!1,r}return e.match(/^[^\]]+/,!0),P}function b(e,t){return e.eatSpace()?null:(e.match(/^[^\s]+/,!0),void 0===e.peek()?t.linkTitle=!0:e.match(/^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/,!0),t.f=t.inline=p,F+" url")}function w(e){return X[e]||(e=(e+"").replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1"),X[e]=new RegExp("^(?:[^\\\\]|\\\\.)*?("+e+")")),X[e]}var C=e.modes.hasOwnProperty("xml"),k=e.getMode(t,C?{name:"xml",htmlMode:!0}:"text/plain");void 0===n.highlightFormatting&&(n.highlightFormatting=!1),void 0===n.maxBlockquoteDepth&&(n.maxBlockquoteDepth=0),void 0===n.underscoresBreakWords&&(n.underscoresBreakWords=!0),void 0===n.taskLists&&(n.taskLists=!1),void 0===n.strikethrough&&(n.strikethrough=!1);var S=0,L="header",T="comment",M="quote",N="variable-2",A="variable-3",O="keyword",H="hr",D="tag",W="formatting",E="link",I="link",P="link",F="string",z="em",R="strong",B="strikethrough",_=/^([*\-_])(?:\s*\1){2,}\s*$/,j=/^[*\-+]\s+/,q=/^[0-9]+([.)])\s+/,G=/^\[(x| )\](?=\s)/,U=n.allowAtxHeaderWithoutSpace?/^(#+)/:/^(#+)(?: |$)/,$=/^ *(?:\={1,}|-{1,})\s*$/,V=/^[^#!\[\]*_\\<>` "'(~]+/,K=new RegExp("^("+(n.fencedCodeBlocks===!0?"~~~+|```+":n.fencedCodeBlocks)+")[ \\t]*([\\w+#]*)"),X=[],Y={startState:function(){return{f:s,prevLine:null,thisLine:null,block:s,htmlState:null,indentation:0,inline:p,text:f,formatting:!1,linkText:!1,linkHref:!1,linkTitle:!1,em:!1,strong:!1,header:0,hr:!1,taskList:!1,list:!1,listDepth:0,quote:0,trailingSpace:0,trailingSpaceNewLine:!1,strikethrough:!1,fencedChars:null}},copyState:function(t){return{f:t.f,prevLine:t.prevLine,thisLine:t["this"],block:t.block,htmlState:t.htmlState&&e.copyState(k,t.htmlState),indentation:t.indentation,localMode:t.localMode,localState:t.localMode?e.copyState(t.localMode,t.localState):null,inline:t.inline,text:t.text,formatting:!1,linkTitle:t.linkTitle,code:t.code,em:t.em,strong:t.strong,strikethrough:t.strikethrough,header:t.header,hr:t.hr,taskList:t.taskList,list:t.list,listDepth:t.listDepth,quote:t.quote,indentedCode:t.indentedCode,trailingSpace:t.trailingSpace,trailingSpaceNewLine:t.trailingSpaceNewLine,md_inside:t.md_inside,fencedChars:t.fencedChars}},token:function(e,t){if(t.formatting=!1,e!=t.thisLine){var n=t.header||t.hr;if(t.header=0,t.hr=!1,e.match(/^\s*$/,!0)||n){if(a(t),!n)return null;t.prevLine=null}t.prevLine=t.thisLine,t.thisLine=e,t.taskList=!1,t.trailingSpace=0,t.trailingSpaceNewLine=!1,t.f=t.block;var r=e.match(/^\s*/,!0)[0].replace(/\t/g," ").length,i=4*Math.floor((r-t.indentation)/4);i>4&&(i=4);var o=t.indentation+i;if(t.indentationDiff=o-t.indentation,t.indentation=o,r>0)return null}return t.f(e,t)},innerMode:function(e){return e.block==c?{state:e.htmlState,mode:k}:e.localState?{state:e.localState,mode:e.localMode}:{state:e,mode:Y}},blankLine:a,getType:h,fold:"markdown"};return Y},"xml"),e.defineMIME("text/x-markdown","markdown")})},{"../../lib/codemirror":6,"../meta":9,"../xml/xml":10}],9:[function(t,n,r){!function(i){"object"==typeof r&&"object"==typeof n?i(t("../lib/codemirror")):"function"==typeof e&&e.amd?e(["../lib/codemirror"],i):i(CodeMirror)}(function(e){"use strict";e.modeInfo=[{name:"APL",mime:"text/apl",mode:"apl",ext:["dyalog","apl"]},{name:"PGP",mimes:["application/pgp","application/pgp-keys","application/pgp-signature"],mode:"asciiarmor",ext:["pgp"]},{name:"ASN.1",mime:"text/x-ttcn-asn",mode:"asn.1",ext:["asn","asn1"]},{name:"Asterisk",mime:"text/x-asterisk",mode:"asterisk",file:/^extensions\.conf$/i},{name:"Brainfuck",mime:"text/x-brainfuck",mode:"brainfuck",ext:["b","bf"]},{name:"C",mime:"text/x-csrc",mode:"clike",ext:["c","h"]},{name:"C++",mime:"text/x-c++src",mode:"clike",ext:["cpp","c++","cc","cxx","hpp","h++","hh","hxx"],alias:["cpp"]},{name:"Cobol",mime:"text/x-cobol",mode:"cobol",ext:["cob","cpy"]},{name:"C#",mime:"text/x-csharp",mode:"clike",ext:["cs"],alias:["csharp"]},{name:"Clojure",mime:"text/x-clojure",mode:"clojure",ext:["clj"]},{name:"Closure Stylesheets (GSS)",mime:"text/x-gss",mode:"css",ext:["gss"]},{name:"CMake",mime:"text/x-cmake",mode:"cmake",ext:["cmake","cmake.in"],file:/^CMakeLists.txt$/},{name:"CoffeeScript",mime:"text/x-coffeescript",mode:"coffeescript",ext:["coffee"],alias:["coffee","coffee-script"]},{name:"Common Lisp",mime:"text/x-common-lisp",mode:"commonlisp",ext:["cl","lisp","el"],alias:["lisp"]},{name:"Cypher",mime:"application/x-cypher-query",mode:"cypher",ext:["cyp","cypher"]},{name:"Cython",mime:"text/x-cython",mode:"python",ext:["pyx","pxd","pxi"]},{name:"CSS",mime:"text/css",mode:"css",ext:["css"]},{name:"CQL",mime:"text/x-cassandra",mode:"sql",ext:["cql"]},{name:"D",mime:"text/x-d",mode:"d",ext:["d"]},{name:"Dart",mimes:["application/dart","text/x-dart"],mode:"dart",ext:["dart"]},{name:"diff",mime:"text/x-diff",mode:"diff",ext:["diff","patch"]},{name:"Django",mime:"text/x-django",mode:"django"},{name:"Dockerfile",mime:"text/x-dockerfile",mode:"dockerfile",file:/^Dockerfile$/},{name:"DTD",mime:"application/xml-dtd",mode:"dtd",ext:["dtd"]},{name:"Dylan",mime:"text/x-dylan",mode:"dylan",ext:["dylan","dyl","intr"]},{name:"EBNF",mime:"text/x-ebnf",mode:"ebnf"},{name:"ECL",mime:"text/x-ecl",mode:"ecl",ext:["ecl"]},{name:"Eiffel",mime:"text/x-eiffel",mode:"eiffel",ext:["e"]},{name:"Elm",mime:"text/x-elm",mode:"elm",ext:["elm"]},{name:"Embedded Javascript",mime:"application/x-ejs",mode:"htmlembedded",ext:["ejs"]},{name:"Embedded Ruby",mime:"application/x-erb",mode:"htmlembedded",ext:["erb"]},{name:"Erlang",mime:"text/x-erlang",mode:"erlang",ext:["erl"]},{name:"Factor",mime:"text/x-factor",mode:"factor",ext:["factor"]},{name:"Forth",mime:"text/x-forth",mode:"forth",ext:["forth","fth","4th"]},{name:"Fortran",mime:"text/x-fortran",mode:"fortran",ext:["f","for","f77","f90"]},{name:"F#",mime:"text/x-fsharp",mode:"mllike",ext:["fs"],alias:["fsharp"]},{name:"Gas",mime:"text/x-gas",mode:"gas",ext:["s"]},{name:"Gherkin",mime:"text/x-feature",mode:"gherkin",ext:["feature"]},{name:"GitHub Flavored Markdown",mime:"text/x-gfm",mode:"gfm",file:/^(readme|contributing|history).md$/i},{name:"Go",mime:"text/x-go",mode:"go",ext:["go"]},{name:"Groovy",mime:"text/x-groovy",mode:"groovy",ext:["groovy"]},{name:"HAML",mime:"text/x-haml",mode:"haml",ext:["haml"]},{name:"Haskell",mime:"text/x-haskell",mode:"haskell",ext:["hs"]},{name:"Haxe",mime:"text/x-haxe",mode:"haxe",ext:["hx"]},{name:"HXML",mime:"text/x-hxml",mode:"haxe",ext:["hxml"]},{name:"ASP.NET",mime:"application/x-aspx",mode:"htmlembedded",ext:["aspx"],alias:["asp","aspx"]},{name:"HTML",mime:"text/html",mode:"htmlmixed",ext:["html","htm"],alias:["xhtml"]},{name:"HTTP",mime:"message/http",mode:"http"},{name:"IDL",mime:"text/x-idl",mode:"idl",ext:["pro"]},{name:"Jade",mime:"text/x-jade",mode:"jade",ext:["jade"]},{name:"Java",mime:"text/x-java",mode:"clike",ext:["java"]},{name:"Java Server Pages",mime:"application/x-jsp",mode:"htmlembedded",ext:["jsp"],alias:["jsp"]},{name:"JavaScript",mimes:["text/javascript","text/ecmascript","application/javascript","application/x-javascript","application/ecmascript"],mode:"javascript",ext:["js"],alias:["ecmascript","js","node"]},{name:"JSON",mimes:["application/json","application/x-json"],mode:"javascript",ext:["json","map"],alias:["json5"]},{name:"JSON-LD",mime:"application/ld+json",mode:"javascript",ext:["jsonld"],alias:["jsonld"]},{name:"Jinja2",mime:"null",mode:"jinja2"},{name:"Julia",mime:"text/x-julia",mode:"julia",ext:["jl"]},{name:"Kotlin",mime:"text/x-kotlin",mode:"clike",ext:["kt"]},{name:"LESS",mime:"text/x-less",mode:"css",ext:["less"]},{name:"LiveScript",mime:"text/x-livescript",mode:"livescript",ext:["ls"],alias:["ls"]},{name:"Lua",mime:"text/x-lua",mode:"lua",ext:["lua"]},{name:"Markdown",mime:"text/x-markdown",mode:"markdown",ext:["markdown","md","mkd"]},{name:"mIRC",mime:"text/mirc",mode:"mirc"},{name:"MariaDB SQL",mime:"text/x-mariadb",mode:"sql"},{name:"Mathematica",mime:"text/x-mathematica",mode:"mathematica",ext:["m","nb"]},{name:"Modelica",mime:"text/x-modelica",mode:"modelica",ext:["mo"]},{name:"MUMPS",mime:"text/x-mumps",mode:"mumps"},{name:"MS SQL",mime:"text/x-mssql",mode:"sql"},{name:"MySQL",mime:"text/x-mysql",mode:"sql"},{name:"Nginx",mime:"text/x-nginx-conf",mode:"nginx",file:/nginx.*\.conf$/i},{name:"NTriples",mime:"text/n-triples",mode:"ntriples",ext:["nt"]},{name:"Objective C",mime:"text/x-objectivec",mode:"clike",ext:["m","mm"]},{name:"OCaml",mime:"text/x-ocaml",mode:"mllike",ext:["ml","mli","mll","mly"]},{name:"Octave",mime:"text/x-octave",mode:"octave",ext:["m"]},{name:"Oz",mime:"text/x-oz",mode:"oz",ext:["oz"]},{name:"Pascal",mime:"text/x-pascal",mode:"pascal",ext:["p","pas"]},{name:"PEG.js",mime:"null",mode:"pegjs",ext:["jsonld"]},{name:"Perl",mime:"text/x-perl",mode:"perl",ext:["pl","pm"]},{name:"PHP",mime:"application/x-httpd-php",mode:"php",ext:["php","php3","php4","php5","phtml"]},{name:"Pig",mime:"text/x-pig",mode:"pig",ext:["pig"]},{name:"Plain Text",mime:"text/plain",mode:"null",ext:["txt","text","conf","def","list","log"]},{name:"PLSQL",mime:"text/x-plsql",mode:"sql",ext:["pls"]},{name:"Properties files",mime:"text/x-properties",mode:"properties",ext:["properties","ini","in"],alias:["ini","properties"]},{name:"Python",mime:"text/x-python",mode:"python",ext:["py","pyw"]},{name:"Puppet",mime:"text/x-puppet",mode:"puppet",ext:["pp"]},{name:"Q",mime:"text/x-q",mode:"q",ext:["q"]},{name:"R",mime:"text/x-rsrc",mode:"r",ext:["r"],alias:["rscript"]},{name:"reStructuredText",mime:"text/x-rst",mode:"rst",ext:["rst"],alias:["rst"]},{name:"RPM Changes",mime:"text/x-rpm-changes",mode:"rpm"},{name:"RPM Spec",mime:"text/x-rpm-spec",mode:"rpm",ext:["spec"]},{name:"Ruby",mime:"text/x-ruby",mode:"ruby",ext:["rb"],alias:["jruby","macruby","rake","rb","rbx"]},{name:"Rust",mime:"text/x-rustsrc",mode:"rust",ext:["rs"]},{name:"Sass",mime:"text/x-sass",mode:"sass",ext:["sass"]},{name:"Scala",mime:"text/x-scala",mode:"clike",ext:["scala"]},{name:"Scheme",mime:"text/x-scheme",mode:"scheme",ext:["scm","ss"]},{name:"SCSS",mime:"text/x-scss",mode:"css",ext:["scss"]},{name:"Shell",mime:"text/x-sh",mode:"shell",ext:["sh","ksh","bash"],alias:["bash","sh","zsh"]},{name:"Sieve",mime:"application/sieve",mode:"sieve",ext:["siv","sieve"]},{name:"Slim",mimes:["text/x-slim","application/x-slim"],mode:"slim",ext:["slim"]},{name:"Smalltalk",mime:"text/x-stsrc",mode:"smalltalk",ext:["st"]},{name:"Smarty",mime:"text/x-smarty",mode:"smarty",ext:["tpl"]},{name:"Solr",mime:"text/x-solr",mode:"solr"},{name:"Soy",mime:"text/x-soy",mode:"soy",ext:["soy"],alias:["closure template"]},{name:"SPARQL",mime:"application/sparql-query",mode:"sparql",ext:["rq","sparql"],alias:["sparul"]},{name:"Spreadsheet",mime:"text/x-spreadsheet",mode:"spreadsheet",alias:["excel","formula"]},{name:"SQL",mime:"text/x-sql",mode:"sql",ext:["sql"]},{name:"Squirrel",mime:"text/x-squirrel",mode:"clike",ext:["nut"]},{name:"Swift",mime:"text/x-swift",mode:"swift",ext:["swift"]},{name:"MariaDB",mime:"text/x-mariadb",mode:"sql"},{name:"sTeX",mime:"text/x-stex",mode:"stex"},{name:"LaTeX",mime:"text/x-latex",mode:"stex",ext:["text","ltx"],alias:["tex"]},{name:"SystemVerilog",mime:"text/x-systemverilog",mode:"verilog",ext:["v"]},{name:"Tcl",mime:"text/x-tcl",mode:"tcl",ext:["tcl"]},{name:"Textile",mime:"text/x-textile",mode:"textile",ext:["textile"]},{name:"TiddlyWiki ",mime:"text/x-tiddlywiki",mode:"tiddlywiki"},{name:"Tiki wiki",mime:"text/tiki",mode:"tiki"},{name:"TOML",mime:"text/x-toml",mode:"toml",ext:["toml"]},{name:"Tornado",mime:"text/x-tornado",mode:"tornado"},{name:"troff",mime:"troff",mode:"troff",ext:["1","2","3","4","5","6","7","8","9"]},{name:"TTCN",mime:"text/x-ttcn",mode:"ttcn",ext:["ttcn","ttcn3","ttcnpp"]},{name:"TTCN_CFG",mime:"text/x-ttcn-cfg",mode:"ttcn-cfg",ext:["cfg"]},{name:"Turtle",mime:"text/turtle",mode:"turtle",ext:["ttl"]},{name:"TypeScript",mime:"application/typescript",mode:"javascript",ext:["ts"],alias:["ts"]},{name:"Twig",mime:"text/x-twig",mode:"twig"},{name:"VB.NET",mime:"text/x-vb",mode:"vb",ext:["vb"]},{name:"VBScript",mime:"text/vbscript",mode:"vbscript",ext:["vbs"]},{name:"Velocity",mime:"text/velocity",mode:"velocity",ext:["vtl"]},{name:"Verilog",mime:"text/x-verilog",mode:"verilog",ext:["v"]},{name:"VHDL",mime:"text/x-vhdl",mode:"vhdl",ext:["vhd","vhdl"]},{name:"XML",mimes:["application/xml","text/xml"],mode:"xml",ext:["xml","xsl","xsd"],alias:["rss","wsdl","xsd"]},{name:"XQuery",mime:"application/xquery",mode:"xquery",ext:["xy","xquery"]},{name:"YAML",mime:"text/x-yaml",mode:"yaml",ext:["yaml","yml"],alias:["yml"]},{name:"Z80",mime:"text/x-z80",mode:"z80",ext:["z80"]},{name:"mscgen",mime:"text/x-mscgen",mode:"mscgen",ext:["mscgen","mscin","msc"]},{name:"xu",mime:"text/x-xu",mode:"mscgen",ext:["xu"]},{name:"msgenny",mime:"text/x-msgenny",mode:"mscgen",ext:["msgenny"]}];for(var t=0;t-1&&t.substring(i+1,t.length);return o?e.findModeByExtension(o):void 0},e.findModeByName=function(t){t=t.toLowerCase();for(var n=0;n")):null:e.match("--")?n(l("comment","-->")):e.match("DOCTYPE",!0,!0)?(e.eatWhile(/[\w\._\-]/),n(a(1))):null:e.eat("?")?(e.eatWhile(/[\w\._\-]/),t.tokenize=l("meta","?>"),"meta"):(k=e.eat("/")?"closeTag":"openTag",t.tokenize=i,"tag bracket");if("&"==r){var o;return o=e.eat("#")?e.eat("x")?e.eatWhile(/[a-fA-F\d]/)&&e.eat(";"):e.eatWhile(/[\d]/)&&e.eat(";"):e.eatWhile(/[\w\.\-:]/)&&e.eat(";"),o?"atom":"error"}return e.eatWhile(/[^&<]/),null}function i(e,t){var n=e.next();if(">"==n||"/"==n&&e.eat(">"))return t.tokenize=r,k=">"==n?"endTag":"selfcloseTag","tag bracket";if("="==n)return k="equals",null;if("<"==n){t.tokenize=r,t.state=d,t.tagName=t.tagStart=null;var i=t.tokenize(e,t);return i?i+" tag error":"tag error"}return/[\'\"]/.test(n)?(t.tokenize=o(n),t.stringStartCol=e.column(),t.tokenize(e,t)):(e.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/),"word")}function o(e){var t=function(t,n){for(;!t.eol();)if(t.next()==e){n.tokenize=i;break}return"string"};return t.isInAttribute=!0,t}function l(e,t){return function(n,i){for(;!n.eol();){if(n.match(t)){i.tokenize=r;break}n.next()}return e}}function a(e){return function(t,n){for(var i;null!=(i=t.next());){if("<"==i)return n.tokenize=a(e+1),n.tokenize(t,n);if(">"==i){if(1==e){n.tokenize=r;break}return n.tokenize=a(e-1),n.tokenize(t,n)}}return"meta"}}function s(e,t,n){this.prev=e.context,this.tagName=t,this.indent=e.indented,this.startOfLine=n,(L.doNotIndent.hasOwnProperty(t)||e.context&&e.context.noIndent)&&(this.noIndent=!0)}function c(e){e.context&&(e.context=e.context.prev)}function u(e,t){for(var n;;){if(!e.context)return;if(n=e.context.tagName,!L.contextGrabbers.hasOwnProperty(n)||!L.contextGrabbers[n].hasOwnProperty(t))return;c(e)}}function d(e,t,n){return"openTag"==e?(n.tagStart=t.column(),h):"closeTag"==e?f:d}function h(e,t,n){return"word"==e?(n.tagName=t.current(),S="tag",g):(S="error",h)}function f(e,t,n){if("word"==e){var r=t.current();return n.context&&n.context.tagName!=r&&L.implicitlyClosed.hasOwnProperty(n.context.tagName)&&c(n),n.context&&n.context.tagName==r?(S="tag",p):(S="tag error",m)}return S="error",m}function p(e,t,n){return"endTag"!=e?(S="error",p):(c(n),d)}function m(e,t,n){return S="error",p(e,t,n)}function g(e,t,n){if("word"==e)return S="attribute",v;if("endTag"==e||"selfcloseTag"==e){var r=n.tagName,i=n.tagStart;return n.tagName=n.tagStart=null,"selfcloseTag"==e||L.autoSelfClosers.hasOwnProperty(r)?u(n,r):(u(n,r),n.context=new s(n,r,i==n.indented)),d}return S="error",g}function v(e,t,n){return"equals"==e?y:(L.allowMissing||(S="error"),g(e,t,n))}function y(e,t,n){return"string"==e?x:"word"==e&&L.allowUnquoted?(S="string",g):(S="error",g(e,t,n))}function x(e,t,n){return"string"==e?x:g(e,t,n)}var b=t.indentUnit,w=n.multilineTagIndentFactor||1,C=n.multilineTagIndentPastTag;null==C&&(C=!0);
+var k,S,L=n.htmlMode?{autoSelfClosers:{area:!0,base:!0,br:!0,col:!0,command:!0,embed:!0,frame:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0,menuitem:!0},implicitlyClosed:{dd:!0,li:!0,optgroup:!0,option:!0,p:!0,rp:!0,rt:!0,tbody:!0,td:!0,tfoot:!0,th:!0,tr:!0},contextGrabbers:{dd:{dd:!0,dt:!0},dt:{dd:!0,dt:!0},li:{li:!0},option:{option:!0,optgroup:!0},optgroup:{optgroup:!0},p:{address:!0,article:!0,aside:!0,blockquote:!0,dir:!0,div:!0,dl:!0,fieldset:!0,footer:!0,form:!0,h1:!0,h2:!0,h3:!0,h4:!0,h5:!0,h6:!0,header:!0,hgroup:!0,hr:!0,menu:!0,nav:!0,ol:!0,p:!0,pre:!0,section:!0,table:!0,ul:!0},rp:{rp:!0,rt:!0},rt:{rp:!0,rt:!0},tbody:{tbody:!0,tfoot:!0},td:{td:!0,th:!0},tfoot:{tbody:!0},th:{td:!0,th:!0},thead:{tbody:!0,tfoot:!0},tr:{tr:!0}},doNotIndent:{pre:!0},allowUnquoted:!0,allowMissing:!0,caseFold:!0}:{autoSelfClosers:{},implicitlyClosed:{},contextGrabbers:{},doNotIndent:{},allowUnquoted:!1,allowMissing:!1,caseFold:!1},T=n.alignCDATA;return r.isInText=!0,{startState:function(){return{tokenize:r,state:d,indented:0,tagName:null,tagStart:null,context:null}},token:function(e,t){if(!t.tagName&&e.sol()&&(t.indented=e.indentation()),e.eatSpace())return null;k=null;var n=t.tokenize(e,t);return(n||k)&&"comment"!=n&&(S=null,t.state=t.state(k||n,e,t),S&&(n="error"==S?n+" error":S)),n},indent:function(t,n,o){var l=t.context;if(t.tokenize.isInAttribute)return t.tagStart==t.indented?t.stringStartCol+1:t.indented+b;if(l&&l.noIndent)return e.Pass;if(t.tokenize!=i&&t.tokenize!=r)return o?o.match(/^(\s*)/)[0].length:0;if(t.tagName)return C?t.tagStart+t.tagName.length+2:t.tagStart+b*w;if(T&&/$/,blockCommentStart:"",configuration:n.htmlMode?"html":"xml",helperType:n.htmlMode?"html":"xml"}}),e.defineMIME("text/xml","xml"),e.defineMIME("application/xml","xml"),e.mimeModes.hasOwnProperty("text/html")||e.defineMIME("text/html",{name:"xml",htmlMode:!0})})},{"../../lib/codemirror":6}],11:[function(t,n,r){(function(t){(function(){function t(e){this.tokens=[],this.tokens.links={},this.options=e||h.defaults,this.rules=f.normal,this.options.gfm&&(this.options.tables?this.rules=f.tables:this.rules=f.gfm)}function i(e,t){if(this.options=t||h.defaults,this.links=e,this.rules=p.normal,this.renderer=this.options.renderer||new o,this.renderer.options=this.options,!this.links)throw new Error("Tokens array requires a `links` property.");this.options.gfm?this.options.breaks?this.rules=p.breaks:this.rules=p.gfm:this.options.pedantic&&(this.rules=p.pedantic)}function o(e){this.options=e||{}}function l(e){this.tokens=[],this.token=null,this.options=e||h.defaults,this.options.renderer=this.options.renderer||new o,this.renderer=this.options.renderer,this.renderer.options=this.options}function a(e,t){return e.replace(t?/&/g:/&(?!#?\w+;)/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function s(e){return e.replace(/&([#\w]+);/g,function(e,t){return t=t.toLowerCase(),"colon"===t?":":"#"===t.charAt(0)?"x"===t.charAt(1)?String.fromCharCode(parseInt(t.substring(2),16)):String.fromCharCode(+t.substring(1)):""})}function c(e,t){return e=e.source,t=t||"",function n(r,i){return r?(i=i.source||i,i=i.replace(/(^|[^\[])\^/g,"$1"),e=e.replace(r,i),n):new RegExp(e,t)}}function u(){}function d(e){for(var t,n,r=1;rAn error occured:
"+a(u.message+"",!0)+"
";throw u}}var f={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:u,hr:/^( *[-*_]){3,} *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,nptable:u,lheading:/^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,blockquote:/^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,list:/^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:/^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,def:/^ *\[([^\]]+)\]: *([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,table:u,paragraph:/^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,text:/^[^\n]+/};f.bullet=/(?:[*+-]|\d+\.)/,f.item=/^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/,f.item=c(f.item,"gm")(/bull/g,f.bullet)(),f.list=c(f.list)(/bull/g,f.bullet)("hr","\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))")("def","\\n+(?="+f.def.source+")")(),f.blockquote=c(f.blockquote)("def",f.def)(),f._tag="(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b",f.html=c(f.html)("comment",//)("closed",/<(tag)[\s\S]+?<\/\1>/)("closing",/])*?>/)(/tag/g,f._tag)(),f.paragraph=c(f.paragraph)("hr",f.hr)("heading",f.heading)("lheading",f.lheading)("blockquote",f.blockquote)("tag","<"+f._tag)("def",f.def)(),f.normal=d({},f),f.gfm=d({},f.normal,{fences:/^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,paragraph:/^/,heading:/^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/}),f.gfm.paragraph=c(f.paragraph)("(?!","(?!"+f.gfm.fences.source.replace("\\1","\\2")+"|"+f.list.source.replace("\\1","\\3")+"|")(),f.tables=d({},f.gfm,{nptable:/^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,table:/^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/}),t.rules=f,t.lex=function(e,n){var r=new t(n);return r.lex(e)},t.prototype.lex=function(e){return e=e.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n"),this.token(e,!0)},t.prototype.token=function(e,t,n){for(var r,i,o,l,a,s,c,u,d,e=e.replace(/^ +$/gm,"");e;)if((o=this.rules.newline.exec(e))&&(e=e.substring(o[0].length),o[0].length>1&&this.tokens.push({type:"space"})),o=this.rules.code.exec(e))e=e.substring(o[0].length),o=o[0].replace(/^ {4}/gm,""),this.tokens.push({type:"code",text:this.options.pedantic?o:o.replace(/\n+$/,"")});else if(o=this.rules.fences.exec(e))e=e.substring(o[0].length),this.tokens.push({type:"code",lang:o[2],text:o[3]||""});else if(o=this.rules.heading.exec(e))e=e.substring(o[0].length),this.tokens.push({type:"heading",depth:o[1].length,text:o[2]});else if(t&&(o=this.rules.nptable.exec(e))){for(e=e.substring(o[0].length),s={type:"table",header:o[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:o[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:o[3].replace(/\n$/,"").split("\n")},u=0;u ?/gm,""),this.token(o,t,!0),this.tokens.push({type:"blockquote_end"});else if(o=this.rules.list.exec(e)){for(e=e.substring(o[0].length),l=o[2],this.tokens.push({type:"list_start",ordered:l.length>1}),o=o[0].match(this.rules.item),r=!1,d=o.length,u=0;d>u;u++)s=o[u],c=s.length,s=s.replace(/^ *([*+-]|\d+\.) +/,""),~s.indexOf("\n ")&&(c-=s.length,s=this.options.pedantic?s.replace(/^ {1,4}/gm,""):s.replace(new RegExp("^ {1,"+c+"}","gm"),"")),this.options.smartLists&&u!==d-1&&(a=f.bullet.exec(o[u+1])[0],l===a||l.length>1&&a.length>1||(e=o.slice(u+1).join("\n")+e,u=d-1)),i=r||/\n\n(?!\s*$)/.test(s),u!==d-1&&(r="\n"===s.charAt(s.length-1),i||(i=r)),this.tokens.push({type:i?"loose_item_start":"list_item_start"}),this.token(s,!1,n),this.tokens.push({type:"list_item_end"});this.tokens.push({type:"list_end"})}else if(o=this.rules.html.exec(e))e=e.substring(o[0].length),this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:!this.options.sanitizer&&("pre"===o[1]||"script"===o[1]||"style"===o[1]),text:o[0]});else if(!n&&t&&(o=this.rules.def.exec(e)))e=e.substring(o[0].length),this.tokens.links[o[1].toLowerCase()]={href:o[2],title:o[3]};else if(t&&(o=this.rules.table.exec(e))){for(e=e.substring(o[0].length),s={type:"table",header:o[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:o[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:o[3].replace(/(?: *\| *)?\n$/,"").split("\n")},u=0;u])/,autolink:/^<([^ >]+(@|:\/)[^ >]+)>/,url:u,tag:/^|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,link:/^!?\[(inside)\]\(href\)/,reflink:/^!?\[(inside)\]\s*\[([^\]]*)\]/,nolink:/^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,strong:/^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,em:/^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,code:/^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,br:/^ {2,}\n(?!\s*$)/,del:u,text:/^[\s\S]+?(?=[\\?(?:\s+['"]([\s\S]*?)['"])?\s*/,p.link=c(p.link)("inside",p._inside)("href",p._href)(),p.reflink=c(p.reflink)("inside",p._inside)(),p.normal=d({},p),p.pedantic=d({},p.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/}),p.gfm=d({},p.normal,{escape:c(p.escape)("])","~|])")(),url:/^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,del:/^~~(?=\S)([\s\S]*?\S)~~/,text:c(p.text)("]|","~]|")("|","|https?://|")()}),p.breaks=d({},p.gfm,{br:c(p.br)("{2,}","*")(),text:c(p.gfm.text)("{2,}","*")()}),i.rules=p,i.output=function(e,t,n){var r=new i(t,n);return r.output(e)},i.prototype.output=function(e){for(var t,n,r,i,o="";e;)if(i=this.rules.escape.exec(e))e=e.substring(i[0].length),o+=i[1];else if(i=this.rules.autolink.exec(e))e=e.substring(i[0].length),"@"===i[2]?(n=":"===i[1].charAt(6)?this.mangle(i[1].substring(7)):this.mangle(i[1]),r=this.mangle("mailto:")+n):(n=a(i[1]),r=n),o+=this.renderer.link(r,null,n);else if(this.inLink||!(i=this.rules.url.exec(e))){if(i=this.rules.tag.exec(e))!this.inLink&&/^/i.test(i[0])&&(this.inLink=!1),e=e.substring(i[0].length),o+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(i[0]):a(i[0]):i[0];else if(i=this.rules.link.exec(e))e=e.substring(i[0].length),this.inLink=!0,o+=this.outputLink(i,{href:i[2],title:i[3]}),this.inLink=!1;else if((i=this.rules.reflink.exec(e))||(i=this.rules.nolink.exec(e))){if(e=e.substring(i[0].length),t=(i[2]||i[1]).replace(/\s+/g," "),t=this.links[t.toLowerCase()],!t||!t.href){o+=i[0].charAt(0),e=i[0].substring(1)+e;continue}this.inLink=!0,o+=this.outputLink(i,t),this.inLink=!1}else if(i=this.rules.strong.exec(e))e=e.substring(i[0].length),o+=this.renderer.strong(this.output(i[2]||i[1]));else if(i=this.rules.em.exec(e))e=e.substring(i[0].length),o+=this.renderer.em(this.output(i[2]||i[1]));else if(i=this.rules.code.exec(e))e=e.substring(i[0].length),o+=this.renderer.codespan(a(i[2],!0));else if(i=this.rules.br.exec(e))e=e.substring(i[0].length),o+=this.renderer.br();else if(i=this.rules.del.exec(e))e=e.substring(i[0].length),o+=this.renderer.del(this.output(i[1]));else if(i=this.rules.text.exec(e))e=e.substring(i[0].length),o+=this.renderer.text(a(this.smartypants(i[0])));else if(e)throw new Error("Infinite loop on byte: "+e.charCodeAt(0))}else e=e.substring(i[0].length),n=a(i[1]),r=n,o+=this.renderer.link(r,null,n);return o},i.prototype.outputLink=function(e,t){var n=a(t.href),r=t.title?a(t.title):null;return"!"!==e[0].charAt(0)?this.renderer.link(n,r,this.output(e[1])):this.renderer.image(n,r,a(e[1]))},i.prototype.smartypants=function(e){return this.options.smartypants?e.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014\/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014\/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…"):e},i.prototype.mangle=function(e){if(!this.options.mangle)return e;for(var t,n="",r=e.length,i=0;r>i;i++)t=e.charCodeAt(i),Math.random()>.5&&(t="x"+t.toString(16)),n+=""+t+";";return n},o.prototype.code=function(e,t,n){if(this.options.highlight){var r=this.options.highlight(e,t);null!=r&&r!==e&&(n=!0,e=r)}return t?''+(n?e:a(e,!0))+"\n
\n":""+(n?e:a(e,!0))+"\n
"},o.prototype.blockquote=function(e){return"\n"+e+"
\n"},o.prototype.html=function(e){return e},o.prototype.heading=function(e,t,n){return"\n"},o.prototype.hr=function(){return this.options.xhtml?"
\n":"
\n"},o.prototype.list=function(e,t){var n=t?"ol":"ul";return"<"+n+">\n"+e+""+n+">\n"},o.prototype.listitem=function(e){return""+e+"\n"},o.prototype.paragraph=function(e){return""+e+"
\n"},o.prototype.table=function(e,t){return"\n"},o.prototype.tablerow=function(e){return"\n"+e+"
\n"},o.prototype.tablecell=function(e,t){var n=t.header?"th":"td",r=t.align?"<"+n+' style="text-align:'+t.align+'">':"<"+n+">";return r+e+""+n+">\n"},o.prototype.strong=function(e){return""+e+""},o.prototype.em=function(e){return""+e+""},o.prototype.codespan=function(e){return""+e+"
"},o.prototype.br=function(){return this.options.xhtml?"
":"
"},o.prototype.del=function(e){return""+e+""},o.prototype.link=function(e,t,n){if(this.options.sanitize){try{var r=decodeURIComponent(s(e)).replace(/[^\w:]/g,"").toLowerCase()}catch(i){return""}if(0===r.indexOf("javascript:")||0===r.indexOf("vbscript:"))return""}var o='"+n+""},o.prototype.image=function(e,t,n){var r='":">"},o.prototype.text=function(e){return e},l.parse=function(e,t,n){var r=new l(t,n);return r.parse(e)},l.prototype.parse=function(e){this.inline=new i(e.links,this.options,this.renderer),this.tokens=e.reverse();for(var t="";this.next();)t+=this.tok();return t},l.prototype.next=function(){return this.token=this.tokens.pop()},l.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0},l.prototype.parseText=function(){for(var e=this.token.text;"text"===this.peek().type;)e+="\n"+this.next().text;return this.inline.output(e)},l.prototype.tok=function(){switch(this.token.type){case"space":return"";case"hr":return this.renderer.hr();case"heading":return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,this.token.text);case"code":return this.renderer.code(this.token.text,this.token.lang,this.token.escaped);case"table":var e,t,n,r,i,o="",l="";for(n="",e=0;e=o?"bigger"==t?"###### "+i:"# "+i:6==o&&"smaller"==t?i.substr(7):1==o&&"bigger"==t?i.substr(2):"bigger"==t?i.substr(1):"#"+i:1==n?0>=o?"# "+i:o==n?i.substr(o+1):"# "+i.substr(o+1):2==n?0>=o?"## "+i:o==n?i.substr(o+1):"## "+i.substr(o+1):0>=o?"### "+i:o==n?i.substr(o+1):"### "+i.substr(o+1),e.replaceRange(i,{line:r,ch:0},{line:r,ch:99999999999999})}(o);e.focus()}}function A(e,t){if(!/editor-preview-active/.test(e.getWrapperElement().lastChild.className)){for(var n=l(e),r=e.getCursor("start"),i=e.getCursor("end"),o={quote:/^(\s*)\>\s+/,"unordered-list":/^(\s*)(\*|\-|\+)\s+/,"ordered-list":/^(\s*)\d+\.\s+/},a={quote:"> ","unordered-list":"* ","ordered-list":"1. "},s=r.line;s<=i.line;s++)!function(r){var i=e.getLine(r);i=n[t]?i.replace(o[t],"$1"):a[t]+i,e.replaceRange(i,{line:r,ch:0},{line:r,ch:99999999999999})}(s);e.focus()}}function O(e,t,n,r){if(!/editor-preview-active/.test(e.codemirror.getWrapperElement().lastChild.className)){r="undefined"==typeof r?n:r;var i,o=e.codemirror,a=l(o),s=n,c=r,u=o.getCursor("start"),d=o.getCursor("end");a[t]?(i=o.getLine(u.line),s=i.slice(0,u.ch),c=i.slice(u.ch),"bold"==t?(s=s.replace(/(\*\*|__)(?![\s\S]*(\*\*|__))/,""),c=c.replace(/(\*\*|__)/,"")):"italic"==t?(s=s.replace(/(\*|_)(?![\s\S]*(\*|_))/,""),c=c.replace(/(\*|_)/,"")):"strikethrough"==t&&(s=s.replace(/(\*\*|~~)(?![\s\S]*(\*\*|~~))/,""),c=c.replace(/(\*\*|~~)/,"")),o.replaceRange(s+c,{line:u.line,ch:0},{line:u.line,ch:99999999999999}),"bold"==t||"strikethrough"==t?(u.ch-=2,u!==d&&(d.ch-=2)):"italic"==t&&(u.ch-=1,u!==d&&(d.ch-=1))):(i=o.getSelection(),"bold"==t?(i=i.split("**").join(""),i=i.split("__").join("")):"italic"==t?(i=i.split("*").join(""),i=i.split("_").join("")):"strikethrough"==t&&(i=i.split("~~").join("")),o.replaceSelection(s+i+c),u.ch+=n.length,d.ch=u.ch+i.length),o.setSelection(u,d),o.focus()}}function H(e){var t=/[a-zA-Z0-9_\u0392-\u03c9]+|[\u4E00-\u9FFF\u3400-\u4dbf\uf900-\ufaff\u3040-\u309f\uac00-\ud7af]+/g,n=e.match(t),r=0;if(null===n)return r;for(var i=0;i=19968?n[i].length:1;return r}function D(e){e=e||{},e.parent=this;var t=!0;if(e.autoDownloadFontAwesome===!1&&(t=!1),e.autoDownloadFontAwesome!==!0)for(var n=document.styleSheets,r=0;r-1&&(t=!1);if(t){var i=document.createElement("link");i.rel="stylesheet",i.href="https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css",document.getElementsByTagName("head")[0].appendChild(i)}if(e.element)this.element=e.element;else if(null===e.element)return void console.log("SimpleMDE: Error. No element was found.");e.toolbar!==!1&&(e.toolbar=e.toolbar||D.toolbar),e.hasOwnProperty("status")||(e.status=["autosave","lines","words","cursor"]),e.previewRender||(e.previewRender=function(e){return this.parent.markdown(e)}),e.parsingConfig=e.parsingConfig||{},this.options=e,this.render(),e.initialValue&&this.value(e.initialValue)}var W=e("codemirror");e("codemirror/addon/edit/continuelist.js"),e("./codemirror/tablist"),e("codemirror/addon/display/fullscreen.js"),e("codemirror/mode/markdown/markdown.js"),e("codemirror/addon/mode/overlay.js"),e("codemirror/mode/gfm/gfm.js"),e("codemirror/mode/xml/xml.js"),e("spell-checker");var E=e("marked"),I=/Mac/.test(navigator.platform),P={"Cmd-B":s,"Cmd-I":c,"Cmd-K":b,"Cmd-H":f,"Shift-Cmd-H":p,"Cmd-Alt-I":w,"Cmd-'":h,"Cmd-Alt-L":x,"Cmd-L":y,"Cmd-Alt-C":d,"Cmd-P":T},F="",z={bold:{name:"bold",action:s,className:"fa fa-bold",title:"Bold (Ctrl+B)"},italic:{name:"italic",action:c,className:"fa fa-italic",title:"Italic (Ctrl+I)"},strikethrough:{name:"strikethrough",action:u,className:"fa fa-strikethrough",title:"Strikethrough"},heading:{name:"heading",action:f,className:"fa fa-header",title:"Heading (Ctrl+H)"},"heading-smaller":{name:"heading-smaller",action:f,className:"fa fa-header fa-header-x fa-header-smaller",title:"Smaller Heading (Ctrl+H)"},"heading-bigger":{name:"heading-bigger",action:p,className:"fa fa-header fa-header-x fa-header-bigger",title:"Bigger Heading (Shift+Ctrl+H)"},"heading-1":{name:"heading-1",action:m,className:"fa fa-header fa-header-x fa-header-1",title:"Big Heading"},"heading-2":{name:"heading-2",action:g,className:"fa fa-header fa-header-x fa-header-2",title:"Medium Heading"},"heading-3":{name:"heading-3",action:v,className:"fa fa-header fa-header-x fa-header-3",title:"Small Heading"},code:{name:"code",action:d,className:"fa fa-code",title:"Code (Ctrl+Alt+C)"},quote:{name:"quote",action:h,className:"fa fa-quote-left",title:"Quote (Ctrl+')"},"unordered-list":{name:"unordered-list",action:y,className:"fa fa-list-ul",title:"Generic List (Ctrl+L)"},"ordered-list":{name:"ordered-list",action:x,className:"fa fa-list-ol",title:"Numbered List (Ctrl+Alt+L)"},link:{name:"link",action:b,className:"fa fa-link",title:"Create Link (Ctrl+K)"},image:{name:"image",action:w,className:"fa fa-picture-o",title:"Insert Image (Ctrl+Alt+I)"},"horizontal-rule":{name:"horizontal-rule",action:C,className:"fa fa-minus",title:"Insert Horizontal Line"},preview:{name:"preview",action:T,className:"fa fa-eye no-disable",title:"Toggle Preview (Ctrl+P)"},"side-by-side":{name:"side-by-side",action:L,className:"fa fa-columns no-disable no-mobile",title:"Toggle Side by Side (F9)"},fullscreen:{name:"fullscreen",action:a,className:"fa fa-arrows-alt no-disable no-mobile",title:"Toggle Fullscreen (F11)"},guide:{name:"guide",action:"http://nextstepwebs.github.io/simplemde-markdown-editor/markdown-guide",className:"fa fa-question-circle",title:"Markdown Guide"}};D.toolbar=["bold","italic","heading","|","quote","unordered-list","ordered-list","|","link","image","|","preview","side-by-side","fullscreen","guide"],D.prototype.markdown=function(e){if(E){var t={};return this.options&&this.options.renderingConfig&&this.options.renderingConfig.singleLineBreaks!==!1&&(t.breaks=!0),this.options&&this.options.renderingConfig&&this.options.renderingConfig.codeSyntaxHighlighting===!0&&window.hljs&&(t.highlight=function(e){return window.hljs.highlightAuto(e).value}),E.setOptions(t),E(e)}},D.prototype.render=function(e){if(e||(e=this.element||document.getElementsByTagName("textarea")[0]),!this._rendered||this._rendered!==e){this.element=e;var t=this.options,n=this,i={};for(var o in P)!function(e){i[r(e)]=function(){P[e](n)}}(o);i.Enter="newlineAndIndentContinueMarkdownList",i.Tab="tabAndIndentMarkdownList",i["Shift-Tab"]="shiftTabAndUnindentMarkdownList",i.F11=function(){a(n)},i.F9=function(){L(n)},i.Esc=function(e){e.getOption("fullScreen")&&a(n)};var l,s;t.spellChecker!==!1?(l="spell-checker",s=t.parsingConfig,s.name="gfm",s.gitHubSpice=!1):(l=t.parsingConfig,l.name="gfm",l.gitHubSpice=!1),this.codemirror=W.fromTextArea(e,{mode:l,backdrop:s,theme:"paper",tabSize:void 0!=t.tabSize?t.tabSize:2,indentUnit:void 0!=t.tabSize?t.tabSize:2,indentWithTabs:t.indentWithTabs===!1?!1:!0,lineNumbers:!1,autofocus:t.autofocus===!0?!0:!1,extraKeys:i,lineWrapping:t.lineWrapping===!1?!1:!0,allowDropFileTypes:["text/plain"]}),t.toolbar!==!1&&this.createToolbar(),t.status!==!1&&this.createStatusbar(),void 0!=t.autosave&&t.autosave.enabled===!0&&this.autosave(),this.createSideBySide(),this._rendered=this.element}},D.prototype.autosave=function(){var e=this.value(),t=this;if(void 0==this.options.autosave.unique_id||""==this.options.autosave.unique_id)return void console.log("SimpleMDE: You must set a unique_id to use the autosave feature");null!=t.element.form&&void 0!=t.element.form&&t.element.form.addEventListener("submit",function(){localStorage.setItem(t.options.autosave.unique_id,"")}),this.options.autosave.loaded!==!0&&(null!=localStorage.getItem(this.options.autosave.unique_id)&&this.codemirror.setValue(localStorage.getItem(this.options.autosave.unique_id)),this.options.autosave.loaded=!0),localStorage&&localStorage.setItem(this.options.autosave.unique_id,e);var n=document.getElementById("autosaved");if(null!=n&&void 0!=n&&""!=n){var r=new Date,i=r.getHours(),o=r.getMinutes(),l="am",a=i;a>=12&&(a=i-12,l="pm"),0==a&&(a=12),o=10>o?"0"+o:o,n.innerHTML="Autosaved: "+a+":"+o+" "+l}setTimeout(function(){t.autosave()},this.options.autosave.delay||1e4)},D.prototype.createSideBySide=function(){var e=this.codemirror,t=e.getWrapperElement(),n=t.nextSibling;n&&/editor-preview-side/.test(n.className)||(n=document.createElement("div"),
+n.className="editor-preview-side",t.parentNode.insertBefore(n,t.nextSibling));var r=!1,i=!1;return e.on("scroll",function(e){if(r)return void(r=!1);i=!0;var t=e.getScrollInfo().height-e.getScrollInfo().clientHeight,o=parseFloat(e.getScrollInfo().top)/t,l=(n.scrollHeight-n.clientHeight)*o;n.scrollTop=l}),n.onscroll=function(){if(i)return void(i=!1);r=!0;var t=n.scrollHeight-n.clientHeight,o=parseFloat(n.scrollTop)/t,l=(e.getScrollInfo().height-e.getScrollInfo().clientHeight)*o;e.scrollTo(0,l)},!0},D.prototype.createToolbar=function(e){if(e=e||this.options.toolbar,e&&0!==e.length){var t;for(t=0;t