JqGrid Context menu and sub menus - jqgrid

I am using Jqgrid 4.5.4 version. To achieve right click context menu functionality I am using Jquery Plugin for right click context menus (http://www.trendskitchens.co.nz/jquery/contextmenu/).
My requirement is to show the sub menu inside main right click context menu. For example:
--------------------------
Delete
Add
Highlight > ---------------
Highlight doc1
Highlight doc 2
----------------
Edit
----------------------------
When I click on "Highlight" context menu option, sub menu opens up with "Highlight doc 1" and "Highlight doc 2". I could not achieve the same functionality with context menu plugin.
Could anyone please provide some solution.
Here is my code:
loadComplete : function(data) {
$("tr.jqgrow", this)
.contextMenu('rightClickMenu', {
bindings : createContextMenuBindings(),
onContextMenu : function(event) {
var rowId = $(event.target).closest("tr.jqgrow").attr("id");
return createContextMenu(myMap[rowId].ctxMenu);
}
});
}
function createContextMenuBindings() {
var bindings = {
'delete' : function(trigger) {
handleDelete(trigger.id);
},
'add' : function(trigger) {
handleAccept(codeType, trigger.id, true);
},
'highlight' : function(trigger) {
// This case I have to show sub menu with different options
}
};
return bindings;
}
function createContextMenu(ctxMenu) {
var menuItem = '';
if (ctxMenu != null) {
for ( var i = 0; i < ctxMenu.length; i++) {
if (ctxMenu[i].display) {
var subMenu = ctxMenu[i].subCtxMenu;
if(subMenu != null && subMenu.length > 0)
{
menuItem += "<li class='menuitem dropdown' id= "+ ctxMenu[i].handler + ">";
var ulTag = '<a class="btn btn-sm btn-link" data-toggle="dropdown">' + ctxMenu[i].name +
'<span class="caret"></span>'+
'</a>';
ulTag += "<ul class='subrowul dropdown-menu'>";
$.each(subMenu, function( index, value ) {
ulTag += "<li class='submenuitem' id= '"+ value.handler + "'>" + value.name + "</li>";
});
ulTag += "</ul>";
menuItem += ulTag;
}
else
{
menuItem += "<li class='menuitem' id= "+ ctxMenu[i].handler + ">" + ctxMenu[i].name;
}
menuItem += "</li>";
}
$('#rightClickMenu > ul li.menuitem').remove();
$("#rightClickMenu > ul").append(menuItem);
if (menuItem == '') {
return false;
} else {
return true;
}
}
I would appreciate your answers.
Thanks
UPDATE:
I am answering to my own question. I could able to integrate Jquery context menu to jqgrid. Now I could able to see menu and sub menus. I am attaching some code for anybody looking for the answer:
Below logic will be called after jqgrid load complete by row.
$.contextMenu({
selector: 'tr#'+rowId,
build: function($trigger, e){
// ctxMenu is rendered from server.
var ctxMenu = jqGridJsonResponseMap[code_type] [$trigger[0].id].rowInfo.ctxMenu;
// ctx menu options constructed for each row. It is dynamic based on each row.
var _items = getCtxMenuOptionsByRowId(ctxMenu, code_type, $trigger[0].id);
return {
callback: function(key, options) {
// each context menu has differnt callback
ctx_bindings[key](key, options, code_type);
},
items: _items
};
}
});

Related

How to implement checkbox and select option in datatables?

I populate the table with an ajax call. In the first column I have checkboxes for selecting and deselecting rows and submit data to a php script. I have also two columns with select fields.
The render function for the one (of the two) column with select:
{
targets: 6,
render: function(data, type, full, meta) {
if(data.length == 4) {
return '<select class="form-control" id="selectotpionmonths' + data[0].cataloguenumber + '"><option value="'+ data[3].months
+ '">' + data[3].months + '<option value="'+ data[2].months
+ '">' + data[2].months + '<option value="'+ data[1].months
+ '">' + data[1].months + '<option value="'+ data[0].months
+ '">' + data[0].months + '</select>';
} else {
return data[0].months;
}
}
},
And the handler for click event and on change event:
$('#results tbody').on('click', 'input[type="checkbox"]', function(e){
var $row = $(this).closest('tr');
// Get row data
var data = table.row($row).data();
$('#selectotpionmonths' + data['enc_unit']).change(function(){
e.preventDefault();
var selectedoptionformonths = $('#selectotpionmonths' + data['enc_unit']).find("option:selected").text();
if(selectedoptionformonths == 3) {
$('#selectoptionprice' + data['enc_unit']).find('option[value="' + data['price_rrp'][3].price + '"]').prop('selected', true);
} else if(selectedoptionformonths == 6) {
$('#selectoptionprice' + data['enc_unit']).find('option[value="' + data['price_rrp'][2].price + '"]').prop('selected', true);
} else if(selectedoptionformonths == 9) {
$('#selectoptionprice' + data['enc_unit']).find('option[value="' + data['price_rrp'][1].price + '"]').prop('selected', true);
} else if(selectedoptionformonths == 12) {
$('#selectoptionprice' + data['enc_unit']).find('option[value="' + data['price_rrp'][0].price + '"]').prop('selected', true);
}
});
if(data['price_numberofmonths'].length == 4) {
var monthsoption = $('#selectotpionmonths' + data['enc_unit']).find("option:selected").text();
var priceoption = $('#selectoptionprice' + data['enc_unit']).find("option:selected").text();
} else {
var monthsoption = data['price_numberofmonths'][0].months;
var priceoption = data['price_rrp'][0].price;
}
// Get row ID
var dataforserver = {name: data['enc_unit'], duration: monthsoption, price: priceoption};
var rowId = dataforserver.name;
// Determine whether row ID is in the list of selected row IDs
var index = $.inArray(rowId, rows_selected);
// If checkbox is checked and row ID is not in list of selected row IDs
if(this.checked && index === -1){
rows_selected.push(rowId);
units_selected.push(dataforserver);
// Otherwise, if checkbox is not checked and row ID is in list of selected row IDs
} else if (!this.checked && index !== -1){
rows_selected.splice(index, 1);
units_selected.splice(index, 1);
}
if(this.checked){
$row.addClass('selected');
} else {
$row.removeClass('selected');
}
order_total = 0;
for(i=0; i < units_selected.length; i++) {
order_total += parseFloat(units_selected[i].price);
}
//console.log(order_total.toFixed(2));
$( "#ukhoanswer" ).html(
"Number of units selected: " + units_selected.length + "<br/>" +
"Total cost of order: " + order_total.toFixed(2)
);
// Update state of "Select all" control
updateDataTableSelectAllCtrl(table);
// Prevent click event from propagating to parent
e.stopPropagation();
});
// Handle click on table cells with checkboxes
$('#results').on('click', 'tbody td, thead th:first-child', function(e){
$(this).parent().find('input[type="checkbox"]').trigger('click');
});
// Handle click on "Select all" control
$('thead input[name="select_all"]', table.table().container()).on('click', function(e){
if(this.checked){
$('#results tbody input[type="checkbox"]:not(:checked)').trigger('click');
} else {
$('#results tbody input[type="checkbox"]:checked').trigger('click');
}
// Prevent click event from propagating to parent
e.stopPropagation();
});
You may view the initial code for the checkboxes here
When I click on the cell with the select field I want to prevent the click event on the row. I have tried adding e.preventDefault but with no success. For the columns with the select option I want only the change event to be triggered.
Any ideas?
I did the following:
var selectField = $('.form-control');
selectField.on('click', function(event) {
event.stopPropagation();
});
The selector are for the cells with the select options. Now the first time that I click on the select field the row is selected. But next time I select an option the click event is prevented and the row is not selected/deselected.
I am looking on how to prevent the row being selected on the first click on a select field.

JQueryMobile: page with data (from ajax) doesn't change

function returnQueryResultJson(url,callback) {
return $.ajax({
url: url,
type: "GET",
dataType: "json",
success: function(response) {
callback(response);
}
});
}
function showCategory(url,hash, options) {
var cat = hash.replace(/.*category=/, "");
if (cat == '#page1') {
cat = '';
}
var a = returnQueryResultJson('http://www.placetowebservice.nl/categories.php?category=' + cat,function(res) {
var
category = res,
pageSelector = hash.replace(/\?.*$/, ""),
$page = $(pageSelector),
$header = $page.children(":jqmData(role=header)"),
$content = $page.children(":jqmData(role=content)"),
markup = '<ul data-role="listview" data-theme="c" data-dividertheme="b">';
var cItems = category;
var headername = category.name;
var numItems = cItems.length;
if (cat == '') {
markup = '<ul data-role="listview" data-theme="c" data-dividertheme="b" style="min-height:100%;">';
}
for (var i=0;i<numItems;i++) {
markup += '<li><h3>' + cItems[i].title + '</h3><p>' + cItems[i].description + '</p></li>';
}
markup += "</ul>";
$header.find("h1").html(headername);
$content.html(markup);
$page.page();
$content.find(":jqmData(role=listview)").listview();
options.dataUrl = url;
options.changeHash = true;
options.reloadPage = true;
console.log($page);
$.mobile.changePage($page, options);
//}
});
}
$(document).bind("pagebeforechange", function(e,data) {
if (typeof data.toPage === "string") {
var
uz = $.mobile.path.parseUrl(data.toPage),
re = /^#category-item/,
re2 = /^#page1/
;
if (uz.hash.search(re) !== -1 || uz.hash.search(re2) !== -1) {
showCategory(uz.href,uz.hash,data.options);
e.preventDefault();
}
}
});
I have got this code, and it works pretty good (first time). I first load a page with:
$(document).ready(function(){
$.mobile.changePage('index.html#page1',{ dataUrl: "index.html#page1?category=", transition: "fade" });
});
It works, it loads the ajax-data in the page with id="page1".
Then I click on a link (category 1) and it shows the second page (with id="category-item") and fills it with the right data (category 1: sub 1, category 1: sub 2). Then I go back and it shows the categories again.
Now the problem appears, when I click on the next category (category 2). When I go to that page, it gives the right data from ajax (I used console.log to check this), but the data on the screen remains the data from category 1.
So the content from the first category you click on remains, even though you afterwards went to another category. It will remain showing the category you first clicked on.
What am I doing wrong?
It worked When I did this:
$page.page();
$page.trigger('create'); // added this one
$content.find(":jqmData(role=listview)").listview();
$content.find(":jqmData(role=listview)").listview("refresh"); // added this one

Add checkbox to the ckeditor toolbar to addClass/removeClass in img elements

I want to write a ckeditor plugin that adds a checkbox to the toolbar.
When an img element is selected, the checkbox should reflect whether the image has a certain class or not.
When something else than an image is selected, the checkbox should be
disabled, or maybe invisible.
When I check or uncheck the checkbox, the class should be added/removed to/from the img.
In other words, I want to add something else than a button to the toolbar (those that are added with editor.ui.addButton), and that something should be a checkbox.
How do I do that?
I managed to do it anyway, using editor.ui.add and editor.ui.addHandler. Here is a screenshot:
plugin.js:
CKEDITOR.plugins.add('add_zoomable_to_image',
{
init: function( editor )
{
var disabled_span_color = "#cccccc";
var enabled_span_color = "#000000";
var cb;
var span;
var html =
"<div style='height: 25px; display: inline-block'>" +
"<div style='margin: 5px'><input class='add_zoomable_cb' type='checkbox' disabled='disabled'>" +
"<span class='add_zoomable_span' style='color: " + disabled_span_color + "'> Add zoomable to image</span>" +
"</div>" +
"</div>";
editor.ui.add('add_zoomable_to_image', "zoomable_button", {});
editor.ui.addHandler("zoomable_button",
{
create: function ()
{
return {
render: function (editor, output)
{
output.push(html);
return {};
}
};
}
});
editor.on("selectionChange", function()
{
var sel = editor.getSelection();
var ranges = sel.getRanges();
if (ranges.length == 1)
{
var el = sel.getStartElement();
if (el.is('img'))
{
enable_input();
if (el.hasClass('zoomable'))
check_cb();
else
uncheck_cb();
}
else
disable_input();
}
else
disable_input();
});
editor.on("instanceReady", function ()
{
cb = $('.add_zoomable_cb');
span = $('.add_zoomable_span');
cb.change(function()
{
var element = editor.getSelection().getStartElement();
if (cb.is(':checked'))
element.addClass('zoomable');
else
element.removeClass('zoomable');
});
});
function enable_input()
{
cb.removeAttr('disabled');
span.css('color', enabled_span_color);
}
function disable_input()
{
uncheck_cb();
cb.attr('disabled', 'disabled');
span.css('color', disabled_span_color);
}
function check_cb()
{
cb.attr('checked', 'checked');
}
function uncheck_cb()
{
cb.removeAttr('checked');
}
}
});
CKEditor doesn't include the option to add a checkbox to the toolbar, so your first step is to look at their code and extend it to add the checkbox.
Then look at how other buttons work to modify the content and detect when the selection in the editor changes to reflect its new state, and apply those ideas to your checkbox.

How to add toggle button from jqgrid toolbar to autogenerated context menu

Toggle button in jqgrid top toolbar is defined using Oleg answer as
var autoedit;
$("#grid_toppager_left table.navtable tbody tr").append(
'<td class="ui-pg-button ui-corner-all" >' +
'<div class="ui-pg-div my-nav-checkbox">' +
'<input tabindex="-1" type="checkbox" id="AutoEdit" '+(autoedit ? 'checked ' : '')+'/>' +
'<label title="Toggle autoedit" for="AutoEdit">this text is ignored in toolbar</label></div></td>'
);
$("#AutoEdit").button({
text: false,
icons: {primary: "ui-icon-star"}
}).click(function () {
autoedit = $(this).is(':checked');
});
Answer from how to add standard textbox command to jqgrid context menu is used to autogenerate context menu for grid from toolbar.
In generated context menu for this item only text "this text is ignored in toolbar" appears and selecting it does nothing.
How to make it work or remove this item from context menu?
Look at the demo or the same demo with another themes: this and this.
First of all I modified the code of the jquery.contextmenu.js to support jQuery UI Themes. Then I modified the code more, to be able to create context menu more dynamically. In the modified version of jquery.contextmenu.js one can crate menu and the bindings not only in the onContextMenu, but also in onShowMenu. Inside of onContextMenu one can create just the empty menu
<div id="myMenu"><ul></ul></div>
It is important only if one use dynamically switching of the icons of the text of buttons from the navigator bar.
You can download the modified version of the file here.
In the demo I used the last modification of the code from the answer, so the standard context menu can be still used in the grid on selected text or in the enabled input/textarea fields. The context menu of the browser will be displayed in the case:
I modified the code of createContexMenuFromNavigatorButtons function from the answer to the following:
var getSelectedText = function () {
var text = '';
if (window.getSelection) {
text = window.getSelection();
} else if (document.getSelection) {
text = document.getSelection();
} else if (document.selection) {
text = document.selection.createRange().text;
}
return typeof (text) === 'string' ? text : text.toString();
},
createContexMenuFromNavigatorButtons = function (grid, pager) {
var menuId = 'menu_' + grid[0].id, menuUl = $('<ul>'),
menuDiv = $('<div>').attr('id', menuId);
menuUl.appendTo(menuDiv);
menuDiv.appendTo('body');
grid.contextMenu(menuId, {
bindings: {}, // the bindings will be created in the onShowMenu
onContextMenu: function (e) {
var p = grid[0].p, i, lastSelId, $target = $(e.target),
rowId = $target.closest("tr.jqgrow").attr("id"),
isInput = $target.is(':text:enabled') ||
$target.is('input[type=textarea]:enabled') ||
$target.is('textarea:enabled');
if (rowId && !isInput && getSelectedText() === '') {
i = $.inArray(rowId, p.selarrrow);
if (p.selrow !== rowId && i < 0) {
// prevent the row from be unselected
// the implementation is for "multiselect:false" which we use,
// but one can easy modify the code for "multiselect:true"
grid.jqGrid('setSelection', rowId);
} else if (p.multiselect) {
// Edit will edit FIRST selected row.
// rowId is allready selected, but can be not the last selected.
// Se we swap rowId with the first element of the array p.selarrrow
lastSelId = p.selarrrow[p.selarrrow.length - 1];
if (i !== p.selarrrow.length - 1) {
p.selarrrow[p.selarrrow.length - 1] = rowId;
p.selarrrow[i] = lastSelId;
p.selrow = rowId;
}
}
return true;
} else {
return false; // no contex menu
}
},
onShowMenu: function (e, $menu) {
var options = this, $menuUl = $menu.find('ul:first').empty();
$('table.navtable .ui-pg-button', pager).each(function () {
var $spanIcon, text, $td, id, $li, $a, button,
$div = $(this).children('div.ui-pg-div:first'),
gridId = grid[0].id;
if ($div.length === 1) {
text = $div.text();
$td = $div.parent();
if (text === '') {
text = $td.attr('title');
}
if (this.id !== '' && text !== '') {
id = 'menuitem_' + this.id;
if (id.length > gridId.length + 2) {
id = id.substr(0, id.length - gridId.length - 1);
}
} else {
// for custom buttons
id = $.jgrid.randId();
}
$li = $('<li>').attr('id', id);
$spanIcon = $div.children('span.ui-icon');
if ($spanIcon.length > 0) {
// standard navGrid button or button added by navButtonAdd
$li.append($('<a>')
.text(text)
.prepend($spanIcon.clone().css({
float: 'left',
marginRight: '0.5em'
})));
$menuUl.append($li);
options.bindings[id] = (function ($button) {
return function () { $button.click(); };
}($div));
} else {
button = $div.children("input").data("button");
if (button !== undefined) {
$a = $('<a>')
.text(button.options.label)
.prepend(
$('<label>').addClass("ui-corner-all").css({
float: 'left',
width: '16px',
borderWidth: '0px',
marginRight: '0.5em'//'4px'
}).append(
$('<span>').addClass("ui-button-icon-primary ui-icon " +
button.options.icons.primary)
.css({
float: 'left',
marginRight: '0.5em'
})
)
);
$li.append($a);
if (button.type === "checkbox" && button.element.is(':checked')) {
$a.find('label:first').addClass("ui-state-active");
}
$menuUl.append($li);
options.bindings[id] = (function ($button, isCheckbox) {
if (isCheckbox) {
return function () {
if ($button.is(':checked')) {
$button.siblings('label').removeClass("ui-state-active");
} else {
$button.siblings('label').addClass("ui-state-active");
}
$button.click();
$button.button("refresh"); // needed for IE7-IE8
};
} else {
return function () { $button.click(); };
}
}(button.element, button.type === "checkbox"));
}
}
}
});
return $menu;
}
});
},
autoedit = false;
and fill the check-button in the navigator bar with the code which is changed only a little:
$("#pager_left table.navtable tbody tr").append(
'<td class="ui-pg-button ui-corner-all">' +
'<div class="ui-pg-div my-nav-checkbox">' +
'<input tabindex="-1" type="checkbox" id="AutoEdit" />' +
'<label title="Checkx caption which should appear as button tooltip"' +
' for="AutoEdit">Autoedit</label></div></td>'
);
$("#AutoEdit").button({
text: false,
icons: {primary: "ui-icon-mail-closed"}
}).click(function () {
var iconClass, $this = $(this);
if (!autoedit) { // $this.is(':checked')) {
autoedit = true;
iconClass = "ui-icon-mail-open";
} else {
autoedit = false;
iconClass = "ui-icon-mail-closed";
}
$this.button("option", {icons: {primary: iconClass}});
});
createContexMenuFromNavigatorButtons($grid, '#pager');
UPDATED: One more demo which support buttons added by new inlineNav method you can find here. Additionally I included in the demo the function normalizePagers which I use to improve the look of the pager:
How you can see the contextmenu includes only enabled buttons from the navigator bar.

Inline editing with AJAX - how do I create multiple editable areas on the same page?

I found a tutorial on how to create editable regions on a page using AJAX.
This is great, except it was written for a single element with a unique ID. I'd like to be able to click on multiple elements on the same page and have them also be editable (e.g., I'd like to alter the script below so it works not with a single element, but with multiple elements of a particular class).
Here is my HTML:
<h2>Edit This</h2>
<p class="edit">This is some editable content</p>
<p class="edit">This is some more editable content</p>
<p class="edit">I could do this all day</p>
Here is the JS file I'm working with (I updated the script per Rex's answer below): This script is, unfortunately, not working - can anyone point me in the right direction?
Event.observe(window, 'load', init, false);
function init() {
makeEditable('edit');
}
function makeEditable(className) {
var editElements = document.getElementsByClassName(className);
for(var i=0;i<editElements.length;i++) {
Event.observe(editElements[i], 'click', function(){edit($(className))}, false);
Event.observe(editElements[i], 'mouseover', function(){showAsEditable($(className))}, false);
Event.observe(editElements[i], 'mouseout', function(){showAsEditable($(className), true)}, false);
}
}
function showAsEditable(obj, clear) {
if (!clear) {
Element.addClassName(obj, 'editable');
} else {
Element.removeClassName(obj, 'editable');
}
}
function edit(obj) {
Element.hide(obj);
var textarea ='<div id="' + obj.id + '_editor"><textarea cols="60" rows="4" name="' + obj.id + '" id="' + obj.id + '_edit">' + obj.innerHTML + '</textarea>';
var button = '<input type="button" value="SAVE" id="' + obj.id + '_save"/> OR <input type="button" value="CANCEL" id="' + obj.id + '_cancel"/></div>';
new Insertion.After(obj, textarea+button);
Event.observe(obj.id+'_save', 'click', function(){saveChanges(obj)}, false);
Event.observe(obj.id+'_cancel', 'click', function(){cleanUp(obj)}, false);
}
function cleanUp(obj, keepEditable) {
Element.remove(obj.id+'_editor');
Element.show(obj);
if (!keepEditable) showAsEditable(obj, true);
}
function saveChanges(obj) {
var new_content = escape($F(obj.id+'_edit'));
obj.preUpdate = obj.innerHTML // stow contents prior to saving in case of an error
obj.innerHTML = "Saving…";
cleanUp(obj, true);
var success = function(t){editComplete(t, obj);}
var failure = function(t){editFailed(t, obj);}
var url = 'http://portal.3roadsmedia.com/scripts/edit.php';
var pars = 'id=' + obj.id + '&content=' + new_content + '&pre=' + obj.preUpdate;
var myAjax = new Ajax.Request(url, {method:'post',
postBody:pars, onSuccess:success, onFailure:failure});
}
function editComplete(t, obj) {
obj.innerHTML = t.responseText;
showAsEditable(obj, true);
}
function editFailed(t, obj) {
obj.innerHTML = 'Sorry, the update failed.';
cleanUp(obj);
}
The Event.observe method currently attaches to a single element with the ID specified. You should change this to iterate over a collection of elements located by classname and attach to each of them. According to the Prototype documentation, you can provide an element object as the first parameter, instead of an ID.
Currently, id is a string:
function makeEditable(id) {
Event.observe(id, 'click', function(){edit($(id))}, false);
//...
Which means Event.observe is attaching to the click event of the element with the ID provided. You want to attach to all elements with a class. Try:
function makeEditable(className) {
var editElements = document.getElementsByClassName(className);
for(var i=0;i<editElements.length;i++) {
Event.observe(editElements[i], 'click', function()
//...
}
//...

Resources