﻿/// <reference name="MicrosoftAjax.js"/>
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  Utils.js
*
*      General utilities for User Portal
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.Utils = function() { }

UserPCSite.Utils.getDetailsURL = function(appId) {
    /// <summary>
    ///     generate details page url
    /// </summary>
    /// <param name="appId">appId</param>
    /// <returns>
    ///    details page url 
    /// </returns>

    return "/details.aspx?appId=" + appId + "&retURL=" + escape(window.location.pathname) + escape(window.location.search);
}

/// <reference name="MicrosoftAjax.js"/>

function ApplicationSearchBox_onFocus(searchBox, defaultSearchText) {
    if (defaultSearchText == searchBox.value) {
        searchBox.value = "";
        Sys.UI.DomElement.addCssClass(searchBox, "ApplicationSearchBox_textbox_fontCustom");
        Sys.UI.DomElement.removeCssClass(searchBox, "ApplicationSearchBox_textbox_fontDefault");
    }
}

function ApplicationSearchBox_onBlur(searchBox, defaultSearchText) {
    if ("" == searchBox.value) {
        searchBox.value = defaultSearchText;
        Sys.UI.DomElement.addCssClass(searchBox, "ApplicationSearchBox_textbox_fontDefault");
        Sys.UI.DomElement.removeCssClass(searchBox, "ApplicationSearchBox_textbox_fontCustom");
    }
}

function ApplicationSearchBox_Search(id, defaultSearchText) {
    searchBox = $get(id);
    if (defaultSearchText != searchBox.value) {
        window.location.href = "../../search.aspx?keywords=" + encodeURIComponent(searchBox.value);
        
        // omniture tracking
        SiteCommon.Utils.OmnitureTrackSearch(searchBox.value);
        return false;
    }
}
/// <reference name="MicrosoftAjax.js"/>
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  IController.js
*
*      Declares the initial call pattern for controllers.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("SiteCommon");

SiteCommon.IController = function() { }

SiteCommon.IController.prototype = {
    initializeRequest: function() {
        /// <summary>
        ///     initializeRequest is called to initialize the ajax data request needed by
        ///     this controller, as well as any additional requirements.
        /// </summary>
    }
}
SiteCommon.IController.registerInterface('SiteCommon.IController');


/// <reference name="MicrosoftAjax.js"/>
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  IView.js
*
*      Declares the general view abstract routines.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

SiteCommon.IView = function(element) {
    SiteCommon.IView.initializeBase(this, [element]);
}

SiteCommon.IView.prototype = {
    initialize: function() {
        /// <summary>
        ///     initialize the main control, and setup the static UI content.
        /// </summary>
    },
    _setupRender: function() {
        /// <summary>
        ///     Sets up the dom element structure which doesn't require queried data.
        /// </summary>
    },
    dataRender: function() {
        /// <summary>
        ///     Update the UI with main data components from controller.
        /// </summary>
    }
}

SiteCommon.IView.registerInterface('SiteCommon.IView');


/// <reference name="MicrosoftAjax.js"/>
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  IThumbnailData.js
*
*      Declares the methods for thumbnail data.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.IThumbnailData = function() {}

UserPCSite.IThumbnailData.prototype = {
    getThumbnailName: function(dataContext) {
        /// <summary>
        ///     Get the name of a thumbnail.
        /// </summary>
        /// <param name="dataContext">DataContext required to return the thumbnail data.</param>
        /// <returns>Name value of the thumbnail.</returns>
    },
    getThumbnailNameUI: function(dataContext) {
        /// <summary>
        ///     Get the name of a thumbnail.
        /// </summary>
        /// <param name="dataContext">DataContext required to return the thumbnail data.</param>
        /// <returns>Name value of the thumbnail.</returns>
    },
    getThumbnailImagePath: function(dataContext) {
        /// <summary>
        ///     Get the image url of a thumbnail.
        /// </summary>
        /// <param name="dataContext">DataContext required to return the thumbnail data.</param>
        /// <returns>ImagePath value of the thumbnail.</returns>
    },
    getThumbnailRating: function(dataContext) {
        /// <summary>
        ///     Get the rating of a thumbnail.
        /// </summary>
        /// <param name="dataContext">DataContext required to return the thumbnail data.</param>
        /// <returns>Rating value of the thumbnail.</returns>
    },
    getThumbnailReviewCount: function(dataContext) {
        /// <summary>
        ///     Get the ReviewCount
        /// </summary>
        /// <param name="dataContext">dataContext of the referenced thumbnail</param>
        /// <returns>ReviewCount value.</returns>    
    },
    getThumbnailReviewCountUI: function(dataContext) {
        /// <summary>
        ///     Get the ReviewCount of a thumbnail.
        /// </summary>
        /// <param name="dataContext">DataContext required to return the thumbnail data.</param>
        /// <returns>ReviewCount value of the thumbnail.</returns>
    },
    getThumbnailCompanyNameUI: function(dataContext) {
        /// <summary>
        ///     Get the Company Name of a thumbnail.
        /// </summary>
        /// <param name="dataContext">DataContext required to return the thumbnail data.</param>
        /// <returns>Company Name value of the thumbnail.</returns>
    },
    getThumbnailCategory: function(dataContext) {
        /// <summary>
        ///     Get the Category name.
        /// </summary>
        /// <param name="dataContext">dataContext of the referenced thumbnail</param>
        /// <returns>Category name string.</returns>
    },
    getThumbnailCategoryUI: function(dataContext) {
        /// <summary>
        ///     Get the Category of a thumbnail.
        /// </summary>
        /// <param name="dataContext">DataContext required to return the thumbnail data.</param>
        /// <returns>Category value of the thumbnail.</returns>
    },
    getThumbnailDescriptionUI: function(dataContext) {
        /// <summary>
        ///     Get the Description of a thumbnail.
        /// </summary>
        /// <param name="dataContext">DataContext required to return the thumbnail data.</param>
        /// <returns>Description value of the thumbnail.</returns>
    },
    getThumbnailPrice: function(dataContext) {
        /// <summary>
        ///     Get the price of a thumbnail.
        /// </summary>
        /// <param name="dataContext">DataContext required to return the thumbnail data.</param>
        /// <returns>Price value of the thumbnail.</returns>
    },
    getThumbnailPriceUI: function(dataContext) {
        /// <summary>
        ///     Get the price of a thumbnail.
        /// </summary>
        /// <param name="dataContext">DataContext required to return the thumbnail data.</param>
        /// <returns>Price value of the thumbnail.</returns>
    },
    getPlatformTypeByID: function(dataContext) {
        /// <summary>
        ///     Get the Platform Type by ID.
        ///     This is used when data return a list
        /// </summary>
        /// <param name="dataContext">DataContext required to return the thumbnail data.</param>
        /// <returns>Platform Type ID</returns>    
    },
    getIsFeatured: function(dataContext) {
        /// <summary>
        ///     Get if the app is featured app.
        /// </summary>
        /// <param name="dataContext">DataContext required to return the thumbnail data.</param>
        /// <returns>Boolean isFeatured</returns>
    }
}
UserPCSite.IThumbnailData.registerInterface('UserPCSite.IThumbnailData');


/// <reference name="MicrosoftAjax.js"/>
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  InteractiveBaseView.js
*
*      Declares common view behaviour.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("SiteCommon");

SiteCommon.InteractiveBaseView = function(element) {
    /// <summary>
    ///     Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <param name="element">HTML DomElement to associate.</param>
    /// <returns>
    ///     A newly created object.
    /// </returns>
    SiteCommon.InteractiveBaseView.initializeBase(this, [element]);

    this._clickDelegate = null;
    this._hoverDelegate = null;
    this._unhoverDelegate = null;
    this._controller = null;
    this._eventTargets = new Array();
    for (eventType in SiteCommon.InteractiveBaseView.eventTypes) {
        this._eventTargets[this._eventTargets.length] = new Array();
    }
}

SiteCommon.InteractiveBaseView.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///     
        ///     Initializes the view.
        /// </summary>

        if (SiteCommon.Utils.isNullOrUndefined(this.get_id())) {
            throw "ID must be defined during $create.";
        }

        var element = this.get_element();

        // Setup the click event
        if (this._clickDelegate === null) {
            this._clickDelegate = Function.createDelegate(this, this._clickHandler);
        }
        Sys.UI.DomEvent.addHandler(element, 'click', this._clickDelegate);

        // Setup the hover event
        if (this._hoverDelegate === null) {
            this._hoverDelegate = Function.createDelegate(this, this._hoverHandler);
        }
        Sys.UI.DomEvent.addHandler(element, 'mouseover', this._hoverDelegate);
        Sys.UI.DomEvent.addHandler(element, 'focus', this._hoverDelegate);

        // Setup the unhover event
        if (this._unhoverDelegate === null) {
            this._unhoverDelegate = Function.createDelegate(this, this._unhoverHandler);
        }
        Sys.UI.DomEvent.addHandler(element, 'mouseout', this._unhoverDelegate);
        Sys.UI.DomEvent.addHandler(element, 'blur', this._unhoverDelegate);

        // Initialize the base class
        SiteCommon.InteractiveBaseView.callBaseMethod(this, 'initialize');

        // Call render on the subclass
        this._setupRender();
    },
    add_click: function(handler) {
        /// <summary>
        ///     Adds a click event handler.
        /// </summary>
        /// <param name="handler">Reference to the handler to call.</param>
        this.get_events().addHandler('click', handler);
    },
    remove_click: function(handler) {
        /// <summary>
        ///     Removes a click event handler.
        /// </summary>
        /// <param name="handler">Reference to the handler.</param>
        this.get_events().removeHandler('click', handler);
    },
    add_hover: function(handler) {
        /// <summary>
        ///     Adds a hover event handler.
        /// </summary>
        /// <param name="handler">Reference to the handler to call.</param>
        this.get_events().addHandler('hover', handler);
    },
    remove_hover: function(handler) {
        /// <summary>
        ///     Removes a hover event handler.
        /// </summary>
        /// <param name="handler">Reference to the handler.</param>
        this.get_events().removeHandler('hover', handler);
    },
    add_unhover: function(handler) {
        /// <summary>
        ///     Adds an unhover event handler.
        /// </summary>
        /// <param name="handler">Reference to the handler to call.</param>
        this.get_events().addHandler('unhover', handler);
    },
    remove_unhover: function(handler) {
        /// <summary>
        ///     Removes an unhover event handler.
        /// </summary>
        /// <param name="handler">Reference to the handler.</param>
        this.get_events().removeHandler('unhover', handler);
    },
    get_controller: function() {
        /// <summary>
        ///     Get a reference to the controller.
        /// </summary>
        return this._controller;
    },
    set_controller: function(controller) {
        /// <summary>
        ///     Sets a reference to the controller to use.
        /// </summary>
        /// <param name="controller">Reference to the controller.</param>
        this._controller = controller;
    },
    addEventTarget: function(eventType, targetID, contextData) {
        /// <summary>
        ///     Adds an event target using the provided ID and the given contextData, which will be
        ///     returned as 'args' to the handler when the event is fired.
        /// </summary>
        /// <param name="eventType">A SiteCommon.InteractiveBaseView.eventTypes value referring to the event type.</param>
        /// <param name="targetID">The string targetID of the sub-element to catch.</param>
        /// <param name="contextData">The context data to return to the handler as args.</param>
        this._eventTargets[eventType][targetID] = contextData;
    },
    removeEventTarget: function(eventType, targetID) {
        /// <summary>
        ///     Removes an event target using the provided ID
        /// </summary>
        /// <param name="eventType">A SiteCommon.InteractiveBaseView.eventTypes value referring to the event type.</param>
        /// <param name="targetID">The string targetID of the sub-element to catch.</param>
        this._eventTargets[eventType][targetID] = undefined;
    },
    dispose: function() {
        /// <summary>
        ///     Called to dispose of this objects resources.
        /// </summary>

        var element = this.get_element();

        // Remove the click handler
        if (this._clickDelegate) {
            Sys.UI.DomEvent.removeHandler(element, 'click', this._clickDelegate);
            delete this._clickDelegate;
        }

        // Remove the hover handler
        if (this._hoverDelegate) {
            Sys.UI.DomEvent.removeHandler(element, 'focus', this._hoverDelegate);
            Sys.UI.DomEvent.removeHandler(element, 'mouseover', this._hoverDelegate);
            delete this._hoverDelegate;
        }

        // Remove the unhover handler
        if (this._unhoverDelegate) {
            Sys.UI.DomEvent.removeHandler(element, 'blur', this._unhoverDelegate);
            Sys.UI.DomEvent.removeHandler(element, 'mouseout', this._unhoverDelegate);
            delete this._unhoverDelegate;
        }

        SiteCommon.InteractiveBaseView.callBaseMethod(this, 'dispose');
    },
    _clickHandler: function(event) {
        /// <summary>
        ///     Resends the click event to the handler.
        /// </summary>
        /// <param name="event">Event reference.</param>
        this.launchEventHandler('click', SiteCommon.InteractiveBaseView.eventTypes.click, event);
    },
    _hoverHandler: function(event) {
        /// <summary>
        ///     Resends the click event to the handler.
        /// </summary>
        /// <param name="event">Event reference.</param>
        this.launchEventHandler('hover', SiteCommon.InteractiveBaseView.eventTypes.hover, event);
    },
    _unhoverHandler: function(event) {
        /// <summary>
        ///     Resends the click event to the handler.
        /// </summary>
        /// <param name="event">Event reference.</param>
        this.launchEventHandler('unhover', SiteCommon.InteractiveBaseView.eventTypes.unhover, event);
    },
    launchEventHandler: function(eventName, eventType, event) {
        /// <summary>
        ///     Adds any required data (EventHandlerArgs) to the event and passes it on to the
        ///     handler, if one was registered.
        /// </summary>
        /// <param name="eventName">Name of the event raised.</param>
        /// <param name="eventType">A SiteCommon.InteractiveBaseView.eventTypes value referring to the event type.</param>
        /// <param name="event">The event object fired by the system, containing the target element.</param>
        var handler = this.get_events().getHandler(eventName);
        if (handler) {
            handler(this, this.getEventHandlerArg(eventType, event));
        }
    },
    getEventHandlerArg: function(eventType, eventObj) {
        /// <summary>
        ///     Checks if a contextData element exists for the provided eventType and eventObj's target ID.
        /// </summary>
        /// <param name="eventType">A SiteCommon.InteractiveBaseView.eventTypes value referring to the event type.</param>
        /// <param name="eventObj">The event object fired by the system, containing the target element.</param>

        if (!SiteCommon.Utils.isNullOrUndefined(eventObj)
            && !SiteCommon.Utils.isNullOrUndefined(eventObj.target)
            && !SiteCommon.Utils.isNullOrUndefined(eventObj.target.id)) {
            eventObj.customData = this._eventTargets[eventType][eventObj.target.id];
        }
        return eventObj;
    }
}
SiteCommon.InteractiveBaseView.eventTypes = { 'click': 0, 'hover': 1, 'unhover': 2 };
/// <field name="eventType">A static list of possible event handler types.</field>

SiteCommon.InteractiveBaseView.registerClass('SiteCommon.InteractiveBaseView', Sys.UI.Control, SiteCommon.IView);


/// <reference name="MicrosoftAjax.js"/>
/// <reference path="IPurchaseData.js"/>
/// <reference path="InteractiveBaseView.js" />
/// <reference path="IController.js" />
/// <reference path="~/Services/Commerce/Commerce.svc" />
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  PurchaseData.js
*
*      Processes the Purchase data, abstracting it from the UI.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.PurchaseData = function() {
    /// <summary>
    ///     Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <returns>
    ///     A newly created PurchaseData object.
    /// </returns>
    UserPCSite.PurchaseData.initializeBase(this);
}

UserPCSite.PurchaseData.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///
        ///     Initializes the object.
        /// </summary>
        UserPCSite.PurchaseData.callBaseMethod(this, 'initialize');
    },
    getLength: function() {
        /// <summary>
        ///     Gets the number of credit cards currently acquired.
        /// </summary>
        /// <returns>Count of items acquired.</returns>
        var length = 0;
        if (!((this._json == null) && (this._json.Entity.CreditCards == null))) {
            length = this._json.Entity.CreditCards.NumItems;
        }
        return length;
    },
    getAccountId: function() {
        /// <summary>
        ///     Get the account ID for the current user.
        /// </summary>
        /// <returns>AccountId of current user.</returns>
        return this._json.Entity.AccountId;
    },
    getDefaultPaymentInstrumentId: function() {
        /// <summary>
        ///     Get the default means of payment.
        /// </summary>
        /// <returns>The id of the payment method.</returns>

        return this._getDefaultPaymentItem().PaymentInstrumentId;
    },
    getDefaultLastFourDigits: function() {
        /// <summary>
        ///     Get the last four digits of the default credit card
        /// </summary>
        /// <returns>last four digits of credit card number</returns>

        return this._getDefaultPaymentItem().LastFourDigits;
    },
    getPaymentInstrumentCount: function() {
        if (!SiteCommon.Utils.isNullOrUndefined(this._json.Entity.CreditCards)) {
            return this._json.Entity.CreditCards.NumItems;
        }
        else {
            return 0;
        }
    },
    _getDefaultPaymentItem: function() {
        /// <summary>
        ///     Get the item that is the default (or the first item if no item is default).
        /// </summary>
        /// <returns>Default Purchase Item.</returns>
        var value = null;
        try {
            value = this._json.Entity.CreditCards.List[0];
            for (var i = 0; i < this.getPaymentInstrumentCount(); i++) {
                if (this._json.Entity.CreditCards.List[i].IsDefault) {
                    value = this._json.Entity.CreditCards.List[i];
                    break;
                }
            }
        }
        catch (exception) {
            throw ("No Credit Cards available.");
        }
        return value;
    },
    _getPaymentItem: function(index) {
        /// <summary>
        ///     Get the item referenced by index.
        /// </summary>
        /// <param name="index">Index of the referenced Purchase</param>
        /// <returns>Purchase Item at the specified index.</returns>
        var value = null;
        try {
            value = this._json.Entity.CreditCards.List[index];
        }
        catch (exception) {
            throw ("Purchase Data Index is Out of Range.");
        }
        return value;
    },
    dispose: function() {
        /// <summary>
        ///     Dispose of any local resources.
        /// </summary>
        UserPCSite.PurchaseData.callBaseMethod(this, 'dispose');
    }
}

UserPCSite.PurchaseData.prototype.get_json = function() { return this._json; };
UserPCSite.PurchaseData.prototype.set_json = function(json) { this._json = json; }
/// <field name="json">The source data element to use.</field>

UserPCSite.PurchaseData.registerClass('UserPCSite.PurchaseData', Sys.Component);

/*********************************************************************************************
*
*  PurchaseView.js
*
*      Displays a purchase button, and contains the logic to control it.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.PurchaseView = function(element) {
    /// <summary>
    ///     Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <param name="element">HTML DomElement to associate.</param>
    /// <returns>
    ///     A newly created object.
    /// </returns>
    UserPCSite.PurchaseView.initializeBase(this, [element]);

    this.purchaseState = UserPCSite.PurchaseView.purchaseStates.buy;
}

UserPCSite.PurchaseView.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///
        ///     Initializes the view.
        /// </summary>

        this._Loc = SiteCommon.Utils.JsonParser(this.locDict);

        UserPCSite.PurchaseView.callBaseMethod(this, 'initialize');

        // Setup default click handler.
        this._handlingClick = false;
        this.add_click(this._onClick);
        Sys.UI.DomElement.addCssClass(this.get_element(), 'CursorHand');

        if (!SiteCommon.Utils.isNullOrUndefined(UserPCSite.PurchasedAppController)
            && !SiteCommon.Utils.isNullOrUndefined(UserPCSite.PurchasedAppController.get_instance)) {

            UserPCSite.PurchasedAppController.get_instance().registerForNotifications(this.updatePurchaseState, this);
        }
    },
    dispose: function() {
        //Add custom dispose actions here
        UserPCSite.PurchaseView.callBaseMethod(this, 'dispose');
    },
    _setupRender: function() {
        /// <summary>
        ///     Sets up the panel dom element.
        /// </summary>

        // Set the css style for the wrapping div element
        Sys.UI.DomElement.addCssClass(this.get_element(), 'PurchaseView_base PurchaseView_' + this.getSizeText());

        this._buttonText = document.createTextNode('');

        this._spanRight = SiteCommon.Utils.CreateButton(this._buttonText, this.get_id() + '_PurchaseBuy', 'WhiteButton', '');

        this.get_element().appendChild(this._spanRight);

        this._setState(this.purchaseState);

        //if buy=true and we are on app details or premium offer page, trigger the buy button.
        if (SiteCommon.Utils.QueryString('buy') == 'true' &&
            (window.location.href.indexOf('/details.aspx') != -1 ||
             window.location.href.indexOf('/premiumoffer.aspx') != -1)) 
        {
            this._onClick(this, null);
        }
    },
    updatePurchaseState: function(context) {
        ///<summary>
        ///Checks if the app has been purchased, and updates the purchase
        ///state of the buy button.
        /// don't set premium offers as purchased since they are multiple purchase enabled
        ///</summary>

        Sys.Debug.assert(context != null);
        Sys.Debug.assert(typeof context._setState == 'function');
        if (!SiteCommon.Utils.isNullOrUndefined(UserPCSite.PurchasedAppController)
                && !SiteCommon.Utils.isNullOrUndefined(UserPCSite.PurchasedAppController.get_instance)
                && window.location.href.indexOf('/premiumoffer.aspx') == -1) {
            //if already purchased, set the state as purchased
            if (UserPCSite.PurchasedAppController.get_instance().isPurchased(context.AppSKU)
                && !context._handlingClick) {
                context._setState(UserPCSite.PurchaseView.purchaseStates.purchased);
            }
        }
    },
    dataRender: function() {
        /* defined for the interface */
    },
    // jdavin: the isLargeSize thing is unused/dead code. there is no CSS rule for PurchaseView_large. Cleanup. TODO
    getSizeText: function() {
        /// <summary>
        ///     Determines the size of the button to use, and gets a string representation of it.
        /// </summary>
        /// <returns>
        ///     The resulting string representation of the size.
        /// </returns>
        return (this.isLargeSize) ? 'large' : 'small';
    },
    _onClick: function(sender, args) {
        /// <summary>
        ///     Click event handler for the subcategoryname.
        /// </summary>
        /// <param name="sender">CategoryListView object that is hovered.</param>
        /// <param name="args">Event args.</param>

        if ((!sender._handlingClick)
            && ((sender.purchaseState == UserPCSite.PurchaseView.purchaseStates.buy)
                || (sender.purchaseState == UserPCSite.PurchaseView.purchaseStates.confirmdialog)
                || (sender.purchaseState == UserPCSite.PurchaseView.purchaseStates.confirm))) {
            // Disable the onclick behaviour for the button, and adjust the opacity
            sender._handlingClick = true;
            SiteCommon.Utils.setOpacity(sender.get_element(), 5);

            // Notify the PurchaseController to act.
            var purchaseController = UserPCSite.PurchaseController.get_instance();
            purchaseController.purchaseButtonClicked(sender);
        }

        //args are null, when our code triggers this function
        if (args != null) {
            // Mark the event as handled to avoid bubbling/cascading.
            SiteCommon.Utils.markEventHandled(args);
        }

    },
    _onPurchaseCallback: function(isSuccess) {
        /// <summary>
        ///     Called from the PurchaseController when a purchase update has been performed.  This will
        ///     re-enable any button UI or actions.
        /// </summary>
        /// <param name="isSuccess">Indicates whether the action was performed successfully.</param>

        // If successful then move the state forward.
        if (isSuccess) {
            switch (this.purchaseState) {
                case UserPCSite.PurchaseView.purchaseStates.buy:
                    {
                        // show purchase confirmation dialog for paid apps, unless the user has
                        // already asked to not be shown the dialog again
                        var isFreeApp = false;
                        if (this.ThumbnailPrice == 0) {
                            isFreeApp = true;
                        }

                        if (SiteCommon.Utils.getShowPurchaseConfirmation() && !isFreeApp) {
                            this._setState(UserPCSite.PurchaseView.purchaseStates.confirmdialog);
                        }
                        else {
                            this._setState(UserPCSite.PurchaseView.purchaseStates.confirm);
                        }

                        break;
                    }
                case UserPCSite.PurchaseView.purchaseStates.confirm:
                case UserPCSite.PurchaseView.purchaseStates.confirmdialog:
                    {
                        this._setState(UserPCSite.PurchaseView.purchaseStates.purchased);
                        break;
                    }
            }
        }
        else {
            // On any errors revert the transaction.
            this._setState(UserPCSite.PurchaseView.purchaseStates.buy);
        }

        // Reset the opacity and activate the button.
        SiteCommon.Utils.setOpacity(this.get_element(), 10)
        this._handlingClick = false;
    },
    _setState: function(newState) {
        /// <summary>
        ///     Changes the item's state to the provided one.  This involves replacing the
        ///     current style, as well as the button text contents.
        /// </summary>
        /// <param name="newState">The new button display state to use.</param>

        this.get_element().removeChild(this._spanRight);

        this.purchaseState = newState;
        var newText = "";
        switch (newState) {
            case UserPCSite.PurchaseView.purchaseStates.buy:
                {
                    if (0 != this.ThumbnailPrice) {
                        newText = this._Loc.Buy;
                    }
                    else {
                        newText = this._Loc.SendToPhone;
                    }
                    this._spanRight = SiteCommon.Utils.CreateButton(newText, this.get_id() + '_PurchaseBuy', 'WhiteButton', '');
                    break;
                }
            case UserPCSite.PurchaseView.purchaseStates.confirmdialog:
                {
                    // show purchase confirmation dialog
                    var purchaseController = UserPCSite.PurchaseController.get_instance();
                    purchaseController._popupPurchaseConfirmation();
                    break;
                }
            case UserPCSite.PurchaseView.purchaseStates.confirm:
                {
                    newText = this._Loc.Confirm;
                    this._spanRight = SiteCommon.Utils.CreateButton(newText, this.get_id() + '_PurchaseBuy', 'GreenButton', '');
                    break;
                }
            case UserPCSite.PurchaseView.purchaseStates.purchased:
                {
                    newText = this._Loc.Purchased;
                    this._spanRight = SiteCommon.Utils.CreateSpan(null, this.get_id() + '_PurchaseBuy', 'PurchaseView_PurchasedState', SiteCommon.Utils.toUIString(newText));
                    Sys.UI.DomElement.removeCssClass(this.get_element(), 'CursorHand');
                    break;
                }
        }
        this.get_element().appendChild(this._spanRight);
    }
}
UserPCSite.PurchaseView.prototype.isLargeSize = false;
/// <field name="isLargeSize">The size of the element; small or large.</field>
UserPCSite.PurchaseView.prototype.purchaseState = undefined;
/// <field name="purchaseState">The style of button to display.</field>

UserPCSite.PurchaseView.prototype.locDict = null;
/// <field name="locDict">The dictionary to receive localized strings.</field>

UserPCSite.PurchaseView.purchaseStates = { 'buy': 'buy', 'confirm': 'confirm', 'confirmdialog': 'confirmdialog', 'purchased': 'purchased' };
/// <field name="purchaseState">A static list of possible purchase view states.</field>

UserPCSite.PurchaseView.prototype.AppSKU = null;
/// <field name="AppSKU">The AppSKU</field>
UserPCSite.PurchaseView.prototype.ThumbnailPrice = null;
/// <field name="ThumbnailPrice">The app price</field>
UserPCSite.PurchaseView.prototype.ThumbnailName = null;
/// <field name="ThumbnailName">The app name</field>
UserPCSite.PurchaseView.prototype.ThumbnailImagePath = null;
/// <field name="ThumbnailImagePath">the app thumbnail image path</field>
UserPCSite.PurchaseView.prototype.ThumbnailCompanyName = null;
/// <field name="ThumbnailCompanyName">the app company name</field>
UserPCSite.PurchaseView.prototype.ThumbnailPriceDisplay = null;
/// <field name="ThumbnailPriceDisplay">the app price, in UI-displayable format</field>
UserPCSite.PurchaseView.prototype.ThumbnailPriceWithTaxDisplay = null;
/// <field name="ThumbnailPriceWithTaxDisplay">the app price, in UI-displayable format (including taxes)</field>
UserPCSite.PurchaseView.prototype.deviceId = null;
/// <field name="deviceId">the Device ID</field>
UserPCSite.PurchaseView.prototype.websiteDeviceId = null;
/// <field name="websiteDeviceId">the website device ID</field>
UserPCSite.PurchaseView.prototype.supportUrl = null;
/// <field name="supportUrl">the support URL</field>
UserPCSite.PurchaseView.prototype.transactionID = null;
/// <field name="transactionID">the transaction ID (PremiumOffer only)</field>
UserPCSite.PurchaseView.prototype.puid = null;
/// <field name="puid">the puid (PremiumOffer only)</field>
UserPCSite.PurchaseView.prototype.returnUrl = null;
/// <field name="returnUrl">the return URL (PremiumOffer only)</field>

UserPCSite.PurchaseView.registerClass('UserPCSite.PurchaseView', SiteCommon.InteractiveBaseView);

/*********************************************************************************************
*
*  PurchaseController.js
*
*       This class is used to drive the purchase logic and commerce API integration.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.PurchaseController = function() {
    /// <summary>
    ///     PurchaseController Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <returns>
    ///     A newly created PurchaseController.
    /// </returns>
    UserPCSite.PurchaseController.initializeBase(this);
}

UserPCSite.PurchaseController.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///
        ///     Initializes the controller.
        /// </summary>
        UserPCSite.PurchaseController.callBaseMethod(this, 'initialize');

        this._purchaseView = null;
    },
    dispose: function() {
        /// <summary>
        ///     Dispose any local resources.
        /// </summary>
        UserPCSite.PurchaseController.callBaseMethod(this, 'dispose');
    },
    initializeRequest: function() {
        /* defined for interface */
    },
    purchaseButtonClicked: function(purchaseView) {
        /// <summary>
        ///     The button has been clicked on the purchaseView so this will take the appropriate server action.
        /// </summary>
        /// <param name="purchaseView">A reference to the purchaseView object that was clicked.</param>

        this._purchaseView = purchaseView;
        if (SiteCommon.Utils.isNullOrUndefined(UserPCSite.Commerce)) {
            //Expected: if user signed in, https set on server side

            if (window.location.protocol == 'http:') {
                //Force user to login to liveID
                if (!this._forceLogin()) {
                    //Show error message
                    var divContent = SiteCommon.Utils.CreateDiv(null, 'PopupDialogContent', 'PopupDialogContent', null);
                    var p = SiteCommon.Utils.CreatePara(divContent, 'PopupDialogContent_errormessage',
                                '', SiteCommon.Utils.toUIString(this._purchaseView._Loc.SigninPrompt));
                    SiteCommon.Utils.displayPopup(true, true,
                            SiteCommon.Utils.toUIString(this._purchaseView._Loc.LoginError), divContent);
                }
                return;
            }
        }

        var service = new UserPCSite.Commerce();


        // Take the appropriate action based on the button state.
        switch (purchaseView.purchaseState) {
            case UserPCSite.PurchaseView.purchaseStates.buy:
                {
                    // TODO: Verify that LiveID is logged in, or depend on the response code from this call.
                    service.GetCreditCardsForUser(this._purchaseView.deviceId, this._purchaseView.websiteDeviceId, this._onQueryCards, this._onQueryCardsFailure, this);
                    break;
                }
            case UserPCSite.PurchaseView.purchaseStates.confirmdialog:
            case UserPCSite.PurchaseView.purchaseStates.confirm:
                {
                    // Launch spinner UI while processing purchase
                    this._popupProcessingPurchase();

                    // If somehow we're in this state even though a bad case was hit, then wait and issue a failure.
                    if (!SiteCommon.Utils.isSuccessResponse(this._data.get_json())) {
                        // A timeout callback is used here to simulate a failure callback from the server.  This will allow the
                        // normal View click logic to work, and it will expectedly wait for a server response.
                        setTimeout(this._failedTimerHandler, 10);
                    }
                    else {
                        // Pack the data into a PaymentRequest object.
                        var PaymentRequest = new Object();
                        PaymentRequest.__type = "OneTimePaymentRequest:Microsoft.WindowsMobile.Service.Marketplace";
                        PaymentRequest.ItemId = this._purchaseView.AppSKU;
                        PaymentRequest.AccountId = this._data.getAccountId();

                        //check if free app
                        if (0 == this._purchaseView.ThumbnailPrice) {
                            PaymentRequest.PaymentInstrumentId = null;
                        }
                        else {
                            PaymentRequest.PaymentInstrumentId = this._data.getDefaultPaymentInstrumentId();
                        }

                        PaymentRequest.DeviceInstance = this._purchaseView.websiteDeviceId;

                        if (!SiteCommon.Utils.isNullOrUndefined(this._purchaseView.transactionID)) {
                            PaymentRequest.TransactionId = this._purchaseView.transactionID;
                        }

                        service.ChargeCCAccountOneTime(this._purchaseView.deviceId, PaymentRequest, this._onSuccessfulPurchase, this._onFailurePurchase, this);
                    }
                    break;
                }
        }
    },
    _onQueryCards: function(response, context) {
        /// <summary>
        ///     The user data has been queried from the server, store it and notify the user that they can purchase.
        /// </summary>
        /// <param name="response">The json response of the user query.</param>
        /// <param name="context">A reference to the PurchaseController object.</param>

        // Create the data object with the response object
        context._data = $create(UserPCSite.PurchaseData,
                                 { 'json': response
                                 },
                                 {},
                                 {},
                                 null);

        if (context._data.getPaymentInstrumentCount() > 0
        //or if free app
            || 0 == context._purchaseView.ThumbnailPrice) {
            // Notify the user of the query results.
            context._purchaseView._onPurchaseCallback(SiteCommon.Utils.isSuccessResponse(response));
        }
        else {
            //if no credit cards, handle response as failure
            response.ResponseCode = "0x80040207";
            context._onQueryCardsFailure(response, context);
        }
    },
    _sendToSignupWrapper: function() {
        /// <summary>
        /// The LiveID currently signed in does not have any billing information associated with it.
        /// prompt the user to go through the pcs flow and add a credit card
        /// </summary>
        var page = window.location.pathname;
        page = page.substr(1, page.length);
        var url = "https://" + window.location.host + "/signupwrapper.aspx" + window.location.search
                    + "&signupRetUrl=" + page;

        if (SiteCommon.Utils.QueryString('appSKU') == null) {
            url += "&appSKU=" + this._purchaseView.AppSKU;
        }

        window.location = url;
    },
    _handleResponseCode: function(response) {
        /// <summary>
        ///     Handle specific response codes in the response object
        /// </summary>
        /// <param name="response">The json response of the user query.</param>
        /// <returns>true if handled a response code</returns>
        if (!SiteCommon.Utils.isNullOrUndefined(response.ResponseCode)) {
            switch (response.ResponseCode) {
                case "0x80040207":  //No CC
                    {
                        //if free
                        if (0 == this._purchaseView.ThumbnailPrice) {
                            //go to the next step, no credit card is required
                            response._statusCode = 200;
                            response.ResponseCode = "0x00000000";
                            this._onQueryCards(response, this);
                            return true;
                        }
                        else {
                            this._sendToSignupWrapper()
                            return true;
                        }
                    }
                case "0x80040820":  //TOU unsigned
                    {
                        this._sendToSignupWrapper()
                        return true;
                    }
            }
        }
        return false;
    },
    _forceLogin: function() {
        if (SiteCommon.Utils.isNullOrUndefined(this._purchaseView._Loc.SignInURL)) {
            return false;
        }
        else {
            var returl = new String(this._purchaseView._Loc.SignInURL);

            ///returl contains the page url under two layers of encoding, so we
            ///need to first decode the real page url, attach 'buy' & 'appSKU' params,
            ///and then reconstruct the returl by replacing the old strings with new ones.

            var escapedUrl1 = SiteCommon.Utils.QueryStringInUrl('wreply', returl);
            var unescapedUrl1 = unescape(escapedUrl1);
            var escapedUrl2 = SiteCommon.Utils.QueryStringInUrl('url', unescapedUrl1);
            var unescapedUrl2 = unescape(escapedUrl2);

            var unescapedUrl2New = unescapedUrl2;
            if (null == SiteCommon.Utils.QueryStringInUrl('buy', unescapedUrl2New)) {
                unescapedUrl2New += '&buy=true';
            }
            var appSKU = this._purchaseView.AppSKU;
            if (appSKU != SiteCommon.Utils.QueryStringInUrl('appSKU', unescapedUrl2New)) {
                unescapedUrl2New += '&appSKU=' + appSKU;
            }

            // for premium offers, both the puid hash and return url 
            // need to be url encoded twice prior to the login redirect,
            // so that when the redirect decodes the entire url, they remain url encoded

            var puidHash = this._purchaseView.puid;
            if (!SiteCommon.Utils.isNullOrUndefined(puidHash)) {
                var singleEscapePuidHash = encodeURIComponent(puidHash);
                var doubleEscapePuidHash = encodeURIComponent(singleEscapePuidHash);
                unescapedUrl2New = unescapedUrl2New.replace(puidHash, doubleEscapePuidHash);
            }

            var queryStringReturnUrl = this._purchaseView.returnUrl;
            if (!SiteCommon.Utils.isNullOrUndefined(queryStringReturnUrl)) {
                var singleEscapeReturnUrl = encodeURIComponent(queryStringReturnUrl);
                var doubleEscapeReturnUrl = encodeURIComponent(singleEscapeReturnUrl);
                unescapedUrl2New = unescapedUrl2New.replace(queryStringReturnUrl, doubleEscapeReturnUrl);
            }

            ///Notice: encodeURIComponent() encodes '/' characters while escape() does not.
            var escapedUrl2New = encodeURIComponent(unescapedUrl2New);
            var unescapedUrl1New = unescapedUrl1.replace(escapedUrl2, escapedUrl2New);
            var escapedUrl1New = encodeURIComponent(unescapedUrl1New);
            var returlNew = returl.replace(escapedUrl1, escapedUrl1New);

            window.location = returlNew;
            return true;
        }
    },
    _onQueryCardsFailure: function(response, context) {
        /// <summary>
        ///     There was a failure in getting the user's account and credit cards.
        /// </summary>
        /// <param name="response">The json response of the user query.</param>
        /// <param name="context">A reference to the PurchaseController object.</param>
        var handled = false;
        var divContent = SiteCommon.Utils.CreateDiv(null, 'PopupDialogContent', 'PopupDialogContent', null);
        var errorTitle = "";

        if (context._handleResponseCode(response)) {
            handled = true;
            return;
        }
        else if (response._statusCode == "401"
                || response._statusCode == "302") {
            // Look for a special case of no LiveID logged in (this error is returned before our server code can be accessed).
            if (context._forceLogin()) {
                //Do not display popup
                return;
            }
            else {
                var p = SiteCommon.Utils.CreatePara(divContent, 'PopupDialogContent_errormessage',
                                '', SiteCommon.Utils.toUIString(this._purchaseView._Loc.SigninPrompt));
                errorTitle = this._purchaseView._Loc.LoginError;

                //Display popup
                handled = true;
            }
        }

        if (handled) {
            SiteCommon.Utils.displayPopup(true, true, document.createTextNode(errorTitle), divContent);
        }
        else {
            UserPCSite.PurchaseController.get_instance()._showErrorPopup(context._purchaseView._Loc.PurchaseError);
        }

        // Notify the user of failure.
        context._purchaseView._onPurchaseCallback(false);
    },
    _failedTimerHandler: function() {
        /// <summary>
        ///     Used to delay notify the user of a failure (i.e. bad LiveID data).  A timer is used to ensure
        ///     the current thread is unaffected.
        /// </summary>

        // Issue a fake failure response.
        UserPCSite.PurchaseController.get_instance()._onFailureResponse({ 'ResponseCode': "0x80000005" }, UserPCSite.PurchaseController.get_instance());
    },
    _onSuccessfulPurchase: function(response, context) {
        /// <summary>
        ///     A successful purchase server call has happened.  Verify the purchase was actually successful and notify the user.
        /// </summary>
        /// <param name="response">The json response of the user query.</param>
        /// <param name="context">A reference to the PurchaseController object.</param>

        var success = false; // Return to user with successful purchase?

        if (SiteCommon.Utils.isSuccessResponse(response)) {
            success = true;

            if (!SiteCommon.Utils.isNullOrUndefined(UserPCSite.PurchasedAppController)
                && !SiteCommon.Utils.isNullOrUndefined(UserPCSite.PurchasedAppController.get_instance)) {
                //Add appSku to the current purchased apps list
                UserPCSite.PurchasedAppController.get_instance().addPurchasedApp(
                context._purchaseView.AppSKU);

                // omniture tracking
                SiteCommon.Utils.OmnitureTrackPurchase("User : TryBuy : Purchase", "User-TryBuy", context._purchaseView.AppSKU, context._purchaseView.ThumbnailName, context._purchaseView.ThumbnailPriceDisplay);
            }

            // Notify the user to download on phone.
            if (UserPCSite.PurchaseController.get_instance()._shouldShowPopup()) {
                context._popupDownloadConfirmation();
            }
        }
        else {
            var isError = true;
            var dialogTitle = context._purchaseView._Loc.PurchaseError;
            var divContent = document.createElement('div');
            var handled = false;

            // Check for possible predicted error conditions.
            if (!SiteCommon.Utils.isNullOrUndefined(response.ResponseCode)) {
                switch (response.ResponseCode) {
                    case "0x80040800": // AlreadyExists, returned since the user already purchased the app.
                        {
                            var p = SiteCommon.Utils.CreatePara(divContent, null, null,
                                        document.createTextNode(String.format(
                                            context._purchaseView._Loc.PurchasedMessage,
                                            context._purchaseView.ThumbnailName)));
                            dialogTitle = context._purchaseView._Loc.PurchaseConfirmation;

                            // Since the app was already purchased, display a non-error notification to the user.
                            // Eventually we won't hit this error very often, but currently our list queries don't
                            // know if the app was already purchased.
                            isError = false;
                            success = true;
                            handled = true;
                        }
                }
            }
            if (handled) {
                SiteCommon.Utils.displayPopup(isError, true, document.createTextNode(dialogTitle), divContent);
            }
            else {
                // Default error condition as a catch-all.
                UserPCSite.PurchaseController.get_instance()._showErrorPopup(dialogTitle);
            }
        }
        context._purchaseView._onPurchaseCallback(success);

        // if we're going to navigate back via return url, need to also send result code, then redirect
        if (!SiteCommon.Utils.isNullOrUndefined(context._purchaseView.returnUrl)) {
            window.location = context._purchaseView.returnUrl + "&result=success";
        }

    },
    _onFailurePurchase: function(response, context) {
        /// <summary>
        ///     A failed purchase server call has happened, notify the user.
        /// </summary>
        /// <param name="response">The json response of the user query.</param>
        /// <param name="context">A reference to the PurchaseController object.</param>

        // Setup the response UI.
        var handled = false; // Used to determine if a default error dialog should be used.
        var divContent = document.createElement('div');
        Sys.UI.DomElement.addCssClass(divContent, 'PopupDialogContent');
        var errorTitle = "";

        // Add any expected error conditions here, once they are known.

        if (handled) {
            SiteCommon.Utils.displayPopup(true, true, document.createTextNode(errorTitle), divContent);
        }
        else {
            // Default error dialog if not handled previously.
            UserPCSite.PurchaseController.get_instance()._showErrorPopup(context._purchaseView._Loc.PurchaseError);
        }

        // Notify the user of failure.
        context._purchaseView._onPurchaseCallback(false);

        // if we're going to navigate back via return url, need to also send result code, then redirect
        if (!SiteCommon.Utils.isNullOrUndefined(context._purchaseView.returnUrl)) {
            window.location = context._purchaseView.returnUrl + "&result=failure";
        }
    },
    _popupDownloadConfirmation: function() {
        /// <summary>
        ///     Displays the download confirmation dialog.
        /// </summary>
        var id = 'popupconfirmation_pickup';

        var divContent = SiteCommon.Utils.CreateDiv(null, id, 'PopupDownloadConfirmation', null);

        var divLeftCol = SiteCommon.Utils.CreateDiv(divContent, id + '_left', 'PickupLeftColumn', null);

        //Left column Message box
        var smallBoxId = id + '_left' + '_smallBox';
        var divBox1 = SiteCommon.Utils.CreateDiv(divLeftCol, smallBoxId, 'PickupSmallBox', null);
        var para1 = SiteCommon.Utils.CreatePara(divBox1, smallBoxId + '_p1', null, null);
        SiteCommon.Utils.CreateSpan(para1, smallBoxId + '_t1', 'PickupBold',
                        SiteCommon.Utils.toUIString(this._purchaseView._Loc.Pickup_Box_Bold));
        SiteCommon.Utils.CreateSpan(para1, smallBoxId + '_t2', null,
                        SiteCommon.Utils.toUIString(this._purchaseView._Loc.Pickup_Box_Text1));
        var para2 = SiteCommon.Utils.CreatePara(divBox1, smallBoxId + '_p2', null,
                        SiteCommon.Utils.toUIString(this._purchaseView._Loc.Pickup_Box_Text2));

        //Left column Instructions
        var listBoxId = id + '_left' + '_list';
        var divBox2 = SiteCommon.Utils.CreateDiv(divLeftCol, listBoxId, 'PickupListBox', null);
        SiteCommon.Utils.CreatePara(divBox2, listBoxId + '_item1', null,
                         SiteCommon.Utils.toUIString(this._purchaseView._Loc.Pickup_List1));
        SiteCommon.Utils.CreatePara(divBox2, listBoxId + '_item2a', null,
                        SiteCommon.Utils.toUIString(this._purchaseView._Loc.Pickup_List2a));
        SiteCommon.Utils.CreatePara(divBox2, listBoxId + '_item2b', null,
                        SiteCommon.Utils.toUIString(this._purchaseView._Loc.Pickup_List2b));
        SiteCommon.Utils.CreatePara(divBox2, listBoxId + '_item3', null,
                        SiteCommon.Utils.toUIString(this._purchaseView._Loc.Pickup_List3));

        var footerBoxId = id + '_left' + '_footer';
        var divBox3 = SiteCommon.Utils.CreateDiv(divContent, footerBoxId, 'PickupFooterBox', null);
        SiteCommon.Utils.CreatePara(divBox3, footerBoxId + '_bold', 'PickupBold',
                         SiteCommon.Utils.toUIString(this._purchaseView._Loc.Pickup_Footer_Bold));
        var faq = this._purchaseView._Loc.Pickup_FAQ + '';
        var footerTextBox = SiteCommon.Utils.CreatePara(divBox3, footerBoxId + '_text', null, null);
        //We decided to not put link on FAQ 
        //but the FAQ locale string came ready with 'a' tag, so I had to do string manipulate to show no clickable link.
        footerTextBox.innerHTML = this._purchaseView._Loc.Pickup_Footer_Text.replace('<a href={0}>', '').replace('</a>', '');


        //Right column Phone image
        var divRightCol = SiteCommon.Utils.CreateDiv(divContent, id + '_right', 'PickupRightColumn', null);
        SiteCommon.Utils.CreateDiv(divRightCol, id + '_right_phone', 'PickupPhoneImage', null);
        SiteCommon.Utils.CreateDiv(divRightCol, id + '_right_phone_caption', 'PickupPhoneImageCaption',
                                SiteCommon.Utils.toUIString(this._purchaseView._Loc.Pickup_Image_Caption));

        //Close button
        var buttonRow = SiteCommon.Utils.CreateDiv(divRightCol, id + '_buttons', 'PickupCloseButton', null);
        var btn_close = SiteCommon.Utils.CreateButton(this._purchaseView._Loc.Close, 'Confirmation_Close',
                                'OrangeButton', 'SiteCommon.Utils.closePopup();');
        buttonRow.appendChild(btn_close);

        SiteCommon.Utils.displayPopup(false, true, document.createTextNode(this._purchaseView._Loc.DownloadInfo), divContent);
    },
    _popupPurchaseConfirmation: function() {
        /// <summary>
        ///     Displays a purchase confirmation dialog
        /// </summary>
        var id = this._purchaseView.get_id();

        var divContent = document.createElement('div');
        Sys.UI.DomElement.addCssClass(divContent, 'PopupDialogContent PurchaseView_confdlg');

        // confirmation label
        var divConfirmationLabel = SiteCommon.Utils.CreateDiv(divContent, this.get_id() + '_confirmationLabel',
                        'PurchaseView_confdlg_conf_label', SiteCommon.Utils.toUIString(this._purchaseView._Loc.ConfirmPurchase));

        // product icon
        var divProductIcon = SiteCommon.Utils.CreateDiv(divContent, this.get_id() + '_productIcon',
                        'PurchaseView_confdlg_product_icon', null);

        var imgProductIcon = SiteCommon.Utils.CreateImg(divProductIcon, this.get_id() + '_productIconImg',
                        null, SiteCommon.Utils.FixUrlProtocol(this._purchaseView.ThumbnailImagePath), null);

        // div for company details, etc
        var divCompanyDetails = SiteCommon.Utils.CreateDiv(divContent, this.get_id() + '_companyDetails',
                        null, null);

        // app name
        var divAppName = SiteCommon.Utils.CreateDiv(divCompanyDetails, this.get_id() + '_appName',
                        'PurchaseView_confdlg_product_name', SiteCommon.Utils.boldFirstWord(SiteCommon.Utils.toUIString(this._purchaseView.ThumbnailName)));

        // company
        var divCompany = SiteCommon.Utils.CreateDiv(divCompanyDetails, this.get_id() + '_publisherName',
                        'PurchaseView_confdlg_publisher_name', SiteCommon.Utils.toUIString(this._purchaseView._Loc.By));
        divCompany.appendChild(SiteCommon.Utils.toUIString(this._purchaseView.ThumbnailCompanyName));

        // price
        var divPrice = SiteCommon.Utils.CreateDiv(divCompanyDetails, this.get_id() + '_price',
                        null, SiteCommon.Utils.toUIString(this._purchaseView.ThumbnailPriceDisplay));

        // divider
        var divDivider = SiteCommon.Utils.CreateDiv(divContent, this.get_id() + '_divDivider',
                        'PurchaseView_confdlg_divider', null);

        // purchase description - "your credit card ending in 1234 will be charged ..."
        var divPurchaseDescription = SiteCommon.Utils.CreateDiv(divContent, this.get_id() + '_purchaseDescription',
                        'PurchaseView_confdlg_purchase_desc', SiteCommon.Utils.toUIString(String.format(this._purchaseView._Loc.PurchaseDescription,
                        this._data.getDefaultLastFourDigits(), this._purchaseView.ThumbnailPriceWithTaxDisplay, this._purchaseView.supportUrl)));

        // "automatically confirm purchases" checkbox - this is currently hidden
        var autoConfirmCheckBox = SiteCommon.Utils.CreateCheckBox('_confirmCheckBox', false);
        Sys.UI.DomElement.addCssClass(autoConfirmCheckBox, 'PurchaseView_confdlg_autoconfirm_checkbox');
        divContent.appendChild(autoConfirmCheckBox);

        // confirm button
        var confirmButtonDiv = SiteCommon.Utils.CreateDiv(divContent, this.get_id() + '_confirmButtonDiv',
        'PurchaseView_confdlg_confirm', null);
        divContent.appendChild(confirmButtonDiv);

        var confirmButton = SiteCommon.Utils.CreateButton(this._purchaseView._Loc.Confirm, this.get_id() + '_confirmButton', 'OrangeButton',
        'UserPCSite.PurchaseController.purchaseConfDialogConfirmCallback(\'_confirmCheckBox\');');
        confirmButtonDiv.appendChild(confirmButton);

        // cancel button
        var cancelButtonDiv = SiteCommon.Utils.CreateDiv(divContent, this.get_id() + '_cancelButtonDiv',
                    'PurchaseView_confdlg_cancel', null);
        divContent.appendChild(cancelButtonDiv);

        var cancelButton = SiteCommon.Utils.CreateButton(this._purchaseView._Loc.Cancel, this.get_id() + '_cancelButton', 'WhiteButton',
        'UserPCSite.PurchaseController.purchaseConfDialogCancelCallback();');
        cancelButtonDiv.appendChild(cancelButton);

        // launch popup
        SiteCommon.Utils.displayPopup(false, true, SiteCommon.Utils.toUIString(this._purchaseView._Loc.PurchaseConfirmation), divContent);

    },
    _purchaseConfDialogConfirmCallback: function(confirmCheckBox) {
        /// <summary>
        ///     callback for purchase confirmation dialog's "confirm" button
        /// </summary>

        // store setting to suppress dialog if the user selected it
        SiteCommon.Utils.setShowPurchaseConfirmation(confirmCheckBox);

        // close popup
        SiteCommon.Utils.closePopup();

        // set new state and fake a click to transition to next state
        this._purchaseView._onClick(this._purchaseView, null);
    },
    _purchaseConfDialogCancelCallback: function() {
        /// <summary>
        ///     callback for purchase confirmation dialog's "cancel" button
        /// </summary>

        // close popup
        SiteCommon.Utils.closePopup();

        // set state back to 'buy' when user cancels purchase confirmation
        this._purchaseView._setState(UserPCSite.PurchaseView.purchaseStates.buy);
    },
    _popupProcessingPurchase: function() {
        /// <summary>
        ///     Displays a purchase processing spinner dialog.
        /// </summary>

        var divContent = document.createElement('div');
        Sys.UI.DomElement.addCssClass(divContent, 'PopupDialogContent');

        // Set the dialog text.
        var p = document.createElement('p');
        p.appendChild(document.createTextNode(
            String.format(this._purchaseView._Loc.ProcessingPurchaseOf,
                SiteCommon.Utils.AddEllipses(
                    this._purchaseView.ThumbnailName,
                    UserPCSite.PurchaseController.thumbnailNameLength.purchase))));

        divContent.appendChild(p);

        // Add the Loading message
        var loading = SiteCommon.Utils.createLoadingDiv(this._purchaseView._Loc.Loading);
        divContent.appendChild(loading);

        // Launch the progress dialog.
        SiteCommon.Utils.displayPopup(false, false, document.createTextNode(this._purchaseView._Loc.ProcessingPurchase), divContent);
    },
    _shouldShowPopup: function() {
        /// <summary>
        ///     Returns true if we should show popups, false otherwise
        ///     Useful for the premium offer scenario where we want to follow a redirect
        ///     to a return url after the purchase has completed
        /// </summary>
        var shouldShowPopup = true;

        if (!SiteCommon.Utils.isNullOrUndefined(this._purchaseView.returnUrl)) {
            shouldShowPopup = false;
        }
        return shouldShowPopup;
    },
    _showErrorPopup: function(header) {
        /// <summary>
        ///     Wrapper for showing error popups. 
        /// </summary>
        if (this._shouldShowPopup()) {
            SiteCommon.Utils.displayErrorMessagePopup(document.createTextNode(header), document.createTextNode(context._Loc.ERROR_GENERIC_DESCRIPTION));
        }
    }
}

UserPCSite.PurchaseController.get_instance = function() { return UserPCSite.PurchaseController._instance; }
/// <field name="instance">Get the current Purchase instance, as this is a singleton class.</field>

UserPCSite.PurchaseController.prototype._Loc = null;
/// <field name="locDict">The dictionary to receive localized strings.</field>

UserPCSite.PurchaseController.thumbnailNameLength = { 'purchase': 20 };
/// <field name="thumbnailNameLength">A static list of the thumbnail name text length.</field>


UserPCSite.PurchaseController.registerClass('UserPCSite.PurchaseController', Sys.Component, SiteCommon.IController);

// Create the PurchaseController singleton.
UserPCSite.PurchaseController._instance = $create(UserPCSite.PurchaseController, {}, {}, {}, null);

UserPCSite.PurchaseController.purchaseConfDialogConfirmCallback = function(confirmCheckBox) {
    /// <summary>
    ///     Static callback for "confirm" button on purchase confirmation dialog
    /// </summary>
    /// <param name="confirmCheckBox">checkbox indicating whether to bypass the dialog in the future</param>

    var purchaseController = UserPCSite.PurchaseController.get_instance();
    purchaseController._purchaseConfDialogConfirmCallback(confirmCheckBox);

};

UserPCSite.PurchaseController.purchaseConfDialogCancelCallback = function() {
    /// <summary>
    ///     Static callback for "cancel" button on purchase confirmation dialog
    /// </summary>

    var purchaseController = UserPCSite.PurchaseController.get_instance();
    purchaseController._purchaseConfDialogCancelCallback();

};

/// <reference name="MicrosoftAjax.js"/>
/// <reference path="~/AjaxControls/IThumbnailData.js"/>
/// <reference path="~/AjaxControls/InteractiveBaseControl.js" />
/// <reference path="~/Services/Catalog/Catalog.svc"/>
/// <reference path="~/AjaxControls/IController.js"/>
/// <reference path="~/AjaxControls/CategoryListData.js"/>
/// <reference path="~/AjaxControls/CategoryListView.js"/>
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//


/*********************************************************************************************
*
*  CategoryListData.js
*
*      Processes the app data, abstracting it from the UI.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.CategoryListData = function() {
    UserPCSite.CategoryListData.initializeBase(this);
}

UserPCSite.CategoryListData.prototype = {
    initialize: function() {
        /// <summary>
        ///     Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
        /// </summary>
        /// <returns>
        ///     A newly created data object.
        /// </returns>
        
        this._Loc = SiteCommon.Utils.JsonParser(this.locDict);

        UserPCSite.CategoryListData.callBaseMethod(this, 'initialize');
    },
    getCategoryNameUI: function(index) {
        /// <summary>
        ///     Get the name of a category.
        /// </summary>
        /// <returns>Name value of the category.</returns>
        return SiteCommon.Utils.toUIString((this.isRootCategory) ? this._Loc.Categories : this._getItem(index).ParentCategoryName);
    },
    getLength: function() {
        /// <summary>
        ///     Gets the number of items currently acquired.
        /// </summary>
        /// <returns>Count of items acquired.</returns>
        var length = 0;
        if (!((this._json == null) && (this._json.ItemList == null))) {
            length = this._json.ItemList.NumItems;
        }
        return length;
    },
    getSubCategoryNameUI: function(index) {
        /// <summary>
        ///     Get the name of a subcategory.
        /// </summary>
        /// <param name="index">Index of the referenced subcategory</param>
        /// <returns>Name value of the subcategory.</returns>
        return SiteCommon.Utils.toUIString(this._getItem(index).Name);
    },
    getSubCategoryCount: function(index) {
        /// <summary>
        ///     Get the count of items of a subcategory.
        /// </summary>
        /// <param name="index">Index of the referenced subcategory</param>
        /// <returns>Count of items in the subcategory.</returns>
        return this._getItem(index).NumItems;
    },
    getSubCategoryCountUI: function(index) {
        /// <summary>
        ///     Get the count of items of a subcategory.
        /// </summary>
        /// <param name="index">Index of the referenced subcategory</param>
        /// <returns>Count of items in the subcategory, as a UI string element</returns>
        return SiteCommon.Utils.toUIString(this.getSubCategoryCount(index));
    },
    getSubCategoryId: function(index) {
        /// <summary>
        ///     Get the id of a subcategory.
        /// </summary>
        /// <param name="index">Index of the referenced subcategory</param>
        /// <returns>Id value of the subcategory.</returns>
        return this._getItem(index).Id;
    },
    getQueryString: function(name, url) {
        name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
        var regexS = "[\\?&amp;]" + name + "=([^&amp;#]*)";
        var regex = new RegExp(regexS);
        if (typeof (url) == 'undefined') {url = window.location.href;}
        var results = regex.exec(url);
        if (results == null){
            return "";
        } else {
            return results[1];
        }
    },
    _getItem: function(index) {
        /// <summary>
        ///     Get the item referenced by index.
        /// </summary>
        /// <param name="index">Index of the referenced subcategory</param>
        /// <returns>SubCategory Item at the specified index.</returns>
        var value = null;
        try {
            value = this._json.ItemList.List[index];
        }
        catch (exception) {
            throw ("Category Data Index is Out of Range.");
        }
        return value;
    },
    dispose: function() {
        /// <summary>
        ///     Dispose of any local resources.
        /// </summary>
        UserPCSite.CategoryListData.callBaseMethod(this, 'dispose');
    }
}
UserPCSite.CategoryListData.prototype.isRootCategory = false;
/// <field name="isRootCategory">Indicates if this is a list of root categories, or a subcatgory list.</field>

UserPCSite.CategoryListData.prototype.get_json = function() { return this._json; };
UserPCSite.CategoryListData.prototype.set_json = function(json) { this._json = json; }
/// <field name="json">The source data element to use.</field>

UserPCSite.CategoryListData.prototype.locDict = null;
/// <field name="locDict">The dictionary to receive localized strings.</field>

UserPCSite.CategoryListData.registerClass('UserPCSite.CategoryListData', Sys.Component, UserPCSite.IThumbnailData);



/*********************************************************************************************
*
*  CategoryListView.js
*
*      Displays the list of subcategories.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/


UserPCSite.CategoryListView = function(element) {
    /// <summary>
    ///     CategoryListView Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <param name="element">HTML DomElement to associate.</param>
    /// <returns>
    ///     A newly created CategoryListView object.
    /// </returns>
    UserPCSite.CategoryListView.initializeBase(this, [element]);
}

UserPCSite.CategoryListView.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///     
        ///     Initializes the view.
        /// </summary>

        this._categories = new Array();
        this.add_click(this._onClick);

        this._Loc = SiteCommon.Utils.JsonParser(this.get_controller().locDict);

        // Explicitly setting 'Browse Charts' on all views.
        //this.showCharts = false;
        // Do local initialization (needed for_setupRender before calling base class, as it will call _setupRender).
        UserPCSite.CategoryListView.callBaseMethod(this, 'initialize');
    },
    _setupRender: function() {
        /// <summary>
        ///     Sets up the panel dom element.
        /// </summary>

        // Set the main CSS style for the element
        Sys.UI.DomElement.addCssClass(this.get_element(), 'CategoryListView');

        // Create a div for status information (loading, failed, no results, etc)
        this._divStatusInfo = document.createElement('div');
        Sys.UI.DomElement.setVisibilityMode(this._divStatusInfo, Sys.UI.VisibilityMode.collapse);
        this.get_element().appendChild(this._divStatusInfo);
        this.displayLoading();

        // Create a div for the Category List Container
        this._divCategories = document.createElement('div');
        this.get_element().appendChild(this._divCategories);

        // Create a div for the Category Header
        this._divHeader = document.createElement('div');
        Sys.UI.DomElement.addCssClass(this._divHeader, 'CategoryListView_header uppercase');
        this._divCategories.appendChild(this._divHeader);

    },
    dataRender: function() {
        /// <summary>
        ///     Update the UI with main data components from controller.
        /// </summary>

        // Hide the status UI
        Sys.UI.DomElement.setVisible(this._divStatusInfo, false);

        // Assign the category name label.
        //this._divHeader.appendChild(SiteCommon.Utils.boldFirstWord(this.get_controller().get_data().getCategoryNameUI()));

    },
    resetRender: function() {
        /// <summary>
        ///     Reset the UI to initial state.
        /// </summary>

        this._divCategories.innerHTML = "";
        this._divHeader.innerHTML = "";
        delete this._categories;
        this._categories = new Array();
    },
    displayLoading: function() {
        /// <summary>
        ///     Displays loading message.
        /// </summary>

        this._divStatusInfo.innerHTML = "";
        var div = SiteCommon.Utils.createLoadingDiv(this._Loc.Loading);
        this._divStatusInfo.appendChild(div);
        Sys.UI.DomElement.setVisible(this._divStatusInfo, true);
    },
    displayFailed: function() {
        /// <summary>
        ///     Displays failure message.
        /// </summary>
        this._divStatusInfo.innerHTML = "";
        var reloadObj = SiteCommon.Utils.createReloadDiv(this.get_id(), this._Loc.Refresh, this._Loc.ServerBusy);
        this.addEventTarget(SiteCommon.InteractiveBaseView.eventTypes.click,
                                reloadObj.reloadButton.getAttribute('id'),
                                { 'isReload': true });
        this._divStatusInfo.appendChild(reloadObj.div);
        Sys.UI.DomElement.setVisible(this._divStatusInfo, true);
    },
    addCategoryHeader: function(index) {
        // Assign the category name label.  
        if (index != -1) {
            this._divHeader.appendChild(SiteCommon.Utils.boldFirstWord(this.get_controller().get_data().getCategoryNameUI(index)));
        } else {
            this.get_controller()._data.isRootCategory = true;
            this._divHeader.appendChild(SiteCommon.Utils.boldFirstWord(this.get_controller().get_data().getCategoryNameUI(index)));
        }
    },
    addCategory: function(dataContext) {
        /// <summary>
        ///     Add a category to the navigation list.
        /// </summary>
        /// <param name="dataContext">Data context to refer to the data elements.</param>
        var index = this._categories.length;

        // Create the list item
        var divId = this.get_id() + '_' + index;
        //filter category/subcategory
        if ((this.get_id() == 'MainCategoryList_view') || (this.get_controller()._isRootCategory)) {
            var div = this._createListItem(divId, UserPCSite.CategoryListController.itemType.category, dataContext);
        } else {
            var div = this._createListItem(divId, UserPCSite.CategoryListController.itemType.subcategory, dataContext);
        }

        this._categories[index] = div;
        this._divCategories.appendChild(div);
    },
    dispose: function() {
        /// <summary>
        ///     Called to dispose of this objects resources.
        /// </summary>
        UserPCSite.CategoryListView.callBaseMethod(this, 'dispose');
    },
    _createListItem: function(id, itemType, dataContext) {
        /// <summary>
        ///     Create a list item and returns the wrapping Div DOM element.
        /// </summary>
        /// <param name="id">DOM ID to attach to the resulting elements.</param>
        /// <param name="itemType">A type referring to the list item; chart, or category.</param>
        /// <param name="dataContext">Data context to refer to the data elements.</param>
        /// <returns>
        ///     A reference the wrapping DOM element.
        /// </returns>

        // Create the item wrapper.
        var div = document.createElement('div');
        div.setAttribute('id', id);
        Sys.UI.DomElement.addCssClass(div, 'CategoryListView_item uppercase');


        // Create the link for the item name.
        var itemLink = document.createElement('a');
        itemLink.setAttribute('id', id + '_name');
        itemLink.setAttribute('href', this.get_controller().getCategoryURL(itemType, dataContext));
        Sys.UI.DomElement.addCssClass(itemLink, 'CursorHand CategoryListView_item_name');
        div.appendChild(itemLink);

        var itemName = "";

        //subcategory 
        var data = this.get_controller().get_data();
        itemName = data.getSubCategoryNameUI(dataContext);

        // Create the span for the number of items in a subcategory.
        var spanCount = document.createElement('span');
        Sys.UI.DomElement.addCssClass(spanCount, 'CategoryListView_item_count');
        spanCount.appendChild(document.createTextNode(' ('));
        spanCount.appendChild(data.getSubCategoryCountUI(dataContext));
        spanCount.appendChild(document.createTextNode(')'));
        div.appendChild(spanCount);

        itemLink.appendChild(itemName);
        return div;
    },
    _onClick: function(sender, args) {
        /// <summary>
        ///     Click event handler for the subcategoryname.
        /// </summary>
        /// <param name="sender">CategoryListView object that is hovered.</param>
        /// <param name="args">Event args.</param>

        // Notify the controller that a category was clicked.
        if (!SiteCommon.Utils.isNullOrUndefined(args.customData)) {
            if (!SiteCommon.Utils.isNullOrUndefined(args.customData.isReload) && args.customData.isReload) {
                sender.get_controller().reloadClicked();
            }
        }

        // Mark the event as handled to avoid refiring.
        SiteCommon.Utils.markEventHandled(event);
    }
}
UserPCSite.CategoryListView.prototype.showCharts = false;
/// <field name="showCharts">Indicates if the 'browse charts' section should be listed.</field>

UserPCSite.CategoryListView.registerClass('UserPCSite.CategoryListView', SiteCommon.InteractiveBaseView);



/*********************************************************************************************
*
*  CategoryListController.js
*
*       This class is used to drive the category list pane.  It sets up
*       the data class, and initializes the UI classes.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/



UserPCSite.CategoryListController = function(element) {
    /// <summary>
    ///     CategoryListController Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <param name="element">HTML DomElement to associate.</param>
    /// <returns>
    ///     A newly created CategoryListController.
    /// </returns>
    UserPCSite.CategoryListController.initializeBase(this, [element]);
}

UserPCSite.CategoryListController.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///     
        ///     Initializes the controller.
        /// </summary>
        UserPCSite.CategoryListController.callBaseMethod(this, 'initialize');
    },
    initializeRequest: function() {
        /// <summary>
        ///     initializeRequest is called to initialize the ajax data request needed by
        ///     this controller, as well as any additional requirements.
        /// </summary>

        this._ParentCategoryId = 0;
        this._isRootCategory = true;
        if (!SiteCommon.Utils.isNullOrUndefined(this.categoryId) && (this.categoryId != "")) {
            this._isRootCategory = false;
            this._ParentCategoryId = this.categoryId;
        } else {
            this._ParentCategoryId = 0;
        }

        // Create a div to contain the view
        var div = document.createElement('div');
        div.setAttribute('id', this.get_id() + '_view');
        this.get_element().appendChild(div);
        // Create the panel view element
        this._view = $create(UserPCSite.CategoryListView,
                                 { 'controller': this,
                                     'showCharts': this._isRootCategory
                                 },
                                 {},
                                 {},
                                 div);
        this.makeRequest();
    },
    makeRequest: function() {
        /// <summary>
        ///     Issue the actual ajax request.
        /// </summary>

        if (this._isRootCategory) {
            this.makeRequestGetMainCategories();
        }
        else {
            this._view.displayLoading();

            // Get a reference to the catalog service, and issue a data call
            var service = new UserPCSite.Catalog();
            service.GetSubCategories(this.deviceId, this.categoryId, this.langId, this.geoFilter, this.successfulResponse, this.failureResponse, this);
        }
    },
    successfulResponse: function(response, context) {
        /// <summary>
        ///     Called when a data query has successfully completed.
        /// </summary>
        /// <param name="response">A JSON encoded object representing the data acquired.</param>
        /// <param name="context">A reference to the controller that issued the request</param>

        // Create the data object with the response object
        context._data = $create(UserPCSite.CategoryListData,
                                 { 'json': response,
                                     'isRootCategory': context._isRootCategory,
                                     'locDict': context.locDict
                                 },
                                 {},
                                 {},
                                 null);

        // Setup the main UI components.
        context._view.dataRender();

        if (context._data.getLength() > 0) {
            // Add categories for each data element, if the category has more than 0 items in it.
            for (var i = 0; i < context._data.getLength(); i++) {
                if (context._data.getSubCategoryCount(i) > 0) {
                    context._view.addCategory(i);
                }
            }

            //Add Category Heading
            var i = context._data.getLength();
            context._view.addCategoryHeader(i - 1);
        }
        else if (!context._isRootCategory) {
            // We're in a sub-category and there were no additional sub-categories under it. 
            // Load main categories for categories nav list instead. 
            
            // Set _isRootCategory to true so when addCategory is called it will set div itemtype to category rather than subcategory
            // Also ensures we don't enter an infinite loop.
            context._isRootCategory = true;
            context.makeRequestGetMainCategories();
        }
    },
    failureResponse: function(response, context) {
        /// <summary>
        ///     Called when a data query has failed.
        /// </summary>
        /// <param name="response">A string representing the failure information.</param>
        /// <param name="context">A reference to the controller that issued the request</param>
        context._view.displayFailed();
    },
    makeRequestGetMainCategories: function() {
        /// <summary>
        ///     Issue a request to get top-level categories
        /// </summary>

        this._view.displayLoading();
        // Get a reference to the catalog service, and issue a data call
        var service = new UserPCSite.Catalog();
        service.GetCategories(this.deviceId, this.langId, this.geoFilter, this.successfulResponse, this.failureResponse, this);
    },
    getCategoryURL: function(itemType, dataContext) {
        /// <summary>
        /// get appropriate URL to return for catagory page
        /// </summary>
        /// <param name="itemType">the itemType (category or subcategory)</param>
        /// <param name="dataContext">DataContext reference to determine which thumbnail was clicked.</param>
        var href = "";
        if (!SiteCommon.Utils.isNullOrUndefined(dataContext)) {
            if (itemType == UserPCSite.CategoryListController.itemType.category) {
                href = "/categories.aspx?categoryId=" + this._data.getSubCategoryId(dataContext);
            }
            else {
                //In order to stop CategoryList to load sub-subCategory we need to keep ParentID(pid) as part of the querystring.
                if (this._data.getQueryString('pid') == '') {
                    href = "/categories.aspx?categoryId=" + this._data.getSubCategoryId(dataContext) + "&pid=" + this._data.getQueryString('categoryId');
                } else {
                    href = "/categories.aspx?categoryId=" + this._data.getSubCategoryId(dataContext) + "&pid=" + this._data.getQueryString('pid');
                }
            }
        }
        return href;
    },
    reloadClicked: function() {
        /// <summary>
        ///     Called when the view should be reloaded.
        /// </summary>

        // Reset the view to an initial state.
        this._view.resetRender();

        // Issue the actual request.
        this.makeRequest();
    },
    dispose: function() {
        /// <summary>
        ///     Dispose any local resources.
        /// </summary>
        UserPCSite.CategoryListController.callBaseMethod(this, 'dispose');
    }
}
UserPCSite.CategoryListController.prototype.categoryId = null;
/// <field name="categoryId">The categoryId to query.</field>
UserPCSite.CategoryListController.prototype.deviceId = null;
/// <field name="deviceID">The deviceId to query applications.</field>
UserPCSite.CategoryListController.prototype.get_data = function() { return this._data; };
/// <field name="data">Get the data element.</field>

UserPCSite.CategoryListController.itemType = { 'category': 'category', 'chart': 'chart', 'subcategory': 'subcategory' };
/// <field name="itemType">A static list of possible item types.</field>

UserPCSite.CategoryListController.prototype.locDict = null;
/// <field name="locDict">The dictionary to receive localized strings.</field>

UserPCSite.CategoryListController.prototype.langId = null;
/// <field name="langId">The language to display the website in.</field>
UserPCSite.CategoryListController.prototype.geoFilter = null;
/// <field name="geoFilter">The Geo Filter selected by user.</field>

UserPCSite.CategoryListController.registerClass('UserPCSite.CategoryListController', Sys.UI.Control, SiteCommon.IController);

/// <reference name="MicrosoftAjax.js"/>
/// <reference path="~/AjaxControls/IThumbnailData.js"/>

/// <reference path="~/AjaxControls/InteractiveBaseControl.js" />

/// <reference path="~/Services/Catalog/Catalog.svc"/>
/// <reference path="~/AjaxControls/IController.js"/>
/// <reference path="~/AjaxControls/ChartListData.js"/>
/// <reference path="~/AjaxControls/ChartListView.js"/>
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  ChartListData.js
*
*      Processes the app data, abstracting it from the UI.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.ChartListData = function() {
    UserPCSite.ChartListData.initializeBase(this);
}

UserPCSite.ChartListData.prototype = {
    initialize: function() {
        /// <summary>
        ///     Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
        /// </summary>
        /// <returns>
        ///     A newly created data object.
        /// </returns>
        this._Loc = SiteCommon.Utils.JsonParser(this.locDict);

        UserPCSite.ChartListData.callBaseMethod(this, 'initialize');
    },
    getChartNameUI: function() {
        /// <summary>
        ///     Get the name of a Chart.
        /// </summary>
        /// <returns>Name value of the Chart.</returns>
        return SiteCommon.Utils.toUIString((this.isRootChart) ? this._Loc.Charts : this._Loc.ChartName);
    },
    getSubChartName: function(index) {
        /// <summary>
        ///     Get the name of a subChart. 
        ///     e.g. Showcase/WhatsNew etc.
        /// </summary>
        /// <param name="index">Index of the referenced Chart</param>
        /// <returns>Name value of the Chart.</returns>
        return this._getItem(index).Name;
    },
    getLength: function() {
        /// <summary>
        ///     Gets the number of items currently acquired.
        /// </summary>
        /// <returns>Count of items acquired.</returns>
        var length = 0;
        if (!((this._json == null) && (this._json.ItemList == null))) {
            length = this._json.ItemList.NumItems;
        }
        return length;
    },
    getSubChartNameUI: function(index) {
        /// <summary>
        ///     Get the name of a subChart.
        /// </summary>
        /// <param name="index">Index of the referenced subChart</param>
        /// <returns>Name value of the subChart.</returns>   
        return SiteCommon.Utils.toUIString(this._getItem(index).Name);
    },
    getSubChartCountUI: function(index) {
        /// <summary>
        ///     Get the count of items of a subChart.
        /// </summary>
        /// <param name="index">Index of the referenced subChart</param>
        /// <returns>Count of items in the subChart.</returns>
        return SiteCommon.Utils.toUIString(this._getItem(index).NumItems);
    },
    getSubChartId: function(index) {
        /// <summary>
        ///     Get the id of a subChart.
        /// </summary>
        /// <param name="index">Index of the referenced subChart</param>
        /// <returns>Id value of the subChart.</returns>
        return this._getItem(index).Id;
    },
    _getItem: function(index) {
        /// <summary>
        ///     Get the item referenced by index.
        /// </summary>
        /// <param name="index">Index of the referenced subChart</param>
        /// <returns>SubChart Item at the specified index.</returns>
        var value = null;
        try {
            value = this._json.ItemList.List[index];
        }
        catch (exception) {
            throw ("Chart Data Index is Out of Range.");
        }
        return value;
    },
    dispose: function() {
        /// <summary>
        ///     Dispose of any local resources.
        /// </summary>
        UserPCSite.ChartListData.callBaseMethod(this, 'dispose');
    }
}
UserPCSite.ChartListData.prototype.isRootChart = true;
/// <field name="isRootChart">Indicates if this is a list of root Charts, or a subcatgory list.</field>

UserPCSite.ChartListData.prototype.get_json = function() { return this._json; };
UserPCSite.ChartListData.prototype.set_json = function(json) { this._json = json; }
/// <field name="json">The source data element to use.</field>

UserPCSite.ChartListData.prototype.locDict = null;
/// <field name="locDict">The dictionary to receive localized strings.</field>

UserPCSite.ChartListData.registerClass('UserPCSite.ChartListData', Sys.Component, UserPCSite.IThumbnailData);



/*********************************************************************************************
*
*  ChartListView.js
*
*      Displays the list of subCharts.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

UserPCSite.ChartListView = function(element) {
    /// <summary>
    ///     ChartListView Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <param name="element">HTML DomElement to associate.</param>
    /// <returns>
    ///     A newly created ChartListView object.
    /// </returns>
    UserPCSite.ChartListView.initializeBase(this, [element]);
}

UserPCSite.ChartListView.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///     
        ///     Initializes the view.
        /// </summary>

        this._charts = new Array();
        this.add_click(this._onClick);

        this._Loc = SiteCommon.Utils.JsonParser(this.get_controller().locDict);

        // Do local initialization (needed for_setupRender before calling base class, as it will call _setupRender).
        UserPCSite.ChartListView.callBaseMethod(this, 'initialize');
    },
    _setupRender: function() {
        /// <summary>
        ///     Sets up the panel dom element.
        /// </summary>

        // Set the main CSS style for the element
        Sys.UI.DomElement.addCssClass(this.get_element(), 'ChartListView');

        // Create a div for status information (loading, failed, no results, etc)
        this._divStatusInfo = document.createElement('div');
        Sys.UI.DomElement.setVisibilityMode(this._divStatusInfo, Sys.UI.VisibilityMode.collapse);
        this.get_element().appendChild(this._divStatusInfo);
        this.displayLoading();

        // Create a div for the Chart List Container
        this._divCharts = document.createElement('div');
        this.get_element().appendChild(this._divCharts);

        // Create a div for the Chart Browse Header
        this._divHeader = document.createElement('div');
        Sys.UI.DomElement.addCssClass(this._divHeader, 'ChartListView_header uppercase');
        this._divCharts.appendChild(this._divHeader);

    },
    dataRender: function() {
        /// <summary>
        ///     Update the UI with main data components from controller.
        /// </summary>

        // Hide the status UI
        Sys.UI.DomElement.setVisible(this._divStatusInfo, false);
        // Assign the Chart name label.
        this._divHeader.appendChild(SiteCommon.Utils.boldFirstWord(this.get_controller().get_data().getChartNameUI()));
    },
    resetRender: function() {
        /// <summary>
        ///     Reset the UI to initial state.
        /// </summary>

        this._divCharts.innerHTML = "";
        this._divHeader.innerHTML = "";
        delete this._charts;
        this._charts = new Array();
    },
    displayLoading: function() {
        /// <summary>
        ///     Displays loading message.
        /// </summary>

        this._divStatusInfo.innerHTML = "";
        var div = SiteCommon.Utils.createLoadingDiv(this._Loc.Loading);
        this._divStatusInfo.appendChild(div);
        Sys.UI.DomElement.setVisible(this._divStatusInfo, true);
    },
    displayFailed: function() {
        /// <summary>
        ///     Displays failure message.
        /// </summary>
        this._divStatusInfo.innerHTML = "";
        var reloadObj = SiteCommon.Utils.createReloadDiv(this.get_id(), this._Loc.Refresh, this._Loc.ServerBusy);
        this.addEventTarget(SiteCommon.InteractiveBaseView.eventTypes.click,
                                reloadObj.reloadButton.getAttribute('id'),
                                { 'isReload': true });
        this._divStatusInfo.appendChild(reloadObj.div);
        Sys.UI.DomElement.setVisible(this._divStatusInfo, true);
    },
    addChart: function(dataContext) {
        /// <summary>
        ///     Add a Chart to the navigation list.
        /// </summary>
        /// <param name="dataContext">Data context to refer to the data elements.</param>

        var index = this._charts.length;

        // Create the list item
        var divId = this.get_id() + '_' + index;
        var div = this._createListItem(divId, UserPCSite.ChartListController.itemType.chart, dataContext, null, null);
        this._charts[index] = div;
        this._divCharts.appendChild(div);
    },
    dispose: function() {
        /// <summary>
        ///     Called to dispose of this objects resources.
        /// </summary>
        UserPCSite.ChartListView.callBaseMethod(this, 'dispose');
    },
    _createListItem: function(id, itemType, dataContext, chartId) {
        /// <summary>
        ///     Create a list item and returns the wrapping Div DOM element.
        /// </summary>
        /// <param name="id">DOM ID to attach to the resulting elements.</param>
        /// <param name="itemType">A type referring to the list item; chart, or Chart.</param>
        /// <param name="dataContext">Data context to refer to the data elements.</param>
        /// <param name="chartId">If chart type, this contains the chartId.</param>
        /// <returns>
        ///     A reference the wrapping DOM element.
        /// </returns>

        // Create the item wrapper.
        var div = document.createElement('div');
        div.setAttribute('id', id);
        Sys.UI.DomElement.addCssClass(div, 'ChartListView_item uppercase');

        // Create the span contents for the item name.
        var itemLink = document.createElement('a');
        itemLink.setAttribute('id', id + '_name');
        itemLink.setAttribute('href', this.get_controller().getChartURL(itemType, dataContext));
        Sys.UI.DomElement.addCssClass(itemLink, 'CursorHand ChartListView_item_name');
        div.appendChild(itemLink);

        var itemName = "";
        if (itemType == UserPCSite.ChartListController.itemType.chart) {
            var data = this.get_controller().get_data();
            itemName = data.getSubChartNameUI(dataContext);
        }

        itemLink.appendChild(itemName);
        return div;
    },
    _onClick: function(sender, args) {
        /// <summary>
        ///     Click event handler for the subChartname.
        /// </summary>
        /// <param name="sender">ChartListView object that is hovered.</param>
        /// <param name="args">Event args.</param>


        // Notify the controller that a Chart was clicked.
        if (!SiteCommon.Utils.isNullOrUndefined(args.customData)) {
            if (!SiteCommon.Utils.isNullOrUndefined(args.customData.isReload) && args.customData.isReload) {
                sender.get_controller().reloadClicked();
            }
        }

        // Mark the event as handled to avoid refiring.
        SiteCommon.Utils.markEventHandled(event);
    }
}
//UserPCSite.ChartListView.prototype.showCharts = false;
/// <field name="showCharts">Indicates if the 'browse charts' section should be listed.</field>

UserPCSite.ChartListView.registerClass('UserPCSite.ChartListView', SiteCommon.InteractiveBaseView);



/*********************************************************************************************
*
*  ChartListController.js
*
*       This class is used to drive the Chart list pane.  It sets up
*       the data class, and initializes the UI classes.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

UserPCSite.ChartListController = function(element) {
    /// <summary>
    ///     ChartListController Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <param name="element">HTML DomElement to associate.</param>
    /// <returns>
    ///     A newly created ChartListController.
    /// </returns>
    UserPCSite.ChartListController.initializeBase(this, [element]);
}

UserPCSite.ChartListController.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///     
        ///     Initializes the controller.
        /// </summary>
        UserPCSite.ChartListController.callBaseMethod(this, 'initialize');
    },
    initializeRequest: function() {
        /// <summary>
        ///     initializeRequest is called to initialize the ajax data request needed by
        ///     this controller, as well as any additional requirements.
        /// </summary>
        this._isRootChart = true;
        if (!SiteCommon.Utils.isNullOrUndefined(this.chartId) && (this.chartId != "")) {
            this._isRootChart = false;
        }

        // Create a div to contain the view
        var div = document.createElement('div');
        div.setAttribute('id', this.get_id() + '_view');
        this.get_element().appendChild(div);
        // Create the panel view element
        this._view = $create(UserPCSite.ChartListView,
                                 { 'controller': this
                                 },
                                 {},
                                 {},
                                 div);
        this.makeRequest();
    },
    makeRequest: function() {
        /// <summary>
        ///     Issue the actual ajax request.
        /// </summary>
        this._view.displayLoading();
        // Get a reference to the catalog service, and issue a data call
        var service = new UserPCSite.Catalog();

        service.GetCharts(this.deviceId, this.langId, this.geoFilter, '', this.successfulResponse, this.failureResponse, this);
    },
    successfulResponse: function(response, context) {
        /// <summary>
        ///     Called when a data query has successfully completed.
        /// </summary>
        /// <param name="response">A JSON encoded object representing the data acquired.</param>
        /// <param name="context">A reference to the controller that issued the request</param>

        // Create the data object with the response object
        context._data = $create(UserPCSite.ChartListData,
                                 { 'json': response,
                                     'isRootChart': context._isRootChart,
                                     'locDict': context.locDict
                                 },
                                 {},
                                 {},
                                 null);

        // Setup the main UI components.
        context._view.dataRender();

        if (context._data.getLength() > 0) {
            // Add thumbnails to the view for each data element.
            for (var i = 0; i < context._data.getLength(); i++) {
                //Since Client and WebSite are sharing code. Chart for MO EndCap are being displayed on website.
                //MO EndCap should only be displayed on Client Phone not on Website.
                //Code below do just that.
                if (context._data.getSubChartId(i) != context.chartIdToIgnore) {
                    context._view.addChart(i);
                }
            }
        }
    },
    failureResponse: function(response, context) {
        /// <summary>
        ///     Called when a data query has failed.
        /// </summary>
        /// <param name="response">A string representing the failure information.</param>
        /// <param name="context">A reference to the controller that issued the request</param>
        context._view.displayFailed();
    },
    getChartURL: function(itemType, dataContext) {
        var href = "";
        if (!SiteCommon.Utils.isNullOrUndefined(dataContext)) {
            if (itemType == UserPCSite.ChartListController.itemType.chart) {
                href = "/chart.aspx?chartId=" + this._data.getSubChartId(dataContext) + "&type=" + dataContext;
            }
        }
        return href;
    },
    reloadClicked: function() {
        /// <summary>
        ///     Called when the view should be reloaded.
        /// </summary>

        // Reset the view to an initial state.
        this._view.resetRender();

        // Issue the actual request.
        this.makeRequest();
    },
    dispose: function() {
        /// <summary>
        ///     Dispose any local resources.
        /// </summary>
        UserPCSite.ChartListController.callBaseMethod(this, 'dispose');
    }
}
UserPCSite.ChartListController.prototype.chartId = null;
/// <field name="chartId">The chartId to query.</field>
UserPCSite.ChartListController.prototype.deviceId = null;
/// <field name="deviceID">The deviceId to query applications.</field>
UserPCSite.ChartListController.prototype.chartIdToIgnore = null;
/// <field name="chartIdToIgnore">The chartId of MOEndCap that needs to be ignored</field>
UserPCSite.ChartListController.prototype.get_data = function() { return this._data; };
/// <field name="data">Get the data element.</field>

UserPCSite.ChartListController.itemType = { 'Chart': 'Chart', 'chart': 'chart' };
/// <field name="itemType">A static list of possible item types.</field>

UserPCSite.ChartListController.prototype.locDict = null;
/// <field name="locDict">The dictionary to receive localized strings.</field>

UserPCSite.ChartListController.prototype.geoFilter = null;
/// <field name="geoFilter">The Geo Filter selected by user.</field>

UserPCSite.ChartListController.prototype.langId = null;
/// <field name="langId">The language to display the website in.</field>

UserPCSite.ChartListController.registerClass('UserPCSite.ChartListController', Sys.UI.Control, SiteCommon.IController);

/// <reference name="MicrosoftAjax.js"/>
/// <reference path="~/Services/Catalog/Catalog.svc"/>
/// <reference path="~/AjaxControls/IController.js"/>
/// <reference path="~/AjaxControls/PremiumOfferData.js"/>
/// <reference path="~/AjaxControls/PremiumOfferView.js"/>
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  PremiumOfferController.js
*
*       This class is used to drive the entire app details page.  It sets up
*       the data class, and initializes the UI classes.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.PremiumOfferController = function(element) {
    /// <summary>
    ///     PremiumOfferController Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <param name="element">HTML DomElement to associate.</param>
    /// <returns>
    ///     A newly created PremiumOfferController.
    /// </returns>
    UserPCSite.PremiumOfferController.initializeBase(this, [element]);
}

UserPCSite.PremiumOfferController.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///
        ///     Initializes the controller.
        /// </summary>
        UserPCSite.PremiumOfferController.callBaseMethod(this, 'initialize');

    },
    initializeRequest: function() {
        /// <summary>
        ///     initializeRequest is called to initialize the ajax data request needed by
        ///     this controller, as well as any additional requirements.
        /// </summary>

        // Create a div to contain the view
        var div = document.createElement('div');
        div.setAttribute('id', this.get_id() + '_view');
        this.get_element().appendChild(div);
        // Create the panel view element
        this._view = $create(UserPCSite.PremiumOfferView,
                                 { 'controller': this },
                                 {},
                                 {},
                                 div);
        this.makeRequest();
    },
    makeRequest: function() {
        /// <summary>
        ///     Issue the actual ajax request.
        /// </summary>

        this._view.displayLoading();

        // Get a reference to the catalog service, and issue a data call
        var service = new UserPCSite.Catalog();
        service.GetAppDetails(this.deviceId, this.appSKU, this.langId, this.geoFilter, this.successfulResponse, this.failureResponse, this);
    },
    successfulResponse: function(response, context) {
        /// <summary>
        ///     Called when a data query has successfully completed.
        /// </summary>
        /// <param name="response">A JSON encoded object representing the data acquired.</param>
        /// <param name="context">A reference to the controller that issued the request</param>

        // Create the data object with the response object
        context._data = $create(UserPCSite.PremiumOfferData,
                                 { 'json': response,
                                     'locDict': context.locDict
                                 },
                                 {},
                                 {},
                                 null);

        // Setup the main UI components.
        context._view.dataRender();
    },
    failureResponse: function(response, context) {
        /// <summary>
        ///     Called when a data query has failed.
        /// </summary>
        /// <param name="response">A string representing the failure information.</param>
        /// <param name="context">A reference to the controller that issued the request</param>
        context._view.displayFailed(response);
    },
    reloadClicked: function() {
        /// <summary>
        ///     Called when the view should be reloaded.
        /// </summary>

        // Reset the view to an initial state.
        this._view.resetRender();

        // Issue the actual request.
        this.makeRequest();
    },
    dispose: function() {
        //Add custom dispose actions here
        UserPCSite.PremiumOfferController.callBaseMethod(this, 'dispose');
    }
}
UserPCSite.PremiumOfferController.prototype.appSKU = null;
/// <field name="appSKU">The appSKU to query applications.</field>
UserPCSite.PremiumOfferController.prototype.deviceId = null;
/// <field name="deviceID">The deviceId to query applications.</field>
UserPCSite.PremiumOfferController.prototype.websiteDeviceId = null;
/// <field name="websiteDeviceId">web site id that we pass in the call to purchase.</field>
UserPCSite.PremiumOfferController.prototype.HasUserSelectedDevice = null;
/// <field name="HasUserSelectedDevice">true if user has a selected device (string attribute, "True" or "False")</field>
UserPCSite.PremiumOfferController.prototype.transactionID = null;
/// <field name="transactionID">The transactionID to query applications.</field>
UserPCSite.PremiumOfferController.prototype.puid = null;
/// <field name="puid">The puid to query applications.</field>
UserPCSite.PremiumOfferController.prototype.returnUrl = null;
/// <field name="returnUrl">The returnUrl to query applications.</field>
UserPCSite.PremiumOfferController.prototype.get_data = function() { return this._data; };
/// <field name="data">Get the data element.</field>
UserPCSite.PremiumOfferController.prototype.locDict = null;
/// <field name="locDict">The dictionary to receive localized strings.</field>
UserPCSite.PremiumOfferController.prototype.supportUrl = null;
/// <field name="supportUrl">support url</field>
UserPCSite.PremiumOfferController.prototype.geoFilter = null;
/// <field name="geoFilter">The Geo Filter selected by user.</field>
UserPCSite.PremiumOfferController.prototype.langId = null;
/// <field name="langId">The language to display the website in.</field>

UserPCSite.PremiumOfferController.registerClass('UserPCSite.PremiumOfferController', Sys.UI.Control, SiteCommon.IController);



/// <reference name="MicrosoftAjax.js"/>
/// <reference path="~/AjaxControls/IThumbnailData.js"/>
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  PremiumOfferData.js
*
*      Processes the app data, abstracting it from the UI.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.PremiumOfferData = function() {
    UserPCSite.PremiumOfferData.initializeBase(this);
}

UserPCSite.PremiumOfferData.prototype = {
    initialize: function() {
        /// <summary>
        ///     Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
        /// </summary>
        /// <returns>
        ///     A newly created data object.
        /// </returns>
        this._Loc = SiteCommon.Utils.JsonParser(this.locDict);

        UserPCSite.PremiumOfferData.callBaseMethod(this, 'initialize');
    },
    getThumbnailName: function() {
        /// <summary>
        ///     Get the name of a thumbnail.
        /// </summary>
        /// <param name="index">Unused.</param>
        /// <returns>Name value of the thumbnail.</returns>
        return this._json.Entity.Name;
    },
    getThumbnailNameUI: function() {
        /// <summary>
        ///     Get the name of a thumbnail.
        /// </summary>
        /// <returns>Name value of the thumbnail.</returns>
        return SiteCommon.Utils.toUIString(this._json.Entity.Name);
    },
    getThumbnailImagePath: function() {
        /// <summary>
        ///     Get the image url of a thumbnail.
        /// </summary>
        /// <returns>ImagePath value of the thumbnail.</returns>
        return this._json.Entity.SmallImageUrl;
    },
    getThumbnailRating: function() {
        /// <summary>
        ///     Get the rating of a thumbnail.
        /// </summary>
        /// <returns>Rating value of the thumbnail.</returns>
        return Math.round(this._json.Entity.Rating / 100);
    },
    getThumbnailReviewCount: function(dataContext) {
        /// <summary>
        ///     Get the ReviewCount
        /// </summary>
        /// <param name="dataContext">dataContext of the referenced thumbnail</param>
        /// <returns>ReviewCount value.</returns>
        return this._json.Entity.NumOfReviews;
    },
    getThumbnailReviewCountUI: function() {
        /// <summary>
        ///     Get the ReviewCount of a thumbnail.
        /// </summary>
        /// <returns>ReviewCount value of the thumbnail.</returns>
        return SiteCommon.Utils.toUIString(this._json.Entity.NumOfReviews);
    },
    getThumbnailCompanyNameUI: function() {
        /// <summary>
        ///     Get the Company Name of a thumbnail.
        /// </summary>
        /// <returns>Company Name value of the thumbnail.</returns>
        return SiteCommon.Utils.toUIString(this._json.Entity.CompanyName);
    },
    getThumbnailCategoryUI: function() {
        /// <summary>
        ///     Get the Category of a thumbnail.
        /// </summary>
        /// <returns>Category value of the thumbnail.</returns>
        return SiteCommon.Utils.toUIString(this._Loc.NotSupported);
    },
    getThumbnailDescriptionUI: function() {
        /// <summary>
        ///     Get the Description of a thumbnail.
        /// </summary>
        /// <returns>Description value of the thumbnail.</returns>
        return SiteCommon.Utils.toUIString(this._json.Entity.LongDescription);
    },
    getThumbnailPrice: function(dataContext) {
        /// <summary>
        ///     Get the price of a thumbnail.
        /// </summary>
        /// <param name="dataContext">dataContext of the referenced thumbnail</param>
        /// <returns>Price value of the thumbnail.</returns>
        var price = this._json.Entity.Price;
        if (SiteCommon.Utils.isNullOrUndefined(price)) {
            price = 0;
        }
        return price;
    },
    getThumbnailPriceUI: function() {
        /// <summary>
        ///     Get the price of a thumbnail.
        /// </summary>
        /// <returns>Price value of the thumbnail.</returns>
        var price = this._json.Entity.Price;
        if (SiteCommon.Utils.isNullOrUndefined(price)) {
            price = this._Loc.Free;
        }
        return SiteCommon.Utils.toUIString(price);
    },
    getThumbnailPriceWithTaxUI: function(dataContext) {
        /// <summary>
        ///     Not used
        /// </summary>
        return "";
    },
    getAppSKU: function() {
        /// <summary>
        ///     Get the app sku.
        /// </summary>
        /// <returns>Sku of the app.</returns>
        return this._json.Entity.Id;
    },
    getDateAddedUI: function(format) {
        /// <summary>
        ///     Get the date of the app.
        /// </summary>
        /// <returns>Date of the app.</returns>
        if (!SiteCommon.Utils.isNullOrUndefined(format)) {
            return SiteCommon.Utils.toUIString(this._json.Entity.DateAdded.format(format));
        }
        else {
            return SiteCommon.Utils.toUIString(this._json.Entity.DateAdded.localeFormat("d"));
        }
    },
    getSizeUI: function() {
        /// <summary>
        ///     Get the size of a app.
        /// </summary>
        /// <returns>Size of the app.</returns>
        return SiteCommon.Utils.toUIString(this._json.Entity.Size);
    },
    getSupportUrlUI: function() {
        /// <summary>
        ///     Get the url of a app.
        /// </summary>
        /// <returns>Support url of the app.</returns>
        return SiteCommon.Utils.toUIString(this._json.Entity.SupportUrl);
    },
    getVersionUI: function() {
        /// <summary>
        ///     Get the version of the app.
        /// </summary>
        /// <returns>Version of the app.</returns>
        return SiteCommon.Utils.toUIString(this._json.Entity.Version);
    },
    getScreenshotCount: function() {
        /// <summary>
        ///     Get the number of screenshots available.
        /// </summary>
        /// <returns>Count of the screenshots.</returns>
        return this._json.Entity.Screenshots.length;
    },
    getScreenshotPath: function(index) {
        /// <summary>
        ///     Get the screenshot of the app suing the referenced dataContext.
        /// </summary>
        /// <param name="index">index of the referenced screenshot</param>
        /// <returns>Screenshot URL at the specified index.</returns>
        var value = null;
        try {
            value = this._json.Entity.Screenshots[index];
        }
        catch (exception) {
            throw ("Screenshot Index is Out of Range.");
        }
        return value;
    },
    getPlatformTypeByID: function() {
        //unused for interface
        return 0;
    },
    getIsFeatured: function() {
        /// <summary>
        ///     Get if the app is featured app.
        /// </summary>
        /// <param name="dataContext">DataContext required to return the thumbnail data.</param>
        /// <returns>Boolean isFeatured</returns>
        return false;
    },
    getFeaturesCount: function() {
        /// <summary>
        ///     Get the number of features available.
        /// </summary>
        /// <returns>Count of the features.</returns>
        return this._json.Entity.Features.length;
    },
    getFeature: function(index) {
        /// <summary>
        ///     Get the feature at index 'index'.
        /// </summary>
        /// <param name="index">index of the referenced feature</param>
        /// <returns>Feature at the specified index.</returns>
        var value = null;
        try {
            value = this._json.Entity.Features[index];
        }
        catch (exception) {
            throw ("Feature Index is Out of Range.");
        }
        return value;
    },
    dispose: function() {
        /// <summary>
        ///     Dispose of any local resources.
        /// </summary>
        UserPCSite.PremiumOfferData.callBaseMethod(this, 'dispose');
    }
}

UserPCSite.PremiumOfferData.prototype.get_json = function() { return this._json; };
UserPCSite.PremiumOfferData.prototype.set_json = function(json) { this._json = json; }
/// <field name="json">The source data element to use.</field>

UserPCSite.PremiumOfferData.registerClass('UserPCSite.PremiumOfferData', Sys.Component, UserPCSite.IThumbnailData);

UserPCSite.PremiumOfferData.prototype.locDict = null;
/// <field name="locDict">The dictionary to receive localized strings.</field>


/// <reference name="MicrosoftAjax.js"/>
/// <reference path="~/AjaxControls/InteractiveBaseControl.js" />
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  PremiumOfferView.js
*
*      Displays the details of an Application.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.PremiumOfferView = function(element) {
    /// <summary>
    ///     PremiumOfferView Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <param name="element">HTML DomElement to associate.</param>
    /// <returns>
    ///     A newly created PremiumOfferView object.
    /// </returns>
    UserPCSite.PremiumOfferView.initializeBase(this, [element]);
}

UserPCSite.PremiumOfferView.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///
        ///     Initializes the view.
        /// </summary>

        this._Loc = SiteCommon.Utils.JsonParser(this.get_controller().locDict);

        UserPCSite.PremiumOfferView.callBaseMethod(this, 'initialize');
    },
    _setupRender: function() {
        /// <summary>
        ///     Sets up the panel dom element.
        /// </summary>

        // Set the main CSS style for the element
        Sys.UI.DomElement.addCssClass(this.get_element(), 'AppDetailsView');

        // Create a table to control the main two columns, rather than hacking CSS.
        var table = document.createElement('table');
        Sys.UI.DomElement.addCssClass(table, 'AppDetailsView_table');
        var tbody = document.createElement('tbody');
        table.appendChild(tbody);
        var tr = document.createElement('tr');
        tbody.appendChild(tr);
        this.get_element().appendChild(table);

        // Create a td for the main content column
        var tdMainContent = document.createElement('td');
        tdMainContent.vAlign = "top";
        Sys.UI.DomElement.addCssClass(tdMainContent, 'AppDetailsView_mainContent_td');
        tr.appendChild(tdMainContent);

        // Create a div for the main content.
        var divMainContent = document.createElement('div');
        Sys.UI.DomElement.addCssClass(divMainContent, 'AppDetailsView_mainContent');
        tdMainContent.appendChild(divMainContent);

        // Create a div for the right content column
        var tdRightColumn = document.createElement('td');
        tdRightColumn.vAlign = "top";
        Sys.UI.DomElement.addCssClass(tdRightColumn, 'AppDetailsView_rightColumn');
        tr.appendChild(tdRightColumn);

        // Create a div for the screenshots
        this._divScreenShots = document.createElement('div');
        Sys.UI.DomElement.addCssClass(this._divScreenShots, 'AppDetailsView_screenShots');
        Sys.UI.DomElement.setVisibilityMode(this._divScreenShots, Sys.UI.VisibilityMode.collapse);
        Sys.UI.DomElement.setVisible(this._divScreenShots, false);
        tdRightColumn.appendChild(this._divScreenShots);

        // Create a div for status information (loading, failed, no results, etc)
        this._divStatusInfo = document.createElement('div');
        Sys.UI.DomElement.setVisibilityMode(this._divStatusInfo, Sys.UI.VisibilityMode.collapse);
        divMainContent.appendChild(this._divStatusInfo);
        this.displayLoading();

        // Create a div for the thumbnail data
        this._divThumbnail = document.createElement('div');
        this._divThumbnail.setAttribute('id', this.get_id() + '_thumbnail');
        Sys.UI.DomElement.addCssClass(this._divThumbnail, 'AppDetailsView_thumbnail');
        divMainContent.appendChild(this._divThumbnail);

        // cancel button
        this._divCancelButton = document.createElement('div');
        Sys.UI.DomElement.addCssClass(this._divCancelButton, 'PremiumOfferCancelButton');
        divMainContent.appendChild(this._divCancelButton);

        var cancelButton = SiteCommon.Utils.CreateButton(this._Loc.Cancel, this.get_id() + '_Cancel', 'WhiteButton', '');
        this._divCancelButton.appendChild(cancelButton);

        var retUrl = this.get_controller().returnUrl;
        this._divCancelButton.onclick = function() { window.location = retUrl + "&result=cancel"; };


        // Create a div for the thumbnail divider
        this._divDivider = document.createElement('div');
        divMainContent.appendChild(this._divDivider);

        // Create a div for the thumbnail divider
        this._divDividerBottom = document.createElement('div');
        divMainContent.appendChild(this._divDividerBottom);

        // Setup the UI divider.
        Sys.UI.DomElement.addCssClass(this._divDivider, 'AppDetailsView_divider');
        Sys.UI.DomElement.addCssClass(this._divDividerBottom, 'AppDetailsView_dividerBottom');

        // Create a div for the details header
        var divDetailsHeader = document.createElement('div');
        Sys.UI.DomElement.addCssClass(divDetailsHeader, 'AppDetailsView_detailsHeader uppercase');
        divDetailsHeader.innerHTML = this._Loc.Details;
        divMainContent.appendChild(divDetailsHeader);

        // Create a div for the description
        var divDescription = document.createElement('div');
        Sys.UI.DomElement.addCssClass(divDescription, 'AppDetailsView_description');
        divMainContent.appendChild(divDescription);

        // Create a div for the description header
        var divDescriptionHeader = document.createElement('div');
        Sys.UI.DomElement.addCssClass(divDescriptionHeader, 'AppDetailsView_description_header');
        divDescriptionHeader.innerHTML = this._Loc.Description;
        divDescription.appendChild(divDescriptionHeader);

        // Create a div for the description data
        this._pDescriptionParagraph = document.createElement('p');
        Sys.UI.DomElement.addCssClass(this._pDescriptionParagraph, 'AppDetailsView_description_paragraph');
        divDescription.appendChild(this._pDescriptionParagraph);

        // Create a div for the application specifics
        this._divApplicationSpecifics = document.createElement('div');
        Sys.UI.DomElement.addCssClass(this._divApplicationSpecifics, 'AppDetailsView_applicationSpecifics');
        divMainContent.appendChild(this._divApplicationSpecifics);
    },
    dataRender: function() {
        /// <summary>
        ///     Update the UI with main data components from controller.
        /// </summary>

        // Get reference to data element.
        var data = this.get_controller().get_data();

        // Hide the status information
        Sys.UI.DomElement.setVisible(this._divStatusInfo, false);

        // Create top thumbnail data.
        this._thumbnail = $create(UserPCSite.ThumbnailView,
        { 'dataObject': data,
            'dataContext': null,
            'cssType': UserPCSite.ThumbnailView.thumbnailType.details,
            'controller': this.get_controller(),
            'locDict': this.get_controller().locDict
        },
        {},
        {},
        this._divThumbnail);

        // Set the app description
        this._pDescriptionParagraph.appendChild(data.getThumbnailDescriptionUI());

        // Features
        if (data.getFeaturesCount() > 0) {
            var ulList = document.createElement('ul');
            this._divApplicationSpecifics.appendChild(ulList);

            for (var i = 0; i < data.getFeaturesCount(); i++) {
                if (data.getFeature(i).length > 0) {
                    var liItem = document.createElement('li');
                    liItem.innerHTML = data.getFeature(i);
                    ulList.appendChild(liItem);
                }
            }

            var divFeatures = this.createDetailedListItem(this._Loc.Features, ulList);
            Sys.UI.DomElement.addCssClass(divFeatures, 'AppDetailsView_applicationSpecifics_features');
            this._divApplicationSpecifics.appendChild(divFeatures);
        }

        // Fill in application details
        var divReleased = this.createDetailedListItem(this._Loc.Released, data.getDateAddedUI(this._Loc.ShortDateFormat));
        Sys.UI.DomElement.addCssClass(divReleased, 'AppDetailsView_applicationSpecifics_released');
        this._divApplicationSpecifics.appendChild(divReleased);

        var divVersion = this.createDetailedListItem(this._Loc.Version, data.getVersionUI());
        Sys.UI.DomElement.addCssClass(divVersion, 'AppDetailsView_applicationSpecifics_version');
        this._divApplicationSpecifics.appendChild(divVersion);

        // Add screenshot
        if (data.getScreenshotCount() > 0) {
            var img = document.createElement('img');
            img.setAttribute('src', data.getScreenshotPath(0));
            img.setAttribute('alt', data.getThumbnailNameUI().data);
            Sys.UI.DomElement.addCssClass(img, 'AppDetailsView_screenShots_single');
            this._divScreenShots.appendChild(img);
            Sys.UI.DomElement.setVisible(this._divScreenShots, true);
        }

    },
    resetRender: function() {
        /// <summary>
        ///     Reset the UI to initial state.
        /// </summary>

        delete this._thumbnail;
        this._thumbnail = null;
        this._divThumbnail.innerHTML = "";
        this._pDescriptionParagraph.innerHTML = "";
        this._divApplicationSpecifics.innerHTML = "";
        this._divScreenShots.innerHTML = "";
    },
    displayLoading: function() {
        /// <summary>
        ///     Displays loading message.
        /// </summary>

        this._divStatusInfo.innerHTML = "";
        var div = SiteCommon.Utils.createLoadingDiv(this._Loc.Loading);
        this._divStatusInfo.appendChild(div);
        Sys.UI.DomElement.setVisible(this._divStatusInfo, true);
    },
    displayFailed: function(response) {
        /// <summary>
        ///     Displays failure message.
        /// </summary>

        this._divStatusInfo.innerHTML = "";
        var reloadDivObj = SiteCommon.Utils.createReloadDiv(this.get_id(), this._Loc.Refresh, this._Loc.ServerBusy);
        var panelId = this.get_id();
        reloadDivObj.reloadButton.onclick = function() { UserPCSite.PremiumOfferView.reloadClicked(panelId); return false; };
        this._divStatusInfo.appendChild(reloadDivObj.div);
        Sys.UI.DomElement.setVisible(this._divStatusInfo, true);

        // display error message
        SiteCommon.Utils.displayErrorMessagePopup(SiteCommon.Utils.toUIString(this._Loc.AppDetailsError), SiteCommon.Utils.toUIString(this._Loc.ERROR_GENERIC_DESCRIPTION));
    },
    createDetailedListItem: function(header, value) {
        /// <summary>
        ///     Setup the layout for a single application detail
        /// </summary>
        /// <param name="header">Label for the item.</param>
        /// <param name="value">Value to display with the label.</param>
        /// <returns>DOM element of row data</returns>
        var divRow = document.createElement('div');
        Sys.UI.DomElement.addCssClass(divRow, 'AppDetailsView_applicationSpecifics_row');
        var spanHeader = document.createElement('span');
        Sys.UI.DomElement.addCssClass(spanHeader, 'AppDetailsView_applicationSpecifics_header');
        spanHeader.appendChild(document.createTextNode(header + ": "));
        divRow.appendChild(spanHeader);
        var spanValue = document.createElement('span');
        Sys.UI.DomElement.addCssClass(spanValue, 'AppDetailsView_applicationSpecifics_value');
        spanValue.appendChild(value);
        divRow.appendChild(spanValue);

        return divRow;
    },
    dispose: function() {
        /// <summary>
        ///     Called to dispose of this objects resources.
        /// </summary>
        UserPCSite.PremiumOfferView.callBaseMethod(this, 'dispose');
    }
}

UserPCSite.PremiumOfferView.reloadClicked = function(viewId) {
    /// <summary>
    ///     Static method to handle reload clicks.
    /// </summary>
    /// <param name="viewId">ID of the view component.</param>
    var view = $find(viewId);
    var controller = view.get_controller();

    controller.reloadClicked();
};

UserPCSite.PremiumOfferView.registerClass('UserPCSite.PremiumOfferView', SiteCommon.InteractiveBaseView);

/// <reference name="MicrosoftAjax.js"/>
/// <reference path="~/AjaxControls/IThumbnailData.js"/>
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  ThumbnailData.js
*
*      Processes the Thumbnail data, abstracting it from the UI.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.ThumbnailData = function() {
    /// <summary>
    ///     ThumbnailController Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <returns>
    ///     A newly created ThumbnailData object.
    /// </returns>
    UserPCSite.ThumbnailData.initializeBase(this);
}

UserPCSite.ThumbnailData.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///     
        ///     Initializes the object.
        /// </summary>
        this._Loc = SiteCommon.Utils.JsonParser(this.locDict);

        UserPCSite.ThumbnailData.callBaseMethod(this, 'initialize');
    },
    getDataContext: function(thumbnailContainer, index) {
        return { 'thumbnailContainer': thumbnailContainer, 'index': index };
    },
    getLength: function(thumbnailContainer) {
        /// <summary>
        ///     Gets the number of items currently acquired.
        /// </summary>
        /// <returns>Count of items acquired.</returns>
        var length = 0;
        if (!SiteCommon.Utils.isNullOrUndefined(this._json)) {
            if ((thumbnailContainer == UserPCSite.ThumbnailData.thumbnailContainer.regular)
                && !SiteCommon.Utils.isNullOrUndefined(this._json.ItemList)) {
                length = this._json.ItemList.NumItems;
            }
            else if (thumbnailContainer == UserPCSite.ThumbnailData.thumbnailContainer.featured) {
                //Disabling featured apps section here by returning 0
                //return length of array from this._getFeaturedList() to enable featured apps section
                length = 0;
            }
        }
        return length;
    },
    getCategoryName: function(value) {
        /// <summary>
        ///     Get the name of the category queried.
        /// </summary>
        /// <param name="index">index of the value to get</param>
        /// <param name="sender">optional - the caller</param>
        /// <returns>Name the category or parent category</returns>
        if (!SiteCommon.Utils.isNullOrUndefined(this._json.ItemList.List[value])) {
            var returnValue = (this.parentCategoryId != "") ? SiteCommon.Utils.toUIString(this._json.ItemList.List[value].CategoryName) : SiteCommon.Utils.toUIString(this._json.ItemList.List[value].ParentCategoryName);
            if (returnValue.data == '') {
                if (SiteCommon.Utils.toUIString(this._json.ItemList.List[value].ParentCategoryName).data == '') {
                    returnValue = SiteCommon.Utils.toUIString(this._json.ItemList.List[value].CategoryName);
                } else {
                    returnValue = SiteCommon.Utils.toUIString(this._json.ItemList.List[value].ParentCategoryName);
                }
            }
            return returnValue;
        } else {
            //return empty string
            return SiteCommon.Utils.toUIString("");
        }

    },
    getThumbnailName: function(dataContext) {
        /// <summary>
        ///     Get the name of a thumbnail.
        /// </summary>
        /// <param name="dataContext">dataContext of the referenced thumbnail</param>
        /// <returns>Name value of the thumbnail.</returns>
        return this._getItem(dataContext).Name;
    },
    getThumbnailNameUI: function(dataContext) {
        /// <summary>
        ///     Get the name of a thumbnail.
        /// </summary>
        /// <param name="dataContext">dataContext of the referenced thumbnail</param>
        /// <returns>Name value of the thumbnail.</returns>
        return SiteCommon.Utils.toUIString(this._getItem(dataContext).Name);
    },
    getThumbnailImagePath: function(dataContext) {
        /// <summary>
        ///     Get the image url of a thumbnail.
        /// </summary>
        /// <param name="dataContext">dataContext of the referenced thumbnail</param>
        /// <returns>ImagePath value of the thumbnail.</returns>
        return this._getItem(dataContext).SmallImageUrl;
    },
    getThumbnailRating: function(dataContext) {
        /// <summary>
        ///     Get the rating of a thumbnail.
        /// </summary>
        /// <param name="dataContext">dataContext of the referenced thumbnail</param>
        /// <returns>Rating value of the thumbnail.</returns>
        return Math.round(this._getItem(dataContext).Rating / 100);
    },
    getThumbnailReviewCount: function(dataContext) {
        /// <summary>
        ///     Get the ReviewCount
        /// </summary>
        /// <param name="dataContext">dataContext of the referenced thumbnail</param>
        /// <returns>ReviewCount value.</returns>
        return this._getItem(dataContext).NumOfReviews;
    },
    getThumbnailReviewCountUI: function(dataContext) {
        /// <summary>
        ///     Get the ReviewCount of a thumbnail.
        /// </summary>
        /// <param name="dataContext">dataContext of the referenced thumbnail</param>
        /// <returns>ReviewCount value of the thumbnail.</returns>
        return SiteCommon.Utils.toUIString(this._getItem(dataContext).NumOfReviews);
    },
    getThumbnailCompanyNameUI: function(dataContext) {
        /// <summary>
        ///     Get the Company Name of a thumbnail.
        /// </summary>
        /// <param name="dataContext">dataContext of the referenced thumbnail</param>
        /// <returns>Company Name value of the thumbnail.</returns>
        return SiteCommon.Utils.toUIString(this._getItem(dataContext).CompanyName);
    },
    getThumbnailCategory: function(dataContext) {
        /// <summary>
        ///     Get the Category name.
        /// </summary>
        /// <param name="dataContext">dataContext of the referenced thumbnail</param>
        /// <returns>Category name string.</returns>
        return this._getItem(dataContext).CategoryName;
    },
    getThumbnailCategoryUI: function(dataContext) {
        /// <summary>
        ///     Get the Category of a thumbnail.
        /// </summary>
        /// <param name="dataContext">dataContext of the referenced thumbnail</param>
        /// <returns>Category value of the thumbnail.</returns>
        return SiteCommon.Utils.toUIString(this._getItem(dataContext).CategoryName);
    },
    getThumbnailDescriptionUI: function(dataContext) {
        /// <summary>
        ///     Get the Description of a thumbnail.
        /// </summary>
        /// <param name="dataContext">dataContext of the referenced thumbnail</param>
        /// <returns>Description value of the thumbnail.</returns>
        return SiteCommon.Utils.toUIString(this._Loc.NotSupported);
    },
    getThumbnailPrice: function(dataContext) {
        /// <summary>
        ///     Get the price of a thumbnail.
        /// </summary>
        /// <param name="dataContext">dataContext of the referenced thumbnail</param>
        /// <returns>Price value of the thumbnail.</returns>
        var price = this._getItem(dataContext).Price;
        if (SiteCommon.Utils.isNullOrUndefined(price)) {
            price = 0;
        }
        return price;
    },
    getThumbnailPriceUI: function(dataContext) {
        /// <summary>
        ///     Get the price of a thumbnail.
        /// </summary>
        /// <param name="dataContext">dataContext of the referenced thumbnail</param>
        /// <returns>Price value of the thumbnail.</returns>
        var price = this._getItem(dataContext).Price;
        if (SiteCommon.Utils.isNullOrUndefined(price)) {
            price = this._Loc.Free;
        }
        return SiteCommon.Utils.toUIString(price);
    },
    getThumbnailPriceWithTaxUI: function(dataContext) {
        /// <summary>
        ///     Get the price of a thumbnail (including tax).
        /// </summary>
        /// <param name="dataContext">dataContext of the referenced thumbnail</param>
        /// <returns>Price value of the thumbnail.</returns>
        var price = this._getItem(dataContext).PriceWithTax;
        if (SiteCommon.Utils.isNullOrUndefined(price)) {
            price = this._Loc.Free;
        }
        return SiteCommon.Utils.toUIString(price);
    },
    getAppSKU: function(dataContext) {
        /// <summary>
        ///     Get the sku of a thumbnail.
        /// </summary>
        /// <param name="dataContext">dataContext of the referenced thumbnail</param>
        /// <returns>Sku of the thumbnail.</returns>
        return this._getItem(dataContext).Id;
    },
    getApplicationId: function(dataContext) {
        /// <summary>
        ///     Get the applicationId (CatalogId) of a thumbnail.
        /// </summary>
        /// <param name="dataContext">dataContext of the referenced thumbnail</param>
        /// <returns>ApplicationId (CatalogId) of the thumbnail.</returns>
        return this._getItem(dataContext).ApplicationId;
    },
    getPlatformTypeByID: function(dataContext) {
        /// <summary>
        ///     Get the Platform Type by ID.
        /// </summary>
        /// <param name="dataContext">DataContext required to return the thumbnail data.</param>
        /// <returns>Platform Type ID</returns>
        return this._getItem(dataContext).PlatformTypeId;
    },
    getIsFeatured: function(dataContext) {
        /// <summary>
        ///     Get if the app is featured app.
        /// </summary>
        /// <param name="dataContext">DataContext required to return the thumbnail data.</param>
        /// <returns>Boolean isFeatured</returns>
        return this._getItem(dataContext).IsFeatured;
    },
    _getItem: function(dataContext) {
        /// <summary>
        ///     Get the item referenced by dataContext.
        /// </summary>
        /// <param name="dataContext">dataContext of the referenced thumbnail</param>
        /// <returns>Thumbnail Item at the specified dataContext.</returns>
        Sys.Debug.assert(typeof(dataContext) != 'undefined');
        var value = null;
        try {
            if (dataContext.thumbnailContainer == UserPCSite.ThumbnailData.thumbnailContainer.regular) {
                value = this._json.ItemList.List[dataContext.index];
            }
            else if (dataContext.thumbnailContainer == UserPCSite.ThumbnailData.thumbnailContainer.featured) {
                var list = this._getFeaturedList(dataContext.thumbnailContainer);
                if (!SiteCommon.Utils.isNullOrUndefined(list)) {
                    value = list[dataContext.index];
                }
            }
        }
        catch (exception) {
            throw ("Thumbnail Data Index is Out of Range.");
        }
        return value;
    },
    _getFeaturedList: function(thumbnailContainer) {
        /// <summary>
        ///     Get the list of featured apps.
        /// </summary>
        /// <param name="thumbnailContainer">
        /// Type of thumbnail should be 
        /// UserPCSite.ThumbnailData.thumbnailContainer.featured
        /// </param>
        /// <returns>List of featured apps</returns>
        if (thumbnailContainer == UserPCSite.ThumbnailData.thumbnailContainer.featured) {
            if (SiteCommon.Utils.isNullOrUndefined(this._featuredList)) {
                this._featuredList = new Array();
                for (var i = 0; i < this.getLength(UserPCSite.ThumbnailData.thumbnailContainer.regular); i++) {
                    if (this._json.ItemList.List[i].IsFeatured) {
                        this._featuredList[this._featuredList.length] = this._json.ItemList.List[i];
                    }
                }
            }
            return this._featuredList;
        }
        else {
            return null;
        }
    },
    dispose: function() {
        /// <summary>
        ///     Dispose of any local resources.
        /// </summary>
        UserPCSite.ThumbnailData.callBaseMethod(this, 'dispose');
    }
}

UserPCSite.ThumbnailData.prototype.get_json = function() { return this._json; };
UserPCSite.ThumbnailData.prototype.set_json = function(json) { this._json = json; }
/// <field name="json">The source data element to use.</field>
UserPCSite.ThumbnailData.prototype.chartId = "";
/// <field name="chartName">The name of the chart.</field>

UserPCSite.ThumbnailData.prototype.locDict = null;
/// <field name="locDict">The dictionary to receive localized strings.</field>

UserPCSite.ThumbnailData.prototype.parentCategoryId = "";
/// <field name="parentCategoryId">The parent category id from query string</field>

UserPCSite.ThumbnailData.prototype.chartName = "";
/// <field name="chartName">represent heading of chart sections</field>

UserPCSite.ThumbnailData.thumbnailContainer = { 'regular': 'regular', 'featured': 'featured' };
/// <field name="thumbnailContainer">A static list of possible thumbnail types.</field>

UserPCSite.ThumbnailData.registerClass('UserPCSite.ThumbnailData', Sys.Component, UserPCSite.IThumbnailData);


/// <reference name="MicrosoftAjax.js"/>
/// <reference path="~/AjaxControls/InteractiveBaseControl.js" />
/// <reference path="~/AjaxControls/Utils.js" />
/// <reference path="~/AjaxControls/PurchaseView.js" />
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  ThumbnailView.js
*
*      Displays an Application Thumbnail.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.ThumbnailView = function(element) {
    /// <summary>
    ///     ThumbnailView Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <param name="element">HTML DomElement to associate.</param>
    /// <returns>
    ///     A newly created ThumbnailView object.
    /// </returns>
    UserPCSite.ThumbnailView.initializeBase(this, [element]);
}

UserPCSite.ThumbnailView.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///     
        ///     Initializes the panel.
        /// </summary>

        this._Loc = SiteCommon.Utils.JsonParser(this.locDict);

        UserPCSite.ThumbnailView.callBaseMethod(this, 'initialize');

        // Setup default click handler.
        if (this.cssType != UserPCSite.ThumbnailView.thumbnailType.details) {
            this.add_click(UserPCSite.ThumbnailView.onClick);
            this.add_hover(UserPCSite.ThumbnailView.onHover);
            this.add_unhover(UserPCSite.ThumbnailView.onUnhover);
        }
    },
    dispose: function() {
        /// <summary>
        ///     Called to dispose of this objects resources.
        /// </summary>
        UserPCSite.ThumbnailView.callBaseMethod(this, 'dispose');
    },
    _setupRender: function() {
        /// <summary>
        ///     Sets up the thumbnails dom element.
        /// </summary> 

        // Note: we still need isDetails for the PremiumOffer page (but not for details.aspx), 
        // but if we ever refactor PremiumOffer to be like details.aspx, we can remove all the 
        // isDetails specialization from this file. Currently ThumbnailView handles several different 
        // types of thumbnail UIs, and the details mode UI is not really even a thumbnail and has 
        // little in common with the category/featured view thumbnails.
        var isDetails = (this.cssType == 'details');

        // Set the css style for the wrapping div element
        Sys.UI.DomElement.addCssClass(this.get_element(), 'ThumbnailView_base ThumbnailView_' + this.cssType);
        if (!isDetails) {
            Sys.UI.DomElement.addCssClass(this.get_element(), 'CursorHand');
        }

        // Create the html for all required data elements.  All supported data elements will be listed
        // as the style will dictate which ones will be displayed.
        var divName = document.createElement('a');
        if(!isDetails)
        {
            divName.setAttribute("href", UserPCSite.Utils.getDetailsURL(this._dataObject.getApplicationId(this._dataContext)));
        }
        Sys.UI.DomElement.addCssClass(divName, 'ThumbnailView_base_name ThumbnailView_' + this.cssType + '_name');
        if (isDetails) {
            divName.appendChild(
                SiteCommon.Utils.boldFirstWord(
                    SiteCommon.Utils.toUIString(
                        SiteCommon.Utils.AddEllipses(
                            this._dataObject.getThumbnailName(this._dataContext),
                            UserPCSite.ThumbnailView.thumbnailNameLength.details))));
        }
        else {
            var ellipseLength = this._dataObject.getThumbnailName(this._dataContext).length;
            switch (this.cssType) {
                case UserPCSite.ThumbnailView.thumbnailType.basic:
                case UserPCSite.ThumbnailView.thumbnailType.title:
                    {
                        ellipseLength = UserPCSite.ThumbnailView.thumbnailNameLength.home;
                        break;
                    }
                default:
                    {
                        ellipseLength = UserPCSite.ThumbnailView.thumbnailNameLength.browse;
                        break;
                    }
            }
            divName.appendChild(
                    SiteCommon.Utils.toUIString(
                        SiteCommon.Utils.AddEllipses(this._dataObject.getThumbnailName(this._dataContext), ellipseLength)));
        }
        this.get_element().appendChild(divName);

        var img = document.createElement('img');
        Sys.UI.DomElement.addCssClass(img, 'ThumbnailView_base_image ThumbnailView_' + this.cssType + '_image');
        img.setAttribute('src', SiteCommon.Utils.FixUrlProtocol(this._dataObject.getThumbnailImagePath(this._dataContext)));
        img.setAttribute('alt', this._dataObject.getThumbnailNameUI(this._dataContext).data);
        this.get_element().appendChild(img);

        var divRating = SiteCommon.Utils.CreateRatings(this.get_element(), this.get_id() + '_ratingLabel', 'ThumbnailView_base_rating ThumbnailView_' + this.cssType + '_rating',
                        this._dataObject.getThumbnailRating(this._dataContext));

        var divReviewCount = document.createElement('div');
        Sys.UI.DomElement.addCssClass(divReviewCount, 'ThumbnailView_base_reviewCount ThumbnailView_' + this.cssType + '_reviewCount');
        divReviewCount.appendChild(SiteCommon.Utils.toUIString('(' + this._dataObject.getThumbnailReviewCount(this._dataContext) + ')'));
        this.get_element().appendChild(divReviewCount);

        var divCompanyName = document.createElement('div');
        Sys.UI.DomElement.addCssClass(divCompanyName, 'ThumbnailView_base_companyName ThumbnailView_' + this.cssType + '_companyName');
        divCompanyName.appendChild(this._dataObject.getThumbnailCompanyNameUI(this._dataContext));
        this.get_element().appendChild(divCompanyName);

        var divCategory = document.createElement('div');
        Sys.UI.DomElement.addCssClass(divCategory, 'ThumbnailView_base_category ThumbnailView_' + this.cssType + '_category');
        var categoryName = SiteCommon.Utils.AddEllipses(this._dataObject.getThumbnailCategory(this._dataContext), 
                                                        UserPCSite.ThumbnailView.thumbnailNameLength.categoryname);
        divCategory.appendChild(SiteCommon.Utils.toUIString(categoryName));
        this.get_element().appendChild(divCategory);

        var divPrice = document.createElement('div');
        Sys.UI.DomElement.addCssClass(divPrice, 'ThumbnailView_base_price ThumbnailView_' + this.cssType + '_price');
        divPrice.appendChild(this._dataObject.getThumbnailPriceUI(this._dataContext));
        this.get_element().appendChild(divPrice);

        if ((this.cssType == UserPCSite.ThumbnailView.thumbnailType.category)
            || (this.cssType == UserPCSite.ThumbnailView.thumbnailType.details)) 
        {
            var divPurchase = document.createElement('div');
            Sys.UI.DomElement.addCssClass(divPurchase, 'ThumbnailView_base_purchase ThumbnailView_' + this.cssType + '_purchase');
            var isLargeSize = false;
            if (this.cssType == UserPCSite.ThumbnailView.thumbnailType.details) {
                isLargeSize = true;
            }

            //render 'buy' button
            var platformTypeId = 0;
            var displayText = '';
            var isCompatible = true;
            var cssValue = '';

            if (isDetails) {
                //App detail page; it returns single result
                displayText = this._Loc.DeviceNotMatched;
                cssValue = 'AppDetailsView_NotCompatibleWithDevice';
                platformTypeId = this._dataObject.getPlatformTypeByID();
            } else {
                displayText = this._Loc.DeviceNotMatchedShort;
                cssValue = 'AppDetailsView_NotCompatibleWithDeviceShort';
                platformTypeId = this._dataObject.getPlatformTypeByID(this._dataContext);
            }

            switch (platformTypeId) {
                //note that this will only 'break' when it is mis-matched; otherwise still goto default case.                      
                case UserPCSite.ThumbnailView.SupportedPlatformContainer.PocketPC:
                    {
                        if (SiteCommon.Utils.readCookie('DevicePlatform') == 'SmartPhone') {
                            divPurchase.appendChild(SiteCommon.Utils.CreateDiv(null, this.get_id(), cssValue, SiteCommon.Utils.toUIString(displayText)));
                            isCompatible = false;
                        }
                        break;
                    }
                case UserPCSite.ThumbnailView.SupportedPlatformContainer.SmartPhone:
                    {
                        if (SiteCommon.Utils.readCookie('DevicePlatform') == 'PocketPC') {
                            divPurchase.appendChild(SiteCommon.Utils.CreateDiv(null, this.get_id(), cssValue, SiteCommon.Utils.toUIString(displayText)));
                            isCompatible = false;
                        }
                        break;
                    }
            }

            if (isCompatible &&
                Boolean.parse(this.get_controller().HasUserSelectedDevice)) {
                //verify app compatibility and that the user has a device to use

                this._purchase = $create(UserPCSite.PurchaseView,
                         { 
                             'isLargeSize': isLargeSize,
                             'locDict': this.locDict,
                             'AppSKU'                : this.get_controller().get_data().getAppSKU(this.get_dataContext()),
                             'ThumbnailPrice'        : this.get_controller().get_data().getThumbnailPrice(this.get_dataContext()),
                             'ThumbnailName'         : this.get_controller().get_data().getThumbnailName(this.get_dataContext()),
                             'ThumbnailImagePath'    : this.get_controller().get_data().getThumbnailImagePath(this.get_dataContext()),
                             'ThumbnailCompanyName'  : this.get_controller().get_data().getThumbnailCompanyNameUI(this.get_dataContext()).data,
                             'ThumbnailPriceDisplay' : this.get_controller().get_data().getThumbnailPriceUI(this.get_dataContext()).data,
                             'ThumbnailPriceWithTaxDisplay': this.get_controller().get_data().getThumbnailPriceWithTaxUI(this.get_dataContext()).data,
                             'deviceId'              : this.get_controller().deviceId,
                             'websiteDeviceId'       : this.get_controller().websiteDeviceId,
                             'supportUrl'            : this.get_controller().supportUrl,
                             'transactionID'         : this.get_controller().transactionID, // available with PremiumOfferController only.
                             'puid'                  : this.get_controller().puid, // available with PremiumOfferController only.
                             'returnUrl'             : this.get_controller().returnUrl // available with PremiumOfferController only.
                         },
                         {},
                         {},
                         divPurchase);
            }

            this.get_element().appendChild(divPurchase);
        }

        // Create the hover thumbnail box
        this._thumbnailViewHoverItem = document.createElement('div');
        Sys.UI.DomElement.setVisibilityMode(this._thumbnailViewHoverItem, Sys.UI.VisibilityMode.hide);
        Sys.UI.DomElement.setVisible(this._thumbnailViewHoverItem, false);
        Sys.UI.DomElement.addCssClass(this._thumbnailViewHoverItem, 'ThumbnailView_base_hover_box '
                                            + 'ThumbnailView_' + this.cssType + '_hover_box');
        var divBg = document.createElement('div');
        Sys.UI.DomElement.addCssClass(divBg, 'ThumbnailView_base_hover_box_bg');
        this._thumbnailViewHoverItem.appendChild(divBg);
        this.get_element().appendChild(this._thumbnailViewHoverItem);

        if (!SiteCommon.Utils.isNullOrUndefined(this.get_controller().showFeatured)
            && this.get_controller().showFeatured
            && this._dataObject.getIsFeatured(this._dataContext)) {
            // Create the featured apps highlighting box
            this._thumbnailFeaturedItem = document.createElement('div');
            Sys.UI.DomElement.setVisibilityMode(this._thumbnailFeaturedItem, Sys.UI.VisibilityMode.hide);
            Sys.UI.DomElement.setVisible(this._thumbnailFeaturedItem, true);
            Sys.UI.DomElement.addCssClass(this._thumbnailFeaturedItem, 'ThumbnailView_base_featured_box');
            this.get_element().appendChild(this._thumbnailFeaturedItem);
        }
    },
    dataRender: function() {
        /* Declared for interface */
    },
    updatePurchaseState: function(context) {
        ///<summary>
        ///Checks if the purchase controller if present, 
        /// and sends the call to its updatePurchaseState method.
        ///</summary>        

        if (!SiteCommon.Utils.isNullOrUndefined(context._purchase)) {
            context._purchase.updatePurchaseState(context._purchase);
        }
    }
}

UserPCSite.ThumbnailView.onClick = function(sender, args) {
    /// <summary>
    ///     Click event handler.
    /// </summary>
    /// <param name="sender">ThumbnailView object that was clicked.</param>
    /// <param name="args">Event args.</param>

    // Notify the controller that the thumbnail was clicked.   
    sender.get_controller().thumbnailClicked(sender._dataContext);
},
UserPCSite.ThumbnailView.onHover = function(sender, args) {
    /// <summary>
    ///     Hover event handler.
    /// </summary>
    /// <param name="sender">ThumbnailView object that is hovered.</param>
    /// <param name="args">Event args.</param>

    // Set selection UI on current element.
    Sys.UI.DomElement.setVisible(sender._thumbnailFeaturedItem, false); 
    Sys.UI.DomElement.setVisible(sender._thumbnailViewHoverItem, true);
},
UserPCSite.ThumbnailView.onUnhover = function(sender, args) {
    /// <summary>
    ///     Unhover event handler.
    /// </summary>
    /// <param name="sender">ThumbnailView object that was previously hovered.</param>
    /// <param name="args">Event args.</param>

    // Remove the selection UI on the current element.
    Sys.UI.DomElement.setVisible(sender._thumbnailFeaturedItem, true); 
    Sys.UI.DomElement.setVisible(sender._thumbnailViewHoverItem, false);
}

UserPCSite.ThumbnailView.prototype.cssType = '';
/// <field name="cssType">The style of thumbnail to display.</field>
UserPCSite.ThumbnailView.prototype.get_dataObject = function() { return this._dataObject; }
UserPCSite.ThumbnailView.prototype.set_dataObject = function(dataObject) { this._dataObject = dataObject; }
/// <field name="dataObject">The data object to get application information from.</field>
UserPCSite.ThumbnailView.prototype.get_dataContext = function() { return this._dataContext; }
UserPCSite.ThumbnailView.prototype.set_dataContext = function(dataContext) { this._dataContext = dataContext; }
/// <field name="dataContext">An object identifying the desired application, used to query dataObject data.</field>

UserPCSite.ThumbnailView.prototype.locDict = null;
/// <field name="locDict">The dictionary to receive localized strings.</field>

UserPCSite.ThumbnailView.thumbnailType = { 'basic': 'basic', 'category': 'category', 'details': 'details', 'title': 'title' };
/// <field name="thumbnailType">A static list of possible thumbnail view types.</field>

UserPCSite.ThumbnailView.thumbnailNameLength = { 'home': 15, 'browse': 22, 'details': 28, 'categoryname': 16};
/// <field name="thumbnailNameLength">A static list of possible thumbnail name lengths.</field>

UserPCSite.ThumbnailView.SupportedPlatformContainer = { 'PocketPC': 1, 'SmartPhone': 2 };
/// <field name="SupportedPlatformContainer">A static list of possible supported platform types.</field>

// Register the classname with the system
UserPCSite.ThumbnailView.registerClass('UserPCSite.ThumbnailView', SiteCommon.InteractiveBaseView);


/// <reference name="MicrosoftAjax.js"/>
/// <reference path="~/AjaxControls/ThumbnailView.js"/>
/// <reference path="~/AjaxControls/Utils.js"/>
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  ThumbnailPanelView.js
*
*      Displays a panel of Application Thumbnails.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.ThumbnailPanelView = function(element) {
    /// <summary>
    ///     ThumbnailPanelView Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <param name="element">HTML DomElement to associate.</param>
    /// <returns>
    ///     A newly created ThumbnailPanelView object.
    /// </returns>
    UserPCSite.ThumbnailPanelView.initializeBase(this, [element]);
}

UserPCSite.ThumbnailPanelView.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///     
        ///     Initializes the panel.
        /// </summary>

        // Initialize the private data members
        this._thumbnailViews = new Array();
        this._rowElements = new Array();
        this._featuredViews = new Array();
        this._featuredSlideTRs = new Array();

        this.add_click(this._onClick);

        this._Loc = SiteCommon.Utils.JsonParser(this.get_controller().locDict);

        UserPCSite.ThumbnailPanelView.callBaseMethod(this, 'initialize');
    },
    drawPagination: function(pageStart, pageEnd, pageSelected) {
        ///<summary>
        ///     Draws the pagination controls like:
        ///     Previous, 1, 2, 3, 4, ... N, Next
        /// </summary>
        var parent = this.get_element();
        var id = parent.getAttribute('id') + '_pagination';

        //Remove old pagination div from the view
        if (this._paginationDiv != null) {
            parent.removeChild(this._paginationDiv);
            delete this._paginationDiv;
        }

        this._paginationDiv = SiteCommon.Utils.CreateDiv(parent, id, 'PaginationContainer', null);

        var prevCss = 'sprite PaginationPreviousIcon FloatLeft';
        if (pageStart < pageSelected)
            prevCss += ' CursorHand PaginationPreviousIcon-active';
        var prevLink = SiteCommon.Utils.CreateLink(this._paginationDiv, id + '_previous', prevCss, null, null, null);
        if (pageStart < pageSelected) {
            this.addEventTarget(SiteCommon.InteractiveBaseView.eventTypes.click,
                                id + '_previous',
                                { 'eventType': 'pagination',
                                    'page': (pageSelected - 1)
                                });
        }
        else {
            // disable the link to remove it from the tab order (accessibility). Left as <a> rather than <div> for DOM consistency.
            prevLink.setAttribute('disabled', 'true'); 
        }

        for (var i = pageStart; i <= pageEnd; i++) {
            if (pageSelected == i) { // current page (no link)
                SiteCommon.Utils.CreateDiv(this._paginationDiv, id + i,
                        'PaginationEntry PaginationEntrySelected FloatLeft',
                        SiteCommon.Utils.toUIString(i));
            }
            else { 
                SiteCommon.Utils.CreateLink(this._paginationDiv, id + i,
                        'PaginationEntry CursorHand FloatLeft',
                        SiteCommon.Utils.toUIString(i), null, null);
                this.addEventTarget(SiteCommon.InteractiveBaseView.eventTypes.click,
                                id + i,
                                { 'eventType': 'pagination',
                                    'page': i
                                });
            }
        }

        var nextCss = 'sprite PaginationNextIcon FloatLeft';
        if (pageSelected < pageEnd)
            nextCss += ' CursorHand PaginationNextIcon-active';
        var nextLink = SiteCommon.Utils.CreateLink(this._paginationDiv, id + '_next', nextCss, null, null, null);
        if (pageSelected < pageEnd) {
            this.addEventTarget(SiteCommon.InteractiveBaseView.eventTypes.click,
                                id + '_next',
                                { 'eventType': 'pagination',
                                    'page': (pageSelected + 1)
                                });
        }
        else {
            // disable the link to remove it from the tab order (accessibility). Left as <a> rather than <div> for DOM consistency.
            nextLink.setAttribute('disabled', 'true'); 
        }

        //Add clear:both div
        SiteCommon.Utils.CreateDiv(this._paginationDiv, id + '_Clear', 'clear', null);

        // omniture tracking
        SiteCommon.Utils.OmnitureTrackThumbnailPage(pageSelected);

        return this._paginationDiv;
    },
    drawNavigator: function(pageSelected) {
        ///<summary>
        ///     Draws the pagination controls without page number markers, like:
        ///     Previous, Next
        ///
        ///     Parameters:
        ///     (0) => First page => Next active
        ///     (1) => Middle page => Next & Previous active
        ///     (2) => Last page => Previous active 
        ///     (-1) => Only page => NEITHER active
        /// </summary>
        var pageStart = 0;
        var pageEnd = 2;
        var parent = this._divPanelData; //this.get_element();
        var id = parent.getAttribute('id') + '_pagination';

        //Remove old pagination div from the view
        if (this._paginationDiv != null) {
            parent.removeChild(this._paginationDiv);
            delete this._paginationDiv;
            this._paginationDiv = null;
        }

        //Not adding navigation bar
        if (pageSelected < 0) {
            return null;
        }

        this._paginationDiv = SiteCommon.Utils.CreateDiv(parent, id, 'PaginationContainer', null);

        var content = SiteCommon.Utils.CreateDiv(this._paginationDiv, id + '_Content', 'PaginationContent', null);

        var prevCss = 'sprite PaginationPreviousIcon FloatLeft';
        if (pageStart < pageSelected)
            prevCss += ' CursorHand PaginationPreviousIcon-active';
        SiteCommon.Utils.CreateDiv(content, id + '_previous', prevCss, null);
        if (pageStart < pageSelected) {
            this.addEventTarget(SiteCommon.InteractiveBaseView.eventTypes.click,
                                id + '_previous',
                                { 'eventType': 'navigation',
                                    'button': 'previous'
                                });
        }

        var nextCss = 'sprite PaginationNextIcon FloatRight';
        if (pageSelected < pageEnd && pageSelected >= 0)
            nextCss += ' CursorHand PaginationNextIcon-active';
        SiteCommon.Utils.CreateDiv(content, id + '_next', nextCss, null);
        if (pageSelected < pageEnd && pageSelected >= 0) {
            this.addEventTarget(SiteCommon.InteractiveBaseView.eventTypes.click,
                                id + '_next',
                                { 'eventType': 'navigation',
                                    'button': 'next'
                                });
        }

        //Add clear:both div
        SiteCommon.Utils.CreateDiv(content, id + '_Clear', 'clear', null);

        return this._paginationDiv;
    },
    addThumbnail: function(dataObject, dataContext) {
        /// <summary>
        ///     Adds an application thumbnail to the panel grid.
        /// </summary>
        /// <param name="dataObject">A reference to the thumbnail data object.</param>
        /// <param name="dataContext">A value that can identify the data element to use.</param>

        // Add a regular thumbnail to the panel.
        if (dataContext.thumbnailContainer == UserPCSite.ThumbnailData.thumbnailContainer.regular) {
            // Find the index of the new thumbnail.
            var index = this._thumbnailViews.length;

            // Create table data element.
            var td = document.createElement('td');
            var cssClass = 'ThumbnailPanelView_td ThumbnailPanelView_' + this.panelType + '_td';

            // Determine the location in the grid to add the thumbnail.
            var rowIndex = parseInt(index / this.columns);
            var column = index % this.columns;
            if (column == 0) {
                // Create a new row.
                this._rowElements[rowIndex] = document.createElement('tr');
                Sys.UI.DomElement.addCssClass(this._rowElements[rowIndex], 'ThumbnailPanelView_tr ThumbnailPanelView_' + this.panelType + '_tr');
                this._tableBody.appendChild(this._rowElements[rowIndex]);
            }
            else {
                // Append horizontal spacing between the cells.
                cssClass += ' ThumbnailPanelView_' + this.panelType + '_td_leftSpacing';
            }

            if ((column == parseInt(this.columns / 2)) && (this.panelType == "ChartSplit")) {
                // Append horizontal spacing between the cells.
                cssClass += ' ThumbnailPanelView_' + this.panelType + '_td_splitSpacing';
            }

            // Add vertical spacing between the cells.
            cssClass += ' ThumbnailPanelView_' + this.panelType + '_td_bottomSpacing';

            // Assign the classes to the new table data element.
            Sys.UI.DomElement.addCssClass(td, cssClass);

            this._thumbnailViews[index] = this._createThumbnail(dataObject, dataContext);

            var div = this._thumbnailViews[index].get_element();

            div.setAttribute('id', this.get_id() + '_' + index);

            // Add thumbnail to the visual tree
            td.appendChild(div);

            this._rowElements[rowIndex].appendChild(td);

        }
        // Add a featured thumbnail to the panel
        else if (dataContext.thumbnailContainer == UserPCSite.ThumbnailData.thumbnailContainer.featured) {
            // Determine the index of the thumbnail, and which featured table it should belong to.
            var index = this._featuredViews.length;
            var slideIndex = parseInt(index / this.columns);
            var column = index % this.columns;
            if (column == 0) {
                // Create a new slide content element.
                var divWrapper = document.createElement('div');
                Sys.UI.DomElement.addCssClass(divWrapper, 'ThumbnailPanelView_featured ThumbnailPanelView_' + this.panelType + '_featured');
                this._divFeatureApps.appendChild(divWrapper);
                this._featuredTable = document.createElement('table');
                Sys.UI.DomElement.addCssClass(this._featuredTable, 'ThumbnailPanelView_table ThumbnailPanelView_featured_table ThumbnailPanelView_' + this.panelType + '_featured_table');
                var tbody = document.createElement('tbody');
                this._featuredTable.appendChild(tbody);
                this._featuredSlideTRs[slideIndex] = document.createElement('tr');
                Sys.UI.DomElement.addCssClass(this._featuredSlideTRs[slideIndex], 'ThumbnailPanelView_tr ThumbnailPanelView_featured_tr');

                tbody.appendChild(this._featuredSlideTRs[slideIndex]);

                divWrapper.appendChild(this._featuredTable);

                // add divWrapper to the slider
                this._contentSlider.addSlide(divWrapper);
            }

            var td = document.createElement('td');
            var cssClass = 'ThumbnailPanelView_td ThumbnailPanelView_featured_td';

            if (column != 0) {
                cssClass += ' ThumbnailPanelView_featured_td_leftSpacing';
            }
            // Assign the classes to the new table data element.
            Sys.UI.DomElement.addCssClass(td, cssClass);

            this._featuredViews[index] = this._createThumbnail(dataObject, dataContext);
            var div = this._featuredViews[index].get_element();
            div.setAttribute('id', this.get_id() + '_featured_' + index);
            td.appendChild(div);

            // Add cell to the appropriate slider element.
            this._featuredSlideTRs[slideIndex].appendChild(td);
        }

    },
    _createThumbnail: function(dataObject, dataContext) {
        /// <summary>
        ///     Creates an application thumbnail.
        /// </summary>
        /// <param name="dataObject">A reference to the thumbnail data object.</param>
        /// <param name="dataContext">A value that can identify the data element to use.</param>

        // Determine the desired thumbnailType
        var thumbnailType = "";
        switch (this.panelType) {
            case "CategoryView":
            case "SearchResults":
            case "FullChart":
                {
                    thumbnailType = UserPCSite.ThumbnailView.thumbnailType.category;
                    break;
                }
            case "Chart":
                {
                    thumbnailType = UserPCSite.ThumbnailView.thumbnailType.basic;
                    break;
                }
            case "ChartSplit":
                {
                    thumbnailType = UserPCSite.ThumbnailView.thumbnailType.title;
                    break;
                }
        }

        var div = document.createElement('div');
        Sys.UI.DomElement.addCssClass(div, 'ThumbnailPanelView_element');
        var thumbnail = $create(UserPCSite.ThumbnailView,
                                              { 'cssType': thumbnailType,
                                                  'dataObject': dataObject,
                                                  'dataContext': dataContext,
                                                  'controller': this.get_controller(),
                                                  'locDict': this.get_controller().locDict
                                              },
                                              {},
                                              {}, // No references
                                              div);

        if (!SiteCommon.Utils.isNullOrUndefined(UserPCSite.PurchasedAppController)
            && !SiteCommon.Utils.isNullOrUndefined(UserPCSite.PurchasedAppController.get_instance)) {
            //Register this thumbnail for purchase notifications.
            UserPCSite.PurchasedAppController.get_instance().registerThumbnailForNotifications(
                                        dataObject.getAppSKU(dataContext),
                                        thumbnail.updatePurchaseState,
                                        thumbnail);
        }
        //Update the purchase state
        thumbnail.updatePurchaseState(thumbnail);

        return thumbnail;
    },
    dispose: function() {
        /// <summary>
        ///     Called to dispose of this objects resources.
        /// </summary>
        UserPCSite.ThumbnailPanelView.callBaseMethod(this, 'dispose');
    },
    _setupRender: function() {
        /// <summary>
        ///     Sets up the panel dom element.
        /// </summary>

        // Set the main CSS style for the element
        Sys.UI.DomElement.addCssClass(this.get_element(), 'ThumbnailPanelView + ThumbnailPanelView_' + this.panelType);

        // Create a div for the Category Title and Filter button
        this._divTitleFilter = SiteCommon.Utils.CreateDiv(this.get_element(), this.get_element().getAttribute('id') + '_titleFilter',
                                                        'ThumbnailPanelView_titleFilter ThumbnailPanelView_' + this.panelType + '_titleFilter',
                                                        null);

        // Create a div for the top header bar
        var divHeaderBar = document.createElement('div');
        Sys.UI.DomElement.addCssClass(divHeaderBar, 'ThumbnailPanelView_headerBar ThumbnailPanelView_' + this.panelType + '_headerBar');
        this.get_element().appendChild(divHeaderBar);

        // Create a div for the remaining category data
        var divMainContents = SiteCommon.Utils.CreateDiv(this.get_element(), this.get_element().getAttribute('id') + '_mainContents',
                                                        'ThumbnailPanelView_mainContents ThumbnailPanelView_' + this.panelType + '_mainContents',
                                                        null);

        // Create a div for the indices of the resulting apps (x-y of z results)
        this._divIndexResults = document.createElement('div');
        Sys.UI.DomElement.addCssClass(this._divIndexResults, 'ThumbnailPanelView_indexResults ThumbnailPanelView_' + this.panelType + '_indexResults');
        divMainContents.appendChild(this._divIndexResults);

        // Create a panel for the application specific data
        this._divPanelData = SiteCommon.Utils.CreateDiv(divMainContents, divMainContents.getAttribute('id') + '_panelData',
                                                    'ThumbnailPanelView_panelData ThumbnailPanelView_' + this.panelType + '_panelData',
                                                    null);

        // Create a div for the Featured Apps (ContentSlider)
        if (this.showFeatured) {
            this._divFeatureApps = document.createElement('div');
            this._divFeatureApps.setAttribute('id', this.get_id() + '_contentSlider');
            Sys.UI.DomElement.addCssClass(this._divFeatureApps, 'ThumbnailPanelView_contentSlider ThumbnailPanelView_' + this.panelType + '_contentSlider');
            this._divPanelData.appendChild(this._divFeatureApps);
        }

        // Create a div for status information (loading, failed, no results, etc)
        this._divStatusInfo = document.createElement('div');
        Sys.UI.DomElement.setVisibilityMode(this._divStatusInfo, Sys.UI.VisibilityMode.collapse);
        this._divPanelData.appendChild(this._divStatusInfo);
        this.displayLoading();

        // Create a wrapper for the table data
        var divThumbnails = document.createElement('div');
        Sys.UI.DomElement.addCssClass(divThumbnails, 'ThumbnailPanelView_divThumbnails ThumbnailPanelView_' + this.panelType + '_divThumbnails');
        this._divPanelData.appendChild(divThumbnails);

        // Create a table to hold the application data
        var table = document.createElement('table');
        Sys.UI.DomElement.addCssClass(table, 'ThumbnailPanelView_table ThumbnailPanelView_' + this.panelType + '_table');
        divThumbnails.appendChild(table);

        // Initialize the table body for row and cell data
        this._tableBody = document.createElement('tbody');
        table.appendChild(this._tableBody);
    },
    dataRender: function() {
        /// <summary>
        ///     Update the UI with main data components from controller.
        /// </summary>

        var itemCount = this.get_controller().get_data().getLength(UserPCSite.ThumbnailData.thumbnailContainer.regular);
        if (itemCount > 0) {
            var offset = parseInt(this.get_controller().beforeMarker);
            var firstIndex = offset + 1;
            var secondIndex = offset + itemCount;
            this._divIndexResults.innerHTML = String.format(this._Loc.ShowingResults, firstIndex, secondIndex);
        }
        else {
            this._divIndexResults.innerHTML = ""
        }
        this.processViewName(this._divTitleFilter);

        // Hide the status UI
        Sys.UI.DomElement.setVisible(this._divStatusInfo, false);

        if (this.showFeatured && SiteCommon.Utils.isNullOrUndefined(this._contentSlider)) {
            this._contentSlider = $create(UserPCSite.ContentSliderView,
                                 { 'controller': this.get_controller(),
                                     'sliderType': "featured",
                                     'slideWidth': this.slideWidth
                                 },
                                 {},
                                 {},
                                 this._divFeatureApps);
        }
    },
    resetRender: function() {
        /// <summary>
        ///     Reset the UI to initial state.
        /// </summary>

        this._divIndexResults.innerHTML = "";

        for (var i = 0; i < this._rowElements.length; i++) {
            this._tableBody.removeChild(this._rowElements[i]);
        }
        delete this._rowElements;
        this._rowElements = new Array();
        for (var i = 0; i < this._thumbnailViews.length; i++) {
            delete this._thumbnailViews[i];
        }
        delete this._thumbnailViews;
        this._thumbnailViews = new Array();
    },
    displayLoading: function() {
        /// <summary>
        ///     Displays loading message.
        /// </summary>

        this._divStatusInfo.innerHTML = "";
        var div = SiteCommon.Utils.createLoadingDiv(this._Loc.Loading);
        this._divStatusInfo.appendChild(div);
        Sys.UI.DomElement.setVisible(this._divStatusInfo, true);
    },
    displayNoResults: function() {
        /// <summary>
        ///     Displays 'no results found'.
        /// </summary>

        this._divStatusInfo.innerHTML = this._Loc.NoResults;
        Sys.UI.DomElement.setVisible(this._divStatusInfo, true);
    },
    displayFailed: function() {
        /// <summary>
        ///     Displays failure message.
        /// </summary>

        this._divStatusInfo.innerHTML = "";
        var reloadDivObj = SiteCommon.Utils.createReloadDiv(this.get_id(), this._Loc.Refresh, this._Loc.ServerBusy);
        var panelId = this.get_id();
        reloadDivObj.reloadButton.onclick = function() { UserPCSite.ThumbnailPanelView.reloadClicked(panelId); return false; };
        this._divStatusInfo.appendChild(reloadDivObj.div);
        Sys.UI.DomElement.setVisible(this._divStatusInfo, true);
    },
    _onClick: function(sender, args) {
        /// <summary>
        ///     Click event handler for the list items.
        /// </summary>
        /// <param name="sender">ThumbnailView object that is clicked on.</param>
        /// <param name="args">Event args.</param>

        if (args.customData != null) {
            if (args.customData.eventType == 'pagination') {
                sender.get_controller().handlePagination(args.customData.page);
            }
            else if (args.customData.eventType == 'navigation') {
                sender.get_controller().handleNavigation(args.customData.button);
            }
        }
    },
    processViewName: function(div) {
        /// <summary>
        ///     Updates the name of the view as appropriate (search results, category or chart name, etc.
        /// </summary>
        var outerSpan = document.createElement('span');
        Sys.UI.DomElement.addCssClass(outerSpan, 'ThumbnailPanelView_titleFilter_category uppercase');

        if (this.panelType == "SearchResults") {
            var boldSpan = document.createElement('span');
            Sys.UI.DomElement.addCssClass(boldSpan, 'ThumbnailPanelView_titleFilter_category_bold');
            boldSpan.appendChild(document.createTextNode('"' + this.get_controller().keywords + '"'));
            outerSpan.appendChild(boldSpan);

            outerSpan.appendChild(document.createTextNode(' ' + this._Loc.Results));
        }
        else {
            //    add param to getCategoryName
            //this will display ParentCategoryName
            //When categoryID is empty that means the default page is being rendered.
            switch (this.panelType) {
                case "CategoryView":
                    {
                        //Passing in zero because ParentCategoryName result will always return same value for the entire resultset.
                        outerSpan.appendChild(SiteCommon.Utils.boldFirstWord(this.get_controller().get_data().getCategoryName(0)));
                        break;
                    }
                case "Chart":
                case "ChartSplit":
                case "FullChart":
                    {
                        outerSpan.appendChild(SiteCommon.Utils.boldFirstWord(SiteCommon.Utils.toUIString(this._controller.chartName)));
                        break;
                    }
            }
        }

        div.innerHTML = "";
        div.appendChild(outerSpan);
    }
}
UserPCSite.ThumbnailPanelView.prototype.rows = 0;
/// <field name="rows">The number of rows of apps to display.</field>
UserPCSite.ThumbnailPanelView.prototype.columns = 0;
/// <field name="columns">The number of columns of apps to display.</field>
UserPCSite.ThumbnailPanelView.prototype.panelType = 0;
/// <field name="panelType">Type of panel to display.</field>
UserPCSite.ThumbnailPanelView.prototype.showFeatured = 0;
/// <field name="showFeatured">Determines if a featured app panel should be displayed.</field>
UserPCSite.ThumbnailPanelView.prototype.slideWidth = 0;
/// <field name="slideWidth">Indicates the width of viewable area for the contentslider.</field>

UserPCSite.ThumbnailPanelView.reloadClicked = function(panelId) {
    /// <summary>
    ///     Static method to handle reload clicks.
    /// </summary>
    /// <param name="viewId">ID of the view component.</param>
    var panel = $find(panelId);
    var controller = panel.get_controller();

    controller.reloadClicked();
};

UserPCSite.ThumbnailPanelView.registerClass('UserPCSite.ThumbnailPanelView', SiteCommon.InteractiveBaseView);


/// <reference name="MicrosoftAjax.js"/>
/// <reference path="~/Services/Catalog/Catalog.svc" />
/// <reference path="~/AjaxControls/IController.js"/>
/// <reference path="~/AjaxControls/ThumbnailData.js"/>
/// <reference path="~/AjaxControls/ThumbnailPanelView.js"/>
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  ThumbnailController.js
*
*       This class is used to drive the entire thumbnail panel experience.  It sets up
*       the data class, and initializes the UI classes.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.ThumbnailController = function(element) {
    /// <summary>
    ///     ThumbnailController Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <param name="element">HTML DomElement to associate.</param>
    /// <returns>
    ///     A newly created ThumbnailController.
    /// </returns>
    UserPCSite.ThumbnailController.initializeBase(this, [element]);
}

UserPCSite.ThumbnailController.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///     
        ///     Initializes the controller.
        /// </summary>
        UserPCSite.ThumbnailController.callBaseMethod(this, 'initialize');
    },
    initializeRequest: function() {
        /// <summary>
        ///     initializeRequest is called to initialize the ajax data request needed by
        ///     this controller, as well as any additional requirements.
        /// </summary>

        // Create a div to contain the panel view
        var div = document.createElement('div');
        div.setAttribute('id', this.get_id() + '_view');
        this.get_element().appendChild(div);

        // Hide showcase if chartId is empty or rendering Categories apps
        var isRenderPanel = true;
        if (this.chartId == ""
            && (this.panelType == "Chart" || this.panelType == "ChartSplit" || this.panelType == "FullChart")) {
            isRenderPanel = false;
        }

        if (isRenderPanel) {
            // Create the panel view element
            this._view = $create(UserPCSite.ThumbnailPanelView,
                             { 'rows': this.rows,
                                 'columns': this.columns,
                                 'panelType': this.panelType,
                                 'controller': this,
                                 'showFeatured': this.showFeatured,
                                 'slideWidth': this.slideWidth
                             },
                             {},
                             {},
                             div);
            this.makeRequest();
        }

    },
    makeRequest: function() {
        /// <summary>
        ///     Issue the actual ajax request.
        /// </summary>

        this._view.displayLoading();
        // Get a reference to the catalog service, and issue a data call
        var service = new UserPCSite.Catalog();
        switch (this.panelType) {
            case "CategoryView":
                {
                    service.GetAppsInCategory(this.deviceId,
                                              this.categoryId,
                                              this.beforeMarker,
                                              this.chunkSize,
                                              'all', // orderBy
                                              this.langId,
                                              this.geoFilter,
                                              this.successfulResponse,
                                              this.failureResponse,
                                              this);
                    break;
                }
            case "SearchResults":
                {
                    service.SearchApps(this.deviceId,
                                       this.keywords,
                                       this.beforeMarker,
                                       this.chunkSize,
                                       'all', // orderBy
                                       this.langId,
                                       this.geoFilter,
                                       this.successfulResponse,
                                       this.failureResponse,
                                       this);
                    break;
                }
            case "Chart":
            case "ChartSplit":
            case "FullChart":
                {
                    service.GetAppsInChart(this.deviceId,
                                           this.chartId,
                                           this.beforeMarker,
                                           this.chunkSize,
                                           'all', // orderBy
                                           this.langId,
                                           this.geoFilter,
                                           this.successfulResponse,
                                           this.failureResponse,
                                           this);
                    break;
                }
            default:
                {
                    throw ("panelType (" + this.panelType + ") not supported.");
                }
        }
    },
    successfulResponse: function(response, context) {
        /// <summary>
        ///     Called when a data query has successfully completed.
        /// </summary>
        /// <param name="response">A JSON encoded object representing the data acquired.</param>
        /// <param name="context">A reference to the ThumbnailController that issued the request</param>

        // Create the data object with the response object
        context._data = $create(UserPCSite.ThumbnailData,
                                 { 'json': response,
                                     'chartId': context.chartId,
                                     'locDict': context.locDict,
                                     'parentCategoryId': context.parentCategoryId,
                                     'chartName': context.chartName
                                 },
                                 {},
                                 {},
                                 null);

        if (!SiteCommon.Utils.isNullOrUndefined(UserPCSite.PurchasedAppController)
            && !SiteCommon.Utils.isNullOrUndefined(UserPCSite.PurchasedAppController.get_instance)) {

            UserPCSite.PurchasedAppController.get_instance().registerForNotifications(context.continueRendering, context);
        }
    },
    continueRendering: function(context) {
        /// <summary>
        ///     Continue rendering after purchased apps library is ready.
        /// </summary>
        /// <param name="context">A reference to the ThumbnailController that issued the request</param>

        // Check if feature apps are required.
        var featuredCount = context._data.getLength(UserPCSite.ThumbnailData.thumbnailContainer.featured);
        var useFeaturedApps = context.showFeatured && (featuredCount > 0);
        context._view.showFeatured = useFeaturedApps;

        // Setup the main UI components.
        context._view.dataRender();

        var itemCount = context._data.getLength(UserPCSite.ThumbnailData.thumbnailContainer.regular);
        if (itemCount > 0) {
            // Add thumbnails to the view for each data element.
            for (var i = 0; i < itemCount; i++) {
                context._view.addThumbnail(context._data, context._data.getDataContext(UserPCSite.ThumbnailData.thumbnailContainer.regular, i));

            }
        }
        // If no thumbnails were returned then display an error message
        else {
            context._view.displayNoResults();
        }

        // If featured apps are used && we are not paginating, then add any found featured thumbnails to the panel.
        if (useFeaturedApps && context._view._featuredViews.length == 0) {
            // Add thumbnails to the view for each data element.
            for (var i = 0; i < featuredCount; i++) {
                context._view.addThumbnail(context._data, context._data.getDataContext(UserPCSite.ThumbnailData.thumbnailContainer.featured, i));
            }
        }
        if (itemCount < context.chunkSize) {
            if (context.beforeMarker == 0) {
                //First page has fewer than chunk size to display, 
                //so no more pages can be retrieved next
                context._page = -1;
            }
            else {
                //Last page, 
                //no more pages can be retrieved next
                context._page = 2;
            }
        }
        else {
            if (context.beforeMarker == 0) {
                //First page, but more results may be available
                context._page = 0;
            }
            else {
                //Middle page, and more pages may be available
                context._page = 1;
            }
        }

        switch (context.panelType) {
            case "Chart":
            case "ChartSplit":
                if (context.chunkSize * 2 <= context.beforeMarker && context._page == 1) {
                    context._page = 2;
                }
                break;
            case "FullChart":
            default:
        }
        context._view.drawNavigator(context._page);
    },
    failureResponse: function(response, context) {
        /// <summary>
        ///     Called when a data query has failed.
        /// </summary>
        /// <param name="response">A string representing the failure information.</param>
        /// <param name="context">A reference to the ThumbnailController that issued the request</param>

        context._view.displayFailed();
    },
    thumbnailClicked: function(dataContext) {
        /// <summary>
        ///     Called when a thumbnail has been clicked.
        /// </summary>
        /// <param name="dataContext">DataContext reference to determine which thumbnail was clicked.</param>

        window.location.href = UserPCSite.Utils.getDetailsURL(this.get_data().getApplicationId(dataContext))

        // omniture tracking
        SiteCommon.Utils.OmnitureTrackThumbnailClick(dataContext.index);
    },
    reloadClicked: function() {
        /// <summary>
        ///     Called when the view should be reloaded.
        /// </summary>

        // Reset the view to an initial state.
        this._view.resetRender();

        // Issue the actual request.
        this.makeRequest();
    },
    dispose: function() {
        /// <summary>
        ///     Called to dispose of this objects resources.
        /// </summary>
        UserPCSite.ThumbnailController.callBaseMethod(this, 'dispose');
    },
    handlePagination: function(page) {
        /// <summary>
        ///     TODO: Handle pagination here
        /// </summary>
    },
    handleNavigation: function(button) {
        /// <summary>
        ///     Handle page navigation
        ///     Handles next+previous buttons only.
        ///     Cannot handle just to pagenumbers
        /// </summary>
        var before = parseInt(this.beforeMarker);
        var chunk = parseInt(this.chunkSize);

        if (button == 'next') {
            if (this._page == 2) {
                //Last page, cant go next
                return;
            }
            //Move to next page
            this.beforeMarker = String(before + chunk);
        }
        else if (button == 'previous') {
            if (this._page == 0) {
                //First page, cant go previous
                return;
            }
            else if (before - chunk >= 0) {
                //Move to previous page
                this.beforeMarker = String(before - chunk);
            }
            else {
                //before market cant be -ve
                this.beforeMarker = "0";
            }
        }
        else {
            //Invalid button
            return;
        }

        // Reset the view to an initial state.
        this._view.resetRender();

        // Issue the actual request.
        this.makeRequest();
    }
}
UserPCSite.ThumbnailController.prototype.rows = 0;
/// <field name="rows">The number of rows of apps to display.</field>
UserPCSite.ThumbnailController.prototype.columns = 0;
/// <field name="columns">The number of columns of apps to display.</field>
UserPCSite.ThumbnailController.prototype.panelType = "";
/// <field name="panelType">The type of thumbnail panel to display.</field>
UserPCSite.ThumbnailController.prototype.showFeatured = 0;
/// <field name="showFeatured">Indicates if featured apps should be used.</field>
UserPCSite.ThumbnailController.prototype.slideWidth = 0;
/// <field name="slideWidth">The width of the visible content slider window.</field>
UserPCSite.ThumbnailController.prototype.chunkSize = "";
/// <field name="chunkSize">The number of items to query.</field>
UserPCSite.ThumbnailController.prototype.beforeMarker = "";
/// <field name="beforeMarker">The offset of the application to start the query with.</field>
UserPCSite.ThumbnailController.prototype.categoryId = "";
/// <field name="categoryId">The id of the category to query with.</field>
UserPCSite.ThumbnailController.prototype.chartId = "";
/// <field name="chartId">The chart to start the query with.</field>
UserPCSite.ThumbnailController.prototype.keywords = "";
/// <field name="keywords">The keywords to search with.</field>
UserPCSite.ThumbnailController.prototype.chartName = "";
/// <field name="chartName">The chartName to search with.</field>
UserPCSite.ThumbnailController.prototype.deviceId = null;
/// <field name="deviceID">The deviceId to query applications.</field>
UserPCSite.ThumbnailController.prototype.HasUserSelectedDevice = null;
/// <field name="HasUserSelectedDevice">true if user has a selected device (string attribute, "True" or "False")</field>
UserPCSite.ThumbnailController.prototype.websiteDeviceId = null;
/// <field name="websiteDeviceId">web site id that we pass in the call to purchase.</field>
UserPCSite.ThumbnailController.prototype.langId = null;
/// <field name="langId">The language to display the website in.</field>
UserPCSite.ThumbnailController.prototype.geoFilter = null;
/// <field name="geoFilter">The Geo Filter selected by user.</field>
UserPCSite.ThumbnailController.prototype.get_data = function() { return this._data; };
/// <field name="data">Get the data element.</field>

UserPCSite.ThumbnailController.prototype.locDict = null;
/// <field name="locDict">The dictionary to receive localized strings.</field>

UserPCSite.ThumbnailController.prototype.parentCategoryId = "";
/// <field name="parentCategoryId">The parent category id from query string</field>
UserPCSite.ThumbnailController.prototype.chartType = "";
/// <field name="chartType">This will store chartType e.g. ShowCase/What'sNew/Most Popular</field>
UserPCSite.ThumbnailController.prototype.supportUrl = null;
/// <field name="supportUrl">support url</field>

// Register the component
UserPCSite.ThumbnailController.registerClass('UserPCSite.ThumbnailController', Sys.UI.Control, SiteCommon.IController);


/// <reference name="MicrosoftAjax.js"/>
/// <reference path="~/AjaxControls/InteractiveBaseControl.js" />
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  ContentSliderView.js
*
*      Displays the details of an Application.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.HeroView = function(element) {
    /// <summary>
    ///     ContentSliderView Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <param name="element">HTML DomElement to associate.</param>
    /// <returns>
    ///     A newly created ContentSliderView object.
    /// </returns>
    UserPCSite.HeroView.initializeBase(this, [element]);
}

UserPCSite.HeroView.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///     
        ///     Initializes the view.
        /// </summary>
        UserPCSite.HeroView.callBaseMethod(this, 'initialize');
    },
    _setupRender: function() {
    /// <summary>
    ///     Sets up the Hero dom element.
    /// </summary>
    var a = document.createElement('a');
    a.setAttribute('href', this.imgLink);
    this.get_element().appendChild(a);

    var img = document.createElement('img');
    Sys.UI.DomElement.addCssClass(img, 'HeroView_base_image');
    img.setAttribute('src', this.imgSrc);
    img.setAttribute('alt', this.altText);
    a.appendChild(img);

    },
    dispose: function() {
    /// <summary>
    ///     Called to dispose of this objects resources.
    /// </summary>    
        UserPCSite.HeroView.callBaseMethod(this, 'dispose');
    }
}

UserPCSite.HeroView.prototype.imgLink = "";
/// <field name="imgLink">The slider Type for css display.</field>
UserPCSite.HeroView.prototype.altText = "";
/// <field name="altText">The slider Type for css display.</field>
UserPCSite.HeroView.prototype.imgSrc = "";
/// <field name="imgSrc">The slider Type for css display.</field>

UserPCSite.HeroView.registerClass('UserPCSite.HeroView', SiteCommon.InteractiveBaseView);


/// <reference name="MicrosoftAjax.js"/>
/// <reference path="~/Services/Catalog/Catalog.svc"/>
/// <reference path="~/AjaxControls/IController.js"/>
/// <reference path="~/AjaxControls/ContentSliderView.js"/>
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  HeroController.js
*
*       This class is used to drive the entire app details page.  It sets up
*       the data class, and initializes the UI classes.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.HeroController = function(element) {
    /// <summary>
    ///     HeroController Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <param name="element">HTML DomElement to associate.</param>
    /// <returns>
    ///     A newly created HeroController.
    /// </returns>
    UserPCSite.HeroController.initializeBase(this, [element]);
}

UserPCSite.HeroController.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///     
        ///     Initializes the controller.
        /// </summary>
        UserPCSite.HeroController.callBaseMethod(this, 'initialize');
    },
    initializeRequest: function() {
        /// <summary>
        ///     initializeRequest is called to initialize the ajax data request needed by
        ///     this controller, as well as any additional requirements.
        /// </summary>

        // Create a div to contain the view
        var mainDiv = document.createElement('div');
        this.get_element().appendChild(mainDiv);

        var div = document.createElement('div');
        div.setAttribute('id', this.get_id() + '_view');
        mainDiv.appendChild(div);
        // Create the Slider view
        this._view = $create(UserPCSite.ContentSliderView,
                                 { 'controller': this,
                                     'sliderType': "hero",
                                     'slideWidth': 442
                                 },
                                 {},
                                 {},
                                 div);

        // Create slides using straight data here.  In the future hook in data object.
        this._slideElements = new Array();
        for (var i = 0; i < this.get_heroData().length; i++) {
            var div = document.createElement('div');
            this._slideElements[this._slideElements.length] = $create(UserPCSite.HeroView,
                                { 'altText': this.get_heroData()[i].AltText,
                                    'imgSrc': this.get_heroData()[i].Source,
                                    'imgLink': this.get_heroData()[i].Link
                                },
                                {},
                                {},
                                div);
            this._view.addSlide(div);
        }
    },
    dispose: function() {
        //Add custom dispose actions here
        UserPCSite.HeroController.callBaseMethod(this, 'dispose');
    }
}
UserPCSite.HeroController.prototype.set_heroData = function(heroData) { this._heroData = heroData; }
UserPCSite.HeroController.prototype.get_heroData = function() { return this._heroData; }
/// <field name="heroData">JSON encoded hero control data from the ascx control.</field>

UserPCSite.HeroController.registerClass('UserPCSite.HeroController', Sys.UI.Control, SiteCommon.IController);



/// <reference name="MicrosoftAjax.js"/>
/// <reference path="~/AjaxControls/InteractiveBaseControl.js" />
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  ContentSliderView.js
*
*      Displays the details of an Application.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.ContentSliderView = function(element) {
    /// <summary>
    ///     ContentSliderView Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <param name="element">HTML DomElement to associate.</param>
    /// <returns>
    ///     A newly created ContentSliderView object.
    /// </returns>
    UserPCSite.ContentSliderView.initializeBase(this, [element]);
}

UserPCSite.ContentSliderView.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///     
        ///     Initializes the view.
        /// </summary>

        UserPCSite.ContentSliderView.callBaseMethod(this, 'initialize');

        this._listSlides = new Array();
        this.add_click(this._onClick);
        this._curIndex = 0;
        this._newIndex = -1;
        this._handlingSlide = false;
        // Length of time to aim for full animation.
        this._slideTime = 400;
        // Amount of time to wait between calls
        this._slideWait = this._slideTime / 30; // (30 ~= 30fps, where 1000/30 => 33.333 ms and some time for processing.)
    },
    _setupRender: function() {
        /// <summary>
        ///     Sets up the slider main element.
        /// </summary>

        // Set the main CSS style for the element
        Sys.UI.DomElement.addCssClass(this.get_element(), 'ContentSlider ContentSlider_' + this.sliderType);

        // Create a table for the slide elements
        this._slidesTable = document.createElement('table');
        this.get_element().appendChild(this._slidesTable);
        var tbody = document.createElement('tbody');
        this._slidesTable.appendChild(tbody);
        this._slidesTR = document.createElement('tr');
        tbody.appendChild(this._slidesTR);

        // Create a table for the buttons
        var table = document.createElement('table');
        this.get_element().appendChild(table);
        Sys.UI.DomElement.addCssClass(table, 'ContentSlider_buttonBar ContentSlider_' + this.sliderType + '_buttonBar'); // absolutely position, high z-order
        var tbody = document.createElement('tbody');
        table.appendChild(tbody);
        var tr = document.createElement('tr');
        tbody.appendChild(tr);
        var td = document.createElement('td');
        tr.appendChild(td);

        // Create the left button
        var leftButton = document.createElement('div');
        var buttonName = this.get_id() + '_left';
        leftButton.setAttribute('id', buttonName);
        Sys.UI.DomElement.addCssClass(leftButton, 'CursorHand ContentSlider_buttonBar_left');
        this.addEventTarget(SiteCommon.InteractiveBaseView.eventTypes.click, buttonName, { 'button': 'left' });
        td.appendChild(leftButton);
        var td = document.createElement('td');
        tr.appendChild(td);

        // Create the right button
        var rightButton = document.createElement('div');
        buttonName = this.get_id() + '_right';
        rightButton.setAttribute('id', buttonName);
        Sys.UI.DomElement.addCssClass(rightButton, 'CursorHand ContentSlider_buttonBar_right');
        this.addEventTarget(SiteCommon.InteractiveBaseView.eventTypes.click, buttonName, { 'button': 'right' });
        td.appendChild(rightButton);
    },
    dataRender: function() {
        /* Declared for interface */
    },
    addSlide: function(domElement) {
        /// <summary>
        ///     Called to add a element to the slide deck.
        /// </summary>
        /// <param name="domElement">HTML DOM element to add as member of the slide.</param>

        var index = this._listSlides.length;
        this._listSlides[index] = domElement;
        domElement.setAttribute('id', this.get_id() + '_' + index);

        Sys.UI.DomElement.addCssClass(domElement, 'ContentSlider_slideItem');
        var td = document.createElement('td');
        td.appendChild(domElement);
        this._slidesTR.appendChild(td);
    },
    setSlide: function(index, animate) {
        /// <summary>
        ///     Show a particular slide from the deck.
        /// </summary>
        /// <param name="index">Index of the slide to navigate to.</param>
        if ((index > this._listSlides.length - 1) || (index < 0)) {
            throw "Content Slider index is out of range.";
        }

        // Check non-animated, then assign the new location directly.
        if (!animate) {
            this._curIndex = index;
            Sys.UI.DomElement.setLocation(this._slidesTable, -(this.slideWidth * index), 0);
        }
        else {
            // Animate the element change, based on the setup parameters.
            this._newIndex = index;
            var slideSteps = Math.ceil(this._slideTime / this._slideWait);

            SiteCommon.Utils.simpleXAnimation(this,                                 // owning object
                                              this._slidesTable,                    // domElement to slide
                                              -this.slideWidth * this._curIndex,    // current X location
                                              -this.slideWidth * this._newIndex,    // current Y location
                                              slideSteps,                           // number of steps to take
                                              this._slideWait,                      // delay between animation steps
                                              this._slideComplete);                 // animation completed callback.
        }

    },
    _slideComplete: function(context) {
        /// <summary>
        ///     Notification of animation completion.
        /// </summary>
        /// <param name="context">ContentSliderView object that was animating.</param>
        context._handlingSlide = false;
        // Call non-animated setSlide to ensure no animation rounding affects layout, and to update the current index.
        context.setSlide(context._newIndex, false);
    },
    dispose: function() {
        /// <summary>
        ///     Called to dispose of this objects resources.
        /// </summary>

        UserPCSite.ContentSliderView.callBaseMethod(this, 'dispose');
    },
    _onClick: function(sender, args) {
        /// <summary>
        ///     Click event handler for the subcategoryname.
        /// </summary>
        /// <param name="sender">CategoryListView object that is hovered.</param>
        /// <param name="args">Event args.</param>

        var handled = false;

        if (!sender._handlingSlide) {
            // Notify the controller that a category was clicked.
            if (!SiteCommon.Utils.isNullOrUndefined(args.customData)) {
                var newIndex = -1;
                if (args.customData.button == 'right') {
                    var newIndex = (sender._curIndex == sender._listSlides.length - 1) ? 0 : sender._curIndex + 1;
                    var handled = true;
                }
                else if (args.customData.button == 'left') {
                    var newIndex = (sender._curIndex == 0) ? sender._listSlides.length - 1 : sender._curIndex - 1;
                    var handled = true;
                }
                if ((newIndex != sender._curIndex) && (newIndex >= 0) && (newIndex < sender._listSlides.length)) {
                    sender._handlingSlide = true;
                    sender.setSlide(newIndex, true);
                }
            }
        }
        if (handled) {
            // Mark the event as handled to avoid refiring.
            SiteCommon.Utils.markEventHandled(event);
        }
    }
}

UserPCSite.ContentSliderView.prototype.sliderType = "";
/// <field name="sliderType">The slider Type for css display.</field>
UserPCSite.ContentSliderView.prototype.slideWidth = 0;
/// <field name="slideWidth">The width of the slider window to view (used for sliding to next element).</field>

UserPCSite.ContentSliderView.registerClass('UserPCSite.ContentSliderView', SiteCommon.InteractiveBaseView);


/// <reference name="MicrosoftAjax.js"/>
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  Utils.js
*
*      General utilities
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("SiteCommon");

SiteCommon.Utils = function() { }

SiteCommon.Utils.boldFirstWord = function(textNode, additionalBoldCss) {
    /// <summary>
    ///     Bolds the first word of a string of text
    /// </summary>
    /// <param name="textNode">textNode to style</param>
    /// <param name="additionalBoldCss">An optional css class to apply to the resulting textnode.</param>
    /// <returns>
    ///     domElement of the provided text.
    /// </returns>
    var boldText = "";
    var normalText = "";
    var text = textNode.data;

    var index = text.indexOf(' ');
    if (index >= 0) {
        boldText = text.substring(0, index);
        normalText = text.substring(index);
    }
    else {
        boldText = text;
    }
    // Create bold element.
    var boldSpan = document.createElement('span');
    Sys.UI.DomElement.addCssClass(boldSpan, 'BoldFirstWord');
    if (!SiteCommon.Utils.isNullOrUndefined(additionalBoldCss)) {
        Sys.UI.DomElement.addCssClass(boldSpan, additionalBoldCss);
    }
    boldSpan.appendChild(document.createTextNode(boldText));
    // Create main span container.
    var outerSpan = document.createElement('span');
    outerSpan.appendChild(boldSpan);
    outerSpan.appendChild(document.createTextNode(normalText));
    return outerSpan;
}

SiteCommon.Utils.setOpacity = function(domElement, value) {
    /// <summary>
    ///     Sets the opacity of the provided domElement to the provided value.
    /// </summary>
    /// <param name="domElement">A reference to the domElement to change.</param>
    /// <param name="value">A value (0-10) of the desired opacity.</param>
    domElement.style.opacity = value / 10;
    domElement.style.filter = 'alpha(opacity=' + value * 10 + ')';
}

SiteCommon.Utils.markEventHandled = function(event) {
    /// <summary>
    ///     Marks the provided event as handled in the system, to avoid it bubbling (or cascading) anymore.
    /// </summary>
    /// <param name="event">The event to handle.</param>

    event.cancelBubble = true;
    if (event.stopPropagation) event.stopPropagation();
}

SiteCommon.Utils.toUIString = function(value) {
    /// <summary>
    ///     Converts the value string object to a proper UI textnode (empty strings for null or undefined objects).
    /// </summary>
    /// <param name="value">Text string to check.</param>
    /// <returns>
    ///     The resulting textnode.
    /// </returns>
    var newString = SiteCommon.Utils.isNullOrUndefined(value) ? "" : value;

    return document.createTextNode(newString);
}

SiteCommon.Utils.isNullOrUndefined = function(value) {
    /// <summary>
    ///     Determines if the provided value is null or undefined.
    /// </summary>
    /// <param name="value">The object to check.</param>
    /// <returns>
    ///     A bool value, true if value is null or undefined, false otherwise.
    /// </returns>
    return (value == null || value == undefined);
},

SiteCommon.Utils.displayPopup = function(isError, showXClose, textNodeHeader, divContent) {
    /// <summary>
    ///     Displays a modal popup to the user.
    /// </summary>
    /// <param name="isError">Display normal or error dialog.</param>
    /// <param name="showXClose">Determines if a close button in the corner should be displayed.</param>
    /// <param name="textNodeHeader">The dialog header text node.</param>
    /// <param name="divContent">The main dialog contents to use.</param>


    var upperLeft = $get('ModalPopup_header_upperleft');
    var top = $get('ModalPopup_header_top');
    var topContent = $get('ModalPopup_header_content');
    var upperRight = $get('ModalPopup_header_upperright');
    var content = $get('ModalPopupContent');
    var xClose = $get('ModalPopup_xClose');

    // Skin the top of the dialog with either the normal or error UI
    var styleString = (isError) ? "error" : "normal";
    var styleStringOpposite = (!isError) ? "error" : "normal";
    upperLeft.innerHTML = "<div class='popup-" + styleString + "-upperleft' />";
    Sys.UI.DomElement.removeCssClass(top, 'ModalPopup-' + styleStringOpposite + '-top');
    Sys.UI.DomElement.addCssClass(top, 'ModalPopup-' + styleString + '-top');
    upperRight.innerHTML = "<div class='popup-" + styleString + "-upperright"
                                + (showXClose ? "-close" : "") + "' />";

    // Toggle the visible state of the XClose Button
    if (showXClose) {
        Sys.UI.DomElement.removeCssClass(xClose, 'Hidden');
    }
    else {
        Sys.UI.DomElement.addCssClass(xClose, 'Hidden');
    }

    // Reset the dialog contents
    content.innerHTML = "";
    content.appendChild(divContent);
    topContent.innerHTML = "";
    topContent.appendChild(SiteCommon.Utils.boldFirstWord(textNodeHeader, 'ModalPopup_header_content_bold'));

    var popup = $find('ModalPopupBehaviorID');
    if (!SiteCommon.Utils.isNullOrUndefined(popup)) {
        popup.show();
    }
}

SiteCommon.Utils.generateErrorDiv = function(response) {
    /// <summary>
    ///     Creates a Div containing the error details as specified in the response parameter.
    /// </summary>
    /// <param name="response">The server response to display.</param>
    // Initialize data values.
    var statusCode = SiteCommon.Utils.isNullOrUndefined(response._statusCode) ? "" : response._statusCode;
    var exceptionType = SiteCommon.Utils.isNullOrUndefined(response._exceptionType) ? "" : response._exceptionType;
    var message = SiteCommon.Utils.isNullOrUndefined(response._message) ? "" : response._message;
    var stackTrace = SiteCommon.Utils.isNullOrUndefined(response._stackTrace) ? "" : response._stackTrace;
    var responseCode = SiteCommon.Utils.isNullOrUndefined(response.ResponseCode) ? "" : response.ResponseCode;
    var responseMessage = SiteCommon.Utils.isNullOrUndefined(response.ResponseMessage) ? "" : response.ResponseMessage;

    // Construct main dialog wrapper.
    var divContent = document.createElement('div');
    Sys.UI.DomElement.addCssClass(divContent, 'PopupDialogContent');

    // Display the error status code
    var p = document.createElement('p');
    p.appendChild(document.createTextNode('Error Encountered (' + response._statusCode + '): ' + exceptionType));
    divContent.appendChild(p);

    // If a ResponseCode was found, display it and the response message.
    if (responseCode != "") {
        p = document.createElement('p');
        p.appendChild(document.createTextNode("ResponseMessage (" + responseCode + "): " + responseMessage));
        divContent.appendChild(p);
    }

    // Add a corresponding error message
    p = document.createElement('p');
    p.appendChild(document.createTextNode(message));
    divContent.appendChild(p);

    // Add the callstack if it is included.
    p = document.createElement('p');
    Sys.UI.DomElement.addCssClass(p, 'PopupErrorContent_stack');

    // Replace any newlines in the stack with <br/>'s
    var index = 0;
    while ((index = stackTrace.indexOf('\n', 0)) >= 0) {
        p.appendChild(document.createTextNode(stackTrace.substring(0, index)));
        p.innerHTML += "<br/>";
        if (index + 1 < stackTrace.length) {
            stackTrace = stackTrace.substring(index + 1);
        }
        else {
            stackTrace = "";
        }
    }
    divContent.appendChild(p);

    return divContent;
}

SiteCommon.Utils.displayErrorMessagePopup = function(textNodeHeader, textNodeMessage) {
    /// <summary>
    ///     Displays a modal error dialog to the user.
    /// </summary>
    /// <param name="textNodeHeader">The dialog header text node.</param>
    /// <param name="textNodeMessage">The message to display.</param>
    var divContent = document.createElement('div');
    Sys.UI.DomElement.addCssClass(divContent, 'PopupErrorDialogContent');
    
    // Display the error message
    var p = document.createElement('p');
    p.appendChild(textNodeMessage);
    divContent.appendChild(p);

    // Launch the dialog.
    SiteCommon.Utils.displayPopup(true, true, textNodeHeader, divContent);
}

SiteCommon.Utils.displayErrorPopup = function(textNodeHeader, response) {
    /// <summary>
    ///     Displays a modal error dialog to the user.
    /// </summary>
    /// <param name="textNodeHeader">The dialog header text node.</param>
    /// <param name="response">The server response to display.</param>

    var divContent = SiteCommon.Utils.generateErrorDiv(response);

    // Launch the dialog.
    SiteCommon.Utils.displayPopup(true, true, textNodeHeader, divContent);
}

SiteCommon.Utils.closePopup = function() {
    /// <summary>
    ///     Displays a modal popup to the user.
    ///
    ///     Currently only implemented for the download confirmation dialog.
    /// </summary>
    var buttonLabel = $get('ModalPopupButtonLabel');
    var closeButton = $get(buttonLabel.innerHTML);
    closeButton.click();
}

SiteCommon.Utils.isSuccessResponse = function(response) {
    /// <summary>
    ///     Validates the provided JSON encoded server response doesn't contain a non-succesful StatusCode or ResponseCode.
    /// </summary>
    /// <param name="response">The server response to check.</param>
    /// <returns>
    ///     A bool value, true if the codes weren't error conditions, false otherwise.
    /// </returns>
    var isSuccess = false;
    // We assume non-error unless one is detected (due to JSON allowing values to be undefined)
    var isStatusCodeSuccess = true;
    var isResponseCodeSuccess = true;

    if (!SiteCommon.Utils.isNullOrUndefined(response)) {
        // If a statusCode is defined, then validate it.
        if (!SiteCommon.Utils.isNullOrUndefined(response._statusCode)) {
            isStatusCodeSuccess = (response._statusCode == 200);
        }

        // If a responseCode is defined, then validate it.
        if (!SiteCommon.Utils.isNullOrUndefined(response.ResponseCode)) {
            isResponseCodeSuccess = (response.ResponseCode == "0x00000000");
        }

        isSuccess = (isStatusCodeSuccess && isResponseCodeSuccess);
    }

    return isSuccess;
}

SiteCommon.Utils.simpleXAnimation = function(hostingObject, domElement, startX, endX, stepCount, interval, onCompletedHandler) {
    /// <summary>
    ///     Animations a provided object over the x-axis based on the input parameters.
    /// </summary>
    /// <param name="hostingObject">The hosting UI object of the animation.</param>
    /// <param name="domElement">The dom object to animation.</param>
    /// <param name="startX">The x position to start the animation.</param>
    /// <param name="endX">The x position to end the animation.</param>
    /// <param name="stepCount">The number of steps for the animation.</param>
    /// <param name="interval">The time to wait between steps.</param>
    /// <param name="onCompletedHandler">The animation completed handler to call.</param>
    var delta = (endX - startX) / stepCount;
    var currentX = 0;
    var currentStep = 1;
    hostingObject._animationInterval = window.setInterval(function() {
        currentX = startX + parseInt(delta * currentStep + 0.5);
        Sys.UI.DomElement.setLocation(domElement, currentX, 0);
        currentStep++;
        if (currentStep > stepCount) {
            window.clearInterval(hostingObject._animationInterval);
            if (!SiteCommon.Utils.isNullOrUndefined(onCompletedHandler)) {
                onCompletedHandler(hostingObject);
            }
        }
    }, interval);
}

SiteCommon.Utils.createLoadingDiv = function(loadingMsg) {
    /// <summary>
    ///     Create the UI for a loading message.
    /// </summary>
    /// <returns>
    ///     A dom element to use as a loading message.
    /// </returns>
    var div = document.createElement('div');
    Sys.UI.DomElement.addCssClass(div, 'LoadingMessage');

    var p = document.createElement('p');
    p.appendChild(document.createTextNode(loadingMsg));
    div.appendChild(p);

    var divImg = document.createElement('div');
    Sys.UI.DomElement.addCssClass(divImg, 'LoadingMessage_image');
    div.appendChild(divImg);

    return div;
}

SiteCommon.Utils.createReloadDiv = function(parentId, refreshMsg, busyMsg) {
    /// <summary>
    ///     Create the UI for a reload message.
    /// </summary>
    /// <param name="parentId">Id of the parent view.</param>
    /// <returns>
    ///     An object with two properties:
    ///         object.reloadButton: A reference to the actual reload button DOM element.
    ///         object.div: The main div of the UI element.
    /// </returns>
    var returnObj = new Object();

    var div = document.createElement('div');
    Sys.UI.DomElement.addCssClass(div, 'LoadingMessage');

    var p = document.createElement('p');
    p.appendChild(document.createTextNode(busyMsg));
    div.appendChild(p);

    var divImg = document.createElement('div');
    Sys.UI.DomElement.addCssClass(divImg, 'LoadingMessage_image');
    div.appendChild(divImg);

    var divReload = document.createElement('div');
    divReload.setAttribute('id', parentId + "_reload");
    returnObj.reloadButton = divReload;
    divReload.appendChild(document.createTextNode(refreshMsg));
    Sys.UI.DomElement.addCssClass(divReload, 'ReloadMessage_button CursorHand');
    div.appendChild(divReload);

    returnObj.div = div;

    return returnObj;
}
SiteCommon.Utils.createElement = function(type, parent, id, cssClass, appendChild) {
    ///<summary>
    ///     Creates a new element
    ///</summary>
    /// <param name="type">Type of the new element.</param>    
    /// <param name="parent">Parent div to add new element to.</param>
    /// <param name="id">ID of the new element.</param>
    /// <param name="cssClass">Css Class to add to the new element.</param>
    /// <param name="appendChild">First child element to add to the new element.</param>

    var divName = document.createElement(type);

    if (!SiteCommon.Utils.isNullOrUndefined(id)) {
        divName.setAttribute('id', id);
    }
    if (!SiteCommon.Utils.isNullOrUndefined(parent)) {
        parent.appendChild(divName);
    }

    if (!SiteCommon.Utils.isNullOrUndefined(cssClass)) {
        Sys.UI.DomElement.addCssClass(divName, cssClass);
    }
    if (!SiteCommon.Utils.isNullOrUndefined(appendChild)) {
        divName.appendChild(appendChild);
    }
    return divName;
}

SiteCommon.Utils.DisplayConfirmationDialog = function(textTitle, textContent, textHeader, textOk,
                                                textCancel, okClickHandler) {
    /// <summary>
    ///     Create the UI for a loading message.
    /// </summary>
    /// <returns>
    ///     A dom element to use as a loading message.
    /// </returns>
    var headerUI = SiteCommon.Utils.toUIString(textTitle);
    var contentHeaderUI = SiteCommon.Utils.createElement('h2', null,
                            'ConfirmHeader', 'Confirmation_Content_Header',
                            SiteCommon.Utils.toUIString(textHeader));
    var contentUI = SiteCommon.Utils.toUIString(textContent);
    var okUI = SiteCommon.Utils.toUIString(textOk);
    var cancelUI = SiteCommon.Utils.toUIString(textCancel);

    var contentDiv = SiteCommon.Utils.CreateDiv(null, 'Confirmation_Content',
                        null, null);

    //Add text as a div                        
    var contentUIDiv = SiteCommon.Utils.CreateDiv(contentDiv, 'Confirmation_Content_Text',
                        null, contentHeaderUI);
    contentUIDiv.appendChild(contentUI);

    //Add clear:both div
    SiteCommon.Utils.CreateDiv(contentDiv, 'Confirmation_Content_Clear',
                              'clear', null);

    //Add OK & Cancel buttons
    var btn_cancel = SiteCommon.Utils.CreateButton(textCancel, 'Confirmation_Cancel',
                    'WhiteButton', 'SiteCommon.Utils.closePopup();');
    contentDiv.appendChild(btn_cancel);

    var btn_ok = SiteCommon.Utils.CreateButton(textOk, 'Confirmation_Ok',
                                'OrangeButton', okClickHandler);
    contentDiv.appendChild(btn_ok);

    //Show the popup
    SiteCommon.Utils.displayPopup(false, true, headerUI, contentDiv);
}

SiteCommon.Utils.getShowPurchaseConfirmation = function() {
    ///<summary>
    ///     Check whether or not to show the purchase confirmation dialog
    ///     by reading the 'BypassPurchaseConf' cookie value
    ///</summary>
    ///<return>
    /// true or false
    ///</return>    
    var bypassPurchaseConf = SiteCommon.Utils.readCookie('BypassPurchaseConf');
    if (SiteCommon.Utils.isNullOrUndefined(bypassPurchaseConf)) {
        return true;
    } else {
        return false;
    }
}

SiteCommon.Utils.setShowPurchaseConfirmation = function(checkBoxId) {
    ///<summary>
    ///     Set the 'BypassPurchaseConf' cookie value to bypass showing the 
    ///     purchase confirmation dialog
    ///</summary>

    var checkBox = document.getElementById(checkBoxId);

    if (!SiteCommon.Utils.isNullOrUndefined(checkBox) && checkBox.checked) {
        SiteCommon.Utils.createCookie('BypassPurchaseConf', '1', 14);
    }
}

SiteCommon.Utils.createCookie = function(name, value, days) {
    ///<summary>
    /// create cookie
    ///</summary>
    ///<param name="name">cookie name</param>
    ///<param name="value">cookie value</param>
    ///<param name="days">Pass in zero - cookie will expire when close browser</param>
    if (days) {
        var date = new Date();
        date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
        var expires = "; expires=" + date.toGMTString();
    }
    else var expires = "";
    document.cookie = name + "=" + value + expires + "; path=/";
}

SiteCommon.Utils.readCookie = function(name) {
    ///<summary>
    ///     Read cookie
    ///</summary>
    ///<param name="name">cookie name</param>
    ///<returns>
    ///     return cookie value
    ///</return>
    var nameEQ = name + "=";
    var ca = document.cookie.split(';');
    for (var i = 0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0) == ' ') c = c.substring(1, c.length);
        if (c.indexOf(nameEQ) == 0) {
            return c.substring(nameEQ.length, c.length);
        }
    }
    return null;
}

SiteCommon.Utils.extractValueFromACookie = function(CookieName, KeyName) {
    ///<summary>
    ///     Extract a specific value from a cookie
    ///</summary>
    ///<param name="name">Key name</param>
    ///<returns>
    ///     return value of the Key/Value pair
    ///</return>
    var sCookieValues = SiteCommon.Utils.readCookie(CookieName);
    var nameEQ = KeyName + "=";
    var ca = sCookieValues.split('&');
    for (var i = 0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0) == ' ') c = c.substring(1, c.length);
        if (c.indexOf(nameEQ) == 0) {
            return c.substring(nameEQ.length, c.length);
        }
    }
    return null;
}

SiteCommon.Utils.eraseCookie = function(name) {
    ///<summary>
    ///     Delete existing cookie
    ///</summary>
    ///<param name="name">cookie name</param>
    createCookie(name, "", -1);
}


SiteCommon.Utils.CreateDiv = function(parent, id, cssClass, appendChild) {
    ///<summary>
    ///     Creates a new div element
    ///</summary>
    /// <param name="parent">Parent div to add new div element to.</param>
    /// <param name="id">ID of the new div element.</param>
    /// <param name="cssClass">Css Class to add to the new div element.</param>
    /// <param name="appendChild">First child element to add to the new element.</param>

    return SiteCommon.Utils.createElement(SiteCommon.Utils.elementTypes.div, parent, id, cssClass, appendChild);
}
SiteCommon.Utils.CreateImg = function(parent, id, cssClass, url, text) {
    ///<summary>
    ///     Creates a new image element
    ///</summary>
    /// <param name="parent">Parent div to add new div element to.</param>
    /// <param name="id">ID of the new div element.</param>
    /// <param name="cssClass">Css Class to add to the new div element.</param>
    /// <param name="url">URL to the image.</param>

    var img = SiteCommon.Utils.createElement(SiteCommon.Utils.elementTypes.img, parent, id, cssClass, null);
    img.setAttribute('src', url);
    img.setAttribute('alt', text);
    return img;
}

SiteCommon.Utils.CreateSpan = function(parent, id, cssClass, appendChild) {
    ///<summary>
    ///     Creates a new span element
    ///</summary>
    /// <param name="parent">Parent div to add new span element to.</param>
    /// <param name="id">ID of the new span element.</param>
    /// <param name="cssClass">Css Class to add to the new span element.</param>
    /// <param name="appendChild">First child element to add to the new span element.</param>

    return SiteCommon.Utils.createElement(SiteCommon.Utils.elementTypes.span, parent, id, cssClass, appendChild);
}

SiteCommon.Utils.CreatePara = function(parent, id, cssClass, appendChild) {
    ///<summary>
    ///     Creates a new Para element
    ///</summary>
    /// <param name="parent">Parent div to add new para element to.</param>
    /// <param name="id">ID of the new span element.</param>
    /// <param name="cssClass">Css Class to add to the new para element.</param>
    /// <param name="appendChild">First child element to add to the new para element.</param>

    return SiteCommon.Utils.createElement(SiteCommon.Utils.elementTypes.p, parent, id, cssClass, appendChild);
}

SiteCommon.Utils.CreateLink = function(parent, id, cssClass, appendChild, onClick, href) {
    ///<summary>
    ///     Creates a new link element
    ///</summary>
    /// <param name="parent">Parent div to add new link element to.</param>
    /// <param name="id">ID of the new link element.</param>
    /// <param name="cssClass">Css Class to add to the new link element.</param>
    /// <param name="appendChild">First child element to add to the new link element.</param>
    /// <param name="onClick">OnClick event to add to the new link element.</param>
    /// <param name="href">Href element to add to the new link element.</param>

    var link = SiteCommon.Utils.createElement(SiteCommon.Utils.elementTypes.a, parent, id, cssClass, appendChild);
    if (!SiteCommon.Utils.isNullOrUndefined(onClick)) {
        link.setAttribute('href', 'javascript:' + onClick);
    }
    else if (!SiteCommon.Utils.isNullOrUndefined(href)) {
        link.setAttribute('href', href);
    }
    else {
        link.setAttribute('href', '#');
    }

    return link;
}

SiteCommon.Utils.CreateTable = function(parent, id, cssClass, appendChild) {
    ///<summary>
    ///     Creates a new Table element
    ///</summary>
    /// <param name="parent">Parent div to add new Table element to.</param>
    /// <param name="id">ID of the new Table element.</param>
    /// <param name="cssClass">Css Class to add to the new Table element.</param>
    /// <param name="appendChild">First child element to add to the new Table element.</param>

    return SiteCommon.Utils.createElement(SiteCommon.Utils.elementTypes.table, parent, id, cssClass, appendChild);
}

SiteCommon.Utils.CreateTableRow = function(parent, id, cssClass, appendChild, colspan) {
    ///<summary>
    ///     Creates a new Table Row element
    ///</summary>
    /// <param name="parent">Parent div to add new Table Row element to.</param>
    /// <param name="id">ID of the new Table Row element.</param>
    /// <param name="cssClass">Css Class to add to the new Table Row element.</param>
    /// <param name="appendChild">First child element to add to the new Table Row element.</param>

    var row = SiteCommon.Utils.createElement(SiteCommon.Utils.elementTypes.row, parent, id, cssClass, appendChild);
    if (!SiteCommon.Utils.isNullOrUndefined(colspan)) {
        row.setAttribute('colspan', colspan);
    }
    return row;
}

SiteCommon.Utils.CreateTableColumn = function(parent, id, cssClass, appendChild, rowspan) {
    ///<summary>
    ///     Creates a new Table Column element
    ///</summary>
    /// <param name="parent">Parent div to add new Table Column element to.</param>
    /// <param name="id">ID of the new Table Column element.</param>
    /// <param name="cssClass">Css Class to add to the new Table Column element.</param>
    /// <param name="appendChild">First child element to add to the new Table Column element.</param>

    var col = SiteCommon.Utils.createElement(SiteCommon.Utils.elementTypes.column, parent, id, cssClass, appendChild);
    if (!SiteCommon.Utils.isNullOrUndefined(rowspan)) {
        col.setAttribute('rowspan', rowspan);
    }
    return col;
}

SiteCommon.Utils.CreateButton = function(text, id, cssClassBase, onClick) {
    /// <summary>
    ///     Create a button and returns the wrapping Div DOM element.
    /// </summary>
    /// <param name="text">Text to use as button value.</param>
    /// <param name="id">DOM ID to attach to the resulting elements.</param>
    /// <returns>
    ///     A reference the wrapping DOM element.
    /// </returns>

    var div = document.createElement('div');
    div.setAttribute('id', id + '_wrapper');
    Sys.UI.DomElement.addCssClass(div, cssClassBase);

    var button = document.createElement('a');
    button.setAttribute('id', id + '_button');
    button.appendChild(SiteCommon.Utils.toUIString(text));
    Sys.UI.DomElement.addCssClass(button, cssClassBase + 'Text');
    button.setAttribute('href', 'javascript:' + onClick);
    var divRight = document.createElement('div');
    divRight.setAttribute('id', id + '_right');
    Sys.UI.DomElement.addCssClass(divRight, cssClassBase + 'Div');

    div.appendChild(button);
    div.appendChild(divRight);

    return div;
}
SiteCommon.Utils.CreateRadioButton = function(id, name, value, onClick, isChecked) {
    /// <summary>
    ///     Create a radio button that will work for both IE and FireFox. 
    /// </summary>
    /// <param name="id">id attribute of radio button</param>
    /// <param name="name">name required attribute to group radio button</param>
    /// <param name="value">value of the radio button</param>
    /// <param name="onClick">String representation of javascript call</param>
    /// <param name="isChecked">default is fault</param>
    /// <returns>
    ///     A reference of a radiobutton.
    /// </returns>
    //This to handle IE createElement bug that prevents radio button to be selectable
    var radiobutton = null;
    var html = '';
    if (SiteCommon.Utils.isNullOrUndefined(isChecked)) {
        isChecked = false;
    }

    try {
        if (!SiteCommon.Utils.isNullOrUndefined(onClick)) {
            html = '<input type="radio" id="' + id + '" name="' + name + '" value="' + value + '" onClick="' + onClick + '" ';
        } else {
            html = '<input type="radio" id="' + id + '" name="' + name + '" value="' + value + '"';
        }

        if (isChecked) {
            html += 'checked="checked">';
        } else {
            html += '>';
        }
        radiobutton = document.createElement(html);
    } catch (e) {
        //do nothing
    }

    //if FireFox/Netscape checkbox will be null.
    if (SiteCommon.Utils.isNullOrUndefined(radiobutton)) {
        radiobutton = document.createElement('input');
        radiobutton.setAttribute('type', 'radio');
        radiobutton.setAttribute('id', id);
        radiobutton.setAttribute('name', name);
        radiobutton.setAttribute('value', value);
        if (!SiteCommon.Utils.isNullOrUndefined(onClick)) {
            radiobutton.setAttribute('onclick', onClick);
        }
        if (isChecked) {
            radiobutton.setAttribute('checked', 'checked');
        }
    }
    return radiobutton;
}
SiteCommon.Utils.CreateCheckBox = function(id, isChecked) {
    /// <summary>
    ///     Create a checkbox that will with in both IE and FireFox
    /// </summary>
    /// <param name="id">ID that will be assigned to the checkbox ID attribute</param>
    /// <param name="isChecked">default to false unless pass in true</param>
    /// <returns>
    ///     A checkbox reference.
    /// </returns>
    var checkbox = null;
    try {
        if (isChecked) {
            checkbox = document.createElement('<input type="checkbox" id="' + id + '" checked=checked />');
        } else {
            checkbox = document.createElement('<input type="checkbox" id="' + id + '" />');
        }
    } catch (e) {
        //do nothing
    }

    //if FireFox/Netscape checkbox will be null.
    if ((checkbox == null || checkbox == undefined)) {
        checkbox = document.createElement('input');
        checkbox.setAttribute('type', 'checkbox');
        checkbox.setAttribute('id', id);
        checkbox.checked = isChecked;
    }

    return checkbox;

}

SiteCommon.Utils.CreateRatings = function(parent, id, cssClass, rating) {
    ///<summary>
    ///     Creates a new ratings div containing star icons
    ///</summary>
    /// <param name="parent">Parent div to add new element to.</param>
    /// <param name="id">ID of the new element.</param>
    /// <param name="cssClass">Css Class to add to the new element.</param>
    /// <param name="rating">Rating number to specify the number of active stars.</param>

    var mainDiv = SiteCommon.Utils.CreateDiv(parent, id, cssClass, null);
    for (var i = 0; i < 5; i++) {
        if (i < rating) {
            //Add active star
            var star = SiteCommon.Utils.CreateDiv(mainDiv, id + i,
                            'sprite ProductRatingStar', null);
        }
        else {
            //Add empty star
            star = SiteCommon.Utils.CreateDiv(mainDiv, id + i,
                            'sprite ProductRatingStarEmpty', null);
        }
    }
    return mainDiv;
}

SiteCommon.Utils.TriggerEvent = function(ElementID, TargetID, RedirectURL) {
    /// <summary>
    ///     Trigger and event based on ElementID
    /// </summary>
    /// <param name="ElementID">ID of an object to trigger and event from</param>
    /// <param name="TargetID">Name of sender that call this function</param>
    /// <param name="RedirectURL">Optional - URL to redirect to appropriate page if needed.</param>

    var controller = $find(ElementID);
    controller.handleEvent(TargetID, RedirectURL);
}

SiteCommon.Utils.JsonParser = function(json) {
    ///<summary>
    ///     Creates a Json parsed object from a string
    ///</summary>
    /// <param name="json">String containing data in Json format.</param>
    /// <returns>    Json Object
    /// </returns>
    return eval(json)[0];
}

SiteCommon.Utils.QueryString = function(key) {
    ///<summary>
    ///     Gets the specified key value from the querystring
    ///</summary>
    /// <param name="key">key to look for</param>
    /// <returns>    value for the key in the current querystring
    /// </returns>
    return SiteCommon.Utils.QueryStringInUrl(key, window.location.href);
}

SiteCommon.Utils.QueryStringInUrl = function(key, url) {
    ///<summary>
    ///     Gets the specified key value from the querystring
    ///</summary>
    /// <param name="key">key to look for</param>
    /// <param name="url">url to look into</param>
    /// <returns>    value for the key in the given url
    /// </returns>
    key = key.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
    var regex = new RegExp("[\\?&]" + key + "=([^&#]*)");
    var qs = regex.exec(url);
    if (qs == null) {
        return null;
    }
    else {
        return qs[1];
    }
}

SiteCommon.Utils.FixUrlProtocol = function(url) {
    ///<summary>
    ///     Modifies the url to fix the protocol in the url
    ///     using the current window's protocol
    ///</summary>
    /// <param name="url">url to look into</param>
    /// <returns>    updated url
    /// </returns>

    if (SiteCommon.Utils.isNullOrUndefined(url)) {
        return url;
    }

    var prot = window.location.protocol.toLowerCase();
    if (url.toLowerCase().substr(0, prot.length) == prot) {
        return url;
    }
    else {
        var index = url.toString().indexOf(':', 0)
        return prot + url.substring(index + 1, url.length);
    }
}

SiteCommon.Utils.AddEllipses = function(s, n) {
    ///<summary> 
    /// Trims the string to the desired length
    /// and adds ellipses only if the string is longer 
    /// than this specified length
    ///</summary>
    /// <param name="s">string to chop</param>
    /// <param name="n">number of chars to cut string</param>
    if (!SiteCommon.Utils.isNullOrUndefined(s)
        && s.length > n) {
        return s.substr(0, n).trimEnd() + '...';
    }
    return s;
}

SiteCommon.Utils.OpenNewWindow = function(url) {
    var wMax, hMax

    try {
        if (screen.width > 0 && screen.height > 0) {
            wMax = screen.width;
            hMax = screen.height;
        } else if (window.outerWidth > 0 && window.outerHeight > 0) {
            wMax = window.outerWidth;
            hMax = window.outerHeight;
        } else {
            wMax = 1280;
            hMax = 960;
        }
    } catch (e) {
        wMax = 1280;
        hMax = 960;
    }

    var d = url; if (!d) return;
    var w = wMax / 2;
    var h = hMax / 2;

    try {
        if (xwin && !xwin.closed) xwin.close();
    } catch (e) { };

    xwin = window.open(d, 'mywindow', 'height=' + h + ',width=' + w + ',top=' + (hMax - h) / 2 + ',left=' + (wMax - w) / 2 + ',resizable=1,scrollbars=1');
    setTimeout('xwin.location=\'' + d + '\';xwin.focus()', 1500);
}

SiteCommon.Utils.OmnitureTrackPurchase = function(pageName, channel, appSku, productName, cost) {
    s.pageName = pageName;
    s.channel = channel;
    s.events = "purchase";
    s.products = "CC;" + productName + ";1;" + cost;
    SiteCommon.Utils.OmnitureSubmit();
}

SiteCommon.Utils.OmnitureTrackThumbnailClick = function(thumbnailNumber) {
    s.prop18 = "Slot " + thumbnailNumber;
    SiteCommon.Utils.OmnitureSubmit();
}

SiteCommon.Utils.OmnitureTrackThumbnailPage = function(thumbnailPage) {
    s.prop18 = "Page " + thumbnailPage;
    SiteCommon.Utils.OmnitureSubmit();
}

SiteCommon.Utils.OmnitureTrackSearch = function(searchQuery) {
    s.prop32 = searchQuery;
    SiteCommon.Utils.OmnitureSubmit();
}

SiteCommon.Utils.OmnitureSubmit = function() {
    var s_code = s.t(); if (s_code) document.write(s_code);
}

SiteCommon.Utils.elementTypes = { div: 'div', span: 'span', a: 'a', img: 'img',
    p: 'p', table: 'table', row: 'tr', column: 'td'
};
/// <field name="elementTypes">An enum of possible element types used in the function SiteCommon.Utils.createElement.</field>

SiteCommon.Utils.chartIds = { 'popular': 'popular', 'whatshot': 'whatshot', 'whatsnew': 'whatsnew' };
/// <field name="chartIds">A static list of possible chart types.</field>

SiteCommon.Utils.registerClass('SiteCommon.Utils', Sys.Component);


/// ***************************************************************************************************************
///
///     This function has been taken from MicrosoftAjax.js, and is used to complete the response codes sent
///     back to the callback once a proper WCF call has taken place.  It has been copied here to allow a single
///     line change (highlighted in place) to support REST calls.  The problem is that when an HTTP errorcode
///     (or rather an non-200 response) is enountered this library throws away the server response and only
///     returns a chunk of generic information.  This does not support REST api's that should allow additional
///     (partial) data to be returned along with the StatusCode.
///
///     The single change below ensures that the original server response is passed through, unless it is
///     of type HTML.  This allows any of our REST overloaded responses to make it through, but html errors
///     (server errors before out code, some LiveID errors, etc) will be passed through in the original
///     format.
///
///     The entire function is defined here, as it is the only way to modify the line in question, and due
///     to JSON, we can just redefine a function at any time and it will be used instead of the original.
///
/// ***************************************************************************************************************
Sys.Net.WebServiceProxy.invoke = function Sys$Net$WebServiceProxy$invoke(servicePath, methodName, useGet, params, onSuccess, onFailure, userContext, timeout) {
    /// <summary locid="M:J#Sys.Net.WebServiceProxy.invoke" />
    /// <param name="servicePath" type="String"></param>
    /// <param name="methodName" type="String"></param>
    /// <param name="useGet" type="Boolean" optional="true"></param>
    /// <param name="params" mayBeNull="true" optional="true"></param>
    /// <param name="onSuccess" type="Function" mayBeNull="true" optional="true"></param>
    /// <param name="onFailure" type="Function" mayBeNull="true" optional="true"></param>
    /// <param name="userContext" mayBeNull="true" optional="true"></param>
    /// <param name="timeout" type="Number" optional="true"></param>
    /// <returns type="Sys.Net.WebRequest"></returns>
    var e = Function._validateParams(arguments, [
        { name: "servicePath", type: String },
        { name: "methodName", type: String },
        { name: "useGet", type: Boolean, optional: true },
        { name: "params", mayBeNull: true, optional: true },
        { name: "onSuccess", type: Function, mayBeNull: true, optional: true },
        { name: "onFailure", type: Function, mayBeNull: true, optional: true },
        { name: "userContext", mayBeNull: true, optional: true },
        { name: "timeout", type: Number, optional: true }
    ]);
    if (e) throw e;
    var request = new Sys.Net.WebRequest();
    request.get_headers()['Content-Type'] = 'application/json; charset=utf-8';
    if (!params) params = {};
    var urlParams = params;
    if (!useGet || !urlParams) urlParams = {};
    request.set_url(Sys.Net.WebRequest._createUrl(servicePath + "/" + encodeURIComponent(methodName), urlParams));
    var body = null;
    if (!useGet) {
        body = Sys.Serialization.JavaScriptSerializer.serialize(params);
        if (body === "{}") body = "";
    }
    request.set_body(body);
    request.add_completed(onComplete);
    if (timeout && timeout > 0) request.set_timeout(timeout);
    request.invoke();
    function onComplete(response, eventArgs) {
        if (response.get_responseAvailable()) {
            var statusCode = response.get_statusCode();
            var result = null;

            try {
                var contentType = response.getResponseHeader("Content-Type");
                if (contentType.startsWith("application/json")) {
                    result = response.get_object();
                }
                else if (contentType.startsWith("text/xml")) {
                    result = response.get_xml();
                }
                else {
                    result = response.get_responseData();
                }
            } catch (ex) {
            }
            var error = response.getResponseHeader("jsonerror");
            var errorObj = (error === "true");
            if (errorObj) {
                if (result) {
                    result = new Sys.Net.WebServiceError(false, result.Message, result.StackTrace, result.ExceptionType);
                }
            }
            else if (contentType.startsWith("application/json")) {
                if (!result || typeof (result.d) === "undefined") {
                    throw Sys.Net.WebServiceProxy._createFailedError(methodName, String.format(Sys.Res.webServiceInvalidJsonWrapper, methodName));
                }
                result = result.d;
            }
            if (((statusCode < 200) || (statusCode >= 300)) || errorObj) {
                if (onFailure) {
                    // **************************************************************************************
                    // Changed this line to only clobber result here if an html response has been found.
                    if (contentType.startsWith("text/html")) {
                        // **************************************************************************************
                        result = new Sys.Net.WebServiceError(false, String.format(Sys.Res.webServiceFailedNoMsg, methodName), "", "");
                    }
                    result._statusCode = statusCode;
                    onFailure(result, userContext, methodName);
                }
                else {
                    var error;
                    if (result && errorObj) {
                        error = result.get_exceptionType() + "-- " + result.get_message();
                    }
                    else {
                        error = response.get_responseData();
                    }
                    throw Sys.Net.WebServiceProxy._createFailedError(methodName, String.format(Sys.Res.webServiceFailed, methodName, error));
                }
            }
            else if (onSuccess) {
                onSuccess(result, userContext, methodName);
            }
        }
        else {
            var msg;
            if (response.get_timedOut()) {
                msg = String.format(Sys.Res.webServiceTimedOut, methodName);
            }
            else {
                msg = String.format(Sys.Res.webServiceFailedNoMsg, methodName)
            }
            if (onFailure) {
                onFailure(new Sys.Net.WebServiceError(response.get_timedOut(), msg, "", ""), userContext, methodName);
            }
            else {
                throw Sys.Net.WebServiceProxy._createFailedError(methodName, msg);
            }
        }
    }
    return request;
}

/// <reference name="MicrosoftAjax.js"/>
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  PurchaseHistoryData.js
*
*      Processes the app data, abstracting it from the UI.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.PurchaseHistoryData = function() {
    UserPCSite.PurchaseHistoryData.initializeBase(this);
}

UserPCSite.PurchaseHistoryData.prototype = {
    initialize: function() {
        /// <summary>
        ///     Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
        /// </summary>
        /// <returns>
        ///     A newly created data object.
        /// </returns>

        this._Loc = SiteCommon.Utils.JsonParser(this.locDict);
        UserPCSite.PurchaseHistoryData.callBaseMethod(this, 'initialize');
    },
    _getGroup: function() {
        /// <summary>
        ///     Gets the list of data
        /// </summary>
        /// <returns>Group acquired.</returns>
        var g;
        try {
            g = this._json.ItemList;
        }
        catch (exception) {
            throw ("Purchase Details List structure invalid.");
        }
        return g;
    },
    getLength: function() {
        /// <summary>
        ///     Gets the number of items currently acquired in the group.
        /// </summary>
        /// <returns>Count of items acquired in the group.</returns>
        var length = 0;
        var g = this._getGroup();
        if (!((g == null) && (g.NumItems == null))) {
            length = g.NumItems;
        }
        return length;
    },
    _getItem: function(index) {
        /// <summary>
        ///     Get the item referenced by index in the group specified
        /// </summary>
        /// <param name="index">index of the referenced item</param>  
        /// <returns>purchase details item at the specified index.</returns>
        var value = null;
        try {
            value = this._getGroup().List[index];
        }
        catch (exception) {
            throw ("purchase details item index is out of range");
        }
        return value;
    },
    getPurchaseDate: function(index) {
        /// <summary>
        ///     get the purchase date of an application
        /// </summary>
        /// <param name="index">index of the referenced item</param>
        /// <returns>purchase date</returns>
        return this._getItem(index).PurchaseDate;
    },
    getProductName: function(index) {
        /// <summary>
        ///     get the name of the application
        /// </summary>  
        /// <param name="index">index of the referenced item</param>  
        /// <returns>name</returns>
        return this._getItem(index).ProductName;
    },
    getProductIcon: function(index) {
        /// <summary>
        ///     get the product icon of an application
        /// </summary>
        /// <param name="index">index of the referenced item</param>
        /// <returns>product icon</returns>
        return this._getItem(index).ProductIcon;
    },
    getPublisher: function(index) {
        /// <summary>
        ///     get the publisher of the application
        /// </summary>
        /// <param name="index">index of the referenced item</param>
        /// <returns>publisher</returns>
        return this._getItem(index).Publisher;
    },
    getPlatform: function(index) {
        /// <summary>
        ///     get the platform for the application
        /// </summary>
        /// <param name="index">index of the referenced item</param>
        /// <returns>platform</returns>
        return this._Loc[this._getItem(index).Platform];
    },
    getIsMOBilling: function(index) {
        /// <summary>
        ///     bool indicating MO vs DTC purchase
        /// </summary>
        /// <param name="index">index of the referenced item</param>
        /// <returns>is MO billing</returns>
        return this._getItem(index).IsMOBilling;
    },
    getPurchaseStatus: function(index) {
        /// <summary>
        ///     get the purchase status of an application
        /// </summary>
        /// <param name="index">index of the referenced item</param>
        /// <returns>status</returns>
        return this._getItem(index).PurchaseStatus;
    },
    getPrice: function(index) {
        /// <summary>
        ///     get the price of the application
        /// </summary>
        /// <param name="index">index of the referenced item</param>
        /// <returns>price</returns>
        var price = this._getItem(index).Price;
        if (SiteCommon.Utils.isNullOrUndefined(price)) {
            price = 0;
        }
        return price;
    },
    getPriceUI: function(index) {
        /// <summary>
        ///     get the price of the application as a UI text node
        /// </summary>
        /// <param name="index">index of the referenced item</param>
        /// <returns>price</returns>
        var price = this.getPrice(index);
        if (price == 0) {
            price = this._Loc.Free;
        }
        return SiteCommon.Utils.toUIString(price);
    },
    getOrderID: function(index) {
        /// <summary>
        ///     get the csat order id for the purchase of an app
        /// </summary>
        /// <param name="index">index of the referenced item</param>
        /// <returns>order id</returns>
        return this._getItem(index).CSATOrderID;
    },
    getTransactionID: function(index) {
        /// <summary>
        ///     get the transaction id for the purchase of an app
        /// </summary>
        /// <param name="index">index of the referenced item</param>
        /// <returns>transaction id</returns>
        return this._getItem(index).TransactionID;
    },
    getCSATAccountID: function(index) {
        /// <summary>
        ///     get the CSAT account id for this purchase
        /// </summary>
        /// <param name="index">index of the referenced item</param>
        /// <returns>csat acct id</returns>
        return this._getItem(index).CSATAccountID;
    },
    getVersion: function(index) {
        /// <summary>
        ///     get the version of the application
        /// </summary>
        /// <param name="index">index of the referenced item</param>
        /// <returns>version</returns>
        return this._getItem(index).Version;
    },
    getPublisherWebsite: function(index) {
        /// <summary>
        ///     get the app seller's website
        /// </summary>
        /// <param name="index">index of the referenced item</param>
        /// <returns>website</returns>
        return this._getItem(index).PublisherWebsite;
    },
    getType: function(index) {
        /// <summary>
        ///     get the type of the application
        /// </summary>
        /// <param name="index">index of the referenced item</param>
        /// <returns>type</returns>
        return this._getItem(index).Type;
    },
    getTax: function(index) {
        /// <summary>
        ///     get the tax for this application
        /// </summary>
        /// <param name="index">index of the referenced item</param>
        /// <returns>tax</returns>
        var tax = this._getItem(index).Tax;
        if (SiteCommon.Utils.isNullOrUndefined(tax)) {
            tax = this._Loc.Free;
        }
        return tax;
    },
    getPriceWithTax: function(index) {
        /// <summary>
        ///     get the tax for this application
        /// </summary>
        /// <param name="index">index of the referenced item</param>
        /// <returns>tax</returns>
        var priceWithTax = this._getItem(index).PriceWithTax;
        if (SiteCommon.Utils.isNullOrUndefined(priceWithTax)) {
            priceWithTax = this._Loc.Free;
        }
        return priceWithTax;
    },
    getPaymentMethod: function(index) {
        /// <summary>
        ///     get the payment method for the application
        /// </summary>
        /// <param name="index">index of the referenced item</param>
        /// <returns>payment method</returns>
        return "";
    },
    getProductID: function(index) {
        /// <summary>
        ///     get the product id
        /// </summary>
        /// <param name="index">index of the referenced item</param>
        /// <returns>product id</returns>
        return this._getItem(index).ProductID;
    },
    getProductAppId: function(index) {
        /// <summary>
        ///     get the app id
        /// </summary>
        /// <param name="index">index of the referenced item</param>
        /// <returns>app id</returns>
        return this._getItem(index).ApplicationId;
    },
    getIsFreeApp: function(index) {
        /// <summary>
        ///     whether or not an app is free
        /// </summary>
        /// <param name="index">index of the referenced item</param>
        /// <returns>true for free, false otherwise</returns>
        var price = this._getItem(index).Price;
        return SiteCommon.Utils.isNullOrUndefined(price);
    },
    dispose: function() {
        /// <summary>
        ///     Dispose of any local resources.
        /// </summary>
        UserPCSite.PurchaseHistoryData.callBaseMethod(this, 'dispose');
    }
}

UserPCSite.PurchaseHistoryData.prototype.get_json = function() { return this._json; };
UserPCSite.PurchaseHistoryData.prototype.set_json = function(json) { this._json = json; }

UserPCSite.PurchaseHistoryData.prototype.locDict = null;
/// <field name="locDict">The dictionary to receive localized strings.</field>

UserPCSite.PurchaseHistoryData.registerClass('UserPCSite.PurchaseHistoryData', Sys.Component);


/// <reference name="MicrosoftAjax.js"/>
/// <reference path="~/Common/AjaxControls/InteractiveBaseControl.js" />
/// <reference path="~/AjaxControls/PurchaseHistoryController.js" />
/// <reference path="~/AjaxControls/PurchaseHistoryData.js" />
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  PurchaseHistoryView.js
*
*      Displays the list of products.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.PurchaseHistoryView = function(element) {
    /// <summary>
    ///     CategoryListView Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <param name="element">HTML DomElement to associate.</param>
    /// <returns>
    ///     A newly created PurchaseHistoryView object.
    /// </returns>
    UserPCSite.PurchaseHistoryView.initializeBase(this, [element]);
}

UserPCSite.PurchaseHistoryView.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///     
        ///     Initializes the view.
        /// </summary>

        this._listItems = new Array();
        this.add_click(this._onClick);

        this._Loc = SiteCommon.Utils.JsonParser(this.get_controller().locDict);

        // Do local initialization (needed for_setupRender before calling base class, as it will call _setupRender).
        UserPCSite.PurchaseHistoryView.callBaseMethod(this, 'initialize');
    },
    _createResultsText: function(id) {
        /// <summary>
        ///     Creates the Results text div element.
        /// </summary>
        /// <param name="id">Id to assign to the results text div.</param>
        /// <returns>
        ///     A newly created Results text div element.
        /// </returns>

        //Panel to hold the result string
        var divResultText = document.createElement('div');
        divResultText.setAttribute('id', id);
        Sys.UI.DomElement.addCssClass(divResultText, 'PurchaseHistoryViewResults');
        Sys.UI.DomElement.setVisibilityMode(divResultText, Sys.UI.VisibilityMode.hide);

        // Create a div for the Status Panel header
        this._divHeaderBar = SiteCommon.Utils.CreateDiv(this.get_element(), this.get_id() + '_Header', 'PurchaseHistoryViewHeader', null);
        Sys.UI.DomElement.setVisibilityMode(this._divHeaderBar, Sys.UI.VisibilityMode.collapse);

        //Add column headings
        SiteCommon.Utils.CreateDiv(this._divHeaderBar, this._divHeaderBar.getAttribute('id') + '_date',
                        'PurchaseHistoryViewHeading PurchaseHistoryViewDateWidth', SiteCommon.Utils.toUIString(this._Loc.PurchaseDate));
        SiteCommon.Utils.CreateDiv(this._divHeaderBar, this._divHeaderBar.getAttribute('id') + '_product',
                        'PurchaseHistoryViewHeading PurchaseHistoryViewNameWidth', SiteCommon.Utils.toUIString(this._Loc.Product));
        SiteCommon.Utils.CreateDiv(this._divHeaderBar, this._divHeaderBar.getAttribute('id') + '_publisher',
                        'PurchaseHistoryViewHeading PurchaseHistoryViewPublisherWidth', SiteCommon.Utils.toUIString(this._Loc.Publisher));
        SiteCommon.Utils.CreateDiv(this._divHeaderBar, this._divHeaderBar.getAttribute('id') + '_platform',
                        'PurchaseHistoryViewHeading PurchaseHistoryViewPlatformWidth', SiteCommon.Utils.toUIString(this._Loc.Platform));
        SiteCommon.Utils.CreateDiv(this._divHeaderBar, this._divHeaderBar.getAttribute('id') + '_appStatus',
                        'PurchaseHistoryViewHeading PurchaseHistoryViewAppStatusWidth', SiteCommon.Utils.toUIString(this._Loc.AppStatus));
        SiteCommon.Utils.CreateDiv(this._divHeaderBar, this._divHeaderBar.getAttribute('id') + '_cost',
                        'PurchaseHistoryViewHeading PurchaseHistoryViewPriceWidth', SiteCommon.Utils.toUIString(this._Loc.Cost));
        SiteCommon.Utils.CreateDiv(this._divHeaderBar, this._divHeaderBar.getAttribute('id') + '_options',
                        'PurchaseHistoryViewHeading PurchaseHistoryViewOptionsWidth', SiteCommon.Utils.toUIString(""));

        return divResultText;
    },
    _setupRender: function() {
        /// <summary>
        ///     Sets up the panel dom element.
        /// </summary>

        // Set the main CSS style for the element    
        Sys.UI.DomElement.addCssClass(this.get_element(), 'PurchaseBox');

        // Create a div for the Results Text
        this._divResultText = this._createResultsText(this.get_id() + '_ResultText');
        this._divHeaderBar.appendChild(this._divResultText);

        // Create an array to hold the list items
        this._listItems = new Array();

        // Create a div for status information (loading, failed, no results, etc)
        this._divStatusInfo = document.createElement('div');
        Sys.UI.DomElement.setVisibilityMode(this._divStatusInfo, Sys.UI.VisibilityMode.collapse);
        this.get_element().appendChild(this._divStatusInfo);
        this.displayLoading();

    },
    dataRender: function() {
        /// <summary>
        ///     Update the UI with main data components from controller.
        /// </summary>

        // Hide the status UI
        Sys.UI.DomElement.setVisible(this._divStatusInfo, false);

        var data = this.get_controller().get_data();

        if (data.getLength() == 0) {

            // add the empty list message
            this._emptyDiv = document.createElement('div');
            Sys.UI.DomElement.addCssClass(this._emptyDiv, 'PurchaseItemBox');

            SiteCommon.Utils.CreateDiv(this._emptyDiv, this.get_id() + '_NoHistory',
                        'PurchaseHistoryEmpty', SiteCommon.Utils.toUIString(this._Loc.NoHistory));

            this.get_element().appendChild(this._emptyDiv);
        }

        //Show/Hide header string
        Sys.UI.DomElement.setVisible(this._divResultText, (data.getLength() != 0));
    },
    resetRender: function() {
        /// <summary>
        ///     Reset the UI to initial state.
        /// </summary>

        if (this._emptyDiv != null) {
            this.get_element().removeChild(this._emptyDiv);
            this._emptyDiv = null;
        }

        // Clear the list of items
        for (var i = 0; i < this._listItems.length; i++) {
            this.get_element().removeChild(this._listItems[i]);
        }
        delete this._listItems;
        this._listItems = new Array();
    },
    displayLoading: function() {
        /// <summary>
        ///     Displays loading message.
        /// </summary>

        this._divStatusInfo.innerHTML = "";
        var div = SiteCommon.Utils.createLoadingDiv(this._Loc.Loading);
        this._divStatusInfo.appendChild(div);
        Sys.UI.DomElement.setVisible(this._divStatusInfo, true);
    },
    displayFailed: function() {
        /// <summary>
        ///     Displays failure message.
        /// </summary>
        this._divStatusInfo.innerHTML = "";
        var reloadObj = SiteCommon.Utils.createReloadDiv(this.get_id(), this._Loc.Refresh, this._Loc.ServerBusy);
        this.addEventTarget(SiteCommon.InteractiveBaseView.eventTypes.click,
                                reloadObj.reloadButton.getAttribute('id'),
                                { 'isReload': true });
        this._divStatusInfo.appendChild(reloadObj.div);
        Sys.UI.DomElement.setVisible(this._divStatusInfo, true);
    },
    addPurchaseHistoryRow: function(dataContext) {
        /// <summary>
        ///     Add a product to the navigation list.
        /// </summary>
        /// <param name="dataContext">Data context to refer to the data elements.</param>

        var index = this._listItems.length;

        // Create the list item
        var div = this._createRowItem(this.get_id(), dataContext);
        this._listItems[index] = div;
        this.get_element().appendChild(div);
    },
    dispose: function() {
        /// <summary>
        ///     Called to dispose of this objects resources.
        /// </summary>
        UserPCSite.PurchaseHistoryView.callBaseMethod(this, 'dispose');
    },
    _createRowItem: function(id, dataContext) {
        /// <summary>
        ///     Create a list item and returns the wrapping Div DOM element.
        /// </summary>
        /// <param name="id">DOM ID to prefix to the resulting elements' IDs.</param>
        /// <param name="dataContext">Data context to refer to the data elements.</param>
        /// <returns>
        ///     A reference the wrapping DOM element.
        /// </returns>

        var data = this.get_controller().get_data();

        // Create the item wrapper.
        var div = document.createElement('div');
        div.setAttribute('id', id + '_' + dataContext);
        Sys.UI.DomElement.addCssClass(div, 'PurchaseItemBox');


        // set the z-index dynamically because each subsequent row item must have a z-index one lower than the one above it.
        // This is due to IE which establishes a new stacking context with z-index:1 for all positioned elements regardless of whether they have a z-index.
        // And we must have PurchaseItemBox be relative positioned because the button popup menu positions itself relative to the PurchaseItemBox boundaries.
        // We start the z-index at 99 because it must be lower than ProductItemButtonPopup which is 100.
        // dataContext = 0, 1, 2, ...
        div.style.cssText = 'z-index:' + (99 - dataContext);

        var purchaseDate = SiteCommon.Utils.CreateDiv(
                        div, id + '_' + dataContext + '_purchaseDateLabel',
                        'PurchaseHistoryViewBaseCell PurchaseHistoryViewDateWidth',
                        SiteCommon.Utils.toUIString(
                            (new Date(data.getPurchaseDate(dataContext))).localeFormat(
                                this._Loc.UserMarketDateFormat)));

        // name
        SiteCommon.Utils.CreateLink(div, id + '_' + dataContext + '_nameLabel',
                        'PurchaseHistoryViewBaseCell PurchaseHistoryViewNameWidth', SiteCommon.Utils.toUIString(data.getProductName(dataContext)), 
                        null, UserPCSite.Utils.getDetailsURL(data.getProductAppId(dataContext)));

        var publisher = SiteCommon.Utils.CreateDiv(div, id + '_' + dataContext + '_publisherLabel',
                        'PurchaseHistoryViewBaseCell PurchaseHistoryViewPublisherWidth', SiteCommon.Utils.toUIString(data.getPublisher(dataContext)));

        var platform = SiteCommon.Utils.CreateDiv(div, id + '_' + dataContext + '_platformLabel',
                        'PurchaseHistoryViewBaseCell PurchaseHistoryViewPlatformWidth', SiteCommon.Utils.toUIString(data.getPlatform(dataContext)));

        var appStatus = SiteCommon.Utils.CreateDiv(div, id + '_' + dataContext + '_appStatusLabel',
                        'PurchaseHistoryViewBaseCell PurchaseHistoryViewAppStatusWidth', SiteCommon.Utils.toUIString(data.getPurchaseStatus(dataContext)));

        var price = SiteCommon.Utils.CreateDiv(div, id + '_' + dataContext + '_priceLabel',
                        'PurchaseHistoryViewBaseCell PurchaseHistoryViewPriceWidth', SiteCommon.Utils.toUIString(data.getPriceWithTax(dataContext)));

        // TODO: from createbutton
        var optionsButtonDiv = SiteCommon.Utils.CreateDiv(div, id + '_' + dataContext + '_optionsLabel',
                        'PurchaseHistoryViewBaseCell PurchaseHistoryViewOptionsWidth');

        optionsButtonDiv.setAttribute('id', id + '_' + dataContext + '_wrapper');
        Sys.UI.DomElement.addCssClass(optionsButtonDiv, 'WhiteButton');
        Sys.UI.DomElement.addCssClass(optionsButtonDiv, 'CursorHand');

        var button = document.createElement('a');
        button.setAttribute('id', id + '_' + dataContext + '_button');
        button.appendChild(SiteCommon.Utils.toUIString(this._Loc.Details));
        Sys.UI.DomElement.addCssClass(button, 'WhiteButton' + 'Text');

        var divRight = document.createElement('div');
        divRight.setAttribute('id', id + '_' + dataContext + '_right');
        Sys.UI.DomElement.addCssClass(divRight, 'WhiteButton' + 'Div');

        optionsButtonDiv.appendChild(button);
        optionsButtonDiv.appendChild(divRight);



        this.addEventTarget(SiteCommon.InteractiveBaseView.eventTypes.click, id + '_' + dataContext + '_button', { 'eventType': 'purchaseDetails',
            'index': dataContext
        });

        return div;
    },
    _onClick: function(sender, args) {
        /// <summary>
        ///     Click event handler for the list items.
        /// </summary>
        /// <param name="sender">PurchaseHistoryView object that is clicked on.</param>
        /// <param name="args">Event args.</param>
        if (args.customData != null) {
            if (args.customData.eventType == 'purchaseDetails') {
                //Handle item title click
                sender.get_controller().handlePurchaseDetails(args.customData.index);

            }
            else if (args.customData.eventType == 'pagination') {
                sender.get_controller().handlePagination(args.customData.page);
            }
            else if (args.customData.eventType == 'showall') {
                sender.get_controller().showSeverityGroup(null);
            }
            else if (args.customData.isReload) {
                sender.get_controller().reloadClicked();
            }
        }
    },
    drawPagination: function(pageStart, pageEnd, pageSelected) {
        var parent = this.get_element();
        var id = parent.getAttribute('id') + '_pagination';

        //Remove old pagination div from the view
        if (this._paginationDiv != null) {
            parent.removeChild(this._paginationDiv);
            delete this._paginationDiv;
        }

        this._paginationDiv = SiteCommon.Utils.CreateDiv(parent, id, 'PaginationContainer', null);

        var prevCss = 'sprite PaginationPreviousIcon FloatLeft';
        if (pageStart < pageSelected)
            prevCss += ' CursorHand PaginationPreviousIcon-active';
        var prevLink = SiteCommon.Utils.CreateLink(this._paginationDiv, id + '_previous', prevCss, null, null, null);
        if (pageStart < pageSelected) {
            this.addEventTarget(SiteCommon.InteractiveBaseView.eventTypes.click,
                                id + '_previous',
                                { 'eventType': 'pagination',
                                    'page': (pageSelected - 1)
                                });
        }
        else {
            // disable the link to remove it from the tab order (accessibility). Left as <a> rather than <div> for DOM consistency.
            prevLink.setAttribute('disabled', 'true'); 
        }

        for (var i = pageStart; i <= pageEnd; i++) {
            if (pageSelected == i) { // current page (no link)
                SiteCommon.Utils.CreateDiv(this._paginationDiv, id + i,
                        'PaginationEntry PaginationEntrySelected FloatLeft',
                        SiteCommon.Utils.toUIString(i));
            }
            else { 
                SiteCommon.Utils.CreateLink(this._paginationDiv, id + i,
                        'PaginationEntry CursorHand FloatLeft',
                        SiteCommon.Utils.toUIString(i), null, null);
                this.addEventTarget(SiteCommon.InteractiveBaseView.eventTypes.click,
                                id + i,
                                { 'eventType': 'pagination',
                                    'page': i
                                });
            }
        }

        var nextCss = 'sprite PaginationNextIcon FloatLeft';
        if (pageSelected < pageEnd)
            nextCss += ' CursorHand PaginationNextIcon-active';
        var nextLink = SiteCommon.Utils.CreateLink(this._paginationDiv, id + '_next', nextCss, null, null, null);
        if (pageSelected < pageEnd) {
            this.addEventTarget(SiteCommon.InteractiveBaseView.eventTypes.click,
                                id + '_next',
                                { 'eventType': 'pagination',
                                    'page': (pageSelected + 1)
                                });
        }
        else {
            // disable the link to remove it from the tab order (accessibility). Left as <a> rather than <div> for DOM consistency.
            nextLink.setAttribute('disabled', 'true'); 
        }

        //Add clear:both div
        SiteCommon.Utils.CreateDiv(this._paginationDiv, id + '_Clear', 'clear', null);

        return this._paginationDiv;
    }
}

UserPCSite.PurchaseHistoryView.registerClass('UserPCSite.PurchaseHistoryView', SiteCommon.InteractiveBaseView);



/// <reference name="MicrosoftAjax.js"/>
/// <reference path="~/Common/AjaxControls/IController.js"/>
/// <reference path="~/AjaxControls/PurchaseHistoryData.js"/>
/// <reference path="~/AjaxControls/PurchaseHistoryView.js"/>
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  PurchaseHistoryController.js
*
*       This class is used to drive the product list panel.  It sets up
*       the data class, and initializes the UI classes.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.PurchaseHistoryController = function(element) {
    /// <summary>
    ///     CategoryListController Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <param name="element">HTML DomElement to associate.</param>
    /// <returns>
    ///     A newly created CategoryListController.
    /// </returns>
    UserPCSite.PurchaseHistoryController.initializeBase(this, [element]);
}

UserPCSite.PurchaseHistoryController.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///     
        ///     Initializes the controller.
        /// </summary>
        UserPCSite.PurchaseHistoryController.callBaseMethod(this, 'initialize');
    },
    initializeRequest: function() {
        /// <summary>
        ///     initializeRequest is called to initialize the ajax data request needed by
        ///     this controller, as well as any additional requirements.
        /// </summary>

        var div = document.createElement('div');
        div.setAttribute('id', this.get_id() + '_view');
        this.get_element().appendChild(div);
        // Create the panel view element
        this._view = $create(UserPCSite.PurchaseHistoryView,
                                 { 'controller': this
                                 },
                                 {},
                                 {},
                                 div);
        this.makeRequest();
    },
    makeRequest: function(group) {
        /// <summary>
        ///     Issue the actual ajax request.
        /// </summary>
        /// <param name="group">Optional parameter to only display items belonging to the specified
        /// severity group</param>

        this._view.displayLoading();

        //If server side asks us to switch to a specified group, then we do so and clear this stored value
        if (group == null && this.groupId != null) {
            group = this.groupId;
            this.groupId = null;
        }

        // Request the purchase history from the server
        var service = new UserPCSite.Profiles();
        service.GetUserPurchaseHistory('StartDate', 'EndDate', 0, 0, this.successfulResponse, this.failureResponse, this);
        this._displayedGroup = group;
    },
    successfulResponse: function(response, context) {
        /// <summary>
        ///     Called when a data query has successfully completed.
        /// </summary>
        /// <param name="response">A JSON encoded object representing the data acquired.</param>
        /// <param name="context">A reference to the controller that issued the request</param>

        // Create the data object with the response object
        context._data = $create(UserPCSite.PurchaseHistoryData,
                                 { 'json': response,
                                     'locDict': context.locDict
                                 },
                                 {},
                                 {},
                                 null);

        // Setup the main UI components.
        context._view.dataRender();

        context.handlePagination(1);
    },
    calculateNumPages: function() {
        /// <summary>
        ///     Calculate the number of pages based on the number of items and page size
        /// </summary>
        return Math.ceil(this._data.getLength() / this.maxPageLength);
    },
    displayData: function(page) {
        /// <summary>
        ///     Display the product list for the specified page number
        /// </summary>
        /// <param name="page">Page number to display.</param>
        if (this._data.getLength() > 0) {
            this._view.resetRender(page);
            this._view.dataRender();
            // Add items to the view for each data element.
            for (var i = this.getStart(page);
            (i <= this.getEnd(page));
            i++) {
                this._view.addPurchaseHistoryRow(i);
            }
        }
    },
    handlePagination: function(page) {
        /// <summary>
        ///     Handle the pagination of the product list for the specified page number
        /// </summary>
        /// <param name="page">Page number to display.</param>
        var numPages = this.calculateNumPages();
        if (1 <= page && page <= numPages) {
            this._page = page;
            this.displayData(page);
            this._view.drawPagination(1, numPages, page);
        }
    },
    getEFormSupportUrl: function(index) {

        // create the base eform support url, for example:
        // https://support.live-int.com/default.aspx?productKey=wmm&ct=eformcs&sky=1

        var data = this._view.get_controller().get_data();

        var mktParams = '&mkt=' + this._view._Loc.mkt;

        // get base url
        var baseUrl = this._view.get_controller().basePurchaseSupportUrl;

        baseUrl += "?ct=eformcs";
        var currentDate = new Date();

        // parameters
        // format: &{!}Param=Value
        var mainParams = String.format("&{{!}}CurrentDate={0}&{{!}}PurchaseDate={1}&{{!}}ProductID={2}&{{!}}ProductName={3}&{{!}}ProductVersion={4}&{{!}}Publisher={5}&{{!}}Platform={6}&{{!}}Price={7}&{{!}}TransactionID={8}&{{!}}OrderID={9}",
        encodeURI(currentDate),
        encodeURI(data.getPurchaseDate(index)),
        encodeURI(data.getProductID(index)),
        encodeURI(data.getProductName(index)),
        encodeURI(data.getVersion(index)),
        encodeURI(data.getPublisher(index)),
        encodeURI(data.getPlatform(index)),
        encodeURI(data.getPrice(index)),
        encodeURI(data.getTransactionID(index)),
        encodeURI(data.getOrderID(index)));

        // either "wmm", "wmmmo", or wmmcc"
        var paymentMethod = "";
        var txParams = "";

        if (data.getIsFreeApp(index)) {
            paymentMethod += "&productKey=wmm&sid=wmm&brand=wmm";
        }
        else {
            if (data.getIsMOBilling(index) == "1") {
                paymentMethod += "&productKey=wmmmo&sid=wmmmo&brand=wmm";
            }
            else {
                paymentMethod += "&productKey=wmmcc&sid=wmmcc&brand=wmm";
            }

            txParams = String.format("&{{!}}PaymentMethod={0}",
                encodeURI(data.getPaymentMethod(index)));
        }

        return baseUrl + paymentMethod + mainParams + txParams + mktParams;
    },
    handlePurchaseDetails: function(index) {
        /// <summary>
        ///     when clicking on the "options" button, launch purchase history details page
        /// </summary>
        /// <param name="index">index of the purchase history item</param>

        var data = this._view.get_controller().get_data();

        var divContent = document.createElement('div');
        Sys.UI.DomElement.addCssClass(divContent, 'PopupDialogContent');

        // div for product icon
        var divProductIcon = SiteCommon.Utils.CreateDiv(divContent, this.get_id() + '_productIcon',
                        'PurchaseHistoryDetailsProductIcon', null);

        // product icon
        var imgProductIcon = SiteCommon.Utils.CreateImg(divProductIcon, this.get_id() + '_productIconImg',
                        null, SiteCommon.Utils.FixUrlProtocol(data.getProductIcon(index)), null);

        // div for company details, etc
        var divCompanyDetails = SiteCommon.Utils.CreateDiv(divContent, this.get_id() + '_companyDetails',
                        null, null);

        // app name
        var divAppName = SiteCommon.Utils.CreateDiv(divCompanyDetails, this.get_id() + '_appName',
                        'PurchaseHistoryDetailsProductName', SiteCommon.Utils.boldFirstWord(SiteCommon.Utils.toUIString(
                                                                SiteCommon.Utils.AddEllipses(data.getProductName(index),
                                                                    UserPCSite.PurchaseHistoryController.nameLength.myapps))));

        // company
        var divCompany = SiteCommon.Utils.CreateDiv(divCompanyDetails, this.get_id() + '_publisherName',
                        'PurchaseHistoryDetailsPublisherName', SiteCommon.Utils.toUIString(this._view._Loc.By));
        divCompany.appendChild(SiteCommon.Utils.toUIString(data.getPublisher(index)));

        // version
        var divVersion = SiteCommon.Utils.CreateDiv(divCompanyDetails, this.get_id() + '_version',
                        null, SiteCommon.Utils.toUIString(this._view._Loc.Version + " "));
        divVersion.appendChild(SiteCommon.Utils.toUIString(data.getVersion(index)));

        // seller's website
        var divWebsite = SiteCommon.Utils.CreateDiv(divCompanyDetails, this.get_id() + '_website',
                        null, SiteCommon.Utils.toUIString(this._view._Loc.SellerWebsite));

        var companyUrl = String(data.getPublisherWebsite(index));
        if (companyUrl.indexOf('http', 0) != 0) {
            companyUrl = 'http://' + companyUrl;
        }
        var lnkCompanyURL = SiteCommon.Utils.CreateLink(divWebsite, 'website_link', 'CursorHand',
                        SiteCommon.Utils.toUIString(data.getPublisherWebsite(index)),
                        null, companyUrl);
        lnkCompanyURL.setAttribute('target', '_blank');
        divContent.appendChild(divWebsite);

        // purchase info label
        var divPurchaseInfo = SiteCommon.Utils.CreateDiv(divContent, this.get_id() + '_divPurchaseInfo',
                        'PurchaseInfoLabel', SiteCommon.Utils.toUIString(this._view._Loc.PurchaseInfo));

        // Create a div for the purchase history details header
        var _divHeaderBar = SiteCommon.Utils.CreateDiv(this.get_element(), this.get_id() + '_Header', 'PurchaseHistoryDetailsHeader clearme', null);
        Sys.UI.DomElement.setVisibilityMode(_divHeaderBar, Sys.UI.VisibilityMode.collapse);

        //Add column headings
        SiteCommon.Utils.CreateDiv(_divHeaderBar, _divHeaderBar.getAttribute('id') + '_orderId',
                        'PurchaseHistoryDetailsHeading PurchaseHistoryDetailsOrderIdWidth', SiteCommon.Utils.toUIString(this._view._Loc.OrderId));
        SiteCommon.Utils.CreateDiv(_divHeaderBar, _divHeaderBar.getAttribute('id') + '_transactionId',
                        'PurchaseHistoryDetailsHeading PurchaseHistoryDetailsTransactionIdWidth', SiteCommon.Utils.toUIString(this._view._Loc.TransactionId));
        SiteCommon.Utils.CreateDiv(_divHeaderBar, _divHeaderBar.getAttribute('id') + '_accountId',
                        'PurchaseHistoryDetailsHeading PurchaseHistoryDetailsAccountIdWidth', SiteCommon.Utils.toUIString(this._view._Loc.AccountId));
        SiteCommon.Utils.CreateDiv(_divHeaderBar, _divHeaderBar.getAttribute('id') + '_purchaseDate',
                        'PurchaseHistoryDetailsHeading PurchaseHistoryDetailsPurchaseDateWidth', SiteCommon.Utils.toUIString(this._view._Loc.Date));
        SiteCommon.Utils.CreateDiv(_divHeaderBar, _divHeaderBar.getAttribute('id') + '_type',
                        'PurchaseHistoryDetailsHeading PurchaseHistoryDetailsTypeWidth', SiteCommon.Utils.toUIString(this._view._Loc.Type));
        SiteCommon.Utils.CreateDiv(_divHeaderBar, _divHeaderBar.getAttribute('id') + '_price',
                        'PurchaseHistoryDetailsHeading PurchaseHistoryDetailsPriceWidth', SiteCommon.Utils.toUIString(this._view._Loc.Price));

        divContent.appendChild(_divHeaderBar);


        var div = document.createElement('div');
        div.setAttribute('id', this.get_id() + '_' + index);
        Sys.UI.DomElement.addCssClass(div, 'PurchaseDetailsBox');

        var orderid = SiteCommon.Utils.CreateDiv(div, this.get_id() + '_' + index + '_orderIdLabel',
                        'PurchaseHistoryDetailsBaseCell PurchaseHistoryDetailsOrderIdWidth', SiteCommon.Utils.toUIString(data.getOrderID(index)));

        var txid = SiteCommon.Utils.CreateDiv(div, this.get_id() + '_' + index + '_transactionIdLabel',
                        'PurchaseHistoryDetailsBaseCell PurchaseHistoryDetailsTransactionIdWidth', SiteCommon.Utils.toUIString(data.getTransactionID(index)));

        var acctid = SiteCommon.Utils.CreateDiv(div, this.get_id() + '_' + index + '_accountIdLabel',
                        'PurchaseHistoryDetailsBaseCell PurchaseHistoryDetailsAccountIdWidth', SiteCommon.Utils.toUIString(data.getCSATAccountID(index)));

        var date = SiteCommon.Utils.CreateDiv(div, this.get_id() + '_' + index + '_purchaseDateLabel',
                        'PurchaseHistoryDetailsBaseCell PurchaseHistoryDetailsPurchaseDateWidth',
                        SiteCommon.Utils.toUIString(
                            data.getPurchaseDate(index).localeFormat(
                                this._view._Loc.UserMarketDateFormat)));

        var type = SiteCommon.Utils.CreateDiv(div, this.get_id() + '_' + index + '_typeLabel',
                        'PurchaseHistoryDetailsBaseCell PurchaseHistoryDetailsTypeWidth', SiteCommon.Utils.toUIString(data.getType(index)));

        var price = SiteCommon.Utils.CreateDiv(div, this.get_id() + '_' + index + '_priceLabel',
                        'PurchaseHistoryDetailsBaseCell PurchaseHistoryDetailsPriceWidth PurchaseHistoryDetailsPriceValue', data.getPriceUI(index));

        // tax
        var taxDiv = SiteCommon.Utils.CreateDiv(div, this.get_id() + '_' + index + '_taxDiv',
                        'PurchaseHistoryDetailsDiv', null);
        var taxValue = SiteCommon.Utils.CreateDiv(taxDiv, this.get_id() + '_' + index + '_taxValue',
                        'PurchaseHistoryDetailsTaxValue', SiteCommon.Utils.toUIString(data.getTax(index)));
        var taxLabel = SiteCommon.Utils.CreateDiv(taxDiv, this.get_id() + '_' + index + '_taxLabel',
                        'PurchaseHistoryDetailsTaxLabel', SiteCommon.Utils.toUIString(this._view._Loc.Tax));

        divContent.appendChild(div);

        // total price
        var totalDiv = SiteCommon.Utils.CreateDiv(divContent,
                        this.get_id() + '_' + index + '_taxDiv',
                        'PurchaseHistoryDetailsDiv', null);
        var totalPriceValue = SiteCommon.Utils.CreateDiv(totalDiv,
                        this.get_id() + '_' + index + '_totalPriceValue',
                        'PurchaseHistoryDetailsTotalPriceValue',
                        SiteCommon.Utils.toUIString(data.getPriceWithTax(index)));
        var totalPriceLabel = SiteCommon.Utils.CreateDiv(totalDiv,
                        this.get_id() + '_' + index + '_totalPriceLabel',
                        'PurchaseHistoryDetailsTotalPriceLabel',
                        SiteCommon.Utils.toUIString(this._view._Loc.Total));

        // get support button
        var getSupportButtonDiv = SiteCommon.Utils.CreateDiv(div, this.get_id() + '_' + index + '_getSupportButtonDiv',
                        null, null);
        divContent.appendChild(getSupportButtonDiv);

        // only create support button when the app is NOT free.
        if (data.getPrice(index) != 0) {
            var url = this.getEFormSupportUrl(index);
            var getSupportButton = SiteCommon.Utils.CreateButton(
                    this._view._Loc.GetSupport,
                    this.get_id() + '_' + index + '_getSupportButton',
                    'WhiteButton',
                    'SiteCommon.Utils.TriggerEvent("' + this.get_id() + '", "Support","' + url + '");');
        }
        else {
            //This is a white-space to keep the spacing consistent with/without button
            var getSupportButton = SiteCommon.Utils.CreateDiv(
                                        null,
                                        this.get_id() + '_' + index + '_getSupportButton',
                                        'PurchaseHistorySpace',
                                        null);
        }

        getSupportButtonDiv.appendChild(getSupportButton);


        // faq link
        var faq = SiteCommon.Utils.CreateDiv(div, this.get_id() + '_' + index + '_faq',
                        'PurchaseHistoryDetailsFAQ', SiteCommon.Utils.toUIString(this._view._Loc.Faq));
        divContent.appendChild(faq);

        // done button
        var doneButtonDiv = SiteCommon.Utils.CreateDiv(div, this.get_id() + '_' + index + '_doneButtonDiv',
                        'PurchaseHistoryDetailsDoneButton', null);
        divContent.appendChild(doneButtonDiv);

        var doneButton = SiteCommon.Utils.CreateButton(this._view._Loc.Done, this.get_id() + '_' + index + '_doneButton', 'OrangeButton', 'SiteCommon.Utils.closePopup();');
        doneButtonDiv.appendChild(doneButton);


        // launch popup
        SiteCommon.Utils.displayPopup(false, true, SiteCommon.Utils.toUIString(this._view._Loc.PurchaseDetails), divContent);
    },
    handleEvent: function(TargetID, url) {
        if (TargetID == 'Support') {
            SiteCommon.Utils.OpenNewWindow(url);
        }
    },
    failureResponse: function(response, context) {
        /// <summary>
        ///     Called when a data query has failed.
        /// </summary>
        /// <param name="response">A string representing the failure information.</param>
        /// <param name="context">A reference to the controller that issued the request</param>
        context._view.displayFailed();
    },
    reloadClicked: function() {
        /// <summary>
        ///     Called when the view should be reloaded.
        /// </summary>

        // Reset the view to an initial state.
        this._view.resetRender();

        // Issue the actual request.
        this.makeRequest(this._displayedGroup);
    },
    getStart: function(page) {
        /// <summary>
        ///     Gets the Start index of items currently acquired.
        /// </summary>
        /// <returns>Start index of items acquired. -1 in case of error</returns>
        if (page == null
             && this._page != null) {
            page = this._page;
        }
        else if (this._page == null) {
            return -1;
        }
        return (page - 1) * this.maxPageLength;
    },
    getStartUI: function() {
        /// <summary>
        ///     Gets UIString for the Start Index of items currently acquired.
        /// </summary>
        /// <returns>UIString Start Index of items acquired.</returns>
        return SiteCommon.Utils.toUIString(this.getStart() + 1);
    },
    getEnd: function(page) {
        /// <summary>
        ///     Gets the End index of items currently acquired.
        /// </summary>
        /// <returns>End index of items acquired. -1 in case of error</returns>
        if (page == null
             && this._page != null) {
            page = this._page;
        }
        else if (this._page == null) {
            return -1;
        }
        var end = page * this.maxPageLength;
        if (end > this._data.getLength())
            return this._data.getLength() - 1;
        return end - 1;
    },
    getEndUI: function() {
        /// <summary>
        ///     Gets UIString for the End Index of items currently acquired.
        /// </summary>
        /// <returns>UIString End Index of items acquired.</returns>
        return SiteCommon.Utils.toUIString(this.getEnd() + 1);
    },
    dispose: function() {
        /// <summary>
        ///     Dispose any local resources.
        /// </summary>
        UserPCSite.PurchaseHistoryController.callBaseMethod(this, 'dispose');
    }
}
UserPCSite.PurchaseHistoryController.prototype.groupId = null;
/// <field name="companyId">The severity group to display.</field>

UserPCSite.PurchaseHistoryController.prototype.locDict = null;
/// <field name="locDict">The dictionary to receive localized strings.</field>

UserPCSite.PurchaseHistoryController.prototype.get_data = function() { return this._data; };
/// <field name="data">Get the data element.</field>

UserPCSite.PurchaseHistoryController.prototype.maxPageLength = 5;
/// <field name="data">Number of data elements to show on each page.</field>

UserPCSite.PurchaseHistoryController.prototype.basePurchaseSupportUrl = null;
/// <field name="basePurchaseSupportUrl">support url for eform</field>

UserPCSite.PurchaseHistoryController.nameLength = { 'myapps': 40 };
/// <field name="nameLength">A static list of name text max length</field>

UserPCSite.PurchaseHistoryController.registerClass('UserPCSite.PurchaseHistoryController', Sys.UI.Control, SiteCommon.IController);

/// <reference name="MicrosoftAjax.js"/>
/// <reference name="MicrosoftAjax.js"/>
/// <reference path="IPurchasedAppData.js"/>
/// <reference path="InteractiveBaseView.js" />
/// <reference path="IController.js" />
/// <reference path="~/Services/Commerce/Commerce.svc" />
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  PurchasedAppData.js
*
*      Processes the Purchase data, abstracting it from the UI.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.PurchasedAppData = function() {
    /// <summary>
    ///     Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <returns>
    ///     A newly created PurchasedAppData object.
    /// </returns>
    UserPCSite.PurchasedAppData.initializeBase(this);
}

UserPCSite.PurchasedAppData.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///     
        ///     Initializes the object.
        /// </summary>
        UserPCSite.PurchasedAppData.callBaseMethod(this, 'initialize');
    },
    getLength: function() {
        /// <summary>
        ///     Gets the number of creditcards currently acquired.
        /// </summary>
        /// <returns>Count of items acquired.</returns>
        var length = 0;
        if (this._json != null) {
            length = this._json.length;
        }
        return length;
    },
    findSku: function(sku) {
        /// <summary>
        ///     Find an appsku in the purchased apps list
        /// </summary>
        /// <param name="sku">AppSku of the referenced Purchase</param>
        /// <returns>Boolean true if found</returns>

        if (this._json != null) {
            return (this._json[sku] == sku);
        }
        return false;
    },
    addSku: function(sku) {
        /// <summary>
        ///     Add an appsku into the purchased apps list
        /// </summary>
        /// <param name="sku">AppSku of the referenced Purchase</param>

        if (!this.findSku(sku)) {
            this._json[sku] = sku;
        }
    },
    dispose: function() {
        /// <summary>
        ///     Dispose of any local resources.
        /// </summary>
        UserPCSite.PurchasedAppData.callBaseMethod(this, 'dispose');
    }
}

UserPCSite.PurchasedAppData.prototype.get_json = function() { return this._json; };
UserPCSite.PurchasedAppData.prototype.set_json = function(json) { this._json = json; }
/// <field name="json">The source data element to use.</field>

UserPCSite.PurchasedAppData.registerClass('UserPCSite.PurchasedAppData', Sys.Component);


/*********************************************************************************************
*
*  PurchasedAppController.js
*
*       This class is used to drive the purchase logic and commerce API integration.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.PurchasedAppController = function() {
    /// <summary>
    ///     PurchasedAppController Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <returns>
    ///     A newly created PurchasedAppController.
    /// </returns>
    UserPCSite.PurchasedAppController.initializeBase(this);
}

UserPCSite.PurchasedAppController.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///     
        ///     Initializes the controller.
        /// </summary>

        UserPCSite.PurchasedAppController.callBaseMethod(this, 'initialize');

        //List of Thumbnail panel for notification
        this._handlerList = new Array();

        //Number of tries to Service call
        this._tries = 0;

        //List of thumbnails for notification
        this._thumbnailList = new Array();

        //Change state to Waiting to connect to service
        this._changeState(UserPCSite.PurchasedAppController.controllerStates.waiting);
    },
    dispose: function() {
        /// <summary>
        ///     Dispose any local resources.
        /// </summary>
        UserPCSite.PurchasedAppController.callBaseMethod(this, 'dispose');
    },
    initializeRequest: function() {
        /* defined for interface */
    },
    getPurchasedAppsArray: function() {
        /// <summary>
        ///     Get the array of Purchased apps, from the cookie
        /// </summary>

        var cookieValue = SiteCommon.Utils.readCookie(
                    UserPCSite.PurchasedAppController.cookieName);

        var dataArray = new Array();
        if (!SiteCommon.Utils.isNullOrUndefined(cookieValue)) {
            dataArray = cookieValue.split(',');
        }
        return dataArray;
    },
    _updateDataFromCookie: function() {
        /// <summary>
        ///     Update data object with app list in cookie
        /// </summary>
        /// <returns>
        ///     True if cookie contains a list of apps
        /// </returns>

        //Get list from cookie
        var dataArray = this.getPurchasedAppsArray();

        //Convert to associative array
        var data = new Array();
        var i = 0;
        for (i = 0; i < dataArray.length; i++) {
            data[dataArray[i]] = dataArray[i];
        }

        //Set data object if it exists
        if (!SiteCommon.Utils.isNullOrUndefined(this._data)) {
            this._data._json = data;
        }
        else {
            //create new data object
            this._data = $create(UserPCSite.PurchasedAppData,
                                 { 'json': data
                                 },
                                 {},
                                 {},
                                 null);
        }

        //Return if read from cookie was successful
        return (!SiteCommon.Utils.isNullOrUndefined(
                    SiteCommon.Utils.readCookie(
                        UserPCSite.PurchasedAppController.cookieName)));
    },
    addPurchasedApp: function(appSku) {
        /// <summary>
        ///     Add a new appSku to the purchased apps list
        /// </summary>
        /// <param name="sku">AppSku of the referenced Purchase</param>

        var dataArray = this.getPurchasedAppsArray();
        dataArray[dataArray.length] = appSku;
        SiteCommon.Utils.createCookie(UserPCSite.PurchasedAppController.cookieName, dataArray, false);

        this._data.addSku(appSku);
        this._notifyThumbnail(appSku);
    },
    makeRequest: function() {
        /// <summary>
        ///     Issue the actual ajax request IF NEEDED
        ///     Read from cookie if available before making ajax request
        /// </summary>

        //Change state to BUSY
        this._changeState(UserPCSite.PurchasedAppController.controllerStates.busy);

        //if reading from cookie was successful, we are ready
        if (this._updateDataFromCookie()) {
            this._changeState(UserPCSite.PurchasedAppController.controllerStates.ready);
        }
        else {
            // Get a reference to the device service, and issue a data call 
            // to get list of products already purchased by the user
            var service = new UserPCSite.DeviceService();
            service.GetUserProducts(
                                  this.successfulResponse,
                                  this.failureResponse,
                                  this);
            this._tries++;
        }
    },
    _notifyListener: function(method, context) {
        /// <summary>
        ///     Notify a listener method
        /// </summary>
        /// <param name="method">method to invoke</param>
        /// <param name="context">Context of the method</param>
        method(context);
    },
    _notifyThumbnail: function(appSku) {
        /// <summary>
        ///     Notify the thumbnails which have been registered for the given appSku
        /// </summary>
        /// <param name="sku">AppSku of the referenced Purchase</param>


        if (!SiteCommon.Utils.isNullOrUndefined(this._thumbnailList[appSku])) {
            var thumbarray = this._thumbnailList[appSku];
            for (i = 0; i < thumbarray.length; i++) {
                this._notifyListener(thumbarray[i].method, thumbarray[i].context);
            }
        }
    },
    _notifyListeners: function(notifyAll) {
        /// <summary>
        ///     Notify the list of listener object
        /// </summary>
        /// <param name="notifyAll">Notify all listeners or only ones that haven't been notified</param>
        var i = 0;
        for (i = 0; i < this._handlerList.length; i++) {
            var item = this._handlerList[i];

            if ((!SiteCommon.Utils.isNullOrUndefined(notifyAll)
                 && notifyAll == true)
                || item.notified == false) {

                item.notified = true;
                this._notifyListener(item.method, item.context);
            }
        }
    },
    _changeState: function(newState) {
        /// <summary>
        ///     Change state and take action accordingly
        /// </summary>
        /// <param name="new state">New state to change to</param>

        var oldState = this._state;
        this._state = newState;

        switch (newState) {
            case UserPCSite.PurchasedAppController.controllerStates.waiting:
                //kick off the request to get data
                this.makeRequest();
                return;

            case UserPCSite.PurchasedAppController.controllerStates.ready:
                //notify the listeners, we are ready with data
                this._notifyListeners(false);
                return;

            case UserPCSite.PurchasedAppController.controllerStates.busy:
                //Do nothing special here
                return;

            case UserPCSite.PurchasedAppController.controllerStates.failed:
                if (this._tries < 3) {
                    //retry upto 3 times
                    this.makeRequest();
                }
                else {
                    //notify the listeners anyway to
                    //unblock them even if we dont have data
                    this._state = UserPCSite.PurchasedAppController.controllerStates.ready;
                    this._notifyListeners(false);
                }
                return;

            default:
                //Invalid case, do nothing
                return;
        }
    },
    registerThumbnailForNotifications: function(appSku, method, context) {
        /// <summary>
        ///     Register a thumbnail for notification
        /// </summary>
        /// <param name="sku">AppSku of the referenced Purchase</param>
        /// <param name="method">method to invoke</param>
        /// <param name="context">Context of the method</param>

        var thumbArray = this._thumbnailList[appSku];
        if (SiteCommon.Utils.isNullOrUndefined(this._thumbnailList[appSku])) {
            thumbArray = new Array();
            this._thumbnailList[appSku] = thumbArray;
        }
        thumbArray[thumbArray.length] = {
            'method': method,
            'context': context,
            'appSku': appSku
        };

    },
    registerForNotifications: function(method, context) {
        /// <summary>
        ///     Register a control for notifications
        /// </summary>
        /// <param name="method">method to invoke</param>
        /// <param name="context">Context of the method</param>

        var notified = false;
        switch (this._state) {
            case UserPCSite.PurchasedAppController.controllerStates.ready:
                //notify the object, if we are ready with data
                this._notifyListener(method, context);
                notified = true;
                break;

            default:
                //nothing special to do here
                break;
        }

        this._handlerList[this._handlerList.length] = {
            'method': method,
            'context': context,
            'notified': notified
        };

    },
    successfulResponse: function(response, context) {
        /// <summary>
        ///     A successful server call has happened.  
        ///     Save the data to the cookie and update state
        /// </summary>
        /// <param name="response">The json response of the user query.</param>
        /// <param name="context">A reference to the PurchasedAppController object.</param>

        //Save to cookie
        SiteCommon.Utils.createCookie(UserPCSite.PurchasedAppController.cookieName, response, false);

        //Read from cookie and update the data object
        context._updateDataFromCookie();

        //Set state to READY
        context._changeState(UserPCSite.PurchasedAppController.controllerStates.ready);

    },
    failureResponse: function(response, context) {
        /// <summary>
        ///     A failed server call has happened, change state to FAILED.
        /// </summary>
        /// <param name="response">The json response of the user query.</param>
        /// <param name="context">A reference to the PurchasedAppController object.</param>

        this._changeState(UserPCSite.PurchasedAppController.controllerStates.failed);
    },
    isPurchased: function(appSku) {
        /// <summary>
        ///     Check if a given appSKU is present in our purchased apps list
        /// </summary>
        /// <param name="sku">AppSku to verify</param>

        if (SiteCommon.Utils.isNullOrUndefined(this._data)
        || this._state != UserPCSite.PurchasedAppController.controllerStates.ready) {
            //if we have no data or not READY
            return false;
        }
        else {
            return this._data.findSku(appSku);
        }
    }
}

UserPCSite.PurchasedAppController.get_instance = function() { return UserPCSite.PurchasedAppController._instance; }
/// <field name="instance">Get the current Purchase instance, as this is a singleton class.</field>

UserPCSite.PurchasedAppController.prototype._state = null;
/// <field name="state">State of the purchased apps controler.</field>

UserPCSite.PurchasedAppController.controllerStates = { 'waiting': 'waiting', 'ready': 'ready', 'busy': 'busy', 'failed': 'failed' };
/// <field name="purchaseState">A static list of possible purchase view states.</field>

UserPCSite.PurchasedAppController.cookieName = 'purchasedAppsData';
/// <field name="cookieName">Name of the cookie used to store the purchased apps list</field>

UserPCSite.PurchasedAppController.prototype.get_data = function() { return this._data; };
/// <field name="data">Get the data element.</field>

UserPCSite.PurchasedAppController.registerClass('UserPCSite.PurchasedAppController', Sys.Component, SiteCommon.IController);

// Create the PurchasedAppController singleton.
UserPCSite.PurchasedAppController._instance = $create(UserPCSite.PurchasedAppController, {}, {}, {}, null);

/// <reference name="MicrosoftAjax.js"/>
/// <reference path="~/AjaxControls/RatingReviewController.js"/>
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  RatingReviewData
*
*      Processes the ratings and reviews data, abstracting it from the UI.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.RatingReviewData = function() {
    UserPCSite.RatingReviewData.initializeBase(this);
}

UserPCSite.RatingReviewData.prototype = {
    initialize: function() {
        /// <summary>
        ///     Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
        /// </summary>
        /// <returns>
        ///     A newly created data object.
        /// </returns>
        UserPCSite.RatingReviewData.callBaseMethod(this, 'initialize');
    },
    _getItemList: function() {
        /// <summary>
        ///     Gets the desired item list
        /// </summary>
        /// <param name="reportStatus">Type of list desired</param>  
        /// <returns>A reference to the items list.</returns>
        if (!SiteCommon.Utils.isNullOrUndefined(this._json)) {
            var itemList = this._json.ItemList;
        }
        return itemList;
    },
    getLength: function() {
        /// <summary>
        ///     Gets the number of items 
        /// </summary>
        /// <param name="reportStatus">Type of list desired</param>
        /// <returns>Count of items in the list.</returns>
        var length = 0;
        if (!SiteCommon.Utils.isNullOrUndefined(this._getItemList())) {
            length = this._getItemList().NumItems;
        }
        return length;
    },
    getRating: function(index) {
        /// <summary>
        ///     Get the rating.
        /// </summary>
        /// <param name="index">index in the list</param>
        /// <returns>Rating value of the review.</returns>
        return Math.round(this._getItem(index).Rating / 100);
    },
    getId: function(index) {
        /// <summary>
        ///     Get the Id.
        /// </summary>
        /// <param name="index">index in the list</param>
        /// <returns>Id value of the review.</returns>
        return this._getItem(index).Id;
    },
    getReviewer: function(index) {
        /// <summary>
        ///     Get the Reviewer name.
        /// </summary>
        /// <param name="index">index in the list</param>
        /// <returns>Reviewer name value of the review.</returns>
        return this._getItem(index).Reviewer;
    },
    getTitle: function(index) {
        /// <summary>
        ///     Get the Title.
        /// </summary>
        /// <param name="index">index in the list</param>
        /// <returns>Title value of the review.</returns>
        return this._getItem(index).Title;
    },
    getText: function(index) {
        /// <summary>
        ///     Get the Text.
        /// </summary>
        /// <param name="index">index in the list</param>
        /// <returns>Text value of the review.</returns>
        return this._getItem(index).Text;
    },
    getDate: function(index) {
        /// <summary>
        ///     Get the DateAdded.
        /// </summary>
        /// <param name="index">index in the list</param>
        /// <returns>DateAdded value of the review.</returns>
        return this._getItem(index).DateAdded;
    },
    _getItem: function(index) {
        /// <summary>
        ///     Get the item referenced by index
        /// </summary>
        /// <param name="reportStatus">Type of list desired</param>  
        /// <param name="index">Index of the referenced Product item</param>  
        /// <returns>Reports Item at the specified index.</returns>
        var value = null;
        try {
            value = this._getItemList().List[index];
        }
        catch (exception) {
            throw ("Index is Out of Range.");
        }
        return value;
    },
    dispose: function() {
        /// <summary>
        ///     Dispose of any local resources.
        /// </summary>
        UserPCSite.RatingReviewData.callBaseMethod(this, 'dispose');
    }
}
UserPCSite.RatingReviewData.prototype.get_json = function() { return this._json; };
UserPCSite.RatingReviewData.prototype.set_json = function(json) { this._json = json; }
/// <field name="json">The source data element to use.</field>

UserPCSite.RatingReviewData.registerClass('UserPCSite.RatingReviewData', Sys.Component);



/// <reference name="MicrosoftAjax.js"/>
/// <reference path="~/Common/AjaxControls/InteractiveBaseControl.js" />
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  RatingReviewView
*
*      Displays the list of Ratings and Reviews.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.RatingReviewView = function(element) {
    /// <summary>
    ///     CategoryListView Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <param name="element">HTML DomElement to associate.</param>
    /// <returns>
    ///     A newly created RatingReviewView object.
    /// </returns>
    UserPCSite.RatingReviewView.initializeBase(this, [element]);
}

UserPCSite.RatingReviewView.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///     
        ///     Initializes the view.
        /// </summary>

        this._listItems = new Array();
        this.add_click(this._onClick);

        this._Loc = SiteCommon.Utils.JsonParser(this.get_controller().locDict);

        // Do local initialization (needed for_setupRender before calling base class, as it will call _setupRender).
        UserPCSite.RatingReviewView.callBaseMethod(this, 'initialize');
    },
    _createResultsText: function(id) {
        /// <summary>
        ///     Creates the Results text div element.
        /// </summary>
        /// <param name="id">Id to assign to the results text div.</param>
        /// <returns>
        ///     A newly created Results text div element.
        /// </returns>

        //Panel to hold the result string
        var divResultText = document.createElement('div');
        divResultText.setAttribute('id', id);
        Sys.UI.DomElement.addCssClass(divResultText, 'RatingReviewViewResults');
        Sys.UI.DomElement.setVisibilityMode(divResultText, Sys.UI.VisibilityMode.hide);

        // Create a div for the Status Panel header
        this._divHeaderBar = SiteCommon.Utils.CreateDiv(this.get_element(), this.get_id() + '_Header', 'RatingReviewViewHeader', null);
        Sys.UI.DomElement.setVisibilityMode(this._divHeaderBar, Sys.UI.VisibilityMode.collapse);

        //Add column headings
        SiteCommon.Utils.CreateDiv(this._divHeaderBar, this._divHeaderBar.getAttribute('id') + '_product',
                        'RatingReviewViewHeading RatingReviewViewNameWidth', SiteCommon.Utils.toUIString(this._Loc.Rating));
        SiteCommon.Utils.CreateDiv(this._divHeaderBar, this._divHeaderBar.getAttribute('id') + '_date',
                        'RatingReviewViewHeading RatingReviewViewDateWidth', SiteCommon.Utils.toUIString(this._Loc.Date));

        return divResultText;
    },
    _setupRender: function() {
        /// <summary>
        ///     Sets up the panel dom element.
        /// </summary>

        // Set the main CSS style for the element    
        Sys.UI.DomElement.addCssClass(this.get_element(), 'RatingReviewBox');

        // Create a div for the Results Text
        this._divResultText = this._createResultsText(this.get_id() + '_ResultText');
        this._divHeaderBar.appendChild(this._divResultText);

        // Create an array to hold the list items
        this._listItems = new Array();

        // Create a div for status information (loading, failed, no results, etc)
        this._divStatusInfo = document.createElement('div');
        Sys.UI.DomElement.setVisibilityMode(this._divStatusInfo, Sys.UI.VisibilityMode.collapse);
        this.get_element().appendChild(this._divStatusInfo);
        this.displayLoading();

    },
    dataRender: function() {
        /// <summary>
        ///     Update the UI with main data components from controller.
        /// </summary>

        // Hide the status UI
        Sys.UI.DomElement.setVisible(this._divStatusInfo, false);

        var data = this.get_controller().get_data();

        if (data.getLength() == 0) {

            // add the empty list message
            this._emptyDiv = document.createElement('div');
            Sys.UI.DomElement.addCssClass(this._emptyDiv, 'RatingReviewItemBox');

            var noitemsString = this._Loc.NoReviews;
            if (this.get_controller()._page > 1) {
                noitemsString = this._Loc.NoMoreReviews;
            }
            SiteCommon.Utils.CreateDiv(this._emptyDiv, this.get_id() + '_NoHistory',
                        'RatingReviewEmpty', SiteCommon.Utils.toUIString(noitemsString));

            this.get_element().appendChild(this._emptyDiv);
        }

        //Show/Hide header string
        Sys.UI.DomElement.setVisible(this._divResultText, (data.getLength() != 0));
    },
    resetRender: function() {
        /// <summary>
        ///     Reset the UI to initial state.
        /// </summary>

        if (this._emptyDiv != null) {
            this.get_element().removeChild(this._emptyDiv);
            this._emptyDiv = null;
        }

        // Clear the list of items
        for (var i = 0; i < this._listItems.length; i++) {
            this.get_element().removeChild(this._listItems[i]);
        }
        delete this._listItems;
        this._listItems = new Array();
    },
    displayLoading: function() {
        /// <summary>
        ///     Displays loading message.
        /// </summary>

        this._divStatusInfo.innerHTML = "";
        var div = SiteCommon.Utils.createLoadingDiv(this._Loc.Loading);
        this._divStatusInfo.appendChild(div);
        Sys.UI.DomElement.setVisible(this._divStatusInfo, true);
    },
    displayFailed: function() {
        /// <summary>
        ///     Displays failure message.
        /// </summary>
        this._divStatusInfo.innerHTML = "";
        var reloadObj = SiteCommon.Utils.createReloadDiv(this.get_id(), this._Loc.Refresh, this._Loc.ServerBusy);
        this.addEventTarget(SiteCommon.InteractiveBaseView.eventTypes.click,
                                reloadObj.reloadButton.getAttribute('id'),
                                { 'isReload': true });
        this._divStatusInfo.appendChild(reloadObj.div);
        Sys.UI.DomElement.setVisible(this._divStatusInfo, true);
    },
    addRow: function(dataContext) {
        /// <summary>
        ///     Add a product to the navigation list.
        /// </summary>
        /// <param name="dataContext">Data context to refer to the data elements.</param>

        var index = this._listItems.length;

        // Create the list item
        var div = this._createRowItem(this.get_id(), dataContext);
        this._listItems[index] = div;
        this.get_element().appendChild(div);
    },
    dispose: function() {
        /// <summary>
        ///     Called to dispose of this objects resources.
        /// </summary>
        UserPCSite.RatingReviewView.callBaseMethod(this, 'dispose');
    },
    _createRowItem: function(id, dataContext) {
        /// <summary>
        ///     Create a list item and returns the wrapping Div DOM element.
        /// </summary>
        /// <param name="id">DOM ID to prefix to the resulting elements' IDs.</param>
        /// <param name="dataContext">Data context to refer to the data elements.</param>
        /// <returns>
        ///     A reference the wrapping DOM element.
        /// </returns>

        var data = this.get_controller().get_data();

        // Create the item wrapper.
        var div = document.createElement('div');
        div.setAttribute('id', id + '_' + dataContext);
        Sys.UI.DomElement.addCssClass(div, 'RatingReviewItemBox');


        var stars = SiteCommon.Utils.CreateRatings(div, id + '_' + dataContext + '_stars',
                        'RatingReviewViewStars', data.getRating(dataContext));

        var title = SiteCommon.Utils.CreateDiv(div, id + '_' + dataContext + '_title',
                                'RatingReviewViewTitle', SiteCommon.Utils.toUIString(data.getTitle(dataContext)));

        var name = SiteCommon.Utils.CreateDiv(div, id + '_' + dataContext + '_name',
                                'RatingReviewViewName', SiteCommon.Utils.toUIString(
                                    data.getReviewer(dataContext)
                                    + ' | ' 
                                    + (new Date(data.getDate(dataContext))).localeFormat(
                                        this._Loc.UserMarketDateFormat)));
                                
        var text = SiteCommon.Utils.CreateDiv(div, id + '_' + dataContext + '_text',
                                'RatingReviewViewText', SiteCommon.Utils.toUIString(data.getText(dataContext)));

        return div;
    },
    _onClick: function(sender, args) {
        /// <summary>
        ///     Click event handler for the list items.
        /// </summary>
        /// <param name="sender">RatingReviewView object that is clicked on.</param>
        /// <param name="args">Event args.</param>
        if (args.customData != null) {
            if (args.customData.eventType == 'pagination') {
                sender.get_controller().handlePagination(args.customData.page);
            }
            else if (args.customData.isReload) {
                sender.get_controller().reloadClicked();
            }
        }
    },
    drawPagination: function(pageStart, pageEnd, pageSelected) {
        var parent = this.get_element();
        var id = parent.getAttribute('id') + '_pagination';

        //Remove old pagination div from the view
        if (this._paginationDiv != null) {
            parent.removeChild(this._paginationDiv);
            delete this._paginationDiv;
        }

        this._paginationDiv = SiteCommon.Utils.CreateDiv(parent, id, 'RatingReview_PaginationContainer', null);

        var prevCss = 'sprite PaginationPreviousIcon FloatLeft';
        if (1 < pageSelected)
            prevCss += ' CursorHand PaginationPreviousIcon-active';
        var prevLink = SiteCommon.Utils.CreateLink(this._paginationDiv, id + '_previous', prevCss, null, null, null);
        if (1 < pageSelected) {
            this.addEventTarget(SiteCommon.InteractiveBaseView.eventTypes.click,
                                id + '_previous',
                                { 'eventType': 'pagination',
                                    'page': (pageSelected - 1)
                                });
        }
        else {
            // disable the link to remove it from the tab order (accessibility). Left as <a> rather than <div> for DOM consistency.
            prevLink.setAttribute('disabled', 'true'); 
        }

        for (var i = pageStart; i <= pageEnd; i++) {
            if (pageSelected == i) { // current page (no link)
                SiteCommon.Utils.CreateDiv(this._paginationDiv, id + i,
                        'PaginationEntry PaginationEntrySelected FloatLeft',
                        SiteCommon.Utils.toUIString(i));
            }
            else { 
                SiteCommon.Utils.CreateLink(this._paginationDiv, id + i,
                        'PaginationEntry CursorHand FloatLeft',
                        SiteCommon.Utils.toUIString(i), null, null);
                this.addEventTarget(SiteCommon.InteractiveBaseView.eventTypes.click,
                                id + i,
                                { 'eventType': 'pagination',
                                    'page': i
                                });
            }
        }

        var nextCss = 'sprite PaginationNextIcon FloatLeft';
        if (pageSelected < pageEnd)
            nextCss += ' CursorHand PaginationNextIcon-active';
        var nextLink = SiteCommon.Utils.CreateLink(this._paginationDiv, id + '_next', nextCss, null, null, null);
        if (pageSelected < pageEnd) {
            this.addEventTarget(SiteCommon.InteractiveBaseView.eventTypes.click,
                                id + '_next',
                                { 'eventType': 'pagination',
                                    'page': (pageSelected + 1)
                                });
        }
        else {
            // disable the link to remove it from the tab order (accessibility). Left as <a> rather than <div> for DOM consistency.
            nextLink.setAttribute('disabled', 'true'); 
        }

        //Add clear:both div
        SiteCommon.Utils.CreateDiv(this._paginationDiv, id + '_Clear', 'clear', null);

        return this._paginationDiv;
    }
}

UserPCSite.RatingReviewView.registerClass('UserPCSite.RatingReviewView', SiteCommon.InteractiveBaseView);





/// <reference name="MicrosoftAjax.js"/>
/// <reference path="~/Common/AjaxControls/IController.js"/>
/// <reference path="~/AjaxControls/RatingReviewData.js"/>
/// <reference path="~/AjaxControls/RatingReviewView.js"/>
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  RatingReviewController
*
*       This class is used to drive the ratings and reviews list.  It sets up
*       the data class, and initializes the UI classes.
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.RatingReviewController = function(element) {
    /// <summary>
    ///     RatingReviewController Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <param name="element">HTML DomElement to associate.</param>
    /// <returns>
    ///     A newly created RatingReviewController.
    /// </returns>
    UserPCSite.RatingReviewController.initializeBase(this, [element]);
}

UserPCSite.RatingReviewController.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///     
        ///     Initializes the controller.
        /// </summary>
        UserPCSite.RatingReviewController.callBaseMethod(this, 'initialize');
    },
    initializeRequest: function() {
        /// <summary>
        ///     initializeRequest is called to initialize the ajax data request needed by
        ///     this controller, as well as any additional requirements.
        /// </summary>

        // Create a div to contain the view
        var div = document.createElement('div');
        div.setAttribute('id', this.get_id() + '_view');
        this.get_element().appendChild(div);
        // Create the panel view element
        this._view = $create(UserPCSite.RatingReviewView,
                                 { 'controller': this
                                 },
                                 {},
                                 {},
                                 div);

        this._maxPage = 1;
        this._page = 0;

        this.handlePagination(1);
    },
    makeRequest: function() {
        /// <summary>
        ///     Issue the actual ajax request.
        /// </summary>
        this._view.displayLoading();

        // Get a reference to the catalog service, and issue a data call
        var service = new UserPCSite.Catalog();

        service.GetAppReviews(
                                this.deviceId, // deviceId
                                this.appSKU, //app sku
                                this.getStart(this._page), // Offset
                                this.maxPageLength, // Chunk size
                                this.successfulResponse,
                                this.failureResponse,
                                this);
    },
    successfulResponse: function(response, context) {
        /// <summary>
        ///     Called when a data query has successfully completed.
        /// </summary>
        /// <param name="response">A JSON encoded object representing the data acquired.</param>
        /// <param name="context">A reference to the controller that issued the request</param>

        // Create the data object with the response object
        context._data = $create(UserPCSite.RatingReviewData,
                                 { 'json': response
                                 },
                                 {},
                                 {},
                                 null);

        context.handlePagination(context._page);
    },
    failureResponse: function(response, context) {
        /// <summary>
        ///     Called when a data query has failed.
        /// </summary>
        /// <param name="response">A string representing the failure information.</param>
        /// <param name="context">A reference to the controller that issued the request</param>
        context._view.displayFailed();
    },
    displayData: function(page) {
        /// <summary>
        ///     Display the product list for the specified page number
        /// </summary>
        /// <param name="page">Page number to display.</param>
        this._view.resetRender(page);
        this._view.dataRender();
        if (this._data.getLength() > 0) {
            // Add items to the view for each data element.
            for (var i = 0;
                 (i < this._data.getLength());
                 i++) {

                this._view.addRow(i);
            }
        }
    },
    handlePagination: function(page) {
        /// <summary>
        ///     Handle the pagination of the product list for the specified page number
        /// </summary>
        /// <param name="page">Page number to display.</param>

        var numPages = this._maxPage;
        if (1 <= page && page <= numPages) {
            if (page != this._page) {
                this._page = page;
                this.makeRequest();
            }
            else {
                //increment maxPage limit if next page could be available
                if (page == this._maxPage
                    && !SiteCommon.Utils.isNullOrUndefined(this._data)
                    && this._data.getLength() == this.maxPageLength) {
                    this._maxPage++;
                    numPages = this._maxPage;
                }

                this.displayData(page);

                //sliding window implementation
                var start = 1; var end = numPages;
                if (page + this.slidingWindowLength < end) {
                    end = page + this.slidingWindowLength;
                }
                if (end - this.slidingWindowLength > start) {
                    start = end - this.slidingWindowLength;
                }
                //forget the max page extent
                this._maxPage = end;
                
                this._view.drawPagination(start, end, page);
            }
        }
    },
    getStart: function(page) {
        /// <summary>
        ///     Gets the Start index of items currently acquired.
        /// </summary>
        /// <returns>Start index of items acquired. -1 in case of error</returns>
        if (page == null
             && this._page != null) {
            page = this._page;
        }
        else if (this._page == null) {
            return -1;
        }
        return (page - 1) * this.maxPageLength;
    },
    reloadClicked: function() {
        /// <summary>
        ///     Called when the view should be reloaded.
        /// </summary>

        // Reset the view to an initial state.
        this._view.resetRender();

        // Issue the actual request.
        this.makeRequest();
    },
    dispose: function() {
        /// <summary>
        ///     Dispose any local resources.
        /// </summary>
        UserPCSite.RatingReviewController.callBaseMethod(this, 'dispose');
    }
}

UserPCSite.RatingReviewController.prototype.locDict = null;
/// <field name="locDict">The dictionary to receive localized strings.</field>

UserPCSite.RatingReviewController.prototype.get_data = function() { return this._data; };
/// <field name="data">Get the data element.</field>

UserPCSite.RatingReviewController.prototype.maxPageLength = 5;
/// <field name="maxPageLength">Number of data elements to show on each page.</field>
UserPCSite.RatingReviewController.prototype.slidingWindowLength = 5;
/// <field name="slidingWindowLength">Number of entries to show in the pagination control.</field>

UserPCSite.RatingReviewController.prototype.appSKU = null;
/// <field name="appSku">Number of data elements to show on each page.</field>

UserPCSite.RatingReviewController.prototype.deviceId = null;
/// <field name="deviceID">The deviceId to query applications.</field>

UserPCSite.RatingReviewController.registerClass('UserPCSite.RatingReviewController', Sys.UI.Control, SiteCommon.IController);

/// <reference name="MicrosoftAjax.js"/>
/// <reference path="~/AjaxControls/IThumbnailData.js"/>
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//


/*********************************************************************************************
*
*  ScreenshotsSliderView.js
*
*      Displays a screenshots slider
*
*  Copyright Microsoft Corporation, All Rights reserved.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.ScreenshotsSliderView = function(element) {
    /// <summary>
    ///     ScreenshotsSliderView Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <param name="element">HTML DomElement to associate.</param>
    /// <returns>
    ///     A newly created ScreenshotsSliderView object.
    /// </returns>
    UserPCSite.ScreenshotsSliderView.initializeBase(this, [element]);
}

UserPCSite.ScreenshotsSliderView.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///     
        ///     Initializes the view.
        /// </summary>

        this._Screenshots = SiteCommon.Utils.JsonParser(this.screenshots);

        this.add_click(this._onClick);

        UserPCSite.ScreenshotsSliderView.callBaseMethod(this, 'initialize');

        this.dataRender();
    },
    _setupRender: function() {
        /// <summary>
        ///     Sets up the panel dom element.
        /// </summary>

        // Create a div for the screenshots
        this._divScreenShots = document.createElement('div');
        Sys.UI.DomElement.addCssClass(this._divScreenShots, 'AppDetailsView_screenShots');
        Sys.UI.DomElement.setVisibilityMode(this._divScreenShots, Sys.UI.VisibilityMode.collapse);
        Sys.UI.DomElement.setVisible(this._divScreenShots, false);
        this.get_element().appendChild(this._divScreenShots);
    },
    drawPagination: function(parent, pageStart, pageEnd, pageSelected) {
        ///<summary>
        ///     Draws the pagination controls like:
        ///     Previous, 1, 2, 3, 4, ... N, Next
        /// </summary>
        var id = 'AppDetailsView_pagination';

        //Remove old pagination div from the view
        if (this._paginationDiv != null) {
            parent.removeChild(this._paginationDiv);
            delete this._paginationDiv;
        }

        this._paginationDiv = SiteCommon.Utils.CreateDiv(parent, id, 'AppDetailsView_PaginationContainer', null);

        var prevCss = 'sprite PaginationPreviousIcon FloatLeft';
        if (pageStart < pageSelected)
            prevCss += ' CursorHand PaginationPreviousIcon-active';
        var prevLink = SiteCommon.Utils.CreateLink(this._paginationDiv, id + '_previous', prevCss, null, null, null);
        if (pageStart < pageSelected) {
            this.addEventTarget(SiteCommon.InteractiveBaseView.eventTypes.click,
                                id + '_previous',
                                { 'eventType': 'pagination',
                                    'page': (pageSelected - 1)
                                });
        }
        else {
            // disable the link to remove it from the tab order (accessibility). Left as <a> rather than <div> for DOM consistency.
            prevLink.setAttribute('disabled', 'true'); 
        }

        for (var i = pageStart; i <= pageEnd; i++) {
            if (pageSelected == i) { // current page (no link)
                SiteCommon.Utils.CreateDiv(this._paginationDiv, id + i,
                        'PaginationEntry PaginationEntrySelected FloatLeft',
                        SiteCommon.Utils.toUIString(i));
            }
            else { 
                SiteCommon.Utils.CreateLink(this._paginationDiv, id + i,
                        'PaginationEntry CursorHand FloatLeft',
                        SiteCommon.Utils.toUIString(i), null, null);
                this.addEventTarget(SiteCommon.InteractiveBaseView.eventTypes.click,
                                id + i,
                                { 'eventType': 'pagination',
                                    'page': i
                                });
            }
        }

        var nextCss = 'sprite PaginationNextIcon FloatLeft';
        if (pageSelected < pageEnd)
            nextCss += ' CursorHand PaginationNextIcon-active';
        var nextLink = SiteCommon.Utils.CreateLink(this._paginationDiv, id + '_next', nextCss, null, null, null);
        if (pageSelected < pageEnd) {
            this.addEventTarget(SiteCommon.InteractiveBaseView.eventTypes.click,
                                id + '_next',
                                { 'eventType': 'pagination',
                                    'page': (pageSelected + 1)
                                });
        }
        else {
            // disable the link to remove it from the tab order (accessibility). Left as <a> rather than <div> for DOM consistency.
            nextLink.setAttribute('disabled', 'true'); 
        }

        //Add clear:both div
        SiteCommon.Utils.CreateDiv(this._paginationDiv, id + '_Clear', 'clear', null);

        return this._paginationDiv;
    },
    handlePagination: function(page) {
        /// <summary>
        ///     Handle the pagination of the product list for the specified page number
        /// </summary>
        /// <param name="page">Page number to display.</param>

        var numPages = this._Screenshots.count;
        if (1 <= page && page <= numPages) {
            if (numPages > 1) {
                this.drawPagination(this.get_element(), 1, numPages, page);
            }
            this.drawScreenshot(page - 1);
        }
    },
    drawScreenshot: function(index) {
        /// <summary>
        ///     Draw the screenshot given the index of the screenshot to be displayed
        /// </summary>
        /// <param name="index">Index of Screenshot in the data list.</param>

        if (!SiteCommon.Utils.isNullOrUndefined(this._img)) {
            this._divScreenShots.removeChild(this._img);
            delete this._img;
        }

        this._img = document.createElement('img');
        Sys.UI.DomElement.addCssClass(this._img, 'AppDetailsView_screenShots_single');
        this._divScreenShots.appendChild(this._img);
        Sys.UI.DomElement.setVisible(this._divScreenShots, true);

        this._img.setAttribute('src', SiteCommon.Utils.FixUrlProtocol(this._Screenshots[index]));
        this._img.setAttribute('alt', this.appName);
        this._img.setAttribute('id', 'screenshotImg');
        this._img.width = 240;
    },
    dataRender: function() {
        /// <summary>
        ///     render the data
        /// </summary>

        // Add screenshot
        if (this._Screenshots.count > 0) {
            this.handlePagination(1);
        }
    },
    _onClick: function(sender, args) {
        /// <summary>
        ///     Click event handler for the list items.
        /// </summary>
        /// <param name="sender">Object that is clicked on.</param>
        /// <param name="args">Event args.</param>

        if (args.customData != null) {
            if (args.customData.eventType == 'pagination') {
                sender.handlePagination(args.customData.page);
            }
        }
    },
    dispose: function() {
        /// <summary>
        ///     Called to dispose of this objects resources.
        /// </summary>
        UserPCSite.ScreenshotsSliderView.callBaseMethod(this, 'dispose');
    }
}

UserPCSite.ScreenshotsSliderView.prototype.appName = null;
/// <field name="appName">The app name.</field>
UserPCSite.ScreenshotsSliderView.prototype.screenshots = null;
/// <field name="screenshots">The screenshots url list.</field>

UserPCSite.ScreenshotsSliderView.registerClass('UserPCSite.ScreenshotsSliderView', SiteCommon.InteractiveBaseView);


/// <reference name="MicrosoftAjax.js"/>
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

/*********************************************************************************************
*
*  SubmitReviewRatingSelector.js
*
*      The purpose of this class is to manage the dynamic actions of the rating selector
*      on the Submit Review form. It handles displaying the correct star icons upon 
*      mouse hover and click, updating the descriptive text label, and setting the selected
*      value in a hidden form field.
*
*********************************************************************************************/

Type.registerNamespace("UserPCSite");

UserPCSite.SubmitReviewRatingSelector = function(element) {
    /// <summary>
    ///     Constructor.  DO NOT CALL DIRECTLY, USE $CREATE.
    /// </summary>
    /// <param name="element">HTML DomElement to associate.</param>
    /// <returns>
    ///     A newly created object.
    /// </returns>
    UserPCSite.SubmitReviewRatingSelector.initializeBase(this, [element]);

    this._links = null;
    this._selectedValue = null;
    this._ratingLabel = null;

    this._clickDelegate = null;
    this._hoverDelegate = null;
    this._unhoverDelegate = null;
}

UserPCSite.SubmitReviewRatingSelector.prototype = {
    initialize: function() {
        /// <summary>
        ///     Initialize.  DO NOT CALL DIRECTLY, USE $CREATE.
        ///
        ///     Initializes the view.
        /// </summary>

        this._Loc = SiteCommon.Utils.JsonParser(this.locDict);

        UserPCSite.SubmitReviewRatingSelector.callBaseMethod(this, 'initialize');

        if (this._clickDelegate == null) {
            this._clickDelegate = Function.createDelegate(this, this._onClick);
        }
        if (this._hoverDelegate == null) {
            this._hoverDelegate = Function.createDelegate(this, this._onHover);
        }
        if (this._unhoverDelegate == null) {
            this._unhoverDelegate = Function.createDelegate(this, this._onUnhover);
        }

        this._links = this.get_element().getElementsByTagName("a");

        this._ratingLabel = Sys.UI.DomElement.getElementById("submitRatingLabel");

        for (var i=0; i<this._links.length; i++) {
            Sys.UI.DomEvent.addHandler(this._links[i], 'click', this._clickDelegate);
            Sys.UI.DomEvent.addHandler(this._links[i], 'keypress', this._clickDelegate);
            Sys.UI.DomEvent.addHandler(this._links[i], 'mouseover', this._hoverDelegate);
            Sys.UI.DomEvent.addHandler(this._links[i], 'focus', this._hoverDelegate);
            Sys.UI.DomEvent.addHandler(this._links[i], 'mouseout', this._unhoverDelegate);
            Sys.UI.DomEvent.addHandler(this._links[i], 'blur', this._unhoverDelegate);
        }
    },
    dispose: function() {
        // Remove the event handlers
        if (this._links) {
            for (var i = 0; i < this._links.length; i++) {
                Sys.UI.DomEvent.removeHandler(this._links[i], 'click', this._clickDelegate);
                Sys.UI.DomEvent.removeHandler(this._links[i], 'keypress', this._clickDelegate);

                Sys.UI.DomEvent.removeHandler(this._links[i], 'mouseover', this._hoverDelegate);
                Sys.UI.DomEvent.removeHandler(this._links[i], 'focus', this._hoverDelegate);

                Sys.UI.DomEvent.removeHandler(this._links[i], 'mouseout', this._unhoverDelegate);
                Sys.UI.DomEvent.removeHandler(this._links[i], 'blur', this._unhoverDelegate);
            }

            delete this._clickDelegate;
            delete this._hoverDelegate;
            delete this._unhoverDelegate;

            delete this._links;
            this._links = null;
        }

        UserPCSite.SubmitReviewRatingSelector.callBaseMethod(this, 'dispose');
    },
    _onClick: function(eventElement) {
        /// <summary>
        /// Handles click event (and keypress, for keyboard) for each star.
        /// </summary>
        /// <param name="eventElement">Event args.</param>

        // if this is keypress event, only handle the return key
        if(eventElement.type == "keypress" && eventElement.charCode != 13)
        {
            return true;
        }

        var inputElements = this.get_element().getElementsByTagName("input");
        Sys.Debug.assert(inputElements.length == 1);

        // if this is the first selectedValue, then the Submit button is disabled and we should enable it.
        if (!this._selectedValue) {
            var SubmitBtnBlock = Sys.UI.DomElement.getElementById("SubmitBtnBlock");

            Sys.UI.DomElement.removeCssClass(SubmitBtnBlock, "WhiteButtonDisabled");
            Sys.UI.DomElement.addCssClass(SubmitBtnBlock, "WhiteButton CursorHand");

            var inputElems = SubmitBtnBlock.getElementsByTagName("input");
            Sys.Debug.assert(inputElems.length == 1);
            inputElems[0].disabled = false;
        }

        this._selectedValue = this.getStarIndex(eventElement.target);

        inputElements[0].value = ""+this._selectedValue;

        // now highlight all the stars as appropriate
        for (var i = 0; i < this._links.length; i++) {
            if (i < this._selectedValue) {
                this._links[i].className = "sprite SubmitReviewStar";
            }
            else {
                this._links[i].className = "sprite SubmitReviewStarInactive";
            }
        }

        this._ratingLabel.innerHTML = "";
        this._ratingLabel.appendChild(SiteCommon.Utils.toUIString(this._Loc["Rating"+this._selectedValue]));

        return false;
    },
    _onHover: function(eventElement) {
        /// <summary>
        /// Handles hover event (and focus, for keyboard) for each star.    
        /// </summary>
        /// <param name="eventElement">Event args.</param>

        var idx = this.getStarIndex(eventElement.target);

        // highlight all stars less than and equal to the current star that is hovered on.
        for (var i = 0; i < this._links.length; i++) {
            if (i < idx) {
                this._links[i].className = "sprite SubmitReviewStar";
            }
            else {
                this._links[i].className = "sprite SubmitReviewStarEmpty";
            }
        }

        this._ratingLabel.innerHTML = "";
        this._ratingLabel.appendChild(SiteCommon.Utils.toUIString(this._Loc["Rating"+idx]));

        return false;
    },
    _onUnhover: function(eventElement) {
        /// <summary>
        /// Handles unhover event (and blur, for keyboard) for each star.      
        /// </summary>
        /// <param name="eventElement">Event args.</param>

        // if there is no selected value yet, clear all the stars (empty state). Else, set the stars to the selection value.
        for (var i = 0; i < this._links.length; i++) {
            if (this._selectedValue) {
                // highlight all stars less than and equal to the selected value
                if (i < this._selectedValue) {
                    this._links[i].className = "sprite SubmitReviewStar";
                }
                else {
                    this._links[i].className = "sprite SubmitReviewStarInactive";
                }
            }
            else {
                this._links[i].className = "sprite SubmitReviewStarEmpty";
            }
        }

        this._ratingLabel.innerHTML = "&nbsp;"; // &nbsp; because setting empty innerHTML causes layout jumpiness in IE (7+8).
        if (this._selectedValue) {
            this._ratingLabel.appendChild(SiteCommon.Utils.toUIString(this._Loc["Rating" + this._selectedValue]));
        }

        return false;
    },
    getStarIndex: function(linkElement) {
        /// <summary>
        /// Returns the integer index of this star (1-5), based on the element id ("star1",...,"star5").
        /// </summary>
        /// <param name="linkElement">DOM object of one of the link elements</param>

        var idx = parseInt(linkElement.id.charAt(linkElement.id.length - 1));
        Sys.Debug.assert(1 <= idx && idx <= 5);
        return idx;
    }
}
UserPCSite.SubmitReviewRatingSelector.prototype.locDict = null;
/// <field name="locDict">The dictionary to receive localized strings.</field>


UserPCSite.SubmitReviewRatingSelector.registerClass('UserPCSite.SubmitReviewRatingSelector', Sys.UI.Control);

if(typeof(Sys)!=='undefined')Sys.Application.notifyScriptLoaded();