gemfile: add json requirement.
[web.git] / js / foundation / foundation.magellan.js
1 ;
2 (function ($, window, document, undefined) {
3 'use strict';
4
5 Foundation.libs['magellan-expedition'] = {
6 name: 'magellan-expedition',
7
8 version: '5.5.0',
9
10 settings: {
11 active_class: 'active',
12 threshold: 0, // pixels from the top of the expedition for it to become fixes
13 destination_threshold: 20, // pixels from the top of destination for it to be considered active
14 throttle_delay: 30, // calculation throttling to increase framerate
15 fixed_top: 0, // top distance in pixels assigend to the fixed element on scroll
16 offset_by_height: true, // whether to offset the destination by the expedition height. Usually you want this to be true, unless your expedition is on the side.
17 duration: 700, // animation duration time
18 easing: 'swing' // animation easing
19 },
20
21 init: function (scope, method, options) {
22 Foundation.inherit(this, 'throttle');
23 this.bindings(method, options);
24 },
25
26 events: function () {
27 var self = this,
28 S = self.S,
29 settings = self.settings;
30
31 // initialize expedition offset
32 self.set_expedition_position();
33
34 S(self.scope)
35 .off('.magellan')
36 .on('click.fndtn.magellan', '[' + self.add_namespace('data-magellan-arrival') + '] a[href^="#"]', function (e) {
37 e.preventDefault();
38 var expedition = $(this).closest('[' + self.attr_name() + ']'),
39 settings = expedition.data('magellan-expedition-init'),
40 hash = this.hash.split('#').join(''),
41 target = $('a[name="' + hash + '"]');
42
43 if (target.length === 0) {
44 target = $('#' + hash);
45
46 }
47
48
49 // Account for expedition height if fixed position
50 var scroll_top = target.offset().top - settings.destination_threshold + 1;
51 if (settings.offset_by_height) {
52 scroll_top = scroll_top - expedition.outerHeight();
53 }
54
55 $('html, body').stop().animate({
56 'scrollTop': scroll_top
57 }, settings.duration, settings.easing, function () {
58 if (history.pushState) {
59 history.pushState(null, null, '#' + hash);
60 }
61 else {
62 location.hash = '#' + hash;
63 }
64 });
65 })
66 .on('scroll.fndtn.magellan', self.throttle(this.check_for_arrivals.bind(this), settings.throttle_delay));
67
68 $(window)
69 .on('resize.fndtn.magellan', self.throttle(this.set_expedition_position.bind(this), settings.throttle_delay));
70 },
71
72 check_for_arrivals: function () {
73 var self = this;
74 self.update_arrivals();
75 self.update_expedition_positions();
76 },
77
78 set_expedition_position: function () {
79 var self = this;
80 $('[' + this.attr_name() + '=fixed]', self.scope).each(function (idx, el) {
81 var expedition = $(this),
82 settings = expedition.data('magellan-expedition-init'),
83 styles = expedition.attr('styles'), // save styles
84 top_offset, fixed_top;
85
86 expedition.attr('style', '');
87 top_offset = expedition.offset().top + settings.threshold;
88
89 //set fixed-top by attribute
90 fixed_top = parseInt(expedition.data('magellan-fixed-top'));
91 if (!isNaN(fixed_top))
92 self.settings.fixed_top = fixed_top;
93
94 expedition.data(self.data_attr('magellan-top-offset'), top_offset);
95 expedition.attr('style', styles);
96 });
97 },
98
99 update_expedition_positions: function () {
100 var self = this,
101 window_top_offset = $(window).scrollTop();
102
103 $('[' + this.attr_name() + '=fixed]', self.scope).each(function () {
104 var expedition = $(this),
105 settings = expedition.data('magellan-expedition-init'),
106 styles = expedition.attr('style'), // save styles
107 top_offset = expedition.data('magellan-top-offset');
108
109 //scroll to the top distance
110 if (window_top_offset + self.settings.fixed_top >= top_offset) {
111 // Placeholder allows height calculations to be consistent even when
112 // appearing to switch between fixed/non-fixed placement
113 var placeholder = expedition.prev('[' + self.add_namespace('data-magellan-expedition-clone') + ']');
114 if (placeholder.length === 0) {
115 placeholder = expedition.clone();
116 placeholder.removeAttr(self.attr_name());
117 placeholder.attr(self.add_namespace('data-magellan-expedition-clone'), '');
118 expedition.before(placeholder);
119 }
120 expedition.css({position: 'fixed', top: settings.fixed_top}).addClass('fixed');
121 } else {
122 expedition.prev('[' + self.add_namespace('data-magellan-expedition-clone') + ']').remove();
123 expedition.attr('style', styles).css('position', '').css('top', '').removeClass('fixed');
124 }
125 });
126 },
127
128 update_arrivals: function () {
129 var self = this,
130 window_top_offset = $(window).scrollTop();
131
132 $('[' + this.attr_name() + ']', self.scope).each(function () {
133 var expedition = $(this),
134 settings = expedition.data(self.attr_name(true) + '-init'),
135 offsets = self.offsets(expedition, window_top_offset),
136 arrivals = expedition.find('[' + self.add_namespace('data-magellan-arrival') + ']'),
137 active_item = false;
138 offsets.each(function (idx, item) {
139 if (item.viewport_offset >= item.top_offset) {
140 var arrivals = expedition.find('[' + self.add_namespace('data-magellan-arrival') + ']');
141 arrivals.not(item.arrival).removeClass(settings.active_class);
142 item.arrival.addClass(settings.active_class);
143 active_item = true;
144 return true;
145 }
146 });
147
148 if (!active_item) arrivals.removeClass(settings.active_class);
149 });
150 },
151
152 offsets: function (expedition, window_offset) {
153 var self = this,
154 settings = expedition.data(self.attr_name(true) + '-init'),
155 viewport_offset = window_offset;
156
157 return expedition.find('[' + self.add_namespace('data-magellan-arrival') + ']').map(function (idx, el) {
158 var name = $(this).data(self.data_attr('magellan-arrival')),
159 dest = $('[' + self.add_namespace('data-magellan-destination') + '=' + name + ']');
160 if (dest.length > 0) {
161 var top_offset = dest.offset().top - settings.destination_threshold;
162 if (settings.offset_by_height) {
163 top_offset = top_offset - expedition.outerHeight();
164 }
165 top_offset = Math.floor(top_offset);
166 return {
167 destination: dest,
168 arrival: $(this),
169 top_offset: top_offset,
170 viewport_offset: viewport_offset
171 }
172 }
173 }).sort(function (a, b) {
174 if (a.top_offset < b.top_offset) return -1;
175 if (a.top_offset > b.top_offset) return 1;
176 return 0;
177 });
178 },
179
180 data_attr: function (str) {
181 if (this.namespace.length > 0) {
182 return this.namespace + '-' + str;
183 }
184
185 return str;
186 },
187
188 off: function () {
189 this.S(this.scope).off('.magellan');
190 this.S(window).off('.magellan');
191 },
192
193 reflow: function () {
194 var self = this;
195 // remove placeholder expeditions used for height calculation purposes
196 $('[' + self.add_namespace('data-magellan-expedition-clone') + ']', self.scope).remove();
197 }
198 };
199 }(jQuery, window, window.document));