Difference between revisions of "HTML HUD Demo"

From Second Life Wiki
Jump to: navigation, search
m
m (Reformatted script and deleted copy.)
Line 1: Line 1:
 
<span id="top"></span>
 
<span id="top"></span>
  
Suggest a "Stop All Animations" button be added to Animations selection frame for convenience. There are two versions of this script. The original by Kelly Linden above and a copy from Kireji Haiku below, who hasn't added anything to the script but reformatted it for better readability.
+
<lsl>
'''[[#original by Kelly Linden | original script]]'''
+
// Suggest a "Stop All Animations" button be added to Animations selection frame for convenience.
'''[[#reformatted version | reformatted version]]'''
+
</lsl>
  
 
[[File:HTML HUD Demo.jpg|200px|thumb|top|screenshots]]
 
[[File:HTML HUD Demo.jpg|200px|thumb|top|screenshots]]
  
=== original by Kelly Linden ===
+
<lsl>
{{Anchor|original by Kelly Linden}}
+
//HTML-based, single script HUD
 +
//
 +
// 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.
 +
//  original by Kelly Linden and reformatted by Kireji Haiku, 2011.
  
<lsl>// HTML Based, Single Script HUD
 
// This is intended to be a tech demo of what html on a hud allows.
 
// * Interaction with SL world (give items, play animations, scan nearby avatars)
 
// * Simple, low overhead (1 scripts, 1 prim)
 
// * Rich, dynamic interface (images, menus, flash)
 
// Updates and improvements are welcome at http://wiki.secondlife.com/wiki/HTML_HUD_Demo
 
 
// To Use:
 
// * Create a cube and wear it as a hud on the top-left. (script will need tweaking for other spots)
 
// * Edit the cube while wearing it and add this script to it.
 
// * Add any animations you want to play to the object
 
// * Add any notecards or objects you want to give away to the object.
 
 
// 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.
 
// Created by Kelly Linden, 2011.
 
 
 
// CONFIG PARAMS
 
string video_url = "http://www.youtube.com/embed/m7p9IEpPu-c?rel=0";
 
 
integer display_face = 4;  // The face to display HTML on
 
integer button_face = 2;    // The face that shows when hiding the HTML
 
integer link = LINK_THIS;  // The prim with media and button.
 
 
// Prim params for when media is shown. Rotates and scales to show the media.
 
list visible = [PRIM_POS_LOCAL,<0,-0.13,-0.13>,PRIM_SIZE,<0.01,0.25,0.25>,PRIM_ROT_LOCAL,<0,0,0,1>];
 
// Prim params for when media is hidden. Rotates and scales to show the button.
 
list button = [PRIM_POS_LOCAL,<0,-0.04,-0.04>,PRIM_SIZE,<0.05,0.05,0.05>,PRIM_ROT_LOCAL,<0,0,-1,0>];
 
// Initial setup: sets the button texture.
 
list init = [PRIM_TEXTURE,ALL_SIDES,TEXTURE_BLANK,<1,1,1>,<1,1,1>,0,PRIM_TEXTURE,button_face,"0b815b79-c8f5-fc98-91fc-e77b53a468e2",<1,1,1>,<1,1,1>,0];
 
 
// INTERNAL VARS
 
// These variables are set by the program.
 
integer is_visible = FALSE; // Whether or not the hud is showing HTML side.
 
list last_path;            // Last visited
 
string footer;              // Footer: set in set_url
 
string header;              // Header: set in set_url
 
key current_request;        // HTTP request ID for the current request.
 
 
// Toggle between button and HTML
 
toggle_show()
 
{
 
    if (is_visible)
 
        llSetLinkPrimitiveParamsFast(link,visible);
 
    else
 
        llSetLinkPrimitiveParamsFast(link,button);
 
    is_visible = !is_visible;
 
}
 
 
// Build an image url from an avatar's key.
 
string av_image(key id)
 
{
 
    return "https://my-secondlife.s3.amazonaws.com/users/" + llGetUsername(id) + "/thumb_sl_image.png";
 
}
 
 
// Build an url that opens the profile for an avatar
 
string app_profile_url(key id)
 
{
 
    return "secondlife:///app/agent/" + (string)id + "/about";
 
}
 
 
// Setup required after getting an url.
 
set_url(string url)
 
{
 
    // Build the common header:
 
    // * link to the css file
 
    // * set a base url
 
    // * html boilerplate code.
 
    // TODO: Remove dependency on this css file.
 
    header = "<html><head>"
 
            + "<link href='https://d1979ns0fqtj19.cloudfront.net/assets/common-103828347986224535963905120979424958961.css' media='all' rel='stylesheet' type='text/css' />"
 
            + "<base href='" + url + "/' />"
 
            + "</head><body>";
 
 
    // Build the common footer
 
    // * Navigation menu
 
    // * JS helper script (for fancy buttons)
 
    // * html boilerplate code
 
    // TODO: Remove dependency on the js file
 
    footer = "<div align='center' style='position:absolute;top:93%;left:8%;'>"
 
        + "<a href=''>Scan</a> | "
 
        + "<a href='anims'>Anims</a> | "
 
        + "<a href='video'>Video</a> | "
 
        + "<a href='config'>Config</a> | "
 
        + "<a href='hide'>Hide</a></div>"
 
        + "<script src='https://d2mjw3k7q9u8rb.cloudfront.net/assets/common-170919042270376442559931151451605602726.js' type='text/javascript'></script></body></html>";
 
 
    llSetLinkMedia(link, display_face,              // Side to display the media on.
 
            [PRIM_MEDIA_AUTO_PLAY,TRUE,    // Show this page immediately
 
            PRIM_MEDIA_CURRENT_URL,url,    // The url if they hit 'home'
 
            PRIM_MEDIA_HOME_URL,url,      // The url currently showing
 
            PRIM_MEDIA_HEIGHT_PIXELS,256,  // Height/width of media texture will be
 
            PRIM_MEDIA_WIDTH_PIXELS,256,  //  rounded up to nearest power of 2.
 
            PRIM_MEDIA_PERMS_CONTROL, PRIM_MEDIA_PERM_NONE]);
 
}
 
 
// Turn a number of bytes (script size) into human readable format.
 
string bytes2str(integer b)
 
{
 
    if (b < 1024 * 1024) return (string)(b / 1024) + "KB";
 
    return (string)(b / (1024 * 1024)) + "MB";
 
}
 
 
// Build the html for the profile thumbnail image that links to their profile.
 
string profile_thumb_html(key agent)
 
{
 
    return "<a href='" + app_profile_url(agent) + "' 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(agent) + "/sl_image.png' /></a>";
 
}
 
 
// Build the agent options menu
 
string agent_menu_html(key agentid)
 
{
 
    return "<div class='menu_button'>"
 
        + "<a class='button call_to_action'>Options</a>"
 
        + "<ul style='list-style-type:none;text-align:left' class='menu'>"
 
        + "<li><a href='secondlife:///app/agent/" + (string)agentid + "/im'>IM</a></li>"
 
        + "<li><a href='secondlife:///app/agent/" + (string)agentid + "/offerteleport'>Offer Teleport</a></li>"
 
        + "<li><a href='secondlife:///app/maptrackavatar/" + (string)agentid + "'>Map</a></li>"
 
        + "<li><a href='secondlife:///app/sharewithavatar/" + (string)agentid + "'>Share</a></li>"
 
        + "<li><a href='secondlife:///app/agent/" + (string)agentid + "/pay'>Pay</a></li></ul></div>";
 
}
 
 
// Build an html list of items of type for the Give menu
 
string give_list(key agentid, integer type)
 
{
 
    string resp;
 
    integer n = llGetInventoryNumber(type);
 
    integer i;
 
    for (i=0;i<n;++i)
 
    {
 
        resp += "<li><a href='agent/" + (string)agentid + "/give/" + llGetInventoryName(type,i) + "'>" + llGetInventoryName(type,i) + "</a></li>";
 
    }
 
    return resp;
 
}
 
 
// Build the html for the Give menu and fill it with notecards and objects.
 
string give_menu_html(key agentid)
 
{
 
    string resp = "<div class='menu_button'>"
 
        + "<a class='button call_to_action'>Give</a>"
 
        + "<ul style='list-style-type:none;text-align:left;white-space:nowrap' class='menu'>";
 
 
    // Give notecards and objects.
 
    integer resp_size = llStringLength(resp);
 
    resp += give_list(agentid,INVENTORY_NOTECARD);
 
    resp += give_list(agentid,INVENTORY_OBJECT);
 
 
    if (resp_size == llStringLength(resp))
 
    {
 
        // No inventory.
 
        resp += "<li>(no objects or notecards found)</li>";
 
    }
 
 
    resp += "</ul></div>";
 
    return resp;
 
}
 
 
// Strip the "Resident" last name, break long names into 2 lines if possible.
 
string agent_name(key id)
 
{
 
    string name = llList2String(llParseString2List(llKey2Name(id),[" Resident"],[]),0);
 
    if (llStringLength(name) > 15)
 
    {
 
        name = llDumpList2String(llParseString2List(name,[" "],[]),"<br>");
 
    }
 
    return name;
 
}
 
 
// Build the Agent Details page.
 
agent_details(key request, key agent)
 
{
 
    // Get the details we want.
 
    list r = llGetObjectDetails(agent,[OBJECT_POS,OBJECT_TOTAL_SCRIPT_COUNT,OBJECT_SCRIPT_MEMORY,OBJECT_SCRIPT_TIME]);
 
 
    // Build the html.
 
    string resp = header + "<table border='0' cellspacing='1' cellpadding='1'>"
 
        + "<tr><td colspan='2' style='white-space:nowrap'><div class='profile_title'>"
 
        +  "<h1 id='display_name'>" + agent_name(agent) + "</h1>"
 
        +  "<h2 id='username'>" + llGetUsername(agent) + "</h2>"
 
        + "</div></td><td><div align='right'>" + agent_menu_html(agent) + "<br>" + give_menu_html(agent) + "</div></td></tr>"
 
        + "<tr><td width='80'>" + profile_thumb_html(agent) + "</td><td colspan='2'>"
 
        +  "<ul style='list-style-type:circle'>"
 
        +  "<li>Scripts:"
 
        +      "<ul style='list-style-type:disc;margin-left:10px'>"
 
        +      "<li>" + (string)llList2Integer(r,1) + " total</li>"
 
        +      "<li>" + bytes2str(llList2Integer(r,2)) + "</li>"
 
        +      "<li>" + (string)((integer)(llList2Float(r,3) * 1000000.0)) + "us</li>"
 
        +  "</ul></li></ul></td></tr></table>" + footer;
 
 
    // Send the response.
 
    llSetContentType(current_request, CONTENT_TYPE_HTML);
 
    llHTTPResponse(current_request,200,resp);
 
 
    // Uncomment this to debug memory usage.
 
    // Done here as this is the largest, most complex page.
 
    // llOwnerSay("Memory used: " + (string)llGetUsedMemory() + ", response size: " + (string)llStringLength(resp));
 
}
 
 
// Build up an animation menu with any animations in the object.
 
string anim_menu_html()
 
{
 
    string resp = "<div class='menu_button' style='align:center'>"
 
        + "<a class='button call_to_action'>Animate<b class='actions_dropdown'>&nbsp;</b></a>"
 
        + "<ul style='list-style-type:none;text-align:left' class='menu'>";
 
 
    integer n = llGetInventoryNumber(INVENTORY_ANIMATION);
 
    if (n == 0)
 
    {
 
        resp += "<li>(no animations found)</li>";
 
    }
 
    else
 
    {
 
        integer i;
 
        for(i=0;i<n;++i)
 
        {
 
            resp += "<li><a href='anims/" + llGetInventoryName(INVENTORY_ANIMATION,i) + "'>" + llGetInventoryName(INVENTORY_ANIMATION,i) + "</a></li>";
 
        }
 
    }
 
 
    resp += "</ul></div>";
 
    return resp;
 
}
 
 
// Play an animation given the name.
 
play_anim(string anim)
 
{
 
    llRequestPermissions(llGetOwner(),PERMISSION_TRIGGER_ANIMATION);
 
    llStartAnimation(llUnescapeURL(anim));
 
}
 
 
// Build the anims page.
 
anims_page()
 
{
 
    string resp = header + "<h1>Animations</h1><h2>Choose an animation:</h2>"
 
    + "<div style='margin-left:40px'>" + anim_menu_html() + "</div><br><br>" + footer;
 
    llSetContentType(current_request, CONTENT_TYPE_HTML);
 
    llHTTPResponse(current_request,200,resp);
 
}
 
 
// Build the configuration page.
 
config_page()
 
{
 
    string resp = header + "<h1>Options:</h1>"
 
        + "<form action='config/set' method='get'>"
 
        + "Video URL: <input type='text' name='video' value='" + video_url + "' />"
 
        + "<input type='submit' value='Set' /></form>"
 
        + footer;
 
    llSetContentType(current_request, CONTENT_TYPE_HTML);
 
    llHTTPResponse(current_request,200,resp);
 
}
 
 
// Process a get request with a path.
 
GET(key request, list path)
 
{
 
    current_request = request;
 
    integer path_segments = llGetListLength(path);
 
    if (path_segments == 0)
 
    {
 
        // Home page is handled in the sensor response.
 
        llSensor("",NULL_KEY,AGENT,96,PI);
 
       
 
        // Set the last_path incase the next request is a hide
 
        last_path = [];
 
        return;
 
    }
 
 
    string p0 = llList2String(path,0);
 
    if (p0 == "hide")
 
    {
 
        toggle_show();
 
       
 
        // We need to send back a page to show otherwise the
 
        // the next time the hud is shown there will be an error.
 
        // So we loop back in here with our last path.
 
        // Effectively this just refreshes the page we were already on.
 
        GET(request,last_path);
 
       
 
        // Do *not* set the last_path here to avoid any infinite loops.
 
        return;
 
    }
 
   
 
    // Set the last path here, after the recursive hide call.
 
    last_path = path;
 
   
 
    if (p0 == "agent")
 
    {
 
        if (path_segments == 1)
 
        {
 
            // <url>/agent
 
            // Show owner details.
 
            agent_details(request,llGetOwner());
 
            return;
 
        }
 
 
        // p1 should be an agent id
 
        key agent = (key)llList2String(path,1);
 
 
        if (path_segments == 2)
 
        {
 
            // <url>/agent/<id>
 
            // Show the details for the agent.
 
            agent_details(request,agent);
 
            return;
 
        }
 
       
 
        if (path_segments == 4)
 
        {
 
            // <url>/agent/<id>/<cmd>/<option>
 
            string cmd = llList2String(path,2);
 
            if (cmd == "give")
 
            {
 
                string object_name = llUnescapeURL(llList2String(path,3));
 
                llOwnerSay("Giving " + object_name + " to " + llKey2Name((key)agent));
 
                llGiveInventory(agent,object_name);
 
                agent_details(request,agent);
 
                return;
 
            }
 
        }
 
    }
 
    else if (p0 == "anims")
 
    {
 
        if (path_segments == 1)
 
        {
 
            // <url>/anims
 
            // Show the animation selection page
 
            anims_page();
 
            return;
 
        }
 
        else if (path_segments == 2)
 
        {
 
            // <url>/anims/<anim-name>
 
            // Play the specified animation
 
            play_anim(llList2String(path,1));
 
            anims_page();
 
            return;
 
        }
 
    }
 
    else if (p0 == "video")
 
    {
 
        // <url>/video
 
        // Play a demo video
 
        string resp = header
 
            + "<br><iframe width='255' height='173' src='" + video_url + "' frameborder='0' allowfullscreen></iframe>"
 
            + footer;
 
        llSetContentType(current_request, CONTENT_TYPE_HTML);
 
        llHTTPResponse(current_request,200,resp);
 
        return;
 
    }
 
    else if (p0 == "config")
 
    {
 
        if (path_segments == 1)
 
        {
 
            // <url>/config
 
            config_page();
 
            return;
 
        }
 
        if (llList2String(path,1) == "set")
 
        {
 
            // Parse config args
 
            list args = llParseString2List(llGetHTTPHeader(current_request,"x-query-string"),["&","="],[]);
 
            integer i = 0;
 
            integer argn = llGetListLength(args);
 
            while(i < argn)
 
            {
 
                string var = llList2String(args,i++);
 
                string val = llUnescapeURL(llList2String(args,i++));
 
                if (var == "video")
 
                {
 
                    video_url = val;
 
                }
 
            }
 
            config_page();
 
        }
 
    }
 
   
 
    // Unknown page. 404.
 
    string resp = header + "<h1>404 Page Not Found.</h1>" + footer;
 
    llSetContentType(current_request, CONTENT_TYPE_HTML);
 
    llHTTPResponse(current_request,404,resp);
 
}
 
 
default
 
{
 
    state_entry()
 
    {
 
        // Set the object name so inventory offers make sense to the receiver.
 
        llSetObjectName(llKey2Name(llGetOwner()) + "'s HUD");
 
        // Init button textures.
 
        llSetLinkPrimitiveParamsFast(link,init);
 
        // Reset to a hidden state
 
        toggle_show();
 
        // Request an URL.
 
        llRequestURL();
 
    }
 
 
    // We need to reset in the cases because we lose the URL.
 
    on_rez(integer n) { llResetScript();}
 
    changed(integer c)
 
    {
 
        if (c & (CHANGED_TELEPORT | CHANGED_OWNER | CHANGED_REGION)) llResetScript();
 
    }
 
 
    touch_start(integer total_number)
 
    {
 
        // Handles the button case.
 
        toggle_show();
 
    }
 
 
    http_request(key id, string method, string body)
 
    {
 
        if (method == URL_REQUEST_GRANTED)
 
        {
 
            set_url(body);
 
            return;
 
        }
 
        else if (method == URL_REQUEST_DENIED)
 
        {
 
            llOwnerSay("Unable to get url: " + body);
 
            llSleep(10);
 
            llRequestURL();
 
            return;
 
        }
 
 
        if (method == "GET")
 
        {
 
            list path = llParseString2List(llGetHTTPHeader(id,"x-path-info"),["/"],[]);
 
            GET(id,path);
 
            return;
 
        }
 
 
        llHTTPResponse(id,400,"Unsupported method.");
 
        return;
 
    }
 
 
    sensor(integer n)
 
    {
 
        // This is actually the main page.
 
        // Shows nearby residents for interacting with.
 
        string resp = header + "<h2>Scan Results:</h2><ul style='list-style-type:circle;margin-left:20px'>";
 
 
        // TODO: Only fits 14 right now. Figure out how to fit all 16.
 
        // TODO: Figure out how to find more than 16.
 
        if (n > 14) n = 14;
 
        for (--n; n >= 0; --n)
 
        {
 
            resp += "<li><a href='agent/" + (string)llDetectedKey(n) + "'>" + agent_name(llDetectedKey(n)) + "</a></li>";
 
        }
 
        resp += "</ul>" + footer;
 
        llSetContentType(current_request, CONTENT_TYPE_HTML);
 
        llHTTPResponse(current_request,200,resp);
 
    }
 
 
    no_sensor()
 
    {
 
        // No one nearby.
 
        // Include a link to the owner's details to make it easier to debug when no one is around.
 
        string resp = header + "<h2>Scan Results:</h2><ul style='list-style-type:circle;margin-left:20px'>";
 
        resp += "<li>No one near by.</li>";
 
        resp += "<li>Owner: <a href='agent/" + (string)llGetOwner() + "'>" + llKey2Name(llGetOwner()) + "</a></li>";
 
        resp += "</ul>" + footer;
 
        llSetContentType(current_request, CONTENT_TYPE_HTML);
 
        llHTTPResponse(current_request,200,resp);
 
    }
 
}</lsl>
 
 
[[#top|Go to top!]]
 
 
=== reformatted version ===
 
{{Anchor|reformatted version}}
 
 
<lsl>
 
/*
 
*  HTML-based, single script HUD
 
*
 
*  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.
 
*    original by Kelly Linden and reformatted by Kireji Haiku, 2011.
 
*/
 
  
 
key owner;
 
key owner;
Line 524: Line 48:
 
string exceptions;
 
string exceptions;
  
/*
+
// user-function: init
user-function: init
+
// - does not return anything
- does not return anything
+
// - sets initial variable values
- sets initial variable values
+
// - sets object's name and textures
- sets object's name and textures
+
// - request a url to use the HUD
- request a url to use the HUD
+
*/
+
  
 
init()
 
init()
Line 551: Line 73:
 
}
 
}
  
/*
+
// user-function: toggle_visibility_of_HUD_button
user-function: toggle_visibility_of_HUD_button
+
// - does not return anything
- does not return anything
+
// - toggle the visibility of the prim
- toggle the visibility of the prim
+
// - will rotate, position and scale the prim
- will rotate, position and scale the prim
+
*/
+
  
 
toggle_visibility_of_HUD_button()
 
toggle_visibility_of_HUD_button()
Line 574: Line 94:
 
}
 
}
  
/*
+
// user-function: request url
user-function: request url
+
// - does not return anything
- does not return anything
+
// - make sure we drop the old url before requesting a new one
- make sure we drop the old url before requesting a new one
+
*/
+
  
 
request_url()
 
request_url()
Line 586: Line 104:
 
}
 
}
  
/*
+
// user-function: set_link_media
user-function: set_link_media
+
// - does not return anything
- does not return anything
+
// - set the values for the string variables 'header' and 'footer'
- set the values for the string variables 'header' and 'footer'
+
// - prepare face 4 for media on a prim
- prepare face 4 for media on a prim
+
*/
+
  
 
set_link_media(string scriptUrl)
 
set_link_media(string scriptUrl)
Line 597: Line 113:
 
     url = 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>";
+
     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 = "<div align='center' style='position:absolute;top:93%;left:8%;'><a href=''>Scan</a> |  
+
     footer = "<div align='center' style='position:absolute;top:93%;left:8%;'><a href=''>Scan</a> | "
    <a href='anims'>Anims</a> | <a href='video'>Video</a> | <a href='config'>Config</a> | <a href='hide'>Hide</a></div><script src='https://d2mjw3k7q9u8rb.cloudfront.net/assets/common-170919042270376442559931151451605602726.js' type='text/javascript'></script></body></html>";
+
        + "<a href='anims'>Anims</a> | <a href='video'>Video</a> | <a href='config'>Config</a> | "
 +
        + "<a href='hide'>Hide</a></div><script src='"
 +
        + "https://d2mjw3k7q9u8rb.cloudfront.net/assets/common-170919042270376442559931151451605602726.js"
 +
        + "' type='text/javascript'></script></body></html>";
  
 
     llSetLinkMedia(LINK_THIS, 4, [
 
     llSetLinkMedia(LINK_THIS, 4, [
Line 611: Line 133:
 
}
 
}
  
/*
+
// user-function:
user-function:
+
// - does not return anything
- does not return anything
+
// - used if there's not a request to the homepage of our website
- 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
- check where on the site we are and prepare variables for the response
+
*/
+
  
 
http_get(key requestID, list path)
 
http_get(key requestID, list path)
Line 679: Line 199:
 
     {
 
     {
 
         responseStatus = 200;
 
         responseStatus = 200;
         responseBody = header + "<br><iframe width='255' height='173' src='" + video_url + "' frameborder='0' allowfullscreen></iframe>" + footer;
+
         responseBody = header + "<br><iframe width='255' height='173' src='"
 +
            + video_url + "' frameborder='0' allowfullscreen></iframe>"
 +
            + footer;
 +
 
 +
 
 
         return;
 
         return;
 
     }
 
     }
Line 710: Line 234:
 
     responseStatus = 404;
 
     responseStatus = 404;
 
     responseBody = header + "<h1>404 Page Not Found.</h1>" + footer;
 
     responseBody = header + "<h1>404 Page Not Found.</h1>" + 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.");
+
     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
user-function: prepare_profile_overview_page
+
// - does not return anything
- does not return anything
+
// - prepares a response for a page with information about a certain avatar
- prepares a response for a page with information about a certain avatar
+
// - includes profile thumbnail, name, script info, give menu
- includes profile thumbnail, name, script info, give menu
+
*/
+
  
 
prepare_profile_overview_page(key requestID, key id)
 
prepare_profile_overview_page(key requestID, key id)
Line 729: Line 252:
  
 
     responseStatus = 200;
 
     responseStatus = 200;
     responseBody = header + "<table border='0' cellspacing='1' cellpadding='1'><tr><td colspan='2' style='white-space:nowrap'><div class='profile_title'><h1 id='display_name'>" + html_body_with_formatted_avatar_name(id) + "</h1><h2 id='username'>" + llGetUsername(id) + "</h2></div></td><td><div align='right'>" + html_body_with_links_for_interaction_with_certain_avatar(id) + "<br>" + html_body_with_inventory_overview_for_give_menu(id) + "</div></td></tr><tr><td width='80'>" + html_body_avatar_profile_pic_thumbnail(id) + "</td><td colspan='2'><ul style='list-style-type:circle'><li>Scripts:<ul style='list-style-type:disc;margin-left:10px'><li>" + (string)llList2Integer(avatarDetails, 1) + " total</li><li>" + bytes2str(llList2Integer(avatarDetails, 2)) + "</li><li>" + (string)((integer)(llList2Float(avatarDetails, 3) * 1000000.0)) + "us</li></ul></li></ul></td></tr></table>" + footer;
+
     responseBody = header
 +
        + "<table border='0' cellspacing='1' cellpadding='1'><tr><td colspan='2' style='white-space:nowrap'>"
 +
        + "<div class='profile_title'><h1 id='display_name'>" + html_body_with_formatted_avatar_name(id)
 +
        + "</h1><h2 id='username'>" + llGetUsername(id) + "</h2></div></td><td><div align='right'>"
 +
        + html_body_with_links_for_interaction_with_certain_avatar(id) + "<br>"
 +
        + html_body_with_inventory_overview_for_give_menu(id) + "</div></td></tr><tr><td width='80'>"
 +
        + html_body_avatar_profile_pic_thumbnail(id)
 +
        + "</td><td colspan='2'><ul style='list-style-type:circle'>"
 +
        + "<li>Scripts:<ul style='list-style-type:disc;margin-left:10px'><li>"
 +
        + (string)llList2Integer(avatarDetails, 1) + " total</li><li>" + bytes2str(llList2Integer(avatarDetails, 2))
 +
        + "</li><li>" + (string)((integer)(llList2Float(avatarDetails, 3) * 1000000.0))
 +
        + "us</li></ul></li></ul></td></tr></table>" + footer;
 
}
 
}
  
/*
+
// user-function: html_body_with_formatted_avatar_name
user-function: html_body_with_formatted_avatar_name
+
// - returns the name of the avatar in html format
- returns the name of the avatar in html format
+
// - removes the lastname if it is Resident
- removes the lastname if it is Resident
+
// - adds a line-break for long names
- adds a line-break for long names
+
*/
+
  
 
string html_body_with_formatted_avatar_name(key id)
 
string html_body_with_formatted_avatar_name(key id)
Line 752: Line 284:
 
}
 
}
  
/*
+
// user-function: html_body_with_links_for_interaction_with_certain_avatar
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
- 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)
 
string html_body_with_links_for_interaction_with_certain_avatar(key id)
 
{
 
{
     return "<div class='menu_button'><a class='button call_to_action'>Options</a><ul style='list-style-type:none;text-align:left' class='menu'><li><a href='secondlife:///app/agent/" + (string)id + "/im'>IM</a></li><li><a href='secondlife:///app/agent/" + (string)id + "/offerteleport'>Offer Teleport</a></li><li><a href='secondlife:///app/maptrackavatar/" + (string)id + "'>Map</a></li><li><a href='secondlife:///app/sharewithavatar/" + (string)id + "'>Share</a></li><li><a href='secondlife:///app/agent/" + (string)id + "/pay'>Pay</a></li></ul></div>";
+
     return "<div class='menu_button'><a class='button call_to_action'>Options</a>"
 +
        + "<ul style='list-style-type:none;text-align:left' class='menu'><li>"
 +
        + "<a href='secondlife:///app/agent/" + (string)id
 +
        + "/im'>IM</a></li><li><a href='secondlife:///app/agent/"
 +
        + (string)id + "/offerteleport'>Offer Teleport</a></li><li><a href='secondlife:///app/maptrackavatar/"
 +
        + (string)id + "'>Map</a></li><li><a href='secondlife:///app/sharewithavatar/"
 +
        + (string)id + "'>Share</a></li><li><a href='secondlife:///app/agent/"
 +
        + (string)id + "/pay'>Pay</a></li></ul></div>";
 
}
 
}
  
/*
+
// user-function: html_body_with_inventory_overview_for_give_menu
user-function: html_body_with_inventory_overview_for_give_menu
+
// - returns html text with inventory item lists
- returns html text with inventory item lists
+
*/
+
  
 
string html_body_with_inventory_overview_for_give_menu(key id)
 
string html_body_with_inventory_overview_for_give_menu(key id)
 
{
 
{
     string stringToReturn = "<div class='menu_button'><a class='button call_to_action'>Give</a><ul style='list-style-type:none;text-align:left;white-space:nowrap' class='menu'>";
+
     string stringToReturn = "<div class='menu_button'><a class='button call_to_action'>Give</a>"
 +
        + "<ul style='list-style-type:none;text-align:left;white-space:nowrap' class='menu'>";
 +
 
  
 
     integer sizeOfStringBefore = llStringLength(stringToReturn);
 
     integer sizeOfStringBefore = llStringLength(stringToReturn);
Line 783: Line 320:
 
}
 
}
  
/*
+
// user-function: bytes2str
user-function: bytes2str
+
// - returns a string with script memory info in readable format
- returns a string with script memory info in readable format
+
*/
+
  
 
string bytes2str(integer bytes)
 
string bytes2str(integer bytes)
Line 798: Line 333:
 
}
 
}
  
/*
+
// user-function: html_body_avatar_profile_pic_thumbnail
user-function: html_body_avatar_profile_pic_thumbnail
+
// - returns html text with urls to someone's profile pic
- returns html text with urls to someone's profile pic
+
*/
+
  
 
string html_body_avatar_profile_pic_thumbnail(key id)
 
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>";
+
     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
user-function: inventory_list_in_html_format_for_give_menu
+
// - returns html text with inventory item list of given type
- returns html text with inventory item list of given type
+
*/
+
  
 
string inventory_list_in_html_format_for_give_menu(key id, integer type)
 
string inventory_list_in_html_format_for_give_menu(key id, integer type)
Line 830: Line 364:
 
}
 
}
  
/*
+
// user-function: anims_page
user-function: anims_page
+
// - does not return anything
- does not return anything
+
// - prepares an html text overview page of animations
- prepares an html text overview page of animations
+
*/
+
  
 
anims_page()
 
anims_page()
 
{
 
{
 
     responseStatus = 200;
 
     responseStatus = 200;
     responseBody = header + "<h1>Animations</h1><h2>Choose an animation:</h2><div style='margin-left:40px'>" + html_body_animations_overview() + "</div><br><br>" + footer;
+
     responseBody = header + "<h1>Animations</h1><h2>Choose an animation:</h2><div style='margin-left:40px'>"
 +
        + html_body_animations_overview() + "</div><br><br>" + footer;
 
}
 
}
  
/*
+
// user-function: html_body_animations_overview
user-function: html_body_animations_overview
+
// - returns html text with a list of included animations
- returns html text with a list of included animations
+
*/
+
  
 
string html_body_animations_overview()
 
string html_body_animations_overview()
 
{
 
{
     string stringToReturn = "<div class='menu_button' style='align:center'><a class='button call_to_action'>Animate<b class='actions_dropdown'>&nbsp;</b></a><ul style='list-style-type:none;text-align:left' class='menu'>";
+
     string stringToReturn = "<div class='menu_button' style='align:center'><a class='button call_to_action'>Animate"
 +
        + "<b class='actions_dropdown'>&nbsp;</b></a><ul style='list-style-type:none;text-align:left' class='menu'>";
  
 
     integer index = llGetInventoryNumber(INVENTORY_ANIMATION);
 
     integer index = llGetInventoryNumber(INVENTORY_ANIMATION);
Line 870: Line 402:
 
}
 
}
  
/*
+
// user-function: play_anim
user-function: play_anim
+
// - does not return anything
- does not return anything
+
// - prompts a perms request to animate owner
- prompts a perms request to animate owner
+
*/
+
  
 
play_anim(string anim)
 
play_anim(string anim)
Line 882: Line 412:
 
}
 
}
  
/*
+
// user-function: config_page
user-function: config_page
+
// - does not return anything
- does not return anything
+
// - prepares page to configure youtube video link
- prepares page to configure youtube video link
+
*/
+
  
 
config_page()
 
config_page()
 
{
 
{
 
     responseStatus = 200;
 
     responseStatus = 200;
     responseBody = header + "<h1>Options:</h1><form action='config/set' method='get'>Video URL: <input type='text' name='video' value='" + video_url + "' /><input type='submit' value='Set' /></form>" + footer;
+
     responseBody = header + "<h1>Options:</h1><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
user-function: throw_exception
+
// - does not return anything
- does not return anything
+
// - logs errors into cache for later viewing and debugging
- logs errors into cache for later viewing and debugging
+
*/
+
  
 
throw_exception(string inputString)
 
throw_exception(string inputString)
Line 908: Line 436:
 
}
 
}
  
default  
+
default
{  
+
{
 
     on_rez(integer start_param)
 
     on_rez(integer start_param)
 
     {
 
     {
Line 977: Line 505:
 
         }
 
         }
  
/*
+
// check if doing a sensor sweep and if so don't send a response
check if doing a sensor sweep and if so don't send a response
+
// but send the response in 'sensor event' or 'no_sensor event'
but send the response in 'sensor event' or 'no_sensor event'
+
//
*
+
// (time-out 30.0 seconds for response)
(time-out 30.0 seconds for response)
+
*/
+
  
 
         if (sendResponseNow)
 
         if (sendResponseNow)
Line 1,001: Line 527:
 
         }
 
         }
 
         else
 
         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.");
+
             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 != "")
 
         if (exceptions != "")
Line 1,021: Line 548:
 
             key id = llDetectedKey(num_detected);
 
             key id = llDetectedKey(num_detected);
  
             responseBody += "<li><a href='agent/" + (string)id + "'>" + html_body_with_formatted_avatar_name(id) + "</a></li>";
+
             responseBody += "<li><a href='agent/" + (string)id + "'>"
 +
                + html_body_with_formatted_avatar_name(id) + "</a></li>";
 
         }
 
         }
  
Line 1,033: Line 561:
 
     {
 
     {
 
         responseStatus = 200;
 
         responseStatus = 200;
         responseBody = header + "<h2>Scan Results:</h2><ul style='list-style-type:circle;margin-left:20px'><li>No one near by.</li><li>Owner: <a href='agent/" + (string)owner + "'>" + ownerName + "</a></li></ul>" + footer;
+
         responseBody = header + "<h2>Scan Results:</h2><ul style='list-style-type:circle;margin-left:20px'>"
 +
            + "<li>No one near by.</li><li>Owner: <a href='agent/" + (string)owner + "'>"
 +
            + ownerName + "</a></li></ul>" + footer;
  
 
         llSetContentType(currentRequestID, CONTENT_TYPE_HTML);
 
         llSetContentType(currentRequestID, CONTENT_TYPE_HTML);

Revision as of 12:59, 2 October 2012

<lsl> // Suggest a "Stop All Animations" button be added to Animations selection frame for convenience. </lsl>

screenshots

<lsl> //HTML-based, single script HUD // // 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. // original by Kelly Linden and reformatted by Kireji Haiku, 2011.


key owner; string ownerName;

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(ownerName+"'s HUD");
   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)
   llSetLinkPrimitiveParamsFast(LINK_THIS, [
       PRIM_TEXTURE, ALL_SIDES, TEXTURE_BLANK, <1.0, 1.0, 1.0>, ZERO_VECTOR, (float)FALSE,
       PRIM_TEXTURE, 2, "0b815b79-c8f5-fc98-91fc-e77b53a468e2", <1.0, 1.0, 1.0>, ZERO_VECTOR, (float)FALSE]);
   toggle_visibility_of_HUD_button();
   request_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: request url // - does not return anything // - make sure we drop the old url before requesting a new one

request_url() {

   llReleaseURL(url);
   llRequestURL();

}

// 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 = 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 "";

}

// 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 = "";
   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 + "

    Animations

    Choose 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 = "";
       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)
       {
           llReleaseURL(url);
           llResetScript();
       }
    
       changed(integer change)
       {
           if (change & (CHANGED_OWNER | CHANGED_INVENTORY))
           {
               llReleaseURL(url);
               llResetScript();
           }
    
           if (change & (CHANGED_REGION | CHANGED_REGION_START | CHANGED_TELEPORT))
               request_url();
       }
    
       state_entry()
       {
           init();
       }
    
       touch_start(integer num_detected)
       {
           toggle_visibility_of_HUD_button();
       }
    
       http_request(key id, string method, string body)
       {
           integer sendResponseNow = TRUE;
    
           responseStatus = 400;
           responseBody = "Unsupported method";
    
           if (method == URL_REQUEST_GRANTED)
           {
               responseStatus = 200;
               responseBody = "OK";
    
               set_link_media(body);
           }
           else if (method == URL_REQUEST_DENIED)
           {
               responseStatus = 400;
               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 == [])
               {
                   sendResponseNow = FALSE;
                   llSensor("", NULL_KEY, AGENT_BY_LEGACY_NAME, 96.0, PI);
                   lastPath = [];
               }
               else
                   http_get(id, path);
           }
    

    // check if doing a sensor sweep and if so don't send a response // but send the response in 'sensor event' or 'no_sensor event' // // (time-out 30.0 seconds for response)

           if (sendResponseNow)
           {
               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;
       }
    
       sensor(integer num_detected)
       {
           responseStatus = 200;
    
    responseBody = header + "

    Scan Results:

      ";
             if (14 < num_detected)
                 num_detected = 14;
      
             while (num_detected)
             {
                 --num_detected;
      
                 key id = llDetectedKey(num_detected);
      
      responseBody += "
    • <a href='agent/" + (string)id + "'>" + html_body_with_formatted_avatar_name(id) + "</a>
    • ";
             }
      
      responseBody += "
    " + footer;
           llSetContentType(currentRequestID, CONTENT_TYPE_HTML);
           llHTTPResponse(currentRequestID, responseStatus, responseBody);
       }
    
       no_sensor()
       {
           responseStatus = 200;
    
    responseBody = header + "

    Scan Results:

      " + "
    • No one near by.
    • Owner: <a href='agent/" + (string)owner + "'>" + ownerName + "</a>
    " + footer;
           llSetContentType(currentRequestID, CONTENT_TYPE_HTML);
           llHTTPResponse(currentRequestID, responseStatus, responseBody);
       }
    
       state_exit()
       {
           llReleaseURL(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();
       }
    

    } </lsl>

    Go to top!