');
30 | }).toThrow();
31 |
32 | expect(function () {
33 | createElem('
');
34 | }).toThrow();
35 | });
36 |
37 | it('adds novalidate to forms', function () {
38 | var form = createElem('
');
39 | expect(form.attr('novalidate')).toBeDefined();
40 | });
41 |
42 | it('should not let invalid forms submit themselves', function () {
43 | var form = createElem('
');
44 | $scope.submit = function () {
45 | console.log('whoops');
46 | };
47 | spyOn($scope, 'submit');
48 |
49 | $scope.form.$setValidity('some-rule', false);
50 | form.triggerHandler('submit');
51 | expect($scope.submit).not.toHaveBeenCalled();
52 |
53 | $scope.form.$setValidity('some-rule', true);
54 | form.triggerHandler('submit');
55 | expect($scope.submit).toHaveBeenCalled();
56 | });
57 |
58 | it('should make all children validation errors visible when trying to submit', function () {
59 | var form = createElem('
');
63 | var input1 = angular.element(form.children()[0]);
64 | var input2 = angular.element(form.children()[0]);
65 | var vmsg1 = input1.data('message-element')[0];
66 | var vmsg2 = input2.data('message-element')[0];
67 | $scope.value1 = '';
68 | $scope.value2 = 'abc';
69 |
70 | $scope.$digest();
71 | expect(vmsg1).toBeHidden();
72 | expect(vmsg2).toBeHidden();
73 |
74 | form.triggerHandler('submit');
75 | $scope.$digest();
76 |
77 | expect(vmsg1).toBeVisible();
78 | expect(vmsg2).toBeVisible();
79 | });
80 |
81 | it('should support custom error messages from form', function () {
82 | $scope.opts = {errorMessages: {pattern: 'Must be the secret word!'}};
83 | var form = createElem('
');
86 | var input = form.find('input');
87 | var messageElement = input.data('message-element');
88 | $scope.test = 'not the word';
89 | $scope.$digest();
90 | input.triggerHandler('blur');
91 | $scope.$digest();
92 | expect(messageElement.text()).toBe('Must be the secret word!');
93 | });
94 |
95 | it('should work with various inputs', function () {
96 | var form = createElem('
');
102 | var nameInput = angular.element(form.find('input')[0]);
103 | var emailInput = angular.element(form.find('input')[1]);
104 | var urlInput = angular.element(form.find('input')[2]);
105 | var ageInput = angular.element(form.find('input')[3]);
106 |
107 | var nameMessage = nameInput.data('message-element')[0];
108 | var emailMessage = emailInput.data('message-element')[0];
109 | var urlMessage = urlInput.data('message-element')[0];
110 | var ageMessage = ageInput.data('message-element')[0];
111 |
112 | $scope.$digest();
113 | form.triggerHandler('submit');
114 | $scope.$digest();
115 |
116 | expect(nameMessage).toBeVisible();
117 | expect(emailMessage).toBeVisible();
118 | expect(urlMessage).toBeHidden();
119 | expect(ageMessage).toBeVisible();
120 |
121 | expect(nameMessage).toHaveText(service._getErrorMessage('required'));
122 | expect(emailMessage).toHaveText(service._getErrorMessage('required', 'email'));
123 | expect(ageMessage).toHaveText(service._getErrorMessage('required', 'number'));
124 |
125 | $scope.url = 'not an url';
126 | $scope.name = 'Bob';
127 | $scope.email = 'not an email';
128 | $scope.age = 22;
129 |
130 | $scope.$digest();
131 | form.triggerHandler('submit');
132 | $scope.$digest();
133 | expect(nameMessage).toBeHidden();
134 | expect(emailMessage).toBeVisible();
135 | expect(urlMessage).toBeVisible();
136 | expect(ageMessage).toBeHidden();
137 |
138 | expect(emailMessage).toHaveText(service._getErrorMessage('email'));
139 | expect(urlMessage).toHaveText(service._getErrorMessage('url'));
140 | });
141 | });
142 |
--------------------------------------------------------------------------------
/test/lib/jasmine-dom-matchers.js:
--------------------------------------------------------------------------------
1 | /*jsl:declare jasmine*/
2 | /*jsl:declare Sizzle*/
3 | /*jsl:declare Prototype*/
4 | /*jsl:declare jQuery*/
5 |
6 | jasmine.DOM = {};
7 |
8 | jasmine.DOM.browserTagCaseIndependentHtml = function(html)
9 | {
10 | var div= document.createElement('div');
11 | div.innerHTML= html;
12 | return div.innerHTML;
13 | }
14 |
15 | jasmine.DOM.elementToString = function(element)
16 | {
17 | var div= document.createElement('div');
18 | div.appendChild(element.cloneNode(true));
19 | return div.innerHTML;
20 | }
21 |
22 | jasmine.DOM.trim= function(string)
23 | {
24 | var str= string.replace(/^\s+/, '');
25 | for (var i = str.length - 1; i > 0; --i)
26 | if (/\S/.test(str.charAt(i)))
27 | {
28 | str = str.substring(0, i + 1);
29 | break;
30 | }
31 | return str;
32 | }
33 |
34 | jasmine.DOM.slice= function(arrayLike, startIndex)
35 | {
36 | return [].slice.call(arrayLike, startIndex||0);
37 | }
38 |
39 | jasmine.DOM.uniqueId= 1;
40 | jasmine.DOM.assignId= function(element)
41 | {
42 | return element.id || (element.id=('jasmine_id_' + jasmine.DOM.uniqueId++));
43 | };
44 |
45 | /**
46 | jasmine.DOM.queryAll(selector[, scope]) -> array
47 | */
48 | jasmine.DOM.queryAll= (function(){
49 | if ('undefined'!==typeof(Sizzle))
50 | return Sizzle;
51 | if ('undefined'!==typeof(Prototype))
52 | return function(selector, node)
53 | {
54 | return Element.getElementsBySelector(node||document, selector);
55 | };
56 | if ('undefined'!==typeof(jQuery))
57 | return function(selector, node)
58 | {
59 | var result= jQuery(selector, node);
60 | var nodes= [];
61 | var len= result.length;
62 |
63 | for (var i=0; i
0;
162 | }
163 | };
164 |
165 | function comparePropertyValues(actualValue, expectedValue)
166 | {
167 | if (void(0) === expectedValue)
168 | return void(0) !== actualValue;
169 | return actualValue == expectedValue;
170 | }
171 |
172 | function bindMatcher(methodName)
173 | {
174 | var originalMatcher = jasmine.Matchers.prototype[methodName];
175 |
176 | jasmine.DOM.matchers[methodName] = function()
177 | {
178 | // If the actual value is a DOM node...
179 | if (this.actual && this.actual.nodeType)
180 | {
181 | var result = matchers[methodName].apply(this, arguments);
182 | this.actual = jasmine.DOM.elementToString(this.actual);
183 | return result;
184 | }
185 |
186 | if (originalMatcher)
187 | return originalMatcher.apply(this, arguments);
188 |
189 | return false;
190 | }
191 |
192 | }
193 |
194 | for (var methodName in matchers)
195 | bindMatcher(methodName);
196 |
197 | })();
198 |
199 | beforeEach(function() {
200 | this.addMatchers(jasmine.DOM.matchers);
201 | });
202 |
203 | afterEach(function() {
204 | jasmine.getFixtures().cleanUp();
205 | });
206 |
--------------------------------------------------------------------------------
/src/helper.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Gabriel_Grinberg on 11/14/14.
3 | */
4 | (function () {
5 | 'use strict';
6 | angular.module('gg.vmsgs')
7 | .factory('ValidationMessagesHelper', function ($compile) {
8 |
9 | var defaultErrorKeys = ['required', 'email', 'url', 'minlength', 'maxlength', 'pattern', 'min', 'max'];
10 |
11 | var defaultOptions = {
12 | hideClassName: 'ng-hide',
13 | messageClassName: 'validation-message',
14 | messageTemplate: '',
15 | showTrigger: 'blur',
16 | hideTrigger: 'valid',
17 | dummy: false,
18 |
19 | //for overriding error messages
20 | errorMessages: {},
21 |
22 | //to support boostrap styling
23 | parentContainerClassName: 'form-group',
24 | parentErrorClassName: 'has-error',
25 | parentSuccessClassName: ''
26 | };
27 |
28 | return {
29 | setOptions: function (opts) {
30 | defaultOptions = angular.extend({}, defaultOptions, opts || {});
31 | if (opts) {
32 | angular.forEach(opts, function (value, key) {
33 | if (key.toUpperCase() === 'ERRORMESSAGES') {
34 | angular.forEach(opts[key], function (eValue, eKey) {
35 | if (defaultErrorKeys.indexOf(eKey) === -1) {
36 | defaultErrorKeys.push(eKey);
37 | }
38 | });
39 | }
40 | });
41 | }
42 | },
43 | getOptions: function (opts, formOpts) {
44 | return angular.extend({}, defaultOptions, formOpts || {}, opts || {});
45 | },
46 | showTriggers: ['blur', 'submit', 'keydown', 'keyup'],
47 | hideTriggers: ['valid', 'keydown', 'keyup'],
48 |
49 | //these also define the order of rendering
50 | errorKeys: defaultErrorKeys,
51 | errorMessages: {
52 | required: {
53 | default: 'This field is required',
54 | email: 'A valid e-mail address is required',
55 | number: 'A valid number is required',
56 | date: 'A valid date is required',
57 | week: 'A valid week is required',
58 | url: 'A valid url is required',
59 | month: 'A valid month is required'
60 | },
61 | url: 'A valid url is required',
62 | email: 'A valid e-mail address is required',
63 | minlength: 'This field must be at least {minlength} chars',
64 | maxlength: 'This field must be less than {maxlength} chars',
65 | min: {
66 | default: 'This field must be higher than {min}',
67 | number: 'This number must be higher than {min}',
68 | date: 'This date must be after {min}',
69 | week: 'This week must be after {min}',
70 | month: 'This month must be after {min}'
71 | },
72 | max: {
73 | default: 'This field must be lower than {max}',
74 | number: 'This number must be lower than {max}',
75 | date: 'This date must be before {max}',
76 | week: 'This week must be before {max}',
77 | month: 'This month must be before {max}'
78 | },
79 | pattern: 'This field must match a specific pattern {pattern}'
80 | },
81 | renderError: function (msg) {
82 | var args;
83 | args = arguments;
84 | if (args.length === 2 && args[1] !== null && typeof args[1] === 'object') {
85 | args = args[1];
86 | }
87 | return msg.replace(/{([^}]*)}/g, function (match, key) {
88 | return (typeof args[key] !== 'undefined' ? args[key] : match);
89 | });
90 | },
91 | _getErrorMessage: function (errorKey, inputType, params, opts) {
92 | var errorMessageFromOpts = opts && opts.errorMessages && opts.errorMessages[errorKey];
93 | var errorMessageObject = errorMessageFromOpts || this.errorMessages[errorKey];
94 |
95 | if (errorMessageObject) {
96 | return this.renderError(errorMessageObject[inputType] || errorMessageObject.default || errorMessageObject, params);
97 | } else {
98 | throw 'Error message not supported for type ' + errorKey + ' and inputType ' + inputType;
99 | }
100 | },
101 | getRenderParameters: function (elem) {
102 | var params = {};
103 | ['minlength', 'maxlength', 'min', 'max', 'pattern'].forEach(function (attr) {
104 | var calculatedAttr = elem.attr(attr) || elem.attr('ng-' + attr);
105 | if (calculatedAttr) {
106 | params[attr] = calculatedAttr;
107 | }
108 | });
109 | return params;
110 | },
111 | getErrorMessage: function ($error, elem, opts) {
112 | var inputType = elem.attr('type');
113 | var selectedError = '';
114 | var renderParameters = this.getRenderParameters(elem);
115 | this.errorKeys.forEach(function (errorKey) {
116 | if (!selectedError && $error[errorKey]) {
117 | selectedError = errorKey;
118 | }
119 | });
120 | return this._getErrorMessage(selectedError, inputType, renderParameters, opts);
121 | },
122 | createMessageElement: function (scope, opts) {
123 | return $compile(opts.messageTemplate)(scope)
124 | .addClass(opts.messageClassName)
125 | .addClass(opts.hideClassName);
126 | }
127 | };
128 | });
129 | })();
130 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/GabiGrin/angular-validation-messages)
2 | [](https://coveralls.io/r/GabiGrin/angular-validation-messages?branch=master)
3 | [](https://codeclimate.com/github/GabiGrin/angular-validation-messages)
4 | angular-validation-messages
5 | ===========================
6 |
7 | Add validation messages to your forms in a breeze
8 |
9 | ##Demo
10 | ###[Here](http://gabigrin.github.io/angular-validation-messages/)
11 | PS: It doesn't cover everything yet, more to come.
12 |
13 | ##What it does
14 |
15 | This directive adds automatic validation messages to your forms, by just adding it to your form and fields.
16 | It also prevents your form from being submitted unless it's valid.
17 | For example:
18 | 
19 |
20 | When writing forms, a you will soon find yourself writing the same code over and over again, just to display what is invalid in the form.
21 | This directive turns this:
22 | ```
23 |
40 |
41 | //inside controller
42 | $scope.doSomething(){
43 | if (myForm.$valid){
44 | //then actually do something
45 | }
46 | }
47 | ```
48 |
49 | into this:
50 | ```
51 |
56 |
57 | //inside controller
58 | $scope.doSomething(){
59 | //actually do something
60 | }
61 | ```
62 |
63 | It comes with a bunch of predefined messages for commonly used validation messages, such as email, number, minlength, url and more. _You can always override them if you think the stock messages suck!_
64 |
65 | ##What it doesn't do
66 | It doesn't create custom validation rules, there are plenty of modules for that.
67 | It doesn't make your forms prettier, use Bootstrap/Foundation/Zimit/Custom css for that.
68 |
69 | ##How
70 | Use bower:
71 | ```
72 | bower install angular-validation-messages
73 | ```
74 | Or just [download](https://github.com/GabiGrin/angular-validation-messages/archive/master.zip) the files and add the following files to your html:
75 | ```
76 |
77 |
78 | ```
79 | Add `'gg.vmsgs'` to your app dependencies (`angular.module('yourApp', ['gg.vmsgs']`)
80 |
81 | Now just add the "vmsg-form" directive to your forms, and "vmsg" to the inputs you wish to show validation messages for.
82 |
83 | ##Features
84 | * Just-add-water form validation messages
85 | * Allows custom message templates to be used
86 | * Does not require "name" attribute to be intact
87 | * Submit only valid forms
88 | * Customize error messages
89 | * Override options either globally, per form or per control
90 | * Supports different show/hide message triggers
91 | * 100% test coverage
92 | * No jQuery used, no other dependencies - 100% AngularJS
93 | * Out of the box bootstrap parent form-group has-error support
94 |
95 | ##Overriding options
96 | To override global options, use `ValidationMessagesHelper.setOptions(yourOptions)` in a run block.
97 | vmsg-form and vmsg directive can also receive an options object. When multiple overrides are used, the most specific one "wins".
98 |
99 | ###Supported options
100 |
101 | Name | Explanation | Accepts | Default
102 | --- | --- | --- | ---
103 | showTrigger | trigger to show messages | blur/keydown/keyup/submit | blur
104 | hideTrigger | trigger to hide messages | valid/keydown/keyup | valid
105 | messageClassName | class name added to the message dom element | any string | 'validation-message'
106 | messageTemplate | html to use as the template for the messages. | any valid html, with 1 root, containing a element (which will receive the message |
107 | hideClassName | class that is added to hide messages | any classname | ng-hide
108 | parentErrorClassName | adds (or removes) a class name to a field's parent element, if it matches the 'parentContainerClassName' option. Good for using with bootstrap, where you want to add 'has-error' to the parent div.form-group | any string | 'has-error'
109 | parentSuccessClassName | adds (or removes) a class name to a field's parent element, if it matches the 'parentContainerClassName' option. Good for using with bootstrap, where you want to add 'has-success' to the parent div.form-group.| any string | ''
110 | parentContainerClassName | only if this class name is present in the parent element, parentErrorClassName and parentSuccessClassName will be applied | any string | 'form-group'
111 | errorMessages | allows you to override error messages for specific errors. The error messages passed will be combined with the default ones (using angular.extend) | any valid object | please check src/helper.js:36
112 |
113 |
114 | ##Caveats
115 | *The vmsg-form directive will add the "novalidate" attribute automatically, so the browser doesn't catch the validation issues by itself.
116 | *It doesn't support hiding the messages when the form is pristine at the moment. Will be added in the near future.
117 | *When overriding the message html template, you must have only one root element, and include the where you want the message displayed. There is no validation for this yet, so be aware.
118 |
119 | ##Contributing
120 | PR are more than welcome!
121 | Please make sure you do not brake the build, and add tests for any new features added, maintaining 100% coverage (TDD is highly recommended).
122 | To start hacking just clone the repository and run npm install + bower install, then run 'grunt serve'.
123 |
124 |
--------------------------------------------------------------------------------
/test/helper.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Gabriel_Grinberg on 11/14/14.
3 | */
4 | 'use strict';
5 | describe('helper', function () {
6 | var helper;
7 |
8 | beforeEach(function () {
9 | module('gg.vmsgs');
10 | inject(function (ValidationMessagesHelper) {
11 | helper = ValidationMessagesHelper;
12 | });
13 | });
14 |
15 | it('should have a helper service', function () {
16 | expect(helper).toBeDefined();
17 | });
18 |
19 | it('can change default options', function () {
20 | var original = helper.getOptions();
21 | helper.setOptions();
22 | expect(helper.getOptions()).toEqual(original);
23 |
24 | helper.setOptions({showTrigger: 'submit'});
25 | expect(helper.getOptions()).toEqual(angular.extend({}, original, {showTrigger: 'submit'}));
26 | });
27 |
28 | it('should be able to render errors', function () {
29 | expect(helper.renderError('test')).toBe('test');
30 | expect(helper.renderError('test {0} with {1}', {0: 'one', 1: 'parameters'})).toBe('test one with parameters');
31 | expect(helper.renderError('Number must be between {min} and {max}', {min: 5, max: 10})).toBe('Number must be between 5 and 10');
32 | expect(helper.renderError('Hello {world}', {notWorld: 'world'})).toBe('Hello {world}');
33 | });
34 |
35 | it('should have error messages for all known validators', function () {
36 | helper.errorKeys.forEach(function (errorKey) {
37 | expect(helper.errorMessages[errorKey]).toBeTruthy();
38 | });
39 | });
40 |
41 | it('should extract render parameters from input', function () {
42 | var elem1 = angular.element('');
43 | expect(helper.getRenderParameters(elem1)).toEqual(
44 | {
45 | min: '2',
46 | max: '4',
47 | minlength: '10',
48 | maxlength: '50',
49 | pattern: 'pat'
50 | });
51 | });
52 |
53 | it('should extract render parameters from input even when using ng-[attr]', function () {
54 | var elem1 = angular.element('');
55 | expect(helper.getRenderParameters(elem1)).toEqual(
56 | {
57 | min: '2',
58 | max: '4',
59 | minlength: '10',
60 | maxlength: '50',
61 | pattern: '/pat/'
62 | });
63 | });
64 |
65 | it('should fall back to default when type is not provided when getting error message', function () {
66 | expect(helper._getErrorMessage('required', 'bogus')).toBe(helper._getErrorMessage('required', 'default'));
67 | expect(helper._getErrorMessage('maxlength', 'bogus2')).toBe(helper._getErrorMessage('maxlength', 'default'));
68 | });
69 |
70 | it('should throw error when trying to get bad error messages', function () {
71 | expect(function () {
72 | helper._getErrorMessage('pumpkin');
73 | }).toThrow();
74 |
75 | expect(function () {
76 | helper._getErrorMessage('spongebob');
77 | }).toThrow();
78 | });
79 |
80 | it('should get error messages based on error object and element', function () {
81 | var input1 = angular.element('');
82 | var input2 = angular.element('');
83 | var $error1 = {required: true};
84 | var $error2 = {minlength: true};
85 |
86 | expect(helper.getErrorMessage($error1, input1)).toBe(helper._getErrorMessage('required'));
87 | expect(helper.getErrorMessage($error2, input1)).toBe(helper._getErrorMessage('minlength', null, {minlength: 20}));
88 | expect(helper.getErrorMessage($error1, input2)).toBe(helper._getErrorMessage('required', 'number'));
89 | });
90 |
91 | it('should prioritize error messages, and show the most important one only', function () {
92 | var input1 = angular.element('');
93 | var $error1 = {required: true, pattern: true, minlength: true};
94 | var $error2 = {pattern: true, minlength: true};
95 | var $error3 = {pattern: true, someother: true};
96 |
97 | expect(helper.getErrorMessage($error1, input1)).toBe(helper._getErrorMessage('required'));
98 | expect(helper.getErrorMessage($error2, input1)).toBe(helper._getErrorMessage('minlength', 'text', {minlength: 20}));
99 | expect(helper.getErrorMessage($error3, input1)).toBe(helper._getErrorMessage('pattern', 'text', {pattern: 'test'}));
100 | });
101 |
102 | it('should use custom error messages if available', function () {
103 | var input1 = angular.element('');
104 | var input2 = angular.element('');
105 | var $error1 = {required: true};
106 | var $error2 = {minlength: true};
107 | var $error3 = {pattern: true};
108 |
109 | var opts = {
110 | errorMessages: {
111 | required: {
112 | default: 'Custom msg #1',
113 | number: 'Number here!'
114 | },
115 | minlength: 'Custom msg #2',
116 | pattern: {
117 | text: 'Text must match pattern',
118 | number: 'Numbers must be only 0-4'
119 | }
120 | }
121 | };
122 |
123 | var opts2 = {
124 | errorMessages: {
125 | required: 'Required'
126 | }
127 | };
128 |
129 | //should use the new custom message, because text goes to default
130 | expect(helper.getErrorMessage($error1, input1, opts)).toBe('Custom msg #1');
131 | expect(helper.getErrorMessage($error1, input1, opts2)).toBe('Required');
132 | expect(helper.getErrorMessage($error1, input2, opts)).toBe('Number here!');
133 |
134 | expect(helper.getErrorMessage($error2, input1, opts)).toBe('Custom msg #2');
135 | expect(helper.getErrorMessage($error3, input1, opts)).toBe('Text must match pattern');
136 | expect(helper.getErrorMessage($error3, input2, opts)).toBe('Numbers must be only 0-4');
137 | });
138 |
139 | })
140 | ;
141 |
142 | describe('helper', function () {
143 | var helper;
144 | var $scope;
145 |
146 | beforeEach(function () {
147 | module('gg.vmsgs');
148 | inject(function (ValidationMessagesHelper, $rootScope) {
149 | helper = ValidationMessagesHelper;
150 | $scope = $rootScope.$new();
151 | });
152 | });
153 |
154 | it('should be able to create empty message element', function () {
155 | var opts = helper.getOptions();
156 | var messageElement = helper.createMessageElement($scope, opts);
157 | $scope.$digest();
158 | expect(opts.messageClassName).toBeTruthy();
159 | expect(messageElement.attr('class')).toContain(opts.messageClassName);
160 | expect(messageElement.text()).toBe('');
161 | });
162 |
163 | it('should add custom error key if not yet present', function () {
164 | var input1 = angular.element('');
165 | var $error1 = {someNewErrorKey: true};
166 |
167 | expect(helper.errorKeys.length).toBe(8);
168 |
169 | var opts = {
170 | errorMessages: {
171 | required: {
172 | default: 'Custom msg #1',
173 | number: 'Number here!'
174 | },
175 | someNewErrorKey: 'Some new error key message'
176 | }
177 | };
178 |
179 | helper.setOptions(opts);
180 | expect(helper.errorKeys.length).toBe(9);
181 |
182 | expect(helper.getErrorMessage($error1, input1, opts)).toBe('Some new error key message');
183 | });
184 |
185 | });
186 |
187 |
--------------------------------------------------------------------------------
/test/model-directive.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | describe('base usage: model directive', function () {
3 | var form,
4 | $compile,
5 | $scope,
6 | helper,
7 | body;
8 |
9 | function createElem(template, parent) {
10 | var elem = $compile(template)($scope);
11 | parent = parent || body;
12 | parent.append(elem);
13 | $scope.$digest();
14 | return elem;
15 | }
16 |
17 | beforeEach(function () {
18 | module('gg.vmsgs');
19 | inject(function (_$compile_, _$rootScope_, $document, _ValidationMessagesHelper_) {
20 | $compile = _$compile_;
21 | $scope = _$rootScope_.$new();
22 | body = $document.find('body').empty();
23 | helper = _ValidationMessagesHelper_;
24 | });
25 | form = createElem('');
26 | });
27 |
28 | it('should not work without parent form', function () {
29 | expect(function () {
30 | createElem('');
31 | }).toThrow();
32 |
33 | expect(function () {
34 | createElem('');
35 | }).not.toThrow();
36 | });
37 |
38 | it('should have an empty message element', function () {
39 | var form = createElem('');
40 | var input = form.find('input');
41 | var messageElement = input.data('message-element');
42 | expect(messageElement).toBeTruthy();
43 | expect(messageElement.text()).toBe('');
44 | expect(messageElement[0]).toBeHidden();
45 | });
46 |
47 | it('shows error message when item is invalid and blurred', function () {
48 | var form = createElem('');
49 | var input = form.find('input');
50 | var messageElement = input.data('message-element');
51 | expect(messageElement[0]).toBeHidden();
52 |
53 | input.triggerHandler('blur');
54 | $scope.$digest();
55 | expect(messageElement[0]).toBeVisible();
56 |
57 | $scope.test = 'something';
58 | input.triggerHandler('blur');
59 | $scope.$digest();
60 | expect(messageElement[0]).toBeHidden();
61 | });
62 |
63 | it('hides the error message as soon as the item is valid', function () {
64 | var form = createElem('');
65 | var input = form.find('input');
66 | var messageElement = input.data('message-element');
67 | input.triggerHandler('blur');
68 | $scope.$digest();
69 | expect(messageElement[0]).toBeVisible();
70 |
71 | $scope.test = 'bla bla';
72 | $scope.$digest();
73 | input.triggerHandler('blur');
74 | expect(messageElement[0]).toBeHidden();
75 | });
76 |
77 | it('changes the error type when needed', function () {
78 | var form = createElem('');
79 | var input = form.find('input');
80 | var messageElement = input.data('message-element');
81 | $scope.$digest();
82 | expect(messageElement.text()).toBe(helper.getErrorMessage({required: true}, input));
83 | $scope.test = 'bla';
84 | $scope.$digest();
85 | expect(messageElement.text()).toBe(helper.getErrorMessage({minlength: true}, input));
86 | });
87 |
88 | });
89 |
90 | describe('option overrides: model directive', function () {
91 | var $compile,
92 | $scope,
93 | helper,
94 | body;
95 |
96 | function createElem(template, parent) {
97 | var elem = $compile(template)($scope);
98 | parent = parent || body;
99 | parent.append(elem);
100 | $scope.$digest();
101 | return elem;
102 | }
103 |
104 | beforeEach(function () {
105 | module('gg.vmsgs');
106 | inject(function (_$compile_, _$rootScope_, $document, _ValidationMessagesHelper_) {
107 | $compile = _$compile_;
108 | $scope = _$rootScope_.$new();
109 | body = $document.find('body').empty();
110 | helper = _ValidationMessagesHelper_;
111 | });
112 | });
113 |
114 | //show triggers
115 | it('should support show keydown trigger', function () {
116 | helper.setOptions({showTrigger: 'keydown'});
117 | var form = createElem('');
118 | var input = form.find('input');
119 | var messageElement = input.data('message-element');
120 |
121 | $scope.test = 'something';
122 | $scope.$digest();
123 | $scope.test = '';
124 | $scope.$digest();
125 | input.triggerHandler('keydown');
126 | $scope.$digest();
127 | expect(messageElement[0]).toBeVisible();
128 | });
129 |
130 | it('should support show keyup trigger', function () {
131 | helper.setOptions({showTrigger: 'keyup'});
132 | var form = createElem('');
133 | var input = form.find('input');
134 | var messageElement = input.data('message-element');
135 |
136 | $scope.test = 'something';
137 | $scope.$digest();
138 | $scope.test = '';
139 | $scope.$digest();
140 | input.triggerHandler('keyup');
141 | $scope.$digest();
142 | expect(messageElement[0]).toBeVisible();
143 | });
144 |
145 | it('should support show submit trigger', function () {
146 | helper.setOptions({showTrigger: 'submit'});
147 |
148 | var form = createElem('');
149 | var input = form.find('input');
150 | var messageElement = input.data('message-element');
151 |
152 | $scope.test = '';
153 | $scope.$digest();
154 | form.triggerHandler('submit');
155 | $scope.$digest();
156 | expect(messageElement[0]).toBeVisible();
157 | });
158 |
159 | it('throws error when unsupported show trigger is used', function () {
160 | helper.setOptions({showTrigger: 'unsupported'});
161 | expect(function () {
162 | createElem('');
163 | }).toThrow();
164 | });
165 |
166 | it('should support change hide trigger', function () {
167 | helper.setOptions({hideTrigger: 'keydown'});
168 | var form = createElem('');
169 | var input = form.find('input');
170 | var messageElement = input.data('message-element');
171 |
172 | input.triggerHandler('blur');
173 | $scope.$digest();
174 | expect(messageElement[0]).toBeVisible();
175 |
176 | input.triggerHandler('keydown');
177 | $scope.$digest();
178 | expect(messageElement[0]).toBeHidden();
179 | });
180 |
181 | it('throws error when unsupported hide trigger is used', function () {
182 | helper.setOptions({hideTrigger: 'unsupported'});
183 | expect(function () {
184 | createElem('');
185 | }).toThrow();
186 | });
187 |
188 | it('supports changing template via helper', function () {
189 | helper.setOptions({messageTemplate: '{{errorMessage}}'});
190 | var form = createElem('');
191 | var input = form.find('input');
192 | var messageElement = input.data('message-element');
193 |
194 | expect(messageElement[0].tagName).toBe('A');
195 | // expect(messageElement.is('a')).toBeTruthy();
196 | });
197 |
198 | it('supports changing template via input', function () {
199 | var form = createElem('');
200 | var input = form.find('input');
201 | var messageElement = input.data('message-element');
202 | expect(messageElement[0].tagName).toBe('A');
203 | });
204 |
205 | it('supports changing template via form', function () {
206 | var form = createElem('');
207 | var input = form.find('input');
208 | var messageElement = input.data('message-element');
209 | expect(messageElement[0].tagName).toBe('A');
210 | });
211 |
212 | it('will get first input settings, if form settings are also given', function () {
213 | var form = createElem('');
214 | var input = form.find('input');
215 | var messageElement = input.data('message-element');
216 | expect(messageElement[0].tagName).toBe('P');
217 | });
218 |
219 | it('supports parent class changing (for bootstrap) on error', function () {
220 | var form = createElem(
221 | '');
226 | var formGroup = form.find('div');
227 | var input = form.find('input');
228 | var opts = helper.getOptions();
229 |
230 | input.triggerHandler('blur');
231 | $scope.$digest();
232 | expect(formGroup.hasClass(opts.parentErrorClassName)).toBeTruthy();
233 |
234 | $scope.test = 'one two three';
235 | $scope.$digest();
236 | expect(formGroup.hasClass(opts.parentErrorClassName)).toBeFalsy();
237 | });
238 |
239 | it('supports parent class changing (for bootstrap) on success', function () {
240 | var successClassName = 'has-success';
241 | var opts = $scope.opts = {
242 | parentSuccessClassName: successClassName
243 | };
244 | var form = createElem(
245 | '');
250 | var formGroup = form.find('div');
251 | var input = form.find('input');
252 |
253 | expect(formGroup.hasClass(opts.parentSuccessClassName)).toBeFalsy();
254 |
255 | input.triggerHandler('blur');
256 | $scope.$digest();
257 | expect(formGroup.hasClass(opts.parentSuccessClassName)).toBeFalsy();
258 |
259 | $scope.test = 'one two three';
260 | $scope.$digest();
261 | input.triggerHandler('blur');
262 | expect(formGroup.hasClass(opts.parentSuccessClassName)).toBeTruthy();
263 |
264 | $scope.test = '';
265 | input.triggerHandler('blur');
266 | $scope.$digest();
267 | expect(formGroup.hasClass(opts.parentSuccessClassName)).toBeFalsy();
268 | });
269 |
270 | it('should support custom error messages', function () {
271 | var form = createElem('');
274 | var input = form.find('input');
275 | var messageElement = input.data('message-element');
276 | $scope.$digest();
277 | input.triggerHandler('blur');
278 | $scope.$digest();
279 | expect(messageElement.text()).toBe('Must fill this one!');
280 | });
281 |
282 | });
283 |
--------------------------------------------------------------------------------
/dist/angular-validation-messages.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | angular.module('gg.vmsgs', []);
3 | ;(function () {
4 | 'use strict';
5 | angular.module('gg.vmsgs')
6 | .directive('vmsgForm', ['$parse', function ($parse) {
7 | return {
8 | restrict: 'A',
9 | require: 'form',
10 | priority: -1,
11 | compile: function () {
12 | return {
13 | pre: function preLink(scope, elem, attrs) {
14 |
15 | scope.formOpts = $parse(attrs.vmsgForm || '')(scope);
16 | elem.attr('novalidate', 'novalidate');
17 | scope.$watch(function () {
18 | return attrs.vmsgForm;
19 | }, function (opts) {
20 | scope.formOpts = $parse(opts || '')(scope);
21 | });
22 | },
23 | post: function postLink(scope, elem, attrs, formCtrl) {
24 | elem.bind('submit', function (e) {
25 | if (formCtrl.$invalid) {
26 | e.stopImmediatePropagation();
27 | scope.$broadcast('submit');
28 | }
29 | });
30 | }
31 | };
32 | },
33 | controller: ['$scope', function ($scope) {
34 | this.getOptions = function () {
35 | return $scope.formOpts;
36 | };
37 | }]
38 | };
39 | }]);
40 | })();
41 |
42 | ;/**
43 | * Created by Gabriel_Grinberg on 11/14/14.
44 | */
45 | (function () {
46 | 'use strict';
47 | angular.module('gg.vmsgs')
48 | .factory('ValidationMessagesHelper', ['$compile', function ($compile) {
49 |
50 | var defaultErrorKeys = ['required', 'email', 'url', 'minlength', 'maxlength', 'pattern', 'min', 'max'];
51 |
52 | var defaultOptions = {
53 | hideClassName: 'ng-hide',
54 | messageClassName: 'validation-message',
55 | messageTemplate: '',
56 | showTrigger: 'blur',
57 | hideTrigger: 'valid',
58 | dummy: false,
59 |
60 | //for overriding error messages
61 | errorMessages: {},
62 |
63 | //to support boostrap styling
64 | parentContainerClassName: 'form-group',
65 | parentErrorClassName: 'has-error',
66 | parentSuccessClassName: ''
67 | };
68 |
69 | return {
70 | setOptions: function (opts) {
71 | defaultOptions = angular.extend({}, defaultOptions, opts || {});
72 | if (opts) {
73 | angular.forEach(opts, function (value, key) {
74 | if (key.toUpperCase() === 'ERRORMESSAGES') {
75 | angular.forEach(opts[key], function (eValue, eKey) {
76 | if (defaultErrorKeys.indexOf(eKey) === -1) {
77 | defaultErrorKeys.push(eKey);
78 | }
79 | });
80 | }
81 | });
82 | }
83 | },
84 | getOptions: function (opts, formOpts) {
85 | return angular.extend({}, defaultOptions, formOpts || {}, opts || {});
86 | },
87 | showTriggers: ['blur', 'submit', 'keydown', 'keyup'],
88 | hideTriggers: ['valid', 'keydown', 'keyup'],
89 |
90 | //these also define the order of rendering
91 | errorKeys: defaultErrorKeys,
92 | errorMessages: {
93 | required: {
94 | default: 'This field is required',
95 | email: 'A valid e-mail address is required',
96 | number: 'A valid number is required',
97 | date: 'A valid date is required',
98 | week: 'A valid week is required',
99 | url: 'A valid url is required',
100 | month: 'A valid month is required'
101 | },
102 | url: 'A valid url is required',
103 | email: 'A valid e-mail address is required',
104 | minlength: 'This field must be at least {minlength} chars',
105 | maxlength: 'This field must be less than {maxlength} chars',
106 | min: {
107 | default: 'This field must be higher than {min}',
108 | number: 'This number must be higher than {min}',
109 | date: 'This date must be after {min}',
110 | week: 'This week must be after {min}',
111 | month: 'This month must be after {min}'
112 | },
113 | max: {
114 | default: 'This field must be lower than {max}',
115 | number: 'This number must be lower than {max}',
116 | date: 'This date must be before {max}',
117 | week: 'This week must be before {max}',
118 | month: 'This month must be before {max}'
119 | },
120 | pattern: 'This field must match a specific pattern {pattern}'
121 | },
122 | renderError: function (msg) {
123 | var args;
124 | args = arguments;
125 | if (args.length === 2 && args[1] !== null && typeof args[1] === 'object') {
126 | args = args[1];
127 | }
128 | return msg.replace(/{([^}]*)}/g, function (match, key) {
129 | return (typeof args[key] !== 'undefined' ? args[key] : match);
130 | });
131 | },
132 | _getErrorMessage: function (errorKey, inputType, params, opts) {
133 | var errorMessageFromOpts = opts && opts.errorMessages && opts.errorMessages[errorKey];
134 | var errorMessageObject = errorMessageFromOpts || this.errorMessages[errorKey];
135 |
136 | if (errorMessageObject) {
137 | return this.renderError(errorMessageObject[inputType] || errorMessageObject.default || errorMessageObject, params);
138 | } else {
139 | throw 'Error message not supported for type ' + errorKey + ' and inputType ' + inputType;
140 | }
141 | },
142 | getRenderParameters: function (elem) {
143 | var params = {};
144 | ['minlength', 'maxlength', 'min', 'max', 'pattern'].forEach(function (attr) {
145 | var calculatedAttr = elem.attr(attr) || elem.attr('ng-' + attr);
146 | if (calculatedAttr) {
147 | params[attr] = calculatedAttr;
148 | }
149 | });
150 | return params;
151 | },
152 | getErrorMessage: function ($error, elem, opts) {
153 | var inputType = elem.attr('type');
154 | var selectedError = '';
155 | var renderParameters = this.getRenderParameters(elem);
156 | this.errorKeys.forEach(function (errorKey) {
157 | if (!selectedError && $error[errorKey]) {
158 | selectedError = errorKey;
159 | }
160 | });
161 | return this._getErrorMessage(selectedError, inputType, renderParameters, opts);
162 | },
163 | createMessageElement: function (scope, opts) {
164 | return $compile(opts.messageTemplate)(scope)
165 | .addClass(opts.messageClassName)
166 | .addClass(opts.hideClassName);
167 | }
168 | };
169 | }]);
170 | })();
171 | ;/**
172 | * Created by Gabriel_Grinberg on 11/15/14.
173 | */
174 | (function () {
175 | 'use strict';
176 | angular.module('gg.vmsgs')
177 | .directive('vmsg', ['ValidationMessagesHelper', '$parse', function (ValidationMessagesHelper, $parse) {
178 | return {
179 | require: ['ngModel', '^vmsgForm'],
180 | restrict: 'A',
181 | compile: function () {
182 | return function postLink(scope, elem, attrs, ctrls) {
183 | var localOpts = $parse(attrs.vmsg || '')(scope);
184 | var formOpts = ctrls[1].getOptions();
185 | var opts = ValidationMessagesHelper.getOptions(localOpts, formOpts);
186 | var messageElem = ValidationMessagesHelper.createMessageElement(scope, opts);
187 | var ngModelCtrl = ctrls[0];
188 | var elemParent = elem.parent();
189 |
190 | var showStatusMessage = function () {
191 | if (ngModelCtrl.$invalid) {
192 | window.ngModelCtrl = ngModelCtrl;
193 | messageElem.removeClass(opts.hideClassName);
194 | if (elem.parent().hasClass(opts.parentContainerClassName)) {
195 | elemParent.addClass(opts.parentErrorClassName);
196 | }
197 | } else {
198 | if (elemParent.hasClass(opts.parentContainerClassName)) {
199 | elemParent.addClass(opts.parentSuccessClassName);
200 | }
201 | }
202 | };
203 |
204 | var hideMessage = function () {
205 | messageElem.addClass(opts.hideClassName);
206 | if (elemParent.hasClass(opts.parentContainerClassName)) {
207 | elemParent.removeClass(opts.parentErrorClassName);
208 | elemParent.removeClass(opts.parentSuccessClassName);
209 | }
210 | };
211 |
212 | elem.data('message-element', messageElem);
213 | elem.after(messageElem);
214 |
215 | //set up show message trigger
216 | switch (opts.showTrigger) {
217 | case 'blur':
218 | case 'keydown':
219 | case 'keyup':
220 | elem.on(opts.showTrigger, function () {
221 | showStatusMessage();
222 | });
223 | break;
224 | case 'submit':
225 | //we always show the errors when its submitted.. this option is for clarification only
226 | break;
227 | default:
228 | throw 'Show trigger "' + opts.showTrigger + '" is not supported';
229 | }
230 |
231 | //we'll always show
232 | scope.$on('submit', function () {
233 | showStatusMessage();
234 | });
235 |
236 | //set up hide message trigger
237 | switch (opts.hideTrigger) {
238 | case 'valid':
239 | scope.$watch(function () {
240 | return ngModelCtrl.$valid;
241 | }, function (isValid, wasValid) {
242 | if (isValid !== wasValid) {
243 | hideMessage();
244 | }
245 | });
246 | break;
247 | case 'keydown':
248 | case 'keyup':
249 | elem.on(opts.hideTrigger, function () {
250 | hideMessage();
251 | });
252 | break;
253 | default:
254 | throw 'Unsupported hide trigger used';
255 | }
256 |
257 | //each time the error changes, update the error message
258 | scope.$watch(function () {
259 | return ngModelCtrl.$error;
260 | }, function (newError) {
261 | if (ngModelCtrl.$invalid) {
262 | messageElem.find('msg').text(ValidationMessagesHelper.getErrorMessage(newError, elem, opts));
263 | }
264 | }, true);
265 |
266 | };
267 | }
268 | };
269 | }]);
270 | })();
271 |
--------------------------------------------------------------------------------
/coverage/PhantomJS 1.9.8 (Mac OS X)/src/time-heatmap/time-directive.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Code coverage report for src/time-heatmap/time-directive.js
5 |
6 |
7 |
8 |
9 |
196 |
197 |
198 |
217 |
223 |
226 |
227 |
228 |
229 |
230 |
321 |
322 |
323 |
--------------------------------------------------------------------------------
/coverage/PhantomJS 1.9.8 (Mac OS X)/src/module.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Code coverage report for src/module.js
5 |
6 |
7 |
8 |
9 |
196 |
197 |
198 |
217 |
218 |
219 | | 1
220 | 2
221 | 3 |
222 | 1
223 | | 'use strict';
224 | angular.module('gg.vmsgs', []);
225 | |
226 |
227 |
228 |
229 |
232 |
233 |
234 |
235 |
236 |
327 |
328 |
329 |
--------------------------------------------------------------------------------
/coverage/PhantomJS 1.9.8 (Mac OS X)/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Code coverage report for All files
5 |
6 |
7 |
8 |
9 |
196 |
197 |
198 |
217 |
218 |
219 |
220 |
221 |
222 | | File |
223 | |
224 | Statements |
225 | |
226 | Branches |
227 | |
228 | Functions |
229 | |
230 | Lines |
231 | |
232 |
233 |
234 |
235 | | src/ |
236 | |
237 | 100% |
238 | (92 / 92) |
239 | 100% |
240 | (56 / 56) |
241 | 100% |
242 | (35 / 35) |
243 | 100% |
244 | (92 / 92) |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
254 |
255 |
256 |
257 |
258 |
349 |
350 |
351 |
--------------------------------------------------------------------------------
/coverage/PhantomJS 1.9.8 (Mac OS X)/src/directive.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Code coverage report for src/directive.js
5 |
6 |
7 |
8 |
9 |
196 |
197 |
198 |
217 |
218 |
219 | | 1
220 | 2
221 | 3
222 | 4
223 | 5
224 | 6
225 | 7
226 | 8
227 | 9
228 | 10
229 | 11
230 | 12
231 | 13
232 | 14
233 | 15
234 | 16
235 | 17
236 | 18
237 | 19
238 | 20
239 | 21
240 | 22
241 | 23
242 | 24 | 1
243 |
244 | 1
245 | 2
246 |
247 |
248 |
249 |
250 | 1
251 | 2
252 | 1
253 | 1
254 | 1
255 |
256 |
257 |
258 |
259 |
260 |
261 | 1
262 |
263 |
264 |
265 | | (function () {
266 | 'use strict';
267 | function Yavd() {
268 | return {
269 | restrict: 'A',
270 | require: 'form',
271 | priority: -1,
272 | link: function (scope, elem, attrs, formCtrl) {
273 | elem.bind('submit', function (e){
274 | if (formCtrl.$invalid) {
275 | e.stopImmediatePropagation();
276 | window.formCtrl = formCtrl;
277 | debugger;
278 | }
279 | });
280 | }
281 | };
282 | }
283 |
284 | angular.module('gg.yavd')
285 | .directive('yavd', Yavd);
286 | })();
287 |
288 | |
289 |
290 |
291 |
292 |
295 |
296 |
297 |
298 |
299 |
390 |
391 |
392 |
--------------------------------------------------------------------------------
/coverage/PhantomJS 1.9.8 (Mac OS X)/src/heatmap/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Code coverage report for src/heatmap/
5 |
6 |
7 |
8 |
9 |
196 |
197 |
198 |
217 |
218 |
219 |
220 |
221 |
222 | | File |
223 | |
224 | Statements |
225 | |
226 | Branches |
227 | |
228 | Functions |
229 | |
230 | Lines |
231 | |
232 |
233 |
234 |
235 | | directive.js |
236 | |
237 | 11.76% |
238 | (2 / 17) |
239 | 0% |
240 | (0 / 6) |
241 | 0% |
242 | (0 / 7) |
243 | 11.76% |
244 | (2 / 17) |
245 |
246 |
247 |
248 | | service.js |
249 | |
250 | 65.35% |
251 | (83 / 127) |
252 | 64.52% |
253 | (40 / 62) |
254 | 77.78% |
255 | (21 / 27) |
256 | 65.35% |
257 | (83 / 127) |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
267 |
268 |
269 |
270 |
271 |
362 |
363 |
364 |
--------------------------------------------------------------------------------
/coverage/PhantomJS 1.9.8 (Mac OS X)/src/time-heatmap/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Code coverage report for src/time-heatmap/
5 |
6 |
7 |
8 |
9 |
196 |
197 |
198 |
217 |
218 |
219 |
220 |
221 |
222 | | File |
223 | |
224 | Statements |
225 | |
226 | Branches |
227 | |
228 | Functions |
229 | |
230 | Lines |
231 | |
232 |
233 |
234 |
235 | | time-directive.js |
236 | |
237 | 100% |
238 | (0 / 0) |
239 | 100% |
240 | (0 / 0) |
241 | 100% |
242 | (0 / 0) |
243 | 100% |
244 | (0 / 0) |
245 |
246 |
247 |
248 | | time-service.js |
249 | |
250 | 68.42% |
251 | (13 / 19) |
252 | 70% |
253 | (7 / 10) |
254 | 80% |
255 | (4 / 5) |
256 | 68.42% |
257 | (13 / 19) |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
267 |
268 |
269 |
270 |
271 |
362 |
363 |
364 |
--------------------------------------------------------------------------------
/coverage/PhantomJS 1.9.8 (Mac OS X)/src/form-directive.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Code coverage report for src/form-directive.js
5 |
6 |
7 |
8 |
9 |
196 |
197 |
198 |
217 |
218 |
219 | | 1
220 | 2
221 | 3
222 | 4
223 | 5
224 | 6
225 | 7
226 | 8
227 | 9
228 | 10
229 | 11
230 | 12
231 | 13
232 | 14
233 | 15
234 | 16
235 | 17
236 | 18
237 | 19
238 | 20
239 | 21
240 | 22
241 | 23
242 | 24
243 | 25
244 | 26
245 | 27
246 | 28
247 | 29
248 | 30
249 | 31
250 | 32
251 | 33
252 | 34
253 | 35
254 | 36
255 | 37
256 | 38
257 | 39
258 | 40
259 | 41 | 1
260 |
261 | 1
262 | 20
263 |
264 |
265 |
266 |
267 | 26
268 |
269 |
270 | 24
271 | 24
272 | 112
273 |
274 | 22
275 |
276 |
277 |
278 | 22
279 | 4
280 | 3
281 | 3
282 |
283 |
284 |
285 |
286 |
287 |
288 | 26
289 | 18
290 |
291 |
292 |
293 |
294 |
295 | 1
296 |
297 |
298 |
299 | | (function () {
300 | 'use strict';
301 | function ValidationMessagesForm($parse) {
302 | return {
303 | restrict: 'A',
304 | require: 'form',
305 | priority: -1,
306 | compile: function () {
307 | return {
308 | pre: function preLink(scope, elem, attrs) {
309 |
310 | scope.formOpts = $parse(attrs.vmsgForm || '')(scope);
311 | scope.$watch(function () {
312 | return attrs.vmsgForm;
313 | }, function (opts){
314 | scope.formOpts = $parse(opts || '')(scope);
315 | });
316 | },
317 | post: function postLink(scope, elem, attrs, formCtrl) {
318 | elem.bind('submit', function (e) {
319 | if (formCtrl.$invalid) {
320 | e.stopImmediatePropagation();
321 | scope.$broadcast('submit');
322 | }
323 | });
324 | }
325 | };
326 | },
327 | controller: function ($scope) {
328 | this.getOptions = function () {
329 | return $scope.formOpts;
330 | };
331 | }
332 | };
333 | }
334 |
335 | angular.module('gg.vmsgs')
336 | .directive('vmsgForm', ['$parse', ValidationMessagesForm]);
337 | })();
338 |
339 | |
340 |
341 |
342 |
343 |
346 |
347 |
348 |
349 |
350 |
441 |
442 |
443 |
--------------------------------------------------------------------------------
/coverage/PhantomJS 1.9.8 (Mac OS X)/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Code coverage report for src/
5 |
6 |
7 |
8 |
9 |
196 |
197 |
198 |
217 |
218 |
219 |
220 |
221 |
222 | | File |
223 | |
224 | Statements |
225 | |
226 | Branches |
227 | |
228 | Functions |
229 | |
230 | Lines |
231 | |
232 |
233 |
234 |
235 | | form-directive.js |
236 | |
237 | 100% |
238 | (15 / 15) |
239 | 100% |
240 | (6 / 6) |
241 | 100% |
242 | (10 / 10) |
243 | 100% |
244 | (15 / 15) |
245 |
246 |
247 |
248 | | helper.js |
249 | |
250 | 100% |
251 | (32 / 32) |
252 | 100% |
253 | (31 / 31) |
254 | 100% |
255 | (12 / 12) |
256 | 100% |
257 | (32 / 32) |
258 |
259 |
260 |
261 | | model-directive.js |
262 | |
263 | 100% |
264 | (44 / 44) |
265 | 100% |
266 | (19 / 19) |
267 | 100% |
268 | (13 / 13) |
269 | 100% |
270 | (44 / 44) |
271 |
272 |
273 |
274 | | module.js |
275 | |
276 | 100% |
277 | (1 / 1) |
278 | 100% |
279 | (0 / 0) |
280 | 100% |
281 | (0 / 0) |
282 | 100% |
283 | (1 / 1) |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
293 |
294 |
295 |
296 |
297 |
388 |
389 |
390 |
--------------------------------------------------------------------------------
/coverage/PhantomJS 1.9.8 (Mac OS X)/prettify.js:
--------------------------------------------------------------------------------
1 | window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^