Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created January 19, 2012 15:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bennadel/1640716 to your computer and use it in GitHub Desktop.
Save bennadel/1640716 to your computer and use it in GitHub Desktop.
Using An IFrame To Override document.write() Inside A DOM (Document Object Model) Sandbox
<!DOCTYPE html>
<html>
<head>
<title>Loading GitHub Gists After Page Content Has Loaded</title>
<style type="text/css">
.gist,
pre {
font-size: 12px ;
}
</style>
<!-- Load the jQuery library a and the Gist plugin. -->
<script type="text/javascript" src="../jquery-1.7.1.js"></script>
<script type="text/javascript" src="./jquery.gist-frame.js"></script>
<!-- Load the Gist after DOM ready. -->
<script type="text/javascript">
$(function(){
// Get the placeholder.
var placeholder = $( "#placeholder" );
// Get the gist with the given ID. This will come back
// as both a hash of file names and an ordered array.
var gistResult = $.getGist( "1600811" );
// When the gist has loaded, append the contents to the
// rendered DOM.
gistResult.done(
function( gist ){
// Empty the placeholder.
placeholder.empty();
// Get the ordered files.
var ordered = gist.ordered;
// Add each gist to the content.
for (var i = 0 ; i < ordered.length; i++){
// Add a title for the gist.
placeholder.append(
"<h3>" + ordered[ i ].fileName + "</h3>"
);
// Add the gist content.
placeholder.append( ordered[ i ].content );
}
}
);
});
</script>
</head>
<body>
<h1>
Loading GitHub Gists After Page Content Has Loaded
</h1>
<h2>
Gists From GitHub:
</h2>
<div id="placeholder">
Loading...
</div>
<p>
This page has finised loading.
</p>
</body>
</html>
// Define a sandbox in which to create the Gist loader jQuery plugin.
// This Gist loader only works with public gists. It will load all of
// the files (in a single request) and then return an array of loaded
// files (with the ability to access by file name).
(function( $ ){
// I flag whether or not a stylesheet has been appending to the
// current document. Since all Gist requests share the same
// style, we can write it to the active document once and then
// disregard all subsequent Link tags.
var stylesheetIsEmbedded = false;
// When the Gist comes back, the first call to the write() method
// writes out the stylesheet. This takes the value and appends it
// to the head of the document.
var injectStyleSheet = function( value ){
// Append the stylesheet Link tag.
$( "head:first" ).append( value );
};
// I determind if the given Gist meta tag content is the name of
// the parent Gist? Or, if this is just a control meta tag.
var isFilenameMetaTag = function( metaTag ){
// Create a pattern that will find the meta tags that are NOT
// proper file names.
var controlText = new RegExp(
"^\\s*(view raw|this gist|github)",
"i"
);
// Return true if the given text does NOT contain an action
// text (that is, if the control text cannot be found in the
// given text).
return( metaTag.text().search( controlText ) === -1 );
};
// I take the Gist content, parse it into HTML, and return the
// collection of Gist files, indexed by order and by filename.
var parseGistContent = function( value ){
// Create a hash of Gist files.
var files = {};
// We'll also want to list the files by Index, if the user
// wants that information.
files.ordered = [];
// Parse the Gist HTML in a local DOM tree.
var gistContent = $( value );
// Get all of the files in the gist.
gistContent.find( "div.gist-file" ).each(
function(){
// Get a jQuery reference to the current gist node.
var gistFile = $( this );
// Get the name of the file. For this, we will return
// the content of the first Meta anchor that doesn't
// contain a syntactic link.
var metaTags = gistFile.find( "div.gist-meta a" )
.filter(
function(){
// Only keep this value if it doesn't
// contain a useless value.
return( isFilenameMetaTag( $( this ) ) );
}
)
;
// Get the file name from the first filtered Meta
// anchor tag.
var fileName = $.trim( metaTags.first().text() );
// Get the content of the file. Each file will need
// to be re-wrapped in its own Gist div.
var content = $( "<div class='gist'></div>" )
.append( gistFile )
;
// Add the file the collection, indexed by name.
files[ fileName ] = {
fileName: fileName,
content: content
};
// Add this file to the "ordered" list as well.
files.ordered.push( files[ fileName ] );
}
);
// Return the Gist file collection.
return( files );
};
// ------------------------------------------------------ //
// ------------------------------------------------------ //
// Define the actual script loader.
$.getGist = function( gistID ){
// Create a deferred value for our Gist content.
var result = $.Deferred();
// Create a blank iframe. This will be used to actually load
// the Gist in another document context so that we dont'
// corrupt the active document.
var iframe = $( "<iframe src='about:blank'></iframe>" )
.hide()
.prependTo( "html" )
;
// Get the iframe document.
var iframeDocument = iframe[ 0 ].contentWindow.document;
// Create a function that will handle the first write defined
// by the gist (writing the stylesheet).
var writeStyleSheet = function( value ){
// Check to make sure the stylesheet is not yet embedded.
if (!stylesheetIsEmbedded){
// Inject the stylesheet.
injectStyleSheet( value );
// Flag the stylesheet as having been injected.
stylesheetIsEmbedded = true;
}
// Change the proxy write method.
iframeDocument.proxyWrite = writeGistContent;
};
// Create a function that will handle the second write
// defined by the gist (writing the Gist content).
var writeGistContent = function( value ){
// Detach the iframe - we no longer need it.
delete( iframeDocument.proxyWrite );
iframe.remove();
// Parse the gist content into files.
var files = parseGistContent( value );
// Resolve the files promise.
result.resolve( files );
};
// Assign the first write proxy.
iframeDocument.proxyWrite = writeStyleSheet;
// Now that we have our proxy method hooked up, let's inject
// the Gist script into the iFrame. Notice that we are
// overriding the iframe's document.write() method to always
// point to the proxyWrite() method. This way, we can reassign
// the proxyWrite() binding without changing document.write()
// more than once.
var markupBuffer = [
"<script type='text/javascript'>",
"document.write = function( value ){",
"document.proxyWrite( value );",
"};",
"</script>",
"<script",
"type='text/javascript'",
"src='https://gist.github.com/" + gistID + ".js'>",
"</script>"
];
// Write the new content to the iframe.
iframeDocument.write(
markupBuffer.join( " " )
);
// Return the promise of the gist.
return( result.promise() );
};
})( jQuery );
// Now that we have our proxy method hooked up, let's inject
// the Gist script into the iFrame. Notice that we are
// overriding the iframe's document.write() method to always
// point to the proxyWrite() method. This way, we can reassign
// the proxyWrite() binding without changing document.write()
// more than once.
var markupBuffer = [
"<script type='text/javascript'>",
"document.write = function( value ){",
"document.proxyWrite( value );",
"};",
"</script>",
"<script",
"type='text/javascript'",
"src='https://gist.github.com/" + gistID + ".js'>",
"</script>"
];
// Write the new content to the iframe.
iframeDocument.write(
markupBuffer.join( " " )
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment