blob: 00aaa2b1f3187553cf927f21cdc76fd26aa61cca (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
/*eslint-env node */
/*!
* Postprocessor for shimming @media (hover: hover) from Media Queries Level 4
* https://github.com/twbs/mq4-hover-shim
* Copyright 2014-2015 Christopher Rebert
* Licensed under MIT (https://github.com/twbs/mq4-hover-shim/blob/master/LICENSE.txt)
*/
'use strict';
var postcss = require('postcss');
var parseMediaQuery = require('css-mq-parser');
// Returns media type iff the at-rule is: @media optional-media-type (hover: hover) {...}
function mediaTypeIfSimpleHoverHover(atRule) {
var mediaOrs = parseMediaQuery(atRule.params);
if (mediaOrs.length !== 1) {
return false;
}
var mediaAnds = mediaOrs[0];
if (mediaAnds.inverse) {
return false;
}
if (mediaAnds.expressions.length !== 1) {
return false;
}
var mediaExpr = mediaAnds.expressions[0];
if (mediaExpr.feature === 'hover' && mediaExpr.value === 'hover') {
return mediaAnds.type;
}
else {
return undefined;
}
}
function replaceWithItsChildren(atRule) {
atRule.each(function (child) {
child.moveBefore(atRule);
});
atRule.removeSelf();
}
// Prefixes each selector in the given rule with the given prefix string
function prefixSelectorsWith(rule, selectorPrefix) {
// Yes, this parsing is horribly naive.
// We don't use rule.selectors because it's "some kind of hack" per https://github.com/postcss/postcss/issues/37
// and it doesn't preserve inter-selector whitespace.
var selectorsWithWhitespace = rule.selector.split(',');
var revisedSelectors = selectorsWithWhitespace.map(function (selectorWithWhitespace) {
var quadruple = /^(\s*)(\S.*\S)(\s*)$/.exec(selectorWithWhitespace);
if (quadruple === null) {
// Skip weirdness
return selectorWithWhitespace;
}
var prefixWhitespace = quadruple[1];
var selector = quadruple[2];
var suffixWhitespace = quadruple[3];
var revisedSelector = prefixWhitespace + selectorPrefix + selector + suffixWhitespace;
return revisedSelector;
});
rule.selector = revisedSelectors.join(',');
}
module.exports = function (opts) {
return postcss(function process(css) {
var hoverSelectorPrefix = opts.hoverSelectorPrefix;
if ((typeof hoverSelectorPrefix) !== 'string') {
throw new Error('hoverSelectorPrefix option must be a string');
}
css.eachAtRule('media', function (atRule) {
var mediaType = mediaTypeIfSimpleHoverHover(atRule);
switch (mediaType) {
case 'all':
/* falls through */
case 'screen': {
atRule.eachRule(function (rule) {
prefixSelectorsWith(rule, hoverSelectorPrefix);
});
if (mediaType === 'screen') {
atRule.params = 'screen';
}
else {
// Remove tautological @media all {...} wrapper
replaceWithItsChildren(atRule);
}
return;
}
case 'print':
/* falls through */
case 'speech': {
// These media types never support hovering
// Delete always-false media query
atRule.removeSelf();
return;
}
case undefined: {
return; // Media query irrelevant or too complicated
}
default: {
return; // Deprecated media type; take no action.
}
}
});
});
};
|