/[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.6 by wakaba, Fri Apr 25 13:42:51 2008 UTC revision 1.13 by wakaba, Sun Apr 27 11:27:04 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 {
10        margin: 0;
11        font-size: 150%;
12      }
13      h2 {
14        margin: 0;
15        font-size: 100%;
16      }
17      p {
18        margin: 0 1em;
19      }
20    textarea {    textarea {
21       display: block;      width: 100%;
22       width: 80%;      -width: 99%;
23       margin-left: auto;      height: 10em;
      margin-right: auto;  
      min-height: 20em;  
24    }    }
25    output {    output {
26      display: block;      display: block;
# Line 18  Line 30 
30    }    }
31  </style>  </style>
32  <script>  <script>
33      var delayedUpdater = 0;
34    
35    function update () {    function update () {
36      document.logElement.textContent = '';      if (delayedUpdater) {
37      var p = new Parser (new InputStream (document.sourceElement.value));        clearTimeout (delayedUpdater);
38      var doc = p.doc;        delayedUpdater = 0;
39      p.parse ();      }
40      log (dumpTree (doc, ''));      delayedUpdater = setTimeout (update2, 100);
41    } // update    } // update
42    
43      function update2 () {
44        var v = document.sourceElement.value;
45        if (v != document.previousSourceText) {
46          document.previousSourceText = v;
47          document.links['permalink'].href
48              = location.pathname + '?s=' + encodeURIComponent (v);
49          document.links['ldvlink'].href
50              = 'http://software.hixie.ch/utilities/js/live-dom-viewer/?'
51              + 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
65    
66    var logIndentLevel = 0;    var logIndentLevel = 0;
67    function log (s) {    function log (s) {
68      for (var i = 0; i < logIndentLevel; i++) {      for (var i = 0; i < logIndentLevel; i++) {
# Line 46  Line 83 
83      }      }
84      this.doc = doc;      this.doc = doc;
85      this.openElements = [doc];      this.openElements = [doc];
86      this.in = i;      this.input = i;
87      this.scriptsExecutedAfterParsing = [];      this.scriptsExecutedAfterParsing = [];
88        this.scriptsExecutedSoon = [];
89        this.scriptsExecutedAsynchronously = [];
90    } // Parser    } // Parser
91    
92    Parser.prototype.getNextToken = function () {    Parser.prototype.getNextToken = function () {
93      var p = this;      var p = this;
94      var i = this.in;      var i = this.input;
95      if (this.parseMode == 'script') {      if (this.parseMode == 'script') {
96        var token;        var token;
97        if (p.insertionPoint <= 0) {        if (p.insertionPoint <= 0) {
# Line 125  Line 164 
164          tagName = v.toLowerCase ();          tagName = v.toLowerCase ();
165          return '';          return '';
166        });        });
167        e = e.replace (/^\s*([^\s=]+)\s*(?:=\s*(?:"([^"]*)"|'([^']*)'|([^"']+)))?/,        while (true) {
168        function (x, attrName, attrValue1, attrValue2, attrValue3) {          var m = false;
169          v = attrValue1 || attrValue2 || attrValue3;          e = e.replace (/^\s*([^\s=]+)\s*(?:=\s*(?:"([^"]*)"|'([^']*)'|([^"'\s]*)))?/,
170          v = v.replace (/&quot;/g, '"').replace (/&apos;/g, "'")          function (x, attrName, attrValue1, attrValue2, attrValue3) {
171              .replace (/&amp;/g, '&');            v = attrValue1 || attrValue2 || attrValue3;
172          attrs[attrName.toLowerCase ()] = v;            v = v.replace (/&quot;/g, '"').replace (/&apos;/g, "'")
173          return '';                .replace (/&amp;/g, '&');
174        });            attrs[attrName.toLowerCase ()] = v;
175              m = true;
176              return '';
177            });
178            if (!m) break;
179          }
180        if (e.length) {        if (e.length) {
181          log ('Broken start tag: "' + e + '"');          log ('Broken start tag: "' + e + '"');
182        }        }
# Line 235  Line 279 
279            this.openElements[this.openElements.length - 1].appendChild (el);            this.openElements[this.openElements.length - 1].appendChild (el);
280    
281            // 11. Let the insertion point have the value of the old ...            // 11. Let the insertion point have the value of the old ...
282    
283            oldInsertionPoint += this.insertionPoint;            oldInsertionPoint += this.insertionPoint;
284            this.setInsertionPoint (oldInsertionPoint);            this.setInsertionPoint (oldInsertionPoint);
285    
# Line 298  Line 343 
343    
344      // "When a script completes loading" rules start applying.      // "When a script completes loading" rules start applying.
345    
346      // TODO: Handles "list of scripts that will execute as soon as possible"      while (this.scriptsExecutedSoon.length > 0 ||
347      // and "list of scripts that will execute asynchronously"             this.scriptsExecutedAsynchronously.length > 0) {
348          // Handle "list of scripts that will execute as soon as possible".
349          while (this.scriptsExecutedSoon.length > 0) {
350            var e = this.scriptsExecutedSoon.shift ();
351      
352            // If it has completed loading
353            log ('Execute an external script not inserted by parser...');
354            executeScript (this.doc, e);
355    
356            // NOTE: It MAY be executed before the end of the parsing, according
357            // to the spec.
358            this.hasAsyncScript = true;
359          }
360    
361          // Handle "list of scripts that will execute asynchronously".
362          while (this.scriptsExecutedAsynchronously.length > 0) {
363            var e = this.scriptsExecutedAsynchronously.shift ();
364    
365            // Step 1.
366            // We assume that all scripts have been loaded at this time.
367      
368            // Step 2.
369            log ('Execute an asynchronous script...');
370            executeScript (this.doc, e);
371    
372            // Step 3.
373            //
374    
375            // Step 4.
376            //
377    
378            this.hasAsyncScript = true;
379          }
380        }
381    
382      // Handle "list of scripts that will execute when the document has finished      // Handle "list of scripts that will execute when the document has finished
383      // parsing".      // parsing".
# Line 330  Line 408 
408      if (ip == undefined || ip == null || isNaN (ip)) {      if (ip == undefined || ip == null || isNaN (ip)) {
409        log ('insertion point: set to undefined');        log ('insertion point: set to undefined');
410        this.insertionPoint = undefined;        this.insertionPoint = undefined;
411      } else if (ip == this.in.s.length) {      } else if (ip == this.input.s.length) {
412        log ('insertion point: end of file');        log ('insertion point: end of file');
413        this.insertionPoint = ip;        this.insertionPoint = ip;
414      } else {      } else {
415        log ('insertion point: set to ' + ip +        log ('insertion point: set to ' + ip +
416             ' (before "' + this.in.s.substring (0, 10) + '")');             ' (before "' + this.input.s.substring (0, 10) + '")');
417        this.insertionPoint = ip;        this.insertionPoint = ip;
418      }      }
419    }; // setInsertionPoint    }; // setInsertionPoint
# Line 394  Line 472 
472          p.scriptsExecutedAfterParsing.push (e);          p.scriptsExecutedAfterParsing.push (e);
473          log ('Running a script: aborted (defer)');          log ('Running a script: aborted (defer)');
474        } else if (e.async && e.src != null) {        } else if (e.async && e.src != null) {
475          // TODO          p.scriptsExecutedAsynchronously.push (e);
476        } else if (e.async && e.src == null          log ('Running a script: aborted (async src)');
477                   /* && list of scripts that will execute asynchronously is not empty */) {        } else if (e.async && e.src == null &&
478          // TODO                   p.scriptsExecutedAsynchronously.length > 0) {
479            p.scriptsExecutedAsynchronously.push (e);