ckeditor plugin strinsert: how to set custom strings after initialization - ckeditor

I'm trying to implement fixed placeholders to ckeditor by using plugins placeholder and strinsert as described here.
Unfortunately I'm not able to alter strings array of plugin strinsert dynamically.
This lines in config.js should work but it does not:
CKEDITOR.editorConfig = function( config ) {
config.strinsert_strings = [
['[[foo]]']
];
};
Maybe this plugin does not support altering strings array after init?
How could I do to present users different placeholders in different ckeditor instances without cloning strinsert plugin x times?
NB: I'm using newest versions of all of them.

Like suggested use the placeholder rplugin instead. Just edit the file ckedit/plugins/placeholder/dialogs/placeholder.js. Change the the element type to select and add the required value to items like in the following example:
contents: [
{
id: 'info',
label: generalLabel,
title: generalLabel,
elements: [
// Dialog window UI elements.
{
id: 'name',
type: 'select',
style: 'width: 100%;',
label: lang.name,
items:[
['ONE'],
['TWO'],
['THREE']
],
// SNIP...

You can set custom placeholders dynamically by using dialogDefinition event.
I did it like this (html attribute data-placeholders specifies the valid placeholders in form of "placeholder1,different text to show=placeholder2,another text=placeholder3"
CKEDITOR.on ('dialogDefinition', function (e) {
// Check if the definition is from the dialog window you are interested in (the "Link" dialog window).
if (e.data.name == 'placeholder') {
var $textarea = $(e.editor.element.$),
placeholders = $textarea.attr("data-placeholders"),
tab, ff, i, a;
//placeholders = [['placeholder1'],['Text to show', 'placholdervalue']];
if (placeholders && placeholders.length) {
// convert placeholders from desc1=val1,desc2=val2,val3,... format to array(array(desc,val))
placeholders = placeholders.split(",");
// ensure placeholders is array of arrays with exact 2 members
for (i = 0; i < placeholders.length; i++) {
a = placeholders[i].split("=");
if (a.length < 1) a[1] = a[0];
placeholders[i] = a;
}
tab = e.data.definition.getContents ('info');
// Set the default value for the URL field.
ff = tab.get ('name');
ff['type'] = 'select';
ff['items'] = placeholders;
}
}
});

For strinsert, I got it to work by editing its plugin.js, get rid of the strings declaration and replace with:
var strings = editor.config.strinsert_strings;
Then in your html before creating CKEDITOR:
CKEDITOR.config.strinsert_strings = [];
CKEDITOR.config.strinsert_strings.push(['myvalue1', 'myname1', 'mylabel1']);
CKEDITOR.config.strinsert_strings.push(['myvalue2', 'myname2', 'mylabel2']);
CKEDITOR.replace('mytextarea');

Related

Custom template in kendo-ui grid column with inconsistent data type

I need to use kendo-ui grid for data editing. Problem is that every possible item in returned response is string, but which contains other types of value (eg Value = "true" or Value = "32%" or Value = "[0:standard, 1:advanced]").
So I need to set up template on grid to correspond different data type within string.
So for true/false i have to have checkbox, for 32% it should provide text box but with percent validation, for array response it needs to be a drop down.
I managed to set up drop down and text box options using editor, but I cannot make checkbox to handle properly in any way. Checkbox is displayed as expected, but whatever I try it won't bind data to the grid after grid is saved. (it is always not checked, regardless of value)
Here is code snippet of column "value" and what I used for template (item.type === "3" is boolean).
field: 'value',
title: 'value',
headerAttributes: {
'class': 'table-header-cell'
},
template: function (item) {
if (item.type === "3") {
var boolValue = (/true/i).test(item.value);
item.value = boolValue;
return '<input id="' + item.name+ '" type="checkbox" #= value ? \'checked="checked"\' : "" # class="chkbx" />';
} else {
return ''; //this will follow for other types
}
},
Thanks in advance.
When the template definition is a function, you don't need to use the # markers to differentiate between markup and javascript like you do when you are defining a kendo template using kendo's template language or a string directly.
This is because inside the function it is always javascript and the # markers are only directives in the kendo templating language.
So, simplify your template to just:
template: function (item) {
return '<input class="chkbx" id="' + item.name + '" type="checkbox"' + (item.value ? 'checked="checked"' : '') + '/>';
}
I've left out the other datatype handling for simplicity.
Then, you need to add code to push the checkbox changes into the grid's datasource when they occur:
$("#grid").on("click", ".chkbx", function () {
var $checkBox = $(this),
checked = $checkBox.is(":checked"),
dataItem = grid.dataItem($checkBox.closest("tr"));
dataItem.set("value", checked);
});
This is a technique that I am currently using in production code.
Dojo example
It may also be possible to use the kendo MVVM bindings in your template for a more elegant solution instead of the explicit click handler, but I'd have to experiment more with that to figure it out.

Ckeditor issue calling setData during onchange event

I am trying to create a plugin that works similar to the tagging feature here on Stack Overflow. The plugin adds an onchange event to the editor and than checks the data to see if the user entered a tag and replaces any tags found with a div.
CKEDITOR.plugins.add('tagit', {
icons: '',
init: function (editor) {
var tags = ['MyTag'],
tokens = [];
editor.on('change', function (event) {
var tokenUpdated = false;
tokens = tokenize(event.editor.getData());
for (var tokenIndex = 0; tokenIndex < tokens.length; tokenIndex++) {
var token = String(tokens[tokenIndex]);
if (!token.match(/tagit/gmi) && tags.some(function (tag) { return token.indexOf(tag) >= 0; })) {
tokens[tokenIndex] = '<div class="tagit">' + tokens[tokenIndex] + '</div>';
tokenUpdated = true;
}
}
if (tokenUpdated) {
event.editor.setData(tokens.join(''));
}
});
var tokenize = function (data) {
var match = '(<div class="tagit">.*?<\/div>)';
for (var i = 0; i < tags.length; i++) {
match += '|(' + tags[i] + ')';
}
var re = new RegExp(match, "gmi");
return data.split(re);
}
}
});
The problem is when I call setData the change event is fired again and event.editor.getData() returns the html before I called setData. Is the change event fired before the data has actually been set? There's an option internal that I tried setting to true but than the data doesn't appear to be updated.
You are changing editors content so it's natural that change event will be called with editor.setData function. TBO I think that your implementation has a much important problem than circular call - you are comparing HTML content by regex. It's bad practice and you will encounter more problems during this implementation.
This feature is not obvious and requires working with document selection, not simply querying its content (also for performance reasons).
But I have a good information. With CKEditor 4.10 we are shipping new plugins which can easily be used to create feature you are talking about - especially textmatch and textwatcher. Mentioned plugins will be shipped alongside with autocomplete and mentions plugins. You can read more about our progress on GH:
Mentions: https://github.com/ckeditor/ckeditor-dev/issues/1703
Autocomplete: https://github.com/ckeditor/ckeditor-dev/issues/1751
4.10 release is set on 26 June but it could change, check GH milestones for updates.
After release, I can provide some example implementation for your feature - but I'm sure that with new plugins it will be easy as pie.

Kendo UI TreeView: Add only nodes not exisisting in the view

I have tree view on a page which gets data from a ComboBox and a multiselect. The ComboBox contains the name of each ingredient and the multiselect contains the possible amount types which are then used as names for all their child nodes.
The tree looks something like that:
Ingredient 1
100mg
200mg
Ingredient 2
50mg
100mg
Everything works fine except I can add the same value twice because I am not able to validate if a node already exists.
Here is the function I am using to add new elements:
var addElement = function () {
var treeview = $("#ingredientTree").data("kendoTreeView");
var multiselect = $("#ingredientAmount").data("kendoMultiSelect");
var ingredientToAdd= $("#ingredient").val();
// I allways get an empty array at this point.
var exinstingIngredient= treeview.findByText(ingredientToAdd);
var children = new Array();
var amount = multiselect.value();
for (var j = 0; j < amount.length; j++) {
children.push({ text: amount[j] });
}
// it allways adds the items because the length is allways 0
if (exinstingIngredient.length === 0) {
treeview.append({
text: ingredientToAdd,
items: children
});
}
}
I don't understand why it can't find the existing element even I set its name as text and search for this text.
edit:
Here we have the treeview:
#(Html.Kendo().TreeView().TemplateId("treeview-template").Name("ingredientTree"))
That is the source of the ingredients, it handles just plain strings:
#(Html.Kendo().ComboBox()
.Name("ingredient")
.DataSource(source => source.Read(r => r.Url(Url.HttpRouteUrl("DefaultApi", new { controller = "InternationalIngredients" }))))
.Events(events => events.Change("onIngredientChanged"))
)
Following you find the source for amounts, which handles strings to:
#(Html.Kendo().MultiSelect()
.Name("ingredientAmount")
.DataSource(source => source.Read(read => read.Url(Url.HttpRouteUrl("DefaultApi", new { controller = "InternationalIngredientAmount" })).Data("getIngredient")).ServerFiltering(true)))
This is a function to determine the selected ingredient for the service call:
function getIngredient() {
return { ingredient: $("#ingredient").val() }
}
I've found the reason for my problem now. findByText seems to check the Content of the nodes span with class "k-in". Unfortunatly, this Content is modified when you add a template as described here. So if you want to find an element with template, you should use findById or you define your template in a way you can use jQuery.

jqGrid Make a Row readonly

I have a grid setup using form editing. I want the the user to be able to edit only some of the rows. As a start, I figured the easiest way to do this was to have a column (probably hidden) in my server query and XML that denotes the Access or Role the user has. So essentially the grid now has a column "Access Role" with 'Y' or 'N' for each row. (where Y = user can edit, N = View/readonly)
I've tried a couple things to implement this. The best I've come up with is using the rowattr function, but my use is flawed since it hides the row in the grid (I don't want it hidden, just readonly):
function (rd) {
console.log('Row = '+rd.WWBPITM_SURROGATE_ID);
if (rd.ACCROLE === "N") {
console.log('RowAttr '+rd.ACCROLE);
return {"style": "display:none"};
}
This might be a start, but I'm not sure where to go from here and I'm not sure if I'm barking up the wrong tree with using rowattr.
I also tried using setCell in a loadComplete function, like this:
function GridComplete() {
var grid = $('#list1');
var rowids = grid.getDataIDs();
var columnModels = grid.getGridParam().colModel;
console.log('Check ACCROLE');
// check each visible row
for (var i = 0; i < rowids.length; i++) {
var rowid = rowids[i];
var data = grid.getRowData(rowid);
console.log('ACCROLE for '+rowid+' is '+data.ACCROLE);
if (data.ACCROLE == 'N') { // view only
// check each column
//console.log(data);
for (var j = 0; j < columnModels.length; j++) {
var model = columnModels[j];
if (model.editable) {
console.log('Is Editable? '+model.editable);
//grid.setCell(rowid, model.name, '', 'not-editable-cell', {editable: false, edithidden: true});
grid.setCell(rowid, model.name, '', 'not-editable-cell', {editoptions: { readonly: 'readonly', disabled: 'disabled' }});
}
}
}
}
}
But the editoptions don't seem to do anything with this.
Any ideas how to do this?
OK thanks for explaining about Form editing. Here's an example of how to prevent edits on certain records for jqGrid with form editing:
Start with this example of jqGrid form edit: http://www.ok-soft-gmbh.com/jqGrid/MulticolumnEdit.htm
Use the beforeInitData event to check your data before the edit form is displayed. Note that this is bound to the pager object.
Use getGridParam and getCell methods to get the current value you want. In my example I grabbed the client name
Add your own business logic for checking (I don't allow edits on 'test2')
Return false to prevent the edit form from popping up.
This example only handles edit, not insert or delete.
Replace $grid.jqGrid("navGrid", "#pager",...) from the example with this:
$grid.jqGrid("navGrid", "#pager", {view: true},
// Events for edit
{
beforeInitData: function (formid) {
var selectedRow = jQuery("#list").jqGrid('getGridParam','selrow'); //get selected rows
var selectedClient = $("#list").jqGrid('getCell', selectedRow, 'name');
if(selectedClient == 'test2')
{
alert('You are not allowed to edit records for client "' + selectedClient + '"');
return false;
}
}
},
// Events for add
{
beforeShowForm: function (formid) {
}
}
);
You didn't provide much information about how you're updating rows (there are various methods as described in JQGrid web page demos, but I took a guess as to a possible solution. I started with the example on the bottom of this page (trirand's web site wiki for inline_editing) http://www.trirand.com/jqgridwiki/doku.php?id=wiki:inline_editing and made a few changes.
Added a new data column securityGroup, and put in data like 'A', 'B', 'C'
Displayed the new data column in the grid
The example used the onSelectRow event to start editing a row if you clicked on a new row. I updated this callback to check the value of row['securityGroup'], and only start .editRow if it's in securityGroupA
JSFiddle at http://jsfiddle.net/brianwoelfel/52rrunar/
Here's the callback:
onSelectRow: function(id){
var row = $(this).getLocalRow(id);
if(id && id!==lastsel2){
jQuery('#rowed5').restoreRow(lastsel2);
if(row['securityGroup'] == 'A') {
jQuery('#rowed5').editRow(id,true);
}
lastsel2=id;
}
},
If this method won't work for you, please provide more information about how you're currently doing edits with jqGrid. This example obviously is very trivial and doesn't even post to PHP or mysql or anything.
In case it will be helpful for others, here is how I am implementing Read Only rows in Form Editing mode, based on a column which designates what level of access the user has to each row:
In editoptions, use the beforeShowForm event, like so:
beforeShowForm: function (formid) {
console.log('Checking for READONLY '+formid.name);
var selectedRow = jQuery("#list1").jqGrid('getGridParam','selrow'); //get selected rows
var selRole = $("#list1").jqGrid('getCell', selectedRow, 'ACCROLE');
if(selRole == 'N' || selRole == 'S' || selRole == 'R')
{
//$("<div>Sorry, you do not have access to edit this record.</div>").dialog({title: "Access Denied"});
formid.find("input,select,textarea")
.prop("readonly", "readonly")
.addClass("ui-state-disabled")
.closest(".DataTD")
.prev(".CaptionTD")
.prop("disabled", true)
.addClass("ui-state-disabled");
formid.parent().find('#sData').hide();
var title=$(".ui-jqdialog-title","#edithd"+"list1").html();
title+=' - READONLY VIEW';
$(".ui-jqdialog-title","#edithd"+"list1").html(title);
formid.prepend('<span style="color: Red; font-size: 1em; font-weight: bold;">You viewing READONLY data.</span>');
}

highlight error cell or input when validation fails in jqgrid

I am using jqgrid inline editing with validation in grid using edit rules . i want to add class to highlight errors(eg: ui-state-error) for the input which fails in validation .
i can set class to highlight error using this
jQuery('#'+grid_id).jqGrid('setCell',row_id,errfields[a],'','ui-state-error',{color: 'blue'});
But it is not working in jqgrid when inbuilt validation fails .
How do i highlight the validation error triggered cell/input .
The demo shows how the probelm can be solved:
In the demo the columns "Amount", "Tax" and "Total" will be validated with the following validation rule:
editrules:{required:true,number:true}
On any validation error the first input field where the validation failed dditional class "ui-state-error" will be added. It is the standard jQuery UI CSS class. Addionally I set focus to the input field.
For the implementation I overwride (chain) the default implementation of the methods $.jgrid.checkValues and $.jgrid.hideModal. Here is the corresponding code:
var grid = $("#list");
grid.jqGrid({
// define all jqGrid options
});
var originalCheckValues = $.jgrid.checkValues,
originalHideModal = $.jgrid.hideModal,
iColWithError = 0;
$.jgrid.checkValues = function(val, valref,g, customobject, nam) {
var tr,td,
ret = originalCheckValues.call(this,val, valref,g, customobject, nam);
if (!ret[0]) {
tr = g.rows.namedItem(editingRowId);
if (tr) {
$(tr).children('td').children('input.editable[type="text"]').removeClass("ui-state-error");
iColWithError = valref; // save to set later the focus
//error_td_input_selector = 'tr#'+editingRowId+' > td:nth-child('+(valref+1)+') > input.editable[type="text"]:first';
td = tr.cells[valref];
if (td) {
$(td).find('input.editable[type="text"]').addClass("ui-state-error");
}
}
}
return ret;
};
$.jgrid.hideModal = function (selector,o) {
var input, oldOnClose, td,
tr = grid[0].rows.namedItem(editingRowId);
if (tr) {
td = tr.cells[iColWithError];
if (td) {
input = $(td).children('input.editable[type="text"]:first');
if (input.length > 0) {
oldOnClose = o.onClose;
o.onClose = function(s) {
if ($.isFunction(oldOnClose)) {
oldOnClose.call(s);
}
setTimeout(function(){
input.focus();
},100);
};
}
}
}
originalHideModal.call(this,selector,o);
};
In my project, I combine to use jqgrid and jquery validation plugin to examine and highlight errors, to provide unified look and feel in the entire application. You can use rowId_columnName as id to find the editor (input, select, etc.), e.g. $('#1_name') for name column in row 1 and then use the jquery object to add a rules, e.g. $('#1_name').rules('add', {required:true}) to add a rule to enforce that the cell is required, then calling $('#1_name').valid() to force a validation pass when value is submitted, e.g. before calling jqgrid saveRow method. Open the link for the plugin to know more about rules method and valid method.

Resources