/[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.2 by wakaba, Sun Apr 20 07:48:00 2008 UTC revision 1.10 by wakaba, Sun Apr 27 10:34:18 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  <style>  <style>
6      h1, h2 {
7        margin: 0;
8        font-size: 100%;
9      }
10      p, pre {
11        margin: 0;
12      }
13    textarea {    textarea {
14       display: block;      width: 100%;
15       width: 80%;      -width: 99%;
16       margin-left: auto;      height: 10em;
      margin-right: auto;  
      min-height: 20em;  
17    }    }
18    output {    output {
19      display: block;      display: block;
20      font-family: monospace;      font-family: monospace;
21      white-space: pre;      white-space: -moz-pre-wrap;
22        white-space: pre-wrap;
23    }    }
24  </style>  </style>
25  <script>  <script>
26      var delayedUpdater = 0;
27    
28    function update () {    function update () {
29      document.logElement.textContent = '';      if (delayedUpdater) {
30      var p = new Parser (new InputStream (document.sourceElement.value));        clearTimeout (delayedUpdater);
31      p.parse ();        delayedUpdater = 0;
32      log (dumpTree (p.doc, ''));      }
33        delayedUpdater = setTimeout (update2, 100);
34    } // update    } // update
35    
36      function update2 () {
37        var v = document.sourceElement.value;
38        if (v != document.previousSourceText) {
39          document.previousSourceText = v;
40          document.links['permalink'].href
41              = location.pathname + '?s=' + encodeURIComponent (v);
42          document.links['ldvlink'].href
43              = 'http://software.hixie.ch/utilities/js/live-dom-viewer/?'
44              + encodeURIComponent (v);
45    
46          document.logElement.textContent = '';
47          var p = new Parser (new InputStream (v));
48          var doc = p.doc;
49          p.parse ();
50          
51          log (dumpTree (doc, ''));
52          
53          if (p.hasAsyncScript) {
54            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');
55          }
56        }
57      } // update2
58    
59      var logIndentLevel = 0;
60    function log (s) {    function log (s) {
61        for (var i = 0; i < logIndentLevel; i++) {
62          s = '  ' + s;
63        }
64      document.logElement.appendChild (document.createTextNode (s + "\n"));      document.logElement.appendChild (document.createTextNode (s + "\n"));
65    } // log    } // log
66    
# Line 32  Line 68 
68      this.s = s;      this.s = s;
69    } // InputStream    } // InputStream
70    
71    function Parser (i) {    function Parser (i, doc) {
72      this.parseMode = 'pcdata';      this.parseMode = 'pcdata';
73      this.doc = new JSDocument (this);      if (!doc) {
74      this.openElements = [this.doc];        doc = new JSDocument (this);
75      this.in = i;        doc.manakaiIsHTML = true;
76        }
77        this.doc = doc;
78        this.openElements = [doc];
79        this.input = i;
80        this.scriptsExecutedAfterParsing = [];
81        this.scriptsExecutedSoon = [];
82    } // Parser    } // Parser
83    
84    Parser.prototype.getNextToken = function () {    Parser.prototype.getNextToken = function () {
85      var i = this.in;      var p = this;
86        var i = this.input;
87      if (this.parseMode == 'script') {      if (this.parseMode == 'script') {
88        var token;        var token;
89        i.s = i.s.replace (/^([\s\S]+?)<\/[Ss][Cc][Rr][Ii][Pp][Tt]>/,        if (p.insertionPoint <= 0) {
90            return {type: 'abort'};
91          }
92          i.s = i.s.replace (/^([^<]+)/,
93        function (s, t) {        function (s, t) {
94            if (0 < p.insertionPoint && p.insertionPoint < t.length) {
95              token = {type: 'char', value: t.substring (0, p.insertionPoint)};
96              var ip = p.insertionPoint;
97              p.insertionPoint = 0;
98              return t.substring (ip, t.length);
99            }
100          token = {type: 'char', value: t};          token = {type: 'char', value: t};
101          return '<' + '/script>';          p.insertionPoint -= t.length;
102            return '';
103        });        });
104        if (token) return token;        if (token) return token;
105        i.s = i.s.replace (/^<\/[Ss][Cc][Rr][Ii][Pp][Tt]>/, function () {        i.s = i.s.replace (/^<\/[Ss][Cc][Rr][Ii][Pp][Tt]>/, function (s) {
106            if (p.insertionPoint < s.length) {
107              token = {type: 'abort'};
108              return s;
109            }
110          token = {type: 'end-tag', value: 'script'};          token = {type: 'end-tag', value: 'script'};
111            p.insertionPoint -= s.length;
112            return '';
113          });
114          if (token) return token;
115          var m;
116          if ((p.insertionPoint < '</script'.length) &&
117              (m = i.s.match (/^<\/([SCRIPTscript]+)/))) {
118            var v = m[1].substring (0, p.insertionPoint).toLowerCase ();
119            if (v == 'script'.substring (0, p.insertionPoint - '</'.length)) {
120              return {type: 'abort'};
121            }
122          }
123          i.s = i.s.replace (/^</,
124          function (s) {
125            token = {type: 'char', value: s};
126            p.insertionPoint -= s.length;
127          return '';          return '';
128        });        });
129        if (token) return token;        if (token) return token;
# Line 58  Line 131 
131      }      }
132    
133      var token;      var token;
134      i.s = i.s.replace (/^<\/([^>]+)>/, function (s, e) {      i.s = i.s.replace (/^<\/([^>]+)(?:>|$)/, function (s, e) {
135          if (p.insertionPoint < s.length ||
136              (p.insertionPoint <= s.length &&
137               s.substring (s.length - 1, 1) != '>')) {
138            token = {type: 'abort'};
139            return s;
140          }
141        token = {type: 'end-tag', value: e.toLowerCase ()};        token = {type: 'end-tag', value: e.toLowerCase ()};
142          p.insertionPoint -= s.length;
143        return '';        return '';
144      });      });
145      if (token) return token;      if (token) return token;
146      i.s = i.s.replace (/^<([^>]+)>/, function (s, e) {      i.s = i.s.replace (/^<([^>]+)(?:>|$)/, function (s, e) {
147        token = {type: 'start-tag', value: e.toLowerCase ()};        if (p.insertionPoint < s.length ||
148              (p.insertionPoint <= s.length &&
149               s.substring (s.length - 1, 1) != '>')) {
150            token = {type: 'abort'};
151            return s;
152          }
153          var tagName;
154          var attrs = {};
155          e = e.replace (/^[\S]+/, function (v) {
156            tagName = v.toLowerCase ();
157            return '';
158          });
159          while (true) {
160            var m = false;
161            e = e.replace (/^\s*([^\s=]+)\s*(?:=\s*(?:"([^"]*)"|'([^']*)'|([^"'\s]*)))?/,
162            function (x, attrName, attrValue1, attrValue2, attrValue3) {
163              v = attrValue1 || attrValue2 || attrValue3;
164              v = v.replace (/&quot;/g, '"').replace (/&apos;/g, "'")
165                  .replace (/&amp;/g, '&');
166              attrs[attrName.toLowerCase ()] = v;
167              m = true;
168              return '';
169            });
170            if (!m) break;
171          }
172          if (e.length) {
173            log ('Broken start tag: "' + e + '"');
174          }
175          token = {type: 'start-tag', value: tagName, attrs: attrs};
176          p.insertionPoint -= s.length;
177        return '';        return '';
178      });      });
179      if (token) return token;      if (token) return token;
180        if (p.insertionPoint <= 0) {
181          return {type: 'abort'};
182        }
183      i.s = i.s.replace (/^[^<]+/, function (s) {      i.s = i.s.replace (/^[^<]+/, function (s) {
184          if (p.insertionPoint < s.length) {
185            token = {type: 'char', value: s.substring (0, p.insertionPoint)};
186            var ip = p.insertionPoint;
187            p.insertionPoint = 0;
188            return s.substring (ip, s.length);
189          }
190        token = {type: 'char', value: s};        token = {type: 'char', value: s};
191          p.insertionPoint -= s.length;
192        return '';        return '';
193      });      });
194      if (token) return token;      if (token) return token;
195      i.s = i.s.replace (/^[\s\S]/, function (s) {      i.s = i.s.replace (/^[\s\S]/, function (s) {
196        token = {type: 'char', value: s};        token = {type: 'char', value: s};
197          p.insertionPoint -= s.length;
198        return '';        return '';
199      });      });
200      if (token) return token;      if (token) return token;
# Line 82  Line 202 
202    } // getNextToken    } // getNextToken
203    
204    Parser.prototype.parse =