draiodoir 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,74 @@
1
+ Draíodóir
2
+ =========
3
+
4
+ A javascript that turns web-pages into Wizards.
5
+
6
+
7
+ Usage
8
+ -----
9
+
10
+ - Include (in a `<script>` tag) on your *HTML5* page
11
+ - Make the form a Wizard, with `new Wizard("my_form");`
12
+
13
+ Example
14
+ -------
15
+
16
+ <!DOCTYPE html>
17
+ <html>
18
+ <head>
19
+ <meta content-type='UTF-8'>
20
+ <script type='text/javascript' src='wizard.js'></src>
21
+ <script>
22
+ addEventListener('load', function(e) {
23
+ new Wizard('my_form');
24
+ }, false);
25
+ </script>
26
+ <title>Draíodóir Test</title>
27
+ </head>
28
+ <body>
29
+ <form id='my_form' action='javascript:alert("Submitting form");'>
30
+ <fieldset>
31
+ <label for='name'>Name</label>
32
+ <input id='name' type='text' required>
33
+ </fieldset>
34
+ <fieldset>
35
+ <label for='age'>Age</label>
36
+ <input id='age' type='number' minimum='18' maximum='80' required>
37
+ </fieldset>
38
+ <input type='submit' value='Go'>
39
+ </form>
40
+ </body>
41
+ </html>
42
+
43
+ Caveats
44
+ -------
45
+
46
+ *Draíodóir* relies on [validation][validation] being fully implemented by
47
+ the browser. This is almost never the case (as of this writing), so you'll
48
+ probably need to include another library which performs validation properly,
49
+ such as [Bailitheoir][bailitheoir].
50
+
51
+ The library is targeted, therefore, at the [more][firefox] [modern][chrome]
52
+ [browsers][safari], so don't expect it to work out of the box in [those][ie]
53
+ that are, err, less [standards][w3c] compliant.
54
+
55
+ Author(s)
56
+ ---------
57
+
58
+ - JJ Buckley <jj@bjjb.org>
59
+
60
+ Copyright
61
+ ---------
62
+
63
+ This software is released under the [GPL][gpl], so feel free to steal and
64
+ destroy - just comply with the terms of that license. I accept no
65
+ responsibilty for anything.
66
+
67
+ [validation]: http://www.w3.org/TR/html5/association-of-controls-and-forms.html#constraints
68
+ [bailitheoir]: http://jjbuckley.github.com/bailitheoir
69
+ [firefox]: http://www.mozilla.com/firefox
70
+ [chrome]: http://www.google.com/chrome
71
+ [safari]: http://www.apple.com/safari
72
+ [ie]: http://www.microsoft.com/ie
73
+ [w3c]: http://www.w3.org
74
+ [gpl]: http://www.gnu.org/licenses/gpl.html
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "draiodoir/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "draiodoir"
7
+ s.version = Draiodoir::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["JJ Buckley"]
10
+ s.email = ["jj@bjjb.org"]
11
+ s.homepage = "http://jjbuckley.github.com/draiodoir"
12
+ s.summary = %q{A JavaScript multi-step form helper}
13
+ s.description = %q{Draíodóir lets you take a regular HTML5 page, and turn it
14
+ into a multi-step wizard.}
15
+
16
+ s.rubyforge_project = "draiodoir"
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+ end
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Example account signup form.
3
+ */
4
+ body {
5
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
6
+ min-width: 620px;
7
+ }
8
+ form {
9
+ display: block;
10
+ width: 500px;
11
+ padding: 50px;
12
+ margin: auto;
13
+
14
+ background-color: #ccddaa;
15
+ background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(255,255,235,0.9)), to(rgba(20,20,20,0.0)));
16
+ background-image: -moz-linear-gradient(90deg, rgba(255,255,235,0.9), rgba(20,20,0,0.0));
17
+
18
+ border: 5px solid #889966;
19
+ -webkit-border-radius: 10px;
20
+ -moz-border-radius: 10px;
21
+ -o-border-radius: 1em;
22
+ border-radius: 10px;
23
+ -webkit-box-shadow: 0 0 5px #112200;
24
+ -moz-box-shadow: 0 0 0.5em #112200;
25
+ -o-box-shadow: 0 0 5px #112200;
26
+ box-shadow: 0 0 5px #112200;
27
+ }
28
+
29
+ label {
30
+ float: left;
31
+ height: 2em;
32
+ line-height: 2em;
33
+ width: 150px;
34
+ text-align: right;
35
+ }
36
+
37
+ input,select {
38
+ margin-left: 170px;
39
+ display: block;
40
+ clear: right;
41
+ padding: 5px;
42
+ font-weight: bold;
43
+ }
44
+
45
+ input:invalid,select:invalid {
46
+ color: red;
47
+ -webkit-box-shadow: inset 1px 1px 7px red;
48
+ -moz-box-shadow: inset 0 0 7px red;
49
+ box-shadow: inset 0 0 7px red;
50
+ }
51
+
52
+ fieldset {
53
+ border: none;
54
+ margin: 0;
55
+ padding: 0;
56
+ position: relative;
57
+ display: block;
58
+ }
59
+ fieldset[hidden] {
60
+ display: none;
61
+ }
62
+ legend {
63
+ display: block;
64
+ padding: 5px;
65
+ border: 5px solid #889966;
66
+ border: 5px solid #889966;
67
+ -webkit-border-radius: 10px;
68
+ -moz-border-radius: 10px;
69
+ -o-border-radius: 1em;
70
+ border-radius: 10px;
71
+ position: absolute;
72
+ font-size: 30px;
73
+ font-weight: bold;
74
+ color: #334400;
75
+ text-shadow: 0px 0px 4px #999999;
76
+ top: -77px;
77
+ background-color: #ccddaa;
78
+ }
@@ -0,0 +1,76 @@
1
+ <html>
2
+ <head>
3
+ <title>New Account</title>
4
+ <script type="text/javascript" src="validator.js"></script>
5
+ <script type="text/javascript" src="../draiodoir.js"></script>
6
+ <script type="text/javascript">
7
+ addEventListener('load', function(event) {
8
+ var form = document.getElementById('new_account');
9
+ new Validator(form);
10
+ new Wizard(form, {
11
+ labels: {
12
+ next: {
13
+ personal: "That's me...",
14
+ tastes: "That all I'm telling you."
15
+ }
16
+ }
17
+ });
18
+ }, false);
19
+ </script>
20
+ <link rel="stylesheet" type="text/css" href="account_wizard.css">
21
+ </head>
22
+ <body>
23
+ <h1>New Account</h1>
24
+ <form id="new_account" name="account" method="POST"
25
+ action="javascript:alert('Submitting form!');"
26
+ enctype="application/x-www-form-urlencoded">
27
+
28
+ <!-- The fields here are required, so the submit button does nothing
29
+ (except retrigger validation) until validation passes. -->
30
+ <fieldset name="personal">
31
+ <legend>Personal Details</legend>
32
+
33
+ <label for="account_name">Name</label>
34
+ <input type="text" id="account_name" name="account[name]"
35
+ placeholder="Your full name" required="required" />
36
+ <br />
37
+ <label for="account_dob">Date of Birth</label>
38
+ <input type="date" id="account_dob" name="account[dob]"
39
+ placeholder="YYYY-mm-dd" required="required"
40
+ min="1900-01-01" max="1990-12-12" />
41
+ </fieldset>
42
+
43
+ <!-- Everything in this fieldset is optional. It will be shown, but the
44
+ submit button should be enabled already. -->
45
+ <fieldset name="tastes">
46
+ <legend>Some additional info</legend>
47
+ <label for="account_favourite_film">Your favourite film</label>
48
+ <select id="account_favourite_film" name="account[favourite_film]">
49
+ <option value="">Pick one...</option>
50
+ <option value="1">Fight Club</option>
51
+ <option value="2">True Romance</option>
52
+ </select>
53
+
54
+ <label for="account_favourite_music">Your favourite music</label>
55
+ <select id="account_favourite_music" name="account[favourite_music]">
56
+ <option value="">Select from...</option>
57
+ <option value="1">Skip James</option>
58
+ <option value="2">Pond</option>
59
+ <option value="3">Antonio Vivaldi</option>
60
+ </select>
61
+ </fieldset>
62
+
63
+ <!-- This is the last step, so it should trigger the submit! -->
64
+ <fieldset name='plan'>
65
+ <legend>Select your plan</legend>
66
+ <select id="account_plan" name="account[plan]" required='required'>
67
+ <option value="">Please Choose...</option>
68
+ <option value="1">Bronze</option>
69
+ <option value="2">Silver</option>
70
+ <option value="3">Gold</option>
71
+ </select>
72
+ </fieldset>
73
+ <input type='submit' value="Continue" />
74
+ </form>
75
+ </body>
76
+ </html>
@@ -0,0 +1,92 @@
1
+ /**
2
+ * A basic implementation of the W3's HTML5 constraints.
3
+ * (http://www.w3.org/TR/html5/association-of-controls-and-forms.html#constraints)
4
+ *
5
+ * Usage
6
+ * -----
7
+ *
8
+ * new Validator(form);
9
+ * form.addEventListener('invalid', function() {
10
+ * alert("Detected an invalid field!");
11
+ * }, false);
12
+ *
13
+ * Copyright
14
+ * ---------
15
+ *
16
+ * Copyright (c) 2010 JJ Buckley (jj@bjjb.org)
17
+ *
18
+ * This program is free software: you can redistribute it and/or modify
19
+ * it under the terms of the GNU General Public License as published by
20
+ * the Free Software Foundation, either version 3 of the License, or
21
+ * (at your option) any later version.
22
+ *
23
+ * This program is distributed in the hope that it will be useful,
24
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
25
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26
+ * GNU General Public License for more details.
27
+ *
28
+ * You should have received a copy of the GNU General Public License
29
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
30
+ */
31
+ function Validator(form) {
32
+ var fields = [];
33
+ function Validator(field) {
34
+ function Validity() {
35
+ this.valueMissing = false;
36
+ this.typeMismatch = false;
37
+ this.patternMismatch = false;
38
+ this.tooLong = false;
39
+ this.rangeUnderflow = false;
40
+ this.rangeOverflow = false;
41
+ this.stepMismatch = false;
42
+ this.customError = false;
43
+ this.valid = true;
44
+ };
45
+ field.willValidate = !!(~['INPUT', 'SELECT', 'TEXTAREA'].indexOf(field.tagName));
46
+ field.setCustomValidity = function(message) {
47
+ if (message == "") {
48
+ field.validity.customError = false;
49
+ }
50
+ else {
51
+ field.validity.customError = true;
52
+ field.validationMessage = message;
53
+ }
54
+ };
55
+ field.checkValidity = function() {
56
+ if (this.willValidate) {
57
+ this.validity.valueMissing = (this.hasAttribute('required') && this.value.trim() == "");
58
+ this.validity.typeMismatch = ((this.type == 'email' && this.value && !this.value.match(/.+@.+\..+/)) ||
59
+ (this.type == 'number' && this.value && !this.value.match(/^\d+/)) ||
60
+ (this.type == 'date' && false && this.value && !this.value.match(/\d{4}-\d{2}-\d{2}/)) ||
61
+ (this.type == 'url' && this.value && !this.value.match(/.+\..+/)));
62
+ this.validity.tooLong = (this.hasAttribute('maxlength') && this.value > this.getAttribute('maxlength'));
63
+ this.validity.rangeUnderflow = (this.hasAttribute('min') && this.value < this.getAttribute('min'));
64
+ this.validity.rangeOverflow = (this.type == 'number' && this.hasAttribute('max') && this.value < this.getAttribute('max'));
65
+ this.validity.stepMismatch = false; // TODO
66
+ this.valid = !(this.validity.valueMissing ||
67
+ this.validity.typeMismatch ||
68
+ this.validity.tooLong ||
69
+ this.validity.rangeUnderflow ||
70
+ this.validity.rangeOverflow ||
71
+ this.validity.stepMismatch ||
72
+ this.validity.customError);
73
+ if (!this.valid) {
74
+ var event = document.createEvent('Events');
75
+ event.initEvent('invalid', true, true);
76
+ field.dispatchEvent(event);
77
+ return false;
78
+ }
79
+ }
80
+ return true;
81
+ };
82
+ field.validity = new Validity();
83
+ console.debug("Added a validator (%o) on %o", this, field);
84
+ }
85
+ ['input', 'select', 'textarea'].forEach(function(tag) {
86
+ var elements = form.getElementsByTagName(tag);
87
+ for (var i = 0; i < elements.length; i++) {
88
+ new Validator(elements[i]);
89
+ }
90
+ });
91
+ }
92
+
@@ -0,0 +1,2 @@
1
+ module Draiodoir
2
+ end
@@ -0,0 +1,3 @@
1
+ module Draiodoir
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,252 @@
1
+ /**
2
+ * Draíodóir
3
+ * =========
4
+ *
5
+ * Turns a HTML5 <form> into a wizard.
6
+ *
7
+ * The form *must* be marked up in a particular way, but it's pretty standard
8
+ * HTML. Basically, each fieldset is a step in the form, and (by default) the
9
+ * submit button is used as the control for moving forward. If the elements in
10
+ * a fieldset don't validate (either on a modern browser, or using a compatible
11
+ * JavaScript validation library, such as
12
+ * bailitheoir(http://jjbuckley.github.com/bailitheoir), then the Wizard won't
13
+ * allow you to progress.
14
+ *
15
+ * To hide all steps except the current, *Draíodóir* sets the "hidden"
16
+ * attribute on the fieldset. Since most browsers can't handle this properly
17
+ * yet, you need to either add a CSS rule such as
18
+ *
19
+ * fieldset[hidden] { display: none; }
20
+ *
21
+ * or listen for the `wizard:step:show` and `wizard:step:hide` events, and
22
+ * take appropriate action, such as setting a style attribute, on the event's
23
+ * target.
24
+ *
25
+ * Usage
26
+ * -----
27
+ *
28
+ * form = document.getElementById('my_form');
29
+ * new Wizard(form);
30
+ *
31
+ * A new Wizard immediately hides all steps except the first one.
32
+ *
33
+ * You can pass in an options object as a second argumens (all optional):
34
+ *
35
+ * Options
36
+ * -------
37
+ *
38
+ * next:: Identifies the control used to move forwards in the form. The
39
+ * default is the first "submit".
40
+ * previous:: Identifies the control to be used to move backwards in the form.
41
+ * If a suitable element can't be found, it is created (as a
42
+ * <button>), and given the value "Back" (though you can override
43
+ * this with a label for any or all steps). An auto-generated
44
+ * previous control is inserted immediately before the "next"
45
+ * control.
46
+ * labels:: Used to display text (or HTML) in certain elements at various
47
+ * steps. Valid values are "next" and "previous", which indicate the
48
+ * next and previous controls. Each value can either be a string
49
+ * (which is applied to the control for every step), or an object
50
+ * containing keys for the step names. For example:
51
+ *
52
+ * new Wizard(form, {
53
+ * previous: 'back_button',
54
+ * labels: {
55
+ * next: {
56
+ * step1: "To Step 2", step2: "Step 3, please", step3: "Go!",
57
+ * },
58
+ * previous: "Back"
59
+ * }
60
+ * });
61
+ *
62
+ * The wizard has 3 steps, fieldsets with names "step1", "step2" and
63
+ * step3. On step1, the next button says "To Step 2", and on step2,
64
+ * it says "Step 3, please". On the last step, it says "Go!". If this
65
+ * were left blank, it would revert to the original value of the
66
+ * submit field.
67
+ * On all steps (besides the first), the 'previous' control says
68
+ * "Back". It's hidden and disabled on the first step.
69
+ *
70
+ * Hook into the wizard by listening for the events it generates, or by using
71
+ * its "next()", "previous()", "first()" and "last()" methods.
72
+ *
73
+ * Caveats
74
+ * -------
75
+ *
76
+ * *Draíodóir* has been tested on Google Chrome 6.0.472.63, Mozilla Firefox
77
+ * 3.6.10, and Sarari on iPhone OS v4.1. I hardly expect it to work in any
78
+ * current version of Microsoft Explorer, but that will hopefully change.
79
+ * For more information, see the *Draíodóir* homepage at
80
+ * http://jjbuckley.github.com/draiodoir. And feel free to fork the project,
81
+ * and improve it!
82
+ *
83
+ * Events
84
+ * ------
85
+ *
86
+ * These Events are fired by a Wizard:
87
+ *
88
+ * wizard:load:: the wizard is set up
89
+ * wizard:next:: the next step is being shown
90
+ * wizard:previous:: the previous step is being shown
91
+ * wizard:first:: the first step is being shown
92
+ * wizard:last:: the last step is being shown
93
+ * wizard:invalid:: validation failed on the current step
94
+ * wizard:valid:: validation passed on all steps
95
+ * wizard:submit:: the form is being submitted
96
+ * wizard:step:show:: a step is being shown
97
+ * wizard:step:hide:: a step is being hidden
98
+ *
99
+ * Copyright
100
+ * ---------
101
+ *
102
+ * Copyright (c) 2010 JJ Buckley (jj@bjjb.org)
103
+ *
104
+ * This program is free software: you can redistribute it and/or modify
105
+ * it under the terms of the GNU General Public License as published by
106
+ * the Free Software Foundation, either version 3 of the License, or
107
+ * (at your option) any later version.
108
+ *
109
+ * This program is distributed in the hope that it will be useful,
110
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
111
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
112
+ * GNU General Public License for more details.
113
+ *
114
+ * You should have received a copy of the GNU General Public License
115
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
116
+ */
117
+ function Wizard(form, options) {
118
+ if (typeof(form) == "string") {
119
+ form = document.getElementById(form);
120
+ }
121
+
122
+ var self = this;
123
+ form.addEventListener('submit', function(event) {
124
+ if (!self.submit()) {
125
+ event.preventDefault();
126
+ return false;
127
+ }
128
+ }, false);
129
+
130
+ form.addEventListener('wizard:step:hide', function(event) {
131
+ // A step has been hidden.
132
+ }, false);
133
+
134
+ form.addEventListener('wizard:step:show', function(event) {
135
+ for (var i = 0; i < steps.length; i++) {
136
+ if (i !== index) {
137
+ steps[i].hide();
138
+ }
139
+ }
140
+ }, false);
141
+
142
+ function getSubmit() {
143
+ elements = form.getElementsByTagName('input');
144
+ for (var i = 0; i < elements.length; i++) {
145
+ if (elements[i].type == 'submit') {
146
+ return elements[i];
147
+ }
148
+ }
149
+ }
150
+
151
+ function Step(fieldset, options) {
152
+ this.name = fieldset.getAttribute('name');
153
+ this.toString = function() {
154
+ return "Step:" + this.name;
155
+ };
156
+ var fields = [];
157
+ ['input', 'select', 'textarea'].forEach(function(tag) {
158
+ elements = fieldset.getElementsByTagName(tag);
159
+ for (var i = 0; i < elements.length; i++) {
160
+ elements[i].addEventListener('invalid', function(event) {
161
+ console.debug("%o is invalid! (%o)", this, this.validity);
162
+ fieldset.valid = false;
163
+ }, false);
164
+ fields.push(elements[i]);
165
+ }
166
+ });
167
+ this.hide = function() {
168
+ fieldset.setAttribute('hidden', 'hidden');
169
+ var event = document.createEvent('Events');
170
+ event.initEvent('wizard:step:hide', true, true);
171
+ event.step = this;
172
+ return fieldset.dispatchEvent(event);
173
+ };
174
+ this.show = function() {
175
+ fieldset.removeAttribute('hidden');
176
+ var event = document.createEvent('Events');
177
+ event.initEvent('wizard:step:show', true, true);
178
+ event.step = this;
179
+ return fieldset.dispatchEvent(event);
180
+ };
181
+ this.validate = function() {
182
+ return fields.every(function(field) {
183
+ return field.checkValidity();
184
+ });
185
+ };
186
+ }
187
+
188
+ var steps = [];
189
+ var index = 0;
190
+ var submit = getSubmit();
191
+ var submit_text = submit.value;
192
+
193
+ var fieldsets = form.getElementsByTagName('FIELDSET');
194
+ for (var i = 0; i < fieldsets.length; i++) {
195
+ var step = new Step(fieldsets[i]);
196
+ steps.push(step);
197
+ }
198
+
199
+ function showStep(index) {
200
+ // TODO - set the label
201
+ if (steps[index]) {
202
+ steps[index].show();
203
+ }
204
+ };
205
+ showStep(0);
206
+
207
+ this.next = function() {
208
+ if (steps[index]) {
209
+ if (steps[index].validate()) {
210
+ var event = document.createEvent('Events');
211
+ event.initEvent('wizard:next', true, true);
212
+ event.step = steps[index];
213
+ event.index = index;
214
+ event.wizard = this;
215
+ form.dispatchEvent(event);
216
+ showStep(++index);
217
+ }
218
+ else {
219
+ var event = document.createEvent('Events');
220
+ event.initEvent('wizard:complete', true, true);
221
+ event.step = steps[index];
222
+ event.index = index;
223
+ event.wizard = this;
224
+ form.dispatchEvent(event);
225
+ }
226
+ }
227
+ else {
228
+ var event = document.createEvent('Events');
229
+ event.initEvent('wizard:invalid', true, true);
230
+ event.step = steps[index];
231
+ event.index = index;
232
+ event.wizard = this;
233
+ form.dispatchEvent(event);
234
+ return false;
235
+ }
236
+ };
237
+
238
+ this.submit = function() {
239
+ this.next();
240
+ if (index == steps.length) {
241
+ var event = document.createEvent('Events');
242
+ event.initEvent('wizard:submit', true, true);
243
+ event.wizard = this;
244
+ event.index = index;
245
+ event.step = steps[index];
246
+ form.dispatchEvent(event);
247
+ return true;
248
+ }
249
+ return false;
250
+ };
251
+ };
252
+