System.register(['reactive-jsx', 'src/components/widgets/lively-morph.js', 'src/external/highlight.js', 'src/client/graphics.js', 'src/client/contextmenu.js', 'src/client/syntax.js', 'src/client/morphic/component-loader.js', 'src/client/ContextJS/src/contextjs.js', 'src/client/favorites.js', 'src/client/files.js', 'src/client/strings.js', 'src/client/clipboard.js', 'utils', 'src/client/viewnav.js', 'src/external/upndown.js'], function (_export, _context) {
  "use strict";

  var _addSourceLocation, _attributeExpression, _childExpression, _attributeStringLiteral, _childElement, _childText, _children, _attributes, _element, Morph, highlight, pt, ContextMenu, SyntaxChecker, components, cop, Favorites, files, Strings, Clipboard, fileEnding, replaceFileEndingWith, updateEditors, ViewNav, Upndown;

  return {
    setters: [function (_reactiveJsx) {
      _addSourceLocation = _reactiveJsx.addSourceLocation;
      _attributeExpression = _reactiveJsx.attributeExpression;
      _childExpression = _reactiveJsx.childExpression;
      _attributeStringLiteral = _reactiveJsx.attributeStringLiteral;
      _childElement = _reactiveJsx.childElement;
      _childText = _reactiveJsx.childText;
      _children = _reactiveJsx.children;
      _attributes = _reactiveJsx.attributes;
      _element = _reactiveJsx.element;
    }, function (_srcComponentsWidgetsLivelyMorphJs) {
      Morph = _srcComponentsWidgetsLivelyMorphJs.default;
    }, function (_srcExternalHighlightJs) {
      highlight = _srcExternalHighlightJs.default;
    }, function (_srcClientGraphicsJs) {
      pt = _srcClientGraphicsJs.pt;
    }, function (_srcClientContextmenuJs) {
      ContextMenu = _srcClientContextmenuJs.default;
    }, function (_srcClientSyntaxJs) {
      SyntaxChecker = _srcClientSyntaxJs.default;
    }, function (_srcClientMorphicComponentLoaderJs) {
      components = _srcClientMorphicComponentLoaderJs.default;
    }, function (_srcClientContextJSSrcContextjsJs) {
      cop = _srcClientContextJSSrcContextjsJs;
    }, function (_srcClientFavoritesJs) {
      Favorites = _srcClientFavoritesJs.default;
    }, function (_srcClientFilesJs) {
      files = _srcClientFilesJs.default;
    }, function (_srcClientStringsJs) {
      Strings = _srcClientStringsJs.default;
    }, function (_srcClientClipboardJs) {
      Clipboard = _srcClientClipboardJs.default;
    }, function (_utils) {
      fileEnding = _utils.fileEnding;
      replaceFileEndingWith = _utils.replaceFileEndingWith;
      updateEditors = _utils.updateEditors;
    }, function (_srcClientViewnavJs) {
      ViewNav = _srcClientViewnavJs.default;
    }, function (_srcExternalUpndownJs) {
      Upndown = _srcExternalUpndownJs.default;
    }],
    execute: function () {
      const __SystemJSRewritingHack = {};
      _recorder_._src_components_tools_lively_container_js = _recorder_._src_components_tools_lively_container_js || {};
      Object.defineProperty(_recorder_._src_components_tools_lively_container_js, 'Morph', {
        get() {
          return Morph;
        },

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

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_tools_lively_container_js, 'highlight', {
        get() {
          return highlight;
        },

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

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_tools_lively_container_js, 'pt', {
        get() {
          return pt;
        },

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

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_tools_lively_container_js, 'ContextMenu', {
        get() {
          return ContextMenu;
        },

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

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_tools_lively_container_js, 'SyntaxChecker', {
        get() {
          return SyntaxChecker;
        },

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

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_tools_lively_container_js, 'components', {
        get() {
          return components;
        },

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

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_tools_lively_container_js, 'cop', {
        get() {
          return cop;
        },

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

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_tools_lively_container_js, 'Favorites', {
        get() {
          return Favorites;
        },

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

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_tools_lively_container_js, 'files', {
        get() {
          return files;
        },

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

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_tools_lively_container_js, 'Strings', {
        get() {
          return Strings;
        },

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

        enumerable: true,
        configurable: true
      });

      let ScopedScripts = _recorder_._src_components_tools_lively_container_js.ScopedScripts; // lazy load this... #TODO fix #ContextJS #Bug actual stack overflow
      Object.defineProperty(_recorder_._src_components_tools_lively_container_js, 'ScopedScripts', {
        get() {
          return ScopedScripts;
        },

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

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_tools_lively_container_js, 'Clipboard', {
        get() {
          return Clipboard;
        },

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

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_tools_lively_container_js, 'updateEditors', {
        get() {
          return updateEditors;
        },

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

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_tools_lively_container_js, 'replaceFileEndingWith', {
        get() {
          return replaceFileEndingWith;
        },

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

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_tools_lively_container_js, 'fileEnding', {
        get() {
          return fileEnding;
        },

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

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_tools_lively_container_js, 'ViewNav', {
        get() {
          return ViewNav;
        },

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

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_tools_lively_container_js, 'Upndown', {
        get() {
          return Upndown;
        },

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

        enumerable: true,
        configurable: true
      });


      /*MD
      
      ![](lively-container.drawio)
      
      MD*/

      class Container extends Morph {

        get target() {
          return this.childNodes[0];
        }
        /*MD ## Setup MD*/
        initialize() {

          // this.shadowRoot.querySelector("livelyStyle").innerHTML = '{color: red}'

          // there seems to be no <link ..> tag allowed to reference css inside of templates
          // files.loadFile(lively4url + "/templates/livelystyle.css").then(css => {
          //   this.shadowRoot.querySelector("#livelySt\yle").innerHTML = css
          // })
          if (!this.getAttribute("mode")) {
            this.setAttribute("mode", "show");
          }

          this.windowTitle = "Browser";
          if (this.isSearchBrowser) {
            this.windowTitle = "Search Browser";
          }

          this.contentChangedDelay = (() => {
            this.checkForContentChanges();
          }).debounce(1000);

          lively.addEventListener("Container", this, "mousedown", evt => this.onMouseDown(evt));

          lively.html.addDeepMousePressed(this.get("#back"), () => this.history(), (evt, url) => {
            this.unwindAndFollowHistoryUntil(url);
          });
          lively.html.addDeepMousePressed(this.get("#forward"), () => this.forwardHistory(), (evt, url) => {
            this.unwindAndFollowForwardHistoryUntil(url);
          });

          this.addEventListener("extent-changed", function (evt) {
            if (this.target) {
              this.target.dispatchEvent(new CustomEvent("extent-changed"));
            }
          });

          let path, edit;
          if (!this.useBrowserHistory()) {
            path = this.getAttribute("src");
            edit = this.getAttribute("mode") == "edit";
          }
          this.viewOrEditPath(path, edit

          // #TODO very ugly... I want to hide that level of JavaScript and just connect "onEnter" of the input field with my code
          );var input = this.get("#container-path");
          input.addEventListener("keyup", evt => {
            if (evt.code == "Enter") {
              this.onPathEntered(input.value);
            }
            if (evt.altKey && evt.code == "ArrowDown") {
              this.get("lively-container-navbar").focusFiles();
            }
          });
          this.get("#fullscreenInline").onclick = e => this.onFullscreen(e);

          this.registerButtons();

          this.addEventListener('contextmenu', evt => this.onContextMenu(evt), false);
          this.addEventListener('keydown', evt => this.onKeyDown(evt));
          this.setAttribute("tabindex", 0);
          this.addEventListener('click', evt => this.onClick(evt));
          this.hideCancelAndSave();

          if (this.getAttribute("controls") == "hidden") {
            this.hideControls();
          }
          this.withAttributeDo("leftpane-flex", value => this.get("#container-leftpane").style.flex = value);
          this.withAttributeDo("rightpane-flex", value => this.get("#container-rightpane").style.flex = value);

          this.addEventListener("editorbacknavigation", evt => {
            this.onEditorBackNavigation(evt);
          });
        }
        /*MD ## Navigation MD*/
        // #private
        viewOrEditPath(path, edit) {
          if (path) {
            if (edit) {
              this.livelyContentLoaded = this.editFile(path);
            } else {
              this.livelyContentLoaded = this.followPath(path);
            }
          }
        }

        // #important #api
        async followPath(path) {
          if (path.toString().match(/^https?:\/\//)) {
            path = this.normalizeURL(path);
          }

          if (this.unsavedChanges()) {
            if (!(await lively.confirm("You will lose unsaved changes, continue anyway?"))) {
              return;
            }
          }

          if (path.match(/^#/)) {
            // just anchor navigation
            return this.scrollToAnchor(path);
          }

          var m = path.match(/start\.html\?load=(.*)/);
          if (m) {
            return this.followPath(m[1]);
          }

          try {
            var options = await fetch(path, { method: "OPTIONS" }).then(r => r.json());
          } catch (e) {}
          // no options... found

          // this check could happen later
          if (path.match(/^https?:\/\//) && !path.match("https://lively4") && !path.match(/http:?\/\/localhost/) && !path.match(window.location.host) && !path.match("https://www.draw.io/")) {
            if (!options) {
              return window.open(path);
            }
          }
          if (options && options.donotfollowpath) {
            fetch(path // e.g. open://my-component
            );return;
          }

          var lastPath = _.last(this.history());
          if (lastPath !== path) {
            if (lastPath && path && path.startsWith(lastPath) && lastPath.match(/\.md\/?$/)) {
              // we have a #Bundle here... and the navigation is already in the history
            } else if (lastPath && path && path.replace(/\/index\.((html)|(md))$/, "") == lastPath.replace(/\/?$/, "")) {
              // we have a index file redirection here...
            } else {
              this.history().push(path);
            }
          }

          var opts = "";
          if (this.useBrowserHistory() && this.isFullscreen()) {
            opts = "&fullscreen=true";
          }

          if (this.isEditing() && (!path.match(/\/$/) || path.match(/\.((md)|(l4d))\//))) {
            if (this.useBrowserHistory()) window.history.pushState({ followInline: true, path: path }, 'view ' + path, window.location.pathname + "?edit=" + path + opts);
            return this.setPath(path, true).then(() => this.editFile());
          } else {
            if (this.useBrowserHistory()) window.history.pushState({ followInline: true, path: path }, 'view ' + path, window.location.pathname + "?load=" + path + opts);
            // #TODO replace this with a dynamic fetch
            return this.setPath(path);
          }
        }

        getPath() {
          return encodeURI(this.shadowRoot.querySelector("#container-path").value);
        }

        // #important
        async setPath(path, donotrender) {

          this.get('#container-content').style.display = "block";
          this.get('#container-editor').style.display = "none";

          if (this.viewNav) {
            lively.setPosition(this.get("#container-root"), pt(0, 0));
            this.viewNav.disable();
          }

          this.windowTitle = path.replace(/.*\//, "");
          if (!path) {
            path = "";
          }
          var isdir = path.match(/.\/$/);

          var url;
          if (path.match(/^https?:\/\//)) {
            url = new URL(this.normalizeURL(path));
            // url.pathname = lively.paths.normalize(url.pathname);
            path = "" + url;
          } else if (path.match(/^[a-zA-Z]+:\/\//)) {
            url = new URL(path);
            var other = true;
          } else {
            path = lively.paths.normalize(path);
            url = "https://lively4" + path;
          }

          // check if our file is a directory
          var options = await fetch(url, { method: "OPTIONS" }).then(r => r.json()).catch(e => {});
          if (!isdir && !other) {
            if (options && options.type == "directory") {
              isdir = true;
            }
            // console.log("[container] isdir " + isdir)
          }
          if (!path.match(/\/$/) && isdir) {
            path = path + "/";
          }

          var container = this.get('#container-content');
          // don't scroll away whe reloading the same url
          if (this.getPath() == path) {
            this.preserveContentScroll = this.get("#container-content").scrollTop;
          }

          var markdown = this.get("lively-markdown");
          if (markdown && markdown.get) {
            // #TODO how to dynamically test for being initialized?
            var presentation = markdown.get("lively-presentation");
            if (presentation && presentation.currentSlideNumber) {
              this.lastPage = presentation.currentSlideNumber();
            }
            this.wasContentEditable = markdown.contentEditable == "true";
          }

          this.setPathAttributeAndInput(path);

          var anchorMatch = path.match(/^(https?\:\/\/[^#]*)(#.+)/);
          if (anchorMatch) {
            path = anchorMatch[1];
            var anchor = anchorMatch[2];
            console.log("path " + path);
            console.log("anchor " + anchor);
            this.anchor = anchor;
          } else {
            this.anchor = null;
          }

          // this.clear(); // don't clear here yet... makes browsing more stable


          container.style.overflow = "auto";

          url = this.getURL();

          if (!url.toString().match(/^https?:\/\//)) {
            var resolvedURL = lively.swxURL(url);
          } else {
            resolvedURL = url;
          }

          this.content = "";

          this.showNavbar();

          // console.log("set url: " + url);
          this.sourceContent = "NOT EDITABLE";
          var render = !donotrender;
          // Handling directories


          // Handling files
          this.lastVersion = null; // just to be sure
          var renderTimeStamp = Date.now // #Idean, this is clearly a use-case for #COP, I have to refactor this propagate this dynamical context asyncronously #AsyncContextJS
          ();this.renderTimeStamp = renderTimeStamp;

          var format = files.getEnding(path);
          if (url.protocol == "search:") {
            format = "html";
          }
          if (path.match(/\?html/)) {
            format = "html";
          }

          if (isdir) {
            // return new Promise((resolve) => { resolve("") });
            if (!options || !options["index-available"]) {
              return this.listingForDirectory(url, render, renderTimeStamp);
            } else {
              format = "html"; // e.g. #Bundle 
            }
          }
          // ACM #Hack... #TODO -> find out when content is HTML
          if (format == "cfm") {
            format = "html";
          }

          if (files.isPicture(format)) {
            if (render) {
              fetch(resolvedURL // cache bust IMG resources.... otherwise the SWX is not asked by the browser
              );return this.appendHtml("<img style='max-width:100%; max-height:100%' src='" + resolvedURL + "'>", renderTimeStamp);
            } else return;
          } else if (files.isVideo(format)) {
            if (render) return this.appendHtml(`<video autoplay controls><source src="${resolvedURL}" type="video/${format}"></video>`, renderTimeStamp);else return;
          } else if (files.isAudio(format)) {
            if (render) return this.appendHtml(`<audio controls src="${resolvedURL}"></audio>`, renderTimeStamp);else return;
          } else if (format == "pdf") {
            if (render) return this.appendHtml('<lively-pdf overflow="visible" src="' + resolvedURL + '"></lively-pdf>', renderTimeStamp);else return;
          }

          var headers = {};
          if (format == "html") {
            headers["content-type"] = "text/html"; // maybe we can convice the url to return html
          }

          if (url.toString().match(lively4url)) {
            headers["forediting"] = true;
          }

          return fetch(url, {
            method: "GET",
            headers: headers
          }).then(resp => {

            this.clear(); // could already be filled again...


            this.lastVersion = resp.headers.get("fileversion");
            this.contentType = resp.headers.get("content-type");
            if (this.contentType && this.contentType.match("image")) {
              this.appendHtml("<img style='max-width:100%; max-height:100%' src='" + resolvedURL + "'>", renderTimeStamp);
              return;
            }

            // if (this.contentType.match("text/html"))  {
            //   format = "html"  
            // }


            // console.log("[container] lastVersion " +  this.lastVersion)

            // Handle cache error when offline
            if (resp.status == 503 || resp.status == 403) {
              format = 'error';

              // print error early...
              this.getContentRoot().appendChild(_element('div', _attributes(), _children(_childText('\n            '), _childElement(_element('h2', _attributes(), _children(_childText('\n              '), _childElement(_element('span', _attributes(_attributeStringLiteral('style', 'color: darkred')), _children(_childExpression(resp.status), _childText(' '), _childExpression(resp.statusText), _childText(' ')), {
                file: 'https://lively-kernel.org/lively4/lively-connectors/src/components/tools/lively-container.js',
                end: {
                  column: 82,
                  line: 384
                },
                start: {
                  column: 14,
                  line: 384
                }
              })), _childText('\n            ')), {
                file: 'https://lively-kernel.org/lively4/lively-connectors/src/components/tools/lively-container.js',
                end: {
                  column: 17,
                  line: 385
                },
                start: {
                  column: 12,
                  line: 383
                }
              })), _childText('\n            '), _childElement(_element('a', _attributes(_attributeStringLiteral('href', ''), _attributeExpression('click', async evt => {
                evt.preventDefault();
                var GitHub = await System.import("src/client/github.js").then(m => m.default);
                var gh = new GitHub();
                await gh.loadCredentials();
                window.lively4github = gh;
              })), _children(_childText('maybe loginto github?')), {
                file: 'https://lively-kernel.org/lively4/lively-connectors/src/components/tools/lively-container.js',
                end: {
                  column: 42,
                  line: 392
                },
                start: {
                  column: 12,
                  line: 386
                }
              })), _childText('\n          ')), {
                file: 'https://lively-kernel.org/lively4/lively-connectors/src/components/tools/lively-container.js',
                end: {
                  column: 16,
                  line: 393
                },
                start: {
                  column: 10,
                  line: 382
                }
              }));
            }

            return resp.text();
          }).then(content => {

            console.log("setPath content " + url);

            this.content = content;
            this.showNavbar();

            if (format == "html" || this.contentType.match("text/html")) {
              this.sourceContent = content;
              if (render) return this.appendHtml(content), renderTimeStamp;
            } else if (format == "md") {
              this.sourceContent = content;
              if (render) return this.appendMarkdown(content, renderTimeStamp);
            } else if (format == "livelymd") {
              this.sourceContent = content;
              if (render) return this.appendLivelyMD(content, renderTimeStamp);
            } else if (format == "csv") {
              this.sourceContent = content;
              if (render) return this.appendCSV(content, renderTimeStamp);
            } else if (format == "error") {
              this.sourceCountent = content;
              if (render) {
                return this.appendHtml(`
            <h2>
              <span style="color: darkred">Error: </span>${content}
            </h2>
          `, renderTimeStamp);
              }
            } else if (format == "bib") {
              this.sourceContent = content;
              if (render) {
                return this.appendHtml('<lively-bibtex src="' + url + '"></lively-bibtex>', renderTimeStamp);
              }
            } else if (format == "dot") {
              this.sourceContent = content;
              if (render) {

                return this.appendHtml(`<graphviz-dot><script type="graphviz">${content}</script></graphviz-dot>`, renderTimeStamp);
              } else return;
            } else if (format == "xhtml") {
              this.sourceContent = content;
              if (render) {
                return this.appendHtml('<lively-iframe style="position: absolute; top: 0px;left: 0px;" navigation="false" src="' + url + '"></lively-iframe>', renderTimeStamp);
              }
            } else if (format == "xml" || format == "drawio") {
              this.sourceContent = content;
              if (render && content.match(/^\<mxfile/)) {
                return this.appendHtml(`<lively-drawio src="${resolvedURL}"></<lively-drawio>`, renderTimeStamp);
              }
            } else {
              if (content) {
                if (content.length > 1 * 1024 * 1024) {
                  if (render) return this.appendHtml("file size to large", renderTimeStamp);
                } else {
                  this.sourceContent = content;
                  if (render) return this.appendHtml("<pre><code>" + content.replace(/</g, "&lt;") + "</code></pre>", renderTimeStamp);
                }
              }
            }
          }).then(() => {
            this.dispatchEvent(new CustomEvent("path-changed", { url: this.getURL() }));
            this.updateFavInfo();
          }).catch(function (err) {
            console.log("Error: ", err);
            lively.notify("ERROR: Could not set path: " + path, "because of: ", err);
          });
        }

        useBrowserHistory() {
          return this.getAttribute("load") == "auto";
        }

        history() {
          if (!this._history) this._history = [];
          return this._history;
        }

        forwardHistory() {
          if (!this._forwardHistory) this._forwardHistory = [];
          return this._forwardHistory;
        }
        /*MD ## Getter / Setter MD*/

        getSourceCode() {
          var editor = this.get("#editor");
          if (!editor) return "";
          return editor.currentEditor().getValue();
        }

        getBaseURL() {
          return this.getURL().toString().replace(/[#?].*/, "");
        }

        getContentRoot() {
          // #Design #Lively4 The container should hide all its contents. The styles defined here should not affect others.

          // return this // #TODO only reason.. interacting with Halo and drag and drop into container...

          return this.get('#container-root'); // #TODO fix halo interactrion with this hidden content!

          // #BUT #TODO Blockly and connectors just work globally...
          // but we do not use blockly and connectors any more...
          // return this;
        }

        getDir() {
          return this.getPath().replace(/[^/]*$/, "");
        }

        getURL() {
          var path = this.getPath();
          if (!path) return;
          if (files.isURL(path)) {
            return new URL(path);
          }if (path.match(/^[a-zA-Z]+:\/\//)) {
            return new URL(path);
          } else {
            return new URL("https://lively4/" + path);
          }
        }

        async getEditor(editorType) {
          editorType = editorType || this.currentEditorType || "lively-editor";
          this.currentEditorType = editorType;

          var container = this.get('#container-editor');

          var livelyEditor = container.querySelector("lively-image-editor, lively-editor, babylonian-programming-editor");

          if (livelyEditor && livelyEditor.localName != editorType) {
            livelyEditor.remove();
            livelyEditor = null;
          }

          if (livelyEditor) return Promise.resolve(livelyEditor);

          livelyEditor = await _addSourceLocation(lively.create(editorType, container), {
            file: 'https://lively-kernel.org/lively4/lively-connectors/src/components/tools/lively-container.js',
            end: {
              column: 61,
              line: 538
            },
            start: {
              column: 25,
              line: 538
            }
          });
          livelyEditor.id = "editor";

          if (livelyEditor.awaitEditor) {
            // console.log("[container] opened editor")
            livelyEditor.hideToolbar();
            await livelyEditor.awaitEditor();
            var livelyCodeMirror = livelyEditor.livelyCodeMirror();
            if (!livelyCodeMirror) throw new Error("Could not initialalize lively-editor");
            if (livelyCodeMirror.tagName == "LIVELY-CODE-MIRROR") {
              await new Promise(resolve => {
                if (livelyCodeMirror["editor-loaded"]) {
                  resolve // the livelyEditor was very quick and the event was fired in the past
                  ();
                } else {
                  livelyCodeMirror.addEventListener("editor-loaded", resolve);
                }
              });
            }

            livelyCodeMirror.enableAutocompletion();
            livelyCodeMirror.getDoitContext = () => this.getDoitContext();

            livelyCodeMirror.doSave = text => {
              if (livelyCodeMirror.tagName !== "LIVELY-CODE-MIRROR") {
                this.onSave(); // CTRL+S does not come through...
              }
            };

            livelyCodeMirror.editor.on("cursorActivity", cm => {
              this.onEditorCursorActivity(cm);
            });
          }
          return livelyEditor;
        }

        getOtherContainers(editing = false) {
          var url = this.getURL();
          if (!url) return;
          return document.body.querySelectorAll("lively-container").filter(ea => {
            var otherURL = ea.getURL();
            return otherURL && ea.isEditing() == editing && otherURL.pathname == url.pathname && otherURL.host == url.host;
          });
        }

        getDoitContext() {
          if (this.getURL().pathname.match(/.*\.md/)) {
            var url = this.getURL();
            debugger;
            var otherContainer = this.getOtherContainers()[0];
            var markdown = otherContainer && otherContainer.get("lively-markdown");
            var script = markdown && markdown.get("lively-script");

            return script;
          }

          return window.that;
        }

        getLivelyCodeMirror() {
          var livelyEditor = this.get('lively-editor');
          return livelyEditor && livelyEditor.get && livelyEditor.get('#editor');
        }

        setPathAttributeAndInput(path) {
          this.setAttribute("src", path);
          this.get('#container-path').value = decodeURI(path);
        }

        // #private let's do it the hard way
        asyncGet(selector, maxtime) {
          maxtime = maxtime || 10000;
          var startTime = Date.now();
          return new Promise((resolve, reject) => {
            var check = () => {
              var found = this.get(selector);
              if (found) resolve(found);else if (Date.now() - startTime > maxtime) reject();else setTimeout(check, 100);
            };
            check();
          });
        }

        /*MD ## Testing MD*/

        async isTemplate(url) {
          var filename = url.replace(/[#?].*/, "").toString().replace(/.*\//, "" // #Idea #Refactor Extract getFilename, add "filename" to URL class 
          );var foundTemplate = await lively.components.searchTemplateFilename(filename);
          return url == foundTemplate;
        }

        isFullscreen() {
          return this.get("#container-navigation").style.display == "none";
        }

        isEditing() {
          return this.getAttribute("mode") == "edit";
        }

        contentIsTemplate(sourceCode) {
          return this.getURL().pathname.match(/.*html/) && sourceCode.match(/<template/);
        }

        contentIsEditable() {
          var url = this.getURL();
          return url && url.pathname && url.pathname.match(/\.html$/) || this.getURL().pathname.match(/\.md$/);
        }

        /*MD ## Modules MD*/

        reloadModule(url) {
          console.log("reloadModule " + url);
          var urlString = url.toString();
          lively.unloadModule(urlString);
          return System.import(urlString).then(m => {
            lively.notify({
              title: "Loaded " + url, color: "green" });
            this.resetLoadingFailed();
          }, error => {
            this.loadingFailed(url.toString().replace(/.*\//, ""), error);
          });
        }

        async loadTestModule(url) {
          var testRunner = document.body.querySelector("lively-testrunner");
          if (testRunner) {
            try {
              console.group("run test: " + this.getPath());
              var scrollContainer = testRunner.get(".container");
              var scrollTop = scrollContainer && scrollContainer.scrollTop; // preserve scroll during update
              await testRunner.clearTests();
              await testRunner.resetMocha();
              await lively.reloadModule(url.toString(), true);
              await System.import(url.toString());
              console.log("RUN TESTS:");
              await testRunner.runTests();
              await lively.sleep(100);
              if (scrollContainer) scrollContainer.scrollTop = scrollTop;
            } finally {
              console.groupEnd("run test: " + this.getPath());
            }
          } else {
            lively.notify("no rest-runner to run " + url.toString().replace(/.*\//, ""));
          }
        }

        async loadModule(url) {
          return lively.reloadModule("" + url, true).then(module => {
            lively.notify("", "Module " + url + " reloaded!", 3, null, "green");

            this.resetLoadingFailed();
          }, err => {
            this.loadingFailed(url, err);
          });
        }

        resetLoadingFailed() {
          // that.resetLoadingFailed()
          // System.import(urlString)
          var urlString = this.getURL().toString();

          // #TODO #babel6refactoring
          if (lively.modules) {
            if (urlString.match(/\.js$/)) {
              var m = lively.modules.module(urlString);
            }
          }
          this.lastLoadingFailed = false;
          var b = this.get("#apply");if (b) b.style.border = "";
        }

        loadingFailed(moduleName, err) {
          this.lastLoadingFailed = err;
          this.get("#apply").style.border = "2px solid red";

          lively.notify({
            title: "Error loading module " + moduleName,
            text: err.toString().slice(0, 200),
            color: "red",
            details: err });
          console.error(err);
        }

        async openTemplateInstance(url) {
          var name = url.toString().replace(/.*\//, "").replace(/\.((html)|(js))$/, "");
          var comp = await lively.openComponentInWindow(name);
          if (comp.livelyExample) comp.livelyExample(); // fill in with example content
        }
        /*MD ## Navigation Hisotry MD*/
        unwindAndFollowHistoryUntil(urlInHistory) {
          var url = "nourl";
          while (url && url !== urlInHistory) {
            url = this.history().pop();
            this.forwardHistory().push(url);
          }
          this.followPath(url);
        }

        unwindAndFollowForwardHistoryUntil(urlInHistory) {
          var url = "nourl";
          while (url && url !== urlInHistory) {
            url = this.forwardHistory().pop();
            this.history().push(url);
          }
          this.followPath(url);
        }
        /*MD ## File Operations MD*/

        async deleteFile(url, urls) {
          lively.notify("delelteFile " + url);
          if (!urls || !urls.includes(url)) {
            urls = [url]; // clicked somewhere else
          }
          if (!urls) urls = [url];
          var names = urls.map(ea => decodeURI(ea.replace(/\/$/, "").replace(/.*\//, "")));
          if (await lively.confirm("<b>Delete " + urls.length + " files:</b><br>" + "<ol>" + names.map(ea => "<li>" + ea + "</li>").join("") + "</ol>")) {
            for (let url of urls) {
              var result = await fetch(url, { method: 'DELETE' }).then(r => {
                if (r.status !== 200) {
                  lively.error("Could not delete: " + url);
                }
                r.text();
              });
            }
            this.get("#container-leftpane").update();

            this.setAttribute("mode", "show");
            this.setPath(url.replace(/\/$/, "").replace(/[^/]*$/, ""));
            this.hideCancelAndSave();

            lively.notify("deleted " + names);
          }
        }

        async renameFile(url) {
          url = "" + url;
          var base = url.replace(/[^/]*$/, "");
          var name = url.replace(/.*\//, "");

          var newName = await lively.prompt("rename", name);
          if (!newName) {
            lively.notify("cancel rename " + name);
            return;
          }
          var newURL = base + newName;
          if (newURL != url) {
            await files.moveFile(url, newURL);

            this.setPath(newURL);
            this.hideCancelAndSave();

            lively.notify("moved to " + newURL);
          }
        }

        async newFile(path = "", type = "md") {
          var content = "here we go....";
          var ending = type;
          if (type == "drawio") {
            ending = "drawio";
            content = await fetch(lively4url + "/media/drawio.xml").then(r => r.text());
          }

          path = path.replace(/[^/]*$/, "") + "newfile." + ending;

          var fileName = await lively.prompt('Please enter the name of the file', path, async dialog => {
            // select the filename in the path...
            await lively.sleep(100 // wait for the new file
            );var input = dialog.get("input");
            var s = input.value;
            var m = s.match(/([^/.]*)([^/]*)$/);
            input.select();
            input.setSelectionRange(m.index, m.index + m[1].length);
          });
          if (!fileName) {
            lively.notify("no file created");
            return;
          }
          await files.saveFile(fileName, content);
          lively.notify("created " + fileName);

          if (type == "drawio") {
            this.setAttribute("mode", "show");
          } else {
            this.setAttribute("mode", "edit");
          }

          this.showCancelAndSave();

          await this.followPath(fileName);

          this.focus();
        }

        async newDirectory(path = "") {
          var fileName = await lively.prompt('Please enter the name of the directory', path);
          if (!fileName) {
            lively.notify("no file created");
            return;
          }
          await fetch(fileName, { method: 'MKCOL' });
          lively.notify("created " + fileName);
          this.followPath(fileName);
        }
        /*MD ## Events MD*/

        onKeyDown(evt) {
          var char = String.fromCharCode(evt.keyCode || evt.charCode);
          if ((evt.ctrlKey || evt.metaKey /* metaKey = cmd key on Mac */) && char == "S") {
            if (evt.shiftKey) {
              this.onAccept();
            } else {
              this.onSave();
            }
            evt.preventDefault();
            evt.stopPropagation();
          } else if (evt.key == "F7") {
            this.switchBetweenJSAndHTML();
            evt.stopPropagation();
            evt.preventDefault();
          }
        }

        onContextMenu(evt) {
          // fall back to system context menu if shift pressed
          if (!evt.shiftKey) {
            evt.preventDefault();
            evt.stopPropagation();

            var path = evt.composedPath();
            var markdown = path.find(ea => ea.localName == "lively-markdown");
            if (markdown) {
              var elements = path.filter(ea => ea.getAttribute && ea.getAttribute("data-source-line"));
              if (elements.length > 0) {
                this.onMarkdownContextMenu(evt, markdown, elements);
                return false;
              }
            }

            var worldContext = document.body; // default to opening context menu content globally
            // opening in the content makes only save if that content could be persisted and is displayed
            if (this.contentIsEditable() && !this.isEditing()) {
              worldContext = this;
            }
            lively.openContextMenu(document.body, evt, undefined, worldContext);
            return false;
          }
        }

        onClick(evt) {
          if (evt.shiftKey && !this.isEditing() && this.getURL().pathname.match(/.*\.md/)) {
            this.onMarkdownClick(evt);
          }
        }

        onFullscreen(evt) {
          this.toggleControls();
          if (!this.parentElement.isMaximized) return;
          if (this.isFullscreen() && !this.parentElement.isMaximized() || !this.isFullscreen() && this.parentElement.isMaximized()) {
            this.parentElement.toggleMaximize();
            if (this.parentElement.isMaximized()) {
              this.parentElement.get(".window-titlebar").style.display = "none";
              this.parentElement.style.zIndex = 0;
            } else {
              this.parentElement.style.zIndex = 1000;
              this.parentElement.get(".window-titlebar").style.display = "";
            }
          }
        }

        async onToggleOptions() {
          if (this.classList.contains('show-options')) {
            this.classList.remove('show-options');
          } else {
            this.classList.add('show-options');
          }
        }

        async onFavorite() {
          await Favorites.toggle(this.getPath());
          this.updateFavInfo();
        }

        async onBeautify() {
          var _context2;

          const ending = (_context2 = this.getPath(), fileEnding).call(_context2);

          if (ending !== 'js' && ending !== 'css' && ending !== 'html') {
            return;
          }

          const editor = this.get("lively-editor");
          const text = editor.lastText;
          let beautifulText;
          const options = {
            'end_with_newline': true,
            'max_preserve_newlines': 3,
            'js': {
              'brace_style': ['collapse', 'preserve-inline'],
              'indent_size': 2,
              'wrap_line_length': 120
            },
            'indent_size': 2
            // load the beatify code async... because they are big
          };if (ending === 'js') {
            await System.import("src/client/js-beautify/beautify.js");
            beautifulText = global.js_beautify(text, options);
          } else if (ending === 'css') {
            await System.import("src/client/js-beautify/beautify-css.js");
            beautifulText = global.css_beautify(text, options);
          } else if (ending === 'html') {
            await System.import("src/client/js-beautify/beautify-html.js");
            beautifulText = global.html_beautify(text, options);
          }
          editor.setText(beautifulText, true);
        }

        onDelete() {
          var url = this.getURL() + "";
          this.deleteFile(url);
        }

        async onApply() {
          var url = this.getBaseURL();
          var filename = url.replace(/.*\//, "");
          var foundTemplate = await lively.components.searchTemplateFilename(filename);
          if (url == foundTemplate) {
            this.openTemplateInstance(url);
          } else if (url.match(/\.js$/)) {
            this.reloadModule(url);
          } else {
            lively.openBrowser(url);
          }
        }

        onHome() {
          this.clearNavbar();
          this.followPath(lively4url);
        }

        async onSync(evt) {
          var comp = lively.components.createComponent("lively-sync");
          var compWindow;
          lively.components.openInWindow(comp).then(w => {
            compWindow = w;
            lively.setPosition(w, lively.pt(100, 100));
          });

          var serverURL = lively4url.match(/(.*)\/([^\/]+$)/)[1];
          comp.setServerURL(serverURL);
          console.log("server url: " + serverURL);
          if (!this.getURL().pathname.match(serverURL)) {
            return lively.notify("can only sync on our repositories");
          }
          var repo = this.getPath().replace(serverURL + "/", "").replace(/\/.*/, "");
          comp.setRepository(repo);
          comp.sync();
          // .then(() => compWindow.remove())
        }

        onPathEntered(path) {
          this.followPath(path);
        }

        onEdit() {
          this.setAttribute("mode", "edit");
          this.showCancelAndSave();
          this.editFile();
        }

        async onCancel() {
          if (this.unsavedChanges()) {
            if (!(await lively.confirm("There are unsaved changes. Discard them?"))) {
              return;
            }
          }
          this.setAttribute("mode", "show");
          this.setPath(this.getPath());
          this.hideCancelAndSave();
        }

        onUp() {
          var path = this.getPath();
          if (path.match(/(README|index)\.((html)|(md))/))
            // one level more
            this.followPath(path.replace(/(\/[^/]+\/[^/]+$)|([^/]+\/$)/, "/"));else this.followPath(path.replace(/(\/[^/]+$)|([^/]+\/$)/, "/"));
        }

        onBack() {
          if (this.history().length < 2) {
            lively.notify("No history to go back!");
            return;
          }
          lively.lastBackButtonClicked = Date.now(); // can be used by scripts to prevent autoforwarding
          var url = this.history().pop();
          var last = _.last(this.history());
          // lively.notify("follow " + url)
          this.forwardHistory().push(url);
          this.followPath(last);
        }

        onMouseDown(evt) {
          if (lively.halo) {
            // close halo
            lively.halo.onBodyMouseDown(evt, this);
          }
          evt.stopPropagation();
          // evt.preventDefault();
          Clipboard.onBodyMouseDown(evt);
        }

        onForward() {
          var url = this.forwardHistory().pop();
          if (url) {
            this.followPath(url);
          } else {
            lively.notify("Could not navigate forward");
          }
        }

        async onBrowse() {
          var url = this.getURL();
          var comp = await lively.openComponentInWindow("lively-container");
          comp.editFile("" + url);
        }

        async onDependencies() {
          lively.openComponentInWindow("d3-tree").then(tree => {
            tree.dataName = function (d) {
              return d.name.replace(/.*\//, "").replace(/\.js/, "");
            };
            tree.setTreeData(lively.findDependedModulesGraph(this.getURL().toString()));
            lively.setExtent(tree.parentElement, pt(1200, 800));
            tree.parentElement.setAttribute("title", "Dependency Graph: " + this.getURL().toString().replace(/.*\//, ""));
          });
        }

        onEditorBackNavigation() {
          this.get("lively-container-navbar").focusDetails();
        }

        async onSaveAs() {
          var newPath = await lively.prompt("Save as..", this.getPath());
          if (newPath === undefined) return;

          if (!this.isEditing()) {
            lively.notify("save as " + newPath);
            var result = await fetch(newPath, {
              method: "get"
            });
            this.lastVersion = result.headers.get("fileversion");
            this.saveEditsInView(newPath);
            this.get("#container-path").value = newPath;
            return;
          }
          lively.notify("Save as... in EditMode not implemented yet");
        }

        // #important
        async onSave(doNotQuit) {
          var url = this.getURL();
          url = url.toString().replace(/#.*/, ""); // strip anchors while saving and loading files
          if (!this.isEditing()) {
            this.saveEditsInView();
            return;
          }

          if (this.getURL().pathname.match(/\/$/)) {
            files.saveFile(url, "");

            return;
          }
          this.get("#editor").setURL(url);
          await this.get("#editor").saveFile();
          this.__ignoreUpdates = true; // #LiveProgramming #S3 don't affect yourself...
          this.parentElement.__ignoreUpdates = true;
          var sourceCode = this.getSourceCode();
          // lively.notify("!!!saved " + url)
          window.LastURL = url;
          if (await this.isTemplate(url)) {
            lively.notify("update template");
            if (url.toString().match(/\.html/)) {
              // var templateSourceCode = await fetch(url.toString().replace(/\.[^.]*$/, ".html")).then( r => r.text())
              var templateSourceCode = sourceCode;

              await lively.updateTemplate(templateSourceCode);
            }
          }
          if (this.getURL().pathname.match(/.*css/)) {
            this.updateCSS();
          }
          this.updateOtherContainers();

          var moduleName = this.getURL().pathname.match(/([^/]+)\.js$/);
          if (moduleName) {
            moduleName = moduleName[1];

            const testRegexp = /((test\/.*)|([.-]test)|([.-]spec))\.js/;
            if (this.lastLoadingFailed) {
              console.log("last loading failed... reload");
              await this.reloadModule(url); // use our own mechanism...
            } else if (this.getURL().pathname.match(testRegexp)) {
              await this.loadTestModule(url);
            } else if (this.get("#live").checked) {
              // lively.notify("load module " + moduleName)
              await this.loadModule("" + url);
              console.log("START DEP TEST RUN");
              lively.findDependedModules("" + url).forEach(ea => {
                if (ea.match(testRegexp)) {
                  this.loadTestModule(ea);
                }
              });
              console.log("END DEP TEST RUN");
            } else {
              lively.notify("ignore module " + moduleName);
            }
          }
          // this.showNavbar();

          // something async... 
          lively.sleep(5000).then(() => {
            this.__ignoreUpdates = false;
            this.parentElement.__ignoreUpdates = false;
          });
        }

        onNewfile() {
          this.newFile(this.getPath());
        }

        async onNewdirectory() {
          this.newDirectory(this.getPath());
        }

        onVersions() {
          this.get("#editor").toggleVersions();
        }

        onAccept() {
          this.onSave().then(sourceCode => {
            this.setAttribute("mode", "show");
            this.setPath(this.getPath());
            this.hideCancelAndSave();
          });
        }

        async onTextChanged() {
          if (!this.getURL().pathname.match(/\.js$/)) {
            return;
          }
        }

        onHTMLMutation(mutations, observer) {
          if (this.isPersisting) return; // we mutate while persisting

          mutations.forEach(record => {

            var indicator = this.get("#changeIndicator");
            if (indicator) {
              indicator.style.backgroundColor = "rgb(250,250,0)";
            }

            // if (record.target.id == 'console'
            //     || record.target.id == 'editor') return;

            this.contentChangedDelay

            // let shouldSave = true;
            ();if (record.type == 'childList') {
              //     let addedNodes = [...record.addedNodes],
              //         removedNodes = [...record.removedNodes],
              //         nodes = addedNodes.concat(removedNodes);

              //     //removed nodes never have a parent, so remeber orphans when they are created
              //     for (let node of addedNodes) {
              //         if (hasParentTag(node) == false) {
              //             orphans.add(node);
              //         }
              //     }

              //     // shouldSave = hasNoDonotpersistFlagInherited(addedNodes) || checkRemovedNodes(removedNodes, orphans);

              //     //remove removed orphan nodes from orphan set
              //     for (let node of removedNodes) {
              //         if (orphans.has(node)) {
              //             orphans.delete(node);
              //         }
              //     }
            } else if (record.type == 'attributes' || record.type == 'characterData') {}

            // shouldSave = hasNoDonotpersistFlagInherited([record.target]);


            // if (shouldSave) {
            // sessionStorage["lively.scriptMutationsDetected"] = 'true';
            // restartPersistenceTimerInterval();
            // }
          });
        }

        onEditorCursorActivity(cm) {
          if (this.getURL().pathname.match(/.*\.md/)) {
            var url = this.getURL();
            var otherContainer = this.getOtherContainers()[0];
            var markdown = otherContainer && otherContainer.get("lively-markdown");
            if (markdown) {
              this.onMarkdownEditorCursorActivity(cm, markdown);
            }
          }
        }

        /*MD ## Render Content MD*/

        async appendMarkdown(content, renderTimeStamp) {
          var md = await _addSourceLocation(lively.create("lively-markdown", this.getContentRoot()
          // md.setAttribute("data-lively4-donotpersist", true) // will be thrown away after loading anyway, #DoesNotWork
          ), {
            file: 'https://lively-kernel.org/lively4/lively-connectors/src/components/tools/lively-container.js',
            end: {
              column: 74,
              line: 1280
            },
            start: {
              column: 19,
              line: 1280
            }
          });if (renderTimeStamp && this.renderTimeStamp !== renderTimeStamp) {
            return md.remove();
          }
          md.classList.add("presentation" // for the presentation button
          );md.getDir = this.getDir.bind(this);
          md.followPath = this.followPath.bind(this);
          await md.setContent(content);
          if (md.getAttribute("mode") == "presentation") {
            var presentation = await md.startPresentation();
            if (this.lastPage) {
              presentation.gotoSlideAt(this.lastPage);
            }
          }
          if (this.wasContentEditable) {
            md.contentEditable = true;
          }

          // get around some async fun
          if (this.preserveContentScroll !== undefined) {
            this.get("#container-content").scrollTop = this.preserveContentScroll;
            delete this.preserveContentScroll;
          }

          var contentRoot = md.get("#content");

          contentRoot.querySelectorAll(`input[type="checkbox"]`).forEach(ea => {

            lively.addEventListener("input", ea, "click", async evt => {
              ea.checked = !ea.checked;
              this.markdownCheckCheckboxAndSave(ea);
              var highlight = lively.showElement(ea.parentElement);
              highlight.innerHTML = `<br><span style="background:white">SAVE...</span>`;
              highlight.style.border = "1px solid green";
              highlight.style.color = "green";
            });
          });
          await lively.sleep(500 // wait for renderer to get some positions to scroll to....
          );this.scrollToAnchor(this.anchor);
        }

        appendLivelyMD(content, renderTimeStamp) {
          content = content.replace(/@World.*/g, "");
          content = content.replace(/@+Text: name="Title".*\n/g, "# ");
          content = content.replace(/@+Text: name="Text.*\n/g, "\n");
          content = content.replace(/@+Text: name="Content.*\n/g, "\n");
          content = content.replace(/@+Box: name="SteppingWordCounter".*\n/g, "\n");
          content = content.replace(/@+Text: name="MetaNoteText".*\n(.*)\n\n/g, "<i style='color:orange'>$1</i>\n\n");
          content = content.replace(/@+Text: name="WordsText".*\n.*/g, "\n");

          this.appendMarkdown(content, renderTimeStamp);
        }

        async appendScript(scriptElement) {
          // #IDEA by instanciating we can avoid global (de-)activation collisions
          // Scenario (A) There should be no activation conflict in this case, because appendScript wait on each other...
          // Scenario (B)  #TODO opening a page on two licely-containers at the same time will produce such a conflict.
          // #DRAFT instead of using ScopedScripts as a singleton, we should instanciate it.
          if (!ScopedScripts) {
            _recorder_._src_components_tools_lively_container_js.ScopedScripts = await System.import("src/client/scoped-scripts.js").then(m => m.default);
          }
          var layers = ScopedScripts.layers(this.getURL(), this.getContentRoot());
          ScopedScripts.openPromises = [];
          return new Promise((resolve, reject) => {
            var root = this.getContentRoot();
            var script = _addSourceLocation(document.createElement("script"), {
              file: 'https://lively-kernel.org/lively4/lively-connectors/src/components/tools/lively-container.js',
              end: {
                column: 53,
                line: 1350
              },
              start: {
                column: 21,
                line: 1350
              }
            });
            script.type = "text/javascript";

            layers.forEach(ea => ea && ea.beGlobal());

            if (scriptElement.src) {
              script.src = scriptElement.src;
              script.onload = () => {
                // #WIP multiple activations are not covered.... through this...
                Promise.all(ScopedScripts.openPromises).then(() => {
                  layers.forEach(ea => ea && ea.beNotGlobal());
                  // console.log("ScopedScripts openPromises: " + ScopedScripts.openPromises)
                  resolve();
                }, reject);
              };
              script.onerror = reject;
            }
            script.text = scriptElement.textContent;

            cop.withLayers(layers, () => {
              try {
                root.appendChild(script);
              } catch (e) {
                console.error(e);
              }
            });
            if (!script.src) {
              Promise.all(ScopedScripts.openPromises).then(() => {
                layers.forEach(ea => ea && ea.beNotGlobal());
                // console.log("ScopedScripts openPromises: " + ScopedScripts.openPromises)
                resolve();
              }, reject);
            }
          });
        }

        async appendHtml(content, renderTimeStamp) {
          if (renderTimeStamp && this.renderTimeStamp !== renderTimeStamp) {
            return;
          }
          // strip lively boot code...

          // content = content.replace(/\<\!-- BEGIN SYSTEM\.JS(.|\n)*\<\!-- END SYSTEM.JS--\>/,"");
          // content = content.replace(/\<\!-- BEGIN LIVELY BOOT(.|\n)*\<\!-- END LIVELY BOOT --\>/,"");

          this.clear();

          if (content.match("<template") && this.getURL().pathname.match("html$")) {

            content = "<pre><code> " + content.replace(/</g, "&lt;") + "</code></pre>";
          }

          if (content.match(/<script src=".*d3\.v3(.min)?\.js".*>/)) {
            if (!window.d3) {
              console.log("LOAD D3");
              // #TODO check if dealing with this D3 is covered now through our general approach...
              await lively.loadJavaScriptThroughDOM("d3", "src/external/d3.v3.js");
            }

            if (!window.ScopedD3) {
              console.log("LOAD D3 Adaption Layer");
              await System.import("src/client/container-scoped-d3.js");
              ScopedD3.updateCurrentBodyAndURLFrom(this);
              // return this.appendHtml(content) // try again
            }
          }

          if (content.match(/<script src=".*cola(\.min)?\.js".*>/)) {
            console.log("LOAD Cola");
            await lively.loadJavaScriptThroughDOM("cola", "src/external/cola.js");
          }

          //  var content = this.sourceContent
          try {
            var root = this.getContentRoot();
            var nodes = lively.html.parseHTML(content, document, true);
            if (nodes[0] && nodes[0].localName == 'template') {
              // lively.notify("append template " + nodes[0].id);
              return this.appendTemplate(nodes[0].id);
            }
            lively.html.fixLinks(nodes, this.getDir(), path => this.followPath(path));
            for (var ea of nodes) {
              if (ea && ea.tagName == "SCRIPT") {
                try {
                  await this.appendScript(ea);
                } catch (e) {
                  console.error(e);
                }
              } else {
                root.appendChild(ea);
                if (ea.querySelectorAll) {
                  for (var block of ea.querySelectorAll("pre code")) {
                    highlight.highlightBlock(block);
                  }
                  // now we can use the structural information we got from the highlighter
                  for (var comment of ea.querySelectorAll(".hljs-comment")) {
                    var markdownMatch = comment.textContent.match(/^\/\*MD((.|\n)*)MD\*\/$/);
                    if (markdownMatch) {
                      //source.startsWidth("/*MD") && source.endsWidth("MD*/")
                      var markdown = await _element('lively-markdown', _attributes(), _children(_childExpression(markdownMatch[1])), {
                        file: 'https://lively-kernel.org/lively4/lively-connectors/src/components/tools/lively-container.js',
                        end: {
                          column: 91,
                          line: 1452
                        },
                        start: {
                          column: 38,
                          line: 1452
                        }
                      });
                      comment.innerHTML = "";
                      comment.appendChild(markdown);
                      await markdown.updateView();
                      markdown.style.whiteSpace = "normal"; // revert effect of outside `pre` tag 
                      markdown.style.display = "inline-block";
                      // markdown.style.border = "2px solid blue"
                      // markdown.style.backgroundColor = "lightgray"

                      lively.html.fixLinks(markdown.shadowRoot.querySelectorAll("[href],[src]"), this.getURL().toString().replace(/[^/]*$/, ""), url => this.followPath(url));
                    }
                  }
                }
              }
            }

            await components.loadUnresolved(root, false, "container.js", true);

            lively.clipboard.initializeElements(root.querySelectorAll("*"));

            if (nodes.length == 1 && (nodes[0].localName == "lively-window" || nodes[0].classList.contains("lively-content"))) {
              lively.setPosition(nodes[0], pt(0, 0));
            }
          } catch (e) {
            console.log("Could not append html:" + content.slice(0, 200) + "..." + " ERROR:", e);
          }

          // get around some async fun
          if (this.preserveContentScroll !== undefined) {
            this.get("#container-content").scrollTop = this.preserveContentScroll;
            delete this.preserveContentScroll;
          }

          ViewNav.enable(this

          // await lively.sleep(500) // wait for renderer to get some positions to scroll to....
          );this.scrollToAnchor(this.anchor);

          setTimeout(() => {
            this.resetContentChanges();
            this.observeHTMLChanges();
          }, 0);
        }

        async appendCSV(content, renderTimeStamp) {
          var container = this.get('#container-content');
          var table = await _addSourceLocation(lively.create("lively-table"), {
            file: 'https://lively-kernel.org/lively4/lively-connectors/src/components/tools/lively-container.js',
            end: {
              column: 51,
              line: 1506
            },
            start: {
              column: 22,
              line: 1506
            }
          });
          table.setFromCSV(content);

          if (renderTimeStamp && this.renderTimeStamp !== renderTimeStamp) {
            return;
          }
          container.appendChild(table);
        }

        async appendTemplate(name, renderTimeStamp) {
          try {
            var node = lively.components.createComponent(name);
            if (renderTimeStamp && this.renderTimeStamp !== renderTimeStamp) {
              return;
            }
            this.getContentRoot().appendChild(node);
            await lively.components.loadByName(name);
          } catch (e) {
            console.log("Could not append html:" + content);
          }
        }

        /*MD ## Navbar MD*/

        clearNavbar() {
          var container = this.get('#container-leftpane');
          container.clear();
          return container;
        }

        hideNavbar() {
          if (lively.getExtent(this).x > 1) {
            this.get('lively-separator').onClick();
          }
        }

        navbar() {
          return this.get('#container-leftpane');
        }

        async showNavbar() {
          // this.get('#container-leftpane').style.display = "block";
          // this.get('lively-separator').style.display = "block";

          var navbar = this.navbar
          // implement hooks
          ();navbar.deleteFile = (url, urls) => {
            this.deleteFile(url, urls);
          };
          navbar.renameFile = url => {
            this.renameFile(url);
          };
          navbar.newFile = (url, type) => {
            this.newFile(url, type);
          };
          navbar.newDirectory = (url, type) => {
            this.newDirectory(url, type);
          };
          navbar.followPath = (path, lastPath) => {
            this.contextURL = lastPath;
            this.followPath(path);
          };
          navbar.navigateToName = (name, data) => {
            this.navigateToName(name, data);
          };

          (await navbar.show) && navbar.show(this.getURL(), this.content, navbar.contextURL, false, this.contentType);
        }

        /*MD ## Controls MD*/

        toggleControls() {
          var showsControls = this.get("#container-navigation").style.display == "none";
          if (showsControls) {
            this.showControls();
          } else {
            this.hideControls();
          }
          // remember the toggle fullscreen in the url parameters
          var path = this.getPath();
          if (this.useBrowserHistory()) {
            window.history.pushState({ followInline: true, path: path }, 'view ' + path, window.location.pathname + "?edit=" + path + "&fullscreen=" + !showsControls);
          }
        }

        hideControls() {
          this.setAttribute("controls", "hidden");
          this.get("#fullscreenInline").style.display = "block";
          this.get("#container-navigation").style.display = "none";
          this.get("#container-leftpane").style.display = "none";
          this.get("#container-rightpane").style.flex = 1;
          this.get("lively-separator").style.display = "none";
        }

        showControls() {
          this.getAttribute("controls");
          this.setAttribute("controls", "shown");
          this.get("#fullscreenInline").style.display = "none";
          this.get("#container-navigation").style.display = "";
          this.get("#container-leftpane").style.display = "";

          this.get("#container-leftpane").style.flex = 20;
          this.get("#container-rightpane").style.flex = 80;
          this.get("lively-separator").style.display = "";
        }

        /*MD ## Edit MD*/

        async editFile(path) {
          // console.log("[container] editFile " + path)
          this.setAttribute("mode", "edit"); // make it persistent
          if (path) await this.setPath(path, true /* do not render */);

          this.clear();
          var urlString = this.getURL().toString();

          var containerContent = this.get('#container-content');
          containerContent.style.display = "none";
          var containerEditor = this.get('#container-editor');
          containerEditor.style.display = "block";

          this.resetLoadingFailed();

          this.showNavbar();

          // console.log("[container] editFile befor getEditor")
          // ... demos\/
          var editorType = urlString.match(/babylonian-programming-editor\/demos\/.*\js$/) ? "babylonian-programming-editor" : "lively-editor";

          if (this.sourceContent && this.sourceContent.match('^.*"enable examples"')) {
            editorType = "babylonian-programming-editor";
          }

          if (urlString.match(/((png)|(jpe?g)|(gif))$/i)) {
            editorType = "lively-image-editor";
          }

          var livelyEditor = await this.getEditor(editorType
          // console.log("[container] editFile got editor ")

          );if (livelyEditor.awaitEditor) {
            await livelyEditor.awaitEditor();
            var codeMirror = livelyEditor.livelyCodeMirror();

            codeMirror.addEventListener("change", evt => this.onTextChanged(evt));
          }

          var url = this.getURL();
          livelyEditor.setURL(url);
          // console.log("[container] editFile setURL " + url)
          if (codeMirror) {
            if (codeMirror.editor && codeMirror.editor.session) {
              codeMirror.editor.session.setOptions({
                mode: "ace/mode/javascript",
                tabSize: 2,
                useSoftTabs: true
              });
            }
            codeMirror.changeModeForFile(url.pathname);

            // NOTE: we don't user loadFile directly... because we don't want to edit PNG binaries etc...
            if (urlString.match(/\.svg$/)) {
              this.sourceContent = await fetch(urlString).then(r => r.text());
            }

            livelyEditor.setText(this.sourceContent); // directly setting the source we got
            if (livelyEditor.checkAndLoadAnnotations) {
              await livelyEditor.checkAndLoadAnnotations();
            }

            if (codeMirror.editor) {
              if (!codeMirror.tagName == "LIVELY-CODE-MIRROR") {
                codeMirror.editor.selection.moveCursorTo(0, 0);
                var lineWidth = 100;
                codeMirror.editor.session.setWrapLimit(lineWidth);
                codeMirror.editor.renderer.setPrintMarginColumn(lineWidth);
              }
            }
          }

          livelyEditor.lastVersion = this.lastVersion;
          this.showCancelAndSave();

          if (codeMirror && ("" + url).match(/\.((js)|(py))$/)) {
            codeMirror.setTargetModule("" + url); // for editing
          }

          if (codeMirror && this.anchor) {
            this.scrollToAnchor(this.anchor);
          }
        }

        getHTMLSource() {
          this.querySelectorAll("*").forEach(ea => {
            if (ea.livelyPrepareSave) ea.livelyPrepareSave();
          });
          return this.getContentRoot().innerHTML;
        }

        /*MD ## Save Content MD*/

        saveSource(url, source) {
          return this.getEditor().then(editor => {
            editor.setURL(url);
            editor.setText(source);
            editor.lastVersion = this.lastVersion;
            editor.saveFile().then(() => {
              this.lastVersion = editor.lastVersion;
              // #TODO we should update here after conflict resolution?
              this.updateOtherContainers();
            }).then(() => {
              this.resetContentChanges();
              lively.notify("saved content!");
            });
          });
        }

        async saveHTML(url) {
          return this.saveSource(url, this.getHTMLSource());
        }

        async saveMarkdown(url) {
          var markdown = await this.get("lively-markdown");
          if (markdown.getAttribute("mode") == "presentation") {
            lively.notify("saving in presentation mode not supported yet");
          } else {
            var source = await markdown.htmlAsMarkdownSource();
            return this.saveSource(url, source);
          }
        }

        async saveEditsInView(url) {
          url = (url || this.getURL()).toString();
          var contentElement = this.getContentRoot();
          if (url.match(/template.*\.html$/)) {
            return lively.notify("Editing templates in View not supported yet!");
          } else if (url.match(/\.html$/)) {
            this.saveHTML(new URL(url)).then(() => {
              // lively.notify({
              //   title: "saved HTML",
              //   color: "green"});
            });
          } else if (url.match(/\.md$/)) {
            this.saveMarkdown(new URL(url)).then(() => {
              // lively.notify({
              //   title: "saved HTML",
              //   color: "green"});
            });
          } else if (contentElement && contentElement.livelySource) {
            var source = contentElement.livelySource();
            if (source.then) source = await source; // maybe some elements take a while to generate their source
            return this.saveSource(url, source);
          } else {
            lively.notify("Editing in view not supported for the content type!");
          }
        }

        unsavedChanges() {
          var url = this.getURL();
          if (!url) return false;
          if (url.toString().match(/\/$/)) return false; // isDirectory...

          var editor = this.get("#editor");
          if (!editor) return this.contentChanged;
          return editor.textChanged;
        }
        /*MD ## ContentChange MD*/

        checkForContentChanges() {

          if (!this.contentIsEditable()) {
            this.contentChanged = false;
            return;
          }

          // don't know how to check for edits here... 
          if (!this.isEditing() && this.getURL().pathname.match(/\.md$/)) {
            this.contentChanged = false;
            return;
          }

          if (this.isPersisting) return;
          this.isPersisting = true;
          // console.log("checkForContentChanges " + (Date.now() - this.lastChecked) + "ms " + document.activeElement)
          this.lastChecked = Date.now();

          try {
            window.oldActiveElement = document.activeElement;
            var currentSource = this.getHTMLSource();

            if (!this.lastSource || this.lastSource != currentSource) {
              this.contentChanged = true;
            } else {
              this.contentChanged = false;
            }
            this.updateChangeIndicator();
          } finally {
            // setTimeout(() => {
            // console.log("refocus " + oldActiveElement)

            if (oldActiveElement && oldActiveElement.editor) oldActiveElement.editor.focus

            // we don't want to catch our own mutations... that were cause
            // by detecting some mutations in the first place
            ();this.isPersisting = false;
            // }, 0)
          }
        }

        resetContentChanges() {
          this.lastSource = this.getHTMLSource();
          this.contentChanged = false;
          this.updateChangeIndicator();
        }

        updateChangeIndicator() {
          var indicator = this.get("#changeIndicator");
          if (indicator && this.contentChanged) {
            indicator.style.backgroundColor = "rgb(220,30,30)";
          } else {
            indicator.style.backgroundColor = "rgb(200,200,200)";
          }
        }

        /*MD ## Focus / Scroll / Navigation MD*/

        focus() {
          const livelyCodeMirror = this.getLivelyCodeMirror();
          if (livelyCodeMirror) {
            livelyCodeMirror.focus();
          } else {
            this.get("lively-container-navbar").focus();
          }
        }

        navigateToName(name, data) {
          // lively.notify("navigate to " + name);
          var baseURL = this.getURL().toString().replace(/\#.*/, "");
          var anchor = "#" + encodeURIComponent(name.replace(/# ?/g, "").replace(/\*/g, ""));
          var nextURL = baseURL + anchor;
          var editor = this.getLivelyCodeMirror();

          this.setPathAttributeAndInput(nextURL);
          this.history().push(nextURL);

          if (editor) {
            if (data && data.start) {
              // we have more information
              var cm = editor.editor;
              var start = cm.posFromIndex(data.start);
              var end = cm.posFromIndex(data.end);

              cm.setSelection(start, end

              // scroll only if necessary
              );var rect = cm.getWrapperElement().getBoundingClientRect();
              var topVisibleLine = cm.lineAtHeight(rect.top, "window");
              var bottomVisibleLine = cm.lineAtHeight(rect.bottom, "window");

              if (start.line < topVisibleLine) {
                editor.scrollToLine(start.line);
              }
              if (end.line > bottomVisibleLine) {
                var visibleLines = bottomVisibleLine - topVisibleLine;
                editor.scrollToLine(end.line - visibleLines);
              }
            } else {
              editor.find(name);
            }
          } else {

            this.scrollToAnchor(anchor);
          }
        }

        /*MD ## UI MD*/

        clear() {

          this.getContentRoot().innerHTML = '';
          Array.from(this.get('#container-content').childNodes).filter(ea => ea.id !== "container-root").forEach(ea => ea.remove());
          this.get('#container-editor').innerHTML = '';
        }

        hideCancelAndSave() {
          return;
          _.each(this.shadowRoot.querySelectorAll(".edit"), ea => {
            ea.style.visibility = "hidden";
            ea.style.display = "none";
          });
          _.each(this.shadowRoot.querySelectorAll(".browse"), ea => {
            ea.style.visibility = "visible";
            ea.style.display = "inline-block";
          });
        }

        showCancelAndSave() {
          return;
          _.each(this.shadowRoot.querySelectorAll(".browse"), ea => {
            ea.style.visibility = "hidden";
            ea.style.display = "none";
          });
          _.each(this.shadowRoot.querySelectorAll(".edit"), ea => {
            ea.style.visibility = "visible";
            ea.style.display = "inline-block";
          });
        }

        async switchBetweenJSAndHTML() {
          var _context3;

          const ending = (_context3 = this.getPath().replace(/[#?].*/, ""), fileEnding).call(_context3);
          if (ending === 'js' || ending === 'html') {
            var _context4;

            const targetURLString = (_context4 = this.getPath(), replaceFileEndingWith).call(_context4, ending === 'js' ? 'html' : 'js');
            const existingContainer = Array.from(document.body.querySelectorAll('lively-container')).find(container => container.getPath().match(targetURLString));
            if (existingContainer) {
              lively.gotoWindow(existingContainer.parentElement, true);
              existingContainer.focus();
            } else {
              lively.openBrowser(targetURLString, true).then(browser => browser.focus());
            }
          }
        }

        // #private
        async updateFavInfo() {
          const starIcon = this.get('#favorite').querySelector('i');

          if (await Favorites.has(this.getPath())) {
            starIcon.classList.add('fa-star');
            starIcon.classList.remove('fa-star-o');
          } else {
            starIcon.classList.add('fa-star-o');
            starIcon.classList.remove('fa-star');
          }
        }

        observeHTMLChanges() {
          if (this.mutationObserver) this.mutationObserver.disconnect();
          this.mutationObserver = new MutationObserver((mutations, observer) => {
            this.onHTMLMutation(mutations, observer);
          });
          this.mutationObserver.observe(this, {
            childList: true,
            subtree: true,
            characterData: true,
            attributes: true });
        }
        /*MD ## Update External Content MD*/

        updateCSS() {
          var url = "" + this.getURL();
          var all = Array.from(lively.allElements(true));

          all.filter(ea => ea.localName == "link").filter(ea => ea.href == url).forEach(ea => {
            var parent = ea.parentElement;
            ea.remove();
            parent.appendChild(ea);
            lively.notify("update css", ea.href);
          });

          all.filter(ea => ea.localName == "style").filter(ea => ea.url == url).forEach(ea => {
            lively.fillTemplateStyle(ea, url);
            lively.notify("upodate css", url);
          });
        }

        updateOtherContainers() {
          console.warn('updateOtherContainers');
          var url = "" + this.getURL();
          document.body.querySelectorAll('lively-container').forEach(ea => {
            if (ea !== this && !ea.isEditing() && ("" + ea.getURL()).match(url.replace(/\.[^.]+$/, ""))) {
              console.log("update container content: " + ea);
              ea.setPath(ea.getURL() + "");
            }
          });

          updateEditors(url, [this.get("lively-editor")]);
        }

        /*MD ## Content Navigation MD*/

        async scrollToAnchor(anchor) {
          if (anchor) {
            var name = decodeURI(anchor.replace(/#/, "")).replace(/\n/g, "");
            if (this.isEditing()) {
              // use Navbar and it's structural knowledge to find the right name
              var codeMirror = (await this.asyncGet("#editor")).get('#editor');
              var navbar = await this.asyncGet("lively-container-navbar");
              await lively.waitOnQuerySelector(navbar.shadowRoot, "#details ul li" // wait for some content
              );var item = navbar.detailItems.find(ea => ea.name == name);
              if (item) {
                navbar.onDetailsItemClick(item, new CustomEvent("nothing"));
              }
              return;
            }

            // markdown specific ?
            var md = this.getContentRoot().querySelector("lively-markdown");
            if (md) {
              var root = md.shadowRoot;
            } else {
              root = this.getContentRoot();
            }

            var presentation = md && md.get("lively-presentation");
            var pageNumberMatch = name.match(/^\@([0-9]+)$/);
            if (presentation && pageNumberMatch) {
              presentation.gotoSlideAt(parseInt(pageNumberMatch[1]));
              return;
            }

            // Special Case:

            // 1. search by id
            try {
              var element = root.querySelector(`#${name.replace(/"/g, "%22").replace(/%2F/g, "\\/")}`);
            } catch (e) {}
            // ignore

            // 2. search for exactly matching anchors
            if (!element) {
              element = root.querySelector(`a[name="${name.replace(/"/g, "%22")}"]`);
            }
            // 3. brute force search for headings with the text
            if (!element) {
              element = _.find(root.querySelectorAll("h1,h2,h3,h4"), ea => ea.textContent == name);
            }

            // 4. ok, try fulltext search
            if (!element) {

              // search for the text nodes because they are the smallest entities and go to a nearby entity..
              var node = lively.allTextNodes(root).find(ea => ea.textContent.match(Strings.escapeRegExp(name))
              // going one level up will go to far... in most cases
              // so we cannot do: element = node.parentElement 
              );if (node) element = node.previousElementSibling; // instead we go sideways
            }
            if (element) {
              // var element = that
              var slide = lively.allParents(element).find(ea => ea.classList.contains("lively-slide"));

              if (presentation && slide) {
                console.log('goto slide ', slide);
                presentation.setSlide(slide);
              }

              // await lively.sleep(500)
              // a very hacky way to somehow find the position where to scroll
              this.get("#container-content").scrollTop = 0;
              var offset = lively.getGlobalPosition(element).subPt(lively.getGlobalPosition(this.get("#container-content")));
              this.get("#container-content").scrollTop = offset.y;
            }
          }
        }

        /*MD ## Helper MD*/

        becomeMainContainer() {
          this.__ignoreUpdates = true; // a hack... since I am missing DevLayers...
          this.get('#container-content').style.overflow = "visible";

          this.parentElement.toggleMaximize();
          this.parentElement.hideTitlebar();
          this.parentElement.style.zIndex = 0;
          this.parentElement.setAttribute("data-lively4-donotpersist", "all");

          this.id = 'main-content';
          this.setAttribute("load", "auto");

          let path, edit;
          window.onpopstate = event => {
            var state = event.state;
            if (state && state.followInline) {
              console.log("follow " + state.path);
              this.followPath(state.path);
            }
          };
          path = lively.preferences.getURLParameter("load");
          edit = lively.preferences.getURLParameter("edit");
          let fullscreen = lively.preferences.getURLParameter("fullscreen") == "true";
          if (fullscreen) {
            this.onFullscreen // #TODO replace toggle logic with enableFullscreen, disableFullscreen
            ();
          }

          // force read mode
          if (this.getAttribute("mode") == "read" && edit) {
            path = edit;
            edit = undefined;
          }

          if (!path || path == "null") {
            path = lively4url + "/";
          }

          this.viewOrEditPath(path, edit);
        }

        // #Refactor, currently used by _navigation.html
        // _navigation.html should instead use normal link creation... and we might do magic there?
        // #deprecated #Problem related to html.fixLinks
        createLink(base, name, href) {
          var link = _addSourceLocation(document.createElement("a"), {
            file: 'https://lively-kernel.org/lively4/lively-connectors/src/components/tools/lively-container.js',
            end: {
              column: 42,
              line: 2131
            },
            start: {
              column: 15,
              line: 2131
            }
          });
          link.textContent = name;
          var path = base + href;
          link.href = path;
          link.addEventListener("click", evt => {
            this.followPath(path);
            evt.preventDefault();
            evt.stopPropagation();
          });
          return link;
        }

        // #deprecated
        thumbnailFor(url, name) {
          if (name.match(/\.((png)|(jpe?g))$/)) return "<img class='thumbnail' src='" + name + "'>";else return "";
        }

        listingForDirectory(url, render, renderTimeStamp) {
          return files.statFile(url).then(content => {
            if (this.renderTimeStamp !== renderTimeStamp) {
              return;
            }
            var files = JSON.parse(content).contents;
            var index = _.find(files, ea => ea.name.match(/^\index\.md$/i));
            if (!index) index = _.find(files, ea => ea.name.match(/^index\.html$/i));
            if (!index) index = _.find(files, ea => ea.name.match(/^README\.md$/i));
            if (index) {
              // lively.notify("found index" + index)
              // this.contextURL

              return this.followPath(url.toString().replace(/\/?$/, "/" + index.name));
            }
            return Promise.resolve(""); // DISABLE Listings

            this.sourceContent = content;

            var fileBrowser = _addSourceLocation(document.createElement("lively-file-browser"), {
              file: 'https://lively-kernel.org/lively4/lively-connectors/src/components/tools/lively-container.js',
              end: {
                column: 69,
                line: 2170
              },
              start: {
                column: 24,
                line: 2170
              }
            });
            /* DEV
              fileBrowser = that.querySelector("lively-file-browser")
              url = "https://lively-kernel.org/lively4/"
             */
            if (render) {
              return lively.components.openIn(this.getContentRoot(), fileBrowser).then(() => {
                if (this.renderTimeStamp !== renderTimeStamp) {
                  fileBrowser.remove();
                  return;
                }

                // lively.notify("set url " + url)
                fileBrowser.hideToolbar();
                // override browsing file and direcotry
                fileBrowser.setMainAction(newURL => {
                  // lively.notify("go " + newURL)
                  this.followPath(newURL.toString());
                });
                fileBrowser.setMainDirectoryAction(newURL => {
                  // lively.notify("go dir " + newURL)
                  this.followPath(newURL.toString() + "/");
                });
                fileBrowser.setURL(url);
              });
            } else {
              return;
            }
          }).catch(function (err) {
            console.log("Error: ", err);
            lively.notify("ERROR: Could not set path: " + url, "because of: ", err);
          });
        }

        // #private
        normalizeURL(urlString) {
          var url = new URL(urlString);
          url.pathname = lively.paths.normalize(url.pathname);
          return "" + url;
        }

        /*MD ## Markdown Source Mapping 
        
        ![](../../../doc/figures/markdown-mapping.drawio)
        
        MD*/

        async onMarkdownContextMenu(evt, markdown, elements) {
          let menuItems = [['edit source', () => this.showMarkdownElement(elements[0]), '', ``], ['edit', () => this.editMarkdownElement(elements.last, markdown), '', ``]];
          var checkbox = elements[0].querySelector(`input[type="checkbox"]`);
          if (checkbox) {
            menuItems.push(["check", async () => {
              this.markdownCheckCheckboxAndSave(checkbox);
            }]);
          }
          let menu = await ContextMenu.openIn(document.body, evt, undefined, document.body, menuItems);
          return false;
        }

        async markdownCheckCheckboxAndSave(checkbox) {
          var elements = lively.allParents(checkbox).filter(ea => ea.getAttribute("data-source-line"));
          debugger;
          checkbox.checked = !checkbox.checked;
          if (checkbox.checked) {
            checkbox.setAttribute("checked", undefined);
          } else {
            checkbox.removeAttribute("checked");
          }
          var source = await this.elementToMarkdown(elements.last);
          var range = this.getMarkdownRange(elements.last);
          this.saveRegionWithEditor(this.getURL(), range, source);
        }

        getMarkdownRange(element) {
          var sourceLine = element.getAttribute("data-source-line");
          return sourceLine && sourceLine.split("-").map(n => parseFloat(n));
        }

        setMarkdownRange(element, range) {
          element.setAttribute("data-source-line", range[0] + "-" + range[1]);
        }

        onMarkdownEditorCursorActivity(cm, markdown) {
          var line = cm.getCursor().line + 1;
          var root = markdown.get("#content");
          var elements = root.querySelectorAll(`[data-source-line]`).filter(ea => {
            var range = this.getMarkdownRange(ea);
            return range[0] == line; // && (line < range[1])
          });
          if (elements.length > 0) {
            var element = Array.from(elements).last;
            var slide = lively.allParents(element).find(ea => ea.classList.contains("lively-slide"));

            if (slide) {
              var presentation = lively.allParents(element).find(ea => ea.localName == "lively-presentation");
              if (presentation) presentation.setSlide(slide);
            }

            if (this.lastEditCursorHighlight) this.lastEditCursorHighlight.remove();
            this.lastEditCursorHighlight = lively.showElement(element);
            this.lastEditCursorHighlight.style.borderColor = "rgba(0,0,200,0.5)";
            this.lastEditCursorHighlight.innerHTML = "";
          }
        }

        onMarkdownClick(evt) {
          var markdownElements = evt.composedPath().filter(ea => ea && ea.getAttribute && ea.getAttribute("data-source-line"));
          if (markdownElements.length == 0) return;
          var last = markdownElements.first;

          var otherContainer = this.getOtherContainers(true)[0];
          if (otherContainer) {
            this.containerShowMarkdownElement(otherContainer, last);
          }
        }

        async showMarkdownElement(element) {
          var otherContainer = this.getOtherContainers(true)[0];
          if (!otherContainer) {
            otherContainer = await lively.openBrowser(this.getURL().toString(), true);
          }
          this.containerShowMarkdownElement(otherContainer, element);
        }

        async elementToMarkdown(element) {
          var markdownConverter = new Upndown();
          markdownConverter.tabindent = "  ";
          markdownConverter.bullet = "- ";
          return markdownConverter.convert(element.outerHTML, { keepHtml: true });
        }

        async editMarkdownElement(element, markdown) {
          var source = await this.elementToMarkdown(element);
          var range = this.getMarkdownRange(element);
          var rangeOffset = range[0] - 1;

          if (this.lastMarkdownWorkspace) {
            if (this.lastMarkdownWorkspace.targetElement) {
              this.lastMarkdownWorkspace.targetElement.style.display = "";
            }
            this.lastMarkdownWorkspace.remove();
          }

          var workspace = await _element('lively-code-mirror', _attributes(_attributeStringLiteral('mode', 'gfm')), _children(), {
            file: 'https://lively-kernel.org/lively4/lively-connectors/src/components/tools/lively-container.js',
            end: {
              column: 79,
              line: 2320
            },
            start: {
              column: 27,
              line: 2320
            }
          });
          workspace.targetElement = element;

          this.lastMarkdownWorkspace = workspace;

          element.parentElement.insertBefore(workspace, element);
          element.style.display = "none";
          await workspace.editorLoaded();
          workspace.value = source;
          workspace.focus();
          workspace.registerExtraKeys({
            Enter: async () => {
              await workspace.doSave();
              element.style.display = "";

              var root = _element('div', _attributes(), _children(), {
                file: 'https://lively-kernel.org/lively4/lively-connectors/src/components/tools/lively-container.js',
                end: {
                  column: 30,
                  line: 2337
                },
                start: {
                  column: 19,
                  line: 2337
                }
              });

              // Just a preview... since we have to reload later .... #TODO make the real thing?
              await markdown.renderMarkdown(root, workspace.value);
              Array.from(root.childNodes).forEach(ea => {
                workspace.parentElement.insertBefore(ea, workspace);

                var tmpRange = this.getMarkdownRange(ea);
                tmpRange[0] += rangeOffset;
                tmpRange[0] += rangeOffset;
                this.setMarkdownRange(element, range

                // lively.showElement(ea)
                );
              }

              // #TODO fix data-source-line ... in elements after this element! or give up and reload!
              );this.followPath(this.getURL() // #GiveUp

              );element.remove();
              workspace.remove();
            }
          }

          // prevent slide navigation etc....
          );workspace.addEventListener("keydown", async evt => {
            evt.stopPropagation();
          });

          workspace.doSave = async () => {
            lively.notify("save range " + range[0] + "-" + range[1]);
            range = await this.saveRegionWithEditor(this.getURL(), range, workspace.value);
          };
        }

        async saveRegionWithEditor(url, range, text) {
          var editor = await _addSourceLocation(lively.create("lively-editor"), {
            file: 'https://lively-kernel.org/lively4/lively-connectors/src/components/tools/lively-container.js',
            end: {
              column: 53,
              line: 2374
            },
            start: {
              column: 23,
              line: 2374
            }
          });
          editor.setURL(url);
          await editor.loadFile();
          var cm = editor.currentEditor

          // #Hack... realy hack: preserve trailing lines... because outerHTML and the markdown range only fit roughly 
          ();var lines = cm.getValue().split("\n");
          var lastRangeLine = range[1] - 2;
          if (lines[lastRangeLine] == "") {
            range[1]--;
          }

          cm.setSelection({ line: range[0] - 1, ch: 0 }, { line: range[1] - 1, ch: 0 });
          cm.replaceSelection(text + "\n"

          // update element...
          );var newRangeHeight = text.split("\n").length;
          range[1] = range[0] + newRangeHeight;
          editor.saveFile();
          return range;
        }

        containerShowMarkdownElement(otherContainer, element) {
          var livleyEditor = otherContainer.get('#editor');
          var livleyCodeMirror = livleyEditor && livleyEditor.get('#editor');
          var cm = livleyCodeMirror && livleyCodeMirror.editor;

          if (cm) {
            var line = element.getAttribute("data-source-line").split("-")[0];
            // cm.setCursor({line: line - 1, ch: 0}) 
            cm.setSelection({ line: line - 1, ch: 0 }, { line: line, ch: 0 });
            otherContainer.parentElement.focus();
            otherContainer.focus();
          }
        }

        /*MD ## Lively Hooks MD*/

        // #hook make a gloval position relative, so it can be used in local content
        localizePosition(pos) {
          var offsetBounds = this.get('#container-content').getBoundingClientRect();
          return pos.subPt(pt(offsetBounds.left, offsetBounds.top));
        }

        // #hook
        livelyAllowsSelection(evt) {
          if (!this.contentIsEditable() || this.isEditing()) return false;

          if (evt.composedPath()[0].id == "container-content") return true;

          return false;
        }

        // #hook
        livelyAcceptsDrop() {
          return this.contentIsEditable() && !this.isEditing();
        }

        // #hook
        livelyPrepareSave() {
          this.setAttribute("leftpane-flex", this.get("#container-leftpane").style.flex);
          this.setAttribute("rightpane-flex", this.get("#container-rightpane").style.flex);
        }

        // #hook
        livelyPreMigrate() {
          // do something before I got replaced
          this.oldContentScroll = this.get("#container-content").scrollTop;
          var livelyEditor = this.get("#editor");
          if (livelyEditor) {
            this.oldScrollInfo = livelyEditor.getScrollInfo();
            this.oldCursor = livelyEditor.getCursor();
            this.oldFocused = document.activeElement == this;
          }
        }

        // #hook
        async livelyExample() {
          return this.followPath(lively4url + "/README.md");
        }

        // customize clipboard interaction... etc
        // navigating in this multidimensional space can be hard
        // #hook
        livelyTarget() {
          var markdownElement = this.get("lively-markdown");
          if (markdownElement && markdownElement.get) {
            // maybe not initialized yet.. damn! 
            return markdownElement.get("#content");
          }
          return this;
        }

        // #hook
        livelyMigrate(other) {
          // other = that

          this._history = other._history;
          this._forwardHistory = other._forwardHistory;

          this.isMigrating = true;
          this.preserveContentScroll = other.oldContentScroll;
          var editor = other.get("#editor");
          if (editor) {
            var otherCodeMirror = editor.currentEditor();
            if (otherCodeMirror && otherCodeMirror.selection) {
              var range = otherCodeMirror.selection.getRange();
              var scrollTop = otherCodeMirror.session.getScrollTop();
              this.asyncGet("#editor").then(editor => {
                var thisCodeMirror = editor.currentEditor();
                if (otherCodeMirror && thisCodeMirror) {
                  thisCodeMirror.session.setScrollTop(scrollTop);
                  thisCodeMirror.selection.setRange(range);
                }
                this.isMigrating = false;
              }).catch(() => {
                // jsut to be sure..
                this.isMigrating = false;
              });
            }
            this.asyncGet("#editor").then(editor => {
              editor.setScrollInfo(other.oldScrollInfo);
              editor.setCursor(other.oldCursor);
              if (other.oldFocused) {
                // lively.notify("set focus again!")
                // setTimeout(() => editor.focus(), 1000)
              }
            });
          } else {
            this.isMigrating = false;
          }
        }

      }

      _export('default', Container);

      Object.defineProperty(_recorder_._src_components_tools_lively_container_js, 'Container', {
        get() {
          return Container;
        },

        set(thisIsVererySecretVariableName) {
          _export('default', Container = thisIsVererySecretVariableName);

          return true;
        },

        enumerable: true,
        configurable: true
      });
    }
  };
});