// https://civicrm.org/licensing
var CRM = CRM || {};
-var cj = jQuery;
+var cj = CRM.$ = jQuery;
+CRM._ = _;
/**
* Short-named function for string translation, defined in global scope so it's available everywhere.
functions: []
};
-(function ($, undefined) {
+(function ($, _, undefined) {
"use strict";
// Theme classes for unattached elements
$elect = $(this),
val = $elect.val() || [],
opts = removePlaceholder ? '' : '[value!=""]';
- if (typeof(val) !== 'array') {
+ if (!$.isArray(val)) {
val = [val];
}
$elect.find('option' + opts).remove();
});
};
+/**
+ * Compare Form Input values against cached initial value.
+ *
+ * @return {Boolean} true if changes have been made.
+ */
+ CRM.utils.initialValueChanged = function(el) {
+ var isDirty = false;
+ $(':input:visible, :input.select2-offscreen', el).each(function () {
+ var initialValue = $(this).data('crm-initial-value');
+ if (initialValue !== undefined && initialValue != $(this).val()) {
+ isDirty = true;
+ }
+ });
+ return isDirty;
+ }
+
/**
* Wrapper for select2 initialization function; supplies defaults
* @param options object
return $(this).each(function () {
var
$el = $(this),
- defaults = {allowClear: !$el.hasClass('required')};
+ settings = {allowClear: !$el.hasClass('required')};
// quickform doesn't support optgroups so here's a hack :(
$('option[value^=crm_optgroup]', this).each(function () {
$(this).nextUntil('option[value^=crm_optgroup]').wrapAll('<optgroup label="' + $(this).text() + '" />');
});
// Defaults for single-selects
if ($el.is('select:not([multiple])')) {
- defaults.minimumResultsForSearch = 10;
+ settings.minimumResultsForSearch = 10;
if ($('option:first', this).val() === '') {
- defaults.placeholderOption = 'first';
+ settings.placeholderOption = 'first';
}
}
- $el.select2($.extend(defaults, $el.data('select-params') || {}, options || {}));
+ $.extend(settings, $el.data('select-params') || {}, options || {});
+ if (settings.ajax) {
+ $el.addClass('crm-ajax-select');
+ }
+ $el.select2(settings);
});
};
options.select = options.select || {};
return $(this).each(function() {
var
- $el = $(this),
+ $el = $(this).off('.crmEntity'),
entity = options.entity || $el.data('api-entity') || 'contact',
selectParams = {};
$el.data('api-entity', entity);
$el.data('select-params', $.extend({}, $el.data('select-params') || {}, options.select));
$el.data('api-params', $.extend({}, $el.data('api-params') || {}, options.api));
$el.data('create-links', options.create || $el.data('create-links'));
- $el.addClass('crm-ajax-select crm-' + entity + '-ref');
+ $el.addClass('crm-form-entityref crm-' + entity + '-ref');
var settings = {
// Use select2 ajax helper instead of CRM.api because it provides more value
ajax: {
}
}
};
- if ($el.data('create-links')) {
+ if ($el.data('create-links') && entity.toLowerCase() === 'contact') {
selectParams.formatInputTooShort = function() {
var txt = $el.data('select-params').formatInputTooShort || $.fn.select2.defaults.formatInputTooShort.call(this);
if ($el.data('create-links')) {
var txt = $el.data('select-params').formatNoMatches || $.fn.select2.defaults.formatNoMatches;
return txt + '<br />' + formatSelect2CreateLinks($el);
};
- $el.off('.createLinks').on('select2-open.createLinks', function() {
+ $el.on('select2-open.crmEntity', function() {
var $el = $(this);
$('#select2-drop').off('.crmEntity').on('click.crmEntity', 'a.crm-add-entity', function(e) {
$el.select2('close');
});
});
}
- $el.crmSelect2($.extend(settings, $el.data('select-params'), selectParams));
+ // Create new items inline - works for tags
+ else if ($el.data('create-links')) {
+ selectParams.createSearchChoice = function(term, data) {
+ if (!_.findKey(data, {label: term})) {
+ return {id: "0", term: term, label: term + ' (' + ts('new tag') + ')'};
+ }
+ };
+ selectParams.tokenSeparators = [','];
+ selectParams.createSearchChoicePosition = 'bottom';
+ }
+ $el.crmSelect2($.extend(settings, $el.data('select-params'), selectParams))
+ .on('select2-selecting.crmEntity', function(e) {
+ if (e.val === "0") {
+ e.object.label = e.object.term;
+ CRM.api3(entity, 'create', $.extend({name: e.object.term}, $el.data('api-params').params || {}))
+ .done(function(created) {
+ var
+ multiple = !!$el.data('select-params').multiple,
+ val = $el.select2('val'),
+ data = $el.select2('data'),
+ item = {id: created.id, label: e.object.term};
+ if (val === "0") {
+ $el.select2('data', item, true);
+ }
+ else if ($.isArray(val) && $.inArray("0", val) > -1) {
+ _.remove(data, {id: "0"});
+ data.push(item);
+ $el.select2('data', data, true);
+ }
+ });
+ }
+ });
});
};
.on('crmLoad', function(e) {
$('table.row-highlight', e.target)
.off('.rowHighlight')
- .on('change.rowHighlight', 'input.select-row, input.select-rows', function () {
- var target, table = $(this).closest('table');
+ .on('change.rowHighlight', 'input.select-row, input.select-rows', function (e, data) {
+ var filter, $table = $(this).closest('table');
if ($(this).hasClass('select-rows')) {
- target = $('tbody tr', table);
- $('input.select-row', table).prop('checked', $(this).prop('checked'));
+ filter = $(this).prop('checked') ? ':not(:checked)' : ':checked';
+ $('input.select-row' + filter, $table).prop('checked', $(this).prop('checked')).trigger('change', 'master-selected');
}
else {
- target = $(this).closest('tr');
- $('input.select-rows', table).prop('checked', $(".select-row:not(':checked')", table).length < 1);
+ $(this).closest('tr').toggleClass('crm-row-selected', $(this).prop('checked'));
+ if (data !== 'master-selected') {
+ $('input.select-rows', $table).prop('checked', $(".select-row:not(':checked')", $table).length < 1);
+ }
}
- target.toggleClass('crm-row-selected', $(this).is(':checked'));
})
.find('input.select-row:checked').parents('tr').addClass('crm-row-selected');
$('.crm-select2:not(.select2-offscreen, .select2-container)', e.target).crmSelect2();
$('.crm-form-entityref:not(.select2-offscreen, .select2-container)', e.target).crmEntityRef();
+ // Cache Form Input initial values
+ $('form[data-warn-changes] :input', e.target).each(function() {
+ $(this).data('crm-initial-value', $(this).val());
+ });
})
- // Modal dialogs should disable scrollbars
.on('dialogopen', function(e) {
- if ($(e.target).dialog('option', 'modal')) {
- $(e.target).addClass('modal-dialog');
+ var $el = $(e.target);
+ // Modal dialogs should disable scrollbars
+ if ($el.dialog('option', 'modal')) {
+ $el.addClass('modal-dialog');
$('body').css({overflow: 'hidden'});
}
+ $el.parent().find('.ui-dialog-titlebar-close').attr('title', ts('Close'));
+ // Add resize button
+ if ($el.parent().hasClass('crm-container') && $el.dialog('option', 'resizable')) {
+ $el.parent().find('.ui-dialog-titlebar').append($('<button class="crm-dialog-titlebar-resize ui-dialog-titlebar-close" title="'+ts('Toggle fullscreen')+'" style="right:2em;"/>').button({icons: {primary: 'ui-icon-newwin'}, text: false}));
+ $('.crm-dialog-titlebar-resize', $el.parent()).click(function(e) {
+ if ($el.data('origSize')) {
+ $el.dialog('option', $el.data('origSize'));
+ $el.data('origSize', null);
+ } else {
+ $el.data('origSize', {
+ position: 'center',
+ width: $el.dialog('option', 'width'),
+ height: $el.dialog('option', 'height')
+ });
+ var menuHeight = $('#civicrm-menu').height();
+ $el.dialog('option', {width: '100%', height: ($(window).height() - menuHeight), position: [0, menuHeight]});
+ }
+ e.preventDefault();
+ });
+ }
})
.on('dialogclose', function(e) {
+ // Restore scrollbars when closing modal
if ($('.ui-dialog .modal-dialog').not(e.target).length < 1) {
$('body').css({overflow: ''});
}
- });
+ })
+ .on('submit', function(e) {
+ // CRM-14353 - disable changes warn when submitting the form
+ $(this).removeAttr('data-warn-changes');
+ })
+ ;
+
+ window.onbeforeunload = function() {
+ if (CRM.utils.initialValueChanged($('form[data-warn-changes]'))) {
+ return ts('You have unsaved changes.');
+ }
+ };
/**
* Function to make multiselect boxes behave as fields in small screens
/**
* @see https://wiki.civicrm.org/confluence/display/CRMDOC/Notification+Reference
*/
- CRM.confirm = function (buttons, options, cancelLabel) {
- var dialog, callbacks = {};
- cancelLabel = cancelLabel || ts('Cancel');
- var settings = {
+ CRM.confirm = function (options) {
+ var dialog, settings = {
title: ts('Confirm Action'),
message: ts('Are you sure you want to continue?'),
- resizable: false,
- modal: true,
width: 'auto',
+ modal: true,
+ resizable: false,
+ dialogClass: 'crm-container crm-confirm',
close: function () {
- $(dialog).dialog('destroy').remove();
+ $(this).dialog('destroy').remove();
},
- buttons: {}
- };
-
- settings.buttons[cancelLabel] = function () {
- dialog.trigger('crmConfirmNo').dialog('close');
+ options: {
+ no: ts('Cancel'),
+ yes: ts('Continue')
+ }
};
- options = options || {};
- $.extend(settings, options);
- if ($.isFunction(buttons)) {
- callbacks[ts('Continue')] = buttons;
- }
- else if (_.isString(buttons) || !buttons) {
- callbacks[buttons || ts('Continue')] = function() {};
+ $.extend(settings, ($.isFunction(options) ? arguments[1] : options) || {});
+ if (!settings.buttons && $.isPlainObject(settings.options)) {
+ settings.buttons = [];
+ $.each(settings.options, function(key, label) {
+ settings.buttons.push({
+ text: label,
+ click: function() {
+ var event = $.Event('crmConfirm:' + key);
+ $(this).trigger(event);
+ if (!event.isDefaultPrevented()) {
+ dialog.dialog('close');
+ }
+ }
+ });
+ });
}
- else {
- callbacks = buttons;
+ dialog = $('<div class="crm-confirm-dialog"></div>').html(settings.message);
+ delete settings.options;
+ delete settings.message;
+ if ($.isFunction(options)) {
+ dialog.on('crmConfirm:yes', options);
}
- $.each(callbacks, function (label, callback) {
- settings.buttons[label] = function () {
- dialog.trigger('crmConfirmYes');
- if (callback.call(dialog) !== false) {
- dialog.dialog('close');
- }
- };
- });
- dialog = $('<div class="crm-confirm-dialog"></div>')
- .html(options.message)
- .dialog(settings)
- .trigger('crmLoad');
- return dialog;
+ return dialog.dialog(settings).trigger('crmLoad');
};
/**
});
// Handle qf form errors
$('form :input.error', this).one('blur', function() {
- // ignore autocomplete fields
- if ($(this).is('.ac_input')) {
- return;
- }
-
$('.ui-notify-message.error a.ui-notify-close').click();
$(this).removeClass('error');
$(this).next('span.crm-error').remove();
messagesFromMarkup.call($('#crm-container'));
}
- // bind the event for image popup
$('body')
- .on('click', 'a.crm-image-popup', function() {
- var o = $('<div class="crm-custom-image-popup"><img src=' + $(this).attr('href') + '></div>');
-
- CRM.confirm('',
- {
- title: ts('Preview'),
- message: o
- },
- ts('Done')
- );
- return false;
+ // bind the event for image popup
+ .on('click', 'a.crm-image-popup', function(e) {
+ CRM.confirm({
+ title: ts('Preview'),
+ resizable: true,
+ message: '<div class="crm-custom-image-popup"><img src=' + $(this).attr('href') + '></div>',
+ options: null
+ });
+ e.preventDefault();
})
.on('click', function (event) {
})
.on('change', 'input.crm-form-radio:checked', function() {
$(this).siblings('.crm-clear-link').css({visibility: ''});
- });
-
- $().crmtooltip();
- });
+ })
- $.fn.crmAccordions = function (speed) {
- var container = $(this).length > 0 ? $(this) : $('.crm-container');
- speed = speed === undefined ? 200 : speed;
- container
- .off('click.crmAccordions')
- // Allow normal clicking of links
+ // Allow normal clicking of links within accordions
.on('click.crmAccordions', 'div.crm-accordion-header a', function (e) {
- e.stopPropagation && e.stopPropagation();
+ e.stopPropagation();
})
- .on('click.crmAccordions', '.crm-accordion-header, .crm-collapsible .collapsible-title', function () {
+ // Handle accordions
+ .on('click.crmAccordions', '.crm-accordion-header, .crm-collapsible .collapsible-title', function (e) {
if ($(this).parent().hasClass('collapsed')) {
- $(this).next().css('display', 'none').slideDown(speed);
+ $(this).next().css('display', 'none').slideDown(200);
}
else {
- $(this).next().css('display', 'block').slideUp(speed);
+ $(this).next().css('display', 'block').slideUp(200);
}
$(this).parent().toggleClass('collapsed');
- return false;
+ e.preventDefault();
});
- };
+
+ $().crmtooltip();
+ });
+ /**
+ * @deprecated
+ */
+ $.fn.crmAccordions = function () {};
+ /**
+ * Collapse or expand an accordion
+ * @param speed
+ */
$.fn.crmAccordionToggle = function (speed) {
$(this).each(function () {
if ($(this).hasClass('collapsed')) {
result = sign + (j ? i.substr(0, j) + separator : '') + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + separator) + (2 ? decimal + Math.abs(value - i).toFixed(2).slice(2) : '');
return format.replace(/1.*234.*56/, result);
};
-})(jQuery);
+})(jQuery, _);