WorkFlowy CommonMark live preview

Render a live CommonMark-interpreted preview of WorkFlowy notes: On nodes with a description, preview appears beneath while edit window is limited to a specific height with a scrollbar. Doesn't affect nodes without a description.

  1. // ==UserScript==
  2. // @name WorkFlowy CommonMark live preview
  3. // @namespace http://codeoptimism.com/
  4. // @version 1.0
  5. // @description Render a live CommonMark-interpreted preview of WorkFlowy notes: On nodes with a description, preview appears beneath while edit window is limited to a specific height with a scrollbar. Doesn't affect nodes without a description.
  6. // @author Christopher Galpin
  7. // @license MIT
  8. // @contributionURL https://www.paypal.com/donate/?token=J_EdsnWs02cJRSpntD-QR8w9JFbbUJaSHkgOk_c2tOhWuSPxtH_6OipUutJE07ZnqGZgFG
  9. // @match https://workflowy.com/
  10. // @grant GM_addStyle
  11. // @require https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js
  12. // @require https://gitcdn.link/cdn/andreyfedoseev/jquery-textareaPreview/342c1059940021f0f7bf84b3898773e7e5e4ec3a/jquery.textareaPreview.min.js
  13. // @require https://cdnjs.cloudflare.com/ajax/libs/commonmark/0.27.0/commonmark.min.js
  14. // ==/UserScript==
  15.  
  16. // Uses Andrey Fedoseev's textAreaPreview jQuery plugin: https://github.com/andreyfedoseev/jquery-textareaPreview
  17. // styles shamelessly stolen from http://stackoverflow.com
  18. var styles = `
  19. #cm a:hover,#cm a:active{color:#3af;text-decoration:none}
  20. #cm a{color:#07C;text-decoration:none;cursor:pointer}
  21. #cm blockquote *:last-child{margin-bottom:0}
  22. #cm blockquote,#cm q{quotes:none}
  23. #cm blockquote:before,#cm q:before,#cm blockquote:after,#cm q:after{content:"";content:none}
  24. #cm blockquote{margin-bottom:10px;padding:10px;background-color:#FFF8DC;border-left:2px solid #ffeb8e}
  25. #cm code{font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif;background-color:#eff0f1}
  26. #cm code{font-size:13px}
  27. #cm code{white-space:pre-wrap;padding:1px 5px}
  28. #cm h1 code,#cm h2 code,#cm h3 code,#cm h4 code,#cm h5 code,#cm h6 code{margin-bottom:.5em}
  29. #cm h1,#cm h1 code{font-size:21px}
  30. #cm h1,#cm h2,#cm h3,#cm h4,#cm h5,#cm h6{font-weight:bold !important}
  31. #cm h1,#cm h2,#cm h3{line-height:1.3;margin-bottom:1em;}
  32. #cm h1{font-size:22px}
  33. #cm h2,#cm h2 code{font-size:19px}
  34. #cm h2{font-size:18px}
  35. #cm h3,#cm h3 code{font-size:17px}
  36. #cm h3{font-size:15px}
  37. #cm h4,#cm h4 code{font-size:15px}
  38. #cm hr{background-color:#d6d9dc;color:#d6d9dc}
  39. #cm img{max-width:100%;margin-bottom:.5em}
  40. #cm li blockquote{margin:.5em 0 1em 0}
  41. #cm li pre{margin:.5em 0 1em 0}
  42. #cm li pre{word-wrap:normal}
  43. #cm li>ul,#cm li>ol{padding-top:.5em}
  44. #cm li{word-wrap:break-word}
  45. #cm ol{list-style-type:decimal}
  46. #cm p code{padding:1px 5px}
  47. #cm p img,#cm li img,#cm blockquote img{margin-bottom:0}
  48. #cm p.lead small{display:block;font-size:13px;color:#5e666e}
  49. #cm p.lead{font-size:1.3em;line-height:1.5em}
  50. #cm pre code{white-space:inherit;padding:0}
  51. #cm pre>code:first-child{max-height:600px;display:block}
  52. #cm pre{-ms-word-wrap:normal;word-wrap:normal}
  53. #cm pre{margin-bottom:1em;padding:5px;padding-bottom:20px !ie7;width:auto;width:650px !ie7;max-height:600px;overflow:auto;font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif;font-size:13px;background-color:#eff0f1}
  54. #cm pre{max-height:none}
  55. #cm pre{word-wrap:normal}
  56. #cm p{clear:both;margin-bottom:1em;margin-top:0}
  57. #cm p{font-size:100%}
  58. #cm ul li,#cm ol li{margin-bottom:.5em}
  59. #cm ul li:last-child,#cm ol li:last-child{margin-bottom:0}
  60. #cm ul p:last-of-type,#cm ol p:last-of-type{margin-bottom:0}
  61. #cm ul ul,#cm ol ul,#cm ul ol,#cm ol ol{margin-bottom:0}
  62. #cm ul,#cm ol,#cm li{margin:0;padding:0}
  63. #cm ul,#cm ol{margin-left:30px;margin-bottom:1em}
  64. #cm ul{list-style-type:disc}
  65. #cm{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;padding:10px 0;border-top:1px dotted #c8ccd0;border-bottom:1px dotted #c8ccd0;clear:both}
  66. #cm{margin-top:-4px}
  67. #cm{width:660px;margin-bottom:5px;word-wrap:break-word;font-size:15px;line-height:1.3}
  68. `;
  69.  
  70. GM_addStyle(styles);
  71.  
  72. (function() {
  73. 'use strict';
  74. var jQuery_1_6_1 = $.noConflict(true);
  75. var reader = new commonmark.Parser();
  76. var writer = new commonmark.HtmlRenderer({sourcepos: false, softbreak: '<br />'});
  77.  
  78. // explained below
  79. jQuery_1_6_1.fn.overrideNodeMethod = function(methodName, action) {
  80. var originalVal = jQuery_1_6_1.fn[methodName];
  81. var thisNode = this;
  82. jQuery_1_6_1.fn[methodName] = function() {
  83. if (this[0] == thisNode[0]) {
  84. return action.apply(this, arguments);
  85. } else {
  86. return originalVal.apply(this, arguments);
  87. }
  88. };
  89. };
  90.  
  91. setInterval(function() {
  92. var $topNote = jQuery_1_6_1("div.selected > div.notes .content");
  93. if ($topNote.length === 0)
  94. return;
  95. if ($topNote.attr('data-hasPreview')) {
  96. return;
  97. }
  98.  
  99. if (jQuery_1_6_1('#cm').length === 0)
  100. jQuery_1_6_1('#pageContainer').append("<div id='cm' style='display: none; background-color: white; padding: 30px 60px 10px; width: 820px; margin: 15px auto;'></div>");
  101.  
  102. if (!$topNote.text().trim()) {
  103. jQuery_1_6_1('#cm').css('display', 'none');
  104. jQuery_1_6_1('#pageContainer > div:first').css({'height': 'auto', 'overflow-y': 'inherit'});
  105. return;
  106. }
  107.  
  108. jQuery_1_6_1('#cm').css('display', 'inherit');
  109. jQuery_1_6_1('#pageContainer > div:first').css({'height': '400px', 'overflow-y': 'scroll'});
  110.  
  111. // Andrey Fedoseev's textareaPreview expects a textarea, so uses val()
  112. // but WorkFlowy uses editable div's, so we override val()
  113. $topNote.overrideNodeMethod('val', function() {
  114. // needed by CommonMark
  115. var softSpaced = this.html().replace(/&nbsp;/g, ' ');
  116. return jQuery_1_6_1("<div/>").html(softSpaced).text();
  117. });
  118.  
  119. $topNote.textareaPreview({
  120. container: '#cm',
  121. preprocess: function(text) {
  122. var parsed = reader.parse(text);
  123. var result = writer.render(parsed);
  124. return result;
  125. }
  126. });
  127.  
  128. $topNote.attr('data-hasPreview', true);
  129. }, 100);
  130. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址