Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
// yada.ajax.js
// Depends on yada.js
(function( yada ) {
"use strict";
// Namespace trick explained here: http://stackoverflow.com/a/5947280/587641
// For a public property or function, use "yada.xxx = ..."
// For a private property use "var xxx = "
// For a private function use "function xxx(..."
const markerAjaxButtonOnly = 'yadaAjaxButtonOnly';
const markerAjaxModal = 'yadaAjaxModal';
const markerDropTarget = 'yadaDropTarget';
var clickedButton;
var ajaxCounter = 0; // Counter for the ajax call parallelism
// WARNING: if you change this, also change it in yada.js
const markerClass = 'yadaAjaxed'; // To prevent double submission
// Deprecated: these were once used when opening the login form via yada.openLoginModal and yada.openLoginModalAjax
yada.postLoginHandler = null; // Handler to run after login, if any
var postLoginUrl = null;
var postLoginData = null;
var postLoginType = null;
const yadaAjaxResponseHtmlRoot = "
";
yada.markerAjaxModal = markerAjaxModal; // For use in other scripts
/**
* Init yada ajax handlers on the specified element
* @param $element the element, or null for the entire body
*/
yada.initAjaxHandlersOn = function($element) {
yada.enableAjaxForms(null, $element);
yada.enableAjaxLinks(null, $element);
yada.enableAjaxSelects(null, $element);
yada.enableAjaxCheckboxes(null, $element); // Maybe obsoleted by yada.enableAjaxInputs
yada.enableDropUpload(null, $element);
initObservers($element)
yada.enableAjaxInputs();
}
/**
* All observers should be enabled in this function, that is also called after a returned ajax HTML is cloned.
*/
function initObservers($element) {
yada.enableAjaxTriggerInViewport($element);
}
//////////////////////
/// Pagination support
/**
* Changes the current URL history adding pagination parameters: the back button will enforce the previous page parameters on the url.
* This is needed when loading a new page via ajax so that the browser back button will run the correct query again.
* Pagination parameters on the nextpage url/form must always be named "page" and "size", but they can be different on the history url in order to have
* more than one pageable section on the same page, for example "product.page" and "project.page".
* @param $linkOrForm the jQuery object of the clicked anchor, or submitted form, with pagination parameters named "page" and "size"
* @param pageParam the optional name of the parameter that will contain the page number to load on back, for example "product.page"
* @param sizeParam the optional name of the parameter that will contain the page size to load on back, for example "product.size"
* @param loadPreviousParam the optional name of the parameter that will contain the loadPrevious flag, for example "product.loadPrevious".
* It just tells when the user pressed the back button, so that eventually all previous pages can be loaded, not just the last one.
* @see net.yadaframework.web.YadaPageRequest
*/
yada.fixPaginationLinkHistory = function($linkOrForm, pageParam, sizeParam, loadPreviousParam) {
const NEXTPAGE_NAME="page";
const NEXTSIZE_NAME="size";
const CONTAINERID_NAME="yadaContainer";
const CONTAINERSCROLL_NAME="yadaScroll";
// Default param names
pageParam = pageParam || "page";
sizeParam = sizeParam || "size";
loadPreviousParam = loadPreviousParam || "loadPrevious";
//
// ".../en/search/loadMoreProducts?searchString=tolo&page=2&size=4"
const nextPageUrl = $linkOrForm.attr("data-yadahref") || $linkOrForm.attr("href");
var nextPage = yada.getUrlParameter(nextPageUrl, NEXTPAGE_NAME);
var nextSize = yada.getUrlParameter(nextPageUrl, NEXTSIZE_NAME);
if (nextPageUrl==null) {
// Could be a form
nextPage = $("input[name="+NEXTPAGE_NAME+"]", $linkOrForm).val() || 1;
nextSize = $("input[name="+NEXTSIZE_NAME+"]", $linkOrForm).val() || 32;
}
const currentUrl = window.location.href;
var newUrl = yada.addOrUpdateUrlParameter(currentUrl, pageParam, nextPage);
newUrl = yada.addOrUpdateUrlParameter(newUrl, sizeParam, nextSize);
newUrl = yada.addOrUpdateUrlParameter(newUrl, loadPreviousParam, true); // This is always true
// Add the container id and the scroll position.
// We presume that the scrolling element is the parent of the update target
const updateTargetSelector = $linkOrForm.attr("data-yadaUpdateOnSuccess");
const $container = yada.extendedSelect($linkOrForm, updateTargetSelector).parent();
var containerId = $container.attr("id");
if (containerId!=null) {
// If there is no id, there is no autoscroll (which is both easier to implement and a way to turn off the scroll behavior somehow)
const scrollPos = $container.scrollTop();
newUrl = yada.addOrUpdateUrlParameter(newUrl, CONTAINERID_NAME, containerId);
newUrl = yada.addOrUpdateUrlParameter(newUrl, CONTAINERSCROLL_NAME, scrollPos);
}
history.pushState({}, "", newUrl);
};
/**
* If the "data-yadaPaginationHistory" attribute is present, set a new history entry.
* @return true if the attribute is present.
*/
function handlePaginationHistoryAttribute($elem, $linkOrForm) {
var yadaPagination = $elem.attr("data-yadaPaginationHistory"); // ="pageParam, sizeParam, loadPreviousParam"
if (yadaPagination==null) {
return false;
}
if (yadaPagination=="") {
yadaPagination=null;
}
const paginationParams = yada.listToArray(yadaPagination);
yada.fixPaginationLinkHistory($linkOrForm, paginationParams[0], paginationParams[1], paginationParams[2]);
return true;
}
////////////////////
/// Modal
/**
* Open a modal when the location.hash contains the needed value.
* Example: openModalOnHash('/mymodal', ['id', 'name'], '/', function(data){return !isNaN(data.id);}
* @param targetUrl the modal url to open via ajax; can have url parameters
* @param paramNames an array of request parameter names that are assigned to from the hash
* @param separator the values contained in the hash are separated by this character
* @param validator a function that returns true if the hash values are valid
*/
yada.openModalOnHash = function(targetUrl, paramNames, separator, validator) {
var hashValue = document.location.hash;
if (hashValue!=null && hashValue.length>1) {
try {
var data = yada.hashPathToMap(paramNames, hashValue, separator);
if (typeof validator == "function" && validator(data)) {
yada.ajax(targetUrl, data);
}
} catch(e) {
console.error(e);
}
}
}
////////////////////
/// Form
// This is for coupled selects
yada.enableAjaxSelectOptions = function() {
$('.s_chain select').change(function() {
var selectedValue = $('option:selected', this).val();
var $triggerSelectContainer = $(this).parents('.s_chain');
var triggerContainerId = $triggerSelectContainer.attr('id');
if (triggerContainerId) {
var $targetSelectContainer = $triggerSelectContainer.siblings('[data-trigger-id='+triggerContainerId+']');
if ($targetSelectContainer) {
var targetUrl = $targetSelectContainer.attr('data-url');
var targetExclude = $targetSelectContainer.attr('data-exclude-id');
var selectedOption = $targetSelectContainer.attr('data-selected-option');
var data={triggerId : selectedValue, excludeId : targetExclude};
yada.ajax(targetUrl, data, function(responseText) {
$('select', $targetSelectContainer).children().remove();
$('select', $targetSelectContainer).append(responseText);
if (selectedOption) {
$('select option[value="'+selectedOption+'"]', $targetSelectContainer).prop('selected', true);
}
$('select', $targetSelectContainer).prop('disabled', false);
},
null, null, getLoaderOption($(this)) /*, asJson, responseType */);
}
}
});
};
// @Deprecated. Should use the generic modal instead of the login modal
function openLoginModalIfPresent(responseHtml) {
var loadedLoginModal=$(responseHtml).find("#loginModal");
if (loadedLoginModal.length>0) {
var currentLoginModal = $('#loginModal.in');
if (currentLoginModal.length>0) {
// The login modal is already open: just update the content
var currentLoginDialog = $('#loginModalDialog', currentLoginModal);
currentLoginDialog.replaceWith($('#loginModalDialog', loadedLoginModal));
$('#username').focus();
} else {
// Open a new login modal
const $existingModals = $(".modal.show");
$existingModals.modal("hide"); // Remove the background too
$existingModals.remove(); // Remove any existing modals, stick modals too because their content may need to change after login
$("#loginModal").remove(); // Just in case
$("body").append(loadedLoginModal);
$("#loginModal").on('shown.bs.modal', function (e) {
$('#username').focus();
})
$('#loginModal').modal('show');
}
yada.enableAjaxForm($('#loginForm'), null); // Login POST via Ajax
return true;
}
return false;
}
/**
* Returns true if a node has been removed from the DOM
* @param $someNode a jquery HTML element
*/
function isRemoved($someNode) {
return $someNode.closest("html").length==0;
}
// Al ritorno di un post di login, mostra eventuali notify ed esegue l'eventuale handler, oppure ricarica la pagina corrente se l'handler non c'è.
// @Deprecated. Should use the generic modal instead of the login modal
yada.handlePostLoginHandler = function(responseHtml, responseText) {
var isError = yada.isNotifyError(responseHtml);
yada.handleNotify(responseHtml);
if (yada.postLoginHandler != null) {
if (!isError) { // Esegue l'handler solo se non ho ricevuto una notifica di errore
yada.postLoginHandler(responseText, responseHtml);
}
} else {
// Not good: reload or not reload is application specific
console.error("YadaWarning: deprecated page reload after ajax login")
// If you really need to reload the page, do it in the login form successHandler
debugger; // Set a debugger point here otherwise the above message is lost on page reload
yada.loaderOn();
window.location.href=yada.removeHash(window.location.href); // Ricarico la pagina corrente (senza ripetere la post) se non ho un handler
}
yada.postLoginHandler = null;
};
// Apre il modal del login se è già presente in pagina.
// handler viene chiamato quando il login va a buon fine.
// return true se il modal è presente ed è stato aperto, false se il modal non c'è e non può essere aperto.
// @Deprecated. Should use the generic modal instead of the login modal
yada.openLoginModal = function(url, data, handler, type) {
if ($('#loginModal').length>0) {
// ?????????? A cosa servono questi postXXXX ??????????????????
postLoginUrl = url;
postLoginData = data;
yada.postLoginHandler = handler;
postLoginType = type;
$("#loginModal").on('shown.bs.modal', function (e) {
$('#username').focus();
})
$('#loginModal').modal('show');
return true;
}
return false;
}
// Apre il modal del login caricandolo via ajax.
// handler viene chiamato quando il login va a buon fine
// @Deprecated. Should use the generic modal instead of the login modal
yada.openLoginModalAjax = function(loginFormUrl, handler, errorTitle, errorText) {
yada.postLoginHandler = handler;
$.get(loginFormUrl, function(responseText, statusText) {
var responseHtml=$(yadaAjaxResponseHtmlRoot).html(responseText);
var loginReceived = openLoginModalIfPresent(responseHtml);
if (!loginReceived) {
yada.showErrorModal(errorTitle, errorText);
}
});
}
// Chiama la funzione javascript yadaCallback() se presente nell'html ricevuto dal server.
// - responseHtml = l'html ricevuto dal server, creato con $("
").html(responseText)
yada.callYadaCallbackIfPresent = function(responseHtml) {
// Cerco se c'è una funzione js da eseguire chiamata "yadaCallback".
var scriptNodes = $(responseHtml).find("script#yadaCallback");
if (scriptNodes.length>0) {
$('#callbackJavascript').append(scriptNodes);
yadaCallback();
return true;
}
return false;
}
/**
* Returns true if the page loader has been disabled on the element
*/
function hasNoLoader($element) {
return $element.hasClass("noLoader") || $element.hasClass("noloader") || $element.hasClass("yadaNoLoader") || $element.hasClass("yadaNoloader") || $element.hasClass("yadanoloader");
}
const ajaxTriggerInViewportObserver = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.intersectionRatio > 0) {
// console.log("firing " + $(entry.target).attr("data-yadahref"));
ajaxTriggerInViewportObserver.unobserve(entry.target); // Fires once only
makeAjaxCall(null, $(entry.target));
}
})
})
/**
* Enables the triggering of ajax calls when the element is entering the viewport (or is already in the viewport).
* @param $element the dom section where to look for elements to enable, can be null for the entire body
*/
yada.enableAjaxTriggerInViewport = function($element) {
if ($element==null || $element=="") {
$element = $('body');
}
var $target = $element.parent();
if ($target.length==0) {
$target = $element;
}
$('[data-yadaTriggerInViewport]', $target).each(function() {
var fetchUrl = $(this).attr("data-yadaHref") || $(this).attr("href");
if (fetchUrl!=null) {
ajaxTriggerInViewportObserver.observe(this);
// console.log("Observing " + $(this).attr("data-yadahref"));
}
});
};
/**
* Transform links and non-submit buttons into ajax links: all anchors/buttons with a class of "yadaAjax" will be sent via ajax.
* @param handler a function to call upon successful link submission, can be null
* @param $element the element on which to enable ajax links, can be null for the entire body
*/
yada.enableAjaxLinks = function(handler, $element) {
if ($element==null || $element=="") {
$element = $('body');
}
var $target = $element.parent();
if ($target.length==0) {
$target = $element;
}
$('a.yadaAjax, button.yadaAjax:not([type="submit"])', $target).each(function() {
$(this).removeClass('yadaAjax');
yada.enableAjaxLink($(this), handler);
});
// Legacy
$('.s_ajaxLink', $target).each(function() {
$(this).removeClass('s_ajaxLink');
yada.enableAjaxLink($(this), handler);
});
};
/**
* Enables ajax on a checkbox change. Will either submit a parent form or make an ajax call directly.
*/
// Legacy version
yada.enableAjaxCheckboxes = function(handler, $element) {
if ($element==null || $element=="") {
$element = $('body');
}
var $target = $element.parent();
if ($target.length==0) {
$target = $element;
}
$("input[type='checkbox'].yadaAjax", $target).each(function() {
$(this).removeClass('yadaAjax');
yada.enableAjaxCheckbox($(this), handler);
});
};
// Legacy version
yada.enableAjaxCheckbox = function($checkbox, handler) {
// If array, recurse to unroll
if ($checkbox.length>1) {
$checkbox.each(function() {
yada.enableAjaxCheckbox($(this), handler);
});
return;
}
// From here on the $checkbox is a single element, not an array
$checkbox.not('.'+markerClass).change(function(e) {
$checkbox = $(this); // Needed otherwise $checkbox could be stale (from a previous ajax replacement)
// If there is a parent form, submit it, otherwise make an ajax call defined on the checkbox
var $form = $checkbox.parents("form.yadaAjaxed");
if ($form.length>0) {
$form.submit();
return;
}
return makeAjaxCall(e, $checkbox, handler);
})
$checkbox.removeClass('yadaAjax');
$checkbox.not('.'+markerClass).addClass(markerClass);
};
/**
* Enables ajax calls on select change.
* @param handler a function to call upon successful link submission, can be null
* @param $element the element on which to enable ajax, can be null for the entire body
*/
// TODO this may conflict with yada.enableAjaxInputs and should be replaced with that one if possible
// Legacy version
yada.enableAjaxSelects = function(handler, $element) {
if ($element==null || $element=="") {
$element = $('body');
}
var $target = $element.parent();
if ($target.length==0) {
$target = $element;
}
$('select.yadaAjax', $target).each(function() {
$(this).removeClass('yadaAjax');
yada.enableAjaxSelect($(this), handler);
});
};
// Legacy version
yada.enableAjaxSelect = function($select, handler) {
// If array, recurse to unroll
if ($select.length>1) {
$select.each(function() {
yada.enableAjaxSelect($(this), handler);
});
return;
}
// From here on the $select is a single element, not an array
$select.not('.'+markerClass).change(function(e) {
$select = $(this); // Needed otherwise $select could be stale (from a previous ajax replacement)
return makeAjaxCall(e, $select, handler);
})
$select.removeClass('yadaAjax');
$select.not('.'+markerClass).addClass(markerClass);
};
/**
* Sends a link/button via ajax, it doesn't have to have class .yadaAjax.
* Buttons must have a yada-href attribute and not be submit buttons.
* Links with a "yadaLinkDisabled" class are disabled.
* @param $link the jquery anchor or button (could be an array), e.g. $('.niceLink')
* @param handler funzione chiamata in caso di successo e nessun yadaWebUtil.modalError()
*/
// See yada.enableAjaxLinks
yada.enableAjaxLink = function($link, handler) {
// If array, recurse to unroll
if ($link.length>1) {
$link.each(function() {
yada.enableAjaxLink($(this), handler);
});
return;
}
// From here on the $link is a single anchor, not an array
$link.not('.'+markerClass).click(function(e) {
$link = $(this); // Needed otherwise $link could be stale (from a previous ajax replacement)
// Fix pagination parameters if any
handlePaginationHistoryAttribute($link, $link);
//
return makeAjaxCall(e, $link, handler);
})
$link.removeClass('yadaAjax');
$link.removeClass('s_ajaxLink'); // Legacy
$link.not('.'+markerClass).addClass(markerClass);
};
function handleDragenterDragover(event) {
this.classList.add('yadaDragOver');
event.preventDefault();
event.stopPropagation();
}
function handleDragleaveDragend(event) {
this.classList.remove('yadaDragOver');
event.preventDefault();
event.stopPropagation();
}
function handleDrop($dropTarget, handler, event) {
let files = event.originalEvent.dataTransfer.files;
if (files) {
handleDroppedFiles(event, files, $dropTarget, handler);
}
}
// Needed to remove the hanlder later
var handleDropProxy = null;
/**
* Enables file upload by drag&drop.
*/
yada.enableDropUpload = function(handler, $element) {
if ($element==null || $element=="") {
$element = $('body');
}
// If array, recurse to unroll
if ($element.length>1) {
$element.each(function() {
yada.enableDropUpload(handler, $(this));
});
return;
}
$(`[data-yadaDropUpload]:not(.${markerDropTarget})`, $element).each(function() {
const $dropTarget = $(this);
$dropTarget.addClass(markerDropTarget);
const url = $dropTarget.data('yadadropupload');
if (url!=null) {
handleDropProxy = jQuery.proxy(handleDrop, null, $dropTarget, handler);
$dropTarget.on('dragenter dragover', handleDragenterDragover);
$dropTarget.on('dragleave dragend drop', handleDragleaveDragend); // Is "drop" needed here?
$dropTarget.on('drop', handleDropProxy);
}
});
}
function handleDroppedFiles(event, files, $dropTarget, handler) {
const singleFileOnly = $dropTarget.attr("data-yadaSingleFileOnly"); // Title,message
if (singleFileOnly!=null && files.length>1) {
if (singleFileOnly=="") {
singleFileOnly = "File Upload Error,Too many files";
}
const parts = singleFileOnly.split(',', 2);
yada.showErrorModal(parts[0], parts[1]);
return;
}
return makeAjaxCall(event, $dropTarget, handler);
}
/**
* Remove upload handlers and markers
*/
function disableDropUpload($element) {
$(`[data-yadaDropUpload].${markerDropTarget}`, $element).each(function() {
const $dropTarget = $(this);
$dropTarget.removeClass(markerDropTarget);
$dropTarget.off('dragenter dragover', null, handleDragenterDragover);
$dropTarget.off('dragleave dragend drop', null, handleDragleaveDragend); // Is "drop" needed here?
if (handleDropProxy!=null) {
$dropTarget.off('drop', null, handleDropProxy);
}
});
}
/**
* Returns true if the current input key is listed in the data-yadaAjaxTriggerKeys attibute.
* Also returns true if the current event is not a key event (like the input event)
* Example: yada:ajaxTriggerKeys="Enter| |,"
* @param inputEvent the input event that has been triggered
* return true if the key that triggered the event is listed in
* the "data-yadaAjaxTriggerKeys" when present or if that attribute is not present
*/
yada.isAjaxTriggerKey = function(keyEvent) {
const key = keyEvent.key; // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
if (key==null) {
return true; // Not a key event so we can't check the key
}
const input = keyEvent.target;
const $input = $(input);
const ajaxTriggerKeys = $input.attr("data-yadaAjaxTriggerKeys");
if (ajaxTriggerKeys==null) {
return true; // Do ajax call on any key when no attribute present
}
const triggerKeys = ajaxTriggerKeys.split("|");
for (var i=0; i fields that fire the "keyup" event.
* There is no need to pass any element because it is always registered even on dynamically added content.
*/
yada.enableAjaxInputs = function() {
if (this.enableAjaxInputsDone) {
// Prevent binding multiple event handlers because yada.enableAjaxInputs is called after each ajax call for legacy reasons
return;
}
// All input fields that are either yadaAjax or data-yadaHref get handled, if they are not radio
var selector = "input.yadaAjax:not([type=radio]), input[data-yadaHref]:not([type=radio])";
$(document).on("keyup", selector, function(e) {
// If "data-yadaAjaxTriggerKeys" is present, call ajax only when one of the keys is pressed.
// If attribute not present, always call ajax
if (yada.isAjaxTriggerKey(e)) {
const $input = $(this);
// Ajax calls are not executed immediately, but after a timeout that is reset each time a valid key is pressed
yada.dequeueFunctionCall(this, function(){
makeAjaxCall(e, $input, null, true);
});
}
});
// Prevent form submission on Enter otherwise the ajax call is not made.
// Browsers simulate a click on submit buttons when the enter key is pressed in a form, so we check using the "yadaDoNotSubmitNow" flag.
// This doesn't always work and may be necessary to replace submit buttons with normal buttons to prevent form submission on enter.
$(selector).each(function(){
const $input = $(this);
// Form submission by Enter keypress is allowed when the input element ajax call is not triggered by "Enter".
// This only happens if yadaAjaxTriggerKeys is present and does not contain "Enter"
const ajaxTriggerKeys = $input.attr("data-yadaAjaxTriggerKeys");
if (ajaxTriggerKeys==null || yada.stringContains(ajaxTriggerKeys, "Enter")) {
const $form = $input.closest("form").not(".yadaEnterNoSubmit");
$form.addClass("yadaEnterNoSubmit");
$form.on("submit", function(e){
// The "yadaDoNotSubmitNow" flag is added when the Enter key is pressed in any input element
// that does not call ajax when pressing Enter
const preventSubmit = $form.data("yadaDoNotSubmitNow")==true;
if (preventSubmit) {
// e.stopImmediatePropagation();
e.preventDefault(); // No submit, but exec other handlers
$form.data("yadaDoNotSubmitNow", false);
yada.log("Form submission prevented");
if (ajaxCounter<1) {
yada.loaderOff();
}
// return false;
}
});
$form.on("keydown", function(keyEvent){
if (keyEvent.key=="Enter") {
const $target = $(keyEvent.target);
// The target could be any control in the form, also non-ajax inputs
if (!$target.hasClass("yadaAjax") && $target.attr("data-yadaHref")==null) {
return; // Non-ajax element can trigger submit
}
// Prevent submission depending on value of yadaAjaxTriggerKeys, but only if there is a submit control
const wouldSubmit = $("[type=submit]:enabled", $form).length>0;
if (!wouldSubmit) {
// The enter key would not cause a submit, so keep going normally
return;
}
const targetAjaxTriggerKeys = $target.attr("data-yadaAjaxTriggerKeys");
if (targetAjaxTriggerKeys==null || yada.stringContains(targetAjaxTriggerKeys, "Enter")) {
$form.data("yadaDoNotSubmitNow", true); // Let the ajax call on the input element run
}
}
});
}
});
// Radio buttons that do not use keyup
selector = "input.yadaAjax[type=radio], input[data-yadaHref][type=radio]";
$(document).on("input", selector, function(e) {
const $input = $(this);
makeAjaxCall(e, $input, null, true);
});
this.enableAjaxInputsDone = true;
$(selector).addClass(markerClass); // Not really needed
};
/**
* Make an ajax call when a link is clicked, a select is chosen, a checkbox is selected etc.
* @param e the triggering event, can be null (for yadaTriggerInViewport)
* @param $element the jQuery element that triggered the ajax call
* @param optional additional handler to call on success
* @param allowDefault true to allow the default event action, if any
*/
function makeAjaxCall(e, $element, handler, allowDefault) {
if (e && !allowDefault==true) {
e.preventDefault();
}
if ($element.hasClass("yadaAjaxDisabled")) {
return false;
}
// Call, in sequence, the handler specified in data-successHandler and the one passed to this function
var joinedHandler = function(responseText, responseHtml) {
showFeedbackIfNeeded($element);
deleteOnSuccess($element);
responseHtml = updateOnSuccess($element, responseHtml); // This removes the added root
// No: Put the responseHtml back into a div if it is not an array and not the original yadaAjaxResponseHtml
// Can't be done because the html is removed from the page on append()
// if (!(responseHtml instanceof Array) && responseHtml.attr("class")!="yadaAjaxResponseHtml") {
// responseHtml = $(yadaAjaxResponseHtmlRoot).append(responseHtml);
// }
// responseHtml = appendOnSuccess($element, responseHtml);
var handlerNames = $element.attr("data-yadaSuccessHandler");
if (handlerNames===undefined) {
handlerNames = $element.attr("data-successHandler"); // Legacy
}
if (handlerNames!=null) {
// Can be a comma-separated list of handlers, which are called in sequence
yada.executeFunctionListByName(handlerNames, $element, responseText, responseHtml, $element[0]);
}
if (handler != null) {
handler(responseText, responseHtml, $element[0]);
}
}
var data = [];
var multipart = false;
var method = null; // Defaults to GET
var url = null;
const droppedFiles = e?.originalEvent?.dataTransfer?.files;
if (droppedFiles) {
// File drop events use a specific URL that has priority
url = $element.attr('data-yadaDropUpload');
// Compile the data object
multipart = true;
data = new FormData();
for (let i = 0; i < droppedFiles.length; i++) {
let file = droppedFiles[i];
data.append('multipartFile', file);
}
}
if (url==null || url=='') {
url = $element.attr('data-yadaHref');
}
if (url==null || url=='') {
url = $element.attr('href');
}
if (url==null || url=='') {
yada.log("No url for ajax call");
return false;
}
// Execute submit handlers if any
if (!execSubmitHandlers($element)) {
return false;
}
var confirmText = $element.attr("data-yadaConfirm") || $element.attr("data-confirm");
// Create data for submission
var value = [];
// In a select, set the data object to the selected option
if ($element.is("select")) {
$("option:selected", $element).each(function(){ // Could be a multiselect!
value.push($(this).val()); // $(this) is correct here
});
} else if ($element.is("input")) {
if ($element.prop('type')=="checkbox") {
value.push($element.prop('checked')); // Always send the element value
} else {
value.push($element.val());
}
}
// Add form data when specified with yadaFormGroup
const yadaFormGroup = $element.attr('data-yadaFormGroup');
if (yadaFormGroup!=null) {
// Find all forms of the same group
const $formGroup = $('form[data-yadaFormGroup='+yadaFormGroup+']');
if ($formGroup.length>0) {
multipart = $formGroup.filter("[enctype='multipart/form-data']").length > 0;
data = multipart ? new FormData() : [];
addAllFormsInGroup($formGroup, data);
}
}
// Any yadaRequestData is also sent (see yada.dialect.js)
const yadaRequestData = $element[0].yadaRequestData; // Object with name=value
data = mergeData(data, yadaRequestData);
// Add element value
if (value.length>0) {
const name = $element.attr("name") || "value"; // Parameter name fallback to "value" by default
const toAdd = {};
toAdd.name = name;
toAdd.value = value;
data = mergeData(data, toAdd);
}
if (!multipart) {
data = $.param(data);
} else {
method="POST";
}
//
if (confirmText!=null && confirmText!="") {
var title = $element.attr("data-yadaTitle");
var okButton = $element.attr("data-yadaOkButton") || $element.attr("data-okButton") || yada.messages.confirmButtons.ok;
var cancelButton = $element.attr("data-yadaCancelButton") || $element.attr("data-cancelButton") || yada.messages.confirmButtons.cancel;
var okShowsPreviousModal = $element.attr("data-yadaOkShowsPrevious")==null || $element.attr("data-yadaOkShowsPrevious")=="true";
yada.confirm(title, confirmText, function(result) {
if (result==true) {
yada.ajax(url, data, joinedHandler==null?joinedHandler:joinedHandler.bind($element), method, getTimeoutValue($element), getLoaderOption($element));
}
}, okButton, cancelButton, okShowsPreviousModal);
} else {
yada.ajax(url, data, joinedHandler==null?joinedHandler:joinedHandler.bind($element), method, null, getLoaderOption($element));
}
return true; // Run other listeners
}
function getTimeoutValue($element) {
var timeout = $element.attr('data-yadaTimeout');
if (timeout==null) {
timeout = $element.attr('data-timeout'); // Legacy
}
return timeout;
}
/**
*
* @param $element the link or the form
* @returns true if "data-yadaDeleteOnSuccess" was present
*/
function deleteOnSuccess($element) {
// Delete a (parent) element
// The target can be a parent when the css selector starts with parentSelector (currently "yadaParents:").
// The selector can be multiple, separated by comma.
var deleteSelector = $element.attr("data-yadaDeleteOnSuccess");
if (deleteSelector != null) {
var selectors = deleteSelector.split(',');
// If we delete the $element first, then any following selected elements may not match when relative to the $element.
// We therefore first get all selected element then delete them.
const toDelete = [];
for (var count=0; count1) {
// yadaFragment is used only when there is more than one selector, otherwise the whole result is used for replacement
$replacementArray = $(".yadaFragment", responseHtml);
if ($replacementArray.length==0) {
$replacementArray = $("._yadaReplacement_", responseHtml); // Legacy
}
}
if ($replacementArray!=null && $replacementArray.length>1) {
$return = [];
}
//
var fragmentCount = 0;
var focused = false;
for (var count=0; count0) {
// Clone so that the original responseHtml is not removed by replaceWith.
// All handlers are also cloned.
$replacement = $replacementArray.eq(fragmentCount).clone(true, true);
initObservers($replacement);
if (count==0 && $replacementArray.length==1) {
$return = $replacement;
} else {
$return.push($replacement);
}
// When there are more selectors than fragments, fragments are cycled from the first one
fragmentCount = (fragmentCount+1) % $replacementArray.length;
}
// Detect the jquery funcion used in the selector, if any
var jqueryFunction = $.fn.replaceWith; // Default
var isReplace = true;
var jqueryFunctions = [
{"jqfunction": $.fn.replaceWith, "prefix": "$replaceWith"},
{"jqfunction": $.fn.replaceWith, "prefix": "$replace"}, // $replace() is an alias for $replaceWith()
{"jqfunction": $.fn.append, "prefix": "$append"},
{"jqfunction": $.fn.prepend, "prefix": "$prepend"}
// More can be added
]
for (var i = 0; i < jqueryFunctions.length; i++) {
const toCheck = jqueryFunctions[i];
if (yada.startsWith(selector, toCheck.prefix + "(") && selector.indexOf(")") > toCheck.prefix.length) {
jqueryFunction = toCheck.jqfunction;
selector = yada.extract(selector, toCheck.prefix + "(", ")");
if (!yada.startsWith(toCheck.prefix, "$replace")) {
isReplace = false; // Not a replace function
}
break;
}
}
// Call the jquery function
jqueryFunction.call(yada.extendedSelect($element, selector), $replacement);
if (isReplace && (selector == null || selector.trim()=="")) {
// The original element has been replaced so we need to change it or following selectors won't work anymore
$element = $replacement;
}
if (!focused) {
// Focus on the first result element with data-yadaAjaxResultFocus
const $toFocus = $("[data-yadaAjaxResultFocus]:not([readonly]):not([disabled])", $replacement);
if ($toFocus.length>0) {
$toFocus.get(0).focus();
focused=true;
}
}
}
return $return;
}
/**
* Show a checkmark fading in and out
* @param $element
* @returns
*/
function showFeedbackIfNeeded($element) {
var showFeedback = $element.attr("data-yadaShowAjaxFeedback");
if (showFeedback!=undefined) {
yada.showAjaxFeedback();
}
}
/**
* Show a checkmark fading in and out, to be called in an ajax success handler when yada:showAjaxFeedback can't be used
*/
yada.showAjaxFeedback = function() {
// Check if the HTML is in page already, else insert it
const $feedbackElement = $("#yadaAjaxFeedback");
if ($feedbackElement.length==0) {
$("body").append("