From c4b84589e118e72eb3d3031cdd77f1ec319be1fd Mon Sep 17 00:00:00 2001 From: James Macmillan Date: Mon, 21 Oct 2019 13:57:51 +0100 Subject: [PATCH 1/6] 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. --- package.json | 2 + scripts/lib/find-breakpoints.js | 204 ++++++++++++++++++++++++++++++++ scripts/re-view.js | 4 +- 3 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 scripts/lib/find-breakpoints.js diff --git a/package.json b/package.json index 9cc0e4b..4d15ff2 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,8 @@ "main": "index.js", "scripts": { "publish": "NODE_ENV=production gulp pack", + "watch": "gulp watch", + "build": "gulp", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { diff --git a/scripts/lib/find-breakpoints.js b/scripts/lib/find-breakpoints.js new file mode 100644 index 0000000..0d05118 --- /dev/null +++ b/scripts/lib/find-breakpoints.js @@ -0,0 +1,204 @@ +/** + * Scans media query breakpoints in given CSSStylesheet and returns + * them as array + */ +'use strict'; +const allowedFeatures = ['min-width', 'max-width']; + +export default function(doc=document, options={}) { + + var list = Array.from(doc.styleSheets); + console.log('list:',list); + return Promise.all(list.map(ss => { + // NB Check for own `.cssRules` property to bypass Chrome security + // with external stylesheets + // try...catch because browser may not able to enumerate rules for cross-domain sheets + + try { + const rules = ss.rules || ss.cssRules; + if (rules) return readCSSOM(ss); + } catch (e) { + console.warn("Can't read the css rules of: " + ss.href, e); + } + + // Rules are not available: most likely CSS was loaded + // from another domain and browser blocked access to CSSOM + // according to same-origin policy. + // Try to use external loader, if available + if (options.loadCSS) { + return options.loadCSS(ss.href).then(readCSSOM); + } + + return null; + })) + // flatten nested arrays of breakpoints + .then(values => [].concat(...values.filter(Boolean))) + .then((array) => { + console.log('Making unique', array); + var uniqueArray = unique(array); + console.log('Unique', uniqueArray); + return uniqueArray; + }) + .then(optimize) + .then(bp => bp.sort((a, b) => a.smallest - b.smallest)); +}; + +class Breakpoint { + constructor(query, features) { + this._real = null; + this._id = null; + this._features = null; + this._query = query.trim(); + this.features = features; + } + + get id() { + if (this._id === null && this.features.length) { + this._id = 'bp_' + this.features.map(f => f + this[f]).join('__'); + } + + return this._id; + } + + get query() { + return this._query; + } + + get features() { + if (!this._features) { + this._features = Object.keys(this).filter(key => key[0] !== '_').sort(); + } + + return this._features; + } + + set features(value={}) { + this._id = this._features = this._real = null; + Object.keys(value).forEach(feature => this[normalizeName(feature)] = value[feature]); + } + + /** + * Returns smallest feature size for current breakpoint + * @type {Number} + */ + get smallest() { + var smallest = this.features.reduce((prev, f) => Math.min(prev, this.real(f)), Number.POSITIVE_INFINITY); + return smallest !== Number.POSITIVE_INFINITY ? smallest : 0; + } + + /** + * Returns real size (in pixels) of given feature + * @param {String} feature + */ + real(feature) { + if (!this._real) { + this.measure(); + } + + return this._real[normalizeName(feature)]; + } + + /** + * Measures real feature's dimentions + * @param {Element} ctx Optional content element where + * features should be measured + * @return {Object} Real feature sizes + */ + measure(ctx=document.body) { + var m = document.createElement('div'); + m.style.cssText = 'position:absolute;padding:0;margin:0;top:0;left:0;height:0;'; + ctx.appendChild(m); + + var real = this.features.reduce((out, feature) => { + if (typeof this[feature] === 'number') { + out[feature] = this[feature]; + } else { + m.style.width = this[feature]; + out[feature] = m.offsetWidth; + } + return out; + }, {}); + + ctx.removeChild(m); + return this._real = real; + } +} + +/** + * Parses media query expression and returns breakpoint + * options + * @param {String} mq Media Query expression + * @return {Breakpoint} + */ +function parse(mq) { + var feature, out = {}, empty = true; + mq.replace(/\(\s*([\w\-]+)\s*:\s*(.+?)\)/g, function(str, feature, value) { + feature = feature.trim(); + if (allowedFeatures.indexOf(feature) !== -1) { + empty = false; + out[feature] = value.trim(); + } + return ''; + }); + + return empty ? null : new Breakpoint(mq, out); +} + +function readCSSOM(stylesheet, breakpoints=[]) { + var rules = stylesheet.rules || stylesheet.cssRules; + console.log('Processing', stylesheet.href || stylesheet); + + // Have Found Media Query? + var found = false; + + for (var i = 0, il = rules.length; i < il; i++) { + if (rules[i].media) { + var cssMediaList = rules[i].media; + found = true; + for (var j = 0, jl = cssMediaList.length; j < jl; j++) { + var mediaRule = parse(cssMediaList[j]); + if(mediaRule) breakpoints.push(mediaRule); + } + } + + if (rules[i].styleSheet) { + readCSSOM(rules[i].styleSheet, breakpoints); + } + } + + if(!found) console.warn('Did not find any media queries in', stylesheet.href || stylesheet); + if(breakpoints.length > 0) console.log('Found', breakpoints.length, ' breakpoints in ', stylesheet.href || stylesheet); + + return breakpoints; +} + +/** + * Filters given breakpoints list and leaves unique items only + * @param {Array} breakpoints + * @return {Array} + */ +function unique(breakpoints) { + var lookup = {}; + return breakpoints.filter(b => !b || lookup[b.id] ? false : lookup[b.id] = true); +} + +function normalizeName(str) { + return str.replace(/\-([a-z])/g, (str, ch) => ch.toUpperCase()); +} + +/** + * Optimizes breakpoints list: keeps only ones with unique width + * @param {Array} breakpoints + * @return {Array} + */ +function optimize(breakpoints) { + var lookup = {} + return breakpoints.reduce((out, bp) => { + var width = bp.smallest; + if (!lookup[width]) { + lookup[width] = true; + out.push(bp); + } + return out; + }, []); +} diff --git a/scripts/re-view.js b/scripts/re-view.js index 1379975..9251098 100644 --- a/scripts/re-view.js +++ b/scripts/re-view.js @@ -1,8 +1,10 @@ 'use strict'; -import {default as reView, dispatch, subscribe, findBreakpoints, getStateValue, UI, APP, DONATION} from 'livestyle-re-view'; +import {default as reView, dispatch, subscribe, getStateValue, UI, APP, DONATION} from 'livestyle-re-view'; import {throttle} from './lib/utils'; import * as donation from './lib/donation'; +import findBreakpoints from './lib/find-breakpoints'; + const storage = chrome.storage.sync; const storageKey = 're-view2'; const donatedKey = 'donated'; From 94f02121f959d2fa9844e73ee76dcf39719f5e53 Mon Sep 17 00:00:00 2001 From: James Macmillan Date: Mon, 21 Oct 2019 14:03:01 +0100 Subject: [PATCH 2/6] Update gitignore --- .gitignore | 198 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) diff --git a/.gitignore b/.gitignore index e9bcd3d..8b25c21 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,201 @@ node_modules out .DS_Store + +# Packed Extension and Certificates +*.crx +*.pem + +# Created by https://www.gitignore.io/api/node +# Edit at https://www.gitignore.io/?templates=node + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# react / gatsby +public/ + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# End of https://www.gitignore.io/api/node + + + +# Created by https://www.gitignore.io/api/jetbrains +# Edit at https://www.gitignore.io/?templates=jetbrains + +### JetBrains ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### JetBrains Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +.idea/**/sonarlint/ + +# SonarQube Plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator/ + +# End of https://www.gitignore.io/api/jetbrains + From 82007ebe3522c31540069941ced7028f1aca8b73 Mon Sep 17 00:00:00 2001 From: James Macmillan Date: Mon, 21 Oct 2019 14:06:17 +0100 Subject: [PATCH 3/6] 2.1.8 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index e4560a8..bc4bb36 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "re-view-chrome", - "version": "2.1.7", + "version": "2.1.8", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 4d15ff2..f5c29f1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "re-view-chrome", - "version": "2.1.7", + "version": "2.1.8", "description": "Emmet Re:view extension for Google Chrome", "main": "index.js", "scripts": { From 999d67cbd86ea06c142f597cef86ca2b08f09c0e Mon Sep 17 00:00:00 2001 From: James Macmillan Date: Mon, 21 Oct 2019 14:06:27 +0100 Subject: [PATCH 4/6] Update readme.md --- readme.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 readme.md diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..5d6055f --- /dev/null +++ b/readme.md @@ -0,0 +1,14 @@ +# Emmet Review (2) +https://chrome.google.com/webstore/detail/emmet-review/epejoicbhllgiimigokgjdoijnpaphdp?hl=en + +Fork from: https://github.com/PacktDev/re-view-chrome + +# Local Development +1. Clone +2. `npm i` +3. `npm run watch` +4. Goto `chrome://extensions` +5. Load unpacked extension +6. Load the `out` folder + +When done, pack, do whatever, happy days. From 79f667a657a49ca12943d7199470d92e5596c453 Mon Sep 17 00:00:00 2001 From: James Macmillan Date: Mon, 21 Oct 2019 14:21:49 +0100 Subject: [PATCH 5/6] Update package.json: Add 8.10.0 as engine as that's the version I found that works. 10.x was no good, neither was 12.x Add self-contributor so that people can get in touch Sort run scripts --- package.json | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index f5c29f1..b5947e3 100644 --- a/package.json +++ b/package.json @@ -3,11 +3,12 @@ "version": "2.1.8", "description": "Emmet Re:view extension for Google Chrome", "main": "index.js", + "engines" : { "node" : "~8.10.0" }, "scripts": { - "publish": "NODE_ENV=production gulp pack", - "watch": "gulp watch", "build": "gulp", - "test": "echo \"Error: no test specified\" && exit 1" + "publish": "NODE_ENV=production gulp pack", + "test": "echo \"Error: no test specified\" && exit 1", + "watch": "gulp watch" }, "repository": { "type": "git", @@ -20,6 +21,13 @@ "view" ], "author": "Sergey Chikuyonok ", + "contributors": [ + { + "name": "JamesCodesThings", + "email": "jamescodesthings@gmail.com", + "url": "https://github.com/jamescodesthings" + } + ], "license": "ISC", "bugs": { "url": "https://github.com/emmetio/re-view-chrome/issues" From fc38beabdb4fbf227944173bd28e4170c7d147fe Mon Sep 17 00:00:00 2001 From: James Macmillan Date: Mon, 21 Oct 2019 14:33:53 +0100 Subject: [PATCH 6/6] Remove old console.logs --- scripts/lib/find-breakpoints.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scripts/lib/find-breakpoints.js b/scripts/lib/find-breakpoints.js index 0d05118..405dcf5 100644 --- a/scripts/lib/find-breakpoints.js +++ b/scripts/lib/find-breakpoints.js @@ -8,7 +8,6 @@ const allowedFeatures = ['min-width', 'max-width']; export default function(doc=document, options={}) { var list = Array.from(doc.styleSheets); - console.log('list:',list); return Promise.all(list.map(ss => { // NB Check for own `.cssRules` property to bypass Chrome security // with external stylesheets @@ -34,9 +33,7 @@ export default function(doc=document, options={}) { // flatten nested arrays of breakpoints .then(values => [].concat(...values.filter(Boolean))) .then((array) => { - console.log('Making unique', array); var uniqueArray = unique(array); - console.log('Unique', uniqueArray); return uniqueArray; }) .then(optimize) @@ -146,7 +143,6 @@ function parse(mq) { function readCSSOM(stylesheet, breakpoints=[]) { var rules = stylesheet.rules || stylesheet.cssRules; - console.log('Processing', stylesheet.href || stylesheet); // Have Found Media Query? var found = false; @@ -167,7 +163,6 @@ function readCSSOM(stylesheet, breakpoints=[]) { } if(!found) console.warn('Did not find any media queries in', stylesheet.href || stylesheet); - if(breakpoints.length > 0) console.log('Found', breakpoints.length, ' breakpoints in ', stylesheet.href || stylesheet); return breakpoints; }