I am trying to add a custom id attribute for each img & input elements in ckeditor 3.6.4.
So far I have added dataProcessor.htmlFilter to handle the id attribute like this
CKEDITOR.on( 'instanceReady', function(event) {
var editor = CKEDITOR.instances.editor;
editor.dataProcessor.htmlFilter.addRules(
{
elements: {
$: function (element) {
if ( (element.name == 'img' || element.name == 'input') && CKEDITOR.instances.editor.mode == 'wysiwyg' ) {
if (!element.attributes.id){
var g = createID();
alert('new id: ' + g);
element.attributes.id = g;
}
}
}
}
});
});
and when I add a new textfield in visual editor I do get a new id. But if I set to the source mode the mode is still 'wysiwyg' and not 'source' and it gives a new Id.
How can I prevent the double action?
Did some testing. Added this
CKEDITOR.instances.editor.on('mode', function() {
// Code to execute when the user switches editing modes
alert('Changed to: ' + CKEDITOR.instances.editor.mode);
});
Somehow that fires after the htmlFilter rule.
You can try adding a check for the editor mode to your rule.
if ( (element.name == 'img' || element.name == 'input') && editor.mode == "wysiwyg" ) {
if (!element.attributes.id){
var g = createID();
alert('new id: ' + g);
element.attributes.id = g;
}
}
I'm not sure whether "editor" would be the correct object name to use, you may want to use CKEDITOR.currentInstance.mode
There is also a getMode() method.
Here are some api references for the items mentioned:
mode property
currentInstance property
getMode() method
Went with plan B
Customized the widgets needed and added the id attribute in plugin. Was easy and fast.
Related
I have turned on allowedContent property in config.
config.allowedContent = "true"
This allows me to add ids to paragraphs inside contenteditable div.
However, now whenever I hit enter key inside the contenteditable div a new paragraph with same id is generated. I would assume after hiiting enter key a new paragraph should be inserted without any ids but it looks like the ids are copied from previously generated paragraph.
Is there any way to avoid this?
Try this. It's not bullet proof but works well enough. Even though I wrote it, I kind of hate it so if you improve on it, please share the love ;)
editor.on('key', function (evt) {
// Only if editor is not in source mode.
if (editor.mode === 'source') { return; }
// Enter is keyCode 13
if (evt.data.keyCode === 13) {
// if we call getStartElement too soon, we get the wrong element sometimes
setTimeout(function () {
var selection = editor.getSelection();
if (typeof selection === 'undefined') { return; }
var startElement = selection.getStartElement();
// If there are spans nested in the paragraph preserve them
// And we need to find the parent paragraph
// This could be optimized...
if (startElement.getName() == 'span') {
var text = "";
while (startElement.getName() == 'span') {
text += startElement.getHtml();
startElement = startElement.getParent();
}
if (text.length === 0) {
startElement.setHtml(' ');
} else {
startElement.setHtml(text);
}
}
// HERE I remove the "id" attribute.
startElement.removeAttribute("id");;
}, 10);
}
});
I've got a weird bug happening on a music library site that I'm working on. The intended functionality (take a form of checkboxes, dynamically change them into selectable words that highlight and "check" the associated checkbox on click, then automatically update the songs below based on the highlighted tags) works fine -- but when you click a selected tag to remove it, it does the correct functionality with the data below and the highlight is removed, but all other selected tags are getting "display: none" added to them.
Here, I think, is the function causing the weird issue:
// Given an label "$label," if it hasn't been selected, then
// highlight the label area and set the "checked" value of the
// appropriate checkbox input to true; if it is already selected,
// remove the highlight and set the "checked" value of the appropriate
// checkbox to "false"
function highlightAndCheck( $label )
{
var id = $label.attr("id"),
$checkbox = $label.prev(),
val = $checkbox.attr("value");
if( id === val )
{
if( $label.hasClass("clicked") )
{
$checkbox.prop("checked", false);
$label.removeClass("clicked");
} else
{
$checkbox.prop("checked", true);
$label.addClass("clicked");
}
}
}
Here's the full jQuery code for the page. I can provide more code if anything is confusing, but I hope the labeling, etc. are straightforward:
$(function() { //on document ready
var $categoriesAndTags = $("div#categories-and-tags"),
$tagCategory = $categoriesAndTags.find("div.tag-category"),
$searchButton = $categoriesAndTags.find("input#public-user-tag-search-submit");
// First, hide the checkboxes and search button, since we don't need them in the dynamic version
$tagCategory.addClass("tag-spinner-skin")
.find("input[type=checkbox]").hide();
$tagCategory.find("br").hide();
$searchButton.hide();
// Make it so then clicking on the text of a tag makes the hidden select box "select"
$tagCategory.find("label").each(function(){
$(this).on("click",function(){
var $this = $(this);
highlightAndCheck( $this );
searchByTags();
//While the unwanted "display:none" bug is happening, use this to make them re-appear on next click
$this.siblings("label").show();
});
});
// Make the search update automatically when a select box is clicked or unclicked.
var tagIDs = getUrlVarValArray( "tagID" );
$tagCategory.find("label").each(function(){
var $this = $(this),
id = $this.attr("id");
if( tagIDs.indexOf( id ) > -1 )
{ highlightAndCheck( $this ); }
});
});
function searchByTags( tags )
{
data = $("form#tag-select input").serialize()
if( data.length > 0 )
{
data += "&search=search";
}
$.ajax({
url: "songs/",
data: data,
type: "GET",
success: function(data){
// DO THIS if we successfully do the Ajax call
$newSongPlayers = $(data).find("div#songs-area");
$("div#songs-area").replaceWith( $newSongPlayers );
$.getScript("javascripts/public.js");
}
});
}
// Given an label "$label," if it hasn't been selected, then
// highlight the label area and set the "checked" value of the
// appropriate checkbox input to true; if it is already selected,
// remove the highlight and set the "checked" value of the appropriate
// checkbox to "false"
function highlightAndCheck( $label )
{
var id = $label.attr("id"),
$checkbox = $label.prev(),
val = $checkbox.attr("value");
if( id === val )
{
if( $label.hasClass("clicked") )
{
$checkbox.prop("checked", false);
$label.removeClass("clicked");
} else
{
$checkbox.prop("checked", true);
$label.addClass("clicked");
}
}
}
function getUrlVarValArray( needle )
{
var results = [],
hash,
hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
for(var i = 0; i < hashes.length; i++)
{
hash = hashes[i].split('=');
if( needle.length > 0 )
{
if( hash[0] === needle )
{
results[ results.length ] = hash[1]; // array[ array.length ] is a faster way to apend than array.push() in small arrays
}
}
}
return results;
}
Thanks in advance for your help! If it's helpful to login and see this in context, please go to the test site and use username: stackoverflowuser; password: HelpMeFigureThisOut -- once you're logged in, click on "View Songs"and the jQuery is referencing the tags at the top of the page.
Look at the following code in the public.js file:
$("html").on("click", function(event){
if(!$(event.target).is('.options-button')
&& !$(event.target).is('input.add-new-playlist')
&& !$(event.target).is('button.submit-new-playlist')
&& !$(event.target).is('button.add-song-to-playlist')
&& !$(event.target).is('button.playlist-popup-button')
)
{
if(!$(event.target).is('.clicked') && !$(event.target).is('.clicked > div') )
{ $(".clicked").hide().removeClass("clicked"); }
}
});
This handler gets executed because the click event propagates from the <label> element to the <html> element. It executes after the click handler on the <label> element, which removes the "clicked" class from the <label> element. Since the <label> element is the event.target and no longer has the "clicked" class, the following line is executed:
$(".clicked").hide().removeClass("clicked");
That line hides all the labels that still have the "clicked" class.
I have a text area with tiny mce, I load it like this:
$(document).ready(function () {
tinyMCE.init({
mode: "textareas",
...
This text area is in a form. I bind forms submit button to:
$('#btnSubmit').click(function() {
tinymce.triggerSave();
var editorContent = tinyMCE.get('Description').getContent();
if (editorContent == '' || editorContent == null)
{
$(tinymce.activeEditor.getBody()).css("background-color", '#ffeeee');
$(tinymce.activeEditor.getBody().parentNode).css("background-color", '#ffeeee');
$(tinymce.activeEditor.getBody().parentNode).css("border", '1px solid #ff0000');
}
});
In my entity class I have Required attribute.
My goal is to make tinyMCE background red when model is not valid. But I get error from ti question title.
Any help?
So, validation works. If I remove textarea empty check and leave color changing it changes. But the problem is when there is something in text area and I click submit area first become red and then submit.
Is there maybe some fubction where I can do something if validation fail?
It sounds to me just like an undefined object error - where the code can't resolve this line tinyMCE.get('Description').getContent();.
You seem to be mixing between using activeEditor sometimes and other times not, so instead I've standardised the code so you are always relying on activeEditor - this means I've removed the line that was triggering the error. You also seem to switch between using tinymce and tinyMCE which might not be causing problems but is best to be avoided... so I've standardised that as well.
Without seeing more of the way the rest of the code and markup is set-up however it's a bit difficult to tell exactly what is going on. Does my change repair the problem?
$('#btnSubmit').click(function() {
tinyMCE.triggerSave();
var editorContent = tinyMCE.activeEditor.getContent();
if (editorContent == '' || editorContent == null)
{
$(tinyMCE.activeEditor.getBody())
.css("background-color", '#ffeeee')
.parent()
.css({
"background-color": '#ffeeee',
"border": '1px solid #ff0000'
});
}
});
If you do not have control over init method of TinyMCE then, you can follow this solution.
jQuery(document).ready(function($) {
function myCustomSetContent( id, content ) {
// Check if TinyMCE is defined or not.
if( typeof tinymce != "undefined" ) {
var editor = tinymce.get( id );
// Check if TinyMCE is initialized properly or not.
if( editor && editor instanceof tinymce.Editor ) {
editor.setContent( text );
editor.save( { no_events: true } );
} else {
// Fallback
// If TinyMCE is not initialized then directly set the value in textarea.
//TinyMCE will take up this value when it gets initialized.
jQuery( '#'+id ).val( text );
}
return true;
}
return false;
}
function myCustomGetContent( id ) {
// Check if TinyMCE is defined or not.
if( typeof tinymce != "undefined" ) {
var editor = tinymce.get( id );
// Check if TinyMCE is initialized properly or not.
if( editor && editor instanceof tinymce.Editor ) {
return editor.getContent();
} else {
// Fallback
// If TinyMCE is not initialized then directly set the value in textarea.
// TinyMCE will take up this value when it gets initialized.
return jQuery( '#'+id ).val();
}
}
return '';
}
$(".class-to-update-content").on("click", function(e) {
myCustomSetContent( "tinymce-editor-id", "New Content in Editor" );
});
$(".class-to-get-content").on("click", function(e) {
$("div.class-to-display-content").html( myCustomGetContent( "tinymce-editor-id" ) );
});
});
Ref : http://blog.incognitech.in/tinymce-undefined-issue/
I get this message : tinyMCE.get("message_data_" + msg_id) is undefined on Firebug after
I click on the button to submit the form.
msg_id is defined, I checked it. message_data_ is also defined.
The function tinyMCE.get is not for some reason.
If you are using one single editor instance you may use tinymce.editors[0] instead of tinyMCE.get("message_data_" + msg_id).
If you do not have control over init method of TinyMCE then, you can follow this solution. Basically it adds a fallback if TinyMCE is not initialized.
jQuery(document).ready(function($) {
function myCustomSetContent( id, content ) {
// Check if TinyMCE is defined or not.
if( typeof tinymce != "undefined" ) {
var editor = tinymce.get( id );
// Check if TinyMCE is initialized properly or not.
if( editor && editor instanceof tinymce.Editor ) {
editor.setContent( text );
editor.save( { no_events: true } );
} else {
// Fallback
// If TinyMCE is not initialized then directly set the value in textarea.
//TinyMCE will take up this value when it gets initialized.
jQuery( '#'+id ).val( text );
}
return true;
}
return false;
}
function myCustomGetContent( id ) {
// Check if TinyMCE is defined or not.
if( typeof tinymce != "undefined" ) {
var editor = tinymce.get( id );
// Check if TinyMCE is initialized properly or not.
if( editor && editor instanceof tinymce.Editor ) {
return editor.getContent();
} else {
// Fallback
// If TinyMCE is not initialized then directly set the value in textarea.
// TinyMCE will take up this value when it gets initialized.
return jQuery( '#'+id ).val();
}
}
return '';
}
$(".class-to-update-content").on("click", function(e) {
myCustomSetContent( "tinymce-editor-id", "New Content in Editor" );
});
$(".class-to-get-content").on("click", function(e) {
$("div.class-to-display-content").html( myCustomGetContent( "tinymce-editor-id" ) );
});
});
Ref : http://blog.incognitech.in/tinymce-undefined-issue/
if you are using multiple editor in a form then you can create a function and get its value
i.e.
// Declare a function to get editor value
function tinyMca_text(field_id) {
if ( jQuery("#"+field_id+":hidden").length > 0)
{
return tinyMCE.get(field_id).getContent();
}
else
{
return jQuery('#'+field_id).val();
}
}
// show that value
console.log(tinyMca_text('field'));
The following javascript (prototype 1.6) code hides all checkboxes on the page and inserts a div element with some css style and a click event to act as a fake-checkbox. It also looks out for a label next (or previous) the checkbox, to also trigger the same event.
When I click the div (fake_el) itself, everything works as expected, but when I try the same with the label, it only works one time. after that, the el isn't gonna change - as if it (the el) would be a value-parameter.
Any ideas here?
$$('input[type=checkbox]').each(function(el) {
if (el.visible()) {
var fake_el = new Element('div', { className:'checkbox checkbox_' + el.checked });
var label = (el.next() != null && el.next().tagName === 'LABEL' && el.next().readAttribute('for') === el.id) ? el.next() : undefined;
label = (el.previous() != null && el.previous().tagName === 'LABEL' && el.previous().readAttribute('for') === el.id) ? el.previous() : label;
var action = function(el) {
el.checked = (el.checked) ? false : true;
fake_el.className = 'checkbox checkbox_' + el.checked;
}
fake_el.observe('click', function() { action(el); });
if (label != null) { label.observe('click', function() { c.log(el); action(el); c.log(el); }); }
el.insert({ after:fake_el }).hide();
}
});
I changed a couple items and created a jsFiddle for this. First and foremost, c.log had to be changed to console.log for it to work for me :). After that, the only thing I changed was how the divs were added, since it wasn't working for me with insert. I set up some test data and away I went...
EDIT: Perhaps you don't have a non-label tag between two checkboxes and it is getting confused? Notice I have a br between label and the next checkbox, maybe you need to do something like that.