/**
 * GRLP (Global Rewards & Loyalty Platform) Utilities Library
 * Contains implementation of OpenAjax 2.0.3 library and utility function to run various widgets on market sites
 *
 * @author The Coca-Cola Company
 * @version 2.2.0
 *
 * Copyright 2010, The Coca-Cola Company;  All Rights Reserved.
 * Reproduction or use of this file without express written consent is prohibited.
 */

/**
 * Create gim (Global Interactive Marketing) namespace to isolate all variables and functions
 */
var gim = function(){

    /**
     * Object literal that will store instances/object of each widget created using AddWidget() constructor function
     * @public
     */
    var widget = {};

    /**
     * Hub Client (Listner) object, which basically listen to each and every topic and pass it on to handleTopics() function
     * Also point to window.hubClient (used by Widget Explorer)
     * @public
     */
    var hubClient;

    /**
     * Object literal that will store ID (as key) and existing CSS classes (as value) of wrapper div elements used for widgets
     * These classes are preserved to avoid getting overwritten by custom and default classes assigned by utility
     * @private
     */
    var _wrapperHtmlCss = {};

    /**
     * Array that will stores last topic value, it is NOT specific to widget
     * Used for duplicate topic handling
     * @private
     */
    var _topicQueue = [];

    /**
     * Object literal that will store list of all widget listerner events
     * @private
     */
    var _listeners = {};

    /**
     * Object literal that will store list of all valid events, listner functions will only works for events listed in this object literal
     * @private
     */
    var _events = {
        'onWidgetStart' : true,
        'onWidgetEnd' : true,
        'onWidgetConn' : true,
        'onWidgetClose' : true
    };

    /**
     * Object literal that will store default settings used by all widgets, these will be overwritten by values in settings.js
     * Also, widget will fall back to default settings if settings.js file is missing
     * @private
     */
    var _defaultSetting = {
        hubConnectionTimeout : 60000,
        locale : 'en_US',
        inputFormat : 'html',
        outputFormat : 'html',
        isInline : true,

        minWidgetHeight : 150,
        minWidgetWidth : 200,

        outerOverlayWrapper : {
            'id':'widgetOuterOverlayWrapper'
        }
    };

    /**
     * Interget that will store Unique ID used in various functions to unique ID for div, widget etc.
     * @private
     */
    var _uniId = 1;

    /**
     * Boolean that will enable/disable logger function
     * @private
     */
    var _debug = true;

    /**
     * *********************************************************************************************
     * Initialize OpenAjax Managed Hub and define callback methods
     * *********************************************************************************************
     */

    /**
     * Callback function for publish requests, return true will approve all publish requests.
     * @params topic, data, publishContainer, subscribeContainer
     * @private
     */
    var onMHPublish = function() {
        return true;
    };

    /**
     * Callback function for subscribe requests, return true will approve all subscribe requests.
     * @params topic, container
     * @private
     */
    var onMHSubscribe = function() {
        return true;
    };

    /**
     * Callback function for unsubscribe requests, return true will approve all unsubscribe requests.
     * @param topic, container
     * @private
     */
    var onMHUnsubscribe = function() {
        return true;
    };

    /**
     * Callback function for security alerts
     * @param source, alertType
     * @private
     */
    var onMHSecurityAlert = function() {
        return true;
    };

    /**
     * Initialize OpenAjax ManagedHub Object and assign callback functions
     * @private
     */
    var manageAgencyHub = new OpenAjax.hub.ManagedHub({
        onPublish:       onMHPublish,
        onSubscribe:     onMHSubscribe,
        onUnsubscribe:   onMHUnsubscribe,
        onSecurityAlert: onMHSecurityAlert
    });

    /**
     * *********************************************************************************************
     * Initialize listner container and define callback methods
     * *********************************************************************************************
     */

    /**
     * Callback function for listner container connect
     * @private
     */
    var onListnerConnect = function(container) {
        return true;
    };

    /**
     * Callback function for listner container disconnect
     * @private
     */
    var onListnerDisconnect = function(container) {
        return true;
    };

    /**
     * Callback function for listner container security alert
     * @private
     */
    var onListnerSecurityAlert = function(source, alertType) {
        return true;
    };

    /**
     * Callback function for listner container Hub Connect
     * Once the Hub is connected, it will assign handleTopics as callback function for all topics
     * @private
     */
    var onListnerHubConnect = function(hubClient, success, error ) {
        if (success) {
            hubClient.subscribe('**', handleTopics);
        }
    };

    /**
     * Initialize function will create an inline container client, that will as a listner for all topics
     * published by various widgets, without a lister client nothing will work
     * @public
     */
    var init = function() {

        /**
         * Check if hubClient is already initialized
         */
        if(typeof hubClient === 'undefined'){
            var listnerContainer = new OpenAjax.hub.InlineContainer(manageAgencyHub , 'listnerClient', {
                Container: {
                    onConnect:       onListnerConnect,
                    onDisconnect:    onListnerDisconnect,
                    onSecurityAlert: onListnerSecurityAlert
                }
            });

            /**
             * Create listner Hub client
             */
            hubClient = new OpenAjax.hub.InlineHubClient({
                HubClient: {
                    onSecurityAlert: onListnerSecurityAlert
                },
                InlineHubClient: {
                    container: listnerContainer
                }
            });
            hubClient.connect( onListnerHubConnect );

            /**
             * Close hubClient to global hubClient object for backward compatibility
             */
            window.hubClient = hubClient;
        }

        /**
         * Override init function to make sure it is initialized once once
         */
        gim.init = function(){};
    };

    /**
     * Function to render a new widget. If the widget already exist, it will be removed first
     * by calling removeWidget function to make sure there is no duplicate instances of same widget
     * @param {string} name Widget name
     * @param {string} view Widget View name
     * @param {string} locale Language in which widget will be rednered
     * @param {string} param Additional query parameter that needs to be appended to a widget while rendering
     * @param {string} wrapper Wrapper <div> ID in which widget needs to be rendered
     * @return {boolean}
     * @public
     */
    var renderWidget = function(name, view, locale, param, wrapper){
        logger.info(name + '_' + view + ' [START]', arguments);

        /**
         * Perform validation for mandatory parameters
         */
        if(!isSet(name) || !isSet(view)){
            logger.warn('Render Widget Failed - name or view missing', arguments);
            return false;
        }

        /**
         * Increment the ID to keep it unique
         */
        _uniId++;

        /**
         * Generate Unique Widget ID
         */
        var widgetId = _returnWidgetId(name, view);

        /**
         * Perform cleanup, remove all the existing widgets assigned to the wrapper of new widget
         */
        var _wrapper = getWrapper(name, view, wrapper, false);
        for(var key in gim.widget){
            if(isSet(gim.widget[key].setting.wrapper.id) && gim.widget[key].setting.wrapper.id == _wrapper){
                removeWidget(_returnWidgetId(gim.widget[key].name, gim.widget[key].view), false);
            }
        }

        /**
         * Remove widget if it already exist, this is done to make sure, duplicated instances are not created for same widget
         */
        removeWidget(widgetId, false);

        /**
         * Create a new widget object and add it to widget object literal
         */
        widget[widgetId] = new AddWidget({
            'id' : widgetId,
            'name': name,
            'view' : view,
            'locale' : locale,
            'param' : param,
            'wrapper' : wrapper,
            'defaultSetting' : _defaultSetting
        });

        /**
         * Check if the new widget container initialized, if not, remove current widget from widget object literal
         */
        if(!isSet(widget[widgetId].container) ||  widget[widgetId].container === null){
            removeWidget(widgetId, false);
            return false;
        }

        return true;
    };

    /**
     * *********************************************************************************************
     * Add Widget Constructor Function
     * *********************************************************************************************
     */

    /**
     * Constructor function that will return widget object
     * @constructor
     * @this {AddWidget}
     * @param {object} args Arguments
     * args.id {string} Unique widget ID
     * args.name {string} Widget name
     * args.view {string} Widget View name
     * args.locale {string} Language that will be used while rendering widget
     * args.param {string} Additional query parameter that needs to be appended to a widget while rendering
     * args.wrapper {string} Wrapper <div> ID in which widget needs to be rendered
     * @public
     * @return {widget} Widget object
     */
    var AddWidget = function(args){

        /*
         * Set widget defaults
         */
        this.id = args.id;
        this.name = args.name;
        this.view = args.view;

        /**
         * Get the widget setting values from settings.js
         */
        this.setting = getSetting(this.name, this.view);

        /**
         * Set widget locale, it will be overridden in order
         * utility default >> settings.js >> global widgetLocale (configurations.js) >> passed as a argument to renderWidget function
         */
        this.setting.locale = args.locale || window.widgetLocale || this.setting.locale || args.defaultSetting.locale;

        this.setting.parameters = args.param || this.setting.parameters;

        /*
         * Assign various widget setting values to default if they don't exist
         */
        if(!isSet(this.setting.hubConnectionTimeout)){
            this.setting.hubConnectionTimeout = args.defaultSetting.hubConnectionTimeout;
        }

        if(!isSet(this.setting.inputFormat)){
            this.setting.inputFormat = args.defaultSetting.inputFormat;
        }

        if(!isSet(this.setting.outputFormat)){
            this.setting.outputFormat = args.defaultSetting.outputFormat;
        }

        if(!isSet(this.setting.isInline)){
            this.setting.isInline = args.defaultSetting.isInline;
        }

        /**
         * Dispatch onWidgetStart event
         */
        dispatchEvent.call(this, 'onWidgetStart', {
            'eventType' : 'onWidgetStart',
            'widget':{
                'id' : this.id,
                'name' : this.name,
                'view' : this.view
            },
            'setting' : this.setting
        });

        /*
         * if url property exists in widget, do a complete page redirect
         */
        if(isSet(this.setting.url)){
            window.location.href = this.setting.url;
            return true;
        }

        /*
         * Get widget wrapper element
         */
        this.setting.wrapper = getWrapper(this.name, this.view, args.wrapper);

        /**
         * Get wrapper BG iframe for dropdown overlapping fix in IE6
         */
        this.setting.wrapperBGIframe = getWrapperBGIframe.apply(this);

        /**
         * Set the widget wrapper properties/look and feel
         */
        readyWrapper.apply(this);

        /**
         * Get widget URL from configurables.js
         */
        if((this.url = getWidgetURL.apply(this)) === null){
            logger.warn('Widget URL is null.');
            return false;
        }

        /**
         * Callback function for widget connect, it will be invoked as soon as the widget connects with Hub
         * @param container {object} iframe container object of the widget
         * @private
         */
        this.onWidgetConnect = function (container) {

            /**
             * Get Widget Object
             */
            var _widget = getWidgetByClientId(container.getClientID());

            showHideOverlay(false, 'in', container.getIframe().parentNode);

            /**
             * Dispatch onWidgetConn event
             */
            dispatchEvent.call(_widget, 'onWidgetConn', {
                'eventType' : 'onWidgetConn',
                'widget':{
                    'id' : _widget.id,
                    'name' : _widget.name,
                    'view' : _widget.view
                },
                'setting' : _widget.setting,
                'container' : _widget.container
            });

            logger.info(_widget.id + ' [END]');
        };

        /**
         * Callback function for widget disconnects
         */
        this.onWidgetDisconnect = function(container) {
            return true;
        };

        /**
         * Callback function to handle client-side security alerts
         */
        this.onWidgetSecurityAlert = function(container, alertType) {
            showHideOverlay(false, 'in', container.getIframe().parentNode);

            handleSecurityAlert({
                'container' : container,
                'alertType' : alertType,
                'widget' : getWidgetByClientId(container.getClientID())
            });
        };

        try{
            this.container = new OpenAjax.hub.IframeContainer(manageAgencyHub , 'widgetID' + _uniId, {
                Container: {
                    onConnect:          this.onWidgetConnect,
                    onDisconnect:       this.onWidgetDisconnect,
                    onSecurityAlert:    this.onWidgetSecurityAlert
                },
                IframeContainer: {
                    // DOM element that is parent of this container:
                    parent:             this.setting.wrapper,
                    // Container's iframe loads the following URL:
                    uri:                this.url,
                    // Tunnel URL required by IframeHubClient. This particular tunnel URL
                    // is the one that corresponds to release/all/OpenAjaxManagedHub-all.js:
                    tunnelURI:          window.tunnelURI,
                    // If the client at uri does not establish a connection with this
                    // container in the given time (in milliseconds), the onSecurityAlert callback is
                    // called with a LoadTimeout error code.
                    timeout:            this.setting.hubConnectionTimeout,
                    // Container's iframe will have these CSS styles:
                    iframeAttrs:        isSet(this.setting.iframeAttributes)? this.setting.iframeAttributes : window.iframeAttributes
                }
            });

        }
        catch(e){
            logger.warn('Widget Rendering failed with error >> ' + e.message);
            return false;
        }

        /**
         * Set focus on the widget iframe, so that any focus on field within iframe will start work
         */
        this.container.getIframe().contentWindow.focus();

        /**
         * Dispatch onWidgetEnd event
         */
        dispatchEvent.call(this, 'onWidgetEnd', {
            'eventType' : 'onWidgetEnd',
            'widget':{
                'id' : this.id,
                'name' : this.name,
                'view' : this.view
            },
            'setting' : this.setting,
            'container' : this.container
        });

        return true;
    };

    /**
     * This function will reset the widget wrapper width and height as per the parameters
     * @this {AddWidget}
     * @param {object} size New Dimensions
     * size.height {string} New Height
     * size.width {string} New Width
     * size.unit {string} Unit Type
     * @return {boolean}
     */
    AddWidget.prototype.resizeWrapper = function(size){
        /**
         * Perform Validation
         */
        if(!isObject(size)){
            return false;
        }

        /**
         * Set default unit type
         */
        size.unit = size.unit || 'px';

        /**
         * Check if wrapperIe6IframeFix.inc property exist in widget setting, and apply its value to incBGIframe
         * incBGIframe value will be used to increment the widget BG iframe height & width
         */
        var incBGIframe = (isSet(this.setting.wrapperIe6IframeFix) && isSet(this.setting.wrapperIe6IframeFix.inc))? parseInt(this.setting.wrapperIe6IframeFix.inc, 10) : 0;

        if(!isEmpty(size.width)){
            /**
             * Check if the width is passed with unit or without unit
             */
            size.width = isNaN(size.width)? size.width : parseInt(size.width, 10) + size.unit;
            this.setting.wrapper.style.width = size.width;

            if(!isEmpty(this.setting.wrapperBGIframe)){
                /**
                 * Get unit type
                 */
                size.unit = !isEmpty(size.width.replace(/[0-9]/g, ''))? size.width.replace(/[0-9]/g, '') : size.unit;
                this.setting.wrapperBGIframe.style.width = parseInt(size.width, 10) + incBGIframe + size.unit;
            }
        }

        if(!isEmpty(size.height)){
            /**
             * Check if the width is passed with unit or without unit
             */
            size.height = isNaN(size.height)? size.height : parseInt(size.height, 10) + size.unit;
            this.setting.wrapper.style.height = size.height;

            if(!isEmpty(this.setting.wrapperBGIframe)){
                /**
                 * Get unit type
                 */
                size.unit = !isEmpty(size.height.replace(/[0-9]/g, ''))? size.height.replace(/[0-9]/g, '') : size.unit;
                this.setting.wrapperBGIframe.style.height = parseInt(size.height, 10) + incBGIframe + size.unit;
            }
        }

        return true;
    };

    /**
     * This function will check if resizeWidgetDimensions property is defined in widget settings,
     * and if it exists, it will reset the height and width of popup widget. It will also increment
     * the height & width value, if width and height property defined in resizeWidgetDimensions object literal
     * @this {AddWidget}
     * @param {string} w New Width
     * @param {string} h New Height
     */
    AddWidget.prototype.resizeDimension = function(w, h){
        if(this.setting.isInline === false && isSet(this.setting.resizeWidgetDimensions) && this.setting.resizeWidgetDimensions !== false){
            if(!isEmpty(w)){
                w = (isSet(this.setting.resizeWidgetDimensions.width)) ? w + this.setting.resizeWidgetDimensions.width : w;
            }
            if(!isEmpty(h)){
                h = (isSet(this.setting.resizeWidgetDimensions.height)) ? h + this.setting.resizeWidgetDimensions.height : h;
            }
            this.resizeWrapper({
                'width':w,
                'height':h
            });
        }
    };

    /**
     * This function will reload the widget iframe
     * @this {AddWidget}
     */
    AddWidget.prototype.reload = function(){
        try{
            this.container.getIframe().contentWindow.location = updateUrlParam(this.container.getIframe().src, {
                'ieFix': Math.floor(Math.random()*101)
            });
        }
        catch(e){
            logger.warn('Iframe reload failed with error >> ' + e.message);
        }
    };

    /**
     * This function will reposition the widget wrapper (by changing its left & top CSS value) as per the parameters
     * @this {AddWidget}
     * @param {object} pos Position object literal
     * pos.left {string} New Left
     * pos.top {string} New Top
     * size.unit {string} Unit Type
     */
    AddWidget.prototype.repositionWrapper = function(pos){
        /**
         * Check for popup widget
         */
        if(this.setting.isInline === false){
            /**
             * Set the default unit type to px
             */
            pos.unit = pos.unit || 'px';

            /**
             * Set top position
             */
            if(isSet(pos.top)){

                pos.top = isNaN(pos.top)? pos.top : parseInt(pos.top, 10) + pos.unit;

                this.setting.wrapper.style.top = pos.top;

                if(!isEmpty(this.setting.wrapperBGIframe)){
                    this.setting.wrapperBGIframe.style.top = pos.top;
                }
            }

            /**
             * Set left position
             */
            if(isSet(pos.left)){

                pos.left = isNaN(pos.left)? pos.left : parseInt(pos.left, 10) + pos.unit;

                this.setting.wrapper.style.left = pos.left;

                if(!isEmpty(this.setting.wrapperBGIframe)){
                    this.setting.wrapperBGIframe.style.left = pos.left;
                }
            }
        }
    };

    /**
     * This function will reposition the popup widget wrapper to center of the page
     * @this {AddWidget}
     */
    AddWidget.prototype.reposition = function(){

        /**
         * Check if the current widget is a popup widget
         */
        if(this.setting.isInline === false){

            /**
             * Check if the repositionWidget property is true
             */
            if(isSet(this.setting.repositionWidget) && this.setting.repositionWidget === true){
                this.repositionWrapper(repositionElement({
                    'p':window.document,
                    'c':this.setting.wrapper
                }));
            }

            /**
             * Check for IE6 Browser + Overlay wrapper exist
             * If found, change its height and width to fixed pixel
             */
            if (/MSIE 6/i.test(navigator.userAgent) && document.getElementById(gim.defaultSetting.outerOverlayWrapper.id) !== null) {
                var eF = document.getElementById(gim.defaultSetting.outerOverlayWrapper.id);
                eF.style.width = document.documentElement.clientWidth;
                eF.style.height = getDimension.call(window.document,'Height');
            }
        }
    };

    /*
     * This is HubConnect callback function, which will handle all topics fired by various open widgets
     * - It can be overridden by defining handleTopics_override function
     * - Specific topic handling functionality can be overridden by defining
     *   [topic]_override function (e.g. ko_platform_ui_changeWidget__override)
     * @param {string} topic Topic name
     * @param {object} data Topic data
     * @return {boolean}
     */
    var handleTopics = function(topic, data) {
        logger.log('[TOPIC]', topic, data);

        /**
         * Temporary key variable for looping
         */
        var key;

        /**
         * Get source widget setting object literal from from settings.js
         */
        var srcWidgetConfig = getSetting(data.srcWidget, data.srcView);

        /**
         * Check if mapping property exist in widget setting, if it exist, change data value
         * Mapping allow marketsite to change topic data value by defining mapping property in widget setting
         */
        if(!isEmpty(srcWidgetConfig) && isSet(srcWidgetConfig.mapping)){
            for(key in data){
                if(isSet(srcWidgetConfig.mapping[key]) && isSet(srcWidgetConfig.mapping[key][data[key]])){
                    logger.warn('[Override] Topic Data', topic, key, data);
                    data[key] = srcWidgetConfig.mapping[key][data[key]];
                }
            }
        }

        /**
         * Check if handleTopics_override function exist in window scope, if it exist, invoke it by passing topic and data values
         * This will override the default functionality of gim.handleTopics function
         */
        if(typeof window.handleTopics_override === 'function'){
            logger.warn('[Override] handleTopics', topic, data);
            window.handleTopics_override(topic, data);
            return true;
        }

        /**
         * Check if topic specific override function exist in window scope, if it exist, invoke it by passing topic and data values
         * Based on the reurn value of override function, default functionality of topic handling defined gim.handleTopics function
         * will be executed
         */
        if(typeof window[(topic.replace(/\./g, '_') + '_override')] === 'function'){
            logger.warn('[Override] Topic - ' + topic, data);
            if(!window[(topic.replace(/\./g, '_') + '_override')](data, topic)){
                return true;
            }
        }

        /**
         * Check if the current topic is duplicate and checkDupTopic is NOT false
         */
        if(typeof _topicQueue[0] !== 'undefined' && _topicQueue[0] === topic){

            if(typeof window.widget_default_config !== 'undefined' && typeof window.widget_default_config.dupTopic === 'boolean' && window.widget_default_config.dupTopic !== true){
                return true;

            /**
             * Check if callback function exist for duplicate topic scenario, if it exists, invoke the same
             */
            }else if(typeof window.widget_default_config !== 'undefined' && typeof window.widget_default_config.dupTopic === 'function'){

                /**
                 * Execute the callback function and check for return value,
                 * if it is false, exit handleTopics function else continue normal execution
                 */
                if(!window.widget_default_config.dupTopic(data, topic)){
                    return true;
                }

            /**
             * Check if callback function exist for specific duplicate topic, if it exists, invoke the same
             */
            }else if(typeof window.widget_default_config !== 'undefined' && typeof window.widget_default_config.dupTopic === 'object' && typeof window.widget_default_config.dupTopic[topic] === 'function'){

                /**
                 * Execute the callback function and check for return value,
                 * if it is false, exit handleTopics function else continue normal execution
                 */
                if(!window.widget_default_config.dupTopic[topic](data, topic)){
                    return true;
                }

            /**
             * If no callback function is defined, but topic property exists in dupTopic object literal
             * in widget setting, stop the execution of handleTopics function
             */
            }else if(typeof window.widget_default_config !== 'undefined' && (typeof window.widget_default_config.dupTopic === 'object' && typeof window.widget_default_config.dupTopic[topic] !== 'undefined')){
                return true;
            }
        }

        /**
         * Store the value of current topic in _topicQueue array to check for duplicate topic
         */
        _topicQueue[0] = topic;

        /**
         * This will store source widget name
         */
        var widgetName;

        /**
         * This will store temp widget object
         */
        var _widget;

        /**
         * Stores current widget change URL
         */
        var widgetUrl = appendWidgetUrl(data);

        /**
         * Close source widget for certain topics, if it is a popup widget
         */
        if(topic == 'ko.platform.ui.changeWidget' || topic == 'ko.platform.ui.displayItemDetails' || (isSet(window.widget_default_config) && isSet(window.widget_default_config.onTopicWidgetClose) && isSet(window.widget_default_config.onTopicWidgetClose[topic]))){
            /**
             * Get Source Widget
             */
            _widget = getWidget({
                'name' : data.srcWidget,
                'view' : data.srcView
            });

            /**
             * Check if source widget is available and is popup
             */
            if(!isEmpty(_widget) && _widget.setting.isInline === false){
                /**
                 * Close Widget
                 */
                closeWidget({
                    'name' : _widget.name,
                    'view' : _widget.view,
                    'remWrp' : true
                });
            }
        }

        /**********************************
         * Start Topic handling
         **********************************/
        if(topic == 'ko.platform.ui.changeWidget') {
            // Check if widget URL change is required
            if(widgetUrl !== null && widgetUrl !== false){
                window.location = widgetUrl;
                return true;
            }

            widgetName = getConfigByProperty({
                'srcWidget' : data.srcWidget,
                'type' : data.dstWidgetType
            }, 'type').name;

            renderWidget(widgetName, data.dstView);

        } else if (topic == 'ko.platform.state.login') {

            /**
             * get Login widget
             */
            _widget = getWidget({
                'name' : data.srcWidget,
                'view' : data.srcView
            });

            /**
             * If login widget is popup, close it
             */
            if(!isEmpty(_widget) && _widget.setting.isInline === false){
                closeWidget({
                    'name' : _widget.name,
                    'view' : _widget.view,
                    'remWrp' : true
                });
            }

            /**
             * Refresh Widgets
             */
            var _refresh = {};
            _refresh[getConfigByProperty({
                'type' : 'ProfileWidget'
            }).name] = true;
            for(key in gim.widget){
                if(isSet(_refresh[gim.widget[key].name]) && gim.widget[key].setting.isInline === true){
                    gim.widget[key].reload();
                }
            }

        } else if (topic == 'ko.platform.state.loginRequired') {
            renderWidget(getConfigByProperty({
                'type' : 'LoginWidget'
            }).name, 'login');

        } else if (topic == 'ko.platform.state.logout') {
            renderWidget(data.srcWidget, 'start');

        } else if (topic == 'ko.platform.ui.changeView') {
            renderWidget(data.srcWidget, data.dstView);

        } else if(topic == 'ko.platform.ui.popup' || topic == 'ko.platform.ui.osso') {
            // Check if widget URL change is required
            if(widgetUrl !== null && widgetUrl !== false){
                window.location = widgetUrl;
                return true;
            }

            widgetName = getConfigByProperty({
                'srcWidget' : data.srcWidget,
                'type' : data.dstWidgetType
            }, 'type').name;

            data.locale = isSet(data.locale)? data.locale : null;

            renderWidget(widgetName, data.dstView, data.locale, 'url=' + data.url);

        } else if (topic == 'ko.platform.ui.redirect') {
            window.open(data.url);

        } else if(topic == 'ko.platform.ui.changeCatalogWidget') {
            // Check if widget URL change is required
            if(widgetUrl !== null && widgetUrl !== false){
                window.location = widgetUrl;
                return true;
            }

            widgetName = getConfigByProperty({
                'type' : data.dstWidgetType
            }).name;

            if(!isEmpty(data.ticket)) {
                renderWidget(widgetName, data.dstView, null, 'ticket=' + data.ticket);
            } else {
                renderWidget(widgetName, data.dstView, null, 'prizeId=' + data.itemId + '&showPrizeInfo=true');
            }

        } else if(topic == 'ko.platform.ui.displayItemDetails') {
            // Check if widget URL change is required
            if(widgetUrl !== null && widgetUrl !== false){
                window.location = widgetUrl;
                return true;
            }

            widgetName = getConfigByProperty({
                'type' : data.dstWidgetType
            }).name;
            renderWidget(widgetName, data.dstView, null, 'itemId=' + data.itemId + '&fromListItem=' + data.fromListItem);

        } else if(topic == 'ko.platform.ui.widgetDimensions') {
            _widget = getWidget({
                'name' : data.srcWidget,
                'view' : data.srcView
            });

            /**
             * If widget exist with the name and view return by topic, do resizing and repositioning
             * ELSE
             * Loop through all widget, do resizing and repositioning for all the widgets
             * where name = data.srcWidget and the widget is a popup widget
             *
             * This is done to fix issue when the widget view is changed internally at server side
             */
            if(!isEmpty(_widget)){
                _widget.resizeDimension(data.width, data.height);
                _widget.reposition();
            }else{
                for(key in gim.widget){
                    if(gim.widget[key].name === data.srcWidget && gim.widget[key].setting.isInline === false){
                        gim.widget[key].resizeDimension(data.width, data.height);
                        gim.widget[key].reposition();
                    }
                }
            }

        } else if(topic == 'ko.platform.ui.cancel') {
            _widget = getWidget({
                'name' : data.srcWidget,
                'view' : data.srcView
            });

            /**
             * If widget exist with the name and view close the widget
             * ELSE
             * Loop through all widget, close all the widgets
             * where name = data.srcWidget and the widget is a popup widget
             *
             * This is done to fix issue when the widget view is changed internally at server side
             */
            if(!isEmpty(_widget)){
                closeWidget({
                    'name' : _widget.name,
                    'view' : _widget.view,
                    'remWrp' : true
                });
            }else{
                for(key in gim.widget){
                    if(gim.widget[key].name === data.srcWidget && gim.widget[key].setting.isInline === false){
                        closeWidget({
                            'name' : data.srcWidget,
                            'remWrp' : true
                        });
                    }
                }
            }

        } else if(topic == 'ko.platform.ui.displayListItems') {
            // Check if widget URL change is required
            if(widgetUrl !== null && widgetUrl !== false){
                window.location = widgetUrl;
                return true;
            }
            widgetName = getConfigByProperty({
                'type' : data.dstWidgetType
            }).name;
            renderWidget(widgetName, data.dstView, null, 'categoryId=' + toString(data.categoryId) + '&subCategoryId=' + toString(data.subCategoryId) + '&subSubCategoryId=' + toString(data.subSubCategoryId) + '&searchText=' + toString(data.searchText)  + '&typeOfStructure=' + toString(data.typeOfStructure));

        } else if(topic == 'ko.core.app.enable.cookies') {
            /**
             * Check for callback function
             */
            if(isSet(window.cookieFired) && window.cookieFired === true){
                /**
                 * Execute callback function and check for return,
                 */
                if(isSet(window.widget_default_config) && typeof window.widget_default_config.dupTopic === 'object' && typeof window.widget_default_config.dupTopic[topic] === 'function'){
                    window.widget_default_config.dupTopic[topic](data, topic);
                }

            }else{
                var cookieDisableMsg = (isSet(window.widget_default_config) && isSet(window.widget_default_config.cookieDisableMsg))? window.widget_default_config.cookieDisableMsg : "Cookies are Disabled: \nWe're sorry, but we are unable to process your request \nbecause cookies are currently disabled in your web browser, \nor you have Internet security software that is preventing \nthem from reaching us.!";
                alert(cookieDisableMsg);
            }

            /**
             * Create cookieFired flag
             */
            window.cookieFired = true;

        } else if(topic == 'ko.platform.state.pointsChanged') {
            if(isSet(data.srcWidgetType) && data.srcWidgetType === 'OutboundSSOWidget'){
                closeWidget({
                    'name' : getConfigByProperty({
                        'type' : data.srcWidgetType
                    }).name,
                    'remWrp' : true
                });
            }

        } else if(topic == 'ko.core.app.load.failure') {
            logger.warn('Widget failed to load from server.', data);

        } else {
            logger.warn('[TOPIC] ELSE', topic, data);
            //alert('Publishing Topic ELSE: Topic - ' + topic + ' Data - ' + JSON.stringify(data));
            return false;
        }

        return true;
    };
    
    /**
    * Converts a value into string. In case value is undefined, returns back blank string.
    */
    var toString = function(value) {
    	if (value == undefined) {
    		return "";
    	}
    	return "" + value;
    }

    /**
     * This will remove widget including its instance from gim.widget object as well as wrapper
     * @param {string} id Widget ID (name + '_' + view)
     * @param {boolean} remWrp If true, it will set diaplay of wrapper element to none
     * @private
     */
    var removeWidget = function(id, remWrp){

        if(typeof widget[id] !== 'undefined'){
            try{
                /**
                 * Remove the instance from Managed Hub as well as its container
                 */
                manageAgencyHub.removeContainer(widget[id].container);
            }catch(e){}

            /**
             * Hide wrapper and empty content
             */
            if(!isSet(remWrp) || remWrp !== false){

                /**
                 * Remove any style and class attribute if defined
                 */
                widget[id].setting.wrapper.setAttribute ('style', '');
                widget[id].setting.wrapper.style.cssText = '';
                widget[id].setting.wrapper.className = '';

                /**
                 * Hide the Wrapper Div and cleanup
                 */
                widget[id].setting.wrapper.style.display = 'none';
                widget[id].setting.wrapper.innerHTML = '';
            }

            /**
             * In case of popup widget:
             * 1. Remove overlay
             * 2. Remove BG Iframe
             */
            if(widget[id].setting.isInline !== true){
                showHideOverlay(false, 'out');
                removeWrapperBGIframe();
            }

            /**
             * Restore the original CSS class if defined
             */
            if(typeof _wrapperHtmlCss[widget[id].setting.wrapper.id] !== 'undefined'){
                widget[id].setting.wrapper.className = _wrapperHtmlCss[widget[id].setting.wrapper.id];
            }

            /**
             * Dispatch onWidgetClose event
             */
            dispatchEvent.call(widget[id], 'onWidgetClose', {
                'eventType' : 'onWidgetClose',
                'widget':{
                    'id' : widget[id].id,
                    'name' : widget[id].name,
                    'view' : widget[id].view
                },
                'setting' : widget[id].setting
            });

            /**
             * Delete widget from global gim.widget array
             */
            delete(widget[id]);

            return true;
        }
        return false;
    };

    /**
     * This function will remove widget from gim.widget object by Wrapper/Name/Name+View
     * This funcion also accept Wrapper ID as parameter instead of args object for backword compatibility
     * @param {object/string} args Argument object / Wrapper ID
     * args.name {string} Widget Name
     * args.view {string} Widget View
     * args.wrapper {string} Widget Wrapper
     * args.remWrp {boolean} Value to check if wrapper cleanup is required or not
     * @public
     */
    var closeWidget = function(args){
        /**
         * Temporary key variable for looping
         */
        var key;

        if(isSet(args.name) && isSet(args.view)){
            removeWidget(_returnWidgetId(args.name, args.view), args.remWrp);

        }else if(isSet(args.name)){
            for(key in gim.widget){
                if(isSet(gim.widget[key].name) && gim.widget[key].name == args.name){
                    removeWidget(_returnWidgetId(gim.widget[key].name, gim.widget[key].view), args.remWrp);
                }
            }

        }else if(isSet(args.wrapper)){
            for(key in gim.widget){
                if(isSet(gim.widget[key].setting.wrapper.id) && gim.widget[key].setting.wrapper.id == args.wrapper){
                    removeWidget(_returnWidgetId(gim.widget[key].name, gim.widget[key].view), args.remWrp);
                }
            }

        /**
         * For backword compatibility, where onCloseClick() is called with wrapper ID
         */
        }else if(typeof args === 'string'){
            closeWidget({
                'wrapper' : args,
                'remWrp' : true
            });
        }
    };

    /**
     * This will return wrapper element to display widget
     * @param {string} name Widget Name
     * @param {string} view Widget View
     * @param {string} override Wrapper ID that will override the ID defined in settings.js
     * @param {boolean} createEl If true, it will create <div> element if it does not exist
     * @return {object} div element
     * @private
     */
    var getWrapper = function(name, view, override, createEl){
        var setting = getSetting(name, view);
        var def = (isSet(setting.isInline) && setting.isInline === true)? 'inlineWidget' : 'popupWidget';
        var wrapper = override || setting.wrapper || setting.divId || def;

        if(!isSet(createEl) || createEl === true){
            /**
             * Check if the Weidget Wrapper div element already exist, IF not, create a new div element
             */
            if(document.getElementById(wrapper) === null){
                var wrpEl = document.createElement('div');
                wrpEl.id = wrapper;
                wrpEl.style.display = 'none';
                wrpEl.className = def;
                document.body.appendChild(wrpEl);
            }
            return document.getElementById(wrapper);
        }
        return wrapper;
    };

    /**
     * This function will create a iframe which will act as a background for wrapper iframe
     * to fix IE6/7 select element z-index bug
     * @private
     */
    var getWrapperBGIframe = function(){
        if(this.setting.isInline === false && isSet(this.setting.wrapperIe6IframeFix) && (/MSIE 6/i.test(navigator.userAgent))){
            /**
             * Get BG iframe element
             */
            var bgIframeEl = (document.getElementById('widgetBgIframe') === null)? document.createElement('iframe') : document.getElementById('widgetBgIframe');

            /**
             * Add default style properties
             */
            bgIframeEl.id = 'widgetBgIframe';
            bgIframeEl.src = 'javascript:"<html></html>";';
            bgIframeEl.style.position = 'absolute';
            bgIframeEl.style.border = 'none';
            bgIframeEl.style.display = 'block';
            bgIframeEl.style.zIndex = '95';
            bgIframeEl.className = 'widgetBgIframe';

            /**
             * Override defaults with user supplied properties
             */
            if(typeof this.setting.wrapperIe6IframeFix === 'object'){
                for(var key in this.setting.wrapperIe6IframeFix){
                    if(key !== ('id' || 'inc')){
                        bgIframeEl.style[key] = this.setting.wrapperIe6IframeFix[key];
                    }
                }
            }

            /**
             * Append BG iframe element to document
             */
            if(document.getElementById('widgetBgIframe') === null){
                document.body.appendChild(bgIframeEl);
            }

            return document.getElementById(bgIframeEl.id);
        }
        
        return false;
    };

    /**
     * Remove popup widget BG iframe
     * @private
     */
    var removeWrapperBGIframe = function(){
        if(document.getElementById('widgetBgIframe') !== null){
            document.body.removeChild(document.getElementById('widgetBgIframe'));
        }
    };

    /**
     * This is a decorator function, that will apply various CSS classes, and other styles like height/width etc. to widget wrapper
     * @private
     */
    var readyWrapper = function(){

        /**
         * If class is already defined in HTML, store it for future reference
         */
        if(typeof _wrapperHtmlCss[this.setting.wrapper.id] === 'undefined'){
            _wrapperHtmlCss[this.setting.wrapper.id] = this.setting.wrapper.className;
        }

        this.setting.wrapper.innerHTML = '';

        /**
         * Call override function if exist
         */
        if(typeof window.readyWrapper_override === 'function'){
            if(!window.readyWrapper_override.apply(this)){
                return true;
            }

        }else if (this.setting.isInline === true) {
            if(typeof window.loadInlineWidget_override === 'function'){
                if(!window.loadInlineWidget_override(this)){
                    return true;
                }
            }

        }else if(typeof window.loadPopupWidget_override === 'function') {
            if(!window.loadPopupWidget_override(this)){
                return true;
            }
        }

        /**
         * Add required classes
         */
        var _class = _wrapperHtmlCss[this.setting.wrapper.id] + ' ';
        _class += (typeof this.setting.isInline !== 'undefined' && this.setting.isInline !== true)? 'popupWidget' : 'inlineWidget';
        _class += ' ' + this.name + ' ' + this.name + '_' + this.view;
        _class += (typeof this.setting.wrapperCSSClass !== 'undefined')? ' ' + this.setting.wrapperCSSClass : '';
        this.setting.wrapper.className = _class;

        /**
         * Resize Wrapper
         */
        this.resizeWrapper({
            'width':this.setting.width,
            'height':this.setting.height
        });

        /**
         * Check if the widget is popup widget
         */
        if(this.setting.isInline === false){
            var widgetCloseId = 'linkToClosepopUp' + _uniId;
            this.setting.wrapper.innerHTML = "<div id='closeButton' class='closeWidget'><a href='#' id='"+widgetCloseId+"' class='"+ this.setting.closeButtonClassName +"'><img src='" + this.setting.closeImageSrc + "' id='closeIconImage' /></a></div><div id='popupIFrame'></div>";
            this.repositionWrapper({
                'top':this.setting.top,
                'left':this.setting.left
            });

            /**
             * Add listner for widget close
             */
            var widgetObj = this;
            addListener(document.getElementById(widgetCloseId), 'click', function(){
                removeWidget(widgetObj.id);
            });

        }

        this.setting.wrapper.style.display = 'block';

        /**
         * If widget wrapper does not have any height/width either by JS or by CSS,
         * set a min height/width to avoid empty display in IE6
         * This will also fix auto repositionWidget widget to center
         */
        if(isSet(this.setting.isInline) && this.setting.isInline !== true && getDimension.call(this.setting.wrapper, 'Width') < _defaultSetting.minWidgetWidth){
            this.setting.wrapper.style.width = _defaultSetting.minWidgetWidth + 'px';
        }
        if(isSet(this.setting.isInline) && this.setting.isInline !== true && getDimension.call(this.setting.wrapper, 'Height') < _defaultSetting.minWidgetHeight){
            this.setting.wrapper.style.height = _defaultSetting.minWidgetHeight + 'px';
        }

        /**
         * If repositionWidget property is true, move the wiget to center of the page
         */
        if(isSet(this.setting.isInline) && this.setting.isInline !== true && isSet(this.setting.repositionWidget) && this.setting.repositionWidget === true){
            this.repositionWrapper(repositionElement({
                'p':window.document,
                'c':this.setting.wrapper
            }));
        }

        // Show Overlay
        showHideOverlay.call(this, true, 'out', document.body);
        showHideOverlay.call(this, true, 'in', this.setting.wrapper);

        return true;
    };

    /**
     * Creates widget URL by merging widget config and settings data
     * @return {string} url Widget URL
     * @private
     */
    var getWidgetURL = function(){
        var widget = getConfigByName(this.name);

        // Check if widget config exists
        if(widget === null){
            return widget;
        }

        var url;
		var isSecure = getSecureView(widget.type,this.view);
		if(isSecure) {
			url = window.getMatchedSecureWidgetsDomain();
		} else {
			url = window.getMatchedWidgetsDomain();
		}
		url += window.widgetPath;
        url += widget.configKey + '/';
        url += this.view + '/';
        url += this.setting.inputFormat +  '/';
        url += this.setting.outputFormat;

        url += '?';

        if (typeof this.setting.locale !== 'undefined'){
            url += 'locale=' + this.setting.locale + '&';
        }

        if (typeof this.setting.parameters !== 'undefined' && this.setting.parameters !== null){
            url += this.setting.parameters + '&';
        }

        return url;
    };

    /**
     * Return widget config (from configurables.js) based on properties passed as an object literal
     * @param {object} args Parameters passed as object literal e.g. {'type':'mytype', 'name':'myname'}
     * @param {string} fallback Fallback property in case multi-property check failed
     * @return {object}
     * @public
     */
    var getConfigByProperty = function(args, fallback) {
        if(typeof window.widgetData !== 'undefined' && typeof window.widgetData.widgets !== 'undefined' && typeof args === 'object'){
            var widgets = window.widgetData.widgets;
            for(var i = 0, j = widgets.length; i < j; i++) {
                var k = true;
                for(var prop in args){
                    if(widgets[i].widget[prop] !== args[prop]) {
                        k = false;
                        break;
                    }
                }
                if(k === true) {
                    return widgets[i].widget;
                }
            }

            /**
             * If widget config not found with defined parameters, do fallback
             */
            if(isSet(fallback) && isSet(args[fallback])){
                logger.warn('Widget Config Not Found. Falling back to ' + fallback, args[fallback]);
                var _args = {};
                _args[fallback] = args[fallback];
                return getConfigByProperty(_args);
            }
        }
        logger.warn('Widget Config Not Found!');
        return null;
    };
/**
 * Check if any secure view exists in the widget 
 */
 
    var getSecureView = function(type,view) {
		if(typeof window.widgetMetaData !== 'undefined' && typeof window.widgetMetaData.widgets !== 'undefined'){
            var widgets = window.widgetMetaData.widgets;
            for(var i = 0; i < widgets.length; i++) {
				var widget = widgets[i].widget;
				if(typeof widget !== 'undefined') {
                    if(widget.widgetType === type) {
						if(widget.secureViews !== 'undefined') {
							for(var k = 0; k < widget.secureViews.length; k++) {
								if(widget.secureViews[k] === view) {
									return true;
									
								}
							}
						}
					}
				}
			}
		}
		return false;
   };
   
    /**
     * This function will return widget config by widget name property
     * @param {string} name Widget Name
     * @return {object} Widget config object
     * @public
     */
    var getConfigByName = function(name) {
        return getConfigByProperty({
            'name' : name
        });
    };

    /**
     * This function will return widget config by widget type property
     * @param {string} type Widget Type
     * @return {object} Widget config object
     * @public
     */
    var getConfigByType = function(type) {
        return getConfigByProperty({
            'type' : type
        });
    };

    /**
     * Loads the settings literal object from page scope
     * @private
     */
    var getSetting = function(name, view) {
        // Return from cache if exist
        if(typeof widget[name + '_' + view] !== 'undefined' && typeof widget[name + '_' + view].setting !== 'undefined'){
            return widget[name + '_' + view].setting;
        }

        var mergedConfig = {};
        var defaultConfig = window.widget_default_config || {};
        var widgetConfig = window['widget_' + name + '_config'];
        var widgetViewConfig = window['widget_' + name + '_' + view + '_config'];

        if (typeof defaultConfig !== 'undefined') {
            mergedConfig = extend(mergedConfig, defaultConfig);
        }

        if (typeof widgetConfig !== 'undefined') {
            mergedConfig = extend(mergedConfig, widgetConfig);
        }

        if (typeof widgetViewConfig !== 'undefined') {
            mergedConfig = extend(mergedConfig, widgetViewConfig);
        }

        return mergedConfig;
    };

    /**
     * Return widget object by its container client ID passed to OpenAjax
     * @param {string} id Container Client ID
     * @return {object} Widget Object
     * @public
     */
    var getWidgetByClientId = function(id) {
        for(var key in gim.widget){
            if(typeof gim.widget[key].container !== 'undefined' && gim.widget[key].container.getClientID() === id){
                return gim.widget[key];
            }
        }
        return null;
    };

    /**
     * Return widget object by widget name
     * @param {string} name Container Client ID
     * @return {object} Widget Object
     * @public
     */
    var getWidgetByName = function(name) {
        for(var key in gim.widget){
            if(typeof gim.widget[key].name !== 'undefined' && gim.widget[key].name === name){
                return gim.widget[key];
            }
        }
        return null;
    };

    /**
     * Return widget object by various properties
     * @return {object} Widget Object
     * @private
     */
    var getWidget = function(args) {
        for(var key in gim.widget){
            var r = true;
            for(var prop in args){
                if(gim.widget[key][prop] !== args[prop]) {
                    r = false;
                    break;
                }
            }
            if(r){
                return gim.widget[key];
            }
        }
        return null;
    };

    /**
     * Create and return unique widget ID
     * @param {string} name Widget Name
     * @param {string} view Widget View
     * @return {string} Widget ID
     * @private
     */
    var _returnWidgetId = function(name, view){
        return name + '_' + view;
    };

    /**
     * Append default URL querystring with values from data based on array defined
     * as property of widget_default_config object in settings.js
     * @private
     */
    var appendWidgetUrl = function(data){
        /** Get current widget config data from settings.js */
        var srcWidgetConfig = getSetting(data.srcWidget, data.srcView);
        /** URL value if exist */
        var locationUrl = srcWidgetConfig[data.dstWidgetType+'Url'];

        if(typeof locationUrl !== 'undefined' && locationUrl !== null){
            locationUrl += '?';
            if(typeof srcWidgetConfig.urlParam !== 'undefined'){
                for(var i = 0, j = srcWidgetConfig.urlParam.length; i < j; i++){
                    if(typeof data[srcWidgetConfig.urlParam[i]] !== 'undefined'){
                        locationUrl += '&'+ srcWidgetConfig.urlParam[i] + '=' + data[srcWidgetConfig.urlParam[i]];
                    }
                }
            }
            return locationUrl;
        }
        return null;
    };

    /** GIM COMMON CALLBACK FUNCTION *********************************************************************/

    /**
     * Call back function that will be invoked by OpenAjax, if it encounters any security alert
     * @param {object} args Arguments
     * args.container {object} Widget container object returned by OpenAjax
     * args.container {string} Security alert type
     * args.widget {object} Widget object reference from gim.widget
     * @private
     */
    var handleSecurityAlert = function(args) {

        // Check and execute if handleSecurityAlert_override function exist
        try {
            if(typeof handleSecurityAlert_override === 'function'){
                handleSecurityAlert_override(args);
                return;
            }

            // Check and execute if alert specific function exist
            if(typeof window[(args.alertType.replace(/\./g, '_') + '_override')] === 'function'){
                window[(args.alertType.replace(/\./g, '_') + '_override')](args);
                return;
            }
        }
        catch (e) {}

        // Handle various security alert
        switch (args.alertType) {
            case 'OpenAjax.hub.SecurityAlert.LoadTimeout':
                /**
                 * Callback function, If the client at params.IframeContainer.uri does not establish
                 * a connection with the container in the given time
                 */
                try{
                    if(typeof widgetConnectionTimeout === 'function'){
                        widgetConnectionTimeout(args);
                    }
                }catch(ex){}
                break;
            case 'OpenAjax.hub.SecurityAlert.FramePhish':
                break;
            case 'OpenAjax.hub.SecurityAlert.ForgedMsg':
                break;
            default:
                break;
        }
    };

    /** HELPER FUNCTIONS ***************************************************************************/

    /**
     * Global method to "extend" objects
     * @private
     */
    var extend = function (obj, extObj) {
        if (arguments.length > 2) {
            for (var i = 1, j = arguments.length; i < j; i++) {
                extend(obj, arguments[i]);
            }
        } else {
            for (var key in extObj) {
                obj[key] = extObj[key];
            }
        }
        return obj;
    };

    /**
     * This function create a overlay on widget onload and remove it once Hub is connected
     * @param {boolean} show If show is true, it will create an overlay, otherwise remove it
     * @param {string} type Type value (in/out) define the type of overlay to create
     * @param {object} wrp Widget wrapper element
     * @return {object} Overlay element
     * @private
     */
    var showHideOverlay = function(show, type, wrapper){

        if(show === true){
            /**
             * Perform Validation
             */
            if(!isSet(type) || (this.setting.isInline === true && type !== 'in') || typeof this.setting.overlay !== 'object' || !isSet(this.setting.overlay[type])){
                return false;
            }

            /*
             * Check if any exisiting overlay element exist, if Not, create a new <div> element
             */
            var _id = (type === 'in')? this.setting.wrapper.id + '_overlayWrapper' : _defaultSetting.outerOverlayWrapper.id;
            var eF = (document.getElementById(_id) !== null)? document.getElementById(_id) : document.createElement('div');

            /**
             * Add attributes and apply style
             */
            eF.id = _id;
            eF.className = type + 'OverlayWrapper';
            applyStyle(eF, this.setting.overlay[type].style, {
                'height':'100%',
                'width':'100%'
            });

            /**
             * Append overlay element to document
             */
            if(document.getElementById(_id) === null){
                wrapper.appendChild(eF);
            }

            /**
             * In case of IN overlay, display message and loading icon
             */
            if(type === 'in'){
                eF.innerHTML = "<div id='" + this.setting.wrapper.id + "_overlayMsg" +"' class='overlayMsg'><p>" + (this.setting.overlay[type].msg || 'Loading...') + "</p><img src='" + (this.setting.overlay[type].loadImg || 'ajax-loader.gif') + "' alt=''/></div>";
                var _overlayMsg = document.getElementById(this.setting.wrapper.id + "_overlayMsg");
                _overlayMsg.style.top = ((parseInt(((this.setting.wrapper.offsetHeight - _overlayMsg.offsetHeight)/2), 10) > 0)? parseInt(((this.setting.wrapper.offsetHeight - _overlayMsg.offsetHeight)/2), 10) : 0) + 'px';
                _overlayMsg.style.left = parseInt(((this.setting.wrapper.offsetWidth - _overlayMsg.offsetWidth)/2), 10) + 'px';
            }

            /**
             * Check for IE6 Browser and use fixed height/width
             */
            if (/MSIE 6/i.test(navigator.userAgent)) {
                eF.style.width = (type === 'out')? document.documentElement.clientWidth : '100%';
                eF.style.height = (type === 'out')? getDimension.call(window.document,'Height') : this.setting.wrapper.offsetHeight;
            }

            /**
             * Check for IE6/7 Browser and add iframe Bg for <select> overlapping fix
             */
            if(type === 'out' && (/MSIE 6/i.test(navigator.userAgent))){

                /**
                 * Get ID for Bg iframe
                 */
                _id = _defaultSetting.outerOverlayWrapper.id + '_bgIframe';

                /**
                 * Get BG Overlay iframe element
                 */
                var eBgIf = (document.getElementById(_id) === null)? document.createElement('iframe') : document.getElementById(_id);

                /**
                 * Add attributes and apply style
                 */
                eBgIf.id = _id;
                eBgIf.className = 'overlayBgIframe';
                eBgIf.src = 'javascript:"<html></html>";';
                eBgIf.setAttribute('allowtransparency', 'true');
                eBgIf.style.filter='progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)';
                applyStyle(eBgIf, null, {
                    'position':'absolute',
                    'display':'block',
                    'top':'0px',
                    'left':'0px',
                    'height':getDimension.call(window.document,'Height'),
                    'width':'100%'
                });

                /**
                 * Append BG iframe element to document
                 */
                if(document.getElementById(_id) === null){
                    document.body.appendChild(eBgIf);
                }
            }

            return eF;

        }else{
            /**
             * Get overlay element
             */
            var _obj = (type === 'in')? document.getElementById(wrapper.id + '_overlayWrapper') : document.getElementById(_defaultSetting.outerOverlayWrapper.id);

            /**
             * If overlay element found, remove it from the document
             */
            if(!isEmpty(_obj)){
                (type === 'in')? wrapper.removeChild(_obj) : document.body.removeChild(_obj);
            }

            /**
             * Check for BG iframe element and if found, remove it from the document
             */
            if(type === 'out' && document.getElementById(_defaultSetting.outerOverlayWrapper.id + '_bgIframe') !== null){
                document.body.removeChild(document.getElementById(_defaultSetting.outerOverlayWrapper.id + '_bgIframe'));
            }

            return true;
        }
    };

    /**
     * This function will return the left and right coordinated for child element based on parent element
     * @param {object} args Arguments
     * args.minHeight {int} Unique widget ID
     * args.minWidth {int} Widget name
     * args.p {string} p Parent Element
     * args.c {string} c Child Element
     * @return {object}
     * @private
     */
    var repositionElement = function(args){
        var _r = {};
        args.minHeight = args.minHeight || 0;
        args.minWidth = args.minWidth || 0;
        _r.top = (args.p.nodeType === 9)?
        ((parseInt((document.documentElement.clientHeight - Math.max(args.c.offsetHeight, args.minHeight))/2, 10) > 0)? parseInt((document.documentElement.clientHeight - Math.max(args.c.offsetHeight, args.minHeight))/2, 10) : 0)
        : '';
        _r.left = (args.p.nodeType === 9)?
        ((document.documentElement.clientWidth - Math.max(args.c.offsetWidth, args.minWidth))/2)
        : '';
        return _r;
    };

    /**
     * This will add/remove any query string parameter in the URL
     * @param {string} url This should be full URI starting from http
     * @param {object} param Function will loop thorugh each property in this object and update/add query param as required
     *                 To remove any query string param, set its value to null
     * @return {string} url Modified url
     * @private
     */
    var updateUrlParam = function(url, param){
        var urlparts = url.split('?');
        var qString = (urlparts.length > 1)? urlparts[1] : '';
        qString = (qString.indexOf('#') !== -1)? qString.split('#')[0] : qString;

        // Existing parameters array
        var paramArray = qString.split(/[&;]/g);
        for(var newParam in param){
            for(var i = (paramArray.length - 1); i >= 0; i--){
                if (paramArray[i].lastIndexOf(encodeURIComponent(newParam), 0) !== -1){
                    (param[newParam] === null)? paramArray.splice(i, 1) : paramArray[i] = newParam + '=' + param[newParam];
                    break;
                }else{
                    paramArray.push(newParam + '=' + param[newParam]);
                    break;
                }
            }
        }
        return urlparts[0]+'?'+paramArray.join('&');
    };

    /**
     * This function will dispatch custom event on a widget
     * @param {string} eType Type of event to dispatch
     * @param {object} args Data object that will be sent to listner functions
     * @private
     */
    var dispatchEvent = function(eType, args){
        if(_events[eType] && _listeners[eType]){
            var listeners = _listeners[eType];
            for(var l = 0; listeners && l < listeners.length; l++){
                var listener = listeners[l];
                if((listener.name === null && listener.view === null) || (this.name === listener.name && listener.view === null) || (this.name === listener.name && this.view === listener.view)){
                    listener.callback.call(this, args);
                }
            }
        }
    };

    /**
     * This function will add a listner function to existing custom widget events
     * @param {string} eType Type of event on which listner needs to be added
     * @param {function} cb Callback listner function that will be invoked on specified event
     * @name {string} name Widget name will allow market site to attach listner function to specific widget
     * @view {string} view Widget View name will allow market site to attach listner function to specific widget
     * @public
     */
    var addWidgetListener = function(eType, cb, name, view){
        if(typeof _events[eType] !== 'undefined'){
            if(!_listeners[eType]){
                _listeners[eType]=[];
            }
            _listeners[eType].push({
                'name' : name || null,
                'view' : view || null,
                'callback' : cb
            });
        }
    };

    /**
     * This function will remove listner function already attached to a event
     * @param {string} eType Type of event on which listner needs to be removed
     * @param {function} cb Callback listner function
     * @name {string} name Widget name
     * @view {string} view Widget View
     * @public
     */
    var removeWidgetListener = function(eType, cb, name, view){
        var listeners = _listeners[eType];
        for(var l = 0; listeners && l < listeners.length; l++){
            if(listeners[l].name === name && listeners[l].view === view && listeners[l].callback === cb) {
                listeners.splice(l,1);
            }
        }
    };

    /**
     * Add event to an element
     * @param {object} el Element on which event needs to be added
     * @param {string} ev Event name
     * @param {function} cb Callback function
     * @param {boolean} cp if true, will enable capture event instead of bubble
     * @private
     */
    var addListener = function(el, ev, cb, cp){
        if(!el){
            return false;
        }
        cp = cp || false;
        if(typeof window.addEventListener === 'function') {
            el.addEventListener(ev, cb, cp);
        }else if(typeof document.attachEvent === 'function'){
            el.attachEvent('on' + ev, cb);
        }else{
            el['on' + ev] = cb;
        }
        return true;
    };

    /**
     * Remove event from an element
     * @param {object} el Element on which event needs to be removed
     * @param {string} ev Event name
     * @param {function} cb Callback function
     * @param {boolean} cp if true, will enable capture event instead of bubble
     * @private
     */
    var removeListener = function(el, ev, cb, cp){
        if(!el){
            return false;
        }
        cp = cp || false;
        if(typeof window.removeEventListener === 'function') {
            el.removeEventListener(ev, cb, cp);
        }else if(typeof document.detachEvent === 'function'){
            el.detachEvent('on' + ev, cb);
        }else{
            el['on' + ev] = null;
        }
        return true;
    };

    /**
     * Apply style on element passed as parameter, will fallback to default, if style does not exist
     * @param {object} el Element
     * @param {object} style Style object literal
     * @param {object} def Default object literal
     * @return {object} Element
     * @private
     */
    var applyStyle = function(el, style, def){
        /**
         * Validate parameters
         */
        if(typeof el !== 'object' || (!isSet(style) && !isSet(def))){
            return false;
        }
        /**
         * Override default with supplied value
         */
        if(isSet(def) && typeof def === 'object'){
            style = (isSet(style) && typeof style === 'object')? extend(def, style) : def;
        }
        for(var key in style){
            el.style[key] = style[key];
        }
        return el;
    };

    /**
     * This will check if the passed parameter is defined or not
     * @private
     */
    var isSet = function(v){
        return (typeof v === 'undefined')? false : true;
    };

    /**
     * This will check if the passed parameter is empty or not
     * will return true if parameter is:
     * - undefined
     * - null
     * - false
     * - ''
     * - 0
     * @private
     */
    var isEmpty = function(v){
        return ((typeof v === 'undefined') || (v === null) || (v === false) || (v === '') || (v === 0))? true : false;
    };

    /**
     * Check if the passed parameter is an object
     * @private
     */
    var isObject = function(v){
        return (typeof v === 'object' && v !== null)? true : false;
    };

    /**
     * Return the dimension of the element passed as parameter
     * @param {string} v Dimension value that needs to be get Height/Width
     * @return {int}
     * @private
     */
    var getDimension = function(v){
        return (this.nodeType === 9)? Math.max(
            this.documentElement['client' + v],
            this.body['scroll' + v], this.documentElement['scroll' + v],
            this.body['offset' + v], this.documentElement['offset' + v]
            ) : Math.max(
            this['offset' + v], this.style[v.toLowerCase()]
            );
    };

    /**
     *  This is a logger object with store various functions like log, info etc. which internally use
     *  console.* functions. Market site can override this function if required to enable their own logging concept.
     *  @public
     */
    var logger = {
        'log' : function(){},
        'info' : function(){},
        'warn' : function(){},
        'error' : function(){}
    };
    if(typeof _debug !== 'undefined' && _debug === true){
        if (typeof window.console !== 'undefined'){
            logger.log = (typeof window.console.log !== 'undefined')? function(){
                window.console.log(arguments);
            } : function(){};
            logger.info = (typeof window.console.info !== 'undefined')? function(){
                window.console.info(arguments);
            } : function(){};
            logger.warn = (typeof window.console.warn !== 'undefined')? function(){
                window.console.warn(arguments);
            } : function(){};
            logger.error = (typeof window.console.error !== 'undefined')? function(){
                window.console.error(arguments);
            } : function(){};
        }
    }

    return{
        /**
         * Public Variables
         */
        hubClient:hubClient,
        widget:widget,
        
        /**
         * Public Functions
         */
        init: init,
        getConfigByProperty: getConfigByProperty,
        getConfigByName: getConfigByName,
        getConfigByType : getConfigByType,
        renderWidget: renderWidget,
        closeWidget: closeWidget,
        getWidgetByName: getWidgetByName,
        getWidgetByClientId: getWidgetByClientId,
        addWidgetListener : addWidgetListener,
        removeWidgetListener : removeWidgetListener,
        logger: logger,

        /**
         * Depricated functions & variables for backward compatibility
         * @deprecated
         */
        onCloseClick : closeWidget
    };
}();


(function(){

    var DomReady = window.DomReady = {};

    // Everything that has to do with properly supporting our document ready event. Brought over from the most awesome jQuery.

    var userAgent = navigator.userAgent.toLowerCase();

    // Figure out what browser is being used
    var browser = {
        version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [])[1],
        safari: /webkit/.test(userAgent),
        opera: /opera/.test(userAgent),
        msie: (/msie/.test(userAgent)) && (!/opera/.test( userAgent )),
        mozilla: (/mozilla/.test(userAgent)) && (!/(compatible|webkit)/.test(userAgent))
    };

    var readyBound = false;
    var isReady = false;
    var readyList = [];

    // Handle when the DOM is ready
    function domReady() {
        // Make sure that the DOM is not already loaded
        if(!isReady) {
            // Remember that the DOM is ready
            isReady = true;

            if(readyList) {
                for(var fn = 0; fn < readyList.length; fn++) {
                    readyList[fn].call(window, []);
                }

                readyList = [];
            }
        }
    }

    // From Simon Willison. A safe way to fire onload w/o screwing up everyone else.
    function addLoadEvent(func) {
        var oldonload = window.onload;
        if (typeof window.onload != 'function') {
            window.onload = func;
        } else {
            window.onload = function() {
                if (oldonload) {
                    oldonload();
                }
                func();
            };
        }
    }

    // does the heavy work of working through the browsers idiosyncracies (let's call them that) to hook onload.
    function bindReady() {
        if(readyBound) {
            return;
        }

        readyBound = true;

        // Mozilla, Opera (see further below for it) and webkit nightlies currently support this event
        if (document.addEventListener && !browser.opera) {
            // Use the handy event callback
            document.addEventListener("DOMContentLoaded", domReady, false);
        }

        // If IE is used and is not in a frame
        // Continually check to see if the document is ready
        if (browser.msie && window == top) (function(){
            if (isReady) return;
            try {
                // If IE is used, use the trick by Diego Perini
                // http://javascript.nwbox.com/IEContentLoaded/
                document.documentElement.doScroll("left");
            } catch(error) {
                setTimeout(arguments.callee, 0);
                return;
            }
            // and execute any waiting functions
            domReady();
        })();

        if(browser.opera) {
            document.addEventListener( "DOMContentLoaded", function () {
                if (isReady) return;
                for (var i = 0; i < document.styleSheets.length; i++)
                    if (document.styleSheets[i].disabled) {
                        setTimeout( arguments.callee, 0 );
                        return;
                    }
                // and execute any waiting functions
                domReady();
            }, false);
        }

        if(browser.safari) {
            var numStyles;
            (function(){
                if (isReady) return;
                if (document.readyState != "loaded" && document.readyState != "complete") {
                    setTimeout( arguments.callee, 0 );
                    return;
                }
                if (numStyles === undefined) {
                    var links = document.getElementsByTagName("link");
                    for (var i=0; i < links.length; i++) {
                        if(links[i].getAttribute('rel') == 'stylesheet') {
                            numStyles++;
                        }
                    }
                    var styles = document.getElementsByTagName("style");
                    numStyles += styles.length;
                }
                if (document.styleSheets.length != numStyles) {
                    setTimeout( arguments.callee, 0 );
                    return;
                }

                // and execute any waiting functions
                domReady();
            })();
        }

        // A fallback to window.onload, that will always work
        addLoadEvent(domReady);
    }

    // This is the public function that people can use to hook up ready.
    DomReady.ready = function(fn, args) {
        // Attach the listeners
        bindReady();

        // If the DOM is already ready
        if (isReady) {
            // Execute the function immediately
            fn.call(window, []);
        } else {
            // Add the function to the wait list
            readyList.push( function() {
                return fn.call(window, []);
            } );
        }
    };

    bindReady();

})();

/**
 * Invoke init function to create managed hub and listner container
 */
window.DomReady.ready(gim.init);

