initial commit
[namibia] / public / js / vendor / fuelux-wizard.js
1 /*
2  * Fuel UX Wizard
3  * https://github.com/ExactTarget/fuelux
4  *
5  * Copyright (c) 2012 ExactTarget
6  * Licensed under the MIT license.
7  */
8 ;(function(){
9
10         // WIZARD CONSTRUCTOR AND PROTOTYPE
11
12         var Wizard = function (element, options) {
13                 var kids;
14
15                 this.$element = $(element);
16                 this.options = $.extend({}, $.fn.wizard.defaults, options);
17                 this.options.disablePreviousStep = ( this.$element.data().restrict === "previous" ) ? true : false;
18                 this.currentStep = this.options.selectedItem.step;
19                 this.numSteps = this.$element.find('.steps li').length;
20                 this.$prevBtn = this.$element.find('button.btn-prev');
21                 this.$nextBtn = this.$element.find('button.btn-next');
22
23                 kids = this.$nextBtn.children().detach();
24                 this.nextText = $.trim(this.$nextBtn.text());
25                 this.$nextBtn.append(kids);
26
27                 //this.$element.find( '.steps li:gt(0)' ).addClass( 'handy' );
28
29                 // handle events
30                 this.$prevBtn.on('click', $.proxy(this.previous, this));
31                 this.$nextBtn.on('click', $.proxy(this.next, this));
32                 //this.$element.on('click', '.steps li', $.proxy(this.stepclicked, this));
33
34                 if(this.currentStep > 1) {
35                         this.selectedItem(this.options.selectedItem);
36                 }
37
38                 if( this.options.disablePreviousStep ) {
39                         this.$prevBtn.attr( 'disabled', true );
40                         this.$element.find( '.steps' ).addClass( 'previous-disabled' );
41                 }
42         };
43
44         Wizard.prototype = {
45
46                 constructor: Wizard,
47
48                 setState: function () {
49                         var canMovePrev = (this.currentStep > 1);
50                         var firstStep = (this.currentStep === 1);
51                         var lastStep = (this.currentStep === this.numSteps);
52
53                         // disable buttons based on current step
54                         if( !this.options.disablePreviousStep ) {
55                                 this.$prevBtn.attr('disabled', (firstStep === true || canMovePrev === false));
56                         }
57
58                         // change button text of last step, if specified
59                         var data = this.$nextBtn.data();
60                         if (data && data.last) {
61                                 this.lastText = data.last;
62                                 if (typeof this.lastText !== 'undefined') {
63                                         // replace text
64                                         var text = (lastStep !== true) ? this.nextText : this.lastText;
65                                         var kids = this.$nextBtn.children().detach();
66                                         this.$nextBtn.text(text).append(kids);
67                                 }
68                         }
69
70                         // reset classes for all steps
71                         var $steps = this.$element.find('.steps li');
72                         $steps.removeClass('active')
73                                 .removeClass('complete')
74                                 .removeClass('handy');
75                         $steps.find('span.badge')
76                                 .removeClass('badge-info')
77                                 .removeClass('badge-success');
78
79                         // set class for all previous steps
80                         var prevSelector = '.steps li:lt(' + (this.currentStep - 1) + ')';
81                         var $prevSteps = this.$element.find(prevSelector);
82                         $prevSteps.addClass('complete');
83                         $prevSteps.find('span.badge').addClass('badge-success');
84
85                         // set class for current step
86                         var currentSelector = '.steps li:eq(' + (this.currentStep - 1) + ')';
87                         var $currentStep = this.$element.find(currentSelector);
88                         $currentStep.addClass('active');
89                         $currentStep.find('span.badge').addClass('badge-info');
90
91                         // set class of next steps
92                         var nextSelector = '.steps li:gt(' + (this.currentStep - 1) + ')';
93                         var $nextSteps = this.$element.find(nextSelector);
94                         $nextSteps.addClass('handy');
95
96                         // set display of target element
97                         $('.step-pane.active').removeClass('active');
98                         $('.step-pane[data-step=' + $currentStep.data().step + ']').addClass('active');
99
100                         // reset the wizard position to the left
101                         this.$element.find('.steps').first().attr('style','margin-left: 0');
102
103                         // check if the steps are wider than the container div
104                         var totalWidth = 0;
105                         this.$element.find('.steps > li').each(function () {
106                                 totalWidth += $(this).outerWidth();
107                         });
108                         var containerWidth = 0;
109                         if (this.$element.find('.actions').length) {
110                                 containerWidth = this.$element.width() - this.$element.find('.actions').first().outerWidth();
111                         } else {
112                                 containerWidth = this.$element.width();
113                         }
114                         if (totalWidth > containerWidth) {
115
116                                 // set the position so that the last step is on the right
117                                 var newMargin = totalWidth - containerWidth;
118                                 this.$element.find('.steps').first().attr('style','margin-left: -' + newMargin + 'px');
119
120                                 // set the position so that the active step is in a good
121                                 // position if it has been moved out of view
122                                 if (this.$element.find('li.active').first().position().left < 200) {
123                                         newMargin += this.$element.find('li.active').first().position().left - 200;
124                                         if (newMargin < 1) {
125                                                 this.$element.find('.steps').first().attr('style','margin-left: 0');
126                                         } else {
127                                                 this.$element.find('.steps').first().attr('style','margin-left: -' + newMargin + 'px');
128                                         }
129                                 }
130                         }
131
132                         this.$element.trigger('changed', { currentStep: this.currentStep });
133                 },
134
135                 stepclicked: function (e) {
136                         var li          = $(e.currentTarget);
137                         var index       = this.$element.find('.steps li').index(li);
138                         var canMovePrev = true;
139
140                         if( this.options.disablePreviousStep ) {
141                                 if( index < this.currentStep ) {
142                                         canMovePrev = false;
143                                 }
144                         }
145
146                         if( canMovePrev ) {
147                                 var evt = $.Event('stepclick');
148                                 this.$element.trigger(evt, {step: index + 1});
149                                 if (evt.isDefaultPrevented()) return;
150
151                                 this.currentStep = (index + 1);
152                                 this.setState();
153                         }
154                 },
155
156                 previous: function () {
157                         var canMovePrev = (this.currentStep > 1);
158                         if( this.options.disablePreviousStep ) {
159                                 canMovePrev = false;
160                         }
161                         if (canMovePrev) {
162                                 var e = $.Event('change');
163                                 this.$element.trigger(e, {step: this.currentStep, direction: 'previous'});
164                                 if (e.isDefaultPrevented()) return;
165
166                                 this.currentStep -= 1;
167                                 this.setState();
168                         }
169                 },
170
171                 next: function () {
172                         var canMoveNext = (this.currentStep + 1 <= this.numSteps);
173                         var lastStep = (this.currentStep === this.numSteps);
174
175                         if (canMoveNext) {
176                                 var e = $.Event('change');
177                                 this.$element.trigger(e, {step: this.currentStep, direction: 'next'});
178
179                                 if (e.isDefaultPrevented()) return;
180
181                                 this.currentStep += 1;
182                                 this.setState();
183                         }
184                         else if (lastStep) {
185                                 this.$element.trigger('finished');
186                         }
187                 },
188
189                 selectedItem: function (selectedItem) {
190                         var retVal, step;
191
192                         if(selectedItem) {
193
194                                 step = selectedItem.step || -1;
195
196                                 if(step >= 1 && step <= this.numSteps) {
197                                         this.currentStep = step;
198                                         this.setState();
199                                 }
200
201                                 retVal = this;
202                         }
203                         else {
204                                 retVal = { step: this.currentStep };
205                         }
206
207                         return retVal;
208                 }
209         };
210
211
212         // WIZARD PLUGIN DEFINITION
213
214         $.fn.wizard = function (option) {
215                 var args = Array.prototype.slice.call( arguments, 1 );
216                 var methodReturn;
217
218                 var $set = this.each(function () {
219                         var $this   = $( this );
220                         var data    = $this.data( 'wizard' );
221                         var options = typeof option === 'object' && option;
222
223                         if( !data ) $this.data('wizard', (data = new Wizard( this, options ) ) );
224                         if( typeof option === 'string' ) methodReturn = data[ option ].apply( data, args );
225                 });
226
227                 return ( methodReturn === undefined ) ? $set : methodReturn;
228         };
229
230         $.fn.wizard.defaults = {
231         selectedItem: {step:1}
232         };
233
234         $.fn.wizard.Constructor = Wizard;
235
236         $.fn.wizard.noConflict = function () {
237                 return this;
238         };
239
240
241         // WIZARD DATA-API
242
243         $(function () {
244                 $('body').on('mouseover.wizard.data-api', '.wizard', function () {
245                         var $this = $(this);
246                         if ($this.data('wizard')) return;
247                         $this.wizard($this.data());
248                 });
249         });
250 })();