Greasy Fork is available in English.

Table to Markdown Copier

Convert html table to markdown format

// ==UserScript==
// @name         Table to Markdown Copier
// @name:vi      Chép bảng HTML qua dạng markdown
// @namespace    https://github.com/hotmit/table-markdown-userscript
// @version      1.0.1
// @description  Convert html table to markdown format
// @description:vi  Chuyển bảng html (table) qua dạng markdown.
// @author       Du Dang
// @include      http://*/*
// @include      https://*/*
// @grant        none
// @require      https://code.jquery.com/jquery-3.1.1.min.js
// @require 	 https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.5.16/clipboard.min.js
// @license      MIT
// ==/UserScript==
(function($) {
    'use strict';

    // max cell with in chars count
    var MAX_COL_SIZE = 80;

    var Str = {};

    $(function(){
        var lastThreeKeys = [], combinationLength = 3;

        // region [ Display Control ]
        /**
         * Insert the "MD" button to all the last cell in the header of
         * all the tables in the current page.
         * @private
         */
        function displayTableControl(){
            $('table').each(function(i, e){
                var id = 'btn-copy-md-' + i, $tb = $(e),
                    $btnMd = $('<button type="button" class="convert-to-markdown btn btn-primary" />'),
                    $lastCell = $tb.find('tr:first').find('td:last, th:last').first();

                $btnMd.css({
                    height: '20px',
                    width: '30px',
                    'background-color': '#81358c',
                    color: '#fff',
                    padding: '0'
                }).text('MD').attr('id', id);

                // copy markdown content to the clipboard
                new Clipboard('#' + id, {
                    text: function() {
                        return convertTableToMd($btnMd);
                    }
                });

                $lastCell.append($btnMd);
            });

            //$('.convert-to-markdown').click(convertTableToMd);
        }
        // endregion

        // region [ Code ]
        /**
         * Extract the data from the table. Return array of row of data along with
         * the maximum length for each column.
         *
         * @param $table
         * @returns {{maxLengths: Array, tableData: Array}}
         * @private
         */
        function getData($table){
            var maxLengths = [], tableData = [];

            function setMax(index, length){
                // create new column if does not exist
                while(index >= maxLengths.length){
                    maxLengths.push(0);
                }
                maxLengths[index] = Math.max(maxLengths[index], length);
            }

            $table.find('tr').each(function(trIndex, tr){
                var $tr = $(tr), row = [], offset = 0;

                $tr.find('td, th').each(function(i, td){
                    var $td = $(td), text = getText($td, trIndex), tl = text.length,
                        index = i + offset, colspan = $td.attr('colspan');

                    setMax(index, tl);
                    row.push(text);

                    if (colspan && $.isNumeric(colspan) && Number(colspan) > 1){
                        colspan = Number(colspan);
                        offset += colspan - 1;
                        for (var k=0; k<colspan; k++){
                            row.push('');
                        }
                    }
                });

                tableData.push(row);
            });

            return {
                maxLengths: maxLengths,
                tableData: tableData
            };
        }

        /**
         * Convert the data from getData to actual markdown content.
         *
         * @param $btn - The "MD" button housed inside the table.
         * @returns {string} - The markdown table content
         * @private
         */
        function convertTableToMd($btn){
            var md = '', $table = $btn.parents('table').first(), data = getData($table), i, k,
                maxLengths = data.maxLengths;

            for (i=0; i<data.tableData.length; i++){
                var row = data.tableData[i], rowMd = '| ', sepMd = '| ';

                for(k=0; k<row.length; k++){
                    var rowWidth = Math.min(maxLengths[k], MAX_COL_SIZE),
                        text = Str.padRight(row[k], ' ', rowWidth);
                    rowMd += text + ' | ';

                    // add header separator
                    if (i === 0){
                        sepMd += Str.repeat(' ', rowWidth) + ' | ';
                    }
                }

                if (rowMd.length > 2){
                    md += Str.trim(rowMd) + "\n";
                    if (sepMd.length > 2){
                        md += Str.trim(sepMd).replace(/ /g, '-') + "\n";
                    }
                }
            }

            md += getReferenceLink($table);

            // copied indicator
            $btn.css('background-color', '#6AB714');
            setTimeout(function(){
                $btn.css('background-color', '#81358c');
            }, 3000);

            return md;
        }

        /**
         * Generate markdown link to the table for future reference.
         *
         * @param $table
         * @returns {*}
         */
        function getReferenceLink($table) {
            var refLink, $anchor, refId = $table.attr('id'), href = location.href;

            if(!refId) {
                $anchor = $table.parents('[id]').first();
                if ($anchor.length){
                    refId = $anchor.attr('id');
                }
            }

            if (refId) {
                if (href.indexOf('#') != -1) {
                    refLink = href.replace(/#.+/, '#' + refId);
                }
                else {
                    refLink = href + '#' + refId;
                }
            }
            // if no id link, then just use the main link as reference
            refLink = refLink || href;
            return '[Table Source](' + refLink + ')';
        }

        /**
         * Clean up the text for the cell content. Like remove new line so it doesn't break the table.
         *
         * @param $td
         * @param trIndex
         * @returns {string|*}
         */
        function getText($td, trIndex) {
            var text = $td.text();
            if (trIndex === 0){
                // remove the MD link from the text
                text = text.replace(/MD$/, '');
            }
            text = text.replace("\n", '');
            text = text.replace(/\s/g, ' ');
            return Str.trim(text);
        }
        // endregion

        // region [ Capture shortcut keys ]
        // Activate Markdown Converter Interface
        //      => Shift, Shift, T (3 key strokes as a sequence, NOT press all together)
        $(document).on('keydown', function(e) {
            lastThreeKeys.push(e.which);
            lastThreeKeys = lastThreeKeys.slice(-combinationLength);
            if (lastThreeKeys.toString() == '16,16,84') {
                displayTableControl();
                e.preventDefault();
                return false;
            }
        });
        // endregion

        // uncomment for dev
        // displayTableControl();

    }); //end jqReady

    // region [ Str Lib ]
    Str.trim = function (s) {
        return s.replace(/^\s+/, '').replace(/\s+$/, '');
    };

    Str.padRight = function(s, padStr, totalLength){
        return s.length >= totalLength  ? s : s + Str.repeat(padStr, (totalLength-s.length)/padStr.length);
    };

    Str.repeat = function(s, count) {
        var newS = "", i;
        for (i=0; i<count; i++) {
            newS += s;
        }
        return newS;
    };
    // endregion

})(jQuery.noConflict(true));