Project

General

Profile

1
/*!
2
 * jQuery Steps v1.1.0 - 09/04/2014
3
 * Copyright (c) 2014 Rafael Staib (http://www.jquery-steps.com)
4
 * Licensed under MIT http://www.opensource.org/licenses/MIT
5
 */
6
;(function ($, undefined)
7
{
8
    $.fn.extend({
9
        _aria: function (name, value)
10
        {
11
            return this.attr("aria-" + name, value);
12
        },
13

    
14
        _removeAria: function (name)
15
        {
16
            return this.removeAttr("aria-" + name);
17
        },
18

    
19
        _enableAria: function (enable)
20
        {
21
            return (enable == null || enable) ?
22
                this.removeClass("disabled")._aria("disabled", "false") :
23
                this.addClass("disabled")._aria("disabled", "true");
24
        },
25

    
26
        _showAria: function (show)
27
        {
28
            return (show == null || show) ?
29
                this.show()._aria("hidden", "false") :
30
                this.hide()._aria("hidden", "true");
31
        },
32

    
33
        _selectAria: function (select)
34
        {
35
            return (select == null || select) ?
36
                this.addClass("current")._aria("selected", "true") :
37
                this.removeClass("current")._aria("selected", "false");
38
        },
39

    
40
        _id: function (id)
41
        {
42
            return (id) ? this.attr("id", id) : this.attr("id");
43
        }
44
    });
45

    
46
    if (!String.prototype.format)
47
    {
48
        String.prototype.format = function()
49
        {
50
            var args = (arguments.length === 1 && $.isArray(arguments[0])) ? arguments[0] : arguments;
51
            var formattedString = this;
52
            for (var i = 0; i < args.length; i++)
53
            {
54
                var pattern = new RegExp("\\{" + i + "\\}", "gm");
55
                formattedString = formattedString.replace(pattern, args[i]);
56
            }
57
            return formattedString;
58
        };
59
    }
60

    
61
    /**
62
     * A global unique id count.
63
     *
64
     * @static
65
     * @private
66
     * @property _uniqueId
67
     * @type Integer
68
     **/
69
    var _uniqueId = 0;
70

    
71
    /**
72
     * The plugin prefix for cookies.
73
     *
74
     * @final
75
     * @private
76
     * @property _cookiePrefix
77
     * @type String
78
     **/
79
    var _cookiePrefix = "jQu3ry_5teps_St@te_";
80

    
81
    /**
82
     * Suffix for the unique tab id.
83
     *
84
     * @final
85
     * @private
86
     * @property _tabSuffix
87
     * @type String
88
     * @since 0.9.7
89
     **/
90
    var _tabSuffix = "-t-";
91

    
92
    /**
93
     * Suffix for the unique tabpanel id.
94
     *
95
     * @final
96
     * @private
97
     * @property _tabpanelSuffix
98
     * @type String
99
     * @since 0.9.7
100
     **/
101
    var _tabpanelSuffix = "-p-";
102

    
103
    /**
104
     * Suffix for the unique title id.
105
     *
106
     * @final
107
     * @private
108
     * @property _titleSuffix
109
     * @type String
110
     * @since 0.9.7
111
     **/
112
    var _titleSuffix = "-h-";
113

    
114
    /**
115
     * An error message for an "index out of range" error.
116
     *
117
     * @final
118
     * @private
119
     * @property _indexOutOfRangeErrorMessage
120
     * @type String
121
     **/
122
    var _indexOutOfRangeErrorMessage = "Index out of range.";
123

    
124
    /**
125
     * An error message for an "missing corresponding element" error.
126
     *
127
     * @final
128
     * @private
129
     * @property _missingCorrespondingElementErrorMessage
130
     * @type String
131
     **/
132
    var _missingCorrespondingElementErrorMessage = "One or more corresponding step {0} are missing.";
133

    
134
    /**
135
     * Adds a step to the cache.
136
     *
137
     * @static
138
     * @private
139
     * @method addStepToCache
140
     * @param wizard {Object} A jQuery wizard object
141
     * @param step {Object} The step object to add
142
     **/
143
    function addStepToCache(wizard, step)
144
    {
145
        getSteps(wizard).push(step);
146
    }
147

    
148
    function analyzeData(wizard, options, state)
149
    {
150
        var stepTitles = wizard.children(options.headerTag),
151
            stepContents = wizard.children(options.bodyTag);
152

    
153
        // Validate content
154
        if (stepTitles.length > stepContents.length)
155
        {
156
            throwError(_missingCorrespondingElementErrorMessage, "contents");
157
        }
158
        else if (stepTitles.length < stepContents.length)
159
        {
160
            throwError(_missingCorrespondingElementErrorMessage, "titles");
161
        }
162

    
163
        var startIndex = options.startIndex;
164

    
165
        state.stepCount = stepTitles.length;
166

    
167
        // Tries to load the saved state (step position)
168
        if (options.saveState && $.cookie)
169
        {
170
            var savedState = $.cookie(_cookiePrefix + getUniqueId(wizard));
171
            // Sets the saved position to the start index if not undefined or out of range
172
            var savedIndex = parseInt(savedState, 0);
173
            if (!isNaN(savedIndex) && savedIndex < state.stepCount)
174
            {
175
                startIndex = savedIndex;
176
            }
177
        }
178

    
179
        state.currentIndex = startIndex;
180

    
181
        stepTitles.each(function (index)
182
        {
183
            var item = $(this), // item == header
184
                content = stepContents.eq(index),
185
                modeData = content.data("mode"),
186
                mode = (modeData == null) ? contentMode.html : getValidEnumValue(contentMode,
187
                    (/^\s*$/.test(modeData) || isNaN(modeData)) ? modeData : parseInt(modeData, 0)),
188
                contentUrl = (mode === contentMode.html || content.data("url") === undefined) ?
189
                    "" : content.data("url"),
190
                contentLoaded = (mode !== contentMode.html && content.data("loaded") === "1"),
191
                step = $.extend({}, stepModel, {
192
                    title: item.html(),
193
                    content: (mode === contentMode.html) ? content.html() : "",
194
                    contentUrl: contentUrl,
195
                    contentMode: mode,
196
                    contentLoaded: contentLoaded
197
                });
198

    
199
            addStepToCache(wizard, step);
200
        });
201
    }
202

    
203
    /**
204
     * Triggers the onCanceled event.
205
     *
206
     * @static
207
     * @private
208
     * @method cancel
209
     * @param wizard {Object} The jQuery wizard object
210
     **/
211
    function cancel(wizard)
212
    {
213
        wizard.triggerHandler("canceled");
214
    }
215

    
216
    function decreaseCurrentIndexBy(state, decreaseBy)
217
    {
218
        return state.currentIndex - decreaseBy;
219
    }
220

    
221
    /**
222
     * Removes the control functionality completely and transforms the current state to the initial HTML structure.
223
     *
224
     * @static
225
     * @private
226
     * @method destroy
227
     * @param wizard {Object} A jQuery wizard object
228
     **/
229
    function destroy(wizard, options)
230
    {
231
        var eventNamespace = getEventNamespace(wizard);
232

    
233
        // Remove virtual data objects from the wizard
234
        wizard.unbind(eventNamespace).removeData("uid").removeData("options")
235
            .removeData("state").removeData("steps").removeData("eventNamespace")
236
            .find(".actions a").unbind(eventNamespace);
237

    
238
        // Remove attributes and CSS classes from the wizard
239
        wizard.removeClass(options.clearFixCssClass + " vertical");
240

    
241
        var contents = wizard.find(".content > *");
242

    
243
        // Remove virtual data objects from panels and their titles
244
        contents.removeData("loaded").removeData("mode").removeData("url");
245

    
246
        // Remove attributes, CSS classes and reset inline styles on all panels and their titles
247
        contents.removeAttr("id").removeAttr("role").removeAttr("tabindex")
248
            .removeAttr("class").removeAttr("style")._removeAria("labelledby")
249
            ._removeAria("hidden");
250

    
251
        // Empty panels if the mode is set to 'async' or 'iframe'
252
        wizard.find(".content > [data-mode='async'],.content > [data-mode='iframe']").empty();
253

    
254
        var wizardSubstitute = $("<{0} class=\"{1}\"></{0}>".format(wizard.get(0).tagName, wizard.attr("class")));
255

    
256
        var wizardId = wizard._id();
257
        if (wizardId != null && wizardId !== "")
258
        {
259
            wizardSubstitute._id(wizardId);
260
        }
261

    
262
        wizardSubstitute.html(wizard.find(".content").html());
263
        wizard.after(wizardSubstitute);
264
        wizard.remove();
265

    
266
        return wizardSubstitute;
267
    }
268

    
269
    /**
270
     * Triggers the onFinishing and onFinished event.
271
     *
272
     * @static
273
     * @private
274
     * @method finishStep
275
     * @param wizard {Object} The jQuery wizard object
276
     * @param state {Object} The state container of the current wizard
277
     **/
278
    function finishStep(wizard, state)
279
    {
280
        var currentStep = wizard.find(".steps li").eq(state.currentIndex);
281

    
282
        if (wizard.triggerHandler("finishing", [state.currentIndex]))
283
        {
284
            currentStep.addClass("done").removeClass("error");
285
            wizard.triggerHandler("finished", [state.currentIndex]);
286
        }
287
        else
288
        {
289
            currentStep.addClass("error");
290
        }
291
    }
292

    
293
    /**
294
     * Gets or creates if not exist an unique event namespace for the given wizard instance.
295
     *
296
     * @static
297
     * @private
298
     * @method getEventNamespace
299
     * @param wizard {Object} A jQuery wizard object
300
     * @return {String} Returns the unique event namespace for the given wizard
301
     */
302
    function getEventNamespace(wizard)
303
    {
304
        var eventNamespace = wizard.data("eventNamespace");
305

    
306
        if (eventNamespace == null)
307
        {
308
            eventNamespace = "." + getUniqueId(wizard);
309
            wizard.data("eventNamespace", eventNamespace);
310
        }
311

    
312
        return eventNamespace;
313
    }
314

    
315
    function getStepAnchor(wizard, index)
316
    {
317
        var uniqueId = getUniqueId(wizard);
318

    
319
        return wizard.find("#" + uniqueId + _tabSuffix + index);
320
    }
321

    
322
    function getStepPanel(wizard, index)
323
    {
324
        var uniqueId = getUniqueId(wizard);
325

    
326
        return wizard.find("#" + uniqueId + _tabpanelSuffix + index);
327
    }
328

    
329
    function getStepTitle(wizard, index)
330
    {
331
        var uniqueId = getUniqueId(wizard);
332

    
333
        return wizard.find("#" + uniqueId + _titleSuffix + index);
334
    }
335

    
336
    function getOptions(wizard)
337
    {
338
        return wizard.data("options");
339
    }
340

    
341
    function getState(wizard)
342
    {
343
        return wizard.data("state");
344
    }
345

    
346
    function getSteps(wizard)
347
    {
348
        return wizard.data("steps");
349
    }
350

    
351
    /**
352
     * Gets a specific step object by index.
353
     *
354
     * @static
355
     * @private
356
     * @method getStep
357
     * @param index {Integer} An integer that belongs to the position of a step
358
     * @return {Object} A specific step object
359
     **/
360
    function getStep(wizard, index)
361
    {
362
        var steps = getSteps(wizard);
363

    
364
        if (index < 0 || index >= steps.length)
365
        {
366
            throwError(_indexOutOfRangeErrorMessage);
367
        }
368

    
369
        return steps[index];
370
    }
371

    
372
    /**
373
     * Gets or creates if not exist an unique id from the given wizard instance.
374
     *
375
     * @static
376
     * @private
377
     * @method getUniqueId
378
     * @param wizard {Object} A jQuery wizard object
379
     * @return {String} Returns the unique id for the given wizard
380
     */
381
    function getUniqueId(wizard)
382
    {
383
        var uniqueId = wizard.data("uid");
384

    
385
        if (uniqueId == null)
386
        {
387
            uniqueId = wizard._id();
388
            if (uniqueId == null)
389
            {
390
                uniqueId = "steps-uid-".concat(_uniqueId);
391
                wizard._id(uniqueId);
392
            }
393

    
394
            _uniqueId++;
395
            wizard.data("uid", uniqueId);
396
        }
397

    
398
        return uniqueId;
399
    }
400

    
401
    /**
402
     * Gets a valid enum value by checking a specific enum key or value.
403
     *
404
     * @static
405
     * @private
406
     * @method getValidEnumValue
407
     * @param enumType {Object} Type of enum
408
     * @param keyOrValue {Object} Key as `String` or value as `Integer` to check for
409
     */
410
    function getValidEnumValue(enumType, keyOrValue)
411
    {
412
        validateArgument("enumType", enumType);
413
        validateArgument("keyOrValue", keyOrValue);
414

    
415
        // Is key
416
        if (typeof keyOrValue === "string")
417
        {
418
            var value = enumType[keyOrValue];
419
            if (value === undefined)
420
            {
421
                throwError("The enum key '{0}' does not exist.", keyOrValue);
422
            }
423

    
424
            return value;
425
        }
426
        // Is value
427
        else if (typeof keyOrValue === "number")
428
        {
429
            for (var key in enumType)
430
            {
431
                if (enumType[key] === keyOrValue)
432
                {
433
                    return keyOrValue;
434
                }
435
            }
436

    
437
            throwError("Invalid enum value '{0}'.", keyOrValue);
438
        }
439
        // Type is not supported
440
        else
441
        {
442
            throwError("Invalid key or value type.");
443
        }
444
    }
445

    
446
    /**
447
     * Routes to the next step.
448
     *
449
     * @static
450
     * @private
451
     * @method goToNextStep
452
     * @param wizard {Object} The jQuery wizard object
453
     * @param options {Object} Settings of the current wizard
454
     * @param state {Object} The state container of the current wizard
455
     * @return {Boolean} Indicates whether the action executed
456
     **/
457
    function goToNextStep(wizard, options, state)
458
    {
459
        return paginationClick(wizard, options, state, increaseCurrentIndexBy(state, 1));
460
    }
461

    
462
    /**
463
     * Routes to the previous step.
464
     *
465
     * @static
466
     * @private
467
     * @method goToPreviousStep
468
     * @param wizard {Object} The jQuery wizard object
469
     * @param options {Object} Settings of the current wizard
470
     * @param state {Object} The state container of the current wizard
471
     * @return {Boolean} Indicates whether the action executed
472
     **/
473
    function goToPreviousStep(wizard, options, state)
474
    {
475
        return paginationClick(wizard, options, state, decreaseCurrentIndexBy(state, 1));
476
    }
477

    
478
    /**
479
     * Routes to a specific step by a given index.
480
     *
481
     * @static
482
     * @private
483
     * @method goToStep
484
     * @param wizard {Object} The jQuery wizard object
485
     * @param options {Object} Settings of the current wizard
486
     * @param state {Object} The state container of the current wizard
487
     * @param index {Integer} The position (zero-based) to route to
488
     * @return {Boolean} Indicates whether the action succeeded or failed
489
     **/
490
    function goToStep(wizard, options, state, index)
491
    {
492
        if (index < 0 || index >= state.stepCount)
493
        {
494
            throwError(_indexOutOfRangeErrorMessage);
495
        }
496

    
497
        if (options.forceMoveForward && index < state.currentIndex)
498
        {
499
            return;
500
        }
501

    
502
        var oldIndex = state.currentIndex;
503
        if (wizard.triggerHandler("stepChanging", [state.currentIndex, index]))
504
        {
505
            // Save new state
506
            state.currentIndex = index;
507
            saveCurrentStateToCookie(wizard, options, state);
508

    
509
            // Change visualisation
510
            refreshStepNavigation(wizard, options, state, oldIndex);
511
            refreshPagination(wizard, options, state);
512
            loadAsyncContent(wizard, options, state);
513
            startTransitionEffect(wizard, options, state, index, oldIndex, function()
514
            {
515
                wizard.triggerHandler("stepChanged", [index, oldIndex]);
516
            });
517
        }
518
        else
519
        {
520
            wizard.find(".steps li").eq(oldIndex).addClass("error");
521
        }
522

    
523
        return true;
524
    }
525

    
526
    function increaseCurrentIndexBy(state, increaseBy)
527
    {
528
        return state.currentIndex + increaseBy;
529
    }
530

    
531
    /**
532
     * Initializes the component.
533
     *
534
     * @static
535
     * @private
536
     * @method initialize
537
     * @param options {Object} The component settings
538
     **/
539
    function initialize(options)
540
    {
541
        /*jshint -W040 */
542
        var opts = $.extend(true, {}, defaults, options);
543

    
544
        return this.each(function ()
545
        {
546
            var wizard = $(this);
547
            var state = {
548
                currentIndex: opts.startIndex,
549
                currentStep: null,
550
                stepCount: 0,
551
                transitionElement: null
552
            };
553

    
554
            // Create data container
555
            wizard.data("options", opts);
556
            wizard.data("state", state);
557
            wizard.data("steps", []);
558

    
559
            analyzeData(wizard, opts, state);
560
            render(wizard, opts, state);
561
            registerEvents(wizard, opts);
562

    
563
            // Trigger focus
564
            if (opts.autoFocus && _uniqueId === 0)
565
            {
566
                getStepAnchor(wizard, opts.startIndex).focus();
567
            }
568

    
569
            wizard.triggerHandler("init", [opts.startIndex]);
570
        });
571
    }
572

    
573
    /**
574
     * Inserts a new step to a specific position.
575
     *
576
     * @static
577
     * @private
578
     * @method insertStep
579
     * @param wizard {Object} The jQuery wizard object
580
     * @param options {Object} Settings of the current wizard
581
     * @param state {Object} The state container of the current wizard
582
     * @param index {Integer} The position (zero-based) to add
583
     * @param step {Object} The step object to add
584
     * @example
585
     *     $("#wizard").steps().insert(0, {
586
 *         title: "Title",
587
 *         content: "", // optional
588
 *         contentMode: "async", // optional
589
 *         contentUrl: "/Content/Step/1" // optional
590
 *     });
591
     * @chainable
592
     **/
593
    function insertStep(wizard, options, state, index, step)
594
    {
595
        if (index < 0 || index > state.stepCount)
596
        {
597
            throwError(_indexOutOfRangeErrorMessage);
598
        }
599

    
600
        // TODO: Validate step object
601

    
602
        // Change data
603
        step = $.extend({}, stepModel, step);
604
        insertStepToCache(wizard, index, step);
605
        if (state.currentIndex !== state.stepCount && state.currentIndex >= index)
606
        {
607
            state.currentIndex++;
608
            saveCurrentStateToCookie(wizard, options, state);
609
        }
610
        state.stepCount++;
611

    
612
        var contentContainer = wizard.find(".content"),
613
            header = $("<{0}>{1}</{0}>".format(options.headerTag, step.title)),
614
            body = $("<{0}></{0}>".format(options.bodyTag));
615

    
616
        if (step.contentMode == null || step.contentMode === contentMode.html)
617
        {
618
            body.html(step.content);
619
        }
620

    
621
        if (index === 0)
622
        {
623
            contentContainer.prepend(body).prepend(header);
624
        }
625
        else
626
        {
627
            getStepPanel(wizard, (index - 1)).after(body).after(header);
628
        }
629

    
630
        renderBody(wizard, state, body, index);
631
        renderTitle(wizard, options, state, header, index);
632
        refreshSteps(wizard, options, state, index);
633
        if (index === state.currentIndex)
634
        {
635
            refreshStepNavigation(wizard, options, state);
636
        }
637
        refreshPagination(wizard, options, state);
638

    
639
        return wizard;
640
    }
641

    
642
    /**
643
     * Inserts a step object to the cache at a specific position.
644
     *
645
     * @static
646
     * @private
647
     * @method insertStepToCache
648
     * @param wizard {Object} A jQuery wizard object
649
     * @param index {Integer} The position (zero-based) to add
650
     * @param step {Object} The step object to add
651
     **/
652
    function insertStepToCache(wizard, index, step)
653
    {
654
        getSteps(wizard).splice(index, 0, step);
655
    }
656

    
657
    /**
658
     * Handles the keyup DOM event for pagination.
659
     *
660
     * @static
661
     * @private
662
     * @event keyup
663
     * @param event {Object} An event object
664
     */
665
    function keyUpHandler(event)
666
    {
667
        var wizard = $(this),
668
            options = getOptions(wizard),
669
            state = getState(wizard);
670

    
671
        if (options.suppressPaginationOnFocus && wizard.find(":focus").is(":input"))
672
        {
673
            event.preventDefault();
674
            return false;
675
        }
676

    
677
        var keyCodes = { left: 37, right: 39 };
678
        if (event.keyCode === keyCodes.left)
679
        {
680
            event.preventDefault();
681
            goToPreviousStep(wizard, options, state);
682
        }
683
        else if (event.keyCode === keyCodes.right)
684
        {
685
            event.preventDefault();
686
            goToNextStep(wizard, options, state);
687
        }
688
    }
689

    
690
    /**
691
     * Loads and includes async content.
692
     *
693
     * @static
694
     * @private
695
     * @method loadAsyncContent
696
     * @param wizard {Object} A jQuery wizard object
697
     * @param options {Object} Settings of the current wizard
698
     * @param state {Object} The state container of the current wizard
699
     */
700
    function loadAsyncContent(wizard, options, state)
701
    {
702
        if (state.stepCount > 0)
703
        {
704
            var currentIndex = state.currentIndex,
705
                currentStep = getStep(wizard, currentIndex);
706

    
707
            if (!options.enableContentCache || !currentStep.contentLoaded)
708
            {
709
                switch (getValidEnumValue(contentMode, currentStep.contentMode))
710
                {
711
                    case contentMode.iframe:
712
                        wizard.find(".content > .body").eq(state.currentIndex).empty()
713
                            .html("<iframe src=\"" + currentStep.contentUrl + "\" frameborder=\"0\" scrolling=\"no\" />")
714
                            .data("loaded", "1");
715
                        break;
716

    
717
                    case contentMode.async:
718
                        var currentStepContent = getStepPanel(wizard, currentIndex)._aria("busy", "true")
719
                            .empty().append(renderTemplate(options.loadingTemplate, { text: options.labels.loading }));
720

    
721
                        $.ajax({ url: currentStep.contentUrl, cache: false }).done(function (data)
722
                        {
723
                            currentStepContent.empty().html(data)._aria("busy", "false").data("loaded", "1");
724
                            wizard.triggerHandler("contentLoaded", [currentIndex]);
725
                        });
726
                        break;
727
                }
728
            }
729
        }
730
    }
731

    
732
    /**
733
     * Fires the action next or previous click event.
734
     *
735
     * @static
736
     * @private
737
     * @method paginationClick
738
     * @param wizard {Object} The jQuery wizard object
739
     * @param options {Object} Settings of the current wizard
740
     * @param state {Object} The state container of the current wizard
741
     * @param index {Integer} The position (zero-based) to route to
742
     * @return {Boolean} Indicates whether the event fired successfully or not
743
     **/
744
    function paginationClick(wizard, options, state, index)
745
    {
746
        var oldIndex = state.currentIndex;
747

    
748
        if (index >= 0 && index < state.stepCount && !(options.forceMoveForward && index < state.currentIndex))
749
        {
750
            var anchor = getStepAnchor(wizard, index),
751
                parent = anchor.parent(),
752
                isDisabled = parent.hasClass("disabled");
753

    
754
            // Enable the step to make the anchor clickable!
755
            parent._enableAria();
756
            anchor.click();
757

    
758
            // An error occured
759
            if (oldIndex === state.currentIndex && isDisabled)
760
            {
761
                // Disable the step again if current index has not changed; prevents click action.
762
                parent._enableAria(false);
763
                return false;
764
            }
765

    
766
            return true;
767
        }
768

    
769
        return false;
770
    }
771

    
772
    /**
773
     * Fires when a pagination click happens.
774
     *
775
     * @static
776
     * @private
777
     * @event click
778
     * @param event {Object} An event object
779
     */
780
    function paginationClickHandler(event)
781
    {
782
        event.preventDefault();
783

    
784
        var anchor = $(this),
785
            wizard = anchor.parent().parent().parent().parent(),
786
            options = getOptions(wizard),
787
            state = getState(wizard),
788
            href = anchor.attr("href");
789

    
790
        switch (href.substring(href.lastIndexOf("#") + 1))
791
        {
792
            case "cancel":
793
                cancel(wizard);
794
                break;
795

    
796
            case "finish":
797
                finishStep(wizard, state);
798
                break;
799

    
800
            case "next":
801
                goToNextStep(wizard, options, state);
802
                break;
803

    
804
            case "previous":
805
                goToPreviousStep(wizard, options, state);
806
                break;
807
        }
808
    }
809

    
810
    /**
811
     * Refreshs the visualization state for the entire pagination.
812
     *
813
     * @static
814
     * @private
815
     * @method refreshPagination
816
     * @param wizard {Object} A jQuery wizard object
817
     * @param options {Object} Settings of the current wizard
818
     * @param state {Object} The state container of the current wizard
819
     */
820
    function refreshPagination(wizard, options, state)
821
    {
822
        if (options.enablePagination)
823
        {
824
            var finish = wizard.find(".actions a[href$='#finish']").parent(),
825
                next = wizard.find(".actions a[href$='#next']").parent();
826

    
827
            if (!options.forceMoveForward)
828
            {
829
                var previous = wizard.find(".actions a[href$='#previous']").parent();
830
                previous._enableAria(state.currentIndex > 0);
831
            }
832

    
833
            if (options.enableFinishButton && options.showFinishButtonAlways)
834
            {
835
                finish._enableAria(state.stepCount > 0);
836
                next._enableAria(state.stepCount > 1 && state.stepCount > (state.currentIndex + 1));
837
            }
838
            else
839
            {
840
                finish._showAria(options.enableFinishButton && state.stepCount === (state.currentIndex + 1));
841
                next._showAria(state.stepCount === 0 || state.stepCount > (state.currentIndex + 1)).
842
                    _enableAria(state.stepCount > (state.currentIndex + 1) || !options.enableFinishButton);
843
            }
844
        }
845
    }
846

    
847
    /**
848
     * Refreshs the visualization state for the step navigation (tabs).
849
     *
850
     * @static
851
     * @private
852
     * @method refreshStepNavigation
853
     * @param wizard {Object} A jQuery wizard object
854
     * @param options {Object} Settings of the current wizard
855
     * @param state {Object} The state container of the current wizard
856
     * @param [oldIndex] {Integer} The index of the prior step
857
     */
858
    function refreshStepNavigation(wizard, options, state, oldIndex)
859
    {
860
        var currentOrNewStepAnchor = getStepAnchor(wizard, state.currentIndex),
861
            currentInfo = $("<span class=\"current-info audible\">" + options.labels.current + " </span>"),
862
            stepTitles = wizard.find(".content > .title");
863

    
864
        if (oldIndex != null)
865
        {
866
            var oldStepAnchor = getStepAnchor(wizard, oldIndex);
867
            oldStepAnchor.parent().addClass("done").removeClass("error")._selectAria(false);
868
            stepTitles.eq(oldIndex).removeClass("current").next(".body").removeClass("current");
869
            currentInfo = oldStepAnchor.find(".current-info");
870
            currentOrNewStepAnchor.focus();
871
        }
872

    
873
        currentOrNewStepAnchor.prepend(currentInfo).parent()._selectAria().removeClass("done")._enableAria();
874
        stepTitles.eq(state.currentIndex).addClass("current").next(".body").addClass("current");
875
    }
876

    
877
    /**
878
     * Refreshes step buttons and their related titles beyond a certain position.
879
     *
880
     * @static
881
     * @private
882
     * @method refreshSteps
883
     * @param wizard {Object} A jQuery wizard object
884
     * @param options {Object} Settings of the current wizard
885
     * @param state {Object} The state container of the current wizard
886
     * @param index {Integer} The start point for refreshing ids
887
     */
888
    function refreshSteps(wizard, options, state, index)
889
    {
890
        var uniqueId = getUniqueId(wizard);
891

    
892
        for (var i = index; i < state.stepCount; i++)
893
        {
894
            var uniqueStepId = uniqueId + _tabSuffix + i,
895
                uniqueBodyId = uniqueId + _tabpanelSuffix + i,
896
                uniqueHeaderId = uniqueId + _titleSuffix + i,
897
                title = wizard.find(".title").eq(i)._id(uniqueHeaderId);
898

    
899
            wizard.find(".steps a").eq(i)._id(uniqueStepId)
900
                ._aria("controls", uniqueBodyId).attr("href", "#" + uniqueHeaderId)
901
                .html(renderTemplate(options.titleTemplate, { index: i + 1, title: title.html() }));
902
            wizard.find(".body").eq(i)._id(uniqueBodyId)
903
                ._aria("labelledby", uniqueHeaderId);
904
        }
905
    }
906

    
907
    function registerEvents(wizard, options)
908
    {
909
        var eventNamespace = getEventNamespace(wizard);
910

    
911
        wizard.bind("canceled" + eventNamespace, options.onCanceled);
912
        wizard.bind("contentLoaded" + eventNamespace, options.onContentLoaded);
913
        wizard.bind("finishing" + eventNamespace, options.onFinishing);
914
        wizard.bind("finished" + eventNamespace, options.onFinished);
915
        wizard.bind("init" + eventNamespace, options.onInit);
916
        wizard.bind("stepChanging" + eventNamespace, options.onStepChanging);
917
        wizard.bind("stepChanged" + eventNamespace, options.onStepChanged);
918

    
919
        if (options.enableKeyNavigation)
920
        {
921
            wizard.bind("keyup" + eventNamespace, keyUpHandler);
922
        }
923

    
924
        wizard.find(".actions a").bind("click" + eventNamespace, paginationClickHandler);
925
    }
926

    
927
    /**
928
     * Removes a specific step by an given index.
929
     *
930
     * @static
931
     * @private
932
     * @method removeStep
933
     * @param wizard {Object} A jQuery wizard object
934
     * @param options {Object} Settings of the current wizard
935
     * @param state {Object} The state container of the current wizard
936
     * @param index {Integer} The position (zero-based) of the step to remove
937
     * @return Indecates whether the item is removed.
938
     **/
939
    function removeStep(wizard, options, state, index)
940
    {
941
        // Index out of range and try deleting current item will return false.
942
        if (index < 0 || index >= state.stepCount || state.currentIndex === index)
943
        {
944
            return false;
945
        }
946

    
947
        // Change data
948
        removeStepFromCache(wizard, index);
949
        if (state.currentIndex > index)
950
        {
951
            state.currentIndex--;
952
            saveCurrentStateToCookie(wizard, options, state);
953
        }
954
        state.stepCount--;
955

    
956
        getStepTitle(wizard, index).remove();
957
        getStepPanel(wizard, index).remove();
958
        getStepAnchor(wizard, index).parent().remove();
959

    
960
        // Set the "first" class to the new first step button
961
        if (index === 0)
962
        {
963
            wizard.find(".steps li").first().addClass("first");
964
        }
965

    
966
        // Set the "last" class to the new last step button
967
        if (index === state.stepCount)
968
        {
969
            wizard.find(".steps li").eq(index).addClass("last");
970
        }
971

    
972
        refreshSteps(wizard, options, state, index);
973
        refreshPagination(wizard, options, state);
974

    
975
        return true;
976
    }
977

    
978
    function removeStepFromCache(wizard, index)
979
    {
980
        getSteps(wizard).splice(index, 1);
981
    }
982

    
983
    /**
984
     * Transforms the base html structure to a more sensible html structure.
985
     *
986
     * @static
987
     * @private
988
     * @method render
989
     * @param wizard {Object} A jQuery wizard object
990
     * @param options {Object} Settings of the current wizard
991
     * @param state {Object} The state container of the current wizard
992
     **/
993
    function render(wizard, options, state)
994
    {
995
        // Create a content wrapper and copy HTML from the intial wizard structure
996
        var wrapperTemplate = "<{0} class=\"{1}\">{2}</{0}>",
997
            orientation = getValidEnumValue(stepsOrientation, options.stepsOrientation),
998
            verticalCssClass = (orientation === stepsOrientation.vertical) ? " vertical" : "",
999
            contentWrapper = $(wrapperTemplate.format(options.contentContainerTag, "content " + options.clearFixCssClass, wizard.html())),
1000
            stepsWrapper = $(wrapperTemplate.format(options.stepsContainerTag, "steps " + options.clearFixCssClass, "<ul role=\"tablist\"></ul>")),
1001
            stepTitles = contentWrapper.children(options.headerTag),
1002
            stepContents = contentWrapper.children(options.bodyTag);
1003

    
1004
        // Transform the wizard wrapper and remove the inner HTML
1005
        wizard.attr("role", "application").empty().append(stepsWrapper).append(contentWrapper)
1006
            .addClass(options.cssClass + " " + options.clearFixCssClass + verticalCssClass);
1007

    
1008
        // Add WIA-ARIA support
1009
        stepContents.each(function (index)
1010
        {
1011
            renderBody(wizard, state, $(this), index);
1012
        });
1013

    
1014
        stepTitles.each(function (index)
1015
        {
1016
            renderTitle(wizard, options, state, $(this), index);
1017
        });
1018

    
1019
        refreshStepNavigation(wizard, options, state);
1020
        renderPagination(wizard, options, state);
1021
    }
1022

    
1023
    /**
1024
     * Transforms the body to a proper tabpanel.
1025
     *
1026
     * @static
1027
     * @private
1028
     * @method renderBody
1029
     * @param wizard {Object} A jQuery wizard object
1030
     * @param body {Object} A jQuery body object
1031
     * @param index {Integer} The position of the body
1032
     */
1033
    function renderBody(wizard, state, body, index)
1034
    {
1035
        var uniqueId = getUniqueId(wizard),
1036
            uniqueBodyId = uniqueId + _tabpanelSuffix + index,
1037
            uniqueHeaderId = uniqueId + _titleSuffix + index;
1038

    
1039
        body._id(uniqueBodyId).attr("role", "tabpanel")._aria("labelledby", uniqueHeaderId)
1040
            .addClass("body" + " step-" + index).attr('data-step',index)._showAria(state.currentIndex === index);
1041
    }
1042

    
1043
    /**
1044
     * Renders a pagination if enabled.
1045
     *
1046
     * @static
1047
     * @private
1048
     * @method renderPagination
1049
     * @param wizard {Object} A jQuery wizard object
1050
     * @param options {Object} Settings of the current wizard
1051
     * @param state {Object} The state container of the current wizard
1052
     */
1053
    function renderPagination(wizard, options, state)
1054
    {
1055
        if (options.enablePagination)
1056
        {
1057
            var pagination = "<{0} class=\"actions {1}\"><ul role=\"menu\" aria-label=\"{2}\">{3}</ul></{0}>",
1058
                buttonTemplate = "<li class=\"button_{0}\"><a href=\"#{0}\" role=\"menuitem\">{1}</a></li>",
1059
                buttons = "";
1060

    
1061
            if (!options.forceMoveForward)
1062
            {
1063
                buttons += buttonTemplate.format("previous", options.labels.previous);
1064
            }
1065

    
1066
            buttons += buttonTemplate.format("next", options.labels.next);
1067

    
1068
            if (options.enableFinishButton)
1069
            {
1070
                buttons += buttonTemplate.format("finish", options.labels.finish);
1071
            }
1072

    
1073
            if (options.enableCancelButton)
1074
            {
1075
                buttons += buttonTemplate.format("cancel", options.labels.cancel);
1076
            }
1077

    
1078
            wizard.append(pagination.format(options.actionContainerTag, options.clearFixCssClass,
1079
                options.labels.pagination, buttons));
1080

    
1081
            refreshPagination(wizard, options, state);
1082
            loadAsyncContent(wizard, options, state);
1083
        }
1084
    }
1085

    
1086
    /**
1087
     * Renders a template and replaces all placeholder.
1088
     *
1089
     * @static
1090
     * @private
1091
     * @method renderTemplate
1092
     * @param template {String} A template
1093
     * @param substitutes {Object} A list of substitute
1094
     * @return {String} The rendered template
1095
     */
1096
    function renderTemplate(template, substitutes)
1097
    {
1098
        var matches = template.match(/#([a-z]*)#/gi);
1099

    
1100
        for (var i = 0; i < matches.length; i++)
1101
        {
1102
            var match = matches[i],
1103
                key = match.substring(1, match.length - 1);
1104

    
1105
            if (substitutes[key] === undefined)
1106
            {
1107
                throwError("The key '{0}' does not exist in the substitute collection!", key);
1108
            }
1109

    
1110
            template = template.replace(match, substitutes[key]);
1111
        }
1112

    
1113
        return template;
1114
    }
1115

    
1116
    /**
1117
     * Transforms the title to a step item button.
1118
     *
1119
     * @static
1120
     * @private
1121
     * @method renderTitle
1122
     * @param wizard {Object} A jQuery wizard object
1123
     * @param options {Object} Settings of the current wizard
1124
     * @param state {Object} The state container of the current wizard
1125
     * @param header {Object} A jQuery header object
1126
     * @param index {Integer} The position of the header
1127
     */
1128
    function renderTitle(wizard, options, state, header, index)
1129
    {
1130
        var uniqueId = getUniqueId(wizard),
1131
            uniqueStepId = uniqueId + _tabSuffix + index,
1132
            uniqueBodyId = uniqueId + _tabpanelSuffix + index,
1133
            uniqueHeaderId = uniqueId + _titleSuffix + index,
1134
            stepCollection = wizard.find(".steps > ul"),
1135
            title = renderTemplate(options.titleTemplate, {
1136
                index: index + 1,
1137
                title: header.html()
1138
            }),
1139
            stepItem = $("<li role=\"tab\"><a id=\"" + uniqueStepId + "\" href=\"#" + uniqueHeaderId +
1140
                "\" aria-controls=\"" + uniqueBodyId + "\">" + title + "</a></li>");
1141

    
1142
        stepItem._enableAria(options.enableAllSteps || state.currentIndex > index);
1143

    
1144
        if (state.currentIndex > index)
1145
        {
1146
            stepItem.addClass("done");
1147
        }
1148

    
1149
        header._id(uniqueHeaderId).attr("tabindex", "-1").addClass("title");
1150

    
1151
        if (index === 0)
1152
        {
1153
            stepCollection.prepend(stepItem);
1154
        }
1155
        else
1156
        {
1157
            stepCollection.find("li").eq(index - 1).after(stepItem);
1158
        }
1159

    
1160
        // Set the "first" class to the new first step button
1161
        if (index === 0)
1162
        {
1163
            stepCollection.find("li").removeClass("first").eq(index).addClass("first");
1164
        }
1165

    
1166
        // Set the "last" class to the new last step button
1167
        if (index === (state.stepCount - 1))
1168
        {
1169
            stepCollection.find("li").removeClass("last").eq(index).addClass("last");
1170
        }
1171

    
1172
        // Register click event
1173
        stepItem.children("a").bind("click" + getEventNamespace(wizard), stepClickHandler);
1174
    }
1175

    
1176
    /**
1177
     * Saves the current state to a cookie.
1178
     *
1179
     * @static
1180
     * @private
1181
     * @method saveCurrentStateToCookie
1182
     * @param wizard {Object} A jQuery wizard object
1183
     * @param options {Object} Settings of the current wizard
1184
     * @param state {Object} The state container of the current wizard
1185
     */
1186
    function saveCurrentStateToCookie(wizard, options, state)
1187
    {
1188
        if (options.saveState && $.cookie)
1189
        {
1190
            $.cookie(_cookiePrefix + getUniqueId(wizard), state.currentIndex);
1191
        }
1192
    }
1193

    
1194
    function startTransitionEffect(wizard, options, state, index, oldIndex, doneCallback)
1195
    {
1196
        var stepContents = wizard.find(".content > .body"),
1197
            effect = getValidEnumValue(transitionEffect, options.transitionEffect),
1198
            effectSpeed = options.transitionEffectSpeed,
1199
            newStep = stepContents.eq(index),
1200
            currentStep = stepContents.eq(oldIndex);
1201

    
1202
        switch (effect)
1203
        {
1204
            case transitionEffect.fade:
1205
            case transitionEffect.slide:
1206
                var hide = (effect === transitionEffect.fade) ? "fadeOut" : "slideUp",
1207
                    show = (effect === transitionEffect.fade) ? "fadeIn" : "slideDown";
1208

    
1209
                state.transitionElement = newStep;
1210
                currentStep[hide](effectSpeed, function ()
1211
                {
1212
                    var wizard = $(this)._showAria(false).parent().parent(),
1213
                        state = getState(wizard);
1214

    
1215
                    if (state.transitionElement)
1216
                    {
1217
                        state.transitionElement[show](effectSpeed, function ()
1218
                        {
1219
                            $(this)._showAria();
1220
                        }).promise().done(doneCallback);
1221
                        state.transitionElement = null;
1222
                    }
1223
                });
1224
                break;
1225

    
1226
            case transitionEffect.slideLeft:
1227
                var outerWidth = currentStep.outerWidth(true),
1228
                    posFadeOut = (index > oldIndex) ? -(outerWidth) : outerWidth,
1229
                    posFadeIn = (index > oldIndex) ? outerWidth : -(outerWidth);
1230

    
1231
                $.when(currentStep.animate({ left: posFadeOut }, effectSpeed,
1232
                        function () { $(this)._showAria(false); }),
1233
                    newStep.css("left", posFadeIn + "px")._showAria()
1234
                        .animate({ left: 0 }, effectSpeed)).done(doneCallback);
1235
                break;
1236

    
1237
            default:
1238
                $.when(currentStep._showAria(false), newStep._showAria())
1239
                    .done(doneCallback);
1240
                break;
1241
        }
1242
    }
1243

    
1244
    /**
1245
     * Fires when a step click happens.
1246
     *
1247
     * @static
1248
     * @private
1249
     * @event click
1250
     * @param event {Object} An event object
1251
     */
1252
    function stepClickHandler(event)
1253
    {
1254
        event.preventDefault();
1255

    
1256
        var anchor = $(this),
1257
            wizard = anchor.parent().parent().parent().parent(),
1258
            options = getOptions(wizard),
1259
            state = getState(wizard),
1260
            oldIndex = state.currentIndex;
1261

    
1262
        if (anchor.parent().is(":not(.disabled):not(.current)"))
1263
        {
1264
            var href = anchor.attr("href"),
1265
                position = parseInt(href.substring(href.lastIndexOf("-") + 1), 0);
1266

    
1267
            goToStep(wizard, options, state, position);
1268
        }
1269

    
1270
        // If nothing has changed
1271
        if (oldIndex === state.currentIndex)
1272
        {
1273
            getStepAnchor(wizard, oldIndex).focus();
1274
            return false;
1275
        }
1276
    }
1277

    
1278
    function throwError(message)
1279
    {
1280
        if (arguments.length > 1)
1281
        {
1282
            message = message.format(Array.prototype.slice.call(arguments, 1));
1283
        }
1284

    
1285
        throw new Error(message);
1286
    }
1287

    
1288
    /**
1289
     * Checks an argument for null or undefined and throws an error if one check applies.
1290
     *
1291
     * @static
1292
     * @private
1293
     * @method validateArgument
1294
     * @param argumentName {String} The name of the given argument
1295
     * @param argumentValue {Object} The argument itself
1296
     */
1297
    function validateArgument(argumentName, argumentValue)
1298
    {
1299
        if (argumentValue == null)
1300
        {
1301
            throwError("The argument '{0}' is null or undefined.", argumentName);
1302
        }
1303
    }
1304

    
1305
    /**
1306
     * Represents a jQuery wizard plugin.
1307
     *
1308
     * @class steps
1309
     * @constructor
1310
     * @param [method={}] The name of the method as `String` or an JSON object for initialization
1311
     * @param [params=]* {Array} Additional arguments for a method call
1312
     * @chainable
1313
     **/
1314
    $.fn.steps = function (method)
1315
    {
1316
        if ($.fn.steps[method])
1317
        {
1318
            return $.fn.steps[method].apply(this, Array.prototype.slice.call(arguments, 1));
1319
        }
1320
        else if (typeof method === "object" || !method)
1321
        {
1322
            return initialize.apply(this, arguments);
1323
        }
1324
        else
1325
        {
1326
            $.error("Method " + method + " does not exist on jQuery.steps");
1327
        }
1328
    };
1329

    
1330
    /**
1331
     * Adds a new step.
1332
     *
1333
     * @method add
1334
     * @param step {Object} The step object to add
1335
     * @chainable
1336
     **/
1337
    $.fn.steps.add = function (step)
1338
    {
1339
        var state = getState(this);
1340
        return insertStep(this, getOptions(this), state, state.stepCount, step);
1341
    };
1342

    
1343
    /**
1344
    * Gets the step count.
1345
    *
1346
    * @method count
1347
    * @return {Integer} step count
1348
    * @for steps
1349
    **/
1350
    $.fn.steps.count = function ()
1351
    {
1352
        var state = getState(this);
1353
        return state.stepCount;
1354
    };
1355

    
1356
    /**
1357
     * Removes the control functionality completely and transforms the current state to the initial HTML structure.
1358
     *
1359
     * @method destroy
1360
     * @chainable
1361
     **/
1362
    $.fn.steps.destroy = function ()
1363
    {
1364
        return destroy(this, getOptions(this));
1365
    };
1366

    
1367
    /**
1368
     * Triggers the onFinishing and onFinished event.
1369
     *
1370
     * @method finish
1371
     **/
1372
    $.fn.steps.finish = function ()
1373
    {
1374
        finishStep(this, getState(this));
1375
    };
1376

    
1377
    /**
1378
     * Gets the current step index.
1379
     *
1380
     * @method getCurrentIndex
1381
     * @return {Integer} The actual step index (zero-based)
1382
     * @for steps
1383
     **/
1384
    $.fn.steps.getCurrentIndex = function ()
1385
    {
1386
        return getState(this).currentIndex;
1387
    };
1388

    
1389
    /**
1390
     * Gets the current step object.
1391
     *
1392
     * @method getCurrentStep
1393
     * @return {Object} The actual step object
1394
     **/
1395
    $.fn.steps.getCurrentStep = function ()
1396
    {
1397
        return getStep(this, getState(this).currentIndex);
1398
    };
1399

    
1400
    /**
1401
     * Gets a specific step object by index.
1402
     *
1403
     * @method getStep
1404
     * @param index {Integer} An integer that belongs to the position of a step
1405
     * @return {Object} A specific step object
1406
     **/
1407
    $.fn.steps.getStep = function (index)
1408
    {
1409
        return getStep(this, index);
1410
    };
1411

    
1412
    /**
1413
     * Inserts a new step to a specific position.
1414
     *
1415
     * @method insert
1416
     * @param index {Integer} The position (zero-based) to add
1417
     * @param step {Object} The step object to add
1418
     * @example
1419
     *     $("#wizard").steps().insert(0, {
1420
 *         title: "Title",
1421
 *         content: "", // optional
1422
 *         contentMode: "async", // optional
1423
 *         contentUrl: "/Content/Step/1" // optional
1424
 *     });
1425
     * @chainable
1426
     **/
1427
    $.fn.steps.insert = function (index, step)
1428
    {
1429
        return insertStep(this, getOptions(this), getState(this), index, step);
1430
    };
1431

    
1432
    /**
1433
     * Routes to the next step.
1434
     *
1435
     * @method next
1436
     * @return {Boolean} Indicates whether the action executed
1437
     **/
1438
    $.fn.steps.next = function ()
1439
    {
1440
        return goToNextStep(this, getOptions(this), getState(this));
1441
    };
1442

    
1443
    /**
1444
     * Routes to the previous step.
1445
     *
1446
     * @method previous
1447
     * @return {Boolean} Indicates whether the action executed
1448
     **/
1449
    $.fn.steps.previous = function ()
1450
    {
1451
        return goToPreviousStep(this, getOptions(this), getState(this));
1452
    };
1453

    
1454
    /**
1455
     * Removes a specific step by an given index.
1456
     *
1457
     * @method remove
1458
     * @param index {Integer} The position (zero-based) of the step to remove
1459
     * @return Indecates whether the item is removed.
1460
     **/
1461
    $.fn.steps.remove = function (index)
1462
    {
1463
        return removeStep(this, getOptions(this), getState(this), index);
1464
    };
1465

    
1466
    /**
1467
     * Sets a specific step object by index.
1468
     *
1469
     * @method setStep
1470
     * @param index {Integer} An integer that belongs to the position of a step
1471
     **/
1472
    $.fn.steps.setStep = function (index)
1473
    {
1474
        var options = getOptions(this),
1475
            state = getState(this);
1476
        return goToStep(this, options, state, index);
1477
    };
1478

    
1479
    /**
1480
     * Skips an certain amount of steps.
1481
     *
1482
     * @method skip
1483
     * @param count {Integer} The amount of steps that should be skipped
1484
     * @return {Boolean} Indicates whether the action executed
1485
     **/
1486
    $.fn.steps.skip = function (count)
1487
    {
1488
        throw new Error("Not yet implemented!");
1489
    };
1490

    
1491
    /**
1492
     * An enum represents the different content types of a step and their loading mechanisms.
1493
     *
1494
     * @class contentMode
1495
     * @for steps
1496
     **/
1497
    var contentMode = $.fn.steps.contentMode = {
1498
        /**
1499
         * HTML embedded content
1500
         *
1501
         * @readOnly
1502
         * @property html
1503
         * @type Integer
1504
         * @for contentMode
1505
         **/
1506
        html: 0,
1507

    
1508
        /**
1509
         * IFrame embedded content
1510
         *
1511
         * @readOnly
1512
         * @property iframe
1513
         * @type Integer
1514
         * @for contentMode
1515
         **/
1516
        iframe: 1,
1517

    
1518
        /**
1519
         * Async embedded content
1520
         *
1521
         * @readOnly
1522
         * @property async
1523
         * @type Integer
1524
         * @for contentMode
1525
         **/
1526
        async: 2
1527
    };
1528

    
1529
    /**
1530
     * An enum represents the orientation of the steps navigation.
1531
     *
1532
     * @class stepsOrientation
1533
     * @for steps
1534
     **/
1535
    var stepsOrientation = $.fn.steps.stepsOrientation = {
1536
        /**
1537
         * Horizontal orientation
1538
         *
1539
         * @readOnly
1540
         * @property horizontal
1541
         * @type Integer
1542
         * @for stepsOrientation
1543
         **/
1544
        horizontal: 0,
1545

    
1546
        /**
1547
         * Vertical orientation
1548
         *
1549
         * @readOnly
1550
         * @property vertical
1551
         * @type Integer
1552
         * @for stepsOrientation
1553
         **/
1554
        vertical: 1
1555
    };
1556

    
1557
    /**
1558
     * An enum that represents the various transition animations.
1559
     *
1560
     * @class transitionEffect
1561
     * @for steps
1562
     **/
1563
    var transitionEffect = $.fn.steps.transitionEffect = {
1564
        /**
1565
         * No transition animation
1566
         *
1567
         * @readOnly
1568
         * @property none
1569
         * @type Integer
1570
         * @for transitionEffect
1571
         **/
1572
        none: 0,
1573

    
1574
        /**
1575
         * Fade in transition
1576
         *
1577
         * @readOnly
1578
         * @property fade
1579
         * @type Integer
1580
         * @for transitionEffect
1581
         **/
1582
        fade: 1,
1583

    
1584
        /**
1585
         * Slide up transition
1586
         *
1587
         * @readOnly
1588
         * @property slide
1589
         * @type Integer
1590
         * @for transitionEffect
1591
         **/
1592
        slide: 2,
1593

    
1594
        /**
1595
         * Slide left transition
1596
         *
1597
         * @readOnly
1598
         * @property slideLeft
1599
         * @type Integer
1600
         * @for transitionEffect
1601
         **/
1602
        slideLeft: 3
1603
    };
1604

    
1605
    var stepModel = $.fn.steps.stepModel = {
1606
        title: "",
1607
        content: "",
1608
        contentUrl: "",
1609
        contentMode: contentMode.html,
1610
        contentLoaded: false
1611
    };
1612

    
1613
    /**
1614
     * An object that represents the default settings.
1615
     * There are two possibities to override the sub-properties.
1616
     * Either by doing it generally (global) or on initialization.
1617
     *
1618
     * @static
1619
     * @class defaults
1620
     * @for steps
1621
     * @example
1622
     *   // Global approach
1623
     *   $.steps.defaults.headerTag = "h3";
1624
     * @example
1625
     *   // Initialization approach
1626
     *   $("#wizard").steps({ headerTag: "h3" });
1627
     **/
1628
    var defaults = $.fn.steps.defaults = {
1629
        /**
1630
         * The header tag is used to find the step button text within the declared wizard area.
1631
         *
1632
         * @property headerTag
1633
         * @type String
1634
         * @default "h1"
1635
         * @for defaults
1636
         **/
1637
        headerTag: "h1",
1638

    
1639
        /**
1640
         * The body tag is used to find the step content within the declared wizard area.
1641
         *
1642
         * @property bodyTag
1643
         * @type String
1644
         * @default "div"
1645
         * @for defaults
1646
         **/
1647
        bodyTag: "div",
1648

    
1649
        /**
1650
         * The content container tag which will be used to wrap all step contents.
1651
         *
1652
         * @property contentContainerTag
1653
         * @type String
1654
         * @default "div"
1655
         * @for defaults
1656
         **/
1657
        contentContainerTag: "div",
1658

    
1659
        /**
1660
         * The action container tag which will be used to wrap the pagination navigation.
1661
         *
1662
         * @property actionContainerTag
1663
         * @type String
1664
         * @default "div"
1665
         * @for defaults
1666
         **/
1667
        actionContainerTag: "div",
1668

    
1669
        /**
1670
         * The steps container tag which will be used to wrap the steps navigation.
1671
         *
1672
         * @property stepsContainerTag
1673
         * @type String
1674
         * @default "div"
1675
         * @for defaults
1676
         **/
1677
        stepsContainerTag: "div",
1678

    
1679
        /**
1680
         * The css class which will be added to the outer component wrapper.
1681
         *
1682
         * @property cssClass
1683
         * @type String
1684
         * @default "wizard"
1685
         * @for defaults
1686
         * @example
1687
         *     <div class="wizard">
1688
         *         ...
1689
         *     </div>
1690
         **/
1691
        cssClass: "wizard",
1692

    
1693
        /**
1694
         * The css class which will be used for floating scenarios.
1695
         *
1696
         * @property clearFixCssClass
1697
         * @type String
1698
         * @default "clearfix"
1699
         * @for defaults
1700
         **/
1701
        clearFixCssClass: "clearfix",
1702

    
1703
        /**
1704
         * Determines whether the steps are vertically or horizontally oriented.
1705
         *
1706
         * @property stepsOrientation
1707
         * @type stepsOrientation
1708
         * @default horizontal
1709
         * @for defaults
1710
         * @since 1.0.0
1711
         **/
1712
        stepsOrientation: stepsOrientation.horizontal,
1713

    
1714
        /*
1715
         * Tempplates
1716
         */
1717

    
1718
        /**
1719
         * The title template which will be used to create a step button.
1720
         *
1721
         * @property titleTemplate
1722
         * @type String
1723
         * @default "<span class=\"number\">#index#.</span> #title#"
1724
         * @for defaults
1725
         **/
1726
        titleTemplate: "<span class=\"number\">#index#</span> <span class='title'>#title#</span>",
1727

    
1728
        /**
1729
         * The loading template which will be used to create the loading animation.
1730
         *
1731
         * @property loadingTemplate
1732
         * @type String
1733
         * @default "<span class=\"spinner\"></span> #text#"
1734
         * @for defaults
1735
         **/
1736
        loadingTemplate: "<span class=\"spinner\"></span> #text#",
1737

    
1738
        /*
1739
         * Behaviour
1740
         */
1741

    
1742
        /**
1743
         * Sets the focus to the first wizard instance in order to enable the key navigation from the begining if `true`.
1744
         *
1745
         * @property autoFocus
1746
         * @type Boolean
1747
         * @default false
1748
         * @for defaults
1749
         * @since 0.9.4
1750
         **/
1751
        autoFocus: false,
1752

    
1753
        /**
1754
         * Enables all steps from the begining if `true` (all steps are clickable).
1755
         *
1756
         * @property enableAllSteps
1757
         * @type Boolean
1758
         * @default false
1759
         * @for defaults
1760
         **/
1761
        enableAllSteps: false,
1762

    
1763
        /**
1764
         * Enables keyboard navigation if `true` (arrow left and arrow right).
1765
         *
1766
         * @property enableKeyNavigation
1767
         * @type Boolean
1768
         * @default true
1769
         * @for defaults
1770
         **/
1771
        enableKeyNavigation: true,
1772

    
1773
        /**
1774
         * Enables pagination if `true`.
1775
         *
1776
         * @property enablePagination
1777
         * @type Boolean
1778
         * @default true
1779
         * @for defaults
1780
         **/
1781
        enablePagination: true,
1782

    
1783
        /**
1784
         * Suppresses pagination if a form field is focused.
1785
         *
1786
         * @property suppressPaginationOnFocus
1787
         * @type Boolean
1788
         * @default true
1789
         * @for defaults
1790
         **/
1791
        suppressPaginationOnFocus: true,
1792

    
1793
        /**
1794
         * Enables cache for async loaded or iframe embedded content.
1795
         *
1796
         * @property enableContentCache
1797
         * @type Boolean
1798
         * @default true
1799
         * @for defaults
1800
         **/
1801
        enableContentCache: true,
1802

    
1803
        /**
1804
         * Shows the cancel button if enabled.
1805
         *
1806
         * @property enableCancelButton
1807
         * @type Boolean
1808
         * @default false
1809
         * @for defaults
1810
         **/
1811
        enableCancelButton: false,
1812

    
1813
        /**
1814
         * Shows the finish button if enabled.
1815
         *
1816
         * @property enableFinishButton
1817
         * @type Boolean
1818
         * @default true
1819
         * @for defaults
1820
         **/
1821
        enableFinishButton: true,
1822

    
1823
        /**
1824
         * Not yet implemented.
1825
         *
1826
         * @property preloadContent
1827
         * @type Boolean
1828
         * @default false
1829
         * @for defaults
1830
         **/
1831
        preloadContent: false,
1832

    
1833
        /**
1834
         * Shows the finish button always (on each step; right beside the next button) if `true`.
1835
         * Otherwise the next button will be replaced by the finish button if the last step becomes active.
1836
         *
1837
         * @property showFinishButtonAlways
1838
         * @type Boolean
1839
         * @default false
1840
         * @for defaults
1841
         **/
1842
        showFinishButtonAlways: false,
1843

    
1844
        /**
1845
         * Prevents jumping to a previous step.
1846
         *
1847
         * @property forceMoveForward
1848
         * @type Boolean
1849
         * @default false
1850
         * @for defaults
1851
         **/
1852
        forceMoveForward: false,
1853

    
1854
        /**
1855
         * Saves the current state (step position) to a cookie.
1856
         * By coming next time the last active step becomes activated.
1857
         *
1858
         * @property saveState
1859
         * @type Boolean
1860
         * @default false
1861
         * @for defaults
1862
         **/
1863
        saveState: false,
1864

    
1865
        /**
1866
         * The position to start on (zero-based).
1867
         *
1868
         * @property startIndex
1869
         * @type Integer
1870
         * @default 0
1871
         * @for defaults
1872
         **/
1873
        startIndex: 0,
1874

    
1875
        /*
1876
         * Animation Effect Configuration
1877
         */
1878

    
1879
        /**
1880
         * The animation effect which will be used for step transitions.
1881
         *
1882
         * @property transitionEffect
1883
         * @type transitionEffect
1884
         * @default none
1885
         * @for defaults
1886
         **/
1887
        transitionEffect: transitionEffect.none,
1888

    
1889
        /**
1890
         * Animation speed for step transitions (in milliseconds).
1891
         *
1892
         * @property transitionEffectSpeed
1893
         * @type Integer
1894
         * @default 200
1895
         * @for defaults
1896
         **/
1897
        transitionEffectSpeed: 280,
1898

    
1899
        /*
1900
         * Events
1901
         */
1902

    
1903
        /**
1904
         * Fires before the step changes and can be used to prevent step changing by returning `false`.
1905
         * Very useful for form validation.
1906
         *
1907
         * @property onStepChanging
1908
         * @type Event
1909
         * @default function (event, currentIndex, newIndex) { return true; }
1910
         * @for defaults
1911
         **/
1912
        onStepChanging: function (event, currentIndex, newIndex) { return true; },
1913

    
1914
        /**
1915
         * Fires after the step has change.
1916
         *
1917
         * @property onStepChanged
1918
         * @type Event
1919
         * @default function (event, currentIndex, priorIndex) { }
1920
         * @for defaults
1921
         **/
1922
        onStepChanged: function (event, currentIndex, priorIndex) { },
1923

    
1924
        /**
1925
         * Fires after cancelation.
1926
         *
1927
         * @property onCanceled
1928
         * @type Event
1929
         * @default function (event) { }
1930
         * @for defaults
1931
         **/
1932
        onCanceled: function (event) { },
1933

    
1934
        /**
1935
         * Fires before finishing and can be used to prevent completion by returning `false`.
1936
         * Very useful for form validation.
1937
         *
1938
         * @property onFinishing
1939
         * @type Event
1940
         * @default function (event, currentIndex) { return true; }
1941
         * @for defaults
1942
         **/
1943
        onFinishing: function (event, currentIndex) { return true; },
1944

    
1945
        /**
1946
         * Fires after completion.
1947
         *
1948
         * @property onFinished
1949
         * @type Event
1950
         * @default function (event, currentIndex) { }
1951
         * @for defaults
1952
         **/
1953
        onFinished: function (event, currentIndex) { },
1954

    
1955
        /**
1956
         * Fires after async content is loaded.
1957
         *
1958
         * @property onContentLoaded
1959
         * @type Event
1960
         * @default function (event, index) { }
1961
         * @for defaults
1962
         **/
1963
        onContentLoaded: function (event, currentIndex) { },
1964

    
1965
        /**
1966
         * Fires when the wizard is initialized.
1967
         *
1968
         * @property onInit
1969
         * @type Event
1970
         * @default function (event) { }
1971
         * @for defaults
1972
         **/
1973
        onInit: function (event, currentIndex) { },
1974

    
1975
        /**
1976
         * Contains all labels.
1977
         *
1978
         * @property labels
1979
         * @type Object
1980
         * @for defaults
1981
         **/
1982
        labels: {
1983
            /**
1984
             * Label for the cancel button.
1985
             *
1986
             * @property cancel
1987
             * @type String
1988
             * @default "Cancel"
1989
             * @for defaults
1990
             **/
1991
            cancel: "Cancel",
1992

    
1993
            /**
1994
             * This label is important for accessability reasons.
1995
             * Indicates which step is activated.
1996
             *
1997
             * @property current
1998
             * @type String
1999
             * @default "current step:"
2000
             * @for defaults
2001
             **/
2002
            current: "current step:",
2003

    
2004
            /**
2005
             * This label is important for accessability reasons and describes the kind of navigation.
2006
             *
2007
             * @property pagination
2008
             * @type String
2009
             * @default "Pagination"
2010
             * @for defaults
2011
             * @since 0.9.7
2012
             **/
2013
            pagination: "Pagination",
2014

    
2015
            /**
2016
             * Label for the finish button.
2017
             *
2018
             * @property finish
2019
             * @type String
2020
             * @default "Finish"
2021
             * @for defaults
2022
             **/
2023
            finish: "Finish",
2024

    
2025
            /**
2026
             * Label for the next button.
2027
             *
2028
             * @property next
2029
             * @type String
2030
             * @default "Next"
2031
             * @for defaults
2032
             **/
2033
            next: "Next <i class='material-icons'>&#xE315;</i>",
2034

    
2035
            /**
2036
             * Label for the previous button.
2037
             *
2038
             * @property previous
2039
             * @type String
2040
             * @default "Previous"
2041
             * @for defaults
2042
             **/
2043
            previous: "<i class='material-icons'>&#xE314;</i> Previous",
2044

    
2045
            /**
2046
             * Label for the loading animation.
2047
             *
2048
             * @property loading
2049
             * @type String
2050
             * @default "Loading ..."
2051
             * @for defaults
2052
             **/
2053
            loading: "Loading ..."
2054
        }
2055
    };
2056
})(jQuery);
(26-26/27)