You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

104 lines
3.4 KiB

  1. /*jshint -W030 */
  2. var tagRE = /(?:<!--[\S\s]*?-->|<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>)/g;
  3. var parseTag = require('./parse-tag');
  4. // re-used obj for quick lookups of components
  5. var empty = Object.create ? Object.create(null) : {};
  6. // common logic for pushing a child node onto a list
  7. function pushTextNode(list, html, level, start, ignoreWhitespace) {
  8. // calculate correct end of the content slice in case there's
  9. // no tag after the text node.
  10. var end = html.indexOf('<', start);
  11. var content = html.slice(start, end === -1 ? undefined : end);
  12. // if a node is nothing but whitespace, collapse it as the spec states:
  13. // https://www.w3.org/TR/html4/struct/text.html#h-9.1
  14. if (/^\s*$/.test(content)) {
  15. content = ' ';
  16. }
  17. // don't add whitespace-only text nodes if they would be trailing text nodes
  18. // or if they would be leading whitespace-only text nodes:
  19. // * end > -1 indicates this is not a trailing text node
  20. // * leading node is when level is -1 and list has length 0
  21. if ((!ignoreWhitespace && end > -1 && level + list.length >= 0) || content !== ' ') {
  22. list.push({
  23. type: 'text',
  24. content: content
  25. });
  26. }
  27. }
  28. module.exports = function parse(html, options) {
  29. options || (options = {});
  30. options.components || (options.components = empty);
  31. var result = [];
  32. var current;
  33. var level = -1;
  34. var arr = [];
  35. var byTag = {};
  36. var inComponent = false;
  37. html.replace(tagRE, function (tag, index) {
  38. if (inComponent) {
  39. if (tag !== ('</' + current.name + '>')) {
  40. return;
  41. } else {
  42. inComponent = false;
  43. }
  44. }
  45. var isOpen = tag.charAt(1) !== '/';
  46. var isComment = tag.indexOf('<!--') === 0;
  47. var start = index + tag.length;
  48. var nextChar = html.charAt(start);
  49. var parent;
  50. if (isOpen && !isComment) {
  51. level++;
  52. current = parseTag(tag);
  53. if (current.type === 'tag' && options.components[current.name]) {
  54. current.type = 'component';
  55. inComponent = true;
  56. }
  57. if (!current.voidElement && !inComponent && nextChar && nextChar !== '<') {
  58. pushTextNode(current.children, html, level, start, options.ignoreWhitespace);
  59. }
  60. byTag[current.tagName] = current;
  61. // if we're at root, push new base node
  62. if (level === 0) {
  63. result.push(current);
  64. }
  65. parent = arr[level - 1];
  66. if (parent) {
  67. parent.children.push(current);
  68. }
  69. arr[level] = current;
  70. }
  71. if (isComment || !isOpen || current.voidElement) {
  72. if (!isComment) {
  73. level--;
  74. }
  75. if (!inComponent && nextChar !== '<' && nextChar) {
  76. // trailing text node
  77. // if we're at the root, push a base text node. otherwise add as
  78. // a child to the current node.
  79. parent = level === -1 ? result : arr[level].children;
  80. pushTextNode(parent, html, level, start, options.ignoreWhitespace);
  81. }
  82. }
  83. });
  84. // If the "html" passed isn't actually html, add it as a text node.
  85. if (!result.length && html.length) {
  86. pushTextNode(result, html, 0, 0, options.ignoreWhitespace);
  87. }
  88. return result;
  89. };