I'm working on a CKEditor plugin for annotating text and adding margin comments, but I'd like some of my custom toolbar buttons to be enabled only when the user has already selected a range of text. Whenever the user is typing, or the cursor is at a single-point (instead of a range), the buttons (and their associated commands) should be disabled.
I'm a pretty experienced plugin author, and I've spent a fair amount of time hunting through the core API docs, but I haven't found anything yet that looks like it'll help.
Your case is a little tricky, because selection change event is not well implemented across browsers, FF is the main problem.
In your case you'll going to need check selection changes very frequently, as you're interested in all selection changes therefore CKEditor selectionChange won't fit it.
Fortunately there's a selectionCheck event in editor that fires much more frequently and is implemented for FF.
Solution:
Here you have init method of a plugin that I've mocked to solve your problem. It will disable / enable Source button the way you explained.
I've already added throttling to this function, so that customers with less expansive machine can admire your feature :)
init: function( editor ) {
// Funciton depending on editor selection (taken from the scope) will set the state of our command.
function RefreshState() {
var editable = editor.editable(),
// Command that we want to control.
command = editor.getCommand( 'source' ),
range,
commandState;
if ( !editable ) {
// It might be a case that editable is not yet ready.
return;
}
// We assume only one range.
range = editable.getDocument().getSelection().getRanges()[ 0 ];
// The state we're about to set for the command.
commandState = ( range && !range.collapsed ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED;
command.setState( commandState );
}
// We'll use throttled function calls, because this event can be fired very, very frequently.
var throttledFunction = CKEDITOR.tools.eventsBuffer( 250, RefreshState );
// Now this is the event that detects all the selection changes.
editor.on( 'selectionCheck', throttledFunction.input );
// You'll most likely also want to execute this function as soon as editor is ready.
editor.on( 'instanceReady', function( evt ) {
// Also do state refresh on instanceReady.
RefreshState();
} );
}
If you are working on a plugin, I guess that you are registering commands with the ckeditor.
In that case, you should provide a refresh method, which will be called by the CKEditor to update the state of the button when needed:
Defined by the command definition, a function to determine the command
state. It will be invoked when the editor has its states or selection
changed.
You can see examples of implementation in several of the plugins developed by the CKEditor team. Here is one taken from the source code of the Link plugin:
refresh: function( editor, path ) {
var element = path.lastElement && path.lastElement.getAscendant( 'a', true );
if ( element && element.getName() == 'a' && element.getAttribute( 'href' ) && element.getChildCount() )
this.setState( CKEDITOR.TRISTATE_OFF );
else
this.setState( CKEDITOR.TRISTATE_DISABLED );
}
Here is the code required to answer exactly the question using the refresh method as suggested by Gyum Fox.
I mention that to make it work, contextSensitive has to be set to 1.
editor.addCommand( 'myCommand', {
exec: function( editor ) {
// Custom logic of the command
},
refresh: function( editor ) {
var editable = editor.editable();
range = editable.getDocument().getSelection().getRanges()[ 0 ]; // We assume only one range.
commandState = ( range && !range.collapsed ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED;
this.setState( commandState );
},
contextSensitive: 1
});
EDIT: I noticed some refresh issues on my side. So, because of that, I'd go for the Marek Lewandowski answer.
Related
I see in CKEitor 4.5 there is a new drag and drop system. I would like to drop external DIVs or SPANs into my CkEditor and have them turn into "placeholders" "fake objects" or "protected source" objects. I.e., the dropped object should turn into arbitrary HTML that's related to the content.
The available demos seem to be about uploading content, but this is different and I'd appreciate a demo ...
Yes, it is possible. CKEditor 4.5 is in the beta phase at the moment, what means there is no tutorials yet, but here is sample how to do it.
First, you need to mark your data on dragstart. You can simple set text:
dragstart( evt ) {
evt.dataTransfer.setData( 'text', 'foo' );
} );
But then you need to make your text unique, otherwise every time someone drop foo it will be recognize as your container.
I prefer to use CKEditor data transfer facade, which let you use custom data type on every browser (including IE 8+):
dragstart( evt ) {
var evt = { data: { $: $evt } }; // Create CKEditor event.
CKEDITOR.plugins.clipboard.initDragDataTransfer( evt );
evt.data.dataTransfer.setData( 'mydatatype', true );
// Some text need to be set, otherwise drop event will not be fired.
evt.data.dataTransfer.setData( 'text', 'x' );
} );
Then in the CKEDITOR you can recognize this data and set your html to be dropped. You can replace dropped content whit whatever you need. Simple set text/html data in the drop event:
editor.on( 'drop', function( evt ) {
var dataTransfer = evt.data.dataTransfer;
if ( dataTransfer.getData( 'mydatatype' ) ) {
dataTransfer.setData( 'text/html', '<div>Bar</div>' );
}
} );
You can find working sample here: http://jsfiddle.net/oqzy8dog/3/
I'd like to place the caret at the end of the text after focus().
There are a lot of solutions for CKEditor 3, I tried around three of them, but they don't seem to work for CKEditor 4.
Note: I'm using inline-editing with contenteditable="true".
This should do the job (it WFM on themed and inline editors):
CKEDITOR.inline( 'editable', {
on: {
focus: function( evt ) {
setTimeout( function() {
var editor = evt.editor,
range = editor.createRange();
range.moveToElementEditEnd( editor.editable() );
range.select();
range.scrollIntoView();
}, 100 );
}
}
} );
Note that the timeout is required because on focus selection is automatically placed at the beginning or in place which was clicked, so you need to wait a while to overwrite that behaviour. You can check shorter timeouts of course.
The title explains it all...
I need to perform a custom action when I know a user has finished resizing, but from what I can find in the Kendo UI documentation there is no event for this accessible to me other that 'resize' which I cannot use as is.
Perhaps i just missed the event?
if not:
Is there a way to use the 'resize' event to determine that a user has stopped resizing?
So here's my answer thus far:
Mine differs slightly due to architectural needs, but here's a general solution
var isResizing = false;
var wndw = $(element).kendoWindow({
// .....
resize: OnResize,
// .....
}).data('kendoWindow');
function onResize() {
isResizing = true;
}
$('body').on('mouseup', '.k-window', function() {
if(isResizing){
// **Your 'Stopped' code here**
isResizing = false;
}
});
Have you considered using underscore.js debounce? I have used it successfully to only trigger then change after the resize events have stopped coming for a certain period (in the case below 300ms). This does add a small delay to captureing the end, but if like me you just want to store the final size then that works fine. Here is the version of the code above but using underscore debounce:
var wndw = $(element).kendoWindow({
// .....
resize: _.debounce( this.hasResized, 300)
// .....
}).data('kendoWindow');
//This is called at the end of a resize operation (using _.debounce)
function hasResized (args) {
// ** Your code here **
};
Hope that helps.
I'm using Tinymce (with jQuery) in a project I'm working at; we use a rich text editor for users to input information; however, sometimes when loading the page Firefox and Chrome will detect a 'tinymce is not defined' error (sometimes at different lines of the code), while other times the page will load just fine. What's weird is that it works perfectly with IE.
Here's a bit of the code I'm using:
view.find('textarea.rich-text').each(function () {
$(this).tinymce( /* ...rules... */);
});
And later on
_variable.find("#summary").tinymce().setContent(content);
This line is where the error (sometimes) gets caught. It seems to me that the problem is a loading issue, even though the tinyMCE plugin is initialized about 5000 lines prior this line.
Update: For now I have managed to 'solve' the problem with a setTimeout, but this seems like a really ugly way to do it.
A few points:
You don't mention whether or not the TinyMCE initialization is done within a jQuery ready event function. It should be of course.
You don't need the each loop. You can just say:
$('textarea.rich-text').tinymce({
script_url : '../js/tinymce/jscripts/tiny_mce/tiny_mce.js',
theme : "advanced",
...
});
You don't need the call to find since you are just selecting by id. Just do:
$("#summary").tinymce().setContent(content);
Your real issue is probably that tinymce has not finished initializing itself when you get the error. You see it has to load a script from the configured script_url. That may take a while. Therefore, you have to make use of a callback such as oninit.
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/
EDIT: Solution included
How do you keep track of your UI elements in Titanium? Say you have a window with a TableView that has some Switches (on/off) in it and you'd like to reference the changed switch onchange with a generic event listener. There's the property event.source, but you still don't really know what field of a form was just toggled, you just have a reference to the element. Is there a way to give the element an ID, as you would with a radiobutton in JavaScript?
Up to now, registered each form UI element in a dictionary, and saved all the values at once, looping through the dictionary and getting each object value. But now I'd like to do this onchange, and I can't find any other way to do it than create a specific callback function for each element (which I'd really rather not).
just assign and id to the element... all of these other solution CAN work, but they seem to be over kill for what you are asking for.
// create switch with id
var switcher0 = Ti.Ui.createSwitch({id:"switch1"});
then inside your event listener
myform.addEventListener('click', function(e){
var obj = e.source;
if ( obj.id == "switch1" ) {
// do some magic!!
}
});
A simple solution is to use a framework that helps you keep track of all your elements, which speeds up development quite a bit, as the project and app grows. I've built a framework of my own called Adamantium.js, which lets you use a syntax like jQuery to deal with your elements, based on ID and type selectors. In a coming release, it will also support for something like classes, that can be arbitrarily added or removed from an element, tracking of master/slave relationships and basic filter methods, to help you narrow your query. Most methods are chainable, so building apps with rich interaction is quick and simple.
A quick demo:
// Type selector, selects all switches
$(':Switch')
// Bind a callback to the change event on all switches
// This callback is also inherited by all new switch elements
$(':Switch').bind('change', function (e) {
alert(e.type + ' fired on ' + e.source.id + ', value = ' + e.value);
});
// Select by ID and trigger an event
$('#MyCustomSwitch').trigger('change', {
foo: 'bar'
});
Then there's a lot of other cool methods in the framework, that are all designed to speed up development and modeled after the familiar ways of jQuery, more about that in the original blog post.
I completely understand not wanting to write a listener to each one because that is very time consuming. I had the same problem that you did and solved it like so.
var switches = [];
function createSwitch(i) {
switches[i] = Ti.UI.createSwitch();
switches[i].addEventListener('change', function(e) {
Ti.API.info('switch '+i+' = '+e.value);
});
return switches[i];
}
for(i=0;i<rows.length;i++) {
row = Ti.UI.createTableViewRow();
row.add(createSwitch(i));
}
However keep in mind that this solution may not fit your needs as it did mine. For me it was good because each time I created a switch it added a listener to it dynamically then I could simply get the e.source.parent of the switch to interact with whatever I needed.
module Id just for the hold it's ID. When we have use id the call any another space just use . and use easily.
Try This
var but1 = Ti.Ui.createButton({title : 'Button', id:"1"});
window.addEventListener('click', function(e){
var obj = e.source;
if ( obj.id == "1" ) {
// do some magic!!
}
});
window.add(but1);
I, think this is supported for you.
how do you create your tableview and your switcher? usually i would define a eventListener function while creating the switcher.
// first switch
var switcher0 = Ti.Ui.createSwitch();
switch0.addEventListener('change',function(e){});
myTableViewRow.add(switch0);
myTableView.add(myTableViewRow);
// second switch
var switch1 = ..
so no generic event listener is needed.