text: odoo.define('wysiwyg.widgets.LinkDialog', function (require) {
  'use strict';
  
  var core = require('web.core');
  var Dialog = require('wysiwyg.widgets.Dialog');
  
  var dom = $.summernote.core.dom;
  var range = $.summernote.core.range;
  
  var _t = core._t;
  
  /**
   * Allows to customize link content and style.
   */
  var LinkDialog = Dialog.extend({
      template: 'wysiwyg.widgets.link',
      xmlDependencies: (Dialog.prototype.xmlDependencies || []).concat([
          '/web_editor/static/src/xml/wysiwyg.xml'
      ]),
      events: _.extend({}, Dialog.prototype.events || {}, {
          'input': '_onAnyChange',
          'change [name="link_style_color"]': '_onTypeChange',
          'change': '_onAnyChange',
          'input input[name="url"]': '_onURLInput',
      }),
  
      /**
       * @constructor
       * @param {Boolean} linkInfo.isButton - whether if the target is a button element.
       */
      init: function (parent, options, editable, linkInfo) {
          this.options = options || {};
          this._super(parent, _.extend({
              title: _t("Link to"),
          }, this.options));
  
          this.trigger_up('getRecordInfo', {
              recordInfo: this.options,
              callback: recordInfo => {
                  _.defaults(this.options, recordInfo);
              },
          });
  
          this.data = linkInfo || {};
          this.isButton = this.data.isButton;
          // Using explicit type 'link' to preserve style when the target is <button class="...btn-link"/>.
          this.colorsData = [
              {type: this.isButton ? 'link' : '', label: _t("Link"), btnPreview: 'link'},
              {type: 'primary', label: _t("Primary"), btnPreview: 'primary'},
              {type: 'secondary', label: _t("Secondary"), btnPreview: 'secondary'},
              // Note: by compatibility the dialog should be able to remove old
              // colors that were suggested like the BS status colors or the
              // alpha -> epsilon classes. This is currently done by removing
              // all btn-* classes anyway.
          ];
  
          this.editable = editable;
          this.data.className = "";
          this.data.iniClassName = "";
  
          var r = this.data.range;
          this.needLabel = !r || (r.sc === r.ec && r.so === r.eo);
  
          if (this.data.range) {
              const $el = $(this.data.range.sc).filter(this.isButton ? "button" : "a");
              this.data.iniClassName = $el.attr("class") || "";
              this.colorCombinationClass = false;
              let $node = $el;
              while ($node.length && !$node.is('body')) {
                  const className = $node.attr('class') || '';
                  const m = className.match(/\b(o_cc\d+)\b/g);
                  if (m) {
                      this.colorCombinationClass = m[0];
                      break;
                  }
                  $node = $node.parent();
              }
              this.data.className = this.data.iniClassName.replace(/(^|\s+)btn(-[a-z0-9_-]*)?/gi, ' ');
  
              var is_link = this.data.range.isOnAnchor();
  
              var sc = r.sc;
              var so = r.so;
              var ec = r.ec;
              var eo = r.eo;
  
              var nodes;
              if (!is_link) {
                  if (sc.tagName) {
                      sc = dom.firstChild(so ? sc.childNodes[so] : sc);
                      so = 0;
                  } else if (so !== sc.textContent.length) {
                      if (sc === ec) {
                          ec = sc = sc.splitText(so);
                          eo -= so;
                      } else {
                          sc = sc.splitText(so);
                      }
                      so = 0;
                  }
                  if (ec.tagName) {
                      ec = dom.lastChild(eo ? ec.childNodes[eo-1] : ec);
                      eo = ec.textContent.length;
                  } else if (eo !== ec.textContent.length) {
                      ec.splitText(eo);
                  }
  
                  nodes = dom.listBetween(sc, ec);
  
                  // browsers can't target a picture or void node
                  if (dom.isVoid(sc) || dom.isImg(sc)) {
                      so = dom.listPrev(sc).length-1;
                      sc = sc.parentNode;
                  }
                  if (dom.isBR(ec)) {
                      eo = dom.listPrev(ec).length-1;
                      ec = ec.parentNode;
                  } else if (dom.isVoid(ec) || dom.isImg(sc)) {
                      eo = dom.listPrev(ec).length;
                      ec = ec.parentNode;
                  }
  
                  this.data.range = range.create(sc, so, ec, eo);
                  $(editable).data("range", this.data.range);
                  this.data.range.select();
              } else {
                  nodes = dom.ancestor(sc, dom.isAnchor).childNodes;
              }
  
              if (dom.isImg(sc) && nodes.indexOf(sc) === -1) {
                  nodes.push(sc);
              }
              if (nodes.length > 1 || dom.ancestor(nodes[0], dom.isImg)) {
                  var text = "";
                  this.data.images = [];
                  for (var i=0; i<nodes.length; i++) {
                      if (dom.ancestor(nodes[i], dom.isImg)) {
                          this.data.images.push(dom.ancestor(nodes[i], dom.isImg));
                          text += '[IMG]';
                      } else if (!is_link && nodes[i].nodeType === 1) {
                          // just use text nodes from listBetween
                      } else if (!is_link && i===0) {
                          text += nodes[i].textContent.slice(so, Infinity);
                      } else if (!is_link && i===nodes.length-1) {
                          text += nodes[i].textContent.slice(0, eo);
                      } else {
                          text += nodes[i].textContent;
                      }
                  }
                  this.data.text = text;
              }
          }
  
          this.data.text = this.data.text.replace(/[ \t\r\n]+/g, ' ');
  
          var allBtnClassSuffixes = /(^|\s+)btn(-[a-z0-9_-]*)?/gi;
          var allBtnShapes = /\s*(rounded-circle|flat)\s*/gi;
          this.data.className = this.data.iniClassName
              .replace(allBtnClassSuffixes, ' ')
              .replace(allBtnShapes, ' ');
          // 'o_submit' class will force anchor to be handled as a button in linkdialog.
          if (/(?:s_website_form_send|o_submit)/.test(this.data.className)) {
              this.isButton = true;
          }
      },
      /**
       * @override
       */
      start: function () {
          this.buttonOptsCollapseEl = this.el.querySelector('#o_link_dialog_button_opts_collapse');
  
          this.$styleInputs = this.$('input.link-style');
          this.$styleInputs.prop('checked', false).filter('[value=""]').prop('checked', true);
          if (this.data.iniClassName) {
              _.each(this.$('input[name="link_style_color"], select[name="link_style_size"] > option, select[name="link_style_shape"] > option'), el => {
                  var $option = $(el);
                  if ($option.val() && this.data.iniClassName.match(new RegExp('(^|btn-| |btn-outline-)' + $option.val()))) {
                      if ($option.is("input")) {
                          $option.prop("checked", true);
                      } else {
                          $option.parent().find('option').removeAttr('selected').removeProp('selected');
                          $option.parent().val($option.val());
                          $option.attr('selected', 'selected').prop('selected', 'selected');
                      }
                  }
              });
          }
          if (this.data.url) {
              var match = /mailto:(.+)/.exec(this.data.url);
              this.$('input[name="url"]').val(match ? match[1] : this.data.url);
              this._onURLInput();
          }
  
          this._updateOptionsUI();
          this._adaptPreview();
  
          this.$('input:visible:first').focus();
  
          return this._super.apply(this, arguments);
      },
  
      //--------------------------------------------------------------------------
      // Public
      //--------------------------------------------------------------------------
  
      /**
       * @override
       */
      save: function () {
          var data = this._getData();
          if (data === null) {
              var $url = this.$('input[name="url"]');
              $url.closest('.form-group').addClass('o_has_error').find('.form-control, .custom-select').addClass('is-invalid');
              $url.focus();
              return Promise.reject();
          }
          this.data.text = data.label;
          this.data.url = data.url;
          var allWhitespace = /\s+/gi;
          var allStartAndEndSpace = /^\s+|\s+$/gi;
          var allBtnTypes = /(^|[ ])(btn-secondary|btn-success|btn-primary|btn-info|btn-warning|btn-danger)([ ]|$)/gi;
          this.data.className = data.classes.replace(allWhitespace, ' ').replace(allStartAndEndSpace, '');
          if (data.classes.replace(allBtnTypes, ' ')) {
              this.data.style = {
                  'background-color': '',
                  'color': '',
              };
          }
          this.data.isNewWindow = data.isNewWindow;
          this.final_data = this.data;
          return this._super.apply(this, arguments);
      },
  
      //--------------------------------------------------------------------------
      // Private
      //--------------------------------------------------------------------------
  
      /**
       * Adapt the link preview to changes.
       *
       * @private
       */
      _adaptPreview: function () {
          var data = this._getData();
          if (data === null) {
              return;
          }
          const attrs = {
              target: data.isNewWindow ? '_blank' : '',
              href: data.url && data.url.length ? data.url : '#',
              class: `${data.classes.replace(/float-\w+/, '')} o_btn_preview`,
          };
          this.$("#link-preview").attr(attrs).html((data.label && data.label.length) ? data.label : data.url);
      },
      /**
       * Get the link's data (url, label and styles).
       *
       * @private
       * @returns {Object} {label: String, url: String, classes: String, isNewWindow: Boolean}
       */
      _getData: function () {
          var $url = this.$('input[name="url"]');
          var url = $url.val();
          var label = _.escape(this.$('input[name="label"]').val() || url);
  
          if (label && this.data.images) {
              for (var i = 0; i < this.data.images.length; i++) {
                  label = label.replace(/\[IMG\]/, this.data.images[i].outerHTML);
              }
          }
  
          if (!this.isButton && $url.prop('required') && (!url || !$url[0].checkValidity())) {
              return null;
          }
  
          const type = this.$('input[name="link_style_color"]:checked').val() || '';
          const size = this.$('select[name="link_style_size"]').val() || '';
          const shape = this.$('select[name="link_style_shape"]').val() || '';
          const shapes = shape ? shape.split(',') : [];
          const style = ['outline', 'fill'].includes(shapes[0]) ? `${shapes[0]}-` : '';
          const shapeClasses = shapes.slice(style ? 1 : 0).join(' ');
          const classes = (this.data.className || '') +
              (type ? (` btn btn-${style}${type}`) : '') +
              (shapeClasses ? (` ${shapeClasses}`) : '') +
              (size ? (' btn-' + size) : '');
          var isNewWindow = this.$('input[name="is_new_window"]').prop('checked');
          if (url.indexOf('@') >= 0 && url.indexOf('mailto:') < 0 && !url.match(/^http[s]?/i)) {
              url = ('mailto:' + url);
          } else if (url.indexOf(location.origin) === 0 && this.$('#o_link_dialog_url_strip_domain').prop("checked")) {
              url = url.slice(location.origin.length);
          }
          var allWhitespace = /\s+/gi;
          var allStartAndEndSpace = /^\s+|\s+$/gi;
          return {
              label: label,
              url: url,
              classes: classes.replace(allWhitespace, ' ').replace(allStartAndEndSpace, ''),
              isNewWindow: isNewWindow,
          };
      },
      /**
       * @private
       */
      _updateOptionsUI: function () {
          const el = this.el.querySelector('[name="link_style_color"]:checked');
          $(this.buttonOptsCollapseEl).collapse(el && el.value ? 'show' : 'hide');
      },
  
      //--------------------------------------------------------------------------
      // Handlers
      //--------------------------------------------------------------------------
  
      /**
       * @private
       */
      _onAnyChange: function () {
          this._adaptPreview();
      },
      /**
       * @private
       */
      _onTypeChange() {
          this._updateOptionsUI();
      },
      /**
       * @private
       */
      _onURLInput: function () {
          var $linkUrlInput = this.$('#o_link_dialog_url_input');
          $linkUrlInput.closest('.form-group').removeClass('o_has_error').find('.form-control, .custom-select').removeClass('is-invalid');
          let value = $linkUrlInput.val();
          let isLink = value.indexOf('@') < 0;
          this.$('input[name="is_new_window"]').closest('.form-group').toggleClass('d-none', !isLink);
          this.$('.o_strip_domain').toggleClass('d-none', value.indexOf(window.location.origin) !== 0);
      },
  });
  
  return LinkDialog;
  });
  