rubycentral/cfp-app

View on GitHub
app/javascript/controllers/editor_controller.js

Summary

Maintainability
C
1 day
Test Coverage
import { Controller } from 'stimulus'
import CodeMirror from 'codemirror/lib/codemirror.js'
import 'codemirror/mode/htmlmixed/htmlmixed.js'

export default class extends Controller {
  static targets = ['htmlContent', 'wysiwygContent', 'wysiwyg', 'html']
  static values = { changed: { type: Boolean, default: false } }

  initialize () {
    this.tinyMCEDefaults = {
      height: 500,
      menubar: false,
      plugins: [
        'advlist autolink lists link image charmap print preview anchor',
        'searchreplace visualblocks code fullscreen',
        'insertdatetime media table paste code help wordcount'
      ],
      toolbar: 'undo redo | formatselect | ' +
      ' bold italic backcolor | alignleft aligncenter ' +
      ' alignright alignjustify | bullist numlist outdent indent | ' +
      ' removeformat | image code | help',
      valid_elements: '*[*]',
      forced_root_block : '',
      images_upload_url: '/image_uploads',
      images_file_types: 'jpeg,jpg,jpe,jfi,jif,jfif,png,gif,bmp,webp,svg',
      relative_urls: false,
      convert_urls: false,
      init_instance_callback: (editor) => {
        editor.on('input', (e) => {
          this.preview(e.target.innerHTML);
          this.changedValue = true;
        });
        editor.on('change', (e) => {
          this.preview(e.target.getContent());
          this.changedValue = true;
        });
      }
    }
  }

  editHtml(e) {
    e.preventDefault();
    this.wysiwygTarget.classList.add("hidden");
    this.htmlTarget.classList.remove("hidden");
    this.htmlContentTarget.disabled = false;
    this.initializeCodeMirror(this.wysiwygEditor.getContent());
  }

  initializeCodeMirror(content) {
    var editor = CodeMirror.fromTextArea(this.htmlContentTarget, {
      mode: "htmlmixed",
      lineWrapping: true,
    });
    if (content) {
      editor.setValue(content);
    }
    this.indentCodeMirror(editor);
    editor.on('change', (e) => {
      this.changedValue = true;
      this.preview(e.getValue());
    })
    editor.on('drop', (e, event) => {
      event.preventDefault();
      this.uploadFile(event.dataTransfer.files[0], event, e)
    })
    return editor;
  }

  indentCodeMirror(editor) {
    for (var i=0;i<editor.lineCount();i++) { editor.indentLine(i); }
  }

  preview(content) {
    this.debounce(function() {
      document.getElementById('hidden-preview').value = content;
      document.getElementById('preview-form').submit();
    }, 1000)
  }

  debounce(func, delay) {
    if(this.timeout) { clearTimeout(this.timeout) }
    this.timeout = setTimeout(func, delay);
  }

  wysiwyg(e) {
    e.preventDefault();
    this.wysiwygTarget.classList.remove("hidden");
    this.htmlTarget.classList.add("hidden");
    this.htmlContentTarget.disabled = true;
    this.wysiwygEditor.setContent(this.htmlEditor.getValue());
    this.htmlEditor.toTextArea();
  }

  uploadFile(file, event, editor) {
    let url = '/image_uploads'
    let formData = new FormData()

    formData.append('file', file)

    fetch(url, {
      method: 'POST',
      body: formData
    }).then(response => response.json())
      .then(data => {
        let newline = `<img src="${data.location}"/>`
        let doc= editor.getDoc()
        editor.focus()
        let x = event.pageX
        let y = event.pageY
        editor.setCursor(editor.coordsChar({left:x,top:y}))
        let newpos = editor.getCursor()
        doc.replaceRange(newline, newpos)
      })
  }

  get wysiwygEditor() {
    return tinyMCE.activeEditor;
  }

  get htmlEditor() {
    return document.querySelector('.CodeMirror').CodeMirror;
  }

  leavingPage(event) {
    if (this.changedValue) {
      event.returnValue = "Are you sure you want to leave with unsaved changes?";
      return event.returnValue;
    }
  }

  allowFormSubmission(event) {
    this.changedValue = false;
  }

  connect () {
    let config = Object.assign({ target: this.wysiwygContentTarget }, this.tinyMCEDefaults)
    tinyMCE.init(config)
    this.initializeCodeMirror();
  }

  disconnect () {
    tinyMCE.remove()
  }
}