p-as-heading-evaluate.js 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import { findUpVirtual } from '../../commons/dom';
  2. function normalizeFontWeight(weight) {
  3. switch (weight) {
  4. case 'lighter':
  5. return 100;
  6. case 'normal':
  7. return 400;
  8. case 'bold':
  9. return 700;
  10. case 'bolder':
  11. return 900;
  12. }
  13. weight = parseInt(weight);
  14. return !isNaN(weight) ? weight : 400;
  15. }
  16. function getTextContainer(elm) {
  17. let nextNode = elm;
  18. const outerText = elm.textContent.trim();
  19. let innerText = outerText;
  20. while (innerText === outerText && nextNode !== undefined) {
  21. let i = -1;
  22. elm = nextNode;
  23. if (elm.children.length === 0) {
  24. return elm;
  25. }
  26. do {
  27. // find the first non-empty child
  28. i++;
  29. innerText = elm.children[i].textContent.trim();
  30. } while (innerText === '' && i + 1 < elm.children.length);
  31. nextNode = elm.children[i];
  32. }
  33. return elm;
  34. }
  35. function getStyleValues(node) {
  36. const style = window.getComputedStyle(getTextContainer(node));
  37. return {
  38. fontWeight: normalizeFontWeight(style.getPropertyValue('font-weight')),
  39. fontSize: parseInt(style.getPropertyValue('font-size')),
  40. isItalic: style.getPropertyValue('font-style') === 'italic'
  41. };
  42. }
  43. function isHeaderStyle(styleA, styleB, margins) {
  44. return margins.reduce((out, margin) => {
  45. return (
  46. out ||
  47. ((!margin.size || styleA.fontSize / margin.size > styleB.fontSize) &&
  48. (!margin.weight ||
  49. styleA.fontWeight - margin.weight > styleB.fontWeight) &&
  50. (!margin.italic || (styleA.isItalic && !styleB.isItalic)))
  51. );
  52. }, false);
  53. }
  54. function pAsHeadingEvaluate(node, options, virtualNode) {
  55. const siblings = Array.from(node.parentNode.children);
  56. const currentIndex = siblings.indexOf(node);
  57. options = options || {};
  58. const margins = options.margins || [];
  59. const nextSibling = siblings
  60. .slice(currentIndex + 1)
  61. .find(elm => elm.nodeName.toUpperCase() === 'P');
  62. const prevSibling = siblings
  63. .slice(0, currentIndex)
  64. .reverse()
  65. .find(elm => elm.nodeName.toUpperCase() === 'P');
  66. const currStyle = getStyleValues(node);
  67. const nextStyle = nextSibling ? getStyleValues(nextSibling) : null;
  68. const prevStyle = prevSibling ? getStyleValues(prevSibling) : null;
  69. if (!nextStyle || !isHeaderStyle(currStyle, nextStyle, margins)) {
  70. return true;
  71. }
  72. const blockquote = findUpVirtual(virtualNode, 'blockquote');
  73. if (blockquote && blockquote.nodeName.toUpperCase() === 'BLOCKQUOTE') {
  74. return undefined;
  75. }
  76. if (prevStyle && !isHeaderStyle(currStyle, prevStyle, margins)) {
  77. return undefined;
  78. }
  79. return false;
  80. }
  81. export default pAsHeadingEvaluate;