Merge pull request #5059 from totten/master-validation
[civicrm-core.git] / js / Common.js
index 11c6c5d5e31e05fd15e991156ffc332eed3a32e6..4d2ae26f9ddbcf591b580e23a5a23088d97f32af 100644 (file)
@@ -13,7 +13,13 @@ CRM._ = _;
  */
 function ts(text, params) {
   "use strict";
-  text = CRM.strings[text] || text;
+  var d = (params && params.domain) ? ('strings::' + params.domain) : null;
+  if (d && CRM[d] && CRM[d][text]) {
+    text = CRM[d][text];
+  }
+  else if (CRM['strings'][text]) {
+    text = CRM['strings'][text];
+  }
   if (typeof(params) === 'object') {
     for (var i in params) {
       if (typeof(params[i]) === 'string' || typeof(params[i]) === 'number') {
@@ -39,12 +45,14 @@ function ts(text, params) {
  */
 function on_load_init_blocks(showBlocks, hideBlocks, elementType) {
   if (elementType == null) {
-    var elementType = 'block';
+    elementType = 'block';
   }
 
+  var myElement, i;
+
   /* This loop is used to display the blocks whose IDs are present within the showBlocks array */
-  for (var i = 0; i < showBlocks.length; i++) {
-    var myElement = document.getElementById(showBlocks[i]);
+  for (i = 0; i < showBlocks.length; i++) {
+    myElement = document.getElementById(showBlocks[i]);
     /* getElementById returns null if element id doesn't exist in the document */
     if (myElement != null) {
       myElement.style.display = elementType;
@@ -55,8 +63,8 @@ function on_load_init_blocks(showBlocks, hideBlocks, elementType) {
   }
 
   /* This loop is used to hide the blocks whose IDs are present within the hideBlocks array */
-  for (var i = 0; i < hideBlocks.length; i++) {
-    var myElement = document.getElementById(hideBlocks[i]);
+  for (i = 0; i < hideBlocks.length; i++) {
+    myElement = document.getElementById(hideBlocks[i]);
     /* getElementById returns null if element id doesn't exist in the document */
     if (myElement != null) {
       myElement.style.display = 'none';
@@ -80,13 +88,14 @@ function on_load_init_blocks(showBlocks, hideBlocks, elementType) {
  * @param  invert               Boolean - if true, we HIDE target on value match; if false, we SHOW target on value match
  */
 function showHideByValue(trigger_field_id, trigger_value, target_element_id, target_element_type, field_type, invert) {
+  var target, j;
 
   if (field_type == 'select') {
     var trigger = trigger_value.split("|");
     var selectedOptionValue = cj('#' + trigger_field_id).val();
 
-    var target = target_element_id.split("|");
-    for (var j = 0; j < target.length; j++) {
+    target = target_element_id.split("|");
+    for (j = 0; j < target.length; j++) {
       if (invert) {
         cj('#' + target[j]).show();
       }
@@ -108,8 +117,8 @@ function showHideByValue(trigger_field_id, trigger_value, target_element_id, tar
   }
   else {
     if (field_type == 'radio') {
-      var target = target_element_id.split("|");
-      for (var j = 0; j < target.length; j++) {
+      target = target_element_id.split("|");
+      for (j = 0; j < target.length; j++) {
         if (cj('[name="' + trigger_field_id + '"]:first').is(':checked')) {
           if (invert) {
             cj('#' + target[j]).hide();
@@ -309,6 +318,11 @@ CRM.strings = CRM.strings || {};
         $(this).nextUntil('option[value^=crm_optgroup]').wrapAll('<optgroup label="' + $(this).text() + '" />');
         $(this).remove();
       });
+
+      // quickform does not support disabled option, so yet another hack to
+      // add disabled property for option values
+      $('option[value^=crm_disabled_opt]', this).attr('disabled', 'disabled');
+
       // Defaults for single-selects
       if ($el.is('select:not([multiple])')) {
         settings.minimumResultsForSearch = 10;
@@ -488,9 +502,9 @@ CRM.strings = CRM.strings || {};
       filter = $.extend({}, $el.data('user-filter') || {});
     if (filter.key && filter.value) {
       // Special case for contact type/sub-type combo
-      if (filter.key === 'contact_type' && (filter.value.indexOf('.') > 0)) {
-        combined.params.contact_type = filter.value.split('.')[0];
-        combined.params.contact_sub_type = filter.value.split('.')[1];
+      if (filter.key === 'contact_type' && (filter.value.indexOf('__') > 0)) {
+        combined.params.contact_type = filter.value.split('__')[0];
+        combined.params.contact_sub_type = filter.value.split('__')[1];
       } else {
         // Allow json-encoded api filters e.g. {"BETWEEN":[123,456]}
         combined.params[filter.key] = filter.value.charAt(0) === '{' ? $.parseJSON(filter.value) : filter.value;
@@ -551,7 +565,7 @@ CRM.strings = CRM.strings || {};
       }
       else if (this.key == 'contact_type' && typeof params.contact_sub_type === 'undefined') {
         this.options = _.remove(this.options, function(option) {
-          return option.key.indexOf(params.contact_type + '.') === 0;
+          return option.key.indexOf(params.contact_type + '__') === 0;
         });
         result.push(this);
       }
@@ -597,7 +611,7 @@ CRM.strings = CRM.strings || {};
         CRM.utils.setOptions($valField, filterSpec.options, false, filter.value);
       } else {
         $valField.prop('disabled', true);
-        CRM.api3(filterSpec.entity || $el.data('api-entity'), 'getoptions', {field: filter.key, sequential: 1})
+        CRM.api3(filterSpec.entity || $el.data('api-entity'), 'getoptions', {field: filter.key, context: 'search', sequential: 1})
           .done(function(result) {
             var entity = $el.data('api-entity').toLowerCase(),
               globalFilterSpec = _.find(CRM.config.entityRef.filters[entity], {key: filter.key}) || {};
@@ -836,8 +850,9 @@ CRM.strings = CRM.strings || {};
     var opts = $.extend({
       start: ts('Saving...'),
       success: ts('Saved'),
-      error: function() {
-        CRM.alert(ts('Sorry an error occurred and your information was not saved'), ts('Error'));
+      error: function(data) {
+        var msg = $.isPlainObject(data) && data.error_message;
+        CRM.alert(msg || ts('Sorry an error occurred and your information was not saved'), ts('Error'), 'error');
       }
     }, options || {});
     var $msg = $('<div class="crm-status-box-outer status-start"><div class="crm-status-box-inner"><div class="crm-status-box-msg">' + opts.start + '</div></div></div>')
@@ -848,7 +863,9 @@ CRM.strings = CRM.strings || {};
       if (endMsg) {
         $msg.removeClass('status-start').addClass('status-' + status).find('.crm-status-box-msg').html(endMsg);
         window.setTimeout(function() {
-          $msg.fadeOut('slow', function() {$msg.remove()});
+          $msg.fadeOut('slow', function() {
+            $msg.remove();
+          });
         }, 2000);
       } else {
         $msg.remove();
@@ -865,6 +882,27 @@ CRM.strings = CRM.strings || {};
       });
   };
 
+  // Convert an Angular promise to a jQuery promise
+  CRM.toJqPromise = function(aPromise) {
+    var jqDeferred = $.Deferred();
+    aPromise.then(
+      function(data) { jqDeferred.resolve(data); },
+      function(data) { jqDeferred.reject(data); }
+      // should we also handle progress events?
+    );
+    return jqDeferred.promise();
+  };
+
+  CRM.toAPromise = function($q, jqPromise) {
+    var aDeferred = $q.defer();
+    jqPromise.then(
+      function(data) { aDeferred.resolve(data); },
+      function(data) { aDeferred.reject(data); }
+      // should we also handle progress events?
+    );
+    return aDeferred.promise;
+  };
+
   /**
    * @see https://wiki.civicrm.org/confluence/display/CRMDOC/Notification+Reference
    */
@@ -979,6 +1017,12 @@ CRM.strings = CRM.strings || {};
     };
   };
 
+  CRM.addStrings = function(domain, strings) {
+    var bucket = (domain == 'civicrm' ? 'strings' : 'strings::' + domain);
+    CRM[bucket] = CRM[bucket] || {};
+    _.extend(CRM[bucket], strings);
+  };
+
   /**
    * @see https://wiki.civicrm.org/confluence/display/CRMDOC/Notification+Reference
    */
@@ -1003,14 +1047,14 @@ CRM.strings = CRM.strings || {};
           title = $label.text();
         }
       }
-      $(this).addClass('error');
+      $(this).addClass('crm-error');
     }
     var msg = CRM.alert(text, title, 'error', $.extend(extra, options));
     if ($(this).length) {
       var ele = $(this);
       setTimeout(function () {
         ele.one('change', function () {
-          msg && msg.close && msg.close();
+          if (msg && msg.close) msg.close();
           ele.removeClass('error');
           label.removeClass('crm-error');
         });
@@ -1080,7 +1124,7 @@ CRM.strings = CRM.strings || {};
         if (typeof(response.crmMessages) == 'object') {
           $.each(response.crmMessages, function(n, msg) {
             CRM.alert(msg.text, msg.title, msg.type, msg.options);
-          })
+          });
         }
         if (response.backtrace) {
           CRM.console('log', response.backtrace);
@@ -1161,7 +1205,9 @@ CRM.strings = CRM.strings || {};
   /**
    * @deprecated
    */
-  $.fn.crmAccordions = function () {};
+  $.fn.crmAccordions = function () {
+    CRM.console('warn', 'Warning: $.crmAccordions was called. This function is deprecated and should not be used.');
+  };
   /**
    * Collapse or expand an accordion
    * @param speed