You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by we...@apache.org on 2009/06/26 16:17:13 UTC

svn commit: r788713 - in /myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/myfaces/_impl: _util/_HtmlStripper.js _util/_Utils.js xhrCore/_AjaxResponse.js

Author: werpu
Date: Fri Jun 26 14:17:13 2009
New Revision: 788713

URL: http://svn.apache.org/viewvc?rev=788713&view=rev
Log:
https://issues.apache.org/jira/browse/MYFACES-2260

Modified:
    myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_HtmlStripper.js
    myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_Utils.js
    myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/myfaces/_impl/xhrCore/_AjaxResponse.js

Modified: myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_HtmlStripper.js
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_HtmlStripper.js?rev=788713&r1=788712&r2=788713&view=diff
==============================================================================
--- myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_HtmlStripper.js (original)
+++ myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_HtmlStripper.js Fri Jun 26 14:17:13 2009
@@ -19,137 +19,139 @@
  */
 
 /**
+ * <p>
  * A simple html parser class
  * that handles the stripping of the body
  * and head area properly
- *
+ * </p>
+ * <p>
  * we assume that the ajax response encapsules
  * the html elements
- * so we only have to strip those
+ * so we only have to strip those really needed by the response
+ * </p>
+ * Due to this fact we can make several shortcuts...
+ * First we only have to parse either for html or body for now
+ * hence we can skip code and pre parsing to skip embedded tags as well
+ *
+ * <p>
+ * secondly once we have found our section we can parse bottom up
+ * which adds an additional speedup to our parsing process!
+ * </p>
+ * <p>
+ * Note we do not solve the head and body stripping via regular expressions
+ * because there are usecases where this does not work out
+ * for instance comments with embedded head and body sections
+ * or javascripts with head and body in strings..
+ *
+ * We tried to solve that that way in the first place but due to
+ * the nature of things a minimal semantic understanding in the parsing
+ * process is needed to strip everything out correctly which is not entirely
+ * given via normal pattern matching, so we went the hard route
+ * of implementing everything with a minimal parser
+ * which tries to cover the semantics needed to strip out the correct content!
+ * </p>
  */
 
-
 _reserveMyfacesNamespaces();
 
-if(!myfaces._impl._util._LangUtils.exists(myfaces._impl._util,"_HtmlStripper")) {
+if (!myfaces._impl._util._LangUtils.exists(myfaces._impl._util, "_HtmlStripper")) {
 
     myfaces._impl._util._HtmlStripper = function() {
     };
 
-
+    myfaces._impl._util._HtmlStripper.prototype.BEGIN_TAG = "html";
+    myfaces._impl._util._HtmlStripper.prototype.END_TAG = "lmth";
 
     /**
-    * parses the token array
-    * and handles the incoming tokens via an ll
-    * parsing and backtracking of one char
-    * the handling of the parsing is done via
-    * recursive descension
-    *
-    * note in the subroutines the tokenPos must be at the last char of the operation
-    * so that the
-    */
-    myfaces._impl._util._HtmlStripper.prototype.parse = function(theString) {
+     * parses the token array
+     * and handles the incoming tokens via an ll
+     * parsing and backtracking of one char
+     * the handling of the parsing is done via
+     * recursive descension
+     *
+     * note in the subroutines the tokenPos must be at the last char of the operation
+     * so that the
+     */
+    myfaces._impl._util._HtmlStripper.prototype.parse = function(theString, tagNameStart, tagNameEnd) {
         this.tokens = theString.split("");
+        this.tagAttributes = {};
+
         this._contentStart = -1;
         this._contentEnd = -1;
         this._tokenPos = 0;
 
         this._tokenForward = 1;
+
+
+
+        if ('undefined' == typeof tagNameStart || null == tagNameStart) {
+            this.tagNameStart = myfaces._impl._util._HtmlStripper.prototype.BEGIN_TAG;
+        } else {
+            this.tagNameStart = tagNameStart;
+        }
+
+        if ('undefined' == typeof tagNameEnd || null == tagNameEnd) {
+            this.tagNameEnd = this.tagNameStart.split("").reverse().join("");
+        } else {
+            this.tagNameEnd = tagNameEnd.split("").reverse().join("");
+        }
+
         this.handleInstructionBlock();
-        if(this._contentStart >= 0 && this._contentEnd == -1) {
+
+        if (this._contentStart >= 0 && this._contentEnd == -1) {
             this._tokenPos = this.tokens.length - 1;
             this._tokenForward = -1;
+            //we now can skip the parsing for the rest of the block and have to roll out the parsing
+            //from bottom up, this speeds up the entire process tremendously!
             this.handleEndBlock();
         }
 
-        if(this._contentStart >= 0 && this._contentEnd == -1) {
+        if (this._contentStart >= 0 && this._contentEnd == -1) {
             this._contentEnd = this.tokens.length - 1;
-        } else if(this._contentStart == -1) {
+        } else if (this._contentStart == -1) {
             return "";
-        } 
-        return this.tokens.slice(this._contentStart ,this._contentEnd).join("");
-        
-    };
-
-    myfaces._impl._util._HtmlStripper.prototype.handleEndBlock = function() {
-     
-        for(;this._tokenPos >= 0; this._tokenPos += this._tokenForward  ) {
-            this._skipBlank(0);
-            var token = this._getCurrentToken();
-            if(token != ">") {
-                continue;
-            } else {
-                this.handleEndDocument();
-            }
         }
+        return this.tokens.slice(this._contentStart, this._contentEnd + 1).join("");
+
     };
 
+    /**
+     * normal characters are skipped
+     * a < clearly is an indicator for
+     * a ... normal chars should not occur here, but
+     * in the long run for deeper parsing they might happen!
+     */
     myfaces._impl._util._HtmlStripper.prototype.handleInstructionBlock = function() {
-        var len =  this.tokens.length;
-        for(;this._tokenPos < len && this._tokenPos >= 0; this._tokenPos += this._tokenForward  ) {
+        var len = this.tokens.length;
+        for (; this._contentStart < 0 && this._tokenPos < len && this._tokenPos >= 0; this._tokenPos += this._tokenForward) {
             this._skipBlank();
             var token = this._getCurrentToken();
-            if(token != "<") {
-                continue;
-            } else {
+            if (token == "<") {
                 this.handleDocument();
-            } 
+            }
         }
     };
 
+    /**
+     * it is either a datablock or a content tag from which we can start parsing
+     */
     myfaces._impl._util._HtmlStripper.prototype.handleDocument = function() {
 
         this._skipBlank(1);
 
-        if(this._tokenPos >= this.tokens.length ) {
+        if (this._tokenPos >= this.tokens.length) {
             throw new Error("Document end reached prematurely");
         }
         var token = this._getCurrentToken();
-        switch(token) {
+        switch (token) {
             case "!":
                 this.handleDataBlock();
                 break;
 
-            default: this.handleContent();
-        }
-    };
-
-    myfaces._impl._util._HtmlStripper.prototype.handleEndDocument = function() {
-
-        this._skipBlank(1);
-
-        if(this._tokenPos < 0 ) {
-            throw new Error("Document end reached prematurely");
-        }
-        var token = this._getCurrentToken();
-        switch(token) {
-            case "-":
-
-                this.handleComment(true);
-                break;
-
-            default: this.handleContentEnd();
+            default: this.handleContentTag();
         }
     };
 
-    myfaces._impl._util._HtmlStripper.prototype.handleContentEnd = function() {
-        var tagFound = false;
-        for( ;this._tokenPos >= 0; this._skipBlank(1)) {
-            if(!tagFound) {
-                var tagName = this._fetchTagname();
-                if(tagName == "lmth") {
-                    tagFound = true;
-                }
-            } else if(tagFound && this.tokens[this._tokenPos] == "<") {
-                this._contentEnd = this._tokenPos - 1;
-                this._tokenPos = -1;
-                return;
-            }
-            
-        }
-    }
-
-
     /**
      * we can skip definitions or comments!
      *
@@ -163,16 +165,15 @@
     myfaces._impl._util._HtmlStripper.prototype.handleDataBlock = function() {
         this._skipBlank(1);
 
-
-        if(this._tokenPos >= this.tokens.length || this._tokenPos < 0 ) {
+        if (this._tokenPos >= this.tokens.length || this._tokenPos < 0) {
             return;
         }
         var token = this.tokens[this._tokenPos];
-        switch(token) {
+        switch (token) {
             case "-":
                 this.handleComment();
                 break;
-            
+
             default:
                 this._getCurrentToken();
                 this.handleDocDefinition();
@@ -181,7 +182,6 @@
 
     };
 
-
     /**
      * doc definition ==
      * <! [^>] >
@@ -189,112 +189,337 @@
     myfaces._impl._util._HtmlStripper.prototype.handleDocDefinition = function() {
         this._skipBlank();
 
-        if(this._tokenPos >= this.tokens.length || this._tokenPos < 0 ) {
+        if (this._tokenPos >= this.tokens.length || this._tokenPos < 0) {
             throw new Error("Document end reached prematurely");
         }
         var len = this.tokens.length;
-        while(this._tokenPos < len && this._tokenPos >= 0 ) {
+        while (this._tokenPos < len && this._tokenPos >= 0) {
             //var token = this._getCurrentToken();
             //inlining for speed reasons  we also could use getCurrentToken
             var token = this.tokens[this._tokenPos];
-           
+
             //inlining end
-            if(token == ">") {
+            if (token == ">") {
                 return;
             }
-            this._tokenPos+=this._tokenForward;
+            this._tokenPos += this._tokenForward;
         }
 
     };
 
-
-    myfaces._impl._util._HtmlStripper.prototype.handleContent = function() {
+    /**
+     * General tag andling section
+     * we define identified and unidentified content
+     * and script which is a subpart of idenitifed
+     *
+     * for now we do not handle the special conditions within pre and code
+     * segments since we have a specialized usage of this stripper
+     * on head and body sections and within head (which is skipped)
+     * we neither can have code or pre segments!
+     */
+    myfaces._impl._util._HtmlStripper.prototype.handleContentTag = function() {
         //lookahead head, body, html which means a lookahead of 4;
         this._currentSection = null;
-
-
         this._skipBlank();
-
         var tagName = this._fetchTagname();
 
         /*we try to avoid lookaheads here hence the shifting of tokens!*/
-        switch(tagName) {
-            case "html":/*either embedded into html */
-
-                this.handleHtml();
-                //after the html tag is processed we can break from the first parsing stage
-                this.tokenPos = this.tokens.length;
+        if (tagName == this.tagNameStart) {
+            /*either embedded into html */
+            this.handleIdentifiedContent();
+            //after the html tag is processed we can break from the first parsing stage
+            this.tokenPos = this.tokens.length;
+        } else    if (tagName == "scri" || tagName == "styl") {
+            //script must be handled separately since we can have embedded tags which must be ignored
+            this.handleScriptStyle();
+        } else {
+            //unidentified content we deal with it by skipping to the tag end
+            //and then be done with it!
+            this.skipToTagEnd();
+        }
+    };
 
-            default: break;
+    /**
+     * script skipping routine...
+     * we skip automatically over embedded scripts
+     * and tags within strings and comments
+     * so that we do not trigger against embedded tags in script sections
+     * of the head!
+     *
+     *
+     */
+    myfaces._impl._util._HtmlStripper.prototype.handleScriptStyle = function() {
+        this.skipToTagEnd();
+        //singleToken??
+        if (this.tokens[this._tokenPos - 1] == "/" && this.tokens[this._tokenPos] == ">") {
+            return;
         }
+        //lets skip until we hit < by ignoring embedded strings and comments
+        do {
+            this._skipBlank(1);
+            var token = this._getCurrentToken();
+            switch (token) {
+                case "\'" || "'":
+                    this.handleString(token);
+                    break;
+                case "/" :
+                    this.handleJSComment();
+                    break;
+            }
+
+        } while (this.tokens[this._tokenPos] != "<");
+        //now we should be at the end of the script tag at any circumstances!
+        this.skipToTagEnd();
     };
 
-    myfaces._impl._util._HtmlStripper.prototype.handleHtml = function() {
+    /**
+     * javascript comment handler
+     * we have to check for escape sequences so that we do not trigger
+     * accidentally the comment parsing within embedded regular expressions
+     * which means we have to prefetch and backtrack one token!
+     * \/* should not start a comment neither should \//
+     * if someone places this in the middle of the code
+     * then oh well, syntax error on the javascript side
+     * we cannot do anything about it
+     */
+    myfaces._impl._util._HtmlStripper.prototype.handleJSComment = function() {
+        var token = this._getCurrentToken();
+        var prefetchToken = this.tokens[this._tokenPos + 1];
+        var backtrackToken = this.tokens[this._tokenPos - 1];
+        var backtrackToken2 = this.tokens[this._tokenPos - 2];
+
+        //comment condition == either /* or // with no \ in backtrack or \\ in backtrack!
+        var backTrackIsComment = backtrackToken != '\\' || (backtrackToken == '\\' && backtrackToken2 == '\\');
+        if (!backTrackIsComment) {
+            return;
+        }
+
+        var singleLineComment = prefetchToken == '/';
+        var multiLineComment = prefetchToken == "*";
 
-        while(this.tokens[this._tokenPos] != ">") {
+        if (singleLineComment) {
+            while (this._tokenPos < this.tokens.length && this._getCurrentToken() != "\n") {
+                this._tokenPos++;
+            }
+
+        } else if (multiLineComment) {
             this._skipBlank(1);
+            while (this._tokenPos < this.tokens.length) {
+                this._skipBlank(1);
+                token = this._getCurrentToken();
+                prefetchToken = this.tokens[this._tokenPos + 1];
+                if (token == "*" && prefetchToken == "/") {
+                    return;
+                }
+            }
+        }
+    };
+
+    /*----------------- reverse parsing --------*/
+
+    /**
+     * the end block tos a bottom up resolution of the parsing process
+     * via an inverted tag name to look for
+     */
+    myfaces._impl._util._HtmlStripper.prototype.handleEndBlock = function() {
+        for (; this._tokenPos >= 0; this._tokenPos += this._tokenForward) {
+            this._skipBlank(0);
             var token = this._getCurrentToken();
-            if((token == "'" || token == '"') && this._isStringStart()) {
-                this.handleString(token);
+            if (token == ">") {
+                this.handleEndTagPart();
             }
         }
+    };
 
-        this._htmlSection = this.html;
+    myfaces._impl._util._HtmlStripper.prototype.handleEndTagPart = function() {
 
-        if(this.tokens[this._tokenPos-1] == "/" && this.tokens[this._tokenPos] == ">") {
+        this._skipBlank(1);
+
+        if (this._tokenPos < 0) {
+            throw new Error("Document end reached prematurely");
+        }
+        var token = this._getCurrentToken();
+
+        //we can assume we are outside of the html or body sections
+        //se we only have to check for comments for the reverse parsing!
+        switch (token) {
+            case "-":
+
+                this.handleComment(true);
+                break;
+
+            default: this.handleContentEnd();
+        }
+
+    };
+
+    myfaces._impl._util._HtmlStripper.prototype.handleContentEnd = function() {
+        var tagFound = false;
+        var first = true;
+        for (; this._tokenPos >= 0; this._skipBlank(1)) {
+            if (first && !tagFound) {
+                var tagName = this._fetchTagname();
+                if (tagName == this.tagNameEnd) {
+                    tagFound = true;
+                }
+                first = false;
+            } else if (tagFound && this.tokens[this._tokenPos] == "<") {
+                this._contentEnd = this._tokenPos - 1;
+                this._tokenPos = -1;
+                return;
+            } else if (this.tokens[this._tokenPos] == "<") {
+                this._tokenPos += 1;
+                return;
+            }
+
+        }
+    };
+
+    /*----------------- helpers ----------------*/
+    /**
+     * skips the current tag definition until the end is reached
+     *
+     * @param analyzeAttributes if set to true we will end up with
+     * a map of determined key value pairs which we then can further process
+     * otherwise the key value pair determination is ignored!
+     */
+    myfaces._impl._util._HtmlStripper.prototype.skipToTagEnd = function(analyzeAttributes) {
+
+        var token = this._getCurrentToken();
+        //faster shortcut for tags which have to be nont analyzed
+        if (!analyzeAttributes) {
+            while (token != ">") {
+                if (this._isStringStart()) {
+                    this._tokenPos += this._tokenForward;
+                    return this.handleString(token)
+                }
+                this._skipBlank(1);
+                token = this._getCurrentToken();
+            }
+
+            return null;
+        }
+
+        //analyze part
+
+        var keyValuePairs = {};
+        var currentWord = [];
+        var currentKey = null;
+        var openKey = false;
+        var lastKey = null;
+        while (this.tokens[this._tokenPos] != ">") {
+            var currentWord = this._fetchWord();
+            var token = this._getCurrentToken();
+
+            if (token == "=") {
+                this._tokenPos += this._tokenForward;
+                keyValuePairs[currentWord] = this._fetchWord();
+            } else {
+                keyValuePairs[currentWord] = null;
+            }
+            this._tokenPos += this._tokenForward;
+        }
+        return keyValuePairs;
+    };
+
+    /**
+     * fetches a word which either can be a string or
+     * a sequence of non string characters until either = or > or blank is reached!
+     */
+    myfaces._impl._util._HtmlStripper.prototype._fetchWord = function() {
+        this._skipBlank(0);
+        var result = [];
+
+        var token = this._getCurrentToken();
+        while ((!this._isBlank()) && token != "=" && token != ">") {
+            if (this._isStringStart()) {
+                this._tokenPos += this._tokenForward;
+                return this.handleString(token)
+            }
+            
+            result.push(token);
+            this._tokenPos += this._tokenForward;
+            token = this._getCurrentToken();
+        }
+        return result.join("");
+    };
+
+    myfaces._impl._util._HtmlStripper.prototype._isBlank = function() {
+        var token = this._getCurrentToken();
+        return token == " " && token == "\t" && token == "\n";
+    };
+
+    /**
+     * we can make speedup shorcut assumptions here
+     * becase we only try to either identfy head
+     * html or body!
+     */
+    myfaces._impl._util._HtmlStripper.prototype.handleIdentifiedContent = function() {
+
+        this.tagAttributes = this.skipToTagEnd(true);
+        //TODO trace down the attributes and store them
+        //they must be key value pairs
+
+        if (this.tokens[this._tokenPos - 1] == "/" && this.tokens[this._tokenPos] == ">") {
             this._contentStart = -1;
-            this._contentEnd = -1; /*we move to the end*/
+            this._contentEnd = -1;
+            /*we move to the end*/
         } else {
-            this._contentStart = this._tokenPos+1;
+            this._contentStart = this._tokenPos + 1;
         }
     };
 
-
+    /**
+     * returns true if the current token is a string start!
+     */
     myfaces._impl._util._HtmlStripper.prototype._isStringStart = function() {
-        var backTrack = (this._tokenPos > 0)? this.tokens[this._tokenPos-1]:null;
+        var backTrack = (this._tokenPos > 0) ? this.tokens[this._tokenPos - 1] : null;
         var token = this.tokens[this._tokenPos];
         return (token == "'" || token == '"') && backTrack != "\\";
     };
 
-   
-
-  
+    /**
+     * skips a string section cintentwise no matter how many other strings
+     * are embedded (uses backtracking to check for escapes)
+     *
+     * @param  {String} stringToken the string token to skip!
+     * @return the string value without the enclosing hypenations to be processed later on
+     */
     myfaces._impl._util._HtmlStripper.prototype.handleString = function(stringToken) {
         var backTrack = null;
-
-        while(this.tokens[this._tokenPos] != stringToken || backTrack == "\\") {
+        var resultString = [];
+        while (this.tokens[this._tokenPos] != stringToken || backTrack == "\\") {
             backTrack = this._getCurrentToken();
-            this._tokenPos+=this._tokenForward ;
-            if(this._tokenPos >= this.tokens.length ) {
+            resultString.push(backTrack);
+            this._tokenPos += this._tokenForward;
+            if (this._tokenPos >= this.tokens.length) {
                 throw Error("Invalid html string opened but not closed");
             }
         }
         this._getCurrentToken();
-
+        return resultString.join("");
     };
 
-
     myfaces._impl._util._HtmlStripper.prototype._assertValues = function(assertValues) {
 
-        for(var loop = 0; loop < assertValues.length; loop++) {
+        for (var loop = 0; loop < assertValues.length; loop++) {
             this._assertValue(assertValues[loop]);
             this._skipBlank(1);
         }
     };
 
-
     myfaces._impl._util._HtmlStripper.prototype._assertValue = function(expectedToken) {
         var token = this._getCurrentToken();
         this._assertLength();
-        if(token != expectedToken) {
-            throw Error("Invalid Token  "+expectedToken+" was expected instead of "+token);
+        if (token != expectedToken) {
+            throw Error("Invalid Token  " + expectedToken + " was expected instead of " + token);
         }
 
         return token;
     };
 
     myfaces._impl._util._HtmlStripper.prototype._assertLength = function() {
-        if(this._tokenPos >= this.tokens.length ) {
+        if (this._tokenPos >= this.tokens.length) {
             throw Error("Invalid html comment opened but not closed");
         }
     };
@@ -302,20 +527,20 @@
     /**
      * nested comments are not allowed hence we
      * skip them!
-     * comment == "<!--" [-->] "-->"
+     * comment == "&lt;!--" [--&gt;] "--&gt;"
      */
     myfaces._impl._util._HtmlStripper.prototype.handleComment = function(reverse) {
         this._assertValues(["-","-"]);
-        if('undefined' == typeof reverse || null == reverse) {
+        if ('undefined' == typeof reverse || null == reverse) {
             reverse = false;
         }
 
-        while(this._tokenPos < this.tokens.length-3) {
+        while (this._tokenPos < this.tokens.length - 3) {
             //lookahead3, to save some code
-            token = this._getCurrentToken();
+            var token = this._getCurrentToken();
             var backTrackBuf = [];
 
-            if(token == "-") {
+            if (token == "-") {
                 backTrackBuf.push(token);
                 this._skipBlank(1);
                 token = this._getCurrentToken();
@@ -323,17 +548,17 @@
                 this._skipBlank(1);
                 token = this._getCurrentToken();
                 backTrackBuf.push(token);
-                
-                if(reverse) {
+
+                if (reverse) {
                     this._skipBlank(1);
                     token = this._getCurrentToken();
                     backTrackBuf.push(token);
                 }
                 backTrackBuf = backTrackBuf.join("");
-                
-                if(reverse && backTrackBuf == "<!--") {
+
+                if (reverse && backTrackBuf == "<!--") {
                     return;
-                } else if(!reverse && backTrackBuf == "-->") {
+                } else if (!reverse && backTrackBuf == "-->") {
                     return;
                 }
             } else {
@@ -342,7 +567,6 @@
         }
     };
 
-
     /**
      * fetches and stores the current token
      */
@@ -350,16 +574,21 @@
         return this.tokens[this._tokenPos];
     };
 
- 
+    /**
+     * skip blank until the next token is found
+     * @param skipVal  the minimum skip forward to happen
+     * 1 means it skips 1 no matter if the current token is a blank or not!
+     *
+     */
     myfaces._impl._util._HtmlStripper.prototype._skipBlank = function(skipVal) {
-        var len =  this.tokens.length;
-        if('undefined' == typeof  skipVal || null == skipVal) {
+        var len = this.tokens.length;
+        if ('undefined' == typeof  skipVal || null == skipVal) {
             skipVal = 0;
         }
 
-        for(this._tokenPos += (skipVal * this._tokenForward);this._tokenPos < len && this._tokenPos >= 0; this._tokenPos+=this._tokenForward) {
+        for (this._tokenPos += (skipVal * this._tokenForward); this._tokenPos < len && this._tokenPos >= 0; this._tokenPos += this._tokenForward) {
             var token = this.tokens[this._tokenPos];
-            if(token != " " && token != "\t" && token != "\n") {
+            if (token != " " && token != "\t" && token != "\n") {
                 return;
             }
         }
@@ -368,15 +597,16 @@
     myfaces._impl._util._HtmlStripper.prototype._fetchTagname = function() {
         var tagName = [];
 
+        //TODO make the tagname prefetch more generic
 
         tagName.push(this.tokens[this._tokenPos]);
-        this._tokenPos+=this._tokenForward ;
+        this._tokenPos += this._tokenForward;
         tagName.push(this._getCurrentToken());
-        this._tokenPos+=this._tokenForward ;
+        this._tokenPos += this._tokenForward;
         tagName.push(this._getCurrentToken());
-        this._tokenPos+=this._tokenForward ;
+        this._tokenPos += this._tokenForward;
         tagName.push(this._getCurrentToken());
-        this._tokenPos+=this._tokenForward ;
+        this._tokenPos += this._tokenForward;
 
         tagName = tagName.join("").toLowerCase();
         return tagName;

Modified: myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_Utils.js
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_Utils.js?rev=788713&r1=788712&r2=788713&view=diff
==============================================================================
--- myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_Utils.js (original)
+++ myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_Utils.js Fri Jun 26 14:17:13 2009
@@ -231,9 +231,12 @@
      */
     myfaces._impl._util._Utils.replaceHtmlItem = function(request, context, itemIdToReplace, newTag, form) {
         try {
+            //for webkit we have to trim otherwise he does not add the adjancent elements correctly
+            newTag = myfaces._impl._util._LangUtils.trim(newTag);
         	// (itemIdToReplace instanceof Node) is NOT compatible with IE8
             var item = (typeof itemIdToReplace == "object") ? itemIdToReplace :
             myfaces._impl._util._Utils.getElementFromForm(request, context, itemIdToReplace, form);
+
             if (item == null) {
                 myfaces._impl.xhrCore._Exception.throwNewWarning
                 (request, context, "Utils", "replaceHTMLItem", "Unknown Html-Component-ID: " + itemIdToReplace);
@@ -254,9 +257,9 @@
                     myfaces._impl._util._Utils.runScripts(request, context, item.previousSibling);
                 }
             }
-
             // and remove the old item
             item.parentNode.removeChild(item);
+            
         } catch (e) {
             myfaces._impl.xhrCore._Exception.throwNewError (request, context, "Utils", "replaceHTMLItem", e);
         }

Modified: myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/myfaces/_impl/xhrCore/_AjaxResponse.js
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/myfaces/_impl/xhrCore/_AjaxResponse.js?rev=788713&r1=788712&r2=788713&view=diff
==============================================================================
--- myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/myfaces/_impl/xhrCore/_AjaxResponse.js (original)
+++ myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/myfaces/_impl/xhrCore/_AjaxResponse.js Fri Jun 26 14:17:13 2009
@@ -109,7 +109,7 @@
                  * </eval>
                  */
 
-            
+
 
                 //this ought to be enough for eval
                 //however the run scripts still makes sense
@@ -240,29 +240,23 @@
 
             switch(node.getAttribute('id')) {
                 case "javax.faces.ViewRoot":
+                    this._replaceBody(request, context,  cDataBlock );
 
-                    var parser =  myfaces.ajax._impl = new (myfaces._impl._util._Utils.getGlobalConfig("updateParser",myfaces._impl._util._HtmlStripper))();
-                
-                    document.documentElement.innerHTML  =  parser.parse(cDataBlock);
-                        // innerHTML doesn't execute scripts, so no browser switch here
-                    myfaces._impl._util._Utils.runScripts(request, context, cDataBlock);
-                   
                     break;
                 case "javax.faces.ViewHead":
                     //we assume the cdata block is our head including the tag
                     this._replaceElement(request, context, head, cDataBlock );
-                    
+
 
                     break;
                 case "javax.faces.ViewBody":
                     //we assume the cdata block is our body including the tag
-                    this._replaceElement(request, context, body, cDataBlock );
-  
+                    this._replaceBody(request, context,  cDataBlock );
                     break;
 
                 default:
                     this._replaceElement(request, context,node.getAttribute('id'), cDataBlock );
-                   
+
                     break;
             }
         }
@@ -270,6 +264,39 @@
     };
 
     /**
+     * special method to handle the body dom manipulation,
+     * replacing the entire body does not work fully by simply adding a second body
+     * and by creating a range instead we have to work around that by dom creating a second
+     * body and then filling it properly!
+     * 
+     * @param {Object} request our request object
+     * @param {Map} context the response context
+     * @param {String} newData the markup which replaces the old dom node!
+     */
+    myfaces._impl.xhrCore._AjaxResponse.prototype._replaceBody = function(request, context, newData) {
+        var parser =  myfaces.ajax._impl = new (myfaces._impl._util._Utils.getGlobalConfig("updateParser",myfaces._impl._util._HtmlStripper))();
+                 
+        var oldBody = document.getElementsByTagName("body")[0];
+        var newBody = document.createElement("body");
+        var placeHolder = document.createElement("div");
+        var bodyParent = oldBody.parentNode;
+
+        newBody.appendChild(placeHolder);
+        bodyParent.replaceChild(newBody, oldBody);
+
+        //the contextualFragment trick does not work on the body tag instead we have to generate a manual body
+        //element and then add a child which then is the replacement holder for our fragment!
+        this._replaceElement(request, context, placeHolder , parser.parse(newData,"body") );
+
+        for(var key in parser.tagAttributes) {
+            var value = parser.tagAttributes[key];
+            myfaces._impl._util._Utils.setAttribute(newBody, key, value);
+        }
+    };
+
+
+
+    /**
      * Helper method to avoid duplicate code
      * @param {Object} request our request object
      * @param {Map} context the response context