freelancer_rails 1.0.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 30cf934fd4cf908c724a3941856b99eb34187500
4
+ data.tar.gz: 8650a66acad625c93ce48ee2e1cf9e4265140415
5
+ SHA512:
6
+ metadata.gz: 91608edba3ae30999f032de9340b26037047cffef4f0619611953395e48d3febb4200277b26c0eadd3813f9f6dab2a91907e4704d382d9b7c3ea61527fd880af
7
+ data.tar.gz: 8e5e7199234fc2f16ec9dbc41b6bc5881f05aa07effe013e651340c13a7c63f1f237ca21c4701e42558178fb6d57204bfd31ac4ebbb8b84da3a88b8a3138acde
data/MIT-LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Freelancer Theme
2
+ --------------
3
+ Copyright (c) 2013-2016 Blackrock Digital LLC.
4
+
5
+ Freelancer Rails Adapted for Ruby Rails
6
+ --------------------------
7
+ Creative Rails Copyright (c) 2016 Michael Price
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining
10
+ a copy of this software and associated documentation files (the
11
+ "Software"), to deal in the Software without restriction, including
12
+ without limitation the rights to use, copy, modify, merge, publish,
13
+ distribute, sublicense, and/or sell copies of the Software, and to
14
+ permit persons to whom the Software is furnished to do so, subject to
15
+ the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be
18
+ included in all copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # Freelancer Rails
2
+ Start Bootstrap Freelancer theme adapted for Ruby on Rails.
3
+
4
+ ## Dependencies
5
+
6
+ This is has been tested on the following:
7
+
8
+ * Ruby 2.3.3
9
+ * Rails 5.0.1
10
+
11
+ Other dependencies:
12
+
13
+ * [jquery-rails](https://github.com/rails/jquery-rails) - Packaged with Rails.
14
+ * [Bootstrap Sass](https://github.com/twbs/bootstrap-sass)
15
+ * [Font-Awesome Rails](https://github.com/bokmann/font-awesome-rails)
16
+
17
+ ## Usage
18
+ How to use my plugin.
19
+
20
+ ## Installation
21
+ Add this line to your application's Gemfile:
22
+
23
+ ```ruby
24
+ # PrettyDocs Rails
25
+ gem 'freelancer_rails', '~> 1.0', '>= 1.0.1'
26
+ ```
27
+
28
+ ## Usage
29
+ How to use this plugin.
30
+
31
+ ### Sass
32
+
33
+ Change your `application.css` to `application.scss`.
34
+
35
+ ```console
36
+ $ mv app/assets/stylesheets/application.css app/assets/stylesheets/application.scss
37
+ ```
38
+
39
+ Then, remove all the `*= require_self` and `*= require_tree .` statements from the sass file. Instead, use `@import` to import Sass files.
40
+
41
+ Do not use `*= require` in Sass or your other stylesheets will not be [able to access][antirequire] the Bootstrap mixins or variables.
42
+
43
+ Using [SCSS](http://sass-lang.com/documentation/file.SASS_REFERENCE.html), add this to your
44
+ `application.scss` file:
45
+
46
+ ```scss
47
+ @import "freelancer_rails";
48
+ ```
49
+
50
+ ### JavaScript
51
+
52
+ Add the following line to your `application.js` file:
53
+
54
+ ```js
55
+ //= require freelancer_rails
56
+ ```
57
+
58
+ ## Demo
59
+
60
+ A working demo of the application is located in `test/dummy`. Once you have installed all gems, run the following.
61
+
62
+ ```bash
63
+ $ cd test/dummy
64
+ $ rake db:create
65
+ $ rake db:migrate
66
+ $ rails c
67
+ ```
68
+
69
+ You can download and view Freelancer [here](https://startbootstrap.com/template-overviews/freelancer/).
70
+
71
+ NOTE: `contact_mail.php` is not included and therefore the contact form will not work as shown in the original demo of the application. Adapt the mail form as needed for a rails application.
72
+
73
+ ## Contributing
74
+ * [Michael Price](http://twitter.com/michaeljprice)
75
+
76
+ ## License
77
+ [Freelancer](https://startbootstrap.com/template-overviews/freelancer/) is a one page [Bootstrap](http://getbootstrap.com/) portfolio theme for freelancers created by [Start Bootstrap](http://startbootstrap.com/).
78
+
79
+ Freelancer Rails was adapted by [Michael Price](http://twitter.com/michaeljprice) for
80
+ Ruby on Rails.
81
+
82
+ The original theme and this gem are available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'FreelancerRails'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+
21
+
22
+ require 'bundler/gem_tasks'
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'lib'
28
+ t.libs << 'test'
29
+ t.pattern = 'test/**/*_test.rb'
30
+ t.verbose = false
31
+ end
32
+
33
+
34
+ task default: :test
data/VERSIONS.md ADDED
@@ -0,0 +1,9 @@
1
+ # Bundled Versions
2
+
3
+ | Gem | Freelancer | Font Awesome Rails | Bootstrap-Sass |
4
+ |--------|------------|--------------------|------------------|
5
+ | 1.0.0 | 3.3.7+1 | ~> 4.7, >= 4.7.0.1 | ~> 3.3, >= 3.3.7 |
6
+
7
+ | Gem | jQuery Easing | jqBootstrapValidation |
8
+ |--------|---------------|-----------------------|
9
+ | 1.0.0 | 1.3 | 1.3.6
@@ -0,0 +1,5 @@
1
+ //= require jquery.easing
2
+ //= require bootstrap-sprockets
3
+ //= require jqBootstrapValidation
4
+ //= require contact_me
5
+ //= require freelancer
@@ -0,0 +1,5 @@
1
+ // "bootstrap-sprockets" must be imported before "bootstrap" and "bootstrap/variables"
2
+ @import "bootstrap-sprockets";
3
+ @import "bootstrap";
4
+ @import "font-awesome";
5
+ @import "freelancer";
@@ -0,0 +1,7 @@
1
+ require 'bootstrap-sass'
2
+ require 'font-awesome-rails'
3
+ require 'freelancer_rails/engine'
4
+ require 'freelancer_rails/version'
5
+
6
+ module FreelancerRails
7
+ end
@@ -0,0 +1,13 @@
1
+ module FreelancerRails
2
+ class Engine < ::Rails::Engine
3
+ initializer 'freelancer_rails.assets.precompile' do |app|
4
+
5
+ %w(stylesheets javascripts fonts images).each do |s|
6
+ app.config.assets.paths << root.join('app', 'assets', s).to_s
7
+ app.config.assets.paths << root.join('vendor', 'assets', s).to_s
8
+ end
9
+
10
+ app.config.assets.precompile += %w( *.png portfolio/*.png )
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module FreelancerRails
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :freelancer_rails do
3
+ # # Task goes here
4
+ # end
Binary file
@@ -0,0 +1,73 @@
1
+ $(function() {
2
+
3
+ $("#contactForm input,#contactForm textarea").jqBootstrapValidation({
4
+ preventSubmit: true,
5
+ submitError: function($form, event, errors) {
6
+ // additional error messages or events
7
+ },
8
+ submitSuccess: function($form, event) {
9
+ // Prevent spam click and default submit behaviour
10
+ $("#btnSubmit").attr("disabled", true);
11
+ event.preventDefault();
12
+
13
+ // get values from FORM
14
+ var name = $("input#name").val();
15
+ var email = $("input#email").val();
16
+ var phone = $("input#phone").val();
17
+ var message = $("textarea#message").val();
18
+ var firstName = name; // For Success/Failure Message
19
+ // Check for white space in name for Success/Fail message
20
+ if (firstName.indexOf(' ') >= 0) {
21
+ firstName = name.split(' ').slice(0, -1).join(' ');
22
+ }
23
+ $.ajax({
24
+ url: "", // removed ././mail/contact_me.php
25
+ type: "POST",
26
+ data: {
27
+ name: name,
28
+ phone: phone,
29
+ email: email,
30
+ message: message
31
+ },
32
+ cache: false,
33
+ success: function() {
34
+ // Enable button & show success message
35
+ $("#btnSubmit").attr("disabled", false);
36
+ $('#success').html("<div class='alert alert-success'>");
37
+ $('#success > .alert-success').html("<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>&times;")
38
+ .append("</button>");
39
+ $('#success > .alert-success')
40
+ .append("<strong>Your message has been sent. </strong>");
41
+ $('#success > .alert-success')
42
+ .append('</div>');
43
+
44
+ //clear all fields
45
+ $('#contactForm').trigger("reset");
46
+ },
47
+ error: function() {
48
+ // Fail message
49
+ $('#success').html("<div class='alert alert-danger'>");
50
+ $('#success > .alert-danger').html("<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>&times;")
51
+ .append("</button>");
52
+ $('#success > .alert-danger').append("<strong>Sorry " + firstName + ", it seems that my mail server is not responding. Please try again later!");
53
+ $('#success > .alert-danger').append('</div>');
54
+ //clear all fields
55
+ $('#contactForm').trigger("reset");
56
+ },
57
+ });
58
+ },
59
+ filter: function() {
60
+ return $(this).is(":visible");
61
+ },
62
+ });
63
+
64
+ $("a[data-toggle=\"tab\"]").click(function(e) {
65
+ e.preventDefault();
66
+ $(this).tab("show");
67
+ });
68
+ });
69
+
70
+ // When clicking on Full hide fail/success boxes
71
+ $('#name').focus(function() {
72
+ $('#success').html('');
73
+ });
@@ -0,0 +1,44 @@
1
+ // Freelancer Theme JavaScript
2
+
3
+ $(function() {
4
+ "use strict"; // Start of use strict
5
+
6
+ // jQuery for page scrolling feature - requires jQuery Easing plugin
7
+ $('.page-scroll a').bind('click', function(event) {
8
+ var $anchor = $(this);
9
+ $('html, body').stop().animate({
10
+ scrollTop: ($($anchor.attr('href')).offset().top - 50)
11
+ }, 1250, 'easeInOutExpo');
12
+ event.preventDefault();
13
+ });
14
+
15
+ // Highlight the top nav as scrolling occurs
16
+ $('body').scrollspy({
17
+ target: '.navbar-fixed-top',
18
+ offset: 51
19
+ });
20
+
21
+ // Closes the Responsive Menu on Menu Item Click
22
+ $('.navbar-collapse ul li a').click(function(){
23
+ $('.navbar-toggle:visible').click();
24
+ });
25
+
26
+ // Offset for Main Navigation
27
+ $('#mainNav').affix({
28
+ offset: {
29
+ top: 100
30
+ }
31
+ })
32
+
33
+ // Floating label headings for the contact form
34
+ $(function() {
35
+ $("body").on("input propertychange", ".floating-label-form-group", function(e) {
36
+ $(this).toggleClass("floating-label-form-group-with-value", !!$(e.target).val());
37
+ }).on("focus", ".floating-label-form-group", function() {
38
+ $(this).addClass("floating-label-form-group-with-focus");
39
+ }).on("blur", ".floating-label-form-group", function() {
40
+ $(this).removeClass("floating-label-form-group-with-focus");
41
+ });
42
+ });
43
+
44
+ }); // End of use strict
@@ -0,0 +1,912 @@
1
+ /* jqBootstrapValidation
2
+ * A plugin for automating validation on Twitter Bootstrap formatted forms.
3
+ *
4
+ * v1.3.6
5
+ *
6
+ * License: MIT <http://opensource.org/licenses/mit-license.php> - see LICENSE file
7
+ *
8
+ * http://ReactiveRaven.github.com/jqBootstrapValidation/
9
+ */
10
+
11
+ $(function(){
12
+
13
+ var createdElements = [];
14
+
15
+ var defaults = {
16
+ options: {
17
+ prependExistingHelpBlock: false,
18
+ sniffHtml: true, // sniff for 'required', 'maxlength', etc
19
+ preventSubmit: true, // stop the form submit event from firing if validation fails
20
+ submitError: false, // function called if there is an error when trying to submit
21
+ submitSuccess: false, // function called just before a successful submit event is sent to the server
22
+ semanticallyStrict: false, // set to true to tidy up generated HTML output
23
+ autoAdd: {
24
+ helpBlocks: true
25
+ },
26
+ filter: function () {
27
+ // return $(this).is(":visible"); // only validate elements you can see
28
+ return true; // validate everything
29
+ }
30
+ },
31
+ methods: {
32
+ init : function( options ) {
33
+
34
+ var settings = $.extend(true, {}, defaults);
35
+
36
+ settings.options = $.extend(true, settings.options, options);
37
+
38
+ var $siblingElements = this;
39
+
40
+ var uniqueForms = $.unique(
41
+ $siblingElements.map( function () {
42
+ return $(this).parents("form")[0];
43
+ }).toArray()
44
+ );
45
+
46
+ $(uniqueForms).bind("submit", function (e) {
47
+ var $form = $(this);
48
+ var warningsFound = 0;
49
+ var $inputs = $form.find("input,textarea,select").not("[type=submit],[type=image]").filter(settings.options.filter);
50
+ $inputs.trigger("submit.validation").trigger("validationLostFocus.validation");
51
+
52
+ $inputs.each(function (i, el) {
53
+ var $this = $(el),
54
+ $controlGroup = $this.parents(".control-group").first();
55
+ if (
56
+ $controlGroup.hasClass("warning")
57
+ ) {
58
+ $controlGroup.removeClass("warning").addClass("error");
59
+ warningsFound++;
60
+ }
61
+ });
62
+
63
+ $inputs.trigger("validationLostFocus.validation");
64
+
65
+ if (warningsFound) {
66
+ if (settings.options.preventSubmit) {
67
+ e.preventDefault();
68
+ }
69
+ $form.addClass("error");
70
+ if ($.isFunction(settings.options.submitError)) {
71
+ settings.options.submitError($form, e, $inputs.jqBootstrapValidation("collectErrors", true));
72
+ }
73
+ } else {
74
+ $form.removeClass("error");
75
+ if ($.isFunction(settings.options.submitSuccess)) {
76
+ settings.options.submitSuccess($form, e);
77
+ }
78
+ }
79
+ });
80
+
81
+ return this.each(function(){
82
+
83
+ // Get references to everything we're interested in
84
+ var $this = $(this),
85
+ $controlGroup = $this.parents(".control-group").first(),
86
+ $helpBlock = $controlGroup.find(".help-block").first(),
87
+ $form = $this.parents("form").first(),
88
+ validatorNames = [];
89
+
90
+ // create message container if not exists
91
+ if (!$helpBlock.length && settings.options.autoAdd && settings.options.autoAdd.helpBlocks) {
92
+ $helpBlock = $('<div class="help-block" />');
93
+ $controlGroup.find('.controls').append($helpBlock);
94
+ createdElements.push($helpBlock[0]);
95
+ }
96
+
97
+ // =============================================================
98
+ // SNIFF HTML FOR VALIDATORS
99
+ // =============================================================
100
+
101
+ // *snort sniff snuffle*
102
+
103
+ if (settings.options.sniffHtml) {
104
+ var message = "";
105
+ // ---------------------------------------------------------
106
+ // PATTERN
107
+ // ---------------------------------------------------------
108
+ if ($this.attr("pattern") !== undefined) {
109
+ message = "Not in the expected format<!-- data-validation-pattern-message to override -->";
110
+ if ($this.data("validationPatternMessage")) {
111
+ message = $this.data("validationPatternMessage");
112
+ }
113
+ $this.data("validationPatternMessage", message);
114
+ $this.data("validationPatternRegex", $this.attr("pattern"));
115
+ }
116
+ // ---------------------------------------------------------
117
+ // MAX
118
+ // ---------------------------------------------------------
119
+ if ($this.attr("max") !== undefined || $this.attr("aria-valuemax") !== undefined) {
120
+ var max = ($this.attr("max") !== undefined ? $this.attr("max") : $this.attr("aria-valuemax"));
121
+ message = "Too high: Maximum of '" + max + "'<!-- data-validation-max-message to override -->";
122
+ if ($this.data("validationMaxMessage")) {
123
+ message = $this.data("validationMaxMessage");
124
+ }
125
+ $this.data("validationMaxMessage", message);
126
+ $this.data("validationMaxMax", max);
127
+ }
128
+ // ---------------------------------------------------------
129
+ // MIN
130
+ // ---------------------------------------------------------
131
+ if ($this.attr("min") !== undefined || $this.attr("aria-valuemin") !== undefined) {
132
+ var min = ($this.attr("min") !== undefined ? $this.attr("min") : $this.attr("aria-valuemin"));
133
+ message = "Too low: Minimum of '" + min + "'<!-- data-validation-min-message to override -->";
134
+ if ($this.data("validationMinMessage")) {
135
+ message = $this.data("validationMinMessage");
136
+ }
137
+ $this.data("validationMinMessage", message);
138
+ $this.data("validationMinMin", min);
139
+ }
140
+ // ---------------------------------------------------------
141
+ // MAXLENGTH
142
+ // ---------------------------------------------------------
143
+ if ($this.attr("maxlength") !== undefined) {
144
+ message = "Too long: Maximum of '" + $this.attr("maxlength") + "' characters<!-- data-validation-maxlength-message to override -->";
145
+ if ($this.data("validationMaxlengthMessage")) {
146
+ message = $this.data("validationMaxlengthMessage");
147
+ }
148
+ $this.data("validationMaxlengthMessage", message);
149
+ $this.data("validationMaxlengthMaxlength", $this.attr("maxlength"));
150
+ }
151
+ // ---------------------------------------------------------
152
+ // MINLENGTH
153
+ // ---------------------------------------------------------
154
+ if ($this.attr("minlength") !== undefined) {
155
+ message = "Too short: Minimum of '" + $this.attr("minlength") + "' characters<!-- data-validation-minlength-message to override -->";
156
+ if ($this.data("validationMinlengthMessage")) {
157
+ message = $this.data("validationMinlengthMessage");
158
+ }
159
+ $this.data("validationMinlengthMessage", message);
160
+ $this.data("validationMinlengthMinlength", $this.attr("minlength"));
161
+ }
162
+ // ---------------------------------------------------------
163
+ // REQUIRED
164
+ // ---------------------------------------------------------
165
+ if ($this.attr("required") !== undefined || $this.attr("aria-required") !== undefined) {
166
+ message = settings.builtInValidators.required.message;
167
+ if ($this.data("validationRequiredMessage")) {
168
+ message = $this.data("validationRequiredMessage");
169
+ }
170
+ $this.data("validationRequiredMessage", message);
171
+ }
172
+ // ---------------------------------------------------------
173
+ // NUMBER
174
+ // ---------------------------------------------------------
175
+ if ($this.attr("type") !== undefined && $this.attr("type").toLowerCase() === "number") {
176
+ message = settings.builtInValidators.number.message;
177
+ if ($this.data("validationNumberMessage")) {
178
+ message = $this.data("validationNumberMessage");
179
+ }
180
+ $this.data("validationNumberMessage", message);
181
+ }
182
+ // ---------------------------------------------------------
183
+ // EMAIL
184
+ // ---------------------------------------------------------
185
+ if ($this.attr("type") !== undefined && $this.attr("type").toLowerCase() === "email") {
186
+ message = "Not a valid email address<!-- data-validator-validemail-message to override -->";
187
+ if ($this.data("validationValidemailMessage")) {
188
+ message = $this.data("validationValidemailMessage");
189
+ } else if ($this.data("validationEmailMessage")) {
190
+ message = $this.data("validationEmailMessage");
191
+ }
192
+ $this.data("validationValidemailMessage", message);
193
+ }
194
+ // ---------------------------------------------------------
195
+ // MINCHECKED
196
+ // ---------------------------------------------------------
197
+ if ($this.attr("minchecked") !== undefined) {
198
+ message = "Not enough options checked; Minimum of '" + $this.attr("minchecked") + "' required<!-- data-validation-minchecked-message to override -->";
199
+ if ($this.data("validationMincheckedMessage")) {
200
+ message = $this.data("validationMincheckedMessage");
201
+ }
202
+ $this.data("validationMincheckedMessage", message);
203
+ $this.data("validationMincheckedMinchecked", $this.attr("minchecked"));
204
+ }
205
+ // ---------------------------------------------------------
206
+ // MAXCHECKED
207
+ // ---------------------------------------------------------
208
+ if ($this.attr("maxchecked") !== undefined) {
209
+ message = "Too many options checked; Maximum of '" + $this.attr("maxchecked") + "' required<!-- data-validation-maxchecked-message to override -->";
210
+ if ($this.data("validationMaxcheckedMessage")) {
211
+ message = $this.data("validationMaxcheckedMessage");
212
+ }
213
+ $this.data("validationMaxcheckedMessage", message);
214
+ $this.data("validationMaxcheckedMaxchecked", $this.attr("maxchecked"));
215
+ }
216
+ }
217
+
218
+ // =============================================================
219
+ // COLLECT VALIDATOR NAMES
220
+ // =============================================================
221
+
222
+ // Get named validators
223
+ if ($this.data("validation") !== undefined) {
224
+ validatorNames = $this.data("validation").split(",");
225
+ }
226
+
227
+ // Get extra ones defined on the element's data attributes
228
+ $.each($this.data(), function (i, el) {
229
+ var parts = i.replace(/([A-Z])/g, ",$1").split(",");
230
+ if (parts[0] === "validation" && parts[1]) {
231
+ validatorNames.push(parts[1]);
232
+ }
233
+ });
234
+
235
+ // =============================================================
236
+ // NORMALISE VALIDATOR NAMES
237
+ // =============================================================
238
+
239
+ var validatorNamesToInspect = validatorNames;
240
+ var newValidatorNamesToInspect = [];
241
+
242
+ do // repeatedly expand 'shortcut' validators into their real validators
243
+ {
244
+ // Uppercase only the first letter of each name
245
+ $.each(validatorNames, function (i, el) {
246
+ validatorNames[i] = formatValidatorName(el);
247
+ });
248
+
249
+ // Remove duplicate validator names
250
+ validatorNames = $.unique(validatorNames);
251
+
252
+ // Pull out the new validator names from each shortcut
253
+ newValidatorNamesToInspect = [];
254
+ $.each(validatorNamesToInspect, function(i, el) {
255
+ if ($this.data("validation" + el + "Shortcut") !== undefined) {
256
+ // Are these custom validators?
257
+ // Pull them out!
258
+ $.each($this.data("validation" + el + "Shortcut").split(","), function(i2, el2) {
259
+ newValidatorNamesToInspect.push(el2);
260
+ });
261
+ } else if (settings.builtInValidators[el.toLowerCase()]) {
262
+ // Is this a recognised built-in?
263
+ // Pull it out!
264
+ var validator = settings.builtInValidators[el.toLowerCase()];
265
+ if (validator.type.toLowerCase() === "shortcut") {
266
+ $.each(validator.shortcut.split(","), function (i, el) {
267
+ el = formatValidatorName(el);
268
+ newValidatorNamesToInspect.push(el);
269
+ validatorNames.push(el);
270
+ });
271
+ }
272
+ }
273
+ });
274
+
275
+ validatorNamesToInspect = newValidatorNamesToInspect;
276
+
277
+ } while (validatorNamesToInspect.length > 0)
278
+
279
+ // =============================================================
280
+ // SET UP VALIDATOR ARRAYS
281
+ // =============================================================
282
+
283
+ var validators = {};
284
+
285
+ $.each(validatorNames, function (i, el) {
286
+ // Set up the 'override' message
287
+ var message = $this.data("validation" + el + "Message");
288
+ var hasOverrideMessage = (message !== undefined);
289
+ var foundValidator = false;
290
+ message =
291
+ (
292
+ message
293
+ ? message
294
+ : "'" + el + "' validation failed <!-- Add attribute 'data-validation-" + el.toLowerCase() + "-message' to input to change this message -->"
295
+ )
296
+ ;
297
+
298
+ $.each(
299
+ settings.validatorTypes,
300
+ function (validatorType, validatorTemplate) {
301
+ if (validators[validatorType] === undefined) {
302
+ validators[validatorType] = [];
303
+ }
304
+ if (!foundValidator && $this.data("validation" + el + formatValidatorName(validatorTemplate.name)) !== undefined) {
305
+ validators[validatorType].push(
306
+ $.extend(
307
+ true,
308
+ {
309
+ name: formatValidatorName(validatorTemplate.name),
310
+ message: message
311
+ },
312
+ validatorTemplate.init($this, el)
313
+ )
314
+ );
315
+ foundValidator = true;
316
+ }
317
+ }
318
+ );
319
+
320
+ if (!foundValidator && settings.builtInValidators[el.toLowerCase()]) {
321
+
322
+ var validator = $.extend(true, {}, settings.builtInValidators[el.toLowerCase()]);
323
+ if (hasOverrideMessage) {
324
+ validator.message = message;
325
+ }
326
+ var validatorType = validator.type.toLowerCase();
327
+
328
+ if (validatorType === "shortcut") {
329
+ foundValidator = true;
330
+ } else {
331
+ $.each(
332
+ settings.validatorTypes,
333
+ function (validatorTemplateType, validatorTemplate) {
334
+ if (validators[validatorTemplateType] === undefined) {
335
+ validators[validatorTemplateType] = [];
336
+ }
337
+ if (!foundValidator && validatorType === validatorTemplateType.toLowerCase()) {
338
+ $this.data("validation" + el + formatValidatorName(validatorTemplate.name), validator[validatorTemplate.name.toLowerCase()]);
339
+ validators[validatorType].push(
340
+ $.extend(
341
+ validator,
342
+ validatorTemplate.init($this, el)
343
+ )
344
+ );
345
+ foundValidator = true;
346
+ }
347
+ }
348
+ );
349
+ }
350
+ }
351
+
352
+ if (! foundValidator) {
353
+ $.error("Cannot find validation info for '" + el + "'");
354
+ }
355
+ });
356
+
357
+ // =============================================================
358
+ // STORE FALLBACK VALUES
359
+ // =============================================================
360
+
361
+ $helpBlock.data(
362
+ "original-contents",
363
+ (
364
+ $helpBlock.data("original-contents")
365
+ ? $helpBlock.data("original-contents")
366
+ : $helpBlock.html()
367
+ )
368
+ );
369
+
370
+ $helpBlock.data(
371
+ "original-role",
372
+ (
373
+ $helpBlock.data("original-role")
374
+ ? $helpBlock.data("original-role")
375
+ : $helpBlock.attr("role")
376
+ )
377
+ );
378
+
379
+ $controlGroup.data(
380
+ "original-classes",
381
+ (
382
+ $controlGroup.data("original-clases")
383
+ ? $controlGroup.data("original-classes")
384
+ : $controlGroup.attr("class")
385
+ )
386
+ );
387
+
388
+ $this.data(
389
+ "original-aria-invalid",
390
+ (
391
+ $this.data("original-aria-invalid")
392
+ ? $this.data("original-aria-invalid")
393
+ : $this.attr("aria-invalid")
394
+ )
395
+ );
396
+
397
+ // =============================================================
398
+ // VALIDATION
399
+ // =============================================================
400
+
401
+ $this.bind(
402
+ "validation.validation",
403
+ function (event, params) {
404
+
405
+ var value = getValue($this);
406
+
407
+ // Get a list of the errors to apply
408
+ var errorsFound = [];
409
+
410
+ $.each(validators, function (validatorType, validatorTypeArray) {
411
+ if (value || value.length || (params && params.includeEmpty) || (!!settings.validatorTypes[validatorType].blockSubmit && params && !!params.submitting)) {
412
+ $.each(validatorTypeArray, function (i, validator) {
413
+ if (settings.validatorTypes[validatorType].validate($this, value, validator)) {
414
+ errorsFound.push(validator.message);
415
+ }
416
+ });
417
+ }
418
+ });
419
+
420
+ return errorsFound;
421
+ }
422
+ );
423
+
424
+ $this.bind(
425
+ "getValidators.validation",
426
+ function () {
427
+ return validators;
428
+ }
429
+ );
430
+
431
+ // =============================================================
432
+ // WATCH FOR CHANGES
433
+ // =============================================================
434
+ $this.bind(
435
+ "submit.validation",
436
+ function () {
437
+ return $this.triggerHandler("change.validation", {submitting: true});
438
+ }
439
+ );
440
+ $this.bind(
441
+ [
442
+ "keyup",
443
+ "focus",
444
+ "blur",
445
+ "click",
446
+ "keydown",
447
+ "keypress",
448
+ "change"
449
+ ].join(".validation ") + ".validation",
450
+ function (e, params) {
451
+
452
+ var value = getValue($this);
453
+
454
+ var errorsFound = [];
455
+
456
+ $controlGroup.find("input,textarea,select").each(function (i, el) {
457
+ var oldCount = errorsFound.length;
458
+ $.each($(el).triggerHandler("validation.validation", params), function (j, message) {
459
+ errorsFound.push(message);
460
+ });
461
+ if (errorsFound.length > oldCount) {
462
+ $(el).attr("aria-invalid", "true");
463
+ } else {
464
+ var original = $this.data("original-aria-invalid");
465
+ $(el).attr("aria-invalid", (original !== undefined ? original : false));
466
+ }
467
+ });
468
+
469
+ $form.find("input,select,textarea").not($this).not("[name=\"" + $this.attr("name") + "\"]").trigger("validationLostFocus.validation");
470
+
471
+ errorsFound = $.unique(errorsFound.sort());
472
+
473
+ // Were there any errors?
474
+ if (errorsFound.length) {
475
+ // Better flag it up as a warning.
476
+ $controlGroup.removeClass("success error").addClass("warning");
477
+
478
+ // How many errors did we find?
479
+ if (settings.options.semanticallyStrict && errorsFound.length === 1) {
480
+ // Only one? Being strict? Just output it.
481
+ $helpBlock.html(errorsFound[0] +
482
+ ( settings.options.prependExistingHelpBlock ? $helpBlock.data("original-contents") : "" ));
483
+ } else {
484
+ // Multiple? Being sloppy? Glue them together into an UL.
485
+ $helpBlock.html("<ul role=\"alert\"><li>" + errorsFound.join("</li><li>") + "</li></ul>" +
486
+ ( settings.options.prependExistingHelpBlock ? $helpBlock.data("original-contents") : "" ));
487
+ }
488
+ } else {
489
+ $controlGroup.removeClass("warning error success");
490
+ if (value.length > 0) {
491
+ $controlGroup.addClass("success");
492
+ }
493
+ $helpBlock.html($helpBlock.data("original-contents"));
494
+ }
495
+
496
+ if (e.type === "blur") {
497
+ $controlGroup.removeClass("success");
498
+ }
499
+ }
500
+ );
501
+ $this.bind("validationLostFocus.validation", function () {
502
+ $controlGroup.removeClass("success");
503
+ });
504
+ });
505
+ },
506
+ destroy : function( ) {
507
+
508
+ return this.each(
509
+ function() {
510
+
511
+ var
512
+ $this = $(this),
513
+ $controlGroup = $this.parents(".control-group").first(),
514
+ $helpBlock = $controlGroup.find(".help-block").first();
515
+
516
+ // remove our events
517
+ $this.unbind('.validation'); // events are namespaced.
518
+ // reset help text
519
+ $helpBlock.html($helpBlock.data("original-contents"));
520
+ // reset classes
521
+ $controlGroup.attr("class", $controlGroup.data("original-classes"));
522
+ // reset aria
523
+ $this.attr("aria-invalid", $this.data("original-aria-invalid"));
524
+ // reset role
525
+ $helpBlock.attr("role", $this.data("original-role"));
526
+ // remove all elements we created
527
+ if (createdElements.indexOf($helpBlock[0]) > -1) {
528
+ $helpBlock.remove();
529
+ }
530
+
531
+ }
532
+ );
533
+
534
+ },
535
+ collectErrors : function(includeEmpty) {
536
+
537
+ var errorMessages = {};
538
+ this.each(function (i, el) {
539
+ var $el = $(el);
540
+ var name = $el.attr("name");
541
+ var errors = $el.triggerHandler("validation.validation", {includeEmpty: true});
542
+ errorMessages[name] = $.extend(true, errors, errorMessages[name]);
543
+ });
544
+
545
+ $.each(errorMessages, function (i, el) {
546
+ if (el.length === 0) {
547
+ delete errorMessages[i];
548
+ }
549
+ });
550
+
551
+ return errorMessages;
552
+
553
+ },
554
+ hasErrors: function() {
555
+
556
+ var errorMessages = [];
557
+
558
+ this.each(function (i, el) {
559
+ errorMessages = errorMessages.concat(
560
+ $(el).triggerHandler("getValidators.validation") ? $(el).triggerHandler("validation.validation", {submitting: true}) : []
561
+ );
562
+ });
563
+
564
+ return (errorMessages.length > 0);
565
+ },
566
+ override : function (newDefaults) {
567
+ defaults = $.extend(true, defaults, newDefaults);
568
+ }
569
+ },
570
+ validatorTypes: {
571
+ callback: {
572
+ name: "callback",
573
+ init: function ($this, name) {
574
+ return {
575
+ validatorName: name,
576
+ callback: $this.data("validation" + name + "Callback"),
577
+ lastValue: $this.val(),
578
+ lastValid: true,
579
+ lastFinished: true
580
+ };
581
+ },
582
+ validate: function ($this, value, validator) {
583
+ if (validator.lastValue === value && validator.lastFinished) {
584
+ return !validator.lastValid;
585
+ }
586
+
587
+ if (validator.lastFinished === true)
588
+ {
589
+ validator.lastValue = value;
590
+ validator.lastValid = true;
591
+ validator.lastFinished = false;
592
+
593
+ var rrjqbvValidator = validator;
594
+ var rrjqbvThis = $this;
595
+ executeFunctionByName(
596
+ validator.callback,
597
+ window,
598
+ $this,
599
+ value,
600
+ function (data) {
601
+ if (rrjqbvValidator.lastValue === data.value) {
602
+ rrjqbvValidator.lastValid = data.valid;
603
+ if (data.message) {
604
+ rrjqbvValidator.message = data.message;
605
+ }
606
+ rrjqbvValidator.lastFinished = true;
607
+ rrjqbvThis.data("validation" + rrjqbvValidator.validatorName + "Message", rrjqbvValidator.message);
608
+ // Timeout is set to avoid problems with the events being considered 'already fired'
609
+ setTimeout(function () {
610
+ rrjqbvThis.trigger("change.validation");
611
+ }, 1); // doesn't need a long timeout, just long enough for the event bubble to burst
612
+ }
613
+ }
614
+ );
615
+ }
616
+
617
+ return false;
618
+
619
+ }
620
+ },
621
+ ajax: {
622
+ name: "ajax",
623
+ init: function ($this, name) {
624
+ return {
625
+ validatorName: name,
626
+ url: $this.data("validation" + name + "Ajax"),
627
+ lastValue: $this.val(),
628
+ lastValid: true,
629
+ lastFinished: true
630
+ };
631
+ },
632
+ validate: function ($this, value, validator) {
633
+ if (""+validator.lastValue === ""+value && validator.lastFinished === true) {
634
+ return validator.lastValid === false;
635
+ }
636
+
637
+ if (validator.lastFinished === true)
638
+ {
639
+ validator.lastValue = value;
640
+ validator.lastValid = true;
641
+ validator.lastFinished = false;
642
+ $.ajax({
643
+ url: validator.url,
644
+ data: "value=" + value + "&field=" + $this.attr("name"),
645
+ dataType: "json",
646
+ success: function (data) {
647
+ if (""+validator.lastValue === ""+data.value) {
648
+ validator.lastValid = !!(data.valid);
649
+ if (data.message) {
650
+ validator.message = data.message;
651
+ }
652
+ validator.lastFinished = true;
653
+ $this.data("validation" + validator.validatorName + "Message", validator.message);
654
+ // Timeout is set to avoid problems with the events being considered 'already fired'
655
+ setTimeout(function () {
656
+ $this.trigger("change.validation");
657
+ }, 1); // doesn't need a long timeout, just long enough for the event bubble to burst
658
+ }
659
+ },
660
+ failure: function () {
661
+ validator.lastValid = true;
662
+ validator.message = "ajax call failed";
663
+ validator.lastFinished = true;
664
+ $this.data("validation" + validator.validatorName + "Message", validator.message);
665
+ // Timeout is set to avoid problems with the events being considered 'already fired'
666
+ setTimeout(function () {
667
+ $this.trigger("change.validation");
668
+ }, 1); // doesn't need a long timeout, just long enough for the event bubble to burst
669
+ }
670
+ });
671
+ }
672
+
673
+ return false;
674
+
675
+ }
676
+ },
677
+ regex: {
678
+ name: "regex",
679
+ init: function ($this, name) {
680
+ return {regex: regexFromString($this.data("validation" + name + "Regex"))};
681
+ },
682
+ validate: function ($this, value, validator) {
683
+ return (!validator.regex.test(value) && ! validator.negative)
684
+ || (validator.regex.test(value) && validator.negative);
685
+ }
686
+ },
687
+ required: {
688
+ name: "required",
689
+ init: function ($this, name) {
690
+ return {};
691
+ },
692
+ validate: function ($this, value, validator) {
693
+ return !!(value.length === 0 && ! validator.negative)
694
+ || !!(value.length > 0 && validator.negative);
695
+ },
696
+ blockSubmit: true
697
+ },
698
+ match: {
699
+ name: "match",
700
+ init: function ($this, name) {
701
+ var element = $this.parents("form").first().find("[name=\"" + $this.data("validation" + name + "Match") + "\"]").first();
702
+ element.bind("validation.validation", function () {
703
+ $this.trigger("change.validation", {submitting: true});
704
+ });
705
+ return {"element": element};
706
+ },
707
+ validate: function ($this, value, validator) {
708
+ return (value !== validator.element.val() && ! validator.negative)
709
+ || (value === validator.element.val() && validator.negative);
710
+ },
711
+ blockSubmit: true
712
+ },
713
+ max: {
714
+ name: "max",
715
+ init: function ($this, name) {
716
+ return {max: $this.data("validation" + name + "Max")};
717
+ },
718
+ validate: function ($this, value, validator) {
719
+ return (parseFloat(value, 10) > parseFloat(validator.max, 10) && ! validator.negative)
720
+ || (parseFloat(value, 10) <= parseFloat(validator.max, 10) && validator.negative);
721
+ }
722
+ },
723
+ min: {
724
+ name: "min",
725
+ init: function ($this, name) {
726
+ return {min: $this.data("validation" + name + "Min")};
727
+ },
728
+ validate: function ($this, value, validator) {
729
+ return (parseFloat(value) < parseFloat(validator.min) && ! validator.negative)
730
+ || (parseFloat(value) >= parseFloat(validator.min) && validator.negative);
731
+ }
732
+ },
733
+ maxlength: {
734
+ name: "maxlength",
735
+ init: function ($this, name) {
736
+ return {maxlength: $this.data("validation" + name + "Maxlength")};
737
+ },
738
+ validate: function ($this, value, validator) {
739
+ return ((value.length > validator.maxlength) && ! validator.negative)
740
+ || ((value.length <= validator.maxlength) && validator.negative);
741
+ }
742
+ },
743
+ minlength: {
744
+ name: "minlength",
745
+ init: function ($this, name) {
746
+ return {minlength: $this.data("validation" + name + "Minlength")};
747
+ },
748
+ validate: function ($this, value, validator) {
749
+ return ((value.length < validator.minlength) && ! validator.negative)
750
+ || ((value.length >= validator.minlength) && validator.negative);
751
+ }
752
+ },
753
+ maxchecked: {
754
+ name: "maxchecked",
755
+ init: function ($this, name) {
756
+ var elements = $this.parents("form").first().find("[name=\"" + $this.attr("name") + "\"]");
757
+ elements.bind("click.validation", function () {
758
+ $this.trigger("change.validation", {includeEmpty: true});
759
+ });
760
+ return {maxchecked: $this.data("validation" + name + "Maxchecked"), elements: elements};
761
+ },
762
+ validate: function ($this, value, validator) {
763
+ return (validator.elements.filter(":checked").length > validator.maxchecked && ! validator.negative)
764
+ || (validator.elements.filter(":checked").length <= validator.maxchecked && validator.negative);
765
+ },
766
+ blockSubmit: true
767
+ },
768
+ minchecked: {
769
+ name: "minchecked",
770
+ init: function ($this, name) {
771
+ var elements = $this.parents("form").first().find("[name=\"" + $this.attr("name") + "\"]");
772
+ elements.bind("click.validation", function () {
773
+ $this.trigger("change.validation", {includeEmpty: true});
774
+ });
775
+ return {minchecked: $this.data("validation" + name + "Minchecked"), elements: elements};
776
+ },
777
+ validate: function ($this, value, validator) {
778
+ return (validator.elements.filter(":checked").length < validator.minchecked && ! validator.negative)
779
+ || (validator.elements.filter(":checked").length >= validator.minchecked && validator.negative);
780
+ },
781
+ blockSubmit: true
782
+ }
783
+ },
784
+ builtInValidators: {
785
+ email: {
786
+ name: "Email",
787
+ type: "shortcut",
788
+ shortcut: "validemail"
789
+ },
790
+ validemail: {
791
+ name: "Validemail",
792
+ type: "regex",
793
+ regex: "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\\.[A-Za-z]{2,4}",
794
+ message: "Not a valid email address<!-- data-validator-validemail-message to override -->"
795
+ },
796
+ passwordagain: {
797
+ name: "Passwordagain",
798
+ type: "match",
799
+ match: "password",
800
+ message: "Does not match the given password<!-- data-validator-paswordagain-message to override -->"
801
+ },
802
+ positive: {
803
+ name: "Positive",
804
+ type: "shortcut",
805
+ shortcut: "number,positivenumber"
806
+ },
807
+ negative: {
808
+ name: "Negative",
809
+ type: "shortcut",
810
+ shortcut: "number,negativenumber"
811
+ },
812
+ number: {
813
+ name: "Number",
814
+ type: "regex",
815
+ regex: "([+-]?\\\d+(\\\.\\\d*)?([eE][+-]?[0-9]+)?)?",
816
+ message: "Must be a number<!-- data-validator-number-message to override -->"
817
+ },
818
+ integer: {
819
+ name: "Integer",
820
+ type: "regex",
821
+ regex: "[+-]?\\\d+",
822
+ message: "No decimal places allowed<!-- data-validator-integer-message to override -->"
823
+ },
824
+ positivenumber: {
825
+ name: "Positivenumber",
826
+ type: "min",
827
+ min: 0,
828
+ message: "Must be a positive number<!-- data-validator-positivenumber-message to override -->"
829
+ },
830
+ negativenumber: {
831
+ name: "Negativenumber",
832
+ type: "max",
833
+ max: 0,
834
+ message: "Must be a negative number<!-- data-validator-negativenumber-message to override -->"
835
+ },
836
+ required: {
837
+ name: "Required",
838
+ type: "required",
839
+ message: "This is required<!-- data-validator-required-message to override -->"
840
+ },
841
+ checkone: {
842
+ name: "Checkone",
843
+ type: "minchecked",
844
+ minchecked: 1,
845
+ message: "Check at least one option<!-- data-validation-checkone-message to override -->"
846
+ }
847
+ }
848
+ };
849
+
850
+ var formatValidatorName = function (name) {
851
+ return name
852
+ .toLowerCase()
853
+ .replace(
854
+ /(^|\s)([a-z])/g ,
855
+ function(m,p1,p2) {
856
+ return p1+p2.toUpperCase();
857
+ }
858
+ )
859
+ ;
860
+ };
861
+
862
+ var getValue = function ($this) {
863
+ // Extract the value we're talking about
864
+ var value = $this.val();
865
+ var type = $this.attr("type");
866
+ if (type === "checkbox") {
867
+ value = ($this.is(":checked") ? value : "");
868
+ }
869
+ if (type === "radio") {
870
+ value = ($('input[name="' + $this.attr("name") + '"]:checked').length > 0 ? value : "");
871
+ }
872
+ return value;
873
+ };
874
+
875
+ function regexFromString(inputstring) {
876
+ return new RegExp("^" + inputstring + "$");
877
+ }
878
+
879
+ /**
880
+ * Thanks to Jason Bunting via StackOverflow.com
881
+ *
882
+ * http://stackoverflow.com/questions/359788/how-to-execute-a-javascript-function-when-i-have-its-name-as-a-string#answer-359910
883
+ * Short link: http://tinyurl.com/executeFunctionByName
884
+ **/
885
+ function executeFunctionByName(functionName, context /*, args*/) {
886
+ var args = Array.prototype.slice.call(arguments).splice(2);
887
+ var namespaces = functionName.split(".");
888
+ var func = namespaces.pop();
889
+ for(var i = 0; i < namespaces.length; i++) {
890
+ context = context[namespaces[i]];
891
+ }
892
+ return context[func].apply(this, args);
893
+ }
894
+
895
+ $.fn.jqBootstrapValidation = function( method ) {
896
+
897
+ if ( defaults.methods[method] ) {
898
+ return defaults.methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
899
+ } else if ( typeof method === 'object' || ! method ) {
900
+ return defaults.methods.init.apply( this, arguments );
901
+ } else {
902
+ $.error( 'Method ' + method + ' does not exist on jQuery.jqBootstrapValidation' );
903
+ return null;
904
+ }
905
+
906
+ };
907
+
908
+ $.jqBootstrapValidation = function (options) {
909
+ $(":input").not("[type=image],[type=submit]").jqBootstrapValidation.apply(this,arguments);
910
+ };
911
+
912
+ });