source: other-projects/nz-flag-design/trunk/main-form/js/liteaccordion.jquery.js@ 29530

Last change on this file since 29530 was 29530, checked in by davidb, 9 years ago

First cut at overview web pages to nz-flag-design project

File size: 16.8 KB
Line 
1/*************************************************!
2*
3* project: liteAccordion - a horizontal accordion plugin for jQuery
4* author: Nicola Hibbert
5* url: http://nicolahibbert.com/liteaccordion-v2/
6* demo: http://www.nicolahibbert.com/demo/liteAccordion/
7*
8* Version: 2.2.0
9* Copyright: (c) 2010-2013 Nicola Hibbert
10* Licence: MIT
11*
12**************************************************/
13
14;(function($) {
15
16 var LiteAccordion = function(elem, options) {
17
18 var defaults = {
19 containerWidth : 960, // fixed (px)
20 containerHeight : 320, // fixed (px)
21 headerWidth : 48, // fixed (px)
22
23 activateOn : 'click', // click or mouseover
24 firstSlide : 1, // displays slide (n) on page load
25 slideSpeed : 800, // slide animation speed
26 onTriggerSlide : function(e) {}, // callback on slide activate
27 onSlideAnimComplete : function() {}, // callback on slide anim complete
28
29 autoPlay : false, // automatically cycle through slides
30 pauseOnHover : false, // pause on hover
31 cycleSpeed : 6000, // time between slide cycles
32 easing : 'swing', // custom easing function
33
34 theme : 'basic', // basic, dark, light, or stitch
35 rounded : false, // square or rounded corners
36 enumerateSlides : false, // put numbers on slides
37 linkable : false // link slides via hash
38 },
39
40 // merge defaults with options in new settings object
41 settings = $.extend({}, defaults, options),
42
43 // 'globals'
44 slides = elem.children('ol').children('li'),
45 header = slides.children(':first-child'),
46 slideLen = slides.length,
47 slideWidth = settings.containerWidth - slideLen * settings.headerWidth,
48
49 // public methods
50 methods = {
51
52 // start elem animation
53 play : function(index) {
54 var next = core.nextSlide(index && index);
55
56 if (core.playing) return;
57
58 // start autoplay
59 core.playing = setInterval(function() {
60 header.eq(next()).trigger('click.liteAccordion');
61 }, settings.cycleSpeed);
62 },
63
64 // stop elem animation
65 stop : function() {
66 clearInterval(core.playing);
67 core.playing = 0;
68 },
69
70 // trigger next slide
71 next : function() {
72 methods.stop();
73 header.eq(core.currentSlide === slideLen - 1 ? 0 : core.currentSlide + 1).trigger('click.liteAccordion');
74 },
75
76 // trigger previous slide
77 prev : function() {
78 methods.stop();
79 header.eq(core.currentSlide - 1).trigger('click.liteAccordion');
80 },
81
82 // destroy plugin instance
83 destroy : function() {
84 // stop autoplay
85 methods.stop();
86
87 // remove hashchange event bound to window
88 $(window).off('.liteAccordion');
89
90 // remove generated styles, classes, data, events
91 elem
92 .attr('style', '')
93 .removeClass('liteAccordion basic dark light stitch')
94 .removeData('liteAccordion')
95 .off('.liteAccordion')
96 .find('li > :first-child')
97 .off('.liteAccordion')
98 .filter('.selected')
99 .removeClass('selected')
100 .end()
101 .find('b')
102 .remove();
103
104 slides
105 .removeClass('slide')
106 .children()
107 .attr('style', '');
108 },
109
110 // poke around the internals (NOT CHAINABLE)
111 debug : function() {
112 return {
113 elem : elem,
114 defaults : defaults,
115 settings : settings,
116 methods : methods,
117 core : core
118 };
119 }
120 },
121
122 // core utility and animation methods
123 core = {
124
125 // set style properties
126 setStyles : function() {
127 // set container height and width, theme and corner style
128 elem
129 .width(settings.containerWidth)
130 .height(settings.containerHeight)
131 .addClass('liteAccordion')
132 .addClass(settings.rounded && 'rounded')
133 .addClass(settings.theme);
134
135 // set slide heights
136 slides
137 .addClass('slide')
138 .children(':first-child')
139 .height(settings.headerWidth);
140
141 // set slide positions
142 core.setSlidePositions();
143 },
144
145 // set initial positions for each slide
146 setSlidePositions : function() {
147 var selected = header.filter('.selected');
148
149 // account for already selected slide
150 if (!selected.length) header.eq(settings.firstSlide - 1).addClass('selected');
151
152 header.each(function(index) {
153 var $this = $(this),
154 left = index * settings.headerWidth,
155 margin = header.first().next(),
156 offset = parseInt(margin.css('marginLeft'), 10) || parseInt(margin.css('marginRight'), 10) || 0;
157
158 // compensate for already selected slide on resize
159 if (selected.length) {
160 if (index > header.index(selected)) left += slideWidth;
161 } else {
162 if (index >= settings.firstSlide) left += slideWidth;
163 }
164
165 // set each slide position
166 $this
167 .css('left', left)
168 .width(settings.containerHeight)
169 .next()
170 .width(slideWidth - offset)
171 .css({ left : left, paddingLeft : settings.headerWidth });
172
173 // add number to bottom of tab
174 settings.enumerateSlides && $this.append('<b>' + (index + 1) + '</b>');
175
176 });
177 },
178
179 // bind events
180 bindEvents : function() {
181 // bind click and mouseover events
182 if (settings.activateOn === 'click') {
183 header.on('click.liteAccordion', core.triggerSlide);
184 } else if (settings.activateOn === 'mouseover') {
185 header.on('click.liteAccordion mouseover.liteAccordion', core.triggerSlide);
186 }
187
188 // bind hashchange event
189 if (settings.linkable) {
190 $(window).on('hashchange.liteAccordion', function(e) {
191 var url = slides.filter(function() {
192 return $(this).attr('data-slide-name') === window.location.hash.split('#')[1];
193 });
194
195 // if slide name exists
196 if (url.length) {
197 // trigger slide
198 core.triggerSlide.call(url.children('h2')[0], e);
199 }
200 });
201 }
202
203 // pause on hover (can't use custom events with $.hover())
204 if (settings.pauseOnHover && settings.autoPlay) {
205 elem
206 .on('mouseover.liteAccordion', function() {
207 core.playing && methods.stop();
208 })
209 .on('mouseout.liteAccordion', function() {
210 !core.playing && methods.play(core.currentSlide);
211 });
212 }
213 },
214
215 // counter for autoPlay (zero index firstSlide on init)
216 currentSlide : settings.firstSlide - 1,
217
218 // next slide index
219 nextSlide : function(index) {
220 var next = index + 1 || core.currentSlide + 1;
221
222 // closure
223 return function() {
224 return next++ % slideLen;
225 };
226 },
227
228 // holds interval counter
229 playing : 0,
230
231 slideAnimCompleteFlag : false,
232
233 // trigger slide animation
234 triggerSlide : function(e) {
235 var $this = $(this),
236 tab = {
237 elem : $this,
238 index : header.index($this),
239 next : $this.next(),
240 prev : $this.parent().prev().children('h2'),
241 parent : $this.parent()
242 };
243
244 // current hash not correct?
245 if (settings.linkable && tab.parent.attr('data-slide-name')) {
246 if (tab.parent.attr('data-slide-name') !== window.location.hash.split('#')[1]) {
247 // exit early and try again (prevents double trigger (issue #60))
248 return window.location.hash = '#' + tab.parent.attr('data-slide-name');
249 }
250 }
251
252 // update core.currentSlide
253 core.currentSlide = tab.index;
254
255 // reset onSlideAnimComplete callback flag
256 core.slideAnimCompleteFlag = false;
257
258 // trigger callback in context of sibling div (jQuery wrapped)
259 settings.onTriggerSlide.call(tab.next, $this);
260
261 // animate
262 if ($this.hasClass('selected') && $this.position().left < slideWidth / 2) {
263 // animate single selected tab
264 core.animSlide.call(tab);
265 } else {
266 // animate groups
267 core.animSlideGroup(tab);
268 }
269
270 // stop autoplay, reset current slide index in core.nextSlide closure
271 if (settings.autoPlay) {
272 methods.stop();
273 methods.play(header.index(header.filter('.selected')));
274 }
275 },
276
277 animSlide : function(triggerTab) {
278 var _this = this;
279
280 // set pos for single selected tab
281 if (typeof this.pos === 'undefined') this.pos = slideWidth;
282
283 // remove, then add selected class
284 header.removeClass('selected').filter(this.elem).addClass('selected');
285
286 // if slide index not zero
287 if (!!this.index) {
288 this.elem
289 .add(this.next)
290 .stop(true)
291 .animate({
292 left : this.pos + this.index * settings.headerWidth
293 },
294 settings.slideSpeed,
295 settings.easing,
296 function() {
297 // flag ensures that fn is only called one time per triggerSlide
298 if (!core.slideAnimCompleteFlag) {
299 // trigger onSlideAnimComplete callback in context of sibling div (jQuery wrapped)
300 settings.onSlideAnimComplete.call(triggerTab ? triggerTab.next : _this.prev.next());
301 core.slideAnimCompleteFlag = true;
302 }
303 });
304
305 // remove, then add selected class
306 header.removeClass('selected').filter(this.prev).addClass('selected');
307
308 }
309 },
310
311 // animates left and right groups of slides
312 animSlideGroup : function(triggerTab) {
313 var group = ['left', 'right'];
314
315 $.each(group, function(index, side) {
316 var filterExpr, left;
317
318 if (side === 'left') {
319 filterExpr = ':lt(' + (triggerTab.index + 1) + ')';
320 left = 0;
321 } else {
322 filterExpr = ':gt(' + triggerTab.index + ')';
323 left = slideWidth;
324 }
325
326 slides
327 .filter(filterExpr)
328 .children('h2')
329 .each(function() {
330 var $this = $(this),
331 tab = {
332 elem : $this,
333 index : header.index($this),
334 next : $this.next(),
335 prev : $this.parent().prev().children('h2'),
336 pos : left
337 };
338
339 // trigger item anim, pass original trigger context for callback fn
340 core.animSlide.call(tab, triggerTab);
341 });
342
343 });
344
345 // remove, then add selected class
346 header.removeClass('selected').filter(triggerTab.elem).addClass('selected');
347 },
348
349 ieClass : function(version) {
350 if (version < 7) methods.destroy();
351 if (version >= 10) return;
352 if (version === 7 || version === 8) {
353 slides.each(function(index) {
354 $(this).addClass('slide-' + index);
355 });
356 }
357
358 elem.addClass('ie ie' + version);
359 },
360
361 init : function() {
362 var ua = navigator.userAgent,
363 index = ua.indexOf('MSIE');
364
365 // test for ie
366 if (index !== -1) {
367 ua = ua.slice(index + 5, index + 7);
368 core.ieClass(+ua);
369 }
370
371 // init styles and events
372 core.setStyles();
373 core.bindEvents();
374
375 // check slide speed is not faster than cycle speed
376 if (settings.cycleSpeed < settings.slideSpeed) settings.cycleSpeed = settings.slideSpeed;
377
378 // init autoplay
379 settings.autoPlay && methods.play();
380 }
381 };
382
383 // init plugin
384 core.init();
385
386 // expose methods
387 return methods;
388
389 };
390
391 $.fn.liteAccordion = function(method) {
392 var elem = this,
393 instance = elem.data('liteAccordion');
394
395 // if creating a new instance
396 if (typeof method === 'object' || !method) {
397 return elem.each(function() {
398 var liteAccordion;
399
400 // if plugin already instantiated, return
401 if (instance) return;
402
403 // otherwise create a new instance
404 liteAccordion = new LiteAccordion(elem, method);
405 elem.data('liteAccordion', liteAccordion);
406 });
407
408 // otherwise, call method on current instance
409 } else if (typeof method === 'string' && instance[method]) {
410 // debug method isn't chainable b/c we need the debug object to be returned
411 if (method === 'debug') {
412 return instance[method].call(elem);
413 } else { // the rest of the methods are chainable though
414 instance[method].call(elem);
415 return elem;
416 }
417 }
418 };
419
420})(jQuery);
Note: See TracBrowser for help on using the repository browser.