6 angular.module('validator.directive', ['validator.provider']).directive('validator', [
7 '$injector', function($injector) {
11 link: function(scope, element, attrs, ctrl) {
12 var $parse, $validator, isAcceptTheBroadcast, model, observerRequired, onBlur, registerRequired, removeRule, rules, validate;
13 $validator = $injector.get('$validator');
14 $parse = $injector.get('$parse');
15 model = $parse(attrs.ngModel);
17 validate = function(from, args) {
18 var errorCount, increaseSuccessCount, rule, successCount, _fn, _i, _len;
24 Validate this element with all rules.
25 @param from: 'watch', 'blur' or 'broadcast'
27 success(): success callback (this callback will return success count)
28 error(): error callback (this callback will return error count)
29 oldValue: the old value of $watch
33 increaseSuccessCount = function() {
35 if (++successCount >= rules.length) {
36 ctrl.$setValidity(attrs.ngModel, true);
37 for (_i = 0, _len = rules.length; _i < _len; _i++) {
39 rule.success(model(scope), scope, element, attrs, $injector);
41 if (typeof args.success === "function") {
46 if (rules.length === 0) {
47 return increaseSuccessCount();
49 _fn = function(rule) {
50 return rule.validator(model(scope), scope, element, attrs, {
52 return increaseSuccessCount();
55 if (rule.enableError && ++errorCount === 1) {
56 ctrl.$setValidity(attrs.ngModel, false);
57 rule.error(model(scope), scope, element, attrs, $injector);
59 if ((typeof args.error === "function" ? args.error() : void 0) === 1) {
61 element[0].scrollIntoViewIfNeeded();
63 return element[0].select();
68 for (_i = 0, _len = rules.length; _i < _len; _i++) {
72 if (rule.invoke !== 'blur') {
75 rule.enableError = true;
78 if (rule.invoke !== 'watch' && !rule.enableError) {
79 increaseSuccessCount();
84 rule.enableError = true;
90 registerRequired = function() {
92 rule = $validator.getRule('required');
94 rule = $validator.convertRule('required', {
99 return rules.push(rule);
101 removeRule = function(name) {
104 Remove the rule in rules by the name.
106 var index, _i, _ref, _ref1, _results;
108 for (index = _i = 0, _ref = rules.length; _i < _ref; index = _i += 1) {
109 if (!(((_ref1 = rules[index]) != null ? _ref1.name : void 0) === name)) {
112 rules[index].success(model(scope), scope, element, attrs, $injector);
113 rules.splice(index, 1);
114 _results.push(index--);
118 attrs.$observe('validator', function(value) {
119 var match, name, rule, ruleNames, _i, _len, _results;
121 if (observerRequired.validatorRequired || observerRequired.required) {
124 match = value.match(/^\/(.*)\/$/);
126 rule = $validator.convertRule('dynamic', {
127 validator: RegExp(match[1]),
128 invoke: attrs.validatorInvoke,
129 error: attrs.validatorError
134 match = value.match(/^\[(.+)\]$/);
136 ruleNames = match[1].split(',');
138 for (_i = 0, _len = ruleNames.length; _i < _len; _i++) {
139 name = ruleNames[_i];
140 rule = $validator.getRule(name.replace(/^\s+|\s+$/g, ''));
141 if (typeof rule.init === "function") {
142 rule.init(scope, element, attrs, $injector);
145 _results.push(rules.push(rule));
147 _results.push(void 0);
153 attrs.$observe('validatorError', function(value) {
155 match = attrs.validator.match(/^\/(.*)\/$/);
157 removeRule('dynamic');
158 rule = $validator.convertRule('dynamic', {
159 validator: RegExp(match[1]),
160 invoke: attrs.validatorInvoke,
163 return rules.push(rule);
167 validatorRequired: false,
170 attrs.$observe('validatorRequired', function(value) {
171 if (value && value !== 'false') {
173 return observerRequired.validatorRequired = true;
174 } else if (observerRequired.validatorRequired) {
175 removeRule('required');
176 return observerRequired.validatorRequired = false;
179 attrs.$observe('required', function(value) {
180 if (value && value !== 'false') {
182 return observerRequired.required = true;
183 } else if (observerRequired.required) {
184 removeRule('required');
185 return observerRequired.required = false;
188 isAcceptTheBroadcast = function(broadcast, modelName) {
189 var anyHashKey, dotIndex, itemExpression, itemModel;
191 if (attrs.validatorGroup === modelName) {
194 if (broadcast.targetScope === scope) {
195 return attrs.ngModel.indexOf(modelName) === 0;
197 anyHashKey = function(targetModel, hashKey) {
199 for (key in targetModel) {
200 x = targetModel[key];
203 if (key === '$$hashKey' && x === hashKey) {
208 if (anyHashKey(x, hashKey)) {
216 dotIndex = attrs.ngModel.indexOf('.');
217 itemExpression = dotIndex >= 0 ? attrs.ngModel.substr(0, dotIndex) : attrs.ngModel;
218 itemModel = $parse(itemExpression)(scope);
219 return anyHashKey($parse(modelName)(broadcast.targetScope), itemModel.$$hashKey);
224 scope.$on($validator.broadcastChannel.prepare, function(self, object) {
225 if (!isAcceptTheBroadcast(self, object.model)) {
228 return object.accept();
230 scope.$on($validator.broadcastChannel.start, function(self, object) {
231 if (!isAcceptTheBroadcast(self, object.model)) {
234 return validate('broadcast', {
235 success: object.success,
239 scope.$on($validator.broadcastChannel.reset, function(self, object) {
241 if (!isAcceptTheBroadcast(self, object.model)) {
244 for (_i = 0, _len = rules.length; _i < _len; _i++) {
246 rule.success(model(scope), scope, element, attrs, $injector);
247 if (rule.invoke !== 'watch') {
248 rule.enableError = false;
251 return ctrl.$setValidity(attrs.ngModel, true);
253 scope.$watch(attrs.ngModel, function(newValue, oldValue) {
254 if (newValue === oldValue) {
257 return validate('watch', {
261 onBlur = function() {
262 if (scope.$root.$$phase) {
263 return validate('blur');
265 return scope.$apply(function() {
266 return validate('blur');
270 $(element).bind('blur', onBlur);
271 return scope.$on('$destroy', function() {
272 return $(element).unbind('blur', onBlur);
282 angular.module('validator', ['validator.directive']);
291 angular.module('validator.provider', []).provider('$validator', function() {
292 var $injector, $q, $timeout;
297 this.broadcastChannel = {
298 prepare: '$validatePrepare',
299 start: '$validateStart',
300 reset: '$validateReset'
302 this.setupProviders = function(injector) {
303 $injector = injector;
304 $q = $injector.get('$q');
305 return $timeout = $injector.get('$timeout');
307 this.convertError = function(error) {
311 @param error: error messate (string) or function(value, scope, element, attrs, $injector)
312 @return: function(value, scope, element, attrs, $injector)
315 if (typeof error === 'function') {
318 errorMessage = error.constructor === String ? error : '';
319 return function(value, scope, element, attrs) {
320 var $label, label, parent, _i, _len, _ref, _results;
321 parent = $(element).parent();
323 while (parent.length !== 0) {
324 if (parent.hasClass('form-group')) {
325 parent.addClass('has-error');
326 _ref = parent.find('label');
327 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
329 if ($(label).hasClass('error')) {
333 $label = $("<label class='control-label error'>" + errorMessage + "</label>");
335 $label.attr('for', attrs.id);
337 if ($(element).parent().hasClass('input-group')) {
338 $(element).parent().parent().append($label);
340 $(element).parent().append($label);
344 _results.push(parent = parent.parent());
349 this.convertSuccess = function(success) {
352 Convert rule.success.
353 @param success: function(value, scope, element, attrs, $injector)
354 @return: function(value, scope, element, attrs, $injector)
356 if (typeof success === 'function') {
359 return function(value, scope, element) {
360 var label, parent, _i, _len, _ref, _results;
361 parent = $(element).parent();
363 while (parent.length !== 0) {
364 if (parent.hasClass('has-error')) {
365 parent.removeClass('has-error');
366 _ref = parent.find('label');
367 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
369 if ($(label).hasClass('error')) {
375 _results.push(parent = parent.parent());
380 this.convertValidator = function(validator) {
383 Convert rule.validator.
384 @param validator: RegExp() or function(value, scope, element, attrs, $injector)
385 { return true / false }
386 @return: function(value, scope, element, attrs, funcs{success, error})
387 (funcs is callback functions)
389 var func, regex, result;
390 result = function() {};
391 if (validator.constructor === RegExp) {
393 result = function(value, scope, element, attrs, funcs) {
397 if (regex.test(value)) {
398 return typeof funcs.success === "function" ? funcs.success() : void 0;
400 return typeof funcs.error === "function" ? funcs.error() : void 0;
403 } else if (typeof validator === 'function') {
405 result = function(value, scope, element, attrs, funcs) {
406 return $q.all([func(value, scope, element, attrs, $injector)]).then(function(objects) {
407 if (objects && objects.length > 0 && objects[0]) {
408 return typeof funcs.success === "function" ? funcs.success() : void 0;
410 return typeof funcs.error === "function" ? funcs.error() : void 0;
413 return typeof funcs.error === "function" ? funcs.error() : void 0;
419 this.convertRule = (function(_this) {
420 return function(name, object) {
421 var result, _ref, _ref1;
422 if (object == null) {
427 Convert the rule object.
431 enableError: object.invoke === 'watch',
432 invoke: object.invoke,
434 validator: (_ref = object.validator) != null ? _ref : function() {
437 error: (_ref1 = object.error) != null ? _ref1 : '',
438 success: object.success
440 result.error = _this.convertError(result.error);
441 result.success = _this.convertSuccess(result.success);
442 result.validator = _this.convertValidator(result.validator);
446 this.register = function(name, object) {
447 if (object == null) {
453 @params name: The rule name.
455 invoke: 'watch' or 'blur' or undefined(validate by yourself)
456 init: function(scope, element, attrs, $injector)
457 validator: RegExp() or function(value, scope, element, attrs, $injector)
458 error: string or function(scope, element, attrs)
459 success: function(scope, element, attrs)
461 return this.rules[name] = this.convertRule(name, object);
463 this.getRule = function(name) {
466 Get the rule form $validator.rules by the name.
469 if (this.rules[name]) {
470 return angular.copy(this.rules[name]);
475 this.validate = (function(_this) {
476 return function(scope, model) {
480 @param scope: The scope.
481 @param model: The model name of the scope or validator-group.
483 @promise success(): The success function.
484 @promise error(): The error function.
486 var broadcastObject, count, deferred, func, promise;
487 deferred = $q.defer();
488 promise = deferred.promise;
501 return count.total++;
503 validatedSuccess: function() {
504 var x, _i, _j, _len, _len1, _ref, _ref1;
505 if (++count.success === count.total) {
506 _ref = func.promises.success;
507 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
511 _ref1 = func.promises.then;
512 for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
517 return count.success;
519 validatedError: function() {
520 var x, _i, _j, _len, _len1, _ref, _ref1;
521 if (count.error++ === 0) {
522 _ref = func.promises.error;
523 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
527 _ref1 = func.promises.then;
528 for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
536 promise.success = function(fn) {
537 func.promises.success.push(fn);
540 promise.error = function(fn) {
541 func.promises.error.push(fn);
544 promise.then = function(fn) {
545 func.promises.then.push(fn);
551 success: func.validatedSuccess,
552 error: func.validatedError
554 scope.$broadcast(_this.broadcastChannel.prepare, broadcastObject);
555 $timeout(function() {
556 var $validator, x, _i, _len, _ref;
557 if (count.total === 0) {
558 _ref = func.promises.success;
559 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
565 $validator = $injector.get('$validator');
566 return scope.$broadcast($validator.broadcastChannel.start, broadcastObject);
571 this.reset = (function(_this) {
572 return function(scope, model) {
575 Reset validated error messages of the model.
576 @param scope: The scope.
577 @param model: The model name of the scope or validator-group.
579 return scope.$broadcast(_this.broadcastChannel.reset, {
584 this.get = function($injector) {
585 this.setupProviders($injector);
588 broadcastChannel: this.broadcastChannel,
589 register: this.register,
590 convertRule: this.convertRule,
591 getRule: this.getRule,
592 validate: this.validate,
596 this.get.$inject = ['$injector'];
597 this.$get = this.get;