main.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. class towxml {
  2. constructor(option) {
  3. const _ts = this;
  4. option = option || {};
  5. for (let i in option) {
  6. _ts.config[i] = option[i];
  7. };
  8. _ts.m = {};
  9. let mdOption = {
  10. html: true,
  11. xhtmlOut: true,
  12. typographer: true,
  13. highlight: function (code, lang, callback) {
  14. return _ts.m.highlight.highlightAuto(code).value;
  15. }
  16. };
  17. if (global) {
  18. _ts.m.html2json = require('./lib/html2json');
  19. _ts.m.highlight = require('./plugins/hljs/index');
  20. _ts.m.md = require('./lib/markdown-it')(mdOption);
  21. _ts.m.md_sub = require('./plugins/markdown-it-sub');
  22. _ts.m.md_sup = require('./plugins/markdown-it-sup');
  23. _ts.m.md_ins = require('./plugins/markdown-it-ins');
  24. _ts.m.md_mark = require('./plugins/markdown-it-mark');
  25. _ts.m.md_emoji = require('./plugins/markdown-it-emoji');
  26. } else if (window) {
  27. _ts.m.html2json = window.html2json;
  28. _ts.m.highlight = window.hljs;
  29. _ts.m.md = new window.markdownit(mdOption);
  30. _ts.m.md_sub = window.markdownitSub;
  31. _ts.m.md_sup = window.markdownitSup;
  32. _ts.m.md_ins = window.markdownitIns;
  33. _ts.m.md_mark = window.markdownitMark;
  34. _ts.m.md_emoji = window.markdownitEmoji;
  35. };
  36. _ts.m.md.use(_ts.m.md_sub);
  37. _ts.m.md.use(_ts.m.md_sup);
  38. _ts.m.md.use(_ts.m.md_ins);
  39. _ts.m.md.use(_ts.m.md_mark);
  40. _ts.m.md.use(_ts.m.md_emoji);
  41. _ts.m.md.renderer.rules.emoji = function (token, idx) {
  42. // return '<img class="h2w__emoji h2w__emoji--'+token[idx].markup+'" src="'+_ts.config.emoji_path + token[idx].content+'.'+ _ts.config.emoji_type+' "/>';
  43. return '<g-emoji class="h2w__emoji h2w__emoji--' + token[idx].markup + '">' + token[idx].content + '</g-emoji>';
  44. };
  45. _ts.wxmlTag = ['view', 'video', 'swiper', 'block', 'swiper-item', 'button', 'slider', 'scroll-view', 'movable-area', 'movable-view', 'text', 'progress', 'checkbox-group', 'label', 'checkbox', 'form', 'switch', 'input', 'radio-group', 'radio', 'picker', 'picker-view', 'switch', 'textarea', 'navigator', 'audio', 'image', 'map', 'canvas', 'contact-button'];
  46. }
  47. //markdown转html
  48. md2html(mdContent) {
  49. const _ts = this;
  50. return _ts.m.md.render(mdContent);
  51. }
  52. //html转wxml
  53. html2wxml(htmlContent) {
  54. const _ts = this;
  55. let re = /<[^<]*>/ig,
  56. wxml = htmlContent.replace(re, (word) => {
  57. //检查是否为关闭标签
  58. let isCloseLabel = (() => {
  59. let star = word.substr(0, 2);
  60. return star === '</';
  61. })();
  62. //处理关闭标签替换
  63. if (isCloseLabel) {
  64. let labelName = word.substr(2, word.length - 3).toLowerCase();
  65. if (_ts.isConversion(labelName)) {
  66. return '</' + _ts.newLabel(labelName) + '>';
  67. };
  68. }
  69. //处理开始标签替换
  70. else {
  71. let delWordBbrackets = word.substr(1, word.length - 2), //剔除首尾尖括号
  72. wordSplit = delWordBbrackets.split(' '), //得到元素标签与属性
  73. labelName = wordSplit[0].toLowerCase(), //取得tagName
  74. className_htmlTag = 'h2w__' + labelName;
  75. if (_ts.isConversion(labelName)) {
  76. wordSplit.splice(0, 1); //剔除元素的标签
  77. //检查元素是否已经有className,有的话在原基础上添加新的类名
  78. let wordSplitLen = wordSplit.length,
  79. isClassExist = (() => {
  80. if (wordSplitLen) {
  81. for (let i = 0; i < wordSplitLen; i++) {
  82. let item = wordSplit[i],
  83. re = /class="/ig;
  84. if (re.test(item)) {
  85. wordSplit[i] = item.replace(re, (word) => {
  86. return word + className_htmlTag + ' ';
  87. });
  88. return true;
  89. };
  90. };
  91. };
  92. return false;
  93. })();
  94. //如果元素没有className,则新加上className
  95. if (!isClassExist) {
  96. wordSplit.unshift('class="' + className_htmlTag + '"');
  97. };
  98. //组合属性
  99. let newAttrs = (() => {
  100. let s = '';
  101. wordSplit.forEach((item, index) => {
  102. s += item + ' ';
  103. });
  104. s = s.substr(0, s.length - 1);
  105. return s;
  106. })();
  107. //如果是图片
  108. if (labelName === 'img') {
  109. return '<image ' + newAttrs + '></image>'
  110. };
  111. return '<' + _ts.newLabel(labelName) + ' ' + newAttrs + '>' + _ts.needClose(labelName);
  112. };
  113. };
  114. return word;
  115. });
  116. return wxml;
  117. }
  118. //markdown转wxml
  119. md2wxml(mdContent) {
  120. const _ts = this;
  121. let html = _ts.md2html(mdContent),
  122. wxml = _ts.html2wxml(html),
  123. deCode = str => {
  124. let s = '';
  125. if(str.length === 0){
  126. return s;
  127. };
  128. s = str.replace(/&amp;/ig,'&');
  129. s = s.replace(/&lt;/ig, "<");
  130. s = s.replace(/&gt;/ig, ">");
  131. s = s.replace(/&nbsp;/g, " ");
  132. s = s.replace(/&#39;/g, "'");
  133. s = s.replace(/&quot;/g, """);
  134. return s;
  135. };
  136. return deCode(wxml);
  137. }
  138. //检查标签是否需要转换
  139. isConversion(labelName) {
  140. const _ts = this;
  141. return !_ts.wxmlTag.some((item, index) => {
  142. return labelName === item;
  143. });
  144. }
  145. //处理自关闭标签,hr,br这些需要添加</view>关闭标签
  146. needClose(labelName) {
  147. let arr = ['hr', 'br'],
  148. s = '',
  149. closeTag = arr.some((item, index) => {
  150. return labelName === item;
  151. });
  152. if (closeTag) {
  153. s = '</view>';
  154. };
  155. return s;
  156. }
  157. //html与wxml对应的标签
  158. newLabel(labelName) {
  159. let temp = 'view';
  160. switch (labelName) {
  161. case 'a':
  162. temp = 'navigator';
  163. break;
  164. case 'span':
  165. case 'b':
  166. case 'strong':
  167. case 'i':
  168. case 'em':
  169. case 'code':
  170. case 'sub':
  171. case 'sup':
  172. case 'g-emoji':
  173. case 'mark':
  174. case 'ins':
  175. temp = 'text';
  176. break;
  177. };
  178. return temp;
  179. }
  180. //html2json
  181. //content html、markdown字符串
  182. //type 'html|markdown|xml'
  183. toJson(content, type) {
  184. const _ts = this;
  185. type = type || 'html';
  186. let json = '',
  187. sortOutJson;
  188. if (type === 'markdown') {
  189. json = _ts.m.html2json(_ts.md2wxml(content));
  190. } else if (type === 'html') {
  191. json = _ts.m.html2json(_ts.html2wxml(content));
  192. };
  193. //遍历json将多个class属性合为一个
  194. (sortOutJson = (json) => {
  195. for (let i in json) {
  196. if (i === 'child' && typeof json[i] === 'object' && json[i].length) {
  197. json[i].forEach((item, index) => {
  198. sortOutJson(item);
  199. });
  200. };
  201. if (i === 'attr') {
  202. if (typeof json[i].class === 'string') {
  203. json[i].className = json[i].class;
  204. } else if (typeof json[i].class === 'object' && json[i].class.length) {
  205. json[i].className = json[i].class.toString().replace(/,/g, ' ');
  206. };
  207. };
  208. };
  209. })(json);
  210. json.theme = 'light';
  211. return json;
  212. }
  213. };
  214. module.exports = towxml;