Clicking right side of inserted CKEditor5 element results in extra empty element - ckeditor

I'm trying to insert a button in CKEditor5. Most of it is working OK but when I click on the right side of an inserted element it results in an extra empty element.
Here is the YouTube video of the behavior: https://www.youtube.com/watch?v=XXLyJcJJk00
...And some of the code to insert the View Fragment:
editor.model.change(writer => {
const viewFragment = editor.data.processor.toView('' + value.text + '');
const modelFragment = editor.data.toModel(viewFragment);
writer.model.insertContent(modelFragment);
writer.setSelection( editor.model.document.getRoot(), 'end' );
});
You can see there are ⁠ ASCII characters in there.
I've been stuck on this for several days now so any help would be much appreciated.

Related

How to I focus on an element after it is added in CKEditor?

I am using CKEditor 4.4.1 with CKEDITOR.config.enterMode set to ENTER_P.
I am adding a new paragraph programmatically and then moving the cursor to the new element:
var element = new CKEDITOR.dom.element('p', editor.document);
element.appendBogus();
var range = editor.createRange();
range.setStartAt(referenceNode, CKEDITOR.POSITION_AFTER_END);
range.collapse(true);
editor.editable().insertElement( element, range );
range.moveToElementEditStart( element );
editor.getSelection().selectRanges( [ range ] );
This creates and inserts the element in the correct place. However, for some reason, the cursor is not placed in the newly created element.
Why is this happening?
Because I was using a elements that were not part of CKEditor to trigger the insertion, the CKEditor instance was losing focus.
In addition to the code above, I also had to do editor.focus().

How to access ColumnChooser's API to toggle the $ui.multiselect components

Is there a way to access the jqGrid's columnChooser's multiselect API objects? I need to call those objects to update the data on ColumnChooser pop-up dialog.
In the snapshot below, is the customized ColumnChooser pop-up dialog. The HTML combo when selected/changes would then change the $ui.multiselect sections (avaiable & unavailable columns).
In the 2nd snapshot below is the view souce using Firefox's firebug and it doesn't have me the option to toggle the 2 columns.
Is there a way to access ColumnChooser's API instead, to manually toggle the columns on the ColumnChooser but not touch the jqGrid's columns? How can I accomplish this?
Thanks...
[Snapshot #1]...
[Snapshot #2]...
After a few days of Google surfing, piecing together sample script from lots of example api and coming up with JQuery to find the html path to a clickable anchor link.
Updated Solution
The "parmSavedBuildDataFormValueColumnModelSetting" value is the colModel's name you passed on to it, whether it be the values you saved from the database or cookie, or anything for populating the MultiSelect "selected" box-windows.
function JqgridColumnChooserSavedBuildsRecordsMultiselectDialogToggler(parmSavedBuildDataFormValueColumnModelSetting) {
//Re-order the $.ui.multiselect's columns in 2 boxed-windows (available & unavailable)...
//http://stackoverflow.com/questions/10439072/add-remove-column-handler-on-jqgrid-columnchooser
//http://stackoverflow.com/questions/11526893/jqgrid-columnchooser-unselected-columns-on-the-right-in-alphabetical-order
var $jqgridColumModelSetting = $('#' + jqgridSpreadsheetId).jqGrid('getGridParam', 'colModel');
var $jqgridColumNameSetting = $('#' + jqgridSpreadsheetId).jqGrid('getGridParam', 'colNames');
//Remove all of the "selected" columns having "-" icon...
//09/11/2013 - This "selected" columns with hyperlink click event does not work too well as it cause 1/3 of all columns not to be visible, so let' use the "Remove All" hyperlink instead...
//#$('#colchooser_' + jqgridSpreadsheetId + ' ul.selected a.action').click();
$('#colchooser_' + jqgridSpreadsheetId + ' div.ui-multiselect div.selected a.remove-all').click();
//Add back the "available" columns having "+" icon, only the one that match the SavedBuilds data...
$.each(parmSavedBuildDataFormValueColumnModelSetting.split('|'), function (i1, o1) { //##parmSavedBuildDataFormValueColumnModelSetting.forEach(function (i, o) {
$.each($jqgridColumModelSetting, function (i2, o2) {
if (o2.name == o1) {
$('#colchooser_' + jqgridSpreadsheetId + ' ul.available li[title="' + $jqgridColumNameSetting[i2] + '"] a.action').click();
return false; //This only break the foreach loop [while "return true" resume the loop] (This have nothing to do with function's returned value)...
}
});
});
}

Modifing Selection Ckeditor

At the moment i have the following problem, i'm applying span tags with the applyStyle Method from CKEDITOR 4.x. But when a span is partial selected and i execute the applyStyle method a new span will be made with the selection, but the other half of the selected span isn't restored and loses his span.
First Question: Is it possible to prevent partial selection of a certain element?
IF NOT My Second Question: Is it possible to extend the Selection only on one side, the side where the span(With a certain class or attribute) is partial selected. So that it will be fully selected for processing.
A Example:
This is 'my text <span class"testClass">, This' is </span> Other Text
And now we want a solution to create:
This is <span class"testClass2"> my text, This</span> <span class"testClass"> is </span> Other Text
Please take notice of the following:
The hard part in this is to maintain the html structure. when half of the selection is in an other block level element, it may not brake! That is the reason that i started using the applyStyle method.
First Question: Is it possible to prevent partial selection of a certain element?
Hmm... You can check placeholder plugin's sample - it uses non-editable inline elements to create those placeholders which at least on Chrome cannot be partially selected. Though, I think it's not a satisfying solution for you :)
Another possible solution is using editor#selectionChange event on which you can check if one of selection ends is located inside that element and if yes, set it before or after that element. It'd look like (I haven't tested this code, it's just a proto):
editor.on( 'selectionChange', function( evt ) {
var sel = evt.data.selection,
range = sel.getRanges()[ 0 ];
if ( protectedElement.contains( range.startContainer ) || protectedElement.equals( range.startContainer ) )
range.setStartAt( protectedElement, CKEDITOR.POSITION_BEFORE_START );
if ( protectedElement.contains( range.endContainer ) || protectedElement.equals( range.endContainer ) )
range.setEndAt( protectedElement, CKEDITOR.POSITION_AFTER_END );
sel.selectRanges( [ range ] );
} );
Although, this kind of solutions are always dangerous and can cause many unpredictable situations. But it may be worth checking it.
Back to the root of your problem - I understand that you want to create styles which work on the same level - i.e. only one can be applied in one place. This isn't possible using styling system. You would have to prepare range before applying style. The code would be similar to the selectionChange listener - you check if ends are anchored in style element, if yes you need to move range's ends out of it. The only question is how to exclude entire element from range in this situation:
<p>foo[bar<span class="st1">bom</span>bim]foo</p>
The result should be two ranges:
<p>foo[bar]<span class="st1">bom</span>[bim]foo</p>
Unfortunately current range's API does not include helpful method like range#exclude, therefore you need to implement yours. I would try doing this with walker. Iterate from range's start to end and remember all style elements. If you'll do this in both directions you'll gather also partially selected elements on both ends, so the first step I described will be unnecessary. When you'll have list of elements which you want to exclude from range, then you just need to create ranges at both ends and between these elements - this part should be easy. Element#getPosition will be helpful, but you'll need to check its code to understand how to use it because it isn't documented.
I have been looking and trying for hours. And chose to make an enlarge function myself to expand the selection. I made my own enlarge/expand function as i wanted to have more control which the enlarge of CKEDITOR doesn't provide.
The code:
//Vars
var firstNode = range.startContainer.getParent();
var lastNode = range.endContainer.getParent();
//Make end Get full if is tcElement
if(lastNode.type === CKEDITOR.NODE_ELEMENT && lastNode.getName() === "myElement")
{
range.setEndAfter(lastNode);
}
//Make end Get full if is tcElement
if(firstNode.type === CKEDITOR.NODE_ELEMENT && firstNode.getName() === "myElement")
{
range.setStartBefore(firstNode);
}
range.select();
Other nice piece of code, which isn't very hard but can be useful for other people.
This code i used to split the code in 2 or 3 parts.. where part 1 and 3 are the partial selection if existed.
Spliting to multiple ranges
//Vars
var newRanges = [];
var allWithinRangeParent = range.getCommonAncestor().getChildren();
var firstNode = range.startContainer;
var lastNode = range.endContainer;
var firstNodeStart = range.startOffset;
var lastNodeEnd = range.endOffset;
//TODO make if to check if this needs to be made.
//make end partial
var newEndRange = new CKEDITOR.dom.range( editor.document );
newEndRange.selectNodeContents( lastNode );
newEndRange.endOffset = lastNodeEnd;
newRanges.push(newEndRange);
//TODO make if to check if this needs to be made.
//Make start partial
var newStartRange = new CKEDITOR.dom.range( editor.document );
newStartRange.selectNodeContents( firstNode );
newStartRange.startOffset = firstNodeStart;
newRanges.push(newStartRange);
//Make center selection.
var tempRange = new CKEDITOR.dom.range( editor.document );
tempRange.setStartBefore(firstNode.getParent().getNext());
tempRange.setEndAfter(lastNode.getParent().getPrevious());
newRanges.push(tempRange);
selection.selectRanges(newRanges);

Getting row data from SlickGrid when using the pager plugin

I'm using SlickGrid with the pager plugin. My intent is to display line items in SlickGrid and allow the user to double click on a row to get more detail. I have code that seems to work fine but it feels as though I'm doing this the hard way:
grid.onDblClick.subscribe(function(e, args) {
var selectedIndex = parseInt(grid.getSelectedRows());
var pageInfo = dataView.getPagingInfo();
var pageSize = pageInfo.pageSize;
var pageNum = pageInfo.pageNum;
var idx = pageSize*pageNum + selectedIndex;
var asset = rows[idx].assetName;
alert("Selected Asset is " + asset);
});
I've seen other questions posted where people did a grid.getData()[selectedIndex] or a dataView.getItemById(selectedIndex), but since selectedIndex is always a 0 to something number, I always got data from the first page in my list regardless of which page I was on. Is there a direct way to map a selected index on a page to the actual row in the data array? Again, the code above seems to work fine - just feels like I'm missing an obvious method somewhere.
grid.onDblClick.subscribe(function(e, args) {
alert("Selected asset is " + args.item);
alert("Or " + grid.getData().getItem(args.row));
alert("Or " + grid.getDataItem(args.row));
});

jqGrid inline delete: selected row "selrow" is incorrect

I have a in-line delete button, I want to append more data to the delete message pop-up like this:
"Delete selected row with code = 7 ?"
I'm using the following in the delOptions:
beforeShowForm: function ($form) {
var sel_id = $("#list").jqGrid('getGridParam', 'selrow');
$("td.delmsg", $form[0]).html("Delete record with <b>code=" + $("#list").jqGrid('getCell', sel_id, 'cd') + "</b>?");}
The problem is If I click on the delete button without first clicking on any part of the row, the selrow is either null or it gets the previously selected row not the currently selected!
How do I make the row selected when clicking on the trash bin icon?
Any help is appreciated
I suppose that you use the example which I posted in the old answer. It was written in case of usage of the Delete button (the part of form editing) from navigator bar.
There are one hidden row in the Delete dialog which could help you. Try this one
beforeShowForm: function ($form) {
// get comma separated list of ids of rows which will be delete
// in case of multiselect:true grid or just id of the row.
// In the code below we suppose that single row selection are used
var idOfDeletedRow = $("#DelData>td:nth-child(1)").text();
$form.find("td.delmsg").eq(0)
.html("Delete record with <b>code=" +
$(this).jqGrid('getCell', idOfDeletedRow, 'cd') + "</b>?");
// REMARK: in old versions of jqGrid you can't use $(this) and
// will have to use something like $("#list")
}

Resources