X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=js%2Fcrm.ajax.js;h=276d02ddfd729d9ebaf9574c37d2baeba7fdd29c;hb=deb6ebbb7ecab1b5dbd94ce13e52da02c19bf689;hp=b647e370c8bfd5a604da392944e2c9e3211b4b1a;hpb=10c5b975bac2283796e1461ceebc2c7888e9f1e6;p=civicrm-core.git diff --git a/js/crm.ajax.js b/js/crm.ajax.js index b647e370c8..276d02ddfd 100644 --- a/js/crm.ajax.js +++ b/js/crm.ajax.js @@ -5,45 +5,41 @@ */ (function($, CRM, undefined) { /** - * Almost like {crmURL} but on the client side - * eg: var url = CRM.url('civicrm/contact/view', {reset:1,cid:42}); - * or: $('a.my-link').crmURL(); + * @param string path + * @param string|object query + * @param string mode - optionally specify "front" or "back" */ - var tplURL = '/civicrm/example?placeholder'; - var urlInitted = false; - CRM.url = function (p, params) { - if (p == "init") { - tplURL = params; - urlInitted = true; - return; + var tplURL; + CRM.url = function (path, query, mode) { + if (typeof path === 'object') { + return tplURL = path; + } + if (!tplURL) { + CRM.console('error', 'Error: CRM.url called before initialization'); } - if (!urlInitted) { - console && console.log && console.log('Warning: CRM.url called before initialization'); + if (!mode) { + mode = CRM.config && CRM.config.isFrontend ? 'front' : 'back'; } - params = params || ''; - var frag = p.split ('?'); - var url = tplURL.replace("civicrm/example", frag[0]); + query = query || ''; + var frag = path.split('?'); + var url = tplURL[mode].replace("*path*", frag[0]); - if (typeof(params) == 'string') { - url = url.replace("placeholder", params); + if (!query) { + url = url.replace(/[?&]\*query\*/, ''); } else { - url = url.replace("placeholder", $.param(params)); + url = url.replace("*query*", typeof query === 'string' ? query : $.param(query)); } if (frag[1]) { - url += (url.indexOf('?') === (url.length - 1) ? '' : '&') + frag[1]; - } - // remove trailing "?" - if (url.indexOf('?') === (url.length - 1)) { - url = url.slice(0, (url.length - 1)); + url += (url.indexOf('?') < 0 ? '?' : '&') + frag[1]; } return url; }; - // Backwards compatible with jQuery fn + // @deprecated $.extend ({'crmURL': function (p, params) { - console && console.log && console.log('Calling crmURL from jQuery is deprecated. Please use CRM.url() instead.'); + CRM.console('warn', 'Calling crmURL from jQuery is deprecated. Please use CRM.url() instead.'); return CRM.url(p, params); } }); @@ -160,7 +156,7 @@ * @deprecated */ $.fn.crmAPI = function(entity, action, params, options) { - console && console.log && console.log('Calling crmAPI from jQuery is deprecated. Please use CRM.api() instead.'); + CRM.console('warn', 'Calling crmAPI from jQuery is deprecated. Please use CRM.api3() instead.'); return CRM.api.call(this, entity, action, params, options); }; @@ -249,8 +245,9 @@ if (this._originalContent === null) { this._originalContent = this.element.contents().detach(); } - this.options.block && $('.blockOverlay', this.element).length < 1 && this.element.block(); + this.options.block && this.element.block(); $.getJSON(url, function(data) { + that.options.block && that.element.unblock(); if (!$.isPlainObject(data)) { that._onFailure(data); return; @@ -260,7 +257,9 @@ return; } data.url = url; - that.element.trigger('crmBeforeLoad', data).html(data.content); + that.element.trigger('crmUnload').trigger('crmBeforeLoad', data); + that._beforeRemovingContent(); + that.element.html(data.content); that._handleOrderLinks(); that.element.trigger('crmLoad', data); that.options.crmForm && that.element.trigger('crmFormLoad', data); @@ -268,9 +267,21 @@ that._onFailure(); }); }, - _destroy: function() { - this.element.removeClass('crm-ajax-container'); + // Perform any cleanup needed before removing/replacing content + _beforeRemovingContent: function() { + var that = this; + if (window.tinyMCE && tinyMCE.editors) { + $.each(tinyMCE.editors, function(k) { + if ($.contains(that.element[0], this.getElement())) { + this.remove(); + } + }); + } this.options.crmForm && $('form', this.element).ajaxFormUnbind(); + }, + _destroy: function() { + this.element.removeClass('crm-ajax-container').trigger('crmUnload'); + this._beforeRemovingContent(); if (this._originalContent !== null) { this.element.empty().append(this._originalContent); } @@ -300,25 +311,52 @@ if (typeof settings.dialog.height === 'string' && settings.dialog.height.indexOf('%') > 0) { settings.dialog.height = parseInt($(window).height() * (parseFloat(settings.dialog.height)/100), 10); } - $('
' + ts('Loading') + '...
').dialog(settings.dialog); - $(settings.target).on('dialogclose', function() { - if ($(this).attr('data-unsaved-changes') !== 'true') { - $(this).crmSnippet('destroy').dialog('destroy').remove(); + // Increase percent width on small screens + if (typeof settings.dialog.width === 'string' && settings.dialog.width.indexOf('%') > 0) { + var screenWidth = $(window).width(), + percentage = parseInt(settings.dialog.width.replace('%', ''), 10), + gap = 100-percentage; + if (screenWidth < 701) { + settings.dialog.width = '100%'; } - }); - } - if (settings.dialog && !settings.dialog.title) { - $(settings.target).on('crmLoad', function(e, data) { - if (e.target === $(settings.target)[0] && data && data.title) { - $(this).dialog('option', 'title', data.title); + else if (screenWidth < 1400) { + settings.dialog.width = '' + parseInt(percentage+gap-((screenWidth - 700)/7*(gap)/100), 10) + '%'; } - }); + } + $('
' + ts('Loading') + '...
').dialog(settings.dialog); + $(settings.target) + .on('dialogclose', function() { + if ($(this).attr('data-unsaved-changes') !== 'true') { + $(this).crmSnippet('destroy').dialog('destroy').remove(); + } + }) + .on('crmLoad', function(e, data) { + // Set title + if (e.target === $(settings.target)[0] && data && !settings.dialog.title && data.title) { + $(this).dialog('option', 'title', data.title); + } + // Adjust height to fit content (small delay to allow elements to render) + window.setTimeout(function() { + var currentHeight = $(settings.target).parent().outerHeight(), + padding = currentHeight - $(settings.target).height(), + newHeight = $(settings.target).prop('scrollHeight') + padding, + menuHeight = $('#civicrm-menu').outerHeight(), + maxHeight = $(window).height() - menuHeight; + newHeight = newHeight > maxHeight ? maxHeight : newHeight; + if (newHeight > (currentHeight + 15)) { + $(settings.target).dialog('option', { + position: {my: 'center', at: 'center center+' + (menuHeight / 2), of: window}, + height: newHeight + }); + } + }, 500); + }); } $(settings.target).crmSnippet(settings).crmSnippet('refresh'); return $(settings.target); }; CRM.loadForm = function(url, options) { - var settings = { + var formErrors = [], settings = { crmForm: { ajaxForm: {}, autoClose: true, @@ -344,9 +382,7 @@ // 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'); + widget.attr('data-unsaved-changes', dirty ? 'true' : 'false'); if (dirty) { var id = widget.attr('id') + '-unsaved-alert', title = widget.dialog('option', 'title'), @@ -357,15 +393,17 @@ }); } } - 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.data('uiDialog') && widget.on('dialogbeforeclose', function(e) { + // CRM-14353 - Warn unsaved changes if user clicks close button or presses "esc" + if (e.originalEvent) { + cancelAction(); + } + }); widget.on('crmFormLoad.crmForm', function(event, data) { - var $el = $(this) - .attr('data-unsaved-changes', 'false'); - var settings = $el.crmSnippet('option', 'crmForm'); + var $el = $(this).attr('data-unsaved-changes', 'false'), + settings = $el.crmSnippet('option', 'crmForm'); settings.cancelButton && $(settings.cancelButton, this).click(function(e) { e.preventDefault(); var returnVal = settings.onCancel.call($el, e); @@ -373,6 +411,7 @@ $el.trigger('crmFormCancel', e); if ($el.data('uiDialog') && settings.autoClose) { cancelAction(); + $el.dialog('close'); } else if (!settings.autoClose) { $el.crmSnippet('resetUrl').crmSnippet('refresh'); @@ -387,7 +426,6 @@ dataType: 'json', success: function(response) { 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 && (response.status === 'redirect' || (settings.refreshAction && $.inArray(response.buttonName, settings.refreshAction) >= 0))) { @@ -395,7 +433,8 @@ $el.data('civiCrmSnippet')._originalUrl = response.userContext; $el.crmSnippet('resetUrl').crmSnippet('refresh'); } - else if ($el.data('uiDialog') && settings.autoClose) { + // Close if we are on the original url or the action was "delete" (in which case returning to view may be inappropriate) + else if ($el.data('uiDialog') && (settings.autoClose || response.action === 8)) { $el.dialog('close'); } else if (settings.autoClose === false) { @@ -403,12 +442,14 @@ } } else { + $el.crmSnippet('option', 'block') && $el.unblock(); response.url = data.url; $el.html(response.content).trigger('crmLoad', response).trigger('crmFormLoad', response); if (response.status === 'form_error') { + formErrors = []; $el.trigger('crmFormError', response); $.each(response.errors || [], function(formElement, msg) { - $('[name="'+formElement+'"]', $el).crmError(msg); + formErrors.push($('[name="'+formElement+'"]', $el).crmError(msg)); }); } } @@ -419,8 +460,16 @@ this.updateElement && this.updateElement(); }); } + if (window.tinyMCE && tinyMCE.editors) { + $.each(tinyMCE.editors, function() { + this.save(); + }); + } }, beforeSubmit: function(submission) { + $.each(formErrors, function() { + this && this.close && this.close(); + }); $el.crmSnippet('option', 'block') && $el.block(); $el.trigger('crmFormSubmit', submission); } @@ -432,7 +481,35 @@ return false; }); } - // Alow a button to prevent ajax submit + // Show form buttons as part of the dialog + if ($el.data('uiDialog')) { + var buttonContainers = '.crm-submit-buttons, .action-link', + buttons = [], + added = []; + $(buttonContainers, $el).find('input.crm-form-submit, a.button').each(function() { + var $el = $(this), + label = $el.is('input') ? $el.attr('value') : $el.text(), + identifier = $el.attr('name') || $el.attr('href'); + if (!identifier || identifier === '#' || $.inArray(identifier, added) < 0) { + var $icon = $el.find('.icon'), + button = {'data-identifier': identifier, text: label, click: function() { + $el.click(); + }}; + if ($icon.length) { + button.icons = {primary: $icon.attr('class')}; + } else { + var action = $el.hasClass('cancel') ? 'close' : (identifier.substr(identifier.length-4) === '_new' ? 'plus' : 'check'); + button.icons = {primary: 'ui-icon-' + action}; + } + buttons.push(button); + added.push(identifier); + } + // display:none causes the form to not submit when pressing "enter" + $el.parents(buttonContainers).css({height: 0, padding: 0, margin: 0, overflow: 'hidden'}); + }); + $el.dialog('option', 'buttons', buttons); + } + // Allow a button to prevent ajax submit $('input[data-no-ajax-submit=true]').click(function() { $(this).closest('form').ajaxFormUnbind(); }); @@ -442,7 +519,7 @@ return widget; }; /** - * Handler for jQuery click event e.g. $('a').click(CRM.popup) + * Handler for jQuery click event e.g. $('a').click(CRM.popup); */ CRM.popup = function(e) { var $el = $(this).first(), @@ -462,9 +539,6 @@ else if ($el.hasClass('medium-popup')) { settings.dialog.width = settings.dialog.height = '50%'; } - else if ($el.hasClass('huge-popup')) { - settings.dialog.height = '90%'; - } var dialog = popup(url, settings); // Trigger events from the dialog on the original link element $el.trigger('crmPopupOpen', [dialog]); @@ -482,14 +556,14 @@ }; /** * An event callback for CRM.popup or a standalone function to refresh the content around a given element - * @param e event|selector + * @param e {event|selector} */ CRM.refreshParent = function(e) { // Use e.target if input smells like an event, otherwise assume it's a jQuery selector var $el = (e.stopPropagation && e.target) ? $(e.target) : $(e), $table = $el.closest('.dataTable'); // Call native refresh method on ajax datatables - if ($table && $.fn.DataTable.fnIsDataTable($table[0]) && $table.dataTable().fnSettings().sAjaxSource) { + if ($table.length && $.fn.DataTable.fnIsDataTable($table[0]) && $table.dataTable().fnSettings().sAjaxSource) { // Refresh ALL datatables - needed for contact relationship tab $.each($.fn.dataTable.fnTables(), function() { $(this).dataTable().fnSettings().sAjaxSource && $(this).unblock().dataTable().fnDraw();