ckeditor - javascriptspellcheck plugin - javascript

I am trying to integrate javascriptspellchecker into a web page with ckEditor (note I am using ckeditor version 3.6). I would like to replace the default spellcheck and SCAYT (spell check as you type) plugins with new custom plugins that use javascriptspellcheck.
I have created a plugin following the example from the javascriptspellchecker website but it doesn't work properly. The javascriptspellchecker taked the id of the textarea and runs a spellcheck on it's value (or attaches event handlers to spellcheck after input when chosing SCAYT). The problem is, when I alter the text in a ckEditor instance, the hidden textbox doesn't seem to be updated in the background. This means the plugin I have written only checks the original value of the textarea, and the SCAYT doesn't work.
My plugin so far:-
(function () {
//Section 1 : Code to execute when the toolbar button is pressed
var a = {
exec: function (editor) {
$Spelling.SpellCheckInWindow($(editor.element).attr('id'))
}
},
//Section 2 : Create the button and add the functionality to it
b = 'javascriptspellcheck';
CKEDITOR.plugins.add(b, {
init: function (editor) {
editor.addCommand(b, a);
editor.ui.addButton("JavaScriptSpellCheck", {
label: 'Check Spelling',
icon: this.path + "images/spell.png",
command: b
});
}
});
})();
Does anyone know if it is possible to make a working plugin? Is there a way to force the editor to update the hidden textarea, or is there another DOM element I can pass to the spellchecker?
Update:
In case it is useful, the SCAYT version of my plugin uses the following execute function
exec: function (editor) {
$Spelling.SpellCheckAsYouType($(editor.element).attr('id'))
}
Update 2:
I found a soltion for the normal spell check, I can call editor.UpdateElement() before running the spell check and it works! I'm not sure why though, when I inspect the original textarea with firebug the value doesn't seem to have changed.
New Spellcheck plugin
(function () {
//Section 1 : Code to execute when the toolbar button is pressed
var a = {
exec: function (editor) {
editor.updateElement();
$Spelling.SpellCheckInWindow($(editor.element).attr('id'));
}
},
//Section 2 : Create the button and add the functionality to it
b = 'javascriptspellcheck';
CKEDITOR.plugins.add(b, {
init: function (editor) {
editor.addCommand(b, a);
editor.ui.addButton("JavaScriptSpellCheck", {
label: 'Check Spelling',
icon: this.path + "images/spell.png",
command: b
});
}
});
})();
I still can't get SCAYT to work though. I found a ckeditor plugin to catch change events, and tried to call the updateElement() funciton again on every change. This doesn't work though, can anyone help?
My SCAYT plugin using the ckeditor onchange plugin:
exec: function (editor) {
editor.on('change', function (e) { this.updateElement(); });
$Spelling.SpellCheckAsYouType($(editor.element).attr('id'));
}

After contacting support for JavaScriptSpellcheck, they replied saying "SCAYT will not work with any editor as it risks injecting junk HTML into your forms". So the SCAYT plugin for CK Editor is not possible. As in my question update, the code for a working Spell Check in Window plugin for CK Editor (v3.6) is below:
(function () {
//Section 1 : Code to execute when the toolbar button is pressed
var a = {
exec: function (editor) {
editor.updateElement();
$Spelling.SpellCheckInWindow($(editor.element).attr('id'));
}
},
//Section 2 : Create the button and add the functionality to it
b = 'javascriptspellcheck';
CKEDITOR.plugins.add(b, {
init: function (editor) {
editor.addCommand(b, a);
editor.ui.addButton("JavaScriptSpellCheck", {
label: 'Check Spelling',
icon: this.path + "images/spell.png",
command: b
});
}
});
})();

Related

CKEDITOR: Disable plugin button when text is not highlighted

I have a CKEDITOR plugin that I'm having trouble disabling when there is no selected copy in the editor. Right now, the user can click the button without any highlighted text in the editor. I would like to modify it so that the plugin button is only active when there is copy highlighted in the editor. I've followed the suggestion here, but it isn't working.
Main Plugin Code:
CKEDITOR.plugins.add('cta', {
icons: 'cta',
init: function (editor) {
// Funciton depending on editor selection (taken from the scope) will set the state of our command.
function RefreshState() {
console.log('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.
console.log("editable not ready yet");
return;
}
// We assume only one range.
range = editable.getDocument().getSelection().getRanges()[0];
console.log(`range: `);
console.log(range);
// The state we're about to set for the command.
commandState = (range && !range.collapsed) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED;
console.log('commandState');
console.log(commandState);
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();
});
editor.addCommand('ctabtn', new CKEDITOR.dialogCommand('ctaDialog'));
editor.ui.addButton('cta', {
label: 'Insert Call to Action button',
command: 'ctabtn',
toolbar: 'insert'
});
CKEDITOR.dialog.add('ctaDialog', this.path + 'dialogs/cta.js');
}
});
My dialog code is here:
CKEDITOR.dialog.add('ctaDialog', function (editor) {
return {
// Basic properties of the dialog window: title, minimum size.
title: 'Call to Action',
minWidth: 400,
minHeight: 200,
// Dialog window content definition.
contents: [{
// Definition of the Basic Settings dialog tab (page).
id: 'tab-basic',
label: 'Basic Settings',
// The tab content.
elements: [{
// Text input field for the Call to Action text.
type: 'text',
id: 'cta',
label: 'Call to Action',
// Validation checking whether the field is not empty.
validate: CKEDITOR.dialog.validate.notEmpty("Call to Action field cannot be empty.")
},
{
// Text input field for the link text.
type: 'text',
id: 'url',
label: 'URL',
// Validation checking whether the field is not empty.
validate: CKEDITOR.dialog.validate.notEmpty("URL field cannot be empty.")
}
]
}],
// method invoked when the dialog button is clicked
onShow: function () {
var element = editor.getSelection();
var link = CKEDITOR.plugins.link;
var _this = this.getParentEditor();
var CTAhref = link.getSelectedLink(_this);
this.setValueOf('tab-basic', 'cta', element.getSelectedText().toString());
if (CTAhref != '' && CTAhref !== null) {
this.setValueOf('tab-basic', 'url', CTAhref.$.href);
}
},
// This method is invoked once a user clicks the OK button, confirming the dialog.
onOk: function () {
var dialog = this;
var CTA = editor.document.createElement('a');
CTA.setAttribute('href', dialog.getValueOf('tab-basic', 'url'));
CTA.setAttribute('class', 'btn btn-special--lg');
CTA.setText(dialog.getValueOf('tab-basic', 'cta'));
editor.insertElement(CTA);
}
};
});
Any ideas on why the plugin icon button on the toolbar doesn't become inactive when there is no copy highlighted in the editor? I can see in the console that CKEDITOR.dom.range.collapsed is toggling between TRUE/FALSE depending upon whether text is highlighted or not. It's just not disabling the button.
As stated, the suggested way of handling this was not working for me. What was working was using range.collapsed in returning a true value if a selection was made in the editor. With that, I turned to using Jquery to handle the rest.
// Hacky. But it gets the job done.
// a.cke_button.cke_button__cta.cke_button_off is the selector for my toolbar button.
// The onclick function listed was pulled from looking at the CKeditor functions
// to initiate my plugins modal.
// The setting of the "onclick" prop to null is needed to override the modal onclick
// binding when there is no selection.
range = editable.getDocument().getSelection().getRanges()[0];
if (range.collapsed === false) {
$('a.cke_button.cke_button__cta.cke_button_off').attr("onclick", 'CKEDITOR.tools.callFunction(83,this);return false;');
$('a.cke_button__cta').toggleClass('cta_button_disabled');
} else {
$('a.cke_button.cke_button__cta.cke_button_off').prop("onclick", null);
}

Dynamically instantiate QuillJS editor

(I'm going to preface this with the fact that I'm a new javascript developer, and I'm sure I have gaps in my knowledge about how javascript/angular/quill all work together on the page.)
I'm wanting to know if this is possible. Instead of instantiating the editor in the script tag on the page, I want to instantiate the editor for the div when it gets clicked. I'm using an Angular controller for my page, and inside the on click event I set up for the div, I tried a few things:
editor = new Quill(myDiv, {
modules: { toolbar: '#toolbar' },
theme: 'snow'
});
But that didn't work, so I thought maybe I had to explicitly pass the id of the div:
editor = new Quill('#editor', {
modules: { toolbar: '#toolbar' },
theme: 'snow'
});
This didn't work, and didn't focus inside the div and allow me to edit. So I thought maybe the problem was that I was hijacking the click event with angular, and maybe I need to switch the focus to the div after instantiating the editor. So I created a focus directive (just copy/pasted from another SO article) which worked fine when I tested on an input.
app.directive('focusOn', function () {
return function (scope, elem, attr) {
scope.$on(attr.focusOn, function (e) {
elem[0].focus();
});
};
then in the on click function in the angular controller:
$scope.$broadcast('focussec123');
if (editor == null) {
editor = new Quill('#editor', {
modules: { toolbar: '#toolbar' },
theme: 'snow'
});
}
That worked to select the text inside the div, but it didn't show the toolbar and so I suspected it didn't really work. I'm sure I'm misunderstanding some interactions and I'm fully aware I lack a lot of necessary knowledge about JS. My bottom line is I want to know:
Is it possible to dynamically instantiate the editor only for the current section, and to instantiate the editor again for another section when it gets clicked, etc.
If so, how?
Thanks in advance.
yes you can create Quill instances dynamically by clicking on a <div>.
It's exactly what we do.
That's how (roughly):
export class TextbocComponent ... {
private quill: Quill;
private target: HTMLElement;
private Quill = require("quill/dist/quill");
private onParagraphClicked(event: MouseEvent): void {
const options = {
theme: "bubble"
};
if (!this.quill) {
this.target = <HTMLElement>event.currentTarget;
this.quill = new this.Quill($(target).get(0), options);
$(target).children(".ql-editor").on("click", function(e) {
e.preventDefault();
});
}
this.quill.focus();
event.stopPropagation();
event.preventDefault();
}
}
For those who aren't using Angular:
$(document).on('click', '#editor', function() {
if (!$(this).hasClass('ql-container')) {
var quill = new Quill($('#editor').get(0), {
theme: 'snow'
});
quill.focus()
}
});
Its much easier:
var quills = [];
counter = 0;
$( ".init_quill_class" ).each(function() { // add this class to desired div
quills[counter] = new Quill($(".init_quill_class")[counter], {});
//quills[counter].enable(false); // if u only want to show elems
counter++;
});

Ckeditor initial disabled widget button state

I'm currently developing a Ckeditor 4 widget, but I run into the following issue. I'd like my widget button initially disabled untill an AJAX call is done and has a particular result.
The widget code:
editor.widgets.add('smartobject', {
dialog: 'smartobject',
pathName: lang.pathName,
upcast: function(element) {
return element.hasClass('smartObject');
},
init: function() {
this.setData('editorHtml', this.element.getOuterHtml());
},
data: function() {
var editorHtml = this.data.editorHtml;
var newElement = new CKEDITOR.dom.element.createFromHtml(editorHtml);
newElement.replace(this.element);
this.element = newElement;
}
});
The button is added as follows:
editor.ui.addButton && editor.ui.addButton('CreateSmartobject', {
label: lang.toolbar,
command: 'smartobject',
toolbar: 'insert,5',
icon: 'smartobject'
});
With this code it seems I can't configure the default disabled state.
So I searched in the docs, and thought I had the fix.
The following code addition seemed to work:
editor.$smartobjectPluginPreloadAvailableSmartobjectsPromise.done(function(availableSmartobjects) {
if (availableSmartobjects && availableSmartobjects.length > 0) {
editor.getCommand('smartobject').enable();
}
});
editor.addCommand('smartobject', new CKEDITOR.dialogCommand('smartobject', {
startDisabled: 1
}));
After adding this code the button is initially disabled, and enabled after the AJAX call is completed. So far so good. After a while I tried to add a new 'smartobject', but after completing the dialog config, the widgets 'data' function is not called. When editing an already existing smartobject by doubleclicking the element in the editor, still works..
I've probably mixed up different 'code styles' for adding a button, but I can't find the fix I need for my use case..
Any ideas how to fix this?
It seemed my idea was not possible through the ckeditor widget API and I combined some API logic which was not meant to be combined..
For now I simply fixed it by initially hiding the widgets button through CSS and adding a class to the button after the AJAX call succeeded:
.cke_button__createsmartobject {
display: none !important;
}
.cke_button__createsmartobject.showButton {
display: block !important;
}
And the JS logic:
editor.ui.addButton && editor.ui.addButton('CreateSmartobject', {
label: lang.toolbar,
command: 'smartobject',
toolbar: 'insert,5',
icon: 'smartobject'
});
// Enable the button if smartobjects are allowed for the itemtype of this editor.
editor.$smartobjectPluginPreloadAvailableSmartobjectsPromise.done(function(availableSmartobjects) {
if (availableSmartobjects && availableSmartobjects.length > 0) {
jQuery('.cke_button__createsmartobject').addClass('showButton');
}
});
It's not the solution I'm most proud of, but it works for now.

TinyMCE with knockout data binding would´t update

I have the TinyMCE WYSiWYG Editor presenting text depending on a selected object, but experience problem with the binding.
The first "instanciation" seems to work, but when choosing a new text from the drop down list of available text the editor goes blank and Firebug console tells me:
TypeError: D.hasChildNodes is not a function
...ute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r...
and
NS_ERROR_UNEXPECTED: Unexpected error
.../,"$1"));return false}});if(!u.getParam("accessibility_focus")){g.add(i.add(k,"a...
I have tried to recreate my code here: http://jsfiddle.net/xc4sz/1/
It´s not 100% but at least it does´t work. ;)
If I instead of clicking directly from text 1 to text 2 go via the "Choose option" the text is presented properly.
I guess it has to do with the "init" section below:
ko.bindingHandlers.tinymce = {
init: function (element, valueAccessor, allBindingsAccessor, context) {
var options = allBindingsAccessor().tinymceOptions || {};
var modelValue = valueAccessor();
var value = ko.utils.unwrapObservable(valueAccessor());
var el = $(element)
//handle edits made in the editor. Updates after an undo point is reached.
options.setup = function (ed) {
console.log(ed)
ed.onChange.add(function (ed, l) {
if (ko.isWriteableObservable(modelValue)) {
modelValue(l.content);
}
});
};
//handle destroying an editor
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
setTimeout(function () { $(element).tinymce().remove() }, 0)
});
//$(element).tinymce(options);
setTimeout(function () { $(element).tinymce(options); }, 0);
el.html(value);
},
update: function (element, valueAccessor, allBindingsAccessor, context) {
var $element = $(element),
value = ko.utils.unwrapObservable(valueAccessor()),
id = $element.attr('id');
//handle programmatic updates to the observable
// also makes sure it doesn't update it if it's the same.
// otherwise, it will reload the instance, causing the cursor to jump.
if (id !== undefined) {
var tinymceInstance = tinyMCE.get(id);
if (!tinymceInstance)
return;
var content = tinymceInstance.getContent({ format: 'raw' });
if (content !== value) {
$element.val(value);
//this should be more proper but ctr+c, ctr+v is broken, above need fixing
//tinymceInstance.setContent(value,{ format: 'raw' })
}
}
}
};
Depending on the versions of TinyMCE and jQuery that you are dependent on, you might like to try the custom binding I've recently rolled myself.
It's available on GitHub and NuGet
I found the issue. What happened was this:
you select Textbatch #1 and make some changes
you switch to Textbatch #2
the binding changes correctly from Textbatch #1 to #2
THEN the ed.onChange.add event handler kicks in and overwrites the content of the previous Textbatch #1 with that of the new Textbatch #2
Take a look at this updated fiddle (remove /show/light from the URL to get back to the editor). I had to inline select2.js, because Github does not allow files it hosts to be included remotely, causing your fiddle to fail.
The important part is in ko.utils.domNodeDisposal.addDisposeCallback:
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).tinymce().onChange.remove(changeHandler);
setTimeout(function () { $(element).tinymce().remove() }, 0)
});
I do not know why the removal of the editor is minimally delayed with a 0-second timeout, but there is probably a good reason for that. So all we do is remove the 'change' handler, so that the old editor cannot update the bound valueAccessor in your viewmodel anymore.
EDIT: I just noticed that I fixed your fiddle, but not necessarily your original exception… here is to hoping that the two were related.

Interested in tracking the events that get fired while user browses a site

I was wondering if it's possible to do as follows:
In my site I am using a lot of jQuery plugins that fire different events that I don't know about.
Is there a way - a program, a browser add-on, or something else - that I can browse the site and get a list of the exact javascript events that were fired with every click?
For example, I have a jQuery plugin that when I right click on any element a custom contextMenu shows and then when I click on one of the options other things come up. I need to know exactly what Javascript basic events were fired:
$('input:submit, button:submit').rightClick(function (e) {
$(this).contextMenu('contextMenuInput', {
'Capture This': {
click: function (element) { // element is the jquery obj clicked on when context menu launched
doSomething();
},
klass: "kgo" // a custom css class for this menu item (usable for styling)
},
'Create List': {
click: function (element) {
},
klass: "kfilter kdisabled"
},
'Collect Data': {
click: function (element) {
},
klass: "kcapture kdisabled"
}
},
{ disable_native_context_menu: true }
);
});
Does anyone have any idea?
You can use the following code to show events currently bound ....
here is an example of using this code : http://jsfiddle.net/manseuk/CNjs3/
(function($) {
$.eventReport = function(selector, root) {
var s = [];
$(selector || '*', root).andSelf().each(function() {
var e = $.data(this, 'events');
if(!e) return;
s.push(this.tagName);
if(this.id) s.push('#', this.id);
if(this.className) s.push('.', this.className);
for(var p in e) s.push('\n', p);
s.push('\n\n');
});
return s.join('');
}
$.fn.eventReport = function(selector) {
return $.eventReport(selector, this);
}
})(jQuery);
Use it like this ->
// all events
alert($.eventReport());
// just events on inputs
alert($.eventReport('input'));
// just events assigned to this element
alert($.eventReport('#myelement'));
// events assigned to inputs in this element
alert($.eventReport('input', '#myelement'));
alert($('#myelement').eventReport('input')); // same result
// just events assigned to this element's children
alert($('#myelement').eventReport());
alert($.eventReport('*', '#myelement'); // same result
Updated as per comments
If you want to see what is bound to these events this is an excellent tool -> http://www.sprymedia.co.uk/article/Visual+Event
It's not quite what your looking for, but with firebug, you can log events for a given DOM element.
You can do this by right clicking on the element in the html tab and clicking log events:
The event log:
You may also find the firebug extension "EventBug" useful:
http://getfirebug.com/wiki/index.php/Firebug_Extensions#Eventbug
http://www.softwareishard.com/blog/firebug/eventbug-alpha-released/

Categories

Resources