formtastic_validation 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Wong Liang Zan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,100 @@
1
+ h1. formtastic_validation
2
+
3
+ h2. Introduction
4
+
5
+ Formtastic validation adds client side validation to your forms.
6
+
7
+ h2. Dependencies
8
+
9
+ * "Formtastic":http://github.com/justinfrench/formtastic
10
+ * "Validation Reflection":http://github.com/redinger/validation_reflection
11
+ * "Live validation":http://github.com/alechill/livevalidation
12
+
13
+ h2. Installation
14
+
15
+ <pre>
16
+ $ gem install formtastic_validation
17
+ </pre>
18
+
19
+ Then run the generators to add the files on
20
+
21
+ From your rails root directory:
22
+
23
+ <pre>
24
+ $ script/generate formtastic_validation
25
+ </pre>
26
+
27
+ This adds a configuration file to config/initializers and a couple of javascript files to public/javascripts
28
+
29
+ After that you need to add this to your application layout
30
+
31
+ <pre>
32
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
33
+ <html lang='en-US' xml:lang='en-US' xmlns='http://www.w3.org/1999/xhtml'>
34
+ <head>
35
+ <meta content='text/html; charset=utf-8' http-equiv='Content-type'/>
36
+ <title>Formtastic Validation</title>
37
+ <%= javascript_include_tag 'livevalidation-1.3.min' %>
38
+ <%= javascript_include_tag 'formtastic_validation-0.1.0.min' %>
39
+ <script type="text/javascript">
40
+ function initialize() {
41
+ var formtasticValidation = new FormtasticValidation;
42
+ formtasticValidation.initialize();
43
+ }
44
+ </script>
45
+ </head>
46
+ <body onload="initialize()">
47
+ </body>
48
+ </html>
49
+ </pre>
50
+
51
+ Two things to add:
52
+
53
+ * include the livevalidation and formtastic_validation javascript files
54
+ * run a function that instantiates a FormatasticValidation object and let it call initialize()
55
+ h2. Usage
56
+
57
+ There is nothing to change in your existing code. You should see "Thankyou!" appearing next to fields which passed the validations. Forms which do not fufil the validations will fail to submit.
58
+
59
+ Validation options work.
60
+
61
+ h2. CSS Styling
62
+
63
+ You can style the validation messages with the css selectors
64
+
65
+ <pre>
66
+ .LV_validation_message { ... } /* the validation message */
67
+ .LV_valid { ... } /* for valid messages */
68
+ .LV_invalid { ... } /* for invalid messages */
69
+ </pre>
70
+
71
+ h2. Caveats
72
+
73
+ Only the following validations work:
74
+
75
+ # validates_presence_of
76
+ # validates_acceptance_of
77
+ # validates_confirmation_of
78
+ # validates_exclusion_of
79
+ # validates_inclusion_of
80
+ # validates_numericality_of
81
+ # validates_format_of
82
+ # validates_length_of
83
+
84
+ validates_size_of does not work. Do use my "fork":http://rubygems.org/gems/liangzan-validation_reflection of the validation_reflection gem for it to work.
85
+
86
+ The rest do not work as they depend on server queries.
87
+
88
+ h2. Note on Patches/Pull Requests
89
+
90
+ * Fork the project.
91
+ * Make your feature addition or bug fix.
92
+ * Add tests for it. This is important so I don't break it in a
93
+ future version unintentionally.
94
+ * Commit, do not mess with rakefile, version, or history.
95
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
96
+ * Send me a pull request. Bonus points for topic branches.
97
+
98
+ h2. Copyright
99
+
100
+ Copyright (c) 2010 Wong Liang Zan. See LICENSE for details.
@@ -0,0 +1,52 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "formtastic_validation"
8
+ gem.summary = %Q{Client side validation with formtastic}
9
+ gem.description = %Q{A formtastic extension that does client side validation}
10
+ gem.email = "zan@liangzan.net"
11
+ gem.homepage = "http://github.com/liangzan/formtastic_validation"
12
+ gem.authors = ["Wong Liang Zan"]
13
+ gem.add_dependency "validation_reflection", "= 0.3.8"
14
+ gem.add_dependency "formtastic", "= 1.0.0"
15
+ gem.add_development_dependency "rspec", "= 1.3.0"
16
+ gem.add_development_dependency "rspec-rails", "= 1.3.2"
17
+ gem.add_development_dependency "rspec_tag_matchers", "= 1.0.0"
18
+ gem.add_development_dependency "nokogiri", "= 1.4.2"
19
+ gem.add_development_dependency "activesupport", "= 2.3.8"
20
+ gem.add_development_dependency "actionpack", "= 2.3.8"
21
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
22
+ end
23
+ Jeweler::GemcutterTasks.new
24
+ rescue LoadError
25
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
26
+ end
27
+
28
+ require 'spec/rake/spectask'
29
+ Spec::Rake::SpecTask.new(:spec) do |spec|
30
+ spec.libs << 'lib' << 'spec'
31
+ spec.spec_files = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
35
+ spec.libs << 'lib' << 'spec'
36
+ spec.pattern = 'spec/**/*_spec.rb'
37
+ spec.rcov = true
38
+ end
39
+
40
+ task :spec => :check_dependencies
41
+
42
+ task :default => :spec
43
+
44
+ require 'rake/rdoctask'
45
+ Rake::RDocTask.new do |rdoc|
46
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
47
+
48
+ rdoc.rdoc_dir = 'rdoc'
49
+ rdoc.title = "formtastic_validation #{version}"
50
+ rdoc.rdoc_files.include('README*')
51
+ rdoc.rdoc_files.include('lib/**/*.rb')
52
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,84 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{formtastic_validation}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Wong Liang Zan"]
12
+ s.date = %q{2010-08-31}
13
+ s.description = %q{A formtastic extension that does client side validation}
14
+ s.email = %q{zan@liangzan.net}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.textile"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.textile",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "formtastic_validation.gemspec",
27
+ "javascripts/README.textile",
28
+ "javascripts/build/formtastic_validation-0.1.0.min.js",
29
+ "javascripts/lib/livevalidation-1.3.min.js",
30
+ "javascripts/src/formtastic_validation.js",
31
+ "lib/formtastic_validation.rb",
32
+ "rails_generators/formtastic_validation/formtastic_validation_generator.rb",
33
+ "rails_generators/formtastic_validation/templates/config/initializers/formtastic_validation.rb",
34
+ "rails_generators/formtastic_validation/templates/public/javascripts/formtastic_validation-0.1.0.min.js",
35
+ "rails_generators/formtastic_validation/templates/public/javascripts/livevalidation-1.3.min.js",
36
+ "spec/formtastic_validation_spec.rb",
37
+ "spec/spec.opts",
38
+ "spec/spec_helper.rb"
39
+ ]
40
+ s.homepage = %q{http://github.com/liangzan/formtastic_validation}
41
+ s.rdoc_options = ["--charset=UTF-8"]
42
+ s.require_paths = ["lib"]
43
+ s.rubygems_version = %q{1.3.7}
44
+ s.summary = %q{Client side validation with formtastic}
45
+ s.test_files = [
46
+ "spec/formtastic_validation_spec.rb",
47
+ "spec/spec_helper.rb"
48
+ ]
49
+
50
+ if s.respond_to? :specification_version then
51
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
52
+ s.specification_version = 3
53
+
54
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
55
+ s.add_runtime_dependency(%q<validation_reflection>, ["= 0.3.8"])
56
+ s.add_runtime_dependency(%q<formtastic>, ["= 1.0.0"])
57
+ s.add_development_dependency(%q<rspec>, ["= 1.3.0"])
58
+ s.add_development_dependency(%q<rspec-rails>, ["= 1.3.2"])
59
+ s.add_development_dependency(%q<rspec_tag_matchers>, ["= 1.0.0"])
60
+ s.add_development_dependency(%q<nokogiri>, ["= 1.4.2"])
61
+ s.add_development_dependency(%q<activesupport>, ["= 2.3.8"])
62
+ s.add_development_dependency(%q<actionpack>, ["= 2.3.8"])
63
+ else
64
+ s.add_dependency(%q<validation_reflection>, ["= 0.3.8"])
65
+ s.add_dependency(%q<formtastic>, ["= 1.0.0"])
66
+ s.add_dependency(%q<rspec>, ["= 1.3.0"])
67
+ s.add_dependency(%q<rspec-rails>, ["= 1.3.2"])
68
+ s.add_dependency(%q<rspec_tag_matchers>, ["= 1.0.0"])
69
+ s.add_dependency(%q<nokogiri>, ["= 1.4.2"])
70
+ s.add_dependency(%q<activesupport>, ["= 2.3.8"])
71
+ s.add_dependency(%q<actionpack>, ["= 2.3.8"])
72
+ end
73
+ else
74
+ s.add_dependency(%q<validation_reflection>, ["= 0.3.8"])
75
+ s.add_dependency(%q<formtastic>, ["= 1.0.0"])
76
+ s.add_dependency(%q<rspec>, ["= 1.3.0"])
77
+ s.add_dependency(%q<rspec-rails>, ["= 1.3.2"])
78
+ s.add_dependency(%q<rspec_tag_matchers>, ["= 1.0.0"])
79
+ s.add_dependency(%q<nokogiri>, ["= 1.4.2"])
80
+ s.add_dependency(%q<activesupport>, ["= 2.3.8"])
81
+ s.add_dependency(%q<actionpack>, ["= 2.3.8"])
82
+ end
83
+ end
84
+
@@ -0,0 +1,20 @@
1
+ h1. Formtastic Validation JS Lib
2
+
3
+ h2. Dependencies
4
+
5
+ Minifying is done using "yui-compressor":http://developer.yahoo.com/yui/compressor/
6
+
7
+ Do install yui-compressor before attempting to build the files.
8
+
9
+ h2. Testing
10
+
11
+ Testing is done using cuucmber on the formtastic validation app
12
+
13
+ h2. Building
14
+
15
+ Run this on the commandline
16
+
17
+ <pre>
18
+ $ yui-compressor -o build/formtastic_validation.min.js src/formtastic_validation.js
19
+ </pre>
20
+
@@ -0,0 +1 @@
1
+ var FormtasticValidation=function(){this.VERSION="0.1.0";this.selectInputElements=function(){var d=document.getElementsByTagName("input");var e=new Array;for(var b=0;b<d.length;b++){var a=d[b];var c=a.getAttribute("type");if(this.isValidInputType(c)){e.push(d[b])}}return e};this.isValidInputType=function(a){switch(a){case"text":case"password":case"radio":case"checkbox":return true;default:return false}};this.getValidationAttributes=function(e){var b={};var f=e.attributes;var d,a;for(var c=0;c<f.length;c++){d=this.extractValidationKey(f[c].name);a=f[c].value;if(d!=null){b[d]=a}}return b};this.extractValidationKey=function(b){var c=/validation_(\w+)/;var a=b.match(c);return a!=null?a[1]:null};this.extractFormatRegex=function(b){var c=/^\/(.*)\/$/;var a=b.match(c);return a!=null?a[1]:null};this.confirmationID=function(b){var a=b.getAttribute("id");return a+"_confirmation"};this.bindInputElements=function(c,a,b){var e=new LiveValidation(c);var f,d;switch(a){case"validates_acceptance_of":e.add(Validate.Acceptance,{failureMessage:b.message});break;case"validates_confirmation_of":d=new LiveValidation(this.confirmationID(c));d.add(Validate.Confirmation,{match:c,failureMessage:b.message});break;case"validates_exclusion_of":e.add(Validate.Exclusion,{within:JSON.parse(b["in"]),allowNull:b.allow_nil,failureMessage:b.message});break;case"validates_format_of":f=this.extractFormatRegex(b["with"]);e.add(Validate.Format,{pattern:new RegExp(f),failureMessage:b.message});break;case"validates_inclusion_of":e.add(Validate.Inclusion,{within:JSON.parse(b["in"]),allowNull:b.allow_nil,failureMessage:b.message});break;case"validates_size_of":case"validates_length_of":e.add(Validate.Length,{is:b.is,minimum:b.minimum,maximum:b.maximum,wrongLengthMessage:b.wrong_length,tooShortMessage:b.too_short,tooLongMessage:b.too_long});break;case"validates_numericality_of":e.add(Validate.Numericality,{is:b.equal_to,minimum:b.greater_than_or_equal_to,maximum:b.less_than_or_equal_to,onlyinteger:b.only_integer,failureMessage:b.message});break;case"validates_presence_of":e.add(Validate.Presence,{failureMessage:b.message});break}}};FormtasticValidation.prototype.initialize=function(){var f=this.selectInputElements();var d,e,a;for(var c=0;c<f.length;c++){d=f[c].getAttribute("validation");if(d!=null){e=d.split(" ");for(var b=0;b<e.length;b++){a=this.getValidationAttributes(f[c]);this.bindInputElements(f[c],e[b],a)}}}};
@@ -0,0 +1,4 @@
1
+ // LiveValidation 1.3 (standalone version)
2
+ // Copyright (c) 2007-2008 Alec Hill (www.livevalidation.com)
3
+ // LiveValidation is licensed under the terms of the MIT License
4
+ var LiveValidation=function(B,A){this.initialize(B,A);};LiveValidation.VERSION="1.3 standalone";LiveValidation.TEXTAREA=1;LiveValidation.TEXT=2;LiveValidation.PASSWORD=3;LiveValidation.CHECKBOX=4;LiveValidation.SELECT=5;LiveValidation.FILE=6;LiveValidation.massValidate=function(C){var D=true;for(var B=0,A=C.length;B<A;++B){var E=C[B].validate();if(D){D=E;}}return D;};LiveValidation.prototype={validClass:"LV_valid",invalidClass:"LV_invalid",messageClass:"LV_validation_message",validFieldClass:"LV_valid_field",invalidFieldClass:"LV_invalid_field",initialize:function(D,C){var A=this;if(!D){throw new Error("LiveValidation::initialize - No element reference or element id has been provided!");}this.element=D.nodeName?D:document.getElementById(D);if(!this.element){throw new Error("LiveValidation::initialize - No element with reference or id of '"+D+"' exists!");}this.validations=[];this.elementType=this.getElementType();this.form=this.element.form;var B=C||{};this.validMessage=B.validMessage||"Thankyou!";var E=B.insertAfterWhatNode||this.element;this.insertAfterWhatNode=E.nodeType?E:document.getElementById(E);this.onValid=B.onValid||function(){this.insertMessage(this.createMessageSpan());this.addFieldClass();};this.onInvalid=B.onInvalid||function(){this.insertMessage(this.createMessageSpan());this.addFieldClass();};this.onlyOnBlur=B.onlyOnBlur||false;this.wait=B.wait||0;this.onlyOnSubmit=B.onlyOnSubmit||false;if(this.form){this.formObj=LiveValidationForm.getInstance(this.form);this.formObj.addField(this);}this.oldOnFocus=this.element.onfocus||function(){};this.oldOnBlur=this.element.onblur||function(){};this.oldOnClick=this.element.onclick||function(){};this.oldOnChange=this.element.onchange||function(){};this.oldOnKeyup=this.element.onkeyup||function(){};this.element.onfocus=function(F){A.doOnFocus(F);return A.oldOnFocus.call(this,F);};if(!this.onlyOnSubmit){switch(this.elementType){case LiveValidation.CHECKBOX:this.element.onclick=function(F){A.validate();return A.oldOnClick.call(this,F);};case LiveValidation.SELECT:case LiveValidation.FILE:this.element.onchange=function(F){A.validate();return A.oldOnChange.call(this,F);};break;default:if(!this.onlyOnBlur){this.element.onkeyup=function(F){A.deferValidation();return A.oldOnKeyup.call(this,F);};}this.element.onblur=function(F){A.doOnBlur(F);return A.oldOnBlur.call(this,F);};}}},destroy:function(){if(this.formObj){this.formObj.removeField(this);this.formObj.destroy();}this.element.onfocus=this.oldOnFocus;if(!this.onlyOnSubmit){switch(this.elementType){case LiveValidation.CHECKBOX:this.element.onclick=this.oldOnClick;case LiveValidation.SELECT:case LiveValidation.FILE:this.element.onchange=this.oldOnChange;break;default:if(!this.onlyOnBlur){this.element.onkeyup=this.oldOnKeyup;}this.element.onblur=this.oldOnBlur;}}this.validations=[];this.removeMessageAndFieldClass();},add:function(A,B){this.validations.push({type:A,params:B||{}});return this;},remove:function(B,D){var E=false;for(var C=0,A=this.validations.length;C<A;C++){if(this.validations[C].type==B){if(this.validations[C].params==D){E=true;break;}}}if(E){this.validations.splice(C,1);}return this;},deferValidation:function(B){if(this.wait>=300){this.removeMessageAndFieldClass();}var A=this;if(this.timeout){clearTimeout(A.timeout);}this.timeout=setTimeout(function(){A.validate();},A.wait);},doOnBlur:function(A){this.focused=false;this.validate(A);},doOnFocus:function(A){this.focused=true;this.removeMessageAndFieldClass();},getElementType:function(){switch(true){case (this.element.nodeName.toUpperCase()=="TEXTAREA"):return LiveValidation.TEXTAREA;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="TEXT"):return LiveValidation.TEXT;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="PASSWORD"):return LiveValidation.PASSWORD;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="CHECKBOX"):return LiveValidation.CHECKBOX;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="FILE"):return LiveValidation.FILE;case (this.element.nodeName.toUpperCase()=="SELECT"):return LiveValidation.SELECT;case (this.element.nodeName.toUpperCase()=="INPUT"):throw new Error("LiveValidation::getElementType - Cannot use LiveValidation on an "+this.element.type+" input!");default:throw new Error("LiveValidation::getElementType - Element must be an input, select, or textarea!");}},doValidations:function(){this.validationFailed=false;for(var C=0,A=this.validations.length;C<A;++C){var B=this.validations[C];switch(B.type){case Validate.Presence:case Validate.Confirmation:case Validate.Acceptance:this.displayMessageWhenEmpty=true;this.validationFailed=!this.validateElement(B.type,B.params);break;default:this.validationFailed=!this.validateElement(B.type,B.params);break;}if(this.validationFailed){return false;}}this.message=this.validMessage;return true;},validateElement:function(A,C){var D=(this.elementType==LiveValidation.SELECT)?this.element.options[this.element.selectedIndex].value:this.element.value;if(A==Validate.Acceptance){if(this.elementType!=LiveValidation.CHECKBOX){throw new Error("LiveValidation::validateElement - Element to validate acceptance must be a checkbox!");}D=this.element.checked;}var E=true;try{A(D,C);}catch(B){if(B instanceof Validate.Error){if(D!==""||(D===""&&this.displayMessageWhenEmpty)){this.validationFailed=true;this.message=B.message;E=false;}}else{throw B;}}finally{return E;}},validate:function(){if(!this.element.disabled){var A=this.doValidations();if(A){this.onValid();return true;}else{this.onInvalid();return false;}}else{return true;}},enable:function(){this.element.disabled=false;return this;},disable:function(){this.element.disabled=true;this.removeMessageAndFieldClass();return this;},createMessageSpan:function(){var A=document.createElement("span");var B=document.createTextNode(this.message);A.appendChild(B);return A;},insertMessage:function(B){this.removeMessage();if((this.displayMessageWhenEmpty&&(this.elementType==LiveValidation.CHECKBOX||this.element.value==""))||this.element.value!=""){var A=this.validationFailed?this.invalidClass:this.validClass;B.className+=" "+this.messageClass+" "+A;if(this.insertAfterWhatNode.nextSibling){this.insertAfterWhatNode.parentNode.insertBefore(B,this.insertAfterWhatNode.nextSibling);}else{this.insertAfterWhatNode.parentNode.appendChild(B);}}},addFieldClass:function(){this.removeFieldClass();if(!this.validationFailed){if(this.displayMessageWhenEmpty||this.element.value!=""){if(this.element.className.indexOf(this.validFieldClass)==-1){this.element.className+=" "+this.validFieldClass;}}}else{if(this.element.className.indexOf(this.invalidFieldClass)==-1){this.element.className+=" "+this.invalidFieldClass;}}},removeMessage:function(){var A;var B=this.insertAfterWhatNode;while(B.nextSibling){if(B.nextSibling.nodeType===1){A=B.nextSibling;break;}B=B.nextSibling;}if(A&&A.className.indexOf(this.messageClass)!=-1){this.insertAfterWhatNode.parentNode.removeChild(A);}},removeFieldClass:function(){if(this.element.className.indexOf(this.invalidFieldClass)!=-1){this.element.className=this.element.className.split(this.invalidFieldClass).join("");}if(this.element.className.indexOf(this.validFieldClass)!=-1){this.element.className=this.element.className.split(this.validFieldClass).join(" ");}},removeMessageAndFieldClass:function(){this.removeMessage();this.removeFieldClass();}};var LiveValidationForm=function(A){this.initialize(A);};LiveValidationForm.instances={};LiveValidationForm.getInstance=function(A){var B=Math.random()*Math.random();if(!A.id){A.id="formId_"+B.toString().replace(/\./,"")+new Date().valueOf();}if(!LiveValidationForm.instances[A.id]){LiveValidationForm.instances[A.id]=new LiveValidationForm(A);}return LiveValidationForm.instances[A.id];};LiveValidationForm.prototype={initialize:function(B){this.name=B.id;this.element=B;this.fields=[];this.oldOnSubmit=this.element.onsubmit||function(){};var A=this;this.element.onsubmit=function(C){return(LiveValidation.massValidate(A.fields))?A.oldOnSubmit.call(this,C||window.event)!==false:false;};},addField:function(A){this.fields.push(A);},removeField:function(C){var D=[];for(var B=0,A=this.fields.length;B<A;B++){if(this.fields[B]!==C){D.push(this.fields[B]);}}this.fields=D;},destroy:function(A){if(this.fields.length!=0&&!A){return false;}this.element.onsubmit=this.oldOnSubmit;LiveValidationForm.instances[this.name]=null;return true;}};var Validate={Presence:function(B,C){var C=C||{};var A=C.failureMessage||"Can't be empty!";if(B===""||B===null||B===undefined){Validate.fail(A);}return true;},Numericality:function(J,E){var A=J;var J=Number(J);var E=E||{};var F=((E.minimum)||(E.minimum==0))?E.minimum:null;var C=((E.maximum)||(E.maximum==0))?E.maximum:null;var D=((E.is)||(E.is==0))?E.is:null;var G=E.notANumberMessage||"Must be a number!";var H=E.notAnIntegerMessage||"Must be an integer!";var I=E.wrongNumberMessage||"Must be "+D+"!";var B=E.tooLowMessage||"Must not be less than "+F+"!";var K=E.tooHighMessage||"Must not be more than "+C+"!";if(!isFinite(J)){Validate.fail(G);}if(E.onlyInteger&&(/\.0+$|\.$/.test(String(A))||J!=parseInt(J))){Validate.fail(H);}switch(true){case (D!==null):if(J!=Number(D)){Validate.fail(I);}break;case (F!==null&&C!==null):Validate.Numericality(J,{tooLowMessage:B,minimum:F});Validate.Numericality(J,{tooHighMessage:K,maximum:C});break;case (F!==null):if(J<Number(F)){Validate.fail(B);}break;case (C!==null):if(J>Number(C)){Validate.fail(K);}break;}return true;},Format:function(C,E){var C=String(C);var E=E||{};var A=E.failureMessage||"Not valid!";var B=E.pattern||/./;var D=E.negate||false;if(!D&&!B.test(C)){Validate.fail(A);}if(D&&B.test(C)){Validate.fail(A);}return true;},Email:function(B,C){var C=C||{};var A=C.failureMessage||"Must be a valid email address!";Validate.Format(B,{failureMessage:A,pattern:/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i});return true;},Length:function(F,G){var F=String(F);var G=G||{};var E=((G.minimum)||(G.minimum==0))?G.minimum:null;var H=((G.maximum)||(G.maximum==0))?G.maximum:null;var C=((G.is)||(G.is==0))?G.is:null;var A=G.wrongLengthMessage||"Must be "+C+" characters long!";var B=G.tooShortMessage||"Must not be less than "+E+" characters long!";var D=G.tooLongMessage||"Must not be more than "+H+" characters long!";switch(true){case (C!==null):if(F.length!=Number(C)){Validate.fail(A);}break;case (E!==null&&H!==null):Validate.Length(F,{tooShortMessage:B,minimum:E});Validate.Length(F,{tooLongMessage:D,maximum:H});break;case (E!==null):if(F.length<Number(E)){Validate.fail(B);}break;case (H!==null):if(F.length>Number(H)){Validate.fail(D);}break;default:throw new Error("Validate::Length - Length(s) to validate against must be provided!");}return true;},Inclusion:function(H,F){var F=F||{};var K=F.failureMessage||"Must be included in the list!";var G=(F.caseSensitive===false)?false:true;if(F.allowNull&&H==null){return true;}if(!F.allowNull&&H==null){Validate.fail(K);}var D=F.within||[];if(!G){var A=[];for(var C=0,B=D.length;C<B;++C){var I=D[C];if(typeof I=="string"){I=I.toLowerCase();}A.push(I);}D=A;if(typeof H=="string"){H=H.toLowerCase();}}var J=false;for(var E=0,B=D.length;E<B;++E){if(D[E]==H){J=true;}if(F.partialMatch){if(H.indexOf(D[E])!=-1){J=true;}}}if((!F.negate&&!J)||(F.negate&&J)){Validate.fail(K);}return true;},Exclusion:function(A,B){var B=B||{};B.failureMessage=B.failureMessage||"Must not be included in the list!";B.negate=true;Validate.Inclusion(A,B);return true;},Confirmation:function(C,D){if(!D.match){throw new Error("Validate::Confirmation - Error validating confirmation: Id of element to match must be provided!");}var D=D||{};var B=D.failureMessage||"Does not match!";var A=D.match.nodeName?D.match:document.getElementById(D.match);if(!A){throw new Error("Validate::Confirmation - There is no reference with name of, or element with id of '"+D.match+"'!");}if(C!=A.value){Validate.fail(B);}return true;},Acceptance:function(B,C){var C=C||{};var A=C.failureMessage||"Must be accepted!";if(!B){Validate.fail(A);}return true;},Custom:function(D,E){var E=E||{};var B=E.against||function(){return true;};var A=E.args||{};var C=E.failureMessage||"Not valid!";if(!B(D,A)){Validate.fail(C);}return true;},now:function(A,D,C){if(!A){throw new Error("Validate::now - Validation function must be provided!");}var E=true;try{A(D,C||{});}catch(B){if(B instanceof Validate.Error){E=false;}else{throw B;}}finally{return E;}},fail:function(A){throw new Validate.Error(A);},Error:function(A){this.message=A;this.name="ValidationError";}};
@@ -0,0 +1,114 @@
1
+ /* Formtastic Validation JS Lib
2
+ *
3
+ * This library selects form input elements, read the attributes
4
+ * and bind the input field to the corresponding live validation function
5
+ */
6
+ var FormtasticValidation = function() {
7
+ this.VERSION = "0.1.0";
8
+
9
+ this.selectInputElements = function() {
10
+ var inputElements = document.getElementsByTagName('input');
11
+ var textInputs = new Array;
12
+ for (var i = 0; i < inputElements.length; i++) {
13
+ var element = inputElements[i];
14
+ var inputType = element.getAttribute("type");
15
+ if (this.isValidInputType(inputType)) {
16
+ textInputs.push(inputElements[i]);
17
+ }
18
+ }
19
+ return textInputs;
20
+ };
21
+
22
+ this.isValidInputType = function(type) {
23
+ switch(type) {
24
+ case "text":
25
+ case "password":
26
+ case "radio":
27
+ case "checkbox":
28
+ return true;
29
+ default:
30
+ return false;
31
+ }
32
+ };
33
+
34
+ this.getValidationAttributes = function(element) {
35
+ var validationOptions = {};
36
+ var elementAttributes = element.attributes;
37
+ var validationKey, validationValue;
38
+ for(var i = 0; i < elementAttributes.length; i++) {
39
+ validationKey = this.extractValidationKey(elementAttributes[i].name);
40
+ validationValue = elementAttributes[i].value;
41
+ if (validationKey != null) {
42
+ validationOptions[validationKey] = validationValue;
43
+ }
44
+ }
45
+ return validationOptions;
46
+ };
47
+
48
+ this.extractValidationKey = function(key) {
49
+ var namespaceRegex = /validation_(\w+)/;
50
+ var result = key.match(namespaceRegex);
51
+ return result != null ? result[1] : null;
52
+ };
53
+
54
+ this.extractFormatRegex = function(format) {
55
+ var formatRegex = /^\/(.*)\/$/;
56
+ var result = format.match(formatRegex);
57
+ return result != null ? result[1] : null;
58
+ };
59
+
60
+ this.confirmationID = function(element) {
61
+ var elementID = element.getAttribute("id");
62
+ return elementID + "_confirmation";
63
+ };
64
+
65
+ this.bindInputElements = function(element, validation, options) {
66
+ var elementValidation = new LiveValidation(element);
67
+ var formatRegex, confirmationElement;
68
+
69
+ switch(validation) {
70
+ case "validates_acceptance_of":
71
+ elementValidation.add(Validate.Acceptance, {failureMessage: options["message"]});
72
+ break;
73
+ case "validates_confirmation_of":
74
+ confirmationElement = new LiveValidation(this.confirmationID(element));
75
+ confirmationElement.add(Validate.Confirmation, {match: element, failureMessage: options["message"]});
76
+ break;
77
+ case "validates_exclusion_of":
78
+ elementValidation.add(Validate.Exclusion, {within: JSON.parse(options["in"]), allowNull: options["allow_nil"], failureMessage: options["message"]});
79
+ break;
80
+ case "validates_format_of":
81
+ formatRegex = this.extractFormatRegex(options["with"]);
82
+ elementValidation.add(Validate.Format, {pattern: new RegExp(formatRegex), failureMessage: options["message"]});
83
+ break;
84
+ case "validates_inclusion_of":
85
+ elementValidation.add(Validate.Inclusion, {within: JSON.parse(options["in"]), allowNull: options["allow_nil"], failureMessage: options["message"]});
86
+ break;
87
+ case "validates_size_of":
88
+ case "validates_length_of":
89
+ elementValidation.add(Validate.Length, {is: options["is"], minimum: options["minimum"], maximum: options["maximum"], wrongLengthMessage: options["wrong_length"], tooShortMessage: options["too_short"], tooLongMessage: options["too_long"]});
90
+ break;
91
+ case "validates_numericality_of":
92
+ elementValidation.add(Validate.Numericality, {is: options["equal_to"], minimum: options["greater_than_or_equal_to"], maximum: options["less_than_or_equal_to"], onlyinteger: options["only_integer"], failureMessage: options["message"]});
93
+ break;
94
+ case "validates_presence_of":
95
+ elementValidation.add(Validate.Presence, {failureMessage: options["message"]});
96
+ break;
97
+ }
98
+ };
99
+ };
100
+
101
+ FormtasticValidation.prototype.initialize = function() {
102
+ var textInputs = this.selectInputElements();
103
+ var validations, validationTypes, validationOptions;
104
+ for (var i = 0; i < textInputs.length; i++) {
105
+ validations = textInputs[i].getAttribute("validation");
106
+ if (validations != null) {
107
+ validationTypes = validations.split(" ");
108
+ for (var j = 0; j < validationTypes.length; j++) {
109
+ validationOptions = this.getValidationAttributes(textInputs[i]);
110
+ this.bindInputElements(textInputs[i], validationTypes[j], validationOptions);
111
+ }
112
+ }
113
+ }
114
+ };
@@ -0,0 +1,81 @@
1
+ module FormtasticValidation
2
+
3
+ NAMESPACE = 'validation'
4
+
5
+ def self.included(base)
6
+ base.class_eval {
7
+ alias_method_chain :input, :validation
8
+ }
9
+ end
10
+
11
+ def input_with_validation(method, options = {})
12
+ if has_validations?(method)
13
+ options[:input_html] = validation_tags(method, options[:input_html])
14
+ end
15
+ input_without_validation(method, options)
16
+ end
17
+
18
+ def has_validations?(attribute)
19
+ if @object && @object.class.respond_to?(:reflect_on_validations_for)
20
+ @object.class.reflect_on_validations_for(attribute_sym(attribute)).present?
21
+ else
22
+ false
23
+ end
24
+ end
25
+
26
+ def attribute_sym(attribute)
27
+ attribute.to_s.sub(/_id$/, '').to_sym
28
+ end
29
+
30
+ def validation_tags(attribute, input_html)
31
+ tags = []
32
+ @object.class.reflect_on_validations_for(attribute).each do |validation|
33
+ tags << { :validation => validation.macro }.merge(validation.options)
34
+ end
35
+ stack_tags(tags, input_html)
36
+ end
37
+
38
+ def stack_tags(tags, input_html)
39
+ vtags = validates_tags(tags.dup)
40
+ otags = options_tags(tags.dup)
41
+ if input_html
42
+ input_html[:validation] = vtags
43
+ input_html.merge(otags)
44
+ else
45
+ { :validation => vtags }.merge(otags)
46
+ end
47
+ end
48
+
49
+ def validates_tags(tags)
50
+ validates_tags = tags.map { |tag| tag[:validation] }
51
+ validates_tags.join(' ')
52
+ end
53
+
54
+ def options_tags(tags)
55
+ otags_array = tags.map do |tag|
56
+ tag.delete_if { |key, value| key.eql?(:validation) }
57
+ end
58
+ opts_hash = otags_array.inject { |memo, hash| memo.merge(hash) }
59
+ namespaced_hash = add_namespace(opts_hash)
60
+ serialise_options(namespaced_hash)
61
+ end
62
+
63
+ def add_namespace(opts_hash)
64
+ namespaced_hash = Hash.new
65
+ opts_hash.each do |key, value|
66
+ namespaced_key = (NAMESPACE + '_' + key.to_s).to_sym
67
+ namespaced_hash[namespaced_key] = value
68
+ end
69
+ namespaced_hash
70
+ end
71
+
72
+ def serialise_options(opts_hash)
73
+ serialised_hash = Hash.new
74
+ opts_hash.each do |key, value|
75
+ serialised_value = value.class.eql?(String) ? value : value.to_json
76
+ serialised_hash[key] = serialised_value
77
+ end
78
+ serialised_hash
79
+ end
80
+
81
+ end
@@ -0,0 +1,16 @@
1
+ class FormtasticValidationGenerator < Rails::Generator::Base
2
+
3
+ def manifest
4
+ record do |m|
5
+ m.file 'public/javascripts/formtastic_validation-0.1.0.min.js', 'public/javascripts/formtastic_validation-0.1.0.min.js'
6
+ m.file 'public/javascripts/livevalidation-1.3.min.js', 'public/javascripts/livevalidation-1.3.min.js'
7
+ m.file 'config/initializers/formtastic_validation.rb', 'config/initializers/formtastic_validation.rb'
8
+ end
9
+ end
10
+
11
+ protected
12
+
13
+ def banner
14
+ %\Usage: #{$0} formtastic_validation\
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ if Object.const_defined? "Formtastic"
2
+ Formtastic::SemanticFormBuilder.send(:include, FormtasticValidation)
3
+ end
@@ -0,0 +1 @@
1
+ var FormtasticValidation=function(){this.VERSION="0.1.0";this.selectInputElements=function(){var d=document.getElementsByTagName("input");var e=new Array;for(var b=0;b<d.length;b++){var a=d[b];var c=a.getAttribute("type");if(this.isValidInputType(c)){e.push(d[b])}}return e};this.isValidInputType=function(a){switch(a){case"text":case"password":case"radio":case"checkbox":return true;default:return false}};this.getValidationAttributes=function(e){var b={};var f=e.attributes;var d,a;for(var c=0;c<f.length;c++){d=this.extractValidationKey(f[c].name);a=f[c].value;if(d!=null){b[d]=a}}return b};this.extractValidationKey=function(b){var c=/validation_(\w+)/;var a=b.match(c);return a!=null?a[1]:null};this.extractFormatRegex=function(b){var c=/^\/(.*)\/$/;var a=b.match(c);return a!=null?a[1]:null};this.confirmationID=function(b){var a=b.getAttribute("id");return a+"_confirmation"};this.bindInputElements=function(c,a,b){var e=new LiveValidation(c);var f,d;switch(a){case"validates_acceptance_of":e.add(Validate.Acceptance,{failureMessage:b.message});break;case"validates_confirmation_of":d=new LiveValidation(this.confirmationID(c));d.add(Validate.Confirmation,{match:c,failureMessage:b.message});break;case"validates_exclusion_of":e.add(Validate.Exclusion,{within:JSON.parse(b["in"]),allowNull:b.allow_nil,failureMessage:b.message});break;case"validates_format_of":f=this.extractFormatRegex(b["with"]);e.add(Validate.Format,{pattern:new RegExp(f),failureMessage:b.message});break;case"validates_inclusion_of":e.add(Validate.Inclusion,{within:JSON.parse(b["in"]),allowNull:b.allow_nil,failureMessage:b.message});break;case"validates_size_of":case"validates_length_of":e.add(Validate.Length,{is:b.is,minimum:b.minimum,maximum:b.maximum,wrongLengthMessage:b.wrong_length,tooShortMessage:b.too_short,tooLongMessage:b.too_long});break;case"validates_numericality_of":e.add(Validate.Numericality,{is:b.equal_to,minimum:b.greater_than_or_equal_to,maximum:b.less_than_or_equal_to,onlyinteger:b.only_integer,failureMessage:b.message});break;case"validates_presence_of":e.add(Validate.Presence,{failureMessage:b.message});break}}};FormtasticValidation.prototype.initialize=function(){var f=this.selectInputElements();var d,e,a;for(var c=0;c<f.length;c++){d=f[c].getAttribute("validation");if(d!=null){e=d.split(" ");for(var b=0;b<e.length;b++){a=this.getValidationAttributes(f[c]);this.bindInputElements(f[c],e[b],a)}}}};
@@ -0,0 +1,4 @@
1
+ // LiveValidation 1.3 (standalone version)
2
+ // Copyright (c) 2007-2008 Alec Hill (www.livevalidation.com)
3
+ // LiveValidation is licensed under the terms of the MIT License
4
+ var LiveValidation=function(B,A){this.initialize(B,A);};LiveValidation.VERSION="1.3 standalone";LiveValidation.TEXTAREA=1;LiveValidation.TEXT=2;LiveValidation.PASSWORD=3;LiveValidation.CHECKBOX=4;LiveValidation.SELECT=5;LiveValidation.FILE=6;LiveValidation.massValidate=function(C){var D=true;for(var B=0,A=C.length;B<A;++B){var E=C[B].validate();if(D){D=E;}}return D;};LiveValidation.prototype={validClass:"LV_valid",invalidClass:"LV_invalid",messageClass:"LV_validation_message",validFieldClass:"LV_valid_field",invalidFieldClass:"LV_invalid_field",initialize:function(D,C){var A=this;if(!D){throw new Error("LiveValidation::initialize - No element reference or element id has been provided!");}this.element=D.nodeName?D:document.getElementById(D);if(!this.element){throw new Error("LiveValidation::initialize - No element with reference or id of '"+D+"' exists!");}this.validations=[];this.elementType=this.getElementType();this.form=this.element.form;var B=C||{};this.validMessage=B.validMessage||"Thankyou!";var E=B.insertAfterWhatNode||this.element;this.insertAfterWhatNode=E.nodeType?E:document.getElementById(E);this.onValid=B.onValid||function(){this.insertMessage(this.createMessageSpan());this.addFieldClass();};this.onInvalid=B.onInvalid||function(){this.insertMessage(this.createMessageSpan());this.addFieldClass();};this.onlyOnBlur=B.onlyOnBlur||false;this.wait=B.wait||0;this.onlyOnSubmit=B.onlyOnSubmit||false;if(this.form){this.formObj=LiveValidationForm.getInstance(this.form);this.formObj.addField(this);}this.oldOnFocus=this.element.onfocus||function(){};this.oldOnBlur=this.element.onblur||function(){};this.oldOnClick=this.element.onclick||function(){};this.oldOnChange=this.element.onchange||function(){};this.oldOnKeyup=this.element.onkeyup||function(){};this.element.onfocus=function(F){A.doOnFocus(F);return A.oldOnFocus.call(this,F);};if(!this.onlyOnSubmit){switch(this.elementType){case LiveValidation.CHECKBOX:this.element.onclick=function(F){A.validate();return A.oldOnClick.call(this,F);};case LiveValidation.SELECT:case LiveValidation.FILE:this.element.onchange=function(F){A.validate();return A.oldOnChange.call(this,F);};break;default:if(!this.onlyOnBlur){this.element.onkeyup=function(F){A.deferValidation();return A.oldOnKeyup.call(this,F);};}this.element.onblur=function(F){A.doOnBlur(F);return A.oldOnBlur.call(this,F);};}}},destroy:function(){if(this.formObj){this.formObj.removeField(this);this.formObj.destroy();}this.element.onfocus=this.oldOnFocus;if(!this.onlyOnSubmit){switch(this.elementType){case LiveValidation.CHECKBOX:this.element.onclick=this.oldOnClick;case LiveValidation.SELECT:case LiveValidation.FILE:this.element.onchange=this.oldOnChange;break;default:if(!this.onlyOnBlur){this.element.onkeyup=this.oldOnKeyup;}this.element.onblur=this.oldOnBlur;}}this.validations=[];this.removeMessageAndFieldClass();},add:function(A,B){this.validations.push({type:A,params:B||{}});return this;},remove:function(B,D){var E=false;for(var C=0,A=this.validations.length;C<A;C++){if(this.validations[C].type==B){if(this.validations[C].params==D){E=true;break;}}}if(E){this.validations.splice(C,1);}return this;},deferValidation:function(B){if(this.wait>=300){this.removeMessageAndFieldClass();}var A=this;if(this.timeout){clearTimeout(A.timeout);}this.timeout=setTimeout(function(){A.validate();},A.wait);},doOnBlur:function(A){this.focused=false;this.validate(A);},doOnFocus:function(A){this.focused=true;this.removeMessageAndFieldClass();},getElementType:function(){switch(true){case (this.element.nodeName.toUpperCase()=="TEXTAREA"):return LiveValidation.TEXTAREA;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="TEXT"):return LiveValidation.TEXT;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="PASSWORD"):return LiveValidation.PASSWORD;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="CHECKBOX"):return LiveValidation.CHECKBOX;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="FILE"):return LiveValidation.FILE;case (this.element.nodeName.toUpperCase()=="SELECT"):return LiveValidation.SELECT;case (this.element.nodeName.toUpperCase()=="INPUT"):throw new Error("LiveValidation::getElementType - Cannot use LiveValidation on an "+this.element.type+" input!");default:throw new Error("LiveValidation::getElementType - Element must be an input, select, or textarea!");}},doValidations:function(){this.validationFailed=false;for(var C=0,A=this.validations.length;C<A;++C){var B=this.validations[C];switch(B.type){case Validate.Presence:case Validate.Confirmation:case Validate.Acceptance:this.displayMessageWhenEmpty=true;this.validationFailed=!this.validateElement(B.type,B.params);break;default:this.validationFailed=!this.validateElement(B.type,B.params);break;}if(this.validationFailed){return false;}}this.message=this.validMessage;return true;},validateElement:function(A,C){var D=(this.elementType==LiveValidation.SELECT)?this.element.options[this.element.selectedIndex].value:this.element.value;if(A==Validate.Acceptance){if(this.elementType!=LiveValidation.CHECKBOX){throw new Error("LiveValidation::validateElement - Element to validate acceptance must be a checkbox!");}D=this.element.checked;}var E=true;try{A(D,C);}catch(B){if(B instanceof Validate.Error){if(D!==""||(D===""&&this.displayMessageWhenEmpty)){this.validationFailed=true;this.message=B.message;E=false;}}else{throw B;}}finally{return E;}},validate:function(){if(!this.element.disabled){var A=this.doValidations();if(A){this.onValid();return true;}else{this.onInvalid();return false;}}else{return true;}},enable:function(){this.element.disabled=false;return this;},disable:function(){this.element.disabled=true;this.removeMessageAndFieldClass();return this;},createMessageSpan:function(){var A=document.createElement("span");var B=document.createTextNode(this.message);A.appendChild(B);return A;},insertMessage:function(B){this.removeMessage();if((this.displayMessageWhenEmpty&&(this.elementType==LiveValidation.CHECKBOX||this.element.value==""))||this.element.value!=""){var A=this.validationFailed?this.invalidClass:this.validClass;B.className+=" "+this.messageClass+" "+A;if(this.insertAfterWhatNode.nextSibling){this.insertAfterWhatNode.parentNode.insertBefore(B,this.insertAfterWhatNode.nextSibling);}else{this.insertAfterWhatNode.parentNode.appendChild(B);}}},addFieldClass:function(){this.removeFieldClass();if(!this.validationFailed){if(this.displayMessageWhenEmpty||this.element.value!=""){if(this.element.className.indexOf(this.validFieldClass)==-1){this.element.className+=" "+this.validFieldClass;}}}else{if(this.element.className.indexOf(this.invalidFieldClass)==-1){this.element.className+=" "+this.invalidFieldClass;}}},removeMessage:function(){var A;var B=this.insertAfterWhatNode;while(B.nextSibling){if(B.nextSibling.nodeType===1){A=B.nextSibling;break;}B=B.nextSibling;}if(A&&A.className.indexOf(this.messageClass)!=-1){this.insertAfterWhatNode.parentNode.removeChild(A);}},removeFieldClass:function(){if(this.element.className.indexOf(this.invalidFieldClass)!=-1){this.element.className=this.element.className.split(this.invalidFieldClass).join("");}if(this.element.className.indexOf(this.validFieldClass)!=-1){this.element.className=this.element.className.split(this.validFieldClass).join(" ");}},removeMessageAndFieldClass:function(){this.removeMessage();this.removeFieldClass();}};var LiveValidationForm=function(A){this.initialize(A);};LiveValidationForm.instances={};LiveValidationForm.getInstance=function(A){var B=Math.random()*Math.random();if(!A.id){A.id="formId_"+B.toString().replace(/\./,"")+new Date().valueOf();}if(!LiveValidationForm.instances[A.id]){LiveValidationForm.instances[A.id]=new LiveValidationForm(A);}return LiveValidationForm.instances[A.id];};LiveValidationForm.prototype={initialize:function(B){this.name=B.id;this.element=B;this.fields=[];this.oldOnSubmit=this.element.onsubmit||function(){};var A=this;this.element.onsubmit=function(C){return(LiveValidation.massValidate(A.fields))?A.oldOnSubmit.call(this,C||window.event)!==false:false;};},addField:function(A){this.fields.push(A);},removeField:function(C){var D=[];for(var B=0,A=this.fields.length;B<A;B++){if(this.fields[B]!==C){D.push(this.fields[B]);}}this.fields=D;},destroy:function(A){if(this.fields.length!=0&&!A){return false;}this.element.onsubmit=this.oldOnSubmit;LiveValidationForm.instances[this.name]=null;return true;}};var Validate={Presence:function(B,C){var C=C||{};var A=C.failureMessage||"Can't be empty!";if(B===""||B===null||B===undefined){Validate.fail(A);}return true;},Numericality:function(J,E){var A=J;var J=Number(J);var E=E||{};var F=((E.minimum)||(E.minimum==0))?E.minimum:null;var C=((E.maximum)||(E.maximum==0))?E.maximum:null;var D=((E.is)||(E.is==0))?E.is:null;var G=E.notANumberMessage||"Must be a number!";var H=E.notAnIntegerMessage||"Must be an integer!";var I=E.wrongNumberMessage||"Must be "+D+"!";var B=E.tooLowMessage||"Must not be less than "+F+"!";var K=E.tooHighMessage||"Must not be more than "+C+"!";if(!isFinite(J)){Validate.fail(G);}if(E.onlyInteger&&(/\.0+$|\.$/.test(String(A))||J!=parseInt(J))){Validate.fail(H);}switch(true){case (D!==null):if(J!=Number(D)){Validate.fail(I);}break;case (F!==null&&C!==null):Validate.Numericality(J,{tooLowMessage:B,minimum:F});Validate.Numericality(J,{tooHighMessage:K,maximum:C});break;case (F!==null):if(J<Number(F)){Validate.fail(B);}break;case (C!==null):if(J>Number(C)){Validate.fail(K);}break;}return true;},Format:function(C,E){var C=String(C);var E=E||{};var A=E.failureMessage||"Not valid!";var B=E.pattern||/./;var D=E.negate||false;if(!D&&!B.test(C)){Validate.fail(A);}if(D&&B.test(C)){Validate.fail(A);}return true;},Email:function(B,C){var C=C||{};var A=C.failureMessage||"Must be a valid email address!";Validate.Format(B,{failureMessage:A,pattern:/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i});return true;},Length:function(F,G){var F=String(F);var G=G||{};var E=((G.minimum)||(G.minimum==0))?G.minimum:null;var H=((G.maximum)||(G.maximum==0))?G.maximum:null;var C=((G.is)||(G.is==0))?G.is:null;var A=G.wrongLengthMessage||"Must be "+C+" characters long!";var B=G.tooShortMessage||"Must not be less than "+E+" characters long!";var D=G.tooLongMessage||"Must not be more than "+H+" characters long!";switch(true){case (C!==null):if(F.length!=Number(C)){Validate.fail(A);}break;case (E!==null&&H!==null):Validate.Length(F,{tooShortMessage:B,minimum:E});Validate.Length(F,{tooLongMessage:D,maximum:H});break;case (E!==null):if(F.length<Number(E)){Validate.fail(B);}break;case (H!==null):if(F.length>Number(H)){Validate.fail(D);}break;default:throw new Error("Validate::Length - Length(s) to validate against must be provided!");}return true;},Inclusion:function(H,F){var F=F||{};var K=F.failureMessage||"Must be included in the list!";var G=(F.caseSensitive===false)?false:true;if(F.allowNull&&H==null){return true;}if(!F.allowNull&&H==null){Validate.fail(K);}var D=F.within||[];if(!G){var A=[];for(var C=0,B=D.length;C<B;++C){var I=D[C];if(typeof I=="string"){I=I.toLowerCase();}A.push(I);}D=A;if(typeof H=="string"){H=H.toLowerCase();}}var J=false;for(var E=0,B=D.length;E<B;++E){if(D[E]==H){J=true;}if(F.partialMatch){if(H.indexOf(D[E])!=-1){J=true;}}}if((!F.negate&&!J)||(F.negate&&J)){Validate.fail(K);}return true;},Exclusion:function(A,B){var B=B||{};B.failureMessage=B.failureMessage||"Must not be included in the list!";B.negate=true;Validate.Inclusion(A,B);return true;},Confirmation:function(C,D){if(!D.match){throw new Error("Validate::Confirmation - Error validating confirmation: Id of element to match must be provided!");}var D=D||{};var B=D.failureMessage||"Does not match!";var A=D.match.nodeName?D.match:document.getElementById(D.match);if(!A){throw new Error("Validate::Confirmation - There is no reference with name of, or element with id of '"+D.match+"'!");}if(C!=A.value){Validate.fail(B);}return true;},Acceptance:function(B,C){var C=C||{};var A=C.failureMessage||"Must be accepted!";if(!B){Validate.fail(A);}return true;},Custom:function(D,E){var E=E||{};var B=E.against||function(){return true;};var A=E.args||{};var C=E.failureMessage||"Not valid!";if(!B(D,A)){Validate.fail(C);}return true;},now:function(A,D,C){if(!A){throw new Error("Validate::now - Validation function must be provided!");}var E=true;try{A(D,C||{});}catch(B){if(B instanceof Validate.Error){E=false;}else{throw B;}}finally{return E;}},fail:function(A){throw new Validate.Error(A);},Error:function(A){this.message=A;this.name="ValidationError";}};
@@ -0,0 +1,93 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "FormtasticValidation#input" do
4
+ include FormtasticValidationSpecHelper
5
+
6
+ before do
7
+ @output_buffer = ''
8
+ setup_mocks
9
+ end
10
+
11
+ describe "validation and options attributes" do
12
+
13
+ describe "when object is given" do
14
+ describe 'and validations were called for the method' do
15
+
16
+ it 'should add a validation tag' do
17
+ @bob.class.should_receive(:reflect_on_validations_for).with(:name).any_number_of_times.and_return([mock('MacroReflection', :macro => :validates_whatever, :name => :name, :options => {})])
18
+ semantic_form_for(@bob) do |builder|
19
+ concat(builder.input(:name))
20
+ end
21
+ output_buffer.should have_tag('input[@validation="validates_whatever"]')
22
+ end
23
+
24
+ it "should add multiple validation tags separated by space" do
25
+ @bob.class.should_receive(:reflect_on_validations_for).with(:name).any_number_of_times.and_return([mock('MacroReflection', :macro => :validates_whatever, :name => :name, :options => {}), mock('MacroReflection', :macro => :validates_anything, :name => :name, :options => {})])
26
+ semantic_form_for(@bob) do |builder|
27
+ concat(builder.input(:name))
28
+ end
29
+ output_buffer.should have_tag('input[@validation="validates_whatever validates_anything"]')
30
+ end
31
+
32
+ it 'should add a single option as tag' do
33
+ @bob.class.should_receive(:reflect_on_validations_for).with(:name).any_number_of_times.and_return([mock('MacroReflection', :macro => :validates_whatever, :name => :name, :options => {:one => 'one'})])
34
+ semantic_form_for(@bob) do |builder|
35
+ concat(builder.input(:name))
36
+ end
37
+ output_buffer.should have_tag('input[@validation="validates_whatever"]')
38
+ output_buffer.should have_tag('input[@validation_one="one"]')
39
+ end
40
+
41
+ it "should add multiple options as separate tags" do
42
+ @bob.class.should_receive(:reflect_on_validations_for).with(:name).any_number_of_times.and_return([mock('MacroReflection', :macro => :validates_whatever, :name => :name, :options => {:one => 'one', :two => 'two'})])
43
+ semantic_form_for(@bob) do |builder|
44
+ concat(builder.input(:name))
45
+ end
46
+ output_buffer.should have_tag('input[@validation="validates_whatever"]')
47
+ output_buffer.should have_tag('input[@validation_one="one"]')
48
+ output_buffer.should have_tag('input[@validation_two="two"]')
49
+ end
50
+
51
+ it "should include the input_html" do
52
+ @bob.class.should_receive(:reflect_on_validations_for).with(:name).any_number_of_times.and_return([mock('MacroReflection', :macro => :validates_whatever, :name => :name, :options => {:one => 'one', :two => 'two'})])
53
+ semantic_form_for(@bob) do |builder|
54
+ concat(builder.input(:name, :input_html => {:three => 'three'}))
55
+ end
56
+ output_buffer.should have_tag('input[@validation="validates_whatever"]')
57
+ output_buffer.should have_tag('input[@validation_one="one"]')
58
+ output_buffer.should have_tag('input[@validation_two="two"]')
59
+ output_buffer.should have_tag('input[@three="three"]')
60
+ end
61
+ end
62
+ end
63
+
64
+ describe "validation options" do
65
+ it "should serialise regex as json strings" do
66
+ @bob.class.should_receive(:reflect_on_validations_for).with(:name).any_number_of_times.and_return([mock('MacroReflection', :macro => :validates_format_of, :name => :name, :options => {:with => /^a garden$/})])
67
+ semantic_form_for(@bob) do |builder|
68
+ concat(builder.input(:name))
69
+ end
70
+ output_buffer.should have_tag('input[@validation="validates_format_of"]')
71
+ output_buffer.should have_tag('input[@validation_with="/^a garden$/"]')
72
+ end
73
+
74
+ it "should serialise array as json strings" do
75
+ @bob.class.should_receive(:reflect_on_validations_for).with(:name).any_number_of_times.and_return([mock('MacroReflection', :macro => :validates_inclusion_of, :name => :name, :options => {:in => %w(one two three)})])
76
+ semantic_form_for(@bob) do |builder|
77
+ concat(builder.input(:name))
78
+ end
79
+ output_buffer.should have_tag('input[@validation="validates_inclusion_of"]')
80
+ output_buffer.should have_tag(%\input[@validation_in='["one","two","three"]']\)
81
+ end
82
+
83
+ it "should serialise range as array" do
84
+ @bob.class.should_receive(:reflect_on_validations_for).with(:name).any_number_of_times.and_return([mock('MacroReflection', :macro => :validates_inclusion_of, :name => :name, :options => {:in => 1..3})])
85
+ semantic_form_for(@bob) do |builder|
86
+ concat(builder.input(:name))
87
+ end
88
+ output_buffer.should have_tag('input[@validation="validates_inclusion_of"]')
89
+ output_buffer.should have_tag(%\input[@validation_in='[1,2,3]']\)
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format=progress
@@ -0,0 +1,71 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'formtastic_validation'
4
+
5
+ require 'rubygems'
6
+ gem 'activesupport', '2.3.8'
7
+ gem 'actionpack', '2.3.8'
8
+ require 'active_support'
9
+ require 'active_record'
10
+ require 'action_pack'
11
+ require 'action_view'
12
+ require 'action_controller'
13
+
14
+ gem 'rspec', '>= 1.3.0'
15
+ gem 'rspec-rails', '>= 1.3.2'
16
+ gem 'nokogiri', '>= 1.4.2'
17
+ gem 'rspec_tag_matchers', '>= 1.0.0'
18
+ gem 'validation_reflection', '>=0.3.8'
19
+ gem 'formtastic', '>=1.0.0'
20
+ require 'rspec_tag_matchers'
21
+ require 'validation_reflection'
22
+ require 'nokogiri'
23
+ require 'rspec_tag_matchers'
24
+ require 'formtastic'
25
+
26
+ Spec::Runner.configure do |config|
27
+ config.include(RspecTagMatchers)
28
+ end
29
+
30
+ module FormtasticValidationSpecHelper
31
+ include ActionView::Helpers::FormHelper
32
+ include ActionView::Helpers::FormTagHelper
33
+ include ActionView::Helpers::FormOptionsHelper
34
+ include ActionView::Helpers::UrlHelper
35
+ include ActionView::Helpers::TagHelper
36
+ include ActionView::Helpers::TextHelper
37
+ include ActionView::Helpers::ActiveRecordHelper
38
+ include ActionView::Helpers::RecordIdentificationHelper
39
+ include ActionView::Helpers::DateHelper
40
+ include ActionView::Helpers::CaptureHelper
41
+ include ActionView::Helpers::AssetTagHelper
42
+ include ActiveSupport
43
+ include ActionController::PolymorphicRoutes
44
+
45
+ include Formtastic::SemanticFormHelper
46
+ Formtastic::SemanticFormBuilder.class_eval { include FormtasticValidation }
47
+
48
+ class ::User ; end
49
+
50
+ def setup_mocks
51
+ def user_path(o); "/users/1"; end
52
+ def users_path; "/users"; end
53
+ def new_user_path; "/users/new"; end
54
+
55
+ @bob = User.new
56
+ @bob.stub!(:name).and_return('Bob')
57
+ @bob.stub!(:email).and_return('bob@mail.com')
58
+ @bob.stub!(:mobile).and_return('123456')
59
+ end
60
+
61
+ def self.included(base)
62
+ base.class_eval do
63
+ attr_accessor :output_buffer
64
+
65
+ def protect_against_forgery?
66
+ false
67
+ end
68
+ end
69
+ end
70
+
71
+ end
metadata ADDED
@@ -0,0 +1,214 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: formtastic_validation
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Wong Liang Zan
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-08-31 00:00:00 +08:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: validation_reflection
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - "="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ - 3
33
+ - 8
34
+ version: 0.3.8
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: formtastic
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - "="
44
+ - !ruby/object:Gem::Version
45
+ hash: 23
46
+ segments:
47
+ - 1
48
+ - 0
49
+ - 0
50
+ version: 1.0.0
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: rspec
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - "="
60
+ - !ruby/object:Gem::Version
61
+ hash: 27
62
+ segments:
63
+ - 1
64
+ - 3
65
+ - 0
66
+ version: 1.3.0
67
+ type: :development
68
+ version_requirements: *id003
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec-rails
71
+ prerelease: false
72
+ requirement: &id004 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - "="
76
+ - !ruby/object:Gem::Version
77
+ hash: 31
78
+ segments:
79
+ - 1
80
+ - 3
81
+ - 2
82
+ version: 1.3.2
83
+ type: :development
84
+ version_requirements: *id004
85
+ - !ruby/object:Gem::Dependency
86
+ name: rspec_tag_matchers
87
+ prerelease: false
88
+ requirement: &id005 !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - "="
92
+ - !ruby/object:Gem::Version
93
+ hash: 23
94
+ segments:
95
+ - 1
96
+ - 0
97
+ - 0
98
+ version: 1.0.0
99
+ type: :development
100
+ version_requirements: *id005
101
+ - !ruby/object:Gem::Dependency
102
+ name: nokogiri
103
+ prerelease: false
104
+ requirement: &id006 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - "="
108
+ - !ruby/object:Gem::Version
109
+ hash: 3
110
+ segments:
111
+ - 1
112
+ - 4
113
+ - 2
114
+ version: 1.4.2
115
+ type: :development
116
+ version_requirements: *id006
117
+ - !ruby/object:Gem::Dependency
118
+ name: activesupport
119
+ prerelease: false
120
+ requirement: &id007 !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - "="
124
+ - !ruby/object:Gem::Version
125
+ hash: 19
126
+ segments:
127
+ - 2
128
+ - 3
129
+ - 8
130
+ version: 2.3.8
131
+ type: :development
132
+ version_requirements: *id007
133
+ - !ruby/object:Gem::Dependency
134
+ name: actionpack
135
+ prerelease: false
136
+ requirement: &id008 !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - "="
140
+ - !ruby/object:Gem::Version
141
+ hash: 19
142
+ segments:
143
+ - 2
144
+ - 3
145
+ - 8
146
+ version: 2.3.8
147
+ type: :development
148
+ version_requirements: *id008
149
+ description: A formtastic extension that does client side validation
150
+ email: zan@liangzan.net
151
+ executables: []
152
+
153
+ extensions: []
154
+
155
+ extra_rdoc_files:
156
+ - LICENSE
157
+ - README.textile
158
+ files:
159
+ - .document
160
+ - .gitignore
161
+ - LICENSE
162
+ - README.textile
163
+ - Rakefile
164
+ - VERSION
165
+ - formtastic_validation.gemspec
166
+ - javascripts/README.textile
167
+ - javascripts/build/formtastic_validation-0.1.0.min.js
168
+ - javascripts/lib/livevalidation-1.3.min.js
169
+ - javascripts/src/formtastic_validation.js
170
+ - lib/formtastic_validation.rb
171
+ - rails_generators/formtastic_validation/formtastic_validation_generator.rb
172
+ - rails_generators/formtastic_validation/templates/config/initializers/formtastic_validation.rb
173
+ - rails_generators/formtastic_validation/templates/public/javascripts/formtastic_validation-0.1.0.min.js
174
+ - rails_generators/formtastic_validation/templates/public/javascripts/livevalidation-1.3.min.js
175
+ - spec/formtastic_validation_spec.rb
176
+ - spec/spec.opts
177
+ - spec/spec_helper.rb
178
+ has_rdoc: true
179
+ homepage: http://github.com/liangzan/formtastic_validation
180
+ licenses: []
181
+
182
+ post_install_message:
183
+ rdoc_options:
184
+ - --charset=UTF-8
185
+ require_paths:
186
+ - lib
187
+ required_ruby_version: !ruby/object:Gem::Requirement
188
+ none: false
189
+ requirements:
190
+ - - ">="
191
+ - !ruby/object:Gem::Version
192
+ hash: 3
193
+ segments:
194
+ - 0
195
+ version: "0"
196
+ required_rubygems_version: !ruby/object:Gem::Requirement
197
+ none: false
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ hash: 3
202
+ segments:
203
+ - 0
204
+ version: "0"
205
+ requirements: []
206
+
207
+ rubyforge_project:
208
+ rubygems_version: 1.3.7
209
+ signing_key:
210
+ specification_version: 3
211
+ summary: Client side validation with formtastic
212
+ test_files:
213
+ - spec/formtastic_validation_spec.rb
214
+ - spec/spec_helper.rb