├── _config.yml ├── docs ├── _config.yml ├── README.md ├── contributionguidelines.md └── Setupinstructions.md ├── .env.example ├── mock-up.png ├── src ├── img │ ├── 16.png │ ├── 24.png │ ├── 32.png │ └── circleMarker.png ├── components │ ├── RecenterMapButton.js │ ├── StaticMap.js │ ├── Places.js │ ├── Listing.js │ ├── PlacesListItem.js │ ├── Description.js │ ├── Search.js │ ├── Header.js │ ├── Footer.js │ └── InitialMap.js ├── App.test.js ├── index.js ├── services │ ├── weather.js │ ├── googlePlaces.js │ └── geolocation.js ├── App.js ├── css │ ├── index.min.css │ ├── index.css.map │ ├── index.css │ └── index.scss └── registerServiceWorker.js ├── public ├── favicon.png ├── index.html ├── sw.js └── idb.js ├── .eslintrc ├── package.json ├── LICENSE ├── .gitignore ├── .github └── pull_request_template.md └── README_do_not_edit_on_dev.md /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | REACT_APP_GKEY='insert your key' 2 | REACT_APP_WEATHERKEY='insert your key' -------------------------------------------------------------------------------- /mock-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gwg-women/gwg-women-techmakers/HEAD/mock-up.png -------------------------------------------------------------------------------- /src/img/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gwg-women/gwg-women-techmakers/HEAD/src/img/16.png -------------------------------------------------------------------------------- /src/img/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gwg-women/gwg-women-techmakers/HEAD/src/img/24.png -------------------------------------------------------------------------------- /src/img/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gwg-women/gwg-women-techmakers/HEAD/src/img/32.png -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gwg-women/gwg-women-techmakers/HEAD/public/favicon.png -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["react-app", "plugin:jsx-a11y/recommended"], 3 | "plugins": ["jsx-a11y"] 4 | } -------------------------------------------------------------------------------- /src/img/circleMarker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gwg-women/gwg-women-techmakers/HEAD/src/img/circleMarker.png -------------------------------------------------------------------------------- /src/components/RecenterMapButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | const Button = props => { 3 | const {recenterMyMap, mapCenter, ...other } = props; 4 | return 59 | 60 | ) 61 | } 62 | } 63 | 64 | export default GoogleApiWrapper({ 65 | apiKey: (process.env.REACT_APP_GKEY), 66 | libraries: ['places'] 67 | })(Search) -------------------------------------------------------------------------------- /src/services/googlePlaces.js: -------------------------------------------------------------------------------- 1 | const http = require('https'); 2 | 3 | export default function getPlaces(lat, lng, query){ 4 | let Listings =[]; 5 | let location = {latitude:0, 6 | longitide:0} 7 | location.latitude=lat; 8 | location.longitide=lng; 9 | console.log('in get places'); 10 | 11 | const google_api = process.env.REACT_APP_GKEY; 12 | 13 | const url = `https://maps.googleapis.com/maps/api/place/textsearch/json?query=${query}&key=${google_api}&location=${lat},${lng}&radius=5000`; 14 | 15 | 16 | const GOOGLE_PLACES_OPTIONS = { 17 | method: 'GET', 18 | hostname: 'www.googleapis.com', 19 | port: null, 20 | path: `/geolocation/v1/geolocate?key=${google_api}`, 21 | headers: { 22 | 'content-type': 'application/json', 23 | 'cache-control': 'no-cache', 24 | } 25 | }; 26 | 27 | 28 | return new Promise(function(resolve, reject){ 29 | console.log('in place promise'); 30 | http.get(url, (res) => { 31 | if(res.statusCode == 'OK'){ 32 | console.log(" OK "); 33 | res.setEncoding('utf8'); 34 | res.on('data', (data) => { 35 | data = JSON.parse(data); 36 | if(data.results.length >= 0){ 37 | Listings = data.results; 38 | console.log(Listings); 39 | resolve(Listings); 40 | } 41 | }) 42 | } else if(res.statusCode != 'ZERO_RESULTS'){ 43 | console.log("error" + res.statusCode); 44 | reject(); 45 | } 46 | }); 47 | }); 48 | 49 | } 50 | 51 | 52 | /* NOTES : Getting CORS error 53 | 54 | localhost/:1 Failed to load https://maps.googleapis.com/maps/api/place/textsearch/json?query=food&key=AIzaSyAR9_HMLChf4WdyDIX3ZuDF6pZYqi9aZDI&location=40.854885,-88.081807&radius=5000: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled. 55 | localhost/:1 Uncaught (in promise) TypeError: Failed to fetch 56 | 57 | Also, couldn't find a wasy to use the env variables 58 | api = process.env.API_KEY; is not working 59 | */ -------------------------------------------------------------------------------- /public/sw.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const CACHE_NAME = 'maapa-cache-v15'; 4 | 5 | const urlsToCache =[ 6 | '/', 7 | '/static/js/main.bd8fcf5f.js', 8 | '/static/css/main.181bbb8b.css', 9 | '/index.html', 10 | ]; 11 | 12 | function createDB() { 13 | idb.open('mappa', 1, function(upgradeDB){ 14 | var store = upgradeDB.createObjectStore('weather') 15 | }) 16 | }; 17 | 18 | // The first time the user starts up the app, 'install' is triggered. 19 | self.addEventListener('install', function(event) { 20 | // if (doCache) { 21 | event.waitUntil( 22 | caches.open(CACHE_NAME) 23 | .then(function(cache) { 24 | return cache.addAll(urlsToCache) 25 | }) 26 | ); 27 | // } 28 | }); 29 | 30 | self.addEventListener("activate", function(event) { 31 | const cacheWhitelist = [CACHE_NAME]; 32 | event.waitUntil( 33 | caches.keys() 34 | .then(keyList => 35 | Promise.all(keyList.map(key => { 36 | if (!cacheWhitelist.includes(key)) { 37 | return caches.delete(key); 38 | } 39 | })) 40 | ) 41 | ); 42 | }); 43 | 44 | 45 | // When the webpage goes to fetch files, we intercept that request and serve up the matching files 46 | // if we have them 47 | self.addEventListener('fetch', function (event) { 48 | const requestUrl = new URL(event.request.url) 49 | 50 | if (requestUrl.pathname.startsWith('/maps/api/staticmap')) { 51 | event.respondWith( 52 | caches.open(CACHE_NAME).then(function (cache) { 53 | return cache.match(event.request).then(function (response) { 54 | console.log("caching All request: ", requestUrl, " Response: ", response) 55 | return response || fetch(event.request).then(function (response) { 56 | cache.put(event.request, response.clone()) 57 | return response; 58 | }) 59 | .catch(function () { 60 | // console.log('in catch fetch for request: ', requestUrl.pathname); 61 | if (requestUrl.pathname.startsWith('/maps/api/js')) { 62 | return caches.match('maps/api/staticmap') 63 | } 64 | }) 65 | }) 66 | }) 67 | ) 68 | } 69 | }); 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /docs/contributionguidelines.md: -------------------------------------------------------------------------------- 1 | Thanks for joining the GWG Women Techmakers team! 2 | 3 | ## Issues 4 | Assign yourself an issue you're interested in helping with. 5 | 6 | ## Branches 7 | 8 | We're all working off of the "dev" branch. 9 | 10 | Click the "Fork" button. Once you've done that, you can use your favorite git client to clone your repository or just head straight to the command line: 11 | 12 | Clone your fork to your local machine 13 | `git clone https://github.com//gwg-women-techmakers.git` 14 | 15 | To keep your fork up to date by tracking the original "upstream" repository that you forked. To do this, you'll need to add a remote: 16 | `git remote add upstream https://github.com/gwg-women/gwg-women-techmakers.git` 17 | 18 | After you've set up your local environment and assigned yourself an issue, follow these steps for contributing to the project: 19 | 1. Always pull in new updates from "dev" branch before getting started and pull and push to sync your local repository: 20 | `git checkout dev` 21 | `git pull upstream dev` 22 | `git push origin dev`. 23 | 24 | 2. Create a new branch for our particular issue: `git checkout -b ` 25 | 3. Make your changes to solve that issue. 26 | 4. Commit early, commit often! If you have a large issue that takes time, it's best to save your work on your machine and on Github often. For Github: 27 | - `git add -A' 28 | - 'git commit -m "I did these things during this save"` 29 | - `git push` 30 | - The first push will ask you to create that branch to your fork on Github. Just follow it's instructions. 31 | 5. When you're ready to submit your changes, create a pull request in the branch containing your changes from your forked repository. For more details refer :[GitHub : Creating the Pull Request](https://help.github.com/articles/creating-a-pull-request/) 32 | 6. Fill out the pull request with these questions in mind: 33 | - What issue does it solve? Identify it with a `#` and the number, which will connect your issue and pull request automatically. 34 | - What did you do to solve the issue? Just a brief list of updates is good. 35 | - How can we test this to be sure it works? 36 | 7. Assign a couple teammates to review your pull request and submit. 37 | 8. Once it has been approved and merged, please delete your branch. 38 | -------------------------------------------------------------------------------- /README_do_not_edit_on_dev.md: -------------------------------------------------------------------------------- 1 | # gwg-women-techmakers 2 | This is a Grow with Google project for an offline first app that provides information based on location and search. 3 | 4 | ## Getting Started 5 | Read the the [Set Up Instructions](https://github.com/gwg-women/gwg-women-techmakers/wiki/Set-Up-Instructions) on our wiki. 6 | 7 | ## Contributing to the Project 8 | Please read the [contribution guidelines](https://github.com/gwg-women/gwg-women-techmakers/wiki/Contribution-Guidelines) on the wiki to see how to get started with solving issues and creating pull requests. 9 | 10 | 11 | ## The repository uses the following files : 12 | 13 | * **public/index.html** : The main HTML page. Contains links to all of the CSS resources needed to render the map and results. It also loads results of Google Maps API asyncronously. 14 | 15 | * **src/index.js**: is the JavaScript entry point. 16 | 17 | * **src/components**: Contains all the controls. 18 | ***Description.js** 19 | Contains the Wiki component that displays the wikipedia information about the current city. 20 | 21 | ***Header.js*** 22 | Get's the current location and weather conditions and displays the information on the header. 23 | 24 | ***InitialMap.js*** 25 | Get's and displays the map with markers to the search results. 26 | 27 | ***Places.js*** 28 | Displays the searchResults in the form of a list on the leftside of the app. 29 | 30 | ***PlacesListItem.js*** 31 | Contains the ListItems control that displays all the list items. It displays the name address and opening hours of a place. 32 | 33 | ***Search.js*** 34 | Get's the user Input for search query. 35 | 36 | * **src/css/index.css** 37 | Contains the styles of the App 38 | 39 | * **src/services** folder has the code that makes calls to the location, map and weather API's and fetches their results. 40 | 41 | ***geolocation.js*** 42 | 43 | ***weather.js*** 44 | 45 | * **registerServiceWorker.js** checks for and registers the service worker for the application. 46 | 47 | ## Some Packages Used ## 48 | npm 49 | 50 | react 51 | 52 | react-dom 53 | 54 | react-scripts 55 | 56 | cra-append-sw 57 | 58 | google-maps-react 59 | 60 | 61 | ## Motivation 62 | 63 | This project is made as part of the GROW With GOOGLE Challenge for practicing 64 | * Creating Progressive web apllications 65 | * Registering and create a service worker 66 | * Creatinf a REACT web application 67 | * Colaborationg using GIT 68 | * Interacting with API servers 69 | * Use of third-party libraries and APIs 70 | * Use Google Maps 71 | -------------------------------------------------------------------------------- /src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import {getCity} from '../services/geolocation.js'; 3 | import {getWeather} from '../services/weather.js'; 4 | import {GoogleApiWrapper} from 'google-maps-react'; 5 | import Image from '../img/24.png'; 6 | 7 | class HeaderContainer extends Component { 8 | state = {}; 9 | 10 | componentDidMount() { 11 | this.getMyLocation(); 12 | } 13 | 14 | componentWillUpdate(prevProps, prevState) { 15 | if (prevState.currentCity !== this.state.currentCity) { 16 | this.props.setCurrentCity(this.state.currentCity); 17 | } 18 | } 19 | 20 | getCityWeather(latitude, longitude) { 21 | const {setCurrentCity} = this.props; 22 | 23 | getCity(latitude, longitude).then((city) => { 24 | this.setState({currentCity: city}); 25 | setCurrentCity(city); 26 | 27 | }).catch(function(err) { 28 | console.log('Error retrieving the current city: ', err); 29 | }); 30 | 31 | getWeather(latitude, longitude).then((weather) => { 32 | weather = Math.round(weather); 33 | this.setState({currentWeather: weather}) 34 | }).catch(function(err){ 35 | console.log('Error retrieving the current weather: ', err); 36 | }) 37 | } 38 | 39 | getMyLocation = () => { 40 | const { 41 | handleLocationChange, 42 | setUserPosition 43 | } = this.props; 44 | const pos = { 45 | lat: parseFloat(localStorage.getItem('lat')), 46 | lng: parseFloat(localStorage.getItem('lng')) 47 | } 48 | 49 | handleLocationChange(pos); 50 | setUserPosition(pos); 51 | // ***Get Location from Cache 52 | if (pos.lat && pos.lng) { 53 | this.getCityWeather(pos.lat, pos.lng); 54 | } 55 | 56 | 57 | const errorLocation = (err) => { 58 | console.log("error retrieving current position, " + err); 59 | } 60 | 61 | // ***Get Location from getCurrentPosition 62 | const currentLocation = (position) => { 63 | const { 64 | handleLocationChange, 65 | setUserPosition 66 | } = this.props 67 | 68 | const pos = { 69 | lat: position.coords.latitude, 70 | lng: position.coords.longitude 71 | } 72 | setUserPosition(pos) 73 | handleLocationChange(pos) 74 | 75 | localStorage.setItem('lat', pos.lat); 76 | localStorage.setItem('lng', pos.lng); 77 | this.getCityWeather(pos.lat, pos.lng); 78 | } 79 | 80 | // Ask user for permission to use location services 81 | if (navigator.geolocation) { 82 | navigator.geolocation.getCurrentPosition(currentLocation, errorLocation); 83 | } else { 84 | alert('Sorry your browser doesn\'t support the Geolocation API'); 85 | } 86 | } 87 | 88 | render () { 89 | const {currentCity, currentWeather} = this.state; 90 | const message = (this.state.currentCity && this.state.currentWeather) 91 | ? ` You're in ${currentCity}. It is currently ${currentWeather}°F` 92 | : ``; 93 | 94 | return( 95 |

96 | Welcome to Mappa. {message} 97 |

98 | ); 99 | } 100 | } 101 | 102 | export default GoogleApiWrapper({ 103 | apiKey: (process.env.REACT_APP_GKEY), 104 | libraries: ['places'], 105 | version: '3' 106 | 107 | })(HeaderContainer) 108 | 109 | -------------------------------------------------------------------------------- /docs/Setupinstructions.md: -------------------------------------------------------------------------------- 1 | ### Following these instructions will enable you to run the app locally with your own Google Maps API key. 2 | This is an essential first step to contributing to this project. 3 | 4 | # 5 | 6 | [Special instructions for beginners](http://dont-be-afraid-to-commit.readthedocs.io/en/latest/git/commandlinegit.html) - Follow these steps up to "Give GitHub your public keys". 7 | 8 | 9 | 10 | General Set Up Instructions

11 | 12 | 1.

Fork the repository to your github. 13 | 14 | * To **fork** the project, head over to [gwg-women-techmakers](https://github.com/gwg-women/gwg-women-techmakers) and fork the project into your own github. 15 | 16 | * Head into your forked project and click the green button that says **Clone or download** to see the URL to use to fork into your own computer. It should look a little something like this: 17 | 18 | ```https://github.com//gwg-women-techmakers.git``` 19 | 20 | * Open your command shell of choice and navigate to the folder where you want your project to be in. Type in: 21 | 22 | ```git clone https://github.com//gwg-women-techmakers.git``` 23 |
24 | 25 | 26 | 2.
How to get your own API key 27 | 28 | * Get Google Maps API 29 | 30 | - Go to the [Google API Console](https://console.developers.google.com/apis) 31 | - [Create or select a project for Google Maps Android Backend API](https://console.developers.google.com/flows/enableapi?apiid=maps_android_backend&reusekey=true) or search for it in **Library** panel 32 | - [Create or select a project for Google Maps JavaScript API](https://developers.google.com/maps/documentation/javascript/get-api-key) or find it in **Library** panel 33 | - In Google API Console check to see if the APIs are enabled in **Enabled APIs and services** panel 34 | - In Google API Console retrieve your **API key** inside the **Credentials** panel. 35 | 36 | 37 | * Get a OpenWeatherMap API key: 38 | - Go to the [OpenWeatherMap how to start](https://openweathermap.org/appid) page and click sign up 39 | - Create a free account 40 | - Go to the API Keys tab 41 |
42 | 43 | 2.
Getting it running 44 | 45 | * **Installing Dependencies**: 46 | - Navigate inside **gwg-women-techmakers** folder with your command shell and install all dependencies with your package manager of choice. 47 | * NPM: 48 | `npm install` 49 | * Yarn: 50 | `yarn install` 51 | * **Inserting your API key**: 52 | - Inside the directory, find **.env.example** and create a copy and name it **.env**. 53 | 54 | NOTE: .env is in .gitignore and will not be uploaded to your github. 55 | 56 | - Your API key inside your environment variable (.env) should start with **REACT_APP**. 57 | - Google's API should look this: 58 | 59 | ```REACT_APP_GKEY: api_key``` 60 | - Weather's API should look like this: 61 | 62 | ```REACT_APP_WEATHERKEY: api_key``` 63 | 64 | * **Starting the APP**: 65 | 66 | * Build everything together with your package manager of choice: 67 | * NPM: 68 | `npm run build` or `npm build` 69 | 70 | * Yarn: 71 | `yarn build` 72 | 73 | * Start it!: 74 | * Run the app by typing in: 75 | * NPM: 76 | ```npm start``` 77 | 78 | * Yarn: 79 | ```yarn start``` 80 | 81 | You should be able to see it run on localhost:3000. That's it! 82 |
83 | 84 |

85 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import MapContainer from './components/InitialMap' 3 | import HeaderContainer from './components/Header' 4 | import Search from './components/Search' 5 | import Container from './components/Places' 6 | import Wiki from './components/Description' 7 | import Footer from './components/Footer' 8 | 9 | class App extends Component { 10 | constructor(props) { 11 | super(props) 12 | this.state = { 13 | places: [], 14 | searchTerm: 'food', 15 | pos: {}, 16 | query: '', 17 | mouseOverPlace: '', 18 | userPos: {} 19 | } 20 | 21 | this.handleSubmit = this.handleSubmit.bind(this); 22 | this.handleChange = this.handleChange.bind(this); 23 | this.handleLocationChange = this.handleLocationChange.bind(this); 24 | this.handleLoad = this.handleLoad.bind(this); 25 | this.setCurrentCity = this.setCurrentCity.bind(this); 26 | this.setUserPosition = this.setUserPosition.bind(this); 27 | this.onPlaceSelected = this.onPlaceSelected.bind(this); 28 | } 29 | 30 | /* componentDidMount(){ 31 | console.log("getPlaces") 32 | getPlaces(this.state.mapCenter.lat,this.state.mapCenter.lng,this.state.searchTerm).then((places) => { 33 | this.setState({ places }) 34 | }) 35 | } 36 | */ 37 | 38 | handleSubmit(searchTerm) { 39 | this.setState({searchTerm}) 40 | } 41 | 42 | handleChange(pos) { 43 | this.setState({pos}); 44 | } 45 | 46 | handleLocationChange(pos){ 47 | this.setState({ 48 | pos 49 | }) 50 | } 51 | 52 | handleLoad(places){ 53 | this.setState({places}) 54 | } 55 | 56 | // call method to read currentCity from header 57 | setCurrentCity(city){ 58 | city = city !== undefined? `${city}` : city; 59 | this.setState({city}); 60 | } 61 | 62 | setUserPosition(userPos){ 63 | if(userPos.lat){ 64 | this.setState({userPos}) 65 | } 66 | } 67 | 68 | onPlaceSelected(id) { 69 | if (this.state.mouseOverPlace !== id) { 70 | this.setState({mouseOverPlace: id}); 71 | } 72 | } 73 | 74 | render() { 75 | const { 76 | pos, 77 | searchTerm, 78 | mouseOverPlace, 79 | city 80 | } = this.state; 81 | return ( 82 |
83 |
84 | 89 |
90 |
91 |
92 | 93 |
94 | 95 |
96 | 102 |
103 | 104 |
105 |
106 |

{city}

107 |

Coordinates: {pos.lat}, {pos.lng}

108 |
109 |
110 | {city && 111 | 112 | } 113 |
114 |
115 | 116 |
117 | this.onPlaceSelected(id)} /> 118 |
119 |
120 | 121 |
122 |
123 |
124 |
125 | ); 126 | } 127 | } 128 | 129 | export default App; 130 | -------------------------------------------------------------------------------- /src/css/index.min.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css?family=Open+Sans|Roboto);*,h1{font-family:var(--text-open-sans)}#btnRecenter,.search-box{box-shadow:0 2px 6px rgba(0,0,0,.3)}* a,.footer a{text-decoration:none}:root{--header-color:#D8E83D;--text-color:#202020;--button-color:#84347c;--button-color-hover:#6f6f6f;--border-color:#c2c2c2;--text-open-sans:"Open Sans",Arial,Helvetica,sans-serif;--text-roboto:"Roboto",Arial,Helvetica,sans-serif}*{margin:0;padding:0;box-sizing:border-box;color:var(--text-color)}html{height:100%}body{max-width:60em;min-height:100%}h1{font-size:1.6rem}h2{font-size:1.4rem}h3{font-size:1.2rem}.fullContainer{height:100%;width:100%}.mapContainer{background-color:#fff;padding:.5em}.loadingIndicator{border:16px solid #f3f3f3;border-radius:50%;border-top:16px solid #84347c;width:120px;height:120px;-webkit-animation:spin 2s linear infinite;animation:spin 2s linear infinite;margin:50px auto}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0)}100%{-webkit-transform:rotate(360deg)}}@keyframes spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}.theMap{position:relative;width:100%;height:600px}#btnRecenter{background-color:#fff;border:2px solid #fff;cursor:pointer;color:#000;font-family:var(--text-roboto);padding:.48em;margin:7px;position:absolute;left:100px;top:3px}#btnRecenter:focus,#btnRecenter:hover{background-color:#eee}.mapPlaces{color:var(--text-color);overflow:hidden;overflow-y:scroll;max-height:850px}.mapPlaces li{list-style:none;margin-bottom:0}.mapPlaces .listings{border-bottom:var(--border-color) 1px solid;padding-bottom:5px;align-items:center;cursor:pointer}.mapPlaces .listings .listing-icon-div{min-height:135px;min-width:135px;align-items:center;justify-content:center}.mapPlaces .listings .listings-icon{width:135px;height:135px}.mapPlaces .listings .listing-name{color:#323232;font-weight:400}.mapDescription .mapCoords,.search button{font-weight:700}.mapPlaces .listings .listings-info{color:#336282}.mapPlaces .listings .listing-open{font-weight:700;color:var(--header-color)}.mapPlaces .listings:focus,.mapPlaces .listings:hover{background-color:#eceff1}.mapDescription{padding:0}.mapDescription .wikiDescription{padding-top:2px}.searchContainer{align-items:flex-end;max-width:90%;margin-top:1.3em}.search button{background:var(--button-color);color:#fff;font-size:1.2em;transition:.3s}.search button:focus,.search button:hover{background:var(--button-color-hover)}.footer,.header{background:var(--header-color)}.search-box{border:1px solid transparent;border-radius:3px;font-size:1.3em;text-overflow:ellipses;padding:.8em}.header{padding:2em;font-weight:200;text-align:center;align-items:center}.footer{display:flex;align-items:center;color:#000;font-family:var(--text-open-sans);padding:1em .5em}.footer div{padding:.5em}.footer a{padding:5px;margin:2px;transition:.5s}.footer a:focus,.footer a:hover{background:#fff;color:#000;border-radius:10px}.footer ul{display:flex;flex-wrap:wrap;list-style:none;justify-content:left}.footer li{text-align:center;line-height:2em;list-style-type:none!important}.footer .gwg-link{font-size:1.5em;align-items:center}@media screen and (max-width:800px){h1{font-size:1.2rem}h2{font-size:1.125rem}h3{font-size:1rem}.theMap{height:400px}.searchContainer{max-width:100%}.search-box{font-size:1em}.header{padding:.9em}}@supports (grid-area:auto){body{display:grid;grid-template-rows:1fr auto;max-width:100%}.fullContainer{display:grid;grid-template-columns:1fr}.mapContainer{display:grid;grid-template-columns:65% 33%;grid-template-areas:"search places" "map places" "desc places";grid-gap:.5em}.searchContainer{grid-area:search}.search{display:grid;grid-template-columns:71% 28%;grid-gap:1em .3em}.map{grid-area:map}#info{display:grid;grid-template-columns:1fr 3fr;grid-gap:.5em}.mapDescription{grid-area:desc;align-items:start}.mapPlaces{grid-area:places}.mapPlaces .listings{display:grid;grid-template-columns:1fr 149px;grid-column:auto}.mapPlaces .listings .listing-icon-div{display:grid;grid-column-start:2;grid-row-start:1}.mapPlaces .listings .listing-description{display:grid;grid-template-columns:minmax(100px,300px);grid-template-rows:1fr 2fr}@media screen and (max-width:800px){.fullContainer,.mapContainer{display:grid;grid-template-columns:1fr}.mapContainer{grid-template-areas:"search" "map" "places" "desc";grid-template-rows:repeat(auto-fit,minmax(50px,1fr))}}} -------------------------------------------------------------------------------- /src/services/geolocation.js: -------------------------------------------------------------------------------- 1 | export function getCity(lat, lng){ 2 | let city; 3 | const state = { 4 | //Canada 5 | "AB": "Alberta", 6 | "BC": "British Columbia", 7 | "MB": "Manitoba", 8 | "NB": "New Brunswick", 9 | "NF": "Newfoundland", 10 | "NS": "Nova Scotia", 11 | "NT": "Northwest Territories", 12 | "ON": "Ontario", 13 | "PE": "Prince Edward Island", 14 | "QC": "Quebec", 15 | "SK": "Saskatchewan", 16 | "YT": "Yukon", 17 | //USA 18 | "AK": "Alaska", 19 | "AL": "Alabama", 20 | "AR": "Arkansas", 21 | "AS": "American Samoa", 22 | "AZ": "Arizona", 23 | "CA": "California", 24 | "CO": "Colorado", 25 | "CT": "Connecticut", 26 | "DC": "District of Columbia", 27 | "DE": "Deleware", 28 | "FL": "Florida", 29 | "FM": "Federated State of Micronesia", 30 | "GA": "Georgia", 31 | "GU": "Guam", 32 | "HI": "Hawaii", 33 | "IA": "Iowa", 34 | "ID": "Idaho", 35 | "IL": "Illinois", 36 | "IN": "Indiana", 37 | "KS": "Kansas", 38 | "KY": "Kentucky", 39 | "LA": "Lousiana", 40 | "MA": "Massachusetts", 41 | "MD": "Maryland", 42 | "ME": "Maine", 43 | "MH": "Marshall Islands", 44 | "MI": "Michigan", 45 | "MN": "Minnesota", 46 | "MO": "Missouri", 47 | "MP": "Northern Mariana Islands", 48 | "MS": "Mississippi", 49 | "MT": "Montana", 50 | "NC": "North Carolina", 51 | "ND": "North Dakota", 52 | "NE": "Nebraska", 53 | "NH": "New Hampshire", 54 | "NJ": "New Jersey", 55 | "NM": "New Mexico", 56 | "NV": "Nevada", 57 | "NY": "New York", 58 | "OH": "Ohio", 59 | "OK": "Oklahoma", 60 | "OR": "Oregon", 61 | "PA": "Pennsylvania", 62 | "PR": "Puerto Rico", 63 | "PW": "Palau", 64 | "RI": "Rhode Island", 65 | "SC": "South Carolina", 66 | "SD": "South Dakota", 67 | "TN": "Tennessee", 68 | "TX": "Texas", 69 | "UT": "Utah", 70 | "VA": "Virginia", 71 | "VI": "Virgin Island", 72 | "VT": "Vermont", 73 | "WA": "Washington", 74 | "WI": "Wisconsin", 75 | "WV": "West Virginia", 76 | "WY": "Wyoming", 77 | //Mexico 78 | "Ags.": "Aguascalientes", 79 | "B.C.S.": "Baja California Sur", 80 | "B.C.": "Baja California", 81 | "Chi.": "Chihuahua", 82 | "Col.": "Colima", 83 | "Camp.": "Campeche", 84 | "Coah.": "Coahuila", 85 | "Chis.": "Chiapas", 86 | "Dgo.": "Durango", 87 | "Gro.": "Guerrero", 88 | "Gto.": "Guanajuato", 89 | "Hgo.": "Hidalgo", 90 | "Jal.": "Jalisco", 91 | "CDMX": "México City", 92 | "Méx.": "México State", 93 | "Mich.": "Michoacán", 94 | "Mor.": "Morelos", 95 | "Nay.": "Nayarit", 96 | "N.L.": "Nuevo León", 97 | "Oax.": "Oaxaca", 98 | "Pue.": "Puebla", 99 | "Qro.": "Querétaro", 100 | "Q.R.": "Quintana Roo", 101 | "Sin.": "Sinaloa", 102 | "S.L.P.": "San Luis Potosí", 103 | "Son.": "Sonora", 104 | "Tab.": "Tabasco", 105 | "Tlax": "Tlaxcala", 106 | "Tamps.": "Tamaulipas", 107 | "Ver.": "Veracruz", 108 | "Yuc.": "Yucatán", 109 | "Zac": "Zacatecas", 110 | //Countries with Acronyms 111 | "UK": "United Kingdom" 112 | } 113 | 114 | 115 | return new Promise(function (resolve, reject) { 116 | 117 | const latlng = {lat: parseFloat(lat), lng: parseFloat(lng)}; 118 | 119 | if (!window.google) { 120 | resolve(); 121 | } 122 | 123 | const geocoder = new window.google.maps.Geocoder(); 124 | 125 | geocoder.geocode({'location': latlng}, function(results, status) { 126 | if (status === 'OK') { 127 | if (results[0]) { 128 | 129 | city = results[0].formatted_address; 130 | console.log('this is the original location ' + city) 131 | // Replaces state abbreviation to full name using state object. 132 | // Object.keys(state).forEach(k => city = city.replace(new RegExp(`\\b${k}\\b`, 'g'), state[k])); 133 | Object.keys(state).forEach(k => city = city.replace(k, state[k])); 134 | 135 | //gets rid of 'unnamed road', 'USA', numbers with their word if connected, and -= 136 | city = city.replace(/\b\w*?Unnamed Road|Mexico|Canada|-|USA|[a-z]*\d+[a-z]*\w*?\b/gi, ''); 137 | //split by ',' 138 | let citystate = city.split(','); 139 | 140 | //gets rid of those empty array elements as well as remove empty space in front and back of array elements. 141 | citystate = citystate.filter(entry => entry.trim() !== '').map(string => string.trim()); 142 | 143 | if (citystate.length >= 2) { 144 | citystate = `${[citystate[citystate.length -2]]}, ${[citystate[citystate.length -1]]}`; 145 | } 146 | resolve(citystate); 147 | } else { 148 | window.alert('No results found'); 149 | reject(); 150 | } 151 | } else { 152 | window.alert('Geocoder failed due to: ' + status); 153 | reject(); 154 | } 155 | }); 156 | }) 157 | } -------------------------------------------------------------------------------- /src/css/index.css.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "mappings": "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", 4 | "sources": [ 5 | "index.scss" 6 | ], 7 | "names": [], 8 | "file": "index.css" 9 | } -------------------------------------------------------------------------------- /src/components/Footer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | 4 | export class Footer extends Component { 5 | constructor(props){ 6 | super(props); 7 | this.state = { 8 | members: [] 9 | } 10 | } 11 | 12 | componentWillReceiveProps() { 13 | this.getData(); 14 | } 15 | 16 | // the callback for fetching the information 17 | getData() { 18 | var json_data = [ 19 | { 20 | "avatarUrl": "https://avatars3.githubusercontent.com/u/8497274?v=4", 21 | "url": "https://github.com/tanyagupta", 22 | "login": "tanyagupta", 23 | "id": "MDQ6VXNlcjg0OTcyNzQ=", 24 | "name": "tanyagupta", 25 | "bioHTML": "" 26 | }, 27 | { 28 | "avatarUrl": "https://avatars2.githubusercontent.com/u/35678241?v=4", 29 | "url": "https://github.com/khusbuchandra", 30 | "login": "khusbuchandra", 31 | "id": "MDQ6VXNlcjM1Njc4MjQx", 32 | "name": "Khusbu Chandra", 33 | "bioHTML": "
Traveler, mother, DIYer , has an opinion and likes to write mostly code.
" 34 | }, 35 | { 36 | "avatarUrl": "https://avatars1.githubusercontent.com/u/2319637?v=4", 37 | "url": "https://github.com/sgluzins", 38 | "login": "sgluzins", 39 | "id": "MDQ6VXNlcjIzMTk2Mzc=", 40 | "name": "sasha", 41 | "bioHTML": "
www.linkedin.com/in/sasha-gluzinski
" 42 | }, 43 | { 44 | "avatarUrl": "https://avatars0.githubusercontent.com/u/4163671?v=4", 45 | "url": "https://github.com/sonalikatara", 46 | "login": "sonalikatara", 47 | "id": "MDQ6VXNlcjQxNjM2NzE=", 48 | "name": "Sonali Shukla", 49 | "bioHTML": "
Front End Web Developer\n
" 50 | }, 51 | { 52 | "avatarUrl": "https://avatars2.githubusercontent.com/u/21042658?v=4", 53 | "url": "https://github.com/agonzalez0515", 54 | "login": "agonzalez0515", 55 | "id": "MDQ6VXNlcjIxMDQyNjU4", 56 | "name": "Angelica Gonzalez", 57 | "bioHTML": "
Hufflepuff 💛 \nWeb Developer 👩🏻‍💻\nNintendettes 🎀 \nCal 💙\n
" 58 | }, 59 | { 60 | "avatarUrl": "https://avatars1.githubusercontent.com/u/8865558?v=4", 61 | "url": "https://github.com/digilou", 62 | "login": "digilou", 63 | "id": "MDQ6VXNlcjg4NjU1NTg=", 64 | "name": "Amy Carney", 65 | "bioHTML": "
Front-End Web Designer & Developer
" 66 | }, 67 | { 68 | "avatarUrl": "https://avatars3.githubusercontent.com/u/4171505?v=4", 69 | "url": "https://github.com/PepperAddict", 70 | "login": "PepperAddict", 71 | "id": "MDQ6VXNlcjQxNzE1MDU=", 72 | "name": "Jen Reassey", 73 | "bioHTML": "" 74 | }, 75 | { 76 | "avatarUrl": "https://avatars3.githubusercontent.com/u/17239196?v=4", 77 | "url": "https://github.com/amr08", 78 | "login": "amr08", 79 | "id": "MDQ6VXNlcjE3MjM5MTk2", 80 | "name": "Andrea Roche", 81 | "bioHTML": "
Full-Stack Developer, Mom, Musician, Jack of all trades
" 82 | }, 83 | { 84 | "avatarUrl": "https://avatars0.githubusercontent.com/u/26439524?v=4", 85 | "url": "https://github.com/desdemonhu", 86 | "login": "desdemonhu", 87 | "id": "MDQ6VXNlcjI2NDM5NTI0", 88 | "name": "", 89 | "bioHTML": "" 90 | }, 91 | { 92 | "avatarUrl": "https://avatars1.githubusercontent.com/u/36322603?v=4", 93 | "url": "https://github.com/resant18", 94 | "login": "resant18", 95 | "id": "MDQ6VXNlcjM2MzIyNjAz", 96 | "name": null, 97 | "bioHTML": "" 98 | }, 99 | { 100 | "avatarUrl": "https://avatars0.githubusercontent.com/u/6998954?v=4", 101 | "url": "https://github.com/M0nica", 102 | "login": "M0nica", 103 | "id": "MDQ6VXNlcjY5OTg5NTQ=", 104 | "name": "Monica Powell", 105 | "bioHTML": "
I create websites. I'm a #GIRLBOSS Awardee. I make technology more enjoyable & accessible
" 106 | }, 107 | { 108 | "avatarUrl": "https://avatars1.githubusercontent.com/u/36494738?v=4", 109 | "url": "https://github.com/replimes", 110 | "login": "replimes", 111 | "id": "MDQ6VXNlcjM2NDk0NzM4", 112 | "name": "Kristina B", 113 | "bioHTML": "" 114 | } 115 | 116 | ] 117 | 118 | let names = []; 119 | for(var i = 0; i < json_data.length; i++) { 120 | let sets = json_data[i].login; 121 | let jsonname = json_data[i].name; 122 | names.push([`
  • ${jsonname || sets}
  • `]) 123 | } 124 | 125 | this.setState({members: names}) 126 | } 127 | 128 | render(){ 129 | const {members} = this.state; 130 | let together = members.join(" "); 131 | 132 | return ( 133 |
    134 |

    Grow with Google Project

      135 |
    136 | ) 137 | } 138 | } 139 | 140 | export default Footer; 141 | -------------------------------------------------------------------------------- /src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | 2 | // // In production, we register a service worker to serve assets from local cache. 3 | 4 | // // This lets the app load faster on subsequent visits in production, and gives 5 | // // it offline capabilities. However, it also means that developers (and users) 6 | // // will only see deployed updates on the "N+1" visit to a page, since previously 7 | // // cached resources are updated in the background. 8 | 9 | // // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 10 | // // This link also includes instructions on opting out of this behavior. 11 | 12 | // const isLocalhost = Boolean( 13 | // window.location.hostname === 'localhost' || 14 | // // [::1] is the IPv6 localhost address. 15 | // window.location.hostname === '[::1]' || 16 | // // 127.0.0.1/8 is considered localhost for IPv4. 17 | // window.location.hostname.match( 18 | // /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 19 | // ) 20 | // ); 21 | 22 | 23 | 24 | 25 | // In production, we register a service worker to serve assets from local cache. 26 | 27 | // This lets the app load faster on subsequent visits in production, and gives 28 | // it offline capabilities. However, it also means that developers (and users) 29 | // will only see deployed updates on the "N+1" visit to a page, since previously 30 | // cached resources are updated in the background. 31 | 32 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 33 | // This link also includes instructions on opting out of this behavior. 34 | 35 | // const isLocalhost = Boolean( 36 | // window.location.hostname === 'localhost' || 37 | // // [::1] is the IPv6 localhost address. 38 | // window.location.hostname === '[::1]' || 39 | // // 127.0.0.1/8 is considered localhost for IPv4. 40 | // window.location.hostname.match( 41 | // /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 42 | // ) 43 | // ); 44 | 45 | export default function register() { 46 | console.log(process.env.NODE_ENV) 47 | // if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 48 | // The URL constructor is available in all browsers that support SW. 49 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 50 | // if (publicUrl.origin !== window.location.origin) { 51 | // // Our service worker won't work if PUBLIC_URL is on a different origin 52 | // // from what our page is served on. This might happen if a CDN is used to 53 | // // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 54 | // return; 55 | // } 56 | 57 | window.addEventListener('load', () => { 58 | const swUrl = `${process.env.PUBLIC_URL}/sw.js`; 59 | 60 | // if (isLocalhost) { 61 | // // This is running on localhost. Lets check if a service worker still exists or not. 62 | // checkValidServiceWorker(swUrl); 63 | 64 | // Add some additional logging to localhost, pointing developers to the 65 | // service worker/PWA documentation. 66 | // navigator.serviceWorker.ready.then(() => { 67 | // console.log( 68 | // 'This web app is being served cache-first by a service ' + 69 | // 'worker. To learn more, visit https://goo.gl/SC7cgQ' 70 | // ); 71 | // }); 72 | // } else { 73 | // Is not local host. Just register service worker 74 | registerValidSW(swUrl); 75 | // } 76 | }); 77 | // } 78 | } 79 | 80 | 81 | 82 | function registerValidSW(swUrl) { 83 | navigator.serviceWorker 84 | .register(swUrl) 85 | .then(registration => { 86 | console.log(registration) 87 | registration.onupdatefound = () => { 88 | const installingWorker = registration.installing; 89 | installingWorker.onstatechange = () => { 90 | if (installingWorker.state === 'installed') { 91 | if (navigator.serviceWorker.controller) { 92 | // At this point, the old content will have been purged and 93 | // the fresh content will have been added to the cache. 94 | // It's the perfect time to display a "New content is 95 | // available; please refresh." message in your web app. 96 | console.log('New content is available; please refresh.'); 97 | } else { 98 | // At this point, everything has been precached. 99 | // It's the perfect time to display a 100 | // "Content is cached for offline use." message. 101 | console.log('Content is cached for offline use.'); 102 | } 103 | } 104 | }; 105 | }; 106 | }) 107 | .catch(error => { 108 | console.error('Error during service worker registration:', error); 109 | }); 110 | } 111 | 112 | // function checkValidServiceWorker(swUrl) { 113 | // // Check if the service worker can be found. If it can't reload the page. 114 | // fetch(swUrl) 115 | // .then(response => { 116 | // // Ensure service worker exists, and that we really are getting a JS file. 117 | // if ( 118 | // response.status === 404 || 119 | // response.headers.get('content-type').indexOf('javascript') === -1 120 | // ) { 121 | // // No service worker found. Probably a different app. Reload the page. 122 | // navigator.serviceWorker.ready.then(registration => { 123 | // registration.unregister().then(() => { 124 | // window.location.reload(); 125 | // }); 126 | // }); 127 | // } else { 128 | // // Service worker found. Proceed as normal. 129 | // registerValidSW(swUrl); 130 | // } 131 | // }) 132 | // .catch(() => { 133 | // console.log( 134 | // 'No internet connection found. App is running in offline mode.' 135 | // ); 136 | // }); 137 | // } 138 | 139 | // export function unregister() { 140 | // if ('serviceWorker' in navigator) { 141 | // navigator.serviceWorker.ready.then(registration => { 142 | // registration.unregister(); 143 | // }); 144 | // } 145 | // } -------------------------------------------------------------------------------- /src/css/index.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css?family=Open+Sans|Roboto"); 2 | :root { 3 | --header-color: #D8E83D; 4 | --text-color: #202020; 5 | --button-color: #84347c; 6 | --button-color-hover: #6f6f6f; 7 | --border-color: #c2c2c2; 8 | --text-open-sans: "Open Sans", Arial, Helvetica, sans-serif; 9 | --text-roboto: "Roboto", Arial, Helvetica, sans-serif; 10 | } 11 | 12 | /******* Normalize *******/ 13 | * { 14 | margin: 0; 15 | padding: 0; 16 | box-sizing: border-box; 17 | font-family: var(--text-open-sans); 18 | color: var(--text-color); 19 | } 20 | 21 | * a { 22 | text-decoration: none; 23 | } 24 | 25 | html { 26 | height: 100%; 27 | } 28 | 29 | body { 30 | max-width: 60em; 31 | min-height: 100%; 32 | } 33 | 34 | /****** Typography ******/ 35 | h1 { 36 | font-family: var(--text-open-sans); 37 | font-size: 1.6rem; 38 | } 39 | 40 | h2 { 41 | font-size: 1.4rem; 42 | } 43 | 44 | h3 { 45 | font-size: 1.2rem; 46 | } 47 | 48 | /* This is the main content */ 49 | .fullContainer { 50 | height: 100%; 51 | width: 100%; 52 | } 53 | 54 | /* This is the map part of the content */ 55 | .mapContainer { 56 | background-color: white; 57 | padding: .5em; 58 | } 59 | 60 | /* The loading animation */ 61 | .loadingIndicator { 62 | border: 16px solid #f3f3f3; 63 | border-radius: 50%; 64 | border-top: 16px solid #84347c; 65 | width: 120px; 66 | height: 120px; 67 | -webkit-animation: spin 2s linear infinite; 68 | animation: spin 2s linear infinite; 69 | margin: 50px auto; 70 | } 71 | 72 | @-webkit-keyframes spin { 73 | 0% { 74 | -webkit-transform: rotate(0deg); 75 | } 76 | 100% { 77 | -webkit-transform: rotate(360deg); 78 | } 79 | } 80 | 81 | @keyframes spin { 82 | 0% { 83 | transform: rotate(0deg); 84 | } 85 | 100% { 86 | transform: rotate(360deg); 87 | } 88 | } 89 | 90 | /* Style for the actual map! */ 91 | .theMap { 92 | position: relative; 93 | width: 100%; 94 | height: 600px; 95 | } 96 | 97 | #btnRecenter { 98 | background-color: #fff; 99 | border: 2px solid #fff; 100 | cursor: pointer; 101 | box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); 102 | color: black; 103 | font-family: var(--text-roboto); 104 | padding: .48em; 105 | margin: 7px; 106 | position: absolute; 107 | left: 100px; 108 | top: 3px; 109 | } 110 | 111 | #btnRecenter:hover, #btnRecenter:focus { 112 | background-color: #eee; 113 | } 114 | 115 | /*******************this is the sidebar*/ 116 | .mapPlaces { 117 | color: var(--text-color); 118 | overflow: hidden; 119 | overflow-y: scroll; 120 | max-height: 850px; 121 | } 122 | 123 | .mapPlaces li { 124 | list-style: none; 125 | margin-bottom: 0; 126 | } 127 | 128 | .mapPlaces .listings { 129 | border-bottom: var(--border-color) 1px solid; 130 | padding-bottom: 5px; 131 | align-items: center; 132 | cursor: pointer; 133 | } 134 | 135 | .mapPlaces .listings .listing-icon-div { 136 | min-height: 135px; 137 | min-width: 135px; 138 | align-items: center; 139 | justify-content: center; 140 | } 141 | 142 | .mapPlaces .listings .listings-icon { 143 | width: 135px; 144 | height: 135px; 145 | } 146 | 147 | .mapPlaces .listings .listing-name { 148 | color: #323232; 149 | font-weight: 400; 150 | } 151 | 152 | .mapPlaces .listings .listings-info { 153 | color: #336282; 154 | } 155 | 156 | .mapPlaces .listings .listing-open { 157 | font-weight: bold; 158 | color: var(--header-color); 159 | } 160 | 161 | .mapPlaces .listings:hover, .mapPlaces .listings:focus { 162 | background-color: #eceff1; 163 | } 164 | 165 | /****description bottom of the map */ 166 | .mapDescription { 167 | padding: 0px; 168 | } 169 | 170 | .mapDescription .mapCoords { 171 | font-weight: bold; 172 | } 173 | 174 | .mapDescription .wikiDescription { 175 | padding-top: 2px; 176 | } 177 | 178 | /* This is the Search area */ 179 | .searchContainer { 180 | align-items: flex-end; 181 | max-width: 90%; 182 | margin-top: 1.3em; 183 | } 184 | 185 | .search button { 186 | background: var(--button-color); 187 | color: white; 188 | font-size: 1.2em; 189 | font-weight: bold; 190 | transition: .3s; 191 | } 192 | 193 | .search button:hover, .search button:focus { 194 | background: var(--button-color-hover); 195 | } 196 | 197 | .search-box { 198 | border: 1px solid transparent; 199 | border-radius: 3px; 200 | box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); 201 | font-size: 1.3em; 202 | text-overflow: ellipses; 203 | padding: .8em; 204 | } 205 | 206 | /* This is the header section */ 207 | .header { 208 | background: var(--header-color); 209 | padding: 2em; 210 | font-weight: 200; 211 | text-align: center; 212 | align-items: center; 213 | } 214 | 215 | /*footer*/ 216 | .footer { 217 | display: flex; 218 | background: var(--header-color); 219 | align-items: center; 220 | color: black; 221 | font-family: var(--text-open-sans); 222 | padding: 1em .5em; 223 | } 224 | 225 | .footer div { 226 | padding: .5em; 227 | } 228 | 229 | .footer a { 230 | padding: 5px; 231 | margin: 2px; 232 | transition: .5s; 233 | text-decoration: none; 234 | } 235 | 236 | .footer a:hover, .footer a:focus { 237 | background: white; 238 | color: black; 239 | border-radius: 10px; 240 | } 241 | 242 | .footer ul { 243 | display: flex; 244 | flex-wrap: wrap; 245 | list-style: none; 246 | justify-content: left; 247 | } 248 | 249 | .footer li { 250 | text-align: center; 251 | line-height: 2em; 252 | list-style-type: none !important; 253 | } 254 | 255 | .footer .gwg-link { 256 | font-size: 1.5em; 257 | align-items: center; 258 | } 259 | 260 | @media screen and (max-width: 800px) { 261 | h1 { 262 | font-size: 1.2rem; 263 | } 264 | h2 { 265 | font-size: 1.125rem; 266 | } 267 | h3 { 268 | font-size: 1rem; 269 | } 270 | .theMap { 271 | height: 400px; 272 | } 273 | .searchContainer { 274 | max-width: 100%; 275 | } 276 | .search-box { 277 | font-size: 1em; 278 | } 279 | .header { 280 | padding: .9em; 281 | } 282 | } 283 | 284 | /* Check for new grid support (not -ms-grid) */ 285 | @supports (grid-area: auto) { 286 | body { 287 | display: grid; 288 | grid-template-rows: 1fr auto; 289 | max-width: 100%; 290 | } 291 | .fullContainer { 292 | display: grid; 293 | grid-template-columns: 1fr; 294 | } 295 | .mapContainer { 296 | display: grid; 297 | grid-template-columns: 65% 33%; 298 | grid-template-areas: "search places" 299 | "map places" 300 | "desc places"; 301 | grid-gap: .5em; 302 | } 303 | .searchContainer { 304 | grid-area: search; 305 | } 306 | .search { 307 | display: grid; 308 | grid-template-columns: 71% 28%; 309 | grid-gap: 1em .3em; 310 | } 311 | .map { 312 | grid-area: map; 313 | } 314 | #info { 315 | display: grid; 316 | grid-template-columns: 1fr 3fr; 317 | grid-gap: .5em; 318 | } 319 | .mapDescription { 320 | grid-area: desc; 321 | align-items: start; 322 | } 323 | .mapPlaces { 324 | grid-area: places; 325 | } 326 | .mapPlaces .listings { 327 | display: grid; 328 | grid-template-columns: 1fr 149px; 329 | grid-column: auto; 330 | } 331 | .mapPlaces .listings .listing-icon-div { 332 | display: grid; 333 | grid-column-start: 2; 334 | grid-row-start: 1; 335 | } 336 | .mapPlaces .listings .listing-description { 337 | display: grid; 338 | grid-template-columns: minmax(100px, 300px); 339 | grid-template-rows: 1fr 2fr; 340 | } 341 | @media screen and (max-width: 800px) { 342 | .fullContainer { 343 | display: grid; 344 | grid-template-columns: 1fr; 345 | } 346 | .mapContainer { 347 | display: grid; 348 | grid-template-areas: "search" 349 | "map" 350 | "places" 351 | "desc"; 352 | grid-template-columns: 1fr; 353 | grid-template-rows: repeat(auto-fit, minmax(50px, 1fr)); 354 | } 355 | } 356 | } 357 | /*# sourceMappingURL=index.css.map */ -------------------------------------------------------------------------------- /src/css/index.scss: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Open+Sans|Roboto'); 2 | 3 | :root { 4 | --header-color: #D8E83D; 5 | --text-color: #202020; 6 | --button-color: #84347c; 7 | --button-color-hover: #6f6f6f; 8 | --border-color: #c2c2c2; 9 | --text-open-sans: "Open Sans", Arial, Helvetica, sans-serif; 10 | --text-roboto: "Roboto", Arial, Helvetica, sans-serif; 11 | } 12 | 13 | 14 | /******* Normalize *******/ 15 | * { 16 | margin: 0; 17 | padding: 0; 18 | box-sizing: border-box; 19 | font-family: var(--text-open-sans); 20 | color: var(--text-color); 21 | & a { 22 | text-decoration: none; 23 | } 24 | 25 | } 26 | 27 | html { 28 | height: 100%; 29 | 30 | } 31 | 32 | body { 33 | max-width: 60em; 34 | min-height: 100%; 35 | } 36 | 37 | /****** Typography ******/ 38 | h1 { 39 | font-family: var(--text-open-sans); 40 | font-size: 1.6rem; 41 | } 42 | h2 { 43 | font-size: 1.4rem; 44 | } 45 | h3 { 46 | font-size: 1.2rem; 47 | } 48 | 49 | 50 | /* This is the main content */ 51 | 52 | .fullContainer { 53 | height: 100%; 54 | width: 100%; 55 | } 56 | /* This is the map part of the content */ 57 | .mapContainer { 58 | background-color: white; 59 | padding: .5em; 60 | } 61 | 62 | /* The loading animation */ 63 | 64 | .loadingIndicator { 65 | border: 16px solid #f3f3f3; 66 | border-radius: 50%; 67 | border-top: 16px solid #84347c; 68 | width: 120px; 69 | height: 120px; 70 | -webkit-animation: spin 2s linear infinite; 71 | animation: spin 2s linear infinite; 72 | margin: 50px auto; 73 | } 74 | 75 | @-webkit-keyframes spin { 76 | 0% { -webkit-transform: rotate(0deg); } 77 | 100% { -webkit-transform: rotate(360deg); } 78 | } 79 | 80 | @keyframes spin { 81 | 0% { transform: rotate(0deg); } 82 | 100% { transform: rotate(360deg); } 83 | } 84 | 85 | /* Style for the actual map! */ 86 | .theMap { 87 | position: relative; 88 | width: 100%; 89 | height: 600px; 90 | } 91 | 92 | #btnRecenter { 93 | background-color: #fff; 94 | border: 2px solid #fff; 95 | cursor: pointer; 96 | box-shadow: 0 2px 6px rgba(0,0,0,.3); 97 | color: black; 98 | font-family: var(--text-roboto); 99 | padding: .48em; 100 | margin: 7px; 101 | position: absolute; 102 | left: 100px; 103 | top: 3px; 104 | &:hover, 105 | &:focus { 106 | background-color: #eee; 107 | } 108 | } 109 | 110 | /*******************this is the sidebar*/ 111 | 112 | .mapPlaces { 113 | color: var(--text-color); 114 | overflow: hidden; 115 | overflow-y: scroll; 116 | max-height: 850px; 117 | li { 118 | list-style: none; 119 | margin-bottom:0; 120 | } 121 | .listings { 122 | border-bottom: var(--border-color) 1px solid; 123 | padding-bottom: 5px; 124 | align-items: center; 125 | cursor: pointer; 126 | .listing-icon-div { 127 | min-height: 135px; 128 | min-width: 135px; 129 | align-items: center; 130 | justify-content: center; 131 | } 132 | .listings-icon { 133 | width: 135px; 134 | height: 135px; 135 | } 136 | .listing-name { 137 | color: #323232; 138 | font-weight: 400; 139 | } 140 | .listings-info { 141 | color: #336282; 142 | } 143 | .listing-open { 144 | font-weight: bold; 145 | color: var(--header-color); 146 | } 147 | &:hover, 148 | &:focus { 149 | background-color: #eceff1; 150 | } 151 | } 152 | } 153 | 154 | 155 | /****description bottom of the map */ 156 | .mapDescription { 157 | padding: 0px; 158 | & .mapCoords { 159 | font-weight: bold; 160 | } 161 | & .wikiDescription { 162 | padding-top: 2px; 163 | 164 | } 165 | } 166 | 167 | /* This is the Search area */ 168 | .searchContainer { 169 | align-items: flex-end; 170 | max-width: 90%; 171 | margin-top: 1.3em; 172 | } 173 | .search { 174 | button { 175 | background: var(--button-color); 176 | color: white; 177 | font-size: 1.2em; 178 | font-weight: bold; 179 | transition: .3s; 180 | &:hover, 181 | &:focus { 182 | background: var(--button-color-hover) 183 | } 184 | } 185 | } 186 | 187 | .search-box { 188 | border: 1px solid transparent; 189 | border-radius: 3px; 190 | box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); 191 | font-size: 1.3em; 192 | text-overflow: ellipses; 193 | padding: .8em; 194 | } 195 | 196 | /* This is the header section */ 197 | .header { 198 | background: var(--header-color); 199 | padding: 2em; 200 | font-weight: 200; 201 | text-align: center; 202 | align-items: center; 203 | } 204 | 205 | 206 | /*footer*/ 207 | 208 | .footer { 209 | display: flex; 210 | background: var(--header-color); 211 | align-items: center; 212 | color: black; 213 | font-family: var(--text-open-sans); 214 | padding: 1em .5em; 215 | div { 216 | padding: .5em; 217 | } 218 | a { 219 | 220 | padding: 5px; 221 | margin: 2px; 222 | transition: .5s; 223 | text-decoration: none !important; 224 | &:hover, 225 | &:focus { 226 | background: white; 227 | color: black; 228 | border-radius: 10px; 229 | } 230 | } 231 | ul { 232 | display: flex; 233 | flex-wrap: wrap; 234 | list-style: none !important; 235 | justify-content: left; 236 | } 237 | li { 238 | text-align: center; 239 | line-height: 2em; 240 | list-style: none !important; 241 | } 242 | .gwg-link { 243 | font-size: 1.5em; 244 | align-items: center; 245 | } 246 | } 247 | 248 | @media screen and (max-width: 800px) { 249 | h1 { 250 | font-size: 1.2rem; 251 | } 252 | h2 { 253 | font-size: 1.125rem; 254 | } 255 | h3 { 256 | font-size: 1rem; 257 | } 258 | 259 | .theMap { 260 | height: 400px; 261 | } 262 | 263 | .searchContainer { 264 | max-width: 100%; 265 | } 266 | 267 | .search-box { 268 | font-size: 1em; 269 | } 270 | 271 | .header { 272 | padding: .9em; 273 | } 274 | } 275 | 276 | /* Check for new grid support (not -ms-grid) */ 277 | @supports(grid-area: auto) { 278 | body { 279 | display: grid; 280 | grid-template-rows: 1fr auto; 281 | max-width: 100%; 282 | } 283 | .fullContainer { 284 | display: grid; 285 | grid-template-columns: 1fr; 286 | } 287 | .mapContainer { 288 | display: grid; 289 | grid-template-columns: 65% 33%; 290 | grid-template-areas: 291 | "search places" 292 | "map places" 293 | "desc places"; 294 | grid-gap: .5em; 295 | } 296 | .searchContainer { 297 | grid-area: search; 298 | } 299 | .search { 300 | display: grid; 301 | grid-template-columns: 71% 28%; 302 | grid-gap: 1em .3em; 303 | } 304 | .map { 305 | grid-area: map; 306 | } 307 | #info { 308 | display: grid; 309 | grid-template-columns: 1fr 3fr; 310 | grid-gap: .5em; 311 | } 312 | .mapDescription { 313 | grid-area: desc; 314 | align-items: start; 315 | } 316 | .mapPlaces { 317 | grid-area: places; 318 | .listings { 319 | display: grid; 320 | grid-template-columns: 1fr 149px; 321 | grid-column: auto; 322 | .listing-icon-div { 323 | display: grid; 324 | grid-column-start: 2; 325 | grid-row-start: 1; 326 | } 327 | .listing-description { 328 | display: grid; 329 | grid-template-columns: minmax(100px, 300px); 330 | grid-template-rows: 1fr 2fr; 331 | } 332 | 333 | } 334 | } 335 | 336 | @media screen and (max-width: 800px) { 337 | .fullContainer { 338 | display: grid; 339 | grid-template-columns: 1fr; 340 | } 341 | 342 | .mapContainer { 343 | display: grid; 344 | grid-template-areas: 345 | "search" 346 | "map" 347 | "places" 348 | "desc"; 349 | grid-template-columns: 1fr; 350 | grid-template-rows: repeat(auto-fit, minmax(50px, 1fr)); 351 | } 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /src/components/InitialMap.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Map, Marker, InfoWindow, GoogleApiWrapper } from 'google-maps-react'; 3 | import StaticMap from './StaticMap' 4 | import Button from './RecenterMapButton' 5 | 6 | const selectedIconUrl = 'https://maps.google.com/mapfiles/kml/paddle/red-circle.png'; 7 | const defaultIconUrl = 'https://maps.google.com/mapfiles/ms/icons/red-dot.png'; 8 | const markerIconUrl = 'https://maps.google.com/mapfiles/ms/icons/green-dot.png'; 9 | 10 | class MapContainer extends Component { 11 | constructor(props) { 12 | super(props); 13 | this.state = { 14 | searchTerm: this.props.searchTerm, 15 | places: this.props.places, 16 | showingInfoWindow: false, 17 | activeMarker: {}, 18 | selectedPlace: {}, 19 | 20 | } 21 | 22 | // binding this to event-handler functions 23 | this.onMarkerClick = this.onMarkerClick.bind(this) 24 | this.onMarkerOver = this.onMarkerOver.bind(this) 25 | this.onMarkerOut = this.onMarkerOut.bind(this) 26 | this.onMapClicked = this.onMapClicked.bind(this); 27 | this.recenterMyMap = this.recenterMyMap.bind(this); 28 | } 29 | 30 | // change marker image to green on mouse over 31 | onMarkerOver = (props, marker, e) => { 32 | const { google } = this.props 33 | marker.setIcon({ 34 | url: markerIconUrl, 35 | anchor: google.maps.Point(10, 10), 36 | scaledSize: google.maps.Size(10, 17) 37 | }) 38 | } 39 | 40 | // change the marker image to red when mouse out 41 | onMarkerOut = (props, marker, e) => { 42 | const { google } = this.props 43 | marker.setIcon({ 44 | url: defaultIconUrl, 45 | anchor: google.maps.Point(10, 10), 46 | scaledSize: google.maps.Size(10, 17) 47 | }) 48 | } 49 | 50 | // show Infowindow when a marker is clicked and make it the active marker 51 | onMarkerClick = (props, marker, e) => { 52 | this.setState({ 53 | selectedPlace: props, 54 | activeMarker: marker, 55 | showingInfoWindow: true 56 | }); 57 | } 58 | 59 | 60 | onMapClicked = (props) => { 61 | if (this.state.showingInfoWindow) { 62 | this.setState({ 63 | showingInfoWindow: false, 64 | activeMarker: null 65 | }) 66 | } 67 | } 68 | 69 | onGoogleMapLoad = map => { 70 | this.map = map; 71 | } 72 | 73 | onMapReady = (mapProps, map) => { 74 | this.setState({ map }); 75 | this.searchText(map, map.center, this.props.searchTerm) 76 | } 77 | 78 | searchText = (map, center, query) => { 79 | const { google } = this.props 80 | const service = new google.maps.places.PlacesService(map) 81 | const request = { 82 | location: center, 83 | radius: '500', 84 | query: query 85 | } 86 | 87 | service.textSearch(request, (results, status) => { 88 | if (status === google.maps.places.PlacesServiceStatus.OK) { 89 | //console.log(results); 90 | 91 | this.setState({ 92 | places: results, 93 | }) 94 | 95 | //console.log("results= " + JSON.stringify(results)) 96 | this.props.onLoad(results); 97 | } 98 | }) 99 | 100 | } 101 | 102 | componentWillReceiveProps(nextProps) { 103 | if (!this.state.map) return; 104 | if (nextProps.searchTerm && this.props.searchTerm !== nextProps.searchTerm) { 105 | this.setState({ searchTerm: nextProps.searchTerm }) 106 | this.searchText(this.state.map, this.state.map.center, nextProps.searchTerm) 107 | } 108 | 109 | if (this.props.pos !== nextProps.pos) { 110 | this.searchText(this.state.map, nextProps.pos, this.props.searchTerm) 111 | } 112 | } 113 | 114 | componentWillMount(){ 115 | 116 | } 117 | 118 | // recenter the map to User's current location 119 | recenterMyMap(){ 120 | const { 121 | userPos 122 | } = this.props 123 | this.state.map.setCenter(userPos) 124 | } 125 | 126 | render() { 127 | //const google_api = process.env.REACT_APP_GKEY; 128 | let {pos} = this.props; 129 | 130 | const { 131 | places, 132 | google, 133 | mouseOverPlace, 134 | loaded 135 | } = this.props 136 | 137 | const { 138 | activeMarker, 139 | showingInfoWindow, 140 | selectedPlace 141 | } = this.state; 142 | 143 | //console.log("places : " + JSON.stringify(places)) 144 | if (!loaded) { 145 | return 146 | } 147 | 148 | // This code to fix issue#157 where map is only partially loading when current position value has not returned yet. 149 | if(!pos.lat) { 150 | // pos.lat = 0.0; 151 | // pos.lng = 0.0; 152 | 153 | return
    154 | } 155 | 156 | 157 | return ( 158 | 159 |
    160 | 161 |
    162 | 163 | 172 | 173 | 178 | 194 | 195 | 196 | { 197 | places && places.map(p => { 198 | //console.log("p.Rating: " + p.rating); 199 | let isOpenNow = ""; 200 | let priceLevel = ""; 201 | let rating = ""; 202 | 203 | const priceLevelDesc = { 204 | 0: "Free", 205 | 1: "Inexpensive", 206 | 2: "Moderate", 207 | 3: "Expensive", 208 | 4: "Very Expensive" 209 | } 210 | 211 | if (p.opening_hour !== undefined) { 212 | isOpenNow = p.opening_hours.open_now ? 'OPEN NOW' : 'CLOSED' 213 | } 214 | 215 | if (p.rating !== undefined) { 216 | rating = p.rating 217 | } 218 | 219 | if (p.price_level !== undefined) { 220 | priceLevel = priceLevelDesc[p.price_level] 221 | } 222 | 223 | let iconUrl = defaultIconUrl; 224 | if (mouseOverPlace === p.id) { 225 | iconUrl = selectedIconUrl; 226 | } 227 | 228 | return ( 229 | : no image 239 | photo={p.photos === undefined ? `` : p.photos[0].getUrl({ 'maxWidth': 100, 'maxHeight': 100 })} 240 | icon={{ 241 | url: iconUrl, 242 | anchor: google.maps.Point(10, 10), 243 | scaledSize: google.maps.Size(10, 17) 244 | }} 245 | onClick={this.onMarkerClick} 246 | onMouseover={this.onMarkerOver} 247 | onMouseout={this.onMarkerOut} /> 248 | ) 249 | }) 250 | } 251 | 252 | 256 |
    257 |
    258 | 259 |
    260 |
    261 |
    262 | {selectedPlace.name} 263 |
    264 |
    265 | Ratings : 266 | 267 | {selectedPlace.rating 268 | ? selectedPlace.rating 269 | : "" 270 | } 271 | 272 | 273 | Price Level : 274 | 275 | {selectedPlace.priceLevel} 276 | 277 |
    278 |
    {selectedPlace.address}
    279 |
    {selectedPlace.openNow}
    280 |
    281 |
    282 |
    283 |
    284 |
    285 |
    286 | ); 287 | } 288 | } 289 | 290 | export default GoogleApiWrapper({ 291 | apiKey: (process.env.REACT_APP_GKEY), 292 | libraries: ['places'], 293 | version: 3.31 294 | })(MapContainer) 295 | -------------------------------------------------------------------------------- /public/idb.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function () { 4 | function toArray(arr) { 5 | return Array.prototype.slice.call(arr); 6 | } 7 | 8 | function promisifyRequest(request) { 9 | return new Promise(function (resolve, reject) { 10 | request.onsuccess = function () { 11 | resolve(request.result); 12 | }; 13 | 14 | request.onerror = function () { 15 | reject(request.error); 16 | }; 17 | }); 18 | } 19 | 20 | function promisifyRequestCall(obj, method, args) { 21 | var request; 22 | var p = new Promise(function (resolve, reject) { 23 | request = obj[method].apply(obj, args); 24 | promisifyRequest(request).then(resolve, reject); 25 | }); 26 | 27 | p.request = request; 28 | return p; 29 | } 30 | 31 | function promisifyCursorRequestCall(obj, method, args) { 32 | var p = promisifyRequestCall(obj, method, args); 33 | return p.then(function (value) { 34 | if (!value) return; 35 | return new Cursor(value, p.request); 36 | }); 37 | } 38 | 39 | function proxyProperties(ProxyClass, targetProp, properties) { 40 | properties.forEach(function (prop) { 41 | Object.defineProperty(ProxyClass.prototype, prop, { 42 | get: function () { 43 | return this[targetProp][prop]; 44 | } 45 | }); 46 | }); 47 | } 48 | 49 | function proxyRequestMethods(ProxyClass, targetProp, Constructor, properties) { 50 | properties.forEach(function (prop) { 51 | if (!(prop in Constructor.prototype)) return; 52 | ProxyClass.prototype[prop] = function () { 53 | return promisifyRequestCall(this[targetProp], prop, arguments); 54 | }; 55 | }); 56 | } 57 | 58 | function proxyMethods(ProxyClass, targetProp, Constructor, properties) { 59 | properties.forEach(function (prop) { 60 | if (!(prop in Constructor.prototype)) return; 61 | ProxyClass.prototype[prop] = function () { 62 | return this[targetProp][prop].apply(this[targetProp], arguments); 63 | }; 64 | }); 65 | } 66 | 67 | function proxyCursorRequestMethods(ProxyClass, targetProp, Constructor, properties) { 68 | properties.forEach(function (prop) { 69 | if (!(prop in Constructor.prototype)) return; 70 | ProxyClass.prototype[prop] = function () { 71 | return promisifyCursorRequestCall(this[targetProp], prop, arguments); 72 | }; 73 | }); 74 | } 75 | 76 | function Index(index) { 77 | this._index = index; 78 | } 79 | 80 | proxyProperties(Index, '_index', [ 81 | 'name', 82 | 'keyPath', 83 | 'multiEntry', 84 | 'unique' 85 | ]); 86 | 87 | proxyRequestMethods(Index, '_index', IDBIndex, [ 88 | 'get', 89 | 'getKey', 90 | 'getAll', 91 | 'getAllKeys', 92 | 'count' 93 | ]); 94 | 95 | proxyCursorRequestMethods(Index, '_index', IDBIndex, [ 96 | 'openCursor', 97 | 'openKeyCursor' 98 | ]); 99 | 100 | function Cursor(cursor, request) { 101 | this._cursor = cursor; 102 | this._request = request; 103 | } 104 | 105 | proxyProperties(Cursor, '_cursor', [ 106 | 'direction', 107 | 'key', 108 | 'primaryKey', 109 | 'value' 110 | ]); 111 | 112 | proxyRequestMethods(Cursor, '_cursor', IDBCursor, [ 113 | 'update', 114 | 'delete' 115 | ]); 116 | 117 | // proxy 'next' methods 118 | ['advance', 'continue', 'continuePrimaryKey'].forEach(function (methodName) { 119 | if (!(methodName in IDBCursor.prototype)) return; 120 | Cursor.prototype[methodName] = function () { 121 | var cursor = this; 122 | var args = arguments; 123 | return Promise.resolve().then(function () { 124 | cursor._cursor[methodName].apply(cursor._cursor, args); 125 | return promisifyRequest(cursor._request).then(function (value) { 126 | if (!value) return; 127 | return new Cursor(value, cursor._request); 128 | }); 129 | }); 130 | }; 131 | }); 132 | 133 | function ObjectStore(store) { 134 | this._store = store; 135 | } 136 | 137 | ObjectStore.prototype.createIndex = function () { 138 | return new Index(this._store.createIndex.apply(this._store, arguments)); 139 | }; 140 | 141 | ObjectStore.prototype.index = function () { 142 | return new Index(this._store.index.apply(this._store, arguments)); 143 | }; 144 | 145 | proxyProperties(ObjectStore, '_store', [ 146 | 'name', 147 | 'keyPath', 148 | 'indexNames', 149 | 'autoIncrement' 150 | ]); 151 | 152 | proxyRequestMethods(ObjectStore, '_store', IDBObjectStore, [ 153 | 'put', 154 | 'add', 155 | 'delete', 156 | 'clear', 157 | 'get', 158 | 'getAll', 159 | 'getAllKeys', 160 | 'count' 161 | ]); 162 | 163 | proxyCursorRequestMethods(ObjectStore, '_store', IDBObjectStore, [ 164 | 'openCursor', 165 | 'openKeyCursor' 166 | ]); 167 | 168 | proxyMethods(ObjectStore, '_store', IDBObjectStore, [ 169 | 'deleteIndex' 170 | ]); 171 | 172 | function Transaction(idbTransaction) { 173 | this._tx = idbTransaction; 174 | this.complete = new Promise(function (resolve, reject) { 175 | idbTransaction.oncomplete = function () { 176 | resolve(); 177 | }; 178 | idbTransaction.onerror = function () { 179 | reject(idbTransaction.error); 180 | }; 181 | }); 182 | } 183 | 184 | Transaction.prototype.objectStore = function () { 185 | return new ObjectStore(this._tx.objectStore.apply(this._tx, arguments)); 186 | }; 187 | 188 | proxyProperties(Transaction, '_tx', [ 189 | 'objectStoreNames', 190 | 'mode' 191 | ]); 192 | 193 | proxyMethods(Transaction, '_tx', IDBTransaction, [ 194 | 'abort' 195 | ]); 196 | 197 | function UpgradeDB(db, oldVersion, transaction) { 198 | this._db = db; 199 | this.oldVersion = oldVersion; 200 | this.transaction = new Transaction(transaction); 201 | } 202 | 203 | UpgradeDB.prototype.createObjectStore = function () { 204 | return new ObjectStore(this._db.createObjectStore.apply(this._db, arguments)); 205 | }; 206 | 207 | proxyProperties(UpgradeDB, '_db', [ 208 | 'name', 209 | 'version', 210 | 'objectStoreNames' 211 | ]); 212 | 213 | proxyMethods(UpgradeDB, '_db', IDBDatabase, [ 214 | 'deleteObjectStore', 215 | 'close' 216 | ]); 217 | 218 | function DB(db) { 219 | this._db = db; 220 | } 221 | 222 | DB.prototype.transaction = function () { 223 | return new Transaction(this._db.transaction.apply(this._db, arguments)); 224 | }; 225 | 226 | proxyProperties(DB, '_db', [ 227 | 'name', 228 | 'version', 229 | 'objectStoreNames' 230 | ]); 231 | 232 | proxyMethods(DB, '_db', IDBDatabase, [ 233 | 'close' 234 | ]); 235 | 236 | // Add cursor iterators 237 | // TODO: remove this once browsers do the right thing with promises 238 | ['openCursor', 'openKeyCursor'].forEach(function (funcName) { 239 | [ObjectStore, Index].forEach(function (Constructor) { 240 | Constructor.prototype[funcName.replace('open', 'iterate')] = function () { 241 | var args = toArray(arguments); 242 | var callback = args[args.length - 1]; 243 | var request = (this._store || this._index)[funcName].apply(this._store, args.slice(0, -1)); 244 | request.onsuccess = function () { 245 | callback(request.result); 246 | }; 247 | }; 248 | }); 249 | }); 250 | 251 | // polyfill getAll 252 | [Index, ObjectStore].forEach(function (Constructor) { 253 | if (Constructor.prototype.getAll) return; 254 | Constructor.prototype.getAll = function (query, count) { 255 | var instance = this; 256 | var items = []; 257 | 258 | return new Promise(function (resolve) { 259 | instance.iterateCursor(query, function (cursor) { 260 | if (!cursor) { 261 | resolve(items); 262 | return; 263 | } 264 | items.push(cursor.value); 265 | 266 | if (count !== undefined && items.length == count) { 267 | resolve(items); 268 | return; 269 | } 270 | cursor.continue(); 271 | }); 272 | }); 273 | }; 274 | }); 275 | 276 | var exp = { 277 | open: function (name, version, upgradeCallback) { 278 | var p = promisifyRequestCall(indexedDB, 'open', [name, version]); 279 | var request = p.request; 280 | 281 | request.onupgradeneeded = function (event) { 282 | if (upgradeCallback) { 283 | upgradeCallback(new UpgradeDB(request.result, event.oldVersion, request.transaction)); 284 | } 285 | }; 286 | 287 | return p.then(function (db) { 288 | return new DB(db); 289 | }); 290 | }, 291 | delete: function (name) { 292 | return promisifyRequestCall(indexedDB, 'deleteDatabase', [name]); 293 | } 294 | }; 295 | 296 | if (typeof module !== 'undefined') { 297 | module.exports = exp; 298 | } 299 | else { 300 | self.idb = exp; 301 | } 302 | }()); 303 | 304 | console.log("indexeddbpromised included") 305 | 306 | //////////////////////////////////////////////////////////////////////////// 307 | 308 | //IDB Test code 309 | console.log("I am here") 310 | 311 | 312 | const idb = 313 | 314 | function () { 315 | 316 | 'use strict'; 317 | 318 | //check for support 319 | if (!('indexedDB' in window)) { 320 | console.log('This browser does not support IndexedDB'); 321 | return; 322 | } 323 | 324 | function createDB() { 325 | idb.open('mappa', 1, function (upgradeDb){ 326 | switch (upgradeDb.oldVersion) { 327 | case 0: 328 | if (!upgradeDb.objectStoreNames.contains('weather')) { 329 | upgradeDb.createObjectStore('weather', { keyPath: 'id_weather' }); 330 | //addWeather() 331 | 332 | } 333 | if (!upgradeDb.objectStoreNames.contains('places')) { 334 | upgradeDb.createObjectStore('places', { keyPath: 'id_places' }); 335 | //addPlaces() 336 | } 337 | } 338 | }) 339 | } 340 | 341 | 342 | function addWeather() { 343 | if (!db) return; 344 | 345 | var tx = db.transaction('weather', 'readwrite'); 346 | var store = tx.objectStore('weather'); 347 | store.put(); 348 | } 349 | 350 | 351 | }() --------------------------------------------------------------------------------