Difference between revisions of "HTML HUD Demo"

From Second Life Wiki
Jump to navigation Jump to search
m (removed a redundant line)
m (minor cleanup)
Line 61: Line 61:
     owner    = llGetOwner();
     owner    = llGetOwner();
     ownerName = llKey2Name(owner);
     ownerName = llKey2Name(owner);
     llSetObjectName(ownerName+"'s HUD");
     llSetObjectName("HTML HUD");


     scope = AGENT_LIST_PARCEL;
     scope = AGENT_LIST_PARCEL;
Line 78: Line 78:


     toggle_visibility_of_HUD_button();
     toggle_visibility_of_HUD_button();
     request_url();
     request_secure_url();
}
}


Line 106: Line 106:
}
}


//  user-function: request url
//  user-function: drop and clear the old url
//  - does not return anything
//  - does not return anything
//  - make sure we drop the old url before requesting a new one


request_url()
release_url()
{
{
     llReleaseURL(url);
     llReleaseURL(url);
     url = "";
     url = "";
}
//  user-function: request secure url
//  - does not return anything
//  - make sure we release the old url before requesting a new one


     llRequestURL();
request_secure_url()
{
     release_url();
    currentRequestID = llRequestSecureURL();
}
}


Line 482: Line 489:
     on_rez(integer start_param)
     on_rez(integer start_param)
     {
     {
         llReleaseURL(url);
         release_url();
         llResetScript();
         llResetScript();
     }
     }
Line 490: Line 497:
         if (change & (CHANGED_OWNER | CHANGED_INVENTORY))
         if (change & (CHANGED_OWNER | CHANGED_INVENTORY))
         {
         {
             llReleaseURL(url);
             release_url();
             llResetScript();
             llResetScript();
         }
         }
Line 496: Line 503:
         if (change & (CHANGED_REGION | CHANGED_REGION_START | CHANGED_TELEPORT))
         if (change & (CHANGED_REGION | CHANGED_REGION_START | CHANGED_TELEPORT))
         {
         {
             request_url();
             request_secure_url();
         }
         }
     }
     }
Line 524: Line 531:
         else if (method == URL_REQUEST_DENIED)
         else if (method == URL_REQUEST_DENIED)
         {
         {
            responseStatus = 400;
             responseBody  = "Bad request";
             responseBody  = "Bad request";


Line 608: Line 614:
     state_exit()
     state_exit()
     {
     {
         llReleaseURL(url);
         release_url();
     }
     }
}
}

Revision as of 07:53, 5 January 2014

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

screenshots

<lsl> // 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 "

";

}

// 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)
       {
           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;

                       while (index)
                       {
                           --index;
    
                           key agent = llList2Key(agents, index);
    

    responseBody += "

  • <a href='agent/" + (string)agent + "'>" + html_body_with_formatted_avatar_name(agent) + "</a>
  • "; } responseBody += "" + footer;

                   }
                   else
                   {
    

    responseBody = header + "

    Scan Results:

      ";
                     }
      
                     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();
         }
      

      } </lsl>

      Go to top!