docs: function ( opts ) {
      var that = this;
  
      // Real focus function will be set when iframe will be loaded.
      var postponedFocus = false;
      var postponedHTML = null;
      var iframeHasChanged = false;
  
      var elem = $.elem( this, 'div', 'wdg-wysiwyg', 'thm-ele2' );
      var slider = $.div( 'slider' );
      var menu = $.div( 'menu', "thm-bgPL" );
      var iframe = $.tag( 'iframe', { src: 'squire2/squire.html' } );
      var loaded = new Promise( function ( resolve, reject ) {
          iframe.addEventListener( 'load', function () {
              // Storing a reference to the wysiwyg editor.
              var squire = iframe.contentWindow.editor;
              that.squire = squire;
              if ( postponedFocus ) {
                  that.focus = true;
              }
              // Adding editor buttons.
              if ( postponedHTML !== null ) {
                  squire.setHTML( postponedHTML );
                  postponedHTML = null;
              }
              // Adding onChange event when focus is lost.
              squire.addEventListener( 'input', function () {
                  // Prevent from update looping.
                  iframeHasChanged = true;
                  that.value = squire.getHTML();
              } );
              squire.addEventListener( "willPaste", function ( event ) {
                  console.info( "[tp4.wysiwyg-editor] (willPaste) event=", event );
                  var text = event.fragment.textContent;
                  var normalized = text.trim().toLowerCase();
                  if ( normalized.substr( 0, 8 ) !== '<iframe ' ) return;
                  if ( normalized.substr( normalized.length - 9 ) !== '</iframe>' ) return;
                  var div = $.div();
                  div.innerHTML = text;
                  var iframe = div.firstChild;
                  $.css( iframe, { maxWidth: "80vw" } );
                  var src = iframe.getAttribute( "src" );
                  var a = $.tag( "a", { target: "IFRAME", href: src } );
                  a.innerHTML = '<svg width=32 height=32><g stroke="#000" fill="none" stroke-width=2><path d="M14,8h-6v6 M14,24h-6v-6 M18,8h6v6 M18,24h6v-6"/></g></svg>';
                  a.setAttribute(
                      "style",
                      "border-radius:100%;position:absolute;right:4px;top:4px;display:inline-block;z-index:2;" +
                      "background:#fff;width:32px;height:32px;line-height:32px;text-align:center;" +
                      "font-size:16px;font-weight:bold;font-family:sans-serif;box-shadow:0 8px 16px rgba(0,0,0,.5);" +
                      "text-decoration:none;color:#000;"
                  );
                  $.add( div, a );
                  event.fragment.firstChild.textContent = "";
                  event.fragment.firstChild.innerHTML = div.innerHTML;
                  $.css( event.fragment.firstChild, { position: "relative", padding: 0 } );
              } );
              Object.defineProperty( that, 'squire', {
                  value: squire,
                  writable: false,
                  configurable: true,
                  enumerable: true
              } );
              resolve( squire );
          }, false );
      } );
      var body = $.div( 'body', [ iframe ] );
      $.add( elem, menu, body, slider );
  
      DB.propBoolean( this, 'focus' )( function ( v ) {
          loaded.then( function () {
              if ( v ) {
                  that.squire.focus();
              } else {
                  that.squire.blur();
              }
          } );
      } );
      DB.propUnit( this, 'height' )( function ( v ) {
          if ( v.u === 'px' ) {
              // Show the slider only for heights in pixels.
              $.removeClass( elem, 'absolute' );
          } else {
              // Otherwise, put the widget in absolute position.
              $.addClass( elem, 'absolute' );
          }
          $.css( elem, {
              height: v.v + v.u
          } );
      } );
      DB.prop( this, 'menu' )( function ( v ) {
          loaded.then( function () {
              $.clear( menu );
              v.forEach( function ( itm ) {
                  if ( itm === '-' ) {
                      $.add( menu, $.div( 'sep', [ itm ] ) );
                  } else {
                      // Expand itm.
                      if ( typeof itm === 'string' ) {
                          itm = {
                              id: itm
                          };
                      }
                      if ( typeof itm.icon !== 'string' ) {
                          itm.icon = itm.id;
                      }
                      if ( typeof itm.tip !== 'string' ) {
                          itm.tip = _( 'tip-' + itm.id );
                      }
                      if ( typeof itm.key === 'string' ) {
                          itm.tip = ( itm.tip || '' ) + " [" + itm.key + "]";
                          loaded.then( function () {
                              that.squire.setKeyHandler( itm.key.toLowerCase(), function ( self, event, range ) {
                                  event.preventDefault();
                                  onMenu.call( that, itm, range );
                              } );
                          } );
                      }
                      var icon = new Icon( { size: '2rem', content: itm.icon } );
                      var tip = itm.tip;
                      if ( tip && tip != 'icon-' + itm.id ) {
                          icon.$.setAttribute( 'title', tip );
                      }
                      $.addClass( icon, 'icon' );
                      $.on( icon, {
                          down: $.addClass.bind( $, icon, 'down' ),
                          up: $.removeClass.bind( $, icon, 'down' ),
                          tap: onMenu.bind( that, itm )
                      } );
                      $.add( menu, icon );
                  }
              } );
          } );
      } );
      DB.propString( this, 'value' )( function ( v ) {
          loaded.then( function () {
              if ( iframeHasChanged ) {
                  iframeHasChanged = false;
              } else {
                  that.squire.setHTML( v );
              }
          } );
      } );
      DB.propAddClass( this, 'wide' );
      DB.propRemoveClass( this, 'visible', 'hide' );
  
      DB.extend( {
          value: "",
          menu: DEFAULT_MENU,
          height: 240,
          visible: true
      }, opts, this );
  
      var initialHeight = this.height.v;
  
      $.on( slider, {
          down: function () {
              initialHeight = that.height.v;
          },
          drag: function ( evt ) {
              if ( that.height.u === 'px' ) {
                  // Drag only if height is in pixels.
                  that.height = Math.max( 200, initialHeight + evt.dy );
              }
          }
      } );
  
      // Managing fullscreen display.
      var fullscreen = new Fx.Fullscreen( {
          target: elem,
          // When the component is put fullscreen, it is temporarly removed from the DOM. This causes the IFrame to be reloaded,
          // and the result is that we lost content. That's why we use `postponedHTML`.
          onBeforeReplace: function () {
              postponedHTML = that.squire.getHTML();
          }
      } );
  }