if( window.jQuery )  // Avoid more errors in IE 5.0
{
  // Directly set flag for CSS to indicate JavaScript is enabled.
  // At this point "HTML" is the only accessable element.
  $(document.documentElement).addClass("jsEnabled");

  jQuery.fn.debug = function() { window.console && console.log( this.selector, this ); return this; };

  $(document).ready( function()

  { 

/* ------------ Videobox 
*/

$("#wb-overlay").click(
    function(e) {
      $("#wb-container").css("display", "none");
      return false;
    }
  );

$(".wb-video").click(
    function(e) {
      // No position fixed, allow users to scroll in case there are large descriptions.
      $("#wb-body").css("top", $("html").scrollTop() );

      $("#wb-content-videocontainer").css("display", "block");
      //$("#wb-content-inner").css("display", "none");
      var videoContainerDiv = $("#wb-content-videocontainer");
      $("#wb-container").css("display", "block");
      swfobject.embedSWF(this.href, "wb-content-videoobject", videoContainerDiv.css("width"), videoContainerDiv.css("height"), "9.0.0");
      return false;
    }
  );



    var isFinderViewer = ( jQuery.ui && jQuery.ui.draggable );
    if( isFinderViewer )
    {
      initCustomScrollbars();
    }

    // General
    initCorners();
    initOverlabels();

    // Finder/viewer
    if( isFinderViewer )
    {
      initFinder();
      initViewer();
    }
    else
    {
      var str = new String(document.location);
      if (str.lastIndexOf("preview.shtml")>=0)
      {
        initPreview();
      }
    }

    // All not that important
    fixExternalLinks();
    initTooltip();
    initSearch();

	
		// --------------------------------
    // Flash

    var demovideo = document.getElementById('demovideo');
    if( demovideo != null)
    { 
      var objectAttr = { align: 'center' };
      var params = { allowscriptaccess: 'samedomain'
                   , allowfullScreen:   'false'
                   , quality:           'high'
                   , scale:             'noscale'
                   , salign:            'lt'            // must be below scale
                   , wmode:             'transparent'  // not supported in Linux
                   , bgcolor:           '#000000'
                   };

      swfobject.embedSWF( "/resources/swf/FLVPlayer_Progressive.swf?streamName=/resources/swf/demovideo.flv&skinName=/resources/swf/Clear_Skin_3&MM_ComponentVersion=1&autoPlay=true&autoRewind=false",
                          'demovideo', '640', '400', '6.0.0' , '/resources/swf/expressInstall.swf', objectAttr, params);

    } 

  });

}



// Namespace for global vars
var umap =
{
  // Settings
  placeholderImage: "/resources/img/roset_big_placeholder.png"
, blankImage:       "/resources/img/blank.png"
, diagramPath:      "/resources/img/diagram/"

  // Current state
, filters: {}
, lastQuery: ""
, selectedUniversities: []

  // Ajax objects
, server: null
, serverCallbacks: {}   // each function in this object can be called by the server.
, searchRequest: null
};


// -------------------------------
// Small stuff

function initCorners()
{
  if( jQuery.browser.msie && parseInt( jQuery.browser.version ) > 6 )  // IE 6 crashed.
  {
    $('.rounded').corners();
  }
}


function fixExternalLinks()
{
  // give links with rel="external" target="_blank" attribuut.
  $(document.links).filter('[href][rel=external]').attr("target", "_blank");
}


function initTooltip()
{
  $("a.tooltip").umapTooltip();
}


function initCustomScrollbars()
{
  updateScrollbars( true );               // Make sure the content div has a proper size.
  $(".ui-scrollarea").customScrollbar();  // And initialize the scrollbars.
}


// --------------------------------
// Form input label flip

function initOverlabels()
{
  // Activate 'overlabels' for compact forms
  var overlabels = $('label.overlabel')
   .each( function()
   {
     // Get field
     var label = $(this);
     var id    = this.htmlFor || this.getAttribute('for');
     var field = null;
     if( ! id || ! (field = document.getElementById( id ) ) ) return;
  
     // Implement show/hide effect
     $(field).addClass("overlabel-js")                 // for setTimeout() below
             .focus( function() { label.hide(); } )
             .blur ( function() { this.value === '' && label.show(); } );
  
     // Also for mouse click
     label.mousedown( function() { setTimeout( function() { field.focus(); }, 10 ); } );   // IE requires setTimeout()
   }
  );

  // Hide overlabels after the form is auto-filled in by the browser.
  setTimeout( function()
  {
   var fields = $("input.overlabel-js");
   var labels = $("label.overlabel");
   fields.each( function() { this.value !== '' && labels.filter("[for=" + this.id + "]").hide(); } );
  }, 50 );
}



// -------------------------------
// Tooltip plugin

jQuery.fn.umapTooltip = function()
{
  this.hover(
    function(event)
    {
      this.t = this.alt || this.title;
      this.title = "";
      var tooltip = $("body").append("<div id='tooltip' style='display:none'><p>"+ this.t +"</p></div>").children("#tooltip");
      var pos = _getTooltipPos( event, tooltip );
      tooltip.css( pos ).fadeIn("fast");
    },
    function()
    {
      this.alt = this.t;
      this.title = this.t;
      $("#tooltip").remove();
    }
  )

  function _getTooltipPos( event, tooltip )
  {
    var e = $( event.target );
    var epos = e.offset();
    var ttWidth  = tooltip.outerWidth();
    var ttHeight = tooltip.outerHeight( true );
    return { left: epos.left + ( e.outerWidth() - ttWidth ) / 2
           , top: epos.top  - ttHeight
           };
  }
};



// -------------------------------
// Ajax code

if( window.sis && sis.ajax )
{
  // Make server
  umap.server = new sis.ajax( umap.serverCallbacks, "%5Efinder.shtml/ajaxhandler.shtml" );
  umap.server.onReplaceHtml( "universitiesrow", onReplaceUniversitiesRow );
}

// Called when the universityrow is replaced.
function onReplaceUniversitiesRow()
{
  updateScrollbars();
  $("#universitiesrow a.tooltip").umapTooltip();
}



// -------------------------------
// Finder


function initFinder()
{
  if( $("#page_finder").length == 0 )
    return;

  // Read current state form the HTML.
  var filters = staticInit_filters();

  // Redirect links to Ajax versions.
  filters.click( onFilterClick );
  $("#clear_selection").click( onClearSelectionClick );
}


// Initialize the JavaScript filter code based on what's already set in the HTML.
function staticInit_filters()
{
  var re = /filter(\d+)-(\d+)/;
  var filters = $("#criteria a.filter");
  for( var i = 0; i < filters.length; i++ )
  {
    var filter = filters.eq(i);
    if( filter.hasClass("selected")
     && ( cap = re.exec( filter.attr("id") ) ) )
    {
      umap.filters[ "f" + cap[1] ] = cap[2];
    }
  }

  return filters;
}

function removeValueFromFilter( _filter, _value)
{
  if (_filter == "") {
    return "";
  }
  var tokens = _filter.split(";");
  for (i=0; i<tokens.length; i++) {
    if (tokens[i]==_value) {
      delete tokens[i];
    }
  }
  return tokens.join(";");
}

function addValueToFilter(_filter, _value)
{
  var tokens;
  if ((! _filter) || (_filter == "")) {
    tokens = new Array();
  }
  else {
    tokens = _filter.split(";");
  }
  elementfound = false;
  for (i=0; i<tokens.length; i++) {
    if (tokens[i]==_value) {
      elementfound = true;
    }
  }
  if (elementfound == false) {
    tokens.push(_value);
  }
  return tokens.join(";");
}

// Handle clicks on the filter links.
function onFilterClick( event )
{
  var link = $(event.target);
  var cap = /filter(\d+)-(\d+)/.exec( link.attr("id") );
  if( cap )
  {
    if( link.hasClass("selected") )
    {
      // Unselect
      link.removeClass("selected");
      //delete umap.filters[ "f" + cap[1] ];
      newfilter = removeValueFromFilter(umap.filters[ "f" + cap[1] ], cap[2]);
      if (newfilter == "") {
        delete umap.filters[ "f" + cap[1] ];
      } else {
        umap.filters[ "f" + cap[1] ] = newfilter;
      }

    }
    else
    {
      // Remove highlight of previous one
      //var old = umap.filters[ "f" + cap[1] ];
      //if( old != null )
     // {
     //   $("a#filter" + cap[1] + "-" + old).removeClass("selected");
     // }

      // Set new selection
      link.addClass("selected");
      //umap.filters[ "f" + cap[1] ] = cap[2];
      umap.filters[ "f" + cap[1] ] = addValueToFilter(umap.filters[ "f" + cap[1] ], cap[2]);
    }

    // also add the existing search query.
    var search = $("#universitySearch")[0];
    if( search && search.value )
    {
      umap.filters[ search.name ] = search.value;
    }

    // Call the server.
    umap.server.callMethod( "getFilterResults", umap.filters );

    event.preventDefault();
  }
}


// Action to clear the selection.
function onClearSelectionClick( event )
{
  // Update the DOM
  $("#universitySearch").val("").blur();   // blur triggers overlabel code.
  $("#criteria a.filter").removeClass("selected");

  // Update administration
  umap.filters   = {};
  umap.lastQuery = "";

  // Make sure the server is also up to date.
  umap.server.callMethod( "getFilterResults", umap.filters );

  event.preventDefault();
}


// Server-side request to update the filters (in response to getFilterResults()).
if( umap.server )
{
  umap.serverCallbacks.updateFilterResults = function( universityIds )
  {
    // Find all universities.
    var unis = $("#universitiesrow .university");

    // Update stats
    $("#numberfound").text( universityIds.length );

    // directly update to allocate space
    var allocatedItems = Math.max( universityIds.length, unis.filter(":visible").length );
    updateScrollbars( false, allocatedItems );

    // Start animating universities
    for( var i = 0; i < unis.length; i++ )
    {
      var uni       = unis.eq(i);
      var uniId     = parseInt( uni.attr("id").substring(11) );  // university-###
      var isVisible = ( jQuery.inArray( uniId, universityIds ) != -1 );

      if( isVisible != uni.is(":visible") )
      {
        if( isVisible ) uni.slideLeftShow(300);
        else            uni.slideLeftHide(300);
      }
    }

    // After all animations, update scrollbars to deal with hidden items.
    setTimeout( updateScrollbars, 350 );
  }
}



// -------------------------------
// Search part

function initSearch()
{
  if( umap.server )
  {
    // activate incremental search
    var searchField = $("#universitySearch");
    searchField.umapTyping( onSearchTyping );

    umap.lastQuery = searchField[0].value;

    // avoid submitting with "enter" or the "go" button.
    searchField[0].form.onsubmit = function() { return false; };
  }
}


// Handle typing in the search text field,
function onSearchTyping()
{
  // Get query.
  // Avoid updates if the result is the same (e.g. press a key + backspace).
  var query = this.value;
  if( query == umap.lastQuery )
  {
    return;
  }

  umap.lastQuery = query;

  // Abort running request
  if( umap.searchRequest != null )
  {
    umap.searchRequest.abort();
    umap.searchRequest = null;
  }

  // Make new call
  var postdata = sis.ajax.getFormData( this.form );
  umap.searchRequest = umap.server.callMethod( "getFilterResults", postdata );
}


// Internal plugin to detect keypresses with a small delay
jQuery.fn.umapTyping = function( callback, timeout )
{
  if( this.length == 0 ) return this;

  // Event handler which makes the call with a delay.
  var delayedCall = function(event)
  {
    var domElement = this;
    if( ! timer )
    {
      timer = setTimeout( function()
      {
        timer = 0;
        callback.apply( domElement );
      }, timeout );
    }
  }

  timeout = timeout || 300;
  var timer;
  return this.keyup( delayedCall ).change( delayedCall );
}



// -------------------------------
// Viewer dragging


function initPreview()
{
  // Get possible universities
  var universities = $("#universitiesrow .university");
  var dropzones = $(".university-dropzone");

  // Init the dropzones which were already filled in the HTML.
  dropzones.filter(".university-dropzone-active").each( staticInit_dropzone );
}

function initViewer()
{
  // Get possible universities
  var universities = $("#universitiesrow .university");
  var dropzones = $("#compare .university-dropzone");
  if( universities.length == 0 || dropzones.length == 0 )
    return;

  // Make source row draggable
  universities.each( function() { var src = $(this);
                                   src.data("universityId", parseInt( this.id.substring( 11 ) ) )  // university-###
                                      .data("origwidth", src.width() )
                                      .data("title", src.find("a").attr("title") )  // store, because the title attr is edited by the tooltip -> breaking clicks
                                 } )
              .draggable( { scope: 'universities'
                          , revert: 'invalid'
                          , opacity: 0.8
                          , containment: 'document'  // stay within the page.

                          // to escape the overflow: hidden;
                          , helper: 'clone'
                          , appendTo: 'body'
                          , start: onSrcItemDragStart
                          , stop:  onSrcItemDragStop
                          } )
              .click( onSrcItemClick );

  // Configure drop zones
  dropzones.each( function(i) { $(this).data("dropId", i); } )
           .droppable( { scope: 'universities'
                       , hoverClass: 'drag-hover'
                       , drop: onSrcItemDrop
                       } )
           .hover( onDropzoneEnter, onDropzoneLeave );

  // Make the source row a drop area for universities to unselect.
  $("#image_scroll > .ui-scrollviewport").droppable( { scope: 'universities-dragout'
                                                     , hoverClass: 'drag-hover'
                                                     , drop: onActiveItemItemDrop
                                                     } );

  // Init the dropzones which were already filled in the HTML.
  dropzones.filter(".university-dropzone-active").each( staticInit_dropzone );
}


// Initialize the JavaScript code based on what's already set in the HTML.
function staticInit_dropzone()
{
  var dropzone = $(this);
  var uniId = parseInt( this.id.substring(19) );  // selectedUniversity-###
  var uni = $("#university-" + uniId );

  // Make sure the university is properly hidden for the restore animation.
  uni.slideLeftHide(0);

  // Make draggable so users can intuitively unselect it.
  var str = new String(document.location);
  if (str.lastIndexOf("preview.shtml")<0)
  {
    _makeDestDraggable( dropzone );
  }
  
  // Extract all scores.
  var re = /image(\d+) score(\d+)/;
  var areas = dropzone.find("area[coords]");
  var scores = [];
  for( var i = 0; i < areas.length; i++ )
  {
    var area = areas.eq(i);
    if( cap = re.exec( area.attr("class") ) )
      scores[ cap[1] - 1] = { value: parseInt( cap[2] ) };

    area.data("title", area[0].title);
    area[0].title = "";
    area[0].alt = "";
  }

  // Update administration
  var dropId = dropzone.data("dropId");
  umap.selectedUniversities[ dropId ] = uni;
  dropzone.data("universityId", uniId);
  dropzone.data("scores", scores);

  // Make sure the image map hover works for these images.
  areas.hover( onAreaEnter, onAreaLeave );
}


// New jQuery functions
jQuery.fn.slideLeftHide = function( speed, callback ) { return this.css("visibility", "hidden").show().animate( { width: "hide", paddingLeft: "hide", paddingRight: "hide", marginLeft: "hide", marginRight: "hide" }, speed, function() { this.style.display = 'none'; callback && callback.call( this, arguments ); } ); }
jQuery.fn.slideLeftShow = function( speed, callback ) { return this.css("visibility", "hidden").animate( { width: "show", paddingLeft: "show", paddingRight: "show", marginLeft: "show", marginRight: "show" }, speed, function() { this.style.visibility = ""; callback && callback.call( this, arguments ); } ); }


// Called when the user starts dragging a university.
function onSrcItemDragStart( event, ui )
{
  // Hide the original.
  // The user is actually dragging a clone.
  event.target.style.visibility = 'hidden';
}


// Called when 
function onSrcItemDragStop( event, ui )
{
  var uni = $( event.target );
  if( ! uni.hasClass("university-selected") && ! uni.data("selected") )
  {
    // Not dropped, restore
    uni.css( "visibility", "" );
  }
}


// Do what must be done when a university is dropped.
function onSrcItemDrop( event, ui )
{
  copyUniversityToDropzone( ui.draggable, $(this) );
}


// The non-JS version uses clicks to update universities.
// Implement this for the JS version too because users can assume it.
function onSrcItemClick( event )
{
  // Get the drop zones
  var dropzones = $("#compare .university-dropzone");
  var freezones = dropzones.not(".university-dropzone-active");

  // Find out the src + dest.
  var src  = $(this);
  var dest = ( freezones.length == 0 ? dropzones.eq( dropzones.length - 1 ) : freezones.eq(0) );

  // And apply the effect in nice Ajax fashion.
  copyUniversityToDropzone( src, dest );

  event.preventDefault();
}



// ------------------------------------------------------
// Viewer, transfer of university to destination.


// Copy the university appearance and settings to the dropzone.
function copyUniversityToDropzone( src, dest )
{
  // Get new source
  var dropId = dest.data('dropId');
  var oldSrc = umap.selectedUniversities[ dropId ];

  // Hide source
  // Avoids flashes when displaing original source later.
  hideSrcItem( src );

  // Show old source
  if( oldSrc )
  {
    showSrcItem( oldSrc );
  }
  else
  {
    // New area.

    // Fadeout placeholder
    dest.addClass('university-dropzone-active');
    dest.find(".hint").hide();

    // Make draggable so users can intuitively unselect it.
    _makeDestDraggable( dest );
  }

  // Update administration
  umap.selectedUniversities[ dropId ] = src;
  dest.data("universityId", src.data("universityId") );

  // Configure dest
  dest.parent().find(".university-name").text( src.data("title") )
  _setDropzoneImage( dest );

  // Make a call to the server.
  umap.server.callMethod("getDropzoneInfo", { dropId: dropId, universityId: dest.data("universityId") } );
}


// Server callback to update the drop zone.
if( umap.server )
{
  umap.serverCallbacks.updateDropZone = function( dropId, uniInfo )
  {
    // Add scores
    var dropzone = $(".university-dropzone").eq( dropId );
    var img      = dropzone.find("img");
    dropzone.data("scores", uniInfo.scores);
    dropzone.data("mapid",  uniInfo.mapid);

    // Assign image map.
    $("#dropzone-map-" + dropId)
      .html( uniInfo.maphtml )
      .find("area[coords]").hover( onAreaEnter, onAreaLeave )
                           .each( function() { $(this).data("title", this.title); this.alt = ""; this.title = ""; } );

    img.attr( { usemap: "#" + uniInfo.mapid
              , useMap: "#" + uniInfo.mapid  // IE 6 and 7 bug: requires camel case
              } );
  }
}


function hideSrcItem( src )
{
  src.data("selected", 1);
  if( src.data("universityId") >= _getLastVisibleSrcItemId() )
  {
    src.hide();
    onSrcItemHide.apply( src[0] );
  }
  else
  {
    src.slideLeftHide(300, onSrcItemHide );  // slowly hide, for test below
  }
}


function showSrcItem( src )
{
  src.data("selected", 0).removeClass("university-selected");   // remove earlier.
  updateScrollbars();                                           // directly fix, in case the last one emerges
  if( src.data("universityId") >= _getLastVisibleSrcItemId() )
  {
    // Show last item directly
    src.show();
    onSrcItemShow.apply( src[0] );
  }
  else
  {
    src.slideLeftShow(300, onSrcItemShow );
  }
}


function _getLastVisibleSrcItemId( ignoreId )
{
  var items = $("#universitiesrow").children();
  for( var i = items.length - 1; i >= 0; i-- )  // avoid overhead of ":visible" on all elements.
  {
    var item = items.eq(i);
    if( item.is(":visible") )
    {
      return item.data("universityId");
    }
  }

  return 0;
}


// A university is hidden by animation
function onSrcItemHide()
{
  updateScrollbars();
}


// A university is shown by animation
function onSrcItemShow()
{
  $(this).removeClass("university-selected");
  this.style.visibility = "";  // in case slideLeftShow() is circumvented.
  updateScrollbars();
}


// Update the scrollbars.
// Resize the div based on the visible number of items.
function updateScrollbars( noRefresh, knownVisibleItems )
{
  var visibleUnis = $("#universitiesrow > .university");

  // Find out how many are - or will be - displayed.
  // TODO: does not always return the proper number of items.
  var detected;
  if( knownVisibleItems == null )
  {
    detected = true;
    var knownVisibleItems = 1;
    for( var i = 0; i < visibleUnis.length; i++ )
    {
      var t = visibleUnis.eq(i);
      var isa = t.is(":animated") ?1:0;
      var isv = t.is(":visible")  ?1:0;
      if( isa || isv ) knownVisibleItems++;
    }
  }

  if( window.console )
    console.log( "updateScrollbars, items=", knownVisibleItems, " detected=", detected );

  // Adjust the row to the number of items
  $("#universitiesrow").css( "width", knownVisibleItems * visibleUnis.eq(0).outerWidth(true) + 2 );

  if( ! noRefresh )
  {
    $(".ui-scrollarea").customScrollbar("refresh");
  }
}


// ------------------------------------------------------
// Viewer drag-out

function _makeDestDraggable( dest )
{
  dest.draggable( { scope: 'universities-dragout'
                  , revert: 'invalid'
                  , opacity: 0.8
                  , addClasses: false   // no ui-draggable.
                  , containment: 'document'  // stay within the page.

                  // to escape the overflow: hidden;
                  , helper: _createActiveItemDragClone
                  , appendTo: 'body'
                  , start: onActiveItemDragStart
                  , stop:  onActiveItemDragStop
                  } )
}

function _createActiveItemDragClone()
{
  var dropzone = $(this);
  var img      = dropzone.find("img");

  // Make a clone none.  no clone() but manual setup to avoid conflicting stuff (usemap, highlighed images, etc..)
  var div = document.createElement("div");
  div.style.backgroundImage = 'url("' + _getDropzoneImg( dropzone ) + '")';
  div.style.width      = img.width()  + "px";
  div.style.height     = img.height() + "px";
  div.style.marginLeft = dropzone.css("marginLeft");  // copy margins to the cursor is at the right position.
  div.style.marginTop  = dropzone.css("marginTop");

  return div;
}

function onActiveItemDragStart( event, ui )
{
  // Mark the dropzone is being dragged,
  // so other code can check it.
  var dropzone = $(this);
  dropzone.addClass("is-dragged");

  // Swap images to give the impression of an empty placeholder.
  var img = dropzone.find("img");
  img.data("origsrc", img.attr("src"));
  img.attr( { src:    umap.placeholderImage
            , usemap: ""      // disable area effects, in case the mouse moves over the dropzone while the restore animation runs.
            });
  dropzone.find(".hint").fadeIn(300);
}

function onActiveItemDragStop( event, ui )
{
  var dropzone = $( event.target );
  if( dropzone.hasClass("is-dragged") )
  {
    // Remove the tag
    dropzone.removeClass("is-dragged");

    // Restore the DOM.
    // If dragged properly, the image will quickly be replaced again with a highlighted part.
    dropzone.find(".hint").hide();
    dropzone.find("img").attr("usemap", dropzone.data("mapid"));
    _setDropzoneImage( dropzone );
  }
  else
  {
    // Item was dragged and reset.
    // Disable dragging of the placeholder.
  }
}

function onActiveItemItemDrop( event, ui )
{
  // Remove the tag.
  var dropzone = $(ui.draggable);
  dropzone.removeClass("is-dragged");

  // And reset the appearance.
  resetDropzone( dropzone );
}


// The opposite of copyUniversityToDropzone()
function resetDropzone( dropzone )
{
  // Get new source
  var dropId = dropzone.data('dropId');
  var uni    = umap.selectedUniversities[ dropId ];

  // Restore the item in the row.
  showSrcItem( uni );

  // Update administration
  dropzone.removeData("universityId");
  dropzone.removeData("scores");
  dropzone.removeData("mapid");
  delete umap.selectedUniversities[ dropId ];

  // Reset DOM elements
  //dropzone.draggable("destroy");
  dropzone.removeClass('university-dropzone-active');
  dropzone.parent().find(".university-name").text( String.fromCharCode(160) );  // place &nbsp; back.
  dropzone.find("img").attr( { src: umap.placeholderImage
                             , usemap: ""
                             } )
                      .css( { backgroundImage: "none" } );
  $("#dropzone-map-" + dropId).empty();

  // Destroy the draggable.
  // Must be either done in onActiveItemDragStop(), or after all other JS execution in the draggable.
  setTimeout( function() { dropzone.draggable("destroy") },0 );
}


// ------------------------------------------------------
// Viewer mouse hover events

var leaveTimer1;
var leaveTimer2;


// The mouse enters the dropzone
function onDropzoneEnter()
{

}


// The mouse leaves the dropzone
function onDropzoneLeave()
{
  clearTimeout( leaveTimer1 );
  clearTimeout( leaveTimer2 );

  // Reset highlight of all images.
  var dropzones = $(".university-dropzone-active");
  for( var i = 0; i < dropzones.length; i++ )
  {
    dropzone = dropzones.eq(i);

    // Direct reset
    // unless the image is dragged.
    if( ! dropzone.hasClass("is-dragged") )
    {
      _setDropzoneImage( dropzone );
    }
  }

  $(".dropzone-tooltip").hide();
}


function _setDropzoneImage( dropzone )
{
  dropzone.find("img").attr("src", _getDropzoneImg( dropzone ) )
                      .css( { backgroundImage: "none" } );
}


// The mouse enters an image part.
function onAreaEnter()
{
  if( cap = /image(\d+) score(\d+)/.exec( this.className ) )
  {
    var imageId = parseInt( cap[1] );
    clearTimeout( leaveTimer1 );
    clearTimeout( leaveTimer2 );

    // Transfer title of active item.
    // Avoid showing the non-Ajax version.
    if( this.title != "" )
    {
      $(this).data("title", this.title);
      this.title = "";
      this.alt = "";
    }

    highlightAreaPart( parseInt( cap[1] ), parseInt( cap[2] ) );
  }
}


// Highlight an image part.
function highlightAreaPart( imageId, score )
{
  // Find all IDs of tooltips.
  var allTooltipIds = [];
  var allTooltips = $(".dropzone-tooltip");
  for( var i = 0; i < allTooltips.length; i++ )
  {
    allTooltipIds.push( allTooltips.get(i).id );
  }

  // Show tooltips for all areas
  var areas = $("area.image" + imageId);
  for( var i = 0; i < areas.length; i++ )
  {
    var area    = areas.eq(i)
    var title   = area.attr("title") || area.data("title") || "";
    var tooltip = area.closest(".roset_big").children(".dropzone-tooltip");

    title = title.replace( /&/g, '&amp;' )
                 .replace( /</g, "&lt;" )
                 .replace( />/g, "&gt;" )
                 .replace( /(\r\n|\r|\n)/g, "<br>" );
    tooltip.html( title ).show();
    
    // Remove from list
    allTooltipIds.splice( jQuery.inArray( tooltip.attr("id"), allTooltipIds ), 1 );
  }

  // Are there missing tooltips?
  if( allTooltipIds.length )
  {
    $("#" + allTooltipIds.join(", #") ).hide().text("");
  }

  // Highlight images for every dropzone
  var dropzones = $(".university-dropzone");
  for( var i = 0; i < dropzones.length; i++ )
  {
    var dropzone = dropzones.eq(i);
    if( ! dropzone.data("universityId") ) continue;

    var img = dropzone.find("img");

    // Background becomes grayed out version
    if( img.css("backgroundImage").length < 10 )
    {
      img.css( { backgroundImage: 'url("' + _getDropzoneImg( dropzone, "largegrayedout" ) + '")' } );
    }

    // Foreground becomes highlighted item.
    var scores = dropzone.data("scores")[ imageId - 1 ] || {};  // scores may be missing, e.g. ajax call didn't return yet
    img.attr("src", _getImagePart( imageId, scores.value || 0 ) );
  }
}


// The mouse leaves an image part.
function onAreaLeave()
{
  clearTimeout( leaveTimer1 );
  clearTimeout( leaveTimer2 );

  // Add a delay. If no new item will be entered, unhighlight.
  leaveTimer1 = setTimeout( function() {
    $(".university-dropzone-active").find("img").attr("src", umap.blankImage);
    $(".dropzone-tooltip").hide();
  }, 50 );
}



// --------------------
// Final utils


// Find out what the proper image URL is for a dropzone.
function _getDropzoneImg( dropzone, style )
{
  var uniId = dropzone.data("universityId");
  return "/rosetimage.shtml?id=" + uniId + "&style=" + ( style || "large" );
}


// Find out what the proper URL is for a dropzone part.
function _getImagePart( imageId, score )
{
  if( ! imageId || ! score )
  {
    return umap.blankImage;
  }

  var percentage = parseInt( score * 25 );
  if( imageId < 10 ) imageId = "0" + imageId;
  return umap.diagramPath + "170_" + percentage + "_" + imageId + ".png";
}



// --------------------
// Custom scrollbar plugin


// Plugin
jQuery.fn.customScrollbar = function( call )
{
  if( this.length == 0 ) return;

  // Make sure the functions are scoped per item.
  this.each( function()
  {
    var $this = $(this);
    var viewport      = $this.children("div.ui-scrollviewport");
    var scrollbararea = $this.children("div.ui-scrollbararea");
    var scrollbar     = scrollbararea.find("div.ui-scrollbar");
    var scrollFactor  = 1;  // shared var


    // Check if this is a scrollbar, or a request to initialize it.
    var isScrollbar = scrollbar.hasClass("ui-draggable");
    if( call != null && ! isScrollbar ) return;

    // Inner function, shares outside scope
    function onResize()
    {
      var lastItem  = viewport.children(":last-child");   // #universitiesrow
      var fullWidth = lastItem.position().left + lastItem.outerWidth( true );
      var viewWidth = viewport.width();

      if( viewWidth >= fullWidth )
      {
        //scrollbar.parent(".ui-scrollbararea").css("visibility", "hidden");
        scrollbar.css( { left: 0, width: "100%", visibility: "hidden" } );
        viewport.css("left", 0 );
      }
      else
      {
        // Calculate parameters
        var newWidth = viewWidth * ( viewWidth / fullWidth )                // factor of the scrollbar
                                 * ( scrollbararea.width() / viewWidth );   // for scrollbars smaller then the viewport
        scrollFactor = ( fullWidth / viewWidth );                           // speed at which the viewport scrolls.

        // Apply to scrollbar
        //scrollbar.parent(".ui-scrollbararea").css("visibility", "visible");
        scrollbar.css( { width: newWidth, visibility: "visible" } );

        // Adjust the left position if needed.
        var scrollLeft = scrollbar.position().left;
        if( scrollLeft + newWidth > viewWidth )
        {
          scrollLeft = viewWidth - newWidth;
          scrollbar.css( "left", scrollLeft );
        }
        viewport.css("left", -scrollLeft * scrollFactor );
      }
    }

    function onDrag( event, ui )
    {
      viewport.css("left", -ui.position.left * scrollFactor );
    }

    function onClick( event )
    {
      // Ignore clicks on child elements.
      //if( event.target != this ) return;
      //alert("here");

      // Clicks next to the scrollbar move the whole 1 screen left/right.
      var offset = scrollbararea.offset();
      var clickpos = { left: event.pageX - parseInt( offset.left )
                     , top:  event.pageY - parseInt( offset.top )
                     };

      // next to the scrollbar?
      var dir = ( clickpos.left < scrollbar.position().left ) ? -1 : 1;
      scrollBy( scrollbar.width() * dir );
      event.stopPropagation();
    }

    function onMouseWheel( event, delta )
    {
      scrollBy( - delta * 50 );
      event.preventDefault();
    }

    function scrollBy( offset )
    {
      // Update pos, check in bounds.
      var newLeft = scrollbar.position().left + offset;
      newLeft = Math.min( scrollbararea.width() - scrollbar.width(), Math.max( 0, newLeft ) );

      // Move it!
      scrollbar.css("left", newLeft);
      viewport.css("left", -newLeft * scrollFactor );
    }

    // Fix sizes
    onResize();

    // Enable it.
    if( call == null )
    {
      $(window).resize( onResize );

      // Make bar draggable
      scrollbar.draggable( { containment: 'parent'
                           , drag: onDrag
                           } );
      scrollbararea.click( onClick );

      if( jQuery.fn.mousewheel )
      {
        $this.mousewheel( onMouseWheel );
      }
    }

  } );

}



// -------------------------------
// External libraries


/*
 * jQuery Corners 0.3
 * Copyright (c) 2008 David Turnbull, Steven Wittens
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 */
jQuery.fn.corners=function(C){var N="";var V=B(C);var F=false;try{F=(document.body.style.WebkitBorderRadius!==undefined);var Y=navigator.userAgent.indexOf("Chrome");if(Y>=0){F=false}}catch(E){}var W=false;try{W=(document.body.style.MozBorderRadius!==undefined);var Y=navigator.userAgent.indexOf("Firefox");if(Y>=0&&parseInt(navigator.userAgent.substring(Y+8))<3){W=false}}catch(E){}return this.each(function(b,h){$e=jQuery(h);if($e.hasClass(N)){return }$e.addClass(N);var a=/{(.*)}/.exec(h.className);var c=a?B(a[1],V):V;var j=h.nodeName.toLowerCase();if(j=="input"){h=O(h)}if(F&&c.webkit){K(h,c)}else{if(W&&c.mozilla&&(c.sizex==c.sizey)){M(h,c)}else{var d=D(h.parentNode);var f=D(h);switch(j){case"a":case"input":Z(h,c,d,f);break;default:R(h,c,d,f);break}}}});function K(d,c){var a=""+c.sizex+"px "+c.sizey+"px";var b=jQuery(d);if(c.tl){b.css("WebkitBorderTopLeftRadius",a)}if(c.tr){b.css("WebkitBorderTopRightRadius",a)}if(c.bl){b.css("WebkitBorderBottomLeftRadius",a)}if(c.br){b.css("WebkitBorderBottomRightRadius",a)}}function M(d,c){var a=""+c.sizex+"px";var b=jQuery(d);if(c.tl){b.css("-moz-border-radius-topleft",a)}if(c.tr){b.css("-moz-border-radius-topright",a)}if(c.bl){b.css("-moz-border-radius-bottomleft",a)}if(c.br){b.css("-moz-border-radius-bottomright",a)}}function Z(k,n,l,a){var m=S("table");var i=S("tbody");m.appendChild(i);var j=S("tr");var d=S("td","top");j.appendChild(d);var h=S("tr");var c=T(k,n,S("td"));h.appendChild(c);var f=S("tr");var b=S("td","bottom");f.appendChild(b);if(n.tl||n.tr){i.appendChild(j);X(d,n,l,a,true)}i.appendChild(h);if(n.bl||n.br){i.appendChild(f);X(b,n,l,a,false)}k.appendChild(m);if(jQuery.browser.msie){m.onclick=Q}k.style.overflow="hidden"}function Q(){if(!this.parentNode.onclick){this.parentNode.click()}}function O(c){var b=document.createElement("a");b.id=c.id;b.className=c.className;if(c.onclick){b.href="javascript:";b.onclick=c.onclick}else{jQuery(c).parent("form").each(function(){b.href=this.action});b.onclick=I}var a=document.createTextNode(c.value);b.appendChild(a);c.parentNode.replaceChild(b,c);return b}function I(){jQuery(this).parent("form").each(function(){this.submit()});return false}function R(d,a,b,c){var f=T(d,a,document.createElement("div"));d.appendChild(f);if(a.tl||a.tr){X(d,a,b,c,true)}if(a.bl||a.br){X(d,a,b,c,false)}}function T(j,i,k){var b=jQuery(j);var l;while(l=j.firstChild){k.appendChild(l)}if(j.style.height){var f=parseInt(b.css("height"));k.style.height=f+"px";f+=parseInt(b.css("padding-top"))+parseInt(b.css("padding-bottom"));j.style.height=f+"px"}if(j.style.width){var a=parseInt(b.css("width"));k.style.width=a+"px";a+=parseInt(b.css("padding-left"))+parseInt(b.css("padding-right"));j.style.width=a+"px"}k.style.paddingLeft=b.css("padding-left");k.style.paddingRight=b.css("padding-right");if(i.tl||i.tr){k.style.paddingTop=U(j,i,b.css("padding-top"),true)}else{k.style.paddingTop=b.css("padding-top")}if(i.bl||i.br){k.style.paddingBottom=U(j,i,b.css("padding-bottom"),false)}else{k.style.paddingBottom=b.css("padding-bottom")}j.style.padding=0;return k}function U(f,a,d,c){if(d.indexOf("px")<0){try{console.error("%s padding not in pixels",(c?"top":"bottom"),f)}catch(b){}d=a.sizey+"px"}d=parseInt(d);if(d-a.sizey<0){try{console.error("%s padding is %ipx for %ipx corner:",(c?"top":"bottom"),d,a.sizey,f)}catch(b){}d=a.sizey}return d-a.sizey+"px"}function S(b,a){var c=document.createElement(b);c.style.border="none";c.style.borderCollapse="collapse";c.style.borderSpacing=0;c.style.padding=0;c.style.margin=0;if(a){c.style.verticalAlign=a}return c}function D(b){try{var d=jQuery.css(b,"background-color");if(d.match(/^(transparent|rgba\(0,\s*0,\s*0,\s*0\))$/i)&&b.parentNode){return D(b.parentNode)}if(d==null){return"#ffffff"}if(d.indexOf("rgb")>-1){d=A(d)}if(d.length==4){d=L(d)}return d}catch(a){return"#ffffff"}}function L(a){return"#"+a.substring(1,2)+a.substring(1,2)+a.substring(2,3)+a.substring(2,3)+a.substring(3,4)+a.substring(3,4)}function A(h){var a=255;var d="";var b;var e=/([0-9]+)[, ]+([0-9]+)[, ]+([0-9]+)/;var f=e.exec(h);for(b=1;b<4;b++){d+=("0"+parseInt(f[b]).toString(16)).slice(-2)}return"#"+d}function B(b,d){var b=b||"";var c={sizex:5,sizey:5,tl:false,tr:false,bl:false,br:false,webkit:true,mozilla:true,transparent:false};if(d){c.sizex=d.sizex;c.sizey=d.sizey;c.webkit=d.webkit;c.transparent=d.transparent;c.mozilla=d.mozilla}var a=false;var e=false;jQuery.each(b.split(" "),function(f,j){j=j.toLowerCase();var h=parseInt(j);if(h>0&&j==h+"px"){c.sizey=h;if(!a){c.sizex=h}a=true}else{switch(j){case"no-native":c.webkit=c.mozilla=false;break;case"webkit":c.webkit=true;break;case"no-webkit":c.webkit=false;break;case"mozilla":c.mozilla=true;break;case"no-mozilla":c.mozilla=false;break;case"anti-alias":c.transparent=false;break;case"transparent":c.transparent=true;break;case"top":e=c.tl=c.tr=true;break;case"right":e=c.tr=c.br=true;break;case"bottom":e=c.bl=c.br=true;break;case"left":e=c.tl=c.bl=true;break;case"top-left":e=c.tl=true;break;case"top-right":e=c.tr=true;break;case"bottom-left":e=c.bl=true;break;case"bottom-right":e=c.br=true;break}}});if(!e){if(!d){c.tl=c.tr=c.bl=c.br=true}else{c.tl=d.tl;c.tr=d.tr;c.bl=d.bl;c.br=d.br}}return c}function P(f,d,h){var e=Array(parseInt("0x"+f.substring(1,3)),parseInt("0x"+f.substring(3,5)),parseInt("0x"+f.substring(5,7)));var c=Array(parseInt("0x"+d.substring(1,3)),parseInt("0x"+d.substring(3,5)),parseInt("0x"+d.substring(5,7)));r="0"+Math.round(e[0]+(c[0]-e[0])*h).toString(16);g="0"+Math.round(e[1]+(c[1]-e[1])*h).toString(16);d="0"+Math.round(e[2]+(c[2]-e[2])*h).toString(16);return"#"+r.substring(r.length-2)+g.substring(g.length-2)+d.substring(d.length-2)}function X(f,a,b,d,c){if(a.transparent){G(f,a,b,c)}else{J(f,a,b,d,c)}}function J(k,z,p,a,n){var h,f;var l=document.createElement("div");l.style.fontSize="1px";l.style.backgroundColor=p;var b=0;for(h=1;h<=z.sizey;h++){var u,t,q;arc=Math.sqrt(1-Math.pow(1-h/z.sizey,2))*z.sizex;var c=z.sizex-Math.ceil(arc);var w=Math.floor(b);var v=z.sizex-c-w;var o=document.createElement("div");var m=l;o.style.margin="0px "+c+"px";o.style.height="1px";o.style.overflow="hidden";for(f=1;f<=v;f++){if(f==1){if(f==v){u=((arc+b)*0.5)-w}else{t=Math.sqrt(1-Math.pow(1-(c+1)/z.sizex,2))*z.sizey;u=(t-(z.sizey-h))*(arc-w-v+1)*0.5}}else{if(f==v){t=Math.sqrt(1-Math.pow((z.sizex-c-f+1)/z.sizex,2))*z.sizey;u=1-(1-(t-(z.sizey-h)))*(1-(b-w))*0.5}else{q=Math.sqrt(1-Math.pow((z.sizex-c-f)/z.sizex,2))*z.sizey;t=Math.sqrt(1-Math.pow((z.sizex-c-f+1)/z.sizex,2))*z.sizey;u=((t+q)*0.5)-(z.sizey-h)}}H(z,o,m,n,P(p,a,u));m=o;var o=m.cloneNode(false);o.style.margin="0px 1px"}H(z,o,m,n,a);b=arc}if(n){k.insertBefore(l,k.firstChild)}else{k.appendChild(l)}}function H(c,a,e,d,b){if(d&&!c.tl){a.style.marginLeft=0}if(d&&!c.tr){a.style.marginRight=0}if(!d&&!c.bl){a.style.marginLeft=0}if(!d&&!c.br){a.style.marginRight=0}a.style.backgroundColor=b;if(d){e.appendChild(a)}else{e.insertBefore(a,e.firstChild)}}function G(c,o,l,h){var f=document.createElement("div");f.style.fontSize="1px";var a=document.createElement("div");a.style.overflow="hidden";a.style.height="1px";a.style.borderColor=l;a.style.borderStyle="none solid";var m=o.sizex-1;var j=o.sizey-1;if(!j){j=1}for(var b=0;b<o.sizey;b++){var n=m-Math.floor(Math.sqrt(1-Math.pow(1-b/j,2))*m);if(b==2&&o.sizex==6&&o.sizey==6){n=2}var k=a.cloneNode(false);k.style.borderWidth="0 "+n+"px";if(h){k.style.borderWidth="0 "+(o.tr?n:0)+"px 0 "+(o.tl?n:0)+"px"}else{k.style.borderWidth="0 "+(o.br?n:0)+"px 0 "+(o.bl?n:0)+"px"}h?f.appendChild(k):f.insertBefore(k,f.firstChild)}if(h){c.insertBefore(f,c.firstChild)}else{c.appendChild(f)}}};

/* jQuery mousewheel 3.0.2
 * Copyright (c) 2009 Brandon Aaron (http://brandonaaron.net)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
 * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
 */
(function(c){var a=["DOMMouseScroll","mousewheel"];c.event.special.mousewheel={setup:function(){if(this.addEventListener){for(var d=a.length;d;){this.addEventListener(a[--d],b,false)}}else{this.onmousewheel=b}},teardown:function(){if(this.removeEventListener){for(var d=a.length;d;){this.removeEventListener(a[--d],b,false)}}else{this.onmousewheel=null}}};c.fn.extend({mousewheel:function(d){return d?this.bind("mousewheel",d):this.trigger("mousewheel")},unmousewheel:function(d){return this.unbind("mousewheel",d)}});function b(f){var d=[].slice.call(arguments,1),g=0,e=true;f=c.event.fix(f||window.event);f.type="mousewheel";if(f.wheelDelta){g=f.wheelDelta/120}if(f.detail){g=-f.detail/3}d.unshift(f,g);return c.event.handle.apply(this,d)}})(jQuery);
