client/player/js/ui/jquery.ui.accordion.js
changeset 1 436d4791d7ac
child 57 3a3c15c462f8
equal deleted inserted replaced
0:ecdfc63274bf 1:436d4791d7ac
       
     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);