How to call a function inside a Javascript class (jQuery, jSuggest) - javascript

I'm using the code found in this jsbin: http://jsbin.com/asahe5/10/edit
There is a function within that class called addItem, which adds an auto-suggested item to the page. However, there is no API built in to add something by clicking on a button for example.
I have tried the following, but it doesn't work (Uncaught TypeError: Object [object Object] has no method 'addItem'):
var test = $("#test").jSuggest({
source: 'http://example.com/page.php',
minChars: 1,
keyDelay: 200,
selectedItemProp: 'name',
seekVal: 'name',
startText: 'Enter a country',
newItem: false,
newText: 'Please select a country from the list.',
selectionAdded: function(elem, data){ add_country(data.value); },
selectionRemoved: function(elem, data){ elem.fadeTo("fast", 0, function(){ elem.remove(); rem_country(data.value); }); }
});
function add_item(object, id) {
test.addItem(object, id);
}
The most relevant part of the plugin:
(function($){
$.fn.jSuggest = function(options) {
var defaults = {
source: {}, // Object or URL where jSuggest gets the suggestions from.
uniqID: false,
startText: 'Enter a Value', // Text to display when the jSuggest input field is empty.
emptyText: 'No Results Found', // Text to display when their are no search results.
preFill: {}, // Object from which you automatically add items when the page is first loaded.
limitText: 'No More Values Are Allowed', // Text to display when the number of selections has reached it's limit.
newItem: false, // If set to false, the user will not be able to add new items by any other way than by selecting from the suggestions list.
newText: 'Adding New Values Is Not Allowed', // Text to display when the user tries to enter a new item by typing.
selectedItemProp: 'value', // Value displayed on the added item
selectProp: 'value', // Name of object property added to the hidden input.
seekVal: 'value', // Comma separated list of object property names.
queryParam: 'q', // The name of the param that will hold the search string value in the AJAX request.
queryLimit: false, // Number for 'limit' param on ajax request.
extraParams: '', // This will be added onto the end of the AJAX request URL. Make sure you add an '&' before each param.
matchCase: false, // Make the search case sensitive when set to true.
minChars: 1, // Minimum number of characters that must be entered before the search begins.
keyDelay: 400, // The delay after a keydown on the jSuggest input field and before search is started.
resultsHighlight: true, // Option to choose whether or not to highlight the matched text in each result item.
selectionLimit: false, // Limits the number of selections that are allowed.
showResultList: true, // If set to false, the Results Dropdown List will never be shown at any time.
selectionClick: function(elem){}, // Custom function that is run when a previously chosen item is clicked.
selectionAdded: function(elem, data){}, // Custom function that is run when an item is added to the items holder.
selectionRemoved: function(elem, data){ elem.remove(); }, // Custom function that is run when an item is removed from the items holder.
spotFirst: true, // Option that spots the first suggestions on the results list if true.
formatList: false, // Custom function that is run after all the data has been retrieved and before the results are put into the suggestion results list.
beforeRetrieve: function(string){ return string; }, // Custom function that is run before the AJAX request is made, or the local objected is searched.
retrieveComplete: function(data){ return data; },
resultClick: function(data){}, // Custom function that is run when a search result item is clicked.
resultsComplete: function(){} // Custom function that is run when the suggestion results dropdown list is made visible.
};
// Merge the options passed with the defaults.
var opts = $.extend(defaults, options);
// Get the data type of the source.
var dType = typeof opts.source;
.....................................
function addItem(data, num) {
// Add to the hidden input the seleced values property from the passed data.
hiddenInput.val(hiddenInput.val()+data[opts.selectProp]+',');
// If a selected item is clicked, add the selected class and call the custom selectionClick function.
var item = $('<li class="as-selection-item" id="as-selection-'+num+'"></li>').click(function() {
opts.selectionClick.call(this, $(this));
itemsHolder.children().removeClass('selected');
$(this).addClass('selected');
});
// If the close cross is clicked,
var close = $('<a class="as-close">x</a>').click(function() {
// Remove the item from the hidden input.
hiddenInput.val(hiddenInput.val().replace(data[opts.selectProp]+',',''));
// Call the custom selectionRemoved function.
opts.selectionRemoved.call(this, item, data);
input.focus();
return false;
});
// Insert the item with the selectedItemProp as text and the close cross.
orgLI.before(item.html(data[opts.selectedItemProp]).prepend(close));
// Call the custom selectionAdded function with the recently added item as elem and its associated data.
opts.selectionAdded.call(this, orgLI.prev(), data);
}
.....................................
});
}
};
})(jQuery);

You can't call that function without changing the plugin to expose the function as a visible API.

Related

How do you Get and Add events to a specific source?

I was successfully able to specify multiple sources: My SQL server source, and an empty array to hold any user-generated events userAddedEvents = []. The idea was that I could use this array later to "save" new events to my server.
var userAddedEvents = []; //container for event objects that will hold user-generated content
var calendar = new FullCalendar.Calendar(calendarEl, {
eventSources: [
{
events: function (fetchInfo, successCallback, failureCallback) {
$.ajax({
url: url,
type: 'post',
contentType: "application/json; charset=utf-8", //include fetchInfo in request body instead of form-data
dataType: "json",
data: JSON.stringify(fetchInfo), //the fetchInfo object must be stringified
success: function (data) {
events = $.merge([], data.events);
successCallback(events);
}
});
}
}, //end server source
{
id: 'userAddedEvents',
events: userAddedEvents
} //end local array source
]
});
So, here's me trying to add an event to my userAddedEvents source...
select: function (info) {
// https://fullcalendar.io/docs/select-callback
console.log(info)
console.log('selected ' + info.startStr + ' to ' + info.endStr)
calendar.addEvent({
start: info.startStr,
end: info.endStr,
rendering: 'background', //v4 and v5 use different terminology
color: '#ff9f89',
selected: true, //custom, extended property
}, userAddedEvents);
calendar.unselect(); //clear the current selection
},
Anyway, long story short... when I try to dump out the results of userAddedEvents, it's empty, although, I do see new content on my calendar.
==UPDATED== with example included... I added a custom button where I attempt to get content from the userAddedEvents array.
Alternatively (also shown in the example), I've had success getting ALL events with calendar.getEvents(), then using $.grep to filter some specific property or extended property. Ultimately though, I suppose I'm trying to use a "temporary event source" for the sake of convenience -- I can act upon the userAddedEvents array, stringify it, empty it, etc. I do not know how to getEvents for a specific source object.
customButtons: {
getUserCreatedEvents: {
text: 'Get User Events',
click: function () {
console.log(calendar.getEventSources()); //both event sources are listed
console.log(calendar.getEventSourceById(userAddedEvents)); //missing the id in output {calendar, id, internalEventSource, url}
console.log(calendar.getEventSourceById('userAddedEvents')); //has the the id in output {calendar, id, internalEventSource, url}
console.log(userAddedEvents) //the array is empty
/*
events = calendar.getEvents();
// console.log(events)
var filteredResultsGREP = $.grep(events, function (event) {
return event.rendering === 'background';
});
*/
/*
https://fullcalendar.io/docs/Event-toPlainObject (version 5 only)
*/
// this WILL show any events added based on the property specified
// console.log(filteredResultsGREP);
}
}
}
How do I get the events that are new? I want to hold all user-created events before I send them to SQL for processing.
Firstly, apologies for totally forgetting about this question, having started to help with it.
After some digging it looks like you can't get the raw events for an individual event source, which is a bit annoying.
So I think actually your simplest approach is just to add the events to your separate array, without worrying about the structure in fullCalendar. Then you can just send that list to the server when you want to save them.
select: function (info) {
var evt = {
start: info.startStr,
end: info.endStr,
rendering: "background", //v4 and v5 use different terminology
color: "#ff9f89",
selected: true //custom, extended property
};
calendar.addEvent(evt);
userAddedEvents.push(evt);
calendar.unselect(); //clear the current selection
},
Demo: https://codepen.io/ADyson82/pen/abdVVNM
The only extra complication you might have is if you allow events to be dragged or resized (or otherwise edited) after they've been added, you'll have to sync that with the separate array, which is a bit of extra work.

Getting DataTable row clicked on with context menu

I would like to add a context menu to each of my DataTable rows.
I want to get the row that was clicked on and then some way to identify it (I suppose the first cell value which contains the primary key would work) and then send an AJAX request containing the PK and option clicked.
I have figured out how to get the row by using "tr" as a selector, but how can I get the 1st cell's value (which contains the primary key). This prints out all of the cells:
$(function(){
$.contextMenu({
selector: 'td',
trigger: 'right',
callback: function(key, options) {
var m = $(options.$trigger).text();
window.console && console.log(m) || alert(m);
},
items: {
"delete": {name: "Delete", icon: "delete"},
});
});
Also, is this the best way to do this? I plan to have ~10 options in the context menu that interact with the rows. I am using Django as the backend.
Always use the API when you want to interact with DT. If you have an instance
var table = $('#example').DataTable( {..} )
then retrieve the current row by passing options.$trigger which holds the <tr> node :
$.contextMenu({
selector: 'tr',
trigger: 'right',
callback: function(key, options) {
var row = table.row(options.$trigger)
switch (key) {
case 'delete' :
row.remove().draw()
break;
case ...
}
},
items: {
'delete': { name: 'Delete', icon: 'delete' },
...
}
})
but how can I get the 1st cell's value (which contains the primary
key).
row.data()[0]
demo -> http://jsfiddle.net/z2q5scgr/

magnific popup: open by clicking on something other than the image

Client has requested that the image caption completely cover the thumbnail on hover, so I now need to be able to click the caption to open Magnific Popup instead of the <a>. So far I have been able to do:
JS/jQuery:
jQuery(".caption").on("click", function(event) {
var items = [];
jQuery(".item").each(function() {
items.push( {
src: jQuery(this).find("a").first().attr("href")
} );
});
jQuery.magnificPopup.open({
type: 'image',
gallery: {
enabled: true
},
items: items,
image: {
titleSrc: function(item) {
console.log( item.el );
// return item.el.clone();
}
}
});
});
See the fiddle for an example, and the HTML and CSS (plus alternative JS that doesn't work either).
It's giving me two blockers:
It's always the first image that pops up, instead of the image that one clicked on.
That part about return item.el.clone(); is commented out because it's producing an "item.el is undefined" error (which doesn't seem to happen when magnificPopup is instantiated via jQuery('.caption').magnificPopup() as opposed to jQuery.magnificPopup.open()). However, I need the caption HTML to show up in the popup as well.
Any help would be appreciated. Thanks.
When you use an array of items you can pass the index of the first item you want to show. So I have used var index = jQuery(this).parent().index() to get the index of the current clicked item and then passed that variable in to the magnificPopup function.
To get the caption in the popup I have added an extra property to the items object called titleSrc, which you can then retreive in the titleSrc option using item.data.titleSrc.
https://jsfiddle.net/sjp7j1zx/4/
jQuery(".caption a").on("click", function(event) {
event.stopPropagation();
});
jQuery(".caption").on("click", function(event) {
var items = [];
jQuery(".item").each(function() {
// Pass an extra titleSrc property to the item object so we can use it in the magnificPopup function
items.push( {
src: jQuery(this).find("a").first().attr("href"),
titleSrc: jQuery(this).find('.caption').html()
} );
});
// Get the index of the current selected item
var index = jQuery(this).parent().index();
jQuery.magnificPopup.open({
type: 'image',
gallery: {
enabled: true
},
items: items,
image: {
titleSrc: function(item) {
// get the titleSrc from the data property of the item object that we defined in the .each loop
return item.data.titleSrc;
}
}
// Pass the current items index here to define which item in the array to show first
}, index);
});

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);
}

jqGrid - checkbox editing not able to edit selected row

In my jqGrid, I have a checkbox which is also available for editing, i.e. a user can click on the checkbox and that checkbox's value will be updated in the database. That is working fine. However when I click on the checkbox and if I try clicking on it again, nothing happens. The row does not get saved. Theoretically the unchecked value of the checkbox should be saved. But this does not happen.
I have tried referring to this answer of Oleg but it does not help.
The weird problem is if I select another row and then try to unselect the checkbox again, I do see a save request going.
I am guessing this is because I am trying to edit a row which is currently selected. But I am not sure what I am doing wrong here.
This is what I am doing in my beforeSelectRow
beforeSelectRow: function (rowid, e) {
var $target = $(e.target),
$td = $target.closest("td"),
iCol = $.jgrid.getCellIndex($td[0]),
colModel = $(this).jqGrid("getGridParam", "colModel");
if (iCol >= 0 && $target.is(":checkbox")) {
if (colModel[iCol].name == "W3LabelSelected") {
console.log(colModel[iCol].name);
$(this).setSelection(rowid, true);
$(this).jqGrid('resetSelection');
$(this).jqGrid('saveRow', rowid, {
succesfunc: function (response) {
$grid.trigger('reloadGrid');
return true;
}
});
}
}
return true;
},
Configuration:
jqGrid version: Latest free jqGrid
Data Type: Json being saved to server
Minimal Grid Code: jsFiddle
EDIT: After Oleg's answer this is what I have so far:
beforeSelectRow: function (rowid, e) {
var $self = $(this),
iCol = $.jgrid.getCellIndex($(e.target).closest("td")[0]),
cm = $self.jqGrid("getGridParam", "colModel");
if (cm[iCol].name === "W3LabelSelected") {
//console.log($(e.target).is(":checked"));
$(this).jqGrid('saveRow', rowid, {
succesfunc: function (response) {
$grid.trigger('reloadGrid');
return true;
}
});
}
return true; // allow selection
}
This is close to what I want. However if I select on the checkbox the first time and the second time, the console.log does get called everytime. However the saveRow gets called only when I check the checkbox and then click on it again to uncheck it the first time and never after that. By default the checkbox can be checked or unchecked based on data sent from server.
In the image, the request is sent after selecting the checkbox two times instead of being sent everytime.
UPDATE: As per #Oleg's suggestion, I have implemented cellattr and called a function inside. In the function I simply pass the rowid and update the checkbox of that rowid on the server.
Here's the code I used:
{
name: 'W3LabelSelected',
index: 'u.W3LabelSelected',
align: 'center',
width: '170',
editable: false,
edittype: 'checkbox',
formatter: "checkbox",
search: false,
formatoptions: {
disabled: false
},
editoptions: {
value: "1:0"
},
cellattr: function (rowId, tv, rawObject, cm, rdata) {
return ' onClick="selectThis(' + rowId + ')"';
}
},
and my selectThis function:
function selectThis(rowid) {
$.ajax({
type: 'POST',
url: myurl,
data: {
'id': rowid
},
success: function (data) {
if (data.success == 'success') {
$("#list").setGridParam({
datatype: 'json',
page: 1
}).trigger('reloadGrid');
} else {
$("<div title='Error' class = 'ui-state-error ui-corner-all'>" + data.success + "</div>").dialog({});
}
}
});
}
FIDDLE
I think there is a misunderstanding in the usage of formatter: "checkbox", formatoptions: { disabled: false }. If you creates non-disabled checkboxs in the column of the grid in the way then the user just see the checkbox, which can be clicked and which state can be changed. On the other side nothing happens if the user changes the state of the checkbox. On the contrary the initial state of the checkbox corresponds to input data of the grid, but the changed checkbox makes illusion that the state is changed, but nothing will be done automatically. Even if you use datatype: "local" nothing is happens and even local data will be changed on click. If you do need to save the changes based on the changing the state of the checkbox then you have to implement additional code. The answer demonstrates a possible implementation. You can change the state of some checkboxes on the corresponding demo, then change the page and go back to the first page. You will see that the state of the checkbox corresponds the lates changes.
Now let us we try to start inline editing (start editRow) on select the row of the grid. First of all inline editing get the values from the rows (editable columns) using unformatter, saves the old values in internal savedRow parameter and then it creates new editing controls inside of editable cells on the place of old content. Everything is relatively easy in case of using standard disabled checkbox of formatter: "checkbox". jqGrid just creates enabled checkboxs on the place of disabled checkboxs. The user can change the state of the checkboxs or the content of any other editable columns and saves the changes by usage of Enter for example. After that the selected row will be saved and will be not more editable. You can monitor the changes of the state of the checkbox additionally (by usage editoptions.dataEvents with "change" event for example) and call saveRow on changing the state. It's important that the row will be not editable after the saving. If you do need to hold the row editable you will have to call editRow once more after successful saving of the row. You can call editRow inside of aftersavefunc callback of saveRow, but I recommend you to wrap the call of editRow inside of setTimeout to be sure that processing of previous saving is finished. It's the way which I would recommend you.
On the other side if you try to combine enabled checkboxs of formatter: "checkbox" with inline editing then you will have much more complex processing. It's important that enabled checkbox can be changed first of all before processing of onclick and onchange event handlers. The order of 3 events (changing the state of the checkbox, processing of onclick and processing of onchange) can be different in different web browsers. If the method editRow be executing it uses unformatter of checkbox-formatter to get the current state of the checkbox. Based of the value of the state editRow replace the content of the cell to another content with another enabled checkbox. It can be that the state of the checkbox is already changed, but editRow interprets the changes state like the initial state of the checkbox. In the same way one can call saveRow only after editRow. So you can't just use saveRow inside of change handler of formatter: "checkbox", formatoptions: { disabled: false }, because the line is not yet in editing mode.
UPDATED: The corresponding implementation (in case of usage formatter: "checkbox", formatoptions: { disabled: false }) could be the following:
editurl: "SomeUrl",
beforeSelectRow: function (rowid, e) {
var $self = $(this),
$td = $(e.target).closest("tr.jqgrow>td"),
p = $self.jqGrid("getGridParam"),
savedRow = p.savedRow,
cm = $td.length > 0 ? p.colModel[$td[0].cellIndex] : null,
cmName = cm != null && cm.editable ? cm.name : "Quantity",
isChecked;
if (savedRow.length > 0 && savedRow[0].id !== rowid) {
$self.jqGrid("restoreRow", savedRow[0].id);
}
if (cm != null && cm.name === "W3LabelSelected" && $(e.target).is(":checkbox")) {
if (savedRow.length > 0) {
// some row is editing now
isChecked = $(e.target).is(":checked");
if (savedRow[0].id === rowid) {
$self.jqGrid("saveRow", rowid, {
extraparam: {
W3LabelSelected: isChecked ? "1" : "0",
},
aftersavefunc: function (response) {
$self.jqGrid("editRow", rowid, {
keys: true,
focusField: cmName
});
}
});
}
} else {
$.ajax({
type: "POST",
url: "SomeUrl", // probably just p.editurl
data: $self.jqGrid("getRowData", rowid)
});
}
}
if (rowid) {
$self.jqGrid("editRow", rowid, {
keys: true,
focusField: cmName
});
}
return true; // allow selection
}
See jsfiddle demo http://jsfiddle.net/OlegKi/HJema/190/

Categories

Resources