function addCustomFeatures(spec) {
  const { $rteContainer, $textarea, myID } = spec;
  const $ce = $rteContainer.find('[contenteditable="true"]');

  // Style whole container (incl. menu bar etc.) like all inputs
  const addFocusStyle = () => $rteContainer.addClass('rte-focused');
  const removeFocusStyle = () => $rteContainer.removeClass('rte-focused');
  $ce.focus(addFocusStyle);
  $ce.focusout(removeFocusStyle);

  // Adjust height to match textarea
  const setRTEHeight = () => {
    let textareaHeight = $textarea.css('height');
    if (textareaHeight)
      $rteContainer.css('height', textareaHeight);
    else
      $rteContainer.css('height', '10em');
    textareaHeight = parseInt($textarea.css('height'), 10);
    let menuHeight = parseInt($rteContainer.find('.ProseMirror-menubar').css('height'), 10) || 41;
    let rteHeight = textareaHeight - (menuHeight + 2);
    $ce.css('height', rteHeight + 'px');
  };
  setRTEHeight();

  // Adjust height to full window
  const setRTEHeightFullScreen = () => {
    $rteContainer.addClass('rte-container-full-screen');
    $rteContainer.find('.ProseMirror,.ProseMirror-menubar').addClass('ProseMirror-full-screen');
    let menuHeight = parseInt($rteContainer.find('.ProseMirror-menubar').css('height'), 10) || 41;
    let rteHeight = parseInt($rteContainer.css('height'), 10) - (menuHeight + 2);
    $ce.css('height', rteHeight + 'px');
  };

  // Helper to use full available window (or full screen if enabled) for editor
  rtes[myID].enterFullScreen = () => {
    $(window).off('resize', setRTEHeight);
    $(window).on('resize', setRTEHeightFullScreen);
    setRTEHeightFullScreen();
    $ce.off('focus', addFocusStyle);
    $ce.off('focusout', removeFocusStyle);
    removeFocusStyle();
    rtes[myID].editorView.focus();
  };

  // Back to normal
  rtes[myID].exitFullScreen = () => {
    $(window).off('resize', setRTEHeightFullScreen);
    $(window).on('resize', setRTEHeight);
    $rteContainer.removeClass('rte-container-full-screen');
    $rteContainer.find('.ProseMirror,.ProseMirror-menubar').removeClass('ProseMirror-full-screen');
    setRTEHeight();
    $ce.focus(addFocusStyle);
    $ce.focusout(removeFocusStyle);
    addFocusStyle();
    rtes[myID].editorView.focus();
  };

  // Menu can wrap, so keep an eye on the height
  $(window).resize(setRTEHeight);

  $ce.blur(function() {
    updateRTESelectionData($textarea, $(this));
    // Re-generating the markdown on blur is a performance compromise; we may want
    // to add more triggers if this is insufficient.
    updateTextarea($textarea, $(this), rtes[myID].editorView);
  });

  // Try to ensure the textarea is up-to-date before any "Save draft"
  // functionality kicks in
  const updateOnUnload = () => updateTextarea($textarea, $ce, rtes[myID].editorView);

  $(window).on('beforeunload', updateOnUnload);

  // Full remove this control and all associated event handlers
  rtes[myID].nuke = function() {
    $ce.off();
    $(window).off('resize', setRTEHeight);
    $(window).off('beforeunload', updateOnUnload);
    rtes[myID].editorView.destroy();
    delete rtes[myID];
    $rteContainer.remove();
  };

  // Helper for external access to re-generate RTE
  rtes[myID].reRender = function() {
    rtes[myID].nuke();
    renderRTE($textarea);
  };
}
// - Event handler to track selection data within RTE even if mode is switched
Event handler to track selection data within RTE even if mode is switched
Event handler to track selection data within RTE even if mode is switched
function addCustomFeatures(spec) {
  const { $rteContainer, $textarea, myID } = spec;
  const $ce = $rteContainer.find('[contenteditable="true"]');
  const addFocusStyle = () => $rteContainer.addClass('rte-focused');
  const removeFocusStyle = () => $rteContainer.removeClass('rte-focused');
  $ce.focus(addFocusStyle);
  $ce.focusout(removeFocusStyle);
  const setRTEHeight = () => {
    let textareaHeight = $textarea.css('height');
    if (textareaHeight)
      $rteContainer.css('height', textareaHeight);
    else
      $rteContainer.css('height', '10em');
    textareaHeight = parseInt($textarea.css('height'), 10);
    let menuHeight = parseInt($rteContainer.find('.ProseMirror-menubar').css('height'), 10) || 41;
    let rteHeight = textareaHeight - (menuHeight + 2);
    $ce.css('height', rteHeight + 'px');
  };
  setRTEHeight();
  const setRTEHeightFullScreen = () => {
    $rteContainer.addClass('rte-container-full-screen');
    $rteContainer.find('.ProseMirror,.ProseMirror-menubar').addClass('ProseMirror-full-screen');
    let menuHeight = parseInt($rteContainer.find('.ProseMirror-menubar').css('height'), 10) || 41;
    let rteHeight = parseInt($rteContainer.css('height'), 10) - (menuHeight + 2);
    $ce.css('height', rteHeight + 'px');
  };
  rtes[myID].enterFullScreen = () => {
    $(window).off('resize', setRTEHeight);
    $(window).on('resize', setRTEHeightFullScreen);
    setRTEHeightFullScreen();
    $ce.off('focus', addFocusStyle);
    $ce.off('focusout', removeFocusStyle);
    removeFocusStyle();
    rtes[myID].editorView.focus();
  };
  rtes[myID].exitFullScreen = () => {
    $(window).off('resize', setRTEHeightFullScreen);
    $(window).on('resize', setRTEHeight);
    $rteContainer.removeClass('rte-container-full-screen');
    $rteContainer.find('.ProseMirror,.ProseMirror-menubar').removeClass('ProseMirror-full-screen');
    setRTEHeight();
    $ce.focus(addFocusStyle);
    $ce.focusout(removeFocusStyle);
    addFocusStyle();
    rtes[myID].editorView.focus();
  };
  $(window).resize(setRTEHeight);
  $ce.blur(function() {
    updateRTESelectionData($textarea, $(this));
    updateTextarea($textarea, $(this), rtes[myID].editorView);
  });
  const updateOnUnload = () => updateTextarea($textarea, $ce, rtes[myID].editorView);
  $(window).on('beforeunload', updateOnUnload);
  rtes[myID].nuke = function() {
    $ce.off();
    $(window).off('resize', setRTEHeight);
    $(window).off('beforeunload', updateOnUnload);
    rtes[myID].editorView.destroy();
    delete rtes[myID];
    $rteContainer.remove();
  };
  rtes[myID].reRender = function() {
    rtes[myID].nuke();
    renderRTE($textarea);
  };
}
function addCustomFeatures ( spec ) { const { $rteContainer , $textarea , myID } = spec ; const $ce = $rteContainer . find ( '[contenteditable="true"]' ) ; const addFocusStyle = ( ) => $rteContainer . addClass ( 'rte-focused' ) ; const removeFocusStyle = ( ) => $rteContainer . removeClass ( 'rte-focused' ) ; $ce . focus ( addFocusStyle ) ; $ce . focusout ( removeFocusStyle ) ; const setRTEHeight = ( ) => { let textareaHeight = $textarea . css ( 'height' ) ; if ( textareaHeight ) $rteContainer . css ( 'height' , textareaHeight ) ; else $rteContainer . css ( 'height' , '10em' ) ; textareaHeight = parseInt ( $textarea . css ( 'height' ) , 10 ) ; let menuHeight = parseInt ( $rteContainer . find ( '.ProseMirror-menubar' ) . css ( 'height' ) , 10 ) || 41 ; let rteHeight = textareaHeight - ( menuHeight + 2 ) ; $ce . css ( 'height' , rteHeight + 'px' ) ; } ; setRTEHeight ( ) ; const setRTEHeightFullScreen = ( ) => { $rteContainer . addClass ( 'rte-container-full-screen' ) ; $rteContainer . find ( '.ProseMirror,.ProseMirror-menubar' ) . addClass ( 'ProseMirror-full-screen' ) ; let menuHeight = parseInt ( $rteContainer . find ( '.ProseMirror-menubar' ) . css ( 'height' ) , 10 ) || 41 ; let rteHeight = parseInt ( $rteContainer . css ( 'height' ) , 10 ) - ( menuHeight + 2 ) ; $ce . css ( 'height' , rteHeight + 'px' ) ; } ; rtes [ myID ] . enterFullScreen = ( ) => { $ ( window ) . off ( 'resize' , setRTEHeight ) ; $ ( window ) . on ( 'resize' , setRTEHeightFullScreen ) ; setRTEHeightFullScreen ( ) ; $ce . off ( 'focus' , addFocusStyle ) ; $ce . off ( 'focusout' , removeFocusStyle ) ; removeFocusStyle ( ) ; rtes [ myID ] . editorView . focus ( ) ; } ; rtes [ myID ] . exitFullScreen = ( ) => { $ ( window ) . off ( 'resize' , setRTEHeightFullScreen ) ; $ ( window ) . on ( 'resize' , setRTEHeight ) ; $rteContainer . removeClass ( 'rte-container-full-screen' ) ; $rteContainer . find ( '.ProseMirror,.ProseMirror-menubar' ) . removeClass ( 'ProseMirror-full-screen' ) ; setRTEHeight ( ) ; $ce . focus ( addFocusStyle ) ; $ce . focusout ( removeFocusStyle ) ; addFocusStyle ( ) ; rtes [ myID ] . editorView . focus ( ) ; } ; $ ( window ) . resize ( setRTEHeight ) ; $ce . blur ( function ( ) { updateRTESelectionData ( $textarea , $ ( this ) ) ; updateTextarea ( $textarea , $ ( this ) , rtes [ myID ] . editorView ) ; } ) ; const updateOnUnload = ( ) => updateTextarea ( $textarea , $ce , rtes [ myID ] . editorView ) ; $ ( window ) . on ( 'beforeunload' , updateOnUnload ) ; rtes [ myID ] . nuke = function ( ) { $ce . off ( ) ; $ ( window ) . off ( 'resize' , setRTEHeight ) ; $ ( window ) . off ( 'beforeunload' , updateOnUnload ) ; rtes [ myID ] . editorView . destroy ( ) ; delete rtes [ myID ] ; $rteContainer . remove ( ) ; } ; rtes [ myID ] . reRender = function ( ) { rtes [ myID ] . nuke ( ) ; renderRTE ( $textarea ) ; } ; }
Event handler to track selection data within RTE even if mode is switched
Event handler to track selection data within RTE even if mode is switched
// Style whole container (incl. menu bar etc.) like all inputs // Adjust height to match textarea // Adjust height to full window // Helper to use full available window (or full screen if enabled) for editor // Back to normal // Menu can wrap, so keep an eye on the height // Re-generating the markdown on blur is a performance compromise; we may want // to add more triggers if this is insufficient. // Try to ensure the textarea is up-to-date before any "Save draft" // functionality kicks in // Full remove this control and all associated event handlers // Helper for external access to re-generate RTE