# All files / src/lib/markdown-it/plugins markdown-it-radio-button.js

98.3% Statements 58/59
96.55% Branches 28/29
100% Functions 14/14
98.24% Lines 56/57

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    11917x

    17x
    17x

    17x
    25x
    9x
    9x

    25x
    300x
    300x
    852x
    15x
    15x
    5x

    10x

    15x
    15x
    15x
    15x

    45x
    45x

    45x
    35x

    10x

    15x

    15x
    10x

    5x

    45x
    45x
    150x
    45x

    852x

    15x
    15x
    15x

    15x
    13x
    13x

    15x
    15x
    15x
    13x
    2x
    2x

    15x

    13x
    13x
    13x

    13x
    13x
    13x

    852x
    79x
    45x

    31x

     const crypto = require('crypto');

    var disableRadio = false;
    var useLabelWrapper = true;

    /**
     * Modified from https://github.com/revin/markdown-it-task-lists/blob/master/index.js
     */
    module.exports = function(md, options) {
      if (options) {
        disableRadio = !options.enabled;
        useLabelWrapper = !!options.label;
      }

      md.core.ruler.after('inline', 'radio-lists', function(state) {
        var tokens = state.tokens;
        for (var i = 2; i < tokens.length; i++) {
          if (isTodoItem(tokens, i)) {
            var group = attrGet(tokens[parentToken(tokens, i-2)], 'radio-group'); // try retrieve the group id
            if (group) {
              group = group[1];
            } else {
              group = crypto.createHash('md5')
                            .update(tokens[i-5].content)
                            .update(tokens[i-4].content)
                            .update(tokens[i].content).digest('hex').substr(2, 5); // generate a deterministic group id
            }
            radioify(tokens[i], state.Token, group);
            attrSet(tokens[i-2], 'class', 'radio-list-item');
            attrSet(tokens[parentToken(tokens, i-2)], 'radio-group', group); // save the group id to the top-level list
            attrSet(tokens[parentToken(tokens, i-2)], 'class', 'radio-list');
          }
        }
      });
    };

    function attrSet(token, name, value) {
      var index = token.attrIndex(name);
      var attr = [name, value];

      if (index < 0) {
        token.attrPush(attr);
      } else {
        token.attrs[index] = attr;
      }
    }

    function attrGet(token, name) {
      var index = token.attrIndex(name);

      if (index < 0) {
        return void(0);
      } else {
        return token.attrs[index];
      }
    }

    function parentToken(tokens, index) {
      var targetLevel = tokens[index].level - 1;
      for (var i = index - 1; i >= 0; i--) {
        if (tokens[i].level === targetLevel) {
          return i;
        }
      }
      return -1;
    }

    function isTodoItem(tokens, index) {
      return isInline(tokens[index]) &&
        isParagraph(tokens[index - 1]) &&
        isListItem(tokens[index - 2]) &&
        startsWithTodoMarkdown(tokens[index]);
    }

    function radioify(token, TokenConstructor, radioId) {
      token.children.unshift(makeRadioButton(token, TokenConstructor, radioId));
      token.children[1].content = token.children[1].content.slice(3);
      token.content = token.content.slice(3);

      if (useLabelWrapper) {
        token.children.unshift(beginLabel(TokenConstructor));
        token.children.push(endLabel(TokenConstructor));
      }
    }

    function makeRadioButton(token, TokenConstructor, radioId) {
      var radio = new TokenConstructor('html_inline', '', 0);
      var disabledAttr = disableRadio ? ' disabled="" ' : '';
      if (token.content.indexOf('( ) ') === 0) {
        radio.content = '<input class="radio-list-input" name="' + radioId + '"' + disabledAttr + 'type="radio">';
      } else Eif (token.content.indexOf('(x) ') === 0 || token.content.indexOf('(X) ') === 0) {
        radio.content = '<input class="radio-list-input" checked="" name="' + radioId + '"' + disabledAttr + 'type="radio">';
      }
      return radio;
    }

    // these next two functions are kind of hacky; probably should really be a
    // true block-level token with .tag=='label'
    function beginLabel(TokenConstructor) {
      var token = new TokenConstructor('html_inline', '', 0);
      token.content = '<label>';
      return token;
    }

    function endLabel(TokenConstructor) {
      var token = new TokenConstructor('html_inline', '', 0);
      token.content = '</label>';
      return token;
    }

    function isInline(token) { return token.type === 'inline'; }
    function isParagraph(token) { return token.type === 'paragraph_open'; }
    function isListItem(token) { return token.type === 'list_item_open'; }

    function startsWithTodoMarkdown(token) {
      // leading whitespace in a list item is already trimmed off by markdown-it
      return token.content.indexOf('( ) ') === 0 || token.content.indexOf('(x) ') === 0 || token.content.indexOf('(X) ') === 0;
    }