1 | const parseCSS = require('css-rules');
|
2 | const cheerio = require('cheerio');
|
3 | const pseudoCheck = require('./pseudoCheck');
|
4 | const handleRule = require('./handleRule');
|
5 | const flatten = require('flat-util');
|
6 | const setStyleAttrs = require('./setStyleAttrs');
|
7 | const setWidthAttrs = require('./setWidthAttrs');
|
8 | const removeClassId = require('./removeClassId');
|
9 | const setTableAttrs = require('./setTableAttrs');
|
10 | const pick = require('pick-util');
|
11 |
|
12 | function replaceCodeBlock(html, re, block) {
|
13 | return html.replace(re, () => block);
|
14 | }
|
15 |
|
16 | module.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 |
|
84 | return false;
|
85 | }
|
86 | });
|
87 |
|
88 |
|
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 | };
|