Source: managers/prsonas-manager.js

/**
 * @module managers/prsonas-manager
 * @description This module manages the DXP SDK functionality.
 * @requires prsonasSdk
 * @requires prsonasApp
 * @requires prsonasAvatar
 */

define([    
    "prsonasSdk",
    "prsonasApp",
    "prsonasAvatar"
], 

function ()
{

    // ---------------------------------------------------------------
    //
    // PRSONAS MANAGER
    //
    // ---------------------------------------------------------------

    var PrsonasManager = function ()
    {
        console.log(" * <prsonas>");

        window.tEvent.eventStr.EVENT_SONAR = "EVENT_SONAR";

        this.def = {            
            MSG_TYPE_PAGE     : "page",
            MSG_TYPE_LOCATION : "location",
            MSG_TYPE_BUTTON   : "button",
            MSG_TYPE_ACTION   : "action",
            MSG_TYPE_LANGUAGE : "language",
            MSG_TYPE_STATUS   : "status",
            MSG_TYPE_CUSTOM   : "custom"
        };

        // from JSON config
        this.jConfig          = -1;
        this.jLangConfig      = -1;
        this.config           = -1;
        this.sendKeyEvents    = -1;
        this.receiveKeyEvents = -1;
        
        this.manifest         = -1;

        // prsonas code library
        this.sdk    = window.com.prsonas.sdk;
        this.avatar = window.com.prsonas.sdk.avatar;
        this.app    = window.com.prsonas.sdk.app;

        this.ui = {

        };

        // experience timer
        this.experienceTimer = {
            startTime   : -1,
            currentPage : -1, 
            inProgress  : false
        };

        // maintains current page Event ID
        this.currentPage = -1;

        this.init();
    };

    PrsonasManager.prototype =
    {    
       
        // ______________________________________________________________
        //                                                           init
        /**
            * init
            * @description Initializes class
            * @fires assignListeners()
            * @memberOf module:managers/prsonas-manager
            * @instance
        */
        init: function()
        { 
            this.assignListeners();            
        },


        // ______________________________________________________________
        //                                                assignListeners
        /** 
        * assignListeners
        * @description Assigns listeners to events used in this class.
        * @listens  EVENT_JSON_LOADED
        * @listens  EVENT_ADMIN_GOT_MANIFEST
        * @listens  EVENT_SET_LANGUAGE
        * @listens  EVENT_LANGUAGE_CONFIG_LOADED
        * @listens  sdk.subscribe("SENDKEY")
        * @memberOf module:managers/prsonas-manager
        * @instance
        */
        assignListeners: function()
        {           
            var self = this;

            if (typeof window.oModel === "undefined" || window.oModel.jConfig === -1){
                window.tEvent.addListener("EVENT_JSON_LOADED", function(evt, data)
                {
                    // clean up for new page
                    self.onJsonLoaded(data); 
                });                
            } else {
                self.onJsonLoaded(window.oModel.jConfig); 
            }

            // manifest load
            window.tEvent.addListener("EVENT_ADMIN_GOT_MANIFEST", function(evt, data){                   
                self.onGotManifest(data);
            });

            // get language config
            window.tEvent.addListener("EVENT_LANGUAGE_CONFIG_LOADED", function(evt, data){
                self.jLangConfig = data;
            });

            // manifest load
            window.tEvent.addListener("EVENT_SET_LANGUAGE", function(evt, languageData){                   
                self.onSetLanguage(languageData);
            });


            if (typeof this.sdk !== "undefined") {
                this.sdk.subscribe(this.sdk.app.eventTypes.SEND_KEY, function(evt){
                    self.onSonarEvent(evt);
                });
            }

        },

        // --------------------------------------------------------------
        // HELPERS
        // --------------------------------------------------------------
        
        /** 
        * getTimeBasedIntent
        * @description Checks for a time-based intent
        * @param  {String} intent - Intent to test against 
        * @return {String} Returns the intent to be used
        * @memberOf module:managers/prsonas-manager
        * @instance
        */
        // ______________________________________________________________
        //                                             getTimeBasedIntent
        getTimeBasedIntent: function(intent) {

            if (typeof intent === "undefined" || typeof this.jConfig.iha === "undefined" || typeof this.jConfig.iha.timeBasedIntents === "undefined") {
                return(intent);
            }

            var tbiObj = this.jConfig.iha.timeBasedIntents;
            var isFound = "";
            var newIntent = "";
            var tempObj,
                startTime,
                endTime,
                currentTime;

            for (var key in tbiObj) {
                if (key.toLowerCase() === intent.toLowerCase()){
                    isFound = key;
                    break;
                }
            }

            if (isFound === ""){
                return(intent);
            } else {
                tempObj = tbiObj[isFound];

                currentTime = window.moment();

                for (var i = 0; i < tempObj.validTimes.length; i++){
                    startTime = window.moment(tempObj.validTimes[i].timeStart, "h:mma");
                    endTime = window.moment(tempObj.validTimes[i].timeStop, "h:mma");

                    if ( currentTime.isBetween(startTime, endTime) ){
                        newIntent = tempObj.validTimes[i].intent;
                        break;
                    }
                }

                if (newIntent === ""){
                    newIntent = tempObj.defaultIntent;
                }

                console.log(" * <oPrsonas.sendKey.getTimeBasedIntent> Using time based intent", newIntent);

                return(newIntent);
            }
        },


        // ______________________________________________________________
        //                                                        sendKey
        /** 
        * sendKey
        * @description Fires off animation intent. First checking for a time-based intent
        * @param {String} intent - Intent to test against 
        * @fires avatar.playAnimation()
        * @memberOf module:managers/prsonas-manager
        * @instance
        */
        sendKey: function(intent, speechText) {

            if (intent === ""){
                return;
            }                       

            // check for time based intent
            intent = this.getTimeBasedIntent(intent);

            // play anim/intent
            if (typeof speechText !== "undefined"){
                this.avatar.playAnimation(intent, speechText); 
                console.log(" * <prsonas.sendKey> " + intent + " | " + speechText);
            } else {
                this.avatar.playAnimation(intent);
                console.log(" * <prsonas.sendKey> " + intent);
            }

            
        },


        // ______________________________________________________________
        //                                           startExperienceTimer
        /** 
        * startExperienceTimer
        * @description Restarts experience timer
        * @param  {String} pageEvent - Page event restarting the experience timer
        * @memberOf module:managers/prsonas-manager
        * @instance
        */
        startExperienceTimer: function(pageEvent) {   
            this.experienceTimer.inProgress = true;
            this.experienceTimer.startTime = new Date();
            this.experienceTimer.currentPage = pageEvent;
        },

        
        // ______________________________________________________________
        //                                           clearExperienceTimer
        /** 
        * clearExperienceTimer
        * @description Clears/kills experience timer
        * @memberOf module:managers/prsonas-manager
        * @instance
        */
        clearExperienceTimer: function() {
            this.experienceTimer.startTime = -1;
            this.experienceTimer.inProgress = false;
            this.experienceTimer.currentPage = -1;
        },


        // ______________________________________________________________
        //                                             endExperienceTimer
        /** 
        * endExperienceTimer
        * @description Ends experience experience timer and packages data for stats
        * @todo Need to log the stats
        * @memberOf module:managers/prsonas-manager
        * @instance
        */
        endExperienceTimer: function() {    

            if (!this.experienceTimer.inProgress) return;

            var experienceTime = Math.abs( this.experienceTimer.startTime - (new Date()) );

            if (experienceTime < this.config.timers.minInteractTime) return;

            // Convert to seconds
            var duration = Math.abs(experienceTime / 1000);
            duration = Math.ceil(duration);
            console.log(" * <prsonas.endExperienceTimer> Experience lasted " + duration + " seconds");
            
            //Need to check if we are in an interaction. If not exit.
            if(duration < 1000){
                
                //Package up data
                var payload = {};
                payload["SessionDuration"] = duration.toString();
                payload["ExitPage"] = this.currentPage;

                var data = JSON.stringify(payload);
                //this.sendGenericStat(data);
            } else {
                console.log(" * <prsonas.endExperienceTimer> Session Timer sent out of range Capture");
            }
            
            // Reset start times
            this.clearExperienceTimer();
        },


        // ______________________________________________________________
        //                                              initSendKeyEvents
        /** 
        * initSendKeyEvents
        * @description Initializes the project JSON sendKeyEvents to trigger intents/sendkeys when event is fired
        * @fires endExperienceTimer()
        * @fires startExperienceTimer()
        * @fires sendKey()
        * @memberOf module:managers/prsonas-manager
        * @instance
        */
        initSendKeyEvents: function(){
            var lang = $("body").attr("data-lang");
            var self = this;
            var knowledgebase = window.oPrsonas.avatar.getQAProjectInfo(true);
            var isKb = false;

            for (var e in this.sendKeyEvents) {

                window.tEvent.addListener(e, function(evt, data) {

                    // end timer
                    self.endExperienceTimer();

                    // start timer
                    self.startExperienceTimer(evt.type);

                    if (typeof self.sendKeyEvents[evt.type] !== "undefined") {

                        var intent = self.sendKeyEvents[evt.type].toLowerCase();

                        // check if genai
                        if (typeof data !== "undefined" && typeof data.spokenStr !== "undefined" && typeof data !== "string" && typeof data.action_speech === "undefined") {

                            var doFallback = false;
                            if (typeof data.action_speechfallback !== "undefined"){
                                var langExceptions = self.jConfig.services.genaiFallbackToIntents;
                                for (var i = 0; i < langExceptions.length; i++) {
                                    if (langExceptions[i] === lang) {
                                        doFallback = true;
                                        break;
                                    }
                                }
                            } 


                            if (doFallback){
                                self.sendKey(data.action_speechfallback.id);
                            } else {
                                if (typeof data._raw.properties.answer.qnaId !== "undefined") {
                                    intent = window.oSpeech.formatQnaIntent(data._raw.properties.answer.qnaId);
                                } else {
                                    intent = window.oSpeech.formatIntentName(data._raw.value);
                                }
                                self.sendKey(intent, data.answer);                                
                            }


                        } else if (typeof data !== "undefined" && typeof data !== "string" && typeof data.action_speech !== "undefined") {
                            
                            var doFallback = false;
                            if (typeof data.action_speechfallback !== "undefined"){
                                var langExceptions = self.jConfig.services.genaiFallbackToIntents;
                                for (var i = 0; i < langExceptions.length; i++) {
                                    if (langExceptions[i] === lang) {
                                        doFallback = true;
                                        break;
                                    }
                                }
                            } 

                            if (doFallback){
                                self.sendKey(data.action_speechfallback.id);
                            } else {
                                if (data.action_speech.context === lang){
                                    self.sendKey(data.action_speech.id);
                                } else {
                                    self.sendKey(data.action_speech.id, data.answer);
                                }                                
                            }


                        } else {

                            // check against knowledgebase
                            isKb = false
                            let temp = -1;
                            if (knowledgebase.success === true && typeof knowledgebase.data.qna !== "undefined") {
                                // look for intent
                                for (let i = 0; i < knowledgebase.data.qna.length; i++) {

                                    // get which metadata we need
                                    if (typeof knowledgebase.data.qna[i].metadata !== "undefined") {


                                        if (typeof knowledgebase.data.qna[i].metadata.action_ux !== "undefined") { //action_ux
                                            temp = knowledgebase.data.qna[i].metadata.action_ux.split("_")[1];
                                        } else if (typeof knowledgebase.data.qna[i].metadata.navigation !== "undefined") { // navigation
                                            temp = knowledgebase.data.qna[i].metadata.navigation;
                                        } else if (typeof knowledgebase.data.qna[i].metadata.intent !== "undefined") { // intent
                                            temp = knowledgebase.data.qna[i].metadata.intent;
                                        }

                                        if (temp !== -1 && temp === intent) {
                                            isKb = true;
                                            self.sendKey(intent, knowledgebase.data.qna[i].answer);
                                            break;
                                        }
                                    }
                                }
                            }


                            // if there is no match in the knowledgebase -> check for json copy
                            if (!isKb) {
                                if (self.jLangConfig !== -1 && typeof self.jLangConfig.customIntents[intent] !== "undefined"){
                                    if (lang === "_ASL" || lang === "_BSL"){
                                        self.sendKey(intent);
                                    } else {
                                        self.sendKey(intent, self.jLangConfig.customIntents[intent]);
                                    }
                                    
                                } else {
                                    self.sendKey(intent);
                                }                                         
                            }
                   
                        }
                        
                    }

                });  

            }
        },

        // ______________________________________________________________
        //                                               getConfigSetting
        /** 
        * getConfigSetting
        * @description Returns setting from avatar config
        * @param {String} settingStr - property from avatar config
        * @returns SettingsValue
        * @memberOf module:managers/prsonas-manager
        * @instance
        */
        getConfigSetting: function(settingStr) { 

            return (this.app.getSetting(settingStr));
        },

        // ______________________________________________________________
        //                                               fireSendkeyEvent
        /** 
        * fireSendkeyEvent
        * @description Fires off event based on sonar received sendkey based on project JSON receiveKeyEvents object
        * @param {String} sendkey - sendkey received from sonar
        * @returns SettingsValue
        * @memberOf module:managers/prsonas-manager
        * @instance
        */
        fireSendkeyEvent: function(sendkey) { 

            if (typeof this.receiveKeyEvents[sendkey.toUpperCase()] === "undefined") {
                return;
            }

            var evt = this.receiveKeyEvents[sendkey.toUpperCase()].event;
            console.log(" * <prsonas.fireSendkeyEvent> ", evt);
            window.tEvent.fire(evt);
        },

        // --------------------------------------------------------------
        // EVENTS
        // --------------------------------------------------------------

        // ______________________________________________________________
        //                                                   onJsonLoaded
        /** 
        * onJsonLoaded
        * @description Project JSON loaded handling
        * @param {JSON} jData - Project JSON
        * @fires initSendKeyEvents()
        * @memberOf module:managers/prsonas-manager
        * @instance
        */
        onJsonLoaded: function(jData) { 

            console.log(" * <prsonas.onJsonLoaded>", jData);

            if (this.config !== -1){
                return;
            }

            // get JSON personas config
            this.jConfig = jData;
            this.config = window.helpers.clone(jData.prsonas);

            // sendKey Events
            this.sendKeyEvents = window.helpers.clone(jData.sendKeyEvents);

            // receive key Events
            this.receiveKeyEvents = window.helpers.clone(jData.receiveKeyEvents);

            this.initSendKeyEvents();
        
        },

        // ______________________________________________________________
        //                                                  onGotManifest
        /** 
        * onGotManifest
        * @description Received SDK manifest JSON
        * @param {JSON} jData - SDK Manifest JSON
        * @memberOf module:managers/prsonas-manager
        * @instance
        */
        onGotManifest: function(jData) { 

            if (this.manifest !== -1){
                return;
            }

            this.manifest = jData;               
        },


        // ______________________________________________________________
        //                                                  onSetLanguage
        /** 
        * onSetLanguage
        * @description Sets the avatar language.
        * @param {Object} languageData - Data of the language being set to
        * @memberOf module:managers/prsonas-manager
        * @instance
        */
        onSetLanguage: function(languageData) { 

            var langCode = this.jConfig.language.nuBrowserIds[languageData.language];

            if (typeof langCode !== "undefined") {

                console.log(" ! <prsonas.onSetLanguage> langCode", langCode);

                // set avatar language.
                window.oPrsonas.avatar.setLanguage(langCode);

            } else {
                console.warn(" ! <prsonas.onSetLanguage> No language data available.");
            }
            
        },


        // ______________________________________________________________
        //                                                  onSonarEvent
        /** 
        * onSonarEvent
        * @description Received sonar event
        * @param {Object} sonarEvt - Sonar event data
        * @fires EVENT_SONAR_SENDKEY
        * @memberOf module:managers/prsonas-manager
        * @instance
        */
        onSonarEvent: function(sonarEvt) { 

            console.log(" * <prsonas.onSonarEvent> ", sonarEvt);
            
            window.tEvent.fire(window.tEvent.eventStr.EVENT_SONAR, sonarEvt);

            switch(sonarEvt.type){
                case this.sdk.app.eventTypes.SEND_KEY:
                    this.fireSendkeyEvent(sonarEvt.value);
                    break;
            }
        }


        

    };

    /** 
    * @global 
    */
    window.oPrsonas = new PrsonasManager();
        
    return (window.oPrsonas);        
   
});