1/**
2* Gumby SkipLink
3*/
4!function($) {
5
6	'use strict';
7
8	function SkipLink($el) {
9
10		Gumby.debug('Initializing Skiplink', $el);
11
12		this.$el = $el;
13		this.targetPos = 0;
14		this.duration = 0;
15		this.offset = false;
16		this.easing = '';
17		this.update = false;
18
19		// set up module based on attributes
20		this.setup();
21
22		var scope = this;
23
24		// skip to target element on click or trigger of gumby.skipTo event
25		this.$el.on(Gumby.click+' gumby.skip', function(e) {
26			e.preventDefault();
27
28			if(e.namespace === 'skip') {
29				Gumby.debug('Skip event triggered', scope.$el);
30			}
31
32			// calculate target on each click if update var set to true
33			if(scope.update) {
34				scope.calculateTarget(scope.skipTo);
35
36			// skip straight to target
37			} else {
38				scope.skipTo();
39			}
40		}).on('gumby.initialize', function() {
41			Gumby.debug('Re-initializing Skiplink', scope.$el);
42			scope.setup();
43		});
44	}
45
46	// set up module based on attributes
47	SkipLink.prototype.setup = function() {
48		this.duration = Number(Gumby.selectAttr.apply(this.$el, ['duration'])) || 200;
49		this.offset = Gumby.selectAttr.apply(this.$el, ['offset']) || false;
50		this.easing = Gumby.selectAttr.apply(this.$el, ['easing']) || 'swing';
51		this.update = Gumby.selectAttr.apply(this.$el, ['update']) ? true : false;
52
53		this.calculateTarget();
54	};
55
56	// calculate target px point to skip to
57	SkipLink.prototype.calculateTarget = function(cb) {
58
59		var scope = this,
60			target = Gumby.selectAttr.apply(this.$el, ['goto']),
61			$target;
62
63		// 'top' specified so target is 0px
64		if(target == 'top') {
65			this.targetPos = 0;
66
67		// px point specified
68		} else if($.isNumeric(target)) {
69			this.targetPos = Number(target);
70		} else {
71
72			// check for element with target as selector
73			$target = $(target);
74
75			// target does not exist, we need a target
76			if(!$target.length) {
77				Gumby.error('Cannot find skiplink target: '+target);
78				return false;
79			}
80
81			this.targetPos = $target.offset().top;
82		}
83
84		if(cb) {
85			cb.apply(this);
86		}
87	};
88
89	// animate body, html scrollTop value to target px point
90	SkipLink.prototype.skipTo = function() {
91
92		Gumby.debug('Skipping to target', this.$el);
93
94		var scope = this;
95
96		// slide to position of target
97		$('html,body').animate({
98			'scrollTop' : this.calculateOffset()
99		}, this.duration, this.easing).promise().done(function() {
100
101			Gumby.debug('Triggering onComplete event', scope.$el);
102			scope.$el.trigger('gumby.onComplete');
103		});
104	};
105
106	// calculate offset with current target point
107	SkipLink.prototype.calculateOffset = function() {
108		// no offset so return target here
109		if(!this.offset) {
110			return this.targetPos;
111		}
112
113		// negative / positive
114		var op = this.offset.substr(0, 1),
115			off = Number(this.offset.substr(1, this.offset.length));
116
117		// subtract offset from target position
118		if(op === '-') {
119			return this.targetPos - off;
120		// add offset to target position
121		} else if(op === '+') {
122			return this.targetPos + off;
123		}
124	};
125
126	// add initialisation
127	Gumby.addInitalisation('skiplink', function(all) {
128		$('.skiplink > a, .skip').each(function() {
129			var $this = $(this);
130
131			// this element has already been initialized
132			// and we're only initializing new modules
133			if($this.data('isSkipLink') && !all) {
134				return true;
135
136			// this element has already been initialized
137			// and we need to reinitialize it
138			} else if($this.data('isSkipLink') && all) {
139				$this.trigger('gumby.initialize');
140				return true;
141			}
142
143			// mark element as initialized
144			$this.data('isSkipLink', true);
145			new SkipLink($this);
146		});
147	});
148
149	// register UI module
150	Gumby.UIModule({
151		module: 'skiplink',
152		events: ['initialize', 'onComplete', 'skip'],
153		init: function() {
154			Gumby.initialize('skiplink');
155		}
156	});
157}(jQuery);
158