/[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.3 by wakaba, Sun Apr 20 10:02:43 2008 UTC revision 1.14 by wakaba, Tue Apr 29 02:50:00 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;
27      font-family: monospace;      font-family: monospace;
28      white-space: pre;      white-space: -moz-pre-wrap;
29        white-space: pre-wrap;
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      p.parse ();        delayedUpdater = 0;
39      log (dumpTree (p.doc, ''));      }
40        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;
67    function log (s) {    function log (s) {
68        for (var i = 0; i < logIndentLevel; i++) {
69          s = '  ' + s;
70        }
71      document.logElement.appendChild (document.createTextNode (s + "\n"));      document.logElement.appendChild (document.createTextNode (s + "\n"));
72    } // log    } // log
73    
# Line 32  Line 75 
75      this.s = s;      this.s = s;
76    } // InputStream    } // InputStream
77    
78    function Parser (i) {    function Parser (i, doc) {
79      this.parseMode = 'pcdata';      this.parseMode = 'pcdata';
80      this.doc = new JSDocument (this);      if (!doc) {
81      this.openElements = [this.doc];        doc = new JSDocument (this);
82      this.in = i;        doc.manakaiIsHTML = true;
83        }
84        this.doc = doc;
85        this.openElements = [doc];
86        this.input = i;
87        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 == 'cdata') {
96          var tagName = this.endTagName;
97        var token;        var token;
98        if (p.insertionPoint <= 0) {        if (p.insertionPoint <= 0) {
99          return {type: 'abort'};          return {type: 'abort'};
100        }        }
101        i.s = i.s.replace (/^([\s\S]+?)<\/[Ss][Cc][Rr][Ii][Pp][Tt]>/,        i.s = i.s.replace (/^([^<]+)/,
102        function (s, t) {        function (s, t) {
103          if (0 < p.insertionPoint && p.insertionPoint < t.length) {          if (0 < p.insertionPoint && p.insertionPoint < t.length) {
104            token = {type: 'char', value: t.substring (0, p.insertionPoint)};            token = {type: 'char', value: t.substring (0, p.insertionPoint)};
105            var ip = p.insertionPoint;            var ip = p.insertionPoint;
106            p.insertionPoint = 0;            p.insertionPoint = 0;
107            return t.substring (ip, t.length) +            return t.substring (ip, t.length);
               s.substring (s.length - 9, s.length);  
108          }          }
109          token = {type: 'char', value: t};          token = {type: 'char', value: t};
110          p.insertionPoint -= s.length;          p.insertionPoint -= t.length;
111          return '<' + '/script>';          return '';
112        });        });
113        if (token) return token;        if (token) return token;
114        i.s = i.s.replace (/^<\/[Ss][Cc][Rr][Ii][Pp][Tt]>/, function (s) {        var pattern = new RegExp ('^</' + tagName + '>', 'i');
115          if (s.length < p.insertionPoint) {        i.s = i.s.replace (pattern, function (s) {
116            if (p.insertionPoint < s.length) {
117            token = {type: 'abort'};            token = {type: 'abort'};
118            return s;            return s;
119          }          }
120          token = {type: 'end-tag', value: 'script'};          token = {type: 'end-tag', value: tagName};
121            p.insertionPoint -= s.length;
122            return '';
123          });
124          if (token) return token;
125          var m;
126          if ((p.insertionPoint < ('</' + tagName).length) &&
127              (m = i.s.match (/^<\/([A-Za-z]+)/))) {
128            var v = m[1].substring (0, p.insertionPoint).toLowerCase ();
129            if (v == tagName.substring (0, p.insertionPoint - '</'.length)) {
130              return {type: 'abort'};
131            }
132          }
133          i.s = i.s.replace (/^</,
134          function (s) {
135            token = {type: 'char', value: s};
136          p.insertionPoint -= s.length;          p.insertionPoint -= s.length;
137          return '';          return '';
138        });        });
# Line 75  Line 141 
141      }      }
142    
143      var token;      var token;
144      i.s = i.s.replace (/^<\/([^>]+)>/, function (s, e) {      i.s = i.s.replace (/^<\/([^>]+)(?:>|$)/, function (s, e) {
145        if (p.insertionPoint < s.length) {        if (p.insertionPoint < s.length ||
146              (p.insertionPoint <= s.length &&
147               s.substring (s.length - 1, 1) != '>')) {
148          token = {type: 'abort'};          token = {type: 'abort'};
149          return s;          return s;
150        }        }
# Line 85  Line 153 
153        return '';        return '';
154      });      });
155      if (token) return token;      if (token) return token;
156      i.s = i.s.replace (/^<([^>]+)>/, function (s, e) {      i.s = i.s.replace (/^<([^>]+)(?:>|$)/, function (s, e) {
157        if (p.insertionPoint < s.length) {        if (p.insertionPoint < s.length ||
158              (p.insertionPoint <= s.length &&
159               s.substring (s.length - 1, 1) != '>')) {
160          token = {type: 'abort'};          token = {type: 'abort'};
161          return s;          return s;
162        }        }
163        token = {type: 'start-tag', value: e.toLowerCase ()};        var tagName;
164          var attrs = {};
165          e = e.replace (/^[\S]+/, function (v) {
166            tagName = v.toLowerCase ();
167            return '';
168          });
169          while (true) {
170            var m = false;
171            e = e.replace (/^\s*([^\s=]+)\s*(?:=\s*(?:"([^"]*)"|'([^']*)'|([^"'\s]*)))?/,
172            function (x, attrName, attrValue1, attrValue2, attrValue3) {
173              v = attrValue1 || attrValue2 || attrValue3;
174              v = v.replace (/&quot;/g, '"').replace (/&apos;/g, "'")