//----------------------------------------------------------
// This class divides a group jQuery object containing HTML
// elements into slides.
//----------------------------------------------------------
function SlideFactory(args){

   // private instance variables
   var settings = (args && args.settings) ? args.settings : undefined,      // Slide boundaries
       bodyContent = (args && args.bodyContent) ? args.bodyContent : null,  // html collection to break up into slides
       lastSlides = [];                                                     // Last set of slides created

   // setters
   this.setContent = function(content){
       bodyContent = content;
   };
   this.setSettings = function(s){
       settings = s;
   };
    
   // getters
   this.getContent = function(){
       return bodyContent;
   };
   this.getSlides = function(){
       return slides;
   };
   this.getSettings = function(){
       return settings;
   };
   
   this.divideSlideByListItems = function(start, stop){
	    var listLength = 0;
	    var listArray = [];
	    if (bodyContent.slice(start, stop).is("li")){
	       bodyContent.slice(start, stop).each(function(x){
	           if (bodyContent.slice(start, stop).slice(x, x + 1).is("li")){
	               listArray[listLength++] = start + x;
	           }
	       });
	       listArray[listLength] = stop;
	    }
	    return listArray;
	}

   // Creates slides from jQuery collection of elements
   this.generateSlides = function(){

      var slides = [];
      var startIndex = 0;
      var stopIndex = 0;
      var slidesIndex = 0;
      var slidesList = [];
      var leftActive;
      var rightActive;
      var foundSlideEnd = false;
      var slideLists;
      var currentSlice = {};

      while (startIndex < bodyContent.length){
    	  
         currentSlice = bodyContent.slice(startIndex, startIndex + 1);

         // Locate valid starting point for slide
         if (settings.h2TagSlides && currentSlice.is("h2:has(span.mw-headline)") ||
            (settings.h1TagSlides && currentSlice.is("h1:has(span.mw-headline)")) ||
            (currentSlice.is("h3:has(span.mw-headline)"))){

            stopIndex = startIndex + 2;  // End - Start must be at least 2 html elements
            foundSlideEnd = false;
            
            // Find end point for slide
            while (!foundSlideEnd && stopIndex <= bodyContent.length){
            
               currentSlice = bodyContent.slice(stopIndex, stopIndex + 1);

               if ((currentSlice.is("h1:has(span.mw-headline)")) ||
                  (currentSlice.is("h2:has(span.mw-headline)")) ||
                  (currentSlice.is("h3:has(span.mw-headline)")) ||
                  (stopIndex >= bodyContent.length)){

                  // Can user navigate through this slide, or is it a blocking boundary?
                  if (settings.h1TagSlides === false){
                     if (slidesIndex > 0){
                        leftActive = (bodyContent.slice(slides[slidesIndex - 1][0], startIndex + 1).is("h1")) ? false: true;
                     }
                     else{
                        leftActive = false;
                     }
                     rightActive = (bodyContent.slice(stopIndex, stopIndex + 1).is("h1")) ? false : true;
                  }
                  else{
                     leftActive = true;
                     rightActive = true;
                  }
                         
                  // Valid slide end point successfully found
                  if (!(!settings.h3TagSlides && bodyContent.slice(startIndex, stopIndex).is("h3:has(span.mw-headline)"))){

                     slides[slidesIndex] = [startIndex,  // Position of first slide html element
                                            stopIndex,    // Position of last slide html element
                                            leftActive,   // Can the user go backwards from this slide?
                                            rightActive,  // Can the user go forward beyond this slide?
                                            bodyContent.slice(startIndex, stopIndex), // Collection of all elements in current slide
                                            2.000,
                                            0];

                     slidesIndex += 1;
                     foundSlideEnd = true;
                  }
               }
               stopIndex += 1;
            }
         }
         startIndex += 1;
      }

      if (slides.length > 0){
         slides[slides.length - 1][3] = false;  // Can't go forward past last slide
         slides[0][2] = false;                  // Can't go backward past first slide
      }
    
      lastSlides = slides;  // Save local copy of most recent slide set
      return slides;
   };
}

// This class creates and manages a button navigation panel
// Each button reacts to mouse events and has a function associated with it.
function ButtonNavigator(buttonNames,
                         buttonAttributes,
                         parentID,
                         clientNm) {

    ButtonNavigator.timer = {};

    var buttonMap = buttonNames,
        buttons = buttonAttributes,
        parentId = parentID,
        clientName = clientNm || ("default" + Math.round(10000*Math.random()));
    
    var internalName = clientName.replace(" ", ""); // No spaces allowed in ID's
    var controlPanelId = internalName + "Controls"; // Id of button container
    var thisRef = this;                             // Safe reference to this object
    
    // Handler for the mouseover event of a presentation button
    this.mouseOver = function(event) {
        if (buttons[event.target.id]) {
            event.target.src = buttons[event.target.id]["over"];
        }
    };

    // Handler for the mouseout event of a presentation button
    this.mouseOut = function(event) {
        if (buttons[event.target.id]) {
            clearTimeout(ButtonNavigator.timer);
            for (var i = 0; i < buttonMap.length; i++) {
                $("#" + buttonMap[i]).attr("src", buttons[buttonMap[i]]["normal"]);
            }
        }
    };
    
    // Handler for the mouseclick event of a presentation button
    this.mouseClick = function(event) {
        if (buttons[event.target.id]) {
            if (ButtonNavigator.timer !== undefined) {
                clearTimeout(ButtonNavigator.timer);
            }
            event.target.src = buttons[event.target.id]["click"];
        }
    };
    
    this.removeControls = function() {
        if ($("#" + parentId + " *").is("#" + controlPanelId)) {
            $("#" + controlPanelId).remove();
        }
    };
    
    var createButtonHtml = function() {
        var htmlString = "";
        for (var i = 0; i < buttonMap.length; i++) {
            htmlString += '<td>' +
                            '<img style="cursor: pointer;" align="center" height="35" width="35" border="0" id="' + buttonMap[i] + '" src="' + buttons[buttonMap[i]]["normal"] + '" />' +
                          '</td>';
        }
        return htmlString;
    };
    
    this.setupControls = function() {
        var controlsHtml =
            '<table align = "center" style = "width: 15%;" id = "' + controlPanelId + '">' +
              '<tr>' +
                createButtonHtml() +
              '</tr>' +
            '</table>';

        if (!($("#" + parentId + " *").is("#" + controlPanelId))){

           $("#" + parentId).prepend(controlsHtml);
           $("#" + controlPanelId).css("position", "relative");
           $("#" + controlPanelId).css("top", "40px");
           
           for (var i = 0; i < buttonMap.length; i++) {
               $("#" + buttonMap[i]).bind("mouseover", function(event) {
                   thisRef.mouseOver(event);
               });
               $("#" + buttonMap[i]).bind("mouseout", function(event) {
                   thisRef.mouseOut(event);
               });
               $("#" + buttonMap[i]).bind("click", function(event) {
                   thisRef.mouseClick(event);
                   ButtonNavigator.timer =
                   setTimeout("$('#" + event.target.id + "')." + 
                   "attr('src', '" + buttons[event.target.id]["over"] +
                   "'); ButtonNavigator.timer = undefined;", 300);
                   buttons[event.target.id]["callback"]();
               });
           }
        }
    };
}

// This class manages the interaction between the presentation
// and the table of contents entries for the slides.
function TocSlideList(){
    
	var targetElements = "";
	var listItems = {};
	var subsectionLinks = undefined;
	var headerBoundElements = undefined;
	var highlightColor = "#FFFF00";
	
	// Getters
	this.getTargetElements = function(){
	    return targetElements;
	};
	this.getListItems = function(){
	    return listItems;
	};
	this.getSubsectionLinks = function(){
	    return subsectionLinks;
	};
	this.getHighlightColor = function(){
	    return highlightColor;
	};
	this.getHeaderBoundElements = function(){
		if (!headerBoundElements){
			headerBoundElements = $("table#toc li.toclevel-1 > a span, " +
                                    "table#toc li.toclevel-2 > a span, " +
                                    "table#toc li.toclevel-3 > a span");
		}
	    return headerBoundElements;
	};
	
	// Determines slide number selected in table of contents
	this.slideLastClicked = function(event){
		var num = 0;
		$(targetElements).each(function(n){
	         if ($(targetElements).get(n) === event.target){
	             num = n;
	             return false;
	         }
		});
		return num;
	};
	
	// Applies different color to the background of the selected table 
	// of contents slide link
	this.highlight = function(currentSlide){
		
	    if (listItems.slice(currentSlide, currentSlide + 1).get(0).style.display !== "none"){
	    
	       // Clears highlighting of previous slide
	       listItems.css("background-color", "");

	       // Highlights current table of contents slide position
	       listItems.slice(currentSlide, currentSlide + 1).css("background-color", highlightColor);
	    }
	};
	
	// Clears table of contents highlighting 
	this.clearHighlighting = function(){
		listItems.css("background-color", "");
	};
	
	this.findAssociatedHeader = function(event, arraynames){
	    var headerElements = $("span.mw-headline"),
	        headerLocation = 0;

	    headerBoundElements.each(function(x){
	        arraynames[x] = headerElements.parent().get(x).nodeName;
	        if (headerBoundElements.get(x) == event.target){
	            headerLocation = x;
	            return false;
	        }
	    });
	    return headerLocation;
	};
	
	this.findNormalModeSlide = function(settings, tocNumber, headerNodeNames){

	    var h1TagsBeforeCurrent = 0, 
	        h3TagsBeforeCurrent = 0,
	        InactiveSlidesBeforeCurrent = 0,
	        headerNames = headerNodeNames,
	        tocEntryPosition = tocNumber,
	        loopCount = 0,
	        slideNumber = 0;
	    
	    if (settings.h1TagSlides === false &&
	        settings.h3TagSlides === false){
	        while (loopCount < headerNames.length &&
	               loopCount < tocEntryPosition){
	            if (headerNames[loopCount] === "H1"){
	                h1TagsBeforeCurrent += 1;
	            }
	            if (headerNames[loopCount] === "H3"){
	                h3TagsBeforeCurrent += 1;
	            }
	            loopCount += 1;
	        }
	    }
	    if (settings.h1TagSlides === true &&
	        settings.h3TagSlides === false){
	        while (loopCount < headerNames.length &&
	               loopCount < tocEntryPosition){
	             if (headerNames[loopCount] === "H3"){
	                 h3TagsBeforeCurrent += 1;
	             }
	             loopCount += 1;
	         }
	    }
	    if (settings.h1TagSlides === false &&
	        settings.h3TagSlides === true){
	        while (loopCount < headerNames.length &&
	                loopCount < tocEntryPosition){
	             if (headerNames[loopCount] === "H1"){
	                 h1TagsBeforeCurrent += 1;
	             }
	             loopCount += 1;
	         }
	    }
	    InactiveSlidesBeforeCurrent = h1TagsBeforeCurrent +
	                                  h3TagsBeforeCurrent;
	    slideNumber = tocEntryPosition - InactiveSlidesBeforeCurrent;
	    
	    if (slideNumber < 0) slideNumber = 0;
	    
	    return slideNumber;
	};
	
    this.initialize = function(psettings){
    	
    headerBoundElements = $("table#toc li.toclevel-1 > a span, " +
    	                    "table#toc li.toclevel-2 > a span, " +
                            "table#toc li.toclevel-3 > a span");

 	if (psettings.h1TagSlides === false && psettings.h3TagSlides === false){
 	    targetElements = "table#toc li.toclevel-2 > a[href^='#'] span";
 	    listItems = $("table#toc li.toclevel-2");
 	    subsectionLinks = $("table#toc li.toclevel-2 > a[href^='#']");
 	}
 	else if (psettings.h1TagSlides === true && psettings.h3TagSlides === false){
 	    targetElements = "table#toc li.toclevel-1 > a[href^='#'] span, " +
 		"table#toc li.toclevel-2 > a[href^='#'] span";
 	    listItems = $("table#toc li.toclevel-1, " +
 					   "table#toc li.toclevel-2");
 	    subsectionLinks = $("table#toc li.toclevel-1 > a[href^='#'], " +
 						 "table#toc li.toclevel-2 > a[href^='#']");
 	}
 	else if (psettings.h1TagSlides === false && psettings.h3TagSlides === true){
 	    targetElements = "table#toc li.toclevel-2 > a[href^='#'] span, " +
 		"table#toc li.toclevel-3 > a[href^='#'] span";
 	    listItems = $("table#toc li.toclevel-2, " +
 					   "table#toc li.toclevel-3");
 	    subsectionLinks = $("table#toc li.toclevel-2 > a[href^='#'], " +
 						 "table#toc li.toclevel-3 > a[href^='#']");
 	}
 	else{
 	    targetElements = "table#toc li.toclevel-1 > a[href^='#'] span, " +
 		"table#toc li.toclevel-2 > a[href^='#'] span, " +
 		"table#toc li.toclevel-3 > a[href^='#'] span";
 	    listItems = $("table#toc li.toclevel-1, " +
 					   "table#toc li.toclevel-2, " +
 					   "table#toc li.toclevel-3");
 	    subsectionLinks = $("table#toc li.toclevel-1 > a[href^='#'], " +
 						 "table#toc li.toclevel-2 > a[href^='#'], " +
 						 "table#toc li.toclevel-3 > a[href^='#']");
 	}
    };
}

/* Returns a cross browser compatible height or width value for the window */
function getDimension(dim){
    if (dim === "height"){
       if (window.innerHeight){
          return window.innerHeight;
       }
       else if (document.documentElement.clientHeight){
          return document.documentElement.clientHeight;
       }
       else if (document.body.clientHeight){
          return document.body.clientHeight;
       }
    }
    else if (dim === "width"){
       if (window.innerWidth){
          return window.innerWidth;
       }
       else if (document.documentElement.clientWidth){
          return document.documentElement.clientWidth;
       }
       else if (document.body.clientWidth){
          return document.body.clientWidth;
       }
    }
    return undefined;
}

var presentation = {
//-----------------------------------------------------------
// This group of properties consists of the settings which
// can be set by the user in the F10 menu. The values here
// are the defaults for presentation mode if settings XML
// code is not found for the page.
//-----------------------------------------------------------
    settings: {textSizeIncrease: 120,            // Text size percentage
               F6Resize : "1024x768",            // Size of page after resize
               incrementalListItemReveal: true,  // show lists in increments
               showListItemsBackwards: true,     // Show entire previous slide
               listHideBackwards: false,         // Hides lists in steps
               h1TagSlides: true,                // Displays h1 sections
               h2TagSlides: true,
               h3TagSlides: false,               // Displays h3 sections
               showControls: false,              // Displays button controls
               autoStart: false,
               displayTOC: true
              },
//-----------------------------------------------------------
// The style property of the presentation object contains
// css information needed by presentation mode.
//-----------------------------------------------------------
    style: {contentMargin:
              {"normalTocShown": "1.5em 0 2em 20.2em",
               "presentTocShown": "0.5em 0em 0.5em 20.2em",
               "normalTocHidden": "1.5em 0 2em 1em",
               "presentTocHidden": "0.5em 1em 1em 1em"
              }
           },
    state: {activated: false,
            running: false,
            runBefore: false,
            h1SlidesPrevious: true,
            h2SlidesPrevious: true,
            h3SlidesPrevious: true,
            h3SlidesPossible: false
           },
    toc: {display: 
             {normalmode: true,
              action: "entering"
             }
         },
    slides: [],  // Contains start/stop points for each slide
    down: false,
    mousePos: 0,
    origwidth: 0,
    barThis: {},
    thisNum: {},
    slideActing: 0,
    back: false,
    parentId: "globalWrapper",
    contentId: "content",
    pageName: "",
    XML: "",
    xmlObject: {},
    xmlrequestMode: ""
   },
   slidesList = [],
getXMLfromServer = false,
sendXMLToServer = false,
presentationInProgress = false,
elem = {},
tocBindings = {
     flash: function(event){
        flashHeader(event);
     }
},
documentBindings = {
     outside: function(event){
         if (event.which === 123){
             setupPresentationMode();
             return false;
         }
         if (event.which === 115){
             showTableofContents();
             return false;
         }
         if (event.which === 116){
             presentationMode(event);
             return false;
         }
     },
     inside: function(event){
         if (event.which === 116){
             exitPresentation();
             return false;
         }  
         if (event.which === 115){
             showTableofContents();
             return false;
         }
         if (event.which === 27){
             exitPresentation();
         }
         if (event.which === 32 || event.which === 39 || event.which === 40){
             moveForward();
         }
         else if (event.which === 37 || event.which === 38){
             moveBackward();
         }
    }
},
windowBindings = {
        adjustImages: function(event) { updateContentWindowHeight(); adjustImageSizes(); }
},
currentSlide = 0,
oldSlidesLength = 0,
imageOriginalDimensions = [], imagesStoredToArray = false,
previousFrameWidth = 850,
contentElement,
bodyContent, bodyContentImages, bodyContentLength = 0,
presentationDocument, presentationWindow,
presentLinks,
currentListItem = 0,
presentSpanElements = "div#bodyContent span.editsection:has(a.presentationLink)",
slideCreator = new SlideFactory(),
tocSlideLinks = new TocSlideList(),
slideSort = new SlideSorter("globalWrapper",
                            "1.5em 0 2em 20.2em",
                            "content"),
navigationPanel = new ButtonNavigator(["presentLeft", "presentExit", "presentRight"],
   {"presentLeft": {"callback": moveBackward,
                    "over": "http://www.cds130.org/wiki/images/PresentButtonLeftOver.png",
                    "normal": "http://www.cds130.org/wiki/images/PresentButtonLeft.png",
                    "click": "http://www.cds130.org/wiki/images/PresentButtonLeftClick.png"},
    "presentExit": {"callback": exitPresentation,
                    "over": "http://www.cds130.org/wiki/images/PresentButtonExitOver.png",
                    "normal": "http://www.cds130.org/wiki/images/PresentButtonExit.png",
                    "click": "http://www.cds130.org/wiki/images/PresentButtonExitClick.png"},
    "presentRight": {"callback": moveForward,
                     "over": "http://www.cds130.org/wiki/images/PresentButtonRightOver.png",
                     "normal": "http://www.cds130.org/wiki/images/PresentButtonRight.png",
                     "click": "http://www.cds130.org/wiki/images/PresentButtonRightClick.png"}},
                     "content",
                     "presentation");

function autoPlay(frequency) {
    if (!presentation.state.running) {
        presentationMode("auto");
    } else {
        if (!presentation.back) {
           nextSlide();
        } else {
           previousSlide();
        }
    }

    setTimeout("autoPlay(" + frequency + ")", (1/frequency)*1000);

    if (!presentation.back && currentSlide === presentation.slides.length - 1) {
    	presentation.back = true;
    }
    else if (presentation.back && currentSlide === 0) {
    	presentation.back = false;
    }
}

/* This function activates presentation mode on the page by placing the present
   links and binding the F5 key by which the user can enter the presentation
   mode and display the sections on the page as slides.  The user can
   toggle the present links onto and off of the page with this function. */
function setupPresentationMode(){

    // Gets collection of elements representing the presentation
    bodyContent = $("div#bodyContent *");
    bodyContentImages = $("div#bodyContent img").not(".tex");
    contentElement = $("div#content");
    bodyContentLength = bodyContent.length;

    var presentLinkTarget,
	presentLinkHtml = '<span class = "editsection">' +
	'<a class = "presentationLink" title="Display section in presentation mode. ESC to exit.">+</a></span>';

    if ((presentation.state.running === false) &&
        (presentation.state.activated === false)){

	presentation.state.activated = true;
         
	// Tests to see which header sections can be displayed in
	// presentation mode.
	presentationCapability();
         
	// Places "+" links at the top right of each page section corresponing
	// to a slide, giving the user the option to enter the presentation at
	// that point by clicking the link.
	if (presentation.settings.h1TagSlides === false && presentation.settings.h3TagSlides === false){
	    presentLinkTarget = "div#bodyContent h2:has(span.mw-headline)";
	}
	else if (presentation.settings.h1TagSlides === true && presentation.settings.h3TagSlides === false){
	    presentLinkTarget = "div#bodyContent h1:has(span.mw-headline), " +
		"div#bodyContent h2:has(span.mw-headline)";
	}
	else if (presentation.settings.h1TagSlides === false && presentation.settings.h3TagSlides === true){
	    presentLinkTarget = "div#bodyContent h2:has(span.mw-headline), " +
		"div#bodyContent h3:has(span.mw-headline)";
	}
	else{
	    presentLinkTarget = "div#bodyContent h1:has(span.mw-headline), " +
		"div#bodyContent h2:has(span.mw-headline), " +
		"div#bodyContent h3:has(span.mw-headline)";
	}
	$(presentLinkHtml).prependTo(presentLinkTarget);
	presentLinks = $("div#bodyContent a.presentationLink");
	presentLinks.css("cursor", "pointer");

	// We don't want to show horizontal scrollbar
	$("body").css("overflow-x", "hidden");

	// Builds relationship between presentation and table of contents
	tocSlideLinks.initialize(presentation.settings);

	// Generates array of presentation slides by breaking up the page
	slideCreator.setSettings(presentation.settings);
	slideCreator.setContent(bodyContent);
	presentation.slides = slideCreator.generateSlides();
	slideSort.setSlides(presentation.slides);

	// Binds the "+" links to start presentation at their locations
	presentLinks.bind("click", function(event){
		presentationMode(event);
	});
    }
    else if ((presentation.state.running === false) &&
             (presentation.state.activated === true)){

       // User has chosen to remove presentation links
       // from page and restore it to its normal state
	   presentation.state.activated = false;
	   $("body").removeAttr("style");
	   $(presentSpanElements).remove();
    }
}

// Enters presentation (slide view) mode
function presentationMode(event){

    if (presentation.state.activated === false){
        setupPresentationMode();
    }

    presentation.state.running = true;

    // Is table of contents displayed in normal mode?
    if ($("div#column-one").css("display") === "none"){
        presentation.toc.display.normalmode = false;
    }
    else{
        presentation.toc.display.normalmode = true;
    }

    // Presentation mode binding adjustments
    $(window).bind("resize", windowBindings.adjustImages);
    $(document).unbind("keydown", documentBindings.outside);
    $(document).bind("keydown", documentBindings.inside);
    $("#toolsPresentLink").unbind();
    tocSlideLinks.getHeaderBoundElements().unbind("click", tocBindings.flash);
    tocSlideLinks.getSubsectionLinks().bind("mousedown", function(event){
       gotoSection(event);
    });
    setResizeBinding();

    // Determines presentation start location
    if (event.which !== 116 && event !== "toolsLink" && event !== "auto"){
        presentLinks.each(function(n){
		    if (presentLinks.get(n) === event.target){
		        currentSlide = n;
		        return false;
		    }
	    });
    }

    presentation.state.runBefore = true;

    // Hides areas of page not wanted in presentation
    $("div#column-one, div#p-personal, div#p-cactions, div#footer").hide();
    bodyContent.hide();

    /* Alters the padding-top css property of the content div to move it
       closer to the top of the page while in presentation mode. */
    contentElement.css("padding-top", "0.5em");
    contentElement.css("overflow-y", "auto");
    updateContentWindowHeight();
    
    // Enlarge the images for presentation mode.
    adjustImageSizes();
    
    // Increases the text size for presentation mode.
    fontSize = (presentation.settings.textSizeIncrease/100).toString() + "em";
    $("#bodyContent").css("font-size", fontSize);

    // Displays the current slide in presentation mode.
    displaySlide(currentSlide);

    // Highlights the current slide name in the table of contents.
    tocSlideLinks.highlight(currentSlide);

    // Save old table of contents state
    presentation.toc.display.action = "entering";
    showTableofContents();
    
    // Places forward/backward navigation buttons on screen
    // as an alternative to using keys to control presentation
    if (presentation.settings.showControls){
        navigationPanel.setupControls();
    }
}

// Closes slideshow and returns page to normal state
function exitPresentation(){

    var exitScrollPosition;
    
    $("#bodyContent").css("font-size", "1.0em");  // Set normal text size
    restoreImageDimensions();                     // Set normal image size
    $(presentSpanElements).show();                // Displays "+" slide links

    // Clears highlighting of current slide in table of contents
    tocSlideLinks.clearHighlighting();
    
    // Shows the page details that were hidden when in the presentation mode.
    $("div#p-personal, div#p-cactions, div#footer, a#col1hidelink").show();
    $("embed").show();

    // Displays all the main content except for the script tag and the
    // jump-to div tag at the top of the content section.
    bodyContent.not(".smartTOCSettings, script, div#jump-to-nav, div.printfooter, #siteSub, .config").show();

    // Show/Hide sections should be hidden
    $("table[class = 'wikitable collapsible collapsed'] tbody > tr:nth-child(2)").hide();
    
    // Places table of contents back into initial state
    presentation.settings.displayTOC = ($("#column-one").css("display") === "none") ? false : true;
    presentation.toc.display.action = "exiting";
    showTableofContents();

    // Restores original content div css settings
    contentElement.css("padding-top", "2em").css("height", "auto");
    
    // Switches bindings back to normal mode
    $(window).unbind("resize", windowBindings.adjustImages);
    $(document).unbind("keydown", documentBindings.inside);
    $(document).bind("keydown", documentBindings.outside);
    tocSlideLinks.getSubsectionLinks().unbind("mousedown");
    tocSlideLinks.getHeaderBoundElements().bind("click", tocBindings.flash);
    setResizeBinding();

    // Scrolls to the position of the slide that the user was viewing when
    // they exited presentation mode.
    exitScrollPosition = bodyContent.slice(presentation.slides[currentSlide][0],
            presentation.slides[currentSlide][0] + 1).offset();
    window.scrollTo(0,exitScrollPosition.top);
    
    presentation.state.running = false;
    
    // Removes button controls if present
    navigationPanel.removeControls();
}

/* Determines whether to display the table of contents and adjust the contents
div or not based on several factors.  If the user is entering or exiting
presentation mode, the table of contents is restored to the state that
it had when they were last in that particular mode. If they are 
already in presentation mode, then it simply alternates between 
displayed and hidden every time the showTableofContents function is called. */
function showTableofContents() {
   if (presentation.state.running === true) {
     if ((presentation.settings.displayTOC === false && presentation.toc.display.action === "alternate") ||
         (presentation.settings.displayTOC === true && presentation.toc.display.action === "entering")) {
          $("div#content").css("margin", presentation.style.contentMargin.presentTocShown);
          $("div#column-one").show();
          presentation.toc.display.action = "alternate";
          presentation.settings.displayTOC = true;
          adjustImageSizes();
     }
     else if (presentation.settings.displayTOC === true && presentation.toc.display.action === "exiting") {
          $("div#column-one").show();
          $("div#content").css("margin", presentation.style.contentMargin.normalTocShown);
          presentation.toc.display.action = "entering";
          presentation.settings.displayTOC = true;
     }
     else if (presentation.settings.displayTOC === false && presentation.toc.display.action === "exiting") {
          $("div#column-one").hide();
          $("div#content").css("margin", presentation.style.contentMargin.normalTocHidden);
          presentation.toc.display.action = "entering";
          presentation.settings.displayTOC = false;
     } else {
          $("div#column-one").hide();
          $("div#content").css("margin", presentation.style.contentMargin.presentTocHidden);
          presentation.toc.display.action = "alternate";
          presentation.settings.displayTOC = false;
          adjustImageSizes();
     }
   } else {
       if ($("div#column-one").css("display") === "none") {
            $("div#column-one").show();
            $("div#content").css("margin", presentation.style.contentMargin.normalTocShown);
       } else {
            $("div#column-one").hide();
            $("div#content").css("margin", presentation.style.contentMargin.normalTocHidden);
       }
   }
}

//Adjusts the height of the content window as page is resized
function updateContentWindowHeight() {
    if (window.innerHeight) {
       contentElement.css("height", parseInt(window.innerHeight.toString(), 10) - 90);
    } else {
       contentElement.css("height", parseInt(document.documentElement.clientHeight.toString(), 10) - 90);
    }
}

// Restores image sizes back to their normal dimensions
function restoreImageDimensions() {
    var previousWidth, previousHeight;
    
    if (bodyContent.is("img")) {  // Are there images in presentation?
        bodyContentImages.each(function(n) {
            previousWidth = imageOriginalDimensions[n][0];
            previousHeight = imageOriginalDimensions[n][1];
            bodyContentImages.slice(n, n + 1).width(previousWidth).height(previousHeight);
        });
    }
}

// Changes image dimensions upon page resize
function adjustImageSizes(){
	var currentImage, contentWidth, ratioHeightWidth, widthFactor,
    roundedFactor, width, height, previousWidth, previousHeight;

   // Are images present?
   if (bodyContent.is("img")){

     // Loops through the images on the page and changes the width
     // and height in a way that preserves the original size ratio.
     bodyContentImages.each(function(n){

        // Gets the current image and stores it to a variable 
        // for use in the current iteration of the loop.
        currentImage = bodyContentImages.slice(n, n + 1);

        // Stores normal image dimensions
        if (imagesStoredToArray === false) {
           if (currentImage.get(0).getAttribute("height") === null) {
               height = currentImage.get(0).naturalHeight;
               width = currentImage.get(0).naturalWidth;
               imageRatio = height/width;
               width = currentImage.attr("width");
               height = width*imageRatio;
           } else {
               width = currentImage.attr("width");
               height = currentImage.attr("height");
           }
           imageOriginalDimensions[n] = [width,height];
        } else {
           width = imageOriginalDimensions[n][0];
           height = imageOriginalDimensions[n][1];
        }

        // Determines new image height and width upon page resize
        if (typeof width === "number" && typeof height === "number"){
            contentWidth = contentElement.css("width");
            contentWidth = contentWidth.slice(0, -2);
            contentWidth = parseInt(contentWidth, 10);
            ratioHeightWidth = height/width;
            width = (width / previousFrameWidth)*contentWidth;
            if (width > contentWidth - 50){
                width = contentWidth - 50;
            }
            height = width*ratioHeightWidth;
            if (height > window.innerHeight - 175){
                height = window.innerHeight - 175;
                width = height/ratioHeightWidth;
            }

            // Rounds width to the nearest multiple of 30.
            widthFactor = width/30;
            roundedFactor = Math.round(widthFactor);
            width = 30*roundedFactor;

            height = width*ratioHeightWidth;
            width = width.toString() + "px";
            height = height.toString() + "px";
            currentImage.css("width", width).css("height", height);
        }
    });
    imagesStoredToArray = true;
  }
}

// These three functions navigate through the list item components
// making up a slide, allowing a slide to be incrementally displayed/hidden
function nextListItem() {
    currentListItem += 1;
    bodyContent.slice(presentation.slides[currentSlide][0],
    presentation.slides[currentSlide][6][currentListItem + 1]).not("table[class = 'wikitable collapsible collapsed'] tbody > tr:nth-child(2), script, div#jump-to-nav, div.printfooter, #siteSub, .config").show();
}
function previousListItem() {
    bodyContent.slice(presentation.slides[currentSlide][6][currentListItem],
                      presentation.slides[currentSlide][1]).hide();
    currentListItem -= 1;
}
function displayAllListItems() {
    currentListItem = presentation.slides[currentSlide][6].length - 2;
    bodyContent.slice(presentation.slides[currentSlide][0],
                      presentation.slides[currentSlide][1]).not("table[class = 'wikitable collapsible collapsed'] tbody > tr:nth-child(2), script, div#jump-to-nav, div.printfooter, #siteSub, .config").show();
}

// Shows the current slide of the presentation
function displaySlide(currentSlide) {

    var slideStart = presentation.slides[currentSlide][0];
    var slideEnd = presentation.slides[currentSlide][1];

    // Shows the next slide of the presentation (without displaying unwanted elements)
    bodyContent.slice(slideStart, slideEnd).not("table[class = 'wikitable collapsible collapsed']" +
        " tbody > tr:nth-child(2), script, div#jump-to-nav, " +
        "div.printfooter, #siteSub, .config").show();

    $(presentSpanElements).hide();  // We don't want to display these
    $('.editsection').hide();       // We also won't display the footer
    
    // Breaks up slide into list item components to be incrementally displayed
    if (presentation.settings.incrementalListItemReveal &&
        bodyContent.slice(slideStart, slideEnd).is("li")) {
        presentation.slides[currentSlide][6] = slideCreator.divideSlideByListItems(slideStart, slideEnd);
        bodyContent.slice(presentation.slides[currentSlide][6][1], slideEnd).hide();
    }
    
    currentListItem = 0;  // Start with only first list item displayed by default
}

// Advances the presentation by one slide, or does nothing if
// the presentation has reached its end
function nextSlide() {
	
    if (presentation.slides[currentSlide][3] && // Can user move forward?
       (currentSlide + 1 < presentation.slides.length)) {
    
        // Hides previous slide
        bodyContent.slice(presentation.slides[currentSlide][0],
                          presentation.slides[currentSlide][1]).hide();
        
        // Advances to next slide
        currentSlide += 1;
        
        // Shows next slide of the presentation
        displaySlide(currentSlide);
        
        // Highlights the position of the current slide in the table of contents.
        tocSlideLinks.highlight(currentSlide);
    }
}

// Moves the presentation back by one slide, or does nothing
// if the presentation is at the beginning
function previousSlide() {

    if (presentation.slides[currentSlide][2] &&  // Can user go backward?
        currentSlide - 1 >= 0) {
    
        // Hides the elements of the previous slide.
        bodyContent.slice(presentation.slides[currentSlide][0],
                          presentation.slides[currentSlide][1]).hide();
        
        // Moves to previous slide
        currentSlide -= 1;
        
        // Displays previous slide of the presentation
        displaySlide(currentSlide);
        if (presentation.settings.showListItemsBackwards) {
            displayAllListItems();
        }
        
        // Highlights the current slide position in the table of contents.
        tocSlideLinks.highlight(currentSlide);
    }
}

// Displays next slide or next list item
function moveForward() {
    if (presentation.settings.incrementalListItemReveal &&
       (presentation.slides[currentSlide][6].length > 0) &&
       (currentListItem < presentation.slides[currentSlide][6].length - 2)) {
        nextListItem();  // Still another list item on slide
    } else {
        nextSlide();     // Go to next slide
    }
}

// Displays previous slide or list item
function moveBackward() {
    if (presentation.settings.incrementalListItemReveal && 
        presentation.settings.listHideBackwards &&
       (presentation.slides[currentSlide][6].length > 0) && 
       (currentListItem > 0)) {
        previousListItem();  // Still more list items before slide beginning
    } else {
        previousSlide();     // Go to previous slide
    }
}

// Jumps the presentation to a slide based on the table of contents
// link clicked by the user
function gotoSection(event) {

    var newSlideNumber = tocSlideLinks.slideLastClicked(event);

    if (newSlideNumber >= 0 && newSlideNumber < presentation.slides.length) {

       // Hides the elements comprising the previous slide.
       bodyContent.slice(presentation.slides[currentSlide][0],
                         presentation.slides[currentSlide][1]).hide();
             
       // Moves to new slide
       currentSlide = newSlideNumber;
             
       // Displays the current slide.
       displaySlide(currentSlide);
              
       // Highlights the current slide position in the table of contents.
       tocSlideLinks.highlight(currentSlide);
    }
}

function clearFlashing() {
   $("div#bodyContent span.mw-headline").css("background-color", "white");
}

function flashHeader(event){
    var presentationHeader = $("div#bodyContent span.mw-headline"),
        headerLocation = 0,
        arraynames = [];
    
    headerLocation = tocSlideLinks.findAssociatedHeader(event, arraynames);
    currentSlide = tocSlideLinks.findNormalModeSlide(presentation.settings, headerLocation, arraynames);
    
    //var altMethod = tocSlideLinks.slideLastClicked(event);
    //window.alert(altMethod + " " + headerLocation + " " + currentSlide);
    
    presentationHeader.slice(headerLocation, headerLocation + 1).css("background-color", "#FFFF00");
    setTimeout(clearFlashing, 1000);
}

// Prepares F6 window resize operation with user-selected size
function setResizeBinding(){

    var windowDimensions, windowWidth, windowHeight;
    
    if (!presentation.settings.F6Resize) return;

    windowDimensions = presentation.settings.F6Resize.split("x");
    windowWidth = parseInt(windowDimensions[0], 10);
    windowHeight = parseInt(windowDimensions[1], 10);

    if (!(typeof windowWidth === "number" && windowWidth > 50 &&
          typeof windowHeight === "number" && windowHeight > 50)){
            windowWidth = 1024;
            windowHeight = 768;
            presentation.settings.F6Resize = "1024x768";
    }
    
    $(document).bind("keydown", function(event){
        if (event.which === 117){
            window.resizeTo(windowWidth, windowHeight);
            return false;
        }
    });
}

function presentationCapability(){

    var h3SpanTag = "h3 span.mw-headline",
    h3TagsLength,
    h3TocEntriesLength;

    presentation.state.h1SlidesPrevious = presentation.settings.h1TagSlides;
    
    h3TagsLength = $("div#bodyContent h3:has(span.mw-headline)").length;
    h3TocEntriesLength = $("table#toc li.toclevel-3").length;
    
    if ($("div#bodyContent *").is(h3SpanTag) &&
       (h3TagsLength === h3TocEntriesLength)){
        presentation.state.h3SlidesPossible = true;
    }
    else{
        presentation.state.h3SlidesPossible = false;
    }
    
    if (!presentation.state.h3SlidesPossible){
        presentation.settings.h3TagSlides = false;
    }
    
    presentation.state.h3SlidesPrevious = presentation.settings.h3TagSlides;
    presentation.settings.listHideBackwards = presentation.settings.incrementalListItemReveal;
}

// Callback for the presentation options menu when a setting is changed
// Updates presentation state according to settings chosen by user
function updateOptions() {

    // Validate text size
    presentation.settings.textSizeIncrease = parseInt(presentation.settings.textSizeIncrease, 10);
    if ((typeof presentation.settings.textSizeIncrease !== "number") ||
        (presentation.settings.textSizeIncrease < 0)) {
         presentation.settings.textSizeIncrease = 120;
    }
    
    // Re-bind the F6 resize operation with updated dimensions
    setResizeBinding();
    
    // Re-starts presentation mode if needed
    if ((presentation.state.h1SlidesPrevious !== presentation.settings.h1TagSlides) ||
        (presentation.state.h2SlidesPrevious !== presentation.settings.h2TagSlides) ||
        (presentation.state.h3SlidesPrevious !== presentation.settings.h3TagSlides) &&
        (presentation.state.activated === true)) {
         
           // If structure of presentation changed, starts at beginning
           currentSlide = 0;
           
           // Reinitializes presentation mode
           setupPresentationMode();
           setupPresentationMode();
           
           // Saves new previous state values for future comparison
           presentation.state.h1SlidesPrevious = presentation.settings.h1TagSlides;
           presentation.state.h2SlidesPrevious = presentation.settings.h2TagSlides;
           presentation.state.h3SlidesPrevious = presentation.settings.h3TagSlides;
    }
}

function setupToolsLink(){
    var toolsLinkHTML = '<li id = "presentToolsListItem">'+
    '<a id = "toolsPresentLink" title = ' +
    '"Enter presentation mode [F5]. '+
    '(Select [F10] for presentation mode options.)">'+
    'Presentation Mode</a></li>';
    $("div#column-one #p-tb .pBody ul").prepend(toolsLinkHTML);
    $("#toolsPresentLink").css("cursor", "pointer");

    //setupPresentationMode();
    //$('<li>[<a class = "presentationLink">present</a>]</li>').appendTo($('#p-personal > div > ul'))
}

$(function(){

	var editorBind = function(event){
	       if (event.which == 119){
	           if (presentation.slides.length == 0){
	              setupPresentationMode();
	           }
	           slideSort.setup();
	           return false;
	        }
	};
	
    $(document).bind("keydown", documentBindings.outside);
    $(document).bind("keydown", editorBind);
    tocSlideLinks.getHeaderBoundElements().bind("click", tocBindings.flash);
    
    var presentOptionNames = ["incrementalListItemReveal", "showListItemsBackwards",
                              "displayTOC", "h1TagSlides", "h2TagSlides", "h3TagSlides",
                              "showControls", "autoStart", "textSizeIncrease",
                              "F6Resize"];

    var mtagOptions = [{text: "Reveal list items incrementally", xmlTag: "DynamicListReveal", type: "checkbox"},
                      {text: "When going back a slide, show all list items", xmlTag: "ShowListItemsBackward", type: "checkbox"},
    	              {text: "Show table of contents by default", xmlTag: "ShowLeftColumn", type: "checkbox"},
    	              {text: "Show h1 Sections in Presentation Mode", xmlTag: "ShowH1Sections", type: "checkbox"},
    	              {text: "Show h2 Sections in Presentation Mode", xmlTag: "ShowH2Sections", type: "checkbox"},
    	              {text: "Show h3 Sections in Presentation Mode", xmlTag: "ShowH3Sections", type: "checkbox"},
    	              {text: "Show Presentation Controls (Buttons)", xmlTag: "showPresentControls", type: "checkbox"},
    	              {text: "Start presentation on page load", xmlTag: "StartInPresentationMode", type: "checkbox"},
    	              {text: "Text Size (% of normal)", xmlTag: "TextSizePercent", type: "textEntry"},
    	              {text1: "F4", text2: "Display/Hide table of contents", type: "pairedText"},
    	              {text1: "F5", text2: "Enter/Exit Presentation", type: "pairedText"},
    	              {text1: "F6", text2: "Resize browser window to", dropValues: ["800x600", "1024x768", "1280x1024", "1920x1080"], xmlTag: "ResizeDimensions", type: "textWithDropdown"},
    	              {text1: "F8", text2: "Enter/Exit Slide Sorter", type: "pairedText"},
    	              {text1: "F10", text2: "Show/Hide Presentation Mode Options", type: "pairedText"},
    	              {text1: "F11", text2: "Full Screen Mode", type: "pairedText"},
    	              {text1: "F12", text2: "Show + links", type: "pairedText"},
    	              {text1: "Right/Down", text2: "Next Section/Show next list item", type: "pairedText"},
    	              {text1: "Left/Up", text2: "Previous Section/Hide current list item", type: "pairedText"},
    	              {text1: "Esc", text2: "Exit Presentation", type: "pairedText"}];
    
    var clientDescrip = 'This MediaWiki installation has a presentation mode that simulates a ' +
                        'PowerPoint-style presentation.<br>  In presentation mode, only one section ' +
                        'is shown at a time and sections are changed using the arrow keys.';

    var presentOptions = new OptionsMenu(updateOptions,
                                         2,
    		                             presentation.settings,
    		                             presentOptionNames,
    		                             "content",
    		                             "Presentation Mode",
    		                             clientDescrip,
    		                             mtagOptions,
    		                             121);
    
    presentOptions.getXML();  // Obtains configuration XML
    //updateOptions();          // Updates presentation with loaded defaults
    presentation.settings.textSizeIncrease = parseInt(presentation.settings.textSizeIncrease, 10);
    if ((typeof presentation.settings.textSizeIncrease !== "number") ||
        (presentation.settings.textSizeIncrease < 0)) {
         presentation.settings.textSizeIncrease = 120;
    }
    setupToolsLink();         // Creates tools menu link for presentation mode
    setResizeBinding();       // Sets binding for the F6 window resize key

    $("#presentToolsListItem").bind("click", function(event){
        presentOptions.displayMenu();
    });
    
    // Assigns tooltip to the link used to display the left column
    $("#col1hidelink").attr("title", "Show/Hide Left Column [F4]");
    
    if (presentation.settings.autoStart){
           presentationMode("auto");
    }
});
