jquery-form-validator-rails 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ OTQwOGFiMjk2MTYwNGE2NGQxNzdmNDVlYWUyNGZiNDFkNjJjNzA2Yw==
5
+ data.tar.gz: !binary |-
6
+ MzFlZDVkOWZhY2FlNzczZTk1NDkzNjcyMGRiMGVlMDc5NmE4M2Y4Ng==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ZmZiMGRjYjAwYjBlOTZhMzk0NjNkMDAwYWUyNmUxZjM2Njk2OWYzYmI4YjU5
10
+ YzM1OWY5MmE4YzE3ZmNiYjE5ZWMyZTkzMTkwMDEzY2RlODdlYzI5ZGNmMWI2
11
+ ZmFkY2Q0OTM1YTU1OGRkMDY4YjY3NDViMzYxM2QxNzBiZTA1MjE=
12
+ data.tar.gz: !binary |-
13
+ ZGM1MTdhNjFkN2Q1Y2JjMTFkOTFmYTU3MDRlN2IxNjYxZmIzOTY0ZjliOWIx
14
+ OGM1OTA3M2Q4ZDkzMDM4YmYxZWQ5ODc5NTY0ZTY5ZmRlZmRiMmMwNjQ3MmUz
15
+ YjU2NjhhNWFmMzc0NzEwM2VlNGZlOGFjOGE0OWUwN2FmY2RmMTY=
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,4 @@
1
+
2
+ ## 0.0.1
3
+
4
+ * Initialize the jQuery form validator gem
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in jquery-form-validator-rails.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ Copyright (c) 2014 Gustaf Lindqvist
2
+
3
+ jQuery Copyright (c)2012 jQuery Foundation and other contributors
4
+
5
+ MIT License
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining
8
+ a copy of this software and associated documentation files (the
9
+ "Software"), to deal in the Software without restriction, including
10
+ without limitation the rights to use, copy, modify, merge, publish,
11
+ distribute, sublicense, and/or sell copies of the Software, and to
12
+ permit persons to whom the Software is furnished to do so, subject to
13
+ the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be
16
+ included in all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,51 @@
1
+ # Jquery::Form::Validator::Rails
2
+
3
+
4
+ ## Installation
5
+
6
+ Add this line to your application's Gemfile:
7
+
8
+ gem 'jquery-form-validator-rails'
9
+
10
+ And then execute:
11
+
12
+ $ bundle
13
+
14
+ Add `jquery-form-validator-rails` to your Gemfile and run `bundle install`:
15
+
16
+ gem "jquery-form-validator-rails"
17
+
18
+ ### Include jquery.form-validator-rails javascript assets
19
+
20
+ Add the following to your `app/assets/javascripts/application.js`:
21
+
22
+ //= require jquery.form-validator
23
+
24
+ ### Add modules:
25
+
26
+ * security
27
+ * date
28
+ * location
29
+ * file
30
+ * sweden
31
+ * uk
32
+
33
+ Read the documentation for the modules at http://formvalidator.net
34
+
35
+ <script>
36
+ $.validate({
37
+ modules : 'security'
38
+ });
39
+ </script>
40
+
41
+ ## Changes
42
+
43
+ See [CHANGELOG.md](CHANGELOG.md) in this repository for detailed changes.
44
+
45
+ ## Contributing makes this repo happy!
46
+
47
+ 1. Fork it
48
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
49
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
50
+ 4. Push to the branch (`git push origin my-new-feature`)
51
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1 @@
1
+ (function($){$.formUtils.addValidator({name:"time",validatorFunction:function(time){if(time.match(/^(\d{2}):(\d{2})$/)===null){return false}else{var hours=parseInt(time.split(":")[0],10);var minutes=parseInt(time.split(":")[1],10);if(hours>23||minutes>59){return false}}return true},errorMessage:"",errorMessageKey:"badTime"});$.formUtils.addValidator({name:"birthdate",validatorFunction:function(val,$el,conf){var dateFormat="yyyy-mm-dd";if($el.valAttr("format")){dateFormat=$el.valAttr("format")}else if(typeof conf.dateFormat!="undefined"){dateFormat=conf.dateFormat}var inputDate=$.formUtils.parseDate(val,dateFormat);if(!inputDate){return false}var d=new Date;var currentYear=d.getFullYear();var year=inputDate[0];var month=inputDate[1];var day=inputDate[2];if(year===currentYear){var currentMonth=d.getMonth()+1;if(month===currentMonth){var currentDay=d.getDate();return day<=currentDay}else{return month<currentMonth}}else{return year<currentYear&&year>currentYear-124}},errorMessage:"",errorMessageKey:"badDate"})})(jQuery);
@@ -0,0 +1 @@
1
+ (function($,window){var SUPPORTS_FILE_READER=typeof window.FileReader!="undefined",_getTypes=function($input){var allowedTypes=$.split(($input.valAttr("allowing")||"").toLowerCase());if($.inArray("jpg",allowedTypes)>-1&&$.inArray("jpeg",allowedTypes)==-1)allowedTypes.push("jpeg");else if($.inArray("jpeg",allowedTypes)>-1&&$.inArray("jpg",allowedTypes)==-1)allowedTypes.push("jpg");return allowedTypes};$.formUtils.addValidator({name:"mime",validatorFunction:function(str,$input){var files=$input.get(0).files||[];if(SUPPORTS_FILE_READER){var valid=true,mime="",allowedTypes=_getTypes($input);$.each(files,function(i,file){valid=false;mime=file.type||"";$.each(allowedTypes,function(j,type){valid=mime.indexOf(type)>-1;if(valid){return false}});return valid});return valid}else{return $.formUtils.validators.validate_extension.validatorFunction(str,$input)}},errorMessage:"The file you are trying to upload is of wrong type",errorMessageKey:"wrongFileType"});$.formUtils.addValidator({name:"extension",validatorFunction:function(value,$input){var valid=true,types=_getTypes($input);$.each($input.get(0).files||[],function(i,file){var val=file.value,ext=val.substr(val.lastIndexOf(".")+1);if($.inArray(ext.toLowerCase(),types)==-1){valid=false;return false}});return valid},errorMessage:"The file you are trying to upload is of wrong type",errorMessageKey:"wrongFileType"});$.formUtils.addValidator({name:"size",validatorFunction:function(val,$input){var maxSize=$input.valAttr("max-size");if(!maxSize){console.log('Input "'+$input.attr("name")+'" is missing data-validation-max-size attribute');return true}else if(!SUPPORTS_FILE_READER){return true}var maxBytes=$.formUtils.convertSizeNameToBytes(maxSize),valid=true;$.each($input.get(0).files||[],function(i,file){valid=file.size<=maxBytes;return valid});return valid},errorMessage:"The file you are trying to upload is too large",errorMessageKey:"wrongFileSize"});$.formUtils.convertSizeNameToBytes=function(sizeName){sizeName=sizeName.toUpperCase();if(sizeName.substr(sizeName.length-1,1)=="M"){return parseInt(sizeName.substr(0,sizeName.length-1),10)*1024*1024}else if(sizeName.substr(sizeName.length-2,2)=="MB"){return parseInt(sizeName.substr(0,sizeName.length-2),10)*1024*1024}else if(sizeName.substr(sizeName.length-2,2)=="KB"){return parseInt(sizeName.substr(0,sizeName.length-2),10)*1024}else if(sizeName.substr(sizeName.length-1,1)=="B"){return parseInt(sizeName.substr(0,sizeName.length-1),10)}else{return parseInt(sizeName,10)}};$(window).one("validatorsLoaded formValidationSetup",function(evt,$form){var $inputs;if($form){$inputs=$form.find('input[type="file"]')}else{$inputs=$('input[type="file"]')}$inputs.filter("*[data-validation]").bind("change",function(){$(this).removeClass("error").parent().find(".form-error").remove()})})})(jQuery,window);
@@ -0,0 +1 @@
1
+ (function($,window){"use strict";var SUPPORTS_PLACEHOLDER="placeholder"in document.createElement("INPUT"),SUPPORTS_DATALIST="options"in document.createElement("DATALIST");$(window).bind("validatorsLoaded formValidationSetup",function(evt,$form){if(!$form){$form=$("form")}var hasLoadedDateModule=false;$form.each(function(){var $f=$(this),$formInputs=$f.find("input,textarea,select"),foundHtml5Rule=false;$formInputs.each(function(){var validation=[],$input=$(this),isRequired=$input.attr("required"),attrs={};switch(($input.attr("type")||"").toLowerCase()){case"time":validation.push("time");if(!$.formUtils.validators.validate_date&&!hasLoadedDateModule){hasLoadedDateModule=true;$.formUtils.loadModules("date")}break;case"url":validation.push("url");break;case"email":validation.push("email");break;case"date":validation.push("date");break;case"number":validation.push("number");var max=$input.attr("max"),min=$input.attr("min");if(min||max){if(!min)min=0;if(!max)max=9007199254740992;attrs["data-validation-allowing"]="range["+min+";"+max+"]";if(min.indexOf("-")===0||max.indexOf("-")===0){attrs["data-validation-allowing"]+=",negative"}if(min.indexOf(".")>-1||max.indexOf(".")>-1){attrs["data-validation-allowing"]+=",float"}}break}if($input.attr("pattern")){validation.push("custom");attrs["data-validation-regexp"]=$input.attr("pattern")}if($input.attr("maxlength")){validation.push("length");attrs["data-validation-length"]="max"+$input.attr("maxlength")}console.log($input.html());if(!SUPPORTS_DATALIST&&$input.attr("list")){console.log($input.attr("list"));var suggestions=[];$("#"+$input.attr("list")+" option").each(function(){var $opt=$(this);suggestions.push($opt.attr("value")||$opt.text())});$.formUtils.suggest($input,suggestions)}if(validation.length){if(!isRequired){attrs["data-validation-optional"]="true"}foundHtml5Rule=true;$input.attr("data-validation",validation.join(" "));$.each(attrs,function(attrName,attrVal){$input.attr(attrName,attrVal)})}});if(foundHtml5Rule){$f.trigger("html5ValidationAttrsFound")}if(!SUPPORTS_PLACEHOLDER){$formInputs.filter("input[placeholder]").each(function(){this.defaultValue=this.getAttribute("placeholder");$(this).bind("focus",function(){if(this.value==this.defaultValue){this.value="";$(this).removeClass("showing-placeholder")}}).bind("blur",function(){if($.trim(this.value)==""){this.value=this.defaultValue;$(this).addClass("showing-placeholder")}})})}})})})(jQuery,window);
@@ -0,0 +1,1681 @@
1
+ /**
2
+ * jQuery Form Validator
3
+ * ------------------------------------------
4
+ * Created by Victor Jonsson <http://www.victorjonsson.se>
5
+ *
6
+ * @website http://formvalidator.net/
7
+ * @license Dual licensed under the MIT or GPL Version 2 licenses
8
+ * @version 2.1.66
9
+ */
10
+ (function($) {
11
+
12
+ 'use strict';
13
+
14
+ var $window = $(window),
15
+ _applyErrorStyle = function($elem, conf) {
16
+ $elem
17
+ .addClass(conf.errorElementClass)
18
+ .removeClass('valid')
19
+ .parent()
20
+ .addClass('has-error')
21
+ .removeClass('has-success'); // twitter bs
22
+
23
+ if(conf.borderColorOnError !== '') {
24
+ $elem.css('border-color', conf.borderColorOnError);
25
+ }
26
+ },
27
+ _removeErrorStyle = function($elem, conf) {
28
+ $elem.each(function() {
29
+ _setInlineErrorMessage($(this), '', conf, conf.errorMessagePosition);
30
+ $(this)
31
+ .removeClass('valid')
32
+ .removeClass(conf.errorElementClass)
33
+ .css('border-color', '')
34
+ .parent()
35
+ .removeClass('has-error')
36
+ .removeClass('has-success')
37
+ .find('.'+conf.errorMessageClass) // remove inline error message
38
+ .remove();
39
+ });
40
+ },
41
+ _setInlineErrorMessage = function($input, mess, conf, $messageContainer) {
42
+ var custom = _getInlineErrorElement($input);
43
+ if( custom ) {
44
+ custom.innerHTML = mess;
45
+ }
46
+ else if( typeof $messageContainer == 'object' ) {
47
+ var $found = false;
48
+ $messageContainer.find('.'+conf.errorMessageClass).each(function() {
49
+ if( this.inputReferer == $input[0] ) {
50
+ $found = $(this);
51
+ return false;
52
+ }
53
+ });
54
+ if( $found ) {
55
+ if( !mess ) {
56
+ $found.remove();
57
+ } else {
58
+ $found.html(mess);
59
+ }
60
+ } else {
61
+ var $mess = $('<div class="'+conf.errorMessageClass+'">'+mess+'</div>');
62
+ $mess[0].inputReferer = $input[0];
63
+ $messageContainer.prepend($mess);
64
+ }
65
+ }
66
+ else {
67
+ var $mess = $input.parent().find('.'+conf.errorMessageClass+'.help-block');
68
+ if( $mess.length == 0 ) {
69
+ $mess = $('<span></span>').addClass('help-block').addClass(conf.errorMessageClass);
70
+ $mess.appendTo($input.parent());
71
+ }
72
+ $mess.html(mess);
73
+ }
74
+ },
75
+ _getInlineErrorElement = function($input, conf) {
76
+ return document.getElementById($input.attr('name')+'_err_msg');
77
+ },
78
+ _templateMessage = function($form, title, errorMessages, conf) {
79
+ var messages = conf.errorMessageTemplate.messages.replace(/\{errorTitle\}/g, title);
80
+ var fields = [];
81
+ $.each(errorMessages, function(i, msg) {
82
+ fields.push(conf.errorMessageTemplate.field.replace(/\{msg\}/g, msg));
83
+ });
84
+ messages = messages.replace(/\{fields\}/g, fields.join(''));
85
+ var container = conf.errorMessageTemplate.container.replace(/\{errorMessageClass\}/g, conf.errorMessageClass);
86
+ container = container.replace(/\{messages\}/g, messages);
87
+ $form.children().eq(0).before(container);
88
+ };
89
+
90
+ /**
91
+ * Assigns validateInputOnBlur function to elements blur event
92
+ *
93
+ * @param {Object} language Optional, will override $.formUtils.LANG
94
+ * @param {Object} settings Optional, will override the default settings
95
+ * @return {jQuery}
96
+ */
97
+ $.fn.validateOnBlur = function(language, settings) {
98
+ this.find('input[data-validation],textarea[data-validation],select[data-validation]')
99
+ .bind('blur.validation', function() {
100
+ $(this).validateInputOnBlur(language, settings);
101
+ });
102
+
103
+ return this;
104
+ };
105
+
106
+ /*
107
+ * Assigns validateInputOnBlur function to elements custom event
108
+ * @param {Object} language Optional, will override $.formUtils.LANG
109
+ * @param {Object} settings Optional, will override the default settings
110
+ * * @return {jQuery}
111
+ */
112
+ $.fn.validateOnEvent = function(language, settings) {
113
+ this.find('input[data-validation][data-validation-event],textarea[data-validation][data-validation-event],select[data-validation][data-validation-event]')
114
+ .each(function(){
115
+ var $el = $(this),
116
+ etype = $el.attr("data-validation-event");
117
+ if (etype){
118
+ $el.bind(etype + ".validation", function(){
119
+ $(this).validateInputOnBlur(language, settings, false, etype);
120
+ });
121
+ }
122
+ });
123
+ return this;
124
+ };
125
+
126
+ /**
127
+ * fade in help message when input gains focus
128
+ * fade out when input loses focus
129
+ * <input data-help="The info that I want to display for the user when input is focused" ... />
130
+ *
131
+ * @param {String} attrName - Optional, default is data-help
132
+ * @return {jQuery}
133
+ */
134
+ $.fn.showHelpOnFocus = function(attrName) {
135
+ if(!attrName) {
136
+ attrName = 'data-validation-help';
137
+ }
138
+
139
+ // Remove previously added event listeners
140
+ this.find('.has-help-txt')
141
+ .valAttr('has-keyup-event', false)
142
+ .valAttr('backend-valid', false)
143
+ .valAttr('backend-invalid', false)
144
+ .removeClass('has-help-txt');
145
+
146
+ // Add help text listeners
147
+ this.find('textarea,input').each(function() {
148
+ var $elem = $(this),
149
+ className = 'jquery_form_help_' + ($elem.attr('name') || '').replace( /(:|\.|\[|\])/g, "" ),
150
+ help = $elem.attr(attrName);
151
+
152
+ if(help) {
153
+ $elem
154
+ .addClass('has-help-txt')
155
+ .unbind('focus.help')
156
+ .bind('focus.help', function() {
157
+ var $help = $elem.parent().find('.'+className);
158
+ if($help.length == 0) {
159
+ $help = $('<span />')
160
+ .addClass(className)
161
+ .addClass('help')
162
+ .addClass('help-block') // twitter bs
163
+ .text(help)
164
+ .hide();
165
+
166
+ $elem.after($help);
167
+
168
+ }
169
+ $help.fadeIn();
170
+ })
171
+ .unbind('blur.help')
172
+ .bind('blur.help', function() {
173
+ $(this)
174
+ .parent()
175
+ .find('.'+className)
176
+ .fadeOut('slow');
177
+ });
178
+ }
179
+ });
180
+
181
+ return this;
182
+ };
183
+
184
+ /**
185
+ * Validate single input when it loses focus
186
+ * shows error message in a span element
187
+ * that is appended to the parent element
188
+ *
189
+ * @param {Object} [language] Optional, will override $.formUtils.LANG
190
+ * @param {Object} [conf] Optional, will override the default settings
191
+ * @param {Boolean} [attachKeyupEvent] Optional
192
+ * @param {String} [eventContext]
193
+ * @return {jQuery}
194
+ */
195
+ $.fn.validateInputOnBlur = function(language, conf, attachKeyupEvent, eventContext) {
196
+ if(attachKeyupEvent === undefined)
197
+ attachKeyupEvent = true;
198
+ if(!eventContext)
199
+ eventContext = 'blur';
200
+
201
+ if( (this.valAttr('suggestion-nr') || this.valAttr('postpone') || this.hasClass('hasDatepicker')) && !window.postponedValidation ) {
202
+ // This validation has to be postponed
203
+ var _self = this,
204
+ postponeTime = this.valAttr('postpone') || 200;
205
+
206
+ window.postponedValidation = function() {
207
+ _self.validateInputOnBlur(language, conf, attachKeyupEvent);
208
+ window.postponedValidation = false;
209
+ };
210
+ setTimeout(function() {
211
+ if( window.postponedValidation ) {
212
+ window.postponedValidation();
213
+ }
214
+ }, postponeTime);
215
+
216
+ return this;
217
+ }
218
+
219
+ language = $.extend({}, $.formUtils.LANG, language || {});
220
+ _removeErrorStyle(this, conf);
221
+
222
+ var $elem = this,
223
+ $form = $elem.closest("form"),
224
+ validationRule = $elem.attr(conf.validationRuleAttribute),
225
+ validation = $.formUtils.validateInput(
226
+ $elem,
227
+ language,
228
+ $.extend({}, conf, {errorMessagePosition:'element'}),
229
+ $form,
230
+ eventContext
231
+ );
232
+
233
+ $elem.trigger('validation', [validation===null ? null : validation===true]);
234
+
235
+ if(validation === true) {
236
+ $elem
237
+ .addClass('valid')
238
+ .parent()
239
+ .addClass('has-success'); // twitter bs
240
+ } else if(validation !== null) {
241
+
242
+ _applyErrorStyle($elem, conf);
243
+ _setInlineErrorMessage($elem, validation, conf, conf.errorMessagePosition);
244
+
245
+ if(attachKeyupEvent) {
246
+ $elem.bind('keyup', function() {
247
+ $(this).validateInputOnBlur(language, conf, false, 'keyup');
248
+ });
249
+ }
250
+ }
251
+
252
+ return this;
253
+ };
254
+
255
+ /**
256
+ * Short hand for fetching/adding/removing element attributes
257
+ * prefixed with 'data-validation-'
258
+ *
259
+ * @param {String} name
260
+ * @param {String|Boolean} [val]
261
+ * @return string|undefined
262
+ * @protected
263
+ */
264
+ $.fn.valAttr = function(name, val) {
265
+ if( val === undefined ) {
266
+ return this.attr('data-validation-'+name);
267
+ } else if( val === false || val === null ) {
268
+ return this.removeAttr('data-validation-'+name);
269
+ } else {
270
+ if(name.length > 0) name='-'+name;
271
+ return this.attr('data-validation'+name, val);
272
+ }
273
+ };
274
+
275
+ /**
276
+ * Function that validate all inputs in given element
277
+ *
278
+ * @param {Object} [language]
279
+ * @param {Object} [conf]
280
+ * @param {Boolean} [displayError] Defaults to true
281
+ */
282
+ $.fn.isValid = function(language, conf, displayError) {
283
+
284
+ if ($.formUtils.isLoadingModules) {
285
+ var $self = this;
286
+ setTimeout(function () {
287
+ $self.isValid(language, conf, displayError);
288
+ }, 200);
289
+ return null;
290
+ }
291
+
292
+ conf = $.extend({}, $.formUtils.defaultConfig(), conf || {});
293
+ language = $.extend({}, $.formUtils.LANG, language || {});
294
+ displayError = displayError !== false;
295
+
296
+ $.formUtils.isValidatingEntireForm = true;
297
+ $.formUtils.haltValidation = false;
298
+
299
+ /**
300
+ * Adds message to error message stack if not already in the message stack
301
+ *
302
+ * @param {String} mess
303
+ * @para {jQuery} $elem
304
+ */
305
+ var addErrorMessage = function(mess, $elem) {
306
+ // validate server side will return null as error message before the server is requested
307
+ if(mess !== null) {
308
+ if ($.inArray(mess, errorMessages) < 0) {
309
+ errorMessages.push(mess);
310
+ }
311
+ errorInputs.push($elem);
312
+ $elem.attr('current-error', mess);
313
+ if( displayError )
314
+ _applyErrorStyle($elem, conf);
315
+ }
316
+ },
317
+
318
+ /** Error messages for this validation */
319
+ errorMessages = [],
320
+
321
+ /** Input elements which value was not valid */
322
+ errorInputs = [],
323
+
324
+ /** Form instance */
325
+ $form = this,
326
+
327
+ /**
328
+ * Tells whether or not to validate element with this name and of this type
329
+ *
330
+ * @param {String} name
331
+ * @param {String} type
332
+ * @return {Boolean}
333
+ */
334
+ ignoreInput = function(name, type) {
335
+ if (type === 'submit' || type === 'button' || type == 'reset') {
336
+ return true;
337
+ }
338
+ return $.inArray(name, conf.ignore || []) > -1;
339
+ };
340
+
341
+ // Reset style and remove error class
342
+ if( displayError ) {
343
+ $form.find('.'+conf.errorMessageClass+'.alert').remove();
344
+ _removeErrorStyle($form.find('.'+conf.errorElementClass+',.valid'), conf);
345
+ }
346
+
347
+ // Validate element values
348
+ $form.find('input,textarea,select').filter(':not([type="submit"],[type="button"])').each(function() {
349
+ var $elem = $(this);
350
+ var elementType = $elem.attr('type');
351
+ if (!ignoreInput($elem.attr('name'), elementType)) {
352
+
353
+ var validation = $.formUtils.validateInput(
354
+ $elem,
355
+ language,
356
+ conf,
357
+ $form,
358
+ 'submit'
359
+ );
360
+
361
+ $elem.trigger('validation', [validation===true]);
362
+
363
+ // Run element validation callback
364
+ if( typeof conf.onElementValidate == 'function' ) {
365
+ conf.onElementValidate((validation === true), $elem, $form, validation);
366
+ }
367
+
368
+ if(validation !== true) {
369
+ addErrorMessage(validation, $elem);
370
+ } else {
371
+ $elem
372
+ .valAttr('current-error', false)
373
+ .addClass('valid')
374
+ .parent()
375
+ .addClass('has-success');
376
+ }
377
+ }
378
+
379
+ });
380
+
381
+ // Run validation callback
382
+ if( typeof conf.onValidate == 'function' ) {
383
+ var errors = conf.onValidate($form);
384
+ if( $.isArray(errors) ) {
385
+ $.each(errors, function(i, err) {
386
+ addErrorMessage(err.message, err.element);
387
+ });
388
+ }
389
+ else if( errors && errors.element && errors.message ) {
390
+ addErrorMessage(errors.message, errors.element);
391
+ }
392
+ }
393
+
394
+ // Reset form validation flag
395
+ $.formUtils.isValidatingEntireForm = false;
396
+
397
+ // Validation failed
398
+ if ( !$.formUtils.haltValidation && errorInputs.length > 0 ) {
399
+
400
+ if( displayError ) {
401
+ // display all error messages in top of form
402
+ if (conf.errorMessagePosition === 'top') {
403
+ _templateMessage($form, language.errorTitle, errorMessages, conf);
404
+ }
405
+ // Customize display message
406
+ else if(conf.errorMessagePosition === 'custom') {
407
+ if( typeof conf.errorMessageCustom === 'function' ) {
408
+ conf.errorMessageCustom($form, language.errorTitle, errorMessages, conf);
409
+ }
410
+ }
411
+ // Display error message below input field or in defined container
412
+ else {
413
+ $.each(errorInputs, function(i, $input) {
414
+ _setInlineErrorMessage($input, $input.attr('current-error'), conf, conf.errorMessagePosition);
415
+ });
416
+ }
417
+
418
+ if(conf.scrollToTopOnError) {
419
+ $window.scrollTop($form.offset().top - 20);
420
+ }
421
+ }
422
+
423
+ return false;
424
+ }
425
+
426
+ return !$.formUtils.haltValidation;
427
+ };
428
+
429
+ /**
430
+ * @deprecated
431
+ * @param language
432
+ * @param conf
433
+ */
434
+ $.fn.validateForm = function(language, conf) {
435
+ if( window.console && typeof window.console.warn == 'function' ) {
436
+ window.console.warn('Use of deprecated function $.validateForm, use $.isValid instead');
437
+ }
438
+ return this.isValid(language, conf, true);
439
+ }
440
+
441
+ /**
442
+ * Plugin for displaying input length restriction
443
+ */
444
+ $.fn.restrictLength = function(maxLengthElement) {
445
+ new $.formUtils.lengthRestriction(this, maxLengthElement);
446
+ return this;
447
+ };
448
+
449
+ /**
450
+ * Add suggestion dropdown to inputs having data-suggestions with a comma
451
+ * separated string with suggestions
452
+ * @param {Array} [settings]
453
+ * @returns {jQuery}
454
+ */
455
+ $.fn.addSuggestions = function(settings) {
456
+ var sugs = false;
457
+ this.find('input').each(function() {
458
+ var $field = $(this);
459
+
460
+ sugs = $.split($field.attr('data-suggestions'));
461
+
462
+ if( sugs.length > 0 && !$field.hasClass('has-suggestions') ) {
463
+ $.formUtils.suggest($field, sugs, settings);
464
+ $field.addClass('has-suggestions');
465
+ }
466
+ });
467
+ return this;
468
+ };
469
+
470
+ /**
471
+ * A bit smarter split function
472
+ * @param {String} val
473
+ * @param {Function|String} [func]
474
+ * @param {String} [delim]
475
+ * @returns {Array|void}
476
+ */
477
+ $.split = function(val, func, delim) {
478
+ if( typeof func != 'function' ) {
479
+ // return string
480
+ if( !val )
481
+ return [];
482
+ var values = [];
483
+ $.each(val.split(func ? func:','), function(i,str) {
484
+ str = $.trim(str);
485
+ if( str.length )
486
+ values.push(str);
487
+ });
488
+ return values;
489
+ } else if( val ) {
490
+ // use callback on each
491
+ if( !delim )
492
+ delim = ',';
493
+ $.each(val.split(delim), function(i, str) {
494
+ str = $.trim(str);
495
+ if( str.length )
496
+ return func(str, i);
497
+ });
498
+ }
499
+ };
500
+
501
+ /**
502
+ * Short hand function that makes the validation setup require less code
503
+ * @param conf
504
+ */
505
+ $.validate = function(conf) {
506
+
507
+ var defaultConf = $.extend($.formUtils.defaultConfig(), {
508
+ form : 'form',
509
+ /*
510
+ * Enable custom event for validation
511
+ */
512
+ validateOnEvent : true,
513
+ validateOnBlur : true,
514
+ showHelpOnFocus : true,
515
+ addSuggestions : true,
516
+ modules : '',
517
+ onModulesLoaded : null,
518
+ language : false,
519
+ onSuccess : false,
520
+ onError : false,
521
+ onElementValidate : false
522
+ });
523
+
524
+ conf = $.extend(defaultConf, conf || {});
525
+
526
+ // Add validation to forms
527
+ $.split(conf.form, function(formQuery) {
528
+
529
+ var $form = $(formQuery);
530
+ $window.trigger('formValidationSetup', [$form]);
531
+
532
+ // Remove all event listeners previously added
533
+ $form.find('.has-help-txt')
534
+ .unbind('focus.validation')
535
+ .unbind('blur.validation');
536
+ $form
537
+ .removeClass('has-validation-callback')
538
+ .unbind('submit.validation')
539
+ .unbind('reset.validation')
540
+ .find('input[data-validation],textarea[data-validation]')
541
+ .unbind('blur.validation');
542
+
543
+ // Validate when submitted
544
+ $form.bind('submit.validation', function() {
545
+ var $form = $(this);
546
+
547
+ if($.formUtils.isLoadingModules) {
548
+ setTimeout(function() {
549
+ $form.trigger('submit.validation');
550
+ }, 200);
551
+ return false;
552
+ }
553
+ var valid = $form.isValid(conf.language, conf);
554
+ if( valid && typeof conf.onSuccess == 'function') {
555
+ var callbackResponse = conf.onSuccess($form);
556
+ if( callbackResponse === false )
557
+ return false;
558
+ } else if ( !valid && typeof conf.onError == 'function' ) {
559
+ conf.onError($form);
560
+ return false;
561
+ } else {
562
+ return valid;
563
+ }
564
+ })
565
+ .bind('reset.validation', function() {
566
+ // remove messages
567
+ $(this).find('.'+conf.errorMessageClass+'.alert').remove();
568
+ _removeErrorStyle($(this).find('.'+conf.errorElementClass+',.valid'), conf);
569
+ })
570
+ .addClass('has-validation-callback');
571
+
572
+ if( conf.showHelpOnFocus ) {
573
+ $form.showHelpOnFocus();
574
+ }
575
+ if( conf.addSuggestions ) {
576
+ $form.addSuggestions();
577
+ }
578
+ if( conf.validateOnBlur ) {
579
+ $form.validateOnBlur(conf.language, conf);
580
+ $form.bind('html5ValidationAttrsFound', function() {
581
+ $form.validateOnBlur(conf.language, conf);
582
+ })
583
+ }
584
+ if( conf.validateOnEvent ){
585
+ $form.validateOnEvent(conf.language, conf);
586
+ }
587
+
588
+ });
589
+
590
+ if( conf.modules != '' ) {
591
+ if( typeof conf.onModulesLoaded == 'function' ) {
592
+ $window.one('validatorsLoaded', conf.onModulesLoaded);
593
+ }
594
+ $.formUtils.loadModules(conf.modules);
595
+ }
596
+ };
597
+
598
+ /**
599
+ * Object containing utility methods for this plugin
600
+ */
601
+ $.formUtils = {
602
+
603
+ /**
604
+ * Default config for $(...).isValid();
605
+ */
606
+ defaultConfig : function() {
607
+ return {
608
+ ignore : [], // Names of inputs not to be validated even though node attribute containing the validation rules tells us to
609
+ errorElementClass : 'error', // Class that will be put on elements which value is invalid
610
+ borderColorOnError : 'red', // Border color of elements which value is invalid, empty string to not change border color
611
+ errorMessageClass : 'form-error', // class name of div containing error messages when validation fails
612
+ validationRuleAttribute : 'data-validation', // name of the attribute holding the validation rules
613
+ validationErrorMsgAttribute : 'data-validation-error-msg', // define custom err msg inline with element
614
+ errorMessagePosition : 'element', // Can be either "top" or "element" or "custom"
615
+ errorMessageTemplate : {
616
+ container: '<div class="{errorMessageClass} alert alert-danger">{messages}</div>',
617
+ messages: '<strong>{errorTitle}</strong><ul>{fields}</ul>',
618
+ field: '<li>{msg}</li>'
619
+ },
620
+ errorMessageCustom: _templateMessage,
621
+ scrollToTopOnError : true,
622
+ dateFormat : 'yyyy-mm-dd',
623
+ addValidClassOnAll : false, // whether or not to apply class="valid" even if the input wasn't validated
624
+ decimalSeparator : '.'
625
+ }
626
+ },
627
+
628
+ /**
629
+ * Available validators
630
+ */
631
+ validators : {},
632
+
633
+ /**
634
+ * Events triggered by form validator
635
+ */
636
+ _events : {load : [], valid: [], invalid:[]},
637
+
638
+ /**
639
+ * Setting this property to true during validation will
640
+ * stop further validation from taking place and form will
641
+ * not be sent
642
+ */
643
+ haltValidation : false,
644
+
645
+ /**
646
+ * This variable will be true $.fn.isValid() is called
647
+ * and false when $.fn.validateOnBlur is called
648
+ */
649
+ isValidatingEntireForm : false,
650
+
651
+ /**
652
+ * Function for adding a validator
653
+ * @param {Object} validator
654
+ */
655
+ addValidator : function(validator) {
656
+ // prefix with "validate_" for backward compatibility reasons
657
+ var name = validator.name.indexOf('validate_') === 0 ? validator.name : 'validate_'+validator.name;
658
+ if( validator.validateOnKeyUp === undefined )
659
+ validator.validateOnKeyUp = true;
660
+ this.validators[name] = validator;
661
+ },
662
+
663
+ /**
664
+ * @var {Boolean}
665
+ */
666
+ isLoadingModules : false,
667
+
668
+ /**
669
+ * @var {Object}
670
+ */
671
+ loadedModules : {},
672
+
673
+ /**
674
+ * @example
675
+ * $.formUtils.loadModules('date, security.dev');
676
+ *
677
+ * Will load the scripts date.js and security.dev.js from the
678
+ * directory where this script resides. If you want to load
679
+ * the modules from another directory you can use the
680
+ * path argument.
681
+ *
682
+ * The script will be cached by the browser unless the module
683
+ * name ends with .dev
684
+ *
685
+ * @param {String} modules - Comma separated string with module file names (no directory nor file extension)
686
+ * @param {String} [path] - Optional, path where the module files is located if their not in the same directory as the core modules
687
+ * @param {Boolean} [fireEvent] - Optional, whether or not to fire event 'load' when modules finished loading
688
+ */
689
+ loadModules : function(modules, path, fireEvent) {
690
+
691
+ if( fireEvent === undefined )
692
+ fireEvent = true;
693
+
694
+ if( $.formUtils.isLoadingModules ) {
695
+ setTimeout(function() {
696
+ $.formUtils.loadModules(modules, path, fireEvent);
697
+ });
698
+ return;
699
+ }
700
+
701
+ var hasLoadedAnyModule = false,
702
+ loadModuleScripts = function(modules, path) {
703
+
704
+ var moduleList = $.split(modules),
705
+ numModules = moduleList.length,
706
+ moduleLoadedCallback = function() {
707
+ numModules--;
708
+ if( numModules == 0 ) {
709
+ $.formUtils.isLoadingModules = false;
710
+ if( fireEvent && hasLoadedAnyModule ) {
711
+ $window.trigger('validatorsLoaded');
712
+ }
713
+ }
714
+ };
715
+
716
+ if( numModules > 0 ) {
717
+ $.formUtils.isLoadingModules = true;
718
+ }
719
+
720
+ var cacheSuffix = '?__='+( new Date().getTime() ),
721
+ appendToElement = document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0];
722
+
723
+ $.each(moduleList, function(i, modName) {
724
+ modName = $.trim(modName);
725
+ if( modName.length == 0 ) {
726
+ moduleLoadedCallback();
727
+ }
728
+ else {
729
+ var scriptUrl = path + modName + (modName.substr(-3) == '.js' ? '':'.js'),
730
+ script = document.createElement('SCRIPT');
731
+
732
+ if( scriptUrl in $.formUtils.loadedModules ) {
733
+ // already loaded
734
+ moduleLoadedCallback();
735
+ }
736
+ else {
737
+
738
+ // Remember that this script is loaded
739
+ $.formUtils.loadedModules[scriptUrl] = 1;
740
+ hasLoadedAnyModule = true;
741
+
742
+ // Load the script
743
+ script.type = 'text/javascript';
744
+ script.onload = moduleLoadedCallback;
745
+ script.src = scriptUrl + ( scriptUrl.substr(-7) == '.dev.js' ? cacheSuffix:'' );
746
+ script.onreadystatechange = function() {
747
+ // IE 7 fix
748
+ if( this.readyState == 'complete' || this.readyState == 'loaded' ) {
749
+ moduleLoadedCallback();
750
+ // Handle memory leak in IE
751
+ this.onload = null;
752
+ this.onreadystatechange = null;
753
+ }
754
+ };
755
+ appendToElement.appendChild( script );
756
+ }
757
+ }
758
+ });
759
+ };
760
+
761
+ if( path ) {
762
+ loadModuleScripts(modules, path);
763
+ } else {
764
+ var findScriptPathAndLoadModules = function() {
765
+ var foundPath = false;
766
+ $('script').each(function() {
767
+ if( this.src ) {
768
+ var scriptName = this.src.substr(this.src.lastIndexOf('/')+1, this.src.length);
769
+ if(scriptName.indexOf('jquery.form-validator.js') > -1 || scriptName.indexOf('jquery.form-validator.min.js') > -1) {
770
+ foundPath = this.src.substr(0, this.src.lastIndexOf('/')) + '/';
771
+ if( foundPath == '/' )
772
+ foundPath = '';
773
+ return false;
774
+ }
775
+ }
776
+ });
777
+
778
+ if( foundPath !== false) {
779
+ loadModuleScripts(modules, foundPath);
780
+ return true;
781
+ }
782
+ return false;
783
+ };
784
+
785
+ if( !findScriptPathAndLoadModules() ) {
786
+ $(findScriptPathAndLoadModules);
787
+ }
788
+ }
789
+ },
790
+
791
+ /**
792
+ * Validate the value of given element according to the validation rules
793
+ * found in the attribute data-validation. Will return true if valid,
794
+ * error message otherwise
795
+ *
796
+ * @param {jQuery} $elem
797
+ * @param {Object} language ($.formUtils.LANG)
798
+ * @param {Object} conf
799
+ * @param {jQuery} $form
800
+ * @param {String} [eventContext]
801
+ * @return {String|Boolean}
802
+ */
803
+ validateInput : function($elem, language, conf, $form, eventContext) {
804
+
805
+ if( $elem.attr('disabled') )
806
+ return null; // returning null will prevent that the valid class gets applied to the element
807
+
808
+ $elem.trigger('beforeValidation');
809
+
810
+ var value = $.trim( $elem.val() || ''),
811
+ optional = $elem.valAttr('optional'),
812
+
813
+ // test if a checkbox forces this element to be validated
814
+ validationDependsOnCheckedInput = false,
815
+ validationDependentInputIsChecked = false,
816
+ validateIfCheckedElement = false,
817
+
818
+ // get value of this element's attribute "... if-checked"
819
+ validateIfCheckedElementName = $elem.valAttr("if-checked");
820
+
821
+ // make sure we can proceed
822
+ if (validateIfCheckedElementName != null) {
823
+
824
+ // Set the boolean telling us that the validation depends
825
+ // on another input being checked
826
+ validationDependsOnCheckedInput = true;
827
+
828
+ // select the checkbox type element in this form
829
+ validateIfCheckedElement = $form.find('input[name="' + validateIfCheckedElementName + '"]');
830
+
831
+ // test if it's property "checked" is checked
832
+ if ( validateIfCheckedElement.prop('checked') ) {
833
+ // set value for validation checkpoint
834
+ validationDependentInputIsChecked = true;
835
+ }
836
+ }
837
+
838
+ // validation checkpoint
839
+ // if empty AND optional attribute is present
840
+ // OR depending on a checkbox being checked AND checkbox is checked, return true
841
+ if ((!value && optional === 'true') || (validationDependsOnCheckedInput && !validationDependentInputIsChecked)) {
842
+ return conf.addValidClassOnAll ? true:null;
843
+ }
844
+
845
+ var validationRules = $elem.attr(conf.validationRuleAttribute),
846
+
847
+ // see if form element has inline err msg attribute
848
+ validationErrorMsg = true;
849
+
850
+ if( !validationRules ) {
851
+ return conf.addValidClassOnAll ? true:null;
852
+ }
853
+
854
+ $.split(validationRules, function(rule) {
855
+ if( rule.indexOf('validate_') !== 0 ) {
856
+ rule = 'validate_' + rule;
857
+ }
858
+
859
+ var validator = $.formUtils.validators[rule];
860
+
861
+ if( validator && typeof validator['validatorFunction'] == 'function' ) {
862
+ // special change of element for checkbox_group rule
863
+ if ( rule == 'validate_checkbox_group' ) {
864
+ // set element to first in group, so error msg is set only once
865
+ $elem = $("[name='"+$elem.attr('name')+"']:eq(0)");
866
+ }
867
+
868
+ var isValid = true;
869
+ if( eventContext != 'keyup' || validator.validateOnKeyUp ) {
870
+ isValid = validator.validatorFunction(value, $elem, conf, language, $form);
871
+ }
872
+
873
+ if(!isValid) {
874
+ validationErrorMsg = $elem.attr(conf.validationErrorMsgAttribute+'-'+rule.replace('validate_', ''));
875
+ if( !validationErrorMsg ) {
876
+ validationErrorMsg = $elem.attr(conf.validationErrorMsgAttribute);
877
+ if( !validationErrorMsg ) {
878
+ validationErrorMsg = language[validator.errorMessageKey];
879
+ if( !validationErrorMsg )
880
+ validationErrorMsg = validator.errorMessage;
881
+ }
882
+ }
883
+ return false; // breaks the iteration
884
+ }
885
+
886
+ } else {
887
+ console.warn('Using undefined validator "'+rule+'"');
888
+ }
889
+
890
+ }, ' ');
891
+
892
+ if( typeof validationErrorMsg == 'string' ) {
893
+ return validationErrorMsg;
894
+ } else {
895
+ return true;
896
+ }
897
+ },
898
+
899
+ /**
900
+ * Is it a correct date according to given dateFormat. Will return false if not, otherwise
901
+ * an array 0=>year 1=>month 2=>day
902
+ *
903
+ * @param {String} val
904
+ * @param {String} dateFormat
905
+ * @return {Array}|{Boolean}
906
+ */
907
+ parseDate : function(val, dateFormat) {
908
+ var divider = dateFormat.replace(/[a-zA-Z]/gi, '').substring(0,1),
909
+ regexp = '^',
910
+ formatParts = dateFormat.split(divider),
911
+ matches, day, month, year;
912
+
913
+ $.each(formatParts, function(i, part) {
914
+ regexp += (i > 0 ? '\\'+divider:'') + '(\\d{'+part.length+'})';
915
+ });
916
+
917
+ regexp += '$';
918
+
919
+ matches = val.match(new RegExp(regexp));
920
+ if (matches === null) {
921
+ return false;
922
+ }
923
+
924
+ var findDateUnit = function(unit, formatParts, matches) {
925
+ for(var i=0; i < formatParts.length; i++) {
926
+ if(formatParts[i].substring(0,1) === unit) {
927
+ return $.formUtils.parseDateInt(matches[i+1]);
928
+ }
929
+ }
930
+ return -1;
931
+ };
932
+
933
+ month = findDateUnit('m', formatParts, matches);
934
+ day = findDateUnit('d', formatParts, matches);
935
+ year = findDateUnit('y', formatParts, matches);
936
+
937
+ if ((month === 2 && day > 28 && (year % 4 !== 0 || year % 100 === 0 && year % 400 !== 0))
938
+ || (month === 2 && day > 29 && (year % 4 === 0 || year % 100 !== 0 && year % 400 === 0))
939
+ || month > 12 || month === 0) {
940
+ return false;
941
+ }
942
+ if ((this.isShortMonth(month) && day > 30) || (!this.isShortMonth(month) && day > 31) || day === 0) {
943
+ return false;
944
+ }
945
+
946
+ return [year, month, day];
947
+ },
948
+
949
+ /**
950
+ * skum fix. är talet 05 eller lägre ger parseInt rätt int annars får man 0 när man kör parseInt?
951
+ *
952
+ * @param {String} val
953
+ * @param {Number}
954
+ */
955
+ parseDateInt : function(val) {
956
+ if (val.indexOf('0') === 0) {
957
+ val = val.replace('0', '');
958
+ }
959
+ return parseInt(val,10);
960
+ },
961
+
962
+ /**
963
+ * Has month only 30 days?
964
+ *
965
+ * @param {Number} m
966
+ * @return {Boolean}
967
+ */
968
+ isShortMonth : function(m) {
969
+ return (m % 2 === 0 && m < 7) || (m % 2 !== 0 && m > 7);
970
+ },
971
+
972
+ /**
973
+ * Restrict input length
974
+ *
975
+ * @param {jQuery} $inputElement Jquery Html object
976
+ * @param {jQuery} $maxLengthElement jQuery Html Object
977
+ * @return void
978
+ */
979
+ lengthRestriction : function($inputElement, $maxLengthElement) {
980
+ // read maxChars from counter display initial text value
981
+ var maxChars = parseInt($maxLengthElement.text(),10),
982
+ charsLeft = 0,
983
+
984
+ // internal function does the counting and sets display value
985
+ countCharacters = function() {
986
+ var numChars = $inputElement.val().length;
987
+ if(numChars > maxChars) {
988
+ // get current scroll bar position
989
+ var currScrollTopPos = $inputElement.scrollTop();
990
+ // trim value to max length
991
+ $inputElement.val($inputElement.val().substring(0, maxChars));
992
+ $inputElement.scrollTop(currScrollTopPos);
993
+ }
994
+ charsLeft = maxChars - numChars;
995
+ if( charsLeft < 0 )
996
+ charsLeft = 0;
997
+
998
+ // set counter text
999
+ $maxLengthElement.text(charsLeft);
1000
+ };
1001
+
1002
+ // bind events to this element
1003
+ // setTimeout is needed, cut or paste fires before val is available
1004
+ $($inputElement).bind('keydown keyup keypress focus blur', countCharacters )
1005
+ .bind('cut paste', function(){ setTimeout(countCharacters, 100); } ) ;
1006
+
1007
+ // count chars on pageload, if there are prefilled input-values
1008
+ $(document).bind("ready", countCharacters);
1009
+ },
1010
+
1011
+ /**
1012
+ * Test numeric against allowed range
1013
+ *
1014
+ * @param $value int
1015
+ * @param $rangeAllowed str; (1-2, min1, max2)
1016
+ * @return array
1017
+ */
1018
+ numericRangeCheck : function(value, rangeAllowed)
1019
+ {
1020
+ // split by dash
1021
+ var range = $.split(rangeAllowed, '-');
1022
+ // min or max
1023
+ var minmax = parseInt(rangeAllowed.substr(3),10)
1024
+ // range ?
1025
+ if (range.length == 2 && (value < parseInt(range[0],10) || value > parseInt(range[1],10) ) )
1026
+ { return [ "out", range[0], range[1] ] ; } // value is out of range
1027
+ else if (rangeAllowed.indexOf('min') === 0 && (value < minmax ) ) // min
1028
+ { return ["min", minmax]; } // value is below min
1029
+ else if (rangeAllowed.indexOf('max') === 0 && (value > minmax ) ) // max
1030
+ { return ["max", minmax]; } // value is above max
1031
+ else { return [ "ok" ] ; } // value is in allowed range
1032
+ },
1033
+
1034
+
1035
+ _numSuggestionElements : 0,
1036
+ _selectedSuggestion : null,
1037
+ _previousTypedVal : null,
1038
+
1039
+ /**
1040
+ * Utility function that can be used to create plugins that gives
1041
+ * suggestions when inputs is typed into
1042
+ * @param {jQuery} $elem
1043
+ * @param {Array} suggestions
1044
+ * @param {Object} settings - Optional
1045
+ * @return {jQuery}
1046
+ */
1047
+ suggest : function($elem, suggestions, settings) {
1048
+ var conf = {
1049
+ css : {
1050
+ maxHeight: '150px',
1051
+ background: '#FFF',
1052
+ lineHeight:'150%',
1053
+ textDecoration : 'underline',
1054
+ overflowX : 'hidden',
1055
+ overflowY : 'auto',
1056
+ border : '#CCC solid 1px',
1057
+ borderTop : 'none',
1058
+ cursor: 'pointer'
1059
+ },
1060
+ activeSuggestionCSS : {
1061
+ background : '#E9E9E9'
1062
+ }
1063
+ },
1064
+ setSuggsetionPosition = function($suggestionContainer, $input) {
1065
+ var offset = $input.offset();
1066
+ $suggestionContainer.css({
1067
+ width : $input.outerWidth(),
1068
+ left : offset.left + 'px',
1069
+ top : (offset.top + $input.outerHeight()) +'px'
1070
+ });
1071
+ };
1072
+
1073
+ if(settings)
1074
+ $.extend(conf, settings);
1075
+
1076
+ conf.css['position'] = 'absolute';
1077
+ conf.css['z-index'] = 9999;
1078
+ $elem.attr('autocomplete', 'off');
1079
+
1080
+ if( this._numSuggestionElements === 0 ) {
1081
+ // Re-position suggestion container if window size changes
1082
+ $window.bind('resize', function() {
1083
+ $('.jquery-form-suggestions').each(function() {
1084
+ var $container = $(this),
1085
+ suggestID = $container.attr('data-suggest-container');
1086
+ setSuggsetionPosition($container, $('.suggestions-'+suggestID).eq(0));
1087
+ });
1088
+ });
1089
+ }
1090
+
1091
+ this._numSuggestionElements++;
1092
+
1093
+ var onSelectSuggestion = function($el) {
1094
+ var suggestionId = $el.valAttr('suggestion-nr');
1095
+ $.formUtils._selectedSuggestion = null;
1096
+ $.formUtils._previousTypedVal = null;
1097
+ $('.jquery-form-suggestion-'+suggestionId).fadeOut('fast');
1098
+ };
1099
+
1100
+ $elem
1101
+ .data('suggestions', suggestions)
1102
+ .valAttr('suggestion-nr', this._numSuggestionElements)
1103
+ .unbind('focus.suggest')
1104
+ .bind('focus.suggest', function() {
1105
+ $(this).trigger('keyup');
1106
+ $.formUtils._selectedSuggestion = null;
1107
+ })
1108
+ .unbind('keyup.suggest')
1109
+ .bind('keyup.suggest', function() {
1110
+ var $input = $(this),
1111
+ foundSuggestions = [],
1112
+ val = $.trim($input.val()).toLocaleLowerCase();
1113
+
1114
+ if(val == $.formUtils._previousTypedVal) {
1115
+ return;
1116
+ }
1117
+ else {
1118
+ $.formUtils._previousTypedVal = val;
1119
+ }
1120
+
1121
+ var hasTypedSuggestion = false,
1122
+ suggestionId = $input.valAttr('suggestion-nr'),
1123
+ $suggestionContainer = $('.jquery-form-suggestion-'+suggestionId);
1124
+
1125
+ $suggestionContainer.scrollTop(0);
1126
+
1127
+ // Find the right suggestions
1128
+ if(val != '') {
1129
+ var findPartial = val.length > 2;
1130
+ $.each($input.data('suggestions'), function(i, suggestion) {
1131
+ var lowerCaseVal = suggestion.toLocaleLowerCase();
1132
+ if( lowerCaseVal == val ) {
1133
+ foundSuggestions.push('<strong>'+suggestion+'</strong>');
1134
+ hasTypedSuggestion = true;
1135
+ return false;
1136
+ } else if(lowerCaseVal.indexOf(val) === 0 || (findPartial && lowerCaseVal.indexOf(val) > -1)) {
1137
+ foundSuggestions.push(suggestion.replace(new RegExp(val, 'gi'), '<strong>$&</strong>'));
1138
+ }
1139
+ });
1140
+ }
1141
+
1142
+ // Hide suggestion container
1143
+ if(hasTypedSuggestion || (foundSuggestions.length == 0 && $suggestionContainer.length > 0)) {
1144
+ $suggestionContainer.hide();
1145
+ }
1146
+
1147
+ // Create suggestion container if not already exists
1148
+ else if(foundSuggestions.length > 0 && $suggestionContainer.length == 0) {
1149
+ $suggestionContainer = $('<div></div>').css(conf.css).appendTo('body');
1150
+ $elem.addClass('suggestions-'+suggestionId);
1151
+ $suggestionContainer
1152
+ .attr('data-suggest-container', suggestionId)
1153
+ .addClass('jquery-form-suggestions')
1154
+ .addClass('jquery-form-suggestion-'+suggestionId);
1155
+ }
1156
+
1157
+ // Show hidden container
1158
+ else if(foundSuggestions.length > 0 && !$suggestionContainer.is(':visible')) {
1159
+ $suggestionContainer.show();
1160
+ }
1161
+
1162
+ // add suggestions
1163
+ if(foundSuggestions.length > 0 && val.length != foundSuggestions[0].length) {
1164
+
1165
+ // put container in place every time, just in case
1166
+ setSuggsetionPosition($suggestionContainer, $input);
1167
+
1168
+ // Add suggestions HTML to container
1169
+ $suggestionContainer.html('');
1170
+ $.each(foundSuggestions, function(i, text) {
1171
+ $('<div></div>')
1172
+ .append(text)
1173
+ .css({
1174
+ overflow: 'hidden',
1175
+ textOverflow : 'ellipsis',
1176
+ whiteSpace : 'nowrap',
1177
+ padding: '5px'
1178
+ })
1179
+ .addClass('form-suggest-element')
1180
+ .appendTo($suggestionContainer)
1181
+ .click(function() {
1182
+ $input.focus();
1183
+ $input.val( $(this).text() );
1184
+ onSelectSuggestion($input);
1185
+ });
1186
+ });
1187
+ }
1188
+ })
1189
+ .unbind('keydown.validation')
1190
+ .bind('keydown.validation', function(e) {
1191
+ var code = (e.keyCode ? e.keyCode : e.which),
1192
+ suggestionId,
1193
+ $suggestionContainer,
1194
+ $input = $(this);
1195
+
1196
+ if(code == 13 && $.formUtils._selectedSuggestion !== null) {
1197
+ suggestionId = $input.valAttr('suggestion-nr');
1198
+ $suggestionContainer = $('.jquery-form-suggestion-'+suggestionId);
1199
+ if($suggestionContainer.length > 0) {
1200
+ var newText = $suggestionContainer.find('div').eq($.formUtils._selectedSuggestion).text();
1201
+ $input.val(newText);
1202
+ onSelectSuggestion($input);
1203
+ e.preventDefault();
1204
+ }
1205
+ }
1206
+ else {
1207
+ suggestionId = $input.valAttr('suggestion-nr');
1208
+ $suggestionContainer = $('.jquery-form-suggestion-'+suggestionId);
1209
+ var $suggestions = $suggestionContainer.children();
1210
+ if($suggestions.length > 0 && $.inArray(code, [38,40]) > -1) {
1211
+ if(code == 38) { // key up
1212
+ if($.formUtils._selectedSuggestion === null)
1213
+ $.formUtils._selectedSuggestion = $suggestions.length-1;
1214
+ else
1215
+ $.formUtils._selectedSuggestion--;
1216
+ if($.formUtils._selectedSuggestion < 0)
1217
+ $.formUtils._selectedSuggestion = $suggestions.length-1;
1218
+ }
1219
+ else if(code == 40) { // key down
1220
+ if($.formUtils._selectedSuggestion === null)
1221
+ $.formUtils._selectedSuggestion = 0;
1222
+ else
1223
+ $.formUtils._selectedSuggestion++;
1224
+ if($.formUtils._selectedSuggestion > ($suggestions.length-1))
1225
+ $.formUtils._selectedSuggestion = 0;
1226
+
1227
+ }
1228
+
1229
+ // Scroll in suggestion window
1230
+ var containerInnerHeight = $suggestionContainer.innerHeight(),
1231
+ containerScrollTop = $suggestionContainer.scrollTop(),
1232
+ suggestionHeight = $suggestionContainer.children().eq(0).outerHeight(),
1233
+ activeSuggestionPosY = suggestionHeight * ($.formUtils._selectedSuggestion);
1234
+
1235
+ if( activeSuggestionPosY < containerScrollTop || activeSuggestionPosY > (containerScrollTop+containerInnerHeight)) {
1236
+ $suggestionContainer.scrollTop( activeSuggestionPosY );
1237
+ }
1238
+
1239
+ $suggestions
1240
+ .removeClass('active-suggestion')
1241
+ .css('background', 'none')
1242
+ .eq($.formUtils._selectedSuggestion)
1243
+ .addClass('active-suggestion')
1244
+ .css(conf.activeSuggestionCSS);
1245
+
1246
+ e.preventDefault();
1247
+ return false;
1248
+ }
1249
+ }
1250
+ })
1251
+ .unbind('blur.suggest')
1252
+ .bind('blur.suggest', function() {
1253
+ onSelectSuggestion($(this));
1254
+ });
1255
+
1256
+ return $elem;
1257
+ },
1258
+
1259
+ /**
1260
+ * Error dialogs
1261
+ *
1262
+ * @var {Object}
1263
+ */
1264
+ LANG : {
1265
+ errorTitle : 'Form submission failed!',
1266
+ requiredFields : 'You have not answered all required fields',
1267
+ badTime : 'You have not given a correct time',
1268
+ badEmail : 'You have not given a correct e-mail address',
1269
+ badTelephone : 'You have not given a correct phone number',
1270
+ badSecurityAnswer : 'You have not given a correct answer to the security question',
1271
+ badDate : 'You have not given a correct date',
1272
+ lengthBadStart : 'You must give an answer between ',
1273
+ lengthBadEnd : ' characters',
1274
+ lengthTooLongStart : 'You have given an answer longer than ',
1275
+ lengthTooShortStart : 'You have given an answer shorter than ',
1276
+ notConfirmed : 'Values could not be confirmed',
1277
+ badDomain : 'Incorrect domain value',
1278
+ badUrl : 'The answer you gave was not a correct URL',
1279
+ badCustomVal : 'You gave an incorrect answer',
1280
+ badInt : 'The answer you gave was not a correct number',
1281
+ badSecurityNumber : 'Your isVsocial security number was incorrect',
1282
+ badUKVatAnswer : 'Incorrect UK VAT Number',
1283
+ badStrength : 'The password isn\'t strong enough',
1284
+ badNumberOfSelectedOptionsStart : 'You have to choose at least ',
1285
+ badNumberOfSelectedOptionsEnd : ' answers',
1286
+ badAlphaNumeric : 'The answer you gave must contain only alphanumeric characters ',
1287
+ badAlphaNumericExtra: ' and ',
1288
+ wrongFileSize : 'The file you are trying to upload is too large',
1289
+ wrongFileType : 'The file you are trying to upload is of wrong type',
1290
+ groupCheckedRangeStart : 'Please choose between ',
1291
+ groupCheckedTooFewStart : 'Please choose at least ',
1292
+ groupCheckedTooManyStart : 'Please choose a maximum of ',
1293
+ groupCheckedEnd : ' item(s)',
1294
+ badCreditCard : 'The credit card number is not correct',
1295
+ badCVV : 'The CVV number was not correct'
1296
+ }
1297
+ };
1298
+
1299
+
1300
+ /* * * * * * * * * * * * * * * * * * * * * *
1301
+ CORE VALIDATORS
1302
+ * * * * * * * * * * * * * * * * * * * * */
1303
+
1304
+
1305
+ /*
1306
+ * Validate email
1307
+ */
1308
+ $.formUtils.addValidator({
1309
+ name : 'email',
1310
+ validatorFunction : function(email) {
1311
+
1312
+ var emailParts = email.toLowerCase().split('@');
1313
+ if( emailParts.length == 2 ) {
1314
+ return $.formUtils.validators.validate_domain.validatorFunction(emailParts[1]) &&
1315
+ !(/[^\w\+\.\-]/.test(emailParts[0]));
1316
+ }
1317
+
1318
+ return false;
1319
+ },
1320
+ errorMessage : '',
1321
+ errorMessageKey : 'badEmail'
1322
+ });
1323
+
1324
+ /*
1325
+ * Validate domain name
1326
+ */
1327
+ $.formUtils.addValidator({
1328
+ name : 'domain',
1329
+ validatorFunction : function(val, $input) {
1330
+
1331
+ var topDomains = ['.ac', '.ad', '.ae', '.aero', '.af', '.ag', '.ai', '.al', '.am', '.an', '.ao',
1332
+ '.aq', '.ar', '.arpa', '.as', '.asia', '.at', '.au', '.aw', '.ax', '.az', '.ba', '.bb',
1333
+ '.bd', '.be', '.bf', '.bg', '.bh', '.bi', '.bike', '.biz', '.bj', '.bm', '.bn', '.bo',
1334
+ '.br', '.bs', '.bt', '.bv', '.bw', '.by', '.bz', '.ca', '.camera', '.cat', '.cc', '.cd',
1335
+ '.cf', '.cg', '.ch', '.ci', '.ck', '.cl', '.clothing', '.cm', '.cn', '.co', '.com',
1336
+ '.construction', '.contractors', '.coop', '.cr', '.cu', '.cv', '.cw', '.cx', '.cy', '.cz',
1337
+ '.de', '.diamonds', '.directory', '.dj', '.dk', '.dm', '.do', '.dz', '.ec', '.edu', '.ee',
1338
+ '.eg', '.enterprises', '.equipment', '.er', '.es', '.estate', '.et', '.eu', '.fi', '.fj',
1339
+ '.fk', '.fm', '.fo', '.fr', '.ga', '.gallery', '.gb', '.gd', '.ge', '.gf', '.gg', '.gh',
1340
+ '.gi', '.gl', '.gm', '.gn', '.gov', '.gp', '.gq', '.gr', '.graphics', '.gs', '.gt', '.gu',
1341
+ '.guru', '.gw', '.gy', '.hk', '.hm', '.hn', '.holdings', '.hr', '.ht', '.hu', '.id', '.ie',
1342
+ '.il', '.im', '.in', '.info', '.int', '.io', '.iq', '.ir', '.is', '.it', '.je', '.jm', '.jo',
1343
+ '.jobs', '.jp', '.ke', '.kg', '.kh', '.ki', '.kitchen', '.km', '.kn', '.kp', '.kr', '.kw',
1344
+ '.ky', '.kz', '.la', '.land', '.lb', '.lc', '.li', '.lighting', '.lk', '.lr', '.ls', '.lt',
1345
+ '.lu', '.lv', '.ly', '.ma', '.mc', '.md', '.me', '.menu', '.mg', '.mh', '.mil', '.mk', '.ml',
1346
+ '.mm', '.mn', '.mo', '.mobi', '.mp', '.mq', '.mr', '.ms', '.mt', '.mu', '.museum', '.mv',
1347
+ '.mw', '.mx', '.my', '.mz', '.na', '.name', '.nc', '.ne', '.net', '.nf', '.ng', '.ni',
1348
+ '.nl', '.no', '.np', '.nr', '.nu', '.nz', '.om', '.org', '.pa', '.pe', '.pf', '.pg', '.ph',
1349
+ '.photography', '.pk', '.pl', '.plumbing', '.pm', '.pn', '.post', '.pr', '.pro', '.ps', '.pt',
1350
+ '.pw', '.py', '.qa', '.re', '.ro', '.rs', '.ru', '.rw', '.sa', '.sb', '.sc', '.sd', '.se',
1351
+ '.sexy', '.sg', '.sh', '.si', '.singles', '.sj', '.sk', '.sl', '.sm', '.sn', '.so', '.sr',
1352
+ '.st', '.su', '.sv', '.sx', '.sy', '.sz', '.tattoo', '.tc', '.td', '.technology', '.tel', '.tf',
1353
+ '.tg', '.th', '.tips', '.tj', '.tk', '.tl', '.tm', '.tn', '.to', '.today', '.tp', '.tr', '.travel',
1354
+ '.tt', '.tv', '.tw', '.tz', '.ua', '.ug', '.uk', '.uno', '.us', '.uy', '.uz', '.va', '.vc', '.ve',
1355
+ '.ventures', '.vg', '.vi', '.vn', '.voyage', '.vu', '.wf', '.ws', '.xn--3e0b707e', '.xn--45brj9c',
1356
+ '.xn--80ao21a', '.xn--80asehdb', '.xn--80aswg', '.xn--90a3ac', '.xn--clchc0ea0b2g2a9gcd', '.xn--fiqs8s',
1357
+ '.xn--fiqz9s', '.xn--fpcrj9c3d', '.xn--fzc2c9e2c', '.xn--gecrj9c', '.xn--h2brj9c', '.xn--j1amh',
1358
+ '.xn--j6w193g', '.xn--kprw13d', '.xn--kpry57d', '.xn--l1acc', '.xn--lgbbat1ad8j', '.xn--mgb9awbf',
1359
+ '.xn--mgba3a4f16a', '.xn--mgbaam7a8h', '.xn--mgbayh7gpa', '.xn--mgbbh1a71e', '.xn--mgbc0a9azcg',
1360
+ '.xn--mgberp4a5d4ar', '.xn--mgbx4cd0ab', '.xn--ngbc5azd', '.xn--o3cw4h', '.xn--ogbpf8fl', '.xn--p1ai',
1361
+ '.xn--pgbs0dh', '.xn--q9jyb4c', '.xn--s9brj9c', '.xn--unup4y', '.xn--wgbh1c', '.xn--wgbl6a',
1362
+ '.xn--xkc2al3hye2a', '.xn--xkc2dl3a5ee0h', '.xn--yfro4i67o', '.xn--ygbi2ammx', '.xxx', '.ye',
1363
+ '.yt', '.za', '.zm', '.zw'],
1364
+
1365
+ ukTopDomains = ['co', 'me', 'ac', 'gov', 'judiciary','ltd', 'mod', 'net', 'nhs', 'nic',
1366
+ 'org', 'parliament', 'plc', 'police', 'sch', 'bl', 'british-library', 'jet','nls'],
1367
+
1368
+ dot = val.lastIndexOf('.'),
1369
+ domain = val.substring(0, dot),
1370
+ ext = val.substring(dot, val.length),
1371
+ hasTopDomain = false;
1372
+
1373
+ for (var i = 0; i < topDomains.length; i++) {
1374
+ if (topDomains[i] === ext) {
1375
+ if(ext==='.uk') {
1376
+ //Run Extra Checks for UK Domain Names
1377
+ var domainParts = val.split('.');
1378
+ var tld2 = domainParts[domainParts.length-2];
1379
+ for(var j = 0; j < ukTopDomains.length; j++) {
1380
+ if(ukTopDomains[j] === tld2) {
1381
+ hasTopDomain = true;
1382
+ break;
1383
+ }
1384
+ }
1385
+
1386
+ if(hasTopDomain)
1387
+ break;
1388
+
1389
+ } else {
1390
+ hasTopDomain = true;
1391
+ break;
1392
+ }
1393
+ }
1394
+ }
1395
+
1396
+ if (!hasTopDomain) {
1397
+ return false;
1398
+ } else if (dot < 2 || dot > 57) {
1399
+ return false;
1400
+ } else {
1401
+ var firstChar = domain.substring(0, 1),
1402
+ lastChar = domain.substring(domain.length - 1, domain.length);
1403
+
1404
+ if (firstChar === '-' || firstChar === '.' || lastChar === '-' || lastChar === '.') {
1405
+ return false;
1406
+ }
1407
+ if (domain.split('.').length > 3 || domain.split('..').length > 1) {
1408
+ return false;
1409
+ }
1410
+ if (domain.replace(/[-\da-z\.]/g, '') !== '') {
1411
+ return false;
1412
+ }
1413
+ }
1414
+
1415
+ // It's valid, lets update input with trimmed value perhaps??
1416
+ if(typeof $input !== 'undefined') {
1417
+ $input.val(val);
1418
+ }
1419
+
1420
+ return true;
1421
+ },
1422
+ errorMessage : '',
1423
+ errorMessageKey: 'badDomain'
1424
+ });
1425
+
1426
+ /*
1427
+ * Validate required
1428
+ */
1429
+ $.formUtils.addValidator({
1430
+ name : 'required',
1431
+ validatorFunction : function(val, $el, config, language, $form) {
1432
+ switch ( $el.attr('type') ) {
1433
+ case 'checkbox':
1434
+ return $el.is(':checked');
1435
+ case 'radio':
1436
+ return $form.find('input[name="'+$el.attr('name')+'"]').filter(':checked').length > 0;
1437
+ default:
1438
+ return $.trim(val) !== '';
1439
+ }
1440
+ },
1441
+ errorMessage : '',
1442
+ errorMessageKey: 'requiredFields'
1443
+ });
1444
+
1445
+ /*
1446
+ * Validate length range
1447
+ */
1448
+ $.formUtils.addValidator({
1449
+ name : 'length',
1450
+ validatorFunction : function(val, $el, conf, lang) {
1451
+ var lengthAllowed = $el.valAttr('length'),
1452
+ type = $el.attr('type');
1453
+
1454
+ if(lengthAllowed == undefined) {
1455
+ var elementType = $el.get(0).nodeName;
1456
+ alert('Please add attribute "data-validation-length" to '+elementType+' named '+$el.attr('name'));
1457
+ return true;
1458
+ }
1459
+
1460
+ // check if length is above min, below max or within range.
1461
+ var len = type == 'file' && $el.get(0).files !== undefined ? $el.get(0).files.length : val.length,
1462
+ lengthCheckResults = $.formUtils.numericRangeCheck(len, lengthAllowed),
1463
+ checkResult;
1464
+
1465
+ switch(lengthCheckResults[0])
1466
+ { // outside of allowed range
1467
+ case "out":
1468
+ this.errorMessage = lang.lengthBadStart + lengthAllowed + lang.lengthBadEnd;
1469
+ checkResult = false;
1470
+ break;
1471
+ // too short
1472
+ case "min":
1473
+ this.errorMessage = lang.lengthTooShortStart + lengthCheckResults[1] + lang.lengthBadEnd;
1474
+ checkResult = false;
1475
+ break;
1476
+ // too long
1477
+ case "max":
1478
+ this.errorMessage = lang.lengthTooLongStart + lengthCheckResults[1] + lang.lengthBadEnd;
1479
+ checkResult = false;
1480
+ break;
1481
+ // ok
1482
+ default:
1483
+ checkResult = true;
1484
+ }
1485
+
1486
+ return checkResult;
1487
+ },
1488
+ errorMessage : '',
1489
+ errorMessageKey: ''
1490
+ });
1491
+
1492
+ /*
1493
+ * Validate url
1494
+ */
1495
+ $.formUtils.addValidator({
1496
+ name : 'url',
1497
+ validatorFunction : function(url) {
1498
+ // written by Scott Gonzalez: http://projects.scottsplayground.com/iri/
1499
+ // - Victor Jonsson added support for arrays in the url ?arg[]=sdfsdf
1500
+ // - General improvements made by Stéphane Moureau <https://github.com/TraderStf>
1501
+ var urlFilter = /^(https?|ftp):\/\/((((\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])(\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])(\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/(((\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/((\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|\[|\]|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#(((\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i;
1502
+ if( urlFilter.test(url) ) {
1503
+ var domain = url.split('://')[1];
1504
+ var domainSlashPos = domain.indexOf('/');
1505
+ if(domainSlashPos > -1)
1506
+ domain = domain.substr(0, domainSlashPos);
1507
+
1508
+ return $.formUtils.validators.validate_domain.validatorFunction(domain); // todo: add support for IP-addresses
1509
+ }
1510
+ return false;
1511
+ },
1512
+ errorMessage : '',
1513
+ errorMessageKey: 'badUrl'
1514
+ });
1515
+
1516
+ /*
1517
+ * Validate number (floating or integer)
1518
+ */
1519
+ $.formUtils.addValidator({
1520
+ name : 'number',
1521
+ validatorFunction : function(val, $el, conf) {
1522
+ if(val !== '') {
1523
+ var allowing = $el.valAttr('allowing') || '',
1524
+ decimalSeparator = $el.valAttr('decimal-separator') || conf.decimalSeparator,
1525
+ allowsRange = false,
1526
+ begin, end;
1527
+
1528
+ if(allowing.indexOf('number') == -1)
1529
+ allowing += ',number';
1530
+
1531
+ if(allowing.indexOf('negative') > -1 && val.indexOf('-') === 0) {
1532
+ val = val.substr(1);
1533
+ }
1534
+
1535
+ if (allowing.indexOf('range') > -1)
1536
+ {
1537
+ begin = parseFloat(allowing.substring(allowing.indexOf("[")+1, allowing.indexOf(";")));
1538
+ end = parseFloat(allowing.substring(allowing.indexOf(";")+1,allowing.indexOf("]")));
1539
+ allowsRange = true;
1540
+ }
1541
+
1542
+ if( decimalSeparator == ',' ) {
1543
+ if( val.indexOf('.') > -1 ) {
1544
+ return false;
1545
+ }
1546
+ // Fix for checking range with floats using ,
1547
+ val = val.replace(',', '.');
1548
+ }
1549
+
1550
+ if(allowing.indexOf('number') > -1 && val.replace(/[0-9]/g, '') === '' && (!allowsRange || (val >= begin && val <= end)) ) {
1551
+ return true;
1552
+ }
1553
+ if(allowing.indexOf('float') > -1 && val.match(new RegExp('^([0-9]+)\\.([0-9]+)$')) !== null && (!allowsRange || (val >= begin && val <= end)) ) {
1554
+ return true;
1555
+ }
1556
+ }
1557
+ return false;
1558
+ },
1559
+ errorMessage : '',
1560
+ errorMessageKey: 'badInt'
1561
+ });
1562
+
1563
+ /*
1564
+ * Validate alpha numeric
1565
+ */
1566
+ $.formUtils.addValidator({
1567
+ name : 'alphanumeric',
1568
+ validatorFunction : function(val, $el, conf, language) {
1569
+ var patternStart = '^([a-zA-Z0-9',
1570
+ patternEnd = ']+)$',
1571
+ additionalChars = $el.attr('data-validation-allowing'),
1572
+ pattern = '';
1573
+
1574
+ if( additionalChars ) {
1575
+ pattern = patternStart + additionalChars + patternEnd;
1576
+ var extra = additionalChars.replace(/\\/g, '');
1577
+ if( extra.indexOf(' ') > -1 ) {
1578
+ extra = extra.replace(' ', '');
1579
+ extra += ' and spaces ';
1580
+ }
1581
+ this.errorMessage = language.badAlphaNumeric + language.badAlphaNumericExtra + extra;
1582
+ } else {
1583
+ pattern = patternStart + patternEnd;
1584
+ this.errorMessage = language.badAlphaNumeric;
1585
+ }
1586
+
1587
+ return new RegExp(pattern).test(val);
1588
+ },
1589
+ errorMessage : '',
1590
+ errorMessageKey: ''
1591
+ });
1592
+
1593
+ /*
1594
+ * Validate against regexp
1595
+ */
1596
+ $.formUtils.addValidator({
1597
+ name : 'custom',
1598
+ validatorFunction : function(val, $el, conf) {
1599
+ var regexp = new RegExp($el.valAttr('regexp'));
1600
+ return regexp.test(val);
1601
+ },
1602
+ errorMessage : '',
1603
+ errorMessageKey: 'badCustomVal'
1604
+ });
1605
+
1606
+ /*
1607
+ * Validate date
1608
+ */
1609
+ $.formUtils.addValidator({
1610
+ name : 'date',
1611
+ validatorFunction : function(date, $el, conf) {
1612
+ var dateFormat = 'yyyy-mm-dd';
1613
+ if($el.valAttr('format')) {
1614
+ dateFormat = $el.valAttr('format');
1615
+ }
1616
+ else if( conf.dateFormat ) {
1617
+ dateFormat = conf.dateFormat;
1618
+ }
1619
+
1620
+ return $.formUtils.parseDate(date, dateFormat) !== false;
1621
+ },
1622
+ errorMessage : '',
1623
+ errorMessageKey: 'badDate'
1624
+ });
1625
+
1626
+
1627
+ /*
1628
+ * Validate group of checkboxes, validate qty required is checked
1629
+ * written by Steve Wasiura : http://stevewasiura.waztech.com
1630
+ * element attrs
1631
+ * data-validation="checkbox_group"
1632
+ * data-validation-qty="1-2" // min 1 max 2
1633
+ * data-validation-error-msg="chose min 1, max of 2 checkboxes"
1634
+ */
1635
+ $.formUtils.addValidator({
1636
+ name : 'checkbox_group',
1637
+ validatorFunction : function(val, $el, conf, lang, $form)
1638
+ { // preset return var
1639
+ var checkResult = true;
1640
+ // get name of element. since it is a checkbox group, all checkboxes will have same name
1641
+ var elname = $el.attr('name');
1642
+ // get count of checked checkboxes with this name
1643
+ var checkedCount = $("input[type=checkbox][name^='"+elname+"']:checked", $form).length;
1644
+ // get el attr that specs qty required / allowed
1645
+ var qtyAllowed = $el.valAttr('qty');
1646
+ if (qtyAllowed == undefined) {
1647
+ var elementType = $el.get(0).nodeName;
1648
+ alert('Attribute "data-validation-qty" is missing from '+elementType+' named '+$el.attr('name'));
1649
+ }
1650
+ // call Utility function to check if count is above min, below max, within range etc.
1651
+ var qtyCheckResults = $.formUtils.numericRangeCheck(checkedCount, qtyAllowed) ;
1652
+ // results will be array, [0]=result str, [1]=qty int
1653
+ switch(qtyCheckResults[0] ) {
1654
+ // outside allowed range
1655
+ case "out":
1656
+ this.errorMessage = lang.groupCheckedRangeStart + qtyAllowed + lang.groupCheckedEnd;
1657
+ checkResult = false;
1658
+ break;
1659
+ // below min qty
1660
+ case "min":
1661
+ this.errorMessage = lang.groupCheckedTooFewStart + qtyCheckResults[1] + lang.groupCheckedEnd;
1662
+ checkResult = false;
1663
+ break;
1664
+ // above max qty
1665
+ case "max":
1666
+ this.errorMessage = lang.groupCheckedTooManyStart + qtyCheckResults[1] + lang.groupCheckedEnd;
1667
+ checkResult = false;
1668
+ break;
1669
+ // ok
1670
+ default:
1671
+ checkResult = true;
1672
+ }
1673
+
1674
+ return checkResult;
1675
+
1676
+ }
1677
+ // errorMessage : '', // set above in switch statement
1678
+ // errorMessageKey: '' // not used
1679
+ });
1680
+
1681
+ })(jQuery);