jinda 0.5.6 → 0.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b19ce89ffc7d0cc62a3694aa01206e6de1d04ae31c3204a8fd39ca7195593434
4
- data.tar.gz: 425b20ce57cfba2d88b3eb9f578366bdb363594bde4e175f98e4a5106a38ba45
3
+ metadata.gz: c9ccb1a4e45e53213b02caa625662e84ccd386d446509fef479f183f8a2aa3b9
4
+ data.tar.gz: f0efd2ac4630d6a145510969de1d361a28d3c376179649b906bb3f6f9e5b46b9
5
5
  SHA512:
6
- metadata.gz: 1380caefa23d4e6fc49053f55f40dcfd1e5b244efdebe101b7adfb48043bf0d8b57cfc6f2b5fa48dcd67f500d5e99a3574c75608cff9521d7b0a3643f6c093ef
7
- data.tar.gz: 7fef8092499796b99418abef949dce07f3d61565dcdfd5a95525580a90ceefeea5c7c443e1a8b60588265cad164052a9b8bca74f1f2f85c6eb6b4c23a0477c3a
6
+ metadata.gz: a2fbe028c4cd8b129c70b35c908479ca9aec7b0d0389256a1e423eb81315b8c07728ac8dc5a81d469114f2eb9e6380c39420d35202247b878bc690cda832d8d2
7
+ data.tar.gz: 84ef6f473657779def3fdfe49dfbfdb08f386d79028d3cffb5d3896ae06d864185b6ddb949e695a0deb1162fcae7f40ffa353b289f67cc6d2320c532a0f5b88a
data/README.md CHANGED
@@ -40,7 +40,7 @@ app without ActiveRecord
40
40
 
41
41
  ## Add jinda to your Gemfile:
42
42
 
43
- gem 'jinda', '~> 0.5.6'
43
+ gem 'jinda', '~> 0.5.7'
44
44
 
45
45
  For Development (most updated)
46
46
 
@@ -110,7 +110,8 @@ module Jinda
110
110
  route "resources :users"
111
111
  route "resources :docs"
112
112
  route "resources :notes"
113
- route "resources :articles"
113
+ route "resources :comments"
114
+ route "resources :articles do resources :comments end"
114
115
  route "get '/jinda/document/:id' => 'jinda#document'"
115
116
  route "get '/notes/destroy/:id' => 'notes#destroy'"
116
117
  route "get '/notes/my/destroy/:id' => 'notes#destroy'"
@@ -118,7 +119,8 @@ module Jinda
118
119
  route "get '/notes/my' => 'notes/my'"
119
120
  route "get '/docs/my' => 'docs/my'"
120
121
  route "get '/articles/my/destroy' => 'articles#destroy'"
121
- route "get '/articles/my' => 'articles/my'"
122
+ route "get '/articles/show' => 'articles/show'"
123
+ route "get '/articles/my' => 'articles#my'"
122
124
  route "get '/logout' => 'sessions#destroy', :as => 'logout'"
123
125
  route "get '/auth/failure' => 'sessions#destroy'"
124
126
  route "get '/auth/:provider/callback' => 'sessions#create'"
@@ -1,5 +1,4 @@
1
1
  class ApplicationController < ActionController::Base
2
- # https://www.cookieshq.co.uk/posts/easy-seo-metatags-with-rails-4#a-word-about-turbolinks
3
2
  before_action :prepare_meta_tags, if: -> { request.get? }
4
3
  # CSRF protection is turned on with the protect_from_forgery method.
5
4
  protect_from_forgery unless: -> { request.format.json? }
@@ -1,23 +1,30 @@
1
1
  class ArticlesController < ApplicationController
2
- before_action :load_article, only: [:show,]
3
- before_action :load_edit_article, only: [:edit, :destroy]
4
- before_action :load_comments, only: [:show]
2
+ before_action :load_articles, except: [:destroy]
3
+ before_action :load_my_articles, only: [:my]
4
+ before_action :load_article, only: [:destroy, :update]
5
5
 
6
6
  def index
7
- @articles = Article.desc(:created_at).page(params[:page]).per(10)
7
+ # before_action
8
8
  end
9
9
 
10
- def show
10
+ def my
11
+ # before_action
12
+ end
13
+
14
+ def show
15
+ @article = Article.find(params[:article_id])
16
+ @comments = @article.comments.desc(:created_at).page(params[:page]).per(10)
11
17
  prepare_meta_tags(title: @article.title,
12
18
  description: @article.text,
13
19
  keywords: @article.keywords)
14
20
  end
15
21
 
16
22
  def edit
17
- @page_title = 'Member Login'
23
+ @page_title = 'Edit Article'
18
24
  end
19
25
 
20
26
  def create
27
+ # Use Jinda $xvars
21
28
  @article = Article.new(
22
29
  title: $xvars["form_article"]["title"],
23
30
  text: $xvars["form_article"]["text"],
@@ -27,51 +34,67 @@ class ArticlesController < ApplicationController
27
34
  @article.save!
28
35
  end
29
36
 
30
- def my
31
- @articles = Article.where(user_id: current_ma_user).desc(:created_at).page(params[:page]).per(10)
32
- @page_title = 'Member Login'
37
+ def my_update
38
+ # before_action
39
+ @article.update(title: $xvars["edit_article"]["article"]["title"],
40
+ text: $xvars["edit_article"]["article"]["text"],
41
+ keywords: $xvars["edit_article"]["article"]["keywords"],
42
+ body: $xvars["edit_article"]["article"]["body"]
43
+ )
33
44
  end
34
45
 
35
- def update
36
- # $xvars["select_article"] and $xvars["edit_article"]
37
- # These are variables with params when called
38
- # They contain everything that we get their forms select_article and edit_article
39
-
40
- article_id = $xvars["select_article"] ? $xvars["select_article"]["title"] : $xvars["p"]["article_id"]
41
- @article = Article.find(article_id)
42
- @article.update(title: $xvars["edit_article"]["title"],
43
- text: $xvars["edit_article"]["text"],
44
- keywords: $xvars["edit_article"]["keywords"],
45
- body: $xvars["edit_article"]["body"]
46
+ def j_update
47
+ # Use Jinda $xvars
48
+ @article_id = $xvars["select_article"] ? $xvars["select_article"]["title"] : $xvars["p"]["article_id"]
49
+ @article = Article.find_by :id => @article_id
50
+ @article.update(title: $xvars["edit_article"]["article"]["title"],
51
+ text: $xvars["edit_article"]["article"]["text"],
52
+ keywords: $xvars["edit_article"]["article"]["keywords"],
53
+ body: $xvars["edit_article"]["article"]["body"]
46
54
  )
47
55
  end
48
56
 
49
57
  def destroy
50
- #
51
- # duplicated from jinda_controller
52
- # Expected to use in jinda)controller
53
- current_ma_user = User.where(:auth_token => cookies[:auth_token]).first if cookies[:auth_token]
54
-
55
- if Rails.env.test? #Temp solution until fix test of current_ma_user
56
- current_ma_user = $xvars["current_ma_user"]
57
- #current_ma_user = @article.user
58
- end
58
+ # Use Rails
59
+ # before_action
59
60
 
60
- if current_ma_user.role.upcase.split(',').include?("A") || current_ma_user == @article.user
61
+ if current_admin? || current_ma_user == @article.user
61
62
  @article.destroy
62
63
  end
63
64
 
64
- redirect_to :action=>'my'
65
+ action = (current_admin? ? 'index' : 'my')
66
+ redirect_to :action=> (current_admin? ? 'index' : 'my')
65
67
  end
66
68
 
67
69
  private
70
+
71
+ def current_admin?
72
+ if current_ma_user.role.upcase.split(',').include?("A")
73
+ return true
74
+ else
75
+ return false
76
+ end
77
+ end
68
78
 
69
- def load_edit_article
70
- @article = Article.find(params.require(:article_id))
79
+
80
+ def load_articles
81
+ @articles = Article.desc(:created_at).page(params[:page]).per(10)
71
82
  end
72
-
83
+
84
+ def load_my_articles
85
+ @my_articles = @articles.where(user: current_ma_user)
86
+ end
87
+
73
88
  def load_article
74
- @article = Article.find(params.permit(:id))
89
+ @article = Article.find(params[:article_id])
90
+ end
91
+
92
+ def article_params
93
+ params[:article_id]
94
+ end
95
+
96
+ def load_edit_article
97
+ @article = Article.find(params.require(:article).permit(:article_id))
75
98
  end
76
99
 
77
100
  def load_comments
@@ -1,17 +1,19 @@
1
1
  class CommentsController < ApplicationController
2
2
 
3
3
  def create
4
- # $xvars['p'] is the query parameters like params in rails guide:
5
- # private
6
- # def comment_params
7
- # params.require(:comment).permit(:commenter, :body)
8
- # end
9
- #
10
- @article = Article.find($xvars['p']['comment']['article_id'])
11
- @comment = @article.comments.new(
12
- body: $xvars['p']['comment']['body'],
13
- user_id: $xvars["user_id"])
4
+ @article = Article.find(article_params["article_id"])
5
+ @comment = @article.comments.new(comment_params)
14
6
  @comment.save!
7
+ redirect_to controller: 'articles', action: 'show', article_id: @article
15
8
  end
16
9
 
10
+ private
11
+
12
+ def article_params
13
+ params.require(:comment).permit(:article_id)
14
+ end
15
+
16
+ def comment_params
17
+ params.require(:comment).permit(:body, :article_id, :user_id)
18
+ end
17
19
  end
@@ -49,7 +49,7 @@
49
49
  <node CREATED="1275788317299" ID="ID_716276608" MODIFIED="1511159716471" TEXT="link: error_logs: /jinda/error_logs"/>
50
50
  <node CREATED="1275788317299" ID="ID_1570419198" MODIFIED="1587858894833" TEXT="link: notice_logs: /jinda/notice_logs"/>
51
51
  </node>
52
- <node CREATED="1589756777499" ID="ID_853155456" MODIFIED="1589772589662" TEXT="docs: Document">
52
+ <node CREATED="1589756777499" FOLDED="true" ID="ID_853155456" MODIFIED="1592785016438" TEXT="docs: Document">
53
53
  <node CREATED="1589757073388" ID="ID_1938238774" MODIFIED="1589772640862" TEXT="link: My Document: /docs/my">
54
54
  <node CREATED="1589757125861" ID="ID_521286668" MODIFIED="1589757136547" TEXT="role:m"/>
55
55
  </node>
@@ -87,7 +87,7 @@
87
87
  </node>
88
88
  </node>
89
89
  </node>
90
- <node CREATED="1493393619430" FOLDED="true" ID="ID_554831343" MODIFIED="1591392794806" TEXT="notes: Notes">
90
+ <node CREATED="1493393619430" ID="ID_554831343" MODIFIED="1592926786394" TEXT="notes: Notes">
91
91
  <node CREATED="1493489768542" ID="ID_737469676" MODIFIED="1589772615102" TEXT="link:My Notes: /notes/my">
92
92
  <node CREATED="1493490295677" ID="ID_514416082" MODIFIED="1493490302239" TEXT="role:m"/>
93
93
  </node>
@@ -151,15 +151,22 @@
151
151
  </node>
152
152
  </node>
153
153
  </node>
154
- <node CREATED="1493393619430" FOLDED="true" ID="ID_328863650" MODIFIED="1591392789585" TEXT="articles: Article">
154
+ <node CREATED="1493393619430" ID="ID_328863650" MODIFIED="1592785024002" TEXT="articles: Article">
155
155
  <node CREATED="1493419096527" ID="ID_1521905276" MODIFIED="1493478060121" TEXT="link: All Articles: /articles"/>
156
156
  <node CREATED="1493489768542" FOLDED="true" ID="ID_1376361427" MODIFIED="1589757167952" TEXT="link: My article: /articles/my">
157
157
  <node CREATED="1493490295677" ID="ID_628476988" MODIFIED="1493490302239" TEXT="role:m"/>
158
158
  </node>
159
159
  <node CREATED="1493419257021" ID="ID_1355420049" MODIFIED="1588619429446" TEXT="new_article: New Article">
160
+ <node CREATED="1592785043843" ID="ID_345287367" MODIFIED="1592799450248" TEXT="condition=1">
161
+ <icon BUILTIN="help"/>
162
+ <node CREATED="1592786104568" ID="ID_282788674" MODIFIED="1592802765385" TEXT="redirect: &quot;next&quot;"/>
163
+ </node>
160
164
  <node CREATED="1493419299004" ID="ID_1468250197" MODIFIED="1493419428686" TEXT="form_article: New Article">
161
165
  <icon BUILTIN="attach"/>
162
166
  <node CREATED="1493479075294" ID="ID_1145618514" MODIFIED="1493479079687" TEXT="role:m"/>
167
+ <node CREATED="1592785043843" ID="ID_962055360" MODIFIED="1592785146999" TEXT="match: &quot;end&quot;">
168
+ <icon BUILTIN="help"/>
169
+ </node>
163
170
  </node>
164
171
  <node CREATED="1493419491125" ID="ID_1687683396" MODIFIED="1493483244848" TEXT="create: Create Article">
165
172
  <icon BUILTIN="bookmark"/>
@@ -179,9 +186,14 @@
179
186
  <icon BUILTIN="attach"/>
180
187
  <node CREATED="1493479557266" ID="ID_1681993437" MODIFIED="1493479561055" TEXT="role:m"/>
181
188
  </node>
182
- <node CREATED="1493419735921" ID="ID_1575963748" MODIFIED="1493489477505" TEXT="update: Update Article">
189
+ <node CREATED="1493419735921" ID="ID_1575963748" MODIFIED="1593286858918" TEXT="j_update: Update Article">
183
190
  <icon BUILTIN="bookmark"/>
184
191
  </node>
192
+ <node CREATED="1591278861459" ID="ID_863187878" MODIFIED="1591319807025" TEXT="/articles/my">
193
+ <arrowlink DESTINATION="ID_863187878" ENDARROW="Default" ENDINCLINATION="0;0;" ID="Arrow_ID_604727235" STARTARROW="None" STARTINCLINATION="0;0;"/>
194
+ <linktarget COLOR="#b0b0b0" DESTINATION="ID_863187878" ENDARROW="Default" ENDINCLINATION="0;0;" ID="Arrow_ID_604727235" SOURCE="ID_863187878" STARTARROW="None" STARTINCLINATION="0;0;"/>
195
+ <icon BUILTIN="forward"/>
196
+ </node>
185
197
  </node>
186
198
  <node CREATED="1495246388313" ID="ID_1861034169" MODIFIED="1585001527103" TEXT="xedit_article: xEdit Article">
187
199
  <icon BUILTIN="button_cancel"/>
@@ -189,12 +201,12 @@
189
201
  <icon BUILTIN="attach"/>
190
202
  <node CREATED="1495246517185" ID="ID_1224073606" MODIFIED="1495246522964" TEXT="role:m"/>
191
203
  </node>
192
- <node CREATED="1495246466176" ID="ID_1635586443" MODIFIED="1495266959360" TEXT="update: Update Article">
204
+ <node CREATED="1495246466176" ID="ID_1635586443" MODIFIED="1593286876869" TEXT="j_update: Update Article">
193
205
  <icon BUILTIN="bookmark"/>
194
206
  </node>
195
207
  </node>
196
208
  </node>
197
- <node CREATED="1493664700564" FOLDED="true" ID="ID_704959130" MODIFIED="1591392798294" TEXT="comments: Comment">
209
+ <node CREATED="1493664700564" ID="ID_704959130" MODIFIED="1592950100226" TEXT="comments: Comment">
198
210
  <icon BUILTIN="button_cancel"/>
199
211
  <node CREATED="1493665155709" ID="ID_1973520751" MODIFIED="1591392666652" TEXT="new_comment: New Comment">
200
212
  <icon BUILTIN="button_cancel"/>
@@ -202,6 +214,11 @@
202
214
  <icon BUILTIN="bookmark"/>
203
215
  <node CREATED="1493665231940" ID="ID_1645532530" MODIFIED="1493665237018" TEXT="role:m"/>
204
216
  </node>
217
+ <node CREATED="1591278861459" ID="ID_170265872" MODIFIED="1591319807025" TEXT="/articles/my">
218
+ <arrowlink DESTINATION="ID_170265872" ENDARROW="Default" ENDINCLINATION="0;0;" ID="Arrow_ID_1468117" STARTARROW="None" STARTINCLINATION="0;0;"/>
219
+ <linktarget COLOR="#b0b0b0" DESTINATION="ID_170265872" ENDARROW="Default" ENDINCLINATION="0;0;" ID="Arrow_ID_1468117" SOURCE="ID_170265872" STARTARROW="None" STARTINCLINATION="0;0;"/>
220
+ <icon BUILTIN="forward"/>
221
+ </node>
205
222
  </node>
206
223
  <node CREATED="1494393494180" ID="ID_1101205873" MODIFIED="1495275996686" TEXT="role:m"/>
207
224
  </node>
@@ -11,7 +11,7 @@
11
11
  - @articles.each do |article|
12
12
  %tr
13
13
  %td= article.user.code if article.user
14
- %td= link_to article.title, :controller=>"articles", :action=>"show", :id=>article.id
14
+ %td= link_to article.title, :controller=>"articles", :action=>"show", :article_id=>article.id
15
15
  %td= article.text
16
16
  %td= article.created_at
17
17
  %td= article.updated_at
@@ -20,4 +20,4 @@
20
20
  - unless current_ma_user.role.upcase.split(',').include?("A")
21
21
  - next unless article.user
22
22
  - next unless current_ma_user == article.user
23
- = link_to image_tag('delete.png', style:'border:none; float:none;'), "#", :onclick=>"if (confirm('Please Confirm')) {location.hash='/articles/destroy/#{article.id}';}"
23
+ = link_to image_tag('delete.png', style:'border:none; float:none;'), {controller: "articles", action: "destroy", article_id: article.id}, data: { confirm: "Please Confirm" }
@@ -1,7 +1,7 @@
1
1
  - @title= "My Articles"
2
2
  %p
3
3
  - @page_title = 'All Articlces'
4
- = paginate @articles
4
+ = paginate @my_articles
5
5
  %table#article-table
6
6
  %tr
7
7
  %th Title
@@ -10,9 +10,9 @@
10
10
  %th Updated
11
11
  %th Delete
12
12
  %th Edit
13
- - @articles.each do |article|
13
+ - @my_articles.each do |article|
14
14
  %tr
15
- %td= link_to article.title, :controller=>"articles", :action=>"show", :id=>article.id
15
+ %td= link_to article.title, :controller=>"articles", :action=>"show", :article_id => article.id
16
16
  %td= article.text.html_safe
17
17
  %td= article.created_at.strftime('%m/%d/%Y')
18
18
  %td= article.updated_at.strftime('%m/%d/%Y')
@@ -7,18 +7,19 @@
7
7
 
8
8
  #article-text= @article.body.html_safe
9
9
 
10
- = link_to image_tag('pencil.png', style:'border:none; float:none;'), {controller: "jinda", action: "init", s: 'articles:xedit_article', article_id: @article.id}, data: { confirm: "Please Confirm" }
10
+ = link_to image_tag('pencil.png', style:'border:none; float:none;'), {controller: "comments", action: "create", article_id: @article.id}, data: { confirm: "Please Confirm" }
11
11
  - @comments.each do |comment|
12
12
  %div#article-comment.ui-corner-all
13
13
  Author:
14
14
  %b= comment.user.code if comment.user
15
15
  %div#comment-body
16
16
  %i= comment.body
17
-
17
+ -#
18
18
  - if login?
19
19
  %h3 Add a comment:
20
- = form_for @article.comments.build, url: "/jinda/init?s=comments:new_comment" do |f|
20
+ = form_for([@comment, @article.comments.build]) do |f|
21
21
  = f.hidden_field :article_id, :value => @article.id
22
22
  = f.label :body, "Comment"
23
23
  = f.text_area :body
24
+ = f.hidden_field :user_id, :value => current_ma_user
24
25
  = f.submit
@@ -21,7 +21,7 @@
21
21
 
22
22
  %h2 Installation
23
23
  %ul
24
- %li add gem 'jinda', '0.5.6'
24
+ %li add gem 'jinda', '0.5.7'
25
25
  %li bundle
26
26
  %li rails generate jinda:install
27
27
  %li (run all with "sh install.sh" )
@@ -1,3 +1,3 @@
1
1
  module Jinda
2
- VERSION = "0.5.6"
2
+ VERSION = "0.5.7"
3
3
  end
@@ -0,0 +1,1293 @@
1
+ /*!
2
+ * jQuery Validation Plugin v1.12.0
3
+ *
4
+ * http://jqueryvalidation.org/
5
+ *
6
+ * Copyright (c) 2014 Jörn Zaefferer
7
+ * Released under the MIT license
8
+ */
9
+ (function($) {
10
+
11
+ $.extend($.fn, {
12
+ // http://jqueryvalidation.org/validate/
13
+ validate: function( options ) {
14
+
15
+ // if nothing is selected, return nothing; can't chain anyway
16
+ if ( !this.length ) {
17
+ if ( options && options.debug && window.console ) {
18
+ console.warn( "Nothing selected, can't validate, returning nothing." );
19
+ }
20
+ return;
21
+ }
22
+
23
+ // check if a validator for this form was already created
24
+ var validator = $.data( this[0], "validator" );
25
+ if ( validator ) {
26
+ return validator;
27
+ }
28
+
29
+ // Add novalidate tag if HTML5.
30
+ this.attr( "novalidate", "novalidate" );
31
+
32
+ validator = new $.validator( options, this[0] );
33
+ $.data( this[0], "validator", validator );
34
+
35
+ if ( validator.settings.onsubmit ) {
36
+
37
+ this.validateDelegate( ":submit", "click", function( event ) {
38
+ if ( validator.settings.submitHandler ) {
39
+ validator.submitButton = event.target;
40
+ }
41
+ // allow suppressing validation by adding a cancel class to the submit button
42
+ if ( $(event.target).hasClass("cancel") ) {
43
+ validator.cancelSubmit = true;
44
+ }
45
+
46
+ // allow suppressing validation by adding the html5 formnovalidate attribute to the submit button
47
+ if ( $(event.target).attr("formnovalidate") !== undefined ) {
48
+ validator.cancelSubmit = true;
49
+ }
50
+ });
51
+
52
+ // validate the form on submit
53
+ this.submit( function( event ) {
54
+ if ( validator.settings.debug ) {
55
+ // prevent form submit to be able to see console output
56
+ event.preventDefault();
57
+ }
58
+ function handle() {
59
+ var hidden;
60
+ if ( validator.settings.submitHandler ) {
61
+ if ( validator.submitButton ) {
62
+ // insert a hidden input as a replacement for the missing submit button
63
+ hidden = $("<input type='hidden'/>").attr("name", validator.submitButton.name).val( $(validator.submitButton).val() ).appendTo(validator.currentForm);
64
+ }
65
+ validator.settings.submitHandler.call( validator, validator.currentForm, event );
66
+ if ( validator.submitButton ) {
67
+ // and clean up afterwards; thanks to no-block-scope, hidden can be referenced
68
+ hidden.remove();
69
+ }
70
+ return false;
71
+ }
72
+ return true;
73
+ }
74
+
75
+ // prevent submit for invalid forms or custom submit handlers
76
+ if ( validator.cancelSubmit ) {
77
+ validator.cancelSubmit = false;
78
+ return handle();
79
+ }
80
+ if ( validator.form() ) {
81
+ if ( validator.pendingRequest ) {
82
+ validator.formSubmitted = true;
83
+ return false;
84
+ }
85
+ return handle();
86
+ } else {
87
+ validator.focusInvalid();
88
+ return false;
89
+ }
90
+ });
91
+ }
92
+
93
+ return validator;
94
+ },
95
+ // http://jqueryvalidation.org/valid/
96
+ valid: function() {
97
+ var valid, validator;
98
+
99
+ if ( $(this[0]).is("form")) {
100
+ valid = this.validate().form();
101
+ } else {
102
+ valid = true;
103
+ validator = $(this[0].form).validate();
104
+ this.each(function() {
105
+ valid = validator.element(this) && valid;
106
+ });
107
+ }
108
+ return valid;
109
+ },
110
+ // attributes: space separated list of attributes to retrieve and remove
111
+ removeAttrs: function( attributes ) {
112
+ var result = {},
113
+ $element = this;
114
+ $.each(attributes.split(/\s/), function( index, value ) {
115
+ result[value] = $element.attr(value);
116
+ $element.removeAttr(value);
117
+ });
118
+ return result;
119
+ },
120
+ // http://jqueryvalidation.org/rules/
121
+ rules: function( command, argument ) {
122
+ var element = this[0],
123
+ settings, staticRules, existingRules, data, param, filtered;
124
+
125
+ if ( command ) {
126
+ settings = $.data(element.form, "validator").settings;
127
+ staticRules = settings.rules;
128
+ existingRules = $.validator.staticRules(element);
129
+ switch (command) {
130
+ case "add":
131
+ $.extend(existingRules, $.validator.normalizeRule(argument));
132
+ // remove messages from rules, but allow them to be set separately
133
+ delete existingRules.messages;
134
+ staticRules[element.name] = existingRules;
135
+ if ( argument.messages ) {
136
+ settings.messages[element.name] = $.extend( settings.messages[element.name], argument.messages );
137
+ }
138
+ break;
139
+ case "remove":
140
+ if ( !argument ) {
141
+ delete staticRules[element.name];
142
+ return existingRules;
143
+ }
144
+ filtered = {};
145
+ $.each(argument.split(/\s/), function( index, method ) {
146
+ filtered[method] = existingRules[method];
147
+ delete existingRules[method];
148
+ if ( method === "required" ) {
149
+ $(element).removeAttr("aria-required");
150
+ }
151
+ });
152
+ return filtered;
153
+ }
154
+ }
155
+
156
+ data = $.validator.normalizeRules(
157
+ $.extend(
158
+ {},
159
+ $.validator.classRules(element),
160
+ $.validator.attributeRules(element),
161
+ $.validator.dataRules(element),
162
+ $.validator.staticRules(element)
163
+ ), element);
164
+
165
+ // make sure required is at front
166
+ if ( data.required ) {
167
+ param = data.required;
168
+ delete data.required;
169
+ data = $.extend({ required: param }, data );
170
+ $(element).attr( "aria-required", "true" );
171
+ }
172
+
173
+ // make sure remote is at back
174
+ if ( data.remote ) {
175
+ param = data.remote;
176
+ delete data.remote;
177
+ data = $.extend( data, { remote: param });
178
+ }
179
+
180
+ return data;
181
+ }
182
+ });
183
+
184
+ // Custom selectors
185
+ $.extend($.expr[":"], {
186
+ // http://jqueryvalidation.org/blank-selector/
187
+ blank: function( a ) { return !$.trim("" + $(a).val()); },
188
+ // http://jqueryvalidation.org/filled-selector/
189
+ filled: function( a ) { return !!$.trim("" + $(a).val()); },
190
+ // http://jqueryvalidation.org/unchecked-selector/
191
+ unchecked: function( a ) { return !$(a).prop("checked"); }
192
+ });
193
+
194
+ // constructor for validator
195
+ $.validator = function( options, form ) {
196
+ this.settings = $.extend( true, {}, $.validator.defaults, options );
197
+ this.currentForm = form;
198
+ this.init();
199
+ };
200
+
201
+ // http://jqueryvalidation.org/jQuery.validator.format/
202
+ $.validator.format = function( source, params ) {
203
+ if ( arguments.length === 1 ) {
204
+ return function() {
205
+ var args = $.makeArray(arguments);
206
+ args.unshift(source);
207
+ return $.validator.format.apply( this, args );
208
+ };
209
+ }
210
+ if ( arguments.length > 2 && params.constructor !== Array ) {
211
+ params = $.makeArray(arguments).slice(1);
212
+ }
213
+ if ( params.constructor !== Array ) {
214
+ params = [ params ];
215
+ }
216
+ $.each(params, function( i, n ) {
217
+ source = source.replace( new RegExp("\\{" + i + "\\}", "g"), function() {
218
+ return n;
219
+ });
220
+ });
221
+ return source;
222
+ };
223
+
224
+ $.extend($.validator, {
225
+
226
+ defaults: {
227
+ messages: {},
228
+ groups: {},
229
+ rules: {},
230
+ errorClass: "error",
231
+ validClass: "valid",
232
+ errorElement: "label",
233
+ focusInvalid: true,
234
+ errorContainer: $([]),
235
+ errorLabelContainer: $([]),
236
+ onsubmit: true,
237
+ ignore: ":hidden",
238
+ ignoreTitle: false,
239
+ onfocusin: function( element ) {
240
+ this.lastActive = element;
241
+
242
+ // hide error label and remove error class on focus if enabled
243
+ if ( this.settings.focusCleanup && !this.blockFocusCleanup ) {
244
+ if ( this.settings.unhighlight ) {
245
+ this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass );
246
+ }
247
+ this.addWrapper(this.errorsFor(element)).hide();
248
+ }
249
+ },
250
+ onfocusout: function( element ) {
251
+ if ( !this.checkable(element) && (element.name in this.submitted || !this.optional(element)) ) {
252
+ this.element(element);
253
+ }
254
+ },
255
+ onkeyup: function( element, event ) {
256
+ if ( event.which === 9 && this.elementValue(element) === "" ) {
257
+ return;
258
+ } else if ( element.name in this.submitted || element === this.lastElement ) {
259
+ this.element(element);
260
+ }
261
+ },
262
+ onclick: function( element ) {
263
+ // click on selects, radiobuttons and checkboxes
264
+ if ( element.name in this.submitted ) {
265
+ this.element(element);
266
+
267
+ // or option elements, check parent select in that case
268
+ } else if ( element.parentNode.name in this.submitted ) {
269
+ this.element(element.parentNode);
270
+ }
271
+ },
272
+ highlight: function( element, errorClass, validClass ) {
273
+ if ( element.type === "radio" ) {
274
+ this.findByName(element.name).addClass(errorClass).removeClass(validClass);
275
+ } else {
276
+ $(element).addClass(errorClass).removeClass(validClass);
277
+ }
278
+ },
279
+ unhighlight: function( element, errorClass, validClass ) {
280
+ if ( element.type === "radio" ) {
281
+ this.findByName(element.name).removeClass(errorClass).addClass(validClass);
282
+ } else {
283
+ $(element).removeClass(errorClass).addClass(validClass);
284
+ }
285
+ }
286
+ },
287
+
288
+ // http://jqueryvalidation.org/jQuery.validator.setDefaults/
289
+ setDefaults: function( settings ) {
290
+ $.extend( $.validator.defaults, settings );
291
+ },
292
+
293
+ messages: {
294
+ required: "This field is required.",
295
+ remote: "Please fix this field.",
296
+ email: "Please enter a valid email address.",
297
+ url: "Please enter a valid URL.",
298
+ date: "Please enter a valid date.",
299
+ dateISO: "Please enter a valid date (ISO).",
300
+ number: "Please enter a valid number.",
301
+ digits: "Please enter only digits.",
302
+ creditcard: "Please enter a valid credit card number.",
303
+ equalTo: "Please enter the same value again.",
304
+ maxlength: $.validator.format("Please enter no more than {0} characters."),
305
+ minlength: $.validator.format("Please enter at least {0} characters."),
306
+ rangelength: $.validator.format("Please enter a value between {0} and {1} characters long."),
307
+ range: $.validator.format("Please enter a value between {0} and {1}."),
308
+ max: $.validator.format("Please enter a value less than or equal to {0}."),
309
+ min: $.validator.format("Please enter a value greater than or equal to {0}.")
310
+ },
311
+
312
+ autoCreateRanges: false,
313
+
314
+ prototype: {
315
+
316
+ init: function() {
317
+ this.labelContainer = $(this.settings.errorLabelContainer);
318
+ this.errorContext = this.labelContainer.length && this.labelContainer || $(this.currentForm);
319
+ this.containers = $(this.settings.errorContainer).add( this.settings.errorLabelContainer );
320
+ this.submitted = {};
321
+ this.valueCache = {};
322
+ this.pendingRequest = 0;
323
+ this.pending = {};
324
+ this.invalid = {};
325
+ this.reset();
326
+
327
+ var groups = (this.groups = {}),
328
+ rules;
329
+ $.each(this.settings.groups, function( key, value ) {
330
+ if ( typeof value === "string" ) {
331
+ value = value.split(/\s/);
332
+ }
333
+ $.each(value, function( index, name ) {
334
+ groups[name] = key;
335
+ });
336
+ });
337
+ rules = this.settings.rules;
338
+ $.each(rules, function( key, value ) {
339
+ rules[key] = $.validator.normalizeRule(value);
340
+ });
341
+
342
+ function delegate(event) {
343
+ var validator = $.data(this[0].form, "validator"),
344
+ eventType = "on" + event.type.replace(/^validate/, ""),
345
+ settings = validator.settings;
346
+ if ( settings[eventType] && !this.is( settings.ignore ) ) {
347
+ settings[eventType].call(validator, this[0], event);
348
+ }
349
+ }
350
+ $(this.currentForm)
351
+ .validateDelegate(":text, [type='password'], [type='file'], select, textarea, " +
352
+ "[type='number'], [type='search'] ,[type='tel'], [type='url'], " +
353
+ "[type='email'], [type='datetime'], [type='date'], [type='month'], " +
354
+ "[type='week'], [type='time'], [type='datetime-local'], " +
355
+ "[type='range'], [type='color'] ",
356
+ "focusin focusout keyup", delegate)
357
+ .validateDelegate("[type='radio'], [type='checkbox'], select, option", "click", delegate);
358
+
359
+ if ( this.settings.invalidHandler ) {
360
+ $(this.currentForm).bind("invalid-form.validate", this.settings.invalidHandler);
361
+ }
362
+
363
+ // Add aria-required to any Static/Data/Class required fields before first validation
364
+ // Screen readers require this attribute to be present before the initial submission http://www.w3.org/TR/WCAG-TECHS/ARIA2.html
365
+ $(this.currentForm).find("[required], [data-rule-required], .required").attr("aria-required", "true");
366
+ },
367
+
368
+ // http://jqueryvalidation.org/Validator.form/
369
+ form: function() {
370
+ this.checkForm();
371
+ $.extend(this.submitted, this.errorMap);
372
+ this.invalid = $.extend({}, this.errorMap);
373
+ if ( !this.valid() ) {
374
+ $(this.currentForm).triggerHandler("invalid-form", [ this ]);
375
+ }
376
+ this.showErrors();
377
+ return this.valid();
378
+ },
379
+
380
+ checkForm: function() {
381
+ this.prepareForm();
382
+ for ( var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++ ) {
383
+ this.check( elements[i] );
384
+ }
385
+ return this.valid();
386
+ },
387
+
388
+ // http://jqueryvalidation.org/Validator.element/
389
+ element: function( element ) {
390
+ var cleanElement = this.clean( element ),
391
+ checkElement = this.validationTargetFor( cleanElement ),
392
+ result = true;
393
+
394
+ this.lastElement = checkElement;
395
+
396
+ if ( checkElement === undefined ) {
397
+ delete this.invalid[ cleanElement.name ];
398
+ } else {
399
+ this.prepareElement( checkElement );
400
+ this.currentElements = $( checkElement );
401
+
402
+ result = this.check( checkElement ) !== false;
403
+ if (result) {
404
+ delete this.invalid[checkElement.name];
405
+ } else {
406
+ this.invalid[checkElement.name] = true;
407
+ }
408
+ }
409
+ // Add aria-invalid status for screen readers
410
+ $( element ).attr( "aria-invalid", !result );
411
+
412
+ if ( !this.numberOfInvalids() ) {
413
+ // Hide error containers on last error
414
+ this.toHide = this.toHide.add( this.containers );
415
+ }
416
+ this.showErrors();
417
+ return result;
418
+ },
419
+
420
+ // http://jqueryvalidation.org/Validator.showErrors/
421
+ showErrors: function( errors ) {
422
+ if ( errors ) {
423
+ // add items to error list and map
424
+ $.extend( this.errorMap, errors );
425
+ this.errorList = [];
426
+ for ( var name in errors ) {
427
+ this.errorList.push({
428
+ message: errors[name],
429
+ element: this.findByName(name)[0]
430
+ });
431
+ }
432
+ // remove items from success list
433
+ this.successList = $.grep( this.successList, function( element ) {
434
+ return !(element.name in errors);
435
+ });
436
+ }
437
+ if ( this.settings.showErrors ) {
438
+ this.settings.showErrors.call( this, this.errorMap, this.errorList );
439
+ } else {
440
+ this.defaultShowErrors();
441
+ }
442
+ },
443
+
444
+ // http://jqueryvalidation.org/Validator.resetForm/
445
+ resetForm: function() {
446
+ if ( $.fn.resetForm ) {
447
+ $(this.currentForm).resetForm();
448
+ }
449
+ this.submitted = {};
450
+ this.lastElement = null;
451
+ this.prepareForm();
452
+ this.hideErrors();
453
+ this.elements()
454
+ .removeClass( this.settings.errorClass )
455
+ .removeData( "previousValue" )
456
+ .removeAttr( "aria-invalid" );
457
+ },
458
+
459
+ numberOfInvalids: function() {
460
+ return this.objectLength(this.invalid);
461
+ },
462
+
463
+ objectLength: function( obj ) {
464
+ /* jshint unused: false */
465
+ var count = 0,
466
+ i;
467
+ for ( i in obj ) {
468
+ count++;
469
+ }
470
+ return count;
471
+ },
472
+
473
+ hideErrors: function() {
474
+ this.addWrapper( this.toHide ).hide();
475
+ },
476
+
477
+ valid: function() {
478
+ return this.size() === 0;
479
+ },
480
+
481
+ size: function() {
482
+ return this.errorList.length;
483
+ },
484
+
485
+ focusInvalid: function() {
486
+ if ( this.settings.focusInvalid ) {
487
+ try {
488
+ $(this.findLastActive() || this.errorList.length && this.errorList[0].element || [])
489
+ .filter(":visible")
490
+ .focus()
491
+ // manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find
492
+ .trigger("focusin");
493
+ } catch(e) {
494
+ // ignore IE throwing errors when focusing hidden elements
495
+ }
496
+ }
497
+ },
498
+
499
+ findLastActive: function() {
500
+ var lastActive = this.lastActive;
501
+ return lastActive && $.grep(this.errorList, function( n ) {
502
+ return n.element.name === lastActive.name;
503
+ }).length === 1 && lastActive;
504
+ },
505
+
506
+ elements: function() {
507
+ var validator = this,
508
+ rulesCache = {};
509
+
510
+ // select all valid inputs inside the form (no submit or reset buttons)
511
+ return $(this.currentForm)
512
+ .find("input, select, textarea")
513
+ .not(":submit, :reset, :image, [disabled]")
514
+ .not( this.settings.ignore )
515
+ .filter(function() {
516
+ if ( !this.name && validator.settings.debug && window.console ) {
517
+ console.error( "%o has no name assigned", this);
518
+ }
519
+
520
+ // select only the first element for each name, and only those with rules specified
521
+ if ( this.name in rulesCache || !validator.objectLength($(this).rules()) ) {
522
+ return false;
523
+ }
524
+
525
+ rulesCache[this.name] = true;
526
+ return true;
527
+ });
528
+ },
529
+
530
+ clean: function( selector ) {
531
+ return $(selector)[0];
532
+ },
533
+
534
+ errors: function() {
535
+ var errorClass = this.settings.errorClass.split(" ").join(".");
536
+ return $(this.settings.errorElement + "." + errorClass, this.errorContext);
537
+ },
538
+
539
+ reset: function() {
540
+ this.successList = [];
541
+ this.errorList = [];
542
+ this.errorMap = {};
543
+ this.toShow = $([]);
544
+ this.toHide = $([]);
545
+ this.currentElements = $([]);
546
+ },
547
+
548
+ prepareForm: function() {
549
+ this.reset();
550
+ this.toHide = this.errors().add( this.containers );
551
+ },
552
+
553
+ prepareElement: function( element ) {
554
+ this.reset();
555
+ this.toHide = this.errorsFor(element);
556
+ },
557
+
558
+ elementValue: function( element ) {
559
+ var val,
560
+ $element = $(element),
561
+ type = $element.attr("type");
562
+
563
+ if ( type === "radio" || type === "checkbox" ) {
564
+ return $("input[name='" + $element.attr("name") + "']:checked").val();
565
+ }
566
+
567
+ val = $element.val();
568
+ if ( typeof val === "string" ) {
569
+ return val.replace(/\r/g, "");
570
+ }
571
+ return val;
572
+ },
573
+
574
+ check: function( element ) {
575
+ element = this.validationTargetFor( this.clean( element ) );
576
+
577
+ var rules = $(element).rules(),
578
+ rulesCount = $.map( rules, function(n, i) {
579
+ return i;
580
+ }).length,
581
+ dependencyMismatch = false,
582
+ val = this.elementValue(element),
583
+ result, method, rule;
584
+
585
+ for (method in rules ) {
586
+ rule = { method: method, parameters: rules[method] };
587
+ try {
588
+
589
+ result = $.validator.methods[method].call( this, val, element, rule.parameters );
590
+
591
+ // if a method indicates that the field is optional and therefore valid,
592
+ // don't mark it as valid when there are no other rules
593
+ if ( result === "dependency-mismatch" && rulesCount === 1 ) {
594
+ dependencyMismatch = true;
595
+ continue;
596
+ }
597
+ dependencyMismatch = false;
598
+
599
+ if ( result === "pending" ) {
600
+ this.toHide = this.toHide.not( this.errorsFor(element) );
601
+ return;
602
+ }
603
+
604
+ if ( !result ) {
605
+ this.formatAndAdd( element, rule );
606
+ return false;
607
+ }
608
+ } catch(e) {
609
+ if ( this.settings.debug && window.console ) {
610
+ console.log( "Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.", e );
611
+ }
612
+ throw e;
613
+ }
614
+ }
615
+ if ( dependencyMismatch ) {
616
+ return;
617
+ }
618
+ if ( this.objectLength(rules) ) {
619
+ this.successList.push(element);
620
+ }
621
+ return true;
622
+ },
623
+
624
+ // return the custom message for the given element and validation method
625
+ // specified in the element's HTML5 data attribute
626
+ // return the generic message if present and no method specific message is present
627
+ customDataMessage: function( element, method ) {
628
+ return $( element ).data( "msg" + method[ 0 ].toUpperCase() +
629
+ method.substring( 1 ).toLowerCase() ) || $( element ).data("msg");
630
+ },
631
+
632
+ // return the custom message for the given element name and validation method
633
+ customMessage: function( name, method ) {
634
+ var m = this.settings.messages[name];
635
+ return m && (m.constructor === String ? m : m[method]);
636
+ },
637
+
638
+ // return the first defined argument, allowing empty strings
639
+ findDefined: function() {
640
+ for (var i = 0; i < arguments.length; i++) {
641
+ if ( arguments[i] !== undefined ) {
642
+ return arguments[i];
643
+ }
644
+ }
645
+ return undefined;
646
+ },
647
+
648
+ defaultMessage: function( element, method ) {
649
+ return this.findDefined(
650
+ this.customMessage( element.name, method ),
651
+ this.customDataMessage( element, method ),
652
+ // title is never undefined, so handle empty string as undefined
653
+ !this.settings.ignoreTitle && element.title || undefined,
654
+ $.validator.messages[method],
655
+ "<strong>Warning: No message defined for " + element.name + "</strong>"
656
+ );
657
+ },
658
+
659
+ formatAndAdd: function( element, rule ) {
660
+ var message = this.defaultMessage( element, rule.method ),
661
+ theregex = /\$?\{(\d+)\}/g;
662
+ if ( typeof message === "function" ) {
663
+ message = message.call(this, rule.parameters, element);
664
+ } else if (theregex.test(message)) {
665
+ message = $.validator.format(message.replace(theregex, "{$1}"), rule.parameters);
666
+ }
667
+ this.errorList.push({
668
+ message: message,
669
+ element: element,
670
+ method: rule.method
671
+ });
672
+
673
+ this.errorMap[element.name] = message;
674
+ this.submitted[element.name] = message;
675
+ },
676
+
677
+ addWrapper: function( toToggle ) {
678
+ if ( this.settings.wrapper ) {
679
+ toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) );
680
+ }
681
+ return toToggle;
682
+ },
683
+
684
+ defaultShowErrors: function() {
685
+ var i, elements, error;
686
+ for ( i = 0; this.errorList[i]; i++ ) {
687
+ error = this.errorList[i];
688
+ if ( this.settings.highlight ) {
689
+ this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );
690
+ }
691
+ this.showLabel( error.element, error.message );
692
+ }
693
+ if ( this.errorList.length ) {
694
+ this.toShow = this.toShow.add( this.containers );
695
+ }
696
+ if ( this.settings.success ) {
697
+ for ( i = 0; this.successList[i]; i++ ) {
698
+ this.showLabel( this.successList[i] );
699
+ }
700
+ }
701
+ if ( this.settings.unhighlight ) {
702
+ for ( i = 0, elements = this.validElements(); elements[i]; i++ ) {
703
+ this.settings.unhighlight.call( this, elements[i], this.settings.errorClass, this.settings.validClass );
704
+ }
705
+ }
706
+ this.toHide = this.toHide.not( this.toShow );
707
+ this.hideErrors();
708
+ this.addWrapper( this.toShow ).show();
709
+ },
710
+
711
+ validElements: function() {
712
+ return this.currentElements.not(this.invalidElements());
713
+ },
714
+
715
+ invalidElements: function() {
716
+ return $(this.errorList).map(function() {
717
+ return this.element;
718
+ });
719
+ },
720
+
721
+ showLabel: function( element, message ) {
722
+ var label = this.errorsFor( element );
723
+ if ( label.length ) {
724
+ // refresh error/success class
725
+ label.removeClass( this.settings.validClass ).addClass( this.settings.errorClass );
726
+ // replace message on existing label
727
+ label.html(message);
728
+ } else {
729
+ // create label
730
+ label = $("<" + this.settings.errorElement + ">")
731
+ .attr("for", this.idOrName(element))
732
+ .addClass(this.settings.errorClass)
733
+ .html(message || "");
734
+ if ( this.settings.wrapper ) {
735
+ // make sure the element is visible, even in IE
736
+ // actually showing the wrapped element is handled elsewhere
737
+ label = label.hide().show().wrap("<" + this.settings.wrapper + "/>").parent();
738
+ }
739
+ if ( !this.labelContainer.append(label).length ) {
740
+ if ( this.settings.errorPlacement ) {
741
+ this.settings.errorPlacement(label, $(element) );
742
+ } else {
743
+ label.insertAfter(element);
744
+ }
745
+ }
746
+ }
747
+ if ( !message && this.settings.success ) {
748
+ label.text("");
749
+ if ( typeof this.settings.success === "string" ) {
750
+ label.addClass( this.settings.success );
751
+ } else {
752
+ this.settings.success( label, element );
753
+ }
754
+ }
755
+ this.toShow = this.toShow.add(label);
756
+ },
757
+
758
+ errorsFor: function( element ) {
759
+ var name = this.idOrName(element);
760
+ return this.errors().filter(function() {
761
+ return $(this).attr("for") === name;
762
+ });
763
+ },
764
+
765
+ idOrName: function( element ) {
766
+ return this.groups[element.name] || (this.checkable(element) ? element.name : element.id || element.name);
767
+ },
768
+
769
+ validationTargetFor: function( element ) {
770
+ // if radio/checkbox, validate first element in group instead
771
+ if ( this.checkable(element) ) {
772
+ element = this.findByName( element.name ).not(this.settings.ignore)[0];
773
+ }
774
+ return element;
775
+ },
776
+
777
+ checkable: function( element ) {
778
+ return (/radio|checkbox/i).test(element.type);
779
+ },
780
+
781
+ findByName: function( name ) {
782
+ return $(this.currentForm).find("[name='" + name + "']");
783
+ },
784
+
785
+ getLength: function( value, element ) {
786
+ switch ( element.nodeName.toLowerCase() ) {
787
+ case "select":
788
+ return $("option:selected", element).length;
789
+ case "input":
790
+ if ( this.checkable( element) ) {
791
+ return this.findByName(element.name).filter(":checked").length;
792
+ }
793
+ }
794
+ return value.length;
795
+ },
796
+
797
+ depend: function( param, element ) {
798
+ return this.dependTypes[typeof param] ? this.dependTypes[typeof param](param, element) : true;
799
+ },
800
+
801
+ dependTypes: {
802
+ "boolean": function( param ) {
803
+ return param;
804
+ },
805
+ "string": function( param, element ) {
806
+ return !!$(param, element.form).length;
807
+ },
808
+ "function": function( param, element ) {
809
+ return param(element);
810
+ }
811
+ },
812
+
813
+ optional: function( element ) {
814
+ var val = this.elementValue(element);
815
+ return !$.validator.methods.required.call(this, val, element) && "dependency-mismatch";
816
+ },
817
+
818
+ startRequest: function( element ) {
819
+ if ( !this.pending[element.name] ) {
820
+ this.pendingRequest++;
821
+ this.pending[element.name] = true;
822
+ }
823
+ },
824
+
825
+ stopRequest: function( element, valid ) {
826
+ this.pendingRequest--;
827
+ // sometimes synchronization fails, make sure pendingRequest is never < 0
828
+ if ( this.pendingRequest < 0 ) {
829
+ this.pendingRequest = 0;
830
+ }
831
+ delete this.pending[element.name];
832
+ if ( valid && this.pendingRequest === 0 && this.formSubmitted && this.form() ) {
833
+ $(this.currentForm).submit();
834
+ this.formSubmitted = false;
835
+ } else if (!valid && this.pendingRequest === 0 && this.formSubmitted) {
836
+ $(this.currentForm).triggerHandler("invalid-form", [ this ]);
837
+ this.formSubmitted = false;
838
+ }
839
+ },
840
+
841
+ previousValue: function( element ) {
842
+ return $.data(element, "previousValue") || $.data(element, "previousValue", {
843
+ old: null,
844
+ valid: true,
845
+ message: this.defaultMessage( element, "remote" )
846
+ });
847
+ }
848
+
849
+ },
850
+
851
+ classRuleSettings: {
852
+ required: { required: true },
853
+ email: { email: true },
854
+ url: { url: true },
855
+ date: { date: true },
856
+ dateISO: { dateISO: true },
857
+ number: { number: true },
858
+ digits: { digits: true },
859
+ creditcard: { creditcard: true }
860
+ },
861
+
862
+ addClassRules: function( className, rules ) {
863
+ if ( className.constructor === String ) {
864
+ this.classRuleSettings[className] = rules;
865
+ } else {
866
+ $.extend(this.classRuleSettings, className);
867
+ }
868
+ },
869
+
870
+ classRules: function( element ) {
871
+ var rules = {},
872
+ classes = $(element).attr("class");
873
+
874
+ if ( classes ) {
875
+ $.each(classes.split(" "), function() {
876
+ if ( this in $.validator.classRuleSettings ) {
877
+ $.extend(rules, $.validator.classRuleSettings[this]);
878
+ }
879
+ });
880
+ }
881
+ return rules;
882
+ },
883
+
884
+ attributeRules: function( element ) {
885
+ var rules = {},
886
+ $element = $(element),
887
+ type = element.getAttribute("type"),
888
+ method, value;
889
+
890
+ for (method in $.validator.methods) {
891
+
892
+ // support for <input required> in both html5 and older browsers
893
+ if ( method === "required" ) {
894
+ value = element.getAttribute(method);
895
+ // Some browsers return an empty string for the required attribute
896
+ // and non-HTML5 browsers might have required="" markup
897
+ if ( value === "" ) {
898
+ value = true;
899
+ }
900
+ // force non-HTML5 browsers to return bool
901
+ value = !!value;
902
+ } else {
903
+ value = $element.attr(method);
904
+ }
905
+
906
+ // convert the value to a number for number inputs, and for text for backwards compability
907
+ // allows type="date" and others to be compared as strings
908
+ if ( /min|max/.test( method ) && ( type === null || /number|range|text/.test( type ) ) ) {
909
+ value = Number(value);
910
+ }
911
+
912
+ if ( value || value === 0 ) {
913
+ rules[method] = value;
914
+ } else if ( type === method && type !== "range" ) {
915
+ // exception: the jquery validate 'range' method
916
+ // does not test for the html5 'range' type
917
+ rules[method] = true;
918
+ }
919
+ }
920
+
921
+ // maxlength may be returned as -1, 2147483647 (IE) and 524288 (safari) for text inputs
922
+ if ( rules.maxlength && /-1|2147483647|524288/.test(rules.maxlength) ) {
923
+ delete rules.maxlength;
924
+ }
925
+
926
+ return rules;
927
+ },
928
+
929
+ dataRules: function( element ) {
930
+ var method, value,
931
+ rules = {}, $element = $( element );
932
+ for ( method in $.validator.methods ) {
933
+ value = $element.data( "rule" + method[ 0 ].toUpperCase() + method.substring( 1 ).toLowerCase() );
934
+ if ( value !== undefined ) {
935
+ rules[ method ] = value;
936
+ }
937
+ }
938
+ return rules;
939
+ },
940
+
941
+ staticRules: function( element ) {
942
+ var rules = {},
943
+ validator = $.data(element.form, "validator");
944
+
945
+ if ( validator.settings.rules ) {
946
+ rules = $.validator.normalizeRule(validator.settings.rules[element.name]) || {};
947
+ }
948
+ return rules;
949
+ },
950
+
951
+ normalizeRules: function( rules, element ) {
952
+ // handle dependency check
953
+ $.each(rules, function( prop, val ) {
954
+ // ignore rule when param is explicitly false, eg. required:false
955
+ if ( val === false ) {
956
+ delete rules[prop];
957
+ return;
958
+ }
959
+ if ( val.param || val.depends ) {
960
+ var keepRule = true;
961
+ switch (typeof val.depends) {
962
+ case "string":
963
+ keepRule = !!$(val.depends, element.form).length;
964
+ break;
965
+ case "function":
966
+ keepRule = val.depends.call(element, element);
967
+ break;
968
+ }
969
+ if ( keepRule ) {
970
+ rules[prop] = val.param !== undefined ? val.param : true;
971
+ } else {
972
+ delete rules[prop];
973
+ }
974
+ }
975
+ });
976
+
977
+ // evaluate parameters
978
+ $.each(rules, function( rule, parameter ) {
979
+ rules[rule] = $.isFunction(parameter) ? parameter(element) : parameter;
980
+ });
981
+
982
+ // clean number parameters
983
+ $.each([ "minlength", "maxlength" ], function() {
984
+ if ( rules[this] ) {
985
+ rules[this] = Number(rules[this]);
986
+ }
987
+ });
988
+ $.each([ "rangelength", "range" ], function() {
989
+ var parts;
990
+ if ( rules[this] ) {
991
+ if ( $.isArray(rules[this]) ) {
992
+ rules[this] = [ Number(rules[this][0]), Number(rules[this][1]) ];
993
+ } else if ( typeof rules[this] === "string" ) {
994
+ parts = rules[this].split(/[\s,]+/);
995
+ rules[this] = [ Number(parts[0]), Number(parts[1]) ];
996
+ }
997
+ }
998
+ });
999
+
1000
+ if ( $.validator.autoCreateRanges ) {
1001
+ // auto-create ranges
1002
+ if ( rules.min && rules.max ) {
1003
+ rules.range = [ rules.min, rules.max ];
1004
+ delete rules.min;
1005
+ delete rules.max;
1006
+ }
1007
+ if ( rules.minlength && rules.maxlength ) {
1008
+ rules.rangelength = [ rules.minlength, rules.maxlength ];
1009
+ delete rules.minlength;
1010
+ delete rules.maxlength;
1011
+ }
1012
+ }
1013
+
1014
+ return rules;
1015
+ },
1016
+
1017
+ // Converts a simple string to a {string: true} rule, e.g., "required" to {required:true}
1018
+ normalizeRule: function( data ) {
1019
+ if ( typeof data === "string" ) {
1020
+ var transformed = {};
1021
+ $.each(data.split(/\s/), function() {
1022
+ transformed[this] = true;
1023
+ });
1024
+ data = transformed;
1025
+ }
1026
+ return data;
1027
+ },
1028
+
1029
+ // http://jqueryvalidation.org/jQuery.validator.addMethod/
1030
+ addMethod: function( name, method, message ) {
1031
+ $.validator.methods[name] = method;
1032
+ $.validator.messages[name] = message !== undefined ? message : $.validator.messages[name];
1033
+ if ( method.length < 3 ) {
1034
+ $.validator.addClassRules(name, $.validator.normalizeRule(name));
1035
+ }
1036
+ },
1037
+
1038
+ methods: {
1039
+
1040
+ // http://jqueryvalidation.org/required-method/
1041
+ required: function( value, element, param ) {
1042
+ // check if dependency is met
1043
+ if ( !this.depend(param, element) ) {
1044
+ return "dependency-mismatch";
1045
+ }
1046
+ if ( element.nodeName.toLowerCase() === "select" ) {
1047
+ // could be an array for select-multiple or a string, both are fine this way
1048
+ var val = $(element).val();
1049
+ return val && val.length > 0;
1050
+ }
1051
+ if ( this.checkable(element) ) {
1052
+ return this.getLength(value, element) > 0;
1053
+ }
1054
+ return $.trim(value).length > 0;
1055
+ },
1056
+
1057
+ // http://jqueryvalidation.org/email-method/
1058
+ email: function( value, element ) {
1059
+ // From http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#e-mail-state-%28type=email%29
1060
+ // Retrieved 2014-01-14
1061
+ // If you have a problem with this implementation, report a bug against the above spec
1062
+ // Or use custom methods to implement your own email validation
1063
+ return this.optional(element) || /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(value);
1064
+ },
1065
+
1066
+ // http://jqueryvalidation.org/url-method/
1067
+ url: function( value, element ) {
1068
+ // contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/
1069
+ return this.optional(element) || /^(https?|s?ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\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])([a-z]|\d|-|\.|_|~|[\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])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
1070
+ },
1071
+
1072
+ // http://jqueryvalidation.org/date-method/
1073
+ date: function( value, element ) {
1074
+ return this.optional(element) || !/Invalid|NaN/.test(new Date(value).toString());
1075
+ },
1076
+
1077
+ // http://jqueryvalidation.org/dateISO-method/
1078
+ dateISO: function( value, element ) {
1079
+ return this.optional(element) || /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/.test(value);
1080
+ },
1081
+
1082
+ // http://jqueryvalidation.org/number-method/
1083
+ number: function( value, element ) {
1084
+ return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value);
1085
+ },
1086
+
1087
+ // http://jqueryvalidation.org/digits-method/
1088
+ digits: function( value, element ) {
1089
+ return this.optional(element) || /^\d+$/.test(value);
1090
+ },
1091
+
1092
+ // http://jqueryvalidation.org/creditcard-method/
1093
+ // based on http://en.wikipedia.org/wiki/Luhn/
1094
+ creditcard: function( value, element ) {
1095
+ if ( this.optional(element) ) {
1096
+ return "dependency-mismatch";
1097
+ }
1098
+ // accept only spaces, digits and dashes
1099
+ if ( /[^0-9 \-]+/.test(value) ) {
1100
+ return false;
1101
+ }
1102
+ var nCheck = 0,
1103
+ nDigit = 0,
1104
+ bEven = false,
1105
+ n, cDigit;
1106
+
1107
+ value = value.replace(/\D/g, "");
1108
+
1109
+ // Basing min and max length on
1110
+ // http://developer.ean.com/general_info/Valid_Credit_Card_Types
1111
+ if ( value.length < 13 || value.length > 19 ) {
1112
+ return false;
1113
+ }
1114
+
1115
+ for ( n = value.length - 1; n >= 0; n--) {
1116
+ cDigit = value.charAt(n);
1117
+ nDigit = parseInt(cDigit, 10);
1118
+ if ( bEven ) {
1119
+ if ( (nDigit *= 2) > 9 ) {
1120
+ nDigit -= 9;
1121
+ }
1122
+ }
1123
+ nCheck += nDigit;
1124
+ bEven = !bEven;
1125
+ }
1126
+
1127
+ return (nCheck % 10) === 0;
1128
+ },
1129
+
1130
+ // http://jqueryvalidation.org/minlength-method/
1131
+ minlength: function( value, element, param ) {
1132
+ var length = $.isArray( value ) ? value.length : this.getLength($.trim(value), element);
1133
+ return this.optional(element) || length >= param;
1134
+ },
1135
+
1136
+ // http://jqueryvalidation.org/maxlength-method/
1137
+ maxlength: function( value, element, param ) {
1138
+ var length = $.isArray( value ) ? value.length : this.getLength($.trim(value), element);
1139
+ return this.optional(element) || length <= param;
1140
+ },
1141
+
1142
+ // http://jqueryvalidation.org/rangelength-method/
1143
+ rangelength: function( value, element, param ) {
1144
+ var length = $.isArray( value ) ? value.length : this.getLength($.trim(value), element);
1145
+ return this.optional(element) || ( length >= param[0] && length <= param[1] );
1146
+ },
1147
+
1148
+ // http://jqueryvalidation.org/min-method/
1149
+ min: function( value, element, param ) {
1150
+ return this.optional(element) || value >= param;
1151
+ },
1152
+
1153
+ // http://jqueryvalidation.org/max-method/
1154
+ max: function( value, element, param ) {
1155
+ return this.optional(element) || value <= param;
1156
+ },
1157
+
1158
+ // http://jqueryvalidation.org/range-method/
1159
+ range: function( value, element, param ) {
1160
+ return this.optional(element) || ( value >= param[0] && value <= param[1] );
1161
+ },
1162
+
1163
+ // http://jqueryvalidation.org/equalTo-method/
1164
+ equalTo: function( value, element, param ) {
1165
+ // bind to the blur event of the target in order to revalidate whenever the target field is updated
1166
+ // TODO find a way to bind the event just once, avoiding the unbind-rebind overhead
1167
+ var target = $(param);
1168
+ if ( this.settings.onfocusout ) {
1169
+ target.unbind(".validate-equalTo").bind("blur.validate-equalTo", function() {
1170
+ $(element).valid();
1171
+ });
1172
+ }
1173
+ return value === target.val();
1174
+ },
1175
+
1176
+ // http://jqueryvalidation.org/remote-method/
1177
+ remote: function( value, element, param ) {
1178
+ if ( this.optional(element) ) {
1179
+ return "dependency-mismatch";
1180
+ }
1181
+
1182
+ var previous = this.previousValue(element),
1183
+ validator, data;
1184
+
1185
+ if (!this.settings.messages[element.name] ) {
1186
+ this.settings.messages[element.name] = {};
1187
+ }
1188
+ previous.originalMessage = this.settings.messages[element.name].remote;
1189
+ this.settings.messages[element.name].remote = previous.message;
1190
+
1191
+ param = typeof param === "string" && { url: param } || param;
1192
+
1193
+ if ( previous.old === value ) {
1194
+ return previous.valid;
1195
+ }
1196
+
1197
+ previous.old = value;
1198
+ validator = this;
1199
+ this.startRequest(element);
1200
+ data = {};
1201
+ data[element.name] = value;
1202
+ $.ajax($.extend(true, {
1203
+ url: param,
1204
+ mode: "abort",
1205
+ port: "validate" + element.name,
1206
+ dataType: "json",
1207
+ data: data,
1208
+ context: validator.currentForm,
1209
+ success: function( response ) {
1210
+ var valid = response === true || response === "true",
1211
+ errors, message, submitted;
1212
+
1213
+ validator.settings.messages[element.name].remote = previous.originalMessage;
1214
+ if ( valid ) {
1215
+ submitted = validator.formSubmitted;
1216
+ validator.prepareElement(element);
1217
+ validator.formSubmitted = submitted;
1218
+ validator.successList.push(element);
1219
+ delete validator.invalid[element.name];
1220
+ validator.showErrors();
1221
+ } else {
1222
+ errors = {};
1223
+ message = response || validator.defaultMessage( element, "remote" );
1224
+ errors[element.name] = previous.message = $.isFunction(message) ? message(value) : message;
1225
+ validator.invalid[element.name] = true;
1226
+ validator.showErrors(errors);
1227
+ }
1228
+ previous.valid = valid;
1229
+ validator.stopRequest(element, valid);
1230
+ }
1231
+ }, param));
1232
+ return "pending";
1233
+ }
1234
+
1235
+ }
1236
+
1237
+ });
1238
+
1239
+ $.format = function deprecated() {
1240
+ throw "$.format has been deprecated. Please use $.validator.format instead.";
1241
+ };
1242
+
1243
+ }(jQuery));
1244
+
1245
+ // ajax mode: abort
1246
+ // usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
1247
+ // if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()
1248
+ (function($) {
1249
+ var pendingRequests = {},
1250
+ ajax;
1251
+ // Use a prefilter if available (1.5+)
1252
+ if ( $.ajaxPrefilter ) {
1253
+ $.ajaxPrefilter(function( settings, _, xhr ) {
1254
+ var port = settings.port;
1255
+ if ( settings.mode === "abort" ) {
1256
+ if ( pendingRequests[port] ) {
1257
+ pendingRequests[port].abort();
1258
+ }
1259
+ pendingRequests[port] = xhr;
1260
+ }
1261
+ });
1262
+ } else {
1263
+ // Proxy ajax
1264
+ ajax = $.ajax;
1265
+ $.ajax = function( settings ) {
1266
+ var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode,
1267
+ port = ( "port" in settings ? settings : $.ajaxSettings ).port;
1268
+ if ( mode === "abort" ) {
1269
+ if ( pendingRequests[port] ) {
1270
+ pendingRequests[port].abort();
1271
+ }
1272
+ pendingRequests[port] = ajax.apply(this, arguments);
1273
+ return pendingRequests[port];
1274
+ }
1275
+ return ajax.apply(this, arguments);
1276
+ };
1277
+ }
1278
+ }(jQuery));
1279
+
1280
+ // provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation
1281
+ // handler is only called when $(event.target).is(delegate), in the scope of the jquery-object for event.target
1282
+ (function($) {
1283
+ $.extend($.fn, {
1284
+ validateDelegate: function( delegate, type, handler ) {
1285
+ return this.bind(type, function( event ) {
1286
+ var target = $(event.target);
1287
+ if ( target.is(delegate) ) {
1288
+ return handler.apply(target, arguments);
1289
+ }
1290
+ });
1291
+ }
1292
+ });
1293
+ }(jQuery));