CKEditor Inline selection wrapping - javascript

I'm looking for a way to add a inline span element with attributes to a selection.
The hard part of this is getting it working with selections that pass over multiple block level elements.
I was looking in the sourcecode of the StyleCombobox and found this line.
var style = styles[ value ],
elementPath = editor.elementPath();
editor[ style.checkActive( elementPath ) ? 'removeStyle' : 'applyStyle' ]( style );
This way it already works on multiple block level elements.
The only thing is that i would like to apply attributes to the span that is made around the multiple selections for different block level elements instead of applying a style element.
Does anyone know how this can be done?

I used this as solution.
It is indeed possible to set attributes and element type.
this wasn't defined in the api. I found this in the CKEditor 3.0 api (older version)
var style = new CKEDITOR.style({attributes: {name:"changed"}});
editor.applyStyle(style);

The latest Solution for your Problem.
Get Selected Text:
editor.getSelection().getSelectedText();
Put tags and attributes
editor.applyStyle(new CKEDITOR.style({
element : 'span',
attributes : {'class':'YourClass','data-Otherattr':'otherattrvalue'},
style : {'background-color':'gray'}
});
);

Related

How to get html element after append with pure JavaScript?

I found some JQuery solutions, but I am limited by school task restrictions to use pure Javascript, and I need to use specific early appended element that is still not in DOM for replacing by my CKEDITOR.
Code:
function newOption(){
...
mainUL = document.getElementById("myUL");
var inputA = document.createElement("input");
inputA.type ="text";
inputA.style = "margin-right: 45px";
inputA.name = "option[]";
inputA.id = inputID;
mainUL.appendChild(inputA );
CKEDITOR.replace(inputID).setData('Type anything you want ...');
...
}
By replacing my input with CKEDITOR will JS fail, because input, commonly, is still not in DOM. I tried to use
mainUL.innerHTML += "all elements like html text";
and this is working and will immediately insert elements into DOM, but I can't to use innerHTML, because it will remove old listeners (for example checked checkboxes that JS will set from checked to unchecked, what is my main problem due to I have to try using append DOM function).
Try changing the code to wrap the call to CKEDITOR.replace in a setTimeout:
setTimeout(function() {
CKEDITOR.replace(inputID).setData('Type anything you want ...');
},0).
This will allow the browser time to insert the element before trying to replace it.
And I assume that inputID has a valid value in it...

Dynamic way to unbind dynamically binded XBL

I am applying a binding like this in a restartless add-on:
var css = '.findbar-container { -moz-binding:url("' + self.path.chrome + 'findbar.xml#matchword") }';
var cssEnc = encodeURIComponent(css);
var newURIParam = {
aURL: 'data:text/css,' + cssEnc,
aOriginCharset: null,
aBaseURI: null
}
cssUri = Services.io.newURI(newURIParam.aURL, newURIParam.aOriginCharset, newURIParam.aBaseURI);
myServices.sss.loadAndRegisterSheet(cssUri, myServices.sss.USER_SHEET);
findbar.xml contents are:
<?xml version="1.0"?>
<bindings xmlns="http://www.mozilla.org/xbl" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="matchword">
<content>
<children/>
<xul:toolbarbutton anonid="matchwordbtn" accesskey="w" class="tabbable" label="Whole Word Only" tooltiptext="Match only whole words" oncommand="console.log('hi')" type="checkbox"/>
</content>
</binding>
</bindings>
This just adds a button to the FindBar labeled "Whole Word Only". But now to remove it, I am just unregistering the stylesheet with myServices.sss.unregisterSheet(cssUri, myServices.sss.USER_SHEET);, however this is not unbinding it.
An answer on ask.mozilla.org told me this is expected behavior, but offered no solution.
I was thinking maybe I should dynamically add the binding rather than via CSS, I didn't test this but it doesn't fit the 3 reasons for XBL updates:
A bound element matches a style rule that specifies a different binding
The element is removed from the bound document
The element is destroyed (e.g., by closing the document)
The answer told me it's expected yet funky behavior.
Well, I just remembered that I have some working code that does (re)bind different XBL bindings, essentially.
It goes like this:
There is a base binding, or not (in your case the original binding of .findbar-container).
Then I have multiple classes that define different -moz-bindings.
These classes are set and removed at runtime.
Since that works for me, it should in theory work for you:
In your style, do not have a rule for the element itself, but for a class, e.g.
.findbar-container.myaddonclass { moz-binding: ... }
In your code, on load add that new class, e.g.
Array.forEach(
document.querySelectorAll(".findbar-container"),
e => e.classList.add("myaddonclass")
);
In your code, on unload remove the class again:
Array.forEach(
document.querySelectorAll(".findbar-container"),
e => e.classList.remove("myaddonclass")
);
This should force a CSS-rule reevaluation, and bindings reevaluation with that and hence fits the "A bound element matches a style rule that specifies a different binding" rule.
Of course, this sucks when not all elements you want to rebind are already present on load of your add-on, but MutationObserver could help with that...

CKEditor: Remove style properties from pasted content

I am trying to figure out a way to remove style properties from pasted HTML content into a CKEditor instance. I used the following to remove style attributes completely, but I actually want to keep the margin-left property.
CKEDITOR.on('instanceReady', function(ev) {
ev.editor.on('paste', function(evt) {
if (evt.data.type == 'html') {
evt.data.dataValue = evt.data.dataValue.replace(/ style=".*?"/g, '');
}
}, null, null, 9);
});
The issue is, sometimes margin-left is just switched to the margin shorthand and extra data that I do not want is added to that.
I am looking into jQuery and Javascript methods to try and accomplish this, but I haven't had any success yet.
You can apply properly configured Allowed Content Filter to the pasted data. See this answer to learn how to apply it to a string: Apply CKEditor Advanced Content Filter to a string
The only problem which you may have is that you cannot tell ACF to allow all elements and their attributes, you have to specify the elements. So the filter may look like this:
var filter = new CKEDITOR.filter(
'p h1 h2 h3 img strong em ul ol li[*](*){margin-left}'
);
It will allow all attributes and classes, but only margin-left on these elements.
EDIT
There's an easy way to list all elements:
var filter = new CKEDITOR.filter( {
'$1': {
// Use the hash containing all correct HTML elements
// (plus some more things, but we can ignore them in this case).
elements: CKEDITOR.dtd,
attributes: true,
classes: true,
styles: 'margin-left'
}
} );

Change an element itself with jquery or Raw javascript

is there any way we can change an element itself with jQuery or basic Javascript. for example : we have an Html Tag as <a href="mysite.com" title='titlevalue'>Link</a>, can we replace it like this <a href="mysite.com" data-title='titlevalue'>Link</a>.
Yeah, we can do add/remove title/data-title attribute however we have quite a lot of links so it would be better if we can have such sort of things. Searched on Google and here but didn't find anything like this, just changing the values of an attribute and add/remove of attributes.
Please Advice.
Thanks
If you want to add the data title attribute to each anchor tag, by using the value in the title attribute, you can do something like this:
$("a").each(function(){
$(this).attr("data-title", $(this).attr("title"));
$(this).removeAttr("title");
});
That will change title to data-title in all a-Tags:
$("a").each(function() {
$(this).attr("data-title", $(this).attr("title"));
$(this).data("title", $(this).attr("title"));
$(this).removeAttr("title");
});
http://jsfiddle.net/eC2cQ/
Hmm... without jQuery... (just because it's always nice to have that option)
var el = document.getElementsByTagName("a");
for(i=0; i < el.length; i++) {
el[i].setAttribute("data-title", el[i].getAttribute("title"));
el[i].removeAttribute("title");
}
Could be done like that too:
$('a').attr('data-title', function () {
return this.title
}).removeAttr('title');
Using jQuery, you could do the following:
// find each a tag with a title attribute
$( 'a[title]' ).each( function ( index, item ) {
// cache your jQuery object
var $item = $(item);
// replace and remove the attributes
$item.attr( 'data-title', $item.attr( 'title' ) );
$item.removeAttr( 'title' );
});
Some points of note:
if you want custom attributes on elements in new pages, you are
better off changing your back-end process to create these new
attributes. To expand on this: if you are hand-coding new pages,
simply write out the new attributes; if you are using a server-side
technology, use the templating system you're already using to write
out these new attributes.
your example is not good practice: title attributes are useful to
many people and machines. Replacing them with data-title prevents
a lot of standard tech from finding the information contained in them. There is extra
meaning implied by data-attributes, that they are extra,
application-specific information, and link titles are better used in
the generic way.
it's redundant: if you can read a
data-attribute, you can also read a normal attribute.
there will be other ways to do this without using jQuery, but it's a pretty typical library in use these days

setting color of a link in javascript

I want to set the color of "val" in the link in below code.
var link = $('' + val + '<br><br>');//this is the link
link.style.color="red";//this is how iam trying to set the color of "val"
SO HOW TO EXACTLY DO IT.
You can do this:
link.css({ color: 'red' });
But the correct and nice way would be:
$(".parent_element").prepend(''+val+'<br><br>');
$(".parent_element > a:first").css({ color: 'red' });
Try this:
$(link[0]).css({ color: 'red'});
The reason for this is that link is not an <a> element - it's a set of elements: <a>, <br> and another <br>.
Another approach would be:
link.css({ color: 'red' });
but this will set this CSS to not only <a>, but both <br>'s as well (not a big deal though).
If you are using jQuery(which it does seem like) go ahead with this,
jQuery
link.css("color","red");
Otherwise,
JavaScript
link[0].style.color = "red";
What you did doesn't work because link is an array. Before applying a style to it, you have to first select the first element by link[0] and then operate on it.
You could use link.style.color="red" if link was an HTMLElementNode, but it isn't. It might be a jQuery object, but if you are using an older version of the library then it will return undefined.
First you need to fix your jQuery call. You can't create multiple elements at the top level. (You can skip this bit if you are using a sufficiently new version of jQuery).
Since there is no good reason to use a double <br> (it shouts "Use CSS to add a margin instead"), I've taken them out:
var link = $('' + val + '');
Now you have a jQuery object so you can either use the jQuery method of setting CSS:
link.css("color", "red");
or get the HTMLElementNode from the jQuery object and use that:
link.get(0).style.color="red";
link.css("color", "red")
However, I think it would be better to create a css class for that and set up the color there. In Javascript/jQuery I would just add the class to the tag when needed. It is more elegant.

Categories