html2md.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876
  1. (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.toMarkdown = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  2. /*
  3. * to-markdown - https://github.com/LCTT/LCTT-Helper/blob/master/js/to-markdown.js
  4. *
  5. * Copyright 2011+, Dom Christie
  6. * Licenced under the MIT licence
  7. *
  8. */
  9. 'use strict'
  10. var toMarkdown
  11. var converters
  12. var mdConverters = require('./lib/md-converters')
  13. var gfmConverters = require('./lib/gfm-converters')
  14. var HtmlParser = require('./lib/html-parser')
  15. var collapse = require('collapse-whitespace')
  16. /*
  17. * Utilities
  18. */
  19. var blocks = ['address', 'article', 'aside', 'audio', 'blockquote', 'body',
  20. 'canvas', 'center', 'dd', 'dir', 'div', 'dl', 'dt', 'fieldset', 'figcaption',
  21. 'figure', 'footer', 'form', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h7',
  22. 'header', 'hgroup', 'hr', 'html', 'isindex', 'li', 'main', 'menu', 'nav',
  23. 'noframes', 'noscript', 'ol', 'output', 'p', 'pre', 'section', 'table',
  24. 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'ul'
  25. ]
  26. function isBlock (node) {
  27. return blocks.indexOf(node.nodeName.toLowerCase()) !== -1
  28. }
  29. var voids = [
  30. 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input',
  31. 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'
  32. ]
  33. function isVoid (node) {
  34. return voids.indexOf(node.nodeName.toLowerCase()) !== -1
  35. }
  36. function htmlToDom (string) {
  37. var tree = new HtmlParser().parseFromString(string, 'text/html')
  38. collapse(tree.documentElement, isBlock)
  39. return tree
  40. }
  41. /*
  42. * Flattens DOM tree into single array
  43. */
  44. function bfsOrder (node) {
  45. var inqueue = [node]
  46. var outqueue = []
  47. var elem
  48. var children
  49. var i
  50. while (inqueue.length > 0) {
  51. elem = inqueue.shift()
  52. outqueue.push(elem)
  53. children = elem.childNodes
  54. for (i = children.length - 1; i >= 0; i--) {
  55. if (children[i].nodeType === 1) inqueue.push(children[i])
  56. }
  57. }
  58. outqueue.shift()
  59. return outqueue
  60. }
  61. /*
  62. * Contructs a Markdown string of replacement text for a given node
  63. */
  64. function getContent (node) {
  65. var text = ''
  66. for (var i = 0; i < node.childNodes.length; i++) {
  67. if (node.childNodes[i].nodeType === 1) {
  68. text += node.childNodes[i]._replacement
  69. } else if (node.childNodes[i].nodeType === 3) {
  70. text += node.childNodes[i].data
  71. } else continue
  72. }
  73. return text
  74. }
  75. /*
  76. * Returns the HTML string of an element with its contents converted
  77. */
  78. function outer (node, content) {
  79. return node.cloneNode(false).outerHTML.replace('><', '>' + content + '<')
  80. }
  81. function canConvert (node, filter) {
  82. if (typeof filter === 'string') {
  83. return filter === node.nodeName.toLowerCase()
  84. }
  85. if (Array.isArray(filter)) {
  86. return filter.indexOf(node.nodeName.toLowerCase()) !== -1
  87. } else if (typeof filter === 'function') {
  88. return filter.call(toMarkdown, node)
  89. } else {
  90. throw new TypeError('`filter` needs to be a string, array, or function')
  91. }
  92. }
  93. function isFlankedByWhitespace (side, node) {
  94. var sibling
  95. var regExp
  96. var isFlanked
  97. if (side === 'left') {
  98. sibling = node.previousSibling
  99. regExp = / $/
  100. } else {
  101. sibling = node.nextSibling
  102. regExp = /^ /
  103. }
  104. if (sibling) {
  105. if (sibling.nodeType === 3) {
  106. isFlanked = regExp.test(sibling.nodeValue)
  107. } else if (sibling.nodeType === 1 && !isBlock(sibling)) {
  108. isFlanked = regExp.test(sibling.textContent)
  109. }
  110. }
  111. return isFlanked
  112. }
  113. function flankingWhitespace (node, content) {
  114. var leading = ''
  115. var trailing = ''
  116. if (!isBlock(node)) {
  117. var hasLeading = /^[ \r\n\t]/.test(content)
  118. var hasTrailing = /[ \r\n\t]$/.test(content)
  119. if (hasLeading && !isFlankedByWhitespace('left', node)) {
  120. leading = ' '
  121. }
  122. if (hasTrailing && !isFlankedByWhitespace('right', node)) {
  123. trailing = ' '
  124. }
  125. }
  126. return { leading: leading, trailing: trailing }
  127. }
  128. /*
  129. * Finds a Markdown converter, gets the replacement, and sets it on
  130. * `_replacement`
  131. */
  132. function process (node) {
  133. var replacement
  134. var content = getContent(node)
  135. // Remove blank nodes
  136. if (!isVoid(node) && !/A|TH|TD/.test(node.nodeName) && /^\s*$/i.test(content)) {
  137. node._replacement = ''
  138. return
  139. }
  140. for (var i = 0; i < converters.length; i++) {
  141. var converter = converters[i]
  142. if (canConvert(node, converter.filter)) {
  143. if (typeof converter.replacement !== 'function') {
  144. throw new TypeError(
  145. '`replacement` needs to be a function that returns a string'
  146. )
  147. }
  148. var whitespace = flankingWhitespace(node, content)
  149. if (whitespace.leading || whitespace.trailing) {
  150. content = content.trim()
  151. }
  152. replacement = whitespace.leading +
  153. converter.replacement.call(toMarkdown, content, node) +
  154. whitespace.trailing
  155. break
  156. }
  157. }
  158. node._replacement = replacement
  159. }
  160. toMarkdown = function (input, options) {
  161. options = options || {}
  162. if (typeof input !== 'string') {
  163. throw new TypeError(input + ' is not a string')
  164. }
  165. if (input === '') {
  166. return ''
  167. }
  168. // Escape potential ol triggers
  169. input = input.replace(/(\d+)\. /g, '$1\\. ')
  170. var clone = htmlToDom(input).body
  171. var nodes = bfsOrder(clone)
  172. var output
  173. converters = mdConverters.slice(0)
  174. if (options.gfm) {
  175. converters = gfmConverters.concat(converters)
  176. }
  177. if (options.converters) {
  178. converters = options.converters.concat(converters)
  179. }
  180. // Process through nodes in reverse (so deepest child elements are first).
  181. for (var i = nodes.length - 1; i >= 0; i--) {
  182. process(nodes[i])
  183. }
  184. output = getContent(clone)
  185. return output.replace(/^[\t\r\n]+|[\t\r\n\s]+$/g, '')
  186. .replace(/\n\s+\n/g, '\n\n')
  187. .replace(/\n{3,}/g, '\n\n')
  188. }
  189. toMarkdown.isBlock = isBlock
  190. toMarkdown.isVoid = isVoid
  191. toMarkdown.outer = outer
  192. module.exports = toMarkdown
  193. },{"./lib/gfm-converters":2,"./lib/html-parser":3,"./lib/md-converters":4,"collapse-whitespace":6}],2:[function(require,module,exports){
  194. 'use strict'
  195. function cell (content, node) {
  196. var index = Array.prototype.indexOf.call(node.parentNode.childNodes, node)
  197. var prefix = ' '
  198. if (index === 0) prefix = '| '
  199. return prefix + content + ' |'
  200. }
  201. var highlightRegEx = /highlight highlight-(\S+)/
  202. module.exports = [
  203. {
  204. filter: 'br',
  205. replacement: function () {
  206. return '\n'
  207. }
  208. },
  209. {
  210. filter: ['del', 's', 'strike'],
  211. replacement: function (content) {
  212. return '~~' + content + '~~'
  213. }
  214. },
  215. {
  216. filter: function (node) {
  217. return node.type === 'checkbox' && node.parentNode.nodeName === 'LI'
  218. },
  219. replacement: function (content, node) {
  220. return (node.checked ? '[x]' : '[ ]') + ' '
  221. }
  222. },
  223. {
  224. filter: ['th', 'td'],
  225. replacement: function (content, node) {
  226. return cell(content, node)
  227. }
  228. },
  229. {
  230. filter: 'tr',
  231. replacement: function (content, node) {
  232. var borderCells = ''
  233. var alignMap = { left: ':--', right: '--:', center: ':-:' }
  234. if (node.parentNode.nodeName === 'THEAD') {
  235. for (var i = 0; i < node.childNodes.length; i++) {
  236. var align = node.childNodes[i].attributes.align
  237. var border = '---'
  238. if (align) border = alignMap[align.value] || border
  239. borderCells += cell(border, node.childNodes[i])
  240. }
  241. }
  242. return '\n' + content + (borderCells ? '\n' + borderCells : '')
  243. }
  244. },
  245. {
  246. filter: 'table',
  247. replacement: function (content) {
  248. return '\n\n' + content + '\n\n'
  249. }
  250. },
  251. {
  252. filter: ['thead', 'tbody', 'tfoot'],
  253. replacement: function (content) {
  254. return content
  255. }
  256. },
  257. // Fenced code blocks
  258. {
  259. filter: function (node) {
  260. return node.nodeName === 'PRE' &&
  261. node.firstChild &&
  262. node.firstChild.nodeName === 'CODE'
  263. },
  264. replacement: function (content, node) {
  265. return '\n\n```\n' + node.firstChild.textContent + '\n```\n\n'
  266. }
  267. },
  268. // Syntax-highlighted code blocks
  269. {
  270. filter: function (node) {
  271. return node.nodeName === 'PRE' &&
  272. node.parentNode.nodeName === 'DIV' &&
  273. highlightRegEx.test(node.parentNode.className)
  274. },
  275. replacement: function (content, node) {
  276. var language = node.parentNode.className.match(highlightRegEx)[1]
  277. return '\n\n```' + language + '\n' + node.textContent + '\n```\n\n'
  278. }
  279. },
  280. {
  281. filter: function (node) {
  282. return node.nodeName === 'DIV' &&
  283. highlightRegEx.test(node.className)
  284. },
  285. replacement: function (content) {
  286. return '\n\n' + content + '\n\n'
  287. }
  288. }
  289. ]
  290. },{}],3:[function(require,module,exports){
  291. /*
  292. * Set up window for Node.js
  293. */
  294. var _window = (typeof window !== 'undefined' ? window : this)
  295. /*
  296. * Parsing HTML strings
  297. */
  298. function canParseHtmlNatively () {
  299. var Parser = _window.DOMParser
  300. var canParse = false
  301. // Adapted from https://gist.github.com/1129031
  302. // Firefox/Opera/IE throw errors on unsupported types
  303. try {
  304. // WebKit returns null on unsupported types
  305. if (new Parser().parseFromString('', 'text/html')) {
  306. canParse = true
  307. }
  308. } catch (e) {}
  309. return canParse
  310. }
  311. function createHtmlParser () {
  312. var Parser = function () {}
  313. // For Node.js environments
  314. if (typeof document === 'undefined') {
  315. var jsdom = require('jsdom')
  316. Parser.prototype.parseFromString = function (string) {
  317. return jsdom.jsdom(string, {
  318. features: {
  319. FetchExternalResources: [],
  320. ProcessExternalResources: false
  321. }
  322. })
  323. }
  324. } else {
  325. if (!shouldUseActiveX()) {
  326. Parser.prototype.parseFromString = function (string) {
  327. var doc = document.implementation.createHTMLDocument('')
  328. doc.open()
  329. doc.write(string)
  330. doc.close()
  331. return doc
  332. }
  333. } else {
  334. Parser.prototype.parseFromString = function (string) {
  335. var doc = new window.ActiveXObject('htmlfile')
  336. doc.designMode = 'on' // disable on-page scripts
  337. doc.open()
  338. doc.write(string)
  339. doc.close()
  340. return doc
  341. }
  342. }
  343. }
  344. return Parser
  345. }
  346. function shouldUseActiveX () {
  347. var useActiveX = false
  348. try {
  349. document.implementation.createHTMLDocument('').open()
  350. } catch (e) {
  351. if (window.ActiveXObject) useActiveX = true
  352. }
  353. return useActiveX
  354. }
  355. module.exports = canParseHtmlNatively() ? _window.DOMParser : createHtmlParser()
  356. },{"jsdom":8}],4:[function(require,module,exports){
  357. 'use strict'
  358. module.exports = [
  359. // P标签处理
  360. {
  361. filter: 'p',
  362. replacement: function (content, node) {
  363. var attrClass = node.getAttribute('class') || ''
  364. if (attrClass === 'command') {
  365. if (!content.endsWith('\n')) {
  366. content += '\n'
  367. }
  368. return '\n```\n' + content + '```\n'
  369. } else {
  370. return '\n\n' + content + '\n\n'
  371. }
  372. }
  373. },
  374. // BR 标签处理
  375. {
  376. filter: 'br',
  377. replacement: function () {
  378. return ' \n'
  379. }
  380. },
  381. // H1 处理
  382. {
  383. filter: 'h1',
  384. replacement: function (content, node) {
  385. if (typeof titleLock === "undefined") {
  386. var titleLock = false;
  387. }
  388. if (!titleLock) {
  389. titleLock = true;
  390. return content + "\n" + "=".repeat(60);
  391. }
  392. else {
  393. return '\n\n' + '# ' + content + '\n\n'
  394. }
  395. }
  396. },
  397. // H2-H7 标签处理
  398. {
  399. filter: ['h2', 'h3', 'h4', 'h5', 'h6', 'h7'],
  400. replacement: function (content, node) {
  401. var hLevel = node.nodeName.charAt(1)
  402. var hPrefix = '##'
  403. hLevel = hLevel - 2
  404. for (var i = 0; i < hLevel; i++) {
  405. hPrefix += '#'
  406. }
  407. return '\n\n' + hPrefix + ' ' + content + '\n\n'
  408. }
  409. },
  410. // HR 标签处理
  411. {
  412. filter: 'hr',
  413. replacement: function () {
  414. return '\n\n* * *\n\n'
  415. }
  416. },
  417. // em i 斜体处理
  418. {
  419. filter: ['em', 'i'],
  420. replacement: function (content) {
  421. return ' _' + content + '_ '
  422. }
  423. },
  424. // Strong b 粗体处理
  425. {
  426. filter: ['strong', 'b'],
  427. replacement: function (content) {
  428. return '**' + content + '**'
  429. }
  430. },
  431. // Inline code
  432. {
  433. filter: function (node) {
  434. var hasSiblings = node.previousSibling || node.nextSibling
  435. var isCodeBlock = node.parentNode.nodeName === 'PRE' && !hasSiblings
  436. return node.nodeName === 'CODE' && !isCodeBlock
  437. },
  438. replacement: function (content) {
  439. return '`' + content + '`'
  440. }
  441. },
  442. // A 标签处理
  443. {
  444. filter: function (node) {
  445. return node.nodeName === 'A' && node.getAttribute('href')
  446. },
  447. replacement: function (content, node) {
  448. return '[' + content + '](' + node.getAttribute('href') + ')'
  449. }
  450. },
  451. // 特殊情况下的A标签处理
  452. {
  453. filter: function (node) {
  454. return node.nodeName === 'A' && node.getAttribute('style')
  455. },
  456. replacement: function (content, node) {
  457. return content
  458. }
  459. },
  460. // IMG 标签处理
  461. {
  462. filter: 'img',
  463. replacement: function (content, node) {
  464. var alt = node.alt || ''
  465. var src = node.getAttribute('src') || ''
  466. var title = node.title || ''
  467. var titlePart = title ? ' "' + title + '"' : ''
  468. return src ? '\n![' + alt + ']' + '(' + src + titlePart + ')\n' : ''
  469. }
  470. },
  471. // 代码块处理
  472. {
  473. filter: 'pre',
  474. replacement: function (content, node) {
  475. let contentText = node.innerText
  476. if (!contentText.endsWith('\n')) {
  477. contentText += '\n'
  478. }
  479. return '\n```\n' + contentText + '```\n'
  480. }
  481. },
  482. // 行内代码处理
  483. {
  484. filter: 'code',
  485. replacement: function (content, node) {
  486. return '`' + content + '`'
  487. }
  488. },
  489. // IFrame 提醒
  490. {
  491. filter: 'iframe',
  492. replacement: function (content, node) {
  493. console.log(node);
  494. console.log(content);
  495. return '\n ** 此处有iframe,请手动处理 ** \n'
  496. }
  497. },
  498. // Canvas 提醒
  499. {
  500. filter: 'canvas',
  501. replacement: function (content, node) {
  502. return '\n ** 此处有Canvas,请手动处理 ** \n'
  503. }
  504. },
  505. // div 处理
  506. {
  507. filter: 'div',
  508. replacement: function (content, node) {
  509. var attrClass = node.getAttribute('class') || ''
  510. if (attrClass === 'code') {
  511. if (!content.endsWith('\n')) {
  512. content += '\n'
  513. }
  514. return '\n```\n' + content + '```\n'
  515. } else {
  516. return content
  517. }
  518. }
  519. },
  520. {
  521. 'filter': 'textarea',
  522. replacement: function (content, node) {
  523. return ''
  524. }
  525. },
  526. // 直接返回内容的标签
  527. {
  528. filter: ['figure', 'span', 'small', 'section', 'font', 'asymspc', 'button', 'article', 'figcaption'],
  529. replacement: function (content) {
  530. return content
  531. }
  532. },
  533. // 引用
  534. {
  535. filter: 'blockquote',
  536. replacement: function (content) {
  537. content = content.trim()
  538. content = content.replace(/\n{3,}/g, '\n\n')
  539. content = content.replace(/^/gm, '> ')
  540. return '\n\n' + content + '\n\n'
  541. }
  542. },
  543. // 列表项
  544. {
  545. filter: 'li',
  546. replacement: function (content, node) {
  547. content = content.replace(/^\s+/, '').replace(/\n/gm, '\n ')
  548. var prefix = '* '
  549. var parent = node.parentNode
  550. var index = Array.prototype.indexOf.call(parent.children, node) + 1
  551. prefix = /ol/i.test(parent.nodeName) ? index + '. ' : '* '
  552. return prefix + content + '\n'
  553. }
  554. },
  555. // 有序/无序列表
  556. {
  557. filter: ['ul', 'ol'],
  558. replacement: function (content, node) {
  559. var strings = []
  560. for (var i = 0; i < node.childNodes.length; i++) {
  561. strings.push(node.childNodes[i]._replacement)
  562. }
  563. if (/li/i.test(node.parentNode.nodeName)) {
  564. return '\n' + strings.join('\n')
  565. }
  566. return '\n\n' + strings.join('\n') + '\n\n'
  567. }
  568. },
  569. // 判断是否是block,如果是block,前后加空行
  570. {
  571. filter: function (node) {
  572. return this.isBlock(node)
  573. },
  574. replacement: function (content, node) {
  575. return '\n\n' + this.outer(node, content) + '\n\n'
  576. }
  577. },
  578. // Anything else!
  579. {
  580. filter: function () {
  581. return true
  582. },
  583. replacement: function (content, node) {
  584. return this.outer(node, content)
  585. }
  586. }
  587. ]
  588. },{}],5:[function(require,module,exports){
  589. /**
  590. * This file automatically generated from `build.js`.
  591. * Do not manually edit.
  592. */
  593. module.exports = [
  594. "address",
  595. "article",
  596. "aside",
  597. "blockquote",
  598. "canvas",
  599. "dd",
  600. "div",
  601. "dl",
  602. "dt",
  603. "fieldset",
  604. "figcaption",
  605. "figure",
  606. "footer",
  607. "form",
  608. "h1",
  609. "h2",
  610. "h3",
  611. "h4",
  612. "h5",
  613. "h6",
  614. "h7",
  615. "header",
  616. "hgroup",
  617. "hr",
  618. "li",
  619. "main",
  620. "nav",
  621. "noscript",
  622. "ol",
  623. "output",
  624. "p",
  625. "pre",
  626. "section",
  627. "table",
  628. "tfoot",
  629. "ul",
  630. "video"
  631. ];
  632. },{}],6:[function(require,module,exports){
  633. 'use strict';
  634. var voidElements = require('void-elements');
  635. Object.keys(voidElements).forEach(function (name) {
  636. voidElements[name.toUpperCase()] = 1;
  637. });
  638. var blockElements = {};
  639. require('block-elements').forEach(function (name) {
  640. blockElements[name.toUpperCase()] = 1;
  641. });
  642. /**
  643. * isBlockElem(node) determines if the given node is a block element.
  644. *
  645. * @param {Node} node
  646. * @return {Boolean}
  647. */
  648. function isBlockElem(node) {
  649. return !!(node && blockElements[node.nodeName]);
  650. }
  651. /**
  652. * isVoid(node) determines if the given node is a void element.
  653. *
  654. * @param {Node} node
  655. * @return {Boolean}
  656. */
  657. function isVoid(node) {
  658. return !!(node && voidElements[node.nodeName]);
  659. }
  660. /**
  661. * whitespace(elem [, isBlock]) removes extraneous whitespace from an
  662. * the given element. The function isBlock may optionally be passed in
  663. * to determine whether or not an element is a block element; if none
  664. * is provided, defaults to using the list of block elements provided
  665. * by the `block-elements` module.
  666. *
  667. * @param {Node} elem
  668. * @param {Function} blockTest
  669. */
  670. function collapseWhitespace(elem, isBlock) {
  671. if (!elem.firstChild || elem.nodeName === 'PRE') return;
  672. if (typeof isBlock !== 'function') {
  673. isBlock = isBlockElem;
  674. }
  675. var prevText = null;
  676. var prevVoid = false;
  677. var prev = null;
  678. var node = next(prev, elem);
  679. while (node !== elem) {
  680. if (node.nodeType === 3) {
  681. // Node.TEXT_NODE
  682. var text = node.data.replace(/[ \r\n\t]+/g, ' ');
  683. if ((!prevText || / $/.test(prevText.data)) && !prevVoid && text[0] === ' ') {
  684. text = text.substr(1);
  685. }
  686. // `text` might be empty at this point.
  687. if (!text) {
  688. node = remove(node);
  689. continue;
  690. }
  691. node.data = text;
  692. prevText = node;
  693. } else if (node.nodeType === 1) {
  694. // Node.ELEMENT_NODE
  695. if (isBlock(node) || node.nodeName === 'BR') {
  696. if (prevText) {
  697. prevText.data = prevText.data.replace(/ $/, '');
  698. }
  699. prevText = null;
  700. prevVoid = false;
  701. } else if (isVoid(node)) {
  702. // Avoid trimming space around non-block, non-BR void elements.
  703. prevText = null;
  704. prevVoid = true;
  705. }
  706. } else {
  707. node = remove(node);
  708. continue;
  709. }
  710. var nextNode = next(prev, node);
  711. prev = node;
  712. node = nextNode;
  713. }
  714. if (prevText) {
  715. prevText.data = prevText.data.replace(/ $/, '');
  716. if (!prevText.data) {
  717. remove(prevText);
  718. }
  719. }
  720. }
  721. /**
  722. * remove(node) removes the given node from the DOM and returns the
  723. * next node in the sequence.
  724. *
  725. * @param {Node} node
  726. * @return {Node} node
  727. */
  728. function remove(node) {
  729. var next = node.nextSibling || node.parentNode;
  730. node.parentNode.removeChild(node);
  731. return next;
  732. }
  733. /**
  734. * next(prev, current) returns the next node in the sequence, given the
  735. * current and previous nodes.
  736. *
  737. * @param {Node} prev
  738. * @param {Node} current
  739. * @return {Node}
  740. */
  741. function next(prev, current) {
  742. if (prev && prev.parentNode === current || current.nodeName === 'PRE') {
  743. return current.nextSibling || current.parentNode;
  744. }
  745. return current.firstChild || current.nextSibling || current.parentNode;
  746. }
  747. module.exports = collapseWhitespace;
  748. },{"block-elements":5,"void-elements":7}],7:[function(require,module,exports){
  749. /**
  750. * This file automatically generated from `pre-publish.js`.
  751. * Do not manually edit.
  752. */
  753. module.exports = {
  754. "area": true,
  755. "base": true,
  756. "br": true,
  757. "col": true,
  758. "embed": true,
  759. "hr": true,
  760. "img": true,
  761. "input": true,
  762. "keygen": true,
  763. "link": true,
  764. "menuitem": true,
  765. "meta": true,
  766. "param": true,
  767. "source": true,
  768. "track": true,
  769. "wbr": true
  770. };
  771. },{}],8:[function(require,module,exports){
  772. },{}]},{},[1])(1)
  773. });