﻿// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Permissive License.
// See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx.
// All other rights reserved.


/// <reference name="MicrosoftAjax.debug.js" />
/// <reference name="MicrosoftAjaxTimer.debug.js" />
/// <reference name="MicrosoftAjaxWebForms.debug.js" />
/// <reference path="../ExtenderBase/BaseScripts.js" />


Type.registerNamespace('OS.TC.Web');

OS.TC.Web.RatingBehavior = function(element) {
    /// <summary>
    /// The RatingBehavior creates a sequence of stars used to rate an item
    /// </summary>
    /// <param name="element" type="Sys.UI.DomElement" domElement="true">
    /// DOM element associated with the behavior
    /// </param>
    OS.TC.Web.RatingBehavior.initializeBase(this, [element]);
    this._servicePath = null;
    this._serviceMethod = null;
    this._params = null;
    this._starCssClass = null;
    this._filledStarCssClass = null;
    this._emptyStarCssClass = null;
    this._waitingStarCssClass = null;
    this._readOnly = false;
    this._readOnlyMessage = null;
    this._ratingValue = 0;
    this._currentRating = 0;
    this._maxRatingValue = 5;
    this._averageRating = 0;
    this._tag = "";
    this._isNewRating = true;
    this._ratingDirection = 0;
    this._stars = null;
    this._mouseOutHandler = Function.createDelegate(this, this._onMouseOut);
    this._starClickHandler = Function.createDelegate(this, this._onStarClick);
    this._starMouseOverHandler = Function.createDelegate(this, this._onStarMouseOver);
    this._keyDownHandler = Function.createDelegate(this, this._onKeyDownBack);
}
OS.TC.Web.RatingBehavior.prototype = {
    initialize: function() {
        /// <summary>
        /// Initialize the behavior
        /// </summary>
        OS.TC.Web.RatingBehavior.callBaseMethod(this, 'initialize');
        var elt = this.get_element();
        this._stars = [];
        for (var i = 1; i <= this._maxRatingValue; i++) {
            starElement = $get(elt.id + '_Star_' + i);
            starElement.value = i;
            Array.add(this._stars, starElement);
            $addHandler(starElement, 'click', this._starClickHandler);
            $addHandler(starElement, 'mouseover', this._starMouseOverHandler);
        }
        $addHandler(elt, 'mouseout', this._mouseOutHandler);
        $addHandler(elt, "keydown", this._keyDownHandler);
        this._update();
    },

    dispose: function() {
        /// <summary>
        /// Dispose the behavior
        /// </summary>

        var elt = this.get_element();
        if (this._stars) {
            for (var i = 0; i < this._stars.length; i++) {
                var starElement = this._stars[i];
                $removeHandler(starElement, 'click', this._starClickHandler);
                $removeHandler(starElement, 'mouseover', this._starMouseOverHandler);
            }
            this._stars = null;
        }
        $removeHandler(elt, 'mouseout', this._mouseOutHandler);
        $removeHandler(elt, "keydown", this._keyDownHandler);
        OS.TC.Web.RatingBehavior.callBaseMethod(this, 'dispose');
    },

    get_ServicePath: function() {
        /// <value type="String" mayBeNull="true" optional="true">
        /// The URL of the web service to call.  If the ServicePath is not defined, then we will invoke a PageMethod instead of a web service.
        /// </value>
        return this._servicePath;
    },
    set_ServicePath: function(value) {
        if (this._servicePath != value) {
            this._servicePath = value;
            this.raisePropertyChanged('ServicePath');
        }
    },

    get_ServiceMethod: function() {
        /// <value type="String">
        /// The name of the method to call on the page or web service
        /// </value>
        /// <remarks>
        /// The signature of the method must exactly match the following:
        ///    [WebMethod]
        ///    string DynamicPopulateMethod(string params)
        ///    {
        ///        ...
        ///    }
        /// </remarks>
        return this._serviceMethod;
    },
    set_ServiceMethod: function(value) {
        if (this._serviceMethod != value) {
            this._serviceMethod = value;
            this.raisePropertyChanged('ServiceMethod');
        }
    },
    get_Params: function() {
        /// <value type="String">
        /// An arbitrary string value to be passed to the web method.
        /// For example, if the element to be populated is within a
        /// data-bound repeater, this could be the ID of the current row.
        /// </value>
        return this._params;
    },
    set_Params: function(value) {
        if (this._params != value) {
            this._params = value;
            this.raisePropertyChanged('Params');
        }
    },

    _onRatingUpdateError: function(webServiceError, userContext, methodName) {
        /// <summary>
        /// Callback used when the populating service fails
        /// </summary>
        /// <param name="webServiceError" type="Sys.Net.WebServiceError">
        /// Web service error
        /// </param>
        /// <param name="userContext" type="Object">
        /// The context information that was passed when the Web service method was invoked
        /// </param>        
        /// <param name="methodName" type="String">
        /// The Web service method that was invoked
        /// </param>        

        alert(String.format(OS.TC.Web.Resources.Rating_CallbackError, message));
    },

    _onRatingUpdateComplete: function(result, userContext, methodName) {
        /// <summary>
        /// Callback used when the populating service returns successfully
        /// </summary>
        /// <param name="result" type="Object" mayBeNull="">
        /// The data returned from the Web service method call
        /// </param>
        /// <param name="userContext" type="Object">
        /// The context information that was passed when the Web service method was invoked
        /// </param>        
        /// <param name="methodName" type="String">
        /// The Web service method that was invoked
        /// </param>

        this._waitingMode(false);
        this.raiseRatingUpdatedCallback();
    },

    _onMouseOut: function(e) {
        /// <summary>
        /// Handler for a star's mouseout event
        /// </summary>
        /// <param name="e" type="Sys.UI.DomEvent">
        /// Event info
        /// </param>

        if (this._readOnly) {
            return;
        }
        this._currentRating = this._ratingValue;
        this._update();
        this.raiseMouseOut(this._currentRating);
    },

    _onStarClick: function(e) {
        /// <summary>
        /// Handler for a star's click event
        /// </summary>
        /// <param name="e" type="Sys.UI.DomEvent">
        /// Event info
        /// </param>
        if (this._readOnly) {
            if (this._readOnlyMessage != null)
                alert(this._readOnlyMessage);

            return;
        }
        if (this._isNewRating || this._ratingValue != this._currentRating) {
            this.set_Rating(this._currentRating);
            this._isNewRating = false;
        }
    },

    _onStarMouseOver: function(e) {
        /// <summary>
        /// Handler for a star's mouseover event
        /// </summary>
        /// <param name="e" type="Sys.UI.DomEvent">
        /// Event info
        /// </param>
        if (this._readOnly) {
            return;
        }
        if (this._ratingDirection == 0) {
            this._currentRating = e.target.value;
        } else {
            this._currentRating = this._maxRatingValue + 1 - e.target.value;
        }
        this._update();
        this.raiseMouseOver(this._currentRating);
    },


    _onKeyDownBack: function(ev) {
        /// <summary>
        /// Handler for a star's keyDown event
        /// </summary>
        /// <param name="ev" type="Sys.UI.DomEvent">
        /// Event info
        /// </param>
        if (this._readOnly) {
            return;
        }
        var k = ev.keyCode ? ev.keyCode : ev.rawEvent.keyCode;
        if ((k == Sys.UI.Key.right) || (k == Sys.UI.Key.up)) {
            this._currentRating = Math.min(this._currentRating + 1, this._maxRatingValue);
            this.set_Rating(this._currentRating);
            ev.preventDefault();
            ev.stopPropagation();
        } else if ((k == Sys.UI.Key.left) || (k == Sys.UI.Key.down)) {
            this._currentRating = Math.max(this._currentRating - 1, 1);
            this.set_Rating(this._currentRating);
            ev.preventDefault();
            ev.stopPropagation();
        }
    },

    _waitingMode: function(activated) {
        /// <summary>
        /// Update the display to indicate whether or not we are waiting
        /// </summary>
        /// <param name="activated" type="Boolean">
        /// Whether or not we are waiting
        /// </param>

        for (var i = 0; i < this._maxRatingValue; i++) {
            var starElement;
            if (this._ratingDirection == 0) {
                starElement = this._stars[i];
            } else {
                starElement = this._stars[this._maxRatingValue - i - 1];
            }
            if (this._currentRating > i) {
                if (activated) {
                    Sys.UI.DomElement.removeCssClass(starElement, this._filledStarCssClass);
                    Sys.UI.DomElement.addCssClass(starElement, this._waitingStarCssClass);
                } else {
                    Sys.UI.DomElement.removeCssClass(starElement, this._waitingStarCssClass);
                    Sys.UI.DomElement.addCssClass(starElement, this._filledStarCssClass);
                }
            } else {
                Sys.UI.DomElement.removeCssClass(starElement, this._waitingStarCssClass);
                Sys.UI.DomElement.removeCssClass(starElement, this._filledStarCssClass);
                Sys.UI.DomElement.addCssClass(starElement, this._emptyStarCssClass);
            }
        }
    },

    _update: function() {
        /// <summary>
        /// Update the display
        /// </summary>
        // Update title attribute element        

        var elt = this.get_element();

        if (!this._readOnly)
            $get(elt.id + "_A").title = this._currentRating;
        else
            $get(elt.id + "_A").title = this._averageRating.toFixed(1);

        for (var i = 0; i < this._maxRatingValue; i++) {
            var starElement;
            if (this._ratingDirection == 0) {
                starElement = this._stars[i];
            } else {
                starElement = this._stars[this._maxRatingValue - i - 1];
            }

            if (this._currentRating > i) {
                Sys.UI.DomElement.removeCssClass(starElement, this._emptyStarCssClass);
                Sys.UI.DomElement.addCssClass(starElement, this._filledStarCssClass);
            }
            else {
                Sys.UI.DomElement.removeCssClass(starElement, this._filledStarCssClass);
                Sys.UI.DomElement.addCssClass(starElement, this._emptyStarCssClass);
            }
        }
    },

    add_Rated: function(handler) {
        /// <summary>
        /// Add a handler to the rated event
        /// </summary>
        /// <param name="handler" type="Function">
        /// Handler
        /// </param>
        this.get_events().addHandler("Rated", handler);
    },
    remove_Rated: function(handler) {
        /// <summary>
        /// Remove a handler from the rated event
        /// </summary>
        /// <param name="handler" type="Function">
        /// Handler
        /// </param>
        this.get_events().removeHandler("Rated", handler);
    },
    raiseRated: function(rating) {
        /// <summary>
        /// Raise the rated event
        /// </summary>
        /// <param name="rating" type="Number" integer="true">
        /// Rating
        /// </param>
        var handler = this.get_events().getHandler("Rated");
        if (handler) {
            handler(this, new OS.TC.Web.RatingEventArgs(rating));
        }
    },

    add_MouseOver: function(handler) {
        /// <summary>
        /// Add a handler to the MouseMove event
        /// </summary>
        /// <param name="handler" type="Function">
        /// Handler
        /// </param>
        this.get_events().addHandler("MouseOver", handler);
    },
    remove_MouseOver: function(handler) {
        /// <summary>
        /// Remove a handler from the MouseOver event
        /// </summary>
        /// <param name="handler" type="Function">
        /// Handler
        /// </param>
        this.get_events().removeHandler("MouseOver", handler);
    },
    raiseMouseOver: function(rating_tmp) {
        /// <summary>
        /// Raise the MouseOver event
        /// </summary>
        /// <param name="eventArgs" type="">
        /// eventArgs
        /// </param>
        var handler = this.get_events().getHandler("MouseOver");
        if (handler) {
            handler(this, new OS.TC.Web.RatingEventArgs(rating_tmp));
        }
    },

    add_MouseOut: function(handler) {
        /// <summary>
        /// Add a handler to the MouseOut event
        /// </summary>
        /// <param name="handler" type="Function">
        /// Handler
        /// </param>
        this.get_events().addHandler("MouseOut", handler);
    },
    remove_MouseOut: function(handler) {
        /// <summary>
        /// Remove a handler from the MouseOut event
        /// </summary>
        /// <param name="handler" type="Function">
        /// Handler
        /// </param>
        this.get_events().removeHandler("MouseOut", handler);
    },
    raiseMouseOut: function(rating_old) {
        /// <summary>
        /// Raise the MouseOut event
        /// </summary>
        /// <param name="eventArgs" type="">
        /// eventArgs
        /// </param>
        var handler = this.get_events().getHandler("MouseOut");
        if (handler) {
            handler(this, new OS.TC.Web.RatingEventArgs(rating_old));
        }
    },

    add_RatingUpdatedCallback: function(handler) {
        /// <summary>
        /// Add a handler to the EndClientCallback event
        /// </summary>
        /// <param name="handler" type="Function">
        /// Handler
        /// </param>
        this.get_events().addHandler("RatingUpdatedCallback", handler);
    },
    remove_RatingUpdatedCallback: function(handler) {
        /// <summary>
        /// Remove a handler from the EndClientCallback event
        /// </summary>
        /// <param name="handler" type="Function">
        /// Handler
        /// </param>
        this.get_events().removeHandler("RatingUpdatedCallback", handler);
    },
    raiseRatingUpdatedCallback: function(result) {
        /// <summary>
        /// Raise the EndClientCallback event
        /// </summary>
        /// <param name="result" type="String">
        /// Callback result
        /// </param>
        var handler = this.get_events().getHandler("RatingUpdatedCallback");
        if (handler) {
            handler(this, Sys.EventArgs.Empty);
        }
    },
    get_Stars: function() {
        /// <value type="Array" elementType="Sys.UI.DomElement" elementDomElement="true">
        /// Elements for the displayed stars
        /// </value>
        return this._stars;
    },

    get_Tag: function() {
        /// <value type="String">
        /// A custom parameter to pass to the ClientCallBack
        /// </value>
        return this._tag;
    },
    set_Tag: function(value) {
        if (this._tag != value) {
            this._tag = value;
            this.raisePropertyChanged('Tag');
        }
    },

    get_RatingDirection: function() {
        /// <value type="Number" integer="true">
        /// RatingDirection - Orientation of the stars (LeftToRightTopToBottom or RightToLeftBottomToTop)
        /// </value>
        /// TODO: We should create an enum for this
        return this._ratingDirection;
    },
    set_RatingDirection: function(value) {
        if (this._ratingDirection != value) {
            this._ratingDirection = value;
            if (this.get_isInitialized()) {
                this._update();
            }
            this.raisePropertyChanged('RatingDirection');
        }
    },

    get_EmptyStarCssClass: function() {
        /// <value type="String">
        /// CSS class for a star in empty mode
        /// </value>
        return this._emptyStarCssClass;
    },
    set_EmptyStarCssClass: function(value) {
        if (this._emptyStarCssClass != value) {
            this._emptyStarCssClass = value;
            this.raisePropertyChanged('EmptyStarCssClass');
        }
    },

    get_FilledStarCssClass: function() {
        /// <value type="String">
        /// CSS class for star in filled mode
        /// </value>
        return this._filledStarCssClass;
    },
    set_FilledStarCssClass: function(value) {
        if (this._filledStarCssClass != value) {
            this._filledStarCssClass = value;
            this.raisePropertyChanged('FilledStarCssClass');
        }
    },

    get_WaitingStarCssClass: function() {
        /// <value type="String">
        /// CSS class for a star in waiting mode
        /// </value>
        return this._waitingStarCssClass;
    },
    set_WaitingStarCssClass: function(value) {
        if (this._waitingStarCssClass != value) {
            this._waitingStarCssClass = value;
            this.raisePropertyChanged('WaitingStarCssClass');
        }
    },

    get_Rating: function() {
        /// <value type="Number" integer="true">
        /// Current rating value
        /// </value>
        this._ratingValue = OS.TC.Web.RatingBehavior.callBaseMethod(this, 'get_ClientState');
        if (this._ratingValue == '')
            this._ratingValue = null;
        return this._ratingValue;
    },
    set_Rating: function(value) {
        if (this._isNewRating || this._ratingValue != value) {
            this._ratingValue = value;
            this._currentRating = value;
            if (this.get_isInitialized()) {
                if ((value < 0) || (value > this._maxRatingValue)) {
                    return;
                }

                this._update();

                OS.TC.Web.RatingBehavior.callBaseMethod(this, 'set_ClientState', [this._ratingValue]);
                this.raisePropertyChanged('Rating');
                this.raiseRated(this._currentRating);

                if (this._servicePath == null || this._servicePath.trim() == "")
                    Error.invalidOperation("RatingBehavior.ServicePath cannot be empty.");

                if (this._serviceMethod == null || this._serviceMethod.trim() == "")
                    Error.invalidOperation("RatingBehavior.ServiceMethod cannot be empty.");

                this._waitingMode(true);

                var serviceParams = { rating: this._currentRating };

                for (var param in this._params)
                    serviceParams[param] = this._params[param];

                Sys.Net.WebServiceProxy.invoke(this._servicePath, this._serviceMethod, true,
                                              serviceParams,
                                              Function.createDelegate(this, this._onRatingUpdateComplete),
                                              Function.createDelegate(this, this._onRatingUpdateError));
            }
        }
    },

    get_MaxRating: function() {
        /// <value type="Number" integer="true">
        /// Maximum rating value
        /// </value>
        return this._maxRatingValue;
    },
    set_MaxRating: function(value) {
        if (this._maxRatingValue != value) {
            this._maxRatingValue = value;
            this.raisePropertyChanged('MaxRating');
        }
    },

    get_AverageRating: function() {
        return this._averageRating;
    },
    set_AverageRating: function(value) {
        this._averageRating = value;
    },


    get_ReadOnly: function() {
        /// <value type="Boolean">
        /// Whether or not the rating can be changed
        /// </value>
        return this._readOnly;
    },
    set_ReadOnly: function(value) {
        if (this._readOnly != value) {
            this._readOnly = value;
            this.raisePropertyChanged('ReadOnly');
        }
    },

    get_ReadOnlyMessage: function() {
        /// <value type="string">
        /// Whether or not the rating can be changed
        /// </value>
        return this._readOnlyMessage;
    },
    set_ReadOnlyMessage: function(value) {
        if (this._readOnlyMessage != value) {
            this._readOnlyMessage = value;
            this.raisePropertyChanged('ReadOnlyMessage');
        }
    },

    get_StarCssClass: function() {
        /// <value type="String">
        /// CSS class for a visible star
        /// </value>
        return this._starCssClass;
    },
    set_StarCssClass: function(value) {
        if (this._starCssClass != value) {
            this._starCssClass = value;
            this.raisePropertyChanged('StarCssClass');
        }
    }
}
OS.TC.Web.RatingBehavior.registerClass('OS.TC.Web.RatingBehavior', AjaxControlToolkit.BehaviorBase);


OS.TC.Web.RatingEventArgs = function(rating) {
    /// <summary>
    /// Event arguments for the RatingBehavior's rated event
    /// </summary>
    /// <param name="rating" type="Number" integer="true">
    /// Rating
    /// </param>
    OS.TC.Web.RatingEventArgs.initializeBase(this);

    this._rating = rating;
}
OS.TC.Web.RatingEventArgs.prototype = {
    get_Rating : function() {
        /// <value type="Number" integer="true">
        /// Rating
        /// </value>
        return this._rating;
    }
}
OS.TC.Web.RatingEventArgs.registerClass('OS.TC.Web.RatingEventArgs', Sys.EventArgs);
if(typeof(Sys)!=='undefined')Sys.Application.notifyScriptLoaded();