| 26 |
log (dumpTree (doc, '')); |
log (dumpTree (doc, '')); |
| 27 |
} // update |
} // update |
| 28 |
|
|
| 29 |
|
var logIndentLevel = 0; |
| 30 |
function log (s) { |
function log (s) { |
| 31 |
|
for (var i = 0; i < logIndentLevel; i++) { |
| 32 |
|
s = ' ' + s; |
| 33 |
|
} |
| 34 |
document.logElement.appendChild (document.createTextNode (s + "\n")); |
document.logElement.appendChild (document.createTextNode (s + "\n")); |
| 35 |
} // log |
} // log |
| 36 |
|
|
| 125 |
tagName = v.toLowerCase (); |
tagName = v.toLowerCase (); |
| 126 |
return ''; |
return ''; |
| 127 |
}); |
}); |
| 128 |
e = e.replace (/^\s*(\S+)\s*(?:=\s*"([^"]*)"|'([^']*)'|([^"']+))?/, |
e = e.replace (/^\s*([^\s=]+)\s*(?:=\s*(?:"([^"]*)"|'([^']*)'|([^"']+)))?/, |
| 129 |
function (x, attrName, attrValue1, attrValue2, attrValue3) { |
function (x, attrName, attrValue1, attrValue2, attrValue3) { |
| 130 |
attrs[attrName] = attrValue1 || attrValue2 || attrValue3; |
v = attrValue1 || attrValue2 || attrValue3; |
| 131 |
|
v = v.replace (/"/g, '"').replace (/'/g, "'") |
| 132 |
|
.replace (/&/g, '&'); |
| 133 |
|
attrs[attrName.toLowerCase ()] = v; |
| 134 |
return ''; |
return ''; |
| 135 |
}); |
}); |
| 136 |
|
if (e.length) { |
| 137 |
|
log ('Broken start tag: "' + e + '"'); |
| 138 |
|
} |
| 139 |
token = {type: 'start-tag', value: tagName, attrs: attrs}; |
token = {type: 'start-tag', value: tagName, attrs: attrs}; |
| 140 |
p.insertionPoint -= s.length; |
p.insertionPoint -= s.length; |
| 141 |
return ''; |
return ''; |
| 166 |
} // getNextToken |
} // getNextToken |
| 167 |
|
|
| 168 |
Parser.prototype.parse = function () { |
Parser.prototype.parse = function () { |
| 169 |
log ('start parsing'); |
logIndentLevel++; |
| 170 |
|
log ('parse: start'); |
| 171 |
|
|
| 172 |
while (true) { |
while (true) { |
| 173 |
var token = this.getNextToken (); |
var token = this.getNextToken (); |
| 239 |
this.setInsertionPoint (oldInsertionPoint); |
this.setInsertionPoint (oldInsertionPoint); |
| 240 |
|
|
| 241 |
// 12. If there is a script that will execute as soon as ... |
// 12. If there is a script that will execute as soon as ... |
| 242 |
|
while (this.scriptExecutedWhenParserResumes) { |
| 243 |
|
// 12.1. If the tree construction stage is being called reentrantly |
| 244 |
|
if (this.reentrant) { |
| 245 |
|
log ('parse: abort (reentrance)'); |
| 246 |
|
logIndentLevel--; |
| 247 |
|
return; |
| 248 |
|
|
| 249 |
|
// 12.2. Otherwise |
| 250 |
|
} else { |
| 251 |
|
// 1. |
| 252 |
|
var script = this.scriptExecutedWhenParserResumes; |
| 253 |
|
this.scriptExecutedWhenParserResumes = null; |
| 254 |
|
|
| 255 |
|
// 2. Pause until the script has completed loading. |
| 256 |
|
// |
| 257 |
|
|
| 258 |
|
// 3. Let the insertion point to just before the next input char. |
| 259 |
|
this.setInsertionPoint (0); |
| 260 |
|
|
| 261 |
|
// 4. Execute the script. |
| 262 |
|
executeScript (this.doc, script); |
| 263 |
|
|
| 264 |
|
// 5. Let the insertion point be undefined again. |
| 265 |
|
this.setInsertionPoint (undefined); |
| 266 |
|
|
| 267 |
|
// 6. If there is once again a script that will execute ... |
| 268 |
|
// |
| 269 |
|
} |
| 270 |
|
} |
| 271 |
} else { |
} else { |
| 272 |
var el = new JSElement (this.doc, token.value); |
var el = new JSElement (this.doc, token.value); |
| 273 |
this.openElements[this.openElements.length - 1].appendChild (el); |
this.openElements[this.openElements.length - 1].appendChild (el); |
| 287 |
break; |
break; |
| 288 |
} else if (token.type == 'abort') { |
} else if (token.type == 'abort') { |
| 289 |
log ('parse: abort'); |
log ('parse: abort'); |
| 290 |
|
logIndentLevel--; |
| 291 |
return; |
return; |
| 292 |
} |
} |
| 293 |
} |
} |
| 322 |
// "delays tha load event" things has completed: |
// "delays tha load event" things has completed: |
| 323 |
// readyState = 'complete' |
// readyState = 'complete' |
| 324 |
log ('load event fired'); |
log ('load event fired'); |
| 325 |
|
|
| 326 |
|
logIndentLevel--; |
| 327 |
} // parse |
} // parse |
| 328 |
|
|
| 329 |
Parser.prototype.setInsertionPoint = function (ip) { |
Parser.prototype.setInsertionPoint = function (ip) { |
| 357 |
e.parentNode = this; |
e.parentNode = this; |
| 358 |
|
|
| 359 |
if (e.localName == 'script') { |
if (e.localName == 'script') { |
| 360 |
|
logIndentLevel++; |
| 361 |
log ('Running a script: start'); |
log ('Running a script: start'); |
| 362 |
|
|
| 363 |
var doc = this.ownerDocument || this; |
var doc = this.ownerDocument || this; |
| 376 |
if (e.manakaiAlreadyExecuted) { |
if (e.manakaiAlreadyExecuted) { |
| 377 |
// 2.5. Abort these steps at this point. |
// 2.5. Abort these steps at this point. |
| 378 |
log ('Running a script: aborted'); |
log ('Running a script: aborted'); |
| 379 |
|
logIndentLevel--; |
| 380 |
return e; |
return e; |
| 381 |
} |
} |
| 382 |
|
|
| 399 |
/* && list of scripts that will execute asynchronously is not empty */) { |
/* && list of scripts that will execute asynchronously is not empty */) { |
| 400 |
// TODO |
// TODO |
| 401 |
} else if (e.src != null && e.manakaiParserInserted) { |
} else if (e.src != null && e.manakaiParserInserted) { |
| 402 |
// TODO |
if (p.scriptExecutedWhenParserResumes) { |
| 403 |
|
log ('Error: There is a script that will execute as soon as the parser resumes.'); |
| 404 |
|
} |
| 405 |
|
p.scriptExecutedWhenParserResumes = e; |
| 406 |
|
log ('Running a script: aborted (src)'); |
| 407 |
} else if (e.src != null) { |
} else if (e.src != null) { |
| 408 |
// TODO |
// TODO |
| 409 |
} else { |
} else { |
| 411 |
} |
} |
| 412 |
|
|
| 413 |
log ('Running a script: end'); |
log ('Running a script: end'); |
| 414 |
|
logIndentLevel--; |
| 415 |
} |
} |
| 416 |
|
|
| 417 |
return e; |
return e; |
| 420 |
function executeScript (doc, e) { |
function executeScript (doc, e) { |
| 421 |
log ('executing a script block: start'); |
log ('executing a script block: start'); |
| 422 |
|
|
| 423 |
// If the load resulted in an error, then ... firing an error event ... |
var s; |
| 424 |
|
if (e.src != null) { |
| 425 |
|
s = getExternalScript (e.src); |
| 426 |
|
|
| 427 |
|
// If the load resulted in an error, then ... firing an error event ... |
| 428 |
|
if (s == null) { |
| 429 |
|
log ('error event fired at the script element'); |
| 430 |
|
return; |
| 431 |
|
} |
| 432 |
|
|
| 433 |
|
log ('External script loaded: "' + s + '"'); |
| 434 |
|
} else { |
| 435 |
|
s = e.text; |
| 436 |
|
} |
| 437 |
|
|
| 438 |
// If the load was successful |
// If the load was successful |
| 439 |
log ('load event fired at the script element'); |
log ('load event fired at the script element'); |
| 442 |
// Scripting is enabled, Document.designMode is disabled, |
// Scripting is enabled, Document.designMode is disabled, |
| 443 |
// Document is the active document in its browsing context |
// Document is the active document in its browsing context |
| 444 |
|
|
|
var s; |
|
|
if (e.src != null) { |
|
|
// TODO: from external file |
|
|
} else { |
|
|
s = e.text; |
|
|
} |
|
|
|
|
| 445 |
parseAndRunScript (doc, s); |
parseAndRunScript (doc, s); |
| 446 |
} |
} |
| 447 |
|
|
| 448 |
log ('executing a script block: end'); |
log ('executing a script block: end'); |
| 449 |
} // executeScript |
} // executeScript |
| 450 |
|
|
| 451 |
|
function getExternalScript (uri) { |
| 452 |
|
if (uri.match (/^javascript:/i)) { |
| 453 |
|
var m; |
| 454 |
|
if (m = uri.match (/^javascript:\s*(?:'([^']*)'|"([^"]+)")\s*$/i)) { |
| 455 |
|
if (m[1]) { |
| 456 |
|
return m[1]; |
| 457 |
|
} else if (m[2]) { |
| 458 |
|
return m[2]; |
| 459 |
|
} else { |
| 460 |
|
return null; |
| 461 |
|
} |
| 462 |
|
} else { |
| 463 |
|
log ('Complex javascript: URI is not supported: <' + uri + '>'); |
| 464 |
|
return null; |
| 465 |
|
} |
| 466 |
|
} else { |
| 467 |
|
log ('URI scheme not supported: <' + uri + '>'); |
| 468 |
|
return null; |
| 469 |
|
} |
| 470 |
|
} // getExternalScript |
| 471 |
|
|
| 472 |
function parseAndRunScript (doc, s) { |
function parseAndRunScript (doc, s) { |
| 473 |
while (true) { |
while (true) { |
| 474 |
var matched = false; |
var matched = false; |
| 558 |
}; // document.open |
}; // document.open |
| 559 |
|
|
| 560 |
JSDocument.prototype.write = function () { |
JSDocument.prototype.write = function () { |
| 561 |
|
logIndentLevel++; |
| 562 |
|
|
| 563 |
var p = this._parser; |
var p = this._parser; |
| 564 |
|
|
| 565 |
// 1. If the insertion point is undefined, the open() method must be ... |
// 1. If the insertion point is undefined, the open() method must be ... |
| 577 |
p.insertionPoint += s.length; |
p.insertionPoint += s.length; |
| 578 |
|
|
| 579 |
// 3. If there is a script that will execute as soon as the parser resumes |
// 3. If there is a script that will execute as soon as the parser resumes |
| 580 |
// TODO |
if (p.scriptExecutedAfterParserResumes) { |
| 581 |
|
log ('document.write: processed later (there is an unprocessed <script src>)'); |
| 582 |
|
logIndentLevel--; |
| 583 |
|
return; |
| 584 |
|
} |
| 585 |
|
|
| 586 |
// 4. Process the characters that were inserted, ... |
// 4. Process the characters that were inserted, ... |
| 587 |
|
var originalReentrant = p.reentrant; |
| 588 |
|
p.reentrant = true; |
| 589 |
p.parse (); |
p.parse (); |
| 590 |
|
p.reentrant = originalReentrant; |
| 591 |
|
// TODO: "Abort the processing of any nested invokations of the tokeniser, |
| 592 |
|
// yielding control back to the caller." (<script> parsing). Do we need |
| 593 |
|
// to do something here? |
| 594 |
|
|
| 595 |
// 5. Return |
// 5. Return |
| 596 |
log ('document.write: return'); |
log ('document.write: return'); |
| 597 |
|
|
| 598 |
|
logIndentLevel--; |
| 599 |
return; |
return; |
| 600 |
}; // document.write |
}; // document.write |
| 601 |
|
|