text: /*
   edu-field v0.0.65
   (c) Educarm, http://www.educarm.es
   License: MIT
  */
  // Main eduField Module
  //Declare app level module which depends on filters, and services
  var eduFieldServices = angular.module('edu-field.services', []);
  var eduFieldDirectives = angular.module('edu-field.directives', []);
  var eduFieldFilters = angular.module('edu-field.filters', []);
  var eduFieldTpl = angular.module('edu-field.tpl', []);
  // initialization of services into the main module
  angular.module('eduField', [
    'ngFileUpload',
    'edu-field.services',
    'edu-field.directives',
    'edu-field.filters',
    'edu-field.tpl',
    'ngResource',
    'textAngular',
    'angularFileUpload',
    'ui.bootstrap'
  ]);
  eduFieldDirectives.directive('eduComplete', [
    '$parse',
    '$http',
    '$sce',
    '$timeout',
    function ($parse, $http, $sce, $timeout) {
      return {
        restrict: 'EA',
        priority: 0,
        scope: {
          'id': '@id_',
          'name': '@name',
          'onblur': '&onblur',
          'onfocus': '&onfocus',
          'autofocus': '=autofocus',
          'required': '=required',
          'placeholder': '@placeholder',
          'selectedObject': '=selectedobject',
          'url': '@url',
          'urldataloadall': '@urldataloadall',
          'dataField': '@datafield',
          'titleField': '@titlefield',
          'descriptionField': '@descriptionfield',
          'imageField': '@imagefield',
          'imageUri': '@imageuri',
          'inputClass': '@inputclass',
          'userPause': '@pause',
          'localData': '=localdata',
          'searchFields': '@searchfields',
          'minLengthUser': '@minlength',
          'matchClass': '@matchclass',
          'disabled': '@disabled',
          'readonly': '@readonly',
          'widthdropdown': '=widthdropdown',
          'whenNotFoundReturnSearchString': '=whennotfoundreturnsearchstring'
        },
        template: '<div class="eduComplete-holder">' + '\t<input id="{{id}}" name="{{name}}" ng-disabled="{{disabled}}" ng-readonly={{readonly}} edu-focus="{{autofocus}}"  ng-blur="onblur()" ng-change="onChangeSearchString(searchStr)"  ng-focus="onfocus()"  ng-required="{{required}}" ng-model="searchStr" type="text" placeholder="{{placeholder}}" class="{{inputClass}}" onmouseup="this.select();" ng-focus="resetHideResults()" ng-blur="hideResults()" />' + '\t<div id="{{id}}_dropdown" class="eduComplete-dropdown" ng-if="showDropdown"  ng-style="widthdropdown2"> ' + '\t\t<div class="eduComplete-searching" ng-show="searching">Buscando...</div>' + '\t\t<div class="eduComplete-searching" ng-show="!searching && (!results || results.length == 0)">No hay resultados</div>' + '\t\t<div class="eduComplete-row" ng-repeat="result in results" ng-click="selectResult(result)"  ng-mouseover="hoverRow()" ng-class="{\'eduComplete-selected-row\': $index == currentIndex}">' + '\t\t\t<div ng-if="imageField" class="eduComplete-image-holder">' + '\t\t\t\t<img ng-if="result.image && result.image != \'\'" ng-src="{{result.image}}" class="eduComplete-image"/>' + '\t\t\t\t<div ng-if="!result.image && result.image != \'\'" class="eduComplete-image-default"></div>' + '\t\t\t</div>' + '\t\t\t<div class="eduComplete-title" ng-if="matchClass" ng-bind-html="result.title"></div>' + '\t\t\t<div class="eduComplete-title" ng-if="!matchClass">{{ result.title }}</div>' + '\t\t\t<div ng-if="result.description && result.description != \'\'" class="eduComplete-description">{{result.description}}</div>' + '\t\t</div>' + '\t</div>' + '</div>',
        link: function ($scope, elem, attrs) {
          $scope.lastSearchTerm = null;
          $scope.currentIndex = null;
          $scope.justChanged = false;
          $scope.searchTimer = null;
          $scope.hideTimer = null;
          $scope.searching = false;
          $scope.pause = 500;
          $scope.minLength = 3;
          $scope.widthdropdown2 = { width: $scope.widthdropdown };
          if ($scope.urldataloadall && $scope.urldataloadall != '') {
            $http.get($scope.urldataloadall, {}).success(function (responseData, status, headers, config) {
              $scope.localData = responseData;
              console.log('Todos los datos:' + angular.toJson(responseData));  //$scope.processResults( responseData , str);
            }).error(function (data, status, headers, config) {
              console.log('error');
            });
          }
          if ($scope.minLengthUser && $scope.minLengthUser != '') {
            $scope.minLength = $scope.minLengthUser;
          }
          if ($scope.userPause) {
            $scope.pause = $scope.userPause;
          }
          isNewSearchNeeded = function (newTerm, oldTerm) {
            return newTerm.length >= $scope.minLength && newTerm != oldTerm;
          };
          $scope.processResults = function (responseData, str) {
            if (responseData && responseData.length > 0) {
              $scope.results = [];
              var titleFields = [];
              if ($scope.titleField && $scope.titleField != '') {
                titleFields = $scope.titleField.split(',');
              }
              for (var i = 0; i < responseData.length; i++) {
                // Get title variables
                var titleCode = [];
                for (var t = 0; t < titleFields.length; t++) {
                  titleCode.push(responseData[i][titleFields[t]]);
                }
                var description = '';
                if ($scope.descriptionField) {
                  description = responseData[i][$scope.descriptionField];
                }
                var data = '';
                if ($scope.dataField) {
                  data = responseData[i][$scope.dataField];
                }
                var imageUri = '';
                if ($scope.imageUri) {
                  imageUri = $scope.imageUri;
                }
                var image = '';
                if ($scope.imageField) {
                  image = imageUri + responseData[i][$scope.imageField];
                }
                var text = titleCode.join(' ');
                if ($scope.matchClass) {
                  var re = new RegExp(str, 'i');
                  var strPart = text.match(re)[0];
                  text = $sce.trustAsHtml(text.replace(re, '<span class="' + $scope.matchClass + '">' + strPart + '</span>'));
                }
                var resultRow = {
                    title: text,
                    description: description,
                    image: image,
                    data: data,
                    allData: responseData[i]
                  };
                $scope.results[$scope.results.length] = resultRow;
              }
            } else {
              $scope.results = [];
              //if search string not found, return this how value
              if ($scope.whenNotFoundReturnSearchString) {
                $scope.selectedObject = str;
              }
            }
          };
          $scope.searchTimerComplete = function (str) {
            // Begin the search
            if (str && str != '' && str.length >= $scope.minLength) {
              if ($scope.localData) {
                var searchFields = $scope.searchFields.split(',');
                var matches = [];
                for (var i = 0; i < $scope.localData.length; i++) {
                  var match = false;
                  for (var s = 0; s < searchFields.length; s++) {
                    match = match || typeof $scope.localData[i][searchFields[s]] === 'string' && typeof str === 'string' && $scope.localData[i][searchFields[s]].toLowerCase().indexOf(str.toLowerCase()) >= 0;
                  }
                  if (match) {
                    matches[matches.length] = $scope.localData[i];
                  }
                }
                $scope.searching = false;
                $scope.processResults(matches, str);
              } else {
                $http.get($scope.url + str + '&field=' + $scope.titleField, {}).success(function (responseData, status, headers, config) {
                  $scope.searching = false;
                  $scope.processResults(responseData, str);
                }).error(function (data, status, headers, config) {
                  console.log('error');
                });
              }
            }
          };
          $scope.hideResults = function () {
            $scope.hideTimer = $timeout(function () {
              $scope.showDropdown = false;
            }, $scope.pause);
          };
          $scope.resetHideResults = function () {
            if ($scope.hideTimer) {
              $timeout.cancel($scope.hideTimer);
            }
            ;
          };
          $scope.hoverRow = function (index) {
            $scope.currentIndex = index;
          };
          $scope.keyPressed = function (event) {
            if (!(event.which == 38 || event.which == 40 || event.which == 13)) {
              if (!$scope.searchStr || $scope.searchStr == '') {
                $scope.showDropdown = false;
                $scope.lastSearchTerm = null;
              } else if (isNewSearchNeeded($scope.searchStr, $scope.lastSearchTerm)) {
                $scope.lastSearchTerm = $scope.searchStr;
                $scope.showDropdown = true;
                $scope.currentIndex = -1;
                $scope.results = [];
                if ($scope.searchTimer) {
                  $timeout.cancel($scope.searchTimer);
                }
                $scope.searching = true;
                $scope.searchTimer = $timeout(function () {
                  $scope.searchTimerComplete($scope.searchStr);
                }, $scope.pause);
              }
            } else {
              event.preventDefault();
            }
          };
          //cuando hay cambios en el value del control autocomplete
          $scope.$watch('selectedObject', function (value) {
            if (typeof value !== 'undefined' && value != '') {
              //coloca en el input text que ve el usuario el titulo o nombre que corresponde al valor que hay en la propiedad value del control autocomplete
              var titleField = '';
              var str = $scope.selectedObject;
              if ($scope.titleField && $scope.titleField != '') {
                titleFields = $scope.titleField.split(',');
              }
              if ($scope.localData) {
                var matches = [];
                for (var i = 0; i < $scope.localData.length; i++) {
                  var match = false;
                  match = typeof $scope.localData[i][$scope.dataField] === 'string' && typeof str === 'string' && $scope.localData[i][$scope.dataField].toLowerCase().indexOf(str.toLowerCase()) >= 0;
                  if (match) {
                    //matches[matches.length] = $scope.localData[i];
                    $scope.searchStr = '';
                    for (var t = 0; t < titleFields.length; t++) {
                      if (t == 0) {
                        $scope.searchStr = $scope.localData[i][titleFields[t]];
                      } else {
                        $scope.searchStr = $scope.searchStr + ' ' + $scope.localData[i][titleFields[t]];
                      }
                    }
                    break;
                  }
                }
                $scope.searching = false;
              } else {
                $http.get($scope.url + str + '&field=' + $scope.dataField, {}).success(function (data, status, headers, config) {
                  $scope.searching = false;
                  if (data.length > 0) {
                    $scope.searchStr = '';
                    for (var t = 0; t < titleFields.length; t++) {
                      if (t == 0) {
                        $scope.searchStr = data[0][titleFields[t]];
                      } else {
                        $scope.searchStr = $scope.searchStr + ' ' + data[0][titleFields[t]];
                      }
                    }
                  }
                }).error(function (data, status, headers, config) {
                  console.log('error');
                });
              }
            } else {
              // cuando ponemos el value del autocomplete a null o vacio, elimina la el contenido del textbox que ve el usuario
              $scope.searchStr = '';
            }
          });
          $scope.selectResult = function (result) {
            if ($scope.matchClass) {
              result.title = result.title.toString().replace(/(<([^>]+)>)/gi, '');
            }
            $scope.searchStr = $scope.lastSearchTerm = result.title;
            $scope.selectedObject = result.data;
            $scope.showDropdown = false;
            $scope.results = [];
            //$scope.value=result;
            $scope.$parent.onChange(result);
          };
          $scope.onChangeSearchString = function (text) {
            $scope.$parent.onChangeSearchString(text);
          };
          var inputField = elem.find('input');
          inputField.on('keyup', $scope.keyPressed);
          elem.on('keyup', function (event) {
            if (event.which === 40) {
              if ($scope.results && $scope.currentIndex + 1 < $scope.results.length) {
                $scope.currentIndex++;
                $scope.$apply();
                event.preventDefault;
                event.stopPropagation();
              }
              $scope.$apply();
            } else if (event.which == 38) {
              if ($scope.currentIndex >= 1) {
                $scope.currentIndex--;
                $scope.$apply();
                event.preventDefault;
                event.stopPropagation();
              }
            } else if (event.which == 13) {
              if ($scope.results && $scope.currentIndex >= 0 && $scope.currentIndex < $scope.results.length) {
                $scope.selectResult($scope.results[$scope.currentIndex]);
                $scope.$apply();
                event.preventDefault;
                event.stopPropagation();
              } else {
                $scope.results = [];
                $scope.$apply();
                event.preventDefault;
                event.stopPropagation();
              }
            } else if (event.which == 27) {
              $scope.results = [];
              $scope.showDropdown = false;
              $scope.$apply();
            } else if (event.which == 8) {
              $scope.selectedObject = null;
              $scope.$apply();
            }
          });
          $scope.onXX = function () {
            console.log('edu-complete.js link--> onXX');
          };
        },
        controller: [
          '$scope',
          'Upload',
          'FileUploader',
          function fieldController($scope, Upload, FileUploader) {
            $scope.onXX = function () {
              console.log('edu-complete.js controller--> onXX');
            };
          }
        ]
      };
    }
  ]);
  'use strict';
  /*eduFieldDirectives.directive('validDate', function () {
      return {
          restrict: 'A',
          require: 'ngModel',
          link: function (scope, element, attrs, control) {
              control.$parsers.push(function (viewValue) {
                  var newDate = model.$viewValue;
                  control.$setValidity("invalidDate", true);  
                  if (typeof newDate === "object" || newDate == "") return newDate;  // pass through if we clicked date from popup
                  if (!newDate.match(/^\d{1,2}\/\d{1,2}\/((\d{2})|(\d{4}))$/))
                      control.$setValidity("invalidDate", false);
                  return viewValue;
              });
          }
      };
  })*/
  eduFieldDirectives.factory('timepickerState', function () {
    var pickers = [];
    return {
      addPicker: function (picker) {
        pickers.push(picker);
      },
      closeAll: function () {
        for (var i = 0; i < pickers.length; i++) {
          pickers[i].close();
        }
      }
    };
  }).directive('scrollableTable', [
    '$timeout',
    '$q',
    '$parse',
    function ($timeout, $q, $parse) {
      return {
        transclude: true,
        restrict: 'E',
        scope: {
          rows: '=watch',
          sortFn: '=',
          changes: '='
        },
        template: '<div class="scrollableContainer">' + '<div class="headerSpacer"></div>' + '<div class="scrollArea" ng-transclude></div>' + '</div>',
        controller: [
          '$scope',
          '$element',
          '$attrs',
          function ($scope, $element, $attrs) {
            // define an API for child directives to view and modify sorting parameters
            this.getSortExpr = function () {
              return $scope.sortExpr;
            };
            this.isAsc = function () {
              return $scope.asc;
            };
            this.setSortExpr = function (exp) {
              $scope.asc = true;
              $scope.sortExpr = exp;
            };
            this.toggleSort = function () {
              $scope.asc = !$scope.asc;
            };
            this.doSort = function (comparatorFn) {
              if (comparatorFn) {
                $scope.rows.sort(function (r1, r2) {
                  var compared = comparatorFn(r1, r2);
                  return $scope.asc ? compared : compared * -1;
                });
              } else {
                $scope.rows.sort(function (r1, r2) {
                  var compared = defaultCompare(r1, r2);
                  return $scope.asc ? compared : compared * -1;
                });
              }
            };
            this.renderTalble = function () {
              return waitForRender().then(fixHeaderWidths);
            };
            this.getTableElement = function () {
              return $element;
            };
            /**
             * append handle function to execute after table header resize.
             */
            this.appendTableResizingHandler = function (handler) {
              var handlerSequence = $scope.headerResizeHanlers || [];
              for (var i = 0; i < handlerSequence.length; i++) {
                if (handlerSequence[i].name === handler.name) {
                  return;
                }
              }
              handlerSequence.push(handler);
              $scope.headerResizeHanlers = handlerSequence;
            };
            function defaultCompare(row1, row2) {
              var exprParts = $scope.sortExpr.match(/(.+)\s+as\s+(.+)/);
              var scope = {};
              scope[exprParts[1]] = row1;
              var x = $parse(exprParts[2])(scope);
              scope[exprParts[1]] = row2;
              var y = $parse(exprParts[2])(scope);
              if (x === y)
                return 0;
              return x > y ? 1 : -1;
            }
            function scrollToRow(row) {
              var offset = $element.find('.headerSpacer').height();
              var currentScrollTop = $element.find('.scrollArea').scrollTop();
              $element.find('.scrollArea').scrollTop(currentScrollTop + row.position().top - offset);
            }
            $scope.$on('rowSelected', function (event, rowId) {
              var row = $element.find('.scrollArea table tr[row-id=\'' + rowId + '\']');
              if (row.length === 1) {
                // Ensure that the headers have been fixed before scrolling, to ensure accurate
                // position calculations
                $q.all([
                  waitForRender(),
                  headersAreFixed.promise
                ]).then(function () {
                  scrollToRow(row);
                });
              }
            });
            // Set fixed widths for the table headers in case the text overflows.
            // There's no callback for when rendering is complete, so check the visibility of the table
            // periodically -- see http://stackoverflow.com/questions/11125078
            function waitForRender() {
              var deferredRender = $q.defer();
              function wait() {
                if ($element.find('table:visible').length === 0) {
                  $timeout(wait, 100);
                } else {
                  deferredRender.resolve();
                }
              }
              $timeout(wait);
              return deferredRender.promise;
            }
            var headersAreFixed = $q.defer();
            function fixHeaderWidths() {
              if (!$element.find('thead th .th-inner').length) {
                $element.find('thead th').wrapInner('<div class="th-inner"></div>');
              }
              if ($element.find('thead th .th-inner:not(:has(.box))').length) {
                $element.find('thead th .th-inner:not(:has(.box))').wrapInner('<div class="box"></div>');
              }
              $element.find('table th .th-inner:visible').each(function (index, el) {
                el = angular.element(el);
                var width = el.parent().width(), lastCol = $element.find('table th:visible:last'), headerWidth = width;
                if (lastCol.css('text-align') !== 'center') {
                  var hasScrollbar = $element.find('.scrollArea').height() < $element.find('table').height();
                  if (lastCol[0] == el.parent()[0] && hasScrollbar) {
                    headerWidth += $element.find('.scrollArea').width() - $element.find('tbody tr').width();
                    headerWidth = Math.max(headerWidth, width);
                  }
                }
                var minWidth = _getScale(el.parent().css('min-width')), title = el.parent().attr('title');
                headerWidth = Math.max(minWidth, headerWidth);
                el.css('width', headerWidth);
                if (!title) {
                  // ordinary column(not sortableHeader) has box child div element that contained title string.
                  title = el.find('.title .ng-scope').html() || el.find('.box').html();
                }
                el.attr('title', title.trim());
              });
              //------------------------------------------------	
              if (typeof $scope.changes == 'function') {
                $scope.changes();
              }
              //------------------------------------------------	
              headersAreFixed.resolve();
            }
            // when the data model changes, fix the header widths.  See the comments here:
            // http://docs.angularjs.org/api/ng.$timeout
            $scope.$watch('rows', function (newValue, oldValue) {
              if (newValue) {
                renderChains($element.find('.scrollArea').width());
                // clean sort status and scroll to top of table once records replaced.
                $scope.sortExpr = null;
                // FIXME what is the reason here must scroll to top? This may cause confusing if using scrolling to implement pagination.
                $element.find('.scrollArea').scrollTop(0);
              }
            });
            $scope.asc = !$attrs.hasOwnProperty('desc');
            $scope.sortAttr = $attrs.sortAttr;
            $element.find('.scrollArea').scroll(function (event) {
              $element.find('thead th .th-inner').css('margin-left', 0 - event.target.scrollLeft);
            });
            $scope.$on('renderScrollableTable', function () {
              renderChains($element.find('.scrollArea').width());
            });
            angular.element(window).on('resize', function () {
              $scope.$apply();
            });
            $scope.$watch(function () {
              return $element.find('.scrollArea').width();
            }, function (newWidth, oldWidth) {
              if (newWidth * oldWidth <= 0) {
                return;
              }
              renderChains();
            });
            function renderChains() {
              var resizeQueue = waitForRender().then(fixHeaderWidths), customHandlers = $scope.headerResizeHanlers || [];
              for (var i = 0; i < customHandlers.length; i++) {
                resizeQueue = resizeQueue.then(customHandlers[i]);
              }
              return resizeQueue;
            }
          }
        ]
      };
      function _getScale(sizeCss) {
        return parseInt(sizeCss.replace(/px|%/, ''), 10);
      }
    }
  ]).directive('timeFormat', function ($filter) {
    return {
      restrict: 'A',
      require: 'ngModel',
      scope: { showMeridian: '=' },
      link: function (scope, element, attrs, ngModel) {
        var parseTime = function (viewValue) {
          if (!viewValue) {
            ngModel.$setValidity('time', true);
            return null;
          } else if (angular.isDate(viewValue) && !isNaN(viewValue)) {
            ngModel.$setValidity('time', true);
            return viewValue;
          } else if (angular.isString(viewValue)) {
            var timeRegex = /^(0?[0-9]|1[0-2]):[0-5][0-9] ?[a|p]m$/i;
            if (!scope.showMeridian) {
              timeRegex = /^([01]?[0-9]|2[0-3]):[0-5][0-9]$/;
            }
            if (!timeRegex.test(viewValue)) {
              ngModel.$setValidity('time', false);
              return undefined;
            } else {
              ngModel.$setValidity('time', true);
              var date = new Date();
              var sp = viewValue.split(':');
              var apm = sp[1].match(/[a|p]m/i);
              if (apm) {
                sp[1] = sp[1].replace(/[a|p]m/i, '');
                if (apm[0].toLowerCase() == 'pm') {
                  sp[0] = sp[0] + 12;
                }
              }
              date.setHours(sp[0], sp[1]);
              return date;
            }
            ;
          } else {
            ngModel.$setValidity('time', false);
            return undefined;
          }
          ;
        };
        ngModel.$parsers.push(parseTime);
        var showTime = function (data) {
          parseTime(data);
          var timeFormat = !scope.showMeridian ? 'HH:mm' : 'hh:mm a';
          return $filter('date')(data, timeFormat);
        };
        ngModel.$formatters.push(showTime);
        scope.$watch('showMeridian', function (value) {
          var myTime = ngModel.$modelValue;
          if (myTime) {
            element.val(showTime(myTime));
          }
        });
      }
    };
  }).directive('timepickerPop', function ($document, timepickerState) {
    return {
      restrict: 'E',
      transclude: false,
      scope: {
        inputTime: '=',
        showMeridian: '=',
        disabled: '='
      },
      controller: function ($scope, $element) {
        $scope.isOpen = false;
        $scope.disabledInt = angular.isUndefined($scope.disabled) ? false : $scope.disabled;
        $scope.toggle = function () {
          if ($scope.isOpen) {
            $scope.close();
          } else {
            $scope.open();
          }
        };
      },
      link: function (scope, element, attrs) {
        var picker = {
            open: function () {
              timepickerState.closeAll();
              scope.isOpen = true;
            },
            close: function () {
              scope.isOpen = false;
            }
          };
        timepickerState.addPicker(picker);
        scope.open = picker.open;
        scope.close = picker.close;
        scope.$watch('disabled', function (value) {
          scope.disabledInt = angular.isUndefined(scope.disabled) ? false : scope.disabled;
        });
        scope.$watch('inputTime', function (value) {
          if (!scope.inputTime) {
            element.addClass('has-error');
          } else {
            element.removeClass('has-error');
          }
        });
        element.bind('click', function (event) {
          event.preventDefault();
          event.stopPropagation();
        });
        $document.bind('click', function (event) {
          scope.$apply(function () {
            scope.isOpen = false;
          });
        });
      },
      template: '<input type=\'text\' class=\'form-control\' ng-model=\'inputTime\' ng-disabled=\'disabledInt\' time-format show-meridian=\'showMeridian\' ng-focus=\'open()\' />' + '  <div class=\'input-group-btn\' ng-class=\'{open:isOpen}\'> ' + '    <button type=\'button\' ng-disabled=\'disabledInt\' class=\'btn btn-default \' ng-class="{\'btn-primary\':isOpen}" data-toggle=\'dropdown\' ng-click=\'toggle()\'> ' + '        <i class=\'glyphicon glyphicon-time\'></i></button> ' + '          <div class=\'dropdown-menu pull-right\'> ' + '            <timepicker ng-model=\'inputTime\' show-meridian=\'showMeridian\'></timepicker> ' + '           </div> ' + '  </div>'
    };
  });
  eduFieldDirectives.directive('capitalize', function () {
    return {
      require: 'ngModel',
      link: function (scope, element, attrs, modelCtrl) {
        if (attrs.capitalize == 'true') {
          var capitalize = function (inputValue) {
            if (inputValue == undefined)
              inputValue = '';
            var capitalized = inputValue.toUpperCase();
            if (capitalized !== inputValue) {
              // see where the cursor is before the update so that we can set it back
              var selection = element[0].selectionStart;
              modelCtrl.$setViewValue(capitalized);
              modelCtrl.$render();
              // set back the cursor after rendering
              element[0].selectionStart = selection;
              element[0].selectionEnd = selection;
            }
            return capitalized;
          };
          modelCtrl.$parsers.push(capitalize);
          capitalize(scope[attrs.ngModel]);  // capitalize initial value
        }
      }
    };
  });
  eduFieldDirectives.factory('dataFactoryField', [
    '$resource',
    function ($resource) {
      return function (uri, actions) {
        var defActions = {
            getAll: {
              method: 'GET',
              params: {},
              withCredentials: true,
              isArray: true
            },
            getCount: {
              method: 'GET',
              url: uri + '/count',
              params: {},
              withCredentials: true,
              isArray: false
            },
            get: {
              method: 'GET',
              params: {},
              withCredentials: true,
              isArray: false
            },
            insert: {
              method: 'POST',
              params: {},
              withCredentials: true,
              isArray: false
            },
            update: {
              method: 'PUT',
              params: {},
              withCredentials: true,
              isArray: false
            },
            remove: {
              method: 'DELETE',
              params: {},
              withCredentials: true,
              isArray: false
            }
          };
        if (typeof actions !== 'undefined' && actions !== '') {
          for (keyAction in actions) {
            for (keyDefAction in defActions) {
              if (keyAction == keyDefAction) {
                defActions[keyDefAction] = actions[keyAction];
              }
            }
          }
        }
        return $resource(uri, {}, defActions);
      };
    }
  ]);
  eduFieldDirectives.directive('eduFocus', [
    '$timeout',
    function ($timeout) {
      return {
        link: function (scope, element, attrs) {
          scope.$watch(attrs.eduFocus, function (val) {
            if (angular.isDefined(val) && val) {
              $timeout(function () {
                element[0].focus();
              });
            }
          }, true);
          element.bind('blur', function () {
            if (angular.isDefined(attrs.ngFocusLost)) {
              scope.$apply(attrs.ngFocusLost);
            }
          });
        }
      };
    }
  ]);
  eduFieldDirectives.directive('datepickerLocaldate', [
    '$parse',
    function ($parse) {
      var directive = {
          restrict: 'A',
          require: ['ngModel'],
          link: link
        };
      return directive;
      function link(scope, element, attr, ctrls) {
        var ngModelController = ctrls[0];
        // called with a JavaScript Date object when picked from the datepicker
        ngModelController.$parsers.push(function (viewValue) {
          // undo the timezone adjustment we did during the formatting----
          if (viewValue != null) {
            var minutes = viewValue.getMinutes();
            if (typeof minutes != 'undefined') {
              viewValue.setMinutes(minutes - viewValue.getTimezoneOffset());
            }
            // we just want a local date in ISO format
            return viewValue.toISOString().substring(0, 10);
          } else {
            return viewValue;
          }
        });
      }
    }
  ]);
  eduFieldDirectives.directive('validateIban', [
    '$compile',
    function ($compile) {
      return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, elm, attrs, ctrl) {
          function myValidation(value) {
            /*if (value.indexOf("e") > -1) {
  					  ctrl.$setValidity('charE', true);
  					} else {
  					  ctrl.$setValidity('charE', false);
  					}*/
            //---------------------------------------
            var prefId = attrs.id.substring(0, attrs.id.indexOf('-'));
            var pais = angular.element('input#' + prefId + '-pais');
            var entidad = angular.element('input#' + prefId + '-entidad');
            var oficina = angular.element('input#' + prefId + '-oficina');
            var control2 = angular.element('input#' + prefId + '-control2');
            var cuenta = angular.element('input#' + prefId + '-cuenta');
            var iban = pais.val() + entidad.val() + oficina.val() + control2.val() + cuenta.val();
            var valid = false;
            if (!pais.attr('class') && !entidad.attr('class') && !oficina.attr('class') && !control2.attr('class') && !cuenta.attr('class')) {
              valid = true;
            }
            if (attrs.name == pais.attr('name')) {
              if (pais.val().length == pais.attr('maxlength')) {
                valid = true;
                entidad.focus();
              }
            } else if (attrs.name == entidad.attr('name')) {
              if (entidad.val().length == entidad.attr('maxlength')) {
                valid = true;
                oficina.focus();
              }
            } else if (attrs.name == oficina.attr('name')) {
              if (oficina.val().length == oficina.attr('maxlength')) {
                valid = true;
                control2.focus();
              }
            } else if (attrs.name == control2.attr('name')) {
              if (control2.val().length == control2.attr('maxlength')) {
                valid = true;
                cuenta.focus();
              }
            } else if (attrs.name == cuenta.attr('name')) {
              //if(cuenta.val().length==cuenta.attr("maxlength")){
              valid = IBAN.isValid(iban);
              ctrl.$setValidity('validateIban', valid);
              return iban;  //}
            }
            return value;
          }
          ctrl.$parsers.push(myValidation);
        }
      };
    }
  ]);
  eduFieldDirectives.directive('dynamicName', [
    '$compile',
    function ($compile) {
      return {
        restrict: 'A',
        terminal: true,
        priority: 1000,
        link: function (scope, element, attrs) {
          element.attr('name', scope.$eval(attrs.dynamicName));
          element.removeAttr('dynamic-name');
          $compile(element)(scope);
        }
      };
    }
  ]);
  eduFieldDirectives.directive('dateInput', [
    'dateFilter',
    function (dateFilter) {
      return {
        require: 'ngModel',
        template: '<input type="date" ></input>',
        replace: true,
        link: function (scope, elm, attrs, ngModelCtrl) {
          ngModelCtrl.$formatters.unshift(function (modelValue) {
            return dateFilter(modelValue, 'yyyy-MM-dd');
          });
          ngModelCtrl.$parsers.unshift(function (viewValue) {
            return new Date(viewValue);
          });
        }
      };
    }
  ]);
  eduFieldDirectives.directive('replaceBlank', function () {
    return {
      require: 'ngModel',
      link: function (scope, elm, attrs, ngModelCtrl) {
        ngModelCtrl.$formatters.unshift(function (modelValue) {
          if (typeof modelValue != 'undefined') {
            return modelValue.replace(new RegExp(' ', 'g'), 'aeiou');
          } else {
            return modelValue;
          }
        });
        ngModelCtrl.$parsers.unshift(function (viewValue) {
          if (typeof viewValue != 'undefined') {
            return viewValue.replace(new RegExp('aeiou', 'g'), ' ');
          } else {
            return viewValue;
          }
        });
      }
    };
  });
  eduFieldDirectives.directive('dateTimeInput', [
    'dateFilter',
    function (dateFilter) {
      return {
        require: 'ngModel',
        template: '<input type="datetime-local"></input>',
        replace: true,
        link: function (scope, elm, attrs, ngModelCtrl) {
          ngModelCtrl.$formatters.unshift(function (modelValue) {
            return dateFilter(modelValue, 'yyyy-MM-ddTHH:mm');
          });
          ngModelCtrl.$parsers.unshift(function (viewValue) {
            return new Date(viewValue);
          });
        }
      };
    }
  ]);
  eduFieldDirectives.filter('toEuros', function () {
    return function (input, fractionDigit) {
      var fractD = fractionDigit ? fractionDigit : 2;
      var amount = Number(input).toLocaleString('es-ES', { minimumFractionDigits: fractD }) + ' \u20ac';
      if (amount == '0,00 \u20ac' || amount == 'NaN \u20ac') {
        //if(amount=='NaN €'){
        return;
      } else {
        console.log('toEuros2:' + amount);
        return amount;
      }
    };
  });
  eduFieldDirectives.directive('currency', [
    '$filter',
    function ($filter) {
      return {
        require: '?ngModel',
        link: function (scope, elem, attrs, ctrl) {
          if (!ctrl)
            return;
          ctrl.$formatters.unshift(function (a) {
            if (ctrl.$modelValue) {
              return function () {
                return $filter('toEuros')(ctrl.$modelValue.toString().replace(',', '.'));
              };
            } else {
              return $filter('toEuros')('');
            }
          });
          elem.bind('keydown', function (event) {
            if (event.which == 188 || event.keyCode == 188) {
              return false;
            }
          });
          elem.bind('blur', function (event) {
            var arrPartsNumber;
            // La primera vez que se introduce un valor en el input no puede contener coma porque el evento 'keydown' no lo permite.
            // si lleva coma es porque ya ha sido procesada la entrada por el input currency y entonces puede llevar también puntos de miles.
            // Por eso quitamos todos los puntos y el simbolo del euro
            if (elem.val().indexOf(',') >= 0) {
              var pureNumber = elem.val().replace('\u20ac', '').replace(/\./g, '');
              var arrPartsNumber = pureNumber.split(',');
            }  // si no lleva coma es porque la entrada es nueva  y no ha sido procesado por el input currency.
               // Puede llevar puntos
            else {
              var pureNumber = elem.val().replace('\u20ac', '');
              var arrPartsNumber = pureNumber.split('.');
            }
            elem.val($filter('toEuros')(arrPartsNumber.join('.')));  //var partEntera='';
                                                                     //var partDecimal='';
                                                                     //if(arrPartsNumber[0] && arrPartsNumber[1]){
                                                                     //partEntera=arrPartsNumber[0];
                                                                     //elem.val($filter('toEuros')(partEntera+'.'+partDecimal));
                                                                     //}
                                                                     //if(arrPartsNumber[1]){
                                                                     //partDecimal=arrPartsNumber[1];
                                                                     //}
                                                                     // var plainNumber = elem.val().replace(/[^\d|\-+|\.+]/g, '').replace('€','');
                                                                     //var plainPartEntera=partEntera.replace(/[^\d|\-+|\.+]/g, '');
                                                                     //var plainPartDecimal=partDecimal.replace(/[^\d|\-+|\.+]/g, '');
                                                                     //elem.val($filter('toEuros')(partEntera+'.'+partDecimal));
          });
        }
      };
    }
  ]);
  eduFieldDirectives.directive('eduField', [
    '$http',
    '$compile',
    '$templateCache',
    '$timeout',
    function formField($http, $compile, $templateCache, $timeout) {
      var getTemplateUrl = function (type) {
        var templateUrl = '';
        switch (type) {
        case 'tritoggle':
          templateUrl = 'directives/edu-field-tritoggle-tpl.html';
          break;
        case 'image':
          templateUrl = 'directives/edu-field-image-tpl.html';
          break;
        case 'textbutton':
          templateUrl = 'directives/edu-field-textbutton-tpl.html';
          break;
        case 'button':
          templateUrl = 'directives/edu-field-button-tpl.html';
          break;
        case 'currency':
          templateUrl = 'directives/edu-field-currency-tpl.html';
          break;
        case 'hidden':
          templateUrl = 'directives/edu-field-hidden-tpl.html';
          break;
        case 'literal':
          templateUrl = 'directives/edu-field-literal-tpl.html';
          break;
        case 'upload':
          templateUrl = 'directives/edu-field-upload-tpl.html';
          break;
        case 'upload15x':
          templateUrl = 'directives/edu-field-upload15x-tpl.html';
          break;
        case 'nifniecif':
          templateUrl = 'directives/edu-field-nifniecif-tpl.html';
          break;
        case 'grid':
          templateUrl = 'directives/edu-field-grid-tpl.html';
          break;
        case 'iban':
          templateUrl = 'directives/edu-field-iban-tpl.html';
          break;
        case 'iban2':
          templateUrl = 'directives/edu-field-iban2-tpl.html';
          break;
        case 'autocomplete':
          templateUrl = 'directives/edu-field-autocomplete-tpl.html';
          break;
        case 'range':
          templateUrl = 'directives/edu-field-range-tpl.html';
          break;
        case 'textedit':
          templateUrl = 'directives/edu-field-textedit-tpl.html';
          break;
        case 'url':
          templateUrl = 'directives/edu-field-url-tpl.html';
          break;
        case 'time':
          templateUrl = 'directives/edu-field-time-tpl.html';
          break;
        case 'week':
          templateUrl = 'directives/edu-field-week-tpl.html';
          break;
        case 'month':
          templateUrl = 'directives/edu-field-month-tpl.html';
          break;
        case 'date<13':
          templateUrl = 'directives/edu-field-date-tpl.html';
          break;
        case 'date':
          templateUrl = 'directives/edu-field-date-ag-ui-tpl.html';
          break;
        case 'date-time':
          templateUrl = 'directives/edu-field-date-time-tpl.html';
          break;
        case 'textarea':
          templateUrl = 'directives/edu-field-textarea-tpl.html';
          break;
        case 'radio':
          templateUrl = 'directives/edu-field-radio-tpl.html';
          break;
        case 'select':
          templateUrl = 'directives/edu-field-select-tpl.html';
          break;
        case 'number':
          templateUrl = 'directives/edu-field-number-tpl.html';
          break;
        case 'checkbox':
          templateUrl = 'directives/edu-field-checkbox-tpl.html';
          break;
        case 'password':
          templateUrl = 'directives/edu-field-password-tpl.html';
          break;
        case 'hidden':
          templateUrl = 'directives/edu-field-hidden-tpl.html';
          break;
        case 'email':
          templateUrl = 'directives/edu-field-email-tpl.html';
          break;
        case 'text':
          templateUrl = 'directives/edu-field-text-tpl.html';
          break;
        default:
          templateUrl = null;
          break;
        }
        return templateUrl;
      };
      var getStringPattern = function (type) {
        var stringPattern = '';
        switch (type) {
        case 'image':
        case 'textbutton':
        case 'button':
          stringPattern = '';
          break;
        case 'currency':
          stringPattern = '';
          break;
        case 'hidden':
          stringPattern = '';
          break;
        case 'literal':
          stringPattern = '';
          break;
        case 'upload':
          stringPattern = '';
          break;
        case 'upload15x':
          stringPattern = '';
          break;
        case 'nifniecif':
          stringPattern = '';
          break;
        case 'grid':
          stringPattern = '';
          break;
        case 'iban':
          stringPattern = '';
          break;
        case 'autocomplete':
          stringPattern = '';
          break;
        case 'range':
          stringPattern = '';
          break;
        case 'textedit':
          stringPattern = '';
          break;
        case 'url':
          stringPattern = '';
          break;
        case 'time':
          stringPattern = '';
          break;
        case 'week':
          stringPattern = '';
          break;
        case 'month':
          stringPattern = '^(19|20)dd[- /](0[1-9]|1[012])$';
          break;
        case 'date<13':
          stringPattern = '';
          break;
        case 'date':
          stringPattern = '';
          break;
        case 'date-time':
          stringPattern = '';
          break;
        case 'textarea':
          stringPattern = '';
          break;
        case 'radio':
          stringPattern = '';
          break;
        case 'select':
          stringPattern = '';
          break;
        case 'number':
          stringPattern = '^-?[0-9]+$';
          break;
        case 'checkbox':
          stringPattern = '';
          break;
        case 'password':
          stringPattern = '';
          break;
        case 'hidden':
          stringPattern = '';
          break;
        case 'email':
          stringPattern = '^[a-z0-9!#$%&\'*+/=?^_`{|}~-]+(?:.[a-z0-9!#$%&\'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?.)+(?:[A-Z]{2}|es|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|asia|jobs|museum)\b$';
          break;
        case 'text':
          stringPattern = '';
          break;
        default:
          stringPattern = '';
          break;
        }
        return stringPattern;
      };
      return {
        restrict: 'AE',
        transclude: true,
        scope: {
          options: '=options',
          value: '=value'
        },
        link: function fieldLink($scope, $element, $attr) {
          if (!$scope.hasOwnProperty('options')) {
            throw new Error('options are required!');
          }
          // load the correct template
          var templateUrl = $scope.options.templateUrl || getTemplateUrl($scope.options.type);
          if (templateUrl) {
            $http.get(templateUrl, { cache: $templateCache }).success(function (data) {
              $element.html(data);
              $compile($element.contents())($scope);
            });
          } else {
            console.log('eduField Error: plantilla tipo \'' + $scope.options.type + '\' no soportada.');
          }
          // validate field
          $scope.$dirty = false;
          $scope.$invalid = false;
          $scope.$invalidRequired = false;
          $scope.$invalidPattern = false;
          $scope.$invalidMinlength = false;
          $scope.$invalidMaxlength = false;
          $scope.$invalidMin = false;
          $scope.$invalidMax = false;
          if ($scope.options.type == 'grid') {
            //height scrollable-table
            $timeout(function () {
              $('#input-grid-' + $scope.options.key + ' .scrollableContainer').css('height', $scope.options.height + 'px');
            });
          }
          // ---
          // CALLBACKS
          // ---
          $scope.onClick = function () {
            if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onClick == 'function') {
              $scope.options.fieldListeners.onClick($scope.value);
            }
          };
          $scope.onChangeSearchString = function (text) {
            console.log('onChangeSearchString:' + text);
            if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onChangeSearchString == 'function') {
              $scope.options.fieldListeners.onChangeSearchString(text);
            }
          };
          $scope.onChange = function (subitem, field, column, value) {
            if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onChange == 'function') {
              var item = {};
              var value = '';
              if ($scope.options.type == 'select') {
                for (var i = 0; i < $scope.optionsSelect.length; i++) {
                  if ($scope.optionsSelect[i][$scope.options.optionvalue] == $scope.value) {
                    item = $scope.optionsSelect[i];
                    value = $scope.value;
                    break;
                  }
                }
              } else if ($scope.options.type == 'iban2') {
                value = $scope.value[subitem];
                item = subitem;
              } else if ($scope.options.type == 'autocomplete') {
                value = $scope.value;
                item = subitem;
              } else {
                value = $scope.value;
              }
              $scope.options.fieldListeners.onChange(value, item, $scope.options.showPopupCalendar);
            }
            if ($scope.options.type == 'grid') {
              if (field.type == 'select') {
                subitem.$optionSelectedName[column] = _.find(field.selectOptions, { 'value': value }).name;
              }
              if (field.hasOwnProperty('fieldListeners') && typeof field.fieldListeners.onChange == 'function') {
                field.fieldListeners.onChange(value, subitem);
              }
            }
          };
          $scope.onKeypress = function ($event) {
            if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onKeypress == 'function') {
              $scope.options.fieldListeners.onKeypress($event);
            }
          };
          $scope.onInit = function () {
            var callInit = function () {
              $scope.options.fieldListeners.onInit($scope.value);
              $scope.$apply();
            };
            if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onInit == 'function') {
              $timeout(callInit, 2000);
            }
          };
          $scope.onFocus = function (subitem) {
            var item = '';
            var value = '';
            if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onFocus == 'function') {
              if ($scope.options.type == 'iban') {
                value = $scope.value ? $scope.value[subitem] : '';
                item = subitem;
              } else {
                value = $scope.value;
              }
              $scope.options.fieldListeners.onFocus(value, item);
            }
          };
          $scope.onBlur = function (subitem) {
            var item = '';
            var value = '';
            var elementClass = $element.find('[id^=\'' + $scope.id + '\']').attr('class');
            $element.find('[id^=\'' + $scope.id + '\']').attr('blur', true);
            if (typeof elementClass !== 'undefined') {
              var aClass = elementClass.split(' ');
              for (var i = 0; i < aClass.length; i++) {
                if (aClass[i] === 'ng-dirty') {
                  $scope.$dirty = true;
                } else if (aClass[i] == 'ng-invalid') {
                  $scope.$invalid = true;
                } else if (aClass[i] == 'ng-invalid-required') {
                  $scope.$invalidRequired = true;
                } else if (aClass[i] == 'ng-invalid-pattern') {
                  $scope.$invalidPattern = true;
                } else if (aClass[i] == 'ng-invalid-minlength') {
                  $scope.$invalidMinlength = true;
                } else if (aClass[i] == 'ng-invalid-maxlength') {
                  $scope.$invalidMaxlength = true;
                } else if (aClass[i] == 'ng-invalid-min') {
                  $scope.$invalidMin = true;
                } else if (aClass[i] == 'ng-invalid-max') {
                  $scope.$invalidMax = true;
                } else if (aClass[i] == 'ng-valid') {
                  $scope.$invalid = false;
                } else if (aClass[i] == 'ng-valid-required') {
                  $scope.$invalidRequired = false;
                } else if (aClass[i] == 'ng-valid-pattern') {
                  $scope.$invalidPattern = false;
                } else if (aClass[i] == 'ng-valid-minlength') {
                  $scope.$invalidMinlength = false;
                } else if (aClass[i] == 'ng-valid-maxlength') {
                  $scope.$invalidMaxlength = false;
                }
              }
            }
            if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onBlur == 'function') {
              if ($scope.options.type == 'iban') {
                value = $scope.value ? $scope.value[subitem] : '';
                item = subitem;
              } else {
                value = $scope.value ? $scope.value : '';
              }
              $scope.options.fieldListeners.onBlur(value, $scope.options.showPopupCalendar);
            }
          };
        },
        controller: [
          '$scope',
          '$http',
          'Upload',
          'FileUploader',
          'dataFactoryField',
          function fieldController($scope, $http, Upload, FileUploader, dataFactoryField) {
            // component control
            $scope.options.fieldControl = {};
            $scope.internalControl = $scope.options.fieldControl || {};
            // ---
            // METHODS
            // ---  
            $scope.internalControl.upload = function (idxFile) {
              console.log('llamada a file upload file:' + idxFile);
              if ($scope.options.type == 'upload') {
                $scope.uploader.queue[idxFile - 1].upload();
              }
              if ($scope.options.type == 'upload15x') {
                $scope.ngfupload();
              }
            };
            $scope.internalControl.filesInQueue = function () {
              if ($scope.options.type == 'upload') {
                return $scope.uploader.queue.length;
              } else {
                return 0;
              }
            };
            $scope.internalControl.clearQueue = function () {
              if ($scope.options.type == 'upload') {
                return $scope.uploader.clearQueue();
              } else {
                return 0;
              }
            };
            $scope.internalControl.refresh = function (value) {
              if ($scope.options.type == 'select') {
                $scope.refreshSelect(value);
              }
              if ($scope.options.type == 'grid') {
                if (value) {
                  $scope.options.valueFk = value;
                }
                $scope.refreshGrid();
              }
            };
            $scope.internalControl.clean = function (value) {
              if ($scope.options.type == 'select') {
                $scope.optionsSelect = [];
              }
            };
            if (!$scope.options.hasOwnProperty('loadOnInit') && $scope.options.type == 'select') {
              $scope.options.loadOnInit = true;
            }
            //apply pattern to types. For fix change to 1.5.x
            $scope.pattern_validator = function () {
              var regexp = '';
              if ($scope.options.hasOwnProperty('pattern')) {
                regexp = $scope.options.pattern;
                if (regexp.substr(0, 1) == '/') {
                  regexp = regexp.substr(1);
                }
                if (regexp.substr(regexp.length - 1, 1) == '/') {
                  regexp = regexp.substr(0, regexp.length - 1);
                }
              } else {
                regexp = getStringPattern($scope.options.type);
              }
              return regexp;
            }();
            $scope.onChange = function (subitem) {
              if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onChange == 'function') {
                var item = {};
                var value = '';
                if ($scope.options.type == 'select') {
                  for (var i = 0; i < $scope.optionsSelect.length; i++) {
                    if ($scope.optionsSelect[i][$scope.options.optionvalue] == $scope.value) {
                      item = $scope.optionsSelect[i];
                      value = $scope.value;
                      break;
                    }
                  }
                } else if ($scope.options.type == 'iban2') {
                  value = $scope.value[subitem];
                  item = subitem;
                } else if ($scope.options.type == 'autocomplete') {
                  value = $scope.value;
                  item = subitem;
                  console.log('onChange()- edu-field.js -- subitem:' + subitem);
                }
                $scope.options.fieldListeners.onChange(value, item);
              }
            };
            // ----------------------------------------------------------//
            // CONTROL TYPE= checkbox
            // ----------------------------------------------------------//
            if ($scope.options.type == 'checkbox') {
              if (!$scope.options.false_value) {
                $scope.options.false_value = '\'N\'';
              }
              if (!$scope.options.true_value) {
                $scope.options.true_value = '\'S\'';
              }
            }
            //-----------------------------------------------------------//
            // CONTROL TYPE= grid
            //-----------------------------------------------------------//
            if ($scope.options.type == 'grid') {
              //si se define la opción width, ajusta el ancho de la tabla a él
              $scope.widthTableGrid = { 'width': $scope.options.width ? $scope.options.width + 'px' : '100%' };
              // por defecto, en modo listado 
              $scope.options.state = 'list';
              function configField() {
                $scope.field = null;
                //Añade a cada row el objeto que va a guardar las descripciones correspondientes a los values para los input tipo select
                for (var i = 0, row; row = $scope.gridRows[i]; i++) {
                  row.$optionSelectedName = {};
                }
                ;
                for (var k = 0, field; field = $scope.options.listFields[k]; k++) {
                  $scope.field = field;
                  //for transform content input before render
                  for (var property in field) {
                    if (typeof field.renderer !== 'function') {
                      field.orderByValue = field.column;
                      field.renderer = function (input, row, column, type) {
                        return input;
                      };
                    }
                  }
                  // if item in listField is checkbox, by default, false and true value are 'N' and 'S'
                  if (field.type == 'checkbox') {
                    if (!field.false_value) {
                      field.false_value = '\'N\'';
                    }
                    if (!field.true_value) {
                      field.true_value = '\'S\'';
                    }
                  }
                  if (field.type == 'select') {
                    if (field.selecttypesource == 'url') {
                      if (field.hasOwnProperty('selectsource') && field.selectsource != '') {
                        //****************************************************************************
                        $http.get(field.selectsource).success(function (data, status, headers, config) {
                          $scope.field.selectOptions = data;
                          //for each option, adjust properties value an name
                          for (var i = 0, option; option = $scope.field.selectOptions[i]; i++) {
                            if (!option.hasOwnProperty('value')) {
                              option.value = option[$scope.field.optionvalue];
                            }
                            if (!option.hasOwnProperty('name')) {
                              if ($scope.field.selectconcatvaluename) {
                                option.name = option[$scope.field.optionvalue] + ' - ' + option[$scope.field.optionname];
                              } else {
                                option.name = option[$scope.field.optionname];
                              }
                            } else {
                              if ($scope.field.selectconcatvaluename) {
                                option.name = option['value'] + ' - ' + option['name'];
                              } else {
                                option.name = option[$scope.field.optionname];
                              }
                            }
                          }
                        }).error(function (data, status, headers, config) {
                          console.log('Error getting options select:' + data);
                        });  //****************************************************************************
                      }
                    }
                    //guarda la descripción de la opción del select correspondiente con el código en select.option.value
                    //para poder mostarla cuando la fila esté en edición
                    if ($scope.gridRows) {
                      for (var i = 0, row; row = $scope.gridRows[i]; i++) {
                        for (var key in row) {
                          if (key == $scope.field.column) {
                            row.$optionSelectedName[key] = _.find($scope.field.selectOptions, { 'value': row[key] }).name;
                          }
                        }
                      }
                    }
                  }
                  if (field.type == 'autocomplete') {
                    if (field.hasOwnProperty('autocurldata') && field.autocurldata != '') {
                      //****************************************************************************
                      $http.get(field.autocurldata).success(function (data, status, headers, config) {
                        $scope.field.selectOptions = data;
                        //for each option, adjust properties value an name
                        for (var i = 0, option; option = $scope.field.selectOptions[i]; i++) {
                          if (!option.hasOwnProperty('value')) {
                            option.value = option[$scope.field.optionvalue];
                          }
                          if (!option.hasOwnProperty('name')) {
                            if ($scope.field.selectconcatvaluename) {
                              option.name = option[$scope.field.optionvalue] + ' - ' + option[$scope.field.optionname];
                            } else {
                              option.name = option[$scope.field.optionname];
                            }
                          } else {
                            if ($scope.field.selectconcatvaluename) {
                              option.name = option['value'] + ' - ' + option['name'];
                            } else {
                              option.name = option[$scope.field.optionname];
                            }
                          }
                        }
                        //guarda la descripción de la opción del select correspondiente con el código en select.option.value
                        //para poder mostarla cuando la fila esté en edición
                        if ($scope.gridRows) {
                          for (var i = 0, row; row = $scope.gridRows[i]; i++) {
                            for (var key in row) {
                              if (key == $scope.field.column) {
                                row.$optionSelectedName[key] = _.find($scope.field.selectOptions, { 'value': row[key] }).name;
                              }
                            }
                          }
                        }
                      }).error(function (data, status, headers, config) {
                        console.log('Error getting options select:' + data);
                      });  //****************************************************************************
                    }
                  }
                }
              }
              var apiField = null;
              if (typeof $scope.options.uri !== 'undefined' && $scope.options.uri !== '') {
                apiField = dataFactoryField($scope.options.uri, typeof $scope.options.actions !== 'undefined' ? $scope.options.actions : '');
              }
              ;
              $scope.refreshGrid = function () {
                //get all element with foreing key like  fieldFk
                $scope.options.inputGridLoading = true;
                var filterFK = '';
                var oParamGrid = {};
                if ($scope.options.valueFk != '') {
                  if ($scope.options.hasOwnProperty('fieldFk') && typeof $scope.options.fieldFk != undefined && $scope.options.hasOwnProperty('valueFk') && typeof $scope.options.valueFk != undefined) {
                    filterFK = '[' + $scope.options.fieldFk + ']=' + $scope.options.valueFk;
                    oParamGrid.filter = filterFK;
                  }
                  apiField.getAll(oParamGrid, function (data) {
                    $scope.options.inputGridLoading = false;
                    $scope.gridRows = data;
                    $scope.options.gridRows = $scope.gridRows;
                    configField();
                  }, function (data) {
                    $scope.options.inputGridLoading = false;  //$scope.internalControl.showOverlayFormSuccessError('0',data.data || data.message,20005);
                  });
                }
              };
              // get one element for edit
              $scope.gridEdit = function (item) {
                var dataCopy = {};
                angular.copy(item, dataCopy);
                item.$dataCopy = dataCopy;
                item.$visible = true;
                item.$inserted = false;
                if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onChangeState == 'function') {
                  $scope.options.fieldListeners.onChangeState($scope.options.state, 'edit');
                  $scope.options.state = 'edit';
                } else {
                  $scope.options.state = 'edit';
                }
              };
              // button grid cancel
              $scope.gridCancel = function (item) {
                angular.extend(item, item.$dataCopy);
                item.$visible = false;
                item.$inserted = false;
                if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onChangeState == 'function') {
                  $scope.options.fieldListeners.onChangeState($scope.options.state, 'list');
                  $scope.options.state = 'list';
                } else {
                  $scope.options.state = 'list';
                }
              };
              // button grid delete
              $scope.gridDelete = function (item, index) {
                //if state is equal to new, delete register only of local data
                if ($scope.options.state == 'new') {
                  $scope.gridRows.splice(index, 1);
                } else {
                  $scope.options.showOverlayInputGridFormDelete = true;
                  $scope.itemForDelete = {
                    item: item,
                    index: index
                  };
                }
                if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onChangeState == 'function') {
                  $scope.options.fieldListeners.onChangeState($scope.options.state, 'list');
                  $scope.options.state = 'list';
                } else {
                  $scope.options.state = 'list';
                }
              };
              // button grid add new
              $scope.gridNew = function () {
                var newItem = {};
                for (var i = 0; i < $scope.options.listFields.length; i++) {
                  newItem[$scope.options.listFields[i].column] = '';
                }
                $scope.gridRows.unshift(newItem);
                newItem.$visible = true;
                newItem.$inserted = true;
                if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onChangeState == 'function') {
                  $scope.options.fieldListeners.onChangeState($scope.options.state, 'new');
                  $scope.options.state = 'new';
                } else {
                  $scope.options.state = 'new';
                }
              };
              //Overlay for notifications
              $scope.options.overlayFormSuccessErrorInputGrid = {
                show: false,
                type: 'danger',
                message: ''
              };
              // button grid save
              $scope.gridSave = function (item) {
                console.log('gridLocalSave: ' + angular.toJson(item));
                var dataTemp = {};
                angular.copy(item, dataTemp);
                delete dataTemp.$optionSelectedName;
                delete dataTemp.$dataCopy;
                delete dataTemp.$visible;
                delete dataTemp.$inserted;
                if (item.$inserted) {
                  dataTemp[$scope.options.fieldFk] = $scope.options.valueFk;
                  if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onBeforeSave == 'function') {
                    dataTemp = $scope.options.fieldListeners.onBeforeSave(dataTemp, true);
                  }
                  apiField.insert(dataTemp, function (data) {
                    item.$visible = false;
                    if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onAfterSave == 'function') {
                      $scope.options.fieldListeners.onAfterSave(dataTemp, true);
                    }
                    if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onSaveSuccess == 'function') {
                      $scope.options.fieldListeners.onSaveSuccess(data);
                    }
                    if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onChangeState == 'function') {
                      $scope.options.fieldListeners.onChangeState($scope.options.state, 'list');
                      $scope.options.state = 'list';
                    } else {
                      $scope.options.state = 'list';
                    }
                    $scope.refreshGrid();
                  }, function (data) {
                    $scope.options.overlayFormSuccessErrorInputGrid.message = 'No ha sido posible insertar el registro: ' + data.data;
                    $scope.options.overlayFormSuccessErrorInputGrid.show = true;
                  });
                } else {
                  var oId = getOid(dataTemp);
                  if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onBeforeSave == 'function') {
                    $scope.options.fieldListeners.onBeforeSave(dataTemp, false);
                  }
                  apiField.update(oId, dataTemp, function (data) {
                    item.$visible = false;
                    if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onAfterSave == 'function') {
                      $scope.options.fieldListeners.onAfterSave(dataTemp, false);
                    }
                    if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onUpdateSuccess == 'function') {
                      $scope.options.fieldListeners.onUpdateSuccess(data);
                    }
                    if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onChangeState == 'function') {
                      $scope.options.fieldListeners.onChangeState($scope.options.state, 'list');
                      $scope.options.state = 'list';
                    } else {
                      $scope.options.state = 'list';
                    }  //updateElementInArray($scope.gridRows,dataTemp);
                  }, function (data) {
                    $scope.options.overlayFormSuccessErrorInputGrid.message = 'No ha sido posible realizar la modificaci\xf3n: ' + data.data;
                    $scope.options.overlayFormSuccessErrorInputGrid.show = true;
                  });
                }
              };
              $scope.inputGridFormDeleteContinue = function () {
                var oId = {};
                oId.id = $scope.itemForDelete.item[$scope.options.fieldKey];
                if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onBeforeDelete == 'function') {
                  $scope.options.fieldListeners.onBeforeDelete(oId.id);
                } else {
                  apiField.delete(oId, function (data) {
                    $scope.gridRows.splice($scope.itemForDelete.index, 1);
                  }, function (data) {
                  });
                }
                if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onAfterDelete == 'function') {
                  $scope.options.fieldListeners.onAfterDelete(oId.id);
                }
                $scope.options.showOverlayInputGridFormDelete = false;
              };
              $scope.inputGridFormDeleteCancel = function () {
                $scope.options.showOverlayInputGridFormDelete = false;
              };
              function getOid(row) {
                var vid = row[$scope.options.fieldKey];
                var oId = {};
                oId['id'] = vid;
                return oId;
              }
              function updateElementInArray(rows, item) {
                for (var i = 0; i < rows.length; i++) {
                  var vid = rows[i][$scope.options.fieldKey];
                  if (vid == item[$scope.options.fieldKey]) {
                    $scope.gridRows.splice(i, 1, item);
                    break;
                  }
                }
              }
            }
            //Especific validator
            // ----------------------------------------------------------//
            // CONTROL TYPE= date
            // ----------------------------------------------------------//
            if ($scope.options.type == 'date') {
              $scope.options.dateOptions = {
                'startingDay': 1,
                'showWeeks': false
              };
              $scope.internalControl.showCalendar = function ($event) {
                $event.preventDefault();
                $event.stopPropagation();
                $scope.options.showPopupCalendar = true;
              };
              if (typeof $scope.options.minDate) {
                $scope.options.minDateObj = new Date($scope.options.minDate);
              }
              if (typeof $scope.options.maxDate) {
                $scope.options.maxDateObj = new Date($scope.options.maxDate);
              }
            }
            // ----------------------------------------------------------//
            // CONTROL TYPE= iban
            // ----------------------------------------------------------//
            $scope.ibanValidator = function () {
              return {
                test: function (value) {
                  return IBAN.isValid(value);
                }
              };
            }();
            // ----------------------------------------------------------//
            // CONTROL TYPE= nif nie cif
            // ----------------------------------------------------------//
            $scope.nifniecifValidator = function () {
              return {
                test: function (value) {
                  var sValid = valida_nif_cif_nie(value);
                  if (sValid > 0)
                    return true;
                  else
                    return false;
                }
              };
            }();
            // ---
            // CONTROL TYPE= ss
            // ---
            $scope.ssValidator = function () {
              return {
                test: function (value) {
                  var sValid = ss(value);
                  if (sValid >= 0)
                    return true;
                  else
                    return false;
                }
              };
            }();
            // ---
            // CONTROL TYPE= uploader15x -- plugin ng-file-upload
            // ---
            $scope.ngfselect = function (file) {
              if (file) {
                $scope.value = file.$ngfName || file.name;
                if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onAfterAddingFile == 'function') {
                  $scope.options.fieldListeners.onAfterAddingFile(file);
                }
              }
            };
            $scope.uploading = false;
            $scope.ngfupload = function () {
              $scope.uploading = true;
              if ($scope.file) {
                Upload.upload({
                  url: $scope.options.url,
                  data: { file: $scope.file }
                }).then(function (resp) {
                  $scope.uploading = false;
                  if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onSuccessItem == 'function') {
                    $scope.options.fieldListeners.onSuccessItem(resp.config.data.file, resp);
                  }
                }, function (resp) {
                  $scope.uploading = false;
                  if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onErrorItem == 'function') {
                    $scope.options.fieldListeners.onErrorItem(resp.status);
                  }
                }, function (evt) {
                  $scope.progressPercentage = parseInt(100 * evt.loaded / evt.total);
                  if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onProgressItem == 'function') {
                    $scope.options.fieldListeners.onProgressItem($scope.progressPercentage, evt.config.data.file.name);
                  }
                });
              }
            };
            // ---
            // CONTROL TYPE= grid
            // ---
            if ($scope.options.type == 'grid') {
              if ($scope.options.selecttypesource == 'url' && (typeof $scope.options.autoload == 'undefined' || $scope.options.autoload == true)) {
                var sUrl = $scope.options.selectsource;
                if ($scope.options.loadOnInit) {
                  $http.get(sUrl).success(function (data, status, headers, config) {
                    $scope.optionsSelect = data;
                  });
                }
              }
            }
            // ---
            // CONTROL TYPE= uploader
            // ---
            // create a uploader with options
            var uploader = $scope.uploader = new FileUploader({ url: $scope.options.url });
            // FILTERS
            uploader.filters.push({
              name: 'customFilter',
              fn: function (item, options) {
                return this.queue.length < 10;
              }
            });
            // CALLBACKS
            uploader.onWhenAddingFileFailed = function (item, filter, options) {
              if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onWhenAddingFileFailed == 'function') {
                $scope.options.fieldListeners.onWhenAddingFileFailed(item, filter, options);
              }
            };
            uploader.onAfterAddingFile = function (fileItem) {
              $scope.value = uploader.queue[0]._file.name;
              if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onAfterAddingFile == 'function') {
                $scope.options.fieldListeners.onAfterAddingFile(fileItem);
              }
            };
            uploader.onAfterAddingAll = function (addedFileItems) {
              if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onAfterAddingAll == 'function') {
                $scope.options.fieldListeners.onAfterAddingAll(addedFileItems);
              }
            };
            uploader.onBeforeUploadItem = function (item) {
              if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onBeforeUploadItem == 'function') {
                $scope.options.fieldListeners.onBeforeUploadItem(item);
              }
            };
            uploader.onProgressItem = function (fileItem, progress) {
              if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onProgressItem == 'function') {
                $scope.options.fieldListeners.onProgressItem(fileItem, progress);
              }
            };
            uploader.onProgressAll = function (progress) {
              if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onProgressAll == 'function') {
                $scope.options.fieldListeners.onProgressAll(progress);
              }
            };
            uploader.onSuccessItem = function (fileItem, response, status, headers) {
              if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onSuccessItem == 'function') {
                $scope.options.fieldListeners.onSuccessItem(fileItem, response, status, headers);
              }
            };
            uploader.onErrorItem = function (fileItem, response, status, headers) {
              if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onErrorItem == 'function') {
                $scope.options.fieldListeners.onErrorItem(fileItem, response, status, headers);
              }
            };
            uploader.onCancelItem = function (fileItem, response, status, headers) {
              if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onCancelItem == 'function') {
                $scope.options.fieldListeners.onCancelItem(fileItem, response, status, headers);
              }
            };
            uploader.onCompleteItem = function (fileItem, response, status, headers) {
              if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onCompleteItem == 'function') {
                $scope.options.fieldListeners.onCompleteItem(fileItem, response, status, headers);
              }
            };
            uploader.onCompleteAll = function () {
              if ($scope.options.hasOwnProperty('fieldListeners') && typeof $scope.options.fieldListeners.onCompleteAll == 'function') {
                $scope.options.fieldListeners.onCompleteAll();
              }
            };
            // ---
            // CONTROL TYPE= SELECT
            // ---
            $scope.refreshSelect = function (value) {
              if ($scope.options.selecttypesource == 'url' && true) {
                var sUrl = $scope.options.selectsource;
                if (typeof value != 'undefined') {
                  sUrl = sUrl + '&' + value;
                }
                $scope.options.loading = true;
                $http.get(sUrl).success(function (data, status, headers, config) {
                  $scope.optionsSelect = data;
                  for (var i = 0; i < $scope.optionsSelect.length; i++) {
                    if (!$scope.optionsSelect[i].hasOwnProperty('value')) {
                      var val = $scope.optionsSelect[i][$scope.options.optionvalue];
                      //if(typeof(val)=='string'){
                      //	$scope.optionsSelect[i].value=val.replace(new RegExp(' ', 'g'), 'aeiou');
                      //}else{
                      $scope.optionsSelect[i].value = val;  //}
                                                            //$scope.optionsSelect[i].value = $scope.optionsSelect[i][$scope.options.optionvalue].replace(' ','|');
                    }
                    if (!$scope.optionsSelect[i].hasOwnProperty('name')) {
                      if ($scope.options.selectconcatvaluename) {
                        $scope.optionsSelect[i].name = $scope.optionsSelect[i][$scope.options.optionvalue] + ' - ' + $scope.optionsSelect[i][$scope.options.optionname];
                      } else {
                        $scope.optionsSelect[i].name = $scope.optionsSelect[i][$scope.options.optionname];
                      }  //delete $scope.optionsSelect[i][$scope.options.optionname];
                         //delete $scope.optionsSelect[i][$scope.options.optionvalue];
                    } else {
                      if ($scope.options.selectconcatvaluename) {
                        $scope.optionsSelect[i].name = $scope.optionsSelect[i]['value'] + ' - ' + $scope.optionsSelect[i]['name'];
                      } else {
                        $scope.optionsSelect[i].name = $scope.optionsSelect[i][$scope.options.optionname];
                      }
                    }
                  }
                  $scope.onInit();
                  $scope.options.loading = false;
                }).error(function (data, status, headers, config) {
                  $scope.options.loading = false;
                });
              } else if ($scope.options.selecttypesource == 'array') {
                $scope.optionsSelect = $scope.options.selectsource;
                if (typeof $scope.optionsSelect != 'undefined') {
                  for (var i = 0; i < $scope.optionsSelect.length; i++) {
                    if (!$scope.optionsSelect[i].hasOwnProperty('value')) {
                      var val = $scope.optionsSelect[i][$scope.options.optionvalue];
                      //if(typeof(val)=='string'){
                      //	$scope.optionsSelect[i].value=val.replace(new RegExp(' ', 'g'), 'aeiou');
                      //}else{
                      $scope.optionsSelect[i].value = val;  //}
                                                            //$scope.optionsSelect[i].value = $scope.optionsSelect[i][$scope.options.optionvalue].replace(' ','|');
                    }
                    if (!$scope.optionsSelect[i].hasOwnProperty('name')) {
                      if ($scope.options.selectconcatvaluename) {
                        $scope.optionsSelect[i].name = $scope.optionsSelect[i][$scope.options.optionvalue] + ' - ' + $scope.optionsSelect[i][$scope.options.optionname];
                      } else {
                        $scope.optionsSelect[i].name = $scope.optionsSelect[i][$scope.options.optionname];
                      }
                    }
                  }
                }
                $scope.onInit();
              }
            };
            if ($scope.options.type == 'select') {
              if ($scope.options.selecttypesource == 'url' && (typeof $scope.options.autoload == 'undefined' || $scope.options.autoload == true)) {
                var sUrl = $scope.options.selectsource;
                if ($scope.options.loadOnInit) {
                  $scope.options.loading = true;
                  $http.get(sUrl).success(function (data, status, headers, config) {
                    $scope.optionsSelect = data;
                    for (var i = 0; i < $scope.optionsSelect.length; i++) {
                      if (!$scope.optionsSelect[i].hasOwnProperty('value')) {
                        var val = $scope.optionsSelect[i][$scope.options.optionvalue];
                        //if(typeof(val)=='string'){
                        //	$scope.optionsSelect[i].value=val.replace(new RegExp(' ', 'g'), 'aeiou');
                        //}else{
                        $scope.optionsSelect[i].value = val;  //}
                                                              //$scope.optionsSelect[i].value = $scope.optionsSelect[i][$scope.options.optionvalue].replace(' ','|');
                      }
                      if (!$scope.optionsSelect[i].hasOwnProperty('name')) {
                        if ($scope.options.selectconcatvaluename) {
                          $scope.optionsSelect[i].name = $scope.optionsSelect[i][$scope.options.optionvalue] + ' - ' + $scope.optionsSelect[i][$scope.options.optionname];
                        } else {
                          $scope.optionsSelect[i].name = $scope.optionsSelect[i][$scope.options.optionname];
                        }  //delete $scope.optionsSelect[i][$scope.options.optionname];
                           //delete $scope.optionsSelect[i][$scope.options.optionvalue];
                      } else {
                        if ($scope.options.selectconcatvaluename) {
                          $scope.optionsSelect[i].name = $scope.optionsSelect[i]['value'] + ' - ' + $scope.optionsSelect[i]['name'];
                        } else {
                          $scope.optionsSelect[i].name = $scope.optionsSelect[i][$scope.options.optionname];
                        }
                      }
                    }
                    $scope.onInit();
                    $scope.options.loading = false;
                  }).error(function (data, status, headers, config) {
                    $scope.options.loading = false;
                  });
                }
              } else if ($scope.options.selecttypesource == 'array') {
                $scope.optionsSelect = $scope.options.selectsource;
                $scope.$watchCollection('optionsSelect', function () {
                  if (typeof $scope.optionsSelect != 'undefined' && !$scope.optionsSelect[0].hasOwnProperty('value')) {
                    for (var i = 0; i < $scope.optionsSelect.length; i++) {
                      if (!$scope.optionsSelect[i].hasOwnProperty('value')) {
                        var val = $scope.optionsSelect[i][$scope.options.optionvalue];
                        //if(typeof(val)=='string'){
                        //	$scope.optionsSelect[i].value=val.replace(new RegExp(' ', 'g'), 'aeiou');
                        //}else{
                        $scope.optionsSelect[i].value = val;  //}
                                                              //$scope.optionsSelect[i].value = $scope.optionsSelect[i][$scope.options.optionvalue].replace(' ','|');
                      }
                      if (!$scope.optionsSelect[i].hasOwnProperty('name')) {
                        if ($scope.options.selectconcatvaluename) {
                          $scope.optionsSelect[i].name = $scope.optionsSelect[i][$scope.options.optionvalue] + ' - ' + $scope.optionsSelect[i][$scope.options.optionname];
                        } else {
                          $scope.optionsSelect[i].name = $scope.optionsSelect[i][$scope.options.optionname];
                        }
                      }
                    }
                  }
                  $scope.onInit();
                });
              }
            }
            // default value
            if (typeof $scope.options.default !== 'undefined') {
              $scope.value = $scope.options.default;
            }
            // set field id and name
            $scope.id = $scope.options.id || $scope.options.key;
            $scope.name = $scope.options.name || $scope.options.key;
          }
        ]
      };
    }
  ]);
  angular.module('edu-field.tpl').run([
    '$templateCache',
    function ($templateCache) {
      'use strict';
      $templateCache.put('directives/edu-field-autocomplete-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><edu-complete id_={{id}} name={{name}} onblur=onBlur() onfocus=onFocus() edu-focus=options.autofocus required placeholder={{options.placeholder}} pause={{autocpause}} selectedobject=value disabled readonly url={{options.autocurldata}} urldataloadall={{options.autocurldataloadall}} localdata=options.autoclocaldata searchfields={{options.autocsearchfields}} datafield={{options.autocfieldvalue}} titlefield={{options.autocfieldtitle}} descriptionfield={{options.autocfielddescription}} imagefield={{options.autocfieldimg}} minlength={{options.autocminlength}} inputclass="form-control input-{{options.inputSize}}" widthdropdown=options.widthdropdown whennotfoundreturnsearchstring="options.whenNotFoundReturnSearchString"><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un valor</small> <small class=error ng-show=$invalidMinlength>Debe tener al menos {{options.min}} caracteres</small> <small class=error ng-show=$invalidMaxlength>No puede tener m\xe1s de {{options.max}} caracteres</small> <small class=error ng-show=$invalidPattern>Introduzca un valor v\xe1lido</small></div></div>');
      $templateCache.put('directives/edu-field-button-tpl.html', '<div title={{options.title}} class="form-group {{options.col}}" style=position:relative style="border-style: dotted;height:40px;vertical-align:text-bottom" ng-hide=options.hidden><label for={{id}}>{{options.labelButton}} {{options.required ? \'*\' : \'\'}}</label><div style="padding-top:10px;border: 0px solid #ccc; -webkit-box-shadow: inset 0 0px 0px rgba(0,0,0,.075); box-shadow: inset 0 0px 0px rgba(0,0,0,.075)"><button id={{id}} name={{name}} ng-click=options.onClick() class="btn btn-{{options.state}} btn-{{options.size}}" ng-disabled=options.disabled><i class={{options.icon}}></i><span>{{options.label}}</span></button></div></div>');
      $templateCache.put('directives/edu-field-checkbox-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>&nbsp;</label><div class="form-control input-{{options.inputSize}}" style="border:solid 0px"><input type=checkbox id={{id}} name={{options.name}} autofocus ng-false-value={{options.false_value}} ng-true-value={{options.true_value}} ng-readonly=options.readonly ng-disabled=options.disabled ng-model=value ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange()> {{options.label}} {{options.required ? \'*\' : \'\'}}</div><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un valor</small></div></div>');
      $templateCache.put('directives/edu-field-cif-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><div class=input-group><span class=input-group-btn><button class="btn btn-{{options.typebutton || \'default\'}} btn-{{options.inputSize || \'default\'}}" type=button>CIF</button></span><div class=input-group><input style=min-width:250px class="form-control input-{{options.inputSize || \'default\'}}" id={{id}} name={{name}} edu-focus=options.autofocus placeholder={{options.placeholder}} ng-required=options.required ng-readonly=options.readonly ng-disabled=options.disabled ng-minlength=options.min ng-maxlength=options.max ng-pattern="nifniecifValidator(\'cif\')" ng-model=value ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange()></div></div><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un CIF</small> <small class=error ng-show=$invalidMinlength>Debe tener al menos {{options.min}} caracteres</small> <small class=error ng-show=$invalidMaxlength>No puede tener m\xe1s de {{options.max}} caracteres</small> <small class=error ng-show=$invalidPattern>Introduzca un NIF/NIE v\xe1lido</small></div></div>');
      $templateCache.put('directives/edu-field-currency-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><input currency class="form-control input-{{options.inputSize}}" id={{id}} placeholder={{options.placeholder}} autofocus ng-required=options.required ng-disabled=options.disabled ng-readonly=options.readonly ng-pattern=pattern_validator ng-model=value ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange()><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un valor.</small> <small class=error ng-show=$invalidPattern>Introduzca un n\xfamero v\xe1lido.</small></div></div>');
      $templateCache.put('directives/edu-field-date-ag-ui-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><div class="input-group date-group"><input class="form-control input-{{options.inputSize}}" datepicker-localdate ng-click="(options.disabled || options.readonly)?angular.noop():internalControl.showCalendar($event)" datepicker-popup="{{options.format || \'dd-MM-yyyy\'}}" min-date=options.minDateObj max-date=options.maxDateObj datepicker-options=options.dateOptions ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange() ng-keypress=onKeypress($event) ng-model=value ng-required=options.required ng-disabled=options.disabled ng-readonly=options.readonly is-open=options.showPopupCalendar edu-focus=options.autofocus current-text=Hoy clear-text=Borrar close-text="Cerrar"> <span class=input-group-btn ng-show=options.showButtonCalendar><button type=button ng-disabled=options.disabled class="btn btn-{{options.inputSize || \'sm\'}} btn-info" ng-click="(options.disabled || options.readonly)?angular.noop():internalControl.showCalendar($event)"><i class="glyphicon glyphicon-calendar"></i></button></span></div><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca una fecha</small> <small class=error ng-show=$invalidPattern>Introduzca un fecha v\xe1lida (dd-mm-aaaa)</small></div></div>');
      $templateCache.put('directives/edu-field-date-time-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><input date-time-input class="form-control {{options.inputSize}}" id={{id}} name={{name}} placeholder={{options.placeholder}} edu-focus=options.autofocus ng-required=options.required ng-disabled=options.disabled ng-readonly=options.readonly ng-model=value ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange()><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca una fecha y una hora</small> <small class=error ng-show=$invalidPattern>Introduzca un fecha y una hora v\xe1lida</small></div></div>');
      $templateCache.put('directives/edu-field-date-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><input type=date date-input class="form-control {{options.inputSize}}" id={{id}} name={{name}} placeholder={{options.placeholder}} edu-focus=options.autofocus ng-model=value ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange() ng-required=options.required ng-disabled=options.disabled ng-readonly=options.readonly><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca una fecha</small> <small class=error ng-show=$invalidPattern>Introduzca un fecha v\xe1lida (dd-mm-aaaa)</small></div></div>');
      $templateCache.put('directives/edu-field-email-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><input class="form-control input-{{options.inputSize}}" id={{id}} name={{name}} placeholder={{options.placeholder}} edu-focus=options.autofocus ng-required=options.required ng-disabled=options.disabled ng-readonly=options.readonly ng-minlength=options.min ng-maxlength=options.max ng-model=value ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange()><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un email</small> <small class=error ng-show=$invalidMinlength>Debe tener al menos {{options.min}} caracteres</small> <small class=error ng-show=$invalidMaxlength>No puede tener m\xe1s de {{options.max}} caracteres</small> <small class=error ng-show=$invalidPattern>Introduzca un email v\xe1lido</small></div></div>');
      $templateCache.put('directives/edu-field-file-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><div id={{id}}><div class=input-group><input type=file class="form-control input-{{options.inputSize}}" aria-describedby=basic-addon1 readonly file-model=value autofocus ng-required=options.required ng-disabled=options.disabled edu-focus="options.autofocus"> <span class=input-group-btn><label class="btn btn-{{options.typeButton || \'default\'}} btn-{{options.inputSize}}" type=button ng-click=upload(value)><i class="fa fa-{{options.iconButton}}"></i> {{options.labelButton}}</label></span></div></div><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un valor</small></div></div>');
      $templateCache.put('directives/edu-field-grid-tpl.html', '<style>.input-grid table {\r' + '\n' + '\t\tborder-collapse: collapse;\r' + '\n' + '\t}\r' + '\n' + '\r' + '\n' + '\tinput-grid table, th, td {\r' + '\n' + '\t\tborder: 1px solid #DCDCDC;\r' + '\n' + '\t}</style><div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" ng-hide=options.hidden><label for=input-grid-{{options.key}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><div style="overflow-x:scroll; height:{{(options.height*1+40)}}px; position:relative" class=form-control id=input-grid-{{options.key}}><scrollable-table watch=gridRows><table border=1 ng-style=widthTableGrid class=input-grid><thead><tr><th width=60px title="Nuevo elemento" ng-show="options.showButtons!=false"><button ng-click=gridNew() class="btn btn-sm btn-info" ng-hide=options.buttonNewHidden ng-disabled="options.state!=\'list\' || options.buttonNewDisabled"><span class="fa fa-plus"></span></button></th><th ng-repeat="field in options.listFields" class=noFixedColumn width={{field.weight}}% title=&nbsp style="text-align: center" id={{$index}}><span ng-click="changeOrder(field, field.orderByValue, \'desc\')" ng-show="field.order==\'asc\'" class=header-column title="Ordenar por {{field.label}}"><i class="glyphicon glyphicon-sort-by-alphabet"></i> <a>&nbsp{{field.label}}</a></span> <span ng-click="changeOrder(field, field.orderByValue, \'asc\')" ng-show="field.order==\'desc\'" class=header-column title="Ordenar por {{field.label}}"><i class="glyphicon glyphicon-sort-by-alphabet-alt"></i> <a>&nbsp{{field.label}}</a></span> <span ng-click="changeOrder(field, field.orderByValue, \'desc\')" ng-hide="field.order.length>0" class=header-column title="Ordenar por {{field.label}}"><a>&nbsp{{field.label}}</a></span></th></tr></thead><tbody><tr ng-repeat="item in gridRows" ng-click=options.gridOnRowClick(item)><td style="white-space: nowrap" ng-show="options.showButtons!=false"><form ng-show=item.$visible class="form-buttons form-inline" shown=item.$inserted><button ng-click=gridSave(item) ng-disabled="item.$waiting || options.buttonSaveDisabled" class="btn btn-sm btn-warning"><span class="fa fa-save"></span></button> <button type=button ng-show="options.state!=\'new\'" ng-disabled="item.$waiting || options.buttonUndoDisabled" ng-click=gridCancel(item) class="btn btn-sm btn-default"><span class="fa fa-undo"></span></button> <button class="btn btn-sm btn-danger" ng-show="options.state==\'new\'" ng-hide=options.buttonDeleteHidden ng-disabled=options.buttonDeleteDisabled ng-click=gridDelete(item,$index)><span class="fa fa-remove"></span></button></form><div class=buttons ng-show=!item.$visible><button class="btn btn-sm btn-primary" ng-disabled=options.buttonEditDisabled ng-hide=options.buttonEditHidden ng-click=gridEdit(item)><span class="fa fa-edit"></span></button> <button class="btn btn-sm btn-danger" ng-disabled=options.buttonDeleteDisabled ng-hide=options.buttonDeleteHidden ng-click=gridDelete(item,$index)><span class="fa fa-remove"></span></button></div></td><td ng-repeat="field in options.listFields" ng-click=options.gridOnRowClick()><div ng-switch on=field.type><div ng-switch-when=autocomplete><span ng-show=!item.$visible>{{item.$optionSelectedName[field.column]}}</span> <span ng-show=item.$visible><edu-complete id_={{id}} name={{name}} onblur=onBlur() onfocus=onFocus() edu-focus=options.autofocus required placeholder={{options.placeholder}} pause={{autocpause}} selectedobject=value disabled readonly url={{options.autocurldata}} urldataloadall={{options.autocurldataloadall}} localdata=options.autoclocaldata searchfields={{options.autocsearchfields}} datafield={{options.autocfieldvalue}} titlefield={{options.autocfieldtitle}} descriptionfield={{options.autocfielddescription}} imagefield={{options.autocfieldimg}} minlength={{options.autocminlength}} inputclass="form-control input-{{options.inputSize}}" widthdropdown=options.widthdropdown whennotfoundreturnsearchstring="options.whenNotFoundReturnSearchString"></span></div><div ng-switch-when=text><span ng-show=!item.$visible>{{item[field.column]}}</span> <span ng-show=item.$visible><input style=width:100% ng-disabled=field.disabled ng-model=item[field.column] class="form-control"></span></div><div ng-switch-when=number class=pull-right style=margin-right:10px><span ng-show=!item.$visible>{{field.renderer(item[field.column], entry, field.column,field.type)}}</span> <span ng-show=item.$visible><input style=width:100% ng-disabled=field.disabled ng-model=item[field.column] class="form-control"></span></div><div ng-switch-when=date><span ng-show=!item.$visible>{{item[field.column] | date:field.format ||\'dd/MM/yyyy\'}}</span> <span ng-show=item.$visible><input style=width:100% type=date ng-disabled=field.disabled ng-model=item[field.column] class="form-control"></span></div><div ng-switch-when=currency><span ng-show=!item.$visible>{{item[field.column] | toEuros}}</span> <span ng-show=item.$visible><input style=width:100% ng-disabled=field.disabled ng-model=item[field.column] class="form-control"></span></div><div ng-switch-when=checkbox><span ng-show=!item.$visible><input type=checkbox ng-model=item[field.column] ng-false-value={{options.false_value}} ng-true-value={{options.true_value}} onclick="return false"></span> <span ng-show=item.$visible><input style=width:100% type=checkbox ng-disabled=field.disabled ng-model=item[field.column] ng-false-value="\'N\'" ng-true-value="\'S\'"></span></div><div ng-switch-when=select><span ng-show=!item.$visible>{{item.$optionSelectedName[field.column]}}</span> <span ng-show=item.$visible><select style=width:100% ng-disabled=field.disabled ng-change=onChange(item,field,field.column,item[field.column]) ng-model=item[field.column] class=form-control><option></option><option ng-repeat="option in field.selectOptions" value={{option.value}}>{{option.name}}</option></select></span></div><div ng-switch-default><span ng-show=!item.$visible>{{item[field.column]}}</span> <span ng-show=item.$visible><input style=width:100% ng-disabled=field.disabled ng-model="item[field.column]"></span></div></div></td></tr></tbody></table></scrollable-table></div><div ng-show=options.inputGridLoading class=overlay-edu-grid><div class="spin centrado-edu-grid"></div></div><div name=overlay class=overlay-edu-crud ng-show=options.showOverlayInputGridFormDelete><div class=centrado-edu-crud><div class="panel panel-info"><div class=panel-heading><div class=row><div class=col-md-12><h4>{{options.snippets.formDeleteTitle || \'Por favor confirme\'}}</h4></div></div></div><div class=panel-body><h4>{{options.snippets.formDeleteMessage || \'\xbfEst\xe1 seguro que quiere ELIMINAR el registro\'}} {{keyRowForDelete}} ?</h4></div><div class=panel-footer><div class=row><div class="col-md-offset-6 col-md-6"><button ng-click=inputGridFormDeleteContinue() class="btn btn-sm btn-primary">{{options.snippets.formDeleteButtonContinue || \'Continuar\'}}</button> <button ng-click=inputGridFormDeleteCancel() class="btn btn-sm">{{options.snippets.formDeleteButtonCancel || \'Cancelar\'}}</button></div></div></div></div></div></div><div class=overlay-edu-crud ng-show=options.overlayFormSuccessErrorInputGrid.show><div class="panel panel-{{options.overlayFormSuccessErrorInputGrid.type|| \'info\'}} centrado-edu-grid" style=min-width:{{options.overlayFormSuccessErrorInputGrid.width||200}}px><div class=panel-heading><span ng-if="options.overlayFormSuccessErrorInputGrid.type==\'success\'" class="glyphicon glyphicon-ok pull-right"></span> <span ng-if="options.overlayFormSuccessErrorInputGrid.type==\'danger\'" class="glyphicon glyphicon-remove pull-right"></span><br></div><div class=panel-body><h4>{{options.overlayFormSuccessErrorInputGrid.message}}</h4></div><div class=panel-footer><div class=row><div class="col-md-offset-3 col-md-9"><button ng-click="options.overlayFormSuccessErrorInputGrid.show=false" class="btn btn-sm btn-primary">{{options.snippets.overlayFormSuccessErrorInputGrid || \'Aceptar\'}}</button></div></div></div></div></div></div>');
      $templateCache.put('directives/edu-field-hidden-tpl.html', '<input type=hidden class=form-control id={{id}} name={{name}} ng-model=value ng-change=onChange()>');
      $templateCache.put('directives/edu-field-iban-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><div class=input-group><span class=input-group-btn><button class="btn btn-{{options.typebutton || \'default\' }} btn-{{options.inputSize || \'default\'}}" type=button>IBAN</button></span> <input class="form-control input-{{options.inputSize || \'default\'}}" id={{id}} name={{name}} autofocus placeholder={{options.placeholder}} edu-focus=options.autofocus ng-required=options.required ng-disabled=options.disabled ng-readonly=options.readonly ng-pattern=ibanValidator ng-minlength=options.min ng-maxlength=options.max ng-model=value ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange()></div><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un IBAN</small> <small class=error ng-show=$invalidMinlength>Debe tener al menos {{options.min}} caracteres</small> <small class=error ng-show=$invalidMaxlength>No puede tener m\xe1s de {{options.max}} caracteres</small> <small class=error ng-show=$invalidPattern>Introduzca un IBAN v\xe1lido</small></div></div>');
      $templateCache.put('directives/edu-field-iban2-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative><style>.iban-sm::-webkit-input-placeholder{\r' + '\n' + '\t\tfont-size: 8px;\t\r' + '\n' + '\t}\r' + '\n' + '\t\r' + '\n' + '\t.input-sm.dp{\r' + '\n' + '\t\twidth:40px;\r' + '\n' + '\t\tpadding:2px;\r' + '\n' + '\t}\r' + '\n' + '\t.input-sm.dc1{\r' + '\n' + '\t\twidth:20px;\r' + '\n' + '\t\tpadding:2px;\r' + '\n' + '\t}\r' + '\n' + '\t.input-sm.entidad{\r' + '\n' + '\t\twidth:35px;\r' + '\n' + '\t\tpadding:2px;\r' + '\n' + '\t}\r' + '\n' + '\t.input-sm.oficina{\r' + '\n' + '\t\twidth:35px;\r' + '\n' + '\t\tpadding:2px;\r' + '\n' + '\t}\r' + '\n' + '\t.input-sm.dc2{\r' + '\n' + '\t\twidth:20px;\r' + '\n' + '\t\tpadding:2px;\r' + '\n' + '\t}\r' + '\n' + '\t\r' + '\n' + '\t.input-sm.cuenta{\r' + '\n' + '\t\twidth:77px;\r' + '\n' + '\t\tpadding:2px;\r' + '\n' + '\t}\r' + '\n' + '\t\r' + '\n' + '\t\r' + '\n' + '\t\r' + '\n' + '\t.iban-lg::-webkit-input-placeholder{\r' + '\n' + '\t\tfont-size: 13px;\t\r' + '\n' + '\t}\r' + '\n' + '\t\r' + '\n' + '\t.input-lg.dp{\r' + '\n' + '\t\twidth:56px;\r' + '\n' + '\t\tpadding:2px;\r' + '\n' + '\t}\r' + '\n' + '\t.input-lg.dc1{\r' + '\n' + '\t\twidth:28px;\r' + '\n' + '\t\tpadding:2px;\r' + '\n' + '\t}\r' + '\n' + '\t.input-lg.entidad{\r' + '\n' + '\t\twidth:50px;\r' + '\n' + '\t\tpadding:2px;\r' + '\n' + '\t}\r' + '\n' + '\t.input-lg.oficina{\r' + '\n' + '\t\twidth:50px;\r' + '\n' + '\t\tpadding:2px;\r' + '\n' + '\t}\r' + '\n' + '\t.input-lg.dc2{\r' + '\n' + '\t\twidth:28px;\r' + '\n' + '\t\tpadding:2px;\r' + '\n' + '\t}\r' + '\n' + '\t\r' + '\n' + '\t.input-lg.cuenta{\r' + '\n' + '\t\twidth:110px;\r' + '\n' + '\t\tpadding:2px;\r' + '\n' + '\t}\r' + '\n' + '\t\r' + '\n' + '\t\r' + '\n' + '\t\r' + '\n' + '\t.iban-default::-webkit-input-placeholder{\r' + '\n' + '\t\tfont-size: 10px;\t\r' + '\n' + '\t}\r' + '\n' + '\t\r' + '\n' + '\r' + '\n' + '\t\r' + '\n' + '\t.input-default.dp{\r' + '\n' + '\t\twidth:44px;\r' + '\n' + '\t\tpadding:2px;\r' + '\n' + '\t}\r' + '\n' + '\t.input-default.dc1{\r' + '\n' + '\t\twidth:22px;\r' + '\n' + '\t\tpadding:2px;\r' + '\n' + '\t}\r' + '\n' + '\t.input-default.entidad{\r' + '\n' + '\t\twidth:40px;\r' + '\n' + '\t\tpadding:1px;\r' + '\n' + '\t}\r' + '\n' + '\t.input-default.oficina{\r' + '\n' + '\t\twidth:40px;\r' + '\n' + '\t\tpadding:2px;\r' + '\n' + '\t}\r' + '\n' + '\t.input-default.dc2{\r' + '\n' + '\t\twidth:22px;\r' + '\n' + '\t\tpadding:2px;\r' + '\n' + '\t}\r' + '\n' + '\t\r' + '\n' + '\t.input-default.cuenta{\r' + '\n' + '\t\twidth:87px;\r' + '\n' + '\t\tpadding:2px;\r' + '\n' + '\t}</style><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><div class=input-group><span class=input-group-btn ng-if=options.showbutton><button class="btn btn-{{options.typebutton || \'default\' }} btn-{{options.inputSize || \'def\'}}" type=button>IBAN</button></span><form><input maxlength=4 class="form-control dp iban-{{options.inputSize || \'default\'}} input-{{options.inputSize || \'default\'}}" id={{options.key}}-pais name={{options.key}}-pais placeholder=DP autofocus ng-required=options.required ng-disabled=options.disabled ng-model="value[\'IBANDP\']" ng-blur="onBlur(\'IBANDP\')" ng-focus="onFocus(\'IBANDP\')" ng-change="onChange(\'IBANDP\')" ng-keypress=onKeypress($event) validate-iban ng-style=border> <input maxlength=4 class="form-control entidad iban-{{options.inputSize || \'default\'}} input-{{options.inputSize || \'default\'}}" id={{options.key}}-entidad name={{options.key}}-entidad placeholder=Entidad ng-required=options.required ng-disabled=options.disabled ng-model="value[\'IBANEntidad\']" ng-blur="onBlur(\'IBANEntidad\')" ng-focus="onFocus(\'IBANEntidad\')" ng-change="onChange(\'IBANEntidad\')" ng-keypress=onKeypress($event) validate-iban ng-style=border> <input maxlength=4 class="form-control oficina iban-{{options.inputSize || \'default\'}} input-{{options.inputSize || \'default\'}}" id={{options.key}}-oficina name={{options.key}}-oficina placeholder=Oficina ng-required=options.required ng-disabled=options.disabled ng-model="value[\'IBANOficina\']" ng-blur="onBlur(\'IBANOficina\')" ng-focus="onFocus(\'IBANOficina\')" ng-change="onChange(\'IBANOficina\')" ng-keypress=onKeypress($event) validate-iban ng-style=border> <input maxlength=2 class="form-control dc2 iban-{{options.inputSize || \'default\'}} input-{{options.inputSize || \'default\'}}" id={{options.key}}-control2 name={{options.key}}-control2 placeholder=DC ng-required=options.required ng-disabled=options.disabled ng-model="value[\'IBANDC2\']" ng-blur="onBlur(\'IBANDC2\')" ng-focus="onFocus(\'IBANDC2\')" ng-change="onChange(\'IBANDC2\')" ng-keypress=onKeypress($event) validate-iban ng-style=border> <input maxlength=10 class="form-control cuenta iban-{{options.inputSize || \'default\'}} input-{{options.inputSize || \'default\'}}" id={{options.key}}-cuenta name={{options.key}}-cuenta placeholder="N\xba Cuenta" ng-required=options.required ng-disabled=options.disabled ng-model="value[\'IBANCuenta\']" ng-blur="onBlur(\'IBANCuenta\')" ng-focus="onFocus(\'IBANCuenta\')" ng-change="onChange(\'IBANCuenta\')" ng-keypress=onKeypress($event) validate-iban ng-style=border></form></div><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un IBAN</small> <small class=error ng-show=$invalidMinlength>Debe tener al menos {{options.min}} caracteres</small> <small class=error ng-show=$invalidMaxlength>No puede tener m\xe1s de {{options.max}} caracteres</small> <small class=error ng-show=$invalidPattern>Introduzca un IBAN v\xe1lido</small></div></div>');
      $templateCache.put('directives/edu-field-image-tpl.html', '<div class="form-group {{options.col}}" ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><div id={{id}} style=height:{{options.height}}>{{options.text}}</div></div>');
      $templateCache.put('directives/edu-field-literal-tpl.html', '<div class="form-group {{options.col}}" ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><div id={{id}}>{{options.text}}</div></div>');
      $templateCache.put('directives/edu-field-month-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><input type=month class="form-control input-{{options.inputSize}}" id={{id}} name={{name}} edu-focus=options.autofocus placeholder={{options.placeholder}} edu-focus=options.autofocus ng-required=options.required ng-disabled=options.disabled ng-readonly=options.readonly pattern=options.pattern ng-model=value ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange()><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un mes</small> <small class=error ng-show=$invalidPattern>Introduzca un mes v\xe1lido</small></div></div>');
      $templateCache.put('directives/edu-field-nie-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><div class=input-group><span class=input-group-btn><button class="btn btn-{{options.typebutton || \'default\'}} btn-{{options.inputSize || \'default\'}}" type=button>NIE</button></span><div class=input-group><input style=min-width:250px class="form-control input-{{options.inputSize}}" id={{id}} name={{name}} edu-focus=options.autofocus placeholder={{options.placeholder}} ng-required=options.required ng-disabled=options.disabled ng-readonly=options.readonly ng-minlength=options.min ng-maxlength=options.max ng-pattern=nifniecifValidator ng-model=value ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange()></div></div><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un NIE</small> <small class=error ng-show=$invalidMinlength>Debe tener al menos {{options.min}} caracteres</small> <small class=error ng-show=$invalidMaxlength>No puede tener m\xe1s de {{options.max}} caracteres</small> <small class=error ng-show=$invalidPattern>Introduzca un NIF/NIE v\xe1lido</small></div></div>');
      $templateCache.put('directives/edu-field-nif-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><div class=input-group><span class=input-group-btn><button class="btn btn-{{options.typebutton || \'default\'}} btn-{{options.inputSize || \'default\'}}" type=button>NIE</button></span><div class=input-group><input style=min-width:250px class="form-control {{options.inputSizeClass}}" id={{id}} name={{name}} placeholder={{options.placeholder}} edu-focus=options.autofocus ng-required=options.required ng-disabled=options.disabled ng-readonly=options.readonly ng-minlength=options.min ng-maxlength=options.max ng-pattern="nifniecifValidator(\'nif\')" ng-model=value ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange()></div></div><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un NIF</small> <small class=error ng-show=$invalidMinlength>Debe tener al menos {{options.min}} caracteres</small> <small class=error ng-show=$invalidMaxlength>No puede tener m\xe1s de {{options.max}} caracteres</small> <small class=error ng-show=$invalidPattern>Introduzca un NIF/NIE v\xe1lido</small></div></div>');
      $templateCache.put('directives/edu-field-nifniecif-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><div class=input-group><span class=input-group-btn><button class="btn btn-{{options.typebutton || \'default\' }} btn-{{options.inputSize || \'default\'}}" type=button>{{options.textbutton}}</button></span> <input class="form-control input-{{options.inputSize || \'default\'}}" id={{id}} name={{name}} placeholder={{options.placeholder}} edu-focus=options.autofocus ng-required=options.required ng-disabled=options.disabled ng-readonly=options.readonly ng-minlength=options.min ng-maxlength=options.max ng-pattern=nifniecifValidator ng-model=value ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange()></div><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un {{options.textbutton|| \'N\xfamero de documento\'}}</small> <small class=error ng-show=$invalidMinlength>Debe tener al menos {{options.min}} caracteres</small> <small class=error ng-show=$invalidMaxlength>No puede tener m\xe1s de {{options.max}} caracteres</small> <small class=error ng-show=$invalidPattern>Introduzca un {{options.textbutton|| \'N\xfamero de documento\'}} v\xe1lido</small></div></div>');
      $templateCache.put('directives/edu-field-number-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><input class="form-control input-{{options.inputSize}}" id={{id}} placeholder={{options.placeholder}} edu-focus=options.autofocus ng-required=options.required ng-disabled=options.disabled ng-readonly=options.readonly ng-pattern=pattern_validator ng-model=value ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange()><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un valor.</small> <small class=error ng-show=$invalidPattern>Introduzca un n\xfamero v\xe1lido.</small></div></div>');
      $templateCache.put('directives/edu-field-password-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><input type=password class="form-control input-{{options.inputSize}}" id={{id}} edu-focus=options.autofocus ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange() autofocus ng-required=options.required ng-disabled=options.disabled ng-readonly=options.readonly ng-pattern=pattern_validator ng-model=value><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un valor</small> <small class=error ng-show=$invalidMinlength>Debe tener al menos {{options.min}} caracteres</small> <small class=error ng-show=$invalidMaxlength>No puede tener m\xe1s de {{options.max}} caracteres</small> <small class=error ng-show=$invalidPattern>Introduzca un valor v\xe1lido</small></div></div>');
      $templateCache.put('directives/edu-field-radio-tpl.html', '<div class="radio-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label class=control-label>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><div class=radio ng-repeat="(key, option) in options.options"><label><input type=radio id="{{id + \'_\'+ $index}}" name={{options.name}} edu-focus=options.autofocus ng-value=option.value ng-required=options.required ng-model=$parent.value ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange()> {{option.name}}</label></div><div class="help-block has-error" ng-show=$invalid><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un valor.</small></div></div>');
      $templateCache.put('directives/edu-field-range-tpl.html', '<div class="form-group {{options.col}}" ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}} : {{value}}</label><div><div style=clear:both><span class=pull-left><strong>{{options.min}}</strong></span> <span class=pull-right><strong>{{options.max}}</strong></span></div><div style=clear:both><div style=float:left;margin:3px;width:10%><button type=button class="btn btn-xs btn-primary pull-left" ng-click="value=(value>options.min)?(value*1-1):options.min"><i class="fa fa-minus"></i></button></div><div style=float:left;width:70%><input type=range id={{id}} edu-focus=options.autofocus placeholder={{options.placeholder}} ng-required=options.required ng-disabled=options.disabled ng-readonly=options.readonly min=options.min max=options.max ng-model=value></div><div style=float:left;margin:3px;width:10%><button type=button class="btn btn-xs btn-primary pull-right" ng-click="value=(value<options.max)?(value*1+1):options.max"><i class="fa fa-plus"></i></button></div></div></div></div>');
      $templateCache.put('directives/edu-field-select-remote-tpl.html', '<div class="form-group {{options.col}}" ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><div edu-select-remote inputclass="form-control input-{{options.inputSize}}" ng-model=value edu-focus=options.autofocus ng-required=options.required ng-disabled=options.disabled ng-readonly=options.readonly uri-data=options.uridata field-value=options.fieldvalue field-name=options.fieldname></div></div>');
      $templateCache.put('directives/edu-field-select-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}} <i ng-if=options.loading style=position:absolute;top:30px;left:25px;font-size:20px class=loadinggif>&nbsp;&nbsp;&nbsp;</i></label><select ng-show=options.groupBy class="form-control input-{{options.inputSize}}" id=1-{{id}} dependent-control=options.dependentControl edu-focus=options.autofocus ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange() ng-model=value ng-required=options.required ng-disabled=options.disabled ng-options="option.value as option.name group by option[options.groupBy] for option in optionsSelect"><option value="">{{options.emptyOptionText}}</option></select><select ng-show=!options.groupBy class="form-control input-{{options.inputSize}}" id=2-{{id}} dependent-control=options.dependentControl edu-focus=options.autofocus ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange() ng-model=value ng-required=options.required ng-disabled=options.disabled ng-options="option.value as option.name for option in optionsSelect">><option value="">{{options.emptyOptionText}}</option></select><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un valor</small> <small class=error ng-show=$invalidMinlength>Debe tener al menos {{options.min}} caracteres</small> <small class=error ng-show=$invalidMaxlength>No puede tener m\xe1s de {{options.max}} caracteres</small> <small class=error ng-show=$invalidPattern>Introduzca un valor v\xe1lido</small></div></div>');
      $templateCache.put('directives/edu-field-text-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><input capitalize={{options.uppercase}} class="form-control input-{{options.inputSize}}" id={{id}} name={{name}} placeholder={{options.placeholder}} edu-focus=options.autofocus ng-required=options.required ng-disabled=options.disabled ng-readonly=options.readonly ng-pattern=pattern_validator ng-minlength=options.min ng-maxlength=options.max ng-model=value ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange() ng-keypress=onKeypress($event)><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un valor</small> <small class=error ng-show=$invalidMinlength>Debe tener al menos {{options.min}} caracteres</small> <small class=error ng-show=$invalidMaxlength>No puede tener m\xe1s de {{options.max}} caracteres</small> <small class=error ng-show=$invalidPattern>Introduzca un valor v\xe1lido</small></div></div>');
      $templateCache.put('directives/edu-field-textarea-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><textarea type=text class=form-control id={{id}} rows={{options.rows}} placeholder={{options.placeholder}} edu-focus=options.autofocus ng-required=options.required ng-readonly=options.readonly ng-disabled=options.disabled ng-minlength=options.minlength ng-maxlength=options.maxlength ng-pattern=pattern_validator ng-trim=options.trim ng-model=value ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange()>\r' + '\n' + '</textarea><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un valor</small> <small class=error ng-show=$invalidMinlength>No alcanza el n\xfamero m\xednimo de caracteres ({{options.minlength}})</small> <small class=error ng-show=$invalidMaxlength>Excede el n\xfamero m\xe1ximo de caracteres ({{options.maxlength}})</small> <small class=error ng-show=$invalidPattern>Introduzca un valor v\xe1lido</small></div></div>');
      $templateCache.put('directives/edu-field-textbutton-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><div class=input-group><span class=input-group-btn><a ng-click=onClick() class="btn btn-{{options.typebutton||\'default\'}} btn-{{options.inputSize || \'default\'}}"><i class="fa fa-{{options.icon}}"></i> {{options.textbutton}}</a></span> <input class="form-control input-{{options.inputSize || \'default\'}}" id={{id}} name={{name}} placeholder={{options.placeholder}} autofocus ng-required=options.required ng-disabled=options.disabled ng-pattern=options.pattern ng-minlength=options.min ng-maxlength=options.max ng-model=value ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange()></div><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un valor</small> <small class=error ng-show=$invalidMinlength>Debe tener al menos {{options.min}} caracteres</small> <small class=error ng-show=$invalidMaxlength>No puede tener m\xe1s de {{options.max}} caracteres</small> <small class=error ng-show=$invalidPattern>Introduzca un valor v\xe1lido</small></div></div>');
      $templateCache.put('directives/edu-field-textedit-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}} ng-click="options.readonly=true">{{options.label}} {{options.required ? \'*\' : \'\'}}</label><div text-angular id={{id}} name={{name}} ta-toolbar="options.toolbar || [[\'h1\',\'h2\',\'h3\'],[\'bold\',\'italics\']] " ng-model=value ng-required=options.required ta-disabled="options.disabled || options.readonly" autofocus ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange()></div><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un valor</small></div></div>');
      $templateCache.put('directives/edu-field-time-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><input class="form-control input-{{options.inputSize}}" id={{id}} name={{name}} placeholder={{options.placeholder}} edu-focus=options.autofocus ng-required=options.required ng-disabled=options.disabled ng-readonly=options.readonly ng-pattern="/^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/" ng-model=value ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange()><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca una hora</small> <small class=error ng-show=$invalidPattern>Introduzca una hora v\xe1lida</small></div></div>');
      $templateCache.put('directives/edu-field-tritoggle-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>&nbsp;</label><div class="form-control input-{{options.inputSize}}" style="border:solid 0px"><div three-state-checkbox ng-model=value ng-change=onChange() options="{color: \'red\'}"></div>{{options.label}} {{options.required ? \'*\' : \'\'}}</div><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un valor</small></div></div>');
      $templateCache.put('directives/edu-field-upload-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><div id={{id}}><input ng-if=!options.multiple type=file class=jfilestyle nv-file-select="" uploader=uploader data-buttontext="Find file" id=jfilestyle-2 style="position: fixed; left: -500px"><div class=input-group><input class="form-control input-{{options.inputSize}}" aria-describedby=basic-addon1 readonly ng-model=value edu-focus=options.autofocus ng-required=options.required ng-disabled=options.disabled ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange()> <span class=input-group-btn><label for=jfilestyle-2 class="btn btn-{{options.typeButton || \'default\'}} btn-{{options.inputSize}}" type=button><i class="fa fa-{{options.iconButton}}"></i> {{options.labelButton}}</label></span></div></div><div style="margin-bottom: 0px"><table class=table style=width:100%><thead><tr ng-show=options.showbuttons><th width=50%>Nombre</th><th ng-show=uploader.isHTML5>Tama\xf1o</th><th>Estado</th><th>Acciones</th></tr></thead><tbody><tr ng-repeat="item in uploader.queue" ng-show=options.showbuttons><td><strong>{{ item.file.name }}</strong></td><td ng-show=uploader.isHTML5 nowrap>{{ item.file.size/1024/1024|number:2 }} MB</td><td class=text-center><span ng-show=item.isSuccess><i class="glyphicon glyphicon-ok"></i></span> <span ng-show=item.isCancel><i class="glyphicon glyphicon-ban-circle"></i></span> <span ng-show=item.isError><i class="glyphicon glyphicon-remove"></i></span></td><td nowrap><button type=button class="btn btn-success btn-xs" ng-click=item.upload() ng-disabled="item.isReady || item.isUploading || item.isSuccess"><span class="glyphicon glyphicon-upload"></span> Subir</button> <button type=button class="btn btn-warning btn-xs" ng-click=item.cancel() ng-disabled=!item.isUploading><span class="glyphicon glyphicon-ban-circle"></span> Cancelar</button> <button type=button class="btn btn-danger btn-xs" ng-click=item.remove()><span class="glyphicon glyphicon-trash"></span> Eliminar</button></td></tr></tbody></table><table ng-show=options.showprogressbar style=width:100%><tr><td>Progreso:</td></tr><tr><td><div class=progress><div class=progress-bar role=progressbar ng-style="{ \'width\': uploader.progress + \'%\' }"></div></div></td></tr></table></div><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un valor</small></div></div>');
      $templateCache.put('directives/edu-field-upload15x-tpl.html', '<div flow-prevent-drop class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><div id={{id}} ngf-drop><div class=input-group><span class=input-group-btn><button class="btn btn-{{options.typeButton || \'default\'}} btn-{{options.inputSize}}" type=button ngf-select=ngfselect(file) ng-model=file name=file ngf-pattern="\'{{options.pattern}}/*\'" ngf-accept="\'{{options.pattern}}/*\'" ngf-max-size={{options.maxsize}} ngf-min-height=100 ngf-resize="{width: 100, height: 100}"><i class="fa fa-{{options.iconButtonSelectFile || \'folder-open\'}}"></i> {{options.labelButtonSelectFile}}</button></span> <input class="form-control input-{{options.inputSize}}" aria-describedby=basic-addon1 readonly ng-model=value edu-focus=options.autofocus ng-required=options.required ng-disabled=options.disabled ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange()> <span class=input-group-btn><button class="btn btn-{{options.typeButton || \'default\'}} btn-{{options.inputSize}}" type=button ng-click=ngfupload()><i ng-if=!uploading class="fa fa-{{options.iconButtonUpload || \'upload\'}}"></i> <i ng-if=uploading class="fa fa-spinner fa-spin"></i> <span ng-if=uploading>{{progressPercentage}}%</span> {{options.labelButtonUpload}}</button></span></div></div><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca un valor</small></div></div>');
      $templateCache.put('directives/edu-field-url-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><input class="form-control input-{{options.inputSize}}" id={{id}} name=name placeholder={{options.placeholder}} ng-blur=onBlur() edu-focus=options.autofocus ng-focus=onFocus() ng-change=onChange() ng-focus=options.autofocus ng-required=options.required ng-disabled=options.disabled ng-pattern="/^(https?:\\/\\/)?([\\da-z\\.-]+)\\.([a-z\\.]{2,6})([\\/\\w \\.-]*)*\\/?$/" ng-minlength=options.min ng-maxlength=options.max ng-model=value><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca una url</small> <small class=error ng-show=$invalidMinlength>Debe tener al menos {{options.min}} caracteres</small> <small class=error ng-show=$invalidMaxlength>No puede tener m\xe1s de {{options.max}} caracteres</small> <small class=error ng-show=$invalidPattern>Introduzca una url v\xe1lida</small></div></div>');
      $templateCache.put('directives/edu-field-week-tpl.html', '<div class="form-group {{options.col}}" ng-class="{\'has-error\':  $invalid, \'has-success\': !$invalid && $dirty}" style=position:relative ng-hide=options.hidden><label for={{id}}>{{options.label}} {{options.required ? \'*\' : \'\'}}</label><input type=week class="form-control input-{{options.inputSize}}" id={{id}} name={{name}} placeholder={{options.placeholder}} edu-focus=options.autofocus ng-required=options.required ng-disabled=options.disabled ng-pattern="/^(19|20)\\d\\d[-\\/]W(0[1-9]|[1-4][0-9]|5[0123])$/" ng-model=value ng-blur=onBlur() ng-focus=onFocus() ng-change=onChange()><div class="help-block has-error" ng-show=$invalid style=position:absolute;top:50px><small class=error ng-show=$invalidRequired>Campo obligatorio. Introduzca una semana</small> <small class=error ng-show=$invalidPattern>Introduzca una semana v\xe1lida</small></div></div>');
    }
  ]);
  //funciones de validacion
  var modo_validacion_general;
  function esBarra(cad, pos) {
    var letra = cad.substr(pos, 1);
    if (letra == '/')
      return true;
    else
      return false;
  }
  function esDosPuntos(cad, pos) {
    var letra = cad.substr(pos, 1);
    if (letra == ':')
      return true;
    else
      return false;
  }
  //convierte una letra en numero si puede dentro de una cadena en una posicion dada
  function valorLetra(cadena, pos) {
    return parseInt(cadena.substr(pos, 1));
  }
  //toma una letra dentro de una cadena 
  function cogeLetra(cadena, pos) {
    return cadena.substr(pos, 1);
  }
  //comprueba si una letra dentro de una cadena es un numero o no
  function esNumero(cadena, pos) {
    var letra = cogeLetra(cadena, pos);
    if (letra >= '0' && letra <= '9')
      return true;
    else
      return false;
  }
  //indica cuantos digitos hay a partir de una posicion dada, modifica la posicion
  function cuantosNumeros(cad, pos) {
    var res = 0;
    var len = cad.length;
    var letra;
    letra = cad.substr(pos, 1);
    while (pos < len && letra >= '0' && letra <= '9') {
      res = res + 1;
      pos = pos + 1;
      if (pos < len)
        letra = cad.substr(pos, 1);
    }
    return res;
  }
  //devuelve una cadena de error si hay error
  //jgr59k 16/01/2007 13:37 h. Funci�n que comprueba si una direcci�n de correo est� bien construida, por ejemplo, xxx@yyyy.zz
  function compruebaEmail(campo, valor) {
    /*DGILG  5/06/2009 
  	if ( valor.indexOf('@', 0)==-1 || valor.indexOf('.',0)==-1 ) {		
  		return "el campo "+campo+" = "+valor+" no es un email correcto\n";	
  	}		
  		return "";
  	*/
    //function checkMail(campo,valor) {
    if (valor.length == 0) {
      return '';
    }
    var emailPat = /^(.+)@(.+)$/;
    var specialChars = '\\(\\)<>@,;:\\\\\\"\\.\\[\\]';
    var validChars = '[^\\s' + specialChars + ']';
    var quotedUser = '("[^"]*")';
    var ipDomainPat = /^(\d{1,3})[.](\d{1,3})[.](\d{1,3})[.](\d{1,3})$/;
    var atom = validChars + '+';
    var word = '(' + atom + '|' + quotedUser + ')';
    var userPat = new RegExp('^' + word + '(\\.' + word + ')*$');
    var domainPat = new RegExp('^' + atom + '(\\.' + atom + ')*$');
    var matchArray = valor.match(emailPat);
    if (matchArray == null) {
      //return false;
      return 'el campo ' + campo + ' = ' + valor + ' no es un email correcto\n';
    }
    var user = matchArray[1];
    var domain = matchArray[2];
    if (user.match(userPat) == null) {
      return 'el campo ' + campo + ' = ' + valor + ' no es un email correcto\n';  //return false;
    }
    var IPArray = domain.match(ipDomainPat);
    if (IPArray != null) {
      for (var i = 1; i <= 4; i++) {
        if (IPArray[i] > 255) {
          return 'el campo ' + campo + ' = ' + valor + ' no es un email correcto\n';  //return false;
        }
      }
      return '';  //return true;
    }
    var domainArray = domain.match(domainPat);
    if (domainArray == null) {
      return 'el campo ' + campo + ' = ' + valor + ' no es un email correcto\n';  //return false;
    }
    var atomPat = new RegExp(atom, 'g');
    var domArr = domain.match(atomPat);
    var len = domArr.length;
    if (domArr[domArr.length - 1].length < 2 || domArr[domArr.length - 1].length > 3) {
      return 'el campo ' + campo + ' = ' + valor + ' no es un email correcto\n';  //return false;
    }
    if (len < 2) {
      return 'el campo ' + campo + ' = ' + valor + ' no es un email correcto\n';  //return false;
    }
    //return true;
    return '';  //}
  }
  //ayuda para calcular los dias que tiene el mes de febrero
  function esBisiesto(anio) {
    var BISIESTO;
    if (anio % 4 == 0) {
      if (anio % 100 == 0) {
        if (anio % 400 == 0)
          BISIESTO = true;
        else
          BISIESTO = false;
      } else
        BISIESTO = true;
    } else
      BISIESTO = false;
    return BISIESTO;
  }
  //devuelve una cadena de error si hay error
  //opcionalmente se puede pasar el formato del numero como tercer parametro
  //el formato es: F, F2,F4 (2/4, 2 y 4 digitos para el a�o, respectivamente)
  function compruebaFecha(campo, valor) {
    var error;
    var iDia, iMes, iAno;
    var dia, mes, ano;
    var maxdia = new Array(-1, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
    //codigo para calcular el numero de digitos del a�o y el mensaje de error
    var formato = 'F';
    if (compruebaFecha.arguments.length > 2)
      formato = compruebaFecha.arguments[2];
    var digitosAno;
    var auxErr;
    if (formato == 'F') {
      digitosAno = 4;
      //ojo OBLIGATORIAMENTE debe ser 4 para que luego compruebe los de 2
      auxErr = '2 o 4';
    } else if (formato == 'F2') {
      digitosAno = 2;
      auxErr = '2';
    } else if (formato == 'F4') {
      digitosAno = 4;
      auxErr = '4';
    }
    error = '';
    iDia = cuantosNumeros(valor, 0);
    if (iDia == 1 || iDia == 2) {
      if (esBarra(valor, iDia)) {
        iMes = cuantosNumeros(valor, iDia + 1);
        if (iMes == 1 || iMes == 2) {
          if (esBarra(valor, iDia + 1 + iMes)) {
            iAno = cuantosNumeros(valor, iDia + 1 + iMes + 1);
            //agm88x 20-6-2006: a�adido para formatos de 2/4 y 4 digitos he complicado la formula
            if (iAno == digitosAno || formato == 'F' && iAno == 2) {
              if (iDia + 1 + iMes + 1 + iAno == valor.length) {
                dia = parseInt(valor.substr(0, iDia), 10);
                mes = parseInt(valor.substr(iDia + 1, iMes), 10);
                ano = parseInt(valor.substr(iDia + 1 + iMes + 1, iAno), 10);
                //arreglo para los meses bisiestos
                if (esBisiesto(ano))
                  maxdia[2] = 29;
                else
                  maxdia[2] = 28;
                if (dia >= 1 && mes >= 1 && mes <= 12 && dia <= maxdia[mes] && ano >= 0)
                  error = '';
                else
                  error = 'rango de dias/mes/a\ufffdo invalido';
              } else
                error = 'la longitud de la fecha es incorrecta';
            } else
              error = 'el a\ufffdo es de ' + auxErr + ' d\ufffdgitos';
          } else
            error = 'no se ha encontrado la barra separadora del a\ufffdo';
        } else
          error = 'el mes es de 1 o 2 d\ufffdgitos';
      } else
        error = 'no se ha encontrado la barra separadora del mes';
    } else
      error = 'el dia es de 1 o 2 d\ufffdgitos';
    if (error != '')
      error = 'Campo ' + campo + ' = ' + valor + ', ' + error + '\n';
    else {
    }
    return error;
  }
  //devuelve una cadena de error si hay error
  function compruebaHora(campo, valor) {
    var error;
    var iHora, iMinutos;
    var hora, minutos;
    error = '';
    iHora = cuantosNumeros(valor, 0);
    if (iHora == 1 || iHora == 2) {
      if (esDosPuntos(valor, iHora)) {
        iMinutos = cuantosNumeros(valor, iHora + 1);
        if (iMinutos == 2) {
          if (iHora + 1 + iMinutos == valor.length) {
            hora = parseInt(valor.substr(0, iHora), 10);
            minutos = parseInt(valor.substr(iHora + 1, valor.length), 10);
            if (hora >= 0 && hora <= 23 && minutos >= 0 && minutos <= 59)
              error = '';
            else
              error = 'rango de hora:minutos invalido';
          } else
            error = 'la longitud de la hora introducida es incorrecta';
        } else
          error = 'Los minutos son de 2 d\ufffdgitos';
      } else
        error = 'No se ha encontrado los dos puntos que separan la hora de los minutos';
    } else
      error = 'La hora es de 1 o 2 d\ufffdgitos';
    if (error != '')
      error = 'Campo ' + campo + ' = ' + valor + ', ' + error + '\n';
    return error;
  }
  //devuelve una cadena de error si hay error
  function compruebaMoneda(campo, valor) {
    //obligo a que los numeros introducidos tengan el formato: +/-#.###.###,##
    var error = '';
    var reg = /^-?\d{1,3}(\.?\d{3})*(\,\d{1,2})?$/;
    if (!reg.test(valor))
      error = 'el campo ' + campo + ' = ' + valor + ' no tiene formato moneda\n';
    return error;  /*	var iDigitos = 0;
  	var pos = 0;
  	var letra = "";
  	var max = valor.length;
  	var primeraLetra = cogeLetra(valor,pos);
  	var posComa = 0;
  	var i = 0;
  
  	//si la primera letra es + o - avanzo la posicion
  	if ((primeraLetra=="+") || (primeraLetra=="-"))	pos++;
  
  	//busco si hay una , (de momento debe ser obligatoria)
  	posComa = valor.indexOf(",");
  	if (posComa!=-1){
  		//ahora recorro la cadena desde posComa hasta pos y voy contando los # al 3 espero un .
  		iDigitos = 0;
  		i = posComa-1;	//salto la ,
  		//cuento los decimales a partir de la coma. deben ser unicamente 2
  		if (cuantosNumeros(valor,posComa+1)!=2) error = "el campo "+campo+ " = "+valor+" no es un numero. Debe haber dos decimales\n";
  		while ((i>=pos) && (error=="")){
  			letra = cogeLetra(valor,i);
  			if (iDigitos<3){
  				if ((letra>="0") && (letra<="9")) iDigitos++;
  				else error = "el campo "+campo+ " = "+valor+" no es un numero. Esperaba un digito.\n";
  			} else {
  				if (letra==".") iDigitos=0;
  				else error = "el campo "+campo+ " = "+valor+" no es un numero. Esperaba un .\n";
  			}
  			i--;
  		}
  	} else error = "el campo "+campo+ " = "+valor+" no es un numero. El formato es +/-#.###.###,##\n";
  
  //	antigua funcion, lastima que no sea lo que ellos quieren
  //	if (isNaN(parseFloat(valor))) error = "el campo "+campo+ " = "+valor+" no es un numero\n";
  	return error;*/
  }
  //devuelve una cadena de error si hay error
  //opcionalmente se puede pasar el formato del numero como tercer parametro
  //el formato es: N, N(numeroDigitos), N(numeroDigitos,numeroDecimales)
  function compruebaNumero(campo, valor) {
    var error = '';
    var formato = '';
    var numDigitos = -9999;
    var numDecimales = -9999;
    var reg = /^-?\d{1,3}(\.?\d{3})*(\,\d+)?$/;
    /*	if (compruebaNumero.arguments.length>2){
  		formato = compruebaNumero.arguments[2];
  		var re = /\(|\)|,/;
  		var arr = formato.split(re);
  		var i = 0;
  		var txt = "";
  		for(i=0;i<arr.length;i++){
  			
  			txt = txt + i + " = "+arr[i]+"\n";
  		}
  		alert(txt);
  		//	var reg = /^\d{1,3}(\.+\d{3})*(\,\d{2})+$/;
  	} else ;
  */
    if (!reg.test(valor))
      error = 'el campo ' + campo + ' = ' + valor + ' no es un numero valido\n';
    return error;
  }
  //devuelve una cadena de error si hay error
  //opcionalmente se puede pasar el formato del numero como tercer parametro
  //el formato es: I, Ix donde x es el n�mero de d�gitos m�ximo permitido
  function compruebaEntero(campo, valor, numDigitos) {
    var error = '';
    if (numDigitos != '') {
      var reg = new RegExp('^-?\\d{1,' + numDigitos + '}$');
    } else {
      var reg = /^-?\d+$/;
    }
    if (!reg.test(valor)) {
      if (numDigitos != '') {
        error = 'el campo ' + campo + ' = ' + valor + ' no es un entero v\ufffdlido o supera la longitud m\ufffdxima de ' + numDigitos + ' digitos\n';
      } else {
        error = 'el campo ' + campo + ' = ' + valor + ' no es un entero v\ufffdlido\n';
      }
    }
    return error;
  }
  //devuelve una cadena de error si el valor no es un codigo CCC correcto
  function compruebaCCC(campo, valor) {
    var c1;
    var c2;
    var c3;
    var c4;
    var m1, m2, d1, d2, digito1, digito2;
    var error = '';
    var ccc = valor;
    //comprobacion de 20 digitos
    if (ccc.length != 20) {
      error = 'el campo ' + campo + ' no tiene 20 digitos:' + valor + '\n';
      return error;
    }
    c1 = ccc.substr(0, 4);
    c2 = ccc.substr(4, 4);
    c3 = ccc.substr(8, 2);
    c4 = ccc.substr(10, 10);
    //	alert("c1="+c1+" c2="+c2+" c3="+c3+" c4="+c4);
    m1 = 4 * c1.substr(0, 1) + 8 * c1.substr(1, 1) + 5 * c1.substr(2, 1) + 10 * c1.substr(3, 1) + 9 * c2.substr(0, 1) + 7 * c2.substr(1, 1) + 3 * c2.substr(2, 1) + 6 * c2.substr(3, 1);
    m2 = 1 * c4.substr(0, 1) + 2 * c4.substr(1, 1) + 4 * c4.substr(2, 1) + 8 * c4.substr(3, 1) + 5 * c4.substr(4, 1) + 10 * c4.substr(5, 1) + 9 * c4.substr(6, 1) + 7 * c4.substr(7, 1) + 3 * c4.substr(8, 1) + 6 * c4.substr(9, 1);
    //	alert("m1="+m1+" m2="+m2);
    d1 = 11 - m1 % 11;
    d2 = 11 - m2 % 11;
    if (d1 == 10)
      d1 = 1;
    if (d1 == 11)
      d1 = 0;
    if (d2 == 10)
      d2 = 1;
    if (d2 == 11)
      d2 = 0;
    //	alert("d1="+d1+" d2="+d2);
    if (d1 != c3.substr(0, 1)) {
      error = 'el campo ' + campo + ' tiene el primer digito de control del CCC inv\ufffdlido\n';
      return error;
    }
    if (d2 != c3.substr(1, 1)) {
      error = 'el campo ' + campo + ' tiene el segundo digito de control del CCC inv\ufffdlido\n';
      return error;
    }
    return error;
  }
  //comprueba si es un NIF bien formado
  //modificado el 6-10-2008 para la nueva normativa
  function esNIF(campo, cadena) {
    var valor;
    var indice;
    var tabla;
    var cadena2;
    var letra;
    var esperado, actual;
    var error = '';
    var primeraLetra = cogeLetra(cadena, 0);
    //para el caso de NIEs que empiecen por X,Y o Z es lo mismo que un NIF que empieza por 0,1 o 2 respectivamente
    if (primeraLetra == 'X')
      cadena = '0' + cadena.substr(1);
    if (primeraLetra == 'Y')
      cadena = '1' + cadena.substr(1);
    if (primeraLetra == 'Z')
      cadena = '2' + cadena.substr(1);
    tabla = 'TRWAGMYFPDXBNJZSQVHLCKET';
    //TRWAGMYFPDXBNJZSQVHLCKET
    cadena2 = cadena.substr(0, 8);
    letra = cogeLetra(cadena2, 0);
    while (letra == '0') {
      cadena2 = cadena2.substr(1);
      letra = cogeLetra(cadena2, 0);
    }
    valor = parseInt(cadena2);
    indice = valor % 23;
    esperado = cogeLetra(tabla, indice);
    actual = cogeLetra(cadena, 8);
    //alert('cadena:'+cadena+' cadena(0,8):'+cadena.substr(0,8)+' valor:'+valor+' indice:'+indice+' esperado:'+esperado+' actual:'+actual);
    if (esperado != actual)
      error = 'Campo ' + campo + ' = ' + valor + ' el NIF es incorrecto, deberia acabar en ' + esperado + '\n';
    return error;
  }
  //comprueba si es un CIF bien formado
  //modificado el 6-10-2008 para la nueva normativa
  function esCIF(campo, cadena) {
    var primeraLetra;
    var ultimaLetra;
    var suma;
    var suma2;
    var act;
    var i;
    var tabla;
    var error = '';
    //compruebo que la primera letra sea del tipo aceptado
    primeraLetra = cogeLetra(cadena, 0);
    ultimaLetra = cogeLetra(cadena, 8);
    tabla = 'ABCDEFGHJKLMNPQRSUVWXYZ';
    tablaEspecial = 'CKLMNPQRSW';
    if (tabla.indexOf(primeraLetra) == -1)
      error = 'la primera letra no es valida';
    else if (primeraLetra == 'X' || primeraLetra == 'Y' || primeraLetra == 'Z') {
      //para el caso de CIF que empiecen por X,Y o Z es lo mismo que un NIF que empieza por 0,1 o 2 Respectivamente
      error = error + esNIF(campo, cadena);
    } else {
      //para el resto de casos...realizo la suma de pares e impares y todo eso.
      suma = valorLetra(cadena, 2) + valorLetra(cadena, 4) + valorLetra(cadena, 6);
      for (i = 0; i < 4; i++) {
        act = valorLetra(cadena, 2 * i + 1) * 2;
        if (act >= 10)
          act = act - 9;
        //-10 +1
        suma = suma + act;
      }
      suma = 10 - suma % 10;
      /*
  		suma = valorLetra(cadena,2)+valorLetra(cadena,4)+valorLetra(cadena,6);
  		suma2 = 0;
  		for(i=0;i<4;i++){
  			act = valorLetra(cadena,2*i+1)*2;
  			suma2 = suma2 + act;
  			if (suma2>=10) suma2 = suma2 - 9;				//-10 +1
  		}
  		if (suma2>=10) suma2 = suma2 - 9;				//-10 +1
  		suma = suma + suma2;
  		suma = 10 - (suma % 10);
  */
      //comprobacion del digito de control para varios casos
      if (tablaEspecial.indexOf(primeraLetra) != -1) {
        //caso de organismos autonomos
        if (ultimaLetra != String.fromCharCode(64 + suma))
          error = error + 'CIF incorrecto, deberia acabar en ' + String.fromCharCode(64 + suma);
      } else {
        //el resto de casos
        suma = suma % 10;
        //alert(ultimaLetra+" "+suma);
        if (ultimaLetra != suma.toString())
          error = error + 'CIF incorrecto, deberia acabar en ' + suma;
      }
    }
    if (error != '')
      error = 'Campo ' + campo + ' = ' + cadena + ', ' + error + '\n';
    return error;
  }
  //devuelve una cadena de error
  function compruebaCIFNIF(campo, valor) {
    var error = '';
    //pasar a mayusculas y quitar los caracteres raros
    valor = valor.toUpperCase();
    //efren incorporando la validaci�n de pasaportes
    //si las tres primeras letras son PAS no comprueba y admite meter un texto plano sin comprobaci�n
    if (valor.substring(0, 3) == 'PAS') {
      return error;
    }
    //veo los casos mas basicos y desdoblo la funcion
    if (valor.length == 9) {
      if (esNumero(valor, 0) || cogeLetra(valor, 0) == 'X')
        error = error + esNIF(campo, valor);
      else
        error = error + esCIF(campo, valor);
    } else
      error = 'El campo ' + campo + ' = ' + valor + ' debe contener 9 d\ufffdgitos\n';
    return error;
  }
  //devuelve una cadena de error
  function compruebaCIF(campo, valor) {
    var error = '';
    //pasar a mayusculas y quitar los caracteres raros
    valor = valor.toUpperCase();
    //veo los casos mas basicos y desdoblo la funcion
    if (valor.length == 9)
      error = error + esCIF(campo, valor);
    else
      error = 'El campo ' + campo + ' = ' + valor + ' debe contener 9 d\ufffdgitos\n';
    return error;
  }
  //devuelve una cadena de error
  function compruebaNIF(campo, valor) {
    var error = '';
    //pasar a mayusculas y quitar los caracteres raros
    valor = valor.toUpperCase();
    //veo los casos mas basicos y desdoblo la funcion
    if (valor.length == 9)
      error = error + esNIF(campo, valor);
    else
      error = 'El campo ' + campo + ' = ' + valor + ' debe contener 9 d\ufffdgitos\n';
    return error;
  }
  //comprueba si una cadena de texto es un numero de la seguridad social valido
  //comprueba si una cadena de texto es un numero de la seguridad social valido
  function compruebaSS(campo, valor) {
    var error = '';
    var limpio = '';
    var ch = '';
    var i, aa, bb, cc, num1, num2, num3, num4, modulo, intermedio, lenbb;
    //El numero de la seguridad social debe entrarse con este formato:
    //28/1234567/40
    //o bien:
    //28/12345678/40
    //En funci�n de que se trate del numero de una empresa
    //o del numero de un trabajador.
    limpio = '';
    for (i = 0; i < valor.length; i++) {
      ch = valor.charAt(i);
      if (ch >= '0' && ch <= '9')
        limpio = limpio + ch;
    }
    //Si no tiene 11 � 12 digitos, no es v�lido
    var len = limpio.length;
    if (len == 11 || len == 12) {
      //1=aa
      //2=bb
      //3=cc
      aa = limpio.substring(0, 2);
      bb = limpio.substring(2, len - 2);
      cc = limpio.substring(len - 2, len);
      intermedio = '000000000000' + bb.toString();
      lenbb = bb.toString().length;
      num1 = aa + intermedio.substr(intermedio.length - 8);
      //Cdbl(Cstr(Cdbl(aa))+Format(Cstr(Cdbl(bb)),"00000000"))
      num2 = aa + intermedio.substr(intermedio.length - 7);
      //Cdbl(Cstr(Cdbl(aa))+Format(Cstr(Cdbl(bb)),"0000000"))
      num3 = aa + intermedio.substr(intermedio.length - 6);
      //Cdbl(Cstr(Cdbl(aa))+Format(Cstr(Cdbl(bb)),"000000"))
      num4 = aa + intermedio.substr(intermedio.length - 5);
      //Cdbl(Cstr(Cdbl(aa))+Format(Cstr(Cdbl(bb)),"00000"))
      if (intermedio.length - 8 < lenbb)
        num1 = cc + 1;
      if (intermedio.length - 7 < lenbb)
        num2 = cc + 1;
      if (intermedio.length - 6 < lenbb)
        num3 = cc + 1;
      if (intermedio.length - 5 < lenbb)
        num4 = cc + 1;
      if (num1 % 97 != cc && num2 % 97 != cc && num3 % 97 != cc && num4 % 97 != cc)
        error = 'El campo ' + campo + ' = ' + valor + ' no es un numero de la seguridad social valido';
    } else
      error = 'El campo ' + campo + ' = ' + valor + ' debe contener 11 o 12 digitos';
    return error;
  }
  //llama a una funcion en la pagina del usuario que trata la validacion
  function compruebaValidacion(funcion, opcion, formato) {
    if (modo_validacion_general == 'completo') {
      //deberia comprobar si existe una funcion con ese nombre pero...ahi va eso.
      return eval(funcion + '()');
    } else
      return '';
  }
  //devuelve una cadena de error
  function compruebaMultivaluado(campo, valor, formato) {
    var str = '';
    var j = 0;
    var k = 0;
    //quito los caracteres de la izquierda hasta el siguiente a los :
    while (formato != '' && formato.substr(0, 1) != ':')
      formato = formato.substr(1);
    formato = formato.substr(1);
    var sizeFormato = formato.length;
    var valores = '';
    while (k != -1) {
      k = formato.indexOf('#', j);
      //separador usado para el formato multivaluado
      if (k != -1) {
        str = formato.substr(j, k - j);
        j = k + 1;
      } else if (j < sizeFormato) {
        str = formato.substr(j);
      }
      if (valor == str)
        return '';
      if (valores == '')
        valores = str;
      else
        valores = valores + ', ' + str;
    }
    return 'Campo ' + campo + ' = ' + valor + ', se esperaba: ' + valores + '\n';
  }
  //comprueba una lista de valores segun su tipo
  function compruebaListaCampos(campo, valor, formato) {
    var error = '';
    var str = '';
    var sizeValor = valor.length;
    var j = 0;
    var k = 0;
    var primeraLetraFormato = formato.substr(0, 1);
    var restoFormato = formato.substr(1);
    var indiceSeparadorLista = formato.indexOf('_', 1);
    var etiquetaSeparadorLista = formato.substr(indiceSeparadorLista + 1);
    var separadorLista = String.fromCharCode(13, 10);
    //Retorno de carro y alimentaci�n de l�nea por defecto como separador de listas.
    var formatoSinSeparadorLista = formato.substr(0, indiceSeparadorLista);
    if (etiquetaSeparadorLista != -1) {
      switch (etiquetaSeparadorLista) {
      case 'B':
        //Coma ","
        separadorLista = ',';
        break;
      case 'C':
        //Coma ";"
        separadorLista = ';';
        break;
      case 'D':
        //Coma "|"
        separadorLista = '|';
        break;
      case 'E':
        //Coma "#"
        separadorLista = '#';
        break;
      case 'F':
        //Coma "~"
        separadorLista = '~';
        break;
      case 'A':
        separadorLista = String.fromCharCode(13, 10);
        //Retorno de carro y alimentaci�n de l�nea por defecto como separador de listas.
        break;
      }
    }
    //alert("indiceSeparadorLista = " + indiceSeparadorLista);
    //alert("etiquetaSeparadorLista = " + etiquetaSeparadorLista);
    //alert("separadorLista = " + separadorLista);
    //alert("formatoSinSeparadorLista = " + formatoSinSeparadorLista);
    while (k != -1) {
      k = valor.indexOf(separadorLista, j);
      if (k != -1) {
        str = valor.substr(j, k - j);
        j = k + separadorLista.length;
      } else if (j < sizeValor) {
        str = valor.substr(j);
      }
      if (primeraLetraFormato == '')
        error = error;
      else if (primeraLetraFormato == 'T')
        error = error;
      else if (primeraLetraFormato == 'F')
        error = error + compruebaFecha(campo, str, formatoSinSeparadorLista);
      else if (primeraLetraFormato == 'N')
        error = error + compruebaNumero(campo, str, formatoSinSeparadorLista);
      else if (formatoSinSeparadorLista == 'EMAIL')
        error = error + compruebaEmail(campo, str, formatoSinSeparadorLista);
      else if (primeraLetraFormato == 'E')
        error = error + compruebaMoneda(campo, str);
      else if (formatoSinSeparadorLista == 'SS')
        error = error + compruebaSS(campo, str);
      else if (primeraLetraFormato == 'C') {
        if (restoFormato == 'N')
          error = error + compruebaNIF(campo, str);
        else if (restoFormato == 'C')
          error = error + compruebaCIF(campo, str);
        else
          error = error + compruebaCIFNIF(campo, str);
      } else if (primeraLetraFormato == 'M')
        error = error + compruebaMultivaluado(campo, str, restoFormato);
      else
        error = error + 'Formato inv\ufffdlido: ' + campo + ' = ' + valor + ', formato=' + formato + '\n';
    }
    return error;
  }
  function compruebaCampo(campo, valor, opcion, formato) {
    var str = '';
    var primeraLetraFormato = formato.substr(0, 1);
    var restoFormato = formato.substr(1);
    //	alert("Comprueba "+campo+" = "+valor+" "+opcion+" "+formato);
    if (valor == '') {
      //si es opcional no comprueba nada
      if (modo_validacion_general == 'completo' && (opcion == 'O' || opcion == 'OL'))
        str = str + 'Campo ' + campo + ': no puede ser vac\ufffdo\n';
    } else {
      //dependiendo del formato llamo a una funcion u otra
      if (primeraLetraFormato == '')
        str = str;
      else if (primeraLetraFormato == 'T')
        str = str;
      else if (primeraLetraFormato == 'F')
        str = str + compruebaFecha(campo, valor, formato);
      else if (primeraLetraFormato == 'N')
        str = str + compruebaNumero(campo, valor, formato);
      else if (formato == 'EMAIL')
        str = str + compruebaEmail(campo, valor);
      else if (primeraLetraFormato == 'E')
        str = str + compruebaMoneda(campo, valor);
      else if (formato == 'CCC')
        str = str + compruebaCCC(campo, valor);
      else if (formato == 'SS')
        str = str + compruebaSS(campo, valor);
      else if (primeraLetraFormato == 'C') {
        if (restoFormato == 'N')
          str = str + compruebaNIF(campo, valor);
        else if (restoFormato == 'C')
          str = str + compruebaCIF(campo, valor);
        else
          str = str + compruebaCIFNIF(campo, valor);
      } else if (primeraLetraFormato == 'M')
        str = str + compruebaMultivaluado(campo, valor, formato);
      else if (primeraLetraFormato == 'L')
        str = str + compruebaListaCampos(campo, valor, restoFormato);
      else if (primeraLetraFormato == 'H')
        str = str + compruebaHora(campo, valor);
      else if (primeraLetraFormato == 'I')
        str = str + compruebaEntero(campo, valor, restoFormato);
      else
        str = str + 'Formato inv\ufffdlido: ' + campo + ' = ' + valor + ', opcion=' + opcion + ' formato=' + formato + '\n';
    }
    return str;
  }
  //devuelve true si hay error
  //los parametros de entrada son tripletas de la forma: campo, opcion, formato
  function compruebaCampos() {
    var args = compruebaCampos.arguments.length;
    var i;
    var campo, opcion, formato;
    var str = '';
    var obj;
    var valor;
    var error;
    for (i = 0; i < args; i += 3) {
      campo = compruebaCampos.arguments[i];
      opcion = compruebaCampos.arguments[i + 1];
      formato = compruebaCampos.arguments[i + 2];
      //AGM88X 10-10-2005: funcion especial de validacion. Formato V
      if (formato.substr(0, 1) == 'V') {
        str = str + compruebaValidacion(campo, opcion, formato);
      } else {
        //prosigo con el tratamiento normal de campos
        obj = document.all[campo];
        if (obj) {
          //determino el valor del objeto en sus diversas maneras
          if (obj.length) {
            //AGM88X 28-4-2006: arreglo 'definitivamente' el valor para los select
            if (obj.selectedIndex != null && obj.selectedIndex >= 0)
              valor = obj.options[obj.selectedIndex].text;
            else {
              //el nombre esta repetido creo un string y recooro el array
              valor = '';
              for (j = 0; j < obj.length; j++) {
                //obtengo el valor de cada objeto individual
                var objAct = obj[j];
                var valorAct = '';
                if (objAct.selectedIndex != null && objAct.selectedIndex >= 0)
                  valorAct = objAct.options[objAct.selectedIndex].text;
                else
                  valorAct = objAct.value;
                //y lo inserto en el valor final separado por /n
                if (valor == '')
                  valor = valorAct;
                else
                  valor = valor + String.fromCharCode(13, 10) + valorAct;
              }
            }
          } else {
            if (obj.selectedIndex != null && obj.selectedIndex >= 0)
              valor = obj.options[obj.selectedIndex].text;
            else
              valor = obj.value;
          }
          str = str + compruebaCampo(campo, valor, opcion, formato);
        } else
          str = str + 'Falta campo: ' + campo + '\n ' + formato;
      }
    }
    if (str != '') {
      alert('Errores en compruebaCampos:\n' + str);
      return true;
    } else
      return false;
  }
  //funcion usada cuando se dispone de los tres campos columna: campo, opcion,formato
  function compruebaCamposExtendido(campos, opciones, formatos) {
    var error = false;
    var parametros = '';
    //AGM88X 11-10-2005 A�ado el nuevo formato de lista de campos con "~"
    if (typeof campos == 'object' && campos[0]) {
      //caso de la nueva implementacion en arrays
      var lc = campos;
      var lo = opciones;
      var lf = formatos;
      for (var i = 0; i < lc.length; i++) {
        if (parametros != '')
          parametros = parametros + ',';
        parametros = parametros + '\'' + lc[i] + '\',\'' + lo[i] + '\',\'' + lf[i] + '\'';
      }
      error = eval('compruebaCampos(' + parametros + ')');
      return error;
    } else {
      //resto de casos implementados en strings con dos separadores distintos
      if (campos.indexOf('~') > 0) {
        //caso de separador con ~
        var lc = campos.split('~');
        var lo = opciones.split('~');
        var lf = formatos.split('~');
        for (var i = 0; i < lc.length; i++) {
          if (parametros != '')
            parametros = parametros + ',';
          parametros = parametros + '\'' + lc[i] + '\',\'' + lo[i] + '\',\'' + lf[i] + '\'';
        }
        error = eval('compruebaCampos(' + parametros + ')');
        return error;
      } else {
        //sigo con la version antigua: separador con retorno de carro
        var str = '';
        var str2 = '';
        var str3 = '';
        var sizeCampos = campos.length;
        var sizeOpciones = opciones.length;
        var sizeFormatos = formatos.length;
        var j = 0;
        var k = 0;
        var j2 = 0;
        var k2 = 0;
        var j3 = 0;
        var k3 = 0;
        while (k != -1) {
          k = campos.indexOf(String.fromCharCode(13, 10), j);
          k2 = opciones.indexOf(String.fromCharCode(13, 10), j2);
          k3 = formatos.indexOf(String.fromCharCode(13, 10), j3);
          if (k != -1) {
            str = campos.substr(j, k - j);
            j = k + 2;
          } else if (j < sizeCampos) {
            str = campos.substr(j);
          }
          if (k2 != -1) {
            str2 = opciones.substr(j2, k2 - j2);
            j2 = k2 + 2;
          } else if (j2 < sizeOpciones) {
            str2 = opciones.substr(j2);
          }
          if (k3 != -1) {
            str3 = formatos.substr(j3, k3 - j3);
            j3 = k3 + 2;
          } else if (j3 < sizeFormatos) {
            str3 = formatos.substr(j3);
          }
          if (parametros == '')
            parametros = parametros + '\'' + str + '\',\'' + str2 + '\',\'' + str3 + '\'';
          else
            parametros = parametros + ',\'' + str + '\',\'' + str2 + '\',\'' + str3 + '\'';
        }
        error = eval('compruebaCampos(' + parametros + ')');
        return error;
      }
    }
  }
  function compruebaCamposDocumento() {
    //modo_validacion_general ="completo";
    modo_validacion_general = compruebaCamposDocumento.arguments.length > 0 ? compruebaCamposDocumento.arguments[0] : 'completo';
    var error = false;
    var campos = document.ListaCampos;
    var opciones = document.ListaOpcionCampos;
    var formatos = document.ListaFormatoCampos;
    if (campos && opciones && formatos) {
      error = compruebaCamposExtendido(campos, opciones, formatos);
    }
    //else alert("No existe campo opciones o formato.\nSe guardaran los datos sin chequeo!\nCampos="+campos+"\nopciones="+opciones+"\nFormatos="+formatos);
    return error;
  }
  function compruebaCamposDocumento2() {
    modo_validacion_general = compruebaCamposDocumento2.arguments.length > 0 ? compruebaCamposDocumento2.arguments[0] : 'completo';
    var error = false;
    var campos = document.ListaCampos;
    var opciones = document.ListaOpcionCampos;
    var formatos = document.ListaFormatoCampos;
    if (campos && opciones && formatos) {
      error = compruebaCamposExtendido(campos, opciones, formatos);
    }
    //else alert("No existe campo opciones o formato.\nSe guardaran los datos sin chequeo!\nCampos="+campos+"\nopciones="+opciones+"\nFormatos="+formatos);
    return error;
  }
  (function (exports) {
    // Array.prototype.map polyfill
    // code from https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/map
    if (!Array.prototype.map) {
      Array.prototype.map = function (fun) {
        'use strict';
        if (this === void 0 || this === null)
          throw new TypeError();
        var t = Object(this);
        var len = t.length >>> 0;
        if (typeof fun !== 'function')
          throw new TypeError();
        var res = new Array(len);
        var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
        for (var i = 0; i < len; i++) {
          // NOTE: Absolute correctness would demand Object.defineProperty
          //       be used.  But this method is fairly new, and failure is
          //       possible only if Object.prototype or Array.prototype
          //       has a property |i| (very unlikely), so use a less-correct
          //       but more portable alternative.
          if (i in t)
            res[i] = fun.call(thisArg, t[i], i, t);
        }
        return res;
      };
    }
    var A = 'A'.charCodeAt(0), Z = 'Z'.charCodeAt(0);
    /**
       * Prepare an IBAN for mod 97 computation by moving the first 4 chars to the end and transforming the letters to
       * numbers (A = 10, B = 11, ..., Z = 35), as specified in ISO13616.
       *
       * @param {string} iban the IBAN
       * @returns {string} the prepared IBAN
       */
    function iso13616Prepare(iban) {
      iban = iban.toUpperCase();
      iban = iban.substr(4) + iban.substr(0, 4);
      return iban.split('').map(function (n) {
        var code = n.charCodeAt(0);
        if (code >= A && code <= Z) {
          // A = 10, B = 11, ... Z = 35
          return code - A + 10;
        } else {
          return n;
        }
      }).join('');
    }
    /**
       * Calculates the MOD 97 10 of the passed IBAN as specified in ISO7064.
       *
       * @param iban
       * @returns {number}
       */
    function iso7064Mod97_10(iban) {
      var remainder = iban, block;
      while (remainder.length > 2) {
        block = remainder.slice(0, 9);
        remainder = parseInt(block, 10) % 97 + remainder.slice(block.length);
      }
      return parseInt(remainder, 10) % 97;
    }
    /**
       * Parse the BBAN structure used to configure each IBAN Specification and returns a matching regular expression.
       * A structure is composed of blocks of 3 characters (one letter and 2 digits). Each block represents
       * a logical group in the typical representation of the BBAN. For each group, the letter indicates which characters
       * are allowed in this group and the following 2-digits number tells the length of the group.
       *
       * @param {string} structure the structure to parse
       * @returns {RegExp}
       */
    function parseStructure(structure) {
      // split in blocks of 3 chars
      var regex = structure.match(/(.{3})/g).map(function (block) {
          // parse each structure block (1-char + 2-digits)
          var format, pattern = block.slice(0, 1), repeats = parseInt(block.slice(1), 10);
          switch (pattern) {
          case 'A':
            format = '0-9A-Za-z';
            break;
          case 'B':
            format = '0-9A-Z';
            break;
          case 'C':
            format = 'A-Za-z';
            break;
          case 'F':
            format = '0-9';
            break;
          case 'L':
            format = 'a-z';
            break;
          case 'U':
            format = 'A-Z';
            break;
          case 'W':
            format = '0-9a-z';
            break;
          }
          return '([' + format + ']{' + repeats + '})';
        });
      return new RegExp('^' + regex.join('') + '$');
    }
    /**
       * Create a new Specification for a valid IBAN number.
       *
       * @param countryCode the code of the country
       * @param length the length of the IBAN
       * @param structure the structure of the undernying BBAN (for validation and formatting)
       * @param example an example valid IBAN
       * @constructor
       */
    function Specification(countryCode, length, structure, example) {
      this.countryCode = countryCode;
      this.length = length;
      this.structure = structure;
      this.example = example;
    }
    /**
       * Lazy-loaded regex (parse the structure and construct the regular expression the first time we need it for validation)
       */
    Specification.prototype._regex = function () {
      return this._cachedRegex || (this._cachedRegex = parseStructure(this.structure));
    };
    /**
       * Check if the passed iban is valid according to this specification.
       *
       * @param {String} iban the iban to validate
       * @returns {boolean} true if valid, false otherwise
       */
    Specification.prototype.isValid = function (iban) {
      return this.length == iban.length && this.countryCode === iban.slice(0, 2) && this._regex().test(iban.slice(4)) && iso7064Mod97_10(iso13616Prepare(iban)) == 1;
    };
    /**
       * Convert the passed IBAN to a country-specific BBAN.
       *
       * @param iban the IBAN to convert
       * @param separator the separator to use between BBAN blocks
       * @returns {string} the BBAN
       */
    Specification.prototype.toBBAN = function (iban, separator) {
      return this._regex().exec(iban.slice(4)).slice(1).join(separator);
    };
    /**
       * Convert the passed BBAN to an IBAN for this country specification.
       * Please note that <i>"generation of the IBAN shall be the exclusive responsibility of the bank/branch servicing the account"</i>.
       * This method implements the preferred algorithm described in http://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_IBAN_check_digits
       *
       * @param bban the BBAN to convert to IBAN
       * @returns {string} the IBAN
       */
    Specification.prototype.fromBBAN = function (bban) {
      if (!this.isValidBBAN(bban)) {
        throw new Error('Invalid BBAN');
      }
      var remainder = iso7064Mod97_10(iso13616Prepare(this.countryCode + '00' + bban)), checkDigit = ('0' + (98 - remainder)).slice(-2);
      return this.countryCode + checkDigit + bban;
    };
    /**
       * Check of the passed BBAN is valid.
       * This function only checks the format of the BBAN (length and matching the letetr/number specs) but does not
       * verify the check digit.
       *
       * @param bban the BBAN to validate
       * @returns {boolean} true if the passed bban is a valid BBAN according to this specification, false otherwise
       */
    Specification.prototype.isValidBBAN = function (bban) {
      return this.length - 4 == bban.length && this._regex().test(bban);
    };
    var countries = {};
    function addSpecification(IBAN) {
      countries[IBAN.countryCode] = IBAN;
    }
    addSpecification(new Specification('AD', 24, 'F04F04A12', 'AD1200012030200359100100'));
    addSpecification(new Specification('AE', 23, 'F03F16', 'AE070331234567890123456'));
    addSpecification(new Specification('AL', 28, 'F08A16', 'AL47212110090000000235698741'));
    addSpecification(new Specification('AT', 20, 'F05F11', 'AT611904300234573201'));
    addSpecification(new Specification('AZ', 28, 'U04A20', 'AZ21NABZ00000000137010001944'));
    addSpecification(new Specification('BA', 20, 'F03F03F08F02', 'BA391290079401028494'));
    addSpecification(new Specification('BE', 16, 'F03F07F02', 'BE68539007547034'));
    addSpecification(new Specification('BG', 22, 'U04F04F02A08', 'BG80BNBG96611020345678'));
    addSpecification(new Specification('BH', 22, 'U04A14', 'BH67BMAG00001299123456'));
    addSpecification(new Specification('BR', 29, 'F08F05F10U01A01', 'BR9700360305000010009795493P1'));
    addSpecification(new Specification('CH', 21, 'F05A12', 'CH9300762011623852957'));
    addSpecification(new Specification('CR', 21, 'F03F14', 'CR0515202001026284066'));
    addSpecification(new Specification('CY', 28, 'F03F05A16', 'CY17002001280000001200527600'));
    addSpecification(new Specification('CZ', 24, 'F04F06F10', 'CZ6508000000192000145399'));
    addSpecification(new Specification('DE', 22, 'F08F10', 'DE89370400440532013000'));
    addSpecification(new Specification('DK', 18, 'F04F09F01', 'DK5000400440116243'));
    addSpecification(new Specification('DO', 28, 'U04F20', 'DO28BAGR00000001212453611324'));
    addSpecification(new Specification('EE', 20, 'F02F02F11F01', 'EE382200221020145685'));
    addSpecification(new Specification('ES', 24, 'F04F04F01F01F10', 'ES9121000418450200051332'));
    addSpecification(new Specification('FI', 18, 'F06F07F01', 'FI2112345600000785'));
    addSpecification(new Specification('FO', 18, 'F04F09F01', 'FO6264600001631634'));
    addSpecification(new Specification('FR', 27, 'F05F05A11F02', 'FR1420041010050500013M02606'));
    addSpecification(new Specification('GB', 22, 'U04F06F08', 'GB29NWBK60161331926819'));
    addSpecification(new Specification('GE', 22, 'U02F16', 'GE29NB0000000101904917'));
    addSpecification(new Specification('GI', 23, 'U04A15', 'GI75NWBK000000007099453'));
    addSpecification(new Specification('GL', 18, 'F04F09F01', 'GL8964710001000206'));
    addSpecification(new Specification('GR', 27, 'F03F04A16', 'GR1601101250000000012300695'));
    addSpecification(new Specification('GT', 28, 'A04A20', 'GT82TRAJ01020000001210029690'));
    addSpecification(new Specification('HR', 21, 'F07F10', 'HR1210010051863000160'));
    addSpecification(new Specification('HU', 28, 'F03F04F01F15F01', 'HU42117730161111101800000000'));
    addSpecification(new Specification('IE', 22, 'U04F06F08', 'IE29AIBK93115212345678'));
    addSpecification(new Specification('IL', 23, 'F03F03F13', 'IL620108000000099999999'));
    addSpecification(new Specification('IS', 26, 'F04F02F06F10', 'IS140159260076545510730339'));
    addSpecification(new Specification('IT', 27, 'U01F05F05A12', 'IT60X0542811101000000123456'));
    addSpecification(new Specification('KW', 30, 'U04A22', 'KW81CBKU0000000000001234560101'));
    addSpecification(new Specification('KZ', 20, 'F03A13', 'KZ86125KZT5004100100'));
    addSpecification(new Specification('LB', 28, 'F04A20', 'LB62099900000001001901229114'));
    addSpecification(new Specification('LI', 21, 'F05A12', 'LI21088100002324013AA'));
    addSpecification(new Specification('LT', 20, 'F05F11', 'LT121000011101001000'));
    addSpecification(new Specification('LU', 20, 'F03A13', 'LU280019400644750000'));
    addSpecification(new Specification('LV', 21, 'U04A13', 'LV80BANK0000435195001'));
    addSpecification(new Specification('MC', 27, 'F05F05A11F02', 'MC5811222000010123456789030'));
    addSpecification(new Specification('MD', 24, 'U02F18', 'MD24AG000225100013104168'));
    addSpecification(new Specification('ME', 22, 'F03F13F02', 'ME25505000012345678951'));
    addSpecification(new Specification('MK', 19, 'F03A10F02', 'MK07250120000058984'));
    addSpecification(new Specification('MR', 27, 'F05F05F11F02', 'MR1300020001010000123456753'));
    addSpecification(new Specification('MT', 31, 'U04F05A18', 'MT84MALT011000012345MTLCAST001S'));
    addSpecification(new Specification('MU', 30, 'U04F02F02F12F03U03', 'MU17BOMM0101101030300200000MUR'));
    addSpecification(new Specification('NL', 18, 'U04F10', 'NL91ABNA0417164300'));
    addSpecification(new Specification('NO', 15, 'F04F06F01', 'NO9386011117947'));
    addSpecification(new Specification('PK', 24, 'U04A16', 'PK36SCBL0000001123456702'));
    addSpecification(new Specification('PL', 28, 'F08F16', 'PL61109010140000071219812874'));
    addSpecification(new Specification('PS', 29, 'U04A21', 'PS92PALS000000000400123456702'));
    addSpecification(new Specification('PT', 25, 'F04F04F11F02', 'PT50000201231234567890154'));
    addSpecification(new Specification('RO', 24, 'U04A16', 'RO49AAAA1B31007593840000'));
    addSpecification(new Specification('RS', 22, 'F03F13F02', 'RS35260005601001611379'));
    addSpecification(new Specification('SA', 24, 'F02A18', 'SA0380000000608010167519'));
    addSpecification(new Specification('SE', 24, 'F03F16F01', 'SE4550000000058398257466'));
    addSpecification(new Specification('SI', 19, 'F05F08F02', 'SI56263300012039086'));
    addSpecification(new Specification('SK', 24, 'F04F06F10', 'SK3112000000198742637541'));
    addSpecification(new Specification('SM', 27, 'U01F05F05A12', 'SM86U0322509800000000270100'));
    addSpecification(new Specification('TN', 24, 'F02F03F13F02', 'TN5910006035183598478831'));
    addSpecification(new Specification('TR', 26, 'F05A01A16', 'TR330006100519786457841326'));
    addSpecification(new Specification('VG', 24, 'U04F16', 'VG96VPVG0000012345678901'));
    var NON_ALPHANUM = /[^a-zA-Z0-9]/g, EVERY_FOUR_CHARS = /(.{4})(?!$)/g;
    /**
       * Utility function to check if a variable is a String.
       *
       * @param v
       * @returns {boolean} true if the passed variable is a String, false otherwise.
       */
    function isString(v) {
      return typeof v == 'string' || v instanceof String;
    }
    /**
       * Check if an IBAN is valid.
       *
       * @param {String} iban the IBAN to validate.
       * @returns {boolean} true if the passed IBAN is valid, false otherwise
       */
    exports.isValid = function (iban) {
      if (!isString(iban)) {
        return false;
      }
      iban = this.electronicFormat(iban);
      var countryStructure = countries[iban.slice(0, 2)];
      return !!countryStructure && countryStructure.isValid(iban);
    };
    /**
       * Convert an IBAN to a BBAN.
       *
       * @param iban
       * @param {String} [separator] the separator to use between the blocks of the BBAN, defaults to ' '
       * @returns {string|*}
       */
    exports.toBBAN = function (iban, separator) {
      if (typeof separator == 'undefined') {
        separator = ' ';
      }
      iban = this.electronicFormat(iban);
      var countryStructure = countries[iban.slice(0, 2)];
      if (!countryStructure) {
        throw new Error('No country with code ' + iban.slice(0, 2));
      }
      return countryStructure.toBBAN(iban, separator);
    };
    /**
       * Convert the passed BBAN to an IBAN for this country specification.
       * Please note that <i>"generation of the IBAN shall be the exclusive responsibility of the bank/branch servicing the account"</i>.
       * This method implements the preferred algorithm described in http://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_IBAN_check_digits
       *
       * @param countryCode the country of the BBAN
       * @param bban the BBAN to convert to IBAN
       * @returns {string} the IBAN
       */
    exports.fromBBAN = function (countryCode, bban) {
      var countryStructure = countries[countryCode];
      if (!countryStructure) {
        throw new Error('No country with code ' + countryCode);
      }
      return countryStructure.fromBBAN(this.electronicFormat(bban));
    };
    /**
       * Check the validity of the passed BBAN.
       *
       * @param countryCode the country of the BBAN
       * @param bban the BBAN to check the validity of
       */
    exports.isValidBBAN = function (countryCode, bban) {
      if (!isString(bban)) {
        return false;
      }
      var countryStructure = countries[countryCode];
      return countryStructure && countryStructure.isValidBBAN(this.electronicFormat(bban));
    };
    /**
       *
       * @param iban
       * @param separator
       * @returns {string}
       */
    exports.printFormat = function (iban, separator) {
      if (typeof separator == 'undefined') {
        separator = ' ';
      }
      return this.electronicFormat(iban).replace(EVERY_FOUR_CHARS, '$1' + separator);
    };
    /**
       *
       * @param iban
       * @returns {string}
       */
    exports.electronicFormat = function (iban) {
      return iban.replace(NON_ALPHANUM, '').toUpperCase();
    };
    /**
       * An object containing all the known IBAN specifications.
       */
    exports.countries = countries;
  }(typeof exports == 'undefined' ? this.IBAN = {} : exports));
  //***********************************************************************************************
  //*** COMPROBACIÓN CIF
  //*** return:
  //*** 1 = NIF ok
  //*** 2 = CIF ok
  //*** 3 = NIE ok
  //*** -1 = NIF error
  //*** -2 = CIF error
  //*** -3 = NIE error
  //*** 0 = ??? error
  //***********************************************************************************************
  function valida_nif_cif_nie(a) {
    var temp = a.toUpperCase();
    var cadenadni = 'TRWAGMYFPDXBNJZSQVHLCKE';
    if (temp !== '') {
      //si no tiene un formato valido devuelve error
      if (!/^[A-Z]{1}[0-9]{7}[A-Z0-9]{1}$/.test(temp) && !/^[T]{1}[A-Z0-9]{8}$/.test(temp) && !/^[0-9]{8}[A-Z]{1}$/.test(temp)) {
        return 0;
      }
      //comprobacion de NIFs estandar
      if (/^[0-9]{8}[A-Z]{1}$/.test(temp)) {
        posicion = a.substring(8, 0) % 23;
        letra = cadenadni.charAt(posicion);
        var letradni = temp.charAt(8);
        if (letra == letradni) {
          return 1;
        } else {
          return -1;
        }
      }
      //algoritmo para comprobacion de codigos tipo CIF
      suma = parseInt(a[2]) + parseInt(a[4]) + parseInt(a[6]);
      for (i = 1; i < 8; i += 2) {
        temp1 = 2 * parseInt(a[i]);
        temp1 += '';
        temp1 = temp1.substring(0, 1);
        temp2 = 2 * parseInt(a[i]);
        temp2 += '';
        temp2 = temp2.substring(1, 2);
        if (temp2 == '') {
          temp2 = '0';
        }
        suma += parseInt(temp1) + parseInt(temp2);
      }
      suma += '';
      n = 10 - parseInt(suma.substring(suma.length - 1, suma.length));
      //comprobacion de NIFs especiales (se calculan como CIFs)
      if (/^[KLM]{1}/.test(temp)) {
        if (a[8] == String.fromCharCode(64 + n)) {
          //console.log("NIF 1");
          return 1;
        } else {
          //console.log("NIF -1");
          return -1;
        }
      }
      //comprobacion de CIFs
      if (/^[ABCDEFGHJNPQRSUVW]{1}/.test(temp)) {
        temp = n + '';
        if (a[8] == String.fromCharCode(64 + n) || a[8] == parseInt(temp.substring(temp.length - 1, temp.length))) {
          return 2;
        } else {
          return -2;
        }
      }
      //comprobacion de NIEs
      //T
      if (/^[T]{1}/.test(temp)) {
        if (a[8] == /^[T]{1}[A-Z0-9]{8}$/.test(temp)) {
          return 3;
        } else {
          return -3;
        }
      }
      //XYZ
      if (/^[XYZ]{1}/.test(temp)) {
        pos = str_replace([
          'X',
          'Y',
          'Z'
        ], [
          '0',
          '1',
          '2'
        ], temp).substring(0, 8) % 23;
        if (a[8] == cadenadni.substring(pos, pos + 1)) {
          return 3;
        } else {
          return -3;
        }
      }
    }
    return 0;
  }
  //***********************************************************************************************
  //*** COMPROBACIÓN NÚMERO SEGURIDAD SOCIAL
  //*** return: 
  //*** 1 = SS ok
  //*** -1 = SS error
  //***********************************************************************************************
  function ss(f, r, i, o) {
    var valor = $(f).val().toUpperCase();
    //podemos encontrarnos números con 11 o 12 dígitos
    if (valor.length != 12 && valor.length != 11) {
      return 'N\xfamero Seguridad Social no v\xe1lido';
    }
    //sacamos las partes…
    var a = parseInt(valor.substring(0, 2));
    var b = valor.substring(2, valor.length - 2);
    var c = parseInt(valor.substring(valor.length - 2, valor.length));
    // si el numero b (la parte central) empieza por 0 hay que quitar primero los ceros
    var cero = b.substring(0, 1);
    if (cero == '0') {
      for (var i = 0; i < b.length; i++) {
        if (b[0] == 0) {
          b = b.substring(1, b.length);
        } else {
          break;
        }
      }
    }
    var d = 0;
    if (valor.length == 11) {
      if (parseInt(b) < 1000000)
        d = parseInt(b) + parseInt(a) * 1000000;
      else
        d = parseInt(a + '' + b);
    } else if (valor.length == 12) {
      if (parseInt(b) < 10000000)
        d = parseInt(b) + parseInt(a) * 10000000;
      else
        d = parseInt(a + '' + b);
    } else {
      return -1;  //"Número Seguridad Social no válido";
    }
    //alert("d:"+d);
    var dc = d % 97;
    if (c != dc) {
      return -1;  //"Número Seguridad Social no válido";
    } else {
      return 1;
    }
  }
  function str_replace(search, replace, subject) {
    var f = search, r = replace, s = subject;
    var ra = r instanceof Array, sa = s instanceof Array, f = [].concat(f), r = [].concat(r), i = (s = [].concat(s)).length;
    while (j = 0, i--) {
      if (s[i]) {
        while (s[i] = s[i].split(f[j]).join(ra ? r[j] || '' : r[0]), ++j in f) {
        }
        ;
      }
    }
    ;
    return sa ? s : s[0];
  }