UNPKG

4.2 kBJavaScriptView Raw
1/* jshint quotmark: false */
2'use strict';
3
4exports.type = 'perItem';
5
6exports.active = true;
7
8exports.description = 'converts style to attributes';
9
10exports.params = {
11 keepImportant: false
12};
13
14var stylingProps = require('./_collections').attrsGroups.presentation,
15 rEscape = '\\\\(?:[0-9a-f]{1,6}\\s?|\\r\\n|.)', // Like \" or \2051. Code points consume one space.
16 rAttr = '\\s*(' + g('[^:;\\\\]', rEscape) + '*?)\\s*', // attribute name like ‘fill’
17 rSingleQuotes = "'(?:[^'\\n\\r\\\\]|" + rEscape + ")*?(?:'|$)", // string in single quotes: 'smth'
18 rQuotes = '"(?:[^"\\n\\r\\\\]|' + rEscape + ')*?(?:"|$)', // string in double quotes: "smth"
19 rQuotedString = new RegExp('^' + g(rSingleQuotes, rQuotes) + '$'),
20
21 // Parentheses, E.g.: url(data:image/png;base64,iVBO...).
22 // ':' and ';' inside of it should be threated as is. (Just like in strings.)
23 rParenthesis = '\\(' + g('[^\'"()\\\\]+', rEscape, rSingleQuotes, rQuotes) + '*?' + '\\)',
24
25 // The value. It can have strings and parentheses (see above). Fallbacks to anything in case of unexpected input.
26 rValue = '\\s*(' + g('[^!\'"();\\\\]+?', rEscape, rSingleQuotes, rQuotes, rParenthesis, '[^;]*?') + '*?' + ')',
27
28 // End of declaration. Spaces outside of capturing groups help to do natural trimming.
29 rDeclEnd = '\\s*(?:;\\s*|$)',
30
31 // Important rule
32 rImportant = '(\\s*!important(?![-(\w]))?',
33
34 // Final RegExp to parse CSS declarations.
35 regDeclarationBlock = new RegExp(rAttr + ':' + rValue + rImportant + rDeclEnd, 'ig'),
36
37 // Comments expression. Honors escape sequences and strings.
38 regStripComments = new RegExp(g(rEscape, rSingleQuotes, rQuotes, '/\\*[^]*?\\*/'), 'ig');
39
40/**
41 * Convert style in attributes. Cleanups comments and illegal declarations (without colon) as a side effect.
42 *
43 * @example
44 * <g style="fill:#000; color: #fff;">
45 * ⬇
46 * <g fill="#000" color="#fff">
47 *
48 * @example
49 * <g style="fill:#000; color: #fff; -webkit-blah: blah">
50 * ⬇
51 * <g fill="#000" color="#fff" style="-webkit-blah: blah">
52 *
53 * @param {Object} item current iteration item
54 * @return {Boolean} if false, item will be filtered out
55 *
56 * @author Kir Belevich
57 */
58exports.fn = function(item, params) {
59 /* jshint boss: true */
60
61 if (item.elem && item.hasAttr('style')) {
62 // ['opacity: 1', 'color: #000']
63 var styleValue = item.attr('style').value,
64 styles = [],
65 attrs = {};
66
67 // Strip CSS comments preserving escape sequences and strings.
68 styleValue = styleValue.replace(regStripComments, function(match) {
69 return match[0] == '/' ? '' :
70 match[0] == '\\' && /[-g-z]/i.test(match[1]) ? match[1] : match;
71 });
72
73 regDeclarationBlock.lastIndex = 0;
74 for (var rule; rule = regDeclarationBlock.exec(styleValue);) {
75 if (!params.keepImportant || !rule[3]) {
76 styles.push([rule[1], rule[2]]);
77 }
78 }
79
80 if (styles.length) {
81
82 styles = styles.filter(function(style) {
83 if (style[0]) {
84 var prop = style[0].toLowerCase(),
85 val = style[1];
86
87 if (rQuotedString.test(val)) {
88 val = val.slice(1, -1);
89 }
90
91 if (stylingProps.indexOf(prop) > -1) {
92
93 attrs[prop] = {
94 name: prop,
95 value: val,
96 local: prop,
97 prefix: ''
98 };
99
100 return false;
101 }
102 }
103
104 return true;
105 });
106
107 Object.assign(item.attrs, attrs);
108
109 if (styles.length) {
110 item.attr('style').value = styles
111 .map(function(declaration) { return declaration.join(':') })
112 .join(';');
113 } else {
114 item.removeAttr('style');
115 }
116
117 }
118
119 }
120
121};
122
123function g() {
124 return '(?:' + Array.prototype.join.call(arguments, '|') + ')';
125}