Multiple Instanced CKEditor & CKEDITOR.stylesSet.add - javascript

I'm using CKeditor V3.x and I have multiple instances on one page. I had to add custom styles for a client so I used the CKEDITOR.stylesSet.add like this (as mentioned in the documentation http://docs.cksource.com/CKEditor_3.x/Developers_Guide/Styles):
CKEDITOR.stylesSet.add( 'mystyles',
[
{ name : 'Quote onderschrift', element : 'p', attributes : { 'class' : 'quote_sub' } },
{ name : 'Inleiding', element : 'p', attributes : { 'class' : 'inleiding' } }
]);
config.stylesSet = 'mystyles';
This does work when only having one editor on a page. When I have more editors on a single page I get the following error (via Firebug):
uncaught exception: [CKEDITOR.resourceManager.add] The resource name "mystyles" is already registered.
The result is I'm not getting any editors at all. While this error may seems logic I would like to solve it in a good way. For the time being I managed to solve it following this thread Adding custom styles to CKEditor, so I solved the addition of custom styles on the page itself rather that in the config like this:
CKEDITOR.replace('post_description', {
stylesSet :
[
{ name : 'Quote onderschrift', element : 'p', attributes : { 'class' : 'quote_sub' } },
{ name : 'Inleiding', element : 'p', attributes : { 'class' : 'inleiding' } }
]
});:
As said, this works for forces me to use this fix on all pages where I want the custom styles. Is there a better solution on how to fix this? I'd prefer to have the styled added in the main config file.

I was getting the same bug with only a single editor on the page. I fixed it by setting stylesSet in the main config to be the styles definition I wanted.
config.stylesSet = [
{
name: 'Blue Title',
element: 'h3',
attributes: { 'class': 'blue-title' },
styles: { color: 'blue' }
}, {
name: 'Red Text',
element: 'p',
attributes: { 'class': 'red-text' },
styles: { color: 'red' }
}
];
It's not the perfect solution, but it gets the job done.

Related

How to set or override target= "_blank" in anchor tag in 'sanitize-html' ?

I am using sanitize-html in my project. Say suppose I get a mail which has anchor tag, something like this:
this is to test something open google
This mail appears in my mail box like this :
this is to test something open google
Which opens google.com in the same tab. But I need to open in new tab.
Here is my code.
__html = sanitizeHTML(children, {
allowedTags: sanitizeHTML.defaults.allowedTags.concat([ 'img' ]),
allowedAttributes: {
'*': [ 'href', 'align', 'alt', 'center', 'bgcolor', 'style', 'width' ],
'img': ['src'],
'a' : ['target']
},
}
Here I want to set or override target = "_blank" . How to achieve that in sanitize-html ?
Unfortunately I could not find a tag for sanitize-html.
According to READ.MD/doc and your problem description, something like:
__html = sanitizeHTML(children, {
...,
transformTags: {
'a': function(tagName, attribs) {// simpleTransform also possible...
return {
tagName: 'a',//tagName
attribs: {
target: '_blank',
href: '*'
}
};
}
...should do it.
EDIT:
A better solution preserving the current (allowed!) attributes:
....
transformTags: {
'a': sanitizeHtml.simpleTransform('a', {target: '_blank'})
}

saveSnapshot does not work after modifying the DOM

I have a web site with inline CKEditors, on which I modify the HTML DOM with javascript and then try to fire the saveSnapshot event to save the modifications to the server. And here's the tricky part; if I make only one DOM change (remove an element) and call editor.fire("saveSnapshot"); it seems to work fine, but if I remove more than one element from the DOM I very often get the error below.
I have tried many different variations on doing what I need to do but with no success, except fire the event editor.fire("change") instead (which seems to be not recommended for some reason by CK). I have tried to delay with timeouts if things are happening to fast for the CK, using different combinations of lock/unlock snapshot events etc.
So, my questions are:
1) what is the reason using fire("change) is not recommended?
2) why is this behaviour causing CK to err?
Any clue that can take me forward on this issue is greatly appreciated.
Error message from CK:
ckeditor.js:149 Uncaught TypeError: Cannot read property 'type' of null
at a (ckeditor.js:149)
at CKEDITOR.dom.range.createBookmark2 (ckeditor.js:151)
at Array.createBookmarks2 (ckeditor.js:510)
at CKEDITOR.dom.selection.createBookmarks2 (ckeditor.js:463)
at CKEDITOR.plugins.undo.Image (ckeditor.js:1072)
at CKEDITOR.plugins.undo.UndoManager.save (ckeditor.js:1067)
at a.<anonymous> (ckeditor.js:1063)
at a.n (ckeditor.js:10)
at a.CKEDITOR.event.CKEDITOR.event.fire (ckeditor.js:12)
at a.CKEDITOR.editor.CKEDITOR.editor.fire (ckeditor.js:13)
Here's the code where I modify the DOM (remove one or several elements) and fire the saveSnapshot event for each affected editor instance:
var sectionIds = [];
if (textMarkers instanceof Array) {
for (var i = 0; i < textMarkers.length; i++) {
var textMarker = textMarkers[i];
$("#sectionText-" + textMarker.sectionId).find("span.hypo-marker[data-marker-id='" + textMarker.id + "']").remove();
if (sectionIds.indexOf(textMarker.sectionId) === -1) {
sectionIds.push(textMarker.sectionId);
}
}
} else {
$("#sectionText-" + textMarkers.sectionId).find("span.hypo-marker[data-marker-id='" + textMarkers.id + "']").remove();
sectionIds.push(textMarkers.sectionId);
}
// Trigger save event on editor for each section that's been updated
for (var c = 0; c < sectionIds.length; c++) {
var editor = CKEDITOR.instances["sectionText-" + sectionIds[c]];
// editor.fire("saveSnapshot");
editor.fire("change");
}
I use CKEditor standard 4.7 with Chrome Version 60.0.3112.113 (Official Build) (64-bit) with the following CKEditor config:
var options = {
removePlugins: 'magicline,horizontalrule',
skin: 'office2013',
extraPlugins: 'colorbutton,panelbutton,panel,floatpanel,button,scayt,html5audio,uploadwidget,youtube,image2,uploadimage,keystrokes,notification,notificationaggregator,magicline2,undo',
language: 'sv',
title: false,
entities: false,
basicEntities: false,
extraAllowedContent: 'span(*)[*]; img(*)[!src,*]; div(*)[*]; a(*)[*]; p(*)[*]; i(*); table(*)[*]; thead(*)[*]; tbody(*)[*]; tr(*)[*]; td(*)[*];',
// disallowedContent: 'span(rangySelectionBoundary)[id];',
imageUploadUrl: '/api/v2/editor/images',
//filebrowserUploadUrl: '/api/v2/editor/images',
toolbarGroups: [
{ name: 'clipboard', groups: [ 'clipboard', 'undo' ] },
{ name: 'editing', groups: [ 'find', 'selection', 'spellchecker' ] },
{ name: 'links' },
{ name: 'insert' },
'/',
{ name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] },
{ name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ] },
{ name: 'styles' },
{ name: 'colors' }
],
removeButtons: "Anchor",
magicline_color: "#00a651",
coreStyles_italic: { element: 'i', overrides: 'em' }
};
I can answer your first question:
It is not recommended to catch every change event because it gets fired quite often and it may prove quite heavy however firing should not be a problem.
If you intend to catch it maybe you could add extra data to that event based on which you can identify yours - https://docs.ckeditor.com/#!/api/CKEDITOR.event-method-fire.
The change event also doesn't get fired in source mode so if you use that mode, you need to find some other way of checking for code changes (maybe periodically comparing previous and current textarea value).

TypeError: c[a] is undefined in CKEditor

I am loading ckeditor.js file using $.getScript and in callback I am initiating CKEditor. But it is showing an error TypeError: c[a] is undefined. Here is my code. How can I solve this issue?
$.getScript("ckeditor.js", function (data, textStatus, jqxhr) {
if (textStatus == 'success' && jqxhr.status == 200) {
CKEDITOR.replace( 'commentBox',
{
toolbar :
[
{ name: 'basicstyles', items : [ 'Bold','Italic','Underline','Strike','Subscript','Superscript','-','RemoveFormat' ] },
{ name: 'paragraph', items : [ 'NumberedList','BulletedList','-','Blockquote'] },
{ name: 'insert', items : [ 'Table','HorizontalRule','SpecialChar' ] },
{ name: 'styles', items : [ 'Styles','Format','Font','FontSize' ] },
{ name: 'colors', items : [ 'TextColor','BGColor' ] }
]
});
}
});
I was getting the same error in similar circumstances.
I checked the formatted source in Chrome and discovered that this was being caused by the Format plugin trying to load its labels from the CKEDITOR.language object.
Turns out I didn't have en-gb included in my build and apparently it won't automatically fall back to straight en. Adding English (United Kingdom) to the build corrected the issues.
Re. https://stackoverflow.com/a/50719171/6462713
I had same issue. I have also loaded all supported languages in "/lang" folder. Basically my issue was - CKEditor isn't identifying properly its own folder path. So I set a CKEDITOR_BASEPATH variable before loading CKEditor.
It's briefly said here: (but there might be other places where it's explained better.) http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.html#.basePath
Therefore implementation will be like this:
<script>
window.CKEDITOR_BASEPATH = 'http://example.com/path/to/libs/ckeditor/';
</script>
In my case i used window.CKEDITOR_BASEPATH = '/app/storereport/ckeditor/';
Then load the main ckeditor.js script. Hope this may help you.
<script type="application/javascript"/>
$(document).ready(function (){
CKEDITOR.replace( 'product_content' ); // ID of element
});
</script>

Vue.js - recursive call of the component through render function. (runtime only)

I have one big request for you. I am a high-school student and I want to create an app for students with my friend. In the begining we wanted to use React for our reactive components, but then we saw Vue and it looked really good. But because of the fact, that we already have a big part of the app written in twig, we didn't want to use Vue.js standalone, because we would have to change a lot of our code, especially my friend, which is writing backend in Sympfony. So we use the runtime only version, which does not have a template option, so i have to write render functions for our components. And i am stucked with one particular problem.
I am writing a file-manager, and i need to render layer for every folder. Code is better then million words, so, take a look please :
var data = {
name: 'My Tree',
children: [
{
name: 'hello',
isFolder: false,
},
{
name: 'works',
isFolder: true,
children: [
{
name: 'child2',
isFolder: true,
},
{
name: 'child3',
isFolder: false,
},
]
}
]
}
Vue.component('layer', {
render: function renderChild (createElement) {
if(data.children.length){
return createElement('ul', data.children.map(function(child){
return createElement('li', {
'class' : {
isFolder: child.isFolder,
isFile: !child.isFolder
},
attrs: {
id: "baa"
},
domProps: {
innerHTML: child.name,
},
on:{
click: function(){
console.log("yes");
},
dblclick: function(){
console.log("doubleclicked");
if(child.children.length){
// if this has children array, create whole "layer" component again.
}
}
}}
)
}))
}
},
props: {
level: {
type: Number,
required: true
},
name: {
type: String,
}
}
})
new Vue({
el: '#fileManagerContainer',
data: data,
render (h) {
return (
<layer level={1} name={"pseudo"}>
</layer>
)
}
})
My question is, how to write that recursive call, which will render the whole Layer component on the doubleclick event, if the element has children array.
Thank you in advance for any reactions, suggestions or answers :)
I know this is a very old question, so my answer won't be useful to the OP, but I wanted to drop in to answer because I found myself with the very same problem yesterday.
The answer to writing these recursive render functions is to not try to recurse the render function itself at all.
For my example, I had a set of structured text (ish) - An array of objects which represent content - which can be nested, like so:
[
// each array item (object) maps to an html tag
{
tag: 'h3',
classes: 'w-full md:w-4/5 lg:w-full xl:w-3/4 mx-auto font-black text-2xl lg:text-3xl',
content: 'This is a paragraph of text'
},
{
tag: 'img',
classes: 'w-2/3 md:w-1/2 xl:w-2/5 block mx-auto mt-8',
attrs: {
src: `${process.env.GLOBAL_CSS_URI}imgsrc.svg`,
alt: 'image'
}
},
{
tag: 'p',
classes: 'mt-8 text-xl w-4/5 mx-auto',
content: [
{
tag: 'strong',
content: 'This is a nested <strong> tag'
},
{
content: ' '
},
{
tag: 'a',
classes: 'underline ml-2',
content: 'This is a link within a <p> tag',
attrs: {
href: '#'
}
},
{
content: '.'
}
]
}
]
Note the nested elements - These would need recursion to render properly.
The solution was to move the actual rendering work out to a method as follows:
export default {
name: 'block',
props: {
block: {
type: Object,
required: true
}
},
methods: {
renderBlock (h, block) {
// handle plain text without a tag
if (!block.tag) return this._v(block.content || '')
if (Array.isArray(block.content)) {
return h(block.tag, { class: block.classes }, block.content.map(childBlock => this.renderBlock(h, childBlock)))
}
// return an html tag with classes attached and content inside
return h(block.tag, { class: block.classes, attrs: block.attrs, on: block.on }, block.content)
}
},
render: function(h) {
return this.renderBlock(h, this.block)
}
}
So the render function calls the renderBlock method, and that renderBlock method is what calls itself over and over if it needs to - The recursion happens within the method call. You'll see that the method has a check to see whether the content property is of an Array type - At this point it performs the same render task, but rather than passing the content as-is, it passes it as an Array map, calling the same render method for each item in the array.
This means that, no matter how deeply the content is nested, it will keep calling itself until it has reached all the way to the "bottom" of the stack.
I hope this helps save someone some time in the future - I certainly wish I'd had an example like this yesterday - I would have saved myself a solid couple of hours!

Add another "Formats" select to TinyMce 4 in Wordpress

I'm using TinyMce Style Formats to add custom formats to the ""Formats" dropdown.
The problem is that I have too many styles to add, and I would like to use another "Formats" dropdown, separated from the first one. I know I can nest formats but it's not enough, I want to add two different Dropdown, how can I do it?
Have a look at the style plugin in tinymce3 (in tinymce4 it is part of the tinymce core). You may copy that plugin rename it and configure it to your needs. Then you need to add the plugin to your plugin list and the tinymce button to your button list.
This is best done adding formats to the formatter programmatically and then adding a menu item and trigger the formatter
editor.on( 'Init', function( e ) {
editor.formatter.register(
'page-title',
{ 'selector': 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li',wrapper: false, classes : ['giga', 'primary-font'] }
);
} );
editor.addButton( 'typography', {
text: 'Typography',
icon: false,
type: 'menubutton',
menu: [
{
text: 'Page Title',
menu: [
{
text: '(Giga) 88 Clan pro thin #000000',
onclick: function() {
editor.formatter.toggle( 'page-title' );
}
},
]
},
]
});

Categories

Resources