https://perchance.org/welcome

送信済みURL:
https://perchance.org/リダイレクト済み
レポート終了日:

リンク · 14件検出

リンクテキスト
https://www.youtube.com/@ChaoclypseChaoclypse
https://lemmy.world/c/perchancethe community forum
https://www.khanacademy.org/computing/computer-programming/html-cssHTML/CSS
https://www.khanacademy.org/computing/computer-programming/programmingJavaScript
https://www.khanacademy.org/computing/computer-programmingKhan Academy
https://scratch.mit.edu/Scratch
http://orteil.dashnet.org/randomgenrandomgen
https://github.com/rant-lang/rantRant
https://en.wikipedia.org/wiki/Domain-specific_languageDSL
https://chartopia.d12dev.com/Chartopia

JavaScript変数 · 72件検出

名前規模
0object
1object
onbeforetoggleobject
documentPictureInPictureobject
onscrollendobject
kvobject
initKvfunction
advertHeightnumber
queuedStatCountKeysobject
mdded4sfef4dsedddse1d34344string

コンソールログメッセージ · 10件検出

規模分類ログ
debugother
テキスト
let a of arr supported
debugother
URL
https://perchance.org/welcome
テキスト
transferSize (likely partial): 219622
warningother
URL
https://perchance.org/welcome
テキスト
Allow attribute will take precedence over 'allowfullscreen'.
verbosedom
URL
https://perchance.org/welcome
テキスト
[DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o
verbosedom
URL
https://perchance.org/welcome
テキスト
[DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o
verbosedom
URL
https://perchance.org/welcome
テキスト
[DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o
verbosedom
URL
https://perchance.org/welcome
テキスト
[DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o
verbosedom
URL
https://perchance.org/welcome
テキスト
[DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o
verbosedom
URL
https://perchance.org/welcome
テキスト
[DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o
debugother
テキスト
async/await is supported

HTML

<!DOCTYPE html><html class="no-js" lang=""><head>
        <meta charset="utf-8">
        <meta http-equiv="x-ua-compatible" content="ie=edge">

        <title>Create a Random Generator</title> 
        <meta name="description" content="">
        <meta id="viewportMetaEl" name="viewport" content="width=device-width, initial-scale=1, interactive-widget=resizes-content">

        <link rel="apple-touch-icon" href="apple-touch-icon.png">
        <link rel="icon" type="image/png" href="/favicon-32x32-white-bg.png" sizes="32x32">
        <link rel="icon" type="image/png" href="/favicon-16x16-white-bg.png" sizes="16x16">
        
        
        <meta property="og:type" content="website">
        <!-- <meta property="og:url" content="https://perchance.org/..." />  -->
        <meta property="og:image" content="https://perchance.org/api/getGeneratorScreenshot?generatorName=welcome">
        <!-- <meta property="og:description" content="..." /> -->

        <meta name="color-scheme" content="dark light">

        <link rel="preconnect" href="https://a257cd3ebbfff8e014d264b056e5727d.perchance.org">

        
        

        <style>
          :root {
            --menu-bar-height: 26px;
          }
        </style>

        <script>
          if(!Array.prototype.at) {
            window.addEventListener("error", function(message, url, linenumber) {
              console.warn(`JavaScript error: ${message} on line ${linenumber} for ${url}`, message, url, linenumber);
            });
          }
        </script>

        <script>
          {
            // attempt at solving virtual keyboard causing top-level scroll in mobile:
            // let viewport = document.querySelector("meta[name=viewport]");
            // viewport.setAttribute("content", viewport.content + `, height=${window.innerHeight}`);

            // window.visualViewport.addEventListener('resize', function (event) {
            // });

            // doesn't work properly - causes momentary screen jumping/flashing on keyboard close
            // if("virtualKeyboard" in navigator) {
            //   navigator.virtualKeyboard.overlaysContent = true;
            // }
          }
        </script>  

        <script>
          window.name = window.location.pathname; // for Notification.onclick to not open a new window if that generator is open already
        </script>

        <script>
          window.kv = null; // so `if(kv)...` doesn't throw error.
          window.initKv = async function() {
            if(window.kv) return;
            let idbKeyval = await import("https://cdn.jsdelivr.net/npm/idb-keyval@6/+esm");
            window.kv = new Proxy(idbKeyval, { // so api is like: kv.myStoreName.get(...)
              get(target, prop, receiver) {
                const store = idbKeyval.createStore(`${prop}-db`, `${prop}-store`);
                return {
                  get: (key) => idbKeyval.get(key, store),
                  set: (key, value) => idbKeyval.set(key, value, store),
                  delete: (key) => idbKeyval.del(key, store),
                  entries: () => idbKeyval.entries(store),
                };
              },
            });
          }
        </script>

        <script>
          (async function() { 
            let hasDynamicMetaData = "false" === "true";
            if(hasDynamicMetaData) {
              let urlQueryStringPart = new URLSearchParams(window.location.search).toString();
              if(urlQueryStringPart) urlQueryStringPart = "&" + urlQueryStringPart;
              let data = await fetch(`/api/getDynamicMetaData?__generatorName=${window.location.pathname.slice(1)}${urlQueryStringPart}`).then(r => r.json()).catch(console.error);
              if(data && data.success) {
                // NOTE: Be wary of script injection when editing.
                document.querySelector("meta[name='description']").content = data.description;
                document.title = data.title;
                document.querySelector("meta[property='og:image']").content = data.image;
              }
            }
          })(); 
        </script>
        
        

    </head>  
    <body style="overscroll-behavior:none;">
      
        <!-- <div id="controlHeightEl" style="height:100vh; width:0px; position: absolute;"></div> -->
        <script>
          // document.documentElement.style.height = controlHeightEl.clientHeight+"px";
          
          window.advertHeight = window.innerWidth > 700 ? 90 : 50; 

          // document.documentElement.style.height = `height:calc(100lvh + var(--menu-bar-height) + 1px)`;
          // function updateMainElHeight() {
          //   document.querySelector("#editorEl #main").style.height = `calc(100dvh - ${menuBarEl.offsetHeight - Math.abs(menuBarEl.getBoundingClientRect().top)}px - ${window.advertHeight+4+4}px)`;
          //   console.debug(document.querySelector("#editorEl #main").style.height)
          // }
          // window.addEventListener("scroll", updateMainElHeight);

          {
            const viewportMetaEl = document.querySelector('#viewportMetaEl');
            const originalViewportMetaContent = viewportMetaEl.getAttribute("content");
            let lastChangeTime = Date.now();
            const observer = new MutationObserver(mutations => {
              mutations.forEach(m => {
                if(m.type === 'attributes' && Date.now() - lastChangeTime > 2000) {
                  lastChangeTime = Date.now();
                  console.log("🚩🚩🚩 viewport meta tag content attribute was changed.");
                  viewportMetaEl.setAttribute("content", originalViewportMetaContent);
                  window.queuedStatCountKeys.add("viewport-mtc");
                }
              })
            });
            observer.observe(viewportMetaEl, { attributes: true, attributeFilter: ['content'] });
          }
        </script>
        
        <script>
          // 'polyfill' Safari's lack of support for interactive-widget=resizes-content on
          // EDIT: disabled because causes weird scroll jank issues
          // {
          //   let isTouchScreen = window.matchMedia("(pointer: coarse)").matches;
          //   if(isTouchScreen) {
          //     let shouldTriggerResizeBarrage = true;
          //     function handler() {
          //       if(!shouldTriggerResizeBarrage) return;
          //       shouldTriggerResizeBarrage = false;
          //       async function doResize() {
          //         // console.debug("doResize1", window.visualViewport.pageTop, window.visualViewport.height, document.documentElement.offsetHeight)
          //         shouldTriggerResizeBarrage = false; // just to be safe
          //         if(window.visualViewport.pageTop <= 1 && Math.abs(window.visualViewport.height - document.documentElement.offsetHeight) <= 2) return;
          //         console.debug("scrolling/resizing", window.visualViewport.pageTop, window.visualViewport.height, document.documentElement.offsetHeight);
          //         // for whatever reason, this is needed, since otherwise the scrollTo(0,0) doesn't work. Safari sucks.
          //         window.scrollTo(0,1);
          //         document.documentElement.style.height = window.visualViewport.height+"px";
          //         window.scrollTo(0,1);
          //       }
          //       let interval = setInterval(doResize, 100);
          //       doResize();
          //       setTimeout(() => {
          //         clearInterval(interval);
          //         doResize();
          //         setTimeout(() => shouldTriggerResizeBarrage=true, 50);
          //       }, 600);
          //     }
          //     window.addEventListener('scroll', handler);
          //     visualViewport.addEventListener('scroll', handler);
          //     visualViewport.addEventListener('resize', handler);
          //   }
          // }
        </script>

        <script>window.queuedStatCountKeys = new Set();</script>
        <script>window.mdded4sfef4dsedddse1d34344 = `Error: Ad blocker preventing proper page load?`;</script>
        <script>
          window.js0nparse = JSON.parse;
        </script>

        <script>
        // COMPATABILITY TESTING:
        window.canExecuteModernJavascript = true;
        try {

          var str = "";
          str += "\n(async () => { await new Promise(resolve => setTimeout(resolve, 500)); console.debug('async/await is supported'); })();";
          str += "\nlet arr = [1,2,3]; for(let a of arr) { arr[0]+=a; }; console.debug('let a of arr supported');";
          str += "\nvar m = [1,2,3].map(n => n+1);";
          str += "\nvar k = [...[1,2,3]];";
          str += "\nvar a = (function(a=3) { return a+1; })();";

          window.eval(str);

        } catch(e) {
          window.canExecuteModernJavascript = false;
          // var message = "<div style='overflow:hidden;height:0px;'><h1>Create a Random Generator</h1><p>Perhance is a platform that allows you to create your own custom random generators. You could use it to create your own random text generator for a tabletop RPG, or to make your own random name generator, or to simply create a generator which selects a random item from your list with different weights. You can also save your generator so it has a permalink that you can share with your friends.</p><p>Perchance is based around creating lists which reference other lists. You can adjust the odds of items in each list so that when you randomly select an item from that list, they all have the likelihoods that you've specified. If you'd like to learn how to make a random text generator with Perchance, you should check out the tutorial. It explains all the basic features of the Perchance engine and should get you up and running in only 10 or 20 minutes. Perchance has many powerful features that you don't need to learn from the start, but can learn later if you want to create particularly complex generators.</p><p>I created Perchance because most people are still creating random generators in excel. Those work all right, but excel and other spreadsheets aren't really made for that purpose, so there's a lot to be desired in terms of functionality and ease of use. Whatever you're using it for, I hope you find Perchance useful! You might like to check out our <a href='http://reddit.com/r/perchance'>community forum</a> where you can ask questions and share your random generator creations.</p></div>(」゚ロ゚)」 Sorry! Perchance uses cutting edge browser features that are only available in the latest versions of Chrome and Firefox. All modern browsers will eventually be supported, but for now you'll need to use one of those two. Sorry again for the inconvenience!";
          // //window.alert(message);
          // document.body.innerHTML = message;
        }
        </script>


        <div id="generator-description" style="display:none; height:20px; width:20px; z-index:-10; overflow:hidden; position:fixed; bottom:-100px;"><h1>Create a Random Generator</h1>
<h2> Welcome to Perchance! </h2>
 
	  
	<p>Perchance is a platform for creating and sharing random generators. To create a random generator you simply create lists which reference other lists:</p>

	<div>
<pre>output
  Your [pack] contains [item], [item] and [item].

item
  a few coins
  an old {silver|bronze} ring
  a handkerchief
  a shard of bone
  some lint
  a tin of tea leaves

pack
  purse
  backpack
  bag
  pack
  knapsack
  rucksack
</pre>
	<a href="/rg4m14ri#edit"><div>try this example</div></a>
	</div>
	<p>So, for example, whenever you write [pack], a random item from the "pack" list will be put in its place — as shown in <a href="https://user-uploads.perchance.org/file/bc63c883358b59575af1b8a142b90834.mp4">this animation</a> by <a href="https://perchance.org/vionet20-gens">Vionet20</a>.</p>
  <p><a href="/rg4m14ri#edit">Here's a link</a> to a minimal perchance generator based on the above code. You can edit it, save your own version, and share it.</p>
  <p>You can change the odds of items like so:</p>
<pre>pack
  purse
  backpack ^2
  bag
	...
</pre>
	<p>That makes "backpack" twice as likely as the others to be chosen when we write [pack]. You can import other peoples' generators like this:</p>
<pre>sentence
	I need a new {import:noun}.
	Quickly, {import:verb}!
</pre>
	<p>You can also alter the capitalisation, pluralisation, tense, and other properties of your word lists. Here's how to play around with capitalisation, for example:</p>
<pre>sentence
	[name.titleCase], can you hear me?
	HELLO, [name.upperCase]!

name
  patricia
	khalid
	anaya
	...
</pre>
	<p>There are a bunch of different shorthand notations to make common tasks easier:</p>
<pre>sentence
	{She|He} was about {15-20}0cm tall and was carrying {1-3} {import:concrete-noun}{s}.
</pre>
  <p>If you prefer to learn via video, here's a great unofficial introduction video by YouTuber <a href="https://www.youtube.com/@Chaoclypse">Chaoclypse</a> who is using Perchance to create RPG generators:</p>
  <p></p>
  <p>(btw, feel free to share your own Perchance video tutorials on <a href="https://lemmy.world/c/perchance">the community forum</a> - you might get featured here!)</p>
	<p>The above examples only show the <b>absolute basics</b> of Perchance! If you'd like to learn more, check out the <a href="/tutorial">tutorial</a>, and start playing around with a <a href="/minimal#edit">minimal example</a> (or the <a href="/basic-template#edit">basic template</a>). Check out the <a href="/examples">examples</a> page and this <a href="/aqr03wn1#edit">somewhat complexified version</a> of the previous backpack example for some demonstration of advanced features.</p>
	
	<div>
		<a href="/generators">View Creations 🔧</a>
		<a href="/minimal#edit">Start Creating ⚄</a>
		<a href="/tutorial">Keep Learning 📚</a>
	</div>
	
		
	<p>Once you've created a generator, you can <b>share it</b> with others by simply sharing the URL. You can change the URL in the "settings" menu. You can also <b>download it</b> as a single HTML file so that you can run it offline, or even turn it into a <a href="https://perchance.org/create-a-twitter-bot">Twitter bot</a> or a <a href="https://perchance.org/create-a-tumblr-bot">Tumblr bot</a>. It's also easy to <b>embed it</b> in your own website:</p>
<pre>&lt;iframe src="https://null.perchance.org/my-generator-name" style="width:100%; height:600px; border:none;"&gt;&lt;/iframe&gt;
</pre>
	<p>And a note to coders: It's also possible to <a href="/diy-perchance-api">set up an API server</a> for your generator, even though Perchance doesn't have an "official" API at this point. It's also possible to <a href="/perchance-discord-bot">set up a Discord bot</a> which will respond to messages with the output of a generator that you choose.</p>
	
	<p>We have a <a href="https://lemmy.world/c/perchance">Lemmy community</a> which you might like to join. Feel free to share your creations there or ask questions if you need some help building your generator. Also check out the work-in-progress <a href="/useful-generators">useful generators</a> list which might have some generators that you'd find useful for importing into your own generator.</p>
	<p>If you're looking for a simple random generator maker that just randomly selects one of several different items that you specify, then you can still use Perchance, but it might be a bit excessive for such a simple task. That said, the advantage of using Perchance for something like this is that it makes it really easy to add different weights/chances to each item and to extend it with some additional complexity later if needed. Perchance is designed to allow you to create random generators that have a lot of complexity, without requiring any existing coding knowledge, and with a fairly simple interface.</p>
  <p>Perchance is based on <a href="https://www.khanacademy.org/computing/computer-programming/html-css">HTML/CSS</a>, and <a href="https://www.khanacademy.org/computing/computer-programming/programming">JavaScript</a>, and my secret mission with Perchance is to get people interested in coding with a smooth, fun learning-curve. If you enjoy using Perchance and want to take your coding skills to the next level, you might like to learn to code on <a href="https://www.khanacademy.org/computing/computer-programming">Khan Academy</a>, or <a href="https://scratch.mit.edu/">Scratch</a>.</p>
	<p>Finally, Perchance's structure is based on other awesome random text languages like <a href="http://orteil.dashnet.org/randomgen">randomgen</a> by Orteil and <a href="https://github.com/rant-lang/rant">Rant</a> by TheBerkin. Many thanks to these and other <a href="https://en.wikipedia.org/wiki/Domain-specific_language">DSL</a> creators which inspired Perchance's syntax and functionality. You might also like to check out <a href="https://chartopia.d12dev.com/">Chartopia</a> - another website which allows you to make random generators.</p>
  <p>Thanks to the contributors behind these open source software projects:</p>
	<ul>
		<li><a href="https://github.com/nlp-compromise/compromise">Compromise</a>: A JavaScript natural language library that powers pastTense and pluralForm and the like.</li>
		<li><a href="https://nathancahill.github.io/Split.js/">Split.js</a>: A library that allows you to easily split the screen up into resizable quadrants.</li>
		<li><a href="https://mongodb.com">MongoDB</a>: A great document/NoSQL database. I use it for everything.</li>
		<li><a href="https://expressjs.com">Express.js</a>: Helps serve you this webpage and power the internal APIs of Perchance.</li>
	</ul>
	<p>Thanks for checking out Perchance! •ᴗ•</p>

<p>⚄︎</p>
<p><a href="/privacy-policy">Privacy Policy</a></p>
<br>


</div>

        <style>
          .cm-syntax-style1 { color: #f18c16; }
          .cm-syntax-style2 { color: #2795EE; }
          .cm-syntax-style3-comment { color: #A0A1A7; }
        </style>

        <!-- PRELOADED DATA --> 
        <script id="preloaded-generator-data" type="notjs">%7B%22name%22:%22welcome%22,%22modelText%22:%22$meta%5Cn%20%20title%20=%20Create%20a%20Random%20Generator%5Cn%5Cnpride%20=%20%7Bimport:pride-plugin%7D%20%20%20%20%5Cn%5Cn//%20Yep,%20even%20the%20Perchance%20homepage%20is%20technically%20a%20%5C%22generator%5C%22,%20but%20all%20the%20content%20is%20in%20the%20HTML%20code%20in%20the%20lower-right%20editor.%22,%22outputTemplate%22:%22%3Ch1%20style=%5C%22position:fixed;%20top:-1000px;%5C%22%3ECreate%20a%20Random%20Generator%3C/h1%3E%5Cn%3Ch2%20id=%5C%22header%5C%22%20style=%5C%22font-size:200%25;%20margin-top:1em;%5C%22%3E%5Bpride()%5D%20Welcome%20to%20Perchance!%20%5Bpride()%5D%3C/h2%3E%5Cn%3Cmain%3E%20%5Cn%5Ct%20%20%5Cn%5Ct%3Cp%3EPerchance%20is%20a%20platform%20for%20creating%20and%20sharing%20random%20generators.%20To%20create%20a%20random%20generator%20you%20simply%20create%20lists%20which%20reference%20other%20lists:%3C/p%3E%5Cn%5Cn%5Ct%3Cdiv%20style=%5C%22position:relative;%5C%22%3E%5Cn%3Cpre%3Eoutput%5Cn%20%20Your%20%5C%5C%5Bpack%5C%5C%5D%20contains%20%5C%5C%5Bitem%5C%5C%5D,%20%5C%5C%5Bitem%5C%5C%5D%20and%20%5C%5C%5Bitem%5C%5C%5D.%5Cn%5Cnitem%5Cn%20%20a%20few%20coins%5Cn%20%20an%20old%20%5C%5C%7Bsilver%7Cbronze%5C%5C%7D%20ring%5Cn%20%20a%20handkerchief%5Cn%20%20a%20shard%20of%20bone%5Cn%20%20some%20lint%5Cn%20%20a%20tin%20of%20tea%20leaves%5Cn%5Cnpack%5Cn%20%20purse%5Cn%20%20backpack%5Cn%20%20bag%5Cn%20%20pack%5Cn%20%20knapsack%5Cn%20%20rucksack%5Cn%3C/pre%3E%5Cn%5Ct%3Ca%20href=%5C%22/rg4m14ri#edit%5C%22%3E%3Cdiv%20style=%5C%22position:absolute;bottom:1em;right:1em;background:%20#14b914;%20background:light-dark(#14b914,%20#038003);%20color:white;padding:%200.5em;width:max-content;border-radius:%202px;%20box-shadow:%200px%202px%202px%20#0000005c;%5C%22%3Etry%20this%20example%3C/div%3E%3C/a%3E%5Cn%5Ct%3C/div%3E%5Cn%5Ct%3Cp%3ESo,%20for%20example,%20whenever%20you%20write%20%3Ccode%3E%5C%5C%5Bpack%5C%5C%5D%3C/code%3E,%20a%20random%20item%20from%20the%20%5C%22pack%5C%22%20list%20will%20be%20put%20in%20its%20place%20%E2%80%94%20as%20shown%20in%20%3Ca%20href=%5C%22https://user-uploads.perchance.org/file/bc63c883358b59575af1b8a142b90834.mp4%5C%22%20target=%5C%22_blank%5C%22%3Ethis%20animation%3C/a%3E%20by%20%3Ca%20href=%5C%22https://perchance.org/vionet20-gens%5C%22%20target=%5C%22_blank%5C%22%3EVionet20%3C/a%3E.%3C/p%3E%5Cn%20%20%3Cp%3E%3Ca%20href=%5C%22/rg4m14ri#edit%5C%22%3EHere's%20a%20link%3C/a%3E%20to%20a%20minimal%20perchance%20generator%20based%20on%20the%20above%20code.%20You%20can%20edit%20it,%20save%20your%20own%20version,%20and%20share%20it.%3C/p%3E%5Cn%20%20%3Cp%3EYou%20can%20change%20the%20odds%20of%20items%20like%20so:%3C/p%3E%5Cn%3Cpre%3E%5Cnpack%5Cn%20%20purse%5Cn%20%20backpack%20%5E2%5Cn%20%20bag%5Cn%5Ct...%5Cn%3C/pre%3E%5Cn%5Ct%3Cp%3EThat%20makes%20%5C%22backpack%5C%22%20twice%20as%20likely%20as%20the%20others%20to%20be%20chosen%20when%20we%20write%20%3Ccode%3E%5C%5C%5Bpack%5C%5C%5D%3C/code%3E.%20You%20can%20import%20other%20peoples'%20generators%20like%20this:%3C/p%3E%5Cn%3Cpre%3E%5Cnsentence%5Cn%5CtI%20need%20a%20new%20%5C%5C%7Bimport:noun%5C%5C%7D.%5Cn%5CtQuickly,%20%5C%5C%7Bimport:verb%5C%5C%7D!%5Cn%3C/pre%3E%5Cn%5Ct%3Cp%3EYou%20can%20also%20alter%20the%20capitalisation,%20pluralisation,%20tense,%20and%20other%20properties%20of%20your%20word%20lists.%20Here's%20how%20to%20play%20around%20with%20capitalisation,%20for%20example:%3C/p%3E%5Cn%3Cpre%3E%5Cnsentence%5Cn%5Ct%5C%5C%5Bname.titleCase%5C%5C%5D,%20can%20you%20hear%20me?%5Cn%5CtHELLO,%20%5C%5C%5Bname.upperCase%5C%5C%5D!%5Cn%5Cnname%5Cn%20%20patricia%5Cn%5Ctkhalid%5Cn%5Ctanaya%5Cn%5Ct...%5Cn%3C/pre%3E%5Cn%5Ct%3Cp%3EThere%20are%20a%20bunch%20of%20different%20shorthand%20notations%20to%20make%20common%20tasks%20easier:%3C/p%3E%5Cn%3Cpre%3E%5Cnsentence%5Cn%5Ct%5C%5C%7BShe%7CHe%5C%5C%7D%20was%20about%20%5C%5C%7B15-20%5C%5C%7D0cm%20tall%20and%20was%20carrying%20%5C%5C%7B1-3%5C%5C%7D%20%5C%5C%7Bimport:concrete-noun%5C%5C%7D%5C%5C%7Bs%5C%5C%7D.%5Cn%3C/pre%3E%5Cn%20%20%3Cp%3EIf%20you%20prefer%20to%20learn%20via%20video,%20here's%20a%20great%20unofficial%20introduction%20video%20by%20YouTuber%20%3Ca%20href=%5C%22https://www.youtube.com/@Chaoclypse%5C%22%20target=%5C%22_blank%5C%22%3EChaoclypse%3C/a%3E%20who%20is%20using%20Perchance%20to%20create%20RPG%20generators:%3C/p%3E%5Cn%20%20%3Cp%20style=%5C%22text-align:center;%20margin-bottom:0.1rem;%5C%22%3E%3Ciframe%20width=%5C%22560%5C%22%20height=%5C%22315%5C%22%20style=%5C%22max-width:100%25;%5C%22%20src=%5C%22https://www.youtube-nocookie.com/embed/2DRSuHDPU6I?si=_Ih0m9pR5qt-2b0h&autoplay=0&rel=0%5C%22%20title=%5C%22YouTube%20video%20player%5C%22%20frameborder=%5C%220%5C%22%20allow=%5C%22accelerometer;%20autoplay;%20clipboard-write;%20encrypted-media;%20gyroscope;%20picture-in-picture;%20web-share%5C%22%20allowfullscreen%3E%3C/iframe%3E%3C/p%3E%5Cn%20%20%3Cp%20style=%5C%22text-align:center;%20margin-top:0.1rem;%20font-size:70%25;%5C%22%3E(btw,%20feel%20free%20to%20share%20your%20own%20Perchance%20video%20tutorials%20on%20%3Ca%20href=%5C%22https://lemmy.world/c/perchance%5C%22%20target=%5C%22_blank%5C%22%3Ethe%20community%20forum%3C/a%3E%20-%20you%20might%20get%20featured%20here!)%3C/p%3E%5Cn%5Ct%3Cp%3EThe%20above%20examples%20only%20show%20the%20%3Cb%3Eabsolute%20basics%3C/b%3E%20of%20Perchance!%20If%20you'd%20like%20to%20learn%20more,%20check%20out%20the%20%3Ca%20href=%5C%22/tutorial%5C%22%3Etutorial%3C/a%3E,%20and%20start%20playing%20around%20with%20a%20%3Ca%20href=%5C%22/minimal#edit%5C%22%3Eminimal%20example%3C/a%3E%20(or%20the%20%3Ca%20href=%5C%22/basic-template#edit%5C%22%3Ebasic%20template%3C/a%3E).%20Check%20out%20the%20%3Ca%20href=%5C%22/examples%5C%22%3Eexamples%3C/a%3E%20page%20and%20this%20%3Ca%20href=%5C%22/aqr03wn1#edit%5C%22%3Esomewhat%20complexified%20version%3C/a%3E%20of%20the%20previous%20backpack%20example%20for%20some%20demonstration%20of%20advanced%20features.%3C/p%3E%5Cn%5Ct%5Cn%5Ct%3Cdiv%20id=%5C%22next-step-buttons-ctn%5C%22%20style=%5C%22display:flex;%20justify-content:center;%5C%22%3E%5Cn%5Ct%5Ct%3Ca%20href=%5C%22/generators%5C%22%20style=%5C%22display:inline-block;%20text-decoration:none;%20color:white;%20background:%20#ea8616;%20background:light-dark(#ea8616,%20#cd730f);%20border:0;%20outline:none;%20padding:0.5em%201em;%20border-radius:3px;%20font-size:120%25;%20margin:1em;%20box-shadow:%200px%202px%202px%20#00000029;%5C%22%3EView%20Creations%20%F0%9F%94%A7%3C/a%3E%5Cn%5Ct%5Ct%3Ca%20href=%5C%22/minimal#edit%5C%22%20style=%5C%22display:inline-block;%20text-decoration:none;%20color:white;%20background:%20#00a3ce;%20background:light-dark(#ea8616,%20#0084a7);%20border:0;%20outline:none;%20padding:0.5em%201em;%20border-radius:3px;%20font-size:120%25;%20margin:1em;%20box-shadow:%200px%202px%202px%20#00000029;%5C%22%3EStart%20Creating%20%E2%9A%84%3C/a%3E%5Cn%5Ct%5Ct%3Ca%20href=%5C%22/tutorial%5C%22%20style=%5C%22display:inline-block;%20text-decoration:none;%20color:white;%20background:%20#4abe4a;%20background:light-dark(#4abe4a,%20#0f8e0f);%20border:0;%20outline:none;%20padding:0.5em%201em;%20border-radius:3px;%20font-size:120%25;%20margin:1em;%20box-shadow:%200px%202px%202px%20#00000029;%5C%22%3EKeep%20Learning%20%F0%9F%93%9A%3C/a%3E%5Cn%5Ct%3C/div%3E%5Cn%5Ct%3Cstyle%3E%5Cn%5Ct@media%20screen%20and%20(max-width:%20800px)%20%7B%5Cn%5Ct%5Ct#next-step-buttons-ctn%20%7B%5Cn%5Ct%5Ct%5Ct%20flex-direction:column;%5Cn%5Ct%5Ct%7D%5Cn%5Ct%7D%5Cn%5Ct%3C/style%3E%5Cn%5Ct%5Ct%5Cn%5Ct%3Cp%3EOnce%20you've%20created%20a%20generator,%20you%20can%20%3Cb%3Eshare%20it%3C/b%3E%20with%20others%20by%20simply%20sharing%20the%20URL.%20You%20can%20change%20the%20URL%20in%20the%20%5C%22settings%5C%22%20menu.%20You%20can%20also%20%3Cb%3Edownload%20it%3C/b%3E%20as%20a%20single%20HTML%20file%20so%20that%20you%20can%20run%20it%20offline,%20or%20even%20turn%20it%20into%20a%20%3Ca%20href=%5C%22https://perchance.org/create-a-twitter-bot%5C%22%20target=%5C%22_blank%5C%22%20style=%5C%22font-weight:bold;%5C%22%3ETwitter%20bot%3C/a%3E%20or%20a%20%3Ca%20href=%5C%22https://perchance.org/create-a-tumblr-bot%5C%22%20target=%5C%22_blank%5C%22%20style=%5C%22font-weight:bold;%5C%22%3ETumblr%20bot%3C/a%3E.%20It's%20also%20easy%20to%20%3Cb%3Eembed%20it%3C/b%3E%20in%20your%20own%20website:%3C/p%3E%5Cn%3Cpre%3E%5Cn&lt;iframe%20src=%5C%22https://null.perchance.org/my-generator-name%5C%22%20style=%5C%22width:100%25;%20height:600px;%20border:none;%5C%22&gt;&lt;/iframe&gt;%5Cn%3C/pre%3E%5Cn%5Ct%3Cp%3EAnd%20a%20note%20to%20coders:%20It's%20also%20possible%20to%20%3Ca%20href=%5C%22/diy-perchance-api%5C%22%3Eset%20up%20an%20API%20server%3C/a%3E%20for%20your%20generator,%20even%20though%20Perchance%20doesn't%20have%20an%20%5C%22official%5C%22%20API%20at%20this%20point.%20It's%20also%20possible%20to%20%3Ca%20href=%5C%22/perchance-discord-bot%5C%22%20target=%5C%22_blank%5C%22%20style=%5C%22font-weight:bold;%5C%22%3Eset%20up%20a%20Discord%20bot%3C/a%3E%20which%20will%20respond%20to%20messages%20with%20the%20output%20of%20a%20generator%20that%20you%20choose.%3C/p%3E%5Cn%5Ct%5Cn%5Ct%3Cp%3EWe%20have%20a%20%3Ca%20href=%5C%22https://lemmy.world/c/perchance%5C%22%3ELemmy%20community%3C/a%3E%20which%20you%20might%20like%20to%20join.%20Feel%20free%20to%20share%20your%20creations%20there%20or%20ask%20questions%20if%20you%20need%20some%20help%20building%20your%20generator.%20Also%20check%20out%20the%20work-in-progress%20%3Ca%20href=%5C%22/useful-generators%5C%22%3Euseful%20generators%3C/a%3E%20list%20which%20might%20have%20some%20generators%20that%20you'd%20find%20useful%20for%20importing%20into%20your%20own%20generator.%3C/p%3E%5Cn%5Ct%3Cp%3EIf%20you're%20looking%20for%20a%20simple%20random%20generator%20maker%20that%20just%20randomly%20selects%20one%20of%20several%20different%20items%20that%20you%20specify,%20then%20you%20can%20still%20use%20Perchance,%20but%20it%20might%20be%20a%20bit%20excessive%20for%20such%20a%20simple%20task.%20That%20said,%20the%20advantage%20of%20using%20Perchance%20for%20something%20like%20this%20is%20that%20it%20makes%20it%20really%20easy%20to%20add%20different%20weights/chances%20to%20each%20item%20and%20to%20extend%20it%20with%20some%20additional%20complexity%20later%20if%20needed.%20Perchance%20is%20designed%20to%20allow%20you%20to%20create%20random%20generators%20that%20have%20a%20lot%20of%20complexity,%20without%20requiring%20any%20existing%20coding%20knowledge,%20and%20with%20a%20fairly%20simple%20interface.%3C/p%3E%5Cn%20%20%3Cp%3EPerchance%20is%20based%20on%20%3Ca%20href=%5C%22https://www.khanacademy.org/computing/computer-programming/html-css%5C%22%20target=%5C%22_blank%5C%22%3EHTML/CSS%3C/a%3E,%20and%20%3Ca%20href=%5C%22https://www.khanacademy.org/computing/computer-programming/programming%5C%22%20target=%5C%22_blank%5C%22%3EJavaScript%3C/a%3E,%20and%20my%20secret%20mission%20with%20Perchance%20is%20to%20get%20people%20interested%20in%20coding%20with%20a%20smooth,%20fun%20learning-curve.%20If%20you%20enjoy%20using%20Perchance%20and%20want%20to%20take%20your%20coding%20skills%20to%20the%20next%20level,%20you%20might%20like%20to%20learn%20to%20code%20on%20%3Ca%20href=%5C%22https://www.khanacademy.org/computing/computer-programming%5C%22%20target=%5C%22_blank%5C%22%3EKhan%20Academy%3C/a%3E,%20or%20%3Ca%20href=%5C%22https://scratch.mit.edu/%5C%22%20target=%5C%22_blank%5C%22%3EScratch%3C/a%3E.%3C/p%3E%5Cn%5Ct%3Cp%3EFinally,%20Perchance's%20structure%20is%20based%20on%20other%20awesome%20random%20text%20languages%20like%20%3Ca%20href=%5C%22http://orteil.dashnet.org/randomgen%5C%22%3Erandomgen%3C/a%3E%20by%20Orteil%20and%20%3Ca%20href=%5C%22https://github.com/rant-lang/rant%5C%22%3ERant%3C/a%3E%20by%20TheBerkin.%20Many%20thanks%20to%20these%20and%20other%20%3Ca%20href=%5C%22https://en.wikipedia.org/wiki/Domain-specific_language%5C%22%3EDSL%3C/a%3E%20creators%20which%20inspired%20Perchance's%20syntax%20and%20functionality.%20You%20might%20also%20like%20to%20check%20out%20%3Ca%20href=%5C%22https://chartopia.d12dev.com/%5C%22%3EChartopia%3C/a%3E%20-%20another%20website%20which%20allows%20you%20to%20make%20random%20generators.%3C/p%3E%5Cn%20%20%3Cp%3EThanks%20to%20the%20contributors%20behind%20these%20open%20source%20software%20projects:%3C/p%3E%5Cn%5Ct%3Cul%20style=%5C%22text-align:left;%5C%22%3E%5Cn%5Ct%5Ct%3Cli%3E%3Ca%20href=%5C%22https://github.com/nlp-compromise/compromise%5C%22%3ECompromise%3C/a%3E:%20A%20JavaScript%20natural%20language%20library%20that%20powers%20%3Ccode%3EpastTense%3C/code%3E%20and%20%3Ccode%3EpluralForm%3C/code%3E%20and%20the%20like.%3C/li%3E%5Cn%5Ct%5Ct%3Cli%3E%3Ca%20href=%5C%22https://nathancahill.github.io/Split.js/%5C%22%3ESplit.js%3C/a%3E:%20A%20library%20that%20allows%20you%20to%20easily%20split%20the%20screen%20up%20into%20resizable%20quadrants.%3C/li%3E%5Cn%5Ct%5Ct%3Cli%3E%3Ca%20href=%5C%22https://mongodb.com%5C%22%3EMongoDB%3C/a%3E:%20A%20great%20document/NoSQL%20database.%20I%20use%20it%20for%20everything.%3C/li%3E%5Cn%5Ct%5Ct%3Cli%3E%3Ca%20href=%5C%22https://expressjs.com%5C%22%3EExpress.js%3C/a%3E:%20Helps%20serve%20you%20this%20webpage%20and%20power%20the%20internal%20APIs%20of%20Perchance.%3C/li%3E%5Cn%5Ct%3C/ul%3E%5Cn%5Ct%3Cp%3EThanks%20for%20checking%20out%20Perchance!%20%E2%80%A2%E1%B4%97%E2%80%A2%3C/p%3E%5Cn%3C/main%3E%5Cn%3Cp%20style=%5C%22text-align:center;%20font-size:200%25;%20opacity:0.2;%20margin-top:0.5em;%5C%22%3E%3Cspan%3E%E2%9A%84&#xFE0E;%3C/span%3E%3C/p%3E%5Cn%3Cp%20style=%5C%22margin-bottom:2rem;%20font-size:80%25;%20color:grey;%20text-align:center;%5C%22%3E%3Ca%20style=%5C%22color:inherit;%20text-decoration:none;%5C%22%20href=%5C%22/privacy-policy%5C%22%20target=%5C%22_blank%5C%22%3EPrivacy%20Policy%3C/a%3E%3C/p%3E%5Cn%3Cbr/%3E%5Cn%5Cn%3Cstyle%3E%5Cn%5Ctul%20li%20%7B%5Cn%5Ct%5Ctmargin-top:0.5em;%5Ct%5Cn%5Ct%7D%5Cn%5Ctmain%20%7B%5Cn%5Ct%5Ctmax-width:900px;%5Cn%5Ct%5Ctmargin:0%20auto;%5Cn%5Ct%5Ctbackground:%20#fff;%5Cn%5Ct%5Ctbackground:%20light-dark(#fff,%20#101010);%5Cn%5Ct%5Ctpadding:1em;%5Cn%5Ct%5Ctborder-radius:3px;%5Cn%5Ct%5Ctbox-shadow:%200%200.5px%200%200%20#ffffff%20inset,%200%201px%202px%200%20#B3B3B3;%5Cn%5Ct%5Ctbox-shadow:%200%200.5px%200%200%20light-dark(#fff,%20#060606)%20inset,%200%201px%202px%200%20light-dark(#B3B3B3,%20#2c2c2c);%5Cn%5Ct%7D%5Cn%20%20main%20p:first-child%20%7B%5Cn%20%20%20%20margin-top:0;%5Cn%20%20%7D%5Cn%5Ctp%20%7B%20text-align:left;%20line-height:%201.4em;%20%7D%5Cn%5Ctbody%20%7B%5Cn%5Ct%5Ctbackground-color:#f6f6f6;%5Ct%5Cn%5Ct%5Ctbackground-color:%20light-dark(#f6f6f6,%20#000);%5Cn%20%20%20%20color:%20black;%5Cn%20%20%20%20color:%20light-dark(black,%20#d6d6d6);%5Cn%5Ct%7D%5Cn%5Ctpre%20%7B%5Cn%5Ct%5Cttext-align:left;%5Cn%5Ct%5Ctbackground:%20#333;%5Cn%20%20%20%20background:%20light-dark(#333,%20#212121);%5Cn%20%20%20%20color:%20#e2e2e2;%5Cn%20%20%20%20padding:%201em;%5Cn%20%20%20%20border-radius:%202px;%5Cn%5Ct%5Cttab-size:%202;%5Cn%5Ct%5Ct-moz-tab-size:%202;%5Cn%5Ct%5Ct-o-tab-size:%202;%5Cn%5Ct%5Ct-webkit-tab-size:%202;%5Cn%5Ct%7D%5Cn%5Ctcode%20%7B%5Cn%5Ct%5Ctbackground:%20#eff0f1;%5Cn%20%20%20%20background:%20%20light-dark(#eff0f1,%20#272727);%5Cn%20%20%20%20padding:%201px%205px;%5Ct%5Cn%5Ct%5Ctwhite-space:%20nowrap;%5Cn%5Ct%7D%5Cn%5Cth2%20%7B%5Cn%5Ct%5Ctmargin-top:%201.5em;%5Ct%5Cn%5Ct%7D%5Cn%5Ct@media%20screen%20and%20(max-width:%20600px)%20%7B%5Cn%5Ct%5Ct#header%20%7B%5Cn%5Ct%5Ct%5Ctfont-size:1.6rem%20!important;%5Cn%5Ct%5Ct%7D%20%5Cn%5Ct%7D%5Cn%20%20html%20%7B%20color-scheme:%20dark%20light%20%7D%5Cn%3C/style%3E%5Cn%22,%22imports%22:%5B%22pride-plugin%22%5D,%22canLink%22:true,%22isPrivate%22:true%7D</script>
        <script id="imported-generators" type="notjs">%5B%7B%22name%22:%22pride-plugin%22,%22modelText%22:%22$output(size,%20forceShow)%20=%3E%5Cn%20%20if(size%20===%20undefined%20%7C%7C%20typeof%20size%20===%20%5C%22number%5C%22)%20%7B%5Cn%20%20%20%20//%20simple%20mode:%5Cn%20%20%20%20if(size%20===%20undefined)%20size%20=%201;%20%5Cn%20%20%20%20if(typeof%20size%20!==%20%5C%22number%5C%22)%20return%20%5C%22You%20gave%20this%20plugin%20something%20other%20than%20a%20number?%5C%22;%5Cn%20%20%20%20if(forceShow%20%7C%7C%20new%20Date().getUTCMonth()%20===%205)%20return%20%60%3Cimg%20class=%5C%22pride-plugin-image%5C%22%20style=%5C%22height:$%7Bsize*1.4%7Drem%5C%22%20src=%5C%22data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAB4CAMAAAAOusbgAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAACslBMVEUAAAD/AAr/ADD/ADD/AA75ABL/AA3/AA36ABH/AA3/AA7/AA7/AA7/AA7/AA7/AA7/AAz/AA3/AA7/AA7/AA3/AA7/AA6IAIKIAIKIAIKIAIKHAIKHAIKGAIKIAIKIAIKIAICIAIKIAIKIAIKbAHGGAIOHAISWAG+IAIKFAH+WAJSWAJT/AA3/AA7/AA7/AA7/AA7/AA7/AA7+AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7+AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA3/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/AA7/Bg3/Bg2BB4SIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKJAIKHAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIL/AA7/AA7/AA7/AA7/AA7/AA7/AA7/Bg3/QQP/QQP/UQD/UAD/TgD9iQ760yD60iD60iD80yDfyiQulzoRjj4Tjz4Tjz4Tjz0mcHU2V6E1WKA0WaBESJtESJuBB4SIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIKIAIL///+qtFapAAAAvXRSTlMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkiP1JZWloBIGy24fT7/v7+4bZtIQERcNj9/dhyEimx+/uyKjLLzDIjxsYjCaGgCVb29VUQurkQTPHxTJWVzc3s7Pr6////+vrs683NlZRM8fFLELm5EFX19VQJoKAJI8bGIzHLyzEpsfv7sikRcNf9/dhyEgEgbLXh9Pv+/v779OG2bSABCCI/UllaWllSPyLK/1dpAAAAAWJLR0TlWGULvwAAAAd0SU1FB+MDBhQZFdM9oN8AAAGiSURBVGje7do9a5RBFIbhc585GxGx8hcETKGgqQxBLFRQ/A3RwlokoASinVUIWPhZWKUTLERrMdiIbarArlWqYGsR1MLdYyGL9rpzRJ672fJiZuejeMdMKaWUUtUx/eUkAMxMyszMHE7ydzgWmTZTeADw/vsUHnAOuGNGznZ209gE3vHNDLPDnIfb2eu/fQDbHBh2lIusZvZbVu0x23xuduRSuznpuZ5zaWdh72s7drndGHfeSss7C5/Cg96ujQPnOtcKjo/nREB2dwk8rvZ3LVfcfVww0zZ2dypgPDwq5PRwrxkxH0qm2jJKBmxG1AzYrGjAggULFvwHlR2Zgv9/WPtYsGDBOjIF63YSLFiwbifBgrWdBAsWrGvxr8JFH7za3BkqelY1Yg8vgr1oWVd9uHZ336qAtyJqNnLQRsu7p7q7L9rT8KD1fhgRgbeD0dnh6a6PjGzwsj3cd/Mn7dVcT/fQ68GjMMzmuQVXurlv4P5kzzCz46wBF7rM8ltgg4/Tx4Lz93o8Fmw/hfWR2a/nkSfYnDXswJe7k6EppZRS6h/pBzoNVGWAoj5vAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE5LTAzLTA2VDIwOjI1OjA4KzAwOjAwSBEKDQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOS0wMy0wNlQyMDoyNTowOCswMDowMDlMsrEAAAAASUVORK5CYII=%5C%22/%3E%60;%5Cn%20%20%20%20else%20return%20%5C%22%5C%22;%5Cn%20%20%7D%20else%20%7B%5Cn%20%20%20%20//%20advanced/custom%20mode:%5Cn%20%20%20%20let%20options%20=%20size;%5Cn%20%20%20%20let%20monthNameToMonthIndex%20=%20%7B%5Cn%20%20%20%20%20%20%5C%22january%5C%22:%200,%5Cn%20%20%20%20%20%20%5C%22february%5C%22:%201,%5Cn%20%20%20%20%20%20%5C%22march%5C%22:%202,%5Cn%20%20%20%20%20%20%5C%22april%5C%22:%203,%5Cn%20%20%20%20%20%20%5C%22may%5C%22:%204,%5Cn%20%20%20%20%20%20%5C%22june%5C%22:%205,%5Cn%20%20%20%20%20%20%5C%22july%5C%22:%206,%5Cn%20%20%20%20%20%20%5C%22august%5C%22:%207,%5Cn%20%20%20%20%20%20%5C%22september%5C%22:%208,%5Cn%20%20%20%20%20%20%5C%22october%5C%22:%209,%5Cn%20%20%20%20%20%20%5C%22november%5C%22:%2010,%5Cn%20%20%20%20%20%20%5C%22december%5C%22:%2011,%5Cn%20%20%20%20%7D;%5Cn%20%20%20%20let%20date%20=%20new%20Date();%5Cn%20%20%20%20let%20currentMonthIndex%20=%20date.getUTCMonth();%5Cn%20%20%20%20let%20currentDayOfMonth%20=%20date.getDate();%5Cn%20%20%20%20%5Cn%20%20%20%20let%20names%20=%20options.getPropertyNames;%5Cn%20%20%20%20for(let%20name%20of%20names)%20%7B%5Cn%20%20%20%20%20%20let%20monthName%20=%20name.split(/%5C%5Cs+/)%5B0%5D;%5Cn%20%20%20%20%20%20let%20dayOfMonthNumber%20=%20name.split(/%5C%5Cs+/)%5B1%5D;%5Cn%20%20%20%20%20%20if(monthNameToMonthIndex%5BmonthName%5D%20!==%20undefined%20&&%20monthNameToMonthIndex%5BmonthName%5D%20===%20currentMonthIndex)%20%7B%5Cn%20%20%20%20%20%20%20%20if(dayOfMonthNumber%20===%20undefined%20%7C%7C%20Number(dayOfMonthNumber)%20===%20currentDayOfMonth)%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20return%20options%5Bname%5D;%5Cn%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%7D%5Cn%20%20%20%20%7D%5Cn%20%20%20%20return%20options.default%20%7C%7C%20%5C%22%5C%22;%5Cn%20%20%7D%22,%22imports%22:%5B%5D,%22lastEditTime%22:1696389136663,%22found%22:true%7D%5D</script>
        <script id="imported-generator-names" type="notjs">%5B%22pride-plugin%22%5D</script>
        <script id="static-meta-data" type="notjs">%7B%22title%22:%22Create%20a%20Random%20Generator%22,%22description%22:%22%22,%22image%22:%22%22%7D</script>
        <script id="this-html-server-render-time" type="notjs">1729872381666</script>
        
        
        <script>window.generatorPublicId = "a257cd3ebbfff8e014d264b056e5727d";</script> 
        <script>window.generatorLastSaveTime = Number(`1724944183253`);</script>
        <script>window.shouldLiftGeneratorHtmlFromEmbed = !!Number(`1`);</script>


        <script>
          window.currentInterfaceMode = "view";

          window.generatorData = window.js0nparse( decodeURI( document.querySelector("#preloaded-generator-data").innerText ) );
          window.generatorName = window.generatorData.name;
          window.generatorCanLinkWithRelFollow = generatorData.canLink; // make it global so the iframe-based description update has access to it.

          window.generatorStaticMetaData = {};
          try {
            window.generatorStaticMetaData = window.js0nparse( decodeURI( document.querySelector("#static-meta-data").innerText ) );
          } catch(e) { console.error(e); }

          // For crawlers that don't have es6 support and thus can't execute the iframe:
          if(!window.canExecuteModernJavascript) {
            window.addEventListener("DOMContentLoaded", function() {
              var data = window.generatorData;

              window.document.querySelector("#generator-description").innerHTML = DOMPurify.sanitize(data.outputTemplate, {ALLOWED_TAGS: ['h1', 'h2', 'h3', 'h4', 'p', 'div', 'pre', 'button', 'i', 'b', 'a', 'ul', 'li', 'br', 'ol', 'hr', 'span'], ALLOWED_ATTR: ['href']});
              var pre = document.createElement('pre');
              pre.innerText = data.modelText.slice(0, 10000);
              window.document.querySelector("#generator-description").appendChild(pre);
              
              window.document.querySelector("#generator-description").style.cssText = "";

              // EDIT: Removed, since title is hardcoded in HTML now.
              // var h1 = window.document.querySelector("#generator-description h1");
              // if(!h1) h1 = window.document.querySelector("#generator-description h2");
              // if(h1) {
              //   var h1Text = h1.textContent;
              //   if(h1Text !== "Your Generator's Title" && h1Text !== "Minimal Example" && h1Text.indexOf("[") === -1 && h1Text.indexOf("]") === -1) {
              //     document.title = h1Text+" ― Perchance" + (h1Text.toLowerCase().includes("gener") ? "" : " Generator");
              //   }
              // } else {
              //   let title = window.location.pathname.slice(1).split("-").filter(function(a){return a;}).map(function(word){return word[0].toUpperCase()+word.slice(1); }).join(" ");
              //   document.title = title+" ― Perchance" + (title.toLowerCase().includes("gener") ? "" : " Generator");
              // }

              if(!window.generatorCanLinkWithRelFollow) {
                var links = window.document.querySelectorAll("#generator-description a");
                for(var i = 0; i < links.length; i++) {
                  links[i].rel = "nofollow";
                }
              }
            });
          }
        </script>  

        <!-- CODEMIRROR (stylesheets must come before javascript init) -->
        <style>
/* BASICS */

.CodeMirror {
  /* Set height, width, borders, and global font properties here */
  font-family: monospace;
  height: 300px;
  color: black;
  direction: ltr;
}

/* PADDING */

.CodeMirror-lines {
  padding: 4px 0; /* Vertical padding around content */
}
.CodeMirror pre.CodeMirror-line,
.CodeMirror pre.CodeMirror-line-like {
  padding: 0 4px; /* Horizontal padding of content */
}

.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
  background-color: white; /* The little square between H and V scrollbars */
}

/* GUTTER */

.CodeMirror-gutters {
  border-right: 1px solid #ddd;
  background-color: #f7f7f7;
  white-space: nowrap;
}
.CodeMirror-linenumbers {}
.CodeMirror-linenumber {
  padding: 0 3px 0 5px;
  min-width: 20px;
  text-align: right;
  color: #999;
  white-space: nowrap;
}

.CodeMirror-guttermarker { color: black; }
.CodeMirror-guttermarker-subtle { color: #999; }

/* CURSOR */

.CodeMirror-cursor {
  border-left: 1px solid black;
  border-right: none;
  width: 0;
}
/* Shown when moving in bi-directional text */
.CodeMirror div.CodeMirror-secondarycursor {
  border-left: 1px solid silver;
}
.cm-fat-cursor .CodeMirror-cursor {
  width: auto;
  border: 0 !important;
  background: #7e7;
}
.cm-fat-cursor div.CodeMirror-cursors {
  z-index: 1;
}
.cm-fat-cursor .CodeMirror-line::selection,
.cm-fat-cursor .CodeMirror-line > span::selection, 
.cm-fat-cursor .CodeMirror-line > span > span::selection { background: transparent; }
.cm-fat-cursor .CodeMirror-line::-moz-selection,
.cm-fat-cursor .CodeMirror-line > span::-moz-selection,
.cm-fat-cursor .CodeMirror-line > span > span::-moz-selection { background: transparent; }
.cm-fat-cursor { caret-color: transparent; }
@-moz-keyframes blink {
  0% {}
  50% { background-color: transparent; }
  100% {}
}
@-webkit-keyframes blink {
  0% {}
  50% { background-color: transparent; }
  100% {}
}
@keyframes blink {
  0% {}
  50% { background-color: transparent; }
  100% {}
}

/* Can style cursor different in overwrite (non-insert) mode */
.CodeMirror-overwrite .CodeMirror-cursor {}

.cm-tab { display: inline-block; text-decoration: inherit; }

.CodeMirror-rulers {
  position: absolute;
  left: 0; right: 0; top: -50px; bottom: 0;
  overflow: hidden;
}
.CodeMirror-ruler {
  border-left: 1px solid #ccc;
  top: 0; bottom: 0;
  position: absolute;
}

/* DEFAULT THEME */

.cm-s-default .cm-header {color: blue;}
.cm-s-default .cm-quote {color: #090;}
.cm-negative {color: #d44;}
.cm-positive {color: #292;}
.cm-header, .cm-strong {font-weight: bold;}
.cm-em {font-style: italic;}
.cm-link {text-decoration: underline;}
.cm-strikethrough {text-decoration: line-through;}

.cm-s-default .cm-keyword {color: #708;}
.cm-s-default .cm-atom {color: #219;}
.cm-s-default .cm-number {color: #164;}
.cm-s-default .cm-def {color: #00f;}
.cm-s-default .cm-variable,
.cm-s-default .cm-punctuation,
.cm-s-default .cm-property,
.cm-s-default .cm-operator {}
.cm-s-default .cm-variable-2 {color: #05a;}
.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
.cm-s-default .cm-comment {color: #a50;}
.cm-s-default .cm-string {color: #a11;}
.cm-s-default .cm-string-2 {color: #f50;}
.cm-s-default .cm-meta {color: #555;}
.cm-s-default .cm-qualifier {color: #555;}
.cm-s-default .cm-builtin {color: #30a;}
.cm-s-default .cm-bracket {color: #997;}
.cm-s-default .cm-tag {color: #170;}
.cm-s-default .cm-attribute {color: #00c;}
.cm-s-default .cm-hr {color: #999;}
.cm-s-default .cm-link {color: #00c;}

.cm-s-default .cm-error {color: #f00;}
.cm-invalidchar {color: #f00;}

.CodeMirror-composing { border-bottom: 2px solid; }

/* Default styles for common addons */

div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
.CodeMirror-activeline-background {background: #e8f2ff;}

/* STOP */

/* The rest of this file contains styles related to the mechanics of
   the editor. You probably shouldn't touch them. */

.CodeMirror {
  position: relative;
  overflow: hidden;
  background: white;
}

.CodeMirror-scroll {
  overflow: scroll !important; /* Things will break if this is overridden */
  /* 50px is the magic margin used to hide the element's real scrollbars */
  /* See overflow: hidden in .CodeMirror */
  margin-bottom: -50px; margin-right: -50px;
  padding-bottom: 50px;
  height: 100%;
  outline: none; /* Prevent dragging from highlighting the element */
  position: relative;
  z-index: 0;
}
.CodeMirror-sizer {
  position: relative;
  border-right: 50px solid transparent;
}

/* The fake, visible scrollbars. Used to force redraw during scrolling
   before actual scrolling happens, thus preventing shaking and
   flickering artifacts. */
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
  position: absolute;
  z-index: 6;
  display: none;
  outline: none;
}
.CodeMirror-vscrollbar {
  right: 0; top: 0;
  overflow-x: hidden;
  overflow-y: scroll;
}
.CodeMirror-hscrollbar {
  bottom: 0; left: 0;
  overflow-y: hidden;
  overflow-x: scroll;
}
.CodeMirror-scrollbar-filler {
  right: 0; bottom: 0;
}
.CodeMirror-gutter-filler {
  left: 0; bottom: 0;
}

.CodeMirror-gutters {
  position: absolute; left: 0; top: 0;
  min-height: 100%;
  z-index: 3;
}
.CodeMirror-gutter {
  white-space: normal;
  height: 100%;
  display: inline-block;
  vertical-align: top;
  margin-bottom: -50px;
}
.CodeMirror-gutter-wrapper {
  position: absolute;
  z-index: 4;
  background: none !important;
  border: none !important;
}
.CodeMirror-gutter-background {
  position: absolute;
  top: 0; bottom: 0;
  z-index: 4;
}
.CodeMirror-gutter-elt {
  position: absolute;
  cursor: default;
  z-index: 4;
}
.CodeMirror-gutter-wrapper ::selection { background-color: transparent }
.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }

.CodeMirror-lines {
  cursor: text;
  min-height: 1px; /* prevents collapsing before first draw */
}
.CodeMirror pre.CodeMirror-line,
.CodeMirror pre.CodeMirror-line-like {
  /* Reset some styles that the rest of the page might have set */
  -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
  border-width: 0;
  background: transparent;
  font-family: inherit;
  font-size: inherit;
  margin: 0;
  white-space: pre;
  word-wrap: normal;
  line-height: inherit;
  color: inherit;
  z-index: 2;
  position: relative;
  overflow: visible;
  -webkit-tap-highlight-color: transparent;
  -webkit-font-variant-ligatures: contextual;
  font-variant-ligatures: contextual;
}
.CodeMirror-wrap pre.CodeMirror-line,
.CodeMirror-wrap pre.CodeMirror-line-like {
  word-wrap: break-word;
  white-space: pre-wrap;
  word-break: normal;
}

.CodeMirror-linebackground {
  position: absolute;
  left: 0; right: 0; top: 0; bottom: 0;
  z-index: 0;
}

.CodeMirror-linewidget {
  position: relative;
  z-index: 2;
  padding: 0.1px; /* Force widget margins to stay inside of the container */
}

.CodeMirror-widget {}

.CodeMirror-rtl pre { direction: rtl; }

.CodeMirror-code {
  outline: none;
}

/* Force content-box sizing for the elements where we expect it */
.CodeMirror-scroll,
.CodeMirror-sizer,
.CodeMirror-gutter,
.CodeMirror-gutters,
.CodeMirror-linenumber {
  -moz-box-sizing: content-box;
  box-sizing: content-box;
}

.CodeMirror-measure {
  position: absolute;
  width: 100%;
  height: 0;
  overflow: hidden;
  visibility: hidden;
}

.CodeMirror-cursor {
  position: absolute;
  pointer-events: none;
}
.CodeMirror-measure pre { position: static; }

div.CodeMirror-cursors {
  visibility: hidden;
  position: relative;
  z-index: 3;
}
div.CodeMirror-dragcursors {
  visibility: visible;
}

.CodeMirror-focused div.CodeMirror-cursors {
  visibility: visible;
}

.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
.CodeMirror-crosshair { cursor: crosshair; }
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }

.cm-searching {
  background-color: #ffa;
  background-color: rgba(255, 255, 0, .4);
}

/* Used to force a border model for a node */
.cm-force-border { padding-right: .1px; }

@media print {
  /* Hide the cursor when printing */
  .CodeMirror div.CodeMirror-cursors {
    visibility: hidden;
  }
}

/* See issue #2901 */
.cm-tab-wrap-hack:after { content: ''; }

/* Help users use markselection to safely style text background */
span.CodeMirror-selectedtext { background: none; }

.CodeMirror-dialog {
  position: absolute;
  left: 0; right: 0;
  background: inherit;
  z-index: 15;
  padding: .1em .8em;
  overflow: hidden;
  color: inherit;
}

.CodeMirror-dialog-top {
  border-bottom: 1px solid #eee;
  top: 0;
}

.CodeMirror-dialog-bottom {
  border-top: 1px solid #eee;
  bottom: 0;
}

.CodeMirror-dialog input {
  border: none;
  outline: none;
  background: transparent;
  width: 20em;
  color: inherit;
  font-family: monospace;
}

.CodeMirror-dialog button {
  font-size: 70%;
}
/*
    Name:       one-light 1.0.0
    Author:     Yohan de Rose (https://github.com/yohanderose/)
				-> originally Török Ádám (http://github.com/Aerobird98)
    Original Atom One Dark Theme (https://github.com/atom/one-dark-ui & https://github.com/atom/one-dark-syntax)
*/

/* basic */
.cm-s-one-light {
  font-family: Menlo, Consolas, 'DejaVu Sans Mono', monospace;
  /*font-weight: 350;*/
  /*font-size: 18px;*/
  color: #383a42;
  background-color: #fafafa;
}

.cm-s-one-light .CodeMirror-selected {background-color: #d0d1d5;}
.cm-s-one-light .CodeMirror-gutter,
.cm-s-one-light .CodeMirror-gutters {
  border: none;
  background-color: #fafafa;
}

.cm-s-one-light .CodeMirror-linenumber,
.cm-s-one-light .CodeMirror-linenumbers {
  color: #9a9b9f !important;
  background-color: transparent;
}

.cm-s-one-light .CodeMirror-lines {
  color: #383a42 !important;
  background-color: transparent;
}

.cm-s-one-light .CodeMirror-cursor {border-left: 2px solid #0184bb !important;}

/* addon: edit/machingbrackets.js & addon: edit/matchtags.js */
.cm-s-one-light .CodeMirror-matchingbracket,
.cm-s-one-light .CodeMirror-matchingtag {
  border-bottom: 2px solid #0184bb;
  color: #383a42 !important;
  background-color: transparent;
}

.cm-s-one-light .CodeMirror-nonmatchingbracket {
  border-bottom: 2px solid #e45649;
  color: #383a42 !important;
  background-color: transparent;
}

/* addon: fold/foldgutter.js */
.cm-s-one-light .CodeMirror-foldmarker,
.cm-s-one-light .CodeMirror-foldgutter,
.cm-s-one-light .CodeMirror-foldgutter-open,
.cm-s-one-light .CodeMirror-foldgutter-folded {
  border: none;
  text-shadow: none;
  color: #9a9b9f !important;
  background-color: transparent;
}

/* addon: selection/active-line.js */
.cm-s-one-light .CodeMirror-activeline-background {background-color: rgba(0, 123, 255, 0.04);}

/* basic syntax */
.cm-s-one-light .cm-header {color: #e45649;}
.cm-s-one-light .cm-quote {color: #9a9b9f;font-style: italic;}
.cm-s-one-light .cm-negative {color: #e45649;}
.cm-s-one-light .cm-positive {color: #50a14f;}
.cm-s-one-light .cm-strong {color: #c18401;font-weight: bold;}
.cm-s-one-light .cm-header .cm-strong {color: #c18401;font-weight: bold;}
.cm-s-one-light .cm-em {color: #986801;font-style: italic;}
.cm-s-one-light .cm-header .cm-em {color: #986801;font-style: italic;}
.cm-s-one-light .cm-tag {color: #e45649;}
.cm-s-one-light .cm-attribute {color: #c18401;}
.cm-s-one-light .cm-link {color: #0184bb;border-bottom: solid 1px #0184bb;}
.cm-s-one-light .cm-builtin {color: #0184bb;}
.cm-s-one-light .cm-keyword {color: #a626a4;}
.cm-s-one-light .cm-def {color: #4078f2;}
.cm-s-one-light .cm-atom {color: #986801;}
.cm-s-one-light .cm-number {color: #986801;}
.cm-s-one-light .cm-property {color: #0184bb;}
.cm-s-one-light .cm-qualifier {color: #c18401;}
.cm-s-one-light .cm-variable {color: #e45649;}
.cm-s-one-light .cm-string {color: #50a14f;}
.cm-s-one-light .cm-punctuation {color: #383a42;}
.cm-s-one-light .cm-operator {color: #0184bb;}
.cm-s-one-light .cm-meta {color: #383a42;}
.cm-s-one-light .cm-bracket {color: #383a42;}
.cm-s-one-light .cm-comment {color: #a0a1a7;font-style: italic;}
.cm-s-one-light .cm-error {color: #e45649;}

/* css syntax corrections */
.cm-s-one-light .cm-m-css.cm-variable {color: #383a42;}
.cm-s-one-light .cm-m-css.cm-property {color: #383a42;}
.cm-s-one-light .cm-m-css.cm-atom {color: #0184bb;}
.cm-s-one-light .cm-m-css.cm-builtin {color: #0184bb;}

/* lua syntax corrections */
.cm-s-one-light .cm-m-lua.cm-variable {color: #0184bb;}.CodeMirror-search-match {
  background: gold;
  border-top: 1px solid orange;
  border-bottom: 1px solid orange;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
  opacity: .5;
}
/*
    Name:       one-dark 1.1.1
    Author:     Török Ádám (http://github.com/Aerobird98)
    Original Atom One Dark Theme (https://github.com/atom/one-dark-ui & https://github.com/atom/one-dark-syntax)
*/

/* basic */
.cm-s-one-dark {
  font-family: Menlo, Consolas, 'DejaVu Sans Mono', monospace;
  /*font-weight: 350;*/
  /*font-size: 18px;*/
  color: #abb2bf;
  background-color: #282c34;
}
.cm-s-one-dark .CodeMirror-selected {background-color: #3e4451;}
.cm-s-one-dark .CodeMirror-gutter,
.cm-s-one-dark .CodeMirror-gutters {
  border: none;
  background-color: #282c34;
}
.cm-s-one-dark .CodeMirror-linenumber,
.cm-s-one-dark .CodeMirror-linenumbers {
  color: #5c6370 !important;
  background-color: transparent;
}
.cm-s-one-dark .CodeMirror-lines {
  color: #abb2bf !important;
  background-color: transparent;
}
.cm-s-one-dark .CodeMirror-cursor {border-left: 2px solid #56b6c2 !important;}
/* addon: edit/machingbrackets.js & addon: edit/matchtags.js */
.cm-s-one-dark .CodeMirror-matchingbracket,
.cm-s-one-dark .CodeMirror-matchingtag {
  border-bottom: 2px solid #56b6c2;
  color: #abb2bf !important;
  background-color: transparent;
}
.cm-s-one-dark .CodeMirror-nonmatchingbracket {
  border-bottom: 2px solid #e06c75;
  color: #abb2bf !important;
  background-color: transparent;
}
/* addon: fold/foldgutter.js */
.cm-s-one-dark .CodeMirror-foldmarker,
.cm-s-one-dark .CodeMirror-foldgutter,
.cm-s-one-dark .CodeMirror-foldgutter-open,
.cm-s-one-dark .CodeMirror-foldgutter-folded {
  border: none;
  text-shadow: none;
  color: #5c6370 !important;
  background-color: transparent;
}
/* addon: selection/active-line.js */
.cm-s-one-dark .CodeMirror-activeline-background {background-color: rgba(153, 187, 255, 0.04);}
/* basic syntax */
.cm-s-one-dark .cm-header {color: #e06c75;}
.cm-s-one-dark .cm-quote {color: #5c6370;font-style: italic;}
.cm-s-one-dark .cm-negative {color: #e06c75;}
.cm-s-one-dark .cm-positive {color: #e06c75;}
.cm-s-one-dark .cm-strong {color: #d19a66;font-weight: bold;}
.cm-s-one-dark .cm-header .cm-strong {color: #d19a66;font-weight: bold;}
.cm-s-one-dark .cm-em {color: #c678dd;font-style: italic;}
.cm-s-one-dark .cm-header .cm-em {color: #c678dd;font-style: italic;}
.cm-s-one-dark .cm-tag {color: #e06c75;}
.cm-s-one-dark .cm-attribute {color: #d19a66;}
.cm-s-one-dark .cm-link {color: #98c379;border-bottom: solid 1px #98c379;}
.cm-s-one-dark .cm-builtin {color: #e06c75;}
.cm-s-one-dark .cm-keyword {color: #c678dd;}
.cm-s-one-dark .cm-def {color: #e5c07b;} /* original:  #d19a66; */
.cm-s-one-dark .cm-atom {color: #d19a66;}
.cm-s-one-dark .cm-number {color: #d19a66;}
.cm-s-one-dark .cm-property {color: #56b6c2;} /* original: #abb2bf */
.cm-s-one-dark .cm-qualifier {color: #d19a66;}
.cm-s-one-dark .cm-variable {color: #e06c75;}
.cm-s-one-dark .cm-string {color: #98c379;}
.cm-s-one-dark .cm-punctuation {color: #abb2bf;}
.cm-s-one-dark .cm-operator {color: #56b6c2;} /* original: #abb2bf */
.cm-s-one-dark .cm-meta {color: #abb2bf;}
.cm-s-one-dark .cm-bracket {color: #abb2bf;}
.cm-s-one-dark .cm-comment {color: #5c6370;font-style: italic;}
.cm-s-one-dark .cm-error {color: #e06c75;}
/* css syntax corrections */
.cm-s-one-dark .cm-m-css.cm-variable {color: #828997;}
.cm-s-one-dark .cm-m-css.cm-property  {color: #abb2bf;}
.cm-s-one-dark .cm-m-css.cm-atom  {color: #56b6c2;}
.cm-s-one-dark .cm-m-css.cm-builtin {color: #56b6c2;}
/* lua syntax corrections */
.cm-s-one-dark .cm-m-lua.cm-variable {color: #56b6c2;}.CodeMirror-foldmarker {
  color: blue;
  text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;
  font-family: arial;
  line-height: .3;
  cursor: pointer;
}
.CodeMirror-foldgutter {
  width: .7em;
}
.CodeMirror-foldgutter-open,
.CodeMirror-foldgutter-folded {
  cursor: pointer;
}
.CodeMirror-foldgutter-open:after {
  content: "\25BE";
}
.CodeMirror-foldgutter-folded:after {
  content: "\25B8";
}

</style><script>
(function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?module.exports=factory():typeof define==="function"&&define.amd?define(factory):(global=global||self,global.CodeMirror=factory())})(this,(function(){"use strict";var userAgent=navigator.userAgent;var platform=navigator.platform;var gecko=/gecko\/\d/i.test(userAgent);var ie_upto10=/MSIE \d/.test(userAgent);var ie_11up=/Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);var edge=/Edge\/(\d+)/.exec(userAgent);var ie=ie_upto10||ie_11up||edge;var ie_version=ie&&(ie_upto10?document.documentMode||6:+(edge||ie_11up)[1]);var webkit=!edge&&/WebKit\//.test(userAgent);var qtwebkit=webkit&&/Qt\/\d+\.\d+/.test(userAgent);var chrome=!edge&&/Chrome\/(\d+)/.exec(userAgent);var chrome_version=chrome&&+chrome[1];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=safari&&(/Mobile\/\w+/.test(userAgent)||navigator.maxTouchPoints>2);var android=/Android/.test(userAgent);var mobile=ios||android||/webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);var mac=ios||/Mac/.test(platform);var chromeOS=/\bCrOS\b/.test(userAgent);var windows=/win/i.test(platform);var presto_version=presto&&userAgent.match(/Version\/(\d*\.\d*)/);if(presto_version){presto_version=Number(presto_version[1])}if(presto_version&&presto_version>=15){presto=false;webkit=true}var flipCtrlCmd=mac&&(qtwebkit||presto&&(presto_version==null||presto_version<12.11));var captureRightClick=gecko||ie&&ie_version>=9;function classTest(cls){return new RegExp("(^|\\s)"+cls+"(?:$|\\s)\\s*")}var rmClass=function(node,cls){var current=node.className;var match=classTest(cls).exec(current);if(match){var after=current.slice(match.index+match[0].length);node.className=current.slice(0,match.index)+(after?match[1]+after:"")}};function removeChildren(e){for(var count=e.childNodes.length;count>0;--count){e.removeChild(e.firstChild)}return e}function removeChildrenAndAdd(parent,e){return removeChildren(parent).appendChild(e)}function elt(tag,content,className,style){var e=document.createElement(tag);if(className){e.className=className}if(style){e.style.cssText=style}if(typeof content=="string"){e.appendChild(document.createTextNode(content))}else if(content){for(var i=0;i<content.length;++i){e.appendChild(content[i])}}return e}function eltP(tag,content,className,style){var e=elt(tag,content,className,style);e.setAttribute("role","presentation");return e}var range;if(document.createRange){range=function(node,start,end,endNode){var r=document.createRange();r.setEnd(endNode||node,end);r.setStart(node,start);return r}}else{range=function(node,start,end){var r=document.body.createTextRange();try{r.moveToElementText(node.parentNode)}catch(e){return r}r.collapse(true);r.moveEnd("character",end);r.moveStart("character",start);return r}}function contains(parent,child){if(child.nodeType==3){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(doc){var activeElement;try{activeElement=doc.activeElement}catch(e){activeElement=doc.body||null}while(activeElement&&activeElement.shadowRoot&&activeElement.shadowRoot.activeElement){activeElement=activeElement.shadowRoot.activeElement}return activeElement}function addClass(node,cls){var current=node.className;if(!classTest(cls).test(current)){node.className+=(current?" ":"")+cls}}function joinClasses(a,b){var as=a.split(" ");for(var i=0;i<as.length;i++){if(as[i]&&!classTest(as[i]).test(b)){b+=" "+as[i]}}return b}var selectInput=function(node){node.select()};if(ios){selectInput=function(node){node.selectionStart=0;node.selectionEnd=node.value.length}}else if(ie){selectInput=function(node){try{node.select()}catch(_e){}}}function doc(cm){return cm.display.wrapper.ownerDocument}function win(cm){return doc(cm).defaultView}function bind(f){var args=Array.prototype.slice.call(arguments,1);return function(){return f.apply(null,args)}}function copyObj(obj,target,overwrite){if(!target){target={}}for(var prop in obj){if(obj.hasOwnProperty(prop)&&(overwrite!==false||!target.hasOwnProperty(prop))){target[prop]=obj[prop]}}return target}function countColumn(string,end,tabSize,startIndex,startValue){if(end==null){end=string.search(/[^\s\u00a0]/);if(end==-1){end=string.length}}for(var i=startIndex||0,n=startValue||0;;){var nextTab=string.indexOf("\t",i);if(nextTab<0||nextTab>=end){return n+(end-i)}n+=nextTab-i;n+=tabSize-n%tabSize;i=nextTab+1}}var Delayed=function(){this.id=null;this.f=null;this.time=0;this.handler=bind(this.onTimeout,this)};Delayed.prototype.onTimeout=function(self){self.id=0;if(self.time<=+new Date){self.f()}else{setTimeout(self.handler,self.time-+new Date)}};Delayed.prototype.set=function(ms,f){this.f=f;var time=+new Date+ms;if(!this.id||time<this.time){clearTimeout(this.id);this.id=setTimeout(this.handler,ms);this.time=time}};function indexOf(array,elt){for(var i=0;i<array.length;++i){if(array[i]==elt){return i}}return-1}var scrollerGap=50;var Pass={toString:function(){return"CodeMirror.Pass"}};var sel_dontScroll={scroll:false},sel_mouse={origin:"*mouse"},sel_move={origin:"+move"};function findColumn(string,goal,tabSize){for(var pos=0,col=0;;){var nextTab=string.indexOf("\t",pos);if(nextTab==-1){nextTab=string.length}var skipped=nextTab-pos;if(nextTab==string.length||col+skipped>=goal){return pos+Math.min(skipped,goal-col)}col+=nextTab-pos;col+=tabSize-col%tabSize;pos=nextTab+1;if(col>=goal){return pos}}}var spaceStrs=[""];function spaceStr(n){while(spaceStrs.length<=n){spaceStrs.push(lst(spaceStrs)+" ")}return spaceStrs[n]}function lst(arr){return arr[arr.length-1]}function map(array,f){var out=[];for(var i=0;i<array.length;i++){out[i]=f(array[i],i)}return out}function insertSorted(array,value,score){var pos=0,priority=score(value);while(pos<array.length&&score(array[pos])<=priority){pos++}array.splice(pos,0,value)}function nothing(){}function createObj(base,props){var inst;if(Object.create){inst=Object.create(base)}else{nothing.prototype=base;inst=new nothing}if(props){copyObj(props,inst)}return inst}var nonASCIISingleCaseWordChar=/[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;function isWordCharBasic(ch){return/\w/.test(ch)||ch>"€"&&(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}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)}function skipExtendingChars(str,pos,dir){while((dir<0?pos>0:pos<str.length)&&isExtendingChar(str.charAt(pos))){pos+=dir}return pos}function findFirst(pred,from,to){var dir=from>to?-1:1;for(;;){if(from==to){return from}var midF=(from+to)/2,mid=dir<0?Math.ceil(midF):Math.floor(midF);if(mid==from){return pred(mid)?from:to}if(pred(mid)){to=mid}else{from=mid+dir}}}function iterateBidiSections(order,from,to,f){if(!order){return f(from,to,"ltr",0)}var found=false;for(var i=0;i<order.length;++i){var part=order[i];if(part.from<to&&part.to>from||from==to&&part.to==from){f(Math.max(part.from,from),Math.min(part.to,to),part.level==1?"rtl":"ltr",i);found=true}}if(!found){f(from,to,"ltr")}}var bidiOther=null;function getBidiPartAt(order,ch,sticky){var found;bidiOther=null;for(var i=0;i<order.length;++i){var cur=order[i];if(cur.from<ch&&cur.to>ch){return i}if(cur.to==ch){if(cur.from!=cur.to&&sticky=="before"){found=i}else{bidiOther=i}}if(cur.from==ch){if(cur.from!=cur.to&&sticky!="before"){found=i}else{bidiOther=i}}}return found!=null?found:bidiOther}var bidiOrdering=function(){var lowTypes="bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";var arabicTypes="nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";function charType(code){if(code<=247){return lowTypes.charAt(code)}else if(1424<=code&&code<=1524){return"R"}else if(1536<=code&&code<=1785){return arabicTypes.charAt(code-1536)}else if(1774<=code&&code<=2220){return"r"}else if(8192<=code&&code<=8203){return"w"}else if(code==8204){return"b"}else{return"L"}}var bidiRE=/[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;var isNeutral=/[stwN]/,isStrong=/[LRr]/,countsAsLeft=/[Lb1n]/,countsAsNum=/[1n]/;function BidiSpan(level,from,to){this.level=level;this.from=from;this.to=to}return function(str,direction){var outerType=direction=="ltr"?"L":"R";if(str.length==0||direction=="ltr"&&!bidiRE.test(str)){return false}var len=str.length,types=[];for(var i=0;i<len;++i){types.push(charType(str.charCodeAt(i)))}for(var i$1=0,prev=outerType;i$1<len;++i$1){var type=types[i$1];if(type=="m"){types[i$1]=prev}else{prev=type}}for(var i$2=0,cur=outerType;i$2<len;++i$2){var type$1=types[i$2];if(type$1=="1"&&cur=="r"){types[i$2]="n"}else if(isStrong.test(type$1)){cur=type$1;if(type$1=="r"){types[i$2]="R"}}}for(var i$3=1,prev$1=types[0];i$3<len-1;++i$3){var type$2=types[i$3];if(type$2=="+"&&prev$1=="1"&&types[i$3+1]=="1"){types[i$3]="1"}else if(type$2==","&&prev$1==types[i$3+1]&&(prev$1=="1"||prev$1=="n")){types[i$3]=prev$1}prev$1=type$2}for(var i$4=0;i$4<len;++i$4){var type$3=types[i$4];if(type$3==","){types[i$4]="N"}else if(type$3=="%"){var end=void 0;for(end=i$4+1;end<len&&types[end]=="%";++end){}var replace=i$4&&types[i$4-1]=="!"||end<len&&types[end]=="1"?"1":"N";for(var j=i$4;j<end;++j){types[j]=replace}i$4=end-1}}for(var i$5=0,cur$1=outerType;i$5<len;++i$5){var type$4=types[i$5];if(cur$1=="L"&&type$4=="1"){types[i$5]="L"}else if(isStrong.test(type$4)){cur$1=type$4}}for(var i$6=0;i$6<len;++i$6){if(isNeutral.test(types[i$6])){var end$1=void 0;for(end$1=i$6+1;end$1<len&&isNeutral.test(types[end$1]);++end$1){}var before=(i$6?types[i$6-1]:outerType)=="L";var after=(end$1<len?types[end$1]:outerType)=="L";var replace$1=before==after?before?"L":"R":outerType;for(var j$1=i$6;j$1<end$1;++j$1){types[j$1]=replace$1}i$6=end$1-1}}var order=[],m;for(var i$7=0;i$7<len;){if(countsAsLeft.test(types[i$7])){var start=i$7;for(++i$7;i$7<len&&countsAsLeft.test(types[i$7]);++i$7){}order.push(new BidiSpan(0,start,i$7))}else{var pos=i$7,at=order.length,isRTL=direction=="rtl"?1:0;for(++i$7;i$7<len&&types[i$7]!="L";++i$7){}for(var j$2=pos;j$2<i$7;){if(countsAsNum.test(types[j$2])){if(pos<j$2){order.splice(at,0,new BidiSpan(1,pos,j$2));at+=isRTL}var nstart=j$2;for(++j$2;j$2<i$7&&countsAsNum.test(types[j$2]);++j$2){}order.splice(at,0,new BidiSpan(2,nstart,j$2));at+=isRTL;pos=j$2}else{++j$2}}if(pos<i$7){order.splice(at,0,new BidiSpan(1,pos,i$7))}}}if(direction=="ltr"){if(order[0].level==1&&(m=str.match(/^\s+/))){order[0].from=m[0].length;order.unshift(new BidiSpan(0,0,m[0].length))}if(lst(order).level==1&&(m=str.match(/\s+$/))){lst(order).to-=m[0].length;order.push(new BidiSpan(0,len-m[0].length,len))}}return direction=="rtl"?order.reverse():order}}();function getOrder(line,direction){var order=line.order;if(order==null){order=line.order=bidiOrdering(line.text,direction)}return order}var noHandlers=[];var on=function(emitter,type,f){if(emitter.addEventListener){emitter.addEventListener(type,f,false)}else if(emitter.attachEvent){emitter.attachEvent("on"+type,f)}else{var map=emitter._handlers||(emitter._handlers={});map[type]=(map[type]||noHandlers).concat(f)}};function getHandlers(emitter,type){return emitter._handlers&&emitter._handlers[type]||noHandlers}function off(emitter,type,f){if(emitter.removeEventListener){emitter.removeEventListener(type,f,false)}else if(emitter.detachEvent){emitter.detachEvent("on"+type,f)}else{var map=emitter._handlers,arr=map&&map[type];if(arr){var index=indexOf(arr,f);if(index>-1){map[type]=arr.slice(0,index).concat(arr.slice(index+1))}}}}function signal(emitter,type){var handlers=getHandlers(emitter,type);if(!handlers.length){return}var args=Array.prototype.slice.call(arguments,2);for(var i=0;i<handlers.length;++i){handlers[i].apply(null,args)}}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}function eventMixin(ctor){ctor.prototype.on=function(type,f){on(this,type,f)};ctor.prototype.off=function(type,f){off(this,type,f)}}function e_preventDefault(e){if(e.preventDefault){e.preventDefault()}else{e.returnValue=false}}function e_stopPropagation(e){if(e.stopPropagation){e.stopPropagation()}else{e.cancelBubble=true}}function e_defaultPrevented(e){return e.defaultPrevented!=null?e.defaultPrevented:e.returnValue==false}function e_stop(e){e_preventDefault(e);e_stopPropagation(e)}function e_target(e){return e.target||e.srcElement}function e_button(e){var b=e.which;if(b==null){if(e.button&1){b=1}else if(e.button&2){b=3}else if(e.button&4){b=2}}if(mac&&e.ctrlKey&&b==1){b=3}return b}var dragAndDrop=function(){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","​");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","​"):elt("span"," ",null,"display: inline-block; width: 1px; margin-right: -1px");node.setAttribute("cm-text","");return node}var badBidiRects;function hasBadBidiRects(measure){if(badBidiRects!=null){return badBidiRects}var txt=removeChildrenAndAdd(measure,document.createTextNode("AخA"));var r0=range(txt,0,1).getBoundingClientRect();var r1=range(txt,1,2).getBoundingClientRect();removeChildren(measure);if(!r0||r0.left==r0.right){return false}return badBidiRects=r1.right-r0.right<3}var splitLinesAuto="\n\nb".split(/\n/).length!=3?function(string){var pos=0,result=[],l=string.length;while(pos<=l){var nl=string.indexOf("\n",pos);if(nl==-1){nl=string.length}var line=string.slice(pos,string.charAt(nl-1)=="\r"?nl-1:nl);var rt=line.indexOf("\r");if(rt!=-1){result.push(line.slice(0,rt));pos+=rt+1}else{result.push(line);pos=nl+1}}return result}:function(string){return string.split(/\r\n?|\n/)};var hasSelection=window.getSelection?function(te){try{return te.selectionStart!=te.selectionEnd}catch(e){return false}}:function(te){var range;try{range=te.ownerDocument.selection.createRange()}catch(e){}if(!range||range.parentElement()!=te){return false}return range.compareEndPoints("StartToEnd",range)!=0};var hasCopyEvent=function(){var e=elt("div");if("oncopy"in e){return true}e.setAttribute("oncopy","return;");return typeof e.oncopy=="function"}();var badZoomedRects=null;function hasBadZoomedRects(measure){if(badZoomedRects!=null){return badZoomedRects}var node=removeChildrenAndAdd(measure,elt("span","x"));var normal=node.getBoundingClientRect();var fromRange=range(node,0,1).getBoundingClientRect();return badZoomedRects=Math.abs(normal.left-fromRange.left)>1}var modes={},mimeModes={};function defineMode(name,mode){if(arguments.length>2){mode.dependencies=Array.prototype.slice.call(arguments,2)}modes[name]=mode}function defineMIME(mime,spec){mimeModes[mime]=spec}function resolveMode(spec){if(typeof spec=="string"&&mimeModes.hasOwnProperty(spec)){spec=mimeModes[spec]}else if(spec&&typeof spec.name=="string"&&mimeModes.hasOwnProperty(spec.name)){var found=mimeModes[spec.name];if(typeof found=="string"){found={name:found}}spec=createObj(found,spec);spec.name=found.name}else if(typeof spec=="string"&&/^[\w\-]+\/[\w\-]+\+xml$/.test(spec)){return resolveMode("application/xml")}else if(typeof spec=="string"&&/^[\w\-]+\/[\w\-]+\+json$/.test(spec)){return resolveMode("application/json")}if(typeof spec=="string"){return{name:spec}}else{return spec||{name:"null"}}}function getMode(options,spec){spec=resolveMode(spec);var mfactory=modes[spec.name];if(!mfactory){return getMode(options,"text/plain")}var modeObj=mfactory(options,spec);if(modeExtensions.hasOwnProperty(spec.name)){var exts=modeExtensions[spec.name];for(var prop in exts){if(!exts.hasOwnProperty(prop)){continue}if(modeObj.hasOwnProperty(prop)){modeObj["_"+prop]=modeObj[prop]}modeObj[prop]=exts[prop]}}modeObj.name=spec.name;if(spec.helperType){modeObj.helperType=spec.helperType}if(spec.modeProps){for(var prop$1 in spec.modeProps){modeObj[prop$1]=spec.modeProps[prop$1]}}return modeObj}var modeExtensions={};function extendMode(mode,properties){var exts=modeExtensions.hasOwnProperty(mode)?modeExtensions[mode]:modeExtensions[mode]={};copyObj(properties,exts)}function copyState(mode,state){if(state===true){return state}if(mode.copyState){return mode.copyState(state)}var nstate={};for(var n in state){var val=state[n];if(val instanceof Array){val=val.concat([])}nstate[n]=val}return nstate}function innerMode(mode,state){var info;while(mode.innerMode){info=mode.innerMode(state);if(!info||info.mode==mode){break}state=info.state;mode=info.mode}return info||{mode:mode,state:state}}function startState(mode,a1,a2){return mode.startState?mode.startState(a1,a2):true}var StringStream=function(string,tabSize,lineOracle){this.pos=this.start=0;this.string=string;this.tabSize=tabSize||8;this.lastColumnPos=this.lastColumnValue=0;this.lineStart=0;this.lineOracle=lineOracle};StringStream.prototype.eol=function(){return this.pos>=this.string.length};StringStream.prototype.sol=function(){return this.pos==this.lineStart};StringStream.prototype.peek=function(){return this.string.charAt(this.pos)||undefined};StringStream.prototype.next=function(){if(this.pos<this.string.length){return this.string.charAt(this.pos++)}};StringStream.prototype.eat=function(match){var ch=this.string.charAt(this.pos);var ok;if(typeof match=="string"){ok=ch==match}else{ok=ch&&(match.test?match.test(ch):match(ch))}if(ok){++this.pos;return ch}};StringStream.prototype.eatWhile=function(match){var start=this.pos;while(this.eat(match)){}return this.pos>start};StringStream.prototype.eatSpace=function(){var start=this.pos;while(/[\s\u00a0]/.test(this.string.charAt(this.pos))){++this.pos}return this.pos>start};StringStream.prototype.skipToEnd=function(){this.pos=this.string.length};StringStream.prototype.skipTo=function(ch){var found=this.string.indexOf(ch,this.pos);if(found>-1){this.pos=found;return true}};StringStream.prototype.backUp=function(n){this.pos-=n};StringStream.prototype.column=function(){if(this.lastColumnPos<this.start){this.lastColumnValue=countColumn(this.string,this.start,this.tabSize,this.lastColumnPos,this.lastColumnValue);this.lastColumnPos=this.start}return this.lastColumnValue-(this.lineStart?countColumn(this.string,this.lineStart,this.tabSize):0)};StringStream.prototype.indentation=function(){return countColumn(this.string,null,this.tabSize)-(this.lineStart?countColumn(this.string,this.lineStart,this.tabSize):0)};StringStream.prototype.match=function(pattern,consume,caseInsensitive){if(typeof pattern=="string"){var cased=function(str){return caseInsensitive?str.toLowerCase():str};var substr=this.string.substr(this.pos,pattern.length);if(cased(substr)==cased(pattern)){if(consume!==false){this.pos+=pattern.length}return true}}else{var match=this.string.slice(this.pos).match(pattern);if(match&&match.index>0){return null}if(match&&consume!==false){this.pos+=match[0].length}return match}};StringStream.prototype.current=function(){return this.string.slice(this.start,this.pos)};StringStream.prototype.hideFirstChars=function(n,inner){this.lineStart+=n;try{return inner()}finally{this.lineStart-=n}};StringStream.prototype.lookAhead=function(n){var oracle=this.lineOracle;return oracle&&oracle.lookAhead(n)};StringStream.prototype.baseToken=function(){var oracle=this.lineOracle;return oracle&&oracle.baseToken(this.pos)};function getLine(doc,n){n-=doc.first;if(n<0||n>=doc.size){throw new Error("There is no line "+(n+doc.first)+" in the document.")}var chunk=doc;while(!chunk.lines){for(var i=0;;++i){var child=chunk.children[i],sz=child.chunkSize();if(n<sz){chunk=child;break}n-=sz}}return chunk.lines[n]}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}function getLines(doc,from,to){var out=[];doc.iter(from,to,(function(line){out.push(line.text)}));return out}function updateLineHeight(line,height){var diff=height-line.height;if(diff){for(var n=line;n;n=n.parent){n.height+=diff}}}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}function lineAtHeight(chunk,h){var n=chunk.first;outer:do{for(var i$1=0;i$1<chunk.children.length;++i$1){var child=chunk.children[i$1],ch=child.height;if(h<ch){chunk=child;continue outer}h-=ch;n+=child.chunkSize()}return n}while(!chunk.lines);var i=0;for(;i<chunk.lines.length;++i){var line=chunk.lines[i],lh=line.height;if(h<lh){break}h-=lh}return n+i}function isLine(doc,l){return l>=doc.first&&l<doc.first+doc.size}function lineNumberFor(options,i){return String(options.lineNumberFormatter(i+options.firstLineNumber))}function Pos(line,ch,sticky){if(sticky===void 0)sticky=null;if(!(this instanceof Pos)){return new Pos(line,ch,sticky)}this.line=line;this.ch=ch;this.sticky=sticky}function cmp(a,b){return a.line-b.line||a.ch-b.ch}function equalCursorPos(a,b){return a.sticky==b.sticky&&cmp(a,b)==0}function copyPos(x){return Pos(x.line,x.ch)}function maxPos(a,b){return cmp(a,b)<0?b:a}function minPos(a,b){return cmp(a,b)<0?a:b}function clipLine(doc,n){return Math.max(doc.first,Math.min(n,doc.first+doc.size-1))}function clipPos(doc,pos){if(pos.line<doc.first){return Pos(doc.first,0)}var last=doc.first+doc.size-1;if(pos.line>last){return Pos(last,getLine(doc,last).text.length)}return clipToLen(pos,getLine(doc,pos.line).text.length)}function clipToLen(pos,linelen){var ch=pos.ch;if(ch==null||ch>linelen){return Pos(pos.line,linelen)}else if(ch<0){return Pos(pos.line,0)}else{return pos}}function clipPosArray(doc,array){var out=[];for(var i=0;i<array.length;i++){out[i]=clipPos(doc,array[i])}return out}var SavedContext=function(state,lookAhead){this.state=state;this.lookAhead=lookAhead};var Context=function(doc,state,line,lookAhead){this.state=state;this.doc=doc;this.line=line;this.maxLookAhead=lookAhead||0;this.baseTokens=null;this.baseTokenPos=1};Context.prototype.lookAhead=function(n){var line=this.doc.getLine(this.line+n);if(line!=null&&n>this.maxLookAhead){this.maxLookAhead=n}return line};Context.prototype.baseToken=function(n){if(!this.baseTokens){return null}while(this.baseTokens[this.baseTokenPos]<=n){this.baseTokenPos+=2}var type=this.baseTokens[this.baseTokenPos+1];return{type:type&&type.replace(/( |^)overlay .*/,""),size:this.baseTokens[this.baseTokenPos]-n}};Context.prototype.nextLine=function(){this.line++;if(this.maxLookAhead>0){this.maxLookAhead--}};Context.fromSaved=function(doc,saved,line){if(saved instanceof SavedContext){return new Context(doc,copyState(doc.mode,saved.state),line,saved.lookAhead)}else{return new Context(doc,copyState(doc.mode,saved),line)}};Context.prototype.save=function(copy){var state=copy!==false?copyState(this.doc.mode,this.state):this.state;return this.maxLookAhead>0?new SavedContext(state,this.maxLookAhead):state};function highlightLine(cm,line,context,forceToEnd){var st=[cm.state.modeGen],lineClasses={};runMode(cm,line.text,cm.doc.mode,context,(function(end,style){return st.push(end,style)}),lineClasses,forceToEnd);var state=context.state;var loop=function(o){context.baseTokens=st;var overlay=cm.state.overlays[o],i=1,at=0;context.state=true;runMode(cm,line.text,overlay.mode,context,(function(end,style){var start=i;while(at<end){var i_end=st[i];if(i_end>end){st.splice(i,1,end,st[i+1],i_end)}i+=2;at=Math.min(end,i_end)}if(!style){return}if(overlay.opaque){st.splice(start,i-start,end,"overlay "+style);i=start+2}else{for(;start<i;start+=2){var cur=st[start+1];st[start+1]=(cur?cur+" ":"")+"overlay "+style}}}),lineClasses);context.state=state;context.baseTokens=null;context.baseTokenPos=1};for(var o=0;o<cm.state.overlays.length;++o)loop(o);return{styles:st,classes:lineClasses.bgClass||lineClasses.textClass?lineClasses:null}}function getLineStyles(cm,line,updateFrontier){if(!line.styles||line.styles[0]!=cm.state.modeGen){var context=getContextBefore(cm,lineNo(line));var resetState=line.text.length>cm.options.maxHighlightLength&&copyState(cm.doc.mode,context.state);var result=highlightLine(cm,line,context);if(resetState){context.state=resetState}line.stateAfter=context.save(!resetState);line.styles=result.styles;if(result.classes){line.styleClasses=result.classes}else if(line.styleClasses){line.styleClasses=null}if(updateFrontier===cm.doc.highlightFrontier){cm.doc.modeFrontier=Math.max(cm.doc.modeFrontier,++cm.doc.highlightFrontier)}}return line.styles}function getContextBefore(cm,n,precise){var doc=cm.doc,display=cm.display;if(!doc.mode.startState){return new Context(doc,true,n)}var start=findStartLine(cm,n,precise);var saved=start>doc.first&&getLine(doc,start-1).stateAfter;var context=saved?Context.fromSaved(doc,saved,start):new Context(doc,startState(doc.mode),start);doc.iter(start,n,(function(line){processLine(cm,line.text,context);var pos=context.line;line.stateAfter=pos==n-1||pos%5==0||pos>=display.viewFrom&&pos<display.viewTo?context.save():null;context.nextLine()}));if(precise){doc.modeFrontier=context.line}return context}function processLine(cm,text,context,startAt){var mode=cm.doc.mode;var stream=new StringStream(text,cm.options.tabSize,context);stream.start=stream.pos=startAt||0;if(text==""){callBlankLine(mode,context.state)}while(!stream.eol()){readToken(mode,stream,context.state);stream.start=stream.pos}}function callBlankLine(mode,state){if(mode.blankLine){return mode.blankLine(state)}if(!mode.innerMode){return}var inner=innerMode(mode,state);if(inner.mode.blankLine){return inner.mode.blankLine(inner.state)}}function readToken(mode,stream,state,inner){for(var i=0;i<10;i++){if(inner){inner[0]=innerMode(mode,state).mode}var style=mode.token(stream,state);if(stream.pos>stream.start){return style}}throw new Error("Mode "+mode.name+" failed to advance stream.")}var Token=function(stream,type,state){this.start=stream.start;this.end=stream.pos;this.string=stream.current();this.type=type||null;this.state=state};function takeToken(cm,pos,precise,asArray){var doc=cm.doc,mode=doc.mode,style;pos=clipPos(doc,pos);var line=getLine(doc,pos.line),context=getContextBefore(cm,pos.line,precise);var stream=new StringStream(line.text,cm.options.tabSize,context),tokens;if(asArray){tokens=[]}while((asArray||stream.pos<pos.ch)&&!stream.eol()){stream.start=stream.pos;style=readToken(mode,stream,context.state);if(asArray){tokens.push(new Token(stream,style,copyState(doc.mode,context.state)))}}return asArray?tokens:new Token(stream,style,context.state)}function extractLineClasses(type,output){if(type){for(;;){var lineClass=type.match(/(?:^|\s+)line-(background-)?(\S+)/);if(!lineClass){break}type=type.slice(0,lineClass.index)+type.slice(lineClass.index+lineClass[0].length);var prop=lineClass[1]?"bgClass":"textClass";if(output[prop]==null){output[prop]=lineClass[2]}else if(!new RegExp("(?:^|\\s)"+lineClass[2]+"(?:$|\\s)").test(output[prop])){output[prop]+=" "+lineClass[2]}}}return type}function runMode(cm,text,mode,context,f,lineClasses,forceToEnd){var flattenSpans=mode.flattenSpans;if(flattenSpans==null){flattenSpans=cm.options.flattenSpans}var curStart=0,curStyle=null;var stream=new StringStream(text,cm.options.tabSize,context),style;var inner=cm.options.addModeClass&&[null];if(text==""){extractLineClasses(callBlankLine(mode,context.state),lineClasses)}while(!stream.eol()){if(stream.pos>cm.options.maxHighlightLength){flattenSpans=false;if(forceToEnd){processLine(cm,text,context,stream.pos)}stream.pos=text.length;style=null}else{style=extractLineClasses(readToken(mode,stream,context.state,inner),lineClasses)}if(inner){var mName=inner[0].name;if(mName){style="m-"+(style?mName+" "+style:mName)}}if(!flattenSpans||curStyle!=style){while(curStart<stream.start){curStart=Math.min(stream.start,curStart+5e3);f(curStart,curStyle)}curStyle=style}stream.start=stream.pos}while(curStart<stream.pos){var pos=Math.min(stream.pos,curStart+5e3);f(pos,curStyle);curStart=pos}}function findStartLine(cm,n,precise){var minindent,minline,doc=cm.doc;var lim=precise?-1:n-(cm.doc.mode.innerMode?1e3:100);for(var search=n;search>lim;--search){if(search<=doc.first){return doc.first}var line=getLine(doc,search-1),after=line.stateAfter;if(after&&(!precise||search+(after instanceof SavedContext?after.lookAhead:0)<=doc.modeFrontier)){return search}var indented=countColumn(line.text,null,cm.options.tabSize);if(minline==null||minindent>indented){minline=search-1;minindent=indented}}return minline}function retreatFrontier(doc,n){doc.modeFrontier=Math.min(doc.modeFrontier,n);if(doc.highlightFrontier<n-10){return}var start=doc.first;for(var line=n-1;line>start;line--){var saved=getLine(doc,line).stateAfter;if(saved&&(!(saved instanceof SavedContext)||line+saved.lookAhead<n)){start=line+1;break}}doc.highlightFrontier=Math.min(doc.highlightFrontier,start)}var sawReadOnlySpans=false,sawCollapsedSpans=false;function seeReadOnlySpans(){sawReadOnlySpans=true}function seeCollapsedSpans(){sawCollapsedSpans=true}function MarkedSpan(marker,from,to){this.marker=marker;this.from=from;this.to=to}function getMarkedSpanFor(spans,marker){if(spans){for(var i=0;i<spans.length;++i){var span=spans[i];if(span.marker==marker){return span}}}}function removeMarkedSpan(spans,span){var r;for(var i=0;i<spans.length;++i){if(spans[i]!=span){(r||(r=[])).push(spans[i])}}return r}function addMarkedSpan(line,span,op){var inThisOp=op&&window.WeakSet&&(op.markedSpans||(op.markedSpans=new WeakSet));if(inThisOp&&line.markedSpans&&inThisOp.has(line.markedSpans)){line.markedSpans.push(span)}else{line.markedSpans=line.markedSpans?line.markedSpans.concat([span]):[span];if(inThisOp){inThisOp.add(line.markedSpans)}}span.marker.attachLine(line)}function markedSpansBefore(old,startCh,isInsert){var nw;if(old){for(var i=0;i<old.length;++i){var span=old[i],marker=span.marker;var startsBefore=span.from==null||(marker.inclusiveLeft?span.from<=startCh:span.from<startCh);if(startsBefore||span.from==startCh&&marker.type=="bookmark"&&(!isInsert||!span.marker.insertLeft)){var endsAfter=span.to==null||(marker.inclusiveRight?span.to>=startCh:span.to>startCh);(nw||(nw=[])).push(new MarkedSpan(marker,span.from,endsAfter?null:span.to))}}}return nw}function markedSpansAfter(old,endCh,isInsert){var nw;if(old){for(var i=0;i<old.length;++i){var span=old[i],marker=span.marker;var endsAfter=span.to==null||(marker.inclusiveRight?span.to>=endCh:span.to>endCh);if(endsAfter||span.from==endCh&&marker.type=="bookmark"&&(!isInsert||span.marker.insertLeft)){var startsBefore=span.from==null||(marker.inclusiveLeft?span.from<=endCh:span.from<endCh);(nw||(nw=[])).push(new MarkedSpan(marker,startsBefore?null:span.from-endCh,span.to==null?null:span.to-endCh))}}}return nw}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;var first=markedSpansBefore(oldFirst,startCh,isInsert);var last=markedSpansAfter(oldLast,endCh,isInsert);var sameLine=change.text.length==1,offset=lst(change.text).length+(sameLine?startCh:0);if(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){for(var i$1=0;i$1<last.length;++i$1){var span$1=last[i$1];if(span$1.to!=null){span$1.to+=offset}if(span$1.from==null){var found$1=getMarkedSpanFor(first,span$1.marker);if(!found$1){span$1.from=offset;if(sameLine){(first||(first=[])).push(span$1)}}}else{span$1.from+=offset;if(sameLine){(first||(first=[])).push(span$1)}}}}if(first){first=clearEmptySpans(first)}if(last&&last!=first){last=clearEmptySpans(last)}var newMarkers=[first];if(!sameLine){var gap=change.text.length-2,gapMarkers;if(gap>0&&first){for(var i$2=0;i$2<first.length;++i$2){if(first[i$2].to==null){(gapMarkers||(gapMarkers=[])).push(new MarkedSpan(first[i$2].marker,null,null))}}}for(var i$3=0;i$3<gap;++i$3){newMarkers.push(gapMarkers)}newMarkers.push(last)}return newMarkers}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}function removeReadOnlyRanges(doc,from,to){var markers=null;doc.iter(from.line,to.line+1,(function(line){if(line.markedSpans){for(var i=0;i<line.markedSpans.length;++i){var mark=line.markedSpans[i].marker;if(mark.readOnly&&(!markers||indexOf(markers,mark)==-1)){(markers||(markers=[])).push(mark)}}}}));if(!markers){return null}var parts=[{from:from,to:to}];for(var i=0;i<markers.length;++i){var mk=markers[i],m=mk.find(0);for(var j=0;j<parts.length;++j){var p=parts[j];if(cmp(p.to,m.from)<0||cmp(p.from,m.to)>0){continue}var newParts=[j,1],dfrom=cmp(p.from,m.from),dto=cmp(p.to,m.to);if(dfrom<0||!mk.inclusiveLeft&&!dfrom){newParts.push({from:p.from,to:m.from})}if(dto>0||!mk.inclusiveRight&&!dto){newParts.push({from:m.to,to:p.to})}parts.splice.apply(parts,newParts);j+=newParts.length-3}}return parts}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}function extraLeft(marker){return marker.inclusiveLeft?-1:0}function extraRight(marker){return marker.inclusiveRight?1:0}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}function collapsedSpanAtSide(line,start){var sps=sawCollapsedSpans&&line.markedSpans,found;if(sps){for(var sp=void 0,i=0;i<sps.length;++i){sp=sps[i];if(sp.marker.collapsed&&(start?sp.from:sp.to)==null&&(!found||compareCollapsedMarkers(found,sp.marker)<0)){found=sp.marker}}}return found}function collapsedSpanAtStart(line){return collapsedSpanAtSide(line,true)}function collapsedSpanAtEnd(line){return collapsedSpanAtSide(line,false)}function collapsedSpanAround(line,ch){var sps=sawCollapsedSpans&&line.markedSpans,found;if(sps){for(var i=0;i<sps.length;++i){var sp=sps[i];if(sp.marker.collapsed&&(sp.from==null||sp.from<ch)&&(sp.to==null||sp.to>ch)&&(!found||compareCollapsedMarkers(found,sp.marker)<0)){found=sp.marker}}}return found}function conflictingCollapsedRange(doc,lineNo,from,to,marker){var line=getLine(doc,lineNo);var sps=sawCollapsedSpans&&line.markedSpans;if(sps){for(var i=0;i<sps.length;++i){var sp=sps[i];if(!sp.marker.collapsed){continue}var found=sp.marker.find(0);var fromCmp=cmp(found.from,from)||extraLeft(sp.marker)-extraLeft(marker);var toCmp=cmp(found.to,to)||extraRight(sp.marker)-extraRight(marker);if(fromCmp>=0&&toCmp<=0||fromCmp<=0&&toCmp>=0){continue}if(fromCmp<=0&&(sp.marker.inclusiveRight&&marker.inclusiveLeft?cmp(found.to,from)>=0:cmp(found.to,from)>0)||fromCmp>=0&&(sp.marker.inclusiveRight&&marker.inclusiveLeft?cmp(found.from,to)<=0:cmp(found.from,to)<0)){return true}}}}function visualLine(line){var merged;while(merged=collapsedSpanAtStart(line)){line=merged.find(-1,true).line}return line}function visualLineEnd(line){var merged;while(merged=collapsedSpanAtEnd(line)){line=merged.find(1,true).line}return line}function visualLineContinued(line){var merged,lines;while(merged=collapsedSpanAtEnd(line)){line=merged.find(1,true).line;(lines||(lines=[])).push(line)}return lines}function visualLineNo(doc,lineN){var line=getLine(doc,lineN),vis=visualLine(line);if(line==vis){return lineN}return lineNo(vis)}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}function lineIsHidden(doc,line){var sps=sawCollapsedSpans&&line.markedSpans;if(sps){for(var sp=void 0,i=0;i<sps.length;++i){sp=sps[i];if(!sp.marker.collapsed){continue}if(sp.from==null){return true}if(sp.marker.widgetNode){continue}if(sp.from==0&&sp.marker.inclusiveLeft&&lineIsHiddenInner(doc,line,sp)){return true}}}}function lineIsHiddenInner(doc,line,span){if(span.to==null){var end=span.marker.find(1,true);return lineIsHiddenInner(doc,end.line,getMarkedSpanFor(end.line.markedSpans,span.marker))}if(span.marker.inclusiveRight&&span.to==line.text.length){return true}for(var sp=void 0,i=0;i<line.markedSpans.length;++i){sp=line.markedSpans[i];if(sp.marker.collapsed&&!sp.marker.widgetNode&&sp.from==span.to&&(sp.to==null||sp.to!=span.from)&&(sp.marker.inclusiveLeft||span.marker.inclusiveRight)&&lineIsHiddenInner(doc,line,sp)){return true}}}function heightAtLine(lineObj){lineObj=visualLine(lineObj);var h=0,chunk=lineObj.parent;for(var i=0;i<chunk.lines.length;++i){var line=chunk.lines[i];if(line==lineObj){break}else{h+=line.height}}for(var p=chunk.parent;p;chunk=p,p=chunk.parent){for(var i$1=0;i$1<p.children.length;++i$1){var cur=p.children[i$1];if(cur==chunk){break}else{h+=cur.height}}}return h}function lineLength(line){if(line.height==0){return 0}var len=line.text.length,merged,cur=line;while(merged=collapsedSpanAtStart(cur)){var found=merged.find(0,true);cur=found.from.line;len+=found.from.ch-found.to.ch}cur=line;while(merged=collapsedSpanAtEnd(cur)){var found$1=merged.find(0,true);len-=cur.text.length-found$1.from.ch;cur=found$1.to.line;len+=cur.text.length-found$1.to.ch}return len}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}}))}var Line=function(text,markedSpans,estimateHeight){this.text=text;attachMarkedSpans(this,markedSpans);this.height=estimateHeight?estimateHeight(this):1};Line.prototype.lineNo=function(){return lineNo(this)};eventMixin(Line);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)}}function cleanUpLine(line){line.parent=null;detachMarkedSpans(line)}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-$&"))}function buildLineContent(cm,lineView){var content=eltP("span",null,null,webkit?"padding-right: .1px":null);var builder={pre:eltP("pre",[content],"CodeMirror-line"),content:content,col:0,pos:0,cm:cm,trailingSpace:false,splitSpaces:cm.getOption("lineWrapping")};lineView.measure={};for(var i=0;i<=(lineView.rest?lineView.rest.length:0);i++){var line=i?lineView.rest[i-1]:lineView.line,order=void 0;builder.pos=0;builder.addToken=buildToken;if(hasBadBidiRects(cm.display.measure)&&(order=getOrder(line,cm.doc.direction))){builder.addToken=buildTokenBadBidi(builder.addToken,order)}builder.map=[];var allowFrontierUpdate=lineView!=cm.display.externalMeasured&&lineNo(line);insertLineContent(line,builder,getLineStyles(cm,line,allowFrontierUpdate));if(line.styleClasses){if(line.styleClasses.bgClass){builder.bgClass=joinClasses(line.styleClasses.bgClass,builder.bgClass||"")}if(line.styleClasses.textClass){builder.textClass=joinClasses(line.styleClasses.textClass,builder.textClass||"")}}if(builder.map.length==0){builder.map.push(0,0,builder.content.appendChild(zeroWidthElement(cm.display.measure)))}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({})}}if(webkit){var last=builder.content.lastChild;if(/\bcm-tab\b/.test(last.className)||last.querySelector&&last.querySelector(".cm-tab")){builder.content.className="cm-tab-wrap-hack"}}signal(cm,"renderLine",cm,lineView.line,builder.pre);if(builder.pre.className){builder.textClass=joinClasses(builder.pre.className,builder.textClass||"")}return builder}function defaultSpecialCharPlaceholder(ch){var token=elt("span","•","cm-invalidchar");token.title="\\u"+ch.charCodeAt(0).toString(16);token.setAttribute("aria-label",token.title);return token}function buildToken(builder,text,style,startStyle,endStyle,css,attributes){if(!text){return}var displayText=builder.splitSpaces?splitSpaces(text,builder.trailingSpace):text;var special=builder.cm.state.specialChars,mustWrap=false;var content;if(!special.test(text)){builder.col+=text.length;content=document.createTextNode(displayText);builder.map.push(builder.pos,builder.pos+text.length,content);if(ie&&ie_version<9){mustWrap=true}builder.pos+=text.length}else{content=document.createDocumentFragment();var pos=0;while(true){special.lastIndex=pos;var m=special.exec(text);var skipped=m?m.index-pos:text.length-pos;if(skipped){var txt=document.createTextNode(displayText.slice(pos,pos+skipped));if(ie&&ie_version<9){content.appendChild(elt("span",[txt]))}else{content.appendChild(txt)}builder.map.push(builder.pos,builder.pos+skipped,txt);builder.col+=skipped;builder.pos+=skipped}if(!m){break}pos+=skipped+1;var txt$1=void 0;if(m[0]=="\t"){var tabSize=builder.cm.options.tabSize,tabWidth=tabSize-builder.col%tabSize;txt$1=content.appendChild(elt("span",spaceStr(tabWidth),"cm-tab"));txt$1.setAttribute("role","presentation");txt$1.setAttribute("cm-text","\t");builder.col+=tabWidth}else if(m[0]=="\r"||m[0]=="\n"){txt$1=content.appendChild(elt("span",m[0]=="\r"?"␍":"␤","cm-invalidchar"));txt$1.setAttribute("cm-text",m[0]);builder.col+=1}else{txt$1=builder.cm.options.specialCharPlaceholder(m[0]);txt$1.setAttribute("cm-text",m[0]);if(ie&&ie_version<9){content.appendChild(elt("span",[txt$1]))}else{content.appendChild(txt$1)}builder.col+=1}builder.map.push(builder.pos,builder.pos+1,txt$1);builder.pos++}}builder.trailingSpace=displayText.charCodeAt(text.length-1)==32;if(style||startStyle||endStyle||mustWrap||css||attributes){var fullStyle=style||"";if(startStyle){fullStyle+=startStyle}if(endStyle){fullStyle+=endStyle}var token=elt("span",[content],fullStyle,css);if(attributes){for(var attr in attributes){if(attributes.hasOwnProperty(attr)&&attr!="style"&&attr!="class"){token.setAttribute(attr,attributes[attr])}}}return builder.content.appendChild(token)}builder.content.appendChild(content)}function splitSpaces(text,trailingBefore){if(text.length>1&&!/  /.test(text)){return text}var spaceBefore=trailingBefore,result="";for(var i=0;i<text.length;i++){var ch=text.charAt(i);if(ch==" "&&spaceBefore&&(i==text.length-1||text.charCodeAt(i+1)==32)){ch=" "}result+=ch;spaceBefore=ch==" "}return result}function buildTokenBadBidi(inner,order){return function(builder,text,style,startStyle,endStyle,css,attributes){style=style?style+" cm-force-border":"cm-force-border";var start=builder.pos,end=start+text.length;for(;;){var part=void 0;for(var i=0;i<order.length;i++){part=order[i];if(part.to>start&&part.from<=start){break}}if(part.to>=end){return inner(builder,text,style,startStyle,endStyle,css,attributes)}inner(builder,text.slice(0,part.to-start),style,startStyle,null,css,attributes);startStyle=null;text=text.slice(part.to-start);start=part.to}}}function buildCollapsedSpan(builder,size,marker,ignoreWidget){var widget=!ignoreWidget&&marker.widgetNode;if(widget){builder.map.push(builder.pos,builder.pos+size,widget)}if(!ignoreWidget&&builder.cm.display.input.needsContentAttribute){if(!widget){widget=builder.content.appendChild(document.createElement("span"))}widget.setAttribute("cm-marker",marker.id)}if(widget){builder.cm.display.input.setUneditable(widget);builder.content.appendChild(widget)}builder.pos+=size;builder.trailingSpace=false}function insertLineContent(line,builder,styles){var spans=line.markedSpans,allText=line.text,at=0;if(!spans){for(var i$1=1;i$1<styles.length;i$1+=2){builder.addToken(builder,allText.slice(at,at=styles[i$1]),interpretTokenStyle(styles[i$1+1],builder.cm.options))}return}var len=allText.length,pos=0,i=1,text="",style,css;var nextChange=0,spanStyle,spanEndStyle,spanStartStyle,collapsed,attributes;for(;;){if(nextChange==pos){spanStyle=spanEndStyle=spanStartStyle=css="";attributes=null;collapsed=null;nextChange=Infinity;var foundBookmarks=[],endStyles=void 0;for(var j=0;j<spans.length;++j){var sp=spans[j],m=sp.marker;if(m.type=="bookmark"&&sp.from==pos&&m.widgetNode){foundBookmarks.push(m)}else if(sp.from<=pos&&(sp.to==null||sp.to>pos||m.collapsed&&sp.to==pos&&sp.from==pos)){if(sp.to!=null&&sp.to!=pos&&nextChange>sp.to){nextChange=sp.to;spanEndStyle=""}if(m.className){spanStyle+=" "+m.className}if(m.css){css=(css?css+";":"")+m.css}if(m.startStyle&&sp.from==pos){spanStartStyle+=" "+m.startStyle}if(m.endStyle&&sp.to==nextChange){(endStyles||(endStyles=[])).push(m.endStyle,sp.to)}if(m.title){(attributes||(attributes={})).title=m.title}if(m.attributes){for(var attr in m.attributes){(attributes||(attributes={}))[attr]=m.attributes[attr]}}if(m.collapsed&&(!collapsed||compareCollapsedMarkers(collapsed.marker,m)<0)){collapsed=sp}}else if(sp.from>pos&&nextChange>sp.from){nextChange=sp.from}}if(endStyles){for(var j$1=0;j$1<endStyles.length;j$1+=2){if(endStyles[j$1+1]==nextChange){spanEndStyle+=" "+endStyles[j$1]}}}if(!collapsed||collapsed.from==pos){for(var j$2=0;j$2<foundBookmarks.length;++j$2){buildCollapsedSpan(builder,0,foundBookmarks[j$2])}}if(collapsed&&(collapsed.from||0)==pos){buildCollapsedSpan(builder,(collapsed.to==null?len+1:collapsed.to)-pos,collapsed.marker,collapsed.from==null);if(collapsed.to==null){return}if(collapsed.to==pos){collapsed=false}}}if(pos>=len){break}var upto=Math.min(len,nextChange);while(true){if(text){var end=pos+text.length;if(!collapsed){var tokenText=end>upto?text.slice(0,upto-pos):text;builder.addToken(builder,tokenText,style?style+spanStyle:spanStyle,spanStartStyle,pos+tokenText.length==nextChange?spanEndStyle:"",css,attributes)}if(end>=upto){text=text.slice(upto-pos);pos=upto;break}pos=end;spanStartStyle=""}text=allText.slice(at,at=styles[i++]);style=interpretTokenStyle(styles[i++],builder.cm.options)}}}function LineView(doc,line,lineN){this.line=line;this.rest=visualLineContinued(line);this.size=this.rest?lineNo(lst(this.rest))-lineN+1:1;this.node=this.text=null;this.hidden=lineIsHidden(doc,line)}function buildViewArray(cm,from,to){var array=[],nextPos;for(var pos=from;pos<to;pos=nextPos){var view=new LineView(cm.doc,getLine(cm.doc,pos),pos);nextPos=pos+view.size;array.push(view)}return array}var operationGroup=null;function pushOperation(op){if(operationGroup){operationGroup.ops.push(op)}else{op.ownsGroup=operationGroup={ops:[op],delayedCallbacks:[]}}}function fireCallbacksForOps(group){var callbacks=group.delayedCallbacks,i=0;do{for(;i<callbacks.length;i++){callbacks[i].call(null)}for(var j=0;j<group.ops.length;j++){var op=group.ops[j];if(op.cursorActivityHandlers){while(op.cursorActivityCalled<op.cursorActivityHandlers.length){op.cursorActivityHandlers[op.cursorActivityCalled++].call(null,op.cm)}}}}while(i<callbacks.length)}function finishOperation(op,endCb){var group=op.ownsGroup;if(!group){return}try{fireCallbacksForOps(group)}finally{operationGroup=null;endCb(group)}}var orphanDelayedCallbacks=null;function signalLater(emitter,type){var arr=getHandlers(emitter,type);if(!arr.length){return}var args=Array.prototype.slice.call(arguments,2),list;if(operationGroup){list=operationGroup.delayedCallbacks}else if(orphanDelayedCallbacks){list=orphanDelayedCallbacks}else{list=orphanDelayedCallbacks=[];setTimeout(fireOrphanDelayed,0)}var loop=function(i){list.push((function(){return arr[i].apply(null,args)}))};for(var i=0;i<arr.length;++i)loop(i)}function fireOrphanDelayed(){var delayed=orphanDelayedCallbacks;orphanDelayedCallbacks=null;for(var i=0;i<delayed.length;++i){delayed[i]()}}function updateLineForChanges(cm,lineView,lineN,dims){for(var j=0;j<lineView.changes.length;j++){var type=lineView.changes[j];if(type=="text"){updateLineText(cm,lineView)}else if(type=="gutter"){updateLineGutter(cm,lineView,lineN,dims)}else if(type=="class"){updateLineClasses(cm,lineView)}else if(type=="widget"){updateLineWidgets(cm,lineView,dims)}}lineView.changes=null}function ensureLineWrapped(lineView){if(lineView.node==lineView.text){lineView.node=elt("div",null,null,"position: relative");if(lineView.text.parentNode){lineView.text.parentNode.replaceChild(lineView.node,lineView.text)}lineView.node.appendChild(lineView.text);if(ie&&ie_version<8){lineView.node.style.zIndex=2}}return lineView.node}function updateLineBackground(cm,lineView){var cls=lineView.bgClass?lineView.bgClass+" "+(lineView.line.bgClass||""):lineView.line.bgClass;if(cls){cls+=" CodeMirror-linebackground"}if(lineView.background){if(cls){lineView.background.className=cls}else{lineView.background.parentNode.removeChild(lineView.background);lineView.background=null}}else if(cls){var wrap=ensureLineWrapped(lineView);lineView.background=wrap.insertBefore(elt("div",null,cls),wrap.firstChild);cm.display.input.setUneditable(lineView.background)}}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)}function updateLineText(cm,lineView){var cls=lineView.text.className;var built=getLineContent(cm,lineView);if(lineView.text==lineView.node){lineView.node=built.pre}lineView.text.parentNode.replaceChild(built.pre,lineView.text);lineView.text=built.pre;if(built.bgClass!=lineView.bgClass||built.textClass!=lineView.textClass){lineView.bgClass=built.bgClass;lineView.textClass=built.textClass;updateLineClasses(cm,lineView)}else if(cls){lineView.text.className=cls}}function updateLineClasses(cm,lineView){updateLineBackground(cm,lineView);if(lineView.line.wrapClass){ensureLineWrapped(lineView).className=lineView.line.wrapClass}else if(lineView.node!=lineView.text){lineView.node.className=""}var textClass=lineView.textClass?lineView.textClass+" "+(lineView.line.textClass||""):lineView.line.textClass;lineView.text.className=textClass||""}function updateLineGutter(cm,lineView,lineN,dims){if(lineView.gutter){lineView.node.removeChild(lineView.gutter);lineView.gutter=null}if(lineView.gutterBackground){lineView.node.removeChild(lineView.gutterBackground);lineView.gutterBackground=null}if(lineView.line.gutterClass){var wrap=ensureLineWrapped(lineView);lineView.gutterBackground=elt("div",null,"CodeMirror-gutter-background "+lineView.line.gutterClass,"left: "+(cm.options.fixedGutter?dims.fixedPos:-dims.gutterTotalWidth)+"px; width: "+dims.gutterTotalWidth+"px");cm.display.input.setUneditable(lineView.gutterBackground);wrap.insertBefore(lineView.gutterBackground,lineView.text)}var markers=lineView.line.gutterMarkers;if(cm.options.lineNumbers||markers){var wrap$1=ensureLineWrapped(lineView);var gutterWrap=lineView.gutter=elt("div",null,"CodeMirror-gutter-wrapper","left: "+(cm.options.fixedGutter?dims.fixedPos:-dims.gutterTotalWidth)+"px");gutterWrap.setAttribute("aria-hidden","true");cm.display.input.setUneditable(gutterWrap);wrap$1.insertBefore(gutterWrap,lineView.text);if(lineView.line.gutterClass){gutterWrap.className+=" "+lineView.line.gutterClass}if(cm.options.lineNumbers&&(!markers||!markers["CodeMirror-linenumbers"])){lineView.lineNumber=gutterWrap.appendChild(elt("div",lineNumberFor(cm.options,lineN),"CodeMirror-linenumber CodeMirror-gutter-elt","left: "+dims.gutterLeft["CodeMirror-linenumbers"]+"px; width: "+cm.display.lineNumInnerWidth+"px"))}if(markers){for(var k=0;k<cm.display.gutterSpecs.length;++k){var id=cm.display.gutterSpecs[k].className,found=markers.hasOwnProperty(id)&&markers[id];if(found){gutterWrap.appendChild(elt("div",[found],"CodeMirror-gutter-elt","left: "+dims.gutterLeft[id]+"px; width: "+dims.gutterWidth[id]+"px"))}}}}}function updateLineWidgets(cm,lineView,dims){if(lineView.alignable){lineView.alignable=null}var isWidget=classTest("CodeMirror-linewidget");for(var node=lineView.node.firstChild,next=void 0;node;node=next){next=node.nextSibling;if(isWidget.test(node.className)){lineView.node.removeChild(node)}}insertLineWidgets(cm,lineView,dims)}function buildLineElement(cm,lineView,lineN,dims){var built=getLineContent(cm,lineView);lineView.text=lineView.node=built.pre;if(built.bgClass){lineView.bgClass=built.bgClass}if(built.textClass){lineView.textClass=built.textClass}updateLineClasses(cm,lineView);updateLineGutter(cm,lineView,lineN,dims);insertLineWidgets(cm,lineView,dims);return lineView.node}function insertLineWidgets(cm,lineView,dims){insertLineWidgetsFor(cm,lineView.line,lineView,dims,true);if(lineView.rest){for(var i=0;i<lineView.rest.length;i++){insertLineWidgetsFor(cm,lineView.rest[i],lineView,dims,false)}}}function insertLineWidgetsFor(cm,line,lineView,dims,allowAbove){if(!line.widgets){return}var wrap=ensureLineWrapped(lineView);for(var i=0,ws=line.widgets;i<ws.length;++i){var widget=ws[i],node=elt("div",[widget.node],"CodeMirror-linewidget"+(widget.className?" "+widget.className:""));if(!widget.handleMouseEvents){node.setAttribute("cm-ignore-events","true")}positionLineWidget(widget,node,lineView,dims);cm.display.input.setUneditable(node);if(allowAbove&&widget.above){wrap.insertBefore(node,lineView.gutter||lineView.text)}else{wrap.appendChild(node)}signalLater(widget,"redraw")}}function positionLineWidget(widget,node,lineView,dims){if(widget.noHScroll){(lineView.alignable||(lineView.alignable=[])).push(node);var width=dims.wrapperWidth;node.style.left=dims.fixedPos+"px";if(!widget.coverGutter){width-=dims.gutterTotalWidth;node.style.paddingLeft=dims.gutterTotalWidth+"px"}node.style.width=width+"px"}if(widget.coverGutter){node.style.zIndex=5;node.style.position="relative";if(!widget.noHScroll){node.style.marginLeft=-dims.gutterTotalWidth+"px"}}}function widgetHeight(widget){if(widget.height!=null){return widget.height}var cm=widget.doc.cm;if(!cm){return 0}if(!contains(document.body,widget.node)){var parentStyle="position: relative;";if(widget.coverGutter){parentStyle+="margin-left: -"+cm.display.gutters.offsetWidth+"px;"}if(widget.noHScroll){parentStyle+="width: "+cm.display.wrapper.clientWidth+"px;"}removeChildrenAndAdd(cm.display.measure,elt("div",[widget.node],null,parentStyle))}return widget.height=widget.node.parentNode.offsetHeight}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}}}function paddingTop(display){return display.lineSpace.offsetTop}function paddingVert(display){return display.mover.offsetHeight-display.lineSpace.offsetHeight}function paddingH(display){if(display.cachedPaddingH){return display.cachedPaddingH}var e=removeChildrenAndAdd(display.measure,elt("pre","x","CodeMirror-line-like"));var style=window.getComputedStyle?window.getComputedStyle(e):e.currentStyle;var data={left:parseInt(style.paddingLeft),right:parseInt(style.paddingRight)};if(!isNaN(data.left)&&!isNaN(data.right)){display.cachedPaddingH=data}return data}function scrollGap(cm){return scrollerGap-cm.display.nativeBarWidth}function displayWidth(cm){return cm.display.scroller.clientWidth-scrollGap(cm)-cm.display.barWidth}function displayHeight(cm){return cm.display.scroller.clientHeight-scrollGap(cm)-cm.display.barHeight}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)}}function mapFromLineView(lineView,line,lineN){if(lineView.line==line){return{map:lineView.measure.map,cache:lineView.measure.cache}}if(lineView.rest){for(var i=0;i<lineView.rest.length;i++){if(lineView.rest[i]==line){return{map:lineView.measure.maps[i],cache:lineView.measure.caches[i]}}}for(var i$1=0;i$1<lineView.rest.length;i$1++){if(lineNo(lineView.rest[i$1])>lineN){return{map:lineView.measure.maps[i$1],cache:lineView.measure.caches[i$1],before:true}}}}}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}function measureChar(cm,line,ch,bias){return measureCharPrepared(cm,prepareMeasureForLine(cm,line),ch,bias)}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}}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}}function measureCharPrepared(cm,prepared,ch,bias,varHeight){if(prepared.before){ch=-1}var key=ch+(bias||""),found;if(prepared.cache.hasOwnProperty(key)){found=prepared.cache[key]}else{if(!prepared.rect){prepared.rect=prepared.view.text.getBoundingClientRect()}if(!prepared.hasHeights){ensureLineHeights(cm,prepared.view,prepared.rect);prepared.hasHeights=true}found=measureCharInner(cm,prepared,ch,bias);if(!found.bogus){prepared.cache[key]=found}}return{left:found.left,right:found.right,top:varHeight?found.rtop:found.top,bottom:varHeight?found.rbottom:found.bottom}}var nullRect={left:0,right:0,top:0,bottom:0};function nodeAndOffsetInLineMap(map,ch,bias){var node,start,end,collapse,mStart,mEnd;for(var i=0;i<map.length;i+=3){mStart=map[i];mEnd=map[i+1];if(ch<mStart){start=0;end=1;collapse="left"}else if(ch<mEnd){start=ch-mStart;end=start+1}else if(i==map.length-3||ch==mEnd&&map[i+3]>ch){end=mEnd-mStart;start=end-1;if(ch>=mEnd){collapse="right"}}if(start!=null){node=map[i+2];if(mStart==mEnd&&bias==(node.insertLeft?"left":"right")){collapse=bias}if(bias=="left"&&start==0){while(i&&map[i-2]==map[i-3]&&map[i-1].insertLeft){node=map[(i-=3)+2];collapse="left"}}if(bias=="right"&&start==mEnd-mStart){while(i<map.length-3&&map[i+3]==map[i+4]&&!map[i+5].insertLeft){node=map[(i+=3)+2];collapse="right"}}break}}return{node:node,start:start,end:end,collapse:collapse,coverStart:mStart,coverEnd:mEnd}}function getUsefulRect(rects,bias){var rect=nullRect;if(bias=="left"){for(var i=0;i<rects.length;i++){if((rect=rects[i]).left!=rect.right){break}}}else{for(var i$1=rects.length-1;i$1>=0;i$1--){if((rect=rects[i$1]).left!=rect.right){break}}}return rect}function measureCharInner(cm,prepared,ch,bias){var place=nodeAndOffsetInLineMap(prepared.map,ch,bias);var node=place.node,start=place.start,end=place.end,collapse=place.collapse;var rect;if(node.nodeType==3){for(var i$1=0;i$1<4;i$1++){while(start&&isExtendingChar(prepared.line.text.charAt(place.coverStart+start))){--start}while(place.coverStart+end<place.coverEnd&&isExtendingChar(prepared.line.text.charAt(place.coverStart+end))){++end}if(ie&&ie_version<9&&start==0&&end==place.coverEnd-place.coverStart){rect=node.parentNode.getBoundingClientRect()}else{rect=getUsefulRect(range(node,start,end).getClientRects(),bias)}if(rect.left||rect.right||start==0){break}end=start;start=start-1;collapse="right"}if(ie&&ie_version<11){rect=maybeUpdateRectForZooming(cm.display.measure,rect)}}else{if(start>0){collapse=bias="right"}var rects;if(cm.options.lineWrapping&&(rects=node.getClientRects()).length>1){rect=rects[bias=="right"?rects.length-1:0]}else{rect=node.getBoundingClientRect()}}if(ie&&ie_version<9&&!start&&(!rect||!rect.left&&!rect.right)){var rSpan=node.parentNode.getClientRects()[0];if(rSpan){rect={left:rSpan.left,right:rSpan.left+charWidth(cm.display),top:rSpan.top,bottom:rSpan.bottom}}else{rect=nullRect}}var rtop=rect.top-prepared.rect.top,rbot=rect.bottom-prepared.rect.top;var mid=(rtop+rbot)/2;var heights=prepared.view.measure.heights;var i=0;for(;i<heights.length-1;i++){if(mid<heights[i]){break}}var top=i?heights[i-1]:0,bot=heights[i];var result={left:(collapse=="right"?rect.right:rect.left)-prepared.rect.left,right:(collapse=="left"?rect.left:rect.right)-prepared.rect.left,top:top,bottom:bot};if(!rect.left&&!rect.right){result.bogus=true}if(!cm.options.singleCursorHeightPerLine){result.rtop=rtop;result.rbottom=rbot}return result}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(doc){if(chrome&&android){return-(doc.body.getBoundingClientRect().left-parseInt(getComputedStyle(doc.body).marginLeft))}return doc.defaultView.pageXOffset||(doc.documentElement||doc.body).scrollLeft}function pageScrollY(doc){if(chrome&&android){return-(doc.body.getBoundingClientRect().top-parseInt(getComputedStyle(doc.body).marginTop))}return doc.defaultView.pageYOffset||(doc.documentElement||doc.body).scrollTop}function widgetTopHeight(lineObj){var ref=visualLine(lineObj);var widgets=ref.widgets;var height=0;if(widgets){for(var i=0;i<widgets.length;++i){if(widgets[i].above){height+=widgetHeight(widgets[i])}}}return height}function intoCoordSystem(cm,lineObj,rect,context,includeWidgets){if(!includeWidgets){var height=widgetTopHeight(lineObj);rect.top+=height;rect.bottom+=height}if(context=="line"){return rect}if(!context){context="local"}var yOff=heightAtLine(lineObj);if(context=="local"){yOff+=paddingTop(cm.display)}else{yOff-=cm.display.viewOffset}if(context=="page"||context=="window"){var lOff=cm.display.lineSpace.getBoundingClientRect();yOff+=lOff.top+(context=="window"?0:pageScrollY(doc(cm)));var xOff=lOff.left+(context=="window"?0:pageScrollX(doc(cm)));rect.left+=xOff;rect.right+=xOff}rect.top+=yOff;rect.bottom+=yOff;return rect}function fromCoordSystem(cm,coords,context){if(context=="div"){return coords}var left=coords.left,top=coords.top;if(context=="page"){left-=pageScrollX(doc(cm));top-=pageScrollY(doc(cm))}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)}function cursorCoords(cm,pos,context,lineObj,preparedMeasure,varHeight){lineObj=lineObj||getLine(cm.doc,pos.line);if(!preparedMeasure){preparedMeasure=prepareMeasureForLine(cm,lineObj)}function get(ch,right){var m=measureCharPrepared(cm,preparedMeasure,ch,right?"right":"left",varHeight);if(right){m.left=m.right}else{m.right=m.left}return intoCoordSystem(cm,lineObj,m,context)}var order=getOrder(lineObj,cm.doc.direction),ch=pos.ch,sticky=pos.sticky;if(ch>=lineObj.text.length){ch=lineObj.text.length;sticky="before"}else if(ch<=0){ch=0;sticky="after"}if(!order){return get(sticky=="before"?ch-1:ch,sticky=="before")}function getBidi(ch,partPos,invert){var part=order[partPos],right=part.level==1;return get(invert?ch-1:ch,right!=invert)}var partPos=getBidiPartAt(order,ch,sticky);var other=bidiOther;var val=getBidi(ch,partPos,sticky=="before");if(other!=null){val.other=getBidi(ch,other,sticky!="before")}return val}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}}function PosWithInfo(line,ch,sticky,outside,xRel){var pos=Pos(line,ch,sticky);pos.xRel=xRel;if(outside){pos.outside=outside}return pos}function coordsChar(cm,x,y){var doc=cm.doc;y+=cm.display.viewOffset;if(y<0){return PosWithInfo(doc.first,0,null,-1,-1)}var lineN=lineAtHeight(doc,y),last=doc.first+doc.size-1;if(lineN>last){return PosWithInfo(doc.first+doc.size-1,getLine(doc,last).text.length,null,1,1)}if(x<0){x=0}var lineObj=getLine(doc,lineN);for(;;){var found=coordsCharInner(cm,lineObj,lineN,x,y);var collapsed=collapsedSpanAround(lineObj,found.ch+(found.xRel>0||found.outside>0?1:0));if(!collapsed){return found}var rangeEnd=collapsed.find(1);if(rangeEnd.line==lineN){return rangeEnd}lineObj=getLine(doc,lineN=rangeEnd.line)}}function wrappedLineExtent(cm,lineObj,preparedMeasure,y){y-=widgetTopHeight(lineObj);var end=lineObj.text.length;var begin=findFirst((function(ch){return measureCharPrepared(cm,preparedMeasure,ch-1).bottom<=y}),end,0);end=findFirst((function(ch){return measureCharPrepared(cm,preparedMeasure,ch).top>y}),begin,end);return{begin:begin,end:end}}function wrappedLineExtentChar(cm,lineObj,preparedMeasure,target){if(!preparedMeasure){preparedMeasure=prepareMeasureForLine(cm,lineObj)}var targetTop=intoCoordSystem(cm,lineObj,measureCharPrepared(cm,preparedMeasure,target),"line").top;return wrappedLineExtent(cm,lineObj,preparedMeasure,targetTop)}function boxIsAfter(box,x,y,left){return box.bottom<=y?false:box.top>y?true:(left?box.left:box.right)>x}function coordsCharInner(cm,lineObj,lineNo,x,y){y-=heightAtLine(lineObj);var preparedMeasure=prepareMeasureForLine(cm,lineObj);var widgetHeight=widgetTopHeight(lineObj);var begin=0,end=lineObj.text.length,ltr=true;var order=getOrder(lineObj,cm.doc.direction);if(order){var part=(cm.options.lineWrapping?coordsBidiPartWrapped:coordsBidiPart)(cm,lineObj,lineNo,preparedMeasure,order,x,y);ltr=part.level!=1;begin=ltr?part.from:part.to-1;end=ltr?part.to:part.from-1}var chAround=null,boxAround=null;var ch=findFirst((function(ch){var box=measureCharPrepared(cm,preparedMeasure,ch);box.top+=widgetHeight;box.bottom+=widgetHeight;if(!boxIsAfter(box,x,y,false)){return false}if(box.top<=y&&box.left<=x){chAround=ch;boxAround=box}return true}),begin,end);var baseX,sticky,outside=false;if(boxAround){var atLeft=x-boxAround.left<boxAround.right-x,atStart=atLeft==ltr;ch=chAround+(atStart?0:1);sticky=atStart?"after":"before";baseX=atLeft?boxAround.left:boxAround.right}else{if(!ltr&&(ch==end||ch==begin)){ch++}sticky=ch==0?"after":ch==lineObj.text.length?"before":measureCharPrepared(cm,preparedMeasure,ch-(ltr?1:0)).bottom+widgetHeight<=y==ltr?"after":"before";var coords=cursorCoords(cm,Pos(lineNo,ch,sticky),"line",lineObj,preparedMeasure);baseX=coords.left;outside=y<coords.top?-1:y>=coords.bottom?1:0}ch=skipExtendingChars(lineObj.text,ch,1);return PosWithInfo(lineNo,ch,sticky,outside,x-baseX)}function coordsBidiPart(cm,lineObj,lineNo,preparedMeasure,order,x,y){var index=findFirst((function(i){var part=order[i],ltr=part.level!=1;return boxIsAfter(cursorCoords(cm,Pos(lineNo,ltr?part.to:part.from,ltr?"before":"after"),"line",lineObj,preparedMeasure),x,y,true)}),0,order.length-1);var part=order[index];if(index>0){var ltr=part.level!=1;var start=cursorCoords(cm,Pos(lineNo,ltr?part.from:part.to,ltr?"after":"before"),"line",lineObj,preparedMeasure);if(boxIsAfter(start,x,y,true)&&start.top>y){part=order[index-1]}}return part}function coordsBidiPartWrapped(cm,lineObj,_lineNo,preparedMeasure,order,x,y){var ref=wrappedLineExtent(cm,lineObj,preparedMeasure,y);var begin=ref.begin;var end=ref.end;if(/\s/.test(lineObj.text.charAt(end-1))){end--}var part=null,closestDist=null;for(var i=0;i<order.length;i++){var p=order[i];if(p.from>=end||p.to<=begin){continue}var ltr=p.level!=1;var endX=measureCharPrepared(cm,preparedMeasure,ltr?Math.min(end,p.to)-1:Math.max(begin,p.from)).right;var dist=endX<x?x-endX+1e9:endX-x;if(!part||closestDist>dist){part=p;closestDist=dist}}if(!part){part=order[order.length-1]}if(part.from<begin){part={from:begin,to:part.to,level:part.level}}if(part.to>end){part={from:part.from,to:end,level:part.level}}return part}var measureText;function textHeight(display){if(display.cachedTextHeight!=null){return display.cachedTextHeight}if(measureText==null){measureText=elt("pre",null,"CodeMirror-line-like");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}function charWidth(display){if(display.cachedCharWidth!=null){return display.cachedCharWidth}var anchor=elt("span","xxxxxxxxxx");var pre=elt("pre",[anchor],"CodeMirror-line-like");removeChildrenAndAdd(display.measure,pre);var rect=anchor.getBoundingClientRect(),width=(rect.right-rect.left)/10;if(width>2){display.cachedCharWidth=width}return width||10}function getDimensions(cm){var d=cm.display,left={},width={};var gutterLeft=d.gutters.clientLeft;for(var n=d.gutters.firstChild,i=0;n;n=n.nextSibling,++i){var id=cm.display.gutterSpecs[i].className;left[id]=n.offsetLeft+n.clientLeft+gutterLeft;width[id]=n.clientWidth}return{fixedPos:compensateForHScroll(d),gutterTotalWidth:d.gutters.offsetWidth,gutterLeft:left,gutterWidth:width,wrapperWidth:d.wrapper.clientWidth}}function compensateForHScroll(display){return display.scroller.getBoundingClientRect().left-display.sizer.getBoundingClientRect().left}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 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();try{x=e.clientX-space.left;y=e.clientY-space.top}catch(e$1){return null}var coords=coordsChar(cm,x,y),line;if(forRect&&coords.xRel>0&&(line=getLine(cm.doc,coords.line).text).length==coords.ch){var colDiff=countColumn(line,line.length,cm.options.tabSize)-line.length;coords=Pos(coords.line,Math.max(0,Math.round((x-paddingH(cm.display).left)/charWidth(cm.display))-colDiff))}return coords}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 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){if(sawCollapsedSpans&&visualLineNo(cm.doc,from)<display.viewTo){resetView(cm)}}else if(to<=display.viewFrom){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){resetView(cm)}else if(from<=display.viewFrom){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){var cut$1=viewCuttingPoint(cm,from,from,-1);if(cut$1){display.view=display.view.slice(0,cut$1.index);display.viewTo=cut$1.lineN}else{resetView(cm)}}else{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}}}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)}}function resetView(cm){cm.display.viewFrom=cm.display.viewTo=cm.doc.first;cm.display.view=[];cm.display.viewOffset=0}function viewCuttingPoint(cm,oldN,newN,dir){var index=findViewIndex(cm,oldN),diff,view=cm.display.view;if(!sawCollapsedSpans||newN==cm.doc.first+cm.doc.size){return{index:index,lineN:newN}}var n=cm.display.viewFrom;for(var i=0;i<index;i++){n+=view[i].size}if(n!=oldN){if(dir>0){if(index==view.length-1){return null}diff=n+view[index].size-oldN;index++}else{diff=n-oldN}oldN+=diff;newN+=diff}while(visualLineNo(cm.doc,newN)!=newN){if(index==(dir<0?0:view.length-1)){return null}newN+=dir*view[index-(dir<0?1:0)].size;index+=dir}return{index:index,lineN:newN}}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}function countDirtyView(cm){var view=cm.display.view,dirty=0;for(var i=0;i<view.length;i++){var lineView=view[i];if(!lineView.hidden&&(!lineView.node||lineView.changes)){++dirty}}return dirty}function updateSelection(cm){cm.display.input.showSelection(cm.display.input.prepareSelection())}function prepareSelection(cm,primary){if(primary===void 0)primary=true;var doc=cm.doc,result={};var curFragment=result.cursors=document.createDocumentFragment();var selFragment=result.selection=document.createDocumentFragment();var customCursor=cm.options.$customCursor;if(customCursor){primary=true}for(var i=0;i<doc.sel.ranges.length;i++){if(!primary&&i==doc.sel.primIndex){continue}var range=doc.sel.ranges[i];if(range.from().line>=cm.display.viewTo||range.to().line<cm.display.viewFrom){continue}var collapsed=range.empty();if(customCursor){var head=customCursor(cm,range);if(head){drawSelectionCursor(cm,head,curFragment)}}else if(collapsed||cm.options.showCursorWhenSelecting){drawSelectionCursor(cm,range.head,curFragment)}if(!collapsed){drawSelectionRange(cm,range,selFragment)}}return result}function drawSelectionCursor(cm,head,output){var pos=cursorCoords(cm,head,"div",null,null,!cm.options.singleCursorHeightPerLine);var cursor=output.appendChild(elt("div"," ","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(/\bcm-fat-cursor\b/.test(cm.getWrapperElement().className)){var charPos=charCoords(cm,head,"div",null,null);var width=charPos.right-charPos.left;cursor.style.width=(width>0?width:cm.defaultCharWidth())+"px"}if(pos.other){var otherCursor=output.appendChild(elt("div"," ","CodeMirror-cursor CodeMirror-secondarycursor"));otherCursor.style.display="";otherCursor.style.left=pos.other.left+"px";otherCursor.style.top=pos.other.top+"px";otherCursor.style.height=(pos.other.bottom-pos.other.top)*.85+"px"}}function cmpCoords(a,b){return a.top-b.top||a.left-b.left}function drawSelectionRange(cm,range,output){var display=cm.display,doc=cm.doc;var fragment=document.createDocumentFragment();var padding=paddingH(cm.display),leftSide=padding.left;var rightSide=Math.max(display.sizerWidth,displayWidth(cm)-display.sizer.offsetLeft)-padding.right;var docLTR=doc.direction=="ltr";function add(left,top,width,bottom){if(top<0){top=0}top=Math.round(top);bottom=Math.round(bottom);fragment.appendChild(elt("div",null,"CodeMirror-selected","position: absolute; left: "+left+"px;\n                             top: "+top+"px; width: "+(width==null?rightSide-left:width)+"px;\n                             height: "+(bottom-top)+"px"))}function drawForLine(line,fromArg,toArg){var lineObj=getLine(doc,line);var lineLen=lineObj.text.length;var start,end;function coords(ch,bias){return charCoords(cm,Pos(line,ch),"div",lineObj,bias)}function wrapX(pos,dir,side){var extent=wrappedLineExtentChar(cm,lineObj,null,pos);var prop=dir=="ltr"==(side=="after")?"left":"right";var ch=side=="after"?extent.begin:extent.end-(/\s/.test(lineObj.text.charAt(extent.end-1))?2:1);return coords(ch,prop)[prop]}var order=getOrder(lineObj,doc.direction);iterateBidiSections(order,fromArg||0,toArg==null?lineLen:toArg,(function(from,to,dir,i){var ltr=dir=="ltr";var fromPos=coords(from,ltr?"left":"right");var toPos=coords(to-1,ltr?"right":"left");var openStart=fromArg==null&&from==0,openEnd=toArg==null&&to==lineLen;var first=i==0,last=!order||i==order.length-1;if(toPos.top-fromPos.top<=3){var openLeft=(docLTR?openStart:openEnd)&&first;var openRight=(docLTR?openEnd:openStart)&&last;var left=openLeft?leftSide:(ltr?fromPos:toPos).left;var right=openRight?rightSide:(ltr?toPos:fromPos).right;add(left,fromPos.top,right-left,fromPos.bottom)}else{var topLeft,topRight,botLeft,botRight;if(ltr){topLeft=docLTR&&openStart&&first?leftSide:fromPos.left;topRight=docLTR?rightSide:wrapX(from,dir,"before");botLeft=docLTR?leftSide:wrapX(to,dir,"after");botRight=docLTR&&openEnd&&last?rightSide:toPos.right}else{topLeft=!docLTR?leftSide:wrapX(from,dir,"before");topRight=!docLTR&&openStart&&first?rightSide:fromPos.right;botLeft=!docLTR&&openEnd&&last?leftSide:toPos.left;botRight=!docLTR?rightSide:wrapX(to,dir,"after")}add(topLeft,fromPos.top,topRight-topLeft,fromPos.bottom);if(fromPos.bottom<toPos.top){add(leftSide,fromPos.bottom,null,toPos.top)}add(botLeft,toPos.top,botRight-botLeft,toPos.bottom)}if(!start||cmpCoords(fromPos,start)<0){start=fromPos}if(cmpCoords(toPos,start)<0){start=toPos}if(!end||cmpCoords(fromPos,end)<0){end=fromPos}if(cmpCoords(toPos,end)<0){end=toPos}}));return{start:start,end:end}}var sFrom=range.from(),sTo=range.to();if(sFrom.line==sTo.line){drawForLine(sFrom.line,sFrom.ch,sTo.ch)}else{var fromLine=getLine(doc,sFrom.line),toLine=getLine(doc,sTo.line);var singleVLine=visualLine(fromLine)==visualLine(toLine);var leftEnd=drawForLine(sFrom.line,sFrom.ch,singleVLine?fromLine.text.length+1:null).end;var rightStart=drawForLine(sTo.line,singleVLine?0:null,sTo.ch).start;if(singleVLine){if(leftEnd.top<rightStart.top-2){add(leftEnd.right,leftEnd.top,null,leftEnd.bottom);add(leftSide,rightStart.top,rightStart.left,rightStart.bottom)}else{add(leftEnd.right,leftEnd.top,rightStart.left-leftEnd.right,leftEnd.bottom)}}if(leftEnd.bottom<rightStart.top){add(leftSide,leftEnd.bottom,null,rightStart.top)}}output.appendChild(fragment)}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(){if(!cm.hasFocus()){onBlur(cm)}display.cursorDiv.style.visibility=(on=!on)?"":"hidden"}),cm.options.cursorBlinkRate)}else if(cm.options.cursorBlinkRate<0){display.cursorDiv.style.visibility="hidden"}}function ensureFocus(cm){if(!cm.hasFocus()){cm.display.input.focus();if(!cm.state.focused){onFocus(cm)}}}function delayBlurEvent(cm){cm.state.delayingBlurEvent=true;setTimeout((function(){if(cm.state.delayingBlurEvent){cm.state.delayingBlurEvent=false;if(cm.state.focused){onBlur(cm)}}}),100)}function onFocus(cm,e){if(cm.state.delayingBlurEvent&&!cm.state.draggingText){cm.state.delayingBlurEvent=false}if(cm.options.readOnly=="nocursor"){return}if(!cm.state.focused){signal(cm,"focus",cm,e);cm.state.focused=true;addClass(cm.display.wrapper,"CodeMirror-focused");if(!cm.curOp&&cm.display.selForContextMenu!=cm.doc.sel){cm.display.input.reset();if(webkit){setTimeout((function(){return cm.display.input.reset(true)}),20)}}cm.display.input.receivedFocus()}restartBlink(cm)}function onBlur(cm,e){if(cm.state.delayingBlurEvent){return}if(cm.state.focused){signal(cm,"blur",cm,e);cm.state.focused=false;rmClass(cm.display.wrapper,"CodeMirror-focused")}clearInterval(cm.display.blinker);setTimeout((function(){if(!cm.state.focused){cm.display.shift=false}}),150)}function updateHeightsInViewport(cm){var display=cm.display;var prevBottom=display.lineDiv.offsetTop;var viewTop=Math.max(0,display.scroller.getBoundingClientRect().top);var oldHeight=display.lineDiv.getBoundingClientRect().top;var mustScroll=0;for(var i=0;i<display.view.length;i++){var cur=display.view[i],wrapping=cm.options.lineWrapping;var height=void 0,width=0;if(cur.hidden){continue}oldHeight+=cur.line.height;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;if(!wrapping&&cur.text.firstChild){width=cur.text.firstChild.getBoundingClientRect().right-box.left-1}}var diff=cur.line.height-height;if(diff>.005||diff<-.005){if(oldHeight<viewTop){mustScroll-=diff}updateLineHeight(cur.line,height);updateWidgetHeight(cur.line);if(cur.rest){for(var j=0;j<cur.rest.length;j++){updateWidgetHeight(cur.rest[j])}}}if(width>cm.display.sizerWidth){var chWidth=Math.ceil(width/charWidth(cm.display));if(chWidth>cm.display.maxLineLength){cm.display.maxLineLength=chWidth;cm.display.maxLine=cur.line;cm.display.maxLineChanged=true}}}if(Math.abs(mustScroll)>2){display.scroller.scrollTop+=mustScroll}}function updateWidgetHeight(line){if(line.widgets){for(var i=0;i<line.widgets.length;++i){var w=line.widgets[i],parent=w.node.parentNode;if(parent){w.height=parent.offsetHeight}}}}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);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)}}function maybeScrollWindow(cm,rect){if(signalDOMEvent(cm,"scrollCursorIntoView")){return}var display=cm.display,box=display.sizer.getBoundingClientRect(),doScroll=null;var doc=display.wrapper.ownerDocument;if(rect.top+box.top<0){doScroll=true}else if(rect.bottom+box.top>(doc.defaultView.innerHeight||doc.documentElement.clientHeight)){doScroll=false}if(doScroll!=null&&!phantom){var scrollNode=elt("div","​",null,"position: absolute;\n                         top: "+(rect.top-display.viewOffset-paddingTop(cm.display))+"px;\n                         height: "+(rect.bottom-rect.top+scrollGap(cm)+display.barHeight)+"px;\n                         left: "+rect.left+"px; width: "+Math.max(2,rect.right-rect.left)+"px;");cm.display.lineSpace.appendChild(scrollNode);scrollNode.scrollIntoView(doScroll);cm.display.lineSpace.removeChild(scrollNode)}}function scrollPosIntoView(cm,pos,end,margin){if(margin==null){margin=0}var rect;if(!cm.options.lineWrapping&&pos==end){end=pos.sticky=="before"?Pos(pos.line,pos.ch+1,"before"):pos;pos=pos.ch?Pos(pos.line,pos.sticky=="before"?pos.ch-1:pos.ch,"after"):pos}for(var limit=0;limit<5;limit++){var changed=false;var coords=cursorCoords(cm,pos);var endCoords=!end||end==pos?coords:cursorCoords(cm,end);rect={left:Math.min(coords.left,endCoords.left),top:Math.min(coords.top,endCoords.top)-margin,right:Math.max(coords.left,endCoords.left),bottom:Math.max(coords.bottom,endCoords.bottom)+margin};var scrollPos=calculateScrollPos(cm,rect);var startTop=cm.doc.scrollTop,startLeft=cm.doc.scrollLeft;if(scrollPos.scrollTop!=null){updateScrollTop(cm,scrollPos.scrollTop);if(Math.abs(cm.doc.scrollTop-startTop)>1){changed=true}}if(scrollPos.scrollLeft!=null){setScrollLeft(cm,scrollPos.scrollLeft);if(Math.abs(cm.doc.scrollLeft-startLeft)>1){changed=true}}if(!changed){break}}return rect}function scrollIntoView(cm,rect){var scrollPos=calculateScrollPos(cm,rect);if(scrollPos.scrollTop!=null){updateScrollTop(cm,scrollPos.scrollTop)}if(scrollPos.scrollLeft!=null){setScrollLeft(cm,scrollPos.scrollLeft)}}function calculateScrollPos(cm,rect){var display=cm.display,snapMargin=textHeight(cm.display);if(rect.top<0){rect.top=0}var screentop=cm.curOp&&cm.curOp.scrollTop!=null?cm.curOp.scrollTop:display.scroller.scrollTop;var screen=displayHeight(cm),result={};if(rect.bottom-rect.top>screen){rect.bottom=rect.top+screen}var docBottom=cm.doc.height+paddingVert(display);var atTop=rect.top<snapMargin,atBottom=rect.bottom>docBottom-snapMargin;if(rect.top<screentop){result.scrollTop=atTop?0:rect.top}else if(rect.bottom>screentop+screen){var newTop=Math.min(rect.top,(atBottom?docBottom:rect.bottom)-screen);if(newTop!=screentop){result.scrollTop=newTop}}var gutterSpace=cm.options.fixedGutter?0:display.gutters.offsetWidth;var screenleft=cm.curOp&&cm.curOp.scrollLeft!=null?cm.curOp.scrollLeft:display.scroller.scrollLeft-gutterSpace;var screenw=displayWidth(cm)-display.gutters.offsetWidth;var tooWide=rect.right-rect.left>screenw;if(tooWide){rect.right=rect.left+screenw}if(rect.left<10){result.scrollLeft=0}else if(rect.left<screenleft){result.scrollLeft=Math.max(0,rect.left+gutterSpace-(tooWide?0:10))}else if(rect.right>screenw+screenleft-3){result.scrollLeft=rect.right+(tooWide?0:10)-screenw}return result}function addToScrollTop(cm,top){if(top==null){return}resolveScrollToPos(cm);cm.curOp.scrollTop=(cm.curOp.scrollTop==null?cm.doc.scrollTop:cm.curOp.scrollTop)+top}function ensureCursorVisible(cm){resolveScrollToPos(cm);var cur=cm.getCursor();cm.curOp.scrollToPos={from:cur,to:cur,margin:cm.options.cursorScrollMargin}}function scrollToCoords(cm,x,y){if(x!=null||y!=null){resolveScrollToPos(cm)}if(x!=null){cm.curOp.scrollLeft=x}if(y!=null){cm.curOp.scrollTop=y}}function scrollToRange(cm,range){resolveScrollToPos(cm);cm.curOp.scrollToPos=range}function resolveScrollToPos(cm){var range=cm.curOp.scrollToPos;if(range){cm.curOp.scrollToPos=null;var from=estimateCoords(cm,range.from),to=estimateCoords(cm,range.to);scrollToCoordsRange(cm,from,to,range.margin)}}function scrollToCoordsRange(cm,from,to,margin){var sPos=calculateScrollPos(cm,{left:Math.min(from.left,to.left),top:Math.min(from.top,to.top)-margin,right:Math.max(from.right,to.right),bottom:Math.max(from.bottom,to.bottom)+margin});scrollToCoords(cm,sPos.scrollLeft,sPos.scrollTop)}function updateScrollTop(cm,val){if(Math.abs(cm.doc.scrollTop-val)<2){return}if(!gecko){updateDisplaySimple(cm,{top:val})}setScrollTop(cm,val,true);if(gecko){updateDisplaySimple(cm)}startWorker(cm,100)}function setScrollTop(cm,val,forceScroll){val=Math.max(0,Math.min(cm.display.scroller.scrollHeight-cm.display.scroller.clientHeight,val));if(cm.display.scroller.scrollTop==val&&!forceScroll){return}cm.doc.scrollTop=val;cm.display.scrollbars.setScrollTop(val);if(cm.display.scroller.scrollTop!=val){cm.display.scroller.scrollTop=val}}function setScrollLeft(cm,val,isScroller,forceScroll){val=Math.max(0,Math.min(val,cm.display.scroller.scrollWidth-cm.display.scroller.clientWidth));if((isScroller?val==cm.doc.scrollLeft:Math.abs(cm.doc.scrollLeft-val)<2)&&!forceScroll){return}cm.doc.scrollLeft=val;alignHorizontally(cm);if(cm.display.scroller.scrollLeft!=val){cm.display.scroller.scrollLeft=val}cm.display.scrollbars.setScrollLeft(val)}function measureForScrollbars(cm){var d=cm.display,gutterW=d.gutters.offsetWidth;var docH=Math.round(cm.doc.height+paddingVert(cm.display));return{clientHeight:d.scroller.clientHeight,viewHeight:d.wrapper.clientHeight,scrollWidth:d.scroller.scrollWidth,clientWidth:d.scroller.clientWidth,viewWidth:d.wrapper.clientWidth,barLeft:cm.options.fixedGutter?gutterW:0,docHeight:docH,scrollHeight:docH+scrollGap(cm)+d.barHeight,nativeBarWidth:d.nativeBarWidth,gutterWidth:gutterW}}var NativeScrollbars=function(place,scroll,cm){this.cm=cm;var vert=this.vert=elt("div",[elt("div",null,null,"min-width: 1px")],"CodeMirror-vscrollbar");var horiz=this.horiz=elt("div",[elt("div",null,null,"height: 100%; min-height: 1px")],"CodeMirror-hscrollbar");vert.tabIndex=horiz.tabIndex=-1;place(vert);place(horiz);on(vert,"scroll",(function(){if(vert.clientHeight){scroll(vert.scrollTop,"vertical")}}));on(horiz,"scroll",(function(){if(horiz.clientWidth){scroll(horiz.scrollLeft,"horizontal")}}));this.checkedZeroWidth=false;if(ie&&ie_version<8){this.horiz.style.minHeight=this.vert.style.minWidth="18px"}};NativeScrollbars.prototype.update=function(measure){var needsH=measure.scrollWidth>measure.clientWidth+1;var needsV=measure.scrollHeight>measure.clientHeight+1;var sWidth=measure.nativeBarWidth;if(needsV){this.vert.style.display="block";this.vert.style.bottom=needsH?sWidth+"px":"0";var totalHeight=measure.viewHeight-(needsH?sWidth:0);this.vert.firstChild.style.height=Math.max(0,measure.scrollHeight-measure.clientHeight+totalHeight)+"px"}else{this.vert.scrollTop=0;this.vert.style.display="";this.vert.firstChild.style.height="0"}if(needsH){this.horiz.style.display="block";this.horiz.style.right=needsV?sWidth+"px":"0";this.horiz.style.left=measure.barLeft+"px";var totalWidth=measure.viewWidth-measure.barLeft-(needsV?sWidth:0);this.horiz.firstChild.style.width=Math.max(0,measure.scrollWidth-measure.clientWidth+totalWidth)+"px"}else{this.horiz.style.display="";this.horiz.firstChild.style.width="0"}if(!this.checkedZeroWidth&&measure.clientHeight>0){if(sWidth==0){this.zeroWidthHack()}this.checkedZeroWidth=true}return{right:needsV?sWidth:0,bottom:needsH?sWidth:0}};NativeScrollbars.prototype.setScrollLeft=function(pos){if(this.horiz.scrollLeft!=pos){this.horiz.scrollLeft=pos}if(this.disableHoriz){this.enableZeroWidthBar(this.horiz,this.disableHoriz,"horiz")}};NativeScrollbars.prototype.setScrollTop=function(pos){if(this.vert.scrollTop!=pos){this.vert.scrollTop=pos}if(this.disableVert){this.enableZeroWidthBar(this.vert,this.disableVert,"vert")}};NativeScrollbars.prototype.zeroWidthHack=function(){var w=mac&&!mac_geMountainLion?"12px":"18px";this.horiz.style.height=this.vert.style.width=w;this.horiz.style.visibility=this.vert.style.visibility="hidden";this.disableHoriz=new Delayed;this.disableVert=new Delayed};NativeScrollbars.prototype.enableZeroWidthBar=function(bar,delay,type){bar.style.visibility="";function maybeDisable(){var box=bar.getBoundingClientRect();var elt=type=="vert"?document.elementFromPoint(box.right-1,(box.top+box.bottom)/2):document.elementFromPoint((box.right+box.left)/2,box.bottom-1);if(elt!=bar){bar.style.visibility="hidden"}else{delay.set(1e3,maybeDisable)}}delay.set(1e3,maybeDisable)};NativeScrollbars.prototype.clear=function(){var parent=this.horiz.parentNode;parent.removeChild(this.horiz);parent.removeChild(this.vert)};var NullScrollbars=function(){};NullScrollbars.prototype.update=function(){return{bottom:0,right:0}};NullScrollbars.prototype.setScrollLeft=function(){};NullScrollbars.prototype.setScrollTop=function(){};NullScrollbars.prototype.clear=function(){};function updateScrollbars(cm,measure){if(!measure){measure=measureForScrollbars(cm)}var startWidth=cm.display.barWidth,startHeight=cm.display.barHeight;updateScrollbarsInner(cm,measure);for(var i=0;i<4&&startWidth!=cm.display.barWidth||startHeight!=cm.display.barHeight;i++){if(startWidth!=cm.display.barWidth&&cm.options.lineWrapping){updateHeightsInViewport(cm)}updateScrollbarsInner(cm,measureForScrollbars(cm));startWidth=cm.display.barWidth;startHeight=cm.display.barHeight}}function updateScrollbarsInner(cm,measure){var d=cm.display;var sizes=d.scrollbars.update(measure);d.sizer.style.paddingRight=(d.barWidth=sizes.right)+"px";d.sizer.style.paddingBottom=(d.barHeight=sizes.bottom)+"px";d.heightForcer.style.borderBottom=sizes.bottom+"px solid transparent";if(sizes.right&&sizes.bottom){d.scrollbarFiller.style.display="block";d.scrollbarFiller.style.height=sizes.bottom+"px";d.scrollbarFiller.style.width=sizes.right+"px"}else{d.scrollbarFiller.style.display=""}if(sizes.bottom&&cm.options.coverGutterNextToScrollbar&&cm.options.fixedGutter){d.gutterFiller.style.display="block";d.gutterFiller.style.height=sizes.bottom+"px";d.gutterFiller.style.width=measure.gutterWidth+"px"}else{d.gutterFiller.style.display=""}}var scrollbarModel={native:NativeScrollbars,null:NullScrollbars};function initScrollbars(cm){if(cm.display.scrollbars){cm.display.scrollbars.clear();if(cm.display.scrollbars.addClass){rmClass(cm.display.wrapper,cm.display.scrollbars.addClass)}}cm.display.scrollbars=new scrollbarModel[cm.options.scrollbarStyle]((function(node){cm.display.wrapper.insertBefore(node,cm.display.scrollbarFiller);on(node,"mousedown",(function(){if(cm.state.focused){setTimeout((function(){return cm.display.input.focus()}),0)}}));node.setAttribute("cm-not-content","true")}),(function(pos,axis){if(axis=="horizontal"){setScrollLeft(cm,pos)}else{updateScrollTop(cm,pos)}}),cm);if(cm.display.scrollbars.addClass){addClass(cm.display.wrapper,cm.display.scrollbars.addClass)}}var nextOpId=0;function startOperation(cm){cm.curOp={cm:cm,viewChanged:false,startHeight:cm.doc.height,forceUpdate:false,updateInput:0,typing:false,changeObjs:null,cursorActivityHandlers:null,cursorActivityCalled:0,selectionChanged:false,updateMaxLine:false,scrollLeft:null,scrollTop:null,scrollToPos:null,focus:false,id:++nextOpId,markArrays:null};pushOperation(cm.curOp)}function endOperation(cm){var op=cm.curOp;if(op){finishOperation(op,(function(group){for(var i=0;i<group.ops.length;i++){group.ops[i].cm.curOp=null}endOperations(group)}))}}function endOperations(group){var ops=group.ops;for(var i=0;i<ops.length;i++){endOperation_R1(ops[i])}for(var i$1=0;i$1<ops.length;i$1++){endOperation_W1(ops[i$1])}for(var i$2=0;i$2<ops.length;i$2++){endOperation_R2(ops[i$2])}for(var i$3=0;i$3<ops.length;i$3++){endOperation_W2(ops[i$3])}for(var i$4=0;i$4<ops.length;i$4++){endOperation_finish(ops[i$4])}}function endOperation_R1(op){var cm=op.cm,display=cm.display;maybeClipScrollbars(cm);if(op.updateMaxLine){findMaxLine(cm)}op.mustUpdate=op.viewChanged||op.forceUpdate||op.scrollTop!=null||op.scrollToPos&&(op.scrollToPos.from.line<display.viewFrom||op.scrollToPos.to.line>=display.viewTo)||display.maxLineChanged&&cm.options.lineWrapping;op.update=op.mustUpdate&&new DisplayUpdate(cm,op.mustUpdate&&{top:op.scrollTop,ensure:op.scrollToPos},op.forceUpdate)}function endOperation_W1(op){op.updatedDisplay=op.mustUpdate&&updateDisplayIfNeeded(op.cm,op.update)}function endOperation_R2(op){var cm=op.cm,display=cm.display;if(op.updatedDisplay){updateHeightsInViewport(cm)}op.barMeasure=measureForScrollbars(cm);if(display.maxLineChanged&&!cm.options.lineWrapping){op.adjustWidthTo=measureChar(cm,display.maxLine,display.maxLine.text.length).left+3;cm.display.sizerWidth=op.adjustWidthTo;op.barMeasure.scrollWidth=Math.max(display.scroller.clientWidth,display.sizer.offsetLeft+op.adjustWidthTo+scrollGap(cm)+cm.display.barWidth);op.maxScrollLeft=Math.max(0,display.sizer.offsetLeft+op.adjustWidthTo-displayWidth(cm))}if(op.updatedDisplay||op.selectionChanged){op.preparedSelection=display.input.prepareSelection()}}function endOperation_W2(op){var cm=op.cm;if(op.adjustWidthTo!=null){cm.display.sizer.style.minWidth=op.adjustWidthTo+"px";if(op.maxScrollLeft<cm.doc.scrollLeft){setScrollLeft(cm,Math.min(cm.display.scroller.scrollLeft,op.maxScrollLeft),true)}cm.display.maxLineChanged=false}var takeFocus=op.focus&&op.focus==activeElt(doc(cm));if(op.preparedSelection){cm.display.input.showSelection(op.preparedSelection,takeFocus)}if(op.updatedDisplay||op.startHeight!=cm.doc.height){updateScrollbars(cm,op.barMeasure)}if(op.updatedDisplay){setDocumentHeight(cm,op.barMeasure)}if(op.selectionChanged){restartBlink(cm)}if(cm.state.focused&&op.updateInput){cm.display.input.reset(op.typing)}if(takeFocus){ensureFocus(op.cm)}}function endOperation_finish(op){var cm=op.cm,display=cm.display,doc=cm.doc;if(op.updatedDisplay){postUpdateDisplay(cm,op.update)}if(display.wheelStartX!=null&&(op.scrollTop!=null||op.scrollLeft!=null||op.scrollToPos)){display.wheelStartX=display.wheelStartY=null}if(op.scrollTop!=null){setScrollTop(cm,op.scrollTop,op.forceScroll)}if(op.scrollLeft!=null){setScrollLeft(cm,op.scrollLeft,true,true)}if(op.scrollToPos){var rect=scrollPosIntoView(cm,clipPos(doc,op.scrollToPos.from),clipPos(doc,op.scrollToPos.to),op.scrollToPos.margin);maybeScrollWindow(cm,rect)}var hidden=op.maybeHiddenMarkers,unhidden=op.maybeUnhiddenMarkers;if(hidden){for(var i=0;i<hidden.length;++i){if(!hidden[i].lines.length){signal(hidden[i],"hide")}}}if(unhidden){for(var i$1=0;i$1<unhidden.length;++i$1){if(unhidden[i$1].lines.length){signal(unhidden[i$1],"unhide")}}}if(display.wrapper.offsetHeight){doc.scrollTop=cm.display.scroller.scrollTop}if(op.changeObjs){signal(cm,"changes",cm,op.changeObjs)}if(op.update){op.update.finish()}}function runInOp(cm,f){if(cm.curOp){return f()}startOperation(cm);try{return f()}finally{endOperation(cm)}}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)}}}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)}}}function startWorker(cm,time){if(cm.doc.highlightFrontier<cm.display.viewTo){cm.state.highlight.set(time,bind(highlightWorker,cm))}}function highlightWorker(cm){var doc=cm.doc;if(doc.highlightFrontier>=cm.display.viewTo){return}var end=+new Date+cm.options.workTime;var context=getContextBefore(cm,doc.highlightFrontier);var changedLines=[];doc.iter(context.line,Math.min(doc.first+doc.size,cm.display.viewTo+500),(function(line){if(context.line>=cm.display.viewFrom){var oldStyles=line.styles;var resetState=line.text.length>cm.options.maxHighlightLength?copyState(doc.mode,context.state):null;var highlighted=highlightLine(cm,line,context,true);if(resetState){context.state=resetState}line.styles=highlighted.styles;var oldCls=line.styleClasses,newCls=highlighted.classes;if(newCls){line.styleClasses=newCls}else if(oldCls){line.styleClasses=null}var ischange=!oldStyles||oldStyles.length!=line.styles.length||oldCls!=newCls&&(!oldCls||!newCls||oldCls.bgClass!=newCls.bgClass||oldCls.textClass!=newCls.textClass);for(var i=0;!ischange&&i<oldStyles.length;++i){ischange=oldStyles[i]!=line.styles[i]}if(ischange){changedLines.push(context.line)}line.stateAfter=context.save();context.nextLine()}else{if(line.text.length<=cm.options.maxHighlightLength){processLine(cm,line.text,context)}line.stateAfter=context.line%5==0?context.save():null;context.nextLine()}if(+new Date>end){startWorker(cm,cm.options.workDelay);return true}}));doc.highlightFrontier=context.line;doc.modeFrontier=Math.max(doc.modeFrontier,context.line);if(changedLines.length){runInOp(cm,(function(){for(var i=0;i<changedLines.length;i++){regLineChange(cm,changedLines[i],"text")}}))}}var DisplayUpdate=function(cm,viewport,force){var display=cm.display;this.viewport=viewport;this.visible=visibleLines(display,cm.doc,viewport);this.editorIsHidden=!display.wrapper.offsetWidth;this.wrapperHeight=display.wrapper.clientHeight;this.wrapperWidth=display.wrapper.clientWidth;this.oldDisplayWidth=displayWidth(cm);this.force=force;this.dims=getDimensions(cm);this.events=[]};DisplayUpdate.prototype.signal=function(emitter,type){if(hasHandler(emitter,type)){this.events.push(arguments)}};DisplayUpdate.prototype.finish=function(){for(var i=0;i<this.events.length;i++){signal.apply(null,this.events[i])}};function maybeClipScrollbars(cm){var display=cm.display;if(!display.scrollbarsClipped&&display.scroller.offsetWidth){display.nativeBarWidth=display.scroller.offsetWidth-display.scroller.clientWidth;display.heightForcer.style.height=scrollGap(cm)+"px";display.sizer.style.marginBottom=-display.nativeBarWidth+"px";display.sizer.style.borderRightWidth=scrollGap(cm)+"px";display.scrollbarsClipped=true}}function selectionSnapshot(cm){if(cm.hasFocus()){return null}var active=activeElt(doc(cm));if(!active||!contains(cm.display.lineDiv,active)){return null}var result={activeElt:active};if(window.getSelection){var sel=win(cm).getSelection();if(sel.anchorNode&&sel.extend&&contains(cm.display.lineDiv,sel.anchorNode)){result.anchorNode=sel.anchorNode;result.anchorOffset=sel.anchorOffset;result.focusNode=sel.focusNode;result.focusOffset=sel.focusOffset}}return result}function restoreSelection(snapshot){if(!snapshot||!snapshot.activeElt||snapshot.activeElt==activeElt(snapshot.activeElt.ownerDocument)){return}snapshot.activeElt.focus();if(!/^(INPUT|TEXTAREA)$/.test(snapshot.activeElt.nodeName)&&snapshot.anchorNode&&contains(document.body,snapshot.anchorNode)&&contains(document.body,snapshot.focusNode)){var doc=snapshot.activeElt.ownerDocument;var sel=doc.defaultView.getSelection(),range=doc.createRange();range.setEnd(snapshot.anchorNode,snapshot.anchorOffset);range.collapse(false);sel.removeAllRanges();sel.addRange(range);sel.extend(snapshot.focusNode,snapshot.focusOffset)}}function updateDisplayIfNeeded(cm,update){var display=cm.display,doc=cm.doc;if(update.editorIsHidden){resetView(cm);return false}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)}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));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}var selSnapshot=selectionSnapshot(cm);if(toUpdate>4){display.lineDiv.style.display="none"}patchDisplay(cm,display.updateLineNumbers,update.dims);if(toUpdate>4){display.lineDiv.style.display=""}display.renderedView=display.view;restoreSelection(selSnapshot);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)){if(viewport&&viewport.top!=null){viewport={top:Math.min(cm.doc.height+paddingVert(cm.display)-displayHeight(cm),viewport.top)}}update.visible=visibleLines(cm.display,cm.doc,viewport);if(update.visible.from>=cm.display.viewFrom&&update.visible.to<=cm.display.viewTo){break}}else if(first){update.visible=visibleLines(cm.display,cm.doc,viewport)}if(!updateDisplayIfNeeded(cm,update)){break}updateHeightsInViewport(cm);var barMeasure=measureForScrollbars(cm);updateSelection(cm);updateScrollbars(cm,barMeasure);setDocumentHeight(cm,barMeasure);update.force=false}update.signal(cm,"update",cm);if(cm.display.viewFrom!=cm.display.reportedViewFrom||cm.display.viewTo!=cm.display.reportedViewTo){update.signal(cm,"viewportChange",cm,cm.display.viewFrom,cm.display.viewTo);cm.display.reportedViewFrom=cm.display.viewFrom;cm.display.reportedViewTo=cm.display.viewTo}}function updateDisplaySimple(cm,viewport){var update=new DisplayUpdate(cm,viewport);if(updateDisplayIfNeeded(cm,update)){updateHeightsInViewport(cm);postUpdateDisplay(cm,update);var barMeasure=measureForScrollbars(cm);updateSelection(cm);updateScrollbars(cm,barMeasure);setDocumentHeight(cm,barMeasure);update.finish()}}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;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;for(var i=0;i<view.length;i++){var lineView=view[i];if(lineView.hidden);else if(!lineView.node||lineView.node.parentNode!=container){var node=buildLineElement(cm,lineView,lineN,dims);container.insertBefore(node,cur)}else{while(cur!=lineView.node){cur=rm(cur)}var updateNumber=lineNumbers&&updateNumbersFrom!=null&&updateNumbersFrom<=lineN&&lineView.lineNumber;if(lineView.changes){if(indexOf(lineView.changes,"gutter")>-1){updateNumber=false}updateLineForChanges(cm,lineView,lineN,dims)}if(updateNumber){removeChildren(lineView.lineNumber);lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options,lineN)))}cur=lineView.node.nextSibling}lineN+=lineView.size}while(cur){cur=rm(cur)}}function updateGutterSpace(display){var width=display.gutters.offsetWidth;display.sizer.style.marginLeft=width+"px";signalLater(display,"gutterChanged",display)}function setDocumentHeight(cm,measure){cm.display.sizer.style.minHeight=measure.docHeight+"px";cm.display.heightForcer.style.top=measure.docHeight+"px";cm.display.gutters.style.height=measure.docHeight+cm.display.barHeight+scrollGap(cm)+"px"}function alignHorizontally(cm){var display=cm.display,view=display.view;if(!display.alignWidgets&&(!display.gutters.firstChild||!cm.options.fixedGutter)){return}var comp=compensateForHScroll(display)-display.scroller.scrollLeft+cm.doc.scrollLeft;var gutterW=display.gutters.offsetWidth,left=comp+"px";for(var i=0;i<view.length;i++){if(!view[i].hidden){if(cm.options.fixedGutter){if(view[i].gutter){view[i].gutter.style.left=left}if(view[i].gutterBackground){view[i].gutterBackground.style.left=left}}var align=view[i].alignable;if(align){for(var j=0;j<align.length;j++){align[j].style.left=left}}}}if(cm.options.fixedGutter){display.gutters.style.left=comp+gutterW+"px"}}function maybeUpdateLineNumberWidth(cm){if(!cm.options.lineNumbers){return false}var doc=cm.doc,last=lineNumberFor(cm.options,doc.first+doc.size-1),display=cm.display;if(last.length!=display.lineNumChars){var test=display.measure.appendChild(elt("div",[elt("div",last)],"CodeMirror-linenumber CodeMirror-gutter-elt"));var innerW=test.firstChild.offsetWidth,padding=test.offsetWidth-innerW;display.lineGutter.style.width="";display.lineNumInnerWidth=Math.max(innerW,display.lineGutter.offsetWidth-padding)+1;display.lineNumWidth=display.lineNumInnerWidth+padding;display.lineNumChars=display.lineNumInnerWidth?last.length:-1;display.lineGutter.style.width=display.lineNumWidth+"px";updateGutterSpace(cm.display);return true}return false}function getGutters(gutters,lineNumbers){var result=[],sawLineNumbers=false;for(var i=0;i<gutters.length;i++){var name=gutters[i],style=null;if(typeof name!="string"){style=name.style;name=name.className}if(name=="CodeMirror-linenumbers"){if(!lineNumbers){continue}else{sawLineNumbers=true}}result.push({className:name,style:style})}if(lineNumbers&&!sawLineNumbers){result.push({className:"CodeMirror-linenumbers",style:null})}return result}function renderGutters(display){var gutters=display.gutters,specs=display.gutterSpecs;removeChildren(gutters);display.lineGutter=null;for(var i=0;i<specs.length;++i){var ref=specs[i];var className=ref.className;var style=ref.style;var gElt=gutters.appendChild(elt("div",null,"CodeMirror-gutter "+className));if(style){gElt.style.cssText=style}if(className=="CodeMirror-linenumbers"){display.lineGutter=gElt;gElt.style.width=(display.lineNumWidth||1)+"px"}}gutters.style.display=specs.length?"":"none";updateGutterSpace(display)}function updateGutters(cm){renderGutters(cm.display);regChange(cm);alignHorizontally(cm)}function Display(place,doc,input,options){var d=this;this.input=input;d.scrollbarFiller=elt("div",null,"CodeMirror-scrollbar-filler");d.scrollbarFiller.setAttribute("cm-not-content","true");d.gutterFiller=elt("div",null,"CodeMirror-gutter-filler");d.gutterFiller.setAttribute("cm-not-content","true");d.lineDiv=eltP("div",null,"CodeMirror-code");d.selectionDiv=elt("div",null,null,"position: relative; z-index: 1");d.cursorDiv=elt("div",null,"CodeMirror-cursors");d.measure=elt("div",null,"CodeMirror-measure");d.lineMeasure=elt("div",null,"CodeMirror-measure");d.lineSpace=eltP("div",[d.measure,d.lineMeasure,d.selectionDiv,d.cursorDiv,d.lineDiv],null,"position: relative; outline: none");var lines=eltP("div",[d.lineSpace],"CodeMirror-lines");d.mover=elt("div",[lines],null,"position: relative");d.sizer=elt("div",[d.mover],"CodeMirror-sizer");d.sizerWidth=null;d.heightForcer=elt("div",null,null,"position: absolute; height: "+scrollerGap+"px; width: 1px;");d.gutters=elt("div",null,"CodeMirror-gutters");d.lineGutter=null;d.scroller=elt("div",[d.sizer,d.heightForcer,d.gutters],"CodeMirror-scroll");d.scroller.setAttribute("tabIndex","-1");d.wrapper=elt("div",[d.scrollbarFiller,d.gutterFiller,d.scroller],"CodeMirror");if(chrome&&chrome_version>=105){d.wrapper.style.clipPath="inset(0px)"}d.wrapper.setAttribute("translate","no");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)}}d.viewFrom=d.viewTo=doc.first;d.reportedViewFrom=d.reportedViewTo=doc.first;d.view=[];d.renderedView=null;d.externalMeasured=null;d.viewOffset=0;d.lastWrapHeight=d.lastWrapWidth=0;d.updateLineNumbers=null;d.nativeBarWidth=d.barHeight=d.barWidth=0;d.scrollbarsClipped=false;d.lineNumWidth=d.lineNumInnerWidth=d.lineNumChars=null;d.alignWidgets=false;d.cachedCharWidth=d.cachedTextHeight=d.cachedPaddingH=null;d.maxLine=null;d.maxLineLength=0;d.maxLineChanged=false;d.wheelDX=d.wheelDY=d.wheelStartX=d.wheelStartY=null;d.shift=false;d.selForContextMenu=null;d.activeTouch=null;d.gutterSpecs=getGutters(options.gutters,options.lineNumbers);renderGutters(d);input.init(d)}var wheelSamples=0,wheelPixelsPerUnit=null;if(ie){wheelPixelsPerUnit=-.53}else if(gecko){wheelPixelsPerUnit=15}else if(chrome){wheelPixelsPerUnit=-.7}else if(safari){wheelPixelsPerUnit=-1/3}function wheelEventDelta(e){var dx=e.wheelDeltaX,dy=e.wheelDeltaY;if(dx==null&&e.detail&&e.axis==e.HORIZONTAL_AXIS){dx=e.detail}if(dy==null&&e.detail&&e.axis==e.VERTICAL_AXIS){dy=e.detail}else if(dy==null){dy=e.wheelDelta}return{x:dx,y:dy}}function wheelEventPixels(e){var delta=wheelEventDelta(e);delta.x*=wheelPixelsPerUnit;delta.y*=wheelPixelsPerUnit;return delta}function onScrollWheel(cm,e){if(chrome&&chrome_version==102){if(cm.display.chromeScrollHack==null){cm.display.sizer.style.pointerEvents="none"}else{clearTimeout(cm.display.chromeScrollHack)}cm.display.chromeScrollHack=setTimeout((function(){cm.display.chromeScrollHack=null;cm.display.sizer.style.pointerEvents=""}),100)}var delta=wheelEventDelta(e),dx=delta.x,dy=delta.y;var pixelsPerUnit=wheelPixelsPerUnit;if(e.deltaMode===0){dx=e.deltaX;dy=e.deltaY;pixelsPerUnit=1}var display=cm.display,scroll=display.scroller;var canScrollX=scroll.scrollWidth>scroll.clientWidth;var canScrollY=scroll.scrollHeight>scroll.clientHeight;if(!(dx&&canScrollX||dy&&canScrollY)){return}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}}}}if(dx&&!gecko&&!presto&&pixelsPerUnit!=null){if(dy&&canScrollY){updateScrollTop(cm,Math.max(0,scroll.scrollTop+dy*pixelsPerUnit))}setScrollLeft(cm,Math.max(0,scroll.scrollLeft+dx*pixelsPerUnit));if(!dy||dy&&canScrollY){e_preventDefault(e)}display.wheelStartX=null;return}if(dy&&pixelsPerUnit!=null){var pixels=dy*pixelsPerUnit;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&&e.deltaMode!==0){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}}}var Selection=function(ranges,primIndex){this.ranges=ranges;this.primIndex=primIndex};Selection.prototype.primary=function(){return this.ranges[this.primIndex]};Selection.prototype.equals=function(other){if(other==this){return true}if(other.primIndex!=this.primIndex||other.ranges.length!=this.ranges.length){return false}for(var i=0;i<this.ranges.length;i++){var here=this.ranges[i],there=other.ranges[i];if(!equalCursorPos(here.anchor,there.anchor)||!equalCursorPos(here.head,there.head)){return false}}return true};Selection.prototype.deepCopy=function(){var out=[];for(var i=0;i<this.ranges.length;i++){out[i]=new Range(copyPos(this.ranges[i].anchor),copyPos(this.ranges[i].head))}return new Selection(out,this.primIndex)};Selection.prototype.somethingSelected=function(){for(var i=0;i<this.ranges.length;i++){if(!this.ranges[i].empty()){return true}}return false};Selection.prototype.contains=function(pos,end){if(!end){end=pos}for(var i=0;i<this.ranges.length;i++){var range=this.ranges[i];if(cmp(end,range.from())>=0&&cmp(pos,range.to())<=0){return i}}return-1};var Range=function(anchor,head){this.anchor=anchor;this.head=head};Range.prototype.from=function(){return minPos(this.anchor,this.head)};Range.prototype.to=function(){return maxPos(this.anchor,this.head)};Range.prototype.empty=function(){return this.head.line==this.anchor.line&&this.head.ch==this.anchor.ch};function normalizeSelection(cm,ranges,primIndex){var mayTouch=cm&&cm.options.selectionsMayTouch;var prim=ranges[primIndex];ranges.sort((function(a,b){return cmp(a.from(),b.from())}));primIndex=indexOf(ranges,prim);for(var i=1;i<ranges.length;i++){var cur=ranges[i],prev=ranges[i-1];var diff=cmp(prev.to(),cur.from());if(mayTouch&&!cur.empty()?diff>0:diff>=0){var from=minPos(prev.from(),cur.from()),to=maxPos(prev.to(),cur.to());var inv=prev.empty()?cur.from()==cur.head:prev.from()==prev.head;if(i<=primIndex){--primIndex}ranges.splice(--i,2,new Range(inv?to:from,inv?from:to))}}return new Selection(ranges,primIndex)}function simpleSelection(anchor,head){return new Selection([new Range(anchor,head||anchor)],0)}function changeEnd(change){if(!change.text){return change.to}return Pos(change.from.line+change.text.length-1,lst(change.text).length+(change.text.length==1?change.from.ch:0))}function adjustForChange(pos,change){if(cmp(pos,change.from)<0){return pos}if(cmp(pos,change.to)<=0){return changeEnd(change)}var line=pos.line+change.text.length-(change.to.line-change.from.line)-1,ch=pos.ch;if(pos.line==change.to.line){ch+=changeEnd(change).ch-change.to.ch}return Pos(line,ch)}function computeSelAfterChange(doc,change){var out=[];for(var i=0;i<doc.sel.ranges.length;i++){var range=doc.sel.ranges[i];out.push(new Range(adjustForChange(range.anchor,change),adjustForChange(range.head,change)))}return normalizeSelection(doc.cm,out,doc.sel.primIndex)}function offsetPos(pos,old,nw){if(pos.line==old.line){return Pos(nw.line,pos.ch-old.ch+nw.ch)}else{return Pos(nw.line+(pos.line-old.line),pos.ch)}}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)}function loadMode(cm){cm.doc.mode=getMode(cm.options,cm.doc.modeOption);resetModeState(cm)}function resetModeState(cm){cm.doc.iter((function(line){if(line.stateAfter){line.stateAfter=null}if(line.styles){line.styles=null}}));cm.doc.modeFrontier=cm.doc.highlightFrontier=cm.doc.first;startWorker(cm,100);cm.state.modeGen++;if(cm.curOp){regChange(cm)}}function isWholeLineUpdate(doc,change){return change.from.ch==0&&change.to.ch==0&&lst(change.text)==""&&(!doc.cm||doc.cm.options.wholeLineUpdateBefore)}function updateDoc(doc,change,markedSpans,estimateHeight){function spansFor(n){return markedSpans?markedSpans[n]:null}function update(line,text,spans){updateLine(line,text,spans,estimateHeight);signalLater(line,"change",line,change)}function linesFor(start,end){var result=[];for(var i=start;i<end;++i){result.push(new Line(text[i],spansFor(i),estimateHeight))}return result}var from=change.from,to=change.to,text=change.text;var firstLine=getLine(doc,from.line),lastLine=getLine(doc,to.line);var lastText=lst(text),lastSpans=spansFor(text.length-1),nlines=to.line-from.line;if(change.full){doc.insert(0,linesFor(0,text.length));doc.remove(text.length,doc.size-text.length)}else if(isWholeLineUpdate(doc,change)){var added=linesFor(0,text.length-1);update(lastLine,lastLine.text,lastSpans);if(nlines){doc.remove(from.line,nlines)}if(added.length){doc.insert(from.line,added)}}else if(firstLine==lastLine){if(text.length==1){update(firstLine,firstLine.text.slice(0,from.ch)+lastText+firstLine.text.slice(to.ch),lastSpans)}else{var added$1=linesFor(1,text.length-1);added$1.push(new Line(lastText+firstLine.text.slice(to.ch),lastSpans,estimateHeight));update(firstLine,firstLine.text.slice(0,from.ch)+text[0],spansFor(0));doc.insert(from.line+1,added$1)}}else if(text.length==1){update(firstLine,firstLine.text.slice(0,from.ch)+text[0]+lastLine.text.slice(to.ch),spansFor(0));doc.remove(from.line+1,nlines)}else{update(firstLine,firstLine.text.slice(0,from.ch)+text[0],spansFor(0));update(lastLine,lastText+lastLine.text.slice(to.ch),lastSpans);var added$2=linesFor(1,text.length-1);if(nlines>1){doc.remove(from.line+1,nlines-1)}doc.insert(from.line+1,added$2)}signalLater(doc,"change",doc,change)}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)}function attachDoc(cm,doc){if(doc.cm){throw new Error("This document is already in use.")}cm.doc=doc;doc.cm=cm;estimateLineHeights(cm);loadMode(cm);setDirectionClass(cm);cm.options.direction=doc.direction;if(!cm.options.lineWrapping){findMaxLine(cm)}cm.options.mode=doc.modeOption;regChange(cm)}function setDirectionClass(cm){(cm.doc.direction=="rtl"?addClass:rmClass)(cm.display.lineDiv,"CodeMirror-rtl")}function directionChanged(cm){runInOp(cm,(function(){setDirectionClass(cm);regChange(cm)}))}function History(prev){this.done=[];this.undone=[];this.undoDepth=prev?prev.undoDepth:Infinity;this.lastModTime=this.lastSelTime=0;this.lastOp=this.lastSelOp=null;this.lastOrigin=this.lastSelOrigin=null;this.generation=this.maxGeneration=prev?prev.maxGeneration:1}function historyChangeFromChange(doc,change){var histChange={from:copyPos(change.from),to:changeEnd(change),text:getBetween(doc,change.from,change.to)};attachLocalSpans(doc,histChange,change.from.line,change.to.line+1);linkedDocs(doc,(function(doc){return attachLocalSpans(doc,histChange,change.from.line,change.to.line+1)}),true);return histChange}function clearSelectionEvents(array){while(array.length){var last=lst(array);if(last.ranges){array.pop()}else{break}}}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)}}function addChangeToHistory(doc,change,selAfter,opId){var hist=doc.history;hist.undone.length=0;var time=+new Date,cur;var last;if((hist.lastOp==opId||hist.lastOrigin==change.origin&&change.origin&&(change.origin.charAt(0)=="+"&&hist.lastModTime>time-(doc.cm?doc.cm.options.historyEventDelay:500)||change.origin.charAt(0)=="*"))&&(cur=lastChangeEvent(hist,hist.lastOp==opId))){last=lst(cur.changes);if(cmp(change.from,change.to)==0&&cmp(change.from,last.to)==0){last.to=changeEnd(change)}else{cur.changes.push(historyChangeFromChange(doc,change))}}else{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)}function addSelectionToHistory(doc,sel,opId,options){var hist=doc.history,origin=options&&options.origin;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)}}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}))}function removeClearedSpans(spans){if(!spans){return null}var out;for(var i=0;i<spans.length;++i){if(spans[i].marker.explicitlyCleared){if(!out){out=spans.slice(0,i)}}else if(out){out.push(spans[i])}}return!out?spans:out.length?out:null}function getOldSpans(doc,change){var found=change["spans_"+doc.id];if(!found){return null}var nw=[];for(var i=0;i<change.text.length;++i){nw.push(removeClearedSpans(found[i]))}return nw}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}function copyHistoryArray(events,newGroup,instantiateSel){var copy=[];for(var i=0;i<events.length;++i){var event=events[i];if(event.ranges){copy.push(instantiateSel?Selection.prototype.deepCopy.call(event):event);continue}var changes=event.changes,newChanges=[];copy.push({changes:newChanges});for(var j=0;j<changes.length;++j){var change=changes[j],m=void 0;newChanges.push({from:change.from,to:change.to,text:change.text});if(newGroup){for(var prop in change){if(m=prop.match(/^spans_(\d+)$/)){if(indexOf(newGroup,Number(m[1]))>-1){lst(newChanges)[prop]=change[prop];delete change[prop]}}}}}}return copy}function extendRange(range,head,other,extend){if(extend){var anchor=range.anchor;if(other){var posBefore=cmp(head,anchor)<0;if(posBefore!=cmp(other,anchor)<0){anchor=head;head=other}else if(posBefore!=cmp(head,other)<0){head=other}}return new Range(anchor,head)}else{return new Range(other||head,head)}}function extendSelection(doc,head,other,options,extend){if(extend==null){extend=doc.cm&&(doc.cm.display.shift||doc.extend)}setSelection(doc,new Selection([extendRange(doc.sel.primary(),head,other,extend)],0),options)}function extendSelections(doc,heads,options){var out=[];var extend=doc.cm&&(doc.cm.display.shift||doc.extend);for(var i=0;i<doc.sel.ranges.length;i++){out[i]=extendRange(doc.sel.ranges[i],heads[i],null,extend)}var newSel=normalizeSelection(doc.cm,out,doc.sel.primIndex);setSelection(doc,newSel,options)}function replaceOneSelection(doc,i,range,options){var ranges=doc.sel.ranges.slice(0);ranges[i]=range;setSelection(doc,normalizeSelection(doc.cm,ranges,doc.sel.primIndex),options)}function setSimpleSelection(doc,anchor,head,options){setSelection(doc,simpleSelection(anchor,head),options)}function filterSelectionChange(doc,sel,options){var obj={ranges:sel.ranges,update:function(ranges){this.ranges=[];for(var i=0;i<ranges.length;i++){this.ranges[i]=new Range(clipPos(doc,ranges[i].anchor),clipPos(doc,ranges[i].head))}},origin:options&&options.origin};signal(doc,"beforeSelectionChange",doc,obj);if(doc.cm){signal(doc.cm,"beforeSelectionChange",doc.cm,obj)}if(obj.ranges!=sel.ranges){return normalizeSelection(doc.cm,obj.ranges,obj.ranges.length-1)}else{return sel}}function setSelectionReplaceHistory(doc,sel,options){var done=doc.history.done,last=lst(done);if(last&&last.ranges){done[done.length-1]=sel;setSelectionNoUndo(doc,sel,options)}else{setSelection(doc,sel,options)}}function setSelection(doc,sel,options){setSelectionNoUndo(doc,sel,options);addSelectionToHistory(doc,doc.sel,doc.cm?doc.cm.curOp.id:NaN,options)}function setSelectionNoUndo(doc,sel,options){if(hasHandler(doc,"beforeSelectionChange")||doc.cm&&hasHandler(doc.cm,"beforeSelectionChange")){sel=filterSelectionChange(doc,sel,options)}var bias=options&&options.bias||(cmp(sel.primary().head,doc.sel.primary().head)<0?-1:1);setSelectionInner(doc,skipAtomicInSelection(doc,sel,bias,true));if(!(options&&options.scroll===false)&&doc.cm&&doc.cm.getOption("readOnly")!="nocursor"){ensureCursorVisible(doc.cm)}}function setSelectionInner(doc,sel){if(sel.equals(doc.sel)){return}doc.sel=sel;if(doc.cm){doc.cm.curOp.updateInput=1;doc.cm.curOp.selectionChanged=true;signalCursorActivity(doc.cm)}signalLater(doc,"cursorActivity",doc)}function reCheckSelection(doc){setSelectionInner(doc,skipAtomicInSelection(doc,doc.sel,null,false))}function skipAtomicInSelection(doc,sel,bias,mayClear){var out;for(var i=0;i<sel.ranges.length;i++){var range=sel.ranges[i];var old=sel.ranges.length==doc.sel.ranges.length&&doc.sel.ranges[i];var newAnchor=skipAtomic(doc,range.anchor,old&&old.anchor,bias,mayClear);var newHead=range.head==range.anchor?newAnchor:skipAtomic(doc,range.head,old&&old.head,bias,mayClear);if(out||newAnchor!=range.anchor||newHead!=range.head){if(!out){out=sel.ranges.slice(0,i)}out[i]=new Range(newAnchor,newHead)}}return out?normalizeSelection(doc.cm,out,sel.primIndex):sel}function skipAtomicInner(doc,pos,oldPos,dir,mayClear){var line=getLine(doc,pos.line);if(line.markedSpans){for(var i=0;i<line.markedSpans.length;++i){var sp=line.markedSpans[i],m=sp.marker;var preventCursorLeft="selectLeft"in m?!m.selectLeft:m.inclusiveLeft;var preventCursorRight="selectRight"in m?!m.selectRight:m.inclusiveRight;if((sp.from==null||(preventCursorLeft?sp.from<=pos.ch:sp.from<pos.ch))&&(sp.to==null||(preventCursorRight?sp.to>=pos.ch:sp.to>pos.ch))){if(mayClear){signal(m,"beforeCursorEnter");if(m.explicitlyCleared){if(!line.markedSpans){break}else{--i;continue}}}if(!m.atomic){continue}if(oldPos){var near=m.find(dir<0?1:-1),diff=void 0;if(dir<0?preventCursorRight:preventCursorLeft){near=movePos(doc,near,-dir,near&&near.line==pos.line?line:null)}if(near&&near.line==pos.line&&(diff=cmp(near,oldPos))&&(dir<0?diff<0:diff>0)){return skipAtomicInner(doc,near,pos,dir,mayClear)}}var far=m.find(dir<0?-1:1);if(dir<0?preventCursorLeft:preventCursorRight){far=movePos(doc,far,dir,far.line==pos.line?line:null)}return far?skipAtomicInner(doc,far,pos,dir,mayClear):null}}}return pos}function skipAtomic(doc,pos,oldPos,bias,mayClear){var dir=bias||1;var found=skipAtomicInner(doc,pos,oldPos,dir,mayClear)||!mayClear&&skipAtomicInner(doc,pos,oldPos,dir,true)||skipAtomicInner(doc,pos,oldPos,-dir,mayClear)||!mayClear&&skipAtomicInner(doc,pos,oldPos,-dir,true);if(!found){doc.cantEdit=true;return Pos(doc.first,0)}return found}function movePos(doc,pos,dir,line){if(dir<0&&pos.ch==0){if(pos.line>doc.first){return clipPos(doc,Pos(pos.line-1))}else{return null}}else if(dir>0&&pos.ch==(line||getLine(doc,pos.line)).text.length){if(pos.line<doc.first+doc.size-1){return Pos(pos.line+1,0)}else{return null}}else{return new Pos(pos.line,pos.ch+dir)}}function selectAll(cm){cm.setSelection(Pos(cm.firstLine(),0),Pos(cm.lastLine()),sel_dontScroll)}function filterChange(doc,change,update){var obj={canceled:false,from:change.from,to:change.to,text:change.text,origin:change.origin,cancel:function(){return obj.canceled=true}};if(update){obj.update=function(from,to,text,origin){if(from){obj.from=clipPos(doc,from)}if(to){obj.to=clipPos(doc,to)}if(text){obj.text=text}if(origin!==undefined){obj.origin=origin}}}signal(doc,"beforeChange",doc,obj);if(doc.cm){signal(doc.cm,"beforeChange",doc.cm,obj)}if(obj.canceled){if(doc.cm){doc.cm.curOp.updateInput=2}return null}return{from:obj.from,to:obj.to,text:obj.text,origin:obj.origin}}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}}var split=sawReadOnlySpans&&!ignoreReadOnly&&removeReadOnlyRanges(doc,change.from,change.to);if(split){for(var i=split.length-1;i>=0;--i){makeChangeInner(doc,{from:split[i].from,to:split[i].to,text:i?[""]:change.text,origin:change.origin})}}else{makeChangeInner(doc,change)}}function makeChangeInner(doc,change){if(change.text.length==1&&change.text[0]==""&&cmp(change.from,change.to)==0){return}var selAfter=computeSelAfterChange(doc,change);addChangeToHistory(doc,change,selAfter,doc.cm?doc.cm.curOp.id:NaN);makeChangeSingleDoc(doc,change,selAfter,stretchSpansOverChange(doc,change));var rebased=[];linkedDocs(doc,(function(doc,sharedHist){if(!sharedHist&&indexOf(rebased,doc.history)==-1){rebaseHist(doc.history,change);rebased.push(doc.history)}makeChangeSingleDoc(doc,change,null,stretchSpansOverChange(doc,change))}))}function makeChangeFromHistory(doc,type,allowSelectionOnly){var suppress=doc.cm&&doc.cm.state.suppressEdits;if(suppress&&!allowSelectionOnly){return}var hist=doc.history,event,selAfter=doc.sel;var source=type=="undo"?hist.done:hist.undone,dest=type=="undo"?hist.undone:hist.done;var i=0;for(;i<source.length;i++){event=source[i];if(allowSelectionOnly?event.ranges&&!event.equals(doc.sel):!event.ranges){break}}if(i==source.length){return}hist.lastOrigin=hist.lastSelOrigin=null;for(;;){event=source.pop();if(event.ranges){pushSelectionToHistory(event,dest);if(allowSelectionOnly&&!event.equals(doc.sel)){setSelection(doc,event,{clearRedo:false});return}selAfter=event}else if(suppress){source.push(event);return}else{break}}var antiChanges=[];pushSelectionToHistory(selAfter,dest);dest.push({changes:antiChanges,generation:hist.generation});hist.generation=event.generation||++hist.maxGeneration;var filter=hasHandler(doc,"beforeChange")||doc.cm&&hasHandler(doc.cm,"beforeChange");var loop=function(i){var change=event.changes[i];change.origin=type;if(filter&&!filterChange(doc,change,false)){source.length=0;return{}}antiChanges.push(historyChangeFromChange(doc,change));var after=i?computeSelAfterChange(doc,change):lst(source);makeChangeSingleDoc(doc,change,after,mergeOldSpans(doc,change));if(!i&&doc.cm){doc.cm.scrollIntoView({from:change.from,to:changeEnd(change)})}var rebased=[];linkedDocs(doc,(function(doc,sharedHist){if(!sharedHist&&indexOf(rebased,doc.history)==-1){rebaseHist(doc.history,change);rebased.push(doc.history)}makeChangeSingleDoc(doc,change,null,mergeOldSpans(doc,change))}))};for(var i$1=event.changes.length-1;i$1>=0;--i$1){var returned=loop(i$1);if(returned)return returned.v}}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")}}}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}if(change.from.line<doc.first){var shift=change.text.length-1-(doc.first-change.from.line);shiftDoc(doc,shift);change={from:Pos(doc.first,0),to:Pos(change.to.line+shift,change.to.ch),text:[lst(change.text)],origin:change.origin}}var last=doc.lastLine();if(change.to.line>last){change={from:change.from,to:Pos(last,getLine(doc,last).text.length),text:[change.text[0]],origin:change.origin}}change.removed=getBetween(doc,change.from,change.to);if(!selAfter){selAfter=computeSelAfterChange(doc,change)}if(doc.cm){makeChangeSingleDocInEditor(doc.cm,change,spans)}else{updateDoc(doc,change,spans)}setSelectionNoUndo(doc,selAfter,sel_dontScroll);if(doc.cantEdit&&skipAtomic(doc,Pos(doc.firstLine(),0))){doc.cantEdit=false}}function makeChangeSingleDocInEditor(cm,change,spans){var doc=cm.doc,display=cm.display,from=change.from,to=change.to;var recomputeMaxLength=false,checkWidthStart=from.line;if(!cm.options.lineWrapping){checkWidthStart=lineNo(visualLine(getLine(doc,from.line)));doc.iter(checkWidthStart,to.line+1,(function(line){if(line==display.maxLine){recomputeMaxLength=true;return true}}))}if(doc.sel.contains(change.from,change.to)>-1){signalCursorActivity(cm)}updateDoc(doc,change,spans,estimateHeight(cm));if(!cm.options.lineWrapping){doc.iter(checkWidthStart,from.line+change.text.length,(function(line){var len=lineLength(line);if(len>display.maxLineLength){display.maxLine=line;display.maxLineLength=len;display.maxLineChanged=true;recomputeMaxLength=false}}));if(recomputeMaxLength){cm.curOp.updateMaxLine=true}}retreatFrontier(doc,from.line);startWorker(cm,400);var lendiff=change.text.length-(to.line-from.line)-1;if(change.full){regChange(cm)}else if(from.line==to.line&&change.text.length==1&&!isWholeLineUpdate(cm.doc,change)){regLineChange(cm,from.line,"text")}else{regChange(cm,from.line,to.line+1,lendiff)}var changesHandler=hasHandler(cm,"changes"),changeHandler=hasHandler(cm,"change");if(changeHandler||changesHandler){var obj={from:from,to:to,text:change.text,removed:change.removed,origin:change.origin};if(changeHandler){signalLater(cm,"change",cm,obj)}if(changesHandler){(cm.curOp.changeObjs||(cm.curOp.changeObjs=[])).push(obj)}}cm.display.selForContextMenu=null}function replaceRange(doc,code,from,to,origin){var assign;if(!to){to=from}if(cmp(to,from)<0){assign=[to,from],from=assign[0],to=assign[1]}if(typeof code=="string"){code=doc.splitLines(code)}makeChange(doc,{from:from,to:to,text:code,origin:origin})}function rebaseHistSelSingle(pos,from,to,diff){if(to<pos.line){pos.line+=diff}else if(from<pos.line){pos.line=from;pos.ch=0}}function rebaseHistArray(array,from,to,diff){for(var i=0;i<array.length;++i){var sub=array[i],ok=true;if(sub.ranges){if(!sub.copied){sub=array[i]=sub.deepCopy();sub.copied=true}for(var j=0;j<sub.ranges.length;j++){rebaseHistSelSingle(sub.ranges[j].anchor,from,to,diff);rebaseHistSelSingle(sub.ranges[j].head,from,to,diff)}continue}for(var j$1=0;j$1<sub.changes.length;++j$1){var cur=sub.changes[j$1];if(to<cur.from.line){cur.from=Pos(cur.from.line+diff,cur.from.ch);cur.to=Pos(cur.to.line+diff,cur.to.ch)}else if(from<=cur.to.line){ok=false;break}}if(!ok){array.splice(0,i+1);i=0}}}function rebaseHist(hist,change){var from=change.from.line,to=change.to.line,diff=change.text.length-(to-from)-1;rebaseHistArray(hist.done,from,to,diff);rebaseHistArray(hist.undone,from,to,diff)}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}function LeafChunk(lines){this.lines=lines;this.parent=null;var height=0;for(var i=0;i<lines.length;++i){lines[i].parent=this;height+=lines[i].height}this.height=height}LeafChunk.prototype={chunkSize:function(){return this.lines.length},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)},collapse:function(lines){lines.push.apply(lines,this.lines)},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}},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(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){var remaining=child.lines.length%25+25;for(var pos=remaining;pos<child.lines.length;){var leaf=new LeafChunk(child.lines.slice(pos,pos+=25));child.height-=leaf.height;this.children.splice(++i,0,leaf);leaf.parent=this}child.lines=child.lines.slice(0,remaining);this.maybeSpill()}break}at-=sz}},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){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 LineWidget=function(doc,node,options){if(options){for(var opt in options){if(options.hasOwnProperty(opt)){this[opt]=options[opt]}}}this.doc=doc;this.node=node};LineWidget.prototype.clear=function(){var cm=this.doc.cm,ws=this.line.widgets,line=this.line,no=lineNo(line);if(no==null||!ws){return}for(var i=0;i<ws.length;++i){if(ws[i]==this){ws.splice(i--,1)}}if(!ws.length){line.widgets=null}var height=widgetHeight(this);updateLineHeight(line,Math.max(0,line.height-height));if(cm){runInOp(cm,(function(){adjustScrollWhenAboveVisible(cm,line,-height);regLineChange(cm,no,"widget")}));signalLater(cm,"lineWidgetCleared",cm,this,no)}};LineWidget.prototype.changed=function(){var this$1=this;var oldH=this.height,cm=this.doc.cm,line=this.line;this.height=null;var diff=widgetHeight(this)-oldH;if(!diff){return}if(!lineIsHidden(this.doc,line)){updateLineHeight(line,line.height+diff)}if(cm){runInOp(cm,(function(){cm.curOp.forceUpdate=true;adjustScrollWhenAboveVisible(cm,line,diff);signalLater(cm,"lineWidgetChanged",cm,this$1,lineNo(line))}))}};eventMixin(LineWidget);function adjustScrollWhenAboveVisible(cm,line,diff){if(heightAtLine(line)<(cm.curOp&&cm.curOp.scrollTop||cm.doc.scrollTop)){addToScrollTop(cm,diff)}}function addLineWidget(doc,handle,node,options){var widget=new LineWidget(doc,node,options);var cm=doc.cm;if(cm&&widget.noHScroll){cm.display.alignWidgets=true}changeLine(doc,handle,"widget",(function(line){var widgets=line.widgets||(line.widgets=[]);if(widget.insertAt==null){widgets.push(widget)}else{widgets.splice(Math.min(widgets.length,Math.max(0,widget.insertAt)),0,widget)}widget.line=line;if(cm&&!lineIsHidden(doc,line)){var aboveVisible=heightAtLine(line)<doc.scrollTop;updateLineHeight(line,line.height+widgetHeight(widget));if(aboveVisible){addToScrollTop(cm,widget.height)}cm.curOp.forceUpdate=true}return true}));if(cm){signalLater(cm,"lineWidgetAdded",cm,widget,typeof handle=="number"?handle:lineNo(handle))}return widget}var nextMarkerId=0;var TextMarker=function(doc,type){this.lines=[];this.type=type;this.doc=doc;this.id=++nextMarkerId};TextMarker.prototype.clear=function(){if(this.explicitlyCleared){return}var cm=this.doc.cm,withOp=cm&&!cm.curOp;if(withOp){startOperation(cm)}if(hasHandler(this,"clear")){var found=this.find();if(found){signalLater(this,"clear",found.from,found.to)}}var min=null,max=null;for(var i=0;i<this.lines.length;++i){var line=this.lines[i];var span=getMarkedSpanFor(line.markedSpans,this);if(cm&&!this.collapsed){regLineChange(cm,lineNo(line),"text")}else if(cm){if(span.to!=null){max=lineNo(line)}if(span.from!=null){min=lineNo(line)}}line.markedSpans=removeMarkedSpan(line.markedSpans,span);if(span.from==null&&this.collapsed&&!lineIsHidden(this.doc,line)&&cm){updateLineHeight(line,textHeight(cm.display))}}if(cm&&this.collapsed&&!cm.options.lineWrapping){for(var i$1=0;i$1<this.lines.length;++i$1){var visual=visualLine(this.lines[i$1]),len=lineLength(visual);if(len>cm.display.maxLineLength){cm.display.maxLine=visual;cm.display.maxLineLength=len;cm.display.maxLineChanged=true}}}if(min!=null&&cm&&this.collapsed){regChange(cm,min,max+1)}this.lines.length=0;this.explicitlyCleared=true;if(this.atomic&&this.doc.cantEdit){this.doc.cantEdit=false;if(cm){reCheckSelection(cm.doc)}}if(cm){signalLater(cm,"markerCleared",cm,this,min,max)}if(withOp){endOperation(cm)}if(this.parent){this.parent.clear()}};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}};TextMarker.prototype.changed=function(){var this$1=this;var pos=this.find(-1,true),widget=this,cm=this.doc.cm;if(!pos||!cm){return}runInOp(cm,(function(){var line=pos.line,lineN=lineNo(pos.line);var view=findViewForLine(cm,lineN);if(view){clearLineMeasurementCacheFor(view);cm.curOp.selectionChanged=cm.curOp.forceUpdate=true}cm.curOp.updateMaxLine=true;if(!lineIsHidden(widget.doc,line)&&widget.height!=null){var oldHeight=widget.height;widget.height=null;var dHeight=widgetHeight(widget)-oldHeight;if(dHeight){updateLineHeight(line,line.height+dHeight)}}signalLater(cm,"markerChanged",cm,this$1)}))};TextMarker.prototype.attachLine=function(line){if(!this.lines.length&&this.doc.cm){var op=this.doc.cm.curOp;if(!op.maybeHiddenMarkers||indexOf(op.maybeHiddenMarkers,this)==-1){(op.maybeUnhiddenMarkers||(op.maybeUnhiddenMarkers=[])).push(this)}}this.lines.push(line)};TextMarker.prototype.detachLine=function(line){this.lines.splice(indexOf(this.lines,line),1);if(!this.lines.length&&this.doc.cm){var op=this.doc.cm.curOp;(op.maybeHiddenMarkers||(op.maybeHiddenMarkers=[])).push(this)}};eventMixin(TextMarker);function markText(doc,from,to,options,type){if(options&&options.shared){return markTextShared(doc,from,to,options,type)}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)}if(diff>0||diff==0&&marker.clearWhenEmpty!==false){return marker}if(marker.replacedWith){marker.collapsed=true;marker.widgetNode=eltP("span",[marker.replacedWith],"CodeMirror-widget");if(!options.handleMouseEvents){marker.widgetNode.setAttribute("cm-ignore-events","true")}if(options.insertLeft){marker.widgetNode.insertLeft=true}}if(marker.collapsed){if(conflictingCollapsedRange(doc,from.line,from,to,marker)||from.line!=to.line&&conflictingCollapsedRange(doc,to.line,from,to,marker)){throw new Error("Inserting collapsed marker partially overlapping an existing one")}seeCollapsedSpans()}if(marker.addToHistory){addChangeToHistory(doc,{from:from,to:to,origin:"markText"},doc.sel,NaN)}var curLine=from.line,cm=doc.cm,updateMaxLine;doc.iter(curLine,to.line+1,(function(line){if(cm&&marker.collapsed&&!cm.options.lineWrapping&&visualLine(line)==cm.display.maxLine){updateMaxLine=true}if(marker.collapsed&&curLine!=from.line){updateLineHeight(line,0)}addMarkedSpan(line,new MarkedSpan(marker,curLine==from.line?from.ch:null,curLine==to.line?to.ch:null),doc.cm&&doc.cm.curOp);++curLine}));if(marker.collapsed){doc.iter(from.line,to.line+1,(function(line){if(lineIsHidden(doc,line)){updateLineHeight(line,0)}}))}if(marker.clearOnEnter){on(marker,"beforeCursorEnter",(function(){return marker.clear()}))}if(marker.readOnly){seeReadOnlySpans();if(doc.history.done.length||doc.history.undone.length){doc.clearHistory()}}if(marker.collapsed){marker.id=++nextMarkerId;marker.atomic=true}if(cm){if(updateMaxLine){cm.curOp.updateMaxLine=true}if(marker.collapsed){regChange(cm,from.line,to.line+1)}else if(marker.className||marker.startStyle||marker.endStyle||marker.css||marker.attributes||marker.title){for(var i=from.line;i<=to.line;i++){regLineChange(cm,i,"text")}}if(marker.atomic){reCheckSelection(cm.doc)}signalLater(cm,"markerAdded",cm,marker)}return marker}var SharedTextMarker=function(markers,primary){this.markers=markers;this.primary=primary;for(var i=0;i<markers.length;++i){markers[i].parent=this}};SharedTextMarker.prototype.clear=function(){if(this.explicitlyCleared){return}this.explicitlyCleared=true;for(var i=0;i<this.markers.length;++i){this.markers[i].clear()}signalLater(this,"clear")};SharedTextMarker.prototype.find=function(side,lineObj){return this.primary.find(side,lineObj)};eventMixin(SharedTextMarker);function markTextShared(doc,from,to,options,type){options=copyObj(options);options.shared=false;var markers=[markText(doc,from,to,options,type)],primary=markers[0];var widget=options.widgetNode;linkedDocs(doc,(function(doc){if(widget){options.widgetNode=widget.cloneNode(true)}markers.push(markText(doc,clipPos(doc,from),clipPos(doc,to),options,type));for(var i=0;i<doc.linked.length;++i){if(doc.linked[i].isParent){return}}primary=lst(markers)}));return new SharedTextMarker(markers,primary)}function findSharedMarkers(doc){return doc.findMarks(Pos(doc.first,0),doc.clipPos(Pos(doc.lastLine())),(function(m){return m.parent}))}function copySharedMarkers(doc,markers){for(var i=0;i<markers.length;i++){var marker=markers[i],pos=marker.find();var mFrom=doc.clipPos(pos.from),mTo=doc.clipPos(pos.to);if(cmp(mFrom,mTo)){var subMark=markText(doc,mFrom,mTo,marker.primary,marker.primary.type);marker.markers.push(subMark);subMark.parent=marker}}}function detachSharedMarkers(markers){var loop=function(i){var marker=markers[i],linked=[marker.primary.doc];linkedDocs(marker.primary.doc,(function(d){return linked.push(d)}));for(var j=0;j<marker.markers.length;j++){var subMarker=marker.markers[j];if(indexOf(linked,subMarker.doc)==-1){subMarker.parent=null;marker.markers.splice(j--,1)}}};for(var i=0;i<markers.length;i++)loop(i)}var nextDocId=0;var Doc=function(text,mode,firstLine,lineSep,direction){if(!(this instanceof Doc)){return new Doc(text,mode,firstLine,lineSep,direction)}if(firstLine==null){firstLine=0}BranchChunk.call(this,[new LeafChunk([new Line("",null)])]);this.first=firstLine;this.scrollTop=this.scrollLeft=0;this.cantEdit=false;this.cleanGeneration=1;this.modeFrontier=this.highlightFrontier=firstLine;var start=Pos(firstLine,0);this.sel=simpleSelection(start);this.history=new History(null);this.id=++nextDocId;this.modeOption=mode;this.lineSep=lineSep;this.direction=direction=="rtl"?"rtl":"ltr";this.extend=false;if(typeof text=="string"){text=this.splitLines(text)}updateDoc(this,{from:start,to:start,text:text});setSelection(this,simpleSelection(start),sel_dontScroll)};Doc.prototype=createObj(BranchChunk.prototype,{constructor:Doc,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)}},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)},getValue:function(lineSep){var lines=getLines(this,this.first,this.first+this.size);if(lineSep===false){return lines}return lines.join(lineSep||this.lineSeparator())},setValue:docMethodOp((function(code){var top=Pos(this.first,0),last=this.first+this.size-1;makeChange(this,{from:top,to:Pos(last,getLine(this,last).text.length),text:this.splitLines(code),origin:"setValue",full:true},true);if(this.cm){scrollToCoords(this.cm,0,0)}setSelection(this,simpleSelection(top),sel_dontScroll)})),replaceRange:function(code,from,to,origin){from=clipPos(this,from);to=to?clipPos(this,to):from;replaceRange(this,code,from,to,origin)},getRange:function(from,to,lineSep){var lines=getBetween(this,clipPos(this,from),clipPos(this,to));if(lineSep===false){return lines}if(lineSep===""){return lines.join("")}return lines.join(lineSep||this.lineSeparator())},getLine:function(line){var l=this.getLineHandle(line);return l&&l.text},getLineHandle:function(line){if(isLine(this,line)){return getLine(this,line)}},getLineNumber:function(line){return lineNo(line)},getLineHandleVisualStart:function(line){if(typeof line=="number"){line=getLine(this,line)}return visualLine(line)},lineCount:function(){return this.size},firstLine:function(){return this.first},lastLine:function(){return this.first+this.size-1},clipPos:function(pos){return clipPos(this,pos)},getCursor:function(start){var range=this.sel.primary(),pos;if(start==null||start=="head"){pos=range.head}else if(start=="anchor"){pos=range.anchor}else if(start=="end"||start=="to"||start===false){pos=range.to()}else{pos=range.from()}return pos},listSelections:function(){return this.sel.ranges},somethingSelected:function(){return this.sel.somethingSelected()},setCursor:docMethodOp((function(line,ch,options){setSimpleSelection(this,clipPos(this,typeof line=="number"?Pos(line,ch||0):line),null,options)})),setSelection:docMethodOp((function(anchor,head,options){setSimpleSelection(this,clipPos(this,anchor),clipPos(this,head||anchor),options)})),extendSelection:docMethodOp((function(head,other,options){extendSelection(this,clipPos(this,head),other&&clipPos(this,other),options)})),extendSelections:docMethodOp((function(heads,options){extendSelections(this,clipPosArray(this,heads),options)})),extendSelectionsBy:docMethodOp((function(f,options){var heads=map(this.sel.ranges,f);extendSelections(this,clipPosArray(this,heads),options)})),setSelections:docMethodOp((function(ranges,primary,options){if(!ranges.length){return}var out=[];for(var i=0;i<ranges.length;i++){out[i]=new Range(clipPos(this,ranges[i].anchor),clipPos(this,ranges[i].head||ranges[i].anchor))}if(primary==null){primary=Math.min(ranges.length-1,this.sel.primIndex)}setSelection(this,normalizeSelection(this.cm,out,primary),options)})),addSelection:docMethodOp((function(anchor,head,options){var ranges=this.sel.ranges.slice(0);ranges.push(new Range(clipPos(this,anchor),clipPos(this,head||anchor)));setSelection(this,normalizeSelection(this.cm,ranges,ranges.length-1),options)})),getSelection:function(lineSep){var ranges=this.sel.ranges,lines;for(var i=0;i<ranges.length;i++){var sel=getBetween(this,ranges[i].from(),ranges[i].to());lines=lines?lines.concat(sel):sel}if(lineSep===false){return lines}else{return lines.join(lineSep||this.lineSeparator())}},getSelections:function(lineSep){var parts=[],ranges=this.sel.ranges;for(var i=0;i<ranges.length;i++){var sel=getBetween(this,ranges[i].from(),ranges[i].to());if(lineSep!==false){sel=sel.join(lineSep||this.lineSeparator())}parts[i]=sel}return parts},replaceSelection:function(code,collapse,origin){var dup=[];for(var i=0;i<this.sel.ranges.length;i++){dup[i]=code}this.replaceSelections(dup,collapse,origin||"+input")},replaceSelections:docMethodOp((function(code,collapse,origin){var changes=[],sel=this.sel;for(var i=0;i<sel.ranges.length;i++){var range=sel.ranges[i];changes[i]={from:range.from(),to:range.to(),text:this.splitLines(code[i]),origin:origin}}var newSel=collapse&&collapse!="end"&&computeReplacedSel(this,changes,collapse);for(var i$1=changes.length-1;i$1>=0;i$1--){makeChange(this,changes[i$1])}if(newSel){setSelectionReplaceHistory(this,newSel)}else if(this.cm){ensureCursorVisible(this.cm)}})),undo:docMethodOp((function(){makeChangeFromHistory(this,"undo")})),redo:docMethodOp((function(){makeChangeFromHistory(this,"redo")})),undoSelection:docMethodOp((function(){makeChangeFromHistory(this,"undo",true)})),redoSelection:docMethodOp((function(){makeChangeFromHistory(this,"redo",true)})),setExtending:function(val){this.extend=val},getExtending:function(){return this.extend},historySize:function(){var hist=this.history,done=0,undone=0;for(var i=0;i<hist.done.length;i++){if(!hist.done[i].ranges){++done}}for(var i$1=0;i$1<hist.undone.length;i$1++){if(!hist.undone[i$1].ranges){++undone}}return{undo:done,redo:undone}},clearHistory:function(){var this$1=this;this.history=new History(this.history);linkedDocs(this,(function(doc){return doc.history=this$1.history}),true)},markClean:function(){this.cleanGeneration=this.changeGeneration(true)},changeGeneration:function(forceSplit){if(forceSplit){this.history.lastOp=this.history.lastSelOp=this.history.lastOrigin=null}return this.history.generation},isClean:function(gen){return this.history.generation==(gen||this.cleanGeneration)},getHistory:function(){return{done:copyHistoryArray(this.history.done),undone:copyHistoryArray(this.history.undone)}},setHistory:function(histData){var hist=this.history=new History(this.history);hist.done=copyHistoryArray(histData.done.slice(0),null,true);hist.undone=copyHistoryArray(histData.undone.slice(0),null,true)},setGutterMarker:docMethodOp((function(line,gutterID,value){return changeLine(this,line,"gutter",(function(line){var markers=line.gutterMarkers||(line.gutterMarkers={});markers[gutterID]=value;if(!value&&isEmpty(markers)){line.gutterMarkers=null}return true}))})),clearGutter:docMethodOp((function(gutterID){var this$1=this;this.iter((function(line){if(line.gutterMarkers&&line.gutterMarkers[gutterID]){changeLine(this$1,line,"gutter",(function(){line.gutterMarkers[gutterID]=null;if(isEmpty(line.gutterMarkers)){line.gutterMarkers=null}return true}))}}))})),lineInfo:function(line){var n;if(typeof line=="number"){if(!isLine(this,line)){return null}n=line;line=getLine(this,line);if(!line){return null}}else{n=lineNo(line);if(n==null){return null}}return{line:n,handle:line,text:line.text,gutterMarkers:line.gutterMarkers,textClass:line.textClass,bgClass:line.bgClass,wrapClass:line.wrapClass,widgets:line.widgets}},addLineClass:docMethodOp((function(handle,where,cls){return changeLine(this,handle,where=="gutter"?"gutter":"class",(function(line){var prop=where=="text"?"textClass":where=="background"?"bgClass":where=="gutter"?"gutterClass":"wrapClass";if(!line[prop]){line[prop]=cls}else if(classTest(cls).test(line[prop])){return false}else{line[prop]+=" "+cls}return true}))})),removeLineClass:docMethodOp((function(handle,where,cls){return changeLine(this,handle,where=="gutter"?"gutter":"class",(function(line){var prop=where=="text"?"textClass":where=="background"?"bgClass":where=="gutter"?"gutterClass":"wrapClass";var cur=line[prop];if(!cur){return false}else if(cls==null){line[prop]=null}else{var found=cur.match(classTest(cls));if(!found){return false}var end=found.index+found[0].length;line[prop]=cur.slice(0,found.index)+(!found.index||end==cur.length?"":" ")+cur.slice(end)||null}return true}))})),addLineWidget:docMethodOp((function(handle,node,options){return addLineWidget(this,handle,node,options)})),removeLineWidget:function(widget){widget.clear()},markText:function(from,to,options){return markText(this,clipPos(this,from),clipPos(this,to),options,options&&options.type||"range")},setBookmark:function(pos,options){var realOpts={replacedWith:options&&(options.nodeType==null?options.widget:options),insertLeft:options&&options.insertLeft,clearWhenEmpty:false,shared:options&&options.shared,handleMouseEvents:options&&options.handleMouseEvents};pos=clipPos(this,pos);return markText(this,pos,pos,realOpts,"bookmark")},findMarksAt:function(pos){pos=clipPos(this,pos);var markers=[],spans=getLine(this,pos.line).markedSpans;if(spans){for(var i=0;i<spans.length;++i){var span=spans[i];if((span.from==null||span.from<=pos.ch)&&(span.to==null||span.to>=pos.ch)){markers.push(span.marker.parent||span.marker)}}}return markers},findMarks:function(from,to,filter){from=clipPos(this,from);to=clipPos(this,to);var found=[],lineNo=from.line;this.iter(from.line,to.line+1,(function(line){var spans=line.markedSpans;if(spans){for(var i=0;i<spans.length;i++){var span=spans[i];if(!(span.to!=null&&lineNo==from.line&&from.ch>=span.to||span.from==null&&lineNo!=from.line||span.from!=null&&lineNo==to.line&&span.from>=to.ch)&&(!filter||filter(span.marker))){found.push(span.marker.parent||span.marker)}}}++lineNo}));return found},getAllMarks:function(){var markers=[];this.iter((function(line){var sps=line.markedSpans;if(sps){for(var i=0;i<sps.length;++i){if(sps[i].from!=null){markers.push(sps[i].marker)}}}}));return markers},posFromIndex:function(off){var ch,lineNo=this.first,sepSize=this.lineSeparator().length;this.iter((function(line){var sz=line.text.length+sepSize;if(sz>off){ch=off;return true}off-=sz;++lineNo}));return clipPos(this,Pos(lineNo,ch))},indexFromPos:function(coords){coords=clipPos(this,coords);var index=coords.ch;if(coords.line<this.first||coords.ch<0){return 0}var sepSize=this.lineSeparator().length;this.iter(this.first,coords.line,(function(line){index+=line.text.length+sepSize}));return index},copy:function(copyHistory){var doc=new Doc(getLines(this,this.first,this.first+this.size),this.modeOption,this.first,this.lineSep,this.direction);doc.scrollTop=this.scrollTop;doc.scrollLeft=this.scrollLeft;doc.sel=this.sel;doc.extend=false;if(copyHistory){doc.history.undoDepth=this.history.undoDepth;doc.setHistory(this.getHistory())}return doc},linkedDoc:function(options){if(!options){options={}}var from=this.first,to=this.first+this.size;if(options.from!=null&&options.from>from){from=options.from}if(options.to!=null&&options.to<to){to=options.to}var copy=new Doc(getLines(this,from,to),options.mode||this.modeOption,from,this.lineSep,this.direction);if(options.sharedHist){copy.history=this.history}(this.linked||(this.linked=[])).push({doc:copy,sharedHist:options.sharedHist});copy.linked=[{doc:this,isParent:true,sharedHist:options.sharedHist}];copySharedMarkers(copy,findSharedMarkers(this));return copy},unlinkDoc:function(other){if(other instanceof CodeMirror){other=other.doc}if(this.linked){for(var i=0;i<this.linked.length;++i){var link=this.linked[i];if(link.doc!=other){continue}this.linked.splice(i,1);other.unlinkDoc(this);detachSharedMarkers(findSharedMarkers(this));break}}if(other.history==this.history){var splitIds=[other.id];linkedDocs(other,(function(doc){return splitIds.push(doc.id)}),true);other.history=new History(null);other.history.done=copyHistoryArray(this.history.done,splitIds);other.history.undone=copyHistoryArray(this.history.undone,splitIds)}},iterLinkedDocs:function(f){linkedDocs(this,f)},getMode:function(){return this.mode},getEditor:function(){return this.cm},splitLines:function(str){if(this.lineSep){return str.split(this.lineSep)}return splitLinesAuto(str)},lineSeparator:function(){return this.lineSep||"\n"},setDirection:docMethodOp((function(dir){if(dir!="rtl"){dir="ltr"}if(dir==this.direction){return}this.direction=dir;this.iter((function(line){return line.order=null}));if(this.cm){directionChanged(this.cm)}}))});Doc.prototype.eachLine=Doc.prototype.iter;var lastDrop=0;function onDrop(e){var cm=this;clearDragCursor(cm);if(signalDOMEvent(cm,e)||eventInWidget(cm.display,e)){return}e_preventDefault(e);if(ie){lastDrop=+new Date}var pos=posFromMouse(cm,e,true),files=e.dataTransfer.files;if(!pos||cm.isReadOnly()){return}if(files&&files.length&&window.FileReader&&window.File){var n=files.length,text=Array(n),read=0;var markAsReadAndPasteIfAllFilesAreRead=function(){if(++read==n){operation(cm,(function(){pos=clipPos(cm.doc,pos);var change={from:pos,to:pos,text:cm.doc.splitLines(text.filter((function(t){return t!=null})).join(cm.doc.lineSeparator())),origin:"paste"};makeChange(cm.doc,change);setSelectionReplaceHistory(cm.doc,simpleSelection(clipPos(cm.doc,pos),clipPos(cm.doc,changeEnd(change))))}))()}};var readTextFromFile=function(file,i){if(cm.options.allowDropFileTypes&&indexOf(cm.options.allowDropFileTypes,file.type)==-1){markAsReadAndPasteIfAllFilesAreRead();return}var reader=new FileReader;reader.onerror=function(){return markAsReadAndPasteIfAllFilesAreRead()};reader.onload=function(){var content=reader.result;if(/[\x00-\x08\x0e-\x1f]{2}/.test(content)){markAsReadAndPasteIfAllFilesAreRead();return}text[i]=content;markAsReadAndPasteIfAllFilesAreRead()};reader.readAsText(file)};for(var i=0;i<files.length;i++){readTextFromFile(files[i],i)}}else{if(cm.state.draggingText&&cm.doc.sel.contains(pos)>-1){cm.state.draggingText(e);setTimeout((function(){return cm.display.input.focus()}),20);return}try{var text$1=e.dataTransfer.getData("Text");if(text$1){var selected;if(cm.state.draggingText&&!cm.state.draggingText.copy){selected=cm.listSelections()}setSelectionNoUndo(cm.doc,simpleSelection(pos,pos));if(selected){for(var i$1=0;i$1<selected.length;++i$1){replaceRange(cm.doc,"",selected[i$1].anchor,selected[i$1].head,"drag")}}cm.replaceSelection(text$1,"around","paste");cm.display.input.focus()}}catch(e$1){}}}function onDragStart(cm,e){if(ie&&(!cm.state.draggingText||+new Date-lastDrop<100)){e_stop(e);return}if(signalDOMEvent(cm,e)||eventInWidget(cm.display,e)){return}e.dataTransfer.setData("Text",cm.getSelection());e.dataTransfer.effectAllowed="copyMove";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);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}}function forEachCodeMirror(f){if(!document.getElementsByClassName){return}var byClass=document.getElementsByClassName("CodeMirror"),editors=[];for(var i=0;i<byClass.length;i++){var cm=byClass[i].CodeMirror;if(cm){editors.push(cm)}}if(editors.length){editors[0].operation((function(){for(var i=0;i<editors.length;i++){f(editors[i])}}))}}var globalsRegistered=false;function ensureGlobalHandlers(){if(globalsRegistered){return}registerGlobalHandlers();globalsRegistered=true}function registerGlobalHandlers(){var resizeTimer;on(window,"resize",(function(){if(resizeTimer==null){resizeTimer=setTimeout((function(){resizeTimer=null;forEachCodeMirror(onResize)}),100)}}));on(window,"blur",(function(){return forEachCodeMirror(onBlur)}))}function onResize(cm){var d=cm.display;d.cachedCharWidth=d.cachedTextHeight=d.cachedPaddingH=null;d.scrollbarsClipped=false;cm.setSize()}var keyNames={3:"Pause",8:"Backspace",9:"Tab",13:"Enter",16:"Shift",17:"Ctrl",18:"Alt",19:"Pause",20:"CapsLock",27:"Esc",32:"Space",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",44:"PrintScrn",45:"Insert",46:"Delete",59:";",61:"=",91:"Mod",92:"Mod",93:"Mod",106:"*",107:"=",109:"-",110:".",111:"/",145:"ScrollLock",173:"-",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'",224:"Mod",63232:"Up",63233:"Down",63234:"Left",63235:"Right",63272:"Delete",63273:"Home",63275:"End",63276:"PageUp",63277:"PageDown",63302:"Insert"};for(var i=0;i<10;i++){keyNames[i+48]=keyNames[i+96]=String(i)}for(var i$1=65;i$1<=90;i$1++){keyNames[i$1]=String.fromCharCode(i$1)}for(var i$2=1;i$2<=12;i$2++){keyNames[i$2+111]=keyNames[i$2+63235]="F"+i$2}var keyMap={};keyMap.basic={Left:"goCharLeft",Right:"goCharRight",Up:"goLineUp",Down:"goLineDown",End:"goLineEnd",Home:"goLineStartSmart",PageUp:"goPageUp",PageDown:"goPageDown",Delete:"delCharAfter",Backspace:"delCharBefore","Shift-Backspace":"delCharBefore",Tab:"defaultTab","Shift-Tab":"indentAuto",Enter:"newlineAndIndent",Insert:"toggleOverwrite",Esc:"singleSelection"};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"};keyMap.emacsy={"Ctrl-F":"goCharRight","Ctrl-B":"goCharLeft","Ctrl-P":"goLineUp","Ctrl-N":"goLineDown","Ctrl-A":"goLineStart","Ctrl-E":"goLineEnd","Ctrl-V":"goPageDown","Shift-Ctrl-V":"goPageUp","Ctrl-D":"delCharAfter","Ctrl-H":"delCharBefore","Alt-Backspace":"delWordBefore","Ctrl-K":"killLine","Ctrl-T":"transposeChars","Ctrl-O":"openLine"};keyMap.macDefault={"Cmd-A":"selectAll","Cmd-D":"deleteLine","Cmd-Z":"undo","Shift-Cmd-Z":"redo","Cmd-Y":"redo","Cmd-Home":"goDocStart","Cmd-Up":"goDocStart","Cmd-End":"goDocEnd","Cmd-Down":"goDocEnd","Alt-Left":"goGroupLeft","Alt-Right":"goGroupRight","Cmd-Left":"goLineLeft","Cmd-Right":"goLineRight","Alt-Backspace":"delGroupBefore","Ctrl-Alt-Backspace":"delGroupAfter","Alt-Delete":"delGroupAfter","Cmd-S":"save","Cmd-F":"find","Cmd-G":"findNext","Shift-Cmd-G":"findPrev","Cmd-Alt-F":"replace","Shift-Cmd-Alt-F":"replaceAll","Cmd-[":"indentLess","Cmd-]":"indentMore","Cmd-Backspace":"delWrappedLineLeft","Cmd-Delete":"delWrappedLineRight","Cmd-U":"undoSelection","Shift-Cmd-U":"redoSelection","Ctrl-Up":"goDocStart","Ctrl-Down":"goDocEnd",fallthrough:["basic","emacsy"]};keyMap["default"]=mac?keyMap.macDefault:keyMap.pcDefault;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}function normalizeKeyMap(keymap){var copy={};for(var keyname in keymap){if(keymap.hasOwnProperty(keyname)){var value=keymap[keyname];if(/^(name|fallthrough|(de|at)tach)$/.test(keyname)){continue}if(value=="..."){delete keymap[keyname];continue}var keys=map(keyname.split(" "),normalizeKeyName);for(var i=0;i<keys.length;i++){var val=void 0,name=void 0;if(i==keys.length-1){name=keys.join(" ");val=value}else{name=keys.slice(0,i+1).join(" ");val="..."}var prev=copy[name];if(!prev){copy[name]=val}else if(prev!=val){throw new Error("Inconsistent bindings for "+name)}}delete keymap[keyname]}}for(var prop in copy){keymap[prop]=copy[prop]}return keymap}function lookupKey(key,map,handle,context){map=getKeyMap(map);var found=map.call?map.call(key,context):map[key];if(found===false){return"nothing"}if(found==="..."){return"multi"}if(found!=null&&handle(found)){return"handled"}if(map.fallthrough){if(Object.prototype.toString.call(map.fallthrough)!="[object Array]"){return lookupKey(key,map.fallthrough,handle,context)}for(var i=0;i<map.fallthrough.length;i++){var result=lookupKey(key,map.fallthrough[i],handle,context);if(result){return result}}}}function isModifierKey(value){var name=typeof value=="string"?value:keyNames[value.keyCode];return name=="Ctrl"||name=="Alt"||name=="Shift"||name=="Mod"}function addModifierNames(name,event,noShift){var base=name;if(event.altKey&&base!="Alt"){name="Alt-"+name}if((flipCtrlCmd?event.metaKey:event.ctrlKey)&&base!="Ctrl"){name="Ctrl-"+name}if((flipCtrlCmd?event.ctrlKey:event.metaKey)&&base!="Mod"){name="Cmd-"+name}if(!noShift&&event.shiftKey&&base!="Shift"){name="Shift-"+name}return name}function keyName(event,noShift){if(presto&&event.keyCode==34&&event["char"]){return false}var name=keyNames[event.keyCode];if(name==null||event.altGraphKey){return false}if(event.keyCode==3&&event.code){name=event.code}return addModifierNames(name,event,noShift)}function getKeyMap(val){return typeof val=="string"?keyMap[val]:val}function deleteNearSelection(cm,compute){var ranges=cm.doc.sel.ranges,kill=[];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)}runInOp(cm,(function(){for(var i=kill.length-1;i>=0;i--){replaceRange(cm.doc,"",kill[i].from,kill[i].to,"+delete")}ensureCursorVisible(cm)}))}function moveCharLogically(line,ch,dir){var target=skipExtendingChars(line.text,ch+dir,dir);return target<0||target>line.text.length?null:target}function moveLogically(line,start,dir){var ch=moveCharLogically(line,start.ch,dir);return ch==null?null:new Pos(start.line,ch,dir<0?"after":"before")}function endOfLine(visually,cm,lineObj,lineNo,dir){if(visually){if(cm.doc.direction=="rtl"){dir=-dir}var order=getOrder(lineObj,cm.doc.direction);if(order){var part=dir<0?lst(order):order[0];var moveInStorageOrder=dir<0==(part.level==1);var sticky=moveInStorageOrder?"after":"before";var ch;if(part.level>0||cm.doc.direction=="rtl"){var prep=prepareMeasureForLine(cm,lineObj);ch=dir<0?lineObj.text.length-1:0;var targetTop=measureCharPrepared(cm,prep,ch).top;ch=findFirst((function(ch){return measureCharPrepared(cm,prep,ch).top==targetTop}),dir<0==(part.level==1)?part.from:part.to-1,ch);if(sticky=="before"){ch=moveCharLogically(lineObj,ch,1)}}else{ch=dir<0?part.to:part.from}return new Pos(lineNo,ch,sticky)}}return new Pos(lineNo,dir<0?lineObj.text.length:0,dir<0?"before":"after")}function moveVisually(cm,line,start,dir){var bidi=getOrder(line,cm.doc.direction);if(!bidi){return moveLogically(line,start,dir)}if(start.ch>=line.text.length){start.ch=line.text.length;start.sticky="before"}else if(start.ch<=0){start.ch=0;start.sticky="after"}var partPos=getBidiPartAt(bidi,start.ch,start.sticky),part=bidi[partPos];if(cm.doc.direction=="ltr"&&part.level%2==0&&(dir>0?part.to>start.ch:part.from<start.ch)){return moveLogically(line,start,dir)}var mv=function(pos,dir){return moveCharLogically(line,pos instanceof Pos?pos.ch:pos,dir)};var prep;var getWrappedLineExtent=function(ch){if(!cm.options.lineWrapping){return{begin:0,end:line.text.length}}prep=prep||prepareMeasureForLine(cm,line);return wrappedLineExtentChar(cm,line,prep,ch)};var wrappedLineExtent=getWrappedLineExtent(start.sticky=="before"?mv(start,-1):start.ch);if(cm.doc.direction=="rtl"||part.level==1){var moveInStorageOrder=part.level==1==dir<0;var ch=mv(start,moveInStorageOrder?1:-1);if(ch!=null&&(!moveInStorageOrder?ch>=part.from&&ch>=wrappedLineExtent.begin:ch<=part.to&&ch<=wrappedLineExtent.end)){var sticky=moveInStorageOrder?"before":"after";return new Pos(start.line,ch,sticky)}}var searchInVisualLine=function(partPos,dir,wrappedLineExtent){var getRes=function(ch,moveInStorageOrder){return moveInStorageOrder?new Pos(start.line,mv(ch,1),"before"):new Pos(start.line,ch,"after")};for(;partPos>=0&&partPos<bidi.length;partPos+=dir){var part=bidi[partPos];var moveInStorageOrder=dir>0==(part.level!=1);var ch=moveInStorageOrder?wrappedLineExtent.begin:mv(wrappedLineExtent.end,-1);if(part.from<=ch&&ch<part.to){return getRes(ch,moveInStorageOrder)}ch=moveInStorageOrder?part.from:mv(part.to,-1);if(wrappedLineExtent.begin<=ch&&ch<wrappedLineExtent.end){return getRes(ch,moveInStorageOrder)}}};var res=searchInVisualLine(partPos+dir,dir,wrappedLineExtent);if(res){return res}var nextCh=dir>0?wrappedLineExtent.end:mv(wrappedLineExtent.begin,-1);if(nextCh!=null&&!(dir>0&&nextCh==line.text.length)){res=searchInVisualLine(dir>0?0:bidi.length-1,dir,getWrappedLineExtent(nextCh));if(res){return res}}return null}var commands={selectAll:selectAll,singleSelection:function(cm){return cm.setSelection(cm.getCursor("anchor"),cm.getCursor("head"),sel_dontScroll)},killLine:function(cm){return deleteNearSelection(cm,(function(range){if(range.empty()){var len=getLine(cm.doc,range.head.line).text.length;if(range.head.ch==len&&range.head.line<cm.lastLine()){return{from:range.head,to:Pos(range.head.line+1,0)}}else{return{from:range.head,to:Pos(range.head.line,len)}}}else{return{from:range.from(),to:range.to()}}}))},deleteLine:function(cm){return deleteNearSelection(cm,(function(range){return{from:Pos(range.from().line,0),to:clipPos(cm.doc,Pos(range.to().line+1,0))}}))},delLineLeft:function(cm){return deleteNearSelection(cm,(function(range){return{from:Pos(range.from().line,0),to:range.from()}}))},delWrappedLineLeft:function(cm){return deleteNearSelection(cm,(function(range){var top=cm.charCoords(range.head,"div").top+5;var leftPos=cm.coordsChar({left:0,top:top},"div");return{from:leftPos,to:range.from()}}))},delWrappedLineRight:function(cm){return deleteNearSelection(cm,(function(range){var top=cm.charCoords(range.head,"div").top+5;var rightPos=cm.coordsChar({left:cm.display.lineDiv.offsetWidth+100,top:top},"div");return{from:range.from(),to:rightPos}}))},undo:function(cm){return cm.undo()},redo:function(cm){return cm.redo()},undoSelection:function(cm){return cm.undoSelection()},redoSelection:function(cm){return cm.redoSelection()},goDocStart:function(cm){return cm.extendSelection(Pos(cm.firstLine(),0))},goDocEnd:function(cm){return cm.extendSelection(Pos(cm.lastLine()))},goLineStart:function(cm){return cm.extendSelectionsBy((function(range){return lineStart(cm,range.head.line)}),{origin:"+move",bias:1})},goLineStartSmart:function(cm){return cm.extendSelectionsBy((function(range){return lineStartSmart(cm,range.head)}),{origin:"+move",bias:1})},goLineEnd:function(cm){return cm.extendSelectionsBy((function(range){return lineEnd(cm,range.head.line)}),{origin:"+move",bias:-1})},goLineRight:function(cm){return cm.extendSelectionsBy((function(range){var top=cm.cursorCoords(range.head,"div").top+5;return cm.coordsChar({left:cm.display.lineDiv.offsetWidth+100,top:top},"div")}),sel_move)},goLineLeft:function(cm){return cm.extendSelectionsBy((function(range){var top=cm.cursorCoords(range.head,"div").top+5;return cm.coordsChar({left:0,top:top},"div")}),sel_move)},goLineLeftSmart:function(cm){return cm.extendSelectionsBy((function(range){var top=cm.cursorCoords(range.head,"div").top+5;var pos=cm.coordsChar({left:0,top:top},"div");if(pos.ch<cm.getLine(pos.line).search(/\S/)){return lineStartSmart(cm,range.head)}return pos}),sel_move)},goLineUp:function(cm){return cm.moveV(-1,"line")},goLineDown:function(cm){return cm.moveV(1,"line")},goPageUp:function(cm){return cm.moveV(-1,"page")},goPageDown:function(cm){return cm.moveV(1,"page")},goCharLeft:function(cm){return cm.moveH(-1,"char")},goCharRight:function(cm){return cm.moveH(1,"char")},goColumnLeft:function(cm){return cm.moveH(-1,"column")},goColumnRight:function(cm){return cm.moveH(1,"column")},goWordLeft:function(cm){return cm.moveH(-1,"word")},goGroupRight:function(cm){return cm.moveH(1,"group")},goGroupLeft:function(cm){return cm.moveH(-1,"group")},goWordRight:function(cm){return cm.moveH(1,"word")},delCharBefore:function(cm){return cm.deleteH(-1,"codepoint")},delCharAfter:function(cm){return cm.deleteH(1,"char")},delWordBefore:function(cm){return cm.deleteH(-1,"word")},delWordAfter:function(cm){return cm.deleteH(1,"word")},delGroupBefore:function(cm){return cm.deleteH(-1,"group")},delGroupAfter:function(cm){return cm.deleteH(1,"group")},indentAuto:function(cm){return cm.indentSelection("smart")},indentMore:function(cm){return cm.indentSelection("add")},indentLess:function(cm){return cm.indentSelection("subtract")},insertTab:function(cm){return cm.replaceSelection("\t")},insertSoftTab:function(cm){var spaces=[],ranges=cm.listSelections(),tabSize=cm.options.tabSize;for(var i=0;i<ranges.length;i++){var pos=ranges[i].from();var col=countColumn(cm.getLine(pos.line),pos.ch,tabSize);spaces.push(spaceStr(tabSize-col%tabSize))}cm.replaceSelections(spaces)},defaultTab:function(cm){if(cm.somethingSelected()){cm.indentSelection("add")}else{cm.execCommand("insertTab")}},transposeChars:function(cm){return runInOp(cm,(function(){var ranges=cm.listSelections(),newSel=[];for(var i=0;i<ranges.length;i++){if(!ranges[i].empty()){continue}var cur=ranges[i].head,line=getLine(cm.doc,cur.line).text;if(line){if(cur.ch==line.length){cur=new Pos(cur.line,cur.ch-1)}if(cur.ch>0){cur=new Pos(cur.line,cur.ch+1);cm.replaceRange(line.charAt(cur.ch-1)+line.charAt(cur.ch-2),Pos(cur.line,cur.ch-2),cur,"+transpose")}else if(cur.line>cm.doc.first){var prev=getLine(cm.doc,cur.line-1).text;if(prev){cur=new Pos(cur.line,1);cm.replaceRange(line.charAt(0)+cm.doc.lineSeparator()+prev.charAt(prev.length-1),Pos(cur.line-1,prev.length-1),cur,"+transpose")}}}newSel.push(new Range(cur,cur))}cm.setSelections(newSel)}))},newlineAndIndent:function(cm){return runInOp(cm,(function(){var sels=cm.listSelections();for(var i=sels.length-1;i>=0;i--){cm.replaceRange(cm.doc.lineSeparator(),sels[i].anchor,sels[i].head,"+input")}sels=cm.listSelections();for(var i$1=0;i$1<sels.length;i$1++){cm.indentLine(sels[i$1].from().line,null,true)}ensureCursorVisible(cm)}))},openLine:function(cm){return cm.replaceSelection("\n","start")},toggleOverwrite:function(cm){return cm.toggleOverwrite()}};function lineStart(cm,lineN){var line=getLine(cm.doc,lineN);var visual=visualLine(line);if(visual!=line){lineN=lineNo(visual)}return endOfLine(true,cm,visual,lineN,1)}function lineEnd(cm,lineN){var line=getLine(cm.doc,lineN);var visual=visualLineEnd(line);if(visual!=line){lineN=lineNo(visual)}return endOfLine(true,cm,line,lineN,-1)}function lineStartSmart(cm,pos){var start=lineStart(cm,pos.line);var line=getLine(cm.doc,start.line);var order=getOrder(line,cm.doc.direction);if(!order||order[0].level==0){var firstNonWS=Math.max(start.ch,line.text.search(/\S/));var inWS=pos.line==start.line&&pos.ch<=firstNonWS&&pos.ch;return Pos(start.line,inWS?0:firstNonWS,start.sticky)}return start}function doHandleBinding(cm,bound,dropShift){if(typeof bound=="string"){bound=commands[bound];if(!bound){return false}}cm.display.input.ensurePolled();var prevShift=cm.display.shift,done=false;try{if(cm.isReadOnly()){cm.state.suppressEdits=true}if(dropShift){cm.display.shift=false}done=bound(cm)!=Pass}finally{cm.display.shift=prevShift;cm.state.suppressEdits=false}return done}function lookupKeyForEditor(cm,name,handle){for(var i=0;i<cm.state.keyMaps.length;i++){var result=lookupKey(name,cm.state.keyMaps[i],handle,cm);if(result){return result}}return cm.options.extraKeys&&lookupKey(name,cm.options.extraKeys,handle,cm)||lookupKey(name,cm.options.keyMap,handle,cm)}var stopSeq=new Delayed;function dispatchKey(cm,name,e,handle){var seq=cm.state.keySeq;if(seq){if(isModifierKey(name)){return"handled"}if(/\'$/.test(name)){cm.state.keySeq=null}else{stopSeq.set(50,(function(){if(cm.state.keySeq==seq){cm.state.keySeq=null;cm.display.input.reset()}}))}if(dispatchKeyInner(cm,seq+" "+name,e,handle)){return true}}return dispatchKeyInner(cm,name,e,handle)}function dispatchKeyInner(cm,name,e,handle){var result=lookupKeyForEditor(cm,name,handle);if(result=="multi"){cm.state.keySeq=name}if(result=="handled"){signalLater(cm,"keyHandled",cm,name,e)}if(result=="handled"||result=="multi"){e_preventDefault(e);restartBlink(cm)}return!!result}function handleKeyBinding(cm,e){var name=keyName(e,true);if(!name){return false}if(e.shiftKey&&!cm.state.keySeq){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)}))}}function handleCharBinding(cm,e,ch){return dispatchKey(cm,"'"+ch+"'",e,(function(b){return doHandleBinding(cm,b,true)}))}var lastStoppedKey=null;function onKeyDown(e){var cm=this;if(e.target&&e.target!=cm.display.input.getField()){return}cm.curOp.focus=activeElt(doc(cm));if(signalDOMEvent(cm,e)){return}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;if(!handled&&code==88&&!hasCopyEvent&&(mac?e.metaKey:e.ctrlKey)){cm.replaceSelection("",null,"cut")}}if(gecko&&!mac&&!handled&&code==46&&e.shiftKey&&!e.ctrlKey&&document.execCommand){document.execCommand("cut")}if(code==18&&!/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)){showCrossHair(cm)}}function showCrossHair(cm){var lineDiv=cm.display.lineDiv;addClass(lineDiv,"CodeMirror-crosshair");function up(e){if(e.keyCode==18||!e.altKey){rmClass(lineDiv,"CodeMirror-crosshair");off(document,"keyup",up);off(document,"mouseover",up)}}on(document,"keyup",up);on(document,"mouseover",up)}function onKeyUp(e){if(e.keyCode==16){this.doc.sel.shift=false}signalDOMEvent(this,e)}function onKeyPress(e){var cm=this;if(e.target&&e.target!=cm.display.input.getField()){return}if(eventInWidget(cm.display,e)||signalDOMEvent(cm,e)||e.ctrlKey&&!e.altKey||mac&&e.metaKey){return}var keyCode=e.keyCode,charCode=e.charCode;if(presto&&keyCode==lastStoppedKey){lastStoppedKey=null;e_preventDefault(e);return}if(presto&&(!e.which||e.which<10)&&handleKeyBinding(cm,e)){return}var ch=String.fromCharCode(charCode==null?keyCode:charCode);if(ch=="\b"){return}if(handleCharBinding(cm,e,ch)){return}cm.display.input.onKeyPress(e)}var DOUBLECLICK_DELAY=400;var PastClick=function(time,pos,button){this.time=time;this.pos=pos;this.button=button};PastClick.prototype.compare=function(time,pos,button){return this.time+DOUBLECLICK_DELAY>time&&cmp(pos,this.pos)==0&&button==this.button};var lastClick,lastDoubleClick;function clickRepeat(pos,button){var now=+new Date;if(lastDoubleClick&&lastDoubleClick.compare(now,pos,button)){lastClick=lastDoubleClick=null;return"triple"}else if(lastClick&&lastClick.compare(now,pos,button)){lastDoubleClick=new PastClick(now,pos,button);lastClick=null;return"double"}else{lastClick=new PastClick(now,pos,button);lastDoubleClick=null;return"single"}}function onMouseDown(e){var cm=this,display=cm.display;if(signalDOMEvent(cm,e)||display.activeTouch&&display.input.supportsTouch()){return}display.input.ensurePolled();display.shift=e.shiftKey;if(eventInWidget(display,e)){if(!webkit){display.scroller.draggable=false;setTimeout((function(){return display.scroller.draggable=true}),100)}return}if(clickInGutter(cm,e)){return}var pos=posFromMouse(cm,e),button=e_button(e),repeat=pos?clickRepeat(pos,button):"single";win(cm).focus();if(button==1&&cm.state.selectingText){cm.state.selectingText(e)}if(pos&&handleMappedButton(cm,button,pos,repeat,e)){return}if(button==1){if(pos){leftButtonDown(cm,pos,repeat,e)}else if(e_target(e)==display.scroller){e_preventDefault(e)}}else if(button==2){if(pos){extendSelection(cm.doc,pos)}setTimeout((function(){return display.input.focus()}),20)}else if(button==3){if(captureRightClick){cm.display.input.onContextMenu(e)}else{delayBlurEvent(cm)}}}function handleMappedButton(cm,button,pos,repeat,event){var name="Click";if(repeat=="double"){name="Double"+name}else if(repeat=="triple"){name="Triple"+name}name=(button==1?"Left":button==2?"Middle":"Right")+name;return dispatchKey(cm,addModifierNames(name,event),event,(function(bound){if(typeof bound=="string"){bound=commands[bound]}if(!bound){return false}var done=false;try{if(cm.isReadOnly()){cm.state.suppressEdits=true}done=bound(cm,pos)!=Pass}finally{cm.state.suppressEdits=false}return done}))}function configureMouse(cm,repeat,event){var option=cm.getOption("configureMouse");var value=option?option(cm,repeat,event):{};if(value.unit==null){var rect=chromeOS?event.shiftKey&&event.metaKey:event.altKey;value.unit=rect?"rectangle":repeat=="single"?"char":repeat=="double"?"word":"line"}if(value.extend==null||cm.doc.extend){value.extend=cm.doc.extend||event.shiftKey}if(value.addNew==null){value.addNew=mac?event.metaKey:event.ctrlKey||event.altKey}if(value.moveOnDrag==null){value.moveOnDrag=!(mac?event.altKey:event.ctrlKey)}return value}function leftButtonDown(cm,pos,repeat,event){if(ie){setTimeout(bind(ensureFocus,cm),0)}else{cm.curOp.focus=activeElt(doc(cm))}var behavior=configureMouse(cm,repeat,event);var sel=cm.doc.sel,contained;if(cm.options.dragDrop&&dragAndDrop&&!cm.isReadOnly()&&repeat=="single"&&(contained=sel.contains(pos))>-1&&(cmp((contained=sel.ranges[contained]).from(),pos)<0||pos.xRel>0)&&(cmp(contained.to(),pos)>0||pos.xRel<0)){leftButtonStartDrag(cm,event,pos,behavior)}else{leftButtonSelect(cm,event,pos,behavior)}}function leftButtonStartDrag(cm,event,pos,behavior){var display=cm.display,moved=false;var dragEnd=operation(cm,(function(e){if(webkit){display.scroller.draggable=false}cm.state.draggingText=false;if(cm.state.delayingBlurEvent){if(cm.hasFocus()){cm.state.delayingBlurEvent=false}else{delayBlurEvent(cm)}}off(display.wrapper.ownerDocument,"mouseup",dragEnd);off(display.wrapper.ownerDocument,"mousemove",mouseMove);off(display.scroller,"dragstart",dragStart);off(display.scroller,"drop",dragEnd);if(!moved){e_preventDefault(e);if(!behavior.addNew){extendSelection(cm.doc,pos,null,null,behavior.extend)}if(webkit&&!safari||ie&&ie_version==9){setTimeout((function(){display.wrapper.ownerDocument.body.focus({preventScroll:true});display.input.focus()}),20)}else{display.input.focus()}}}));var mouseMove=function(e2){moved=moved||Math.abs(event.clientX-e2.clientX)+Math.abs(event.clientY-e2.clientY)>=10};var dragStart=function(){return moved=true};if(webkit){display.scroller.draggable=true}cm.state.draggingText=dragEnd;dragEnd.copy=!behavior.moveOnDrag;on(display.wrapper.ownerDocument,"mouseup",dragEnd);on(display.wrapper.ownerDocument,"mousemove",mouseMove);on(display.scroller,"dragstart",dragStart);on(display.scroller,"drop",dragEnd);cm.state.delayingBlurEvent=true;setTimeout((function(){return display.input.focus()}),20);if(display.scroller.dragDrop){display.scroller.dragDrop()}}function rangeForUnit(cm,pos,unit){if(unit=="char"){return new Range(pos,pos)}if(unit=="word"){return cm.findWordAt(pos)}if(unit=="line"){return new Range(Pos(pos.line,0),clipPos(cm.doc,Pos(pos.line+1,0)))}var result=unit(cm,pos);return new Range(result.from,result.to)}function leftButtonSelect(cm,event,start,behavior){if(ie){delayBlurEvent(cm)}var display=cm.display,doc$1=cm.doc;e_preventDefault(event);var ourRange,ourIndex,startSel=doc$1.sel,ranges=startSel.ranges;if(behavior.addNew&&!behavior.extend){ourIndex=doc$1.sel.contains(start);if(ourIndex>-1){ourRange=ranges[ourIndex]}else{ourRange=new Range(start,start)}}else{ourRange=doc$1.sel.primary();ourIndex=doc$1.sel.primIndex}if(behavior.unit=="rectangle"){if(!behavior.addNew){ourRange=new Range(start,start)}start=posFromMouse(cm,event,true,true);ourIndex=-1}else{var range=rangeForUnit(cm,start,behavior.unit);if(behavior.extend){ourRange=extendRange(ourRange,range.anchor,range.head,behavior.extend)}else{ourRange=range}}if(!behavior.addNew){ourIndex=0;setSelection(doc$1,new Selection([ourRange],0),sel_mouse);startSel=doc$1.sel}else if(ourIndex==-1){ourIndex=ranges.length;setSelection(doc$1,normalizeSelection(cm,ranges.concat([ourRange]),ourIndex),{scroll:false,origin:"*mouse"})}else if(ranges.length>1&&ranges[ourIndex].empty()&&behavior.unit=="char"&&!behavior.extend){setSelection(doc$1,normalizeSelection(cm,ranges.slice(0,ourIndex).concat(ranges.slice(ourIndex+1)),0),{scroll:false,origin:"*mouse"});startSel=doc$1.sel}else{replaceOneSelection(doc$1,ourIndex,ourRange,sel_mouse)}var lastPos=start;function extendTo(pos){if(cmp(lastPos,pos)==0){return}lastPos=pos;if(behavior.unit=="rectangle"){var ranges=[],tabSize=cm.options.tabSize;var startCol=countColumn(getLine(doc$1,start.line).text,start.ch,tabSize);var posCol=countColumn(getLine(doc$1,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$1,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$1,normalizeSelection(cm,startSel.ranges.slice(0,ourIndex).concat(ranges),ourIndex),{origin:"*mouse",scroll:false});cm.scrollIntoView(pos)}else{var oldRange=ourRange;var range=rangeForUnit(cm,pos,behavior.unit);var anchor=oldRange.anchor,head;if(cmp(range.anchor,anchor)>0){head=range.head;anchor=minPos(oldRange.from(),range.anchor)}else{head=range.anchor;anchor=maxPos(oldRange.to(),range.head)}var ranges$1=startSel.ranges.slice(0);ranges$1[ourIndex]=bidiSimplify(cm,new Range(clipPos(doc$1,anchor),head));setSelection(doc$1,normalizeSelection(cm,ranges$1,ourIndex),sel_mouse)}}var editorSize=display.wrapper.getBoundingClientRect();var counter=0;function extend(e){var curCount=++counter;var cur=posFromMouse(cm,e,true,behavior.unit=="rectangle");if(!cur){return}if(cmp(cur,lastPos)!=0){cm.curOp.focus=activeElt(doc(cm));extendTo(cur);var visible=visibleLines(display,doc$1);if(cur.line>=visible.to||cur.line<visible.from){setTimeout(operation(cm,(function(){if(counter==curCount){extend(e)}})),150)}}else{var outside=e.clientY<editorSize.top?-20:e.clientY>editorSize.bottom?20:0;if(outside){setTimeout(operation(cm,(function(){if(counter!=curCount){return}display.scroller.scrollTop+=outside;extend(e)})),50)}}}function done(e){cm.state.selectingText=false;counter=Infinity;if(e){e_preventDefault(e);display.input.focus()}off(display.wrapper.ownerDocument,"mousemove",move);off(display.wrapper.ownerDocument,"mouseup",up);doc$1.history.lastSelOrigin=null}var move=operation(cm,(function(e){if(e.buttons===0||!e_button(e)){done(e)}else{extend(e)}}));var up=operation(cm,done);cm.state.selectingText=up;on(display.wrapper.ownerDocument,"mousemove",move);on(display.wrapper.ownerDocument,"mouseup",up)}function bidiSimplify(cm,range){var anchor=range.anchor;var head=range.head;var anchorLine=getLine(cm.doc,anchor.line);if(cmp(anchor,head)==0&&anchor.sticky==head.sticky){return range}var order=getOrder(anchorLine);if(!order){return range}var index=getBidiPartAt(order,anchor.ch,anchor.sticky),part=order[index];if(part.from!=anchor.ch&&part.to!=anchor.ch){return range}var boundary=index+(part.from==anchor.ch==(part.level!=1)?0:1);if(boundary==0||boundary==order.length){return range}var leftSide;if(head.line!=anchor.line){leftSide=(head.line-anchor.line)*(cm.doc.direction=="ltr"?1:-1)>0}else{var headIndex=getBidiPartAt(order,head.ch,head.sticky);var dir=headIndex-index||(head.ch-anchor.ch)*(part.level==1?-1:1);if(headIndex==boundary-1||headIndex==boundary){leftSide=dir<0}else{leftSide=dir>0}}var usePart=order[boundary+(leftSide?-1:0)];var from=leftSide==(usePart.level==1);var ch=from?usePart.from:usePart.to,sticky=from?"after":"before";return anchor.ch==ch&&anchor.sticky==sticky?range:new Range(new Pos(anchor.line,ch,sticky),head)}function gutterEvent(cm,e,type,prevent){var mX,mY;if(e.touches){mX=e.touches[0].clientX;mY=e.touches[0].clientY}else{try{mX=e.clientX;mY=e.clientY}catch(e$1){return false}}if(mX>=Math.floor(cm.display.gutters.getBoundingClientRect().right)){return false}if(prevent){e_preventDefault(e)}var display=cm.display;var lineBox=display.lineDiv.getBoundingClientRect();if(mY>lineBox.bottom||!hasHandler(cm,type)){return e_defaultPrevented(e)}mY-=lineBox.top-display.viewOffset;for(var i=0;i<cm.display.gutterSpecs.length;++i){var g=display.gutters.childNodes[i];if(g&&g.getBoundingClientRect().right>=mX){var line=lineAtHeight(cm.doc,mY);var gutter=cm.display.gutterSpecs[i];signal(cm,type,cm,line,gutter.className,e);return e_defaultPrevented(e)}}}function clickInGutter(cm,e){return gutterEvent(cm,e,"gutterClick",true)}function onContextMenu(cm,e){if(eventInWidget(cm.display,e)||contextMenuInGutter(cm,e)){return}if(signalDOMEvent(cm,e,"contextmenu")){return}if(!captureRightClick){cm.display.input.onContextMenu(e)}}function contextMenuInGutter(cm,e){if(!hasHandler(cm,"gutterContextMenu")){return false}return gutterEvent(cm,e,"gutterContextMenu",false)}function themeChanged(cm){cm.display.wrapper.className=cm.display.wrapper.className.replace(/\s*cm-s-\S+/g,"")+cm.options.theme.replace(/(^|\s)\s*/g," cm-s-");clearCaches(cm)}var Init={toString:function(){return"CodeMirror.Init"}};var defaults={};var optionHandlers={};function defineOptions(CodeMirror){var optionHandlers=CodeMirror.optionHandlers;function option(name,deflt,handle,notOnInit){CodeMirror.defaults[name]=deflt;if(handle){optionHandlers[name]=notOnInit?function(cm,val,old){if(old!=Init){handle(cm,val,old)}}:handle}}CodeMirror.defineOption=option;CodeMirror.Init=Init;option("value","",(function(cm,val){return cm.setValue(val)}),true);option("mode",null,(function(cm,val){cm.doc.modeOption=val;loadMode(cm)}),true);option("indentUnit",2,loadMode,true);option("indentWithTabs",false);option("smartIndent",true);option("tabSize",4,(function(cm){resetModeState(cm);clearCaches(cm);regChange(cm)}),true);option("lineSeparator",null,(function(cm,val){cm.doc.lineSep=val;if(!val){return}var newBreaks=[],lineNo=cm.doc.first;cm.doc.iter((function(line){for(var pos=0;;){var found=line.text.indexOf(val,pos);if(found==-1){break}pos=found+val.length;newBreaks.push(Pos(lineNo,found))}lineNo++}));for(var i=newBreaks.length-1;i>=0;i--){replaceRange(cm.doc,val,newBreaks[i],Pos(newBreaks[i].line,newBreaks[i].ch+val.length))}}));option("specialChars",/[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\u2066\u2067\u2069\ufeff\ufff9-\ufffc]/g,(function(cm,val,old){cm.state.specialChars=new RegExp(val.source+(val.test("\t")?"":"|\t"),"g");if(old!=Init){cm.refresh()}}));option("specialCharPlaceholder",defaultSpecialCharPlaceholder,(function(cm){return cm.refresh()}),true);option("electricChars",true);option("inputStyle",mobile?"contenteditable":"textarea",(function(){throw new Error("inputStyle can not (yet) be changed in a running editor")}),true);option("spellcheck",false,(function(cm,val){return cm.getInputField().spellcheck=val}),true);option("autocorrect",false,(function(cm,val){return cm.getInputField().autocorrect=val}),true);option("autocapitalize",false,(function(cm,val){return cm.getInputField().autocapitalize=val}),true);option("rtlMoveVisually",!windows);option("wholeLineUpdateBefore",true);option("theme","default",(function(cm){themeChanged(cm);updateGutters(cm)}),true);option("keyMap","default",(function(cm,val,old){var next=getKeyMap(val);var prev=old!=Init&&getKeyMap(old);if(prev&&prev.detach){prev.detach(cm,next)}if(next.attach){next.attach(cm,prev||null)}}));option("extraKeys",null);option("configureMouse",null);option("lineWrapping",false,wrappingChanged,true);option("gutters",[],(function(cm,val){cm.display.gutterSpecs=getGutters(val,cm.options.lineNumbers);updateGutters(cm)}),true);option("fixedGutter",true,(function(cm,val){cm.display.gutters.style.left=val?compensateForHScroll(cm.display)+"px":"0";cm.refresh()}),true);option("coverGutterNextToScrollbar",false,(function(cm){return updateScrollbars(cm)}),true);option("scrollbarStyle","native",(function(cm){initScrollbars(cm);updateScrollbars(cm);cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft)}),true);option("lineNumbers",false,(function(cm,val){cm.display.gutterSpecs=getGutters(cm.options.gutters,val);updateGutters(cm)}),true);option("firstLineNumber",1,updateGutters,true);option("lineNumberFormatter",(function(integer){return integer}),updateGutters,true);option("showCursorWhenSelecting",false,updateSelection,true);option("resetSelectionOnContextMenu",true);option("lineWiseCopyCut",true);option("pasteLinesPerSelection",true);option("selectionsMayTouch",false);option("readOnly",false,(function(cm,val){if(val=="nocursor"){onBlur(cm);cm.display.input.blur()}cm.display.input.readOnlyChanged(val)}));option("screenReaderLabel",null,(function(cm,val){val=val===""?null:val;cm.display.input.screenReaderLabelChanged(val)}));option("disableInput",false,(function(cm,val){if(!val){cm.display.input.reset()}}),true);option("dragDrop",true,dragDropChanged);option("allowDropFileTypes",null);option("cursorBlinkRate",530);option("cursorScrollMargin",0);option("cursorHeight",1,updateSelection,true);option("singleCursorHeightPerLine",true,updateSelection,true);option("workTime",100);option("workDelay",100);option("flattenSpans",true,resetModeState,true);option("addModeClass",false,resetModeState,true);option("pollInterval",100);option("undoDepth",200,(function(cm,val){return cm.doc.history.undoDepth=val}));option("historyEventDelay",1250);option("viewportMargin",10,(function(cm){return cm.refresh()}),true);option("maxHighlightLength",1e4,resetModeState,true);option("moveInputWithCursor",true,(function(cm,val){if(!val){cm.display.input.resetPosition()}}));option("tabindex",null,(function(cm,val){return cm.display.input.getField().tabIndex=val||""}));option("autofocus",null);option("direction","ltr",(function(cm,val){return cm.doc.setDirection(val)}),true);option("phrases",null)}function dragDropChanged(cm,value,old){var wasOn=old&&old!=Init;if(!value!=!wasOn){var funcs=cm.display.dragFunctions;var toggle=value?on:off;toggle(cm.display.scroller,"dragstart",funcs.start);toggle(cm.display.scroller,"dragenter",funcs.enter);toggle(cm.display.scroller,"dragover",funcs.over);toggle(cm.display.scroller,"dragleave",funcs.leave);toggle(cm.display.scroller,"drop",funcs.drop)}}function wrappingChanged(cm){if(cm.options.lineWrapping){addClass(cm.display.wrapper,"CodeMirror-wrap");cm.display.sizer.style.minWidth="";cm.display.sizerWidth=null}else{rmClass(cm.display.wrapper,"CodeMirror-wrap");findMaxLine(cm)}estimateLineHeights(cm);regChange(cm);clearCaches(cm);setTimeout((function(){return updateScrollbars(cm)}),100)}function CodeMirror(place,options){var this$1=this;if(!(this instanceof CodeMirror)){return new CodeMirror(place,options)}this.options=options=options?copyObj(options):{};copyObj(defaults,options,false);var doc=options.value;if(typeof doc=="string"){doc=new Doc(doc,options.mode,null,options.lineSeparator,options.direction)}else if(options.mode){doc.modeOption=options.mode}this.doc=doc;var input=new CodeMirror.inputStyles[options.inputStyle](this);var display=this.display=new Display(place,doc,input,options);display.wrapper.CodeMirror=this;themeChanged(this);if(options.lineWrapping){this.display.wrapper.className+=" CodeMirror-wrap"}initScrollbars(this);this.state={keyMaps:[],overlays:[],modeGen:0,overwrite:false,delayingBlurEvent:false,focused:false,suppressEdits:false,pasteIncoming:-1,cutIncoming:-1,selectingText:false,draggingText:false,highlight:new Delayed,keySeq:null,specialChars:null};if(options.autofocus&&!mobile){display.input.focus()}if(ie&&ie_version<11){setTimeout((function(){return this$1.display.input.reset(true)}),20)}registerEventHandlers(this);ensureGlobalHandlers();startOperation(this);this.curOp.forceUpdate=true;attachDoc(this,doc);if(options.autofocus&&!mobile||this.hasFocus()){setTimeout((function(){if(this$1.hasFocus()&&!this$1.state.focused){onFocus(this$1)}}),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);if(webkit&&options.lineWrapping&&getComputedStyle(display.lineDiv).textRendering=="optimizelegibility"){display.lineDiv.style.textRendering="auto"}}CodeMirror.defaults=defaults;CodeMirror.optionHandlers=optionHandlers;function registerEventHandlers(cm){var d=cm.display;on(d.scroller,"mousedown",operation(cm,onMouseDown));if(ie&&ie_version<11){on(d.scroller,"dblclick",operation(cm,(function(e){if(signalDOMEvent(cm,e)){return}var pos=posFromMouse(cm,e);if(!pos||clickInGutter(cm,e)||eventInWidget(cm.display,e)){return}e_preventDefault(e);var word=cm.findWordAt(pos);extendSelection(cm.doc,word.anchor,word.head)})))}else{on(d.scroller,"dblclick",(function(e){return signalDOMEvent(cm,e)||e_preventDefault(e)}))}on(d.scroller,"contextmenu",(function(e){return onContextMenu(cm,e)}));on(d.input.getField(),"contextmenu",(function(e){if(!d.scroller.contains(e.target)){onContextMenu(cm,e)}}));var touchFinished,prevTouch={end:0};function finishTouch(){if(d.activeTouch){touchFinished=setTimeout((function(){return d.activeTouch=null}),1e3);prevTouch=d.activeTouch;prevTouch.end=+new Date}}function isMouseLikeTouchEvent(e){if(e.touches.length!=1){return false}var touch=e.touches[0];return touch.radiusX<=1&&touch.radiusY<=1}function farAway(touch,other){if(other.left==null){return true}var dx=other.left-touch.left,dy=other.top-touch.top;return dx*dx+dy*dy>20*20}on(d.scroller,"touchstart",(function(e){if(!signalDOMEvent(cm,e)&&!isMouseLikeTouchEvent(e)&&!clickInGutter(cm,e)){d.input.ensurePolled();clearTimeout(touchFinished);var now=+new Date;d.activeTouch={start:now,moved:false,prev:now-prevTouch.end<=300?prevTouch:null};if(e.touches.length==1){d.activeTouch.left=e.touches[0].pageX;d.activeTouch.top=e.touches[0].pageY}}}));on(d.scroller,"touchmove",(function(){if(d.activeTouch){d.activeTouch.moved=true}}));on(d.scroller,"touchend",(function(e){var touch=d.activeTouch;if(touch&&!eventInWidget(d,e)&&touch.left!=null&&!touch.moved&&new Date-touch.start<300){var pos=cm.coordsChar(d.activeTouch,"page"),range;if(!touch.prev||farAway(touch,touch.prev)){range=new Range(pos,pos)}else if(!touch.prev.prev||farAway(touch,touch.prev.prev)){range=cm.findWordAt(pos)}else{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);on(d.scroller,"scroll",(function(){if(d.scroller.clientHeight){updateScrollTop(cm,d.scroller.scrollTop);setScrollLeft(cm,d.scroller.scrollLeft,true);signal(cm,"scroll",cm)}}));on(d.scroller,"mousewheel",(function(e){return onScrollWheel(cm,e)}));on(d.scroller,"DOMMouseScroll",(function(e){return onScrollWheel(cm,e)}));on(d.wrapper,"scroll",(function(){return d.wrapper.scrollTop=d.wrapper.scrollLeft=0}));d.dragFunctions={enter:function(e){if(!signalDOMEvent(cm,e)){e_stop(e)}},over:function(e){if(!signalDOMEvent(cm,e)){onDragOver(cm,e);e_stop(e)}},start:function(e){return onDragStart(cm,e)},drop:operation(cm,onDrop),leave:function(e){if(!signalDOMEvent(cm,e)){clearDragCursor(cm)}}};var inp=d.input.getField();on(inp,"keyup",(function(e){return onKeyUp.call(cm,e)}));on(inp,"keydown",operation(cm,onKeyDown));on(inp,"keypress",operation(cm,onKeyPress));on(inp,"focus",(function(e){return onFocus(cm,e)}));on(inp,"blur",(function(e){return onBlur(cm,e)}))}var initHooks=[];CodeMirror.defineInitHook=function(f){return initHooks.push(f)};function indentLine(cm,n,how,aggressive){var doc=cm.doc,state;if(how==null){how="add"}if(how=="smart"){if(!doc.mode.indent){how="prev"}else{state=getContextBefore(cm,n).state}}var tabSize=cm.options.tabSize;var line=getLine(doc,n),curSpace=countColumn(line.text,null,tabSize);if(line.stateAfter){line.stateAfter=null}var curSpaceString=line.text.match(/^\s*/)[0],indentation;if(!aggressive&&!/\S/.test(line.text)){indentation=0;how="not"}else if(how=="smart"){indentation=doc.mode.indent(state,line.text.slice(curSpaceString.length),line.text);if(indentation==Pass||indentation>150){if(!aggressive){return}how="prev"}}if(how=="prev"){if(n>doc.first){indentation=countColumn(getLine(doc,n-1).text,null,tabSize)}else{indentation=0}}else if(how=="add"){indentation=curSpace+cm.options.indentUnit}else if(how=="subtract"){indentation=curSpace-cm.options.indentUnit}else if(typeof how=="number"){indentation=curSpace+how}indentation=Math.max(0,indentation);var indentString="",pos=0;if(cm.options.indentWithTabs){for(var i=Math.floor(indentation/tabSize);i;--i){pos+=tabSize;indentString+="\t"}}if(pos<indentation){indentString+=spaceStr(indentation-pos)}if(indentString!=curSpaceString){replaceRange(doc,indentString,Pos(n,0),Pos(n,curSpaceString.length),"+input");line.stateAfter=null;return true}else{for(var i$1=0;i$1<doc.sel.ranges.length;i$1++){var range=doc.sel.ranges[i$1];if(range.head.line==n&&range.head.ch<curSpaceString.length){var pos$1=Pos(n,curSpaceString.length);replaceOneSelection(doc,i$1,new Range(pos$1,pos$1));break}}}}var lastCopied=null;function setLastCopied(newLastCopied){lastCopied=newLastCopied}function applyTextInput(cm,inserted,deleted,sel,origin){var doc=cm.doc;cm.display.shift=false;if(!sel){sel=doc.sel}var recent=+new Date-200;var paste=origin=="paste"||cm.state.pasteIncoming>recent;var textLines=splitLinesAuto(inserted),multiPaste=null;if(paste&&sel.ranges.length>1){if(lastCopied&&lastCopied.text.join("\n")==inserted){if(sel.ranges.length%lastCopied.text.length==0){multiPaste=[];for(var i=0;i<lastCopied.text.length;i++){multiPaste.push(doc.splitLines(lastCopied.text[i]))}}}else if(textLines.length==sel.ranges.length&&cm.options.pasteLinesPerSelection){multiPaste=map(textLines,(function(l){return[l]}))}}var updateInput=cm.curOp.updateInput;for(var i$1=sel.ranges.length-1;i$1>=0;i$1--){var range=sel.ranges[i$1];var from=range.from(),to=range.to();if(range.empty()){if(deleted&&deleted>0){from=Pos(from.line,from.ch-deleted)}else if(cm.state.overwrite&&!paste){to=Pos(to.line,Math.min(getLine(doc,to.line).text.length,to.ch+lst(textLines).length))}else if(paste&&lastCopied&&lastCopied.lineWise&&lastCopied.text.join("\n")==textLines.join("\n")){from=to=Pos(from.line,0)}}var changeEvent={from:from,to:to,text:multiPaste?multiPaste[i$1%multiPaste.length]:textLines,origin:origin||(paste?"paste":cm.state.cutIncoming>recent?"cut":"+input")};makeChange(cm.doc,changeEvent);signalLater(cm,"inputRead",cm,changeEvent)}if(inserted&&!paste){triggerElectric(cm,inserted)}ensureCursorVisible(cm);if(cm.curOp.updateInput<2){cm.curOp.updateInput=updateInput}cm.curOp.typing=true;cm.state.pasteIncoming=cm.state.cutIncoming=-1}function handlePaste(e,cm){var pasted=e.clipboardData&&e.clipboardData.getData("Text");if(pasted){e.preventDefault();if(!cm.isReadOnly()&&!cm.options.disableInput&&cm.hasFocus()){runInOp(cm,(function(){return applyTextInput(cm,pasted,0,null,"paste")}))}return true}}function triggerElectric(cm,inserted){if(!cm.options.electricChars||!cm.options.smartIndent){return}var sel=cm.doc.sel;for(var i=sel.ranges.length-1;i>=0;i--){var range=sel.ranges[i];if(range.head.ch>100||i&&sel.ranges[i-1].head.line==range.head.line){continue}var mode=cm.getModeAt(range.head);var indented=false;if(mode.electricChars){for(var j=0;j<mode.electricChars.length;j++){if(inserted.indexOf(mode.electricChars.charAt(j))>-1){indented=indentLine(cm,range.head.line,"smart");break}}}else if(mode.electricInput){if(mode.electricInput.test(getLine(cm.doc,range.head.line).text.slice(0,range.head.ch))){indented=indentLine(cm,range.head.line,"smart")}}if(indented){signalLater(cm,"electricInput",cm,range.head.line)}}}function copyableRanges(cm){var text=[],ranges=[];for(var i=0;i<cm.doc.sel.ranges.length;i++){var line=cm.doc.sel.ranges[i].head.line;var lineRange={anchor:Pos(line,0),head:Pos(line+1,0)};ranges.push(lineRange);text.push(cm.getRange(lineRange.anchor,lineRange.head))}return{text:text,ranges:ranges}}function disableBrowserMagic(field,spellcheck,autocorrect,autocapitalize){field.setAttribute("autocorrect",autocorrect?"on":"off");field.setAttribute("autocapitalize",autocapitalize?"on":"off");field.setAttribute("spellcheck",!!spellcheck)}function hiddenTextarea(){var te=elt("textarea",null,null,"position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; min-height: 1em; outline: none");var div=elt("div",[te],null,"overflow: hidden; position: relative; width: 3px; height: 0px;");if(webkit){te.style.width="1000px"}else{te.setAttribute("wrap","off")}if(ios){te.style.border="1px solid black"}return div}function addEditorMethods(CodeMirror){var optionHandlers=CodeMirror.optionHandlers;var helpers=CodeMirror.helpers={};CodeMirror.prototype={constructor:CodeMirror,focus:function(){win(this).focus();this.display.input.focus()},setOption:function(option,value){var options=this.options,old=options[option];if(options[option]==value&&option!="mode"){return}options[option]=value;if(optionHandlers.hasOwnProperty(option)){operation(this,optionHandlers[option])(this,value,old)}signal(this,"optionChange",this,option)},getOption:function(option){return this.options[option]},getDoc:function(){return this.doc},addKeyMap:function(map,bottom){this.state.keyMaps[bottom?"push":"unshift"](getKeyMap(map))},removeKeyMap:function(map){var maps=this.state.keyMaps;for(var i=0;i<maps.length;++i){if(maps[i]==map||maps[i].name==map){maps.splice(i,1);return true}}},addOverlay:methodOp((function(spec,options){var mode=spec.token?spec:CodeMirror.getMode(this.options,spec);if(mode.startState){throw new Error("Overlays may not be stateful.")}insertSorted(this.state.overlays,{mode:mode,modeSpec:spec,opaque:options&&options.opaque,priority:options&&options.priority||0},(function(overlay){return overlay.priority}));this.state.modeGen++;regChange(this)})),removeOverlay:methodOp((function(spec){var overlays=this.state.overlays;for(var i=0;i<overlays.length;++i){var cur=overlays[i].modeSpec;if(cur==spec||typeof spec=="string"&&cur.name==spec){overlays.splice(i,1);this.state.modeGen++;regChange(this);return}}})),indentLine:methodOp((function(n,dir,aggressive){if(typeof dir!="string"&&typeof dir!="number"){if(dir==null){dir=this.options.smartIndent?"smart":"prev"}else{dir=dir?"add":"subtract"}}if(isLine(this.doc,n)){indentLine(this,n,dir,aggressive)}})),indentSelection:methodOp((function(how){var ranges=this.doc.sel.ranges,end=-1;for(var i=0;i<ranges.length;i++){var range=ranges[i];if(!range.empty()){var from=range.from(),to=range.to();var start=Math.max(end,from.line);end=Math.min(this.lastLine(),to.line-(to.ch?0:1))+1;for(var j=start;j<end;++j){indentLine(this,j,how)}var newRanges=this.doc.sel.ranges;if(from.ch==0&&ranges.length==newRanges.length&&newRanges[i].from().ch>0){replaceOneSelection(this.doc,i,new Range(from,newRanges[i].to()),sel_dontScroll)}}else if(range.head.line>end){indentLine(this,range.head.line,how,true);end=range.head.line;if(i==this.doc.sel.primIndex){ensureCursorVisible(this)}}}})),getTokenAt:function(pos,precise){return takeToken(this,pos,precise)},getLineTokens:function(line,precise){return takeToken(this,Pos(line),precise,true)},getTokenTypeAt:function(pos){pos=clipPos(this.doc,pos);var styles=getLineStyles(this,getLine(this.doc,pos.line));var before=0,after=(styles.length-1)/2,ch=pos.ch;var type;if(ch==0){type=styles[2]}else{for(;;){var mid=before+after>>1;if((mid?styles[mid*2-1]:0)>=ch){after=mid}else if(styles[mid*2+1]<ch){before=mid+1}else{type=styles[mid*2+2];break}}}var cut=type?type.indexOf("overlay "):-1;return cut<0?type:cut==0?null:type.slice(0,cut-1)},getModeAt:function(pos){var mode=this.doc.mode;if(!mode.innerMode){return mode}return CodeMirror.innerMode(mode,this.getTokenAt(pos).state).mode},getHelper:function(pos,type){return this.getHelpers(pos,type)[0]},getHelpers:function(pos,type){var found=[];if(!helpers.hasOwnProperty(type)){return found}var help=helpers[type],mode=this.getModeAt(pos);if(typeof mode[type]=="string"){if(help[mode[type]]){found.push(help[mode[type]])}}else if(mode[type]){for(var i=0;i<mode[type].length;i++){var val=help[mode[type][i]];if(val){found.push(val)}}}else if(mode.helperType&&help[mode.helperType]){found.push(help[mode.helperType])}else if(help[mode.name]){found.push(help[mode.name])}for(var i$1=0;i$1<help._global.length;i$1++){var cur=help._global[i$1];if(cur.pred(mode,this)&&indexOf(found,cur.val)==-1){found.push(cur.val)}}return found},getStateAfter:function(line,precise){var doc=this.doc;line=clipLine(doc,line==null?doc.first+doc.size-1:line);return getContextBefore(this,line+1,precise).state},cursorCoords:function(start,mode){var pos,range=this.doc.sel.primary();if(start==null){pos=range.head}else if(typeof start=="object"){pos=clipPos(this.doc,start)}else{pos=start?range.from():range.to()}return cursorCoords(this,pos,mode||"page")},charCoords:function(pos,mode){return charCoords(this,clipPos(this.doc,pos),mode||"page")},coordsChar:function(coords,mode){coords=fromCoordSystem(this,coords,mode||"page");return coordsChar(this,coords.left,coords.top)},lineAtHeight:function(height,mode){height=fromCoordSystem(this,{top:height,left:0},mode||"page").top;return lineAtHeight(this.doc,height+this.display.viewOffset)},heightAtLine:function(line,mode,includeWidgets){var end=false,lineObj;if(typeof line=="number"){var last=this.doc.first+this.doc.size-1;if(line<this.doc.first){line=this.doc.first}else if(line>last){line=last;end=true}lineObj=getLine(this.doc,line)}else{lineObj=line}return intoCoordSystem(this,lineObj,{top:0,left:0},mode||"page",includeWidgets||end).top+(end?this.doc.height-heightAtLine(lineObj):0)},defaultTextHeight:function(){return textHeight(this.display)},defaultCharWidth:function(){return charWidth(this.display)},getViewport:function(){return{from:this.display.viewFrom,to:this.display.viewTo}},addWidget:function(pos,node,scroll,vert,horiz){var display=this.display;pos=cursorCoords(this,clipPos(this.doc,pos));var top=pos.bottom,left=pos.left;node.style.position="absolute";node.setAttribute("cm-ignore-events","true");this.display.input.setUneditable(node);display.sizer.appendChild(node);if(vert=="over"){top=pos.top}else if(vert=="above"||vert=="near"){var vspace=Math.max(display.wrapper.clientHeight,this.doc.height),hspace=Math.max(display.sizer.clientWidth,display.lineSpace.clientWidth);if((vert=="above"||pos.bottom+node.offsetHeight>vspace)&&pos.top>node.offsetHeight){top=pos.top-node.offsetHeight}else if(pos.bottom+node.offsetHeight<=vspace){top=pos.bottom}if(left+node.offsetWidth>hspace){left=hspace-node.offsetWidth}}node.style.top=top+"px";node.style.left=node.style.right="";if(horiz=="right"){left=display.sizer.clientWidth-node.offsetWidth;node.style.right="0px"}else{if(horiz=="left"){left=0}else if(horiz=="middle"){left=(display.sizer.clientWidth-node.offsetWidth)/2}node.style.left=left+"px"}if(scroll){scrollIntoView(this,{left:left,top:top,right:left+node.offsetWidth,bottom:top+node.offsetHeight})}},triggerOnKeyDown:methodOp(onKeyDown),triggerOnKeyPress:methodOp(onKeyPress),triggerOnKeyUp:onKeyUp,triggerOnMouseDown:methodOp(onMouseDown),execCommand:function(cmd){if(commands.hasOwnProperty(cmd)){return commands[cmd].call(null,this)}},triggerElectric:methodOp((function(text){triggerElectric(this,text)})),findPosH:function(from,amount,unit,visually){var dir=1;if(amount<0){dir=-1;amount=-amount}var cur=clipPos(this.doc,from);for(var i=0;i<amount;++i){cur=findPosH(this.doc,cur,dir,unit,visually);if(cur.hitSide){break}}return cur},moveH:methodOp((function(dir,unit){var this$1=this;this.extendSelectionsBy((function(range){if(this$1.display.shift||this$1.doc.extend||range.empty()){return findPosH(this$1.doc,range.head,dir,unit,this$1.options.rtlMoveVisually)}else{return dir<0?range.from():range.to()}}),sel_move)})),deleteH:methodOp((function(dir,unit){var sel=this.doc.sel,doc=this.doc;if(sel.somethingSelected()){doc.replaceSelection("",null,"+delete")}else{deleteNearSelection(this,(function(range){var other=findPosH(doc,range.head,dir,unit,false);return dir<0?{from:other,to:range.head}:{from:range.head,to:other}}))}})),findPosV:function(from,amount,unit,goalColumn){var dir=1,x=goalColumn;if(amount<0){dir=-1;amount=-amount}var cur=clipPos(this.doc,from);for(var i=0;i<amount;++i){var coords=cursorCoords(this,cur,"div");if(x==null){x=coords.left}else{coords.left=x}cur=findPosV(this,coords,dir,unit);if(cur.hitSide){break}}return cur},moveV:methodOp((function(dir,unit){var this$1=this;var doc=this.doc,goals=[];var collapse=!this.display.shift&&!doc.extend&&doc.sel.somethingSelected();doc.extendSelectionsBy((function(range){if(collapse){return dir<0?range.from():range.to()}var headPos=cursorCoords(this$1,range.head,"div");if(range.goalColumn!=null){headPos.left=range.goalColumn}goals.push(headPos.left);var pos=findPosV(this$1,headPos,dir,unit);if(unit=="page"&&range==doc.sel.primary()){addToScrollTop(this$1,charCoords(this$1,pos,"div").top-headPos.top)}return pos}),sel_move);if(goals.length){for(var i=0;i<doc.sel.ranges.length;i++){doc.sel.ranges[i].goalColumn=goals[i]}}})),findWordAt:function(pos){var doc=this.doc,line=getLine(doc,pos.line).text;var start=pos.ch,end=pos.ch;if(line){var helper=this.getHelper(pos,"wordChars");if((pos.sticky=="before"||end==line.length)&&start){--start}else{++end}var startChar=line.charAt(start);var check=isWordChar(startChar,helper)?function(ch){return isWordChar(ch,helper)}:/\s/.test(startChar)?function(ch){return/\s/.test(ch)}:function(ch){return!/\s/.test(ch)&&!isWordChar(ch)};while(start>0&&check(line.charAt(start-1))){--start}while(end<line.length&&check(line.charAt(end))){++end}}return new Range(Pos(pos.line,start),Pos(pos.line,end))},toggleOverwrite:function(value){if(value!=null&&value==this.state.overwrite){return}if(this.state.overwrite=!this.state.overwrite){addClass(this.display.cursorDiv,"CodeMirror-overwrite")}else{rmClass(this.display.cursorDiv,"CodeMirror-overwrite")}signal(this,"overwriteToggle",this,this.state.overwrite)},hasFocus:function(){return this.display.input.getField()==activeElt(doc(this))},isReadOnly:function(){return!!(this.options.readOnly||this.doc.cantEdit)},scrollTo:methodOp((function(x,y){scrollToCoords(this,x,y)})),getScrollInfo:function(){var scroller=this.display.scroller;return{left:scroller.scrollLeft,top:scroller.scrollTop,height:scroller.scrollHeight-scrollGap(this)-this.display.barHeight,width:scroller.scrollWidth-scrollGap(this)-this.display.barWidth,clientHeight:displayHeight(this),clientWidth:displayWidth(this)}},scrollIntoView:methodOp((function(range,margin){if(range==null){range={from:this.doc.sel.primary().head,to:null};if(margin==null){margin=this.options.cursorScrollMargin}}else if(typeof range=="number"){range={from:Pos(range,0),to:null}}else if(range.from==null){range={from:range,to:null}}if(!range.to){range.to=range.from}range.margin=margin||0;if(range.from.line!=null){scrollToRange(this,range)}else{scrollToCoordsRange(this,range.from,range.to,range.margin)}})),setSize:methodOp((function(width,height){var this$1=this;var interpret=function(val){return typeof val=="number"||/^\d+$/.test(String(val))?val+"px":val};if(width!=null){this.display.wrapper.style.width=interpret(width)}if(height!=null){this.display.wrapper.style.height=interpret(height)}if(this.options.lineWrapping){clearLineMeasurementCache(this)}var lineNo=this.display.viewFrom;this.doc.iter(lineNo,this.display.viewTo,(function(line){if(line.widgets){for(var i=0;i<line.widgets.length;i++){if(line.widgets[i].noHScroll){regLineChange(this$1,lineNo,"widget");break}}}++lineNo}));this.curOp.forceUpdate=true;signal(this,"refresh",this)})),operation:function(f){return runInOp(this,f)},startOperation:function(){return startOperation(this)},endOperation:function(){return endOperation(this)},refresh:methodOp((function(){var oldHeight=this.display.cachedTextHeight;regChange(this);this.curOp.forceUpdate=true;clearCaches(this);scrollToCoords(this,this.doc.scrollLeft,this.doc.scrollTop);updateGutterSpace(this.display);if(oldHeight==null||Math.abs(oldHeight-textHeight(this.display))>.5||this.options.lineWrapping){estimateLineHeights(this)}signal(this,"refresh",this)})),swapDoc:methodOp((function(doc){var old=this.doc;old.cm=null;if(this.state.selectingText){this.state.selectingText()}attachDoc(this,doc);clearCaches(this);this.display.input.reset();scrollToCoords(this,doc.scrollLeft,doc.scrollTop);this.curOp.forceScroll=true;signalLater(this,"swapDoc",this,old);return old})),phrase:function(phraseText){var phrases=this.options.phrases;return phrases&&Object.prototype.hasOwnProperty.call(phrases,phraseText)?phrases[phraseText]:phraseText},getInputField:function(){return this.display.input.getField()},getWrapperElement:function(){return this.display.wrapper},getScrollerElement:function(){return this.display.scroller},getGutterElement:function(){return this.display.gutters}};eventMixin(CodeMirror);CodeMirror.registerHelper=function(type,name,value){if(!helpers.hasOwnProperty(type)){helpers[type]=CodeMirror[type]={_global:[]}}helpers[type][name]=value};CodeMirror.registerGlobalHelper=function(type,name,predicate,value){CodeMirror.registerHelper(type,name,value);helpers[type]._global.push({pred:predicate,val:value})}}function findPosH(doc,pos,dir,unit,visually){var oldPos=pos;var origDir=dir;var lineObj=getLine(doc,pos.line);var lineDir=visually&&doc.direction=="rtl"?-dir:dir;function findNextLine(){var l=pos.line+lineDir;if(l<doc.first||l>=doc.first+doc.size){return false}pos=new Pos(l,pos.ch,pos.sticky);return lineObj=getLine(doc,l)}function moveOnce(boundToLine){var next;if(unit=="codepoint"){var ch=lineObj.text.charCodeAt(pos.ch+(dir>0?0:-1));if(isNaN(ch)){next=null}else{var astral=dir>0?ch>=55296&&ch<56320:ch>=56320&&ch<57343;next=new Pos(pos.line,Math.max(0,Math.min(lineObj.text.length,pos.ch+dir*(astral?2:1))),-dir)}}else if(visually){next=moveVisually(doc.cm,lineObj,pos,dir)}else{next=moveLogically(lineObj,pos,dir)}if(next==null){if(!boundToLine&&findNextLine()){pos=endOfLine(visually,doc.cm,lineObj,pos.line,lineDir)}else{return false}}else{pos=next}return true}if(unit=="char"||unit=="codepoint"){moveOnce()}else if(unit=="column"){moveOnce(true)}else if(unit=="word"||unit=="group"){var sawType=null,group=unit=="group";var helper=doc.cm&&doc.cm.getHelper(pos,"wordChars");for(var first=true;;first=false){if(dir<0&&!moveOnce(!first)){break}var cur=lineObj.text.charAt(pos.ch)||"\n";var type=isWordChar(cur,helper)?"w":group&&cur=="\n"?"n":!group||/\s/.test(cur)?null:"p";if(group&&!first&&!type){type="s"}if(sawType&&sawType!=type){if(dir<0){dir=1;moveOnce();pos.sticky="after"}break}if(type){sawType=type}if(dir>0&&!moveOnce(!first)){break}}}var result=skipAtomic(doc,pos,oldPos,origDir,true);if(equalCursorPos(oldPos,result)){result.hitSide=true}return result}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,win(cm).innerHeight||doc(cm).documentElement.clientHeight);var moveAmount=Math.max(pageSize-.5*textHeight(cm.display),3);y=(dir>0?pos.bottom:pos.top)+dir*moveAmount}else if(unit=="line"){y=dir>0?pos.bottom+3:pos.top-3}var target;for(;;){target=coordsChar(cm,x,y);if(!target.outside){break}if(dir<0?y<=0:y>=doc.height){target.hitSide=true;break}y+=dir*5}return target}var ContentEditableInput=function(cm){this.cm=cm;this.lastAnchorNode=this.lastAnchorOffset=this.lastFocusNode=this.lastFocusOffset=null;this.polling=new Delayed;this.composing=null;this.gracePeriod=false;this.readDOMTimeout=null};ContentEditableInput.prototype.init=function(display){var this$1=this;var input=this,cm=input.cm;var div=input.div=display.lineDiv;div.contentEditable=true;disableBrowserMagic(div,cm.options.spellcheck,cm.options.autocorrect,cm.options.autocapitalize);function belongsToInput(e){for(var t=e.target;t;t=t.parentNode){if(t==div){return true}if(/\bCodeMirror-(?:line)?widget\b/.test(t.className)){break}}return false}on(div,"paste",(function(e){if(!belongsToInput(e)||signalDOMEvent(cm,e)||handlePaste(e,cm)){return}if(ie_version<=11){setTimeout(operation(cm,(function(){return this$1.updateFromDOM()})),20)}}));on(div,"compositionstart",(function(e){this$1.composing={data:e.data,done:false}}));on(div,"compositionupdate",(function(e){if(!this$1.composing){this$1.composing={data:e.data,done:false}}}));on(div,"compositionend",(function(e){if(this$1.composing){if(e.data!=this$1.composing.data){this$1.readFromDOMSoon()}this$1.composing.done=true}}));on(div,"touchstart",(function(){return input.forceCompositionEnd()}));on(div,"input",(function(){if(!this$1.composing){this$1.readFromDOMSoon()}}));function onCopyCut(e){if(!belongsToInput(e)||signalDOMEvent(cm,e)){return}if(cm.somethingSelected()){setLastCopied({lineWise:false,text:cm.getSelections()});if(e.type=="cut"){cm.replaceSelection("",null,"cut")}}else if(!cm.options.lineWiseCopyCut){return}else{var ranges=copyableRanges(cm);setLastCopied({lineWise:true,text:ranges.text});if(e.type=="cut"){cm.operation((function(){cm.setSelections(ranges.ranges,0,sel_dontScroll);cm.replaceSelection("",null,"cut")}))}}if(e.clipboardData){e.clipboardData.clearData();var content=lastCopied.text.join("\n");e.clipboardData.setData("Text",content);if(e.clipboardData.getData("Text")==content){e.preventDefault();return}}var kludge=hiddenTextarea(),te=kludge.firstChild;disableBrowserMagic(te);cm.display.lineSpace.insertBefore(kludge,cm.display.lineSpace.firstChild);te.value=lastCopied.text.join("\n");var hadFocus=activeElt(div.ownerDocument);selectInput(te);setTimeout((function(){cm.display.lineSpace.removeChild(kludge);hadFocus.focus();if(hadFocus==div){input.showPrimarySelection()}}),50)}on(div,"copy",onCopyCut);on(div,"cut",onCopyCut)};ContentEditableInput.prototype.screenReaderLabelChanged=function(label){if(label){this.div.setAttribute("aria-label",label)}else{this.div.removeAttribute("aria-label")}};ContentEditableInput.prototype.prepareSelection=function(){var result=prepareSelection(this.cm,false);result.focus=activeElt(this.div.ownerDocument)==this.div;return result};ContentEditableInput.prototype.showSelection=function(info,takeFocus){if(!info||!this.cm.display.view.length){return}if(info.focus||takeFocus){this.showPrimarySelection()}this.showMultipleSelections(info)};ContentEditableInput.prototype.getSelection=function(){return this.cm.display.wrapper.ownerDocument.getSelection()};ContentEditableInput.prototype.showPrimarySelection=function(){var sel=this.getSelection(),cm=this.cm,prim=cm.doc.sel.primary();var from=prim.from(),to=prim.to();if(cm.display.viewTo==cm.display.viewFrom||from.line>=cm.display.viewTo||to.line<cm.display.viewFrom){sel.removeAllRanges();return}var curAnchor=domToPos(cm,sel.anchorNode,sel.anchorOffset);var curFocus=domToPos(cm,sel.focusNode,sel.focusOffset);if(curAnchor&&!curAnchor.bad&&curFocus&&!curFocus.bad&&cmp(minPos(curAnchor,curFocus),from)==0&&cmp(maxPos(curAnchor,curFocus),to)==0){return}var view=cm.display.view;var start=from.line>=cm.display.viewFrom&&posToDOM(cm,from)||{node:view[0].measure.map[2],offset:0};var end=to.line<cm.display.viewTo&&posToDOM(cm,to);if(!end){var measure=view[view.length-1].measure;var map=measure.maps?measure.maps[measure.maps.length-1]:measure.map;end={node:map[map.length-1],offset:map[map.length-2]-map[map.length-3]}}if(!start||!end){sel.removeAllRanges();return}var old=sel.rangeCount&&sel.getRangeAt(0),rng;try{rng=range(start.node,start.offset,end.offset,end.node)}catch(e){}if(rng){if(!gecko&&cm.state.focused){sel.collapse(start.node,start.offset);if(!rng.collapsed){sel.removeAllRanges();sel.addRange(rng)}}else{sel.removeAllRanges();sel.addRange(rng)}if(old&&sel.anchorNode==null){sel.addRange(old)}else if(gecko){this.startGracePeriod()}}this.rememberSelection()};ContentEditableInput.prototype.startGracePeriod=function(){var this$1=this;clearTimeout(this.gracePeriod);this.gracePeriod=setTimeout((function(){this$1.gracePeriod=false;if(this$1.selectionChanged()){this$1.cm.operation((function(){return this$1.cm.curOp.selectionChanged=true}))}}),20)};ContentEditableInput.prototype.showMultipleSelections=function(info){removeChildrenAndAdd(this.cm.display.cursorDiv,info.cursors);removeChildrenAndAdd(this.cm.display.selectionDiv,info.selection)};ContentEditableInput.prototype.rememberSelection=function(){var sel=this.getSelection();this.lastAnchorNode=sel.anchorNode;this.lastAnchorOffset=sel.anchorOffset;this.lastFocusNode=sel.focusNode;this.lastFocusOffset=sel.focusOffset};ContentEditableInput.prototype.selectionInEditor=function(){var sel=this.getSelection();if(!sel.rangeCount){return false}var node=sel.getRangeAt(0).commonAncestorContainer;return contains(this.div,node)};ContentEditableInput.prototype.focus=function(){if(this.cm.options.readOnly!="nocursor"){if(!this.selectionInEditor()||activeElt(this.div.ownerDocument)!=this.div){this.showSelection(this.prepareSelection(),true)}this.div.focus()}};ContentEditableInput.prototype.blur=function(){this.div.blur()};ContentEditableInput.prototype.getField=function(){return this.div};ContentEditableInput.prototype.supportsTouch=function(){return true};ContentEditableInput.prototype.receivedFocus=function(){var this$1=this;var input=this;if(this.selectionInEditor()){setTimeout((function(){return this$1.pollSelection()}),20)}else{runInOp(this.cm,(function(){return input.cm.curOp.selectionChanged=true}))}function poll(){if(input.cm.state.focused){input.pollSelection();input.polling.set(input.cm.options.pollInterval,poll)}}this.polling.set(this.cm.options.pollInterval,poll)};ContentEditableInput.prototype.selectionChanged=function(){var sel=this.getSelection();return sel.anchorNode!=this.lastAnchorNode||sel.anchorOffset!=this.lastAnchorOffset||sel.focusNode!=this.lastFocusNode||sel.focusOffset!=this.lastFocusOffset};ContentEditableInput.prototype.pollSelection=function(){if(this.readDOMTimeout!=null||this.gracePeriod||!this.selectionChanged()){return}var sel=this.getSelection(),cm=this.cm;if(android&&chrome&&this.cm.display.gutterSpecs.length&&isInGutter(sel.anchorNode)){this.cm.triggerOnKeyDown({type:"keydown",keyCode:8,preventDefault:Math.abs});this.blur();this.focus();return}if(this.composing){return}this.rememberSelection();var anchor=domToPos(cm,sel.anchorNode,sel.anchorOffset);var head=domToPos(cm,sel.focusNode,sel.focusOffset);if(anchor&&head){runInOp(cm,(function(){setSelection(cm.doc,simpleSelection(anchor,head),sel_dontScroll);if(anchor.bad||head.bad){cm.curOp.selectionChanged=true}}))}};ContentEditableInput.prototype.pollContent=function(){if(this.readDOMTimeout!=null){clearTimeout(this.readDOMTimeout);this.readDOMTimeout=null}var cm=this.cm,display=cm.display,sel=cm.doc.sel.primary();var from=sel.from(),to=sel.to();if(from.ch==0&&from.line>cm.firstLine()){from=Pos(from.line-1,getLine(cm.doc,from.line-1).length)}if(to.ch==getLine(cm.doc,to.line).text.length&&to.line<cm.lastLine()){to=Pos(to.line+1,0)}if(from.line<display.viewFrom||to.line>display.viewTo-1){return false}var fromIndex,fromLine,fromNode;if(from.line==display.viewFrom||(fromIndex=findViewIndex(cm,from.line))==0){fromLine=lineNo(display.view[0].line);fromNode=display.view[0].node}else{fromLine=lineNo(display.view[fromIndex].line);fromNode=display.view[fromIndex-1].node.nextSibling}var toIndex=findViewIndex(cm,to.line);var toLine,toNode;if(toIndex==display.view.length-1){toLine=display.viewTo-1;toNode=display.lineDiv.lastChild}else{toLine=lineNo(display.view[toIndex+1].line)-1;toNode=display.view[toIndex+1].node.previousSibling}if(!fromNode){return false}var newText=cm.doc.splitLines(domTextBetween(cm,fromNode,toNode,fromLine,toLine));var oldText=getBetween(cm.doc,Pos(fromLine,0),Pos(toLine,getLine(cm.doc,toLine).text.length));while(newText.length>1&&oldText.length>1){if(lst(newText)==lst(oldText)){newText.pop();oldText.pop();toLine--}else if(newText[0]==oldText[0]){newText.shift();oldText.shift();fromLine++}else{break}}var cutFront=0,cutEnd=0;var newTop=newText[0],oldTop=oldText[0],maxCutFront=Math.min(newTop.length,oldTop.length);while(cutFront<maxCutFront&&newTop.charCodeAt(cutFront)==oldTop.charCodeAt(cutFront)){++cutFront}var newBot=lst(newText),oldBot=lst(oldText);var maxCutEnd=Math.min(newBot.length-(newText.length==1?cutFront:0),oldBot.length-(oldText.length==1?cutFront:0));while(cutEnd<maxCutEnd&&newBot.charCodeAt(newBot.length-cutEnd-1)==oldBot.charCodeAt(oldBot.length-cutEnd-1)){++cutEnd}if(newText.length==1&&oldText.length==1&&fromLine==from.line){while(cutFront&&cutFront>from.ch&&newBot.charCodeAt(newBot.length-cutEnd-1)==oldBot.charCodeAt(oldBot.length-cutEnd-1)){cutFront--;cutEnd++}}newText[newText.length-1]=newBot.slice(0,newBot.length-cutEnd).replace(/^\u200b+/,"");newText[0]=newText[0].slice(cutFront).replace(/\u200b+$/,"");var chFrom=Pos(fromLine,cutFront);var chTo=Pos(toLine,oldText.length?lst(oldText).length-cutEnd:0);if(newText.length>1||newText[0]||cmp(chFrom,chTo)){replaceRange(cm.doc,newText,chFrom,chTo,"+input");return true}};ContentEditableInput.prototype.ensurePolled=function(){this.forceCompositionEnd()};ContentEditableInput.prototype.reset=function(){this.forceCompositionEnd()};ContentEditableInput.prototype.forceCompositionEnd=function(){if(!this.composing){return}clearTimeout(this.readDOMTimeout);this.composing=null;this.updateFromDOM();this.div.blur();this.div.focus()};ContentEditableInput.prototype.readFromDOMSoon=function(){var this$1=this;if(this.readDOMTimeout!=null){return}this.readDOMTimeout=setTimeout((function(){this$1.readDOMTimeout=null;if(this$1.composing){if(this$1.composing.done){this$1.composing=null}else{return}}this$1.updateFromDOM()}),80)};ContentEditableInput.prototype.updateFromDOM=function(){var this$1=this;if(this.cm.isReadOnly()||!this.pollContent()){runInOp(this.cm,(function(){return regChange(this$1.cm)}))}};ContentEditableInput.prototype.setUneditable=function(node){node.contentEditable="false"};ContentEditableInput.prototype.onKeyPress=function(e){if(e.charCode==0||this.composing){return}e.preventDefault();if(!this.cm.isReadOnly()){operation(this.cm,applyTextInput)(this.cm,String.fromCharCode(e.charCode==null?e.keyCode:e.charCode),0)}};ContentEditableInput.prototype.readOnlyChanged=function(val){this.div.contentEditable=String(val!="nocursor")};ContentEditableInput.prototype.onContextMenu=function(){};ContentEditableInput.prototype.resetPosition=function(){};ContentEditableInput.prototype.needsContentAttribute=true;function posToDOM(cm,pos){var view=findViewForLine(cm,pos.line);if(!view||view.hidden){return null}var line=getLine(cm.doc,pos.line);var info=mapFromLineView(view,line,pos.line);var order=getOrder(line,cm.doc.direction),side="left";if(order){var partPos=getBidiPartAt(order,pos.ch);side=partPos%2?"right":"left"}var result=nodeAndOffsetInLineMap(info.map,pos.ch,side);result.offset=result.collapse=="right"?result.end:result.start;return result}function isInGutter(node){for(var scan=node;scan;scan=scan.parentNode){if(/CodeMirror-gutter-wrapper/.test(scan.className)){return true}}return false}function badPos(pos,bad){if(bad){pos.bad=true}return pos}function domTextBetween(cm,from,to,fromLine,toLine){var text="",closing=false,lineSep=cm.doc.lineSeparator(),extraLinebreak=false;function recognizeMarker(id){return function(marker){return marker.id==id}}function close(){if(closing){text+=lineSep;if(extraLinebreak){text+=lineSep}closing=extraLinebreak=false}}function addText(str){if(str){close();text+=str}}function walk(node){if(node.nodeType==1){var cmText=node.getAttribute("cm-text");if(cmText){addText(cmText);return}var markerID=node.getAttribute("cm-marker"),range;if(markerID){var found=cm.findMarks(Pos(fromLine,0),Pos(toLine+1,0),recognizeMarker(+markerID));if(found.length&&(range=found[0].find(0))){addText(getBetween(cm.doc,range.from,range.to).join(lineSep))}return}if(node.getAttribute("contenteditable")=="false"){return}var isBlock=/^(pre|div|p|li|table|br)$/i.test(node.nodeName);if(!/^br$/i.test(node.nodeName)&&node.textContent.length==0){return}if(isBlock){close()}for(var i=0;i<node.childNodes.length;i++){walk(node.childNodes[i])}if(/^(pre|p)$/i.test(node.nodeName)){extraLinebreak=true}if(isBlock){closing=true}}else if(node.nodeType==3){addText(node.nodeValue.replace(/\u200b/g,"").replace(/\u00a0/g," "))}}for(;;){walk(from);if(from==to){break}from=from.nextSibling;extraLinebreak=false}return text}function domToPos(cm,node,offset){var lineNode;if(node==cm.display.lineDiv){lineNode=cm.display.lineDiv.childNodes[offset];if(!lineNode){return badPos(cm.clipPos(Pos(cm.display.viewTo-1)),true)}node=null;offset=0}else{for(lineNode=node;;lineNode=lineNode.parentNode){if(!lineNode||lineNode==cm.display.lineDiv){return null}if(lineNode.parentNode&&lineNode.parentNode==cm.display.lineDiv){break}}}for(var i=0;i<cm.display.view.length;i++){var lineView=cm.display.view[i];if(lineView.node==lineNode){return locateNodeInLineView(lineView,node,offset)}}}function locateNodeInLineView(lineView,node,offset){var wrapper=lineView.text.firstChild,bad=false;if(!node||!contains(wrapper,node)){return badPos(Pos(lineNo(lineView.line),0),true)}if(node==wrapper){bad=true;node=wrapper.childNodes[offset];offset=0;if(!node){var line=lineView.rest?lst(lineView.rest):lineView.line;return badPos(Pos(lineNo(line),line.text.length),bad)}}var textNode=node.nodeType==3?node:null,topNode=node;if(!textNode&&node.childNodes.length==1&&node.firstChild.nodeType==3){textNode=node.firstChild;if(offset){offset=textNode.nodeValue.length}}while(topNode.parentNode!=wrapper){topNode=topNode.parentNode}var measure=lineView.measure,maps=measure.maps;function find(textNode,topNode,offset){for(var i=-1;i<(maps?maps.length:0);i++){var map=i<0?measure.map:maps[i];for(var j=0;j<map.length;j+=3){var curNode=map[j+2];if(curNode==textNode||curNode==topNode){var line=lineNo(i<0?lineView.line:lineView.rest[i]);var ch=map[j]+offset;if(offset<0||curNode!=textNode){ch=map[j+(offset?1:0)]}return Pos(line,ch)}}}}var found=find(textNode,topNode,offset);if(found){return badPos(found,bad)}for(var after=topNode.nextSibling,dist=textNode?textNode.nodeValue.length-offset:0;after;after=after.nextSibling){found=find(after,after.firstChild,0);if(found){return badPos(Pos(found.line,found.ch-dist),bad)}else{dist+=after.textContent.length}}for(var before=topNode.previousSibling,dist$1=offset;before;before=before.previousSibling){found=find(before,before.firstChild,-1);if(found){return badPos(Pos(found.line,found.ch+dist$1),bad)}else{dist$1+=before.textContent.length}}}var TextareaInput=function(cm){this.cm=cm;this.prevInput="";this.pollingFast=false;this.polling=new Delayed;this.hasSelection=false;this.composing=null;this.resetting=false};TextareaInput.prototype.init=function(display){var this$1=this;var input=this,cm=this.cm;this.createField(display);var te=this.textarea;display.wrapper.insertBefore(this.wrapper,display.wrapper.firstChild);if(ios){te.style.width="0px"}on(te,"input",(function(){if(ie&&ie_version>=9&&this$1.hasSelection){this$1.hasSelection=null}input.poll()}));on(te,"paste",(function(e){if(signalDOMEvent(cm,e)||handlePaste(e,cm)){return}cm.state.pasteIncoming=+new Date;input.fastPoll()}));function prepareCopyCut(e){if(signalDOMEvent(cm,e)){return}if(cm.somethingSelected()){setLastCopied({lineWise:false,text:cm.getSelections()})}else if(!cm.options.lineWiseCopyCut){return}else{var ranges=copyableRanges(cm);setLastCopied({lineWise:true,text:ranges.text});if(e.type=="cut"){cm.setSelections(ranges.ranges,null,sel_dontScroll)}else{input.prevInput="";te.value=ranges.text.join("\n");selectInput(te)}}if(e.type=="cut"){cm.state.cutIncoming=+new Date}}on(te,"cut",prepareCopyCut);on(te,"copy",prepareCopyCut);on(display.scroller,"paste",(function(e){if(eventInWidget(display,e)||signalDOMEvent(cm,e)){return}if(!te.dispatchEvent){cm.state.pasteIncoming=+new Date;input.focus();return}var event=new Event("paste");event.clipboardData=e.clipboardData;te.dispatchEvent(event)}));on(display.lineSpace,"selectstart",(function(e){if(!eventInWidget(display,e)){e_preventDefault(e)}}));on(te,"compositionstart",(function(){var start=cm.getCursor("from");if(input.composing){input.composing.range.clear()}input.composing={start:start,range:cm.markText(start,cm.getCursor("to"),{className:"CodeMirror-composing"})}}));on(te,"compositionend",(function(){if(input.composing){input.poll();input.composing.range.clear();input.composing=null}}))};TextareaInput.prototype.createField=function(_display){this.wrapper=hiddenTextarea();this.textarea=this.wrapper.firstChild;var opts=this.cm.options;disableBrowserMagic(this.textarea,opts.spellcheck,opts.autocorrect,opts.autocapitalize)};TextareaInput.prototype.screenReaderLabelChanged=function(label){if(label){this.textarea.setAttribute("aria-label",label)}else{this.textarea.removeAttribute("aria-label")}};TextareaInput.prototype.prepareSelection=function(){var cm=this.cm,display=cm.display,doc=cm.doc;var result=prepareSelection(cm);if(cm.options.moveInputWithCursor){var headPos=cursorCoords(cm,doc.sel.primary().head,"div");var wrapOff=display.wrapper.getBoundingClientRect(),lineOff=display.lineDiv.getBoundingClientRect();result.teTop=Math.max(0,Math.min(display.wrapper.clientHeight-10,headPos.top+lineOff.top-wrapOff.top));result.teLeft=Math.max(0,Math.min(display.wrapper.clientWidth-10,headPos.left+lineOff.left-wrapOff.left))}return result};TextareaInput.prototype.showSelection=function(drawn){var cm=this.cm,display=cm.display;removeChildrenAndAdd(display.cursorDiv,drawn.cursors);removeChildrenAndAdd(display.selectionDiv,drawn.selection);if(drawn.teTop!=null){this.wrapper.style.top=drawn.teTop+"px";this.wrapper.style.left=drawn.teLeft+"px"}};TextareaInput.prototype.reset=function(typing){if(this.contextMenuPending||this.composing&&typing){return}var cm=this.cm;this.resetting=true;if(cm.somethingSelected()){this.prevInput="";var content=cm.getSelection();this.textarea.value=content;if(cm.state.focused){selectInput(this.textarea)}if(ie&&ie_version>=9){this.hasSelection=content}}else if(!typing){this.prevInput=this.textarea.value="";if(ie&&ie_version>=9){this.hasSelection=null}}this.resetting=false};TextareaInput.prototype.getField=function(){return this.textarea};TextareaInput.prototype.supportsTouch=function(){return false};TextareaInput.prototype.focus=function(){if(this.cm.options.readOnly!="nocursor"&&(!mobile||activeElt(this.textarea.ownerDocument)!=this.textarea)){try{this.textarea.focus()}catch(e){}}};TextareaInput.prototype.blur=function(){this.textarea.blur()};TextareaInput.prototype.resetPosition=function(){this.wrapper.style.top=this.wrapper.style.left=0};TextareaInput.prototype.receivedFocus=function(){this.slowPoll()};TextareaInput.prototype.slowPoll=function(){var this$1=this;if(this.pollingFast){return}this.polling.set(this.cm.options.pollInterval,(function(){this$1.poll();if(this$1.cm.state.focused){this$1.slowPoll()}}))};TextareaInput.prototype.fastPoll=function(){var missed=false,input=this;input.pollingFast=true;function p(){var changed=input.poll();if(!changed&&!missed){missed=true;input.polling.set(60,p)}else{input.pollingFast=false;input.slowPoll()}}input.polling.set(20,p)};TextareaInput.prototype.poll=function(){var this$1=this;var cm=this.cm,input=this.textarea,prevInput=this.prevInput;if(this.contextMenuPending||this.resetting||!cm.state.focused||hasSelection(input)&&!prevInput&&!this.composing||cm.isReadOnly()||cm.options.disableInput||cm.state.keySeq){return false}var text=input.value;if(text==prevInput&&!cm.somethingSelected()){return false}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==8203&&!prevInput){prevInput="​"}if(first==8666){this.reset();return this.cm.execCommand("undo")}}var same=0,l=Math.min(prevInput.length,text.length);while(same<l&&prevInput.charCodeAt(same)==text.charCodeAt(same)){++same}runInOp(cm,(function(){applyTextInput(cm,text.slice(same),prevInput.length-same,null,this$1.composing?"*compose":null);if(text.length>1e3||text.indexOf("\n")>-1){input.value=this$1.prevInput=""}else{this$1.prevInput=text}if(this$1.composing){this$1.composing.range.clear();this$1.composing.range=cm.markText(this$1.composing.start,cm.getCursor("to"),{className:"CodeMirror-composing"})}}));return true};TextareaInput.prototype.ensurePolled=function(){if(this.pollingFast&&this.poll()){this.pollingFast=false}};TextareaInput.prototype.onKeyPress=function(){if(ie&&ie_version>=9){this.hasSelection=null}this.fastPoll()};TextareaInput.prototype.onContextMenu=function(e){var input=this,cm=input.cm,display=cm.display,te=input.textarea;if(input.contextMenuPending){input.contextMenuPending()}var pos=posFromMouse(cm,e),scrollPos=display.scroller.scrollTop;if(!pos||presto){return}var reset=cm.options.resetSelectionOnContextMenu;if(reset&&cm.doc.sel.contains(pos)==-1){operation(cm,setSelection)(cm.doc,simpleSelection(pos),sel_dontScroll)}var oldCSS=te.style.cssText,oldWrapperCSS=input.wrapper.style.cssText;var wrapperBox=input.wrapper.offsetParent.getBoundingClientRect();input.wrapper.style.cssText="position: static";te.style.cssText="position: absolute; width: 30px; height: 30px;\n      top: "+(e.clientY-wrapperBox.top-5)+"px; left: "+(e.clientX-wrapperBox.left-5)+"px;\n      z-index: 1000; background: "+(ie?"rgba(255, 255, 255, .05)":"transparent")+";\n      outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";var oldScrollY;if(webkit){oldScrollY=te.ownerDocument.defaultView.scrollY}display.input.focus();if(webkit){te.ownerDocument.defaultView.scrollTo(null,oldScrollY)}display.input.reset();if(!cm.somethingSelected()){te.value=input.prevInput=" "}input.contextMenuPending=rehide;display.selForContextMenu=cm.doc.sel;clearTimeout(display.detectingSelectAll);function prepareSelectAllHack(){if(te.selectionStart!=null){var selected=cm.somethingSelected();var extval="​"+(selected?te.value:"");te.value="⇚";te.value=extval;input.prevInput=selected?"":"​";te.selectionStart=1;te.selectionEnd=extval.length;display.selForContextMenu=cm.doc.sel}}function rehide(){if(input.contextMenuPending!=rehide){return}input.contextMenuPending=false;input.wrapper.style.cssText=oldWrapperCSS;te.style.cssText=oldCSS;if(ie&&ie_version<9){display.scrollbars.setScrollTop(display.scroller.scrollTop=scrollPos)}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=="​"){operation(cm,selectAll)(cm)}else if(i++<10){display.detectingSelectAll=setTimeout(poll,500)}else{display.selForContextMenu=null;display.input.reset()}};display.detectingSelectAll=setTimeout(poll,200)}}if(ie&&ie_version>=9){prepareSelectAllHack()}if(captureRightClick){e_stop(e);var mouseup=function(){off(window,"mouseup",mouseup);setTimeout(rehide,20)};on(window,"mouseup",mouseup)}else{setTimeout(rehide,50)}};TextareaInput.prototype.readOnlyChanged=function(val){if(!val){this.reset()}this.textarea.disabled=val=="nocursor";this.textarea.readOnly=!!val};TextareaInput.prototype.setUneditable=function(){};TextareaInput.prototype.needsContentAttribute=false;function fromTextArea(textarea,options){options=options?copyObj(options):{};options.value=textarea.value;if(!options.tabindex&&textarea.tabIndex){options.tabindex=textarea.tabIndex}if(!options.placeholder&&textarea.placeholder){options.placeholder=textarea.placeholder}if(options.autofocus==null){var hasFocus=activeElt(textarea.ownerDocument);options.autofocus=hasFocus==textarea||textarea.getAttribute("autofocus")!=null&&hasFocus==document.body}function save(){textarea.value=cm.getValue()}var realSubmit;if(textarea.form){on(textarea.form,"submit",save);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;save();textarea.parentNode.removeChild(cm.getWrapperElement());textarea.style.display="";if(textarea.form){off(textarea.form,"submit",save);if(!options.leaveSubmitMethodAlone&&typeof textarea.form.submit=="function"){textarea.form.submit=realSubmit}}}};textarea.style.display="none";var cm=CodeMirror((function(node){return textarea.parentNode.insertBefore(node,textarea.nextSibling)}),options);return cm}function addLegacyProps(CodeMirror){CodeMirror.off=off;CodeMirror.on=on;CodeMirror.wheelEventPixels=wheelEventPixels;CodeMirror.Doc=Doc;CodeMirror.splitLines=splitLinesAuto;CodeMirror.countColumn=countColumn;CodeMirror.findColumn=findColumn;CodeMirror.isWordChar=isWordCharBasic;CodeMirror.Pass=Pass;CodeMirror.signal=signal;CodeMirror.Line=Line;CodeMirror.changeEnd=changeEnd;CodeMirror.scrollbarModel=scrollbarModel;CodeMirror.Pos=Pos;CodeMirror.cmpPos=cmp;CodeMirror.modes=modes;CodeMirror.mimeModes=mimeModes;CodeMirror.resolveMode=resolveMode;CodeMirror.getMode=getMode;CodeMirror.modeExtensions=modeExtensions;CodeMirror.extendMode=extendMode;CodeMirror.copyState=copyState;CodeMirror.startState=startState;CodeMirror.innerMode=innerMode;CodeMirror.commands=commands;CodeMirror.keyMap=keyMap;CodeMirror.keyName=keyName;CodeMirror.isModifierKey=isModifierKey;CodeMirror.lookupKey=lookupKey;CodeMirror.normalizeKeyMap=normalizeKeyMap;CodeMirror.StringStream=StringStream;CodeMirror.SharedTextMarker=SharedTextMarker;CodeMirror.TextMarker=TextMarker;CodeMirror.LineWidget=LineWidget;CodeMirror.e_preventDefault=e_preventDefault;CodeMirror.e_stopPropagation=e_stopPropagation;CodeMirror.e_stop=e_stop;CodeMirror.addClass=addClass;CodeMirror.contains=contains;CodeMirror.rmClass=rmClass;CodeMirror.keyNames=keyNames}defineOptions(CodeMirror);addEditorMethods(CodeMirror);var dontDelegate="iter insert remove copy getEditor constructor".split(" ");for(var prop in Doc.prototype){if(Doc.prototype.hasOwnProperty(prop)&&indexOf(dontDelegate,prop)<0){CodeMirror.prototype[prop]=function(method){return function(){return method.apply(this.doc,arguments)}}(Doc.prototype[prop])}}eventMixin(Doc);CodeMirror.inputStyles={textarea:TextareaInput,contenteditable:ContentEditableInput};CodeMirror.defineMode=function(name){if(!CodeMirror.defaults.mode&&name!="null"){CodeMirror.defaults.mode=name}defineMode.apply(this,arguments)};CodeMirror.defineMIME=defineMIME;CodeMirror.defineMode("null",(function(){return{token:function(stream){return stream.skipToEnd()}}}));CodeMirror.defineMIME("text/plain","null");CodeMirror.defineExtension=function(name,func){CodeMirror.prototype[name]=func};CodeMirror.defineDocExtension=function(name,func){Doc.prototype[name]=func};CodeMirror.fromTextArea=fromTextArea;addLegacyProps(CodeMirror);CodeMirror.version="5.65.15";return CodeMirror}));(function(mod){if(typeof exports=="object"&&typeof module=="object")mod(require("../lib/codemirror"),require("../addon/search/searchcursor"),require("../addon/edit/matchbrackets"));else if(typeof define=="function"&&define.amd)define(["../lib/codemirror","../addon/search/searchcursor","../addon/edit/matchbrackets"],mod);else mod(CodeMirror)})((function(CodeMirror){"use strict";var cmds=CodeMirror.commands;var Pos=CodeMirror.Pos;function findPosSubword(doc,start,dir){if(dir<0&&start.ch==0)return doc.clipPos(Pos(start.line-1));var line=doc.getLine(start.line);if(dir>0&&start.ch>=line.length)return doc.clipPos(Pos(start.line+1,0));var state="start",type,startPos=start.ch;for(var pos=startPos,e=dir<0?0:line.length,i=0;pos!=e;pos+=dir,i++){var next=line.charAt(dir<0?pos-1:pos);var cat=next!="_"&&CodeMirror.isWordChar(next)?"w":"o";if(cat=="w"&&next.toUpperCase()==next)cat="W";if(state=="start"){if(cat!="o"){state="in";type=cat}else startPos=pos+dir}else if(state=="in"){if(type!=cat){if(type=="w"&&cat=="W"&&dir<0)pos--;if(type=="W"&&cat=="w"&&dir>0){if(pos==startPos+1){type="w";continue}else pos--}break}}}return Pos(start.line,pos)}function moveSubword(cm,dir){cm.extendSelectionsBy((function(range){if(cm.display.shift||cm.doc.extend||range.empty())return findPosSubword(cm.doc,range.head,dir);else return dir<0?range.from():range.to()}))}cmds.goSubwordLeft=function(cm){moveSubword(cm,-1)};cmds.goSubwordRight=function(cm){moveSubword(cm,1)};cmds.scrollLineUp=function(cm){var info=cm.getScrollInfo();if(!cm.somethingSelected()){var visibleBottomLine=cm.lineAtHeight(info.top+info.clientHeight,"local");if(cm.getCursor().line>=visibleBottomLine)cm.execCommand("goLineUp")}cm.scrollTo(null,info.top-cm.defaultTextHeight())};cmds.scrollLineDown=function(cm){var info=cm.getScrollInfo();if(!cm.somethingSelected()){var visibleTopLine=cm.lineAtHeight(info.top,"local")+1;if(cm.getCursor().line<=visibleTopLine)cm.execCommand("goLineDown")}cm.scrollTo(null,info.top+cm.defaultTextHeight())};cmds.splitSelectionByLine=function(cm){var ranges=cm.listSelections(),lineRanges=[];for(var i=0;i<ranges.length;i++){var from=ranges[i].from(),to=ranges[i].to();for(var line=from.line;line<=to.line;++line)if(!(to.line>from.line&&line==to.line&&to.ch==0))lineRanges.push({anchor:line==from.line?from:Pos(line,0),head:line==to.line?to:Pos(line)})}cm.setSelections(lineRanges,0)};cmds.singleSelectionTop=function(cm){var range=cm.listSelections()[0];cm.setSelection(range.anchor,range.head,{scroll:false})};cmds.selectLine=function(cm){var ranges=cm.listSelections(),extended=[];for(var i=0;i<ranges.length;i++){var range=ranges[i];extended.push({anchor:Pos(range.from().line,0),head:Pos(range.to().line+1,0)})}cm.setSelections(extended)};function insertLine(cm,above){if(cm.isReadOnly())return CodeMirror.Pass;cm.operation((function(){var len=cm.listSelections().length,newSelection=[],last=-1;for(var i=0;i<len;i++){var head=cm.listSelections()[i].head;if(head.line<=last)continue;var at=Pos(head.line+(above?0:1),0);cm.replaceRange("\n",at,null,"+insertLine");cm.indentLine(at.line,null,true);newSelection.push({head:at,anchor:at});last=head.line+1}cm.setSelections(newSelection)}));cm.execCommand("indentAuto")}cmds.insertLineAfter=function(cm){return insertLine(cm,false)};cmds.insertLineBefore=function(cm){return insertLine(cm,true)};function wordAt(cm,pos){var start=pos.ch,end=start,line=cm.getLine(pos.line);while(start&&CodeMirror.isWordChar(line.charAt(start-1)))--start;while(end<line.length&&CodeMirror.isWordChar(line.charAt(end)))++end;return{from:Pos(pos.line,start),to:Pos(pos.line,end),word:line.slice(start,end)}}cmds.selectNextOccurrence=function(cm){var from=cm.getCursor("from"),to=cm.getCursor("to");var fullWord=cm.state.sublimeFindFullWord==cm.doc.sel;if(CodeMirror.cmpPos(from,to)==0){var word=wordAt(cm,from);if(!word.word)return;cm.setSelection(word.from,word.to);fullWord=true}else{var text=cm.getRange(from,to);var query=fullWord?new RegExp("\\b"+text+"\\b"):text;var cur=cm.getSearchCursor(query,to);var found=cur.findNext();if(!found){cur=cm.getSearchCursor(query,Pos(cm.firstLine(),0));found=cur.findNext()}if(!found||isSelectedRange(cm.listSelections(),cur.from(),cur.to()))return;cm.addSelection(cur.from(),cur.to())}if(fullWord)cm.state.sublimeFindFullWord=cm.doc.sel};cmds.skipAndSelectNextOccurrence=function(cm){var prevAnchor=cm.getCursor("anchor"),prevHead=cm.getCursor("head");cmds.selectNextOccurrence(cm);if(CodeMirror.cmpPos(prevAnchor,prevHead)!=0){cm.doc.setSelections(cm.doc.listSelections().filter((function(sel){return sel.anchor!=prevAnchor||sel.head!=prevHead})))}};function addCursorToSelection(cm,dir){var ranges=cm.listSelections(),newRanges=[];for(var i=0;i<ranges.length;i++){var range=ranges[i];var newAnchor=cm.findPosV(range.anchor,dir,"line",range.anchor.goalColumn);var newHead=cm.findPosV(range.head,dir,"line",range.head.goalColumn);newAnchor.goalColumn=range.anchor.goalColumn!=null?range.anchor.goalColumn:cm.cursorCoords(range.anchor,"div").left;newHead.goalColumn=range.head.goalColumn!=null?range.head.goalColumn:cm.cursorCoords(range.head,"div").left;var newRange={anchor:newAnchor,head:newHead};newRanges.push(range);newRanges.push(newRange)}cm.setSelections(newRanges)}cmds.addCursorToPrevLine=function(cm){addCursorToSelection(cm,-1)};cmds.addCursorToNextLine=function(cm){addCursorToSelection(cm,1)};function isSelectedRange(ranges,from,to){for(var i=0;i<ranges.length;i++)if(CodeMirror.cmpPos(ranges[i].from(),from)==0&&CodeMirror.cmpPos(ranges[i].to(),to)==0)return true;return false}var mirror="(){}[]";function selectBetweenBrackets(cm){var ranges=cm.listSelections(),newRanges=[];for(var i=0;i<ranges.length;i++){var range=ranges[i],pos=range.head,opening=cm.scanForBracket(pos,-1);if(!opening)return false;for(;;){var closing=cm.scanForBracket(pos,1);if(!closing)return false;if(closing.ch==mirror.charAt(mirror.indexOf(opening.ch)+1)){var startPos=Pos(opening.pos.line,opening.pos.ch+1);if(CodeMirror.cmpPos(startPos,range.from())==0&&CodeMirror.cmpPos(closing.pos,range.to())==0){opening=cm.scanForBracket(opening.pos,-1);if(!opening)return false}else{newRanges.push({anchor:startPos,head:closing.pos});break}}pos=Pos(closing.pos.line,closing.pos.ch+1)}}cm.setSelections(newRanges);return true}cmds.selectScope=function(cm){selectBetweenBrackets(cm)||cm.execCommand("selectAll")};cmds.selectBetweenBrackets=function(cm){if(!selectBetweenBrackets(cm))return CodeMirror.Pass};function puncType(type){return!type?null:/\bpunctuation\b/.test(type)?type:undefined}cmds.goToBracket=function(cm){cm.extendSelectionsBy((function(range){var next=cm.scanForBracket(range.head,1,puncType(cm.getTokenTypeAt(range.head)));if(next&&CodeMirror.cmpPos(next.pos,range.head)!=0)return next.pos;var prev=cm.scanForBracket(range.head,-1,puncType(cm.getTokenTypeAt(Pos(range.head.line,range.head.ch+1))));return prev&&Pos(prev.pos.line,prev.pos.ch+1)||range.head}))};cmds.swapLineUp=function(cm){if(cm.isReadOnly())return CodeMirror.Pass;var ranges=cm.listSelections(),linesToMove=[],at=cm.firstLine()-1,newSels=[];for(var i=0;i<ranges.length;i++){var range=ranges[i],from=range.from().line-1,to=range.to().line;newSels.push({anchor:Pos(range.anchor.line-1,range.anchor.ch),head:Pos(range.head.line-1,range.head.ch)});if(range.to().ch==0&&!range.empty())--to;if(from>at)linesToMove.push(from,to);else if(linesToMove.length)linesToMove[linesToMove.length-1]=to;at=to}cm.operation((function(){for(var i=0;i<linesToMove.length;i+=2){var from=linesToMove[i],to=linesToMove[i+1];var line=cm.getLine(from);cm.replaceRange("",Pos(from,0),Pos(from+1,0),"+swapLine");if(to>cm.lastLine())cm.replaceRange("\n"+line,Pos(cm.lastLine()),null,"+swapLine");else cm.replaceRange(line+"\n",Pos(to,0),null,"+swapLine")}cm.setSelections(newSels);cm.scrollIntoView()}))};cmds.swapLineDown=function(cm){if(cm.isReadOnly())return CodeMirror.Pass;var ranges=cm.listSelections(),linesToMove=[],at=cm.lastLine()+1;for(var i=ranges.length-1;i>=0;i--){var range=ranges[i],from=range.to().line+1,to=range.from().line;if(range.to().ch==0&&!range.empty())from--;if(from<at)linesToMove.push(from,to);else if(linesToMove.length)linesToMove[linesToMove.length-1]=to;at=to}cm.operation((function(){for(var i=linesToMove.length-2;i>=0;i-=2){var from=linesToMove[i],to=linesToMove[i+1];var line=cm.getLine(from);if(from==cm.lastLine())cm.replaceRange("",Pos(from-1),Pos(from),"+swapLine");else cm.replaceRange("",Pos(from,0),Pos(from+1,0),"+swapLine");cm.replaceRange(line+"\n",Pos(to,0),null,"+swapLine")}cm.scrollIntoView()}))};cmds.toggleCommentIndented=function(cm){cm.toggleComment({indent:true})};cmds.joinLines=function(cm){var ranges=cm.listSelections(),joined=[];for(var i=0;i<ranges.length;i++){var range=ranges[i],from=range.from();var start=from.line,end=range.to().line;while(i<ranges.length-1&&ranges[i+1].from().line==end)end=ranges[++i].to().line;joined.push({start:start,end:end,anchor:!range.empty()&&from})}cm.operation((function(){var offset=0,ranges=[];for(var i=0;i<joined.length;i++){var obj=joined[i];var anchor=obj.anchor&&Pos(obj.anchor.line-offset,obj.anchor.ch),head;for(var line=obj.start;line<=obj.end;line++){var actual=line-offset;if(line==obj.end)head=Pos(actual,cm.getLine(actual).length+1);if(actual<cm.lastLine()){cm.replaceRange(" ",Pos(actual),Pos(actual+1,/^\s*/.exec(cm.getLine(actual+1))[0].length));++offset}}ranges.push({anchor:anchor||head,head:head})}cm.setSelections(ranges,0)}))};cmds.duplicateLine=function(cm){cm.operation((function(){var rangeCount=cm.listSelections().length;for(var i=0;i<rangeCount;i++){var range=cm.listSelections()[i];if(range.empty())cm.replaceRange(cm.getLine(range.head.line)+"\n",Pos(range.head.line,0));else cm.replaceRange(cm.getRange(range.from(),range.to()),range.from())}cm.scrollIntoView()}))};function sortLines(cm,caseSensitive,direction){if(cm.isReadOnly())return CodeMirror.Pass;var ranges=cm.listSelections(),toSort=[],selected;for(var i=0;i<ranges.length;i++){var range=ranges[i];if(range.empty())continue;var from=range.from().line,to=range.to().line;while(i<ranges.length-1&&ranges[i+1].from().line==to)to=ranges[++i].to().line;if(!ranges[i].to().ch)to--;toSort.push(from,to)}if(toSort.length)selected=true;else toSort.push(cm.firstLine(),cm.lastLine());cm.operation((function(){var ranges=[];for(var i=0;i<toSort.length;i+=2){var from=toSort[i],to=toSort[i+1];var start=Pos(from,0),end=Pos(to);var lines=cm.getRange(start,end,false);if(caseSensitive)lines.sort((function(a,b){return a<b?-direction:a==b?0:direction}));else lines.sort((function(a,b){var au=a.toUpperCase(),bu=b.toUpperCase();if(au!=bu){a=au;b=bu}return a<b?-direction:a==b?0:direction}));cm.replaceRange(lines,start,end);if(selected)ranges.push({anchor:start,head:Pos(to+1,0)})}if(selected)cm.setSelections(ranges,0)}))}cmds.sortLines=function(cm){sortLines(cm,true,1)};cmds.reverseSortLines=function(cm){sortLines(cm,true,-1)};cmds.sortLinesInsensitive=function(cm){sortLines(cm,false,1)};cmds.reverseSortLinesInsensitive=function(cm){sortLines(cm,false,-1)};cmds.nextBookmark=function(cm){var marks=cm.state.sublimeBookmarks;if(marks)while(marks.length){var current=marks.shift();var found=current.find();if(found){marks.push(current);return cm.setSelection(found.from,found.to)}}};cmds.prevBookmark=function(cm){var marks=cm.state.sublimeBookmarks;if(marks)while(marks.length){marks.unshift(marks.pop());var found=marks[marks.length-1].find();if(!found)marks.pop();else return cm.setSelection(found.from,found.to)}};cmds.toggleBookmark=function(cm){var ranges=cm.listSelections();var marks=cm.state.sublimeBookmarks||(cm.state.sublimeBookmarks=[]);for(var i=0;i<ranges.length;i++){var from=ranges[i].from(),to=ranges[i].to();var found=ranges[i].empty()?cm.findMarksAt(from):cm.findMarks(from,to);for(var j=0;j<found.length;j++){if(found[j].sublimeBookmark){found[j].clear();for(var k=0;k<marks.length;k++)if(marks[k]==found[j])marks.splice(k--,1);break}}if(j==found.length)marks.push(cm.markText(from,to,{sublimeBookmark:true,clearWhenEmpty:false}))}};cmds.clearBookmarks=function(cm){var marks=cm.state.sublimeBookmarks;if(marks)for(var i=0;i<marks.length;i++)marks[i].clear();marks.length=0};cmds.selectBookmarks=function(cm){var marks=cm.state.sublimeBookmarks,ranges=[];if(marks)for(var i=0;i<marks.length;i++){var found=marks[i].find();if(!found)marks.splice(i--,0);else ranges.push({anchor:found.from,head:found.to})}if(ranges.length)cm.setSelections(ranges,0)};function modifyWordOrSelection(cm,mod){cm.operation((function(){var ranges=cm.listSelections(),indices=[],replacements=[];for(var i=0;i<ranges.length;i++){var range=ranges[i];if(range.empty()){indices.push(i);replacements.push("")}else replacements.push(mod(cm.getRange(range.from(),range.to())))}cm.replaceSelections(replacements,"around","case");for(var i=indices.length-1,at;i>=0;i--){var range=ranges[indices[i]];if(at&&CodeMirror.cmpPos(range.head,at)>0)continue;var word=wordAt(cm,range.head);at=word.from;cm.replaceRange(mod(word.word),word.from,word.to)}}))}cmds.smartBackspace=function(cm){if(cm.somethingSelected())return CodeMirror.Pass;cm.operation((function(){var cursors=cm.listSelections();var indentUnit=cm.getOption("indentUnit");for(var i=cursors.length-1;i>=0;i--){var cursor=cursors[i].head;var toStartOfLine=cm.getRange({line:cursor.line,ch:0},cursor);var column=CodeMirror.countColumn(toStartOfLine,null,cm.getOption("tabSize"));var deletePos=cm.findPosH(cursor,-1,"char",false);if(toStartOfLine&&!/\S/.test(toStartOfLine)&&column%indentUnit==0){var prevIndent=new Pos(cursor.line,CodeMirror.findColumn(toStartOfLine,column-indentUnit,indentUnit));if(prevIndent.ch!=cursor.ch)deletePos=prevIndent}cm.replaceRange("",deletePos,cursor,"+delete")}}))};cmds.delLineRight=function(cm){cm.operation((function(){var ranges=cm.listSelections();for(var i=ranges.length-1;i>=0;i--)cm.replaceRange("",ranges[i].anchor,Pos(ranges[i].to().line),"+delete");cm.scrollIntoView()}))};cmds.upcaseAtCursor=function(cm){modifyWordOrSelection(cm,(function(str){return str.toUpperCase()}))};cmds.downcaseAtCursor=function(cm){modifyWordOrSelection(cm,(function(str){return str.toLowerCase()}))};cmds.setSublimeMark=function(cm){if(cm.state.sublimeMark)cm.state.sublimeMark.clear();cm.state.sublimeMark=cm.setBookmark(cm.getCursor())};cmds.selectToSublimeMark=function(cm){var found=cm.state.sublimeMark&&cm.state.sublimeMark.find();if(found)cm.setSelection(cm.getCursor(),found)};cmds.deleteToSublimeMark=function(cm){var found=cm.state.sublimeMark&&cm.state.sublimeMark.find();if(found){var from=cm.getCursor(),to=found;if(CodeMirror.cmpPos(from,to)>0){var tmp=to;to=from;from=tmp}cm.state.sublimeKilled=cm.getRange(from,to);cm.replaceRange("",from,to)}};cmds.swapWithSublimeMark=function(cm){var found=cm.state.sublimeMark&&cm.state.sublimeMark.find();if(found){cm.state.sublimeMark.clear();cm.state.sublimeMark=cm.setBookmark(cm.getCursor());cm.setCursor(found)}};cmds.sublimeYank=function(cm){if(cm.state.sublimeKilled!=null)cm.replaceSelection(cm.state.sublimeKilled,null,"paste")};cmds.showInCenter=function(cm){var pos=cm.cursorCoords(null,"local");cm.scrollTo(null,(pos.top+pos.bottom)/2-cm.getScrollInfo().clientHeight/2)};function getTarget(cm){var from=cm.getCursor("from"),to=cm.getCursor("to");if(CodeMirror.cmpPos(from,to)==0){var word=wordAt(cm,from);if(!word.word)return;from=word.from;to=word.to}return{from:from,to:to,query:cm.getRange(from,to),word:word}}function findAndGoTo(cm,forward){var target=getTarget(cm);if(!target)return;var query=target.query;var cur=cm.getSearchCursor(query,forward?target.to:target.from);if(forward?cur.findNext():cur.findPrevious()){cm.setSelection(cur.from(),cur.to())}else{cur=cm.getSearchCursor(query,forward?Pos(cm.firstLine(),0):cm.clipPos(Pos(cm.lastLine())));if(forward?cur.findNext():cur.findPrevious())cm.setSelection(cur.from(),cur.to());else if(target.word)cm.setSelection(target.from,target.to)}}cmds.findUnder=function(cm){findAndGoTo(cm,true)};cmds.findUnderPrevious=function(cm){findAndGoTo(cm,false)};cmds.findAllUnder=function(cm){var target=getTarget(cm);if(!target)return;var cur=cm.getSearchCursor(target.query);var matches=[];var primaryIndex=-1;while(cur.findNext()){matches.push({anchor:cur.from(),head:cur.to()});if(cur.from().line<=target.from.line&&cur.from().ch<=target.from.ch)primaryIndex++}cm.setSelections(matches,primaryIndex)};var keyMap=CodeMirror.keyMap;keyMap.macSublime={"Cmd-Left":"goLineStartSmart","Shift-Tab":"indentLess","Shift-Ctrl-K":"deleteLine","Alt-Q":"wrapLines","Ctrl-Left":"goSubwordLeft","Ctrl-Right":"goSubwordRight","Ctrl-Alt-Up":"scrollLineUp","Ctrl-Alt-Down":"scrollLineDown","Cmd-L":"selectLine","Shift-Cmd-L":"splitSelectionByLine",Esc:"singleSelectionTop","Cmd-Enter":"insertLineAfter","Shift-Cmd-Enter":"insertLineBefore","Cmd-D":"selectNextOccurrence","Shift-Cmd-Space":"selectScope","Shift-Cmd-M":"selectBetweenBrackets","Cmd-M":"goToBracket","Cmd-Ctrl-Up":"swapLineUp","Cmd-Ctrl-Down":"swapLineDown","Cmd-/":"toggleCommentIndented","Cmd-J":"joinLines","Shift-Cmd-D":"duplicateLine",F5:"sortLines","Shift-F5":"reverseSortLines","Cmd-F5":"sortLinesInsensitive","Shift-Cmd-F5":"reverseSortLinesInsensitive",F2:"nextBookmark","Shift-F2":"prevBookmark","Cmd-F2":"toggleBookmark","Shift-Cmd-F2":"clearBookmarks","Alt-F2":"selectBookmarks",Backspace:"smartBackspace","Cmd-K Cmd-D":"skipAndSelectNextOccurrence","Cmd-K Cmd-K":"delLineRight","Cmd-K Cmd-U":"upcaseAtCursor","Cmd-K Cmd-L":"downcaseAtCursor","Cmd-K Cmd-Space":"setSublimeMark","Cmd-K Cmd-A":"selectToSublimeMark","Cmd-K Cmd-W":"deleteToSublimeMark","Cmd-K Cmd-X":"swapWithSublimeMark","Cmd-K Cmd-Y":"sublimeYank","Cmd-K Cmd-C":"showInCenter","Cmd-K Cmd-G":"clearBookmarks","Cmd-K Cmd-Backspace":"delLineLeft","Cmd-K Cmd-1":"foldAll","Cmd-K Cmd-0":"unfoldAll","Cmd-K Cmd-J":"unfoldAll","Ctrl-Shift-Up":"addCursorToPrevLine","Ctrl-Shift-Down":"addCursorToNextLine","Cmd-F3":"findUnder","Shift-Cmd-F3":"findUnderPrevious","Alt-F3":"findAllUnder","Shift-Cmd-[":"fold","Shift-Cmd-]":"unfold","Cmd-I":"findIncremental","Shift-Cmd-I":"findIncrementalReverse","Cmd-H":"replace",F3:"findNext","Shift-F3":"findPrev",fallthrough:"macDefault"};CodeMirror.normalizeKeyMap(keyMap.macSublime);keyMap.pcSublime={"Shift-Tab":"indentLess","Shift-Ctrl-K":"deleteLine","Alt-Q":"wrapLines","Ctrl-T":"transposeChars","Alt-Left":"goSubwordLeft","Alt-Right":"goSubwordRight","Ctrl-Up":"scrollLineUp","Ctrl-Down":"scrollLineDown","Ctrl-L":"selectLine","Shift-Ctrl-L":"splitSelectionByLine",Esc:"singleSelectionTop","Ctrl-Enter":"insertLineAfter","Shift-Ctrl-Enter":"insertLineBefore","Ctrl-D":"selectNextOccurrence","Shift-Ctrl-Space":"selectScope","Shift-Ctrl-M":"selectBetweenBrackets","Ctrl-M":"goToBracket","Shift-Ctrl-Up":"swapLineUp","Shift-Ctrl-Down":"swapLineDown","Ctrl-/":"toggleCommentIndented","Ctrl-J":"joinLines","Shift-Ctrl-D":"duplicateLine",F9:"sortLines","Shift-F9":"reverseSortLines","Ctrl-F9":"sortLinesInsensitive","Shift-Ctrl-F9":"reverseSortLinesInsensitive",F2:"nextBookmark","Shift-F2":"prevBookmark","Ctrl-F2":"toggleBookmark","Shift-Ctrl-F2":"clearBookmarks","Alt-F2":"selectBookmarks",Backspace:"smartBackspace","Ctrl-K Ctrl-D":"skipAndSelectNextOccurrence","Ctrl-K Ctrl-K":"delLineRight","Ctrl-K Ctrl-U":"upcaseAtCursor","Ctrl-K Ctrl-L":"downcaseAtCursor","Ctrl-K Ctrl-Space":"setSublimeMark","Ctrl-K Ctrl-A":"selectToSublimeMark","Ctrl-K Ctrl-W":"deleteToSublimeMark","Ctrl-K Ctrl-X":"swapWithSublimeMark","Ctrl-K Ctrl-Y":"sublimeYank","Ctrl-K Ctrl-C":"showInCenter","Ctrl-K Ctrl-G":"clearBookmarks","Ctrl-K Ctrl-Backspace":"delLineLeft","Ctrl-K Ctrl-1":"foldAll","Ctrl-K Ctrl-0":"unfoldAll","Ctrl-K Ctrl-J":"unfoldAll","Ctrl-Alt-Up":"addCursorToPrevLine","Ctrl-Alt-Down":"addCursorToNextLine","Ctrl-F3":"findUnder","Shift-Ctrl-F3":"findUnderPrevious","Alt-F3":"findAllUnder","Shift-Ctrl-[":"fold","Shift-Ctrl-]":"unfold","Ctrl-I":"findIncremental","Shift-Ctrl-I":"findIncrementalReverse","Ctrl-H":"replace",F3:"findNext","Shift-F3":"findPrev",fallthrough:"pcDefault"};CodeMirror.normalizeKeyMap(keyMap.pcSublime);var mac=keyMap.default==keyMap.macDefault;keyMap.sublime=mac?keyMap.macSublime:keyMap.pcSublime}));(function(mod){if(typeof exports=="object"&&typeof module=="object")mod(require("../../lib/codemirror"),require("./searchcursor"),require("../scroll/annotatescrollbar"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror","./searchcursor","../scroll/annotatescrollbar"],mod);else mod(CodeMirror)})((function(CodeMirror){"use strict";CodeMirror.defineExtension("showMatchesOnScrollbar",(function(query,caseFold,options){if(typeof options=="string")options={className:options};if(!options)options={};return new SearchAnnotation(this,query,caseFold,options)}));function SearchAnnotation(cm,query,caseFold,options){this.cm=cm;this.options=options;var annotateOptions={listenForChanges:false};for(var prop in options)annotateOptions[prop]=options[prop];if(!annotateOptions.className)annotateOptions.className="CodeMirror-search-match";this.annotation=cm.annotateScrollbar(annotateOptions);this.query=query;this.caseFold=caseFold;this.gap={from:cm.firstLine(),to:cm.lastLine()+1};this.matches=[];this.update=null;this.findMatches();this.annotation.update(this.matches);var self=this;cm.on("change",this.changeHandler=function(_cm,change){self.onChange(change)})}var MAX_MATCHES=1e3;SearchAnnotation.prototype.findMatches=function(){if(!this.gap)return;for(var i=0;i<this.matches.length;i++){var match=this.matches[i];if(match.from.line>=this.gap.to)break;if(match.to.line>=this.gap.from)this.matches.splice(i--,1)}var cursor=this.cm.getSearchCursor(this.query,CodeMirror.Pos(this.gap.from,0),{caseFold:this.caseFold,multiline:this.options.multiline});var maxMatches=this.options&&this.options.maxMatches||MAX_MATCHES;while(cursor.findNext()){var match={from:cursor.from(),to:cursor.to()};if(match.from.line>=this.gap.to)break;this.matches.splice(i++,0,match);if(this.matches.length>maxMatches)break}this.gap=null};function offsetLine(line,changeStart,sizeChange){if(line<=changeStart)return line;return Math.max(changeStart,line+sizeChange)}SearchAnnotation.prototype.onChange=function(change){var startLine=change.from.line;var endLine=CodeMirror.changeEnd(change).line;var sizeChange=endLine-change.to.line;if(this.gap){this.gap.from=Math.min(offsetLine(this.gap.from,startLine,sizeChange),change.from.line);this.gap.to=Math.max(offsetLine(this.gap.to,startLine,sizeChange),change.from.line)}else{this.gap={from:change.from.line,to:endLine+1}}if(sizeChange)for(var i=0;i<this.matches.length;i++){var match=this.matches[i];var newFrom=offsetLine(match.from.line,startLine,sizeChange);if(newFrom!=match.from.line)match.from=CodeMirror.Pos(newFrom,match.from.ch);var newTo=offsetLine(match.to.line,startLine,sizeChange);if(newTo!=match.to.line)match.to=CodeMirror.Pos(newTo,match.to.ch)}clearTimeout(this.update);var self=this;this.update=setTimeout((function(){self.updateAfterChange()}),250)};SearchAnnotation.prototype.updateAfterChange=function(){this.findMatches();this.annotation.update(this.matches)};SearchAnnotation.prototype.clear=function(){this.cm.off("change",this.changeHandler);this.annotation.clear()}}));(function(mod){if(typeof exports=="object"&&typeof module=="object")mod(require("../../lib/codemirror"),require("./foldcode"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror","./foldcode"],mod);else mod(CodeMirror)})((function(CodeMirror){"use strict";CodeMirror.defineOption("foldGutter",false,(function(cm,val,old){if(old&&old!=CodeMirror.Init){cm.clearGutter(cm.state.foldGutter.options.gutter);cm.state.foldGutter=null;cm.off("gutterClick",onGutterClick);cm.off("changes",onChange);cm.off("viewportChange",onViewportChange);cm.off("fold",onFold);cm.off("unfold",onFold);cm.off("swapDoc",onChange);cm.off("optionChange",optionChange)}if(val){cm.state.foldGutter=new State(parseOptions(val));updateInViewport(cm);cm.on("gutterClick",onGutterClick);cm.on("changes",onChange);cm.on("viewportChange",onViewportChange);cm.on("fold",onFold);cm.on("unfold",onFold);cm.on("swapDoc",onChange);cm.on("optionChange",optionChange)}}));var Pos=CodeMirror.Pos;function State(options){this.options=options;this.from=this.to=0}function parseOptions(opts){if(opts===true)opts={};if(opts.gutter==null)opts.gutter="CodeMirror-foldgutter";if(opts.indicatorOpen==null)opts.indicatorOpen="CodeMirror-foldgutter-open";if(opts.indicatorFolded==null)opts.indicatorFolded="CodeMirror-foldgutter-folded";return opts}function isFolded(cm,line){var marks=cm.findMarks(Pos(line,0),Pos(line+1,0));for(var i=0;i<marks.length;++i){if(marks[i].__isFold){var fromPos=marks[i].find(-1);if(fromPos&&fromPos.line===line)return marks[i]}}}function marker(spec){if(typeof spec=="string"){var elt=document.createElement("div");elt.className=spec+" CodeMirror-guttermarker-subtle";return elt}else{return spec.cloneNode(true)}}function updateFoldInfo(cm,from,to){var opts=cm.state.foldGutter.options,cur=from-1;var minSize=cm.foldOption(opts,"minFoldSize");var func=cm.foldOption(opts,"rangeFinder");var clsFolded=typeof opts.indicatorFolded=="string"&&classTest(opts.indicatorFolded);var clsOpen=typeof opts.indicatorOpen=="string"&&classTest(opts.indicatorOpen);cm.eachLine(from,to,(function(line){++cur;var mark=null;var old=line.gutterMarkers;if(old)old=old[opts.gutter];if(isFolded(cm,cur)){if(clsFolded&&old&&clsFolded.test(old.className))return;mark=marker(opts.indicatorFolded)}else{var pos=Pos(cur,0);var range=func&&func(cm,pos);if(range&&range.to.line-range.from.line>=minSize){if(clsOpen&&old&&clsOpen.test(old.className))return;mark=marker(opts.indicatorOpen)}}if(!mark&&!old)return;cm.setGutterMarker(line,opts.gutter,mark)}))}function classTest(cls){return new RegExp("(^|\\s)"+cls+"(?:$|\\s)\\s*")}function updateInViewport(cm){var vp=cm.getViewport(),state=cm.state.foldGutter;if(!state)return;cm.operation((function(){updateFoldInfo(cm,vp.from,vp.to)}));state.from=vp.from;state.to=vp.to}function onGutterClick(cm,line,gutter){var state=cm.state.foldGutter;if(!state)return;var opts=state.options;if(gutter!=opts.gutter)return;var folded=isFolded(cm,line);if(folded)folded.clear();else cm.foldCode(Pos(line,0),opts)}function optionChange(cm,option){if(option=="mode")onChange(cm)}function onChange(cm){var state=cm.state.foldGutter;if(!state)return;var opts=state.options;state.from=state.to=0;clearTimeout(state.changeUpdate);state.changeUpdate=setTimeout((function(){updateInViewport(cm)}),opts.foldOnChangeTimeSpan||600)}function onViewportChange(cm){var state=cm.state.foldGutter;if(!state)return;var opts=state.options;clearTimeout(state.changeUpdate);state.changeUpdate=setTimeout((function(){var vp=cm.getViewport();if(state.from==state.to||vp.from-state.to>20||state.from-vp.to>20){updateInViewport(cm)}else{cm.operation((function(){if(vp.from<state.from){updateFoldInfo(cm,vp.from,state.from);state.from=vp.from}if(vp.to>state.to){updateFoldInfo(cm,state.to,vp.to);state.to=vp.to}}))}}),opts.updateViewportTimeSpan||400)}function onFold(cm,from){var state=cm.state.foldGutter;if(!state)return;var line=from.line;if(line>=state.from&&line<state.to)updateFoldInfo(cm,line,line+1)}}));(function(mod){if(typeof exports=="object"&&typeof module=="object")mod(require("../../lib/codemirror"),require("../dialog/dialog"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror","../dialog/dialog"],mod);else mod(CodeMirror)})((function(CodeMirror){"use strict";CodeMirror.defineOption("search",{bottom:false});function dialog(cm,text,shortText,deflt,f){if(cm.openDialog)cm.openDialog(text,f,{value:deflt,selectValueOnOpen:true,bottom:cm.options.search.bottom});else f(prompt(shortText,deflt))}function getJumpDialog(cm){return cm.phrase("Jump to line:")+' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">'+cm.phrase("(Use line:column or scroll% syntax)")+"</span>"}function interpretLine(cm,string){var num=Number(string);if(/^[-+]/.test(string))return cm.getCursor().line+num;else return num-1}CodeMirror.commands.jumpToLine=function(cm){var cur=cm.getCursor();dialog(cm,getJumpDialog(cm),cm.phrase("Jump to line:"),cur.line+1+":"+cur.ch,(function(posStr){if(!posStr)return;var match;if(match=/^\s*([\+\-]?\d+)\s*\:\s*(\d+)\s*$/.exec(posStr)){cm.setCursor(interpretLine(cm,match[1]),Number(match[2]))}else if(match=/^\s*([\+\-]?\d+(\.\d+)?)\%\s*/.exec(posStr)){var line=Math.round(cm.lineCount()*Number(match[1])/100);if(/^[-+]/.test(match[1]))line=cur.line+line+1;cm.setCursor(line-1,cur.ch)}else if(match=/^\s*\:?\s*([\+\-]?\d+)\s*/.exec(posStr)){cm.setCursor(interpretLine(cm,match[1]),cur.ch)}}))};CodeMirror.keyMap["default"]["Alt-G"]="jumpToLine"}));(function(mod){if(typeof exports=="object"&&typeof module=="object")mod(require("../../lib/codemirror"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror"],mod);else mod(CodeMirror)})((function(CodeMirror){"use strict";var noOptions={};var nonWS=/[^\s\u00a0]/;var Pos=CodeMirror.Pos,cmp=CodeMirror.cmpPos;function firstNonWS(str){var found=str.search(nonWS);return found==-1?0:found}CodeMirror.commands.toggleComment=function(cm){cm.toggleComment()};CodeMirror.defineExtension("toggleComment",(function(options){if(!options)options=noOptions;var cm=this;var minLine=Infinity,ranges=this.listSelections(),mode=null;for(var i=ranges.length-1;i>=0;i--){var from=ranges[i].from(),to=ranges[i].to();if(from.line>=minLine)continue;if(to.line>=minLine)to=Pos(minLine,0);minLine=from.line;if(mode==null){if(cm.uncomment(from,to,options))mode="un";else{cm.lineComment(from,to,options);mode="line"}}else if(mode=="un"){cm.uncomment(from,to,options)}else{cm.lineComment(from,to,options)}}}));function probablyInsideString(cm,pos,line){return/\bstring\b/.test(cm.getTokenTypeAt(Pos(pos.line,0)))&&!/^[\'\"\`]/.test(line)}function getMode(cm,pos){var mode=cm.getMode();return mode.useInnerComments===false||!mode.innerMode?mode:cm.getModeAt(pos)}CodeMirror.defineExtension("lineComment",(function(from,to,options){if(!options)options=noOptions;var self=this,mode=getMode(self,from);var firstLine=self.getLine(from.line);if(firstLine==null||probablyInsideString(self,from,firstLine))return;var commentString=options.lineComment||mode.lineComment;if(!commentString){if(options.blockCommentStart||mode.blockCommentStart){options.fullLines=true;self.blockComment(from,to,options)}return}var end=Math.min(to.ch!=0||to.line==from.line?to.line+1:to.line,self.lastLine()+1);var pad=options.padding==null?" ":options.padding;var blankLines=options.commentBlankLines||from.line==to.line;self.operation((function(){if(options.indent){var baseString=null;for(var i=from.line;i<end;++i){var line=self.getLine(i);var whitespace=line.search(nonWS)===-1?line:line.slice(0,firstNonWS(line));if(baseString==null||baseString.length>whitespace.length){baseString=whitespace}}for(var i=from.line;i<end;++i){var line=self.getLine(i),cut=baseString.length;if(!blankLines&&!nonWS.test(line))continue;if(line.slice(0,cut)!=baseString)cut=firstNonWS(line);self.replaceRange(baseString+commentString+pad,Pos(i,0),Pos(i,cut))}}else{for(var i=from.line;i<end;++i){if(blankLines||nonWS.test(self.getLine(i)))self.replaceRange(commentString+pad,Pos(i,0))}}}))}));CodeMirror.defineExtension("blockComment",(function(from,to,options){if(!options)options=noOptions;var self=this,mode=getMode(self,from);var startString=options.blockCommentStart||mode.blockCommentStart;var endString=options.blockCommentEnd||mode.blockCommentEnd;if(!startString||!endString){if((options.lineComment||mode.lineComment)&&options.fullLines!=false)self.lineComment(from,to,options);return}if(/\bcomment\b/.test(self.getTokenTypeAt(Pos(from.line,0))))return;var end=Math.min(to.line,self.lastLine());if(end!=from.line&&to.ch==0&&nonWS.test(self.getLine(end)))--end;var pad=options.padding==null?" ":options.padding;if(from.line>end)return;self.operation((function(){if(options.fullLines!=false){var lastLineHasText=nonWS.test(self.getLine(end));self.replaceRange(pad+endString,Pos(end));self.replaceRange(startString+pad,Pos(from.line,0));var lead=options.blockCommentLead||mode.blockCommentLead;if(lead!=null)for(var i=from.line+1;i<=end;++i)if(i!=end||lastLineHasText)self.replaceRange(lead+pad,Pos(i,0))}else{var atCursor=cmp(self.getCursor("to"),to)==0,empty=!self.somethingSelected();self.replaceRange(endString,to);if(atCursor)self.setSelection(empty?to:self.getCursor("from"),to);self.replaceRange(startString,from)}}))}));CodeMirror.defineExtension("uncomment",(function(from,to,options){if(!options)options=noOptions;var self=this,mode=getMode(self,from);var end=Math.min(to.ch!=0||to.line==from.line?to.line:to.line-1,self.lastLine()),start=Math.min(from.line,end);var lineString=options.lineComment||mode.lineComment,lines=[];var pad=options.padding==null?" ":options.padding,didSomething;lineComment:{if(!lineString)break lineComment;for(var i=start;i<=end;++i){var line=self.getLine(i);var found=line.indexOf(lineString);if(found>-1&&!/comment/.test(self.getTokenTypeAt(Pos(i,found+1))))found=-1;if(found==-1&&nonWS.test(line))break lineComment;if(found>-1&&nonWS.test(line.slice(0,found)))break lineComment;lines.push(line)}self.operation((function(){for(var i=start;i<=end;++i){var line=lines[i-start];var pos=line.indexOf(lineString),endPos=pos+lineString.length;if(pos<0)continue;if(line.slice(endPos,endPos+pad.length)==pad)endPos+=pad.length;didSomething=true;self.replaceRange("",Pos(i,pos),Pos(i,endPos))}}));if(didSomething)return true}var startString=options.blockCommentStart||mode.blockCommentStart;var endString=options.blockCommentEnd||mode.blockCommentEnd;if(!startString||!endString)return false;var lead=options.blockCommentLead||mode.blockCommentLead;var startLine=self.getLine(start),open=startLine.indexOf(startString);if(open==-1)return false;var endLine=end==start?startLine:self.getLine(end);var close=endLine.indexOf(endString,end==start?open+startString.length:0);var insideStart=Pos(start,open+1),insideEnd=Pos(end,close+1);if(close==-1||!/comment/.test(self.getTokenTypeAt(insideStart))||!/comment/.test(self.getTokenTypeAt(insideEnd))||self.getRange(insideStart,insideEnd,"\n").indexOf(endString)>-1)return false;var lastStart=startLine.lastIndexOf(startString,from.ch);var firstEnd=lastStart==-1?-1:startLine.slice(0,from.ch).indexOf(endString,lastStart+startString.length);if(lastStart!=-1&&firstEnd!=-1&&firstEnd+endString.length!=from.ch)return false;firstEnd=endLine.indexOf(endString,to.ch);var almostLastStart=endLine.slice(to.ch).lastIndexOf(startString,firstEnd-to.ch);lastStart=firstEnd==-1||almostLastStart==-1?-1:to.ch+almostLastStart;if(firstEnd!=-1&&lastStart!=-1&&lastStart!=to.ch)return false;self.operation((function(){self.replaceRange("",Pos(end,close-(pad&&endLine.slice(close-pad.length,close)==pad?pad.length:0)),Pos(end,close+endString.length));var openEnd=open+startString.length;if(pad&&startLine.slice(openEnd,openEnd+pad.length)==pad)openEnd+=pad.length;self.replaceRange("",Pos(start,open),Pos(start,openEnd));if(lead)for(var i=start+1;i<=end;++i){var line=self.getLine(i),found=line.indexOf(lead);if(found==-1||nonWS.test(line.slice(0,found)))continue;var foundEnd=found+lead.length;if(pad&&line.slice(foundEnd,foundEnd+pad.length)==pad)foundEnd+=pad.length;self.replaceRange("",Pos(i,found),Pos(i,foundEnd))}}));return true}))}));(function(mod){if(typeof exports=="object"&&typeof module=="object")mod(require("../../lib/codemirror"),require("./matchesonscrollbar"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror","./matchesonscrollbar"],mod);else mod(CodeMirror)})((function(CodeMirror){"use strict";var defaults={style:"matchhighlight",minChars:2,delay:100,wordsOnly:false,annotateScrollbar:false,showToken:false,trim:true};function State(options){this.options={};for(var name in defaults)this.options[name]=(options&&options.hasOwnProperty(name)?options:defaults)[name];this.overlay=this.timeout=null;this.matchesonscroll=null;this.active=false}CodeMirror.defineOption("highlightSelectionMatches",false,(function(cm,val,old){if(old&&old!=CodeMirror.Init){removeOverlay(cm);clearTimeout(cm.state.matchHighlighter.timeout);cm.state.matchHighlighter=null;cm.off("cursorActivity",cursorActivity);cm.off("focus",onFocus)}if(val){var state=cm.state.matchHighlighter=new State(val);if(cm.hasFocus()){state.active=true;highlightMatches(cm)}else{cm.on("focus",onFocus)}cm.on("cursorActivity",cursorActivity)}}));function cursorActivity(cm){var state=cm.state.matchHighlighter;if(state.active||cm.hasFocus())scheduleHighlight(cm,state)}function onFocus(cm){var state=cm.state.matchHighlighter;if(!state.active){state.active=true;scheduleHighlight(cm,state)}}function scheduleHighlight(cm,state){clearTimeout(state.timeout);state.timeout=setTimeout((function(){highlightMatches(cm)}),state.options.delay)}function addOverlay(cm,query,hasBoundary,style){var state=cm.state.matchHighlighter;cm.addOverlay(state.overlay=makeOverlay(query,hasBoundary,style));if(state.options.annotateScrollbar&&cm.showMatchesOnScrollbar){var searchFor=hasBoundary?new RegExp((/\w/.test(query.charAt(0))?"\\b":"")+query.replace(/[\\\[.+*?(){|^$]/g,"\\$&")+(/\w/.test(query.charAt(query.length-1))?"\\b":"")):query;state.matchesonscroll=cm.showMatchesOnScrollbar(searchFor,false,{className:"CodeMirror-selection-highlight-scrollbar"})}}function removeOverlay(cm){var state=cm.state.matchHighlighter;if(state.overlay){cm.removeOverlay(state.overlay);state.overlay=null;if(state.matchesonscroll){state.matchesonscroll.clear();state.matchesonscroll=null}}}function highlightMatches(cm){cm.operation((function(){var state=cm.state.matchHighlighter;removeOverlay(cm);if(!cm.somethingSelected()&&state.options.showToken){var re=state.options.showToken===true?/[\w$]/:state.options.showToken;var cur=cm.getCursor(),line=cm.getLine(cur.line),start=cur.ch,end=start;while(start&&re.test(line.charAt(start-1)))--start;while(end<line.length&&re.test(line.charAt(end)))++end;if(start<end)addOverlay(cm,line.slice(start,end),re,state.options.style);return}var from=cm.getCursor("from"),to=cm.getCursor("to");if(from.line!=to.line)return;if(state.options.wordsOnly&&!isWord(cm,from,to))return;var selection=cm.getRange(from,to);if(state.options.trim)selection=selection.replace(/^\s+|\s+$/g,"");if(selection.length>=state.options.minChars)addOverlay(cm,selection,false,state.options.style)}))}function isWord(cm,from,to){var str=cm.getRange(from,to);if(str.match(/^\w+$/)!==null){if(from.ch>0){var pos={line:from.line,ch:from.ch-1};var chr=cm.getRange(pos,from);if(chr.match(/\W/)===null)return false}if(to.ch<cm.getLine(from.line).length){var pos={line:to.line,ch:to.ch+1};var chr=cm.getRange(to,pos);if(chr.match(/\W/)===null)return false}return true}else return false}function boundariesAround(stream,re){return(!stream.start||!re.test(stream.string.charAt(stream.start-1)))&&(stream.pos==stream.string.length||!re.test(stream.string.charAt(stream.pos)))}function makeOverlay(query,hasBoundary,style){return{token:function(stream){if(stream.match(query)&&(!hasBoundary||boundariesAround(stream,hasBoundary)))return style;stream.next();stream.skipTo(query.charAt(0))||stream.skipToEnd()}}}}));(function(mod){if(typeof exports=="object"&&typeof module=="object")mod(require("../../lib/codemirror"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror"],mod);else mod(CodeMirror)})((function(CodeMirror){function dialogDiv(cm,template,bottom){var wrap=cm.getWrapperElement();var dialog;dialog=wrap.appendChild(document.createElement("div"));if(bottom)dialog.className="CodeMirror-dialog CodeMirror-dialog-bottom";else dialog.className="CodeMirror-dialog CodeMirror-dialog-top";if(typeof template=="string"){dialog.innerHTML=template}else{dialog.appendChild(template)}CodeMirror.addClass(wrap,"dialog-opened");return dialog}function closeNotification(cm,newVal){if(cm.state.currentNotificationClose)cm.state.currentNotificationClose();cm.state.currentNotificationClose=newVal}CodeMirror.defineExtension("openDialog",(function(template,callback,options){if(!options)options={};closeNotification(this,null);var dialog=dialogDiv(this,template,options.bottom);var closed=false,me=this;function close(newVal){if(typeof newVal=="string"){inp.value=newVal}else{if(closed)return;closed=true;CodeMirror.rmClass(dialog.parentNode,"dialog-opened");dialog.parentNode.removeChild(dialog);me.focus();if(options.onClose)options.onClose(dialog)}}var inp=dialog.getElementsByTagName("input")[0],button;if(inp){inp.focus();if(options.value){inp.value=options.value;if(options.selectValueOnOpen!==false){inp.select()}}if(options.onInput)CodeMirror.on(inp,"input",(function(e){options.onInput(e,inp.value,close)}));if(options.onKeyUp)CodeMirror.on(inp,"keyup",(function(e){options.onKeyUp(e,inp.value,close)}));CodeMirror.on(inp,"keydown",(function(e){if(options&&options.onKeyDown&&options.onKeyDown(e,inp.value,close)){return}if(e.keyCode==27||options.closeOnEnter!==false&&e.keyCode==13){inp.blur();CodeMirror.e_stop(e);close()}if(e.keyCode==13)callback(inp.value,e)}));if(options.closeOnBlur!==false)CodeMirror.on(dialog,"focusout",(function(evt){if(evt.relatedTarget!==null)close()}))}else if(button=dialog.getElementsByTagName("button")[0]){CodeMirror.on(button,"click",(function(){close();me.focus()}));if(options.closeOnBlur!==false)CodeMirror.on(button,"blur",close);button.focus()}return close}));CodeMirror.defineExtension("openConfirm",(function(template,callbacks,options){closeNotification(this,null);var dialog=dialogDiv(this,template,options&&options.bottom);var buttons=dialog.getElementsByTagName("button");var closed=false,me=this,blurring=1;function close(){if(closed)return;closed=true;CodeMirror.rmClass(dialog.parentNode,"dialog-opened");dialog.parentNode.removeChild(dialog);me.focus()}buttons[0].focus();for(var i=0;i<buttons.length;++i){var b=buttons[i];(function(callback){CodeMirror.on(b,"click",(function(e){CodeMirror.e_preventDefault(e);close();if(callback)callback(me)}))})(callbacks[i]);CodeMirror.on(b,"blur",(function(){--blurring;setTimeout((function(){if(blurring<=0)close()}),200)}));CodeMirror.on(b,"focus",(function(){++blurring}))}}));CodeMirror.defineExtension("openNotification",(function(template,options){closeNotification(this,close);var dialog=dialogDiv(this,template,options&&options.bottom);var closed=false,doneTimer;var duration=options&&typeof options.duration!=="undefined"?options.duration:5e3;function close(){if(closed)return;closed=true;clearTimeout(doneTimer);CodeMirror.rmClass(dialog.parentNode,"dialog-opened");dialog.parentNode.removeChild(dialog)}CodeMirror.on(dialog,"click",(function(e){CodeMirror.e_preventDefault(e);close()}));if(duration)doneTimer=setTimeout(close,duration);return close}))}));(function(mod){if(typeof exports=="object"&&typeof module=="object")mod(require("../../lib/codemirror"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror"],mod);else mod(CodeMirror)})((function(CodeMirror){"use strict";function lineIndent(cm,lineNo){var text=cm.getLine(lineNo);var spaceTo=text.search(/\S/);if(spaceTo==-1||/\bcomment\b/.test(cm.getTokenTypeAt(CodeMirror.Pos(lineNo,spaceTo+1))))return-1;return CodeMirror.countColumn(text,null,cm.getOption("tabSize"))}CodeMirror.registerHelper("fold","indent",(function(cm,start){var myIndent=lineIndent(cm,start.line);if(myIndent<0)return;var lastLineInFold=null;for(var i=start.line+1,end=cm.lastLine();i<=end;++i){var indent=lineIndent(cm,i);if(indent==-1){}else if(indent>myIndent){lastLineInFold=i}else{break}}if(lastLineInFold)return{from:CodeMirror.Pos(start.line,cm.getLine(start.line).length),to:CodeMirror.Pos(lastLineInFold,cm.getLine(lastLineInFold).length)}}))}));(function(mod){if(typeof exports=="object"&&typeof module=="object")mod(require("../../lib/codemirror"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror"],mod);else mod(CodeMirror)})((function(CodeMirror){"use strict";var Pos=CodeMirror.Pos;function regexpFlags(regexp){var flags=regexp.flags;return flags!=null?flags:(regexp.ignoreCase?"i":"")+(regexp.global?"g":"")+(regexp.multiline?"m":"")}function ensureFlags(regexp,flags){var current=regexpFlags(regexp),target=current;for(var i=0;i<flags.length;i++)if(target.indexOf(flags.charAt(i))==-1)target+=flags.charAt(i);return current==target?regexp:new RegExp(regexp.source,target)}function maybeMultiline(regexp){return/\\s|\\n|\n|\\W|\\D|\[\^/.test(regexp.source)}function searchRegexpForward(doc,regexp,start){regexp=ensureFlags(regexp,"g");for(var line=start.line,ch=start.ch,last=doc.lastLine();line<=last;line++,ch=0){regexp.lastIndex=ch;var string=doc.getLine(line),match=regexp.exec(string);if(match)return{from:Pos(line,match.index),to:Pos(line,match.index+match[0].length),match:match}}}function searchRegexpForwardMultiline(doc,regexp,start){if(!maybeMultiline(regexp))return searchRegexpForward(doc,regexp,start);regexp=ensureFlags(regexp,"gm");var string,chunk=1;for(var line=start.line,last=doc.lastLine();line<=last;){for(var i=0;i<chunk;i++){if(line>last)break;var curLine=doc.getLine(line++);string=string==null?curLine:string+"\n"+curLine}chunk=chunk*2;regexp.lastIndex=start.ch;var match=regexp.exec(string);if(match){var before=string.slice(0,match.index).split("\n"),inside=match[0].split("\n");var startLine=start.line+before.length-1,startCh=before[before.length-1].length;return{from:Pos(startLine,startCh),to:Pos(startLine+inside.length-1,inside.length==1?startCh+inside[0].length:inside[inside.length-1].length),match:match}}}}function lastMatchIn(string,regexp,endMargin){var match,from=0;while(from<=string.length){regexp.lastIndex=from;var newMatch=regexp.exec(string);if(!newMatch)break;var end=newMatch.index+newMatch[0].length;if(end>string.length-endMargin)break;if(!match||end>match.index+match[0].length)match=newMatch;from=newMatch.index+1}return match}function searchRegexpBackward(doc,regexp,start){regexp=ensureFlags(regexp,"g");for(var line=start.line,ch=start.ch,first=doc.firstLine();line>=first;line--,ch=-1){var string=doc.getLine(line);var match=lastMatchIn(string,regexp,ch<0?0:string.length-ch);if(match)return{from:Pos(line,match.index),to:Pos(line,match.index+match[0].length),match:match}}}function searchRegexpBackwardMultiline(doc,regexp,start){if(!maybeMultiline(regexp))return searchRegexpBackward(doc,regexp,start);regexp=ensureFlags(regexp,"gm");var string,chunkSize=1,endMargin=doc.getLine(start.line).length-start.ch;for(var line=start.line,first=doc.firstLine();line>=first;){for(var i=0;i<chunkSize&&line>=first;i++){var curLine=doc.getLine(line--);string=string==null?curLine:curLine+"\n"+string}chunkSize*=2;var match=lastMatchIn(string,regexp,endMargin);if(match){var before=string.slice(0,match.index).split("\n"),inside=match[0].split("\n");var startLine=line+before.length,startCh=before[before.length-1].length;return{from:Pos(startLine,startCh),to:Pos(startLine+inside.length-1,inside.length==1?startCh+inside[0].length:inside[inside.length-1].length),match:match}}}}var doFold,noFold;if(String.prototype.normalize){doFold=function(str){return str.normalize("NFD").toLowerCase()};noFold=function(str){return str.normalize("NFD")}}else{doFold=function(str){return str.toLowerCase()};noFold=function(str){return str}}function adjustPos(orig,folded,pos,foldFunc){if(orig.length==folded.length)return pos;for(var min=0,max=pos+Math.max(0,orig.length-folded.length);;){if(min==max)return min;var mid=min+max>>1;var len=foldFunc(orig.slice(0,mid)).length;if(len==pos)return mid;else if(len>pos)max=mid;else min=mid+1}}function searchStringForward(doc,query,start,caseFold){if(!query.length)return null;var fold=caseFold?doFold:noFold;var lines=fold(query).split(/\r|\n\r?/);search:for(var line=start.line,ch=start.ch,last=doc.lastLine()+1-lines.length;line<=last;line++,ch=0){var orig=doc.getLine(line).slice(ch),string=fold(orig);if(lines.length==1){var found=string.indexOf(lines[0]);if(found==-1)continue search;var start=adjustPos(orig,string,found,fold)+ch;return{from:Pos(line,adjustPos(orig,string,found,fold)+ch),to:Pos(line,adjustPos(orig,string,found+lines[0].length,fold)+ch)}}else{var cutFrom=string.length-lines[0].length;if(string.slice(cutFrom)!=lines[0])continue search;for(var i=1;i<lines.length-1;i++)if(fold(doc.getLine(line+i))!=lines[i])continue search;var end=doc.getLine(line+lines.length-1),endString=fold(end),lastLine=lines[lines.length-1];if(endString.slice(0,lastLine.length)!=lastLine)continue search;return{from:Pos(line,adjustPos(orig,string,cutFrom,fold)+ch),to:Pos(line+lines.length-1,adjustPos(end,endString,lastLine.length,fold))}}}}function searchStringBackward(doc,query,start,caseFold){if(!query.length)return null;var fold=caseFold?doFold:noFold;var lines=fold(query).split(/\r|\n\r?/);search:for(var line=start.line,ch=start.ch,first=doc.firstLine()-1+lines.length;line>=first;line--,ch=-1){var orig=doc.getLine(line);if(ch>-1)orig=orig.slice(0,ch);var string=fold(orig);if(lines.length==1){var found=string.lastIndexOf(lines[0]);if(found==-1)continue search;return{from:Pos(line,adjustPos(orig,string,found,fold)),to:Pos(line,adjustPos(orig,string,found+lines[0].length,fold))}}else{var lastLine=lines[lines.length-1];if(string.slice(0,lastLine.length)!=lastLine)continue search;for(var i=1,start=line-lines.length+1;i<lines.length-1;i++)if(fold(doc.getLine(start+i))!=lines[i])continue search;var top=doc.getLine(line+1-lines.length),topString=fold(top);if(topString.slice(topString.length-lines[0].length)!=lines[0])continue search;return{from:Pos(line+1-lines.length,adjustPos(top,topString,top.length-lines[0].length,fold)),to:Pos(line,adjustPos(orig,string,lastLine.length,fold))}}}}function SearchCursor(doc,query,pos,options){this.atOccurrence=false;this.afterEmptyMatch=false;this.doc=doc;pos=pos?doc.clipPos(pos):Pos(0,0);this.pos={from:pos,to:pos};var caseFold;if(typeof options=="object"){caseFold=options.caseFold}else{caseFold=options;options=null}if(typeof query=="string"){if(caseFold==null)caseFold=false;this.matches=function(reverse,pos){return(reverse?searchStringBackward:searchStringForward)(doc,query,pos,caseFold)}}else{query=ensureFlags(query,"gm");if(!options||options.multiline!==false)this.matches=function(reverse,pos){return(reverse?searchRegexpBackwardMultiline:searchRegexpForwardMultiline)(doc,query,pos)};else this.matches=function(reverse,pos){return(reverse?searchRegexpBackward:searchRegexpForward)(doc,query,pos)}}}SearchCursor.prototype={findNext:function(){return this.find(false)},findPrevious:function(){return this.find(true)},find:function(reverse){var head=this.doc.clipPos(reverse?this.pos.from:this.pos.to);if(this.afterEmptyMatch&&this.atOccurrence){head=Pos(head.line,head.ch);if(reverse){head.ch--;if(head.ch<0){head.line--;head.ch=(this.doc.getLine(head.line)||"").length}}else{head.ch++;if(head.ch>(this.doc.getLine(head.line)||"").length){head.ch=0;head.line++}}if(CodeMirror.cmpPos(head,this.doc.clipPos(head))!=0){return this.atOccurrence=false}}var result=this.matches(reverse,head);this.afterEmptyMatch=result&&CodeMirror.cmpPos(result.from,result.to)==0;if(result){this.pos=result;this.atOccurrence=true;return this.pos.match||true}else{var end=Pos(reverse?this.doc.firstLine():this.doc.lastLine()+1,0);this.pos={from:end,to:end};return this.atOccurrence=false}},from:function(){if(this.atOccurrence)return this.pos.from},to:function(){if(this.atOccurrence)return this.pos.to},replace:function(newText,origin){if(!this.atOccurrence)return;var lines=CodeMirror.splitLines(newText);this.doc.replaceRange(lines,this.pos.from,this.pos.to,origin);this.pos.to=Pos(this.pos.from.line+lines.length-1,lines[lines.length-1].length+(lines.length==1?this.pos.from.ch:0))}};CodeMirror.defineExtension("getSearchCursor",(function(query,pos,caseFold){return new SearchCursor(this.doc,query,pos,caseFold)}));CodeMirror.defineDocExtension("getSearchCursor",(function(query,pos,caseFold){return new SearchCursor(this,query,pos,caseFold)}));CodeMirror.defineExtension("selectMatches",(function(query,caseFold){var ranges=[];var cur=this.getSearchCursor(query,this.getCursor("from"),caseFold);while(cur.findNext()){if(CodeMirror.cmpPos(cur.to(),this.getCursor("to"))>0)break;ranges.push({anchor:cur.from(),head:cur.to()})}if(ranges.length)this.setSelections(ranges,0)}))}));(function(mod){if(typeof exports=="object"&&typeof module=="object")mod(require("../../lib/codemirror"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror"],mod);else mod(CodeMirror)})((function(CodeMirror){"use strict";CodeMirror.defineMode("javascript",(function(config,parserConfig){var indentUnit=config.indentUnit;var statementIndent=parserConfig.statementIndent;var jsonldMode=parserConfig.jsonld;var jsonMode=parserConfig.json||jsonldMode;var trackScope=parserConfig.trackScope!==false;var isTS=parserConfig.typescript;var wordRE=parserConfig.wordCharacters||/[\w$\xa1-\uffff]/;var keywords=function(){function kw(type){return{type:type,style:"keyword"}}var A=kw("keyword a"),B=kw("keyword b"),C=kw("keyword c"),D=kw("keyword d");var operator=kw("operator"),atom={type:"atom",style:"atom"};return{if:kw("if"),while:A,with:A,else:B,do:B,try:B,finally:B,return:D,break:D,continue:D,new:kw("new"),delete:C,void:C,throw:C,debugger:kw("debugger"),var:kw("var"),const:kw("var"),let:kw("var"),function:kw("function"),catch:kw("catch"),for:kw("for"),switch:kw("switch"),case:kw("case"),default:kw("default"),in:operator,typeof:operator,instanceof:operator,true:atom,false:atom,null:atom,undefined:atom,NaN:atom,Infinity:atom,this:kw("this"),class:kw("class"),super:kw("atom"),yield:C,export:kw("export"),import:kw("import"),extends:C,await:C}}();var isOperatorChar=/[+\-*&%=<>!?|~^@]/;var isJsonldKeyword=/^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;function readRegexp(stream){var escaped=false,next,inSet=false;while((next=stream.next())!=null){if(!escaped){if(next=="/"&&!inSet)return;if(next=="[")inSet=true;else if(inSet&&next=="]")inSet=false}escaped=!escaped&&next=="\\"}}var type,content;function ret(tp,style,cont){type=tp;content=cont;return style}function tokenBase(stream,state){var ch=stream.next();if(ch=='"'||ch=="'"){state.tokenize=tokenString(ch);return state.tokenize(stream,state)}else if(ch=="."&&stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)){return ret("number","number")}else if(ch=="."&&stream.match("..")){return ret("spread","meta")}else if(/[\[\]{}\(\),;\:\.]/.test(ch)){return ret(ch)}else if(ch=="="&&stream.eat(">")){return ret("=>","operator")}else if(ch=="0"&&stream.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/)){return ret("number","number")}else if(/\d/.test(ch)){stream.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/);return ret("number","number")}else if(ch=="/"){if(stream.eat("*")){state.tokenize=tokenComment;return tokenComment(stream,state)}else if(stream.eat("/")){stream.skipToEnd();return ret("comment","comment")}else if(expressionAllowed(stream,state,1)){readRegexp(stream);stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/);return ret("regexp","string-2")}else{stream.eat("=");return ret("operator","operator",stream.current())}}else if(ch=="`"){state.tokenize=tokenQuasi;return tokenQuasi(stream,state)}else if(ch=="#"&&stream.peek()=="!"){stream.skipToEnd();return ret("meta","meta")}else if(ch=="#"&&stream.eatWhile(wordRE)){return ret("variable","property")}else if(ch=="<"&&stream.match("!--")||ch=="-"&&stream.match("->")&&!/\S/.test(stream.string.slice(0,stream.start))){stream.skipToEnd();return ret("comment","comment")}else if(isOperatorChar.test(ch)){if(ch!=">"||!state.lexical||state.lexical.type!=">"){if(stream.eat("=")){if(ch=="!"||ch=="=")stream.eat("=")}else if(/[<>*+\-|&?]/.test(ch)){stream.eat(ch);if(ch==">")stream.eat(ch)}}if(ch=="?"&&stream.eat("."))return ret(".");return ret("operator","operator",stream.current())}else if(wordRE.test(ch)){stream.eatWhile(wordRE);var word=stream.current();if(state.lastType!="."){if(keywords.propertyIsEnumerable(word)){var kw=keywords[word];return ret(kw.type,kw.style,word)}if(word=="async"&&stream.match(/^(\s|\/\*([^*]|\*(?!\/))*?\*\/)*[\[\(\w]/,false))return ret("async","keyword",word)}return ret("variable","variable",word)}}function tokenString(quote){return function(stream,state){var escaped=false,next;if(jsonldMode&&stream.peek()=="@"&&stream.match(isJsonldKeyword)){state.tokenize=tokenBase;return ret("jsonld-keyword","meta")}while((next=stream.next())!=null){if(next==quote&&!escaped)break;escaped=!escaped&&next=="\\"}if(!escaped)state.tokenize=tokenBase;return ret("string","string")}}function tokenComment(stream,state){var maybeEnd=false,ch;while(ch=stream.next()){if(ch=="/"&&maybeEnd){state.tokenize=tokenBase;break}maybeEnd=ch=="*"}return ret("comment","comment")}function tokenQuasi(stream,state){var escaped=false,next;while((next=stream.next())!=null){if(!escaped&&(next=="`"||next=="$"&&stream.eat("{"))){state.tokenize=tokenBase;break}escaped=!escaped&&next=="\\"}return ret("quasi","string-2",stream.current())}var brackets="([{}])";function findFatArrow(stream,state){if(state.fatArrowAt)state.fatArrowAt=null;var arrow=stream.string.indexOf("=>",stream.start);if(arrow<0)return;if(isTS){var m=/:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start,arrow));if(m)arrow=m.index}var depth=0,sawSomething=false;for(var pos=arrow-1;pos>=0;--pos){var ch=stream.string.charAt(pos);var bracket=brackets.indexOf(ch);if(bracket>=0&&bracket<3){if(!depth){++pos;break}if(--depth==0){if(ch=="(")sawSomething=true;break}}else if(bracket>=3&&bracket<6){++depth}else if(wordRE.test(ch)){sawSomething=true}else if(/["'\/`]/.test(ch)){for(;;--pos){if(pos==0)return;var next=stream.string.charAt(pos-1);if(next==ch&&stream.string.charAt(pos-2)!="\\"){pos--;break}}}else if(sawSomething&&!depth){++pos;break}}if(sawSomething&&!depth)state.fatArrowAt=pos}var atomicTypes={atom:true,number:true,variable:true,string:true,regexp:true,this:true,import:true,"jsonld-keyword":true};function JSLexical(indented,column,type,align,prev,info){this.indented=indented;this.column=column;this.type=type;this.prev=prev;this.info=info;if(align!=null)this.align=align}function inScope(state,varname){if(!trackScope)return false;for(var v=state.localVars;v;v=v.next)if(v.name==varname)return true;for(var cx=state.context;cx;cx=cx.prev){for(var v=cx.vars;v;v=v.next)if(v.name==varname)return true}}function parseJS(state,style,type,content,stream){var cc=state.cc;cx.state=state;cx.stream=stream;cx.marked=null,cx.cc=cc;cx.style=style;if(!state.lexical.hasOwnProperty("align"))state.lexical.align=true;while(true){var combinator=cc.length?cc.pop():jsonMode?expression:statement;if(combinator(type,content)){while(cc.length&&cc[cc.length-1].lex)cc.pop()();if(cx.marked)return cx.marked;if(type=="variable"&&inScope(state,content))return"variable-2";return style}}}var cx={state:null,column:null,marked:null,cc:null};function pass(){for(var i=arguments.length-1;i>=0;i--)cx.cc.push(arguments[i])}function cont(){pass.apply(null,arguments);return true}function inList(name,list){for(var v=list;v;v=v.next)if(v.name==name)return true;return false}function register(varname){var state=cx.state;cx.marked="def";if(!trackScope)return;if(state.context){if(state.lexical.info=="var"&&state.context&&state.context.block){var newContext=registerVarScoped(varname,state.context);if(newContext!=null){state.context=newContext;return}}else if(!inList(varname,state.localVars)){state.localVars=new Var(varname,state.localVars);return}}if(parserConfig.globalVars&&!inList(varname,state.globalVars))state.globalVars=new Var(varname,state.globalVars)}function registerVarScoped(varname,context){if(!context){return null}else if(context.block){var inner=registerVarScoped(varname,context.prev);if(!inner)return null;if(inner==context.prev)return context;return new Context(inner,context.vars,true)}else if(inList(varname,context.vars)){return context}else{return new Context(context.prev,new Var(varname,context.vars),false)}}function isModifier(name){return name=="public"||name=="private"||name=="protected"||name=="abstract"||name=="readonly"}function Context(prev,vars,block){this.prev=prev;this.vars=vars;this.block=block}function Var(name,next){this.name=name;this.next=next}var defaultVars=new Var("this",new Var("arguments",null));function pushcontext(){cx.state.context=new Context(cx.state.context,cx.state.localVars,false);cx.state.localVars=defaultVars}function pushblockcontext(){cx.state.context=new Context(cx.state.context,cx.state.localVars,true);cx.state.localVars=null}pushcontext.lex=pushblockcontext.lex=true;function popcontext(){cx.state.localVars=cx.state.context.vars;cx.state.context=cx.state.context.prev}popcontext.lex=true;function pushlex(type,info){var result=function(){var state=cx.state,indent=state.indented;if(state.lexical.type=="stat")indent=state.lexical.indented;else for(var outer=state.lexical;outer&&outer.type==")"&&outer.align;outer=outer.prev)indent=outer.indented;state.lexical=new JSLexical(indent,cx.stream.column(),type,null,state.lexical,info)};result.lex=true;return result}function poplex(){var state=cx.state;if(state.lexical.prev){if(state.lexical.type==")")state.indented=state.lexical.indented;state.lexical=state.lexical.prev}}poplex.lex=true;function expect(wanted){function exp(type){if(type==wanted)return cont();else if(wanted==";"||type=="}"||type==")"||type=="]")return pass();else return cont(exp)}return exp}function statement(type,value){if(type=="var")return cont(pushlex("vardef",value),vardef,expect(";"),poplex);if(type=="keyword a")return cont(pushlex("form"),parenExpr,statement,poplex);if(type=="keyword b")return cont(pushlex("form"),statement,poplex);if(type=="keyword d")return cx.stream.match(/^\s*$/,false)?cont():cont(pushlex("stat"),maybeexpression,expect(";"),poplex);if(type=="debugger")return cont(expect(";"));if(type=="{")return cont(pushlex("}"),pushblockcontext,block,poplex,popcontext);if(type==";")return cont();if(type=="if"){if(cx.state.lexical.info=="else"&&cx.state.cc[cx.state.cc.length-1]==poplex)cx.state.cc.pop()();return cont(pushlex("form"),parenExpr,statement,poplex,maybeelse)}if(type=="function")return cont(functiondef);if(type=="for")return cont(pushlex("form"),pushblockcontext,forspec,statement,popcontext,poplex);if(type=="class"||isTS&&value=="interface"){cx.marked="keyword";return cont(pushlex("form",type=="class"?type:value),className,poplex)}if(type=="variable"){if(isTS&&value=="declare"){cx.marked="keyword";return cont(statement)}else if(isTS&&(value=="module"||value=="enum"||value=="type")&&cx.stream.match(/^\s*\w/,false)){cx.marked="keyword";if(value=="enum")return cont(enumdef);else if(value=="type")return cont(typename,expect("operator"),typeexpr,expect(";"));else return cont(pushlex("form"),pattern,expect("{"),pushlex("}"),block,poplex,poplex)}else if(isTS&&value=="namespace"){cx.marked="keyword";return cont(pushlex("form"),expression,statement,poplex)}else if(isTS&&value=="abstract"){cx.marked="keyword";return cont(statement)}else{return cont(pushlex("stat"),maybelabel)}}if(type=="switch")return cont(pushlex("form"),parenExpr,expect("{"),pushlex("}","switch"),pushblockcontext,block,poplex,poplex,popcontext);if(type=="case")return cont(expression,expect(":"));if(type=="default")return cont(expect(":"));if(type=="catch")return cont(pushlex("form"),pushcontext,maybeCatchBinding,statement,poplex,popcontext);if(type=="export")return cont(pushlex("stat"),afterExport,poplex);if(type=="import")return cont(pushlex("stat"),afterImport,poplex);if(type=="async")return cont(statement);if(value=="@")return cont(expression,statement);return pass(pushlex("stat"),expression,expect(";"),poplex)}function maybeCatchBinding(type){if(type=="(")return cont(funarg,expect(")"))}function expression(type,value){return expressionInner(type,value,false)}function expressionNoComma(type,value){return expressionInner(type,value,true)}function parenExpr(type){if(type!="(")return pass();return cont(pushlex(")"),maybeexpression,expect(")"),poplex)}function expressionInner(type,value,noComma){if(cx.state.fatArrowAt==cx.stream.start){var body=noComma?arrowBodyNoComma:arrowBody;if(type=="(")return cont(pushcontext,pushlex(")"),commasep(funarg,")"),poplex,expect("=>"),body,popcontext);else if(type=="variable")return pass(pushcontext,pattern,expect("=>"),body,popcontext)}var maybeop=noComma?maybeoperatorNoComma:maybeoperatorComma;if(atomicTypes.hasOwnProperty(type))return cont(maybeop);if(type=="function")return cont(functiondef,maybeop);if(type=="class"||isTS&&value=="interface"){cx.marked="keyword";return cont(pushlex("form"),classExpression,poplex)}if(type=="keyword c"||type=="async")return cont(noComma?expressionNoComma:expression);if(type=="(")return cont(pushlex(")"),maybeexpression,expect(")"),poplex,maybeop);if(type=="operator"||type=="spread")return cont(noComma?expressionNoComma:expression);if(type=="[")return cont(pushlex("]"),arrayLiteral,poplex,maybeop);if(type=="{")return contCommasep(objprop,"}",null,maybeop);if(type=="quasi")return pass(quasi,maybeop);if(type=="new")return cont(maybeTarget(noComma));return cont()}function maybeexpression(type){if(type.match(/[;\}\)\],]/))return pass();return pass(expression)}function maybeoperatorComma(type,value){if(type==",")return cont(maybeexpression);return maybeoperatorNoComma(type,value,false)}function maybeoperatorNoComma(type,value,noComma){var me=noComma==false?maybeoperatorComma:maybeoperatorNoComma;var expr=noComma==false?expression:expressionNoComma;if(type=="=>")return cont(pushcontext,noComma?arrowBodyNoComma:arrowBody,popcontext);if(type=="operator"){if(/\+\+|--/.test(value)||isTS&&value=="!")return cont(me);if(isTS&&value=="<"&&cx.stream.match(/^([^<>]|<[^<>]*>)*>\s*\(/,false))return cont(pushlex(">"),commasep(typeexpr,">"),poplex,me);if(value=="?")return cont(expression,expect(":"),expr);return cont(expr)}if(type=="quasi"){return pass(quasi,me)}if(type==";")return;if(type=="(")return contCommasep(expressionNoComma,")","call",me);if(type==".")return cont(property,me);if(type=="[")return cont(pushlex("]"),maybeexpression,expect("]"),poplex,me);if(isTS&&value=="as"){cx.marked="keyword";return cont(typeexpr,me)}if(type=="regexp"){cx.state.lastType=cx.marked="operator";cx.stream.backUp(cx.stream.pos-cx.stream.start-1);return cont(expr)}}function quasi(type,value){if(type!="quasi")return pass();if(value.slice(value.length-2)!="${")return cont(quasi);return cont(maybeexpression,continueQuasi)}function continueQuasi(type){if(type=="}"){cx.marked="string-2";cx.state.tokenize=tokenQuasi;return cont(quasi)}}function arrowBody(type){findFatArrow(cx.stream,cx.state);return pass(type=="{"?statement:expression)}function arrowBodyNoComma(type){findFatArrow(cx.stream,cx.state);return pass(type=="{"?statement:expressionNoComma)}function maybeTarget(noComma){return function(type){if(type==".")return cont(noComma?targetNoComma:target);else if(type=="variable"&&isTS)return cont(maybeTypeArgs,noComma?maybeoperatorNoComma:maybeoperatorComma);else return pass(noComma?expressionNoComma:expression)}}function target(_,value){if(value=="target"){cx.marked="keyword";return cont(maybeoperatorComma)}}function targetNoComma(_,value){if(value=="target"){cx.marked="keyword";return cont(maybeoperatorNoComma)}}function maybelabel(type){if(type==":")return cont(poplex,statement);return pass(maybeoperatorComma,expect(";"),poplex)}function property(type){if(type=="variable"){cx.marked="property";return cont()}}function objprop(type,value){if(type=="async"){cx.marked="property";return cont(objprop)}else if(type=="variable"||cx.style=="keyword"){cx.marked="property";if(value=="get"||value=="set")return cont(getterSetter);var m;if(isTS&&cx.state.fatArrowAt==cx.stream.start&&(m=cx.stream.match(/^\s*:\s*/,false)))cx.state.fatArrowAt=cx.stream.pos+m[0].length;return cont(afterprop)}else if(type=="number"||type=="string"){cx.marked=jsonldMode?"property":cx.style+" property";return cont(afterprop)}else if(type=="jsonld-keyword"){return cont(afterprop)}else if(isTS&&isModifier(value)){cx.marked="keyword";return cont(objprop)}else if(type=="["){return cont(expression,maybetype,expect("]"),afterprop)}else if(type=="spread"){return cont(expressionNoComma,afterprop)}else if(value=="*"){cx.marked="keyword";return cont(objprop)}else if(type==":"){return pass(afterprop)}}function getterSetter(type){if(type!="variable")return pass(afterprop);cx.marked="property";return cont(functiondef)}function afterprop(type){if(type==":")return cont(expressionNoComma);if(type=="(")return pass(functiondef)}function commasep(what,end,sep){function proceed(type,value){if(sep?sep.indexOf(type)>-1:type==","){var lex=cx.state.lexical;if(lex.info=="call")lex.pos=(lex.pos||0)+1;return cont((function(type,value){if(type==end||value==end)return pass();return pass(what)}),proceed)}if(type==end||value==end)return cont();if(sep&&sep.indexOf(";")>-1)return pass(what);return cont(expect(end))}return function(type,value){if(type==end||value==end)return cont();return pass(what,proceed)}}function contCommasep(what,end,info){for(var i=3;i<arguments.length;i++)cx.cc.push(arguments[i]);return cont(pushlex(end,info),commasep(what,end),poplex)}function block(type){if(type=="}")return cont();return pass(statement,block)}function maybetype(type,value){if(isTS){if(type==":")return cont(typeexpr);if(value=="?")return cont(maybetype)}}function maybetypeOrIn(type,value){if(isTS&&(type==":"||value=="in"))return cont(typeexpr)}function mayberettype(type){if(isTS&&type==":"){if(cx.stream.match(/^\s*\w+\s+is\b/,false))return cont(expression,isKW,typeexpr);else return cont(typeexpr)}}function isKW(_,value){if(value=="is"){cx.marked="keyword";return cont()}}function typeexpr(type,value){if(value=="keyof"||value=="typeof"||value=="infer"||value=="readonly"){cx.marked="keyword";return cont(value=="typeof"?expressionNoComma:typeexpr)}if(type=="variable"||value=="void"){cx.marked="type";return cont(afterType)}if(value=="|"||value=="&")return cont(typeexpr);if(type=="string"||type=="number"||type=="atom")return cont(afterType);if(type=="[")return cont(pushlex("]"),commasep(typeexpr,"]",","),poplex,afterType);if(type=="{")return cont(pushlex("}"),typeprops,poplex,afterType);if(type=="(")return cont(commasep(typearg,")"),maybeReturnType,afterType);if(type=="<")return cont(commasep(typeexpr,">"),typeexpr);if(type=="quasi"){return pass(quasiType,afterType)}}function maybeReturnType(type){if(type=="=>")return cont(typeexpr)}function typeprops(type){if(type.match(/[\}\)\]]/))return cont();if(type==","||type==";")return cont(typeprops);return pass(typeprop,typeprops)}function typeprop(type,value){if(type=="variable"||cx.style=="keyword"){cx.marked="property";return cont(typeprop)}else if(value=="?"||type=="number"||type=="string"){return cont(typeprop)}else if(type==":"){return cont(typeexpr)}else if(type=="["){return cont(expect("variable"),maybetypeOrIn,expect("]"),typeprop)}else if(type=="("){return pass(functiondecl,typeprop)}else if(!type.match(/[;\}\)\],]/)){return cont()}}function quasiType(type,value){if(type!="quasi")return pass();if(value.slice(value.length-2)!="${")return cont(quasiType);return cont(typeexpr,continueQuasiType)}function continueQuasiType(type){if(type=="}"){cx.marked="string-2";cx.state.tokenize=tokenQuasi;return cont(quasiType)}}function typearg(type,value){if(type=="variable"&&cx.stream.match(/^\s*[?:]/,false)||value=="?")return cont(typearg);if(type==":")return cont(typeexpr);if(type=="spread")return cont(typearg);return pass(typeexpr)}function afterType(type,value){if(value=="<")return cont(pushlex(">"),commasep(typeexpr,">"),poplex,afterType);if(value=="|"||type=="."||value=="&")return cont(typeexpr);if(type=="[")return cont(typeexpr,expect("]"),afterType);if(value=="extends"||value=="implements"){cx.marked="keyword";return cont(typeexpr)}if(value=="?")return cont(typeexpr,expect(":"),typeexpr)}function maybeTypeArgs(_,value){if(value=="<")return cont(pushlex(">"),commasep(typeexpr,">"),poplex,afterType)}function typeparam(){return pass(typeexpr,maybeTypeDefault)}function maybeTypeDefault(_,value){if(value=="=")return cont(typeexpr)}function vardef(_,value){if(value=="enum"){cx.marked="keyword";return cont(enumdef)}return pass(pattern,maybetype,maybeAssign,vardefCont)}function pattern(type,value){if(isTS&&isModifier(value)){cx.marked="keyword";return cont(pattern)}if(type=="variable"){register(value);return cont()}if(type=="spread")return cont(pattern);if(type=="[")return contCommasep(eltpattern,"]");if(type=="{")return contCommasep(proppattern,"}")}function proppattern(type,value){if(type=="variable"&&!cx.stream.match(/^\s*:/,false)){register(value);return cont(maybeAssign)}if(type=="variable")cx.marked="property";if(type=="spread")return cont(pattern);if(type=="}")return pass();if(type=="[")return cont(expression,expect("]"),expect(":"),proppattern);return cont(expect(":"),pattern,maybeAssign)}function eltpattern(){return pass(pattern,maybeAssign)}function maybeAssign(_type,value){if(value=="=")return cont(expressionNoComma)}function vardefCont(type){if(type==",")return cont(vardef)}function maybeelse(type,value){if(type=="keyword b"&&value=="else")return cont(pushlex("form","else"),statement,poplex)}function forspec(type,value){if(value=="await")return cont(forspec);if(type=="(")return cont(pushlex(")"),forspec1,poplex)}function forspec1(type){if(type=="var")return cont(vardef,forspec2);if(type=="variable")return cont(forspec2);return pass(forspec2)}function forspec2(type,value){if(type==")")return cont();if(type==";")return cont(forspec2);if(value=="in"||value=="of"){cx.marked="keyword";return cont(expression,forspec2)}return pass(expression,forspec2)}function functiondef(type,value){if(value=="*"){cx.marked="keyword";return cont(functiondef)}if(type=="variable"){register(value);return cont(functiondef)}if(type=="(")return cont(pushcontext,pushlex(")"),commasep(funarg,")"),poplex,mayberettype,statement,popcontext);if(isTS&&value=="<")return cont(pushlex(">"),commasep(typeparam,">"),poplex,functiondef)}function functiondecl(type,value){if(value=="*"){cx.marked="keyword";return cont(functiondecl)}if(type=="variable"){register(value);return cont(functiondecl)}if(type=="(")return cont(pushcontext,pushlex(")"),commasep(funarg,")"),poplex,mayberettype,popcontext);if(isTS&&value=="<")return cont(pushlex(">"),commasep(typeparam,">"),poplex,functiondecl)}function typename(type,value){if(type=="keyword"||type=="variable"){cx.marked="type";return cont(typename)}else if(value=="<"){return cont(pushlex(">"),commasep(typeparam,">"),poplex)}}function funarg(type,value){if(value=="@")cont(expression,funarg);if(type=="spread")return cont(funarg);if(isTS&&isModifier(value)){cx.marked="keyword";return cont(funarg)}if(isTS&&type=="this")return cont(maybetype,maybeAssign);return pass(pattern,maybetype,maybeAssign)}function classExpression(type,value){if(type=="variable")return className(type,value);return classNameAfter(type,value)}function className(type,value){if(type=="variable"){register(value);return cont(classNameAfter)}}function classNameAfter(type,value){if(value=="<")return cont(pushlex(">"),commasep(typeparam,">"),poplex,classNameAfter);if(value=="extends"||value=="implements"||isTS&&type==","){if(value=="implements")cx.marked="keyword";return cont(isTS?typeexpr:expression,classNameAfter)}if(type=="{")return cont(pushlex("}"),classBody,poplex)}function classBody(type,value){if(type=="async"||type=="variable"&&(value=="static"||value=="get"||value=="set"||isTS&&isModifier(value))&&cx.stream.match(/^\s+#?[\w$\xa1-\uffff]/,false)){cx.marked="keyword";return cont(classBody)}if(type=="variable"||cx.style=="keyword"){cx.marked="property";return cont(classfield,classBody)}if(type=="number"||type=="string")return cont(classfield,classBody);if(type=="[")return cont(expression,maybetype,expect("]"),classfield,classBody);if(value=="*"){cx.marked="keyword";return cont(classBody)}if(isTS&&type=="(")return pass(functiondecl,classBody);if(type==";"||type==",")return cont(classBody);if(type=="}")return cont();if(value=="@")return cont(expression,classBody)}function classfield(type,value){if(value=="!")return cont(classfield);if(value=="?")return cont(classfield);if(type==":")return cont(typeexpr,maybeAssign);if(value=="=")return cont(expressionNoComma);var context=cx.state.lexical.prev,isInterface=context&&context.info=="interface";return pass(isInterface?functiondecl:functiondef)}function afterExport(type,value){if(value=="*"){cx.marked="keyword";return cont(maybeFrom,expect(";"))}if(value=="default"){cx.marked="keyword";return cont(expression,expect(";"))}if(type=="{")return cont(commasep(exportField,"}"),maybeFrom,expect(";"));return pass(statement)}function exportField(type,value){if(value=="as"){cx.marked="keyword";return cont(expect("variable"))}if(type=="variable")return pass(expressionNoComma,exportField)}function afterImport(type){if(type=="string")return cont();if(type=="(")return pass(expression);if(type==".")return pass(maybeoperatorComma);return pass(importSpec,maybeMoreImports,maybeFrom)}function importSpec(type,value){if(type=="{")return contCommasep(importSpec,"}");if(type=="variable")register(value);if(value=="*")cx.marked="keyword";return cont(maybeAs)}function maybeMoreImports(type){if(type==",")return cont(importSpec,maybeMoreImports)}function maybeAs(_type,value){if(value=="as"){cx.marked="keyword";return cont(importSpec)}}function maybeFrom(_type,value){if(value=="from"){cx.marked="keyword";return cont(expression)}}function arrayLiteral(type){if(type=="]")return cont();return pass(commasep(expressionNoComma,"]"))}function enumdef(){return pass(pushlex("form"),pattern,expect("{"),pushlex("}"),commasep(enummember,"}"),poplex,poplex)}function enummember(){return pass(pattern,maybeAssign)}function isContinuedStatement(state,textAfter){return state.lastType=="operator"||state.lastType==","||isOperatorChar.test(textAfter.charAt(0))||/[,.]/.test(textAfter.charAt(0))}function expressionAllowed(stream,state,backUp){return state.tokenize==tokenBase&&/^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType)||state.lastType=="quasi"&&/\{\s*$/.test(stream.string.slice(0,stream.pos-(backUp||0)))}return{startState:function(basecolumn){var state={tokenize:tokenBase,lastType:"sof",cc:[],lexical:new JSLexical((basecolumn||0)-indentUnit,0,"block",false),localVars:parserConfig.localVars,context:parserConfig.localVars&&new Context(null,null,false),indented:basecolumn||0};if(parserConfig.globalVars&&typeof parserConfig.globalVars=="object")state.globalVars=parserConfig.globalVars;return state},token:function(stream,state){if(stream.sol()){if(!state.lexical.hasOwnProperty("align"))state.lexical.align=false;state.indented=stream.indentation();findFatArrow(stream,state)}if(state.tokenize!=tokenComment&&stream.eatSpace())return null;var style=state.tokenize(stream,state);if(type=="comment")return style;state.lastType=type=="operator"&&(content=="++"||content=="--")?"incdec":type;return parseJS(state,style,type,content,stream)},indent:function(state,textAfter){if(state.tokenize==tokenComment||state.tokenize==tokenQuasi)return CodeMirror.Pass;if(state.tokenize!=tokenBase)return 0;var firstChar=textAfter&&textAfter.charAt(0),lexical=state.lexical,top;if(!/^\s*else\b/.test(textAfter))for(var i=state.cc.length-1;i>=0;--i){var c=state.cc[i];if(c==poplex)lexical=lexical.prev;else if(c!=maybeelse&&c!=popcontext)break}while((lexical.type=="stat"||lexical.type=="form")&&(firstChar=="}"||(top=state.cc[state.cc.length-1])&&(top==maybeoperatorComma||top==maybeoperatorNoComma)&&!/^[,\.=+\-*:?[\(]/.test(textAfter)))lexical=lexical.prev;if(statementIndent&&lexical.type==")"&&lexical.prev.type=="stat")lexical=lexical.prev;var type=lexical.type,closing=firstChar==type;if(type=="vardef")return lexical.indented+(state.lastType=="operator"||state.lastType==","?lexical.info.length+1:0);else if(type=="form"&&firstChar=="{")return lexical.indented;else if(type=="form")return lexical.indented+indentUnit;else if(type=="stat")return lexical.indented+(isContinuedStatement(state,textAfter)?statementIndent||indentUnit:0);else if(lexical.info=="switch"&&!closing&&parserConfig.doubleIndentSwitch!=false)return lexical.indented+(/^(?:case|default)\b/.test(textAfter)?indentUnit:2*indentUnit);else if(lexical.align)return lexical.column+(closing?0:1);else return lexical.indented+(closing?0:indentUnit)},electricInput:/^\s*(?:case .*?:|default:|\{|\})$/,blockCommentStart:jsonMode?null:"/*",blockCommentEnd:jsonMode?null:"*/",blockCommentContinue:jsonMode?null:" * ",lineComment:jsonMode?null:"//",fold:"brace",closeBrackets:"()[]{}''\"\"``",helperType:jsonMode?"json":"javascript",jsonldMode:jsonldMode,jsonMode:jsonMode,expressionAllowed:expressionAllowed,skipExpression:function(state){parseJS(state,"atom","atom","true",new CodeMirror.StringStream("",2,null))}}}));CodeMirror.registerHelper("wordChars","javascript",/[\w$]/);CodeMirror.defineMIME("text/javascript","javascript");CodeMirror.defineMIME("text/ecmascript","javascript");CodeMirror.defineMIME("application/javascript","javascript");CodeMirror.defineMIME("application/x-javascript","javascript");CodeMirror.defineMIME("application/ecmascript","javascript");CodeMirror.defineMIME("application/json",{name:"javascript",json:true});CodeMirror.defineMIME("application/x-json",{name:"javascript",json:true});CodeMirror.defineMIME("application/manifest+json",{name:"javascript",json:true});CodeMirror.defineMIME("application/ld+json",{name:"javascript",jsonld:true});CodeMirror.defineMIME("text/typescript",{name:"javascript",typescript:true});CodeMirror.defineMIME("application/typescript",{name:"javascript",typescript:true})}));(function(mod){if(typeof exports=="object"&&typeof module=="object")mod(require("../../lib/codemirror"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror"],mod);else mod(CodeMirror)})((function(CodeMirror){"use strict";CodeMirror.defineExtension("annotateScrollbar",(function(options){if(typeof options=="string")options={className:options};return new Annotation(this,options)}));CodeMirror.defineOption("scrollButtonHeight",0);function Annotation(cm,options){this.cm=cm;this.options=options;this.buttonHeight=options.scrollButtonHeight||cm.getOption("scrollButtonHeight");this.annotations=[];this.doRedraw=this.doUpdate=null;this.div=cm.getWrapperElement().appendChild(document.createElement("div"));this.div.style.cssText="position: absolute; right: 0; top: 0; z-index: 7; pointer-events: none";this.computeScale();function scheduleRedraw(delay){clearTimeout(self.doRedraw);self.doRedraw=setTimeout((function(){self.redraw()}),delay)}var self=this;cm.on("refresh",this.resizeHandler=function(){clearTimeout(self.doUpdate);self.doUpdate=setTimeout((function(){if(self.computeScale())scheduleRedraw(20)}),100)});cm.on("markerAdded",this.resizeHandler);cm.on("markerCleared",this.resizeHandler);if(options.listenForChanges!==false)cm.on("changes",this.changeHandler=function(){scheduleRedraw(250)})}Annotation.prototype.computeScale=function(){var cm=this.cm;var hScale=(cm.getWrapperElement().clientHeight-cm.display.barHeight-this.buttonHeight*2)/cm.getScrollerElement().scrollHeight;if(hScale!=this.hScale){this.hScale=hScale;return true}};Annotation.prototype.update=function(annotations){this.annotations=annotations;this.redraw()};Annotation.prototype.redraw=function(compute){if(compute!==false)this.computeScale();var cm=this.cm,hScale=this.hScale;var frag=document.createDocumentFragment(),anns=this.annotations;var wrapping=cm.getOption("lineWrapping");var singleLineH=wrapping&&cm.defaultTextHeight()*1.5;var curLine=null,curLineObj=null;function getY(pos,top){if(curLine!=pos.line){curLine=pos.line;curLineObj=cm.getLineHandle(pos.line);var visual=cm.getLineHandleVisualStart(curLineObj);if(visual!=curLineObj){curLine=cm.getLineNumber(visual);curLineObj=visual}}if(curLineObj.widgets&&curLineObj.widgets.length||wrapping&&curLineObj.height>singleLineH)return cm.charCoords(pos,"local")[top?"top":"bottom"];var topY=cm.heightAtLine(curLineObj,"local");return topY+(top?0:curLineObj.height)}var lastLine=cm.lastLine();if(cm.display.barWidth)for(var i=0,nextTop;i<anns.length;i++){var ann=anns[i];if(ann.to.line>lastLine)continue;var top=nextTop||getY(ann.from,true)*hScale;var bottom=getY(ann.to,false)*hScale;while(i<anns.length-1){if(anns[i+1].to.line>lastLine)break;nextTop=getY(anns[i+1].from,true)*hScale;if(nextTop>bottom+.9)break;ann=anns[++i];bottom=getY(ann.to,false)*hScale}if(bottom==top)continue;var height=Math.max(bottom-top,3);var elt=frag.appendChild(document.createElement("div"));elt.style.cssText="position: absolute; right: 0px; width: "+Math.max(cm.display.barWidth-1,2)+"px; top: "+(top+this.buttonHeight)+"px; height: "+height+"px";elt.className=this.options.className;if(ann.id){elt.setAttribute("annotation-id",ann.id)}}this.div.textContent="";this.div.appendChild(frag)};Annotation.prototype.clear=function(){this.cm.off("refresh",this.resizeHandler);this.cm.off("markerAdded",this.resizeHandler);this.cm.off("markerCleared",this.resizeHandler);if(this.changeHandler)this.cm.off("changes",this.changeHandler);this.div.parentNode.removeChild(this.div)}}));(function(mod){if(typeof exports=="object"&&typeof module=="object")mod(require("../../lib/codemirror"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror"],mod);else mod(CodeMirror)})((function(CodeMirror){"use strict";function doFold(cm,pos,options,force){if(options&&options.call){var finder=options;options=null}else{var finder=getOption(cm,options,"rangeFinder")}if(typeof pos=="number")pos=CodeMirror.Pos(pos,0);var minSize=getOption(cm,options,"minFoldSize");function getRange(allowFolded){var range=finder(cm,pos);if(!range||range.to.line-range.from.line<minSize)return null;if(force==="fold")return range;var marks=cm.findMarksAt(range.from);for(var i=0;i<marks.length;++i){if(marks[i].__isFold){if(!allowFolded)return null;range.cleared=true;marks[i].clear()}}return range}var range=getRange(true);if(getOption(cm,options,"scanUp"))while(!range&&pos.line>cm.firstLine()){pos=CodeMirror.Pos(pos.line-1,0);range=getRange(false)}if(!range||range.cleared||force==="unfold")return;var myWidget=makeWidget(cm,options,range);CodeMirror.on(myWidget,"mousedown",(function(e){myRange.clear();CodeMirror.e_preventDefault(e)}));var myRange=cm.markText(range.from,range.to,{replacedWith:myWidget,clearOnEnter:getOption(cm,options,"clearOnEnter"),__isFold:true});myRange.on("clear",(function(from,to){CodeMirror.signal(cm,"unfold",cm,from,to)}));CodeMirror.signal(cm,"fold",cm,range.from,range.to)}function makeWidget(cm,options,range){var widget=getOption(cm,options,"widget");if(typeof widget=="function"){widget=widget(range.from,range.to)}if(typeof widget=="string"){var text=document.createTextNode(widget);widget=document.createElement("span");widget.appendChild(text);widget.className="CodeMirror-foldmarker"}else if(widget){widget=widget.cloneNode(true)}return widget}CodeMirror.newFoldFunction=function(rangeFinder,widget){return function(cm,pos){doFold(cm,pos,{rangeFinder:rangeFinder,widget:widget})}};CodeMirror.defineExtension("foldCode",(function(pos,options,force){doFold(this,pos,options,force)}));CodeMirror.defineExtension("isFolded",(function(pos){var marks=this.findMarksAt(pos);for(var i=0;i<marks.length;++i)if(marks[i].__isFold)return true}));CodeMirror.commands.toggleFold=function(cm){cm.foldCode(cm.getCursor())};CodeMirror.commands.fold=function(cm){cm.foldCode(cm.getCursor(),null,"fold")};CodeMirror.commands.unfold=function(cm){cm.foldCode(cm.getCursor(),{scanUp:false},"unfold")};CodeMirror.commands.foldAll=function(cm){cm.operation((function(){for(var i=cm.firstLine(),e=cm.lastLine();i<=e;i++)cm.foldCode(CodeMirror.Pos(i,0),{scanUp:false},"fold")}))};CodeMirror.commands.unfoldAll=function(cm){cm.operation((function(){for(var i=cm.firstLine(),e=cm.lastLine();i<=e;i++)cm.foldCode(CodeMirror.Pos(i,0),{scanUp:false},"unfold")}))};CodeMirror.registerHelper("fold","combine",(function(){var funcs=Array.prototype.slice.call(arguments,0);return function(cm,start){for(var i=0;i<funcs.length;++i){var found=funcs[i](cm,start);if(found)return found}}}));CodeMirror.registerHelper("fold","auto",(function(cm,start){var helpers=cm.getHelpers(start,"fold");for(var i=0;i<helpers.length;i++){var cur=helpers[i](cm,start);if(cur)return cur}}));var defaultOptions={rangeFinder:CodeMirror.fold.auto,widget:"↔",minFoldSize:0,scanUp:false,clearOnEnter:true};CodeMirror.defineOption("foldOptions",null);function getOption(cm,options,name){if(options&&options[name]!==undefined)return options[name];var editorOptions=cm.options.foldOptions;if(editorOptions&&editorOptions[name]!==undefined)return editorOptions[name];return defaultOptions[name]}CodeMirror.defineExtension("foldOption",(function(options,name){return getOption(this,options,name)}))}));(function(mod){"use strict";if(typeof exports==="object"&&typeof module==="object")mod(require("codemirror"));else if(typeof define==="function"&&define.amd)define(["codemirror"],mod);else mod(CodeMirror)})((function(CodeMirror){"use strict";var cmEleToSearch=new Map;var ESCAPE_SPECIAL_CHARS=/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g;CodeMirror.defineOption("searchbox",false,(function(cm){function ctrlFHandler(){var cmEle=cm.display.wrapper;var Search=cmEleToSearch.get(cmEle);if(!Search||!cmEle.parentElement.contains(Search.searchBox)){cmEleToSearch.set(cmEle,new SearchBox(cm));Search=cmEleToSearch.get(cmEle)}var isReplace=false;if(cmEle.parentElement.querySelector("[action=toggleReplace]")){isReplace=cmEle.parentElement.querySelector("[action=toggleReplace]").innerText==="-"}Search.show(cm.getSelection(),isReplace)}cm.addKeyMap({"Ctrl-F":ctrlFHandler,Esc:function(){var Search=cmEleToSearch.get(cm.display.wrapper);if(!Search||!Search.isVisible())return CodeMirror.Pass;Search.hide();if(typeof event!=="undefined")event.stopPropagation()},"Cmd-F":ctrlFHandler})}));function SearchBox(cm){var self=this;init();function initElements(el){self.searchBox=el.querySelector(".ace_search_form");self.replaceBox=el.querySelector(".ace_replace_form");self.searchOptions=el.querySelector(".ace_search_options");self.regExpOption=el.querySelector("[action=toggleRegexpMode]");self.caseSensitiveOption=el.querySelector("[action=toggleCaseSensitive]");self.wholeWordOption=el.querySelector("[action=toggleWholeWords]");self.searchInput=self.searchBox.querySelector(".ace_search_field");self.replaceInput=self.replaceBox.querySelector(".ace_search_field")}function init(){var el=self.element=addHtml();addStyle();initElements(el);bindKeys();el.addEventListener("mousedown",(function(e){setTimeout((function(){self.activeInput.focus()}),0);e.stopPropagation()}));el.addEventListener("click",(function(e){var t=e.target||e.srcElement;var action=t.getAttribute("action");if(action&&self[action])self[action]();else if(self.commands[action])self.commands[action]();e.stopPropagation()}));self.searchInput.addEventListener("input",(function(){self.$onChange.schedule(20)}));self.searchInput.addEventListener("focus",(function(){self.activeInput=self.searchInput}));self.replaceInput.addEventListener("focus",(function(){self.activeInput=self.replaceInput}));self.$onChange=delayedCall((function(){self.find(false,false)}))}function bindKeys(){var sb=self,obj={"Ctrl-F|Cmd-F|Command-Alt-F":function(){sb.searchInput.focus();sb.searchInput.select()},"Ctrl-H":function(){sb.replaceBox.style.display="";sb.replaceInput.focus();sb.replaceInput.select()},"Ctrl-G|Cmd-G":function(){sb.findNext()},"Ctrl-Shift-G|Cmd-Shift-G":function(){sb.findPrev()},Esc:function(){setTimeout((function(){sb.hide()}))},"Shift-Enter":function(){if(sb.activeInput===sb.replaceInput)sb.replace();sb.findPrev()},Enter:function(){if(sb.activeInput===sb.replaceInput)sb.replace();sb.findNext()},"Alt-Enter":function(){if(sb.activeInput===sb.replaceInput)sb.replaceAll();sb.findAll()},Tab:function(){if(self.activeInput===self.replaceInput)self.searchInput.focus();else self.replaceInput.focus()}};self.element.addEventListener("keydown",(function(event){Object.keys(obj).some((function(name){var is=key(name,event);if(is){event.stopPropagation();event.preventDefault();obj[name](event)}return is}))}))}this.commands={toggleRegexpMode:function(){self.regExpOption.checked=!self.regExpOption.checked;self.$syncOptions()},toggleCaseSensitive:function(){self.caseSensitiveOption.checked=!self.caseSensitiveOption.checked;self.$syncOptions()},toggleWholeWords:function(){self.wholeWordOption.checked=!self.wholeWordOption.checked;self.$syncOptions()}};this.$syncOptions=function(){setCssClass(this.regExpOption,"checked",this.regExpOption.checked);setCssClass(this.wholeWordOption,"checked",this.wholeWordOption.checked);setCssClass(this.caseSensitiveOption,"checked",this.caseSensitiveOption.checked);this.find(false,false)};this.find=function(skipCurrent,backwards){var value=this.searchInput.value,options={skipCurrent:skipCurrent,backwards:backwards,regExp:this.regExpOption.checked,caseSensitive:this.caseSensitiveOption.checked,wholeWord:this.wholeWordOption.checked};find(value,options,(function(searchCursor){var current=searchCursor.matches(false,searchCursor.from());cm.setSelection(current.from,current.to)}))};function find(value,options,callback){if(!value){clearSearch(cm);updateCount();setCssClass(self.searchBox,"ace_nomatch",false);return}var done,noMatch,searchCursor,next,prev,matches,cursor,position,val=value,o=options,is=true,caseSensitive=o.caseSensitive,regExp=o.regExp,wholeWord=o.wholeWord;if(regExp){val=val.replace(ESCAPE_SPECIAL_CHARS,"\\$&")}if(wholeWord){if(caseSensitive){val=val=RegExp("\\b"+val+"\\b")}else{val=RegExp("\\b"+val+"\\b","i")}}if(regExp){val=RegExp(val)}clearSearch(cm);doSearch(cm,val,caseSensitive);updateCount();if(o.backwards)position=o.skipCurrent?"from":"to";else position=o.skipCurrent?"to":"from";cursor=cm.getCursor(position);searchCursor=cm.getSearchCursor(val,cursor,!caseSensitive);next=searchCursor.findNext.bind(searchCursor),prev=searchCursor.findPrevious.bind(searchCursor),matches=searchCursor.matches.bind(searchCursor);if(o.backwards&&!prev()){is=next();if(is){cm.setCursor(cm.doc.size-1,0);find(value,options,callback);done=true}}else if(!o.backwards&&!next()){is=prev();if(is){cm.setCursor(0,0);find(value,options,callback);done=true}}noMatch=!is&&self.searchInput.value;setCssClass(self.searchBox,"ace_nomatch",noMatch);if(!done&&is)callback(searchCursor)}this.findNext=function(){this.find(true,false)};this.findPrev=function(){this.find(true,true)};this.findAll=function(){var value=this.searchInput.value,range,noMatch=!range&&this.searchInput.value;setCssClass(this.searchBox,"ace_nomatch",noMatch);if(cm.showMatchesOnScrollbar)cm.showMatchesOnScrollbar(value);this.hide()};this.replace=function(){var readOnly=cm.getOption("readOnly"),isSelection=!!cm.getSelection();if(!readOnly&&isSelection)cm.replaceSelection(this.replaceInput.value,"start");updateCount()};this.replaceAndFindNext=function(){var readOnly=cm.getOption("readOnly");if(!readOnly){this.replace();this.findNext()}};this.replaceAll=function(){var value,cursor,from=this.searchInput.value,to=this.replaceInput.value,reg=RegExp(from.replace(ESCAPE_SPECIAL_CHARS,"\\$&"),this.caseSensitiveOption.checked?"g":"gi");if(this.wholeWordOption.checked&&!this.regExpOption.checked){if(this.caseSensitiveOption.checked){reg=RegExp("\\b"+from+"\\b","g")}else{reg=RegExp("\\b"+from+"\\b","gi")}}if(!cm.getOption("readOnly")&&cm.getSelection()){cursor=cm.getCursor();value=cm.getValue();value=value.replace(reg,to);cm.setValue(value);cm.setCursor(cursor)}updateCount()};this.toggleReplace=function(){var cmEle=cm.display.wrapper;if(cmEle.parentElement.querySelector("[action=toggleReplace]").innerText==="+"){cmEle.parentElement.querySelector("[action=toggleReplace]").innerText="-";this.replaceBox.style.display="";this.isReplace=true}else{cmEle.parentElement.querySelector("[action=toggleReplace]").innerText="+";this.replaceBox.style.display="none";this.isReplace=false}};this.hide=function(){clearSearch(cm);var cmEle=cm.getWrapperElement();cmEleToSearch.set(cmEle,null);cmEle.removeChild(this.element);cm.focus();if(cmEle.parentElement.querySelector(".code-editor-buttons-ctn"))document.querySelector(".code-editor-buttons-ctn").style.display="";if(cmEle.parentElement.querySelector(".toggle-wrap-button-html"))document.querySelector(".toggle-wrap-button-html").style.display=""};this.isVisible=function(){var is=this.element.style.display==="";return is};this.show=function(value,isReplace){this.element.style.display="";if(!isReplace){this.replaceBox.style.display=isReplace?"":"none"}this.isReplace=isReplace;if(value){this.searchInput.value=value;this.find(false,false)}this.searchInput.focus();this.searchInput.select();var cmEle=cm.getWrapperElement();if(cmEle.parentElement.querySelector(".code-editor-buttons-ctn"))document.querySelector(".code-editor-buttons-ctn").style.display="none";if(cmEle.parentElement.querySelector(".toggle-wrap-button-html"))document.querySelector(".toggle-wrap-button-html").style.display="none"};this.isFocused=function(){var el=document.activeElement;return el===this.searchInput||el===this.replaceInput};function doSearch(cm,value,caseSensitive){var state=getSearchState(cm);var query=value;if(query&&query!==state.queryText){startSearch(cm,state,query,caseSensitive);state.posFrom=state.posTo=cm.getCursor()}}function parseString(string){return string.replace(/\\([nrt\\])/g,(function(match,ch){if(ch=="n")return"\n";if(ch=="r")return"\r";if(ch=="t")return"\t";if(ch=="\\")return"\\";return match}))}function parseQuery(query){var reStr=typeof query==="object"?query.toString():query;var isRE=reStr.match(/^\/(.*)\/([a-z]*)$/);if(isRE){try{query=new RegExp(isRE[1],isRE[2].indexOf("i")==-1?"":"i")}catch(e){}}else{query=parseString(query)}if(typeof query=="string"?query=="":query.test(""))query=/x^/;return query}function startSearch(cm,state,query,caseSensitive){state.queryText=query;state.query=parseQuery(query);cm.removeOverlay(state.overlay,queryCaseInsensitive(state.query,caseSensitive));state.overlay=searchOverlay(state.query,queryCaseInsensitive(state.query,caseSensitive));cm.addOverlay(state.overlay);if(cm.showMatchesOnScrollbar){if(state.annotate){state.annotate.clear();state.annotate=null}state.annotate=cm.showMatchesOnScrollbar(state.query,queryCaseInsensitive(state.query,caseSensitive))}}function queryCaseInsensitive(query,caseSensitive){return typeof query=="string"&&!caseSensitive}function searchOverlay(query,caseInsensitive){if(typeof query=="string")query=new RegExp(query.replace(ESCAPE_SPECIAL_CHARS,"\\$&"),caseInsensitive?"gi":"g");else if(!query.global)query=new RegExp(query.source,query.ignoreCase?"gi":"g");return{token:function(stream){query.lastIndex=stream.pos;var match=query.exec(stream.string);if(match&&match.index==stream.pos){stream.pos+=match[0].length||1;return"searching"}else if(match){stream.pos=match.index}else{stream.skipToEnd()}}}}function SearchState(){this.posFrom=this.posTo=this.lastQuery=this.query=null;this.overlay=null}function getSearchState(cm){return cm.state.search||(cm.state.search=new SearchState)}function clearSearch(cm){cm.operation((function(){var state=getSearchState(cm);state.lastQuery=state.query;if(!state.query)return;state.query=state.queryText=null;cm.removeOverlay(state.overlay);if(state.annotate){state.annotate.clear();state.annotate=null}}))}function updateCount(){var val=self.searchInput.value;var matches=[];if(val){val=val.replace(ESCAPE_SPECIAL_CHARS,"\\$&");var reg;if(self.caseSensitiveOption.checked){reg=RegExp(val,"g")}else{reg=RegExp(val,"gi")}if(self.wholeWordOption.checked){if(self.caseSensitiveOption.checked){reg=RegExp("\\b"+val+"\\b","g")}else{reg=RegExp("\\b"+val+"\\b","gi")}}if(self.regExpOption.checked){reg=RegExp(val,"gi")}matches=cm.getValue().match(reg)}var count=matches?matches.length:0;var cmEle=cm.display.wrapper;var countEle=cmEle.parentElement.querySelector(".ace_search_counter");if(countEle){countEle.innerText=count+" matches found."}if(count===0){}}function addStyle(){let isDarkMode=window.matchMedia("(prefers-color-scheme: dark)")?.matches;var style=document.createElement("style"),css=[".ace_search {",`background-color: ${isDarkMode?"#2c2c2c":"#ddd"};`,`border: 1px solid ${isDarkMode?"#575757":"#cbcbcb"};`,"border-top: 0 none;","max-width: 325px;","overflow: hidden;","margin: 0;","padding: 4px;","padding-right: 6px;","padding-bottom: 0;","position: absolute;","top: 0px;","z-index: 99;","white-space: normal;","font-size: 12px;","}",".ace_search.left {","border-left: 0 none;","border-radius: 0px 0px 5px 0px;","left: 0;","}",".ace_search.right {","border-radius: 0px 0px 0px 5px;","border-right: 0 none;","right: 0;","}",".ace_search_form, .ace_replace_form {","border-radius: 3px;",`border: 1px solid ${isDarkMode?"#575757":"#cbcbcb"};`,"float: left;","margin-bottom: 4px;","overflow: hidden;","}",".ace_search_form.ace_nomatch {","outline: 1px solid #c54832;","}",".ace_search_field {",`background-color: ${isDarkMode?"#272727":"white"};`,`border-right: 1px solid ${isDarkMode?"#575757":"#cbcbcb"};`,"border: 0 none;","-webkit-box-sizing: border-box;","-moz-box-sizing: border-box;","box-sizing: border-box;","float: left;","height: 22px;","outline: 0;","padding: 0 7px;","width: 238px;","margin: 0;","}",".ace_searchbtn, .ace_replacebtn { border:none !important; }",".ace_searchbtn,",".ace_replacebtn {",`background: ${isDarkMode?"#2c2c2c":"#fff"};`,"border: 0 none;",`border-left: 1px solid ${isDarkMode?"#575757":"#dcdcdc"};`,"cursor: pointer;","float: left;","height: 22px;","padding: 0 5px;","margin: 0;","position: relative;","width:27px","}",`.ace_searchbtn:hover, .ace_replacebtn:hover { background-color:${isDarkMode?"#3c3c3c":"#dcdcdc"}; border:none !important; }`,".ace_searchbtn:last-child,",".ace_replacebtn:last-child {","border-top-right-radius: 3px;","border-bottom-right-radius: 3px;","}",".ace_searchbtn:disabled {","background: none;","cursor: default;","}",".ace_searchbtn {","background-position: 50% 50%;","background-repeat: no-repeat;","width: 27px;","}",".ace_searchbtn.prev {","background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADFJREFUeNpiSU1NZUAC/6E0I0yACYskCpsJiySKIiY0SUZk40FyTEgCjGgKwTRAgAEAQJUIPCE+qfkAAAAASUVORK5CYII=);    ","}",".ace_searchbtn.next {","background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNpiTE1NZQCC/0DMyIAKwGJMUAYDEo3M/s+EpvM/mkKwCQxYjIeLMaELoLMBAgwAU7UJObTKsvAAAAAASUVORK5CYII=);    ","}",".ace_searchbtn_close {","background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAcCAYAAABRVo5BAAAAZ0lEQVR42u2SUQrAMAhDvazn8OjZBilCkYVVxiis8H4CT0VrAJb4WHT3C5xU2a2IQZXJjiQIRMdkEoJ5Q2yMqpfDIo+XY4k6h+YXOyKqTIj5REaxloNAd0xiKmAtsTHqW8sR2W5f7gCu5nWFUpVjZwAAAABJRU5ErkJggg==) no-repeat 50% 0;","border-radius: 50%;","border: 0 none;","color: #656565;","cursor: pointer;","float: right;","font: 16px/16px Arial;","height: 14px;","margin: 5px 1px 9px 5px;","padding: 0;","text-align: center;","width: 14px;","}",".ace_searchbtn_close:hover {","background-color: #656565;","background-position: 50% 100%;","color: white;","}",".ace_replacebtn.prev {","width: 54px","}",".ace_replacebtn.next {","width: 27px","}",".ace_button {","margin-left: 2px;","cursor: pointer;","-webkit-user-select: none;","-moz-user-select: none;","-o-user-select: none;","-ms-user-select: none;","user-select: none;","overflow: hidden;","opacity: 0.7;",`border: 1px solid ${isDarkMode?"#575757":"rgba(100,100,100,0.23)"};`,"padding: 1.4px;","-moz-box-sizing: border-box;","box-sizing:    border-box;",`color: ${isDarkMode?"white":"black"};`,`border-radius: 3px;`,"}",".ace_button:hover {",`background-color: ${isDarkMode?"#4a4a4a":"#eee"};`,"opacity:1;","}",".ace_button:active {","}",".ace_button.checked {","border-color: #3399ff;","opacity:1;","}",".ace_search_options{","clear: both;","margin: 4px 0;","text-align: right;","-webkit-user-select: none;","-moz-user-select: none;","-o-user-select: none;","-ms-user-select: none;","user-select: none;","}",".replace_toggle{","float: left;","margin-top: -2px;","padding: 0 5px;","border-radius: 3px;","margin-left: 0;",isDarkMode?"border-color:#575757;":""," }",".ace_search_counter{","float: left;","font-family: arial;","padding: 0 8px;","}","button svg,path {","pointer-events: none;","}"].join("");style.setAttribute("data-name","js-searchbox");style.textContent=css;document.head.appendChild(style)}function addHtml(){var elSearch,el=cm.getWrapperElement(),div=document.createElement("div"),html=['<div class="ace_search right">','<button type="button" action="hide" class="ace_searchbtn_close"></button>','<div class="ace_search_form">','<input class="ace_search_field" placeholder="Search for" spellcheck="false" autocomplete="one-time-code"></input>','<button type="button" action="findPrev" class="ace_searchbtn prev"></button>','<button type="button" action="findNext" class="ace_searchbtn next"></button>',"</div>",'<div class="ace_replace_form">','<input class="ace_search_field" placeholder="Replace with" spellcheck="false"></input>','<button type="button" action="replaceAndFindNext" title="Replace" class="ace_replacebtn">','<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">','<path fill-rule="evenodd" clip-rule="evenodd" d="M3.221 3.739L5.482 6.008L7.7 3.784L7 3.084L5.988 4.091L5.98 2.491C5.97909 2.35567 6.03068 2.22525 6.12392 2.12716C6.21716 2.02908 6.3448 1.97095 6.48 1.965H8V1H6.48C6.28496 1.00026 6.09189 1.03902 5.91186 1.11405C5.73183 1.18908 5.56838 1.29892 5.43088 1.43725C5.29338 1.57558 5.18455 1.73969 5.11061 1.92018C5.03667 2.10066 4.99908 2.29396 5 2.489V4.1L3.927 3.033L3.221 3.739ZM9.89014 5.53277H9.90141C10.0836 5.84426 10.3521 6 10.707 6C11.0995 6 11.4131 5.83236 11.6479 5.49708C11.8826 5.1618 12 4.71728 12 4.16353C12 3.65304 11.8995 3.2507 11.6986 2.95652C11.4977 2.66234 11.2113 2.51525 10.8394 2.51525C10.4338 2.51525 10.1211 2.70885 9.90141 3.09604H9.89014V1H9V5.91888H9.89014V5.53277ZM9.87606 4.47177V4.13108C9.87606 3.88449 9.93427 3.6844 10.0507 3.53082C10.169 3.37724 10.3174 3.30045 10.4958 3.30045C10.6854 3.30045 10.831 3.37833 10.9324 3.53407C11.0357 3.68765 11.0873 3.9018 11.0873 4.17651C11.0873 4.50746 11.031 4.76379 10.9183 4.94549C10.8075 5.12503 10.6507 5.2148 10.4479 5.2148C10.2808 5.2148 10.1437 5.14449 10.0366 5.00389C9.92958 4.86329 9.87606 4.68592 9.87606 4.47177ZM9 12.7691C8.74433 12.923 8.37515 13 7.89247 13C7.32855 13 6.87216 12.8225 6.5233 12.4674C6.17443 12.1124 6 11.6543 6 11.0931C6 10.4451 6.18638 9.93484 6.55914 9.5624C6.93429 9.18747 7.43489 9.00001 8.06093 9.00001C8.49343 9.00001 8.80645 9.0596 9 9.17878V10.1769C8.76344 9.99319 8.4994 9.90132 8.20789 9.90132C7.88292 9.90132 7.62485 10.0006 7.43369 10.1993C7.24492 10.3954 7.15054 10.6673 7.15054 11.0149C7.15054 11.3526 7.24134 11.6183 7.42294 11.8119C7.60454 12.0031 7.85424 12.0987 8.17204 12.0987C8.454 12.0987 8.72999 12.0068 9 11.8231V12.7691ZM4 7L3 8V14L4 15H11L12 14V8L11 7H4ZM4 8H5H10H11V9V13V14H10H5H4V13V9V8Z" fill="#656565"/>',"</svg></button>",'<button type="button" action="replaceAll" title="Replace All" class="ace_replacebtn">','<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">','<path fill-rule="evenodd" clip-rule="evenodd" d="M11.6009 2.67683C11.7474 2.36708 11.9559 2.2122 12.2263 2.2122C12.4742 2.2122 12.6651 2.32987 12.7991 2.56522C12.933 2.80056 13 3.12243 13 3.53082C13 3.97383 12.9218 4.32944 12.7653 4.59766C12.6088 4.86589 12.3997 5 12.138 5C11.9014 5 11.7224 4.87541 11.6009 4.62622H11.5934V4.93511H11V1H11.5934V2.67683H11.6009ZM11.584 3.77742C11.584 3.94873 11.6197 4.09063 11.6911 4.20311C11.7624 4.3156 11.8538 4.37184 11.9653 4.37184C12.1005 4.37184 12.205 4.30002 12.2789 4.15639C12.354 4.01103 12.3915 3.80597 12.3915 3.54121C12.3915 3.32144 12.3571 3.15012 12.2883 3.02726C12.2207 2.90266 12.1236 2.84036 11.9972 2.84036C11.8782 2.84036 11.7793 2.9018 11.7005 3.02466C11.6228 3.14752 11.584 3.30759 11.584 3.50487V3.77742ZM4.11969 7.695L2 5.56781L2.66188 4.90594L3.66781 5.90625V4.39594C3.66695 4.21309 3.70219 4.03187 3.7715 3.86266C3.84082 3.69346 3.94286 3.53961 4.07176 3.40992C4.20066 3.28023 4.3539 3.17727 4.52268 3.10692C4.69146 3.03658 4.87246 3.00024 5.05531 3H7.39906V3.90469H5.05531C4.92856 3.91026 4.8089 3.96476 4.72149 4.05672C4.63408 4.14868 4.58571 4.27094 4.58656 4.39781L4.59406 5.89781L5.54281 4.95375L6.19906 5.61L4.11969 7.695ZM9.3556 4.93017H10V3.22067C10 2.40689 9.68534 2 9.05603 2C8.92098 2 8.77083 2.02421 8.6056 2.07263C8.44181 2.12104 8.3125 2.17691 8.21767 2.24022V2.90503C8.45474 2.70205 8.70474 2.60056 8.96767 2.60056C9.22917 2.60056 9.35991 2.75698 9.35991 3.06983L8.76078 3.17318C8.25359 3.25885 8 3.57914 8 4.13408C8 4.39665 8.06106 4.60708 8.18319 4.76536C8.30675 4.92179 8.47557 5 8.68966 5C8.97989 5 9.19899 4.83985 9.34698 4.51955H9.3556V4.93017ZM9.35991 3.57542V3.76816C9.35991 3.9432 9.31968 4.08845 9.23922 4.20391C9.15876 4.3175 9.0546 4.3743 8.92672 4.3743C8.83477 4.3743 8.76149 4.34264 8.7069 4.27933C8.65374 4.21415 8.62716 4.13128 8.62716 4.03073C8.62716 3.80912 8.73779 3.6797 8.95905 3.64246L9.35991 3.57542ZM7 12.9302H6.3556V12.5196H6.34698C6.19899 12.8399 5.97989 13 5.68966 13C5.47557 13 5.30675 12.9218 5.18319 12.7654C5.06106 12.6071 5 12.3966 5 12.1341C5 11.5791 5.25359 11.2588 5.76078 11.1732L6.35991 11.0698C6.35991 10.757 6.22917 10.6006 5.96767 10.6006C5.70474 10.6006 5.45474 10.702 5.21767 10.905V10.2402C5.3125 10.1769 5.44181 10.121 5.6056 10.0726C5.77083 10.0242 5.92098 10 6.05603 10C6.68534 10 7 10.4069 7 11.2207V12.9302ZM6.35991 11.7682V11.5754L5.95905 11.6425C5.73779 11.6797 5.62716 11.8091 5.62716 12.0307C5.62716 12.1313 5.65374 12.2142 5.7069 12.2793C5.76149 12.3426 5.83477 12.3743 5.92672 12.3743C6.0546 12.3743 6.15876 12.3175 6.23922 12.2039C6.31968 12.0885 6.35991 11.9432 6.35991 11.7682ZM9.26165 13C9.58343 13 9.82955 12.9423 10 12.8268V12.1173C9.81999 12.2551 9.636 12.324 9.44803 12.324C9.23616 12.324 9.06969 12.2523 8.94863 12.1089C8.82756 11.9637 8.76702 11.7644 8.76702 11.5112C8.76702 11.2505 8.82995 11.0466 8.95579 10.8994C9.08323 10.7505 9.25528 10.676 9.47192 10.676C9.66627 10.676 9.84229 10.7449 10 10.8827V10.1341C9.87097 10.0447 9.66229 10 9.37395 10C8.95659 10 8.62286 10.1406 8.37276 10.4218C8.12425 10.7011 8 11.0838 8 11.5698C8 11.9907 8.11629 12.3343 8.34887 12.6006C8.58144 12.8669 8.8857 13 9.26165 13ZM2 9L3 8H12L13 9V14L12 15H3L2 14V9ZM3 9V14H12V9H3ZM6 7L7 6H14L15 7V12L14 13V12V7H7H6Z" fill="#656565"/>',"</svg></button>","</div>",'<div class="ace_search_options">','<span action="toggleReplace" class="ace_button replace_toggle">+</span>','<span class="ace_search_counter">0 matches found.</span>','<span action="toggleRegexpMode" title="RegExp Search"></span>','<span action="toggleCaseSensitive" class="ace_button" title="CaseSensitive Search">Aa</span>','<span action="toggleWholeWords" title="Whole Word Search"></span>',"</div>","</div>"].join("");div.innerHTML=html;elSearch=div.firstChild;el.appendChild(elSearch);return elSearch}}function setCssClass(el,className,condition){var list=el.classList;list[condition?"add":"remove"](className)}function delayedCall(fcn,defaultTimeout){var timer,callback=function(){timer=null;fcn()},_self=function(timeout){if(!timer)timer=setTimeout(callback,timeout||defaultTimeout)};_self.delay=function(timeout){timer&&clearTimeout(timer);timer=setTimeout(callback,timeout||defaultTimeout)};_self.schedule=_self;_self.call=function(){this.cancel();fcn()};_self.cancel=function(){timer&&clearTimeout(timer);timer=null};_self.isPending=function(){return timer};return _self}function key(str,event){var right,KEY={BACKSPACE:8,TAB:9,ENTER:13,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,UP:38,DOWN:40,INSERT:45,DELETE:46,INSERT_MAC:96,ASTERISK:106,PLUS:107,MINUS:109,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,SLASH:191,TRA:192,BACKSLASH:220};keyCheck(str,event);right=str.split("|").some((function(combination){var wrong;wrong=combination.split("-").some((function(key){var right;switch(key){case"Ctrl":right=event.ctrlKey;break;case"Shift":right=event.shiftKey;break;case"Alt":right=event.altKey;break;case"Cmd":right=event.metaKey;break;default:if(key.length===1)right=event.keyCode===key.charCodeAt(0);else Object.keys(KEY).some((function(name){var up=key.toUpperCase();if(up===name)right=event.keyCode===KEY[name]}));break}return!right}));return!wrong}));return right}function keyCheck(str,event){if(typeof str!=="string")throw Error("str should be string!");if(typeof event!=="object")throw Error("event should be object!")}}));(function(mod){if(typeof exports=="object"&&typeof module=="object")mod(require("../../lib/codemirror"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror"],mod);else mod(CodeMirror)})((function(CodeMirror){"use strict";var htmlConfig={autoSelfClosers:{area:true,base:true,br:true,col:true,command:true,embed:true,frame:true,hr:true,img:true,input:true,keygen:true,link:true,meta:true,param:true,source:true,track:true,wbr:true,menuitem:true},implicitlyClosed:{dd:true,li:true,optgroup:true,option:true,p:true,rp:true,rt:true,tbody:true,td:true,tfoot:true,th:true,tr:true},contextGrabbers:{dd:{dd:true,dt:true},dt:{dd:true,dt:true},li:{li:true},option:{option:true,optgroup:true},optgroup:{optgroup:true},p:{address:true,article:true,aside:true,blockquote:true,dir:true,div:true,dl:true,fieldset:true,footer:true,form:true,h1:true,h2:true,h3:true,h4:true,h5:true,h6:true,header:true,hgroup:true,hr:true,menu:true,nav:true,ol:true,p:true,pre:true,section:true,table:true,ul:true},rp:{rp:true,rt:true},rt:{rp:true,rt:true},tbody:{tbody:true,tfoot:true},td:{td:true,th:true},tfoot:{tbody:true},th:{td:true,th:true},thead:{tbody:true,tfoot:true},tr:{tr:true}},doNotIndent:{pre:true},allowUnquoted:true,allowMissing:true,caseFold:true};var xmlConfig={autoSelfClosers:{},implicitlyClosed:{},contextGrabbers:{},doNotIndent:{},allowUnquoted:false,allowMissing:false,allowMissingTagName:false,caseFold:false};CodeMirror.defineMode("xml",(function(editorConf,config_){var indentUnit=editorConf.indentUnit;var config={};var defaults=config_.htmlMode?htmlConfig:xmlConfig;for(var prop in defaults)config[prop]=defaults[prop];for(var prop in config_)config[prop]=config_[prop];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","--\x3e"))}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 lower(tagName){return tagName&&tagName.toLowerCase()}function Context(state,tagName,startOfLine){this.prev=state.context;this.tagName=tagName||"";this.indent=state.indented;this.startOfLine=startOfLine;if(config.doNotIndent.hasOwnProperty(tagName)||state.context&&state.context.noIndent)this.noIndent=true}function popContext(state){if(state.context)state.context=state.context.prev}function maybePopContext(state,nextTagName){var parentTagName;while(true){if(!state.context){return}parentTagName=state.context.tagName;if(!config.contextGrabbers.hasOwnProperty(lower(parentTagName))||!config.contextGrabbers[lower(parentTagName)].hasOwnProperty(lower(nextTagName))){return}popContext(state)}}function baseState(type,stream,state){if(type=="openTag"){state.tagStart=stream.column();return tagNameState}else if(type=="closeTag"){return closeTagNameState}else{return baseState}}function tagNameState(type,stream,state){if(type=="word"){state.tagName=stream.current();setStyle="tag";return attrState}else if(config.allowMissingTagName&&type=="endTag"){setStyle="tag bracket";return attrState(type,stream,state)}else{setStyle="error";return tagNameState}}function closeTagNameState(type,stream,state){if(type=="word"){var tagName=stream.current();if(state.context&&state.context.tagName!=tagName&&config.implicitlyClosed.hasOwnProperty(lower(state.context.tagName)))popContext(state);if(state.context&&state.context.tagName==tagName||config.matchClosing===false){setStyle="tag";return closeState}else{setStyle="tag error";return closeStateErr}}else if(config.allowMissingTagName&&type=="endTag"){setStyle="tag bracket";return closeState(type,stream,state)}else{setStyle="error";return closeStateErr}}function closeState(type,_stream,state){if(type!="endTag"){setStyle="error";return closeState}popContext(state);return baseState}function closeStateErr(type,stream,state){setStyle="error";return closeState(type,stream,state)}function attrState(type,_stream,state){if(type=="word"){setStyle="attribute";return attrEqState}else if(type=="endTag"||type=="selfcloseTag"){var tagName=state.tagName,tagStart=state.tagStart;state.tagName=state.tagStart=null;if(type=="selfcloseTag"||config.autoSelfClosers.hasOwnProperty(lower(tagName))){maybePopContext(state,tagName)}else{maybePopContext(state,tagName);state.context=new Context(state,tagName,tagStart==state.indented)}return baseState}setStyle="error";return attrState}function attrEqState(type,stream,state){if(type=="equals")return attrValueState;if(!config.allowMissing)setStyle="error";return attrState(type,stream,state)}function attrValueState(type,stream,state){if(type=="string")return attrContinuedState;if(type=="word"&&config.allowUnquoted){setStyle="string";return attrState}setStyle="error";return attrState(type,stream,state)}function attrContinuedState(type,stream,state){if(type=="string")return attrContinuedState;return attrState(type,stream,state)}return{startState:function(baseIndent){var state={tokenize:inText,state:baseState,indented:baseIndent||0,tagName:null,tagStart:null,context:null};if(baseIndent!=null)state.baseIndent=baseIndent;return state},token:function(stream,state){if(!state.tagName&&stream.sol())state.indented=stream.indentation();if(stream.eatSpace())return null;type=null;var style=state.tokenize(stream,state);if((style||type)&&style!="comment"){setStyle=null;state.state=state.state(type||style,stream,state);if(setStyle)style=setStyle=="error"?style+" error":setStyle}return style},indent:function(state,textAfter,fullLine){var context=state.context;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;if(state.tagName){if(config.multilineTagIndentPastTag!==false)return state.tagStart+state.tagName.length+2;else return state.tagStart+indentUnit*(config.multilineTagIndentFactor||1)}if(config.alignCDATA&&/<!\[CDATA\[/.test(textAfter))return 0;var tagAfter=textAfter&&/^<(\/)?([\w_:\.-]*)/.exec(textAfter);if(tagAfter&&tagAfter[1]){while(context){if(context.tagName==tagAfter[2]){context=context.prev;break}else if(config.implicitlyClosed.hasOwnProperty(lower(context.tagName))){context=context.prev}else{break}}}else if(tagAfter){while(context){var grabbers=config.contextGrabbers[lower(context.tagName)];if(grabbers&&grabbers.hasOwnProperty(lower(tagAfter[2])))context=context.prev;else break}}while(context&&context.prev&&!context.startOfLine)context=context.prev;if(context)return context.indent+indentUnit;else return state.baseIndent||0},electricInput:/<\/[\s\w:]+>$/,blockCommentStart:"\x3c!--",blockCommentEnd:"--\x3e",configuration:config.htmlMode?"html":"xml",helperType:config.htmlMode?"html":"xml",skipAttribute:function(state){if(state.state==attrValueState)state.state=attrState},xmlCurrentTag:function(state){return state.tagName?{name:state.tagName,close:state.type=="closeTag"}:null},xmlCurrentContext:function(state){var context=[];for(var cx=state.context;cx;cx=cx.prev)context.push(cx.tagName);return context.reverse()}}}));CodeMirror.defineMIME("text/xml","xml");CodeMirror.defineMIME("application/xml","xml");if(!CodeMirror.mimeModes.hasOwnProperty("text/html"))CodeMirror.defineMIME("text/html",{name:"xml",htmlMode:true})}));(function(mod){if(typeof exports=="object"&&typeof module=="object")mod(require("../../lib/codemirror"),require("../xml/xml"),require("../javascript/javascript"),require("../css/css"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror","../xml/xml","../javascript/javascript","../css/css"],mod);else mod(CodeMirror)})((function(CodeMirror){"use strict";var defaultTags={script:[["lang",/(javascript|babel)/i,"javascript"],["type",/^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i,"javascript"],["type",/./,"text/plain"],[null,null,"javascript"]],style:[["lang",/^css$/i,"css"],["type",/^(text\/)?(x-)?(stylesheet|css)$/i,"css"],["type",/./,"text/plain"],[null,null,"css"]]};function maybeBackup(stream,pat,style){var cur=stream.current(),close=cur.search(pat);if(close>-1){stream.backUp(cur.length-close)}else if(cur.match(/<\/?$/)){stream.backUp(cur.length);if(!stream.match(pat,false))stream.match(cur)}return style}var attrRegexpCache={};function getAttrRegexp(attr){var regexp=attrRegexpCache[attr];if(regexp)return regexp;return attrRegexpCache[attr]=new RegExp("\\s+"+attr+"\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*")}function getAttrValue(text,attr){var match=text.match(getAttrRegexp(attr));return match?/^\s*(.*?)\s*$/.exec(match[2])[1]:""}function getTagRegexp(tagName,anchored){return new RegExp((anchored?"^":"")+"</\\s*"+tagName+"\\s*>","i")}function addTags(from,to){for(var tag in from){var dest=to[tag]||(to[tag]=[]);var source=from[tag];for(var i=source.length-1;i>=0;i--)dest.unshift(source[i])}}function findMatchingMode(tagInfo,tagText){for(var i=0;i<tagInfo.length;i++){var spec=tagInfo[i];if(!spec[0]||spec[1].test(getAttrValue(tagText,spec[0])))return spec[2]}}CodeMirror.defineMode("htmlmixed",(function(config,parserConfig){var htmlMode=CodeMirror.getMode(config,{name:"xml",htmlMode:true,multilineTagIndentFactor:parserConfig.multilineTagIndentFactor,multilineTagIndentPastTag:parserConfig.multilineTagIndentPastTag,allowMissingTagName:parserConfig.allowMissingTagName});var tags={};var configTags=parserConfig&&parserConfig.tags,configScript=parserConfig&&parserConfig.scriptTypes;addTags(defaultTags,tags);if(configTags)addTags(configTags,tags);if(configScript)for(var i=configScript.length-1;i>=0;i--)tags.script.unshift(["type",configScript[i].matches,configScript[i].mode]);function html(stream,state){var style=htmlMode.token(stream,state.htmlState),tag=/\btag\b/.test(style),tagName;if(tag&&!/[<>\s\/]/.test(stream.current())&&(tagName=state.htmlState.tagName&&state.htmlState.tagName.toLowerCase())&&tags.hasOwnProperty(tagName)){state.inTag=tagName+" "}else if(state.inTag&&tag&&/>$/.test(stream.current())){var inTag=/^([\S]+) (.*)/.exec(state.inTag);state.inTag=null;var modeSpec=stream.current()==">"&&findMatchingMode(tags[inTag[1]],inTag[2]);var mode=CodeMirror.getMode(config,modeSpec);var endTagA=getTagRegexp(inTag[1],true),endTag=getTagRegexp(inTag[1],false);state.token=function(stream,state){if(stream.match(endTagA,false)){state.token=html;state.localState=state.localMode=null;return null}return maybeBackup(stream,endTag,state.localMode.token(stream,state.localState))};state.localMode=mode;state.localState=CodeMirror.startState(mode,htmlMode.indent(state.htmlState,"",""))}else if(state.inTag){state.inTag+=stream.current();if(stream.eol())state.inTag+=" "}return style}return{startState:function(){var state=CodeMirror.startState(htmlMode);return{token:html,inTag:null,localMode:null,localState:null,htmlState:state}},copyState:function(state){var local;if(state.localState){local=CodeMirror.copyState(state.localMode,state.localState)}return{token:state.token,inTag:state.inTag,localMode:state.localMode,localState:local,htmlState:CodeMirror.copyState(htmlMode,state.htmlState)}},token:function(stream,state){return state.token(stream,state)},indent:function(state,textAfter,line){if(!state.localMode||/^\s*<\//.test(textAfter))return htmlMode.indent(state.htmlState,textAfter,line);else if(state.localMode.indent)return state.localMode.indent(state.localState,textAfter,line);else return CodeMirror.Pass},innerMode:function(state){return{state:state.localState||state.htmlState,mode:state.localMode||htmlMode}}}}),"xml","javascript","css");CodeMirror.defineMIME("text/html","htmlmixed")}));(function(mod){if(typeof exports=="object"&&typeof module=="object")mod(require("../../lib/codemirror"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror"],mod);else mod(CodeMirror)})((function(CodeMirror){"use strict";CodeMirror.defineMode("css",(function(config,parserConfig){var inline=parserConfig.inline;if(!parserConfig.propertyKeywords)parserConfig=CodeMirror.resolveMode("text/css");var indentUnit=config.indentUnit,tokenHooks=parserConfig.tokenHooks,documentTypes=parserConfig.documentTypes||{},mediaTypes=parserConfig.mediaTypes||{},mediaFeatures=parserConfig.mediaFeatures||{},mediaValueKeywords=parserConfig.mediaValueKeywords||{},propertyKeywords=parserConfig.propertyKeywords||{},nonStandardPropertyKeywords=parserConfig.nonStandardPropertyKeywords||{},fontProperties=parserConfig.fontProperties||{},counterDescriptors=parserConfig.counterDescriptors||{},colorKeywords=parserConfig.colorKeywords||{},valueKeywords=parserConfig.valueKeywords||{},allowNested=parserConfig.allowNested,lineComment=parserConfig.lineComment,supportsAtComponent=parserConfig.supportsAtComponent===true,highlightNonStandardPropertyKeywords=config.highlightNonStandardPropertyKeywords!==false;var type,override;function ret(style,tp){type=tp;return style}function tokenBase(stream,state){var ch=stream.next();if(tokenHooks[ch]){var result=tokenHooks[ch](stream,state);if(result!==false)return result}if(ch=="@"){stream.eatWhile(/[\w\\\-]/);return ret("def",stream.current())}else if(ch=="="||(ch=="~"||ch=="|")&&stream.eat("=")){return ret(null,"compare")}else if(ch=='"'||ch=="'"){state.tokenize=tokenString(ch);return state.tokenize(stream,state)}else if(ch=="#"){stream.eatWhile(/[\w\\\-]/);return ret("atom","hash")}else if(ch=="!"){stream.match(/^\s*\w*/);return ret("keyword","important")}else if(/\d/.test(ch)||ch=="."&&stream.eat(/\d/)){stream.eatWhile(/[\w.%]/);return ret("number","unit")}else if(ch==="-"){if(/[\d.]/.test(stream.peek())){stream.eatWhile(/[\w.%]/);return ret("number","unit")}else if(stream.match(/^-[\w\\\-]*/)){stream.eatWhile(/[\w\\\-]/);if(stream.match(/^\s*:/,false))return ret("variable-2","variable-definition");return ret("variable-2","variable")}else if(stream.match(/^\w+-/)){return ret("meta","meta")}}else if(/[,+>*\/]/.test(ch)){return ret(null,"select-op")}else if(ch=="."&&stream.match(/^-?[_a-z][_a-z0-9-]*/i)){return ret("qualifier","qualifier")}else if(/[:;{}\[\]\(\)]/.test(ch)){return ret(null,ch)}else if(stream.match(/^[\w-.]+(?=\()/)){if(/^(url(-prefix)?|domain|regexp)$/i.test(stream.current())){state.tokenize=tokenParenthesized}return ret("variable callee","variable")}else if(/[\w\\\-]/.test(ch)){stream.eatWhile(/[\w\\\-]/);return ret("property","word")}else{return ret(null,null)}}function tokenString(quote){return function(stream,state){var escaped=false,ch;while((ch=stream.next())!=null){if(ch==quote&&!escaped){if(quote==")")stream.backUp(1);break}escaped=!escaped&&ch=="\\"}if(ch==quote||!escaped&&quote!=")")state.tokenize=null;return ret("string","string")}}function tokenParenthesized(stream,state){stream.next();if(!stream.match(/^\s*[\"\')]/,false))state.tokenize=tokenString(")");else state.tokenize=null;return ret(null,"(")}function Context(type,indent,prev){this.type=type;this.indent=indent;this.prev=prev}function pushContext(state,stream,type,indent){state.context=new Context(type,stream.indentation()+(indent===false?0:indentUnit),state.context);return type}function popContext(state){if(state.context.prev)state.context=state.context.prev;return state.context.type}function pass(type,stream,state){return states[state.context.type](type,stream,state)}function popAndPass(type,stream,state,n){for(var i=n||1;i>0;i--)state.context=state.context.prev;return pass(type,stream,state)}function wordAsValue(stream){var word=stream.current().toLowerCase();if(valueKeywords.hasOwnProperty(word))override="atom";else if(colorKeywords.hasOwnProperty(word))override="keyword";else override="variable"}var states={};states.top=function(type,stream,state){if(type=="{"){return pushContext(state,stream,"block")}else if(type=="}"&&state.context.prev){return popContext(state)}else if(supportsAtComponent&&/@component/i.test(type)){return pushContext(state,stream,"atComponentBlock")}else if(/^@(-moz-)?document$/i.test(type)){return pushContext(state,stream,"documentTypes")}else if(/^@(media|supports|(-moz-)?document|import)$/i.test(type)){return pushContext(state,stream,"atBlock")}else if(/^@(font-face|counter-style)/i.test(type)){state.stateArg=type;return"restricted_atBlock_before"}else if(/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)){return"keyframes"}else if(type&&type.charAt(0)=="@"){return pushContext(state,stream,"at")}else if(type=="hash"){override="builtin"}else if(type=="word"){override="tag"}else if(type=="variable-definition"){return"maybeprop"}else if(type=="interpolation"){return pushContext(state,stream,"interpolation")}else if(type==":"){return"pseudo"}else if(allowNested&&type=="("){return pushContext(state,stream,"parens")}return state.context.type};states.block=function(type,stream,state){if(type=="word"){var word=stream.current().toLowerCase();if(propertyKeywords.hasOwnProperty(word)){override="property";return"maybeprop"}else if(nonStandardPropertyKeywords.hasOwnProperty(word)){override=highlightNonStandardPropertyKeywords?"string-2":"property";return"maybeprop"}else if(allowNested){override=stream.match(/^\s*:(?:\s|$)/,false)?"property":"tag";return"block"}else{override+=" error";return"maybeprop"}}else if(type=="meta"){return"block"}else if(!allowNested&&(type=="hash"||type=="qualifier")){override="error";return"block"}else{return states.top(type,stream,state)}};states.maybeprop=function(type,stream,state){if(type==":")return pushContext(state,stream,"prop");return pass(type,stream,state)};states.prop=function(type,stream,state){if(type==";")return popContext(state);if(type=="{"&&allowNested)return pushContext(state,stream,"propBlock");if(type=="}"||type=="{")return popAndPass(type,stream,state);if(type=="(")return pushContext(state,stream,"parens");if(type=="hash"&&!/^#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(stream.current())){override+=" error"}else if(type=="word"){wordAsValue(stream)}else if(type=="interpolation"){return pushContext(state,stream,"interpolation")}return"prop"};states.propBlock=function(type,_stream,state){if(type=="}")return popContext(state);if(type=="word"){override="property";return"maybeprop"}return state.context.type};states.parens=function(type,stream,state){if(type=="{"||type=="}")return popAndPass(type,stream,state);if(type==")")return popContext(state);if(type=="(")return pushContext(state,stream,"parens");if(type=="interpolation")return pushContext(state,stream,"interpolation");if(type=="word")wordAsValue(stream);return"parens"};states.pseudo=function(type,stream,state){if(type=="meta")return"pseudo";if(type=="word"){override="variable-3";return state.context.type}return pass(type,stream,state)};states.documentTypes=function(type,stream,state){if(type=="word"&&documentTypes.hasOwnProperty(stream.current())){override="tag";return state.context.type}else{return states.atBlock(type,stream,state)}};states.atBlock=function(type,stream,state){if(type=="(")return pushContext(state,stream,"atBlock_parens");if(type=="}"||type==";")return popAndPass(type,stream,state);if(type=="{")return popContext(state)&&pushContext(state,stream,allowNested?"block":"top");if(type=="interpolation")return pushContext(state,stream,"interpolation");if(type=="word"){var word=stream.current().toLowerCase();if(word=="only"||word=="not"||word=="and"||word=="or")override="keyword";else if(mediaTypes.hasOwnProperty(word))override="attribute";else if(mediaFeatures.hasOwnProperty(word))override="property";else if(mediaValueKeywords.hasOwnProperty(word))override="keyword";else if(propertyKeywords.hasOwnProperty(word))override="property";else if(nonStandardPropertyKeywords.hasOwnProperty(word))override=highlightNonStandardPropertyKeywords?"string-2":"property";else if(valueKeywords.hasOwnProperty(word))override="atom";else if(colorKeywords.hasOwnProperty(word))override="keyword";else override="error"}return state.context.type};states.atComponentBlock=function(type,stream,state){if(type=="}")return popAndPass(type,stream,state);if(type=="{")return popContext(state)&&pushContext(state,stream,allowNested?"block":"top",false);if(type=="word")override="error";return state.context.type};states.atBlock_parens=function(type,stream,state){if(type==")")return popContext(state);if(type=="{"||type=="}")return popAndPass(type,stream,state,2);return states.atBlock(type,stream,state)};states.restricted_atBlock_before=function(type,stream,state){if(type=="{")return pushContext(state,stream,"restricted_atBlock");if(type=="word"&&state.stateArg=="@counter-style"){override="variable";return"restricted_atBlock_before"}return pass(type,stream,state)};states.restricted_atBlock=function(type,stream,state){if(type=="}"){state.stateArg=null;return popContext(state)}if(type=="word"){if(state.stateArg=="@font-face"&&!fontProperties.hasOwnProperty(stream.current().toLowerCase())||state.stateArg=="@counter-style"&&!counterDescriptors.hasOwnProperty(stream.current().toLowerCase()))override="error";else override="property";return"maybeprop"}return"restricted_atBlock"};states.keyframes=function(type,stream,state){if(type=="word"){override="variable";return"keyframes"}if(type=="{")return pushContext(state,stream,"top");return pass(type,stream,state)};states.at=function(type,stream,state){if(type==";")return popContext(state);if(type=="{"||type=="}")return popAndPass(type,stream,state);if(type=="word")override="tag";else if(type=="hash")override="builtin";return"at"};states.interpolation=function(type,stream,state){if(type=="}")return popContext(state);if(type=="{"||type==";")return popAndPass(type,stream,state);if(type=="word")override="variable";else if(type!="variable"&&type!="("&&type!=")")override="error";return"interpolation"};return{startState:function(base){return{tokenize:null,state:inline?"block":"top",stateArg:null,context:new Context(inline?"block":"top",base||0,null)}},token:function(stream,state){if(!state.tokenize&&stream.eatSpace())return null;var style=(state.tokenize||tokenBase)(stream,state);if(style&&typeof style=="object"){type=style[1];style=style[0]}override=style;if(type!="comment")state.state=states[state.state](type,stream,state);return override},indent:function(state,textAfter){var cx=state.context,ch=textAfter&&textAfter.charAt(0);var indent=cx.indent;if(cx.type=="prop"&&(ch=="}"||ch==")"))cx=cx.prev;if(cx.prev){if(ch=="}"&&(cx.type=="block"||cx.type=="top"||cx.type=="interpolation"||cx.type=="restricted_atBlock")){cx=cx.prev;indent=cx.indent}else if(ch==")"&&(cx.type=="parens"||cx.type=="atBlock_parens")||ch=="{"&&(cx.type=="at"||cx.type=="atBlock")){indent=Math.max(0,cx.indent-indentUnit)}}return indent},electricChars:"}",blockCommentStart:"/*",blockCommentEnd:"*/",blockCommentContinue:" * ",lineComment:lineComment,fold:"brace"}}));function keySet(array){var keys={};for(var i=0;i<array.length;++i){keys[array[i].toLowerCase()]=true}return keys}var documentTypes_=["domain","regexp","url","url-prefix"],documentTypes=keySet(documentTypes_);var mediaTypes_=["all","aural","braille","handheld","print","projection","screen","tty","tv","embossed"],mediaTypes=keySet(mediaTypes_);var mediaFeatures_=["width","min-width","max-width","height","min-height","max-height","device-width","min-device-width","max-device-width","device-height","min-device-height","max-device-height","aspect-ratio","min-aspect-ratio","max-aspect-ratio","device-aspect-ratio","min-device-aspect-ratio","max-device-aspect-ratio","color","min-color","max-color","color-index","min-color-index","max-color-index","monochrome","min-monochrome","max-monochrome","resolution","min-resolution","max-resolution","scan","grid","orientation","device-pixel-ratio","min-device-pixel-ratio","max-device-pixel-ratio","pointer","any-pointer","hover","any-hover","prefers-color-scheme","dynamic-range","video-dynamic-range"],mediaFeatures=keySet(mediaFeatures_);var mediaValueKeywords_=["landscape","portrait","none","coarse","fine","on-demand","hover","interlace","progressive","dark","light","standard","high"],mediaValueKeywords=keySet(mediaValueKeywords_);var propertyKeywords_=["align-content","align-items","align-self","alignment-adjust","alignment-baseline","all","anchor-point","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","appearance","azimuth","backdrop-filter","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-position-x","background-position-y","background-repeat","background-size","baseline-shift","binding","bleed","block-size","bookmark-label","bookmark-level","bookmark-state","bookmark-target","border","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","color","color-profile","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","content","counter-increment","counter-reset","crop","cue","cue-after","cue-before","cursor","direction","display","dominant-baseline","drop-initial-after-adjust","drop-initial-after-align","drop-initial-before-adjust","drop-initial-before-align","drop-initial-size","drop-initial-value","elevation","empty-cells","fit","fit-content","fit-position","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","float-offset","flow-from","flow-into","font","font-family","font-feature-settings","font-kerning","font-language-override","font-optical-sizing","font-size","font-size-adjust","font-stretch","font-style","font-synthesis","font-variant","font-variant-alternates","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","gap","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-gap","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-gap","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","inline-box-align","inset","inset-block","inset-block-end","inset-block-start","inset-inline","inset-inline-end","inset-inline-start","isolation","justify-content","justify-items","justify-self","left","letter-spacing","line-break","line-height","line-height-step","line-stacking","line-stacking-ruby","line-stacking-shift","line-stacking-strategy","list-style","list-style-image","list-style-position","list-style-type","margin","margin-bottom","margin-left","margin-right","margin-top","marks","marquee-direction","marquee-loop","marquee-play-count","marquee-speed","marquee-style","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","move-to","nav-down","nav-index","nav-left","nav-right","nav-up","object-fit","object-position","offset","offset-anchor","offset-distance","offset-path","offset-position","offset-rotate","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-style","overflow-wrap","overflow-x","overflow-y","padding","padding-bottom","padding-left","padding-right","padding-top","page","page-break-after","page-break-before","page-break-inside","page-policy","pause","pause-after","pause-before","perspective","perspective-origin","pitch","pitch-range","place-content","place-items","place-self","play-during","position","presentation-level","punctuation-trim","quotes","region-break-after","region-break-before","region-break-inside","region-fragment","rendering-intent","resize","rest","rest-after","rest-before","richness","right","rotate","rotation","rotation-point","row-gap","ruby-align","ruby-overhang","ruby-position","ruby-span","scale","scroll-behavior","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-type","shape-image-threshold","shape-inside","shape-margin","shape-outside","size","speak","speak-as","speak-header","speak-numeral","speak-punctuation","speech-rate","stress","string-set","tab-size","table-layout","target","target-name","target-new","target-position","text-align","text-align-last","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-skip","text-decoration-skip-ink","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-height","text-indent","text-justify","text-orientation","text-outline","text-overflow","text-rendering","text-shadow","text-size-adjust","text-space-collapse","text-transform","text-underline-position","text-wrap","top","touch-action","transform","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","translate","unicode-bidi","user-select","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","z-index","clip-path","clip-rule","mask","enable-background","filter","flood-color","flood-opacity","lighting-color","stop-color","stop-opacity","pointer-events","color-interpolation","color-interpolation-filters","color-rendering","fill","fill-opacity","fill-rule","image-rendering","marker","marker-end","marker-mid","marker-start","paint-order","shape-rendering","stroke","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","text-rendering","baseline-shift","dominant-baseline","glyph-orientation-horizontal","glyph-orientation-vertical","text-anchor","writing-mode"],propertyKeywords=keySet(propertyKeywords_);var nonStandardPropertyKeywords_=["accent-color","aspect-ratio","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","content-visibility","margin-block","margin-block-end","margin-block-start","margin-inline","margin-inline-end","margin-inline-start","overflow-anchor","overscroll-behavior","padding-block","padding-block-end","padding-block-start","padding-inline","padding-inline-end","padding-inline-start","scroll-snap-stop","scrollbar-3d-light-color","scrollbar-arrow-color","scrollbar-base-color","scrollbar-dark-shadow-color","scrollbar-face-color","scrollbar-highlight-color","scrollbar-shadow-color","scrollbar-track-color","searchfield-cancel-button","searchfield-decoration","searchfield-results-button","searchfield-results-decoration","shape-inside","zoom"],nonStandardPropertyKeywords=keySet(nonStandardPropertyKeywords_);var fontProperties_=["font-display","font-family","src","unicode-range","font-variant","font-feature-settings","font-stretch","font-weight","font-style"],fontProperties=keySet(fontProperties_);var counterDescriptors_=["additive-symbols","fallback","negative","pad","prefix","range","speak-as","suffix","symbols","system"],counterDescriptors=keySet(counterDescriptors_);var colorKeywords_=["aliceblue","antiquewhite","aqua","aquamarine","azure","beige","bisque","black","blanchedalmond","blue","blueviolet","brown","burlywood","cadetblue","chartreuse","chocolate","coral","cornflowerblue","cornsilk","crimson","cyan","darkblue","darkcyan","darkgoldenrod","darkgray","darkgreen","darkgrey","darkkhaki","darkmagenta","darkolivegreen","darkorange","darkorchid","darkred","darksalmon","darkseagreen","darkslateblue","darkslategray","darkslategrey","darkturquoise","darkviolet","deeppink","deepskyblue","dimgray","dimgrey","dodgerblue","firebrick","floralwhite","forestgreen","fuchsia","gainsboro","ghostwhite","gold","goldenrod","gray","grey","green","greenyellow","honeydew","hotpink","indianred","indigo","ivory","khaki","lavender","lavenderblush","lawngreen","lemonchiffon","lightblue","lightcoral","lightcyan","lightgoldenrodyellow","lightgray","lightgreen","lightgrey","lightpink","lightsalmon","lightseagreen","lightskyblue","lightslategray","lightslategrey","lightsteelblue","lightyellow","lime","limegreen","linen","magenta","maroon","mediumaquamarine","mediumblue","mediumorchid","mediumpurple","mediumseagreen","mediumslateblue","mediumspringgreen","mediumturquoise","mediumvioletred","midnightblue","mintcream","mistyrose","moccasin","navajowhite","navy","oldlace","olive","olivedrab","orange","orangered","orchid","palegoldenrod","palegreen","paleturquoise","palevioletred","papayawhip","peachpuff","peru","pink","plum","powderblue","purple","rebeccapurple","red","rosybrown","royalblue","saddlebrown","salmon","sandybrown","seagreen","seashell","sienna","silver","skyblue","slateblue","slategray","slategrey","snow","springgreen","steelblue","tan","teal","thistle","tomato","turquoise","violet","wheat","white","whitesmoke","yellow","yellowgreen"],colorKeywords=keySet(colorKeywords_);var valueKeywords_=["above","absolute","activeborder","additive","activecaption","afar","after-white-space","ahead","alias","all","all-scroll","alphabetic","alternate","always","amharic","amharic-abegede","antialiased","appworkspace","arabic-indic","armenian","asterisks","attr","auto","auto-flow","avoid","avoid-column","avoid-page","avoid-region","axis-pan","background","backwards","baseline","below","bidi-override","binary","bengali","blink","block","block-axis","blur","bold","bolder","border","border-box","both","bottom","break","break-all","break-word","brightness","bullets","button","buttonface","buttonhighlight","buttonshadow","buttontext","calc","cambodian","capitalize","caps-lock-indicator","caption","captiontext","caret","cell","center","checkbox","circle","cjk-decimal","cjk-earthly-branch","cjk-heavenly-stem","cjk-ideographic","clear","clip","close-quote","col-resize","collapse","color","color-burn","color-dodge","column","column-reverse","compact","condensed","conic-gradient","contain","content","contents","content-box","context-menu","continuous","contrast","copy","counter","counters","cover","crop","cross","crosshair","cubic-bezier","currentcolor","cursive","cyclic","darken","dashed","decimal","decimal-leading-zero","default","default-button","dense","destination-atop","destination-in","destination-out","destination-over","devanagari","difference","disc","discard","disclosure-closed","disclosure-open","document","dot-dash","dot-dot-dash","dotted","double","down","drop-shadow","e-resize","ease","ease-in","ease-in-out","ease-out","element","ellipse","ellipsis","embed","end","ethiopic","ethiopic-abegede","ethiopic-abegede-am-et","ethiopic-abegede-gez","ethiopic-abegede-ti-er","ethiopic-abegede-ti-et","ethiopic-halehame-aa-er","ethiopic-halehame-aa-et","ethiopic-halehame-am-et","ethiopic-halehame-gez","ethiopic-halehame-om-et","ethiopic-halehame-sid-et","ethiopic-halehame-so-et","ethiopic-halehame-ti-er","ethiopic-halehame-ti-et","ethiopic-halehame-tig","ethiopic-numeric","ew-resize","exclusion","expanded","extends","extra-condensed","extra-expanded","fantasy","fast","fill","fill-box","fixed","flat","flex","flex-end","flex-start","footnotes","forwards","from","geometricPrecision","georgian","grayscale","graytext","grid","groove","gujarati","gurmukhi","hand","hangul","hangul-consonant","hard-light","hebrew","help","hidden","hide","higher","highlight","highlighttext","hiragana","hiragana-iroha","horizontal","hsl","hsla","hue","hue-rotate","icon","ignore","inactiveborder","inactivecaption","inactivecaptiontext","infinite","infobackground","infotext","inherit","initial","inline","inline-axis","inline-block","inline-flex","inline-grid","inline-table","inset","inside","intrinsic","invert","italic","japanese-formal","japanese-informal","justify","kannada","katakana","katakana-iroha","keep-all","khmer","korean-hangul-formal","korean-hanja-formal","korean-hanja-informal","landscape","lao","large","larger","left","level","lighter","lighten","line-through","linear","linear-gradient","lines","list-item","listbox","listitem","local","logical","loud","lower","lower-alpha","lower-armenian","lower-greek","lower-hexadecimal","lower-latin","lower-norwegian","lower-roman","lowercase","ltr","luminosity","malayalam","manipulation","match","matrix","matrix3d","media-play-button","media-slider","media-sliderthumb","media-volume-slider","media-volume-sliderthumb","medium","menu","menulist","menulist-button","menutext","message-box","middle","min-intrinsic","mix","mongolian","monospace","move","multiple","multiple_mask_images","multiply","myanmar","n-resize","narrower","ne-resize","nesw-resize","no-close-quote","no-drop","no-open-quote","no-repeat","none","normal","not-allowed","nowrap","ns-resize","numbers","numeric","nw-resize","nwse-resize","oblique","octal","opacity","open-quote","optimizeLegibility","optimizeSpeed","oriya","oromo","outset","outside","outside-shape","overlay","overline","padding","padding-box","painted","page","paused","persian","perspective","pinch-zoom","plus-darker","plus-lighter","pointer","polygon","portrait","pre","pre-line","pre-wrap","preserve-3d","progress","push-button","radial-gradient","radio","read-only","read-write","read-write-plaintext-only","rectangle","region","relative","repeat","repeating-linear-gradient","repeating-radial-gradient","repeating-conic-gradient","repeat-x","repeat-y","reset","reverse","rgb","rgba","ridge","right","rotate","rotate3d","rotateX","rotateY","rotateZ","round","row","row-resize","row-reverse","rtl","run-in","running","s-resize","sans-serif","saturate","saturation","scale","scale3d","scaleX","scaleY","scaleZ","screen","scroll","scrollbar","scroll-position","se-resize","searchfield","searchfield-cancel-button","searchfield-decoration","searchfield-results-button","searchfield-results-decoration","self-start","self-end","semi-condensed","semi-expanded","separate","sepia","serif","show","sidama","simp-chinese-formal","simp-chinese-informal","single","skew","skewX","skewY","skip-white-space","slide","slider-horizontal","slider-vertical","sliderthumb-horizontal","sliderthumb-vertical","slow","small","small-caps","small-caption","smaller","soft-light","solid","somali","source-atop","source-in","source-out","source-over","space","space-around","space-between","space-evenly","spell-out","square","square-button","start","static","status-bar","stretch","stroke","stroke-box","sub","subpixel-antialiased","svg_masks","super","sw-resize","symbolic","symbols","system-ui","table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row","table-row-group","tamil","telugu","text","text-bottom","text-top","textarea","textfield","thai","thick","thin","threeddarkshadow","threedface","threedhighlight","threedlightshadow","threedshadow","tibetan","tigre","tigrinya-er","tigrinya-er-abegede","tigrinya-et","tigrinya-et-abegede","to","top","trad-chinese-formal","trad-chinese-informal","transform","translate","translate3d","translateX","translateY","translateZ","transparent","ultra-condensed","ultra-expanded","underline","unidirectional-pan","unset","up","upper-alpha","upper-armenian","upper-greek","upper-hexadecimal","upper-latin","upper-norwegian","upper-roman","uppercase","urdu","url","var","vertical","vertical-text","view-box","visible","visibleFill","visiblePainted","visibleStroke","visual","w-resize","wait","wave","wider","window","windowframe","windowtext","words","wrap","wrap-reverse","x-large","x-small","xor","xx-large","xx-small"],valueKeywords=keySet(valueKeywords_);var allWords=documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_).concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_).concat(valueKeywords_);CodeMirror.registerHelper("hintWords","css",allWords);function tokenCComment(stream,state){var maybeEnd=false,ch;while((ch=stream.next())!=null){if(maybeEnd&&ch=="/"){state.tokenize=null;break}maybeEnd=ch=="*"}return["comment","comment"]}CodeMirror.defineMIME("text/css",{documentTypes:documentTypes,mediaTypes:mediaTypes,mediaFeatures:mediaFeatures,mediaValueKeywords:mediaValueKeywords,propertyKeywords:propertyKeywords,nonStandardPropertyKeywords:nonStandardPropertyKeywords,fontProperties:fontProperties,counterDescriptors:counterDescriptors,colorKeywords:colorKeywords,valueKeywords:valueKeywords,tokenHooks:{"/":function(stream,state){if(!stream.eat("*"))return false;state.tokenize=tokenCComment;return tokenCComment(stream,state)}},name:"css"});CodeMirror.defineMIME("text/x-scss",{mediaTypes:mediaTypes,mediaFeatures:mediaFeatures,mediaValueKeywords:mediaValueKeywords,propertyKeywords:propertyKeywords,nonStandardPropertyKeywords:nonStandardPropertyKeywords,colorKeywords:colorKeywords,valueKeywords:valueKeywords,fontProperties:fontProperties,allowNested:true,lineComment:"//",tokenHooks:{"/":function(stream,state){if(stream.eat("/")){stream.skipToEnd();return["comment","comment"]}else if(stream.eat("*")){state.tokenize=tokenCComment;return tokenCComment(stream,state)}else{return["operator","operator"]}},":":function(stream){if(stream.match(/^\s*\{/,false))return[null,null];return false},$:function(stream){stream.match(/^[\w-]+/);if(stream.match(/^\s*:/,false))return["variable-2","variable-definition"];return["variable-2","variable"]},"#":function(stream){if(!stream.eat("{"))return false;return[null,"interpolation"]}},name:"css",helperType:"scss"});CodeMirror.defineMIME("text/x-less",{mediaTypes:mediaTypes,mediaFeatures:mediaFeatures,mediaValueKeywords:mediaValueKeywords,propertyKeywords:propertyKeywords,nonStandardPropertyKeywords:nonStandardPropertyKeywords,colorKeywords:colorKeywords,valueKeywords:valueKeywords,fontProperties:fontProperties,allowNested:true,lineComment:"//",tokenHooks:{"/":function(stream,state){if(stream.eat("/")){stream.skipToEnd();return["comment","comment"]}else if(stream.eat("*")){state.tokenize=tokenCComment;return tokenCComment(stream,state)}else{return["operator","operator"]}},"@":function(stream){if(stream.eat("{"))return[null,"interpolation"];if(stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i,false))return false;stream.eatWhile(/[\w\\\-]/);if(stream.match(/^\s*:/,false))return["variable-2","variable-definition"];return["variable-2","variable"]},"&":function(){return["atom","atom"]}},name:"css",helperType:"less"});CodeMirror.defineMIME("text/x-gss",{documentTypes:documentTypes,mediaTypes:mediaTypes,mediaFeatures:mediaFeatures,propertyKeywords:propertyKeywords,nonStandardPropertyKeywords:nonStandardPropertyKeywords,fontProperties:fontProperties,counterDescriptors:counterDescriptors,colorKeywords:colorKeywords,valueKeywords:valueKeywords,supportsAtComponent:true,tokenHooks:{"/":function(stream,state){if(!stream.eat("*"))return false;state.tokenize=tokenCComment;return tokenCComment(stream,state)}},name:"css",helperType:"gss"})}));(function(mod){if(typeof exports=="object"&&typeof module=="object")mod(require("../../lib/codemirror"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror"],mod);else mod(CodeMirror)})((function(CodeMirror){"use strict";var WRAP_CLASS="CodeMirror-activeline";var BACK_CLASS="CodeMirror-activeline-background";var GUTT_CLASS="CodeMirror-activeline-gutter";CodeMirror.defineOption("styleActiveLine",false,(function(cm,val,old){var prev=old==CodeMirror.Init?false:old;if(val==prev)return;if(prev){cm.off("beforeSelectionChange",selectionChange);clearActiveLines(cm);delete cm.state.activeLines}if(val){cm.state.activeLines=[];updateActiveLines(cm,cm.listSelections());cm.on("beforeSelectionChange",selectionChange)}}));function clearActiveLines(cm){for(var i=0;i<cm.state.activeLines.length;i++){cm.removeLineClass(cm.state.activeLines[i],"wrap",WRAP_CLASS);cm.removeLineClass(cm.state.activeLines[i],"background",BACK_CLASS);cm.removeLineClass(cm.state.activeLines[i],"gutter",GUTT_CLASS)}}function sameArray(a,b){if(a.length!=b.length)return false;for(var i=0;i<a.length;i++)if(a[i]!=b[i])return false;return true}function updateActiveLines(cm,ranges){var active=[];for(var i=0;i<ranges.length;i++){var range=ranges[i];var option=cm.getOption("styleActiveLine");if(typeof option=="object"&&option.nonEmpty?range.anchor.line!=range.head.line:!range.empty())continue;var line=cm.getLineHandleVisualStart(range.head.line);if(active[active.length-1]!=line)active.push(line)}if(sameArray(cm.state.activeLines,active))return;cm.operation((function(){clearActiveLines(cm);for(var i=0;i<active.length;i++){cm.addLineClass(active[i],"wrap",WRAP_CLASS);cm.addLineClass(active[i],"background",BACK_CLASS);cm.addLineClass(active[i],"gutter",GUTT_CLASS)}cm.state.activeLines=active}))}function selectionChange(cm,sel){updateActiveLines(cm,sel.ranges)}}));(function(mod){if(typeof exports=="object"&&typeof module=="object")mod(require("../../lib/codemirror"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror"],mod);else mod(CodeMirror)})((function(CodeMirror){var nonspace=/\S/g;var repeat=String.prototype.repeat||function(n){return Array(n+1).join(this)};function continueComment(cm){if(cm.getOption("disableInput"))return CodeMirror.Pass;var ranges=cm.listSelections(),mode,inserts=[];for(var i=0;i<ranges.length;i++){var pos=ranges[i].head;if(!/\bcomment\b/.test(cm.getTokenTypeAt(pos)))return CodeMirror.Pass;var modeHere=cm.getModeAt(pos);if(!mode)mode=modeHere;else if(mode!=modeHere)return CodeMirror.Pass;var insert=null,line,found;var blockStart=mode.blockCommentStart,lineCmt=mode.lineComment;if(blockStart&&mode.blockCommentContinue){line=cm.getLine(pos.line);var end=line.lastIndexOf(mode.blockCommentEnd,pos.ch-mode.blockCommentEnd.length);if(end!=-1&&end==pos.ch-mode.blockCommentEnd.length||lineCmt&&(found=line.lastIndexOf(lineCmt,pos.ch-1))>-1&&/\bcomment\b/.test(cm.getTokenTypeAt({line:pos.line,ch:found+1}))){}else if(pos.ch>=blockStart.length&&(found=line.lastIndexOf(blockStart,pos.ch-blockStart.length))>-1&&found>end){if(nonspaceAfter(0,line)>=found){insert=line.slice(0,found)}else{var tabSize=cm.options.tabSize,numTabs;found=CodeMirror.countColumn(line,found,tabSize);insert=!cm.options.indentWithTabs?repeat.call(" ",found):repeat.call("\t",numTabs=Math.floor(found/tabSize))+repeat.call(" ",found-tabSize*numTabs)}}else if((found=line.indexOf(mode.blockCommentContinue))>-1&&found<=pos.ch&&found<=nonspaceAfter(0,line)){insert=line.slice(0,found)}if(insert!=null)insert+=mode.blockCommentContinue}if(insert==null&&lineCmt&&continueLineCommentEnabled(cm)){if(line==null)line=cm.getLine(pos.line);found=line.indexOf(lineCmt);if(!pos.ch&&!found)insert="";else if(found>-1&&nonspaceAfter(0,line)>=found){insert=nonspaceAfter(pos.ch,line)>-1;if(!insert){var next=cm.getLine(pos.line+1)||"",nextFound=next.indexOf(lineCmt);insert=nextFound>-1&&nonspaceAfter(0,next)>=nextFound||null}if(insert){insert=line.slice(0,found)+lineCmt+line.slice(found+lineCmt.length).match(/^\s*/)[0]}}}if(insert==null)return CodeMirror.Pass;inserts[i]="\n"+insert}cm.operation((function(){for(var i=ranges.length-1;i>=0;i--)cm.replaceRange(inserts[i],ranges[i].from(),ranges[i].to(),"+insert")}))}function nonspaceAfter(ch,str){nonspace.lastIndex=ch;var m=nonspace.exec(str);return m?m.index:-1}function continueLineCommentEnabled(cm){var opt=cm.getOption("continueComments");if(opt&&typeof opt=="object")return opt.continueLineComment!==false;return true}CodeMirror.defineOption("continueComments",null,(function(cm,val,prev){if(prev&&prev!=CodeMirror.Init)cm.removeKeyMap("continueComment");if(val){var key="Enter";if(typeof val=="string")key=val;else if(typeof val=="object"&&val.key)key=val.key;var map={name:"continueComment"};map[key]=continueComment;cm.addKeyMap(map)}}))}));(function(mod){if(typeof exports=="object"&&typeof module=="object")mod(require("../../lib/codemirror"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror"],mod);else mod(CodeMirror)})((function(CodeMirror){"use strict";CodeMirror.defineOption("selectionPointer",false,(function(cm,val){var data=cm.state.selectionPointer;if(data){CodeMirror.off(cm.getWrapperElement(),"mousemove",data.mousemove);CodeMirror.off(cm.getWrapperElement(),"mouseout",data.mouseout);CodeMirror.off(window,"scroll",data.windowScroll);cm.off("cursorActivity",reset);cm.off("scroll",reset);cm.state.selectionPointer=null;cm.display.lineDiv.style.cursor=""}if(val){data=cm.state.selectionPointer={value:typeof val=="string"?val:"default",mousemove:function(event){mousemove(cm,event)},mouseout:function(event){mouseout(cm,event)},windowScroll:function(){reset(cm)},rects:null,mouseX:null,mouseY:null,willUpdate:false};CodeMirror.on(cm.getWrapperElement(),"mousemove",data.mousemove);CodeMirror.on(cm.getWrapperElement(),"mouseout",data.mouseout);CodeMirror.on(window,"scroll",data.windowScroll);cm.on("cursorActivity",reset);cm.on("scroll",reset)}}));function mousemove(cm,event){var data=cm.state.selectionPointer;if(event.buttons==null?event.which:event.buttons){data.mouseX=data.mouseY=null}else{data.mouseX=event.clientX;data.mouseY=event.clientY}scheduleUpdate(cm)}function mouseout(cm,event){if(!cm.getWrapperElement().contains(event.relatedTarget)){var data=cm.state.selectionPointer;data.mouseX=data.mouseY=null;scheduleUpdate(cm)}}function reset(cm){cm.state.selectionPointer.rects=null;scheduleUpdate(cm)}function scheduleUpdate(cm){if(!cm.state.selectionPointer.willUpdate){cm.state.selectionPointer.willUpdate=true;setTimeout((function(){update(cm);cm.state.selectionPointer.willUpdate=false}),50)}}function update(cm){var data=cm.state.selectionPointer;if(!data)return;if(data.rects==null&&data.mouseX!=null){data.rects=[];if(cm.somethingSelected()){for(var sel=cm.display.selectionDiv.firstChild;sel;sel=sel.nextSibling)data.rects.push(sel.getBoundingClientRect())}}var inside=false;if(data.mouseX!=null)for(var i=0;i<data.rects.length;i++){var rect=data.rects[i];if(rect.left<=data.mouseX&&rect.right>=data.mouseX&&rect.top<=data.mouseY&&rect.bottom>=data.mouseY)inside=true}var cursor=inside?data.value:"";if(cm.display.lineDiv.style.cursor!=cursor)cm.display.lineDiv.style.cursor=cursor}}));(function(mod){if(typeof exports=="object"&&typeof module=="object")mod(require("../../lib/codemirror"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror"],mod);else mod(CodeMirror)})((function(CodeMirror){var ie_lt8=/MSIE \d/.test(navigator.userAgent)&&(document.documentMode==null||document.documentMode<8);var Pos=CodeMirror.Pos;var matching={"(":")>",")":"(<","[":"]>","]":"[<","{":"}>","}":"{<","<":">>",">":"<<"};function bracketRegex(config){return config&&config.bracketRegex||/[(){}[\]]/}function findMatchingBracket(cm,where,config){var line=cm.getLineHandle(where.line),pos=where.ch-1;var afterCursor=config&&config.afterCursor;if(afterCursor==null)afterCursor=/(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className);var re=bracketRegex(config);var match=!afterCursor&&pos>=0&&re.test(line.text.charAt(pos))&&matching[line.text.charAt(pos)]||re.test(line.text.charAt(pos+1))&&matching[line.text.charAt(++pos)];if(!match)return null;var dir=match.charAt(1)==">"?1:-1;if(config&&config.strict&&dir>0!=(pos==where.ch))return null;var style=cm.getTokenTypeAt(Pos(where.line,pos+1));var found=scanForBracket(cm,Pos(where.line,pos+(dir>0?1:0)),dir,style,config);if(found==null)return null;return{from:Pos(where.line,pos),to:found&&found.pos,match:found&&found.ch==match.charAt(0),forward:dir>0}}function scanForBracket(cm,where,dir,style,config){var maxScanLen=config&&config.maxScanLineLength||1e4;var maxScanLines=config&&config.maxScanLines||1e3;var stack=[];var re=bracketRegex(config);var lineEnd=dir>0?Math.min(where.line+maxScanLines,cm.lastLine()+1):Math.max(cm.firstLine()-1,where.line-maxScanLines);for(var lineNo=where.line;lineNo!=lineEnd;lineNo+=dir){var line=cm.getLine(lineNo);if(!line)continue;var pos=dir>0?0:line.length-1,end=dir>0?line.length:-1;if(line.length>maxScanLen)continue;if(lineNo==where.line)pos=where.ch-(dir<0?1:0);for(;pos!=end;pos+=dir){var ch=line.charAt(pos);if(re.test(ch)&&(style===undefined||(cm.getTokenTypeAt(Pos(lineNo,pos+1))||"")==(style||""))){var match=matching[ch];if(match&&match.charAt(1)==">"==dir>0)stack.push(ch);else if(!stack.length)return{pos:Pos(lineNo,pos),ch:ch};else stack.pop()}}}return lineNo-dir==(dir>0?cm.lastLine():cm.firstLine())?false:null}function matchBrackets(cm,autoclear,config){var maxHighlightLen=cm.state.matchBrackets.maxHighlightLineLength||1e3,highlightNonMatching=config&&config.highlightNonMatching;var marks=[],ranges=cm.listSelections();for(var i=0;i<ranges.length;i++){var match=ranges[i].empty()&&findMatchingBracket(cm,ranges[i].head,config);if(match&&(match.match||highlightNonMatching!==false)&&cm.getLine(match.from.line).length<=maxHighlightLen){var style=match.match?"CodeMirror-matchingbracket":"CodeMirror-nonmatchingbracket";marks.push(cm.markText(match.from,Pos(match.from.line,match.from.ch+1),{className:style}));if(match.to&&cm.getLine(match.to.line).length<=maxHighlightLen)marks.push(cm.markText(match.to,Pos(match.to.line,match.to.ch+1),{className:style}))}}if(marks.length){if(ie_lt8&&cm.state.focused)cm.focus();var clear=function(){cm.operation((function(){for(var i=0;i<marks.length;i++)marks[i].clear()}))};if(autoclear)setTimeout(clear,800);else return clear}}function doMatchBrackets(cm){cm.operation((function(){if(cm.state.matchBrackets.currentlyHighlighted){cm.state.matchBrackets.currentlyHighlighted();cm.state.matchBrackets.currentlyHighlighted=null}cm.state.matchBrackets.currentlyHighlighted=matchBrackets(cm,false,cm.state.matchBrackets)}))}function clearHighlighted(cm){if(cm.state.matchBrackets&&cm.state.matchBrackets.currentlyHighlighted){cm.state.matchBrackets.currentlyHighlighted();cm.state.matchBrackets.currentlyHighlighted=null}}CodeMirror.defineOption("matchBrackets",false,(function(cm,val,old){if(old&&old!=CodeMirror.Init){cm.off("cursorActivity",doMatchBrackets);cm.off("focus",doMatchBrackets);cm.off("blur",clearHighlighted);clearHighlighted(cm)}if(val){cm.state.matchBrackets=typeof val=="object"?val:{};cm.on("cursorActivity",doMatchBrackets);cm.on("focus",doMatchBrackets);cm.on("blur",clearHighlighted)}}));CodeMirror.defineExtension("matchBrackets",(function(){matchBrackets(this,true)}));CodeMirror.defineExtension("findMatchingBracket",(function(pos,config,oldConfig){if(oldConfig||typeof config=="boolean"){if(!oldConfig){config=config?{strict:true}:null}else{oldConfig.strict=config;config=oldConfig}}return findMatchingBracket(this,pos,config)}));CodeMirror.defineExtension("scanForBracket",(function(pos,dir,style,config){return scanForBracket(this,pos,dir,style,config)}))}));
</script>

        <script>
// for playing about and testing: http://jsfiddle.net/TcqAf/529/

// newer version: http://jsbin.com/filalidobe/1/edit?html,output
// problem is, even if I do work out this: https://stackoverflow.com/questions/52160551/way-to-overlay-multiple-tokens-rules-with-codemirror-simple-mode
//   i'll still have to sort out this [a = [], "hello"] - probably need to write a proper mode (using same logic that breaks up square blocks)

// CodeMirror.defineSimpleMode("simplemode", {
//   // The start state contains the rules that are intially used
//   start: [
//       // You can embed other modes with the mode property. This rule
//     // causes all code between << and >> to be highlighted with the XML
//     // mode.
// //    {regex: /(?:^|[^\\])(?:\\\\)*(\[)/, token: "variable-3", mode: {spec: "javascript", end: /(?:^|[^\\])(?:\\\\)*(\])/}},
//     // The regex matches the token, the token property contains the type
//     //{regex: /"(?:[^\\]|\\.)*?(?:"|$)/, token: "string"},
//     // You can match multiple tokens at once. Note that the captured
//     // groups must span the whole string in this case
//     {regex: /([a-zA-Z][a-zA-Z0-9$_]+)(\s*)(\()([^)]*)(\))(\s*)(=>)/,
//      token: ["variable-2", null, "keyword", null, "keyword", null, "keyword"]},

//     // curly
//     {regex: /\{[a-zA-Z]-[a-zA-Z]\}/, token: "syntax-style1"},
//     {regex: /\{[0-9]+-[0-9]+\}/, token: "syntax-style1"},
//     {regex: /\{/, token: "syntax-style1"},
//     {regex: /\}/, token: "syntax-style1"},

//     // square
//     //{regex: /\[[^\]]+\]/, token: "variable-3"},
//     {regex: /\[/, token: "syntax-style2"},
//     {regex: /\]/, token: "syntax-style2"},

//     // equals
//     {regex: /=/, token: "keyword"},

//     // not sure
//     {regex: /\$output\b/, token: "variable-2"},
//     {regex: /\$preprocess\b/, token: "variable-2"},
    
//     //{regex: /true|false|null|undefined/, token: "atom"},
//     //{regex: /0x[a-f\d]+|[-+]?(?:\.\d+|\d+\.?\d*)(?:e[-+]?\d+)?/i,
//      //token: "number"},
//     {regex: /[\s](\/\/.*)/, token: "syntax-style3-comment"},
//     {regex: /(\/\/.*)/, token: "syntax-style3-comment", sol:true},
//     //{regex: /\/(?:[^\\]|\\.)*?\//, token: "variable-3"},
//     //{regex: /[-+\/*=<>!]+/, token: "operator"},
//     // indent and dedent properties guide autoindentation
//     //{regex: /=>/, indent: true},
//     //{regex: /[\}\]\)]/, dedent: true},
//     //{regex: /[a-z$][\w$]*/, token: "variable"},
//   ],
//   // The meta property contains global information about the mode. It
//   // can contain properties like lineComment, which are supported by
//   // all modes, and also directives like dontIndentStates, which are
//   // specific to simple modes.
//   meta: {
//     dontIndentStates: ["comment"],
//     lineComment: "//",
//     fold: "indent",
//   }
// });


// CodeMirror.defineMode("mustache", function(config, parserConfig) {
//   var mustacheOverlay = {
//     token: function(stream, state) {
//       var ch;
//       if (stream.match("{")) {
//         while ((ch = stream.next()) != null)
//           if (stream.next() == "}") {
//             stream.eat("}");
//             return "variable-2";
//           }
//       }
//       while (stream.next() != null && !stream.match("{", false)) {}
//       return null;
//     }
//   };
//   return CodeMirror.overlayMode(CodeMirror.getMode(config, parserConfig.backdrop || "text/html"), mustacheOverlay);
// });

try {

  CodeMirror.defineMode("perchancelists", function(config) {

    var jsMode = CodeMirror.getMode(config, "javascript");
  
    // this, in a sense, `trimStart`s the stream and then gets the next two characters
    function skipSpacesAndPeekTwo(stream) {
      let count = 0;
      let result = '';
      for (let i = stream.pos; i < stream.string.length; i++) {
        let char = stream.string.charAt(i);
        if (char !== ' ' && char !== '\t') {
          result += char;
          count++;
          if (count === 2) {
            return result;
          }
        }
      }
      return null;
    }
    
    function token(stream, state) {    
      if(state.inSquareBlock && state.inFunction) console.error("invalid state - cannot be both in function and square block at same time");
      if(stream.string.length > 50000) { // Consume the entire line if it's too long
        stream.skipToEnd();
        return null;  // No styling applied
      }
  
      if(stream.sol() && state.inSquareBlock) { // square blocks cannot span multiple lines
        state.bracketDepth = 0;
        state.inSquareBlock = false;
        state.localState = null;
        return null;
      }
  
      if(stream.sol() && state.inFunction && stream.indentation() <= state.functionIndent && skipSpacesAndPeekTwo(stream) !== "//") {
        state.inFunction = false;
        state.localState = null;
        stream.backUp(stream.current().length); // rewind to the beginning of the line (important)
        return null;
      }
    
      if(state.inSquareBlock || state.inFunction) {
        let style = jsMode.token(stream, state.localState);
        let current = stream.current();
    
        if(state.inFunction) {
          if(state.inSquareBlock) console.error("inSquareBlock while also inFunction");
          // note: we already early-exited in case of indentation-based function end, above, so we know we're still in the function here
          return style;
        } else if(state.inSquareBlock) {
          if(state.inFunction) console.error("inFunction while also inSquareBlock");
  
          // don't count square brackets that are in strings, regex, or comments:
          if(style !== "string" && style !== "string-2" && style !== "comment") {
            if(current === "]") {
              state.bracketDepth--;
              if (state.bracketDepth <= 0) {
                state.inSquareBlock = false;
                state.localState = null;
                return "square-bracket";
              }
            }
            if(current === "[") {
              state.bracketDepth++;
            }
            if(state.bracketDepth <= 0) {
              state.inSquareBlock = false;
              state.localState = null;
            }
          }
          
          return style;
        }
      } else {
        if(stream.sol() && stream.match(/\/\/.*/)) {
          return "comment";
        } else if (stream.match(/\s\/\/.*/)) {
          return "comment";
        }
        if(stream.peek() === "[") {
          state.bracketDepth = 1;
          state.inSquareBlock = true;
          state.localState = CodeMirror.startState(jsMode);
          stream.next();
          return "square-bracket";
        }
    
        // if(stream.match(/^\w*(?=\()/)) {
        //   return "def";
        // }
    
        if(stream.match(/^(async |)[a-zA-Z0-9$_]+ ?\([^\)]*\) *=> */)) {
          state.localState = CodeMirror.startState(jsMode);
          state.inFunction = true;
          state.functionIndent = stream.indentation();
          return "def";
        }
    
        if(stream.match('\\[') || stream.match('\\]')) {
          return "escaped-char";
        }
        if(stream.match('{') || stream.match('}')) {
          return "curly-bracket";
        }
        if(stream.match(/\$output\b/) || stream.match(/\$preprocess\b/)) {
          return "special-list-name";
        }
        stream.next();
        return null;
      }
    }
    
    return {
      startState: function() {
        return {
          inSquareBlock: false,
          localState: null,
          bracketDepth: 0,
          inFunction: false,
          functionIndent: 0,
        };
      },
      token: token,
      lineComment: "//",
      fold: "indent",
    };
  });

  // let systemIsInDarkMode = !!(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches);
  // window.codeMirrorModelTextEditor = CodeMirror.fromTextArea(document.body.querySelector("#input"), {
  //   lineNumbers: true,
  //   foldGutter: true,
  //   extraKeys: {
  //     //"Ctrl-Q": cm => cm.foldCode(cm.getCursor()),
  //     //"Ctrl-Y": cm => CodeMirror.commands.foldAll(cm),
  //     //"Ctrl-I": cm => CodeMirror.commands.unfoldAll(cm),
  //   },
  //   gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
  //   tabSize: 2,
  //   indentUnit: 2,
  //   indentWithTabs: false,
  //   matchBrackets: true,
  //   mode: "perchancelists",
  //   styleActiveLine: true,
  //   // mode: "text",
  //   lineWrapping:false,
  //   theme: systemIsInDarkMode ? "one-dark" : "one-light",
  //   keyMap: "sublime",
  // });
  // window.codeMirrorModelTextEditor.setOption("mode", "perchancelists");





  // OLD:
  // CodeMirror.defineMode("perchancelists", function(config) {

  //   var jsMode = CodeMirror.getMode(config, "javascript");

  //   function token(stream, state) {    
  //     if(state.inSquareBlock && state.inFunction) console.error("invalid state - cannot be both in function and square block at same time");
  //     if(stream.string.length > 50000) { // Consume the entire line if it's too long
  //       stream.skipToEnd();
  //       return null;  // No styling applied
  //     }

  //     if(state.inSquareBlock || state.inFunction) {
  //       var style = jsMode.token(stream, state.localState);
  //       if (style === "string" || style === "string-2" || style === "comment") {
  //         return style;
  //       }
  //       var current = stream.current();

  //       if(state.inFunction) {
  //         if(state.inSquareBlock) console.error("inSquareBlock while also inFunction");
          
  //         if(stream.indentation() <= state.functionIndent) {
  //           state.localState = null;
  //           state.inFunction = false;
  //           stream.backUp(stream.current().length); // Rewind to the beginning of the line (important)
  //           return null;
  //         }
  //       } else if(state.inSquareBlock) {
  //         if(state.inFunction) console.error("inFunction while also inSquareBlock");
    
  //         if (current === "]") { // note: this square bracket is not inside a string/regex, since we early-returned above in those cases
  //           state.bracketDepth--;
  //           if (state.bracketDepth <= 0) {
  //             state.inSquareBlock = false;
  //             state.localState = null;
  //             return "square-bracket";
  //           }
  //         }
    
  //         if(current === "[") {
  //           state.bracketDepth++;
  //         }

  //         if(state.bracketDepth <= 0) {
  //           state.inSquareBlock = false;
  //           state.localState = null;
  //         }
  //       }

  //       return style;
  //     } else {
  //       if(stream.match(/\$output\b/) || stream.match(/\$preprocess\b/)) {
  //         return "special-list-name";
  //       }
  //       if(stream.sol() && stream.match(/\/\/.*/)) {
  //         return "comment";
  //       } else if (stream.match(/\s\/\/.*/)) {
  //         return "comment";
  //       }
  //       if(stream.peek() === "[") {
  //         state.bracketDepth = 1;
  //         state.inSquareBlock = true;
  //         state.localState = CodeMirror.startState(jsMode);
  //         stream.next();
  //         return "square-bracket";
  //       }

  //       if(stream.match(/^[a-zA-Z0-9$_]\w*(?=\()/)) {
  //         return "def";
  //       }

  //       if(stream.match(/\([^\)]*\) =>/)) {
  //         state.localState = CodeMirror.startState(jsMode);
  //         state.inFunction = true;
  //         state.functionIndent = stream.indentation();
  //         return "javascript";
  //       }

  //       if(stream.match('\\[') || stream.match('\\]')) {
  //         return "escaped-char";
  //       }
  //       if(stream.match('{') || stream.match('}')) {
  //         return "curly-bracket";
  //       }
  //       stream.next();
  //       return null;
  //     }
  //   }

  //   return {
  //     startState: function() {
  //       return {
  //         inSquareBlock: false,
  //         localState: null,
  //         bracketDepth: 0,
  //         inFunction: false,
  //         functionIndent: 0,
  //       };
  //     },
  //     token: token,
  //     lineComment: "//",
  //     fold: "indent",
  //   };
  // });

} catch(e) { console.error(e); }
</script>

        <!-- LIBS -->
        <script>
'use strict';(function(){var global=this,isIE8=global.attachEvent&&!global[addEventListener],document=global.document,addEventListener='addEventListener',removeEventListener='removeEventListener',getBoundingClientRect='getBoundingClientRect',FLOAT_FUDGING=0.5,calc=(function(){var el,prefixes=["","-webkit-","-moz-","-o-"]
for(var i=0;i<prefixes.length;i++){el=document.createElement('div')
el.style.cssText="width:"+prefixes[i]+"calc(9px)"
if(el.style.length){return prefixes[i]+"calc"}}})(),elementOrSelector=function(el){if(typeof el==='string'||el instanceof String){return document.querySelector(el)}else{return el}},Split=function(ids,options){var dimension,i,clientDimension,clientAxis,position,gutterClass,paddingA,paddingB,pairs=[]
options=typeof options!=='undefined'?options:{}
if(typeof options.gutterSize==='undefined')options.gutterSize=10
if(typeof options.minSize==='undefined')options.minSize=100
if(typeof options.snapOffset==='undefined')options.snapOffset=30
if(typeof options.direction==='undefined')options.direction='horizontal'
if(typeof options.elementStyle==='undefined')options.elementStyle=function(dimension,size,gutterSize){var style={}
if(typeof size!=='string'&&!(size instanceof String)){if(!isIE8){style[dimension]=calc+'('+size+'% - '+gutterSize+'px)'}else{style[dimension]=size+'%'}}else{style[dimension]=size}
return style}
if(typeof options.gutterStyle==='undefined')options.gutterStyle=function(dimension,gutterSize){var style={}
style[dimension]=gutterSize+'px'
return style}
if(options.direction=='horizontal'){dimension='width'
clientDimension='clientWidth'
clientAxis='clientX'
position='left'
gutterClass='gutter gutter-horizontal'
paddingA='paddingLeft'
paddingB='paddingRight'
if(!options.cursor)options.cursor='ew-resize'}else if(options.direction=='vertical'){dimension='height'
clientDimension='clientHeight'
clientAxis='clientY'
position='top'
gutterClass='gutter gutter-vertical'
paddingA='paddingTop'
paddingB='paddingBottom'
if(!options.cursor)options.cursor='ns-resize'}
var startDragging=function(e){var self=this,a=self.a,b=self.b
if(!self.dragging&&options.onDragStart){options.onDragStart()}
e.preventDefault()
self.dragging=true
self.move=drag.bind(self)
self.stop=stopDragging.bind(self)
global[addEventListener]('mouseup',self.stop)
global[addEventListener]('touchend',self.stop)
global[addEventListener]('touchcancel',self.stop)
self.parent[addEventListener]('mousemove',self.move)
self.parent[addEventListener]('touchmove',self.move)
a[addEventListener]('selectstart',noop)
a[addEventListener]('dragstart',noop)
b[addEventListener]('selectstart',noop)
b[addEventListener]('dragstart',noop)
a.style.userSelect='none'
a.style.webkitUserSelect='none'
a.style.MozUserSelect='none'
a.style.pointerEvents='none'
b.style.userSelect='none'
b.style.webkitUserSelect='none'
b.style.MozUserSelect='none'
b.style.pointerEvents='none'
self.gutter.style.cursor=options.cursor
self.parent.style.cursor=options.cursor
calculateSizes.call(self)},stopDragging=function(){var self=this,a=self.a,b=self.b
if(self.dragging&&options.onDragEnd){options.onDragEnd()}
self.dragging=false
global[removeEventListener]('mouseup',self.stop)
global[removeEventListener]('touchend',self.stop)
global[removeEventListener]('touchcancel',self.stop)
self.parent[removeEventListener]('mousemove',self.move)
self.parent[removeEventListener]('touchmove',self.move)
delete self.stop
delete self.move
a[removeEventListener]('selectstart',noop)
a[removeEventListener]('dragstart',noop)
b[removeEventListener]('selectstart',noop)
b[removeEventListener]('dragstart',noop)
a.style.userSelect=''
a.style.webkitUserSelect=''
a.style.MozUserSelect=''
a.style.pointerEvents=''
b.style.userSelect=''
b.style.webkitUserSelect=''
b.style.MozUserSelect=''
b.style.pointerEvents=''
self.gutter.style.cursor=''
self.parent.style.cursor=''},drag=function(e){var offset
if(!this.dragging)return
if('touches'in e){offset=e.touches[0][clientAxis]-this.start}else{offset=e[clientAxis]-this.start}
if(offset<=this.aMin+options.snapOffset+this.aGutterSize){offset=this.aMin+this.aGutterSize}else if(offset>=this.size-(this.bMin+options.snapOffset+this.bGutterSize)){offset=this.size-(this.bMin+this.bGutterSize)}
offset=offset-FLOAT_FUDGING
adjust.call(this,offset)
if(options.onDrag){options.onDrag()}},calculateSizes=function(){var computedStyle=global.getComputedStyle(this.parent),parentSize=this.parent[clientDimension]-parseFloat(computedStyle[paddingA])-parseFloat(computedStyle[paddingB])
this.size=this.a[getBoundingClientRect]()[dimension]+this.b[getBoundingClientRect]()[dimension]+this.aGutterSize+this.bGutterSize
this.percentage=Math.min(this.size/parentSize*100,100)
this.start=this.a[getBoundingClientRect]()[position]},adjust=function(offset){setElementSize(this.a,(offset/this.size*this.percentage),this.aGutterSize)
setElementSize(this.b,(this.percentage-(offset/this.size*this.percentage)),this.bGutterSize)},setElementSize=function(el,size,gutterSize){var style=options.elementStyle(dimension,size,gutterSize),props=Object.keys(style)
for(var i=0;i<props.length;i++){el.style[props[i]]=style[props[i]]}},setGutterSize=function(gutter,gutterSize){var style=options.gutterStyle(dimension,gutterSize),props=Object.keys(style)
for(var i=0;i<props.length;i++){gutter.style[props[i]]=style[props[i]]}},noop=function(){return false},parent=elementOrSelector(ids[0]).parentNode
if(!options.sizes){var percent=100/ids.length
options.sizes=[]
for(i=0;i<ids.length;i++){options.sizes.push(percent)}}
if(!Array.isArray(options.minSize)){var minSizes=[]
for(i=0;i<ids.length;i++){minSizes.push(options.minSize)}
options.minSize=minSizes}
for(i=0;i<ids.length;i++){var el=elementOrSelector(ids[i]),isFirstPair=(i==1),isLastPair=(i==ids.length-1),size=options.sizes[i],gutterSize=options.gutterSize,pair,parentFlexDirection=window.getComputedStyle(parent).flexDirection,temp
if(i>0){pair={a:elementOrSelector(ids[i-1]),b:el,aMin:options.minSize[i-1],bMin:options.minSize[i],dragging:false,parent:parent,isFirst:isFirstPair,isLast:isLastPair,direction:options.direction}
pair.aGutterSize=options.gutterSize
pair.bGutterSize=options.gutterSize
if(isFirstPair){pair.aGutterSize=options.gutterSize/2}
if(isLastPair){pair.bGutterSize=options.gutterSize/2}
if(parentFlexDirection==='row-reverse'||parentFlexDirection==='column-reverse'){temp=pair.a;pair.a=pair.b;pair.b=temp;}}
if(!isIE8){if(i>0){var gutter=document.createElement('div')
gutter.className=gutterClass
setGutterSize(gutter,gutterSize)
gutter[addEventListener]('mousedown',startDragging.bind(pair))
gutter[addEventListener]('touchstart',startDragging.bind(pair))
parent.insertBefore(gutter,el)
pair.gutter=gutter}
if(i===0||i==ids.length-1){gutterSize=options.gutterSize/2}}
setElementSize(el,size,gutterSize)
if(i>0){var aSize=pair.a[getBoundingClientRect]()[dimension],bSize=pair.b[getBoundingClientRect]()[dimension]
if(aSize<pair.aMin){pair.aMin=aSize}
if(bSize<pair.bMin){pair.bMin=bSize}}
if(i>0){pairs.push(pair)}}
return{setSizes:function(sizes){for(var i=0;i<sizes.length;i++){if(i>0){var pair=pairs[i-1]
setElementSize(pair.a,sizes[i-1],pair.aGutterSize)
setElementSize(pair.b,sizes[i],pair.bGutterSize)}}},getSizes:function(){var sizes=[]
for(var i=0;i<pairs.length;i++){var pair=pairs[i],computedStyle=global.getComputedStyle(pair.parent),parentSize=pair.parent[clientDimension]-parseFloat(computedStyle[paddingA])-parseFloat(computedStyle[paddingB])
sizes.push((pair.a[getBoundingClientRect]()[dimension]+pair.aGutterSize)/parentSize*100)
if(i===pairs.length-1){sizes.push((pair.b[getBoundingClientRect]()[dimension]+pair.bGutterSize)/parentSize*100)}}
return sizes},collapse:function(i){var pair
if(i===pairs.length){pair=pairs[i-1]
calculateSizes.call(pair)
adjust.call(pair,pair.size-pair.bGutterSize)}else{pair=pairs[i]
calculateSizes.call(pair)
adjust.call(pair,pair.aGutterSize)}},destroy:function(){for(var i=0;i<pairs.length;i++){pairs[i].parent.removeChild(pairs[i].gutter)
pairs[i].a.style[dimension]=''
pairs[i].b.style[dimension]=''}}}}
if(typeof exports!=='undefined'){if(typeof module!=='undefined'&&module.exports){exports=module.exports=Split}
exports.Split=Split}else{global.Split=Split}}).call(window);
</script>
        <script>
function Store(name) {
  this.name = name;
  try {
    this.data = JSON.parse( localStorage.getItem(this.name+"-storage") ) || {};
  } catch(e) {
    console.error("Couldn't parse local storage data:", e);
    this.data = {};
  }
  //this.data = JSON.parse( LZString.decompress(localStorage.getItem(this.name+"-storage")) ) || {};

  this.save = function(key, value) {
    try {
      localStorage.setItem(this.name+"-storage", JSON.stringify(this.data) );
    } catch(e) {
      console.error("Failed to save data to local storage:", e);
    }
    //localStorage.setItem(this.name+"-storage", LZString.compress(JSON.stringify(this.data)) );
  }
  return this;
}

</script>
        <script>
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.DOMPurify=t()}(this,function(){"use strict";function e(e,t){for(var n=t.length;n--;)"string"==typeof t[n]&&(t[n]=t[n].toLowerCase()),e[t[n]]=!0;return e}function t(e){var t={},n=void 0;for(n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t}function n(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t<e.length;t++)n[t]=e[t];return n}return Array.from(e)}function o(){var x=arguments.length>0&&void 0!==arguments[0]?arguments[0]:A(),S=function(e){return o(e)};if(S.version="1.0.2",S.removed=[],!x||!x.document||9!==x.document.nodeType)return S.isSupported=!1,S;var k=x.document,E=!1,w=!1,O=x.document,L=x.DocumentFragment,M=x.HTMLTemplateElement,N=x.Node,_=x.NodeFilter,D=x.NamedNodeMap,R=void 0===D?x.NamedNodeMap||x.MozNamedAttrMap:D,C=x.Text,F=x.Comment,z=x.DOMParser,H=x.XMLHttpRequest,I=void 0===H?x.XMLHttpRequest:H,j=x.encodeURI,U=void 0===j?x.encodeURI:j;if("function"==typeof M){var W=O.createElement("template");W.content&&W.content.ownerDocument&&(O=W.content.ownerDocument)}var q=O,G=q.implementation,P=q.createNodeIterator,B=q.getElementsByTagName,X=q.createDocumentFragment,V=k.importNode,Y={};S.isSupported=G&&void 0!==G.createHTMLDocument&&9!==O.documentMode;var K=p,$=f,J=h,Q=g,Z=v,ee=b,te=y,ne=null,oe=e({},[].concat(n(r),n(i),n(a),n(l),n(s))),re=null,ie=e({},[].concat(n(c),n(d),n(u),n(m))),ae=null,le=null,se=!0,ce=!0,de=!1,ue=!1,me=!1,pe=!1,fe=!1,he=!1,ge=!1,ye=!1,ve=!1,be=!0,Te=!0,Ae={},xe=e({},["audio","head","math","script","style","template","svg","video"]),Se=e({},["audio","video","img","source","image"]),ke=e({},["alt","class","for","id","label","name","pattern","placeholder","summary","title","value","style","xmlns"]),Ee=null,we=O.createElement("form"),Oe=function(o){"object"!==(void 0===o?"undefined":T(o))&&(o={}),ne="ALLOWED_TAGS"in o?e({},o.ALLOWED_TAGS):oe,re="ALLOWED_ATTR"in o?e({},o.ALLOWED_ATTR):ie,ae="FORBID_TAGS"in o?e({},o.FORBID_TAGS):{},le="FORBID_ATTR"in o?e({},o.FORBID_ATTR):{},Ae="USE_PROFILES"in o&&o.USE_PROFILES,se=!1!==o.ALLOW_ARIA_ATTR,ce=!1!==o.ALLOW_DATA_ATTR,de=o.ALLOW_UNKNOWN_PROTOCOLS||!1,ue=o.SAFE_FOR_JQUERY||!1,me=o.SAFE_FOR_TEMPLATES||!1,pe=o.WHOLE_DOCUMENT||!1,ge=o.RETURN_DOM||!1,ye=o.RETURN_DOM_FRAGMENT||!1,ve=o.RETURN_DOM_IMPORT||!1,he=o.FORCE_BODY||!1,be=!1!==o.SANITIZE_DOM,Te=!1!==o.KEEP_CONTENT,te=o.ALLOWED_URI_REGEXP||te,me&&(ce=!1),ye&&(ge=!0),Ae&&(ne=e({},[].concat(n(s))),re=[],!0===Ae.html&&(e(ne,r),e(re,c)),!0===Ae.svg&&(e(ne,i),e(re,d),e(re,m)),!0===Ae.svgFilters&&(e(ne,a),e(re,d),e(re,m)),!0===Ae.mathMl&&(e(ne,l),e(re,u),e(re,m))),o.ADD_TAGS&&(ne===oe&&(ne=t(ne)),e(ne,o.ADD_TAGS)),o.ADD_ATTR&&(re===ie&&(re=t(re)),e(re,o.ADD_ATTR)),o.ADD_URI_SAFE_ATTR&&e(ke,o.ADD_URI_SAFE_ATTR),Te&&(ne["#text"]=!0),Object&&"freeze"in Object&&Object.freeze(o),Ee=o},Le=function(e){S.removed.push({element:e});try{e.parentNode.removeChild(e)}catch(t){e.outerHTML=""}},Me=function(e,t){S.removed.push({attribute:t.getAttributeNode(e),from:t}),t.removeAttribute(e)},Ne=function(e){var t=void 0,n=void 0;if(he&&(e="<remove></remove>"+e),w){try{e=U(e)}catch(e){}var o=new I;o.responseType="document",o.open("GET","data:text/html;charset=utf-8,"+e,!1),o.send(null),t=o.response}if(E)try{t=(new z).parseFromString(e,"text/html")}catch(e){}return t&&t.documentElement||((n=(t=G.createHTMLDocument("")).body).parentNode.removeChild(n.parentNode.firstElementChild),n.outerHTML=e),B.call(t,pe?"html":"body")[0]};S.isSupported&&function(){var e=Ne('<svg><g onload="this.parentNode.remove()"></g></svg>');e.querySelector("svg")||(w=!0);try{(e=Ne('<svg><p><style><img src="</style><img src=x onerror=alert(1)//">')).querySelector("svg img")&&(E=!0)}catch(e){}}();var _e=function(e){return P.call(e.ownerDocument||e,e,_.SHOW_ELEMENT|_.SHOW_COMMENT|_.SHOW_TEXT,function(){return _.FILTER_ACCEPT},!1)},De=function(e){return!(e instanceof C||e instanceof F)&&!("string"==typeof e.nodeName&&"string"==typeof e.textContent&&"function"==typeof e.removeChild&&e.attributes instanceof R&&"function"==typeof e.removeAttribute&&"function"==typeof e.setAttribute)},Re=function(e){return"object"===(void 0===N?"undefined":T(N))?e instanceof N:e&&"object"===(void 0===e?"undefined":T(e))&&"number"==typeof e.nodeType&&"string"==typeof e.nodeName},Ce=function(e,t,n){Y[e]&&Y[e].forEach(function(e){e.call(S,t,n,Ee)})},Fe=function(e){var t=void 0;if(Ce("beforeSanitizeElements",e,null),De(e))return Le(e),!0;var n=e.nodeName.toLowerCase();if(Ce("uponSanitizeElement",e,{tagName:n,allowedTags:ne}),!ne[n]||ae[n]){if(Te&&!xe[n]&&"function"==typeof e.insertAdjacentHTML)try{e.insertAdjacentHTML("AfterEnd",e.innerHTML)}catch(e){}return Le(e),!0}return!ue||e.firstElementChild||e.content&&e.content.firstElementChild||!/</g.test(e.textContent)||(S.removed.push({element:e.cloneNode()}),e.innerHTML=e.textContent.replace(/</g,"&lt;")),me&&3===e.nodeType&&(t=(t=(t=e.textContent).replace(K," ")).replace($," "),e.textContent!==t&&(S.removed.push({element:e.cloneNode()}),e.textContent=t)),Ce("afterSanitizeElements",e,null),!1},ze=function(e){var t=void 0,n=void 0,o=void 0,r=void 0,i=void 0,a=void 0,l=void 0;if(Ce("beforeSanitizeAttributes",e,null),a=e.attributes){var s={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:re};for(l=a.length;l--;){if(t=a[l],n=t.name,o=t.value.trim(),r=n.toLowerCase(),s.attrName=r,s.attrValue=o,s.keepAttr=!0,Ce("uponSanitizeAttribute",e,s),o=s.attrValue,"name"===r&&"IMG"===e.nodeName&&a.id)i=a.id,a=Array.prototype.slice.apply(a),Me("id",e),Me(n,e),a.indexOf(i)>l&&e.setAttribute("id",i.value);else{if("INPUT"===e.nodeName&&"type"===r&&"file"===o&&(re[r]||!le[r]))continue;"id"===n&&e.setAttribute(n,""),Me(n,e)}if(s.keepAttr&&(!be||"id"!==r&&"name"!==r||!(o in x||o in O||o in we))){if(me&&(o=(o=o.replace(K," ")).replace($," ")),ce&&J.test(r));else if(se&&Q.test(r));else{if(!re[r]||le[r])continue;if(ke[r]);else if(te.test(o.replace(ee,"")));else if("src"!==r&&"xlink:href"!==r||0!==o.indexOf("data:")||!Se[e.nodeName.toLowerCase()]){if(de&&!Z.test(o.replace(ee,"")));else if(o)continue}else;}try{e.setAttribute(n,o),S.removed.pop()}catch(e){}}}Ce("afterSanitizeAttributes",e,null)}},He=function e(t){var n=void 0,o=_e(t);for(Ce("beforeSanitizeShadowDOM",t,null);n=o.nextNode();)Ce("uponSanitizeShadowNode",n,null),Fe(n)||(n.content instanceof L&&e(n.content),ze(n));Ce("afterSanitizeShadowDOM",t,null)};return S.sanitize=function(e,t){var n=void 0,o=void 0,r=void 0,i=void 0,a=void 0;if(e||(e="\x3c!--\x3e"),"string"!=typeof e&&!Re(e)){if("function"!=typeof e.toString)throw new TypeError("toString is not a function");e=e.toString()}if(!S.isSupported){if("object"===T(x.toStaticHTML)||"function"==typeof x.toStaticHTML){if("string"==typeof e)return x.toStaticHTML(e);if(Re(e))return x.toStaticHTML(e.outerHTML)}return e}if(fe||Oe(t),S.removed=[],e instanceof N)1===(o=(n=Ne("\x3c!--\x3e")).ownerDocument.importNode(e,!0)).nodeType&&"BODY"===o.nodeName?n=o:n.appendChild(o);else{if(!ge&&!pe&&-1===e.indexOf("<"))return e;if(!(n=Ne(e)))return ge?null:""}he&&Le(n.firstChild);for(var l=_e(n);r=l.nextNode();)3===r.nodeType&&r===i||Fe(r)||(r.content instanceof L&&He(r.content),ze(r),i=r);if(ge){if(ye)for(a=X.call(n.ownerDocument);n.firstChild;)a.appendChild(n.firstChild);else a=n;return ve&&(a=V.call(k,a,!0)),a}return pe?n.outerHTML:n.innerHTML},S.setConfig=function(e){Oe(e),fe=!0},S.clearConfig=function(){Ee=null,fe=!1},S.addHook=function(e,t){"function"==typeof t&&(Y[e]=Y[e]||[],Y[e].push(t))},S.removeHook=function(e){Y[e]&&Y[e].pop()},S.removeHooks=function(e){Y[e]&&(Y[e]=[])},S.removeAllHooks=function(){Y={}},S}var r=["a","abbr","acronym","address","area","article","aside","audio","b","bdi","bdo","big","blink","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","content","data","datalist","dd","decorator","del","details","dfn","dir","div","dl","dt","element","em","fieldset","figcaption","figure","font","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","img","input","ins","kbd","label","legend","li","main","map","mark","marquee","menu","menuitem","meter","nav","nobr","ol","optgroup","option","output","p","pre","progress","q","rp","rt","ruby","s","samp","section","select","shadow","small","source","spacer","span","strike","strong","style","sub","summary","sup","table","tbody","td","template","textarea","tfoot","th","thead","time","tr","track","tt","u","ul","var","video","wbr"],i=["svg","a","altglyph","altglyphdef","altglyphitem","animatecolor","animatemotion","animatetransform","audio","canvas","circle","clippath","defs","desc","ellipse","filter","font","g","glyph","glyphref","hkern","image","line","lineargradient","marker","mask","metadata","mpath","path","pattern","polygon","polyline","radialgradient","rect","stop","style","switch","symbol","text","textpath","title","tref","tspan","video","view","vkern"],a=["feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feMerge","feMergeNode","feMorphology","feOffset","feSpecularLighting","feTile","feTurbulence"],l=["math","menclose","merror","mfenced","mfrac","mglyph","mi","mlabeledtr","mmuliscripts","mn","mo","mover","mpadded","mphantom","mroot","mrow","ms","mpspace","msqrt","mystyle","msub","msup","msubsup","mtable","mtd","mtext","mtr","munder","munderover"],s=["#text"],c=["accept","action","align","alt","autocomplete","background","bgcolor","border","cellpadding","cellspacing","checked","cite","class","clear","color","cols","colspan","coords","datetime","default","dir","disabled","download","enctype","face","for","headers","height","hidden","high","href","hreflang","id","ismap","label","lang","list","loop","low","max","maxlength","media","method","min","multiple","name","noshade","novalidate","nowrap","open","optimum","pattern","placeholder","poster","preload","pubdate","radiogroup","readonly","rel","required","rev","reversed","role","rows","rowspan","spellcheck","scope","selected","shape","size","sizes","span","srclang","start","src","srcset","step","style","summary","tabindex","title","type","usemap","valign","value","width","xmlns"],d=["accent-height","accumulate","additivive","alignment-baseline","ascent","attributename","attributetype","azimuth","basefrequency","baseline-shift","begin","bias","by","class","clip","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","cx","cy","d","dx","dy","diffuseconstant","direction","display","divisor","dur","edgemode","elevation","end","fill","fill-opacity","fill-rule","filter","flood-color","flood-opacity","font-family","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-weight","fx","fy","g1","g2","glyph-name","glyphref","gradientunits","gradienttransform","height","href","id","image-rendering","in","in2","k","k1","k2","k3","k4","kerning","keypoints","keysplines","keytimes","lang","lengthadjust","letter-spacing","kernelmatrix","kernelunitlength","lighting-color","local","marker-end","marker-mid","marker-start","markerheight","markerunits","markerwidth","maskcontentunits","maskunits","max","mask","media","method","mode","min","name","numoctaves","offset","operator","opacity","order","orient","orientation","origin","overflow","paint-order","path","pathlength","patterncontentunits","patterntransform","patternunits","points","preservealpha","r","rx","ry","radius","refx","refy","repeatcount","repeatdur","restart","result","rotate","scale","seed","shape-rendering","specularconstant","specularexponent","spreadmethod","stddeviation","stitchtiles","stop-color","stop-opacity","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke","stroke-width","style","surfacescale","tabindex","targetx","targety","transform","text-anchor","text-decoration","text-rendering","textlength","type","u1","u2","unicode","values","viewbox","visibility","vert-adv-y","vert-origin-x","vert-origin-y","width","word-spacing","wrap","writing-mode","xchannelselector","ychannelselector","x","x1","x2","xmlns","y","y1","y2","z","zoomandpan"],u=["accent","accentunder","align","bevelled","close","columnsalign","columnlines","columnspan","denomalign","depth","dir","display","displaystyle","fence","frame","height","href","id","largeop","length","linethickness","lspace","lquote","mathbackground","mathcolor","mathsize","mathvariant","maxsize","minsize","movablelimits","notation","numalign","open","rowalign","rowlines","rowspacing","rowspan","rspace","rquote","scriptlevel","scriptminsize","scriptsizemultiplier","selection","separator","separators","stretchy","subscriptshift","supscriptshift","symmetric","voffset","width","xmlns"],m=["xlink:href","xml:id","xlink:title","xml:space","xmlns:xlink"],p=/\{\{[\s\S]*|[\s\S]*\}\}/gm,f=/<%[\s\S]*|[\s\S]*%>/gm,h=/^data-[\-\w.\u00B7-\uFFFF]/,g=/^aria-[\-\w]+$/,y=/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i,v=/^(?:\w+script|data):/i,b=/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205f\u3000]/g,T="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},A=function(){return"undefined"==typeof window?null:window};return o()});
//# sourceMappingURL=purify.min.js.map

</script>


        <!-- CSS LOADING SPINNER -->
        <style>
          .ldspmzjfhueifssge-ring {
            display: inline-block;
            position: relative;
            width: 64px;
            height: 64px;
          }
          .ldspmzjfhueifssge-ring div {
            box-sizing: border-box;
            display: block;
            position: absolute;
            width: 51px;
            height: 51px;
            margin: 6px;
            border: 6px solid #444;
            border-radius: 50%;
            animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
            border-color: #444 transparent transparent transparent;
          }
          .ldspmzjfhueifssge-ring div:nth-child(1) {
            animation-delay: -0.45s;
          }
          .ldspmzjfhueifssge-ring div:nth-child(2) {
            animation-delay: -0.3s;
          }
          .ldspmzjfhueifssge-ring div:nth-child(3) {
            animation-delay: -0.15s;
          }
          @keyframes lds-ring {
            0% {
              transform: rotate(0deg);
            }
            100% {
              transform: rotate(360deg);
            }
          }
          @media (prefers-color-scheme: dark) {
            #outputLoadSpinner {
              filter: invert(1);
            }
          }
        </style>

        <script>
          let thisIsNotAnOldCachedPageResolver;
          window.thisIsNotAnOldCachedPagePromise = new Promise(r => thisIsNotAnOldCachedPageResolver=r);
          // CloudFlare sometimes has cache purging delays, and these can really mess things up (e.g. make it look like you've lost hours of work).
          // We check the `age` header of this document, and if it's older than the last save time of this generator, then we load a cache-busted URL
          // and then remove the added cacheBust string with history.replaceState
          (async function() {
            if(navigator.webdriver) return; // just in case this is what's confusing the Google crawler - RE the title bug
            if(window.location.href.includes("__cacheBust")) {
              thisIsNotAnOldCachedPageResolver(true);
              return; // no need to run this check if cache is already busted
            }

            let imports = window.js0nparse(decodeURI(document.querySelector("#imported-generator-names").textContent));
            let clientHtmlServerRenderTime = Number(document.querySelector("#this-html-server-render-time").textContent);
            
            await new Promise(r => setTimeout(r, 10)); // give the page a chance to load a bit before checking transferSize
            let transferSize = 1;
            try { transferSize = performance.getEntriesByType("navigation")[0].transferSize; } catch(e) {}
            console.debug("transferSize (likely partial):", transferSize);
            if(isNaN(transferSize)) transferSize = 1;
            // transferSize will be zero if it was loaded from browser cache, assuming the user's browser supports the API

            let checkStartTime = Date.now();
            let weHaveAnOldVersionOfThisGen = await fetch(`https://perchance.org/api/clearCacheIfGeneratorOrImportsHaveBeenUpdated?generatorName=${window.location.pathname.slice(1)}&importedGeneratorNames=${imports.join(",")}&clientHtmlServerRenderTime=${clientHtmlServerRenderTime}&transferSize=${transferSize}&queryParamString=${encodeURIComponent(window.location.search)}&__cacheBust=${Math.random()}`).then(r => r.json()).catch(e => { console.error(e); return "network-error"; });
            if(weHaveAnOldVersionOfThisGen === "network-error") {
              if(app.userOwnsThisGenerator) {
                alert("There was a network error while trying to determine whether the latest generator data has been loaded. You may be viewing/editing an older copy of the data. Try refreshing the page.");
              }
            } else if(weHaveAnOldVersionOfThisGen) {
              console.debug("This is an old version of this generator.");
              if(Date.now()-checkStartTime < 10000) {
                console.debug("Doing cache bust reload now...");
                let url = new URL(window.location.href);
                url.searchParams.set("__cacheBust", Math.random());
                window.location.href = url.href;  
              } else if(app.userOwnsThisGenerator) { 
                // don't want to disrupt them in case they're editing, but still need to let them know:
                alert("This is an old version of this generator. Please refresh the page to get the latest version.");
              }
            } else {
              thisIsNotAnOldCachedPageResolver(true);
            }
          })();
          if(location.href.includes("__cacheBust")) {
            window.needToBustCacheOfIframeOnInitialLoad = true;
            let url = new URL(window.location.href);
            url.searchParams.delete("__cacheBust");
            let newPath = url.href.replace(/^https:\/\/perchance\.org/, "");
            history.replaceState({}, "", newPath);
          }
        </script>


        <style>
/* NOTE: Changes marked with "CHANGE" */

/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */

/**
 * 1. Set default font family to sans-serif.
 * 2. Prevent iOS and IE text size adjust after device orientation change,
 *    without disabling user zoom.
 */

html {
  font-family: sans-serif; /* 1 */
  -ms-text-size-adjust: 100%; /* 2 */
  -webkit-text-size-adjust: 100%; /* 2 */
}

/**
 * Remove default margin.
 */

body {
  margin: 0;
}

/* HTML5 display definitions
   ========================================================================== */

/**
 * Correct `block` display not defined for any HTML5 element in IE 8/9.
 * Correct `block` display not defined for `details` or `summary` in IE 10/11
 * and Firefox.
 * Correct `block` display not defined for `main` in IE 11.
 */

article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
/* summary, CHANGE */
section {
  display: block;
}

/**
 * 1. Correct `inline-block` display not defined in IE 8/9.
 * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
 */

audio,
canvas,
progress,
video {
  display: inline-block; /* 1 */
  vertical-align: baseline; /* 2 */
}

/**
 * Prevent modern browsers from displaying `audio` without controls.
 * Remove excess height in iOS 5 devices.
 */

audio:not([controls]) {
  display: none;
  height: 0;
}

/**
 * Address `[hidden]` styling not present in IE 8/9/10.
 * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.
 */

[hidden] {
  display: none !important; /* EDIT: added `!important` so you can e.g. have inline display:flex, and yet still toggle hidden/unhidden with `el.hidden = trueOrFalse` */
}
template {
  display: none;
}

/* Links
   ========================================================================== */

/**
 * Remove the gray background color from active links in IE 10.
 */

a {
  background-color: transparent;
}

/**
 * Improve readability of focused elements when they are also in an
 * active/hover state.
 */

a:active,
a:hover {
  outline: 0;
}

/* Text-level semantics
   ========================================================================== */

/**
 * Address styling not present in IE 8/9/10/11, Safari, and Chrome.
 */

abbr[title] {
  border-bottom: 1px dotted;
}

/**
 * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
 */

b,
strong {
  font-weight: bold;
}

/**
 * Address styling not present in Safari and Chrome.
 */

dfn {
  font-style: italic;
}

/**
 * Address variable `h1` font-size and margin within `section` and `article`
 * contexts in Firefox 4+, Safari, and Chrome.
 */

h1 {
  font-size: 2em;
  margin: 0.67em 0;
}

/**
 * Address styling not present in IE 8/9.
 */

mark {
  background: #ff0;
  color: #000;
}

/**
 * Address inconsistent and variable font size in all browsers.
 */

small {
  font-size: 80%;
}

/**
 * Prevent `sub` and `sup` affecting `line-height` in all browsers.
 */

sub,
sup {
  font-size: 75%;
  line-height: 0;
  position: relative;
  vertical-align: baseline;
}

sup {
  top: -0.5em;
}

sub {
  bottom: -0.25em;
}

/* Embedded content
   ========================================================================== */

/**
 * Remove border when inside `a` element in IE 8/9/10.
 */

img {
  border: 0;
}

/**
 * Correct overflow not hidden in IE 9/10/11.
 */

svg:not(:root) {
  overflow: hidden;
}

/* Grouping content
   ========================================================================== */

/**
 * Address margin not present in IE 8/9 and Safari.
 */

figure {
  margin: 1em 40px;
}

/**
 * Address differences between Firefox and other browsers.
 */

hr {
  box-sizing: content-box;
  height: 0;
}

/**
 * Contain overflow in all browsers.
 */

pre {
  overflow: auto;
}

/**
 * Address odd `em`-unit font size rendering in all browsers.
 */

code,
kbd,
pre,
samp {
  font-family: monospace, monospace;
  font-size: 1em;
}

/* Forms
   ========================================================================== */

/**
 * Known limitation: by default, Chrome and Safari on OS X allow very limited
 * styling of `select`, unless a `border` property is set.
 */

/**
 * 1. Correct color not being inherited.
 * 2. Correct font properties not being inherited.
 * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
 */

button:not([disabled]), input:not([disabled]), optgroup:not([disabled]), select:not([disabled]), textarea:not([disabled]) {
  color: inherit;
}



button,
input,
optgroup,
select,
textarea {
  font: inherit; /* 2 */
  margin: 0; /* 3 */
}

/**
 * Address `overflow` set to `hidden` in IE 8/9/10/11.
 */

button {
  overflow: visible;
}

/**
 * Address inconsistent `text-transform` inheritance for `button` and `select`.
 * All other form control elements do not inherit `text-transform` values.
 * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
 * Correct `select` style inheritance in Firefox.
 */

button,
select {
  text-transform: none;
}

/**
 * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
 *    and `video` controls.
 * 2. Correct inability to style clickable `input` types in iOS.
 * 3. Improve usability and consistency of cursor style between image-type
 *    `input` and others.
 */

button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
  -webkit-appearance: button; /* 2 */
  cursor: pointer; /* 3 */
}

/**
 * Re-set default cursor for disabled elements.
 */

button[disabled],
html input[disabled] {
  cursor: default;
}

/**
 * Remove inner padding and border in Firefox 4+.
 */

button::-moz-focus-inner,
input::-moz-focus-inner {
  border: 0;
  padding: 0;
}

/**
 * Address Firefox 4+ setting `line-height` on `input` using `!important` in
 * the UA stylesheet.
 */

input {
  line-height: normal;
}

/**
 * It's recommended that you don't attempt to style these elements.
 * Firefox's implementation doesn't respect box-sizing, padding, or width.
 *
 * 1. Address box sizing set to `content-box` in IE 8/9/10.
 * 2. Remove excess padding in IE 8/9/10.
 */

input[type="checkbox"],
input[type="radio"] {
  box-sizing: border-box; /* 1 */
  padding: 0; /* 2 */
}

/**
 * Fix the cursor style for Chrome's increment/decrement buttons. For certain
 * `font-size` values of the `input`, it causes the cursor style of the
 * decrement button to change from `default` to `text`.
 */

input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
  height: auto;
}

/**
 * 1. Address `appearance` set to `searchfield` in Safari and Chrome.
 * 2. Address `box-sizing` set to `border-box` in Safari and Chrome.
 */

input[type="search"] {
  -webkit-appearance: textfield; /* 1 */
  box-sizing: content-box; /* 2 */
}

/**
 * Remove inner padding and search cancel button in Safari and Chrome on OS X.
 * Safari (but not Chrome) clips the cancel button when the search input has
 * padding (and `textfield` appearance).
 */

input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
  -webkit-appearance: none;
}

/**
 * Define consistent border, margin, and padding.
 */

fieldset {
  border: 1px solid #c0c0c0;
  margin: 0 2px;
  padding: 0.35em 0.625em 0.75em;
}

/**
 * 1. Correct `color` not being inherited in IE 8/9/10/11.
 * 2. Remove padding so people aren't caught out if they zero out fieldsets.
 */

legend {
  border: 0; /* 1 */
  padding: 0; /* 2 */
}

/**
 * Remove default vertical scrollbar in IE 8/9/10/11.
 */

textarea {
  overflow: auto;
}

/**
 * Don't inherit the `font-weight` (applied by a rule above).
 * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
 */

optgroup {
  font-weight: bold;
}

/* Tables
   ========================================================================== */

/**
 * Remove most spacing between table cells.
 */

table {
  border-collapse: collapse;
  border-spacing: 0;
}

td,
th {
  padding: 0;
}

</style><style>
html { 
    color: #222;
    font-size: 1em;
    line-height: 1.4;
}

/*
 * A better looking default horizontal rule
 */
hr {
    display: block;
    height: 1px;
    border: 0;
    border-top: 1px solid #ccc;
    margin: 1em 0;
    padding: 0;
}

/*
 * Remove the gap between audio, canvas, iframes,
 * images, videos and the bottom of their containers:
 * https://github.com/h5bp/html5-boilerplate/issues/440
 */

audio,
canvas,
iframe,
img,
svg,
video {
    vertical-align: middle;
}

/*
 * Remove default fieldset styles.
 */

fieldset {
    border: 0;
    margin: 0;
    padding: 0;
}

/*
 * Allow only vertical resizing of textareas.
 */

textarea {
    resize: vertical;
}


/* ==========================================================================
   Author's custom styles
   ========================================================================== */


html, body {
  height: 100%;
  overflow: hidden;
}

body {
  background-color: #f6f6f6;
  box-sizing: border-box;
  font-family: 'Cousine', monospace;
  line-height: 1.05rem;
  font-size: 0.75rem;
  color: #050505;
}


/* =================================================== */
/* ================= DARK MODE STUFF ================= */
/* =================================================== */



/* button {
  padding: 0.135em 0.5em;
  border: 1px solid #afafaf;
  background: linear-gradient(to bottom, rgb(245, 245, 245) 0%, rgb(236, 236, 236) 47%, rgb(223, 223, 223) 100%);
  border-radius: 1px;
}
button:hover {
  border-color: #909090;
} */

</style><style type="text/css">@font-face {font-family:Cousine;font-style:normal;font-weight:400;src:url(/cf-fonts/s/cousine/5.0.18/cyrillic/400/normal.woff2);unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116;font-display:swap;}@font-face {font-family:Cousine;font-style:normal;font-weight:400;src:url(/cf-fonts/s/cousine/5.0.18/greek-ext/400/normal.woff2);unicode-range:U+1F00-1FFF;font-display:swap;}@font-face {font-family:Cousine;font-style:normal;font-weight:400;src:url(/cf-fonts/s/cousine/5.0.18/cyrillic-ext/400/normal.woff2);unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F;font-display:swap;}@font-face {font-family:Cousine;font-style:normal;font-weight:400;src:url(/cf-fonts/s/cousine/5.0.18/vietnamese/400/normal.woff2);unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB;font-display:swap;}@font-face {font-family:Cousine;font-style:normal;font-weight:400;src:url(/cf-fonts/s/cousine/5.0.18/greek/400/normal.woff2);unicode-range:U+0370-03FF;font-display:swap;}@font-face {font-family:Cousine;font-style:normal;font-weight:400;src:url(/cf-fonts/s/cousine/5.0.18/latin-ext/400/normal.woff2);unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF;font-display:swap;}@font-face {font-family:Cousine;font-style:normal;font-weight:400;src:url(/cf-fonts/s/cousine/5.0.18/hebrew/400/normal.woff2);unicode-range:U+0590-05FF,U+200C-2010,U+20AA,U+25CC,U+FB1D-FB4F;font-display:swap;}@font-face {font-family:Cousine;font-style:normal;font-weight:400;src:url(/cf-fonts/s/cousine/5.0.18/latin/400/normal.woff2);unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;font-display:swap;}</style>
        <style>
          #menuBarEl { background:#f6f6f6; }
          #editorEl { background:#f6f6f6; }
          @media (prefers-color-scheme: dark) { 
            #menuBarEl { background:#282c33; }
            #editorEl { background:#282c33; }
          }

          #menuBarEl {
            border-bottom: 1px solid #cbcbcb;
          }
          @media (prefers-color-scheme: dark) { 
            #menuBarEl {
              border-bottom: 1px solid #3d3d3d;
            }
          }
          :root {
            --menu-bar-item-backdrop-filter: brightness(0.9);
            --menu-bar-item-backdrop-filter-hover: brightness(0.84);
            --menu-bar-item-backdrop-filter-dark: brightness(0.75);
            --menu-bar-item-backdrop-filter-hover-dark: brightness(0.6);
          }
          #menuBarEl .menu-item {
            backdrop-filter: var(--menu-bar-item-backdrop-filter);
            display: inline-flex;
            border-radius: 3px;
            align-items: center;
            justify-content: center;
            gap: 0.125rem;
            height: 100%;
            font-size: 80%;
            opacity: 0.9;
            min-width: max-content;
          }
          #menuBarEl .menu-item:hover {
            cursor:pointer;
            backdrop-filter: var(--menu-bar-item-backdrop-filter-hover);
            opacity: 1;
          }
          @media (prefers-color-scheme: dark) { 
            #menuBarEl .menu-item {
              backdrop-filter: var(--menu-bar-item-backdrop-filter-dark);
              color:#e3e3e3;
            }
            #menuBarEl .menu-item:hover {
              backdrop-filter: var(--menu-bar-item-backdrop-filter-hover-dark);
            }
          }
          #menuBarEl .menu-item-icon {
            padding-left: 0.25rem;
          }
          #menuBarEl .menu-item-label {
            padding-right: 0.25rem;
          }
          /* @media screen and (max-width: 690px) { #menuBarEl .menu-item.new { display:none; } }
          @media screen and (max-width: 640px) { #menuBarEl .menu-item.community { display:none; } }
          @media screen and (max-width: 590px) { #menuBarEl .menu-item.resources { display:none; } }
          @media screen and (max-width: 540px) { #menuBarEl .menu-item.hub { display:none; } }
          @media screen and (max-width: 490px) { #menuBarEl .menu-item.tutorial { display:none; } }
          @media screen and (max-width: 440px) { #menuBarEl .menu-item.generators { display:none; } } */

          @media screen and (max-width: 400px) {
            #menuBarEl .menu-item.tutorial { display:none; }
            #menuBarEl .menu-item.hub { display:none; }
            #menuBarEl .menu-item.community { display:none; }
          }
        </style>
        <style>
          #editorEl .warningsModal .warning-item {
            padding: 1rem;
            padding-bottom: 0;
          }
          #editorEl .warningsModal code {
            background: #ececec;
            font-size: 90%;
            padding: 0.07rem 0.2rem;
            border-radius: 2px;
          }
          /* WARNINGS MODAL */
          #editorEl .warningsModal .outer-wrapper {
            position:fixed;
            top:0;
            left:0;
            width:100%;
            z-index:2;
            padding: 1em;
            box-sizing: border-box;
          }
          #editorEl .warningsModal .background {
            position:fixed;
            top:0;
            left:0;
            width:100%;
            height:100%;
            opacity:0.2;
            background:#000;
            transition: opacity .15s linear;
          }
          #editorEl .warningsModal .content-wrapper {
            overflow:hidden;
            background:#fff;
            max-width:700px;
            width:100%;
            border-radius:3px;
            margin:0 auto;
          }
          #editorEl .warningsModal button,
          #editorEl .warningsModal input {
            height:3em;
          }
          #editorEl .warningsModal button {
            background:#4c4c4c;
            color:#fff;
            width:50%;
            border:none;
            outline:none;
            font-size: 90%;
          }
          #editorEl .warningsModal button:hover {
            background:#323232;
          }

          #editorEl .warningsModal .modal-header {
            padding:1em;
          }
          #editorEl .warningsModal .modal-header p {
            color:#444;
            margin:0;
            font-size: 90%
          }
          #editorEl .warningsModal button.main {
            background:#444;
            color:white;
          }
          #editorEl .warningsModal button.main:hover {
            background:#333;
          }
          #editorEl .warningsModal span.link {
            cursor:pointer;
            text-decoration:underline;
            color:#1212ff;
          }
          @media screen and (max-width: 590px) { 
            #editorEl #output-buttons-ctn {
              display: flex;
              flex-direction: column;
            }
            #editorEl #output-buttons-ctn > * {
              margin: 3px !important;
              white-space: nowrap;
              overflow: hidden;
            }
          }

          #editorEl #output { border: 1px solid #C0C0C0; border-bottom:none; }
          @media (prefers-color-scheme: dark) { #editorEl #output { border: 1px solid #575757; border-bottom:none; } }

          #editorEl #output-buttons-ctn { border-top: 1px solid #C0C0C0; }
          @media (prefers-color-scheme: dark) { #editorEl #output-buttons-ctn { border-top: 1px solid #575757; } }

          #adCtn { border-top: 1px solid #C0C0C0; }
          @media (prefers-color-scheme: dark) { #adCtn { border-top: 1px solid #575757; } }

          #editorEl .toggle-wrap-button-html {
            border: solid 1px #737373;
            color: #737373;
            padding: 0 0.3em;
            border-radius: 1px;
            cursor: pointer;
            background:white;
            text-align: center;
            position: absolute;
            top: 5px;
            right: 5px;
            z-index: 10;
          }
          #editorEl .toggle-wrap-button-html:hover {
            border: solid 1px black;
            color:black;
          }

          #editorEl .toggle-wrap-button {
            border: solid 1px #737373;
            color: #737373;
            padding: 0 0.3em;
            border-radius: 1px;
            cursor: pointer;
            background:white;
            text-align: center;
            display: inline-block;
          }
          #editorEl .toggle-wrap-button:hover {
            border: solid 1px black;
            color:black;
          }
          #editorEl .toggle-fold-button {
            border: solid 1px #737373;
            color: #737373;
            padding: 0 0.3em;
            border-radius: 1px;
            cursor: pointer;
            background:white;
            text-align: center;
            display: inline-block;
          }
          #editorEl .toggle-fold-button:hover {
            border: solid 1px black;
            color:black;
          }

          #editorEl .code-editor-buttons-ctn {
            position: absolute;
            top: 5px;
            right: 5px;
            z-index: 10;
          }

          #editorEl #output {
            /* position:absolute; */
            height: 100%;
            width: 100%;
            flex-grow:10;
            padding:0;
            margin:0;
            z-index: 20;
            -webkit-box-sizing: border-box;
            -moz-box-sizing: border-box;
            box-sizing: border-box;
            text-align:center;
            border-left: none;
            border-right: none;
          }

          #editorEl #output iframe {
            height:100%;
            width:100%;
            border:none;
          }
          @media (prefers-color-scheme: dark) {
            #editorEl #output iframe {
              background: #121212; /* this is chrome's default dark mode background color. Note: must only do this in dark mode due to this: https://lemmy.world/post/7512043 */
            }
          }

          #editorEl #main {
            
          }

          #editorEl .CodeMirror {
            height: 100%;
            background:#fbfbfb;
          }


          #editorEl .split {
            -webkit-box-sizing: border-box;
            -moz-box-sizing: border-box;
            box-sizing: border-box;

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

          #editorEl .content {
            border: 1px solid #C0C0C0;
            box-shadow: inset 0 1px 2px #e4e4e4;
            background-color: #fff;
          }

          #editorEl .gutter {
            background-color: transparent;

            background-repeat: no-repeat;
            background-position: 50%;
          }

          #editorEl .gutter.gutter-horizontal {
            cursor: col-resize;
            background-image: url('lib/splitjs/vertical.png');
          }

          #editorEl .gutter.gutter-vertical {
            cursor: row-resize;
            background-image: url('lib/splitjs/horizontal.png');
          }

          #editorEl .split.split-horizontal,
          #editorEl .gutter.gutter-horizontal {
            height: 100%;
            float: left;
          }

          @media (prefers-color-scheme: dark) {
            body {
              color-scheme: dark;
              background-color: #282c33;
              color: #cdcdcd;
            }
            #editorEl .gutter.gutter-horizontal {
              opacity: 0.6 !important;
            }
            #editorEl .gutter.gutter-vertical {
              opacity: 0.6 !important;
            }
            #editorEl .content {
              border: 1px solid #575757;
              background-color: #282c34;
              box-shadow: none;
            }
            #editorEl .toggle-fold-button,
            #editorEl .toggle-wrap-button,
            #editorEl .toggle-wrap-button-html {
              background: #1f2024;
              color: #aaaaaa;
              border: 1px solid #575757;
            }
            #editorEl .toggle-fold-button:hover,
            #editorEl .toggle-wrap-button:hover,
            #editorEl .toggle-wrap-button-html:hover {
              color: #d8d8d8;
              border: solid 1px #bfbfbf;
            }

            #accountModalEl .content-wrapper,
            #settingsModalEl .content-wrapper,
            #revisionsModalEl .content-wrapper,
            #loginModalEl .content-wrapper {
              background: #2e3034;
            }
            #accountModalEl .modal-header p,
            #settingsModalEl .modal-header p,
            #revisionsModalEl .modal-header p,
            #loginModalEl .modal-header p {
              color: #8f8f8f;
            }
            #accountModalEl button,
            #settingsModalEl button,
            #revisionsModalEl button,
            #loginModalEl button {
              background: #474747;
            }
            #accountModalEl button:hover,
            #settingsModalEl button:hover,
            #revisionsModalEl button:hover,
            #loginModalEl button:hover {
              background: #565656;
            }
            #accountModalEl span.link,
            #settingsModalEl span.link,
            #revisionsModalEl span.link,
            #loginModalEl span.link,
            #revisionsModalEl ul li span.clickable {
              color: #6d6dee;
            }

            #perchanceConsoleEl #console-input {
              background: initial !important;
            }

            #output-buttons-ctn {
              background: #282c33 !important;
            }
            #output-buttons-ctn button:not([data-ref='warningsModalOpenButton']) {
              /* border: 1px solid #585858 !important;
              background: linear-gradient(to bottom, rgb(44 44 44) 0%, rgb(37 37 37) 47%, rgb(38 38 38) 100%) !important; */
            }
            #output-buttons-ctn > span { /* auto reload checkbox container */
              background-color: #404040 !important;
            }

            .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
              background-color: #282c33 !important;
            }

            #menuBarEl .menu-item.edit-generator-button {
              color: #00d300 !important;
            }
            #menuBarEl .menu-item.account-modal-open-button {
              color: #00d300 !important;
            }

            .warningsModal .modal-body > div {
              background-color: #282c33 !important;
            }
            .warningsModal .modal-body code {
              background-color: #4c4c4c !important;
            }
          }

          .cm-searching {
            background-color: rgba(255, 255, 0, .4) !important;
          }

          .cm-square-bracket { color: #05a; }
          .cm-special-list-name { color: #05a; }
          .cm-curly-bracket { color: #f18c16; }

          .cm-s-one-dark .cm-square-bracket { color: #61afef; }
          .cm-s-one-dark .cm-special-list-name { color: #61afef; }
          .cm-s-one-dark .cm-curly-bracket { color: #f18c16; }
          .CodeMirror.cm-s-one-dark { background-color: #282c34 !important; }
          .cm-s-one-dark .cm-string-2 { color: #98c379 !important; }
          .cm-s-one-dark .cm-matchhighlight { background-color: #ffde631c; }
          .cm-s-one-dark .CodeMirror-selection-highlight-scrollbar { background-color: #ffdd6333; }

          /*
          .cm-s-one-light .cm-square-bracket { color: #05a; }
          .cm-s-one-light .cm-special-list-name { color: #05a; }
          .cm-s-one-light .cm-curly-bracket { color: #f18c16; }
          .cm-s-one-light .cm-string { color: #419c00 !important; }
          .cm-s-one-light .cm-string-2 { color: #419c00 !important; }
          .cm-s-one-light .cm-def { color: #05a !important; }
          .cm-s-one-light .cm-variable { color: #05a !important; }
          */

          .CodeMirror.cm-s-one-light { background-color: #fafafa !important; }
          .cm-s-one-light .cm-string-2 { color: #50a14f !important; }
          .cm-s-one-light .cm-matchhighlight { background-color: #ffe06d52; }
          .cm-s-one-light .CodeMirror-selection-highlight-scrollbar { background-color: #c0ab5d52; }

          /*
          .cm-s-one-dark .cm-comment { color: #636a78 !important; }
          .cm-s-one-dark .cm-variable { color: #61afef !important; }
          .cm-s-one-dark .cm-variable-2 { color: #61afef !important; }
          */
          /*
          .cm-comment { color: #A0A1A7 !important; }
          .cm-variable { color: #05a !important; }
          .cm-def { color: #05a !important; }
          */
        </style>
        <!-- SHARED MODAL STYLES -->
        <style>
          /* WRAPPER/OUTER STYLES */
          .app-modal .outer-wrapper {
            position:fixed;
            top:0;
            left:0;
            width:100%;
            z-index:2;
            padding: 1em;
            box-sizing: border-box;
          }
          .app-modal .background {
            position:fixed;
            top:0;
            left:0;
            width:100%;
            height:100%;
            opacity:0.2;
            background:#000;
            transition: opacity .15s linear;
          }
          .app-modal .content-wrapper {
            overflow:hidden;
            background:#fff;
            max-width:500px;
            width:100%;
            border-radius:3px;
            margin:0 auto;
          }


          /* DEFAULT CONTENT STYLES */
          .app-modal button, .app-modal input {
            height:3em;
          }
          .app-modal input[type="text"],
          .app-modal input[type="password"],
          .app-modal input[type="email"] {
            margin: 0;
            padding:0;
            box-sizing:border-box;
            display:block;
            width:100%;
            border:none;
            border-top:1px solid #eee;
            border-top:1px solid light-dark(#eee, #323232);
            outline:none;
            padding-left: 0.6em;
          }
          .app-modal input.error {
            background-color: #ffcccc;
          }
          .app-modal button {
            background:#eee;
            color: inherit;
            width:50%;
            border:none;
            outline:none;
            font-size: 90%;
          }
          .app-modal button:hover {
            background:#e1e1e1;
          }

          .app-modal .modal-header {
            padding:1em;
          }
          .app-modal .modal-header p {
            color:#444;
            margin:0;
            font-size: 90%
          }
          .app-modal button.main {
            background:#444;
            color:white;
          }
          .app-modal button.main:hover {
            background:#333;
          }
          .app-modal span.link {
            cursor:pointer;
            text-decoration:underline;
            color:#1212ff;
          }
          #minimalModeMenuBtn {
            background: rgb(226 226 226);
            border: 1px solid rgb(162 162 162);
          }
          @media (prefers-color-scheme: dark) {
            #minimalModeMenuBtn {
              background: #303030;
              border: 1px solid #474747;
            }
          }
        </style>
        <div id="appEl" style="display:flex; flex-direction:column; height:100%;">  
          <div id="minimalModeMenuBtn" onclick="this.style.display='none'; menuBarEl.style.display='flex'; backToMinimalModeBtn.style.display='';" style="display:none; position: fixed; top: 0.21rem; right: 0.21rem; z-index: 1000; border-radius: 3px; cursor: pointer; font-size: 0.6rem; line-height: 0.96rem; aspect-ratio: 1 / 1; height: calc(var(--menu-bar-height) - 0.42rem - 2px); align-items: center; justify-content: center;">🛠️</div>
          <div id="menuBarEl" style="position:relative; height:var(--menu-bar-height); min-height:var(--menu-bar-height); width:100%; overflow:hidden; display:flex; align-items: center; justify-content: center;">
            <script>
              if(window.generatorStaticMetaData.header?.mode === "minimal") { 
                menuBarEl.style.display = "none";
                minimalModeMenuBtn.style.display = "flex"; 
              }  
              function enableRobustBackdropFilter() { // this is more robust to e.g. background images, light backgrounds in dark mode, etc. - only enabling if background is actually set because IIRC some browsers currently have significant performance issues with backdrop-filter blur
                document.querySelector(':root').style.setProperty('--menu-bar-item-backdrop-filter', "brightness(0.9) blur(4px)");
                document.querySelector(':root').style.setProperty('--menu-bar-item-backdrop-filter-hover', "brightness(0.84) blur(4px)");
                document.querySelector(':root').style.setProperty('--menu-bar-item-backdrop-filter-dark', "brightness(0.75) blur(4px)");
                document.querySelector(':root').style.setProperty('--menu-bar-item-backdrop-filter-hover-dark', "brightness(0.6) blur(4px)");
              }  
              function colorIsDark(bgColorStr) {
                let a = document.createElement('div');
                a.style.color = bgColorStr;
                let rgb = window.getComputedStyle( document.body.appendChild(a) ).color.match(/\d+/g).map(function(a){ return parseInt(a,10); });
                document.body.removeChild(a);
                let r = rgb[0];
                let g = rgb[1];
                let b = rgb[2];
                let uicolors = [r / 255, g / 255, b / 255];
                let c = uicolors.map((col) => {
                  if (col <= 0.03928) {
                    return col / 12.92;
                  }
                  return Math.pow((col + 0.055) / 1.055, 2.4);
                });
                let L = (0.2126 * c[0]) + (0.7152 * c[1]) + (0.0722 * c[2]);
                return L < 0.179;
              }
              if(typeof window.generatorStaticMetaData.header?.background === "string") {
                if(CSS.supports("background", window.generatorStaticMetaData.header.background)) {
                  menuBarEl.style.background = window.generatorStaticMetaData.header.background;
                  enableRobustBackdropFilter();
                  if(CSS.supports("color", window.generatorStaticMetaData.header.background)) {
                    menuBarEl.style.color = colorIsDark(window.generatorStaticMetaData.header.background) ? "#e3e3e3" : "#050505";
                  }
                }
              }
            </script>
            <div class="container" style="height: calc(100% - 0.42rem); width: calc(100% - 0.42rem); display: flex;">
              <div style="display:flex; height:100%; gap:0.21rem; overflow:hidden; flex-wrap:wrap;">
                <div class="menu-item" style="aspect-ratio: 1 / 1;" onclick="window.open('/')" onmousedown="if(event.which===2) { this.click() }">
                  <span class="menu-item-icon" style="padding:0; font-size:0.9rem;">⚄︎</span>
                </div>
                <div data-ref="communityButton" class="menu-item community" onclick="window.open('https://lemmy.world/c/perchance')" onmousedown="if(event.which===2) { this.click() }" title="• &quot;[Suggestion] add favourites to account menu so you can favourite generators you like for quick acess.&quot; (1d ago)
• &quot;Hierarchical list issue&quot; (2d ago)
• &quot;Privacy button covering the page&quot; (2d ago)
• &quot;[repost] What version of Llama does Perchance use?&quot; (3d ago)
• &quot;is there any way to save a gallery with prompts?&quot; (5d ago)
• &quot;[Suggestion] Implement &quot;Private&quot; messaging for comments plugin&quot; (5d ago)
• &quot;[Suggestion] More improvements for Comments/Gallery&quot; (6d ago)
• &quot;Downvote gallery block score&quot; (6d ago)
• &quot;Possible to move ads to the sides?&quot; (7d ago)">
                  <span class="menu-item-icon">👥︎</span>
                  <span class="menu-item-label" style="padding-right:0;">forum</span>
                  <span style="font-size:70%; display:inline-block; text-align:center; opacity:0.7; padding-right:0.25rem;" data-ref="communityNotifications"> (1d)</span>
                </div>
                <div class="menu-item hub" onclick="window.open('/hub')" onmousedown="if(event.which===2) { this.click() }">
                  <span class="menu-item-icon">🌈</span>
                  <span class="menu-item-label">hub</span>
                </div>
                <div class="menu-item tutorial" onclick="window.open('/tutorial')" onmousedown="if(event.which===2) { this.click() }">
                  <span class="menu-item-icon">📚</span>
                  <span class="menu-item-label">learn</span>
                </div>
                <!-- <div class="menu-item tutorial" onclick="window.open('/resources')" onmousedown="if(event.which===2) { this.click() }">
                  <span class="menu-item-icon">🧰</span>
                  <span class="menu-item-label">resources</span>
                </div> -->
                <div class="menu-item generators" onclick="window.open('/generators')" onmousedown="if(event.which===2) { this.click() }">
                  <span class="menu-item-inner"><span class="menu-item-icon">🎲︎</span>
                  <span class="menu-item-label">generators</span></span>
                </div>
                <div class="menu-item new" onclick="window.open('/minimal#edit')" onmousedown="if(event.which===2) { this.click() }">
                  <span class="menu-item-icon">➕</span>
                  <span class="menu-item-label">new</span>
                </div><div class="menu-item promo" onclick="window.open('/ai-chat')" onmousedown="if(event.which===2) { this.click() }" style="background-color: rgb(255, 212, 164);">
                  <span class="menu-item-icon">🆕</span>
                  <span class="menu-item-label">ai chat</span>
                </div>
              </div>
              <div style="display:flex; height:100%; flex-grow:1; justify-content:end; gap:0.21rem;">
                <div data-ref="statusMessage" style="min-width:max-content; color:green; padding:0 0.5em; font-size: 80%; display:none; align-items: center; justify-content: center; font-weight: bold;"></div>
                <div data-ref="revisionsButton" class="menu-item" onclick="app.openRevisionsModal()" style="display:none;">
                  <span class="menu-item-icon">🗄️</span>
                  <span class="menu-item-label">backups</span>
                </div>
                <div data-ref="saveButton" class="menu-item" onclick="app.saveGenerator();" style="display:none; ">
                  <span class="menu-item-icon">💾</span>
                  <span class="menu-item-label" data-ref="saveText">save</span>
                </div>
                <div data-ref="settingsButton" onclick="app.openSettingsModal()" class="menu-item" style="display:none;">
                  <span class="menu-item-icon">⚙️</span>
                  <span class="menu-item-label">settings</span>
                </div>
                <div data-ref="editButton" class="menu-item edit-generator-button" onclick="window.lastEditButtonClickTime=Date.now(); app.goToEditMode()" style="color: green; display: inline-flex;">
                  <span class="menu-item-icon">🛠️</span>
                  <span class="menu-item-label">edit</span>
                </div>
                <div data-ref="accountButton" onclick="app.openAccountModal()" class="menu-item account-modal-open-button" style="display:none; color:green;">
                  <span class="menu-item-icon">👤</span>
                  <span class="menu-item-label">account</span>
                </div>
                <div data-ref="loginButton" onclick="app.openLoginModal()" class="menu-item" style="display: inline-flex;">
                  <span class="menu-item-icon">🔑</span>
                  <span class="menu-item-label">login</span>
                </div>
                <div id="backToMinimalModeBtn" class="menu-item" onclick="menuBarEl.style.display='none'; minimalModeMenuBtn.style.display='flex';" style="display:none; aspect-ratio: 1/1;">
                  <span class="menu-item-icon" style="padding: 0; font-size: 0.5rem; line-height: 0.4rem;">❌</span>
                </div>
              </div>
            </div>
            <script> 
              window.menuBar = {
                root: document.querySelector("#menuBarEl"),
                refs: new Proxy({}, {
                  get(target, prop, receiver) {
                    return document.querySelector(`#menuBarEl [data-ref='${prop}']`);
                  },
                }),
                init: function() {

                  this.updateMenuButtonsDisplay = function() {
                    this.refs.accountButton.style.display = app.store.data.user.loggedIn ? "inline-flex" : "none";
                    this.refs.loginButton.style.display = app.store.data.user.loggedIn ? "none" : "inline-flex";
                    this.refs.settingsButton.style.display = app.store.data.user.loggedIn && app.userOwnsThisGenerator && app.appInteractionState === "edit" ? "inline-flex" : "none";
                    this.refs.saveButton.style.display = app.appInteractionState === "edit" ? "inline-flex" : "none";
                    this.refs.revisionsButton.style.display = app.store.data.user.loggedIn && app.userOwnsThisGenerator && app.appInteractionState === "edit" ? "inline-flex" : "none";
                    this.refs.editButton.style.display = app.appInteractionState === "view" ? "inline-flex" : "none";
                  };
                  app.on("LoginStateChange", () => {
                    this.updateMenuButtonsDisplay();
                  });
                  app.on("AppInteractionStateChange", () => {
                    this.updateMenuButtonsDisplay();
                    if(app.interactionStateChange === "edit") {
                      this.updateSaveButtonStyle();
                    }
                  });
                  app.on("GeneratorOwnershipChange", () => {
                    this.updateMenuButtonsDisplay();
                  });

                  // this will be changed to "saved" if they are the owner:
                  window.perchanceSaveState = "unsaved";
                  this.saveState = "unsaved";
                
                  this.setSaveState = (state) => {
                    window.perchanceSaveState = state;
                    this.saveState = state;
                    this.updateSaveButtonStyle();
                  };
                  window.setSaveState = this.setSaveState.bind(this);
                
                  this.updateSaveButtonStyle = () => {
                
                    let state = this.saveState;
                
                    this.refs.saveButton.style.color = "";
                    this.refs.saveButton.style.cursor = "";
                    this.refs.saveButton.style.opacity = 1;
                
                    if(state === "saving") {
                      this.refs.saveText.innerText = "saving…";
                      this.refs.saveButton.style.opacity = 0.5;
                    } else if(state === "saved") {
                      this.refs.saveText.innerText = "saved";
                      this.refs.saveButton.style.opacity = 0.5;
                    } else if(state === "unsaved") {
                      this.refs.saveText.innerText = "save";
                      this.refs.saveButton.style.cursor = "pointer";
                    } else if(state === "error") {
                      this.refs.saveText.innerText = "error";
                      this.refs.saveButton.style.color = "red";
                    } else {
                      console.error("Invalid save state.");
                      this.setSaveState("INVALID!");
                      alert("There was an error while saving :S Please report this at lemmy.world/c/perchance if the problem persists.");
                      //this.saveState = "INVALID!";
                    }
                  };
                
                  this.updateCommunityData = async () => {
                    try {
                      let communityData = await (await fetch("/api/getCommunityData")).json();
                      if(communityData.status !== "success") throw communityData;
                      let data = communityData.data;
                      let str = "";
                      let isMostRecentPost = true;
                      let mostRecentPostTimeAgoStr = "";
                      for(let post of data.posts) {
                        let minsAgo = Math.ceil(post.secondsAgo/60);
                        let timeAgoStr = minsAgo > 60*24 ? Math.round(minsAgo/(60*24))+"d" : minsAgo > 60 ? Math.ceil(minsAgo/60)+"h" : minsAgo+"m";
                        if(isMostRecentPost) mostRecentPostTimeAgoStr = timeAgoStr;
                        str += `• "${post.title}" (${timeAgoStr} ago)\n`;
                        isMostRecentPost = false;
                      }
                      if(data.posts[0]/60 > 60*24) {
                        this.refs.communityNotifications.textContent = "";
                        this.refs.communityNotifications.style.display = "none";
                      } else {
                        this.refs.communityNotifications.textContent = " ("+mostRecentPostTimeAgoStr+")";
                        this.refs.communityNotifications.style.display = "inline-block";
                      }
                      this.refs.communityButton.title = str.trim(); // XSS no possible with setting title but CAREFUL if you change this code
                    } catch(e) {
                      console.error("Failed to update community data.");
                    }
                  };
                
                  this.updateCommunityData();
                  setInterval(this.updateCommunityData, 2*60*1000);
                },
              };
            </script>
          </div>
        
          <div id="editorEl" style="width:100%; flex-grow:1; min-height:0;">
            <div style="clear:both;"></div>
            <div id="main" style="height:100%;">
              <div id="a" style="display:none;" class="split split-horizontal">
                <!-- MODEL TEXT -->
                <div id="c" style="height:100%;overflow:hidden;position:relative; border-left:none;" class="split content">
                  <!--<div id="customListEditorEl" style="display:none; position:absolute;top:0;left:0;right:0;bottom:0;background:blue;z-index: 100;">
                    <iframe srcdoc="hello world" sandbox="allow-scripts" style="border: none; margin: 0; padding: 0; outline: none; width: 100%; height: 100%;"></iframe>
                  </div>-->
                  <div class="code-editor-buttons-ctn">
                    <div class="toggle-fold-button" data-ref="codeEditorFoldRefButton" onclick="editor.toggleCodeEditorFold()">fold</div>
                    <div class="toggle-wrap-button" data-ref="codeEditorWrapRefButton" onclick="editor.toggleCodeEditorWrap()">wrap</div>
                  </div>
                  <textarea id="input"></textarea>
                  <!-- TODO***: fix the problem where the horizontal scoll bar doesn't show up sometimes -->
                </div>
                <!-- CONSOLE -->
                <div id="perchanceConsoleEl" style="display:block; height:100%; position:relative; overflow: hidden; border-bottom:none; border-left:none;" class="split content">
                  <input style="display:none;" type="text" name="topreventchromesbadautofillalgorithm">
                  <input style="display:none;" type="password" name="topreventchromesbadautofillalgorithm">
                  <input class="console" id="console-input" placeholder="type some text here and press enter" type="text" autocomplete="off">
                  <!-- <expanding-textarea style="background: #eee;display:block;" class="console" id="console-input" placeholder="type an expression and press enter" /> -->
                  <iframe class="console" id="console-output" sandbox=""></iframe>
                  <select id="consoleInterpreterOption" style="position:absolute; bottom:4px; right:4px;">
                    <option value="html" selected="">html</option>
                    <option value="plaintext">plain text</option>
                  </select>
                  <style>
                    #perchanceConsoleEl .console {
                      font-size:1em;
                      font-family:inherit;
                      position:absolute;
                      border:none;
                      outline:none;
                      padding:0;
                      margin:0;
                      box-sizing:border-box;
                    }
                    #perchanceConsoleEl #console-input {
                      background: #eee;
                      width:100%;
                      height:20px;
                      padding-left: 0.3em;
                    }

                    #perchanceConsoleEl #console-output {
                      width:100%;
                      height:100%;
                      position:absolute;
                      bottom:0;
                      left:0;
                      right:0;
                      top:20px;
                      border:none;
                    }
                    #e {
                      border-right: none !important;
                      border-top: none !important;
                      height: 100%;
                    }
                    #b {
                      width: 100%;
                    }
                    #output {
                      background: white;
                    }
                    @media (prefers-color-scheme: dark) {
                      #output {
                        background: #121212;
                      }
                    }
                  </style>
                </div>
              </div>
          
              <div id="b" class="split split-horizontal">
                <!-- OUTPUT -->
                <div id="e" style="overflow:hidden; position:initial; display:flex; flex-direction:column; border:none;" class="split content">
                  <!-- WARNING: don't change the ID of this `output` element - there is code that relies on it (e.g. ad refresh focus bug fix stuff) -->
                  <div id="output" style="border-top:none;">
                    <div id="outputLoadSpinner" style="display:none; pointer-events:none; position:absolute; top:0; right:0; bottom:0; left:0; justify-content:center; align-items:center;"><div class="ldspmzjfhueifssge-ring"><div></div><div></div><div></div><div></div></div></div>
                    <script>
                      setTimeout(() => {
                        if(!window.perchanceOutputIframeFinishedFirstLoad) document.querySelector("#outputLoadSpinner").style.display = "flex";
                      }, 1500);
                      window.onNextOutputIframeReloadHandlers = [];
                      window.addEventListener("message", async (e) => {
                        let origin = e.origin || e.originalEvent.origin;
                        if(origin !== "https://null.perchance.org" && origin !== `https://${window.generatorPublicId}.perchance.org`) return;
        
                        if(e.data.type === "finishedLoading") {
                          window.perchanceOutputIframeFinishedFirstLoad = true;
                          document.querySelector("#outputLoadSpinner").style.display = "none";
                          for(let fn of window.onNextOutputIframeReloadHandlers) {
                            try { fn(); } catch(e) { console.error(e); }
                          }
                          window.onNextOutputIframeReloadHandlers = [];
                        }

                        if(e.data.type === "ensureEditorIsLoaded75383748") {
                          if(window.location.hash !== "#edit") {
                            let tmp = window.hasBeenInEditModeAtLeastOnce;
                            app.goToEditMode();
                            app.goToViewMode();
                            window.hasBeenInEditModeAtLeastOnce = tmp;
                          }
                          outputIframeEl.contentWindow.postMessage({type:"ensureEditorIsLoaded75383748_response"}, "*");
                        }
                      });
                    </script>
                    <iframe id="outputIframeEl" src="https://a257cd3ebbfff8e014d264b056e5727d.perchance.org/welcome?__generatorLastEditTime=1724944183253" fetchpriority="high" style="user-select:none;" allowfullscreen="" webkitallowfullscreen="" mozallowfullscreen="" allow="fullscreen; clipboard-write; display-capture; camera; microphone" sandbox="allow-popups allow-popups-to-escape-sandbox allow-forms allow-scripts allow-same-origin allow-top-navigation-by-user-activation allow-modals allow-downloads"></iframe>
                    <script>
                      // Must add query string to iframe if it has one, since generator code may rely on that:
                      {
                        let url = document.querySelector("#outputIframeEl").src;
                        let different = false;
                        if(window.location.search) {
                          if(url.includes("?")) url += "&"+window.location.search.slice(1);
                          else url += "?"+window.location.search.slice(1);
                          different = true;
                        }
                        if(window.location.hash) {
                          url += window.location.hash;
                          different = true;
                        }
                        if(different) {
                          document.querySelector("#outputIframeEl").src = url;
                        }
                      }
                      {
                        const outputIframeEl = document.querySelector("#outputIframeEl");
                        // If hash to this top-level frame changes due to user typing in URL bar, tell the iframe to change its hash too:
                        window.addEventListener("hashchange", () => {
                          outputIframeEl.contentWindow.postMessage({type:"changeHash", hash:window.location.hash}, "*");
                        });
                        window.addEventListener("message", (e) => {
                          if(e.source !== outputIframeEl.contentWindow) return;
                          if(e.origin !== `https://${window.generatorPublicId}.perchance.org`) return;
                          
                          // If iframe hash changes, change the top-level hash too
                          if(e.data.type === "changeHash") {
                            if(typeof e.data.hash !== "string") return;
                            if(window.location.hash !== e.data.hash) { // not sure if this is needed to prevent infinite loop, but can't hurt
                              window.location.hash = e.data.hash;
                            }
                          }

                          // Respond to replaceState (*without* allowing path change):
                          if(e.data.type === "changeUrl") {
                            if(typeof e.data.search !== "string" || typeof e.data.hash !== "string") return;
                            history.replaceState({}, "", window.location.pathname+e.data.search+e.data.hash);
                          }

                          if(e.data.type === "changePageTitle") {
                            document.title = e.data.pageTitle;
                          }
                          
                          if(e.data.type === "changeFavicon") {
                            if(typeof e.data.href !== "string") return;
                            document.querySelectorAll("link[rel~='icon']").forEach(el => el.remove());
                            let link = document.createElement('link');
                            link.rel = 'icon';
                            document.head.appendChild(link);
                            link.href = e.data.href || "/favicon-32x32-white-bg.png";
                          }
                        });
                      }
                    </script> 
                  </div>
                  <div id="output-buttons-ctn" style="display:none; padding: 4px; box-sizing:border-box; background: #e9e9e9;">
                    <button onclick="app.goToViewMode()" style="float:left; height:100%;">⇱︎ fullscreen</button>
                    <button data-ref="warningsModalOpenButton" onclick="editor.openWarningsModal()" style="float:left; height:100%; background:#ffd97c; margin-left: 4px; color:black; border:1px solid rgb(255 175 21); display:none;">⚠︎ warnings</button>
                    <button onclick="editor.handleReloadButtonClick()" style="float:right; height:100%;" title="FULL reload (Ctrl+R / Cmd+R)">⟳︎ reload</button>
                    <span title="Toggle partial 'in-place' reloads on each edit. Variables will *not* be cleared. Use the reload button for a 'full' refresh." style="display:inline-block; float:right; background-color:#d5d5d5; padding:4px; margin-right:4px;height: 100%;box-sizing: border-box; cursor:pointer; display: flex; align-items: center; justify-content: center; user-select:none;" onclick="if(event.target !== autoReloadCheckboxEl) autoReloadCheckboxEl.checked=!autoReloadCheckboxEl.checked, toggleOutputAutoReloadOnEdits(autoReloadCheckboxEl);"><input id="autoReloadCheckboxEl" data-ref="autoReloadCheckbox" style="" type="checkbox" onclick="toggleOutputAutoReloadOnEdits(this)">&nbsp;auto</span>
                  </div>
                </div>
                <!-- OUTPUT TEXT/TEMPLATE -->
                <div id="f" style="height:100%;overflow:hidden;position:relative; display:none; border-right:none; border-bottom:none;" class="split content">
                  <button id="showAiHelperBtn" style="right: 5px; bottom: 5px; position: absolute; z-index: 10; font-size: 70%; padding: 0 0.1rem; line-height: 1.6;" onclick="this.hidden=true; aiHelperCtn.hidden=false; localStorage.aiHelperCtnHidden='';" hidden="">✨</button>
                  <div id="aiHelperCtn" style="display: flex; flex-direction: column; right: 5px; bottom: 5px; width: max-content; position: absolute; z-index: 100; gap: 0.125rem; padding: 0.25rem; border-radius: 3px; min-width: 200px; min-height: 80px;">
                    <div id="aiHelperFeedbackBtn" style="position:absolute;top:0;width:100%;left:0;right:0;height: 0;">
                      <button onclick="window.open('https://perchance.org/ai-coding-helper-feedback')" style="position:absolute; bottom:0.25rem; font-size:70%; right:0.25rem; margin:0; padding:0 0.125rem;">❓</button>
                    </div>
                    <div id="aiHelperCloseBtnCtn" style="position:absolute;top:0;left: 0;width:0;height:0;display:flex;align-items:center;justify-content:center;"><div onpointerdown="aiHelperCtn.hidden=true; showAiHelperBtn.hidden=false; localStorage.aiHelperCtnHidden=1" style="cursor:pointer; background:white; background:light-dark(white, black); color:black; color:light-dark(black, #bababa); border-radius:100%; border:1px solid light-dark(black, #bababa); min-width:1rem; min-height:1rem; display:flex; align-items:center; justify-content:center;"><svg width="10" height="10" viewBox="0 0 24 24"><path d="M6 6l12 12M6 18L18 6" stroke="currentColor" stroke-width="4" stroke-linecap="round"></path></svg></div></div>
                    <select id="aiHelpTypeSelect" onchange="aiHelperSubmitBtn.textContent=this.selectedOptions[0].dataset.submitText;" style="min-height: 1.5rem;">
                      <option value="new" data-submit-text="✨ create">write new code (𝘀𝘁𝗮𝗿𝘁 𝗳𝗿𝗲𝘀𝗵)</option>
                      <option value="edit" data-submit-text="🛠️ adjust">𝗲𝗱𝗶𝘁 the existing code</option>
                      <!-- <option value="question" data-submit-text="✨ ask">ask a question about the code</option> -->
                    </select>
                    <textarea id="aiHelperInputEl" placeholder="Create a simple snake game" style="font-size:85%;flex-grow:1;min-width: 200px;"></textarea>
                    <div style="display:flex; gap:0.25rem;">
                      <button hidden="" style="height:min-content;" id="aiHelperRedoBtn" onclick="undoAiHelper()">⏩ redo</button>
                      <button hidden="" style="height:min-content;" id="aiHelperUndoBtn" onclick="undoAiHelper()">🔙 undo</button>
                      <button style="height:min-content; flex-grow:1;" id="aiHelperSubmitBtn" onclick="executeAiHelper()">✨ create</button>
                    </div>
                  </div> 
                  <style>
                    #aiHelperCtn {
                      background: #e0e0e0;
                    }
                    @media (prefers-color-scheme: dark) { 
                      #aiHelperCtn {
                        background: rgb(32, 32, 32);
                      }
                    }
                    @keyframes rotate {
                      to { transform: rotate(360deg); }
                    }
                    
                    .CodeMirror .cm-highlight-change {
                      background: #d3ed8b;
                    }
                    @media (prefers-color-scheme: dark) { 
                      .CodeMirror .cm-highlight-change {
                        background: #495232;
                      }
                    }
                  </style>
                  <script>
                    if(localStorage.aiHelperCtnHidden) {
                      aiHelperCtn.hidden = true;
                      showAiHelperBtn.hidden = false;
                    } else {
                      aiHelperCtn.hidden = false;
                      showAiHelperBtn.hidden = true;
                    }

                    function getChangeHighlightMarks(original, updated) {
                      const diff = Diff.diffLines(original, updated);
                      const markers = [];
                      let lineNumber = 0;
                      diff.forEach((part) => {
                        const lines = part.value.split('\n');
                        if (part.added) {
                          lines.forEach((line, index) => {
                            if(line.length > 0 || index < lines.length - 1) {
                              markers.push({
                                from: { line: lineNumber, ch: 0 },
                                to: { line: lineNumber, ch: line.length },
                                className: 'cm-highlight-change'
                              });
                              lineNumber++;
                            }
                          });
                        } else if(!part.removed) {
                          lineNumber += lines.length - 1;
                          if (lines[lines.length - 1].length > 0) lineNumber++;
                        }
                      });
                      return markers;
                    }
                    function clearChangeHighlightMarks() {
                      window.outputTemplateEditor.getAllMarks().forEach(mark => {
                        if(mark.className === 'cm-highlight-add' || mark.className === 'cm-highlight-change') {
                          mark.clear();
                        }
                      });
                    }
                    
                    async function executeAiHelper() {
                      let type = aiHelpTypeSelect.value;
                      let instruction = aiHelperInputEl.value; 
                      if(!instruction) return;

                      const submitButtonOriginalText = aiHelperSubmitBtn.textContent;
                      aiHelperSubmitBtn.innerHTML = `⏳ loading…`;
                      aiHelperSubmitBtn.disabled = true;
                      aiHelpTypeSelect.disabled = true;

                      aiHelperUndoBtn.hidden = true;
                      aiHelperRedoBtn.hidden = true;
                      
                      let data = await fetch(`/api/aiHelper`, {
                        signal: window.AbortSignal?.timeout?.(5*60000),
                        method: "POST",
                        headers: { "Content-Type": "application/json" },
                        body: JSON.stringify({type, generatorName:window.generatorName, instruction, modelText:window.modelTextEditor.getValue(), outputTemplateText:window.outputTemplateEditor.getValue()}),
                      }).then(r => r.json()).catch(e => ({status:e.message}));

                      aiHelperSubmitBtn.disabled = false;
                      aiHelpTypeSelect.disabled = false;
                      aiHelperSubmitBtn.textContent = submitButtonOriginalText;

                      if(data.status !== "success") {
                        alert(`Sorry, there was an error with the AI helper: ${data.status}${data.status === "too-many-requests" ? " (could be caused by VPN)" : ""}`);
                        return;
                      }

                      window.outputTemplateEditorBeforeAiHelper = window.outputTemplateEditor.getValue();
                      window.modelTextEditorBeforeAiHelper = window.modelTextEditor.getValue();

                      if(type === "new" || type === "edit") {
                        window.outputTemplateEditor.setValue(data.outputTemplateText); 

                        if(data.outputTemplateText.includes("commentsPlugin(") && !/commentsPlugin *= *\{import:comments-plugin\}/.test(window.modelTextEditor.getValue())) window.modelTextEditor.setValue(`commentsPlugin = {import:comments-plugin}\n${window.modelTextEditor.getValue()}`);
                        if(data.outputTemplateText.includes("generateImage(") && !/generateImage *= *\{import:text-to-image-plugin\}/.test(window.modelTextEditor.getValue())) window.modelTextEditor.setValue(`generateImage = {import:text-to-image-plugin}\n${window.modelTextEditor.getValue()}`);
                        if(data.outputTemplateText.includes("generateText(") && !/generateText *= *\{import:ai-text-plugin\}/.test(window.modelTextEditor.getValue())) window.modelTextEditor.setValue(`generateText = {import:ai-text-plugin}\n${window.modelTextEditor.getValue()}`);
                        if(data.outputTemplateText.includes("superFetch(") && !/superFetch *= *\{import:super-fetch-plugin\}/.test(window.modelTextEditor.getValue())) window.modelTextEditor.setValue(`superFetch = {import:super-fetch-plugin}\n${window.modelTextEditor.getValue()}`);
                        
                        // check if we can remove generateImage and/or generateText imports:
                        let currentOutputTemplateText = window.outputTemplateEditor.getValue();
                        if(!currentOutputTemplateText.includes("generateImage(") && !window.modelTextEditor.getValue().replace(/generateImage *= *\{import:text-to-image-plugin\}/g, "").includes("generateImage(")) {
                          window.modelTextEditor.setValue(window.modelTextEditor.getValue().replace(/generateImage *= *\{import:text-to-image-plugin\}/g, ""));
                        }
                        if(!currentOutputTemplateText.includes("generateText(") && !window.modelTextEditor.getValue().replace(/generateText *= *\{import:ai-text-plugin\}/g, "").includes("generateText(")) {
                          window.modelTextEditor.setValue(window.modelTextEditor.getValue().replace(/generateText *= *\{import:ai-text-plugin\}/g, ""));
                        }

                        window.outputTemplateEditorAfterAiHelper = window.outputTemplateEditor.getValue();
                        window.modelTextEditorAfterAiHelper = window.modelTextEditor.getValue();

                        if(type === "edit") {
                          if(!window.Diff) {
                            let script = document.createElement('script');
                            script.src = "https://cdn.jsdelivr.net/npm/[email protected]/dist/diff.min.js";
                            document.body.appendChild(script);
                            while(!window.Diff) await new Promise(r => setTimeout(r, 200));
                          }
                          if(!window.addedHighlightChangeStuff84783) {
                            window.addedHighlightChangeStuff84783 = true;
                            window.outputTemplateEditor.on('change', (instance, changeObj) => {
                              clearChangeHighlightMarks();
                            });
                          }
                          clearChangeHighlightMarks();
                          let markers = getChangeHighlightMarks(window.outputTemplateEditorBeforeAiHelper, window.outputTemplateEditorAfterAiHelper);
                          markers.forEach(marker => window.outputTemplateEditor.markText(marker.from, marker.to, { className: marker.className }));
                        }

                        editor.handleReloadButtonClick();
                        aiHelperUndoBtn.hidden = false;
                        aiHelperUndoBtn.onclick = function() {
                          clearChangeHighlightMarks();
                          window.outputTemplateEditor.setValue(window.outputTemplateEditorBeforeAiHelper);
                          window.modelTextEditor.setValue(window.modelTextEditorBeforeAiHelper);
                          editor.handleReloadButtonClick();
                          aiHelperUndoBtn.hidden = true;
                          aiHelperRedoBtn.hidden = false;
                        };
                        aiHelperRedoBtn.onclick = function() {
                          window.outputTemplateEditor.setValue(window.outputTemplateEditorAfterAiHelper);
                          window.modelTextEditor.setValue(window.modelTextEditorAfterAiHelper);
                          editor.handleReloadButtonClick();
                          aiHelperUndoBtn.hidden = false;
                          aiHelperRedoBtn.hidden = true;
                          if(type === "edit") {
                            clearChangeHighlightMarks();
                            let markers = getChangeHighlightMarks(window.outputTemplateEditorBeforeAiHelper, window.outputTemplateEditorAfterAiHelper);
                            markers.forEach(marker => window.outputTemplateEditor.markText(marker.from, marker.to, { className: marker.className }));
                          }
                        };
                      } else if(type === "question") {
                        // TODO
                      }
                    }
                    window.addEventListener("pointerdown", (e) => {
                      if(e.target.closest("#aiHelpTypeSelect") || e.target.closest("#aiHelperCloseBtnCtn")) return;
                      if(e.target.closest("#aiHelperUndoBtn") || e.target.closest("#aiHelperRedoBtn")) return;
                      if(e.target.closest("#aiHelperFeedbackBtn")) return;
                      if(e.target.closest("#aiHelperCtn")) { aiHelperCtn.style.minWidth='300px'; aiHelperCtn.style.minHeight='200px'; }
                      else { aiHelperCtn.style.minWidth='200px'; aiHelperCtn.style.minHeight='80px'; }
                    });
                    {
                      let aiHelpTypeToExamples = {
                        new: ["Create a simple snake game", "Create a cute profile page with 3 tabs. One for 'about me', one for 'links' and one for 'feedback' with a comments box. Add some kaomojis and pastel colors. Actually also add a 4th tab with a grid of toggleable checkboxes.", "Make an infinite scroller of AI generated images, with the old ones at the top being deleted as you scroll down far enough.", "create a simple centered template with a button which increments a counter", "make a wizard themed geocities homepage for me", "make a space invaders game using emojis"],
                        edit: ["Fix mistakes", "make it look nicer", "make it look better on smaller screens (like mobile phones)", "the button isn't centered properly. fix pls", "Make the button bigger and add a blue gradient background", "Make all the text on the page wobble around", "Add more code comments so I can understand everything better", "Add a loading thing to show when the images are loading", "make it more silly", "if it's june, make it rain transparent rainbow emojis on the page (and also add a little button to trigger it even if it's not june)", "Use this image as the background but blur it slightly: https://example.com/image.jpg", "reduce the number of lines of code", "add an optional dark mode"],
                        question: ["How does the code in the getPosition function work?", "What does display:flex do? How does it work?", "How can I make the background gradient change depending on the mouse position?"],
                      };
                      let i = 0;
                      setInterval(() => {
                        if(document.querySelector("#aiHelperInputEl").offsetHeight === 0) return;
                        let examples = aiHelpTypeToExamples[document.querySelector("#aiHelpTypeSelect").value];
                        i++;
                        if(i > examples.length-1) i = 0;
                        aiHelperInputEl.placeholder = examples[i];
                      }, 3000);
                    }
                  </script>
                  <div class="toggle-wrap-button-html" data-ref="outputEditorWrapRefButton" onclick="editor.toggleOutputEditorWrap()">wrap</div>
                  <textarea id="template"></textarea>
                </div>
              </div>
            </div>
          
            <div class="warningsModal" data-ref="warningsModal" style="display:none;">
              <div style="z-index:50;" class="background" onclick="editor.closeWarningsModal()"></div>
              <div style="z-index:51;" class="outer-wrapper" onclick="editor.handleWarningsModalOuterWrapperClick(event)">
                <div data-ref="warningsModalContentWrapper" class="content-wrapper">
                  <div class="view">
                    <div class="modal-body" style="max-height: 500px; overflow-y: auto;">
                      <div style="padding: 1rem; background: #eaeaea;"><span style="opacity:0.5;">The items listed below aren't errors - they're just "warnings". Perchance generates "warnings" when it detects code in your editor panel that looks like it may be a mistake, but which is technically not "erroneous" - that is, it's valid Perchance syntax, but it's "unusual" code and so might have been an accident on your part. Feel free to ignore these warnings if you know what you're doing! ꒰•ᴗ•꒱</span></div>
                      <div data-ref="warningsWrapper" style="padding-bottom: 1rem;"></div>
                    </div>
                    <div class="modal-footer">
                      <button style="width:100%;" onclick="editor.closeWarningsModal()">close</button>
                    </div>
                  </div>
                </div>
              </div>
            </div>
            <script>
              window.editor = {
                root: document.querySelector("#editorEl"),
                refs: new Proxy({}, {
                  get(target, prop, receiver) {
                    return document.querySelector(`#editorEl [data-ref='${prop}']`);
                  },
                }),
                init: function() { // CAUTION: if you make this async at some point, ensure you await it in ensureEditorIsLoaded75383748 message handler
                  if(this.alreadyInited) return;
                  this.alreadyInited = true;

                  document.querySelector("#editorEl #input").value = app.data.generator.modelText;
                  document.querySelector("#editorEl #template").value = app.data.generator.outputTemplate;

                  this.updateOutputDelayTimeout = null;
                  this.evaluateTextResolveMap = {};
                  this.outputIframe = null;

                  this.autoReload = JSON.parse( localStorage.getItem("editor-auto-reload") );
                  if(this.autoReload === null) {
                    this.autoReload = true; // defaults to true
                    localStorage.setItem("editor-auto-reload", this.autoReload);
                  }

                  window.toggleOutputAutoReloadOnEdits = (checkboxEl) => {
                    if(checkboxEl.checked) {
                      this.autoReload = true;
                      this.updateOutput(false, true);
                    } else { 
                      this.autoReload = false;
                    }
                    localStorage.setItem("editor-auto-reload", this.autoReload);
                  }

                  document.addEventListener('keydown', function(event) {
                    if(event.ctrlKey || event.metaKey) {
                      if(event.key === 'r') {
                        if(window.currentInterfaceMode === "edit") {
                          event.preventDefault();
                          editor.handleReloadButtonClick();
                        }
                      }
                    }
                  });

                  let lastReloadButtonClickTime = 0;
                  this.handleReloadButtonClick = (e) => {
                    outputLoadSpinner.style.backgroundColor = "rgba(255,255,255,0.3)";
                    if(Date.now()-lastReloadButtonClickTime > 500) {
                      lastReloadButtonClickTime = Date.now();
                      window.onNextOutputIframeReloadHandlers.push(function() {
                        outputIframeEl.contentWindow.postMessage({type:"__triggerPageFocus8573946292"}, "*"); // for cases where e.g. the iframe requires focus for keyboard events (like arrow keys in a game)
                      });
                      window.triggerFocusOnNextIframeLoad = true; // for cases where e.g. the iframe requires focus for keyboard events (like arrow keys in a game)
                      this.updateOutput(true, true);
                    }

                    // NOTE: The below if/else is no longer needed due to the `__initWithDataFromParentWindow=1` stuff I'm now doing - i.e. the reload button tells the iframe to not use the data that is send along with the html file, and instead request the data from the parent during init.
                    // if(window.perchanceSaveState === "saved" || !window.editsHaveBeenMadeSincePageLoad) {
                    //   this.updateOutput(true, true);
                    // } else {
                    //   alert("To initiate a \"hard-reload\" you need to first save your generator. Please click save and then try again. Alternatively, you can reload the whole browser page using your browser's reload button, but this will discard your changes.");
                    // }
                  }

                  // function getScrollBarWidth() {
                  //   let el = document.createElement("div");
                  //   el.innerHTML = "\n".repeat(50)
                  //   el.style.cssText = "position:absolute; overflow:scroll; width:30px; height:30px;";
                  //   document.body.appendChild(el);
                  //   let scrollBarWidth = el.offsetWidth - el.clientWidth;
                  //   el.remove();
                  //   return scrollBarWidth;
                  // }
                  
                  // let scrollBarWidth = getScrollBarWidth();
                  // document.querySelectorAll(".toggle-wrap-button").forEach(el => {
                  //   el.style.
                  // });

                  let eSplitLastHeight = null;
                  let bSplitLastWidth = null;
                  this.switchToViewMode = () => {
                    window.currentInterfaceMode = "view";
                    this.root.querySelector("#e").style.position = "initial";
                    this.root.querySelector("#e").style.border = "none";

                    // so we can recover them if they open the editor again:
                    eSplitLastHeight = this.root.querySelector("#e").style.height; 
                    bSplitLastWidth = this.root.querySelector("#b").style.width;

                    this.root.querySelector("#e").style.height = "";
                    this.root.querySelector("#b").style.width = "";

                    this.root.querySelector("#output-buttons-ctn").style.display = "none";
                    let output = this.root.querySelector("#output");
                    let systemIsInDarkMode = !!(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches);
                    
                    // output.style.position = "absolute";

                    menuBarEl.style.borderBottom = "";
                    output.style.borderTop = "none";

                    if(systemIsInDarkMode) {
                      
                    } else {
                      output.querySelector("iframe").style.background = "white";
                      output.style.background = "white";
                    }
                    // output.style.height = "initial";
                    // output.style.borderLeft = "";
                    // output.style.borderRight = "";
                    // output.style.borderBottom = "none";

                    // if(window.shouldAddOutputBorderBottomInViewMode) {
                    //   document.querySelector("#editorEl #output").style.borderBottom = "";
                    // }

                    // need these because otherwise for some reason there's some weird `overscroll-behaviour` stuff near the edges of the screen (even with touch-action:none overlay workaround).
                    this.root.querySelectorAll("#main .gutter").forEach(el => el.style.display="none");
                    this.root.querySelector("#a").style.display = "none";
                    this.root.querySelector("#f").style.display = "none";
                  }
                  
                  this.alreadyInited = false;
                  this.switchToEditMode = () => {
                    window.hasBeenInEditModeAtLeastOnce = true;
                    window.currentInterfaceMode = "edit";
                    let systemIsInDarkMode = !!(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches);

                    this.root.querySelector("#e").style.position = "relative";
                    this.root.querySelector("#e").style.border = "";

                    // recover last height/width:
                    if(eSplitLastHeight) this.root.querySelector("#e").style.height = eSplitLastHeight;
                    if(bSplitLastWidth) this.root.querySelector("#b").style.width = bSplitLastWidth;

                    this.root.querySelector("#output-buttons-ctn").style.display = "";
                    let output = this.root.querySelector("#output");
                    output.style.position = "relative";

                    menuBarEl.style.display = "flex";
                    minimalModeMenuBtn.style.display = "none";

                    menuBarEl.style.borderBottom = "none";
                    output.style.borderTop = "";

                    if(systemIsInDarkMode) {
                      // output.style.borderTop = "1px solid #575757";
                    } else {
                      // output.style.borderTop = "1px solid #C0C0C0";
                    }
                    // output.style.top = "0px";
                    // output.style.bottom = "35px";
                    // output.style.border = "none";
                    //output.style.height = "100%";
                    // output.style.borderLeft = "none";
                    // output.style.borderRight = "none";
                    // output.style.borderBottom = "";

                    // need these because otherwise for some reason there's some weird `overscroll-behaviour` stuff near the edges of the screen (even with touch-action:none overlay workaround).
                    this.root.querySelectorAll("#main .gutter").forEach(el => el.style.display="");
                    this.root.querySelector("#a").style.display = "";
                    this.root.querySelector("#f").style.display = "";

                    setTimeout(() => {
                      this.modelTextEditor.refresh();
                      this.outputTemplateEditor.refresh();
                    }, 10);
                  }

                  this.openWarningsModal = function() {
                    this.refs.warningsModal.style.display = "block";
                  }
                  this.handleWarningsModalOuterWrapperClick = function(e) {
                    if(!this.refs.warningsModalContentWrapper.contains(e.target)) {
                      this.closeWarningsModal();
                    }
                  }
                  this.closeWarningsModal = function() {
                    this.refs.warningsModal.style.display = "none";
                  }

                  window.outputIframeHardReloadFinishedResolvers = [];
                  window.currentlyReloadingOutputIframePromise = null;
                  window.hardReloadOutputIframe = async () => { // must use arrow function for correct `this`
                    await window.currentlyReloadingOutputIframePromise; // wait for existing reload to finish if there is one

                    window.currentlyReloadingOutputIframePromise = new Promise(r => window.outputIframeHardReloadFinishedResolvers.push(r)); // (EDIT: the comment that follows this parenthetical is i think patched by just waiting for the previous promise, as I'm now doing above) this isn't actually ideal because if there are multiple concurrent calls then the first one that loads will resolve all of them, even if that "load" is actually "stale" (i.e. using older code). But it shouldn't be a problem "in practice". It'll hopefully do for now.
                    this.updateOutput(true, true);
                    await window.currentlyReloadingOutputIframePromise;
                  };

                  window.mostRecentFullRefreshUpdateDependenciesPromise = Promise.resolve();
                  this.updateOutput = async (fullRefresh, removeUnfoundDeps) => {
                    let iframe = this.root.querySelector("#output iframe");
                    this.outputIframe = iframe;

                    if(removeUnfoundDeps) {
                      app.data.dependencies = app.data.dependencies.filter(d => d.found !== false);
                    }

                    if(fullRefresh) {
                      outputLoadSpinner.style.display = "flex";

                      // Note that we check *all dependencies*, not just direct imports/children of this generator, since any descendent may have been updated.
                      let depNames = app.data.dependencies.map(d => d.name);

                      // we await this in the `requestOutputUpdate` event (from iframe) handling so it can load in parallel with iframe reload for __initWithDataFromParentWindow stuff.
                      window.mostRecentFullRefreshUpdateDependenciesPromise = this.updateDependencies(depNames, {checkForUpdatesToDepsWeAlreadyHave:true});

                      iframe.onload = () => {
                        // previously thought we didn't need this request anymore because we embed the data in the loaded iframe,
                        // BUT WE STILL NEED IT for when they click the reload button but they haven't saved their changes to the server.
                        // the server can only render the the document with the data that it has.
                        // EDIT: I've commented this out because now I just tell them that they need to save before doing a "hard" reload. The
                        //       previous approach was causing script tags to be doubly-executed which caused all sorts of troubles (e.g. p5.js creating 2 "setup" canvases).
                        //this.updateOutputRequest();
                      }
                      if(false && window.location.hostname === "app.dev") { // "false &&" to disable this for now
                        iframe.src = "http://null.app.dev:3001/"+app.data.generator.name;
                      } else {
                        // iframe.src = "https://null.perchance.org/" + app.data.generator.name + "?__cacheBuster=" + Math.random().toString().slice(2); 
                        // iframe.src = `https://${window.generatorPublicId}.perchance.org/` + app.data.generator.name + "?__cacheBuster=" + Math.random().toString().slice(2);

                        let url = new URL(window.location.href);
                        url.hostname = `${window.generatorPublicId}.perchance.org`;

                        // EDIT: we no longer need to bust cache since we're using modelText and outputTemplate from parent window (and we do updateDependencies above in case they've edited an import)
                        // url.searchParams.set("__cacheBust", Math.random().toString()+Math.random().toString());
                        
                        let v = 1; // window.lastInitDataFromParentWindowValue==1 ? 2 : 1; // Toggle between 1 and 2 because if URL is same as it current src, then `iframe.src=url` won't cause iframe to reload.
                        // WARNING: I'm currently using this '__initWithDataFromParentWindow' query param on the server to know whether I should turn on Origin-Agent-Cluster stuff, since it's useful for devs (stops infinite loop freezes), but causes virtual keyboard bugs for users: https://issues.chromium.org/issues/339927578
                        url.searchParams.set("__initWithDataFromParentWindow", v);
                        window.lastInitDataFromParentWindowValue = v;

                        // Note that unlike in case of initial page load, we don't need __generatorLastEditTime stuff here because we're loading data from parent window, which has latest data.

                        if(iframe.src.split("#")[0] === url.toString().split("#")[0]) {
                          // this is faster, because it doesn't bust the cdn cache, and also prevents debugger context changing:
                          iframe.contentWindow.postMessage({type:"__triggerPageReload857392947"}, "*");
                        } else {
                          iframe.src = url.toString();
                        }

                      }
                    } else {
                      this.updateOutputRequest();
                    }
                  };

                  this.evaluateText = async (text) => {
                    let callerId = "id-" + Math.random() + "-" + Math.random();
                    this.outputIframe.contentWindow.postMessage({text, callerId, command:"evaluateText"}, '*');

                    return new Promise((resolve) => {
                      this.evaluateTextResolveMap[callerId] = resolve;
                    });
                  };

                  let existingDepsCheckAbortController = null;
                  this.updateDependencies = async (requiredDeps, opts={}) => {

                    // EDIT: We *cannot* do this (and we don't need to anyway), because, confusingly, requiredDeps *may* be a subset of deps - most notably only *direct* children of the generator.
                    // app.data.dependencies = app.data.dependencies.filter(d => requiredDeps.includes(d.name));

                    let currentDeps = app.data.dependencies.map(d => d.name);
                    
                    // see which new imports we need to do based on currentDeps and requiredDeps:
                    let depNamesToDownload;
                    if(opts.checkForUpdatesToDepsWeAlreadyHave) {
                      depNamesToDownload = requiredDeps; // check for updates to ALL required deps, in case they've since been edited
                    } else {
                      depNamesToDownload = requiredDeps.filter(d => !currentDeps.includes(d)); // only download data for generators we don't already have
                    }

                    if(depNamesToDownload.length === 0) return false;

                    // If another dependency request trails right behind this one then it'll override `this.latestDependencyRequestTime` and thus this request will be ignored (see the line below the fetch request)
                    let thisDependencyRequestStartTime = Date.now();
                    this.latestDependencyRequestStartTime = thisDependencyRequestStartTime;

                    // tell server about the data we've already got, and if it all matches latest data, then it'll send back `dataAlreadyUpToDate:true`, indicating that no updates are needed
                    let generatorNameToLastKnownEditTime = {};
                    for(let dep of app.data.dependencies) {
                      generatorNameToLastKnownEditTime[dep.name] = dep.lastEditTime;
                    } 
                    
                    if(existingDepsCheckAbortController) existingDepsCheckAbortController.abort("newer_request_obsoletes_this_one");
                    existingDepsCheckAbortController = new AbortController();
                    
                    let result;
                    for(let i = 0; i < 3; i++) {
                      result = await fetch("/api/getGeneratorsAndDependencies", {
                        signal: existingDepsCheckAbortController.signal,
                        method: 'POST',
                        body: JSON.stringify({generatorNames:depNamesToDownload, generatorNameToLastKnownEditTime}),
                        headers: { 'Content-Type': 'application/json' },
                      }).then(r => r.json()).catch(e => {
                        if(e === "newer_request_obsoletes_this_one") return e;
                        else console.error("Error while trying to get generator dependencies:", e);
                      });
                      existingDepsCheckAbortController = null;

                      if(result === "newer_request_obsoletes_this_one") return false;
                      if(result) break;
                    }
                    if(!result) {
                      if(window.location.hash.startsWith("#edit")) {
                        alert("Error while trying to get generator dependencies. This is likely a server bug. Please report on lemmy.world/c/perchance if this is happening often.");
                      }
                      throw new Error("Couldn't get generator dependencies.");
                    }
                    
                    /*just for debugging:*/if(thisDependencyRequestStartTime > this.latestDependencyRequestTime) throw new Error("this shouldn't be possible");
                    if(thisDependencyRequestStartTime !== this.latestDependencyRequestStartTime) return false;

                    if(result.status === "success") {

                      if(result.dataAlreadyUpToDate) {
                        console.debug("Dependencies were already up to date.");
                        return false; // false means "no new dependencies"
                      }

                      for(let g of result.generators) {
                        if(g.found === false) {
                          console.warn("Couldn't find module named '"+g.name+"'.");
                        }
                      }

                      // Update dependencies with new data:
                      let generatorNameData = {};
                      for(let data of result.generators) {
                        generatorNameData[data.name] = data;
                      }
                      for(let data of app.data.dependencies) {
                        if(!generatorNameData[data.name]) generatorNameData[data.name] = data;
                      }
                      app.data.dependencies = Object.values(generatorNameData);

                      return true;

                    } else {
                      console.error("Error while trying to download dependencies:", newDeps);
                    }
                  }

                  this.updateOutputRequest = () => {
                    this.outputIframe.contentWindow.postMessage({
                      command:"updateOutput",
                      generator:{modelText:this.modelTextEditor.getValue(), outputTemplate:this.outputTemplateEditor.getValue(), name:app.data.generator.name},
                      dependencies: app.data.dependencies,
                    }, '*');
                  };

                  const warnings = {

                    "unescaped-equals-sign": `<div class="warning-item"><b style="color:#d58700">Warning:</b> Is the text on line number <b>###lineNumber###</b> (in ###generatorNameText### generator's code) meant to be a "property" of its parent (i.e. <code>property=value</code>), or it is just meant to be a regular item that happens to have an equals character (<code>=</code>) in it? If it's meant to be a regular item, then you'll want to put a "backslash" character (<code>\\</code>) before the equals sign like this: <code>\\=</code>. That's because the equals sign is a special character in Perchance that is used for creating "properties". You can read more about this in the <a target="_blank" href="/tutorial">tutorial</a>.</div>`,

                    "single-equals-in-dynamic-odds": `<div class="warning-item"><b style="color:#d58700">Warning:</b> The dynamic odds notation on line number <b>###lineNumber###</b> (in ###generatorNameText### generator's code) has a single equals sign instead of a double-equals sign. A single equals sign is used to put a value into a variable - for example <code>age = 10</code> means "put the value <code>10</code> into the <code>age</code> variable" whereas <code>age == 10</code> means "is <code>age</code> equal to <code>10</code>?".</div>`,
                    
                    "single-equals-in-if-else-condition": `<div class="warning-item"><b style="color:#d58700">Warning:</b> The if/else condition on line number <b>###lineNumber###</b> (in ###generatorNameText### generator's code) has a single equals sign instead of a double-equals sign. A single equals sign is used to put a value into a variable - for example <code>age = 10</code> means "put the value <code>10</code> into the <code>age</code> variable" whereas <code>age == 10</code> means "is <code>age</code> equal to <code>10</code>?".</div>`,
                    
                    "square-brackets-wrapping-variable-name-within-square-block": `<div class="warning-item"><b style="color:#d58700">Warning:</b> On line number <b>###lineNumber###</b> (in ###generatorNameText### generator's code), you've got a square block - i.e. some square brackets with some stuff inside. Nothing wrong with that. But inside that square block it looks like you've got another square block? Whenever you're writing stuff inside a square block, you can refer to variable and list name <u>directly</u> - no need to put them inside square blocks. So instead of <code style="white-space:nowrap">[[noun].pluralForm]</code>, you'd just write <code style="white-space:nowrap">[noun.pluralForm]</code>, and instead of <code style="white-space:nowrap">[[characterAge] + 3]</code>, you'd just write <code style="white-space:nowrap">[characterAge + 3]</code>, and instead of <code style="white-space:nowrap">^[[age] < 10]</code>, you'd just write <code style="white-space:nowrap">^[age < 10]</code>, and instead of <code style="white-space:nowrap">[[age] > 60 ? [oldDescription] : [youngDescription]]</code>, you'd just write <code style="white-space:nowrap">[age > 60 ? oldDescription : youngDescription]</code>. You should <b>use normal parentheses to group</b> calculations and conditions, like this: <code style="white-space:nowrap">[(age + 1) * height]</code> and this: <code style="white-space:nowrap">[isMammal || (isReptile && isBrown)]</code>. In fact, if you wrap list/variable names in square brackets when you're <i>already inside a square block</i> then this can sometimes cause all sorts of sneaky errors that are hard to "debug". Square brackets <u>can</u> "legally" be used within square brackets in <i>some</i> circumstances, but they have a special meaning (learn more about this on the <a href="https://perchance.org/examples" target="_blank">examples</a> page - especially the "Dynamic Sub-list Referencing" section). One final note: It's okay to use square brackets within square brackets if the <i>inner</i> square brackets are themselves within double-quotes, like so: <code style="white-space:nowrap">[n == 1 ? "[prefix]er" : "[prefix]ers"]</code>.</div>`,

                    "top-level-list-name-same-as-html-element-id": `<div class="warning-item"><b style="color:#d58700">Warning:</b> It looks like you have a list name on line number <b>###lineNumber###</b> that has the same name as an element's <code>id="..."</code> attribute? If so, note that these will conflict with one another and potentially cause errors. For example if you had a list called <code>animal</code>, then you shouldn't have an element like this: <code>&lt;p id="animal"&gt;...&lt;/p&gt;</code></div>`,

                    "duplicate-top-level-list-name": `<div class="warning-item"><b style="color:#d58700">Warning:</b> It looks like you have a list name on line number <b>###lineNumber###</b> (in ###generatorNameText### generator's code) that has the same name as an earlier list/import/variable?</div>`,

                  };

                  this.generatorMetaData = undefined;
                  
                  this.toggleOutputEditorWrap = function() {
                    let btn = this.refs.outputEditorWrapRefButton;
                    let state = this.outputTemplateEditor.getOption('lineWrapping');
                    if(state) {
                      btn.innerText = "wrap";
                    } else {
                      btn.innerText = "unwrap";
                    }
                    // record cursor position:
                    let cursorLine = this.outputTemplateEditor.getCursor().line;
                    let cursorTop = this.outputTemplateEditor.cursorCoords().top - this.outputTemplateEditor.display.wrapper.getBoundingClientRect().top;

                    this.outputTemplateEditor.setOption('lineWrapping', !state);
                    localStorage[`outputEditorWrap_${window.generatorName}`] = !state ? "1" : "";

                    // recover scroll to cursor position:
                    this.outputTemplateEditor.scrollIntoView({line:cursorLine, char:0}, cursorTop);
                  };
                  this.toggleCodeEditorWrap = function() {
                    let btn = this.refs.codeEditorWrapRefButton;
                    let state = this.modelTextEditor.getOption('lineWrapping');
                    if(state) {
                      btn.innerText = "wrap";
                    } else {
                      btn.innerText = "unwrap";
                    }
                    // record cursor position:
                    let cursorLine = this.modelTextEditor.getCursor().line;
                    let cursorTop = this.modelTextEditor.cursorCoords().top - this.modelTextEditor.display.wrapper.getBoundingClientRect().top;
                    
                    this.modelTextEditor.setOption('lineWrapping', !state);
                    localStorage[`codeEditorWrap_${window.generatorName}`] = !state ? "1" : "";

                    // recover scroll to cursor position:
                    this.modelTextEditor.scrollIntoView({line:cursorLine, char:0}, cursorTop);
                  };

                  this.allListsFolded = false;
                  this.toggleCodeEditorFold = function() {
                    let btn = this.refs.codeEditorFoldRefButton;
                    this.allListsFolded ? CodeMirror.commands.unfoldAll(this.modelTextEditor) : CodeMirror.commands.foldAll(this.modelTextEditor);
                    this.allListsFolded = !this.allListsFolded;
                    btn.innerText =  this.allListsFolded ? "unfold" : "fold";
                  };


                  this.outputIframe = this.root.querySelector("#output iframe");

                  this.refs.autoReloadCheckbox.checked = this.autoReload;

                  window.addEventListener("message", async (e) => {
                    let origin = e.origin || e.originalEvent.origin;
                    if(origin !== "https://null.perchance.org" && origin !== `https://${window.generatorPublicId}.perchance.org`) return;

                    if(e.data.type === "evaluateTextResponse" && typeof e.data.text === "string" && typeof e.data.callerId === "string") {
                      let resolveMap = this.evaluateTextResolveMap;
                      if(resolveMap[e.data.callerId]) {
                        resolveMap[e.data.callerId](e.data.text);
                        delete resolveMap[e.data.callerId];
                      }
                    }

                    if(e.data.type === "requestOutputUpdate") { // for 'hard reload' without having to first save data to server
                      // user clicks reload button which triggers a fullRefresh, but it adds __initWithDataFromParentWindow=1 to the URL, which causes the outputIframeContent.html code to not use the data embedded in the HTML, and instead request this parent frame for the 'fresh' data
                      await window.mostRecentFullRefreshUpdateDependenciesPromise; // so dependency stuff can load in parrallel with iframe for __initWithDataFromParentWindow fullRefresh
                      this.updateOutputRequest();
                    }

                    if(e.data.type === "saveKeyboardShortcut") {
                      app.handleIframeSaveRequest();
                    }

                    // if(e.data.type === "finishedLoading") {
                    //   window.perchanceOutputIframeFinishedFirstLoad = true;
                    //   // initialPageLoadSpinner.style.display = "none";
                    //   outputLoadSpinner.style.display = "none";
                    // }

                    if(e.data.type === "finishedLoadingIncludingMetaData" || e.data.type === "failedToLoadDueToGeneratorErrors") {
                      
                      if(e.data.imports) { // <-- need to check because failedToLoadDueToGeneratorErrors doesn't give us imports
                        // we need to check if there are new imports before resolving the hard reload stuff because the saveGenerator function uses a hard reload before saving (which triggers these messages and resolves below), and we want to make sure we save the generator with all its up-to-date dependencies included.
                        let wereNewDeps = await window.lastImportsUpdateDependencyCheckPromise;
                        if(wereNewDeps) return; // don't want to resolve yet - the importsUpdate handler will trigger a second hard reload
                      }

                      for(let resolver of window.outputIframeHardReloadFinishedResolvers) {
                        resolver();
                      }
                      window.outputIframeHardReloadFinishedResolvers = [];
                    }

                    if(e.data.type === "importsUpdate" && Array.isArray(e.data.imports) && e.data.imports.filter(i => typeof i !== 'string').length === 0) {
                      await window.thisIsNotAnOldCachedPagePromise; // <-- wait until we've confirmed that this is the latest version of this generator. this prevents a weird race condition where an import update will happen fast during page load and beat the cache bust, which cause an auto-save (importsUpdates can do this), and overwrites the new code with old code.
                      // (note that the above promise won't resolve unless it's true)

                      window.lastImportsUpdateDependencyCheckPromise = this.updateDependencies(e.data.imports); // i think we only want to check for NEW deps here, not updates to existing deps, becuase a full iframe reload will do the latter anyway.

                      let wereNewDeps = await window.lastImportsUpdateDependencyCheckPromise; // <-- this actually downloads new dependencies (**if needed**, else returns false)
                      if(wereNewDeps) {
                        // we only update if there were new dependencies otherwise we will get into infinite loops
                        // (due to the way I've handled removing dependencies (I keep them cached still))
                        // TODO: this whole imports-update part is terrible and needs re-writing to make it simple and understandable.
                        // Wouldn't take toooo much work.
                        this.updateOutput(true); // <-- this triggers iframe updateOutput function. `true` => fullRefresh, i.e. fully reload the iframe with __initWithDataFromParentWindow
                      }
                      
                      if(wereNewDeps || app.data.dependencies.length > e.data.imports.length) {
                        // if dependency was *dropped*, no need to update the output - just need to update the data:
                        app.generatorEditedCallback({imports:e.data.imports});
                      }
                      
                    }

                    if(e.data.type === "metaUpdate") {
                      if(e.data._validation.generatorName !== app.data.generator.name) return; // <-- trying to stop weird Google crawler page title bug

                      // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                      // WARNING WARNING WARNING WARNING WARNING WARNING WARNING: If you change this at all, make sure to do a thorough check for XSS bugs. Saved meta info must be sanitised ON THE SERVER during rendering.
                      // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                      let title = undefined;
                      let description = undefined;
                      let image = undefined;
                      let tags = undefined;
                      let header = undefined;
                      let dynamic = undefined;

                      // if(typeof e.data.html === "string") {
                      //   if(e.data.html.length < 100_000 && window.shouldLiftGeneratorHtmlFromEmbed) {
                      //     window.document.querySelector("#generator-description").innerHTML = DOMPurify.sanitize(e.data.html, {ALLOWED_TAGS: ['h1', 'h2', 'h3', 'h4', 'p', 'div', 'pre', 'button', 'i', 'b', 'a', 'ul', 'li', 'br', 'ol', 'hr'], ALLOWED_ATTR: ['href']});
                      //     if(!window.generatorCanLinkWithRelFollow) window.document.querySelectorAll("#generator-description a").forEach(a => a.rel="nofollow");
                      //   }
                      // }

                      if(typeof e.data.title === "string") {
                        title = e.data.title.slice(0, 500); // MUST be sanitised on the server during rendering
                      }
                      if(typeof e.data.description === "string") {
                        description = e.data.description.slice(0, 3000); // MUST be sanitised on the server during rendering
                      }
                      if(typeof e.data.image === "string") {
                        image = e.data.image.slice(0, 500); // MUST be sanitised on the server during rendering
                      }
                      if(typeof e.data.dynamic === "string") {
                        dynamic = e.data.dynamic.slice(0, 40000);
                      }
                      if(typeof e.data.tags === "string") {
                        tags = e.data.tags.slice(0, 1000).split(",").map(t => t.trim()).filter(t => t.length > 0);
                      }
                      if(e.data.header && typeof e.data.header === "object") {
                        header = {};
                        if(typeof e.data.header.background === "string") {
                          if(CSS.supports("background", e.data.header.background)) {
                            header.background = e.data.header.background.slice(0, 2000);
                            menuBarEl.style.background = header.background;
                            enableRobustBackdropFilter();
                            if(CSS.supports("color", header.background)) {
                              menuBarEl.style.color = colorIsDark(header.background) ? "#e3e3e3" : "#050505";
                            }
                          }
                        }
                        if(typeof e.data.header.mode === "string") {
                          header.mode = e.data.header.mode.slice(0, 100);
                        }
                      }
                      this.generatorMetaData = {title, image, description, dynamic, tags, header};
                    }

                    if(e.data.type === "codeWarningsUpdate") {

                      let validWarningsCount = 0;
                      this.refs.warningsModalOpenButton.style.display = "none";
                      this.refs.warningsWrapper.innerHTML = "";
                      let warningsHTML = "";
                      for(let warning of e.data.warnings) {
                        if(typeof warning.lineNumber !== "number" || typeof warning.warningId !== "string" || /[^a-z0-9\-]/.test(warning.generatorName)) return;
                        let warningTemplate = warnings[warning.warningId];
                        if(!warningTemplate) {
                          console.error("invalid warning id?");
                          continue;
                        }
                        warningTemplate = warningTemplate.replace(/###lineNumber###/g, warning.lineNumber);
                        let generatorNameText;
                        if(warning.generatorName === window.location.pathname.slice(1)) generatorNameText = "<b>this</b>";
                        else generatorNameText = "the <b>"+warning.generatorName+"</b>";
                        warningTemplate = warningTemplate.replace(/###generatorNameText###/g, generatorNameText);
                        warningTemplate = warningTemplate.replace(/ \(in <b>this<\/b> generator's code\)/g, ""); // hackily remove this, since it actually makes it more confusing for newbies I think.
                        warningsHTML += warningTemplate;
                        validWarningsCount++;
                        if(validWarningsCount > 10) break;
                      }
                      if(validWarningsCount > 0) {
                        this.refs.warningsModalOpenButton.style.display = "inline-block";
                        this.refs.warningsWrapper.innerHTML = warningsHTML;
                        this.refs.warningsModal.querySelector(".modal-body").scrollTop = 0;
                      }
                    }

                    // if(e.data.type === "requestFullscreen") {
                    //   if(this.outputIframe.requestFullscreen) this.outputIframe.requestFullscreen();
                    //   else if(this.outputIframe.webkitRequestFullscreen) this.outputIframe.webkitRequestFullscreen();
                    //   else if(this.outputIframe.mozRequestFullscreen) this.outputIframe.mozRequestFullscreen();
                    //   else if(this.outputIframe.msRequestFullscreen) this.outputIframe.msRequestFullscreen();
                    // }
                    //
                    // if(e.data.type === "exitFullscreen") {
                    //   if(this.outputIframe.exitFullscreen) this.outputIframe.exitFullscreen();
                    //   else if(this.outputIframe.webkitExitFullscreen) this.outputIframe.webkitExitFullscreen();
                    //   else if(this.outputIframe.mozCancelFullscreen) this.outputIframe.mozCancelFullscreen();
                    //   else if(this.outputIframe.msExitFullscreen) this.outputIframe.msExitFullscreen();
                    // }
                  });

                  // codemirror settings
                  CodeMirror.keyMap.default["Shift-Tab"] = "indentLess";
                  CodeMirror.keyMap.default["Tab"] = "indentMore";

                  // Add some VSCode shortcuts (using sublime.js functions, just different shortcuts):
                  CodeMirror.keyMap.default["Alt-Up"] = "swapLineUp";
                  CodeMirror.keyMap.default["Alt-Down"] = "swapLineDown";
                  CodeMirror.keyMap.default["Ctrl-Shift-Up"] = "addCursorToPrevLine";
                  CodeMirror.keyMap.default["Ctrl-Shift-Down"] = "addCursorToNextLine";
                  // TODO: make both alt+click and ctrl+click work for multi-cursor

                  this.root.querySelector("#input").value = this.root.querySelector("#input").value.replace(/\n[\t ]+/g, (match) => match.replace(/\\t/g, "  "));
                  // I'm reluctant to mess with tabs in the HTML editor because they are significant in e.g. <pre>, and I'm not sure if replacing with entity (&#9;) would actually work in all cases. Should be solved for all future generators anyway (via pasting intercept code below).

                  let isTouchDevice = window.matchMedia("(pointer: coarse)").matches;

                  let systemIsInDarkMode = !!(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches);
                  this.modelTextEditor = CodeMirror.fromTextArea(this.root.querySelector("#input"), {
                    lineNumbers: true,
                    foldGutter: true,
                    extraKeys: {
                      //"Ctrl-Q": cm => cm.foldCode(cm.getCursor()),
                      //"Ctrl-Y": cm => CodeMirror.commands.foldAll(cm),
                      //"Ctrl-I": cm => CodeMirror.commands.unfoldAll(cm),
                    },
                    gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
                    tabSize: 2,
                    indentUnit: 2,
                    indentWithTabs: false,
                    matchBrackets: true,
                    mode: "perchancelists",
                    styleActiveLine: true,
                    lineWrapping:false,
                    theme: systemIsInDarkMode ? "one-dark" : "one-light",
                    keyMap: "sublime",
                    highlightSelectionMatches: isTouchDevice ? undefined : {showToken: /\w/, annotateScrollbar: true}, // this apparently messes up caret movement stuff on touch devices
                  });

                  // hacky fix for a bug where if you type two consecutive backticks, the `perchancelists` mode's JS state gets messed up for some reason.
                  // we just retokenize when backtick is typed.
                  let backtickHighlightRetokenizeTimeout = null;
                  this.modelTextEditor.on("beforeChange", function(cm, changeObj) {
                    if(changeObj.text.some(line => line.includes('`'))) {
                      clearTimeout(backtickHighlightRetokenizeTimeout);
                      backtickHighlightRetokenizeTimeout = setTimeout(() => cm.setOption("mode", "perchancelists"), 200);
                    }
                  }); 

                  this.outputTemplateEditor = CodeMirror.fromTextArea(this.root.querySelector("#template"), {
                    // lineNumbers: true,
                    // foldGutter: true,
                    // gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
                    mode: {name: "htmlmixed"},
                    selectionPointer: true,
                    tabSize: 2,
                    indentUnit: 2,
                    indentWithTabs: false,
                    lineWrapping:false,
                    styleActiveLine: true,
                    theme: systemIsInDarkMode ? "one-dark" : "one-light",
                    keyMap: "sublime",
                    highlightSelectionMatches: isTouchDevice ? undefined : {showToken: /\w/, annotateScrollbar: true}, // this apparently messes up caret movement stuff on touch devices
                  });  




                  // async function initCodeMirror6(targetElement, {mode, initialCode}={}) {
                  //   if(typeof CM === 'undefined') {
                  //     await new Promise(resolve => {
                  //       const script = document.createElement('script');
                  //       // Snapshot of below url on sept 2nd 2024: https://user-uploads.perchance.org/file/b6ff332ae4c0ae17cc05de619ad5dcb5.js
                  //       script.src = 'https://codemirror.net/6/codemirror.js'; // TODO: create a leaner bundle (this contains *everything* - 1.4mb)
                  //       document.head.appendChild(script);
                  //       script.onload = resolve;
                  //     });
                  //   }
                  //   const { EditorView, keymap, lineNumbers } = CM["@codemirror/view"];
                  //   const { EditorState } = CM["@codemirror/state"];
                  //   const { defaultKeymap } = CM["@codemirror/commands"];
                  //   const { syntaxHighlighting, defaultHighlightStyle, bracketMatching } = CM["@codemirror/language"];
                  //   let languageSupport;
                  //   switch (mode) {
                  //     case 'javascript':
                  //       languageSupport = CM["@codemirror/lang-javascript"].javascript();
                  //       break;
                  //     case 'html':
                  //       languageSupport = CM["@codemirror/lang-html"].html();
                  //       break;
                  //     case 'css':
                  //       languageSupport = CM["@codemirror/lang-css"].css();
                  //       break;
                  //     // Add more language modes as needed
                  //     default:
                  //       languageSupport = CM["@codemirror/lang-javascript"].javascript();
                  //   }
                  //   return new EditorView({
                  //     state: EditorState.create({
                  //       doc: initialCode,
                  //       extensions: [
                  //         keymap.of(defaultKeymap),
                  //         lineNumbers(),
                  //         bracketMatching(),
                  //         languageSupport,
                  //         syntaxHighlighting(defaultHighlightStyle)
                  //         // TODO: https://github.com/val-town/codemirror-codeium
                  //       ]
                  //     }),
                  //     parent: targetElement
                  //   });
                  // }
                  // // Usage
                  // document.body.innerHTML = `<div id="containerEl"></div>`;
                  // let editorView = await initCodeMirror6(containerEl, {mode:'javascript', initialCode:'console.log("Hello, CodeMirror 6!");'});


                  // so the save code can access them for markClean stuff:
                  window.modelTextEditor = this.modelTextEditor;
                  window.outputTemplateEditor = this.outputTemplateEditor;


                  if(localStorage.editorFontSize && !isNaN(Number(localStorage.editorFontSize))) {
                    let size = Number(localStorage.editorFontSize);
                    let style = document.createElement("style");
                    style.innerHTML = `.CodeMirror {
                      font-size: ${size}px !important;
                      line-height: ${size*1.4}px !important;
                    }`;
                    document.head.appendChild(style);
                  }

                  if(localStorage[`codeEditorWrap_${window.generatorName}`]) {
                    this.refs.codeEditorWrapRefButton.innerHTML = "unwrap";
                    this.modelTextEditor.setOption('lineWrapping', true);
                  }
                  if(localStorage[`outputEditorWrap_${window.generatorName}`]) {
                    this.refs.outputEditorWrapRefButton.innerHTML = "unwrap";
                    this.outputTemplateEditor.setOption('lineWrapping', true);
                  }

                  // Add Alt+Z shortcut to toggle wrapping:
                  document.addEventListener('keydown', (event) => {
                    if (event.altKey && event.key === 'z') {
                      event.preventDefault();
                      if(this.modelTextEditor.hasFocus()) this.toggleCodeEditorWrap();
                      if(this.outputTemplateEditor.hasFocus()) this.toggleOutputEditorWrap();
                    }
                  });

                  window.editsHaveBeenMadeSincePageLoad = false;

                  // these are also called after the generator is saved, so long as window.lastEditorsChangeTime is the same as the instant that the save operation was initiated.
                  // codemirror will make editor.isClean() return true when it comes back to this state, **including via undo**
                  this.modelTextEditor.markClean();
                  this.outputTemplateEditor.markClean();


                  // hide wrap/fold buttons if cursor is near them, or on first line
                  async function updateToolbarVisibility(editor) {
                    await new Promise(r => setTimeout(r, 10));
                    let editorCtn = editor.getWrapperElement().parentElement;
                    const cursor = editorCtn.querySelector(".CodeMirror-cursor");
                    const hoverToolbar = editorCtn.querySelector('.code-editor-buttons-ctn') || editorCtn.querySelector('.toggle-wrap-button-html');
                    if(!cursor || !hoverToolbar) return;

                    const cursorRect = cursor.getBoundingClientRect();
                    const toolbarRect = hoverToolbar.getBoundingClientRect();
                    let isNear = cursorRect.top <= toolbarRect.bottom+20 && cursorRect.left >= toolbarRect.left-50 && cursorRect.left <= toolbarRect.right+50;

                    if(editor.getCursor().line === 0) isNear = true;
                    
                    hoverToolbar.style.pointerEvents = isNear ? 'none' : 'auto';
                    hoverToolbar.style.opacity = isNear ? '0' : '1';
                  }
                  window.modelTextEditor.on('keyHandled', updateToolbarVisibility);
                  window.outputTemplateEditor.on('keyHandled', updateToolbarVisibility);
                  window.modelTextEditor.getWrapperElement().addEventListener('mouseup', () => updateToolbarVisibility(window.modelTextEditor));
                  window.outputTemplateEditor.getWrapperElement().addEventListener('mouseup', () => updateToolbarVisibility(window.outputTemplateEditor));


                  // this is also set in the save handler. they're used as an extra check in doLocalGeneratorBackupIfNeeded to prevent backing up text that is already saved.
                  window.lastModelTextSaved = this.modelTextEditor.getValue();
                  window.lastOutputTemplateSaved = this.outputTemplateEditor.getValue();

                  // this is stupid but it's the only way I could get chrome to stop suggesting an email/password in codemirror's search dialogue
                  try {
                    function watchForChildWithClass(target, className, callback) {
                      new MutationObserver(mutations => {
                        mutations.forEach(mutation => {
                          Array.from(mutation.addedNodes).forEach(node => {
                            if(node.nodeType === 1 && node.classList.contains(className)) callback(node);
                          });
                        });
                      }).observe(target, { childList: true });
                    }
                    setTimeout(() => {
                      for(let container of Array.from(document.querySelectorAll(".CodeMirror"))) {
                        watchForChildWithClass(container, "CodeMirror-dialog", element => {
                          for(let inputEl of Array.from(element.querySelectorAll("input"))) {
                            let dummyEl = document.createElement("div");
                            dummyEl.innerHTML = `<input type="email"><input type="password">`;
                            dummyEl.style.cssText = "opacity:0; pointer-events:none; position:absolute;";
                            inputEl.parentElement.insertBefore(dummyEl, inputEl);
                          }
                        });
                      }
                    }, 400);
                  } catch(e) { console.error(e); }



                  // there was some funky business happening with tabs when people *pasted* them.
                  // This replaces all tabs with two spaces (users need to write \t if they want a tab in their text).
                  // EDIT: also just added non-breaking space replacement with normal space, since it was messing with indenting stuff.
                  this.modelTextEditor.on("beforeChange", (cm, change) => {
                    if(change.origin === "paste") {
                      change.update(null, null, change.text.map(line => line.replace(/\t/g, "  ").replaceAll(`\u00A0`, " ")));
                    }
                  });
                  // I took this out because in HTML, some text with to consecutive spaces is different to &nbsp;&nbsp;, and I think the same is true of tabs. So I think this would turn code indents into a bunch of visible whitespace.
                  // this.outputTemplateEditor.on("beforeChange", (cm, change) => {
                  //   if(change.origin === "paste") {
                  //     change.update(null, null, change.text.map(line => line.replace(/\t/g, "&#9;")));
                  //   }
                  // });

                  // This is hacky, but it seems to work. It's from here: https://codemirror.net/demo/indentwrap.html
                  // It makes it so wrapped text is indented at the same indent level as the line itself.
                  let basePadding = 4;
                  // Only do the indentwrap stuff if there are no tabs (literal `\t` is fine), because otherwise it (for some reason) doesn't work - it messes things up: jsbin.com/tukuximome
                  if(!this.root.querySelector("#template").value.includes("\t")) {
                    let charWidthOutputTemplate = this.outputTemplateEditor.defaultCharWidth();
                    this.outputTemplateEditor.on("renderLine", function(cm, line, elt) {
                      let off = CodeMirror.countColumn(line.text, null, cm.getOption("tabSize")) * charWidthOutputTemplate;
                      elt.style.textIndent = "-" + off + "px";
                      elt.style.paddingLeft = (basePadding + off) + "px";
                    });
                  }
                  if(!this.root.querySelector("#input").value.includes("\t")) {
                    let charWidthModelText = this.modelTextEditor.defaultCharWidth();
                    this.modelTextEditor.on("renderLine", function(cm, line, elt) {
                      let off = CodeMirror.countColumn(line.text, null, cm.getOption("tabSize")) * charWidthModelText;
                      elt.style.textIndent = "-" + off + "px";
                      elt.style.paddingLeft = (basePadding + off) + "px";
                    });
                  }



                  //////////////////////////////////////
                  //       LOCAL STORAGE BACKUPS      //
                  //////////////////////////////////////
                  const maxLocalBackupInterval = localStorage.__speedUpLocalBackupStuffForTesting ? 1000*5 : 1000*60*5; // CAUTION: if you change this, change `doLocalBackupDebounce6MinTimeout` to be larger than it
                  const oldLocalBackupCleaningInterval = localStorage.__speedUpLocalBackupStuffForTesting ? 1000*60*2 : 1000*60*20;
                  const maxLocalBackupAge = localStorage.__speedUpLocalBackupStuffForTesting ? 1000*60*10 : 1000*60*60*24*30;
                  const maxLocalBackupsPerGenerator = localStorage.__speedUpLocalBackupStuffForTesting ? 10 : 30;
                  if(localStorage.__speedUpLocalBackupStuffForTesting) {
                    console.warn(`WARNING: local backups sped up due to truthy localStorage.__speedUpLocalBackupStuffForTesting value\n`.repeat(20));
                  }
                  let lastLocalBackupTime = Date.now();
                  const doLocalGeneratorBackupIfNeeded = async (modelText, outputTemplate) => {
                    if(!app.userOwnsThisGenerator) return; // they don't own this gen (i.e. are just playing around in editor for now)
                    if(Date.now()-window.generatorLastSaveTime < maxLocalBackupInterval) return; // hasn't been a few mins since last save
                    if(Date.now()-lastLocalBackupTime < maxLocalBackupInterval) return; // at most once every few mins
                    if(window.lastModelTextSaved === modelText && window.lastOutputTemplateSaved === outputTemplate) return; // since they may have made an edit, but then undone it
                    
                    lastLocalBackupTime = Date.now();
                    if(!kv) await window.initKv();
                    let backupsArray = await kv.localBackups.get(app.data.generator.name) || [];
                    backupsArray.unshift({modelText, outputTemplate, time:Date.now()});
                    // if there are more than `maxLocalBackupsPerGenerator` backups, delete every second one in the second half of the array:
                    if(backupsArray.length > maxLocalBackupsPerGenerator) {
                      backupsArray = backupsArray.filter((backup, i) => i < maxLocalBackupsPerGenerator/2 ? true : i%2===0);
                    }
                    await kv.localBackups.set(app.data.generator.name, backupsArray);
                    console.debug("Saved local backup. NEW backupsArray:", backupsArray);
                  };
                  setTimeout(async () => {
                    if(app.userOwnsThisGenerator) {
                      let persistent = await navigator.storage.persist();
                      if(!persistent) console.warn("Browser denied persistent local storage.");
                    }
                  }, 1000*60*20);
                  setInterval(async () => {
                    if(!kv) return;
                    let entries = await kv.localBackups.entries();
                    // for *every* backed-up generator (not just the one they're currently viewing/editing) stored in this user's browser:
                    for(let [generatorName, backupsArray] of entries) {
                      let originalLength = backupsArray.length;
                      // delete the backup array entries older than several weeks:
                      backupsArray = backupsArray.filter(backup => Date.now()-backup.time < maxLocalBackupAge);
                      if(originalLength === backupsArray.length) continue;
                      // save changes:
                      if(backupsArray.length === 0) {
                        await kv.localBackups.delete(generatorName);
                        await kv.localBackupsLastWarnTimes.delete(generatorName);
                      } else {
                        console.debug(`Cleared ${originalLength-backupsArray.length} old backups. NEW backupsArray:`, backupsArray);
                        await kv.localBackups.set(generatorName, backupsArray);
                      }
                    }
                  }, oldLocalBackupCleaningInterval);
                  // if the page loads, and user owns this generator, and the last backup time is more than a few mins forward in time from last save, then warn them to click header 'backups' button
                  setTimeout(async () => {
                    let startTime = Date.now();
                    while(app.userOwnsThisGenerator === undefined) await new Promise(r => setTimeout(r, 1000*10));
                    if(Date.now()-startTime > 1000*60) {
                      console.warn(`Took a ${Date.now()-startTime}ms (i.e. much longer than expected) to determine if this user owns this generator?`);
                      return;
                    }
                    if(!app.userOwnsThisGenerator) return;
                    if(!kv) await window.initKv();
                    let localBackupsArr = await kv.localBackups.get(app.data.generator.name);
                    let lastWarnTimeForThisGen = await kv.localBackupsLastWarnTimes.get(app.data.generator.name) || 0;
                    if(localBackupsArr) {
                      for(let data of localBackupsArr) {
                        if(data.time-window.generatorLastSaveTime > 1000*60*3 && data.time > lastWarnTimeForThisGen) {
                          try {
                            app.goToEditMode();
                            document.querySelector(`#menuBarEl [data-ref="revisionsButton"]`).style.backgroundColor = "#7d5100";
                          } catch(e) { console.error(e); }
                          await kv.localBackupsLastWarnTimes.set(app.data.generator.name, Date.now());
                          alert(`Warning: There's a backup of this generator stored in your browser memory which is newer than the version of this generator you're currently viewing. This could have been caused by a problem where the server didn't properly save your generator, or if your browser previously crashed while you were editing, or it could just be that you made a little edit but then decided not to save it. If you lost some work, click the 'backups' button in the header to download the code for a backed-up version.`);
                          return;
                        }
                      }
                    }
                  }, 1000*3);

                  let editorContentAlreadySavedDebounceTimeout;
                  function checkIfEditorContentAlreadySavedWithDebounce(modelText, outputTemplate) {
                    clearTimeout(editorContentAlreadySavedDebounceTimeout);
                    editorContentAlreadySavedDebounceTimeout = setTimeout(() => {
                      if(window.lastModelTextSaved === modelText && window.lastOutputTemplateSaved === outputTemplate) {
                        if(app.userOwnsThisGenerator) {
                          window.setSaveState("saved");
                        }
                      }
                    }, 300);
                  } 

                  let doLocalBackupDebounce6MinTimeout;
                  window.lastEditorsChangeTime = Date.now();
                  this.outputTemplateEditor.on("changes", () => {
                    window.lastEditorsChangeTime = Date.now();
                    window.editsHaveBeenMadeSincePageLoad = true;
                    let modelText = this.modelTextEditor.getValue();
                    let outputTemplate = this.outputTemplateEditor.getValue();
                    app.generatorEditedCallback({modelText, outputTemplate});
                    if(this.outputTemplateEditor.isClean() && this.modelTextEditor.isClean()) { // in case they undo and go back to a saved state
                      if(app.userOwnsThisGenerator) {
                        window.setSaveState("saved"); // must come after generatorEditedCallback, above, since that sets the save state to "unsaved"
                      }
                    } else {
                      checkIfEditorContentAlreadySavedWithDebounce(modelText, outputTemplate);
                    }
                    try {
                      doLocalGeneratorBackupIfNeeded(modelText, outputTemplate);
                      clearTimeout(doLocalBackupDebounce6MinTimeout);
                      doLocalBackupDebounce6MinTimeout = setTimeout(() => { // without this, we can still lose up to 5 minutes of work, which is annoying
                        doLocalGeneratorBackupIfNeeded(modelText, outputTemplate);
                      }, 1000*60*6);
                    } catch(e) { console.error(e); }
                  });
                  this.modelTextEditor.on("changes", () => {
                    window.lastEditorsChangeTime = Date.now();
                    window.editsHaveBeenMadeSincePageLoad = true;
                    let modelText = this.modelTextEditor.getValue();
                    let outputTemplate = this.outputTemplateEditor.getValue();
                    app.generatorEditedCallback({modelText, outputTemplate});
                    if(this.outputTemplateEditor.isClean() && this.modelTextEditor.isClean()) { // in case they undo and go back to a saved state
                      if(app.userOwnsThisGenerator) {
                        window.setSaveState("saved"); // must come after generatorEditedCallback, above, since that sets the save state to "unsaved"
                      }
                    } else {
                      checkIfEditorContentAlreadySavedWithDebounce(modelText, outputTemplate);
                    }
                    try {
                      doLocalGeneratorBackupIfNeeded(modelText, outputTemplate);
                      clearTimeout(doLocalBackupDebounce6MinTimeout);
                      doLocalBackupDebounce6MinTimeout = setTimeout(() => { // without this, we can still lose up to 5 minutes of work, which is annoying
                        doLocalGeneratorBackupIfNeeded(modelText, outputTemplate);
                      }, 1000*60*6);
                    } catch(e) { console.error(e); }
                  });

                  // disable overwrite insert mode:
                  this.outputTemplateEditor.on("keyup", () => { this.outputTemplateEditor.toggleOverwrite(false); });
                  this.modelTextEditor.on("keyup", () => { this.modelTextEditor.toggleOverwrite(false); });

                  this.outputTemplateEditor.on("changes", () => {
                    clearTimeout(this.updateOutputDelayTimeout);
                    if(this.autoReload) {
                      this.updateOutputDelayTimeout = setTimeout(() => {
                        this.updateOutput(false, true);
                      }, 800);
                    }
                  });
                  this.modelTextEditor.on("changes", () => {
                    clearTimeout(this.updateOutputDelayTimeout);
                    if(this.autoReload) {
                      this.updateOutputDelayTimeout = setTimeout(() => {
                        this.updateOutput(false, true);

                        // EDIT: this is commented out because it doesn't make sense - if they haven't reloaded, then the computed meta data is obviously going to be using the old code.
                        // TODO: Make it so that when they send the save request, that's when we send updateMetadataIfNeeded, and we send the updated modelText which is executed with createPerchanceTree or whatever, but is then discarded, rather than replacing the "real" tree and updating everything.
                        // this.outputIframe.contentWindow.postMessage({command:"updateMetadataIfNeeded"}, '*'); // needed because they may not have 'auto' checked, and so they make some changes to their $meta props, then hit save, but the updated $meta stuff isn't sent along with the save request.
                      }, 800);
                    }
                  });

                  // if(window.DEBUG_FREEZE_MODE) {
                  //   this.refs.autoReloadCheckbox.checked = false;
                  //   //this.root.querySelector("#output iframe").src = "https://null.perchance.org/debug-freeze";
                  // } else {
                  //   // let url = "https://null.perchance.org/" + app.data.generator.name + "?v=1";
                  //   // let url = `https://${window.generatorPublicId}.perchance.org/` + app.data.generator.name + "?v=2";
                  //   // if(window.needToBustCacheOfIframeOnInitialLoad) url += `?__cacheBuster=${Math.random()}`;
                  //   let url = new URL(window.location.href);
                  //   url.hostname = `${window.generatorPublicId}.perchance.org`;
                  //   if(window.needToBustCacheOfIframeOnInitialLoad) {
                  //     url.searchParams.set("__cacheBust", Math.random().toString()+Math.random().toString());
                  //   }
                  //   // This is to ensure cache is busted even if they have URL params added to the URL.
                  //   // Cloudflare has limits to the number of cache clears, so this is a hacky but full-proof workaround.
                  //   // The top-level frame has the `window.thisIsNotAnOldCachedPagePromise` stuff to handle this, but we still
                  //   // need to make sure the iframe is also busted.
                  //   // We only need this during initial page load - not when reload button / save button is pressed, since in those
                  //   // cases the __initWithDataFromParentWindow query param is added to the URL, which means the iframe gets fresh
                  //   // data from the parent, regardless of the html that the server sends (i.e. the data that's embedded in the HTML
                  //   // is ignored)
                  //   url.searchParams.set("__generatorLastEditTime", window.generatorLastSaveTime);
                  //   this.root.querySelector("#output iframe").src = url;
                  // }

                  if(window.DEBUG_FREEZE_MODE) {
                    this.refs.autoReloadCheckbox.checked = false;
                    this.root.querySelector("#output iframe").src = "https://null.perchance.org/debug-freeze";
                  } else {
                    // Edit: not needed now that we load the iframe separately from loading the editor.
                    // if(window.needToBustCacheOfIframeOnInitialLoad) {
                    //   let url = new URL(window.location.href);
                    //   url.hostname = `${window.generatorPublicId}.perchance.org`;
                    //   url.searchParams.set("__cacheBust", Math.random().toString()+Math.random().toString());
                    //   this.root.querySelector("#output iframe").src = url;
                    // }
                  }
                  
                  // whenever the codemirror scrollbar appears or dissapears, update the buttons positioning so they sit beside the scrollbar:
                  const updateCodeEditorButtonsPos = () => {
                    document.querySelector(".code-editor-buttons-ctn").style.right = (document.querySelector(".CodeMirror-vscrollbar").offsetWidth+5)+"px";
                  };
                  new MutationObserver(updateCodeEditorButtonsPos).observe(document.querySelector(".CodeMirror-vscrollbar"), {attributes: true});
                  setTimeout(updateCodeEditorButtonsPos, 2000); // <-- call it once at the start for init

                  // same for html area:
                  const updateHtmlEditorButtonsPos = () => {
                    document.querySelector(".toggle-wrap-button-html").style.right = (document.querySelectorAll(".CodeMirror-vscrollbar")[1].offsetWidth+5)+"px";
                  };
                  new MutationObserver(updateHtmlEditorButtonsPos).observe(document.querySelectorAll(".CodeMirror-vscrollbar")[1], {attributes: true});
                  setTimeout(updateHtmlEditorButtonsPos, 2000); // <-- call it once at the start for init


                  // Initialise Split.js panels
                  let sizesStore = new Store('editor-split-sizes');

                  let sizes;
                  let defaultSizes = {
                    ab: [60,40],
                    cd: [90,10],
                    ef: [75,25],
                  };
                  if(sizesStore.data[app.data.generator.name]) {
                    sizes = sizesStore.data[app.data.generator.name];
                  } else {
                    // default sizes
                    sizes = JSON.parse(JSON.stringify(defaultSizes));
                    sizesStore.data[app.data.generator.name] = sizes;
                    sizesStore.save();
                  }
                  if(localStorage["editor-split-sizes-storage"] && localStorage["editor-split-sizes-storage"].length > 20000) {
                    localStorage["editor-split-sizes-storage"] = "{}";
                  }

                  function trimSplitSizes(arr) {
                    // for some reason split sizes sometimes add to over 100% and the editor breaks.
                    // this trims it to 100%
                    arr = arr.slice(0);
                    let sum = arr[0] + arr[1];
                    if(sum <= 100) return arr;
                    else {
                      arr[0] = Math.round(arr[0]);
                      arr[1] = Math.round(arr[1]);
                      while(arr[0] + arr[1] > 100) {
                        if(Math.random() < 0.5) {
                          arr[0]--;
                        } else {
                          arr[1]--;
                        }
                      }
                      return arr;
                    }
                  }

                  let split_ab = Split([this.root.querySelector('#a'), this.root.querySelector('#b')], {
                    gutterSize: 8,
                    sizes: sizes.ab,
                    cursor: 'col-resize',
                    minSize: 30,
                    onDragEnd: function() {
                      let data = sizesStore.data[app.data.generator.name];
                      if(!data) { data = JSON.parse(JSON.stringify(defaultSizes)); } // they could have changed generator's name
                      data.ab = split_ab.getSizes();
                      data.ab = trimSplitSizes(data.ab);
                      sizesStore.save();
                    }
                  });

                  let split_cd = Split([this.root.querySelector('#c'), this.root.querySelector('#perchanceConsoleEl')], {
                    direction: 'vertical',
                    sizes: sizes.cd,
                    gutterSize: 8,
                    cursor: 'row-resize',
                    minSize: 30,
                    onDragEnd: function() {
                      let data = sizesStore.data[app.data.generator.name];
                      if(!data) { data = JSON.parse(JSON.stringify(defaultSizes)); } // they could have changed generator's name
                      data.cd = split_cd.getSizes();
                      data.cd = trimSplitSizes(data.cd);
                      sizesStore.save();
                    }
                  });

                  let split_ef = Split([this.root.querySelector('#e'), this.root.querySelector('#f')], {
                    direction: 'vertical',
                    sizes: sizes.ef,
                    gutterSize: 8,
                    cursor: 'row-resize',
                    minSize: 30,
                    onDragEnd: function() {
                      let data = sizesStore.data[app.data.generator.name];
                      if(!data) { data = JSON.parse(JSON.stringify(defaultSizes)); } // they could have changed generator's name
                      data.ef = split_ef.getSizes();
                      data.ef = trimSplitSizes(data.ef);
                      sizesStore.save();
                    }
                  });

                  // need this because otherwise for some reason there's some weird `overscroll-behaviour` stuff near the edges of the screen (even with touch-action:none overlay workaround).
                  if(window.location.hash !== "#edit") this.root.querySelectorAll("#main .gutter").forEach(el => el.style.display="none");
                  
                  setTimeout(() => {
                    this.modelTextEditor.refresh();
                    this.outputTemplateEditor.refresh();
                  }, 10);


                  //////////////////////
                  //     CONSOLE      //
                  //////////////////////
                  function escapeHtml(html) {
                    return html.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
                  }

                  let consoleOutputStyle = "<style>html { color-scheme: light dark; } body { margin:0; padding:0.3em; color:#068e06; font-family:monospace; } @media (prefers-color-scheme: dark) { body { color-scheme: dark; background-color: #1f2024; } }</style>";

                  let consoleCommandStore = new Store("console-commands");
                  if(!consoleCommandStore.data.generators) { consoleCommandStore.data.generators = {}; }

                  let consoleOutputEl = document.querySelector("#console-output");
                  let consoleInputEl = document.querySelector("#console-input");
                  consoleOutputEl.addEventListener("click", function() {
                    consoleInputEl.focus();
                  });
                  consoleOutputEl.srcdoc = consoleOutputStyle + `<span style='font-family:monospace; color:#6e6d6d; position:absolute;top:8px;left:8px;'>e.g. try typing <b>I rolled a \{1-6\}!</b> or <b>[yourListName]</b> (including the brackets) above, and then press enter.</span>`;

                  let previousCommands = consoleCommandStore.data.generators[app.data.generator.name] || [""];
                  let currCommandIndex = 0; // NOT previousCommands array index!! 0 represents most recent element, 1 is second most recent, etc.
                  consoleInputEl.addEventListener("keydown", async (e) => {
                    // e.preventDefault();
                    let command = consoleInputEl.value;
                    if(e.which === 13) { // enter
                      let optEl = document.querySelector("#consoleInterpreterOption");
                      let interpreter = optEl.options[optEl.selectedIndex].value;

                      if(interpreter === "plaintext") { consoleOutputEl.srcdoc = consoleOutputStyle.replace("font-family:monospace;", "font-family:monospace; white-space:pre-wrap;")+(escapeHtml(await this.evaluateText(command))); }
                      else if(interpreter === "html") { consoleOutputEl.srcdoc = consoleOutputStyle+(await this.evaluateText(command)); }

                      // this.root.style.background = "#f1f1f1";
                      // setTimeout(() => { this.root.style.background = "white"; },100)

                      let prevCommand = previousCommands[previousCommands.length-1];
                      if(prevCommand !== command) {
                        previousCommands.pop();
                        previousCommands.push(command);
                        previousCommands.push(""); // put the empty one back on the end
                      }
                      if(previousCommands.length > 1000) {
                        previousCommands.shift();
                      }
                      currCommandIndex = 1; // <-- set index to that of the command that was just submitted
                      consoleCommandStore.data.generators[app.data.generator.name] = previousCommands; // (not necessary because it keeps reference??)
                      consoleCommandStore.save();
                      return;
                    }
                    if(e.which === 38) { // up arrow
                      currCommandIndex++; // move towards start of array (counter-intuitive)
                      currCommandIndex = Math.min(previousCommands.length-1, currCommandIndex); // must stay below or at length of command stack
                      consoleInputEl.value = previousCommands[previousCommands.length-1-currCommandIndex];
                      setTimeout(function() { consoleInputEl.selectionStart = consoleInputEl.selectionEnd = 100000; }, 1);
                      return;
                    }
                    if(e.which === 40) { // down arrow
                      currCommandIndex--; // move towards end off array (counter-intuitive)
                      currCommandIndex = Math.max(0, currCommandIndex); // must stay above or at zero
                      consoleInputEl.value = previousCommands[previousCommands.length-1-currCommandIndex];
                      setTimeout(function() { consoleInputEl.selectionStart = consoleInputEl.selectionEnd = 100000; }, 1);
                      return;
                    }
                  });
                },
              };
            </script>
          </div>

          <script>
            async function getCaptchaToken(widgetCtn) {

              let createdWidgetCtn;
              if(!widgetCtn) {
                // No widget container provided, so we create one.
                // Centered on screen with darkened background:
                createdWidgetCtn = document.createElement("div");
                createdWidgetCtn.className = "created-captcha-widget-ctn"; // <-- don't change this without changing references to it
                createdWidgetCtn.style.cssText = "text-align:center; position:fixed; top:50%; left:50%; transform:translate(-50%,-50%); z-index:1000; background-color:#111; padding:1em; border-radius:0.5em; box-shadow:0 0 10px rgba(0,0,0,0.5);";
                createdWidgetCtn.innerHTML = `<div style="margin-bottom:0.5rem;">⏳ Just a sec...</div>`;
                document.body.appendChild(createdWidgetCtn);
              }

              // load captcha lib if needed:
              let attempts = 0;
              while(!window.turnstile) {
                window.onloadTurnstilePromise = new Promise(r => window.onloadTurnstileCallback=r);
                let script = document.createElement("script");
                script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback";
                script.defer = true;
                script.onerror = (e) => {
                  console.error("Cloudflare Turnstile is being blocked?", e)
                };
                document.body.appendChild(script);
                await window.onloadTurnstilePromise;
                if(!window.turnstile) await new Promise(r => setTimeout(r, 10*1000));
                attempts++;
                if(attempts > 3) {
                  console.error("Failed to load Cloudflare Turnstile after 3 attempts.");
                  break;
                }
              }
              // render captcha and get token:
              let captchaWidgetId;
              let token = await new Promise((resolve, reject) => {
                captchaWidgetId = window.turnstile.render(widgetCtn || createdWidgetCtn, {
                  sitekey: '0x4AAAAAAAi3LdM-EVMMMFCv',
                  callback: function(token) {
                    console.debug("✅ Got turnstile token:", token);
                    resolve(token);
                  },
                  "error-callback": function(...args) {
                    console.error("❌ Turnstile error callback fired:", ...args);
                    reject(null);
                  },
                });
              });
              try { window.turnstile.remove(captchaWidgetId); } catch(e) { console.error("turnstile.remove() failed:", e); }
              if(createdWidgetCtn) createdWidgetCtn.remove();
              return token;
            }
          </script>
        
          <div id="loginModalEl" class="app-modal" style="display:none;">
            <div style="z-index:50;" class="background" onmousedown="loginModal.closeModal()"></div>
            <div style="z-index:51;" class="outer-wrapper" onmousedown="loginModal.handleOuterWrapperClick(event)">
              <div data-ref="contentWrapper" class="content-wrapper">
                <!-- LOGIN FORM -->
                <div class="view" data-ref="loginView" style="display: initial;">
                  <div class="modal-header">
                    <p>sign up or login to create your own generators ᕕ(ᐛ)ᕗ we <b>only</b> email you at your request (e.g. for <span class="link" onclick="loginModal.goto('passwordReset1View')">password reset</span>) 👍︎</p>
                    <p data-ref="loginError_request" style="display:none; color:red; margin-top:1em;">there was a problem connecting to the server ¯\_(⊙_ʖ⊙)_/¯ check your internet connection?</p>
                    <p data-ref="loginError_password" style="display:none; color:red; margin-top:1em;">that password is not correct (⊙.☉)7 <span class="link" onclick="loginModal.goto('passwordReset1View')">forgot it?</span></p>
                    <p data-ref="loginError_passwordLength" style="display:none; color:red; margin-top:1em;">that password is too short (⌐■_■) &gt;6 chars plz</p>
                    <p data-ref="loginError_emailInvalid" style="display:none; color:red; margin-top:1em;">something seems wrong about that email address (⊙︿⊙)</p>
                    <p data-ref="loginError_tooManyRequests" style="display:none; color:red; margin-top:1em;">too many requests (this can sometimes be caused by VPN browser extensions)</p>
                    <p data-ref="loginError_server" style="display:none; color:red; margin-top:1em;">something went wrong on the server (✖╭╮✖) <a href="https://lemmy.world/c/perchance" target="_blank">plz post to forum</a> if problem persists</p>
                  </div>
                  <div class="modal-body">
                    <input name="username" data-ref="email" placeholder="email" type="email">
                    <input name="password" data-ref="password" placeholder="password" type="password">
                  </div>
                  <div class="modal-footer">
                    <button onclick="loginModal.closeModal()">cancel</button><button onclick="loginModal.login()" class="main">signup / login</button>
                  </div>
                </div>
                <!-- LOADING -->
                <div class="view" data-ref="loadingView" style="display: none;">
                  <!-- <emoji-ajax-spinner style="display:block; margin:5em 0;" verb="loading"/> -->
                  <div style="margin:5em 0; text-align:center;">⏳ loading...</div>
                  <div data-ref="cfCaptchaOuterEl" style="display:none; text-align:center;">
                    <div data-ref="cfCaptchaCtn"></div>
                  </div>
                </div>
                <!-- VERIFICATION -->
                <div class="view" data-ref="verificationView" style="display: none;">
                  <div class="modal-header">
                    <p>we just sent a verification code to <span class="verificationEmailDisplay" style="font-style:italic;"></span> (check spam folder too)</p>
                    <p data-ref="extraNotice" style="margin-top: 0.25rem;" hidden=""></p>
                    <p data-ref="verifyError_request" style="display:none; color:red; margin-top:1em;">hmm. there was a problem connecting to the server. check your internet connection?</p>
                    <p data-ref="verifyError_code" style="display:none; color:red; margin-top:1em;">that code is not correct (⊙.☉)7 pls ensure you copied it exactly 👌︎</p>
                    <p data-ref="verifyError_server" style="display:none; color:red; margin-top:1em;">something went wrong on the server (✖╭╮✖) <a href="https://lemmy.world/c/perchance" target="_blank">pls post to forum</a> if problem persists</p>
                  </div>
                  <div class="modal-body">
                    <input data-ref="verificationCode" placeholder="verification code" type="text">
                  </div>
                  <div class="modal-footer">
                    <button onclick="loginModal.goto('loginView')">back</button><button id="verify-account-button" class="main" onclick="loginModal.verify()">verify</button>
                  </div>
                </div>
                <!-- SUCCESSFUL LOGIN -->
                <div class="view" data-ref="successView" style="display: none;">
                  <div class="modal-header">
                    <p>you're logged in! s(^‿^)-b</p>
                  </div>
                  <button onclick="loginModal.closeModal_success()" style="width:100%;">close</button>
                </div>
                <!-- PASSWORD RESET REQUEST -->
                <div class="view" data-ref="passwordReset1View" style="display: none;">
                  <div class="modal-header">
                    <p>forgot your password? o(╥﹏╥)o that's all right! just enter the email you used to sign up and you'll be sent a reset code</p>
                    <p data-ref="requestPasswordResetCodeError_request" style="display:none; color:red; margin-top:1em;">there was a problem connecting to the server ¯\_(⊙_ʖ⊙)_/¯ check your internet connection?</p>
                    <p data-ref="requestPasswordResetCodeError_account" style="display:none; color:red; margin-top:1em;">very spooky: that account was not found (⊙.☉)7 are you sure you put in the correct email? 👻︎</p>
                    <p data-ref="requestPasswordResetCodeError_server" style="display:none; color:red; margin-top:1em;">something went wrong on the server (✖╭╮✖) <a href="https://lemmy.world/c/perchance" target="_blank">pls post to forum</a> if problem persists</p>
                  </div>
                  <div class="modal-body">
                    <input data-ref="emailForPasswordReset" placeholder="email" type="text">
                  </div>
                  <div class="modal-footer">
                    <button onclick="loginModal.goto('loginView')">back</button><button class="main" onclick="loginModal.requestPasswordResetCode()">request reset code</button>
                  </div>
                </div>
                <!-- PASSWORD RESET -->
                <div class="view" data-ref="passwordReset2View" style="display: none;">
                  <div class="modal-header">
                    <p>we just sent a password reset code to <span style="font-style:italic;" class="emailForPasswordResetDisplay"></span> 💌 when you recieve it (check your spam folder too) enter it below and then enter a new password of your choosing</p>
                    <p data-ref="resetPasswordError_request" style="display:none; color:red; margin-top:1em;">there was a problem connecting to the server ¯\_(⊙_ʖ⊙)_/¯ check your internet connection?</p>
                    <p data-ref="resetPasswordError_code" style="display:none; color:red; margin-top:1em;">that code is not correct (⊙.☉)7 pls ensure you copied it exactly 👌︎</p>
                    <p data-ref="resetPasswordError_passwordLength" style="display:none; color:red; margin-top:1em;">that password is too short (⌐■_■) &gt;6 chars plz</p>
                    <p data-ref="resetPasswordError_server" style="display:none; color:red; margin-top:1em;">something went wrong on the server (✖╭╮✖) <a href="https://lemmy.world/c/perchance" target="_blank">pls post to forum</a> if problem persists</p>
                  </div>
                  <div class="modal-body">
                    <input data-ref="passwordResetCode" placeholder="reset code" type="text">
                    <input data-ref="newPassword" placeholder="new password" type="password">
                  </div>
                  <div class="modal-footer">
                    <button onclick="loginModal.goto('passwordReset1View')">back</button><button class="main" onclick="loginModal.resetPassword()">set password</button>
                  </div>
                </div>
              </div>
            </div>
            <script>
              window.loginModal = {
                root: document.querySelector("#loginModalEl"),
                refs: new Proxy({}, {
                  get(target, prop, receiver) {
                    return document.querySelector(`#loginModalEl [data-ref='${prop}']`);
                  },
                }),
                init: function() {
                  this.closeModal = function() {
                    this.root.style.display = "none";
                  };
                  this.closeModal_success = function() {
                    this.closeModal();
                    this.goto("loginView");
                  };

                  this.openModal = function() {
                    this.root.style.display = "block";
                  };
                  this.handleOuterWrapperClick = function(e) {
                    if(!this.refs.contentWrapper.contains(e.target)) {
                      this.closeModal();
                    }
                  };
                  this.goto = function(name) {
                    this.hideAllViews();
                    let el = this.refs[name];
                    el.style.display = "initial";
                    loginModalEl.querySelectorAll(".emailForPasswordResetDisplay").forEach(el => el.textContent=this.refs.emailForPasswordReset.value);
                    loginModalEl.querySelectorAll(".verificationEmailDisplay").forEach(el => el.textContent=this.refs.email.value);
                    if(el.querySelector("input")) {
                      // TODO: move caret to end of text if there is text in the input
                      setTimeout(() => { el.querySelector("input").focus(); },200);
                    }
                  };
                  this.hideAllViews = function() {
                    let views = this.root.querySelectorAll(".content-wrapper .view");
                    for(let el of Array.from(views)) {
                      el.style.display = "none";
                    }
                  };
                  this.getCurrentView = function() {
                    let views = this.root.querySelectorAll(".content-wrapper .view");
                    for(let el of Array.from(views)) {
                      if( el.style.display !== "none" ) return el.attributes.ref.value;
                    }
                  };

                  let extraNoticeDisplayTimeout = null;
                  this.login = async function() {

                    if(this.refs.password.value.length < 6) {
                      this.setLoginError("passwordLength");
                      return;
                    }
                    if(!/.+@.+/.test(this.refs.email.value)) {
                      this.setLoginError("emailInvalid");
                      return;
                    }

                    this.goto("loadingView");

                    try {

                      let bodyData = {email: this.refs.email.value, password: this.refs.password.value};

                      let data = await fetch('/api/login', {
                        method: 'POST',
                        body: JSON.stringify(bodyData),
                        signal: AbortSignal.timeout?.(20*1000),
                        headers: { 'Content-Type': 'application/json'},
                      }).then(r => r.json());

                      if(data.status === "captcha-needed") {
                        console.debug("captcha-needed:", data);
                        // get captcha token:
                        this.refs.cfCaptchaOuterEl.style.display = "";
                        this.refs.cfCaptchaCtn.innerHTML = "";
                        let captchaToken = await getCaptchaToken(this.refs.cfCaptchaCtn);
                        this.refs.cfCaptchaOuterEl.style.display = "none";
                        // add the token to the request and try again:
                        bodyData.captchaToken = captchaToken;
                        data = await fetch('/api/login', {
                          method: 'POST',
                          body: JSON.stringify(bodyData),
                          signal: AbortSignal.timeout?.(20*1000),
                          headers: { 'Content-Type': 'application/json'},
                        }).then(r => r.json());
                      }

                      console.debug("login finished:", data);

                      if(data.status === "verification-needed") {
                        this.goto("verificationView");

                        if(bodyData.email.trim().endsWith("@gmail.com")) {
                          extraNoticeDisplayTimeout = setTimeout(() => {
                            this.refs.extraNotice.hidden = false;
                            let aaaaa = "@";
                            this.refs.extraNotice.innerHTML = `<b style="color:red;">note</b>: gmail addresses seem to be having signup issues recently. sometimes the verification email doesn't even make it to the spam folder. if you're not getting an email (even in your spam folder) after a couple of minutes, try <b>sending an email</b> to supp${""}ort${aaaaa}per${"chan"}ce.org (content doesn't matter), and then click 'back' and try again. if it's still not working, you can use something like temp-ma${""}il.org until the issue is resolved, then change your email in account settings.`;
                          }, 1000*40);
                        }

                      } else if(data.status === "success") {
                        app.loginSuccessCallback({sessionToken:data.sessionToken, email:this.refs.email.value});
                        localStorage.sessionTokenLastChangeTime = Date.now();
                        // TODO: make sure modal is visible (they could have (accidentally) closed it! (disable closing while loading?))
                        this.goto("successView");
                        this.refs.email.value = "";
                        this.refs.password.value = "";
                        this.setLoginError("none");
                      } else if(data.status === "incorrect-password") {
                        this.goto("loginView");
                        this.setLoginError("password");
                      } else if(data.status === "invalid-email") {
                        this.goto("loginView");
                        this.setLoginError("emailInvalid");
                      } else if(data.status === "too-many-requests") {
                        this.goto("loginView");
                        this.setLoginError("tooManyRequests");
                      } else if(data.status === "captcha-failed") {
                        this.goto("loginView");
                        this.refs.cfCaptchaCtn.innerHTML = `<b style="color:red;">Failed to solve captcha.</b>`;
                      } else {
                        this.goto("loginView");
                        this.setLoginError("server");
                      }

                    } catch(e) {
                      console.debug(e);
                      this.goto("loginView");
                      this.setLoginError("request");
                    }
                  }
                  this.setLoginError = (name) => {
                    this.refs.loginError_password.style.display = "none";
                    this.refs.loginError_passwordLength.style.display = "none";
                    this.refs.loginError_emailInvalid.style.display = "none";
                    this.refs.loginError_tooManyRequests.style.display = "none";
                    this.refs.loginError_request.style.display = "none";
                    this.refs.loginError_server.style.display = "none";
                    if(name === "none") {
                      return;
                    } else {
                      this.refs["loginError_"+name].style.display = "block";
                    }
                  }

                  this.verify = async function() {
                    this.goto("loadingView");
                    try {
                      let data = await fetch('/api/verify', {
                        method: 'POST',
                        body: JSON.stringify({code: this.refs.verificationCode.value, email:this.refs.email.value}),
                        signal: AbortSignal.timeout?.(20*1000),
                        headers: { 'Content-Type': 'application/json'},
                      }).then(r => r.json());

                      console.debug("verify finished:", data);

                      if(data.status === "success") {
                        clearTimeout(extraNoticeDisplayTimeout);
                        this.refs.extraNotice.hidden = true;
                        this.setVerifyError("none");
                        this.login();
                        this.refs.verificationCode.value = "";
                        //this.refs.email.value = ""; If we set it to an empty string we won't be able to read it after login!!
                      } else if(data.status === "incorrect-code") {
                        this.setVerifyError("code");
                        this.goto("verificationView");
                      } else {
                        this.setVerifyError("server");
                        this.goto("verificationView");
                      }
                    } catch(e) {
                      console.debug(e);
                      this.goto("verificationView");
                      this.setVerifyError("request");
                    }
                  }
                  this.setVerifyError = (name) => {
                    this.refs.verifyError_code.style.display = "none";
                    this.refs.verifyError_request.style.display = "none";
                    this.refs.verifyError_server.style.display = "none";
                    if(name === "none") {
                      return;
                    } else {
                      this.refs["verifyError_"+name].style.display = "block";
                    }
                  }

                  this.requestPasswordResetCode = async function() {

                    this.goto("loadingView");
                    try {

                      let bodyData = {email:this.refs.emailForPasswordReset.value};

                      let data = await fetch('/api/requestPasswordResetCode', {
                        method: 'POST',
                        body: JSON.stringify(bodyData),
                        signal: AbortSignal.timeout?.(20*1000),
                        headers: { 'Content-Type': 'application/json'},
                      }).then(r => r.json());

                      if(data.status === "captcha-needed") {
                        console.debug("captcha-needed:", data);
                        // get captcha token:
                        this.refs.cfCaptchaOuterEl.style.display = "";
                        this.refs.cfCaptchaCtn.innerHTML = "";
                        let captchaToken = await getCaptchaToken(this.refs.cfCaptchaCtn);
                        this.refs.cfCaptchaOuterEl.style.display = "none";
                        // add the token to the request and try again:
                        bodyData.captchaToken = captchaToken;
                        data = await fetch('/api/requestPasswordResetCode', {
                          method: 'POST',
                          body: JSON.stringify(bodyData),
                          signal: AbortSignal.timeout?.(20*1000),
                          headers: { 'Content-Type': 'application/json'},
                        }).then(r => r.json());
                      }

                      console.debug(data);

                      if(data.status === "success") {
                        this.setRequestPasswordResetCodeError("none");
                        this.goto("passwordReset2View");
                      } else if(data.status === "account-not-found") {
                        this.setRequestPasswordResetCodeError("account");
                        this.goto("passwordReset1View");
                      } else if(data.status === "captcha-failed") {
                        alert(`Failed to solve captcha. Please try again.`);
                        this.goto("passwordReset1View");
                      } else {
                        this.setRequestPasswordResetCodeError("server");
                        this.goto("passwordReset1View");
                      }

                    } catch(e) {
                      console.debug(e);
                      this.goto("passwordReset1View");
                      this.setRequestPasswordResetCodeError("request");
                    }
                  }
                  this.setRequestPasswordResetCodeError = function(name) {
                    this.refs.requestPasswordResetCodeError_account.style.display = "none";
                    this.refs.requestPasswordResetCodeError_request.style.display = "none";
                    this.refs.requestPasswordResetCodeError_server.style.display = "none";
                    if(name === "none") {
                      return;
                    } else {
                      this.refs["requestPasswordResetCodeError_"+name].style.display = "block";
                    }
                  }

                  this.resetPassword = async function() {

                    if(this.refs.newPassword.length < 6) {
                      this.setResetPasswordError("passwordLength");
                      this.goto("passwordReset2View");
                      return;
                    }

                    this.goto("loadingView");
                    try {

                      let data = await fetch('/api/resetPassword', {
                        method: 'POST',
                        body: JSON.stringify({email:this.refs.emailForPasswordReset.value, code:this.refs.passwordResetCode.value, newPassword:this.refs.newPassword.value}),
                        signal: AbortSignal.timeout?.(20*1000),
                        headers: { 'Content-Type': 'application/json'},
                      }).then(r => r.json());

                      console.debug(data);

                      if(data.status === "success") {
                        this.setResetPasswordError("none");
                        this.refs.email.value = this.refs.emailForPasswordReset.value;
                        this.refs.password.value = this.refs.newPassword.value;
                        this.login();
                        this.refs.emailForPasswordReset.value = "";
                        this.refs.passwordResetCode.value = "";
                        this.refs.newPassword.value = "";
                      } else if(data.status === "incorrect-code") {
                        this.setResetPasswordError("code");
                        this.goto("passwordReset2View");
                      } else {
                        this.setResetPasswordError("server");
                        this.goto("passwordReset2View");
                      }

                    } catch(e) {
                      console.debug(e);
                      this.goto("passwordReset2View");
                      this.setResetPasswordError("request");
                    }
                  }
                  this.setResetPasswordError = (name) => {
                    this.refs.resetPasswordError_passwordLength.style.display = "none";
                    this.refs.resetPasswordError_code.style.display = "none";
                    this.refs.resetPasswordError_request.style.display = "none";
                    this.refs.resetPasswordError_server.style.display = "none";
                    if(name === "none") {
                      return;
                    } else {
                      this.refs["resetPasswordError_"+name].style.display = "block";
                    }
                  }

                  this.hideAllViews();
                  this.goto("loginView");
                  this.refs.email.addEventListener("keyup", (e) => { if(e.which === 13) { this.login(); } })
                  this.refs.password.addEventListener("keyup", (e) => { if(e.which === 13) { this.login(); } })
                },
              };
            </script>
          </div>
        
          <div id="accountModalEl" class="app-modal" style="display:none;">
            <style>
              #accountModalEl .add-to-folder-btn {
                opacity:0.5;
                cursor:pointer;
              }
              #accountModalEl .add-to-folder-btn:hover {
                opacity:1;
              }
              #accountModalEl .folder-header {
                cursor:pointer;
                padding-left: 0.5rem;
              }
              #accountModalEl .folder-header:hover {
                background: #eee;
              }
              @media (prefers-color-scheme: dark) {
                #accountModalEl .folder-header:hover {
                  background: #484848;
                }
              }
              
              #accountModalEl .folder-ctn[data-fold-state='closed'] .folder-emoji {
                filter: saturate(0);
              }
            </style>
            <div style="z-index:50;" class="background" onmousedown="accountModal.closeModal()"></div>
            <div style="z-index:51;" class="outer-wrapper" onmousedown="accountModal.handleOuterWrapperClick(event)">
              <div data-ref="contentWrapper" class="content-wrapper">
                <!-- ACCOUNT MENU -->
                <div class="view" data-ref="accountMenuView" style="display: initial;">
                  <div class="modal-header">
                    <p>you're logged in as <i class="currentEmailDisplay"></i> - you can:</p>
                    <ul>
                      <li><span class="link" onclick="accountModal.loadGeneratorList()">view your generators</span></li>
                      <li style="margin-top:0.5rem;"><span class="link" onclick="accountModal.goto('passwordChangeView')">change your password</span></li>
                      <li style="margin-top:0.5rem;"><span class="link" onclick="accountModal.goto('emailChangeView')">change your email</span></li>
                      <li style="margin-top:0.5rem;"><span class="link" onclick="app.logout()">logout</span></li>
                    </ul>
                  </div>
                  <div class="modal-body"></div>
                  <div class="modal-footer">
                    <button style="width:100%;" onclick="accountModal.closeModal()">close</button>
                  </div>
                </div>
                <!-- PASSWORD CHANGE -->
                <div class="view" data-ref="passwordChangeView" style="display: none;">
                  <div class="modal-header">
                    <p>changing your password is easy, just enter your current password and your new password:</p>
                    <p data-ref="passwordChangeError_request" style="display:none; color:red; margin-top:1em;">there was a problem connecting to the server ¯\_(⊙_ʖ⊙)_/¯ check your internet connection?</p>
                    <p data-ref="passwordChangeError_password" style="display:none; color:red; margin-top:1em;">the current password is not correct (⊙.☉)7</p>
                    <p data-ref="passwordChangeError_passwordLength" style="display:none; color:red; margin-top:1em;">that password is too short (⌐■_■) &gt;7 chars pls</p>
                    <p data-ref="passwordChangeError_server" style="display:none; color:red; margin-top:1em;">something went wrong on the server (✖╭╮✖) <a href="https://lemmy.world/c/perchance" target="_blank">pls post to forum</a> if problem persists</p>
                  </div>
                  <div class="modal-body">
                    <input data-ref="passwordChange_currentPassword" placeholder="current password" type="password">
                    <input data-ref="passwordChange_newPassword" placeholder="new password" type="password">
                  </div>
                  <div class="modal-footer">
                    <button onclick="accountModal.goto('accountMenuView')">back</button><button class="main" onclick="accountModal.changePassword()">set new password</button>
                  </div>
                </div>
                <!-- PASSWORD CHANGE SUCCESS -->
                <div class="view" data-ref="passwordChangeSuccessView" style="display: none;">
                  <div class="modal-header">
                    <p>your password has been changed!</p>
                  </div>
                  <div class="modal-body">
                  </div>
                  <div class="modal-footer">
                    <button style="width:100%;" onclick="accountModal.closeModal()">close</button>
                  </div>
                </div>
                <!-- EMAIL CHANGE -->
                <div class="view" data-ref="emailChangeView" style="display: none;">
                  <div class="modal-header">
                    <p>your current email is <i class="currentEmailDisplay"></i> to change your email, just enter your password and your new email below</p>
                    <p data-ref="emailChangeError_request" style="display:none; color:red; margin-top:1em;">there was a problem connecting to the server ¯\_(⊙_ʖ⊙)_/¯ check your internet connection?</p>
                    <p data-ref="emailChangeError_password" style="display:none; color:red; margin-top:1em;">that password is not correct (⊙.☉)7</p>
                    <p data-ref="emailChangeError_emailInvalid" style="display:none; color:red; margin-top:1em;">that new email address looks weird (⌐■_■)</p>
                    <p data-ref="emailChangeError_server" style="display:none; color:red; margin-top:1em;">something went wrong on the server (✖╭╮✖) <a href="https://lemmy.world/c/perchance" target="_blank">plz post to forum</a> if problem persists</p>
                  </div>
                  <div class="modal-body">
                    <input data-ref="emailChange_newEmail" placeholder="new email" type="email">
                    <input data-ref="emailChange_password" placeholder="your password" type="password">
                  </div>
                  <div class="modal-footer">
                    <button onclick="accountModal.goto('accountMenuView')">back</button><button class="main" onclick="accountModal.changeEmail()">set new email</button>
                  </div>
                </div> 
                <!-- EMAIL CHANGE SUCCESS -->
                <div class="view" data-ref="emailChangeSuccessView" style="display: none;">
                  <div class="modal-header">
                    <p>your email hand been changed!</p>
                  </div>
                  <div class="modal-body">
                  </div>
                  <div class="modal-footer">
                    <button style="width:100%;" onclick="accountModal.closeModal()">close</button>
                  </div>
                </div>
                <!-- GENERATOR LIST -->
                <div class="view" data-ref="generatorListView" style="display: none;">
                  <div class="modal-header">
                    <input oninput="this.parentNode.parentNode.querySelectorAll('ul li a').forEach(el => el.parentNode.style.display=el.textContent.includes(this.value) ? '' : 'none')" placeholder="search..." style="height: 1rem;">
                    <span title="sort alphabetically" style="cursor:pointer;" onclick="window.accountModal.sortGeneratorListButtonClickHandler(this)">🔤</span>
                    <p data-ref="generatorListError" style="display:none; color:red; margin-top:1em;">there was a problem loading your generators ¯\_(⊙_ʖ⊙)_/¯ check your internet connection? if the problem persists, please <a href="https://lemmy.world/c/perchance">post to forum</a></p>
                  </div>
                  <div class="modal-body" style="max-height: 400px; overflow-y: auto;">
                    <!-- <generator-list style="display:block; margin-bottom:1.3em;"/> -->
                    <div data-ref="generatorFoldersCtn" style="display:block; margin-bottom:1.3em;"></div>
                  </div>
                  <div class="modal-footer">
                    <button style="width:100%;" onclick="accountModal.goto('accountMenuView')">back</button>
                  </div>
                </div>
                <!-- LOADING -->
                <div class="view" data-ref="loadingView" style="display: none;">
                  <!-- <emoji-ajax-spinner style="display:block; margin:5em 0;" verb="loading"/> -->
                  <div style="margin:5em 0; text-align:center;">⏳ loading...</div>
                </div>
              </div>
            </div>
            <script>
              window.accountModal = {
                root: document.querySelector("#accountModalEl"),
                refs: new Proxy({}, {
                  get(target, prop, receiver) {
                    return document.querySelector(`#accountModalEl [data-ref='${prop}']`);
                  },
                }),
                init: function() {

                  this.sortGeneratorListButtonClickHandler = function(el) {
                    let lists = el.parentNode.parentNode.querySelectorAll('ul');
                    let t = Number(el.dataset.sortTypeIndex || 1);
                    for(let list of lists) {
                      if(t == 0) {
                        [...list.children].sort((a,b)=> Number(a.dataset.defaultSortValue)-Number(b.dataset.defaultSortValue)).forEach(n => list.appendChild(n));
                      } else if(t == 1 || t == 2) {
                        let m;
                        if(t == 1) m = 1;
                        if(t == 2) m = -1;
                        [...list.children].sort((a,b)=> a.textContent > b.textContent ? 1*m : -1*m).forEach(n => list.appendChild(n));
                      }
                    }
                    el.dataset.sortTypeIndex = t+1 === 3 ? 0 : t+1;
                  };

                  this.closeModal = function() {
                    this.root.style.display = "none";
                  }
                  this.openModal = function() {
                    this.goto("accountMenuView");
                    this.root.style.display = "block";
                  }
                  this.handleOuterWrapperClick = function(e) {
                    if(!this.refs.contentWrapper.contains(e.target)) {
                      this.closeModal();
                    }
                  }
                  this.goto = function(name) {
                    this.hideAllViews();
                    let el = this.refs[name];
                    el.style.display = "initial";
                    accountModalEl.querySelectorAll(".currentEmailDisplay").forEach(el => el.textContent=app.store.data.user.email);
                    if(el.querySelector("input")) {
                      // TODO: move caret to end of text if there is text in the input
                      setTimeout(() => { el.querySelector("input").focus(); },200);
                    }
                  }
                  this.hideAllViews = function() {
                    let views = this.root.querySelectorAll(".content-wrapper .view");
                    for(let el of Array.from(views)) {
                      el.style.display = "none";
                    }
                  }
                  this.getCurrentView = function() {
                    let views = this.root.querySelectorAll(".content-wrapper .view");
                    for(let el of Array.from(views)) {
                      if( el.style.display !== "none" ) return el.attributes.ref.value;
                    }
                  };

                  try {
                    window.__userGeneratorListFolderFoldStates = JSON.parse(localStorage.userGeneratorListFolderFoldStates || `{"uncategorized":1}`);
                    if(typeof window.__userGeneratorListFolderFoldStates !== "object") throw new Error("Should be a POJO.");
                  } catch(e) {
                    console.error(e);
                    window.__userGeneratorListFolderFoldStates = {uncategorized:1}; // <-- important - otherwise a bug with writing to localStorage.userGeneratorListFolderFoldStates could break the whole web app for the user
                  }

                  this.generatorFolderMap = null;
                  this.lastChosenFolderName = "";
                  this.handleGeneratorFolderMapButtonClick = async (btn) => {
                    let folderName = prompt('Enter a folder name:', this.lastChosenFolderName || undefined);
                    if(folderName !== null) {
                      this.lastChosenFolderName = folderName; // just for convenience if adding a bunch of generators to a new folder
                      if(folderName === "") {
                        folderName = "uncategorized";
                      }
                      let existingFolderName = this.generatorFolderMap[btn.dataset.generatorName];

                      // update local data and view:
                      this.generatorFolderMap[btn.dataset.generatorName] = folderName;
                      let folderEl = this.refs.generatorFoldersCtn.querySelector(`[data-folder-name='${folderName}']`);
                      if(!folderEl) { // create the folder if it doesn't yet exist
                        folderEl = this.createFolderHtml([], folderName, "open");
                        this.refs.generatorFoldersCtn.prepend(folderEl);
                      }
                      folderEl.querySelector("ul").prepend(btn.closest("li"));
                      
                      let itemCountEl = folderEl.querySelector(".folder-item-count");
                      itemCountEl.innerText = Number(itemCountEl.innerText) + 1;

                      // if the generator was previously in a folder, and that folder is now empty, remove that empty folder:
                      if(existingFolderName) {
                        let existingFolderEl = this.refs.generatorFoldersCtn.querySelector(`[data-folder-name='${existingFolderName}']`);
                        let itemCountEl = existingFolderEl.querySelector(".folder-item-count");
                        itemCountEl.innerText = Number(itemCountEl.innerText) - 1;
                        if(existingFolderEl.querySelectorAll("ul li").length === 0) {
                          existingFolderEl.remove();
                        }
                      }
                      
                      // update server data:
                      let data = await fetch('/api/saveUserGeneratorFolderMap', {
                        method: 'POST',
                        body: JSON.stringify({
                          email: app.store.data.user.email,
                          sessionToken: app.store.data.user.sessionToken,
                          generatorFolderMap: JSON.stringify(this.generatorFolderMap),
                        }),
                        signal: AbortSignal.timeout?.(20*1000),
                        headers: { 'Content-Type': 'application/json'},
                      }).then(r => r.json());

                      if(data.status !== "success") {
                        alert("Encountered error while trying to save folder data. Please check your internet connection and perhaps try refreshing the page. If the problem persists, please submit a post on lemmy.world/c/perchance");
                      }
                    }
                  };

                  this.loadGeneratorList = async () => {

                    this.goto("loadingView");
                    this.refs.generatorListError.style.display = "none";
                    this.refs.generatorFoldersCtn.innerHTML = "";

                    try {

                      let data = await fetch('/api/getGeneratorsByUser', {
                        method: 'POST',
                        body: JSON.stringify({
                          email: app.store.data.user.email,
                          sessionToken: app.store.data.user.sessionToken,
                        }),
                        signal: AbortSignal.timeout?.(20*1000),
                        headers: { 'Content-Type': 'application/json'},
                      }).then(r => r.json());

                      this.goto("generatorListView");

                      if(data.status === "success") {
                        this.generatorFolderMap = data.generatorFolderMap;

                        // clean the generatorFolderMap in case they've deleted generators:
                        let generatorNameSet = new Set(data.generators.map(g => g.name));
                        for(let n of Object.keys(this.generatorFolderMap)) {
                          if(!generatorNameSet.has(n)) delete this.generatorFolderMap[n];
                        }

                        let folders = {}; // <-- maps folder name to generator array
                        for(let g of data.generators) {
                          if(!g.views) g.views = 0;
                          g.views = g.views > 999 ? (g.views/1000).toFixed(1) + 'k' : g.views;
                          let folderName = this.generatorFolderMap[g.name] || "uncategorized";
                          if(!folders[folderName]) folders[folderName] = [];
                          folders[folderName].push(g);
                        }
                        
                        let foldersSorted = Object.entries(folders).sort((a,b) => a[0].localeCompare(b[0]));
                        let uncategorizedFolderEntry = foldersSorted.find(e => e[0] === "uncategorized");
                        if(uncategorizedFolderEntry) {
                          foldersSorted = [...foldersSorted.filter(e => e[0] !== "uncategorized"), uncategorizedFolderEntry]; // move 'uncategorized' folder to the end
                        }
                        for(let [folderName, generators] of foldersSorted) {
                          let foldState = window.__userGeneratorListFolderFoldStates[folderName] ? "open" : "closed";
                          let folderEl = this.createFolderHtml(generators, folderName, foldState);
                          this.refs.generatorFoldersCtn.appendChild(folderEl);
                        }
                        // this.tags["generator-list"].generators = data.generators;
                        // this.tags["generator-list"].update();
                      } else {
                        this.refs.generatorListError.style.display = "block";
                      }

                    } catch(e) {
                      console.error("Problem loading generator list");
                      console.debug(e);
                      this.goto("generatorListView");
                      this.refs.generatorListError.style.display = "block";
                    }

                  };

                  this.createFolderHtml = (generators, folderName, foldState) => {
                    let div = document.createElement("div");
                    
                    let folderNameHtml = folderName.replace(/</g, "&lt;").replace(/>/g, "&gt;");
                    let folderNameAttr = folderName.replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/&/g, "&amp;");
                    let generatorListItemsHtml = generators.map((g, i) => `<li data-default-sort-value="${i}"><a href="/${g.name}#edit">${g.name}</a> (${g.views} views) <b style="color:#ff6800; ${g.isPrivate ? "" : "display:none;"}">(priv)</b> <span class="add-to-folder-btn" style="display:inline-block;" data-generator-name="${g.name}" onclick="accountModal.handleGeneratorFolderMapButtonClick(this);">📁</span></li>`).join("");
                    let folderEmoji = foldState === "open" ? "📂" : "📁";
                    
                    div.innerHTML = `<div class="folder-ctn" data-folder-name="${folderNameAttr}" data-fold-state="${foldState}">
                      <div class="folder-header" onclick="this.parentNode.dataset.foldState = (this.parentNode.dataset.foldState=='closed' ? 'open' : 'closed'); this.parentNode.querySelector('ul').style.height = (this.parentNode.dataset.foldState=='closed' ? '0px' : 'auto'); this.parentNode.querySelector('.folder-emoji').textContent = (this.parentNode.dataset.foldState=='open' ? '📂' : '📁'); window.__userGeneratorListFolderFoldStates[this.parentNode.dataset.folderName] = (this.parentNode.dataset.foldState=='open' ? 1 : 0); localStorage.userGeneratorListFolderFoldStates = JSON.stringify(window.__userGeneratorListFolderFoldStates);">
                        <span class="folder-emoji">${folderEmoji}</span>
                        <span style="font-weight:bold;">${folderNameHtml}</span>
                        (<span class="folder-item-count">${generators.length}</span>)
                      </div>
                      <ul style="margin:0; overflow:hidden; ${foldState === "open" ? "height:auto" : "height:0"}; padding-left:2rem;">
                        ${generatorListItemsHtml}
                      </ul>
                    </div>`;
                    return div.firstElementChild;
                  };


                  this.changePassword = async () => {

                    this.goto("loadingView");
                    this.setChangePasswordError("none");

                    if(this.refs.passwordChange_newPassword.value.length < 6) {
                      this.goto("passwordChangeView");
                      this.setChangePasswordError("passwordLength");
                      return;
                    }

                    try {

                      let data = await fetch('/api/changePassword', {
                        method: 'POST',
                        body: JSON.stringify({email:app.store.data.user.email, sessionToken:app.store.data.user.sessionToken, currentPassword:this.refs.passwordChange_currentPassword.value, newPassword:this.refs.passwordChange_newPassword.value}),
                        signal: AbortSignal.timeout?.(20*1000),
                        headers: { 'Content-Type': 'application/json'},
                      }).then(r => r.json());

                      if(data.status === "success") {
                        this.setChangePasswordError("none");
                        this.goto("passwordChangeSuccessView");
                        this.refs.passwordChange_currentPassword.value = "";
                        this.refs.passwordChange_newPassword.value = "";
                      } else if(data.status === "session-token-error") {
                        // TODO: test this
                        this.closeModal();
                        app.logout();
                        app.openLoginModal();
                      } else if(data.status === "incorrect-password") {
                        this.goto("passwordChangeView");
                        this.setChangePasswordError("password");
                      } else {
                        console.debug(data);
                        this.goto("passwordChangeView");
                        this.setChangePasswordError("server");
                      }

                    } catch(e) {
                      console.error("Problem loading generator list");
                      console.debug(e);
                      this.goto("passwordChangeView");
                      this.setChangePasswordError("request");
                    }

                  };
                  this.setChangePasswordError = (name) => {
                    this.refs.passwordChangeError_password.style.display = "none";
                    this.refs.passwordChangeError_passwordLength.style.display = "none";
                    this.refs.passwordChangeError_request.style.display = "none";
                    this.refs.passwordChangeError_server.style.display = "none";
                    if(name === "none") {
                      return;
                    } else {
                      this.refs["passwordChangeError_"+name].style.display = "block";
                    }
                  }

                  this.changeEmail = async () => {
                    this.goto("loadingView");
                    this.setChangeEmailError("none");

                    if(!/.+@.+/.test(this.refs.emailChange_newEmail.value)) {
                      this.setChangeEmailError("emailInvalid");
                      return;
                    }
                    try {

                      let data = await fetch('/api/changeEmail', {
                        method: 'POST',
                        body: JSON.stringify({email:app.store.data.user.email, newEmail:this.refs.emailChange_newEmail.value, sessionToken:app.store.data.user.sessionToken, password:this.refs.emailChange_password.value}),
                        signal: AbortSignal.timeout?.(20*1000),
                        headers: { 'Content-Type': 'application/json'},
                      }).then(r => r.json());

                      if(data.status === "success") {
                        this.setChangeEmailError("none");
                        this.goto("emailChangeSuccessView");
                        app.emailChangeCallback(this.refs.emailChange_newEmail.value);
                        // this.update();
                        this.refs.emailChange_newEmail.value = "";
                        this.refs.emailChange_password.value = "";
                      } else if(data.status === "session-token-error") {
                        // TODO: test this
                        this.closeModal();
                        app.logout();
                        app.openLoginModal();
                      } else if(data.status === "incorrect-password") {
                        this.goto("emailChangeView");
                        this.setChangeEmailError("password");
                      } else {
                        console.debug(data);
                        this.goto("emailChangeView");
                        this.setChangeEmailError("server");
                      }

                    } catch(e) {
                      console.error("Problem loading generator list");
                      console.debug(e);
                      this.goto("emailChangeView");
                      this.setChangeEmailError("request");
                    }

                  };
                  this.setChangeEmailError = (name) => {
                    this.refs.emailChangeError_emailInvalid.style.display = "none";
                    this.refs.emailChangeError_password.style.display = "none";
                    this.refs.emailChangeError_request.style.display = "none";
                    this.refs.emailChangeError_server.style.display = "none";
                    if(name === "none") {
                      return;
                    } else {
                      this.refs["emailChangeError_"+name].style.display = "block";
                    }
                  }

                  this.hideAllViews();
                  this.goto("accountMenuView");
                  this.refs.emailChange_password.addEventListener("keyup", (e) => { if(e.which === 13) { this.changeEmail(); } });
                  this.refs.passwordChange_newPassword.addEventListener("keyup", (e) => { if(e.which === 13) { this.changePassword(); } });
                },
              };
            </script>
          </div>
        
          <div id="settingsModalEl" class="app-modal" style="display:none;">
            <div style="z-index:50;" class="background" onmousedown="settingsModal.closeModal()"></div>
            <div style="z-index:51;" class="outer-wrapper" onmousedown="settingsModal.handleOuterWrapperClick(event)">
              <div data-ref="contentWrapper" class="content-wrapper">
                <!-- SETTINGS MENU -->
                <div class="view" data-ref="settingsMenuView" style="display: initial;">
                  <div class="modal-header">
                    <p>you're viewing your generator with the url <b class="currentGeneratorNameDisplay">welcome</b> - you can:</p>
                    <ul>
                      <li><span class="link" onclick="settingsModal.goto('nameChangeView')">change its url</span></li>
                      <li style="margin-top:0.5rem;"><span class="link" onclick="settingsModal.duplicateGenerator()">duplicate it</span><span data-ref="duplicateGeneratorStatus"></span></li>
                      <li style="margin-top:0.5rem;"><span data-ref="changeGeneratorPrivacyButton" class="link" onclick="settingsModal.changeGeneratorPrivacy()">make public</span></li>
                      <li style="margin-top:0.5rem;"><span class="link" onclick="window.open(`/api/downloadGenerator?generatorName=${app.data.generator.name}`, 'Downloading generator...')">download it</span></li>
                      <li style="margin-top:1em;"><span class="link" onclick="settingsModal.deleteGenerator()">delete it</span></li>
                    </ul>
                  </div>
                  <div class="modal-body">
                  </div>
                  <div class="modal-footer">
                    <button style="width:100%;" onclick="settingsModal.closeModal()">close</button>
                  </div>
                </div>
                <!-- NAME CHANGE -->
                <div class="view" data-ref="nameChangeView" style="display: none;">
                  <div class="modal-header">
                    <p>this generator's current url is: <b class="currentGeneratorNameDisplay">welcome</b></p>
                    <p style="margin-top:0.4em;">to change it, just enter a new one below. remember: you can only use lower-case letters, numbers and hyphens in your url</p>
                    <p style="margin-top:0.4em;"><b style="color:#f60000;">caution:</b> if you change it, the old url will no longer work! if your generator is popular, and others have imported it into their own, you will <b>break their generators!</b> (they will get import errors). because of this, if your generator is popular, it's better to make a copy of this generator rather than change this one's name</p>
                    <p data-ref="nameChangeError_length" style="display:none; color:red; margin-top:1em;">sorry, the new url must be at least 4 characters long</p>
                    <p data-ref="nameChangeError_request" style="display:none; color:red; margin-top:1em;">there was a problem connecting to the server ¯\_(⊙_ʖ⊙)_/¯ check your internet connection?</p>
                    <p data-ref="nameChangeError_alreadyExists" style="display:none; color:red; margin-top:1em;">sorry, a generator with that name already exists (⌐■_■)</p>
                    <p data-ref="nameChangeError_server" style="display:none; color:red; margin-top:1em;">something went wrong on the server (✖╭╮✖) <a href="https://lemmy.world/c/perchance" target="_blank">plz post to forum</a> if problem persists</p>
                  </div>
                  <div class="modal-body">
                    <input data-ref="newGeneratorName" oninput="settingsModal.handleNewGeneratorNameInput(event)" onpaste="settingsModal.handleNewGeneratorNamePaste(event)" placeholder="new url" type="text">
                  </div>
                  <div class="modal-footer">
                    <button onclick="settingsModal.goto('settingsMenuView')">back</button><button class="main" onclick="settingsModal.changeGeneratorName()">set new url</button>
                  </div>
                </div>
                <!-- NAME CHANGE SUCCESS -->
                <div class="view" data-ref="nameChangeSuccessView" style="display: none;">
                  <div class="modal-header">
                    <p>your generator's url has been changed ヾ(⌐■_■)ノ♪♬</p>
                  </div>
                  <div class="modal-body">
                  </div>
                  <div class="modal-footer">
                    <button style="width:100%;" onclick="settingsModal.closeModal()">close</button>
                  </div>
                </div>
                <!-- LOADING -->
                <div class="view" data-ref="loadingView" style="display: none;">
                  <!-- <emoji-ajax-spinner style="display:block; margin:5em 0;" verb="loading"/> -->
                  <div style="margin:5em 0; text-align:center;">⏳ loading...</div>
                </div>
              </div>
            </div>
            <script>
              window.settingsModal = {
                root: document.querySelector("#settingsModalEl"),
                refs: new Proxy({}, {
                  get(target, prop, receiver) {
                    return document.querySelector(`#settingsModalEl [data-ref='${prop}']`);
                  },
                }),
                init: function() {
                  this.closeModal = function() {
                    this.root.style.display = "none";
                  }
                  this.openModal = function() {
                    this.goto("settingsMenuView");
                    this.root.style.display = "block";
                  }
                  this.handleOuterWrapperClick = function(e) {
                    if(!this.refs.contentWrapper.contains(e.target)) {
                      this.closeModal();
                    }
                  }
                  this.goto = function(name) {
                    this.hideAllViews();
                    let el = this.refs[name];
                    el.style.display = "initial";
                    settingsModalEl.querySelectorAll(".currentGeneratorNameDisplay").forEach(el => el.textContent=app.data.generator.name);
                    if(el.querySelector("input")) {
                      // TODO: move caret to end of text if there is text in the input
                      setTimeout(() => { el.querySelector("input").focus(); },200);
                    }
                  }
                  this.hideAllViews = function() {
                    let views = this.root.querySelectorAll(".content-wrapper .view");
                    for(let el of Array.from(views)) {
                      el.style.display = "none";
                    }
                  }
                  this.getCurrentView = function() {
                    let views = this.root.querySelectorAll(".content-wrapper .view");
                    for(let el of Array.from(views)) {
                      if( el.style.display !== "none" ) return el.attributes.ref.value;
                    }
                  };
                  this.handleNewGeneratorNamePaste = (e) => {
                    // let original = e.target.value;
                    // setTimeout(() => { e.target.value = original; },100);
                  };
                  this.handleNewGeneratorNameInput = (e) => {
                    let caretPos = e.target.selectionStart;
                    let text = e.target.value;
                    if(text === "") { return; }
                    let output = "";
                    for(let c of text) {
                      if(/[A-Z]/.test(c)) {
                        output += c.toLowerCase();
                      } else if(/[ \-]/.test(c)) {
                        output += "-";
                      } else if(/[0-9a-z]/.test(c)) {
                        output += c;
                      } else {
                        output += "-";
                      }
                    }
                    e.target.value = output;
                    setCaretPosition(e.target, caretPos);
                  };
                  function setCaretPosition(elem, caretPos) {
                    let range;
                    if (elem.createTextRange) {
                      range = elem.createTextRange();
                      range.move('character', caretPos);
                      range.select();
                    } else {
                      elem.focus();
                      if (elem.selectionStart !== undefined) {
                        elem.setSelectionRange(caretPos, caretPos);
                      }
                    }
                  }

                  var allowedNamesWithLessThan4Chars = ["hub"];
                  this.changeGeneratorName = async () => {

                    this.goto("loadingView");
                    this.setNameChangeError("none");

                    if(this.refs.newGeneratorName.value.length < 4 && !allowedNamesWithLessThan4Chars.includes(this.refs.newGeneratorName.value)) {
                      this.setNameChangeError("length");
                      this.goto("nameChangeView");
                      return;
                    }

                    try {

                      let data = await fetch('/api/changeGeneratorName', {
                        method: 'POST',
                        body: JSON.stringify({email:app.store.data.user.email, sessionToken:app.store.data.user.sessionToken, currentName:app.data.generator.name, newName:this.refs.newGeneratorName.value}),
                        signal: AbortSignal.timeout?.(20*1000),
                        headers: { 'Content-Type': 'application/json'},
                      }).then(r => r.json());

                      if(data.status === "success") {
                        this.setNameChangeError("none");
                        this.goto("nameChangeSuccessView"); 
                        app.data.generator.name = this.refs.newGeneratorName.value;
                        window.generatorName = this.refs.newGeneratorName.value;
                        app.data.generator.publicId = data.publicId;
                        window.generatorPublicId = data.publicId;
                        window.history.replaceState(null, null, "/"+this.refs.newGeneratorName.value+window.location.hash);
                        // this.update();
                        this.refs.newGeneratorName.value = "";
                        window.hardReloadOutputIframe(); // needed to ensure e.g. comments-plugin, text-to-image-plugin gallery, etc. have correct URL
                      } else if(data.status === "session-token-error") {
                        // TODO: test this
                        this.closeModal();
                        app.logout();
                        app.openLoginModal();
                      } else if(data.status === "already-exists") {
                        this.goto("nameChangeView");
                        this.setNameChangeError("alreadyExists");
                      } else {
                        console.debug(data);
                        this.goto("nameChangeView");
                        this.setNameChangeError("server");
                      }

                    } catch(e) {
                      console.error("Problem while requesting generator name change");
                      console.debug(e);
                      this.goto("nameChangeView");
                      this.setNameChangeError("request");
                    }

                  };
                  this.setNameChangeError = (name) => {
                    this.refs.nameChangeError_alreadyExists.style.display = "none";
                    this.refs.nameChangeError_request.style.display = "none";
                    this.refs.nameChangeError_server.style.display = "none";
                    this.refs.nameChangeError_length.style.display = "none";
                    if(name === "none") {
                      return;
                    } else {
                      this.refs["nameChangeError_"+name].style.display = "block";
                    }
                  }


                  this.deleteGenerator = async () => {

                    let confirm = prompt("Are you sure you want to delete this generator? You cannot undo this action. Type \"yes\" to confirm that you want to delete this generator:");

                    if(confirm !== "yes") {
                      return;
                    }

                    this.goto("loadingView");

                    try {

                      let data = await fetch('/api/deleteGenerator', {
                        method: 'POST',
                        body: JSON.stringify({email:app.store.data.user.email, sessionToken:app.store.data.user.sessionToken, generatorName:app.data.generator.name}),
                        signal: AbortSignal.timeout?.(20*1000),
                        headers: { 'Content-Type': 'application/json'},
                      }).then(r => r.json());

                      if(data.status === "success") {
                        alert("This generator has been successfully deleted. You'll now be redirected to the homepage.");
                        window.userDeletedGenerator = true;
                        window.location.href = "https://perchance.org";
                      } else if(data.status === "session-token-error") {
                        this.closeModal();
                        app.logout();
                        app.openLoginModal();
                      } else {
                        alert("Something went wrong. If this error persists, please report it to lemmy.world/c/perchance - thanks!");
                        console.debug(data);
                        this.goto("settingsMenuView");
                      }

                    } catch(e) {
                      alert("Something went wrong. If this error persists, please report it to lemmy.world/c/perchance - thanks!");
                      console.error("Problem with request to delete generator");
                      console.debug(e);
                      this.goto("settingsMenuView");
                    }

                  };


                  this.changeGeneratorPrivacy = async () => {

                    if(!app.data.generator.isPrivate) {
                      let okay = window.confirm("Note that this action will de-list your generator from all publicly accessible Perchance generator lists. Anyone who has your generator's link will still be able to access it. You can easily undo this action.");
                      if(!okay) {
                        return;
                      }
                    }

                    this.goto("loadingView");

                    try {

                      let data = await fetch('/api/changeGeneratorPrivacy', {
                        method: 'POST',
                        body: JSON.stringify({email:app.store.data.user.email, sessionToken:app.store.data.user.sessionToken, generatorName:app.data.generator.name, isPrivate:app.data.generator.isPrivate ? false : true}),
                        signal: AbortSignal.timeout?.(20*1000),
                        headers: { 'Content-Type': 'application/json'},
                      }).then(r => r.json());

                      if(data.status === "success") {
                        app.data.generator.isPrivate = !app.data.generator.isPrivate;
                        this.refs.changeGeneratorPrivacyButton.innerHTML = `make ${app.data.generator.isPrivate ? "public" : "private"}`;
                        this.goto("settingsMenuView");
                      } else if(data.status === "session-token-error") {
                        this.closeModal();
                        app.logout();
                        app.openLoginModal();
                      } else {
                        alert("Something went wrong. If this error persists, please report it to lemmy.world/c/perchance - thanks!");
                        console.debug(data);
                        this.goto("settingsMenuView");
                      }

                    } catch(e) {
                      alert("Something went wrong. If this error persists, please report it to lemmy.world/c/perchance - thanks!");
                      console.error("Problem with request to delete generator");
                      console.debug(e);
                      this.goto("settingsMenuView");
                    }

                  };


                  this.duplicateGenerator = async () => {

                    this.goto("loadingView");

                    try {

                      let data = await fetch('/api/duplicateGenerator', {
                        method: 'POST',
                        body: JSON.stringify({
                          email: app.store.data.user.email,
                          sessionToken: app.store.data.user.sessionToken,
                          generatorName: app.data.generator.name,
                          // overrides are only needed if the user owns this gen, since otherwise they'd just use the save button:
                          outputTemplateOverride: app.userOwnsThisGenerator ? window.outputTemplateEditor?.getValue() : null,
                          modelTextOverride: app.userOwnsThisGenerator ? window.modelTextEditor?.getValue() : null,
                        }),
                        signal: AbortSignal.timeout?.(20*1000),
                        headers: { 'Content-Type': 'application/json'},
                      }).then(r => r.json());

                      if(data.status === "success") {
                        this.refs.duplicateGeneratorStatus.innerHTML = `<span style="color:green;"> Success!</span> → <a target="_blank" href="/${data.newGeneratorName}#edit">${data.newGeneratorName}</a>`;
                        this.goto("settingsMenuView");
                      } else if(data.status === "session-token-error") {
                        this.closeModal();
                        app.logout();
                        app.openLoginModal();
                      } else {
                        alert("Something went wrong. If this error persists, please report it to lemmy.world/c/perchance - thanks!");
                        console.debug(data);
                        this.goto("settingsMenuView");
                      }

                    } catch(e) {
                      alert("Something went wrong. If this error persists, please report it to lemmy.world/c/perchance - thanks!");
                      console.error("Problem with request to duplicate generator");
                      console.debug(e);
                      this.goto("settingsMenuView");
                    }

                  };

                  this.hideAllViews();
                  this.goto("settingsMenuView");
                  this.refs.newGeneratorName.addEventListener("keyup", (e) => { if(e.which === 13) { this.changeGeneratorName(); } });
                  this.refs.changeGeneratorPrivacyButton.innerHTML = `make ${app.data.generator.isPrivate ? "public" : "private"}`;
                },
              };
            </script>
          </div>
        
          <div id="revisionsModalEl" class="app-modal" style="display:none;">
            <style>
              #revisionsModalEl ul li span.clickable {
                color:blue;
                text-decoration:underline;
                cursor: pointer;
              }
              #revisionsModalEl .content-wrapper {
                max-width:750px;
              }
            </style>
            <div style="z-index:50;" class="background" onclick="revisionsModal.closeModal()"></div>
            <div style="z-index:51;" class="outer-wrapper" onclick="revisionsModal.handleOuterWrapperClick(event)">
              <div data-ref="contentWrapper" class="content-wrapper">
                <div class="view" data-ref="revisionsView">
                  <div class="modal-header">
                    <p>if you click the button below, it will load a list of older versions of your generator so you can download them in case you accidentally deleted your code, or there was a system error. copies of your generator code are also backed-up to your local browser storage, so if your computer ever crashes and you hadn't saved in a while, you'll be able to come here to recover your data.</p>
                    <p style="text-align:center;"><button style="margin-top:1em;" onclick="revisionsModal.loadRevisionsList()" data-ref="revisionsButton">load backup/revision history</button></p>
                    <div data-ref="revisionsList" style="display:none; max-height:400px; overflow-y:auto; margin-top:1em;">
                      <ul style="margin:0;"></ul>
                    </div>
                    <div data-ref="loadingSpinner" style="display:none">
                      <!-- <emoji-ajax-spinner style="display:none; margin:5em 0;" verb="loading"/> -->
                      <div style="margin:5em 0; text-align:center;">⏳ loading...</div>
                    </div>
                    <p data-ref="revisionsError" style="color:red; display:none;">hmm (⊙_☉) there was some sort of server error while trying to get your revision history. sorry! this doesn't happen very often. if it keeps happening (and you've checked your internet connection), could you please make a post on the <a href="https://lemmy.world/c/perchance" target="_blank">forum</a>?</p>
                  </div>
                  <div class="modal-body"></div>
                  <div class="modal-footer">
                    <button style="width:100%;" onclick="revisionsModal.closeModal()">close</button>
                  </div>
                </div>
              </div>
            </div>
            <script>
              window.revisionsModal = {
                root: document.querySelector("#revisionsModalEl"),
                refs: new Proxy({}, {
                  get(target, prop, receiver) {
                    return document.querySelector(`#revisionsModalEl [data-ref='${prop}']`);
                  },
                }),
                init: function() {
                  this.closeModal = function() {
                    this.root.style.display = "none";
                  }
                  this.openModal = function() {
                    this.root.style.display = "block";
                    this.refs.revisionsError.style.display = "none";
                    this.refs.revisionsList.style.display = "none";
                    this.refs.revisionsButton.style.display = "";
                  }
                  this.handleOuterWrapperClick = function(e) {
                    if(!this.refs.contentWrapper.contains(e.target)) {
                      this.closeModal();
                    }
                  }

                  this.loadRevisionsList = async () => {

                    this.refs.loadingSpinner.style.display = "";
                    this.refs.revisionsError.style.display = "none";
                    this.refs.revisionsButton.style.display = "none";
                    this.refs.revisionsList.style.display = "none";

                    try {

                      let userData = JSON.parse(localStorage["app-storage"]).user;
                      
                      window.diffStuff.patches = await fetch("https://perchance.org/api/getGeneratorDiffPatches", {
                        method:"POST",
                        headers: {"Content-Type": "application/json; charset=utf-8"},
                        body:JSON.stringify({
                          generatorName: app.data.generator.name,
                          email: userData.email,
                          sessionToken: userData.sessionToken,
                        }),
                      }).then(r => r.json());

                      window.diffStuff.patches.sort((a, b) => a.creationTime-b.creationTime);
                      window.diffStuff.modelTextPatches = window.diffStuff.patches.map(o => o.modelTextPatch);
                      window.diffStuff.outputTemplatePatches = window.diffStuff.patches.map(o => o.outputTemplatePatch);

                      window.diffStuff.patches.sort((a, b) => b.creationTime-a.creationTime);
                      listHTML = window.diffStuff.patches.map((p, i, arr) => `<li style="margin-top:0.25rem;" onclick="window.diffStuff.openNewTabWithRevision(${arr.length-1-i}, '${app.data.generator.name}')"><span class="clickable">${new Date(p.creationTime).toString()}</span></li>`).join("");

                      if(!kv) await window.initKv();
                      let localBackupsArray = await kv.localBackups.get(app.data.generator.name) || [];
                      window.downloadLocalBackup = async (i) => {
                        let backup = localBackupsArray[i];
                        let intro = "<<<<< this file contains your perchance lists first, and your HTML code underneath it >>>>>\n\n\n\n";
                        window.downloadTextFile(intro+backup.modelText+("\n".repeat(20))+backup.outputTemplate+("\n".repeat(20)), `${app.data.generator.name}-revision-${new Date(backup.time).toString().toLowerCase().split(" ").slice(0, 5).join("-").replace(/:/g, "-")}.txt`);
                      };
                      let localBackupsHTML = "";
                      if(localBackupsArray.length > 0) {
                        localBackupsHTML = localBackupsArray.map((d, i) => `<li style="margin-top:0.25rem;" onclick="window.downloadLocalBackup(${i})"><span class="clickable" style="color:#e8690b;">(<b>browser storage</b>) ${new Date(d.time).toString()}</span></li>`).join("");
                        localBackupsHTML += "<hr>";
                      }
                      
                      listHTML = localBackupsHTML + listHTML;

                      this.refs.revisionsList.querySelector("ul").innerHTML = listHTML.trim() === "" ? "<li style='color:black; text-decoration:none;'>no revisions/backups yet! try making an edit to your generator and then save</li>" : listHTML;

                      this.refs.revisionsList.style.display = "";
                      this.refs.revisionsError.style.display = "none";
                      this.refs.loadingSpinner.style.display = "none";

                    } catch(e) {
                      console.error("Problem loading revision list");
                      console.debug(e);
                      this.refs.revisionsError.style.display = "";
                      this.refs.revisionsButton.style.display = "";
                      this.refs.revisionsList.style.display = "none";
                      this.refs.loadingSpinner.style.display = "none";
                    }
                  };
                },
              };
            </script>
          </div>
        </div>

        <script>
          window.app = {
            root: document.querySelector("#appEl"),
            refs: new Proxy({}, {
              get(target, prop, receiver) {
                return document.querySelector(`#appEl [data-ref='${prop}']`);
              },
            }),
            data: {},
            exitWarningAttached: false,
            init: function({generator, dependencies}) {
              
              this.alreadyInitializedEditor = false;
              
              this.goToEditMode = () => {
                
                // To prevent accidental second click in the position of the edit button, resulting in a save, since the save button appears in its place.
                // We switch back to normal values after switchToEditMode() has executed.
                let saveBtn = document.querySelector(`#menuBarEl [data-ref='saveButton']`);
                saveBtn.style.pointerEvents = 'none';

                if(!this.alreadyInitializedEditor) {
                  window.editor.init();
                  this.alreadyInitializedEditor = true;
                }
                this.appInteractionState = "edit";
                this.trigger("AppInteractionStateChange");
                editor.switchToEditMode();

                setTimeout(() => {
                  saveBtn.style.pointerEvents = '';
                }, 700);
                
                if(location.hash === "") { // if there's already a hash, we don't edit it, since we now transfer the hash to the iframe so people can use it for stuff in generators, and the #edit part of the URL isn't "ground truth" - it's just a handy way of sharing a gen that goes straight to edit mode
                  let url = new URL(location.href);
                  url.hash = "#edit"; 
                  history.replaceState({}, "", url.href); // we do this rather than setting the hash with location.hash because otherwise it essentially does a pushState behind the scenes
                }
                
                // initialPageLoadSpinner.style.display = "none"; // otherwise when the generator is frozen and they click edit, the loading spinner doesn't go away
            
                // this.update();
              };
              
              this.goToViewMode = () => {
                this.appInteractionState = "view";
                this.trigger("AppInteractionStateChange");
                
                if(this.alreadyInitializedEditor) editor.switchToViewMode(); // page load in 'view mode' by default, so only needed if we're inited editor

                if(window.location.hash === "#edit") {
                  this.removeURLHash();
                  let metaData = editor.generatorMetaData;
                  if(!metaData) metaData = window.generatorStaticMetaData;
                  if(metaData && metaData.header?.mode === "minimal") { 
                    menuBarEl.style.display = "none";
                    minimalModeMenuBtn.style.display = "flex"; 
                  }
                }
                // this.update();
              };
            
              this.removeURLHash = () => {
                history.replaceState(null, document.title, window.location.pathname + window.location.search);
              };

              let generatorEditedMostRecentCallbackCallIndex = 0;
              this.generatorEditedCallback = async (data, opts={}) => {
                generatorEditedMostRecentCallbackCallIndex++;
                let thisCallbackCallIndex = generatorEditedMostRecentCallbackCallIndex;
                let changed = false;
                let importsChanged = false;
                if(data.modelText !== undefined) {
                  this.data.generator.modelText = data.modelText;
                  changed = true;
                }
                if(data.outputTemplate !== undefined) {
                  this.data.generator.outputTemplate = data.outputTemplate;
                  changed = true
                }
                if(data.imports !== undefined) {
                  let currentImportsSet = new Set(this.data.generator.imports);
                  if(data.imports.sort().toString() !== this.data.generator.imports.sort().toString()) { // if sets are not equal
                    this.data.generator.imports = data.imports;
                    changed = true;
                    importsChanged = true;
                  }
                }
                // if it's changed, we set save state to unsaved, but only if the actual save request has been sent (which is why we check savingNowButStillWaitingForIframeReload here). if it's waiting for the iframe, then we've snuck in the changed data before it was sent to the server, so we're good.
                if(changed && !window.savingNowButStillWaitingForIframeReload) {
                  // save request has been sent to server, so we wait for it to finish, and then set save state to 'unsaved'
                  while(menuBar.saveState === "saving") {
                    if(thisCallbackCallIndex !== generatorEditedMostRecentCallbackCallIndex) return; // there's a newer one, so this one is not needed anymore (important to prevent "traffic jam" for autoSave stuff below)
                    await new Promise(r => setTimeout(r, 50));
                  }
            
                  menuBar.setSaveState("unsaved");
                  if(await window.thisIsNotAnOldCachedPagePromise) { // <-- we don't want an importUpdate message to block a cache-busting page reload
                    this.attachExitWarning(); // this actually only needs to be called once, but meh (it *replaces* the last one anyway)
                  }
                }
              };
              this.attachExitWarning = () => {
                if(!this.exitWarningAttached) {
                  // if there are unsaved changes and the user tries to navigate away from the page, warn them
                  window.onbeforeunload = (e) => {
                    if(menuBar.saveState === "unsaved" && !window.userDeletedGenerator && window.hasBeenInEditModeAtLeastOnce) { // window.hasBeenInEditModeAtLeastOnce is due to importsUpdate 'bug' where user can save gen without correct imports, causing visitors to have to download them on the fly, but that triggers a gen data change, which will trigger this handler if not for the window.hasBeenInEditModeAtLeastOnce check. hacky.
                      var dialogText = "You've got unsaved changes! Are you sure you want to exit?";
                      e.returnValue = dialogText;
                      return dialogText;
                    }
                  };
                  this.exitWarningAttached = true;
                }
              };
              this.openLoginModal = () => {
                loginModal.openModal();
              };
              this.openAccountModal = () => {
                accountModal.openModal();
              };
              this.openSettingsModal = () => {
                settingsModal.openModal();
              };
              this.openRevisionsModal = () => {
                revisionsModal.openModal();
              };
              this.loginSuccessCallback = (details) => {
                this.store.data.user = {email:details.email, sessionToken:details.sessionToken, loggedIn:true};
                this.store.save();
                // this.update();
                this.checkIfOwner();
                this.trigger("LoginStateChange");
              };
              this.emailChangeCallback = (newEmail) => {
                this.store.data.user.email = newEmail;
              };
              this.saveSuccessCallback = (name, publicId) => {
                if(name !== undefined) { // for when the save creates a new generator
                  this.data.generator.name = name;
                  this.data.generator.publicId = publicId; 
                }
                // this.update();
              };
              this.logout = () => {
                settingsModal.closeModal();
                accountModal.closeModal();
                // menuBar.isOwner = false;
                this.store.data.user = {loggedIn:false};
                localStorage.sessionTokenLastChangeTime = Date.now();
                this.store.save();
                // this.update();
                this.trigger("LoginStateChange");
              };
              this.handleIframeSaveRequest = () => {
                if((this.userOwnsThisGenerator && this.store.data.user.loggedIn) || localStorage[`perchance_generatorEditKey_${this.data.generator.name}`]) {
                  this.saveGenerator();
                }
              };
              this.attachKeyboardShortcuts = () => { 
                // CTRL/CMD + S
                document.addEventListener("keydown", (e) => {
                  if(e.keyCode == 83 && (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey)) {
                    e.preventDefault();
                    this.saveGenerator();
                  }
                }, false);
              };

              this.eventListeners = {
                "appinteractionstatechange": [],
                "generatorownershipchange": [],
                "loginstatechange": [],
              }
              this.on = function(name, handler) {
                this.eventListeners[name.toLowerCase()].push(handler);
              };
              this.trigger = function(name, data={}) {
                for(let fn of this.eventListeners[name.toLowerCase()]) {
                  fn(data);
                }
              };

              this.checkIfOwner = async () => {
                try {
                  let data = await fetch('/api/checkGeneratorOwnership', {
                    method: 'POST',
                    body: JSON.stringify({email:this.store.data.user.email, sessionToken:this.store.data.user.sessionToken, generatorName:this.data.generator.name}),
                    signal: AbortSignal.timeout?.(20*1000),
                    headers: { 'Content-Type': 'application/json'},
                  }).then(r => r.json());
            
                  if(data.status === "is-owner") {
                    this.userOwnsThisGenerator = true;
                    this.trigger("GeneratorOwnershipChange");
                    if(!window.editsHaveBeenMadeSincePageLoad) menuBar.setSaveState("saved");
                  } else if(data.status === "is-not-owner") {
                    this.userOwnsThisGenerator = false;
                    this.trigger("GeneratorOwnershipChange");
                    menuBar.setSaveState("unsaved"); // <-- it's not saved if they're not the owner
                  } else if(data.status === "session-token-error") {
                    this.logout();
                    // this.openLoginModal();
                    // alert("Sorry! Your session has expired, please login again.")
                  } else {
                    console.error("server error / unhandled response from checkGeneratorOwnership:");
                    console.debug(data);
                  }
                } catch(e) {
                  console.error("Problem with request to /api/checkGeneratorOwnership");
                  console.debug(e);
                  alert("There was a problem while loading this generator. If your internet connection is slow or intermittent, this could be the cause. Please try reloading the page.");
                }
              };



              // NOTE: THIS IS DEFINITELY NOT FULL-PROOF because browsers seem to pause tabs that haven't been active in a while (probably to converse memory/cpu).
              // So I should really switch to an approach like this: https://stackoverflow.com/a/69924841/11950764 (BroadcastChannel)
              // keep track of open generator tabs so we can warn if they are owner and have the same generator open twice and are editing:
              const tabTrackerUpdateInterval = 70000; // milliseconds - it needs to be higher than 1 min since chrome suspends tabs and wakes them only once per minute, and we send the "i'm still alive" pings during those wakes.
              function clearOldTabRecords(tabsOpen) {
                for(let [genName, tabIdDict] of Object.entries(tabsOpen)) {
                  for(let [tId, lastSeenTime] of Object.entries(tabIdDict)) {
                    if(Date.now()-lastSeenTime > tabTrackerUpdateInterval*2) {
                      console.debug(`Tab has not been seen in a while, assuming closed: ${genName} ${tId}`);
                      delete tabIdDict[tId];
                    }
                  }
                  if(Object.keys(tabIdDict).length === 0) {
                    console.debug(`No more tabs of this gen: ${genName}`);
                    delete tabsOpen[genName];
                  }
                }
              }
              let tabId = Math.random().toString().replace(".", "");
              setInterval(() => {
                let generatorName = app.data.generator.name;
                let tabsOpen;
                try {
                  tabsOpen = JSON.parse(localStorage.perchanceTabsOpen || "{}"); // {generatorName1: {tabId1:lastSeenTime}, generatorName2: {tabId2:lastSeenTime}, ...}
                } catch(e) {
                  console.warn("Something went wrong with localStorage.perchanceTabsOpen?");
                  tabsOpen = {};
                }
                if(!tabsOpen[generatorName]) tabsOpen[generatorName] = {};
                tabsOpen[generatorName][tabId] = Date.now();
                clearOldTabRecords(tabsOpen);
                localStorage.perchanceTabsOpen = JSON.stringify(tabsOpen);
              }, tabTrackerUpdateInterval);
              // this beforeunload will work most of the time, but we need the above mess (based on time-since-last-seen tabId) in case of e.g. sudden computer shutdown.
              window.addEventListener("beforeunload", function() {
                let tabsOpen = JSON.parse(localStorage.perchanceTabsOpen || "{}");
                if(tabsOpen[app.data.generator.name]) delete tabsOpen[app.data.generator.name][tabId];
                localStorage.perchanceTabsOpen = JSON.stringify(tabsOpen);
                return null;
              });

              let doNotAskToSaveEditPasswordFor = new Set();
              let inMemoryGeneratorNameToEditKey = {};
              let exampleGeneratorsOrPluginsEtc = ["blank", "layout-maker-plugin","examples","generators","download-button-plugin","perchance-keyboard-shortcuts","quug0pp21x","simple-if-else-example","simple-if-else-example-3","simple-if-else-example-2","simple-if-else-example-4","simple-critical-hit-example","simple-critical-hit-example-with-function","textarea-example","user-input-list-example","multiple-drop-down-lists-example","dynamic-drop-down-list-example","simple-checkbox-example","checkbox-checklist-example","color-picker-example","hierarchy-example-2","critical-hit-if-condition-is-met","critical-hit-if-condition-is-met-multi-line","critical-hit-if-condition-is-met-with-function","function-with-2-inputs-example","executing-and-hiding-result-example","getparent-getname-example","create-instance-plugin","dynamic-sublist-referencing-example","matching-pronouns-with-genders-example","dynamic-sub-list-referencing-example","multiple-independent-outputs-example","multiple-independent-outputs-example-2","multiple-buttons-same-output","your-generators-name","tags-and-header-meta-data-example","selectmany-sumitems-example","roll-number-of-dice-based-on-previous-roll","draw-a-river-example","add-up-randomly-generated-numbers-example","choose-a-list-based-on-a-selected-number","append-items-when-click-example","output-history-example","hide-output-until-click-example","select-leaf-plugin-example","consumable-leaf-list-plugin-example","dice-game-example","passing-variables-to-lists-example","consumable-list-with-dynamic-odds-example","indented-html-in-your-lists-example","character-role-consumable-example","consumable-list-with-nested-properties","three-percentages-example","click-to-hide-show-text-example","custom-cursor-example","counting-item-selections-example","simple-random-images-example","simple-random-image-example-cropped","add-images-to-output","random-image-with-name-example","random-images-example","acronym-from-words-example","p5js-basic-example","permutations-counter-example","income-by-city-example","randomize-at-regular-intervals-example","future-presidents-example","if-else-in-dynamic-odds-example","generate-based-on-seed-keyword","multiple-outputs-random-number-of-items-example","lineage-of-kings-example","fuel-consumption-distance","commas-in-numbers-example","exclude-item-based-on-previous-selection","change-item-odds-based-on-selections-parent","change-output-based-on-screen-size-example","bingo-table-example","get-median-of-numbers-example","filter-exclude-item-from-list","seed-from-url-example","pass-variable-via-url-example","user-controls-how-many-outputs-example","remember-user-inputs-basic-example","click-counter","click-counter-2","increment-counter-when-specific-items-is-selected-example","increment-counter-caves-example","generate-colored-text-example","custom-image-button-example","custom-image-button-with-hover-example","simple-mouse-hover-image-change-example","metadata-example","different-result-on-first-load","different-items-depending-on-num-clicks","show-specific-text-on-first-load","persistent-consumable-list-example","add-items-to-list-example","map-example","multiple-output-results-option","tap-image-to-randomize","first-n-items-of-random-sub-list","consumable-list-that-stays-consumed-until-refresh","numbered-outputs-example-manual","numbered-outputs-with-html-ol","text-contains-text-example","add-user-inputs-to-list-example","user-input-list-append-example","update-dynamically-generated-elements-example","image-layer-combiner-thumbnail-generator-example","using-name-of-selected-sublist-example","changing-drop-down-without-user-click-example","multi-dropdown-user-provided-pattern","generate-drop-down-input-from-hierarchical-list","use-seeder-plugin-for-one-import-only-example","dynamically-creating-a-list-and-adding-items-to-it","filter-list-based-on-instance-properties-example","filter-a-list-of-generated-instances-example","damage-roll-function-example","randomize-on-tap-with-optional-subtap","combine-multiple-lists-into-one-consumable-list","list-method-with-createinstance-example","combining-list-that-contain-duplicates-without-selecting-duplicates","question-and-answer-example","select-the-highest-number","curly-brackets-within-square-brackets-alternatives-example","array-of-instances-within-instance-example","sequential-pages-example","randomize-image-color-example","curly-blocks-inside-square-blocks","prevent-randomization-of-list-except-at-page-reload","track-selections-display-other-properties-of-selected-items-later","spoiler-hidden-unfold-html-example","comments-plugin-goto-plugin-example","pure-css-html-plugin-template","select-item-from-same-index-position-in-two-lists","adjustable-vertical-split-example","custom-font-importer","power-generator-manager","raggedflights-generators","plugins-page","eathams-pluginhub","dicemonger-generators","nes-template","dark-typography-template","timeline-template","big-typography-template","newspaper-multiple-columns","newspaper-column-template","accordion-template","accordion-multiple-buttons-template","twitter-feed-template","tweet-template","interactive-terminal-template","computer-terminal-template","origin-of-species-template","long-form-template","collection-template","basic-template","little-story-template","minimal","centered-minimal","centered-with-background-template","stat-block-5e-template","emoji-paragraph-template","generator-list-with-stats-template","generator-list-with-stats-2-template","tucked-corners-template","dark-card-template","glitch-text-template","nauseating-retro-flicker","visual-novel-template","diaryexample","khaki-templates","pandan-templates","ro-templates","nicksdropdownmenu","danielm-overview","garlicdolphin-templates","ronin-generators","eathams-templates","rg4m14ri","tutorial","aqr03wn1","diy-perchance-api","perchance-discord-bot","useful-generators","privacy-policy","advanced-tutorial","project-codename","creature-description","geographic-location","paint-color","random-story","dream-description","3d174opr","conjugate-plugin","plural-plugin","be-plugin","templates","noun","abstract-noun","concrete-noun","common-noun","sci-fi-noun","uncountable-noun","pronoun","adjective","comparative-adjective","superlative-adjective","food-adjective","adjective-of-relation","verb","speech-verb","adverb","time-adverb","manner-adverb","frequency-adverb","intensifier","interjection","common-word","preposition","emotion","greeting","goodbye","swear-word","personal-title","personal-suffix","prefix","exclamation","compound-word","cliche","simile","sentence","part-of-speech","rare-word","archaic-word","long-word","3-letter-word","short-word","gre-high-freq-word","funny-word","walk-verb","woo-word","grammatical-case","-ism","latin-word","philia","obsession","nonsense-word","complex-word","person-adjective","honorific","animal","endangered-animal","unusual-animal","land-animal","dog-breed","pet-animal","horse-breed","mammal-species","dinosaur","reptile-species","lizard-species","snake-species","turtle-species","turtle-tortoise-species","fish-species","sea-creature","flower-species","plant-species","tree-species","herb","spider-species","bug-species","beetle-species","bird-species","north-american-bird-species","antarctic-bird-species","scientific-species-name","body-part","horse-color","cat-breed","body-of-water","vegetable","fruit","ingredient","vitamin","condiment","spice","dessert","fast-food","dinner","biological-diet","cocktail","tea-variety","book","stephen-king-book","james-patterson-book","type-of-art","color-palette","color","room-type","rhetorical-device","speech-type","proverb","quote","pokemon","pokemon-ability","futurama-character","lotr-character","fact","password","phobia","fabric-type","complex-form","object","knot-name","container-type","roman-city","ancient-greek-city","form-of-government","mass-surveillance-project-name","uk-political-party","us-federal-agency","us-military-operation","terrorist-group","month","moon-phase","part-of-day","day-of-week","time-zone","egyptian-god","greek-god","greek-titan","greek-monster","tarot-prediction","zodiac-sign","chinese-zodiac-sign","fortune-telling-technique","norse-deity","christian-saint","religion","wingding","emoji","bw-emoji","braille","ascii-face","css-color","hex-color","crayon-color","color-name","hobby","mood","occupation","us-president","us-vice-president","celebrity","pope","british-actor","famous-scientist","person-build","person-height","person-age","face-shape","startup-idea-generator","fortune-500-company","industry","nasdaq-corporation","us-newspaper","shop-or-service","animal-protection-group","common-first-name","common-last-name","norwegian-last-name","male-norwegian-name","female-norwegian-name","aesthetic-username","couple-name","chocobo-name","common-female-name","common-male-name","common-unisex-name","japanese-surname","feminine-name","masculine-name","surname","medical-diagnosis","fake-drug-name","drug-name","us-hospital","language","pokemon-sprite","imgur-image","instagram-image","biology-field","scientific-instrument","planet-name","exoplanet-name","moon-name","fibonacci-number","prime-number","trig-identity","unit-of-measurement","polygon-name","metal","chemical-element","toxic-chemical","material","building-material","packaging-material","sculpting-material","patent","phone-brand","programming-languge","mime-type","gtld","us-geographic-location","river-name","sea-name","type-of-rock","gemstone","terrain","nationality","country","country-or-territory","european-union-member","african-country","continent","us-city","japanese-city","german-town","us-state","canadian-municipality","english-town-name","english-city-name","venue","car-brand","nautical-term","anime-film","disney-film","marvel-film","pixar-film","netflix-category","jeopardy-question","star-trek-planet","monster-type","fantasy-language","website","youtube-thumbnail","youtube-video","social-network","instagram-user","instagram-username","musical-instrument","music-genre","traditional-dance-style","playing-card","playing-card-short","video-game","wrestling-move","wwe-wrestler","plugins","markdown-plugin","storing-selections-example-1","storing-selections-example-2","import-example","fav-book","multiline-example","multiline-pro-example","multiline-pro-simple-example","html-table-example","input-example","input-autoupdate-example","newspaper-column-creator","drop-down-list-example","hierarchy-example","output-keyword-example","battle-simulator-example","text-to-image-plugin","ai-text-plugin","comments-plugin","create-instances-plugin","image-layer-combiner-plugin","pattern-maker-plugin","tap-plugin","favicon-plugin","select-range-plugin","dice-plugin","wheel-plugin","upload-plugin","random-integer-plugin","random-decimal-plugin","seeder-plugin","url-params-plugin","remember-plugin","tooltip-plugin","generator-stats-plugin","tldraw-plugin","rpg-icon-plugin","font-plugin","google-sheets-plugin","sum-odds-plugin","background-image-plugin","background-audio-plugin","press-enter-plugin","tap-anywhere-plugin","select-until-plugin","select-leaf-plugin","select-leaves-plugin","select-all-leaves-plugin","random-select-plugin","consumable-leaf-list-plugin","typewriter-plugin","make-table-plugin","lockable-list-plugin","locker-plugin","fixed-until-reload-plugin","number-set-plugin","numerals-to-words-plugin","numerals-to-ordinal-words-plugin","numerals-to-ordinals-plugin","roman-numerals-plugin","text-to-speech-plugin","join-lists-plugin","exclude-items-plugin","filter-list-plugin","markov-chain-plugin","roll-table-plugin","goto-plugin","nested-plugin","navbar-plugin","literal-plugin","super-fetch-plugin","consumable-list-loop-plugin","dynamic-import-plugin","print-button-plugin","copy-text-plugin","fullscreen-button-plugin","tabs-plugin","title-case-plugin","random-image-plugin","image-plugin","pride-plugin","tornado-plugin","flat-avatar-plugin","a-an-plugin","date-plugin","preprocessors"];
              this.saveGenerator = async () => {
                
                let originalLastEditorsChangeTime = window.lastEditorsChangeTime;

                let editKey = null;
                if(!this.userOwnsThisGenerator && !exampleGeneratorsOrPluginsEtc.includes(this.data.generator.name)) {
                  editKey = localStorage[`perchance_generatorEditKey_${this.data.generator.name}`] || prompt("Click OK to 𝗰𝗿𝗲𝗮𝘁𝗲 a new generator based on this one. If you own this generator and would like to 𝗲𝗱𝗶𝘁 it, then first paste the edit password below.", inMemoryGeneratorNameToEditKey[this.data.generator.name] || "");
                  if(editKey === null) return; // they clicked cancel
                  editKey = editKey.trim();
                  if(!editKey) editKey = null;
                }
            
                // if(!this.store.data.user.loggedIn) {
                //   if(!confirm(`Since you're not logged in, saving will anonymously create a new generator with a unique password that is required to edit it. Continue?`)) {
                //     this.logout(); // ensure they're properly logged out
                //     this.openLoginModal();
                //     return;
                //   }
                // }
            
                console.debug("saveGenerator called. saveState:", menuBar.saveState);
                if(menuBar.saveState !== "unsaved") {
                  return;
                }
            
                if(this.userOwnsThisGenerator) {
                  let tabsOpen = JSON.parse(localStorage.perchanceTabsOpen || "{}");
                  clearOldTabRecords(tabsOpen);
                  if(tabsOpen[this.data.generator.name] && Object.keys(tabsOpen[this.data.generator.name]).length > 1) {
                    let continueSaving = confirm("Note: You have this generator open in another tab. Be careful that you're only editing your generator from a single tab, since you may accidentally overwrite previous edits. Continue saving?");
                    if(!continueSaving) return;
                  }
                }
            
                let saveId = Math.random().toString();
            
                menuBar.setSaveState("saving");
                console.debug("Waiting for reload before saving...", saveId);
                window.savingNowButStillWaitingForIframeReload = true;
                try {
                  await window.hardReloadOutputIframe(); // this updates the metadata and dependencies
                } catch(e) {
                  console.error(e);
                }
                window.savingNowButStillWaitingForIframeReload = false;
                console.debug("Finished waiting for save reload.", saveId);
            
                try {
            
                  // we use these below for lastModelTextSaved/lastOutputTemplateSaved - can't set them yet since we don't know if it will save successfully
                  let modelText = this.data.generator.modelText;
                  let outputTemplate = this.data.generator.outputTemplate;
            
                  let generatorMetaData = editor.generatorMetaData;
                  
                  let bodyData = {
                    email: this.store.data.user.email || "",
                    generator: this.data.generator,
                    generatorMetaData,
                    sessionToken: this.store.data.user.sessionToken || "",
                    lastKnownSaveTime: window.generatorLastSaveTime,
                  };
                  if(editKey !== null) {
                    bodyData.editKey = editKey;
                  }

                  let data = await fetch("/api/save", { method:'POST', body:JSON.stringify(bodyData), signal:AbortSignal.timeout?.(20*1000), headers:{ 'Content-Type': 'application/json' }}).then(r => r.json());
                  if(data.status === "stale") {
                    if(!confirm("It looks like you're editing an old version of this generator. You may have been editing it in a different browser tab. Note that you can access all versions using the 𝗯𝗮𝗰𝗸𝘂𝗽𝘀 button. Are you sure you want to continue?")) {
                      menuBar.setSaveState("unsaved");
                      return;
                    }
                    bodyData.forceSaveDespiteStaleness = true;
                    data = await fetch("/api/save", { method:'POST', body:JSON.stringify(bodyData), signal:AbortSignal.timeout?.(20*1000), headers:{ 'Content-Type': 'application/json' }}).then(r => r.json());
                  }
                  if(data.status === "captcha-needed") { // for 'anon' saves
                    bodyData.captchaToken = await getCaptchaToken();
                    data = await fetch("/api/save", { method:'POST', body:JSON.stringify(bodyData), signal:AbortSignal.timeout?.(20*1000), headers:{ 'Content-Type': 'application/json' }}).then(r => r.json());
                  }
            
                  if(data.status === "saved") {
                    window.generatorLastSaveTime = data.time;
            
                    if(originalLastEditorsChangeTime === window.lastEditorsChangeTime) {
                      window.modelTextEditor.markClean();
                      window.outputTemplateEditor.markClean();
                      menuBar.setSaveState("saved");
                    } else {
                      menuBar.setSaveState("unsaved");
                    }
            
                    // these are used as an extra check in doLocalGeneratorBackupIfNeeded to prevent backing up text that is already saved
                    window.lastModelTextSaved = modelText;
                    window.lastOutputTemplateSaved = outputTemplate;
                    
                    this.saveSuccessCallback();

                    if(editKey && !localStorage[`perchance_generatorEditKey_${this.data.generator.name}`] && !doNotAskToSaveEditPasswordFor.has(this.data.generator.name)) {
                      inMemoryGeneratorNameToEditKey[this.data.generator.name] = editKey;
                      if(confirm(`Your changes have been saved. You can click OK to save this password to your temporary browser storage, so you don't have to enter the password every time you make an edit. You should 𝗰𝗹𝗶𝗰𝗸 𝗰𝗮𝗻𝗰𝗲𝗹 if you're using a shared computer (e.g. at library). Note: If your cookies/storage for this site are cleared, your saved passwords will be cleared.`)) {
                        localStorage[`perchance_generatorEditKey_${this.data.generator.name}`] = editKey;
                      } else {
                        doNotAskToSaveEditPasswordFor.add(this.data.generator.name);
                      }
                    }
            
                  } else if(data.status === "created") {
                    window.generatorLastSaveTime = data.time;
            
                    if(this.store.data.user.loggedIn) {
                      this.userOwnsThisGenerator = true;
                      this.trigger("GeneratorOwnershipChange");
                      menuBar.refs.settingsButton.style.display = "block";
                    }

                    window.history.replaceState(null, null, `/${data.name}${window.location.hash}`);
                    this.data.generator.name = data.name;
                    window.generatorName = data.name;
                    menuBar.setSaveState("saved");
                    window.generatorPublicId = data.publicId;
                    this.saveSuccessCallback(data.name, data.publicId);
                    window.hardReloadOutputIframe();
            
                    menuBar.refs.statusMessage.innerHTML = "<b>(new generator created)</b>";
                    menuBar.refs.statusMessage.style.display = "inline-flex";
                    setTimeout(() => {
                      menuBar.refs.statusMessage.innerHTML = "";
                      menuBar.refs.statusMessage.style.display = "none";
                    }, 4000);

                    if(!this.store.data.user.loggedIn) {
                      inMemoryGeneratorNameToEditKey[data.name] = data.editKey;
                      prompt("New generator created. If you'd like to edit it in the future, you'll need to use the following password. Please copy and paste it somewhere safe:", data.editKey);
                      if(confirm(`You can click OK to save this password to your temporary browser storage, so you don't have to enter the password every time you make an edit. You should 𝗰𝗹𝗶𝗰𝗸 𝗰𝗮𝗻𝗰𝗲𝗹 if you're using a shared computer (e.g. at library). Note: If your cookies/storage for this site are cleared, your saved passwords will be cleared.`)) {
                        localStorage[`perchance_generatorEditKey_${data.name}`] = data.editKey;
                      } else {
                        doNotAskToSaveEditPasswordFor.add(data.name);
                      }
                    }
            
                  } else if(data.status === "invalid-edit-key") {
                    delete localStorage[`perchance_generatorEditKey_${this.data.generator.name}`];
                    menuBar.setSaveState("error");
                    setTimeout(() => { menuBar.setSaveState("unsaved"); }, 1000);
                    alert("The edit password was incorrect. Please try again.");
                  } else if(data.status === "session-token-error") {
            
                    this.logout();
                    this.openLoginModal();
                    menuBar.setSaveState("error");
                    setTimeout(() => { menuBar.setSaveState("unsaved"); }, 1000);
            
                  } else if(data.status === "too-big") {
                    menuBar.setSaveState("error");
                    alert("Hmm. There's too much text in this generator to save it to Perchance's database. :| Can you perhaps break it into several smaller generators that you import into the main one? Note that perchance.org/upload can be used to upload large amounts of data.");
                    setTimeout(() => { menuBar.setSaveState("unsaved"); }, 1000);
                  } else if(data.status === "too-many-requests") {
                    menuBar.setSaveState("error");
                    alert(`You're saving too quickly!${this.store.data.user.loggedIn ? " Please try again in a few seconds." : " If you're not saving quickly, then this 𝗰𝗼𝘂𝗹𝗱 𝗯𝗲 𝗰𝗮𝘂𝘀𝗲𝗱 𝗯𝘆 𝗮 𝗩𝗣𝗡 browser extension - try disabling it, or logging in."}`);
                    setTimeout(() => { menuBar.setSaveState("unsaved"); }, 1000);
                  } else {
                    console.error("Server error while saving:", data);
                    menuBar.setSaveState("error");
                    alert("Seems like there was a problem while trying to save your generator. This could be to do with your internet connection, but it could also be a temporary problem with the server. Please copy and paste your generator data (lists and HTML) to a safe place before leaving or refreshing the page, or you could lose your work. If you keep getting errors even after waiting a couple of minutes, please make a post on lemmy.world/c/perchance to see if others are experiencing the same thing. Remember that you can click the \"backups\" button in the top-right of the screen to download past versions of your generator. Before doing anything though, make sure you copy and paste your data to a safe place! Sorry for the trouble - this stuff happens sometimes :|");
                    setTimeout(() => { menuBar.setSaveState("unsaved"); }, 1000);
                  }
            
                  // track generator save times so we can cache bust if cloudflare cache puring is delayed (see code at bottom of index.html template)
                  if(data.status === "saved" || data.status === "created") {
                    let generatorSavedTimes = JSON.parse(localStorage["generatorSavedTimes"] || "{}");
                    generatorSavedTimes[this.data.generator.name] = Date.now();
                    // clear old save times (older than 3 months, since the cache definitely shouldn't last any longer than that, even if a manual purge isn't initiated):
                    generatorSavedTimes = Object.fromEntries( Object.entries(generatorSavedTimes).filter(e => e[1] > Date.now()-1000*60*60*24*30) );
                    localStorage["generatorSavedTimes"] = JSON.stringify(generatorSavedTimes);
                  }
                } catch(e) {
                  console.error("Request error while saving:");
                  console.debug(e);
                  menuBar.setSaveState("error");
                  setTimeout(() => { menuBar.setSaveState("unsaved"); }, 1000);
                }
              };

              
              this.userOwnsThisGenerator = undefined; // IMPORTANT: this must be undefined while we don't know - there is code which relies on this.

              let originalSessionTokenLastChangeTime = Number(localStorage.sessionTokenLastChangeTime || 0);
              let initStore = () => {
                this.store = new Store("app");
                if(!this.store.data.user) {
                  this.store.data.user = {loggedIn:false};
                  this.store.save();
                }
              }
              initStore();

              setInterval(() => { // in case they logged out/in in another tap
                if(originalSessionTokenLastChangeTime !== Number(localStorage.sessionTokenLastChangeTime || 0)) {
                  originalSessionTokenLastChangeTime = Number(localStorage.sessionTokenLastChangeTime || 0);
                  let wasLoggedIn = this.store.data.user.loggedIn;
                  initStore();
                  if(this.store.data.user.loggedIn) this.checkIfOwner();
                  if(wasLoggedIn !== this.store.data.user.loggedIn) this.trigger("LoginStateChange");
                }
              }, 3000);
            
              this.data.generator = generator;
              this.data.dependencies = dependencies;
              // this.update();
          
              this.appInteractionState = "view";

              if(this.store.data.user.loggedIn) {
                this.checkIfOwner();
              }

              // NOTE: This was originally in the editor, but that doesn't load until edit is clicked, so crawlers wouldn't see it, which is bad for gens with no $meta.title/description - google got very confused for a few weeks.
              let isFirstMetaUpdate = true;
              window.addEventListener("message", async (e) => {
                let origin = e.origin || e.originalEvent.origin;
                if(origin !== "https://null.perchance.org" && origin !== `https://${window.generatorPublicId}.perchance.org`) return;
                
                if(e.data.type === "metaUpdate") {
                  if(e.data._validation.generatorName !== app.data.generator.name) return; // <-- trying to stop weird Google crawler page title bug

                  // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                  // WARNING WARNING WARNING WARNING WARNING WARNING WARNING: If you change this at all, make sure to do a thorough check for XSS bugs.
                  // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

                  if(typeof e.data.html === "string") {
                    if(e.data.html.length < 100_000 && window.shouldLiftGeneratorHtmlFromEmbed) {
                      window.document.querySelector("#generator-description").innerHTML = DOMPurify.sanitize(e.data.html, {ALLOWED_TAGS: ['h1', 'h2', 'h3', 'h4', 'p', 'div', 'pre', 'button', 'i', 'b', 'a', 'ul', 'li', 'br', 'ol', 'hr'], ALLOWED_ATTR: ['href']});
                      if(!window.generatorCanLinkWithRelFollow) window.document.querySelectorAll("#generator-description a").forEach(a => a.rel="nofollow ugc");
                    }
                  }
                  if(typeof e.data.title === "string") {
                    if(!isFirstMetaUpdate) { // correct title is hardcoded in HTML on page load (and if dynamic, then as of writing a `fetch` call updates it.)
                      window.document.title = e.data.title + " ― Perchance" + (/(gener|\bai\b)/i.test(e.data.title) ? "" : " Generator");
                    }
                  }
                  isFirstMetaUpdate = false;
                }
              });

              window.menuBar.init();
              window.loginModal.init();
              window.accountModal.init();
              window.settingsModal.init();
              window.revisionsModal.init();

              if(window.location.hash === "#debugFreeze") {
                window.DEBUG_FREEZE_MODE = true;
            
                // we do this rather than setting the hash with location.hash because otherwise it essentially does a pushState behind the scenes
                let url = new URL(location.href);
                url.hash = "#edit";
                history.replaceState({}, "", url.href);
            
                this.appInteractionState = "edit";
                this.trigger("AppInteractionStateChange");
              } else if(window.location.hash === "#edit") {
                this.appInteractionState = "edit";
                this.trigger("AppInteractionStateChange");
              }

              if(this.appInteractionState === "edit") {
                this.goToEditMode();
              } else if(this.appInteractionState === "view") {
                this.goToViewMode();
              }
              this.attachKeyboardShortcuts();
            }
          };
        </script>

        <script>window.remoteResourcesLoaded = true;</script>
        <script>
          window.empty883974329 = "";
          window.modalFn1Name = "alert";
          window.rlFn1Name = `reload`;
          window.ftcFn1Name = "fetch";
          window.ap1pend1ChildFn2Name = "appendChild";
          window.doc34828272947 = "document";

          try { // adding try/catch here in case of future instances of this: https://lemmy.world/post/4913660
            window.generatorDependenciesData = window.js0nparse( decodeURI( document.querySelector("#imported-generators").innerText ) );
          } catch(e) {
            if(!location.href.includes("__generatorDependenciesCacheBust")) { // try a cache bust if we haven't already.
              let url = new URL(window.location.href);
              url.searchParams.set("__generatorDependenciesCacheBust", Math.random());
              window.location.href = url.href; 
              throw new Error("exiting script tag for dependency cache bust"); // since we can't `return` at top level
            } else { // if we've already tried bust, don't do it again, else it'll be a refresh loop
              window.generatorDependenciesData = [];
              let errorCode = 1;
              if(document.querySelector("#imported-generators").innerText === "") {
                errorCode = 2;
              } else {
                errorCode = 3;
                try {
                  decodeURI(document.querySelector("#imported-generators").innerText);
                } catch(e) {
                  errorCode = 4;
                }
              }
              alert(`There was some sort of bug parsing the imports of this generator. Buggy ad blockers can sometimes cause this. Please report this at https://lemmy.world/c/perchance and mention this error code: ${errorCode}`);
            }
          }

          if(location.href.includes("__generatorDependenciesCacheBust")) {
            let url = new URL(window.location.href);
            url.searchParams.delete("__generatorDependenciesCacheBust");
            let newPath = url.href.replace(/^https:\/\/perchance\.org/, "");
            history.replaceState({}, "", newPath);
          }

          window.iudu843975 = "bbb";
          if(iudu843975.includes("a")) {
            uruf8ufd.jfurwe8j;
          }
          if(iudu843975.includes("a")) {
            uruf8ufd.jfurwe8j;
          }
          if
          (`bbb`.includes("a")) {
            throw new Error("sus");
            uruf8ufd.jfurwe8j;
          }

          if(String.prototype.includes.toString().indexOf("return true") !== -1) {
            uruf8ufd.jfurwe8j;
          }
          
          if(window.generatorDependenciesData.filter(d => d.name === "ai-text-to-image-plugin" || d.name === "ai-text-plugin").length > 0 && !(window[`localStorage`]["app-storage"] || "").includes("sessionToken")) {
            setTimeout(() => {
              `script> (function() { window.adsAreShowing = async () => let adEl window[window.doc34828272947].querySelector(".ad-providers-ctn-el"); if(adEl && adEl.innerHTML.length > 5 adEl.offsetParent !== null adEl.offsetHeight 20) return true; // 20 adEl.querySelector("iframe")) false; }; already1AddedAd3vertIfNeeded window.addEventListener("message", function(e) origin e.origin || e.originalEvent.origin; For Chrome, the property is in event.originalEvent object. if(origin "https://null.perchance.org" https://{window.generatorPublicId}.perchance.org) return; } if(e.data.type === (usingAdPowered+Plugin) window.location.pathname '/text-to-image-plugin' '/ai-text-plugin') addAdvert(); }); setTimeout(() if(window.generatorData.imports.includes("ai-text-to-image-plugin") window.generatorData.imports.includes("ai-text-plugin")) }, 10); function addAdvert() if(already1AddedAd3vertIfNeeded) if(window.location.hash "#edit") window[window.ftcFn1Name]("/api/count?key=uaine"); if((window[local+Storage]["app-storage"] window[local+Storage]["app-storage"].includes("sessionToken")) navigator.webdriver) not for logged-in users and screenshot bot window[window.ftcFn1Name]("/api/count?key=uaineala"); adCtn createAdCtn(); while(!window[window.doc34828272947].querySelector("#main")) await new Promise(r setTimeout(r, 200)); ResizeObserver(function() window[window.doc34828272947].querySelector("#main").style.bottom (adCtn.offsetHeight + 8*2)+"px"; }).observe(adCtn); adProviderName "freestar"; if(window.location.hash.startsWith("#adProviderName=")) window.location.hash.slice("#adProviderName=".length); if(adProviderName sni{("")+("") (window._a3d8 ? "d*li-_?s" : "")}gel "freestar" "sovrn") console.debug("ad:", adProviderName); "")}gel; if(window.location.pathname "/fqruxwnngk") try to avoid multiple CMP popups, we choose one ad provider remember it: validadProviderNames ["freestar", "")}gel]; adProviderNameSetTime window[local+Storage].adProviderNameSetTime !isNaN(Number(window[local+Storage].adProviderNameSetTime))? Number(window[local+Storage].adProviderNameSetTime) daysBetweenRandomize 60; if(!window[local+Storage].adProviderName !validadProviderNames.includes(window[local+Storage].adProviderName) (adProviderNameSetTime Date.now()-adProviderNameSetTime 1000*60*60*24*daysBetweenRandomize)) window[local+Storage].adProviderName Math.random()  0.5 Date.now(); window[local+Storage].adProviderName; catch(e) console.error(e); "freestar") checkConsent() __tcfapi("getTCData", 2, (tcData, success) if(success) a tcData.purpose.consents[1]; b tcData.purpose.consents[10]; c tcData.purpose.legitimateInterests[10]; d tcData.eventStatus if((!a !b !c) & "cmpuishown") window.__tcfapi("displayConsentUi", {}); else window.userGaveAdConsent (async che1ckTCF() 100)); if(window.__tcfapi == undefined) setTimeout(che1ckTCF, 1000); checkConsent(); })(); adCtn.innerHTML div class="ad-providers-ctn-el" align="center" data-freestar-ad="__320x100 __970x90" id="perchanceorg_footer">/div>; window.freestar {}; window.freestar.queue []; window.freestar.config window.freestar.config.enabled_slots window.freestar.initCallback (window.freestar.config.enabled_slots.length 0) window.freestar.initCallbackCalled false window.freestar[newAd{ ""}Slots](window.freestar.config.enabled_slots) if(window.location.hash.includes("testCMP")) (inmobi choice): script window[window.doc34828272947].createElement("script"); script.src https://user-upload{ ""}s.perchance.org/file/63c85ff7ce3ecc0323e0bbd555078fad.js; window[window.doc34828272947].head.appendChild(script); while(!window.cmpScriptHasExecuted) 50)); script.dataset.cfasync "false"; script.async "https://a.pub.network/perchance-org/pubfig.min.js"; if(window.location.hash.includes("testAntiAdBlock")) anti -adblock: ""}s.perchance.org/file/01cea1291c0dbb7b775dc89a385d1d4a.js; window.freestar.config.enabled_slots.push({ placementName: "perchanceorg_footer", slotId: "perchanceorg_footer" "")}gel) this code needed ensure that load immediately after accepting cmp 'reconsider' window.addEventListener('adnginLoaderReady', function() __tcfapi('addEventListener', function(tcData, if(success (tcData.eventStatus 'tcloaded' 'useractioncomplete')) if(tcData.gdprApplies) __tcfapi('getVendorList', function(gvl, 'useractioncomplete') adngin.queue.push(function() adngin.cmd.startAuction(["responsive_banner"]); 'tcloaded') if(Object.keys(tcData.vendor.consents).length Object.keys(gvl.vendors).length Object.keys(tcData.purpose.consents).length Object.keys(gvl.purposes).length) id="adngin-responsive_banner-0">/div>; https://cdn.sni{("")+("") "")}gelweb.com/adengine/perchance.org/loader.js; setTimeout(async if(await window.adsAreShowing()) script.nonce "SEahoBjLmnC565bNlPFeWA"; "https://fundingchoicesmessages.google.com/i/pub-9885689965057708?ers=1"; {function signalGooglefcPresent() {if (!window.frames['googlefcPresent']) (window[window.doc34828272947].body) {const iframe window[window.doc34828272947].createElement('iframe'); iframe.style 'width: 0; height: border: none; z-index: -1000; left: -1000px; top: -1000px;'; iframe.style.display 'none'; iframe.name 'googlefcPresent'; window[window.doc34828272947].body .appendChild(iframe);} {setTimeout(signalGooglefcPresent, 0);signalGooglefcPresent();})(); 30000); window.b8473892 if(window[local+Storage]["app-storage"] (already done above - just defend against future edit mistakes) window[window.ftcFn1Name]("/api/count?key=abt"); if(!(await window.adsAreShowing())) window[window.ftcFn1Name]("/api/count?key=abpr" ); if(window[local+Storage].abpr "1") window[window.ftcFn1Name]("/api/count?key=abpr2"); window[local+Storage].abpr "1"; scri12pts ifra2mes coo1kies clasi window[window.ftcFn1Name](https://cdn.sni{("")+("") "")}gelweb.com/adengine/perchance.org/loader.js).then(r r.text()).then(t t.includes(dou{ ""}bleclick) t.includes("adengine") t.includes("adconsent") t.includes("GDPR")).catch(e false)) window[window.ftcFn1Name]("/api/count?key=abprbclas"); if([...window[window.doc34828272947].querySelectorAll("iframe")].filter(el el.src).map(el URL(el.src).hostname).find(n n[ends{ ""}With](dou{ ""}bleclick.net) ""}With]("onetag-sys.com") ""}With]("openx.net") ""}With](".google.com") ""}With](amazon-ad{''}system.com) ""}With]("rubiconproject.com") ""}With]("criteo.com") ""}With]("3lift.com") ""}With]("googlesyndication.com") ""}With]("connectad.io"))) window[window.ftcFn1Name]("/api/count?key=abprbclasi"); if(window.userGaveAdConsent window.gdprDoesNotApply) if(window.js0nparse(window[local+Storage].id5id_privacy "{}").id5_consent true) if(clasi coo1kies) window[window.ftcFn1Name]("/api/count?key=abprbclasiadgc"); if(scri12pts maybe auction failed or whatever !coo1kies) window[window.modalFn1Name](Your browser seems be blocking cookies, which preventing ad{ "s"} from being shown. You may need add an exception 'perchance.org' your cookie blocker 'incognito browsing' feature. Sometimes VPN antivirus software has as bonus feature.\n\nWhy are necessary? Well, Perchance free doesn't have by default, but creator of generator imported ad-powered plugin (e.g. text-to-image plugin). Plugins use expensive server resources can only exist thanks "s"}. This page will auto-refresh 60 seconds (apologies).); generally "s"}, "s"}.\n\nPlease help keep it turning off blocker. (apologies).\n\n(NOTE: If still aren't loading you even blocker, then also turn 'cookie' perchance.org on built-in cookie-blocking antivirus/incognito/VPN/etc if feature).); startWarnProcess(); window[window.ftcFn1Name]("/api/count?key=utoab"); 60000); 50); img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAtgAAABaAQMAAAC4+rO8AAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAANQTFRFXrP/I7IslgAAAB9JREFUeJztwYEAAAAAw6D5U1/gCFUBAAAAAAAAAHwDIFgAAVR0zeEAAAAASUVORK5CYII="/>; if(window.innerWidth 1400) div window[window.doc34828272947].createElement("div"); div.style.cssText position: absolute; right: font-family: sans-serif; background: #dedede; padding: 0.2rem 0.25rem; border-radius: 3px; cursor: pointer; color: #676767;; div.onclick window[window.modalFn1Name]('The funded via advertisements.'); div.id whyAd{ "s"}Button1El; div.innerHTML why "s"}?; adCtn.appendChild(div); 2000); window[window.doc34828272947].body .appendChild(adCtn); window.adCtn adCtn; abp|cookie|browser|async|ads|alert|ad{ ""}s; window.e7827462 100); /script> createAdCtn() maxHeight window.innerWidth 600 50 90; maxHeight; minHeight; 700) desktop minHeight mobile 100; 50; adCtn.style.cssText position:var(--does-not1-exist-98437593, fixed); bottom:8px; right:8px; left:8px; min-height:{minHeight}px; height:{maxHeight}px; overflow:hidden; text-align:center; display:flex; align-items:center; justify-content:center;; startWarnProcess() warnEl helperNotice ""; 800) div>a href="https://user-upload{ "s"}.perchance.org/file/06b63cbc807acaf79bc25f114bb0dda1.webp" target="_blank">click here/a> it's working don't understand/div>; warnEl.innerHTML += style="max-width:700px; margin:0 auto;">span style="font-weight:bold; color:#f60000;">the author plugin. pls ad/cookie free. 😳/span> {helperNotice "try Chrome Firefox working."}/div>; if(!window.adCtn) window[window.doc34828272947].body .appendChild(window.adCtn); window.adCtn.appendChild(warnEl); if(window[window.doc34828272947].querySelector(#whyAd{ ""}sButton1El)) window[window.doc34828272947].querySelector(#whyAd{ ""}sButton1El).style.display "none"; warnEl.remove(); window.adsAreShowing() !window.e7827462 !window.b8473892)) window.onbeforeunload undefined; window.location[rlFn1Name](); handler() if(!window.e7827462 !window.b8473892) deps window.generatorDependenciesData.map(d d.name); if(!deps.includes("ai-text-to-image-plugin") !deps.includes("ai-text-plugin")) setTimeout(handler, 10001);`;
              document.elementFromPoint;
              document.body.innerHTML;
              window[window.doc34828272947];
              window.alert;
              window.document;
              window.location;
              window["location"];
              "window[`onbefor${''}eunload`] = undefined;";
              'window[`onbeforeunload`] = undefined;';
              "window[`onbeforeunload`] = undefined;";
              "window[`location`][rlFn1Name]();";
              let a32 = window.alert;
              window.alert = function(...args) {
                a32.bind(window)(...args);
              };
              if(!window.alert.toString().includes("a32")) {
                throw new Error("hmm");
                uruf8ufd.jfurwe8j;
              }
              globalThis.da_Zz3qe33sEsZ1 = true;

              function aaa534535() {
                function createAdCtn() {
                  let adCtn = window[doc34828272947].createElement("div");
                  adCtn.id = "adCtn";
                  adCtn.style.cssText = `padding:4px 0; min-height:${window.advertHeight}px !important; max-height:${window.advertHeight}px !important; height:${window.advertHeight}px !important; overflow:hidden !important; text-align:center; display:flex; align-items:center; justify-content:center; position:relative;`;
                  return adCtn;
                }

                globalThis.hu_8urej4 = true;
                function startWarnProcess() {
                  let warnEl = window[doc34828272947].createElement("div");
                  let helperNotice = "";
                  if(window.innerWidth > 800) helperNotice = `<div><a href="https://user-uploads.perchance.org/file/06b63cbc807acaf79bc25f114bb0dda1.webp" target="_blank">click here</a> if it's still not working or if you don't understand</div>`;
                  warnEl.innerHTML += `<div style="max-width:700px; margin:0 auto;"><span style="font-weight:bold; color:#f60000;">the author of this generator has imported an ad-powered plugin. pls turn off your ad/cookie blocker to keep this free. this page will auto-refresh in 60 seconds 😳</span> ${helperNotice || "try Chrome or Firefox if it's not working."}</div>`;
                  if(!window.adCtn) {
                    window.adCtn = createAdCtn();
                    appEl.appendChild(window.adCtn);
                  } else {
                    window.adCtn.innerHTML = "";
                  }
                  window.adCtn.appendChild(warnEl);
                  if(window[doc34828272947].querySelector(`#whyAdsButton1El`)) {
                    window[doc34828272947].querySelector(`#whyAdsButton1El`).style.display = "none";
                  }

                  setTimeout(async () => {
                    warnEl.remove();
                    if(!window.adsAreShowing || !(await window.adsAreShowing() || !window.e7827462 || !window.b8473892)) {
                      window[`onbeforeunload`] = undefined;
                      if(window.userGaveAdConsent === false && window.gdprDoesNotApply === false) {
                        for(let key in localStorage) {
                          if(key.startsWith("perchance")) continue;
                          if(key === "pendingNotifications") continue;
                          if(typeof localStorage[key] !== "string") continue;
                          delete localStorage[key];
                        }
                      }
                      if(Number(localStorage.adPageReloadCount || 0) < 4) {
                        localStorage.adPageReloadCount = Number(localStorage.adPageReloadCount || 0)+1;
                        localStorage.lastAdPageReloadTime = Date.now();
                        window[`location`][rlFn1Name]();
                      }
                    }
                  }, 60001);
                  window.freestar?.analytics?.track?.toString().includes("track(")
                }

                {
                  async function handler() {
                    window.u8398201 = true;
                    if(!window.e7827462 || !window.b8473892) {
                      let deps = window.generatorDependenciesData.map(d => d.name);
                      if(!deps.includes("ai-text-to-image-plugin") && !deps.includes("ai-text-plugin")) return;
                      if(window[`localStorage`]["app-storage"] && window[`localStorage`]["app-storage"].includes("sessionToken") && window.js0nparse(window[`localStorage`]["app-storage"]).user[`sess${``}ionToken`] || window.sdfh929if738ths) return;

                      startWarnProcess();

                      await new Promise(r => setTimeout(r, 1000*40));
                      if(!window.adCtn || getCElement(window.adCtn).src?.includes?.("perchance.org")) {
                        let ms1g = "AI-powered generators on Perchance are entirely funded by ads. Please disable your ad blocker and reload the page. This page will auto-reload in 60 seconds." .replace(/_/g, "");
                        try { alert(ms1g); } catch(e) { window[doc34828272947].body.innerHTML = ms1g; };
                        setTimeout(() => {
                          window[`onbeforeunload`] = undefined;
                          if(Number(localStorage.adPageReloadCount || 0) < 4) {
                            localStorage.adPageReloadCount = Number(localStorage.adPageReloadCount || 0)+1;
                            localStorage.lastAdPageReloadTime = Date.now();
                            window[`location`][rlFn1Name]();
                          }
                        }, 60001);
                      }
                    }
                  }
                  setTimeout(handler, 30001); 
                }
                function getCElement(el) {
                  const rect = el.getBoundingClientRect();
                  return document.elementFromPoint(rect.left + rect.width / 2, rect.top + rect.height / 2);
                }
              }

              // window.shouldAddOutputBorderBottomInViewMode = true;
              // document.querySelector("#editorEl #output").style.borderBottom = "";
              
              app.init({
                generator: window.generatorData,
                dependencies: window.generatorDependenciesData,
              });
              window.successfulPageLoad = true;
            }, 0);
          } else {
            globalThis.da_Zz3qe33sEsZ1 = true;

            app.init({
              generator: window.generatorData,
              dependencies: window.generatorDependenciesData,
            });
            window.successfulPageLoad = true;
          }
        </script>
        
        <!-- <script async src="https://www.googletagmanager.com/gtag/js?id=G-YJWJRNESS5"></script> -->
        <script>
          setTimeout(() => { // dynamically add after delay to reduce initial lag on low-end mobile devices
            let script = document.createElement('script');
            script.async = true;
            script.src = "https://www.googletagmanager.com/gtag/js?id=G-YJWJRNESS5";
            document.body.appendChild(script);
          }, 1000*15);

          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());
          gtag('config', 'G-YJWJRNESS5');
          
          try {
            setTimeout( function() { // wait for init (we're not in any hurry)
              document.querySelector("#verify-account-button").addEventListener('click', function() {
                gtag("event", "signup", {});
              });
            }, 20*1000);
          } catch(e) { console.error(e); }
        </script>


        <script>
          window.downloadTextFile = (text, name) => {
            const a = document.createElement('a');
            const type = name.split(".").pop();
            a.href = URL.createObjectURL( new Blob([text], { type:`text/${type === "txt" ? "plain" : type}` }) );
            a.download = name;
            a.click();
          };
          window.diffStuff = {}; //patches, modelTextPatches and outputTemplatePatches get added to this object in revisions-modal.tag. It's hacky, but it'll do for now.
          window.diffStuff.openNewTabWithRevision = async function(revisionNumber, generatorName) {
            if(!window.diff_match_patch) {
              let script = document.createElement('script');
              script.src = "/lib/diff_match_patch.js";
              document.body.appendChild(script);

              while(!window.diff_match_patch) {
                await new Promise(r => setTimeout(r, 200));
              }
              window.dmp = new diff_match_patch();
              window.diffStuff._patchesToRevision = (patches, revisionNumber) => {
                if(revisionNumber > patches.length-1 || revisionNumber < 0) throw new Error(`revisionNumber ${revisionNumber} doesn't exist. max index of patches array = ${patches.length-1}`);
                let text = patches[0];
                if(revisionNumber === 0) return text;
                for(let n = 1; n < patches.length; n++) {
                  //text = JsDiff.applyPatch(text, patches[n]);
                  text = dmp.patch_apply(dmp.patch_fromText(patches[n]), text)[0];
                  if(text === false) throw new Error("JsDiff says that the patches aren't correct. some sort of corruption or incorrect formatting or mismatch");
                  if(n === revisionNumber) return text;
                }
              };
            }
            let modelText = this._patchesToRevision(this.modelTextPatches, revisionNumber);
            let outputTemplate = this._patchesToRevision(this.outputTemplatePatches, revisionNumber);

            // we used encodeURI to fix emojis problem with https://github.com/google/diff-match-patch, so we need to decode:
            modelText = decodeURI(modelText);
            outputTemplate = decodeURI(outputTemplate);

            let intro = "<<<<< this file contains your perchance lists first, and your HTML code underneath it >>>>>\n\n\n\n";
            window.downloadTextFile(intro+modelText+("\n".repeat(20))+outputTemplate+("\n".repeat(20)), `${generatorName}-revision-${revisionNumber}.txt`);
          };
        </script>


        <script>
          // a temporary hack to make sure search engines see links on generators page:
          if(document.location.pathname === "/generators") {
            var req = new XMLHttpRequest();
            req.overrideMimeType("application/json");
            req.open('GET', "/api/getGeneratorList", true);
            req.onload  = function() {
              var result = window.js0nparse(req.responseText);
              var div = document.createElement('div');
              if(window.canExecuteModernJavascript) {
                div.style.height = "0px";
                div.style.overflow = "hidden";
                div.style.position = "fixed";
              }
              div.innerHTML = "Here are the latest generators: "+result.generators.map(function(g){ return "<a href='/"+g.name+"'>"+g.name+"</a>"; }).join(", ")+".";
              document.body.appendChild(div);
            };
            req.send(null);
          }
        </script>
        
        <script>
          (async function() {
            await new Promise(r => setTimeout(r, 1000*20));
            if(!navigator["\u0077\u0065\u0062\u0064\u0072\u0069\u0076\u0065\u0072"]) {
              fetch(`https://perchance.org/api/cv?generatorName=${location.pathname.slice(1)}&isFromEmbed=0&__cacheBust=${Math.random()}`);
              document.addEventListener("visibilitychange", () => {
                if(document.visibilityState === "visible") {
                  fetch(`https://perchance.org/api/cv?generatorName=${location.pathname.slice(1)}&isFromEmbed=0&__cacheBust=${Math.random()}`);
                }
              });
            }
          })();
        </script>
        
        <script>
          (async function() {
            if(window.location.hash !== "#edit") {
              while(!document.querySelector("div.menu-item.new")) await new Promise(r => setTimeout(r, 20));
              let btnHtml = document.querySelector("div.menu-item.new").outerHTML;
              btnHtml = btnHtml.replace(/\/minimal#edit/g, "/ai-chat"); // "https://discord.gg/43qAQEVV9a"
              btnHtml = btnHtml.replace(/>new</g, ">ai chat<");
              btnHtml = btnHtml.replace(/menu-item new/g, "menu-item promo");
              btnHtml = btnHtml.replace(/➕/g, "🆕");
              let ctn = document.createElement("div");
              ctn.innerHTML = btnHtml;
              let btn = ctn.firstElementChild;
              let systemIsInDarkMode = !!(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches);
              if(systemIsInDarkMode) {
                btn.style.backgroundColor = "rgb(110 53 11)";
              } else {
                btn.style.backgroundColor = "#ffd4a4";
              }
              document.querySelector("div.menu-item.new").after(btn);
            }
          })();
        </script>

        <script>
          (async function() {

            async function sha256Text(text) {
              const msgUint8 = new TextEncoder().encode(text);                          
              const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8);          
              const hashArray = Array.from(new Uint8Array(hashBuffer));                    
              const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
              return hashHex;
            }

            async function sendNotification(data) {
              // safety code to prevent duplicate notifications (shouldn't be needed, but just in case):
              let hash = await sha256Text(JSON.stringify(data));
              if(!localStorage.alreadyRecentlySentNotificationHashes) localStorage.alreadyRecentlySentNotificationHashes = "";
              if(localStorage.alreadyRecentlySentNotificationHashes.includes(hash)) return;
              localStorage.alreadyRecentlySentNotificationHashes += ","+hash;
              if(localStorage.alreadyRecentlySentNotificationHashes.length > 2000) localStorage.alreadyRecentlySentNotificationHashes = localStorage.alreadyRecentlySentNotificationHashes.slice(-1000);
              
              let notification;
              if(Notification.permission === "granted") {
                notification = new Notification(data.title, {body:data.body});
              } else if(Notification.permission !== "denied") {
                const permission = await Notification.requestPermission();
                if(permission === "granted") {
                  notification = new Notification(title, options);
                }
              }
              return notification;
            }

            window.addEventListener("message", async function(e) {
              let origin = e.origin || e.originalEvent.origin; // For Chrome, the origin property is in the event.originalEvent object.
              if(origin !== "https://comments-plugin.perchance.org") {
                return;
              }
              if(e.data.type === "ensure-notification-permission") {
                if(Notification.permission !== "granted" && Notification.permission !== "denied") {
                  const permission = await Notification.requestPermission();
                  if(permission === "granted") {
                    console.debug("notification permission granted");
                  } else {
                    console.debug("notification permission denied");
                  }
                }
              } else if(e.data.type === "notification-request") {
                if(Notification.permission !== "denied") {
                  if(e.data.folderName.split("+")[0] === window.location.pathname.slice(1)) {
                    let notification = await sendNotification(e.data).catch(e => console.error(e));
                    if(notification) notification.onclick = function() { window.focus(); this.close(); };
                  } else {
                    console.debug("Pending notification:", e.data.title, e.data.body, e.data.folderName);
                    // we ideally want this notification to be triggered in an already-open tab of the relevant generator (because then the notification.onclick can focus that tab, rather than opening a duplicate of that tab), so we put the notification data in localStorage and wait a few seconds for the relevant tab to take it out, and if not, then we trigger it from this tab, and set onclick to open a new tab
                    if(!localStorage.pendingNotifications) localStorage.pendingNotifications = "[]";
                    let pendingNotifications;
                    try { pendingNotifications = window.js0nparse(localStorage.pendingNotifications); } catch(e) { console.error("failed to parse pending notifications", e); pendingNotifications = []; }
                    pendingNotifications.push(e.data);
                    localStorage.pendingNotifications = JSON.stringify(pendingNotifications);

                    // try to wake the target tab with a BroadcastChannel message (no idea if this helps, but can't hurt):
                    let broadcastChannel;
                    if(window.BroadcastChannel) {
                      broadcastChannel = new BroadcastChannel("generatorName:"+window.location.pathname.slice(1));
                      broadcastChannel.postMessage({type:"wake-up"});
                    }
                    
                    setTimeout(async function() {
                      if(broadcastChannel) broadcastChannel.close();
                      let pendingNotifications;
                      try { pendingNotifications = window.js0nparse(localStorage.pendingNotifications); } catch(e) { console.error("failed to parse pending notifications", e); pendingNotifications = []; }
                      let notificationData = pendingNotifications.find(d => d.time === e.data.time && d.folderName === e.data.folderName && d.title === e.data.title && d.body === e.data.body);
                      if(notificationData) {
                        let notification = await sendNotification(notificationData).catch(e => console.error(e));
                        if(notification) notification.onclick = function() { window.open(`/${notificationData.folderName.split("+")[0]}`, "_blank"); this.close(); };
                        pendingNotifications = pendingNotifications.filter(d => d !== notificationData);
                        localStorage.pendingNotifications = JSON.stringify(pendingNotifications);
                      }
                    }, 1000*7); // <-- 'pending notification' timeout length (before we give up and just sent the `Notification` from this tab)
                  }
                }
              }
            });

            (async function() {

              if(window.BroadcastChannel) {
                let channel = new BroadcastChannel("generatorName:"+window.location.pathname.slice(1));
                channel.onmessage = (event) => {
                  if(event.data.type === "wake-up") {
                    console.debug("received wake-up message at:", new Date().toString());
                  }
                };
              }

              while(1) {
                let generatorName = window.location.pathname.slice(1);
                try {
                  await new Promise(r => setTimeout(r, 1000*2)); // this wait time must be lower than the 'pending notification' timeout length above
                  // parse localStorage.pendingNotifications and trigger notification if generatorName matches folderName
                  if(!localStorage.pendingNotifications) localStorage.pendingNotifications = "[]";
                  let pendingNotifications;
                  try { pendingNotifications = window.js0nparse(localStorage.pendingNotifications); } catch(e) { console.error("failed to parse pending notifications", e); pendingNotifications = []; }
                  
                  let pendingNotificationsForThisGeneratorName = pendingNotifications.filter(n => n.folderName.split("+")[0] === generatorName);
                  for(let notificationData of pendingNotificationsForThisGeneratorName) {
                    let notification = await sendNotification(notificationData).catch(e => console.error(e));
                    if(notification) notification.onclick = function() { window.focus(); this.close(); };
                    pendingNotifications = pendingNotifications.filter(n => n !== notificationData);
                  }
                  localStorage.pendingNotifications = JSON.stringify(pendingNotifications);
                } catch(e) { console.error(e) }
              }
            })();

          })();
        </script>
        
        <script>
          (function() {

            function chec3kRes1Load(url, type) {
              return new Promise(resolve => {
                let el = document.createElement(type);
                el.src = url;
                el.onload = function() { resolve(true); };
                el.onerror = function() { resolve(false); };
                document['head'].appendChild(el);
              });
            }

            async function hasVariables() {
              return window.freestar || window._snigelConfig || window.quantserve || window.google_image_requests || window.gaData || window.__tcfapi || window.Criteo || window.__halo_loaded__ || await chec3kRes1Load("https://static.criteo.net/images/pixel.gif?ch=1", "img");
            }

            window.adsAreShowing = async () =>  {   
              let adEl = document.querySelector(".ad-providers-ctn-el");
              if(!adEl) return false;
              let hasIframe = adEl.querySelector("iframe");
              let hasAnalytics = await hasVariables() || (await chec3kRes1Load(`https://secure.quantserve.com/quant.js`, `script`));
              if(adEl && adEl.innerHTML.length > 5 && adEl.offsetParent !== null && adEl.offsetHeight > 20 && (hasIframe || hasAnalytics)) return true;
              // if(adEl && adEl.innerHTML.length > 5 && adEl.offsetParent !== null && adEl.offsetHeight > 20 && adEl.querySelector("iframe")) return true;
              return false;
            };

            let already1AddedAd3vertIfNeeded = false;
            window.addEventListener("message", function(e) {
              let origin = e.origin || e.originalEvent.origin; // For Chrome, the origin property is in the event.originalEvent object.
              if(origin !== "https://null.perchance.org" && origin !== `https://${window.generatorPublicId}.perchance.org`) {
                return;
              }
              
              if(e.data.type ==(`usingAdPoweredPlugin`) && window.location.pathname !== '/text-to-image-plugin' && window.location.pathname !== '/ai-text-plugin') {
                addAdvert();
              }
            });

            setTimeout(() => {
              if(window.generatorData.imports.includes("ai-text-to-image-plugin") || window.generatorData.imports.includes("ai-text-plugin")) {
                addAdvert();
              }
            }, 10);

            let addAdvert = async function() {
              if(already1AddedAd3vertIfNeeded) return;
              already1AddedAd3vertIfNeeded = true;

              if(window.location.hash !== "#edit") window.queuedStatCountKeys.add("uaine");

              if((window[`localStorage`]["app-storage"] && window[`localStorage`]["app-storage"].includes("sessionToken")) || navigator.webdriver) {
                return; // not for logged-in users and screenshot bot
              }

              if(window.location.hash !== "#edit") window.queuedStatCountKeys.add("uaineala");

              window.adCtn = createAdCtn();
              
              while(!document.querySelector("#main")) await new Promise(r => setTimeout(r, 200));

              window.pageWillDisplayAds = true;
              
              let adProviderName = "freestar"; 
              if(window.location.hash.startsWith("#adProviderName=")) adProviderName = window.location.hash.slice("#adProviderName=".length);
              if(adProviderName !== `snigel` && adProviderName !== "freestar" && adProviderName !== "sovrn") adProviderName = "freestar";
              if(window.location.pathname === "/ai-rpg") adProviderName = "snigel";
              console.debug("ad:", adProviderName);

              if(!window.___alreadyInitializedAdRefreshFocusBugStuff) {
                window.___alreadyInitializedAdRefreshFocusBugStuff = true;

                let gotFirstOutputIframeFocusMessage = false;
                window.lastKnownOutputIframeFocusTime = Date.now();
                window.addEventListener("message", function(e) {
                  if(e.origin !== "https://null.perchance.org" && e.origin !== `https://${window.generatorPublicId}.perchance.org`) return;

                  if(e.data.type === "outputIframeCurrentlyHasFocus") {
                    gotFirstOutputIframeFocusMessage = true;
                    window.lastKnownOutputIframeFocusTime = e.data.time;
                  }
                });
                
                // NEW FOCUS FIX:
                (async function() {
                  while(!gotFirstOutputIframeFocusMessage) await new Promise(r => setTimeout(r, 500));
                  document.querySelector("#appEl #output iframe").contentWindow.postMessage({type:"enableStrongFocusFix"}, "*");
                })();
                window.addEventListener("pointerdown", function(e) {
                  document.querySelector("#appEl #output iframe").contentWindow.postMessage({type:"pauseStrongFocusFix"}, "*");
                  setTimeout(() => e.target.focus(), 100); // wait a bit til focus fix is disabled, then grab focus back
                });

                // OLD FOCUS FIX (temporarily leaving for debugging/detection stuff):
                let lastUserCausedFocusActionTime = Date.now();
                window.addEventListener("pointerdown", function() {
                  lastUserCausedFocusActionTime = Date.now();
                });
                document.addEventListener("visibilitychange", () => {
                  if(document.visibilityState === "visible") {
                    lastUserCausedFocusActionTime = Date.now();
                  }
                });
                window.addEventListener("pageshow", () => {
                  lastUserCausedFocusActionTime = Date.now();
                });
                
                let iOSSafari = false;
                try {
                  let ua = window.navigator.userAgent;
                  let iOS = !!ua.match(/iPad/i) || !!ua.match(/iPhone/i);
                  let webkit = !!ua.match(/WebKit/i);
                  iOSSafari = iOS && webkit && !ua.match(/CriOS/i);
                  if(iOSSafari) window.queuedStatCountKeys.add("ios");
                  else window.queuedStatCountKeys.add("nonios");
                } catch(e) {
                  console.error("failed to detect iOS Safari", e);
                }

                if(window.innerWidth < 550) window.queuedStatCountKeys.add("mobile");
                else window.queuedStatCountKeys.add("desktop");

                window.addEventListener("focus", async function() {
                  if(!window.perchanceOutputIframeFinishedFirstLoad) return;
                  if(!gotFirstOutputIframeFocusMessage) return;
                  if(Date.now() - window.lastKnownOutputIframeFocusTime > 2000) return; // only if we know embed recenty had focus

                  await new Promise(r => setTimeout(r, 1));
                  if(Date.now()-lastUserCausedFocusActionTime > 1000) {
                    console.warn(`🚩🚩🚩 top-level frame got focus, but there was no pointerdown/pageshow event to have caused it. ${window.location.hash.startsWith("#disable_focus_fix") ? `𝗳𝗶𝘅 𝘄𝗮𝘀 𝗡𝗢𝗧 𝗮𝗽𝗽𝗹𝗶𝗲𝗱 𝗱𝘂𝗲 𝘁𝗼 #𝗱𝗶𝘀𝗮𝗯𝗹𝗲_𝗳𝗼𝗰𝘂𝘀_𝗳𝗶𝘅` : ""}`);
                    if(!window.queuedStatCountKeys.has("ffix")) {
                      window.queuedStatCountKeys.add("ffix");
                      if(iOSSafari) window.queuedStatCountKeys.add("ffixios");
                      
                      if(window.innerWidth < 550) window.queuedStatCountKeys.add("ffmobile");
                      else window.queuedStatCountKeys.add("ffdesktop");
                    }
                    if(!window.location.hash.startsWith("#disable_focus_fix")) {
                      
                      // EDIT: new 'strongFocusFix' approach doesn't need these lines - iframe can "pull" focus. See above.
                      // const iframe = document.querySelector("#appEl #output iframe");
                      // iframe.focus();
                      // iframe.contentWindow.focus();

                      // leaving this message for debugging even though it's not entirely accurate (see above EDIT):
                      console.warn("🚨🚨🚨🚨🚨 FOCUS FIX: RETURNED FOCUS TO IFRAME 🚨🚨🚨🚨🚨");
                    }
                  }
                });

                if(window.location.href.includes("ad-focus-bug-test")) {
                  setInterval(async () => {
                    window.focus();  
                    console.debug("top hasFocus1", document.hasFocus(), document.activeElement);
                    await new Promise(r => setTimeout(r, 100));
                    console.debug("top hasFocus2", document.hasFocus(), document.activeElement);
                  }, 7000);
                }
              }
              
              appEl.appendChild(adCtn);

              // to give loading priority to main page content:
              await new Promise(r => setTimeout(r, 7*1000)); 
              if(document.readyState !== 'complete') await new Promise(r => setTimeout(r, 5*1000)); 

              // CMP (inmobi choice):
              let cmpScript = document.createElement("script");
              cmpScript.src = `https://user-uploads.perchance.org/file/63c85ff7ce3ecc0323e0bbd555078fad.js`;
              document.head.appendChild(cmpScript);
              {
                let waitMs = 0;
                while(!window.cmpScriptHasExecuted) {
                  await new Promise(r => setTimeout(r, 100));
                  waitMs += 100;
                  if(waitMs > 15*1000) break;
                }
              }

              function checkConsent() {
                __tcfapi("getTCData", 2, (tcData, success) => {
                  if(success) {
                    if(tcData.gdprApplies === false) {
                      window.gdprDoesNotApply = true;
                    } else {
                      let a = tcData.purpose.consents[1];
                      let b = tcData.purpose.consents[10];
                      let c = tcData.purpose.legitimateInterests[10];
                      let d = tcData.eventStatus

                      if((!a || !b || !c) & d !== "cmpuishown") {
                        window.__tcfapi("displayConsentUi", 2, function () {});
                      } else {
                        window.userGaveAdConsent = true;
                      }
                    }
                  }
                });
              }
              (async function che1ckTCF() {
                await new Promise(r => setTimeout(r, 100));
                if(window.__tcfapi == undefined) setTimeout(che1ckTCF, 1000);
                else checkConsent();
              })();
              
              if(adProviderName === "freestar") {

                adCtn.innerHTML = `<div class="ad-providers-ctn-el" align="center" data-freestar-ad="__320x100 __970x90" id="perchanceorg_footer"></div>`;
                
                window.freestar = window.freestar || {};
                window.freestar.queue = window.freestar.queue || [];
                window.freestar.config = window.freestar.config || {};
                window.freestar.config.enabled_slots = [];
                window.freestar.initCallback = function () { (window.freestar.config.enabled_slots.length === 0) ? window.freestar.initCallbackCalled = false : window.freestar[`newAdSlots`](window.freestar.config.enabled_slots) }

                let script = document.createElement("script");
                script.dataset.cfasync = "false";
                script.async = true;
                script.src = "https://a.pub.network/perchance-org/pubfig.min.js";
                document.head.appendChild(script);
                
                if(window.location.hash.includes("testAntiAdBlock")) {
                  // anti -ad block:
                  let script = document.createElement("script");
                  script.async = true;
                  script.src = `https://user-uploads.perchance.org/file/01cea1291c0dbb7b775dc89a385d1d4a.js`;
                  document.head.appendChild(script);
                }
                
                window.freestar.config.enabled_slots.push({ placementName: "perchanceorg_footer", slotId: "perchanceorg_footer" });
                
              } else if(adProviderName == `snigel`) {
                
                window.snigelPubConf = {
                  adengine: {
                    sensitiveContent: false
                  },
                };

                // this code is needed to ensure that ad load immediately after accepting cmp 'reconsider'
                window.addEventListener('adnginLoaderReady', function() {
                  adngin.queue.push(function() { adngin.cmd.startAuction(["responsive_banner"]); });
                });
                adCtn.innerHTML = `<div class="ad-providers-ctn-el" id="adngin-responsive_banner-0"></div>`;

                let script = document.createElement("script");
                script.dataset.cfasync = "false";
                script.async = true;
                script.src = `https://cdn.snigelweb.com/adengine/perchance.org/loader.js`;
                document.head.appendChild(script);

                // setTimeout(async () => {
                //   if(await window.adsAreShowing()) return;
                //   let script = document.createElement("script");
                //   script.dataset.cfasync = "false";
                //   script.async = true;
                //   script.nonce = "SEahoBjLmnC565bNlPFeWA";
                //   script.src = "https://fundingchoicesmessages.google.com/i/pub-9885689965057708?ers=1";
                //   document.head.appendChild(script);
                //   (function() {function signalGooglefcPresent() {if (!window.frames['googlefcPresent']) {if (document.body) {const iframe = document.createElement('iframe'); iframe.style = 'width: 0; height: 0; border: none; z-index: -1000; left: -1000px; top: -1000px;'; iframe.style.display = 'none'; iframe.name = 'googlefcPresent'; document.body .appendChild(iframe);} else {setTimeout(signalGooglefcPresent, 0);}}}signalGooglefcPresent();})();
                // }, 30000);
              }

              setTimeout(() => {
                window.b8473892 = true;
                function adIframesExist() {
                  return [...document.querySelectorAll("iframe")].filter(el => el.src).map(el => new URL(el.src).hostname).find(n => n.endsWith(`doubleclick.net`) || n.endsWith("onetag-sys.com") || n.endsWith("openx.net") || n.endsWith(".google.com") || n.endsWith(`amazon-adsystem.com`) || n.endsWith("rubiconproject.com") || n.endsWith("criteo.com") || n.endsWith("3lift.com") || n.endsWith("googlesyndication.com") || n.endsWith("connectad.io"));
                }
                setTimeout(async () => {
                  if(window[`localStorage`]["app-storage"] && window.js0nparse(window[`localStorage`]["app-storage"]).user[`sess${``}ionToken`] || window.sdfh929if738ths) return; // not for logged-in users (already done above - just to defend against future edit mistakes)
                  window.queuedStatCountKeys.add("abt");
                  let aidjr3 = await window.adsAreShowing();
                  if(!aidjr3) {

                    let scri12pts = false;
                    let ifra2mes = false;
                    let coo1kies = false;

                    let clasi = false;
                    try {
                      if(await hasVariables() || await chec3kRes1Load(`https://secure.quantserve.com/quant.js`, "script")) {
                        window.queuedStatCountKeys.add("abprbclas");
                        scri12pts  = true;
                      }
                      if(adIframesExist()) {
                        window.queuedStatCountKeys.add("abprbclasi");
                        clasi = true;
                        ifra2mes = true;
                      }
                    } catch(e) {
                      console.error(e);
                    }

                    if(window.userGaveAdConsent || window.gdprDoesNotApply) {
                      coo1kies = true;
                    }

                    try {
                      if(window.js0nparse(window[`localStorage`].id5id_privacy || "{}").id5_consent === true) {
                        coo1kies = true;
                      }
                    } catch(e) {
                      console.error(e); 
                    }  

                    if(clasi && coo1kies) {
                      window.queuedStatCountKeys.add("abprbclasiadgc");
                    }

                    // if(scri12pts && ifra2mes && coo1kies) {
                    //   return; // maybe auction failed or whatever
                    // }

                    if(!ifra2mes) {
                      await new Promise(r => setTimeout(r, 30*1000));
                      if(adIframesExist()) {
                        ifra2mes = true;
                      }
                    }

                    if(scri12pts && ifra2mes) {
                      return;
                    }

                    window.queuedStatCountKeys.add("abpr");
                    if(window[`localStorage`].abpr === "1") {
                      window.queuedStatCountKeys.add("abpr2");
                    }
                    window[`localStorage`].abpr = ("1");

                    try {
                      if(scri12pts && ifra2mes && !coo1kies) {
                        window[window.modalFn1Name](`Your browser seems to be blocking cookies, which is preventing ads from being shown. You may need to add an exception for 'perchance.org' to your cookie blocker or 'incognito browsing' feature. Sometimes VPN and antivirus software has cookie blocking as a bonus feature.\n\nWhy are ads necessary? Well, Perchance is free and doesn't have ads by default, but the creator of this generator has imported an ad-powered plugin (e.g. the text-to-image plugin). Plugins that use expensive server resources can only exist thanks to ads. This page will auto-refresh in 60 seconds (apologies).`);
                      } else {
                        window[window.modalFn1Name](`Your browser seems to be blocking ads. Perchance is free and generally doesn't have ads, but the creator of this generator has imported an ad-powered plugin (e.g. the text-to-image plugin). Plugins that use expensive server resources can only exist thanks to ads.\n\nPlease help keep it free by turning off your ad blocker. This page will auto-refresh in 60 seconds (apologies).\n\n(NOTE: If ads still aren't loading for you even after turning off your ad blocker, then you may also need to turn off your 'cookie' blocker, and maybe add an exception for perchance.org on the built-in cookie-blocking of your antivirus/incognito/VPN/etc if it has that feature).`);
                      }
                    } catch(e) {

                    } 

                    startWarnProcess();
                  } else {
                    if(window[`localStorage`].abpr === "1") {
                      window.queuedStatCountKeys.add("utoab");
                    }
                  }
                }, 70001);
              }, 50);

              // setTimeout(async () => {
              //   if(!(await window.adsAreShowing())) {
              //     if(adProviderName == `snigel`) adngin.queue.push(function() { adngin.cmd.startAuction(["responsive_banner"]); });
              //   }
              // }, 30000);
              
              // adCtn.innerHTML = `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAtgAAABaAQMAAAC4+rO8AAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAANQTFRFXrP/I7IslgAAAB9JREFUeJztwYEAAAAAw6D5U1/gCFUBAAAAAAAAAHwDIFgAAVR0zeEAAAAASUVORK5CYII="/>`;
              
              if(window.innerWidth > 1400) {
                setTimeout(() => {
                  let div = document.createElement("div");
                  div.style.cssText = `position: absolute; right: 4px; top: 4px; font-family: sans-serif; background: #dedede; padding: 0.2rem 0.25rem; border-radius: 3px; cursor: pointer; color: #676767;`;
                  div.onclick = function() { window[window.modalFn1Name]('The creator of this generator has imported an ad-powered plugin (e.g. the text-to-image plugin). Plugins that use expensive server resources are funded via advertisements.'); };
                  div.id = `whyAdsButton1El`;
                  div.innerHTML = `why ads?`;
                  adCtn.appendChild(div);
                }, 2000);
              }
            }

          })();

          (async function() { 
            while(true) {
              try {
                let i = 0;
                while(true) {
                  await new Promise(r => setTimeout(r, 10000));
                  let deps = window.generatorDependenciesData.map(d => d.name);
                  if((window.adCtn && !getCElement(window.adCtn).src?.includes?.("perchance.org")) || window.js0nparse(window.localStorage["app-storage"] || "{}")?.user?.[`sess${``}ionToken`] || window.sdfh929if738ths) {
                    // 1;adngin.cmd.startAuction(["responsive_banner"]);
                    window.queuedStatCountKeys.add("abpsgp");
                    break;
                  }
                  i++;
                  if(i > 10) {
                    break;
                  }
                }
              } catch(e) {
                console.error(e);
              }
              await new Promise(r => setTimeout(r, 20*60*1000));
            }
          })();

          setTimeout(() => {
            `abp|cookie|browser|async|ads|alert|ads`;
            window.e7827462 = true;
          }, 100);
        </script>
        <script>
          function createAdCtn() {
            let adCtn = window[doc34828272947].createElement("div");
            adCtn.id = "adCtn";
            adCtn.style.cssText = `padding:4px 0; min-height:${window.advertHeight}px !important; max-height:${window.advertHeight}px !important; height:${window.advertHeight}px !important; overflow:hidden !important; text-align:center; display:flex; align-items:center; justify-content:center; position:relative;`;
            return adCtn;
          }

          globalThis.hu_8urej4 = true;
          function startWarnProcess() {
            let warnEl = window[doc34828272947].createElement("div");
            let helperNotice = "";
            if(window.innerWidth > 800) helperNotice = `<div><a href="https://user-uploads.perchance.org/file/06b63cbc807acaf79bc25f114bb0dda1.webp" target="_blank">click here</a> if it's still not working or if you don't understand</div>`;
            warnEl.innerHTML += `<div style="max-width:700px; margin:0 auto;"><span style="font-weight:bold; color:#f60000; font-size:80%;">the author of this generator has imported an ad-powered plugin. pls turn off your ad/cookie blocker to keep this free. this page will auto-refresh in 60 seconds 😳</span> ${helperNotice || "try Chrome or Firefox if it's not working."}</div>`;
            if(!window.adCtn) {
              window.adCtn = createAdCtn();
              appEl.appendChild(window.adCtn);
            } else {
              window.adCtn.innerHTML = "";
            }
            window.adCtn.appendChild(warnEl);
            if(window[doc34828272947].querySelector(`#whyAdsButton1El`)) {
              window[doc34828272947].querySelector(`#whyAdsButton1El`).style.display = "none";
            }

            setTimeout(async () => {
              warnEl.remove();
              let aidjr3 = await window.adsAreShowing();
              if(!aidjr3 || !window.e7827462 || !window.b8473892) {
                window[`onbeforeunload`] = undefined;
                if(window.userGaveAdConsent === false && window.gdprDoesNotApply === false) {
                  localStorage.clear();
                }
                if(Number(localStorage.adPageReloadCount || 0) < 4) {
                  localStorage.adPageReloadCount = Number(localStorage.adPageReloadCount || 0)+1;
                  localStorage.lastAdPageReloadTime = Date.now();
                  window[`location`][rlFn1Name]();
                }
              }
            }, 60001);
          }
        </script>
        <script>
          {
            async function handler() {
              window.u8398201 = true;
              if(!window.e7827462 || !window.b8473892) {
                let deps = window.generatorDependenciesData.map(d => d.name);
                if(!deps.includes("ai-text-to-image-plugin") && !deps.includes("ai-text-plugin")) return;
                if(window[`localStorage`]["app-storage"] && window[`localStorage`]["app-storage"].includes("sessionToken") && window.js0nparse(window[`localStorage`]["app-storage"]).user[`sess${``}ionToken`] || window.sdfh929if738ths) return;

                startWarnProcess();

                await new Promise(r => setTimeout(r, 1000*40));
                if(!window.adCtn || getCElement(window.adCtn).src?.includes?.("perchance.org")) {
                  let ms1g = "AI-powered generators on Perchance are entirely funded by ads. Please disable your ad blocker and reload the page. This page will auto-reload in 60 seconds." .replace(/_/g, "");
                  try { alert(ms1g); } catch(e) { window[doc34828272947].body.innerHTML = ms1g; };
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     window    [false ? '' : `z8473927`] = true;
                  setTimeout(() => {
                    window[`onbeforeunload`] = undefined;
                    if(Number(localStorage.adPageReloadCount || 0) < 4) {
                      localStorage.adPageReloadCount = Number(localStorage.adPageReloadCount || 0)+1;
                      localStorage.lastAdPageReloadTime = Date.now();
                      if(Number(localStorage.adPageReloadCount || 0) < 4) {
                        localStorage.adPageReloadCount = Number(localStorage.adPageReloadCount || 0)+1;
                        localStorage.lastAdPageReloadTime = Date.now();
                        window[`location`][rlFn1Name]();
                      }
                    }
                  }, 60001);
                }
              }
            }
            setTimeout(handler, 30001);
          }
          function getCElement(el) {
            const rect = el.getBoundingClientRect();
            return document.elementFromPoint(rect.left + rect.width / 2, rect.top + rect.height / 2);
          }
        </script>
        <script>
          let cachedAccessCodeForAdPoweredStuff = null;
          setInterval(() => {
            cachedAccessCodeForAdPoweredStuff = null;
          }, 10*60*1000);

          window.addEventListener("message", async function(e) {
            let origin = e.origin || e.originalEvent.origin
            if(origin !== "https://text-generation.perchance.org" && origin !== `https://image-generation.perchance.org`) {
              return;
            }
            
            if(e.data.type == `plsGibAccessCodeForAdPoweredStuff`) {
              let code;
              if(cachedAccessCodeForAdPoweredStuff) {
                code = cachedAccessCodeForAdPoweredStuff;
              } else {
                code = await fetch(`/api/getAccessCodeForAdPoweredStuff?__cacheBust=${Math.round(Date.now()/(1000*60*10))}`).then(r => r.text());
                cachedAccessCodeForAdPoweredStuff = code;
              }

              let deps = window.generatorDependenciesData.map(d => d.name);
              if(!deps.includes("ai-text-to-image-plugin") && !deps.includes("ai-text-plugin")) {
                e.source.postMessage({type:"okayYouMayHaveCodeForAdPoweredStuff♡", code}, origin); 
                return;
              }
              if(window.adCtn && await window.adsAreShowing() && window.adCtn && !getCElement(window.adCtn).src?.includes?.("perchance.org")) {
                e.source.postMessage({type:"okayYouMayHaveCodeForAdPoweredStuff♡", code}, origin); 
                return;
              }
              if(window[`localStorage`]["app-storage"] && window[`localStorage`]["app-storage"].includes("sessionToken") && window.js0nparse(window[`localStorage`]["app-storage"]).user[`sess${``}ionToken`] || window.sdfh929if738ths) {
                e.source.postMessage({type:"okayYouMayHaveCodeForAdPoweredStuff♡", code}, origin); 
                return;
              }
            }
          });
        </script>


        <style>
          .qc-cmp2-persistent-link {
            transform: scale(0.5);
            transform-origin: bottom right 60px;
          }
        </style>
        <script>
          (async function() {
            // Ensures that ads can't be displayed outside of their designated area, no matter what.

            if(window.location.hash.toLowerCase().includes("disable_misplaced_ad_removal")) return;

            while(!window.perchanceOutputIframeFinishedFirstLoad) await new Promise(r => setTimeout(r, 1000));

            if(!window.pageWillDisplayAds) await new Promise(r => setTimeout(r, 1000));
            if(!window.pageWillDisplayAds) await new Promise(r => setTimeout(r, 3000));
            if(!window.pageWillDisplayAds) await new Promise(r => setTimeout(r, 9000));
            if(!window.pageWillDisplayAds) await new Promise(r => setTimeout(r, 30000));
            if(!window.pageWillDisplayAds) return; 

            while(!window.adCtn) await new Promise(r => setTimeout(r, 1000));

            setInterval(() => {
              let el = document.querySelector("#ogy-ad-slot");
              if(el && !window.adCtn.contains(el)) el.remove();
            }, 2000);


            {
              let attempts = 0;
              let cmpButtonCheckInterval = setInterval(() => {
                if(window.adCtn.offsetHeight < 10) {
                  document.querySelector(".qc-cmp2-container").style.cssText = "pointer-events:none; opacity:0;";
                  clearInterval(cmpButtonCheckInterval);
                }
                attempts++;
                if(attempts > 30) clearInterval(cmpButtonCheckInterval);
              }, 1000);
            }


            {
              // Ensure adCtn style can't be changed:
              let lastStyleChangeTime = 0;
              const originalStyle = window.adCtn.getAttribute('style');
              const observer = new MutationObserver((mutations) => {
                mutations.forEach((mutation) => {
                  if(mutation.type === 'attributes' && mutation.attributeName === 'style') {
                    if(window.adCtn.getAttribute('style') !== originalStyle) {
                      if(Date.now()-lastStyleChangeTime > 1000) { // prevent possible infinite loops (setInterval below is a backup)
                        window.adCtn.setAttribute('style', originalStyle);
                        lastStyleChangeTime = Date.now();
                      }
                    }
                  }
                });
              });
              const config = { attributes:true, attributeFilter:['style'] };
              observer.observe(window.adCtn, config);
              setInterval(() => {
                if(window.adCtn.getAttribute('style') !== originalStyle) {
                  window.adCtn.setAttribute('style', originalStyle);
                }
              }, 2000);
            }

            let deletionsInLastFewSeconds = 0;
            setInterval(() => {
              deletionsInLastFewSeconds = 0;
            }, 3000);
            function deleteMisplacedAdElements() {
              if(deletionsInLastFewSeconds > 100) {
                window.queuedStatCountKeys.add("misplaced-ad-loop");
                return; // in case of infinite loop that would freeze the page (e.g. "fighting" with a modal that self-heals or something weird like that)
              }
              let points = getElementKeyPointsInset(document.querySelector("#outputIframeEl"), {insetFactor:0.2});
              points.push(...getElementKeyPointsInset(document.querySelector("#outputIframeEl"), {insetFactor:0.03}));
              let appEl = document.querySelector("#appEl"); 
              for(let {x, y} of points) {
                let elements = document.elementsFromPoint(x, y);
                for(let el of elements) {
                  if(el === document.body || el === document.documentElement) continue;
                  if(!document.body.contains(el)) continue; // in case it was deleted by a previous iteration of this current loop
                  if(appEl.contains(el)) continue;
                  if(el.nodeName !== "IFRAME" || !el.src.includes(".perchance.org")) {
                    let ancestor = el;
                    while(ancestor.parentElement !== document.body) ancestor = ancestor.parentElement;
                    if(appEl.contains(ancestor)) {
                      console.error("Tried to remove a child of appEl?");
                      continue; 
                    }
                    const className = ancestor.className || "";
                    const id = ancestor.id || "";
                    if(className.startsWith("cmp") || className.includes("-cmp") || className.includes("cmp2") || id.includes("cmp")) { // gdpr consent modal
                      continue;
                    }
                    if(id.includes("confiant")) { // ad report button
                      continue;
                    }
                    if(className.includes("created-captcha-widget-ctn")) {
                      continue;
                    }
                    ancestor.remove();
                    deletionsInLastFewSeconds++;
                    console.warn("🧹🧹🧹 Deleted misplaced element ancestor:", {el, ancestor});
                    window.queuedStatCountKeys.add("misplaced-ad");
                  }
                }
              } 
            }

            function insetPointTowardCenter(x, y, centerX, centerY, insetFactor) {
              return { x: x + (centerX - x) * insetFactor, y: y + (centerY - y) * insetFactor };
            }
            function getElementKeyPointsInset(element, opts={}) {
              const rect = element.getBoundingClientRect();
              const centerX = rect.left + rect.width / 2;
              const centerY = rect.top + rect.height / 2;
              const insetFactor = opts.insetFactor ?? 0.2; // default to 20% inset

              const topLeft = insetPointTowardCenter(rect.left, rect.top, centerX, centerY, insetFactor);
              const topRight = insetPointTowardCenter(rect.right, rect.top, centerX, centerY, insetFactor);
              const bottomRight = insetPointTowardCenter(rect.right, rect.bottom, centerX, centerY, insetFactor);
              const bottomLeft = insetPointTowardCenter(rect.left, rect.bottom, centerX, centerY, insetFactor);

              return [ topLeft, topRight, bottomRight, bottomLeft, { x: centerX, y: centerY }];
            }

            const observer = new MutationObserver(function(mutationsList, observer) {
              for(let mutation of mutationsList) {
                if(mutation.type === 'childList') {
                  mutation.addedNodes.forEach(node => {
                    if(node.nodeType === Node.ELEMENT_NODE) {
                      console.debug('Checking new potential ad element for misplacement:', node);
                      deleteMisplacedAdElements();
                      setTimeout(deleteMisplacedAdElements, 1000);
                      setTimeout(deleteMisplacedAdElements, 3000);
                      setTimeout(deleteMisplacedAdElements, 6000);
                    }
                  });
                }
              }
            });
            observer.observe(document.body, { childList: true, subtree: true });
          })();
        </script>

        <style>
          /* .ad-providers-ctn-el .__fs-ancillary {
            display: none !important;
          } */
        </style>

        <script>
          (async function() {
            if(window[`localStorage`]["app-storage"] && window[`localStorage`]["app-storage"].includes("sessionToken") && window.js0nparse(window[`localStorage`]["app-storage"]).user[`sess${``}ionToken`] || window.sdfh929if738ths) return; // not for logged-in users

            window.userGaveAdConsent = null;
            window.gdprDoesNotApply = null;
            let i = 0;
            while(!window.__tcfapi) {
              await new Promise(r => setTimeout(r, 1000));
              i++;
              if(i > 20) return;
            }
            window.__tcfapi('addEventListener', 2, function(tcData, success) {
              if(success && (tcData.eventStatus === 'tcloaded' || tcData.eventStatus === 'useractioncomplete')) {
                if(tcData.gdprApplies) {
                  __tcfapi('getVendorList', 2, function(gvl, success) {
                    if(success) {
                      if(Object.values(tcData.vendor.consents).filter(v => v).length > Object.keys(gvl.vendors).length/2 && Object.values(tcData.purpose.consents).filter(v => v).length === Object.keys(gvl.purposes).length) {
                        window.userGaveAdConsent = true;
                      } else if(Object.values(tcData.vendor.consents).filter(v => v).length == 0 && Object.values(tcData.purpose.consents).filter(v => v).length === 0) {
                        window.userGaveAdConsent = false; // reject
                      } else {
                        window.userGaveAdConsent = false; // partial
                      }
                    }
                  });
                } else {
                  window.gdprDoesNotApply = true;
                }
              } else {
                console.debug("User consent not available yet", tcData);
                if(tcData.gdprApplies === true) {
                  window.gdprDoesNotApply = false;
                }
                if(tcData.eventStatus === 'cmpuishown') {
                  window.gdprDoesNotApply = false;
                }
              }
            });
          })();
        </script>

        <style>
          @media (prefers-color-scheme: dark) {
            #whyAdsButton1El {
              background-color: #333537 !important;
              color: #a79f94 !important;
            }
          }
        </style>

        <script>
          setTimeout(function() {
            // Make the screen "cleaner" for the screenshot bot:
            if(navigator.webdriver) {
              document.querySelector("#menuBarEl").style.display = "none";
              // document.querySelector("#editorEl #main").style.top = "8px";
            }
          }, 10);
        </script>
        
        <script>
          window.addEventListener('pageshow', (event) => {
            if (event.persisted) {
              // bfcache doesn't persist edits in the text editor for some reason (doesn't store JS state?), so we need to reload:
              // window.location.reload(); // edit: for some reason this doesn't seem to be needed anymore - seems to be persisting properly? Maybe due to adding this event handler???
              // console.debug('@@@ This page was restored from the bfcache.');
            } else {
              // console.debug('@@@ This page was loaded normally.');
            }
          });
        </script>

        <script>
          (async function() {
            let checkCount = 0;
            let interval;
            interval = setInterval(() => {
              let a = `s(uc)[],ce,s[s,f(u,lP,a,g,e],Lo),[ad`.replace(/[ ,\]()\[]/g, "");
              let plsp = `in i(ti al,Pa[g eLo adSpi n)ne, r`.replace(/[ ,\]()\[]/g, "");
              if(window.remoteResourcesLoaded && !window[a]) {
                let div = document.createElement("div");
                let isAdPoweredPage = false;
                div.innerHTML = `<p>TL;DR: <b>Try turning off your ad blocker</b>.</p>

                <p>If you see this message, this page failed to load properly. One potential cause is that (as of writing) some ad blockers break <a href="https://github.com/uBlockOrigin/uAssets/blob/9effb98d0ab3be8bc320909144758a326e575ad8/filters/quick-fixes.txt#L160" target="_blank" data-ref="nofollow">basic</a><sup>1</sup> functionality of Perchance pages. This <b>includes pages that don't have any ads</b> - i.e. even "normal" (non-AI-powered) generators.</p>

                <p>Try turning off your browser's ad blocker for this page, and if that doesn't work then please report this problem at <a href="https://lemmy.world/c/perchance" target="_blank">lemmy.world/c/perchance</a>, since there may be some other cause.</p>

                <p style="opacity:0.55;">Note: Perchance only has ads on generators which require GPU resources - a tiny fraction of all pages on Perchance. Perchance has always been <u>completely</u> free, and I've been paying for server resources out of my own pocket since I built Perchance in 2017, but I can't do that for the newly-added AI plugins because they require many GPU servers and they're way too expensive. As of writing (Jan 20th 2023) ads still aren't enough to cover the GPU costs, but they help a lot.</p>

                <p style="opacity:0.55;"><sup>1 </sup><span style="font-size:85%;">The linked code makes your ad blocker extension override some code on this page such that when I (or Perchance generator authors) want to check if some text contains a word (a very basic/common programming operation), it'll always say that the text <i>does</i> contain the word, even when it doesn't. That makes it hard to write code that works correctly, and to make changes to existing code without breaking things.</span></p>`;
                div.style.cssText = "padding:1rem; font-size:120%; z-index:100000000;";
                try { document.querySelector("#" + plsp).remove(); } catch(e) {}
                document.body.appendChild(div);

                clearInterval(interval);
              }
              checkCount++;
              if(checkCount > 20) clearInterval(interval);
            }, 1000*2);
          })();
        </script>

        <script>
          setInterval(() => { 
            if(performance.memory && performance.memory.usedJSHeapSize > 0.8*performance.memory.jsHeapSizeLimit) {
              window.queuedStatCountKeys.add("emem");
            }
            if(document.querySelectorAll("iframe").length > 100) {
              window.queuedStatCountKeys.add("eemb");
              document.querySelectorAll("iframe").forEach(el => {
                let isPerchanceUrl = (el.src||"").split("?")[0].includes("perchance");
                if(!isPerchanceUrl && !document.querySelector("#appEl").contains(el)) {
                  console.warn("❗❗❗ REMOVED EXCESSIVE IFRAMES");
                  el.remove();
                }
              });
            }
          }, 1000*60);
        </script>

        <script>
          setInterval(() => {
            if(window.queuedStatCountKeys.size > 0) {
              window[window.ftcFn1Name](`/api/count?keys=${[...window.queuedStatCountKeys].join(",")}`);
              window.queuedStatCountKeys = new Set();
            }
          }, 1000*20);
        </script>
        
        
        <!-- use invisible `touch-action:none` divs to prevent accidental "drag down to refresh" behavior when scrolling up iframe (dragging finger down) but finger lands on the edge (outside frame) -->
        <!-- <div style="width: 19px;height: 100%;position: fixed;right: -10px;top: 35px;bottom: 0px;/* background: blue; */touch-action: none; z-index:9999999;"></div> -->
        <!-- <div style="width: 19px;height: 100%;position: fixed;left: -10px;top: 35px;bottom: 0px;/* background: blue; */touch-action: none; z-index:9999999;"></div> -->

        <div style="display:none;">
              <a href="/ai-chat" target="_blank" style="position:absolute;top:-200px;">AI Roleplay Chat / Chatbot</a>
              <a href="/ai-story-generator" target="_blank" style="position:absolute;top:-200px;">AI Story Writer</a>
              <a href="/ai-text-to-image-generator" target="_blank" style="position:absolute;top:-200px;">AI Image Generator</a>
              <a href="/ai-human-generator" target="_blank" style="position:absolute;top:-200px;">AI Human Generator</a>
              <a href="/ai-photo-generator" target="_blank" style="position:absolute;top:-200px;">AI Photo Generator</a>
              <a href="/ai-character-description" target="_blank" style="position:absolute;top:-200px;">AI Character Description Generator</a>
              <a href="/ai-text-generator" target="_blank" style="position:absolute;top:-200px;">AI Text Generator</a>
              <a href="/ai-poem-generator" target="_blank" style="position:absolute;top:-200px;">AI Poem Generator</a>
              <a href="/ai-lyrics-generator" target="_blank" style="position:absolute;top:-200px;">AI Lyrics Generator</a>
              <a href="/ai-fanfic-generator" target="_blank" style="position:absolute;top:-200px;">AI Fanfic Generator</a>
              <a href="/ai-character-chat" target="_blank" style="position:absolute;top:-200px;">AI Character Chat</a>
              <a href="/ai-story-outline" target="_blank" style="position:absolute;top:-200px;">AI Story Outline/Plot Generator</a>
              <a href="/ai-text-rewriter" target="_blank" style="position:absolute;top:-200px;">AI Text Rewriter</a>
              <a href="/ai-character-chat?char=ai-adventure" target="_blank" style="position:absolute;top:-200px;">AI Adventure Game</a>
              <a href="/ai-character-chat?char=story-writer" target="_blank" style="position:absolute;top:-200px;">AI Story Generator With Pictures</a>
              <a href="/ai-character-chat?char=coding-assistant" target="_blank" style="position:absolute;top:-200px;">AI Coding Helper</a>
              <a href="/ai-code-generator" target="_blank" style="position:absolute;top:-200px;">AI Code Generator</a>
              <a href="/ai-group-chat" target="_blank" style="position:absolute;top:-200px;">AI Group Chat</a>
              <a href="/ai-insult-generator" target="_blank" style="position:absolute;top:-200px;">AI Insult Generator</a>
              <a href="/ai-rap-lyrics-generator" target="_blank" style="position:absolute;top:-200px;">AI Rap Lyrics Generator</a>
              <a href="/ai-title-generator?type=chapter" target="_blank" style="position:absolute;top:-200px;">AI Chapter Name Generator</a>
              <a href="/ai-title-generator?type=funny-light-novel" target="_blank" style="position:absolute;top:-200px;">Silly Light Novel Title Generator</a>
              <a href="/ai-title-generator?type=caption" target="_blank" style="position:absolute;top:-200px;">AI Caption Generator Using Keywords</a>
              <a href="/ai-title-generator?type=book" target="_blank" style="position:absolute;top:-200px;">AI Book Title Generator Using Keywords</a>
              <a href="/ai-title-generator?type=sad-book" target="_blank" style="position:absolute;top:-200px;">AI Sad Book Title Generator</a>
              <a href="/ai-script-generator" target="_blank" style="position:absolute;top:-200px;">AI Script Generator</a>
              <a href="/ai-plot-generator" target="_blank" style="position:absolute;top:-200px;">AI Plot Generator</a>
            </div>

    <script defer="" src="https://static.cloudflareinsights.com/beacon.min.js/vcd15cbe7772f49c399c6a5babf22c1241717689176015" integrity="sha512-ZpsOmlRQV6y907TI0dKBHq9Md29nnaEIPlkf84rnaERnq6zvWvPUqr2ft8M1aS28oN72PdrCzSjY4U6VaAw1EQ==" data-cf-beacon="{&quot;rayId&quot;:&quot;8d8687684a2f666f&quot;,&quot;version&quot;:&quot;2024.10.4&quot;,&quot;serverTiming&quot;:{&quot;name&quot;:{&quot;cfExtPri&quot;:true,&quot;cfL4&quot;:true,&quot;cfSpeedBrain&quot;:true,&quot;cfCacheStatus&quot;:true}},&quot;token&quot;:&quot;fc685cb0ca0145b3acbca350b7d29943&quot;,&quot;b&quot;:1}" crossorigin="anonymous"></script>


</body></html>