JavaScript, Objective: Display Accurate Source Code

291 views Asked by At

The objective is to be able to click a link to bring up a new window that displays the formatted source code from the parent window.

Through research, I was able to accomplish most of the task, but I need the new window to display the source code exactly as it is in the parent, indentation and all. Currently, HTML entities are being displayed as their display counterpart (so it outputs the actual trademark instead of the source's ™).

I was thinking of looping through a bunch of entities and doing a string replace, as you'll see in my example, but I'm thinking that the logic is off, and I don't know what's going on.

Entities in particular: • (bull), & (amp), ® (reg), ™ (trade), < (lt), > (gt)

Here's the code the hyperlink launches that I have so far, bonus to anyone who can figure out what the closing body tag is being output on a second line (I'm stumped):

Thank you very much in advance for any direction leading to resolution.

function viewSource( e )
{
    e.preventDefault( );

    var source = '<!DOCTYPE html>' + "\n"
               + '<html xmlns="http://www.w3.org/1999/xhtml">' + "\n\n"
               + "\t" + document.getElementsByTagName( 'html' )[0].innerHTML + "\n\n"
               + '</html>';

    entities = [
        new Entity( '<', '&lt;' ),
        new Entity( '>', '&gt;' )
    ];

    for ( var i = 0; i < entities.length; i++ )
    {
        var regex = new RegExp( entities[i].entity, 'g' );
        source = source.replace( regex, entities[i].html );
    }

    source = '<pre>' + source + '</pre>';

    var sourceWindow = window.open( '', '_blank' );
    sourceWindow.document.write( source );
    sourceWindow.document.close( );

    if ( window.focus )
    {
        sourceWindow.focus( );
    }
}

Update (still unsolved mysteries):

I tried the suggestion to use an AJAX request, but it is still yield the same results (line 11 shows new Entity( '<', '<'), ). Here's what the AJAX attempt looks like:

function viewSource( e )
{
    e.preventDefault( );

    var xmlhttp;

    if ( window.XMLHttpRequest )
    {
        // IE7+, Fx, Chrome, Opera, Safari
        xmlhttp = new XMLHttpRequest( );
    }
    else
    {
        // IE6, IE5
        xmlhttp = new ActiveXObject( 'Microsoft.XMLHTTP' );
    }

    xmlhttp.onreadystatechange = 
        function ( )
        {
            if ( xmlhttp.readyState == 4 && xmlhttp.status == 200 )
            {
                var source = xmlhttp.responseText.replace( /</g, '&lt;' ).replace( />/g, '&gt;' );
                source = '<pre>' + source + '</pre>';

                var sourceWindow = window.open( '', '_blank' );
                sourceWindow.document.write( source );
                sourceWindow.document.close( );

                if ( window.focus )
                {
                    sourceWindow.focus( );
                }
            }
        };

    xmlhttp.open( 'GET', '/', true );
    xmlhttp.send( );
}
2

There are 2 answers

1
jeffjenx On BEST ANSWER

So, thanks to @SLaks for all the direction. I was able to achieve close to what I wanted, but there are still a few little quirks that I may just have to live with. I am specifically trying to avoid using any libraries such as jQuery.

The issues that are still coming up are as follows: - The output source is displaying self-closing tags without the final slash. So, for example, <br /> is being displayed as <br>. - The closing body tag is being wrapped to the line below, for some reason. It should be rendering as:

    </body>

</html>

But, instead it renders as:

[tab]
</body>

</html>

Despite this, I am still pleased with the results and it should suffice. However, if anyone has any better solutions that do not require 3rd party libraries, that would be greatly appreciated. Here is the latest attempt:

function htmlEntity( character )
{
    switch ( character.charCodeAt( 0 ) )
    {
        case 174:  return '&reg;';    break;
        case 233:  return '&eacute;'; break;
        case 8211: return '&ndash;';  break;
        case 8220: return '&ldquo;';  break;
        case 8221: return '&rdquo;';  break;
        case 8226: return '&bull;';   break;
        case 8482: return '&trade;';  break;
        default:   return character;
    }

    if ( character.charCodeAt( 0 ) > 127 )
    {
        return '&#' + character.charCodeAt( 0 ) + ';';
    }

    return character;
}

function viewSource( e )
{
    e.preventDefault( );

    var source = '<!DOCTYPE html>' + "\n"
               + '<html xmlns="http://www.w3.org/1999/xhtml">' + "\n\n"
               + "\t" + document.getElementsByTagName( 'html' )[0].innerHTML + "\n\n"
               + '</html>';

    var entities = [ '®' ];
    for ( i = 0; i < entities.length; i++ )
    {
        source.replace( entities[i], htmlEntity( entities[i] ) );
    }

    var temp = '';
    for ( i = 0; i < source.length; i++ )
    {
        temp += htmlEntity( source.charAt( i ) );
    }

    var pre = document.createElement( 'pre' );
    pre.textContent = temp;

    source = '<pre>' + pre.innerHTML + '</pre>';

    var sourceWindow = window.open( '', '_blank' );
    sourceWindow.document.write( source );
    sourceWindow.document.close( );

    if ( window.focus )
    {
        sourceWindow.focus( );
    }
}
5
SLaks On

You aren't properly escaping your source.
You should build a DOM element with a text representation of your source.

Using jQuery:

$('<pre />').text(source).appendTo(document.body);