JSFiddle - React, Tailwind, and code Playground

Dual TinyMCE communication

by tonytlwu

HTML

<script src="https://tinymce.cachefly.net/4.3/tinymce.min.js"></script>
<p>This demo uses 2 TinyMCEs to sync configurations and commands.</p>
<hr />
<textarea id="phantom"></textarea>

<p>Zoom: <input type="range" id="zoom" name="zoom" min="10" max="100" value="100" /></p>

<div id="device">

  <div id="preview">
    <h3>Here's a bunch of content</h3>
    <p>
      Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Vestibulum id ligula porta felis euismod semper.
    </p>
    <ul>
      <li>Risus Egestas</li>
      <li>Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum.</li>
      <li>Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</li>
      <li>Adipiscing Magna Parturient Justo</li>
    </ul>
  </div>
</div>

CSS

#preview {
  background: #fff;
  width: 100%;
  height: 100%;
  border: 1px solid #afafaf;
  box-sizing: border-box;
  padding: 10px;
  font-size: 14px;
  font-family: 'Open Sans';
  overflow: hidden;
}

#preview h1,
#preview h2,
#preview h3 {
  font-family: Dosis;
  font-weight: 200;
  font-size: 1.65em;
  color: #00abd1;
}

#device {
  width: 320px;
  height: 568px;
  box-sizing: content-box;
  padding: 60px 15px 80px;
  background-color: #d0d2d2;
  border-radius: 32px;
  margin: 20px auto;
  transform-origin: center top;
  transition: all 0.2s ease-out;
}

#device > iframe {
  width: 100%;
  height: 100%;
}

#phantom_ifr {
  /* Hides Phantom Editor */
  display: none !important;
}

.mce-container.disabled {
  opacity: 0.5;
  pointer-events: none;
}

.mce-tinymce-inline {
  display: none !important;
}

JavaScript

function syncPreviewToPhantom() {
  tinymce.get('phantom').setContent(document.querySelector('#preview').innerHTML);
}

function savePreviewEditor() {
  tinymce.get('preview').save();
}

// Phantom TinyMCE setup
tinymce.init({
  selector: '#phantom',
  menubar: false,
  statusbar: false,
  setup: function(ed) {
    ed.on('BeforeExecCommand', function(e) {
    	var cmd = e.command;
      var ui = e.ui;
      var value = e.value;
      tinymce.get('preview').execCommand(cmd, ui, value);
      // return false; // Prevents the command from executing in the Phantom Editor so the button state doesn't change
    });
  }
});

// Preview TinyMCE setup
tinymce.init({
  selector: '#preview',
  menubar: false,
  inline: true,
  setup: function(ed) {
    setTimeout(syncPreviewToPhantom, 0);
    ed.on('change paste keyup mouseup focus blur', function(e) {
      // savePreviewEditor();
      // syncPreviewToPhantom();
      var activeButtons = document.querySelectorAll('.mce-tinymce-inline .mce-toolbar-grp .mce-btn');
      var label = null;
      for (var i = 0, l = activeButtons.length; i < l; i++) {
      	label = activeButtons[i].getAttribute('aria-label');
      	if ( label !== null ) {
          if ( activeButtons[i].classList.contains('mce-active') ) {
          	tinymce.get('phantom').editorContainer.querySelector('.mce-toolbar-grp .mce-btn[aria-label="'+label+'"]').classList.add('mce-active');
          } else {
          	tinymce.get('phantom').editorContainer.querySelector('.mce-toolbar-grp .mce-btn[aria-label="'+label+'"]').classList.remove('mce-active');
          }
        }
      }
    });
  }
});

// Adds zoom functionality
function zoomDevice() {
	var zoom = document.querySelector('#zoom').value;
  var device = document.querySelector('#device');
  device.style.webkitTransform = 'scale(' + (zoom/100) + ')';
  device.style.transform = 'scale(' + (zoom/100) + ')';
}
document.querySelector('#zoom').addEventListener( 'change', zoomDevice, false );