(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
class DragHandler {

  constructor(controller) {
    this.controller = controller;
    this.online = false;
  }

  setOnline(online) {
    this.online = online;
  }

  install(graph) {
    const oldStart = graph.graphHandler.start;
    const oldUpdate = graph.graphHandler.updateLivePreview;
    const oldReset = graph.graphHandler.reset;
    const oldMoveCells = graph.graphHandler.moveCells;
    const self = this;

    graph.graphHandler.start = function(cell, x, y) {
      // start dragging (locally)
      oldStart.apply(this, arguments);

      console.log("drag start");

      if (self.online) {
        const cells = graph.graphHandler.getCells(cell);
        const cellIds = cells.map(c => c.id);

        let acquiredCallback, denyCallback;
        const acquiredPromise = new Promise((resolve, reject) => {
          acquiredCallback = resolve;
          denyCallback = reject;
        }).then(
          () => {console.log("acquired")},
          e => {console.log("denied"); throw e},
        );

        console.log("request lock");
        self.controller.addInput("request_lock", "in", [cellIds, acquiredCallback, denyCallback], self.controller.wallclockToSimtime());

        let resolveDragEnd;
        const dragPromise = new Promise(resolve => {
          resolveDragEnd = resolve;
        });

        const resetOnce = {
          reset: () => {
            graph.graphHandler.reset();
          },
        };

        // let's pretend we acquire the lock

        let moveCellsArgs;
        // In the graph handler, moveCells is called when the mouse is released (end of drag).
        graph.graphHandler.moveCells = function(cells, dx, dy, clone, target, event) {
          moveCellsArgs = [cells, dx, dy, clone, target, event];
        };

        // When drag has completed AND a lock was acquired, release the lock.
        Promise.all([dragPromise, acquiredPromise])
        .then(() => {
          if (moveCellsArgs !== undefined) {
            oldMoveCells.apply(graph.graphHandler, moveCellsArgs);
          }
          console.log("release lock");
          self.controller.addInput("release_lock", "in", [cellIds], self.controller.wallclockToSimtime());
        })
        .catch(() => {
          console.log("could not acquire lock");
          resetOnce.reset();
        });

        let dX = 0;
        let dY = 0;
        graph.graphHandler.updateLivePreview = function(dx, dy) {
          oldUpdate.apply(this, arguments);
          dX = dx;
          dY = dy;
          const msg = {
            type: "update_drag",
            cells,
            dx, dy,
          };
        };

        graph.graphHandler.reset = function() {
          console.log("drag end");
          oldReset.apply(this, arguments);
          resetOnce.reset = () => {};
          resolveDragEnd();
          console.log(dX, dY);
        };
      }
      else {
        // offline
        graph.graphHandler.updateLivePreview = oldUpdate;
        graph.graphHandler.moveCells = oldMoveCells;
        graph.graphHandler.reset = oldReset;
      }


    };

    // document.addEventListener('keydown', e => {
    //   if (e.code === 'ShiftLeft') {
    //     graph.graphHandler.reset();
    //   }
    // });
  }
};

module.exports = { DragHandler };
},{}],2:[function(require,module,exports){
const { UserColors } = require("./UserColors.js");

class GhostOverlays {
  constructor(graph, userColors, cursorHref) {
    // this.userNames = userNames;
    this.userColors = userColors;
    this.canvas = graph.view.canvas;
    this.svg = this.canvas.parentElement;
    this.cursorHref = cursorHref;

    this.map = new Map();
  }

  // Update or create ghost cursor. Idempotent.
  put(userId, name, x, y) {
    let state = this.map.get(userId);
    if (state === undefined) {
      const g = document.createElementNS("http://www.w3.org/2000/svg", 'g');
      const image = document.createElementNS("http://www.w3.org/2000/svg", 'image');
      const text = document.createElementNS("http://www.w3.org/2000/svg", 'text');
      text.style.fontSize = "10px";
      text.style.fill = this.userColors.getColor(userId);
      text.setAttribute('y', 30);
      const textNode = document.createTextNode("");
      text.appendChild(textNode);
      image.setAttribute('href', this.cursorHref);
      image.setAttribute('width', 11.6);
      image.setAttribute('height', 18.2);
      g.appendChild(image);
      g.appendChild(text);
      this.canvas.appendChild(g);
      const transform = this.svg.createSVGTransform();
      g.transform.baseVal.appendItem(transform);
      state = {transform, textNode, timeout: null};
      this.map.set(userId, state);
    }
    state.transform.setTranslate(
      (x + graph.view.translate.x) * graph.view.scale,
      (y + graph.view.translate.y) * graph.view.scale,
    );
    state.textNode.data = name;
    if (state.timeout) {
      clearTimeout(state.timeout);
    }
    state.timeout = setTimeout(() => state.transform.setTranslate(-50, -50), 5000);
  }
}

module.exports = { GhostOverlays };
},{"./UserColors.js":3}],3:[function(require,module,exports){
class UserColors {
  constructor() {
    this.colors = ["#F6511D", "#FFB400", "#00A6ED", "#7FB800", "#0D2C54"];
    this.i = 0;

    this.map = new Map();
  }

  getColor(userId) {
    let color = this.map.get(userId);
    if (color === undefined) {
      color = this.colors[this.i++];
      this.map.set(userId, color);
    }
    return color;
  }
}

module.exports = { UserColors };
},{}],4:[function(require,module,exports){
/*
Date: Tue Feb  1 17:49:55 2022

Model author: Joeri Exelmans
Model name: client
Model description:
Statechart model implementing much of the 'client' part of our protocol for synchronous collaboration sessions (joining, leaving, auto-reconnecting, broadcasting and receiving operations, and heartbeats to monitor connection health).
*/


// package client
const {
                RuntimeClassBase,
                StatechartSemantics,
                ObjectManagerBase,
                EventLoopControllerBase,
                State,
                ParallelState,
                Transition,
                Event,
                Association,
            } = require("statecharts_core");

let Main = function(controller, uiState, myId, getMyName) {
    RuntimeClassBase.call(this, controller);
    
    this.inports["ack"] = controller.addInputPort("ack", this);
    this.inports["socket"] = controller.addInputPort("socket", this);
    
    this.semantics.bigStepMaximality = StatechartSemantics.TakeMany;
    this.semantics.internalEventLifeline = StatechartSemantics.NextComboStep;
    this.semantics.inputEventLifeline = StatechartSemantics.FirstComboStep;
    this.semantics.priority = StatechartSemantics.SourceParent;
    this.semantics.concurrency = StatechartSemantics.Single;
    
    // build Statechart structure
    this.buildStatechartStructure();
    
    // call user defined constructor
    Main.prototype.userDefinedConstructor.call(this, uiState, myId, getMyName);
};
Main.prototype = new Object();
(function() {
    var proto = new RuntimeClassBase();
    for (let prop in proto) {
        Main.prototype[prop] = proto[prop];
    }
})();

Main.prototype.userDefinedConstructor = function(uiState, myId, getMyName) {
    this.uiState = uiState;
    this.myId = myId;
    this.getMyName = getMyName;
    
    this.connected = false;
};

Main.prototype.userDefinedDestructor = function() {
};


// builds Statechart structure
Main.prototype.buildStatechartStructure = function() {
    
    // state <root>
    this.states[""] = new State(0, "", this);
    
    // state /p
    this.states["/p"] = new ParallelState(1, "/p", this);
    
    // state /p/response_handler
    this.states["/p/response_handler"] = new State(2, "/p/response_handler", this);
    this.states["/p/response_handler"].setEnter(this.PResponseHandlerEnter);
    
    // state /p/response_handler/default
    this.states["/p/response_handler/default"] = new State(3, "/p/response_handler/default", this);
    
    // state /p/mode
    this.states["/p/mode"] = new State(4, "/p/mode", this);
    
    // state /p/mode/uninitialized
    this.states["/p/mode/uninitialized"] = new State(5, "/p/mode/uninitialized", this);
    
    // state /p/mode/async_disconnected
    this.states["/p/mode/async_disconnected"] = new State(6, "/p/mode/async_disconnected", this);
    this.states["/p/mode/async_disconnected"].setEnter(this.PModeAsyncDisconnectedEnter);
    
    // state /p/mode/async_connected
    this.states["/p/mode/async_connected"] = new State(7, "/p/mode/async_connected", this);
    this.states["/p/mode/async_connected"].setEnter(this.PModeAsyncConnectedEnter);
    
    // state /p/mode/can_leave
    this.states["/p/mode/can_leave"] = new State(8, "/p/mode/can_leave", this);
    this.states["/p/mode/can_leave"].setExit(this.PModeCanLeaveExit);
    
    // state /p/mode/can_leave/waiting_for_new_share_ack
    this.states["/p/mode/can_leave/waiting_for_new_share_ack"] = new State(9, "/p/mode/can_leave/waiting_for_new_share_ack", this);
    this.states["/p/mode/can_leave/waiting_for_new_share_ack"].setEnter(this.PModeCanLeaveWaitingForNewShareAckEnter);
    this.states["/p/mode/can_leave/waiting_for_new_share_ack"].setExit(this.PModeCanLeaveWaitingForNewShareAckExit);
    
    // state /p/mode/can_leave/reshare_when_online
    this.states["/p/mode/can_leave/reshare_when_online"] = new State(10, "/p/mode/can_leave/reshare_when_online", this);
    this.states["/p/mode/can_leave/reshare_when_online"].setEnter(this.PModeCanLeaveReshareWhenOnlineEnter);
    
    // state /p/mode/can_leave/session_set
    this.states["/p/mode/can_leave/session_set"] = new State(11, "/p/mode/can_leave/session_set", this);
    this.states["/p/mode/can_leave/session_set"].setEnter(this.PModeCanLeaveSessionSetEnter);
    this.states["/p/mode/can_leave/session_set"].setExit(this.PModeCanLeaveSessionSetExit);
    
    // state /p/mode/can_leave/session_set/waiting_for_join_ack
    this.states["/p/mode/can_leave/session_set/waiting_for_join_ack"] = new State(12, "/p/mode/can_leave/session_set/waiting_for_join_ack", this);
    this.states["/p/mode/can_leave/session_set/waiting_for_join_ack"].setEnter(this.PModeCanLeaveSessionSetWaitingForJoinAckEnter);
    this.states["/p/mode/can_leave/session_set/waiting_for_join_ack"].setExit(this.PModeCanLeaveSessionSetWaitingForJoinAckExit);
    
    // state /p/mode/can_leave/session_set/joined
    this.states["/p/mode/can_leave/session_set/joined"] = new ParallelState(13, "/p/mode/can_leave/session_set/joined", this);
    
    // state /p/mode/can_leave/session_set/joined/region_sendreceive
    this.states["/p/mode/can_leave/session_set/joined/region_sendreceive"] = new State(14, "/p/mode/can_leave/session_set/joined/region_sendreceive", this);
    
    // state /p/mode/can_leave/session_set/joined/region_sendreceive/default
    this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"] = new State(15, "/p/mode/can_leave/session_set/joined/region_sendreceive/default", this);
    this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"].setEnter(this.PModeCanLeaveSessionSetJoinedRegionSendreceiveDefaultEnter);
    
    // state /p/mode/can_leave/session_set/joined/region_cursor
    this.states["/p/mode/can_leave/session_set/joined/region_cursor"] = new State(16, "/p/mode/can_leave/session_set/joined/region_cursor", this);
    
    // state /p/mode/can_leave/session_set/joined/region_cursor/nodelay
    this.states["/p/mode/can_leave/session_set/joined/region_cursor/nodelay"] = new State(17, "/p/mode/can_leave/session_set/joined/region_cursor/nodelay", this);
    
    // state /p/mode/can_leave/session_set/joined/region_cursor/delay
    this.states["/p/mode/can_leave/session_set/joined/region_cursor/delay"] = new State(18, "/p/mode/can_leave/session_set/joined/region_cursor/delay", this);
    this.states["/p/mode/can_leave/session_set/joined/region_cursor/delay"].setEnter(this.PModeCanLeaveSessionSetJoinedRegionCursorDelayEnter);
    this.states["/p/mode/can_leave/session_set/joined/region_cursor/delay"].setExit(this.PModeCanLeaveSessionSetJoinedRegionCursorDelayExit);
    
    // state /p/mode/can_leave/session_set/joined/region_cursor/delay/inner
    this.states["/p/mode/can_leave/session_set/joined/region_cursor/delay/inner"] = new State(19, "/p/mode/can_leave/session_set/joined/region_cursor/delay/inner", this);
    
    // state /p/mode/can_leave/session_set/rejoin_when_online
    this.states["/p/mode/can_leave/session_set/rejoin_when_online"] = new State(20, "/p/mode/can_leave/session_set/rejoin_when_online", this);
    this.states["/p/mode/can_leave/session_set/rejoin_when_online"].setEnter(this.PModeCanLeaveSessionSetRejoinWhenOnlineEnter);
    
    // state /p/socket_region
    this.states["/p/socket_region"] = new State(21, "/p/socket_region", this);
    
    // state /p/socket_region/disconnected
    this.states["/p/socket_region/disconnected"] = new State(22, "/p/socket_region/disconnected", this);
    
    // state /p/socket_region/connecting_or_connected
    this.states["/p/socket_region/connecting_or_connected"] = new State(23, "/p/socket_region/connecting_or_connected", this);
    
    // state /p/socket_region/connecting_or_connected/connecting
    this.states["/p/socket_region/connecting_or_connected/connecting"] = new State(24, "/p/socket_region/connecting_or_connected/connecting", this);
    this.states["/p/socket_region/connecting_or_connected/connecting"].setEnter(this.PSocketRegionConnectingOrConnectedConnectingEnter);
    
    // state /p/socket_region/connecting_or_connected/connected
    this.states["/p/socket_region/connecting_or_connected/connected"] = new ParallelState(25, "/p/socket_region/connecting_or_connected/connected", this);
    this.states["/p/socket_region/connecting_or_connected/connected"].setEnter(this.PSocketRegionConnectingOrConnectedConnectedEnter);
    this.states["/p/socket_region/connecting_or_connected/connected"].setExit(this.PSocketRegionConnectingOrConnectedConnectedExit);
    
    // state /p/socket_region/connecting_or_connected/connected/send_receive_region
    this.states["/p/socket_region/connecting_or_connected/connected/send_receive_region"] = new State(26, "/p/socket_region/connecting_or_connected/connected/send_receive_region", this);
    
    // state /p/socket_region/connecting_or_connected/connected/send_receive_region/ready
    this.states["/p/socket_region/connecting_or_connected/connected/send_receive_region/ready"] = new State(27, "/p/socket_region/connecting_or_connected/connected/send_receive_region/ready", this);
    
    // state /p/socket_region/connecting_or_connected/connected/connection_monitor_region
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region"] = new State(28, "/p/socket_region/connecting_or_connected/connected/connection_monitor_region", this);
    
    // state /p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good"] = new ParallelState(29, "/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good", this);
    
    // state /p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/send_pings
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/send_pings"] = new State(30, "/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/send_pings", this);
    
    // state /p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/send_pings/waiting
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/send_pings/waiting"] = new State(31, "/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/send_pings/waiting", this);
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/send_pings/waiting"].setEnter(this.PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodSendPingsWaitingEnter);
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/send_pings/waiting"].setExit(this.PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodSendPingsWaitingExit);
    
    // state /p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/receive_pongs
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/receive_pongs"] = new State(32, "/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/receive_pongs", this);
    
    // state /p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/receive_pongs/waiting
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/receive_pongs/waiting"] = new State(33, "/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/receive_pongs/waiting", this);
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/receive_pongs/waiting"].setEnter(this.PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodReceivePongsWaitingEnter);
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/receive_pongs/waiting"].setExit(this.PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodReceivePongsWaitingExit);
    
    // state /p/socket_region/connecting_or_connected/connected/connection_monitor_region/timeout
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/timeout"] = new State(34, "/p/socket_region/connecting_or_connected/connected/connection_monitor_region/timeout", this);
    
    // state /p/socket_region/connecting_or_connected/wait_reconnect
    this.states["/p/socket_region/connecting_or_connected/wait_reconnect"] = new State(35, "/p/socket_region/connecting_or_connected/wait_reconnect", this);
    this.states["/p/socket_region/connecting_or_connected/wait_reconnect"].setEnter(this.PSocketRegionConnectingOrConnectedWaitReconnectEnter);
    this.states["/p/socket_region/connecting_or_connected/wait_reconnect"].setExit(this.PSocketRegionConnectingOrConnectedWaitReconnectExit);
    
    // add children
    this.states[""].addChild(this.states["/p"]);
    this.states["/p"].addChild(this.states["/p/response_handler"]);
    this.states["/p"].addChild(this.states["/p/mode"]);
    this.states["/p"].addChild(this.states["/p/socket_region"]);
    this.states["/p/response_handler"].addChild(this.states["/p/response_handler/default"]);
    this.states["/p/mode"].addChild(this.states["/p/mode/uninitialized"]);
    this.states["/p/mode"].addChild(this.states["/p/mode/async_disconnected"]);
    this.states["/p/mode"].addChild(this.states["/p/mode/async_connected"]);
    this.states["/p/mode"].addChild(this.states["/p/mode/can_leave"]);
    this.states["/p/mode/can_leave"].addChild(this.states["/p/mode/can_leave/waiting_for_new_share_ack"]);
    this.states["/p/mode/can_leave"].addChild(this.states["/p/mode/can_leave/reshare_when_online"]);
    this.states["/p/mode/can_leave"].addChild(this.states["/p/mode/can_leave/session_set"]);
    this.states["/p/mode/can_leave/session_set"].addChild(this.states["/p/mode/can_leave/session_set/waiting_for_join_ack"]);
    this.states["/p/mode/can_leave/session_set"].addChild(this.states["/p/mode/can_leave/session_set/joined"]);
    this.states["/p/mode/can_leave/session_set"].addChild(this.states["/p/mode/can_leave/session_set/rejoin_when_online"]);
    this.states["/p/mode/can_leave/session_set/joined"].addChild(this.states["/p/mode/can_leave/session_set/joined/region_sendreceive"]);
    this.states["/p/mode/can_leave/session_set/joined"].addChild(this.states["/p/mode/can_leave/session_set/joined/region_cursor"]);
    this.states["/p/mode/can_leave/session_set/joined/region_sendreceive"].addChild(this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"]);
    this.states["/p/mode/can_leave/session_set/joined/region_cursor"].addChild(this.states["/p/mode/can_leave/session_set/joined/region_cursor/nodelay"]);
    this.states["/p/mode/can_leave/session_set/joined/region_cursor"].addChild(this.states["/p/mode/can_leave/session_set/joined/region_cursor/delay"]);
    this.states["/p/mode/can_leave/session_set/joined/region_cursor/delay"].addChild(this.states["/p/mode/can_leave/session_set/joined/region_cursor/delay/inner"]);
    this.states["/p/socket_region"].addChild(this.states["/p/socket_region/disconnected"]);
    this.states["/p/socket_region"].addChild(this.states["/p/socket_region/connecting_or_connected"]);
    this.states["/p/socket_region/connecting_or_connected"].addChild(this.states["/p/socket_region/connecting_or_connected/connecting"]);
    this.states["/p/socket_region/connecting_or_connected"].addChild(this.states["/p/socket_region/connecting_or_connected/connected"]);
    this.states["/p/socket_region/connecting_or_connected"].addChild(this.states["/p/socket_region/connecting_or_connected/wait_reconnect"]);
    this.states["/p/socket_region/connecting_or_connected/connected"].addChild(this.states["/p/socket_region/connecting_or_connected/connected/send_receive_region"]);
    this.states["/p/socket_region/connecting_or_connected/connected"].addChild(this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region"]);
    this.states["/p/socket_region/connecting_or_connected/connected/send_receive_region"].addChild(this.states["/p/socket_region/connecting_or_connected/connected/send_receive_region/ready"]);
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region"].addChild(this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good"]);
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region"].addChild(this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/timeout"]);
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good"].addChild(this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/send_pings"]);
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good"].addChild(this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/receive_pongs"]);
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/send_pings"].addChild(this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/send_pings/waiting"]);
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/receive_pongs"].addChild(this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/receive_pongs/waiting"]);
    this.states[""].fixTree();
    this.states[""].defaultState = this.states["/p"];
    this.states["/p/response_handler"].defaultState = this.states["/p/response_handler/default"];
    this.states["/p/mode"].defaultState = this.states["/p/mode/uninitialized"];
    this.states["/p/mode/can_leave"].defaultState = this.states["/p/mode/can_leave/session_set"];
    this.states["/p/mode/can_leave/session_set"].defaultState = this.states["/p/mode/can_leave/session_set/waiting_for_join_ack"];
    this.states["/p/mode/can_leave/session_set/joined/region_sendreceive"].defaultState = this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"];
    this.states["/p/mode/can_leave/session_set/joined/region_cursor"].defaultState = this.states["/p/mode/can_leave/session_set/joined/region_cursor/nodelay"];
    this.states["/p/mode/can_leave/session_set/joined/region_cursor/delay"].defaultState = this.states["/p/mode/can_leave/session_set/joined/region_cursor/delay/inner"];
    this.states["/p/socket_region"].defaultState = this.states["/p/socket_region/disconnected"];
    this.states["/p/socket_region/connecting_or_connected"].defaultState = this.states["/p/socket_region/connecting_or_connected/connecting"];
    this.states["/p/socket_region/connecting_or_connected/connected/send_receive_region"].defaultState = this.states["/p/socket_region/connecting_or_connected/connected/send_receive_region/ready"];
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region"].defaultState = this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good"];
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/send_pings"].defaultState = this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/send_pings/waiting"];
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/receive_pongs"].defaultState = this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/receive_pongs/waiting"];
    
    // transition /p/response_handler/default
    var PResponseHandlerDefault_0 = new Transition(this, this.states["/p/response_handler/default"], [this.states["/p/response_handler/default"]]);
    PResponseHandlerDefault_0.setAction(this.PResponseHandlerDefault_0Exec);
    PResponseHandlerDefault_0.setTrigger(new Event("received", null));
    this.states["/p/response_handler/default"].addTransition(PResponseHandlerDefault_0);
    var PResponseHandlerDefault_1 = new Transition(this, this.states["/p/response_handler/default"], [this.states["/p/response_handler/default"]]);
    PResponseHandlerDefault_1.setAction(this.PResponseHandlerDefault_1Exec);
    PResponseHandlerDefault_1.setTrigger(new Event("disconnected", null));
    this.states["/p/response_handler/default"].addTransition(PResponseHandlerDefault_1);
    
    // transition /p/mode/uninitialized
    var PModeUninitialized_0 = new Transition(this, this.states["/p/mode/uninitialized"], [this.states["/p/mode/async_disconnected"]]);
    PModeUninitialized_0.setTrigger(new Event("init_offline", this.getInPortName("in")));
    this.states["/p/mode/uninitialized"].addTransition(PModeUninitialized_0);
    var PModeUninitialized_1 = new Transition(this, this.states["/p/mode/uninitialized"], [this.states["/p/mode/can_leave/session_set/rejoin_when_online"]]);
    PModeUninitialized_1.setAction(this.PModeUninitialized_1Exec);
    PModeUninitialized_1.setTrigger(new Event("init_join", this.getInPortName("in")));
    this.states["/p/mode/uninitialized"].addTransition(PModeUninitialized_1);
    
    // transition /p/mode/async_disconnected
    var PModeAsyncDisconnected_0 = new Transition(this, this.states["/p/mode/async_disconnected"], [this.states["/p/mode/async_connected"]]);
    PModeAsyncDisconnected_0.setTrigger(new Event("connected", null));
    this.states["/p/mode/async_disconnected"].addTransition(PModeAsyncDisconnected_0);
    
    // transition /p/mode/async_connected
    var PModeAsyncConnected_0 = new Transition(this, this.states["/p/mode/async_connected"], [this.states["/p/mode/async_disconnected"]]);
    PModeAsyncConnected_0.setTrigger(new Event("disconnected", null));
    this.states["/p/mode/async_connected"].addTransition(PModeAsyncConnected_0);
    var PModeAsyncConnected_1 = new Transition(this, this.states["/p/mode/async_connected"], [this.states["/p/mode/can_leave/session_set/waiting_for_join_ack"]]);
    PModeAsyncConnected_1.setAction(this.PModeAsyncConnected_1Exec);
    PModeAsyncConnected_1.setTrigger(new Event("join", this.getInPortName("in")));
    this.states["/p/mode/async_connected"].addTransition(PModeAsyncConnected_1);
    var PModeAsyncConnected_2 = new Transition(this, this.states["/p/mode/async_connected"], [this.states["/p/mode/can_leave/waiting_for_new_share_ack"]]);
    PModeAsyncConnected_2.setAction(this.PModeAsyncConnected_2Exec);
    PModeAsyncConnected_2.setTrigger(new Event("new_share", this.getInPortName("in")));
    this.states["/p/mode/async_connected"].addTransition(PModeAsyncConnected_2);
    
    // transition /p/mode/can_leave/waiting_for_new_share_ack
    var PModeCanLeaveWaitingForNewShareAck_0 = new Transition(this, this.states["/p/mode/can_leave/waiting_for_new_share_ack"], [this.states["/p/mode/can_leave/reshare_when_online"]]);
    PModeCanLeaveWaitingForNewShareAck_0.setTrigger(new Event("disconnected", null));
    this.states["/p/mode/can_leave/waiting_for_new_share_ack"].addTransition(PModeCanLeaveWaitingForNewShareAck_0);
    var PModeCanLeaveWaitingForNewShareAck_1 = new Transition(this, this.states["/p/mode/can_leave/waiting_for_new_share_ack"], [this.states["/p/mode/can_leave/session_set/joined"]]);
    PModeCanLeaveWaitingForNewShareAck_1.setAction(this.PModeCanLeaveWaitingForNewShareAck_1Exec);
    PModeCanLeaveWaitingForNewShareAck_1.setTrigger(new Event("ack_new_share", this.getInPortName("ack")));
    this.states["/p/mode/can_leave/waiting_for_new_share_ack"].addTransition(PModeCanLeaveWaitingForNewShareAck_1);
    
    // transition /p/mode/can_leave/reshare_when_online
    var PModeCanLeaveReshareWhenOnline_0 = new Transition(this, this.states["/p/mode/can_leave/reshare_when_online"], [this.states["/p/mode/can_leave/waiting_for_new_share_ack"]]);
    PModeCanLeaveReshareWhenOnline_0.setTrigger(new Event("connected", null));
    this.states["/p/mode/can_leave/reshare_when_online"].addTransition(PModeCanLeaveReshareWhenOnline_0);
    
    // transition /p/mode/can_leave/session_set/waiting_for_join_ack
    var PModeCanLeaveSessionSetWaitingForJoinAck_0 = new Transition(this, this.states["/p/mode/can_leave/session_set/waiting_for_join_ack"], [this.states["/p/mode/can_leave/session_set/joined"]]);
    PModeCanLeaveSessionSetWaitingForJoinAck_0.setAction(this.PModeCanLeaveSessionSetWaitingForJoinAck_0Exec);
    PModeCanLeaveSessionSetWaitingForJoinAck_0.setTrigger(new Event("ack_join", this.getInPortName("ack")));
    this.states["/p/mode/can_leave/session_set/waiting_for_join_ack"].addTransition(PModeCanLeaveSessionSetWaitingForJoinAck_0);
    var PModeCanLeaveSessionSetWaitingForJoinAck_1 = new Transition(this, this.states["/p/mode/can_leave/session_set/waiting_for_join_ack"], [this.states["/p/mode/can_leave/session_set/rejoin_when_online"]]);
    PModeCanLeaveSessionSetWaitingForJoinAck_1.setTrigger(new Event("disconnected", null));
    this.states["/p/mode/can_leave/session_set/waiting_for_join_ack"].addTransition(PModeCanLeaveSessionSetWaitingForJoinAck_1);
    
    // transition /p/mode/can_leave/session_set/joined/region_sendreceive/default
    var PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_0 = new Transition(this, this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"], [this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"]]);
    PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_0.setAction(this.PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_0Exec);
    PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_0.setTrigger(new Event("received", null));
    PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_0.setGuard(this.PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_0Guard);
    this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"].addTransition(PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_0);
    var PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_1 = new Transition(this, this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"], [this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"]]);
    PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_1.setAction(this.PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_1Exec);
    PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_1.setTrigger(new Event("new_edit", this.getInPortName("in")));
    this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"].addTransition(PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_1);
    var PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_2 = new Transition(this, this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"], [this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"]]);
    PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_2.setAction(this.PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_2Exec);
    PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_2.setTrigger(new Event("request_lock", this.getInPortName("in")));
    this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"].addTransition(PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_2);
    var PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_3 = new Transition(this, this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"], [this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"]]);
    PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_3.setAction(this.PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_3Exec);
    PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_3.setTrigger(new Event("release_lock", this.getInPortName("in")));
    this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"].addTransition(PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_3);
    var PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_4 = new Transition(this, this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"], [this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"]]);
    PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_4.setAction(this.PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_4Exec);
    PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_4.setTrigger(new Event("received", null));
    PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_4.setGuard(this.PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_4Guard);
    this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"].addTransition(PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_4);
    var PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_5 = new Transition(this, this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"], [this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"]]);
    PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_5.setAction(this.PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_5Exec);
    PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_5.setTrigger(new Event("broadcast_selection", null));
    this.states["/p/mode/can_leave/session_set/joined/region_sendreceive/default"].addTransition(PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_5);
    
    // transition /p/mode/can_leave/session_set/joined/region_cursor/nodelay
    var PModeCanLeaveSessionSetJoinedRegionCursorNodelay_0 = new Transition(this, this.states["/p/mode/can_leave/session_set/joined/region_cursor/nodelay"], [this.states["/p/mode/can_leave/session_set/joined/region_cursor/delay"]]);
    PModeCanLeaveSessionSetJoinedRegionCursorNodelay_0.setAction(this.PModeCanLeaveSessionSetJoinedRegionCursorNodelay_0Exec);
    PModeCanLeaveSessionSetJoinedRegionCursorNodelay_0.setTrigger(new Event("broadcast_cursor", this.getInPortName("in")));
    this.states["/p/mode/can_leave/session_set/joined/region_cursor/nodelay"].addTransition(PModeCanLeaveSessionSetJoinedRegionCursorNodelay_0);
    
    // transition /p/mode/can_leave/session_set/joined/region_cursor/delay/inner
    var PModeCanLeaveSessionSetJoinedRegionCursorDelayInner_0 = new Transition(this, this.states["/p/mode/can_leave/session_set/joined/region_cursor/delay/inner"], [this.states["/p/mode/can_leave/session_set/joined/region_cursor/delay/inner"]]);
    PModeCanLeaveSessionSetJoinedRegionCursorDelayInner_0.setAction(this.PModeCanLeaveSessionSetJoinedRegionCursorDelayInner_0Exec);
    PModeCanLeaveSessionSetJoinedRegionCursorDelayInner_0.setTrigger(new Event("broadcast_cursor", this.getInPortName("in")));
    this.states["/p/mode/can_leave/session_set/joined/region_cursor/delay/inner"].addTransition(PModeCanLeaveSessionSetJoinedRegionCursorDelayInner_0);
    
    // transition /p/mode/can_leave/session_set/rejoin_when_online
    var PModeCanLeaveSessionSetRejoinWhenOnline_0 = new Transition(this, this.states["/p/mode/can_leave/session_set/rejoin_when_online"], [this.states["/p/mode/can_leave/session_set/waiting_for_join_ack"]]);
    PModeCanLeaveSessionSetRejoinWhenOnline_0.setTrigger(new Event("connected", null));
    this.states["/p/mode/can_leave/session_set/rejoin_when_online"].addTransition(PModeCanLeaveSessionSetRejoinWhenOnline_0);
    
    // transition /p/socket_region/disconnected
    var PSocketRegionDisconnected_0 = new Transition(this, this.states["/p/socket_region/disconnected"], [this.states["/p/socket_region/connecting_or_connected"]]);
    PSocketRegionDisconnected_0.setAction(this.PSocketRegionDisconnected_0Exec);
    PSocketRegionDisconnected_0.setTrigger(new Event("connect", this.getInPortName("in")));
    this.states["/p/socket_region/disconnected"].addTransition(PSocketRegionDisconnected_0);
    
    // transition /p/socket_region/connecting_or_connected/connecting
    var PSocketRegionConnectingOrConnectedConnecting_0 = new Transition(this, this.states["/p/socket_region/connecting_or_connected/connecting"], [this.states["/p/socket_region/connecting_or_connected/connected"]]);
    PSocketRegionConnectingOrConnectedConnecting_0.setTrigger(new Event("open", this.getInPortName("socket")));
    this.states["/p/socket_region/connecting_or_connected/connecting"].addTransition(PSocketRegionConnectingOrConnectedConnecting_0);
    var PSocketRegionConnectingOrConnectedConnecting_1 = new Transition(this, this.states["/p/socket_region/connecting_or_connected/connecting"], [this.states["/p/socket_region/connecting_or_connected/wait_reconnect"]]);
    PSocketRegionConnectingOrConnectedConnecting_1.setAction(this.PSocketRegionConnectingOrConnectedConnecting_1Exec);
    PSocketRegionConnectingOrConnectedConnecting_1.setTrigger(new Event("error", this.getInPortName("socket")));
    this.states["/p/socket_region/connecting_or_connected/connecting"].addTransition(PSocketRegionConnectingOrConnectedConnecting_1);
    
    // transition /p/socket_region/connecting_or_connected/connected/send_receive_region/ready
    var PSocketRegionConnectingOrConnectedConnectedSendReceiveRegionReady_0 = new Transition(this, this.states["/p/socket_region/connecting_or_connected/connected/send_receive_region/ready"], [this.states["/p/socket_region/connecting_or_connected/connected/send_receive_region/ready"]]);
    PSocketRegionConnectingOrConnectedConnectedSendReceiveRegionReady_0.setAction(this.PSocketRegionConnectingOrConnectedConnectedSendReceiveRegionReady_0Exec);
    PSocketRegionConnectingOrConnectedConnectedSendReceiveRegionReady_0.setTrigger(new Event("send", null));
    this.states["/p/socket_region/connecting_or_connected/connected/send_receive_region/ready"].addTransition(PSocketRegionConnectingOrConnectedConnectedSendReceiveRegionReady_0);
    var PSocketRegionConnectingOrConnectedConnectedSendReceiveRegionReady_1 = new Transition(this, this.states["/p/socket_region/connecting_or_connected/connected/send_receive_region/ready"], [this.states["/p/socket_region/connecting_or_connected/connected/send_receive_region/ready"]]);
    PSocketRegionConnectingOrConnectedConnectedSendReceiveRegionReady_1.setAction(this.PSocketRegionConnectingOrConnectedConnectedSendReceiveRegionReady_1Exec);
    PSocketRegionConnectingOrConnectedConnectedSendReceiveRegionReady_1.setTrigger(new Event("message", this.getInPortName("socket")));
    PSocketRegionConnectingOrConnectedConnectedSendReceiveRegionReady_1.setGuard(this.PSocketRegionConnectingOrConnectedConnectedSendReceiveRegionReady_1Guard);
    this.states["/p/socket_region/connecting_or_connected/connected/send_receive_region/ready"].addTransition(PSocketRegionConnectingOrConnectedConnectedSendReceiveRegionReady_1);
    
    // transition /p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/send_pings/waiting
    var PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodSendPingsWaiting_0 = new Transition(this, this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/send_pings/waiting"], [this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/send_pings/waiting"]]);
    PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodSendPingsWaiting_0.setTrigger(new Event("send", this.getInPortName("in")));
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/send_pings/waiting"].addTransition(PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodSendPingsWaiting_0);
    var PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodSendPingsWaiting_1 = new Transition(this, this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/send_pings/waiting"], [this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/send_pings/waiting"]]);
    PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodSendPingsWaiting_1.setAction(this.PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodSendPingsWaiting_1Exec);
    PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodSendPingsWaiting_1.setTrigger(new Event("event1After"));
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/send_pings/waiting"].addTransition(PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodSendPingsWaiting_1);
    
    // transition /p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/receive_pongs/waiting
    var PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodReceivePongsWaiting_0 = new Transition(this, this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/receive_pongs/waiting"], [this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/receive_pongs/waiting"]]);
    PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodReceivePongsWaiting_0.setTrigger(new Event("message", this.getInPortName("socket")));
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/receive_pongs/waiting"].addTransition(PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodReceivePongsWaiting_0);
    var PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodReceivePongsWaiting_1 = new Transition(this, this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/receive_pongs/waiting"], [this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/receive_pongs/waiting"]]);
    PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodReceivePongsWaiting_1.setAction(this.PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodReceivePongsWaiting_1Exec);
    PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodReceivePongsWaiting_1.setTrigger(new Event("event2After"));
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good/receive_pongs/waiting"].addTransition(PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodReceivePongsWaiting_1);
    
    // transition /p/socket_region/connecting_or_connected/connected/connection_monitor_region/timeout
    var PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionTimeout_0 = new Transition(this, this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/timeout"], [this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good"]]);
    PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionTimeout_0.setAction(this.PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionTimeout_0Exec);
    PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionTimeout_0.setTrigger(new Event("message", this.getInPortName("socket")));
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/timeout"].addTransition(PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionTimeout_0);
    
    // transition /p/socket_region/connecting_or_connected/wait_reconnect
    var PSocketRegionConnectingOrConnectedWaitReconnect_0 = new Transition(this, this.states["/p/socket_region/connecting_or_connected/wait_reconnect"], [this.states["/p/socket_region/connecting_or_connected/connecting"]]);
    PSocketRegionConnectingOrConnectedWaitReconnect_0.setTrigger(new Event("event3After"));
    this.states["/p/socket_region/connecting_or_connected/wait_reconnect"].addTransition(PSocketRegionConnectingOrConnectedWaitReconnect_0);
    
    // transition /p/mode/can_leave
    var PModeCanLeave_0 = new Transition(this, this.states["/p/mode/can_leave"], [this.states["/p/mode/async_connected"]]);
    PModeCanLeave_0.setTrigger(new Event("leave", this.getInPortName("in")));
    PModeCanLeave_0.setGuard(this.PModeCanLeave_0Guard);
    this.states["/p/mode/can_leave"].addTransition(PModeCanLeave_0);
    var PModeCanLeave_1 = new Transition(this, this.states["/p/mode/can_leave"], [this.states["/p/mode/async_disconnected"]]);
    PModeCanLeave_1.setTrigger(new Event("leave", this.getInPortName("in")));
    PModeCanLeave_1.setGuard(this.PModeCanLeave_1Guard);
    this.states["/p/mode/can_leave"].addTransition(PModeCanLeave_1);
    
    // transition /p/mode/can_leave/session_set/joined
    var PModeCanLeaveSessionSetJoined_0 = new Transition(this, this.states["/p/mode/can_leave/session_set/joined"], [this.states["/p/mode/can_leave/session_set/rejoin_when_online"]]);
    PModeCanLeaveSessionSetJoined_0.setTrigger(new Event("disconnected", null));
    this.states["/p/mode/can_leave/session_set/joined"].addTransition(PModeCanLeaveSessionSetJoined_0);
    
    // transition /p/mode/can_leave/session_set/joined/region_cursor/delay
    var PModeCanLeaveSessionSetJoinedRegionCursorDelay_0 = new Transition(this, this.states["/p/mode/can_leave/session_set/joined/region_cursor/delay"], [this.states["/p/mode/can_leave/session_set/joined/region_cursor/nodelay"]]);
    PModeCanLeaveSessionSetJoinedRegionCursorDelay_0.setAction(this.PModeCanLeaveSessionSetJoinedRegionCursorDelay_0Exec);
    PModeCanLeaveSessionSetJoinedRegionCursorDelay_0.setTrigger(new Event("event0After"));
    this.states["/p/mode/can_leave/session_set/joined/region_cursor/delay"].addTransition(PModeCanLeaveSessionSetJoinedRegionCursorDelay_0);
    
    // transition /p/socket_region/connecting_or_connected
    var PSocketRegionConnectingOrConnected_0 = new Transition(this, this.states["/p/socket_region/connecting_or_connected"], [this.states["/p/socket_region/disconnected"]]);
    PSocketRegionConnectingOrConnected_0.setAction(this.PSocketRegionConnectingOrConnected_0Exec);
    PSocketRegionConnectingOrConnected_0.setTrigger(new Event("disconnect", this.getInPortName("in")));
    this.states["/p/socket_region/connecting_or_connected"].addTransition(PSocketRegionConnectingOrConnected_0);
    
    // transition /p/socket_region/connecting_or_connected/connected
    var PSocketRegionConnectingOrConnectedConnected_0 = new Transition(this, this.states["/p/socket_region/connecting_or_connected/connected"], [this.states["/p/socket_region/connecting_or_connected/wait_reconnect"]]);
    PSocketRegionConnectingOrConnectedConnected_0.setTrigger(new Event("close", this.getInPortName("socket")));
    this.states["/p/socket_region/connecting_or_connected/connected"].addTransition(PSocketRegionConnectingOrConnectedConnected_0);
    
    // transition /p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good
    var PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGood_0 = new Transition(this, this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good"], [this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/timeout"]]);
    PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGood_0.setAction(this.PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGood_0Exec);
    PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGood_0.setTrigger(new Event("timeout", null));
    this.states["/p/socket_region/connecting_or_connected/connected/connection_monitor_region/all_good"].addTransition(PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGood_0);
};

Main.prototype.PResponseHandlerEnter = function() {
    this.reqHandlers = new Map();
    this.reqCounter = 0;
    this.sendReq = (obj, handler) => {
      const reqId = this.reqCounter++;
      const req = {
        reqId,
        ...obj,
      };
      if (handler !== undefined) {
        this.reqHandlers.set(reqId, handler);
      }
      console.log("Sending req", req);
      this.addEvent(new Event("send", null, [req]));
    
      // allow to cancel the request handler
      return () => {
        this.reqHandlers.delete(reqId);
      };
    }
};

Main.prototype.PModeCanLeaveExit = function() {
    this.sendReq({type:"leave"});
};

Main.prototype.PModeCanLeaveSessionSetEnter = function() {
    if (!this.sessionId) {
      throw new Error("DEBUG: no session id");
    }
    this.uiState.setSession(this.sessionId);
};

Main.prototype.PModeCanLeaveSessionSetExit = function() {
    this.uiState.unsetSession();
};

Main.prototype.PModeCanLeaveSessionSetJoinedRegionCursorDelayEnter = function() {
    this.cursorState = null; 
    this.addTimer(0, 0.1);
};

Main.prototype.PModeCanLeaveSessionSetJoinedRegionCursorDelayExit = function() {
    this.removeTimer(0);
};

Main.prototype.PSocketRegionConnectingOrConnectedConnectedEnter = function() {
    this.raiseInternalEvent(new Event("connected", null, []));
    this.connected = true; 
};

Main.prototype.PSocketRegionConnectingOrConnectedConnectedExit = function() {
    this.raiseInternalEvent(new Event("disconnected", null, []));
    this.connected = false; 
};

Main.prototype.PModeAsyncDisconnectedEnter = function() {
    this.uiState.setOfflineDisconnected();
};

Main.prototype.PModeAsyncConnectedEnter = function() {
    this.uiState.setOfflineConnected();
};

Main.prototype.PModeCanLeaveWaitingForNewShareAckEnter = function() {
    this.uiState.setCreatingShare();
    this.deleteNewShareHandler = this.sendReq(
      {type: "new_share", ops: this.ops},
      res => {
        // called during transition (as action code), so no need to 'wake up' the controller like we do with 'addInput':
        this.addEvent(new Event("ack_new_share", "ack", [res.sessionId]));
      });
};

Main.prototype.PModeCanLeaveWaitingForNewShareAckExit = function() {
    this.deleteNewShareHandler();
};

Main.prototype.PModeCanLeaveReshareWhenOnlineEnter = function() {
    this.uiState.setReconnecting();
};

Main.prototype.PModeCanLeaveSessionSetWaitingForJoinAckEnter = function() {
    this.uiState.setJoining();
    
    this.deleteJoinHandler = this.sendReq(
      {type: "join", sessionId: this.sessionId},
      res => {
        // called during transition (as action code), so no need to 'wake up' the controller like we do with 'addInput':
        this.addEvent(new Event("ack_join", "ack", [res.ops]));
      });
};

Main.prototype.PModeCanLeaveSessionSetWaitingForJoinAckExit = function() {
    this.deleteJoinHandler();
};

Main.prototype.PModeCanLeaveSessionSetJoinedRegionSendreceiveDefaultEnter = function() {
    this.uiState.setOnline();
};

Main.prototype.PModeCanLeaveSessionSetRejoinWhenOnlineEnter = function() {
    this.uiState.setReconnecting();
};

Main.prototype.PSocketRegionConnectingOrConnectedConnectingEnter = function() {
    console.log("connecting...");
    
    this.socket = new WebSocket(this.addr);
    
    // Translate socket events to statechart events:
    
    this.socket.onopen = event => {
      this.controller.addInput("open", this.inports["socket"], [], this.controller.wallclockToSimtime());
    };
    
    this.socket.onmessage = event => {
      let parsed;
      try {
        parsed = JSON.parse(event.data);
      } catch (e) {
        console.log("received unparsable message", e);
        return;
      }
    
      this.controller.addInput("message", this.inports["socket"], [parsed], this.controller.wallclockToSimtime());
    };
    
    this.socket.onerror = event => {
      // From what I see, this event only happens when the socket could not connect.
      // An 'error' happening after the connection is established, will be indicated by a close event.
      this.controller.addInput("error", this.inports["socket"], [], this.controller.wallclockToSimtime());
    };
    
    this.socket.onclose = event => {
      this.controller.addInput("close", this.inports["socket"], [], this.controller.wallclockToSimtime());
    };
};

Main.prototype.PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodSendPingsWaitingEnter = function() {
    this.addTimer(1, 1);
};

Main.prototype.PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodSendPingsWaitingExit = function() {
    this.removeTimer(1);
};

Main.prototype.PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodReceivePongsWaitingEnter = function() {
    this.addTimer(2, 5);
};

Main.prototype.PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodReceivePongsWaitingExit = function() {
    this.removeTimer(2);
};

Main.prototype.PSocketRegionConnectingOrConnectedWaitReconnectEnter = function() {
    this.addTimer(3, 1);
};

Main.prototype.PSocketRegionConnectingOrConnectedWaitReconnectExit = function() {
    this.removeTimer(3);
};

Main.prototype.PModeCanLeave_0Guard = function(parameters) {
    return this.connected;
};

Main.prototype.PModeCanLeave_1Guard = function(parameters) {
    return !this.connected;
};

Main.prototype.PModeCanLeaveSessionSetJoinedRegionCursorDelay_0Exec = function(parameters) {
    if (this.cursorState !== null) {
      this.addEvent(new Event("send", null, [{
        type:"broadcast",
        msg: {
          type:"update_cursor",
          userId: this.myId,
          name: this.getMyName(),
          ...this.cursorState,
        },
      }]));
    }
};

Main.prototype.PSocketRegionConnectingOrConnected_0Exec = function(parameters) {
    this.socket.close();
};

Main.prototype.PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGood_0Exec = function(parameters) {
    this.raiseInternalEvent(new Event("timeout", null, []));
};

Main.prototype.PResponseHandlerDefault_0Exec = function(parameters) {
    var parsed = parameters[0];
    if (parsed.type === 'ack' && parsed.reqId !== undefined) {
      const handler = this.reqHandlers.get(parsed.reqId);
      if (handler) {
        handler(parsed);
      }
    }
};

Main.prototype.PResponseHandlerDefault_1Exec = function(parameters) {
    // no way we will get a response for our pending requests
    this.reqHandlers.clear();
    this.reqCounter = 0;
};

Main.prototype.PModeUninitialized_1Exec = function(parameters) {
    var sessionId = parameters[0];
    this.sessionId = sessionId;
};

Main.prototype.PModeAsyncConnected_1Exec = function(parameters) {
    var sessionId = parameters[0];
    this.sessionId = sessionId; 
};

Main.prototype.PModeAsyncConnected_2Exec = function(parameters) {
    var ops = parameters[0];
    // console.log("new share event, ops=", ops);
    this.ops = ops;
};

Main.prototype.PModeCanLeaveWaitingForNewShareAck_1Exec = function(parameters) {
    var sessionId = parameters[0];
    this.sessionId = sessionId;
    this.bigStep.outputEvent(new Event("ack_new_share", this.getOutPortName("out"), [sessionId]));
};

Main.prototype.PModeCanLeaveSessionSetWaitingForJoinAck_0Exec = function(parameters) {
    var ops = parameters[0];
    this.bigStep.outputEvent(new Event("ack_join", this.getOutPortName("out"), [ops]));
};

Main.prototype.PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_0Exec = function(parameters) {
    var parsed = parameters[0];
    if (parsed.sessionId !== this.sessionId) {
      throw new Error("Unexpected: received edit for another session:" +  parsed.sessionId);
    }
    this.bigStep.outputEvent(new Event("received_op", this.getOutPortName("out"), [parsed.op]));
};

Main.prototype.PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_0Guard = function(parameters) {
    var parsed = parameters[0];
    return parsed.type === 'pub_edit';
};

Main.prototype.PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_1Exec = function(parameters) {
    var serialized = parameters[0];
    this.sendReq(
      {type:"new_edit", sessionId: this.sessionId, op: serialized},
      res => {
        // this.unacknowledged.delete(serialized.id);
      });
    // this.unacknowledged.set(serialized.id, serialized);
};

Main.prototype.PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_2Exec = function(parameters) {
    var cellIds = parameters[0];
    var acquiredCallback = parameters[1];
    var deniedCallback = parameters[2];
    this.sendReq(
      {type:"request_lock", sessionId: this.sessionId, cellIds},
      res => {
        if (res.success) {
          acquiredCallback();
        } else {
          deniedCallback();
        }
      });
};

Main.prototype.PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_3Exec = function(parameters) {
    var cellIds = parameters[0];
    this.sendReq(
      {type:"release_lock", sessionId: this.sessionId, cellIds},
      res => {},
    );
};

Main.prototype.PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_4Exec = function(parameters) {
    var parsed = parameters[0];
    this.bigStep.outputEvent(new Event("broadcast", this.getOutPortName("out"), [parsed.sessionId, parsed.msg]));
};

Main.prototype.PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_4Guard = function(parameters) {
    var parsed = parameters[0];
    return parsed.type === 'broadcast';
};

Main.prototype.PModeCanLeaveSessionSetJoinedRegionSendreceiveDefault_5Exec = function(parameters) {
    var selectedIds = parameters[0];
    if (this.cursorState !== null) {
      this.addEvent(new Event("send", null, [{
        type:"broadcast",
        msg: {
          type:"update_selection",
          userId: this.myId,
          selectedIds,
          ...this.cursorState,
        },
      }]));
    }
};

Main.prototype.PModeCanLeaveSessionSetJoinedRegionCursorNodelay_0Exec = function(parameters) {
    var x = parameters[0];
    var y = parameters[1];
    this.raiseInternalEvent(new Event("send", null, [{type:"broadcast", msg: {type:"update_cursor", name: this.getMyName(), x, y, userId: this.myId}}]));
};

Main.prototype.PModeCanLeaveSessionSetJoinedRegionCursorDelayInner_0Exec = function(parameters) {
    var x = parameters[0];
    var y = parameters[1];
    this.cursorState = {x,y};
};

Main.prototype.PSocketRegionDisconnected_0Exec = function(parameters) {
    var addr = parameters[0];
    console.log("received connect", addr)
    this.addr = addr;
};

Main.prototype.PSocketRegionConnectingOrConnectedConnecting_1Exec = function(parameters) {
    this.raiseInternalEvent(new Event("error", null, []));
};

Main.prototype.PSocketRegionConnectingOrConnectedConnectedSendReceiveRegionReady_0Exec = function(parameters) {
    var json = parameters[0];
    this.socket.send(JSON.stringify(json));
};

Main.prototype.PSocketRegionConnectingOrConnectedConnectedSendReceiveRegionReady_1Exec = function(parameters) {
    var parsed = parameters[0];
    this.raiseInternalEvent(new Event("received", null, [parsed]));
};

Main.prototype.PSocketRegionConnectingOrConnectedConnectedSendReceiveRegionReady_1Guard = function(parameters) {
    var parsed = parameters[0];
    return parsed.type !== 'pong';
};

Main.prototype.PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodSendPingsWaiting_1Exec = function(parameters) {
    this.socket.send(JSON.stringify({type:"ping"}));
};

Main.prototype.PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionAllGoodReceivePongsWaiting_1Exec = function(parameters) {
    this.raiseInternalEvent(new Event("timeout", null, []));
};

Main.prototype.PSocketRegionConnectingOrConnectedConnectedConnectionMonitorRegionTimeout_0Exec = function(parameters) {
    this.raiseInternalEvent(new Event("timeout_recovered", null, []));
};

Main.prototype.initializeStatechart = function() {
    // enter default state
    this.defaultTargets = this.states["/p"].getEffectiveTargetStates();
    RuntimeClassBase.prototype.initializeStatechart.call(this);
};

module.exports.Main = Main;

let ObjectManager = function(controller) {
    ObjectManagerBase.call(this, controller);
};
ObjectManager.prototype = new Object();
(function() {
    var proto = new ObjectManagerBase();
    for (let prop in proto) {
        ObjectManager.prototype[prop] = proto[prop];
    }
})();

ObjectManager.prototype.instantiate = function(className, constructParams) {
    if (className === "Main") {
        var instance = new Main(this.controller, constructParams[0], constructParams[1], constructParams[2]);
        instance.associations = {};
        instance.associations["socket"] = new Association("Socket", 1, 1);
    } else  {
        throw new Error("Cannot instantiate class " + className);
    }
    return instance;
};

module.exports.ObjectManager = ObjectManager;

let Controller = function(uiState, myId, getMyName, eventLoopCallbacks, finishedCallback, behindScheduleCallback) {
    if (finishedCallback === undefined) finishedCallback = null;
    if (behindScheduleCallback === undefined) behindScheduleCallback = null;
    EventLoopControllerBase.call(this, new ObjectManager(this), eventLoopCallbacks, finishedCallback, behindScheduleCallback);
    this.addInputPort("in");
    this.addOutputPort("out");
    this.objectManager.createInstance("Main", [uiState, myId, getMyName]);
};
Controller.prototype = new Object();
(function() {
    var proto = new EventLoopControllerBase();
    for (let prop in proto) {
        Controller.prototype[prop] = proto[prop];
    }
})();

module.exports.Controller = Controller;
},{"statecharts_core":22}],5:[function(require,module,exports){
// Build this plugin with 'browserify':
//   browserify versioning.js > versioning.browser.js

Draw.loadPlugin(async function(ui) {

  const {Context, History, uuidv4} = require("doh");
  const {UserColors} = require("./UserColors.js")
  const {DragHandler} = require("./DragHandler.js");
  const {GhostOverlays} = require("./GhostOverlays.js");
  const {Controller: StatechartController} = require("./client_compiled.js");
  const {JsEventLoop} = require("statecharts_core");

  const myId = uuidv4();

  const names = ["Billy", "James", "Robert", "John", "Mary", "Patricia", "Jennifer", "Barbara"];
  let myName = window.localStorage.getItem('drawio-username');
  if (!myName) {
    myName = names[Math.floor(Math.random()*names.length)];
  }

  const graph = ui.editor.graph;
  const model = graph.model;

  // Interactive debugging
  window.ui = ui;
  window.graph = graph;
  window.model = model;

  const userColors = new UserColors();

  class EnabledState {
    constructor() {
      this.enabled = false;
    }
    enable() {
      this.enabled = true;
    }
    disable() {
      this.enabled = false;
    }
  }

  function workaroundPluginParameter(location) {
    // If the 'p' URL search parameter contained a ';' character, this will be (correctly) escaped.
    // However, drawio expects an actual ';' character (which is probably illegal in a search param value),
    // so we apply the following workaround:
    return location.replace('%3B', ';');
  }

  class UIState {
    constructor() {
      this.statusTextNode = document.createTextNode("");
      this.shareEnabled = new EnabledState();
      this.joinEnabled = new EnabledState();
      this.leaveEnabled = new EnabledState();
    }

    install(ui) {
      ui.toolbar.addSeparator();
      ui.menus.put('collab', new Menu((menu, parent) => {
        const buttonShare = menu.addItem("Share this diagram",
          null, // image
          () => {
            const ops = historyWrapper.history.getOpsSequence();
            const serialized = ops.map(op => op.serialize());
            controller.addInput("new_share", "in", [serialized], controller.wallclockToSimtime());
          }, // callback
          menu,
          null, // ?
          this.shareEnabled.enabled); // enabled
        const buttonJoin = menu.addItem("Join session",
          null, // image
          () => {
            const sessionId = window.prompt("Enter session ID:");
            if (sessionId) {
              controller.addInput("join", "in", [sessionId], controller.wallclockToSimtime());
            }
          }, // callback
          menu,
          null, // ?
          this.joinEnabled.enabled); // enabled
        const buttonLeave = menu.addItem("Leave session",
          null, // image
          () => {
            controller.addInput("leave", "in", [], controller.wallclockToSimtime());
          }, // callback
          menu,
          null, // ?
          this.leaveEnabled.enabled); // enabled
      }));

      const screenshareMenu = ui.toolbar.addMenu('', "Collaboration", true, 'collab');
      screenshareMenu.style.width = '100px';
      screenshareMenu.showDisabled = true;
      screenshareMenu.style.whiteSpace = 'nowrap';
      screenshareMenu.style.position = 'relative';
      screenshareMenu.style.overflow = 'hidden';
      screenshareMenu.innerHTML = "Collaboration" + ui.toolbar.dropdownImageHtml;

      ui.toolbar.addSeparator();

      const statusEl = document.createElement('div');
      statusEl.classList.add("geLabel");
      statusEl.style = "white-space: nowrap; position: relative;";
      statusEl.appendChild(this.statusTextNode);
      ui.toolbar.container.appendChild(statusEl);
    }

    setSession(sessionId) {
      const searchParams = new URLSearchParams(window.location.search);
      searchParams.set('sessionId', sessionId);
      const newLocation = window.location.origin + window.location.pathname + '?' + workaroundPluginParameter(searchParams.toString()) + window.location.hash;
      window.history.replaceState({}, "", newLocation);
    }

    unsetSession(sessionId) {
      const searchParams = new URLSearchParams(window.location.search);
      searchParams.delete('sessionId');
      const newLocation = window.location.origin + window.location.pathname + '?' + workaroundPluginParameter(searchParams.toString()) + window.location.hash;
      window.history.replaceState({}, "", newLocation);
    }

    setOfflineDisconnected() {
      graph.setEnabled(true);
      dragHandler.setOnline(false);
      this.statusTextNode.textContent = "Offline (no server connection)";

      this.shareEnabled.disable();
      this.joinEnabled.disable();
      this.leaveEnabled.disable();
    }

    setOfflineConnected() {
      graph.setEnabled(true);
      dragHandler.setOnline(false);
      this.statusTextNode.textContent = "Offline";

      this.shareEnabled.enable();
      this.joinEnabled.enable();
      this.leaveEnabled.disable();
    }

    setJoining(sessionId) {
      graph.setEnabled(false);
      this.statusTextNode.textContent = "Joining session...";

      this.shareEnabled.disable();
      this.joinEnabled.disable();
      this.leaveEnabled.enable();
    }

    setCreatingShare() {
      graph.setEnabled(false);
      this.statusTextNode.textContent = "Creating share...";

      this.shareEnabled.disable();
      this.joinEnabled.disable();
      this.leaveEnabled.enable();
    }

    setOnline(sessionId) {
      graph.setEnabled(true);
      dragHandler.setOnline(true);
      this.statusTextNode.textContent = "Online";

      this.shareEnabled.disable();
      this.joinEnabled.disable();
      this.leaveEnabled.enable();

    }

    setReconnecting() {
      graph.setEnabled(false);
      this.statusTextNode.textContent = "(re)Connecting...";

      this.shareEnabled.disable();
      this.joinEnabled.disable();
      this.leaveEnabled.enable();
    }
  }

  const uiState = new UIState();

  const codec = new mxCodec();
  const xmlSerializer = new XMLSerializer();
  const xmlParser = new DOMParser();

  function getCell(cellId) {
    let cell = model.cells[cellId];
    if (cell === undefined) {
      // throw new Error("NO SUCH CELL:", cellId);
    }
    return cell;
  }

  function createCell(cellId, isVertex, isEdge) {
    cell = new mxCell("");
    cell.setId(cellId);
    cell.setVertex(isVertex);
    cell.setEdge(isEdge);
    model.cells[cellId] = cell; // HACK!
    return cell;
  }

  const setState = (key, value) => {
    // console.log("setState", key, value);
    if (key === "root") {
      // the only global 'key'

      const rootId = value;
      const root = getCell(rootId) || createCell(rootId, false, false);
      model.setRoot(root);
    }
    else {
      // key is concatenation: <cellId> + '_' + <attribute>
      const splitPoint = key.lastIndexOf('_');
      const attribute = key.substring(splitPoint+1);
      const cellId = key.substring(0, splitPoint-2);
      const isVertex = key[splitPoint-1] === 'V';
      const isEdge = key[splitPoint-1] === 'E';

      const cell = getCell(cellId) || createCell(cellId, isVertex, isEdge);

      const attrHandlers = {
        geometry: () => {
          new mxGeometryChange(model, )
          const serializedGeometry = value;
          const parsed = xmlParser.parseFromString(serializedGeometry, 'text/xml');
          const geometry = codec.decode(parsed.documentElement);
          model.setGeometry(cell, geometry);
        },
        style: () => {
          const style = value;
          model.setStyle(cell, style);
        },
        parent: () => {
          if (value) {
            const {parentId} = value;
            let parent = getCell(parentId);
            if (parent === undefined) {
              console.log("creating non-existing parent");
              const {isVertex, isEdge} = value;
              parent = createCell(parentId, isVertex, isEdge);
            }
            // Creates a mxChildChange object
            model.add(parent, cell, null);
          } else {
            // Creates a mxChildChange object
            model.remove(cell);
          }
        },
        value: () => {
          let v;
          if (typeof value === 'object') {
            const {xmlEncoded} = value;
            v = xmlParser.parseFromString(xmlEncoded, 'text/xml').documentElement;
          } else {
            v = value;
          }
          model.setValue(cell, v);
        },
        source: () => {
          const sourceId = value;
          if (sourceId === null) {
            model.setTerminal(cell, null, true);
          } else {
            const source = getCell(sourceId)
              // if in the same user operation, a vertex is created and an edge
              // is connected to that vertex, it is possible that the connection
              // is made before the vertex is created (operations within a single user
              // operation are currently unordered)
              || createCell(sourceId,
                            /* isVertex */ true,
                            /* isEdge */ false);
            if (source === undefined) {
              throw new Error("NO SUCH CELL:", cellId);
            }
            model.setTerminal(cell, source, true);
          }
        },
        target: () => {
          const targetId = value;
          if (targetId === null) {
            model.setTerminal(cell, null, false);
          } else {
            const target = getCell(targetId)
              || createCell(targetId,
                            /* isVertex */ true,
                            /* isEdge */ false);
            if (target === undefined) {
              throw new Error("NO SUCH CELL:", cellId);
            }
            model.setTerminal(cell, target, false);
          }
        },
        collapsed: () => {
          const collapsed = value;
          model.setCollapsed(cell, collapsed);
        },
        visible: () => {
          const visible = value;
          model.setVisible(cell, visible);
        },
        vertex: () => {
          const vertex = value;
          cell.setVertex(vertex);
        },
        edge: () => {
          const edge = value;
          cell.setEdge(edge);
        },
      };
      attrHandlers[attribute]();
    }
  };

  const resolve = (a, b) => {
    console.log("CONFLICT between", a.id, "and", b.id);
    // Cheap & fun way to define a total ordering on all elements:
    return a.id > b.id;
  };

  // Fired when a local change happens
  model.addListener(mxEvent.NOTIFY, function(sender, event) {
    if (historyWrapper.transactionDepth === 0) {
      console.log("NOTIFY:", event.properties.edit.changes);

      const delta = {};
      for (const change of event.properties.edit.changes) {
        changeToDelta(change, delta);
      }
      if (Object.keys(delta).length > 0) {
        const op = historyWrapper.history.new(delta, 
          false // do NOT update mxGraphModel with the change (the change was already executed)
        );
        const serializedOp = op.serialize();
        console.log("NEW LOCAL OP:", serializedOp);
        controller.addInput("new_edit", "in", [serializedOp], controller.wallclockToSimtime());
      }
    }
  });

  class HistoryWrapper {
    constructor(graph, context) {
      this.graph = graph;
      this.context = context;
      this.transactionDepth = 0;
    }

    newEmptyDiagram() {
      console.log("newEmptyDiagram")
      this.history = new History(this.context, setState, resolve);
      this.graph.model.clear(); // the resetting of the diagram will be recorded into the (blank) history
      this.mergePromise = Promise.resolve();
    }

    // returns Promise
    loadExistingDiagram(ops) {
      console.log("loadExistingDiagram")
      this.graph.setEnabled(false);
      this.transactionDepth++; // prevent us from re-recording any changes
      this.graph.model.clear();
      this.history = new History(this.context, setState, resolve);
      this.mergePromise = Promise.resolve();
      for (const op of ops) {
        this.queuedMerge(op);
      }
      return this.mergePromise.then(() => {
        this.graph.setEnabled(true);
        this.transactionDepth--;
      });
    }

    // returns Promise
    queuedMerge(serializedOp) {
      this.mergePromise = this.mergePromise.then(() => {
        return this.context.receiveOperation(serializedOp).then(op => {
          console.log("Merging ", op.id);
          try {
            this.transactionDepth++;

            this.graph.model.beginUpdate();
            this.history.autoMerge(op);
            this.graph.model.endUpdate();
          }
          catch (e) {
            e.op = op;
            throw e;
          }
          finally {
            this.transactionDepth--;
          }
        });
      });
      return this.mergePromise;
    }
  }

  function getMyName() {
    return myName;
  }

  const controller = new StatechartController(uiState, myId, getMyName, new JsEventLoop());

  const selectionCounter = new Map();
  const userSelection = new Map();

  // const handleMergeError = err => {
  //   console.error("Unexpected error merging:", err);
  //   controller.addInput("leave", "in", [], controller.wallclockToSimtime());
  // }

  controller.addMyOwnOutputListener({
    'add': event => {
      // console.log("output event:", event);
      if (event.name === "ack_join") {
        const [ops] = event.parameters;
        historyWrapper.loadExistingDiagram(ops);
      }
      else if (event.name === "ack_new_share") {
        // nothing needs to happen
        const [sessionId] = event.parameters;
      }
      else if (event.name === "broadcast") {
        const [sessionId, msg] = event.parameters;
        if (msg.type === "pub_edit") {
          const {op} = msg;
          historyWrapper.queuedMerge(op);
        }
        else if (msg.type === "update_cursor") {
          const {userId, name, x, y} = msg;
          ghostOverlays.put(userId, name, x, y);
        }
        else if (msg.type === "update_selection") {
          const {userId, selectedIds} = msg;
          const oldHighlights = userSelection.get(userId) || [];
          for (const highlight of oldHighlights) {
            highlight.destroy();
          }
          const color = userColors.getColor(userId);
          const newHighlights = selectedIds.map(cellId => {
            const cell = graph.model.cells[cellId];
            const highlight = new mxCellHighlight(graph, color, 6);
            highlight.highlight(graph.view.getState(cell));
            return highlight;
          });
          userSelection.set(userId, newHighlights);
        }
      }
    }
  });

  const dragHandler = new DragHandler(controller);
  dragHandler.install(graph);

  const ghostOverlays = new GhostOverlays(graph, userColors, "myPlugins/cursor.svg");

  graph.addMouseListener({
    mouseDown(graph, event) {
    },
    mouseMove(graph, event) {
      const x = event.graphX / graph.view.scale - graph.view.translate.x;
      const y = event.graphY / graph.view.scale - graph.view.translate.y;
      controller.addInput("broadcast_cursor", "in", [x,y], controller.wallclockToSimtime());
    },
    mouseUp(graph, event) {
    },
  });

  graph.selectionModel.addListener(mxEvent.CHANGE, (source, eventObj) => {
    console.log("selectionModel event CHANGE");
    // const addedIds = (eventObj.properties.removed || []).map(cell => cell.id);
    // const removedIds = (eventObj.properties.added || []).map(cell => cell.id);
    const selectedIds = graph.getSelectionCells().map(cell => cell.id);
    controller.addInput(
      "broadcast_selection",
      "in",
      [selectedIds],
      controller.wallclockToSimtime(),
    );
  });

  {
    // Upon loading the page, if a 'sessionId' URL parameter is given, automatically join the given session once the connection with the server is established.
    const searchParams = new URLSearchParams(window.location.search);
    const sessionId = searchParams.get('sessionId');
    if (sessionId) {
      controller.addInput("init_join", "in", [sessionId], 0);
    } else {
      controller.addInput("init_offline", "in", [], 0);
    }
  }

  let websocketAddress;
  if (window.location.protocol === "http:") {
    websocketAddress = "ws:";
  } else if (window.location.protocol === "https:") {
    websocketAddress = "wss:";
  } else {
    throw new Error("unexpected protocol: " + window.location.protocol);
  }
  websocketAddress += "//" + window.location.host + "/websocket?me=" + myId;

  controller.addInput("connect", "in", [websocketAddress], 0);

  controller.start();

  function keyPrefix(cell) {
    return cell.id + '_' + (cell.isVertex() ? 'V' : cell.isEdge() ? 'E' : 'X') + '_';
  }

  function encodeCompleteCellState(cell, deltaObj) {
    const prefix = keyPrefix(cell);

    if (cell.geometry) {
      deltaObj[prefix + 'geometry'] = xmlSerializer.serializeToString(
        codec.encode(
          cell.geometry));
    }
    if (cell.style) {
      deltaObj[prefix + 'style'] = cell.style;
    }
    if (cell.parent) {
      deltaObj[prefix + 'parent'] = {
        parentId: cell.parent.id,
        isVertex: cell.parent.isVertex(),
        isEdge: cell.parent.isEdge(),
      };
    }
    if (cell.value) {
      // For some reason, 'value' can be an HTML string (when only a label is defined, or it can be a DOM object
      if (cell.value.constructor === String) {
        deltaObj[prefix + 'value'] = cell.value;
      } else {
        deltaObj[prefix + 'value'] = {
          xmlEncoded: xmlSerializer.serializeToString(cell.value),
        };
      }
    }
    if (cell.source) {
      deltaObj[prefix + 'source'] = cell.source.id;
    }
    if (cell.target) {
      deltaObj[prefix + 'target'] = cell.target.id;
    }
    if (cell.collapsed) {
      deltaObj[prefix + 'collapsed'] = cell.collapsed;
    }
    if (cell.visible !== undefined) {
      deltaObj[prefix + 'visible'] = cell.visible;
    }
  }

  const changeToDelta = (change, deltaObj) => {
    if (change.constructor === mxRootChange) {

      if (change.root === change.previous) {
        // This doesn't seem to occur:
        console.log("no root change")
        return; // no change
      }
      deltaObj['root'] = change.root.id;

      for (const cell of Object.values(model.cells)) {
        encodeCompleteCellState(cell, deltaObj);
      }
    }
    else {
      if (change.constructor === mxGeometryChange) {
        console.log(change.cell.id, 'change geometry')
        deltaObj[keyPrefix(change.cell) + 'geometry'] = change.geometry ?
          xmlSerializer.serializeToString(
            codec.encode(change.geometry)) : null;
      }
      else if (change.constructor === mxStyleChange) {
        deltaObj[keyPrefix(change.cell) + 'style'] = change.style;
      }
      else if (change.constructor === mxChildChange) {
        console.log(change.child.id, 'change parent')
        if (change.previous && model.contains(change.previous)) {
          // cell had a previous parent
          console.log("previous parent");
          deltaObj[keyPrefix(change.child) + 'parent'] = change.parent ? {
            parentId: change.parent.id,
            isVertex: change.parent.isVertex(),
            isEdge: change.parent.isEdge(),
          } : null;
        } else {
          // no previous parent -> cell was created:
          console.log("no previous parent");
          function recursivelyEncode(cell) {
            encodeCompleteCellState(cell, deltaObj);
            if (cell.children !== null) {
              for (const child of cell.children) {
                recursivelyEncode(child);
              }
            }
          }
          recursivelyEncode(change.child);
        }
      }
      else if (change.constructor === mxValueChange) {
        if (change.value.constructor === String) {
          deltaObj[keyPrefix(change.cell) + 'value'] = change.value;
        } else {
          deltaObj[keyPrefix(change.cell) + 'value'] = {
            xmlEncoded: xmlSerializer.serializeToString(change.value),
          };
        }
      }
      else if (change.constructor === mxTerminalChange) {
        deltaObj[keyPrefix(change.cell) + (change.source ? 'source' : 'target')] =
          change.terminal ? change.terminal.id : null;
      }
      else if (change.constructor === mxCollapseChange) {
        deltaObj[keyPrefix(change.cell) + 'collapsed'] = change.collapsed;
      }
      else if (change.constructor === mxVisibleChange) {
        deltaObj[keyPrefix(change.cell) + 'visible'] = change.visible;
      }
      else if (change.constructor === mxCellAttributeChange) {
        // Mentioned in mxGraph documentation, but what is this, and does it actually occur?
        throw new Error("DEBUG: Don't know what to do with mxCellAttributeChange");
      }
    }
  }


  // UI stuff
  uiState.install(ui);


  const context = new Context(async id => {
    throw new Error("Fetch is forbidden. ", id)
  });
  const historyWrapper = new HistoryWrapper(graph, context);
  historyWrapper.newEmptyDiagram();


  // HACK: disable undo
  
  mxUndoManager.prototype.canUndo = function() {
    return false;
  };
  mxUndoManager.prototype.canRedo = function() {
    return false;
  };


  // For debugging
  document.addEventListener('keydown', e => {
  //   console.log(e);
    // if (e.code === 'KeyC') {
    //   console.log("KeyC pressed: Clear model and reset history");
    //   historyWrapper.reset();
    // }

    // if (e.code === 'KeyH') {
    //   const seq = historyWrapper.history.getOpsSequence();
    //   console.log(seq.map(op => op.serialize()));
    // }

    // if (e.code === 'KeyU') {
    //   const u = window.prompt("Choose a user name:", myName);
    //   if (u) {
    //     myName = u;
    //   }
    // }
  });
});
},{"./DragHandler.js":1,"./GhostOverlays.js":2,"./UserColors.js":3,"./client_compiled.js":4,"doh":6,"statecharts_core":22}],6:[function(require,module,exports){
"use strict";

const { v4: uuidv4 } = require("uuid");

class Operation {
  constructor(id, detail) {
    this.id = id;
    this.detail = detail;
  }
  // Basically replaces JS references by IDs.
  // Result can be JSON'd with constant time+space complexity. Useful for sharing an edit over the network.
  serialize(op) {
    const self = this; // workaround
    return {
      id: this.id,
      detail: Object.fromEntries(
        (function*() {
          for (const [key, {value, parent, depth}] of self.detail.entries()) {
            yield [key, {
              value,
              parentId: parent.id,
              depth,
            }];
          }
        })()),
    }
  }
}

class Context {
  constructor(fetchCallback) {
    // Must be a function taking a single 'id' parameter, returning a Promise resolving to the serialized operation with the given id.
    this.fetchCallback = fetchCallback;

    // "Global" stuff. Operations have GUIDs but can also be shared between Histories. For instance, the 'initial' operation is the common root of all model histories. We could have put these things in a global variable, but that would make it more difficult to mock 'remoteness' (separate contexts) in tests.
    this.initialOp = new Operation("0", new Map()); // The parent of all parentless Operations. Root of all histories.
    this.ops = new Map(); // contains all pending or resolved operation-requests; mapping from operation-id to Promise resolving to Operation.
    this.ops.set(this.initialOp.id, Promise.resolve(this.initialOp));
  }

  // Get a promise resolving to the Operation with given ID. Fetches the operation (and recursively its dependencies) if necessary. Resolves when the operation and all its dependencies are present. Idempotent.
  requestOperation(id) {
    let promise = this.ops.get(id);
    if (promise === undefined) {
      promise = this.fetchCallback(id).then(serialized => this._awaitParents(serialized));
      this.ops.set(id, promise);
    }
    return promise;
  }

  // Similar to requestOperation, but instead the argument is an already fetched/received operation. Missing dependencies are (recursively) fetched, if necessary. Resolves when the operation and all its dependencies are present. Idempotent.
  receiveOperation(serialized) {
    let promise = this.ops.get(serialized.id);
    if (promise === undefined) {
      promise = this._awaitParents(serialized);
      this.ops.set(serialized.id, promise);
    }
    return promise;
  }

  // Internal function. Do not use directly.
  async _awaitParents({id, detail}) {
    const dependencies = Object.entries(detail).map(async ([key, {value, parentId, depth}]) => {
      return [key, {
        value,
        parent: await this.requestOperation(parentId),
        depth,
      }];
    });
    return new Operation(id, new Map(await Promise.all(dependencies)));
  }
}

class History {
  constructor(context, setState, resolve) {
    this.context = context;

    // callbacks
    this.setState = setState;
    this.resolve = resolve;

    this.heads = new Map(); // HEAD ptrs; mapping from key to Operation

    this.ops = new Map(); // Operations (winning and losing) that happened within this History.
    this.ops.set(context.initialOp.id, context.initialOp);

    this.childrenMapping = new Map(); // mapping from operation to object mapping key to current winning child.
  }

  _getHead(key) {
    const op = this.heads.get(key);
    if (op !== undefined) {
      return {
        op,
        depth: op.detail.get(key).depth,
      };
    };
    return {
      op: this.context.initialOp,
      depth: 0,
    };
  }

  _update_head(op) {
    for (const [key, {value}] of op.detail.entries()) {
      this.heads.set(key, op);
    }
  }

  _update_state(op) {
    for (const [key, {value}] of op.detail.entries()) {
      this.setState(key, value);
    }
  }

  _setChild(parent, key, child) {
    let childMap = this.childrenMapping.get(parent);
    if (childMap === undefined) {
      childMap = {};
      this.childrenMapping.set(parent, childMap);
    }
    childMap[key] = child;
  }

  _getChild(parent, key) {
    let childMap = this.childrenMapping.get(parent);
    if (childMap === undefined) return;
    return childMap[key];
  }

  // To be called when a new user operation has happened locally.
  // The new operation advances HEADs.
  new(v, updateState=true) {
    const newId = uuidv4();
    const detail = new Map(Object.entries(v).map(([key,value]) => {
      const {op: parent, depth} = this._getHead(key);
      return [key, {
        value,
        parent,
        depth: depth + 1,
      }];
    }));
    const newOp = new Operation(newId, detail);
    for (const [key, {parent}] of detail.entries()) {
      this._setChild(parent, key, newOp);
    }
    this._update_head(newOp);
    if (updateState) {
      this._update_state(newOp);
    }

    this.context.ops.set(newId, Promise.resolve(newOp));
    this.ops.set(newId, newOp);

    return newOp;
  }

  // Idempotent.
  autoMerge(op) {
    if (this.ops.has(op.id)) {
      // Already merged -> skip
      // console.log('skip (already merged)', op.id)
      return;
    }

    let exec = true;
    for (const [key, {parent}] of op.detail.entries()) {
      if (!this.ops.has(parent.id)) {
        // Update this History with operation's dependencies first
        this.autoMerge(parent);
      }

      // Check if there's a concurrent sibling with whom there is a conflict
      const sibling = this._getChild(parent, key);
      if (sibling) {
        // Conflict
        if (this.resolve(op, sibling)) {
          // console.log("conflict: op wins")
          const visited = new Set();
          const rollback = op => {
            visited.add(op); // Children form a DAG, with possible 'diamond' shapes -> prevent same operation from being visited more than once.
            for (const [key, {parent}] of op.detail.entries()) {
              // recurse, child-first
              const child = this._getChild(op, key);
              if (child && !visited.has(child)) {
                // (DFS) recursion
                rollback(child);
              }
              // rollback
              if (parent === this.context.initialOp) {
                // Invariant: HEADs never contains initialOp
                this.heads.delete(key);
                this.setState(key, undefined);
              } else {
                this.heads.set(key, parent);
                this.setState(key, parent.detail.get(key).value);
              }
            }
          };
          // Received operation wins conflict - state must be rolled back before executing it
          rollback(sibling);
        } else {
          // Received operation loses conflict - nothing to be done
          // console.log("conflict: op loses")
          exec = false;
          continue;
        }
      } else {
        // console.log('no conflict')
      }
      // won (or no conflict):
      this._setChild(parent, key, op);
      if (parent !== this._getHead(key).op) {
        // only execute received operation if it advances HEAD
        exec = false;
      }
    }

    if (exec) {
      this._update_head(op);
      this._update_state(op);
    }

    this.ops.set(op.id, op);
  }

  // Shorthand
  async receiveAndMerge(serializedOp) {
    const op = await this.context.receiveOperation(serializedOp);
    this.autoMerge(op);
    return op;
  }

  // Get operations in history in a sequence, such that any operation's dependencies precede it in the list. To reproduce the state of this History, operations can be executed in the returned order (front to back), and are guaranteed to not give conflicts.
  getOpsSequence() {
    const added = new Set([this.context.initialOp]);
    const visiting = new Set();
    const seq = [];
    const visit = op => {
      if (!added.has(op)) {
        visiting.add(op);
        for (const [key, {parent}] of op.detail.entries()) {
          visit(parent);
        }
        seq.push(op);
        added.add(op);
      }
    }
    for (const op of this.heads.values()) {
      visit(op);
    }
    return seq;
  }
}

module.exports = { Context, History, uuidv4 };

},{"uuid":7}],7:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "v1", {
  enumerable: true,
  get: function () {
    return _v.default;
  }
});
Object.defineProperty(exports, "v3", {
  enumerable: true,
  get: function () {
    return _v2.default;
  }
});
Object.defineProperty(exports, "v4", {
  enumerable: true,
  get: function () {
    return _v3.default;
  }
});
Object.defineProperty(exports, "v5", {
  enumerable: true,
  get: function () {
    return _v4.default;
  }
});
Object.defineProperty(exports, "NIL", {
  enumerable: true,
  get: function () {
    return _nil.default;
  }
});
Object.defineProperty(exports, "version", {
  enumerable: true,
  get: function () {
    return _version.default;
  }
});
Object.defineProperty(exports, "validate", {
  enumerable: true,
  get: function () {
    return _validate.default;
  }
});
Object.defineProperty(exports, "stringify", {
  enumerable: true,
  get: function () {
    return _stringify.default;
  }
});
Object.defineProperty(exports, "parse", {
  enumerable: true,
  get: function () {
    return _parse.default;
  }
});

var _v = _interopRequireDefault(require("./v1.js"));

var _v2 = _interopRequireDefault(require("./v3.js"));

var _v3 = _interopRequireDefault(require("./v4.js"));

var _v4 = _interopRequireDefault(require("./v5.js"));

var _nil = _interopRequireDefault(require("./nil.js"));

var _version = _interopRequireDefault(require("./version.js"));

var _validate = _interopRequireDefault(require("./validate.js"));

var _stringify = _interopRequireDefault(require("./stringify.js"));

var _parse = _interopRequireDefault(require("./parse.js"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
},{"./nil.js":9,"./parse.js":10,"./stringify.js":14,"./v1.js":15,"./v3.js":16,"./v4.js":18,"./v5.js":19,"./validate.js":20,"./version.js":21}],8:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

/*
 * Browser-compatible JavaScript MD5
 *
 * Modification of JavaScript MD5
 * https://github.com/blueimp/JavaScript-MD5
 *
 * Copyright 2011, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * https://opensource.org/licenses/MIT
 *
 * Based on
 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
 * Digest Algorithm, as defined in RFC 1321.
 * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
 * Distributed under the BSD License
 * See http://pajhome.org.uk/crypt/md5 for more info.
 */
function md5(bytes) {
  if (typeof bytes === 'string') {
    const msg = unescape(encodeURIComponent(bytes)); // UTF8 escape

    bytes = new Uint8Array(msg.length);

    for (let i = 0; i < msg.length; ++i) {
      bytes[i] = msg.charCodeAt(i);
    }
  }

  return md5ToHexEncodedArray(wordsToMd5(bytesToWords(bytes), bytes.length * 8));
}
/*
 * Convert an array of little-endian words to an array of bytes
 */


function md5ToHexEncodedArray(input) {
  const output = [];
  const length32 = input.length * 32;
  const hexTab = '0123456789abcdef';

  for (let i = 0; i < length32; i += 8) {
    const x = input[i >> 5] >>> i % 32 & 0xff;
    const hex = parseInt(hexTab.charAt(x >>> 4 & 0x0f) + hexTab.charAt(x & 0x0f), 16);
    output.push(hex);
  }

  return output;
}
/**
 * Calculate output length with padding and bit length
 */


function getOutputLength(inputLength8) {
  return (inputLength8 + 64 >>> 9 << 4) + 14 + 1;
}
/*
 * Calculate the MD5 of an array of little-endian words, and a bit length.
 */


function wordsToMd5(x, len) {
  /* append padding */
  x[len >> 5] |= 0x80 << len % 32;
  x[getOutputLength(len) - 1] = len;
  let a = 1732584193;
  let b = -271733879;
  let c = -1732584194;
  let d = 271733878;

  for (let i = 0; i < x.length; i += 16) {
    const olda = a;
    const oldb = b;
    const oldc = c;
    const oldd = d;
    a = md5ff(a, b, c, d, x[i], 7, -680876936);
    d = md5ff(d, a, b, c, x[i + 1], 12, -389564586);
    c = md5ff(c, d, a, b, x[i + 2], 17, 606105819);
    b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330);
    a = md5ff(a, b, c, d, x[i + 4], 7, -176418897);
    d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426);
    c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341);
    b = md5ff(b, c, d, a, x[i + 7], 22, -45705983);
    a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416);
    d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417);
    c = md5ff(c, d, a, b, x[i + 10], 17, -42063);
    b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162);
    a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682);
    d = md5ff(d, a, b, c, x[i + 13], 12, -40341101);
    c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290);
    b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329);
    a = md5gg(a, b, c, d, x[i + 1], 5, -165796510);
    d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632);
    c = md5gg(c, d, a, b, x[i + 11], 14, 643717713);
    b = md5gg(b, c, d, a, x[i], 20, -373897302);
    a = md5gg(a, b, c, d, x[i + 5], 5, -701558691);
    d = md5gg(d, a, b, c, x[i + 10], 9, 38016083);
    c = md5gg(c, d, a, b, x[i + 15], 14, -660478335);
    b = md5gg(b, c, d, a, x[i + 4], 20, -405537848);
    a = md5gg(a, b, c, d, x[i + 9], 5, 568446438);
    d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690);
    c = md5gg(c, d, a, b, x[i + 3], 14, -187363961);
    b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501);
    a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467);
    d = md5gg(d, a, b, c, x[i + 2], 9, -51403784);
    c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473);
    b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734);
    a = md5hh(a, b, c, d, x[i + 5], 4, -378558);
    d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463);
    c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562);
    b = md5hh(b, c, d, a, x[i + 14], 23, -35309556);
    a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060);
    d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353);
    c = md5hh(c, d, a, b, x[i + 7], 16, -155497632);
    b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640);
    a = md5hh(a, b, c, d, x[i + 13], 4, 681279174);
    d = md5hh(d, a, b, c, x[i], 11, -358537222);
    c = md5hh(c, d, a, b, x[i + 3], 16, -722521979);
    b = md5hh(b, c, d, a, x[i + 6], 23, 76029189);
    a = md5hh(a, b, c, d, x[i + 9], 4, -640364487);
    d = md5hh(d, a, b, c, x[i + 12], 11, -421815835);
    c = md5hh(c, d, a, b, x[i + 15], 16, 530742520);
    b = md5hh(b, c, d, a, x[i + 2], 23, -995338651);
    a = md5ii(a, b, c, d, x[i], 6, -198630844);
    d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415);
    c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905);
    b = md5ii(b, c, d, a, x[i + 5], 21, -57434055);
    a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571);
    d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606);
    c = md5ii(c, d, a, b, x[i + 10], 15, -1051523);
    b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799);
    a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359);
    d = md5ii(d, a, b, c, x[i + 15], 10, -30611744);
    c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380);
    b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649);
    a = md5ii(a, b, c, d, x[i + 4], 6, -145523070);
    d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379);
    c = md5ii(c, d, a, b, x[i + 2], 15, 718787259);
    b = md5ii(b, c, d, a, x[i + 9], 21, -343485551);
    a = safeAdd(a, olda);
    b = safeAdd(b, oldb);
    c = safeAdd(c, oldc);
    d = safeAdd(d, oldd);
  }

  return [a, b, c, d];
}
/*
 * Convert an array bytes to an array of little-endian words
 * Characters >255 have their high-byte silently ignored.
 */


function bytesToWords(input) {
  if (input.length === 0) {
    return [];
  }

  const length8 = input.length * 8;
  const output = new Uint32Array(getOutputLength(length8));

  for (let i = 0; i < length8; i += 8) {
    output[i >> 5] |= (input[i / 8] & 0xff) << i % 32;
  }

  return output;
}
/*
 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
 * to work around bugs in some JS interpreters.
 */


function safeAdd(x, y) {
  const lsw = (x & 0xffff) + (y & 0xffff);
  const msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  return msw << 16 | lsw & 0xffff;
}
/*
 * Bitwise rotate a 32-bit number to the left.
 */


function bitRotateLeft(num, cnt) {
  return num << cnt | num >>> 32 - cnt;
}
/*
 * These functions implement the four basic operations the algorithm uses.
 */


function md5cmn(q, a, b, x, s, t) {
  return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b);
}

function md5ff(a, b, c, d, x, s, t) {
  return md5cmn(b & c | ~b & d, a, b, x, s, t);
}

function md5gg(a, b, c, d, x, s, t) {
  return md5cmn(b & d | c & ~d, a, b, x, s, t);
}

function md5hh(a, b, c, d, x, s, t) {
  return md5cmn(b ^ c ^ d, a, b, x, s, t);
}

function md5ii(a, b, c, d, x, s, t) {
  return md5cmn(c ^ (b | ~d), a, b, x, s, t);
}

var _default = md5;
exports.default = _default;
},{}],9:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _default = '00000000-0000-0000-0000-000000000000';
exports.default = _default;
},{}],10:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _validate = _interopRequireDefault(require("./validate.js"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function parse(uuid) {
  if (!(0, _validate.default)(uuid)) {
    throw TypeError('Invalid UUID');
  }

  let v;
  const arr = new Uint8Array(16); // Parse ########-....-....-....-............

  arr[0] = (v = parseInt(uuid.slice(0, 8), 16)) >>> 24;
  arr[1] = v >>> 16 & 0xff;
  arr[2] = v >>> 8 & 0xff;
  arr[3] = v & 0xff; // Parse ........-####-....-....-............

  arr[4] = (v = parseInt(uuid.slice(9, 13), 16)) >>> 8;
  arr[5] = v & 0xff; // Parse ........-....-####-....-............

  arr[6] = (v = parseInt(uuid.slice(14, 18), 16)) >>> 8;
  arr[7] = v & 0xff; // Parse ........-....-....-####-............

  arr[8] = (v = parseInt(uuid.slice(19, 23), 16)) >>> 8;
  arr[9] = v & 0xff; // Parse ........-....-....-....-############
  // (Use "/" to avoid 32-bit truncation when bit-shifting high-order bytes)

  arr[10] = (v = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000 & 0xff;
  arr[11] = v / 0x100000000 & 0xff;
  arr[12] = v >>> 24 & 0xff;
  arr[13] = v >>> 16 & 0xff;
  arr[14] = v >>> 8 & 0xff;
  arr[15] = v & 0xff;
  return arr;
}

var _default = parse;
exports.default = _default;
},{"./validate.js":20}],11:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _default = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;
exports.default = _default;
},{}],12:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = rng;
// Unique ID creation requires a high quality random # generator. In the browser we therefore
// require the crypto API and do not support built-in fallback to lower quality random number
// generators (like Math.random()).
let getRandomValues;
const rnds8 = new Uint8Array(16);

function rng() {
  // lazy load so that environments that need to polyfill have a chance to do so
  if (!getRandomValues) {
    // getRandomValues needs to be invoked in a context where "this" is a Crypto implementation. Also,
    // find the complete implementation of crypto (msCrypto) on IE11.
    getRandomValues = typeof crypto !== 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto) || typeof msCrypto !== 'undefined' && typeof msCrypto.getRandomValues === 'function' && msCrypto.getRandomValues.bind(msCrypto);

    if (!getRandomValues) {
      throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported');
    }
  }

  return getRandomValues(rnds8);
}
},{}],13:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

// Adapted from Chris Veness' SHA1 code at
// http://www.movable-type.co.uk/scripts/sha1.html
function f(s, x, y, z) {
  switch (s) {
    case 0:
      return x & y ^ ~x & z;

    case 1:
      return x ^ y ^ z;

    case 2:
      return x & y ^ x & z ^ y & z;

    case 3:
      return x ^ y ^ z;
  }
}

function ROTL(x, n) {
  return x << n | x >>> 32 - n;
}

function sha1(bytes) {
  const K = [0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6];
  const H = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0];

  if (typeof bytes === 'string') {
    const msg = unescape(encodeURIComponent(bytes)); // UTF8 escape

    bytes = [];

    for (let i = 0; i < msg.length; ++i) {
      bytes.push(msg.charCodeAt(i));
    }
  } else if (!Array.isArray(bytes)) {
    // Convert Array-like to Array
    bytes = Array.prototype.slice.call(bytes);
  }

  bytes.push(0x80);
  const l = bytes.length / 4 + 2;
  const N = Math.ceil(l / 16);
  const M = new Array(N);

  for (let i = 0; i < N; ++i) {
    const arr = new Uint32Array(16);

    for (let j = 0; j < 16; ++j) {
      arr[j] = bytes[i * 64 + j * 4] << 24 | bytes[i * 64 + j * 4 + 1] << 16 | bytes[i * 64 + j * 4 + 2] << 8 | bytes[i * 64 + j * 4 + 3];
    }

    M[i] = arr;
  }

  M[N - 1][14] = (bytes.length - 1) * 8 / Math.pow(2, 32);
  M[N - 1][14] = Math.floor(M[N - 1][14]);
  M[N - 1][15] = (bytes.length - 1) * 8 & 0xffffffff;

  for (let i = 0; i < N; ++i) {
    const W = new Uint32Array(80);

    for (let t = 0; t < 16; ++t) {
      W[t] = M[i][t];
    }

    for (let t = 16; t < 80; ++t) {
      W[t] = ROTL(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1);
    }

    let a = H[0];
    let b = H[1];
    let c = H[2];
    let d = H[3];
    let e = H[4];

    for (let t = 0; t < 80; ++t) {
      const s = Math.floor(t / 20);
      const T = ROTL(a, 5) + f(s, b, c, d) + e + K[s] + W[t] >>> 0;
      e = d;
      d = c;
      c = ROTL(b, 30) >>> 0;
      b = a;
      a = T;
    }

    H[0] = H[0] + a >>> 0;
    H[1] = H[1] + b >>> 0;
    H[2] = H[2] + c >>> 0;
    H[3] = H[3] + d >>> 0;
    H[4] = H[4] + e >>> 0;
  }

  return [H[0] >> 24 & 0xff, H[0] >> 16 & 0xff, H[0] >> 8 & 0xff, H[0] & 0xff, H[1] >> 24 & 0xff, H[1] >> 16 & 0xff, H[1] >> 8 & 0xff, H[1] & 0xff, H[2] >> 24 & 0xff, H[2] >> 16 & 0xff, H[2] >> 8 & 0xff, H[2] & 0xff, H[3] >> 24 & 0xff, H[3] >> 16 & 0xff, H[3] >> 8 & 0xff, H[3] & 0xff, H[4] >> 24 & 0xff, H[4] >> 16 & 0xff, H[4] >> 8 & 0xff, H[4] & 0xff];
}

var _default = sha1;
exports.default = _default;
},{}],14:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _validate = _interopRequireDefault(require("./validate.js"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/**
 * Convert array of 16 byte values to UUID string format of the form:
 * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
 */
const byteToHex = [];

for (let i = 0; i < 256; ++i) {
  byteToHex.push((i + 0x100).toString(16).substr(1));
}

function stringify(arr, offset = 0) {
  // Note: Be careful editing this code!  It's been tuned for performance
  // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434
  const uuid = (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); // Consistency check for valid UUID.  If this throws, it's likely due to one
  // of the following:
  // - One or more input array values don't map to a hex octet (leading to
  // "undefined" in the uuid)
  // - Invalid input values for the RFC `version` or `variant` fields

  if (!(0, _validate.default)(uuid)) {
    throw TypeError('Stringified UUID is invalid');
  }

  return uuid;
}

var _default = stringify;
exports.default = _default;
},{"./validate.js":20}],15:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _rng = _interopRequireDefault(require("./rng.js"));

var _stringify = _interopRequireDefault(require("./stringify.js"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

// **`v1()` - Generate time-based UUID**
//
// Inspired by https://github.com/LiosK/UUID.js
// and http://docs.python.org/library/uuid.html
let _nodeId;

let _clockseq; // Previous uuid creation time


let _lastMSecs = 0;
let _lastNSecs = 0; // See https://github.com/uuidjs/uuid for API details

function v1(options, buf, offset) {
  let i = buf && offset || 0;
  const b = buf || new Array(16);
  options = options || {};
  let node = options.node || _nodeId;
  let clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq; // node and clockseq need to be initialized to random values if they're not
  // specified.  We do this lazily to minimize issues related to insufficient
  // system entropy.  See #189

  if (node == null || clockseq == null) {
    const seedBytes = options.random || (options.rng || _rng.default)();

    if (node == null) {
      // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)
      node = _nodeId = [seedBytes[0] | 0x01, seedBytes[1], seedBytes[2], seedBytes[3], seedBytes[4], seedBytes[5]];
    }

    if (clockseq == null) {
      // Per 4.2.2, randomize (14 bit) clockseq
      clockseq = _clockseq = (seedBytes[6] << 8 | seedBytes[7]) & 0x3fff;
    }
  } // UUID timestamps are 100 nano-second units since the Gregorian epoch,
  // (1582-10-15 00:00).  JSNumbers aren't precise enough for this, so
  // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'
  // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.


  let msecs = options.msecs !== undefined ? options.msecs : Date.now(); // Per 4.2.1.2, use count of uuid's generated during the current clock
  // cycle to simulate higher resolution clock

  let nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1; // Time since last uuid creation (in msecs)

  const dt = msecs - _lastMSecs + (nsecs - _lastNSecs) / 10000; // Per 4.2.1.2, Bump clockseq on clock regression

  if (dt < 0 && options.clockseq === undefined) {
    clockseq = clockseq + 1 & 0x3fff;
  } // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new
  // time interval


  if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) {
    nsecs = 0;
  } // Per 4.2.1.2 Throw error if too many uuids are requested


  if (nsecs >= 10000) {
    throw new Error("uuid.v1(): Can't create more than 10M uuids/sec");
  }

  _lastMSecs = msecs;
  _lastNSecs = nsecs;
  _clockseq = clockseq; // Per 4.1.4 - Convert from unix epoch to Gregorian epoch

  msecs += 12219292800000; // `time_low`

  const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
  b[i++] = tl >>> 24 & 0xff;
  b[i++] = tl >>> 16 & 0xff;
  b[i++] = tl >>> 8 & 0xff;
  b[i++] = tl & 0xff; // `time_mid`

  const tmh = msecs / 0x100000000 * 10000 & 0xfffffff;
  b[i++] = tmh >>> 8 & 0xff;
  b[i++] = tmh & 0xff; // `time_high_and_version`

  b[i++] = tmh >>> 24 & 0xf | 0x10; // include version

  b[i++] = tmh >>> 16 & 0xff; // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)

  b[i++] = clockseq >>> 8 | 0x80; // `clock_seq_low`

  b[i++] = clockseq & 0xff; // `node`

  for (let n = 0; n < 6; ++n) {
    b[i + n] = node[n];
  }

  return buf || (0, _stringify.default)(b);
}

var _default = v1;
exports.default = _default;
},{"./rng.js":12,"./stringify.js":14}],16:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _v = _interopRequireDefault(require("./v35.js"));

var _md = _interopRequireDefault(require("./md5.js"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const v3 = (0, _v.default)('v3', 0x30, _md.default);
var _default = v3;
exports.default = _default;
},{"./md5.js":8,"./v35.js":17}],17:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;
exports.URL = exports.DNS = void 0;

var _stringify = _interopRequireDefault(require("./stringify.js"));

var _parse = _interopRequireDefault(require("./parse.js"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function stringToBytes(str) {
  str = unescape(encodeURIComponent(str)); // UTF8 escape

  const bytes = [];

  for (let i = 0; i < str.length; ++i) {
    bytes.push(str.charCodeAt(i));
  }

  return bytes;
}

const DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
exports.DNS = DNS;
const URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';
exports.URL = URL;

function _default(name, version, hashfunc) {
  function generateUUID(value, namespace, buf, offset) {
    if (typeof value === 'string') {
      value = stringToBytes(value);
    }

    if (typeof namespace === 'string') {
      namespace = (0, _parse.default)(namespace);
    }

    if (namespace.length !== 16) {
      throw TypeError('Namespace must be array-like (16 iterable integer values, 0-255)');
    } // Compute hash of namespace and value, Per 4.3
    // Future: Use spread syntax when supported on all platforms, e.g. `bytes =
    // hashfunc([...namespace, ... value])`


    let bytes = new Uint8Array(16 + value.length);
    bytes.set(namespace);
    bytes.set(value, namespace.length);
    bytes = hashfunc(bytes);
    bytes[6] = bytes[6] & 0x0f | version;
    bytes[8] = bytes[8] & 0x3f | 0x80;

    if (buf) {
      offset = offset || 0;

      for (let i = 0; i < 16; ++i) {
        buf[offset + i] = bytes[i];
      }

      return buf;
    }

    return (0, _stringify.default)(bytes);
  } // Function#name is not settable on some platforms (#270)


  try {
    generateUUID.name = name; // eslint-disable-next-line no-empty
  } catch (err) {} // For CommonJS default export support


  generateUUID.DNS = DNS;
  generateUUID.URL = URL;
  return generateUUID;
}
},{"./parse.js":10,"./stringify.js":14}],18:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _rng = _interopRequireDefault(require("./rng.js"));

var _stringify = _interopRequireDefault(require("./stringify.js"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function v4(options, buf, offset) {
  options = options || {};

  const rnds = options.random || (options.rng || _rng.default)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`


  rnds[6] = rnds[6] & 0x0f | 0x40;
  rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided

  if (buf) {
    offset = offset || 0;

    for (let i = 0; i < 16; ++i) {
      buf[offset + i] = rnds[i];
    }

    return buf;
  }

  return (0, _stringify.default)(rnds);
}

var _default = v4;
exports.default = _default;
},{"./rng.js":12,"./stringify.js":14}],19:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _v = _interopRequireDefault(require("./v35.js"));

var _sha = _interopRequireDefault(require("./sha1.js"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const v5 = (0, _v.default)('v5', 0x50, _sha.default);
var _default = v5;
exports.default = _default;
},{"./sha1.js":13,"./v35.js":17}],20:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _regex = _interopRequireDefault(require("./regex.js"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function validate(uuid) {
  return typeof uuid === 'string' && _regex.default.test(uuid);
}

var _default = validate;
exports.default = _default;
},{"./regex.js":11}],21:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _validate = _interopRequireDefault(require("./validate.js"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function version(uuid) {
  if (!(0, _validate.default)(uuid)) {
    throw TypeError('Invalid UUID');
  }

  return parseInt(uuid.substr(14, 1), 16);
}

var _default = version;
exports.default = _default;
},{"./validate.js":20}],22:[function(require,module,exports){
/**
 * SCCD runtime for JavaScript. Works with 'javascript' (browser) and 'nodejs' targets.
 * For browser, you can load the script in your HTML by adding a <script> tag.
 * For NodeJS, you can require() this file.
 */

nextObjectId = 1;

function objectId(obj) {
  if (obj == null) {
    return null;
  }
  if (obj.assignedObjectId == null) {
    obj.assignedObjectId = nextObjectId++;
  }
  return obj.assignedObjectId;
}

function time() {
  return (new Date).getTime();
}

function RuntimeException(msg) {
  this.msg = msg;
}

function InputException(msg) {
  RuntimeException.call(this, msg);
}

InputException.prototype = new RuntimeException();

function AssociationException(msg) {
  RuntimeException.call(this, msg);
}

AssociationException.prototype = new RuntimeException();

function AssociationReferenceException(msg) {
  RuntimeException.call(this, msg);
}

AssociationReferenceException.prototype = new RuntimeException();

function ParameterException(msg) {
  RuntimeException.call(this, msg);
}

ParameterException.prototype = new RuntimeException();

function EventQueueEntry(scheduledTime, theEvent, instances) {
  if (!(instances instanceof Array)) {
    throw new Error("Must be array")
  }
  this.scheduledTime = scheduledTime;
  this.theEvent = theEvent;
  this.instances = instances; // list of instances to receive the event
}

function EventQueue() {
  this.eventList = [];
  this.sortFunction = function (a, b) {
    return a.scheduledTime - b.scheduledTime
  }
}

EventQueue.prototype.isEmpty = function () {
  return this.eventList.length === 0;
};

EventQueue.prototype.getEarliestTime = function () {
  if (this.isEmpty()) {
    return Infinity;
  } else {
    return this.eventList[0].scheduledTime;
  }
};

EventQueue.prototype.add = function (theEvent) {
  this.eventList.push(theEvent);
  this.eventList.sort(this.sortFunction);
  return objectId(theEvent);
};

EventQueue.prototype.remove = function (eventId) {
  this.eventList = this.eventList.filter(function (el) {
    return objectId(el) !== eventId;
  }).sort()
}

EventQueue.prototype.pop = function () {
  return this.eventList.shift()
};

function Association(toClass, minCard, maxCard) {
  this.toClass = toClass;
  this.minCard = minCard;
  this.maxCard = maxCard;
  this.instances = {};
  this.size = 0;
  this.nextId = 0;
}

Association.prototype.allowedToAdd = function () {
  return (this.maxCard === -1 || this.size < this.maxCard);
};

Association.prototype.allowedToRemove = function () {
  return (this.minCard === -1 || this.size > this.minCard);
};

Association.prototype.addInstance = function (instance) {
  if (this.allowedToAdd()) {
    const id = this.nextId++;
    this.instances[id] = instance;
    return id;
  } else {
    throw new AssociationException('Not allowed to add the instance to the association.');
  }
};

Association.prototype.removeInstance = function (instance) {
  if (this.allowedToRemove()) {
    delete this.instances[this.instancesToIds[instance]]
  } else {
    throw new AssociationException('Not allowed to remove the instance to the association.');
  }
};

Association.prototype.getInstance = function (index) {
  const instance = this.instances[index];
  if (instance === undefined) {
    throw new AssociationException('Invalid index for fetching instance(s) from association.');
  }
  return instance;
};

function ObjectManagerBase(controller) {
  this.controller = controller;
  // this.events = new EventQueue();
  this.instances = [];
  // this.regex = /^([a-zA-Z_]\w*)(?:\[(\d+)])?$/;
  this.regex = /^([a-zA-Z_]\w*)(?:\[(\d+)\])?$/;
}

// ObjectManagerBase.prototype.addEvent = function (theEvent, timeOffset) {
//   if (timeOffset === undefined) timeOffset = 0;
//   this.events.add(new EventQueueEntry(this.controller.simulatedTime + timeOffset, theEvent));
// };

// ObjectManagerBase.prototype.broadcast = function (source, newEvent, timeOffset) {
//   if (timeOffset === undefined) timeOffset = 0;
//   for (let i in this.instances) {
//     if (!this.instances.hasOwnProperty(i)  || i === source) continue;
//     this.instances[i].addEvent(newEvent, timeOffset);
//   }
// };

// ObjectManagerBase.prototype.getEarliestEventTime = function () {
//   let earliestTime = this.events.getEarliestTime();
//   for (let i in this.instances) {
//     if (!this.instances.hasOwnProperty(i)) continue;
//     earliestTime = Math.min(earliestTime, this.instances[i].earliestEventTime);
//   }
//   return earliestTime;
// };

// ObjectManagerBase.prototype.stepAll = function () {
//   this.step();
//   for (let i in this.instances) {
//     if (!this.instances.hasOwnProperty(i)) continue;
//     let instance = this.instances[i];
//     if (instance.active && (instance.getEarliestEventTime() <= this.controller.simulatedTime || instance.eventlessTransitions())) {
//       instance.step();
//     }
//   }
// };

// ObjectManagerBase.prototype.step = function () {
//   while (this.events.getEarliestTime() <= this.controller.simulatedTime) {
//     this.handleEvent(this.events.pop());
//   }
// };

ObjectManagerBase.prototype.initializeAll = function () {
  for (const i of this.instances) {
    i.initialize();
  }
};

ObjectManagerBase.prototype.performBigStep = function (eventList) {
  for (const e of eventList) {
    if (e.name === 'narrowCast') {
      this.handleNarrowCastEvent(e.parameters);
    } else if (e.name === 'broadCast') {
      this.handleBroadcastEvent(e.parameters);
    } else if (e.name === 'createInstance') {
      this.handleCreateEvent(e.parameters);
    } else if (e.name === 'associateInstance') {
      this.handleAssociateEvent(e.parameters);
    } else if (e.name === 'disassociateInstance') {
      this.handleDisassociateEvent(e.parameters);
    } else if (e.name === 'startInstance') {
      this.handleStartInstanceEvent(e.parameters);
    } else if (e.name === 'createAndStartInstance') {
      this.handleCreateAndStartEvent(e.parameters);
    } else if (e.name === 'deleteInstance') {
      this.handleDeleteInstanceEvent(e.parameters);
    }
  }
};

// just to comply with the RuntimeClassBase interface, ducktype-wise:
ObjectManagerBase.prototype.processBigStepOutput = function () {
}

ObjectManagerBase.prototype.processAssociationReference = function (inputString) {
  if (inputString.length === 0) {
    throw new AssociationReferenceException('Empty association reference.');
  }
  const pathString = inputString.split('/');
  let result = [];
  if (inputString !== '') {
    for (let p in pathString) {
      if (!pathString.hasOwnProperty(p)) continue;
      var m = this.regex.exec(pathString[p]);
      if (m) {
        var name = m[1];
        var index = m[2];
        if (!index) {
          index = -1;
        }
        result.push({name: name, index: index});
      } else {
        throw new AssociationReferenceException('Invalid entry in association reference.');
      }
    }
  }
  return result;
};

ObjectManagerBase.prototype.handleStartInstanceEvent = function (parameters) {
  if (parameters.length !== 2) {
    throw new ParameterException('The start instance event needs 2 parameters.');
  }
  const source = parameters[0];
  const traversalList = this.processAssociationReference(parameters[1]);
  let instances = this.getInstances(source, traversalList);
  for (let i in instances) {
    if (!instances.hasOwnProperty(i)) continue;
    instances[i].instance.initialize();
  }
  source.addEvent(new Event("instanceStarted", parameters=[parameters[1]]));
  // ^ deprecated, will be replaced by:
  //     this.controller.addInputLowLevel(
  //       new Event("instanceStarted", null, [parameters[1]]),
  //       [source],
  //       0);
};

ObjectManagerBase.prototype.handleBroadcastEvent = function (parameters) {
  if (parameters.length !== 2) {
    throw new ParameterException('The broadcast event needs 2 parameters (source of event and event name).');
  }
  this.controller.addInputLowLevel(
    paramaters[0],
    this.instances,
    this.controller.simulatedTime) // time offset
  // this.broadcast(parameters[0], parameters[1]);
};

ObjectManagerBase.prototype.handleCreateEvent = function (parameters) {
  if (parameters.length < 2) {
    throw new ParameterException('The create event needs at least 2 parameters.');
  }
  const source = parameters[0];
  const associationName = parameters[1];
  let association = source.associations[associationName];
  if (!association) {
    throw new ParameterException('No such association: ' + associationName);
  }
  if (association.allowedToAdd()) {
    // allow subclasses to be instantiated
    let className = undefined;
    let creationParameters = undefined;
    if (parameters.length === 2) {
      className = association.toClass;
      creationParameters = [];
    } else {
      // 3rd parameter is class name
      className = parameters[2];
      // parameters after 3rd parameter are creation parameters
      creationParameters = parameters.slice(3);
    }
    let newInstance = this.createInstance(className, creationParameters);
    if (newInstance === undefined) {
      throw new ParameterException('Creating instance: no such class: ' + className);
    }
    let index = association.addInstance(newInstance);
    // add parent association to created instance
    // if a parent association is defined in the class diagram
    let parentAssociation = newInstance.associations['parent'];
    if (parentAssociation !== undefined) {
      parentAssociation.addInstance(source);
    }
    // TODO: maybe change order of Event constructor parameters such that we don't have to
    //       explicitly set the port to 'undefined'?
    source.addEvent(new Event('instanceCreated', undefined, [associationName + '[' + index + ']']));
    return [source, associationName + "[" + index.toString() + "]"];
  } else {
    source.addEvent(new Event('instanceCreationError', undefined, [associationName]));
    return [];
  }
};

ObjectManagerBase.prototype.handleCreateAndStartEvent = function (parameters) {
  params = this.handleCreateEvent(parameters);
  if (params) {
    this.handleStartInstanceEvent(params);
  }
}

ObjectManagerBase.prototype.handleDeleteInstanceEvent = function (parameters) {
  if (parameters.length !== 2) {
    throw new ParameterException('The delete instance event needs 2 parameters.');
  }
  const source = parameters[0];
  const traversalList = this.processAssociationReference(parameters[1]);
  let instances = this.getInstances(source, traversalList);
  for (let i in instances) {
    if (!instances.hasOwnProperty(i)) continue;
    instances[i].instance.stop();
    instances[i].instance.userDefinedDestructor();
    // delete association from source instance
    let associationToRemove = instances[i].ref.associations[instances[i].assocName];
    if (instances[i].assocIndex === -1) {
      // empty instances object
      associationToRemove.instances = {};
    } else {
      // remove property from instances object
      delete associationToRemove.instances[instances[i].assocIndex];
    }
    // also remove instance from Om's list of instances
    let index = this.instances.indexOf(instances[i].instance);
    this.instances.splice(index, 1);
  }
  source.addEvent(new Event('instanceDeleted', undefined, [parameters[1]]));
};

ObjectManagerBase.prototype.handleAssociateEvent = function (parameters) {
  if (parameters.length !== 3) {
    throw new ParameterException('The associateInstance event needs 3 parameters.');
  }
  const source = parameters[0];
  const sourceList = parameters[1];
  let traversalList = this.processAssociationReference(sourceList);
  let toCopyList = this.getInstances(source, traversalList);
  if (toCopyList.length !== 1) {
    throw new AssociationReferenceException('Invalid source association reference.');
  }
  let wrappedToCopyInstance = toCopyList[0].instance;
  let destList = this.processAssociationReference(parameters[2]);
  if (destList.length === 0) {
    throw new AssociationReferenceException('Invalid destination association reference.');
  }
  let last = destList.pop();
  if (last.index !== -1) {
    throw new AssociationReferenceException('Last association name in association reference could not be accompanied by an index.');
  }
  let instances = this.getInstances(source, destList);
  for (let i in instances) {
    if (!instances.hasOwnProperty(i)) continue;
    instances[i].instance.associations[last.name].addInstance(wrappedToCopyInstance);
  }
};

ObjectManagerBase.prototype.handleDisassociateEvent = function(parameters) {
  if (parameters.length !== 2) {
    throw new ParameterException('The disassociate event needs 2 parameters.');
  }
  var source = parameters[0];
  var traversal_list = this.processAssociationReference(parameters[1]);
  var instances = this.getInstances(source, traversal_list);
  for (var i in instances) {
    if (!instances.hasOwnProperty(i)) continue;
    // delete association from source instance
    var association_to_remove = instances[i].ref.associations[instances[i].assocName];
    if (instances[i].assocIndex === -1) {
      // empty instances object
      association_to_remove.instances = {};
    } else {
      // remove property from instances object
      delete association_to_remove.instances[instances[i].assocIndex];
    }
  }
  source.addEvent(new Event("instance_disassociated", undefined, [parameters[1]]));
};

ObjectManagerBase.prototype.handleNarrowCastEvent = function (parameters) {
  if (parameters.length !== 3) {
    throw new ParameterException('The narrowCast event needs 3 parameters.');
  }
  let source = parameters[0];
  let traversalList = this.processAssociationReference(parameters[1]);
  let castEvent = parameters[2];
  let instances = this.getInstances(source, traversalList);
  for (let i in instances) {
    if (!instances.hasOwnProperty(i)) continue;
    instances[i].instance.addEvent(castEvent);
  }
};

ObjectManagerBase.prototype.getInstances = function (source, traversalList) {
  let currents = [{
    instance: source,
    ref: null,
    assocName: null,
    assocIndex: null
  }];
  for (let t in traversalList) {
    if (!traversalList.hasOwnProperty(t)) continue;
    let name = traversalList[t].name;
    let index = traversalList[t].index;
    let nexts = [];
    for (var c in currents) {
      if (!currents.hasOwnProperty(c)) continue;
      let association = currents[c].instance.associations[name];
      if (index >= 0) {
        nexts.push({
          instance: association.getInstance(index),
          ref: currents[c].instance,
          assocName: name,
          assocIndex: index
        });
      } else if (index === -1) {
        for (let i in association.instances) {
          if (!association.instances.hasOwnProperty(i)) continue;
          nexts.push({
            instance: association.instances[i],
            ref: currents[c].instance,
            assocName: name,
            assocIndex: index
          });
        }
      } else {
        throw new AssociationReferenceException('Incorrect index in association reference.');
      }
    }
    currents = nexts;
  }
  return currents;
};

ObjectManagerBase.prototype.instantiate = function (toClass, constructParams) {
  // pure virtual
};

ObjectManagerBase.prototype.createInstance = function (toClass, constructParams) {
  let instance = this.instantiate(toClass, constructParams);
  this.instances.push(instance);
  return instance;
};

// Event
function Event(name, port, parameters) {
  if (port === undefined) port = '';
  if (parameters === undefined) parameters = [];
  this.name = name;
  this.port = port;
  this.parameters = parameters;
}

function OutputListener(portNames) {
  this.portNames = portNames;
  this.queue = [];
}

OutputListener.prototype.add = function (theEvent) {
  if (this.portNames.length === 0 || this.portNames.indexOf(theEvent.port) !== -1) {
    this.queue.push(theEvent);
  }
};

OutputListener.prototype.fetch = function (timeout) {
  if (this.queue.length > 0) {
    return this.queue.shift();
  }
};

function ControllerBase(objectManager) {
  this.objectManager = objectManager;

  this.privatePortCounter = 0;

  // keep track of input ports
  this.inputPorts = {};
  this.inputQueue = new EventQueue();

  // keep track of output ports
  this.outputPorts = [];
  this.outputListeners = [];

  this.simulatedTime = 0;
  this.startTime = 0;
}

ControllerBase.prototype.getSimulatedTime = function () {
  return this.simulatedTime
}

ControllerBase.prototype.addInputPort = function (virtualName, instance) {
  let portName;
  if (instance === undefined) {
    portName = virtualName;
  } else {
    portName = 'private' + (this.privatePortCounter++) + virtualName;
  }
  this.inputPorts[portName] = {
    virtualName: virtualName,
    instance: instance
  };
  return portName;
};

ControllerBase.prototype.addOutputPort = function (portName) {
  this.outputPorts.push(portName);
};

// ControllerBase.prototype.broadcast = function (newEvent, timeOffset) {
//   if (timeOffset === undefined) timeOffset = 0
//   this.objectManager.broadcast(undefined, newEvent, timeOffset);
// };

ControllerBase.prototype.start = function () {
  this.startTime = time();
  this.objectManager.initializeAll();
};

ControllerBase.prototype.stop = function () {
};

ControllerBase.prototype.wakeup = function () {
  // will be overridden
}

ControllerBase.prototype.portToInstanceList = function(port) {
  if (port.instance !== undefined)
    return [port.instance];
  else
    return this.objectManager.instances;
}

ControllerBase.prototype.wallclockToSimtime = function() {
  return time() - this.startTime;
}

ControllerBase.prototype.addInput = function (eventName, portName, parameters, timestamp = 0) {
  // console.log("addInput", eventName, portName, parameters);
  if (eventName === '') {
    throw new InputException("Input event can't have an empty name.");
  }
  const port = this.inputPorts[portName];
  if (port === undefined) {
    throw new InputException('Input port mismatch, no such port: ' + portName + '.');
  }
  const virtualName = port.virtualName;
  const e = new Event(eventName, port.virtualName, parameters);
  const targetInstances = this.portToInstanceList(port);
  this.inputQueue.add(new EventQueueEntry(
    timestamp,
    e,
    targetInstances));
  this.wakeup();
};

// Lower-level: port attribute of event ignored, instead targetInstance is given
ControllerBase.prototype.addInputLowLevel = function (event, targetInstances, timestamp) {
  const result =  this.inputQueue.add(new EventQueueEntry(
    timestamp,
    event,
    targetInstances));
  this.wakeup();
  return result;
}

// Blocking call. Handle input events <= until (in simtime) as fast as possible.
ControllerBase.prototype.runUntil = function (until) {
  while (this.inputQueue.getEarliestTime() <= until) {
    const entry = this.inputQueue.pop();
    this.simulatedTime = entry.scheduledTime;
    // console.log("simTime =", this.simulatedTime);
    for (const i of entry.instances) {
      i.performBigStep([entry.theEvent]);
      i.processBigStepOutput();
    }
  }
};

ControllerBase.prototype.outputEvent = function (theEvent) {
  for (const l of this.outputListeners) {
    l.add(theEvent);
  }
};

ControllerBase.prototype.addOutputListener = function (ports) {
  let listener = new OutputListener(ports);
  this.outputListeners.push(listener);
  return listener;
};

ControllerBase.prototype.addMyOwnOutputListener = function (listener) {
  this.outputListeners.push(listener);
};

// function GameLoopControllerBase(objectManager) {
//   ControllerBase.call(this, objectManager);
// }

// GameLoopControllerBase.prototype = new ControllerBase();

// GameLoopControllerBase.prototype.update = function () {
//   const until = time();
//   while (this.inputQueue.getEarliestTime() <= until) {
//     this.handleDue();
//   }
// };

// EventLoop

// parameters:
//  schedule - a callback scheduling another callback in the event loop
//      this callback should take 2 parameters: (callback, timeout) and return an ID
//  clear - a callback that clears a scheduled callback
//      this callback should take an ID that was returned by 'schedule'
function EventLoop(schedule, clear) {
  this.scheduleCallback = schedule;
  this.clearCallback = clear;
  this.scheduledId = null;
  this.lastPrint = 0.0;
}

EventLoop.prototype.schedule = function (f, waitTime) {
  if (this.scheduledId) {
    // if the following error occurs, it is probably due to a flaw in the logic of EventLoopControllerBase
    throw new RuntimeException('EventLoop class intended to maintain at most 1 scheduled callback.');
  }

  if (waitTime !== Infinity) {
    this.scheduledId = this.scheduleCallback(f, waitTime)
  }
};

EventLoop.prototype.clear = function () {
  if (this.scheduledId) {
    this.clearCallback(this.scheduledId);
    this.scheduledId = null;
  }
};

function EventLoopControllerBase(objectManager, eventLoop, finishedCallback) {
  ControllerBase.call(this, objectManager);
  this.eventLoop = eventLoop;
  this.finishedCallback = finishedCallback;
  // this.behindScheduleCallback = behindScheduleCallback;
  // this.lastPrintTime = 0;
}

EventLoopControllerBase.prototype = new ControllerBase();

// EventLoopControllerBase.prototype.addInput = function (eventName, portName, parameters, timeOffset = 0) {
//   ControllerBase.prototype.addInput.call(this, eventName, portName, parameters, timeOffset);
// };

EventLoopControllerBase.prototype.start = function () {
  ControllerBase.prototype.start.call(this);
  this.wakeup();
};

// EventLoopControllerBase.prototype.stop = function () {
//   this.eventLoop.clear();
//   ControllerBase.prototype.stop.call(this);
// };

EventLoopControllerBase.prototype.wakeup = function () {
  // (re-)schedule next wakeup
  this.eventLoop.clear();
  const simTimeNow = time() - this.startTime;
  const realTimeEarliest = this.inputQueue.getEarliestTime() + this.startTime;
  const realWaitTime = realTimeEarliest - time();
  // We do not directly call 'runUntil'. It only schedules the next call (which may be with timeout zero, i.e. ASAP). Therefore, it is 100% safe to call 'wakeup' from anywhere, even from action code (most likely indirectly by a call to 'addInput').
  this.eventLoop.schedule(() => {
    this.runUntil(simTimeNow + this.purposefullyBehind);
    this.wakeup();
  }, realWaitTime);

  if (realWaitTime < 0) {
    this.purposefullyBehind = realWaitTime;
  } else {
    this.purposefullyBehind = 0;
  }
};

// JsEventLoop
function JsEventLoop() {
  EventLoop.call(this, window.setTimeout.bind(window), window.clearTimeout.bind(window));
}

JsEventLoop.prototype = new EventLoop();

// Enum-like construct holding statechart semantic options.
StatechartSemantics = {
  // Big Step Maximality
  TakeOne: 0,
  TakeMany: 1,
  // Concurrency
  Single: 0,
  Many: 1, // not yet implemented
  // Preemption (unsupported)
  NonPreemptive: 0,
  Preemptive: 1,
  // Internal Event Lifeline
  Queue: 0,
  NextSmallStep: 1,
  NextComboStep: 2,
  // Input Event Lifeline
  Whole: 0,
  FirstSmallStep: 1,
  FirstComboStep: 2,
  // Priority
  SourceParent: 0,
  SourceChild: 1
};

let DefaultStatechartSemantics = function () {
  this.bigStepMaximality = this.TakeMany;
  this.concurrency = this.Single;
  this.internalEventLifeline = this.Queue;
  this.inputEventLifeline = this.FirstComboStep;
  this.priority = this.SourceParent;
};

function State(stateId, name, obj) {
  this.stateId = stateId;
  this.name = name;
  this.obj = obj;

  this.ancestors = [];
  this.descendants = [];
  this.children = [];
  this.myParent = null;
  this.enter = null;
  this.exit = null;
  this.defaultState = null;
  this.transitions = [];
  this.myHistory = [];
  this.hasEventlessTransitions = false;
}

State.prototype.getEffectiveTargetStates = function () {
  let targets = [this];
  if (this.defaultState != null) {
    Array.prototype.push.apply(targets, this.defaultState.getEffectiveTargetStates());
  }
  return targets;
}

State.prototype.fixTree = function () {
  for (let c of this.children) {
    if (c instanceof HistoryState) {
      this.myHistory.push(c);
    }
    c.myParent = this;
    c.ancestors.push(this);
    Array.prototype.push.apply(c.ancestors, this.ancestors);
    c.fixTree();
  }
  Array.prototype.push.apply(this.descendants, this.children)
  for (let c of this.children) {
    Array.prototype.push.apply(this.descendants, c.descendants)
  }
}

State.prototype.addChild = function (child) {
  this.children.push(child);
}

State.prototype.addTransition = function (transition) {
  this.transitions.push(transition);
}

State.prototype.setEnter = function (enter) {
  this.enter = enter.bind(this.obj);
}

State.prototype.setExit = function (exit) {
  this.exit = exit.bind(this.obj);
}

function HistoryState(stateId, name, obj) {
  State.call(this, stateId, name, obj);
}

HistoryState.prototype = new State();

function ShallowHistoryState(stateId, name, obj) {
  HistoryState.call(this, stateId, name, obj);
}

ShallowHistoryState.prototype = new HistoryState();

ShallowHistoryState.prototype.getEffectiveTargetStates = function () {
  if (this.stateId in this.obj.historyValues) {
    let targets = [];
    for (let hv of this.obj.historyValues[this.stateId]) {
      Array.prototype.push.apply(targets, hv.getEffectiveTargetStates());
    }
    return targets;
  } else {
    // TODO: is it correct that in this case, the parent itself is also entered?
    return this.myParent.getEffectiveTargetStates();
  }
}

function DeepHistoryState(stateId, name, obj) {
  HistoryState.call(this, stateId, name, obj);
}

DeepHistoryState.prototype = new HistoryState();

DeepHistoryState.prototype.getEffectiveTargetStates = function () {
  if (this.stateId in this.obj.historyValues) {
    return this.obj.historyValues[this.stateId];
  } else {
    // TODO: is it correct that in this case, the parent itself is also entered?
    return this.myParent.getEffectiveTargetStates();
  }
}

function ParallelState(stateId, name, obj) {
  State.call(this, stateId, name, obj);
}

ParallelState.prototype = new State();

ParallelState.prototype.getEffectiveTargetStates = function () {
  let targets = [this];
  for (let c of this.children) {
    if (!(c instanceof HistoryState)) {
      Array.prototype.push.apply(targets, c.getEffectiveTargetStates());
    }
  }
  return targets;
}

function Transition(obj, source, targets) {
  this.guard = null;
  this.action = null;
  this.trigger = null;
  this.source = source;
  this.targets = targets;
  this.obj = obj;
  this.enabledEvent = null; // the event that enabled this transition
  this.optimize();
}

Transition.prototype.isEnabled = function (events) {
  if (this.trigger === null) {
    this.enabledEvent = null;
    return (this.guard === null || this.guard([]));
  } else {
    for (let i in events) {
      theEvent = events[i];
      if ((this.trigger.name === theEvent.name && (!this.trigger.port || this.trigger.port === theEvent.port)) && (this.guard === null || this.guard(theEvent.parameters))) {
        this.enabledEvent = theEvent;
        return true;
      }
    }
  }
}

Transition.prototype.fire = function () {
  // console.log("TRANSITION ", this.source.name, "->", this.targets[0].name);
  let targets = this.privateGetEffectiveTargetStates();
  let exitSet = this.privateExitSet(targets);
  for (let s of exitSet) {
    for (let h of s.myHistory) {
      let f = function (s0) {
        return s0.ancestors.length > 0 && s0.myParent === s;
      }
      if (h instanceof DeepHistoryState) {
        f = function (s0) {
          return s0.descendants.length === 0 && s.descendants.indexOf(s0) >= 0;
        }
      }
      this.obj.historyValues[h.stateId] = this.obj.configuration.filter(f);
    }
  }
  for (let s of exitSet) {
    // console.log("EXIT", s.name);
    if (s.exit != null) {
      s.exit();
    }
  }
  this.obj.configuration = this.obj.configuration.filter(function (el) {
    return exitSet.indexOf(el) < 0;
  })

  // combo state changed area
  this.obj.comboStep.changed.push(this.lca);
  for (let d of this.lca.descendants) {
    this.obj.comboStep.changed.push(d);
  }

  // execute transition action(s)
  if (this.action != null) {
    let params = [];
    if (this.enabledEvent != null) params = this.enabledEvent.parameters;
    this.action(params);
  }

  // enter states...
  for (let s of this.privateEnterSet(targets)) {
    this.obj.configuration.push(s);
    // console.log("ENTER", s.name);
    if (s.enter != null) {
      s.enter();
    }
  }

  this.obj.configuration = this.obj.configuration.sort(function (a, b) {
    return a.stateId - b.stateId
  })
  this.enabledEvent = null;
}

Transition.prototype.privateGetEffectiveTargetStates = function () {
  let targets = []
  for (let target of this.targets) {
    for (let effectiveTarget of target.getEffectiveTargetStates()) {
      if (targets.indexOf(effectiveTarget) < 0) {
        targets.push(effectiveTarget);
      }
    }
  }
  return targets;
}

Transition.prototype.privateExitSet = function (targets) {
  let exitSet = this.lca.descendants.slice(0).filter(function (obj) {
    return function (s) {
      return obj.obj.configuration.indexOf(s) >= 0;
    }
  }(this));
  exitSet.reverse();
  return exitSet;
}

Transition.prototype.privateEnterSet = function* (targets) {
  let target = targets[0];
  let reversedAncestors = target.ancestors.slice(0).reverse();
  for (let a of reversedAncestors) {
    if (this.source.ancestors.indexOf(a) >= 0) {

    } else {
      yield a;
    }
  }
  for (let target of targets) {
    yield target;
  }
}

Transition.prototype.setGuard = function (guard) {
  this.guard = guard.bind(this.obj);
}

Transition.prototype.setAction = function (action) {
  this.action = action.bind(this.obj);
}

Transition.prototype.setTrigger = function (trigger) {
  this.trigger = trigger;
  if (this.trigger === null) {
    this.source.hasEventlessTransitions = true;
  }
}

Transition.prototype.optimize = function () {
  let target = this.targets[0];
    this.lca = this.source.myParent;
    if (this.source.myParent !== target.myParent) { // external
      for (let a of this.source.ancestors) {
        if (target.ancestors.indexOf(a) >= 0) {
          this.lca = a;
          break;
        }
      }
    }
}

// RuntimeClassBase
function RuntimeClassBase(controller) {
  // this.active = false;
  // this.privateSetStable(true);
  // this.events = new EventQueue();

  this.controller = controller;
  this.inports = {};
  this.timers = {};
  this.states = {};

  this.semantics = new DefaultStatechartSemantics();

  this.configuration = [];

  this.currentState = {};
  this.historyValues = {};
  this.timers = {};
  // this.timersToAdd = {};

  this.bigStep = new BigStepState();
  this.comboStep = new ComboStepState();
  this.smallStep = new SmallStepState();
}

RuntimeClassBase.prototype.eventlessTransitions = function () {
  for (let s of this.configuration) {
    if (s.hasEventlessTransitions) return true;
  }
  return false;
}

RuntimeClassBase.prototype.initialize = function () {

  // this.active = true;
  // this.privateSetStable(false);

  this.initializeStatechart();
  this.processBigStepOutput();
};

RuntimeClassBase.prototype.sccdYield = function () {
  return Math.max(0, (time() - this.controller.simulatedTime) / 1000.0);
}

RuntimeClassBase.prototype.getSimulatedTime = function () {
  return this.controller.simulatedTime;
}

RuntimeClassBase.prototype.updateConfiguration = function (states) {
  this.configuration = states.slice(0);
};

// RuntimeClassBase.prototype.stop = function () {
//   this.active = false;
//   this.privateSetStable(false);
// };

RuntimeClassBase.prototype.addTimer = function (index, timeout) {
  this.timers[index] = this.controller.addInputLowLevel(
    new Event('event'+index+'After'),
    [this], // targets
    this.controller.simulatedTime + timeout*1000); // timestamp
  // this.timersToAdd[index] = new EventQueueEntry(this.controller.simulatedTime + Math.trunc(timeout * 1000), new Event('event' + index + 'After'));
};

RuntimeClassBase.prototype.removeTimer = function (index) {
  this.controller.inputQueue.remove(this.timers[index]);
  delete this.timers[index];
  // if (index in this.timersToAdd) delete this.timersToAdd[index];
  // this.controller.inputQueue.remove(this.timers[index]);
  // // this.events.remove(this.timers[index]);
  // delete this.timers[index];
};

RuntimeClassBase.prototype.addEvent = function (event, timeOffset = 0) {
  // console.log("DEPRECATED: RuntimeClassBase.addEvent");
  const _add = e => {
    this.controller.addInputLowLevel(e, [this], this.controller.simulatedTime + timeOffset);
  }
  if (event instanceof Array) {
    for (const e of event)
      _add(e);
  } else {
    _add(event);
  }
};

RuntimeClassBase.prototype.processBigStepOutput = function () {
  for (const e of this.bigStep.outputEventsPort) {
    this.controller.outputEvent(e);
  }
  let om = this.bigStep.outputEventsPm;
  for (const e of this.bigStep.outputEventsPm) {
    this.controller.addInputLowLevel(e, [this.controller.objectManager], this.controller.simulatedTime);
  }
};

// RuntimeClassBase.prototype.step = function () {
//   let stable = false;
//   while (!stable) {
//     let due = [];
//     if (this.events.getEarliestTime() <= this.controller.simulatedTime) {
//       due = [this.events.pop()];
//     }
//     stable = !this.performBigStep(due);
//     this.processBigStepOutput();
//   }
//   for (let key in this.timersToAdd) {
//     if (!this.timersToAdd.hasOwnProperty(key)) continue;
//     this.timers[key] = this.events.add(this.timersToAdd[key]);
//   }
//   this.timersToAdd = {};
//   this.privateSetStable(true);
// };

RuntimeClassBase.prototype.inState = function (stateStrings) {
  let stateIds = [];
  for (let stateString of stateStrings) {
    stateIds.push(this.states[stateString].stateId);
  }
  for (let stateId of stateIds) {
    let found = false;
    for (let s of this.configuration) {
      if (s.stateId === stateId) {
        found = true;
        break;
      }
    }
    if (!found) return false;
  }
  return true;
};

RuntimeClassBase.prototype.performBigStep = function (inputEvents) {
  this.bigStep.next(inputEvents);
  this.smallStep.reset();
  this.comboStep.reset();
  while (this.performComboStep()) {
    this.bigStep.hasStepped = true;
    if (this.semantics.bigStepMaximality === StatechartSemantics.TakeOne)
      break;
  }
  return this.bigStep.hasStepped;
};

RuntimeClassBase.prototype.performComboStep = function () {
  this.comboStep.next();
  while (this.performSmallStep()) {
    this.comboStep.hasStepped = true;
  }
  return this.comboStep.hasStepped;
};

// generate transition candidates for current small step
RuntimeClassBase.prototype.generateCandidates = function () {
  let enabledEvents = this.getEnabledEvents();
  let enabledTransitions = [];
  for (let s of this.configuration) {
    if (this.comboStep.changed.indexOf(s) < 0) {
      for (let t of s.transitions) {
        if (t.isEnabled(enabledEvents)) {
          enabledTransitions.push(t);
        }
      }
    }
  }
  return enabledTransitions;
};

RuntimeClassBase.prototype.performSmallStep = function () {
  let privateYoungerThan =
    function (x, y) {
      if (y.source.ancestors.indexOf(x.source) >= 0) {
        return 1;
      } else if (x.source.ancestors.indexOf(y.source) >= 0) {
        return -1;
      } else {
        throw new Exception('These items have no relation with each other.');
      }
    };

  if (this.smallStep.hasStepped) {
    this.smallStep.next();
  }
  let candidates = this.generateCandidates();
  if (candidates.length > 0) {
    toSkip = new Set();
    conflicting = [];
    for (var i in candidates) {
      c1 = candidates[i];
      if (!toSkip.has(c1)) {
        conflict = [c1];
        for (let j in candidates.slice(candidates.indexOf(c1))) {
          c2 = candidates[j];
          if (c1.source.ancestors.indexOf(c2.source) >= 0 || c2.source.ancestors.indexOf(c1.source) >= 0) {
            conflict.push(c2);
            toSkip.add(c2);
          }
        }
        conflicting.push(conflict.sort(privateYoungerThan));
      }
    }
    if (this.semantics.concurrency === StatechartSemantics.Single) {
      let candidate = conflicting[0];
      if (this.semantics.priority === StatechartSemantics.SourceParent) {
        candidate[candidate.length - 1].fire();
      } else {
        candidate[0].fire();
      }
    } else if (this.semantics.concurrency === StatechartSemantics.Many) {
      // TODO: Implement
    }
    this.smallStep.hasStepped = true;
  }
  return this.smallStep.hasStepped;
};

RuntimeClassBase.prototype.getEnabledEvents = function () {
  let result = this.smallStep.currentEvents.concat(this.comboStep.currentEvents);
  if (this.semantics.inputEventLifeline === StatechartSemantics.Whole ||
    (!this.bigStep.hasStepped &&
      (this.semantics.inputEventLifeline === StatechartSemantics.FirstComboStep ||
        (!this.comboStep.hasStepped &&
          this.semantics.inputEventLifeline === StatechartSemantics.FirstSmallStep)))) {
    result = result.concat(this.bigStep.inputEvents);
  }
  return result;
};

RuntimeClassBase.prototype.raiseInternalEvent = function (theEvent) {
  if (this.semantics.internalEventLifeline === StatechartSemantics.NextSmallStep) {
    this.smallStep.addNextEvent(theEvent);
  } else if (this.semantics.internalEventLifeline === StatechartSemantics.NextComboStep) {
    this.comboStep.addNextEvent(theEvent);
  } else if (this.semantics.internalEventLifeline === StatechartSemantics.Queue) {
    this.addEvent(theEvent, 0);
  }
};

RuntimeClassBase.prototype.initializeStatechart = function () {
  this.updateConfiguration(this.defaultTargets);
  for (let state of this.defaultTargets) {
    if (state.enter != null) {
      state.enter();
    }
  }
};

RuntimeClassBase.prototype.getInPortName = function (name) {
  // const portName = this.inports[name];
  // if ( portName != null ) {
  //   return portName;
  // }
  return name;
};

RuntimeClassBase.prototype.getOutPortName = function (name) {
  // const portName = this.outports[name];
  // if ( portName != null ) {
  //   return portName;
  // }
  return name;
};

let BigStepState = function () {
  this.inputEvents = [];
  this.outputEventsPort = [];
  this.outputEventsPm = [];
  this.hasStepped = true;
  this.transitions = 0;
};

BigStepState.prototype.next = function (inputEvents) {
  this.inputEvents = inputEvents;
  this.outputEventsPort = [];
  this.outputEventsPm = [];
  this.hasStepped = false;
  this.transitions = 0;
};

BigStepState.prototype.outputEvent = function (event) {
  this.outputEventsPort.push(event);
};

BigStepState.prototype.outputEventOm = function (event) {
  this.outputEventsPm.push(event);
};

let ComboStepState = function () {
  this.currentEvents = [];
  this.nextEvents = [];
  this.changed = [];
  this.hasStepped = true;
};

ComboStepState.prototype.reset = function () {
  this.currentEvents = [];
  this.nextEvents = [];
};

ComboStepState.prototype.next = function () {
  this.currentEvents = this.nextEvents;
  this.nextEvents = [];
  this.changed = [];
  this.hasStepped = false;
};

ComboStepState.prototype.addNextEvent = function (event) {
  this.nextEvents.push(event);
};

ComboStepState.prototype.setArenaChanged = function (arena) {
  this.changed.push(arena);
};

ComboStepState.prototype.isArenaChanged = function (arena) {
  return (this.changed.indexOf(arena) !== -1);
};

ComboStepState.prototype.isStable = function () {
  return (this.changed.length === 0);
};

let SmallStepState = function () {
  this.currentEvents = [];
  this.nextEvents = [];
  this.candidates = [];
  this.hasStepped = true;
};

SmallStepState.prototype.reset = function () {
  this.currentEvents = [];
  this.nextEvents = [];
};

SmallStepState.prototype.next = function () {
  this.currentEvents = this.nextEvents;
  this.nextEvents = [];
  this.candidates = [];
  this.hasStepped = false;
};

SmallStepState.prototype.addNextEvent = function (event) {
  this.nextEvents.push(event);
};

SmallStepState.prototype.addCandidate = function (t, p) {
  this.candidates.push({transition: t, parameters: p});
};

SmallStepState.prototype.hasCandidates = function () {
  return (this.candidates.length > 0);
};


if (typeof module.exports === 'object') {
  // We're on NodeJS
  module.exports = {
    // imported by compiled code
    RuntimeClassBase,
    StatechartSemantics,
    ObjectManagerBase,
    EventLoopControllerBase,
    State,
    ParallelState,
    Transition,
    Event,
    Association,

    // imported by user of compiled code
    JsEventLoop,
  };
}

},{}]},{},[5]);
