parser_stream.js 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. 'use strict';
  2. var WritableStream = require('stream').Writable,
  3. inherits = require('util').inherits,
  4. Parser = require('./index');
  5. var ParserStream = module.exports = function (options) {
  6. WritableStream.call(this);
  7. this.parser = new Parser(options);
  8. this.lastChunkWritten = false;
  9. this.writeCallback = null;
  10. this.pausedByScript = false;
  11. this.document = this.parser.treeAdapter.createDocument();
  12. this.pendingHtmlInsertions = [];
  13. this._resume = this._resume.bind(this);
  14. this._documentWrite = this._documentWrite.bind(this);
  15. this._scriptHandler = this._scriptHandler.bind(this);
  16. this.parser._bootstrap(this.document, null);
  17. };
  18. inherits(ParserStream, WritableStream);
  19. //WritableStream implementation
  20. ParserStream.prototype._write = function (chunk, encoding, callback) {
  21. this.writeCallback = callback;
  22. this.parser.tokenizer.write(chunk.toString('utf8'), this.lastChunkWritten);
  23. this._runParsingLoop();
  24. };
  25. ParserStream.prototype.end = function (chunk, encoding, callback) {
  26. this.lastChunkWritten = true;
  27. WritableStream.prototype.end.call(this, chunk || '', encoding, callback);
  28. };
  29. //Scriptable parser implementation
  30. ParserStream.prototype._runParsingLoop = function () {
  31. this.parser.runParsingLoopForCurrentChunk(this.writeCallback, this._scriptHandler);
  32. };
  33. ParserStream.prototype._resume = function () {
  34. if (!this.pausedByScript)
  35. throw new Error('Parser was already resumed');
  36. while (this.pendingHtmlInsertions.length) {
  37. var html = this.pendingHtmlInsertions.pop();
  38. this.parser.tokenizer.insertHtmlAtCurrentPos(html);
  39. }
  40. this.pausedByScript = false;
  41. //NOTE: keep parsing if we don't wait for the next input chunk
  42. if (this.parser.tokenizer.active)
  43. this._runParsingLoop();
  44. };
  45. ParserStream.prototype._documentWrite = function (html) {
  46. if (!this.parser.stopped)
  47. this.pendingHtmlInsertions.push(html);
  48. };
  49. ParserStream.prototype._scriptHandler = function (scriptElement) {
  50. if (this.listeners('script').length) {
  51. this.pausedByScript = true;
  52. this.emit('script', scriptElement, this._documentWrite, this._resume);
  53. }
  54. else
  55. this._runParsingLoop();
  56. };