diff --git a/src/auth.js b/src/auth.js index bd4fa28..0729e65 100644 --- a/src/auth.js +++ b/src/auth.js @@ -1,8 +1,6 @@ -import { - trackEvent -} from './analytics'; - -import firebase from 'firebase/app' +import { trackEvent } from './analytics'; +import firebase from 'firebase/app'; +import { log } from './utils'; export const auth = { logout() { @@ -24,15 +22,15 @@ export const auth = { return firebase .auth() .signInWithPopup(provider) - .then(function () { + .then(function() { trackEvent('fn', 'loggedIn', providerName); // Save to recommend next time window.db.local.set({ lastAuthProvider: providerName }); }) - .catch(function (error) { - utils.log(error); + .catch(function(error) { + log(error); if (error.code === 'auth/account-exists-with-different-credential') { alert( 'You have already signed up with the same email using different social login' @@ -40,4 +38,4 @@ export const auth = { } }); } -} +}; diff --git a/src/components/AddLibrary.jsx b/src/components/AddLibrary.jsx index 1ee76a0..edeff8c 100644 --- a/src/components/AddLibrary.jsx +++ b/src/components/AddLibrary.jsx @@ -81,7 +81,7 @@ export default class AddLibrary extends Component { <small>Powered by cdnjs</small> </div> <div style="margin:20px 0;"> - Choose from popular libraries: + Choose from popular libraries:{' '} <select name="" id="js-add-library-select" diff --git a/src/components/MainHeader.jsx b/src/components/MainHeader.jsx index 2cb9da2..7b2b102 100644 --- a/src/components/MainHeader.jsx +++ b/src/components/MainHeader.jsx @@ -34,7 +34,7 @@ export function MainHeader(props) { class="flex-v-center hint--rounded hint--bottom-left" aria-label="Add a JS/CSS library" > - Add library + Add library{' '} <span id="js-external-lib-count" style={`display:${props.externalLibCount ? 'inline' : 'none'}`} diff --git a/src/components/SavedItemPane.jsx b/src/components/SavedItemPane.jsx index 14ab258..7eb8c61 100644 --- a/src/components/SavedItemPane.jsx +++ b/src/components/SavedItemPane.jsx @@ -3,6 +3,7 @@ import { log, getHumanDate } from '../utils'; import { trackEvent } from '../analytics'; import { itemService } from '../itemService'; import { alertsService } from '../notifications'; +import { deferred } from '../deferred'; export default class SavedItemPane extends Component { constructor(props) { @@ -23,6 +24,11 @@ export default class SavedItemPane extends Component { }); } } + componentDidUpdate(prevProps) { + if (this.props.isOpen && !prevProps.isOpen) { + window.searchInput.value = ''; + } + } onCloseIntent() { this.props.closeHandler(); } @@ -124,8 +130,7 @@ export default class SavedItemPane extends Component { } else { d.resolve(); } - // FIXME: Move from here - // toggleSavedItemsPane(false); + this.props.closeHandler(); return d.promise; } @@ -216,7 +221,6 @@ export default class SavedItemPane extends Component { </div> </div> <input - type="" id="searchInput" class="search-input" onInput={this.searchInputHandler.bind(this)} diff --git a/src/components/Settings.jsx b/src/components/Settings.jsx index 1638816..657a77a 100644 --- a/src/components/Settings.jsx +++ b/src/components/Settings.jsx @@ -19,7 +19,7 @@ function CheckboxSetting({ checked={pref} onChange={onChange} data-setting={name} - /> + />{' '} {label} </label> ); diff --git a/src/components/app.jsx b/src/components/app.jsx index d36200a..f0b5094 100644 --- a/src/components/app.jsx +++ b/src/components/app.jsx @@ -234,6 +234,7 @@ export default class App extends Component { this.toggleLayout( this.state.currentItem.layoutMode || this.state.prefs.layoutMode ); + this.updateExternalLibCount(); this.contentWrap.refreshEditor(); } // Creates a new item with passed item's contents @@ -822,7 +823,9 @@ export default class App extends Component { } codepenBtnClickHandler(e) { if (this.state.currentItem.cssMode === CssModes.ACSS) { - alert("Oops! CodePen doesn't supports Atomic CSS currently."); + alert( + "Oops! CodePen doesn't supports Atomic CSS currently. \nHere is something you can still do -> https://medium.com/web-maker/sharing-your-atomic-css-work-on-codepen-a402001b26ab" + ); e.preventDefault(); return; } @@ -934,6 +937,9 @@ export default class App extends Component { editorFocusHandler(editor) { this.editorWithFocus = editor; } + modalOverlayClickHandler() { + this.closeAllOverlays(); + } render() { return ( @@ -1103,7 +1109,10 @@ export default class App extends Component { /> <Portal into="body"> - <div class="modal-overlay" /> + <div + class="modal-overlay" + onClick={this.modalOverlayClickHandler.bind(this)} + /> </Portal> <Icons /> diff --git a/src/db.js b/src/db.js index 4d66a1f..e46a9ec 100644 --- a/src/db.js +++ b/src/db.js @@ -1,13 +1,9 @@ import './firebaseInit'; import firebase from 'firebase/app'; import 'firebase/firestore'; -import { - deferred -} from './deferred'; -import { - trackEvent -} from './analytics'; - +import { deferred } from './deferred'; +import { trackEvent } from './analytics'; +import { log } from './utils'; (() => { const FAUX_DELAY = 1; @@ -54,7 +50,7 @@ import { if (dbPromise) { return dbPromise; } - utils.log('Initializing firestore'); + log('Initializing firestore'); dbPromise = new Promise((resolve, reject) => { if (db) { return resolve(db); @@ -62,17 +58,17 @@ import { return firebase .firestore() .enablePersistence() - .then(function () { + .then(function() { // Initialize Cloud Firestore through firebase db = firebase.firestore(); // const settings = { // timestampsInSnapshots: true // }; // db.settings(settings); - utils.log('firebase db ready', db); + log('firebase db ready', db); resolve(db); }) - .catch(function (err) { + .catch(function(err) { reject(err.code); if (err.code === 'failed-precondition') { // Multiple tabs open, persistence can only be enabled @@ -95,7 +91,8 @@ import { const d = deferred(); // Will be chrome.storage.sync in extension environment, // otherwise will fallback to localstorage - dbSyncAlias.get({ + dbSyncAlias.get( + { lastSeenVersion: '' }, result => { @@ -111,18 +108,17 @@ import { // Setting the `lastSeenVersion` in localStorage(sync for extension) always // because next time we need to fetch it irrespective of the user being // logged in or out quickly from local storage. - dbSyncAlias.set({ + dbSyncAlias.set( + { lastSeenVersion: version }, - function () {} + function() {} ); if (window.user) { const remoteDb = await getDb(); - remoteDb - .doc(`users/${window.user.uid}`) - .update({ - lastSeenVersion: version - }); + remoteDb.doc(`users/${window.user.uid}`).update({ + lastSeenVersion: version + }); } } @@ -133,9 +129,12 @@ import { .get() .then(doc => { if (!doc.exists) - return remoteDb.doc(`users/${userId}`).set({}, { - merge: true - }); + return remoteDb.doc(`users/${userId}`).set( + {}, + { + merge: true + } + ); const user = doc.data(); Object.assign(window.user, user); return user; diff --git a/src/itemService.js b/src/itemService.js index 28120ab..c8e0647 100644 --- a/src/itemService.js +++ b/src/itemService.js @@ -1,9 +1,7 @@ -import { - deferred -} from './deferred'; +import { deferred } from './deferred'; +import { log } from 'util'; export const itemService = { - async getItem(id) { var remoteDb = await window.db.getDb(); return remoteDb @@ -32,11 +30,11 @@ export const itemService = { }, async getAllItems() { - var t = Date.now() + var t = Date.now(); var d = deferred(); let itemIds = await this.getUserItemIds(); itemIds = Object.getOwnPropertyNames(itemIds || {}); - utils.log('itemids', itemIds); + log('itemids', itemIds); if (!itemIds.length) { d.resolve([]); @@ -46,16 +44,19 @@ export const itemService = { remoteDb .collection('items') .where('createdBy', '==', window.user.uid) - .onSnapshot(function (querySnapshot) { - querySnapshot.forEach(function (doc) { - items.push(doc.data()); - }); - utils.log('Items fetched in ', Date.now() - t, 'ms') + .onSnapshot( + function(querySnapshot) { + querySnapshot.forEach(function(doc) { + items.push(doc.data()); + }); + log('Items fetched in ', Date.now() - t, 'ms'); - d.resolve(items); - }, function () { - d.resolve([]) - }); + d.resolve(items); + }, + function() { + d.resolve([]); + } + ); return d.promise; }, @@ -91,7 +92,7 @@ export const itemService = { } if (window.user) { var remoteDb = await window.db.getDb(); - utils.log(`Starting to save item ${id}`); + log(`Starting to save item ${id}`); item.createdBy = window.user.uid; remotePromise = remoteDb .collection('items') @@ -100,7 +101,7 @@ export const itemService = { merge: true }) .then(arg => { - utils.log('Document written', arg); + log('Document written', arg); d.resolve(); }) .catch(d.reject); @@ -120,10 +121,11 @@ export const itemService = { // save new items window.db.local.set(items, d.resolve); // Push in new item IDs - window.db.local.get({ + window.db.local.get( + { items: {} }, - function (result) { + function(result) { /* eslint-disable guard-for-in */ for (var id in items) { result.items[id] = true; @@ -163,24 +165,25 @@ export const itemService = { return d.promise; } const remoteDb = await window.db.getDb(); - utils.log(`Starting to save item ${id}`); + log(`Starting to save item ${id}`); return remoteDb .collection('items') .doc(id) .delete() .then(arg => { - utils.log('Document removed', arg); + log('Document removed', arg); }) - .catch(error => utils.log(error)); + .catch(error => log(error)); }, async setItemForUser(itemId) { // When not logged in if (!window.user) { - return window.db.local.get({ + return window.db.local.get( + { items: {} }, - function (result) { + function(result) { result.items[itemId] = true; window.db.local.set({ items: result.items @@ -196,20 +199,21 @@ export const itemService = { [`items.${itemId}`]: true }) .then(arg => { - utils.log(`Item ${itemId} set for user`, arg); + log(`Item ${itemId} set for user`, arg); window.user.items = window.user.items || {}; window.user.items[itemId] = true; }) - .catch(error => utils.log(error)); + .catch(error => log(error)); }, async unsetItemForUser(itemId) { // When not logged in if (!window.user) { - return window.db.local.get({ + return window.db.local.get( + { items: {} }, - function (result) { + function(result) { delete result.items[itemId]; window.db.local.set({ items: result.items @@ -226,8 +230,8 @@ export const itemService = { }) .then(arg => { delete window.user.items[itemId]; - utils.log(`Item ${itemId} unset for user`, arg); + log(`Item ${itemId} unset for user`, arg); }) - .catch(error => utils.log(error)); + .catch(error => log(error)); } -} +}; diff --git a/src/takeScreenshot.js b/src/takeScreenshot.js index 8f3c9fe..8e80a9a 100644 --- a/src/takeScreenshot.js +++ b/src/takeScreenshot.js @@ -1,4 +1,4 @@ -import { handleDownloadsPermission } from './utils'; +import { handleDownloadsPermission, log } from './utils'; import { trackEvent } from './analytics'; function saveScreenshot(dataURI) { @@ -59,7 +59,7 @@ function saveScreenshot(dataURI) { } function errorHandler(e) { - utils.log(e); + log(e); } // create a blob for writing to a file diff --git a/src/utils.js b/src/utils.js index 003fd37..057d576 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,18 +1,8 @@ -import { - trackEvent -} from './analytics'; +import { trackEvent } from './analytics'; -import { - computeHtml, - computeCss, - computeJs -} from './computes'; -import { - JsModes -} from './codeModes'; -import { - deferred -} from './deferred'; +import { computeHtml, computeCss, computeJs } from './computes'; +import { JsModes } from './codeModes'; +import { deferred } from './deferred'; const esprima = require('esprima'); window.DEBUG = document.cookie.indexOf('wmdebug') > -1; @@ -37,7 +27,7 @@ var alphaNum = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; * @param Selector that should match for next siblings * @return element Next element that mathes `selector` */ -Node.prototype.nextUntil = function (selector) { +Node.prototype.nextUntil = function(selector) { const siblings = Array.from(this.parentNode.querySelectorAll(selector)); const index = siblings.indexOf(this); return siblings[index + 1]; @@ -47,7 +37,7 @@ Node.prototype.nextUntil = function (selector) { * @param Selector that should match for next siblings * @return element Next element that mathes `selector` */ -Node.prototype.previousUntil = function (selector) { +Node.prototype.previousUntil = function(selector) { const siblings = Array.from(this.parentNode.querySelectorAll(selector)); const index = siblings.indexOf(this); return siblings[index - 1]; @@ -56,7 +46,7 @@ Node.prototype.previousUntil = function (selector) { // Safari doesn't have this! window.requestIdleCallback = window.requestIdleCallback || - function (fn) { + function(fn) { setTimeout(fn, 10); }; @@ -86,7 +76,7 @@ export function semverCompare(a, b) { export function generateRandomId(len) { var length = len || 10; var id = ''; - for (var i = length; i--;) { + for (var i = length; i--; ) { id += alphaNum[~~(Math.random() * alphaNum.length)]; } return id; @@ -110,9 +100,7 @@ export function log() { * Contributed by Ariya Hidayat! * @param code {string} Code to be protected from infinite loops. */ -export function addInfiniteLoopProtection(code, { - timeout -}) { +export function addInfiniteLoopProtection(code, { timeout }) { var loopId = 1; var patches = []; var varPrefix = '_wmloopvar'; @@ -120,12 +108,13 @@ export function addInfiniteLoopProtection(code, { var checkStr = `\nif (Date.now() - %d > ${timeout}) { window.top.previewException(new Error("Infinite loop")); break;}\n`; esprima.parse( - code, { + code, + { tolerant: true, range: true, jsx: true }, - function (node) { + function(node) { switch (node.type) { case 'DoWhileStatement': case 'ForStatement': @@ -167,10 +156,10 @@ export function addInfiniteLoopProtection(code, { /* eslint-disable no-param-reassign */ patches - .sort(function (a, b) { + .sort(function(a, b) { return b.pos - a.pos; }) - .forEach(function (patch) { + .forEach(function(patch) { code = code.slice(0, patch.pos) + patch.str + code.slice(patch.pos); }); @@ -182,7 +171,8 @@ export function getHumanDate(timestamp) { var d = new Date(timestamp); var retVal = d.getDate() + - ' ' + [ + ' ' + + [ 'January', 'February', 'March', @@ -204,7 +194,7 @@ export function getHumanDate(timestamp) { // create a one-time event export function once(node, type, callback) { // create event - node.addEventListener(type, function (e) { + node.addEventListener(type, function(e) { // remove event e.target.removeEventListener(type, arguments.callee); // call handler @@ -223,7 +213,8 @@ export function downloadFile(fileName, blob) { a.remove(); } if (window.IS_EXTENSION) { - chrome.downloads.download({ + chrome.downloads.download( + { url: window.URL.createObjectURL(blob), filename: fileName, saveAs: true @@ -244,13 +235,13 @@ export function writeFile(name, blob, cb) { var fileWritten = false; function getErrorHandler(type) { - return function () { + return function() { log(arguments); trackEvent('fn', 'error', type); // When there are too many write errors, show a message. writeFile.errorCount = (writeFile.errorCount || 0) + 1; if (writeFile.errorCount === 4) { - setTimeout(function () { + setTimeout(function() { alert( "Oops! Seems like your preview isn't updating. It's recommended to switch to the web app: https://webmakerapp.com/app/.\n\n If you still want to get the extension working, please try the following steps until it fixes:\n - Refresh Web Maker\n - Restart browser\n - Update browser\n - Reinstall Web Maker (don't forget to export all your creations from saved items pane (click the OPEN button) before reinstalling)\n\nIf nothing works, please tweet out to @webmakerApp." ); @@ -264,12 +255,13 @@ export function writeFile(name, blob, cb) { window.webkitRequestFileSystem( window.TEMPORARY, 1024 * 1024 * 5, - function (fs) { + function(fs) { fs.root.getFile( - name, { + name, + { create: true }, - function (fileEntry) { + function(fileEntry) { fileEntry.createWriter(fileWriter => { function onWriteComplete() { if (fileWritten) { @@ -302,7 +294,7 @@ export function loadJS(src) { script.src = src; script.async = true; ref.parentNode.insertBefore(script, ref); - script.onload = function () { + script.onload = function() { d.resolve(); }; return d.promise; @@ -314,12 +306,12 @@ export function getCompleteHtml(html, css, js, item, isForExport) { } var externalJs = item.externalLibs.js .split('\n') - .reduce(function (scripts, url) { + .reduce(function(scripts, url) { return scripts + (url ? '\n<script src="' + url + '"></script>' : ''); }, ''); var externalCss = item.externalLibs.css .split('\n') - .reduce(function (links, url) { + .reduce(function(links, url) { return ( links + (url ? '\n<link rel="stylesheet" href="' + url + '"></link>' : '') @@ -344,29 +336,29 @@ export function getCompleteHtml(html, css, js, item, isForExport) { if (!isForExport) { contents += '<script src="' + - (chrome.extension ? - chrome.extension.getURL('lib/screenlog.js') : - `${location.origin}${BASE_PATH}/lib/screenlog.js`) + + (chrome.extension + ? chrome.extension.getURL('lib/screenlog.js') + : `${location.origin}${BASE_PATH}/lib/screenlog.js`) + '"></script>'; } if (item.jsMode === JsModes.ES6) { contents += '<script src="' + - (chrome.extension ? - chrome.extension.getURL('lib/transpilers/babel-polyfill.min.js') : - `${ + (chrome.extension + ? chrome.extension.getURL('lib/transpilers/babel-polyfill.min.js') + : `${ location.origin - }${BASE_PATH}/lib/transpilers/babel-polyfill.min.js`) + + }${BASE_PATH}/lib/transpilers/babel-polyfill.min.js`) + '"></script>'; } if (typeof js === 'string') { contents += '<script>\n' + js + '\n//# sourceURL=userscript.js'; } else { - var origin = chrome.i18n.getMessage() ? - `chrome-extension://${chrome.i18n.getMessage('@@extension_id')}` : - `${location.origin}`; + var origin = chrome.i18n.getMessage() + ? `chrome-extension://${chrome.i18n.getMessage('@@extension_id')}` + : `${location.origin}`; contents += '<script src="' + `filesystem:${origin}/temporary/script.js` + '">'; } @@ -379,10 +371,10 @@ export function saveAsHtml(item) { var htmlPromise = computeHtml(item.html, item.htmlMode); var cssPromise = computeCss(item.css, item.cssMode); var jsPromise = computeJs(item.js, item.jsMode, false); - Promise.all([htmlPromise, cssPromise, jsPromise]).then(function (result) { - var html = result[0], - css = result[1], - js = result[2]; + Promise.all([htmlPromise, cssPromise, jsPromise]).then(function(result) { + var html = result[0].code, + css = result[1].code, + js = result[2].code; var fileContent = getCompleteHtml(html, css, js, item, true); @@ -417,17 +409,19 @@ export function handleDownloadsPermission() { d.resolve(); return d.promise; } - chrome.permissions.contains({ + chrome.permissions.contains( + { permissions: ['downloads'] }, - function (result) { + function(result) { if (result) { d.resolve(); } else { - chrome.permissions.request({ + chrome.permissions.request( + { permissions: ['downloads'] }, - function (granted) { + function(granted) { if (granted) { trackEvent('fn', 'downloadsPermGiven'); d.resolve();