How to add custom class in wagtail richtextfiled - javascript

how can i add button which adds class in hallo.js editor?
Here is my code, but it dont works, it ony register fuction in wagtai;s edit interface.
In the end I need to add any class to selection or current tag.
Mb I can see it in html in someway and add classes manually?
(function() {
(function(jQuery) {
return jQuery.widget('IKS.center', {
options: {
editable: null,
toolbar: null,
uuid: '',
buttonCssClass: 'center'
},
populateToolbar: function(toolbar) {
var buttonElement, buttonset;
buttonset = jQuery('<span class="' + this.widgetName + '"></span>');
buttonElement = jQuery('<span></span>');
buttonElement.hallobutton({
uuid: this.options.uuid,
editable: this.options.editable,
label: 'Center element',
command: 'addClass("center")',
icon: 'icon-horizontalrule',
cssClass: this.options.buttonCssClass
});
buttonset.append(buttonElement);
buttonset.hallobuttonset();
return toolbar.append(buttonset);
}
});
})(jQuery);
}).call(this);

As mentioned in the Wagtail customisation docs, you need to call registerHalloPlugin. You'll also need to configure the whitelist to allow your <span> element - rich text fields intentionally don't allow inserting arbitrary HTML. (See https://stackoverflow.com/a/38097833/1853523 for more detail.)
However, I would strongly encourage using StreamField for this, rather than extending the rich text editor. The whole purpose of Wagtail is to keep a separation between the information content of pages, and its presentation. A button to say "center this text" is purely presentation - that's a detail that belongs in template code, not in your article content. Instead, you should ask: what is the purpose of this text? Is it a block-quote, a testimonial, an advert? Create block types for that, and then think about how to style them in the template. You'll have much more control over the presentation that way.
(further reading: Rich text fields and faster horses)

Related

ElectronJS - Make bold text in Teaxtarea with local keyboard shortcut

I'm trying to make a text editor in ElectronJS as my first application in Electron.
I got the Textarea and the shortcut setup as it should be, but I cant get the selected text in the textarea to get bold, when pressing CTRL+B. I could use some help in the right direction on how this is done.
I have tried to use the execCommand so far to toggle the bold, but are getting the error:
Document is not defined
Main.js
menu.append(new MenuItem({
label: 'Shortcuts',
submenu: [{
role: 'Bold',
accelerator: process.platform === 'darwin' ? 'Cmd+B' : 'Ctrl+B',
click: () => {
document.execCommand('bold');
}
}]
}))
Menu.setApplicationMenu(menu)
I have also tried this in renderer.js, but not working aswell.
What am I doing wrong?
This actually answered the question: https://stackoverflow.com/a/12831155/652535
Content from link:
If you need to customize your textarea, reproduce its behavior using another element (like a DIV) with the contenteditable attribute.
It's more customizable, and a more modern approach, textarea is just for plain text content, not for rich content.
<div id='fake_textarea' contenteditable></div>
The scrollbars can be reproduced with the CSS overflow property.
You can use this fake textarea in a form normally, i.e: if you have to submit its content through POST method, you could do something like(with jQuery):
<input type='hidden' id='fake_textarea_content' name='foobar' />
...
$('#your_form').submit(function()
{
$('#fake_textarea_content').val($('#fake_textarea').html());
});

Copy content from WYSIWYG editor to textarea

I try to copy the content from a WYSIWYG-Editor (What you see is what you get), to a textarea and It needs to be structured. I am using Quill https://www.quilljs.com/docs/quickstart
I added a textarea, the content from the Editor is pasted into it, on click at the button Edit HTML.
$("#editHTML").click(function() {
var content = $("#editor .ql-editor").html();
$("#htmlEditForm").html(content);
});
It works, but as you can see it is not structured at all. Everything is in one line:
I need it like this:
<p>FOO</p>
<p>BAR</p>
<p class="ql-align-center"><span style="color: rgb(230, 0, 0);">I am centered</span></p>
I need it to be in a textarea, because I want to use it as HTML editor. Later on I will add another button which pastes the html code back into the WYSIWYG-editor.
The goal is, to give advanced users the possibilty to edit the HTML.
I am not sure how I can do this, I was thinking of replacing any closing tags with this: e.g. </p> -> </p>\n but it just outputs </p>\n in the textarea instead of creating a new line.
Is this even possible?
UPDATE:
I tried to use getContents as suggested:
var toolbarOptions = [
['image'],
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
['bold', 'italic', 'underline', 'strike'], // toggled buttons
[{ 'color': [] }, { 'background': [] }], // dropdown with defaults from theme
[{ 'align': [] }],
['blockquote', 'code-block'],
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
[{ 'script': 'sub'}, { 'script': 'super' }], // superscript/subscript
[{ 'direction': 'rtl' }], // text direction
[{ 'font': [ 'arial', 'Monotype Corsiva', 'fantasy', 'serif' ] }],
['clean'] // remove formatting button
];
var quill = new Quill('#editor', {
theme: 'snow',
modules: {
toolbar: toolbarOptions
}
});
$("#editHTML").click(function() {
$("#htmlEditForm").html(quill.getContents());
});
But the textarea stays empty.
quill.getContents() does just return an object:
Unfortunately Quill is not meant to do this. Quill uses it's own document model called Parchment. It tracks everything that changes in the editor so that it can understand it's contents and make changes, but it has some limits on what can be added. For example, you can't add an element that doesn't match an existing blot/format.
If someone were to edit the HTML arbitrarily and add a div for example, the editor's document model would no longer match it's contents. When switching back to the wysiwyg view, some functions wouldn't work until it tries to recreate it's document model and messes up the changes made in the HTML view.
Edit
Also, for the original problem of how to format the HTML with new lines and indention, what your seeing is how it's formatted. The editor is not breaking to new lines for each block element or adding indentation for nesting as someone would when they're writing code.
If you want it to be formatted in some way, you'd have to do it manually. Either by parsing the getContents() output or by writing or finding a script that can format HTML.
You can combine your text area with Ace Editor (https://ace.c9.io/) which allows code editing features.
Instead of detecting .click event use the built-in quill mechanism 'text-change' ... also to get the HTML of the rich text editor all in one go use .container.childNodes[0].innerHTML
quill.on('text-change', function (delta, oldDelta, source) {
// your code here e.g.
$("#htmlEditForm").html(quill.container.childNodes[0].innerHTML);
});
Text area is not capable of showing you the formats of rich text editors. A simple solution can be strip all tags if you do not want the tags in text area. Or if you want tags like bold, italic etc you have to use WYSIWYG editor. You can disable all tools of the editor and it would be looking like a textarea.

jQueryUI dialog hides close (X) button if title contains right-aligned text. How can I fix this?

I have the following code sample:
<div id="dlgTest1" style="display: none">
<p>
Lorem ipsum test popup dialog<br />
<br />
Click the OK button to resume activity.
</p>
</div>
<script type="text/javascript">
$(document).ready(function () {
var myTitle =
"<div style='float: left'>LEFT TEXT</div><div style='float: right'>RIGHT TEXT</div>";
$("#dlgTest1").dialog({
title: '', // new jquery-ui-1.10.2 doesn't allow HTML here
autoOpen: false,
minWidth: 500, width: 500,
minHeight: 200, height: 200,
resizable: true,
modal: true,
show: "blind",
hide: "explode",
buttons: {
"OK": function () {
$(this).dialog("close");
}
}
}).siblings('.ui-dialog-titlebar').html(myTitle); // title goes here
$("#dlgTest1").dialog("open").dialog("moveToTop");
});
</script>
This sample code opens a jQuery dialog, which displays text in the title bar on the left side and on the right side, which is achieved by myTitle, a variable that contains HTML code with two <div> elements.
In previous versions of jQueryUI (e.g. V1.8.20) I was able to directly assign the title attribute with the content of myTitle and it worked like a charm. Now I've upgraded to V1.10.2 and noticed that title doesn't interpret HTML any more but shows the <div> ... in the title instead.
As you can see, I am using the tweak .siblings('.ui-dialog-titlebar').html(myTitle); to pass through my HTML code, which works basically.
But it has the side effect that it hides the (X) button used to close the dialog (that side effect didn't occur in the older jQUery UI version V1.8.20). I can't go back to the earlier version of jQueryUI because I need controls that depend on the latest version.
Question: How can I avoid that the (X) close button is hidden by the right-aligned text?
Note: The icon is displayed, if I use normal text instead of HTML formatted text, so the reason is not that a style or bitmap is missing.
I have already looked everywhere (beginning with http://jqueryui.com/), searched in Google etc. but found no solution.
Any help is much appreciated.
Update: (Answers to the comments below)
I am using the ui-lightness css theme without any modifications
Adding padding-right:20px to the style attribute of the right-aligned div doesn't solve the issue.
Final note:
The discussion with you all helped me a lot solving the issue. I have posted the answer which finally worked for me.
Thank you very much to all who have supported me, you guys are really great! Everyone who has posted an answer got a +1 from me.
The problem is you are replacing the html of the title bar so you are removing the close button.
Change html() to append(), you will need to adjust margins/padding to get it to look right.
var myTitle =
"<div style='float: left'>LEFT TEXT</div><div style='float: right; margin-right:10px;'>RIGHT TEXT</div>";
and
}).siblings('.ui-dialog-titlebar').append(myTitle);
JSFiddle
The approach Epascarello has suggested with .append(myTitle) instead of .html(myTitle) works fine in the JSFiddle example, but unfortunately not with the ui-lightness customization of jQueryUI which I am using.
But the following function does it:
// Insert HTML formatted title in jQuery dialog V1.10.2
// Parameters:
// dialog = reference to jQueryUI dialog,
// hmtlTitle = the title with containing HTML to apply to dialog
function applyHtmlToDialog(dialog, htmlTitle) {
dialog.data("uiDialog")._title = function (title) {
title.html(this.options.title); };
dialog.dialog('option', 'title', htmlTitle);
}
To use it, simply call it the following way:
applyHtmlToDialog($("#dlgTest1").dialog(), myTitle);
or if you prefer to do it in multiple lines:
var myDiag = $("#dlgTest1").dialog(//... your dialog definition ...
);
var myTitle = '<div> ... </div>';
applyHtmlToDialog(myDiag, myTitle);
It will insert myTitle properly and regard the HTML codes contained within.
N.B. (Some background information): This solution was inspired by the following Stackoverflow article:
Icons in jQueryUI dialog title.
I've found out the reason for this behaviour is that the jQuery team had concerns regarding XSS vulnerability of the dialog, so they have changed it on purpose.
You can read more about their discussion here, if you're interested. Hence, the _title is official functionality provided by jQuery in case you want to use HTML codes in the title.
I believe they have chosen an approach which isn't so straightforward, as .dialog({ title: $("... html ...") }), which I personally would have preferred (i.e. using $ to indicate that you want to have HTML in the title).

Dojo: Can I add two or more widgets to the same BorderContainer region?

Please bear with me as I am new to dojo, javascript, and web programming in general.
In a part of the web page I am working on, I have a BorderContainer (inside of a TabContainer, which is inside of another BorderContainer, but I don't think this is relevant to the issue) and I want to put a number of widgets inside it.
I am using the "headline" design on the BorderContainer and - ideally - I would like to have a ContentPane in the center region and a TextBox and four buttons all in the bottom region (lined up side-by-side across the width of the border container).
This could be a very basic idea that I am missing behind BorderContainers or widgets in general (or even in basic paradigms of web programming!), but I am having trouble getting the TextBox and four buttons to line up side-by-side as I would like.
Is it possible for me to put different widgets in the same region without creating another BorderContainer (or other Container or Pane for that matter) just for that region? If so, how would I implement this?
Here are some snippets of creating the BorderContainer and it's future components.
//Main BorderContainer
this.container = new dijit.layout.BorderContainer({
title: that.name + "_CTR",
style: "height: 100%",
design: "headline"
}, that.name + "_CTR");
//Blank ContentPane in the center region
this.msgArea = new dijit.layout.ContentPane({
content: "",
region: "center"
}, that.name + "_MSGS");
//TextBox to be placed in the "bottom" region
this.textBox = new dijit.form.TextBox({
value: "",
placeHolder: "Type a message to publish",
region: "bottom"
}, that.name + "_TB");
//Example of one of my four buttons also to be placed in the "bottom" region
this.pubButton = new dijit.form.Button({
region: "bottom",
label: "Publish",
showLabel: true,
onClick: function () { that.publish(); }
}, that.name + "_PB");
//Function that adds children to the main BorderContainer and calls startup()
this.initialize = function () {
that.container.addChild(that.msgArea);
that.container.addChild(that.textBox);
that.container.addChild(that.pubButton);
that.container.addChild(that.pubButton2);
that.container.addChild(that.pubButton3);
that.container.addChild(that.pubButton4);
that.container.startup();
};
Thank you for your time!
EDIT: Forgot to mention that I would prefer doing this programmatically if possible
EDIT2: Big thanks to Craig below! While I didn't use your exact methods, playing around with dojo.create (haven't converted to 1.7 yet...) helped me learn more about DomNodes (like I said, I'm new to web programming :P), which allowed me to figure out that more than one widget could take the place of the ContentPane's "content" property simply by setting it to an array of domNode references for each widget!
//Create BorderContainer and Buttons as above
//Create the ContentPane for the bottom region of the BorderContainer
this.pane = new dijit.layout.ContentPane({
content: "",
region: "bottom"
}, that.name + "_BTM");
//Add each widget to the ContentPane's "content" by using a DomNodeList
//Then add the ContentPane to the BorderContainer
this.initialize = function () {
that.pane.set("content", [
that.textBox.domNode,
that.button1.domNode,
that.button2.domNode,
that.button3.domNode,
that.button4.domNode
]);
that.container.addChild(that.pane);
that.container.startup();
};
This quick and dirty method might not be best for markup/layout, in which case I think your methods and/or editing of the "innerHTML" might work out better, but this is what I needed at the moment.
Many thanks once again, wish I was able to upvote/give reputation....
Yes you can put multiple widgets in the same region, except for the center region. Notice that the first widget added is farthest most in the direction specified by the region. The first top widget is on top. The first bottom widget is on the bottom, and so on.
http://jsfiddle.net/cswing/ssDXh/
Looking at your example, I would recommend putting the textbox and button into it's own content pane and then putting the pane into the bordercontainer. The bordercontainer will adjust the sizes of the regions based on screen size, and you don't want the button and textbox changing size.
EDIT:
There are two techniques you can consider to accomplish what was asked in the comment.
1) You can use dom-construct to manually build the html and then instantiate a widget using a newly created domNode.
var b1 = new ContentPane({});
var div1 = domConstruct.create('div', {}, b1.containerNode);
// build more html using domConstruct, like a table etc
var btn = new Button({ label: 'My Action 1'},
domConstruct.create('div', {}, div1));
2) You can create a templated widget that has the html markup and the widget is then responsible for instantiating the text box and button.
dojo.declare("MyFormLayout", [dijit._Widget, dijit._Templated], {
//put better html layout in the template
templateString: '<div><div dojoAttachPoint="btnNode"></div></div>',
postCreate: function() {
this.inherited(arguments);
this.button = new Button({ label: 'My Action 2'}, this.btnNode);
}
});
appLayout.addChild(new ContentPane({
region: "bottom",
content: new MyFormLayout({})
}));
The first option is convenient if the markup is simple and strictly for layout.
The second option works well when there is other logic that is coded for the widgets. That code can go in the custom widget.

Is there any way of having TinyMCE options selected by default?

Say I want to have the 'bold' option selected by default when the editor is initialized. How would I do that?
EDIT: A solution working with what Thariama answered is the following. I discovered that having the <p> tag left in there screwed with selecting other options, like with different headlines, and as I have no need for a <p> tag in this particular editor I did this in order to get <h1> by default:
setup: function(ed) {
ed.onInit.add(function(ed) {
if ($('p', ed.getDoc()).length > 0) {
$('p', ed.getDoc()).remove();
$('<h1><br mce_bogus="1" /></h1>').appendTo($('body', ed.getDoc()));
}
});
}
This works great even when the user jumps between selection options in a select.
EDIT2: This seems to work if you do need the <p> tag. This makes it bold by default:
setup: function(ed) {
ed.onInit.add(function(ed) {
if ($('p', ed.getDoc()).children().length == 1 && $('p', ed.getDoc()).children(':first').is('br')) {
$('p', ed.getDoc()).html('<b><br mce_bogus="1" /></b>');
}
});
}
Rather than doing it when the user enters it, why not output the values inside a bold tag when you output the data to the user outside of tinyMCE? That way, the user has no ability to override the option as they would if you simply had a tag inside.
As an aside, if you're using TinyMCE for a wysiwg web editor in an environment where users first generate code in Microsoft Word, there are significant issues with getting "junk" word code in via paste. Tiny's solution is a "paste from word" button, which users seem to often ignore -- I've deployed Tiny in 50+ business websites, and it's been a major issue with a majority of the clients using those sites. I switched over to CKEditor, which does word code-stripping on the FRONT end, and all's been well. People have their preferences, and I even liked using Tiny more. But Word Code issues have become a dealbreaker for me and I won't install Tiny any more.
The procedure of choice depends on many factors. If you have an empty tinymce editor you can initialize your editor with the following content
<p><strong><strong></p>
Please update your question regarding your use case.
EDIT: This might work better for you. Add this handler to one of your own plugins.
It checks for all paragraphs (if your tinymce uses divs you need to alter this piece of code)
and wraps the inner HTML into b-tags (you might want to use strong-tags).
ed.onInit.add(function(ed){
ps = ed.getDoc().getElementsByTagName('p');
for (var i=0; i < ps.length ; i++) {
ps[i].innerHTML = '<b>'+ps[i].innerHTML+'</b>';
}
});
Another option you have is to not use an own plugin, but to use the setup parameter when initializing tinymce:
setup : function(ed) {
ed.onInit.add(function(ed){
ps = ed.getDoc().getElementsByTagName('p');
for (var i=0; i < ps.length ; i++) {
ps[i].innerHTML = '<b>'+ps[i].innerHTML+'</b>';
}
});
},

Categories