Newer
Older
gitbucket_jkp / src / main / webapp / assets / common / js / gitbucket.js
@Naoki Takezoe Naoki Takezoe on 1 Jan 2020 24 KB (refs #2391) Fix online editor preview
$(function(){
  // disable Ajax cache
  $.ajaxSetup({ cache: false });

  // repository url text field
  $('#repository-url').click(function(){
    this.select(0, this.value.length);
  });

  // activate tooltip
  $('img[data-toggle=tooltip]').tooltip();
  $('a[data-toggle=tooltip]').tooltip();
  $('li[data-toggle=tooltip]').tooltip();

  // activate hotkey
  $('a[data-hotkey]').each(function(){
    var target = this;
    $(document).bind('keydown', $(target).data('hotkey'), function(){ target.click(); });
  });

  // anchor icon for markdown
  $('.markdown-head').on('mouseenter', function(e){
    $(this).find('span.octicon').css('visibility', 'visible');
  });
  $('.markdown-head').on('mouseleave', function(e){
    $(this).find('span.octicon').css('visibility', 'hidden');
  });

  // syntax highlighting by google-code-prettify
  prettyPrint();

  // Suppress transition animation on load
  $("body").removeClass("page-load");
});

function displayErrors(data, elem){
  var i = 0;
  $.each(data, function(key, value){
    $('#error-' + key.split(".").join("_"), elem).text(value);
    if(i === 0){
      $('#' + key, elem).focus();
    }
    i++;
  });
}

(function($){
  $.fn.watch = function(callback){
    var timer = null;
    var prevValue = this.val();

    this.on('focus', function(e){
      window.clearInterval(timer);
      timer = window.setInterval(function(){
        var newValue = $(e.target).val();
        if(prevValue != newValue){
          callback();
        }
        prevValue = newValue;
      }, 10);
    });

    this.on('blur', function(){
      window.clearInterval(timer);
    });
  };
})(jQuery);

/**
 * Render diff using jsdifflib.
 *
 * @param oldTextId {String} element id of old text
 * @param newTextId {String} element id of new text
 * @param outputId {String} element id of output element
 * @param viewType {Number} 0: split, 1: unified
 * @param ignoreSpace {Number} 0: include, 1: ignore
 */
function diffUsingJS(oldTextId, newTextId, outputId, viewType, ignoreSpace) {
  var old = $('#' + oldTextId), head = $('#' + newTextId);
  var render = new JsDiffRender({
    oldText    : old.data('val'),
    oldTextName: old.data('file-name'),
    newText    : head.data('val'),
    newTextName: head.data('file-name'),
    ignoreSpace: ignoreSpace,
    contextSize: 4
  });
  var diff = render[viewType == 1 ? "unified" : "split"]();
  if(viewType == 1){
    diff.find('tr:last').after($('<tr><td></td><td></td><td></td></tr>'));
  } else {
    diff.find('tr:last').after($('<tr><td></td><td></td><td></td><td></td></tr>'));
  }
  diff.appendTo($('#' + outputId).html(""));
}



function jqSelectorEscape(val) {
    return val.replace(/[!"#$%&'()*+,.\/:;<=>?@\[\\\]^`{|}~]/g, '\\$&');
}

function JsDiffRender(params){
  var baseTextLines = (params.oldText==="")?[]:params.oldText.split(/\r\n|\r|\n/);
  var headTextLines = (params.newText==="")?[]:params.newText.split(/\r\n|\r|\n/);
  var sm, ctx;
  if(params.ignoreSpace){
    var ignoreSpace = function(a){ return a.replace(/\s+/g,''); };
    sm = new difflib.SequenceMatcher(
      $.map(baseTextLines, ignoreSpace),
      $.map(headTextLines, ignoreSpace));
    ctx = this.flatten(sm.get_opcodes(), headTextLines, baseTextLines, function(text){ return ignoreSpace(text) === ""; });
  }else{
    sm = new difflib.SequenceMatcher(baseTextLines, headTextLines);
    ctx = this.flatten(sm.get_opcodes(), headTextLines, baseTextLines, function(){ return false; });
  }
  var oplines = this.fold(ctx, params.contextSize);

  function prettyDom(text, fileName){
    var dom = null;
    return function(ln){
      if(dom===null){
        var html = prettyPrintOne(
          text.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/"/g,'&quot;').replace(/>/g,'&gt;').replace(/^\n/, '\n\n'),
          (/\.([^.]*)$/.exec(fileName)||[])[1],
          true);
        var re = /<li[^>]*id="?L([0-9]+)"?[^>]*>(.*?)<\/li>/gi, h;
        dom=[];
        while(h=re.exec(html)){
          dom[h[1]]=h[2];
        }
      }
      return dom[ln];
    };
  }
  return this.renders(oplines, prettyDom(params.oldText, params.oldTextName), prettyDom(params.newText, params.newTextName));
}
$.extend(JsDiffRender.prototype,{
  renders: function(oplines, baseTextDom, headTextDom){
    return {
      split:function(){
        var table = $('<table class="diff">');
        table.attr({add:oplines.add, del:oplines.del});
        var tbody = $('<tbody>').appendTo(table);
        for(var i=0;i<oplines.length;i++){
          var o = oplines[i];
          switch(o.change){
          case 'skip':
            $('<tr>').html('<th class="skip"></th><td colspan="3" class="skip">...</td>').appendTo(tbody);
            break;
          case 'delete':
          case 'insert':
          case 'equal':
            $('<tr>').append(
              lineNum('old',o.base, o.change),
              $('<td class="body">').html(o.base ? baseTextDom(o.base): "").addClass(o.change),
              lineNum('new',o.head, o.change),
              $('<td class="body">').html(o.head ? headTextDom(o.head): "").addClass(o.change)
              ).appendTo(tbody);
            break;
          case 'replace':
            var ld = lineDiff(baseTextDom(o.base), headTextDom(o.head));
            $('<tr>').append(
              lineNum('old',o.base, 'delete'),
              $('<td class="body">').append(ld.base).addClass('delete'),
              lineNum('new',o.head, 'insert'),
              $('<td class="body">').append(ld.head).addClass('insert')
              ).appendTo(tbody);
            break;
          }
        }
        return table;
      },
      unified:function(){
        var table = $('<table class="diff inlinediff">');
        table.attr({add:oplines.add, del:oplines.del});
        var tbody = $('<tbody>').appendTo(table);
        for(var i=0;i<oplines.length;i++){
          var o = oplines[i];
          switch(o.change){
          case 'skip':
            tbody.append($('<tr>').html('<th colspan="2" class="skip"></th><td class="skip"></td>'));
            break;
          case 'delete':
          case 'insert':
          case 'equal':
            tbody.append($('<tr>').append(
              lineNum('old',o.base, o.change),
              lineNum('new',o.head, o.change),
              $('<td class="body">').addClass(o.change).html(o.head ? headTextDom(o.head) : baseTextDom(o.base))));
            break;
          case 'replace':
            var deletes = [];
            while(oplines[i] && oplines[i].change == 'replace'){
              if(oplines[i].base && oplines[i].head){
                var ld = lineDiff(baseTextDom(oplines[i].base), headTextDom(oplines[i].head));
                tbody.append($('<tr>').append(lineNum('old', oplines[i].base, 'delete'),'<th class="delete">',$('<td class="body delete">').append(ld.base)));
                deletes.push($('<tr>').append('<th class="insert">',lineNum('new',oplines[i].head, 'insert'),$('<td class="body insert">').append(ld.head)));
              }else if(oplines[i].base){
                tbody.append($('<tr>').append(lineNum('old', oplines[i].base, 'delete'),'<th class="delete">',$('<td class="body delete">').html(baseTextDom(oplines[i].base))));
              }else if(oplines[i].head){
                deletes.push($('<tr>').append('<th class="insert">',lineNum('new',oplines[i].head, 'insert'),$('<td class="body insert">').html(headTextDom(oplines[i].head))));
              }
              i++;
            }
            tbody.append(deletes);
            i--;
            break;
          }
        }
        return table;
      }
    };
    function lineNum(type, num, klass){
      var cell = $('<th class="line-num">').addClass(type+'line').addClass(klass);
      if(num){
        cell.attr('line-number',num);
      }
      return cell;
    }
    function lineDiff(b,n){
      var bc = $('<diff>').html(b).children();
      var nc = $('<diff>').html(n).children();
      var textE = function(){ return $(this).text(); };
      var sm = new difflib.SequenceMatcher(bc.map(textE), nc.map(textE));
      var op = sm.get_opcodes();
      if(op.length==1 || sm.ratio()<0.5){
        return {base:bc,head:nc};
      }
      var ret = { base : [], head: []};
      for(var i=0;i<op.length;i++){
        var o = op[i];
        switch(o[0]){
        case 'equal':
          ret.base=ret.base.concat(bc.slice(o[1],o[2]));
          ret.head=ret.head.concat(nc.slice(o[3],o[4]));
          break;
        case 'delete':
        case 'insert':
        case 'replace':
          if(o[2]!=o[1]){
            ret.base.push($('<del>').append(bc.slice(o[1],o[2])));
          }
          if(o[4]!=o[3]){
            ret.head.push($('<ins>').append(nc.slice(o[3],o[4])));
          }
          break;
        }
      }
      return ret;
    }
  },
  flatten: function(opcodes, headTextLines, baseTextLines, isIgnoreLine){
    var ret = [], add=0, del=0;
    for (var idx = 0; idx < opcodes.length; idx++) {
      var code = opcodes[idx];
      var change = code[0];
      var b = code[1];
      var n = code[3];
      var rowcnt = Math.max(code[2] - b, code[4] - n);
      for (var i = 0; i < rowcnt; i++) {
        switch(change){
        case 'insert':
          add++;
          ret.push({
            change:(isIgnoreLine(headTextLines[n]) ? 'equal' : change),
            head: ++n
          });
          break;
        case 'delete':
          del++;
          ret.push({
            change: (isIgnoreLine(baseTextLines[b]) ? 'equal' : change),
            base: ++b
          });
          break;
        case 'replace':
          add++;
          del++;
          var r = {change: change};
          if(n<code[4]){
            r.head = ++n;
          }
          if(b<code[2]){
            r.base = ++b;
          }
          ret.push(r);
          break;
        default:
          ret.push({
            change:change,
            head: ++n,
            base: ++b
          });
        }
      }
    }
    ret.add=add;
    ret.del=del;
    return ret;
  },
  fold: function(oplines, contextSize){
    var ret = [], skips=[], bskip = contextSize;
    for(var i=0;i<oplines.length;i++){
      var o = oplines[i];
      if(o.change=='equal'){
        if(bskip < contextSize){
          bskip ++;
          ret.push(o);
        }else{
          skips.push(o);
        }
      }else{
        if(skips.length > contextSize){
          ret.push({
            change:'skip',
            start:skips[0],
            end:skips[skips.length-contextSize]
          });
        }
        ret = ret.concat(skips.splice(- contextSize));
        ret.push(o);
        skips = [];
        bskip = 0;
      }
    }
    if(skips.length > contextSize){
      ret.push({
        change:'skip',
        start:skips[0],
        end:skips[skips.length-contextSize]
      });
    }
    ret.add = oplines.add;
    ret.del = oplines.del;
    return ret;
  }
});

/**
 * scroll target into view ( on bottom edge, or on top edge)
 */
function scrollIntoView(target){
  target = $(target);
  var $window = $(window);
  var docViewTop = $window.scrollTop();
  var docViewBottom = docViewTop + $window.height();

  var elemTop = target.offset().top;
  var elemBottom = elemTop + target.height();

  if(elemBottom > docViewBottom){
    $('html, body').scrollTop(elemBottom - $window.height());
  }else if(elemTop < docViewTop){
    $('html, body').scrollTop(elemTop);
  }
}

/**
* escape html
*/
function escapeHtml(text){
 return text.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/"/g,'&quot;').replace(/>/g,'&gt;');
}

/**
 * calculate string ranking for path.
 * Original ported from:
 * http://joshaven.com/string_score
 * https://github.com/joshaven/string_score
 *
 * Copyright (C) 2009-2011 Joshaven Potter <yourtech@@gmail.com>
 * Special thanks to all of the contributors listed here https://github.com/joshaven/string_score
 * MIT license: http://www.opensource.org/licenses/mit-license.php
 */
function string_score(string, word) {
  'use strict';
  var zero = {score:0,matchingPositions:[]};

  // If the string is equal to the word, perfect match.
  if (string === word || word === "") { return {score:1, matchingPositions:[]}; }

  var lString = string.toUpperCase(),
      strLength = string.length,
      lWord = word.toUpperCase(),
      wordLength = word.length;

  return   calc(zero,        0,    0,            0, 0,                []);
  function calc(score, startAt, skip, runningScore, i, matchingPositions){
    if( i < wordLength) {
      var charScore = 0;

      // Find next first case-insensitive match of a character.
      var idxOf = lString.indexOf(lWord[i], skip);

      if (-1 === idxOf) { return score; }
      score = calc(score, startAt, idxOf+1, runningScore, i, matchingPositions);
      if (startAt === idxOf) {
        // Consecutive letter & start-of-string Bonus
        charScore = 0.8;
      } else {
        charScore = 0.1;

        // Acronym Bonus
        // Weighing Logic: Typing the first character of an acronym is as if you
        // preceded it with two perfect character matches.
        if (/^[^A-Za-z0-9]/.test(string[idxOf - 1])){
          charScore += 0.7;
        }else if(string[idxOf]==lWord[i]) {
          // Upper case bonus
          charScore += 0.2;
          // Camel case bonus
          if(/^[a-z]/.test(string[idxOf - 1])){
            charScore += 0.5;
          }
        }
      }

      // Same case bonus.
      if (string[idxOf] === word[i]) { charScore += 0.1; }

      // next round
      return calc(score, idxOf + 1, idxOf + 1, runningScore + charScore, i+1, matchingPositions.concat(idxOf));
    }else{
      // skip non match folder
      var effectiveLength = strLength;
      if(matchingPositions.length){
        var lastSlash = string.lastIndexOf('/',matchingPositions[0]);
        if(lastSlash!==-1){
          effectiveLength = strLength-lastSlash;
        }
      }
      // Reduce penalty for longer strings.
      var finalScore = 0.5 * (runningScore / effectiveLength + runningScore / wordLength);

      if ((lWord[0] === lString[0]) && (finalScore < 0.85)) {
        finalScore += 0.15;
      }
      if(score.score >= finalScore){
        return score;
      }
      return {score:finalScore, matchingPositions:matchingPositions};
    }
  }
}
/**
 * sort by string_score.
 * @param word    {String}        search word
 * @param strings {Array[String]} search targets
 * @param limit   {Integer}       result limit
 * @return {Array[{score:"float matching score", string:"string target string", matchingPositions:"Array[Integer] matching positions"}]}
 */
function string_score_sort(word, strings, limit){
  var ret = [], i=0, l = (word==="")?Math.min(strings.length, limit):strings.length;
  for(; i < l; i++){
    var score = string_score(strings[i],word);
    if(score.score){
      score.string = strings[i];
      ret.push(score);
    }
  }
  ret.sort(function(a,b){
    var s = b.score - a.score;
    if(s === 0){
      return a.string > b.string ? 1 : -1;
    }
    return s;
  });
  ret = ret.slice(0,limit);
  return ret;
}
/**
 * highlight by result.
 * @param score {string:"string target string", matchingPositions:"Array[Integer] matching positions"}
 * @param highlight tag ex: '<b>'
 * @return array of highlighted html elements.
 */
function string_score_highlight(result, tag){
  var str = result.string, msp=0;
  return hilight([], 0,  result.matchingPositions[msp]);
  function hilight(html, c, mpos){
    if(mpos === undefined){
      return html.concat(document.createTextNode(str.substr(c)));
    }else{
      return hilight(html.concat([
                     document.createTextNode(str.substring(c,mpos)),
                     $(tag).text(str[mpos])]),
                     mpos+1, result.matchingPositions[++msp]);
    }
  }
}

/****************************************************************************/
/* Diff */
/****************************************************************************/
// add naturalWidth and naturalHeight for ie 8
function setNatural(img) {
  if(typeof img.naturalWidth == 'undefined'){
    var tmp = new Image();
    tmp.src = img.src;
    img.naturalWidth = tmp.width;
    img.naturalHeight = tmp.height;
  }
}
/**
 * onload handler
 * @param img <img>
 */
function onLoadedDiffImages(img){
  setNatural(img);
  img = $(img);
  img.show();
  var tb = img.parents(".diff-image-render");
  // Find images. If the image has not loaded yet, value is undefined.
  var old = tb.find(".diff-old img.diff-image:visible")[0];
  var neo = tb.find(".diff-new img.diff-image:visible")[0];
  imageDiff.appendImageMeta(tb, old, neo);
  if(old && neo){
    imageDiff.createToolSelector(old, neo).appendTo(tb.parent());
  }
}
var imageDiff ={
  /** append image meta div after image nodes.
   * @param tb <div class="diff-image-2up">
   * @param old <img>||undefined
   * @param neo <img>||undefined
   */
  appendImageMeta:function(tb, old, neo){
    old = old || {};
    neo = neo || {};
    tb.find(".diff-meta").remove();
    // before loaded, image is not visible.
    tb.find("img.diff-image:visible").each(function(){
      var div = $('<p class="diff-meta"><b>W:</b><span class="w"></span> | <b>W:</b><span class="h"></span></p>');
      div.find('.w').text(this.naturalWidth+"px").toggleClass("diff", old.naturalWidth != neo.naturalWidth);
      div.find('.h').text(this.naturalHeight+"px").toggleClass("diff", old.naturalHeight != neo.naturalHeight);
      div.appendTo(this.parentNode);
    });
  },
  /** check this browser can use canvas tag.
   */
  hasCanvasSupport:function(){
    if(!this.hasCanvasSupport.hasOwnProperty('resultCache')){
      this.hasCanvasSupport.resultCache = (typeof $('<canvas>')[0].getContext)=='function';
    }
    return this.hasCanvasSupport.resultCache;
  },
  /** create toolbar
   * @param old <img>
   * @param neo <img>
   * @return jQuery(<ul class="image-diff-tools">)
   */
  createToolSelector:function(old, neo){
    var self = this;
    return $('<ul class="image-diff-tools">'+
      '<li data-mode="diff2up" class="active">2-up</li>'+
      '<li data-mode="swipe">Swipe</li>'+
      '<li data-mode="onion">Onion Skin</li>'+
      '<li data-mode="difference" class="need-canvas">Difference</li>'+
      '<li data-mode="blink">Blink</li>'+
      '</ul>')
      .toggleClass('no-canvas', !this.hasCanvasSupport())
      .on('click', 'li', function(e){
        var td = $(this).parents("td");
        $(e.delegateTarget).find('li').each(function(){ $(this).toggleClass('active',this == e.target); });
        var mode = $(e.target).data('mode');
        td.find(".diff-image-render").hide();
        // create div if not created yet
        if(td.find(".diff-image-render."+mode).show().length===0){
          self[mode](old, neo).insertBefore(e.delegateTarget).addClass("diff-image-render");
        }
        return false;
      });
  },
  /** (private) calc size from images and css (const)
   * @param old <img>
   * @param neo <img>
   */
  calcSizes:function(old, neo){
    var maxWidth = 869 - 20 - 20 - 4; // set by css
    var h = Math.min(Math.max(old.naturalHeight, neo.naturalHeight),maxWidth);
    var w = Math.min(Math.max(old.naturalWidth, neo.naturalWidth),maxWidth);
    var oldRate = Math.min(h/old.naturalHeight, w/old.naturalWidth);
    var neoRate = Math.min(h/neo.naturalHeight, w/neo.naturalWidth);
    var neoW = neo.naturalWidth*neoRate;
    var neoH = neo.naturalHeight*neoRate;
    var oldW = old.naturalWidth*oldRate;
    var oldH = old.naturalHeight*oldRate;
    var paddingLeft = (maxWidth/2)-Math.max(neoW,oldW)/2;
    return {
      height:Math.max(oldH, neoH),
      width:w,
      padding:paddingLeft,
      paddingTop:20, // set by css
      barWidth:200, // set by css
      old:{ rate:oldRate, width:oldW, height:oldH },
      neo:{ rate:neoRate, width:neoW, height:neoH }
    };
  },
  /** (private) create div
   * @param old <img>
   * @param neo <img>
   */
  stack:function(old, neo){
    var size = this.calcSizes(old, neo);
    var diffNew = $('<div class="diff-new">')
      .append($("<img>").attr('src',neo.src).css({width:size.neo.width, height:size.neo.height}));
    var diffOld = $('<div class="diff-old">')
      .append($("<img>").attr('src',old.src).css({width:size.old.width, height:size.old.height}));
    var handle =  $('<span class="diff-swipe-handle icon icon-resize-horizontal"></span>')
      .css({marginTop:size.height-5});
    var bar = $('<hr class="diff-silde-bar">').css({top:size.height+size.paddingTop});
    var div = $('<div class="diff-image-stack">')
      .css({height:size.height+size.paddingTop*2, paddingLeft:size.padding})
      .append(diffOld, diffNew, bar, handle);
    return {
      neo:diffNew,
      old:diffOld,
      size:size,
      handle:handle,
      bar:bar,
      div:div,
      /* add event listener 'on mousemove' */
      onMoveHandleOnBar:function(callback){
        div.on('mousemove',function(e){
          var x = Math.max(Math.min((e.pageX - bar.offset().left), size.barWidth), 0);
          handle.css({left:x});
          callback(x, e);
        });
      }
    };
  },
  /** create swipe box
   * @param old <img>
   * @param neo <img>
   * @return jQuery(<div class="diff-image-stack swipe">)
   */
  swipe:function(old, neo){
    var stack = this.stack(old, neo);
    function setX(x){
      stack.neo.css({width:x});
      stack.handle.css({left:x+stack.size.padding});
    }
    setX(stack.size.neo.width/2);
    stack.div.on('mousemove',function(e){
      setX(Math.max(Math.min(e.pageX - stack.neo.offset().left, stack.size.neo.width),0));
    });
    return stack.div.addClass('swipe');
  },
  /** create blink box
   * @param old <img>
   * @param neo <img>
   * @return jQuery(<div class="diff-image-stack blink">)
   */
  blink:function(old, neo){
    var stack = this.stack(old, neo);
    stack.onMoveHandleOnBar(function(x){
      stack.neo.toggle(Math.floor(x) % 2 === 0);
    });
    return stack.div.addClass('blink');
  },
  /** create onion skin box
   * @param old <img>
   * @param neo <img>
   * @return jQuery(<div class="diff-image-stack onion">)
   */
  onion:function(old, neo){
    var stack = this.stack(old, neo);
    stack.neo.css({opacity:0.5});
    stack.onMoveHandleOnBar(function(x){
      stack.neo.css({opacity:x/stack.size.barWidth});
    });
    return stack.div.addClass('onion');
  },
  /** create difference box
   * @param old <img>
   * @param neo <img>
   * @return jQuery(<div class="diff-image-stack difference">)
   */
  difference:function(old, neo){
    var size = this.calcSizes(old,neo);
    var canvas = $('<canvas>').attr({width:size.width, height:size.height})[0];
    var context = canvas.getContext('2d');

    context.clearRect(0, 0, size.width, size.height);
    context.drawImage(neo, 0, 0, size.neo.width, size.neo.height);
    var neoData = context.getImageData(0, 0, size.neo.width, size.neo.height).data;
    context.clearRect(0, 0, size.width, size.height);

    context.drawImage(old, 0, 0, size.old.width, size.old.height);
    var c = context.getImageData(0, 0, size.neo.width, size.neo.height);
    var cData = c.data;
    for (var i = cData.length -1; i>0; i --){
      cData[i] = (i%4===3) ? Math.max(cData[i], neoData[i]) : Math.abs(cData[i] - neoData[i]);
    }
    context.putImageData(c, 0, 0);

    return $('<div class="diff-image-stack difference">').append(canvas);
  }
};

/**
 * function for account extra mail address form control.
 */
function addExtraMailAddress() {
  var fieldset = $('#extraMailAddresses');
  var count = $('.extraMailAddress').length;
  var html =   '<input type="text" name="extraMailAddresses[' + count + ']" id="extraMailAddresses[' + count + ']" class="form-control extraMailAddress"/>'
  + '<span id="error-extraMailAddresses_' + count + '" class="error"></span>';
  fieldset.append(html);
}

/**
 * function for check account extra mail address form control.
 */
function checkExtraMailAddress(){
  if ($(this).val() != ""){
    var needAdd = true;
    $('.extraMailAddress').each(function(){
      if($(this).val() == ""){
        needAdd = false;
        return false;
      }
      return true;
    });
    if (needAdd){
      addExtraMailAddress();
    }
  }
  else {
    $(this).remove();
  }
}

/**
 * function for extracting markdown from comment area.
 * @param commentArea a comment area
 * @returns {*|jQuery}
 */
var extractMarkdown = function(commentArea){
  $('body').append('<div id="tmp"></div>');
  $('#tmp').html(commentArea);
  var markdown = $('#tmp textarea').val();
  $('#tmp').remove();
  return markdown;
};

/**
 * function for applying checkboxes status of task list.
 * @param commentArea a comment area
 * @param checkboxes checkboxes for task list
 * @returns {string} a markdown that applied checkbox status
 */
var applyTaskListCheckedStatus = function(commentArea, checkboxes) {
  var ss = [],
    markdown = extractMarkdown(commentArea),
    xs = markdown.split(/- \[[x| ]\]/g);
  for (var i=0; i<xs.length; i++) {
    ss.push(xs[i]);
    if (checkboxes.eq(i).prop('checked')) ss.push('- [x]');
    else ss.push('- [ ]');
  }
  ss.pop();
  return ss.join('');
};