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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.17 - (hide annotations) (download) (as text)
Fri May 16 10:29:25 2008 UTC (18 years, 3 months ago) by wakaba
Branch: MAIN
Changes since 1.16: +7 -5 lines
File MIME type: text/html
Fire script |load| event after the execution (HTML5 revision 1613)

1 wakaba 1.1 <!DOCTYPE HTML>
2     <html lang=en>
3     <head>
4 wakaba 1.8 <title>Live Scripting HTML Parser</title>
5 wakaba 1.13 <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 wakaba 1.1 <style>
9 wakaba 1.13 h1 {
10     margin: 0;
11     font-size: 150%;
12     }
13     h2 {
14 wakaba 1.7 margin: 0;
15     font-size: 100%;
16     }
17 wakaba 1.13 p {
18     margin: 0 1em;
19 wakaba 1.7 }
20 wakaba 1.1 textarea {
21 wakaba 1.7 width: 100%;
22     -width: 99%;
23     height: 10em;
24 wakaba 1.1 }
25     output {
26     display: block;
27     font-family: monospace;
28 wakaba 1.4 white-space: -moz-pre-wrap;
29     white-space: pre-wrap;
30 wakaba 1.1 }
31     </style>
32     <script>
33 wakaba 1.7 var delayedUpdater = 0;
34    
35 wakaba 1.1 function update () {
36 wakaba 1.7 if (delayedUpdater) {
37     clearTimeout (delayedUpdater);
38     delayedUpdater = 0;
39     }
40     delayedUpdater = setTimeout (update2, 100);
41     } // update
42    
43     function update2 () {
44     var v = document.sourceElement.value;
45 wakaba 1.8 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 wakaba 1.10
58 wakaba 1.8 log (dumpTree (doc, ''));
59 wakaba 1.10
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 wakaba 1.8 }
64 wakaba 1.7 } // update2
65 wakaba 1.1
66 wakaba 1.6 var logIndentLevel = 0;
67 wakaba 1.1 function log (s) {
68 wakaba 1.15 var indent = '';
69 wakaba 1.6 for (var i = 0; i < logIndentLevel; i++) {
70 wakaba 1.15 indent += ' ';
71 wakaba 1.6 }
72 wakaba 1.15 s = indent + s.replace (/\n/g, "\n" + indent);
73 wakaba 1.1 document.logElement.appendChild (document.createTextNode (s + "\n"));
74     } // log
75    
76     function InputStream (s) {
77     this.s = s;
78     } // InputStream
79    
80 wakaba 1.4 function Parser (i, doc) {
81 wakaba 1.1 this.parseMode = 'pcdata';
82 wakaba 1.4 if (!doc) {
83     doc = new JSDocument (this);
84     doc.manakaiIsHTML = true;
85     }
86 wakaba 1.16 this.nextToken = [];
87 wakaba 1.4 this.doc = doc;
88     this.openElements = [doc];
89 wakaba 1.8 this.input = i;
90 wakaba 1.4 this.scriptsExecutedAfterParsing = [];
91 wakaba 1.10 this.scriptsExecutedSoon = [];
92 wakaba 1.12 this.scriptsExecutedAsynchronously = [];
93 wakaba 1.1 } // Parser
94    
95 wakaba 1.2 Parser.prototype.getNextToken = function () {
96 wakaba 1.16 if (this.nextToken.length) {
97     return this.nextToken.shift ();
98     }
99    
100 wakaba 1.3 var p = this;
101 wakaba 1.8 var i = this.input;
102 wakaba 1.14 if (this.parseMode == 'cdata') {
103     var tagName = this.endTagName;
104 wakaba 1.1 var token;
105 wakaba 1.3 if (p.insertionPoint <= 0) {
106     return {type: 'abort'};
107     }
108 wakaba 1.4 i.s = i.s.replace (/^([^<]+)/,
109 wakaba 1.1 function (s, t) {
110 wakaba 1.3 if (0 < p.insertionPoint && p.insertionPoint < t.length) {
111     token = {type: 'char', value: t.substring (0, p.insertionPoint)};
112     var ip = p.insertionPoint;
113     p.insertionPoint = 0;
114 wakaba 1.4 return t.substring (ip, t.length);
115 wakaba 1.3 }
116 wakaba 1.1 token = {type: 'char', value: t};
117 wakaba 1.4 p.insertionPoint -= t.length;
118     return '';
119 wakaba 1.1 });
120     if (token) return token;
121 wakaba 1.14 var pattern = new RegExp ('^</' + tagName + '>', 'i');
122     i.s = i.s.replace (pattern, function (s) {
123 wakaba 1.4 if (p.insertionPoint < s.length) {
124 wakaba 1.3 token = {type: 'abort'};
125     return s;
126     }
127 wakaba 1.14 token = {type: 'end-tag', value: tagName};
128 wakaba 1.3 p.insertionPoint -= s.length;
129 wakaba 1.1 return '';
130     });
131     if (token) return token;
132 wakaba 1.5 var m;
133 wakaba 1.14 if ((p.insertionPoint < ('</' + tagName).length) &&
134     (m = i.s.match (/^<\/([A-Za-z]+)/))) {
135 wakaba 1.5 var v = m[1].substring (0, p.insertionPoint).toLowerCase ();
136 wakaba 1.14 if (v == tagName.substring (0, p.insertionPoint - '</'.length)) {
137 wakaba 1.5 return {type: 'abort'};
138     }
139     }
140 wakaba 1.4 i.s = i.s.replace (/^</,
141     function (s) {
142     token = {type: 'char', value: s};
143     p.insertionPoint -= s.length;
144     return '';
145     });
146     if (token) return token;
147 wakaba 1.1 return {type: 'eof'};
148     }
149    
150     var token;
151 wakaba 1.5 i.s = i.s.replace (/^<\/([^>]+)(?:>|$)/, function (s, e) {
152     if (p.insertionPoint < s.length ||
153     (p.insertionPoint <= s.length &&
154 wakaba 1.16 s.substring (s.length - 1, s.length) != '>')) {
155 wakaba 1.3 token = {type: 'abort'};
156     return s;
157     }
158 wakaba 1.1 token = {type: 'end-tag', value: e.toLowerCase ()};
159 wakaba 1.3 p.insertionPoint -= s.length;
160 wakaba 1.1 return '';
161     });
162     if (token) return token;
163 wakaba 1.5 i.s = i.s.replace (/^<([^>]+)(?:>|$)/, function (s, e) {
164     if (p.insertionPoint < s.length ||
165     (p.insertionPoint <= s.length &&
166 wakaba 1.16 s.substring (s.length - 1, s.length) != '>')) {
167 wakaba 1.3 token = {type: 'abort'};
168     return s;
169     }
170 wakaba 1.4 var tagName;
171     var attrs = {};
172     e = e.replace (/^[\S]+/, function (v) {
173     tagName = v.toLowerCase ();
174     return '';
175     });
176 wakaba 1.9 while (true) {
177     var m = false;
178     e = e.replace (/^\s*([^\s=]+)\s*(?:=\s*(?:"([^"]*)"|'([^']*)'|([^"'\s]*)))?/,
179     function (x, attrName, attrValue1, attrValue2, attrValue3) {
180     v = attrValue1 || attrValue2 || attrValue3;
181     v = v.replace (/&quot;/g, '"').replace (/&apos;/g, "'")
182     .replace (/&amp;/g, '&');
183     attrs[attrName.toLowerCase ()] = v;
184     m = true;
185     return '';
186     });
187     if (!m) break;
188     }
189 wakaba 1.6 if (e.length) {
190     log ('Broken start tag: "' + e + '"');
191     }
192 wakaba 1.4 token = {type: 'start-tag', value: tagName, attrs: attrs};
193 wakaba 1.3 p.insertionPoint -= s.length;
194 wakaba 1.1 return '';
195     });
196     if (token) return token;