source: main/trunk/model-interfaces-dev/heritage-nz/iframe/heritage-nz-dl_files/jquery_005.js@ 32796

Last change on this file since 32796 was 32796, checked in by davidb, 5 years ago

Initial set of files to provide look and feel of Heritage NZ site, plus SVN clickable map in an iframe

  • Property svn:executable set to *
File size: 9.4 KB
Line 
1!function(factory) {
2
3 if(typeof define === 'function' && define.amd) define(['index', 'jquery', 'jquery.ba-resize'], factory);
4 else TabsAccordion = factory(Index, jQuery);
5}
6(function(Index, $) {
7
8 var count = 0,
9 namespace = 'tabsaccordion',
10 $window = $(window),
11 $html = $(document.documentElement).addClass('js'),
12 $body = $(document.body);
13
14 $.resize.throttleWindow = false;
15 $.fn.TabsAccordion = function(options) {
16
17 function T(element, options) {
18
19 var idNamespace = namespace + '-' + count++,
20 $element = $(element),
21 $panels,
22 $tabs,
23 $tablist,
24 $content,
25 self = {
26 version: '1.2.0',
27 type: ($element.hasClass('accordion') && 'accordion') || ($element.hasClass('tabs') && 'tabs'),
28
29 create: function() {
30
31 $panels = $element.children();
32 $tabs = $panels.children(':first-child');
33
34 if(self.index) var prev = self.index.curr;
35 (self.index = Index($panels.length - 1)).loop = true; // <- index looping for keyboard accessibility
36 if(prev) self.index.set(prev);
37
38 if(self.type === 'tabs') $element.prepend(($tabs = self.tabsCreateTablist($tabs).children()).end());
39
40 $tablist = (self.type === 'tabs' ? $tabs.parent() : $element).attr('role', 'tablist');
41
42 $tabs.attr({
43 'id': function(index) {
44
45 return this.id || idNamespace + '-tab-' + index;
46 },
47 'role': 'tab'
48 });
49
50 ($content = $panels.map(function(index) {
51
52 return $(this)
53 .attr({
54 'aria-labelledby': $tabs[index].id,
55 'id': this.id || idNamespace + '-panel-' + index,
56 'role': 'tabpanel'
57 })
58 .children()
59 .slice(1)
60 .wrapAll('<div><div></div></div>')
61 .parent()
62 .parent()
63 .get();
64 }))
65 .each(self.collapse);
66
67 $element
68 .attr({
69 'id': element.id || idNamespace,
70 'tabindex': 0
71 })
72 .on('click.' + idNamespace, self.type === 'accordion' && '> * > :first-child' || '> :first-child > *', function(event) {
73
74 self.goTo($tabs.index($(event.target).closest($tabs)));
75 })
76 .on('keydown.' + idNamespace, function(event) {
77
78 // event.target should be the element and not a descendant
79 if(event.target !== element) return;
80
81 var match = {
82 37: 'prev',
83 38: 'prev',
84 39: 'next',
85 40: 'next'
86 }[event.keyCode];
87
88 if(match) {
89
90 event.preventDefault();
91
92 self.goTo(self.index[match]);
93 }
94 })
95 .on('resize.' + idNamespace, self.resize)
96 .trigger('create');
97
98
99 if(options.saveState) self.extensions.saveState(options.saveState);
100 if(options.responsiveSwitch) self.extensions.responsiveSwitch(options.responsiveSwitch);
101 if(options.hashWatch) self.extensions.hashWatch();
102 if(options.pauseMedia) self.extensions.pauseMedia();
103
104 if(typeof self.index.curr !== 'number') self.index.set(0);
105
106 setTimeout(function() {
107
108 $element.addClass('transition');
109 })
110
111 return self.expand(self.index.curr);
112 },
113
114 destroy: function(keepData) {
115
116 if(self.type === 'tabs') {
117
118 $element.height('auto');
119
120 $tablist.remove();
121 }
122 else {
123
124 $tabs
125 .removeAttr('role')
126 .filter('[id^="' + idNamespace + '"]').removeAttr('id');
127
128 $tablist.removeAttr('role');
129 }
130
131 $panels
132 .removeAttr('aria-expanded aria-labelledby role')
133 .filter('[id^="' + idNamespace + '"]').removeAttr('id');
134
135 $content
136 .children()
137 .children()
138 .unwrap()
139 .unwrap();
140
141 if(!keepData)
142 $element
143 .removeData(namespace)
144 .removeData('responsiveBreakpoint.' + idNamespace);
145
146 $element
147 .add([window, document.body])
148 .off('.' + idNamespace)
149 .end()
150 .removeAttr('aria-activedescendant tabindex')
151 .removeClass(self.type)
152 .filter('[id^="' + idNamespace + '"]').removeAttr('id')
153 .end()
154 .trigger('destroy');
155
156 return self;
157 },
158
159 resize: function() {
160
161 if(self.type === 'tabs') $element.height($tablist.outerHeight() + $panels.eq(self.index.curr).outerHeight());
162 else if(self.type === 'accordion' && $panels[self.index.curr].ariaExpanded)
163 $content
164 .eq(self.index.curr)
165 .height($content.eq(self.index.curr).children().outerHeight());
166
167 return self;
168 },
169
170 expand: function(index) {
171
172 var $panel = $panels.eq(index).attr('aria-expanded', $panels[index].ariaExpanded = true);
173
174 if(self.resize().type === 'tabs') $tabs.eq(index).addClass('current');
175
176 $element
177 .attr('aria-activedescendant', $panels[self.index.curr].id)
178 .trigger('expand', [index, $panel]);
179
180 return self;
181 },
182
183 collapse: function(index) {
184
185 var $panel = $panels.eq(index).attr('aria-expanded', $panels[index].ariaExpanded = false);
186
187 if(self.type === 'tabs') $tabs.eq(index).removeClass('current');
188 else $content.eq(index).height(0);
189
190 $element.trigger('collapse', [index, $panel]);
191
192 return self;
193 },
194
195 goTo: function(index) {
196
197 if(self.index.curr !== index && typeof self.index.curr === 'number') self.collapse(self.index.curr);
198 self.index.set(index);
199
200 return self[self.type === 'accordion' && $panels.eq(index).prop('ariaExpanded') ? 'collapse' : 'expand'](self.index.curr);
201 },
202
203 tabsCreateTablist: options.tabsCreateTablist || function(titles) {
204
205 for(var i = 0, li = ''; i < titles.length; i++) li += '<li>' + titles[i].innerHTML + '</li>';
206
207 return $('<ul>' + li + '</ul>');
208 },
209
210 extensions: {
211 hashWatch: function() {
212
213 var that = {
214 changeHash: function(hash, $target) {
215
216 var id = $target[0].id;
217
218 $target[0].id = '';
219 location.hash = hash;
220 $target[0].id = id;
221
222 return that;
223 },
224
225 expand: function(hash, event) {
226
227 var $target = $element.find(hash);
228
229 if($target.length) {
230
231 var $panel = $target.closest($panels);
232
233 if($panel.length) {
234
235 if(event) event.preventDefault();
236
237 self.goTo($panels.index($panel));
238 that.changeHash(hash, $target);
239
240 if(event) {
241 setTimeout(function() {
242
243 $html
244 .add($body)
245 .animate({
246 scrollTop: $target.offset().top
247 });
248 },250);
249 }
250 }
251 }
252
253 return that;
254 }
255 };
256
257 $body
258 // hash anchor activation
259 .on('click.' + idNamespace, 'a[href^="#"]:not([href="#"])', function(event) {
260
261 that.expand($(event.target).attr('href'), event);
262 })
263 // navigation e.g. back button
264 .on('hashchange.' + idNamespace, function() {
265
266 that.expand(location.hash);
267 });
268
269 return that.expand(location.hash);
270 },
271
272 saveStateLoaded: false,
273 saveState: function(storage) {
274
275 if(typeof storage !== 'object') return;
276
277 var state = {
278 remove: function() {
279
280 storage.removeItem(idNamespace);
281 },
282
283 load: function() {
284
285 var item = storage.getItem(idNamespace),
286 data = JSON.parse(item);
287
288 if(data && data.current) self.index.set(data.current);
289
290 self.extensions.saveStateLoaded = true;
291 },
292
293 save: function() {
294
295 storage.setItem(idNamespace, JSON.stringify({current: self.index.curr, expanded: $panels[self.index.curr].ariaExpanded}));
296 }
297 };
298
299 // load only once per instance per page load
300 if(!self.extensions.saveStateLoaded) state.load();
301
302 $window.on('unload.' + idNamespace, state.save);
303
304 return state;
305 },
306
307 responsiveSwitch: function(breakpoint) {
308
309 if(breakpoint === 'tablist') {
310
311 if(self.type === 'tabs') $element.data('responsiveBreakpoint.' + idNamespace, breakpoint = getTablistWidth());
312 else breakpoint = $element.data('responsiveBreakpoint.' + idNamespace);
313 }
314
315 function getTablistWidth() {
316
317 // measure combined width of all tabs instead of single width of tablist, because tabs are floated and can jump to the next line
318 for(var i = 0, width = 0; i < $tabs.length; i++) width += $tabs.eq(i).outerWidth(true);
319
320 return width;
321 }
322
323 function switchTo(type) {
324
325 var current = self.index.curr,
326 expanded = $panels[current].ariaExpanded;
327
328 self.destroy(true);
329
330 $element.addClass(self.type = type);
331
332 self.index.set(current);
333 self.create();
334
335 $element.trigger('typechange', type);
336 }
337
338 function checkBreakpoint() {
339
340 var type = $element.outerWidth() <= breakpoint ? 'accordion' : 'tabs';
341
342 if(self.type !== type) switchTo(type);
343 }
344
345 $element.on('resize.' + idNamespace, checkBreakpoint);
346 },
347
348 pauseMedia: function() {
349
350 if(typeof Modernizr === 'undefined' || !Modernizr.audio || !Modernizr.video || !$element.find('audio, video').length) return;
351
352 $element.on('collapse.' + idNamespace, function(event, index, $panel) {
353
354 $panel.find('audio, video').each(function() {
355
356 this.pause();
357 });
358 });
359 }
360 }
361 };
362
363 return self.create();
364 }
365
366 var options = options || {},
367 args = Array.prototype.slice.call(arguments, 1);
368
369 return this.each(function(index) {
370
371 var $this = $(this);
372
373 // method call : instantiation
374 return $this.data(namespace) ? $this.data(namespace)[options].apply(this, args) : $this.data(namespace, T(this, options));
375 });
376 }
377});
Note: See TracBrowser for help on using the repository browser.