﻿/// <summary>
/// Bind live events to buttons on page load
/// </summary>
/// <remarks>
/// Live events are automatically bound to new elements added via ajax
/// </remarks>
$(document).ready(function() {

    // Bind Applet Button Events
    $(".CreateButton").live("click", CreateButtonClicked);
    $(".DeleteButton").live("click", DeleteButtonClicked);
    $(".LoadControlButton").live("click", LoadControlButtonClicked);
    $(".ModeButton").live("click", ModeButtonClicked);
    $(".UpdateButton").live("click", UpdateButtonClicked);
    
    $(".ImageManager").live("AppletLoaded", ImageManager.Initialize);


    Console("Live Events", "Events have been bound");
});




/* SETTINGS
-----------------------------------------------------------------------------------------------*/
var Settings = {

    ConsoleEnabled: false,
    
    // Settings for jQuery dialog
    Dialog: {
        // completely removes the dialog and shows the element when dialog is closed
        Default: {
            modal: true,
            close: function() {
                $(this).dialog("destroy");
                $(this).css("display", "block");
            }
        },
        // completely removes the dialog and element when dialog is closed
        RemoveOnClosing: {
            close: function() {
                $(this).dialog("destroy");
                $(this).remove();
            },
            modal: true
        }
    },
    
    // how long a notification is displayed
    NotificationDuration: 3500,
    
    // the speed items will transition by default
    TransitionSpeed: 500,

    // settings for the webservice
    WebService: {
        URL: 'WebService1.asmx'
    }
}




/* APPLET BUTTON CLICK EVENT HANDLERS
-----------------------------------------------------------------------------------------------*/
/// <summary>
/// Returns the control that contains the child element passed in
/// </summary>
/// <param name="childElem">a child element of a control</param>
function GetControl(childElem) {
    var button = $(childElem);
    var ctrl = button.parents("*[controlId]:first");
    return ctrl;
}


/// <summary>
/// Calls the Webservice CreateControl method
/// </summary>
function CreateButtonClicked(e) {
    
    var button = $(this);
    //var ctrl = GetControl(button);

    // called after the control has been created
    function callback(html) {
        button.before(html);
    }

    // call the create method of the control
    button.createControl(callback);
    
    // prevent the button's default postback behavior
    return false;
}


/// <summary>
/// Calls the webservice delete control method
/// </summary>
function DeleteButtonClicked(e) {

    var button = $(this);
    var ctrl = GetControl(button);

    // call the delete method of the control
    ctrl.deleteControl();

    // prevent the button's default postback behavior
    return false;
}


/// <summary>
/// Loads the control specified by the button and places it into the document
/// </summary>
/// <remarks>
/// This control provides the option to load into a dialog box
/// </remarks>
function LoadControlButtonClicked(e) {
    var button = $(this);
    var postData = button.getPostData();

    // is called when the control has been returned from the WS
    function callback(html) {
       
        // add the html to the document
        $("body").append(html);
        
        
        // trigger the AppletLoaded event for controls that have this event bound (live event)
        html.trigger("AppletLoaded");
        
        
        // see if dialog requested
        var showDialog = button.attr("DisplayInDialog");
        if (showDialog) 
        {
            var _settings = Settings.Dialog.RemoveOnClosing;
            
            // apply dialog size if specified
            var dialogSize = button.attr("DialogSize");
            if(dialogSize)
            {
                _settings.width = parseInt(dialogSize.split("x")[0]);
                _settings.height = parseInt(dialogSize.split("x")[1]);
            }
            
            // apply dialog title if specified
            var dialogTitle = button.attr("DialogTitle");
            if(dialogTitle)
                _settings.title = dialogTitle;
                
            html.dialog(_settings);
        }
       
    }

    WSGetControl(postData, callback);
    
    return false;
}


/// <summary>
/// Loads a different Control mode and replaces the current control
/// </summary>
function ModeButtonClicked(e) {
        
    var button = $(this);
    var ctrl = GetControl(button);

    ctrl.replaceControl(button.attr("LoadType"), ctrl.attr("controlId"));

    return false;
}


/// <summary>
/// Calls the webservice UpdateControl method 
/// </summary>
/// <remarks>
/// Input elements in html received from WS do not contain a name attribute
/// </remarks>
function UpdateButtonClicked(e) {
    
    var button = $(this);
    var ctrl = GetControl(button);

    ctrl.updateControl();

    return false;
}




/* WEBSERVICE METHODS
-----------------------------------------------------------------------------------------------*/
/// <summary>
/// Provides access to the webservice
/// </summary>
/// <param name="data">key / value data that will be posted to the webservice</param>
/// <param name="callback">a function that is called when the webservice responds</param>
/// <param name="method">the webservice method to call</param>
/// <remarks>
/// This function is usually not called directly, but rather through the helper methods below
/// </remarks>
function WebService(data, callback, method) {

    Console("Webservice." + method, data);
    
    var url = getWSURL(method);
    
    jQuery.ajax({
        type: 'post',
        url: url,
        data: data,
        success: successCallback,
        error: WebServiceError,
        dataType: 'xml'
    });

        
    function getWSURL(method){
        // WebService.asmx || http://www.domain.com/WebService.asmx
        var ws = Settings.WebService.URL
        
        if(!ws.endsWith("/"))
            ws = ws + "/";
        
        // WebService.asmx/method || http://www.domain.com/WebService.asmx/method
        return ws + method;
    }
    
    function successCallback(data, textStatus, XMLHttpRequest){
        var string = $(data).find("string").text();
        if(string.beginsWith("Error"))
            WebServiceError(XMLHttpRequest, textStatus, data);
        else
            callback(data, textStatus, XMLHttpRequest);
    }
}


/// <summary>
/// Handles a webservice error
/// </summary>
/// <param name="XMLHttpRequest"></param>
/// <param name="textStatus"></param>
/// <param name="errorThrown"></param>
/// <remarks>
/// </remarks>
function WebServiceError(XMLHttpRequest, textStatus, errorThrown) {
    var html = $(XMLHttpRequest.responseText);
    var status = parseInt(XMLHttpRequest.status);
    Console("Webservice.Error:" + status, html, true);
    //alert("Webservice Error");
}  


/// <summary>
/// Calls the Webservice CreateControl method
/// </summary>
/// <param name="data">key / value data that will be posted to the webservice</param>
/// <param name="callback">a function that is called when the webservice responds</param>
/// <remarks>
/// </remarks>
function WSCreateControl(data, callback) {

    WebService(data, wsResponded, "CreateControl");

    // extracts the html from the webservice response and passes it to the callback function
    function wsResponded(data, textStatus, XMLHttpRequest) {
        var string = $(data).find("string").text();
        var html = $(string);
        callback(html);
    }
}


/// <summary>
/// Calls the Webservice DeleteControl method
/// </summary>
/// <param name="data">key / value data that will be posted to the webservice</param>
/// <param name="callback">a function that is called when the webservice responds</param>
function WSDeleteControl(data, callback) {
    WebService(data, callback, "DeleteControl");
}


/// <summary>
/// Calls the Webservice GetControlHtml method
/// </summary>
/// <param name="data">an object with controlType and controlId properties for the control to load</param>
/// <param name="callback">a function that is called when the webservice responds</param>
/// <remarks>
/// [10/06/2010] - parses the ws response and sends the html to the callback function
/// </remarks>
function WSGetControl(data, callback) {

    WebService(data, wsResponded, "GetControlHtml");
    
    // extracts the html from the webservice response and passes it to the callback function
    function wsResponded(data, textStatus, XMLHttpRequest) {
        var string = $(data).find("string").text();
        var html = $(string);
        callback(html);
    }
}


/// <summary>
/// Calls the Webservice UpdateControl method 
/// </summary>
/// <param name="data">key / value data that will be posted to the webservice</param>
/// <param name="callback">a function that is called when the webservice responds</param>
function WSUpdateControl(data, callback) {
    WebService(data, callback, "UpdateControl");
}







/* CONSOLE
-----------------------------------------------------------------------------------------------*/


/// <summary>
/// a shortcut for showing console entries
/// </summary>
/// <param name="name">The name given to the console entry</param>
/// <param name="value">The value shown next to the name</param>
function Console(name, value, isHtml) {
    var table = $(".console");
    if (table.length == 0) {
        table = $("<table class='console'/>");
        $("body").append(table);
    }
    
    table.console(name, value, isHtml);
}


/// <summary>
/// shows informatino to the user
/// </summary>
/// <param name="name">The name given to the console entry</param>
/// <param name="value">The value shown next to the name</param>
/// <remarks>
/// </remarks>
jQuery.fn.console = function(name, value, isHtml) {

    if (!Settings.ConsoleEnabled)
        return;

    var table = $(this);

    var buttons = table.find(".buttons");

    if (buttons.length == 0) {
        buttons = $("<tr class='buttons'><td colspan='2'><input type='button' value='clear' /></td></tr>");
        var clear = buttons.find("input");
        table.append(buttons);
        clear.click(function() { table.find("tr").remove(); return false; });
    }

    var row = $("<tr/>");

    var n = $("<th class='name' />");
    n.text(name);

    var v = $("<td class='value' />");

    // if value is an object write the overridden toString (Object.prototype.toString)
    if (typeof value == 'object' && !isHtml)
        value = value.toString();

    if(isHtml)
        v.append(value);
    else
        v.text(value);

    row.append(n);
    row.append(v);

    buttons.before(row);
}




/* VISUAL EFFECTS
-----------------------------------------------------------------------------------------------*/

/// <summary>
/// causes an element to fade out and back in again, giving a flashing appearance
/// </summary>
/// <remarks>
/// </remarks>
jQuery.fn.flash = function(speed) {
    var ctrl = $(this);
    
    // use the default speed if none specified
    if (!speed)
        speed = Settings.TransitionSpeed;

    ctrl.fadeTo(speed, 0.10, function() { ctrl.fadeTo(speed, 1.0); });
}




/* JQUERY CONTROL METHODS
-----------------------------------------------------------------------------------------------*/
/// <summary>
/// Calls the webservice create control method
/// </summary>
/// <param name="callback">This function is passed the new control html returned from the WS</param>
/// <remarks>
/// Call this method on a control element
/// </remarks>
jQuery.fn.createControl = function(callback) {
    var ctrl = $(this);
    
    var postData = ctrl.getPostData();
    
    WSCreateControl(postData, callback);
}


/// <summary>
/// Calls the webservice delete control method
/// </summary>
/// <remarks>
/// The control is removed from the document
/// </remarks>
jQuery.fn.deleteControl = function() {
    var ctrl = $(this);
    var postData = ctrl.getPostData();

    // called after the control has been deleted
    function callback(data, textStatus, XMLHttpRequest) {
        ctrl.remove();
    }

    // confirm and delete if desired
    if (confirm("Are you sure you want to delete this control?"))
        WSDeleteControl(postData, callback);
};


/// <summary>
/// Gathers data that will be posted to the server
/// </summary>
/// <remarks>
/// This function returns control type & id, input element class / value pairs, and SerializeAttributes added by the AttributeManager
/// [10/06/2010] - is a modified version of serializeAnything jQuery plugin
/// [10/06/2010] - input elements rendered in WSGetControl do not contain a name attribute which is required in default serializeAnything implementation
/// </remarks>
(function($) {
    jQuery.fn.getPostData = function() {
        var ctrl = $(this);
        var ret = new Object();
        
        // send the control type and id 
        ret["ControlType"] = ctrl.attr("controlType");
        ret["ControlId"] = ctrl.attr("controlId");
        
        // send additional information if provided by AttributeManager
        var attrNames = ctrl.attr("SerializeAttributes");
        
        if(attrNames)
        {
            if(attrNames.indexOf('|') > -1)
            {
                attrNames = attrNames.split('|');
                for (var i = 0; i < attrNames.length; i++) {
                    var name = attrNames[i];
                    var value = ctrl.attr(name);
                    if(name.length > 0)
                        ret[name] = value;
                }
            } else {
                ret[attrNames] = ctrl.attr(attrNames);
            }
        }
        
        // send child input element class / value pairs if they exist 
        var els = ctrl.find('*');
        $.each(els, function() {
            if($(this).attr("PostDataKey"))
            {
                var val = $(this).val();
                ret[$(this).attr("PostDataKey")] = val;
            }
        });
        
        return ret;
    }
    
    
})(jQuery);


/// <summary>
/// Refreshes a control's content
/// </summary>
/// <remarks>
/// This calls the replaceControl method on itself
/// </remarks>
jQuery.fn.refreshControl = function() {
    var ctrl = $(this);
    var newType = ctrl.attr("controlType");
    var newId = ctrl.attr("controlId");
    ctrl.replaceControl(newType, newId);
}


/// <summary>
/// Replaces one control with another
/// </summary>
/// <param name="newType">The type of control that will replace this control</param>
/// <param name="newId">The id of the control that will replace this control</param>
/// <remarks>
/// Call this method on a control element
/// </remarks>
jQuery.fn.replaceControl = function(newType, newId) {
    var ctrl = $(this);
    var postData = { controlType: newType, controlId: newId };

    // replace the existing control with the new control
    function callback(html) {
        // make the new control invisible
        html.fadeOut(1);

        // fade out the existing control
        ctrl.fadeOut(500, function() {
            // once faded out, replace the existing control with the invisible new control and fade it in
            ctrl.after(html);
            ctrl.remove();
            html.fadeIn(500);
        });
    }

    WSGetControl(postData, callback);
}


/// <summary>
/// Calls the webservice update control method
/// </summary>
/// <param name="callback">This function gets passed the html returned from the WS</param>
/// <remarks>
/// Call this method on a control element
/// </remarks>
jQuery.fn.updateControl = function() {
    var ctrl = $(this);

    var postData = ctrl.getPostData();

    function callback(data, textStatus, XMLHttpRequest) {
        ctrl.flash(500);
    }

    WSUpdateControl(postData, callback);
}




/* PROTOTYPES
-----------------------------------------------------------------------------------------------*/
/// <summary>
/// Returns an object properties as a string formatted name:'value',...,name:'value'
/// </summary>
/// <remarks>
/// Returns an empty string if no properties exist for the object
/// </remarks>
Object.prototype.toString = function() {
    var value = "";
    for (var i in this) {
        value += i + ":'" + this[i] + "', ";
    }
    if(value.length >= 2)
        value = value.substring(0, value.length - 2);
        
    return value;
}

/// <summary>
/// Returns true if a String object starts with the string parameter
/// </summary>
String.prototype.beginsWith = function(string) {
    var len = string.length;
    if(this.length >= len && this.substring(0, len) == string)
        return true;
    else 
        return false;
}

/// <summary>
/// Returns true if a String object ends with the string parameter
/// </summary>
String.prototype.endsWith = function(string) {
    var len = string.length;
    if(this.length >= len && this.substring(this.length - len, len) == string)
        return true;
    else 
        return false;
}






/* IMAGENAVIGATOR
-----------------------------------------------------------------------------------------------*/
ImageNavigator = new function(){

    // gets the index of the currently displayed image
    function getCurrentIndex(displays){
        for(var i = 0; i<displays.length; i++)
        {
            var img = $(displays[i]);
            var hidden = img.hasClass("hidden");
            if(!hidden)
                return i;
        }
    }
    
       
    // transitions between the old and new image
    function transition(oldImage, newImage){
        $(oldImage).addClass("hidden");
        $(newImage).removeClass("hidden");
    }


    // display the previous image
    this.Back = function(button, e){
        var b = $(button);
        var nav = b.parents(".ImageNavigator");
        var caption = nav.find(".Navigation .Caption");
        var displays = nav.find(".ImageCollection .ImageDisplay");
        var current = getCurrentIndex(displays);
        var newImage = current - 1
        if(current == 0)
            newImage = displays.length - 1;
                
        transition(displays[current], displays[newImage]);
        
        caption.text((newImage + 1) + " of " + displays.length);
        
    }
    
    // display the next image        
    this.Next = function(button){
        var b = $(button);
        var nav = b.parents(".ImageNavigator");
        var caption = nav.find(".Navigation .Caption");
        var displays = nav.find(".ImageCollection .ImageDisplay");
        var current = getCurrentIndex(displays);
        var newImage = current + 1
        if(current == displays.length - 1)
            newImage = 0;
                
        transition(displays[current], displays[newImage]);
        
        caption.text((newImage + 1) + " of " + displays.length);
        
    }

}






// [9/28/2010] - Created to provide zoom capabilities for an ImageContainer object

jQuery.fn.zoom = function(){
    var button = $(this);

    var src = $(this).parent().find(".ImageContainer img").attr("src");
    var sizes = $(this).attr("Sizes").split('|');
    var currentSize = getCurrentSize(src); // get current size from url
    var newSize = getLargerSize(currentSize, sizes); // get the next size up
    var newSrc = updateImageSize(src, newSize); // calculate the new url
           
    var div = $("<div/>");
    var img = $("<img src='" + newSrc + "'/>");
    div.append(img);
    $(document).append(div);
    div.css("width", img.css("width"));
    div.css("height", img.css("height"));
    
    var settings = Settings.Dialog.RemoveOnClosing;
    settings.width = img.css("width");
    settings.height = img.css("height");
    settings.position = [20,20];
    settings.resizable = false;
    
    div.dialog(settings);
    
    // gets the size of the current image by parsing the url
    function getCurrentSize(currentSrc){
        var re = /\d+\.[a-zA-Z]+$/;
        var m = re.exec(currentSrc).toString();
        return m.substring(0, m.indexOf('.'));
    }
    
    // gets the next larger size of the image sizes
    function getLargerSize(currentSize, sizes){
        var len = sizes.length;
        if(typeof sizes == "string")
            sizes = sizes.split("|");
        
        for(var i=0; i<len; i++)
        {
            if(i<len-1 && sizes[i]==currentSize)
                return sizes[i+1];
        }
        
        return currentSize;
    }
    
    // replaces the size in an image source
    function updateImageSize(src, newSize){
        var re = /\d+\.[a-zA-Z]+$/;
        var m = re.exec(src).toString();
        var ext = m.substring(m.indexOf('.'));
        return src.replace(re, newSize + ext)
    }
    
}








/* IMAGEMANAGER
-----------------------------------------------------------------------------------------------*/

ImageManager = new function(){
    
    var deleteButton;
    var displays;
    var manager;
    var sortOrder;
    var sortMode;
    var selectMode;
    var thumbs;
    var uploader;
    var caption;
   
    
    /// <summary>
    /// Initializes an image manager control
    /// </summary>
    /// <remarks>
    /// Initialize is called when the applet loads
    /// </remarks>
    this.Initialize = function(){
        manager = $(this);
        deleteButton = manager.find(".Delete");
        sortOrder = manager.find("input[type='hidden']");
        thumbs = manager.find(".ImageThumbnails");
        displays = thumbs.find(".ImageDisplay");
        uploader = manager.find(".Upload");
        caption = manager.find(".ImageCaption");
        enableThumbnails();
        
        uploader.enableUpload();
    }
    
    
    // occurs when all uploads have completed in the que
    this.AllUploadsComplete = function(){
        $.jnotify("All uploads have finished!", Settings.NotificationDuration);
        refreshNavigator()
    }
    
    
    // the delete button calls this from the onclick attr
    this.Delete = function(){
        var toDelete = getSelectedImages();
        if(toDelete.length == 0)
        {
            $.jnotify("No Images were selected to delete", Settings.NotificationDuration);
            return;
        }
        
        toDelete.each(function(){
            $(this).deleteControl();
        });
        
        $.jnotify("File Deleted", Settings.NotificationDuration);
        
        refreshNavigator();
    }
    
    
    this.SaveCaption = function(button){
        display = caption["display"];
        if(display == null)
            return;
            
        display.attr("caption", caption.val());
        display.updateControl();
        
        refreshNavigator();
    }
    
    
    // occurs when an image has been uploaded
    this.UploadComplete = function(serverData){
        var ctrl = $(serverData);
        var clear = thumbs.find(".clear");
        clear.before(ctrl);
        enableThumbnails();
    }
    
    
    
    
    
    function clearCaption(display){
        caption["display"] = null;
        caption.val("");
    }   
    
    // enables highlighting the thumbnail click
    function enableThumbnails(){
        displays.live("mousedown", enableSortMode);
        thumbs.live("mousedown", enableSelectMode);
        enableSortMode();
    }
    
    
    // gets the currently selected images
    function getSelectedImages(){
        return manager.find(".ui-selected");
    }
    
    
    // Makes images sortable
    function enableSortMode(e)
    {
        deleteButton.removeAttr("disabled");

        // stop the select and start sort
        thumbs.selectable("destroy");
        thumbs.sortable({ 
            items: '.ImageDisplay',
            update: onThumbnailsSorted
        });   
    
        // highlight the newly selected image
        var display = $(this);
        display.parent().find(".ui-selected").removeClass("ui-selected");
        display.addClass("ui-selected");
    
        setCaption(display);
    
        return false;
    }
    
    
    // Makes images selectable
    function enableSelectMode(e)
    {
        clearCaption();
        
        thumbs.sortable("destroy");
        
        thumbs.selectable({ 
            filter: 'div.ImageDisplay'
        });
        return false;
    }
    
    
    // occurs when the thumbnail sort order has changed
    function onThumbnailsSorted(event, ui) 
    {  
        var container = $(this);
        var movedImage = ui.item;
        var sortPosition = container.find(".ImageDisplay").index(movedImage);
        movedImage.attr("Sequence", sortPosition);
        movedImage.updateControl();
        setTimeout(function(){refreshNavigator();}, 250);
    }
    
    function refreshNavigator(){
        var id = manager.attr("controlId");
        var navigator = $(".ImageNavigator[controlId='" + id + "']");
        navigator.refreshControl();
    }
    
    function setCaption(display){
        caption["display"] = display;
        caption.val(display.attr("caption"));
    }   
}






/* SWF Upload
-----------------------------------------------------------------------------------------------*/
var uploaders = new Object();

jQuery.fn.enableUpload = function() {

    return $(this).each(function() {

        
        var attr = $(this).attr("swfUploadified");
        if (attr != "true") {

            $(this).attr("swfUploadified", "true");

            // id of the element
            var id = $(this).attr("id");
            if(id == "")
                id = $(this).attr("name");
            

            var html = GetUploaderHtml(id);

            $(this).after(html);

            $(this).css("display", "none");

            var settings = {
                ControlType : $(this).attr("ControlType"),
                ControlId : $(this).attr("ControlId"),
                FileExtensions : $(this).attr("FileExtensions"),
                FileDescription : $(this).attr("FileDescription")
            }
            
            var uploader = enableUpload(id, settings);

            uploaders[id] = uploader;
        }

    });
};


function AllUploadsComplete(id) {
    // refresh the panel
}

function cancelUpload(id) {
    var uploader = uploaders[id];
    cancelQueue(uploader)
}

function GetUploaderHtml(id) {

    var html =
    '<div id="swfUploadDiv_' + id + '">' +
		'<div style="padding-left: 5px;">' +
		'	<span id="spanButtonPlaceholder_' + id + '"></span>' +
		'	<img style="display:none;" id="btnCancel_' + id + '" src="/images/Cancel.png" disabled="disabled" style="margin-left: 2px; height: 22px; font-size: 8pt;" />' +
		'	<br />' +
		'</div>' +
		'<div class="fieldset flash" id="fsUploadProgress_' + id + '">' +
		'</div>' +
    '</div>';

    var div = $(html);

    return div;
}


function enableUpload(id, settings) {
    
    var uploader = new SWFUpload({
        // Backend Settings
        //upload_url: "/swfUpload/upload.aspx",
        upload_url: Settings.WebService.URL + "/UploadImage",

        // File Upload Settings
        file_size_limit: "102400", // 100MB
        //file_types: "*.jpg;*.jpeg",
        file_types: settings.FileExtensions,
        //file_types_description: "Images",
        file_types_description: settings.FileDescription,
        file_upload_limit: "100",
        file_queue_limit: "0",

        // Event Handler Settings (all my handlers are in the Handler.js file)
        file_dialog_start_handler: fileDialogStart,
        file_queued_handler: fileQueued,
        file_queue_error_handler: fileQueueError,
        file_dialog_complete_handler: fileDialogComplete,
        upload_start_handler: uploadStart,
        upload_progress_handler: uploadProgress,
        upload_error_handler: uploadError,
        upload_success_handler: uploadSuccess,
        upload_complete_handler: uploadComplete,

        // Button Settings
        button_image_url: "/images/XPButtonUploadText_61x22.png",
        button_placeholder_id: "spanButtonPlaceholder_" + id,
        button_width: 61,
        button_height: 22,

        // Flash Settings
        flash_url: "/swfupload/swfupload.swf",
        
        post_params : settings, 

        custom_settings: {
            progressTarget: "fsUploadProgress_" + id,
            cancelButtonId: "btnCancel_" + id
        },

        // Debug Settings
        debug: false
    });

    return uploader;

}





