UNPKG

2.85 kBJavaScriptView Raw
1const parseCSS = require('css-rules');
2const cheerio = require('cheerio');
3const pseudoCheck = require('./pseudoCheck');
4const handleRule = require('./handleRule');
5const flatten = require('flat-util');
6const setStyleAttrs = require('./setStyleAttrs');
7const setWidthAttrs = require('./setWidthAttrs');
8const removeClassId = require('./removeClassId');
9const setTableAttrs = require('./setTableAttrs');
10const pick = require('pick-util');
11
12function replaceCodeBlock(html, re, block) {
13 return html.replace(re, () => block);
14}
15
16module.exports = (html, css, options) => {
17 const opts = options || {};
18 let rules;
19 let editedElements = [];
20 const codeBlockLookup = [];
21
22 const encodeCodeBlocks = _html => {
23 let __html = _html;
24 const blocks = opts.codeBlocks;
25
26 Object.keys(blocks).forEach(key => {
27 const re = new RegExp(`${blocks[key].start}([\\S\\s]*?)${blocks[key].end}`, 'g');
28
29 __html = __html.replace(re, match => {
30 codeBlockLookup.push(match);
31 return `EXCS_CODE_BLOCK_${codeBlockLookup.length - 1}_`;
32 });
33 });
34 return __html;
35 };
36
37 const decodeCodeBlocks = _html => {
38 let index;
39 let re;
40 let __html = _html;
41
42 for (index = 0; index < codeBlockLookup.length; index++) {
43 re = new RegExp(`EXCS_CODE_BLOCK_${index}_(="")?`, 'gi');
44 __html = replaceCodeBlock(__html, re, codeBlockLookup[index]);
45 }
46 return __html;
47 };
48
49 const encodeEntities = _html => encodeCodeBlocks(_html);
50 const decodeEntities = _html => decodeCodeBlocks(_html);
51 let $;
52
53 $ = cheerio.load(encodeEntities(html), pick(opts, [
54 'xmlMode',
55 'decodeEntities',
56 'lowerCaseTags',
57 'lowerCaseAttributeNames',
58 'recognizeCDATA',
59 'recognizeSelfClosing'
60 ]));
61
62 try {
63 rules = parseCSS(css);
64 } catch (err) {
65 throw new Error(err);
66 }
67
68 rules.forEach(rule => {
69 let el;
70 let ignoredPseudos;
71
72 ignoredPseudos = pseudoCheck(rule);
73
74 if (ignoredPseudos) {
75 return false;
76 }
77
78 try {
79 el = handleRule(rule, $);
80
81 editedElements.push(el);
82 } catch (err) {
83 // skip invalid selector
84 return false;
85 }
86 });
87
88 // flatten array if nested
89 editedElements = flatten(editedElements);
90
91 editedElements.forEach(el => {
92 setStyleAttrs(el, $);
93
94 if (opts.applyWidthAttributes) {
95 setWidthAttrs(el, $);
96 }
97
98 if (opts.removeHtmlSelectors) {
99 removeClassId(el, $);
100 }
101 });
102
103 if (opts.applyTableAttributes) {
104 $('table').each((index, el) => {
105 setTableAttrs(el, $);
106 });
107 }
108
109 return decodeEntities($.html());
110};