/*
 **************************************************************
 * wwHoverPanel Class
 **************************************************************
 *  Author: Rick Strahl 
 *          (c) West Wind Technologies
 *          http://www.west-wind.com/
 * 
 * Created: 1/26/2006 
 * 
 * This ASP.NET Control class provides a number of AJAX
 * functionalities for ASP.NET pages.
 * 
 * Hover Windows:
 * You can specify a URL and use the HoverWindow option
 * to automatically pop up a window at the current mouse 
 * location. Handles auto positioning, delayed display,
 * and basic display management of the panel displayed.
 * 
 * Callback Url Results:
 * Allows you to call another URL and callback to a specified
 * client script handler method. The values returned from this
 * routine are pure strings.
 * 
 * The class is accompanied by a client library contained in
 * wwScriptLibrary.js which matches the wwHoverPanel class on
 * the client. The client and server pieces communicate with
 * each other. The library also contains many utility functions.
 * 
 * Source Dependencies:
 * JSONSerializer.cs
 * wwUtils.cs
 * 
 * Acknowledgements:
 * This code borrows concepts from Jason Diamonds MyAjax.NET
 * also known as Anthem:
 * http://sourceforge.net/mailarchive/forum.php?forum=anthem-dot-net-devel
 * 
 * The original JavaScript JSON deserialization code file is based on:
 *     copyright: '(c)2005 JSON.org',
 *       license: 'http://www.crockford.com/JSON/license.html',
 * 
 * License:
 * Free without any restrictions whatsoever
 * 
 * I only ask that if you make changes that you think are useful
 * you let us know and post a message at:
 * 
 * http://www.west-wind.com/wwThreads/default.asp?forum=West+Wind+Web+Tools+for+.NET
 * 
 * Version History:
 * Please see included help file
 **************************************************************  
*/

using System;
using System.Text;
using System.Data;
using System.Globalization;

using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Drawing;
using System.Drawing.Design;
using System.Collections;

using System.Web;
using System.Collections.Specialized;
using System.Reflection;
using System.IO;


namespace Westwind.Web.Controls
{
    /// <summary>
    /// The wwHoverPanel class provides an easy to use base AJAX control that  
    /// allows quick access to server side content from a URL and embed it into the
    ///  control's content. The control provides various visual customization 
    /// aspects from shadows, transparency, the ability to drag it around and close
    ///  it as well as pop up and auto hide.
    /// 
    /// The most prominent feature of this control is to provide auto-popup 
    /// functionality that shows context sensitive content while hovering and 
    /// hiding it when moving off.
    /// 
    /// &lt;&lt;img src="images/HoverWindow.png"&gt;&gt;
    /// 
    /// For more details on how the control works, see the 
    /// <see cref="_1Q100DYWK">HoverPanel Windows</see> topic. 
    /// This control  allows calling back to explict URLs
    /// either on the same page or calling other URLs within the same site that 
    /// feed the content to be rendered in the  hover panel control.
    /// </summary>
    [ToolboxBitmap(typeof(Panel)), DefaultProperty("Url"),
    ToolboxData("<{0}:wwHoverPanel runat='server' style='width:450px;background:white;display:none;'></{0}:wwHoverPanel>")]
    public class wwHoverPanel : wwDragPanel
    {

        public wwHoverPanel()
        {
            // *** Default background color to white - 
            //     don't want transparent backgrounds on hover windows
            this.BackColor = Color.White;

            // *** Default draggable status to false
            this.Draggable = false;            
        }

        /// <summary>
        /// Internal 
        /// </summary>
        private ClientScriptProxy ClientScriptProxy;

        /// <summary>
        /// The client script event handler function called when the remote call 
        /// completes just before the result content is displayed. Allows modification 
        /// of the content and possibly blocking of the content by returning false.
        /// 
        /// For more information see the wwHoverPanel client class and its 
        /// <see cref="_1WD06AVLG">callbackHandler method</see>. The handler is  passed the result from the 
        /// callback and you can return false to stop  rendering of the hover window.
        /// <seealso>Class wwHoverPanel</seealso>
        /// </summary>
        [Description("The client script event handler function called when the remote call completes. Optional for panel operations. Receives a result string with GetHttpResponse mode is used."),
        DefaultValue(""), Category("HoverPanel")]
        public string OnCompleteClientHandler
        {
            get
            {
                return _ClientEventHandler;
            }
            set
            {
                _ClientEventHandler = value;
            }
        }
        private string _ClientEventHandler = "";


        /// <summary>
        /// The Url to hit on the server for the callback to return the result. Note: Not used when doing a MethodCallback
        /// </summary>
        [Description("The Url to hit on the server for the callback to return the result."),
        DefaultValue(""), Category("HoverPanel"),
        Editor("System.Web.UI.Design.UrlEditor", typeof(UITypeEditor))]
        public string ServerUrl
        {
            get
            {
                return _ServerUrl;
            }
            set
            {
                _ServerUrl = value;
            }
        }
        private string _ServerUrl = "";

        /// <summary>
        /// Determines if the navigation is delayed by a hesitation. Useful for link hovering.
        /// </summary>
        [Description("Determines if the navigation is delayed by a hesitation. Useful for link hovering.")]
        [DefaultValue(0), Category("HoverPanel")]
        public int NavigateDelay
        {
            get
            {
                return _NavigateDelay;
            }
            set
            {
                _NavigateDelay = value;
            }
        }
        private int _NavigateDelay = 0;

        /// <summary>
        /// Determines whether this request is a callbackDetermines whether the current
        ///  request is a callback from the wwMethodCallback or wwHoverPanel control.
        /// 
        /// This property is used internally to trap for method processing, but you can
        ///  also use this in your page or control level code to determine whether you 
        /// need to do any special processing based on the callback situation.
        /// <seealso>Class wwHoverPanel</seealso>
        /// </summary>
        [Browsable(false)]
        public bool IsCallback
        {
            get
            {
                if (!_IsCallback.HasValue)
                {
                    string Id = this.Context.Request.Params["__WWEVENTCALLBACK"];
                    if (Id != null && Id == this.ID)
                    {
                        _IsCallback = true;
                        return true;
                    }
                    _IsCallback = false;
                    return false;
                }
                else
                {
                    return _IsCallback.Value;
                }
            }
        }
        private bool? _IsCallback = null;

        /// <summary>
        /// Determines the how the event is handled  on the callback request. ShowHtmlMousePosition shows the result in a window. CallEventHandler fires the specified script function.
        /// </summary>
        [Description("Determines the how the event is handled  on the callback request. ShowHtmlMousePosition shows the result in a window. CallEventHandler fires the specified script function."),
        DefaultValue(HoverEventHandlerModes.ShowHtmlAtMousePosition), Category("HoverPanel")]
        public HoverEventHandlerModes EventHandlerMode
        {
            get
            {
                return _EventHandlerMode;
            }
            set
            {
                _EventHandlerMode = value;
            }
        }
        private HoverEventHandlerModes _EventHandlerMode = HoverEventHandlerModes.ShowHtmlAtMousePosition;

        /// <summary>
        /// The client ID of the control that receives the hoverpanel output. This affects only the HTML if empty the hoverpanel is used.
        /// </summary>
        [Description("The client ID of the control that receives the hoverpanel output. This affects only the HTML if empty the hoverpanel is used."),
         Category("HoverPanel"), DefaultValue("")]
        public string HtmlTargetClientId
        {
            get { return _HtmlTargetClientId; }
            set { _HtmlTargetClientId = value; }
        }
        private string _HtmlTargetClientId = "";


        /// <summary>
        /// if set tries to move up the window if it's too low to fit content. This setting can cause problems with very large content.
        /// </summary>
        [Description("If set tries to move up the window if it's too low to fit content. This setting can cause problems with very large content."),
       DefaultValue(false), Category("HoverPanel")]
        public bool AdjustWindowPosition
        {
            get
            {
                return _AdjustWindowPosition;
            }
            set
            {
                _AdjustWindowPosition = value;
            }
        }
        private bool _AdjustWindowPosition = false;

        /// <summary>
        /// Determines whether the window is closed automatically if you mouse off it
        /// when the window is a hover window.
        /// </summary>
        [Description("Determines whether the window is closed automatically if you mouse off it when the window is a hover window."),
         Category("HoverPanel"), DefaultValue(true)]
        public bool AutoCloseHoverWindow
        {
            get { return _AutoCloseHoverWindow; }
            set { _AutoCloseHoverWindow = value; }
        }
        private bool _AutoCloseHoverWindow = true;   

 
        /// <summary>
        /// The right offset when the the panel is shown at the mouse position
        /// </summary>
        [Description("The right offset when the the panel is shown at the mouse position"),
         Category("HoverPanel"), DefaultValue(0)]
        public int HoverOffsetRight
        {
            get { return _HoverOffsetRight; }
            set { _HoverOffsetRight = value; }
        }
        private int _HoverOffsetRight = 0;

        /// <summary>
        /// The bottom offset when the the panel is shown at the mouse position
        /// </summary>
        [Description("The bottom offset when the the panel is shown at the mouse position"),
         Category("HoverPanel"), DefaultValue(0)]
        public int HoverOffsetBottom
        {
            get { return _HoverOffsetBottom; }
            set { _HoverOffsetBottom = value; }
        }
        private int _HoverOffsetBottom = 0;

     

        /// <summary>
        /// If true causes the page to post back all form variables.
        /// </summary>
        [Description("If true causes the page to post back all form variables."), DefaultValue(PostBackModes.Get), Category("HoverPanel")]
        public PostBackModes PostBackMode
        {
            get
            {
                return _PostBackFormData;
            }
            set
            {
                _PostBackFormData = value;
            }
        }
        private PostBackModes _PostBackFormData = PostBackModes.Get;

        /// <summary>
        /// The name of the form from which values are posted back to the server. Note only a single form's 
        /// values can be posted back!
        /// </summary>
        [Description("If PostBackData is set, this variable determines which form is posted back."), DefaultValue(""), Category("HoverPanel")]
        public string PostBackFormName
        {
            get
            {
                return _PostBackFormName;
            }
            set
            {
                _PostBackFormName = value;
            }
        }
        private string _PostBackFormName = "";

        /// <summary>
        /// The height of an IFRAME if mode is IFrame related. Use this if you need to specifically size the IFRAME within the rendered panel to get the size just right.
        /// </summary>
        [Description("The height of an IFRAME if mode is IFrame related. Use this if you need to specifically size the IFRAME within the rendered panel to get the size just right."),
        DefaultValue(""), Category("HoverPanel")]
        public Unit IFrameHeight
        {
            get { return _IFrameHeight; }
            set { _IFrameHeight = value; }
        }
        private Unit _IFrameHeight = Unit.Empty;


        /// <summary>
        /// Determines how the JavasScript library gets loaded into the page.
        /// </summary>
        [Description("Determines how the JavaScript code gets emedded into the page."),
        DefaultValue(JavaScriptCodeLocationTypes.WebResource), Category("HoverPanel")]
        public JavaScriptCodeLocationTypes ScriptLocationType
        {
            get { return _ScriptLocationType; }
            set { _ScriptLocationType = value; }
        }
        private JavaScriptCodeLocationTypes _ScriptLocationType = JavaScriptCodeLocationTypes.WebResource;


        /// <summary>
        /// Determines the location of thte JavaScript support file if ScriptLocationType is set to ExternalFile
        /// </summary>
        [Description("Determines the location of thte JavaScript support file if ScriptLocationType is set to ExternalFile"),
        Editor("System.Web.UI.Design.ImageUrlEditor", typeof(UITypeEditor)),
        DefaultValue("~/wwScriptLibrary.js"), Category("HoverPanel")]
        public string ScriptLocation
        {
            get { return _ScriptLocation; }
            set { _ScriptLocation = value; }
        }
        private string _ScriptLocation = "~/wwScriptLibrary.js";


        /// <summary>
        /// Override to force simple IDs all around
        /// </summary>
        public override string UniqueID
        {
            get
            {
                return this.ID;
            }
        }
        /// <summary>
        /// Override to force simple IDs all around
        /// </summary>
        public override string ClientID
        {
            get
            {
                return this.ID;
            }
        }


        protected override void OnInit(EventArgs e)
        {
            this.ClientScriptProxy = ClientScriptProxy.Current;
            base.OnInit(e);
        }

        protected override void OnLoad(EventArgs e)
        {
            if (this.IsCallback)
            {
                // *** Force request to reload always
                HttpContext.Current.Response.Expires = -1;
                HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
            }
            base.OnLoad(e);
        }

        /// <summary>
        /// This method just builds the various JavaScript blocks as strings
        /// and assigns them to the ClientScript object.
        //
        /// MouseEvents to the panel to show/hide the panel on mouse out 
        /// operations.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPreRender(EventArgs e)
        {
            if (!this.IsCallback)
            {
                this.GenerateControlSpecificJavaScript();

                if (this.ScriptLocationType == JavaScriptCodeLocationTypes.WebResource)
                {
                    ControlResources.LoadwwScriptLibrary(this); 
                    //this.ClientScriptProxy.RegisterClientScriptResource(this, typeof(ControlResources), ControlResources.SCRIPTLIBRARY_SCRIPT_RESOURCE);
                }
                else if (this.ScriptLocationType == JavaScriptCodeLocationTypes.ExternalFile)
                {
                    this.ClientScriptProxy.RegisterClientScriptInclude(this, this.GetType(), ControlResources.SCRIPTLIBRARY_SCRIPT_RESOURCE, this.ResolveUrl(this.ScriptLocation));
                }
                else if (this.ScriptLocationType == JavaScriptCodeLocationTypes.EmbeddedInPage)
                {
                    // *** Load the wwHoverPanel.js file from Resources for embedding
                    Stream st = Assembly.GetExecutingAssembly().GetManifestResourceStream(ControlResources.SCRIPTLIBRARY_SCRIPT_RESOURCE);
                    StreamReader sr = new StreamReader(st);
                    this.ClientScriptProxy.RegisterClientScriptBlock(this, this.GetType(), ControlResources.SCRIPTLIBRARY_SCRIPT_RESOURCE, sr.ReadToEnd(), true);
                }

                // *** On HoverPanel operation we need to hide the window when we move off
                if (this.EventHandlerMode == HoverEventHandlerModes.ShowHtmlAtMousePosition)
                {
                    if (this.AutoCloseHoverWindow)
                    {
                        this.Attributes.Add("onmouseover", this.ID + ".show();");
                        this.Attributes.Add("onmouseout", this.ID + ".hide();");
                    }
                }
                else if (this.EventHandlerMode == HoverEventHandlerModes.ShowIFrameAtMousePosition ||
                         this.EventHandlerMode == HoverEventHandlerModes.ShowIFrameInPanel)
                {
                    LiteralControl Ctl = new LiteralControl();
                    Ctl.Text = "<iframe id='" + this.ClientID + "_IFrame' frameborder='0' width='" + this.Width.ToString() + "' height='" + this.IFrameHeight.ToString() + "'></iframe>";
                    this.Controls.Add(Ctl);
                }

                if (PanelOpacity != 1)
                {
                    if (this.Context.Request.Browser.ActiveXControls)
                        this.Style.Add("filter", "alpha(opacity='" + (this.PanelOpacity * 100).ToString(CultureInfo.InvariantCulture.NumberFormat) + "')");
                    else
                        this.Style.Add("opacity", this.PanelOpacity.ToString(CultureInfo.InvariantCulture.NumberFormat));
                }
            }
            base.OnPreRender(e);
        }


        /// <summary>
        /// Generates the ControlSpecific JavaScript. This script is safe to
        /// allow multiple callbacks to run simultaneously.
        /// </summary>
        private void GenerateControlSpecificJavaScript()
        {
            // *** Figure out the initial URL we're going to 
            // *** Either it's the provided URL from the control or 
            // *** we're posting back to the current page
            string Url = null;
            if (this.ServerUrl == null || this.ServerUrl == "")
                Url = this.Context.Request.Path;
            else
                Url = this.ResolveUrl(this.ServerUrl);


            //Uri ExistingUrl = this.Context.Request.Url;

            //// *** Must fix up URL into fully qualified URL for XmlHttp
            //if (!this.ServerUrl.ToLower().StartsWith("http"))
            //    Url = ExistingUrl.Scheme + "://" + ExistingUrl.Authority + Url;

            string CallbackHandler = this.OnCompleteClientHandler;
            if (CallbackHandler == "")
            {
                CallbackHandler = "null";
            }

            string StartupCode =
"function " + this.ID + @"_GetHoverPanel() {
    var Hover = new wwHoverPanel(""" + this.ID + @""",""" + Url + @""");
    Hover.callbackHandler = " + CallbackHandler + @";
    Hover.htmlTargetClientId = """ + (this.HtmlTargetClientId == "" ? this.ID : this.HtmlTargetClientId) + @""";
    Hover.postbackMode = """ + this.PostBackMode.ToString() + @""";
    Hover.navigateDelay = " + this.NavigateDelay.ToString() + @";
    Hover.adjustWindowPosition = " + this.AdjustWindowPosition.ToString().ToLower() + @";
    Hover.eventHandlerMode = """ + this.EventHandlerMode.ToString() + @""";
    Hover.shadowOpacity = " + this.ShadowOpacity.ToString(CultureInfo.InvariantCulture.NumberFormat) + @";
    Hover.shadowOffset = " + this.ShadowOffset + @";
    Hover.hoverOffsetRight = " + this.HoverOffsetRight.ToString() + @";
    Hover.hoverOffsetBottom = " + this.HoverOffsetBottom.ToString() + @";
    return Hover;
}
var " + this.ID + " = " + this.ID + @"_GetHoverPanel();
";

            this.ClientScriptProxy.RegisterStartupScript(this, this.GetType(), this.ID + "_STARTUP", StartupCode, true);
        }

        /// <summary>
        /// Returns an Event Callback reference string that can be used in Client
        /// script to initiate a callback request. 
        /// </summary>
        /// <param name="QueryStringExpression">
        /// An expression that is evaluated in script code and embedded as the second parameter.
        /// The value of this second parameter is interpreted as a QueryString to the URL that
        /// is fired in response to the request to the server.
        /// 
        /// This expression can be a static string or any value or expression that is in scope
        /// at the time of calling the event method. The expression must evaluate to a string
        ///  
        /// Example: 
        /// string GetCallbackEventReference("'CustomerId=' + this.forms[0].txtCustomerId.value + "'");
        ///  
        /// A callback event reference result looks like this:
        /// 
        /// ControlID_StartCallback(event,'CustomerId=_12312')
        /// </param>
        /// <returns></returns>
        public string GetCallbackEventReference(string QueryStringExpression)
        {
            if (QueryStringExpression == null)
                return "StartCallback('" + this.ID + "',event)";

            return "StartCallback('" + this.ID + "',event,'" + QueryStringExpression + "')";
        }


    }

    public enum HoverEventHandlerModes
    {

        /// <summary>
        /// Displays a hover window at the current mouse position. Calls a URL 
        /// specified in the ServerUrl property when the call is initiated. The call 
        /// initiation can add an additional queryString to specify 'parameters' for 
        /// the request.
        /// <seealso>Enumeration HoverEventHandlerModes</seealso>
        /// </summary>
        ShowHtmlAtMousePosition,
        /// <summary>
        /// Shows the result of the URL in the panel. Works like ShowHtmlInPanel
        /// except that the panel is not moved when the callback completes.
        /// </summary>
        ShowHtmlInPanel,
        /// <summary>
        /// Displays a URL in an IFRAME which is independent of the
        /// current page.
        /// </summary>
        ShowIFrameAtMousePosition,
        /// <summary>
        /// Shows an IFRAME in a panel
        /// </summary>
        ShowIFrameInPanel,
        /// <summary>
        /// Calls an external Page and returns the HTML result into the 
        /// ClientEventHandler specified for the control. This is a really high level 
        /// mechanism.
        /// <seealso>Enumeration HoverEventHandlerModes</seealso>
        /// </summary>        
        GetHttpResponse
    }

    public enum PostBackModes
    {
        /// No Form data is posted (but there may still be some post state)
        None,

        /// <summary>
        /// No POST data is posted back to the server
        /// </summary>
        Get,
        /// <summary>
        /// Only standard POST data is posted back - ASP.NET Post stuff left out
        /// </summary>
        Post,
        /// <summary>
        /// Posts back POST data but skips ViewState and EventTargets
        /// </summary>
        PostNoViewstate,
        /// <summary>
        /// Posts only the method parameters and nothing else
        /// </summary>
        PostMethodParametersOnly
    }

    public enum JavaScriptCodeLocationTypes
    {
        /// <summary>
        /// Causes the Javascript code to be embedded into the page on every 
        /// generation. Fully self-contained.
        /// <seealso>Enumeration JavaScriptCodeLocationTypes</seealso>
        /// </summary>
        EmbeddedInPage,
        /// <summary>
        /// Keeps the .js file as an external file in the Web application. If this is 
        /// set you should set the &lt;&lt;%= TopicLink([ScriptLocation],[_1Q01F9K4D]) 
        /// %&gt;&gt; Property to point at the location of the file.
        /// 
        /// This option requires that you deploy the .js file with your application.
        /// <seealso>Enumeration JavaScriptCodeLocationTypes</seealso>
        /// </summary>
        ExternalFile,
        /// <summary>
        /// ASP.NET 2.0 option to generate a WebResource.axd call that feeds the .js 
        /// file to the client.
        /// <seealso>Enumeration JavaScriptCodeLocationTypes</seealso>
        /// </summary>
        WebResource
    }
}
 