zzyengineer / google-code-prettify

Automatically exported from code.google.com/p/google-code-prettify
Apache License 2.0
0 stars 0 forks source link

Fix for innerHTML quirk #21

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?
1. <html>
2.    <head>
3.       <title>Test</title>
4.    </head>
4. </html>

(Please include HTML, not just your source code)

What is the expected output?  What do you see instead?

The visual output looks fine. However, when selecting the formatted output
and, copying it, and then pasting into Microsoft Notepad, or Microsoft
Visual Studio .NET 2005, the code, is pasted onto a single line.

For example, if the "code" is ten lines long, when pasting, it is pasted
onto a single line.

When pasting, I would expect to get:
<html>
   <head>
      <title>Test</title>
   </head>
</html>

What I get is:
<html>   <head>      <title>Test</title>   </head></html>

What version are you using?  On what browser?
Version: 22 May 2007
IE 6.0

Please provide any additional information below.
I fixed the code.

As you know, innerHTML has a quick that is automatically collapses all of
the space within the assigned HTML. The problem is that within a PRE
element, the spaces are significant and should not be collapsed.

You solved part of that problem when you replace /(\r\n?|\n| ) /g with
'$1&nbsp;' and /\r\n?|\n/g with '<br>'.

That code helps IE to display the code correctly, but the occurrences of
\r\n|\n are still stripped. I added code in doWork() to replace all <br>'s
with \r\n. Obviously innerHTML cannot be used to do this. I used the DOM to
fix the problem. Below is the relevant code.

  function doWork() {
    var endTime = new Date().getTime() + 250;
    for (; k < elements.length && new Date().getTime() < endTime; k++) {
      var cs = elements[k];
      if (cs.className && cs.className.indexOf('prettyprint') >= 0) {

        // make sure this is not nested in an already prettified element
        var nested = false;
        for (var p = cs.parentNode; p != null; p = p.parentNode) {
          if ((p.tagName == 'pre' || p.tagName == 'code' ||
               p.tagName == 'xmp') &&
              p.className && p.className.indexOf('prettyprint') >= 0) {
            nested = true;
            break;
          }
        }
        if (!nested) {
          // fetch the content as a snippet of properly escaped HTML.
          // Firefox adds newlines at the end.
          var content = PR_getInnerHtml(cs);
          content = content.replace(/(?:\r\n?|\n)$/, '');

          // do the pretty printing
          var newContent = prettyPrintOne(content);

          // push the prettified html back into the tag.
          var elem = null;
          if (!PR_isRawContent(cs)) {
            // just replace the old html with the new
            cs.innerHTML = newContent;
            elem = cs;
          } else {
            // we need to change the tag to a <pre> since <xmp>s do not allow
            // embedded tags such as the span tags used to attach styles to 
            // sections of source code.
            var pre = document.createElement('PRE');
            for (var i = 0; i < cs.attributes.length; ++i) {
              var a = cs.attributes[i];
              if (a.specified) {
                pre.setAttribute(a.name, a.value);
              }
            }
            pre.innerHTML = newContent;
            elem = pre;
            // remove the old
            cs.parentNode.replaceChild(pre, cs);
          }

          // Replace <BR>s with \r\n via DOM.
          replaceBrsWithCrLf(elem);  //TONY - ADDED THIS LINE
        }
      }
    }
    if (k < elements.length) {
      // finish up in a continuation
      setTimeout(doWork, 250);
    }
  }

  doWork();
}

//TONY - Add the following two methods to make copy/paste work correctly.
//       This "restores" the \r\n that IE stripped out when using .innerHTML
//       property.
/** Replace <br>s with \r\n directly in DOM
  *
  * @param elem as DOM element with <br>s
  */
function replaceBrsWithCrLf(elem) {
  var brs = elem.getElementsByTagName("br");

  // Insert all the \r\n first. Cannot use ".replaceChild()" within this loop
  // because I believe the collection is "live".
  for (var i = 0; i < brs.length; i++) {
    var crlf = document.createTextNode("\r\n");
    insertAfter(crlf, brs[i]);
  }
  // Now remove all of the <BR>s
  // Need to do from bottom up, because the collection is "live"
  for (var i = brs.length; i-- > 0;) {
    brs[i].parentNode.removeChild(brs[i]);
  }
}

/** Insert newElement after targetElement, directly in DOM
  *
  * @param newElement to insert
  * @param targetElement to insert in front of
  */
function insertAfter(newElement, targetElement) {
  var parent = targetElement.parentNode;
  if (parent.lastChild == targetElement) {
    parent.appendChild(newElement);
  }
  else {
    parent.insertBefore(newElement, targetElement.nextSibling);
  }
}

Original issue reported on code.google.com by tbuch...@gmail.com on 26 Sep 2007 at 3:58

GoogleCodeExporter commented 9 years ago
I "cleaned" the new code up some. First, inserting "\r\n" causes some lines to 
be
indented by one space. Instead, insert only "\r". Below is the new updated 
code. Note
that the insertAfter method is no longer needed.

//GDQV - Added the following method to make copy/paste work correctly. 
//       This "restores" the line breaks that IE stripped out when using 
//       .innerHTML property. 
/** Replace <br>s with \r directly in DOM 
  * 
  * @param elem as DOM element with <br>s 
  */ 
function replaceBrsWithCrLf(elem) { 
  var brs = elem.getElementsByTagName("br"); 

  // Insert all the \r first. Work bottom to top, because I believe the 
  // collection is "live". 
  // Inserting \r\n causes a leading space to appear on some lines. 
  for (var i = brs.length; i-- > 0; ) { 
    var crlf = document.createTextNode("\r"); 
    brs[i].parentNode.replaceChild(crlf, brs[i]); 
  } 
}

Original comment by tbuch...@gmail.com on 27 Sep 2007 at 3:26

GoogleCodeExporter commented 9 years ago
I can repeat the bug.  I'd rather not use \r for newlines since that's counter 
to the
spec, and CR isn't used as eol in any OS except commodores and old versions of 
MacOS.

Original comment by mikesamuel@gmail.com on 23 Oct 2007 at 3:12

GoogleCodeExporter commented 9 years ago
I played around a bit.  

Thanks for suggesting the \r trick.  I'm leery of how it'll affect other 
browsers,
and this rewriting step isn't cheap so I'd rather not do it for all the 
browsers that
don't have this problem.

I've managed to avoid browser detection thus-far, but I think using it here 
yields
simpler code.

I fixed it by replacing <br> with \r\n but only on IE 6.

Original comment by mikesamuel@gmail.com on 23 Oct 2007 at 4:49

GoogleCodeExporter commented 9 years ago
I see you say you replaced <br> with \r\n. Notice my post on Sept 26, 2007; 
using 
\r\n causes some lines to be indented by a single space. I did not track down 
the 
reason/conditions for those lines, but changing to only \r fixed that problem.

Original comment by tbuch...@gmail.com on 24 Oct 2007 at 4:42

GoogleCodeExporter commented 9 years ago
@r62. use '\r' instead of '\r\n' on IE6 to separate lines.

Original comment by mikesamuel@gmail.com on 14 Jan 2009 at 8:13