HTML HUD Demo

=TODO:=
* Suggest a Stop All Animations button be added to Animations selection frame for convenience.
=HTML HUD Demo:=
== Screenshot: ==
==Tip:==
==Source code:==
// HTML-based, single script HUD
//
// original by Kelly Linden
//
// To use:
// - create a default prim (cube)
// - wear it as a HUD on top_left (script needs tweaking for other attachment points)
// - edit the cube while wearing
// - add animations you want to use
// - add notecards and objects you want to hand out
// - add this script
//
// License:
// This script itself is free to share, modify and use without restriction.
// Any linked or referenced files are not included in this license and are
// licensed by their respective owners under their own respective copyright
// and other licenses.
key owner;
string ownerName;
integer scope;
string url;
key currentRequestID;
integer responseStatus;
string responseBody;
list lastPath;
string video_url;
string header;
string footer;
string currentAnimation;
integer isVisible;
string exceptions;
// user-function: init
// - does not return anything
// - sets initial variable values
// - sets object's name and textures
// - request a url to use the HUD
init()
{
 owner = llGetOwner();
 ownerName = llKey2Name(owner);
 llSetObjectName("HTML HUD");
 scope = AGENT_LIST_PARCEL;
 video_url = "http://www.youtube.com/embed/m7p9IEpPu-c?rel=0";
// header set in set_link_media(url)
// footer set in set_link_media(url)
 float FLOAT_FALSE = 0.0;
 vector RATIO_ONE_BY_ONE = <0.98, 0.98, 0.00>;// fix Second Life ... or try to!
 llSetLinkPrimitiveParamsFast(LINK_THIS, [
 PRIM_TEXTURE, ALL_SIDES, TEXTURE_BLANK, RATIO_ONE_BY_ONE, ZERO_VECTOR, FLOAT_FALSE,
 PRIM_TEXTURE, 2, "0b815b79-c8f5-fc98-91fc-e77b53a468e2", RATIO_ONE_BY_ONE, ZERO_VECTOR, FLOAT_FALSE]);
 toggle_visibility_of_HUD_button();
 request_secure_url();
}
// user-function: toggle_visibility_of_HUD_button
// - does not return anything
// - toggle the visibility of the prim
// - will rotate, position and scale the prim
toggle_visibility_of_HUD_button()
{
 if (isVisible)
 {
 llSetLinkPrimitiveParamsFast(LINK_THIS, [
 PRIM_POS_LOCAL, <0.0, -0.13, -0.13>,
 PRIM_ROT_LOCAL, <0.0, 0.0, 0.0, 1.0>,
 PRIM_SIZE, <0.01, 0.25, 0.25>]);
 }
 else
 {
 llSetLinkPrimitiveParamsFast(LINK_THIS, [
 PRIM_POS_LOCAL, <0.0, -0.04, -0.04>,
 PRIM_ROT_LOCAL, <0.0, 0.0, -1.0, 0.0>,
 PRIM_SIZE, <0.05, 0.05, 0.05>]);
 }
 isVisible = !isVisible;
}
// user-function: drop and clear the old url
// - does not return anything
release_url()
{
 llReleaseURL(url);
 url = "";
}
// user-function: request secure url
// - does not return anything
// - make sure we release the old url before requesting a new one
request_secure_url()
{
 release_url();
 currentRequestID = llRequestSecureURL();
}
// user-function: set_link_media
// - does not return anything
// - set the values for the string variables 'header' and 'footer'
// - prepare face 4 for media on a prim
set_link_media(string scriptUrl)
{
 url = scriptUrl;
 header = "<html><head><link href='https://d1979ns0fqtj19.cloudfront.net/"
 + "assets/common-103828347986224535963905120979424958961.css'"
 + " media='all' rel='stylesheet' type='text/css' />"
 + "<base href='" + scriptUrl + "/' /></head><body>";
 footer = ""
 + "<a href=>Scan</a> | <a href='anims'>Anims</a> | <a href='video'>"
 + "Video</a> | <a href='config'>Config</a> | <a href='hide'>Hide</a>"
 + "<script src='https://d2mjw3k7q9u8rb.cloudfront.net/assets/"
 + "common-170919042270376442559931151451605602726.js' type='text/"
 + "javascript'></script></body></html>";
 llSetLinkMedia(LINK_THIS, 4, [
 PRIM_MEDIA_AUTO_PLAY, TRUE,
 PRIM_MEDIA_CURRENT_URL, url,
 PRIM_MEDIA_HOME_URL, url,
 PRIM_MEDIA_HEIGHT_PIXELS, 256,
 PRIM_MEDIA_WIDTH_PIXELS, 256,
 PRIM_MEDIA_PERMS_CONTROL, PRIM_MEDIA_PERM_NONE]);
}
// user-function:
// - does not return anything
// - used if there's not a request to the homepage of our website
// - check where on the site we are and prepare variables for the response
http_get(key requestID, list path)
{
 currentRequestID = requestID;
 integer numOfPathsParts = llGetListLength(path);
 string firstPathPart = llList2String(path, 0);
 if (firstPathPart == "hide")
 {
 toggle_visibility_of_HUD_button();
 http_get(requestID, lastPath);
 return;
 }
 lastPath = path;
 if (firstPathPart == "agent")
 {
 if (numOfPathsParts == 1)
 {
 prepare_profile_overview_page(requestID, owner);
 return;
 }
 else if (numOfPathsParts == 2)
 {
 key id = (key)llList2String(path, 1);
 prepare_profile_overview_page(requestID, id);
 return;
 }
 else if (numOfPathsParts == 4)
 {
 if (llList2String(path, 2) != "give")
 {
 return;
 }
 key id = (key)llList2String(path, 1);
 string name = llKey2Name(id);
 string itemName = llUnescapeURL(llList2String(path, 3));
 llOwnerSay("Giving '" + itemName + "' to '" + name + "'.");
 llGiveInventory(id, itemName);
 prepare_profile_overview_page(requestID, id);
 return;
 }
 }
 else if (firstPathPart == "anims")
 {
 if (numOfPathsParts == 1)
 {
 anims_page();
 return;
 }
 else if (numOfPathsParts == 2)
 {
 play_anim(llList2String(path, 1));
 anims_page();
 return;
 }
 }
 else if (firstPathPart == "video")
 {
 responseStatus = 200;
 responseBody = header + "<iframe width='255' height='173' src='"
 + video_url + "' frameborder='0' allowfullscreen></iframe>"
 + footer;
 return;
 }
 else if (firstPathPart == "config")
 {
 if (numOfPathsParts == 1)
 {
 config_page();
 return;
 }
 else if (llList2String(path, 1) == "set")
 {
 string queryString = llGetHTTPHeader(requestID, "x-query-string");
 list args = llParseString2List(queryString, ["="], ["&"]);
 integer index = -llGetListLength(args);
 while (index)
 {
 string variable = llList2String(args, index);
 string value = llUnescapeURL(llList2String(args, index + 1));
 if (variable == "video")
 {
 video_url = value;
 }
// because: var, val, &, var, val, &, ...
 index += 3;
 }
 config_page();
 }
 }
 responseStatus = 404;
 responseBody = header + "404 Page Not Found." + footer;
 throw_exception("There has been a HTTP-request to a non-existant page on "
 + "your HUD's website. Please check the path of the request "
 + "for mistakes.");
}
// user-function: prepare_profile_overview_page
// - does not return anything
// - prepares a response for a page with information about a certain avatar
// - includes profile thumbnail, name, script info, give menu
prepare_profile_overview_page(key requestID, key id)
{
 list avatarDetails = llGetObjectDetails(id, [
 OBJECT_POS, OBJECT_TOTAL_SCRIPT_COUNT,
 OBJECT_SCRIPT_MEMORY, OBJECT_SCRIPT_TIME]);
 responseStatus = 200;
 responseBody = header + ""
 + ""
 + html_body_with_formatted_avatar_name(id) + "" + llGetUsername(id) + ""
 + ""
 + html_body_with_links_for_interaction_with_certain_avatar(id)
 + "" + html_body_with_inventory_overview_for_give_menu(id)
 + ""
 + html_body_avatar_profile_pic_thumbnail(id)
 + ""
 + "Scripts:" + (string)llList2Integer(avatarDetails, 1)
 + " total" + bytes2str(llList2Integer(avatarDetails, 2))
 + ""
 + (string)((integer)(llList2Float(avatarDetails, 3) * 1000000.0))
 + "us" + footer;
}
// user-function: html_body_with_formatted_avatar_name
// - returns the name of the avatar in html format
// - removes the lastname if it is Resident
// - adds a line-break for long names
string html_body_with_formatted_avatar_name(key id)
{
 string stringToReturn = llKey2Name(id);
 if (llGetSubString(stringToReturn, -9, -1) == " Resident")
 {
 stringToReturn = llDeleteSubString(stringToReturn, -9, -1);
 }
 if (15 < llStringLength(stringToReturn))
 {
 stringToReturn = llDumpList2String(
 llParseString2List(stringToReturn,[" "], []),
 ""
 );
 }
 return stringToReturn;
}
// user-function: html_body_with_links_for_interaction_with_certain_avatar
// - returns html text with links for an avatar to interact with who has a certain uuid
string html_body_with_links_for_interaction_with_certain_avatar(key id)
{
 return "<a class='button call_to_action'>Options"
 + "</a>"
 + "<a href='secondlife:///app/agent/" + (string)id + "/im'>IM</a>"
 + "<a href='secondlife:///app/agent/" + (string)id
 + "/offerteleport'>Offer Teleport</a><a href='secondlife:///"
 + "app/maptrackavatar/" + (string)id + "'>Map</a><a href='"
 + "secondlife:///app/sharewithavatar/" + (string)id + "'>Share</a>"
 + "<a href='secondlife:///app/agent/" + (string)id + "/pay'>Pay</a>"
 + "";
}
// user-function: html_body_with_inventory_overview_for_give_menu
// - returns html text with inventory item lists
string html_body_with_inventory_overview_for_give_menu(key id)
{
 string stringToReturn = "<a class='button "
 + "call_to_action'>Give</a>";
 integer sizeOfStringBefore = llStringLength(stringToReturn);
 stringToReturn += inventory_list_in_html_format_for_give_menu(id, INVENTORY_NOTECARD);
 stringToReturn += inventory_list_in_html_format_for_give_menu(id, INVENTORY_OBJECT);
 if (llStringLength(stringToReturn) == sizeOfStringBefore)
 {
 stringToReturn += "(no objects or notecards found)";
 }
 stringToReturn += "";
 return stringToReturn;
}
// user-function: bytes2str
// - returns a string with script memory info in readable format
string bytes2str(integer bytes)
{
// 1024² = 1048576
 if (bytes < 1048576)
 return (string)(bytes / 1024) + " KB";
// else
 return (string)(bytes / 1048576) + " MB";
}
// user-function: html_body_avatar_profile_pic_thumbnail
// - returns html text with urls to someone's profile pic
string html_body_avatar_profile_pic_thumbnail(key id)
{
 return "<a href='secondlife:///app/agent/" + (string)id + "/about' class='"
 + "avatar avatar_thumb' rel='#sl_image_zoom' title='Click to zoom'"
 + "<img alt='Thumb_sl_image' src='https://my-secondlife.s3.amazonaws"
 + ".com/users/" + llGetUsername(id) + "/sl_image.png' /></a>";
}
// user-function: inventory_list_in_html_format_for_give_menu
// - returns html text with inventory item list of given type
string inventory_list_in_html_format_for_give_menu(key id, integer type)
{
 string stringToReturn;
 integer index = llGetInventoryNumber(type);
 while (index)
 {
 --index;
 string name = llGetInventoryName(type, index);
 stringToReturn += "<a href='agent/" + (string)id + "/give/" + name
 + "'>" + name + "</a>";
 }
 return stringToReturn;
}
// user-function: anims_page
// - does not return anything
// - prepares an html text overview page of animations
anims_page()
{
 responseStatus = 200;
 responseBody = header + "AnimationsChoose an animation:"
 + ""
 + html_body_animations_overview() + "" + footer;
}
// user-function: html_body_animations_overview
// - returns html text with a list of included animations
string html_body_animations_overview()
{
 string stringToReturn = ""
 + "<a class='button call_to_action'>Animate </a>";
 integer index = llGetInventoryNumber(INVENTORY_ANIMATION);
 if (!index)
 {
 stringToReturn += "(no animations found)";
 }
 else while (index)
 {
 --index;
 string name = llGetInventoryName(INVENTORY_ANIMATION, index);
 stringToReturn += "<a href='anims/" + name + "'>" + name + "</a>";
 }
 stringToReturn += "";
 return stringToReturn;
}
// user-function: play_anim
// - does not return anything
// - prompts a perms request to animate owner
play_anim(string anim)
{
 llRequestPermissions(owner, PERMISSION_TRIGGER_ANIMATION);
 currentAnimation = llUnescapeURL(anim);
}
// user-function: config_page
// - does not return anything
// - prepares page to configure youtube video link
config_page()
{
 responseStatus = 200;
 responseBody = header + "Options:<form action='config/set' method='"
 + "get'>Video URL: <input type='text' name='video' value='"
 + video_url + "' /><input type='submit' value='Set' /></form>"
 + footer;
}
// user-function: throw_exception
// - does not return anything
// - logs errors into cache for later viewing and debugging
throw_exception(string inputString)
{
 if (exceptions == "")
 {
 exceptions = "The following un-handled exception(s) occurred that are "
 + "preventing this device's operation:\n";
 }
 exceptions += "\t"+inputString+"\n";
}
default
{
 on_rez(integer start_param)
 {
 release_url();
 llResetScript();
 }
 changed(integer change)
 {
 if (change & (CHANGED_OWNER | CHANGED_INVENTORY))
 {
 release_url();
 llResetScript();
 }
 if (change & (CHANGED_REGION | CHANGED_REGION_START | CHANGED_TELEPORT))
 {
 request_secure_url();
 }
 }
 state_entry()
 {
 init();
 }
 touch_start(integer num_detected)
 {
 toggle_visibility_of_HUD_button();
 }
 http_request(key id, string method, string body)
 {
 responseStatus = 400;
 responseBody = "Unsupported method";
 if (method == URL_REQUEST_GRANTED)
 {
 responseStatus = 200;
 responseBody = "OK";
 set_link_media(body);
 }
 else if (method == URL_REQUEST_DENIED)
 {
 responseBody = "Bad request";
 throw_exception("The following error occurred while attempting to "
 + "get a free URL for this device:\n \n" + body);
 }
 else if (method == "GET")
 {
 responseStatus = 200;
 responseBody = "GET";
 string pathInfoHeader = llGetHTTPHeader(id, "x-path-info");
 list path = llParseString2List(pathInfoHeader, ["/"], []);
 if (path == [])
 {
 currentRequestID = id;
 list agents = llGetAgentList(scope, []);
 integer index = llGetListLength(agents);
 if (!index)
 {
 responseBody = header + "Scan Results:No one "
 + "near by.Owner: <a href='agent/" + (string)owner
 + "'>" + ownerName + "</a>" + footer;
 }
 else
 {
 responseBody = header + "Scan Results:";
 while (index)
 {
 --index;
 key agent = llList2Key(agents, index);
 responseBody += "<a href='agent/" + (string)agent + "'>"
 + html_body_with_formatted_avatar_name(agent) + "</a>";
 }
 responseBody += "" + footer;
 }
 lastPath = [];
 }
 else
 {
 http_get(id, path);
 }
 }
 llSetContentType(id, CONTENT_TYPE_HTML);
 llHTTPResponse(id, responseStatus, responseBody);
 if (exceptions != "")
 {
 state error;
 }
 }
 run_time_permissions(integer perm)
 {
 if (perm & PERMISSION_TRIGGER_ANIMATION)
 {
 llStartAnimation(currentAnimation);
 }
 else
 {
 throw_exception("This HUD has tried to animate your avatar WITHOUT "
 + "having the permissions to do so. You must grant this HUD "
 + "permissions to animate your avatar for this feature to work.");
 }
 if (exceptions != "")
 {
 state error;
 }
 }
 state_exit()
 {
 release_url();
 }
}
state error
{
 on_rez(integer start_param)
 {
 llResetScript();
 }
 changed(integer change)
 {
 if (change & (CHANGED_OWNER | CHANGED_INVENTORY))
 {
 llResetScript();
 }
 }
 state_entry()
 {
 llOwnerSay("========== ERROR REPORT START ==========");
 llOwnerSay(exceptions);
 llOwnerSay("========== ERROR REPORT END ==========");
 llOwnerSay("Resetting now...");
 llResetScript();
 }
}
Go to top!
Category:LSL Library