1019 lines · 33.8 KB · JavaScript
RawDownload
"use strict";//// Chat.js//// By Don Hopkins (dhopkins@donhopkins.com) on May 5th, 2017//  Copyright 2017 High Fidelity, Inc.//  Copyright 2024 Overte e.V.////// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// (function() {     var webPageURL = Script.resolvePath("ChatPage.html"); // URL of tablet web page.    var randomizeWebPageURL = true; // Set to true for debugging.    var lastWebPageURL = ""; // Last random URL of tablet web page.    var onChatPage = false; // True when chat web page is opened.    var webHandlerConnected = false; // True when the web handler has been connected.    var channelName = "Chat"; // Unique name for channel that we listen to.    var tabletButtonName = "CHAT"; // Tablet button label.    var tabletButtonIcon = "icons/tablet-icons/menu-i.svg"; // Icon for chat button.    var tabletButtonActiveIcon = "icons/tablet-icons/menu-a.svg"; // Active icon for chat button.    var tabletButton = null; // The button we create in the tablet.    var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); // The awesome tablet.    var chatLog = []; // Array of chat messages in the form of [avatarID, displayName, message, data].    var avatarIdentifiers = {}; // Map of avatar ids to dict of identifierParams.    var speechBubbleShowing = false; // Is the speech bubble visible?    var speechBubbleMessage = null; // The message shown in the speech bubble.    var speechBubbleData = null; // The data of the speech bubble message.    var speechBubbleTextID = null; // The id of the speech bubble local text entity.    var speechBubbleTimer = null; // The timer to pop down the speech bubble.    var speechBubbleParams = null; // The params used to create or edit the speech bubble.     // Persistent variables saved in the Settings.    var chatName = ''; // The user's name shown in chat.    var chatLogMaxSize = 100; // The maximum number of chat messages we remember.    var sendTyping = true; // Send typing begin and end notification.    var identifyAvatarDuration = 10; // How long to leave the avatar identity line up, in seconds.    var identifyAvatarLineColor = { red: 0, green: 255, blue: 0 }; // The color of the avatar identity line.    var identifyAvatarMyJointName = 'Head'; // My bone from which to draw the avatar identity line.    var identifyAvatarYourJointName = 'Head'; // Your bone to which to draw the avatar identity line.    var speechBubbleDuration = 10; // How long to leave the speech bubble up, in seconds.    var speechBubbleTextColor = {red: 255, green: 255, blue: 255}; // The text color of the speech bubble.    var speechBubbleBackgroundColor = {red: 0, green: 0, blue: 0}; // The background color of the speech bubble.    var speechBubbleOffset = {x: 0, y: 0.3, z: 0.0}; // The offset from the joint to whic the speech bubble is attached.    var speechBubbleJointName = 'Head'; // The name of the joint to which the speech bubble is attached.    var speechBubbleLineHeight = 0.05; // The height of a line of text in the speech bubble.    var SPEECH_BUBBLE_MAX_WIDTH = 1; // meters     var textSizeOverlay = Entities.addEntity({        "type": "Text",        "position": MyAvatar.position,        "lineHeight": speechBubbleLineHeight,        "leftMargin": 0,        "topMargin": 0,        "rightMargin": 0,        "bottomMargin": 0,        "unlit": true,        "alignment": "center",        "ignorePickIntersection": true,        "visible": false    }, "local");     // Load the persistent variables from the Settings, with defaults.    function loadSettings() {        chatName = Settings.getValue('Chat_chatName', MyAvatar.displayName);        if (!chatName) {            chatName = randomAvatarName();        }        chatLogMaxSize = Settings.getValue('Chat_chatLogMaxSize', 100);        sendTyping = Settings.getValue('Chat_sendTyping', true);        identifyAvatarDuration = Settings.getValue('Chat_identifyAvatarDuration', 10);        identifyAvatarLineColor = Settings.getValue('Chat_identifyAvatarLineColor', { red: 0, green: 255, blue: 0 });        identifyAvatarMyJointName = Settings.getValue('Chat_identifyAvatarMyJointName', 'Head');        identifyAvatarYourJointName = Settings.getValue('Chat_identifyAvatarYourJointName', 'Head');        speechBubbleDuration = Settings.getValue('Chat_speechBubbleDuration', 10);        speechBubbleTextColor = Settings.getValue('Chat_speechBubbleTextColor', {red: 255, green: 255, blue: 255});        speechBubbleBackgroundColor = Settings.getValue('Chat_speechBubbleBackgroundColor', {red: 0, green: 0, blue: 0});        speechBubbleOffset = Settings.getValue('Chat_speechBubbleOffset', {x: 0.0, y: 0.3, z:0.0});        speechBubbleJointName = Settings.getValue('Chat_speechBubbleJointName', 'Head');        speechBubbleLineHeight = Settings.getValue('Chat_speechBubbleLineHeight', 0.05);        Entities.editEntity(textSizeOverlay, {            "lineHeight": speechBubbleLineHeight        });         saveSettings();    }     // Save the persistent variables to the Settings.    function saveSettings() {        Settings.setValue('Chat_chatName', chatName);        Settings.setValue('Chat_chatLogMaxSize', chatLogMaxSize);        Settings.setValue('Chat_sendTyping', sendTyping);        Settings.setValue('Chat_identifyAvatarDuration', identifyAvatarDuration);        Settings.setValue('Chat_identifyAvatarLineColor', identifyAvatarLineColor);        Settings.setValue('Chat_identifyAvatarMyJointName', identifyAvatarMyJointName);        Settings.setValue('Chat_identifyAvatarYourJointName', identifyAvatarYourJointName);        Settings.setValue('Chat_speechBubbleDuration', speechBubbleDuration);        Settings.setValue('Chat_speechBubbleTextColor', speechBubbleTextColor);        Settings.setValue('Chat_speechBubbleBackgroundColor', speechBubbleBackgroundColor);        Settings.setValue('Chat_speechBubbleOffset', speechBubbleOffset);        Settings.setValue('Chat_speechBubbleJointName', speechBubbleJointName);        Settings.setValue('Chat_speechBubbleLineHeight', speechBubbleLineHeight);    }     // Reset the Settings and persistent variables to the defaults.    function resetSettings() {        Settings.setValue('Chat_chatName', null);        Settings.setValue('Chat_chatLogMaxSize', null);        Settings.setValue('Chat_sendTyping', null);        Settings.setValue('Chat_identifyAvatarDuration', null);        Settings.setValue('Chat_identifyAvatarLineColor', null);        Settings.setValue('Chat_identifyAvatarMyJointName', null);        Settings.setValue('Chat_identifyAvatarYourJointName', null);        Settings.setValue('Chat_speechBubbleDuration', null);        Settings.setValue('Chat_speechBubbleTextColor', null);        Settings.setValue('Chat_speechBubbleBackgroundColor', null);        Settings.setValue('Chat_speechBubbleOffset', null);        Settings.setValue('Chat_speechBubbleJointName', null);        Settings.setValue('Chat_speechBubbleLineHeight', null);         loadSettings();    }     // Update anything that might depend on the settings.    function updateSettings() {        updateSpeechBubble();        trimChatLog();        updateChatPage();    }     // Trim the chat log so it is no longer than chatLogMaxSize lines.    function trimChatLog() {        if (chatLog.length > chatLogMaxSize) {            chatLog.splice(0, chatLogMaxSize - chatLog.length);        }    }     // Clear the local chat log.    function clearChatLog() {        //print("clearChatLog");        chatLog = [];        updateChatPage();    }     // We got a chat message from the channel.     // Trim the chat log, save the latest message in the chat log,     // and show the message on the tablet, if the chat page is showing.    function handleTransmitChatMessage(avatarID, displayName, message, data) {        //print("receiveChat", "avatarID", avatarID, "displayName", displayName, "message", message, "data", data);         trimChatLog();        chatLog.push([avatarID, displayName, message, data]);         if (onChatPage) {            tablet.emitScriptEvent(                JSON.stringify({                    type: "ReceiveChatMessage",                    avatarID: avatarID,                    displayName: displayName,                    message: message,                    data: data                }));        }    }     // Trim the chat log, save the latest log message in the chat log,     // and show the message on the tablet, if the chat page is showing.    function logMessage(message, data) {        //print("logMessage", message, data);         trimChatLog();        chatLog.push([null, null, message, data]);         if (onChatPage) {            tablet.emitScriptEvent(                JSON.stringify({                    type: "LogMessage",                    message: message,                    data: data                }));        }    }     // An empty chat message was entered.    // Hide our speech bubble.    function emptyChatMessage(data) {        popDownSpeechBubble();    }     // Notification that we typed a keystroke.    function type() {        //print("type");    }     // Notification that we began typing.     // Notify everyone that we started typing.    function beginTyping() {        //print("beginTyping");        if (!sendTyping) {            return;        }         Messages.sendMessage(            channelName,             JSON.stringify({                type: 'AvatarBeginTyping',                avatarID: MyAvatar.sessionUUID,                displayName: chatName            }));    }     // Notification that somebody started typing.    function handleAvatarBeginTyping(avatarID, displayName) {        //print("handleAvatarBeginTyping:", "avatarID", avatarID, displayName);    }     // Notification that we stopped typing.    // Notify everyone that we stopped typing.    function endTyping() {        //print("endTyping");        if (!sendTyping) {            return;        }         Messages.sendMessage(            channelName,             JSON.stringify({                type: 'AvatarEndTyping',                avatarID: MyAvatar.sessionUUID,                displayName: chatName            }));    }     // Notification that somebody stopped typing.    function handleAvatarEndTyping(avatarID, displayName) {        //print("handleAvatarEndTyping:", "avatarID", avatarID, displayName);    }     // Identify an avatar by drawing a line from our head to their head.    // If the avatar is our own, then just draw a line up into the sky.    function identifyAvatar(yourAvatarID) {        //print("identifyAvatar", yourAvatarID);         unidentifyAvatars();         var myAvatarID = MyAvatar.sessionUUID;        var myJointIndex = MyAvatar.getJointIndex(identifyAvatarMyJointName);        var myJointRotation =             Quat.multiply(                MyAvatar.orientation,                MyAvatar.getAbsoluteJointRotationInObjectFrame(myJointIndex));        var myJointPosition =            Vec3.sum(                MyAvatar.position,                 Vec3.multiplyQbyV(                    MyAvatar.orientation,                    MyAvatar.getAbsoluteJointTranslationInObjectFrame(myJointIndex)));         var yourJointIndex = -1;        var yourJointPosition;         if (yourAvatarID == myAvatarID) {             // You pointed at your own name, so draw a line up from your head.             yourJointPosition = {                x: myJointPosition.x,                y: myJointPosition.y + 1000.0,                z: myJointPosition.z            };         } else {             // You pointed at somebody else's name, so draw a line from your head to their head.             var yourAvatar = AvatarList.getAvatar(yourAvatarID);            if (!yourAvatar) {                return;            }                        yourJointIndex = yourAvatar.getJointIndex(identifyAvatarMyJointName)             var yourJointRotation =                 Quat.multiply(                    yourAvatar.orientation,                    yourAvatar.getAbsoluteJointRotationInObjectFrame(yourJointIndex));            yourJointPosition =                Vec3.sum(                    yourAvatar.position,                     Vec3.multiplyQbyV(                        yourAvatar.orientation,                        yourAvatar.getAbsoluteJointTranslationInObjectFrame(yourJointIndex)));         }         var identifierParams = {            "type": "PolyLine",            "parentID": myAvatarID,            "parentJointIndex": myJointIndex,            "lifetime": identifyAvatarDuration,            "linePoints": [                myJointPosition,                yourJointPosition            ],            "strokeWidths": [ 0.02, 0.02],            //endParentID: yourAvatarID, //Currently doesn't work. Never implemented.            //endParentJointIndex: yourJointIndex, //Currently doesn't work. Never implemented.            "color": identifyAvatarLineColor        };         avatarIdentifiers[yourAvatarID] = identifierParams;         identifierParams.lineID = Entities.addEntity(identifierParams, "local");         //print("ADDOVERLAY lineID", lineID, "myJointPosition", JSON.stringify(myJointPosition), "yourJointPosition", JSON.stringify(yourJointPosition), "lineData", JSON.stringify(lineData));         identifierParams.timer =            Script.setTimeout(function() {                //print("DELETEOVERLAY lineID");                unidentifyAvatar(yourAvatarID);            }, identifyAvatarDuration * 1000);     }     // Stop identifying an avatar.    function unidentifyAvatar(yourAvatarID) {        //print("unidentifyAvatar", yourAvatarID);         var identifierParams = avatarIdentifiers[yourAvatarID];        if (!identifierParams) {            return;        }         if (identifierParams.timer) {            Script.clearTimeout(identifierParams.timer);        }         if (identifierParams.lineID) {            Entities.deleteEntity(identifierParams.lineID);        }         delete avatarIdentifiers[yourAvatarID];    }     // Stop identifying all avatars.    function unidentifyAvatars() {        var ids = [];         for (var avatarID in avatarIdentifiers) {            ids.push(avatarID);        }         for (var i = 0, n = ids.length; i < n; i++) {            var avatarID = ids[i];            unidentifyAvatar(avatarID);        }     }     // Turn to face another avatar.    function faceAvatar(yourAvatarID, displayName) {        //print("faceAvatar:", yourAvatarID, displayName);         var myAvatarID = MyAvatar.sessionUUID;        if (yourAvatarID == myAvatarID) {            // You clicked on yourself.            return;        }         var yourAvatar = AvatarList.getAvatar(yourAvatarID);        if (!yourAvatar) {            logMessage(displayName + ' is not here!', null);            return;        }         // Project avatar positions to the floor and get the direction between those points,        // then face my avatar towards your avatar.        var yourPosition = yourAvatar.position;        yourPosition.y = 0;        var myPosition = MyAvatar.position;        myPosition.y = 0;        var myOrientation = Quat.lookAtSimple(myPosition, yourPosition);        MyAvatar.orientation = myOrientation;    }     // Make a hopefully unique random anonymous avatar name.    function randomAvatarName() {        return 'Anon_' + Math.floor(Math.random() * 1000000);    }     // Change the avatar size to bigger.    function biggerSize() {        //print("biggerSize");        logMessage("Increasing avatar size", null);        MyAvatar.increaseSize();    }     // Change the avatar size to smaller.    function smallerSize() {        //print("smallerSize");        logMessage("Decreasing avatar size", null);        MyAvatar.decreaseSize();    }     // Set the avatar size to normal.    function normalSize() {        //print("normalSize");        logMessage("Resetting avatar size to normal!", null);        MyAvatar.resetSize();    }     // Send out a "Who" message, including our avatarID as myAvatarID,    // which will be sent in the response, so we can tell the reply    // is to our request.    function transmitWho() {        //print("transmitWho");        logMessage("Who is here?", null);        Messages.sendMessage(            channelName,            JSON.stringify({                type: 'Who',                myAvatarID: MyAvatar.sessionUUID            }));    }     // Send a reply to a "Who" message, with a friendly message,     // our avatarID and our displayName. myAvatarID is the id    // of the avatar who send the Who message, to whom we're    // responding.    function handleWho(myAvatarID) {        var avatarID = MyAvatar.sessionUUID;        if (myAvatarID == avatarID) {            // Don't reply to myself.            return;        }         var message = "I'm here!";        var data = {};         Messages.sendMessage(            channelName,            JSON.stringify({                type: 'ReplyWho',                myAvatarID: myAvatarID,                avatarID: avatarID,                displayName: chatName,                message: message,                data: data            }));    }     // Receive the reply to a "Who" message. Ignore it unless we were the one    // who sent it out (if myAvatarIS is our avatar's id).    function handleReplyWho(myAvatarID, avatarID, displayName, message, data) {        if (myAvatarID != MyAvatar.sessionUUID) {            return;        }         handleTransmitChatMessage(avatarID, displayName, message, data);    }     // Handle input form the user, possibly multiple lines separated by newlines.    // Each line may be a chat command starting with "/", or a chat message.    function handleChatMessage(message, data) {         var messageLines = message.trim().split('\n');         for (var i = 0, n = messageLines.length; i < n; i++) {            var messageLine = messageLines[i];             if (messageLine.substr(0, 1) == '/') {                handleChatCommand(messageLine, data);            } else {                transmitChatMessage(messageLine, data);            }        }     }     // Handle a chat command prefixed by "/".    function handleChatCommand(message, data) {         var commandLine = message.substr(1);        var tokens = commandLine.trim().split(' ');        var command = tokens[0];        var rest = commandLine.substr(command.length + 1).trim();         //print("commandLine", commandLine, "command", command, "tokens", tokens, "rest", rest);         switch (command) {             case '?':            case 'help':                logMessage('Type "/?" or "/help" for help', null);                logMessage('Type "/name <name>" to set your chat name, or "/name" to use your display name. If your display name is not defined, a random name will be used.', null);                logMessage('Type "/close" to close your overhead chat message.', null);                logMessage('Type "/say <something>" to display a new message.', null);

Lines 1–500 of 1019.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t