"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.EditorPluginInjectionAPI = void 0;
Object.defineProperty(exports, "PluginsData", {
  enumerable: true,
  get: function get() {
    return _PluginsData.PluginsData;
  }
});
exports.SharedStateAPI = void 0;
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
var _isEqual = _interopRequireDefault(require("lodash/isEqual"));
var _throttle = _interopRequireDefault(require("lodash/throttle"));
var _corePlugin = require("./core-plugin");
var _PluginsData = require("./PluginsData");
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any

function hasGetSharedState(
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
plugin
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
) {
  // Ignored via go/ees005
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  return typeof plugin.getSharedState === 'function';
}
function hasActions(
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
plugin
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
) {
  // Ignored via go/ees005
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  return (0, _typeof2.default)(plugin.actions) === 'object';
}
function hasCommands(
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
plugin
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
) {
  // Ignored via go/ees005
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  return (0, _typeof2.default)(plugin.commands) === 'object';
}
var filterPluginsWithListeners = function filterPluginsWithListeners(_ref) {
  var listeners = _ref.listeners,
    plugins = _ref.plugins;
  return Array.from(listeners.keys()).map(function (pluginName) {
    return plugins.get(pluginName);
  }).filter(function (plugin) {
    return plugin !== undefined && hasGetSharedState(plugin);
  });
};
var extractSharedStateFromPlugins = function extractSharedStateFromPlugins(_ref2) {
  var oldEditorState = _ref2.oldEditorState,
    newEditorState = _ref2.newEditorState,
    plugins = _ref2.plugins;
  var isInitialization = !oldEditorState && newEditorState;
  var result = new Map();
  var _iterator = _createForOfIteratorHelper(plugins),
    _step;
  try {
    for (_iterator.s(); !(_step = _iterator.n()).done;) {
      var plugin = _step.value;
      if (!plugin || !hasGetSharedState(plugin)) {
        continue;
      }
      var nextSharedState = plugin.getSharedState(newEditorState);
      var prevSharedState = !isInitialization && oldEditorState ? plugin.getSharedState(oldEditorState) : undefined;
      var isSamePluginState = (0, _isEqual.default)(prevSharedState, nextSharedState);
      if (isInitialization || !isSamePluginState) {
        result.set(plugin.name, {
          nextSharedState: nextSharedState,
          prevSharedState: prevSharedState
        });
      }
    }
  } catch (err) {
    _iterator.e(err);
  } finally {
    _iterator.f();
  }
  return result;
};
var THROTTLE_CALLS_FOR_MILLISECONDS = 0;
var notifyListenersThrottled = (0, _throttle.default)(function (_ref3) {
  var listeners = _ref3.listeners,
    updatesToNotifyQueue = _ref3.updatesToNotifyQueue;
  var callbacks = [];
  var _iterator2 = _createForOfIteratorHelper(updatesToNotifyQueue.entries()),
    _step2;
  try {
    var _loop = function _loop() {
      var _step2$value = (0, _slicedToArray2.default)(_step2.value, 2),
        pluginName = _step2$value[0],
        diffs = _step2$value[1];
      var pluginListeners = listeners.get(pluginName) || [];
      pluginListeners.forEach(function (callback) {
        diffs.forEach(function (diff) {
          callbacks.push(callback.bind(callback, diff));
        });
      });
    };
    for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
      _loop();
    }
  } catch (err) {
    _iterator2.e(err);
  } finally {
    _iterator2.f();
  }
  updatesToNotifyQueue.clear();
  if (callbacks.length === 0) {
    return;
  }
  callbacks.reverse().forEach(function (cb) {
    cb();
  });
}, THROTTLE_CALLS_FOR_MILLISECONDS);
var ActionsAPI = /*#__PURE__*/function () {
  function ActionsAPI() {
    (0, _classCallCheck2.default)(this, ActionsAPI);
  }
  return (0, _createClass2.default)(ActionsAPI, [{
    key: "createAPI",
    value: function createAPI(
    // Ignored via go/ees005
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    plugin
    // Ignored via go/ees005
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    ) {
      if (!plugin || !hasActions(plugin)) {
        return {};
      }
      return new Proxy(plugin.actions || {}, {
        get: function get(target, prop, _receiver) {
          // We will be able to track perfomance here
          return Reflect.get(target, prop);
        }
      });
    }
  }]);
}();
var EditorCommandsAPI = /*#__PURE__*/function () {
  function EditorCommandsAPI() {
    (0, _classCallCheck2.default)(this, EditorCommandsAPI);
  }
  return (0, _createClass2.default)(EditorCommandsAPI, [{
    key: "createAPI",
    value: function createAPI(
    // Ignored via go/ees005
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    plugin
    // Ignored via go/ees005
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    ) {
      if (!plugin || !hasCommands(plugin)) {
        return {};
      }
      return new Proxy(plugin.commands || {}, {
        get: function get(target, prop, _receiver) {
          // We will be able to track perfomance here
          return Reflect.get(target, prop);
        }
      });
    }
  }]);
}();
var SharedStateAPI = exports.SharedStateAPI = /*#__PURE__*/function () {
  function SharedStateAPI(_ref4) {
    var getEditorState = _ref4.getEditorState;
    (0, _classCallCheck2.default)(this, SharedStateAPI);
    (0, _defineProperty2.default)(this, "updatesToNotifyQueue", new Map());
    this.getEditorState = getEditorState;
    this.listeners = new Map();
  }
  return (0, _createClass2.default)(SharedStateAPI, [{
    key: "createAPI",
    value: function createAPI(plugin
    // Ignored via go/ees005
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    ) {
      var _this = this;
      if (!plugin) {
        return {
          currentState: function currentState() {
            return undefined;
          },
          onChange: function onChange(_sub) {
            return function () {};
          }
        };
      }
      var pluginName = plugin.name;
      return {
        currentState: function currentState() {
          if (!hasGetSharedState(plugin)) {
            return undefined;
          }
          var state = _this.getEditorState();
          return plugin.getSharedState(state);
        },
        onChange: function onChange(sub) {
          var pluginListeners = _this.listeners.get(pluginName) || new Set();
          pluginListeners.add(sub);
          _this.listeners.set(pluginName, pluginListeners);
          return function () {
            return _this.cleanupSubscription(pluginName, sub);
          };
        }
      };
    }
  }, {
    key: "cleanupSubscription",
    value: function cleanupSubscription(pluginName, sub) {
      (this.listeners.get(pluginName) || new Set()).delete(sub);
    }

    // Drop every listener and pending update for a plugin that is no longer
    // registered. Without this, callbacks (and their captured closures) for
    // evicted plugins would linger in `listeners` until destroy(), and every
    // transaction would still walk their keys via filterPluginsWithListeners.
  }, {
    key: "removePluginListeners",
    value: function removePluginListeners(pluginName) {
      this.listeners.delete(pluginName);
      this.updatesToNotifyQueue.delete(pluginName);
    }
  }, {
    key: "notifyListeners",
    value: function notifyListeners(_ref5) {
      var newEditorState = _ref5.newEditorState,
        oldEditorState = _ref5.oldEditorState,
        plugins = _ref5.plugins;
      var listeners = this.listeners,
        updatesToNotifyQueue = this.updatesToNotifyQueue;
      var pluginsFiltered = filterPluginsWithListeners({
        plugins: plugins,
        listeners: listeners
      });
      var sharedStateDiffs = extractSharedStateFromPlugins({
        oldEditorState: oldEditorState,
        newEditorState: newEditorState,
        plugins: pluginsFiltered
      });
      if (sharedStateDiffs.size === 0) {
        return;
      }
      var _iterator3 = _createForOfIteratorHelper(sharedStateDiffs),
        _step3;
      try {
        for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
          var _step3$value = (0, _slicedToArray2.default)(_step3.value, 2),
            pluginName = _step3$value[0],
            nextDiff = _step3$value[1];
          var currentDiffQueue = updatesToNotifyQueue.get(pluginName) || [];
          updatesToNotifyQueue.set(pluginName, [].concat((0, _toConsumableArray2.default)(currentDiffQueue), [nextDiff]));
        }
      } catch (err) {
        _iterator3.e(err);
      } finally {
        _iterator3.f();
      }
      notifyListenersThrottled({
        updatesToNotifyQueue: updatesToNotifyQueue,
        listeners: listeners
      });
    }
  }, {
    key: "destroy",
    value: function destroy() {
      this.listeners.clear();
      this.updatesToNotifyQueue.clear();
    }
  }]);
}();
var editorAPICache = new WeakMap();

// eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
var EditorPluginInjectionAPI = exports.EditorPluginInjectionAPI = /*#__PURE__*/function () {
  function EditorPluginInjectionAPI(_ref6) {
    var _this2 = this;
    var getEditorState = _ref6.getEditorState,
      getEditorView = _ref6.getEditorView,
      fireAnalyticsEvent = _ref6.fireAnalyticsEvent,
      appearance = _ref6.appearance;
    (0, _classCallCheck2.default)(this, EditorPluginInjectionAPI);
    (0, _defineProperty2.default)(this, "onEditorViewUpdated", function (_ref7) {
      var newEditorState = _ref7.newEditorState,
        oldEditorState = _ref7.oldEditorState;
      _this2.sharedStateAPI.notifyListeners({
        newEditorState: newEditorState,
        oldEditorState: oldEditorState,
        plugins: _this2.plugins
      });
    });
    (0, _defineProperty2.default)(this, "onEditorPluginInitialized", function (plugin) {
      _this2.addPlugin(plugin);
    });
    // Internal cleanup helper used by ReactEditorView's reconfigureState to
    // reconcile the registered plugin set with the current preset. Removes
    // every registered plugin not in `keptPluginNames`; `core` is always
    // preserved. Returns the names that were removed. Intentionally not on
    // PluginInjectionAPIDefinition: this is an editor-internal control, not
    // part of the injection-API contract that plugins or external consumers
    // depend on.
    (0, _defineProperty2.default)(this, "retainPlugins", function (keptPluginNames) {
      var evicted = [];
      var _iterator4 = _createForOfIteratorHelper(_this2.plugins.keys()),
        _step4;
      try {
        for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
          var _name = _step4.value;
          if (_name !== 'core' && !keptPluginNames.has(_name)) {
            evicted.push(_name);
          }
        }
      } catch (err) {
        _iterator4.e(err);
      } finally {
        _iterator4.f();
      }
      for (var _i = 0, _evicted = evicted; _i < _evicted.length; _i++) {
        var name = _evicted[_i];
        _this2.plugins.delete(name);
        _this2.sharedStateAPI.removePluginListeners(name);
      }
      return evicted;
    });
    // Internal: snapshot the names of currently-registered plugins. Used by
    // reconfigureState to capture the previous plugin set before the new
    // preset registers its own plugins via onEditorPluginInitialized.
    (0, _defineProperty2.default)(this, "getRegisteredPluginNames", function () {
      return Array.from(_this2.plugins.keys());
    });
    (0, _defineProperty2.default)(this, "addPlugin", function (plugin) {
      // Plugins other than `core` are checked by the preset itself
      // For some reason in some tests we have duplicates that are missed.
      // To follow-up in ED-19611
      if (plugin.name === 'core' && _this2.plugins.has(plugin.name)) {
        throw new Error("Plugin ".concat(plugin.name, " has already been initialised in the Editor API!\n        There cannot be duplicate plugins or you will have unexpected behaviour"));
      }
      _this2.plugins.set(plugin.name, plugin);
    });
    (0, _defineProperty2.default)(this, "getPluginByName", function (pluginName) {
      var plugin = _this2.plugins.get(pluginName);
      return plugin;
    });
    this.sharedStateAPI = new SharedStateAPI({
      getEditorState: getEditorState
    });
    this.plugins = new Map();
    this.actionsAPI = new ActionsAPI();
    this.commandsAPI = new EditorCommandsAPI();

    // Special core plugin that is always added
    this.addPlugin((0, _corePlugin.corePlugin)({
      config: {
        getEditorView: getEditorView,
        fireAnalyticsEvent: fireAnalyticsEvent,
        appearance: appearance
      }
    }));
  }

  /**
   * Returns PM plugins from internally-registered plugins (e.g. the core plugin)
   * that are not processed through the normal preset builder flow.
   */
  // Ignored via go/ees005
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  return (0, _createClass2.default)(EditorPluginInjectionAPI, [{
    key: "getInternalPMPlugins",
    value: function getInternalPMPlugins() {
      // Ignored via go/ees005
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      var result = [];
      var corePlugin = this.plugins.get('core');
      if (corePlugin && typeof corePlugin.pmPlugins === 'function') {
        var pmPlugins = corePlugin.pmPlugins();
        if (pmPlugins) {
          result.push.apply(result, (0, _toConsumableArray2.default)(pmPlugins));
        }
      }
      return result;
    }
  }, {
    key: "createAPI",
    value: function createAPI() {
      var sharedStateAPI = this.sharedStateAPI,
        actionsAPI = this.actionsAPI,
        commandsAPI = this.commandsAPI,
        getPluginByName = this.getPluginByName;

      // Ignored via go/ees005
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      return new Proxy({}, {
        get: function get(target, prop, _receiver) {
          // If we pass this as a prop React hates us
          // Let's just reflect the result and ignore these
          if (prop === 'toJSON') {
            return Reflect.get(target, prop);
          }
          var plugin = getPluginByName(prop);
          if (!plugin) {
            return undefined;
          }
          var sharedState = sharedStateAPI.createAPI(plugin);
          var actions = actionsAPI.createAPI(plugin);
          var commands = commandsAPI.createAPI(plugin);

          // Ignored via go/ees005
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
          var proxyCoreAPI = {
            sharedState: sharedState,
            actions: actions,
            commands: commands
          };
          return proxyCoreAPI;
        }
      });
    }
  }, {
    key: "api",
    value: function api() {
      if (!editorAPICache.get(this)) {
        editorAPICache.set(this, this.createAPI());
      }

      // Ignored via go/ees005
      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
      return editorAPICache.get(this);
    }
  }]);
}(); // eslint-disable-next-line @atlaskit/editor/no-re-export