Newer
Older
dmpopidor / lib / assets / javascripts / utils / tinymce.js
// Import TinyMCE
import tinymce from 'tinymce/tinymce';
// Import TinyMCE theme
import 'tinymce/themes/modern/theme';
// Plugins
import 'tinymce/plugins/table';
import 'tinymce/plugins/lists';
import 'tinymce/plugins/autoresize';
import 'tinymce/plugins/link';
import 'tinymce/plugins/paste';
import 'tinymce/plugins/advlist';
// Other dependencies
import { isObject, isString } from './isType';

// Configuration extracted from https://www.tinymce.com/docs/advanced/usage-with-module-loaders/
require.context(
  'file-loader?name=./stylesheets/[path][name].[ext]&context=node_modules/tinymce!tinymce/skins',
  true,
  /.*/,
);

export const defaultOptions = {
  selector: '.tinymce',
  statusbar: false,
  menubar: false,
  toolbar: 'bold italic | bullist numlist | link | table',
  plugins: 'table autoresize link paste advlist lists',
  advlist_bullet_styles: 'circle,disc,square', // Only disc bullets display on htmltoword
  target_list: false,
  autoresize_min_height: 130,
  autoresize_bottom_margin: 10,
  branding: false,
  extended_valid_elements: 'iframe[tooltip] , a[href|target=_blank]',
  paste_auto_cleanup_on_paste: true,
  paste_remove_styles: true,
  paste_retain_style_properties: 'none',
  paste_convert_middot_lists: true,
  paste_remove_styles_if_webkit: true,
  paste_remove_spans: true,
  paste_strip_class_attributes: 'all',
  max_height: 350,
  table_default_attributes: {
    border: 1,
  },
  skin_url: '/stylesheets/skins/lightgray', // editorManager.baseURL is not resolved properly for IE since document.currentScript is not supported, see issue https://github.com/tinymce/tinymce/issues/3584
};
/*
  This function is invoked anytime a new editor is initialised (e.g. Tinymce.init())
  and shrinks a tinymce editor to the minimum height specified at autoresize_min_height
  editor's settings. Since there are cases that tinymce editor is loaded in the DOM
  but has display:none style, the iframe associated gets the height of the screen's device
  and using this function there is no need to wait until the tinymce gains focus to be autoresized.
*/
const resizeEditors = (editors) => {
  editors.forEach((editor) => {
    $(editor.iframeElement).height(editor.settings.autoresize_min_height);
  });
};

export const Tinymce = {
  /*
    Initialises a tinymce editor given the object passed. If a non-valid object is passed,
    the defaultOptions object is used instead
    @param options - An object with tinyMCE properties
  */
  init(options = {}) {
    if (isObject(options)) {
      tinymce.init($.extend(true, defaultOptions, options)).then(resizeEditors);
    } else {
      tinymce.init(defaultOptions).then(resizeEditors);
    }
  },
  /*
    Finds any tinyMCE editor whose target element/textarea has the className passed
    @param className - A string representing the class name of the tinyMCE editor
    target element/textarea to look for
    @return An Array of tinymce.Editor objects
  */
  findEditorsByClassName(className) {
    if (isString(className)) {
      return tinymce.editors.reduce((acc, e) => {
        if ($(e.getElement()).hasClass(className)) {
          return acc.concat([e]);
        }
        return acc;
      }, []);
    }
    return [];
  },
  /*
    Finds a tinyMCE editor whose target element/textarea has the id passed
    @param id - A string representing the id of the tinyMCE editor target
    element/textarea to look for
    @return tinymce.Editor object, otherwise undefined
  */
  findEditorById(id) {
    if (isString(id)) {
      return tinymce.editors.find(el => el.id === id);
    }
    return undefined;
  },
  /*
    Destroy every editor instance whose target element/textarea has the className passed. This
    method executes for each editor the method defined at tinymce.Editor.destroy (e.g. https://www.tinymce.com/docs/api/tinymce/tinymce.editor/#destroy).
    @param className - A string representing the class name of the tinyMCE editor
    target element/textarea to look for
    @return undefined
  */
  destroyEditorsByClassName(className) {
    const editors = this.findEditorsByClassName(className);
    editors.forEach(ed => ed.destroy(false));
  },
  /*
    Destroy an editor instance whose target element/textarea has HTML id passed. This method
    executes tinymce.Editor.destroy (e.g. https://www.tinymce.com/docs/api/tinymce/tinymce.editor/#destroy) for a successfull id found.
    @return undefined
  */
  destroyEditorById(id) {
    const editor = this.findEditorById(id);
    if (editor) {
      editor.destroy(false);
    }
  },
};