* @see https://wiki.civicrm.org/confluence/display/CRMDOC/AJAX+Interface
* @see https://wiki.civicrm.org/confluence/display/CRMDOC/Ajax+Pages+and+Forms
*/
-(function($, CRM) {
+(function($, CRM, undefined) {
/**
* Almost like {crmURL} but on the client side
* eg: var url = CRM.url('civicrm/contact/view', {reset:1,cid:42});
entity: 'api3',
action: 'call',
json: JSON.stringify(entity)
- }
+ };
+ status = action;
}
var ajax = $.ajax({
url: CRM.url('civicrm/ajax/rest'),
this.element.trigger('crmAjaxFail', data);
CRM.alert(ts('Unable to reach the server. Please refresh this page in your browser and try again.'), ts('Network Error'), 'error');
},
+ _onError: function(data) {
+ this.element.attr('data-unsaved-changes', 'false').trigger('crmAjaxError', data);
+ if (this.options.crmForm && this.options.crmForm.autoClose && this.element.data('uiDialog')) {
+ this.element.dialog('close');
+ }
+ },
_formatUrl: function(url) {
// Strip hash
url = url.split('#')[0];
}
this.options.block && $('.blockOverlay', this.element).length < 1 && this.element.block();
$.getJSON(url, function(data) {
- if (typeof(data) != 'object' || typeof(data.content) != 'string') {
+ if (!$.isPlainObject(data)) {
that._onFailure(data);
return;
}
+ if (data.status === 'error') {
+ that._onError(data);
+ return;
+ }
data.url = url;
that.element.trigger('crmBeforeLoad', data).html(data.content);
that._handleOrderLinks();
}
$('<div id="'+ settings.target.substring(1) +'"><div class="crm-loading-element">' + ts('Loading') + '...</div></div>').dialog(settings.dialog);
$(settings.target).on('dialogclose', function() {
- $(this).crmSnippet('destroy').dialog('destroy').remove();
+ if ($(this).attr('data-unsaved-changes') !== 'true') {
+ $(this).crmSnippet('destroy').dialog('destroy').remove();
+ }
});
}
if (settings.dialog && !settings.dialog.title) {
refreshAction: ['next_new', 'submit_savenext', 'upload_new'],
cancelButton: '.cancel',
openInline: 'a.open-inline, a.button, a.action-item',
- onCancel: function(event) {},
- onError: function(data) {
- var $el = $(this);
- $el.html(data.content).trigger('crmLoad', data).trigger('crmFormLoad', data).trigger('crmFormError', data);
- if (typeof(data.errors) == 'object') {
- $.each(data.errors, function(formElement, msg) {
- $('[name="'+formElement+'"]', $el).crmError(msg);
- });
- }
- }
+ onCancel: function(event) {}
}
};
// Move options that belong to crmForm. Others will be passed through to crmSnippet
var widget = CRM.loadPage(url, settings).off('.crmForm');
+ // CRM-14353 - Warn of unsaved changes for all forms except those which have opted out
+ function cancelAction() {
+ var dirty = CRM.utils.initialValueChanged($('form:not([data-warn-changes=false])', widget));
+ widget
+ .attr('data-unsaved-changes', dirty ? 'true' : 'false')
+ .dialog('close');
+ if (dirty) {
+ var id = widget.attr('id') + '-unsaved-alert',
+ title = widget.dialog('option', 'title'),
+ alert = CRM.alert('<p>' + ts('%1 has not been saved.', {1: title}) + '</p><p><a href="#" id="' + id + '">' + ts('Restore') + '</a></p>', ts('Unsaved Changes'), 'alert unsaved-dialog', {expires: 60000});
+ $('#' + id).button({icons: {primary: 'ui-icon-arrowreturnthick-1-w'}}).click(function(e) {
+ widget.attr('data-unsaved-changes', 'false').dialog('open');
+ e.preventDefault();
+ });
+ }
+ }
+ if (widget.data('uiDialog')) {
+ // CRM-14353 - This is a bit harsh but we are removing jQuery UI's event handler from the close button and adding our own
+ widget.parent().find('.ui-dialog-titlebar-close').first().off().click(cancelAction);
+ }
+
widget.on('crmFormLoad.crmForm', function(event, data) {
- var $el = $(this);
+ var $el = $(this)
+ .attr('data-unsaved-changes', 'false');
var settings = $el.crmSnippet('option', 'crmForm');
- settings.cancelButton && $(settings.cancelButton, this).click(function(event) {
- var returnVal = settings.onCancel.call($el, event);
+ settings.cancelButton && $(settings.cancelButton, this).click(function(e) {
+ e.preventDefault();
+ var returnVal = settings.onCancel.call($el, e);
if (returnVal !== false) {
- $el.trigger('crmFormCancel', event);
+ $el.trigger('crmFormCancel', e);
if ($el.data('uiDialog') && settings.autoClose) {
- $el.dialog('close');
+ cancelAction();
}
else if (!settings.autoClose) {
$el.crmSnippet('resetUrl').crmSnippet('refresh');
}
}
- return returnVal === false;
});
if (settings.validate) {
$("form", this).validate(typeof(settings.validate) == 'object' ? settings.validate : CRM.validate.params);
url: data.url.replace(/reset=1[&]?/, ''),
dataType: 'json',
success: function(response) {
- if (response.status !== 'form_error') {
+ if (response.content === undefined) {
$el.crmSnippet('option', 'block') && $el.unblock();
$el.trigger('crmFormSuccess', response);
// Reset form for e.g. "save and new"
- if (response.userContext && settings.refreshAction && $.inArray(response.buttonName, settings.refreshAction) >= 0) {
+ if (response.userContext && (response.status === 'redirect' || (settings.refreshAction && $.inArray(response.buttonName, settings.refreshAction) >= 0))) {
// Force reset of original url
$el.data('civiCrmSnippet')._originalUrl = response.userContext;
$el.crmSnippet('resetUrl').crmSnippet('refresh');
}
else {
response.url = data.url;
- settings.onError.call($el, response);
+ $el.html(response.content).trigger('crmLoad', response).trigger('crmFormLoad', response);
+ if (response.status === 'form_error') {
+ $el.trigger('crmFormError', response);
+ $.each(response.errors || [], function(formElement, msg) {
+ $('[name="'+formElement+'"]', $el).crmError(msg);
+ });
+ }
}
},
beforeSerialize: function(form, options) {
return false;
});
}
+ // Alow a button to prevent ajax submit
+ $('input[data-no-ajax-submit=true]').click(function() {
+ $(this).closest('form').ajaxFormUnbind();
+ });
// For convenience, focus the first field
$('input[type=text], textarea, select', this).filter(':visible').first().not('.dateplugin').focus();
});
};
/**
* Handler for jQuery click event e.g. $('a').click(CRM.popup)
- * @returns {boolean}
*/
CRM.popup = function(e) {
var $el = $(this).first(),
e.preventDefault();
};
/**
- * An event callback for CRM.popup or a standalone function to refresh the content around a popup link
+ * An event callback for CRM.popup or a standalone function to refresh the content around a given element
* @param e event|selector
*/
CRM.refreshParent = function(e) {
};
$(function($) {
- $('body').on('click', 'a.crm-popup', CRM.popup);
+ $('body')
+ .on('click', 'a.crm-popup', CRM.popup)
+ // Close unsaved dialog messages
+ .on('dialogopen', function(e) {
+ $('.alert.unsaved-dialog .ui-notify-cross', '#crm-notification-container').click();
+ })
+ // Destroy old unsaved dialog
+ .on('dialogcreate', function(e) {
+ $('.ui-dialog-content.crm-ajax-container:hidden[data-unsaved-changes=true]').crmSnippet('destroy').dialog('destroy').remove();
+ });
});
}(jQuery, CRM));