├── .editorconfig ├── .gitignore ├── bower.json ├── examples ├── index.html └── styles.css ├── external └── jquery │ ├── form │ └── jquery.form-3.50.js │ ├── jquery-2.1.1.min.js │ ├── ui │ ├── jquery-ui-1.10.4.custom.min.js │ └── smoothness │ │ ├── images │ │ ├── animated-overlay.gif │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png │ │ ├── ui-bg_flat_75_ffffff_40x100.png │ │ ├── ui-bg_glass_55_fbf9ee_1x400.png │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ ├── ui-bg_glass_75_dadada_1x400.png │ │ ├── ui-bg_glass_75_e6e6e6_1x400.png │ │ ├── ui-bg_glass_95_fef1ec_1x400.png │ │ ├── ui-bg_highlight-soft_75_cccccc_1x100.png │ │ ├── ui-icons_222222_256x240.png │ │ ├── ui-icons_2e83ff_256x240.png │ │ ├── ui-icons_454545_256x240.png │ │ ├── ui-icons_888888_256x240.png │ │ └── ui-icons_cd0a0a_256x240.png │ │ └── jquery-ui-1.10.4.custom.min.css │ └── validate │ └── jquery.validate-1.12.0.min.js ├── license-gpl ├── license-mit ├── package.json ├── readme.md ├── src └── jquery.wizard.js └── tests ├── index.html ├── qunit ├── qunit.css └── qunit.js └── unit ├── testsuite.js └── wizard ├── wizard_core.js ├── wizard_defaults.js ├── wizard_events.js ├── wizard_methods.js └── wizard_options.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = tab 8 | insert_final_newline = false 9 | max_line_length = 120 10 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .* 3 | !.editorconfig 4 | !.gitignore 5 | !.jshintignore 6 | !.jshintrc 7 | !.nvmrc 8 | !.travis.yml 9 | *.log 10 | *.sublime* 11 | *.swp 12 | 13 | bower_components/ 14 | node_modules/ -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-wizard", 3 | "main": [ 4 | "src/jquery.wizard.js" 5 | ], 6 | "dependencies": { 7 | "jquery": ">=1.6.0", 8 | "jquery-ui": ">=1.9.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jQuery JavaScript Form Wizard | jQuery.wizard 5 | 6 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 121 | 122 | 123 |

jQuery.wizard

124 | 125 |
126 |
127 |

Basic Wizard With Validation

128 | 129 |
130 |
131 | 132 | 133 |
134 | 135 |
136 |

What is your gender?

137 | Male
138 | Female
139 |
140 | 141 |
142 |

You are a named .

143 |
144 | 145 | 152 |
153 |
154 | 155 |
156 |

Basic Wizard With Progress Bar

157 | 158 |
159 |

Progress

160 |
161 |
162 | 163 |
164 |
165 |

This basic wizard has a progress bar attached to it.

166 |
167 | 168 |
169 |

Note your progress in the progress bar above.

170 |
171 | 172 |
173 |

You have reached the end of this wizard. Yippee!

174 |
175 | 176 | 182 |
183 |
184 | 185 |
186 |

Branching Wizard

187 | 188 |
189 |
190 |

This is a branching wizard. You will see different steps depending on your answers.

191 |
192 | 193 |
194 |

Which color do you like best?

195 | 196 | 197 |
198 | 199 |
200 |
201 |

Pink, it was love at first sight

202 |
203 |
204 | 205 |
206 |
207 |

I'm blue da ba dee da ba die...

208 |
209 |
210 | 211 |
212 |

FIN.

213 |
214 | 215 | 221 |
222 |
223 | 224 |
225 |

Nested Branches

226 | 227 |
228 |
229 |

Start

230 |
231 | 232 |
233 |
Branch Step
234 | 235 |
236 |
Nested Branch Step
237 |
238 |
239 | 240 |
241 |

Finish

242 |
243 | 244 | 250 |
251 |
252 | 253 |
254 |

Clearing Input Values Before Navigating

255 | 256 |
257 |
258 |

259 | 260 | A
261 | B
262 |
263 | 264 |
265 | Click "backward" to change your answers. 266 |
267 | 268 | 274 |
275 |
276 | 277 |
278 |

Create Steps Dynamically

279 | 280 |
281 |
282 |
283 |

How many steps would you like to create?

284 | 290 |
291 |
292 | 293 | 299 |
300 |
301 |
302 | 303 | 304 | -------------------------------------------------------------------------------- /examples/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #eee; 3 | font-family: "Arial", "Helvetica", "Verdana", "sans-serif"; 4 | font-size: 12px; 5 | margin: 0; 6 | padding: 0; 7 | } 8 | 9 | h1 { 10 | background-color: #333; 11 | color: #fff; 12 | font-size: 2.0em; 13 | margin: 0; 14 | padding: 10px; 15 | } 16 | 17 | h1 a { 18 | color: #fff; 19 | text-decoration: none; 20 | } 21 | 22 | h1 a:hover { 23 | color: #eee; 24 | text-decoration: underline; 25 | } 26 | 27 | h3 span { 28 | font-weight: normal; 29 | } 30 | 31 | #wrapper { 32 | width: 800px; 33 | } 34 | 35 | #navigation { 36 | background-color: #fff; 37 | border: #ddd 1px solid; 38 | border-radius: 10px; 39 | margin: 10px; 40 | padding: 10px; 41 | } 42 | 43 | #navigation li { 44 | margin: 2px 0; 45 | } 46 | 47 | label.error { 48 | color: #ff0000; 49 | margin-left: 10px; 50 | position: relative; 51 | } 52 | 53 | #progressbar { 54 | margin: 10px 0; 55 | width: 400px; 56 | height: 15px; 57 | } 58 | 59 | .wizard { 60 | background-color: #fff; 61 | border: #ddd 1px solid; 62 | border-radius: 10px; 63 | margin: 10px; 64 | padding: 10px; 65 | } 66 | 67 | .wizard .wizard-header { 68 | background-color: #f4f4f4; 69 | border-bottom: #ddd 1px solid; 70 | border-top-left-radius: 10px; 71 | border-top-right-radius: 10px; 72 | padding: 5px 10px; 73 | margin: 0 0 10px 0; 74 | } 75 | 76 | .wizard .wizard-step { 77 | margin: 10px 0; 78 | } 79 | 80 | .wizard .wizard-step p { 81 | padding: 5px; 82 | } 83 | 84 | .navigation { 85 | border-top: #ddd 1px solid; 86 | margin-top: 10px; 87 | padding-top: 10px; 88 | } 89 | 90 | .navigation ul { 91 | margin: 0; 92 | padding: 0; 93 | list-style: none; 94 | } 95 | 96 | .navigation li { 97 | float: left; 98 | margin-right: 10px; 99 | } 100 | 101 | .pad { 102 | padding: 5px; 103 | } 104 | 105 | .blue { 106 | background: blue; 107 | color: white; 108 | } 109 | 110 | .pink { 111 | background: pink; 112 | color: black; 113 | } 114 | 115 | .clearfix:before, .clearfix:after { 116 | content: "\0020"; 117 | display: block; 118 | height: 0; 119 | visibility: hidden; 120 | } 121 | 122 | .clearfix:after { 123 | clear: both; 124 | } 125 | -------------------------------------------------------------------------------- /external/jquery/form/jquery.form-3.50.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Form Plugin 3 | * version: 3.50.0-2014.02.05 4 | * Requires jQuery v1.5 or later 5 | * Copyright (c) 2013 M. Alsup 6 | * Examples and documentation at: http://malsup.com/jquery/form/ 7 | * Project repository: https://github.com/malsup/form 8 | * Dual licensed under the MIT and GPL licenses. 9 | * https://github.com/malsup/form#copyright-and-license 10 | */ 11 | /*global ActiveXObject */ 12 | 13 | // AMD support 14 | (function (factory) { 15 | "use strict"; 16 | if (typeof define === 'function' && define.amd) { 17 | // using AMD; register as anon module 18 | define(['jquery'], factory); 19 | } else { 20 | // no AMD; invoke directly 21 | factory( (typeof(jQuery) != 'undefined') ? jQuery : window.Zepto ); 22 | } 23 | } 24 | 25 | (function($) { 26 | "use strict"; 27 | 28 | /* 29 | Usage Note: 30 | ----------- 31 | Do not use both ajaxSubmit and ajaxForm on the same form. These 32 | functions are mutually exclusive. Use ajaxSubmit if you want 33 | to bind your own submit handler to the form. For example, 34 | 35 | $(document).ready(function() { 36 | $('#myForm').on('submit', function(e) { 37 | e.preventDefault(); // <-- important 38 | $(this).ajaxSubmit({ 39 | target: '#output' 40 | }); 41 | }); 42 | }); 43 | 44 | Use ajaxForm when you want the plugin to manage all the event binding 45 | for you. For example, 46 | 47 | $(document).ready(function() { 48 | $('#myForm').ajaxForm({ 49 | target: '#output' 50 | }); 51 | }); 52 | 53 | You can also use ajaxForm with delegation (requires jQuery v1.7+), so the 54 | form does not have to exist when you invoke ajaxForm: 55 | 56 | $('#myForm').ajaxForm({ 57 | delegation: true, 58 | target: '#output' 59 | }); 60 | 61 | When using ajaxForm, the ajaxSubmit function will be invoked for you 62 | at the appropriate time. 63 | */ 64 | 65 | /** 66 | * Feature detection 67 | */ 68 | var feature = {}; 69 | feature.fileapi = $("").get(0).files !== undefined; 70 | feature.formdata = window.FormData !== undefined; 71 | 72 | var hasProp = !!$.fn.prop; 73 | 74 | // attr2 uses prop when it can but checks the return type for 75 | // an expected string. this accounts for the case where a form 76 | // contains inputs with names like "action" or "method"; in those 77 | // cases "prop" returns the element 78 | $.fn.attr2 = function() { 79 | if ( ! hasProp ) { 80 | return this.attr.apply(this, arguments); 81 | } 82 | var val = this.prop.apply(this, arguments); 83 | if ( ( val && val.jquery ) || typeof val === 'string' ) { 84 | return val; 85 | } 86 | return this.attr.apply(this, arguments); 87 | }; 88 | 89 | /** 90 | * ajaxSubmit() provides a mechanism for immediately submitting 91 | * an HTML form using AJAX. 92 | */ 93 | $.fn.ajaxSubmit = function(options) { 94 | /*jshint scripturl:true */ 95 | 96 | // fast fail if nothing selected (http://dev.jquery.com/ticket/2752) 97 | if (!this.length) { 98 | log('ajaxSubmit: skipping submit process - no element selected'); 99 | return this; 100 | } 101 | 102 | var method, action, url, $form = this; 103 | 104 | if (typeof options == 'function') { 105 | options = { success: options }; 106 | } 107 | else if ( options === undefined ) { 108 | options = {}; 109 | } 110 | 111 | method = options.type || this.attr2('method'); 112 | action = options.url || this.attr2('action'); 113 | 114 | url = (typeof action === 'string') ? $.trim(action) : ''; 115 | url = url || window.location.href || ''; 116 | if (url) { 117 | // clean url (don't include hash vaue) 118 | url = (url.match(/^([^#]+)/)||[])[1]; 119 | } 120 | 121 | options = $.extend(true, { 122 | url: url, 123 | success: $.ajaxSettings.success, 124 | type: method || $.ajaxSettings.type, 125 | iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank' 126 | }, options); 127 | 128 | // hook for manipulating the form data before it is extracted; 129 | // convenient for use with rich editors like tinyMCE or FCKEditor 130 | var veto = {}; 131 | this.trigger('form-pre-serialize', [this, options, veto]); 132 | if (veto.veto) { 133 | log('ajaxSubmit: submit vetoed via form-pre-serialize trigger'); 134 | return this; 135 | } 136 | 137 | // provide opportunity to alter form data before it is serialized 138 | if (options.beforeSerialize && options.beforeSerialize(this, options) === false) { 139 | log('ajaxSubmit: submit aborted via beforeSerialize callback'); 140 | return this; 141 | } 142 | 143 | var traditional = options.traditional; 144 | if ( traditional === undefined ) { 145 | traditional = $.ajaxSettings.traditional; 146 | } 147 | 148 | var elements = []; 149 | var qx, a = this.formToArray(options.semantic, elements); 150 | if (options.data) { 151 | options.extraData = options.data; 152 | qx = $.param(options.data, traditional); 153 | } 154 | 155 | // give pre-submit callback an opportunity to abort the submit 156 | if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) { 157 | log('ajaxSubmit: submit aborted via beforeSubmit callback'); 158 | return this; 159 | } 160 | 161 | // fire vetoable 'validate' event 162 | this.trigger('form-submit-validate', [a, this, options, veto]); 163 | if (veto.veto) { 164 | log('ajaxSubmit: submit vetoed via form-submit-validate trigger'); 165 | return this; 166 | } 167 | 168 | var q = $.param(a, traditional); 169 | if (qx) { 170 | q = ( q ? (q + '&' + qx) : qx ); 171 | } 172 | if (options.type.toUpperCase() == 'GET') { 173 | options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q; 174 | options.data = null; // data is null for 'get' 175 | } 176 | else { 177 | options.data = q; // data is the query string for 'post' 178 | } 179 | 180 | var callbacks = []; 181 | if (options.resetForm) { 182 | callbacks.push(function() { $form.resetForm(); }); 183 | } 184 | if (options.clearForm) { 185 | callbacks.push(function() { $form.clearForm(options.includeHidden); }); 186 | } 187 | 188 | // perform a load on the target only if dataType is not provided 189 | if (!options.dataType && options.target) { 190 | var oldSuccess = options.success || function(){}; 191 | callbacks.push(function(data) { 192 | var fn = options.replaceTarget ? 'replaceWith' : 'html'; 193 | $(options.target)[fn](data).each(oldSuccess, arguments); 194 | }); 195 | } 196 | else if (options.success) { 197 | callbacks.push(options.success); 198 | } 199 | 200 | options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg 201 | var context = options.context || this ; // jQuery 1.4+ supports scope context 202 | for (var i=0, max=callbacks.length; i < max; i++) { 203 | callbacks[i].apply(context, [data, status, xhr || $form, $form]); 204 | } 205 | }; 206 | 207 | if (options.error) { 208 | var oldError = options.error; 209 | options.error = function(xhr, status, error) { 210 | var context = options.context || this; 211 | oldError.apply(context, [xhr, status, error, $form]); 212 | }; 213 | } 214 | 215 | if (options.complete) { 216 | var oldComplete = options.complete; 217 | options.complete = function(xhr, status) { 218 | var context = options.context || this; 219 | oldComplete.apply(context, [xhr, status, $form]); 220 | }; 221 | } 222 | 223 | // are there files to upload? 224 | 225 | // [value] (issue #113), also see comment: 226 | // https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219 227 | var fileInputs = $('input[type=file]:enabled', this).filter(function() { return $(this).val() !== ''; }); 228 | 229 | var hasFileInputs = fileInputs.length > 0; 230 | var mp = 'multipart/form-data'; 231 | var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp); 232 | 233 | var fileAPI = feature.fileapi && feature.formdata; 234 | log("fileAPI :" + fileAPI); 235 | var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI; 236 | 237 | var jqxhr; 238 | 239 | // options.iframe allows user to force iframe mode 240 | // 06-NOV-09: now defaulting to iframe mode if file input is detected 241 | if (options.iframe !== false && (options.iframe || shouldUseFrame)) { 242 | // hack to fix Safari hang (thanks to Tim Molendijk for this) 243 | // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d 244 | if (options.closeKeepAlive) { 245 | $.get(options.closeKeepAlive, function() { 246 | jqxhr = fileUploadIframe(a); 247 | }); 248 | } 249 | else { 250 | jqxhr = fileUploadIframe(a); 251 | } 252 | } 253 | else if ((hasFileInputs || multipart) && fileAPI) { 254 | jqxhr = fileUploadXhr(a); 255 | } 256 | else { 257 | jqxhr = $.ajax(options); 258 | } 259 | 260 | $form.removeData('jqxhr').data('jqxhr', jqxhr); 261 | 262 | // clear element array 263 | for (var k=0; k < elements.length; k++) { 264 | elements[k] = null; 265 | } 266 | 267 | // fire 'notify' event 268 | this.trigger('form-submit-notify', [this, options]); 269 | return this; 270 | 271 | // utility fn for deep serialization 272 | function deepSerialize(extraData){ 273 | var serialized = $.param(extraData, options.traditional).split('&'); 274 | var len = serialized.length; 275 | var result = []; 276 | var i, part; 277 | for (i=0; i < len; i++) { 278 | // #252; undo param space replacement 279 | serialized[i] = serialized[i].replace(/\+/g,' '); 280 | part = serialized[i].split('='); 281 | // #278; use array instead of object storage, favoring array serializations 282 | result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]); 283 | } 284 | return result; 285 | } 286 | 287 | // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz) 288 | function fileUploadXhr(a) { 289 | var formdata = new FormData(); 290 | 291 | for (var i=0; i < a.length; i++) { 292 | formdata.append(a[i].name, a[i].value); 293 | } 294 | 295 | if (options.extraData) { 296 | var serializedData = deepSerialize(options.extraData); 297 | for (i=0; i < serializedData.length; i++) { 298 | if (serializedData[i]) { 299 | formdata.append(serializedData[i][0], serializedData[i][1]); 300 | } 301 | } 302 | } 303 | 304 | options.data = null; 305 | 306 | var s = $.extend(true, {}, $.ajaxSettings, options, { 307 | contentType: false, 308 | processData: false, 309 | cache: false, 310 | type: method || 'POST' 311 | }); 312 | 313 | if (options.uploadProgress) { 314 | // workaround because jqXHR does not expose upload property 315 | s.xhr = function() { 316 | var xhr = $.ajaxSettings.xhr(); 317 | if (xhr.upload) { 318 | xhr.upload.addEventListener('progress', function(event) { 319 | var percent = 0; 320 | var position = event.loaded || event.position; /*event.position is deprecated*/ 321 | var total = event.total; 322 | if (event.lengthComputable) { 323 | percent = Math.ceil(position / total * 100); 324 | } 325 | options.uploadProgress(event, position, total, percent); 326 | }, false); 327 | } 328 | return xhr; 329 | }; 330 | } 331 | 332 | s.data = null; 333 | var beforeSend = s.beforeSend; 334 | s.beforeSend = function(xhr, o) { 335 | //Send FormData() provided by user 336 | if (options.formData) { 337 | o.data = options.formData; 338 | } 339 | else { 340 | o.data = formdata; 341 | } 342 | if(beforeSend) { 343 | beforeSend.call(this, xhr, o); 344 | } 345 | }; 346 | return $.ajax(s); 347 | } 348 | 349 | // private function for handling file uploads (hat tip to YAHOO!) 350 | function fileUploadIframe(a) { 351 | var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle; 352 | var deferred = $.Deferred(); 353 | 354 | // #341 355 | deferred.abort = function(status) { 356 | xhr.abort(status); 357 | }; 358 | 359 | if (a) { 360 | // ensure that every serialized input is still enabled 361 | for (i=0; i < elements.length; i++) { 362 | el = $(elements[i]); 363 | if ( hasProp ) { 364 | el.prop('disabled', false); 365 | } 366 | else { 367 | el.removeAttr('disabled'); 368 | } 369 | } 370 | } 371 | 372 | s = $.extend(true, {}, $.ajaxSettings, options); 373 | s.context = s.context || s; 374 | id = 'jqFormIO' + (new Date().getTime()); 375 | if (s.iframeTarget) { 376 | $io = $(s.iframeTarget); 377 | n = $io.attr2('name'); 378 | if (!n) { 379 | $io.attr2('name', id); 380 | } 381 | else { 382 | id = n; 383 | } 384 | } 385 | else { 386 | $io = $('