Home Reference Source Test Repository

src/lib/gfm.js

const Util = require( './util.js' );

/**
 * Convert the DOM node to cell text of the table.
 *
 * @param {String} content Content text.
 * @param {Node}   node    DOM node.
 *
 * @return {String} Cell text.
 */
function Cell( node, content ) {
  let index = 0;
  if( node.parentNode && node.parentNode.childNodes ) {
    index = Util.arrayIndexOf( node.parentNode.childNodes, node );
  }

  const prefix = ( index === 0 ? '| ' : ' ' );
  return prefix + content + ' |';
}

/**
 * RegExp for the higlighting.
 * @type {RegExp}
 */
const HighlightRegEx = /highlight highlight-(\S+)/;

/**
 * Convert the HTML DOM Node to GFM ( GitHub Flavored Markdown ) text.
 * @type {Array.<MdConverter>}
 * @see https://github.com/domchristie/to-markdown/blob/master/lib/gfm-converters.js
 */
const GfmConverters = [
  // Line break
  {
    filter: 'br',
    replacement: () => {
      return '\n';
    }
  },
  // Delete
  {
    filter: [ 'del', 's', 'strike' ],
    replacement: ( node, content ) => {
      return '~~' + content + '~~';
    }
  },
  // Checkbox
  {
    filter: ( node ) => {
      return node.type === 'checkbox' && node.parentNode.nodeName === 'LI';
    },
    replacement: ( node ) => {
      return ( node.checked ? '[x]' : '[ ]' ) + ' ';
    }
  },
  // Table cell
  {
    filter: [ 'th', 'td' ],
    replacement: ( node, content ) => {
      return Cell( node, content );
    }
  },
  // Table row
  {
    filter: 'tr',
    replacement: ( node, content ) => {
      let   borderCells = '';
      const alignMap    = { left: ':--', right: '--:', center: ':-:' };

      if( node.parentNode.nodeName === 'THEAD' ) {
        for( let i = 0, max = node.childNodes.length; i < max; ++i ) {
          const elm    = node.childNodes[ i ];
          const align  = ( elm.attributes ? elm.attributes.align : null );
          const border = ( align ? alignMap[ align.value ] : '---' );

          if( elm._replacement ) {
            borderCells += Cell( elm, border );
          }
        }
      }

      return '\n' + content + ( borderCells ? '\n' + borderCells : '' );
    }
  },
  // Table
  {
    filter: 'table',
    replacement: ( node, content ) => {
      return '\n\n' + content + '\n\n';
    }
  },
  // Table parts
  {
    filter: [ 'thead', 'tbody', 'tfoot' ],
    replacement: ( node, content ) => {
      return content;
    }
  },
  // Fenced code blocks
  {
    filter: ( node ) => {
      return node.nodeName === 'PRE' &&
             node.firstChild &&
             node.firstChild.nodeName === 'CODE';
    },
    replacement: ( node ) => {
      return '\n\n```\n' + node.firstChild.textContent + '\n```\n\n';
    }
  },
  // Syntax-highlighted code blocks
  {
    filter: ( node ) => {
      return node.nodeName === 'PRE' &&
             node.parentNode.nodeName === 'DIV' &&
             HighlightRegEx.test( node.parentNode.className );
    },
    replacement: ( node ) => {
      const language = node.parentNode.className.match( HighlightRegEx )[ 1 ];
      return '\n\n```' + language + '\n' + node.textContent + '\n```\n\n';
    }
  },
  // Div
  {
    filter: ( node ) => {
      return node.nodeName === 'DIV' &&
             HighlightRegEx.test( node.className );
    },
    replacement: ( node, content ) => {
      return '\n\n' + content + '\n\n';
    }
  }
];

module.exports = GfmConverters;