Providing debug-useful implementation of toString and toSource.

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5082 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/core/client/JavaScriptObject.java b/user/src/com/google/gwt/core/client/JavaScriptObject.java
index 03f426c..5c2f9b8 100644
--- a/user/src/com/google/gwt/core/client/JavaScriptObject.java
+++ b/user/src/com/google/gwt/core/client/JavaScriptObject.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2008 Google Inc.
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
  * the License at
- * 
+ *
  * http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -28,12 +28,19 @@
 public class JavaScriptObject {
 
   /**
+   * Call the toSource() on the JSO
+   */
+  public native String toSource() /*-{
+    this.toSource ? this.toSource() : "NO SOURCE";
+  }-*/;
+
+  /**
    * Returns a new array.
    */
   public static native JavaScriptObject createArray() /*-{
     return [];
   }-*/;
-  
+
   /**
    * Returns an empty function.
    */
@@ -59,7 +66,7 @@
   /**
    * A helper method to enable cross-casting from any {@link JavaScriptObject}
    * type to any other {@link JavaScriptObject} type.
-   * 
+   *
    * @param <T> the target type
    * @return this object as a different type
    */
@@ -67,7 +74,7 @@
   public final <T extends JavaScriptObject> T cast() {
     return (T) this;
   }
-  
+
   /**
    * Returns <code>true</code> if the objects are JavaScript identical
    * (triple-equals).
@@ -81,9 +88,9 @@
    * Uses a monotonically increasing counter to assign a hash code to the
    * underlying JavaScript object. Do not call this method on non-modifiable
    * JavaScript objects.
-   * 
+   *
    * TODO: if the underlying object defines a 'hashCode' method maybe use that?
-   * 
+   *
    * @return the hash code of the object
    */
   @Override
@@ -92,11 +99,46 @@
   }
 
   /**
-   * Returns the results of calling <code>toString</code> in JavaScript on the
-   * object, if the object implements toString; otherwise returns "[JavaScriptObject]".
+   * catch-all toString in lieu of a better mechanism.
+   * Basic assumption here is that this code is for debugging only!
    */
   @Override
   public final native String toString() /*-{
-    return this.toString ? this.toString() : "[JavaScriptObject]";
+    var defined = function(m) { return typeof m != 'undefined'; };
+    var strip = function(s) { return s.replace(/\r\n/g, ""); };
+    // Output nodes that have outerHTML
+    if (defined(this.outerHTML))
+      return strip(this.outerHTML);
+    // Output nodes that have innerHTML
+    if (defined(this.innerHTML) && this.cloneNode) {
+      $doc.createElement('div').appendChild(this.cloneNode(true)).innerHTML;
+    }
+    // Output text nodes
+    if (defined(this.nodeType) && this.nodeType == 3) {
+      return "'" +
+        this.data.replace(/ /g, "\u25ab").replace(/\u00A0/, "\u25aa") +
+        "'";
+    }
+    // Output IE's TextRange (this code specific to IE7)
+    if (typeof defined(this.htmlText) && this.collapse) {
+      var html = this.htmlText;
+      if (html) {
+        return 'IETextRange [' + strip(html) + ']';
+      } else {
+        // NOTE(lars): using pasteHTML to place a | where the range is collapsed
+        // if *very* useful when debugging. It also, however, in certain very subtle
+        // circumstances change the range being toStringed! If you see different
+        // behaviour in debug vs. release builds (or if logging ranges changes the
+        // behaviour, comment out the 4 of the 6 lines below containing dup.
+        var dup = this.duplicate();
+        dup.pasteHTML('|');
+        var out = 'IETextRange ' + strip(this.parentElement().outerHTML);
+        dup.moveStart('character', -1);
+        dup.pasteHTML('');
+        return out;
+      }
+    }
+    return this.toString ? this.toString() : '[JavaScriptObject]';
   }-*/;
+
 }