Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I recently wrote a small demo that allows users to send and receive files from an HTML5 Javascript application. It's all client-side. I thought this would be a good place to get feedback about what I've done.

It's hosted here: http://danielsadventure.info/HTML5FileDemo/

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>HTML5 File Upload Demo With Saxon-CE and ACE</title>
    <style>
        /* ACE won't work without explicit CSS. */
        .aceEditor
        {
            position: relative;
            width: 500px;
            height: 200px;
        }
    </style>
    <script src='Scripts/ace/ace.js'></script>
    <script src='Scripts/saxon-ce/Saxonce.nocache.js'></script>
    <!--
        FileSaver.js is an open-source Javascript project that provides support for the HTML5 FileSaver API
        on browsers that do not natively support it.
    -->
    <script src='Scripts/FileSaver.js'></script>
</head>
<body>
    Input XML
    <br />
    <div class='aceEditor' id='aceInputXml' style='float:left;'>&lt;test&gt;
    &lt;testing&gt;
        1 2 3
    &lt;/testing&gt;
&lt;/test&gt;</div>
    <div>
        To input XML, drag-and-drop a file into the text editor to the left,
        use the file input below, or key XML into the text editor.
        <br />
        <input id='xmlFileInput' type='file' />
    </div>
    <br style='clear:both;' />
    Input XSLT    
    <br />
    <div class='aceEditor' id='aceInputXslt' style='float:left;'>&lt;!-- 
    Note: This sample XSLT is an "identity" transformation.
    The result of applying this transformation to a document should be the original document.
--&gt;
&lt;xsl:stylesheet version=&quot;1.0&quot;
 xmlns:xsl=&quot;http://www.w3.org/1999/XSL/Transform&quot;&gt;
    &lt;xsl:template match=&quot;node()|@*&quot;&gt;
      &lt;xsl:copy&gt;
        &lt;xsl:apply-templates select=&quot;node()|@*&quot;/&gt;
      &lt;/xsl:copy&gt;
    &lt;/xsl:template&gt;
&lt;/xsl:stylesheet&gt;</div>
    <div>
        To input XSLT, drag-and-drop a file into the text editor to the left,
        use the file input below, or key XSLT into the text editor.
        <br />
        <input id='xsltFileInput' type='file' />
    </div>
    <br style='clear:both;' />
    Output
    <div class='aceEditor' id='aceOutputXml'></div> 
    <br />
    <input type='button' id='btnTransform' value='Transform' />   
    <input type='button' id='btnSave' value='Save' />
</body>
<script>
    (function () {
        'use strict';
        var aceInputXml, aceInputXslt, aceOutputXml, editors, saxon;

        setupAce();
        setupFileDragAndDrop();
        setupFileInput();
        setupFileDownload();
        setupSaxonce();

        // Initialize the ACE editors and save their object references.
        function setupAce() {
            var x;
            aceInputXml = ace.edit('aceInputXml');
            aceInputXslt = ace.edit('aceInputXslt');
            aceOutputXml = ace.edit('aceOutputXml');
            editors = [aceInputXml, aceInputXslt, aceOutputXml];
            for (x in editors) {
                editors[x].setTheme('ace/theme/monokai');
                editors[x].getSession().setMode('ace/mode/xml');
            }
        }

        // Rig up the Input XML and Input XSLT ACE editors so that the user can populate them
        // by dragging and dropping a file that contains XML.
        function setupFileDragAndDrop() {
            var inputXml, inputXslt;
            inputXml = document.getElementById('aceInputXml');
            inputXslt = document.getElementById('aceInputXslt');
            addFileDragAndDropEventListeners(inputXml, aceInputXml);
            addFileDragAndDropEventListeners(inputXslt, aceInputXslt);

            function addFileDragAndDropEventListeners(aceInputDiv, aceObject) {
                aceInputDiv.addEventListener('dragover', function (e) {
                    stopEvent(e);
                });

                aceInputDiv.addEventListener('drop', function (e) {
                    putFileContentsInAceEditor(e.dataTransfer.files[0], aceObject);
                    stopEvent(e);
                });

                function stopEvent(e) {
                    e.stopPropagation();
                    e.preventDefault();
                }
            } // end addFileDragAndDropEventListeners
        } // end setupFileDragAndDrop        

        // Set up the HTML5 file inputs so that the user can populate the ACE editors
        // by selecting a file.
        function setupFileInput() {
            (function () {
                var xmlFileInput;
                xmlFileInput = document.getElementById('xmlFileInput');
                xmlFileInput.addEventListener('change', function (e) {
                    var file;
                    file = e.srcElement.files[0];
                    putFileContentsInAceEditor(file, aceInputXml);
                });
            })();
            (function () {
                var xsltFileInput;
                xsltFileInput = document.getElementById('xsltFileInput');
                xsltFileInput.addEventListener('change', function (e) {
                    var file;
                    file = e.srcElement.files[0];
                    putFileContentsInAceEditor(file, aceInputXslt);
                });
            })();
        }

        // Setup the button that the user can click to download the output.
        function setupFileDownload() {
            var button;
            button = document.getElementById('btnSave');
            button.addEventListener('click', function (e) {
                var outputXmlStr, blob;
                outputXmlStr = aceOutputXml.getSession().getValue();
                // If the string is null or empty, do nothing.
                if (!outputXmlStr)
                    return;
                blob = new Blob([outputXmlStr], { type: 'text/plain' });
                // Use the FileSaver.js interface to download the file.
                saveAs(blob, 'Output.xml');
            });
        }

        // A small function that takes a file and an ACE editor object.
        // The function reads the file and copies its contents into the ACE editor.
        function putFileContentsInAceEditor(file, aceEditor) {
            var reader, text;
            reader = new FileReader();
            reader.onload = (function (file) {
                text = file.target.result;
                aceEditor.getSession().setValue(text);
            });
            reader.readAsText(file);
        }

        function setupSaxonce() {
            // This object is required by Saxon.
            // Saxon will automatically invoke this function when it loads.
            window.onSaxonLoad = function () {
                saxon = Saxon;
                setupTransformButton(Saxon);
            };
        }

        // Note that this gets called when setupSaxonce runs.
        // This ensures that the Saxon object is available when the user clicks the [Transform] button.
        function setupTransformButton(Saxon) {
            var button;
            button = document.getElementById('btnTransform');
            button.addEventListener('click', function (e) {
                var inputXmlStr, inputXsltStr, outputXmlStr,
                    inputXmlDoc, inputXsltDoc, outputXmlDoc, processor;
                // Get the input XML and XSLT strings from the ACE editors.
                inputXmlStr = aceInputXml.getSession().getValue();
                inputXsltStr = aceInputXslt.getSession().getValue();
                // Transform the inputs into Saxon XML documents.
                inputXmlDoc = Saxon.parseXML(inputXmlStr);
                inputXsltDoc = Saxon.parseXML(inputXsltStr);
                // Get an XSLT20Processor object that will apply the transformation.
                processor = Saxon.newXSLT20Processor(inputXsltDoc);
                // Apply the transformation.
                outputXmlDoc = processor.transformToDocument(inputXmlDoc);
                outputXmlStr = Saxon.serializeXML(outputXmlDoc);
                // Put the results of the transformation in the output ACE editor.
                aceOutputXml.getSession().setValue(outputXmlStr);
            });
        }
    })();    
</script>
</html>
share|improve this question
add comment

1 Answer

Your code looks fine to me,

I would probably have aceInputXml, aceInputXslt and aceOutputXml be properties of 'editors'.

Then you could slightly rewrite setupAce as

function setupAce() 
{
  var key;
  editors = 
  {
    InputXml  : ace.edit('aceInputXml'),
    InputXslt : ace.edit('aceInputXslt'),
    OutputXml : ace.edit('aceOutputXml')
  }
  for (key in editors)
  {
    editors[key].setTheme('ace/theme/monokai');
    editors[key].getSession().setMode('ace/mode/xml');
  }
}

In setupFileDragAndDrop you could probably make it so that you dont have to re-use the id's like 'aceInputXml' again ( DRY ). Also you should consider putting stopEvent out of addFileDragAndDropEventListeners, since you will now have 2 stopEvent functions ?

setupFileInput contains 50% copy pasted code, not DRY.

setupTransformButton contains a lot of declarations and assignments that could be merged. Also since you don't keep the button value around you could simply replace

function setupTransformButton(Saxon) {
            var button;
            button = document.getElementById('btnTransform');
            button.addEventListener('click', function (e) {
                var inputXmlStr, inputXsltStr, outputXmlStr,
                    inputXmlDoc, inputXsltDoc, outputXmlDoc, processor;
                // Get the input XML and XSLT strings from the ACE editors.
                inputXmlStr = aceInputXml.getSession().getValue();
                inputXsltStr = aceInputXslt.getSession().getValue();
                // Transform the inputs into Saxon XML documents.
                inputXmlDoc = Saxon.parseXML(inputXmlStr);
                inputXsltDoc = Saxon.parseXML(inputXsltStr);
                // Get an XSLT20Processor object that will apply the transformation.
                processor = Saxon.newXSLT20Processor(inputXsltDoc);
                // Apply the transformation.
                outputXmlDoc = processor.transformToDocument(inputXmlDoc);
                outputXmlStr = Saxon.serializeXML(outputXmlDoc);
                // Put the results of the transformation in the output ACE editor.
                aceOutputXml.getSession().setValue(outputXmlStr);
            });
        }

with

function setupTransformButton(Saxon)
{
  document.getElementById('btnTransform').addEventListener('click', function (e) 
  {
    /* Get the input XML and XSLT strings from the ACE editors. */
    var inputXmlStr  = aceInputXml.getSession().getValue(), 
        inputXsltStr = aceInputXslt.getSession().getValue(); 
    /* Transform the inputs into Saxon XML documents. */
    var inputXmlDoc  = Saxon.parseXML(inputXmlStr),
        inputXsltDoc = Saxon.parseXML(inputXsltStr);
    /* Get an XSLT20Processor object that will apply the transformation */
    var processor    = Saxon.newXSLT20Processor(inputXsltDoc);
    /* Apply the transformation */
    var outputXmlDoc = processor.transformToDocument(inputXmlDoc),
        outputXmlStr = Saxon.serializeXML(outputXmlDoc);
    /* Put the results of the transformation in the output ACE editor. */
    aceOutputXml.getSession().setValue(outputXmlStr);
  });
}

After writing this, I guess it really is a matter of taste, something to consider rather than a hard rule.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.