// Class encapsulating a XAML entry
function xamlEntry(name, xamlUrl, thumbnailUrl) {
    this.name = name;
    this.xamlUrl = xamlUrl;
    this.thumbnailUrl = thumbnailUrl;
}

function slPad(sender, eventArgs) { }

slPad.prototype.handleLoad = function(control, userContext, rootElement) {
    // Local variables
    this.plugIn = control;           // Store the host plug-in
    this.mouseDownX = -1;            // Used for dragging the scene canvas
    this.mouseDownY = -1;            // Used for dragging the scene canvas
    this.splitterY = -1;             // Used for dragging the splitter
    this.scenePanePercentage = .5;   // Used for calculating size of the scene pane during resizes
    this.items = new Array();        // The xamlItems loaded into the toolbox
    this.xamlIndex = 0;              // Index used for loading XAML files
    this.scrollDownClicked = 0;      // Used to track animation scrolling
    this.scrollUpClicked = 0;        // Used to track animation scrolling
    this.xamlTabActive = true;       // Which code tab is shown
    this.previewHtml;                // HTML used for the preview iframe
    
    // Grab the textareas
    this.jsTextArea = document.getElementById("jsTextArea");
    this.xamlTextArea = document.getElementById("xamlTextArea");
    
    // Silverlight Downloader with progress
    this.loader = this.plugIn.createObject("Downloader");     
    this.previewLoader = this.plugIn.createObject("Downloader");     
    
    // The xaml template that will be used by createFromXaml
    this.itemTemplate = 
        "<Canvas xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'" + 
        "      xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'" + 
        "      x:Name='item$0' " + 
        "      Width='106' " + 
        "      Height='106' Canvas.Left='7.5' Canvas.Top='7.5' Cursor='Hand'>" + 
        "   <Rectangle x:Name='itemBackground$0' Fill='#FF424242' StrokeThickness='0' RadiusX='5' RadiusY='5' Width='106' Height='106'/>" + 
        "   <TextBlock x:Name='itemText$0'  Width='110' Height='15' Foreground='#FFC2C2C2' Text='$1' TextWrapping='Wrap' FontFamily='Tahoma' FontSize='11' Canvas.Left='5' Canvas.Top='4'/>" + 
        "   <TextBlock x:Name='itemXaml$0'  Text='$2' Foreground='#00000000'/>" + 
        "   <Image x:Name='itemImage$0' Width='95' Height='75' Canvas.Left='5' Canvas.Top='25' Source='$3' Stretch='Uniform'/>" + 
        "</Canvas>";
    
    // XAML files loaded into the toolbox
    // Each of these will be turned a xamlItem instance
    this.xamlFiles = new Array();
        this.xamlFiles[0] = new xamlEntry("BugsLogo", "XAML/scenes/BugsLogo.xaml", "Images/BugsLogo.png");
        this.xamlFiles[1] = new xamlEntry("Button", "XAML/scenes/Button.xaml", "Images/Button.png");
        this.xamlFiles[2] = new xamlEntry("Blocks", "XAML/scenes/Blocks.xaml", "Images/Blocks.png");
        this.xamlFiles[3] = new xamlEntry("Coffee", "XAML/scenes/Coffee.xaml", "Images/Coffee.png");
        this.xamlFiles[4] = new xamlEntry("Media", "XAML/scenes/Media.xaml", "Images/Media.png");
        this.xamlFiles[5] = new xamlEntry("Camera", "XAML/scenes/Camera.xaml", "Images/Camera.png");
        this.xamlFiles[6] = new xamlEntry("Cowboy", "XAML/scenes/Cowboy.xaml", "Images/Cowboy.png");
        this.xamlFiles[7] = new xamlEntry("RedScanlineGloss", "XAML/scenes/RedScanlineGloss.xaml", "Images/RedScanlineGloss.png");
        this.xamlFiles[8] = new xamlEntry("WelcomeScreen", "XAML/scenes/WelcomeScreen.xaml", "Images/WelcomeScreen.png");
        this.xamlFiles[9] = new xamlEntry("Glasses", "XAML/scenes/Glasses.xaml", "Images/Glasses.png");
        this.xamlFiles[10] = new xamlEntry("Landscape", "XAML/scenes/Landscape.xaml", "Images/Landscape.png");
        this.xamlFiles[11] = new xamlEntry("Techy", "XAML/scenes/Techy.xaml", "Images/Techy.png");
        this.xamlFiles[12] = new xamlEntry("Popcan", "XAML/scenes/Popcan.xaml", "Images/Popcan.png");
        this.xamlFiles[13] = new xamlEntry("Tile", "XAML/scenes/Tile.xaml", "Images/Tile.png");
        this.xamlFiles[14] = new xamlEntry("Suit", "XAML/scenes/Suit.xaml", "Images/Suit.png");
    
    // Hook the plug-in's resize event
    this.resize(rootElement, null);
    this.setCallback(this.plugIn.content, "onResize", this.resize);
    
    // Show the textarea
    this.xamlTextArea.style.visibility = "visible";
    
    // Bind XAML buttons to javascript code
    var loadButton = rootElement.findName("loadButton");
    var loadButtonRect = rootElement.findName("loadButtonRect");
    new button(loadButton, loadButtonRect, "#FF6D6D6D", "#FF595959", Silverlight.createDelegate(this, this.onLoadXamlClicked));
    
    var zoomOutButton = rootElement.findName("zoomOutButton");
    var zoomOutButtonRect = rootElement.findName("zoomOutButtonRect");
    new button(zoomOutButton, zoomOutButtonRect, "#FF6D6D6D", "#FF595959", Silverlight.createDelegate(this, this.onZoomOutClicked));
    
    var zoomInButton = rootElement.findName("zoomInButton");
    var zoomInButtonRect = rootElement.findName("zoomInButtonRect");
    new button(zoomInButton, zoomInButtonRect, "#FF6D6D6D", "#FF595959", Silverlight.createDelegate(this, this.onZoomInClicked));
    
    var scrollDownButton = rootElement.findName("scrollDownButton");
    var scrollDownButtonRect = rootElement.findName("scrollDownButtonRect");
    new button(scrollDownButton, scrollDownButtonRect, "#FF6D6D6D", "#FF595959", Silverlight.createDelegate(this, this.onScrollDownClicked));
    
    var scrollUpButton = rootElement.findName("scrollUpButton");
    var scrollUpButtonRect = rootElement.findName("scrollUpButtonRect");
    new button(scrollUpButton, scrollUpButtonRect, "#FF6D6D6D", "#FF595959", Silverlight.createDelegate(this, this.onScrollUpClicked));
    
    var xamlTab = rootElement.findName("xamlTab");
    var xamlTabBg = rootElement.findName("xamlTabBg");
    this.xamlTab = new button(xamlTab, xamlTabBg, "#FFC2C2C2", "#FFC2C2C2", Silverlight.createDelegate(this, this.onXamlTabClicked));
    
    var jsTab = rootElement.findName("jsTab");
    var jsTabBg = rootElement.findName("jsTabBg");
    this.jsTab = new button(jsTab, jsTabBg, "#FF6D6D6D", "#FF595959", Silverlight.createDelegate(this, this.onJsTabClicked));
    
    var xamlTitleRect = rootElement.findName("xamlTitleRect");
    xamlTitleRect.addEventListener("mouseLeftButtonUp", Silverlight.createDelegate(this, this.splitterMouseUp));
    xamlTitleRect.addEventListener("mouseLeftButtonDown", Silverlight.createDelegate(this, this.splitterMouseDown));
    xamlTitleRect.addEventListener("mouseMove", Silverlight.createDelegate(this, this.splitterMouseMove));
    
    var scrollDown = rootElement.findName("scrollDown");   
    scrollDown.addEventListener("completed", Silverlight.createDelegate(this, this.scrollDownCompleted));
    
    var scrollUp = rootElement.findName("scrollUp");   
    scrollUp.addEventListener("completed", Silverlight.createDelegate(this, this.scrollUpCompleted));
    
    // Hook up the scene drag event handlers
    var sceneClipCanvas = rootElement.findName("sceneClipCanvas");
    
    sceneClipCanvas.addEventListener("mouseLeftButtonUp", Silverlight.createDelegate(this, this.sceneMouseUp));
    sceneClipCanvas.addEventListener("mouseLeftButtonDown", Silverlight.createDelegate(this, this.sceneMouseDown));
    sceneClipCanvas.addEventListener("mouseMove", Silverlight.createDelegate(this, this.sceneMouseMove));
    
    // Hook up mouse-wheel logic
    this.wheelHelper = new WheelHelper();
    this.wheelHelper.wheelScrolled = Silverlight.createDelegate(this, Silverlight.createDelegate(this, this.processMouseWheel));
    
    // Set the downloader's completed event to a local method and star the downloading
    this.loader.addEventListener("completed", Silverlight.createDelegate(this, this.downloadCompleted));
    this.loader.addEventListener("downloadProgressChanged", Silverlight.createDelegate(this, this.downloadProgressChanged));
    
    this.previewLoader.addEventListener("completed", Silverlight.createDelegate(this, this.previewDownloadCompleted));
    
    // Start the first download    
    this.loader.open("Get", "XAML/SceneJS/default.js");
    this.loader.send();
    
    this.previewLoader.open("Get", "preview.html");
    this.previewLoader.send();
}

slPad.prototype.instanceTemplate = function(index, templateData) {
    // Databind the template by replacing the placeholders with real data
    var templateInstanceString = this.itemTemplate.replaceAll("$0", index + 1);
 
    // Iterate over the proeprties in the data item and fill out the template   
    var i = 0;
    for (var propName in templateData) {
        templateInstanceString = templateInstanceString.replaceAll("$" + (i + 1), templateData[propName]);
        i++;
    }

    // Use createFromXaml to create an element from the string
    var templateInstance = this.plugIn.content.createFromXaml(templateInstanceString);
    return templateInstance;
}

slPad.prototype.downloadProgressChanged = function(sender, args) {
    // Set the width of the progress bar
    var loadingRect = sender.findName("loadingRect");
    loadingRect.width = sender.downloadProgress * 96;
}

slPad.prototype.previewDownloadCompleted = function(sender, args) {
    this.previewHtml = sender.responseText;
}

slPad.prototype.downloadCompleted = function(sender, args) {
    if (sender.uri == "XAML/SceneJS/default.js") {
        this.defaultJs = sender.responseText;
        
        sender.open("Get", "XAML/SceneJS/button.js");
        sender.send();
        return;
    }
    else if (sender.uri == "XAML/SceneJS/button.js") {
        this.buttonJs = sender.responseText;
        
        sender.open("Get", this.xamlFiles[this.xamlIndex].xamlUrl);
        sender.send();
        return;
    }
    
    var itemsCanvas = this.plugIn.content.findName("itemsCanvas");
    var templateInstance;
    
    // Fill out the template and create a new element
    templateInstance = this.instanceTemplate(this.xamlIndex, this.xamlFiles[this.xamlIndex]);
    
    // Position the item in items canvas
    templateInstance["Canvas.Top"] = 2 + (this.xamlIndex * (templateInstance.Height + 5));
    
    itemsCanvas.children.insert(0, templateInstance);
    
    // Attach items to eventhandlers
    this.items[this.xamlIndex] = new xamlItem(templateInstance, this.xamlIndex + 1);
    
    // Attach a click handler to the item
    this.items[this.xamlIndex].clickHandler = Silverlight.createDelegate(this, this.handleXamlItemClick);

    // Store the XAML
    this.items[this.xamlIndex].xaml = sender.responseText;
    
    // TODO: Store the button JS, need a better way to bundle JS and XAML together
    if (this.xamlIndex == 1) {
        this.items[this.xamlIndex].defaultJs = false;
    }
    
    if (this.xamlIndex == 0) {
        this.handleXamlItemClick(this.items[0]);
    }
    
    this.xamlIndex++;
    if (this.xamlIndex >= this.xamlFiles.length) {
        // Hide the downlaod status text
        var loadingTextCanvas = this.plugIn.content.findName("loadingTextCanvas");
        loadingTextCanvas.opacity = 0;
    
        // Set the height of the canvas containing the items
        itemsCanvas.height = this.items.length * (this.items[0].target.height + 5);
    }
    else {
        var loadingText = this.plugIn.content.findName("loadingText");
        loadingText.text = "Loading: " + this.xamlFiles[this.xamlIndex].name + ".xaml";
        
        // Kick off the next download
        this.loader.open("Get", this.xamlFiles[this.xamlIndex].xamlUrl);
        this.loader.send();
    }
}

slPad.prototype.handleXamlItemClick = function(xamlItem) {
    this.xamlTextArea.value = xamlItem.xaml;
    
    if (xamlItem.defaultJs) {
        this.jsTextArea.value = this.defaultJs;
    }
    else {
        this.jsTextArea.value = this.buttonJs;
    }
    
    // Reset the translation
    var sceneTranslate = this.plugIn.content.findName("sceneTranslate");
    sceneTranslate.X = 0;
    sceneTranslate.Y = 0;
    
    var sceneScale = this.plugIn.content.findName("sceneScale");
    sceneScale.scaleX = 1;
    sceneScale.scaleY = 1;
    
    var zoomText = this.plugIn.content.findName("zoomText");
    zoomText.text = "Zoom: " + Math.round(sceneScale.scaleX * 100) + "%";
    
    this.updateScene();
}

slPad.prototype.updateScene = function() {
    if (this.xamlTextArea.value == "") {
        return;
    }
    
    var errorTextBlock = this.plugIn.content.findName("errorTextBlock");
    
    try {
        var scene = this.plugIn.content.createFromXaml(this.xamlTextArea.value);
    }
    catch (ex) {
        errorTextBlock.foreground = "red";
        errorTextBlock.text = "Parse error";
        return;
    }
    
    var sceneHostCanvas = this.plugIn.content.findName("sceneHostCanvas");
    
    if(sceneHostCanvas.children.count > 0) {
        sceneHostCanvas.children.removeAt(0);
    }
    
    try {
        sceneHostCanvas.children.add(scene);
        sceneHostCanvas.Width = scene.width;
        sceneHostCanvas.Height = scene.height;
    }
    catch (exception) {
        errorTextBlock.foreground = "red";
        errorTextBlock.text = "error adding the XAML to the main canvas.  This is likely an error in an animation targetting a non-existant object.";
        return;
    }
    
    errorTextBlock.foreground = "#FFC5C5C5";
    errorTextBlock.text = "Parse successful";
}

slPad.prototype.sceneMouseMove = function(sender, eventArgs) {
    if (this.mouseDownX != -1 && this.mouseDownY != -1) {
        var sceneTranslate = sender.findName("sceneTranslate");
        sceneTranslate.X = eventArgs.getPosition(null).X - this.mouseDownX;
        sceneTranslate.Y = eventArgs.getPosition(null).Y - this.mouseDownY;
    }
}

slPad.prototype.sceneMouseDown = function(sender, eventArgs) {
    var sceneTranslate = sender.findName("sceneTranslate");
    this.mouseDownX = eventArgs.getPosition(null).X - sceneTranslate.X;
    this.mouseDownY = eventArgs.getPosition(null).Y - sceneTranslate.Y;
}

slPad.prototype.sceneMouseUp = function(sender, eventArgs) {
    this.mouseDownX = -1;
    this.mouseDownY = -1;
}

slPad.prototype.onLoadXamlClicked = function(sender, eventArgs) {
    if (this.xamlTabActive) {
        this.updateScene();
    }
    else {
        this.showPreview();
    }
}

slPad.prototype.hidePreview = function() {
    this.xamlTextArea.style.visibility = "visible";
    this.jsTextArea.style.visibility = "visible";
    
    document.body.removeChild(document.getElementById("previewFrame"));
    document.body.removeChild(document.getElementById("bgImage"));
    document.body.removeChild(document.getElementById("closeImage"));
}

slPad.prototype.showPreview = function() {
    this.xamlTextArea.style.visibility = "hidden";
    this.jsTextArea.style.visibility = "hidden";

    var bgImage = document.createElement("img");
    bgImage.id = "bgImage";
    bgImage.src = "assets/overlay.png";
    bgImage.style.position = "absolute";
    bgImage.style.top = 0;
    bgImage.style.left = 0;
    bgImage.width = document.body.clientWidth;
    bgImage.height = document.body.clientHeight;
    bgImage.onclick = Silverlight.createDelegate(this, this.hidePreview);
    document.body.appendChild(bgImage);
    
    var closeImage = document.createElement("img");
    closeImage.id = "closeImage";
    closeImage.src = "assets/closebutton.png";
    closeImage.style.position = "absolute";
    closeImage.style.top = 6;
    closeImage.style.left = document.body.clientWidth - 71;
    closeImage.style.cursor = "hand";
    closeImage.width = 43;
    closeImage.height = 18;
    closeImage.onclick = Silverlight.createDelegate(this, this.hidePreview);
    document.body.appendChild(closeImage);

    var previewFrame = document.createElement("iframe");
    previewFrame.id = "previewFrame";
    previewFrame.style.borderRight = "#C52205 1px solid"; 
    previewFrame.style.borderTop = "#C52205 1px solid"; 
    previewFrame.style.borderLeft = "#C52205 1px solid"; 
    previewFrame.style.borderBottom = "#C52205 1px solid"; 
    previewFrame.frameBorder = 0;
    previewFrame.style.position = "absolute";
    previewFrame.style.top = 30;
    previewFrame.style.left = 30;
    previewFrame.width = document.body.clientWidth - 60;
    previewFrame.height = document.body.clientHeight - 60;
    previewFrame.style.zIndex = 0;
    document.body.appendChild(previewFrame);
    
    var contentText = this.previewHtml.replace("%0", this.xamlTextArea.value);
    contentText = contentText.replace("%1", this.jsTextArea.value);
    
    if(window.ActiveXObject){ // if IE
        document.frames("previewFrame").document.write(contentText);
    }
    else { // if FF
        previewFrame.contentDocument.write(contentText);
    }
}

slPad.prototype.onXamlTabClicked = function(sender, eventArgs) {
    this.xamlTabActive = true;
    this.swapTabs(sender);
}

slPad.prototype.onJsTabClicked = function(sender, eventArgs) {
    this.xamlTabActive = false;
    this.swapTabs(sender);
}

slPad.prototype.swapTabs = function(sender) {
    if (this.xamlTabActive) {
        this.xamlTextArea.style.zIndex = 1;
        this.jsTextArea.style.zIndex = 0;
        
        this.xamlTab.oldFill = "#FFC2C2C2";
        this.xamlTab.target.children.getItem(1).foreground = "#FF393939";
        this.xamlTab.hoverFill = "#FFC2C2C2";
        this.xamlTab.mouseDownFill = "#FFC2C2C2";
        
        this.jsTab.backgroundElement.fill = "#FF393939";
        this.jsTab.target.children.getItem(1).foreground = "#FFC2C2C2";
        this.jsTab.hoverFill = "#FF6D6D6D";
        this.jsTab.mouseDownFill = "#FF595959";
        
        sender.findName("loadButtonText")["canvas.left"] -= 3;
        sender.findName("loadButtonText").text = "Parse";
    }
    else {
        this.xamlTextArea.style.zIndex = 0;
        this.jsTextArea.style.zIndex = 1;
        
        this.xamlTab.backgroundElement.fill = "#FF393939";
        this.xamlTab.target.children.getItem(1).foreground = "#FFC2C2C2";
        this.xamlTab.hoverFill = "#FF6D6D6D";
        this.xamlTab.mouseDownFill = "#FF595959";
        
        this.jsTab.oldFill = "#FFC2C2C2";
        this.jsTab.target.children.getItem(1).foreground = "#FF393939";
        this.jsTab.hoverFill = "#FFC2C2C2";
        this.jsTab.mouseDownFill = "#FFC2C2C2";
        
        sender.findName("loadButtonText")["canvas.left"] += 3;
        sender.findName("loadButtonText").text = "Run";
    }
}

slPad.prototype.scrollUpCompleted = function(sender, eventArgs) {
    this.scrollUpClicked--;
    if (this.scrollUpClicked > 0) {
        sender.begin();
    }
}

slPad.prototype.scrollDownCompleted = function(sender, eventArgs) {
    this.scrollDownClicked--;
    if (this.scrollDownClicked > 0) {
        sender.begin();
    }
}

slPad.prototype.onScrollDownClicked = function(sender, eventArgs) {
    var itemsCanvas = sender.findName("itemsCanvas");
    var itemsClippingCanvas = sender.findName("itemsClippingCanvas"); 
    var scrollDown = sender.findName("scrollDown");
    
    if (itemsClippingCanvas.height - itemsCanvas["canvas.top"] + (this.scrollDownClicked * 111) < itemsCanvas.height) {
        if (this.scrollDownClicked == 0) {
            scrollDown.begin();
        }
        this.scrollDownClicked++;
    }
}

slPad.prototype.onScrollUpClicked = function(sender, eventArgs) {
    var itemsCanvas = sender.findName("itemsCanvas");
    var scrollUp = sender.findName("scrollUp");
    
    if (itemsCanvas["canvas.top"] + 111 + (this.scrollUpClicked * 111) <= 0) {
        if (this.scrollUpClicked == 0) {
            scrollUp.begin();
        }
        this.scrollUpClicked++;
    }
}

slPad.prototype.splitterMouseDown = function(sender, eventArgs) {
    var xamlTitleRect = sender.findName("xamlTitleRect");
    xamlTitleRect.captureMouse();
    var codeCanvas = sender.findName("codeCanvas");
    this.splitterY = eventArgs.getPosition(null).Y - codeCanvas["canvas.top"];
}

slPad.prototype.splitterMouseUp = function(sender, eventArgs) {
    this.splitterY = -1;
    var xamlTitleRect = sender.findName("xamlTitleRect");
    xamlTitleRect.releaseMouseCapture();
}

slPad.prototype.splitterMouseMove = function(sender, eventArgs) {
    if (this.splitterY != -1) {
        // Find the new height percentage and resize the scene
        var codeCanvas = sender.findName("codeCanvas");
        var targetHeight = this.plugIn.content.actualHeight;
        
        this.scenePanePercentage = (eventArgs.getPosition(null).Y - this.splitterY) / targetHeight;
        this.resize(sender, null);
    }
}

slPad.prototype.onZoomInClicked = function(sender, eventArgs) {
    this.processZoomIn(sender);
}
slPad.prototype.onZoomOutClicked = function(sender, eventArgs) {
    this.processZoomOut(sender);
}

slPad.prototype.processZoomOut = function() {
    var sceneScale = this.plugIn.content.findName("sceneScale");
    
    // Handle any rounding errors
    var scale = Math.round(sceneScale.scaleX * 100);
    scale = scale / 100;
    
    if (scale <= .01) {
        return;
    }
    
    if (scale <= .1) {
        sceneScale.scaleX -= .01;
        sceneScale.scaleY -= .01;
    }  
    else if (scale <= 1) {
        sceneScale.scaleX -= .1;
        sceneScale.scaleY -= .1;
    }
    else {
        sceneScale.scaleX -= .25;
        sceneScale.scaleY -= .25;
    }
    
    var zoomText = this.plugIn.content.findName("zoomText");
    zoomText.text = "Zoom: " + Math.round(sceneScale.scaleX * 100) + "%";
}

slPad.prototype.processZoomIn = function() {
    var sceneScale = this.plugIn.content.findName("sceneScale");
  
    // Handle any rounding errors
    var scale = Math.round(sceneScale.scaleX * 100);
    scale = scale / 100;
  
    if (scale >= 1) {
        sceneScale.scaleX += .25;
        sceneScale.scaleY += .25;
    }
    else if (scale >= .1) {
        sceneScale.scaleX += .1;
        sceneScale.scaleY += .1;
    }
    else {
        sceneScale.scaleX += .01;
        sceneScale.scaleY += .01;
    }  
    
    var zoomText = this.plugIn.content.findName("zoomText");
    zoomText.text = "Zoom: " + Math.round(sceneScale.scaleX * 100) + "%";
}

slPad.prototype.processMouseWheel = function(delta) {
    if (delta < 0) {
        this.processZoomOut();
    }
    else {
        this.processZoomIn();
    }
}

// Resizes the XAML scene to fill the browser
slPad.prototype.resize = function(sender, eventArgs) {
    // Verify that the first paint has happened and we have a valid plug-in size
    if (this.plugIn.content.actualWidth == 0) {
        return;
    }

    // Find the new size of the control
    var targetWidth = this.plugIn.content.actualWidth;
    var targetHeight = this.plugIn.content.actualHeight;

    // Calculate height based on percentage
    var sceneHeight = targetHeight / (1 / this.scenePanePercentage);

    // Layout XAML items toolbar
    var borderRect = sender.findName("borderRect");
    borderRect.height = targetHeight - 27;

    var itemsClippingCanvas = sender.findName("itemsClippingCanvas");
    itemsClippingCanvas.clip = "M3,3L115,3 115," + (borderRect.height - 41) + " 3," + (borderRect.height - 41) + "z";
    itemsClippingCanvas.height = borderRect.height - 41;

    var scrollDownButton = sender.findName("scrollDownButton");
    scrollDownButton["canvas.top"] = targetHeight - 20;

    // Layout scene pane
    var sceneTitleRect = sender.findName("sceneTitleRect");
    sceneTitleRect.width = targetWidth - 130;

    var sceneHatch = sender.findName("sceneHatch");
    sceneHatch.height = sceneHeight - 27;
    sceneHatch.width = targetWidth - 131;

    var sceneClipCanvas = sender.findName("sceneClipCanvas");
    sceneClipCanvas.clip = "M0,0L" + sceneHatch.width + ",0 " + sceneHatch.width + "," + sceneHatch.height + " 0," + sceneHatch.height + "z";

    var zoomUI = sender.findName("zoomUI");
    zoomUI["canvas.left"] = targetWidth - 240;

    // Layout code pane
    var codeCanvas = sender.findName("codeCanvas");
    codeCanvas["canvas.top"] = sceneHatch.height + 27;

    this.xamlTextArea.style.top = sceneHeight + 25;
    this.xamlTextArea.style.width = targetWidth - 131;
    if ((targetHeight - sceneHeight) - 52 < 1) {
        this.xamlTextArea.style.visibility = "hidden";
    }
    else {
        this.xamlTextArea.style.visibility = "visible";
        this.xamlTextArea.style.height = (targetHeight - sceneHeight) - 52;
    }

    this.jsTextArea.style.top = sceneHeight + 25;
    this.jsTextArea.style.width = targetWidth - 131;
    if ((targetHeight - sceneHeight) - 52 < 1) {
        this.jsTextArea.style.visibility = "hidden";
    }
    else {
        this.jsTextArea.style.visibility = "visible";
        this.jsTextArea.style.height = (targetHeight - sceneHeight) - 52;
    }

    var xamlTitleRect = sender.findName("xamlTitleRect");
    xamlTitleRect.width = targetWidth - 130;

    var loadButton = sender.findName("loadButton");
    loadButton["canvas.left"] = targetWidth - 77;
    loadButton["canvas.top"] = targetHeight - 25;

    var errorTextBlock = sender.findName("errorTextBlock");
    errorTextBlock["canvas.top"] = targetHeight - 22;

    var loadingTextCanvas = sender.findName("loadingTextCanvas");
    loadingTextCanvas["canvas.top"] = targetHeight - 110;

    // Layout preview if it's showing
    var previewFrame = document.getElementById("previewFrame");
    if (previewFrame) {
        previewFrame.width = document.body.clientWidth - 60;
        previewFrame.height = document.body.clientHeight - 60
        this.jsTextArea.style.visibility = "hidden";
        this.xamlTextArea.style.visibility = "hidden"
        
        var bgImage = document.getElementById("bgImage");
        bgImage.width = document.body.clientWidth;
        bgImage.height = document.body.clientHeight;
        
        var closeImage = document.getElementById("closeImage");
        closeImage.style.left = document.body.clientWidth - 71;
    }
}

slPad.prototype.setCallback = function(target, eventName, callback) {
    if (!window.methodIndex)
        window.methodIndex = 0;
    
    var callbackName = "uniqueCallback" + (window.methodIndex++);
    var controller = this;
    var func = function() {
        callback.apply(controller, arguments);
    }
    
    //eval(callbackName + " = func;");  //Why is 'eval' here?
    callbackName = func;
    target[eventName] = callbackName;
}

