diff -r 000000000000 -r bca7a7e058a3 src/pyams_skin/resources/js/myams-form.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/resources/js/myams-form.js Thu Feb 13 11:43:31 2020 +0100 @@ -0,0 +1,765 @@ +/** + * MyAMS forms management + */ +(function($, globals) { + + var ams = globals.MyAMS; + + ams.form = { + + /** + * Init forms to activate form change listeners + * + * @param element: the parent element + */ + init: function(element) { + + $('FORM', element).each(function() { + var form = $(this); + // Store value of hidden inputs + $('INPUT.select2[type="hidden"]', form).each(function() { + var input = $(this); + input.data('ams-select2-input-value', input.val()); + }); + }); + + // Activate form changes if required + var forms; + if (ams.warnOnFormChange) { + forms = $('FORM[data-ams-warn-on-change!="false"]', element); + } else { + forms = $('FORM[data-ams-warn-on-change="true"]', element); + } + forms.each(function() { + var form = $(this), + formChangedCallback = form.data('ams-form-changed-callback') || + ams.formChangedCallback; + $('INPUT[type="text"], ' + + 'INPUT[type="checkbox"], ' + + 'INPUT[type="radio"], ' + + 'SELECT, ' + + 'TEXTAREA, ' + + '[data-ams-changed-event]', form).each(function() { + var source = $(this); + if (source.data('ams-ignore-change') !== true) { + var event = source.data('ams-changed-event') || 'change'; + source.on(event, function () { + ams.form.setChanged(form); + ams.executeFunctionByName(formChangedCallback, form, source); + }); + } + }); + form.on('reset', function() { + ams.form.resetChanged($(this)); + }); + }); + }, + + /** + * Set focus to first container input + */ + setFocus: function(container) { + var focused = $('[data-ams-focus-target]', container).first(); + if (!focused.exists()) { + focused = $('input, select', container).first(); + } + if (focused.exists()) { + if (focused.hasClass('select2-input')) { + focused = focused.parents('.select2'); + } + if (focused.hasClass('select2')) { + setTimeout(function() { + focused.select2('focus'); + if (focused.data('ams-focus-open') === true) { + focused.select2('open'); + } + }, 100); + } else { + focused.focus(); + } + } + }, + + /** + * Check for modified forms before exiting + */ + checkBeforeUnload: function() { + var forms = $('FORM[data-ams-form-changed="true"]'); + if (forms.exists()) { + return ams.i18n.FORM_CHANGED_WARNING; + } + }, + + /** + * Check for modified forms before loading new inner content + */ + confirmChangedForm: function(element, callback, cancelCallback) { + if (typeof(element) === 'function') { + callback = element; + element = undefined; + } + var forms = $('FORM[data-ams-form-changed="true"]', element); + if (forms.exists()) { + if (cancelCallback) { + if (globals.confirm(ams.i18n.FORM_CHANGED_WARNING, ams.i18n.WARNING)) { + callback.call(element); + } else { + cancelCallback.call(element); + } + } else { + ams.skin && ams.skin.bigBox({ + title: ams.i18n.WARNING, + content: ' ' + ams.i18n.FORM_CHANGED_WARNING, + buttons: ams.i18n.BTN_OK_CANCEL + }, function(button) { + if (button === ams.i18n.BTN_OK) { + callback.call(element); + } + }); + } + } else { + callback.call(element); + } + }, + + /** + * Update form "chenged" status flag + */ + setChanged: function(form) { + form.attr('data-ams-form-changed', true); + }, + + /** + * Reset form changed flag + */ + resetChanged: function(form) { + if (form !== undefined) { + $(form).removeAttr('data-ams-form-changed'); + } + }, + + /** + * Submit given form + */ + submit: function(form, handler, submitOptions) { + // Check params + form = $(form); + if (!form.exists()) { + return false; + } + if (typeof(handler) === 'object') { + submitOptions = handler; + handler = undefined; + } + // Prevent multiple submits of the same form + if (form.data('submitted')) { + if (!form.data('ams-form-hide-submitted')) { + ams.skin && ams.skin.messageBox('warning', { + title: ams.i18n.WAIT, + content: ams.i18n.FORM_SUBMITTED, + icon: 'fa fa-save shake animated', + timeout: form.data('ams-form-alert-timeout') || 5000 + }); + } + return false; + } + // Check submit validators + if (ams.form && !ams.form._checkSubmitValidators(form)) { + return false; + } + // Remove remaining status messages + $('.alert-danger, SPAN.state-error', form).not('.persistent').remove(); + $('.state-error', form).removeClassPrefix('state-'); + // Check submit button + var button = $(form.data('ams-submit-button')); + if (button && !button.data('ams-form-hide-loading')) { + button.data('ams-progress-content', button.html()); + button.button('loading'); + } + ams.ajax && ams.ajax.check($.fn.ajaxSubmit, + ams.baseURL + 'ext/jquery-form-3.49' + ams.devext + '.js', + function() { + + function _submitAjaxForm(form, options) { + + var button, + buttonData, + buttonTarget, + data = form.data(), + formOptions = data.amsFormOptions, + formData, + formDataCallback; + + var progressHandler, + progressInterval, + progressCallback, + progressEndCallback; + + // Inner progress status handler + function _getProgress(handler, progress_id) { + + var timeout; + + function _clearProgressStatus() { + clearTimeout(timeout); + ams.form.resetAfterSubmit(form, button); + button.html(button.data('ams-progress-content')); + ams.executeFunctionByName(progressEndCallback, form, button); + ams.form.resetChanged(form); + } + + function _getProgressStatus() { + ams.ajax && ams.ajax.post(handler, + {progress_id: progress_id}, + {error: _clearProgressStatus}, + ams.getFunctionByName(progressCallback) || function(result, status) { + if (status === 'success') { + if (result.status === 'running') { + if (result.message) { + button.text(result.message); + } else { + var text = button.data('ams-progress-text') || ams.i18n.PROGRESS; + if (result.current) { + text += ': ' + result.current + '/ ' + (result.length || 100); + } else { + text += '...'; + } + button.text(text); + } + timeout = setTimeout(_getProgressStatus, progressInterval); + } else if (result.status === 'finished') { + _clearProgressStatus(); + } + } else { + _clearProgressStatus(); + } + }); + } + + button.button('loading'); + timeout = setTimeout(_getProgressStatus, progressInterval); + } + + // Initialize form data + if (submitOptions) { + formDataCallback = submitOptions.formDataInitCallback; + } + if (formDataCallback) { + delete submitOptions.formDataInitCallback; + } else { + formDataCallback = data.amsFormDataInitCallback; + } + if (formDataCallback) { + var veto = {}; + formData = ams.executeFunctionByName(formDataCallback, form, veto); + if (veto.veto) { + button = form.data('ams-submit-button'); + if (button) { + button.button('reset'); + } + ams.form.finalizeSubmitFooter.call(form); + return false; + } + } else { + formData = data.amsFormData || {}; + } + + // Check submit button for custom action handler and target + button = $(form.data('ams-submit-button')); + if (button && button.exists()) { + buttonData = button.data(); + buttonTarget = buttonData.amsFormSubmitTarget; + } else { + buttonData = {}; + } + + // Check action URL + var url; + var formHandler = handler || buttonData.amsFormHandler || data.amsFormHandler || ''; + if (formHandler.startsWith(window.location.protocol)) { + url = formHandler; + } else { + var action = buttonData.amsFormAction || form.attr('action').replace(/#/, ''); + if (action.startsWith(window.location.protocol)) { + url = action; + } else { + url = ams.ajax && (ams.ajax.getAddr() + action); + } + url += formHandler; + } + progressHandler = buttonData.amsProgressHandler || data.amsProgressHandler || ''; + progressInterval = buttonData.amsProgressInterval || data.amsProgressInterval || 1000; + progressCallback = buttonData.amsProgressCallback || data.amsProgressCallback; + progressEndCallback = buttonData.amsProgressEndCallback || data.amsProgressEndCallback; + + // Initialize submit target with AJAX indicator + var target = null; + if (submitOptions && submitOptions.initSubmitTarget) { + ams.executeFunctionByName(submitOptions.initSubmitTarget, form); + } else { + if (data.amsFormInitSubmitTarget) { + target = $(buttonTarget || data.amsFormSubmitTarget || '#content'); + ams.executeFunctionByName(data.amsFormInitSubmit || 'MyAMS.form.initSubmit', form, target); + } else if (!data.amsFormHideSubmitFooter) { + ams.executeFunctionByName(data.amsFormInitSubmit || 'MyAMS.form.initSubmitFooter', form); + } + } + + // Complete form data + if (submitOptions) { + formData = $.extend({}, formData, submitOptions.form_data); + } + + // Check progress handler + var hasUpload; + if (progressHandler) { + formData.progress_id = ams.generateUUID(); + } else { + // Check progress meter via Apache progress module + hasUpload = typeof (options.uuid) !== 'undefined'; + if (hasUpload) { + if (url.indexOf('X-Progress-ID') < 0) { + url += "?X-Progress-ID=" + options.uuid; + } + delete options.uuid; + } + } + + // Initialize default AJAX settings + var defaults = { + url: url, + type: 'post', + cache: false, + data: formData, + dataType: data.amsFormDatatype, + beforeSerialize: function(/*form, options*/) { + form.trigger('myams.form.before-serialize'); + if (typeof (globals.tinyMCE) !== 'undefined') { + globals.tinyMCE.triggerSave(); + } + }, + beforeSubmit: function(data, form /*, options*/) { + form.trigger('myams.form.before-submit', [data]); + form.data('submitted', true); + if (form.data('ams-form-reset-before-submit')) { + setTimeout(function() { + ams.form.resetAfterSubmit(form); + }, 250); + } + }, + error: function(request, status, error, form) { + form.trigger('myams.form.submit-error', [request, status, error]); + if (target) { + ams.executeFunctionByName(data.amsFormSubmitError || 'MyAMS.form.finalizeSubmitOnError', form, target); + } + ams.form.resetAfterSubmit(form); + }, + iframe: hasUpload + }; + + // Initialize IFrame for custom download target + var downloadTarget = (submitOptions && submitOptions.downloadTarget) || data.amsFormDownloadTarget; + if (downloadTarget) { + var iframe = $('iframe[name="' + downloadTarget + '"]'); + if (!iframe.exists()) { + iframe = $('').hide() + .attr('name', downloadTarget) + .appendTo($('body')); + } + defaults = $.extend({}, defaults, { + iframe: true, + iframeTarget: iframe, + success: function(result, status, request, form) { + form.trigger('myams.form.after-submit', [result, status, request]); + var modal = $(form).parents('.modal-dialog'); + if (modal.exists()) { + ams.dialog && ams.dialog.close(form); + } else { + var callback, + button = form.data('ams-submit-button'); + if (button) { + callback = button.data('ams-form-submit-callback'); + } + if (!callback) { + callback = ams.getFunctionByName(data.amsFormSubmitCallback) || ams.form._submitCallback; + } + try { + callback.call(form, result, status, request, form); + } finally { + ams.form.resetAfterSubmit(form); + ams.form.resetChanged(form); + } + } + } + }); + } else { + defaults = $.extend({}, defaults, { + error: function(request, status, error, form) { + if (target) { + ams.executeFunctionByName(data.amsFormSubmitError || 'MyAMS.form.finalizeSubmitOnError', form, target); + } + ams.form.resetAfterSubmit(form); + }, + success: function(result, status, request, form) { + form.trigger('myams.form.after-submit', [result, status, request]); + var callback, + button = form.data('ams-submit-button'); + if (button) { + callback = button.data('ams-form-submit-callback'); + } + if (!callback) { + callback = ams.getFunctionByName(data.amsFormSubmitCallback) || ams.form._submitCallback; + } + try { + callback.call(form, result, status, request, form); + } finally { + ams.form.resetAfterSubmit(form); + ams.form.resetChanged(form); + } + }, + iframe: hasUpload + }); + } + var settings = $.extend({}, defaults, options, formOptions, submitOptions); + + // Initialize progress handler + if (progressHandler) { + _getProgress(progressHandler, formData.progress_id); + } + + // Submit form + $(form).ajaxSubmit(settings); + + // If external download target is specified, reset form submit button and footer + if (downloadTarget) { + var modal = $(form).parents('.modal-dialog'); + var keepModal = modal.exists() && button.exists() && button.data('ams-keep-modal'); + if (modal.exists() && (keepModal !== true)) { + ams.dialog && ams.dialog.close(form); + } else { + if (!progressHandler) { + setTimeout(function() { + ams.form.resetAfterSubmit(form, button); + ams.form.resetChanged(form); + }, button.data('ams-form-reset-timeout') || 2000); + } + } + } + } + + var hasUpload = (form.data('ams-form-ignore-uploads') !== true) && + ($('INPUT[type="file"]', form).length > 0); + if (hasUpload) { + // JQuery-progressbar plug-in must be loaded synchronously!! + // Otherwise, hidden input fields created by jquery-validate plug-in + // and matching named buttons will be deleted (on first form submit) + // before JQuery-form plug-in can get them when submitting the form... + ams.ajax && ams.ajax.check($.progressBar, + ams.baseURL + 'ext/jquery-progressbar' + ams.devext + '.js'); + var settings = $.extend({}, { + uuid: $.progressBar.submit(form) + }); + _submitAjaxForm(form, settings); + } else { + _submitAjaxForm(form, {}); + } + }); + return false; + }, + + /** + * Initialize AJAX submit call + * + * @param this: the submitted form + * @param target: the form submit container target + * @param message: the optional message + */ + initSubmit: function(target, message) { + var form = $(this), + spin = ''; + if (!message) { + message = form.data('ams-form-submit-message'); + } + if (message) { + spin += '' + message + ''; + } + $(target).html('