| | |
| | | * |
| | | */ |
| | | // Regular Expressions for parsing tags and attributes |
| | | var startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/; |
| | | var endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/; |
| | | // fixed by 名导精讲 标签名允许冒号,兼容 Word 导出的 <o:p>、<v:shape> 等命名空间标签, |
| | | // 否则解析循环无法前进会抛出 Parse Error,导致页面把整段 HTML 当纯文本显示。 |
| | | var startTag = /^<([-A-Za-z0-9_:]+)((?:\s+[a-zA-Z_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/; |
| | | var endTag = /^<\/([-A-Za-z0-9_:]+)[^>]*>/; |
| | | var attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g; // Empty Elements - HTML 5 |
| | | |
| | | var empty = makeMap('area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr'); // Block Elements - HTML 5 |
| | |
| | | } |
| | | |
| | | html = html.substring(index + 3); |
| | | chars = false; |
| | | } else { |
| | | // 未闭合注释:吞掉剩余内容,避免解析循环无法前进抛出 Parse Error |
| | | html = ''; |
| | | chars = false; |
| | | } // end tag |
| | | |
| | |
| | | } |
| | | |
| | | function removeDOCTYPE(html) { |
| | | return html.replace(/<\?xml.*\?>\n/, '').replace(/<!doctype.*>\n/, '').replace(/<!DOCTYPE.*>\n/, ''); |
| | | return html |
| | | .replace(/<\?xml[\s\S]*?\?>/gi, '') |
| | | .replace(/<!DOCTYPE[^>]*>/gi, ''); |
| | | } |
| | | |
| | | // Word 导出 HTML 专用清洗:去掉小程序富文本无法处理的噪声内容。 |
| | | function sanitizeWordHtml(html) { |
| | | return removeDOCTYPE(html) |
| | | // downlevel-revealed 条件注释(<![if !supportLists]> / <![endif]>)不是标准注释,会让解析循环卡死 |
| | | .replace(/<!\[if[\s\S]*?\]>/gi, '') |
| | | .replace(/<!\[endif\]>/gi, '') |
| | | // 标准注释与 downlevel-hidden 条件注释块(内含 <xml><w:WordDocument> 等大段配置) |
| | | .replace(/<!--[\s\S]*?-->/g, '') |
| | | .replace(/<head[\s\S]*?<\/head>/gi, '') |
| | | .replace(/<(style|script|xml)[\s\S]*?<\/\1>/gi, '') |
| | | // html/body 外壳 rich-text 不支持,可能连同子树一起被丢弃,只保留内部节点 |
| | | .replace(/<\/?html[^>]*>/gi, '') |
| | | .replace(/<\/?body[^>]*>/gi, '') |
| | | // 命名空间标签(<o:p>、<v:shape>、<st1:place> 等)只删标签本身,保留其中的文本 |
| | | .replace(/<\/?[A-Za-z][\w.-]*:[\w.-]*(?:\s[^>]*)?\/?>/g, ''); |
| | | } |
| | | |
| | | var namedEntities = { |
| | | nbsp: ' ', |
| | | ensp: ' ', |
| | | emsp: ' ', |
| | | quot: '"', |
| | | apos: "'", |
| | | amp: '&', |
| | | lt: '<', |
| | | gt: '>', |
| | | ldquo: '\u201c', |
| | | rdquo: '\u201d', |
| | | lsquo: '\u2018', |
| | | rsquo: '\u2019', |
| | | mdash: '\u2014', |
| | | ndash: '\u2013', |
| | | hellip: '\u2026', |
| | | middot: '\u00b7', |
| | | bull: '\u2022', |
| | | dagger: '\u2020', |
| | | permil: '\u2030', |
| | | lsaquo: '\u2039', |
| | | rsaquo: '\u203a', |
| | | times: '\u00d7', |
| | | divide: '\u00f7', |
| | | copy: '\u00a9', |
| | | reg: '\u00ae', |
| | | trade: '\u2122', |
| | | deg: '\u00b0', |
| | | minus: '\u2212', |
| | | szlig: '\u00df' |
| | | }; |
| | | |
| | | // rich-text 的文本节点不会解码实体,Word 正文里的 “ 等需要在这里转成字符。 |
| | | function decodeEntities(text) { |
| | | if (text.indexOf('&') < 0) return text; |
| | | return text.replace(/&(#[xX]?[0-9a-fA-F]+|[a-zA-Z][a-zA-Z0-9]*);/g, function (all, name) { |
| | | if (name[0] === '#') { |
| | | var code = name[1] === 'x' || name[1] === 'X' ? parseInt(name.slice(2), 16) : parseInt(name.slice(1), 10); |
| | | return isNaN(code) || code < 0 || code > 0x10ffff ? all : String.fromCodePoint(code); |
| | | } |
| | | // amp 最后解,避免 &lt; 被二次解码 |
| | | if (name === 'amp') return '&'; |
| | | return Object.prototype.hasOwnProperty.call(namedEntities, name) ? namedEntities[name] : all; |
| | | }); |
| | | } |
| | | |
| | | function parseAttrs(attrs) { |
| | |
| | | } |
| | | |
| | | function parseHtml(html) { |
| | | html = removeDOCTYPE(html); |
| | | html = sanitizeWordHtml(html); |
| | | var stacks = []; |
| | | var results = { |
| | | node: 'root', |
| | |
| | | chars: function chars(text) { |
| | | var node = { |
| | | type: 'text', |
| | | text: text |
| | | text: decodeEntities(text) |
| | | }; |
| | | |
| | | if (stacks.length === 0) { |