System.register(["reactive-jsx", "src/external/typo.js", "src/external/focalStorage.js"], function (_export, _context) {
	"use strict";

	var _addSourceLocation, Typo, focalStorage;

	return {
		setters: [function (_reactiveJsx) {
			_addSourceLocation = _reactiveJsx.addSourceLocation;
		}, function (_srcExternalTypoJs) {
			Typo = _srcExternalTypoJs.default;
		}, function (_srcExternalFocalStorageJs) {
			focalStorage = _srcExternalFocalStorageJs.default;
		}],
		execute: function () {
			const __SystemJSRewritingHack = {};
			_recorder_._src_external_codemirror_spellcheck_js = _recorder_._src_external_codemirror_spellcheck_js || {};
			Object.defineProperty(_recorder_._src_external_codemirror_spellcheck_js, "getSuggestionBox", {
				get() {
					return getSuggestionBox;
				},

				set(thisIsVererySecretVariableName) {
					_export("getSuggestionBox", getSuggestionBox = thisIsVererySecretVariableName);

					return true;
				},

				enumerable: true,
				configurable: true
			});
			Object.defineProperty(_recorder_._src_external_codemirror_spellcheck_js, "startSpellCheck", {
				get() {
					return startSpellCheck;
				},

				set(thisIsVererySecretVariableName) {
					_export("startSpellCheck", startSpellCheck = thisIsVererySecretVariableName);

					return true;
				},

				enumerable: true,
				configurable: true
			});
			Object.defineProperty(_recorder_._src_external_codemirror_spellcheck_js, "saveIgnoreDict", {
				get() {
					return saveIgnoreDict;
				},

				set(thisIsVererySecretVariableName) {
					saveIgnoreDict = thisIsVererySecretVariableName;
					return true;
				},

				enumerable: true,
				configurable: true
			});
			Object.defineProperty(_recorder_._src_external_codemirror_spellcheck_js, "loadIgnoreDict", {
				get() {
					return loadIgnoreDict;
				},

				set(thisIsVererySecretVariableName) {
					loadIgnoreDict = thisIsVererySecretVariableName;
					return true;
				},

				enumerable: true,
				configurable: true
			});
			Object.defineProperty(_recorder_._src_external_codemirror_spellcheck_js, "loadDictLang", {
				get() {
					return loadDictLang;
				},

				set(thisIsVererySecretVariableName) {
					_export("loadDictLang", loadDictLang = thisIsVererySecretVariableName);

					return true;
				},

				enumerable: true,
				configurable: true
			});
			Object.defineProperty(_recorder_._src_external_codemirror_spellcheck_js, "current", {
				get() {
					return current;
				},

				set(thisIsVererySecretVariableName) {
					_export("current", current = thisIsVererySecretVariableName);

					return true;
				},

				enumerable: true,
				configurable: true
			});
			Object.defineProperty(_recorder_._src_external_codemirror_spellcheck_js, "loadDict", {
				get() {
					return loadDict;
				},

				set(thisIsVererySecretVariableName) {
					_export("loadDict", loadDict = thisIsVererySecretVariableName);

					return true;
				},

				enumerable: true,
				configurable: true
			});
			Object.defineProperty(_recorder_._src_external_codemirror_spellcheck_js, "Typo", {
				get() {
					return Typo;
				},

				set(thisIsVererySecretVariableName) {
					Typo = thisIsVererySecretVariableName;
					return true;
				},

				enumerable: true,
				configurable: true
			});
			Object.defineProperty(_recorder_._src_external_codemirror_spellcheck_js, "focalStorage", {
				get() {
					return focalStorage;
				},

				set(thisIsVererySecretVariableName) {
					focalStorage = thisIsVererySecretVariableName;
					return true;
				},

				enumerable: true,
				configurable: true
			});
			var dictionaries = {};

			_export("dictionaries", dictionaries);

			Object.defineProperty(_recorder_._src_external_codemirror_spellcheck_js, "dictionaries", {
				get() {
					return dictionaries;
				},

				set(thisIsVererySecretVariableName) {
					_export("dictionaries", dictionaries = thisIsVererySecretVariableName);

					return true;
				},

				enumerable: true,
				configurable: true
			});
			async function loadDict(name, aff, dic) {
				if (!dictionaries[name]) {
					dictionaries[name] = new Promise(async resolve => {
						var affData = await fetch(aff).then(r => r.text());
						var wordsData = await fetch(dic).then(r => r.text());
						var dictionary = new Typo(name, affData, wordsData);
						resolve(dictionary);
					});
				}
				return dictionaries[name];
			}

			_export("loadDict", loadDict);

			function current() {
				return loadDictLang("en_US");
			}

			_export("current", current);

			function loadDictLang(lang = "en_US") {
				return loadDict(lang, lively4url + `/src/external/dicts/${lang}.aff`, lively4url + `/src/external/dicts/${lang}.dic`);
			}

			_export("loadDictLang", loadDictLang);

			current

			// copied from kofifus/ Codemirror spellchecker with typo corrections
			// https://gist.github.com/kofifus/4b2f79cadc871a29439d919692099406


			();var spellCheckKey = "SpellcheckIngoreWords";
			Object.defineProperty(_recorder_._src_external_codemirror_spellcheck_js, "spellCheckKey", {
				get() {
					return spellCheckKey;
				},

				set(thisIsVererySecretVariableName) {
					spellCheckKey = thisIsVererySecretVariableName;
					return true;
				},

				enumerable: true,
				configurable: true
			});
			async function loadIgnoreDict() {
				return (await focalStorage.getItem(spellCheckKey)) || {};
			}

			async function saveIgnoreDict(dict) {
				return await focalStorage.setItem(spellCheckKey, dict);
			}

			var ignoreDict = _recorder_._src_external_codemirror_spellcheck_js.ignoreDict;

			_export("ignoreDict", ignoreDict);

			Object.defineProperty(_recorder_._src_external_codemirror_spellcheck_js, "ignoreDict", {
				get() {
					return ignoreDict;
				},

				set(thisIsVererySecretVariableName) {
					_export("ignoreDict", ignoreDict = thisIsVererySecretVariableName);

					return true;
				},

				enumerable: true,
				configurable: true
			});
			async function startSpellCheck(cm, typo) {
				// console.log("start spellcheck " + typo.dictionary)

				if (!cm || !typo) return; // sanity

				// ignoreDict = {}; // dictionary of ignored words
				if (!ignoreDict) {
					_recorder_._src_external_codemirror_spellcheck_js.ignoreDict = await loadIgnoreDict();
				}

				// Define what separates a word
				var rx_word = '!\'\"#$%&()*+,-./:;<=>?@[\\]^_`{|}~ ';

				if (cm.spellcheckOverlay) {
					// console.log("remove spellcheck overlay")
					cm.removeOverlay("spellcheck");
				}

				cm.spellcheckOverlay = {
					name: "spellcheck",
					token: function (stream, state) {

						var ch = stream.peek();
						var word = "";
						if (rx_word.includes(ch) || ch === '\uE000' || ch === '\uE001') {
							stream.next();
							return null;
						}

						while ((ch = stream.peek()) && !rx_word.includes(ch)) {
							word += ch;
							stream.next();
						}
						// cm.doc.findMarksAt(
						// var cm = that.editor


						if (!/[a-zöäüß]/i.test(word)) return null; // no letters

						// console.log("w", word, "stream ", stream)
						// var token = cm.getTokenAt(cm.doc.posFromIndex(stream.pos))
						// console.log("w", word, "type ", token.type)
						// if (token.type && token.type.match("url")) return null; 

						if (ignoreDict && ignoreDict[word]) return null;
						// console.log("check " + word + " in " + typo.dictionary + " " + typo.check(word))
						if (!typo.check(word)) return "spell-error"; // CSS class: cm-spell-error
					}
				};
				cm.addOverlay(cm.spellcheckOverlay);

				var box = document.getElementById('suggestBox');
				if (box) box.remove

				// initialize the suggestion box
				();var sbox = getSuggestionBox(typo);
				// console.log("register context menu spell checker")
				lively.removeEventListener("spellcheck", cm.getWrapperElement(), "contextmenu");
				lively.addEventListener("spellcheck", cm.getWrapperElement(), "contextmenu", e => {
					if (!e.target.classList.contains('cm-spell-error')) return;
					e.preventDefault();
					e.stopPropagation();
					sbox.suggest(cm, e);
					return false;
				});
			}

			_export("startSpellCheck", startSpellCheck);

			function getSuggestionBox(typo) {
				function sboxShow(cm, sbox, items, x, y, hourglass) {
					// console.log("get suggestion box " + typo.dictionary)
					let selwidget = sbox.children[0];

					var isSafari = navigator.vendor && navigator.vendor.indexOf('Apple') > -1 && navigator.userAgent && !navigator.userAgent.match('CriOS');
					let separator = !isSafari && (hourglass || items.length > 0); // separator line does not work well on safari

					let options = '';
					items.forEach(s => options += '<option value="' + s + '">' + s + '</option>');
					if (hourglass) options += '<option disabled="disabled">&nbsp;&nbsp;&nbsp;&#8987;</option>';
					if (separator) options += '<option style="min-height:1px; max-height:1px; padding:0; background-color: #000000;" disabled>&nbsp;</option>';
					options += `<option value="##ignoreall##">Ignore&nbsp;all&nbsp;${typo.dictionary}</option>`;

					let indexInParent = [].slice.call(selwidget.parentElement.children).indexOf(selwidget);
					selwidget.innerHTML = options;
					selwidget = selwidget.parentElement.children[indexInParent];

					let fontSize = window.getComputedStyle(cm.getWrapperElement(), null).getPropertyValue('font-size');
					selwidget.style.fontSize = fontSize;
					selwidget.size = selwidget.length;
					if (separator) selwidget.size--;
					selwidget.value = -1;

					// position widget inside cm
					let cmrect = cm.getWrapperElement().getBoundingClientRect();
					sbox.style.left = x + 'px';
					sbox.style.top = y - sbox.offsetHeight / 2 - document.body.parentElement.scrollTop + 'px';
					let widgetRect = sbox.getBoundingClientRect();
					if (widgetRect.top < cmrect.top) sbox.style.top = cmrect.top + 2 + 'px';
					if (widgetRect.right > cmrect.right) sbox.style.left = cmrect.right - widgetRect.width - 2 + 'px';
					if (widgetRect.bottom > cmrect.bottom) sbox.style.top = cmrect.bottom - widgetRect.height - 2 + 'px';
				}

				function sboxHide(sbox) {
					sbox.style.top = sbox.style.left = '-1000px';
					// typo.suggest(); // disable any running suggeations search
				}

				// create suggestions widget
				let sbox = document.getElementById('suggestBox');
				if (!sbox) {
					sbox = _addSourceLocation(document.createElement('div'), {
						file: 'https://lively-kernel.org/lively4/lively4-petrinet/src/external/codemirror-spellcheck.js',
						end: {
							column: 38,
							line: 158
						},
						start: {
							column: 9,
							line: 158
						}
					});
					sbox.style.zIndex = 100000;
					sbox.id = 'suggestBox';
					sbox.style.position = 'fixed';
					sboxHide(sbox);

					let selwidget = _addSourceLocation(document.createElement('select'), {
						file: 'https://lively-kernel.org/lively4/lively4-petrinet/src/external/codemirror-spellcheck.js',
						end: {
							column: 50,
							line: 164
						},
						start: {
							column: 18,
							line: 164
						}
					});
					selwidget.multiple = 'yes';
					sbox.appendChild(selwidget);

					sbox.suggest = async (cm, e) => {
						// e is the event from cm contextmenu event
						if (!e.target.classList.contains('cm-spell-error')) return false; // not on typo

						let token = e.target.innerText;
						if (!token) return false; // sanity

						// save cm instance, token, token coordinates in sbox
						sbox.codeMirror = cm;
						sbox.token = token;
						sbox.screenPos = { x: e.pageX, y: e.pageY };
						let tokenRect = e.target.getBoundingClientRect();
						let start = cm.coordsChar({ left: tokenRect.left + 1, top: tokenRect.top + 1 });
						let end = cm.coordsChar({ left: tokenRect.right - 1, top: tokenRect.top + 1 });
						sbox.cmpos = { line: start.line, start: start.ch, end: end.ch };

						// show hourglass
						// sboxShow(cm, sbox, [], e.pageX, e.pageY, true);

						var results = [];

						// async 
						typo.suggest(token, null, all => {
							//console.log('done');
							sboxShow(cm, sbox, results, e.pageX, e.pageY);
						}, next => {
							//console.log('found '+next);
							results.push(next);
							sboxShow(cm, sbox, results, e.pageX, e.pageY, true);
						});

						// non async 
						// sboxShow(cm, sbox, typo.suggest(token), e.pageX, e.pageY);

						e.preventDefault();

						return false;
					};

					sbox.onmouseout = e => {
						let related = e.relatedTarget ? e.relatedTarget.tagName : null;
						if (related !== 'SELECT' && related !== 'OPTION') sboxHide(sbox);
					};

					selwidget.onchange = e => {
						sboxHide(sbox);
						let cm = sbox.codeMirror,
						    correction = e.target.value;
						if (correction == '##ignoreall##') {

							ignoreDict[sbox.token] = true;
							saveIgnoreDict(ignoreDict);
							cm.setOption('maxHighlightLength', --cm.options.maxHighlightLength + 1); // ugly hack to rerun overlays
						} else {
							cm.replaceRange(correction, { line: sbox.cmpos.line, ch: sbox.cmpos.start }, { line: sbox.cmpos.line, ch: sbox.cmpos.end });
							cm.focus();
							cm.setCursor({ line: sbox.cmpos.line, ch: sbox.cmpos.start + correction.length });
						}
					};

					document.body.appendChild(sbox);
				}

				return sbox;
			}

			_export("getSuggestionBox", getSuggestionBox);
		}
	};
});