From a780b95a9561e57beb3b0b5dc22fbd43ac72cd9b Mon Sep 17 00:00:00 2001 From: Sam Stern Date: Tue, 13 Feb 2018 13:48:46 -0800 Subject: [PATCH 1/3] Add Travis CI for linting --- .eslintrc.json | 39 ++++ .travis.yml | 9 + README.md | 4 + database/scripts/main.js | 32 +-- firestore/.eslintrc.json | 8 + firestore/package.json | 1 - firestore/scripts/FriendlyEats.Data.js | 35 +-- firestore/scripts/FriendlyEats.Mock.js | 42 ++-- firestore/scripts/FriendlyEats.View.js | 292 +++++++++++++------------ firestore/scripts/FriendlyEats.js | 18 +- lerna.json | 7 + messaging/.eslintrc.json | 5 + messaging/firebase-messaging-sw.js | 8 +- package.json | 19 ++ scripts/test.sh | 4 + 15 files changed, 319 insertions(+), 204 deletions(-) create mode 100644 .eslintrc.json create mode 100644 .travis.yml create mode 100644 firestore/.eslintrc.json create mode 100644 lerna.json create mode 100644 messaging/.eslintrc.json create mode 100644 package.json create mode 100755 scripts/test.sh diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000..b2f1da843 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,39 @@ +{ + "env": { + "browser": true + }, + "extends": "eslint:recommended", + "rules": { + "indent": [ + "error", + 2 + ], + "linebreak-style": [ + "error", + "unix" + ], + "quotes": [ + "error", + "single" + ], + "semi": [ + "error", + "always" + ], + "curly": [ + "error", + "all" + ], + "consistent-this": [ + "error", + "that" + ], + "no-console": 0, + "no-unused-vars": 0 + }, + "globals": { + "chrome": true, + "firebase": true, + "Promise": true + } +} \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..f93e855ab --- /dev/null +++ b/.travis.yml @@ -0,0 +1,9 @@ +language: node_js +node_js: + - "7" +install: + - npm install -g lerna + - npm install -g eslint + - lerna bootstrap +script: + - ./scripts/test.sh \ No newline at end of file diff --git a/README.md b/README.md index c8ad1868e..d2df42ef6 100644 --- a/README.md +++ b/README.md @@ -34,3 +34,7 @@ Please read and follow the steps in the [CONTRIBUTING.md](CONTRIBUTING.md) ## License See [LICENSE](LICENSE) + +## Build Status + +[![Build Status](https://travis-ci.org/firebase/quickstart-js.svg?branch=master)](https://travis-ci.org/firebase/quickstart-js) \ No newline at end of file diff --git a/database/scripts/main.js b/database/scripts/main.js index 44ba878bb..a501e0bda 100644 --- a/database/scripts/main.js +++ b/database/scripts/main.js @@ -15,6 +15,8 @@ */ 'use strict'; +var componentHandler = componentHandler || undefined; + // Shortcuts to DOM Elements. var messageForm = document.getElementById('message-form'); var messageInput = document.getElementById('new-post-message'); @@ -162,7 +164,7 @@ function createPostElement(postId, title, text, author, authorId, authorPic) { // [END post_value_event_listener] // Listen for the starred status. - var starredStatusRef = firebase.database().ref('posts/' + postId + '/stars/' + uid) + var starredStatusRef = firebase.database().ref('posts/' + postId + '/stars/' + uid); starredStatusRef.on('value', function(snapshot) { updateStarredByCurrentUser(postElement, snapshot.val()); }); @@ -273,21 +275,21 @@ function startDatabaseQueries() { var author = data.val().author || 'Anonymous'; var containerElement = sectionElement.getElementsByClassName('posts-container')[0]; containerElement.insertBefore( - createPostElement(data.key, data.val().title, data.val().body, author, data.val().uid, data.val().authorPic), - containerElement.firstChild); + createPostElement(data.key, data.val().title, data.val().body, author, data.val().uid, data.val().authorPic), + containerElement.firstChild); }); - postsRef.on('child_changed', function(data) { - var containerElement = sectionElement.getElementsByClassName('posts-container')[0]; - var postElement = containerElement.getElementsByClassName('post-' + data.key)[0]; - postElement.getElementsByClassName('mdl-card__title-text')[0].innerText = data.val().title; - postElement.getElementsByClassName('username')[0].innerText = data.val().author; - postElement.getElementsByClassName('text')[0].innerText = data.val().body; - postElement.getElementsByClassName('star-count')[0].innerText = data.val().starCount; + postsRef.on('child_changed', function(data) { + var containerElement = sectionElement.getElementsByClassName('posts-container')[0]; + var postElement = containerElement.getElementsByClassName('post-' + data.key)[0]; + postElement.getElementsByClassName('mdl-card__title-text')[0].innerText = data.val().title; + postElement.getElementsByClassName('username')[0].innerText = data.val().author; + postElement.getElementsByClassName('text')[0].innerText = data.val().body; + postElement.getElementsByClassName('star-count')[0].innerText = data.val().starCount; }); postsRef.on('child_removed', function(data) { - var containerElement = sectionElement.getElementsByClassName('posts-container')[0]; - var post = containerElement.getElementsByClassName('post-' + data.key)[0]; - post.parentElement.removeChild(post); + var containerElement = sectionElement.getElementsByClassName('posts-container')[0]; + var post = containerElement.getElementsByClassName('post-' + data.key)[0]; + post.parentElement.removeChild(post); }); }; @@ -370,8 +372,8 @@ function newPostForCurrentUser(title, text) { var username = (snapshot.val() && snapshot.val().username) || 'Anonymous'; // [START_EXCLUDE] return writeNewPost(firebase.auth().currentUser.uid, username, - firebase.auth().currentUser.photoURL, - title, text); + firebase.auth().currentUser.photoURL, + title, text); // [END_EXCLUDE] }); // [END single_value_read] diff --git a/firestore/.eslintrc.json b/firestore/.eslintrc.json new file mode 100644 index 000000000..1a0c107ce --- /dev/null +++ b/firestore/.eslintrc.json @@ -0,0 +1,8 @@ +{ + "globals": { + "Navigo": true, + "mdc": true, + "importScripts": true, + "FriendlyEats": true + } +} \ No newline at end of file diff --git a/firestore/package.json b/firestore/package.json index 40df98b5a..4c096a379 100644 --- a/firestore/package.json +++ b/firestore/package.json @@ -4,7 +4,6 @@ "description": "Finished code for the Cloud Firestore codelab", "main": ".", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", "fmt": "prettier --write scripts/* styles/*" }, "author": "", diff --git a/firestore/scripts/FriendlyEats.Data.js b/firestore/scripts/FriendlyEats.Data.js index e732cb4bf..6b26f0cab 100644 --- a/firestore/scripts/FriendlyEats.Data.js +++ b/firestore/scripts/FriendlyEats.Data.js @@ -16,23 +16,25 @@ 'use strict'; FriendlyEats.prototype.addRestaurant = function(data) { - const collection = firebase.firestore().collection('restaurants'); + var collection = firebase.firestore().collection('restaurants'); return collection.add(data); }; FriendlyEats.prototype.getAllRestaurants = function(render) { - const query = firebase.firestore() - .collection('restaurants') - .orderBy('avgRating', 'desc') - .limit(50); + var query = firebase.firestore() + .collection('restaurants') + .orderBy('avgRating', 'desc') + .limit(50); this.getDocumentsInQuery(query, render); }; FriendlyEats.prototype.getDocumentsInQuery = function(query, render) { - query.onSnapshot(snapshot => { - if (!snapshot.size) return render(); + query.onSnapshot(function (snapshot) { + if (!snapshot.size) { + return render(); + } - snapshot.docChanges.forEach(change => { + snapshot.docChanges.forEach(function(change) { if (change.type === 'added') { render(change.doc); } @@ -45,7 +47,7 @@ FriendlyEats.prototype.getRestaurant = function(id) { }; FriendlyEats.prototype.getFilteredRestaurants = function(filters, render) { - let query = firebase.firestore().collection('restaurants'); + var query = firebase.firestore().collection('restaurants'); if (filters.category !== 'Any') { query = query.where('category', '==', filters.category); @@ -62,21 +64,22 @@ FriendlyEats.prototype.getFilteredRestaurants = function(filters, render) { if (filters.sort === 'Rating') { query = query.orderBy('avgRating', 'desc'); } else if (filters.sort === 'Reviews') { + query = query.orderBy('numRatings', 'desc'); } this.getDocumentsInQuery(query, render); }; FriendlyEats.prototype.addRating = function(restaurantID, rating) { - const collection = firebase.firestore().collection('restaurants'); - const document = collection.doc(restaurantID); + var collection = firebase.firestore().collection('restaurants'); + var document = collection.doc(restaurantID); - return document.collection('ratings').add(rating).then(() => { - return firebase.firestore().runTransaction(transaction => { - return transaction.get(document).then(doc => { - const data = doc.data(); + return document.collection('ratings').add(rating).then(function() { + return firebase.firestore().runTransaction(function(transaction) { + return transaction.get(document).then(function(doc) { + var data = doc.data(); - let newAverage = + var newAverage = (data.numRatings * data.avgRating + rating.rating) / (data.numRatings + 1); diff --git a/firestore/scripts/FriendlyEats.Mock.js b/firestore/scripts/FriendlyEats.Mock.js index a968965bf..94c7998e0 100644 --- a/firestore/scripts/FriendlyEats.Mock.js +++ b/firestore/scripts/FriendlyEats.Mock.js @@ -19,29 +19,29 @@ * Adds a set of mock Restaurants to the Cloud Firestore. */ FriendlyEats.prototype.addMockRestaurants = function() { - const promises = []; + var promises = []; - for (let i = 0; i < 20; i++) { - let name = + for (var i = 0; i < 20; i++) { + var name = this.getRandomItem(this.data.words) + ' ' + this.getRandomItem(this.data.words); - let category = this.getRandomItem(this.data.categories); - let city = this.getRandomItem(this.data.cities); - let price = Math.floor(Math.random() * 4) + 1; - let photoID = Math.floor(Math.random() * 22) + 1; - let photo = `https://storage.googleapis.com/firestorequickstarts.appspot.com/food_${photoID}.png`; - let numRatings = 0; - let avgRating = 0; + var category = this.getRandomItem(this.data.categories); + var city = this.getRandomItem(this.data.cities); + var price = Math.floor(Math.random() * 4) + 1; + var photoID = Math.floor(Math.random() * 22) + 1; + var photo = 'https://storage.googleapis.com/firestorequickstarts.appspot.com/food_' + photoID + '.png'; + var numRatings = 0; + var avgRating = 0; - const promise = this.addRestaurant({ - name, - category, - price, - city, - numRatings, - avgRating, - photo + var promise = this.addRestaurant({ + name: name, + category: category, + price: price, + city: city, + numRating: numRatings, + avgRating: avgRating, + photo: photo }); if (!promise) { @@ -59,9 +59,9 @@ FriendlyEats.prototype.addMockRestaurants = function() { * Adds a set of mock Ratings to the given Restaurant. */ FriendlyEats.prototype.addMockRatings = function(restaurantID) { - const ratingPromises = []; - for (let r = 0; r < 5*Math.random(); r++) { - const rating = this.data.ratings[ + var ratingPromises = []; + for (var r = 0; r < 5*Math.random(); r++) { + var rating = this.data.ratings[ parseInt(this.data.ratings.length*Math.random()) ]; rating.userName = 'Bot (Web)'; diff --git a/firestore/scripts/FriendlyEats.View.js b/firestore/scripts/FriendlyEats.View.js index 09b463322..dc5b7a0b6 100644 --- a/firestore/scripts/FriendlyEats.View.js +++ b/firestore/scripts/FriendlyEats.View.js @@ -17,7 +17,7 @@ FriendlyEats.prototype.initTemplates = function() { this.templates = {}; - document.querySelectorAll('.template').forEach(el => { + document.querySelectorAll('.template').forEach(function(el) { this.templates[el.getAttribute('id')] = el; }); }; @@ -31,37 +31,41 @@ FriendlyEats.prototype.viewList = function(filters, filter_description) { filter_description = 'any type of food with any price in any city.'; } - const mainEl = this.renderTemplate('main-adjusted'); - const headerEl = this.renderTemplate('header-base', { + var mainEl = this.renderTemplate('main-adjusted'); + var headerEl = this.renderTemplate('header-base', { hasSectionHeader: true }); this.replaceElement( - headerEl.querySelector('#section-header'), - this.renderTemplate('filter-display', { filter_description }) + headerEl.querySelector('#section-header'), + this.renderTemplate('filter-display', { + filter_description: filter_description + }) ); this.replaceElement(document.querySelector('.header'), headerEl); this.replaceElement(document.querySelector('main'), mainEl); - headerEl.querySelector('#show-filters').addEventListener('click', () => { + headerEl.querySelector('#show-filters').addEventListener('click', function() { this.dialogs.filter.show(); }); - const renderResults = doc => { + var renderResults = function(doc) { if (!doc) { - const headerEl = this.renderTemplate('header-base', { + var headerEl = this.renderTemplate('header-base', { hasSectionHeader: true }); - - const noResultsEl = this.renderTemplate('no-results'); + + var noResultsEl = this.renderTemplate('no-results'); this.replaceElement( - headerEl.querySelector('#section-header'), - this.renderTemplate('filter-display', { filter_description }) + headerEl.querySelector('#section-header'), + this.renderTemplate('filter-display', { + filter_description: filter_description + }) ); - headerEl.querySelector('#show-filters').addEventListener('click', () => { + headerEl.querySelector('#show-filters').addEventListener('click', function() { this.dialogs.filter.show(); }); @@ -69,13 +73,13 @@ FriendlyEats.prototype.viewList = function(filters, filter_description) { this.replaceElement(document.querySelector('main'), noResultsEl); return; } - const data = doc.data(); + var data = doc.data(); data['.id'] = doc.id; - data['go_to_restaurant'] = () => { - this.router.navigate(`/restaurants/${doc.id}`); + data['go_to_restaurant'] = function() { + this.router.navigate('/restaurants/' + doc.id); }; - const el = this.renderTemplate('restaurant-card', data); + var el = this.renderTemplate('restaurant-card', data); el.querySelector('.rating').append(this.renderRating(data.avgRating)); el.querySelector('.price').append(this.renderPrice(data.price)); @@ -84,40 +88,43 @@ FriendlyEats.prototype.viewList = function(filters, filter_description) { if (filters.city || filters.category || filters.price || filters.sort !== 'Rating' ) { this.getFilteredRestaurants({ - city: filters.city || 'Any', - category: filters.category || 'Any', - price: filters.price || 'Any', - sort: filters.sort + city: filters.city || 'Any', + category: filters.category || 'Any', + price: filters.price || 'Any', + sort: filters.sort }, renderResults); } else { this.getAllRestaurants(renderResults); } - const toolbar = mdc.toolbar.MDCToolbar.attachTo(document.querySelector('.mdc-toolbar')); + var toolbar = mdc.toolbar.MDCToolbar.attachTo(document.querySelector('.mdc-toolbar')); toolbar.fixedAdjustElement = document.querySelector('.mdc-toolbar-fixed-adjust'); mdc.autoInit(); }; FriendlyEats.prototype.viewSetup = function() { - const headerEl = this.renderTemplate('header-base', { + var headerEl = this.renderTemplate('header-base', { hasSectionHeader: false }); - const config = this.getFirebaseConfig(); - const noRestaurantsEl = this.renderTemplate('no-restaurants', config); + var config = this.getFirebaseConfig(); + var noRestaurantsEl = this.renderTemplate('no-restaurants', config); - const button = noRestaurantsEl.querySelector('#add_mock_data'); - let addingMockData = false; + var button = noRestaurantsEl.querySelector('#add_mock_data'); + var addingMockData = false; + + button.addEventListener('click', function(event) { + if (addingMockData) { + return; + } - button.addEventListener('click', event => { - if (addingMockData) return; addingMockData = true; event.target.style.opacity = '0.4'; event.target.innerText = 'Please wait...'; - this.addMockRestaurants().then(() => { + this.addMockRestaurants().then(function() { this.rerender(); }); }); @@ -126,42 +133,42 @@ FriendlyEats.prototype.viewSetup = function() { this.replaceElement(document.querySelector('main'), noRestaurantsEl); firebase - .firestore() - .collection('restaurants') - .limit(1) - .onSnapshot(snapshot => { - if (snapshot.size && !addingMockData) { - this.router.navigate('/'); - } - }); + .firestore() + .collection('restaurants') + .limit(1) + .onSnapshot(function(snapshot) { + if (snapshot.size && !addingMockData) { + this.router.navigate('/'); + } + }); }; FriendlyEats.prototype.initReviewDialog = function() { - const dialog = document.querySelector('#dialog-add-review'); + var dialog = document.querySelector('#dialog-add-review'); this.dialogs.add_review = new mdc.dialog.MDCDialog(dialog); - this.dialogs.add_review.listen('MDCDialog:accept', () => { - let pathname = this.getCleanPath(document.location.pathname); - let id = pathname.split('/')[2]; + this.dialogs.add_review.listen('MDCDialog:accept', function() { + var pathname = this.getCleanPath(document.location.pathname); + var id = pathname.split('/')[2]; this.addRating(id, { - rating, + rating: rating, text: dialog.querySelector('#text').value, userName: 'Anonymous (Web)', timestamp: new Date(), userId: firebase.auth().currentUser.uid - }).then(() => { + }).then(function() { this.rerender(); }); }); - let rating = 0; + var rating = 0; - dialog.querySelectorAll('.star-input i').forEach(el => { - const rate = () => { - let after = false; + dialog.querySelectorAll('.star-input i').forEach(function(el) { + var rate = function() { + var after = false; rating = 0; - [].slice.call(el.parentNode.children).forEach(child => { + [].slice.call(el.parentNode.children).forEach(function(child) { if (!after) { rating++; child.innerText = 'star'; @@ -179,43 +186,43 @@ FriendlyEats.prototype.initFilterDialog = function() { // TODO: Reset filter dialog to init state on close. this.dialogs.filter = new mdc.dialog.MDCDialog(document.querySelector('#dialog-filter-all')); - this.dialogs.filter.listen('MDCDialog:accept', () => { + this.dialogs.filter.listen('MDCDialog:accept', function() { this.updateQuery(this.filters); }); - const dialog = document.querySelector('aside'); - const pages = dialog.querySelectorAll('.page'); + var dialog = document.querySelector('aside'); + var pages = dialog.querySelectorAll('.page'); this.replaceElement( dialog.querySelector('#category-list'), - this.renderTemplate('item-list', { items: ['Any'].concat(this.data.categories) }) + this.renderTemplate('item-list', { items: ['Any'].concat(this.data.categories) }) ); this.replaceElement( - dialog.querySelector('#city-list'), - this.renderTemplate('item-list', { items: ['Any'].concat(this.data.cities) }) + dialog.querySelector('#city-list'), + this.renderTemplate('item-list', { items: ['Any'].concat(this.data.cities) }) ); - const renderAllList = () => { + var renderAllList = function() { this.replaceElement( - dialog.querySelector('#all-filters-list'), - this.renderTemplate('all-filters-list', this.filters) + dialog.querySelector('#all-filters-list'), + this.renderTemplate('all-filters-list', this.filters) ); - - dialog.querySelectorAll('#page-all .mdc-list-item').forEach(el => { - el.addEventListener('click', () => { - const id = el.id.split('-').slice(1).join('-'); + + dialog.querySelectorAll('#page-all .mdc-list-item').forEach(function(el) { + el.addEventListener('click', function() { + var id = el.id.split('-').slice(1).join('-'); displaySection(id); }); }); }; - const displaySection = id => { + var displaySection = function(id) { if (id === 'page-all') { renderAllList(); } - pages.forEach(sel => { + pages.forEach(function(sel) { if (sel.id === id) { sel.style.display = 'block'; } else { @@ -224,12 +231,14 @@ FriendlyEats.prototype.initFilterDialog = function() { }); }; - pages.forEach(sel => { - const type = sel.id.split('-')[1]; - if (type === 'all') return; + pages.forEach(function(sel) { + var type = sel.id.split('-')[1]; + if (type === 'all') { + return; + } - sel.querySelectorAll('.mdc-list-item').forEach(el => { - el.addEventListener('click', () => { + sel.querySelectorAll('.mdc-list-item').forEach(function(el) { + el.addEventListener('click', function() { this.filters[type] = el.innerText.trim() === 'Any'? '' : el.innerText.trim(); displaySection('page-all'); }); @@ -237,30 +246,30 @@ FriendlyEats.prototype.initFilterDialog = function() { }); displaySection('page-all'); - dialog.querySelectorAll('.back').forEach(el => { - el.addEventListener('click', () => { + dialog.querySelectorAll('.back').forEach(function(el) { + el.addEventListener('click', function() { displaySection('page-all'); }); }); }; FriendlyEats.prototype.updateQuery = function(filters) { - let query_description = ''; + var query_description = ''; if (filters.category !== '') { - query_description += `${filters.category} places`; + query_description += filters.category + ' places'; } else { query_description += 'any restaurant'; } if (filters.city !== '') { - query_description += ` in ${filters.city}`; + query_description += ' in ' + filters.city; } else { query_description += ' located anywhere'; } if (filters.price !== '') { - query_description += ` with a price of ${filters.price}`; + query_description += ' with a price of ' + filters.price; } else { query_description += ' with any price'; } @@ -275,13 +284,13 @@ FriendlyEats.prototype.updateQuery = function(filters) { }; FriendlyEats.prototype.viewRestaurant = function(id) { - let sectionHeaderEl; + var sectionHeaderEl; return this.getRestaurant(id) - .then(doc => { - const data = doc.data(); - const dialog = this.dialogs.add_review; + .then(function(doc) { + var data = doc.data(); + var dialog = this.dialogs.add_review; - data.show_add_review = () => { + data.show_add_review = function() { dialog.show(); }; @@ -295,75 +304,80 @@ FriendlyEats.prototype.viewRestaurant = function(id) { .append(this.renderPrice(data.price)); return doc.ref.collection('ratings').orderBy('timestamp', 'desc').get(); }) - .then(ratings => { - let mainEl; + .then(function(ratings) { + var mainEl; if (ratings.size) { mainEl = this.renderTemplate('main'); - ratings.forEach(rating => { - const data = rating.data(); - const el = this.renderTemplate('review-card', data); + ratings.forEach(function(rating) { + var data = rating.data(); + var el = this.renderTemplate('review-card', data); el.querySelector('.rating').append(this.renderRating(data.rating)); mainEl.querySelector('#cards').append(el); }); } else { mainEl = this.renderTemplate('no-ratings', { - add_mock_data: () => { - this.addMockRatings(id).then(() => { + add_mock_data: function() { + this.addMockRatings(id).then(function() { this.rerender(); }); } }); } - const headerEl = this.renderTemplate('header-base', { + var headerEl = this.renderTemplate('header-base', { hasSectionHeader: true }); this.replaceElement(document.querySelector('.header'), sectionHeaderEl); this.replaceElement(document.querySelector('main'), mainEl); }) - .then(() => { + .then(function() { this.router.updatePageLinks(); }) - .catch(err => { + .catch(function(err) { console.warn('Error rendering page', err); }); }; FriendlyEats.prototype.renderTemplate = function(id, data) { - const template = this.templates[id]; - const el = template.cloneNode(true); + var template = this.templates[id]; + var el = template.cloneNode(true); el.removeAttribute('hidden'); this.render(el, data); return el; }; FriendlyEats.prototype.render = function(el, data) { - if (!data) return; + if (!data) { + return; + } - const modifiers = { - 'data-fir-foreach': tel => { - const field = tel.getAttribute('data-fir-foreach'); - const values = this.getDeepItem(data, field); + var modifiers = { + 'data-fir-foreach': function(tel) { + var field = tel.getAttribute('data-fir-foreach'); + var values = this.getDeepItem(data, field); - values.forEach((value, index) => { - const cloneTel = tel.cloneNode(true); + values.forEach(function (value, index) { + var cloneTel = tel.cloneNode(true); tel.parentNode.append(cloneTel); - Object.keys(modifiers).forEach(selector => { - const children = Array.prototype.slice.call( - cloneTel.querySelectorAll(`[${selector}]`) + Object.keys(modifiers).forEach(function(selector) { + var children = Array.prototype.slice.call( + cloneTel.querySelectorAll('[' + selector + ']') ); children.push(cloneTel); - children.forEach(childEl => { - const currentVal = childEl.getAttribute(selector); + children.forEach(function(childEl) { + var currentVal = childEl.getAttribute(selector); + + if (!currentVal) { + return; + } - if (!currentVal) return; childEl.setAttribute( selector, - currentVal.replace('~', `${field}/${index}`) + currentVal.replace('~', field + '/' + index) ); }); }); @@ -371,77 +385,79 @@ FriendlyEats.prototype.render = function(el, data) { tel.parentNode.removeChild(tel); }, - 'data-fir-content': tel => { - const field = tel.getAttribute('data-fir-content'); + 'data-fir-content': function(tel) { + var field = tel.getAttribute('data-fir-content'); tel.innerText = this.getDeepItem(data, field); }, - 'data-fir-click': tel => { - tel.addEventListener('click', () => { - const field = tel.getAttribute('data-fir-click'); + 'data-fir-click': function(tel) { + tel.addEventListener('click', function() { + var field = tel.getAttribute('data-fir-click'); this.getDeepItem(data, field)(); }); }, - 'data-fir-if': tel => { - const field = tel.getAttribute('data-fir-if'); + 'data-fir-if': function(tel) { + var field = tel.getAttribute('data-fir-if'); if (!this.getDeepItem(data, field)) { tel.style.display = 'none'; } }, - 'data-fir-if-not': tel => { - const field = tel.getAttribute('data-fir-if-not'); + 'data-fir-if-not': function(tel) { + var field = tel.getAttribute('data-fir-if-not'); if (this.getDeepItem(data, field)) { tel.style.display = 'none'; } }, - 'data-fir-attr': tel => { - const chunks = tel.getAttribute('data-fir-attr').split(':'); - const attr = chunks[0]; - const field = chunks[1]; + 'data-fir-attr': function(tel) { + var chunks = tel.getAttribute('data-fir-attr').split(':'); + var attr = chunks[0]; + var field = chunks[1]; tel.setAttribute(attr, this.getDeepItem(data, field)); }, - 'data-fir-style': tel => { - const chunks = tel.getAttribute('data-fir-style').split(':'); - const attr = chunks[0]; - const field = chunks[1]; - let value = this.getDeepItem(data, field); + 'data-fir-style': function(tel) { + var chunks = tel.getAttribute('data-fir-style').split(':'); + var attr = chunks[0]; + var field = chunks[1]; + var value = this.getDeepItem(data, field); if (attr.toLowerCase() === 'backgroundimage') { - value = `url(${value})`; + value = 'url(' + value + ')'; } tel.style[attr] = value; } }; - const preModifiers = ['data-fir-foreach']; + var preModifiers = ['data-fir-foreach']; - preModifiers.forEach(selector => { - const modifier = modifiers[selector]; + preModifiers.forEach(function(selector) { + var modifier = modifiers[selector]; this.useModifier(el, selector, modifier); }); - Object.keys(modifiers).forEach(selector => { - if (preModifiers.indexOf(selector) !== -1) return; + Object.keys(modifiers).forEach(function(selector) { + if (preModifiers.indexOf(selector) !== -1) { + return; + } - const modifier = modifiers[selector]; + var modifier = modifiers[selector]; this.useModifier(el, selector, modifier); }); }; FriendlyEats.prototype.useModifier = function(el, selector, modifier) { - el.querySelectorAll(`[${selector}]`).forEach(modifier); + el.querySelectorAll('[' + selector + ']').forEach(modifier); }; FriendlyEats.prototype.getDeepItem = function(obj, path) { - path.split('/').forEach(chunk => { + path.split('/').forEach(function(chunk) { obj = obj[chunk]; }); return obj; }; FriendlyEats.prototype.renderRating = function(rating) { - const el = this.renderTemplate('rating', {}); - for (let r = 0; r < 5; r += 1) { - let star; + var el = this.renderTemplate('rating', {}); + for (var r = 0; r < 5; r += 1) { + var star; if (r < Math.floor(rating)) { star = this.renderTemplate('star-icon', {}); } else { @@ -453,8 +469,8 @@ FriendlyEats.prototype.renderRating = function(rating) { }; FriendlyEats.prototype.renderPrice = function(price) { - const el = this.renderTemplate('price', {}); - for (let r = 0; r < price; r += 1) { + var el = this.renderTemplate('price', {}); + for (var r = 0; r < price; r += 1) { el.append('$'); } return el; diff --git a/firestore/scripts/FriendlyEats.js b/firestore/scripts/FriendlyEats.js index 4914dafcc..4d48bc3a6 100644 --- a/firestore/scripts/FriendlyEats.js +++ b/firestore/scripts/FriendlyEats.js @@ -28,12 +28,12 @@ function FriendlyEats() { this.dialogs = {}; - firebase.auth().signInAnonymously().then(() => { + firebase.auth().signInAnonymously().then(function() { this.initTemplates(); this.initRouter(); this.initReviewDialog(); this.initFilterDialog(); - }).catch(err => { + }).catch(function(err) { console.log(err); }); } @@ -46,19 +46,19 @@ FriendlyEats.prototype.initRouter = function() { this.router .on({ - '/': () => { + '/': function() { this.updateQuery(this.filters); } }) .on({ - '/setup': () => { + '/setup': function() { this.viewSetup(); } }) .on({ - '/restaurants/*': () => { - let path = this.getCleanPath(document.location.pathname); - const id = path.split('/')[2]; + '/restaurants/*': function() { + var path = this.getCleanPath(document.location.pathname); + var id = path.split('/')[2]; this.viewRestaurant(id); } }) @@ -68,7 +68,7 @@ FriendlyEats.prototype.initRouter = function() { .firestore() .collection('restaurants') .limit(1) - .onSnapshot(snapshot => { + .onSnapshot(function(snapshot) { if (snapshot.empty) { this.router.navigate('/setup'); } @@ -190,6 +190,6 @@ FriendlyEats.prototype.data = { ] }; -window.onload = () => { +window.onload = function() { window.app = new FriendlyEats(); }; diff --git a/lerna.json b/lerna.json new file mode 100644 index 000000000..fff071398 --- /dev/null +++ b/lerna.json @@ -0,0 +1,7 @@ +{ + "lerna": "2.8.0", + "packages": [ + "firestore" + ], + "version": "1.0.0" +} diff --git a/messaging/.eslintrc.json b/messaging/.eslintrc.json new file mode 100644 index 000000000..4b84f2481 --- /dev/null +++ b/messaging/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "globals": { + "importScripts": true + } +} \ No newline at end of file diff --git a/messaging/firebase-messaging-sw.js b/messaging/firebase-messaging-sw.js index eb8df5341..43699086f 100644 --- a/messaging/firebase-messaging-sw.js +++ b/messaging/firebase-messaging-sw.js @@ -5,7 +5,7 @@ importScripts('/__/firebase/4.8.1/firebase-app.js'); importScripts('/__/firebase/4.8.1/firebase-messaging.js'); importScripts('/__/firebase/init.js'); -const messaging = firebase.messaging(); +var messaging = firebase.messaging(); /** * Here is is the code snippet to initialize Firebase Messaging in the Service @@ -38,13 +38,13 @@ const messaging = firebase.messaging(); messaging.setBackgroundMessageHandler(function(payload) { console.log('[firebase-messaging-sw.js] Received background message ', payload); // Customize notification here - const notificationTitle = 'Background Message Title'; - const notificationOptions = { + var notificationTitle = 'Background Message Title'; + var notificationOptions = { body: 'Background Message body.', icon: '/firebase-logo.png' }; return self.registration.showNotification(notificationTitle, - notificationOptions); + notificationOptions); }); // [END background_handler] diff --git a/package.json b/package.json new file mode 100644 index 000000000..5db42b908 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "quickstart-js", + "version": "1.0.0", + "description": "JavaScript quickstarts for Firebase.", + "repository": { + "type": "git", + "url": "git+https://github.com/firebase/quickstart-js.git" + }, + "author": "", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/firebase/quickstart-js/issues" + }, + "homepage": "https://github.com/firebase/quickstart-js#readme", + "devDependencies": { + "eslint": "^4.17.0", + "lerna": "^2.8.0" + } +} diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 000000000..3078f21bf --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,4 @@ +set -e +# Run linter +find . -type f -name "*.js" -not -path "*node_modules*" \ + | xargs eslint \ No newline at end of file From 8e01218fc2c828663d56b382d496a40ba842d772 Mon Sep 17 00:00:00 2001 From: Sam Stern Date: Tue, 13 Feb 2018 14:47:48 -0800 Subject: [PATCH 2/3] this --> that --- firestore/scripts/FriendlyEats.View.js | 105 ++++++++++++++----------- firestore/scripts/FriendlyEats.js | 20 ++--- 2 files changed, 68 insertions(+), 57 deletions(-) diff --git a/firestore/scripts/FriendlyEats.View.js b/firestore/scripts/FriendlyEats.View.js index dc5b7a0b6..a5e516d69 100644 --- a/firestore/scripts/FriendlyEats.View.js +++ b/firestore/scripts/FriendlyEats.View.js @@ -17,8 +17,10 @@ FriendlyEats.prototype.initTemplates = function() { this.templates = {}; + + var that = this; document.querySelectorAll('.template').forEach(function(el) { - this.templates[el.getAttribute('id')] = el; + that.templates[el.getAttribute('id')] = el; }); }; @@ -46,42 +48,43 @@ FriendlyEats.prototype.viewList = function(filters, filter_description) { this.replaceElement(document.querySelector('.header'), headerEl); this.replaceElement(document.querySelector('main'), mainEl); + var that = this; headerEl.querySelector('#show-filters').addEventListener('click', function() { - this.dialogs.filter.show(); + that.dialogs.filter.show(); }); var renderResults = function(doc) { if (!doc) { - var headerEl = this.renderTemplate('header-base', { + var headerEl = that.renderTemplate('header-base', { hasSectionHeader: true }); - var noResultsEl = this.renderTemplate('no-results'); + var noResultsEl = that.renderTemplate('no-results'); - this.replaceElement( + that.replaceElement( headerEl.querySelector('#section-header'), - this.renderTemplate('filter-display', { + that.renderTemplate('filter-display', { filter_description: filter_description }) ); headerEl.querySelector('#show-filters').addEventListener('click', function() { - this.dialogs.filter.show(); + that.dialogs.filter.show(); }); - this.replaceElement(document.querySelector('.header'), headerEl); - this.replaceElement(document.querySelector('main'), noResultsEl); + that.replaceElement(document.querySelector('.header'), headerEl); + that.replaceElement(document.querySelector('main'), noResultsEl); return; } var data = doc.data(); data['.id'] = doc.id; data['go_to_restaurant'] = function() { - this.router.navigate('/restaurants/' + doc.id); + that.router.navigate('/restaurants/' + doc.id); }; - var el = this.renderTemplate('restaurant-card', data); - el.querySelector('.rating').append(this.renderRating(data.avgRating)); - el.querySelector('.price').append(this.renderPrice(data.price)); + var el = that.renderTemplate('restaurant-card', data); + el.querySelector('.rating').append(that.renderRating(data.avgRating)); + el.querySelector('.price').append(that.renderPrice(data.price)); mainEl.querySelector('#cards').append(el); }; @@ -114,6 +117,7 @@ FriendlyEats.prototype.viewSetup = function() { var button = noRestaurantsEl.querySelector('#add_mock_data'); var addingMockData = false; + var that = this; button.addEventListener('click', function(event) { if (addingMockData) { return; @@ -124,8 +128,8 @@ FriendlyEats.prototype.viewSetup = function() { event.target.style.opacity = '0.4'; event.target.innerText = 'Please wait...'; - this.addMockRestaurants().then(function() { - this.rerender(); + that.addMockRestaurants().then(function() { + that.rerender(); }); }); @@ -138,7 +142,7 @@ FriendlyEats.prototype.viewSetup = function() { .limit(1) .onSnapshot(function(snapshot) { if (snapshot.size && !addingMockData) { - this.router.navigate('/'); + that.router.navigate('/'); } }); }; @@ -147,18 +151,19 @@ FriendlyEats.prototype.initReviewDialog = function() { var dialog = document.querySelector('#dialog-add-review'); this.dialogs.add_review = new mdc.dialog.MDCDialog(dialog); + var that = this; this.dialogs.add_review.listen('MDCDialog:accept', function() { - var pathname = this.getCleanPath(document.location.pathname); + var pathname = that.getCleanPath(document.location.pathname); var id = pathname.split('/')[2]; - this.addRating(id, { + that.addRating(id, { rating: rating, text: dialog.querySelector('#text').value, userName: 'Anonymous (Web)', timestamp: new Date(), userId: firebase.auth().currentUser.uid }).then(function() { - this.rerender(); + that.rerender(); }); }); @@ -186,8 +191,9 @@ FriendlyEats.prototype.initFilterDialog = function() { // TODO: Reset filter dialog to init state on close. this.dialogs.filter = new mdc.dialog.MDCDialog(document.querySelector('#dialog-filter-all')); + var that = this; this.dialogs.filter.listen('MDCDialog:accept', function() { - this.updateQuery(this.filters); + that.updateQuery(that.filters); }); var dialog = document.querySelector('aside'); @@ -195,18 +201,18 @@ FriendlyEats.prototype.initFilterDialog = function() { this.replaceElement( dialog.querySelector('#category-list'), - this.renderTemplate('item-list', { items: ['Any'].concat(this.data.categories) }) + that.renderTemplate('item-list', { items: ['Any'].concat(that.data.categories) }) ); this.replaceElement( dialog.querySelector('#city-list'), - this.renderTemplate('item-list', { items: ['Any'].concat(this.data.cities) }) + that.renderTemplate('item-list', { items: ['Any'].concat(that.data.cities) }) ); var renderAllList = function() { - this.replaceElement( + that.replaceElement( dialog.querySelector('#all-filters-list'), - this.renderTemplate('all-filters-list', this.filters) + that.renderTemplate('all-filters-list', that.filters) ); dialog.querySelectorAll('#page-all .mdc-list-item').forEach(function(el) { @@ -239,7 +245,7 @@ FriendlyEats.prototype.initFilterDialog = function() { sel.querySelectorAll('.mdc-list-item').forEach(function(el) { el.addEventListener('click', function() { - this.filters[type] = el.innerText.trim() === 'Any'? '' : el.innerText.trim(); + that.filters[type] = el.innerText.trim() === 'Any'? '' : el.innerText.trim(); displaySection('page-all'); }); }); @@ -285,56 +291,58 @@ FriendlyEats.prototype.updateQuery = function(filters) { FriendlyEats.prototype.viewRestaurant = function(id) { var sectionHeaderEl; + var that = this; + return this.getRestaurant(id) .then(function(doc) { var data = doc.data(); - var dialog = this.dialogs.add_review; + var dialog = that.dialogs.add_review; data.show_add_review = function() { dialog.show(); }; - sectionHeaderEl = this.renderTemplate('restaurant-header', data); + sectionHeaderEl = that.renderTemplate('restaurant-header', data); sectionHeaderEl .querySelector('.rating') - .append(this.renderRating(data.avgRating)); + .append(that.renderRating(data.avgRating)); sectionHeaderEl .querySelector('.price') - .append(this.renderPrice(data.price)); + .append(that.renderPrice(data.price)); return doc.ref.collection('ratings').orderBy('timestamp', 'desc').get(); }) .then(function(ratings) { var mainEl; if (ratings.size) { - mainEl = this.renderTemplate('main'); + mainEl = that.renderTemplate('main'); ratings.forEach(function(rating) { var data = rating.data(); - var el = this.renderTemplate('review-card', data); - el.querySelector('.rating').append(this.renderRating(data.rating)); + var el = that.renderTemplate('review-card', data); + el.querySelector('.rating').append(that.renderRating(data.rating)); mainEl.querySelector('#cards').append(el); }); } else { - mainEl = this.renderTemplate('no-ratings', { + mainEl = that.renderTemplate('no-ratings', { add_mock_data: function() { - this.addMockRatings(id).then(function() { - this.rerender(); + that.addMockRatings(id).then(function() { + that.rerender(); }); } }); } - var headerEl = this.renderTemplate('header-base', { + var headerEl = that.renderTemplate('header-base', { hasSectionHeader: true }); - this.replaceElement(document.querySelector('.header'), sectionHeaderEl); - this.replaceElement(document.querySelector('main'), mainEl); + that.replaceElement(document.querySelector('.header'), sectionHeaderEl); + that.replaceElement(document.querySelector('main'), mainEl); }) .then(function() { - this.router.updatePageLinks(); + that.router.updatePageLinks(); }) .catch(function(err) { console.warn('Error rendering page', err); @@ -354,10 +362,11 @@ FriendlyEats.prototype.render = function(el, data) { return; } + var that = this; var modifiers = { 'data-fir-foreach': function(tel) { var field = tel.getAttribute('data-fir-foreach'); - var values = this.getDeepItem(data, field); + var values = that.getDeepItem(data, field); values.forEach(function (value, index) { var cloneTel = tel.cloneNode(true); @@ -387,23 +396,23 @@ FriendlyEats.prototype.render = function(el, data) { }, 'data-fir-content': function(tel) { var field = tel.getAttribute('data-fir-content'); - tel.innerText = this.getDeepItem(data, field); + tel.innerText = that.getDeepItem(data, field); }, 'data-fir-click': function(tel) { tel.addEventListener('click', function() { var field = tel.getAttribute('data-fir-click'); - this.getDeepItem(data, field)(); + that.getDeepItem(data, field)(); }); }, 'data-fir-if': function(tel) { var field = tel.getAttribute('data-fir-if'); - if (!this.getDeepItem(data, field)) { + if (!that.getDeepItem(data, field)) { tel.style.display = 'none'; } }, 'data-fir-if-not': function(tel) { var field = tel.getAttribute('data-fir-if-not'); - if (this.getDeepItem(data, field)) { + if (that.getDeepItem(data, field)) { tel.style.display = 'none'; } }, @@ -411,13 +420,13 @@ FriendlyEats.prototype.render = function(el, data) { var chunks = tel.getAttribute('data-fir-attr').split(':'); var attr = chunks[0]; var field = chunks[1]; - tel.setAttribute(attr, this.getDeepItem(data, field)); + tel.setAttribute(attr, that.getDeepItem(data, field)); }, 'data-fir-style': function(tel) { var chunks = tel.getAttribute('data-fir-style').split(':'); var attr = chunks[0]; var field = chunks[1]; - var value = this.getDeepItem(data, field); + var value = that.getDeepItem(data, field); if (attr.toLowerCase() === 'backgroundimage') { value = 'url(' + value + ')'; @@ -430,7 +439,7 @@ FriendlyEats.prototype.render = function(el, data) { preModifiers.forEach(function(selector) { var modifier = modifiers[selector]; - this.useModifier(el, selector, modifier); + that.useModifier(el, selector, modifier); }); Object.keys(modifiers).forEach(function(selector) { @@ -439,7 +448,7 @@ FriendlyEats.prototype.render = function(el, data) { } var modifier = modifiers[selector]; - this.useModifier(el, selector, modifier); + that.useModifier(el, selector, modifier); }); }; diff --git a/firestore/scripts/FriendlyEats.js b/firestore/scripts/FriendlyEats.js index 4d48bc3a6..7220c0f29 100644 --- a/firestore/scripts/FriendlyEats.js +++ b/firestore/scripts/FriendlyEats.js @@ -28,11 +28,12 @@ function FriendlyEats() { this.dialogs = {}; + var that = this; firebase.auth().signInAnonymously().then(function() { - this.initTemplates(); - this.initRouter(); - this.initReviewDialog(); - this.initFilterDialog(); + that.initTemplates(); + that.initRouter(); + that.initReviewDialog(); + that.initFilterDialog(); }).catch(function(err) { console.log(err); }); @@ -44,22 +45,23 @@ function FriendlyEats() { FriendlyEats.prototype.initRouter = function() { this.router = new Navigo(); + var that = this; this.router .on({ '/': function() { - this.updateQuery(this.filters); + that.updateQuery(that.filters); } }) .on({ '/setup': function() { - this.viewSetup(); + that.viewSetup(); } }) .on({ '/restaurants/*': function() { - var path = this.getCleanPath(document.location.pathname); + var path = that.getCleanPath(document.location.pathname); var id = path.split('/')[2]; - this.viewRestaurant(id); + that.viewRestaurant(id); } }) .resolve(); @@ -70,7 +72,7 @@ FriendlyEats.prototype.initRouter = function() { .limit(1) .onSnapshot(function(snapshot) { if (snapshot.empty) { - this.router.navigate('/setup'); + that.router.navigate('/setup'); } }); }; From e13049f12d811518e31ca60c1ae3fa8b30fc24c3 Mon Sep 17 00:00:00 2001 From: Sam Stern Date: Fri, 2 Mar 2018 10:17:34 -0800 Subject: [PATCH 3/3] Address review feedback --- .travis.yml | 2 +- database/.eslintrc.json | 5 +++++ database/scripts/main.js | 1 - firestore/.eslintrc.json | 2 +- messaging/.eslintrc.json | 2 +- scripts/test.sh | 2 +- 6 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 database/.eslintrc.json diff --git a/.travis.yml b/.travis.yml index f93e855ab..af4d9c220 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,4 +6,4 @@ install: - npm install -g eslint - lerna bootstrap script: - - ./scripts/test.sh \ No newline at end of file + - ./scripts/test.sh diff --git a/database/.eslintrc.json b/database/.eslintrc.json new file mode 100644 index 000000000..068e2a4ac --- /dev/null +++ b/database/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "globals": { + "componentHandler": true + } +} diff --git a/database/scripts/main.js b/database/scripts/main.js index a501e0bda..617d5bc0e 100644 --- a/database/scripts/main.js +++ b/database/scripts/main.js @@ -15,7 +15,6 @@ */ 'use strict'; -var componentHandler = componentHandler || undefined; // Shortcuts to DOM Elements. var messageForm = document.getElementById('message-form'); diff --git a/firestore/.eslintrc.json b/firestore/.eslintrc.json index 1a0c107ce..527348331 100644 --- a/firestore/.eslintrc.json +++ b/firestore/.eslintrc.json @@ -5,4 +5,4 @@ "importScripts": true, "FriendlyEats": true } -} \ No newline at end of file +} diff --git a/messaging/.eslintrc.json b/messaging/.eslintrc.json index 4b84f2481..f85cbb586 100644 --- a/messaging/.eslintrc.json +++ b/messaging/.eslintrc.json @@ -2,4 +2,4 @@ "globals": { "importScripts": true } -} \ No newline at end of file +} diff --git a/scripts/test.sh b/scripts/test.sh index 3078f21bf..09ea4dbcf 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -1,4 +1,4 @@ set -e # Run linter find . -type f -name "*.js" -not -path "*node_modules*" \ - | xargs eslint \ No newline at end of file + | xargs eslint