{"action":"created","issue":{"url":"https://api.github.com/repos/tylingsoft/markdown-plus/issues/4","labels_url":"https://api.github.com/repos/tylingsoft/markdown-plus/issues/4/labels{/name}","comments_url":"https://api.github.com/repos/tylingsoft/markdown-plus/issues/4/comments","events_url":"https://api.github.com/repos/tylingsoft/markdown-plus/issues/4/events","html_url":"https://github.com/tylingsoft/markdown-plus/issues/4","id":62415022,"number":4,"title":"enhacement: replacing underscore with plain JS","user":{"login":"srikanthegdee","id":7389244,"avatar_url":"https://avatars.githubusercontent.com/u/7389244?v=3","gravatar_id":"","url":"https://api.github.com/users/srikanthegdee","html_url":"https://github.com/srikanthegdee","followers_url":"https://api.github.com/users/srikanthegdee/followers","following_url":"https://api.github.com/users/srikanthegdee/following{/other_user}","gists_url":"https://api.github.com/users/srikanthegdee/gists{/gist_id}","starred_url":"https://api.github.com/users/srikanthegdee/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/srikanthegdee/subscriptions","organizations_url":"https://api.github.com/users/srikanthegdee/orgs","repos_url":"https://api.github.com/users/srikanthegdee/repos","events_url":"https://api.github.com/users/srikanthegdee/events{/privacy}","received_events_url":"https://api.github.com/users/srikanthegdee/received_events","type":"User","site_admin":false},"labels":[],"state":"closed","locked":false,"assignee":null,"milestone":null,"comments":4,"created_at":"2015-03-17T14:35:29Z","updated_at":"2015-03-18T12:54:35Z","closed_at":"2015-03-18T11:23:21Z","body":"_.debounce function is replaced with plain JS. (sorry I dont use git)\r\n\r\n\r\n```\r\nvar editor;\r\n\r\n//http://davidwalsh.name/javascript-debounce-function\r\nfunction debounce(func, wait, immediate) {\r\n\tvar timeout;\r\n\treturn function() {\r\n\t\tvar context = this, args = arguments;\r\n\t\tvar later = function() {\r\n\t\t\ttimeout = null;\r\n\t\t\tif (!immediate) func.apply(context, args);\r\n\t\t};\r\n\t\tvar callNow = immediate && !timeout;\r\n\t\tclearTimeout(timeout);\r\n\t\ttimeout = setTimeout(later, wait);\r\n\t\tif (callNow) func.apply(context, args);\r\n\t};\r\n};\r\n\r\n$(document).ready(function() {\r\n\r\n  $.getJSON('bower.json', function(json) {\r\n    $('#version-string').html(json.version); // 从 bower.json 读取版本号\r\n  });\r\n\r\n  // 构造上中右三个面板\r\n  $('body').layout({\r\n    resizerDblClickToggle: false,\r\n    resizable: false,\r\n    slidable: false,\r\n    togglerLength_open: '100%',\r\n    togglerLength_closed: '100%',\r\n    north: {\r\n      size: '1px', // 只是占位，真实大小由内容决定\r\n      togglerTip_open: 'Hide Toolbar',\r\n      togglerTip_closed: 'Show Toolbar'\r\n    },\r\n    east: {\r\n      size: '50%',\r\n      togglerTip_open: 'Hide Preview',\r\n      togglerTip_closed: 'Show Preview',\r\n      onresize: function() {\r\n        editor.session.setUseWrapMode(false); // ACE的wrap貌似有问题，这里手动触发一下。\r\n        editor.session.setUseWrapMode(true);\r\n      }\r\n    }\r\n  });\r\n\r\n  // 左侧编辑器\r\n  editor = ace.edit(\"editor\");\r\n  editor.$blockScrolling = Infinity;\r\n  editor.renderer.setShowPrintMargin(false);\r\n  editor.session.setMode(\"ace/mode/markdown\");\r\n  editor.session.setUseWrapMode(true);\r\n  editor.setFontSize('14px');\r\n  editor.focus();\r\n\r\n  // 编辑器的一些拓展方法\r\n  editor.selection.smartRange = function() {\r\n    var range = editor.selection.getRange();\r\n    if(!range.isEmpty()) {\r\n      return range; // 用户手动选中了一些文字，直接用这个\r\n    }\r\n    // 没有选中任何东西\r\n    var _range = range; // 备份原始range\r\n    range = editor.selection.getWordRange(range.start.row, range.start.column); // 当前单词的range\r\n    if(editor.session.getTextRange(range).trim().length == 0) { // 选中的东西是空或者全空白\r\n      range = _range; // 还使用原始的range\r\n    }\r\n    return range;\r\n  };\r\n\r\n  // 设置marked\r\n  var renderer = new marked.Renderer();\r\n  renderer.listitem = function(text) {\r\n    if(!/^\\[[ x]\\]\\s/.test(text)) {\r\n      return marked.Renderer.prototype.listitem(text);\r\n    }\r\n    // 任务列表\r\n    var checkbox = $('<input type=\"checkbox\" class=\"taskbox\" disabled=\"disabled\"/>');\r\n    if(/^\\[x\\]\\s/.test(text)) { // 完成的任务列表\r\n      checkbox.attr('checked', true);\r\n    }\r\n    return $(marked.Renderer.prototype.listitem(text.substring(4))).addClass('nostyle').prepend(checkbox)[0].outerHTML;\r\n  }\r\n  var mermaidError;\r\n  mermaid.parseError = function(err, hash){\r\n    mermaidError = err;\r\n  };\r\n  renderer.code = function(code, language) {\r\n    code = code.trim();\r\n    var firstLine = code.split(/\\n/)[0].trim();\r\n    if(language === 'math') { // 数学公式\r\n      var tex = '';\r\n      code.split(/\\n\\n/).forEach(function(line){ // 连续两个换行，则开始下一个公式\r\n        line = line.trim();\r\n        if(line.length > 0) {\r\n          try {\r\n            tex += katex.renderToString(line, { displayMode: true });\r\n          } catch(err) {\r\n            tex += '<pre>' + err + '</pre>';\r\n          }\r\n        }\r\n      });\r\n      return tex;\r\n    } else if(firstLine === 'sequenceDiagram' || firstLine.match(/^graph (?:TB|BT|RL|LR|TD);?$/)) {\r\n      if(firstLine === 'sequenceDiagram') {\r\n        code += '\\n'; // 如果末尾没有空行，则语法错误\r\n      }\r\n      if(mermaid.parse(code)) {\r\n        return '<div class=\"mermaid\">' + code + '</div>';\r\n      } else {\r\n        return '<pre>' + mermaidError + '</pre>';\r\n      }\r\n    } else {\r\n      return marked.Renderer.prototype.code.apply(this, arguments);\r\n    }\r\n  }\r\n  marked.setOptions({\r\n    renderer: renderer,\r\n    gfm: true,\r\n    tables: true,\r\n    breaks: false,\r\n    pedantic: false,\r\n    sanitize: false,\r\n    smartLists: true,\r\n    smartypants: true\r\n  });\r\n\r\n  // 实时监听用户的编辑\r\n  editor.session.on('change', function() {\r\n    lazy_change();\r\n  });\r\n\r\n  var lazy_change = debounce(function() { // 用户停止输入256毫秒之后才会触发\r\n    $('.markdown-body').empty().append(marked(editor.session.getValue())); // 实时预览\r\n\tconsole.log(editor.session.getValue())\r\n    $('pre').addClass('prettyprint').addClass('linenums');\r\n    prettyPrint(); // 语法高亮\r\n    $('img[src^=\"emoji/\"]').each(function() { // 转换emoji路径\r\n      $(this).attr('src', 'bower_components/emoji-icons/' + $(this).attr('src').substring(6) + '.png');\r\n    });\r\n    mermaid.init(); // 生成流程图，顺序图等\r\n  }, 256, false);\r\n\r\n  // h1 - h6 heading\r\n  $('.heading-icon').click(function() {\r\n    var level = $(this).data('level');\r\n    var p = editor.getCursorPosition();\r\n    p.column += level + 1; // 光标位置会产生偏移\r\n    editor.navigateTo(editor.getSelectionRange().start.row, 0); // navigateLineStart 在 wrap 的时候有问题\r\n    editor.insert('#'.repeat(level) + ' ');\r\n    editor.moveCursorToPosition(p); // 恢复光标位置\r\n    editor.focus();\r\n  });\r\n\r\n  // styling icons\r\n  $('.styling-icon').click(function() {\r\n    var modifier = $(this).data('modifier');\r\n    var range = editor.selection.smartRange();\r\n    var p = editor.getCursorPosition();\r\n    p.column += modifier.length; // 光标位置会产生偏移\r\n    editor.session.replace(range, modifier + editor.session.getTextRange(range) + modifier);\r\n    editor.moveCursorToPosition(p); // 恢复光标位置\r\n    editor.selection.clearSelection(); // 不知为何上一个语句会选中一部分文字\r\n    editor.focus();\r\n  });\r\n\r\n  // <hr/>\r\n  $('#horizontal-rule').click(function() {\r\n    var p = editor.getCursorPosition();\r\n    if(p.column == 0) { // 光标在行首\r\n      editor.selection.clearSelection();\r\n      editor.insert('\\n---\\n');\r\n    } else {\r\n      editor.navigateTo(editor.getSelectionRange().start.row, Number.MAX_VALUE); // navigateLineEnd 在 wrap 的时候有问题\r\n      editor.insert('\\n\\n---\\n');\r\n    }\r\n    editor.focus();\r\n  });\r\n\r\n  // list icons\r\n  $('.list-icon').click(function() {\r\n    var prefix = $(this).data('prefix');\r\n    var p = editor.getCursorPosition();\r\n    p.column += prefix.length; // 光标位置会产生偏移\r\n    var range = editor.selection.getRange();\r\n    for(var i = range.start.row + 1; i < range.end.row + 2; i++) {\r\n      editor.gotoLine(i);\r\n      editor.insert(prefix);\r\n    }\r\n    editor.moveCursorToPosition(p); // 恢复光标位置\r\n    editor.focus();\r\n  });\r\n\r\n  $('#link-icon').click(function() {\r\n    var range = editor.selection.smartRange();\r\n    var text = editor.session.getTextRange(range);\r\n    if(text.trim().length == 0) {\r\n      text = 'link description';\r\n    }\r\n    editor.session.replace(range, '[' + text +'](http://example.com/ \"optional title\")');\r\n    editor.focus();\r\n  });\r\n\r\n  $('#image-icon').click(function() {\r\n    var text = editor.session.getTextRange(editor.selection.getRange()).trim();\r\n    if(text.length == 0) {\r\n      text = 'image description';\r\n    }\r\n    editor.insert('![' + text + '](http://example.com/example.png)');\r\n    editor.focus();\r\n  });\r\n\r\n  $('#code-icon').click(function() {\r\n    var text = editor.session.getTextRange(editor.selection.getRange()).trim();\r\n    if(text.length == 0) {\r\n      text = 'enter code here';\r\n    }\r\n    editor.insert('\\n```\\n' + text + '\\n```\\n');\r\n    editor.focus();\r\n  });\r\n\r\n  $('#table-icon').click(function() {\r\n    var tableTemplate = 'header 1 | header 2\\n---|---\\nrow 1 col 1 | row 1 col 2\\nrow 2 col 1 | row 2 col 2';\r\n    editor.insert(''); // 删除选中的部分\r\n    var p = editor.getCursorPosition();\r\n    if(p.column == 0) { // 光标在行首\r\n      editor.selection.clearSelection();\r\n      editor.insert('\\n' + tableTemplate + '\\n\\n');\r\n    } else {\r\n      editor.navigateTo(editor.getSelectionRange().start.row, Number.MAX_VALUE);\r\n      editor.insert('\\n\\n' + tableTemplate + '\\n');\r\n    }\r\n    editor.focus();\r\n  });\r\n\r\n  // emoji icon\r\n  $(document).on('opened', '#emoji-modal', function() {\r\n    $('#emoji-code').focus();\r\n  });\r\n  $('#emoji-code').keyup(function(e) {\r\n   if(e.which == 13) { // 回车键确认\r\n      $('#emoji-confirm').click();\r\n    }\r\n  });\r\n  $(document).on('confirm', '#emoji-modal', function() {\r\n    var emoji_code = $('#emoji-code').val().trim();\r\n    if(emoji_code.length > 0) {\r\n      editor.insert('<img src=\"emoji/' + emoji_code + '\" width=\"18\"/>');\r\n      $('#emoji-code').val('');\r\n    }\r\n  });\r\n\r\n  // Font Awesome icon\r\n  $(document).on('opened', '#fa-modal', function() {\r\n    $('#fa-code').focus();\r\n  });\r\n  $('#fa-code').keyup(function(e) {\r\n   if(e.which == 13) { // 回车键确认\r\n      $('#fa-confirm').click();\r\n    }\r\n  });\r\n  $(document).on('confirm', '#fa-modal', function() {\r\n    var fa_code = $('#fa-code').val().trim();\r\n    if(fa_code.length > 0) {\r\n      editor.insert('<i class=\"fa fa-' + fa_code + '\"/>');\r\n      $('#fa-code').val('');\r\n    }\r\n  });\r\n\r\n  // Ionicons icon\r\n  $(document).on('opened', '#ion-modal', function() {\r\n    $('#ion-code').focus();\r\n  });\r\n  $('#ion-code').keyup(function(e) {\r\n   if(e.which == 13) { // 回车键确认\r\n      $('#ion-confirm').click();\r\n    }\r\n  });\r\n  $(document).on('confirm', '#ion-modal', function() {\r\n    var ion_code = $('#ion-code').val().trim();\r\n    if(ion_code.length > 0) {\r\n      editor.insert('<i class=\"icon ion-' + ion_code + '\"/>');\r\n      $('#ion-code').val('');\r\n    }\r\n  });\r\n\r\n  $('#math-icon').click(function(){\r\n    var text = editor.session.getTextRange(editor.selection.getRange()).trim();\r\n    if(text.length == 0) {\r\n      text = 'E = mc^2';\r\n    }\r\n    editor.insert('\\n```math\\n' + text + '\\n```\\n');\r\n    editor.focus();\r\n  });\r\n\r\n  $('#flow-icon').click(function(){\r\n    var text = editor.session.getTextRange(editor.selection.getRange()).trim();\r\n    if(text.length == 0) {\r\n      text = 'graph LR\\nA-->B';\r\n    }\r\n    editor.insert('\\n```\\n' + text + '\\n```\\n');\r\n    editor.focus();\r\n  });\r\n\r\n  $('#seq-icon').click(function(){\r\n    var text = editor.session.getTextRange(editor.selection.getRange()).trim();\r\n    if(text.length == 0) {\r\n      text = 'sequenceDiagram\\nA->>B: How are you?\\nB->>A: Great!';\r\n    }\r\n    editor.insert('\\n```\\n' + text + '\\n```\\n');\r\n    editor.focus();\r\n  });\r\n\r\n  // modals\r\n  $(document).on('close', '.remodal', function(e) {\r\n    editor.focus(); // 关闭modal，编辑器自动获得焦点\r\n  });\r\n\r\n});\r\n```\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/tylingsoft/markdown-plus/issues/comments/82956314","html_url":"https://github.com/tylingsoft/markdown-plus/issues/4#issuecomment-82956314","issue_url":"https://api.github.com/repos/tylingsoft/markdown-plus/issues/4","id":82956314,"user":{"login":"srikanthegdee","id":7389244,"avatar_url":"https://avatars.githubusercontent.com/u/7389244?v=3","gravatar_id":"","url":"https://api.github.com/users/srikanthegdee","html_url":"https://github.com/srikanthegdee","followers_url":"https://api.github.com/users/srikanthegdee/followers","following_url":"https://api.github.com/users/srikanthegdee/following{/other_user}","gists_url":"https://api.github.com/users/srikanthegdee/gists{/gist_id}","starred_url":"https://api.github.com/users/srikanthegdee/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/srikanthegdee/subscriptions","organizations_url":"https://api.github.com/users/srikanthegdee/orgs","repos_url":"https://api.github.com/users/srikanthegdee/repos","events_url":"https://api.github.com/users/srikanthegdee/events{/privacy}","received_events_url":"https://api.github.com/users/srikanthegdee/received_events","type":"User","site_admin":false},"created_at":"2015-03-18T12:54:35Z","updated_at":"2015-03-18T12:54:35Z","body":"No problem. "}}