jquery-form-validator-rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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);