User:Phiarc/October2011/mabe-october.js

From mediawiki.org

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
/**
 * mabe-october.js 
 * Copyright (c) 2011 Marian Beermann
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

// JS Hint settings (jshint.com)
/*global $:true, jQuery:true, browser:true, mw:true */
/*jshint strict:false */

//TODO:
// * compat IE6-8

// CSS is dynamically added at runtime
mw.loader.implement( "slideshow", [ "//www.mediawiki.org/w/index.php?title=User:Phiarc/October2011/jquery.cycle.all.js&action=raw&ctype=text/javascript", "//www.mediawiki.org/w/index.php?title=User:Phiarc/October2011/jquery.scrollTo.js&action=raw&ctype=text/javascript" ], {}, {} );

//------------------------------ Script entry point
mw.loader.using ( "slideshow", function () {
	jQuery( document ).ready( function ( $ ) {
	
		// Custom effect for jQuery.Cycle
		$.fn.cycle.transitions.SlideshowTransitionFX = function ( $cont, $slides, opts ) {
			$cont.width();
			$slides.not( ":eq(" + opts.currSlide + ")" ).css( "opacity", 0 );
	
			opts.before.push( function( curr, next, opts, fwd ) {
				if( opts.rev )
				{
					fwd = !fwd;
				}
				
				$.fn.cycle.commonReset( curr, next, opts );
				
				opts.cssBefore.left = fwd ? ( next.cycleW - 1 ) : ( 1 - next.cycleW );
				opts.cssBefore.opacity = 0;
				opts.animOut.left = fwd ? -curr.cycleW : curr.cycleW;
			} );
			
			opts.cssFirst.left = 0;
			opts.cssBefore.top = 0;
			
			opts.animIn.left = 0;
			opts.animIn.opacity = 1;
			
			opts.animOut.top = 0;
			opts.animOut.opacity = 0;
		};
		
		//------------------------------ Slideshow object
		var Slideshow = {};
		
		Slideshow.Initialized = false;
		Slideshow.Mobile = false;
		Slideshow.WikiPath = "//" + document.domain;
		Slideshow.CycleOpts = {};
		
		// Simple spin-lock mechanism
		Slideshow.lock = false;
		Slideshow.Access = function( fn ) {
			if( Slideshow.lock ) {
				window.setTimeout( function() {
					Slideshow.Access( fn );
				}, 50 );
			} else {
				fn();
			}
		};
		
		//------------------------------ Config
		// The config is a mw.Map extended by loading and storing the contained data to cookies
		//  and "global" default values
		Slideshow.Config = new mw.Map();
		
		Slideshow.Config.load = function () {
			Slideshow.Config.values = Slideshow.Config.defaults;
			
			$.each( Slideshow.Config.values, function ( eindex, elem ) {
				var a = "Slideshow." + eindex;
				var b = $.cookie( a );
	
				// Check if cookie is set
				if( b !== null ) {
					Slideshow.Config.values[ eindex ] = b;
				}
			} );
		};
		
		Slideshow.Config.store = function () {
			$.each( Slideshow.Config.values, function ( eindex, elem ) {
				$.cookie( "Slideshow." + eindex, null );
				$.cookie( "Slideshow." + eindex, elem, { expires: 3650 } );
			} );
		};
		
		// Just to implement central default values
		Slideshow.Config.getOld = Slideshow.Config.get;
		Slideshow.Config.get = function ( selection ) {
			var u = this.getOld( selection, Slideshow.Config.defaults[ selection ] );
			var v;
			
			switch( u ) {
				case "true":
					v = true;
					break;
				case "false":
					v = false;
					break;
				default:
					v = u;
			}
				
			return v;
		};
		
		Slideshow.Config.defaults = {			
			// Commmons
			"Commons.Enable": true,
			"Commons.Limit": 10,
			"Commons.Symbol12px": "//upload.wikimedia.org/wikipedia/commons/thumb/4/4a/Commons-logo.svg/12px-Commons-logo.svg.png",
			
			// Animation
			"Animation.Wrapper": 450,
			"Animation.Slides": 500,
			
			// Mobile
			"Mobile.Force": false,
			"Mobile.Resizing": false,
			"Mobile.UI.LeftArrow": "//upload.wikimedia.org/wikipedia/commons/thumb/5/56/Go-previous-grey.svg/40px-Go-previous-grey.svg.png",
			"Mobile.UI.RightArrow": "//upload.wikimedia.org/wikipedia/commons/thumb/1/13/Go-next-grey.svg/40px-Go-next-grey.svg.png",
			"Mobile.UI.DownArrow": "//upload.wikimedia.org/wikipedia/commons/thumb/2/22/Go-down-grey.svg/40px-Go-down-grey.svg.png",
			
			// Markup (also contains many i18n-relevant strings)
			"Markup.Main": '<div id="slideshow-wrapper" style="display: none;"><div id="slideshow-top"></div><div id="slideshow-main"><div class="close">Close</div><div class="secondary-button"></div><div class="left-arrow"></div><div class="slideshow"></div><div class="right-arrow"></div><div class="thumbnails"></div></div><div id="slideshow-bottom"></div></div>',
			"Markup.Config": '<div id="preferences" style="display:none; font-size:0.8em;" title="Slideshow settings"><fieldset><legend>Slideshow settings</legend><table class="" id="mw-slideshow-config"><tbody><tr><td class="mw-label"><label for="mw-input-sscommons-enable"> Enable integration with Wikimedia Commons:<br><i>(This uses a AJAX service provided by anyorigin.com)</i></label></td><td class="mw-input"><input type="checkbox" id="mw-input-sscommons-enable" /></td></tr><tr><td class="mw-label"><label for="mw-input-sscommons-limit"> Maximal number of images included from Wikimedia Commons: </label></td><td class="mw-input"><input  type="number" id="mw-input-sscommons-limit" size="20" /></td></tr><tr><td class="mw-label"><label for="mw-input-ssanimation-wrapper"> Duration of the slideshow fade in and out animations (milliseconds): </label></td><td class="mw-input"><input type="number" id="mw-input-ssanimation-wrapper" size="20" /></td></tr><tr><td class="mw-label"><label for="mw-input-ssanimation-slides"> Duration of the animation between the slides (milliseconds): </label></td><td class="mw-input"><input type="number" id="mw-input-ssanimation-slides" size="20" /></td></tr><tr><td class="mw-label"><label for="mw-input-ssmobile-force">Always use the mobile mode</label></td><td class="mw-input"><input type="checkbox" id="mw-input-ssmobile-force" /></td></tr><tr><td class="mw-label"><label for="mw-input-ssmobile-resizing">Enable resizing when in mobile mode</label></td><td class="mw-input"><input type="checkbox" id="mw-input-ssmobile-resizing" /></td></tr><tr><td class="mw-label"><input type="button" id="mw-input-sshide" value="Hide (don\'t save)" /></td><td class="mw-input"><input type="button" id="mw-input-sssave" value="Save" /></td></tr></tbody></table></fieldset></div>',
			
			// Other stuff
			"Style.BaseURL": "//www.mediawiki.org/w/index.php?action=raw&ctype=text/css&title=User:Phiarc/October2011/",
			
			// Internationalization
			"i18n.StartSlideshow": "Start Slideshow",
			"i18n.Slideshow": "Slideshow",
			"i18n.SlideshowSettings": "Slideshow settings"
		};
	
		//------------------------------ Objects' functions
		Slideshow.ShowConfig = function ( e ) {
			// This maps the properties of Slideshow.Config to their corresponding input field ids
			var config_map = {
				"Commons.Enable": "mw-input-sscommons-enable",
				"Commons.Limit": "mw-input-sscommons-limit",
				"Animation.Wrapper": "mw-input-ssanimation-wrapper",
				"Animation.Slides": "mw-input-ssanimation-slides",
				"Mobile.Force": "mw-input-ssmobile-force",
				"Mobile.Resizing": "mw-input-ssmobile-resizing"
			};
			
			function load_fn ( idx, e ) {
				var a = $( "#" + config_map[ idx ] );
				
				if( a.attr( "type" ) === "checkbox" ) {
					if( e === true || e === "true") {
						a.attr( {
							value: 1,
							checked: "checked"
						} );
					} else {
						a.attr( "value", "0" );
						a.removeAttr( "checked" );
					}
				} else {
					a.val( e );
				}
			}
			
			function save_fn ( idx, e ) {
				var a = $( "#" + config_map[ idx ] );
				var b = a.val();
				var c;
	
				switch( a.attr( "type" ) ) {
					case "checkbox":
						if( a.filter(":checked").length !== 0 ) {
							c = true;
						} else {
							c = false;
						}
						break;
					case "number":
						if( !isNaN( parseInt( b, 10 ) ) ) {
							c = parseInt( b, 10 );
						}
						
						if( idx === "Animation.Slides" && Slideshow.CycleOpts !== undefined) {
							Slideshow.CycleOpts.speed = Slideshow.CycleOpts.speedIn = Slideshow.CycleOpts.speedOut = c;
						}
						break;
					default:
						c = b;
				}
	
				Slideshow.Config.values[ idx ] = c;
			}
			
			function hide_fn () {
				$( "#preferences" ).css( "display", "none" );
				$( "#pt-slideshow" ).click( Slideshow.ShowConfig );
			}
			
			if( $( "#preferences" ).length === 0 ) {
				$( "#content" ).prepend( Slideshow.Config.get( "Markup.Config" ) );
			}
			
			$( "#preferences" ).css( "display", "block" );
			
			$( this ).unbind( e ).click( hide_fn );
			
			$.each( Slideshow.Config.values, load_fn );
			
			$( "#mw-input-sshide" ).click( hide_fn );
			$( "#mw-input-sssave" ).click( function () {
				$.each( Slideshow.Config.values, save_fn );
				Slideshow.Config.store();
	
				hide_fn();
			} );
		};
		
		Slideshow.ArticleHasImages = function () {
			if( ( $( ".thumb .image img" ).length + $( ".infobox .image" ).length ) === 0 ) {
				return false;
			}
			
			return true;
		};
		
		Slideshow.GetThumbnailURL = function ( BaseURL, ImageId, size ) {
			return BaseURL + "thumb" + ImageId + "/" + size + "px-" + ImageId.split( "/", 4)[3];
		};
		
		Slideshow.Display = function ( showv, animatev ) {
			var show = showv === undefined ? true : showv;
			var animate = animatev === undefined ? true : animatev;
			var wrapper = $( "#slideshow-wrapper" );
			var dur = Slideshow.Config.get( "Animation.Wrapper" );
			
			// Do we actually need to do anything?
			if( ( show && wrapper.css( "display" ) === "block" ) || ( !show && wrapper.css( "display" ) === "none" ) ) {
				return true;
			}
			
			if( animate ) {
				if( show ) {
					wrapper.fadeIn( dur );
				} else {
					wrapper.fadeOut( dur );
				}
			} else {
				if( show ) {
					wrapper.css( "display", "block" );
				} else {
					wrapper.css( "display", "none" );	
				}
			}
			
			if( Slideshow.ShouldResize() ) {
				Slideshow.OnWindowResize( 0 );
			}
		};
		
		Slideshow.StartSlideshow = function () {		
			if( !Slideshow.Initialized ) {			
				//------------------------------ Insert markup
				$( "body" ).append( Slideshow.Config.get( "Markup.Main" ) );
				
				$( "#slideshow-main .slideshow" ).addClass( "loading" );
	
				Slideshow.Display();

				Slideshow.OnWindowResize( true );
				$( "#slideshow-main .slideshow" ).css( "height", $( "#slideshow-main" ).height() );
				
				
				// Handles resizing
				if( Slideshow.ShouldResize() ) {
					$( window ).resize( Slideshow.OnWindowResize );
					
					window.onresize = Slideshow.OnWindowResize;
					
					setInterval( function() {
						Slideshow.OnWindowResize();
					}, 150 ); 
				}
				
				$( "#slideshow-top, #slideshow-bottom, .close" ).click( Slideshow.CloseSlideshow );
				
				// Do the rest asynchronously to not disturb UI rendering etc.
				setTimeout( function() {
					//------------------------------ START OF HELPER FUNCTIONS
					function AddImage ( index, obj, caption ) {
						// This Regex explained:
						// \w{1}\/\w{2}\/[^\/]+
						// Well, we may unescape it:
						// \w{1}/\w{2}/[^/]+
						// What this does:
						//   /     - Find a slash
						//   \w{1} - Foloowed by any character
						//   /     - Followed by a slash
						//   \w{2} - Followed by two characters
						//   /     - Followed by jet another slash
						//   [^/]+ - Followed by anything of any length, as long as it doesn't include a slash
						//
						// This basically rips of the filename and basis path of an thumb URL
						// For example:
						//   http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Proteles_cristata-map.png/300px-Proteles_cristata-map.png
						// The regex would only match
						//   0/0d/Proteles_cristata-map.png
						//
						// This allows us to create thumbs of (effectivly) any size and/or
						// to download the image :-)
						// One may ask: Why don't you just use the Special:Filepath interface?
						//   Well, this way I do not need to do additional request. Imagine we have an article with
						//    25 images, then using that interface would result in doing 25 de facto unnecessary
						//    requests. As this script uses already many many requests, I try to avoid unnecessary ones
						var FindImgId = /\/\w{1}\/\w{2}\/[^\/]+/;
						
						var Id = {
							ImageId: obj.attr( "src" ).match( FindImgId )[ 0 ],
							BaseURL: obj.attr( "src" ).split( "thumb", 1 )[ 0 ],
							Caption: caption
						};
						
						// If it the image is *not* embedded as a thumb, the above method for
						//  finding the base URL won't work
						if( Id.BaseURL.indexOf( Id.ImageId ) !== -1 ) {
							Id.BaseURL = obj.attr( "src" ).split( Id.ImageId, 1 )[ 0 ] + "/";
						}
						
						Id.ImageName = Id.ImageId.split( "/", 4 )[ 3 ];
										
						// Create "non-intrusive" jump ID
						if( obj.attr( "id" ) !== undefined ) {
							Id.jumpID = obj.attr( "id" );
						} else {
							Id.jumpID = "image-" + index;
							obj.attr( "id", Id.jumpID );
						}
						
						return Id;
					}
					
					function AddToSlideshow ( ImageData, index, indirect ) {
						var indirectv = false;
						var HTMLString;
						
						if ( ( indirect !== undefined ) && indirect ) {
							indirectv = true;
						}
						
	
						HTMLString =  "<div style='display:none; opacity: 0;' class='slide' id='slide-" + index + "'><div class='slide-wrapper'><img src='" + ImageData[ index ].BaseURL + ImageData[ index ].ImageId + "' /><p>" + ImageData[ index ].Caption + "</p></div></div>";
						
						if( indirectv ) {
							Slideshow.CycleOpts.addSlide( HTMLString );
						} else {
							$( ".slideshow" ).append( HTMLString );
						}
					}
					
					function GetImageDimensions ( index, finalise_fn ) {
						var Titles = "";
						
						if( index === undefined ) {
							$.each( Slideshow.ImageData, function ( eindex, elem ) {
								if( eindex > 0 ) {
									Titles += "|";
								}
								
								Titles += "File:" + elem.ImageName;
							} );
						} else {
							Titles = "File:" + Slideshow.ImageData[ index ].ImageName;
						}
						
						// For documentation: http://www.mediawiki.org/wiki/API:Properties#imageinfo_.2F_ii
						//  tl;dr: gets image size
						// This gets hold of all image information we need with a single query
						$.ajax( Slideshow.WikiPath + "/w/api.php?action=query&prop=imageinfo&iiprop=size|url&format=xml&titles=" + Titles, {
							async: finalise_fn === undefined ? false : true,
							dataType: "xml",
							success: function ( data ) {
								var pages;
								
								pages = $( data.getElementsByTagName( "page" ) );
								
								pages.each( function( eindex, elem ) {
									var ii = $( this ).find( "imageinfo ii" );
									var ai;
									
									if( index === undefined ) {
										ai = eindex;
									} else {
										ai = index;
									}
									
									Slideshow.ImageData[ ai ].Width = ii.attr( "width" );
									Slideshow.ImageData[ ai ].Height = ii.attr( "height" );
									
									if( finalise_fn !== undefined ) {
										finalise_fn( ai );
									}
								} );
							}
						} );
					}
					
					function SetBackgroundURI( e, cfgname ) {
						e.css( "background-image", "url(" + Slideshow.Config.get( cfgname ) + ")" );
					}
					//------------------------------ END OF HELPER FUNCTIONS
					
				
				//------------------------------ Rip images from article
				var Thumbnails = [];
				var index = 0;
				var bc = $( "#bodyContent" ); // context optimization
	
				// Handles normal thumbs
				bc.find( ".thumbinner a.image img" ).each( function( eindex, elem ) {				
					Thumbnails[ index ] = AddImage( index, $( this ), bc.find( ".thumb .thumbcaption:eq(" + eindex + ")" ).text() );
					AddToSlideshow( Thumbnails, index );
					
					index += 1;
				} );
				
				// Handles infoboxes, taxoboxes and such - sadly there is no standard class for infoboxes
				bc.find( ".infobox, .wikitable, .taxobox, .toccolours" ).each( function ( ibindex, infobox ) {
					var InfoboxTitle, InfoboxTitleElem, InfoboxObj;
					InfoboxObj = $( infobox );
					
					// The infobox title is nearly always the first cell in the table
					InfoboxTitle = InfoboxObj.find( "th" ).first().text();
					if( InfoboxTitle === undefined ) {
						InfoboxTitle = InfoboxObj.find( "caption" ).first().text();
					}
					if( InfoboxTitle === undefined ) {
						InfoboxTitle = InfoboxObj.find( "td" ).first().text();
					}
					if( InfoboxTitle === undefined ) {
						mw.config.get( "wgTitle" );	
					}
					
					$( this ).find( ".image img" ).each( function( eindex, elem ) {
						Thumbnails[ index ] = AddImage( index, $( this ), InfoboxTitle );
						AddToSlideshow( Thumbnails, index );
											
						index += 1;
					} );
				} );
				
				// Handles links to Wikimedia Commons
				bc.find( ".sisterproject a" ).each( function( eindex, elem ) {
					var commons_images = 0;
					
					if( $( this ).attr( "href" ).search( /commons.wikimedia.org/ ) !== -1 && Slideshow.Config.get( "Commons.Enable" ) === true ) {
						// anyorigin does not offer a https service
						$.ajax( "http://anyorigin.com/get?url=http:" + $( this ).attr( "href" ) , {
							dataType: "jsonp",
							success: function ( data ) {
								$( data.contents ).find( ".thumb a.image img" ).each( function( eindex, elem ) {
									commons_images += 1;
									
									if( commons_images > Slideshow.Config.get( "Commons.Limit" ) ) {
										return false;
									}
																
									var entry = AddImage( index, $( this ), $( ".gallerytext:eq(" + eindex + ") p" ).text() + "&nbsp;<img src='" + Slideshow.Config.get( "Commons.Symbol12px" ) + "' width=12 height=16 />" );
									var already_exists = false;
									
									$.each( Slideshow.ImageData, function( aindex, arrayelem ) {
										if( arrayelem.ImageId.indexOf( entry.ImageId ) !== -1 ) {
											already_exists = true;
										}
									} );
									
									if( !already_exists ) {
										Slideshow.ImageData[ index ] = entry;
										
										GetImageDimensions( index, function( ii ) {
											// As GetImageDimensions uses an asynchronous query we need
											// to delay the execution AddToSlideshow until the query is answered
											// Luckily I implemented such a feature earlier :-)
											AddToSlideshow( Slideshow.ImageData, ii, true );
										} );
											
										index += 1;
									}
								} );
								
								Slideshow.OnWindowResize( true );
							}
						} ); 
					}
				} );
	
					
				Slideshow.ImageData = Thumbnails;
				
				GetImageDimensions();
				
				//------------------------------  Click & hover handlers
				// Click on the image - open file page in new window/tab
				var c = $( "#slideshow-wrapper" );
				
				c.find( ".slide img" ).click( function ( e ) {
					index = $( this ).parent().parent().attr( "id" ).match( /[0-9]+/ );
					
					window.open( Slideshow.WikiPath + "/wiki/File:" + Slideshow.ImageData[ index ].ImageName );
				} );
				
				if( Slideshow.Mobile ) {
					c.find( ".secondary-button" ).click( function ( e ) {
						window.location.hash = Slideshow.ImageData[ Slideshow.CycleOpts.currSlide ].jumpID;
					} );
				}
				
				// Click on the text - jump to the image in the article
				c.find( ".slide p" ).click( function ( e ) {
					Slideshow.Display( false );
					
					index = $( this ).parent().parent().attr( "id" ).match( /[0-9]+/ );
					
					window.location.hash = Slideshow.ImageData[ index ].jumpID;
				} );
				
				if( Slideshow.Mobile ) {
					var arrows;
					arrows = c.find( ".left-arrow, .right-arrow, .close, .secondary-button" );
					
					SetBackgroundURI( arrows.filter( ".left-arrow" ), "Mobile.UI.LeftArrow" );
					SetBackgroundURI( arrows.filter( ".right-arrow" ), "Mobile.UI.RightArrow" );
					SetBackgroundURI( arrows.filter(" .secondary-button" ), "Mobile.UI.DownArrow" );
					
					arrows.mouseenter( function ( e ) {
						$( this ).animate( {
							opacity: 0.85
						}, 175 );
					} );
					
					arrows.mouseleave( function ( e ) {
						$( this ).animate( {
							opacity: 0
						}, 175 );
					} );
				}
				
				//------------------------------ Cycle
				c.find( ".slideshow" ).cycle( {
					fx: 'SlideshowTransitionFX',
					speed: parseInt( Slideshow.Config.get( "Animation.Slides" ), 10 ),
					timeout: 0,
					sync: 0,
					height: '100%',
					containerResize: false,
					pager: '.thumbnails',
					prev: '.left-arrow',
					next: '.right-arrow',
					fit: true,
					before: function ( curr, next, opts ) {
						Slideshow.CycleOpts = opts;
					},
					pagerAnchorBuilder: function ( id, slide ) {
						var ThumbWidth, ThumbHeight;
						var Id = Thumbnails[ id ];
						
						// Calculate width of the thumbnail for a height of exactly 100 pixels
						ThumbWidth = 100 * ( Id.Width / Id.Height );
						
						// If the original image is too small, use it instead
						if( ( ThumbWidth >= Id.Width ) && ( Id.Height <= 100 ) ) {
							return '<a href="#"><img src="' + Id.BaseURL + Id.ImageId + '" height="100" /></a>';
						}
						
						if( isNaN( ThumbWidth ) ) {
							ThumbWidth = 100;
						}
						
						return '<a href="#"><img src="' + Slideshow.GetThumbnailURL( Id.BaseURL, Id.ImageId, ThumbWidth.toFixed( 0 ) ) + '" height="100" /></a>';
					},
					onPrevNextEvent: function ( isNext, id, slide ) {
						c.find( ".thumbnails" ).scrollTo( c.find( ".thumbnails a:eq(" + id + ")" ) );
					}
				} );
				
				$( document ).keypress( function ( e ) {
					var code = 0;
					
					if( e.keyCode === 0 ) {
						code = e.charCode;
					} else {
						code = e.keyCode;
					}
					
					switch( code ) {
						case 27: // Escape
							Slideshow.Display( false );
						
							break;
						case 37: // Arrow left
						case 97: // A
							c.find( ".slideshow" ).cycle( "prev" );
						
							break;
						case 39: // Arrow right
						case 100: // D
							c.find( ".slideshow" ).cycle( "next" );
						
							break;
						case 40: // Arrow down
						case 115: // S
							c.find( ".slide:eq(" + Slideshow.CycleOpts.currSlide + ") p" ).click();
						
							break;
						case 38: // Arrow up
						case 119: // W
							c.find( ".slide:eq(" + Slideshow.CycleOpts.currSlide + ") img" ).click();
						
							break;
					}
				} );
				
				//------------------------------ Finish
				c.find( "#slideshow-main .slideshow" ).removeClass( "loading" );
				
				Slideshow.Initialized = true;
				
				Slideshow.OnWindowResize( true );
				
				}, Slideshow.Config.get( "Animation.Wrapper" ) );
			} else {
				Slideshow.Display();
			}
		};
		
		Slideshow.CloseSlideshow = function () {
			Slideshow.Display( false );
		};
		
		Slideshow.ShouldResize = function () {
			return !Slideshow.Mobile || Slideshow.Config.get( "Mobile.Resizing" );
		};
		
		Slideshow.CenterVertically = function ( elem ) {
			var position;
			
			position  = $( "#slideshow-main" ).height() / 2;
			position  = position - $( elem ).height() / 2; 
			$( elem ).css( "top", position );
			
			return true;
		};
		
		Slideshow.OnWindowResize = function ( e ) {
			// context optimization, since this function can be called very often and is thus very CPU intensive
			var c = $( "#slideshow-wrapper" );
			
			var r = Slideshow.ShouldResize();
			var h = c.find( "#slideshow-main" ).height(), w = c.find( "#slideshow-main" ).width();
			
			if( e === true ) {
				r = true;
			}
			
			c.css( "height", window.innerHeight );
			
			if( !Slideshow.Mobile) {
				Slideshow.CenterVertically( "#slideshow-main .left-arrow, #slideshow-main .right-arrow" );
				
				c.find( ".slideshow" ).css( "height", h - 150 );
				c.find( ".slideshow" ).css( "width", w - 380 );
				// minus 380px:
				//  -the arrows are each 150px wide plus 20 pixels margin on the left (respective right) side
				//  -.slideshow has an margin on both sides of 20 pixels
			} else {	
				c.find( ".slideshow" ).css( {
					width: w,
					height: h
				} );
			}
			
			c.find( ".slideshow .slide-wrapper" ).css( "width", c.find( ".slideshow" ).css( "width" ) );
			
			// This function is called once when the slideshow is not fully initialized
			//  So we need to check if it everything is set up...
			if( Slideshow.Initialized &&  r ) {
				var Height, Width, Img, Para, d;
				
				c.find( ".slideshow > .slide" ).each( function( index, element ) {
					Img = $( this ).find( "img" ).first();
					Para = $( this ).find( "p" );
					d = Slideshow.ImageData[ index ];
					
					if ( $( this ).css( "display" ) === "block" ) {
						if( Slideshow.Mobile ) {
							Height = h;
						} else {
							Height = ( h - 150 ) - Para.outerHeight();
						}
						
						//Width = Height * ( c.Height / c.Width );
						Width = Height * ( Img.Width / Img.Height ) ;
												
						if( ( Height > d.Height ) || ( Width > d.Width ) ) {
							Height = d.Height;
							Width = d.Width;
						}
						
						Img.css( {
							"width": Width,
							"height": Height
						} );
					}
				} );
			}
		};
		//------------------------------ End of object
			
		// This is the 'real' body of the function
		// I put everything into this function in order to not pollute the global namespace
		// (which I personally feel is bad style and the pre-commit checklist also implies it
		//   http://www.mediawiki.org/wiki/Manual:Pre-commit_checklist )
		
		Slideshow.Config.load();
		
		function MobileScreen() {
			// Checks screen size considering orientation, i.e. so called landsacpe and
			//  portrait orientation
			function u ( v, w ) {
				if( screen.availWidth <= w && screen.availHeight <= v ) {
					return true;
				} else {
					if( screen.availWidth <= v && screen.availHeight <= w ) {
						return true;
					} else {
						return false;
					}
				}
			}
			
			return u( 960, 640 ); // iPhone 4S, smartphone screen with currently highest resolution
		}
		
		if( mw.config.get( "wgIsArticle") && ( mw.config.get( "wgNamespaceNumber" ) === 0 ) && Slideshow.ArticleHasImages() ) {
			var css;
			
			if( navigator.userAgent.search( /webOS|iPhone|iPod|iPad|Mobile|Android/i ) !== -1 || MobileScreen() || Slideshow.Config.get( "Mobile.Force" ) === true ) {
				Slideshow.Mobile = true;
				
				css = "mabe-october-mobile.css";
			} else {
				css = "mabe-october.css";	
			}
			
			mw.loader.load( Slideshow.Config.get( "Style.BaseURL" ) + css, "text/css" );
			
			mw.util.addPortletLink( "p-views",
				"#",
				Slideshow.Config.get( "i18n.Slideshow" ),
				"ca-slideshow",
				Slideshow.Config.get( "i18n.StartSlideshow" ),
				"",
				"#ca-edit" );
			
			// We cannot create links to javascript functions belonging to objects only existing
			//  in the local scope of a (anonymous) function. So we just define
			//  Slideshow.StartSlideshow as the handler for the onclick event...
			$( "#ca-slideshow" ).click( Slideshow.StartSlideshow );
		}
		
		// Adds a portlet link to the personal toolbar for the slideshow config
		mw.util.addPortletLink( "p-personal",
			"#",
			Slideshow.Config.get( "i18n.SlideshowSettings" ),
			"pt-slideshow",
			Slideshow.Config.get( "i18n.SlideshowSettings" ),
			"",
			"#pt-watchlist" );
		
		$( "#pt-slideshow" ).click( Slideshow.ShowConfig );
	} );
} );