fmsystem-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Fmsystem-commits] [12129] Add form-validator


From: Sigurd Nes
Subject: [Fmsystem-commits] [12129] Add form-validator
Date: Thu, 09 Oct 2014 19:00:02 +0000

Revision: 12129
          http://svn.sv.gnu.org/viewvc/?view=rev&root=fmsystem&revision=12129
Author:   sigurdne
Date:     2014-10-09 19:00:00 +0000 (Thu, 09 Oct 2014)
Log Message:
-----------
Add form-validator

Added Paths:
-----------
    branches/dev-syncromind/phpgwapi/js/form-validator/
    branches/dev-syncromind/phpgwapi/js/form-validator/README.md
    branches/dev-syncromind/phpgwapi/js/form-validator/date.dev.js
    branches/dev-syncromind/phpgwapi/js/form-validator/date.js
    branches/dev-syncromind/phpgwapi/js/form-validator/file.dev.js
    branches/dev-syncromind/phpgwapi/js/form-validator/file.js
    branches/dev-syncromind/phpgwapi/js/form-validator/form-test.html
    branches/dev-syncromind/phpgwapi/js/form-validator/html5.dev.js
    branches/dev-syncromind/phpgwapi/js/form-validator/html5.js
    branches/dev-syncromind/phpgwapi/js/form-validator/jquery.form-validator.js
    
branches/dev-syncromind/phpgwapi/js/form-validator/jquery.form-validator.min.js
    branches/dev-syncromind/phpgwapi/js/form-validator/location.dev.js
    branches/dev-syncromind/phpgwapi/js/form-validator/location.js
    branches/dev-syncromind/phpgwapi/js/form-validator/qunit.html
    branches/dev-syncromind/phpgwapi/js/form-validator/security.dev.js
    branches/dev-syncromind/phpgwapi/js/form-validator/security.js
    branches/dev-syncromind/phpgwapi/js/form-validator/sweden.dev.js
    branches/dev-syncromind/phpgwapi/js/form-validator/sweden.js
    branches/dev-syncromind/phpgwapi/js/form-validator/uk.dev.js
    branches/dev-syncromind/phpgwapi/js/form-validator/uk.js

Added: branches/dev-syncromind/phpgwapi/js/form-validator/README.md
===================================================================
--- branches/dev-syncromind/phpgwapi/js/form-validator/README.md                
                (rev 0)
+++ branches/dev-syncromind/phpgwapi/js/form-validator/README.md        
2014-10-09 19:00:00 UTC (rev 12129)
@@ -0,0 +1,400 @@
+# jQuery Form Validator
+
+With this feature rich jQuery plugin it becomes easy to validate user input 
while keeping your
+ HTML markup clean from javascript code. Even though this plugin has **a wide 
range of validation functions**
+it's designed to require as little bandwidth as possible. This is achieved by 
grouping together validation functions
+in "modules", making it possible for the programmer to load **only those 
functions that's needed** to validate a
+particular form.
+
+**Form demos and full documentation is available at http://formvalidator.net/**
+
+*Usage example*
+
+```html
+<form action="" method="POST">
+  <p>
+    Name (4 characters minimum):
+    <input name="user" data-validation="length" data-validation-length="min4" 
/>
+  </p>
+  <p>
+    Birthdate (yyyy-mm-dd):
+    <input name="birth" data-validation="birthdate" />
+  </p>
+  <p>
+    Website:
+    <input name="website" data-validation="url" />
+  </p>
+  <p>
+    <input type="submit" />
+  </p>
+</form>
+<script src="js/jquery.min.js"></script>
+<script src="js/form-validator/jquery.form-validator.min.js"></script>
+<script>
+/* important to locate this script AFTER the closing form element, so form 
object is loaded in DOM before setup is called */
+    $.validate({
+        modules : 'date, security'
+    });
+</script>
+```
+
+### Support for HTML5
+
+As of version 2.2 (unreleased) you can use this plugin as a fallback solution 
for the validation attributes in the HTML5 spec. Add the module `html5` to the 
module string and you can use the following native features:
+
+**Attributes**: require, pattern, maxlength, min, max, placeholder
+
+**Input types**: url, date, time, email, number
+
+**Elements**: Use the element `datalist` to create input suggestions
+
+
+### Default validators and features (no module needed)
+ * **url**
+ * **email**
+ * **domain** — *domain.com*
+ * **number** — *float/negative/positive/range/step*
+ * **date** — *yyyy-mm-dd (format can be customized, more information below)*
+ * **alphanumeric** — *with support for defining additional characters*
+ * **length** — *min/max/range*
+ * **required** — *no validation except that a value has to be given*
+ * **custom** — *Validate value against regexp*
+ * **checkboxgroup** — *ensure at least 1 checkbox in group has been selected*
+ * Show help information automatically when input is focused
+ * Validate given values immediately when input looses focus.
+ * Make validation optional by adding attribute 
data-validation-optional="true" to the element. This means
+ that the validation defined in data-validation only will take place in case a 
value is given.
+ * Make validation dependent on another input of type checkbox being checked 
by adding attribute
+ data-validation-if-checked="name of checkbox input"
+ * Create input suggestions with ease, no jquery-ui needed
+ * to apply multiple validators to an input element, separate the validator 
names using a space (ex: required email)
+
+Read the documentation for the default features at 
[http://formvalidator.net/#default-validators](http://formvalidator.net/#default-validators)
+
+### Module: security
+ * **spamcheck**
+ * **confirmation**
+ * **creditcard**
+ * **CVV**
+ * **strength** — *Validate the strength of a password*
+ * **server** — *Validate value of input on server side*
+
+Read the documentation for the security module at 
[http://formvalidator.net/#security-validators](http://formvalidator.net/#security-validators)
+
+### Module: date
+ * **time** — *hh:mm*
+ * **birthdate** — *yyyy-mm-dd, not allowing dates in the future or dates 
that's older than 122 years (format can be customized, more information below)*
+
+Read the documentation for the date module at 
[http://formvalidator.net/#date-validators](http://formvalidator.net/#date-validators)
+
+### Module: location
+ * **country**
+ * **federatestate**
+ * **longlat**
+ * Suggest countries (english only)
+ * Suggest states in the US
+
+Read the documentation for the location module at 
[http://formvalidator.net/#location-validators](http://formvalidator.net/#location-validators)
+
+### Module: file
+ * **mime**
+ * **extension**
+ * **size**
+
+Read the documentation for the file module at 
[http://formvalidator.net/#file-validators](http://formvalidator.net/#file-validators)
+
+### Module: sweden
+ * **swemob** — *validate that the value is a swedish mobile telephone number*
+ * **swesec** — *validate swedish social security number*
+ * **county** - *validate that the value is an existing county in Sweden*
+ * **municipality** - *validate that the value is an existing municipality in 
Sweden*
+ * Suggest county
+ * Suggest municipality
+
+Read the documentation for the Swedish module at 
[http://formvalidator.net/#sweden-validators](http://formvalidator.net/#sweden-validators)
+
+### Module: uk
+ * **ukvatnumber**
+
+Read the documentation for the UK module at 
[http://formvalidator.net/#uk-validators](http://formvalidator.net/#uk-validators)
+
+
+## Writing a custom validator
+You can use the function `$.formUtils.addValidator()` to add your own 
validation function. Here's an example of a validator
+that checks if the input contains an even number.
+
+```html
+<form action="" method="POST">
+    <p>
+        <input type="text" data-validation="even" />
+    </p>
+    ...
+</form>
+<script src="js/jquery.min.js"></script>
+<script src="js/form-validator/jquery.form-validator.min.js"></script>
+<script>
+
+    // Add validator
+    $.formUtils.addValidator({
+        name : 'even',
+        validatorFunction : function(value, $el, config, language, $form) {
+            return parseInt(value, 10) % 2 === 0;
+        },
+        errorMessage : 'You have to answer an even number',
+        errorMessageKey: 'badEvenNumber'
+    });
+
+    // Initiate form validation
+    $.validate();
+
+</script>
+```
+
+### Required properties passed into $.formUtils.addValidator
+
+*name* - The name of the validator, which is used in the validation attribute 
of the input element.
+
+*validatorFunction* - Callback function that validates the input. Should 
return a boolean telling if the value is considered valid or not.
+
+*errorMessageKey* - Name of language property that is used in case the value 
of the input is invalid.
+
+*errorMessage* - An alternative error message that is used if errorMessageKey 
is left with an empty value or isn't defined
+in the language object. Note that you also can use [inline error 
messages](http://formvalidator.net/#localization) in your form.
+
+
+The validation function takes these five arguments:
+- value — the value of the input thats being validated
+- $el — jQuery object referring to the input element being validated
+- config — Object containing the configuration of this form validation
+- language — Object with error dialogs
+- $form — jQuery object referring to the form element being validated
+
+## Creating a custom module
+
+A "module" is basically a javascript file containing one or more calls to 
[$.formUtils.addValidator()](#writing-a-custom-validator). The module file
+should either have the file extension *.js* (as an ordinary javascript file) 
or *.dev.js*.
+
+Using the file extension **.dev.js** will tell *$.formUtils.loadModules* to 
always append a timestamp to the end of the
+URL, so that the browser never caches the file. You should of course never use 
*.dev.js* on a production website.
+
+### Loading your module ###
+
+```html
+<html>
+<head>
+    <script src="js/form-validator/jquery.form-validator.min.js"></script>
+    <script>
+        $.formUtils.loadModules('mymodule.dev', 'js/validation-modules/');
+    </script>
+</head>
+</html>
+...
+```
+
+The first argument of $.formUtils.loadModules is a comma separated string with 
names of module files, without
+file extension (add .dev if the file name is for example mymodule.dev.js, this 
will insure that the browser never
+caches the javascript).
+
+The second argument is the path where the module files is located. This 
argument is optional, if not given
+the module files has to be located in the same directory as the core modules 
shipped together with this jquery plugin
+(js/form-validator/)
+
+## Show help information
+It is possible to display help information for each input. The information 
will fade in when input is focused and fade out when input looses focus.
+
+```html
+<form action="" id="my_form">
+       <p>
+         <strong>Why not:</strong>
+         <textarea name="why" data-validation-help="Please give us some more 
information" data-validation="required"></textarea>
+       </p>
+       ...
+```
+
+## Fully customizable
+
+Read about how to customize this plugin over at 
[http://formvalidator.net/#configuration](http://formvalidator.net/#configuration)
+
+## Localization
+This plugin contains a set of error dialogs. In case you don't define an 
inline error message the plugin
+will fall back on one of the dialogs below. You can how ever add the attribute 
*data-validation-error-msg* to an
+element, and that message will be displayed instead. All error dialogs can be 
overwritten by passing an
+object into the validation function.
+
+```javascript
+var enErrorDialogs = {
+    errorTitle : 'Form submission failed!',
+    requiredFields : 'You have not answered all required fields',
+    badTime : 'You have not given a correct time',
+    badEmail : 'You have not given a correct e-mail address',
+    badTelephone : 'You have not given a correct phone number',
+    badSecurityAnswer : 'You have not given a correct answer to the security 
question',
+    badDate : 'You have not given a correct date',
+    lengthBadStart : 'You must give an answer between ',
+    lengthBadEnd : 'characters',
+    lengthTooLongStart : 'You have given an answer longer than ',
+    lengthTooShortStart : 'You have given an answer shorter than ',
+    notConfirmed : 'Values could not be confirmed',
+    badDomain : 'Incorrect domain value',
+    badUrl : 'The answer you gave was not a correct URL',
+    badCustomVal : 'You gave an incorrect answer',
+    badInt : 'The answer you gave was not a correct number',
+    badSecurityNumber : 'Your social security number was incorrect',
+    badUKVatAnswer : 'Incorrect UK VAT Number',
+    badStrength : 'The password isn\'t strong enough',
+    badNumberOfSelectedOptionsStart : 'You have to choose at least ',
+    badNumberOfSelectedOptionsEnd : ' answers',
+    badAlphaNumeric : 'The answer you gave must contain only alphanumeric 
characters ',
+    badAlphaNumericExtra: ' and ',
+    wrongFileSize : 'The file you are trying to upload is too large',
+    wrongFileType : 'The file you are trying to upload is of wrong type',
+    groupCheckedTooFewStart : 'Please choose at least ',
+    groupCheckedTooManyStart : 'Please choose a maximum of ', 
+    groupCheckedRangeStart : 'Please choose between ',
+    groupCheckedEnd : ' item(s)'
+};
+```
+
+```html
+<form action="script.php">
+    ...
+</form>
+<script src="js/jquery.min.js"></script>
+<script src="js/form-validator/jquery.form-validator.min.js"></script>
+<script src="js/form-validator/locale.en.js"></script>
+<script>
+  $.validate({
+    language : enErrorDialogs
+  });
+</script>
+...
+```
+
+It's also possible to add inline error messages. If you add attribute 
`data-validation-error-msg` to an element the value of
+that attribute will be displayed instead of the error dialog that the 
validation function refers to.
+
+## Input length restriction
+```html
+<p>
+  History (<span id="maxlength">50</span> characters left)
+  <textarea rows="3" id="area"></textarea>
+</p>
+<script type="text/javascript">
+  $('#area').restrictLength( $('#maxlength') );
+</script>
+```
+
+## Program Flow
+Form submit() event is bound to jQ func **validateForm()** when the form is 
submitted, it calls
+jQ func **$.formUtils.validateInput**, which calls **validatorFunction** for 
the specific validation
+rule assigned to the input element. If a validation fails, error messages are 
assigned and displayed
+as configured. If **validateOnBlur** is set to true, jQ finds all form input 
elements with the
+data-validation attribute and binds their onBlur event to call the function 
**validateInputOnBlur**.
+it calls jQ func **$.formUtils.validateInput** to validate the single input 
when blurred.
+
+
+## Changelog
+
+#### 2.2.0 (unreleased)
+* Now possible to define an error message for each validation rule on a 
certain input (issue #113)
+* This plugin now serves as a html5 fallback. You can now use the native 
attributes to declare which type
+of validation that should be applied.
+* Use a template for error messages when having errorMessagePosition set to top
+* Added validation of credit card number and CVV to the security module
+* Event onElementValidate added
+* Use the attribute data-validation-confirm to declare which input that should 
be confirmed when using validation=confirmation (issue #112)
+* Validation "required" now supports inputs of type radio
+* $.validateForm is now deprecated, use $.isValid instead
+
+
+#### 2.1.47
+* Incorrect error-styling when using datepicker or suggestions is now fixed
+* Incorrect error-styling of select elements is now fixed
+* Deprecated function $.validationSetup is now removed, use $.validate() 
instead
+* You can now return an array with errors using the event `onValidate`
+* You can now declare an element where all error messages should be placed 
(config.errorMessagePosition)
+
+#### 2.1.36
+* Now possible to use the native reset() function to clear error messages and 
error styling of the input elements
+
+#### 2.1.34
+* General improvements and bug fixes
+* Added events "beforeValidation" and "validation" (see 
http://formvalidator.net/#configuration_callbacks for more info)
+
+#### 2.1.27
+ * E-mail validation support .eu top domain
+ * Improvements in server validation
+ * Now possible to re-initiate the validation. This makes it possible to 
dynamically change the form and then call $.validate() again to refresh the 
validation (issue #59)
+ * Number validation now supports range
+
+#### 2.1.15
+ * E-mail addresses can now contain + symbol
+ * Correction of the US states in validation "federatestate"
+ * Fixed bug in server validation
+
+#### 2.1.9
+ * File validation now support multiple files
+ * Length validation can now be used to validate the number of uploaded files 
using a file input that supports multiple files
+ * Validation classes is no longer applied on inputs that for some reason 
shouldn't become validated
+
+#### 2.1.8
+ * Now possible to configure the decimal separator when validating float 
values. Use either the
+ attribute *data-validation-decimal-separator* or the property 
*decimalSeparator* when
+calling $.validate()
+ * $.validationSetup is renamed to $.validate. You will still be able to 
initiate the validation by calling
+ the $.validationSetup but it's considered deprecated.
+
+#### 2.1.6
+ * Modules can now be loaded from remote website
+
+#### 2.1.5
+ * Fixed language bug (issue #43 on github)
+ * Validation on server side is now triggered by the blur event
+ * Now using class names that's compliant with twitter bootstrap 3.x
+
+#### 2.1
+ * Code refactoring and some functions renamed
+ * Validator "checkbox_group" added
+
+#### 2.0.7
+ * Now possible to validate file size, extension and mime type (using the file 
module)
+
+#### 2.0
+ * [min|max]_length is removed (now merged with length validation).
+ * The number, int and float validation is merged together, all three variants 
is now validated by the number validation.
+ * Phone validation is moved to "sweden" module and renamed to swephone.
+ * The attribute to be used when defining the regular expression for custom 
validations is now moved to its own attribute (data-validation-regexp)
+ * Length validation now looks at attribute data-validation-length (eg. min5, 
max200, 3-12).
+ * The validation rule no longer needs to be prefixed with "validate_" (it's 
still possible to use the prefix but it's considered deprecated).
+ * Some validation functions is moved to modules (see the function reference 
over at http://formvalidator.net).
+ * Added function $.validationSetup() to reduce the amount of code that has to 
be written when initiating the form validation.
+
+
+## Credits
+
+#### Maintainer
+
+[Victor Jonsson](https://github.com/victorjonsson)
+
+#### Contributors
+<a href="http://stevewasiura.waztech.com"; target="_blank">Steve Wasiura</a><br 
/>
+<a href="http://lagden.github.com"; target="_blank">Thiago Lagden</a><br />
+<a href="https://github.com/robamaton"; target="_blank">Joel Sutherland</a><br 
/>
+<a href="https://github.com/mattclements"; target="_blank">Matt Clements</a><br 
/>
+<a href="http://www.joshtoft.com/"; target="_blank">Josh Toft</a><br/>
+<a href="https://github.com/dfcplc"; target="_blank">@dfcplc</a><br />
+<a href="https://github.com/coffein"; target="_blank">Andree Wendel</a><br />
+<a href="http://www.huotmedia.com"; target="_blank">Nicholas Huot</a><br />
+<a href="https://github.com/Repkit"; target="_blank">@repkit</a><br />
+<a href="https://github.com/aL3xa"; target="_blank">Alexandar Blagotic</a><br />
+<a href="http://thekindof.me/"; target="_blank">Yasith Fernando</a><br />
+<a href="https://github.com/S0L4R1S"; target="_blank">@S0L4R1S</a><br />
+<a href="http://lisangan.com/";>Erick Lisangan</a><br />
+<a href="https://github.com/kirbs-";>@kirbs</a>
+<a href="https://github.com/hslee87";>hslee87</a>
+
+#### Additional credits
+
+<a href="http://projects.scottsplayground.com/iri/"; target="_blank">Scott 
Gonzales</a> (URL regexp)<br />
+<a href="http://www.mypocket-technologies.com"; target="_blank">Darren 
Mason</a> (Password strength meter)<br />
+<a href="http://stevewasiura.waztech.com"; target="_blank">Steve Wasiura</a> 
(Checkbox group)

Added: branches/dev-syncromind/phpgwapi/js/form-validator/date.dev.js
===================================================================
--- branches/dev-syncromind/phpgwapi/js/form-validator/date.dev.js              
                (rev 0)
+++ branches/dev-syncromind/phpgwapi/js/form-validator/date.dev.js      
2014-10-09 19:00:00 UTC (rev 12129)
@@ -0,0 +1,81 @@
+/**
+ * jQuery Form Validator Module: Date
+ * ------------------------------------------
+ * Created by Victor Jonsson <http://www.victorjonsson.se>
+ * Documentation and issue tracking on Github 
<https://github.com/victorjonsson/jQuery-Form-Validator/>
+ *
+ * The following validators will be added by this module:
+ *  - Time (HH:mmm)
+ *  - Birth date
+ *
+ * @website http://formvalidator.net/#location-validators
+ * @license Dual licensed under the MIT or GPL Version 2 licenses
+ * @version 2.2.beta.13
+ */
+(function($) {
+
+    /*
+     * Validate time hh:mm
+     */
+    $.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'
+    });
+
+    /*
+     * Is this a valid birth date
+     */
+    $.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); // we 
can not live for ever yet...
+            }
+        },
+        errorMessage : '',
+        errorMessageKey: 'badDate'
+    });
+
+})(jQuery);
\ No newline at end of file

Added: branches/dev-syncromind/phpgwapi/js/form-validator/date.js
===================================================================
--- branches/dev-syncromind/phpgwapi/js/form-validator/date.js                  
        (rev 0)
+++ branches/dev-syncromind/phpgwapi/js/form-validator/date.js  2014-10-09 
19:00:00 UTC (rev 12129)
@@ -0,0 +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);
\ No newline at end of file

Added: branches/dev-syncromind/phpgwapi/js/form-validator/file.dev.js
===================================================================
--- branches/dev-syncromind/phpgwapi/js/form-validator/file.dev.js              
                (rev 0)
+++ branches/dev-syncromind/phpgwapi/js/form-validator/file.dev.js      
2014-10-09 19:00:00 UTC (rev 12129)
@@ -0,0 +1,169 @@
+/**
+ * jQuery Form Validator Module: File
+ * ------------------------------------------
+ * Created by Victor Jonsson <http://www.victorjonsson.se>
+ *
+ * The following validators will be added by this module:
+ *  - mime type
+ *  - file size
+ *  - file extension
+ *
+ * @website http://formvalidator.net/
+ * @license Dual licensed under the MIT or GPL Version 2 licenses
+ * @version 2.2.beta.13
+ */
+(function($, window) {
+
+    var SUPPORTS_FILE_READER = typeof window.FileReader != 'undefined',
+
+        /**
+         * @return {Array}
+         */
+        _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;
+        },
+
+        /**
+         * @param {String} msg
+         */
+        _log = function(msg) {
+            if( window.console && window.console.log ) {
+                window.console.log(msg);
+            }
+        };
+
+    /*
+     * Validate mime type (falls back on validate_extension in older browsers)
+     */
+    $.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;
+                });
+
+                if( !valid ) {
+                    _log('Trying to upload a file with mime type '+mime+' 
which is not allowed');
+                }
+                return valid;
+                
+            } else {
+                _log('FileReader not supported by browser, will check file 
extension');
+                return 
$.formUtils.validators.validate_extension.validatorFunction(str, $input);
+            }
+        },
+        errorMessage : 'The file you are trying to upload is of wrong type',
+        errorMessageKey: 'wrongFileType'
+    });
+
+    /**
+     * Validate file extension
+     */
+    $.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'
+    });
+
+    /**
+     * Validate file size
+     */
+    $.formUtils.addValidator({
+        name : 'size',
+        validatorFunction : function(val, $input) {
+            var maxSize = $input.valAttr('max-size');
+            if( !maxSize ) {
+                _log('Input "'+$input.attr('name')+'" is missing 
data-validation-max-size attribute');
+                return true;
+            } else if( !SUPPORTS_FILE_READER ) {
+                return true; // no fallback available
+            }
+
+            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'
+    });
+
+    /**
+     * Make this function accessible via formUtils for unit tests
+     * @param {String} sizeName
+     * @return {Number}
+     */
+    $.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);
+        }
+    };
+
+    /*
+     * This event listener will remove error messages for file
+     * inputs when file changes
+     */
+    $(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);
\ No newline at end of file

Added: branches/dev-syncromind/phpgwapi/js/form-validator/file.js
===================================================================
--- branches/dev-syncromind/phpgwapi/js/form-validator/file.js                  
        (rev 0)
+++ branches/dev-syncromind/phpgwapi/js/form-validator/file.js  2014-10-09 
19:00:00 UTC (rev 12129)
@@ -0,0 +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},_log=function(msg){if(window.console&&window.console.log){window.console.log(msg)}};$.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});if(!valid){_log("Trying to upload a file with mime 
type "+mime+" which is not allowed")}return valid}else{_log("FileReader not 
supported by browser, will check file extension");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){_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);
\ No newline at end of file

Added: branches/dev-syncromind/phpgwapi/js/form-validator/form-test.html
===================================================================
--- branches/dev-syncromind/phpgwapi/js/form-validator/form-test.html           
                (rev 0)
+++ branches/dev-syncromind/phpgwapi/js/form-validator/form-test.html   
2014-10-09 19:00:00 UTC (rev 12129)
@@ -0,0 +1,415 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8" />
+    <title>Form Test</title>
+    <link rel="stylesheet" 
href="//netdna.bootstrapcdn.com/bootstrap/3.0.0-wip/css/bootstrap.min.css" />
+    <link rel="stylesheet" 
href="//code.jquery.com/ui/1.10.4/themes/black-tie/jquery-ui.css" />
+    <style>
+
+        /* Form and inputs */
+        form {
+            width: 500px;
+            margin: 0 auto;
+            padding: 20px;
+            display: block;
+        }
+
+        input.form-control {
+            width: 375px;
+        }
+
+        button, input[type="submit"], .button {
+            margin-bottom: 8px;
+        }
+
+        /* While server is being requested */
+        form.validating-server-side {
+            background: #F2F2F2;
+        }
+
+        input.validating-server-side {
+            opacity: 0.5;
+            background: lightgoldenrodyellow;
+        }
+
+        /* modify inputs for password strength */
+        .password-strength input.form-control {
+            width: 375px;
+            margin-right: 4px;
+            display: inline;
+        }
+
+        .password-strength label {
+            display: block;
+        }
+
+        /* Checkboxes */
+        .form-group.check-boxes input {
+            margin-left: 10px;
+        }
+
+        span.help {
+            color: #999 !important;
+        }
+
+        /* Error container for form C */
+
+        #error-container div {
+            color: red;
+            line-height: 140%;
+        }
+
+        #error-container div:last-child {
+            padding-bottom: 10px;
+        }
+
+    </style>
+</head>
+<body>
+<div>
+    <form action="" id="form-a" role="form">
+        <div class="form-group">
+            <label class="control-label" for="inline-suggestions">Inline 
suggestions</label>
+            <input name="inline suggestions" type="text" 
id="inline-suggestions" class="form-control" data-suggestions="Monkey, Horse, 
Fox, Tiger, Elephant" />
+        </div>
+
+        <div class="form-group">
+            <label class="control-label" for="country-suggestions">Country 
suggestions</label>
+            <input name="country suggestions" data-validation="country" 
type="text" id="country-suggestions" class="form-control" />
+        </div>
+
+        <div class="form-group">
+            <label class="control-label" for="country-suggestions">Swedish 
county suggestions</label>
+            <input name="Swedish county suggestion" 
data-validation="swecounty" type="text" id="swedish-county-suggestions" 
class="form-control" />
+        </div>
+
+        <div class="form-group">
+            <label class="control-label">Year</label>
+            <input name="birth" class="form-control"
+                   data-validation="date"
+                   data-validation-format="yyyy-mm-dd"
+                   data-suggestions="2014-01-15,2014-01-16,2014-01-17" />
+        </div>
+
+        <div class="form-group">
+            <label class="control-label">Datepicker</label>
+            <input name="birth2" class="form-control"
+                   data-validation="date"
+                   data-validation-format="mm/dd/yyyy"
+                   id="datepicker" />
+        </div>
+
+        <div class="form-group">
+            <label class="control-label">Number 0-10 (accepting floats with 
comma)</label>
+            <input name="floats" class="form-control"
+                   data-validation="number"
+                   data-validation-allowing="range[0;10], float"
+                   data-validation-decimal-separator=","
+                    />
+        </div>
+
+        <div class="form-group password-strength">
+            <label class="control-label" for="password">Display password 
strength (only strong)</label>
+            <input name="password" type="password" id="password" 
class="form-control" data-validation="strength" data-validation-strength="3" />
+        </div>
+
+        <div class="form-group">
+            <label class="control-label">Alphanumeric and -_ and spaces</label>
+            <input name="alphanumeric with spaces" class="form-control" 
name="test" data-validation="alphanumeric" data-validation-allowing="-_ " />
+        </div>
+
+        <div class="form-group">
+            <label class="control-label">Alphanumeric only</label>
+            <input name="aplhanumeric only" class="form-control" name="test2" 
data-validation="alphanumeric" />
+        </div>
+
+        <div class="checkbox form-group">
+            <label>
+                <input name="checkbox" type="checkbox" 
data-validation="required" /> Must be checked
+            </label>
+        </div>
+
+        <div class="form-group">
+            <label class="control-label">Must choose one</label>
+            <br />
+            <input name="radio" type="radio" data-validation="required" 
value="1" /> A
+            <input name="radio" type="radio" value="1" /> B
+            <input name="radio" type="radio" value="1" /> C
+            <input name="radio" type="radio" value="1" /> D
+        </div>
+
+        <div class="form-group">
+            <label class="control-label">Even numbers only</label>
+            <input name="even numbers" class="form-control" name="test4" 
data-validation="even_number" />
+        </div>
+
+        <div class="form-group">
+            <label class="control-label">Make a choice</label>
+            <br />
+            <select name="choice" data-validation="required" 
data-validation-error-msg="Please make a choice">
+                <option value="">- - Choose - -</option>
+                <option>A</option>
+                <option>B</option>
+                <option>C</option>
+                <option>D</option>
+            </select>
+        </div>
+
+        <div class="form-group">
+            <label class="control-label">Text</label>
+            (<span id="max-len">20</span> chars left)<br />
+            <textarea id="text-area" class="form-control" 
name="some-text"></textarea>
+        </div>
+        <div class="form-group">
+            <label class="control-label">Server validation</label>
+            <input class="form-control" name="code" value="secret"
+                   data-validation-help="The word is &quot;secret&quot;"
+                   data-validation="server"
+                   
data-validation-url="http://formvalidator.net/validate-email.php"; />
+        </div>
+        <div class="form-group">
+            <label class="control-label">File validation</label>
+            <input type="file" name="some-file1" class="form-control"
+                   data-validation="size mime required"
+                   data-validation-size-error-msg="The file cant be larger 
than 400kb"
+                   data-validation-error-msg="You must upload an image file 
(max 400 kb)"
+                   data-validation-allowing="jpg, png, ico"
+                   data-validation-max-size="400kb" />
+        </div>
+        <div class="form-group">
+            <label class="control-label">
+                Callback validation, set this value to &quot;1&quot; and
+                validation will fail
+            </label>
+            <input id="callback" class="form-control" />
+        </div>
+
+        <div class="form-group check-boxes">
+            <label>Checkbox group</label><br />
+            <label>
+                <input type="checkbox" name="box" value="1"
+                       data-validation="checkbox_group"
+                       data-validation-qty="1-2" /> 1
+            </label>
+            <label>
+                <input type="checkbox" name="box" value="2" /> 2
+            </label>
+            <label>
+                <input type="checkbox" name="box" value="3" /> 3
+            </label>
+            <label>
+                <input type="checkbox" name="box" value="4" /> 4
+            </label>
+            <label>
+                <input type="checkbox" name="box" value="5" /> 5
+            </label>
+        </div>
+        <p style="line-height: 200%">
+            <input type="submit" class="button">
+            <br />
+            <button class="button" type="button"
+                    onclick="alert('From a is ' + ( $('#form-a').isValid({}, 
{}, false) ? 'VALID':'NOT VALID'));">
+                Test validation via js (<strong>without error 
messages</strong>)
+            </button>
+            <br />
+            <button class="button" type="button"
+                    onclick="alert('From a is ' + ( $('#form-a').isValid() ? 
'VALID':'NOT VALID'));">
+                Test validation via js (showing error messages)
+            </button>
+            <br />
+            <input type="reset" class="button">
+        </p>
+    </form>
+    <hr />
+    <form id="form-b">
+        <div class="form-group">
+            <label class="control-label">Test</label>
+            <input name="test" data-validation="number" type="text" />
+        </div>
+        <div class="form-group">
+            <label class="control-label">Password</label>
+            <input name="pass" data-validation="confirmation" type="password" 
/>
+        </div>
+        <div class="form-group">
+            <label class="control-label">Password again</label>
+            <input name="pass_confirmation" type="password" />
+        </div>
+        <p>
+            <input type="submit" class="button">
+            <input type="reset" class="button">
+        </p>
+    </form>
+    <hr />
+    <form id="form-c">
+        <div class="form-group">
+            <label class="control-label">Country</label>
+            <input name="test" data-validation="country" 
data-validation-error-msg="No valid country given" />
+        </div>
+        <div class="form-group">
+            <label class="control-label">E-mail</label>
+            <input name="testmail" data-validation="email" 
data-validation-error-msg="E-mail is not valid" />
+        </div>
+        <div class="form-group">
+            <label class="control-label">Confirm e-mail</label>
+            <input name="test" data-validation="confirmation" 
data-validation-confirm="testmail" />
+        </div>
+        <div class="form-group">
+            <label class="control-label">Alphanumeric (will only be validated 
if the checkbox is checked)</label>
+            <input name="test2"
+                    data-validation="alphanumeric"
+                    data-validation-error-msg="Invalid..."
+                    data-validation-if-checked="checker" />
+            <br />
+            <input type="checkbox" name="checker" />
+        </div>
+        <div id="error-container">
+
+        </div>
+        <p>
+            <input type="submit" class="button">
+            <input type="reset" class="button">
+        </p>
+    </form>
+    <hr />
+    <form id="form-d">
+        <h2>HTML5 attributes</h2>
+        <div class="form-group">
+            <label class="control-label">type="email"</label>
+            <input type="text" required="required" list="mejl" />
+            <datalist id="mejl">
+                <option value="Test">Test</option>
+                <option value="test2">test2</option>
+                <option value="test3">test3</option>
+            </datalist>
+        </div>
+        <div class="form-group">
+            <label class="control-label">type="url" (optional)</label>
+            <input type="url" />
+        </div>
+        <div class="form-group">
+            <label class="control-label">type="number"</label>
+            <input type="number" required="required" />
+        </div>
+        <div class="form-group">
+            <label class="control-label">type="number"</label>
+            <input type="number" required="required" maxlength="30" />
+        </div>
+        <div class="form-group">
+            <label class="control-label">type="number" range[-5;5]</label>
+            <input type="number" min="-5" max="5" required="required" />
+        </div>
+        <div class="form-group">
+            <label class="control-label">pattern="^([a-z]+)$"</label>
+            <input type="text" name="some-colorz" list="some-colorz" 
pattern="^([a-z]+)$" required="required" />
+            <datalist id="some-colorz" style="display: none">
+                <option value="Green">Green</option>
+                <option value="Blue">Blue</option>
+                <option value="Red">Red</option>
+                <option value="Black">Black</option>
+                <option value="White">White</option>
+            </datalist>
+        </div>
+        <p>
+            <input type="submit" class="button">
+            <input type="reset" class="button">
+        </p>
+    </form>
+</div>
+<script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
+<script src="//code.jquery.com/ui/1.10.4/jquery-ui.min.js"></script>
+<script src="jquery.form-validator.js"></script>
+<script>
+(function($, window) {
+
+    var dev = '.dev'; //window.location.hash.indexOf('dev') > -1 ? '.dev' : '';
+
+    // setup datepicker
+    $("#datepicker").datepicker();
+
+    // Add a new validator
+    $.formUtils.addValidator({
+        name : 'even_number',
+        validatorFunction : function(value, $el, config, language, $form) {
+            return parseInt(value, 10) % 2 === 0;
+        },
+        borderColorOnError : '',
+        errorMessage : 'You have to give an even number',
+        errorMessageKey: 'badEvenNumber'
+    });
+
+    window.applyValidation = function(validateOnBlur, forms, messagePosition) {
+        if( !forms )
+            forms = 'form';
+        if( !messagePosition )
+            messagePosition = 'top';
+
+        $.validate({
+            form : forms,
+            language : {
+                requiredFields: 'Du måste bocka för denna'
+            },
+            validateOnBlur : validateOnBlur,
+            errorMessagePosition : messagePosition,
+            scrollToTopOnError : true,
+            borderColorOnError : 'purple',
+            modules : 'security'+dev+', location'+dev+', sweden'+dev+', 
html5'+dev+', file'+dev+', uk'+dev,
+            onModulesLoaded: function() {
+                $('#country-suggestions').suggestCountry();
+                $('#swedish-county-suggestions').suggestSwedishCounty();
+                $('#password').displayPasswordStrength();
+            },
+            onValidate : function($f) {
+
+                console.log('about to validate form '+$f.attr('id'));
+
+                var $callbackInput = $('#callback');
+                if( $callbackInput.val() == 1 ) {
+                    return {
+                        element : $callbackInput,
+                        message : 'This validation was made in a callback'
+                    };
+                }
+            },
+            onError : function($form) {
+                if( !$.formUtils.haltValidation ) {
+                    alert('Invalid '+$form.attr('id'));
+                }
+            },
+            onSuccess : function($form) {
+                alert('Valid '+$form.attr('id'));
+                return false;
+            }
+        });
+    };
+
+    $('#text-area').restrictLength($('#max-len'));
+
+    window.applyValidation(true, '#form-a', 'top');
+    window.applyValidation(false, '#form-b', 'element');
+    window.applyValidation(true, '#form-c', $('#error-container'));
+    window.applyValidation(true, '#form-d', 'element');
+
+    // Load one module outside $.validate() even though you do not have to
+    $.formUtils.loadModules('date'+dev+'.js', false, false);
+
+    $('input')
+        .on('zbeforeValidation', function() {
+            console.log('About to validate input "'+this.name+'"');
+        })
+        .on('validationz', function(evt, isValid) {
+            var validationResult = '';
+            if( isValid === null ) {
+                validationResult = 'not validated';
+            } else if( isValid ) {
+                validationResult = 'VALID';
+            } else {
+                validationResult = 'INVALID';
+            }
+            console.log('Input '+this.name+' is '+validationResult);
+        });
+
+})(jQuery, window);
+</script>
+<body>
+</html>
\ No newline at end of file

Added: branches/dev-syncromind/phpgwapi/js/form-validator/html5.dev.js
===================================================================
--- branches/dev-syncromind/phpgwapi/js/form-validator/html5.dev.js             
                (rev 0)
+++ branches/dev-syncromind/phpgwapi/js/form-validator/html5.dev.js     
2014-10-09 19:00:00 UTC (rev 12129)
@@ -0,0 +1,144 @@
+/**
+ * jQuery Form Validator Module: html5
+ * ------------------------------------------
+ * Created by Victor Jonsson <http://www.victorjonsson.se>
+ *
+ * The following module will make this jQuery plugin serve as a
+ * html5 fallback. It makes older browsers support the following
+ *  - validation when type="email"
+ *  - validation when type="url"
+ *  - validation when type="time"
+ *  - validation when type="date"
+ *  - validation when type="number" and max="" min=""
+ *  - validation when pattern="REGEXP"
+ *  - validation when using maxlength
+ *  - Using datalist element for creating suggestions
+ *  - placeholders
+ *
+ * @website http://formvalidator.net/
+ * @license Dual licensed under the MIT or GPL Version 2 licenses
+ * @version 2.2.beta.13
+ */
+(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; // js max int
+
+                            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');
+                }
+
+                if( !SUPPORTS_DATALIST && $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);
\ No newline at end of file

Added: branches/dev-syncromind/phpgwapi/js/form-validator/html5.js
===================================================================
--- branches/dev-syncromind/phpgwapi/js/form-validator/html5.js                 
        (rev 0)
+++ branches/dev-syncromind/phpgwapi/js/form-validator/html5.js 2014-10-09 
19:00:00 UTC (rev 12129)
@@ -0,0 +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")}if(!SUPPORTS_DATALIST&&$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);
\ No newline at end of file

Added: 
branches/dev-syncromind/phpgwapi/js/form-validator/jquery.form-validator.js
===================================================================
--- branches/dev-syncromind/phpgwapi/js/form-validator/jquery.form-validator.js 
                        (rev 0)
+++ branches/dev-syncromind/phpgwapi/js/form-validator/jquery.form-validator.js 
2014-10-09 19:00:00 UTC (rev 12129)
@@ -0,0 +1,1689 @@
+/**
+* jQuery Form Validator
+* ------------------------------------------
+* Created by Victor Jonsson <http://www.victorjonsson.se>
+*
+* @website http://formvalidator.net/
+* @license Dual licensed under the MIT or GPL Version 2 licenses
+* @version 2.2.beta.13
+*/
+(function($) {
+
+    'use strict';
+
+    var $window = $(window),
+        _applyErrorStyle = function($elem, conf) {
+            var $parent = $elem.parent();
+            $elem
+                .addClass(conf.errorElementClass)
+                .removeClass('valid');
+            if($parent.hasClass("input-group")) $parent = $parent.parent();
+            
+            $parent.addClass('has-error') .removeClass('has-success'); 
//twitter bs
+                
+
+            if(conf.borderColorOnError !== '') {
+                $elem.css('border-color', conf.borderColorOnError);
+            }
+        },
+        _removeErrorStyle = function($elem, conf) {
+            $elem.each(function() {
+                _setInlineErrorMessage($(this), '', conf, 
conf.errorMessagePosition);
+                var $parent = $(this).parent();
+                
+                if($parent.hasClass("input-group")) $parent = $parent.parent();
+                $(this)
+                    .removeClass('valid')
+                    .removeClass(conf.errorElementClass)
+                    .css('border-color', '');
+                $parent
+                    .removeClass('has-error')
+                    .removeClass('has-success')
+                    .find('.'+conf.errorMessageClass) // remove inline error 
message
+                        .remove();
+            });
+        },
+        _setInlineErrorMessage = function($input, mess, conf, 
$messageContainer) {
+            var custom = _getInlineErrorElement($input);
+            var $parent = $input.parent();
+            if($parent.hasClass("input-group")) $parent = $parent.parent();
+            if( custom ) {
+                custom.innerHTML = mess;
+            }
+            else if( typeof $messageContainer == 'object' ) {
+                var $found = false;
+                
$messageContainer.find('.'+conf.errorMessageClass).each(function() {
+                    if( this.inputReferer == $input[0] ) {
+                        $found = $(this);
+                        return false;
+                    }
+                });
+                if( $found ) {
+                    if( !mess ) {
+                        $found.remove();
+                    } else {
+                        $found.html(mess);
+                    }
+                } else {
+                    var $mess = $('<div 
class="'+conf.errorMessageClass+'">'+mess+'</div>');
+                    $mess[0].inputReferer = $input[0];
+                    $messageContainer.prepend($mess);
+                }
+            }
+            else {
+                var $mess = 
$parent.find('.'+conf.errorMessageClass+'.help-block');
+                if( $mess.length == 0 ) {
+                    $mess = 
$('<span></span>').addClass('help-block').addClass(conf.errorMessageClass);
+                    $mess.appendTo($parent);
+                }
+                $mess.html(mess);
+            }
+        },
+        _getInlineErrorElement = function($input, conf) {
+            return document.getElementById($input.attr('name')+'_err_msg');
+        },
+        _templateMessage = function($form, title, errorMessages, conf) {
+            var messages = 
conf.errorMessageTemplate.messages.replace(/\{errorTitle\}/g, title);
+            var fields = [];
+            $.each(errorMessages, function(i, msg) {
+                
fields.push(conf.errorMessageTemplate.field.replace(/\{msg\}/g, msg));
+            });
+            messages = messages.replace(/\{fields\}/g, fields.join(''));
+            var container = 
conf.errorMessageTemplate.container.replace(/\{errorMessageClass\}/g, 
conf.errorMessageClass);
+            container = container.replace(/\{messages\}/g, messages);
+            $form.children().eq(0).before(container);
+        };
+
+    /**
+    * Assigns validateInputOnBlur function to elements blur event
+    *
+    * @param {Object} language Optional, will override $.formUtils.LANG
+    * @param {Object} settings Optional, will override the default settings
+    * @return {jQuery}
+    */
+    $.fn.validateOnBlur = function(language, settings) {
+        
this.find('input[data-validation],textarea[data-validation],select[data-validation]')
+            .bind('blur.validation', function() {
+                $(this).validateInputOnBlur(language, settings, true, 'blur');
+            });
+
+        return this;
+    };
+
+       /*
+        * Assigns validateInputOnBlur function to elements custom event
+        * @param {Object} language Optional, will override $.formUtils.LANG
+        * @param {Object} settings Optional, will override the default settings
+        * * @return {jQuery}   
+        */
+       $.fn.validateOnEvent = function(language, settings) {
+        
this.find('input[data-validation][data-validation-event],textarea[data-validation][data-validation-event],select[data-validation][data-validation-event]')
+                       .each(function(){
+                               var $el = $(this),
+                                   etype = $el.attr("data-validation-event");
+                               if (etype){
+                                       $el.bind(etype + ".validation", 
function(){
+                               $(this).validateInputOnBlur(language, settings, 
true, etype);
+                                       });
+                               }
+                       });
+               return this;
+       };
+
+    /**
+    * fade in help message when input gains focus
+    * fade out when input loses focus
+    * <input data-help="The info that I want to display for the user when 
input is focused" ... />
+    *
+    * @param {String} attrName - Optional, default is data-help
+    * @return {jQuery}
+    */
+    $.fn.showHelpOnFocus = function(attrName) {
+        if(!attrName) {
+            attrName = 'data-validation-help';
+        }
+
+        // Remove previously added event listeners
+        this.find('.has-help-txt')
+                .valAttr('has-keyup-event', false)
+                .removeClass('has-help-txt');
+
+        // Add help text listeners
+        this.find('textarea,input').each(function() {
+            var $elem = $(this),
+                className = 'jquery_form_help_' + ($elem.attr('name') || 
'').replace( /(:|\.|\[|\])/g, "" ),
+                help = $elem.attr(attrName);
+
+            if(help) {
+                $elem
+                    .addClass('has-help-txt')
+                    .unbind('focus.help')
+                    .bind('focus.help', function() {
+                        var $help = $elem.parent().find('.'+className);
+                        if($help.length == 0) {
+                            $help = $('<span />')
+                                        .addClass(className)
+                                        .addClass('help')
+                                        .addClass('help-block') // twitter bs
+                                        .text(help)
+                                        .hide();
+
+                            $elem.after($help);
+
+                        }
+                        $help.fadeIn();
+                    })
+                    .unbind('blur.help')
+                    .bind('blur.help', function() {
+                        $(this)
+                            .parent()
+                            .find('.'+className)
+                                .fadeOut('slow');
+                    });
+            }
+        });
+
+        return this;
+    };
+
+    /**
+    * Validate single input when it loses focus
+    * shows error message in a span element 
+    * that is appended to the parent element
+    *
+    * @param {Object} [language] Optional, will override $.formUtils.LANG
+    * @param {Object} [conf] Optional, will override the default settings
+    * @param {Boolean} attachKeyupEvent Optional
+    * @param {String} eventType
+    * @return {jQuery}
+    */
+    $.fn.validateInputOnBlur = function(language, conf, attachKeyupEvent, 
eventType) {
+        $.formUtils.eventType = eventType;
+
+        if( (this.valAttr('suggestion-nr') || this.valAttr('postpone') || 
this.hasClass('hasDatepicker')) && !window.postponedValidation ) {
+            // This validation has to be postponed
+            var _self = this,
+                postponeTime = this.valAttr('postpone') || 200;
+
+            window.postponedValidation = function() {
+                _self.validateInputOnBlur(language, conf, attachKeyupEvent, 
eventType);
+                window.postponedValidation = false;
+            };
+            setTimeout(function() {
+                if( window.postponedValidation ) {
+                    window.postponedValidation();
+                }
+            }, postponeTime);
+
+            return this;
+        }
+
+        language = $.extend({}, $.formUtils.LANG, language || {});
+        _removeErrorStyle(this, conf);
+        var $elem = this,
+            $form = $elem.closest("form"),
+            validationRule = $elem.attr(conf.validationRuleAttribute),
+            validation = $.formUtils.validateInput(
+                            $elem,
+                            language,
+                            $.extend({}, conf, 
{errorMessagePosition:'element'}),
+                            $form,
+                            eventType
+                        );
+        
+        if(validation === true) {
+            $elem
+                .addClass('valid')
+                .parent()
+                    .addClass('has-success'); // twitter bs
+        } else if(validation !== null) {
+
+            _applyErrorStyle($elem, conf);
+            _setInlineErrorMessage($elem, validation, conf, 
conf.errorMessagePosition);
+
+            if(attachKeyupEvent) {
+                $elem
+                    .unbind('keyup.validation')
+                    .bind('keyup.validation', function() {
+                        $(this).validateInputOnBlur(language, conf, false, 
'keyup');
+                    });
+            }
+        }
+
+        return this;
+    };
+
+    /**
+     * Short hand for fetching/adding/removing element attributes
+     * prefixed with 'data-validation-'
+     *
+     * @param {String} name
+     * @param {String|Boolean} [val]
+     * @return string|undefined
+     * @protected
+     */
+    $.fn.valAttr = function(name, val) {
+        if( val === undefined ) {
+            return this.attr('data-validation-'+name);
+        } else if( val === false || val === null ) {
+            return this.removeAttr('data-validation-'+name);
+        } else {
+            if(name.length > 0) name='-'+name;
+            return this.attr('data-validation'+name, val);
+        }
+    };
+
+    /**
+     * Function that validate all inputs in given element
+     *
+     * @param {Object} [language]
+     * @param {Object} [conf]
+     * @param {Boolean} [displayError] Defaults to true
+     */
+    $.fn.isValid = function(language, conf, displayError) {
+
+        if ($.formUtils.isLoadingModules) {
+            var $self = this;
+            setTimeout(function () {
+                $self.isValid(language, conf, displayError);
+            }, 200);
+            return null;
+        }
+
+        conf = $.extend({}, $.formUtils.defaultConfig(), conf || {});
+        language = $.extend({}, $.formUtils.LANG, language || {});
+        displayError = displayError !== false;
+
+        $.formUtils.isValidatingEntireForm = true;
+        $.formUtils.haltValidation = false;
+
+        /**
+         * Adds message to error message stack if not already in the message 
stack
+         *
+         * @param {String} mess
+         * @para {jQuery} $elem
+         */
+        var addErrorMessage = function(mess, $elem) {
+            // validate server side will return null as error message before 
the server is requested
+            if(mess !== null) {
+                if ($.inArray(mess, errorMessages) < 0) {
+                    errorMessages.push(mess);
+                }
+                errorInputs.push($elem);
+                $elem.attr('current-error', mess);
+                if( displayError )
+                    _applyErrorStyle($elem, conf);
+            }
+        },
+
+        /** Error messages for this validation */
+        errorMessages = [],
+
+        /** Input elements which value was not valid */
+        errorInputs = [],
+
+        /** Form instance */
+        $form = this,
+
+        /**
+         * Tells whether or not to validate element with this name and of this 
type
+         *
+         * @param {String} name
+         * @param {String} type
+         * @return {Boolean}
+         */
+        ignoreInput = function(name, type) {
+            if (type === 'submit' || type === 'button' || type == 'reset') {
+                return true;
+            }
+            return $.inArray(name, conf.ignore || []) > -1;
+        };
+
+        // Reset style and remove error class
+        if( displayError ) {
+            $form.find('.'+conf.errorMessageClass+'.alert').remove();
+            
_removeErrorStyle($form.find('.'+conf.errorElementClass+',.valid'), conf);
+        }
+
+        // Validate element values
+        
$form.find('input,textarea,select').filter(':not([type="submit"],[type="button"])').each(function()
 {
+            var $elem = $(this);
+            var elementType = $elem.attr('type');
+            if (!ignoreInput($elem.attr('name'), elementType)) {
+
+                var validation = $.formUtils.validateInput(
+                                $elem,
+                                language,
+                                conf,
+                                $form,
+                                'submit'
+                            );
+
+                // Run element validation callback
+                if( typeof conf.onElementValidate == 'function' ) {
+                    conf.onElementValidate((validation === true), $elem, 
$form, validation);
+                }
+
+                if(validation !== true) {
+                    addErrorMessage(validation, $elem);
+                } else {
+                    $elem
+                        .valAttr('current-error', false)
+                        .addClass('valid')
+                        .parent()
+                            .addClass('has-success');
+                }
+            }
+
+        });
+
+        // Run validation callback
+        if( typeof conf.onValidate == 'function' ) {
+            var errors = conf.onValidate($form);
+            if( $.isArray(errors) ) {
+                $.each(errors, function(i, err) {
+                    addErrorMessage(err.message, err.element);
+                });
+            }
+            else if( errors && errors.element && errors.message ) {
+                addErrorMessage(errors.message, errors.element);
+            }
+        }
+
+        // Reset form validation flag
+        $.formUtils.isValidatingEntireForm = false;
+
+        // Validation failed
+        if ( !$.formUtils.haltValidation && errorInputs.length > 0 ) {
+
+            if( displayError ) {
+                // display all error messages in top of form
+                if (conf.errorMessagePosition === 'top') {
+                    _templateMessage($form, language.errorTitle, 
errorMessages, conf);
+                }
+                // Customize display message
+                else if(conf.errorMessagePosition === 'custom') {
+                    if( typeof conf.errorMessageCustom === 'function' ) {
+                        conf.errorMessageCustom($form, language.errorTitle, 
errorMessages, conf);
+                    }
+                }
+                // Display error message below input field or in defined 
container
+                else  {
+                    $.each(errorInputs, function(i, $input) {
+                        _setInlineErrorMessage($input, 
$input.attr('current-error'), conf, conf.errorMessagePosition);
+                    });
+                }
+
+                if(conf.scrollToTopOnError) {
+                    $window.scrollTop($form.offset().top - 20);
+                }
+            }
+
+            return false;
+        }
+
+        return !$.formUtils.haltValidation;
+    };
+
+    /**
+     * @deprecated
+     * @param language
+     * @param conf
+     */
+    $.fn.validateForm = function(language, conf) {
+        if( window.console && typeof window.console.warn == 'function' ) {
+            window.console.warn('Use of deprecated function $.validateForm, 
use $.isValid instead');
+        }
+        return this.isValid(language, conf, true);
+    }
+
+    /**
+    * Plugin for displaying input length restriction
+    */
+    $.fn.restrictLength = function(maxLengthElement) {
+        new $.formUtils.lengthRestriction(this, maxLengthElement);
+        return this;
+    };
+
+    /**
+     * Add suggestion dropdown to inputs having data-suggestions with a comma
+     * separated string with suggestions
+     * @param {Array} [settings]
+     * @returns {jQuery}
+     */
+    $.fn.addSuggestions = function(settings) {
+        var sugs = false;
+        this.find('input').each(function() {
+            var $field = $(this);
+
+            sugs = $.split($field.attr('data-suggestions'));
+
+            if( sugs.length > 0 && !$field.hasClass('has-suggestions') ) {
+                $.formUtils.suggest($field, sugs, settings);
+                $field.addClass('has-suggestions');
+            }
+        });
+        return this;
+    };
+
+    /**
+     * A bit smarter split function
+     * @param {String} val
+     * @param {Function|String} [func]
+     * @param {String} [delim]
+     * @returns {Array|void}
+     */
+    $.split = function(val, func, delim) {
+        if( typeof func != 'function' ) {
+            // return string
+            if( !val )
+                return [];
+            var values = [];
+            $.each(val.split(func ? func:','), function(i,str) {
+                str = $.trim(str);
+                if( str.length )
+                    values.push(str);
+            });
+            return values;
+        } else if( val ) {
+            // use callback on each
+            if( !delim )
+                delim = ',';
+            $.each(val.split(delim), function(i, str) {
+                str = $.trim(str);
+                if( str.length )
+                    return func(str, i);
+            });
+        }
+    };
+
+    /**
+     * Short hand function that makes the validation setup require less code
+     * @param conf
+     */
+    $.validate = function(conf) {
+
+        var defaultConf = $.extend($.formUtils.defaultConfig(), {
+            form : 'form',
+                       /*
+                        * Enable custom event for validation
+                        */
+            validateOnEvent : true,
+            validateOnBlur : true,
+            showHelpOnFocus : true,
+            addSuggestions : true,
+            modules : '',
+            onModulesLoaded : null,
+            language : false,
+            onSuccess : false,
+            onError : false,
+            onElementValidate : false
+        });
+
+        conf = $.extend(defaultConf, conf || {});
+
+        // Add validation to forms
+        $(conf.form).each(function(i, form) {
+
+            var $form  = $(form);
+            $window.trigger('formValidationSetup', [$form]);
+
+            // Remove all event listeners previously added
+            $form.find('.has-help-txt')
+                .unbind('focus.validation')
+                .unbind('blur.validation');
+            $form
+                .removeClass('has-validation-callback')
+                .unbind('submit.validation')
+                .unbind('reset.validation')
+                .find('input[data-validation],textarea[data-validation]')
+                    .unbind('blur.validation');
+
+            // Validate when submitted
+            $form.bind('submit.validation', function() {
+                var $form = $(this);
+
+                if($.formUtils.isLoadingModules) {
+                    setTimeout(function() {
+                        $form.trigger('submit.validation');
+                    }, 200);
+                    return false;
+                }
+                var valid = $form.isValid(conf.language, conf);
+                if( valid && typeof conf.onSuccess == 'function') {
+                    var callbackResponse = conf.onSuccess($form);
+                    if( callbackResponse === false )
+                        return false;
+                } else if ( !valid && typeof conf.onError == 'function' ) {
+                    conf.onError($form);
+                    return false;
+                } else {
+                    return valid;
+                }
+            })
+            .bind('reset.validation', function() {
+                // remove messages
+                $(this).find('.'+conf.errorMessageClass+'.alert').remove();
+                
_removeErrorStyle($(this).find('.'+conf.errorElementClass+',.valid'), conf);
+            })
+            .addClass('has-validation-callback');
+
+            if( conf.showHelpOnFocus ) {
+                $form.showHelpOnFocus();
+            }
+            if( conf.addSuggestions ) {
+                $form.addSuggestions();
+            }
+            if( conf.validateOnBlur ) {
+                $form.validateOnBlur(conf.language, conf);
+                $form.bind('html5ValidationAttrsFound', function() {
+                    $form.validateOnBlur(conf.language, conf);
+                })
+            }
+                       if( conf.validateOnEvent ){
+                $form.validateOnEvent(conf.language, conf);
+                       }
+
+        });
+
+        if( conf.modules != '' ) {
+            if( typeof conf.onModulesLoaded == 'function' ) {
+                $window.one('validatorsLoaded', conf.onModulesLoaded);
+            }
+            $.formUtils.loadModules(conf.modules);
+        }
+    };
+
+    /**
+     * Object containing utility methods for this plugin
+     */
+    $.formUtils = {
+
+        /**
+         * Default config for $(...).isValid();
+         */
+        defaultConfig :  function() {
+            return {
+                ignore : [], // Names of inputs not to be validated even 
though node attribute containing the validation rules tells us to
+                errorElementClass : 'error', // Class that will be put on 
elements which value is invalid
+                borderColorOnError : 'red', // Border color of elements which 
value is invalid, empty string to not change border color
+                errorMessageClass : 'form-error', // class name of div 
containing error messages when validation fails
+                validationRuleAttribute : 'data-validation', // name of the 
attribute holding the validation rules
+                validationErrorMsgAttribute : 'data-validation-error-msg', // 
define custom err msg inline with element
+                errorMessagePosition : 'element', // Can be either "top" or 
"element" or "custom"
+                errorMessageTemplate : {
+                    container: '<div class="{errorMessageClass} alert 
alert-danger">{messages}</div>',
+                    messages: '<strong>{errorTitle}</strong><ul>{fields}</ul>',
+                    field: '<li>{msg}</li>'
+                },
+                errorMessageCustom: _templateMessage,
+                scrollToTopOnError : true,
+                dateFormat : 'yyyy-mm-dd',
+                addValidClassOnAll : false, // whether or not to apply 
class="valid" even if the input wasn't validated
+                decimalSeparator : '.'
+            }
+        },
+
+        /**
+        * Available validators
+        */
+        validators : {},
+
+        /**
+         * Events triggered by form validator
+         */
+        _events : {load : [], valid: [], invalid:[]},
+
+        /**
+         * Setting this property to true during validation will
+         * stop further validation from taking place and form will
+         * not be sent
+         */
+        haltValidation : false,
+
+        /**
+         * This variable will be true $.fn.isValid() is called
+         * and false when $.fn.validateOnBlur is called
+         */
+        isValidatingEntireForm : false,
+
+        /**
+        * Function for adding a validator
+        * @param {Object} validator
+        */
+        addValidator : function(validator) {
+            // prefix with "validate_" for backward compatibility reasons
+            var name = validator.name.indexOf('validate_') === 0 ? 
validator.name : 'validate_'+validator.name;
+            if( validator.validateOnKeyUp === undefined )
+                validator.validateOnKeyUp = true;
+            this.validators[name] = validator;
+        },
+
+        /**
+         * @var {Boolean}
+         */
+        isLoadingModules : false,
+
+        /**
+         * @var {Object}
+         */
+        loadedModules : {},
+
+        /**
+        * @example
+        *  $.formUtils.loadModules('date, security.dev');
+        *
+        * Will load the scripts date.js and security.dev.js from the
+        * directory where this script resides. If you want to load
+        * the modules from another directory you can use the
+        * path argument.
+        *
+        * The script will be cached by the browser unless the module
+        * name ends with .dev
+        *
+        * @param {String} modules - Comma separated string with module file 
names (no directory nor file extension)
+        * @param {String} [path] - Optional, path where the module files is 
located if their not in the same directory as the core modules
+        * @param {Boolean} [fireEvent] - Optional, whether or not to fire 
event 'load' when modules finished loading
+        */
+        loadModules : function(modules, path, fireEvent) {
+
+            if( fireEvent === undefined )
+                fireEvent = true;
+
+            if( $.formUtils.isLoadingModules ) {
+                setTimeout(function() {
+                    $.formUtils.loadModules(modules, path, fireEvent);
+                });
+                return;
+            }
+
+            var hasLoadedAnyModule = false,
+                loadModuleScripts = function(modules, path) {
+
+                    var moduleList = $.split(modules),
+                        numModules = moduleList.length,
+                        moduleLoadedCallback = function() {
+                            numModules--;
+                            if( numModules == 0 ) {
+                                $.formUtils.isLoadingModules = false;
+                                if( fireEvent && hasLoadedAnyModule ) {
+                                    $window.trigger('validatorsLoaded');
+                                }
+                            }
+                        };
+
+                    if( numModules > 0 ) {
+                        $.formUtils.isLoadingModules = true;
+                    }
+
+                    var cacheSuffix = '?__='+( new Date().getTime() ),
+                        appendToElement = 
document.getElementsByTagName('head')[0] || 
document.getElementsByTagName('body')[0];
+
+                    $.each(moduleList, function(i, modName) {
+                        modName = $.trim(modName);
+                        if( modName.length == 0 ) {
+                            moduleLoadedCallback();
+                        }
+                        else {
+                            var scriptUrl = path + modName + 
(modName.substr(-3) == '.js' ? '':'.js'),
+                                script = document.createElement('SCRIPT');
+
+                            if( scriptUrl in $.formUtils.loadedModules ) {
+                                // already loaded
+                                moduleLoadedCallback();
+                            }
+                            else {
+
+                                // Remember that this script is loaded
+                                $.formUtils.loadedModules[scriptUrl] = 1;
+                                hasLoadedAnyModule = true;
+
+                                // Load the script
+                                script.type = 'text/javascript';
+                                script.onload = moduleLoadedCallback;
+                                script.src = scriptUrl + ( 
scriptUrl.substr(-7) == '.dev.js' ? cacheSuffix:'' );
+                                script.onreadystatechange = function() {
+                                    // IE 7 fix
+                                    if( this.readyState == 'complete' || 
this.readyState == 'loaded' ) {
+                                        moduleLoadedCallback();
+                                        // Handle memory leak in IE
+                                        this.onload = null;
+                                        this.onreadystatechange = null;
+                                    }
+                                };
+                                appendToElement.appendChild( script );
+                            }
+                        }
+                    });
+                };
+
+            if( path ) {
+                loadModuleScripts(modules, path);
+            } else {
+                var findScriptPathAndLoadModules = function() {
+                    var foundPath = false;
+                    $('script[src*="form-validator"]').each(function() {
+                        foundPath = this.src.substr(0, 
this.src.lastIndexOf('/')) + '/';
+                        if( foundPath == '/' )
+                            foundPath = '';
+                        return false;
+                    });
+
+                    if( foundPath !== false) {
+                        loadModuleScripts(modules, foundPath);
+                        return true;
+                    }
+                    return false;
+                };
+
+                if( !findScriptPathAndLoadModules() ) {
+                    $(findScriptPathAndLoadModules);
+                }
+            }
+        },
+
+        /**
+        * Validate the value of given element according to the validation rules
+        * found in the attribute data-validation. Will return null if no 
validation
+        * should take place, returns true if valid or error message if not 
valid
+        *
+        * @param {jQuery} $elem
+        * @param {Object} language ($.formUtils.LANG)
+        * @param {Object} conf
+        * @param {jQuery} $form
+        * @param {String} [eventContext]
+        * @return {String|Boolean}
+        */
+        validateInput : function($elem, language, conf, $form, eventContext) {
+
+            if( $elem.attr('disabled') )
+                return null; // returning null will prevent that the valid 
class gets applied to the element
+
+            $elem.trigger('beforeValidation');
+
+            var value = $elem.val() || '',
+                optional = $elem.valAttr('optional'),
+
+                // test if a checkbox forces this element to be validated
+                validationDependsOnCheckedInput = false,
+                validationDependentInputIsChecked = false,
+                validateIfCheckedElement = false,
+
+                // get value of this element's attribute "... if-checked"
+                validateIfCheckedElementName = $elem.valAttr("if-checked");
+
+            // make sure we can proceed
+            if (validateIfCheckedElementName != null) {
+
+                // Set the boolean telling us that the validation depends
+                // on another input being checked
+                validationDependsOnCheckedInput = true;
+
+                // select the checkbox type element in this form
+                validateIfCheckedElement = $form.find('input[name="' + 
validateIfCheckedElementName + '"]');
+
+                // test if it's property "checked" is checked
+                if ( validateIfCheckedElement.prop('checked') ) {
+                    // set value for validation checkpoint
+                    validationDependentInputIsChecked = true;
+                }
+            }
+
+            // validation checkpoint
+            // if empty AND optional attribute is present
+            // OR depending on a checkbox being checked AND checkbox is 
checked, return true
+            if ((!value && optional === 'true') || 
(validationDependsOnCheckedInput && !validationDependentInputIsChecked)) {
+                return conf.addValidClassOnAll ? true:null;
+            }
+
+            var validationRules = $elem.attr(conf.validationRuleAttribute),
+
+                // see if form element has inline err msg attribute
+                validationErrorMsg = true;
+
+            if( !validationRules ) {
+                return conf.addValidClassOnAll ? true:null;
+            }
+
+            $.split(validationRules, function(rule) {
+                if( rule.indexOf('validate_') !== 0 ) {
+                    rule = 'validate_' + rule;
+                }
+
+                var validator = $.formUtils.validators[rule];
+
+                if( validator && typeof validator['validatorFunction'] == 
'function' ) {
+                    // special change of element for checkbox_group rule
+                    if ( rule == 'validate_checkbox_group' ) {
+                        // set element to first in group, so error msg is set 
only once
+                            $elem = $("[name='"+$elem.attr('name')+"']:eq(0)");
+                    }
+
+                    var isValid = null;
+                    if( eventContext != 'keyup' || validator.validateOnKeyUp ) 
{
+                        isValid = validator.validatorFunction(value, $elem, 
conf, language, $form);
+                    }
+
+                    if(!isValid) {
+                        validationErrorMsg = null;
+                        if( isValid !== null ) {
+                            validationErrorMsg =  
$elem.attr(conf.validationErrorMsgAttribute+'-'+rule.replace('validate_', ''));
+                            if( !validationErrorMsg ) {
+                                validationErrorMsg =  
$elem.attr(conf.validationErrorMsgAttribute);
+                                if( !validationErrorMsg ) {
+                                    validationErrorMsg = 
language[validator.errorMessageKey];
+                                    if( !validationErrorMsg )
+                                        validationErrorMsg = 
validator.errorMessage;
+                                }
+                            }
+                        }
+                        return false; // breaks the iteration
+                    }
+
+                } else {
+                    throw new Error('Using undefined validator "'+rule+'"');
+                }
+
+            }, ' ');
+
+            if( typeof validationErrorMsg == 'string' ) {
+                $elem.trigger('validation', false);
+                return validationErrorMsg;
+            } else if( validationErrorMsg === null && !conf.addValidClassOnAll 
) {
+                return null;
+            } else {
+                $elem.trigger('validation', true);
+                return true;
+            }
+        },
+
+       /**
+        * Is it a correct date according to given dateFormat. Will return 
false if not, otherwise
+        * an array 0=>year 1=>month 2=>day
+        *
+        * @param {String} val
+        * @param {String} dateFormat
+        * @return {Array}|{Boolean}
+        */
+        parseDate : function(val, dateFormat) {
+            var divider = dateFormat.replace(/[a-zA-Z]/gi, '').substring(0,1),
+                regexp = '^',
+                formatParts = dateFormat.split(divider),
+                matches, day, month, year;
+
+            $.each(formatParts, function(i, part) {
+               regexp += (i > 0 ? '\\'+divider:'') + '(\\d{'+part.length+'})';
+            });
+
+            regexp += '$';
+            
+            matches = val.match(new RegExp(regexp));
+            if (matches === null) {
+                return false;
+            }
+        
+            var findDateUnit = function(unit, formatParts, matches) {
+                for(var i=0; i < formatParts.length; i++) {
+                    if(formatParts[i].substring(0,1) === unit) {
+                        return $.formUtils.parseDateInt(matches[i+1]);
+                    }
+                }
+                return -1;
+            };
+        
+            month = findDateUnit('m', formatParts, matches);
+            day = findDateUnit('d', formatParts, matches);
+            year = findDateUnit('y', formatParts, matches);
+        
+            if ((month === 2 && day > 28 && (year % 4 !== 0  || year % 100 === 
0 && year % 400 !== 0)) 
+               || (month === 2 && day > 29 && (year % 4 === 0 || year % 100 
!== 0 && year % 400 === 0))
+               || month > 12 || month === 0) {
+                return false;
+            }
+            if ((this.isShortMonth(month) && day > 30) || 
(!this.isShortMonth(month) && day > 31) || day === 0) {
+                return false;
+            }
+        
+            return [year, month, day];
+        },
+
+       /**
+        * skum fix. är talet 05 eller lägre ger parseInt rätt int annars får 
man 0 när man kör parseInt?
+        *
+        * @param {String} val
+        * @param {Number}
+        */
+        parseDateInt : function(val) {
+            if (val.indexOf('0') === 0) {
+                val = val.replace('0', '');
+            }
+            return parseInt(val,10);
+        },
+
+        /**
+        * Has month only 30 days?
+        *
+        * @param {Number} m
+        * @return {Boolean}
+        */
+        isShortMonth : function(m) {
+            return (m % 2 === 0 && m < 7) || (m % 2 !== 0 && m > 7);
+        },
+
+       /**
+        * Restrict input length
+        *
+        * @param {jQuery} $inputElement Jquery Html object
+        * @param {jQuery} $maxLengthElement jQuery Html Object
+        * @return void
+        */
+        lengthRestriction : function($inputElement, $maxLengthElement) {
+                // read maxChars from counter display initial text value
+           var maxChars = parseInt($maxLengthElement.text(),10),
+                charsLeft = 0,
+
+               // internal function does the counting and sets display value
+               countCharacters = function() {
+                   var numChars = $inputElement.val().length;
+                   if(numChars > maxChars) {
+                       // get current scroll bar position
+                       var currScrollTopPos = $inputElement.scrollTop();
+                       // trim value to max length
+                       $inputElement.val($inputElement.val().substring(0, 
maxChars));
+                       $inputElement.scrollTop(currScrollTopPos);
+                   }
+                   charsLeft = maxChars - numChars;
+                   if( charsLeft < 0 )
+                       charsLeft = 0;
+
+                   // set counter text
+                   $maxLengthElement.text(charsLeft);
+               };
+
+           // bind events to this element
+           // setTimeout is needed, cut or paste fires before val is available
+           $($inputElement).bind('keydown keyup keypress focus blur',  
countCharacters )
+               .bind('cut paste', function(){ setTimeout(countCharacters, 
100); } ) ;
+
+           // count chars on pageload, if there are prefilled input-values
+           $(document).bind("ready", countCharacters);
+        },
+
+        /**
+        * Test numeric against allowed range
+        *
+        * @param $value int
+        * @param $rangeAllowed str; (1-2, min1, max2)
+        * @return array 
+        */
+        numericRangeCheck : function(value, rangeAllowed) 
+        {
+            // split by dash
+            var range = $.split(rangeAllowed, '-');
+            // min or max
+            var minmax = parseInt(rangeAllowed.substr(3),10)
+            // range ?
+            if (range.length == 2 && (value < parseInt(range[0],10) || value > 
parseInt(range[1],10) ) )
+            {   return [ "out", range[0], range[1] ] ; } // value is out of 
range
+            else if (rangeAllowed.indexOf('min') === 0 && (value < minmax ) ) 
// min
+            {  return ["min", minmax]; } // value is below min
+            else if (rangeAllowed.indexOf('max') === 0 && (value > minmax ) ) 
// max
+            {   return ["max", minmax]; } // value is above max
+            // since no other returns executed, value is in allowed range
+            return [ "ok" ] ; 
+        },
+
+
+        _numSuggestionElements : 0,
+        _selectedSuggestion : null,
+        _previousTypedVal : null,
+
+        /**
+         * Utility function that can be used to create plugins that gives
+         * suggestions when inputs is typed into
+         * @param {jQuery} $elem
+         * @param {Array} suggestions
+         * @param {Object} settings - Optional
+         * @return {jQuery}
+         */
+        suggest : function($elem, suggestions, settings) {
+            var conf =  {
+                css : {
+                    maxHeight: '150px',
+                    background: '#FFF',
+                    lineHeight:'150%',
+                    textDecoration : 'underline',
+                    overflowX : 'hidden',
+                    overflowY : 'auto',
+                    border : '#CCC solid 1px',
+                    borderTop : 'none',
+                    cursor: 'pointer'
+                },
+                activeSuggestionCSS : {
+                    background : '#E9E9E9'
+                }
+            },
+            setSuggsetionPosition = function($suggestionContainer, $input) {
+                var offset = $input.offset();
+                $suggestionContainer.css({
+                    width : $input.outerWidth(),
+                    left : offset.left + 'px',
+                    top : (offset.top + $input.outerHeight()) +'px'
+                });
+            };
+
+            if(settings)
+                $.extend(conf, settings);
+
+            conf.css['position'] = 'absolute';
+            conf.css['z-index'] = 9999;
+            $elem.attr('autocomplete', 'off');
+
+            if( this._numSuggestionElements === 0 ) {
+                // Re-position suggestion container if window size changes
+                $window.bind('resize', function() {
+                    $('.jquery-form-suggestions').each(function() {
+                        var $container = $(this),
+                            suggestID = 
$container.attr('data-suggest-container');
+                        setSuggsetionPosition($container, 
$('.suggestions-'+suggestID).eq(0));
+                    });
+                });
+            }
+
+            this._numSuggestionElements++;
+
+            var onSelectSuggestion = function($el) {
+                var suggestionId = $el.valAttr('suggestion-nr');
+                $.formUtils._selectedSuggestion = null;
+                $.formUtils._previousTypedVal = null;
+                $('.jquery-form-suggestion-'+suggestionId).fadeOut('fast');
+            };
+
+            $elem
+                .data('suggestions', suggestions)
+                .valAttr('suggestion-nr', this._numSuggestionElements)
+                .unbind('focus.suggest')
+                .bind('focus.suggest', function() {
+                    $(this).trigger('keyup');
+                    $.formUtils._selectedSuggestion = null;
+                })
+                .unbind('keyup.suggest')
+                .bind('keyup.suggest', function() {
+                    var $input = $(this),
+                        foundSuggestions = [],
+                        val = $.trim($input.val()).toLocaleLowerCase();
+
+                    if(val == $.formUtils._previousTypedVal) {
+                        return;
+                    }
+                    else {
+                        $.formUtils._previousTypedVal = val;
+                    }
+
+                    var hasTypedSuggestion = false,
+                        suggestionId = $input.valAttr('suggestion-nr'),
+                        $suggestionContainer = 
$('.jquery-form-suggestion-'+suggestionId);
+
+                    $suggestionContainer.scrollTop(0);
+
+                    // Find the right suggestions
+                    if(val != '') {
+                        var findPartial = val.length > 2;
+                        $.each($input.data('suggestions'), function(i, 
suggestion) {
+                            var lowerCaseVal = suggestion.toLocaleLowerCase();
+                            if( lowerCaseVal == val ) {
+                                
foundSuggestions.push('<strong>'+suggestion+'</strong>');
+                                hasTypedSuggestion = true;
+                                return false;
+                            } else if(lowerCaseVal.indexOf(val) === 0 || 
(findPartial && lowerCaseVal.indexOf(val) > -1)) {
+                                foundSuggestions.push(suggestion.replace(new 
RegExp(val, 'gi'), '<strong>$&</strong>'));
+                            }
+                        });
+                    }
+
+                    // Hide suggestion container
+                    if(hasTypedSuggestion || (foundSuggestions.length == 0 && 
$suggestionContainer.length > 0)) {
+                        $suggestionContainer.hide();
+                    }
+
+                    // Create suggestion container if not already exists
+                    else if(foundSuggestions.length > 0 && 
$suggestionContainer.length == 0) {
+                        $suggestionContainer = 
$('<div></div>').css(conf.css).appendTo('body');
+                        $elem.addClass('suggestions-'+suggestionId);
+                        $suggestionContainer
+                            .attr('data-suggest-container', suggestionId)
+                            .addClass('jquery-form-suggestions')
+                            .addClass('jquery-form-suggestion-'+suggestionId);
+                    }
+
+                    // Show hidden container
+                    else if(foundSuggestions.length > 0 && 
!$suggestionContainer.is(':visible')) {
+                        $suggestionContainer.show();
+                    }
+
+                    // add suggestions
+                    if(foundSuggestions.length > 0 && val.length != 
foundSuggestions[0].length) {
+
+                        // put container in place every time, just in case
+                        setSuggsetionPosition($suggestionContainer, $input);
+
+                        // Add suggestions HTML to container
+                        $suggestionContainer.html('');
+                        $.each(foundSuggestions, function(i, text) {
+                            $('<div></div>')
+                                .append(text)
+                                .css({
+                                    overflow: 'hidden',
+                                    textOverflow : 'ellipsis',
+                                    whiteSpace : 'nowrap',
+                                    padding: '5px'
+                                })
+                                .addClass('form-suggest-element')
+                                .appendTo($suggestionContainer)
+                                .click(function() {
+                                    $input.focus();
+                                    $input.val( $(this).text() );
+                                    onSelectSuggestion($input);
+                                });
+                        });
+                    }
+                })
+                .unbind('keydown.validation')
+                .bind('keydown.validation', function(e) {
+                    var code = (e.keyCode ? e.keyCode : e.which),
+                        suggestionId,
+                        $suggestionContainer,
+                        $input = $(this);
+
+                    if(code == 13 && $.formUtils._selectedSuggestion !== null) 
{
+                        suggestionId = $input.valAttr('suggestion-nr');
+                        $suggestionContainer = 
$('.jquery-form-suggestion-'+suggestionId);
+                        if($suggestionContainer.length > 0) {
+                            var newText = 
$suggestionContainer.find('div').eq($.formUtils._selectedSuggestion).text();
+                            $input.val(newText);
+                            onSelectSuggestion($input);
+                            e.preventDefault();
+                        }
+                    }
+                    else {
+                        suggestionId = $input.valAttr('suggestion-nr');
+                        $suggestionContainer = 
$('.jquery-form-suggestion-'+suggestionId);
+                        var $suggestions = $suggestionContainer.children();
+                        if($suggestions.length > 0 && $.inArray(code, [38,40]) 
> -1) {
+                            if(code == 38) { // key up
+                                if($.formUtils._selectedSuggestion === null)
+                                    $.formUtils._selectedSuggestion = 
$suggestions.length-1;
+                                else
+                                    $.formUtils._selectedSuggestion--;
+                                if($.formUtils._selectedSuggestion < 0)
+                                    $.formUtils._selectedSuggestion = 
$suggestions.length-1;
+                            }
+                            else if(code == 40) { // key down
+                                if($.formUtils._selectedSuggestion === null)
+                                    $.formUtils._selectedSuggestion = 0;
+                                else
+                                    $.formUtils._selectedSuggestion++;
+                                if($.formUtils._selectedSuggestion > 
($suggestions.length-1))
+                                    $.formUtils._selectedSuggestion = 0;
+
+                            }
+
+                            // Scroll in suggestion window
+                            var containerInnerHeight = 
$suggestionContainer.innerHeight(),
+                                containerScrollTop = 
$suggestionContainer.scrollTop(),
+                                suggestionHeight = 
$suggestionContainer.children().eq(0).outerHeight(),
+                                activeSuggestionPosY = suggestionHeight * 
($.formUtils._selectedSuggestion);
+
+                            if( activeSuggestionPosY < containerScrollTop || 
activeSuggestionPosY > (containerScrollTop+containerInnerHeight)) {
+                                $suggestionContainer.scrollTop( 
activeSuggestionPosY );
+                            }
+
+                            $suggestions
+                                .removeClass('active-suggestion')
+                                .css('background', 'none')
+                                .eq($.formUtils._selectedSuggestion)
+                                    .addClass('active-suggestion')
+                                    .css(conf.activeSuggestionCSS);
+
+                            e.preventDefault();
+                            return false;
+                        }
+                    }
+                })
+                .unbind('blur.suggest')
+                .bind('blur.suggest', function() {
+                    onSelectSuggestion($(this));
+                });
+
+            return $elem;
+        },
+
+       /**
+        * Error dialogs
+        *
+        * @var {Object}
+        */
+        LANG : {
+            errorTitle : 'Form submission failed!',
+            requiredFields : 'You have not answered all required fields',
+            badTime : 'You have not given a correct time',
+            badEmail : 'You have not given a correct e-mail address',
+            badTelephone : 'You have not given a correct phone number',
+            badSecurityAnswer : 'You have not given a correct answer to the 
security question',
+            badDate : 'You have not given a correct date',
+            lengthBadStart : 'The input value must be between ',
+            lengthBadEnd : ' characters',
+            lengthTooLongStart : 'The input value is longer than ',
+            lengthTooShortStart : 'The input value is shorter than ',
+            notConfirmed : 'Input values could not be confirmed',
+            badDomain : 'Incorrect domain value',
+            badUrl : 'The input value is not a correct URL',
+            badCustomVal : 'The input value is incorrect',
+            badInt : 'The input value was not a correct number',
+            badSecurityNumber : 'Your social security number was incorrect',
+            badUKVatAnswer : 'Incorrect UK VAT Number',
+            badStrength : 'The password isn\'t strong enough',
+            badNumberOfSelectedOptionsStart : 'You have to choose at least ',
+            badNumberOfSelectedOptionsEnd : ' answers',
+            badAlphaNumeric : 'The input value can only contain alphanumeric 
characters ',
+            badAlphaNumericExtra: ' and ',
+            wrongFileSize : 'The file you are trying to upload is too large',
+            wrongFileType : 'The file you are trying to upload is of wrong 
type',
+            groupCheckedRangeStart : 'Please choose between ',
+            groupCheckedTooFewStart : 'Please choose at least ',
+            groupCheckedTooManyStart : 'Please choose a maximum of ',
+            groupCheckedEnd : ' item(s)',
+            badCreditCard : 'The credit card number is not correct',
+            badCVV : 'The CVV number was not correct'
+        }
+    };
+
+
+    /* * * * * * * * * * * * * * * * * * * * * *
+      CORE VALIDATORS
+    * * * * * * * * * * * * * * * * * * * * */
+
+
+    /*
+    * Validate email
+    */
+    $.formUtils.addValidator({
+        name : 'email',
+        validatorFunction : function(email) {
+
+            var emailParts = email.toLowerCase().split('@');
+            if( emailParts.length == 2 ) {
+                return 
$.formUtils.validators.validate_domain.validatorFunction(emailParts[1]) &&
+                        !(/[^\w\+\.\-]/.test(emailParts[0]));
+            }
+
+            return false;
+        },
+        errorMessage : '',
+        errorMessageKey : 'badEmail'
+    });
+
+    /*
+    * Validate domain name
+    */
+    $.formUtils.addValidator({
+        name : 'domain',
+        validatorFunction : function(val, $input) {
+
+            var topDomains =  ['.ac', '.ad', '.ae', '.aero', '.af', '.ag', 
'.ai', '.al', '.am', '.an', '.ao',
+                        '.aq', '.ar', '.arpa', '.as', '.asia', '.at', '.au', 
'.aw', '.ax', '.az', '.ba', '.bb',
+                        '.bd', '.be', '.bf', '.bg', '.bh', '.bi', '.bike', 
'.biz', '.bj', '.bm', '.bn', '.bo',
+                        '.br', '.bs', '.bt', '.bv', '.bw', '.by', '.bz', 
'.ca', '.camera', '.cat', '.cc', '.cd',
+                        '.cf', '.cg', '.ch', '.ci', '.ck', '.cl', '.clothing', 
'.cm', '.cn', '.co', '.com',
+                        '.construction', '.contractors', '.coop', '.cr', 
'.cu', '.cv', '.cw', '.cx', '.cy', '.cz',
+                        '.de', '.diamonds', '.directory', '.dj', '.dk', '.dm', 
'.do', '.dz', '.ec', '.edu', '.ee',
+                        '.eg', '.enterprises', '.equipment', '.er', '.es', 
'.estate', '.et', '.eu', '.fi', '.fj',
+                        '.fk', '.fm', '.fo', '.fr', '.ga', '.gallery', '.gb', 
'.gd', '.ge', '.gf', '.gg', '.gh',
+                        '.gi', '.gl', '.gm', '.gn', '.gov', '.gp', '.gq', 
'.gr', '.graphics', '.gs', '.gt', '.gu',
+                        '.guru', '.gw', '.gy', '.hk', '.hm', '.hn', 
'.holdings', '.hr', '.ht', '.hu', '.id', '.ie',
+                        '.il', '.im', '.in', '.info', '.int', '.io', '.iq', 
'.ir', '.is', '.it', '.je', '.jm', '.jo',
+                        '.jobs', '.jp', '.ke', '.kg', '.kh', '.ki', 
'.kitchen', '.km', '.kn', '.kp', '.kr', '.kw',
+                        '.ky', '.kz', '.la', '.land', '.lb', '.lc', '.li', 
'.lighting', '.lk', '.lr', '.ls', '.lt',
+                        '.lu', '.lv', '.ly', '.ma', '.mc', '.md', '.me', 
'.menu', '.mg', '.mh', '.mil', '.mk', '.ml',
+                        '.mm', '.mn', '.mo', '.mobi', '.mp', '.mq', '.mr', 
'.ms', '.mt', '.mu', '.museum', '.mv',
+                        '.mw', '.mx', '.my', '.mz', '.na', '.name', '.nc', 
'.ne', '.net', '.nf', '.ng', '.ni',
+                        '.nl', '.no', '.np', '.nr', '.nu', '.nz', '.om', 
'.org', '.pa', '.pe', '.pf', '.pg', '.ph',
+                        '.photography', '.pk', '.pl', '.plumbing', '.pm', 
'.pn', '.post', '.pr', '.pro', '.ps', '.pt',
+                        '.pw', '.py', '.qa', '.re', '.ro', '.rs', '.ru', 
'.rw', '.sa', '.sb', '.sc', '.sd', '.se',
+                        '.sexy', '.sg', '.sh', '.si', '.singles', '.sj', 
'.sk', '.sl', '.sm', '.sn', '.so', '.sr',
+                        '.st', '.su', '.sv', '.sx', '.sy', '.sz', '.tattoo', 
'.tc', '.td', '.technology', '.tel', '.tf',
+                        '.tg', '.th', '.tips', '.tj', '.tk', '.tl', '.tm', 
'.tn', '.to', '.today', '.tp', '.tr', '.travel',
+                        '.tt', '.tv', '.tw', '.tz', '.ua', '.ug', '.uk', 
'.uno', '.us', '.uy', '.uz', '.va', '.vc', '.ve',
+                        '.ventures', '.vg', '.vi', '.vn', '.voyage', '.vu', 
'.wf', '.ws', '.xn--3e0b707e', '.xn--45brj9c',
+                        '.xn--80ao21a', '.xn--80asehdb', '.xn--80aswg', 
'.xn--90a3ac', '.xn--clchc0ea0b2g2a9gcd', '.xn--fiqs8s',
+                        '.xn--fiqz9s', '.xn--fpcrj9c3d', '.xn--fzc2c9e2c', 
'.xn--gecrj9c', '.xn--h2brj9c', '.xn--j1amh',
+                        '.xn--j6w193g', '.xn--kprw13d', '.xn--kpry57d', 
'.xn--l1acc', '.xn--lgbbat1ad8j', '.xn--mgb9awbf',
+                        '.xn--mgba3a4f16a', '.xn--mgbaam7a8h', 
'.xn--mgbayh7gpa', '.xn--mgbbh1a71e', '.xn--mgbc0a9azcg',
+                        '.xn--mgberp4a5d4ar', '.xn--mgbx4cd0ab', 
'.xn--ngbc5azd', '.xn--o3cw4h', '.xn--ogbpf8fl', '.xn--p1ai',
+                        '.xn--pgbs0dh', '.xn--q9jyb4c', '.xn--s9brj9c', 
'.xn--unup4y', '.xn--wgbh1c', '.xn--wgbl6a',
+                        '.xn--xkc2al3hye2a', '.xn--xkc2dl3a5ee0h', 
'.xn--yfro4i67o', '.xn--ygbi2ammx', '.xxx', '.ye',
+                        '.yt', '.za', '.zm', '.zw'],
+
+                ukTopDomains = ['co', 'me', 'ac', 'gov', 'judiciary','ltd', 
'mod', 'net', 'nhs', 'nic',
+                        'org', 'parliament', 'plc', 'police', 'sch', 'bl', 
'british-library', 'jet','nls'],
+
+                dot = val.lastIndexOf('.'),
+                domain = val.substring(0, dot),
+                ext = val.substring(dot, val.length),
+                hasTopDomain = false;
+
+            for (var i = 0; i < topDomains.length; i++) {
+                if (topDomains[i] === ext) {
+                    if(ext==='.uk') {
+                        //Run Extra Checks for UK Domain Names
+                        var domainParts = val.split('.');
+                        var tld2 = domainParts[domainParts.length-2];
+                        for(var j = 0; j < ukTopDomains.length; j++) {
+                            if(ukTopDomains[j] === tld2) {
+                                hasTopDomain = true;
+                                break;
+                            }
+                        }
+
+                        if(hasTopDomain)
+                            break;
+
+                    } else {
+                        hasTopDomain = true;
+                        break;
+                    }
+                }
+            }
+
+            if (!hasTopDomain) {
+                return false;
+            } else if (dot < 2 || dot > 57) {
+                return $.inArray(val, ['i.net', 'q.com', 'q.net', 'x.com', 
'x.org', 'z.com', 'w.org']) > -1;
+            } else {
+                var firstChar = domain.substring(0, 1),
+                    lastChar = domain.substring(domain.length - 1, 
domain.length);
+
+                if (firstChar === '-' || firstChar === '.' || lastChar === '-' 
|| lastChar === '.') {
+                    return false;
+                }
+                if (domain.split('..').length > 1) {
+                    return false;
+                }
+                if (domain.replace(/[-\da-z\.]/g, '') !== '') {
+                    return false;
+                }
+            }
+
+            if(typeof $input !== 'undefined') {
+                $input.val(val);
+            }
+
+            return true;
+        },
+        errorMessage : '',
+        errorMessageKey: 'badDomain'
+    });
+
+    /*
+    * Validate required
+    */
+    $.formUtils.addValidator({
+        name : 'required',
+        validatorFunction : function(val, $el, config, language, $form) {
+            switch ( $el.attr('type') ) {
+                case 'checkbox':
+                    return $el.is(':checked');
+                case 'radio':
+                    return 
$form.find('input[name="'+$el.attr('name')+'"]').filter(':checked').length > 0;
+                default:
+                    return $.trim(val) !== '';
+            }
+        },
+        errorMessage : '',
+        errorMessageKey: 'requiredFields'
+    });
+
+    /*
+    * Validate length range
+    */
+    $.formUtils.addValidator({
+        name : 'length',
+        validatorFunction : function(val, $el, conf, lang) {
+            var lengthAllowed = $el.valAttr('length'),
+                type = $el.attr('type');
+
+            if(lengthAllowed == undefined) {
+                var elementType = $el.get(0).nodeName;
+                alert('Please add attribute "data-validation-length" to 
'+elementType+' named '+$el.attr('name'));
+                return true;
+            }
+
+            // check if length is above min, below max or within range.
+            var len = type == 'file' && $el.get(0).files !== undefined ? 
$el.get(0).files.length : val.length,
+                lengthCheckResults = $.formUtils.numericRangeCheck(len, 
lengthAllowed),
+                checkResult;
+
+            switch(lengthCheckResults[0])
+            {   // outside of allowed range
+                case "out":
+                    this.errorMessage = lang.lengthBadStart + lengthAllowed + 
lang.lengthBadEnd;
+                    checkResult = false;
+                    break;
+                // too short
+                case "min":
+                    this.errorMessage = lang.lengthTooShortStart + 
lengthCheckResults[1] + lang.lengthBadEnd;
+                    checkResult = false;
+                    break;
+                // too long
+                case "max":
+                    this.errorMessage = lang.lengthTooLongStart + 
lengthCheckResults[1] + lang.lengthBadEnd;
+                    checkResult = false;
+                    break;
+                // ok
+                default:
+                    checkResult = true;
+            }
+            
+            return checkResult;
+        },
+        errorMessage : '',
+        errorMessageKey: ''
+    });
+
+    /*
+    * Validate url
+    */
+    $.formUtils.addValidator({
+        name : 'url',
+        validatorFunction : function(url) {
+            // written by Scott Gonzalez: 
http://projects.scottsplayground.com/iri/
+            // - Victor Jonsson added support for arrays in the url 
?arg[]=sdfsdf
+            // - General improvements made by Stéphane Moureau 
<https://github.com/TraderStf>
+            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;
+            if( urlFilter.test(url) ) {
+                var domain = url.split('://')[1];
+                var domainSlashPos = domain.indexOf('/');
+                if(domainSlashPos > -1)
+                    domain = domain.substr(0, domainSlashPos);
+
+                return 
$.formUtils.validators.validate_domain.validatorFunction(domain); // todo: add 
support for IP-addresses
+            }
+            return false;
+        },
+        errorMessage : '',
+        errorMessageKey: 'badUrl'
+    });
+
+    /*
+    * Validate number (floating or integer)
+    */
+    $.formUtils.addValidator({
+        name : 'number',
+        validatorFunction : function(val, $el, conf) {
+            if(val !== '') {
+                var allowing = $el.valAttr('allowing') || '',
+                    decimalSeparator = $el.valAttr('decimal-separator') || 
conf.decimalSeparator,
+                    allowsRange = false,
+                    begin, end,
+                    steps = $el.valAttr('step') || '',
+                    allowsSteps = false;
+
+                if(allowing.indexOf('number') == -1)
+                    allowing += ',number';
+
+                if(allowing.indexOf('negative') > -1 && val.indexOf('-') === 
0) {
+                    val = val.substr(1);
+                }
+
+                if (allowing.indexOf('range') > -1)
+                {
+                    begin = 
parseFloat(allowing.substring(allowing.indexOf("[")+1, allowing.indexOf(";")));
+                    end = 
parseFloat(allowing.substring(allowing.indexOf(";")+1,allowing.indexOf("]")));
+                    allowsRange = true;
+                }
+                
+                if(steps != "") 
+               {
+                   allowsSteps = true;
+               }
+
+                if( decimalSeparator == ',' ) {
+                    if( val.indexOf('.') > -1 ) {
+                        return false;
+                    }
+                    // Fix for checking range with floats using ,
+                    val = val.replace(',', '.');
+                }
+
+                if(allowing.indexOf('number') > -1 && val.replace(/[0-9]/g, 
'') === '' && (!allowsRange || (val >= begin && val <= end)) && (!allowsSteps 
|| (val%steps == 0)) ) {
+                    return true;
+                }
+                if(allowing.indexOf('float') > -1 && val.match(new 
RegExp('^([0-9]+)\\.([0-9]+)$')) !== null && (!allowsRange || (val >= begin && 
val <= end)) && (!allowsSteps || (val%steps == 0)) ) {
+                    return true;
+                }
+            }
+            return false;
+        },
+        errorMessage : '',
+        errorMessageKey: 'badInt'
+    });
+
+    /*
+     * Validate alpha numeric
+     */
+    $.formUtils.addValidator({
+        name : 'alphanumeric',
+        validatorFunction : function(val, $el, conf, language) {
+            var patternStart = '^([a-zA-Z0-9',
+                patternEnd = ']+)$',
+                additionalChars = $el.attr('data-validation-allowing'),
+                pattern = '';
+
+            if( additionalChars ) {
+                pattern = patternStart + additionalChars + patternEnd;
+                var extra = additionalChars.replace(/\\/g, '');
+                if( extra.indexOf(' ') > -1 ) {
+                    extra = extra.replace(' ', '');
+                    extra += ' and spaces ';
+                }
+                this.errorMessage = language.badAlphaNumeric + 
language.badAlphaNumericExtra + extra;
+            } else {
+                pattern = patternStart + patternEnd;
+                this.errorMessage = language.badAlphaNumeric;
+            }
+
+            return new RegExp(pattern).test(val);
+        },
+        errorMessage : '',
+        errorMessageKey: ''
+    });
+
+    /*
+    * Validate against regexp
+    */
+    $.formUtils.addValidator({
+        name : 'custom',
+        validatorFunction : function(val, $el, conf) {
+            var regexp = new RegExp($el.valAttr('regexp'));
+            return regexp.test(val);
+        },
+        errorMessage : '',
+        errorMessageKey: 'badCustomVal'
+    });
+
+    /*
+    * Validate date
+    */
+    $.formUtils.addValidator({
+        name : 'date',
+        validatorFunction : function(date, $el, conf) {
+            var dateFormat = 'yyyy-mm-dd';
+            if($el.valAttr('format')) {
+                dateFormat = $el.valAttr('format');
+            }
+            else if( conf.dateFormat ) {
+                dateFormat = conf.dateFormat;
+            }
+
+            return $.formUtils.parseDate(date, dateFormat) !== false;
+        },
+        errorMessage : '',
+        errorMessageKey: 'badDate'
+    });
+
+
+    /*
+    * Validate group of checkboxes, validate qty required is checked
+    * written by Steve Wasiura : http://stevewasiura.waztech.com
+    * element attrs
+    *    data-validation="checkbox_group"
+    *    data-validation-qty="1-2"  // min 1 max 2
+    *    data-validation-error-msg="chose min 1, max of 2 checkboxes"
+    */
+    $.formUtils.addValidator({
+        name : 'checkbox_group',
+        validatorFunction : function(val, $el, conf, lang, $form)
+        {   // preset return var
+            var checkResult = true;
+            // get name of element. since it is a checkbox group, all 
checkboxes will have same name
+            var elname = $el.attr('name');
+            // get count of checked checkboxes with this name
+            var checkedCount = 
$("input[type=checkbox][name^='"+elname+"']:checked", $form).length;
+            // get el attr that specs qty required / allowed
+            var qtyAllowed = $el.valAttr('qty');
+            if (qtyAllowed == undefined) {
+                var elementType = $el.get(0).nodeName;
+                alert('Attribute "data-validation-qty" is missing from 
'+elementType+' named '+$el.attr('name'));
+            }
+            // call Utility function to check if count is above min, below 
max, within range etc.
+            var qtyCheckResults = $.formUtils.numericRangeCheck(checkedCount, 
qtyAllowed) ;
+            // results will be array, [0]=result str, [1]=qty int
+            switch(qtyCheckResults[0] ) {   
+                // outside allowed range
+                case "out":
+                    this.errorMessage = lang.groupCheckedRangeStart + 
qtyAllowed + lang.groupCheckedEnd;
+                    checkResult = false;
+                    break;
+                // below min qty
+                case "min":
+                    this.errorMessage = lang.groupCheckedTooFewStart + 
qtyCheckResults[1] + lang.groupCheckedEnd;
+                    checkResult = false;
+                    break;
+                // above max qty
+                case "max":
+                    this.errorMessage = lang.groupCheckedTooManyStart + 
qtyCheckResults[1] + lang.groupCheckedEnd;
+                    checkResult = false;
+                    break;
+                // ok
+                default:
+                    checkResult = true;
+            }
+            
+        return checkResult;
+        
+        }
+     //   errorMessage : '', // set above in switch statement
+     //   errorMessageKey: '' // not used
+    });
+
+})(jQuery);

Added: 
branches/dev-syncromind/phpgwapi/js/form-validator/jquery.form-validator.min.js
===================================================================
--- 
branches/dev-syncromind/phpgwapi/js/form-validator/jquery.form-validator.min.js 
                            (rev 0)
+++ 
branches/dev-syncromind/phpgwapi/js/form-validator/jquery.form-validator.min.js 
    2014-10-09 19:00:00 UTC (rev 12129)
@@ -0,0 +1,11 @@
+/**
+* jQuery Form Validator
+* ------------------------------------------
+* Created by Victor Jonsson <http://www.victorjonsson.se>
+*
+* @website http://formvalidator.net/
+* @license Dual licensed under the MIT or GPL Version 2 licenses
+* @version 2.2.beta.13
+*/

@@ Diff output truncated at 153600 characters. @@



reply via email to

[Prev in Thread] Current Thread [Next in Thread]