PDA

View Full Version : Virtual Files!


rnd me
06-21-2007, 09:45 PM
This function will produce a URL that appears to be a file.

There are two versions:
1. a 500-pound gorilla version that accepts almost anything as input.
2. a lightweight version that turns strings into virtual HTML files.


First I'll mention the one major drawback: IE doesn't recognize data urls (http://en.wikipedia.org/wiki/Data:_URI_scheme) yet.

For a Live Demo, paste this into your addressbar and click go. (press back to return).
data:text/html;charset=utf-8;base64,PGI+aGVsbG8gd29ybGQ8L2I+
//output of: dataUrl("<b>hello world<\/b>", "text\/html")

With that out of the way, here's the code:



function dataUrl(data, mimeType){ // turns a string into a url that appears as a file. (to ff/op/saf)
encType= (!!btoa) ? ";base64" : "";
var esc = (!!encType) ? function(d){return btoa(d);} : function(d){return escape(d);};
if(!mimeType){mimeType= (data.nodeName) ? "text\/html" :"text\/plain";};
b="data:"+mimeType+";charset="+document.characterSet+"+encType+",";

if ("string number date boolean function".indexOf(typeof data) > -1){ b+=esc(data.toString()); return b; };
if ( data.constructor==Array){b+= esc( data.join("") ); return b; };
if(typeof data=="xml"){b+=esc(data.toSource()); return b;} //FF2 xml frag/doc
//for more complicated data, attempt to determine the format.
if(typeof data=="object"){
if(!!data.value && !!data.value.length){b+=esc(data.value); return b;}; //input tags w/content
if(!!data.innerHTML){b+=esc(data.innerHTML); return b;} //HTML tag
if(!!data.length){ //weird stuff like nodelists
var G=function(ob){r=[]; i=0;
for(i;i<ob.length;i++){
if(dataUrl(ob)) r=dataUrl(ob[i]);} return r.join("\n");};//end g
return (b+G(data));}//end if object w/length
if(!! eval(data.toSource()) ){b+=esc(data.toSource()); return b;}; //JSON
}//end if object
return;
} //end function dataUrl

function dataUrlStr(data){ return ("data:text\/html,"+escape(data));}



Usage: dataUrl(data, mimeType)
data- The desired contents of the file
mimeType- The type of content you want the browser to see.

output: An encoded string representation of the file; an href.
The output 'file' could be any text-based file format, depending upon what you set mimeType to be:
-HTML document (yawn) (text/html)
-Rss feed (text/xml)
-M3U playlist (audio/mpegurl)
-Excel spreadsheet, CSV (application/vnd.ms-excel)
-JavaScript (text/javascript)
-StyleSheet (text/css)
- You get the idea...

Input Details:
-Can accept [I]almost any javascript type: strings, numbers, arrays (which it will flatten), nodes, node lists, xml objects in FF2, functions (for which it outputs the source code), complex JSON objects, and dates. (whew, I think that's all of 'em...)
-if you want to output an element as html, or anything else as text, you can skip the mimeType argument.

Notes:
-You will most likely want to assign the output to a link href or the .scr of a script, stylesheet, or iframe, or to window.location.href.
-If you assign it to an iframe or object, it will load immediately.
-assigning it to a (<a>) tag will let you 'right-click, SaveAs' the file.
-clicking the link will open the 'file' in the browser just like a regular file.
-output an html document that has scripts, and they execute immediately.
-the cool thing about this is that the script will have a clean context. (no global variables from the parent).
-if used in an iframe- it can push to main page via window.top .
-if used in with an <object>, it will be completely private.

Caveats:
-doesn't work with IE yet.
-'saving As' doesn't offer a default file extention.
-there is 2kb limit for safari and opera URLs.
-search engines cannot see the links.
-XmlHTTP Requests from within usually don't work.
Examples:
To save my fingers, I will utilize the follow shortcut functions in these examples:

function el(tid) {return document.getElementById(tid)};
function tags(tid) {return document.getElementsByTagName(tid)};


//test
tags("a")[0].href=dataUrlStr("<b>Hello World!<\/b>")

//make a 2D array into an Excel SpreadSheet - (works with 1D lists also)
var grades= [["Subject" , "Grade"],
["Math" , "A" ],
["English", "B+" ],
["Gym", "C" ],
["Art", "A-" ],
["Science", "A+" ] ]
var exl=grades.join("\n").toString().replace(/,/g , "\t")
window.location.href=dataUrl(exl, "application/vnd.ms-excel")
//note: if data contains commas: loop through main, .join('\t') each index.


//execute script in a fresh enviroment:
window.testme="123456789"; //make a new global
var testJs="<script>alert('hidden window.testme='+ window.testme )<\/script>";
var fr= document.createElement("iframe");
fr.id="hiddenframe";
fr.style.display='none';
document.body.appendChild(fr); //add a hidden frame
alert("main window.testme="+window.testme);
el("hiddenframe").src=dataUrlStr(testJs);


//re-using the above example, pass existing functions to the hidden frame, and return the result.
var tJs="<script>";
tJs+= [tags, el].join("\n"); //bundle a couple functions
tJs+= "\n alert('hidden window has '+tags('*').length)"
tJs+= "<\/script>";
el("hiddenframe").src=dataUrlStr(tJs)


//open a playlist
var plTxt="#EXTM3U\n#EXTINF:202,Minuet in G\nhttp:\/\/www.mfiles.org.uk\/downloads\/beethoven-minuet-in-G.mp3"
el("hiddenframe").src=dataUrl(plTxt, "audio/mpegurl"


//open all the 'code' on this page as a plain text document.
function ob2Array(ob){var r=[]; for(var z=0; z<ob.length;z++){r[z]=ob[z]};return r};
function group( List, f) //applies a function (f) to an array and returns result...
{var z=0; Ray=[]; len=List.length;for (z;z<len;z++){Ray[Ray.length]=f(List[z]); }
return Ray;};
var codes=group(ob2Array(tags("pre")), function(x){if(x.className=="alt2")return x.textContent});
window.location.href=dataUrl(codes)


//make this thread into an rss feed
function tag(nd, tx){ return ["<",nd,">",tx,"<\/",nd,">"].join("")}
function dp (s) {var d = new Date();d.setTime(Date.parse(s)); return d.toUTCString()}
var X=PHP.htmlspecialchars
var chan=[]; var y = new Date();
chan[0]=tag("title", "CodingForums.com");
chan[1]=tag("link", window.location.href);
y.setTime(ajax_last_post+"000")
chan[2]=tag("pubDate", y.toUTCString())
chan[3]=tag("link", "http:\/\/www.codingforums.com\/")

var codes=Group(ob2Array(tags("table")), function(x){
var i=[];d=""; p=d; t=d; l=d;j=d;a=0;
if(x.id.substr(0,4)=="post"){

p=tag("pubDate", dp(x.rows[0].cells[0].textContent.replace(/-/g, "\/")));
t=tag("title", document.title)
l=tag("link", "http:\/\/www.codingforums.com\/showpost.php?p="+tags("table")[5].id.substr(4))
ta=x.rows[1].cells[0].getElementsByTagName('a')[0]
a="<author>"+ta.href+" ("+ta.textContent+") <\/author>";
d=X(x.rows[1].cells[1].textContent.toString())



j=[d,t,p,l,a].join("\n");
return tag('item', j)
}//end if
});

tags("a")[2].href=dataUrl(tag("channel", chan.join("\n") + codes), "text\/xml")


So, i think that should be enough examples to get going.

[I]"The only limitation is your imagination"

antimatter15
06-22-2007, 06:52 PM
so, which one is the 500 pound gorilla version, and which one is the light version?

And the script isn't working on me: I'm using firefox 2.004

rnd me
06-23-2007, 11:04 PM
so, which one is the 500 pound gorilla version, and which one is the light version?




// do all function (updated)
function dataUrl(data, mimeType){ // turns a string into a url that appears as a file. (to ff/op/saf)
encType= (!!btoa) ? ";base64" : "";
var esc = (!!encType) ? function(d){return btoa(d);} : function(d){return escape(d);};
if(!mimeType){mimeType= (data.nodeName) ? "text\/html" :"text\/plain";};
b="data:"+mimeType+";charset="+document.characterSet+encType+",";

if ("string number date boolean function".indexOf(typeof data) > -1){ b+=esc(data.toString()); return b; };
if ( data.constructor==Array){b+= esc( data.join("") ); return b; };
if(typeof data=="xml"){b+=esc(data.toSource()); return b;} //FF2 xml frag/doc
//for more complicated data, attempt to determine the format.
if(typeof data=="object"){
if(!!data.value && !!data.value.length){b+=esc(data.value); return b;}; //input tags w/content
if(!!data.innerHTML){b+=esc(data.innerHTML); return b;} //HTML tag
if(!!data.length){ //weird stuff like nodelists
var G=function(ob){r=[]; i=0;
for(i;i<ob.length;i++){
if(dataUrl(ob[i])) r[i]=dataUrl(ob[i]);} return r.join("\n");};//end g
return (b+G(data));}//end if object w/length
if(!! eval(data.toSource()) ){b+=esc(data.toSource()); return b;}; //JSON
}//end if object
return;
} //end function dataUrl







//light version- string to .htm
function dataUrlStr(data){ return ("data:text\/html,"+escape(data));}



And the script isn't working on me: I'm using firefox 2.004


* was a grammar error in O.P...

i was using my new forum formatter, which allowed me make things look nice. unfortunately, it apparently likes to re-arrange quotation marks...

antimatter15
06-29-2007, 09:42 PM
Two Questions:

1. Is there any way to decode a data url without loading the data url?
2. Is it possible to encode an array filled with more than just strings (functions, booleans, integers, etc.)

rnd me
07-03-2007, 09:38 AM
Two Questions:

1. Is there any way to decode a data url without loading the data url?


I am not sure what you mean exactly. but does this help?


// to unpack, match the function below with the function above
used for the initial encoding.

function dataUrlStr2Str(u){return unescape(u.split(",")[1]) }
function dataUrl2Str(u){return atob(u.split(",")[1]) }




Two Questions:
2. Is it possible to encode an array filled with more than just strings (functions, booleans, integers, etc.)

2 options:'
a: create a loop and roll trough create an array of urls,
b: make a string first


dataUrl([{a:1},false,21,"bob",7*11].toSource().slice(1,-1), "text\/javascript");