a685c22e6663029f2a965a39R1571">1571
+});
1572
+
1573
+/**
1574
+ * Handle github codeblocks prior to running HashHTML so that
1575
+ * HTML contained within the codeblock gets escaped properly
1576
+ * Example:
1577
+ * ```ruby
1578
+ *     def hello_world(x)
1579
+ *       puts "Hello, #{x}"
1580
+ *     end
1581
+ * ```
1582
+ */
1583
+showdown.subParser('githubCodeBlocks', (text, options, globals) => {
1584
+  // early exit if option is not enabled
1585
+  if (!options.ghCodeBlocks) {
1586
+    return text;
1587
+  }
1588
+
1589
+  text = globals.converter._dispatch('githubCodeBlocks.before', text, options, globals);
1590
+
1591
+  text += '~0';
1592
+
1593
+  text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g, (wholeMatch, language, codeblock) => {
1594
+    const end = (options.omitExtraWLInCodeBlocks) ? '' : '\n';
1595
+
1596
+    // First parse the github code block
1597
+    codeblock = showdown.subParser('encodeCode')(codeblock);
1598
+    codeblock = showdown.subParser('detab')(codeblock);
1599
+    codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
1600
+    codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing whitespace
1601
+
1602
+    codeblock = `<pre><code${language ? ` class="${language} language-${language}"` : ''}>${codeblock}${end}</code></pre>`;
1603
+
1604
+    codeblock = showdown.subParser('hashBlock')(codeblock, options, globals);
1605
+
1606
+    // Since GHCodeblocks can be false positives, we need to
1607
+    // store the primitive text and the parsed text in a global var,
1608
+    // and then return a token
1609
+    return `\n\n~G${globals.ghCodeBlocks.push({ text: wholeMatch, codeblock }) - 1}G\n\n`;
1610
+  });
1611
+
1612
+  // attacklab: strip sentinel
1613
+  text = text.replace(/~0/, '');
1614
+
1615
+  return globals.converter._dispatch('githubCodeBlocks.after', text, options, globals);
1616
+});
1617
+
1618
+showdown.subParser('hashBlock', (text, options, globals) => {
1619
+  text = text.replace(/(^\n+|\n+$)/g, '');
1620
+  return `\n\n~K${globals.gHtmlBlocks.push(text) - 1}K\n\n`;
1621
+});
1622
+
1623
+showdown.subParser('hashElement', (text, options, globals) => function (wholeMatch, m1) {
1624
+  let blockText = m1;
1625
+
1626
+  // Undo double lines
1627
+  blockText = blockText.replace(/\n\n/g, '\n');
1628
+  blockText = blockText.replace(/^\n/, '');
1629
+
1630
+  // strip trailing blank lines
1631
+  blockText = blockText.replace(/\n+$/g, '');
1632
+
1633
+  // Replace the element text with a marker ("~KxK" where x is its key)
1634
+  blockText = `\n\n~K${globals.gHtmlBlocks.push(blockText) - 1}K\n\n`;
1635
+
1636
+  return blockText;
1637
+});
1638
+
1639
+showdown.subParser('hashHTMLBlocks', (text, options, globals) => {
1640
+  const blockTags = [
1641
+    'pre',
1642
+    'div',
1643
+    'h1',
1644
+    'h2',
1645
+    'h3',
1646
+    'h4',
1647
+    'h5',
1648
+    'h6',
1649
+    'blockquote',
1650
+    'table',
1651
+    'dl',
1652
+    'ol',
1653
+    'ul',
1654
+    'script',
1655
+    'noscript',
1656
+    'form',
1657
+    'fieldset',
1658
+    'iframe',
1659
+    'math',
1660
+    'style',
1661
+    'section',
1662
+    'header',
1663
+    'footer',
1664
+    'nav',
1665
+    'article',
1666
+    'aside',
1667
+    'address',
1668
+    'audio',
1669
+    'canvas',
1670
+    'figure',
1671
+    'hgroup',
1672
+    'output',
1673
+    'video',
1674
+    'p',
1675
+  ];
1676
+  const repFunc = function (wholeMatch, match, left, right) {
1677
+    let txt = wholeMatch;
1678
+    // check if this html element is marked as markdown
1679
+    // if so, it's contents should be parsed as markdown
1680
+    if (left.search(/\bmarkdown\b/) !== -1) {
1681
+      txt = left + globals.converter.makeHtml(match) + right;
1682
+    }
1683
+    return `\n\n~K${globals.gHtmlBlocks.push(txt) - 1}K\n\n`;
1684
+  };
1685
+
1686
+  for (let i = 0; i < blockTags.length; ++i) {
1687
+    text = showdown.helper.replaceRecursiveRegExp(text, repFunc, `^(?: |\\t){0,3}<${blockTags[i]}\\b[^>]*>`, `</${blockTags[i]}>`, 'gim');
1688
+  }
1689
+
1690
+  // HR SPECIAL CASE
1691
+  text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,
1692
+    showdown.subParser('hashElement')(text, options, globals));
1693
+
1694
+  // Special case for standalone HTML comments:
1695
+  text = text.replace(/(<!--[\s\S]*?-->)/g,
1696
+    showdown.subParser('hashElement')(text, options, globals));
1697
+
1698
+  // PHP and ASP-style processor instructions (<?...?> and <%...%>)
1699
+  text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,
1700
+    showdown.subParser('hashElement')(text, options, globals));
1701
+  return text;
1702
+});
1703
+
1704
+/**
1705
+ * Hash span elements that should not be parsed as markdown
1706
+ */
1707
+showdown.subParser('hashHTMLSpans', (text, config, globals) => {
1708
+  const matches = showdown.helper.matchRecursiveRegExp(text, '<code\\b[^>]*>', '</code>', 'gi');
1709
+
1710
+  for (let i = 0; i < matches.length; ++i) {
1711
+    text = text.replace(matches[i][0], `~L${globals.gHtmlSpans.push(matches[i][0]) - 1}L`);
1712
+  }
1713
+  return text;
1714
+});
1715
+
1716
+/**
1717
+ * Unhash HTML spans
1718
+ */
1719
+showdown.subParser('unhashHTMLSpans', (text, config, globals) => {
1720
+  for (let i = 0; i < globals.gHtmlSpans.length; ++i) {
1721
+    text = text.replace(`~L${i}L`, globals.gHtmlSpans[i]);
1722
+  }
1723
+
1724
+  return text;
1725
+});
1726
+
1727
+/**
1728
+ * Hash span elements that should not be parsed as markdown
1729
+ */
1730
+showdown.subParser('hashPreCodeTags', (text, config, globals) => {
1731
+  const repFunc = function (wholeMatch, match, left, right) {
1732
+    // encode html entities
1733
+    const codeblock = left + showdown.subParser('encodeCode')(match) + right;
1734
+    return `\n\n~G${globals.ghCodeBlocks.push({ text: wholeMatch, codeblock }) - 1}G\n\n`;
1735
+  };
1736
+
1737
+  text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^(?: |\\t){0,3}<pre\\b[^>]*>\\s*<code\\b[^>]*>', '^(?: |\\t){0,3}</code>\\s*</pre>', 'gim');
1738
+  return text;
1739
+});
1740
+
1741
+showdown.subParser('headers', (text, options, globals) => {
1742
+  text = globals.converter._dispatch('headers.before', text, options, globals);
1743
+
1744
+  let prefixHeader = options.prefixHeaderId;
1745
+  const headerLevelStart = (isNaN(parseInt(options.headerLevelStart))) ? 1 : parseInt(options.headerLevelStart);
1746
+
1747
+  // Set text-style headers:
1748
+  //	Header 1
1749
+  //	========
1750
+  //
1751
+  //	Header 2
1752
+  //	--------
1753
+  //
1754
+  const setextRegexH1 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n={2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n=+[ \t]*\n+/gm;
1755
+  const setextRegexH2 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n-+[ \t]*\n+/gm;
1756
+
1757
+  text = text.replace(setextRegexH1, (wholeMatch, m1) => {
1758
+    const spanGamut = showdown.subParser('spanGamut')(m1, options, globals);
1759
+    const hID = (options.noHeaderId) ? '' : ` id="${headerId(m1)}"`;
1760
+    const hLevel = headerLevelStart;
1761
+    const hashBlock = `<h${hLevel}${hID}>${spanGamut}</h${hLevel}>`;
1762
+    return showdown.subParser('hashBlock')(hashBlock, options, globals);
1763
+  });
1764
+
1765
+  text = text.replace(setextRegexH2, (matchFound, m1) => {
1766
+    const spanGamut = showdown.subParser('spanGamut')(m1, options, globals);
1767
+    const hID = (options.noHeaderId) ? '' : ` id="${headerId(m1)}"`;
1768
+    const hLevel = headerLevelStart + 1;
1769
+    const hashBlock = `<h${hLevel}${hID}>${spanGamut}</h${hLevel}>`;
1770
+    return showdown.subParser('hashBlock')(hashBlock, options, globals);
1771
+  });
1772
+
1773
+  // atx-style headers:
1774
+  //  # Header 1
1775
+  //  ## Header 2
1776
+  //  ## Header 2 with closing hashes ##
1777
+  //  ...
1778
+  //  ###### Header 6
1779
+  //
1780
+  text = text.replace(/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm, (wholeMatch, m1, m2) => {
1781
+    const span = showdown.subParser('spanGamut')(m2, options, globals);
1782
+    const hID = (options.noHeaderId) ? '' : ` id="${headerId(m2)}"`;
1783
+    const hLevel = headerLevelStart - 1 + m1.length;
1784
+    const header = `<h${hLevel}${hID}>${span}</h${hLevel}>`;
1785
+
1786
+    return showdown.subParser('hashBlock')(header, options, globals);
1787
+  });
1788
+
1789
+  function headerId(m) {
1790
+    let title; const
1791
+      escapedId = m.replace(/[^\w]/g, '').toLowerCase();
1792
+
1793
+    if (globals.hashLinkCounts[escapedId]) {
1794
+      title = `${escapedId}-${globals.hashLinkCounts[escapedId]++}`;
1795
+    } else {
1796
+      title = escapedId;
1797
+      globals.hashLinkCounts[escapedId] = 1;
1798
+    }
1799
+
1800
+    // Prefix id to prevent causing inadvertent pre-existing style matches.
1801
+    if (prefixHeader === true) {
1802
+      prefixHeader = 'section';
1803
+    }
1804
+
1805
+    if (showdown.helper.isString(prefixHeader)) {
1806
+      return prefixHeader + title;
1807
+    }
1808
+    return title;
1809
+  }
1810
+
1811
+  text = globals.converter._dispatch('headers.after', text, options, globals);
1812
+  return text;
1813
+});
1814
+
1815
+/**
1816
+ * Turn Markdown image shortcuts into <img> tags.
1817
+ */
1818
+showdown.subParser('images', (text, options, globals) => {
1819
+  text = globals.converter._dispatch('images.before', text, options, globals);
1820
+
1821
+  const inlineRegExp = /!\[(.*?)]\s?\([ \t]*()<?(\S+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(['"])(.*?)\6[ \t]*)?\)/g;
1822
+  const referenceRegExp = /!\[([^\]]*?)] ?(?:\n *)?\[(.*?)]()()()()()/g;
1823
+
1824
+  function writeImageTag(wholeMatch, altText, linkId, url, width, height, m5, title) {
1825
+    const { gUrls } = globals;
1826
+    const { gTitles } = globals;
1827
+    const gDims = globals.gDimensions;
1828
+
1829
+    linkId = linkId.toLowerCase();
1830
+
1831
+    if (!title) {
1832
+      title = '';
1833
+    }
1834
+
1835
+    if (url === '' || url === null) {
1836
+      if (linkId === '' || linkId === null) {
1837
+        // lower-case and turn embedded newlines into spaces
1838
+        linkId = altText.toLowerCase().replace(/ ?\n/g, ' ');
1839
+      }
1840
+      url = `#${linkId}`;
1841
+
1842
+      if (!showdown.helper.isUndefined(gUrls[linkId])) {
1843
+        url = gUrls[linkId];
1844
+        if (!showdown.helper.isUndefined(gTitles[linkId])) {
1845
+          title = gTitles[linkId];
1846
+        }
1847
+        if (!showdown.helper.isUndefined(gDims[linkId])) {
1848
+          width = gDims[linkId].width;
1849
+          height = gDims[linkId].height;
1850
+        }
1851
+      } else {
1852
+        return wholeMatch;
1853
+      }
1854
+    }
1855
+
1856
+    altText = altText.replace(/"/g, '&quot;');
1857
+    altText = showdown.helper.escapeCharacters(altText, '*_', false);
1858
+    url = showdown.helper.escapeCharacters(url, '*_', false);
1859
+    let result = `<img src="${url}" alt="${altText}"`;
1860
+
1861
+    if (title) {
1862
+      title = title.replace(/"/g, '&quot;');
1863
+      title = showdown.helper.escapeCharacters(title, '*_', false);
1864
+      result += ` title="${title}"`;
1865
+    }
1866
+
1867
+    if (width && height) {
1868
+      width = (width === '*') ? 'auto' : width;
1869
+      height = (height === '*') ? 'auto' : height;
1870
+
1871
+      result += ` width="${width}"`;
1872
+      result += ` height="${height}"`;
1873
+    }
1874
+
1875
+    result += ' />';
1876
+    return result;
1877
+  }
1878
+
1879
+  // First, handle reference-style labeled images: ![alt text][id]
1880
+  text = text.replace(referenceRegExp, writeImageTag);
1881
+
1882
+  // Next, handle inline images:  ![alt text](url =<width>x<height> "optional title")
1883
+  text = text.replace(inlineRegExp, writeImageTag);
1884
+
1885
+  text = globals.converter._dispatch('images.after', text, options, globals);
1886
+  return text;
1887
+});
1888
+
1889
+showdown.subParser('italicsAndBold', (text, options, globals) => {
1890
+  text = globals.converter._dispatch('italicsAndBold.before', text, options, globals);
1891
+
1892
+  if (options.literalMidWordUnderscores) {
1893
+    // underscores
1894
+    // Since we are consuming a \s character, we need to add it
1895
+    text = text.replace(/(^|\s|>|\b)__(?=\S)([\s\S]+?)__(?=\b|<|\s|$)/gm, '$1<strong>$2</strong>');
1896
+    text = text.replace(/(^|\s|>|\b)_(?=\S)([\s\S]+?)_(?=\b|<|\s|$)/gm, '$1<em>$2</em>');
1897
+    // asterisks
1898
+    text = text.replace(/(\*\*)(?=\S)([^\r]*?\S[*]*)\1/g, '<strong>$2</strong>');
1899
+    text = text.replace(/(\*)(?=\S)([^\r]*?\S)\1/g, '<em>$2</em>');
1900
+  } else {
1901
+    // <strong> must go first:
1902
+    text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, '<strong>$2</strong>');
1903
+    text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, '<em>$2</em>');
1904
+  }
1905
+
1906
+  text = globals.converter._dispatch('italicsAndBold.after', text, options, globals);
1907
+  return text;
1908
+});
1909
+
1910
+/**
1911
+ * Form HTML ordered (numbered) and unordered (bulleted) lists.
1912
+ */
1913
+showdown.subParser('lists', (text, options, globals) => {
1914
+  text = globals.converter._dispatch('lists.before', text, options, globals);
1915
+  /**
1916
+   * Process the contents of a single ordered or unordered list, splitting it
1917
+   * into individual list items.
1918
+   * @param {string} listStr
1919
+   * @param {boolean} trimTrailing
1920
+   * @returns {string}
1921
+   */
1922
+  function processListItems(listStr, trimTrailing) {
1923
+    // The $g_list_level global keeps track of when we're inside a list.
1924
+    // Each time we enter a list, we increment it; when we leave a list,
1925
+    // we decrement. If it's zero, we're not in a list anymore.
1926
+    //
1927
+    // We do this because when we're not inside a list, we want to treat
1928
+    // something like this:
1929
+    //
1930
+    //    I recommend upgrading to version
1931
+    //    8. Oops, now this line is treated
1932
+    //    as a sub-list.
1933
+    //
1934
+    // As a single paragraph, despite the fact that the second line starts
1935
+    // with a digit-period-space sequence.
1936
+    //
1937
+    // Whereas when we're inside a list (or sub-list), that line will be
1938
+    // treated as the start of a sub-list. What a kludge, huh? This is
1939
+    // an aspect of Markdown's syntax that's hard to parse perfectly
1940
+    // without resorting to mind-reading. Perhaps the solution is to
1941
+    // change the syntax rules such that sub-lists must start with a
1942
+    // starting cardinal number; e.g. "1." or "a.".
1943
+    globals.gListLevel++;
1944
+
1945
+    // trim trailing blank lines:
1946
+    listStr = listStr.replace(/\n{2,}$/, '\n');
1947
+
1948
+    // attacklab: add sentinel to emulate \z
1949
+    listStr += '~0';
1950
+
1951
+    const rgx = /(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm;
1952
+    const isParagraphed = (/\n[ \t]*\n(?!~0)/.test(listStr));
1953
+
1954
+    listStr = listStr.replace(rgx, (wholeMatch, m1, m2, m3, m4, taskbtn, checked) => {
1955
+      checked = (checked && checked.trim() !== '');
1956
+      let item = showdown.subParser('outdent')(m4, options, globals);
1957
+      let bulletStyle = '';
1958
+
1959
+      // Support for github tasklists
1960
+      if (taskbtn && options.tasklists) {
1961
+        bulletStyle = ' class="task-list-item" style="list-style-type: none;"';
1962
+        item = item.replace(/^[ \t]*\[(x|X| )?]/m, () => {
1963
+          let otp = '<input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;"';
1964
+          if (checked) {
1965
+            otp += ' checked';
1966
+          }
1967
+          otp += '>';
1968
+          return otp;
1969
+        });
1970
+      }
1971
+      // m1 - Leading line or
1972
+      // Has a double return (multi paragraph) or
1973
+      // Has sublist
1974
+      if (m1 || (item.search(/\n{2,}/) > -1)) {
1975
+        item = showdown.subParser('githubCodeBlocks')(item, options, globals);
1976
+        item = showdown.subParser('blockGamut')(item, options, globals);
1977
+      } else {
1978
+        // Recursion for sub-lists:
1979
+        item = showdown.subParser('lists')(item, options, globals);
1980
+        item = item.replace(/\n$/, ''); // chomp(item)
1981
+        if (isParagraphed) {
1982
+          item = showdown.subParser('paragraphs')(item, options, globals);
1983
+        } else {
1984
+          item = showdown.subParser('spanGamut')(item, options, globals);
1985
+        }
1986
+      }
1987
+      item = `\n<li${bulletStyle}>${item}</li>\n`;
1988
+      return item;
1989
+    });
1990
+
1991
+    // attacklab: strip sentinel
1992
+    listStr = listStr.replace(/~0/g, '');
1993
+
1994
+    globals.gListLevel--;
1995
+
1996
+    if (trimTrailing) {
1997
+      listStr = listStr.replace(/\s+$/, '');
1998
+    }
1999
+
2000
+    return listStr;
2001
+  }
2002
+
2003
+  /**
2004
+   * Check and parse consecutive lists (better fix for issue #142)
2005
+   * @param {string} list
2006
+   * @param {string} listType
2007
+   * @param {boolean} trimTrailing
2008
+   * @returns {string}
2009
+   */
2010
+  function parseConsecutiveLists(list, listType, trimTrailing) {
2011
+    // check if we caught 2 or more consecutive lists by mistake
2012
+    // we use the counterRgx, meaning if listType is UL we look for UL and vice versa
2013
+    let counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm;
2014
+    const subLists = [];
2015
+    let result = '';
2016
+
2017
+    if (list.search(counterRxg) !== -1) {
2018
+      (function parseCL(txt) {
2019
+        const pos = txt.search(counterRxg);
2020
+        if (pos !== -1) {
2021
+          // slice
2022
+          result += `\n\n<${listType}>${processListItems(txt.slice(0, pos), !!trimTrailing)}</${listType}>\n\n`;
2023
+
2024
+          // invert counterType and listType
2025
+          listType = (listType === 'ul') ? 'ol' : 'ul';
2026
+          counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm;
2027
+
2028
+          // recurse
2029
+          parseCL(txt.slice(pos));
2030
+        } else {
2031
+          result += `\n\n<${listType}>${processListItems(txt, !!trimTrailing)}</${listType}>\n\n`;
2032
+        }
2033
+      }(list));
2034
+      for (let i = 0; i < subLists.length; ++i) {
2035
+
2036
+      }
2037
+    } else {
2038
+      result = `\n\n<${listType}>${processListItems(list, !!trimTrailing)}</${listType}>\n\n`;
2039
+    }
2040
+
2041
+    return result;
2042
+  }
2043
+
2044
+  // attacklab: add sentinel to hack around khtml/safari bug:
2045
+  // http://bugs.webkit.org/show_bug.cgi?id=11231
2046
+  text += '~0';
2047
+
2048
+  // Re-usable pattern to match any entire ul or ol list:
2049
+  let wholeList = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
2050
+
2051
+  if (globals.gListLevel) {
2052
+    text = text.replace(wholeList, (wholeMatch, list, m2) => {
2053
+      const listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
2054
+      return parseConsecutiveLists(list, listType, true);
2055
+    });
2056
+  } else {
2057
+    wholeList = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
2058
+    // wholeList = /(\n\n|^\n?)( {0,3}([*+-]|\d+\.)[ \t]+[\s\S]+?)(?=(~0)|(\n\n(?!\t| {2,}| {0,3}([*+-]|\d+\.)[ \t])))/g;
2059
+    text = text.replace(wholeList, (wholeMatch, m1, list, m3) => {
2060
+      const listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
2061
+      return parseConsecutiveLists(list, listType);
2062
+    });
2063
+  }
2064
+
2065
+  // attacklab: strip sentinel
2066
+  text = text.replace(/~0/, '');
2067
+
2068
+  text = globals.converter._dispatch('lists.after', text, options, globals);
2069
+  return text;
2070
+});
2071
+
2072
+/**
2073
+ * Remove one level of line-leading tabs or spaces
2074
+ */
2075
+showdown.subParser('outdent', (text) => {
2076
+  // attacklab: hack around Konqueror 3.5.4 bug:
2077
+  // "----------bug".replace(/^-/g,"") == "bug"
2078
+  text = text.replace(/^(\t|[ ]{1,4})/gm, '~0'); // attacklab: g_tab_width
2079
+
2080
+  // attacklab: clean up hack
2081
+  text = text.replace(/~0/g, '');
2082
+
2083
+  return text;
2084
+});
2085
+
2086
+/**
2087
+ *
2088
+ */
2089
+showdown.subParser('paragraphs', (text, options, globals) => {
2090
+  text = globals.converter._dispatch('paragraphs.before', text, options, globals);
2091
+  // Strip leading and trailing lines:
2092
+  text = text.replace(/^\n+/g, '');
2093
+  text = text.replace(/\n+$/g, '');
2094
+
2095
+  const grafs = text.split(/\n{2,}/g);
2096
+  const grafsOut = [];
2097
+  let end = grafs.length; // Wrap <p> tags
2098
+
2099
+  for (var i = 0; i < end; i++) {
2100
+    let str = grafs[i];
2101
+    // if this is an HTML marker, copy it
2102
+    if (str.search(/~(K|G)(\d+)\1/g) >= 0) {
2103
+      grafsOut.push(str);
2104
+    } else {
2105
+      str = showdown.subParser('spanGamut')(str, options, globals);
2106
+      str = str.replace(/^([ \t]*)/g, '<p>');
2107
+      str += '</p>';
2108
+      grafsOut.push(str);
2109
+    }
2110
+  }
2111
+
2112
+  /** Unhashify HTML blocks */
2113
+  end = grafsOut.length;
2114
+  for (i = 0; i < end; i++) {
2115
+    let blockText = '';
2116
+    let grafsOutIt = grafsOut[i];
2117
+    let codeFlag = false;
2118
+    // if this is a marker for an html block...
2119
+    while (grafsOutIt.search(/~(K|G)(\d+)\1/) >= 0) {
2120
+      const delim = RegExp.$1;
2121
+      const num = RegExp.$2;
2122
+
2123
+      if (delim === 'K') {
2124
+        blockText = globals.gHtmlBlocks[num];
2125
+      } else {
2126
+        // we need to check if ghBlock is a false positive
2127
+        if (codeFlag) {
2128
+          // use encoded version of all text
2129
+          blockText = showdown.subParser('encodeCode')(globals.ghCodeBlocks[num].text);
2130
+        } else {
2131
+          blockText = globals.ghCodeBlocks[num].codeblock;
2132
+        }
2133
+      }
2134
+      blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs
2135
+
2136
+      grafsOutIt = grafsOutIt.replace(/(\n\n)?~(K|G)\d+\2(\n\n)?/, blockText);
2137
+      // Check if grafsOutIt is a pre->code
2138
+      if (/^<pre\b[^>]*>\s*<code\b[^>]*>/.test(grafsOutIt)) {
2139
+        codeFlag = true;
2140
+      }
2141
+    }
2142
+    grafsOut[i] = grafsOutIt;
2143
+  }
2144
+  text = grafsOut.join('\n\n');
2145
+  // Strip leading and trailing lines:
2146
+  text = text.replace(/^\n+/g, '');
2147
+  text = text.replace(/\n+$/g, '');
2148
+  return globals.converter._dispatch('paragraphs.after', text, options, globals);
2149
+});
2150
+
2151
+/**
2152
+ * Run extension
2153
+ */
2154
+showdown.subParser('runExtension', (ext, text, options, globals) => {
2155
+  if (ext.filter) {
2156
+    text = ext.filter(text, globals.converter, options);
2157
+  } else if (ext.regex) {
2158
+    // TODO remove this when old extension loading mechanism is deprecated
2159
+    let re = ext.regex;
2160
+    if (!re instanceof RegExp) {
2161
+      re = new RegExp(re, 'g');
2162
+    }
2163
+    text = text.replace(re, ext.replace);
2164
+  }
2165
+
2166
+  return text;
2167
+});
2168
+
2169
+/**
2170
+ * These are all the transformations that occur *within* block-level
2171
+ * tags like paragraphs, headers, and list items.
2172
+ */
2173
+showdown.subParser('spanGamut', (text, options, globals) => {
2174
+  text = globals.converter._dispatch('spanGamut.before', text, options, globals);
2175
+  text = showdown.subParser('codeSpans')(text, options, globals);
2176
+  text = showdown.subParser('escapeSpecialCharsWithinTagAttributes')(text, options, globals);
2177
+  text = showdown.subParser('encodeBackslashEscapes')(text, options, globals);
2178
+
2179
+  // Process anchor and image tags. Images must come first,
2180
+  // because ![foo][f] looks like an anchor.
2181
+  text = showdown.subParser('images')(text, options, globals);
2182
+  text = showdown.subParser('anchors')(text, options, globals);
2183
+
2184
+  // Make links out of things like `<http://example.com/>`
2185
+  // Must come after _DoAnchors(), because you can use < and >
2186
+  // delimiters in inline links like [this](<url>).
2187
+  text = showdown.subParser('autoLinks')(text, options, globals);
2188
+  text = showdown.subParser('encodeAmpsAndAngles')(text, options, globals);
2189
+  text = showdown.subParser('italicsAndBold')(text, options, globals);
2190
+  text = showdown.subParser('strikethrough')(text, options, globals);
2191
+
2192
+  // Do hard breaks:
2193
+  text = text.replace(/  +\n/g, ' <br />\n');
2194
+
2195
+  text = globals.converter._dispatch('spanGamut.after', text, options, globals);
2196
+  return text;
2197
+});
2198
+
2199
+showdown.subParser('strikethrough', (text, options, globals) => {
2200
+  if (options.strikethrough) {
2201
+    text = globals.converter._dispatch('strikethrough.before', text, options, globals);
2202
+    text = text.replace(/(?:~T){2}([\s\S]+?)(?:~T){2}/g, '<del>$1</del>');
2203
+    text = globals.converter._dispatch('strikethrough.after', text, options, globals);
2204
+  }
2205
+
2206
+  return text;
2207
+});
2208
+
2209
+/**
2210
+ * Strip any lines consisting only of spaces and tabs.
2211
+ * This makes subsequent regexs easier to write, because we can
2212
+ * match consecutive blank lines with /\n+/ instead of something
2213
+ * contorted like /[ \t]*\n+/
2214
+ */
2215
+showdown.subParser('stripBlankLines', (text) => text.replace(/^[ \t]+$/mg, ''));
2216
+
2217
+/**
2218
+ * Strips link definitions from text, stores the URLs and titles in
2219
+ * hash references.
2220
+ * Link defs are in the form: ^[id]: url "optional title"
2221
+ *
2222
+ * ^[ ]{0,3}\[(.+)\]: // id = $1  attacklab: g_tab_width - 1
2223
+ * [ \t]*
2224
+ * \n?                  // maybe *one* newline
2225
+ * [ \t]*
2226
+ * <?(\S+?)>?          // url = $2
2227
+ * [ \t]*
2228
+ * \n?                // maybe one newline
2229
+ * [ \t]*
2230
+ * (?:
2231
+ * (\n*)              // any lines skipped = $3 attacklab: lookbehind removed
2232
+ * ["(]
2233
+ * (.+?)              // title = $4
2234
+ * [")]
2235
+ * [ \t]*
2236
+ * )?                 // title is optional
2237
+ * (?:\n+|$)
2238
+ * /gm,
2239
+ * function(){...});
2240
+ *
2241
+ */
2242
+showdown.subParser('stripLinkDefinitions', (text, options, globals) => {
2243
+  const regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*<?(\S+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=~0))/gm;
2244
+
2245
+  // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
2246
+  text += '~0';
2247
+
2248
+  text = text.replace(regex, (wholeMatch, linkId, url, width, height, blankLines, title) => {
2249
+    linkId = linkId.toLowerCase();
2250
+    globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url); // Link IDs are case-insensitive
2251
+
2252
+    if (blankLines) {
2253
+      // Oops, found blank lines, so it's not a title.
2254
+      // Put back the parenthetical statement we stole.
2255
+      return blankLines + title;
2256
+    }
2257
+    if (title) {
2258
+      globals.gTitles[linkId] = title.replace(/"|'/g, '&quot;');
2259
+    }
2260
+    if (options.parseImgDimensions && width && height) {
2261
+      globals.gDimensions[linkId] = {
2262
+        width,
2263
+        height,
2264
+      };
2265
+    }
2266
+
2267
+    // Completely remove the definition from the text
2268
+    return '';
2269
+  });
2270
+
2271
+  // attacklab: strip sentinel
2272
+  text = text.replace(/~0/, '');
2273
+
2274
+  return text;
2275
+});
2276
+
2277
+showdown.subParser('tables', (text, options, globals) => {
2278
+  if (!options.tables) {
2279
+    return text;
2280
+  }
2281
+
2282
+  const tableRgx = /^[ \t]{0,3}\|?.+\|.+\n[ \t]{0,3}\|?[ \t]*:?[ \t]*(?:-|=){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:-|=){2,}[\s\S]+?(?:\n\n|~0)/gm;
2283
+
2284
+  function parseStyles(sLine) {
2285
+    if (/^:[ \t]*--*$/.test(sLine)) {
2286
+      return ' style="text-align:left;"';
2287
+    } if (/^--*[ \t]*:[ \t]*$/.test(sLine)) {
2288
+      return ' style="text-align:right;"';
2289
+    } if (/^:[ \t]*--*[ \t]*:$/.test(sLine)) {
2290
+      return ' style="text-align:center;"';
2291
+    }
2292
+    return '';
2293
+  }
2294
+
2295
+  function parseHeaders(header, style) {
2296
+    let id = '';
2297
+    header = header.trim();
2298
+    if (options.tableHeaderId) {
2299
+      id = ` id="${header.replace(/ /g, '_').toLowerCase()}"`;
2300
+    }
2301
+    header = showdown.subParser('spanGamut')(header, options, globals);
2302
+
2303
+    return `<th${id}${style}>${header}</th>\n`;
2304
+  }
2305
+
2306
+  function parseCells(cell, style) {
2307
+    const subText = showdown.subParser('spanGamut')(cell, options, globals);
2308
+    return `<td${style}>${subText}</td>\n`;
2309
+  }
2310
+
2311
+  function buildTable(headers, cells) {
2312
+    let tb = '<table>\n<thead>\n<tr>\n';
2313
+    const tblLgn = headers.length;
2314
+
2315
+    for (var i = 0; i < tblLgn; ++i) {
2316
+      tb += headers[i];
2317
+    }
2318
+    tb += '</tr>\n</thead>\n<tbody>\n';
2319
+
2320
+    for (i = 0; i < cells.length; ++i) {
2321
+      tb += '<tr>\n';
2322
+      for (let ii = 0; ii < tblLgn; ++ii) {
2323
+        tb += cells[i][ii];
2324
+      }
2325
+      tb += '</tr>\n';
2326
+    }
2327
+    tb += '</tbody>\n</table>\n';
2328
+    return tb;
2329
+  }
2330
+
2331
+  text = globals.converter._dispatch('tables.before', text, options, globals);
2332
+
2333
+  text = text.replace(tableRgx, (rawTable) => {
2334
+    let i; const
2335
+      tableLines = rawTable.split('\n');
2336
+
2337
+    // strip wrong first and last column if wrapped tables are used
2338
+    for (i = 0; i < tableLines.length; ++i) {
2339
+      if (/^[ \t]{0,3}\|/.test(tableLines[i])) {
2340
+        tableLines[i] = tableLines[i].replace(/^[ \t]{0,3}\|/, '');
2341
+      }
2342
+      if (/\|[ \t]*$/.test(tableLines[i])) {
2343
+        tableLines[i] = tableLines[i].replace(/\|[ \t]*$/, '');
2344
+      }
2345
+    }
2346
+
2347
+    const rawHeaders = tableLines[0].split('|').map((s) => s.trim());
2348
+    const rawStyles = tableLines[1].split('|').map((s) => s.trim());
2349
+    const rawCells = [];
2350
+    const headers = [];
2351
+    const styles = [];
2352
+    const cells = [];
2353
+
2354
+    tableLines.shift();
2355
+    tableLines.shift();
2356
+
2357
+    for (i = 0; i < tableLines.length; ++i) {
2358
+      if (tableLines[i].trim() === '') {
2359
+        continue;
2360
+      }
2361
+      rawCells.push(
2362
+        tableLines[i]
2363
+          .split('|')
2364
+          .map((s) => s.trim()),
2365
+      );
2366
+    }
2367
+
2368
+    if (rawHeaders.length < rawStyles.length) {
2369
+      return rawTable;
2370
+    }
2371
+
2372
+    for (i = 0; i < rawStyles.length; ++i) {
2373
+      styles.push(parseStyles(rawStyles[i]));
2374
+    }
2375
+
2376
+    for (i = 0; i < rawHeaders.length; ++i) {
2377
+      if (showdown.helper.isUndefined(styles[i])) {
2378
+        styles[i] = '';
2379
+      }
2380
+      headers.push(parseHeaders(rawHeaders[i], styles[i]));
2381
+    }
2382
+
2383
+    for (i = 0; i < rawCells.length; ++i) {
2384
+      const row = [];
2385
+      for (let ii = 0; ii < headers.length; ++ii) {
2386
+        if (showdown.helper.isUndefined(rawCells[i][ii])) {
2387
+
2388
+        }
2389
+        row.push(parseCells(rawCells[i][ii], styles[ii]));
2390
+      }
2391
+      cells.push(row);
2392
+    }
2393
+
2394
+    return buildTable(headers, cells);
2395
+  });
2396
+
2397
+  text = globals.converter._dispatch('tables.after', text, options, globals);
2398
+
2399
+  return text;
2400
+});
2401
+
2402
+/**
2403
+ * Swap back in all the special characters we've hidden.
2404
+ */
2405
+showdown.subParser('unescapeSpecialChars', (text) => {
2406
+  text = text.replace(/~E(\d+)E/g, (wholeMatch, m1) => {
2407
+    const charCodeToReplace = parseInt(m1);
2408
+    return String.fromCharCode(charCodeToReplace);
2409
+  });
2410
+  return text;
2411
+});
2412
+module.exports = showdown;

+ 206 - 0
src/utils/wxParse/wxDiscode.js

@@ -0,0 +1,206 @@
1
+// HTML 支持的数学符号
2
+function strNumDiscode(str) {
3
+  str = str.replace(/&forall;/g, '∀');
4
+  str = str.replace(/&part;/g, '∂');
5
+  str = str.replace(/&exists;/g, '∃');
6
+  str = str.replace(/&empty;/g, '∅');
7
+  str = str.replace(/&nabla;/g, '∇');
8
+  str = str.replace(/&isin;/g, '∈');
9
+  str = str.replace(/&notin;/g, '∉');
10
+  str = str.replace(/&ni;/g, '∋');
11
+  str = str.replace(/&prod;/g, '∏');
12
+  str = str.replace(/&sum;/g, '∑');
13
+  str = str.replace(/&minus;/g, '−');
14
+  str = str.replace(/&lowast;/g, '∗');
15
+  str = str.replace(/&radic;/g, '√');
16
+  str = str.replace(/&prop;/g, '∝');
17
+  str = str.replace(/&infin;/g, '∞');
18
+  str = str.replace(/&ang;/g, '∠');
19
+  str = str.replace(/&and;/g, '∧');
20
+  str = str.replace(/&or;/g, '∨');
21
+  str = str.replace(/&cap;/g, '∩');
22
+  str = str.replace(/&cap;/g, '∪');
23
+  str = str.replace(/&int;/g, '∫');
24
+  str = str.replace(/&there4;/g, '∴');
25
+  str = str.replace(/&sim;/g, '∼');
26
+  str = str.replace(/&cong;/g, '≅');
27
+  str = str.replace(/&asymp;/g, '≈');
28
+  str = str.replace(/&ne;/g, '≠');
29
+  str = str.replace(/&le;/g, '≤');
30
+  str = str.replace(/&ge;/g, '≥');
31
+  str = str.replace(/&sub;/g, '⊂');
32
+  str = str.replace(/&sup;/g, '⊃');
33
+  str = str.replace(/&nsub;/g, '⊄');
34
+  str = str.replace(/&sube;/g, '⊆');
35
+  str = str.replace(/&supe;/g, '⊇');
36
+  str = str.replace(/&oplus;/g, '⊕');
37
+  str = str.replace(/&otimes;/g, '⊗');
38
+  str = str.replace(/&perp;/g, '⊥');
39
+  str = str.replace(/&sdot;/g, '⋅');
40
+  return str;
41
+}
42
+
43
+// HTML 支持的希腊字母
44
+function strGreeceDiscode(str) {
45
+  str = str.replace(/&Alpha;/g, 'Α');
46
+  str = str.replace(/&Beta;/g, 'Β');
47
+  str = str.replace(/&Gamma;/g, 'Γ');
48
+  str = str.replace(/&Delta;/g, 'Δ');
49
+  str = str.replace(/&Epsilon;/g, 'Ε');
50
+  str = str.replace(/&Zeta;/g, 'Ζ');
51
+  str = str.replace(/&Eta;/g, 'Η');
52
+  str = str.replace(/&Theta;/g, 'Θ');
53
+  str = str.replace(/&Iota;/g, 'Ι');
54
+  str = str.replace(/&Kappa;/g, 'Κ');
55
+  str = str.replace(/&Lambda;/g, 'Λ');
56
+  str = str.replace(/&Mu;/g, 'Μ');
57
+  str = str.replace(/&Nu;/g, 'Ν');
58
+  str = str.replace(/&Xi;/g, 'Ν');
59
+  str = str.replace(/&Omicron;/g, 'Ο');
60
+  str = str.replace(/&Pi;/g, 'Π');
61
+  str = str.replace(/&Rho;/g, 'Ρ');
62
+  str = str.replace(/&Sigma;/g, 'Σ');
63
+  str = str.replace(/&Tau;/g, 'Τ');
64
+  str = str.replace(/&Upsilon;/g, 'Υ');
65
+  str = str.replace(/&Phi;/g, 'Φ');
66
+  str = str.replace(/&Chi;/g, 'Χ');
67
+  str = str.replace(/&Psi;/g, 'Ψ');
68
+  str = str.replace(/&Omega;/g, 'Ω');
69
+
70
+  str = str.replace(/&alpha;/g, 'α');
71
+  str = str.replace(/&beta;/g, 'β');
72
+  str = str.replace(/&gamma;/g, 'γ');
73
+  str = str.replace(/&delta;/g, 'δ');
74
+  str = str.replace(/&epsilon;/g, 'ε');
75
+  str = str.replace(/&zeta;/g, 'ζ');
76
+  str = str.replace(/&eta;/g, 'η');
77
+  str = str.replace(/&theta;/g, 'θ');
78
+  str = str.replace(/&iota;/g, 'ι');
79
+  str = str.replace(/&kappa;/g, 'κ');
80
+  str = str.replace(/&lambda;/g, 'λ');
81
+  str = str.replace(/&mu;/g, 'μ');
82
+  str = str.replace(/&nu;/g, 'ν');
83
+  str = str.replace(/&xi;/g, 'ξ');
84
+  str = str.replace(/&omicron;/g, 'ο');
85
+  str = str.replace(/&pi;/g, 'π');
86
+  str = str.replace(/&rho;/g, 'ρ');
87
+  str = str.replace(/&sigmaf;/g, 'ς');
88
+  str = str.replace(/&sigma;/g, 'σ');
89
+  str = str.replace(/&tau;/g, 'τ');
90
+  str = str.replace(/&upsilon;/g, 'υ');
91
+  str = str.replace(/&phi;/g, 'φ');
92
+  str = str.replace(/&chi;/g, 'χ');
93
+  str = str.replace(/&psi;/g, 'ψ');
94
+  str = str.replace(/&omega;/g, 'ω');
95
+  str = str.replace(/&thetasym;/g, 'ϑ');
96
+  str = str.replace(/&upsih;/g, 'ϒ');
97
+  str = str.replace(/&piv;/g, 'ϖ');
98
+  str = str.replace(/&middot;/g, '·');
99
+  return str;
100
+}
101
+
102
+//
103
+
104
+function strcharacterDiscode(str) {
105
+  // 加入常用解析
106
+  str = str.replace(/&nbsp;/g, ' ');
107
+  str = str.replace(/&quot;/g, "'");
108
+  str = str.replace(/&amp;/g, '&');
109
+  // str = str.replace(/&lt;/g, '‹');
110
+  // str = str.replace(/&gt;/g, '›');
111
+
112
+  str = str.replace(/&lt;/g, '<');
113
+  str = str.replace(/&gt;/g, '>');
114
+  str = str.replace(/&#8226;/g, '•');
115
+
116
+  return str;
117
+}
118
+
119
+// HTML 支持的其他实体
120
+function strOtherDiscode(str) {
121
+  str = str.replace(/&OElig;/g, 'Œ');
122
+  str = str.replace(/&oelig;/g, 'œ');
123
+  str = str.replace(/&Scaron;/g, 'Š');
124
+  str = str.replace(/&scaron;/g, 'š');
125
+  str = str.replace(/&Yuml;/g, 'Ÿ');
126
+  str = str.replace(/&fnof;/g, 'ƒ');
127
+  str = str.replace(/&circ;/g, 'ˆ');
128
+  str = str.replace(/&tilde;/g, '˜');
129
+  str = str.replace(/&ensp;/g, '');
130
+  str = str.replace(/&emsp;/g, '');
131
+  str = str.replace(/&thinsp;/g, '');
132
+  str = str.replace(/&zwnj;/g, '');
133
+  str = str.replace(/&zwj;/g, '');
134
+  str = str.replace(/&lrm;/g, '');
135
+  str = str.replace(/&rlm;/g, '');
136
+  str = str.replace(/&ndash;/g, '–');
137
+  str = str.replace(/&mdash;/g, '—');
138
+  str = str.replace(/&lsquo;/g, '‘');
139
+  str = str.replace(/&rsquo;/g, '’');
140
+  str = str.replace(/&sbquo;/g, '‚');
141
+  str = str.replace(/&ldquo;/g, '“');
142
+  str = str.replace(/&rdquo;/g, '”');
143
+  str = str.replace(/&bdquo;/g, '„');
144
+  str = str.replace(/&dagger;/g, '†');
145
+  str = str.replace(/&Dagger;/g, '‡');
146
+  str = str.replace(/&bull;/g, '•');
147
+  str = str.replace(/&hellip;/g, '…');
148
+  str = str.replace(/&permil;/g, '‰');
149
+  str = str.replace(/&prime;/g, '′');
150
+  str = str.replace(/&Prime;/g, '″');
151
+  str = str.replace(/&lsaquo;/g, '‹');
152
+  str = str.replace(/&rsaquo;/g, '›');
153
+  str = str.replace(/&oline;/g, '‾');
154
+  str = str.replace(/&euro;/g, '€');
155
+  str = str.replace(/&trade;/g, '™');
156
+
157
+  str = str.replace(/&larr;/g, '←');
158
+  str = str.replace(/&uarr;/g, '↑');
159
+  str = str.replace(/&rarr;/g, '→');
160
+  str = str.replace(/&darr;/g, '↓');
161
+  str = str.replace(/&harr;/g, '↔');
162
+  str = str.replace(/&crarr;/g, '↵');
163
+  str = str.replace(/&lceil;/g, '⌈');
164
+  str = str.replace(/&rceil;/g, '⌉');
165
+
166
+  str = str.replace(/&lfloor;/g, '⌊');
167
+  str = str.replace(/&rfloor;/g, '⌋');
168
+  str = str.replace(/&loz;/g, '◊');
169
+  str = str.replace(/&spades;/g, '♠');
170
+  str = str.replace(/&clubs;/g, '♣');
171
+  str = str.replace(/&hearts;/g, '♥');
172
+
173
+  str = str.replace(/&diams;/g, '♦');
174
+  str = str.replace(/&#39;/g, '\'');
175
+  return str;
176
+}
177
+
178
+function strMoreDiscode(str) {
179
+  str = str.replace(/\r\n/g, '');
180
+  str = str.replace(/\n/g, '');
181
+
182
+  str = str.replace(/code/g, 'wxxxcode-style');
183
+  return str;
184
+}
185
+
186
+function strDiscode(str) {
187
+  str = strNumDiscode(str);
188
+  str = strGreeceDiscode(str);
189
+  str = strcharacterDiscode(str);
190
+  str = strOtherDiscode(str);
191
+  str = strMoreDiscode(str);
192
+  return str;
193
+}
194
+function urlToHttpUrl(url, rep) {
195
+  const patt1 = new RegExp('^//');
196
+  const result = patt1.test(url);
197
+  if (result) {
198
+    url = `${rep}:${url}`;
199
+  }
200
+  return url;
201
+}
202
+
203
+module.exports = {
204
+  strDiscode,
205
+  urlToHttpUrl,
206
+};

+ 159 - 0
src/utils/wxParse/wxParse.js

@@ -0,0 +1,159 @@
1
+/**
2
+ * author: Di (微信小程序开发工程师)
3
+ * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
4
+ *               垂直微信小程序开发交流社区
5
+ *
6
+ * github地址: https://github.com/icindy/wxParse
7
+ *
8
+ * for: 微信小程序富文本解析
9
+ * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
10
+ */
11
+
12
+/**
13
+ * utils函数引入
14
+ * */
15
+import showdown from './showdown.js';
16
+import HtmlToJson from './html2json.js';
17
+/**
18
+ * 配置及公有属性
19
+ * */
20
+let realWindowWidth = 0;
21
+let realWindowHeight = 0;
22
+wx.getSystemInfo({
23
+  success(res) {
24
+    realWindowWidth = res.windowWidth;
25
+    realWindowHeight = res.windowHeight;
26
+  },
27
+});
28
+/**
29
+ * 主函数入口区
30
+ * */
31
+function wxParse(bindName = 'wxParseData', type = 'html', data = '<div class="color:red;">数据不能为空</div>', target, imagePadding) {
32
+  const that = target;
33
+  let transData = {};// 存放转化后的数据
34
+  if (type == 'html') {
35
+    transData = HtmlToJson.html2json(data, bindName);
36
+    console.log(JSON.stringify(transData, ' ', ' '));
37
+  } else if (type == 'md' || type == 'markdown') {
38
+    const converter = new showdown.Converter();
39
+    const html = converter.makeHtml(data);
40
+    transData = HtmlToJson.html2json(html, bindName);
41
+    console.log(JSON.stringify(transData, ' ', ' '));
42
+  }
43
+  transData.view = {};
44
+  transData.view.imagePadding = 0;
45
+  if (typeof (imagePadding) !== 'undefined') {
46
+    transData.view.imagePadding = imagePadding;
47
+  }
48
+  const bindData = {};
49
+  bindData[bindName] = transData;
50
+  that.setData(bindData);
51
+  that.wxParseImgLoad = wxParseImgLoad;
52
+  that.wxParseImgTap = wxParseImgTap;
53
+}
54
+// 图片点击事件
55
+function wxParseImgTap(e) {
56
+  const that = this;
57
+  const nowImgUrl = e.target.dataset.src;
58
+  const tagFrom = e.target.dataset.from;
59
+  if (typeof (tagFrom) !== 'undefined' && tagFrom.length > 0) {
60
+    wx.previewImage({
61
+      current: nowImgUrl, // 当前显示图片的http链接
62
+      urls: that.data[tagFrom].imageUrls, // 需要预览的图片http链接列表
63
+    });
64
+  }
65
+}
66
+
67
+/**
68
+ * 图片视觉宽高计算函数区
69
+ * */
70
+function wxParseImgLoad(e) {
71
+  const that = this;
72
+  const tagFrom = e.target.dataset.from;
73
+  const { idx } = e.target.dataset;
74
+  if (typeof (tagFrom) !== 'undefined' && tagFrom.length > 0) {
75
+    calMoreImageInfo(e, idx, that, tagFrom);
76
+  }
77
+}
78
+// 假循环获取计算图片视觉最佳宽高
79
+function calMoreImageInfo(e, idx, that, bindName) {
80
+  const temData = that.data[bindName];
81
+  if (!temData || temData.images.length == 0) {
82
+    return;
83
+  }
84
+  const temImages = temData.images;
85
+  // 因为无法获取view宽度 需要自定义padding进行计算,稍后处理
86
+  const recal = wxAutoImageCal(e.detail.width, e.detail.height, that, bindName);
87
+  // temImages[idx].width = recal.imageWidth;
88
+  // temImages[idx].height = recal.imageheight;
89
+  // temData.images = temImages;
90
+  // var bindData = {};
91
+  // bindData[bindName] = temData;
92
+  // that.setData(bindData);
93
+  const { index } = temImages[idx];
94
+  let key = `${bindName}`;
95
+  for (const i of index.split('.')) key += `.nodes[${i}]`;
96
+  const keyW = `${key}.width`;
97
+  const keyH = `${key}.height`;
98
+  that.setData({
99
+    [keyW]: recal.imageWidth,
100
+    [keyH]: recal.imageheight,
101
+  });
102
+}
103
+
104
+// 计算视觉优先的图片宽高
105
+function wxAutoImageCal(originalWidth, originalHeight, that, bindName) {
106
+  // 获取图片的原始长宽
107
+  let windowWidth = 0; let
108
+    windowHeight = 0;
109
+  let autoWidth = 0; let
110
+    autoHeight = 0;
111
+  const results = {};
112
+  const padding = that.data[bindName].view.imagePadding;
113
+  windowWidth = realWindowWidth - 2 * padding;
114
+  windowHeight = realWindowHeight;
115
+  // 判断按照那种方式进行缩放
116
+  // console.log("windowWidth" + windowWidth);
117
+  if (originalWidth > windowWidth) { // 在图片width大于手机屏幕width时候
118
+    autoWidth = windowWidth;
119
+    // console.log("autoWidth" + autoWidth);
120
+    autoHeight = (autoWidth * originalHeight) / originalWidth;
121
+    // console.log("autoHeight" + autoHeight);
122
+    results.imageWidth = autoWidth;
123
+    results.imageheight = autoHeight;
124
+  } else { // 否则展示原来的数据
125
+    results.imageWidth = originalWidth;
126
+    results.imageheight = originalHeight;
127
+  }
128
+  return results;
129
+}
130
+
131
+function wxParseTemArray(temArrayName, bindNameReg, total, that) {
132
+  const array = [];
133
+  const temData = that.data;
134
+  let obj = null;
135
+  for (let i = 0; i < total; i++) {
136
+    const simArr = temData[bindNameReg + i].nodes;
137
+    array.push(simArr);
138
+  }
139
+
140
+  temArrayName = temArrayName || 'wxParseTemArray';
141
+  obj = JSON.parse(`{"${temArrayName}":""}`);
142
+  obj[temArrayName] = array;
143
+  that.setData(obj);
144
+}
145
+
146
+/**
147
+ * 配置emojis
148
+ *
149
+ */
150
+
151
+function emojisInit(reg = '', baseSrc = '/wxParse/emojis/', emojis) {
152
+  HtmlToJson.emojisInit(reg, baseSrc, emojis);
153
+}
154
+
155
+module.exports = {
156
+  wxParse,
157
+  wxParseTemArray,
158
+  emojisInit,
159
+};

+ 967 - 0
src/utils/wxParse/wxParse.wxml

@@ -0,0 +1,967 @@
1
+<!--**
2
+ * author: Di (微信小程序开发工程师)
3
+ * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
4
+ *               垂直微信小程序开发交流社区
5
+ * 
6
+ * github地址: https://github.com/icindy/wxParse
7
+ * 
8
+ * for: 微信小程序富文本解析
9
+ * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
10
+ */-->
11
+
12
+<!--基础元素-->
13
+<template name="wxParseVideo">
14
+  <!--增加video标签支持,并循环添加-->
15
+  <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
16
+    <video class="{{item.classStr}} wxParse-{{item.tag}}-video" src="{{item.attr.src}}"></video>
17
+  </view>
18
+</template>
19
+
20
+<template name="wxParseImg">
21
+  <image class="{{item.classStr}} wxParse-{{item.tag}}" data-from="{{item.from}}" data-src="{{item.attr.src}}" data-idx="{{item.imgIndex}}" src="{{item.attr.src}}" bindload="wxParseImgLoad" bindtap="wxParseImgTap" mode="widthFix" style="width:100%;"
22
+  />
23
+</template>
24
+
25
+<template name="WxEmojiView">
26
+  <view class="WxEmojiView wxParse-inline" style="{{item.styleStr}}">
27
+    <block wx:for="{{item.textArray}}" wx:key="">
28
+      <block class="{{item.text == '\\n' ? 'wxParse-hide':''}}" wx:if="{{item.node == 'text'}}">{{item.text}}</block>
29
+      <block wx:elif="{{item.node == 'element'}}">
30
+        <image class="wxEmoji" src="{{item.baseSrc}}{{item.text}}" />
31
+      </block>
32
+    </block>
33
+  </view>
34
+</template>
35
+
36
+<template name="WxParseBr">
37
+  <text>\n</text>
38
+</template>
39
+<!--入口模版-->
40
+
41
+<template name="wxParse">
42
+  <block wx:for="{{wxParseData}}" wx:key="">
43
+    <template is="wxParse0" data="{{item}}" />
44
+  </block>
45
+</template>
46
+
47
+
48
+<!--循环模版-->
49
+<template name="wxParse0">
50
+  <!--<template is="wxParse1" data="{{item}}" />-->
51
+  <!--判断是否是标签节点-->
52
+  <block wx:if="{{item.node == 'element'}}">
53
+    <block wx:if="{{item.tag == 'button'}}">
54
+      <button type="default" size="mini">
55
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
56
+          <template is="wxParse1" data="{{item}}" />
57
+        </block>
58
+      </button>
59
+    </block>
60
+    <!--li类型-->
61
+    <block wx:elif="{{item.tag == 'li'}}">
62
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
63
+        <view class="{{item.classStr}} wxParse-li-inner">
64
+          <view class="{{item.classStr}} wxParse-li-text">
65
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
66
+          </view>
67
+          <view class="{{item.classStr}} wxParse-li-text">
68
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
69
+              <template is="wxParse1" data="{{item}}" />
70
+            </block>
71
+          </view>
72
+        </view>
73
+      </view>
74
+    </block>
75
+
76
+    <!--video类型-->
77
+    <block wx:elif="{{item.tag == 'video'}}">
78
+      <template is="wxParseVideo" data="{{item}}" />
79
+    </block>
80
+
81
+    <!--img类型-->
82
+    <block wx:elif="{{item.tag == 'img'}}">
83
+      <template is="wxParseImg" data="{{item}}" />
84
+    </block>
85
+
86
+    <!--a类型-->
87
+    <block wx:elif="{{item.tag == 'a'}}">
88
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
89
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
90
+          <template is="wxParse1" data="{{item}}" />
91
+        </block>
92
+      </view>
93
+    </block>
94
+    <block wx:elif="{{item.tag == 'table'}}">
95
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
96
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
97
+          <template is="wxParse1" data="{{item}}" />
98
+        </block>
99
+      </view>
100
+    </block>
101
+
102
+    <block wx:elif="{{item.tag == 'br'}}">
103
+      <template is="WxParseBr"></template>
104
+    </block>
105
+    <!--其他块级标签-->
106
+    <block wx:elif="{{item.tagType == 'block'}}">
107
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
108
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
109
+          <template is="wxParse1" data="{{item}}" />
110
+        </block>
111
+      </view>
112
+    </block>
113
+
114
+    <!--内联标签-->
115
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
116
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
117
+        <template is="wxParse1" data="{{item}}" />
118
+      </block>
119
+    </view>
120
+
121
+  </block>
122
+
123
+  <!--判断是否是文本节点-->
124
+  <block wx:elif="{{item.node == 'text'}}">
125
+    <!--如果是,直接进行-->
126
+    <template is="WxEmojiView" data="{{item}}" />
127
+  </block>
128
+
129
+</template>
130
+
131
+
132
+
133
+<!--循环模版-->
134
+<template name="wxParse1">
135
+  <!--<template is="wxParse2" data="{{item}}" />-->
136
+  <!--判断是否是标签节点-->
137
+  <block wx:if="{{item.node == 'element'}}">
138
+    <block wx:if="{{item.tag == 'button'}}">
139
+      <button type="default" size="mini">
140
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
141
+          <template is="wxParse2" data="{{item}}" />
142
+        </block>
143
+      </button>
144
+    </block>
145
+    <!--li类型-->
146
+    <block wx:elif="{{item.tag == 'li'}}">
147
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
148
+        <view class="{{item.classStr}} wxParse-li-inner">
149
+          <view class="{{item.classStr}} wxParse-li-text">
150
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
151
+          </view>
152
+          <view class="{{item.classStr}} wxParse-li-text">
153
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
154
+              <template is="wxParse2" data="{{item}}" />
155
+            </block>
156
+          </view>
157
+        </view>
158
+      </view>
159
+    </block>
160
+
161
+    <!--video类型-->
162
+    <block wx:elif="{{item.tag == 'video'}}">
163
+      <template is="wxParseVideo" data="{{item}}" />
164
+    </block>
165
+
166
+    <!--img类型-->
167
+    <block wx:elif="{{item.tag == 'img'}}">
168
+      <template is="wxParseImg" data="{{item}}" />
169
+    </block>
170
+
171
+    <!--a类型-->
172
+    <block wx:elif="{{item.tag == 'a'}}">
173
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
174
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
175
+          <template is="wxParse2" data="{{item}}" />
176
+        </block>
177
+      </view>
178
+    </block>
179
+
180
+    <block wx:elif="{{item.tag == 'br'}}">
181
+      <template is="WxParseBr"></template>
182
+    </block>
183
+    <!--其他块级标签-->
184
+    <block wx:elif="{{item.tagType == 'block'}}">
185
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
186
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
187
+          <template is="wxParse2" data="{{item}}" />
188
+        </block>
189
+      </view>
190
+    </block>
191
+
192
+    <!--内联标签-->
193
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
194
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
195
+        <template is="wxParse2" data="{{item}}" />
196
+      </block>
197
+    </view>
198
+
199
+  </block>
200
+
201
+  <!--判断是否是文本节点-->
202
+  <block wx:elif="{{item.node == 'text'}}">
203
+    <!--如果是,直接进行-->
204
+    <template is="WxEmojiView" data="{{item}}" />
205
+  </block>
206
+
207
+</template>
208
+
209
+
210
+<!--循环模版-->
211
+<template name="wxParse2">
212
+  <!--<template is="wxParse3" data="{{item}}" />-->
213
+  <!--判断是否是标签节点-->
214
+  <block wx:if="{{item.node == 'element'}}">
215
+    <block wx:if="{{item.tag == 'button'}}">
216
+      <button type="default" size="mini">
217
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
218
+          <template is="wxParse3" data="{{item}}" />
219
+        </block>
220
+      </button>
221
+    </block>
222
+    <!--li类型-->
223
+    <block wx:elif="{{item.tag == 'li'}}">
224
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
225
+        <view class="{{item.classStr}} wxParse-li-inner">
226
+          <view class="{{item.classStr}} wxParse-li-text">
227
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
228
+          </view>
229
+          <view class="{{item.classStr}} wxParse-li-text">
230
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
231
+              <template is="wxParse3" data="{{item}}" />
232
+            </block>
233
+          </view>
234
+        </view>
235
+      </view>
236
+    </block>
237
+
238
+    <!--video类型-->
239
+    <block wx:elif="{{item.tag == 'video'}}">
240
+      <template is="wxParseVideo" data="{{item}}" />
241
+    </block>
242
+
243
+    <!--img类型-->
244
+    <block wx:elif="{{item.tag == 'img'}}">
245
+      <template is="wxParseImg" data="{{item}}" />
246
+    </block>
247
+
248
+    <!--a类型-->
249
+    <block wx:elif="{{item.tag == 'a'}}">
250
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
251
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
252
+          <template is="wxParse3" data="{{item}}" />
253
+        </block>
254
+      </view>
255
+    </block>
256
+
257
+    <block wx:elif="{{item.tag == 'br'}}">
258
+      <template is="WxParseBr"></template>
259
+    </block>
260
+    <!--其他块级标签-->
261
+    <block wx:elif="{{item.tagType == 'block'}}">
262
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
263
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
264
+          <template is="wxParse3" data="{{item}}" />
265
+        </block>
266
+      </view>
267
+    </block>
268
+
269
+    <!--内联标签-->
270
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
271
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
272
+        <template is="wxParse3" data="{{item}}" />
273
+      </block>
274
+    </view>
275
+
276
+  </block>
277
+
278
+  <!--判断是否是文本节点-->
279
+  <block wx:elif="{{item.node == 'text'}}">
280
+    <!--如果是,直接进行-->
281
+    <template is="WxEmojiView" data="{{item}}" />
282
+  </block>
283
+
284
+</template>
285
+
286
+<!--循环模版-->
287
+<template name="wxParse3">
288
+  <!--<template is="wxParse4" data="{{item}}" />-->
289
+  <!--判断是否是标签节点-->
290
+  <block wx:if="{{item.node == 'element'}}">
291
+    <block wx:if="{{item.tag == 'button'}}">
292
+      <button type="default" size="mini">
293
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
294
+          <template is="wxParse4" data="{{item}}" />
295
+        </block>
296
+      </button>
297
+    </block>
298
+    <!--li类型-->
299
+    <block wx:elif="{{item.tag == 'li'}}">
300
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
301
+        <view class="{{item.classStr}} wxParse-li-inner">
302
+          <view class="{{item.classStr}} wxParse-li-text">
303
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
304
+          </view>
305
+          <view class="{{item.classStr}} wxParse-li-text">
306
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
307
+              <template is="wxParse4" data="{{item}}" />
308
+            </block>
309
+          </view>
310
+        </view>
311
+      </view>
312
+    </block>
313
+
314
+    <!--video类型-->
315
+    <block wx:elif="{{item.tag == 'video'}}">
316
+      <template is="wxParseVideo" data="{{item}}" />
317
+    </block>
318
+
319
+    <!--img类型-->
320
+    <block wx:elif="{{item.tag == 'img'}}">
321
+      <template is="wxParseImg" data="{{item}}" />
322
+    </block>
323
+
324
+    <!--a类型-->
325
+    <block wx:elif="{{item.tag == 'a'}}">
326
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
327
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
328
+          <template is="wxParse4" data="{{item}}" />
329
+        </block>
330
+      </view>
331
+    </block>
332
+
333
+    <block wx:elif="{{item.tag == 'br'}}">
334
+      <template is="WxParseBr"></template>
335
+    </block>
336
+    <!--其他块级标签-->
337
+    <block wx:elif="{{item.tagType == 'block'}}">
338
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
339
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
340
+          <template is="wxParse4" data="{{item}}" />
341
+        </block>
342
+      </view>
343
+    </block>
344
+
345
+    <!--内联标签-->
346
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
347
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
348
+        <template is="wxParse4" data="{{item}}" />
349
+      </block>
350
+    </view>
351
+
352
+  </block>
353
+
354
+  <!--判断是否是文本节点-->
355
+  <block wx:elif="{{item.node == 'text'}}">
356
+    <!--如果是,直接进行-->
357
+    <template is="WxEmojiView" data="{{item}}" />
358
+  </block>
359
+
360
+</template>
361
+
362
+<!--循环模版-->
363
+<template name="wxParse4">
364
+  <!--<template is="wxParse5" data="{{item}}" />-->
365
+  <!--判断是否是标签节点-->
366
+  <block wx:if="{{item.node == 'element'}}">
367
+    <block wx:if="{{item.tag == 'button'}}">
368
+      <button type="default" size="mini">
369
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
370
+          <template is="wxParse5" data="{{item}}" />
371
+        </block>
372
+      </button>
373
+    </block>
374
+    <!--li类型-->
375
+    <block wx:elif="{{item.tag == 'li'}}">
376
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
377
+        <view class="{{item.classStr}} wxParse-li-inner">
378
+          <view class="{{item.classStr}} wxParse-li-text">
379
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
380
+          </view>
381
+          <view class="{{item.classStr}} wxParse-li-text">
382
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
383
+              <template is="wxParse5" data="{{item}}" />
384
+            </block>
385
+          </view>
386
+        </view>
387
+      </view>
388
+    </block>
389
+
390
+    <!--video类型-->
391
+    <block wx:elif="{{item.tag == 'video'}}">
392
+      <template is="wxParseVideo" data="{{item}}" />
393
+    </block>
394
+
395
+    <!--img类型-->
396
+    <block wx:elif="{{item.tag == 'img'}}">
397
+      <template is="wxParseImg" data="{{item}}" />
398
+    </block>
399
+
400
+    <!--a类型-->
401
+    <block wx:elif="{{item.tag == 'a'}}">
402
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
403
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
404
+          <template is="wxParse5" data="{{item}}" />
405
+        </block>
406
+      </view>
407
+    </block>
408
+
409
+    <block wx:elif="{{item.tag == 'br'}}">
410
+      <template is="WxParseBr"></template>
411
+    </block>
412
+    <!--其他块级标签-->
413
+    <block wx:elif="{{item.tagType == 'block'}}">
414
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
415
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
416
+          <template is="wxParse5" data="{{item}}" />
417
+        </block>
418
+      </view>
419
+    </block>
420
+
421
+    <!--内联标签-->
422
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
423
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
424
+        <template is="wxParse5" data="{{item}}" />
425
+      </block>
426
+    </view>
427
+
428
+  </block>
429
+
430
+  <!--判断是否是文本节点-->
431
+  <block wx:elif="{{item.node == 'text'}}">
432
+    <!--如果是,直接进行-->
433
+    <template is="WxEmojiView" data="{{item}}" />
434
+  </block>
435
+
436
+</template>
437
+
438
+<!--循环模版-->
439
+<template name="wxParse5">
440
+  <!--<template is="wxParse6" data="{{item}}" />-->
441
+  <!--判断是否是标签节点-->
442
+  <block wx:if="{{item.node == 'element'}}">
443
+    <block wx:if="{{item.tag == 'button'}}">
444
+      <button type="default" size="mini">
445
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
446
+          <template is="wxParse6" data="{{item}}" />
447
+        </block>
448
+      </button>
449
+    </block>
450
+    <!--li类型-->
451
+    <block wx:elif="{{item.tag == 'li'}}">
452
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
453
+        <view class="{{item.classStr}} wxParse-li-inner">
454
+          <view class="{{item.classStr}} wxParse-li-text">
455
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
456
+          </view>
457
+          <view class="{{item.classStr}} wxParse-li-text">
458
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
459
+              <template is="wxParse6" data="{{item}}" />
460
+            </block>
461
+          </view>
462
+        </view>
463
+      </view>
464
+    </block>
465
+
466
+    <!--video类型-->
467
+    <block wx:elif="{{item.tag == 'video'}}">
468
+      <template is="wxParseVideo" data="{{item}}" />
469
+    </block>
470
+
471
+    <!--img类型-->
472
+    <block wx:elif="{{item.tag == 'img'}}">
473
+      <template is="wxParseImg" data="{{item}}" />
474
+    </block>
475
+
476
+    <!--a类型-->
477
+    <block wx:elif="{{item.tag == 'a'}}">
478
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
479
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
480
+          <template is="wxParse6" data="{{item}}" />
481
+        </block>
482
+      </view>
483
+    </block>
484
+
485
+    <block wx:elif="{{item.tag == 'br'}}">
486
+      <template is="WxParseBr"></template>
487
+    </block>
488
+    <!--其他块级标签-->
489
+    <block wx:elif="{{item.tagType == 'block'}}">
490
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
491
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
492
+          <template is="wxParse6" data="{{item}}" />
493
+        </block>
494
+      </view>
495
+    </block>
496
+
497
+    <!--内联标签-->
498
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
499
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
500
+        <template is="wxParse6" data="{{item}}" />
501
+      </block>
502
+    </view>
503
+
504
+  </block>
505
+
506
+  <!--判断是否是文本节点-->
507
+  <block wx:elif="{{item.node == 'text'}}">
508
+    <!--如果是,直接进行-->
509
+    <template is="WxEmojiView" data="{{item}}" />
510
+  </block>
511
+
512
+</template>
513
+
514
+<!--循环模版-->
515
+<template name="wxParse6">
516
+  <!--<template is="wxParse7" data="{{item}}" />-->
517
+  <!--判断是否是标签节点-->
518
+  <block wx:if="{{item.node == 'element'}}">
519
+    <block wx:if="{{item.tag == 'button'}}">
520
+      <button type="default" size="mini">
521
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
522
+          <template is="wxParse7" data="{{item}}" />
523
+        </block>
524
+      </button>
525
+    </block>
526
+    <!--li类型-->
527
+    <block wx:elif="{{item.tag == 'li'}}">
528
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
529
+        <view class="{{item.classStr}} wxParse-li-inner">
530
+          <view class="{{item.classStr}} wxParse-li-text">
531
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
532
+          </view>
533
+          <view class="{{item.classStr}} wxParse-li-text">
534
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
535
+              <template is="wxParse7" data="{{item}}" />
536
+            </block>
537
+          </view>
538
+        </view>
539
+      </view>
540
+    </block>
541
+
542
+    <!--video类型-->
543
+    <block wx:elif="{{item.tag == 'video'}}">
544
+      <template is="wxParseVideo" data="{{item}}" />
545
+    </block>
546
+
547
+    <!--img类型-->
548
+    <block wx:elif="{{item.tag == 'img'}}">
549
+      <template is="wxParseImg" data="{{item}}" />
550
+    </block>
551
+
552
+    <!--a类型-->
553
+    <block wx:elif="{{item.tag == 'a'}}">
554
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
555
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
556
+          <template is="wxParse7" data="{{item}}" />
557
+        </block>
558
+      </view>
559
+    </block>
560
+
561
+    <block wx:elif="{{item.tag == 'br'}}">
562
+      <template is="WxParseBr"></template>
563
+    </block>
564
+    <!--其他块级标签-->
565
+    <block wx:elif="{{item.tagType == 'block'}}">
566
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
567
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
568
+          <template is="wxParse7" data="{{item}}" />
569
+        </block>
570
+      </view>
571
+    </block>
572
+
573
+    <!--内联标签-->
574
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
575
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
576
+        <template is="wxParse7" data="{{item}}" />
577
+      </block>
578
+    </view>
579
+
580
+  </block>
581
+
582
+  <!--判断是否是文本节点-->
583
+  <block wx:elif="{{item.node == 'text'}}">
584
+    <!--如果是,直接进行-->
585
+    <template is="WxEmojiView" data="{{item}}" />
586
+  </block>
587
+
588
+</template>
589
+<!--循环模版-->
590
+<template name="wxParse7">
591
+  <!--<template is="wxParse8" data="{{item}}" />-->
592
+  <!--判断是否是标签节点-->
593
+  <block wx:if="{{item.node == 'element'}}">
594
+    <block wx:if="{{item.tag == 'button'}}">
595
+      <button type="default" size="mini">
596
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
597
+          <template is="wxParse8" data="{{item}}" />
598
+        </block>
599
+      </button>
600
+    </block>
601
+    <!--li类型-->
602
+    <block wx:elif="{{item.tag == 'li'}}">
603
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
604
+        <view class="{{item.classStr}} wxParse-li-inner">
605
+          <view class="{{item.classStr}} wxParse-li-text">
606
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
607
+          </view>
608
+          <view class="{{item.classStr}} wxParse-li-text">
609
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
610
+              <template is="wxParse8" data="{{item}}" />
611
+            </block>
612
+          </view>
613
+        </view>
614
+      </view>
615
+    </block>
616
+
617
+    <!--video类型-->
618
+    <block wx:elif="{{item.tag == 'video'}}">
619
+      <template is="wxParseVideo" data="{{item}}" />
620
+    </block>
621
+
622
+    <!--img类型-->
623
+    <block wx:elif="{{item.tag == 'img'}}">
624
+      <template is="wxParseImg" data="{{item}}" />
625
+    </block>
626
+
627
+    <!--a类型-->
628
+    <block wx:elif="{{item.tag == 'a'}}">
629
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
630
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
631
+          <template is="wxParse8" data="{{item}}" />
632
+        </block>
633
+      </view>
634
+    </block>
635
+
636
+    <block wx:elif="{{item.tag == 'br'}}">
637
+      <template is="WxParseBr"></template>
638
+    </block>
639
+    <!--其他块级标签-->
640
+    <block wx:elif="{{item.tagType == 'block'}}">
641
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
642
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
643
+          <template is="wxParse8" data="{{item}}" />
644
+        </block>
645
+      </view>
646
+    </block>
647
+
648
+    <!--内联标签-->
649
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
650
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
651
+        <template is="wxParse8" data="{{item}}" />
652
+      </block>
653
+    </view>
654
+
655
+  </block>
656
+
657
+  <!--判断是否是文本节点-->
658
+  <block wx:elif="{{item.node == 'text'}}">
659
+    <!--如果是,直接进行-->
660
+    <template is="WxEmojiView" data="{{item}}" />
661
+  </block>
662
+
663
+</template>
664
+
665
+<!--循环模版-->
666
+<template name="wxParse8">
667
+  <!--<template is="wxParse9" data="{{item}}" />-->
668
+  <!--判断是否是标签节点-->
669
+  <block wx:if="{{item.node == 'element'}}">
670
+    <block wx:if="{{item.tag == 'button'}}">
671
+      <button type="default" size="mini">
672
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
673
+          <template is="wxParse9" data="{{item}}" />
674
+        </block>
675
+      </button>
676
+    </block>
677
+    <!--li类型-->
678
+    <block wx:elif="{{item.tag == 'li'}}">
679
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
680
+        <view class="{{item.classStr}} wxParse-li-inner">
681
+          <view class="{{item.classStr}} wxParse-li-text">
682
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
683
+          </view>
684
+          <view class="{{item.classStr}} wxParse-li-text">
685
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
686
+              <template is="wxParse9" data="{{item}}" />
687
+            </block>
688
+          </view>
689
+        </view>
690
+      </view>
691
+    </block>
692
+
693
+    <!--video类型-->
694
+    <block wx:elif="{{item.tag == 'video'}}">
695
+      <template is="wxParseVideo" data="{{item}}" />
696
+    </block>
697
+
698
+    <!--img类型-->
699
+    <block wx:elif="{{item.tag == 'img'}}">
700
+      <template is="wxParseImg" data="{{item}}" />
701
+    </block>
702
+
703
+    <!--a类型-->
704
+    <block wx:elif="{{item.tag == 'a'}}">
705
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
706
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
707
+          <template is="wxParse9" data="{{item}}" />
708
+        </block>
709
+      </view>
710
+    </block>
711
+
712
+    <block wx:elif="{{item.tag == 'br'}}">
713
+      <template is="WxParseBr"></template>
714
+    </block>
715
+    <!--其他块级标签-->
716
+    <block wx:elif="{{item.tagType == 'block'}}">
717
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
718
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
719
+          <template is="wxParse9" data="{{item}}" />
720
+        </block>
721
+      </view>
722
+    </block>
723
+
724
+    <!--内联标签-->
725
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
726
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
727
+        <template is="wxParse9" data="{{item}}" />
728
+      </block>
729
+    </view>
730
+
731
+  </block>
732
+
733
+  <!--判断是否是文本节点-->
734
+  <block wx:elif="{{item.node == 'text'}}">
735
+    <!--如果是,直接进行-->
736
+    <template is="WxEmojiView" data="{{item}}" />
737
+  </block>
738
+
739
+</template>
740
+
741
+<!--循环模版-->
742
+<template name="wxParse9">
743
+  <!--<template is="wxParse10" data="{{item}}" />-->
744
+  <!--判断是否是标签节点-->
745
+  <block wx:if="{{item.node == 'element'}}">
746
+    <block wx:if="{{item.tag == 'button'}}">
747
+      <button type="default" size="mini">
748
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
749
+          <template is="wxParse10" data="{{item}}" />
750
+        </block>
751
+      </button>
752
+    </block>
753
+    <!--li类型-->
754
+    <block wx:elif="{{item.tag == 'li'}}">
755
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
756
+        <view class="{{item.classStr}} wxParse-li-inner">
757
+          <view class="{{item.classStr}} wxParse-li-text">
758
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
759
+          </view>
760
+          <view class="{{item.classStr}} wxParse-li-text">
761
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
762
+              <template is="wxParse10" data="{{item}}" />
763
+            </block>
764
+          </view>
765
+        </view>
766
+      </view>
767
+    </block>
768
+
769
+    <!--video类型-->
770
+    <block wx:elif="{{item.tag == 'video'}}">
771
+      <template is="wxParseVideo" data="{{item}}" />
772
+    </block>
773
+
774
+    <!--img类型-->
775
+    <block wx:elif="{{item.tag == 'img'}}">
776
+      <template is="wxParseImg" data="{{item}}" />
777
+    </block>
778
+
779
+    <!--a类型-->
780
+    <block wx:elif="{{item.tag == 'a'}}">
781
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
782
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
783
+          <template is="wxParse10" data="{{item}}" />
784
+        </block>
785
+      </view>
786
+    </block>
787
+
788
+    <block wx:elif="{{item.tag == 'br'}}">
789
+      <template is="WxParseBr"></template>
790
+    </block>
791
+    <!--其他块级标签-->
792
+    <block wx:elif="{{item.tagType == 'block'}}">
793
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
794
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
795
+          <template is="wxParse10" data="{{item}}" />
796
+        </block>
797
+      </view>
798
+    </block>
799
+
800
+    <!--内联标签-->
801
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
802
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
803
+        <template is="wxParse10" data="{{item}}" />
804
+      </block>
805
+    </view>
806
+
807
+  </block>
808
+
809
+  <!--判断是否是文本节点-->
810
+  <block wx:elif="{{item.node == 'text'}}">
811
+    <!--如果是,直接进行-->
812
+    <template is="WxEmojiView" data="{{item}}" />
813
+  </block>
814
+
815
+</template>
816
+
817
+<!--循环模版-->
818
+<template name="wxParse10">
819
+  <!--<template is="wxParse11" data="{{item}}" />-->
820
+  <!--判断是否是标签节点-->
821
+  <block wx:if="{{item.node == 'element'}}">
822
+    <block wx:if="{{item.tag == 'button'}}">
823
+      <button type="default" size="mini">
824
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
825
+          <template is="wxParse11" data="{{item}}" />
826
+        </block>
827
+      </button>
828
+    </block>
829
+    <!--li类型-->
830
+    <block wx:elif="{{item.tag == 'li'}}">
831
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
832
+        <view class="{{item.classStr}} wxParse-li-inner">
833
+          <view class="{{item.classStr}} wxParse-li-text">
834
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
835
+          </view>
836
+          <view class="{{item.classStr}} wxParse-li-text">
837
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
838
+              <template is="wxParse11" data="{{item}}" />
839
+            </block>
840
+          </view>
841
+        </view>
842
+      </view>
843
+    </block>
844
+
845
+    <!--video类型-->
846
+    <block wx:elif="{{item.tag == 'video'}}">
847
+      <template is="wxParseVideo" data="{{item}}" />
848
+    </block>
849
+
850
+    <!--img类型-->
851
+    <block wx:elif="{{item.tag == 'img'}}">
852
+      <template is="wxParseImg" data="{{item}}" />
853
+    </block>
854
+
855
+    <!--a类型-->
856
+    <block wx:elif="{{item.tag == 'a'}}">
857
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
858
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
859
+          <template is="wxParse11" data="{{item}}" />
860
+        </block>
861
+      </view>
862
+    </block>
863
+
864
+    <block wx:elif="{{item.tag == 'br'}}">
865
+      <template is="WxParseBr"></template>
866
+    </block>
867
+    <!--其他块级标签-->
868
+    <block wx:elif="{{item.tagType == 'block'}}">
869
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
870
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
871
+          <template is="wxParse11" data="{{item}}" />
872
+        </block>
873
+      </view>
874
+    </block>
875
+
876
+    <!--内联标签-->
877
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
878
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
879
+        <template is="wxParse11" data="{{item}}" />
880
+      </block>
881
+    </view>
882
+
883
+  </block>
884
+
885
+  <!--判断是否是文本节点-->
886
+  <block wx:elif="{{item.node == 'text'}}">
887
+    <!--如果是,直接进行-->
888
+    <template is="WxEmojiView" data="{{item}}" />
889
+  </block>
890
+
891
+</template>
892
+
893
+<!--循环模版-->
894
+<template name="wxParse11">
895
+  <!--<template is="wxParse12" data="{{item}}" />-->
896
+  <!--判断是否是标签节点-->
897
+  <block wx:if="{{item.node == 'element'}}">
898
+    <block wx:if="{{item.tag == 'button'}}">
899
+      <button type="default" size="mini">
900
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
901
+          <template is="wxParse12" data="{{item}}" />
902
+        </block>
903
+      </button>
904
+    </block>
905
+    <!--li类型-->
906
+    <block wx:elif="{{item.tag == 'li'}}">
907
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
908
+        <view class="{{item.classStr}} wxParse-li-inner">
909
+          <view class="{{item.classStr}} wxParse-li-text">
910
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
911
+          </view>
912
+          <view class="{{item.classStr}} wxParse-li-text">
913
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
914
+              <template is="wxParse12" data="{{item}}" />
915
+            </block>
916
+          </view>
917
+        </view>
918
+      </view>
919
+    </block>
920
+
921
+    <!--video类型-->
922
+    <block wx:elif="{{item.tag == 'video'}}">
923
+      <template is="wxParseVideo" data="{{item}}" />
924
+    </block>
925
+
926
+    <!--img类型-->
927
+    <block wx:elif="{{item.tag == 'img'}}">
928
+      <template is="wxParseImg" data="{{item}}" />
929
+    </block>
930
+
931
+    <!--a类型-->
932
+    <block wx:elif="{{item.tag == 'a'}}">
933
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
934
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
935
+          <template is="wxParse12" data="{{item}}" />
936
+        </block>
937
+      </view>
938
+    </block>
939
+
940
+    <block wx:elif="{{item.tag == 'br'}}">
941
+      <template is="WxParseBr"></template>
942
+    </block>
943
+    <!--其他块级标签-->
944
+    <block wx:elif="{{item.tagType == 'block'}}">
945
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
946
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
947
+          <template is="wxParse12" data="{{item}}" />
948
+        </block>
949
+      </view>
950
+    </block>
951
+
952
+    <!--内联标签-->
953
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
954
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
955
+        <template is="wxParse12" data="{{item}}" />
956
+      </block>
957
+    </view>
958
+
959
+  </block>
960
+
961
+  <!--判断是否是文本节点-->
962
+  <block wx:elif="{{item.node == 'text'}}">
963
+    <!--如果是,直接进行-->
964
+    <template is="WxEmojiView" data="{{item}}" />
965
+  </block>
966
+
967
+</template>

+ 270 - 0
src/utils/wxParse/wxParse.wxss

@@ -0,0 +1,270 @@
1
+/**
2
+ * author: Di (微信小程序开发工程师)
3
+ * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
4
+ *               垂直微信小程序开发交流社区
5
+ * 
6
+ * github地址: https://github.com/icindy/wxParse
7
+ * 
8
+ * for: 微信小程序富文本解析
9
+ * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
10
+ */
11
+
12
+.wxParse {
13
+  margin: 0 5px;
14
+  font-family: Helvetica, sans-serif;
15
+  font-size: 28rpx;
16
+  color: #666;
17
+  line-height: 1.8;
18
+}
19
+view {
20
+  word-break: break-all;
21
+}
22
+.wxParse-inline {
23
+  display: inline;
24
+  margin: 0;
25
+  padding: 0;
26
+}
27
+/*//标题 */
28
+.wxParse-div {
29
+  margin: 0;
30
+  padding: 0;
31
+}
32
+.wxParse-h1 {
33
+  font-size: 2em;
34
+  margin: 0.67em 0;
35
+}
36
+.wxParse-h2 {
37
+  font-size: 1.5em;
38
+  margin: 0.75em 0;
39
+}
40
+.wxParse-h3 {
41
+  font-size: 1.17em;
42
+  margin: 0.83em 0;
43
+}
44
+.wxParse-h4 {
45
+  margin: 1.12em 0;
46
+}
47
+.wxParse-h5 {
48
+  font-size: 0.83em;
49
+  margin: 1.5em 0;
50
+}
51
+.wxParse-h6 {
52
+  font-size: 0.75em;
53
+  margin: 1.67em 0;
54
+}
55
+
56
+.wxParse-h1 {
57
+  font-size: 18px;
58
+  font-weight: 400;
59
+  margin-bottom: 0.9em;
60
+}
61
+.wxParse-h2 {
62
+  font-size: 16px;
63
+  font-weight: 400;
64
+  margin-bottom: 0.34em;
65
+}
66
+.wxParse-h3 {
67
+  font-weight: 400;
68
+  font-size: 15px;
69
+  margin-bottom: 0.34em;
70
+}
71
+.wxParse-h4 {
72
+  font-weight: 400;
73
+  font-size: 14px;
74
+  margin-bottom: 0.24em;
75
+}
76
+.wxParse-h5 {
77
+  font-weight: 400;
78
+  font-size: 13px;
79
+  margin-bottom: 0.14em;
80
+}
81
+.wxParse-h6 {
82
+  font-weight: 400;
83
+  font-size: 12px;
84
+  margin-bottom: 0.04em;
85
+}
86
+
87
+.wxParse-h1,
88
+.wxParse-h2,
89
+.wxParse-h3,
90
+.wxParse-h4,
91
+.wxParse-h5,
92
+.wxParse-h6,
93
+.wxParse-b,
94
+.wxParse-strong {
95
+  font-weight: bolder;
96
+}
97
+
98
+.wxParse-i,
99
+.wxParse-cite,
100
+.wxParse-em,
101
+.wxParse-var,
102
+.wxParse-address {
103
+  font-style: italic;
104
+}
105
+.wxParse-pre,
106
+.wxParse-tt,
107
+.wxParse-code,
108
+.wxParse-kbd,
109
+.wxParse-samp {
110
+  font-family: monospace;
111
+}
112
+.wxParse-pre {
113
+  white-space: pre;
114
+}
115
+.wxParse-big {
116
+  font-size: 1.17em;
117
+}
118
+.wxParse-small,
119
+.wxParse-sub,
120
+.wxParse-sup {
121
+  font-size: 0.83em;
122
+}
123
+.wxParse-sub {
124
+  vertical-align: sub;
125
+}
126
+.wxParse-sup {
127
+  vertical-align: super;
128
+}
129
+.wxParse-s,
130
+.wxParse-strike,
131
+.wxParse-del {
132
+  text-decoration: line-through;
133
+}
134
+/*wxparse-自定义个性化的css样式*/
135
+/*增加video的css样式*/
136
+.wxParse-strong,
137
+.wxParse-s {
138
+  display: inline;
139
+}
140
+.wxParse-a {
141
+  color: #576b95;
142
+  text-decoration: underline;
143
+  word-break: break-all;
144
+  overflow: auto;
145
+}
146
+
147
+.wxParse-a-hover {
148
+  opacity: 0.8;
149
+}
150
+
151
+.wxParse-video {
152
+  text-align: center;
153
+  margin: 10px 0;
154
+}
155
+
156
+.wxParse-video-video {
157
+  width: 100%;
158
+}
159
+
160
+.wxParse-img {
161
+  /*background-color: #efefef;*/
162
+  overflow: hidden;
163
+}
164
+
165
+.wxParse-blockquote {
166
+  margin: 0;
167
+  padding: 10px 0 10px 5px;
168
+  font-family: Courier, Calibri, '宋体';
169
+  background: #f5f5f5;
170
+  border-left: 3px solid #dbdbdb;
171
+}
172
+
173
+.wxParse-code,
174
+.wxParse-wxxxcode-style {
175
+  display: inline;
176
+  background: #f5f5f5;
177
+}
178
+.wxParse-ul {
179
+  margin: 20rpx 10rpx;
180
+}
181
+
182
+.wxParse-li,
183
+.wxParse-li-inner {
184
+  display: flex;
185
+  align-items: baseline;
186
+}
187
+.wxParse-li-text {
188
+  align-items: center;
189
+}
190
+
191
+.wxParse-li-circle {
192
+  display: inline-flex;
193
+  width: 6px;
194
+  height: 6px;
195
+  border-radius: 3px;
196
+  background-color: #333;
197
+  margin-right: 5px;
198
+}
199
+
200
+.wxParse-li-square {
201
+  display: inline-flex;
202
+  width: 10rpx;
203
+  height: 10rpx;
204
+  background-color: #333;
205
+  margin-right: 5px;
206
+}
207
+.wxParse-li-ring {
208
+  display: inline-flex;
209
+  width: 10rpx;
210
+  height: 10rpx;
211
+  border: 2rpx solid #333;
212
+  border-radius: 50%;
213
+  background-color: #fff;
214
+  margin-right: 5px;
215
+}
216
+
217
+/*.wxParse-table{
218
+    width: 100%;
219
+    height: 400px;
220
+}
221
+.wxParse-thead,.wxParse-tfoot,.wxParse-tr{
222
+    display: flex;
223
+    flex-direction: row;
224
+}
225
+.wxParse-th,.wxParse-td{
226
+    display: flex;
227
+    width: 580px;
228
+    overflow: auto;
229
+}*/
230
+
231
+.wxParse-u {
232
+  text-decoration: underline;
233
+}
234
+.wxParse-hide {
235
+  display: none;
236
+}
237
+.WxEmojiView {
238
+  align-items: center;
239
+}
240
+.wxEmoji {
241
+  width: 16px;
242
+  height: 16px;
243
+}
244
+.wxParse-tr {
245
+  display: flex;
246
+  border-right: 1px solid #e0e0e0;
247
+  border-bottom: 1px solid #e0e0e0;
248
+  border-top: 1px solid #e0e0e0;
249
+}
250
+.wxParse-th,
251
+.wxParse-td {
252
+  flex: 1;
253
+  padding: 5px;
254
+  font-size: 28rpx;
255
+  border-left: 1px solid #e0e0e0;
256
+  word-break: break-all;
257
+}
258
+.wxParse-td:last {
259
+  border-top: 1px solid #e0e0e0;
260
+}
261
+.wxParse-th {
262
+  background: #f0f0f0;
263
+  border-top: 1px solid #e0e0e0;
264
+}
265
+.wxParse-del {
266
+  display: inline;
267
+}
268
+.wxParse-figure {
269
+  overflow: hidden;
270
+}

+ 10 - 0
src/wxs/stringFilter.wxs

@@ -0,0 +1,10 @@
1
+var filter = function (text) {
2
+  if (text) {
3
+    var pattern = '\\\\n';
4
+    var target = '\n';
5
+    var reg = getRegExp(pattern, 'g');
6
+    return text.replace(reg, target);
7
+  }
8
+};
9
+
10
+module.exports.filter = filter;

pai2 - Gogs: Go Git Service

拍爱

admin.py 1.2KB

    # -*- coding: utf-8 -*- from django.contrib import admin from message.models import SystemMessageDeleteInfo, SystemMessageInfo, SystemMessageReadInfo, UserMessageInfo class UserMessageInfoAdmin(admin.ModelAdmin): list_display = ('from_uid', 'from_nickname', 'to_uid', 'group_id', 'photo_id', 'msg_type', 'read', 'status', 'created_at', 'updated_at') list_filter = ('msg_type', 'read', 'status') class SystemMessageInfoAdmin(admin.ModelAdmin): list_display = ('title', 'content', 'url', 'src', 'status', 'created_at', 'updated_at') list_filter = ('src', 'status') class SystemMessageReadInfoAdmin(admin.ModelAdmin): list_display = ('user_id', 'msg_id', 'status', 'created_at', 'updated_at') list_filter = ('status', ) class SystemMessageDeleteInfoAdmin(admin.ModelAdmin): list_display = ('user_id', 'msg_id', 'status', 'created_at', 'updated_at') list_filter = ('status', ) admin.site.register(UserMessageInfo, UserMessageInfoAdmin) admin.site.register(SystemMessageInfo, SystemMessageInfoAdmin) admin.site.register(SystemMessageReadInfo, SystemMessageReadInfoAdmin) admin.site.register(SystemMessageDeleteInfo, SystemMessageDeleteInfoAdmin)