Find ckeditor instance - javascript

I want to access and manipulate the content of a textarea where ckeditor is used. My original code before I started using the editor was:
(function ($) {
"use strict";
$(document).ready(function () {
for(var i=0; i<=10; i++) {
$('#edit-button'+i).click(function(){
var tag = $(this).attr("value");
var id ="edit-body-und-0-value"; /* id of textarea */
var element = document.getElementById(id);
var start = element.selectionStart;
var end = element.selectionEnd;
var text = element.value;
var prefix = text.substring(0, start);
var selected = text.substring(start, end);
var suffix = text.substring(end);
selected = "["+tag+"]" + selected + "[/"+tag+"]";
element.value = prefix + selected + suffix;
element.selectionStart = start;
element.selectionEnd = start + selected.length;
return false;
});
}
});
})(jQuery);
This stops working when the editor is enabled.
I'm guessing that I need to use some different object then the 'element' object, the ckeditor object, and then I could maybe use the function described here: http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.editor.html
But how do I get the ckeditor object?
The ckeditor is added in drupal so I know very little about it and I am very unsure about how to access it or what information to look for in order to be able to know what to do.
On this page: http://ckeditor.com/blog/CKEditor_for_jQuery
$( 'textarea.editor' ).ckeditor();
is used to create the object(?). But I already have an ckeditor instance that I need to find. Can I some how select the editor for a given textarea?

Using the jquery adapter, you can get the ckeditor "object" like this:
$('textarea.editor').ckeditorGet()
So to destroy it, you'd do
$('textarea.editor').ckeditorGet().destroy()
This is using version 4.x of ckeditor.

Related

Any way to prevent losing focus when clicking an input text out of tinymce container?

I've made this tinymce fiddle to show what I say.
Highlight text in the editor, then click on the input text, highlight in tinyMCE is lost (obviously).
Now, I know it's not easy since both, the inline editor and the input text are in the same document, thus, the focus is only one. But is there any tinymce way to get like an "unfocused" highlight (gray color) whenever I click in an input text?
I'm saying this because I have a customized color picker, this color picker has an input where you can type in the HEX value, when clicking OK it would execCommand a color change on the selected text, but it looks ugly because the highlight is lost.
I don't want to use an iframe, I know that by using the non-inline editor (iframe) is one of the solutions, but for a few reasons, i can't use an iframe text editor.
Any suggestion here? Thanks.
P.S: Out of topic, does any of you guys know why I can't access to tinymce object in the tinyMCE Fiddle ? looks like the tinyMCE global var was overwritten by the tinymce select dom element of the page itself. I can't execute a tinyMCE command lol.
Another solution:
http://fiddle.tinymce.com/sBeaab/5
P.S: Out of topic, does any of you guys know why I can't access to
tinymce object in the tinyMCE Fiddle ? looks like the tinyMCE global
var was overwritten by the tinymce select dom element of the page
itself. I can't execute a tinyMCE command lol.
Well, you can access the tinyMCE variable and even execute commands.
this line is wrong
var colorHex = document.getElementById("colorHex")
colorHex contains input element, not value.
var colorHex = document.getElementById("colorHex").value
now it works ( neolist couldn't load, so I removed it )
http://fiddle.tinymce.com/DBeaab/1
I had to do something similar recently.
First off, you can't really have two different elements "selected" simultaneously. So in order to accomplish this you're going to need to mimic the browser's built-in 'selected text highlight'. To do this, you're going to have to insert spans into the text to simulate highlighting, and then capture the mousedown and mouseup events.
Here's a fiddle from StackOverflow user "fullpipe" which illustrates the technique I used.
http://jsfiddle.net/fullpipe/DpP7w/light/
$(document).ready(function() {
var keylist = "abcdefghijklmnopqrstuvwxyz123456789";
function randWord(length) {
var temp = '';
for (var i=0; i < length; i++)
temp += keylist.charAt(Math.floor(Math.random()*keylist.length));
return temp;
}
for(var i = 0; i < 500; i++) {
var len = Math.round(Math.random() * 5 + 3);
document.body.innerHTML += '<span id="'+ i +'">' + randWord(len) + '</span> ';
}
var start = null;
var end = null;
$('body').on('mousedown', function(event) {
start = null;
end = null;
$('span.s').removeClass('s');
start = $(event.target);
start.addClass('s');
});
$('body').on('mouseup', function(event) {
end = $(event.target);
end.addClass('s');
if(start && end) {
var between = getAllBetween(start,end);
for(var i=0, len=between.length; i<len;i++)
between[i].addClass('s');
alert('You select ' + (len) + ' words');
}
});
});
function getAllBetween(firstEl,lastEl) {
var firstIdx = $('span').index($(firstEl));
var lastIdx = $('span').index($(lastEl));
if(lastIdx == firstIdx)
return [$(firstEl)];
if(lastIdx > firstIdx) {
var firstElement = $(firstEl);
var lastElement = $(lastEl);
} else {
var lastElement = $(firstEl);
var firstElement = $(lastEl);
}
var collection = new Array();
collection.push(firstElement);
firstElement.nextAll().each(function(){
var siblingID = $(this).attr("id");
if (siblingID != $(lastElement).attr("id")) {
collection.push($(this));
} else {
return false;
}
});
collection.push(lastElement);
return collection;
}
As you can see in the fiddle, the gibberish text in the right pane stays highlighted regardless of focus elsewhere on the page.
At that point, you're going to have to apply your color changes to all matching spans.

(jquery) change nested same html tag to other bbcode tag

ok here is what i have:
<div id="mydiv">
<font color="green"><font size="3"><font face="helvetica">hello world</font></font></font>
</div>
I know the tags are strange, but that's what produced by the website.
So basically I want to change the font tag to bbcdoe tag, the jquery code I wrote:
$("#mydiv").find("font").text(function(){
var text = $(this).text();
var size = $(this).attr("size");
var color = $(this).attr("color");
var face = $(this).attr("face");;
if(size!=undefined){
return '[size="'+size+'"]'+text+'[/size]';
}
if(color!=undefined){
return '[color="'+color+'"]'+text+'[/color]';
}
if(face!=undefined){
return '[type="'+face+'"]'+text+'[/type]';
}
});
so what I got is only: [color="green"] hello world [/color]. always only the first tag. any idea?
ps: I tried each, replaceWith, html(), all the same result. only the first tag is change.
The reason it doesn't work is because when you call
$("#mydiv").find("font").text("New text")
For each font tag, starting from the first tag, it will replace the text within that tag.
Here is an example to show you what's going on.
Example | Code
$fonts = $("font","#mydiv");
console.log($fonts.text());
$fonts.text(function(){
return "New text";
});
console.log($fonts.text());
Here is an example of how you could do it instead
Example | Code
jQuery.fn.reverse = [].reverse;
var attributes= ["size", "color", "face"];
var text = $.trim($("#mydiv").text());
$("font","#mydiv").reverse().each(function(i, e) {
for (var i = 0; i < attributes.length; ++i){
var attr = $(e).attr(attributes[i]);
if( typeof attr != "undefined")
text = "["+attributes[i]+"="+attr+"]"+text+"[/"+attributes[i]+"]";
}
});
$("#mydiv").text(text);
A room full of sad, wailing kittens wishes that you'd get rid of those <font> tags, but you could probably make it work by explicitly working your way down through the nested tags.
It does what it does now because the outer call to .text() runs for the very first <font> tag, and it obliterates the other tags.
edit — to clarify, when you call
$('#mydiv').find('font')
jQuery will find 3 font tags. The library will therefore call the function you passed into .text() for each of those elements. However, the first call will have the effect of removing the other two <font> elements from the DOM. Even though the library proceeds to call your callback for those elements, there's no effect because they're not on the page anymore.
Here's what could work:
var $fonts = $('#mydiv').find('font');
var text = $fonts.text();
var attrs = {};
$fonts.each(function(_, font) {
var names = ["size", "color", "face"];
for (var i = 0; i < names.length; ++i)
if (font[names[i]]) attrs[names[i]] = font[names[i]];
});
var newText = "";
for (var name in attrs) {
if (attrs.hasOwnProperty(name))
newText += '[' + name + '=' + attrs[name] + ']';
}
newText += text;
for (var name in attrs) {
if (attrs.hasOwnProperty(name))
newText += '[/' + name + ']';
}
$('#mydiv').text(newText);
Note that I'm not really sure why you want to put the BBCode onto the page like that, but it seems to be the intention.
Seems to me your first line should be:
$("#mydiv").find("font").each(function(){

JQuery Chained/Cascading Dropdown events

I am currently attempting to set up a set of chained selects using the Flexbox Jquery plugin (this is not specifically designed for chaining, but can be used for that).
I have the chaining working if I set everything explicitly, but I am trying to dry my code up and make it more understandable. As such, I have come up with the code below.
All boxes currently load initially, and make their queries. The problem I am having is that when I iterate through the menus (as below), I lose the onSelect functionality - it only fires for the last menu I loaded.
My understanding was that since I am using a different JQuery selector each time - $('#' + fbMenu.divId) - it would not matter that I then set the onSelect behavior for another menu, but evidently this is not the case. Am I somehow overwriting the binding each time I am loading a box?
Hopefully I don't have to specify the onSelect functionality for each dropdown, as there could be a large number of them.
Many thanks for any assistance you can provide!
$(document).ready(function() {
// Create the variables for data objects
var vehicleMakeFb = new Object();
var vehicleModelFb = new Object();
var vehicleTrimFb = new Object();
// Set up each menu with the divId, jsonUrl and the chlid menus that will be updated on select
vehicleMakeFb.divId = 'vehicle_vehicleMake_input';
vehicleMakeFb.jsonUrl = '/vehicles/getmakes';
vehicleMakeFb.children = [vehicleModelFb];
vehicleModelFb.divId = 'vehicle_vehicleModel_input';
vehicleModelFb.jsonUrl = '/vehicles/getmodels';
vehicleModelFb.children = [vehicleTrimFb];
vehicleTrimFb.divId = 'vehicle_vehicleTrim_input';
vehicleTrimFb.jsonUrl = '/vehicles/gettrims';
vehicleTrimFb.children = [];
// Create an array of all menu objects so that they can be iterated through
var allMenus = [vehicleMakeFb,vehicleModelFb,vehicleTrimFb];
// Create the parent menu
for (var i = 0; i < allMenus.length; i++) {
var fbMenu = allMenus[i];
alert(fbMenu.divId);
$('#' + fbMenu.divId).flexbox(fbMenu.jsonUrl + '.json', {
// Update the child menu(s), based on the selection of the first menu
onSelect: function() {
for (var i = 0; i < fbMenu.children.length; i++) {
var fbChild = fbMenu.children[i];
var hiddendiv = document.getElementById(fbMenu.divId + '_hidden');
var jsonurl1 = fbChild.jsonUrl + '/' + hiddendiv.getAttribute('value') + '.json';
alert(jsonurl1);
$('#' + fbChild.divId).flexbox(jsonurl1);
}
}
});
}
});
If you put all the information on the elements them selves i think you will have better results. Although I've been known to be wrong, I think the context of the select functions are getting mixed up.
instead of setting up each menu as an object try:
$(document).ready(function() {
var setupdiv = (function(divId, jsonUrl, children)
{
jQuery('#' + divId)
.data("jsonurl", jsonUrl)
.data("children", children.join(",#"));
   
// Create the parent menu
jQuery('#' + divId).flexbox(jsonUrl + '.json',
{  
// Update the child menu(s), based on the selection of the first menu
onSelect: function()
{  
var children = jQuery(this).data("children");
var jsonUrl = jQuery(this).data("jsonurl");
if(children)
{
children = jQuery('#' + children);
alert('children was true');
}
else
{
children = jQuery();
alert('children was false');
}
var hiddendiv = jQuery('#' + this.id + '_hidden');
children.each(function()
{
var childJsonUrl = jsonUrl + '/' + hiddendiv.val() + '.json';
alert(childJsonUrl);
$(this).flexbox(childJsonUrl);  
});
}
});
});
setupdiv('vehicle_vehicleMake_input', '/vehicles/getmakes', ['vehicle_vehicleModel_input']);
setupdiv('vehicle_vehicleModel_input', '/vehicles/getmodels', ['vehicle_vehicleTrim_input']);
setupdiv('vehicle_vehicleTrim_input', '/vehicles/gettrims', []);
});
DISCLAIMER
I'm known for my spelling mistakes. Please spellcheck before using this code ;)
Update
I've changed the first two lines of code and I've normalized the indenting as there were a mix of tabs and spaces. Should be easier to read now.

Filtering the list of friends extracted by Facebook graph api ( more of a JavaScript/Jquery question than Facebook API question)

Hello there JavaScript and Jquery gurus, I am getting and then displaying list of a facebook user's friend list by using the following code:
<script>
function getFriends(){
var theword = '/me/friends';
FB.api(theword, function(response) {
var divInfo = document.getElementById("divInfo");
var friends = response.data;
divInfo.innerHTML += '<h1 id="header">Friends/h1><ul id="list">';
for (var i = 0; i < friends.length; i++) {
divInfo.innerHTML += '<li>'+friends[i].name +'</li>';
}
divInfo.innerHTML += '</ul></div>';
});
}
</script>
graph friends
<div id = divInfo></div>
Now, in my Facebook integrated website, I would eventually like my users to choose their friends and send them gifts/facebook-punch them..or whatever. Therefore, I am trying to implement a simple Jquery filter using this piece of code that manipulates with the DOM
<script>
(function ($) {
// custom css expression for a case-insensitive contains()
jQuery.expr[':'].Contains = function(a,i,m){
return (a.textContent || a.innerText || "").toUpperCase().indexOf(m[3].toUpperCase())>=0;
};
function listFilter(header, list) { // header is any element, list is an unordered list
// create and add the filter form to the header
var form = $("<form>").attr({"class":"filterform","action":"#"}),
input = $("<input>").attr({"class":"filterinput","type":"text"});
$(form).append(input).appendTo(header);
$(input)
.change( function () {
var filter = $(this).val();
if(filter) {
// this finds all links in a list that contain the input,
// and hide the ones not containing the input while showing the ones that do
$(list).find("a:not(:Contains(" + filter + "))").parent().slideUp();
$(list).find("a:Contains(" + filter + ")").parent().slideDown();
} else {
$(list).find("li").slideDown();
}
return false;
})
.keyup( function () {
// fire the above change event after every letter
$(this).change();
});
}
//ondomready
$(function () {
listFilter($("#header"), $("#list"));
});
}(jQuery));
</script>
Now, This piece of code works on normal unordered list, but when the list is rendered by JavaScript, it does not. I have a hunch that it has to do something with the innerHTML method. Also, I have tried putting the JQuery filter code within and also right before tag. Neither seemed to work.
If anyone knows how to resolve this issue, please help me out. Also, is there a better way to display the friends list from which users can choose from?
The problem is here:
$(list).find("a:not(:Contains(" + filter + "))").parent().slideUp();
$(list).find("a:Contains(" + filter + ")").parent().slideDown();
Since you're rendering this:
divInfo.innerHTML += '<li>'+friends[i].name +'</li>';
There is no anchor wrapper, the text is directly in the <li> so change the first two lines to look in those elements accordingly, like this:
$(list).find("li:not(:Contains(" + filter + "))").slideUp();
$(list).find("li:Contains(" + filter + ")").slideDown();
You could also make that whole section a bit faster by running your Contains() code only once, making a big pact for long lists, like this:
$(input).bind("change keyup", function () {
var filter = $(this).val();
if(filter) {
var matches = $(list).find("li:Contains(" + filter + ")").slideDown();
$(list).find("li").not(matches).slideUp();
} else {
$(list).find("li").slideDown();
}
});
And to resolve those potential (likely really) innerHTML issues, build your structure by using the DOM, like this:
function getFriends(){
var theword = '/me/friends';
FB.api(theword, function(response) {
var divInfo = $("#divInfo"), friends = response.data;
divInfo.append('<h1 id="header">Friends/h1>');
var list = $('<ul id="list" />');
for (var i = 0; i < friends.length; i++) {
$('<li />', { text: friends[i].name }).appendTo(list);
}
divInfo.append(list);
});
}
By doing it this way you're building your content all at once, the <ul> being a document fragment, then one insertion....this is also better for performance for 2 reasons. 1) You're currently adding invalid HTML with the .innerHTML calls...you should never have an unclosed element at any point, and 2) you're doing 2 DOM manipulations (1 for the header, 1 for the list) after the much faster document fragment creation, not repeated .innerHTML changes.

how to select a text range in CKEDITOR programatically?

Problem:
I have a CKEditor instance in my javascript:
var editor = CKEDITOR.instances["id_corpo"];
and I need to insert some text programatically, and select some text range afterwards.
I already did insert text through
editor.insertHtml('<h1 id="myheader">This is a foobar header</h1>');
But I need to select (highlight) the word "foobar", programatically through javascript, so that I can use selenium to work out some functional tests with my CKEditor plugins.
UPDATE 1:
I've also tried something like
var selection = editor.getSelection();
var childs = editor.document.getElementsByTag("p");
selection.selectElement(childs);
But doesn't work at all!
How can I do that?
I think that
selection.selectRange()
could do the job, but I'could not figure out how to use it.
There are no examples over there :(
Get current selection
var editor = CKEDITOR.instances["id_corpo"];
var sel = editor.getSelection();
Change the selection to the current element
var element = sel.getStartElement();
sel.selectElement(element);
Move the range to the text you would like to select
var findString = 'foobar';
var ranges = editor.getSelection().getRanges();
var startIndex = element.getHtml().indexOf(findString);
if (startIndex != -1) {
ranges[0].setStart(element.getFirst(), startIndex);
ranges[0].setEnd(element.getFirst(), startIndex + findString.length);
sel.selectRanges([ranges[0]]);
}
You can also do the following:
get the current selection
var selection = editor.getSelection();
var selectedElement = selection.getSelectedElement();
if nothing is selected then create a new paragraph element
if (!selectedElement)
selectedElement = new CKEDITOR.dom.element('p');
Insert your content into the element
selectedElement.setHtml(someHtml);
If needed, insert your element into the DOM (it will be inserted into the current position)
editor.insertElement(selectedElement);
and then just select it
selection.selectElement(selectedElement);
Check out the selectElement() method of CKEDITOR.dom.selection.
http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.dom.selection.html
insert text at cursor point in ck editor
function insertVar(myValue) {
CKEDITOR.instances['editor1'].fire( 'insertText',myValue);
}
this is working for me

Categories

Resources