LlSetPrimMediaParams/Tricks

Shared Media and HTTP-In

Warning!

While these tricks are still useful, they are largely unneeded now with llSetContentType and CONTENT_TYPE_HTML


Pre-Viewer 2.0 interfacing with an LSL script is a jumble of llDialog, chats, touches and possibly external web sites. With Shared Media that all changes.

One step at a time .......

llSetPrimMediaParams

 
default
{
	state_entry()
	{
		llSetPrimMediaParams(0,								// Side to display the media on.
			[PRIM_MEDIA_AUTO_PLAY, TRUE, 					// Show this page immediately
			 PRIM_MEDIA_CURRENT_URL, "https://google.com",	// The URL currently showing
			 PRIM_MEDIA_HOME_URL, "https://google.com",		// The URL if they hit 'home'
			 PRIM_MEDIA_HEIGHT_PIXELS, 512,					// Height/width of media texture will be
			 PRIM_MEDIA_WIDTH_PIXELS, 512]);				//   rounded up to nearest power of 2.
	}
}

"data:" URLs

There is a special URL type: "data:" that lets you send the HTML in the URL. Paste the below into your browser's address bar.

data:text/html,<h1>This is a test</h1><h2>This is a test</h2><h3>This is a test</h3>

llSetPrimMediaParams for "data:" URLs

show(string html)
{
	html = "data:text/html," + llEscapeURL(html);
	llSetPrimMediaParams(0,						// Side to display the media on.
			[PRIM_MEDIA_AUTO_PLAY, TRUE,		// Show this page immediately
			 PRIM_MEDIA_CURRENT_URL, html,		// The url currently showing
			 PRIM_MEDIA_HOME_URL, html,			// The url if they hit 'home'
			 PRIM_MEDIA_HEIGHT_PIXELS, 512,		// Height/width of media texture will be
			 PRIM_MEDIA_WIDTH_PIXELS, 512]);	//   rounded up to nearest power of 2.
}
default
{
	state_entry()
	{
		show("<h1>This is a test</h1><h2>This is a test</h2><h3>This is a test</h3>");
	}
}

HTTP-In

default
{
	state_entry()
	{
		llRequestURL();
	}
	
	http_request(key id, string method, string body)
	{
		if (method == URL_REQUEST_GRANTED)
		{
			llSay(0, "URL is: " + body);
		}
		else if (method == "GET")
		{
			llSay(0, "Received page request.");
			llHTTPResponse(id, 200, "OK");
		}
	}
}

llSetPrimMediaParams for "data:" URLs that link back to HTTP-In

show(string html)
{
	html = "data:text/html," + llEscapeURL(html);
	llSetPrimMediaParams(0,						// Side to display the media on.
			[PRIM_MEDIA_AUTO_PLAY, TRUE,		// Show this page immediately
			 PRIM_MEDIA_CURRENT_URL, html,		// The URL currently showing
			 PRIM_MEDIA_HOME_URL, html,			// The URL if they hit 'home'
			 PRIM_MEDIA_HEIGHT_PIXELS, 512,		// Height/width of media texture will be
			 PRIM_MEDIA_WIDTH_PIXELS, 512]);	//   rounded up to nearest power of 2.
}

string replace_all(string src, string target, string replace)
{
	return llDumpList2String(llParseString2List(src, [target], []), replace);
}

string get_query(key id, string name)
{
	string query = llGetHTTPHeader(id, "x-query-string");
	query = replace_all(query, "+", " ");
	query = llUnescapeURL(query);
	list q = llParseString2List(query, ["=", "&", ";"], []);
	integer i = llListFindList(q, [name]);
	if (i != -1)
	{
		return llList2String(q, i+1);
	}
	
	return "";
}

default
{
	state_entry()
	{
		show("<h1>This is a test</h1><h2>This is a test</h2><h3>This is a test</h3>");
		llRequestURL();
	}
	
	http_request(key id, string method, string body)
	{
		if (method == URL_REQUEST_GRANTED)
		{
			show("<h1><a href='" + body + "/?get=owner'>Owner ID</a><br><br><a href='"
				+ body + "/?get=object'>Object ID</a></h1>");
		}
		else if (method == "GET")
		{
			string get = get_query(id, "get");
			if (get == "owner")
			{
				llHTTPResponse(id, 200, "Owner is: " + (string)llGetOwner());
			}
			else if (get == "object")
			{
				llHTTPResponse(id, 200, "Object is: " + (string)llGetKey());
			}
			else
			{			
				llHTTPResponse(id, 400, "huh?");
			}
		}
	}
}

Forms

data:text/html,<form action="https://google.com/search" method="GET">Search:<input type="text" name="q"><input type="submit" value="Submit"></form>

The Magic

string html_base = 
"<h1><form action='%url%' method='GET'>
Floating Text:<input type='text' name='text'><br>
<input type='submit' value='Set'>
</form></h1>";

string url;

integer r;
show(string html)
{
	html = "data:text/html," + llEscapeURL(html) + "<span " + (string)((++r) % 10) + "/>";
	
	llSetPrimMediaParams(0,						// Side to display the media on.
			[PRIM_MEDIA_AUTO_PLAY, TRUE,		// Show this page immediately
			 PRIM_MEDIA_CURRENT_URL, html,		// The url currently showing
			 PRIM_MEDIA_HOME_URL, html,			// The url if they hit 'home'
			 PRIM_MEDIA_HEIGHT_PIXELS, 512,		// Height/width of media texture will be
			 PRIM_MEDIA_WIDTH_PIXELS, 512]);	//   rounded up to nearest power of 2.
}

string replace_all(string src, string target, string replace)
{
	return llDumpList2String(llParseString2List(src,[target],[]),replace);
}

string get_query(key id, string name)
{
	string query = llGetHTTPHeader(id, "x-query-string");
	query = replace_all(query, "+", " ");
	query = llUnescapeURL(query);
	list q = llParseString2List(query, ["=", "&", ";"], []);
	integer i = llListFindList(q, [name]);
	if (i != -1)
	{
		return llList2String(q, i + 1);
	}
	
	return "";
}

default
{
	state_entry()
	{
		llRequestURL();
	}
	
	http_request(key id, string method, string body)
	{
		if (method == URL_REQUEST_GRANTED)
		{
			url = body + "/";
			
			show(replace_all(html_base, "%url%", url));
		}
		else if (method == "GET")
		{
			llSetText(get_query(id, "text"), <1.0, 1.0, 0.0>, 1);
			show(replace_all(html_base, "%url%", url));
			llHTTPResponse(id, 200, "Loading....");
		}
	}
}

The Limits

The Tricks

The future?

I think there is a lot more potential here than what I have outlined .... if you have ideas or work out great new hacks let me know! Also let me know if it is ok to share those hacks with others. :)

DHTML / Javascript (Tali Rosca)

Tali Rosca sent me the code to render the output of HTTP-In directly. I've wrapped her magic into the build_url and build_response functions in the example below. This example renders a page that is around 3.5k bytes - well past the 1k limit of URLs.
Pros:

Cons:

string url;		// Our base HTTP-in URL
integer r;		// Used for uniqueness in PrimMedia URLs
integer page;	// The page number.

show(string html)
{
	html += "<span " + (string)((++r) % 10) + "/>";
	
	llSetPrimMediaParams(0,						// Side to display the media on.
			[PRIM_MEDIA_AUTO_PLAY, TRUE,		// Show this page immediately
			 PRIM_MEDIA_CURRENT_URL, html,		// The url if they hit 'home'
			 PRIM_MEDIA_HOME_URL, html,			// The url currently showing
			 PRIM_MEDIA_HEIGHT_PIXELS, 512,		// Height/width of media texture will be
			 PRIM_MEDIA_WIDTH_PIXELS, 512]);	//   rounded up to nearest power of 2.
}

// This creates a "data:" URL that will render the output of the HTTP-in URL given.
string build_url(string url)
{
	return "data:text/html," 
		+ llEscapeURL("<html><head><script src='" + url 
		+ "' type='text/javascript'></script></head><body onload='init()'></body></html>");
}

// This wraps the HTML you want to display so that it will be shown from links
// made with "build_url()"
string build_response(string body)
{
	return "function init() {document.getElementsByTagName('body')[0].innerHTML='" + body + "';}";
}

default
{
	state_entry()
	{
		llRequestURL();
	}
	
	http_request(key id, string method, string body)
	{
		if (method == URL_REQUEST_GRANTED)
		{
			url = body + "/";
			
			show(build_url(url + "page" + (string)(page)));
		}
		else if (method == "GET")
		{
			string path = llGetHTTPHeader(id, "x-path-info");
			string content = "<h1>path: " + path + "</h1>";
			page = (integer)llGetSubString(path, 5, 5);
			if (page > 0) content += "<a href=\"" + build_url(url + "page" + (string)(page - 1)) + "\">Previous</a> ";
			if (page < 9) content += "<a href=\"" + build_url(url + "page" + (string)(page + 1)) + "\">Next</a><br>";
			integer k;
			for(; k<10; ++k)
			{
				if (k == page)
					content += "<br>Page " + (string)k;
				else
					content += "<br><a href=\"" + build_url(url + "page" + (string)(k)) + "\">Page " + (string)k + "</a>";
			}
			content += "<br><br>This page is " + (string)(llStringLength(build_response(content)) + 37) + " bytes long.";
			llHTTPResponse(id, 200, build_response(content));
		}
	}
}

Another JavaScript bootstrapping method (Vegas Silverweb)

This one makes it pretty easy to build out some arbitrary-length web pages, using HTTP-in's output directly. Loading between pages is kinda slow.

string url;

string get(string page)
{
	if (page == "index")
	{
		return "<h1>Index</h1>This is the index page.<br><br><a href='page1.html'>Link to page 1</a>";
	}
	else if (page == "page1")
	{
		return "<h1>Page 1</h1>This is page 1!.<br><br><a href ='index.html'>Link to the index page.</a>";   
	}
	else return "Unknown page.";
}

string serve(string page)
{
	return "document.write(atob('" + llStringToBase64(get(page)) + "'));";
}

integer r;
load(string page)
{
	string content = "data:text/html;base64,"
		+ llStringToBase64("<html><head><base href='" + url + "/'></head><body>"
						+ "<script src='" + page + ".js' type='text/javascript'>"
						+ "</script><span r='" + (string)(++r % 10) + "</body></html>");
	
	llSetPrimMediaParams(0,[
						PRIM_MEDIA_CURRENT_URL, content,
						PRIM_MEDIA_HOME_URL, content,
						PRIM_MEDIA_AUTO_ZOOM, FALSE,
						PRIM_MEDIA_FIRST_CLICK_INTERACT, TRUE,
						PRIM_MEDIA_PERMS_INTERACT, PRIM_MEDIA_PERM_ANYONE,
						PRIM_MEDIA_PERMS_CONTROL, PRIM_MEDIA_PERM_NONE,
						PRIM_MEDIA_AUTO_PLAY, TRUE
						]);
}

default
{
	state_entry()
	{
		llRequestURL();
	}

	http_request(key id, string method, string body)
	{
		if (method == URL_REQUEST_GRANTED)
		{
			url = body;
			load("index");
		}
		else if (method == URL_REQUEST_DENIED)
		{
			llOwnerSay("Unable to get URL!");
		}
		else if (method == "GET")
		{
			string path_raw = llGetHTTPHeader(id, "x-path-info");
			if (path_raw == "")
			{
				// No path, assume index.html
				path_raw = "/index.html";
			}
			
			list path = llParseString2List(llGetSubString(path_raw, 1, -1), ["."], []);
			string page = llList2String(path, 0);
			string type = llList2String(path, 1);
			
			if (type == "js")
			{
				llHTTPResponse(id, 200, serve(page));
			}
			else if (type == "html")
			{
				llHTTPResponse(id, 100, "");
				load(page);
			}
		}
	}
}