Skip to content

Commit c4b8458

Browse files
Quick fix for issue #2
Noticed that findMediaBreakpoints was not working. Worked through the issue, honestly don't remember exactly which bit fixes things. Also updated package.json for my own use. Also extracted findBreakpoints from external project into this one because I just wanted things working. Could make the same/similar changes to the external project but for the purpose of just making this extension do the thing right, I'm leaving it as is.
1 parent 50e807b commit c4b8458

File tree

3 files changed

+209
-1
lines changed

3 files changed

+209
-1
lines changed

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
"main": "index.js",
66
"scripts": {
77
"publish": "NODE_ENV=production gulp pack",
8+
"watch": "gulp watch",
9+
"build": "gulp",
810
"test": "echo \"Error: no test specified\" && exit 1"
911
},
1012
"repository": {

scripts/lib/find-breakpoints.js

+204
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
/**
2+
* Scans media query breakpoints in given CSSStylesheet and returns
3+
* them as array
4+
*/
5+
'use strict';
6+
const allowedFeatures = ['min-width', 'max-width'];
7+
8+
export default function(doc=document, options={}) {
9+
10+
var list = Array.from(doc.styleSheets);
11+
console.log('list:',list);
12+
return Promise.all(list.map(ss => {
13+
// NB Check for own `.cssRules` property to bypass Chrome security
14+
// with external stylesheets
15+
// try...catch because browser may not able to enumerate rules for cross-domain sheets
16+
17+
try {
18+
const rules = ss.rules || ss.cssRules;
19+
if (rules) return readCSSOM(ss);
20+
} catch (e) {
21+
console.warn("Can't read the css rules of: " + ss.href, e);
22+
}
23+
24+
// Rules are not available: most likely CSS was loaded
25+
// from another domain and browser blocked access to CSSOM
26+
// according to same-origin policy.
27+
// Try to use external loader, if available
28+
if (options.loadCSS) {
29+
return options.loadCSS(ss.href).then(readCSSOM);
30+
}
31+
32+
return null;
33+
}))
34+
// flatten nested arrays of breakpoints
35+
.then(values => [].concat(...values.filter(Boolean)))
36+
.then((array) => {
37+
console.log('Making unique', array);
38+
var uniqueArray = unique(array);
39+
console.log('Unique', uniqueArray);
40+
return uniqueArray;
41+
})
42+
.then(optimize)
43+
.then(bp => bp.sort((a, b) => a.smallest - b.smallest));
44+
};
45+
46+
class Breakpoint {
47+
constructor(query, features) {
48+
this._real = null;
49+
this._id = null;
50+
this._features = null;
51+
this._query = query.trim();
52+
this.features = features;
53+
}
54+
55+
get id() {
56+
if (this._id === null && this.features.length) {
57+
this._id = 'bp_' + this.features.map(f => f + this[f]).join('__');
58+
}
59+
60+
return this._id;
61+
}
62+
63+
get query() {
64+
return this._query;
65+
}
66+
67+
get features() {
68+
if (!this._features) {
69+
this._features = Object.keys(this).filter(key => key[0] !== '_').sort();
70+
}
71+
72+
return this._features;
73+
}
74+
75+
set features(value={}) {
76+
this._id = this._features = this._real = null;
77+
Object.keys(value).forEach(feature => this[normalizeName(feature)] = value[feature]);
78+
}
79+
80+
/**
81+
* Returns smallest feature size for current breakpoint
82+
* @type {Number}
83+
*/
84+
get smallest() {
85+
var smallest = this.features.reduce((prev, f) => Math.min(prev, this.real(f)), Number.POSITIVE_INFINITY);
86+
return smallest !== Number.POSITIVE_INFINITY ? smallest : 0;
87+
}
88+
89+
/**
90+
* Returns real size (in pixels) of given feature
91+
* @param {String} feature
92+
*/
93+
real(feature) {
94+
if (!this._real) {
95+
this.measure();
96+
}
97+
98+
return this._real[normalizeName(feature)];
99+
}
100+
101+
/**
102+
* Measures real feature's dimentions
103+
* @param {Element} ctx Optional content element where
104+
* features should be measured
105+
* @return {Object} Real feature sizes
106+
*/
107+
measure(ctx=document.body) {
108+
var m = document.createElement('div');
109+
m.style.cssText = 'position:absolute;padding:0;margin:0;top:0;left:0;height:0;';
110+
ctx.appendChild(m);
111+
112+
var real = this.features.reduce((out, feature) => {
113+
if (typeof this[feature] === 'number') {
114+
out[feature] = this[feature];
115+
} else {
116+
m.style.width = this[feature];
117+
out[feature] = m.offsetWidth;
118+
}
119+
return out;
120+
}, {});
121+
122+
ctx.removeChild(m);
123+
return this._real = real;
124+
}
125+
}
126+
127+
/**
128+
* Parses media query expression and returns breakpoint
129+
* options
130+
* @param {String} mq Media Query expression
131+
* @return {Breakpoint}
132+
*/
133+
function parse(mq) {
134+
var feature, out = {}, empty = true;
135+
mq.replace(/\(\s*([\w\-]+)\s*:\s*(.+?)\)/g, function(str, feature, value) {
136+
feature = feature.trim();
137+
if (allowedFeatures.indexOf(feature) !== -1) {
138+
empty = false;
139+
out[feature] = value.trim();
140+
}
141+
return '';
142+
});
143+
144+
return empty ? null : new Breakpoint(mq, out);
145+
}
146+
147+
function readCSSOM(stylesheet, breakpoints=[]) {
148+
var rules = stylesheet.rules || stylesheet.cssRules;
149+
console.log('Processing', stylesheet.href || stylesheet);
150+
151+
// Have Found Media Query?
152+
var found = false;
153+
154+
for (var i = 0, il = rules.length; i < il; i++) {
155+
if (rules[i].media) {
156+
var cssMediaList = rules[i].media;
157+
found = true;
158+
for (var j = 0, jl = cssMediaList.length; j < jl; j++) {
159+
var mediaRule = parse(cssMediaList[j]);
160+
if(mediaRule) breakpoints.push(mediaRule);
161+
}
162+
}
163+
164+
if (rules[i].styleSheet) {
165+
readCSSOM(rules[i].styleSheet, breakpoints);
166+
}
167+
}
168+
169+
if(!found) console.warn('Did not find any media queries in', stylesheet.href || stylesheet);
170+
if(breakpoints.length > 0) console.log('Found', breakpoints.length, ' breakpoints in ', stylesheet.href || stylesheet);
171+
172+
return breakpoints;
173+
}
174+
175+
/**
176+
* Filters given breakpoints list and leaves unique items only
177+
* @param {Array} breakpoints
178+
* @return {Array}
179+
*/
180+
function unique(breakpoints) {
181+
var lookup = {};
182+
return breakpoints.filter(b => !b || lookup[b.id] ? false : lookup[b.id] = true);
183+
}
184+
185+
function normalizeName(str) {
186+
return str.replace(/\-([a-z])/g, (str, ch) => ch.toUpperCase());
187+
}
188+
189+
/**
190+
* Optimizes breakpoints list: keeps only ones with unique width
191+
* @param {Array} breakpoints
192+
* @return {Array}
193+
*/
194+
function optimize(breakpoints) {
195+
var lookup = {}
196+
return breakpoints.reduce((out, bp) => {
197+
var width = bp.smallest;
198+
if (!lookup[width]) {
199+
lookup[width] = true;
200+
out.push(bp);
201+
}
202+
return out;
203+
}, []);
204+
}

scripts/re-view.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
'use strict';
2-
import {default as reView, dispatch, subscribe, findBreakpoints, getStateValue, UI, APP, DONATION} from 'livestyle-re-view';
2+
import {default as reView, dispatch, subscribe, getStateValue, UI, APP, DONATION} from 'livestyle-re-view';
33
import {throttle} from './lib/utils';
44
import * as donation from './lib/donation';
55

6+
import findBreakpoints from './lib/find-breakpoints';
7+
68
const storage = chrome.storage.sync;
79
const storageKey = 're-view2';
810
const donatedKey = 'donated';

0 commit comments

Comments
 (0)