Liferay.Loader.define("frontend-image-editor-web@4.0.37/ImageEditor.es", ['module', 'exports', 'require', './ImageEditorLoading.es', 'frontend-taglib-clay$clay-dropdown', 'frontend-js-web', 'frontend-js-metal-web$metal', 'frontend-js-metal-web$metal-dom', 'frontend-js-metal-web$metal-soy', './ImageEditor.soy', './ImageEditorHistoryEntry.es'], function (module, exports, require) {
  var define = undefined;
  var global = window;
  {
    Object.defineProperty(exports, "__esModule", {
      value: true
    });
    exports.default = void 0;

    require("./ImageEditorLoading.es");

    require("frontend-taglib-clay$clay-dropdown");

    var _frontendJsWeb = require("frontend-js-web");

    var _metal = require("frontend-js-metal-web$metal");

    var _metalDom = _interopRequireDefault(require("frontend-js-metal-web$metal-dom"));

    var _metalSoy = _interopRequireDefault(require("frontend-js-metal-web$metal-soy"));

    var _ImageEditor = _interopRequireDefault(require("./ImageEditor.soy"));

    var _ImageEditorHistoryEntry = _interopRequireDefault(require("./ImageEditorHistoryEntry.es"));

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

    function _typeof(obj) {
      "@babel/helpers - typeof";
      if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
        _typeof = function _typeof(obj) {
          return typeof obj;
        };
      } else {
        _typeof = function _typeof(obj) {
          return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
        };
      }return _typeof(obj);
    }

    function _classCallCheck(instance, Constructor) {
      if (!(instance instanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function");
      }
    }

    function _defineProperties(target, props) {
      for (var i = 0; i < props.length; i++) {
        var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ("value" in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor);
      }
    }

    function _createClass(Constructor, protoProps, staticProps) {
      if (protoProps) _defineProperties(Constructor.prototype, protoProps);if (staticProps) _defineProperties(Constructor, staticProps);return Constructor;
    }

    function _possibleConstructorReturn(self, call) {
      if (call && (_typeof(call) === "object" || typeof call === "function")) {
        return call;
      }return _assertThisInitialized(self);
    }

    function _assertThisInitialized(self) {
      if (self === void 0) {
        throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
      }return self;
    }

    function _getPrototypeOf(o) {
      _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
        return o.__proto__ || Object.getPrototypeOf(o);
      };return _getPrototypeOf(o);
    }

    function _inherits(subClass, superClass) {
      if (typeof superClass !== "function" && superClass !== null) {
        throw new TypeError("Super expression must either be null or a function");
      }subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } });if (superClass) _setPrototypeOf(subClass, superClass);
    }

    function _setPrototypeOf(o, p) {
      _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
        o.__proto__ = p;return o;
      };return _setPrototypeOf(o, p);
    }

    /**
     * Creates an Image Editor component.
     *
     * <p>
     * This class bootstraps all the necessary parts of an image editor. It only
     * controls the state and history of the editing process, orchestrating how the
     * different parts of the application work.
     * </p>
     *
     * <p>
     * All image processing is delegated to the different image editor capability
     * implementations. The editor provides
     * </p>
     *
     * <ul>
     * <li>
     * A common way of exposing the functionality.
     * </li>
     * <li>
     * Some registration points which can be used by the image editor capability
     * implementors to provide UI controls.
     * </li>
     * </ul>
     */
    var ImageEditor = /*#__PURE__*/function (_PortletBase) {
      _inherits(ImageEditor, _PortletBase);

      function ImageEditor() {
        _classCallCheck(this, ImageEditor);

        return _possibleConstructorReturn(this, _getPrototypeOf(ImageEditor).apply(this, arguments));
      }

      _createClass(ImageEditor, [{
        key: "attached",

        /**
         * @inheritDoc
         */
        value: function attached() {
          var _this = this;

          this.historyIndex_ = 0;
          /**
           * History of the different image states during editing. Every entry
           * represents a change to the image on top of the previous one. History
           * entries are objects with the following attributes:
           *
           * <ul>
           * <li>
           * url (optional): the URL representing the image.
           * </li>
           * <li>
           * data: the image data object of the image.
           * </li>
           * </ul>
           * @protected
           * @type {Array.<Object>}
           */

          this.history_ = [new _ImageEditorHistoryEntry.default({
            url: this.image
          })]; // Polyfill svg usage for lexicon icons

          if (window.svg4everybody) {
            svg4everybody({
              polyfill: true
            });
          } // Load the first entry imageData and render it on the app.


          this.history_[0].getImageData().then(function (imageData) {
            _metal.async.nextTick(function () {
              _this.imageEditorReady = true;

              _this.syncImageData_(imageData);
            });
          });
        }
        /**
         * Accepts the current changes applied by the active control and creates
         * a new entry in the history stack. This wipes out any stale redo states.
         */

      }, {
        key: "accept",
        value: function accept() {
          var _this2 = this;

          var selectedControl = this.components[this.id + '_selected_control_' + this.selectedControl.variant];
          this.history_[this.historyIndex_].getImageData().then(function (imageData) {
            return selectedControl.process(imageData);
          }).then(function (imageData) {
            return _this2.createHistoryEntry_(imageData);
          }).then(function () {
            return _this2.syncHistory_();
          }).then(function () {
            _this2.selectedControl = null;
            _this2.selectedTool = null;
          });
        }
        /**
         * Notifies the opener app that the user wants to close the editor without
         * saving the changes.
         *
         * @protected
         */

      }, {
        key: "close_",
        value: function close_() {
          Liferay.Util.getWindow().hide();
        }
        /**
         * Creates a new history entry state.
         *
         * @param  {ImageData} imageData The image data of the new image.
         * @protected
         */

      }, {
        key: "createHistoryEntry_",
        value: function createHistoryEntry_(imageData) {
          this.historyIndex_++;
          this.history_.length = this.historyIndex_ + 1;
          this.history_[this.historyIndex_] = new _ImageEditorHistoryEntry.default({
            data: imageData
          });
          return Promise.resolve();
        }
        /**
         * Discards the current changes applied by the active control and reverts
         * the image to its state before the control activation.
         */

      }, {
        key: "discard",
        value: function discard() {
          this.selectedControl = null;
          this.selectedTool = null;
          this.syncHistory_();
        }
        /**
         * Retrieves the editor canvas DOM node.
         *
         * @return {Element} The canvas element
         */

      }, {
        key: "getImageEditorCanvas",
        value: function getImageEditorCanvas() {
          return this.element.querySelector('.lfr-image-editor-image-container canvas');
        }
        /**
         * Retrieves the blob representation of the current image.
         *
         * @return {Promise} A promise that resolves with the image blob.
         */

      }, {
        key: "getImageEditorImageBlob",
        value: function getImageEditorImageBlob() {
          var _this3 = this;

          return new Promise(function (resolve) {
            _this3.getImageEditorImageData().then(function (imageData) {
              var canvas = document.createElement('canvas');
              canvas.width = imageData.width;
              canvas.height = imageData.height;
              canvas.getContext('2d').putImageData(imageData, 0, 0);

              if (canvas.toBlob) {
                canvas.toBlob(resolve, _this3.saveMimeType);
              } else {
                var data = atob(canvas.toDataURL(_this3.saveMimeType).split(',')[1]);
                var length = data.length;
                var bytes = new Uint8Array(length);

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

                resolve(new Blob([bytes], {
                  type: _this3.saveMimeType
                }));
              }
            });
          });
        }
        /**
         * Retrieves the image data representation of the current image.
         *
         * @return {Promise} A promise that resolves with the image data.
         */

      }, {
        key: "getImageEditorImageData",
        value: function getImageEditorImageData() {
          return this.history_[this.historyIndex_].getImageData();
        }
        /**
         * Returns a list of all possible image editor capabilities.
         *
         * @return {Array<{Object}>}
         */

      }, {
        key: "getPossibleControls",
        value: function getPossibleControls() {
          return this.imageEditorCapabilities.tools.reduce(function (prev, curr) {
            return prev.concat(curr.controls);
          }, []);
        }
        /**
         * Normalizes different MIME types to the most similar MIME type available
         * to canvas implementations.
         *
         * @param  {String} mimeType The original MIME type.
         * @return {String} The normalized MIME type.
         * @see http://kangax.github.io/jstests/toDataUrl_mime_type_test/
         */

      }, {
        key: "normalizeCanvasMimeType_",
        value: function normalizeCanvasMimeType_(mimeType) {
          mimeType = mimeType.toLowerCase();
          return mimeType.replace('jpg', 'jpeg');
        }
        /**
         * Notifies the opener app of the result of the save action.
         *
         * @param  {Object} result The server response to the save action.
         * @protected
         */

      }, {
        key: "notifySaveResult_",
        value: function notifySaveResult_(result) {
          this.components.loading.show = false;

          if (result && result.success) {
            Liferay.Util.getOpener().Liferay.fire(this.saveEventName, {
              data: result
            });
            Liferay.Util.getWindow().hide();
          } else if (result.error) {
            this.showError_(result.error.message);
          }
        }
        /**
         * Updates the image back to a previously undone state in the history.
         * Redoing an action recovers the undone image changes and enables the
         * undo stack in case the user wants to undo the changes again.
         */

      }, {
        key: "redo",
        value: function redo() {
          this.historyIndex_++;
          this.syncHistory_();
        }
        /**
         * Selects a control and starts its editing phase with filters.
         *
         * @param  {MouseEvent} event The mouse event.
         */

      }, {
        key: "requestImageEditorEditFilters",
        value: function requestImageEditorEditFilters(event) {
          var _this4 = this;

          var controls = this.getPossibleControls();
          var target = event.delegateTarget || event.currentTarget;
          var targetControl = target.getAttribute('data-control');
          var targetTool = target.getAttribute('data-tool');
          this.syncHistory_().then(function () {
            _this4.selectedControl = controls.filter(function (tool) {
              return tool.variant === targetControl;
            })[0];
            _this4.selectedTool = targetTool;
          });
        }
        /**
         * Select a control and starts its editing phase.
         *
         * @param {MouseEvent} event The mouse event.
         */

      }, {
        key: "requestImageEditorEdit",
        value: function requestImageEditorEdit(event) {
          var _this5 = this;

          var controls = this.getPossibleControls();
          var target = event.target.element;
          var targetControl = event.data.item.variant;
          var targetTool = target.getAttribute('data-tool');
          this.syncHistory_().then(function () {
            _this5.selectedControl = controls.filter(function (tool) {
              return tool.variant === targetControl;
            })[0];
            _this5.selectedTool = targetTool;
          });
        }
        /**
         * Queues a request for a preview process of the current image by the
         * currently selected control.
         */

      }, {
        key: "requestImageEditorPreview",
        value: function requestImageEditorPreview() {
          var _this6 = this;

          var selectedControl = this.components[this.id + '_selected_control_' + this.selectedControl.variant];
          this.history_[this.historyIndex_].getImageData().then(function (imageData) {
            return selectedControl.preview(imageData);
          }).then(function (imageData) {
            return _this6.syncImageData_(imageData);
          });
          this.components.loading.show = true;
        }
        /**
         * Discards all changes and restores the original state of the image.
         * Unlike the {@link ImageEditor#undo|undo} and
         * {@link ImageEditor#redo|redo} methods, this method wipes out the entire
         * history.
         */

      }, {
        key: "reset",
        value: function reset() {
          this.historyIndex_ = 0;
          this.history_.length = 1;
          this.syncHistory_();
        }
        /**
         * Tries to save the current image using the provided save URL.
         * @param {MouseEvent} event The mouse event that triggers the save action.
         * @protected
         */

      }, {
        key: "save_",
        value: function save_(event) {
          var _this7 = this;

          if (!event.delegateTarget.disabled) {
            this.getImageEditorImageBlob().then(function (imageBlob) {
              return _this7.submitBlob_(imageBlob);
            }).then(function (result) {
              return _this7.notifySaveResult_(result);
            }).catch(function (error) {
              return _this7.showError_(error);
            });
          }
        }
        /**
         * Setter function for the <code>saveMimeType</code> state key.
         * @param  {!String} saveMimeType The optional value for the attribute.
         * @protected
         * @return {String} The computed value for the attribute.
         */

      }, {
        key: "setterSaveMimeTypeFn_",
        value: function setterSaveMimeTypeFn_(saveMimeType) {
          if (!saveMimeType) {
            var imageExtensionRegex = /https?:\/\/(?:www\.)?(?:.+)\.(\w+)\/[^?/]+/;
            var imageExtension = this.image.match(imageExtensionRegex)[1];
            saveMimeType = "image/".concat(imageExtension);
          }

          return this.normalizeCanvasMimeType_(saveMimeType);
        }
        /**
         * Displays an error message in the editor.
         * @param  {String} message The error message to display.
         * @protected
         */

      }, {
        key: "showError_",
        value: function showError_(_ref) {
          var message = _ref.message;
          this.components.loading.show = false;
          Liferay.Util.openToast({
            container: this.element,
            message: message,
            type: 'danger'
          });
        }
        /**
         * Sends a given image blob to the server for processing and storing.
         * @param  {Blob} imageBlob The image blob to send to the server.
         * @protected
         * @return {Promise} A promise that follows the XHR submission
         * process.
         */

      }, {
        key: "submitBlob_",
        value: function submitBlob_(imageBlob) {
          var _this8 = this;

          var saveFileName = this.saveFileName;
          var saveParamName = this.saveParamName;
          var saveFileEntryId = this.saveFileEntryId;
          var promise = new Promise(function (resolve, reject) {
            var formData = new FormData();
            formData.append(saveParamName, imageBlob, saveFileName);
            formData.append('fileEntryId', saveFileEntryId);

            _this8.fetch(_this8.saveURL, formData).then(function (response) {
              return response.json();
            }).then(resolve).catch(function (error) {
              return reject(error);
            });
          });
          this.components.loading.show = true;
          return promise;
        }
        /**
         * Syncs the image and history values after changes to the history stack.
         * @protected
         */

      }, {
        key: "syncHistory_",
        value: function syncHistory_() {
          var _this9 = this;

          return new Promise(function (resolve) {
            _this9.history_[_this9.historyIndex_].getImageData().then(function (imageData) {
              _this9.syncImageData_(imageData);

              _this9.history = {
                canRedo: _this9.historyIndex_ < _this9.history_.length - 1,
                canReset: _this9.history_.length > 1,
                canUndo: _this9.historyIndex_ > 0
              };
              resolve();
            });
          });
        }
        /**
         * Updates the image data displayed in the editable area.
         * @param  {ImageData} imageData The new image data value to display in the
         * editor.
         * @protected
         */

      }, {
        key: "syncImageData_",
        value: function syncImageData_(imageData) {
          var width = imageData.width;
          var height = imageData.height;
          var aspectRatio = width / height;
          var offscreenCanvas = document.createElement('canvas');
          offscreenCanvas.width = width;
          offscreenCanvas.height = height;
          var offscreenContext = offscreenCanvas.getContext('2d');
          offscreenContext.clearRect(0, 0, width, height);
          offscreenContext.putImageData(imageData, 0, 0);
          var canvas = this.getImageEditorCanvas();

          var boundingBox = _metalDom.default.closest(this.element, '.portlet-layout');

          var availableWidth = boundingBox.offsetWidth;
          var dialogFooterHeight = 0;
          var dialogFooter = this.element.querySelector('.dialog-footer');

          if (dialogFooter) {
            dialogFooterHeight = dialogFooter.offsetHeight;
          }

          var availableHeight = boundingBox.offsetHeight - 142 - 40 - dialogFooterHeight;


          if (availableWidth / availableHeight > 1) {
            canvas.height = availableHeight;
            canvas.width = aspectRatio * availableHeight;
          } else {
            canvas.width = availableWidth;
            canvas.height = availableWidth / aspectRatio;
          }

          var context = canvas.getContext('2d');
          context.clearRect(0, 0, canvas.width, canvas.height);
          context.drawImage(offscreenCanvas, 0, 0, width, height, 0, 0, canvas.width, canvas.height);
          canvas.style.width = canvas.width + 'px';
          canvas.style.height = canvas.height + 'px';
          this.components.loading.show = false;
        }
        /**
         * Reverts the image to the previous state in the history. Undoing an action
         * brings back the previous version of the image and enables the redo stack
         * in case the user wants to reapply the change again.
         */

      }, {
        key: "undo",
        value: function undo() {
          this.historyIndex_--;
          this.syncHistory_();
        }
      }]);

      return ImageEditor;
    }(_frontendJsWeb.PortletBase);
    /**
     * State definition.
     * @static
     * @type {!Object}
     */

    ImageEditor.STATE = {
      /**
       * Whether the editor is ready for user interaction.
       * @type {Object}
       */
      imageEditorReady: {
        validator: _metal.core.isBoolean,
        value: false
      },

      /**
       * Event to dispatch when editing is complete.
       * @type {String}
       */
      saveEventName: {
        validator: _metal.core.isString
      },

      /**
       * ID of the saved image to send to the server for the save action.
       * @type {String}
       */
      saveFileEntryId: {
        validator: _metal.core.isString
      },

      /**
       * Name of the saved image to send to the server for the save action.
       * @type {String}
       */
      saveFileName: {
        validator: _metal.core.isString
      },

      /**
       * MIME type of the saved image. If not explicitly set, the image MIME type
       * is inferred from the image URL.
       * @type {String}
       */
      saveMimeType: {
        setter: 'setterSaveMimeTypeFn_',
        validator: _metal.core.isString
      },

      /**
       * Name of the param that specifies where to send the image to the server
       * for the save action.
       * @type {String}
       */
      saveParamName: {
        validator: _metal.core.isString
      },

      /**
       * URL to save the image changes.
       * @type {String}
       */
      saveURL: {
        validator: _metal.core.isString
      }
    };

    _metalSoy.default.register(ImageEditor, _ImageEditor.default);

    var _default = ImageEditor;
    exports.default = _default;
    //# sourceMappingURL=ImageEditor.es.js.map
  }
});
//# sourceMappingURL=ImageEditor.es.js.map