Subversion Repositories SmartDukaan

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
162 naveen 1
/*
2
 * jQuery UI Accordion 1.8.1
3
 *
4
 * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
5
 * Dual licensed under the MIT (MIT-LICENSE.txt)
6
 * and GPL (GPL-LICENSE.txt) licenses.
7
 *
8
 * http://docs.jquery.com/UI/Accordion
9
 *
10
 * Depends:
11
 *	jquery.ui.core.js
12
 *	jquery.ui.widget.js
13
 */
14
(function($) {
15
 
16
$.widget("ui.accordion", {
17
	options: {
18
		active: 0,
19
		animated: 'slide',
20
		autoHeight: true,
21
		clearStyle: false,
22
		collapsible: false,
23
		event: "click",
24
		fillSpace: false,
25
		header: "> li > :first-child,> :not(li):even",
26
		icons: {
27
			header: "ui-icon-triangle-1-e",
28
			headerSelected: "ui-icon-triangle-1-s"
29
		},
30
		navigation: false,
31
		navigationFilter: function() {
32
			return this.href.toLowerCase() == location.href.toLowerCase();
33
		}
34
	},
35
	_create: function() {
36
 
37
		var o = this.options, self = this;
38
		this.running = 0;
39
 
40
		this.element.addClass("ui-accordion ui-widget ui-helper-reset");
41
 
42
		// in lack of child-selectors in CSS we need to mark top-LIs in a UL-accordion for some IE-fix
43
		if (this.element[0].nodeName == "UL") {
44
			this.element.children("li").addClass("ui-accordion-li-fix");
45
		}
46
 
47
		this.headers = this.element.find(o.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all")
48
			.bind("mouseenter.accordion", function(){ $(this).addClass('ui-state-hover'); })
49
			.bind("mouseleave.accordion", function(){ $(this).removeClass('ui-state-hover'); })
50
			.bind("focus.accordion", function(){ $(this).addClass('ui-state-focus'); })
51
			.bind("blur.accordion", function(){ $(this).removeClass('ui-state-focus'); });
52
 
53
		this.headers
54
			.next()
55
				.addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");
56
 
57
		if ( o.navigation ) {
58
			var current = this.element.find("a").filter(o.navigationFilter);
59
			if ( current.length ) {
60
				var header = current.closest(".ui-accordion-header");
61
				if ( header.length ) {
62
					// anchor within header
63
					this.active = header;
64
				} else {
65
					// anchor within content
66
					this.active = current.closest(".ui-accordion-content").prev();
67
				}
68
			}
69
		}
70
 
71
		this.active = this._findActive(this.active || o.active).toggleClass("ui-state-default").toggleClass("ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");
72
		this.active.next().addClass('ui-accordion-content-active');
73
 
74
		//Append icon elements
75
		this._createIcons();
76
 
77
		this.resize();
78
 
79
		//ARIA
80
		this.element.attr('role','tablist');
81
 
82
		this.headers
83
			.attr('role','tab')
84
			.bind('keydown', function(event) { return self._keydown(event); })
85
			.next()
86
			.attr('role','tabpanel');
87
 
88
		this.headers
89
			.not(this.active || "")
90
			.attr('aria-expanded','false')
91
			.attr("tabIndex", "-1")
92
			.next()
93
			.hide();
94
 
95
		// make sure at least one header is in the tab order
96
		if (!this.active.length) {
97
			this.headers.eq(0).attr('tabIndex','0');
98
		} else {
99
			this.active
100
				.attr('aria-expanded','true')
101
				.attr('tabIndex', '0');
102
		}
103
 
104
		// only need links in taborder for Safari
105
		if (!$.browser.safari)
106
			this.headers.find('a').attr('tabIndex','-1');
107
 
108
		if (o.event) {
109
			this.headers.bind((o.event) + ".accordion", function(event) {
110
				self._clickHandler.call(self, event, this);
111
				event.preventDefault();
112
			});
113
		}
114
 
115
	},
116
 
117
	_createIcons: function() {
118
		var o = this.options;
119
		if (o.icons) {
120
			$("<span/>").addClass("ui-icon " + o.icons.header).prependTo(this.headers);
121
			this.active.find(".ui-icon").toggleClass(o.icons.header).toggleClass(o.icons.headerSelected);
122
			this.element.addClass("ui-accordion-icons");
123
		}
124
	},
125
 
126
	_destroyIcons: function() {
127
		this.headers.children(".ui-icon").remove();
128
		this.element.removeClass("ui-accordion-icons");
129
	},
130
 
131
	destroy: function() {
132
		var o = this.options;
133
 
134
		this.element
135
			.removeClass("ui-accordion ui-widget ui-helper-reset")
136
			.removeAttr("role")
137
			.unbind('.accordion')
138
			.removeData('accordion');
139
 
140
		this.headers
141
			.unbind(".accordion")
142
			.removeClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-corner-top")
143
			.removeAttr("role").removeAttr("aria-expanded").removeAttr("tabIndex");
144
 
145
		this.headers.find("a").removeAttr("tabIndex");
146
		this._destroyIcons();
147
		var contents = this.headers.next().css("display", "").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active");
148
		if (o.autoHeight || o.fillHeight) {
149
			contents.css("height", "");
150
		}
151
 
152
		return this;
153
	},
154
 
155
	_setOption: function(key, value) {
156
		$.Widget.prototype._setOption.apply(this, arguments);
157
 
158
		if (key == "active") {
159
			this.activate(value);
160
		}
161
		if (key == "icons") {
162
			this._destroyIcons();
163
			if (value) {
164
				this._createIcons();
165
			}
166
		}
167
 
168
	},
169
 
170
	_keydown: function(event) {
171
 
172
		var o = this.options, keyCode = $.ui.keyCode;
173
 
174
		if (o.disabled || event.altKey || event.ctrlKey)
175
			return;
176
 
177
		var length = this.headers.length;
178
		var currentIndex = this.headers.index(event.target);
179
		var toFocus = false;
180
 
181
		switch(event.keyCode) {
182
			case keyCode.RIGHT:
183
			case keyCode.DOWN:
184
				toFocus = this.headers[(currentIndex + 1) % length];
185
				break;
186
			case keyCode.LEFT:
187
			case keyCode.UP:
188
				toFocus = this.headers[(currentIndex - 1 + length) % length];
189
				break;
190
			case keyCode.SPACE:
191
			case keyCode.ENTER:
192
				this._clickHandler({ target: event.target }, event.target);
193
				event.preventDefault();
194
		}
195
 
196
		if (toFocus) {
197
			$(event.target).attr('tabIndex','-1');
198
			$(toFocus).attr('tabIndex','0');
199
			toFocus.focus();
200
			return false;
201
		}
202
 
203
		return true;
204
 
205
	},
206
 
207
	resize: function() {
208
 
209
		var o = this.options, maxHeight;
210
 
211
		if (o.fillSpace) {
212
 
213
			if($.browser.msie) { var defOverflow = this.element.parent().css('overflow'); this.element.parent().css('overflow', 'hidden'); }
214
			maxHeight = this.element.parent().height();
215
			if($.browser.msie) { this.element.parent().css('overflow', defOverflow); }
216
 
217
			this.headers.each(function() {
218
				maxHeight -= $(this).outerHeight(true);
219
			});
220
 
221
			this.headers.next().each(function() {
222
    		   $(this).height(Math.max(0, maxHeight - $(this).innerHeight() + $(this).height()));
223
			}).css('overflow', 'auto');
224
 
225
		} else if ( o.autoHeight ) {
226
			maxHeight = 0;
227
			this.headers.next().each(function() {
228
				maxHeight = Math.max(maxHeight, $(this).height());
229
			}).height(maxHeight);
230
		}
231
 
232
		return this;
233
	},
234
 
235
	activate: function(index) {
236
		// TODO this gets called on init, changing the option without an explicit call for that
237
		this.options.active = index;
238
		// call clickHandler with custom event
239
		var active = this._findActive(index)[0];
240
		this._clickHandler({ target: active }, active);
241
 
242
		return this;
243
	},
244
 
245
	_findActive: function(selector) {
246
		return selector
247
			? typeof selector == "number"
248
				? this.headers.filter(":eq(" + selector + ")")
249
				: this.headers.not(this.headers.not(selector))
250
			: selector === false
251
				? $([])
252
				: this.headers.filter(":eq(0)");
253
	},
254
 
255
	// TODO isn't event.target enough? why the seperate target argument?
256
	_clickHandler: function(event, target) {
257
 
258
		var o = this.options;
259
		if (o.disabled)
260
			return;
261
 
262
		// called only when using activate(false) to close all parts programmatically
263
		if (!event.target) {
264
			if (!o.collapsible)
265
				return;
266
			this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all")
267
				.find(".ui-icon").removeClass(o.icons.headerSelected).addClass(o.icons.header);
268
			this.active.next().addClass('ui-accordion-content-active');
269
			var toHide = this.active.next(),
270
				data = {
271
					options: o,
272
					newHeader: $([]),
273
					oldHeader: o.active,
274
					newContent: $([]),
275
					oldContent: toHide
276
				},
277
				toShow = (this.active = $([]));
278
			this._toggle(toShow, toHide, data);
279
			return;
280
		}
281
 
282
		// get the click target
283
		var clicked = $(event.currentTarget || target);
284
		var clickedIsActive = clicked[0] == this.active[0];
285
 
286
		// TODO the option is changed, is that correct?
287
		// TODO if it is correct, shouldn't that happen after determining that the click is valid?
288
		o.active = o.collapsible && clickedIsActive ? false : $('.ui-accordion-header', this.element).index(clicked);
289
 
290
		// if animations are still active, or the active header is the target, ignore click
291
		if (this.running || (!o.collapsible && clickedIsActive)) {
292
			return;
293
		}
294
 
295
		// switch classes
296
		this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all")
297
			.find(".ui-icon").removeClass(o.icons.headerSelected).addClass(o.icons.header);
298
		if (!clickedIsActive) {
299
			clicked.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top")
300
				.find(".ui-icon").removeClass(o.icons.header).addClass(o.icons.headerSelected);
301
			clicked.next().addClass('ui-accordion-content-active');
302
		}
303
 
304
		// find elements to show and hide
305
		var toShow = clicked.next(),
306
			toHide = this.active.next(),
307
			data = {
308
				options: o,
309
				newHeader: clickedIsActive && o.collapsible ? $([]) : clicked,
310
				oldHeader: this.active,
311
				newContent: clickedIsActive && o.collapsible ? $([]) : toShow,
312
				oldContent: toHide
313
			},
314
			down = this.headers.index( this.active[0] ) > this.headers.index( clicked[0] );
315
 
316
		this.active = clickedIsActive ? $([]) : clicked;
317
		this._toggle(toShow, toHide, data, clickedIsActive, down);
318
 
319
		return;
320
 
321
	},
322
 
323
	_toggle: function(toShow, toHide, data, clickedIsActive, down) {
324
 
325
		var o = this.options, self = this;
326
 
327
		this.toShow = toShow;
328
		this.toHide = toHide;
329
		this.data = data;
330
 
331
		var complete = function() { if(!self) return; return self._completed.apply(self, arguments); };
332
 
333
		// trigger changestart event
334
		this._trigger("changestart", null, this.data);
335
 
336
		// count elements to animate
337
		this.running = toHide.size() === 0 ? toShow.size() : toHide.size();
338
 
339
		if (o.animated) {
340
 
341
			var animOptions = {};
342
 
343
			if ( o.collapsible && clickedIsActive ) {
344
				animOptions = {
345
					toShow: $([]),
346
					toHide: toHide,
347
					complete: complete,
348
					down: down,
349
					autoHeight: o.autoHeight || o.fillSpace
350
				};
351
			} else {
352
				animOptions = {
353
					toShow: toShow,
354
					toHide: toHide,
355
					complete: complete,
356
					down: down,
357
					autoHeight: o.autoHeight || o.fillSpace
358
				};
359
			}
360
 
361
			if (!o.proxied) {
362
				o.proxied = o.animated;
363
			}
364
 
365
			if (!o.proxiedDuration) {
366
				o.proxiedDuration = o.duration;
367
			}
368
 
369
			o.animated = $.isFunction(o.proxied) ?
370
				o.proxied(animOptions) : o.proxied;
371
 
372
			o.duration = $.isFunction(o.proxiedDuration) ?
373
				o.proxiedDuration(animOptions) : o.proxiedDuration;
374
 
375
			var animations = $.ui.accordion.animations,
376
				duration = o.duration,
377
				easing = o.animated;
378
 
379
			if (easing && !animations[easing] && !$.easing[easing]) {
380
				easing = 'slide';
381
			}
382
			if (!animations[easing]) {
383
				animations[easing] = function(options) {
384
					this.slide(options, {
385
						easing: easing,
386
						duration: duration || 700
387
					});
388
				};
389
			}
390
 
391
			animations[easing](animOptions);
392
 
393
		} else {
394
 
395
			if (o.collapsible && clickedIsActive) {
396
				toShow.toggle();
397
			} else {
398
				toHide.hide();
399
				toShow.show();
400
			}
401
 
402
			complete(true);
403
 
404
		}
405
 
406
		// TODO assert that the blur and focus triggers are really necessary, remove otherwise
407
		toHide.prev().attr('aria-expanded','false').attr("tabIndex", "-1").blur();
408
		toShow.prev().attr('aria-expanded','true').attr("tabIndex", "0").focus();
409
 
410
	},
411
 
412
	_completed: function(cancel) {
413
 
414
		var o = this.options;
415
 
416
		this.running = cancel ? 0 : --this.running;
417
		if (this.running) return;
418
 
419
		if (o.clearStyle) {
420
			this.toShow.add(this.toHide).css({
421
				height: "",
422
				overflow: ""
423
			});
424
		}
425
 
426
		// other classes are removed before the animation; this one needs to stay until completed
427
		this.toHide.removeClass("ui-accordion-content-active");
428
 
429
		this._trigger('change', null, this.data);
430
	}
431
 
432
});
433
 
434
 
435
$.extend($.ui.accordion, {
436
	version: "1.8.1",
437
	animations: {
438
		slide: function(options, additions) {
439
			options = $.extend({
440
				easing: "swing",
441
				duration: 300
442
			}, options, additions);
443
			if ( !options.toHide.size() ) {
444
				options.toShow.animate({height: "show"}, options);
445
				return;
446
			}
447
			if ( !options.toShow.size() ) {
448
				options.toHide.animate({height: "hide"}, options);
449
				return;
450
			}
451
			var overflow = options.toShow.css('overflow'),
452
				percentDone = 0,
453
				showProps = {},
454
				hideProps = {},
455
				fxAttrs = [ "height", "paddingTop", "paddingBottom" ],
456
				originalWidth;
457
			// fix width before calculating height of hidden element
458
			var s = options.toShow;
459
			originalWidth = s[0].style.width;
460
			s.width( parseInt(s.parent().width(),10) - parseInt(s.css("paddingLeft"),10) - parseInt(s.css("paddingRight"),10) - (parseInt(s.css("borderLeftWidth"),10) || 0) - (parseInt(s.css("borderRightWidth"),10) || 0) );
461
 
462
			$.each(fxAttrs, function(i, prop) {
463
				hideProps[prop] = 'hide';
464
 
465
				var parts = ('' + $.css(options.toShow[0], prop)).match(/^([\d+-.]+)(.*)$/);
466
				showProps[prop] = {
467
					value: parts[1],
468
					unit: parts[2] || 'px'
469
				};
470
			});
471
			options.toShow.css({ height: 0, overflow: 'hidden' }).show();
472
			options.toHide.filter(":hidden").each(options.complete).end().filter(":visible").animate(hideProps,{
473
				step: function(now, settings) {
474
					// only calculate the percent when animating height
475
					// IE gets very inconsistent results when animating elements
476
					// with small values, which is common for padding
477
					if (settings.prop == 'height') {
478
						percentDone = ( settings.end - settings.start === 0 ) ? 0 :
479
							(settings.now - settings.start) / (settings.end - settings.start);
480
					}
481
 
482
					options.toShow[0].style[settings.prop] =
483
						(percentDone * showProps[settings.prop].value) + showProps[settings.prop].unit;
484
				},
485
				duration: options.duration,
486
				easing: options.easing,
487
				complete: function() {
488
					if ( !options.autoHeight ) {
489
						options.toShow.css("height", "");
490
					}
491
					options.toShow.css("width", originalWidth);
492
					options.toShow.css({overflow: overflow});
493
					options.complete();
494
				}
495
			});
496
		},
497
		bounceslide: function(options) {
498
			this.slide(options, {
499
				easing: options.down ? "easeOutBounce" : "swing",
500
				duration: options.down ? 1000 : 200
501
			});
502
		}
503
	}
504
});
505
 
506
})(jQuery);