/[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.9 by wakaba, Sun Apr 27 09:16:11 2008 UTC revision 1.20 by wakaba, Sat Sep 20 13:32:45 2008 UTC
# Line 2  Line 2 
2  <html lang=en>  <html lang=en>
3  <head>  <head>
4  <title>Live Scripting HTML Parser</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 47  Line 54 
54        var p = new Parser (new InputStream (v));        var p = new Parser (new InputStream (v));
55        var doc = p.doc;        var doc = p.doc;
56        p.parse ();        p.parse ();
57          
58        log (dumpTree (doc, ''));        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 69  Line 83 
83        doc = new JSDocument (this);        doc = new JSDocument (this);
84        doc.manakaiIsHTML = true;        doc.manakaiIsHTML = true;
85      }      }
86        this.nextToken = [];
87      this.doc = doc;      this.doc = doc;
88      this.openElements = [doc];      this.openElements = [doc];
89      this.input = i;      this.input = i;
90      this.scriptsExecutedAfterParsing = [];      this.scriptsExecutedAfterParsing = [];
91        this.scriptsExecutedSoon = [];
92        this.scriptsExecutedAsynchronously = [];
93    } // Parser    } // Parser
94    
95    Parser.prototype.getNextToken = function () {    Parser.prototype.getNextToken = function () {
96        if (this.nextToken.length) {
97          return this.nextToken.shift ();
98        }
99    
100      var p = this;      var p = this;
101      var i = this.input;      var i = this.input;
102      if (this.parseMode == 'script') {      if (this.parseMode == 'cdata') {
103          var tagName = this.endTagName;
104        var token;        var token;
105        if (p.insertionPoint <= 0) {        if (p.insertionPoint <= 0) {
106          return {type: 'abort'};          return {type: 'abort'};
# Line 96  Line 118 
118          return '';          return '';
119        });        });
120        if (token) return token;        if (token) return token;
121        i.s = i.s.replace (/^<\/[Ss][Cc][Rr][Ii][Pp][Tt]>/, function (s) {        var pattern = new RegExp ('^</' + tagName + '>', 'i');
122          i.s = i.s.replace (pattern, function (s) {
123          if (p.insertionPoint < s.length) {          if (p.insertionPoint < s.length) {
124            token = {type: 'abort'};            token = {type: 'abort'};
125            return s;            return s;
126          }          }
127          token = {type: 'end-tag', value: 'script'};          token = {type: 'end-tag', value: tagName};
128          p.insertionPoint -= s.length;          p.insertionPoint -= s.length;
129          return '';          return '';
130        });        });
131        if (token) return token;        if (token) return token;
132        var m;        var m;
133        if ((p.insertionPoint < '</script'.length) &&        if ((p.insertionPoint < ('</' + tagName).length) &&
134            (m = i.s.match (/^<\/([SCRIPTscript]+)/))) {            (m = i.s.match (/^<\/([A-Za-z]+)/))) {
135          var v = m[1].substring (0, p.insertionPoint).toLowerCase ();          var v = m[1].substring (0, p.insertionPoint).toLowerCase ();
136          if (v == 'script'.substring (0, p.insertionPoint - '</'.length)) {          if (v == tagName.substring (0, p.insertionPoint - '</'.length)) {
137            return {type: 'abort'};            return {type: 'abort'};
138          }          }
139        }        }
# Line 128  Line 151 
151      i.s = i.s.replace (/^<\/([^>]+)(?:>|$)/, function (s, e) {      i.s = i.s.replace (/^<\/([^>]+)(?:>|$)/, function (s, e) {
152        if (p.insertionPoint < s.length ||        if (p.insertionPoint < s.length ||
153            (p.insertionPoint <= s.length &&            (p.insertionPoint <= s.length &&
154             s.substring (s.length - 1, 1) != '>')) {             s.substring (s.length - 1, s.length) != '>')) {
155          token = {type: 'abort'};          token = {type: 'abort'};
156          return s;          return s;
157        }        }
# Line 140  Line 163 
163      i.s = i.s.replace (/^<([^>]+)(?:>|$)/, function (s, e) {      i.s = i.s.replace (/^<([^>]+)(?:>|$)/, function (s, e) {
164        if (p.insertionPoint < s.length ||        if (p.insertionPoint < s.length ||
165            (p.insertionPoint <= s.length &&            (p.insertionPoint <= s.length &&
166             s.substring (s.length - 1, 1) != '>')) {             s.substring (s.length - 1, s.length) != '>')) {
167          token = {type: 'abort'};          token = {type: 'abort'};
168          return s;          return s;
169        }        }
# Line 203  Line 226 
226        var token = this.getNextToken ();        var token = this.getNextToken ();
227        log ('token: ' + token.type + ' "' + token.value + '"');        log ('token: ' + token.type + ' "' + token.value + '"');
228    
229          if (this.cdataEndTagRequired) {
230            // Generic CDATA parsing algorithm
231    
232            if (token.type != 'abort') {
233              // 7.
234              if (token.type == 'end-tag' && token.value == this.endTagName) {
235                // 7.1. Ignores it.
236                //
237              } else {
238                // 7.2. Parse error.
239                log ('Parse error: no </' + this.endTagName + '>');
240                this.nextToken.unshift (token);
241              }
242              this.cdataEndTagRequired = false;
243              continue;
244            }
245          }
246    
247        if (token.type == 'start-tag') {        if (token.type == 'start-tag') {
248          if (token.value == 'script') {          if (token.value == 'script') {
249            // 1. Create an element for the token in the HTML namespace.            // 1. Create an element for the token in the HTML namespace.
# Line 215  Line 256 
256            el.manakaiParserInserted = true;            el.manakaiParserInserted = true;
257    
258            // 3. Switch the tokeniser's content model flag to the CDATA state.            // 3. Switch the tokeniser's content model flag to the CDATA state.
259            this.parseMode = 'script';            this.parseMode = 'cdata';
260              this.endTagName = 'script';
261    
262            // 4.1. Collect all the character tokens.            // 4.1. Collect all the character tokens.
263            while (true) {            while (true) {
# Line 229  Line 271 
271              // 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
272              // until it stops tokenising.              // until it stops tokenising.
273              } else if (token.type == 'eof' ||              } else if (token.type == 'eof' ||
274                         (token.type == 'end-tag' && token.value == 'script') ||                         token.type == 'end-tag' ||
275                         token.type == 'abort') {                         token.type == 'abort') {
276                // 6. Switched back to the PCDATA state.                // 6. Switched back to the PCDATA state.
277                this.parseMode = 'pcdata';                this.parseMode = 'pcdata';
278    
279                // 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 ...
280                if (token.type != 'end-tag') {                if (!(token.type == 'end-tag' && token.value == 'script')) {
281                  // 7.2. This is a parse error.                  // 7.2. This is a parse error.
282                  log ('Parse error: no </' + 'script>');                  log ('Parse error: no </' + 'script>');
283                    this.nextToken.unshift (token);
284    
285                  // 7.3. Mark the script element as "already executed".                  // 7.3. Mark the script element as "already executed".
286                  el.manakaiAlreadyExecuted = true;                  el.manakaiAlreadyExecuted = true;
# Line 252  Line 295 
295            // 8.1. If the parser were originally created for the ...            // 8.1. If the parser were originally created for the ...
296            if (this.fragmentParsingMode) {            if (this.fragmentParsingMode) {
297              // 8.2. Mark the script element as "already executed" and ...              // 8.2. Mark the script element as "already executed" and ...
298              el.alreadyExecuted = true;              el.manakaiAlreadyExecuted = true;
299              continue;              continue;
300            }            }
301    
# Line 269  Line 312 
312            oldInsertionPoint += this.insertionPoint;            oldInsertionPoint += this.insertionPoint;
313            this.setInsertionPoint (oldInsertionPoint);            this.setInsertionPoint (oldInsertionPoint);
314    
315            // 12. If there is a script that will execute as soon as ...            // 12. If there is a pending external script
316            while (this.scriptExecutedWhenParserResumes) {            while (this.pendingExternalScript) {
317              // 12.1. If the tree construction stage is being called reentrantly              // 12.1. If the tree construction stage is being called reentrantly
318              if (this.reentrant) {              if (this.reentrant) {
319                log ('parse: abort (reentrance)');                log ('parse: abort (reentrance)');
# Line 280  Line 323 
323              // 12.2. Otherwise              // 12.2. Otherwise
324              } else {              } else {
325                // 1.                // 1.
326                var script = this.scriptExecutedWhenParserResumes;                var script = this.pendingExternalScript;
327                this.scriptExecutedWhenParserResumes = null;                this.pendingExternalScript = null;
328    
329                // 2. Pause until the script has completed loading.                // 2. Pause until the script has completed loading.
330                //                //
# Line 299  Line 342 
342                //                //
343              }              }
344            }            }
345            } else if (token.value == 'style' ||
346                       token.value == 'noscript' ||
347                       token.value == 'xmp') {
348              // 1. Create an element for the token in the HTML namespace.
349              var el = new JSElement (this.doc, token.value);
350    
351              // 2. Append the new element to the current node.
352              this.openElements[this.openElements.length - 1].appendChild (el);
353    
354              // 3. Switch the tokeniser's content model flag to the CDATA state.
355              this.parseMode = 'cdata';
356              this.endTagName = token.value;
357    
358              // 4.1. Collect all the character tokens.
359              while (true) {
360                var token = this.getNextToken ();
361                log ('token: ' + token.type + ' "' + token.value + '"');
362    
363                if (token.type == 'char') {
364                  // 5. Append a single Text node to the script element node.
365                  el.manakaiAppendText (token.value);
366    
367                // 4.2. Until it returns a token that is not a character token, or
368                // until it stops tokenising.
369                } else if (token.type == 'eof' ||
370                           token.type == 'end-tag' ||
371                           token.type == 'abort') {
372                  // 6. Switched back to the PCDATA state.
373                  this.parseMode = 'pcdata';
374    
375                  if (token.type == 'abort') {
376                    this.cdataEndTagRequired = true;
377                    break;
378                  }
379    
380                  // 7.1. If the next token is not an end tag token with ...
381                  if (!(token.type == 'end-tag' &&
382                        token.value == this.endTagName)) {
383                    // 7.2. This is a parse error.
384                    log ('Parse error: no </' + this.endTagName + '>');
385                    this.nextToken.unshift (token);
386    
387                    // 7.3. Mark the script element as "already executed".
388                    el.manakaiAlreadyExecuted = true;
389                  } else {
390                    // 7.4. Ignore it.
391                    //
392                  }
393                  break;
394                }
395              }
396          } else {          } else {
397            var el = new JSElement (this.doc, token.value);            var el = new JSElement (this.doc, token.value);
398            this.openElements[this.openElements.length - 1].appendChild (el);            this.openElements[this.openElements.length - 1].appendChild (el);
# Line 329  Line 423 
423    
424      // "When a script completes loading" rules start applying.      // "When a script completes loading" rules start applying.
425    
426      // TODO: Handles "list of scripts that will execute as soon as possible"      while (this.scriptsExecutedSoon.length > 0 ||
427      // and "list of scripts that will execute asynchronously"             this.scriptsExecutedAsynchronously.length > 0) {
428          // Handle "list of scripts that will execute as soon as possible".
429          while (this.scriptsExecutedSoon.length > 0) {
430            var e = this.scriptsExecutedSoon.shift ();
431      
432            // If it has completed loading
433            log ('Execute an external script not inserted by parser...');
434            executeScript (this.doc, e);
435    
436            // NOTE: It MAY be executed before the end of the parsing, according
437            // to the spec.
438            this.hasAsyncScript = true;
439          }
440    
441          // Handle "list of scripts that will execute asynchronously".
442          while (this.scriptsExecutedAsynchronously.length > 0) {
443            var e = this.scriptsExecutedAsynchronously.shift ();
444    
445            // Step 1.
446            // We assume that all scripts have been loaded at this time.
447      
448            // Step 2.
449            log ('Execute an asynchronous script...');
450            executeScript (this.doc, e);
451    
452            // Step 3.
453            //
454    
455            // Step 4.
456            //
457    
458            this.hasAsyncScript = true;
459          }
460        }
461    
462      // Handle "list of scripts that will execute when the document has finished      // Handle "list of scripts that will execute when the document has finished
463      // parsing".      // parsing".
# Line 350  Line 477 
477    
478      log ('DOMContentLoaded event fired');      log ('DOMContentLoaded event fired');
479    
480      // "delays tha load event" things has completed:      // "delays the load event" things has completed:
481      // readyState = 'complete'      // readyState = 'complete'
482      log ('load event fired');      log ('load event fired');
483    
# Line 394  Line 521 
521        var doc = this.ownerDocument || this;        var doc = this.ownerDocument || this;
522        var p = doc._parser;        var p = doc._parser;
523    
524        // 1. Script type        // 1.The script's type
525        //        //
526    
527        // 2.1. If scripting is disabled        // 2. The cript's character encoding
528          //
529    
530          // 3.1. If without script
531        //        //
532        // 2.2. If the script element was created by an XML ... innerHTML ...        // 2.2. If the script element was created by an XML ... innerHTML ...
533        //        //
534        // 2.3. If the user agent does not support the scripting language ...        // 2.3. If the user agent does not support the scripting language ...
535        //        //
536        // 2.4. If the script element has its "already executed" flag set        if (false) {
       if (e.manakaiAlreadyExecuted) {  
537          // 2.5. Abort these steps at this point.          // 2.5. Abort these steps at this point.
538          log ('Running a script: aborted');          log ('Running a script: aborted (noscript)');
539          logIndentLevel--;          logIndentLevel--;
540          return e;          return e;
541        }        }
542    
543        // 3. Set the element's "already executed" flag.        // 4. Set the element's "already executed" flag.
544        e.manakaiAlreadyExecuted = true;        e.manakaiAlreadyExecuted = true;
545    
546        // 4. If the element has a src attribute, then a load for ...        // 5. If the element has a src attribute, then a load for ...
547        // TODO: load an external resource        // TODO: load an external resource
548    
549        // 5. The first of the following options:        // 5. The first of the following options:
550    
       // 5.1.  
551        if (/* TODO: If the document is still being parsed && */        if (/* TODO: If the document is still being parsed && */
552            e.defer && !e.async) {            e.defer && !e.async) {
553            // 6.1.
554          p.scriptsExecutedAfterParsing.push (e);          p.scriptsExecutedAfterParsing.push (e);
555          log ('Running a script: aborted (defer)');          log ('Running a script: aborted (defer)');
556        } else if (e.async && e.src != null) {        } else if (e.async && e.src != null) {
557          // TODO          // 6.2.
558        } else if (e.async && e.src == null          p.scriptsExecutedAsynchronously.push (e);
559                   /* && list of scripts that will execute asynchronously is not empty */) {          log ('Running a script: aborted (async src)');
560          // TODO        } else if (e.async && e.src == null &&
561                     p.scriptsExecutedAsynchronously.length > 0) {
562            // 6.3.
563            p.scriptsExecutedAsynchronously.push (e);
564            log ('Running a script: aborted (async)');
565        } else if (e.src != null && e.manakaiParserInserted) {        } else if (e.src != null && e.manakaiParserInserted) {
566          if (p.scriptExecutedWhenParserResumes) {          // 6.4.
567            log ('Error: There is a script that will execute as soon as the parser resumes.');          if (p.pendingExternalScript) {
568              log ('Error: There is a pending external script.');
569          }          }
570          p.scriptExecutedWhenParserResumes = e;          p.pendingExternalScript = e;
571          log ('Running a script: aborted (src)');          log ('Running a script: aborted (src parser-inserted)');
572        } else if (e.src != null) {        } else if (e.src != null) {
573          // TODO          // 6.5.
574            p.scriptsExecutedSoon.push (e);
575            log ('Running a script: aborted (src)');
576        } else {        } else {
577            // 6.6.
578          executeScript (doc, e); // even if other scripts are already executing.          executeScript (doc, e); // even if other scripts are already executing.
579        }        }
580    
# Line 467  Line 604 
604      }      }
605    
606      // If the load was successful      // If the load was successful
     log ('load event fired at the script element');  
607    
608      if (true) {      if (true) {
609      // Scripting is enabled, Document.designMode is disabled,      // Scripting is enabled, Document.designMode is disabled,
# Line 476  Line 612 
612        parseAndRunScript (doc, s);        parseAndRunScript (doc, s);
613      }      }
614    
615        log ('load event fired at the script element');
616    
617      log ('executing a script block: end');      log ('executing a script block: end');
618    } // executeScript    } // executeScript
619    
# Line 484  Line 622 
622        var m;        var m;
623        if (m = uri.match (/^javascript:\s*(?:'([^']*)'|"([^"]+)")\s*$/i)) {        if (m = uri.match (/^javascript:\s*(?:'([^']*)'|"([^"]+)")\s*$/i)) {
624          if (m[1]) {          if (m[1]) {
625            return m[1];            return unescapeJSLiteral (m[1]);
626          } else if (m[2]) {          } else if (m[2]) {
627            return m[2];            return unescapeJSLiteral (m[2]);
628          } else {          } else {
629            return null;            return null;
630          }          }
# Line 507  Line 645 
645          matched = true;          matched = true;
646          var args = [];          var args = [];
647          t.replace (/('[^']*'|"[^"]*")/g, function (s, v) {          t.replace (/('[^']*'|"[^"]*")/g, function (s, v) {
648            args.push (v.substring (1, v.length - 1));            args.push (unescapeJSLiteral (v.substring (1, v.length - 1)));
649            return '';            return '';
650          });          });
651          doc.write.apply (doc, args);          doc.write.apply (doc, args);
652          return '';          return '';
653        });        });
654          var noDocumentElement = false;
655          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*/,
656          function (s, t, u) {
657            matched = true;
658            var args = [unescapeJSLiteral (t ? t : u)];
659            noDocumentElement = !doc._insertExternalScript.apply (doc, args);
660            return '';
661          });
662          if (noDocumentElement) {
663            log ('Script error: documentElement is null');
664            break;
665          }
666          s = s.replace (/^\s*w\s*\(\s*document\.documentElement\.innerHTML\s*\)\s*;\s*/,
667          function (s, t) {
668            matched = true;
669            log (dumpTree (doc, ''));
670            return '';
671          });
672        if (s == '') break;        if (s == '') break;
673        if (!matched) {        if (!matched) {
674          log ('Script parse error: "' + s + '"');          log ('Script parse error: "' + s + '"');
# Line 521  Line 677 
677      }      }
678    } // parseAndRunScript    } // parseAndRunScript
679    
680      function unescapeJSLiteral (s) {
681        return s.replace (/\\u([0-9A-Fa-f]{4})/g, function (t, v) {
682          return String.fromCharCode (parseInt ('0x' + v));
683        });
684      } // unescapeJSLiteral
685    
686    function JSText (data) {    function JSText (data) {
687      this.data = data;      this.data = data;
688    } // JSText    } // JSText
# Line 589  Line 751 
751    }; // document.open    }; // document.open
752    
753    JSDocument.prototype.write = function () {    JSDocument.prototype.write = function () {
754        log ('document.write: start');
755      logIndentLevel++;      logIndentLevel++;
756    
757      var p = this._parser;      var p = this._parser;
# Line 608  Line 771 
771          + p.input.s.substring (p.insertionPoint, p.input.s.length);          + p.input.s.substring (p.insertionPoint, p.input.s.length);
772      p.insertionPoint += s.length;      p.insertionPoint += s.length;
773    
774      // 3. If there is a script that will execute as soon as the parser resumes      // 3. If there is a pending external script
775      if (p.scriptExecutedAfterParserResumes) {      if (p.pendingExternalScript) {
776        log ('document.write: processed later (there is an unprocessed <script src>)');        log ('document.write: processed later (there is an unprocessed <script src>)');
777        logIndentLevel--;        logIndentLevel--;
778          log ('document.write: return');
779        return;        return;
780      }      }
781    
# Line 625  Line 789 
789      // to do something here?      // to do something here?
790    
791      // 5. Return      // 5. Return
792        logIndentLevel--;
793      log ('document.write: return');      log ('document.write: return');
794    
     logIndentLevel--;  
795      return;      return;
796    }; // document.write    }; // document.write
797    
798      JSDocument.prototype._insertExternalScript = function (uri) {
799        var s = new JSElement (this, 'script');
800        s.src = uri;
801        if (this.documentElement) {
802          this.documentElement.appendChild (s);
803          return true;
804        } else {
805          return false;
806        }
807      }; // _insertExternalScript
808    
809      JSDocument.prototype.__defineGetter__ ('documentElement', function () {
810        var cn = this.childNodes;
811        for (var i = 0; i < cn.length; i++) {
812          if (cn[i] instanceof JSElement) {
813            return cn[i]
814          }
815        }
816        return null;
817      });
818    
819    JSElement.prototype.__defineGetter__ ('text', function () {    JSElement.prototype.__defineGetter__ ('text', function () {
820      var r = '';      var r = '';
821      for (var i = 0; i < this.childNodes.length; i++) {      for (var i = 0; i < this.childNodes.length; i++) {
# Line 700  document.write ('aaaaaaa&lt;/p>&lt;scrip Line 885  document.write ('aaaaaaa&lt;/p>&lt;scrip
885  &lt;p>  &lt;p>
886  </textarea>  </textarea>
887    
888  <h2>Log</h2>  <h2 id=log>Log</h2>
889  <p><output></output>  <p><output></output>
890    
891  <h2>Note</h2>  <h2 id=notes>Notes</h2>
892    
893  <p>This is a <em>simplified</em> implementation of  <p>This is a <em>simplified</em> implementation of
894  <a href="http://www.whatwg.org/specs/web-apps/current-work/#parsing">HTML5  <a href="http://www.whatwg.org/specs/web-apps/current-work/#parsing">HTML5
# Line 718  algorithm.  Especially, this parser: Line 903  algorithm.  Especially, this parser:
903  algorithm, and so on.  algorithm, and so on.
904  <li>Does not raise parse errors for invalid attribute specifications in start  <li>Does not raise parse errors for invalid attribute specifications in start
905  or end tags.  or end tags.
906  <li>Does not support CDATA/PCDATA element other than <code>script</code>.  <li>Does not support RCDATA elements (<code>title</code> and
907    <code>textarea</code>).
908    <li>Does not strip the first newline in <code>pre</code>,
909    <code>listing</code>, and <code>textarea</code> elements.
910  <li>Does not support <code>&lt;!--</code>..<code>--></code> parsing rule  <li>Does not support <code>&lt;!--</code>..<code>--></code> parsing rule
911  in <code>script</code> element.  in CDATA/RCDATA elements.
912  <li>Does not support foreign (SVG or MathML) elements.  <li>Does not support foreign (SVG or MathML) elements.
913  <li>Only supports <code>script</code> <code>type</code>  <li>Only supports <code>script</code> <code>type</code>
914  <code>text/javascript</code>.  <code>type</code> and <code>language</code>  <code>text/javascript</code>.  <code>type</code> and <code>language</code>
915  attributes are ignored.  attributes are ignored.
916  <li>Only supports <code>document.write</code>.  <li>Only supports limited statements.  It must consist of zero or more
917  The script code must be match to the regular expression  of statements looking similar to the following statements, possibly
918  <code>^\s*(?:document\.write\s*\(<var>v</var>\s*(?:,\s*<var>v</var>\s*)*\)\s*;\s*)*$</code>  introduced, followed, or separated by white space characters:
919  where <var>v</var> is <code>"[^"]*"|'[^']*'</code>.    <ul>
920      <li><code>document.write ("<var>string</var>", ["<var>string</var>", ...]);</code>.
921      <li><code>var s = document.createElement ("script");
922                s.src = "<var>string</var>";
923                document.documentElement.appendChild (s);</code>
924      <li><code>w (document.documentElement.innerHTML);</code> (This statement
925      can be used to dump the document, even when the document has no
926      document element.  The output format is the tree dump format used
927      in html5lib test data, not <abbr>HTML</abbr>.)
928      </ul>
929    Note that strings may be delimited by <code>'</code>s instead of
930    <code>"</code>s.
931  <li>Only supports <code>javascript:</code>  <li>Only supports <code>javascript:</code>
932  <abbr title="Uniform Resourace Identifiers">URI</abbr> scheme in the  <abbr title="Uniform Resourace Identifiers">URI</abbr> scheme in the
933  <code>src</code> attribute of the <code>script</code> element.  In addition,  <code>src</code> attribute of the <code>script</code> element.  In addition,
934  the <abbr title="Uniform Resource Identifiers">URI</abbr> must be conform to  the <abbr title="Uniform Resource Identifiers">URI</abbr> must be conform to
935  the regular expression <code>^javascript:\s*(?:"[^"]*"|'[^']*')\s*$</code>.  the regular expression <code>^javascript:\s*(?:"[^"]*"|'[^']*')\s*$</code>.
936    <li>Only supports <code>\u<var>HHHH</var></code> escapes in JavaScript
937    string literals.
938    <li>Does not handle <i>stop parsing</i> phase correctly if the document is
939    replaced by <code>document.open ()</code> call.  In other word, delayed
940    (deferred or asynchronous) script executions and event firings might be
941    treated in a wrong way if a <code>document.open ()</code> invocation
942    is implicitly done by <code>document.write ()</code> in a delayed script.
943  </ul>  </ul>
944    
945  <p>For some reason, this parser does not work in browsers that do  <p>For some reason, this parser does not work in browsers that do
946  not support JavaScript 1.5.  not support JavaScript 1.5.
947    
948    <!-- TODO: |src| attribute value should refer the value at the time
949    when it is inserted into the document, not the value when the script is
950    executed.  Currently it does not matter, since we don't allow dynamic
951    modification to the |src| content/DOM attribute value yet. -->
952    
953    <p>See also
954    <a href="http://suika.fam.cx/gate/2005/sw/Live%20Scripting%20HTML%20Parser">SuikaWiki:
955    Live Scripting HTML Parser</a>.
956    
957  </body>  </body>
 </html>  
958    </html>
959    <!-- $Date$ -->
960    <!--
961    
962    Copyright 2008 Wakaba <w@suika.fam.cx>
963    
964    This program is free software; you can redistribute it and/or
965    modify it under the terms of the GNU General Public License
966    as published by the Free Software Foundation; either version 2
967    of the License, or (at your option) any later version.
968    
969    This program is distributed in the hope that it will be useful,
970    but WITHOUT ANY WARRANTY; without even the implied warranty of
971    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
972    GNU General Public License for more details.
973    
974    You should have received a copy of the GNU General Public License
975    along with this program; if not, write to the Free Software
976    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
977    
978    -->

Legend:
Removed from v.1.9  
changed lines
  Added in v.1.20

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24  
Google Analytics is used in this page; Cookies are used. 忍者AdMax is used in this page; Cookies are used. Privacy policy.