Advertisement
salahzar

chat.js

Aug 13th, 2018
278
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. "use strict";
  2.  
  3. // Chat.js
  4. // By Don Hopkins (dhopkins@donhopkins.com)
  5. //
  6. // Distributed under the Apache License, Version 2.0.
  7. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
  8. //
  9.  
  10. (function() {
  11.  
  12.     var webPageURL = Script.resolvePath("html/ChatPage.html"); // URL of tablet web page.
  13.     var randomizeWebPageURL = true; // Set to true for debugging.
  14.     var lastWebPageURL = ""; // Last random URL of tablet web page.
  15.     var onChatPage = false; // True when chat web page is opened.
  16.     var webHandlerConnected = false; // True when the web handler has been connected.
  17.     var channelName = "Chat"; // Unique name for channel that we listen to.
  18.     var tabletButtonName = "CHAT"; // Tablet button label.
  19.     var tabletButtonIcon = "icons/tablet-icons/menu-i.svg"; // Icon for chat button.
  20.     var tabletButtonActiveIcon = "icons/tablet-icons/menu-a.svg"; // Active icon for chat button.
  21.     var tabletButton = null; // The button we create in the tablet.
  22.     var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); // The awesome tablet.
  23.     var chatLog = []; // Array of chat messages in the form of [avatarID, displayName, message, data].
  24.     var avatarIdentifiers = {}; // Map of avatar ids to dict of identifierParams.
  25.     var speechBubbleShowing = false; // Is the speech bubble visible?
  26.     var speechBubbleMessage = null; // The message shown in the speech bubble.
  27.     var speechBubbleData = null; // The data of the speech bubble message.
  28.     var speechBubbleTextID = null; // The id of the speech bubble local text entity.
  29.     var speechBubbleTimer = null; // The timer to pop down the speech bubble.
  30.     var speechBubbleParams = null; // The params used to create or edit the speech bubble.
  31.  
  32.     // Persistent variables saved in the Settings.
  33.     var chatName = ''; // The user's name shown in chat.
  34.     var chatLogMaxSize = 100; // The maximum number of chat messages we remember.
  35.     var sendTyping = true; // Send typing begin and end notification.
  36.     var identifyAvatarDuration = 10; // How long to leave the avatar identity line up, in seconds.
  37.     var identifyAvatarLineColor = { red: 0, green: 255, blue: 0 }; // The color of the avatar identity line.
  38.     var identifyAvatarMyJointName = 'Head'; // My bone from which to draw the avatar identity line.
  39.     var identifyAvatarYourJointName = 'Head'; // Your bone to which to draw the avatar identity line.
  40.     var speechBubbleDuration = 10; // How long to leave the speech bubble up, in seconds.
  41.     var speechBubbleTextColor = {red: 255, green: 255, blue: 255}; // The text color of the speech bubble.
  42.     var speechBubbleBackgroundColor = {red: 0, green: 0, blue: 0}; // The background color of the speech bubble.
  43.     var speechBubbleOffset = {x: 0, y: 0.3, z: 0.0}; // The offset from the joint to whic the speech bubble is attached.
  44.     var speechBubbleJointName = 'Head'; // The name of the joint to which the speech bubble is attached.
  45.     var speechBubbleLineHeight = 0.05; // The height of a line of text in the speech bubble.
  46.     var SPEECH_BUBBLE_MAX_WIDTH = 1; // meters
  47.  
  48.     // Load the persistent variables from the Settings, with defaults.
  49.     function loadSettings() {
  50.         chatName = Settings.getValue('Chat_chatName', MyAvatar.displayName);
  51.         if (!chatName) {
  52.             chatName = randomAvatarName();
  53.         }
  54.         chatLogMaxSize = Settings.getValue('Chat_chatLogMaxSize', 100);
  55.         sendTyping = Settings.getValue('Chat_sendTyping', true);
  56.         identifyAvatarDuration = Settings.getValue('Chat_identifyAvatarDuration', 10);
  57.         identifyAvatarLineColor = Settings.getValue('Chat_identifyAvatarLineColor', { red: 0, green: 255, blue: 0 });
  58.         identifyAvatarMyJointName = Settings.getValue('Chat_identifyAvatarMyJointName', 'Head');
  59.         identifyAvatarYourJointName = Settings.getValue('Chat_identifyAvatarYourJointName', 'Head');
  60.         speechBubbleDuration = Settings.getValue('Chat_speechBubbleDuration', 10);
  61.         speechBubbleTextColor = Settings.getValue('Chat_speechBubbleTextColor', {red: 255, green: 255, blue: 255});
  62.         speechBubbleBackgroundColor = Settings.getValue('Chat_speechBubbleBackgroundColor', {red: 0, green: 0, blue: 0});
  63.         speechBubbleOffset = Settings.getValue('Chat_speechBubbleOffset', {x: 0.0, y: 0.3, z:0.0});
  64.         speechBubbleJointName = Settings.getValue('Chat_speechBubbleJointName', 'Head');
  65.         speechBubbleLineHeight = Settings.getValue('Chat_speechBubbleLineHeight', 0.05);
  66.  
  67.         saveSettings();
  68.     }
  69.  
  70.     // Save the persistent variables to the Settings.
  71.     function saveSettings() {
  72.         Settings.setValue('Chat_chatName', chatName);
  73.         Settings.setValue('Chat_chatLogMaxSize', chatLogMaxSize);
  74.         Settings.setValue('Chat_sendTyping', sendTyping);
  75.         Settings.setValue('Chat_identifyAvatarDuration', identifyAvatarDuration);
  76.         Settings.setValue('Chat_identifyAvatarLineColor', identifyAvatarLineColor);
  77.         Settings.setValue('Chat_identifyAvatarMyJointName', identifyAvatarMyJointName);
  78.         Settings.setValue('Chat_identifyAvatarYourJointName', identifyAvatarYourJointName);
  79.         Settings.setValue('Chat_speechBubbleDuration', speechBubbleDuration);
  80.         Settings.setValue('Chat_speechBubbleTextColor', speechBubbleTextColor);
  81.         Settings.setValue('Chat_speechBubbleBackgroundColor', speechBubbleBackgroundColor);
  82.         Settings.setValue('Chat_speechBubbleOffset', speechBubbleOffset);
  83.         Settings.setValue('Chat_speechBubbleJointName', speechBubbleJointName);
  84.         Settings.setValue('Chat_speechBubbleLineHeight', speechBubbleLineHeight);
  85.     }
  86.  
  87.     // Reset the Settings and persistent variables to the defaults.
  88.     function resetSettings() {
  89.         Settings.setValue('Chat_chatName', null);
  90.         Settings.setValue('Chat_chatLogMaxSize', null);
  91.         Settings.setValue('Chat_sendTyping', null);
  92.         Settings.setValue('Chat_identifyAvatarDuration', null);
  93.         Settings.setValue('Chat_identifyAvatarLineColor', null);
  94.         Settings.setValue('Chat_identifyAvatarMyJointName', null);
  95.         Settings.setValue('Chat_identifyAvatarYourJointName', null);
  96.         Settings.setValue('Chat_speechBubbleDuration', null);
  97.         Settings.setValue('Chat_speechBubbleTextColor', null);
  98.         Settings.setValue('Chat_speechBubbleBackgroundColor', null);
  99.         Settings.setValue('Chat_speechBubbleOffset', null);
  100.         Settings.setValue('Chat_speechBubbleJointName', null);
  101.         Settings.setValue('Chat_speechBubbleLineHeight', null);
  102.  
  103.         loadSettings();
  104.     }
  105.  
  106.     // Update anything that might depend on the settings.
  107.     function updateSettings() {
  108.         updateSpeechBubble();
  109.         trimChatLog();
  110.         updateChatPage();
  111.     }
  112.  
  113.     // Trim the chat log so it is no longer than chatLogMaxSize lines.
  114.     function trimChatLog() {
  115.         if (chatLog.length > chatLogMaxSize) {
  116.             chatLog.splice(0, chatLogMaxSize - chatLog.length);
  117.         }
  118.     }
  119.  
  120.     // Clear the local chat log.
  121.     function clearChatLog() {
  122.         //print("clearChatLog");
  123.         chatLog = [];
  124.         updateChatPage();
  125.     }
  126.  
  127.     // We got a chat message from the channel.
  128.     // Trim the chat log, save the latest message in the chat log,
  129.     // and show the message on the tablet, if the chat page is showing.
  130.     function handleTransmitChatMessage(avatarID, displayName, message, data) {
  131.         //print("receiveChat", "avatarID", avatarID, "displayName", displayName, "message", message, "data", data);
  132.  
  133.         trimChatLog();
  134.         chatLog.push([avatarID, displayName, message, data]);
  135.  
  136.         if (onChatPage) {
  137.             tablet.emitScriptEvent(
  138.                 JSON.stringify({
  139.                     type: "ReceiveChatMessage",
  140.                     avatarID: avatarID,
  141.                     displayName: displayName,
  142.                     message: message,
  143.                     data: data
  144.                 }));
  145.         }
  146.     }
  147.  
  148.     // Trim the chat log, save the latest log message in the chat log,
  149.     // and show the message on the tablet, if the chat page is showing.
  150.     function logMessage(message, data) {
  151.         //print("logMessage", message, data);
  152.  
  153.         trimChatLog();
  154.         chatLog.push([null, null, message, data]);
  155.  
  156.         if (onChatPage) {
  157.             tablet.emitScriptEvent(
  158.                 JSON.stringify({
  159.                     type: "LogMessage",
  160.                     message: message,
  161.                     data: data
  162.                 }));
  163.         }
  164.     }
  165.  
  166.     // An empty chat message was entered.
  167.     // Hide our speech bubble.
  168.     function emptyChatMessage(data) {
  169.         popDownSpeechBubble();
  170.     }
  171.  
  172.     // Notification that we typed a keystroke.
  173.     function type() {
  174.         //print("type");
  175.     }
  176.  
  177.     // Notification that we began typing.
  178.     // Notify everyone that we started typing.
  179.     function beginTyping() {
  180.         //print("beginTyping");
  181.         if (!sendTyping) {
  182.             return;
  183.         }
  184.  
  185.         Messages.sendMessage(
  186.             channelName,
  187.             JSON.stringify({
  188.                 type: 'AvatarBeginTyping',
  189.                 avatarID: MyAvatar.sessionUUID,
  190.                 displayName: chatName
  191.             }));
  192.     }
  193.  
  194.     // Notification that somebody started typing.
  195.     function handleAvatarBeginTyping(avatarID, displayName) {
  196.         //print("handleAvatarBeginTyping:", "avatarID", avatarID, displayName);
  197.     }
  198.  
  199.     // Notification that we stopped typing.
  200.     // Notify everyone that we stopped typing.
  201.     function endTyping() {
  202.         //print("endTyping");
  203.         if (!sendTyping) {
  204.             return;
  205.         }
  206.  
  207.         Messages.sendMessage(
  208.             channelName,
  209.             JSON.stringify({
  210.                 type: 'AvatarEndTyping',
  211.                 avatarID: MyAvatar.sessionUUID,
  212.                 displayName: chatName
  213.             }));
  214.     }
  215.  
  216.     // Notification that somebody stopped typing.
  217.     function handleAvatarEndTyping(avatarID, displayName) {
  218.         //print("handleAvatarEndTyping:", "avatarID", avatarID, displayName);
  219.     }
  220.  
  221.     // Identify an avatar by drawing a line from our head to their head.
  222.     // If the avatar is our own, then just draw a line up into the sky.
  223.     function identifyAvatar(yourAvatarID) {
  224.         //print("identifyAvatar", yourAvatarID);
  225.  
  226.         unidentifyAvatars();
  227.  
  228.         var myAvatarID = MyAvatar.sessionUUID;
  229.         var myJointIndex = MyAvatar.getJointIndex(identifyAvatarMyJointName);
  230.         var myJointRotation =
  231.             Quat.multiply(
  232.                 MyAvatar.orientation,
  233.                 MyAvatar.getAbsoluteJointRotationInObjectFrame(myJointIndex));
  234.         var myJointPosition =
  235.             Vec3.sum(
  236.                 MyAvatar.position,
  237.                 Vec3.multiplyQbyV(
  238.                     MyAvatar.orientation,
  239.                     MyAvatar.getAbsoluteJointTranslationInObjectFrame(myJointIndex)));
  240.  
  241.         var yourJointIndex = -1;
  242.         var yourJointPosition;
  243.  
  244.         if (yourAvatarID == myAvatarID) {
  245.  
  246.             // You pointed at your own name, so draw a line up from your head.
  247.  
  248.             yourJointPosition = {
  249.                 x: myJointPosition.x,
  250.                 y: myJointPosition.y + 1000.0,
  251.                 z: myJointPosition.z
  252.             };
  253.  
  254.         } else {
  255.  
  256.             // You pointed at somebody else's name, so draw a line from your head to their head.
  257.  
  258.             var yourAvatar = AvatarList.getAvatar(yourAvatarID);
  259.             if (!yourAvatar) {
  260.                 return;
  261.             }
  262.            
  263.             yourJointIndex = yourAvatar.getJointIndex(identifyAvatarMyJointName)
  264.  
  265.             var yourJointRotation =
  266.                 Quat.multiply(
  267.                     yourAvatar.orientation,
  268.                     yourAvatar.getAbsoluteJointRotationInObjectFrame(yourJointIndex));
  269.             yourJointPosition =
  270.                 Vec3.sum(
  271.                     yourAvatar.position,
  272.                     Vec3.multiplyQbyV(
  273.                         yourAvatar.orientation,
  274.                         yourAvatar.getAbsoluteJointTranslationInObjectFrame(yourJointIndex)));
  275.  
  276.         }
  277.  
  278.         var identifierParams = {
  279.             parentID: myAvatarID,
  280.             parentJointIndex: myJointIndex,
  281.             lifetime: identifyAvatarDuration,
  282.             start: myJointPosition,
  283.             endParentID: yourAvatarID,
  284.             endParentJointIndex: yourJointIndex,
  285.             end: yourJointPosition,
  286.             color: identifyAvatarLineColor,
  287.             alpha: 1
  288.         };
  289.  
  290.         avatarIdentifiers[yourAvatarID] = identifierParams;
  291.  
  292.         identifierParams.lineID = Overlays.addOverlay("line3d", identifierParams);
  293.  
  294.         //print("ADDOVERLAY lineID", lineID, "myJointPosition", JSON.stringify(myJointPosition), "yourJointPosition", JSON.stringify(yourJointPosition), "lineData", JSON.stringify(lineData));
  295.  
  296.         identifierParams.timer =
  297.             Script.setTimeout(function() {
  298.                 //print("DELETEOVERLAY lineID");
  299.                 unidentifyAvatar(yourAvatarID);
  300.             }, identifyAvatarDuration * 1000);
  301.  
  302.     }
  303.  
  304.     // Stop identifying an avatar.
  305.     function unidentifyAvatar(yourAvatarID) {
  306.         //print("unidentifyAvatar", yourAvatarID);
  307.  
  308.         var identifierParams = avatarIdentifiers[yourAvatarID];
  309.         if (!identifierParams) {
  310.             return;
  311.         }
  312.  
  313.         if (identifierParams.timer) {
  314.             Script.clearTimeout(identifierParams.timer);
  315.         }
  316.  
  317.         if (identifierParams.lineID) {
  318.             Overlays.deleteOverlay(identifierParams.lineID);
  319.         }
  320.  
  321.         delete avatarIdentifiers[yourAvatarID];
  322.     }
  323.  
  324.     // Stop identifying all avatars.
  325.     function unidentifyAvatars() {
  326.         var ids = [];
  327.  
  328.         for (var avatarID in avatarIdentifiers) {
  329.             ids.push(avatarID);
  330.         }
  331.  
  332.         for (var i = 0, n = ids.length; i < n; i++) {
  333.             var avatarID = ids[i];
  334.             unidentifyAvatar(avatarID);
  335.         }
  336.  
  337.     }
  338.  
  339.     // Turn to face another avatar.
  340.     function faceAvatar(yourAvatarID, displayName) {
  341.         //print("faceAvatar:", yourAvatarID, displayName);
  342.  
  343.         var myAvatarID = MyAvatar.sessionUUID;
  344.         if (yourAvatarID == myAvatarID) {
  345.             // You clicked on yourself.
  346.             return;
  347.         }
  348.  
  349.         var yourAvatar = AvatarList.getAvatar(yourAvatarID);
  350.         if (!yourAvatar) {
  351.             logMessage(displayName + ' is not here!', null);
  352.             return;
  353.         }
  354.  
  355.         // Project avatar positions to the floor and get the direction between those points,
  356.         // then face my avatar towards your avatar.
  357.         var yourPosition = yourAvatar.position;
  358.         yourPosition.y = 0;
  359.         var myPosition = MyAvatar.position;
  360.         myPosition.y = 0;
  361.         var myOrientation = Quat.lookAtSimple(myPosition, yourPosition);
  362.         MyAvatar.orientation = myOrientation;
  363.     }
  364.  
  365.     // Make a hopefully unique random anonymous avatar name.
  366.     function randomAvatarName() {
  367.         return 'Anon_' + Math.floor(Math.random() * 1000000);
  368.     }
  369.  
  370.     // Change the avatar size to bigger.
  371.     function biggerSize() {
  372.         //print("biggerSize");
  373.         logMessage("Increasing avatar size", null);
  374.         MyAvatar.increaseSize();
  375.     }
  376.  
  377.     // Change the avatar size to smaller.
  378.     function smallerSize() {
  379.         //print("smallerSize");
  380.         logMessage("Decreasing avatar size", null);
  381.         MyAvatar.decreaseSize();
  382.     }
  383.  
  384.     // Set the avatar size to normal.
  385.     function normalSize() {
  386.         //print("normalSize");
  387.         logMessage("Resetting avatar size to normal!", null);
  388.         MyAvatar.resetSize();
  389.     }
  390.  
  391.     // Send out a "Who" message, including our avatarID as myAvatarID,
  392.     // which will be sent in the response, so we can tell the reply
  393.     // is to our request.
  394.     function transmitWho() {
  395.         //print("transmitWho");
  396.         logMessage("Who is here?", null);
  397.         Messages.sendMessage(
  398.             channelName,
  399.             JSON.stringify({
  400.                 type: 'Who',
  401.                 myAvatarID: MyAvatar.sessionUUID
  402.             }));
  403.     }
  404.  
  405.     // Send a reply to a "Who" message, with a friendly message,
  406.     // our avatarID and our displayName. myAvatarID is the id
  407.     // of the avatar who send the Who message, to whom we're
  408.     // responding.
  409.     function handleWho(myAvatarID) {
  410.         var avatarID = MyAvatar.sessionUUID;
  411.         if (myAvatarID == avatarID) {
  412.             // Don't reply to myself.
  413.             return;
  414.         }
  415.  
  416.         var message = "I'm here!";
  417.         var data = {};
  418.  
  419.         Messages.sendMessage(
  420.             channelName,
  421.             JSON.stringify({
  422.                 type: 'ReplyWho',
  423.                 myAvatarID: myAvatarID,
  424.                 avatarID: avatarID,
  425.                 displayName: chatName,
  426.                 message: message,
  427.                 data: data
  428.             }));
  429.     }
  430.  
  431.     // Receive the reply to a "Who" message. Ignore it unless we were the one
  432.     // who sent it out (if myAvatarIS is our avatar's id).
  433.     function handleReplyWho(myAvatarID, avatarID, displayName, message, data) {
  434.         if (myAvatarID != MyAvatar.sessionUUID) {
  435.             return;
  436.         }
  437.  
  438.         handleTransmitChatMessage(avatarID, displayName, message, data);
  439.     }
  440.  
  441.     // Handle input form the user, possibly multiple lines separated by newlines.
  442.     // Each line may be a chat command starting with "/", or a chat message.
  443.     function handleChatMessage(message, data) {
  444.  
  445.         var messageLines = message.trim().split('\n');
  446.  
  447.         for (var i = 0, n = messageLines.length; i < n; i++) {
  448.             var messageLine = messageLines[i];
  449.  
  450.             if (messageLine.substr(0, 1) == '/') {
  451.                 handleChatCommand(messageLine, data);
  452.             } else {
  453.                 transmitChatMessage(messageLine, data);
  454.             }
  455.         }
  456.  
  457.     }
  458.  
  459.     // Handle a chat command prefixed by "/".
  460.     function handleChatCommand(message, data) {
  461.  
  462.         var commandLine = message.substr(1);
  463.         var tokens = commandLine.trim().split(' ');
  464.         var command = tokens[0];
  465.         var rest = commandLine.substr(command.length + 1).trim();
  466.  
  467.         //print("commandLine", commandLine, "command", command, "tokens", tokens, "rest", rest);
  468.  
  469.         switch (command) {
  470.  
  471.             case '?':
  472.             case 'help':
  473.                 logMessage('Type "/?" or "/help" for help', null);
  474.                 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);
  475.                 logMessage('Type "/close" to close your overhead chat message.', null);
  476.                 logMessage('Type "/say <something>" to display a new message.', null);
  477.                 logMessage('Type "/clear" to clear your chat log.', null);
  478.                 logMessage('Type "/who" to ask who is in the chat session.', null);
  479.                 logMessage('Type "/bigger", "/smaller" or "/normal" to change your avatar size.', null);
  480.                 break;
  481.  
  482.             case 'name':
  483.                 if (rest == '') {
  484.                     if (MyAvatar.displayName) {
  485.                         chatName = MyAvatar.displayName;
  486.                         saveSettings();
  487.                         logMessage('Your chat name has been set to your display name "' + chatName + '".', null);
  488.                     } else {
  489.                         chatName = randomAvatarName();
  490.                         saveSettings();
  491.                         logMessage('Your avatar\'s display name is not defined, so your chat name has been set to "' + chatName + '".', null);
  492.                     }
  493.                 } else {
  494.                     chatName = rest;
  495.                     saveSettings();
  496.                     logMessage('Your chat name has been set to "' + chatName + '".', null);
  497.                 }
  498.                 break;
  499.  
  500.             case 'close':
  501.                 popDownSpeechBubble();
  502.                 logMessage('Overhead chat message closed.', null);
  503.                 break;
  504.  
  505.             case 'say':
  506.                 if (rest == '') {
  507.                     emptyChatMessage(data);
  508.                 } else {
  509.                     transmitChatMessage(rest, data);
  510.                 }
  511.                 break;
  512.  
  513.             case 'who':
  514.                 transmitWho();
  515.                 break;
  516.  
  517.             case 'clear':
  518.                 clearChatLog();
  519.                 break;
  520.  
  521.             case 'bigger':
  522.                 biggerSize();
  523.                 break;
  524.  
  525.             case 'smaller':
  526.                 smallerSize();
  527.                 break;
  528.  
  529.             case 'normal':
  530.                 normalSize();
  531.                 break;
  532.  
  533.             case 'resetsettings':
  534.                 resetSettings();
  535.                 updateSettings();
  536.                 break;
  537.  
  538.             case 'speechbubbleheight':
  539.                 var y = parseInt(rest);
  540.                 if (!isNaN(y)) {
  541.                     speechBubbleOffset.y = y;
  542.                 }
  543.                 saveSettings();
  544.                 updateSettings();
  545.                 break;
  546.  
  547.             case 'speechbubbleduration':
  548.                 var duration = parseFloat(rest);
  549.                 if (!isNaN(duration)) {
  550.                     speechBubbleDuration = duration;
  551.                 }
  552.                 saveSettings();
  553.                 updateSettings();
  554.                 break;
  555.  
  556.             default:
  557.                 logMessage('Unknown chat command. Type "/help" or "/?" for help.', null);
  558.                 break;
  559.  
  560.         }
  561.  
  562.     }
  563.  
  564.     // Send out a chat message to everyone.
  565.     function transmitChatMessage(message, data) {
  566.         //print("transmitChatMessage", 'avatarID', avatarID, 'displayName', displayName, 'message', message, 'data', data);
  567.  
  568.         popUpSpeechBubble(message, data);
  569.  
  570.         Messages.sendMessage(
  571.             channelName,
  572.             JSON.stringify({
  573.                 type: 'TransmitChatMessage',
  574.                 avatarID: MyAvatar.sessionUUID,
  575.                 displayName: chatName,
  576.                 message: message,
  577.                 data: data
  578.             }));
  579.  
  580.     }
  581.  
  582.     // Show the speech bubble.
  583.     function popUpSpeechBubble(message, data) {
  584.         //print("popUpSpeechBubble", message, data);
  585.  
  586.         popDownSpeechBubble();
  587.  
  588.         speechBubbleShowing = true;
  589.         speechBubbleMessage = message;
  590.         speechBubbleData = data;
  591.  
  592.         updateSpeechBubble();
  593.  
  594.         if (speechBubbleDuration > 0) {
  595.             speechBubbleTimer = Script.setTimeout(
  596.                 function () {
  597.                     popDownSpeechBubble();
  598.                 },
  599.                 speechBubbleDuration * 1000);
  600.         }
  601.     }
  602.  
  603.     // Update the speech bubble.
  604.     // This is factored out so we can update an existing speech bubble if any settings change.
  605.     function updateSpeechBubble() {
  606.         if (!speechBubbleShowing) {
  607.             return;
  608.         }
  609.  
  610.         var jointIndex = MyAvatar.getJointIndex(speechBubbleJointName);
  611.         var dimensions = {
  612.             x: 100.0,
  613.             y: 100.0,
  614.             z: 0.1
  615.         };
  616.  
  617.         speechBubbleParams = {
  618.             type: "Text",
  619.             lifetime: speechBubbleDuration,
  620.             parentID: MyAvatar.sessionUUID,
  621.             jointIndex: jointIndex,
  622.             dimensions: dimensions,
  623.             lineHeight: speechBubbleLineHeight,
  624.             leftMargin: 0,
  625.             topMargin: 0,
  626.             rightMargin: 0,
  627.             bottomMargin: 0,
  628.             faceCamera: true,
  629.             drawInFront: true,
  630.             ignoreRayIntersection: true,
  631.             text: speechBubbleMessage,
  632.             textColor: speechBubbleTextColor,
  633.             color: speechBubbleTextColor,
  634.             backgroundColor: speechBubbleBackgroundColor
  635.         };
  636.  
  637.         // Only overlay text3d has a way to measure the text, not entities.
  638.         // So we make a temporary one just for measuring text, then delete it.
  639.         var speechBubbleTextOverlayID = Overlays.addOverlay("text3d", speechBubbleParams);
  640.         var textSize = Overlays.textSize(speechBubbleTextOverlayID, speechBubbleMessage);
  641.         try {
  642.             Overlays.deleteOverlay(speechBubbleTextOverlayID);
  643.         } catch (e) {}
  644.  
  645.         //print("updateSpeechBubble:", "speechBubbleMessage", speechBubbleMessage, "textSize", textSize.width, textSize.height);
  646.  
  647.         var fudge = 0.02;
  648.  
  649.         var width = textSize.width + fudge;
  650.         var height = speechBubbleLineHeight + fudge;
  651.  
  652.         if (textSize.width >= SPEECH_BUBBLE_MAX_WIDTH) {
  653.             var numLines = Math.ceil(width);
  654.             height = speechBubbleLineHeight * numLines + fudge;
  655.             width = SPEECH_BUBBLE_MAX_WIDTH;
  656.         }
  657.  
  658.         dimensions = {
  659.             x: width,
  660.             y: height,
  661.             z: 0.1
  662.         };
  663.         speechBubbleParams.dimensions = dimensions;
  664.  
  665.         var headRotation =
  666.             Quat.multiply(
  667.                 MyAvatar.orientation,
  668.                 MyAvatar.getAbsoluteJointRotationInObjectFrame(jointIndex));
  669.         var headPosition =
  670.             Vec3.sum(
  671.                 MyAvatar.position,
  672.                 Vec3.multiplyQbyV(
  673.                     MyAvatar.orientation,
  674.                     MyAvatar.getAbsoluteJointTranslationInObjectFrame(jointIndex)));
  675.         var rotatedOffset =
  676.             Vec3.multiplyQbyV(
  677.                 headRotation,
  678.                 speechBubbleOffset);
  679.         var position =
  680.             Vec3.sum(
  681.                 headPosition,
  682.                 rotatedOffset);
  683.         position.y += height / 2; // offset based on half of bubble height
  684.         speechBubbleParams.position = position;
  685.  
  686.         if (!speechBubbleTextID) {
  687.             speechBubbleTextID =
  688.                 Entities.addEntity(speechBubbleParams, true);
  689.         } else {
  690.             Entities.editEntity(speechBubbleTextID, speechBubbleParams);
  691.         }
  692.  
  693.         //print("speechBubbleTextID:", speechBubbleTextID, "speechBubbleParams", JSON.stringify(speechBubbleParams));
  694.     }
  695.  
  696.     // Hide the speech bubble.
  697.     function popDownSpeechBubble() {
  698.         cancelSpeechBubbleTimer();
  699.  
  700.         speechBubbleShowing = false;
  701.  
  702.         //print("popDownSpeechBubble speechBubbleTextID", speechBubbleTextID);
  703.  
  704.         if (speechBubbleTextID) {
  705.             try {
  706.                 Entities.deleteEntity(speechBubbleTextID);
  707.             } catch (e) {}
  708.             speechBubbleTextID = null;
  709.         }
  710.     }
  711.  
  712.     // Cancel the speech bubble popup timer.
  713.     function cancelSpeechBubbleTimer() {
  714.         if (speechBubbleTimer) {
  715.             Script.clearTimeout(speechBubbleTimer);
  716.             speechBubbleTimer = null;
  717.         }
  718.     }
  719.  
  720.     // Show the tablet web page and connect the web handler.
  721.     function showTabletWebPage() {
  722.         var url = Script.resolvePath(webPageURL);
  723.         if (randomizeWebPageURL) {
  724.             url += '?rand=' + Math.random();
  725.         }
  726.         lastWebPageURL = url;
  727.         onChatPage = true;
  728.         tablet.gotoWebScreen(lastWebPageURL);
  729.         // Connect immediately so we don't miss anything.
  730.         connectWebHandler();
  731.     }
  732.  
  733.     // Update the tablet web page with the chat log.
  734.     function updateChatPage() {
  735.         if (!onChatPage) {
  736.             return;
  737.         }
  738.  
  739.         tablet.emitScriptEvent(
  740.             JSON.stringify({
  741.                 type: "Update",
  742.                 chatLog: chatLog
  743.             }));
  744.     }
  745.  
  746.     function onChatMessageReceived(channel, message, senderID) {
  747.  
  748.         // Ignore messages to any other channel than mine.
  749.         if (channel != channelName) {
  750.             return;
  751.         }
  752.  
  753.         // Parse the message and pull out the message parameters.
  754.         var messageData = JSON.parse(message);
  755.         var messageType = messageData.type;
  756.  
  757.         //print("MESSAGE", message);
  758.         //print("MESSAGEDATA", messageData, JSON.stringify(messageData));
  759.  
  760.         switch (messageType) {
  761.  
  762.             case 'TransmitChatMessage':
  763.                 handleTransmitChatMessage(messageData.avatarID, messageData.displayName, messageData.message, messageData.data);
  764.                 break;
  765.  
  766.             case 'AvatarBeginTyping':
  767.                 handleAvatarBeginTyping(messageData.avatarID, messageData.displayName);
  768.                 break;
  769.  
  770.             case 'AvatarEndTyping':
  771.                 handleAvatarEndTyping(messageData.avatarID, messageData.displayName);
  772.                 break;
  773.  
  774.             case 'Who':
  775.                 handleWho(messageData.myAvatarID);
  776.                 break;
  777.  
  778.             case 'ReplyWho':
  779.                 handleReplyWho(messageData.myAvatarID, messageData.avatarID, messageData.displayName, messageData.message, messageData.data);
  780.                 break;
  781.  
  782.             default:
  783.                 print("onChatMessageReceived: unknown messageType", messageType, "message", message);
  784.                 break;
  785.  
  786.         }
  787.  
  788.     }
  789.  
  790.     // Handle events from the tablet web page.
  791.     function onWebEventReceived(event) {
  792.         if (!onChatPage) {
  793.             return;
  794.         }
  795.  
  796.         //print("onWebEventReceived: event", event);
  797.  
  798.         var eventData = JSON.parse(event);
  799.         var eventType = eventData.type;
  800.  
  801.         switch (eventType) {
  802.  
  803.             case 'Ready':
  804.                 updateChatPage();
  805.                 break;
  806.  
  807.             case 'Update':
  808.                 updateChatPage();
  809.                 break;
  810.  
  811.             case 'HandleChatMessage':
  812.                 var message = eventData.message;
  813.                 var data = eventData.data;
  814.                 //print("onWebEventReceived: HandleChatMessage:", 'message', message, 'data', data);
  815.                 handleChatMessage(message, data);
  816.                 break;
  817.  
  818.             case 'PopDownSpeechBubble':
  819.                 popDownSpeechBubble();
  820.                 break;
  821.  
  822.             case 'EmptyChatMessage':
  823.                 emptyChatMessage();
  824.                 break;
  825.  
  826.             case 'Type':
  827.                 type();
  828.                 break;
  829.  
  830.             case 'BeginTyping':
  831.                 beginTyping();
  832.                 break;
  833.  
  834.             case 'EndTyping':
  835.                 endTyping();
  836.                 break;
  837.  
  838.             case 'IdentifyAvatar':
  839.                 identifyAvatar(eventData.avatarID);
  840.                 break;
  841.  
  842.             case 'UnidentifyAvatar':
  843.                 unidentifyAvatar(eventData.avatarID);
  844.                 break;
  845.  
  846.             case 'FaceAvatar':
  847.                 faceAvatar(eventData.avatarID, eventData.displayName);
  848.                 break;
  849.  
  850.             case 'ClearChatLog':
  851.                 clearChatLog();
  852.                 break;
  853.  
  854.             case 'Who':
  855.                 transmitWho();
  856.                 break;
  857.  
  858.             case 'Bigger':
  859.                 biggerSize();
  860.                 break;
  861.  
  862.             case 'Smaller':
  863.                 smallerSize();
  864.                 break;
  865.  
  866.             case 'Normal':
  867.                 normalSize();
  868.                 break;
  869.  
  870.             default:
  871.                 print("onWebEventReceived: unexpected eventType", eventType);
  872.                 break;
  873.  
  874.         }
  875.     }
  876.  
  877.     function onScreenChanged(type, url) {
  878.         //print("onScreenChanged", "type", type, "url", url, "lastWebPageURL", lastWebPageURL);
  879.    
  880.         if ((type === "Web") &&
  881.             (url === lastWebPageURL)) {
  882.             if (!onChatPage) {
  883.                 onChatPage = true;
  884.                 connectWebHandler();
  885.             }
  886.         } else {
  887.             if (onChatPage) {
  888.                 onChatPage = false;
  889.                 disconnectWebHandler();
  890.             }
  891.         }
  892.  
  893.     }
  894.  
  895.     function connectWebHandler() {
  896.         if (webHandlerConnected) {
  897.             return;
  898.         }
  899.  
  900.         try {
  901.             tablet.webEventReceived.connect(onWebEventReceived);
  902.         } catch (e) {
  903.             print("connectWebHandler: error connecting: " + e);
  904.             return;
  905.         }
  906.  
  907.         webHandlerConnected = true;
  908.         //print("connectWebHandler connected");
  909.  
  910.         updateChatPage();
  911.     }
  912.  
  913.     function disconnectWebHandler() {
  914.         if (!webHandlerConnected) {
  915.             return;
  916.         }
  917.  
  918.         try {
  919.             tablet.webEventReceived.disconnect(onWebEventReceived);
  920.         } catch (e) {
  921.             print("disconnectWebHandler: error disconnecting web handler: " + e);
  922.             return;
  923.         }
  924.         webHandlerConnected = false;
  925.  
  926.         //print("disconnectWebHandler: disconnected");
  927.     }
  928.  
  929.     // Show the tablet web page when the chat button on the tablet is clicked.
  930.     function onTabletButtonClicked() {
  931.         showTabletWebPage();
  932.     }
  933.  
  934.     // Shut down the chat application when the tablet button is destroyed.
  935.     function onTabletButtonDestroyed() {
  936.         shutDown();
  937.     }
  938.  
  939.     // Start up the chat application.
  940.     function startUp() {
  941.         //print("startUp");
  942.  
  943.         loadSettings();
  944.  
  945.         tabletButton = tablet.addButton({
  946.             icon: tabletButtonIcon,
  947.             activeIcon: tabletButtonActiveIcon,
  948.             text: tabletButtonName
  949.         });
  950.  
  951.         Messages.subscribe(channelName);
  952.  
  953.         tablet.screenChanged.connect(onScreenChanged);
  954.  
  955.         Messages.messageReceived.connect(onChatMessageReceived);
  956.  
  957.         tabletButton.clicked.connect(onTabletButtonClicked);
  958.  
  959.         Script.scriptEnding.connect(onTabletButtonDestroyed);
  960.  
  961.         logMessage('Type "/?" or "/help" for help with chat.', null);
  962.  
  963.         //print("Added chat button to tablet.");
  964.     }
  965.  
  966.     // Shut down the chat application.
  967.     function shutDown() {
  968.         //print("shutDown");
  969.  
  970.         popDownSpeechBubble();
  971.         unidentifyAvatars();
  972.         disconnectWebHandler();
  973.  
  974.         if (onChatPage) {
  975.             tablet.gotoHomeScreen();
  976.             onChatPage = false;
  977.         }
  978.  
  979.         tablet.screenChanged.disconnect(onScreenChanged);
  980.  
  981.         Messages.messageReceived.disconnect(onChatMessageReceived);
  982.  
  983.         // Clean up the tablet button we made.
  984.         tabletButton.clicked.disconnect(onTabletButtonClicked);
  985.         tablet.removeButton(tabletButton);
  986.         tabletButton = null;
  987.  
  988.         //print("Removed chat button from tablet.");
  989.     }
  990.  
  991.     // Kick off the chat application!
  992.     startUp();
  993.  
  994. }());
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement