text: (function () {
    'use strict';
    
    /**
     * @ngdoc overview
     * @name ui.grid.validate
     * @description
     *
     * # ui.grid.validate
     *
     * <div class="alert alert-warning" role="alert"><strong>Alpha</strong> This feature is in development. There will almost certainly be breaking api changes, or there are major outstanding bugs.</div>
     *
     * This module provides the ability to validate cells upon change.
     *
     * Design information:
     * -------------------
     *
     * Validation is not based on angularjs validation, since it would work only when editing the field.
     * 
     * Instead it adds custom properties to any field considered as invalid.
     *
     * <br/>
     * <br/>
     *
     * <div doc-module-components="ui.grid.expandable"></div>
     */
  
    var module = angular.module('ui.grid.validate', ['ui.grid']);
    
    
    /**
     *  @ngdoc service
     *  @name ui.grid.validate.service:uiGridValidateService
     *
     *  @description Services for validation features
     */
    module.service('uiGridValidateService', ['$sce', '$q', '$http', 'i18nService', 'uiGridConstants', function ($sce, $q, $http, i18nService, uiGridConstants) {
  
      var service = {
        
        /**
         *  @ngdoc object
         *  @name validatorFactories
         *  @propertyOf ui.grid.validate.service:uiGridValidateService
         *  @description object containing all the factories used to validate data.<br/>
         *  These factories will be in the form <br/>
         *  ```
         *  {
         *    validatorFactory: function(argument) {
         *                        return function(newValue, oldValue, rowEntity, colDef) {
         *                          return true || false || promise
         *                        }
         *                      },
         *    messageFunction: function(argument) {
         *                       return string
         *                     }
         *  }
         *  ```
         *
         * Promises should return true or false as result according to the result of validation.
         */
        validatorFactories: {},
  
        
        /**
         * @ngdoc service
         * @name setExternalFactoryFunction
         * @methodOf ui.grid.validate.service:uiGridValidateService
         * @description Adds a way to retrieve validators from an external service
         * <p>Validators from this external service have a higher priority than default
         * ones
         * @param {function} externalFactoryFunction a function that accepts name and argument to pass to a
         * validator factory and that returns an object with the same properties as 
         * you can see in {@link ui.grid.validate.service:uiGridValidateService#properties_validatorFactories validatorFactories}
         */
        setExternalFactoryFunction: function(externalFactoryFunction) {
          service.externalFactoryFunction = externalFactoryFunction;
        },
        
        /**
         * @ngdoc service
         * @name clearExternalFactory
         * @methodOf ui.grid.validate.service:uiGridValidateService
         * @description Removes any link to external factory from this service
         */
        clearExternalFactory: function() {
          delete service.externalFactoryFunction;
        },
  
        /**
         * @ngdoc service
         * @name getValidatorFromExternalFactory
         * @methodOf ui.grid.validate.service:uiGridValidateService
         * @description Retrieves a validator by executing a validatorFactory
         * stored in an external service.
         * @param {string} name the name of the validator to retrieve
         * @param {object} argument an argument to pass to the validator factory
         */
        getValidatorFromExternalFactory: function(name, argument) {
          return service.externalFactoryFunction(name, argument).validatorFactory(argument);
        },
        
        /**
         * @ngdoc service
         * @name getMessageFromExternalFactory
         * @methodOf ui.grid.validate.service:uiGridValidateService
         * @description Retrieves a message stored in an external service.
         * @param {string} name the name of the validator
         * @param {object} argument an argument to pass to the message function
         */
        getMessageFromExternalFactory: function(name, argument) {
          return service.externalFactoryFunction(name, argument).messageFunction(argument);
        },
        
        /**
         * @ngdoc service
         * @name setValidator
         * @methodOf ui.grid.validate.service:uiGridValidateService
         * @description  Adds a new validator to the service
         * @param {string} name the name of the validator, must be unique
         * @param {function} validatorFactory a factory that return a validatorFunction
         * @param {function} messageFunction a function that return the error message
         */
        setValidator: function(name, validatorFactory, messageFunction) {
          service.validatorFactories[name] = {
            validatorFactory: validatorFactory,
            messageFunction: messageFunction
          };
        },
  
        /**
         * @ngdoc service
         * @name getValidator
         * @methodOf ui.grid.validate.service:uiGridValidateService
         * @description Returns a validator registered to the service
         * or retrieved from the external factory
         * @param {string} name the name of the validator to retrieve
         * @param {object} argument an argument to pass to the validator factory
         * @returns {object} the validator function
         */
        getValidator: function(name, argument) {
          if (service.externalFactoryFunction) {
            var validator = service.getValidatorFromExternalFactory(name, argument);
            if (validator) {
              return validator;
            }
          }
          if (!service.validatorFactories[name]) {
            throw ("Invalid validator name: " + name);
          }
          return service.validatorFactories[name].validatorFactory(argument);
        },
  
        /**
         * @ngdoc service
         * @name getMessage
         * @methodOf ui.grid.validate.service:uiGridValidateService
         * @description Returns the error message related to the validator 
         * @param {string} name the name of the validator
         * @param {object} argument an argument to pass to the message function
         * @returns {string} the error message related to the validator
         */
        getMessage: function(name, argument) {
          if (service.externalFactoryFunction) {
            var message = service.getMessageFromExternalFactory(name, argument);
            if (message) {
              return message;
            }
          }
          return service.validatorFactories[name].messageFunction(argument);
        },
  
        /**
         * @ngdoc service
         * @name isInvalid
         * @methodOf ui.grid.validate.service:uiGridValidateService
         * @description Returns true if the cell (identified by rowEntity, colDef) is invalid 
         * @param {object} rowEntity the row entity of the cell
         * @param {object} colDef the colDef of the cell
         * @returns {boolean} true if the cell is invalid
         */
        isInvalid: function (rowEntity, colDef) {
          return rowEntity['$$invalid'+colDef.name];
        },
  
        /**
         * @ngdoc service
         * @name setInvalid
         * @methodOf ui.grid.validate.service:uiGridValidateService
         * @description Makes the cell invalid by adding the proper field to the entity
         * @param {object} rowEntity the row entity of the cell
         * @param {object} colDef the colDef of the cell
         */
        setInvalid: function (rowEntity, colDef) {
          rowEntity['$$invalid'+colDef.name] = true;
        },
      
        /**
         * @ngdoc service
         * @name setValid
         * @methodOf ui.grid.validate.service:uiGridValidateService
         * @description Makes the cell valid by removing the proper error field from the entity
         * @param {object} rowEntity the row entity of the cell
         * @param {object} colDef the colDef of the cell
         */
        setValid: function (rowEntity, colDef) {
          delete rowEntity['$$invalid'+colDef.name];
        },
  
        /**
         * @ngdoc service
         * @name setError
         * @methodOf ui.grid.validate.service:uiGridValidateService
         * @description Adds the proper error to the entity errors field
         * @param {object} rowEntity the row entity of the cell
         * @param {object} colDef the colDef of the cell
         * @param {string} validatorName the name of the validator that is failing
         */
        setError: function(rowEntity, colDef, validatorName) {
          if (!rowEntity['$$errors'+colDef.name]) {
            rowEntity['$$errors'+colDef.name] = {};
          }
          rowEntity['$$errors'+colDef.name][validatorName] = true;
        },
  
        /**
         * @ngdoc service
         * @name clearError
         * @methodOf ui.grid.validate.service:uiGridValidateService
         * @description Removes the proper error from the entity errors field
         * @param {object} rowEntity the row entity of the cell
         * @param {object} colDef the colDef of the cell
         * @param {string} validatorName the name of the validator that is failing
         */
        clearError: function(rowEntity, colDef, validatorName) {
          if (!rowEntity['$$errors'+colDef.name]) {
            return;
          }
          if (validatorName in rowEntity['$$errors'+colDef.name]) {
              delete rowEntity['$$errors'+colDef.name][validatorName];
          }
        },
        
        /**
         * @ngdoc function
         * @name getErrorMessages
         * @methodOf ui.grid.validate.service:uiGridValidateService
         * @description returns an array of i18n-ed error messages.
         * @param {object} rowEntity gridOptions.data[] array instance whose errors we are looking for
         * @param {object} colDef the column whose errors we are looking for
         * @returns {array} An array of strings containing all the error messages for the cell
         */
        getErrorMessages: function(rowEntity, colDef) {
          var errors = [];
  
          if (!rowEntity['$$errors'+colDef.name] || Object.keys(rowEntity['$$errors'+colDef.name]).length === 0) {
            return errors;
          }
  
          Object.keys(rowEntity['$$errors'+colDef.name]).sort().forEach(function(validatorName) {
            errors.push(service.getMessage(validatorName, colDef.validators[validatorName]));
          });
          
          return errors;
        },
        
        /**
         * @ngdoc function
         * @name getFormattedErrors
         * @methodOf  ui.grid.validate.service:uiGridValidateService
         * @description returns the error i18n-ed and formatted in html to be shown inside the page.
         * @param {object} rowEntity gridOptions.data[] array instance whose errors we are looking for
         * @param {object} colDef the column whose errors we are looking for
         * @returns {object} An object that can be used in a template (like a cellTemplate) to display the
         * message inside the page (i.e. inside a div)
         */
        getFormattedErrors: function(rowEntity, colDef) {
  
          var msgString = "";
  
          var errors = service.getErrorMessages(rowEntity, colDef);
          
          if (!errors.length) {
            return;
          }
          
          errors.forEach(function(errorMsg) {
            msgString += errorMsg + "<br/>";
          });
  
          return $sce.trustAsHtml('<p><b>' + i18nService.getSafeText('validate.error') + '</b></p>' + msgString );
        },
  
        /**
         * @ngdoc function
         * @name getTitleFormattedErrors
         * @methodOf ui.grid.validate.service:uiGridValidateService
         * @description returns the error i18n-ed and formatted in javaScript to be shown inside an html 
         * title attribute.
         * @param {object} rowEntity gridOptions.data[] array instance whose errors we are looking for
         * @param {object} colDef the column whose errors we are looking for
         * @returns {object} An object that can be used in a template (like a cellTemplate) to display the
         * message inside an html title attribute
         */
        getTitleFormattedErrors: function(rowEntity, colDef) {
  
          var newLine = "\n";
  
          var msgString = "";
          
          var errors = service.getErrorMessages(rowEntity, colDef);
          
          if (!errors.length) {
            return;
          }
          
          errors.forEach(function(errorMsg) {
            msgString += errorMsg + newLine;
          });
  
          return $sce.trustAsHtml(i18nService.getSafeText('validate.error') + newLine + msgString);
        },
  
        /**
         * @ngdoc function
         * @name getTitleFormattedErrors
         * @methodOf ui.grid.validate.service:uiGridValidateService
         * @description Executes all validators on a cell (identified by row entity and column definition) and sets or clears errors
         * @param {object} rowEntity the row entity of the cell we want to run the validators on
         * @param {object} colDef the column definition of the cell we want to run the validators on
         * @param {object} newValue the value the user just entered
         * @param {object} oldValue the value the field had before
         */
        runValidators: function(rowEntity, colDef, newValue, oldValue, grid) {
          
          if (newValue === oldValue) {
            // If the value has not changed we perform no validation
            return;
          }
          
          if (typeof(colDef.name) === 'undefined' || !colDef.name) {
            throw new Error('colDef.name is required to perform validation');
          }
          
          service.setValid(rowEntity, colDef);
          
          var validateClosureFactory = function(rowEntity, colDef, validatorName) {
            return function(value) {
              if (!value) {
                service.setInvalid(rowEntity, colDef);
                service.setError(rowEntity, colDef, validatorName);
                if (grid) {
                  grid.api.validate.raise.validationFailed(rowEntity, colDef, newValue, oldValue);
                }
              }
            };
          };
  
          for (var validatorName in colDef.validators) {
            service.clearError(rowEntity, colDef, validatorName);
            var msg;
            var validatorFunction = service.getValidator(validatorName, colDef.validators[validatorName]);
            // We pass the arguments as oldValue, newValue so they are in the same order 
            // as ng-model validators (modelValue, viewValue)
            $q.when(validatorFunction(oldValue, newValue, rowEntity, colDef))
              .then(validateClosureFactory(rowEntity, colDef, validatorName)
            );
          }
        },
  
        /**
         * @ngdoc function
         * @name createDefaultValidators
         * @methodOf ui.grid.validate.service:uiGridValidateService
         * @description adds the basic validators to the list of service validators
         */
        createDefaultValidators: function() {
          service.setValidator('minLength',
                               function (argument) {
                                 return function (oldValue, newValue, rowEntity, colDef) {
                                   if (newValue === undefined || newValue === null || newValue === '') {
                                     return true;
                                   }
                                   return newValue.length >= argument;
                                 };
                               },
                                 function(argument) {
                                   return i18nService.getSafeText('validate.minLength').replace('THRESHOLD', argument);
                                 });
          
          service.setValidator('maxLength',
                               function (argument) {
                                 return function (oldValue, newValue, rowEntity, colDef) {
                                   if (newValue === undefined || newValue === null || newValue === '') {
                                     return true;
                                   }
                                   return newValue.length <= argument;
                                 };
                               },
                               function(threshold) {
                                 return i18nService.getSafeText('validate.maxLength').replace('THRESHOLD', threshold);
                               });
          
          service.setValidator('required',
                               function (argument) {
                                 return function (oldValue, newValue, rowEntity, colDef) {
                                   if (argument) {
                                     return !(newValue === undefined || newValue === null || newValue === '');
                                   }
                                   return true;
                                 };
                               },
                               function(argument) {
                                 return i18nService.getSafeText('validate.required');
                               });
        },
  
        initializeGrid: function (scope, grid) {
          grid.validate = {
          
            isInvalid: service.isInvalid,
  
            getFormattedErrors: service.getFormattedErrors,
           
            getTitleFormattedErrors: service.getTitleFormattedErrors,
  
            runValidators: service.runValidators
          };
          
          /**
           *  @ngdoc object
           *  @name ui.grid.validate.api:PublicApi
           *
           *  @description Public Api for validation feature
           */
          var publicApi = {
            events: {
              validate: {
                /**
                 * @ngdoc event
                 * @name validationFailed
                 * @eventOf  ui.grid.validate.api:PublicApi
                 * @description raised when one or more failure happened during validation 
                 * <pre>
                 *      gridApi.validate.on.validationFailed(scope, function(rowEntity, colDef, newValue, oldValue){...})
                 * </pre>
                 * @param {object} rowEntity the options.data element whose validation failed
                 * @param {object} colDef the column whose validation failed
                 * @param {object} newValue new value
                 * @param {object} oldValue old value
                 */
                validationFailed: function (rowEntity, colDef, newValue, oldValue) {
                }
              }
            },
            methods: {
              validate: {
                /**
                 * @ngdoc function
                 * @name isInvalid
                 * @methodOf  ui.grid.validate.api:PublicApi
                 * @description checks if a cell (identified by rowEntity, colDef) is invalid
                 * @param {object} rowEntity gridOptions.data[] array instance we want to check
                 * @param {object} colDef the column whose errors we want to check
                 * @returns {boolean} true if the cell value is not valid
                 */
                isInvalid: function(rowEntity, colDef) {
                  return grid.validate.isInvalid(rowEntity, colDef);
                },
                /**
                 * @ngdoc function
                 * @name getErrorMessages
                 * @methodOf  ui.grid.validate.api:PublicApi
                 * @description returns an array of i18n-ed error messages.
                 * @param {object} rowEntity gridOptions.data[] array instance whose errors we are looking for
                 * @param {object} colDef the column whose errors we are looking for
                 * @returns {array} An array of strings containing all the error messages for the cell
                 */
                getErrorMessages: function (rowEntity, colDef) {
                  return grid.validate.getErrorMessages(rowEntity, colDef);
                },
                /**
                 * @ngdoc function
                 * @name getFormattedErrors
                 * @methodOf  ui.grid.validate.api:PublicApi
                 * @description returns the error i18n-ed and formatted in html to be shown inside the page.
                 * @param {object} rowEntity gridOptions.data[] array instance whose errors we are looking for
                 * @param {object} colDef the column whose errors we are looking for
                 * @returns {object} An object that can be used in a template (like a cellTemplate) to display the
                 * message inside the page (i.e. inside a div)
                 */
                getFormattedErrors: function (rowEntity, colDef) {
                  return grid.validate.getFormattedErrors(rowEntity, colDef);
                },
                /**
                 * @ngdoc function
                 * @name getTitleFormattedErrors
                 * @methodOf  ui.grid.validate.api:PublicApi
                 * @description returns the error i18n-ed and formatted in javaScript to be shown inside an html 
                 * title attribute.
                 * @param {object} rowEntity gridOptions.data[] array instance whose errors we are looking for
                 * @param {object} colDef the column whose errors we are looking for
                 * @returns {object} An object that can be used in a template (like a cellTemplate) to display the
                 * message inside an html title attribute
                 */
                getTitleFormattedErrors: function (rowEntity, colDef) {
                  return grid.validate.getTitleFormattedErrors(rowEntity, colDef);
                }
              } 
            }
          };
          
          grid.api.registerEventsFromObject(publicApi.events);
          grid.api.registerMethodsFromObject(publicApi.methods);
  
          if (grid.edit) {
            grid.api.edit.on.afterCellEdit(scope, function(rowEntity, colDef, newValue, oldValue) {
              grid.validate.runValidators(rowEntity, colDef, newValue, oldValue, grid);
            });
          }
  
          service.createDefaultValidators();
        }
        
      };
    
      return service;
    }]);
    
    
    /**
     *  @ngdoc directive
     *  @name ui.grid.validate.directive:uiGridValidate
     *  @element div
     *  @restrict A
     *  @description Adds validating features to the ui-grid directive.
     *  @example
     <example module="app">
     <file name="app.js">
     var app = angular.module('app', ['ui.grid', 'ui.grid.edit', 'ui.grid.validate']);
  
     app.controller('MainCtrl', ['$scope', function ($scope) {
        $scope.data = [
          { name: 'Bob', title: 'CEO' },
              { name: 'Frank', title: 'Lowly Developer' }
        ];
  
        $scope.columnDefs = [
          {name: 'name', enableCellEdit: true, validators: {minLength: 3, maxLength: 9}, cellTemplate: 'ui-grid/cellTitleValidator'},
          {name: 'title', enableCellEdit: true, validators: {required: true}, cellTemplate: 'ui-grid/cellTitleValidator'}
        ];
      }]);
     </file>
     <file name="index.html">
     <div ng-controller="MainCtrl">
     <div ui-grid="{ data: data, columnDefs: columnDefs }" ui-grid-edit ui-grid-validate></div>
     </div>
     </file>
     </example>
     */
  
    module.directive('uiGridValidate', ['gridUtil', 'uiGridValidateService', function (gridUtil, uiGridValidateService) {
      return {
        priority: 0,
        replace: true,
        require: '^uiGrid',
        scope: false,
        compile: function () {
          return {
            pre: function ($scope, $elm, $attrs, uiGridCtrl) {
              uiGridValidateService.initializeGrid($scope, uiGridCtrl.grid);
            },
            post: function ($scope, $elm, $attrs, uiGridCtrl) {
            }
          };
        }
      };
    }]);
  })();