I am trying to set the default style applied to the P elements that are automatically created when a user enters the blank editing area. I've spent many hours searching for an answer but have not found anything that works. The requirements are:
Style has to be inline, no stylesheet
No user interaction, no format/style plugin to click
When the user clicks in the editing area and starts typing, I want the style to be applied and visible automatically. Surely there is a way to accomplish this?
The closest I have gotten is by using the htmlFilter, like this :
p_rule = {
elements : {
p : function(element) {
if (element.attributes.style === undefined) {
element.attributes.style = "color: #0000ff;";
}
}
}
};
ev.editor.dataProcessor.htmlFilter.addRules(p_rule);
But the new style is not automatically visible.
It does become visible if the user goes into source editing mode and back to WYSIWYG but I want it to be automatic.
I tried using updateElement() in the filter function, but it does not work and creates infinite recursion:
p_rule = {
elements : {
p : function(element) {
if (element.attributes.style === undefined) {
element.attributes.style = "color: #0000ff;";
CKEDITOR.instances['editor1'].updateElement();
}
}
}
};
ev.editor.dataProcessor.htmlFilter.addRules(p_rule);
(I guess updateElement() triggers the filter)
If I use setData(getData()) from an event I can strangely get the textarea to update with the changes the filter applied, for example:
CKEDITOR.instances['editor1'].on('blur', function() {
CKEDITOR.instances['editor1'].setData(CKEDITOR.instances['editor1'].getData());
});
But that too requires user interaction. Using the "change" event creates recursion.
I am new at CKEditor and obviously I'm missing something on how the filter works in relation to what is currently being displayed in the textarea.
Any CKEditor guru out there? Help!
Thanks
I really advise not to go this way. You'll find yourself fighting with countless issues, like what if you copy&paste, what if you change format to h1 and then back, what if you create a list item and then convert that into a paragraph, etc. etc. There are really dozens of those. You'd need to rewrite half of the editor.
The way to handle this in CKEditor 4 is to rethink this:
Style has to be inline, no stylesheet
Inside CKEditor you clearly need to use a stylesheet. I presume though that you want the inline styles in the output. So what I would propose is to:
Write htmlFilter rule which adds this style to every paragraph.
Write dataFilter rule which removes this style from every paragraph.
The second rule is needed so if you save the data and then load it back to the editor, the styles do not pollute it.
PS. CKEditor 5 will separate data model from rendering (the view) so you'll be able to render paragraph as you wish without affecting how other features interact with it. Read more about CKEditor 5 in this article.
Related
I have a plugin which need to show a (Modal) dialog each time the user double click on a word.
Detecting double click is no problem, but the exact fields/values in the dialog depends on exactly which word the user clicked on, and some mutable global state. So I can't create the dialog until the moment before I need to show it. And here is the problem: How do I do that?
Right now I use this code:
var dialogName="uniqueDialog" + counter++;
CKEDITOR.dialog.add(dialogName,function(editor) {
// Creating dialog here.
});
CKEDITOR.instances.editor.openDialog(dialogName);
This works, but having to add a uniquely named dialog, just to show it once and then newer use it again seems really really wrong. Also I fear this will keep using resources since the dialogs are newer removed(I could not find any remove method).
So my question is: Is there a better way to dynamical create and show a "one use" dialog?
Update:
If bootstrap is not allowed then maybe an addFrame version of the dialog is acceptable. This could then refer to a html file that can load from parameters.
NB: The plunkr only works, if you fork and edit it, otherwise it will give you a 404 for the template.
Here is a quick plunkr:
plunky
And here is the plugin in question:
CKEDITOR.plugins.add( 'insertVariable', {
requires: ['iframedialog'],
icons: 'insertvariable',
init: function( editor ) {
editor.addCommand( 'varDialog', new CKEDITOR.dialogCommand( 'varDialog' ) );
CKEDITOR.dialog.addIframe('varDialog','varDialog','sample.html?var='+item,500,400);
editor.ui.addButton( 'insertVariable', {
label: 'Insert Variable',
command: 'varDialog',
icon: this.path + '<insert gif>'
});
}
});
Obviously you are not creating dialogs anymore with different content, but you are referring to another piece of html, that can change. I've kept the bootstrap thing in there as well for reference.
I made one final edit, that will show the current contents. So I think that is roughly what you want. Check it out.
Previous Answer
If you are prepared to use bootstrap, then you can do no worse than check out their modal dialog, which can be just be shown and hidden at will.
http://getbootstrap.com/javascript/#modals
It's simple and cuts down any need to keep creating your own dialog. The dialog won't be one use type, but you will set the defaults as necessary. The varying content link is here:
http://getbootstrap.com/javascript/#modals-related-target
That would be the quickest way to get this going. It all depends on whether you want to use this framework. As CKEDITOR is already using JQuery it is an option worth considering.
I've added a plugin that allows the user to add a specially styled div via a dialog. The issue now is, this element should not be clickable inside the edtior. The problem is the users manage it to click inside the div and enter text there and by this screw it up.
I've already spent some time searching the documentation but couldn't find the right approach to do this yet. I'm not asking for code, just some advice how to do it, a pointer to the right API method would be good enough for me. I guess I can somehow access the elements or intercept an users click and prevent them from adding something to my element somehow, I just couldn't yet figure out how to do it.
Use the Widget System.
Widget Tutorial.
Demos.
I've finally managed to get this done by making the elements content not editable. When I create the element in my dialog:
hrElement.setAttribute('contenteditable', false);
When loading the plugin:
init: function (editor) {
editor.on('contentDom', function () {
var stiching = (this.document.getElementsByTag('div'));
console.log(stiching);
for(var i=0;i<stiching.count();i++){
if (stiching.getItem(i).hasClass('stitching')) {
stiching.getItem(i).setAttribute('contenteditable', false);
}
}
});
}
I'm pretty sure this is not the most best solution (don't like to iterate over the elements) but at least it works for me now. Any suggestions how to improve it for future cases are welcome.
If the source of something I write in my CKEDITOR looks like this:
This is my text. <strong>This part is bold.</strong> This part isn't.
I can highlight the bolded part and unbold it by pressing CTRL+B. However, if I add a class to that strong tag (due to another plugin I'm working on), I can only unbold clean strong tags - no attributes, styles, or classes. For example, consider this scenario:
This is my text. <strong>This part is bold.</strong> This part isn't. <strong class="whatever">This part is bolded AND has a custom class.</strong>
Only the first bolded segmented will be unbolded - the 2nd is pretty much stuck as is until I remove the ".whatever" class. Is there any way to get it to ignore strong tags with classes, and just do them regardless of what other attributes they have? I'm guessing it has to do with that "Advanced Content Filter" or something, but I can't figure out what.
After much hairpulling, I (think) I have the answer. In the CKEDITOR style definition, an applied style (ex, a strong tag) needs to have all of its attributes parsed through the content filter. If an attribute that is not dealt with by this filter remains when it comes time to actually remove the textNode from the style tags and replace it back into the parent element, the tags (and thus the style) will NOT be removed if there are any attributes remaining on the element. There is a (very poorly) documented workaround to this: the alwaysRemoveElement property can be set to true on the style DEFINITION (why the definition, and not the style itself, I have no idea).
Long story short, a little snippet of code that will force the removal of all style tags, even if their attributes don't match exactly with the filter. Hopefully it doesn't cause bugs somewhere else...
//this = Your Editor Instance
this.data.editor.on( 'instanceReady', function(){
//Filter through the existing contentRules, looking for styleCommands
$.each(this.activeFilter.allowedContent, function(i,v) {
var name = v.featureName, command = this.commands[v.featureName];
if (name && command && command.contentForms && command.style) {
command.style._.definition.alwaysRemoveElement = true;
}
}.bind(this));
}.bind(this));
As the previous answer, just add this in the config:
CKEDITOR.config.coreStyles_bold : { element: 'strong', overrides: 'b' ,alwaysRemoveElement: true},
It was also difficult for me to find this workaround, in my case I was adding an id to the strong element.
I am using the following directive to create a ckEditor view. There are other lines to the directive to save the data but these are not included as saving always works for me.
app.directive('ckEditor', [function () {
return {
require: '?ngModel',
link: function ($scope, elm, attr, ngModel) {
var ck = ck = CKEDITOR.replace(elm[0]);
ngModel.$render = function (value) {
ck.setData(ngModel.$modelValue);
setTimeout(function () {
ck.setData(ngModel.$modelValue);
}, 1000);
}; }
};
}])
The window appears but almost always the first time around it is empty. Then after clicking the [SOURCE] button to show the source and clicking it again the window is populated with data.
I'm very sure that the ck.setData works as I tried a ck.getData and then logged the output to the console. However it seems like ck.setData does not make the data visible at the start.
Is there some way to force the view window contents to appear?
You can call render on the model at any time and it will simply do whatever you've told it to do. In your case, calling ngModel.$render() will grab the $modelValue and pass it to ck.setData(). Angular will automatically call $render whenever it needs to during its digest cycle (i.e. whenever it notices that the model has been updated). However, I have noticed that there are times when Angular doesn't update properly, especially in instances where the $modelValue is set prior to the directive being compiled.
So, you can simply call ngModel.$render() when your modal object is set. The only problem with that is you have to have access to the ngModel object to do that, which you don't have in your controller. My suggestion would be to do the following:
In your controller:
$scope.editRow = function (row, entityType) {
$scope.modal.data = row;
$scope.modal.visible = true;
...
...
// trigger event after $scope.modal is set
$scope.$emit('modalObjectSet', $scope.modal); //passing $scope.modal is optional
}
In your directive:
ngModel.$render = function (value) {
ck.setData(ngModel.$modelValue);
};
scope.$on('modalObjectSet', function(e, modalData){
// force a call to render
ngModel.$render();
});
Its not a particularly clean solution, but it should allow you to call $render whenever you need to. I hope that helps.
UPDATE: (after your update)
I wasn't aware that your controllers were nested. This can get really icky in Angular, but I'll try to provide a few possible solutions (given that I'm not able to see all your code and project layout). Scope events (as noted here) are specific to the nesting of the scope and only emit events to child scopes. Because of that, I would suggest trying one of the three following solutions (listed in order of my personal preference):
1) Reorganize your code to have a cleaner layout (less nesting of controllers) so that your scopes are direct decendants (rather than sibling controllers).
2) I'm going to assume that 1) wasn't possible. Next I would try to use the $scope.$broadcast() function. The specs for that are listed here as well. The difference between $emit and $broadcast is that $emit only sends event to child $scopes, while $broadcast will send events to both parent and child scopes.
3) Forget using $scope events in angular and just use generic javascript events (using a framework such as jQuery or even just roll your own as in the example here)
There's a fairly simple answer to the question. I checked the DOM and found out the data was getting loaded in fact all of the time. However it was not displaying in the Chrome browser. So the problem is more of a display issue with ckEditor. Strange solution seems to be to do a resize of the ckEditor window which then makes the text visible.
This is a strange issue with ckeditor when your ckeditor is hidden by default. Trying to show the editor has a 30% chance of the editor being uneditable and the editor data is cleared. If you are trying to hide/show your editor, use a css trick like position:absolute;left-9999px; to hide the editor and just return it back by css. This way, the ckeditor is not being removed in the DOM but is just positioned elsewhere.
Use this java script code that is very simple and effective.Note editor1 is my textarea id
<script>
$(function () {
CKEDITOR.timestamp= new Date();
CKEDITOR.replace('editor1');
});
</script>
Second way In controller ,when your query is fetch data from database then use th
is code after .success(function().
$http.get(url).success(function(){
CKEDITOR.replace('editor1');
});
I know, that this thread is dead for a year, but I got the same problem and I found another (still ugly) solution to this problem:
instance.setData(html, function(){
instance.setData(html);
});
I inherited a web app that uses Dojo 1.5 and the template toolkit. I am enjoying learning dojo but it's at a slow pace.
Initially when bringing up our web form, we'll have a list of files on the right side of the page like so....
AAA_text
BBB_text_1
BBB_text_2
CCC_text
....
....
On the left side we have a search box that asks for the subset of file to use. Normally we would just type in "AAA" and then the div on the right side would find those files that match and display them after you press the "Search" key below the box.
What we are looking to do is to eliminate the "Search box" and have the list of files matching "AAA" to come up in the right side div as "AAA" is being typed, (or "BBB" or "CCC", etc).
I suppose in a nutshell it's the equivalent having the "Search" button pressed after every key is typed in the Search box.
Does this sound like a realistic goal or even possible? The code itself uses a ton of Template Tookit so I'm not looking to do any major rewrite.
If I am not making myself clear, let me know. I can elaborate for clarity. Many many thanks! Janie
EDIT: OK, I have solved a good deal of my problem so far and as it turns out, as so many of these things have a propensity to do, that what I am really needing is to get clear on how to make autocomplete work. Which is to say that I have a data source for my text box but not really sure how to tie it to the text box. I have a dojo.xhrPost routine that can handle grabbing the values.
It looks like this....
dijit.byId('files_matching').getValue(),
Googling dojo autocomplete examples gives me a zillion links and none of which are proving helpful. So I suppose my questions have transitioned to....
1. Can you even use autocomplete on a mere text box (I've seen links that say that you can only use it on combo boxes)
2. Is there a link out there somewhere that describes/shows in detail how to tie a dojo text box to a data source using dojo.xhrPost.
I am so close to solving this and I still seem to have a gaping chasm in front of me.
It's difficult to say for sure without seeing your code but if you don't have one already, I would recommend to create an ItemFileReadStore or something similar to start with. That way you can query that store locally on the client without having server requests after every key stroke.
It could look something like this:
var file_store = new dojo.data.ItemFileReadStore({ data: {
items: [{ name:"AAA_text" },
{ name:"AAA_text_1" },
{ name:"BBB_text_2" }]
}});
When you have that in place you can call a function from your text input's onChange event:
<input type="text" onchange="query_store(this.value);" />
And then you handle to actual query from the function called from the onchange event:
var query_store = function(search_for) {
var my_query = { name: search_for+"*" }; // * for wildcard match
completed = function(items, result){
for(var i = 0; i < items.length; i++){
var value = file_store.getValue(items[i], "name");
alert(value); // Instead of alert, you can save the values to your div.
}
};
file_store.fetch({ query: my_query, onComplete: completed });
}
A lot of good information about this can be found here
Hope this is at least a little helpful.