code cleanup
[civicrm-core.git] / js / angular-crm-ui.js
index 021550d5dac3b2cf7f140489d33b2f5ba7a8161b..793750e8d202dad848b4b47a744215e5927f72c5 100644 (file)
@@ -1,7 +1,70 @@
 /// crmUi: Sundry UI helpers
 (function (angular, $, _) {
+  var idCount = 0;
 
   angular.module('crmUi', [])
+    // example: <form name="myForm">...<label crm-ui-label crm-for="myField">My Field</span>...<input name="myField"/>...</form>
+    //
+    // Label adapts based on <input required>, <input ng-required>, or any other validation.
+    //
+    // Note: This should work in the normal case where <label> and <input> are in roughly the same scope,
+    // but if the scopes are materially different then problems could arise.
+    .directive('crmUiLabel', function($parse) {
+      return {
+        scope: {
+          name: '@'
+        },
+        transclude: true,
+        template: '<span ng-class="cssClasses"><span ng-transclude></span> <span ng-show="crmRequired" class="crm-marker" title="This field is required.">*</span></span>',
+        link: function(scope, element, attrs) {
+          if (attrs.crmFor == 'name') {
+            throw new Error('Validation monitoring does not work for field name "name"');
+          }
+
+          // 1. Figure out form and input elements
+
+          var form = $(element).closest('form');
+          var formCtrl = scope.$parent.$eval(form.attr('name'));
+          var input = $('input[name="' + attrs.crmFor + '"],select[name="' + attrs.crmFor + '"],textarea[name="' + attrs.crmFor + '"]', form);
+          if (form.length != 1 || input.length != 1) {
+            if (console.log) console.log('Label cannot be matched to input element. Expected to find one form and one input.', form.length, input.length);
+            return;
+          }
+
+          // 2. Make sure that inputs are well-defined (with name+id).
+
+          if (!input.attr('id')) {
+            input.attr('id', 'crmUi_' + (++idCount));
+          }
+          $(element).attr('for', input.attr('id'));
+
+          // 3. Monitor is the "required" and "$valid" properties
+
+          if (input.attr('ng-required')) {
+            scope.crmRequired = scope.$parent.$eval(input.attr('ng-required'));
+            scope.$parent.$watch(input.attr('ng-required'), function(isRequired) {
+              scope.crmRequired = isRequired;
+            });
+          } else {
+            scope.crmRequired = input.prop('required');
+          }
+
+          var inputCtrl = form.attr('name') + '.' + input.attr('name');
+          scope.cssClasses = {};
+          scope.$parent.$watch(inputCtrl + '.$valid', function(newValue) {
+            //scope.cssClasses['ng-valid'] = newValue;
+            //scope.cssClasses['ng-invalid'] = !newValue;
+            scope.cssClasses['crm-error'] = !scope.$parent.$eval(inputCtrl + '.$valid') && !scope.$parent.$eval(inputCtrl + '.$pristine');
+          });
+          scope.$parent.$watch(inputCtrl + '.$pristine', function(newValue) {
+            //scope.cssClasses['ng-pristine'] = newValue;
+            //scope.cssClasses['ng-dirty'] = !newValue;
+            scope.cssClasses['crm-error'] = !scope.$parent.$eval(inputCtrl + '.$valid') && !scope.$parent.$eval(inputCtrl + '.$pristine');
+          });
+
+        }
+      };
+    })
 
     // example: <a crm-ui-lock binding="mymodel.boolfield"></a>
     // example: <a crm-ui-lock
         }
       };
     })
+    // Example: <button crm-confirm="{message: ts('Are you sure you want to continue?')}" on-yes="frobnicate(123)">Frobincate</button>
+    // Example: <button crm-confirm="{type: 'disable', obj: myObject}" on-yes="myObject.is_active=0; myObject.save()">Disable</button>
+    .directive('crmConfirm', function () {
+      // Helpers to calculate default options for CRM.confirm()
+      var defaultFuncs = {
+        'disable': function (options) {
+          return {
+            message: ts('Are you sure you want to disable this?'),
+            options: {no: ts('Cancel'), yes: ts('Disable')},
+            width: 300,
+            title: ts('Disable %1?', {
+              1: options.obj.title || options.obj.label || options.obj.name || ts('the record')
+            })
+          };
+        },
+        'revert': function (options) {
+          return {
+            message: ts('Are you sure you want to revert this?'),
+            options: {no: ts('Cancel'), yes: ts('Revert')},
+            width: 300,
+            title: ts('Revert %1?', {
+              1: options.obj.title || options.obj.label || options.obj.name || ts('the record')
+            })
+          };
+        },
+        'delete': function (options) {
+          return {
+            message: ts('Are you sure you want to delete this?'),
+            options: {no: ts('Cancel'), yes: ts('Delete')},
+            width: 300,
+            title: ts('Delete %1?', {
+              1: options.obj.title || options.obj.label || options.obj.name || ts('the record')
+            })
+          };
+        }
+      };
+      return {
+        template: '',
+        link: function (scope, element, attrs) {
+          $(element).click(function () {
+            var options = scope.$eval(attrs['crmConfirm']);
+            var defaults = (options.type) ? defaultFuncs[options.type](options) : {};
+            CRM.confirm(_.extend(defaults, options))
+              .on('crmConfirm:yes', function () { scope.$apply(attrs['onYes']); })
+              .on('crmConfirm:no', function () { scope.$apply(attrs['onNo']); });
+          });
+        }
+      };
+    })
+    .run(function($rootScope, $location) {
+      /// Example: <button ng-click="goto('home')">Go home!</button>
+      $rootScope.goto = function(path) {
+        $location.path(path);
+      };
+      // useful for debugging: $rootScope.log = console.log || function() {};
+    })
   ;
 
-})(angular, CRM.$, CRM._);
\ No newline at end of file
+})(angular, CRM.$, CRM._);