tinymce 5 line break plugin - plugins

I've created a small TinyMCE v5 plugin that adds a line break button to the menu or toolbar :
/* Plugin code */
(function () {
'use strict';
var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
var register = function (editor) {
// Translate button text
tinymce.util.I18n.add('fr_FR', {'Inser Line Break' : 'Insérer un saut de ligne'});
// Register new Icon
editor.ui.registry.addIcon('line-break', '<svg width="24" height="24"><path d="M 6.4,15.129563 H 12 c 3.7,0 6.2,-2 6.8,-5.1 0.6,-2.7 -0.4,-5.6 -2.3,-6.8 a 1.029563,1.029563 0 0 0 -1,1.8 c 1.1,0.6 1.8,2.7 1.4,4.6 -0.5,2.1 -2.1,3.5 -4.9,3.5 H 6.4 l 3.3,-3.3 a 1,1 0 1 0 -1.4,-1.4 l -5,5 a 1,1 0 0 0 0,1.4 l 5,5 a 1,1 0 0 0 1.4,-1.4 z" fill-rule="nonzero"/></svg>');
editor.addCommand('InsertLineBreak', function () {
editor.execCommand('mceInsertContent', false, '<br>');
});
};
var register$1 = function (editor) {
editor.ui.registry.addButton('br', {
icon: 'line-break',
tooltip: 'Inser Line Break',
onAction: function () {
return editor.execCommand('InsertLineBreak');
}
});
editor.ui.registry.addMenuItem('br', {
icon: 'line-break',
text: 'Inser Line Break',
onAction: function () {
return editor.execCommand('InsertLineBreak');
}
});
};
function Plugin () {
global.add('br', function (editor) {
register(editor);
register$1(editor);
});
}
Plugin();
}());
/* Main code */
tinymce.init({
selector : "textarea#mceEditor",
menubar: "",
language : "fr_FR",
plugins: ["advlist autolink link image lists preview hr anchor pagebreak wordcount fullscreen media nonbreaking emoticons template paste help", "br"],
toolbar: "fullscreen undo redo | formatselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist | link unlink image hr br ihtml | media | emoticons ",
block_formats: "Paragraph=p;Header 2=h2",
branding: false,
elementpath: false,
content_css : "https://www.dev-book.fr/styles/editor.css?v=36",
valid_elements : "h1,h2,hr,br,ul,ol,li,strong/b,em/i,p/div[align|style],a[href|target],img[class|src|border=0|width|height|align|style|alt],iframe[src|width|height]",
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/tinymce/5.7.1/tinymce.min.js"></script>
<textarea name="page_texte" style="height:"400px" id="mceEditor"></textarea>
The only way to make it works is adding a space behind <br> on line :
editor.execCommand('mceInsertContent', false, '<br> ');
otherwise the <br> tag is inserted but it has no effects meaning no line return occurs in the editor. The problem by adding this space is that it adds a space char at new line startup and I don't wan't that ...
I just want the same behavior when clicking my plugin button as hitting "MAJ+ENTER" on keyboard to insert line break instead of default new paragraph.
Unfortunately the code snippet does not work because of external TinyMCE CDN resource not being able to access Stack Overflow DOM. If one has suggestions to this sub-questions.

I found replacing '<br> ' by '<br>\u200C' was fixing my issue. \u200C is a code for "zero width non joiner"
Not sure this is the perfect answer but will go on with it waiting for a better one !

Related

"Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'apply')" error in TinyMCE

I'm getthing this error message "Cannot read properties of undefined (reading 'apply')" when I applied TinyMCE and run in my webpage.
tinymce.min.js:9 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'apply')
at _x.execCallback (tinymce.min.js:9:366461)
at m1 (tinymce.min.js:9:332437)
at h1 (tinymce.min.js:9:333521)
at tinymce.min.js:9:340110
This is the error point but I don't know how can I fix this error message.
_x.prototype.execCallback = function(e) {
for (var t = [], n = 1; n < arguments.length; n++)
t[n - 1] = arguments[n];
var r, o = this.settings[e];
if (o)
return this.callbackLookup && (r = this.callbackLookup[e]) && (o = r.func,
r = r.scope),
"string" == typeof o && (r = (r = o.replace(/\.\w+$/, "")) ? Ex(r) : 0,
o = Ex(o),
this.callbackLookup = this.callbackLookup || {},
this.callbackLookup[e] = {
func: o,
scope: r
}),
o.apply(r || this, t)
}
This is the init value in TinyMCE. I copied default value in one of the free bootstrap templates.
var useDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
tinymce.init({
selector: 'textarea',
plugins: 'print preview paste importcss searchreplace autolink autosave save directionality code visualblocks visualchars fullscreen image link media template codesample table charmap hr pagebreak nonbreaking anchor insertdatetime advlist lists wordcount textpattern noneditable help charmap quickbars emoticons',
imagetools_cors_hosts: ['picsum.photos'],
menubar: 'file edit view insert format tools table help',
toolbar: 'undo redo | bold italic underline strikethrough | fontselect fontsizeselect formatselect | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | forecolor backcolor removeformat | pagebreak | charmap emoticons | fullscreen preview save print | insertfile image media template link anchor codesample | ltr rtl',
toolbar_sticky: true,
table_background_color_map: [
{title: 'Red', value: 'FF0000'},
{title: 'White', value: 'FFFFFF'},
{title: 'Yellow', value: 'F1C40F'}
],
table_border_styles: [
{title: 'Solid', value: 'solid'},
{title: 'Dotted', value: 'dotted'},
{title: 'Dashed', value: 'dashed'}
],
autosave_ask_before_unload: true,
autosave_interval: '30s',
autosave_prefix: '{path}{query}-{id}-',
autosave_restore_when_empty: false,
autosave_retention: '2m',
image_advtab: true,
link_list: [{
title: 'My page 1',
value: 'https://www.tiny.cloud'
},
{
title: 'My page 2',
value: 'http://www.moxiecode.com'
}
],
image_list: [{
title: 'My page 1',
value: 'https://www.tiny.cloud'
},
{
title: 'My page 2',
value: 'http://www.moxiecode.com'
}
],
image_class_list: [{
title: 'None',
value: ''
},
{
title: 'Some class',
value: 'class-name'
}
],
importcss_append: true,
file_picker_callback: function(callback, value, meta) {
/* Provide file and text for the link dialog */
if (meta.filetype === 'file') {
callback('https://www.google.com/logos/google.jpg', {
text: 'My text'
});
}
/* Provide image and alt text for the image dialog */
if (meta.filetype === 'image') {
callback('https://www.google.com/logos/google.jpg', {
alt: 'My alt text'
});
}
/* Provide alternative source and posted for the media dialog */
if (meta.filetype === 'media') {
callback('movie.mp4', {
source2: 'alt.ogg',
poster: 'https://www.google.com/logos/google.jpg'
});
}
},
templates: [{
title: 'New Table',
description: 'creates a new table',
content: '<div class="mceTmpl"><table width="98%%" border="0" cellspacing="0" cellpadding="0"><tr><th scope="col"> </th><th scope="col"> </th></tr><tr><td> </td><td> </td></tr></table></div>'
},
{
title: 'Starting my story',
description: 'A cure for writers block',
content: 'Once upon a time...'
},
{
title: 'New list with dates',
description: 'New List with dates',
content: '<div class="mceTmpl"><span class="cdate">cdate</span><br /><span class="mdate">mdate</span><h2>My List</h2><ul><li></li><li></li></ul></div>'
}
],
template_cdate_format: '[Date Created (CDATE): %m/%d/%Y : %H:%M:%S]',
template_mdate_format: '[Date Modified (MDATE): %m/%d/%Y : %H:%M:%S]',
height: 600,
image_caption: true,
quickbars_selection_toolbar: 'bold italic | quicklink h2 h3 blockquote quickimage quicktable',
noneditable_noneditable_class: 'mceNonEditable',
toolbar_mode: 'sliding',
contextmenu: 'link image table',
skin: useDarkMode ? 'oxide-dark' : 'oxide',
content_css: '../static/assets/js/mycontent.css',
content_style: 'body { font-family: Malgun Gothic; font-size:13px}',
font_formats: 'Gothic=Malgun Gothic, sans-serif',
init_instance_callback: 'insert_contents'
});
The problem appears to be caused by this line:
init_instance_callback: 'insert_contents'
In there you're telling TinyMCE to call a function called insert_contents in the global scope when the editor initializes, however in the examples you've given that's never defined. This would then explain why the o variable is undefined as TinyMCE is unable to lookup that function and execute it as part of its initialization sequence.
I'm not sure what your expected behavior is there, but the simplest solution is likely going to be to just remove it.

Inserting custom element, attribute gets stripped

I made a tinymce fiddle about this problem: http://fiddle.tinymce.com/O0gaab
I add a custom element "custom-block" and a custom plugin to insert that element.
tinymce.PluginManager.add('custom', function(editor, url) {
editor.addButton('custom', {
text: 'CUSTOM',
onclick: function() {
// Open window
editor.windowManager.open({
title: 'Custom plugin',
body: [
{type: 'textbox', name: 'src', label: 'SRC'},
{type: 'label', name: 'title', text: 'Insert content bellow:'},
{type: 'textbox', name: 'content', multiline: true, style: 'width:500px;height:100px;'}
],
onsubmit: function(e) {
console.log(e.data);
editor.insertContent('<custom-block src="' + e.data.src + '">' + e.data.content + '</custom-block>');
}
});
}
});
});
tinymce.init({
selector: "textarea",
plugins: [
"advlist autolink lists link image charmap print preview anchor",
"searchreplace visualblocks code fullscreen",
"insertdatetime media table contextmenu paste custom"
],
toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | custom",
//valid_elements: "+*[*]", //when using this option trying to allow everything get an error "Cannot read property 'src' of undefined"
extend_valid_elements: "custom-block[src]",
custom_elements: "custom-block"
});
The element, get inserted correctly but without the src attribute.
From the documentation I though that extend_valid_elements: "custom-block[src]" would allow src attribute on a custom-block but it gets stripped everytime.
I also tried to set valid_elements to everything(+*[*]) just in case, but then gets worse because at inserting, I get an error: "Cannot read property 'src' of undefined".
I am making any mistake or what is the problem?
The name of the configuration option is extended_valid_elements so you simply named it wrong in your configuration. It should be:
extended_valid_elements: "custom-block[src]"
I have updated your fiddle (http://fiddle.tinymce.com/O0gaab/1) and things appear to work.

How to embed Instagram post using Tiny MCE editor

I'm copying instagram embed code in tinymce source code and i can see the block of instagram but couldn't able to see the image. Please help me fix this issue
The problem is that when you add the embed code to tinymce. The code gets rendered in an iframe and the source code does not execute. The best approach in this case is to add a popup to take the embed code, extract the src from it and append it to the head of the iframe. This way the source code will execute and you will get a rendered html.
var editor_id = "";
tinymce.PluginManager.add('instagram', function(editor, url) {
// Add a button that opens a window
editor.addButton('instagram', {
text: 'Instagram',
icon: false,
onclick: function() {
// Open window
editor.windowManager.open({
title: 'Instagram Embed',
body: [
{ type: 'textbox',
size: 40,
height: '100px',
name: 'instagram',
label: 'instagram'
}
],
onsubmit: function(e) {
// Insert content when the window form is submitted
var embedCode = e.data.instagram;
var script = embedCode.match(/<script.*<\/script>/)[0];
var scriptSrc = script.match(/".*\.js/)[0].split("\"")[1];
var sc = document.createElement("script");
sc.setAttribute("src", scriptSrc);
sc.setAttribute("type", "text/javascript");
var iframe = document.getElementById(editor_id + "_ifr");
var iframeHead = iframe.contentWindow.document.getElementsByTagName('head')[0];
tinyMCE.activeEditor.insertContent(e.data.instagram);
iframeHead.appendChild(sc);
// editor.insertContent('Title: ' + e.data.title);
}
});
}
});
});
tinymce.init({
selector:'textarea',
toolbar: 'bold italic | alignleft aligncenter alignright alignjustify | undo redo | link image media | code preview | fullscreen | instagram',
plugins: "wordcount fullscreen link image code preview media instagram",
menubar: "",
extended_valid_elements : "script[language|type|async|src|charset]",
setup: function (editor) {
console.log(editor);
editor.on('init', function (args) {
editor_id = args.target.id;
});
}
});
Refer the JSFiddle - https://jsfiddle.net/z3o2odhx/1/
You can add the embed html from the Instagram toolbar button and get the rendered html, but this also messes the source code. Hope this is helpful.
Alternatively, if you have the option of adjusting the html of the page, you can just add <script async defer src="//www.instagram.com/embed.js"></script> somewhere on that page, since it looks like TinyMCE is stripping the js include.
If you want to selectively include it, you can also use something like this in that page's js:
if($(".instagram-media").length) {
var tag = document.createElement('script');
tag.src = "//www.instagram.com/embed.js";
tag.defer = true;
tag.async = true;
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
}
TinyMCE changed a bit in the last version (v5) - here is updated version of working Ananth Pais' solution:
tinymce.PluginManager.add('instagram', function(editor, url) {
editor.ui.registry.addButton('instagram', {
text: 'Instagram',
onAction: function() {
editor.windowManager.open({
title: 'Instagram Embed',
body: {
type: 'panel',
items: [
{
type: 'textarea',
height: '300px',
name: 'instagram',
label: 'Instagram embed code',
}
],
},
buttons: [
{
type: 'submit',
name: 'submitButton',
text: 'Embed',
disabled: false,
primary: true,
align: 'start',
}
],
onSubmit: function(e) {
var data = e.getData();
var embedCode = data.instagram;
var script = embedCode.match(/<script.*<\/script>/)[0];
var scriptSrc = script.match(/".*\.js/)[0].split("\"")[1];
var sc = document.createElement("script");
sc.setAttribute("src", scriptSrc);
sc.setAttribute("type", "text/javascript");
var iframe = document.getElementById(editor_id + "_ifr");
var iframeHead = iframe.contentWindow.document.getElementsByTagName('head')[0];
tinyMCE.activeEditor.insertContent(data.instagram);
iframeHead.appendChild(sc);
e.close();
}
});
}
});
});
tinymce.init({
selector:'textarea',
toolbar: 'bold italic | alignleft aligncenter alignright alignjustify | undo redo | link image media | code preview | fullscreen | instagram',
plugins: "wordcount fullscreen link image code preview media instagram",
menubar: "",
extended_valid_elements : "script[language|type|async|src|charset]",
setup: function (editor) {
console.log(editor);
editor.on('init', function (args) {
editor_id = args.target.id;
});
}
});
https://www.tiny.cloud/docs/configure/content-filtering/#usingextended_valid_elementstoallowscriptelements
by default tinmye prevent script codes.
enable them on tinymce options
extended_valid_elements : 'script[src|async|defer|type|charset]'

remove the extra p tag in tinyMCE

When you copy and paste from a word document in to the tinyMCE editor sometimes there are unwanted <p> tags:
<p> </p>
<div class="starpasspro-example-question">
<p><strong>Example: Levels of strategy</strong></p>
<p>Microsoft is one of the world’s largest organisations, providing corporate solutions to businesses throughout the world to help them realise their fullest potential. At Microsoft, there are three levels of strategy as follows:</p>
</div>
<p> </p>
Here the code that generates I want to remove the <p> tags any way to do that ?
Add these Lines in your tinymce.init({ });
Example:
tinymce.init({
forced_root_block : "",
force_br_newlines : true,
force_p_newlines : false,
});
it will be helpful.
Add into your tinymce.yml file
forced_root_block : ""
force_br_newlines : true
force_p_newlines : false
Yes, this is possible. There is a secure way to remove all that html elements you want to removed (you may define what to keep). It is by using the tinymce config params paste_preprocess and a custom function strip_tags. Check it out here.
Add this to your functions.php file and the standard p-tags
tags will be removed by adding some parameters to the tiny_mce_before_init hook. If you want to see how it works, you can read further on this page: https://codex.wordpress.org/TinyMCE
////////////////////////////////////////////////////////////////////////
//////////REMOVE STANDARD <P> FROM TINYMCE EDITOR/////////////////////////
///////////////////////////////////////////////////////////////////////
function my_format_TinyMCE( $in ) {
$in['forced_root_block'] = "";
$in['force_br_newlines'] = TRUE;
$in['force_p_newlines'] = FALSE;
return $in;
}
add_filter( 'tiny_mce_before_init', 'my_format_TinyMCE' );
USE HtmlEncode="false" in BoundField
<asp:BoundField DataField="PostContent" HtmlEncode="false" />
Thanks to Prahalad Gaggar!
I had the same problem and I solved it by reading this topic: https://stackoverflow.com/a/22397116/14491024
here is my code with each time adding <p<br/<br/</p so annoying)
function HTMLeditor( parameters) {
$('#exampleModalCenter').modal('show');
tinymce.init({
height: 500,
selector: ".modal-body",
theme: 'modern',
plugins: [
'advlist autolink lists link image charmap print preview hr anchor pagebreak',
'searchreplace wordcount visualblocks visualchars code fullscreen',
'insertdatetime media nonbreaking save table contextmenu directionality',
'emoticons template paste textcolor colorpicker textpattern imagetools'
],
toolbar1: 'insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image',
toolbar2: 'print preview media | forecolor backcolor emoticons ',
image_advtab: true,
setup: function (editor) {
editor.on('init', function (e) {
editor.setContent(parameters);
});
}
});
}
And here is with problem SOLVED:
function HTMLeditor( parameters) {
$('#exampleModalCenter').modal('show');
tinymce.init({
height: 500,
selector: ".modal-body",
theme: 'modern',
plugins: [
'advlist autolink lists link image charmap print preview hr anchor pagebreak',
'searchreplace wordcount visualblocks visualchars code fullscreen',
'insertdatetime media nonbreaking save table contextmenu directionality',
'emoticons template paste textcolor colorpicker textpattern imagetools'
],
toolbar1: 'insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image',
toolbar2: 'print preview media | forecolor backcolor emoticons ',
image_advtab: true,
//remove <p><br /><br /></p>
forced_root_block: "" ,
force_br_newlines: true,
force_p_newlines: false,
setup: function (editor) {
editor.on('init', function (e) {
editor.setContent(parameters);
});
}
});
}

How should I update a TinyMCE plugin using tiny_mce_popup.js for version 4?

TinyMCE4 documentation is currently dismal. I have an insert image plugin compatible with Ruby on Rails but it relies on the deprecated tiny_mce_popup.js. There's no information for how I should update a plugin to circumvent use of that file.
TinyMCE 4 deprecates the old file_browser_callback in favor of the new file_picker_callback which has the advantage that it can return metadata.
tinymce.init({
selector: 'textarea.tinymce',
file_picker_callback: function (callback, value, meta) {
myFilePicker(callback, value, meta);
},
plugins: ['link image'],
toolbar: 'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image'
});
function myFilePicker(callback, value, meta) {
tinymce.activeEditor.windowManager.open({
title: 'File Manager',
url: '/Site/FileManager?type=' + meta.filetype,
width: 650,
height: 550,
}, {
oninsert: function (url) {
callback(url);
}
});
}
In your file browser to return the file to the main page you call mySubmit('/images/file_123.jpg') when you click on a hyperlink or a image.
function mySubmit(url) {
top.tinymce.activeEditor.windowManager.getParams().oninsert(url);
top.tinymce.activeEditor.windowManager.close();
}
TinyMCE 3 was reliant on tiny_mce_popup.js for exchanging variables between the parent and dialogue. TinyMCE 4 does away with dialog.htm and with tiny_mce_popup.js
If you have a look at the image plugin, following editor.windowManager.open you can see the dialogue is created through JS alone. This eliminates the need for goofy access to parent variables through opener. If you can, stick with this template method.
I chose to stick with dialog.htm but I served it from rails so I wouldn't have deal with exchanging the form auth_token with JS. If you do this, remember that inserting your content should come from the plugin and not from your dialogue. This is my simple image uploader :
tinymce.PluginManager.add('railsupload', function(editor, url) {
var win, data, dom = editor.dom
// Add a button that opens a window
editor.addButton('railsupload', {
icon: 'image',
tooltip: 'Insert image',
onclick: showDialog
});
function showDialog() {
win = editor.windowManager.open({
title: 'Insert image',
name: 'railsupload',
url: '/attachments/tinymce?owner_type=' + editor.settings.owner_type + '&owner_id=' + editor.settings.owner_id,
width: 200,
height: 220,
bodyType: 'tabpanel',
buttons: [{
text: 'Insert',
onclick: submitForm
}]
});
}
function submitForm() {
editor.insertContent("<img src=\"" + self.frames[1].document.img_url + "\" />")
win.close()
}
});
You'll need a rails attachments controller and you'll need to pass your attachment init params through the url. If I build this out in a gem, it will be compatible with tinymce-rails and I'll update this answer.
Update: TinyMCE now has this page on migrating from 3.x: http://www.tinymce.com/wiki.php/Tutorial:Migration_guide_from_3.x