/[suikacvs]/markup/html/scripting-parser/parser.html
Suika

Diff of /markup/html/scripting-parser/parser.html

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.7 by wakaba, Fri Apr 25 23:03:35 2008 UTC revision 1.15 by wakaba, Tue Apr 29 03:29:41 2008 UTC
# Line 1  Line 1 
1  <!DOCTYPE HTML>  <!DOCTYPE HTML>
2  <html lang=en>  <html lang=en>
3  <head>  <head>
4  <title>Demo of HTML5 Parsing Algorithm with Scripting Enabled</title>  <title>Live Scripting HTML Parser</title>
5    <link rel=author href="http://suika.fam.cx/~wakaba/who?">
6    <link rel=license href="http://suika.fam.cx/c/gnu/gpl"
7        title="GNU GPL2 or later">
8  <style>  <style>
9    h1, h2 {    h1 {
10        margin: 0;
11        font-size: 150%;
12      }
13      h2 {
14      margin: 0;      margin: 0;
15      font-size: 100%;      font-size: 100%;
16    }    }
17    p, pre {    p {
18      margin: 0;      margin: 0 1em;
19    }    }
20    textarea {    textarea {
21      width: 100%;      width: 100%;
# Line 34  Line 41 
41    } // update    } // update
42    
43    function update2 () {    function update2 () {
     document.logElement.textContent = '';  
44      var v = document.sourceElement.value;      var v = document.sourceElement.value;
45      var p = new Parser (new InputStream (v));      if (v != document.previousSourceText) {
46      var doc = p.doc;        document.previousSourceText = v;
47      p.parse ();        document.links['permalink'].href
48      log (dumpTree (doc, ''));            = location.pathname + '?s=' + encodeURIComponent (v);
49          document.links['ldvlink'].href
50      document.links['permalink'].href            = 'http://software.hixie.ch/utilities/js/live-dom-viewer/?'
51          = location.href + '?s=' + encodeURIComponent (v);            + encodeURIComponent (v);
52    
53          document.logElement.textContent = '';
54          var p = new Parser (new InputStream (v));
55          var doc = p.doc;
56          p.parse ();
57          
58          log (dumpTree (doc, ''));
59          
60          if (p.hasAsyncScript) {
61            log ('Some script codes are executed asynchronously; it means that the document might be rendered in different ways depending on the network condition and other factors');
62          }
63        }
64    } // update2    } // update2
65    
66    var logIndentLevel = 0;    var logIndentLevel = 0;
67    function log (s) {    function log (s) {
68        var indent = '';
69      for (var i = 0; i < logIndentLevel; i++) {      for (var i = 0; i < logIndentLevel; i++) {
70        s = '  ' + s;        indent += '  ';
71      }      }
72        s = indent + s.replace (/\n/g, "\n" + indent);
73      document.logElement.appendChild (document.createTextNode (s + "\n"));      document.logElement.appendChild (document.createTextNode (s + "\n"));
74    } // log    } // log
75    
# Line 65  Line 85 
85      }      }
86      this.doc = doc;      this.doc = doc;
87      this.openElements = [doc];      this.openElements = [doc];
88      this.in = i;      this.input = i;
89      this.scriptsExecutedAfterParsing = [];      this.scriptsExecutedAfterParsing = [];
90        this.scriptsExecutedSoon = [];
91        this.scriptsExecutedAsynchronously = [];
92    } // Parser    } // Parser
93    
94    Parser.prototype.getNextToken = function () {    Parser.prototype.getNextToken = function () {
95      var p = this;      var p = this;
96      var i = this.in;      var i = this.input;
97      if (this.parseMode == 'script') {      if (this.parseMode == 'cdata') {
98          var tagName = this.endTagName;
99        var token;        var token;
100        if (p.insertionPoint <= 0) {        if (p.insertionPoint <= 0) {
101          return {type: 'abort'};          return {type: 'abort'};
# Line 90  Line 113 
113          return '';          return '';
114        });        });
115        if (token) return token;        if (token) return token;
116        i.s = i.s.replace (/^<\/[Ss][Cc][Rr][Ii][Pp][Tt]>/, function (s) {        var pattern = new RegExp ('^</' + tagName + '>', 'i');
117          i.s = i.s.replace (pattern, function (s) {
118          if (p.insertionPoint < s.length) {          if (p.insertionPoint < s.length) {
119            token = {type: 'abort'};            token = {type: 'abort'};
120            return s;            return s;
121          }          }
122          token = {type: 'end-tag', value: 'script'};          token = {type: 'end-tag', value: tagName};
123          p.insertionPoint -= s.length;          p.insertionPoint -= s.length;
124          return '';          return '';
125        });        });
126        if (token) return token;        if (token) return token;
127        var m;        var m;
128        if ((p.insertionPoint < '</script'.length) &&        if ((p.insertionPoint < ('</' + tagName).length) &&
129            (m = i.s.match (/^<\/([SCRIPTscript]+)/))) {            (m = i.s.match (/^<\/([A-Za-z]+)/))) {
130          var v = m[1].substring (0, p.insertionPoint).toLowerCase ();          var v = m[1].substring (0, p.insertionPoint).toLowerCase ();
131          if (v == 'script'.substring (0, p.insertionPoint - '</'.length)) {          if (v == tagName.substring (0, p.insertionPoint - '</'.length)) {
132            return {type: 'abort'};            return {type: 'abort'};
133          }          }
134        }        }
# Line 144  Line 168 
168          tagName = v.toLowerCase ();          tagName = v.toLowerCase ();
169          return '';          return '';
170        });        });
171        e = e.replace (/^\s*([^\s=]+)\s*(?:=\s*(?:"([^"]*)"|'([^']*)'|([^"']+)))?/,        while (true) {
172        function (x, attrName, attrValue1, attrValue2, attrValue3) {          var m = false;
173          v = attrValue1 || attrValue2 || attrValue3;          e = e.replace (/^\s*([^\s=]+)\s*(?:=\s*(?:"([^"]*)"|'([^']*)'|([^"'\s]*)))?/,
174          v = v.replace (/&quot;/g, '"').replace (/&apos;/g, "'")          function (x, attrName, attrValue1, attrValue2, attrValue3) {
175              .replace (/&amp;/g, '&');            v = attrValue1 || attrValue2 || attrValue3;
176          attrs[attrName.toLowerCase ()] = v;            v = v.replace (/&quot;/g, '"').replace (/&apos;/g, "'")
177          return '';                .replace (/&amp;/g, '&');
178        });            attrs[attrName.toLowerCase ()] = v;
179              m = true;
180              return '';
181            });
182            if (!m) break;
183          }
184        if (e.length) {        if (e.length) {
185          log ('Broken start tag: "' + e + '"');          log ('Broken start tag: "' + e + '"');
186        }        }
# Line 204  Line 233 
233            el.manakaiParserInserted = true;            el.manakaiParserInserted = true;
234    
235            // 3. Switch the tokeniser's content model flag to the CDATA state.            // 3. Switch the tokeniser's content model flag to the CDATA state.
236            this.parseMode = 'script';            this.parseMode = 'cdata';
237              this.endTagName = 'script';
238    
239            // 4.1. Collect all the character tokens.            // 4.1. Collect all the character tokens.
240            while (true) {            while (true) {
# Line 218  Line 248 
248              // 4.2. Until it returns a token that is not a character token, or              // 4.2. Until it returns a token that is not a character token, or
249              // until it stops tokenising.              // until it stops tokenising.
250              } else if (token.type == 'eof' ||              } else if (token.type == 'eof' ||
251                         (token.type == 'end-tag' && token.value == 'script') ||                         token.type == 'end-tag' ||
252                         token.type == 'abort') {                         token.type == 'abort') {
253                // 6. Switched back to the PCDATA state.                // 6. Switched back to the PCDATA state.
254                this.parseMode = 'pcdata';                this.parseMode = 'pcdata';
255    
256                // 7.1. If the next token is not an end tag token with ...                // 7.1. If the next token is not an end tag token with ...
257                if (token.type != 'end-tag') {                if (!(token.type == 'end-tag' && token.value == 'script')) {
258                  // 7.2. This is a parse error.                  // 7.2. This is a parse error.
259                  log ('Parse error: no </' + 'script>');                  log ('Parse error: no </' + 'script>');
260    
# Line 288  Line 318 
318                //                //
319              }              }
320            }            }
321            } else if (token.value == 'style' ||
322                       token.value == 'noscript' ||
323                       token.value == 'xmp') {
324              // 1. Create an element for the token in the HTML namespace.
325              var el = new JSElement (this.doc, token.value);
326    
327              // 2. Append the new element to the current node.
328              this.openElements[this.openElements.length - 1].appendChild (el);
329    
330              // 3. Switch the tokeniser's content model flag to the CDATA state.
331              this.parseMode = 'cdata';
332              this.endTagName = token.value;
333    
334              // 4.1. Collect all the character tokens.
335              while (true) {
336                var token = this.getNextToken ();
337                log ('token: ' + token.type + ' "' + token.value + '"');
338    
339                if (token.type == 'char') {
340                  // 5. Append a single Text node to the script element node.
341                  el.manakaiAppendText (token.value);
342    
343                // 4.2. Until it returns a token that is not a character token, or
344                // until it stops tokenising.
345                } else if (token.type == 'eof' ||
346                           token.type == 'end-tag' ||
347                           token.type == 'abort') {
348                  // 6. Switched back to the PCDATA state.
349                  this.parseMode = 'pcdata';
350    
351                  // 7.1. If the next token is not an end tag token with ...
352                  if (!(token.type == 'end-tag' &&
353                        token.value == this.endTagName)) {
354                    // 7.2. This is a parse error.
355                    log ('Parse error: no </' + this.endTagName + '>');
356    
357                    // 7.3. Mark the script element as "already executed".
358                    el.manakaiAlreadyExecuted = true;
359                  } else {
360                    // 7.4. Ignore it.
361                    //
362                  }
363                  break;
364                }
365              }
366          } else {          } else {
367            var el = new JSElement (this.doc, token.value);            var el = new JSElement (this.doc, token.value);
368            this.openElements[this.openElements.length - 1].appendChild (el);            this.openElements[this.openElements.length - 1].appendChild (el);
# Line 318  Line 393 
393    
394      // "When a script completes loading" rules start applying.      // "When a script completes loading" rules start applying.
395    
396      // TODO: Handles "list of scripts that will execute as soon as possible"      while (this.scriptsExecutedSoon.length > 0 ||
397      // and "list of scripts that will execute asynchronously"             this.scriptsExecutedAsynchronously.length > 0) {
398          // Handle "list of scripts that will execute as soon as possible".
399          while (this.scriptsExecutedSoon.length > 0) {
400            var e = this.scriptsExecutedSoon.shift ();
401      
402            // If it has completed loading
403            log ('Execute an external script not inserted by parser...');
404            executeScript (this.doc, e);
405    
406            // NOTE: It MAY be executed before the end of the parsing, according
407            // to the spec.
408            this.hasAsyncScript = true;
409          }
410    
411          // Handle "list of scripts that will execute asynchronously".
412          while (this.scriptsExecutedAsynchronously.length > 0) {
413            var e = this.scriptsExecutedAsynchronously.shift ();
414    
415            // Step 1.
416            // We assume that all scripts have been loaded at this time.
417      
418            // Step 2.
419            log ('Execute an asynchronous script...');
420            executeScript (this.doc, e);
421    
422            // Step 3.
423            //
424    
425            // Step 4.
426            //
427    
428            this.hasAsyncScript = true;
429          }
430        }
431    
432      // Handle "list of scripts that will execute when the document has finished      // Handle "list of scripts that will execute when the document has finished
433      // parsing".      // parsing".
# Line 339  Line 447 
447    
448      log ('DOMContentLoaded event fired');      log ('DOMContentLoaded event fired');
449    
450      // "delays tha load event" things has completed:      // "delays the load event" things has completed:
451      // readyState = 'complete'      // readyState = 'complete'
452      log ('load event fired');      log ('load event fired');
453    
# Line 350  Line 458 
458      if (ip == undefined || ip == null || isNaN (ip)) {      if (ip == undefined || ip == null || isNaN (ip)) {
459        log ('insertion point: set to undefined');        log ('insertion point: set to undefined');
460        this.insertionPoint = undefined;        this.insertionPoint = undefined;
461      } else if (ip == this.in.s.length) {      } else if (ip == this.input.s.length) {
462        log ('insertion point: end of file');        log ('insertion point: end of file');
463        this.insertionPoint = ip;        this.insertionPoint = ip;
464      } else {      } else {
465        log ('insertion point: set to ' + ip +        log ('insertion point: set to ' + ip +
466             ' (before "' + this.in.s.substring (0, 10) + '")');             ' (before "' + this.input.s.substring (0, 10) + '")');
467        this.insertionPoint = ip;        this.insertionPoint = ip;
468      }      }
469    }; // setInsertionPoint    }; // setInsertionPoint
# Line 395  Line 503 
503        // 2.4. If the script element has its "already executed" flag set        // 2.4. If the script element has its "already executed" flag set
504        if (e.manakaiAlreadyExecuted) {        if (e.manakaiAlreadyExecuted) {
505          // 2.5. Abort these steps at this point.          // 2.5. Abort these steps at this point.
506          log ('Running a script: aborted');          log ('Running a script: aborted (already executed)');
507          logIndentLevel--;          logIndentLevel--;
508          return e;          return e;
509        }        }
# Line 414  Line 522 
522          p.scriptsExecutedAfterParsing.push (e);          p.scriptsExecutedAfterParsing.push (e);
523          log ('Running a script: aborted (defer)');          log ('Running a script: aborted (defer)');
524        } else if (e.async && e.src != null) {        } else if (e.async && e.src != null) {
525          // TODO          p.scriptsExecutedAsynchronously.push (e);
526        } else if (e.async && e.src == null          log ('Running a script: aborted (async src)');
527                   /* && list of scripts that will execute asynchronously is not empty */) {        } else if (e.async && e.src == null &&
528          // TODO                   p.scriptsExecutedAsynchronously.length > 0) {
529            p.scriptsExecutedAsynchronously.push (e);
530            log ('Running a script: aborted (async)');
531            // ISSUE: What is the difference with the case above?
532        } else if (e.src != null && e.manakaiParserInserted) {        } else if (e.src != null && e.manakaiParserInserted) {
533          if (p.scriptExecutedWhenParserResumes) {          if (p.scriptExecutedWhenParserResumes) {
534            log ('Error: There is a script that will execute as soon as the parser resumes.');            log ('Error: There is a script that will execute as soon as the parser resumes.');
535          }          }
536          p.scriptExecutedWhenParserResumes = e;          p.scriptExecutedWhenParserResumes = e;
537          log ('Running a script: aborted (src)');          log ('Running a script: aborted (src parser-inserted)');
538        } else if (e.src != null) {        } else if (e.src != null) {
539          // TODO          p.scriptsExecutedSoon.push (e);
540            log ('Running a script: aborted (src)');
541        } else {        } else {
542          executeScript (doc, e); // even if other scripts are already executing.          executeScript (doc, e); // even if other scripts are already executing.
543        }        }
# Line 473  Line 585 
585        var m;        var m;
586        if (m = uri.match (/^javascript:\s*(?:'([^']*)'|"([^"]+)")\s*$/i)) {        if (m = uri.match (/^javascript:\s*(?:'([^']*)'|"([^"]+)")\s*$/i)) {
587          if (m[1]) {          if (m[1]) {
588            return m[1];            return unescapeJSLiteral (m[1]);
589          } else if (m[2]) {          } else if (m[2]) {
590            return m[2];            return unescapeJSLiteral (m[2]);
591          } else {          } else {
592            return null;            return null;
593          }          }
# Line 496  Line 608 
608          matched = true;          matched = true;
609          var args = [];          var args = [];
610          t.replace (/('[^']*'|"[^"]*")/g, function (s, v) {          t.replace (/('[^']*'|"[^"]*")/g, function (s, v) {
611            args.push (v.substring (1, v.length - 1));            args.push (unescapeJSLiteral (v.substring (1, v.length - 1)));
612            return '';            return '';
613          });          });
614          doc.write.apply (doc, args);          doc.write.apply (doc, args);
615          return '';          return '';
616        });        });
617          var noDocumentElement = false;
618          s = s.replace (/^\s*var\s+s\s*=\s*document\.createElement\s*\(\s*['"]script['"]\s*\)\s*;\s*s\.src\s*=\s*(?:'([^']*)'|"([^"]*)")\s*;\s*document\.documentElement\.appendChild\s*\(\s*s\s*\)\s*;\s*/,
619          function (s, t, u) {
620            matched = true;
621            var args = [unescapeJSLiteral (t ? t : u)];
622            noDocumentElement = !doc._insertExternalScript.apply (doc, args);
623            return '';
624          });
625          if (noDocumentElement) {
626            log ('Script error: documentElement is null');
627            break;
628          }
629          s = s.replace (/^\s*w\s*\(\s*document\.documentElement\.innerHTML\s*\)\s*;\s*/,
630          function (s, t) {
631            matched = true;
632            log (dumpTree (doc, ''));
633            return '';
634          });
635        if (s == '') break;        if (s == '') break;
636        if (!matched) {        if (!matched) {
637          log ('Script parse error: "' + s + '"');          log ('Script parse error: "' + s + '"');
# Line 510  Line 640 
640      }      }
641    } // parseAndRunScript    } // parseAndRunScript
642    
643      function unescapeJSLiteral (s) {
644        return s.replace (/\\u([0-9A-Fa-f]{4})/g, function (t, v) {
645          return String.fromCharCode (parseInt ('0x' + v));
646        });
647      } // unescapeJSLiteral
648    
649    function JSText (data) {    function JSText (data) {
650      this.data = data;      this.data = data;
651    } // JSText    } // JSText
# Line 537  Line 673 
673      // Step 3.      // Step 3.
674      if (this._parser &&      if (this._parser &&
675          !this._parser.scriptCreated &&          !this._parser.scriptCreated &&
676          this._parser.in.insertionPoint != undefined) {          this._parser.input.insertionPoint != undefined) {
677        log ('document.open () in parsing mode is ignored');        log ('document.open () in parsing mode is ignored');
678        return this;        return this;
679      }      }
# Line 571  Line 707 
707      }      }
708    
709      // Step 11.      // Step 11.
710      this._parser.setInsertionPoint (this._parser.in.s.length);      this._parser.setInsertionPoint (this._parser.input.s.length);
711    
712      // Step 12.      // Step 12.
713      return this;      return this;
714    }; // document.open    }; // document.open
715    
716    JSDocument.prototype.write = function () {    JSDocument.prototype.write = function () {
717        log ('document.write: start');
718      logIndentLevel++;      logIndentLevel++;
719    
720      var p = this._parser;      var p = this._parser;
# Line 591  Line 728 
728      // 2. ... inserted into the input stream just before the insertion point.      // 2. ... inserted into the input stream just before the insertion point.
729      var s = Array.join (arguments, '');      var s = Array.join (arguments, '');
730      log ('document.write: insert "' + s + '"' +      log ('document.write: insert "' + s + '"' +
731           ' before "' + p.in.s.substring (p.insertionPoint, p.insertionPoint + 10) + '"');           ' before "' +
732      p.in.s = p.in.s.substring (0, p.insertionPoint) + s           p.input.s.substring (p.insertionPoint, p.insertionPoint + 10) + '"');
733          + p.in.s.substring (p.insertionPoint, p.in.s.length);      p.input.s = p.input.s.substring (0, p.insertionPoint) + s
734            + p.input.s.substring (p.insertionPoint, p.input.s.length);
735      p.insertionPoint += s.length;      p.insertionPoint += s.length;
736    
737      // 3. If there is a script that will execute as soon as the parser resumes      // 3. If there is a script that will execute as soon as the parser resumes
738      if (p.scriptExecutedAfterParserResumes) {      if (p.scriptExecutedAfterParserResumes) {
739        log ('document.write: processed later (there is an unprocessed <script src>)');        log ('document.write: processed later (there is an unprocessed <script src>)');
740        logIndentLevel--;        logIndentLevel--;
741          log ('document.write: return');
742        return;        return;
743      }      }
744    
# Line 613  Line 752 
752      // to do something here?      // to do something here?
753    
754      // 5. Return      // 5. Return
755        logIndentLevel--;
756      log ('document.write: return');      log ('document.write: return');
757    
     logIndentLevel--;  
758      return;      return;
759    }; // document.write    }; // document.write
760    
761      JSDocument.prototype._insertExternalScript = function (uri) {
762        var s = new JSElement (this, 'script');
763        s.src = uri;
764        if (this.documentElement) {
765          this.documentElement.appendChild (s);
766          return true;
767        } else {
768          return false;
769        }
770      }; // _insertExternalScript
771    
772      JSDocument.prototype.__defineGetter__ ('documentElement', function () {
773        var cn = this.childNodes;
774        for (var i = 0; i < cn.length; i++) {
775          if (cn[i] instanceof JSElement) {
776            return cn[i]
777          }
778        }
779        return null;
780      });
781    
782    JSElement.prototype.__defineGetter__ ('text', function () {    JSElement.prototype.__defineGetter__ ('text', function () {
783      var r = '';      var r = '';
784      for (var i = 0; i < this.childNodes.length; i++) {      for (var i = 0; i < this.childNodes.length; i++) {
# Line 637  Line 797 
797          r += '| ' + indent + node.localName + '\n';          r += '| ' + indent + node.localName + '\n';
798          if (node.async) r += '| ' + indent + '  async=""\n';          if (node.async) r += '| ' + indent + '  async=""\n';
799          if (node.defer) r += '| ' + indent + '  defer=""\n';          if (node.defer) r += '| ' + indent + '  defer=""\n';
800          if (node.src) r += '| ' + indent + '  src="' + node.src + '"\n';          if (node.src != null) {
801              r += '| ' + indent + '  src="' + node.src + '"\n';
802            }
803          r += dumpTree (node, indent + '  ');          r += dumpTree (node, indent + '  ');
804        } else if (node instanceof JSText) {        } else if (node instanceof JSText) {
805          r += '| ' + indent + '"' + node.data + '"\n';          r += '| ' + indent + '"' + node.data + '"\n';
# Line 651  Line 813 
813  </head>  </head>
814  <body onload="  <body onload="
815    document.sourceElement = document.getElementsByTagName ('textarea')[0];    document.sourceElement = document.getElementsByTagName ('textarea')[0];
816    
817      var q = location.search;
818      if (q != null) {
819        q = q.substring (1).split (/;/);
820        for (var i = 0; i < q.length; i++) {
821          var v = q[i].split (/=/, 2);
822          v[0] = decodeURIComponent (v[0]);
823          v[1] = decodeURIComponent (v[1] || '');
824          if (v[0] == 's') {
825            document.sourceElement.value = v[1];
826          }
827        }
828      }
829    
830    document.logElement = document.getElementsByTagName ('output')[0];    document.logElement = document.getElementsByTagName ('output')[0];
831    update ();    update ();
832  ">  ">
833  <h1>Live Scripting Parser</h1>  <h1>Live Scripting <abbr title="Hypertext Markup Language">HTML</abbr>
834    Parser</h1>
835    
836  <h2>Markup to test  <h2>Markup to test
837  (<a href=data:, id=permalink rel=bookmark>permalink</a>)</h2>  (<a href=data:, id=permalink rel=bookmark>permalink</a>,
838    <a href="http://software.hixie.ch/utilities/js/live-dom-viewer/"
839        id=ldvlink>Live <abbr title="Document Object Model">DOM</abbr>
840        Viewer</a>)</h2>
841  <p>  <p>
842  <textarea onkeydown=" update () " onchange=" update () " oninput=" update () ">&lt;html>  <textarea onkeydown=" update () " onchange=" update () " oninput=" update () ">&lt;html>
843  &lt;head>&lt;/head>&lt;body>  &lt;head>&lt;/head>&lt;body>
# Line 668  document.write ('aaaaaaa&lt;/p>&lt;scrip Line 848  document.write ('aaaaaaa&lt;/p>&lt;scrip
848  &lt;p>  &lt;p>
849  </textarea>  </textarea>
850    
851  <h2>Log</h2>  <h2 id=log>Log</h2>
852  <p><output></output>  <p><output></output>
853    
854  <!-- TODO: short description -->  <h2 id=notes>Notes</h2>
855    
856  <!-- TODO: permalink query -> textarea -->  <p>This is a <em>simplified</em> implementation of
857    <a href="http://www.whatwg.org/specs/web-apps/current-work/#parsing">HTML5
858  <!-- TODO: multiple attributes are not supported yet -->  Parsing Algorithm</a>.  It only implements script-related part of the
859    algorithm.  Especially, this parser:
860    <ul>
861    <li>Does not support <code>DOCTYPE</code> and comment tokens.
862    <li>Does not support entities except for <code>&amp;quot;</code>,
863    <code>&amp;apos;</code>, and <code>&amp;amp;</code> in <code>script</code>
864    <code>src</code> attribute value.
865    <li>Does not support omissions of start or end tags, the <abbr>AAA</abbr>
866    algorithm, and so on.
867    <li>Does not raise parse errors for invalid attribute specifications in start
868    or end tags.
869    <li>Does not support PCDATA elements (<code>title</code> and
870    <code>textarea</code>).
871    <li>Does not strip the first newline in <code>pre</code> elements.
872    <li>Does not support <code>&lt;!--</code>..<code>--></code> parsing rule
873    in <code>script</code> element.
874    <li>Does not support foreign (SVG or MathML) elements.
875    <li>Only supports <code>script</code> <code>type</code>
876    <code>text/javascript</code>.  <code>type</code> and <code>language</code>
877    attributes are ignored.
878    <li>Only supports limited statements.  It must consist of zero or more
879    of statements looking similar to the following statements, possibly
880    introduced, followed, or separated by white space characters:
881      <ul>
882      <li><code>document.write ("<var>string</var>", ["<var>string</var>", ...]);</code>.
883      <li><code>var s = document.createElement ("script");
884                s.src = "<var>string</var>";
885                document.documentElement.appendChild (s);</code>
886      <li><code>w (document.documentElement.innerHTML);</code> (This statement
887      can be used to dump the document, even when the document has no
888      document element.  The output format is the tree dump format used
889      in html5lib test data, not <abbr>HTML</abbr>.)
890      </ul>
891    Note that strings may be delimited by <code>'</code>s instead of
892    <code>"</code>s.
893    <li>Only supports <code>javascript:</code>
894    <abbr title="Uniform Resourace Identifiers">URI</abbr> scheme in the
895    <code>src</code> attribute of the <code>script</code> element.  In addition,
896    the <abbr title="Uniform Resource Identifiers">URI</abbr> must be conform to
897    the regular expression <code>^javascript:\s*(?:"[^"]*"|'[^']*')\s*$</code>.
898    <li>Only supports <code>\u<var>HHHH</var></code> escapes in JavaScript
899    string literals.
900    <li>Does not handle <i>stop parsing</i> phase correctly if the document is
901    replaced by <code>document.open ()</code> call.  In other word, delayed
902    (deferred or asynchronous) script executions and event firings might be
903    treated in a wrong way if a <code>document.open ()</code> invocation
904    is implicitly done by <code>document.write ()</code> in a delayed script.
905    </ul>
906    
907    <p>For some reason, this parser does not work in browsers that do
908    not support JavaScript 1.5.
909    
910    <!-- TODO: |src| attribute value should refer the value at the time
911    when it is inserted into the document, not the value when the script is
912    executed.  Currently it does not matter, since we don't allow dynamic
913    modification to the |src| content/DOM attribute value yet. -->
914    
915  </body>  </body>
 </html>  
916    </html>
917    <!-- $Date$ -->
918    <!--
919    
920    Copyright 2008 Wakaba <w@suika.fam.cx>
921    
922    This program is free software; you can redistribute it and/or
923    modify it under the terms of the GNU General Public License
924    as published by the Free Software Foundation; either version 2
925    of the License, or (at your option) any later version.
926    
927    This program is distributed in the hope that it will be useful,
928    but WITHOUT ANY WARRANTY; without even the implied warranty of
929    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
930    GNU General Public License for more details.
931    
932    You should have received a copy of the GNU General Public License
933    along with this program; if not, write to the Free Software
934    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
935    
936    -->

Legend:
Removed from v.1.7  
changed lines
  Added in v.1.15

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24