﻿
//Array to hold all created scrollbars
var allScrollbars = new Array();

//Scrollbar intialization function called after scrollbar is loaded.
function XamlScrollbarLoaded(sender)
{
    // Requirements for initializing scrollbar:
    //  1. sender is scrollbar defined in xaml
    //  2. sender has the following Tag format:
    //     Tag="Canvas:CanvasName;Geometry:ClipGeometryName;GeometryRect:RectValue"
    //      2a - CanvasName must contain the name of the canvas that will scroll
    //      2b - ClipGeometryName must contain the name of the RectangleGeometry that defines the visible portion of the canvas
    //      2c - RectValue is the value of the RectangleGeometry.Rect property
    //  3. sender must contain at least 3 children:
    //      3a - canvas with tag set to "upArrow", which contains the up arrow [var upArrow]
    //      3b - canvas with tag set to "downArrow", which contains the up arrow [var downArrow]
    //      3c - canvas with tag set to "handle", which contains the scrollbar handle [var handle]
    
    var scrollingCanvas;
    var scrollingCanvasClipGeometry;
    var scrollingCanvasClipGeometryRect;
    var upArrow;
    var downArrow;
    var handle;
    var newScrollbar;

    try
    {
        // Parse sender's Tag
        var nameValuePairs = sender.Tag.split(";");
        for (var i = 0; i < nameValuePairs.length; i++)
        {
            var pair = nameValuePairs[i].split(":");
            switch(pair[0].toLowerCase())
            {
                case "canvas":
                    scrollingCanvas = sender.findName(pair[1]);
                    break;
                case "geometry":
                    scrollingCanvasClipGeometry = sender.findName(pair[1]);
                    break;
                case "geometryrect":
                    scrollingCanvasClipGeometryRect = pair[1];
                    break;
            }
        }
        // Hide scrollbar if not needed
        if (scrollingCanvas.Height <= sender.Height)
        {
            sender.Visibility = "Collapsed";
            return;
        }        
        // Get pointers to upArrow, downArrow, and handle        
        for (var i = 0; i < sender.Children.Count; i++)
        {
            switch(sender.Children.getItem(i).Tag.toLowerCase())
            {
                case "uparrow":
                    upArrow = sender.Children.getItem(i);
                    break;
                case "downarrow":
                    downArrow = sender.Children.getItem(i);
                    break;
                case "handle":
                    handle = sender.Children.getItem(i);
                    break;
            }
        }        
        // Create a new scrollbar, and add it to the collection.
        var newScrollbar = new SilverlightScrollbar(scrollingCanvas, 
                                                    scrollingCanvasClipGeometry,
                                                    scrollingCanvasClipGeometryRect,
                                                    sender,
                                                    handle,
                                                    upArrow,
                                                    downArrow);
        allScrollbars[allScrollbars.length] = newScrollbar;        
    }
    catch(e)
    {
        scrollingCanvas = null;
        scrollingCanvasClipGeometry = null;
        upArrow = null;
        downArrow = null;
        handle = null;
        alert('Scrollbar initialization failed for ' + sender.name + '.\n\nError: ' + e.Message);
    }
}
//Scrollbar constructor.
var SilverlightScrollbar = function(ScrollingCanvas,
                         ScrollingCanvasClipGeometry,
                         ScrollingCanvasClipGeometryRect,
                         Scrollbar,
                         ScrollbarHandle,
                         ScrollbarUpArrow,
                         ScrollbarDownArrow)
{
    // Link up JS objects to XAML objects
    this.scrollingCanvas = ScrollingCanvas;
    this.scrollingCanvasClipGeometry = ScrollingCanvasClipGeometry;
    this.scrollingCanvasInitialTop = this.scrollingCanvas["Canvas.Top"];
    this.scrollbar = Scrollbar;
    this.handle = ScrollbarHandle;
    this.upArrow = ScrollbarUpArrow;
    this.downArrow = ScrollbarDownArrow;

    this.SMALLVALUE = 16;
    this.intervalHandle = null;
    this.maxHandlePosition = this.scrollbar.Height - this.SMALLVALUE - this.handle.Height;

    // Parse out the Clip.RectangleGeometry.Rect values
    var Rect = function(raw)
    {
        var values;
        if (raw.indexOf(",") > -1)
        {
            // Remove whitespace
            raw = raw.replace(new RegExp("\s+", "g"), "");
            values = raw.split(",");            
        }
        else
        {
            // Remove multiple spaces
            raw = raw.replace(new RegExp("\s+", "g"), " ");
            values = raw.split(" ");
        }
        this.X = values[0];
        this.Y = values[1];
        this.Width = values[2];
        this.Height = values[3];
    }
    this.ClipRect = new Rect(ScrollingCanvasClipGeometryRect);    

    // Calculate the ratio of canvas scrolling distance to handle scrolling distance
    //this.ratio = (this.scrollingCanvas.Height - this.ClipRect.Height) / (this.ClipRect.Height - 2 * SMALLVALUE - this.handle.Height);
    this.calculateHandleRatio();
    
    // Attach event handlers
    this.handle.AddEventListener("MouseLeftButtonDown", delegate(this, this.onThumbMouseLeftButtonDown));
    this.handle.AddEventListener("MouseMove", delegate(this, this.onThumbMouseMove));
    this.handle.AddEventListener("MouseLeftButtonUp", delegate(this, this.onThumbMouseLeftButtonUp));
    this.upArrow.AddEventListener("MouseLeftButtonDown", delegate(this, this.onArrowMouseLeftButtonDown));
    this.upArrow.AddEventListener("MouseLeftButtonUp", delegate(this, this.onArrowMouseUpOrLeave));
    this.upArrow.AddEventListener("MouseLeave", delegate(this, this.onArrowMouseUpOrLeave));
    this.downArrow.AddEventListener("MouseLeftButtonDown", delegate(this, this.onArrowMouseLeftButtonDown));
    this.downArrow.AddEventListener("MouseLeftButtonUp", delegate(this, this.onArrowMouseUpOrLeave));
    this.downArrow.AddEventListener("MouseLeave", delegate(this, this.onArrowMouseUpOrLeave));

    // Set scrollbar to top by default
    this.handle["Canvas.Top"] = this.upArrow.Height;
}
//Function to calculate scrollbar ratio.
SilverlightScrollbar.prototype.calculateHandleRatio = function()
{
    this.ratio = (this.scrollingCanvas.Height - this.ClipRect.Height) / (this.ClipRect.Height - 2 * SMALLVALUE - this.handle.Height);
}

//Function to Capture the mouse when pressing the thumb
SilverlightScrollbar.prototype.onThumbMouseLeftButtonDown = function(sender, mouseEventArgs)
{
  this.handle.CaptureMouse();
  this.lastHandlePoint = mouseEventArgs.GetPosition(null);
  this.handleDragging = true;
};

//Function to move the thumb along with the mouse
SilverlightScrollbar.prototype.onThumbMouseMove = function(sender, mouseEventArgs)
{
  if (this.handleDragging)
  {
    var point = mouseEventArgs.GetPosition(null);
    this.scrollTo(this.handle["Canvas.Top"] + point.Y - this.lastHandlePoint.Y);
    this.lastHandlePoint = point;
  }
};

//Function to release mouse capture when releasing the thumb
SilverlightScrollbar.prototype.onThumbMouseLeftButtonUp =
function(sender, mouseEventArgs)
{
  this.handle.ReleaseMouseCapture();
  this.handleDragging = false;
};

// Move the content and thumb to the specified vertical position
SilverlightScrollbar.prototype.scrollTo = function(handlePosition)
{
  // Constrain the position to the bounds of the scrollbar
  handlePosition = Math.max(handlePosition, this.SMALLVALUE);
  handlePosition = Math.min(handlePosition, this.maxHandlePosition);

  if (this.handle["Canvas.Top"] == handlePosition)
  {
    // We're already at the desired position.
    // Just in case this is from a continuous scroll:
    this.stopContinuousScrolling();
  }
  else
  {
    // Move the thumb to the desired position
    this.handle["Canvas.Top"] = handlePosition;

    // Move the canvas to the corresponding position
    var canvasTop = this.scrollingCanvas["Canvas.Top"];
    this.scrollingCanvas["Canvas.Top"] = (this.SMALLVALUE - handlePosition) * this.ratio + this.scrollingCanvasInitialTop;

  }
};

//Function to scroll continuously when pressing the up or down arrow
SilverlightScrollbar.prototype.onArrowMouseLeftButtonDown =
function(sender, mouseEventArgs)
{
  this.startContinuousScrolling(sender.Tag.toLowerCase() == "uparrow");
};

//Function to stop scrolling continuously when releasing the up or down arrow
SilverlightScrollbar.prototype.onArrowMouseUpOrLeave = function(sender, mouseEventArgs)
{
  this.stopContinuousScrolling();
};

//Function to begin continuous scrolling
SilverlightScrollbar.prototype.startContinuousScrolling = function(up)
{

  var delta = this.SMALLVALUE;
  if (up)
    delta *= -1;

  // Call scroll every couple of milliseconds, adding the delta
  var scrollTo = delegate(this, this.scrollTo);
  var handle = this.handle;
  var callback = function() { scrollTo(handle["Canvas.Top"] + delta); }
  this.intervalHandle = setInterval(callback, this.SMALLVALUE);
};

//Function to end the continuous scrolling, if it is happening
SilverlightScrollbar.prototype.stopContinuousScrolling = function()
{
  clearInterval(this.intervalHandle);
};
