PageSourceSearch

https://www.clickcover.co.nz/edit/scripts/jquery.unslider.min.js

js clickcover.co.nz collected 2026-10-04 21:44:25 UTC 20,096 bytes, 684 lines download raw bytes

1/**
2 *   Unslider
3 *   version 2.0
4 *   by @idiot and friends
5 */
6
7(function(factory) {
8	if (typeof module === 'object' && typeof module.exports === 'object') {
9		factory(require('jquery'));
10	} else if (typeof define === 'function' && define.amd) {
11	    // AMD. Register as an anonymous module.
12        define([], factory(window.jQuery));
13    } else {
14		factory(window.jQuery);
15	}
16}(function($) {
17	//  Don't throw any errors when jQuery
18	if(!$) {
19		return console.warn('Unslider needs jQuery');
20	}
21
22	$.Unslider = function(context, options) {
23		var self = this;
24
25		//  Create an Unslider reference we can use everywhere
26		self._ = 'unslider';
27
28		//  Store our default options in here
29		//  Everything will be overwritten by the jQuery plugin though
30		self.defaults = {
31			//  Should the slider move on its own or only when
32			//  you interact with the nav/arrows?
33			//  Only accepts boolean true/false.
34			autoplay: false,
35
36			//  3 second delay between slides moving, pass
37			//  as a number in milliseconds.
38			delay: 3000,
39
40			//  Animation speed in millseconds
41			speed: 750,
42
43			//  An easing string to use. If you're using Velocity, use a
44			//  Velocity string otherwise you can use jQuery/jQ UI options.
45			easing: 'swing', // [.42, 0, .58, 1],
46
47			//  Does it support keyboard arrows?
48			//  Can pass either true or false -
49			//  or an object with the keycodes, like so:
50			//  {
51			//	 prev: 37,
52			//	 next: 39
53			// }
54			//  You can call any internal method name
55			//  before the keycode and it'll be called.
56			keys: {
57				prev: 37,
58				next: 39
59			},
60
61			//  Do you want to generate clickable navigation
62			//  to skip to each slide? Accepts boolean true/false or
63			//  a callback function per item to generate.
64			nav: true,
65
66			//  Should there be left/right arrows to go back/forth?
67			//   -> This isn't keyboard support.
68			//  Either set true/false, or an object with the HTML
69			//  elements for each arrow like below:
70			arrows: {
71				prev: '<a class="' + self._ + '-arrow prev">Prev</a>',
72				next: '<a class="' + self._ + '-arrow next">Next</a>'
73			},
74
75			//  How should Unslider animate?
76			//  It can do one of the following types:
77			//  "fade": each slide fades in to each other
78			//  "horizontal": each slide moves from left to right
79			//  "vertical": each slide moves from top to bottom
80			animation: 'horizontal',
81
82			//  If you don't want to use a list to display your slides,
83			//  you can change it here. Not recommended and you'll need
84			//  to adjust the CSS accordingly.
85			selectors: {
86				container: 'ul:first',
87				slides: 'li'
88			},
89
90			//  Do you want to animate the heights of each slide as
91			//  it moves
92			animateHeight: false,
93
94			//  Active class for the nav
95			activeClass: self._ + '-active',
96
97			//  Have swipe support?
98			//  You can set this here with a boolean and always use
99			//  initSwipe/destroySwipe later on.
100			swipe: true,
101			// Swipe threshold -
102			// lower float for enabling short swipe
103			swipeThreshold: 0.2
104		};
105
106		//  Set defaults
107		self.$context = context;
108		self.options = {};
109
110		//  Leave our elements blank for now
111		//  Since they get changed by the options, we'll need to
112		//  set them in the init method.
113		self.$parent = null;
114		self.$container = null;
115		self.$slides = null;
116		self.$nav = null;
117		self.$arrows = [];
118
119		//  Set our indexes and totals
120		self.total = 0;
121		self.current = 0;
122
123		//  Generate a specific random ID so we don't dupe events
124		self.prefix = self._ + '-';
125		self.eventSuffix = '.' + self.prefix + ~~(Math.random() * 2e3);
126
127		//  In case we're going to use the autoplay
128		self.interval = [];
129
130		//  Get everything set up innit
131		self.init = function(options) {
132			//  Set up our options inside here so we can re-init at
133			//  any time
134			self.options = $.extend({}, self.defaults, options);
135
136			//  Our elements
137			self.$container = self.$context.find(self.options.selectors.container).addClass(self.prefix + 'wrap');
138			self.$slides = self.$container.children(self.options.selectors.slides);
139
140			//  We'll manually init the container
141			self.setup();
142
143			//  We want to keep this script as small as possible
144			//  so we'll optimise some checks
145			$.each(['nav', 'arrows', 'keys', 'infinite'], function(index, module) {
146				self.options[module] && self['init' + $._ucfirst(module)]();
147			});
148
149			//  Add swipe support
150			if(jQuery.event.special.swipe && self.options.swipe) {
151				self.initSwipe();
152			}
153
154			//  If autoplay is set to true, call self.start()
155			//  to start calling our timeouts
156			self.options.autoplay && self.start();
157
158			//  We should be able to recalculate slides at will
159			self.calculateSlides();
160
161			//  Listen to a ready event
162			self.$context.trigger(self._ + '.ready');
163
164			//  Everyday I'm chainin'
165			return self.animate(self.options.index || self.current, 'init');
166		};
167
168		self.setup = function() {
169			//  Add a CSS hook to the main element
170			self.$context.addClass(self.prefix + self.options.animation).wrap('<div class="' + self._ + '" />');
171			self.$parent = self.$context.parent('.' + self._);
172
173			//  We need to manually check if the container is absolutely
174			//  or relatively positioned
175			var position = self.$context.css('position');
176
177			//  If we don't already have a position set, we'll
178			//  automatically set it ourselves
179			if(position === 'static') {
180				self.$context.css('position', 'relative');
181			}
182
183			self.$context.css('overflow', 'hidden');
184		};
185
186		//  Set up the slide widths to animate with
187		//  so the box doesn't float over
188		self.calculateSlides = function() {
189			// update slides before recalculating the total
190			self.$slides = self.$container.children(self.options.selectors.slides);
191
192			self.total = self.$slides.length;
193
194			//  Set the total width
195			if(self.options.animation !== 'fade') {
196				var prop = 'width';
197
198				if(self.options.animation === 'vertical') {
199					prop = 'height';
200				}
201
202				self.$container.css(prop, (self.total * 100) + '%').addClass(self.prefix + 'carousel');
203				self.$slides.css(prop, (100 / self.total) + '%');
204			}
205		};
206
207
208		//  Start our autoplay
209		self.start = function() {
210			self.interval.push(setTimeout(function() {
211				//  Move on to the next slide
212				self.next();
213
214				//  If we've got autoplay set up
215				//  we don't need to keep starting
216				//  the slider from within our timeout
217				//  as .animate() calls it for us
218			}, self.options.delay));
219
220			return self;
221		};
222
223		//  And pause our timeouts
224		//  and force stop the slider if needed
225		self.stop = function() {
226            var timeout;
227            while(timeout = self.interval.pop()) {
228                clearTimeout(timeout);
229            }
230
231			return self;
232		};
233
234
235		//  Set up our navigation
236		self.initNav = function() {
237			var $nav = $('<nav class="' + self.prefix + 'nav"><ol /></nav>');
238
239			//  Build our click navigation item-by-item
240			self.$slides.each(function(key) {
241				//  If we've already set a label, let's use that
242				//  instead of generating one
243				var label = this.getAttribute('data-nav') || key + 1;
244
245				//  Listen to any callback functions
246				if($.isFunction(self.options.nav)) {
247					label = self.options.nav.call(self.$slides.eq(key), key, label);
248				}
249
250				//  And add it to our navigation item
251				$nav.children('ol').append('<li data-slide="' + key + '">' + label + '</li>');
252			});
253
254			//  Keep a copy of the nav everywhere so we can use it
255			self.$nav = $nav.insertAfter(self.$context);
256
257			//  Now our nav is built, let's add it to the slider and bind
258			//  for any click events on the generated links
259			self.$nav.find('li').on('click' + self.eventSuffix, function() {
260				//  Cache our link and set it to be active
261				var $me = $(this).addClass(self.options.activeClass);
262
263				//  Set the right active class, remove any other ones
264				$me.siblings().removeClass(self.options.activeClass);
265
266				//  Move the slide
267				self.animate($me.attr('data-slide'));
268			});
269		};
270
271
272		//  Set up our left-right arrow navigation
273		//  (Not keyboard arrows, prev/next buttons)
274		self.initArrows = function() {
275			if(self.options.arrows === true) {
276				self.options.arrows = self.defaults.arrows;
277			}
278
279			//  Loop our options object and bind our events
280			$.each(self.options.arrows, function(key, val) {
281				//  Add our arrow HTML and bind it
282				self.$arrows.push(
283					$(val).insertAfter(self.$context).on('click' + self.eventSuffix, self[key])
284				);
285			});
286		};
287
288
289		//  Set up our keyboad navigation
290		//  Allow binding to multiple keycodes
291		self.initKeys = function() {
292			if(self.options.keys === true) {
293				self.options.keys = self.defaults.keys;
294			}
295
296			$(document).on('keyup' + self.eventSuffix, function(e) {
297				$.each(self.options.keys, function(key, val) {
298					if(e.which === val) {
299						$.isFunction(self[key]) && self[key].call(self);
300					}
301				});
302			});
303		};
304
305		//  Requires jQuery.event.swipe
306		//  -> stephband.info/jquery.event.swipe
307		self.initSwipe = function() {
308			var width = self.$slides.width();
309
310			//  We don't want to have a tactile swipe in the slider
311			//  in the fade animation, as it can cause some problems
312			//  with layout, so we'll just disable it.
313			if(self.options.animation !== 'fade') {
314
315				self.$container.on({
316
317					movestart: function(e) {
318						//  If the movestart heads off in a upwards or downwards
319						//  direction, prevent it so that the browser scrolls normally.
320						if((e.distX > e.distY && e.distX < -e.distY) || (e.distX < e.distY && e.distX > -e.distY)) {
321							return !!e.preventDefault();
322						}
323
324						self.$container.css('position', 'relative');
325					},
326
327					move: function(e) {
328						self.$container.css('left', -(100 * self.current) + (100 * e.distX / width) + '%');
329					},
330
331					moveend: function(e) {
332						// Check if swiped distance is greater than threshold.
333						// If yes slide to next/prev slide. If not animate to
334						// starting point.
335
336						if((Math.abs(e.distX) / width) > self.options.swipeThreshold) {
337
338							self[e.distX < 0 ? 'next' : 'prev']();
339						}
340						else {
341
342							self.$container.animate({left: -(100 * self.current) + '%' }, self.options.speed / 2 );
343						}
344					}
345				});
346			}
347		};
348
349		//  Infinite scrolling is a massive pain in the arse
350		//  so we need to create a whole bloody function to set
351		//  it up. Argh.
352		self.initInfinite = function() {
353			var pos = ['first', 'last'];
354
355			$.each(pos, function(index, item) {
356				self.$slides.push.apply(
357					self.$slides,
358
359					//  Exclude all cloned slides and call .first() or .last()
360					//  depending on what `item` is.
361					self.$slides.filter(':not(".' + self._ + '-clone")')[item]()
362
363					//  Make a copy of it and identify it as a clone
364					.clone().addClass(self._ + '-clone')
365
366					//  Either insert before or after depending on whether we're
367					//  the first or last clone
368					['insert' + (index === 0 ? 'After' : 'Before')](
369						//  Return the other element in the position array
370						//  if item = first, return "last"
371						self.$slides[pos[~~!index]]()
372					)
373				);
374			});
375		};
376
377		//  Remove any trace of arrows
378		//  Loop our array of arrows and use jQuery to remove
379		//  It'll unbind any event handlers for us
380		self.destroyArrows = function() {
381			$.each(self.$arrows, function(i, $arrow) {
382				$arrow.remove();
383			});
384		};
385
386		//  Remove any swipe events and reset the position
387		self.destroySwipe = function() {
388			//  We bind to 4 events, so we'll unbind those
389			self.$container.off('movestart move moveend');
390		};
391
392		//  Unset the keyboard navigation
393		//  Remove the handler
394		self.destroyKeys = function() {
395			//  Remove the event handler
396			$(document).off('keyup' + self.eventSuffix);
397		};
398
399		self.setIndex = function(to) {
400			if(to < 0) {
401				to = self.total - 1;
402			}
403
404			self.current = Math.min(Math.max(0, to), self.total - 1);
405
406			if(self.options.nav) {
407				self.$nav.find('[data-slide="' + self.current + '"]')._active(self.options.activeClass);
408			}
409
410			self.$slides.eq(self.current)._active(self.options.activeClass);
411
412			return self;
413		};
414
415		//  Despite the name, this doesn't do any animation - since there's
416		//  now three different types of animation, we let this method delegate
417		//  to the right type, keeping the name for backwards compat.
418		self.animate = function(to, dir) {
419			//  Animation shortcuts
420			//  Instead of passing a number index, we can now
421			//  use .data('unslider').animate('last');
422			//  or .unslider('animate:last')
423			//  to go to the very last slide
424			if(to === 'first') to = 0;
425			if(to === 'last') to = self.total;
426
427			//  Don't animate if it's not a valid index
428			if(isNaN(to)) {
429				return self;
430			}
431
432			if(self.options.autoplay) {
433				self.stop().start();
434			}
435
436			self.setIndex(to);
437			
438			// Temp to for slide effect
439			var tempTo = to;
440			var tempTo2 = false;
441			if(self.options.animation !== 'fade') {
442				//
443				tempTo = parseInt(to)+1;
444				//
445				if(tempTo >= (self.total-1) && dir !== 'prev') {tempTo = 1;tempTo2 = (self.total-1);}
446				if(tempTo >= (self.total-1) && dir === 'prev') {tempTo = (self.total-2);tempTo2 = 0;}
447				//console.log(tempTo2);
448			}
449
450			//  Add a callback method to do stuff with
451			self.$context.trigger(self._ + '.change', [tempTo, self.$slides.eq(tempTo), ((tempTo2 !== false) ? self.$slides.eq(tempTo2) : false)]);
452
453			//  Delegate the right method - everything's named consistently
454			//  so we can assume it'll be called "animate" +
455			var fn = 'animate' + $._ucfirst(self.options.animation);
456
457			//  Make sure it's a valid animation method, otherwise we'll get
458			//  a load of bug reports that'll be really hard to report
459			if($.isFunction(self[fn])) {
460				self[fn](self.current, dir);
461			}
462
463			return self;
464		};
465
466
467		//  Shortcuts for animating if we don't know what the current
468		//  index is (i.e back/forward)
469		//  For moving forward we need to make sure we don't overshoot.
470		self.next = function() {
471			var target = self.current + 1;
472			var tempTotal = (self.options.animation === 'fade') ? self.total-2 : self.total;
473
474			//  If we're at the end, we need to move back to the start
475			if(target >= tempTotal) {
476				if(self.options.noloop && !self.options.infinite) {
477					target = self.total - 1;
478				} else {
479					target = 0;
480				}
481			}
482
483			return self.animate(target, 'next');
484		};
485
486		//  The same as next, but the opposite :)
487		self.prev = function() {
488			var target = self.current - 1;
489
490			//  If we're at the first, we need to move forward to the end
491			if(target < 0) {
492				if(self.options.noloop && !self.options.infinite) {
493					target = 0;
494				} else {
495					target = (self.options.animation === 'fade') ? self.total - 3 : self.total - 1;
496				}
497			}
498			
499			return self.animate(target, 'prev');
500		};
501
502
503		//  Our default animation method, the old-school left-to-right
504		//  horizontal animation
505		self.animateHorizontal = function(to) {
506			var prop = 'left';
507
508			//  Add RTL support, slide the slider
509			//  the other way if the site is right-to-left
510			if(self.$context.attr('dir') === 'rtl') {
511				prop = 'right';
512			}
513
514			if(self.options.infinite) {
515				//  So then we need to hide the first slide
516				self.$container.css('margin-' + prop, '-100%');
517			}
518
519			return self.slide(prop, to);
520		};
521
522		//  The same animation methods, but vertical support
523		//  RTL doesn't affect the vertical direction so we
524		//  can just call as is
525		self.animateVertical = function(to) {
526			self.options.animateHeight = true;
527
528			//  Normal infinite CSS fix doesn't work for
529			//  vertical animation so we need to manually set it
530			//  with pixels. Ah well.
531			if(self.options.infinite) {
532				self.$container.css('margin-top', -self.$slides.outerHeight());
533			}
534
535			return self.slide('top', to);
536		};
537
538		//  Actually move the slide now
539		//  We have to pass a property to animate as there's
540		//  a few different directions it can now move, but it's
541		//  otherwise unchanged from before.
542		self.slide = function(prop, to) {
543			//  If we want to change the height of the slider
544			//  to match the current slide, you can set
545			//  {animateHeight: true}
546			self.animateHeight(to);
547
548			//  For infinite sliding we add a dummy slide at the end and start
549			//  of each slider to give the appearance of being infinite
550			if(self.options.infinite) {
551				var dummy;
552
553				//  Going backwards to last slide
554				if(to === self.total - 1) {
555					//  We're setting a dummy position and an actual one
556					//  the dummy is what the index looks like
557					//  (and what we'll silently update to afterwards),
558					//  and the actual is what makes it not go backwards
559					dummy = self.total - 3;
560					to = -1;
561				}
562
563				//  Going forwards to first slide
564				if(to === self.total - 2) {
565					dummy = 0;
566					to = self.total - 2;
567				}
568
569				//  If it's a number we can safely set it
570				if(typeof dummy === 'number') {
571					self.setIndex(dummy);
572
573					//  Listen for when the slide's finished transitioning so
574					//  we can silently move it into the right place and clear
575					//  this whole mess up.
576					self.$context.on(self._ + '.moved', function() {
577						if(self.current === dummy) {
578							self.$container.css(prop, -(100 * dummy) + '%').off(self._ + '.moved');
579						}
580					});
581				}
582			}
583
584			//  We need to create an object to store our property in
585			//  since we don't know what it'll be.
586			var obj = {};
587
588			//  Manually create it here
589			obj[prop] = -(100 * to) + '%';
590
591			//  And animate using our newly-created object
592			return self._move(self.$container, obj);
593		};
594
595
596		//  Fade between slides rather than, uh, sliding it
597        self.animateFade = function(to) {
598			//  If we want to change the height of the slider
599			//  to match the current slide, you can set
600			//  {animateHeight: true}
601			self.animateHeight(to);
602
603			var $active = self.$slides.eq(to).addClass(self.options.activeClass);
604
605			//  Toggle our classes
606			self._move($active.siblings().removeClass(self.options.activeClass), {opacity: 0});
607			self._move($active, {opacity: 1}, false);
608		};
609
610		// Animate height of slider
611		self.animateHeight = function(to) {
612			//  If we want to change the height of the slider
613			//  to match the current slide, you can set
614			//  {animateHeight: true}
615			if (self.options.animateHeight) {
616				self._move(self.$context, {height: self.$slides.eq(to).outerHeight()}, false);
617			}
618		};
619
620		self._move = function($el, obj, callback, speed) {
621			if(callback !== false) {
622				callback = function() {
623					self.$context.trigger(self._ + '.moved');
624				};
625			}
626
627			return $el._move(obj, speed || self.options.speed, self.options.easing, callback);
628		};
629
630		//  Allow daisy-chaining of methods
631		return self.init(options);
632	};
633
634	//  Internal (but global) jQuery methods
635	//  They're both just helpful types of shorthand for
636	//  anything that might take too long to write out or
637	//  something that might be used more than once.
638	$.fn._active = function(className) {
639		return this.addClass(className).siblings().removeClass(className);
640	};
641
642	//  The equivalent to PHP's ucfirst(). Take the first
643	//  character of a string and make it uppercase.
644	//  Simples.
645	$._ucfirst = function(str) {
646		//  Take our variable, run a regex on the first letter
647		return (str + '').toLowerCase().replace(/^./, function(match) {
648			//  And uppercase it. Simples.
649			return match.toUpperCase();
650		});
651	};
652
653	$.fn._move = function() {
654		this.stop(true, true);
655		return $.fn[$.fn.velocity ? 'velocity' : 'animate'].apply(this, arguments);
656	};
657
658	//  And set up our jQuery plugin
659	$.fn.unslider = function(opts) {
660		return this.each(function(index,elem) {
661			var $this = $(elem);
662            var unslider = $(elem).data('unslider');
663            if(unslider instanceof $.Unslider) {
664                return;
665            }
666			//  Allow usage of .unslider('function_name')
667			//  as well as using .data('unslider') to access the
668			//  main Unslider object
669			if(typeof opts === 'string' && $this.data('unslider')) {
670				opts = opts.split(':');
671
672				var call = $this.data('unslider')[opts[0]];
673
674				//  Do we have arguments to pass to the string-function?
675				if($.isFunction(call)) {
676					return call.apply($this, opts[1] ? opts[1].split(',') : null);
677				}
678			}
679
680			return $this.data('unslider', new $.Unslider($this, opts));
681		});
682	};
683
684}));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.