System.register(['reactive-jsx', './preferences.js', 'src/external/focalStorage.js', 'utils', 'src/client/script-manager.js', 'src/components/halo/Connection.js', 'src/external/moment.js', 'src/client/viewnav.js'], function (_export, _context) {
  "use strict";

  var _attributeExpression, _childElement, _childText, _childExpression, _attributeStringLiteral, _children, _attributes, _element, _addSourceLocation, preferences, focalStorage, debounce, scriptManager, Connection, moment, ViewNav;

  return {
    setters: [function (_reactiveJsx) {
      _attributeExpression = _reactiveJsx.attributeExpression;
      _childElement = _reactiveJsx.childElement;
      _childText = _reactiveJsx.childText;
      _childExpression = _reactiveJsx.childExpression;
      _attributeStringLiteral = _reactiveJsx.attributeStringLiteral;
      _children = _reactiveJsx.children;
      _attributes = _reactiveJsx.attributes;
      _element = _reactiveJsx.element;
      _addSourceLocation = _reactiveJsx.addSourceLocation;
    }, function (_preferencesJs) {
      preferences = _preferencesJs.default;
    }, function (_srcExternalFocalStorageJs) {
      focalStorage = _srcExternalFocalStorageJs.default;
    }, function (_utils) {
      debounce = _utils.debounce;
    }, function (_srcClientScriptManagerJs) {
      scriptManager = _srcClientScriptManagerJs.default;
    }, function (_srcComponentsHaloConnectionJs) {
      Connection = _srcComponentsHaloConnectionJs.default;
    }, function (_srcExternalMomentJs) {
      moment = _srcExternalMomentJs.default;
    }, function (_srcClientViewnavJs) {
      ViewNav = _srcClientViewnavJs.default;
    }],
    execute: function () {
      const __SystemJSRewritingHack = {};
      _recorder_._src_client_persistence_js = _recorder_._src_client_persistence_js || {};
      Object.defineProperty(_recorder_._src_client_persistence_js, 'isCurrentlyCloning', {
        get() {
          return isCurrentlyCloning;
        },

        set(thisIsVererySecretVariableName) {
          _export('isCurrentlyCloning', isCurrentlyCloning = thisIsVererySecretVariableName);

          return true;
        },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        enumerable: true,
        configurable: true
      });
      function isCurrentlyCloning() {
        return sessionStorage["lively.persistenceCurrentlyCloning"] === 'true';
      }

      _export('isCurrentlyCloning', isCurrentlyCloning);

      class Persistence {

        constructor() {
          var _context2;

          this.saveDelay = (_context2 = () => {
            this.saveLivelyContent();
            this.showMutationIndicator().style.backgroundColor = "rgba(10,10,10,0.3)";
          }, debounce).call(_context2, 3000 // save the world 3seconds after a change
          );
        }

        backupStep() {
          if (this.running && lively.persistence.current === this) {
            this.ensureLivelyContentBackups();
            setTimeout(() => this.backupStep(), 60 * 1000);
          } else {
            // stop silently
          }
        }

        // work around non static module global state
        static get current() {
          if (!window.lively4persistence) {
            window.lively4persistence = new Persistence();
          }
          return window.lively4persistence;
        }
        static set current(obj) {
          window.lively4persistence = obj;
        }

        static enable() {
          this.disable // #TODO this only works with one module... and breaks when we have many....
          ();this.current = new Persistence();
          this.current.start();
        }

        static disable() {
          if (this.current) {
            this.current.stop();
          }
        }

        start() {
          this.running = true;
          this.ensureLivelyContentBackups();
          this.observeHTMLChanges();
          this.backupStep();
        }

        ensureLivelyContentBackups() {
          var list = Persistence.backupIntervals().reverse();
          for (let i in list) {
            var ea = list[i];
            var previous = list[i + 1];
            this.ensureLivelyContentBackup(undefined, ea, previous);
          }
        }

        stop() {
          if (this.mutationObserver) this.mutationObserver.disconnect();
          this.running = false;
        }

        urlToKey(urlString) {
          urlString = "" + urlString;
          return "livelyWindows_" + urlString.replace(/^https?:\/\//, "").replace(/[^A-Za-z0-9]/, "_");
        }

        defaultTarget() {
          return document.body;
        }

        defaultURL() {
          return document.location.protocol + "//" + document.location.hostname + document.location.pathname;
        }

        async getLivelyContentForURL(url) {
          url = url || this.defaultURL();
          var content = await focalStorage.getItem(this.urlToKey(url.toString()));
          if (!content) {
            try {
              var elementSource = await lively.files.loadFile(lively4url + "/src/parts/initial-content.html");
              content = lively.html.parseHTML(elementSource)[0].innerHTML;
            } catch (e) {
              lively.error("Could not load default desktop conent for lively", e);
            }
          }
          return content;
        }

        setLivelyContentForURL(url, source) {
          url = url || this.defaultURL();
          return focalStorage.setItem(this.urlToKey(url.toString()), source);
        }

        async loadLivelyContentForURL(url, target) {
          var source = await this.getLivelyContentForURL(url);
          return this.loadLivelyContent(source, target);
        }

        async loadLivelyContent(source, target) {
          target = target || this.defaultTarget();
          var div = _addSourceLocation(document.createElement("div"), {
            file: 'https://lively-kernel.org/lively4/lively4-portal/src/client/persistence.js',
            end: {
              column: 43,
              line: 121
            },
            start: {
              column: 14,
              line: 121
            }
          });
          div.innerHTML = source;
          var topLevel = Array.from(div.querySelectorAll(":scope > *"));
          var objs = Array.from(div.childNodes

          // somehow zIndex gets lost...
          );var zIndexMap = new Map();
          topLevel.forEach(ea => {
            zIndexMap.set(ea, ea.style.zIndex);
          });

          objs.map(ea => {
            if (ea.classList && ea.classList.contains('lively-preferences')) {
              target.querySelectorAll('div.lively-preferences').forEach(ele => ele.remove());
            }
            target.appendChild(ea);
          });
          await lively.components.loadUnresolved(target, true, "loadLivelyContentForURL", true

          // restore zIndex in an Async way... it seems focus is responsible for it #Hack
          );lively.sleep(0).then(() => {
            topLevel.forEach(ea => {
              ea.style.zIndex = zIndexMap.get(ea);
            });
          });

          objs.map(ea => {
            Persistence.initLivelyObject(ea);
          });
          return;
        }

        static initLivelyObject(obj) {
          if (!obj) return;
          try {
            scriptManager.findLively4Script(obj, false);
            if (obj.livelyLoad) obj.livelyLoad();
            Connection.deserializeFromObjectIfNeeded(obj);
            if (!obj.querySelectorAll) return;
            obj.querySelectorAll("*").forEach(ea => {
              if (ea.livelyLoad) ea.livelyLoad();
              Connection.deserializeFromObjectIfNeeded(ea);
            });
          } catch (e) {
            lively.showError(e);
          }
        }

        async ensureLivelyContentBackup(url = this.defaultURL(), interval = "yesterday", previousInterval, force) {
          var key = this.urlToKey(url.toString());
          const datePrefix = `backup_${interval}_date_`;
          const sourcePrefix = `backup_${interval}_source_`;
          const dateFormat = "YYYY-MM-DD HH:mm:ss";

          var dateString = await focalStorage.getItem(datePrefix + key);
          var lastBackupDate = moment(dateString, dateFormat);
          if (Number.isNaN(0 + lastBackupDate)) {
            lively.warn("[backup] Currupt date string", dateString);
            dateString = undefined; // handle corrupt date string
          }

          var today = moment
          // lively.notify("minutes since "  + interval + " "  + today + " " + lastBackupDate + " " + this.minutesForBackupInterval(interval))
          ();if (force || !dateString || today.diff(lastBackupDate, "minutes") > this.minutesForBackupInterval(interval)) {
            var backupDate = today.format(dateFormat);
            var previousBackupKey = key;
            if (previousInterval) {
              previousBackupKey = `backup_${previousInterval}_source_` + key;
              backupDate = await focalStorage.getItem(`backup_${previousInterval}_date_` + key);
              if (!moment(backupDate, dateFormat)) {
                backupDate = moment().format(dateFormat // handle corrupt date string
                );
              }
            }
            var source = await focalStorage.getItem(previousBackupKey // get current source ... @onsetsu or should we use current world?

            );await focalStorage.setItem(datePrefix + key, backupDate);
            await focalStorage.setItem(sourcePrefix + key, source
            // lively.notify(`Ensured ${interval} backup! ` + backupDate)
            );
          } else {
            // lively.notify(`Last ${interval} backup is not old enough`)
          }
        }

        async loadLivelyContentBackup(url = this.defaultURL(), interval = "yesterday", target = this.defaultTarget()) {
          var key = this.urlToKey(url.toString());
          const sourcePrefix = `backup_${interval}_source_`;
          var source = await focalStorage.getItem(sourcePrefix + key);
          if (source) {
            this.loadLivelyContent(source, target);
            lively.notify(`load ${interval} backup!`);
          } else {
            lively.notify(`could not load ${interval} backup!`);
          }
        }

        async storeLivelyContentForURL(url, target) {
          target = target || this.defaultTarget();
          this.isPersisting = true;
          var source = lively.html.getGlobalSource(target);
          this.setLivelyContentForURL(url, source);
          setTimeout(() => {
            this.isPersisting = false;
          }, 0 // the mutation events triggered are still pending here...
          );return source;
        }

        async saveLivelyContent() {
          // console.log("[peristence] save after " + 
          //   (Date.now() - this.lastSaved) +"ms")
          this.lastSaved = Date.now();
          await this.storeLivelyContentForURL();
          if (self.__gs_sources__) {
            self.__gs_sources__.saveOpenWindows();
          }
          // console.log("[peristence] saved lively content into focalStorage " + 
          //   (Date.now() - this.lastSaved) +"ms")
        }

        hasDoNotPersistTag(node) {
          var donotperist = node.attributes && node.attributes.hasOwnProperty('data-lively4-donotpersist');
          if (donotperist) return true;
          if (!node.parentElement) return false;
          return this.hasDoNotPersistTag(node.parentElement);
        }

        isBlacklisted(mutation) {
          if (mutation.target.tagName == "BODY") return true;
          if (mutation.target.tagName == "LIVELY-MENU") return true;
          if (mutation.target.tagName == "LIVELY-SELECTION") return true;
          if (mutation.target.tagName == "LIVELY-HAND") return true;
          if (mutation.target.tagName == "LIVELY-HALO") return true;
          if (mutation.target.tagName == "LIVELY-NOTIFICATION-LIST") return true;
          if (mutation.target.id == "mutationIndicator") return true;
          if (mutation.target.getAttribute && mutation.target.getAttribute("data-is-meta")) return true;
          if (this.hasDoNotPersistTag(mutation.target)) return true;
          return false;
        }

        showMutationIndicator() {
          var indicator = document.body.querySelector("#mutationIndicator");
          if (!indicator) {
            var div = _addSourceLocation(document.createElement("div"), {
              file: 'https://lively-kernel.org/lively4/lively4-portal/src/client/persistence.js',
              end: {
                column: 45,
                line: 267
              },
              start: {
                column: 16,
                line: 267
              }
            });
            div.id = "mutationIndicator";
            document.body.appendChild(div);
            div.style.position = "fixed";
            div.style.right = "0px";
            div.style.top = "0px";
            div.style.width = "10px";
            div.style.height = "10px";
            div.style.backgroundColor = "blue";
            div.style.pointerEvents = "none";
            div.style.zIndex = 2000;

            indicator = div;
          }
          return indicator;
        }

        onMutation(mutations, observer) {
          if (this.isPersisting) {
            // console.log("ignore mutation ", mutations)
            return; // we mutate while persisting 
          }

          mutations.filter(ea => !this.isBlacklisted(ea)).forEach(record => {
            this.showMutationIndicator().style.backgroundColor = "rgba(200,0,0,0.5)";
            this.saveDelay();
          });
        }

        observeHTMLChanges(target) {
          target = target || this.defaultTarget();
          if (this.mutationObserver) this.mutationObserver.disconnect();
          this.mutationObserver = new MutationObserver((mutations, observer) => {
            this.onMutation(mutations, observer);
          });
          this.mutationObserver.observe(target, {
            childList: true,
            subtree: true,
            characterData: true,
            attributes: true });
        }

        minutesForBackupInterval(interval) {
          // #TODO refactor... this duplication does not look nice
          if (interval === "lastminute") {
            return 1;
          }
          if (interval === "someminutes") {
            return 2;
          }
          if (interval === "yesterday") {
            return 1 * 24 * 60;
          }
          if (interval === "hour") {
            return 1 * 60;
          }
          if (interval === "twohours") {
            return 2 * 60;
          }
          if (interval === "week") {
            return 7 * 24 * 60;
          }
          if (interval === "month") {
            return 31 * 7 * 24 * 60;
          }
          throw new Error(interval + " not supported");
        }

        static backupIntervals() {
          return ["lastminute", "someminutes", "hour", "twohours", "yesterday", "week", "month"];
        }

        static restoreBackupContextMenuItems() {
          var result = [];
          for (let ea of this.backupIntervals()) {
            result.push(this.restoreBackupContextMenuItem(ea));
          }
          return result;
        }

        static restoreBackupContextMenuItem(interval) {
          let time = _element('span', _attributes(), _children(), {
            file: 'https://lively-kernel.org/lively4/lively4-portal/src/client/persistence.js',
            end: {
              column: 28,
              line: 349
            },
            start: {
              column: 15,
              line: 349
            }
          });
          let key = `backup_${interval}_date_` + this.current.urlToKey(this.current.defaultURL());
          focalStorage.getItem(key).then(s => {
            time.innerHTML = "" + s;
          });
          var label = _element('span', _attributes(_attributeStringLiteral('style', '')), _children(_childExpression(time), _childText(' '), _childExpression(interval)), {
            file: 'https://lively-kernel.org/lively4/lively4-portal/src/client/persistence.js',
            end: {
              column: 55,
              line: 354
            },
            start: {
              column: 16,
              line: 354
            }
          });

          return [label, async evt => {
            // #TODO refactor into component so that behavior is persistent
            var target = _element('div', _attributes(_attributeStringLiteral('id', 'contentRoot')), _children(), {
              file: 'https://lively-kernel.org/lively4/lively4-portal/src/client/persistence.js',
              end: {
                column: 51,
                line: 359
              },
              start: {
                column: 23,
                line: 359
              }
            });
            var scrollPane = _element('div', _attributes(_attributeStringLiteral('style', 'background-color: gray; position:relative; width:100%; height:100%; overflow:scroll')), _children(_childExpression(target)), {
              file: 'https://lively-kernel.org/lively4/lively4-portal/src/client/persistence.js',
              end: {
                column: 138,
                line: 360
              },
              start: {
                column: 27,
                line: 360
              }
            });

            ViewNav.enable(target);
            var ui = _element('div', _attributes(), _children(_childExpression(scrollPane), _childText('\n                '), _childElement(_element('div', _attributes(_attributeStringLiteral('style', 'position:absolute; top:0px; left:0px')), _children(_childText('\n                '), _childElement(_element('button', _attributes(_attributeExpression('click', () => {

              var contents = Array.from(target.childNodes);
              document.body.innerHTML = "";
              for (let ea of contents) {
                document.body.appendChild(ea);
              }

              lively.notify("would restore " + contents.length + " elements");
            })), _children(_childText('restore')), {
              file: 'https://lively-kernel.org/lively4/lively4-portal/src/client/persistence.js',
              end: {
                column: 37,
                line: 375
              },
              start: {
                column: 16,
                line: 365
              }
            })), _childText('\n                ')), {
              file: 'https://lively-kernel.org/lively4/lively4-portal/src/client/persistence.js',
              end: {
                column: 22,
                line: 376
              },
              start: {
                column: 16,
                line: 364
              }
            }))), {
              file: 'https://lively-kernel.org/lively4/lively4-portal/src/client/persistence.js',
              end: {
                column: 28,
                line: 376
              },
              start: {
                column: 19,
                line: 363
              }
            });
            var win = await _element('lively-window', _attributes(_attributeExpression('title', "Restore " + interval)), _children(_childExpression(ui)), {
              file: 'https://lively-kernel.org/lively4/lively4-portal/src/client/persistence.js',
              end: {
                column: 92,
                line: 377
              },
              start: {
                column: 27,
                line: 377
              }
            });
            document.body.appendChild(win);
            lively.setGlobalPosition(win, lively.getPosition(evt));
            lively.persistence.current.loadLivelyContentBackup(undefined, interval, target);
          }];
        }

      }

      _export('default', Persistence);

      Object.defineProperty(_recorder_._src_client_persistence_js, 'Persistence', {
        get() {
          return Persistence;
        },

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

          return true;
        },

        enumerable: true,
        configurable: true
      });
      if (self.lively) {
        if (lively.persistence) lively.persistence.disable();
        lively.persistence = Persistence;
        Persistence.enable();
      }
    }
  };
});